line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# App::GitFind::PathClassMicro.pm: Only the bits of Path::Class used in App::GitFind |
2
|
|
|
|
|
|
|
# Licensed Artistic 1. |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package App::GitFind::PathClassMicro; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.000002'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
############################################################################## |
9
|
|
|
|
|
|
|
# Overall docs {{1 |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
App::GitFind::PathClassMicro.pm - Only the bits of Path::Class used in App::GitFind |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
This combines pieces of L<Path::Class::Entity>, L<Path::Class::File>, and |
18
|
|
|
|
|
|
|
L<Path::Class::Dir> by Ken Williams. Those are licensed under the same terms |
19
|
|
|
|
|
|
|
as Perl itself. This file is licensed under the Artistic license, and these |
20
|
|
|
|
|
|
|
modifications are believed to be permissible under clause 3(a) of the |
21
|
|
|
|
|
|
|
Artistic License. This file is available for use and modification under the |
22
|
|
|
|
|
|
|
terms of the Artistic License. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
B<Modifications>: This file was modified by Christopher White |
25
|
|
|
|
|
|
|
C<< <cxw@cpan.org> >> to combine files and remove functions I don't use in |
26
|
|
|
|
|
|
|
L<App::GitFind>. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
The remainder of the documentation comes from the individual modules. |
29
|
|
|
|
|
|
|
Multiple packages are combined in this file. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=cut |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Path::Class is not included - we use the functions directly |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# }}}1 |
36
|
|
|
|
|
|
|
############################################################################## |
37
|
|
|
|
|
|
|
# Entity {{1 |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
package App::GitFind::PathClassMicro::Entity; |
40
|
2
|
|
|
2
|
|
12
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
83
|
|
41
|
|
|
|
|
|
|
{ |
42
|
|
|
|
|
|
|
$App::GitFind::PathClassMicro::Entity::VERSION = '0.37'; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
2
|
|
|
2
|
|
8
|
use File::Spec 3.26; |
|
2
|
|
|
|
|
40
|
|
|
2
|
|
|
|
|
36
|
|
46
|
|
|
|
|
|
|
#use File::stat (); |
47
|
2
|
|
|
2
|
|
8
|
use Cwd; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
167
|
|
48
|
|
|
|
|
|
|
#use Carp(); |
49
|
0
|
|
|
0
|
|
0
|
sub croak { require Carp; goto &Carp::croak; } |
|
0
|
|
|
|
|
0
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
use overload |
52
|
|
|
|
|
|
|
( |
53
|
2
|
|
|
|
|
66
|
q[""] => 'stringify', |
54
|
|
|
|
|
|
|
'bool' => 'boolify', |
55
|
|
|
|
|
|
|
fallback => 1, |
56
|
2
|
|
|
2
|
|
10
|
); |
|
2
|
|
|
|
|
3
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub new { |
59
|
0
|
|
|
0
|
|
0
|
my $from = shift; |
60
|
|
|
|
|
|
|
my ($class, $fs_class) = (ref($from) |
61
|
|
|
|
|
|
|
? (ref $from, $from->{file_spec_class}) |
62
|
0
|
0
|
|
|
|
0
|
: ($from, $App::GitFind::PathClassMicro::Foreign)); |
63
|
0
|
|
|
|
|
0
|
return bless {file_spec_class => $fs_class}, $class; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
0
|
|
0
|
sub is_dir { 0 } |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub _spec_class { |
69
|
0
|
|
|
0
|
|
0
|
my ($class, $type) = @_; |
70
|
|
|
|
|
|
|
|
71
|
0
|
0
|
|
|
|
0
|
die "Invalid system type '$type'" unless ($type) = $type =~ /^(\w+)$/; # Untaint |
72
|
0
|
|
|
|
|
0
|
my $spec = "File::Spec::$type"; |
73
|
|
|
|
|
|
|
## no critic |
74
|
0
|
0
|
|
|
|
0
|
eval "require $spec; 1" or die $@; |
75
|
0
|
|
|
|
|
0
|
return $spec; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub new_foreign { |
79
|
0
|
|
|
0
|
|
0
|
my ($class, $type) = (shift, shift); |
80
|
0
|
|
|
|
|
0
|
local $App::GitFind::PathClassMicro::Foreign = $class->_spec_class($type); |
81
|
0
|
|
|
|
|
0
|
return $class->new(@_); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
4
|
50
|
33
|
4
|
|
40
|
sub _spec { (ref($_[0]) && $_[0]->{file_spec_class}) || 'File::Spec' } |
85
|
|
|
|
|
|
|
|
86
|
0
|
|
|
0
|
|
|
sub boolify { 1 } |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub is_absolute { |
89
|
|
|
|
|
|
|
# 5.6.0 has a bug with regexes and stringification that's ticked by |
90
|
|
|
|
|
|
|
# file_name_is_absolute(). Help it along with an explicit stringify(). |
91
|
0
|
|
|
0
|
|
|
$_[0]->_spec->file_name_is_absolute($_[0]->stringify) |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
0
|
|
|
sub is_relative { ! $_[0]->is_absolute } |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub cleanup { |
97
|
0
|
|
|
0
|
|
|
my $self = shift; |
98
|
0
|
|
|
|
|
|
my $cleaned = $self->new( $self->_spec->canonpath("$self") ); |
99
|
0
|
|
|
|
|
|
%$self = %$cleaned; |
100
|
0
|
|
|
|
|
|
return $self; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub resolve { |
104
|
0
|
|
|
0
|
|
|
my $self = shift; |
105
|
0
|
0
|
|
|
|
|
croak($! . " $self") unless -e $self; # No such file or directory |
106
|
0
|
|
|
|
|
|
my $cleaned = $self->new( scalar Cwd::realpath($self->stringify) ); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# realpath() always returns absolute path, kind of annoying |
109
|
0
|
0
|
|
|
|
|
$cleaned = $cleaned->relative if $self->is_relative; |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
%$self = %$cleaned; |
112
|
0
|
|
|
|
|
|
return $self; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub absolute { |
116
|
0
|
|
|
0
|
|
|
my $self = shift; |
117
|
0
|
0
|
|
|
|
|
return $self if $self->is_absolute; |
118
|
0
|
|
|
|
|
|
return $self->new($self->_spec->rel2abs($self->stringify, @_)); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub relative { |
122
|
0
|
|
|
0
|
|
|
my $self = shift; |
123
|
0
|
|
|
|
|
|
return $self->new($self->_spec->abs2rel($self->stringify, @_)); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
0
|
|
|
sub stat { [stat("$_[0]")] } |
127
|
0
|
|
|
0
|
|
|
sub lstat { [lstat("$_[0]")] } |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
0
|
|
|
sub PRUNE { return \&PRUNE; } |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
1; |
132
|
|
|
|
|
|
|
# End of App::GitFind::PathClassMicro::Entity |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head1 NAME |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
App::GitFind::PathClassMicro::Entity - Base class for files and directories |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head1 VERSION |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
version 0.37 |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head1 DESCRIPTION |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
This class is the base class for C<App::GitFind::PathClassMicro::File> and |
145
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro::Dir>, it is not used directly by callers. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head1 AUTHOR |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Ken Williams, kwilliams@cpan.org |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head1 SEE ALSO |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
L<Path::Class> |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# }}}1 |
158
|
|
|
|
|
|
|
############################################################################## |
159
|
|
|
|
|
|
|
# File {{{1 |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
package App::GitFind::PathClassMicro::File; |
162
|
|
|
|
|
|
|
{ |
163
|
|
|
|
|
|
|
$App::GitFind::PathClassMicro::File::VERSION = '0.37'; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
2
|
|
|
2
|
|
1380
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
61
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
#use App::GitFind::PathClassMicro::Dir; |
169
|
|
|
|
|
|
|
# In the same file and has no import() - don't need to `use` it |
170
|
2
|
|
|
2
|
|
11
|
use parent -norequire, qw(App::GitFind::PathClassMicro::Entity); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
11
|
|
171
|
|
|
|
|
|
|
#use Carp; |
172
|
0
|
|
|
0
|
|
|
sub croak { require Carp; goto &Carp::croak; } |
|
0
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
174
|
2
|
|
|
2
|
|
1004
|
use IO::File (); |
|
2
|
|
|
|
|
9134
|
|
|
2
|
|
|
|
|
615
|
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub new { |
177
|
0
|
|
|
0
|
|
|
my $self = shift->SUPER::new; |
178
|
0
|
|
|
|
|
|
my $file = pop(); |
179
|
0
|
|
|
|
|
|
my @dirs = @_; |
180
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
|
my ($volume, $dirs, $base) = $self->_spec->splitpath($file); |
182
|
|
|
|
|
|
|
|
183
|
0
|
0
|
|
|
|
|
if (length $dirs) { |
184
|
0
|
|
|
|
|
|
push @dirs, $self->_spec->catpath($volume, $dirs, ''); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
0
|
0
|
|
|
|
|
$self->{dir} = @dirs ? $self->dir_class->new(@dirs) : undef; |
188
|
0
|
|
|
|
|
|
$self->{file} = $base; |
189
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
return $self; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
0
|
|
|
sub dir_class { "App::GitFind::PathClassMicro::Dir" } |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub as_foreign { |
196
|
0
|
|
|
0
|
|
|
my ($self, $type) = @_; |
197
|
0
|
|
|
|
|
|
local $App::GitFind::PathClassMicro::Foreign = $self->_spec_class($type); |
198
|
0
|
|
|
|
|
|
my $foreign = ref($self)->SUPER::new; |
199
|
0
|
0
|
|
|
|
|
$foreign->{dir} = $self->{dir}->as_foreign($type) if defined $self->{dir}; |
200
|
0
|
|
|
|
|
|
$foreign->{file} = $self->{file}; |
201
|
0
|
|
|
|
|
|
return $foreign; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub stringify { |
205
|
0
|
|
|
0
|
|
|
my $self = shift; |
206
|
0
|
0
|
|
|
|
|
return $self->{file} unless defined $self->{dir}; |
207
|
0
|
|
|
|
|
|
return $self->_spec->catfile($self->{dir}->stringify, $self->{file}); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub dir { |
211
|
0
|
|
|
0
|
|
|
my $self = shift; |
212
|
0
|
0
|
|
|
|
|
return $self->{dir} if defined $self->{dir}; |
213
|
0
|
|
|
|
|
|
return $self->dir_class->new($self->_spec->curdir); |
214
|
|
|
|
|
|
|
} |
215
|
2
|
|
|
2
|
|
2402
|
BEGIN { *parent = \&dir; } |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub volume { |
218
|
0
|
|
|
0
|
|
|
my $self = shift; |
219
|
0
|
0
|
|
|
|
|
return '' unless defined $self->{dir}; |
220
|
0
|
|
|
|
|
|
return $self->{dir}->volume; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub components { |
224
|
0
|
|
|
0
|
|
|
my $self = shift; |
225
|
0
|
0
|
|
|
|
|
croak "Arguments are not currently supported by File->components()" if @_; |
226
|
0
|
|
|
|
|
|
return ($self->dir->components, $self->basename); |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
0
|
|
|
sub basename { shift->{file} } |
230
|
0
|
|
|
0
|
|
|
sub open { IO::File->new(@_) } |
231
|
|
|
|
|
|
|
|
232
|
0
|
0
|
|
0
|
|
|
sub openr { $_[0]->open('r') or croak "Can't read $_[0]: $!" } |
233
|
0
|
0
|
|
0
|
|
|
sub openw { $_[0]->open('w') or croak "Can't write to $_[0]: $!" } |
234
|
0
|
0
|
|
0
|
|
|
sub opena { $_[0]->open('a') or croak "Can't append to $_[0]: $!" } |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub touch { |
237
|
0
|
|
|
0
|
|
|
my $self = shift; |
238
|
0
|
0
|
|
|
|
|
if (-e $self) { |
239
|
0
|
|
|
|
|
|
utime undef, undef, $self; |
240
|
|
|
|
|
|
|
} else { |
241
|
0
|
|
|
|
|
|
$self->openw; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub slurp { |
246
|
0
|
|
|
0
|
|
|
my ($self, %args) = @_; |
247
|
0
|
|
0
|
|
|
|
my $iomode = $args{iomode} || 'r'; |
248
|
0
|
0
|
|
|
|
|
my $fh = $self->open($iomode) or croak "Can't read $self: $!"; |
249
|
|
|
|
|
|
|
|
250
|
0
|
0
|
|
|
|
|
if (wantarray) { |
251
|
0
|
|
|
|
|
|
my @data = <$fh>; |
252
|
0
|
0
|
0
|
|
|
|
chomp @data if $args{chomped} or $args{chomp}; |
253
|
|
|
|
|
|
|
|
254
|
0
|
0
|
|
|
|
|
if ( my $splitter = $args{split} ) { |
255
|
0
|
|
|
|
|
|
@data = map { [ split $splitter, $_ ] } @data; |
|
0
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
|
return @data; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
croak "'split' argument can only be used in list context" |
263
|
0
|
0
|
|
|
|
|
if $args{split}; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
0
|
0
|
0
|
|
|
|
if ($args{chomped} or $args{chomp}) { |
267
|
0
|
|
|
|
|
|
chomp( my @data = <$fh> ); |
268
|
0
|
|
|
|
|
|
return join '', @data; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
local $/; |
273
|
0
|
|
|
|
|
|
return <$fh>; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub spew { |
277
|
0
|
|
|
0
|
|
|
my $self = shift; |
278
|
0
|
|
|
|
|
|
my %args = splice( @_, 0, @_-1 ); |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
0
|
|
|
|
my $iomode = $args{iomode} || 'w'; |
281
|
0
|
0
|
|
|
|
|
my $fh = $self->open( $iomode ) or croak "Can't write to $self: $!"; |
282
|
|
|
|
|
|
|
|
283
|
0
|
0
|
|
|
|
|
if (ref($_[0]) eq 'ARRAY') { |
284
|
|
|
|
|
|
|
# Use old-school for loop to avoid copying. |
285
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @{ $_[0] }; $i++) { |
|
0
|
|
|
|
|
|
|
286
|
0
|
0
|
|
|
|
|
print $fh $_[0]->[$i] |
287
|
|
|
|
|
|
|
or croak "Can't write to $self: $!"; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
else { |
291
|
0
|
0
|
|
|
|
|
print $fh $_[0] |
292
|
|
|
|
|
|
|
or croak "Can't write to $self: $!"; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
0
|
0
|
|
|
|
|
close $fh |
296
|
|
|
|
|
|
|
or croak "Can't write to $self: $!"; |
297
|
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
|
return; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub spew_lines { |
302
|
0
|
|
|
0
|
|
|
my $self = shift; |
303
|
0
|
|
|
|
|
|
my %args = splice( @_, 0, @_-1 ); |
304
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
|
my $content = $_[0]; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# If content is an array ref, appends $/ to each element of the array. |
308
|
|
|
|
|
|
|
# Otherwise, if it is a simple scalar, just appends $/ to that scalar. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
$content |
311
|
|
|
|
|
|
|
= ref( $content ) eq 'ARRAY' |
312
|
0
|
0
|
|
|
|
|
? [ map { $_, $/ } @$content ] |
|
0
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
: "$content$/"; |
314
|
|
|
|
|
|
|
|
315
|
0
|
|
|
|
|
|
return $self->spew( %args, $content ); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub remove { |
319
|
0
|
|
|
0
|
|
|
my $file = shift->stringify; |
320
|
0
|
0
|
|
|
|
|
return unlink $file unless -e $file; # Sets $! correctly |
321
|
0
|
|
|
|
|
|
1 while unlink $file; |
322
|
0
|
|
|
|
|
|
return not -e $file; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub copy_to { |
326
|
0
|
|
|
0
|
|
|
my ($self, $dest) = @_; |
327
|
0
|
0
|
|
|
|
|
if ( eval{ $dest->isa("App::GitFind::PathClassMicro::File")} ) { |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
328
|
0
|
|
|
|
|
|
$dest = $dest->stringify; |
329
|
0
|
0
|
|
|
|
|
croak "Can't copy to file $dest: it is a directory" if -d $dest; |
330
|
0
|
|
|
|
|
|
} elsif ( eval{ $dest->isa("App::GitFind::PathClassMicro::Dir") } ) { |
331
|
0
|
|
|
|
|
|
$dest = $dest->stringify; |
332
|
0
|
0
|
|
|
|
|
croak "Can't copy to directory $dest: it is a file" if -f $dest; |
333
|
0
|
0
|
|
|
|
|
croak "Can't copy to directory $dest: no such directory" unless -d $dest; |
334
|
|
|
|
|
|
|
} elsif ( ref $dest ) { |
335
|
0
|
|
|
|
|
|
croak "Don't know how to copy files to objects of type '".ref($self)."'"; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
require Perl::OSType; |
339
|
0
|
0
|
|
|
|
|
if ( !Perl::OSType::is_os_type('Unix') ) { |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
require File::Copy; |
342
|
0
|
0
|
|
|
|
|
return unless File::Copy::cp($self->stringify, "${dest}"); |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
} else { |
345
|
|
|
|
|
|
|
|
346
|
0
|
0
|
|
|
|
|
return unless (system('cp', $self->stringify, "${dest}") == 0); |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
|
return $self->new($dest); |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub move_to { |
354
|
0
|
|
|
0
|
|
|
my ($self, $dest) = @_; |
355
|
0
|
|
|
|
|
|
require File::Copy; |
356
|
0
|
0
|
|
|
|
|
if (File::Copy::move($self->stringify, "${dest}")) { |
357
|
|
|
|
|
|
|
|
358
|
0
|
|
|
|
|
|
my $new = $self->new($dest); |
359
|
|
|
|
|
|
|
|
360
|
0
|
|
|
|
|
|
$self->{$_} = $new->{$_} foreach (qw/ dir file /); |
361
|
|
|
|
|
|
|
|
362
|
0
|
|
|
|
|
|
return $self; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
} else { |
365
|
|
|
|
|
|
|
|
366
|
0
|
|
|
|
|
|
return; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub traverse { |
372
|
0
|
|
|
0
|
|
|
my $self = shift; |
373
|
0
|
|
|
|
|
|
my ($callback, @args) = @_; |
374
|
0
|
|
|
0
|
|
|
return $self->$callback(sub { () }, @args); |
|
0
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub traverse_if { |
378
|
0
|
|
|
0
|
|
|
my $self = shift; |
379
|
0
|
|
|
|
|
|
my ($callback, $condition, @args) = @_; |
380
|
0
|
|
|
0
|
|
|
return $self->$callback(sub { () }, @args); |
|
0
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
1; |
384
|
|
|
|
|
|
|
# End of App::GitFind::PathClassMicro::File |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=head1 NAME |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
App::GitFind::PathClassMicro::File - Objects representing files |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=head1 VERSION |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
version 0.37 |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=head1 SYNOPSIS |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
use App::GitFind::PathClassMicro; # Exports file() by default |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
my $file = file('foo', 'bar.txt'); # App::GitFind::PathClassMicro::File object |
399
|
|
|
|
|
|
|
my $file = App::GitFind::PathClassMicro::File->new('foo', 'bar.txt'); # Same thing |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Stringifies to 'foo/bar.txt' on Unix, 'foo\bar.txt' on Windows, etc. |
402
|
|
|
|
|
|
|
print "file: $file\n"; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
if ($file->is_absolute) { ... } |
405
|
|
|
|
|
|
|
if ($file->is_relative) { ... } |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
my $v = $file->volume; # Could be 'C:' on Windows, empty string |
408
|
|
|
|
|
|
|
# on Unix, 'Macintosh HD:' on Mac OS |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
$file->cleanup; # Perform logical cleanup of pathname |
411
|
|
|
|
|
|
|
$file->resolve; # Perform physical cleanup of pathname |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
my $dir = $file->dir; # A App::GitFind::PathClassMicro::Dir object |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my $abs = $file->absolute; # Transform to absolute path |
416
|
|
|
|
|
|
|
my $rel = $file->relative; # Transform to relative path |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head1 DESCRIPTION |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
The C<App::GitFind::PathClassMicro::File> class contains functionality for manipulating |
421
|
|
|
|
|
|
|
file names in a cross-platform way. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head1 METHODS |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=over 4 |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=item $file = App::GitFind::PathClassMicro::File->new( <dir1>, <dir2>, ..., <file> ) |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=item $file = file( <dir1>, <dir2>, ..., <file> ) |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Creates a new C<App::GitFind::PathClassMicro::File> object and returns it. The |
432
|
|
|
|
|
|
|
arguments specify the path to the file. Any volume may also be |
433
|
|
|
|
|
|
|
specified as the first argument, or as part of the first argument. |
434
|
|
|
|
|
|
|
You can use platform-neutral syntax: |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
my $file = file( 'foo', 'bar', 'baz.txt' ); |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
or platform-native syntax: |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
my $file = file( 'foo/bar/baz.txt' ); |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
or a mixture of the two: |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
my $file = file( 'foo/bar', 'baz.txt' ); |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
All three of the above examples create relative paths. To create an |
447
|
|
|
|
|
|
|
absolute path, either use the platform native syntax for doing so: |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my $file = file( '/var/tmp/foo.txt' ); |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
or use an empty string as the first argument: |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
my $file = file( '', 'var', 'tmp', 'foo.txt' ); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
If the second form seems awkward, that's somewhat intentional - paths |
456
|
|
|
|
|
|
|
like C</var/tmp> or C<\Windows> aren't cross-platform concepts in the |
457
|
|
|
|
|
|
|
first place, so they probably shouldn't appear in your code if you're |
458
|
|
|
|
|
|
|
trying to be cross-platform. The first form is perfectly fine, |
459
|
|
|
|
|
|
|
because paths like this may come from config files, user input, or |
460
|
|
|
|
|
|
|
whatever. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=item $file->stringify |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
This method is called internally when a C<App::GitFind::PathClassMicro::File> object is |
465
|
|
|
|
|
|
|
used in a string context, so the following are equivalent: |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
$string = $file->stringify; |
468
|
|
|
|
|
|
|
$string = "$file"; |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=item $file->volume |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Returns the volume (e.g. C<C:> on Windows, C<Macintosh HD:> on Mac OS, |
473
|
|
|
|
|
|
|
etc.) of the object, if any. Otherwise, returns the empty string. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=item $file->basename |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Returns the name of the file as a string, without the directory |
478
|
|
|
|
|
|
|
portion (if any). |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item $file->components |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Returns a list of the directory components of this file, followed by |
483
|
|
|
|
|
|
|
the basename. |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Note: unlike C<< $dir->components >>, this method currently does not |
486
|
|
|
|
|
|
|
accept any arguments to select which elements of the list will be |
487
|
|
|
|
|
|
|
returned. It may do so in the future. Currently it throws an |
488
|
|
|
|
|
|
|
exception if such arguments are present. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=item $file->is_dir |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Returns a boolean value indicating whether this object represents a |
494
|
|
|
|
|
|
|
directory. Not surprisingly, C<App::GitFind::PathClassMicro::File> objects always |
495
|
|
|
|
|
|
|
return false, and L<App::GitFind::PathClassMicro::Dir> objects always return true. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item $file->is_absolute |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Returns true or false depending on whether the file refers to an |
500
|
|
|
|
|
|
|
absolute path specifier (like C</usr/local/foo.txt> or C<\Windows\Foo.txt>). |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item $file->is_relative |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Returns true or false depending on whether the file refers to a |
505
|
|
|
|
|
|
|
relative path specifier (like C<lib/foo.txt> or C<.\Foo.txt>). |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=item $file->cleanup |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Performs a logical cleanup of the file path. For instance: |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
my $file = file('/foo//baz/./foo.txt')->cleanup; |
512
|
|
|
|
|
|
|
# $file now represents '/foo/baz/foo.txt'; |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=item $dir->resolve |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
Performs a physical cleanup of the file path. For instance: |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
my $file = file('/foo/baz/../foo.txt')->resolve; |
519
|
|
|
|
|
|
|
# $file now represents '/foo/foo.txt', assuming no symlinks |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
This actually consults the filesystem to verify the validity of the |
522
|
|
|
|
|
|
|
path. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=item $dir = $file->dir |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::Dir> object representing the directory |
527
|
|
|
|
|
|
|
containing this file. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=item $dir = $file->parent |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
A synonym for the C<dir()> method. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=item $abs = $file->absolute |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as an |
536
|
|
|
|
|
|
|
absolute path. An optional argument, given as either a string or a |
537
|
|
|
|
|
|
|
L<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base |
538
|
|
|
|
|
|
|
of relativity - otherwise the current working directory will be used. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=item $rel = $file->relative |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as a |
543
|
|
|
|
|
|
|
relative path. An optional argument, given as either a string or a |
544
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base |
545
|
|
|
|
|
|
|
of relativity - otherwise the current working directory will be used. |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item $foreign = $file->as_foreign($type) |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as it would |
550
|
|
|
|
|
|
|
be specified on a system of type C<$type>. Known types include |
551
|
|
|
|
|
|
|
C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which |
552
|
|
|
|
|
|
|
there is a subclass of C<File::Spec>. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Any generated objects (subdirectories, files, parents, etc.) will also |
555
|
|
|
|
|
|
|
retain this type. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=item $foreign = App::GitFind::PathClassMicro::File->new_foreign($type, @args) |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::File> object representing a file as it would |
560
|
|
|
|
|
|
|
be specified on a system of type C<$type>. Known types include |
561
|
|
|
|
|
|
|
C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which |
562
|
|
|
|
|
|
|
there is a subclass of C<File::Spec>. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
The arguments in C<@args> are the same as they would be specified in |
565
|
|
|
|
|
|
|
C<new()>. |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=item $fh = $file->open($mode, $permissions) |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
Passes the given arguments, including C<$file>, to C<< IO::File->new >> |
570
|
|
|
|
|
|
|
(which in turn calls C<< IO::File->open >> and returns the result |
571
|
|
|
|
|
|
|
as an L<IO::File> object. If the opening |
572
|
|
|
|
|
|
|
fails, C<undef> is returned and C<$!> is set. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=item $fh = $file->openr() |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
A shortcut for |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
$fh = $file->open('r') or croak "Can't read $file: $!"; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item $fh = $file->openw() |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
A shortcut for |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
$fh = $file->open('w') or croak "Can't write to $file: $!"; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=item $fh = $file->opena() |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
A shortcut for |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
$fh = $file->open('a') or croak "Can't append to $file: $!"; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item $file->touch |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
Sets the modification and access time of the given file to right now, |
595
|
|
|
|
|
|
|
if the file exists. If it doesn't exist, C<touch()> will I<make> it |
596
|
|
|
|
|
|
|
exist, and - YES! - set its modification and access time to now. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item $file->slurp() |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
In a scalar context, returns the contents of C<$file> in a string. In |
601
|
|
|
|
|
|
|
a list context, returns the lines of C<$file> (according to how C<$/> |
602
|
|
|
|
|
|
|
is set) as a list. If the file can't be read, this method will throw |
603
|
|
|
|
|
|
|
an exception. |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
If you want C<chomp()> run on each line of the file, pass a true value |
606
|
|
|
|
|
|
|
for the C<chomp> or C<chomped> parameters: |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
my @lines = $file->slurp(chomp => 1); |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
You may also use the C<iomode> parameter to pass in an IO mode to use |
611
|
|
|
|
|
|
|
when opening the file, usually IO layers (though anything accepted by |
612
|
|
|
|
|
|
|
the MODE argument of C<open()> is accepted here). Just make sure it's |
613
|
|
|
|
|
|
|
a I<reading> mode. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
my @lines = $file->slurp(iomode => ':crlf'); |
616
|
|
|
|
|
|
|
my $lines = $file->slurp(iomode => '<:encoding(UTF-8)'); |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
The default C<iomode> is C<r>. |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
Lines can also be automatically split, mimicking the perl command-line |
621
|
|
|
|
|
|
|
option C<-a> by using the C<split> parameter. If this parameter is used, |
622
|
|
|
|
|
|
|
each line will be returned as an array ref. |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
my @lines = $file->slurp( chomp => 1, split => qr/\s*,\s*/ ); |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
The C<split> parameter can only be used in a list context. |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=item $file->spew( $content ); |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
The opposite of L</slurp>, this takes a list of strings and prints them |
631
|
|
|
|
|
|
|
to the file in write mode. If the file can't be written to, this method |
632
|
|
|
|
|
|
|
will throw an exception. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
The content to be written can be either an array ref or a plain scalar. |
635
|
|
|
|
|
|
|
If the content is an array ref then each entry in the array will be |
636
|
|
|
|
|
|
|
written to the file. |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
You may use the C<iomode> parameter to pass in an IO mode to use when |
639
|
|
|
|
|
|
|
opening the file, just like L</slurp> supports. |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
$file->spew(iomode => '>:raw', $content); |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
The default C<iomode> is C<w>. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=item $file->spew_lines( $content ); |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Just like C<spew>, but, if $content is a plain scalar, appends $/ |
648
|
|
|
|
|
|
|
to it, or, if $content is an array ref, appends $/ to each element |
649
|
|
|
|
|
|
|
of the array. |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
Can also take an C<iomode> parameter like C<spew>. Again, the |
652
|
|
|
|
|
|
|
default C<iomode> is C<w>. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item $file->traverse(sub { ... }, @args) |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Calls the given callback on $file. This doesn't do much on its own, |
657
|
|
|
|
|
|
|
but see the associated documentation in L<App::GitFind::PathClassMicro::Dir>. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item $file->remove() |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
This method will remove the file in a way that works well on all |
662
|
|
|
|
|
|
|
platforms, and returns a boolean value indicating whether or not the |
663
|
|
|
|
|
|
|
file was successfully removed. |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
C<remove()> is better than simply calling Perl's C<unlink()> function, |
666
|
|
|
|
|
|
|
because on some platforms (notably VMS) you actually may need to call |
667
|
|
|
|
|
|
|
C<unlink()> several times before all versions of the file are gone - |
668
|
|
|
|
|
|
|
the C<remove()> method handles this process for you. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=item $st = $file->stat() |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Invokes C<< File::stat::stat() >> on this file and returns a |
673
|
|
|
|
|
|
|
L<File::stat> object representing the result. |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
MODIFIED: returns an arrayref of C<stat()> results. |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=item $st = $file->lstat() |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Same as C<stat()>, but if C<$file> is a symbolic link, C<lstat()> |
680
|
|
|
|
|
|
|
stats the link instead of the file the link points to. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
MODIFIED: returns an arrayref of C<lstat()> results. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=item $class = $file->dir_class() |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
Returns the class which should be used to create directory objects. |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
Generally overridden whenever this class is subclassed. |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=item $copy = $file->copy_to( $dest ); |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Copies the C<$file> to C<$dest>. It returns a L<App::GitFind::PathClassMicro::File> |
693
|
|
|
|
|
|
|
object when successful, C<undef> otherwise. |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=item $moved = $file->move_to( $dest ); |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
Moves the C<$file> to C<$dest>, and updates C<$file> accordingly. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
It returns C<$file> is successful, C<undef> otherwise. |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=back |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
=head1 AUTHOR |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
Ken Williams, kwilliams@cpan.org |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=head1 SEE ALSO |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
L<Path::Class>, L<Path::Class::Dir>, L<File::Spec> |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=cut |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# }}}1 |
714
|
|
|
|
|
|
|
############################################################################## |
715
|
|
|
|
|
|
|
# Dir {{{1 |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
package App::GitFind::PathClassMicro::Dir; |
718
|
|
|
|
|
|
|
{ |
719
|
|
|
|
|
|
|
$App::GitFind::PathClassMicro::Dir::VERSION = '0.37'; |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
|
722
|
2
|
|
|
2
|
|
14
|
use strict; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
116
|
|
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
#use App::GitFind::PathClassMicro::File; |
725
|
|
|
|
|
|
|
# In the same file and has no import() - don't need to `use` it |
726
|
|
|
|
|
|
|
#use Carp(); |
727
|
0
|
|
|
0
|
|
|
sub croak { require Carp; goto &Carp::croak; } |
|
0
|
|
|
|
|
|
|
728
|
2
|
|
|
2
|
|
31
|
use parent -norequire, qw(App::GitFind::PathClassMicro::Entity); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
11
|
|
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
#use IO::Dir (); |
731
|
|
|
|
|
|
|
#use File::Path (); |
732
|
|
|
|
|
|
|
#use File::Temp (); |
733
|
2
|
|
|
2
|
|
89
|
use Scalar::Util (); |
|
2
|
|
|
|
|
17
|
|
|
2
|
|
|
|
|
3779
|
|
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
# updir & curdir on the local machine, for screening them out in |
736
|
|
|
|
|
|
|
# children(). Note that they don't respect 'foreign' semantics. |
737
|
|
|
|
|
|
|
my $Updir = __PACKAGE__->_spec->updir; |
738
|
|
|
|
|
|
|
my $Curdir = __PACKAGE__->_spec->curdir; |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
sub new { |
741
|
0
|
|
|
0
|
|
|
my $self = shift->SUPER::new(); |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
# If the only arg is undef, it's probably a mistake. Without this |
744
|
|
|
|
|
|
|
# special case here, we'd return the root directory, which is a |
745
|
|
|
|
|
|
|
# lousy thing to do to someone when they made a mistake. Return |
746
|
|
|
|
|
|
|
# undef instead. |
747
|
0
|
0
|
0
|
|
|
|
return if @_==1 && !defined($_[0]); |
748
|
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
|
my $s = $self->_spec; |
750
|
|
|
|
|
|
|
|
751
|
0
|
0
|
0
|
|
|
|
my $first = (@_ == 0 ? $s->curdir : |
|
|
0
|
|
|
|
|
|
752
|
|
|
|
|
|
|
!ref($_[0]) && $_[0] eq '' ? (shift, $s->rootdir) : |
753
|
|
|
|
|
|
|
shift() |
754
|
|
|
|
|
|
|
); |
755
|
|
|
|
|
|
|
|
756
|
0
|
|
|
|
|
|
$self->{dirs} = []; |
757
|
0
|
0
|
0
|
|
|
|
if ( Scalar::Util::blessed($first) && $first->isa("App::GitFind::PathClassMicro::Dir") ) { |
758
|
0
|
|
|
|
|
|
$self->{volume} = $first->{volume}; |
759
|
0
|
|
|
|
|
|
push @{$self->{dirs}}, @{$first->{dirs}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
else { |
762
|
0
|
|
|
|
|
|
($self->{volume}, my $dirs) = $s->splitpath( $s->canonpath("$first") , 1); |
763
|
0
|
0
|
|
|
|
|
push @{$self->{dirs}}, $dirs eq $s->rootdir ? "" : $s->splitdir($dirs); |
|
0
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
|
766
|
0
|
|
|
|
|
|
push @{$self->{dirs}}, map { |
767
|
0
|
|
|
|
|
|
Scalar::Util::blessed($_) && $_->isa("App::GitFind::PathClassMicro::Dir") |
768
|
0
|
0
|
0
|
|
|
|
? @{$_->{dirs}} |
|
0
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
: $s->splitdir( $s->canonpath($_) ) |
770
|
|
|
|
|
|
|
} @_; |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
|
773
|
0
|
|
|
|
|
|
return $self; |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
|
776
|
0
|
|
|
0
|
|
|
sub file_class { "App::GitFind::PathClassMicro::File" } |
777
|
|
|
|
|
|
|
|
778
|
0
|
|
|
0
|
|
|
sub is_dir { 1 } |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
sub as_foreign { |
781
|
0
|
|
|
0
|
|
|
my ($self, $type) = @_; |
782
|
|
|
|
|
|
|
|
783
|
0
|
|
|
|
|
|
my $foreign = do { |
784
|
0
|
|
|
|
|
|
local $self->{file_spec_class} = $self->_spec_class($type); |
785
|
0
|
|
|
|
|
|
$self->SUPER::new; |
786
|
|
|
|
|
|
|
}; |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
# Clone internal structure |
789
|
0
|
|
|
|
|
|
$foreign->{volume} = $self->{volume}; |
790
|
0
|
|
|
|
|
|
my ($u, $fu) = ($self->_spec->updir, $foreign->_spec->updir); |
791
|
0
|
0
|
|
|
|
|
$foreign->{dirs} = [ map {$_ eq $u ? $fu : $_} @{$self->{dirs}}]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
792
|
0
|
|
|
|
|
|
return $foreign; |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
sub stringify { |
796
|
0
|
|
|
0
|
|
|
my $self = shift; |
797
|
0
|
|
|
|
|
|
my $s = $self->_spec; |
798
|
|
|
|
|
|
|
return $s->catpath($self->{volume}, |
799
|
0
|
|
|
|
|
|
$s->catdir(@{$self->{dirs}}), |
|
0
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
''); |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
|
803
|
0
|
|
|
0
|
|
|
sub volume { shift()->{volume} } |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
sub file { |
806
|
0
|
0
|
|
0
|
|
|
local $App::GitFind::PathClassMicro::Foreign = $_[0]->{file_spec_class} if $_[0]->{file_spec_class}; |
807
|
0
|
|
|
|
|
|
return $_[0]->file_class->new(@_); |
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
|
810
|
0
|
|
|
0
|
|
|
sub basename { shift()->{dirs}[-1] } |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
sub dir_list { |
813
|
0
|
|
|
0
|
|
|
my $self = shift; |
814
|
0
|
|
|
|
|
|
my $d = $self->{dirs}; |
815
|
0
|
0
|
|
|
|
|
return @$d unless @_; |
816
|
|
|
|
|
|
|
|
817
|
0
|
|
|
|
|
|
my $offset = shift; |
818
|
0
|
0
|
|
|
|
|
if ($offset < 0) { $offset = $#$d + $offset + 1 } |
|
0
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
|
820
|
0
|
0
|
|
|
|
|
return wantarray ? @$d[$offset .. $#$d] : $d->[$offset] unless @_; |
|
|
0
|
|
|
|
|
|
821
|
|
|
|
|
|
|
|
822
|
0
|
|
|
|
|
|
my $length = shift; |
823
|
0
|
0
|
|
|
|
|
if ($length < 0) { $length = $#$d + $length + 1 - $offset } |
|
0
|
|
|
|
|
|
|
824
|
0
|
|
|
|
|
|
return @$d[$offset .. $length + $offset - 1]; |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
sub components { |
828
|
0
|
|
|
0
|
|
|
my $self = shift; |
829
|
0
|
|
|
|
|
|
return $self->dir_list(@_); |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
sub subdir { |
833
|
0
|
|
|
0
|
|
|
my $self = shift; |
834
|
0
|
|
|
|
|
|
return $self->new($self, @_); |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
sub parent { |
838
|
0
|
|
|
0
|
|
|
my $self = shift; |
839
|
0
|
|
|
|
|
|
my $dirs = $self->{dirs}; |
840
|
0
|
|
|
|
|
|
my ($curdir, $updir) = ($self->_spec->curdir, $self->_spec->updir); |
841
|
|
|
|
|
|
|
|
842
|
0
|
0
|
|
|
|
|
if ($self->is_absolute) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
843
|
0
|
|
|
|
|
|
my $parent = $self->new($self); |
844
|
0
|
0
|
|
|
|
|
pop @{$parent->{dirs}} if @$dirs > 1; |
|
0
|
|
|
|
|
|
|
845
|
0
|
|
|
|
|
|
return $parent; |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
} elsif ($self eq $curdir) { |
848
|
0
|
|
|
|
|
|
return $self->new($updir); |
849
|
|
|
|
|
|
|
|
850
|
0
|
|
|
|
|
|
} elsif (!grep {$_ ne $updir} @$dirs) { # All updirs |
851
|
0
|
|
|
|
|
|
return $self->new($self, $updir); # Add one more |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
} elsif (@$dirs == 1) { |
854
|
0
|
|
|
|
|
|
return $self->new($curdir); |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
} else { |
857
|
0
|
|
|
|
|
|
my $parent = $self->new($self); |
858
|
0
|
|
|
|
|
|
pop @{$parent->{dirs}}; |
|
0
|
|
|
|
|
|
|
859
|
0
|
|
|
|
|
|
return $parent; |
860
|
|
|
|
|
|
|
} |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
sub relative { |
864
|
|
|
|
|
|
|
# File::Spec->abs2rel before version 3.13 returned the empty string |
865
|
|
|
|
|
|
|
# when the two paths were equal - work around it here. |
866
|
0
|
|
|
0
|
|
|
my $self = shift; |
867
|
0
|
|
|
|
|
|
my $rel = $self->_spec->abs2rel($self->stringify, @_); |
868
|
0
|
0
|
|
|
|
|
return $self->new( length $rel ? $rel : $self->_spec->curdir ); |
869
|
|
|
|
|
|
|
} |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
#sub open { IO::Dir->new(@_) } |
872
|
|
|
|
|
|
|
#sub mkpath { File::Path::mkpath(shift()->stringify, @_) } |
873
|
|
|
|
|
|
|
#sub rmtree { File::Path::rmtree(shift()->stringify, @_) } |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
sub remove { |
876
|
0
|
|
|
0
|
|
|
rmdir( shift() ); |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
sub traverse { |
880
|
0
|
|
|
0
|
|
|
my $self = shift; |
881
|
0
|
|
|
|
|
|
my ($callback, @args) = @_; |
882
|
0
|
|
|
|
|
|
my @children = $self->children; |
883
|
|
|
|
|
|
|
return $self->$callback( |
884
|
|
|
|
|
|
|
sub { |
885
|
0
|
|
|
0
|
|
|
my @inner_args = @_; |
886
|
0
|
|
|
|
|
|
return map { $_->traverse($callback, @inner_args) } @children; |
|
0
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
}, |
888
|
|
|
|
|
|
|
@args |
889
|
0
|
|
|
|
|
|
); |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
sub traverse_if { |
893
|
0
|
|
|
0
|
|
|
my $self = shift; |
894
|
0
|
|
|
|
|
|
my ($callback, $condition, @args) = @_; |
895
|
0
|
|
|
|
|
|
my @children = grep { $condition->($_) } $self->children; |
|
0
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
return $self->$callback( |
897
|
|
|
|
|
|
|
sub { |
898
|
0
|
|
|
0
|
|
|
my @inner_args = @_; |
899
|
0
|
|
|
|
|
|
return map { $_->traverse_if($callback, $condition, @inner_args) } @children; |
|
0
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
}, |
901
|
|
|
|
|
|
|
@args |
902
|
0
|
|
|
|
|
|
); |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
sub recurse { |
906
|
0
|
|
|
0
|
|
|
my $self = shift; |
907
|
0
|
|
|
|
|
|
my %opts = (preorder => 1, depthfirst => 0, @_); |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
my $callback = $opts{callback} |
910
|
0
|
0
|
|
|
|
|
or croak( "Must provide a 'callback' parameter to recurse()" ); |
911
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
|
my @queue = ($self); |
913
|
|
|
|
|
|
|
|
914
|
0
|
|
|
|
|
|
my $visit_entry; |
915
|
|
|
|
|
|
|
my $visit_dir = |
916
|
|
|
|
|
|
|
$opts{depthfirst} && $opts{preorder} |
917
|
|
|
|
|
|
|
? sub { |
918
|
0
|
|
|
0
|
|
|
my $dir = shift; |
919
|
0
|
|
|
|
|
|
my $ret = $callback->($dir); |
920
|
0
|
0
|
0
|
|
|
|
unless( ($ret||'') eq $self->PRUNE ) { |
921
|
0
|
|
|
|
|
|
unshift @queue, $dir->children; |
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
: $opts{preorder} |
925
|
|
|
|
|
|
|
? sub { |
926
|
0
|
|
|
0
|
|
|
my $dir = shift; |
927
|
0
|
|
|
|
|
|
my $ret = $callback->($dir); |
928
|
0
|
0
|
0
|
|
|
|
unless( ($ret||'') eq $self->PRUNE ) { |
929
|
0
|
|
|
|
|
|
push @queue, $dir->children; |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
: sub { |
933
|
0
|
|
|
0
|
|
|
my $dir = shift; |
934
|
0
|
|
|
|
|
|
$visit_entry->($_) foreach $dir->children; |
935
|
0
|
|
|
|
|
|
$callback->($dir); |
936
|
0
|
0
|
0
|
|
|
|
}; |
|
|
0
|
|
|
|
|
|
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
$visit_entry = sub { |
939
|
0
|
|
|
0
|
|
|
my $entry = shift; |
940
|
0
|
0
|
|
|
|
|
if ($entry->is_dir) { $visit_dir->($entry) } # Will call $callback |
|
0
|
|
|
|
|
|
|
941
|
0
|
|
|
|
|
|
else { $callback->($entry) } |
942
|
0
|
|
|
|
|
|
}; |
943
|
|
|
|
|
|
|
|
944
|
0
|
|
|
|
|
|
while (@queue) { |
945
|
0
|
|
|
|
|
|
$visit_entry->( shift @queue ); |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
sub children { |
950
|
0
|
|
|
0
|
|
|
my ($self, %opts) = @_; |
951
|
|
|
|
|
|
|
|
952
|
0
|
0
|
|
|
|
|
my $dh = $self->open or croak( "Can't open directory $self: $!" ); |
953
|
|
|
|
|
|
|
|
954
|
0
|
|
|
|
|
|
my @out; |
955
|
0
|
|
|
|
|
|
while (defined(my $entry = $dh->read)) { |
956
|
0
|
0
|
0
|
|
|
|
next if !$opts{all} && $self->_is_local_dot_dir($entry); |
957
|
0
|
0
|
0
|
|
|
|
next if ($opts{no_hidden} && $entry =~ /^\./); |
958
|
0
|
|
|
|
|
|
push @out, $self->file($entry); |
959
|
0
|
0
|
|
|
|
|
$out[-1] = $self->subdir($entry) if -d $out[-1]; |
960
|
|
|
|
|
|
|
} |
961
|
0
|
|
|
|
|
|
return @out; |
962
|
|
|
|
|
|
|
} |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub _is_local_dot_dir { |
965
|
0
|
|
|
0
|
|
|
my $self = shift; |
966
|
0
|
|
|
|
|
|
my $dir = shift; |
967
|
|
|
|
|
|
|
|
968
|
0
|
|
0
|
|
|
|
return ($dir eq $Updir or $dir eq $Curdir); |
969
|
|
|
|
|
|
|
} |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
sub next { |
972
|
0
|
|
|
0
|
|
|
my $self = shift; |
973
|
0
|
0
|
|
|
|
|
unless ($self->{dh}) { |
974
|
0
|
0
|
|
|
|
|
$self->{dh} = $self->open or croak( "Can't open directory $self: $!" ); |
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
|
977
|
0
|
|
|
|
|
|
my $next = $self->{dh}->read; |
978
|
0
|
0
|
|
|
|
|
unless (defined $next) { |
979
|
0
|
|
|
|
|
|
delete $self->{dh}; |
980
|
|
|
|
|
|
|
## no critic |
981
|
0
|
|
|
|
|
|
return undef; |
982
|
|
|
|
|
|
|
} |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
# Figure out whether it's a file or directory |
985
|
0
|
|
|
|
|
|
my $file = $self->file($next); |
986
|
0
|
0
|
|
|
|
|
$file = $self->subdir($next) if -d $file; |
987
|
0
|
|
|
|
|
|
return $file; |
988
|
|
|
|
|
|
|
} |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
sub subsumes { |
991
|
0
|
0
|
|
0
|
|
|
croak "Too many arguments given to subsumes()" if $#_ > 2; |
992
|
0
|
|
|
|
|
|
my ($self, $other) = @_; |
993
|
0
|
0
|
|
|
|
|
croak( "No second entity given to subsumes()" ) unless defined $other; |
994
|
|
|
|
|
|
|
|
995
|
0
|
0
|
|
|
|
|
$other = $self->new($other) unless eval{$other->isa( "App::GitFind::PathClassMicro::Entity")}; |
|
0
|
|
|
|
|
|
|
996
|
0
|
0
|
|
|
|
|
$other = $other->dir unless $other->is_dir; |
997
|
|
|
|
|
|
|
|
998
|
0
|
0
|
|
|
|
|
if ($self->is_absolute) { |
|
|
0
|
|
|
|
|
|
999
|
0
|
|
|
|
|
|
$other = $other->absolute; |
1000
|
|
|
|
|
|
|
} elsif ($other->is_absolute) { |
1001
|
0
|
|
|
|
|
|
$self = $self->absolute; |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
0
|
|
|
|
|
|
$self = $self->cleanup; |
1005
|
0
|
|
|
|
|
|
$other = $other->cleanup; |
1006
|
|
|
|
|
|
|
|
1007
|
0
|
0
|
0
|
|
|
|
if ($self->volume || $other->volume) { |
1008
|
0
|
0
|
|
|
|
|
return 0 unless $other->volume eq $self->volume; |
1009
|
|
|
|
|
|
|
} |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
# The root dir subsumes everything (but ignore the volume because |
1012
|
|
|
|
|
|
|
# we've already checked that) |
1013
|
0
|
0
|
|
|
|
|
return 1 if "@{$self->{dirs}}" eq "@{$self->new('')->{dirs}}"; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
# The current dir subsumes every relative path (unless starting with updir) |
1016
|
0
|
0
|
|
|
|
|
if ($self eq $self->_spec->curdir) { |
1017
|
0
|
|
|
|
|
|
return $other->{dirs}[0] ne $self->_spec->updir; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
0
|
|
|
|
|
|
my $i = 0; |
1021
|
0
|
|
|
|
|
|
while ($i <= $#{ $self->{dirs} }) { |
|
0
|
|
|
|
|
|
|
1022
|
0
|
0
|
|
|
|
|
return 0 if $i > $#{ $other->{dirs} }; |
|
0
|
|
|
|
|
|
|
1023
|
0
|
0
|
|
|
|
|
return 0 if $self->{dirs}[$i] ne $other->{dirs}[$i]; |
1024
|
0
|
|
|
|
|
|
$i++; |
1025
|
|
|
|
|
|
|
} |
1026
|
0
|
|
|
|
|
|
return 1; |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
sub contains { |
1030
|
0
|
0
|
|
0
|
|
|
croak "Too many arguments given to contains()" if $#_ > 2; |
1031
|
0
|
|
|
|
|
|
my ($self, $other) = @_; |
1032
|
0
|
0
|
|
|
|
|
croak "No second entity given to contains()" unless defined $other; |
1033
|
0
|
0
|
0
|
|
|
|
return unless -d $self and (-e $other or -l $other); |
|
|
|
0
|
|
|
|
|
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# We're going to resolve the path, and don't want side effects on the objects |
1036
|
|
|
|
|
|
|
# so clone them. This also handles strings passed as $other. |
1037
|
0
|
|
|
|
|
|
$self= $self->new($self)->resolve; |
1038
|
0
|
|
|
|
|
|
$other= $self->new($other)->resolve; |
1039
|
|
|
|
|
|
|
|
1040
|
0
|
|
|
|
|
|
return $self->subsumes($other); |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
=for comment |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
sub tempfile { |
1046
|
|
|
|
|
|
|
my $self = shift; |
1047
|
|
|
|
|
|
|
return File::Temp::tempfile(@_, DIR => $self->stringify); |
1048
|
|
|
|
|
|
|
} |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
=cut |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
1; |
1053
|
|
|
|
|
|
|
# End of App::GitFind::PathClassMicro::Dir |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
=head1 NAME |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
App::GitFind::PathClassMicro::Dir - Objects representing directories |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
=head1 VERSION |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
version 0.37 |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
use App::GitFind::PathClassMicro; # Exports dir() by default |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
my $dir = dir('foo', 'bar'); # App::GitFind::PathClassMicro::Dir object |
1068
|
|
|
|
|
|
|
my $dir = App::GitFind::PathClassMicro::Dir->new('foo', 'bar'); # Same thing |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
# Stringifies to 'foo/bar' on Unix, 'foo\bar' on Windows, etc. |
1071
|
|
|
|
|
|
|
print "dir: $dir\n"; |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
if ($dir->is_absolute) { ... } |
1074
|
|
|
|
|
|
|
if ($dir->is_relative) { ... } |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
my $v = $dir->volume; # Could be 'C:' on Windows, empty string |
1077
|
|
|
|
|
|
|
# on Unix, 'Macintosh HD:' on Mac OS |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
$dir->cleanup; # Perform logical cleanup of pathname |
1080
|
|
|
|
|
|
|
$dir->resolve; # Perform physical cleanup of pathname |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
my $file = $dir->file('file.txt'); # A file in this directory |
1083
|
|
|
|
|
|
|
my $subdir = $dir->subdir('george'); # A subdirectory |
1084
|
|
|
|
|
|
|
my $parent = $dir->parent; # The parent directory, 'foo' |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
my $abs = $dir->absolute; # Transform to absolute path |
1087
|
|
|
|
|
|
|
my $rel = $abs->relative; # Transform to relative path |
1088
|
|
|
|
|
|
|
my $rel = $abs->relative('/foo'); # Relative to /foo |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
print $dir->as_foreign('Mac'); # :foo:bar: |
1091
|
|
|
|
|
|
|
print $dir->as_foreign('Win32'); # foo\bar |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
# Iterate with IO::Dir methods: |
1094
|
|
|
|
|
|
|
my $handle = $dir->open; |
1095
|
|
|
|
|
|
|
while (my $file = $handle->read) { |
1096
|
|
|
|
|
|
|
$file = $dir->file($file); # Turn into App::GitFind::PathClassMicro::File object |
1097
|
|
|
|
|
|
|
... |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
# Iterate with App::GitFind::PathClassMicro methods: |
1101
|
|
|
|
|
|
|
while (my $file = $dir->next) { |
1102
|
|
|
|
|
|
|
# $file is a App::GitFind::PathClassMicro::File or App::GitFind::PathClassMicro::Dir object |
1103
|
|
|
|
|
|
|
... |
1104
|
|
|
|
|
|
|
} |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
The C<App::GitFind::PathClassMicro::Dir> class contains functionality for manipulating |
1110
|
|
|
|
|
|
|
directory names in a cross-platform way. |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=head1 METHODS |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
=over 4 |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=item $dir = App::GitFind::PathClassMicro::Dir->new( <dir1>, <dir2>, ... ) |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
=item $dir = dir( <dir1>, <dir2>, ... ) |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
Creates a new C<App::GitFind::PathClassMicro::Dir> object and returns it. The |
1121
|
|
|
|
|
|
|
arguments specify names of directories which will be joined to create |
1122
|
|
|
|
|
|
|
a single directory object. A volume may also be specified as the |
1123
|
|
|
|
|
|
|
first argument, or as part of the first argument. You can use |
1124
|
|
|
|
|
|
|
platform-neutral syntax: |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
my $dir = dir( 'foo', 'bar', 'baz' ); |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
or platform-native syntax: |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
my $dir = dir( 'foo/bar/baz' ); |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
or a mixture of the two: |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
my $dir = dir( 'foo/bar', 'baz' ); |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
All three of the above examples create relative paths. To create an |
1137
|
|
|
|
|
|
|
absolute path, either use the platform native syntax for doing so: |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
my $dir = dir( '/var/tmp' ); |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
or use an empty string as the first argument: |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
my $dir = dir( '', 'var', 'tmp' ); |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
If the second form seems awkward, that's somewhat intentional - paths |
1146
|
|
|
|
|
|
|
like C</var/tmp> or C<\Windows> aren't cross-platform concepts in the |
1147
|
|
|
|
|
|
|
first place (many non-Unix platforms don't have a notion of a "root |
1148
|
|
|
|
|
|
|
directory"), so they probably shouldn't appear in your code if you're |
1149
|
|
|
|
|
|
|
trying to be cross-platform. The first form is perfectly natural, |
1150
|
|
|
|
|
|
|
because paths like this may come from config files, user input, or |
1151
|
|
|
|
|
|
|
whatever. |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
As a special case, since it doesn't otherwise mean anything useful and |
1154
|
|
|
|
|
|
|
it's convenient to define this way, C<< App::GitFind::PathClassMicro::Dir->new() >> (or |
1155
|
|
|
|
|
|
|
C<dir()>) refers to the current directory (C<< File::Spec->curdir >>). |
1156
|
|
|
|
|
|
|
To get the current directory as an absolute path, do C<< |
1157
|
|
|
|
|
|
|
dir()->absolute >>. |
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
Finally, as another special case C<dir(undef)> will return undef, |
1160
|
|
|
|
|
|
|
since that's usually an accident on the part of the caller, and |
1161
|
|
|
|
|
|
|
returning the root directory would be a nasty surprise just asking for |
1162
|
|
|
|
|
|
|
trouble a few lines later. |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
=item $dir->stringify |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
This method is called internally when a C<App::GitFind::PathClassMicro::Dir> object is |
1167
|
|
|
|
|
|
|
used in a string context, so the following are equivalent: |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
$string = $dir->stringify; |
1170
|
|
|
|
|
|
|
$string = "$dir"; |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
=item $dir->volume |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
Returns the volume (e.g. C<C:> on Windows, C<Macintosh HD:> on Mac OS, |
1175
|
|
|
|
|
|
|
etc.) of the directory object, if any. Otherwise, returns the empty |
1176
|
|
|
|
|
|
|
string. |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
=item $dir->basename |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
Returns the last directory name of the path as a string. |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
=item $dir->is_dir |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
Returns a boolean value indicating whether this object represents a |
1185
|
|
|
|
|
|
|
directory. Not surprisingly, L<App::GitFind::PathClassMicro::File> objects always |
1186
|
|
|
|
|
|
|
return false, and C<App::GitFind::PathClassMicro::Dir> objects always return true. |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
=item $dir->is_absolute |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
Returns true or false depending on whether the directory refers to an |
1191
|
|
|
|
|
|
|
absolute path specifier (like C</usr/local> or C<\Windows>). |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=item $dir->is_relative |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
Returns true or false depending on whether the directory refers to a |
1196
|
|
|
|
|
|
|
relative path specifier (like C<lib/foo> or C<./dir>). |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
=item $dir->cleanup |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
Performs a logical cleanup of the file path. For instance: |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
my $dir = dir('/foo//baz/./foo')->cleanup; |
1203
|
|
|
|
|
|
|
# $dir now represents '/foo/baz/foo'; |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
=item $dir->resolve |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
Performs a physical cleanup of the file path. For instance: |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
my $dir = dir('/foo//baz/../foo')->resolve; |
1210
|
|
|
|
|
|
|
# $dir now represents '/foo/foo', assuming no symlinks |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
This actually consults the filesystem to verify the validity of the |
1213
|
|
|
|
|
|
|
path. |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=item $file = $dir->file( <dir1>, <dir2>, ..., <file> ) |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
Returns a L<App::GitFind::PathClassMicro::File> object representing an entry in C<$dir> |
1218
|
|
|
|
|
|
|
or one of its subdirectories. Internally, this just calls C<< |
1219
|
|
|
|
|
|
|
App::GitFind::PathClassMicro::File->new( @_ ) >>. |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
=item $subdir = $dir->subdir( <dir1>, <dir2>, ... ) |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
Returns a new C<App::GitFind::PathClassMicro::Dir> object representing a subdirectory |
1224
|
|
|
|
|
|
|
of C<$dir>. |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
=item $parent = $dir->parent |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
Returns the parent directory of C<$dir>. Note that this is the |
1229
|
|
|
|
|
|
|
I<logical> parent, not necessarily the physical parent. It really |
1230
|
|
|
|
|
|
|
means we just chop off entries from the end of the directory list |
1231
|
|
|
|
|
|
|
until we cain't chop no more. If the directory is relative, we start |
1232
|
|
|
|
|
|
|
using the relative forms of parent directories. |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
The following code demonstrates the behavior on absolute and relative |
1235
|
|
|
|
|
|
|
directories: |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
$dir = dir('/foo/bar'); |
1238
|
|
|
|
|
|
|
for (1..6) { |
1239
|
|
|
|
|
|
|
print "Absolute: $dir\n"; |
1240
|
|
|
|
|
|
|
$dir = $dir->parent; |
1241
|
|
|
|
|
|
|
} |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
$dir = dir('foo/bar'); |
1244
|
|
|
|
|
|
|
for (1..6) { |
1245
|
|
|
|
|
|
|
print "Relative: $dir\n"; |
1246
|
|
|
|
|
|
|
$dir = $dir->parent; |
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
########### Output on Unix ################ |
1250
|
|
|
|
|
|
|
Absolute: /foo/bar |
1251
|
|
|
|
|
|
|
Absolute: /foo |
1252
|
|
|
|
|
|
|
Absolute: / |
1253
|
|
|
|
|
|
|
Absolute: / |
1254
|
|
|
|
|
|
|
Absolute: / |
1255
|
|
|
|
|
|
|
Absolute: / |
1256
|
|
|
|
|
|
|
Relative: foo/bar |
1257
|
|
|
|
|
|
|
Relative: foo |
1258
|
|
|
|
|
|
|
Relative: . |
1259
|
|
|
|
|
|
|
Relative: .. |
1260
|
|
|
|
|
|
|
Relative: ../.. |
1261
|
|
|
|
|
|
|
Relative: ../../.. |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
=item @list = $dir->children |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
Returns a list of L<App::GitFind::PathClassMicro::File> and/or C<App::GitFind::PathClassMicro::Dir> |
1266
|
|
|
|
|
|
|
objects listed in this directory, or in scalar context the number of |
1267
|
|
|
|
|
|
|
such objects. Obviously, it is necessary for C<$dir> to |
1268
|
|
|
|
|
|
|
exist and be readable in order to find its children. |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
Note that the children are returned as subdirectories of C<$dir>, |
1271
|
|
|
|
|
|
|
i.e. the children of F<foo> will be F<foo/bar> and F<foo/baz>, not |
1272
|
|
|
|
|
|
|
F<bar> and F<baz>. |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
Ordinarily C<children()> will not include the I<self> and I<parent> |
1275
|
|
|
|
|
|
|
entries C<.> and C<..> (or their equivalents on non-Unix systems), |
1276
|
|
|
|
|
|
|
because that's like I'm-my-own-grandpa business. If you do want all |
1277
|
|
|
|
|
|
|
directory entries including these special ones, pass a true value for |
1278
|
|
|
|
|
|
|
the C<all> parameter: |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
@c = $dir->children(); # Just the children |
1281
|
|
|
|
|
|
|
@c = $dir->children(all => 1); # All entries |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
In addition, there's a C<no_hidden> parameter that will exclude all |
1284
|
|
|
|
|
|
|
normally "hidden" entries - on Unix this means excluding all entries |
1285
|
|
|
|
|
|
|
that begin with a dot (C<.>): |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
@c = $dir->children(no_hidden => 1); # Just normally-visible entries |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item $abs = $dir->absolute |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as an |
1293
|
|
|
|
|
|
|
absolute path. An optional argument, given as either a string or a |
1294
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base |
1295
|
|
|
|
|
|
|
of relativity - otherwise the current working directory will be used. |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
=item $rel = $dir->relative |
1298
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as a |
1300
|
|
|
|
|
|
|
relative path. An optional argument, given as either a string or a |
1301
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base |
1302
|
|
|
|
|
|
|
of relativity - otherwise the current working directory will be used. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=item $boolean = $dir->subsumes($other) |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
Returns true if this directory spec subsumes the other spec, and false |
1307
|
|
|
|
|
|
|
otherwise. Think of "subsumes" as "contains", but we only look at the |
1308
|
|
|
|
|
|
|
I<specs>, not whether C<$dir> actually contains C<$other> on the |
1309
|
|
|
|
|
|
|
filesystem. |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
The C<$other> argument may be a C<App::GitFind::PathClassMicro::Dir> object, a |
1312
|
|
|
|
|
|
|
L<App::GitFind::PathClassMicro::File> object, or a string. In the latter case, we |
1313
|
|
|
|
|
|
|
assume it's a directory. |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
# Examples: |
1316
|
|
|
|
|
|
|
dir('foo/bar' )->subsumes(dir('foo/bar/baz')) # True |
1317
|
|
|
|
|
|
|
dir('/foo/bar')->subsumes(dir('/foo/bar/baz')) # True |
1318
|
|
|
|
|
|
|
dir('foo/..')->subsumes(dir('foo/../bar)) # True |
1319
|
|
|
|
|
|
|
dir('foo/bar' )->subsumes(dir('bar/baz')) # False |
1320
|
|
|
|
|
|
|
dir('/foo/bar')->subsumes(dir('foo/bar')) # False |
1321
|
|
|
|
|
|
|
dir('foo/..')->subsumes(dir('bar')) # False! Use C<contains> to resolve ".." |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
=item $boolean = $dir->contains($other) |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
Returns true if this directory actually contains C<$other> on the |
1327
|
|
|
|
|
|
|
filesystem. C<$other> doesn't have to be a direct child of C<$dir>, |
1328
|
|
|
|
|
|
|
it just has to be subsumed after both paths have been resolved. |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=item $foreign = $dir->as_foreign($type) |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as it would |
1333
|
|
|
|
|
|
|
be specified on a system of type C<$type>. Known types include |
1334
|
|
|
|
|
|
|
C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which |
1335
|
|
|
|
|
|
|
there is a subclass of C<File::Spec>. |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
Any generated objects (subdirectories, files, parents, etc.) will also |
1338
|
|
|
|
|
|
|
retain this type. |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
=item $foreign = App::GitFind::PathClassMicro::Dir->new_foreign($type, @args) |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as it would |
1343
|
|
|
|
|
|
|
be specified on a system of type C<$type>. Known types include |
1344
|
|
|
|
|
|
|
C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which |
1345
|
|
|
|
|
|
|
there is a subclass of C<File::Spec>. |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
The arguments in C<@args> are the same as they would be specified in |
1348
|
|
|
|
|
|
|
C<new()>. |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
=item @list = $dir->dir_list([OFFSET, [LENGTH]]) |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
Returns the list of strings internally representing this directory |
1353
|
|
|
|
|
|
|
structure. Each successive member of the list is understood to be an |
1354
|
|
|
|
|
|
|
entry in its predecessor's directory list. By contract, C<< |
1355
|
|
|
|
|
|
|
App::GitFind::PathClassMicro->new( $dir->dir_list ) >> should be equivalent to C<$dir>. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
The semantics of this method are similar to Perl's C<splice> or |
1358
|
|
|
|
|
|
|
C<substr> functions; they return C<LENGTH> elements starting at |
1359
|
|
|
|
|
|
|
C<OFFSET>. If C<LENGTH> is omitted, returns all the elements starting |
1360
|
|
|
|
|
|
|
at C<OFFSET> up to the end of the list. If C<LENGTH> is negative, |
1361
|
|
|
|
|
|
|
returns the elements from C<OFFSET> onward except for C<-LENGTH> |
1362
|
|
|
|
|
|
|
elements at the end. If C<OFFSET> is negative, it counts backward |
1363
|
|
|
|
|
|
|
C<OFFSET> elements from the end of the list. If C<OFFSET> and |
1364
|
|
|
|
|
|
|
C<LENGTH> are both omitted, the entire list is returned. |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
In a scalar context, C<dir_list()> with no arguments returns the |
1367
|
|
|
|
|
|
|
number of entries in the directory list; C<dir_list(OFFSET)> returns |
1368
|
|
|
|
|
|
|
the single element at that offset; C<dir_list(OFFSET, LENGTH)> returns |
1369
|
|
|
|
|
|
|
the final element that would have been returned in a list context. |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
=item $dir->components |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
Identical to C<dir_list()>. It exists because there's an analogous |
1374
|
|
|
|
|
|
|
method C<dir_list()> in the C<App::GitFind::PathClassMicro::File> class that also |
1375
|
|
|
|
|
|
|
returns the basename string, so this method lets someone call |
1376
|
|
|
|
|
|
|
C<components()> without caring whether the object is a file or a |
1377
|
|
|
|
|
|
|
directory. |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
=item (REMOVED) $fh = $dir->open() |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
Passes C<$dir> to C<< IO::Dir->open >> and returns the result as an |
1382
|
|
|
|
|
|
|
L<IO::Dir> object. If the opening fails, C<undef> is returned and |
1383
|
|
|
|
|
|
|
C<$!> is set. |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
=item (REMOVED) $dir->mkpath($verbose, $mode) |
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
Passes all arguments, including C<$dir>, to C<< File::Path::mkpath() |
1388
|
|
|
|
|
|
|
>> and returns the result (a list of all directories created). |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
=item (REMOVED) $dir->rmtree($verbose, $cautious) |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
Passes all arguments, including C<$dir>, to C<< File::Path::rmtree() |
1393
|
|
|
|
|
|
|
>> and returns the result (the number of files successfully deleted). |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=item $dir->remove() |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
Removes the directory, which must be empty. Returns a boolean value |
1398
|
|
|
|
|
|
|
indicating whether or not the directory was successfully removed. |
1399
|
|
|
|
|
|
|
This method is mainly provided for consistency with |
1400
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro::File>'s C<remove()> method. |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
=item (REMOVED) $dir->tempfile(...) |
1403
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
An interface to L<File::Temp>'s C<tempfile()> function. Just like |
1405
|
|
|
|
|
|
|
that function, if you call this in a scalar context, the return value |
1406
|
|
|
|
|
|
|
is the filehandle and the file is C<unlink>ed as soon as possible |
1407
|
|
|
|
|
|
|
(which is immediately on Unix-like platforms). If called in a list |
1408
|
|
|
|
|
|
|
context, the return values are the filehandle and the filename. |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
The given directory is passed as the C<DIR> parameter. |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
Here's an example of pretty good usage which doesn't allow race |
1413
|
|
|
|
|
|
|
conditions, won't leave yucky tempfiles around on your filesystem, |
1414
|
|
|
|
|
|
|
etc.: |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
my $fh = $dir->tempfile; |
1417
|
|
|
|
|
|
|
print $fh "Here's some data...\n"; |
1418
|
|
|
|
|
|
|
seek($fh, 0, 0); |
1419
|
|
|
|
|
|
|
while (<$fh>) { do something... } |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
Or in combination with a C<fork>: |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
my $fh = $dir->tempfile; |
1424
|
|
|
|
|
|
|
print $fh "Here's some more data...\n"; |
1425
|
|
|
|
|
|
|
seek($fh, 0, 0); |
1426
|
|
|
|
|
|
|
if ($pid=fork()) { |
1427
|
|
|
|
|
|
|
wait; |
1428
|
|
|
|
|
|
|
} else { |
1429
|
|
|
|
|
|
|
something($_) while <$fh>; |
1430
|
|
|
|
|
|
|
} |
1431
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
=item $dir_or_file = $dir->next() |
1434
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
A convenient way to iterate through directory contents. The first |
1436
|
|
|
|
|
|
|
time C<next()> is called, it will C<open()> the directory and read the |
1437
|
|
|
|
|
|
|
first item from it, returning the result as a C<App::GitFind::PathClassMicro::Dir> or |
1438
|
|
|
|
|
|
|
L<App::GitFind::PathClassMicro::File> object (depending, of course, on its actual |
1439
|
|
|
|
|
|
|
type). Each subsequent call to C<next()> will simply iterate over the |
1440
|
|
|
|
|
|
|
directory's contents, until there are no more items in the directory, |
1441
|
|
|
|
|
|
|
and then the undefined value is returned. For example, to iterate |
1442
|
|
|
|
|
|
|
over all the regular files in a directory: |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
while (my $file = $dir->next) { |
1445
|
|
|
|
|
|
|
next unless -f $file; |
1446
|
|
|
|
|
|
|
my $fh = $file->open('r') or die "Can't read $file: $!"; |
1447
|
|
|
|
|
|
|
... |
1448
|
|
|
|
|
|
|
} |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
If an error occurs when opening the directory (for instance, it |
1451
|
|
|
|
|
|
|
doesn't exist or isn't readable), C<next()> will throw an exception |
1452
|
|
|
|
|
|
|
with the value of C<$!>. |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
=item $dir->traverse( sub { ... }, @args ) |
1455
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
Calls the given callback for the root, passing it a continuation |
1457
|
|
|
|
|
|
|
function which, when called, will call this recursively on each of its |
1458
|
|
|
|
|
|
|
children. The callback function should be of the form: |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
sub { |
1461
|
|
|
|
|
|
|
my ($child, $cont, @args) = @_; |
1462
|
|
|
|
|
|
|
# ... |
1463
|
|
|
|
|
|
|
} |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
For instance, to calculate the number of files in a directory, you |
1466
|
|
|
|
|
|
|
can do this: |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
my $nfiles = $dir->traverse(sub { |
1469
|
|
|
|
|
|
|
my ($child, $cont) = @_; |
1470
|
|
|
|
|
|
|
return sum($cont->(), ($child->is_dir ? 0 : 1)); |
1471
|
|
|
|
|
|
|
}); |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
or to calculate the maximum depth of a directory: |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
my $depth = $dir->traverse(sub { |
1476
|
|
|
|
|
|
|
my ($child, $cont, $depth) = @_; |
1477
|
|
|
|
|
|
|
return max($cont->($depth + 1), $depth); |
1478
|
|
|
|
|
|
|
}, 0); |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
You can also choose not to call the callback in certain situations: |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
$dir->traverse(sub { |
1483
|
|
|
|
|
|
|
my ($child, $cont) = @_; |
1484
|
|
|
|
|
|
|
return if -l $child; # don't follow symlinks |
1485
|
|
|
|
|
|
|
# do something with $child |
1486
|
|
|
|
|
|
|
return $cont->(); |
1487
|
|
|
|
|
|
|
}); |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
=item $dir->traverse_if( sub { ... }, sub { ... }, @args ) |
1490
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
traverse with additional "should I visit this child" callback. |
1492
|
|
|
|
|
|
|
Particularly useful in case examined tree contains inaccessible |
1493
|
|
|
|
|
|
|
directories. |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
Canonical example: |
1496
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
$dir->traverse_if( |
1498
|
|
|
|
|
|
|
sub { |
1499
|
|
|
|
|
|
|
my ($child, $cont) = @_; |
1500
|
|
|
|
|
|
|
# do something with $child |
1501
|
|
|
|
|
|
|
return $cont->(); |
1502
|
|
|
|
|
|
|
}, |
1503
|
|
|
|
|
|
|
sub { |
1504
|
|
|
|
|
|
|
my ($child) = @_; |
1505
|
|
|
|
|
|
|
# Process only readable items |
1506
|
|
|
|
|
|
|
return -r $child; |
1507
|
|
|
|
|
|
|
}); |
1508
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
Second callback gets single parameter: child. Only children for |
1510
|
|
|
|
|
|
|
which it returns true will be processed by the first callback. |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
Remaining parameters are interpreted as in traverse, in particular |
1513
|
|
|
|
|
|
|
C<traverse_if(callback, sub { 1 }, @args> is equivalent to |
1514
|
|
|
|
|
|
|
C<traverse(callback, @args)>. |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
=item $dir->recurse( callback => sub {...} ) |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
Iterates through this directory and all of its children, and all of |
1519
|
|
|
|
|
|
|
its children's children, etc., calling the C<callback> subroutine for |
1520
|
|
|
|
|
|
|
each entry. This is a lot like what the L<File::Find> module does, |
1521
|
|
|
|
|
|
|
and of course C<File::Find> will work fine on L<App::GitFind::PathClassMicro> objects, |
1522
|
|
|
|
|
|
|
but the advantage of the C<recurse()> method is that it will also feed |
1523
|
|
|
|
|
|
|
your callback routine C<App::GitFind::PathClassMicro> objects rather than just pathname |
1524
|
|
|
|
|
|
|
strings. |
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
The C<recurse()> method requires a C<callback> parameter specifying |
1527
|
|
|
|
|
|
|
the subroutine to invoke for each entry. It will be passed the |
1528
|
|
|
|
|
|
|
C<App::GitFind::PathClassMicro> object as its first argument. |
1529
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
C<recurse()> also accepts two boolean parameters, C<depthfirst> and |
1531
|
|
|
|
|
|
|
C<preorder> that control the order of recursion. The default is a |
1532
|
|
|
|
|
|
|
preorder, breadth-first search, i.e. C<< depthfirst => 0, preorder => 1 >>. |
1533
|
|
|
|
|
|
|
At the time of this writing, all combinations of these two parameters |
1534
|
|
|
|
|
|
|
are supported I<except> C<< depthfirst => 0, preorder => 0 >>. |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
C<callback> is normally not required to return any value. If it |
1537
|
|
|
|
|
|
|
returns special constant C<App::GitFind::PathClassMicro::Entity::PRUNE()> (more easily |
1538
|
|
|
|
|
|
|
available as C<< $item->PRUNE >>), no children of analyzed |
1539
|
|
|
|
|
|
|
item will be analyzed (mostly as if you set C<$File::Find::prune=1>). Of course |
1540
|
|
|
|
|
|
|
pruning is available only in C<preorder>, in postorder return value |
1541
|
|
|
|
|
|
|
has no effect. |
1542
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
=item $st = $file->stat() |
1544
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
Invokes C<< File::stat::stat() >> on this directory and returns a |
1546
|
|
|
|
|
|
|
C<File::stat> object representing the result. |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
MODIFIED: returns an arrayref of C<stat()> results. |
1549
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
=item $st = $file->lstat() |
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
Same as C<stat()>, but if C<$file> is a symbolic link, C<lstat()> |
1553
|
|
|
|
|
|
|
stats the link instead of the directory the link points to. |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
MODIFIED: returns an arrayref of C<lstat()> results. |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
=item $class = $file->file_class() |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
Returns the class which should be used to create file objects. |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
Generally overridden whenever this class is subclassed. |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=back |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
=head1 AUTHOR |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
Ken Williams, kwilliams@cpan.org |
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
=head1 SEE ALSO |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
L<Path::Class>, L<Path::Class::File>, L<File::Spec> |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
=cut |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
# }}}1 |
1577
|
|
|
|
|
|
|
# vi: set fdm=marker: # |