Debian lenny version packages
[pkg-perl] / deb-src / libfile-chdir-perl / libfile-chdir-perl-0.06 / lib / File / chdir.pm
1 package File::chdir;
2
3 use 5.004;
4
5 use strict;
6 use vars qw($VERSION @ISA @EXPORT $CWD @CWD);
7 $VERSION = 0.06;
8
9 require Exporter;
10 @ISA = qw(Exporter);
11 @EXPORT = qw($CWD @CWD);
12
13 use Cwd;
14 use File::Spec;
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
20 =head1 NAME
21
22 File::chdir - a more sensible way to change directories
23
24 =head1 SYNOPSIS
25
26   use File::chdir;
27
28   $CWD = "/foo/bar";     # now in /foo/bar
29   {
30       local $CWD = "/moo/baz";  # now in /moo/baz
31       ...
32   }
33
34   # still in /foo/bar!
35
36 =head1 DESCRIPTION
37
38 Perl's chdir() has the unfortunate problem of being very, very, very
39 global.  If any part of your program calls chdir() or if any library
40 you use calls chdir(), it changes the current working directory for
41 the B<whole> program.
42
43 This sucks.
44
45 File::chdir gives you an alternative, $CWD and @CWD.  These two
46 variables combine all the power of C<chdir()>, File::Spec and Cwd.
47
48 =head2 $CWD
49
50 Use the $CWD variable instead of chdir() and Cwd.
51
52     use File::chdir;
53     $CWD = $dir;  # just like chdir($dir)!
54     print $CWD;   # prints the current working directory
55
56 It can be localized, and it does the right thing.
57
58     $CWD = "/foo";      # it's /foo out here.
59     {
60         local $CWD = "/bar";  # /bar in here
61     }
62     # still /foo out here!
63
64 $CWD always returns the absolute path.
65
66 $CWD and normal chdir() work together just fine.
67
68 =head2 @CWD
69
70 @CWD represents the current working directory as an array, each
71 directory in the path is an element of the array.  This can often make
72 the directory easier to manipulate, and you don't have to fumble with
73 C<File::Spec-E<gt>splitpath> and C<File::Spec-E<gt>catdir> to make
74 portable code.
75
76   # Similar to chdir("/usr/local/src/perl")
77   @CWD = qw(usr local src perl);
78
79 pop, push, shift, unshift and splice all work.  pop and push are
80 probably the most useful.
81
82   pop @CWD;                 # same as chdir(File::Spec->updir)
83   push @CWD, 'some_dir'     # same as chdir('some_dir')
84
85 @CWD and $CWD both work fine together.
86
87 B<NOTE> Due to a perl bug you can't localize @CWD.  See L</BUGS and
88 CAVEATS> for a work around.
89
90 =cut
91
92 sub _abs_path () {
93     # Otherwise we'll never work under taint mode.
94     my($cwd) = Cwd::abs_path =~ /(.*)/;
95     return $cwd;
96 }
97
98 my $Real_CWD;
99 sub _chdir ($) {
100     my($new_dir) = @_;
101
102     my $Real_CWD = File::Spec->catdir(_abs_path(), $new_dir);
103
104     return CORE::chdir($new_dir);
105 }
106
107 {
108     package File::chdir::SCALAR;
109
110     sub TIESCALAR { 
111         bless [], $_[0];
112     }
113
114     # To be safe, in case someone chdir'd out from under us, we always
115     # check the Cwd explicitly.
116     sub FETCH {
117         return File::chdir::_abs_path;
118     }
119
120     sub STORE {
121         return unless defined $_[1];
122         my $did_chdir = File::chdir::_chdir($_[1]);
123         return $did_chdir ? $Real_CWD : $did_chdir;
124     }
125 }
126
127
128 {
129     package File::chdir::ARRAY;
130
131     sub TIEARRAY {
132         bless {}, $_[0];
133     }
134
135     # splitdir() leaves empty directory names in place on purpose.
136     # I don't think this is the right thing for us, but I could be wrong.
137     sub _splitdir {
138         return grep length, File::Spec->splitdir($_[0]);
139     }
140
141     sub _cwd_list {
142         return _splitdir(File::chdir::_abs_path);
143     }
144
145     sub _catdir {
146         return File::Spec->catdir(File::Spec->rootdir, @_);
147     }
148
149     sub FETCH { 
150         my($self, $idx) = @_;
151         my @cwd = _cwd_list;
152         return $cwd[$idx];
153     }
154
155     sub STORE {
156         my($self, $idx, $val) = @_;
157
158         my @cwd = ();
159         if( $self->{Cleared} ) {
160             $self->{Cleared} = 0;
161         }
162         else {
163             @cwd = _cwd_list;
164         }
165
166         $cwd[$idx] = $val;
167         my $dir = _catdir(@cwd);
168
169         my $did_chdir = File::chdir::_chdir($dir);
170         return $did_chdir ? $dir : $did_chdir;
171     }
172
173     sub FETCHSIZE { return scalar _cwd_list(); }
174     sub STORESIZE {}
175
176     sub PUSH {
177         my($self) = shift;
178
179         my $dir = _catdir(_cwd_list, @_);
180         my $did_chdir = File::chdir::_chdir($dir);
181         return $did_chdir ? $self->FETCHSIZE : $did_chdir;
182     }
183
184     sub POP {
185         my($self) = shift;
186
187         my @cwd = _cwd_list;
188         my $popped = pop @cwd;
189         my $dir = _catdir(@cwd);
190         my $did_chdir = File::chdir::_chdir($dir);
191         return $did_chdir ? $popped : $did_chdir;
192     }
193
194     sub SHIFT {
195         my($self) = shift;
196
197         my @cwd = _cwd_list;
198         my $shifted = shift @cwd;
199         my $dir = _catdir(@cwd);
200         my $did_chdir = File::chdir::_chdir($dir);
201         return $did_chdir ? $shifted : $did_chdir;
202     }
203
204     sub UNSHIFT {
205         my($self) = shift;
206
207         my $dir = _catdir(@_, _cwd_list);
208         my $did_chdir = File::chdir::_chdir($dir);
209         return $did_chdir ? $self->FETCHSIZE : $did_chdir;
210     }
211
212     sub CLEAR  {
213         my($self) = shift;
214         $self->{Cleared} = 1;
215     }
216
217     sub SPLICE {
218         my $self = shift;
219         my $offset = shift || 0;
220         my $len = shift || $self->FETCHSIZE - $offset;
221         my @new_dirs = @_;
222         
223         my @cwd = _cwd_list;
224         my @orig_dirs = splice @cwd, $offset, $len, @new_dirs;
225         my $dir = _catdir(@cwd);
226         my $did_chdir = File::chdir::_chdir($dir);
227         return $did_chdir ? @orig_dirs : $did_chdir;
228     }
229
230     sub EXTEND { }
231     sub EXISTS { 
232         my($self, $idx) = @_;
233         return $self->FETCHSIZE >= $idx ? 1 : 0;
234     }
235
236     sub DELETE {
237         die "Even I can't think of what delete \$CWD[\$idx] should do!";
238     }
239 }
240
241
242 =head1 EXAMPLES
243
244 (We omit the C<use File::chdir> from these examples for terseness)
245
246 Here's $CWD instead of chdir:
247
248     $CWD = 'foo';           # chdir('foo')
249
250 and now instead of Cwd.
251
252     print $CWD;             # use Cwd;  print Cwd::abs_path
253
254 you can even do zsh style C<cd foo bar>
255
256     $CWD = '/usr/local/foo';
257     $CWD =~ s/usr/var/;
258
259 if you want to localize that, make sure you get the parens right
260
261     {
262         (local $CWD) =~ s/usr/var/;
263         ...
264     }
265
266 It's most useful for writing polite subroutines which don't leave the
267 program in some strange directory:
268
269     sub foo {
270         local $CWD = 'some/other/dir';
271         ...do your work...
272     }
273
274 which is much simplier than the equivalent:
275
276     sub foo {
277         use Cwd;
278         my $orig_dir = Cwd::abs_path;
279         chdir('some/other/dir');
280
281         ...do your work...
282
283         chdir($orig_dir);
284     }
285
286 @CWD comes in handy when you want to start moving up and down the
287 directory hierarchy in a cross-platform manner without having to use
288 File::Spec.
289
290     pop @CWD;                   # chdir(File::Spec->updir);
291     push @CWD, 'some', 'dir'    # chdir(File::Spec->catdir(qw(some dir)));
292
293 You can easily change your parent directory:
294
295     # chdir from /some/dir/bar/moo to /some/dir/foo/moo
296     $CWD[-2] = 'foo';
297
298
299 =head1 BUGS and CAVEATS
300
301 C<local @CWD> will not localize C<@CWD>.  This is a bug in Perl, you
302 can't localize tied arrays.  As a work around localizing $CWD will
303 effectively localize @CWD.
304
305     {
306         local $CWD;
307         pop @CWD;
308         ...
309     }
310
311
312 =head1 NOTES
313
314 What should %CWD do?  Something with volumes?
315
316     # chdir to C:\Program Files\Sierra\Half Life ?
317     $CWD{C} = '\\Program Files\\Sierra\\Half Life';
318
319
320 =head1 AUTHOR
321
322 Michael G Schwern E<lt>schwern@pobox.comE<gt>
323
324
325 =head1 LICENSE
326
327 Copyright 2001-2003 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.
328
329 This program is free software; you can redistribute it and/or 
330 modify it under the same terms as Perl itself.
331
332 See F<http://www.perl.com/perl/misc/Artistic.html>
333
334
335 =head1 HISTORY
336
337 I wanted C<local chdir> to work.  p5p didn't.  Did I let that stop me?
338 No!  Did we give up after the Germans bombed Pearl Harbor?  Hell, no!
339
340 Abigail and/or Bryan Warnock suggested the $CWD thing, I forget which.
341 They were right.
342
343 The chdir() override was eliminated in 0.04.
344
345
346 =head1 SEE ALSO
347
348 File::Spec, Cwd, L<perlfunc/chdir>
349
350 =cut
351
352 1;