File Coverage

blib/lib/File/Slurp.pm
Criterion Covered Total %
statement 241 261 92.3
branch 111 124 89.5
condition 39 51 76.4
subroutine 22 22 100.0
pod 7 7 100.0
total 420 465 90.3


line stmt bran cond sub pod time code
1             package File::Slurp;
2              
3 32     32   598677 use strict;
  32         295  
  32         950  
4 32     32   168 use warnings ;
  32         63  
  32         1599  
5              
6             our $VERSION = '9999.31';
7             $VERSION = eval $VERSION;
8              
9 32     32   172 use Carp ;
  32         58  
  32         2245  
10 32     32   194 use Exporter qw(import);
  32         60  
  32         1149  
11 32     32   186 use Fcntl qw( :DEFAULT ) ;
  32         58  
  32         11077  
12 32     32   240 use File::Basename ();
  32         60  
  32         659  
13 32     32   171 use File::Spec;
  32         57  
  32         1052  
14 32     32   8454 use File::Temp qw(tempfile);
  32         274726  
  32         1856  
15 32     32   268 use IO::Handle ();
  32         59  
  32         712  
16 32     32   16856 use POSIX qw( :fcntl_h ) ;
  32         211264  
  32         160  
17 32     32   56930 use Errno ;
  32         75  
  32         18854  
18              
19             my @std_export = qw(
20             read_file
21             write_file
22             overwrite_file
23             append_file
24             read_dir
25             ) ;
26              
27             my @edit_export = qw(
28             edit_file
29             edit_file_lines
30             ) ;
31              
32             my @abbrev_export = qw(
33             rf
34             wf
35             ef
36             efl
37             ) ;
38              
39             our @EXPORT_OK = (
40             @edit_export,
41             @abbrev_export,
42             qw(
43             slurp
44             prepend_file
45             ),
46             ) ;
47              
48             our %EXPORT_TAGS = (
49             'all' => [ @std_export, @edit_export, @abbrev_export, @EXPORT_OK ],
50             'edit' => [ @edit_export ],
51             'std' => [ @std_export ],
52             'abr' => [ @abbrev_export ],
53             ) ;
54              
55             our @EXPORT = @std_export ;
56              
57             my $max_fast_slurp_size = 1024 * 100 ;
58              
59             my $is_win32 = $^O =~ /win32/i ;
60              
61             *slurp = \&read_file ;
62             *rf = \&read_file ;
63              
64             sub read_file {
65 234     234 1 226210 my $file_name = shift;
66 234 100       1316 my $opts = (ref $_[0] eq 'HASH') ? shift : {@_};
67             # options we care about:
68             # array_ref binmode blk_size buf_ref chomp err_mode scalar_ref
69              
70             # let's see if we have a stringified object before doing anything else
71             # We then only have to deal with when we are given a file handle/globref
72 234 100       881 if (ref($file_name)) {
73 16         208 my $ref_result = _check_ref($file_name, $opts);
74 16 50       155 if (ref($ref_result)) {
75 0         0 @_ = ($opts, $ref_result);
76 0         0 goto &_error;
77             }
78 16 100       161 $file_name = $ref_result if $ref_result;
79             # we have now stringified $file_name if possible. if it's still a ref
80             # then we probably have a file handle
81             }
82              
83 234         382 my $fh;
84 234 100       538 if (ref($file_name)) {
85 14         29 $fh = $file_name;
86             }
87             else {
88             # to keep with the old ways, read in :raw by default
89 220 100       8130 unless (open $fh, "<:raw", $file_name) {
90 36         366 @_ = ($opts, "read_file '$file_name' - open: $!");
91 36         237 goto &_error;
92             }
93             # even though we set raw, let binmode take place here (busted)
94 184 100       939 if (my $bm = $opts->{binmode}) {
95 13         84 binmode $fh, $bm;
96             }
97             }
98              
99             # we are now sure to have an open file handle. Let's slurp it in the same
100             # way that File::Slurper does.
101 198         404 my $buf;
102 198   100     1150 my $buf_ref = $opts->{buf_ref} || \$buf;
103 198         398 ${$buf_ref} = '';
  198         811  
104 198   50     877 my $blk_size = $opts->{blk_size} || 1024 * 1024;
105 198 100 100     2599 if (my $size = -f $fh && -s _) {
106 158 100       532 $blk_size = $size if $size < $blk_size;
107 158         348 my ($pos, $read) = 0;
108 158   100     214 do {
109 194 50       320 unless(defined($read = read $fh, ${$buf_ref}, $blk_size, $pos)) {
  194         47457  
110 0         0 @_ = ($opts, "read_file '$file_name' - read: $!");
111 0         0 goto &_error;
112             }
113 194         1181 $pos += $read;
114             } while ($read && $pos < $size);
115             }
116             else {
117 40         100 ${$buf_ref} = do { local $/; <$fh> };
  40         179  
  40         339  
  40         3736  
118             }
119 198 50 66     605 seek($fh, $opts->{_data_tell}, SEEK_SET) if $opts->{_is_data} && $opts->{_data_tell};
120              
121             # line endings if we're on Windows
122 198 50 66     300 ${$buf_ref} =~ s/\015\012/\012/g if ${$buf_ref} && $is_win32 && !$opts->{binmode};
  0   33     0  
  198         1042  
123              
124             # we now have a buffer filled with the file content. Figure out how to
125             # return it to the user
126 198         433 my $want_array = wantarray; # let's only ask for this once
127 198 100 100     973 if ($want_array || $opts->{array_ref}) {
128 32     32   270 use re 'taint';
  32         64  
  32         71135  
129 50         132 my $sep = $/;
130 50 100 66     205 $sep = '\n\n+' if defined $sep && $sep eq '';
131             # split the buffered content into lines
132 50         127 my @lines = length(${$buf_ref}) ?
133 50 100       86 ${$buf_ref} =~ /(.*?$sep|.+)/sg : ();
  40         18678  
134 50 100       231 chomp @lines if $opts->{chomp};
135 50 100       816 return \@lines if $opts->{array_ref};
136 22         857 return @lines;
137             }
138 148 100       762 return $buf_ref if $opts->{scalar_ref};
139             # if the function was called in scalar context, return the contents
140 120 100       359 return ${$buf_ref} if defined $want_array;
  108         14769  
141             # if we were called in void context, return nothing
142 12         195 return;
143             }
144              
145             # errors in this sub are returned as scalar refs
146             # a normal IO/GLOB handle is an empty return
147             # an overloaded object returns its stringified as a scalarfilename
148              
149             sub _check_ref {
150              
151 21     21   144 my( $handle, $opts ) = @_ ;
152              
153             # check if we are reading from a handle (GLOB or IO object)
154              
155 21 100       216 if ( eval { $handle->isa( 'GLOB' ) || $handle->isa( 'IO' ) } ) {
  21 100       1029  
156              
157             # we have a handle. deal with seeking to it if it is DATA
158              
159 18         203 my $err = _seek_data_handle( $handle, $opts ) ;
160              
161             # return the error string if any
162              
163 18 50       130 return \$err if $err ;
164              
165             # we have good handle
166 18         136 return ;
167             }
168              
169 3         8 eval { require overload } ;
  3         18  
170              
171             # return an error if we can't load the overload pragma
172             # or if the object isn't overloaded
173              
174 3 50 33     17 return \"Bad handle '$handle' is not a GLOB or IO object or overloaded"
175             if $@ || !overload::Overloaded( $handle ) ;
176              
177             # must be overloaded so return its stringified value
178              
179 3         131 return "$handle" ;
180             }
181              
182             sub _seek_data_handle {
183              
184 18     18   103 my( $handle, $opts ) = @_ ;
185             # store some meta-data about the __DATA__ file handle
186 18         251 $opts->{_is_data} = 0;
187 18         178 $opts->{_data_tell} = 0;
188              
189             # DEEP DARK MAGIC. this checks the UNTAINT IO flag of a
190             # glob/handle. only the DATA handle is untainted (since it is from
191             # trusted data in the source file). this allows us to test if this is
192             # the DATA handle and then to do a sysseek to make sure it gets
193             # slurped correctly. on some systems, the buffered i/o pointer is not
194             # left at the same place as the fd pointer. this sysseek makes them
195             # the same so slurping with sysread will work.
196              
197 18         78 eval{ require B } ;
  18         579  
198              
199 18 50       81 if ( $@ ) {
200              
201 0         0 return <
202             Can't find B.pm with this Perl: $!.
203             That module is needed to properly slurp the DATA handle.
204             ERR
205             }
206              
207 18 100       1057 if ( B::svref_2object( $handle )->IO->IoFLAGS & 16 ) {
208              
209             # we now know we have the data handle. Let's store its original
210             # location in the file so that we can put it back after the read.
211             # this is only done for Bugwards-compatibility in some dists such as
212             # CPAN::Index::API that made use of the oddity where sysread was in use
213             # before
214 4         12 $opts->{_is_data} = 1;
215 4         11 $opts->{_data_tell} = tell($handle);
216             # set the seek position to the current tell.
217              
218             # unless( sysseek( $handle, tell( $handle ), SEEK_SET ) ) {
219             # return "read_file '$handle' - sysseek: $!" ;
220             # }
221             }
222              
223             # seek was successful, return no error string
224              
225 18         615 return ;
226             }
227              
228             *wf = \&write_file ;
229              
230             sub write_file {
231 185     185 1 1197689 my $file_name = shift;
232 185 100       847 my $opts = (ref $_[0] eq 'HASH') ? shift : {};
233             # options we care about:
234             # append atomic binmode buf_ref err_mode no_clobber perms
235              
236 185         372 my $fh;
237 185         347 my $no_truncate = 0;
238 185         334 my $orig_filename;
239             # let's see if we have a stringified object or some sort of handle
240             # or globref before doing anything else
241 185 100       602 if (ref($file_name)) {
242 5         148 my $ref_result = _check_ref($file_name, $opts);
243 5 50       88 if (ref($ref_result)) {
244             # some error happened while checking for a ref
245 0         0 @_ = ($opts, $ref_result);
246 0         0 goto &_error;
247             }
248 5 100       67 if ($ref_result) {
249             # we have now stringified $file_name from the overloaded obj
250 1         2 $file_name = $ref_result;
251             }
252             else {
253             # we now have a proper handle ref
254             # make sure we don't call truncate on it
255 4         30 $fh = $file_name;
256 4         9 $no_truncate = 1;
257             # can't do atomic or permissions on a file handle
258 4         42 delete $opts->{atomic};
259 4         19 delete $opts->{perms};
260             }
261             }
262              
263             # open the file for writing if we were given a filename
264 185 100       473 unless ($fh) {
265 181         297 $orig_filename = $file_name;
266 181 100       432 my $perms = defined($opts->{perms}) ? $opts->{perms} : 0666;
267             # set the mode for the sysopen
268 181         268 my $mode = O_WRONLY | O_CREAT;
269 181 100       419 $mode |= O_APPEND if $opts->{append};
270 181 100       359 $mode |= O_EXCL if $opts->{no_clobber};
271 181 100       396 if ($opts->{atomic}) {
272             # in an atomic write, we must open a new file in the same directory
273             # as the original to account for ACLs. We must also set the new file
274             # to the same permissions as the original unless overridden by the
275             # caller's request to set a specified permission set.
276 30         1308 my $dir = File::Spec->rel2abs(File::Basename::dirname($file_name));
277 30 100 66     625 if (!defined($opts->{perms}) && -e $file_name && -f _) {
      66        
278 17         223 $perms = 07777 & (stat $file_name)[2];
279             }
280             # we must ensure we're using a good temporary filename (doesn't already
281             # exist). This is slower, but safer.
282             {
283 30         77 local $^W = 0; # AYFKM
  30         132  
284 30         126 (undef, $file_name) = tempfile('.tempXXXXX', DIR => $dir, OPEN => 0);
285             }
286             }
287 181         7654 $fh = local *FH;
288 181 100       7972 unless (sysopen($fh, $file_name, $mode, $perms)) {
289 14         427 @_ = ($opts, "write_file '$file_name' - sysopen: $!");
290 14         86 goto &_error;
291             }
292             }
293             # we now have an open file handle as well as data to write to that handle
294 171 100       2343 if (my $binmode = $opts->{binmode}) {
295 9         61 binmode($fh, $binmode);
296             }
297              
298             # get the data to print to the file
299             # get the buffer ref - it depends on how the data is passed in
300             # after this if/else $buf_ref will have a scalar ref to the data
301 171         288 my $buf_ref;
302 171         274 my $data_is_ref = 0;
303 171 100       866 if (ref($opts->{buf_ref}) eq 'SCALAR') {
    100          
    100          
304             # a scalar ref passed in %opts has the data
305             # note that the data was passed by ref
306 10         25 $buf_ref = $opts->{buf_ref};
307 10         16 $data_is_ref = 1;
308             }
309             elsif (ref($_[0]) eq 'SCALAR') {
310             # the first value in @_ is the scalar ref to the data
311             # note that the data was passed by ref
312 10         22 $buf_ref = shift;
313 10         15 $data_is_ref = 1;
314             }
315             elsif (ref($_[0]) eq 'ARRAY') {
316             # the first value in @_ is the array ref to the data so join it.
317 10         21 ${$buf_ref} = join '', @{$_[0]};
  10         1358  
  10         1182  
318             }
319             else {
320             # good old @_ has all the data so join it.
321 141         3445 ${$buf_ref} = join '', @_;
  141         402  
322             }
323              
324             # seek and print
325 171 100       670 seek($fh, 0, SEEK_END) if $opts->{append};
326 171         276 print {$fh} ${$buf_ref};
  171         492  
  171         25110  
327 171 100       10470 truncate($fh, tell($fh)) unless $no_truncate;
328 171         2447 close($fh);
329              
330 171 100 100     2287 if ($opts->{atomic} && !rename($file_name, $orig_filename)) {
331 16         156 @_ = ($opts, "write_file '$file_name' - rename: $!");
332 16         94 goto &_error;
333             }
334              
335 155         1171 return 1;
336             }
337              
338             # this is for backwards compatibility with the previous File::Slurp module.
339             # write_file always overwrites an existing file
340             *overwrite_file = \&write_file ;
341              
342             # the current write_file has an append mode so we use that. this
343             # supports the same API with an optional second argument which is a
344             # hash ref of options.
345              
346             sub append_file {
347              
348             # get the optional opts hash ref
349 21     21 1 25691 my $opts = $_[1] ;
350 21 100       76 if ( ref $opts eq 'HASH' ) {
351              
352             # we were passed an opts ref so just mark the append mode
353              
354 8         19 $opts->{append} = 1 ;
355             }
356             else {
357              
358             # no opts hash so insert one with the append mode
359              
360 13         61 splice( @_, 1, 0, { append => 1 } ) ;
361             }
362              
363             # magic goto the main write_file sub. this overlays the sub without touching
364             # the stack or @_
365              
366 21         85 goto &write_file
367             }
368              
369             # prepend data to the beginning of a file
370              
371             sub prepend_file {
372              
373 15     15 1 7076 my $file_name = shift ;
374              
375             #print "FILE $file_name\n" ;
376              
377 15 100       52 my $opts = ( ref $_[0] eq 'HASH' ) ? shift : {} ;
378              
379             # delete unsupported options
380              
381             my @bad_opts =
382 15   100     26 grep $_ ne 'err_mode' && $_ ne 'binmode', keys %{$opts} ;
  15         85  
383              
384 15         29 delete @{$opts}{@bad_opts} ;
  15         34  
385              
386 15         23 my $prepend_data = shift ;
387 15 100       39 $prepend_data = '' unless defined $prepend_data ;
388 15 100       35 $prepend_data = ${$prepend_data} if ref $prepend_data eq 'SCALAR' ;
  1         3  
389              
390             #print "PRE [$prepend_data]\n" ;
391              
392 15         29 my $err_mode = delete $opts->{err_mode} ;
393 15         25 $opts->{ err_mode } = 'croak' ;
394 15         30 $opts->{ scalar_ref } = 1 ;
395              
396 15         19 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  15         37  
397              
398 15 100       148 if ( $@ ) {
399              
400 4         31 @_ = ( { err_mode => $err_mode },
401             "prepend_file '$file_name' - read_file: $!" ) ;
402 4         18 goto &_error ;
403             }
404              
405             #print "EXIST [$$existing_data]\n" ;
406              
407 11         23 $opts->{atomic} = 1 ;
408             my $write_result =
409 11         18 eval { write_file( $file_name, $opts,
  11         28  
410             $prepend_data, $$existing_data ) ;
411             } ;
412              
413 11 100       108 if ( $@ ) {
414              
415 3         16 @_ = ( { err_mode => $err_mode },
416             "prepend_file '$file_name' - write_file: $!" ) ;
417 3         12 goto &_error ;
418             }
419              
420 8         36 return $write_result ;
421             }
422              
423             # edit a file as a scalar in $_
424              
425             *ef = \&edit_file ;
426              
427             sub edit_file(&$;$) {
428              
429 12     12 1 8611 my( $edit_code, $file_name, $opts ) = @_ ;
430 12 100       45 $opts = {} unless ref $opts eq 'HASH' ;
431              
432             # my $edit_code = shift ;
433             # my $file_name = shift ;
434             # my $opts = ( ref $_[0] eq 'HASH' ) ? shift : {} ;
435              
436             #print "FILE $file_name\n" ;
437              
438             # delete unsupported options
439              
440             my @bad_opts =
441 12   100     19 grep $_ ne 'err_mode' && $_ ne 'binmode', keys %{$opts} ;
  12         73  
442              
443 12         26 delete @{$opts}{@bad_opts} ;
  12         29  
444              
445             # keep the user err_mode and force croaking on internal errors
446              
447 12         25 my $err_mode = delete $opts->{err_mode} ;
448 12         22 $opts->{ err_mode } = 'croak' ;
449              
450             # get a scalar ref for speed and slurp the file into a scalar
451              
452 12         24 $opts->{ scalar_ref } = 1 ;
453 12         15 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  12         30  
454              
455 12 100       455 if ( $@ ) {
456              
457 7         48 @_ = ( { err_mode => $err_mode },
458             "edit_file '$file_name' - read_file: $!" ) ;
459 7         30 goto &_error ;
460             }
461              
462             #print "EXIST [$$existing_data]\n" ;
463              
464 5         11 my( $edited_data ) = map { $edit_code->(); $_ } $$existing_data ;
  5         16  
  5         94  
465              
466 5         13 $opts->{atomic} = 1 ;
467             my $write_result =
468 5         8 eval { write_file( $file_name, $opts, $edited_data ) } ;
  5         12  
469              
470 5 50       15 if ( $@ ) {
471              
472 0         0 @_ = ( { err_mode => $err_mode },
473             "edit_file '$file_name' - write_file: $!" ) ;
474 0         0 goto &_error ;
475             }
476              
477 5         25 return $write_result ;
478             }
479              
480             *efl = \&edit_file_lines ;
481              
482             sub edit_file_lines(&$;$) {
483              
484 7     7 1 8447 my( $edit_code, $file_name, $opts ) = @_ ;
485 7 100       28 $opts = {} unless ref $opts eq 'HASH' ;
486              
487             # my $edit_code = shift ;
488             # my $file_name = shift ;
489             # my $opts = ( ref $_[0] eq 'HASH' ) ? shift : {} ;
490              
491             #print "FILE $file_name\n" ;
492              
493             # delete unsupported options
494              
495             my @bad_opts =
496 7   33     13 grep $_ ne 'err_mode' && $_ ne 'binmode', keys %{$opts} ;
  7         37  
497              
498 7         14 delete @{$opts}{@bad_opts} ;
  7         13  
499              
500             # keep the user err_mode and force croaking on internal errors
501              
502 7         14 my $err_mode = delete $opts->{err_mode} ;
503 7         16 $opts->{ err_mode } = 'croak' ;
504              
505             # get an array ref for speed and slurp the file into lines
506              
507 7         11 $opts->{ array_ref } = 1 ;
508 7         12 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  7         24  
509              
510 7 50       399 if ( $@ ) {
511              
512 7         60 @_ = ( { err_mode => $err_mode },
513             "edit_file_lines '$file_name' - read_file: $!" ) ;
514 7         31 goto &_error ;
515             }
516              
517             #print "EXIST [$$existing_data]\n" ;
518              
519 0         0 my @edited_data = map { $edit_code->(); $_ } @$existing_data ;
  0         0  
  0         0  
520              
521 0         0 $opts->{atomic} = 1 ;
522             my $write_result =
523 0         0 eval { write_file( $file_name, $opts, @edited_data ) } ;
  0         0  
524              
525 0 0       0 if ( $@ ) {
526              
527 0         0 @_ = ( { err_mode => $err_mode },
528             "edit_file_lines '$file_name' - write_file: $!" ) ;
529 0         0 goto &_error ;
530             }
531              
532 0         0 return $write_result ;
533             }
534              
535             # basic wrapper around opendir/readdir
536              
537             sub read_dir {
538              
539 15     15 1 10568 my $dir = shift ;
540 15 100       66 my $opts = ( ref $_[0] eq 'HASH' ) ? shift : { @_ } ;
541              
542             # this handle will be destroyed upon return
543              
544 15         43 local(*DIRH);
545              
546             # open the dir and handle any errors
547              
548 15 100       354 unless ( opendir( DIRH, $dir ) ) {
549              
550 7         78 @_ = ( $opts, "read_dir '$dir' - opendir: $!" ) ;
551 7         39 goto &_error ;
552             }
553              
554 8         206 my @dir_entries = readdir(DIRH) ;
555              
556             @dir_entries = grep( $_ ne "." && $_ ne "..", @dir_entries )
557 8 100 100     183 unless $opts->{'keep_dot_dot'} ;
558              
559 8 100       22 if ( $opts->{'prefix'} ) {
560              
561 2         182 $_ = File::Spec->catfile($dir, $_) for @dir_entries;
562             }
563              
564 8 100       155 return @dir_entries if wantarray ;
565 1         17 return \@dir_entries ;
566             }
567              
568             # error handling section
569             #
570             # all the error handling uses magic goto so the caller will get the
571             # error message as if from their code and not this module. if we just
572             # did a call on the error code, the carp/croak would report it from
573             # this module since the error sub is one level down on the call stack
574             # from read_file/write_file/read_dir.
575              
576              
577             my %err_func = (
578             'carp' => \&carp,
579             'croak' => \&croak,
580             ) ;
581              
582             sub _error {
583              
584 94     94   218 my( $opts, $err_msg ) = @_ ;
585              
586             # get the error function to use
587              
588 94   100     308 my $func = $err_func{ $opts->{'err_mode'} || 'croak' } ;
589              
590             # if we didn't find it in our error function hash, they must have set
591             # it to quiet and we don't do anything.
592              
593 94 100       248 return unless $func ;
594              
595             # call the carp/croak function
596              
597 71 50       7009 $func->($err_msg) if $func ;
598              
599             # return a hard undef (in list context this will be a single value of
600             # undef which is not a legal in-band value)
601              
602 22         1114 return undef ;
603             }
604              
605             1;
606             __END__