line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package File::Fu::File; |
2
|
|
|
|
|
|
|
$VERSION = v0.0.8; |
3
|
|
|
|
|
|
|
|
4
|
13
|
|
|
13
|
|
67
|
use warnings; |
|
13
|
|
|
|
|
28
|
|
|
13
|
|
|
|
|
471
|
|
5
|
13
|
|
|
13
|
|
123
|
use strict; |
|
13
|
|
|
|
|
24
|
|
|
13
|
|
|
|
|
361
|
|
6
|
13
|
|
|
13
|
|
71
|
use Carp; |
|
13
|
|
|
|
|
20
|
|
|
13
|
|
|
|
|
764
|
|
7
|
|
|
|
|
|
|
|
8
|
13
|
|
|
13
|
|
15031
|
use IO::File (); |
|
13
|
|
|
|
|
268316
|
|
|
13
|
|
|
|
|
420
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
File::Fu::File - a filename object |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 SYNOPSIS |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
use File::Fu; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my $file = File::Fu->file("path/to/file"); |
19
|
|
|
|
|
|
|
$file %= '.extension'; |
20
|
|
|
|
|
|
|
$file->e and warn "$file exists"; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$file->l and warn "$file is a link to ", $file->readlink; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=cut |
25
|
|
|
|
|
|
|
|
26
|
13
|
|
|
13
|
|
123
|
use base 'File::Fu::Base'; |
|
13
|
|
|
|
|
34
|
|
|
13
|
|
|
|
|
12152
|
|
27
|
|
|
|
|
|
|
|
28
|
13
|
|
|
13
|
|
15355
|
use Class::Accessor::Classy; |
|
13
|
|
|
|
|
71146
|
|
|
13
|
|
|
|
|
121
|
|
29
|
|
|
|
|
|
|
lv 'file'; |
30
|
|
|
|
|
|
|
ro 'dir'; aka dir => 'dirname', 'parent'; |
31
|
13
|
|
|
13
|
|
3181
|
no Class::Accessor::Classy; |
|
13
|
|
|
|
|
42
|
|
|
13
|
|
|
|
|
62
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
#use overload (); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 Constructor |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head2 new |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $file = File::Fu::File->new($path); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
my $file = File::Fu::File->new(@path); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
46
|
122
|
|
|
122
|
1
|
741
|
my $package = shift; |
47
|
122
|
|
66
|
|
|
452
|
my $class = ref($package) || $package; |
48
|
122
|
|
|
|
|
379
|
my $self = {$class->_init(@_)}; |
49
|
122
|
|
|
|
|
325
|
bless($self, $class); |
50
|
122
|
|
|
|
|
712
|
return($self); |
51
|
|
|
|
|
|
|
} # end subroutine new definition |
52
|
|
|
|
|
|
|
######################################################################## |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head2 new_direct |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $file = File::Fu::File->new_direct( |
57
|
|
|
|
|
|
|
dir => $dir_obj, |
58
|
|
|
|
|
|
|
file => $name |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=cut |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub new_direct { |
64
|
196
|
|
|
196
|
1
|
326
|
my $package = shift; |
65
|
196
|
|
33
|
|
|
728
|
my $class = ref($package) || $package; |
66
|
196
|
|
|
|
|
814
|
my $self = {@_}; |
67
|
196
|
|
|
|
|
440
|
bless($self, $class); |
68
|
196
|
|
|
|
|
1127
|
return($self); |
69
|
|
|
|
|
|
|
} # end subroutine new_direct definition |
70
|
|
|
|
|
|
|
######################################################################## |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head1 Class Constants |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head2 dir_class |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Return the corresponding dir class for this file object. Default: |
77
|
|
|
|
|
|
|
L. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
my $dc = $class->dir_class; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head2 is_dir |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Always false for a file. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head2 is_file |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Always true for a file. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
13
|
|
|
13
|
|
8396
|
use constant dir_class => 'File::Fu::Dir'; |
|
13
|
|
|
|
|
29
|
|
|
13
|
|
|
|
|
951
|
|
92
|
13
|
|
|
13
|
|
69
|
use constant is_dir => 0; |
|
13
|
|
|
|
|
27
|
|
|
13
|
|
|
|
|
846
|
|
93
|
13
|
|
|
13
|
|
307
|
use constant is_file => 1; |
|
13
|
|
|
|
|
27
|
|
|
13
|
|
|
|
|
35716
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
######################################################################## |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=for internal head2 _init |
98
|
|
|
|
|
|
|
my %fields = $class->_init(@_); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub _init { |
103
|
122
|
|
|
122
|
|
163
|
my $class = shift; |
104
|
122
|
50
|
|
|
|
421
|
my @dirs = @_ or croak("file must have a name"); |
105
|
122
|
|
|
|
|
201
|
my $file = pop(@dirs); |
106
|
122
|
100
|
|
|
|
386
|
if($file =~ m#/#) { |
107
|
33
|
50
|
|
|
|
176
|
croak("strange mix: ", join(',', @_, $file)) if(@dirs); |
108
|
33
|
|
|
|
|
294
|
my %p = $class->dir_class->_init($file); |
109
|
33
|
|
|
|
|
60
|
@dirs = @{$p{dirs}}; |
|
33
|
|
|
|
|
118
|
|
110
|
33
|
|
|
|
|
99
|
$file = pop(@dirs); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
122
|
|
|
|
|
601
|
return(dir => $class->dir_class->new(@dirs), file => $file); |
114
|
|
|
|
|
|
|
} # end subroutine _init definition |
115
|
|
|
|
|
|
|
######################################################################## |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 Parts |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 basename |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Returns a new object representing only the file part of the name. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my $obj = $file->basename; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub basename { |
128
|
84
|
|
|
84
|
1
|
1422
|
my $self = shift; |
129
|
84
|
|
|
|
|
2362
|
$self->new($self->file); |
130
|
|
|
|
|
|
|
} # end subroutine basename definition |
131
|
|
|
|
|
|
|
######################################################################## |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head1 Methods |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head2 stringify |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
my $string = $file->stringify; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub stringify { |
142
|
514
|
|
|
514
|
1
|
16280
|
my $self = shift; |
143
|
514
|
|
|
|
|
15300
|
my $dir = $self->dir; |
144
|
|
|
|
|
|
|
#warn "stringify(..., $_[1], $_[2])"; |
145
|
|
|
|
|
|
|
#Carp::carp("stringify ", overload::StrVal($self), " ($self->{file})"); |
146
|
514
|
100
|
|
|
|
5593
|
$dir = $dir->is_cwd ? '' : $dir->stringify; |
147
|
514
|
|
|
|
|
17502
|
return($dir . $self->file); |
148
|
|
|
|
|
|
|
} # end subroutine stringify definition |
149
|
|
|
|
|
|
|
######################################################################## |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head2 append |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Append a string only to the filename part. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
$file->append('.gz'); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$file %= '.gz'; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
(Yeah... I tried to use .=, but overloading hates me.) |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub append { |
164
|
3
|
|
|
3
|
1
|
6
|
my $self = shift; |
165
|
3
|
|
|
|
|
6
|
my ($tail) = @_; |
166
|
3
|
|
|
|
|
89
|
$self->file .= $tail; |
167
|
3
|
|
|
|
|
19
|
$self; |
168
|
|
|
|
|
|
|
} # end subroutine append definition |
169
|
|
|
|
|
|
|
######################################################################## |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 map |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
$file->map(sub {...}); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
$file &= sub {...}; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub map :method { |
180
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
181
|
2
|
|
|
|
|
3
|
my ($sub) = shift; |
182
|
2
|
|
|
|
|
58
|
local $_ = $self->file; |
183
|
2
|
|
|
|
|
15
|
$sub->(); |
184
|
2
|
|
|
|
|
60
|
$self->file = $_; |
185
|
2
|
|
|
|
|
10
|
$self; |
186
|
|
|
|
|
|
|
} # end subroutine map definition |
187
|
|
|
|
|
|
|
######################################################################## |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 absolute |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Get an absolute name (without checking the filesystem.) |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my $abs = $file->absolute; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub absolute { |
198
|
1
|
|
|
1
|
1
|
346
|
my ($self) = shift; |
199
|
1
|
|
|
|
|
24
|
return($self->dir->absolute->file($self->file)); |
200
|
|
|
|
|
|
|
} # end subroutine absolutely definition |
201
|
|
|
|
|
|
|
######################################################################## |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 absolutely |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Get an absolute name (resolved on the filesytem.) |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
my $abs = $file->absolutely; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=cut |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub absolutely { |
212
|
13
|
|
|
13
|
1
|
45
|
my $self = shift; |
213
|
13
|
|
|
|
|
612
|
return($self->dir->absolutely->file($self->file)); |
214
|
|
|
|
|
|
|
} # end subroutine absolutely definition |
215
|
|
|
|
|
|
|
######################################################################## |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head1 Doing stuff |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head2 open |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Open the file with $mode ('<', 'r', '>', 'w', etc) -- see L. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
my $fh = $file->open($mode, $permissions); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Throws an error if anything goes wrong or if the resulting filehandle |
226
|
|
|
|
|
|
|
happens to be a directory. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=cut |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# TODO should probably have our own filehandle so we can close in the |
231
|
|
|
|
|
|
|
# destructor and croak there too? |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub open :method { |
234
|
75
|
|
|
75
|
1
|
2548
|
my $self = shift; |
235
|
75
|
100
|
|
|
|
437
|
my $fh = IO::File->new($self, @_) or croak("cannot open '$self' $!"); |
236
|
74
|
50
|
|
|
|
6099
|
-d $fh and croak("$self is a directory"); |
237
|
74
|
|
|
|
|
865
|
return($fh); |
238
|
|
|
|
|
|
|
} # end subroutine open definition |
239
|
|
|
|
|
|
|
######################################################################## |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 sysopen |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Interface to the sysopen() builtin. The value of $mode is a text string |
245
|
|
|
|
|
|
|
joined by '|' characters which must be valid O_* constants from Fcntl. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
my $fh = $file->sysopen($mode, $perms); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub sysopen :method { |
252
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
253
|
0
|
|
|
|
|
0
|
my ($mode, $perms) = @_; |
254
|
0
|
|
|
|
|
0
|
my $m = 0; |
255
|
0
|
|
|
|
|
0
|
foreach my $w (split /\|/, $mode) { |
256
|
0
|
|
|
|
|
0
|
my $word = 'O_' . uc($w); |
257
|
0
|
0
|
|
|
|
0
|
my $x = Fcntl->can($word) or croak("'$word' not found in Fcntl"); |
258
|
0
|
|
|
|
|
0
|
$m |= $x->(); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
0
|
|
|
|
|
0
|
my $fh = IO::Handle->new; |
262
|
0
|
0
|
0
|
|
|
0
|
sysopen($fh, "$self", $m, $perms || 0666) |
263
|
|
|
|
|
|
|
or croak("error on sysopen '$self' - $!"); |
264
|
|
|
|
|
|
|
|
265
|
0
|
|
|
|
|
0
|
return($fh); |
266
|
|
|
|
|
|
|
} # sysopen ############################################################ |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=head2 piped_open |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Opens a read pipe. The file is appended to @command. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
my $fh = $file->piped_open(@command); |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
Example: useless use of cat. |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
my $fh = $file->piped_open('cat'); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
This interface is deprecated (maybe) because it is limited to commands |
279
|
|
|
|
|
|
|
which take the $file as the last argument. See run() for the way of the |
280
|
|
|
|
|
|
|
future. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=cut |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub piped_open { |
285
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
286
|
0
|
|
|
|
|
0
|
my (@command) = @_; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# TODO some way to decide where self goes in @command |
289
|
0
|
|
|
|
|
0
|
push(@command, $self); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# TODO closing STDIN and such before the fork? |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# TODO here is where we need our own filehandle object again |
294
|
0
|
0
|
|
|
|
0
|
my $pid = open(my $fh, '-|', @command) or |
295
|
|
|
|
|
|
|
croak("cannot exec '@command' $!"); |
296
|
0
|
|
|
|
|
0
|
return($fh); |
297
|
|
|
|
|
|
|
} # end subroutine piped_open definition |
298
|
|
|
|
|
|
|
######################################################################## |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=head2 run |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Treat C<$file> as a program and execute a pipe open. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
my $fh = $file->run(@args); |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
If called in void context, runs C with autodie semantics and |
307
|
|
|
|
|
|
|
multi-arg form (suppresses shell interpolation.) |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
$file->run(@args); |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
No special treatment is made for whether $file is relative or not (the |
312
|
|
|
|
|
|
|
underlying C/C will search your path.) Use |
313
|
|
|
|
|
|
|
File::Fu->which() to get an absolute path beforehand. |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
File::Fu->which('ls')->run('-l'); |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=cut |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub run { |
320
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
321
|
0
|
|
|
|
|
0
|
my (@args) = @_; |
322
|
|
|
|
|
|
|
|
323
|
0
|
0
|
|
|
|
0
|
if(defined wantarray) { |
324
|
|
|
|
|
|
|
# TODO use IPC::Run |
325
|
0
|
|
|
|
|
0
|
my $fh = IO::Handle->new; |
326
|
0
|
|
|
|
|
0
|
my @command = ($self, @args); |
327
|
0
|
0
|
|
|
|
0
|
my $pid = open($fh, '-|', @command) or |
328
|
|
|
|
|
|
|
croak("cannot exec '@command' $!"); |
329
|
0
|
|
|
|
|
0
|
return($fh); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
else { |
332
|
0
|
|
|
|
|
0
|
my $ret = system {$self} $self, @args; |
|
0
|
|
|
|
|
0
|
|
333
|
0
|
0
|
|
|
|
0
|
croak("error executing '$self'", $ret < 0 ? " $!" : '') if($ret); |
|
|
0
|
|
|
|
|
|
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} # run ################################################################ |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head2 touch |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Update the timestamp of a file (or create it.) |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
$file->touch; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=cut |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub touch { |
346
|
68
|
|
|
68
|
1
|
123
|
my $self = shift; |
347
|
68
|
50
|
|
|
|
635
|
if(-e $self) { |
348
|
0
|
|
|
|
|
0
|
$self->utime(time); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
else { |
351
|
68
|
|
|
|
|
1680
|
$self->open('>'); |
352
|
|
|
|
|
|
|
} |
353
|
67
|
|
|
|
|
277
|
return($self); |
354
|
|
|
|
|
|
|
} # end subroutine touch definition |
355
|
|
|
|
|
|
|
######################################################################## |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head2 mkfifo |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
my $file = $file->mkfifo($mode); |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=cut |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub mkfifo :method { |
364
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
365
|
0
|
|
|
|
|
0
|
my ($mode) = @_; |
366
|
|
|
|
|
|
|
|
367
|
0
|
|
0
|
|
|
0
|
$mode ||= 0700; |
368
|
0
|
|
|
|
|
0
|
require POSIX; |
369
|
0
|
0
|
|
|
|
0
|
POSIX::mkfifo("$self", $mode) or croak("mkfifo '$self' failed $!"); |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
0
|
return $self; |
372
|
|
|
|
|
|
|
} # mkfifo ############################################################# |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head2 link |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my $link = $file->link($name); |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=cut |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub link :method { |
381
|
1
|
|
|
1
|
1
|
9
|
my $self = shift; |
382
|
1
|
|
|
|
|
2
|
my ($name) = @_; |
383
|
1
|
50
|
|
|
|
3
|
link($self, $name) or croak("link '$self' to '$name' failed $!"); |
384
|
1
|
|
|
|
|
61
|
return($self->new($name)); |
385
|
|
|
|
|
|
|
} # end subroutine link definition |
386
|
|
|
|
|
|
|
######################################################################## |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head2 symlink |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
my $link = $file->symlink($linkname); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Note that symlinks are relative to where they live. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my $dir = File::Fu->dir("foo"); |
395
|
|
|
|
|
|
|
my $file = $dir+'file'; |
396
|
|
|
|
|
|
|
# $file->symlink($dir+'link'); is a broken link |
397
|
|
|
|
|
|
|
my $link = $file->basename->symlink($dir+'link'); |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=head2 relative_symlink |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
See L. |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=cut |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub symlink :method { |
406
|
4
|
|
|
4
|
1
|
18
|
my $self = shift; |
407
|
4
|
|
|
|
|
9
|
my ($name) = @_; |
408
|
4
|
50
|
|
|
|
11
|
symlink($self, $name) or |
409
|
|
|
|
|
|
|
croak("symlink '$self' to '$name' failed $!"); |
410
|
4
|
|
|
|
|
308
|
return($self->new($name)); |
411
|
|
|
|
|
|
|
} # end subroutine symlink definition |
412
|
|
|
|
|
|
|
######################################################################## |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# TODO |
415
|
|
|
|
|
|
|
# my $link = $file->dwimlink(absolute|relative|samedir => $linkname); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=head2 unlink |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
$file->unlink; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=cut |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub unlink :method { |
424
|
15
|
|
|
15
|
1
|
5810
|
my $self = shift; |
425
|
15
|
50
|
|
|
|
52
|
unlink("$self") or croak("unlink '$self' failed $!"); |
426
|
|
|
|
|
|
|
} # end subroutine unlink definition |
427
|
|
|
|
|
|
|
######################################################################## |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head2 remove |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
A forced unlink (chmod the file if it is not writable.) |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
$file->remove; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=cut |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub remove { |
438
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
439
|
|
|
|
|
|
|
|
440
|
0
|
0
|
|
|
|
0
|
$self->chmod(0200) unless($self->w); |
441
|
0
|
|
|
|
|
0
|
$self->unlink; |
442
|
|
|
|
|
|
|
} # remove ############################################################# |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head2 readlink |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
my $to = $file->readlink; |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=cut |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
sub readlink :method { |
451
|
4
|
|
|
4
|
1
|
975
|
my $self = shift; |
452
|
4
|
|
|
|
|
13
|
my $name = readlink($self); |
453
|
4
|
100
|
|
|
|
79
|
defined($name) or croak("cannot readlink '$self' $!"); |
454
|
2
|
|
|
|
|
8
|
return($self->new($name)); |
455
|
|
|
|
|
|
|
} # end subroutine readlink definition |
456
|
|
|
|
|
|
|
######################################################################## |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
######################################################################## |
459
|
|
|
|
|
|
|
{ # a closure for this variable |
460
|
|
|
|
|
|
|
my $has_slurp; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head2 read |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Read the entire file into memory (or swap!) |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
my @lines = $file->read; |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
my $file = $file->read; |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
If File::Slurp is available, options to read_file will be passed along. |
471
|
|
|
|
|
|
|
See L. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub read :method { |
476
|
3
|
|
|
3
|
1
|
1034
|
my $self = shift; |
477
|
3
|
|
|
|
|
7
|
my @args = @_; |
478
|
|
|
|
|
|
|
|
479
|
3
|
|
50
|
|
|
14
|
$has_slurp ||= eval {require File::Slurp; 1} || -1; |
|
|
|
66
|
|
|
|
|
480
|
|
|
|
|
|
|
|
481
|
3
|
50
|
|
|
|
16
|
if($has_slurp > 0) { |
482
|
0
|
|
|
|
|
0
|
local $Carp::CarpLevel = 1; |
483
|
0
|
|
|
|
|
0
|
return(File::Slurp::read_file("$self", @args, err_mode => 'croak')); |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
else { |
486
|
3
|
50
|
|
|
|
13
|
croak("must have File::Slurp for fancy reads") if(@args); |
487
|
|
|
|
|
|
|
|
488
|
3
|
|
|
|
|
11
|
my $fh = $self->open; |
489
|
3
|
100
|
|
|
|
23
|
local $/ = wantarray ? $/ : undef; |
490
|
3
|
|
|
|
|
137
|
return(<$fh>); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
} # end subroutine read definition |
493
|
|
|
|
|
|
|
######################################################################## |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=head2 write |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Write the file's contents. Returns the $file object for chaining. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
$file = $file->write($content); |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
If File::Slurp is available, $content may be either a scalar, scalar |
502
|
|
|
|
|
|
|
ref, or array ref. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
$file->write($content, %args); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub write { |
509
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
510
|
0
|
|
|
|
|
|
my ($content, @args) = @_; |
511
|
|
|
|
|
|
|
|
512
|
0
|
|
0
|
|
|
|
$has_slurp ||= eval {require File::Slurp; 1} || -1; |
|
|
|
0
|
|
|
|
|
513
|
|
|
|
|
|
|
|
514
|
0
|
0
|
|
|
|
|
if($has_slurp > 0) { |
515
|
0
|
|
|
|
|
|
local $Carp::CarpLevel = 1; |
516
|
0
|
|
|
|
|
|
File::Slurp::write_file("$self", |
517
|
|
|
|
|
|
|
{@args, err_mode => 'croak'}, |
518
|
|
|
|
|
|
|
$content |
519
|
|
|
|
|
|
|
); |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
else { |
522
|
0
|
0
|
0
|
|
|
|
croak("must have File::Slurp for fancy writes") |
523
|
|
|
|
|
|
|
if(@args or ref($content)); |
524
|
0
|
|
|
|
|
|
my $fh = $self->open('>'); |
525
|
0
|
|
|
|
|
|
print $fh $content; |
526
|
0
|
0
|
|
|
|
|
close($fh) or croak("write '$self' failed: $!"); |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
0
|
|
|
|
|
|
return $self; |
530
|
|
|
|
|
|
|
} # end subroutine write definition |
531
|
|
|
|
|
|
|
######################################################################## |
532
|
|
|
|
|
|
|
} # File::Slurp closure |
533
|
|
|
|
|
|
|
######################################################################## |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=head2 copy |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
Copies $file to $dest (which can be a file or directory) and returns the |
538
|
|
|
|
|
|
|
name of the new file as an object. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
my $new = $file->copy($dest); |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Note that if $dest is already a File object, that existing object will |
543
|
|
|
|
|
|
|
be returned. |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=cut |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
sub copy { |
548
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
549
|
0
|
|
|
|
|
|
my ($dest) = shift; |
550
|
0
|
|
|
|
|
|
my (%opts) = @_; |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# decide if this is file-to-dir or file-to-file |
553
|
0
|
0
|
|
|
|
|
if(-d $dest) { |
554
|
0
|
|
|
|
|
|
$dest = $self->dir_class->new($dest)->file($self->basename); |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
else { |
557
|
0
|
0
|
|
|
|
|
$dest = $self->new($dest) unless(ref($dest)); |
558
|
|
|
|
|
|
|
} |
559
|
0
|
0
|
|
|
|
|
if($dest->e) { |
560
|
0
|
0
|
|
|
|
|
croak("'$dest' and '$self' are the same file") |
561
|
|
|
|
|
|
|
if($self->is_same($dest)); |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
# TODO here's another good reason to have our own filehandle object: |
565
|
|
|
|
|
|
|
# This fh-copy should be in there. |
566
|
0
|
|
|
|
|
|
my $ifh = $self->open; |
567
|
0
|
|
|
|
|
|
my $ofh = $dest->open('>'); |
568
|
0
|
|
|
|
|
|
binmode($_) for($ifh, $ofh); |
569
|
0
|
|
|
|
|
|
while(1) { |
570
|
0
|
|
|
|
|
|
my $buf; |
571
|
0
|
0
|
|
|
|
|
defined(my $r = sysread($ifh, $buf, 1024)) or |
572
|
|
|
|
|
|
|
croak("sysread failed $!"); |
573
|
0
|
0
|
|
|
|
|
$r or last; |
574
|
|
|
|
|
|
|
# why did File::Copy::copy do it like this? |
575
|
0
|
|
|
|
|
|
for(my $t = my $w = 0; $w < $r; $w += $t) { |
576
|
0
|
0
|
|
|
|
|
$t = syswrite($ofh, $buf, $r - $w, $w) or |
577
|
|
|
|
|
|
|
croak("syswrite failed $!"); |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
} |
580
|
0
|
0
|
|
|
|
|
close($ofh) or croak("write '$dest' failed: $!"); |
581
|
|
|
|
|
|
|
# TODO some form of rollback? |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
# TODO handle opts |
584
|
|
|
|
|
|
|
#if($opts{preserve}) { |
585
|
|
|
|
|
|
|
# # TODO chmod/chown and such |
586
|
|
|
|
|
|
|
# $dest->utime($self->stat->mtime); |
587
|
|
|
|
|
|
|
#} |
588
|
|
|
|
|
|
|
|
589
|
0
|
|
|
|
|
|
return($dest); |
590
|
|
|
|
|
|
|
} # copy ############################################################### |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 move |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
my $new = $file->move($dest); |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=cut |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
sub move { |
599
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
600
|
0
|
|
|
|
|
|
my $new = $self->copy(@_); # TODO can use rename? |
601
|
0
|
|
|
|
|
|
$self->unlink; |
602
|
0
|
|
|
|
|
|
return($new); |
603
|
|
|
|
|
|
|
} # move ############################################################### |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
######################################################################## |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=head1 AUTHOR |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
Eric Wilhelm @ |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
http://scratchcomputing.com/ |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head1 BUGS |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
If you found this module on CPAN, please report any bugs or feature |
616
|
|
|
|
|
|
|
requests through the web interface at L. I will be |
617
|
|
|
|
|
|
|
notified, and then you'll automatically be notified of progress on your |
618
|
|
|
|
|
|
|
bug as I make changes. |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
If you pulled this development version from my /svn/, please contact me |
621
|
|
|
|
|
|
|
directly. |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head1 COPYRIGHT |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Copyright (C) 2008 Eric L. Wilhelm, All Rights Reserved. |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=head1 NO WARRANTY |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Absolutely, positively NO WARRANTY, neither express or implied, is |
630
|
|
|
|
|
|
|
offered with this software. You use this software at your own risk. In |
631
|
|
|
|
|
|
|
case of loss, no person or entity owes you anything whatsoever. You |
632
|
|
|
|
|
|
|
have been warned. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head1 LICENSE |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
637
|
|
|
|
|
|
|
under the same terms as Perl itself. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=cut |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
require File::Fu; |
642
|
|
|
|
|
|
|
# vi:ts=2:sw=2:et:sta |
643
|
|
|
|
|
|
|
1; |