line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#======================================================================== |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Badger::Filesystem::Path |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# DESCRIPTION |
6
|
|
|
|
|
|
|
# OO representation of a path in a filesystem, serving as a base class |
7
|
|
|
|
|
|
|
# for file and directories. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# AUTHOR |
10
|
|
|
|
|
|
|
# Andy Wardley |
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
#======================================================================== |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Badger::Filesystem::Path; |
15
|
|
|
|
|
|
|
|
16
|
70
|
|
|
70
|
|
481
|
use File::Spec; |
|
70
|
|
|
|
|
148
|
|
|
70
|
|
|
|
|
5452
|
|
17
|
|
|
|
|
|
|
use Badger::Class |
18
|
70
|
|
|
|
|
1235
|
version => 0.01, |
19
|
|
|
|
|
|
|
debug => 0, |
20
|
|
|
|
|
|
|
base => 'Badger::Filesystem::Base Badger::Exporter', |
21
|
|
|
|
|
|
|
import => 'class', |
22
|
|
|
|
|
|
|
constants => 'HASH ARRAY TRUE', |
23
|
|
|
|
|
|
|
get_methods => 'path name volume directory', |
24
|
|
|
|
|
|
|
utils => 'blessed', |
25
|
|
|
|
|
|
|
as_text => 'path', |
26
|
|
|
|
|
|
|
is_true => 1, |
27
|
|
|
|
|
|
|
constant => { |
28
|
|
|
|
|
|
|
type => 'Path', |
29
|
|
|
|
|
|
|
STAT_PATH => 17, # offset in extended stat fields |
30
|
|
|
|
|
|
|
}, |
31
|
|
|
|
|
|
|
exports => { |
32
|
|
|
|
|
|
|
tags => { fields => '@STAT_FIELDS' }, |
33
|
|
|
|
|
|
|
}, |
34
|
|
|
|
|
|
|
messages => { |
35
|
|
|
|
|
|
|
no_exist => '%s does not exist: %s', |
36
|
|
|
|
|
|
|
bad_stat => '%s cannot be scanned: %s', |
37
|
|
|
|
|
|
|
bad_look => 'No path specified to look %s', |
38
|
|
|
|
|
|
|
missing => 'No %s specified', |
39
|
70
|
|
|
70
|
|
567
|
}; |
|
70
|
|
|
|
|
170
|
|
40
|
|
|
|
|
|
|
|
41
|
70
|
|
|
70
|
|
40211
|
use Badger::Timestamp; |
|
70
|
|
|
|
|
210
|
|
|
70
|
|
|
|
|
819
|
|
42
|
70
|
|
|
70
|
|
1136
|
use Badger::Filesystem; |
|
70
|
|
|
|
|
147
|
|
|
70
|
|
|
|
|
884
|
|
43
|
70
|
|
|
70
|
|
33104
|
use Badger::Filesystem::Directory; |
|
70
|
|
|
|
|
187
|
|
|
70
|
|
|
|
|
866
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
our $FILESYSTEM = 'Badger::Filesystem'; |
47
|
|
|
|
|
|
|
our $TIMESTAMP = 'Badger::Timestamp'; |
48
|
|
|
|
|
|
|
our $MATCH_EXT = qr/\.([^\.]+)$/; # TODO: is this filesystem-specific? |
49
|
|
|
|
|
|
|
our @VDN_FIELDS = @Badger::Filesystem::Base::VDN_FIELDS; |
50
|
|
|
|
|
|
|
our @STAT_FIELDS = qw( device inode mode links user group device_type |
51
|
|
|
|
|
|
|
size atime mtime ctime block_size blocks |
52
|
|
|
|
|
|
|
readable writeable executable owner ); |
53
|
|
|
|
|
|
|
our $STAT_FIELD = { |
54
|
|
|
|
|
|
|
# In here we'll store the map from stat field name to number |
55
|
|
|
|
|
|
|
# device => 0, |
56
|
|
|
|
|
|
|
# inode => 1, |
57
|
|
|
|
|
|
|
# ...etc... |
58
|
|
|
|
|
|
|
}; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
our $TS_FIELD = { |
61
|
|
|
|
|
|
|
# On the left we have the timestamp methods we want to generate as |
62
|
|
|
|
|
|
|
# wrappers around the stat fields listed on the right. |
63
|
|
|
|
|
|
|
created => 'ctime', |
64
|
|
|
|
|
|
|
accessed => 'atime', |
65
|
|
|
|
|
|
|
modified => 'mtime', |
66
|
|
|
|
|
|
|
}; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# generate methods to access stat fields: mode(), atime(), ctime(), etc. |
69
|
|
|
|
|
|
|
my $n = 0; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
class->methods( |
72
|
|
|
|
|
|
|
map { |
73
|
|
|
|
|
|
|
my $m = $n++; # new lexical variable for closure |
74
|
|
|
|
|
|
|
$STAT_FIELD->{ $_ } = $m; # fill in $STAT_FIELD entry |
75
|
56
|
|
|
56
|
|
403
|
$_ => sub { $_[0]->stats->[$m] } # generate subroutine |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
@STAT_FIELDS |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# generate accessed(), created() and modified() methods which return |
81
|
|
|
|
|
|
|
# Badger::Timestamp objects for the atime, ctime and mtime stat values |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
class->methods( |
84
|
|
|
|
|
|
|
map { |
85
|
|
|
|
|
|
|
my $method = $_; # new lexical variable for closure |
86
|
|
|
|
|
|
|
my $stat = $TS_FIELD->{ $_ }; |
87
|
|
|
|
|
|
|
my $statno = $STAT_FIELD->{ $stat }; |
88
|
|
|
|
|
|
|
$method => sub { |
89
|
3
|
|
33
|
3
|
|
52
|
return $_[0]->{ $method } |
90
|
|
|
|
|
|
|
||= $TIMESTAMP->new( $_[0]->stats->[$statno] ) |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
keys %$TS_FIELD |
94
|
|
|
|
|
|
|
); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# define some aliases |
98
|
|
|
|
|
|
|
*is_dir = \&is_directory; |
99
|
|
|
|
|
|
|
*dir = \&directory; |
100
|
|
|
|
|
|
|
*vol = \&volume; # goes up to 11 |
101
|
|
|
|
|
|
|
*ext = \&extension; |
102
|
|
|
|
|
|
|
*base_name = \&basename; |
103
|
|
|
|
|
|
|
*up = \&parent; |
104
|
|
|
|
|
|
|
*meta = \&metadata; |
105
|
|
|
|
|
|
|
*canonical = \&absolute; |
106
|
|
|
|
|
|
|
*perms = \&permissions; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub new { |
110
|
571
|
|
66
|
571
|
1
|
880
|
my $class = shift; $class = ref $class || $class; |
|
571
|
|
|
|
|
1492
|
|
111
|
571
|
|
|
|
|
693
|
my $args; |
112
|
|
|
|
|
|
|
|
113
|
571
|
100
|
|
|
|
964
|
if (@_ == 1) { |
114
|
564
|
50
|
66
|
|
|
1192
|
$args = ref $_[0] eq HASH ? shift |
|
|
100
|
|
|
|
|
|
115
|
|
|
|
|
|
|
: ref $_[0] eq ARRAY || ! ref $_[0] ? { path => shift } |
116
|
|
|
|
|
|
|
: return $class->error_msg( unexpected => arguments => $_[0] => 'hash ref' ) |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
else { |
119
|
7
|
|
|
|
|
31
|
$args = { @_ }; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# allow short aliases for various configuration options, including |
123
|
|
|
|
|
|
|
# those of directory/file subclasses to make life easy for them. |
124
|
571
|
50
|
0
|
|
|
1083
|
$args->{ filesystem } ||= $args->{ fs } if $args->{ fs }; |
125
|
571
|
100
|
33
|
|
|
941
|
$args->{ directory } ||= $args->{ dir } if $args->{ dir }; |
126
|
571
|
50
|
0
|
|
|
930
|
$args->{ volume } ||= $args->{ vol } if $args->{ vol }; |
127
|
571
|
|
|
|
|
970
|
my $self = bless { }, $class; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# maintain a reference to the filesystem that created us, if available, |
130
|
|
|
|
|
|
|
# but don't bother if we didn't get one - we can use the default |
131
|
571
|
100
|
|
|
|
3395
|
$self->{ filesystem } = $args->{ filesystem } if $args->{ filesystem }; |
132
|
571
|
|
|
|
|
1459
|
$self->init($args); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub init { |
136
|
33
|
|
|
33
|
1
|
52
|
my ($self, $config) = @_; |
137
|
33
|
|
50
|
|
|
74
|
my $path = $config->{ path } || return $self->error_msg( missing => 'path' ); |
138
|
33
|
|
|
|
|
60
|
my $fs = $self->filesystem; |
139
|
33
|
|
|
|
|
81
|
$path = $self->{ path } = $fs->join_directory($path); |
140
|
33
|
|
|
|
|
172
|
return $self; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub is_absolute { |
144
|
29
|
|
|
29
|
1
|
50
|
my $self = shift; |
145
|
|
|
|
|
|
|
$self->{ absolute } = $self->filesystem->is_absolute($self->{ path }) |
146
|
29
|
100
|
|
|
|
101
|
unless defined $self->{ absolute }; |
147
|
29
|
|
|
|
|
183
|
return $self->{ absolute }; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub is_relative { |
151
|
2
|
100
|
|
2
|
1
|
15
|
shift->is_absolute ? 0 : 1; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub is_file { |
155
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
156
|
0
|
|
0
|
|
|
0
|
my $defn = $self->filesystem->definitive_read($self->{ path }) || return; |
157
|
0
|
|
|
|
|
0
|
return -f $defn; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub is_directory { |
161
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
162
|
0
|
|
0
|
|
|
0
|
my $defn = $self->filesystem->definitive_read($self->{ path }) || return; |
163
|
0
|
|
|
|
|
0
|
return -d $defn; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub absolute { |
167
|
21
|
|
|
21
|
1
|
83
|
my $self = shift; |
168
|
|
|
|
|
|
|
return $self->is_absolute |
169
|
|
|
|
|
|
|
? $self->{ path } |
170
|
21
|
100
|
|
|
|
78
|
: $self->filesystem->absolute($self->{ path }); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub relative { |
174
|
81
|
|
|
81
|
1
|
142
|
my $self = shift; |
175
|
81
|
|
|
|
|
154
|
my $fs = $self->filesystem; |
176
|
81
|
|
|
|
|
221
|
my $path = $fs->join_directory(@_); |
177
|
|
|
|
|
|
|
# If the path isn't already absolute then we merge it onto our |
178
|
|
|
|
|
|
|
# directory or path if directory is undefined. By calling the |
179
|
|
|
|
|
|
|
# base() method, we allow the file subclass to return its |
180
|
|
|
|
|
|
|
# parent directory so that things Just Work[tm] |
181
|
|
|
|
|
|
|
# $self->debug("relative path: $path is_absolute?\n"); |
182
|
81
|
100
|
|
|
|
262
|
return $fs->is_absolute($path) |
183
|
|
|
|
|
|
|
? $path |
184
|
|
|
|
|
|
|
: $fs->collapse_directory( $fs->join_directory($self->base, $path) ); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub definitive { |
188
|
4
|
|
|
4
|
1
|
17
|
my $self = shift; |
189
|
|
|
|
|
|
|
# use the definitive path from the last stat or fetch anew |
190
|
|
|
|
|
|
|
return |
191
|
|
|
|
|
|
|
($self->{ stats } && $self->{ stats }->[STAT_PATH]) |
192
|
4
|
|
33
|
|
|
41
|
|| $self->filesystem->definitive($self->{ path }); |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub collapse { |
196
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
197
|
0
|
|
|
|
|
0
|
my $fs = $self->filesystem; |
198
|
0
|
|
|
|
|
0
|
$self->{ directory } = $fs->collapse_directory( $self->{ directory } ); |
199
|
0
|
|
|
|
|
0
|
$self->{ path } = $fs->join_path( @$self{ @VDN_FIELDS } ); |
200
|
0
|
|
|
|
|
0
|
return $self; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub above { |
204
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
205
|
0
|
|
|
|
|
0
|
my $this = quotemeta $self->collapse->path; |
206
|
0
|
|
0
|
|
|
0
|
my $that = shift || return $self->error_msg( bad_look => 'above' ); |
207
|
0
|
0
|
0
|
|
|
0
|
$that = $self->new("$that") unless blessed $that && $that->isa(__PACKAGE__); |
208
|
0
|
|
|
|
|
0
|
$that = $that->collapse->path; |
209
|
0
|
0
|
|
|
|
0
|
$self->debug("does $that match /^$this/ ??\n") if $DEBUG; |
210
|
0
|
|
|
|
|
0
|
$that =~ /^$this/; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub below { |
214
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
215
|
0
|
|
0
|
|
|
0
|
my $that = shift || return $self->error_msg( bad_look => 'above' ); |
216
|
0
|
0
|
0
|
|
|
0
|
$that = $self->new("$that") unless blessed $that && $that->isa(__PACKAGE__); |
217
|
0
|
|
|
|
|
0
|
$that->above($self); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub base { |
221
|
3
|
|
|
3
|
1
|
7
|
my $self = shift; |
222
|
3
|
|
33
|
|
|
15
|
return $self->{ directory } || $self->{ path }; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub parent { |
226
|
85
|
|
|
85
|
1
|
154
|
my $self = shift; |
227
|
85
|
|
100
|
|
|
180
|
my $skip = shift || 0; |
228
|
|
|
|
|
|
|
my $parent = $self->{ parent } |
229
|
|
|
|
|
|
|
||= $self->filesystem->directory( |
230
|
85
|
|
66
|
|
|
217
|
$self->{ directory } ||= $self->path_up |
|
|
|
66
|
|
|
|
|
231
|
|
|
|
|
|
|
); |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
return |
234
|
|
|
|
|
|
|
# don't return parents above the root |
235
|
85
|
100
|
|
|
|
478
|
$self->{ path } eq $parent->{ path } ? $self |
|
|
100
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# delegate to parent if there are generations to skip |
237
|
|
|
|
|
|
|
: $skip ? $parent->parent($skip - 1) |
238
|
|
|
|
|
|
|
# otherwise we've found the parent we're looking for |
239
|
|
|
|
|
|
|
: $parent; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub path_up { |
243
|
20
|
|
|
20
|
1
|
29
|
my $self = shift; |
244
|
20
|
|
|
|
|
26
|
my $fs = $self->filesystem; |
245
|
20
|
|
|
|
|
44
|
my $path = $fs->split_directory($self->{ path }); |
246
|
|
|
|
|
|
|
|
247
|
20
|
50
|
|
|
|
41
|
$self->debug("split path [$path] into [", join(', ', @$path), "]\n") |
248
|
|
|
|
|
|
|
if $DEBUG; |
249
|
|
|
|
|
|
|
|
250
|
20
|
100
|
|
|
|
46
|
if (@$path > 1) { |
|
|
50
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# multiple items in path can be relative or absolute - we're not |
252
|
|
|
|
|
|
|
# fussed. e.g. /foo/bar ==> /foo or foo/bar ==> foo |
253
|
17
|
|
|
|
|
23
|
pop(@$path); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
elsif (@$path == 1) { |
256
|
|
|
|
|
|
|
# if there's a single item in a path then it's either a single |
257
|
|
|
|
|
|
|
# relative path item (e.g. 'foo' ==> ['foo']), in which case we |
258
|
|
|
|
|
|
|
# return the current working directory, or it's an empty item |
259
|
|
|
|
|
|
|
# indicating the root directory (e.g. '/' => ['']) in which case we |
260
|
|
|
|
|
|
|
# do nothing, because you can't go up from the root directory. |
261
|
3
|
50
|
|
|
|
10
|
if (length $path->[0]) { |
262
|
3
|
|
|
|
|
9
|
return $fs->cwd; |
263
|
|
|
|
|
|
|
} |
264
|
0
|
|
|
|
|
0
|
$self->not_implemented("going up from relative paths"); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
else { |
267
|
0
|
|
|
|
|
0
|
$self->error("Invalid path (no elements)\n"); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
17
|
|
|
|
|
33
|
return $fs->join_directory($path); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub exists { |
274
|
0
|
|
|
0
|
1
|
0
|
shift->stat; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub must_exist { |
278
|
69
|
|
|
69
|
1
|
118
|
my $self = shift; |
279
|
|
|
|
|
|
|
|
280
|
69
|
100
|
|
|
|
167
|
unless ($self->exists) { |
281
|
4
|
50
|
33
|
|
|
38
|
if (@_ && $_[0]) { |
282
|
4
|
|
|
|
|
12
|
my $flag = shift; |
283
|
|
|
|
|
|
|
# true flag indicates we should attempt to create it |
284
|
4
|
|
|
|
|
35
|
$self->create(@_); # pass any other args, like dir file permission |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
else { |
287
|
0
|
|
|
|
|
0
|
return $self->error_msg( no_exist => $self->type, $self->{ path } ); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
69
|
|
|
|
|
231
|
return $self; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub create { |
294
|
0
|
|
|
0
|
1
|
0
|
shift->not_implemented; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
sub stat { |
298
|
38
|
|
|
38
|
1
|
72
|
my $self = shift->must_exist; |
299
|
|
|
|
|
|
|
my $stats = $self->filesystem->stat_path($self->{ path }) |
300
|
38
|
|
50
|
|
|
82
|
|| return $self->decline_msg( not_found => file => $self->{ path } ); |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# the definitive path can be tagged on the end |
303
|
|
|
|
|
|
|
# $self->{ definitive } = $stats->[STAT_PATH] |
304
|
|
|
|
|
|
|
# if defined $stats->[STAT_PATH]; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
return wantarray |
307
|
38
|
50
|
|
|
|
166
|
? @$stats |
308
|
|
|
|
|
|
|
: $stats; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub stats { |
312
|
59
|
|
66
|
59
|
1
|
166
|
my $stats = $_[0]->{ stats } ||= $_[0]->stat; |
313
|
|
|
|
|
|
|
return wantarray |
314
|
59
|
50
|
|
|
|
284
|
? @$stats |
315
|
|
|
|
|
|
|
: $stats; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub restat { |
319
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
320
|
0
|
|
|
|
|
0
|
delete $self->{ stats }; |
321
|
0
|
|
|
|
|
0
|
delete @$self{ keys %$TS_FIELD }; # timestamps for created, modified, etc. |
322
|
0
|
|
|
|
|
0
|
return $self->stats; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub permissions { |
326
|
1
|
|
|
1
|
1
|
4
|
shift->mode & 0777; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub chmod { |
330
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
331
|
1
|
|
|
|
|
3
|
$self->filesystem->chmod_path($self->{ path }, @_); |
332
|
1
|
|
|
|
|
4
|
return $self; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub basename { |
336
|
3
|
|
|
3
|
1
|
7
|
my $self = shift; |
337
|
3
|
|
|
|
|
10
|
my $name = $self->name; |
338
|
3
|
100
|
|
|
|
10
|
$name = $self->{ path } unless defined $name; |
339
|
3
|
|
|
|
|
40
|
$name =~ s/$MATCH_EXT//g; |
340
|
3
|
|
|
|
|
15
|
return $name; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub extension { |
344
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
345
|
2
|
50
|
|
|
|
39
|
return $self->{ path } =~ $MATCH_EXT |
346
|
|
|
|
|
|
|
? $1 |
347
|
|
|
|
|
|
|
: ''; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub filesystem { |
351
|
1170
|
|
|
1170
|
1
|
1543
|
my $self = shift; |
352
|
1170
|
100
|
|
|
|
2092
|
return $self->class->any_var('FILESYSTEM')->prototype |
353
|
|
|
|
|
|
|
unless ref $self; |
354
|
|
|
|
|
|
|
$self->{ filesystem } |
355
|
1169
|
|
66
|
|
|
4167
|
||= $self->class->any_var('FILESYSTEM')->prototype; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub visit { |
359
|
14
|
|
|
14
|
1
|
64
|
my $self = shift; |
360
|
14
|
|
|
|
|
28
|
my $visitor = $self->filesystem->visitor(@_); |
361
|
14
|
|
|
|
|
43
|
$visitor->visit($self); |
362
|
14
|
|
|
|
|
83
|
return $visitor; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub collect { |
366
|
2
|
|
|
2
|
1
|
6
|
shift->visit(@_)->collect; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub enter { |
370
|
|
|
|
|
|
|
# enter() is a custom accept() method for the entry point of a visitor |
371
|
0
|
|
|
0
|
1
|
0
|
shift->accept; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub accept { |
375
|
0
|
|
|
0
|
1
|
0
|
$_[1]->visit_path($_[0]); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub metadata { |
379
|
3
|
|
|
3
|
1
|
8
|
my $self = shift; |
380
|
3
|
|
100
|
|
|
11
|
my $meta = $self->{ metadata } ||= { }; |
381
|
3
|
100
|
|
|
|
13
|
if (@_ == 1) { |
|
|
100
|
|
|
|
|
|
382
|
1
|
|
|
|
|
6
|
return $meta->{ $_[0] }; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
elsif (@_ > 1) { |
385
|
1
|
|
|
|
|
4
|
while (@_) { |
386
|
1
|
|
|
|
|
2
|
my $key = shift; |
387
|
1
|
|
|
|
|
4
|
$meta->{ $key } = shift; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
} |
390
|
2
|
|
|
|
|
9
|
return $meta; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
1; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=encoding utf8 |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=head1 NAME |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Badger::Filesystem::Path - generic fileystem path object |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head1 SYNOPSIS |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# using Badger::Filesytem constructor subroutine |
404
|
|
|
|
|
|
|
use Badger::Filesystem 'Path'; |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# use native OS-specific paths: |
407
|
|
|
|
|
|
|
$path = Path('/path/to/something'); |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# or generic OS-independant paths |
410
|
|
|
|
|
|
|
$path = Path('path', 'to', 'something'); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# manual object construction |
413
|
|
|
|
|
|
|
use Badger::Filesystem::Path; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# positional arguments |
416
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new('/path/to/something'); |
417
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new(['path', 'to', 'something']); |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# named parameters |
420
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new( |
421
|
|
|
|
|
|
|
path => '/path/to/something' |
422
|
|
|
|
|
|
|
); |
423
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new( |
424
|
|
|
|
|
|
|
path => ['path', 'to', 'something'] |
425
|
|
|
|
|
|
|
); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# path inspection methods |
428
|
|
|
|
|
|
|
$path->path; # current path |
429
|
|
|
|
|
|
|
$path->base; # parent directory or path itself |
430
|
|
|
|
|
|
|
$path->parent; # directory object for base |
431
|
|
|
|
|
|
|
$path->extension # filename .XXX extension |
432
|
|
|
|
|
|
|
$path->basename # filename without .XXX extension |
433
|
|
|
|
|
|
|
$path->is_absolute; # path is absolute |
434
|
|
|
|
|
|
|
$path->is_relative; # path is relative |
435
|
|
|
|
|
|
|
$path->exists; # returns true/false |
436
|
|
|
|
|
|
|
$path->must_exist; # throws error if not |
437
|
|
|
|
|
|
|
@stats = $path->stat; # returns list |
438
|
|
|
|
|
|
|
$stats = $path->stat; # returns list ref |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# path translation methods |
441
|
|
|
|
|
|
|
$path->relative; # relative to cwd |
442
|
|
|
|
|
|
|
$path->relative($base); # relative to $base |
443
|
|
|
|
|
|
|
$path->absolute; # relative to filesystem root |
444
|
|
|
|
|
|
|
$path->definitive; # physical file location |
445
|
|
|
|
|
|
|
$path->collapse; # resolve '.' and '..' in $path |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# path comparison methods |
448
|
|
|
|
|
|
|
$path->above($another_path); # $path is ancestor of $another_path |
449
|
|
|
|
|
|
|
$path->below($another_path); # $path is descendant of $another_path |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head1 INTRODUCTION |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
This is the documentation for the C module. |
454
|
|
|
|
|
|
|
It defines a base class object for the L and |
455
|
|
|
|
|
|
|
L objects which inherit (and in some cases |
456
|
|
|
|
|
|
|
redefine) the methods described below. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
In other words, you should read this documentation first if you're working |
459
|
|
|
|
|
|
|
with L or L objects. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head1 DESCRIPTION |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
The C module defines a base class object for |
464
|
|
|
|
|
|
|
representing paths in a real or virtual file system. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
You can create a generic path object (e.g. to represent a path that doesn't |
467
|
|
|
|
|
|
|
relate to a specific file or directory in a file system), using the C |
468
|
|
|
|
|
|
|
constructor method in L. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
use Badger::Filesystem 'Path'; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
my $path = Path('/path/to/something'); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
However in most cases you'll want to create a file or directory subclass |
475
|
|
|
|
|
|
|
object. The easiest way to do that is like this: |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
use Badger::Filesystem 'File Path'; |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
my $file = File('/path/to/file'); |
480
|
|
|
|
|
|
|
my $dir = Dir('/path/to/dir'); |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
If you're concerned about portability to other operating systems and/or |
483
|
|
|
|
|
|
|
file systems, then you can specify paths as a list or reference to a list |
484
|
|
|
|
|
|
|
of component names. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
my $file = File('path', 'to', 'file'); |
487
|
|
|
|
|
|
|
my $dir = Dir(['path', 'to', 'dir']); |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head1 METHODS |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=head2 new($path) |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Constructor method to create a new C object. |
494
|
|
|
|
|
|
|
The path can be specified as a single positional argument, either as a |
495
|
|
|
|
|
|
|
text string or reference to list of path components. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# single text string |
498
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new('/path/to/something'); |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
# reference to list |
501
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new(['path', 'to', 'something']); |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
It can also be specified as a C named parameter. |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# named parameter list |
506
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new( |
507
|
|
|
|
|
|
|
path => '/path/to/something' |
508
|
|
|
|
|
|
|
); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# reference to hash of named parameter(s) |
511
|
|
|
|
|
|
|
$path = Badger::Filesystem::Path->new({ |
512
|
|
|
|
|
|
|
path => '/path/to/something' |
513
|
|
|
|
|
|
|
}); |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
The constructor method also recognises the C named parameter which |
516
|
|
|
|
|
|
|
can contain a reference to the L object or class that |
517
|
|
|
|
|
|
|
created it. In most cases you can rely on the L to create |
518
|
|
|
|
|
|
|
path objects for you, using either the L |
519
|
|
|
|
|
|
|
method, or the L subroutine. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
use Badger::Filesystem 'FS Path'; |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# FS is alias for 'Badger::Filesystem' |
524
|
|
|
|
|
|
|
# Path() is constructor subrooutine |
525
|
|
|
|
|
|
|
my $path; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# using the path() method |
528
|
|
|
|
|
|
|
$path = FS->path('/path/to/something'); |
529
|
|
|
|
|
|
|
$path = FS->path('path', 'to', 'something'); |
530
|
|
|
|
|
|
|
$path = FS->path(['path', 'to', 'something']); |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
# using the Path() subroutine |
533
|
|
|
|
|
|
|
$path = Path('/path/to/something'); |
534
|
|
|
|
|
|
|
$path = Path('path', 'to', 'something'); |
535
|
|
|
|
|
|
|
$path = Path(['path', 'to', 'something']); |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
The examples that follow will use the C constructor subroutine. |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head2 init(\%config) |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Default initialisation method which subclasses (e.g. |
542
|
|
|
|
|
|
|
L and L) can |
543
|
|
|
|
|
|
|
redefine. |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=head2 path() |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
This method returns the path as a text string. It is called automatically |
548
|
|
|
|
|
|
|
whenever the path object is stringified. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head2 is_absolute() |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Returns true if the path is absolute, false if not. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head2 is_relative() |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
Returns true if the path is relative, false if not. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=head2 absolute($base) |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
Returns an absolute representation of the path, relative to the C<$base> |
561
|
|
|
|
|
|
|
path passed as an argument, or the current working directory if C<$base> |
562
|
|
|
|
|
|
|
is not specified. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
# assume cwd is /foo/bar, |
565
|
|
|
|
|
|
|
my $path = Path('/baz/bam'); |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
print $path->absolute; # /foo/bar/baz/bam |
568
|
|
|
|
|
|
|
print $path->absolute('/wiz'); # /wiz/baz/bam |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=head2 relative($base) |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
Returns a relative representation of the path, relative to the C<$base> |
573
|
|
|
|
|
|
|
path passed as an argument, or the current working directory if C<$base> |
574
|
|
|
|
|
|
|
is not specified. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
# assume cwd is /foo/bar, |
577
|
|
|
|
|
|
|
my $path = Path('/foo/bar/baz/bam'); |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
print $path->relative; # /baz/bam |
580
|
|
|
|
|
|
|
print $path->relative('/foo'); # /bar/baz/bam |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head2 definitive() |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
Returns the definitive representation of the path which in most cases will |
585
|
|
|
|
|
|
|
be the same as the L path. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
However, if you're using a L, |
588
|
|
|
|
|
|
|
then the I path I include the virtual root directory, |
589
|
|
|
|
|
|
|
whereas a the I path will I. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
my $vfs = Badger::Filesystem::Virtual->new( root => '/my/vfs' ); |
592
|
|
|
|
|
|
|
my $path = $vfs->file('/foo/bar'); |
593
|
|
|
|
|
|
|
print $path->absolute; # /foo/bar |
594
|
|
|
|
|
|
|
print $path->definitive; # /my/vfs/foo/bar |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=head2 canonical() |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
This method returns the canonical representation of the path. In most cases |
599
|
|
|
|
|
|
|
this is the same as the absolute path (in fact the base class aliases the |
600
|
|
|
|
|
|
|
C method directly to the L method). |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
print Path('foo')->canonical; # /your/current/path/foo |
603
|
|
|
|
|
|
|
print Path('/foo/bar')->canonical; # /foo/bar |
604
|
|
|
|
|
|
|
print Path('/foo/bar/')->canonical; # /foo/bar |
605
|
|
|
|
|
|
|
print Path('/foo/bar.txt')->canonical; # /foo/bar.txt |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
Note that the C base class will I any |
608
|
|
|
|
|
|
|
trailing slashes (or whatever the appropriate directory separator is for your |
609
|
|
|
|
|
|
|
filesystem) from the end of an absolute path. |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
In the case of directories, implemented by the |
612
|
|
|
|
|
|
|
L subclass, a trailing slash (or relevant |
613
|
|
|
|
|
|
|
separator for your filesystem) will be added. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
print Dir('/foo/bar')->canonical; # /foo/bar/ |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
This is done by delegation to the |
618
|
|
|
|
|
|
|
L method in |
619
|
|
|
|
|
|
|
L. |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=head2 collapse() |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
Reduces the path to its simplest form by resolving and removing any C<.> |
624
|
|
|
|
|
|
|
(current directory) and C<..> (parent directory) components (or whatever the |
625
|
|
|
|
|
|
|
corresponding tokens are for the current and parent directories of your |
626
|
|
|
|
|
|
|
filesystem). |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
my $path = Path('/foo/bar/../baz')->collapse; |
629
|
|
|
|
|
|
|
print $path; # /foo/baz |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
See the L method in |
632
|
|
|
|
|
|
|
L for further information. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head2 above($child) |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Returns true if the path is "above" the C<$child> path passed as an argument. |
637
|
|
|
|
|
|
|
Formally, we say that the path is an I of C<$child> meaning that it |
638
|
|
|
|
|
|
|
is the parent directory, or grand-parent, or great-grand-parent, and so on. |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
my $parent = Path('/foo/bar'); |
641
|
|
|
|
|
|
|
my $child = Path('/foo/bar/baz'); |
642
|
|
|
|
|
|
|
$parent->above($child); # true |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
This is implemented as a simple prefix match. That is, the parent path must |
645
|
|
|
|
|
|
|
appear at the start of the child path. Consequently, this method will not |
646
|
|
|
|
|
|
|
account for symbolic links or other similar filesystem features, and it may |
647
|
|
|
|
|
|
|
not work properly on systems that don't follow this convention (although there |
648
|
|
|
|
|
|
|
are none that I'm aware of). |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=head2 below($parent) |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
Returns true if the path is "below" the C<$parent> path passed as an argument. |
653
|
|
|
|
|
|
|
Formally, we say that the path is a I of C<$parent> meaning that it |
654
|
|
|
|
|
|
|
is an immediate sub-directory, or sub-sub-directory, and so on. |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my $parent = Path('/foo/bar'); |
657
|
|
|
|
|
|
|
my $child = Path('/foo/bar/baz'); |
658
|
|
|
|
|
|
|
$child->below($parent); # true |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
Like L, this is implemented using a simple prefix match. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=head2 base() |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Returns the base directory of a path. For L and |
665
|
|
|
|
|
|
|
L objects, this method will return the complete |
666
|
|
|
|
|
|
|
path. |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
print Path('/foo/bar')->base; # /foo/bar |
669
|
|
|
|
|
|
|
print Directory('/foo/bar')->base; # /foo/bar |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
However the L module returns the parent directory in |
672
|
|
|
|
|
|
|
which the file is located. |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
print File('/foo/bar')->base; # /foo |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=head2 parent($skip_generations) / up($skip_generations) |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
Returns a L object representing the parent |
679
|
|
|
|
|
|
|
directory for a path. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Path->('/foo/bar')->parent; # path object for /foo |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
A numerical argument can be provided to indicate the number of generation |
684
|
|
|
|
|
|
|
you want to skip. A value of C<0> is the same as providing no argument - it |
685
|
|
|
|
|
|
|
returns the parent. A value of C<1> skips the parent and returns the |
686
|
|
|
|
|
|
|
grand-parent, and so on. |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
Path->('/foo/bar/baz/bam')->parent(2); # path object for /foo |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
The root directory will be returned if you try to skip too many generations. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Path->('/foo/bar/baz/bam')->parent(20); # path object for / |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head2 path_up() |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
This returns a text string representing the parent of a path. If the path |
697
|
|
|
|
|
|
|
contains multiple items (e.g. '/foo/bar' or 'foo/bar') then the last item |
698
|
|
|
|
|
|
|
will be removed (e.g. resulting in '/foo' or 'foo' respectively). If an |
699
|
|
|
|
|
|
|
absolute path contains one item or none (e.g. '/foo' or '/') then the |
700
|
|
|
|
|
|
|
root directory ('/') will be returned. A relative path with only one item |
701
|
|
|
|
|
|
|
(e.g. 'foo') is assumed to be relative to the current working directory |
702
|
|
|
|
|
|
|
which will be returned (e.g. '/path/to/current/dir'). |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head2 exists() |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
Returns true if the path exists in the filesystem (e.g. as a file, directory, |
707
|
|
|
|
|
|
|
or some other entry), or false if not. |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
if ($path->exists) { |
710
|
|
|
|
|
|
|
print "$path already exists\n"; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
else { |
713
|
|
|
|
|
|
|
print "Creating $path\n"; |
714
|
|
|
|
|
|
|
# ...etc... |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 must_exist($create) |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Checks that the path exists (by calling L) and throws an error |
720
|
|
|
|
|
|
|
if it doesn't. |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
$path->must_exist; # no need to check return value |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
The C<$create> flag can be set to have it attempt to L itself if it |
725
|
|
|
|
|
|
|
doesn't already exist. However, this only makes sense for file and directory |
726
|
|
|
|
|
|
|
subclasses and not base class paths. |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
$dir->must_exist(1); # create if it doesn't |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=head2 create() |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
In the base class this will method will throw an error. You can't physically |
733
|
|
|
|
|
|
|
create an abstract path unless you know what kind of concrete entity (e.g. |
734
|
|
|
|
|
|
|
file or directory) it maps onto. In other words, the L method will |
735
|
|
|
|
|
|
|
only work for the L and |
736
|
|
|
|
|
|
|
L subclasses. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
$path->create; # FAIL |
739
|
|
|
|
|
|
|
$dir->create; # OK |
740
|
|
|
|
|
|
|
$file->create; # OK |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head2 chmod($perms) |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
This method changes the file permissions on a file or directory. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
$file->chmod(0775); |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=head2 stat() |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Performs a filesystem C on the path and returns a list (in list |
751
|
|
|
|
|
|
|
context), or a reference to a list (in scalar context) containing the 13 |
752
|
|
|
|
|
|
|
information elements. |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
@list = $path->stat; # list context |
755
|
|
|
|
|
|
|
$list = $path->stat; # scalar context |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
A summary of the fields is shown below. See C for complete |
758
|
|
|
|
|
|
|
details. Each of the individual fields can also be accessed via their own |
759
|
|
|
|
|
|
|
methods, also listed in the table. |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
Field Method Description |
762
|
|
|
|
|
|
|
------------------------------------------------------------------------ |
763
|
|
|
|
|
|
|
0 device() device number of filesystem |
764
|
|
|
|
|
|
|
1 inoode() inode number |
765
|
|
|
|
|
|
|
2 mode() file mode (type and permissions) |
766
|
|
|
|
|
|
|
3 links() number of (hard) links to the file |
767
|
|
|
|
|
|
|
4 user() numeric user ID of file’s owner |
768
|
|
|
|
|
|
|
5 group() numeric group ID of file’s owner |
769
|
|
|
|
|
|
|
6 device_type() the device identifier (special files only) |
770
|
|
|
|
|
|
|
7 size() total size of file, in bytes |
771
|
|
|
|
|
|
|
8 atime() last access time in seconds since the epoch |
772
|
|
|
|
|
|
|
9 mtime() last modify time in seconds since the epoch |
773
|
|
|
|
|
|
|
10 ctime() inode change time in seconds since the epoch (*) |
774
|
|
|
|
|
|
|
11 block_size() preferred block size for file system I/O |
775
|
|
|
|
|
|
|
12 blocks() actual number of blocks allocated |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
In addition to those that are returned by Perl's inbuilt C function, |
778
|
|
|
|
|
|
|
this method returns four additional flags. |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
13 readable() file is readable by current process |
781
|
|
|
|
|
|
|
14 writeable() file is writeable by current process |
782
|
|
|
|
|
|
|
15 executable() file is executable by current process |
783
|
|
|
|
|
|
|
16 owner() file is owned by current process |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=head2 stats() |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
A wrapper around the L method which caches the results to avoid |
788
|
|
|
|
|
|
|
making repeated filesystem calls. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
@list = $path->stats; # list context |
791
|
|
|
|
|
|
|
$list = $path->stats; # scalar context |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
Note that the L, L and L methods also |
794
|
|
|
|
|
|
|
cache the L objects they create to represent the |
795
|
|
|
|
|
|
|
access, creation and modification times respectively. |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=head2 restat() |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
Clears any cached values stored by the L, L, |
800
|
|
|
|
|
|
|
L and L methods and calls L to reload |
801
|
|
|
|
|
|
|
(and re-cache) the data from a L call. |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
=head2 device() |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
Returns the device number for the file. See L. |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=head2 inode() |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
Returns the inode number for the file. See L. |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
=head2 mode() |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
Returns the file mode for the file. Note that this contains both the |
814
|
|
|
|
|
|
|
file type and permissions. See L. |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=head2 permissions() / perms() |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
Returns the file permissions. This is equivalent to |
819
|
|
|
|
|
|
|
C<< $file->mode & 0777 >>. |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=head2 links() |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
Returns the number of hard links to the file. See L. |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=head2 user() |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
Returns the numeric user ID of the file's owner. See L. |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=head2 group() |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Returns the numeric group ID of the file's group. See L. |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=head2 device_type() |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
Returns the device identifier (for special files only). See L. |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=head2 size() |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
Returns the total size of the file in bytes. See L. |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=head2 atime() |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
Returns the time (in seconds since the epoch) that the file was last accessed. |
844
|
|
|
|
|
|
|
See L. |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=head2 accessed() |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
Returns a L object for the L value. This object |
849
|
|
|
|
|
|
|
will auto-stringify to produce an ISO-8601 formatted date. You can also |
850
|
|
|
|
|
|
|
call various methods to access different parts of the time and/or date. |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
print $file->accessed; # 2009/04/20 16:25:00 |
853
|
|
|
|
|
|
|
print $file->accessed->date; # 2009/04/20 |
854
|
|
|
|
|
|
|
print $file->accessed->year; # 2009 |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
=head2 mtime() |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
Returns the time (in seconds since the epoch) that the file was last modified. |
859
|
|
|
|
|
|
|
See L. |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=head2 modified() |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
Returns a L object for the L value. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
print $file->modified; # 2009/04/20 16:25:00 |
866
|
|
|
|
|
|
|
print $file->modified->time; # 16:25:0 |
867
|
|
|
|
|
|
|
print $file->modified->hour; # 16 |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=head2 ctime() |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
Returns the time (in seconds since the epoch) that the file was created. See |
872
|
|
|
|
|
|
|
L. |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
=head2 created() |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
Returns a L object for the L value. |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
print $file->created; # 2009/04/20 16:25:00 |
879
|
|
|
|
|
|
|
print $file->created->date; # 2009/04/20 |
880
|
|
|
|
|
|
|
print $file->created->time; # 16:25:00 |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=head2 block_size() |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
Returns the preferred block size for file system I/O on the file. See |
885
|
|
|
|
|
|
|
L. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=head2 blocks() |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
Returns the actual number of blocks allocated to the file. See L. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=head2 readable() |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
Returns a true value if the file is readable by the current user (i.e. the |
894
|
|
|
|
|
|
|
owner of the current process), false if not. See L. |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
=head2 writeable() |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
Returns a true value if the file is writeable by the current user (i.e. the |
899
|
|
|
|
|
|
|
owner of the current process), false if not. See L. |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
=head2 executable() |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
Returns a true value if the file is executable by the current user (i.e. the |
904
|
|
|
|
|
|
|
owner of the current process), false if not. See L. |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
=head2 owner() |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
Returns a true value if the file is owned by the current user (i.e. the |
909
|
|
|
|
|
|
|
owner of the current process), false if not. See L. |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=head2 filesystem() |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
Returns a reference to a L object, or the name of the |
914
|
|
|
|
|
|
|
filesystem class (e.g. L or a subclass) that created |
915
|
|
|
|
|
|
|
the path object. If this is undefined then the default value defined in |
916
|
|
|
|
|
|
|
the L<$FILESYSTEM> class variable is returned. Unless you've changed it, |
917
|
|
|
|
|
|
|
or re-defined it in a subclass, this value will be C. |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
The end result is that you can use the C method to access a |
920
|
|
|
|
|
|
|
L object or class through which you can perform other |
921
|
|
|
|
|
|
|
filesystem related operations. This is used internally by a number of |
922
|
|
|
|
|
|
|
method. |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
# access filesystem via existing path |
925
|
|
|
|
|
|
|
$path->filesystem->dir('/a/new/directory/object'); |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
# same as |
928
|
|
|
|
|
|
|
Badger::Filesystem->dir('/a/new/directory/object'); |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
=head2 visit($visitor) |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
Entry point for a filesystem visitor to visit a filesystem path. A |
933
|
|
|
|
|
|
|
reference to a L object (or subclass) should |
934
|
|
|
|
|
|
|
be passed as the first argument. |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
use Badger::Filesystem::Visitor; |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
my $visitor = Badger::Filesystem::Visitor->new( recurse => 1 ); |
939
|
|
|
|
|
|
|
$path->visit($visitor); |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
Alternately, a list or reference to a hash array of named parameters may be |
942
|
|
|
|
|
|
|
provided. These will be used to instantiate a new |
943
|
|
|
|
|
|
|
L object (via the L |
944
|
|
|
|
|
|
|
L method) which will then be |
945
|
|
|
|
|
|
|
applied to the path. If no arguments are passed then a visitor is created |
946
|
|
|
|
|
|
|
with a default configuration. |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
# either list of named params |
949
|
|
|
|
|
|
|
$path->visit( recurse => 1 ); |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
# or reference to hash array |
952
|
|
|
|
|
|
|
$path->visit({ recurse => 1}); |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
The method then calls the visitor |
955
|
|
|
|
|
|
|
L passing C<$self> as an argument |
956
|
|
|
|
|
|
|
to begin the visit. |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=head2 accept($visitor) |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
This method is called to dispatch a visitor to the correct method for a |
961
|
|
|
|
|
|
|
filesystem object. In the L base class, it calls the |
962
|
|
|
|
|
|
|
visitor L method, |
963
|
|
|
|
|
|
|
passing the C<$self> object reference as an argument. Subclasses redefine this |
964
|
|
|
|
|
|
|
method to call other visitor methods. |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
=head2 enter($visitor) |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
This is a special case of the L method which subclasses (e.g. |
969
|
|
|
|
|
|
|
L) use to differentiate between the |
970
|
|
|
|
|
|
|
initial entry point of a visitor and subsequent visits to directories |
971
|
|
|
|
|
|
|
contained therein. In the base class it simply delegates to the L |
972
|
|
|
|
|
|
|
method. |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
=head2 collect(\%params) |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
This is a short-cut to call the L method and then the |
977
|
|
|
|
|
|
|
L method on the |
978
|
|
|
|
|
|
|
L object returned. |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# short form |
981
|
|
|
|
|
|
|
my @items = $path->collect( files => 1, dirs => 0 ); |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
# long form |
984
|
|
|
|
|
|
|
my @items = $path->visit( files => 1, dirs => 0 )->collect; |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
=head2 metadata() / meta() |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
This method allows you to associate metadata with a path. The method |
989
|
|
|
|
|
|
|
accepts multiple arguments to set metadata: |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
$path->metadata( title => 'An Example', author => 'Arthur Dent' ); |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
It also accepts a single argument to fetch a metadata item: |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
print $path->metadata('author'); # Arthur Dent |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
You can also call it without arguments. The method returns a reference |
998
|
|
|
|
|
|
|
to a hash array of metadata items. |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
my $meta = $path->metadata; |
1001
|
|
|
|
|
|
|
print $meta->{ author }; # Arthur Dent |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
=head1 STUB METHODS |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
The following methods serve little or no purpose in the |
1006
|
|
|
|
|
|
|
C base class. They are redefined by the |
1007
|
|
|
|
|
|
|
C and C modules |
1008
|
|
|
|
|
|
|
to do the right thing. |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
=head2 is_file() |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
This method always returns false in the C base |
1013
|
|
|
|
|
|
|
class. The C subclass redefines this to return |
1014
|
|
|
|
|
|
|
true. NOTE: this may be changed to examine the filesystem and return true |
1015
|
|
|
|
|
|
|
if the path references a file. |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
=head2 is_directory() / is_dir() |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
This method always returns false in the C base |
1020
|
|
|
|
|
|
|
class. The C subclass redefines this to return |
1021
|
|
|
|
|
|
|
true. NOTE: this may be changed to examine the filesystem and return true |
1022
|
|
|
|
|
|
|
if the path references a file. |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
=head2 volume() / vol() |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
Returns any volume defined as part of the path. This method does nothing in |
1027
|
|
|
|
|
|
|
the C base class. |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
=head2 directory() / dir() |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
Returns the directory portion of a path. This method does nothing in the |
1032
|
|
|
|
|
|
|
C base class. |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
=head2 name() |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
Returns the file name portion of a path. This method does nothing in the |
1037
|
|
|
|
|
|
|
C base class. |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
=head2 extension() / ext() |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
Returns any file extension portion following the final C<.> in the path. |
1042
|
|
|
|
|
|
|
This works in the C base class by looking at the |
1043
|
|
|
|
|
|
|
full path. |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
print Path('/foo/bar.txt')->extension; # txt |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
=head2 basename() / base_name() |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
Returns the filename I the file extension following the final |
1050
|
|
|
|
|
|
|
C<.> in the path. This works (for some definition of "works") in the |
1051
|
|
|
|
|
|
|
C base class by looking at the path L, |
1052
|
|
|
|
|
|
|
if defined, or the full C if not. Note that this will produce |
1053
|
|
|
|
|
|
|
unexpected results in some cases due to the fact that the base class |
1054
|
|
|
|
|
|
|
does not define a value for L. e.g. |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
print Path('/foo/bar.txt')->basename; # /foo/bar |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
However, in most cases you would be using this through a |
1059
|
|
|
|
|
|
|
L subclass which will product the correct |
1060
|
|
|
|
|
|
|
results. |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
print File('/foo/bar.txt')->basename; # bar |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
=head1 AUTHOR |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
Andy Wardley L |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
Copyright (C) 2005-2009 Andy Wardley. All rights reserved. |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
The C modules are built around a number of existing |
1075
|
|
|
|
|
|
|
Perl modules, including L, L, L, L, |
1076
|
|
|
|
|
|
|
L and draw heavily on ideas in L. |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
Please see the L |
1079
|
|
|
|
|
|
|
in L for further information. |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
=head1 SEE ALSO |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
L, |
1084
|
|
|
|
|
|
|
L, |
1085
|
|
|
|
|
|
|
L, |
1086
|
|
|
|
|
|
|
L. |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
=cut |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
# Local Variables: |
1091
|
|
|
|
|
|
|
# mode: Perl |
1092
|
|
|
|
|
|
|
# perl-indent-level: 4 |
1093
|
|
|
|
|
|
|
# indent-tabs-mode: nil |
1094
|
|
|
|
|
|
|
# End: |
1095
|
|
|
|
|
|
|
# |
1096
|
|
|
|
|
|
|
# vim: expandtab shiftwidth=4: |
1097
|
|
|
|
|
|
|
# TextMate: should support split pane editing |