File Coverage

blib/lib/File/Slurp.pm
Criterion Covered Total %
statement 240 260 92.3
branch 111 124 89.5
condition 34 45 75.5
subroutine 22 22 100.0
pod 7 7 100.0
total 414 458 90.3


line stmt bran cond sub pod time code
1             package File::Slurp;
2              
3 32     32   607699 use strict;
  32         280  
  32         902  
4 32     32   167 use warnings ;
  32         55  
  32         1462  
5              
6             our $VERSION = '9999.30';
7             $VERSION = eval $VERSION;
8              
9 32     32   168 use Carp ;
  32         50  
  32         2140  
10 32     32   180 use Exporter qw(import);
  32         52  
  32         1064  
11 32     32   176 use Fcntl qw( :DEFAULT ) ;
  32         76  
  32         10777  
12 32     32   235 use File::Basename ();
  32         56  
  32         610  
13 32     32   149 use File::Spec;
  32         52  
  32         999  
14 32     32   8075 use File::Temp qw(tempfile);
  32         255261  
  32         1724  
15 32     32   218 use IO::Handle ();
  32         61  
  32         626  
16 32     32   41942 use POSIX qw( :fcntl_h ) ;
  32         200546  
  32         165  
17 32     32   54160 use Errno ;
  32         82  
  32         17605  
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 210908 my $file_name = shift;
66 234 100       1224 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       901 if (ref($file_name)) {
73 16         270 my $ref_result = _check_ref($file_name, $opts);
74 16 50       148 if (ref($ref_result)) {
75 0         0 @_ = ($opts, $ref_result);
76 0         0 goto &_error;
77             }
78 16 100       191 $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         358 my $fh;
84 234 100       538 if (ref($file_name)) {
85 14         30 $fh = $file_name;
86             }
87             else {
88             # to keep with the old ways, read in :raw by default
89 220 100       8122 unless (open $fh, "<:raw", $file_name) {
90 36         335 @_ = ($opts, "read_file '$file_name' - open: $!");
91 36         225 goto &_error;
92             }
93             # even though we set raw, let binmode take place here (busted)
94 184 100       890 if (my $bm = $opts->{binmode}) {
95 13         73 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         379 my $buf;
102 198   100     1071 my $buf_ref = $opts->{buf_ref} || \$buf;
103 198         362 ${$buf_ref} = '';
  198         457  
104 198   50     737 my $blk_size = $opts->{blk_size} || 1024 * 1024;
105 198 100       2060 if (my $size = -s $fh) {
106 158 100       500 $blk_size = $size if $size < $blk_size;
107 158         347 my ($pos, $read) = 0;
108 158   100     229 do {
109 194 50       323 unless(defined($read = read $fh, ${$buf_ref}, $blk_size, $pos)) {
  194         47996  
110 0         0 @_ = ($opts, "read_file '$file_name' - read: $!");
111 0         0 goto &_error;
112             }
113 194         1249 $pos += $read;
114             } while ($read && $pos < $size);
115             }
116             else {
117 40         87 ${$buf_ref} = do { local $/; <$fh> };
  40         173  
  40         342  
  40         5302  
118             }
119 198 50 66     627 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 33     524 ${$buf_ref} =~ s/\015\012/\012/g if $is_win32 && !$opts->{binmode};
  0         0  
123              
124             # we now have a buffer filled with the file content. Figure out how to
125             # return it to the user
126 198         497 my $want_array = wantarray; # let's only ask for this once
127 198 100 100     912 if ($want_array || $opts->{array_ref}) {
128 32     32   266 use re 'taint';
  32         60  
  32         84732  
129 50         117 my $sep = $/;
130 50 100 66     216 $sep = '\n\n+' if defined $sep && $sep eq '';
131             # split the buffered content into lines
132 50         131 my @lines = length(${$buf_ref}) ?
133 50 100       95 ${$buf_ref} =~ /(.*?$sep|.+)/sg : ();
  40         18457  
134 50 100       233 chomp @lines if $opts->{chomp};
135 50 100       812 return \@lines if $opts->{array_ref};
136 22         872 return @lines;
137             }
138 148 100       824 return $buf_ref if $opts->{scalar_ref};
139             # if the function was called in scalar context, return the contents
140 120 100       292 return ${$buf_ref} if defined $want_array;
  108         14231  
141             # if we were called in void context, return nothing
142 12         176 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   77 my( $handle, $opts ) = @_ ;
152              
153             # check if we are reading from a handle (GLOB or IO object)
154              
155 21 100       186 if ( eval { $handle->isa( 'GLOB' ) || $handle->isa( 'IO' ) } ) {
  21 100       828  
156              
157             # we have a handle. deal with seeking to it if it is DATA
158              
159 18         158 my $err = _seek_data_handle( $handle, $opts ) ;
160              
161             # return the error string if any
162              
163 18 50       119 return \$err if $err ;
164              
165             # we have good handle
166 18         106 return ;
167             }
168              
169 3         7 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     18 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         128 return "$handle" ;
180             }
181              
182             sub _seek_data_handle {
183              
184 18     18   128 my( $handle, $opts ) = @_ ;
185             # store some meta-data about the __DATA__ file handle
186 18         240 $opts->{_is_data} = 0;
187 18         92 $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         54 eval{ require B } ;
  18         623  
198              
199 18 50       92 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       1016 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         9 $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         603 return ;
226             }
227              
228             *wf = \&write_file ;
229              
230             sub write_file {
231 185     185 1 1189939 my $file_name = shift;
232 185 100       824 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         333 my $fh;
237 185         373 my $no_truncate = 0;
238 185         294 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       531 if (ref($file_name)) {
242 5         141 my $ref_result = _check_ref($file_name, $opts);
243 5 50       86 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       59 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         50 $fh = $file_name;
256 4         17 $no_truncate = 1;
257             # can't do atomic or permissions on a file handle
258 4         23 delete $opts->{atomic};
259 4         42 delete $opts->{perms};
260             }
261             }
262              
263             # open the file for writing if we were given a filename
264 185 100       419 unless ($fh) {
265 181         273 $orig_filename = $file_name;
266 181 100       435 my $perms = defined($opts->{perms}) ? $opts->{perms} : 0666;
267             # set the mode for the sysopen
268 181         264 my $mode = O_WRONLY | O_CREAT;
269 181 100       395 $mode |= O_APPEND if $opts->{append};
270 181 100       367 $mode |= O_EXCL if $opts->{no_clobber};
271 181 100       363 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         1297 my $dir = File::Spec->rel2abs(File::Basename::dirname($file_name));
277 30 100 66     632 if (!defined($opts->{perms}) && -e $file_name && -f _) {
      66        
278 17         221 $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         75 local $^W = 0; # AYFKM
  30         136  
284 30         118 (undef, $file_name) = tempfile('.tempXXXXX', DIR => $dir, OPEN => 0);
285             }
286             }
287 181         7242 $fh = local *FH;
288 181 100       7588 unless (sysopen($fh, $file_name, $mode, $perms)) {
289 14         648 @_ = ($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       2325 if (my $binmode = $opts->{binmode}) {
295 9         58 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         268 my $buf_ref;
302 171         240 my $data_is_ref = 0;
303 171 100       730 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         19 $buf_ref = $opts->{buf_ref};
307 10         17 $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         19 $buf_ref = shift;
313 10         22 $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         16 ${$buf_ref} = join '', @{$_[0]};
  10         1259  
  10         1218  
318             }
319             else {
320             # good old @_ has all the data so join it.
321 141         3575 ${$buf_ref} = join '', @_;
  141         405  
322             }
323              
324             # seek and print
325 171 100       699 seek($fh, 0, SEEK_END) if $opts->{append};
326 171         290 print {$fh} ${$buf_ref};
  171         460  
  171         24847  
327 171 100       8710 truncate($fh, tell($fh)) unless $no_truncate;
328 171         2423 close($fh);
329              
330 171 100 100     2193 if ($opts->{atomic} && !rename($file_name, $orig_filename)) {
331 16         150 @_ = ($opts, "write_file '$file_name' - rename: $!");
332 16         100 goto &_error;
333             }
334              
335 155         987 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 25456 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         21 $opts->{append} = 1 ;
355             }
356             else {
357              
358             # no opts hash so insert one with the append mode
359              
360 13         65 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         95 goto &write_file
367             }
368              
369             # prepend data to the beginning of a file
370              
371             sub prepend_file {
372              
373 15     15 1 7244 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     23 grep $_ ne 'err_mode' && $_ ne 'binmode', keys %{$opts} ;
  15         84  
383              
384 15         30 delete @{$opts}{@bad_opts} ;
  15         30  
385              
386 15         27 my $prepend_data = shift ;
387 15 100       34 $prepend_data = '' unless defined $prepend_data ;
388 15 100       37 $prepend_data = ${$prepend_data} if ref $prepend_data eq 'SCALAR' ;
  1         2  
389              
390             #print "PRE [$prepend_data]\n" ;
391              
392 15         29 my $err_mode = delete $opts->{err_mode} ;
393 15         26 $opts->{ err_mode } = 'croak' ;
394 15         25 $opts->{ scalar_ref } = 1 ;
395              
396 15         22 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  15         40  
397              
398 15 100       157 if ( $@ ) {
399              
400 4         31 @_ = ( { err_mode => $err_mode },
401             "prepend_file '$file_name' - read_file: $!" ) ;
402 4         17 goto &_error ;
403             }
404              
405             #print "EXIST [$$existing_data]\n" ;
406              
407 11         24 $opts->{atomic} = 1 ;
408             my $write_result =
409 11         17 eval { write_file( $file_name, $opts,
  11         44  
410             $prepend_data, $$existing_data ) ;
411             } ;
412              
413 11 100       114 if ( $@ ) {
414              
415 3         16 @_ = ( { err_mode => $err_mode },
416             "prepend_file '$file_name' - write_file: $!" ) ;
417 3         10 goto &_error ;
418             }
419              
420 8         34 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 8392 my( $edit_code, $file_name, $opts ) = @_ ;
430 12 100       43 $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     20 grep $_ ne 'err_mode' && $_ ne 'binmode', keys %{$opts} ;
  12         68  
442              
443 12         24 delete @{$opts}{@bad_opts} ;
  12         25  
444              
445             # keep the user err_mode and force croaking on internal errors
446              
447 12         26 my $err_mode = delete $opts->{err_mode} ;
448 12         25 $opts->{ err_mode } = 'croak' ;
449              
450             # get a scalar ref for speed and slurp the file into a scalar
451              
452 12         20 $opts->{ scalar_ref } = 1 ;
453 12         55 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  12         32  
454              
455 12 100       403 if ( $@ ) {
456              
457 7         49 @_ = ( { err_mode => $err_mode },
458             "edit_file '$file_name' - read_file: $!" ) ;
459 7         26 goto &_error ;
460             }
461              
462             #print "EXIST [$$existing_data]\n" ;
463              
464 5         11 my( $edited_data ) = map { $edit_code->(); $_ } $$existing_data ;
  5         20  
  5         79  
465              
466 5         14 $opts->{atomic} = 1 ;
467             my $write_result =
468 5         6 eval { write_file( $file_name, $opts, $edited_data ) } ;
  5         16  
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         24 return $write_result ;
478             }
479              
480             *efl = \&edit_file_lines ;
481              
482             sub edit_file_lines(&$;$) {
483              
484 7     7 1 8347 my( $edit_code, $file_name, $opts ) = @_ ;
485 7 100       25 $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         13 delete @{$opts}{@bad_opts} ;
  7         11  
499              
500             # keep the user err_mode and force croaking on internal errors
501              
502 7         16 my $err_mode = delete $opts->{err_mode} ;
503 7         14 $opts->{ err_mode } = 'croak' ;
504              
505             # get an array ref for speed and slurp the file into lines
506              
507 7         13 $opts->{ array_ref } = 1 ;
508 7         11 my $existing_data = eval { read_file( $file_name, $opts ) } ;
  7         17  
509              
510 7 50       413 if ( $@ ) {
511              
512 7         49 @_ = ( { err_mode => $err_mode },
513             "edit_file_lines '$file_name' - read_file: $!" ) ;
514 7         41 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 30487 my $dir = shift ;
540 15 100       64 my $opts = ( ref $_[0] eq 'HASH' ) ? shift : { @_ } ;
541              
542             # this handle will be destroyed upon return
543              
544 15         45 local(*DIRH);
545              
546             # open the dir and handle any errors
547              
548 15 100       382 unless ( opendir( DIRH, $dir ) ) {
549              
550 7         74 @_ = ( $opts, "read_dir '$dir' - opendir: $!" ) ;
551 7         41 goto &_error ;
552             }
553              
554 8         188 my @dir_entries = readdir(DIRH) ;
555              
556             @dir_entries = grep( $_ ne "." && $_ ne "..", @dir_entries )
557 8 100 100     164 unless $opts->{'keep_dot_dot'} ;
558              
559 8 100       20 if ( $opts->{'prefix'} ) {
560              
561 2         141 $_ = File::Spec->catfile($dir, $_) for @dir_entries;
562             }
563              
564 8 100       138 return @dir_entries if wantarray ;
565 1         20 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   217 my( $opts, $err_msg ) = @_ ;
585              
586             # get the error function to use
587              
588 94   100     327 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       263 return unless $func ;
594              
595             # call the carp/croak function
596              
597 71 50       7012 $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         1077 return undef ;
603             }
604              
605             1;
606             __END__