line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/local/bin/perl
|
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Tie::FileHandle::Split;
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Tie::FileHandle::Split - Filehandle tie that captures, splits and stores output into files in a given path.
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 VERSION
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Version 0.95
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=cut
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
$VERSION = 0.95;
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
This module, when tied to a filehandle, will capture and store all that
|
20
|
|
|
|
|
|
|
is output to that handle. You should then select a path to store files and a
|
21
|
|
|
|
|
|
|
size to split files.
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# $path should exist or the current process have enough priv. for creation.
|
26
|
|
|
|
|
|
|
# $size should be > 0.
|
27
|
|
|
|
|
|
|
tie *HANDLE, 'Tie::FileHandle::Split', $path, $size;
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Register code to listen to file creation
|
30
|
|
|
|
|
|
|
(tied *HANDLE)->add_file_creation_listeners( sub {
|
31
|
|
|
|
|
|
|
my ( $tied_object, $filename) = @_;
|
32
|
|
|
|
|
|
|
print "Created $filename with size: " . -s $filename . "\n";
|
33
|
|
|
|
|
|
|
} );
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Will create int( $many_times_size / $split_size) files of size $split_size.
|
36
|
|
|
|
|
|
|
# Will call each listener int( $many_times_size / $split_size) times.
|
37
|
|
|
|
|
|
|
# Buffers will hold $many_times_size % $split_size outstanding bytes.
|
38
|
|
|
|
|
|
|
(tied *HANDLE)->print( ' ' x $many_times_size );
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Write all outstanding output from buffers to files.
|
41
|
|
|
|
|
|
|
# The last file created can be smaller than split_size
|
42
|
|
|
|
|
|
|
(tied *HANDLE)->write_buffers;
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Get generated filenames to the moment
|
45
|
|
|
|
|
|
|
(tied *HANDLE)->get_filenames();
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=cut
|
48
|
|
|
|
|
|
|
|
49
|
1
|
|
|
1
|
|
133656
|
use 5.10.0;
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
42
|
|
50
|
1
|
|
|
1
|
|
6
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
51
|
1
|
|
|
1
|
|
6
|
use warnings;
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
34
|
|
52
|
|
|
|
|
|
|
|
53
|
1
|
|
|
1
|
|
5
|
use vars qw(@ISA $VERSION);
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
58
|
|
54
|
1
|
|
|
1
|
|
5
|
use base qw(Tie::FileHandle::Base);
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
120
|
|
55
|
|
|
|
|
|
|
|
56
|
1
|
|
|
1
|
|
6
|
use File::Path;
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
75
|
|
57
|
1
|
|
|
1
|
|
6
|
use File::Temp;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
90
|
|
58
|
1
|
|
|
1
|
|
6
|
use Carp;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1142
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Tie::FileHandle implementation
|
61
|
|
|
|
|
|
|
# Usage: tie *HANDLE, 'Tie::FileHandle::Split', $path, $split_size
|
62
|
|
|
|
|
|
|
sub TIEHANDLE {
|
63
|
2
|
|
|
2
|
|
2256
|
my ( $class, $path, $split_size ) = @_;
|
64
|
|
|
|
|
|
|
|
65
|
2
|
|
|
|
|
15
|
my $self = {
|
66
|
|
|
|
|
|
|
class => $class,
|
67
|
|
|
|
|
|
|
path => $path,
|
68
|
|
|
|
|
|
|
split_size => $split_size,
|
69
|
|
|
|
|
|
|
buffer => '',
|
70
|
|
|
|
|
|
|
buffer_size => 0,
|
71
|
|
|
|
|
|
|
filenames => [],
|
72
|
|
|
|
|
|
|
listeners => {},
|
73
|
|
|
|
|
|
|
};
|
74
|
|
|
|
|
|
|
|
75
|
2
|
50
|
|
|
|
38
|
File::Path::make_path( $self->{path} ) unless -d $self->{path};
|
76
|
|
|
|
|
|
|
|
77
|
2
|
|
|
|
|
10
|
bless $self, $class;
|
78
|
|
|
|
|
|
|
}
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Tie::FileHandle implementation
|
81
|
|
|
|
|
|
|
# Print to the selected handle
|
82
|
|
|
|
|
|
|
sub PRINT {
|
83
|
10
|
|
|
10
|
|
8798
|
my ( $self, $data ) = @_;
|
84
|
10
|
|
|
|
|
7230
|
$self->{buffer} .= $data;
|
85
|
10
|
|
|
|
|
29
|
$self->{buffer_size} += length( $data );
|
86
|
|
|
|
|
|
|
|
87
|
10
|
|
|
|
|
30
|
$self->_write_files( $self->{split_size} );
|
88
|
|
|
|
|
|
|
}
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _write_files{
|
91
|
13
|
|
|
13
|
|
20
|
my ( $self, $min_size ) = @_;
|
92
|
|
|
|
|
|
|
|
93
|
13
|
|
|
|
|
17
|
my $written_chunks = 0;
|
94
|
|
|
|
|
|
|
|
95
|
13
|
|
|
|
|
49
|
while ( $self->{buffer_size} - $min_size * $written_chunks >= $min_size ) {
|
96
|
10
|
|
|
|
|
52
|
my ($fh, $filename) = File::Temp::tempfile( DIR => $self->{path} );
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Added complexity to work buffer with a cursor and doing a single buffer chomp
|
99
|
10
|
|
|
|
|
4793
|
$fh->print( substr $self->{buffer},$min_size * $written_chunks++, $min_size );
|
100
|
10
|
|
|
|
|
28658
|
$fh->autoflush;
|
101
|
10
|
|
|
|
|
1200
|
$fh->close;
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Call listeners
|
104
|
10
|
|
|
|
|
217
|
foreach my $listener ( keys %{$self->{listeners}} ) {
|
|
10
|
|
|
|
|
63
|
|
105
|
8
|
|
|
|
|
5040
|
&{$self->{listeners}->{$listener}}( $self, $filename );
|
|
8
|
|
|
|
|
25
|
|
106
|
|
|
|
|
|
|
}
|
107
|
|
|
|
|
|
|
|
108
|
10
|
|
|
|
|
2312
|
push @{$self->{filenames}}, $filename;
|
|
10
|
|
|
|
|
95
|
|
109
|
|
|
|
|
|
|
}
|
110
|
13
|
100
|
|
|
|
46
|
if ( $written_chunks ) {
|
111
|
6
|
|
|
|
|
12
|
$self->{buffer_size} -= $min_size * $written_chunks;
|
112
|
6
|
50
|
|
|
|
45
|
if ( $self->{buffer_size} > 0 ) {
|
113
|
0
|
|
|
|
|
0
|
$self->{buffer} = substr $self->{buffer}, -$self->{buffer_size} ;
|
114
|
|
|
|
|
|
|
} else {
|
115
|
6
|
|
|
|
|
43
|
$self->{buffer} = '';
|
116
|
|
|
|
|
|
|
}
|
117
|
|
|
|
|
|
|
}
|
118
|
|
|
|
|
|
|
}
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head1 METHODS
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head3 C
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
C writes all outstanding buffers to files.
|
125
|
|
|
|
|
|
|
It is automatically called before destroying the object to ensure all data
|
126
|
|
|
|
|
|
|
written to the tied filehandle is written to files. If additional data is
|
127
|
|
|
|
|
|
|
written to the filehandle after a call to C a new file will be
|
128
|
|
|
|
|
|
|
created. On a standard file split operation it is called after writting all data
|
129
|
|
|
|
|
|
|
to the tied file handle ensure the last bit of data is written (in the most
|
130
|
|
|
|
|
|
|
common case where data size is not exactly divisible by the split size).
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=cut
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub write_buffers {
|
135
|
|
|
|
|
|
|
# Must implement
|
136
|
2
|
|
|
2
|
1
|
1018
|
my ( $self ) = @_;
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# this should not happen...
|
139
|
2
|
|
|
|
|
8
|
$self->_write_files( $self->{split_size} );
|
140
|
2
|
100
|
|
|
|
9
|
if ( $self->{buffer_size} > 0 ) {
|
141
|
1
|
|
|
|
|
4
|
$self->_write_files( $self->{buffer_size} );
|
142
|
|
|
|
|
|
|
}
|
143
|
|
|
|
|
|
|
}
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head3 C
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
C returns a list of the files generates until the moment of the
|
148
|
|
|
|
|
|
|
call. It should be used to get the names of files and rename them to the
|
149
|
|
|
|
|
|
|
desired filenames. In a standard splitting operation C is
|
150
|
|
|
|
|
|
|
called after outputting all data to the filehandle and calling C.
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=cut
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# Returns filenames generated up to the moment the method is called
|
155
|
|
|
|
|
|
|
sub get_filenames {
|
156
|
12
|
|
|
12
|
1
|
2149
|
my ( $self ) = @_;
|
157
|
|
|
|
|
|
|
|
158
|
12
|
50
|
|
|
|
42
|
return @{$self->{filenames}} if defined $self->{filenames};
|
|
12
|
|
|
|
|
62
|
|
159
|
|
|
|
|
|
|
}
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head3 C
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
C adds methods to the list of listeners of the
|
164
|
|
|
|
|
|
|
file creation event. Methods should be code, array, arrayref or any
|
165
|
|
|
|
|
|
|
non-recursive structure resulting from them. Since methods are added to a HASH,
|
166
|
|
|
|
|
|
|
several elements pointing to the same piece of code will be added only once.
|
167
|
|
|
|
|
|
|
Code observing this event is called once per file created of the $split_size
|
168
|
|
|
|
|
|
|
size defined in the tie clause. When called the Tie::FileHandle::Split object
|
169
|
|
|
|
|
|
|
and the complete path to the newly created file is passed as parameter. The file
|
170
|
|
|
|
|
|
|
is of the specified C<$split_size> defined in the tie clause unless generated
|
171
|
|
|
|
|
|
|
from a C call, has been closed and an effort has been made for it
|
172
|
|
|
|
|
|
|
to sync (untested).
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=cut
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub add_file_creation_listeners {
|
177
|
4
|
|
|
4
|
1
|
2288
|
my ( $self, @listeners ) = @_;
|
178
|
|
|
|
|
|
|
|
179
|
4
|
|
|
|
|
9
|
foreach my $listener ( @listeners ) {
|
180
|
7
|
100
|
|
|
|
21
|
if( ref( $listener ) eq 'CODE' ) {
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
181
|
6
|
|
|
|
|
24
|
$self->{listeners}->{$listener} = $listener;
|
182
|
|
|
|
|
|
|
} elsif ( ref( $listener ) eq 'ARRAY' ) {
|
183
|
1
|
|
|
|
|
22
|
$self->add_file_creation_listeners( @$listener );
|
184
|
|
|
|
|
|
|
} elsif ( ref( $listener ) eq 'ARRAYREF' ) {
|
185
|
0
|
|
|
|
|
0
|
$self->add_file_creation_listeners( $listener );
|
186
|
|
|
|
|
|
|
} else {
|
187
|
0
|
|
|
|
|
0
|
croak("Unsupported structure in add_file_creation_listeners. " .
|
188
|
|
|
|
|
|
|
"Can use any structure containing CODE, ARRAY and ARRAYREF. " .
|
189
|
|
|
|
|
|
|
"Looks like a " . ref( $listener ) );
|
190
|
|
|
|
|
|
|
}
|
191
|
|
|
|
|
|
|
}
|
192
|
|
|
|
|
|
|
}
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head3 C
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
C removes a list of methods from the list of
|
197
|
|
|
|
|
|
|
listeners of the file creation event. Methods should be code, array, arrayref or
|
198
|
|
|
|
|
|
|
any non-recursive structure resulting from them.
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=cut
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub remove_file_creation_listeners {
|
203
|
2
|
|
|
2
|
1
|
885
|
my ( $self, @listeners ) = @_;
|
204
|
|
|
|
|
|
|
|
205
|
2
|
|
|
|
|
4
|
foreach my $listener ( @listeners ) {
|
206
|
2
|
50
|
|
|
|
6
|
if( ref( $listener ) eq 'CODE' ) {
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
207
|
2
|
|
|
|
|
10
|
delete $self->{listeners}->{$listener};
|
208
|
|
|
|
|
|
|
} elsif ( ref( $listener ) eq 'ARRAY' ) {
|
209
|
0
|
|
|
|
|
0
|
$self->remove_file_creation_listeners( @$listener );
|
210
|
|
|
|
|
|
|
} elsif ( ref( $listener ) eq 'ARRAYREF' ) {
|
211
|
0
|
|
|
|
|
0
|
$self->remove_file_creation_listeners( $listener );
|
212
|
|
|
|
|
|
|
} else {
|
213
|
0
|
|
|
|
|
0
|
croak("Unsupported structure in add_file_creation_listeners. " .
|
214
|
|
|
|
|
|
|
"Can use any structure containing CODE, ARRAY and ARRAYREF. " .
|
215
|
|
|
|
|
|
|
"Looks like a " . ref( $listener ) );
|
216
|
|
|
|
|
|
|
}
|
217
|
|
|
|
|
|
|
}
|
218
|
|
|
|
|
|
|
}
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head3 C
|
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
C removes all methods from the list of listeners
|
223
|
|
|
|
|
|
|
of the file creation event.
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub clear_file_creation_listeners {
|
228
|
2
|
|
|
2
|
1
|
2004
|
my ( $self ) = @_;
|
229
|
|
|
|
|
|
|
|
230
|
2
|
|
|
|
|
7
|
$self->{listeners} = {};
|
231
|
|
|
|
|
|
|
}
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub _get_listeners {
|
234
|
1
|
|
|
1
|
|
5
|
my ( $self ) = @_;
|
235
|
|
|
|
|
|
|
# Behold! Dereferencing fixes incompatibility with pre 5.14 perl.
|
236
|
|
|
|
|
|
|
# Both keys and each are affected if a hashref is passed.
|
237
|
1
|
|
|
|
|
2
|
return map $_,keys %{$self->{listeners}};
|
|
1
|
|
|
|
|
7
|
|
238
|
|
|
|
|
|
|
}
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub DESTROY {
|
241
|
1
|
|
|
1
|
|
2
|
my ( $self ) = @_;
|
242
|
|
|
|
|
|
|
|
243
|
1
|
50
|
|
|
|
8
|
$self->write_buffers() if ( $self->{buffer_size} > 0 );
|
244
|
|
|
|
|
|
|
}
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
1;
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head1 TODO
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=over 4
|
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=item * Very untested for anything other than writing to the filehandle.
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item * write_buffers should sync to disk, untested and seeking advice.
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=back
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head1 BUGS
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
No known bugs. Please report and suggest tests to gbarco@cpan.org.
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=cut
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head1 AUTHORS AND COPYRIGHT
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
Written by Gonzalo Barco based on Tie::FileHandle::Buffer written by Robby Walker ( robwalker@cpan.org ).
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Project repository can be found at https://github.com/gbarco/Tie-FileHandle-Split.
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
You may redistribute/modify/etc. this module under the same terms as Perl itself.
|
271
|
|
|
|
|
|
|
|