File Coverage

blib/lib/Data/Embed/Writer.pm
Criterion Covered Total %
statement 202 220 91.8
branch 73 114 64.0
condition 20 24 83.3
subroutine 20 20 100.0
pod 6 6 100.0
total 321 384 83.5


line stmt bran cond sub pod time code
1             package Data::Embed::Writer;
2              
3 9     9   705 use strict;
  9         8  
  9         254  
4 9     9   26 use warnings;
  9         19  
  9         211  
5 9     9   25 use English qw< -no_match_vars >;
  9         7  
  9         46  
6 9     9   5532 use Data::Embed::Reader;
  9         12  
  9         223  
7 9     9   36 use Log::Log4perl::Tiny qw< :easy :dead_if_first >;
  9         10  
  9         32  
8 9     9   1751 use Data::Embed::Util qw< :constants escape >;
  9         9  
  9         723  
9 9     9   33 use Fcntl qw< :seek >;
  9         8  
  9         668  
10 9     9   32 use Scalar::Util qw< refaddr >;
  9         10  
  9         13350  
11              
12             our $VERSION = '0.32'; # make indexer happy
13              
14             sub __output_for_new {
15 62     62   51 my $self = shift;
16 62         57 my $package = ref $self;
17 62         70 my $output = $self->{output} = $self->{args}{output};
18 62         55 $self->{output_same_as_input} = 0; # by default
19              
20             # The simple stuff: not present/defined, or the classical "-" string
21 62 100 100     326 if ((!defined($output)) || (!length($output)) || ($output eq '-')) {
      100        
22 24         63 DEBUG $package, "::__output_for_new(): output to STDOUT";
23 24 50       461 open my $fh, '>&', \*STDOUT
24             or LOGCROAK "dup(): $OS_ERROR";
25 24 50       48 binmode $fh
26             or LOGCROAK "binmode(\\*STDOUT): $OS_ERROR";
27 24         54 $self->{output_name} = '';
28 24         17 $self->{output_fh} = $fh;
29 24         20 $self->{output_type} = 'filehandle';
30 24         36 return $self;
31             } ## end if ((!defined($output)...))
32              
33 38         43 my $oref = ref $output;
34 38 100       55 if (!$oref) { # filename
35 9         14 DEBUG $package, '::__output:for_new(): output to a file';
36 9         77 $self->{output_type} = 'file';
37 9         9 $self->{output_name} = $output;
38              
39             # same file as input? If yes, do not clobber
40 9 100 100     27 if (($self->{input_type} eq 'file') && ($output eq $self->{input})) {
41 3 50       52 open my $fh, '+<', $output
42             or LOGCROAK "open('$output'): $OS_ERROR";
43 3 50       6 binmode $fh
44             or LOGCROAK "binmode('$output'): $OS_ERROR";
45 3         4 $self->{output_fh} = $fh;
46 3         3 $self->{output_same_as_input} = 1;
47 3         5 return $self;
48             } ## end if (($self->{input_type...}))
49              
50 6 50       295 open my $fh, '>', $output
51             or LOGCROAK "open('$output'): $OS_ERROR";
52 6 50       14 binmode $fh
53             or LOGCROAK "binmode('$output'): $OS_ERROR";
54 6         7 $self->{output_fh} = $fh;
55 6         11 return $self;
56             } ## end if (!$oref)
57              
58 29 100       45 if ($oref eq 'SCALAR') { # reference to a scalar, similar to filename
59 22         38 DEBUG $package, '::__output:for_new(): output to a scalar ref';
60 22         196 $self->{output_type} = 'scalar-ref';
61 22         43 $self->{output_name} = "{$output}";
62              
63             # same file as input? If yes, do not clobber
64 22 100 100     96 if ( ($self->{input_type} eq 'scalar-ref')
65             && (refaddr($output) eq refaddr($self->{input})))
66             {
67 7 50       39 open my $fh, '+<', $output
68             or LOGCROAK "open('$output'): $OS_ERROR";
69 7 50       11 binmode $fh
70             or LOGCROAK "binmode('$output'): $OS_ERROR";
71 7         7 $self->{output_fh} = $fh;
72 7         7 $self->{output_same_as_input} = 1;
73 7         10 return $self;
74             } ## end if (($self->{input_type...}))
75              
76 15 50       170 open my $fh, '>', $output
77             or LOGCROAK "open('$output'): $OS_ERROR";
78 15 50       1746 binmode $fh
79             or LOGCROAK "binmode('$output'): $OS_ERROR";
80 15         32 $self->{output_fh} = $fh;
81 15         27 return $self;
82             } ## end if ($oref eq 'SCALAR')
83              
84             # Otherwise, we will have to assume that it is a filehandle
85 7         12 $self->{output_name} = '';
86 7         5 $self->{output_fh} = $output;
87 7         7 $self->{output_type} = 'filehandle';
88             $self->{output_same_as_input} = ($self->{input_type} eq 'filehandle')
89 7   66     34 && (refaddr($output) eq refaddr($self->{input_fh}));
90 7         9 return $self;
91             } ## end sub __output_for_new
92              
93             sub __input_for_new {
94 62     62   39 my $self = shift;
95 62         67 my $package = ref $self;
96 62         116 my $input = $self->{input} = $self->{args}{input};
97              
98             # if not defined, it just does not exist
99 62 100       99 if (!defined($input)) {
100 24         60 DEBUG $package, "::__input_for_new(): no input";
101 24         320 $self->{input_name} = '*undef*';
102 24         26 $self->{input_fh} = undef;
103 24         24 $self->{input_type} = 'undef';
104 24         30 return $self;
105             } ## end if (!defined($input))
106              
107             # the classical "-" string
108 38 100       72 if ($input eq '-') {
109 7         18 DEBUG $package, "::__input_for_new(): input from STDIN";
110 7 50       166 open my $fh, '<&', \*STDIN
111             or LOGCROAK "dup(): $OS_ERROR";
112 7 50       17 binmode $fh
113             or LOGCROAK "binmode(\\*STDIN): $OS_ERROR";
114 7         11 $self->{input_name} = '';
115 7         10 $self->{input_fh} = $fh;
116 7         8 $self->{input_type} = 'filehandle';
117 7         10 return $self;
118             } ## end if ($input eq '-')
119              
120 31         32 my $iref = ref $input;
121 31 100       48 if (!$iref) { # filename
122 10         24 DEBUG $package, '::__input:for_new(): input from file';
123 10 50       343 open my $fh, '<', $input
124             or LOGCROAK "open('$input'): $OS_ERROR";
125 10 50       23 binmode $fh
126             or LOGCROAK "binmode('$input'): $OS_ERROR";
127 10         18 $self->{input_name} = $input;
128 10         11 $self->{input_fh} = $fh;
129 10         10 $self->{input_type} = 'file';
130 10         18 return $self;
131             } ## end if (!$iref)
132              
133 21 100       38 if ($iref eq 'SCALAR') { # reference to a scalar, similar to filename
134 14         35 DEBUG $package, '::__input:for_new(): input from a scalar ref';
135 14 50       224 open my $fh, '<', $input
136             or LOGCROAK "open('$input'): $OS_ERROR";
137 14 50       32 binmode $fh
138             or LOGCROAK "binmode('$input'): $OS_ERROR";
139 14         31 $self->{input_name} = "{$input}";
140 14         15 $self->{input_fh} = $fh;
141 14         13 $self->{input_type} = 'scalar-ref';
142 14         22 return $self;
143             } ## end if ($iref eq 'SCALAR')
144              
145             # Otherwise, we will have to assume that it is a filehandle
146 7         11 $self->{input_name} = '';
147 7         9 $self->{input_fh} = $input;
148 7         9 $self->{input_type} = 'filehandle';
149 7         10 return $self;
150             } ## end sub __input_for_new
151              
152             sub _transfer_input {
153 62     62   58 my $self = shift;
154 62 50       94 if (!$self->{input_transferred}) {
155              
156             # if there is an input, transfer to the output if it is the case
157 62 100       93 if ($self->{input_fh}) {
158 38 100       49 if ($self->{output_same_as_input})
159             { # don't copy, assume seekable input
160 10         44 my $reader = Data::Embed::Reader->new($self->{input_fh});
161 10         17 my $ifile = $reader->_index(); # private method called
162 10         22 my @index = $ifile->contents();
163 10         10 shift @index; # eliminate STARTER
164 10         8 pop @index; # eliminate TERMINATOR
165 10         12 $self->{index} = \@index; # initialize previous index
166             # put out handle in right position
167 10         83 seek $self->{output_fh}, $ifile->{offset}, SEEK_SET;
168             } ## end if ($self->{output_same_as_input...})
169             else {
170 28         27 my $starter = STARTER;
171 28         20 my $terminator = TERMINATOR;
172 28         20 my (@index, $index_completed);
173 28         24 my $ifh = $self->{input_fh};
174 28         23 my $ofh = $self->{output_fh};
175             INPUT:
176 28         181 while (<$ifh>) {
177 28 50       37 if (!@index) {
    0          
178 28 50       40 if ($_ eq $starter) {
179 0         0 push @index, $_;
180 0         0 next INPUT;
181             }
182             else {
183 28         24 print {$ofh} $_;
  28         196  
184             }
185             } ## end if (!@index)
186             elsif (!$index_completed) { # accumulating index
187 0 0       0 if (m{\A \s* (\d+) \s+ (\S*) \s*\z}mxs) {
    0          
188 0         0 push @index, $_;
189             }
190             elsif ($_ eq $terminator) {
191 0         0 push @index, $_;
192 0         0 $index_completed = 1;
193             }
194             else { # not a valid index, flush accumulated lines
195 0         0 print {$ofh} @index;
  0         0  
196 0         0 @index = ();
197 0         0 $index_completed = undef; # paranoid
198             }
199             } ## end elsif (!$index_completed)
200             else
201             { # accumulating and index completed, but other stuff...
202 0         0 print {$ofh} @index; # flush and reset
  0         0  
203 0         0 @index = ();
204 0         0 $index_completed = undef;
205             } ## end else [ if (!@index) ]
206             } ## end INPUT: while (<$ifh>)
207 28         27 shift @index; # eliminate STARTER
208 28         21 pop @index; # eliminate TERMINATOR
209 28         47 $self->{index} = \@index; # initialize previous index
210             } ## end else [ if ($self->{output_same_as_input...})]
211             } ## end if ($self->{input_fh})
212 62         197 $self->{input_transferred} = 1;
213             } ## end if (!$self->{input_transferred...})
214 62         53 return $self;
215             } ## end sub _transfer_input
216              
217             sub new {
218 62     62 1 69 my $package = shift;
219 62 50 66     350 my %args = (scalar(@_) && ref($_[0])) ? %{$_[0]} : @_;
  0         0  
220 62 50       132 $args{transfer_input} = 1 unless exists $args{transfer_input};
221              
222             # Undocumented, keep additional parameters around...
223 62         125 my $self = bless {args => \%args}, $package;
224              
225             # first of all, resolve the input
226 62         105 $self->__input_for_new();
227              
228             # then the output (might depend on the input)
229 62         86 $self->__output_for_new();
230              
231 62 50       148 $self->_transfer_input() if $args{transfer_input};
232              
233 62         256 return $self;
234             } ## end sub new
235              
236             sub add {
237 106     106 1 15021 my $self = shift;
238 106 50 33     517 my %args = (scalar(@_) && ref($_[0])) ? %{$_[0]} : @_;
  0         0  
239              
240             $self->_add($_)
241 106 100       296 for (defined($args{inputs}) ? @{$args{inputs}} : (\%args));
  7         14  
242              
243 106         284 return $self;
244             } ## end sub add
245              
246             sub _add {
247 112     112   116 my ($self, $args) = @_;
248              
249             # DWIM!
250 112 100       176 if (defined $args->{input}) {
251 4 100       12 if ($args->{input} eq '-') {
252 1 50       19 open my $fh, '<&', \*STDIN
253             or LOGCROAK "dup(): $OS_ERROR";
254 1 50       4 binmode $fh
255             or LOGCROAK "binmode(\\*STDIN): $OS_ERROR";
256 1         2 $args->{fh} = $fh;
257             } ## end if ($args->{input} eq ...)
258             else {
259 3         4 my $ref = ref $args->{input};
260 3 100 100     14 if ((!$ref) || ($ref eq 'SCALAR')) {
261 2         5 $args->{filename} = $args->{input};
262             }
263             else {
264 1         3 $args->{fh} = $args->{input};
265             }
266             } ## end else [ if ($args->{input} eq ...)]
267             } ## end if (defined $args->{input...})
268              
269 112 100       265 if (defined $args->{fh}) {
    100          
    50          
270 8         8 return $self->add_fh(@{$args}{qw< name fh >});
  8         22  
271             }
272             elsif (defined $args->{filename}) {
273 4         5 return $self->add_file(@{$args}{qw< name filename >});
  4         11  
274             }
275             elsif (defined $args->{data}) {
276 100         81 return $self->add_data(@{$args}{qw< name data >});
  100         166  
277             }
278              
279 0         0 LOGCROAK "add() needs some input";
280 0         0 return; # unreached
281             } ## end sub _add
282              
283             sub add_file {
284 111     111 1 1181 my ($self, $name, $filename) = @_;
285 111 50       166 $name = '' unless defined $name;
286 111 100       188 my $print_name =
287             (ref($filename) eq 'SCALAR') ? 'internal data' : $filename;
288 111         327 DEBUG "add_file(): $name => $filename";
289              
290             # To make it work with references to scalars in perl pre-5.14
291             # we split open() and binmode()
292 111 50   7   1795 open my $fh, '<', $filename
  7         30  
  7         7  
  7         39  
293             or LOGCROAK "open('$print_name'): $OS_ERROR";
294 111 50       2676 binmode $fh
295             or LOGCROAK "binmode('$print_name') failed";
296              
297 111         160 return $self->add_fh($name, $fh);
298             } ## end sub add_file
299              
300             sub add_data {
301 102     102 1 369 my ($self, $name) = @_;
302 102         120 return $self->add_file($name, \$_[2]);
303             }
304              
305             sub add_fh {
306 121     121 1 566 my ($self, $name, $input_fh) = @_;
307              
308 121         195 my $output_fh = $self->{output_fh};
309 121         116 my $data_length = 0; # go!
310 121         332 while (!eof $input_fh) {
311 121         101 my $buffer;
312 121 50       384 defined(my $nread = read $input_fh, $buffer, 4096)
313             or LOGCROAK "read(): $OS_ERROR";
314 121 50       241 last unless $nread; # safe side?
315 121 50       87 print {$output_fh} $buffer
  121         358  
316             or LOGCROAK "print(): $OS_ERROR";
317 121         255 $data_length += $nread;
318             } ## end while (!eof $input_fh)
319              
320             # Add separator, not really needed but might come handy for
321             # peeking into the file
322 121 50       96 print {$output_fh} "\n\n"
  121         194  
323             or LOGCROAK "print(): $OS_ERROR";
324              
325 121 50       158 $name = '' unless defined $name;
326 121         80 push @{$self->{index}}, sprintf "%d %s\n", $data_length, escape($name);
  121         320  
327              
328 121         527 return $self;
329             } ## end sub add_fh
330              
331             sub write_index {
332 62     62 1 18129 my $self = shift;
333 62         57 my ($output_fh, $index) = @{$self}{qw< output_fh index >};
  62         105  
334 62 50       49 print {$output_fh} STARTER, @$index, TERMINATOR
  62         200  
335             or LOGCROAK "print(): $OS_ERROR";
336 62         167 delete $self->{$_} for qw< output_fh index >;
337 62         1060 return;
338             } ## end sub write_index
339              
340             sub DESTROY {
341 62     62   38674 my $self = shift;
342 62 100       151 $self->write_index() if exists $self->{output_fh};
343 62         1429 return;
344             }
345              
346             1;