File Coverage

lib/File/DataClass/IO.pm
Criterion Covered Total %
statement 543 544 100.0
branch 229 272 85.6
condition 134 167 80.8
subroutine 139 139 100.0
pod 105 105 100.0
total 1150 1227 94.2


line stmt bran cond sub pod time code
1             package File::DataClass::IO;
2              
3 7     7   134676 use 5.010001;
  7         24  
4              
5 7     7   29 use Cwd qw( );
  7         11  
  7         102  
6 7     7   26 use English qw( -no_match_vars );
  7         7  
  7         38  
7 7     7   2007 use Fcntl qw( :flock :seek );
  7         12  
  7         844  
8 7     7   30 use File::Basename ( );
  7         11  
  7         115  
9 7     7   23 use File::Copy ( );
  7         38  
  7         158  
10 7         664 use File::DataClass::Constants qw( EXCEPTION_CLASS FALSE LOCK_BLOCKING
11             LOCK_NONBLOCKING NO_UMASK_STACK NUL
12 7     7   1647 PERMS STAT_FIELDS TILDE TRUE );
  7         17  
13 7         519 use File::DataClass::Functions qw( ensure_class_loaded first_char is_arrayref
14             is_coderef is_hashref is_member is_mswin
15 7     7   1988 is_ntfs thread_id throw );
  7         14  
16 7     7   33 use File::Spec ( );
  7         10  
  7         119  
17 7     7   1483 use File::Spec::Functions qw( curdir updir );
  7         9  
  7         289  
18 7     7   3294 use IO::Dir;
  7         94777  
  7         312  
19 7     7   44 use IO::File;
  7         11  
  7         927  
20 7     7   33 use IO::Handle;
  7         8  
  7         226  
21 7     7   34 use List::Util qw( first );
  7         18  
  7         333  
22 7     7   28 use Scalar::Util qw( blessed );
  7         11  
  7         249  
23 7     7   27 use Sub::Install qw( install_sub );
  7         8  
  7         62  
24 7     7   673 use Type::Utils qw( enum );
  7         7  
  7         66  
25 7     7   2866 use Unexpected::Functions qw( InvocantUndefined PathNotFound Unspecified );
  7         9  
  7         55  
26 7         48 use Unexpected::Types qw( ArrayRef Bool CodeRef Int Maybe Object
27 7     7   1862 PositiveInt RegexpRef SimpleStr Str );
  7         10  
28 7     7   6894 use Moo;
  7         51  
  7         57  
29              
30 7     7   2578 use namespace::clean -except => [ 'meta' ];
  7         10  
  7         109  
31 1019     1019   21508 use overload '""' => sub { $_[ 0 ]->as_string },
32 1912     1912   9011 'bool' => sub { $_[ 0 ]->as_boolean },
33 7     7   5049 'fallback' => TRUE;
  7         8  
  7         62  
34              
35             my $IO_LOCK = enum 'IO_Lock' => [ FALSE, LOCK_BLOCKING, LOCK_NONBLOCKING ];
36             my $IO_MODE = enum 'IO_Mode' => [ qw( a a+ r r+ w w+ ) ];
37             my $IO_TYPE = enum 'IO_Type' => [ qw( dir file ) ];
38              
39             # Attribute constructors
40             my $_build_dir_pattern = sub {
41 124     124   681 my $cd = curdir; my $ud = updir; qr{ \A (?: \Q${cd}\E | \Q${ud}\E ) \z }mx;
  124         110  
  124         2050  
42             };
43              
44             my $_catfile = sub {
45             return File::Spec->catfile( map { defined( $_ ) ? $_ : NUL } @_ );
46             };
47              
48             my $_expand_tilde = sub {
49             (my $path = $_[ 0 ]) =~ m{ \A ([~] [^/\\]*) .* }mx;
50              
51             my ($dir) = glob( $1 ); $path =~ s{ \A ([~] [^/\\]*) }{$dir}mx;
52              
53             return $path;
54             };
55              
56             my $_coerce_name = sub {
57             my $name = shift;
58              
59             not defined $name and return;
60             is_coderef $name and $name = $name->();
61             blessed $name and $name = "${name}";
62             is_arrayref $name and $name = $_catfile->( @{ $name } );
63             first_char $name eq TILDE and $name = $_expand_tilde->( $name );
64             curdir eq $name and $name = Cwd::getcwd();
65             CORE::length $name > 1 and $name =~ s{ [/\\] \z }{}mx;
66             return $name;
67             };
68              
69             # Public attributes
70             has 'autoclose' => is => 'lazy', isa => Bool, default => TRUE ;
71             has 'have_lock' => is => 'rwp', isa => Bool, default => FALSE ;
72             has 'io_handle' => is => 'rwp', isa => Maybe[Object] ;
73             has 'is_open' => is => 'rwp', isa => Bool, default => FALSE ;
74             has 'mode' => is => 'rwp', isa => $IO_MODE | PositiveInt,
75             default => 'r' ;
76             has 'name' => is => 'rwp', isa => SimpleStr, default => NUL,
77             coerce => $_coerce_name, lazy => TRUE ;
78             has '_perms' => is => 'rwp', isa => PositiveInt, default => PERMS,
79             init_arg => 'perms' ;
80             has 'reverse' => is => 'lazy', isa => Bool, default => FALSE ;
81             has 'sort' => is => 'lazy', isa => Bool, default => TRUE ;
82             has 'type' => is => 'rwp', isa => Maybe[$IO_TYPE] ;
83              
84             # Private attributes
85             has '_assert' => is => 'rw', isa => Bool, default => FALSE ;
86             has '_atomic' => is => 'rw', isa => Bool, default => FALSE ;
87             has '_atomic_infix' => is => 'rw', isa => SimpleStr, default => 'B_*' ;
88             has '_backwards' => is => 'rw', isa => Bool, default => FALSE ;
89             has '_block_size' => is => 'rw', isa => PositiveInt, default => 1024 ;
90             has '_chomp' => is => 'rw', isa => Bool, default => FALSE ;
91             has '_deep' => is => 'rw', isa => Bool, default => FALSE ;
92             has '_dir_pattern' => is => 'lazy', isa => RegexpRef,
93             builder => $_build_dir_pattern ;
94             has '_filter' => is => 'rw', isa => Maybe[CodeRef] ;
95             has '_layers' => is => 'ro', isa => ArrayRef[SimpleStr],
96 588     588   70438 builder => sub { [] } ;
97             has '_lock' => is => 'rw', isa => $IO_LOCK, default => FALSE ;
98             has '_no_follow' => is => 'rw', isa => Bool, default => FALSE ;
99             has '_separator' => is => 'rw', isa => Str, default => $RS ;
100             has '_umask' => is => 'ro', isa => ArrayRef[Int],
101 588     588   46124 builder => sub { [] } ;
102              
103             # Construction
104             my @ARG_NAMES = qw( name mode perms );
105              
106             my $_clone_one_of_us = sub {
107             my ($self, $params) = @_;
108              
109             $self->autoclose; $self->reverse; $self->sort; # Force evaluation
110              
111             my $clone = { %{ $self }, %{ $params // {} } };
112             my $perms = delete $clone->{_perms}; $clone->{perms} //= $perms;
113              
114             return $clone;
115             };
116              
117             my $_constructor = sub {
118             my $self = shift; return (blessed $self)->new( @_ );
119             };
120              
121             my $_inline_args = sub {
122             my $n = shift; return (map { $ARG_NAMES[ $_ ] => $_[ $_ ] } 0 .. $n - 1);
123             };
124              
125             my $_is_one_of_us = sub {
126             return (blessed $_[ 0 ]) && $_[ 0 ]->isa( __PACKAGE__ );
127             };
128              
129             sub BUILDARGS { # Differentiate constructor method signatures
130 591     591 1 71704 my $class = shift; my $n = 0; $n++ while (defined $_[ $n ]);
  591         591  
  591         2064  
131              
132             return ( $n == 0 ) ? { io_handle => IO::Handle->new }
133             : $_is_one_of_us->( $_[ 0 ] ) ? $_clone_one_of_us->( @_ )
134 2         30 : is_hashref( $_[ 0 ] ) ? { %{ $_[ 0 ] } }
135             : ( $n == 1 ) ? { $_inline_args->( 1, @_ ) }
136 591 100       1397 : is_hashref( $_[ 1 ] ) ? { name => $_[ 0 ], %{ $_[ 1 ] } }
  345 100       5161  
    100          
    100          
    100          
    100          
    100          
137             : ( $n == 2 ) ? { $_inline_args->( 2, @_ ) }
138             : ( $n == 3 ) ? { $_inline_args->( 3, @_ ) }
139             : { @_ };
140             }
141              
142             sub BUILD {
143 590     590 1 26067 my $self = shift; my $handle = $self->io_handle;
  590         960  
144              
145 590 100 100     7656 not $self->name and $handle and $self->_set_is_open( $handle->opened );
146              
147 590         6665 return;
148             }
149              
150             sub clone {
151 2 100   2 1 308 my ($self, @args) = @_; blessed $self or throw 'Clone is an object method';
  2         25  
152              
153 1         3 return $self->$_constructor( $self, @args );
154             }
155              
156             sub DEMOLISH {
157 589     589 1 97147 my ($self, $gd) = @_;
158              
159 589 50       1032 $gd and return; # uncoverable branch true
160 589 100       6987 $self->_atomic ? $self->delete : $self->close;
161 589         6722 return;
162             }
163              
164             sub import {
165 14     14   71 my ($class, @wanted) = @_; my $package = caller;
  14         31  
166              
167             (not defined $wanted[ 0 ] or $wanted[ 0 ] eq 'io')
168             and install_sub { into => $package, as => 'io', code => sub (;@) {
169 233     233   68622 return $class->new( @_ );
170 14 100 100     182 } };
171              
172 14         110371 return;
173             }
174              
175             # Private functions
176             my $_should_include_path = sub {
177             return (not defined $_[ 0 ] or (map { $_[ 0 ]->() } ($_[ 1 ]))[ 0 ]);
178             };
179              
180             # Private methods
181             my $_all_file_contents = sub {
182             my $self = shift; $self->is_open or $self->assert_open;
183              
184             local $RS = undef; my $content = $self->io_handle->getline;
185              
186             $self->error_check; $self->autoclose and $self->close;
187              
188             return $content;
189             };
190              
191             my $_find; $_find = sub {
192             my ($self, $files, $dirs, $level) = @_; my (@all, $io);
193              
194             my $filter = $self->_filter; my $follow = not $self->_no_follow;
195              
196             defined $level or $level = $self->_deep ? 0 : 1;
197              
198             while ($io = $self->next) {
199             my $is_dir = $io->is_dir; defined $is_dir or next;
200              
201             (($files and not $is_dir) or ($dirs and $is_dir))
202             and $_should_include_path->( $filter, $io ) and push @all, $io;
203              
204             $is_dir and ($follow or not $io->is_link) and $level != 1
205             and push @all, $io->$_find( $files, $dirs, $level ? $level - 1 : 0 );
206             }
207              
208             not $self->sort and return @all;
209              
210             return $self->reverse ? sort { $b->name cmp $a->name } @all
211             : sort { $a->name cmp $b->name } @all;
212             };
213              
214             my $_get_atomic_path = sub {
215             my $self = shift; my $path = $self->filepath; my $file;
216              
217             my $infix = $self->_atomic_infix; my $tid = thread_id;
218              
219             $infix =~ m{ \%P }mx and $infix =~ s{ \%P }{$PID}gmx;
220             $infix =~ m{ \%T }mx and $infix =~ s{ \%T }{$tid}gmx;
221              
222             if ($infix =~ m{ \* }mx) {
223             my $name = $self->filename; ($file = $infix) =~ s{ \* }{$name}mx;
224             }
225             else { $file = $self->filename.$infix }
226              
227             return $path ? $_catfile->( $path, $file ) : $file;
228             };
229              
230             my $_init = sub {
231             my ($self, $type, $name) = @_;
232              
233             $self->_set_io_handle( undef );
234             $self->_set_is_open ( FALSE );
235             $self->_set_name ( $name ) if ($name);
236             $self->_set_mode ( 'r' );
237             $self->_set_type ( $type );
238              
239             return $self;
240             };
241              
242             my $_mkdir_perms = sub { # Take file perms and add execute if read is true
243             my $perms = $_[ 1 ] || $_[ 0 ]->_perms;
244              
245             return (($perms & oct '0444') >> 2) | $perms;
246             };
247              
248             my $_push_layer = sub {
249             my ($self, $layer) = @_; $layer //= NUL;
250              
251             is_member $layer, $self->_layers and return FALSE;
252             push @{ $self->_layers }, $layer;
253             return TRUE;
254             };
255              
256             my $_sane_binmode = sub {
257             my ($self, $layer) = @_;
258              
259             blessed $self->io_handle eq 'File::ReadBackwards' and return;
260              
261 2     2   14 return $layer ? CORE::binmode( $self->io_handle, $layer )
  2         3  
  2         13  
262             : CORE::binmode( $self->io_handle );
263             };
264              
265             my $_throw = sub {
266             my $self = shift; eval { $self->unlock }; throw @_;
267             };
268              
269             my $_umask_pop = sub {
270             my $self = shift; my $perms = $self->_umask->[ -1 ];
271              
272             (defined $perms and $perms != NO_UMASK_STACK) or return umask;
273              
274             umask pop @{ $self->_umask };
275             return $perms;
276             };
277              
278             my $_umask_push = sub {
279             my ($self, $perms) = @_; $perms or return umask;
280              
281             my $first = $self->_umask->[ 0 ];
282              
283             defined $first and $first == NO_UMASK_STACK and return umask;
284              
285             $perms ^= oct '0777'; push @{ $self->_umask }, umask $perms;
286              
287             return $perms;
288             };
289              
290             my $_untainted_perms = sub {
291             my $self = shift; $self->exists or return;
292             my $stat = $self->stat // {};
293             my $mode = $stat->{mode} // NUL;
294             my $perms = $mode =~ m{ \A (\d+) \z }mx ? $1 : 0;
295              
296             return $perms & oct '07777';
297             };
298              
299             my $_assert_open_backwards = sub {
300             my ($self, @args) = @_; $self->is_open and return;
301              
302             ensure_class_loaded 'File::ReadBackwards';
303              
304             $self->_set_io_handle( File::ReadBackwards->new( $self->name, @args ) )
305             or $self->$_throw( 'File [_1] cannot open backwards: [_2]',
306             [ $self->name, $OS_ERROR ] );
307             $self->_set_is_open( TRUE );
308             $self->_set_mode( 'r' );
309             $self->set_lock;
310             $self->set_binmode;
311             return;
312             };
313              
314             my $_init_type_from_fs = sub {
315             my $self = shift;
316              
317             CORE::length $self->name or $self->$_throw( Unspecified, [ 'path name' ] );
318              
319             return -f $self->name ? $self->file : -d _ ? $self->dir : undef;
320             };
321              
322             my $_open_args = sub {
323             my ($self, $mode, $perms) = @_;
324              
325             CORE::length $self->name or $self->$_throw( Unspecified, [ 'path name' ] );
326              
327             my $pathname = $self->_atomic && !$self->is_reading( $mode )
328             ? $self->$_get_atomic_path : $self->name;
329              
330             $perms = $self->$_untainted_perms || $perms || $self->_perms;
331              
332             return ($pathname, $self->_set_mode( $mode ), $self->_set__perms( $perms ));
333             };
334              
335             my $_open_dir = sub {
336             my ($self, $path) = @_;
337              
338             $self->_assert and $self->assert_dirpath( $path );
339             $self->_set_io_handle( IO::Dir->new( $path ) )
340             or $self->$_throw( 'Directory [_1] cannot open', [ $path ] );
341             $self->_set_is_open( TRUE );
342             return $self;
343             };
344              
345             my $_open_file = sub {
346             my ($self, $path, $mode, $perms) = @_;
347              
348             $self->_assert and $self->assert_filepath; $self->$_umask_push( $perms );
349              
350             unless ($self->_set_io_handle( IO::File->new( $path, $mode ) )) {
351             $self->$_umask_pop;
352             $self->$_throw( 'File [_1] cannot open', [ $path ] );
353             }
354              
355             $self->$_umask_pop;
356             # TODO: Not necessary on normal systems
357             $self->is_writing and CORE::chmod $perms, $path;
358             $self->_set_is_open( TRUE );
359             $self->set_lock;
360             $self->set_binmode;
361             return $self;
362             };
363              
364             my $_print = sub {
365             my ($self, @args) = @_;
366              
367             for (@args) {
368             print {$self->io_handle} $_
369             or $self->$_throw( 'IO error: [_1]', [ $OS_ERROR ] );
370             }
371              
372             return $self;
373             };
374              
375             my $_rename_atomic = sub {
376             my $self = shift; my $path = $self->$_get_atomic_path; -f $path or return;
377              
378             File::Copy::move( $path, $self->name ) and return;
379              
380             is_ntfs or $self->$_throw( 'Path [_1] move to [_2] failed: [_3]',
381             [ $path, $self->name, $OS_ERROR ] );
382              
383             # Try this instead on ntfs
384             warn 'NTFS: Path '.$self->name." move failure: ${OS_ERROR}\n";
385             eval { unlink $self->name }; my $os_error;
386             File::Copy::copy( $path, $self->name ) or $os_error = $OS_ERROR;
387             eval { unlink $path };
388             $os_error and $self->$_throw( 'Path [_1] copy to [_2] failed: [_3]',
389             [ $path, $self->name, $os_error ] );
390             return;
391             };
392              
393             my $_close_and_rename = sub { # This creates a race condition
394             # uncoverable subroutine
395             my $self = shift; # uncoverable statement
396              
397             my $handle; $self->unlock;
398              
399             if ($handle = $self->io_handle) { $handle->close; delete $self->{io_handle} }
400              
401             $self->_atomic and $self->$_rename_atomic;
402              
403             return $self;
404             };
405              
406             my $_getline_backwards = sub {
407             my ($self, @args) = @_; $self->$_assert_open_backwards( @args );
408              
409             return $self->io_handle->readline;
410             };
411              
412             my $_println = sub {
413             return shift->$_print( map { m{ [\n] \z }mx ? ($_) : ($_, "\n") } @_ );
414             };
415              
416             my $_rename_and_close = sub { # This does not create a race condition
417             my $self = shift; my $handle;
418              
419             $self->_atomic and $self->$_rename_atomic; $self->unlock;
420              
421             if ($handle = $self->io_handle) { $handle->close; delete $self->{io_handle} }
422              
423             return $self;
424             };
425              
426             my $_getlines_backwards = sub {
427             my $self = shift; my (@lines, $line);
428              
429             while (defined ($line = $self->$_getline_backwards)) { push @lines, $line }
430              
431             return @lines;
432             };
433              
434             # Public methods
435             sub abs2rel {
436 48     48 1 701 return File::Spec->abs2rel( $_[ 0 ]->name, $_[ 1 ] );
437             }
438              
439             sub absolute {
440 4 100   4 1 4 my ($self, $base) = @_; $base and $base = $_coerce_name->( $base );
  4         12  
441              
442 4 100       50 $self->_set_name
443             ( (CORE::length $self->name) ? $self->rel2abs( $base ) : $base );
444 4         138 return $self;
445             }
446              
447             sub all {
448 69     69 1 211 my ($self, $level) = @_;
449              
450 69 100       133 $self->is_dir and return $self->$_find( TRUE, TRUE, $level );
451              
452 59         180 return $self->$_all_file_contents;
453             }
454              
455             sub all_dirs {
456 10     10 1 32 return $_[ 0 ]->$_find( FALSE, TRUE, $_[ 1 ] );
457             }
458              
459             sub all_files {
460 8     8 1 24 return $_[ 0 ]->$_find( TRUE, FALSE, $_[ 1 ] );
461             }
462              
463             sub append {
464 3     3 1 24 my ($self, @args) = @_;
465              
466 3 100 100     14 if ($self->is_open and not $self->is_reading) { $self->seek( 0, SEEK_END ) }
  1         3  
467 2         4 else { $self->assert_open( 'a' ) }
468              
469 3         10 return $self->$_print( @args );
470             }
471              
472             sub appendln {
473 3     3 1 7 my ($self, @args) = @_;
474              
475 3 100 100     14 if ($self->is_open and not $self->is_reading) { $self->seek( 0, SEEK_END ) }
  1         3  
476 2         5 else { $self->assert_open( 'a' ) }
477              
478 3         11 return $self->$_println( @args );
479             }
480              
481             sub as_boolean {
482 1912 100 100 1912 1 22947 return ((CORE::length $_[ 0 ]->name) || $_[ 0 ]->io_handle) ? TRUE : FALSE;
483             }
484              
485             sub as_string {
486 1019 100   1019 1 838 my $self = shift; CORE::length $self->name and return $self->name;
  1019         12763  
487              
488 1 50       15 return defined $self->io_handle ? $self->io_handle.NUL : NUL;
489             }
490              
491             sub assert {
492 3     3 1 2805 my ($self, $cb) = @_;
493              
494 3 100       6 if ($cb) {
495 2         3 local $_ = $self;
496 2 100       5 $cb->() or throw 'Path [_1] assertion failure', [ $self->name ];
497             }
498 1         16 else { $self->_assert( TRUE ) }
499              
500 2         59 return $self;
501             }
502              
503             sub assert_dirpath {
504 8     8 1 11 my ($self, $dir_name) = @_;
505              
506 8 100       15 $dir_name or return; -d $dir_name and return $dir_name;
  5 100       65  
507              
508 4         8 my $perms = $self->$_mkdir_perms; $self->$_umask_push( oct '07777' );
  4         8  
509              
510 4 100       80 unless (CORE::mkdir( $dir_name, $perms )) {
511 2         6 ensure_class_loaded 'File::Path';
512 2         508 File::Path::make_path( $dir_name, { mode => $perms } );
513             }
514              
515 3         12 $self->$_umask_pop;
516              
517             # uncoverable branch true
518 3 50       28 -d $dir_name or $self->$_throw( 'Path [_1] cannot create: [_2]',
519             [ $dir_name, $OS_ERROR ] );
520 3         4 return $dir_name;
521             }
522              
523             sub assert_filepath {
524 7     7 1 17 my $self = shift; my $dir;
  7         8  
525              
526 7 100       85 CORE::length $self->name or $self->$_throw( Unspecified, [ 'path name' ] );
527              
528 6         94 (undef, $dir) = File::Spec->splitpath( $self->name );
529              
530 6         64 $self->assert_dirpath( $dir );
531 6         13 return $self;
532             }
533              
534             sub assert_open {
535 667   100 667 1 2757 return $_[ 0 ]->open( $_[ 1 ] // 'r', $_[ 2 ] );
536             }
537              
538             sub atomic {
539 34     34 1 560 $_[ 0 ]->_atomic( TRUE ); return $_[ 0 ];
  34         668  
540             }
541              
542             sub atomic_infix {
543 2 100   2 1 18 defined $_[ 1 ] and $_[ 0 ]->_atomic_infix( $_[ 1 ] ); return $_[ 0 ];
  2         37  
544             }
545              
546             sub atomic_suffix {
547 2 100   2 1 421 defined $_[ 1 ] and $_[ 0 ]->_atomic_infix( $_[ 1 ] ); return $_[ 0 ];
  2         32  
548             }
549              
550             sub backwards {
551 2     2 1 30 $_[ 0 ]->_backwards( TRUE ); return $_[ 0 ];
  2         37  
552             }
553              
554             sub basename {
555 2 100   2 1 5 my ($self, @suffixes) = @_; CORE::length $self->name or return;
  2         26  
556              
557 1         17 return File::Basename::basename( $self->name, @suffixes );
558             }
559              
560             sub binary {
561 3     3 1 23 my $self = shift;
562              
563 3 100 100     7 $self->$_push_layer( ':raw' ) and $self->is_open and $self->$_sane_binmode;
564              
565 3         9 return $self;
566             }
567              
568             sub binmode {
569 7     7 1 10 my ($self, $layer) = @_;
570              
571 7 100 100     11 $self->$_push_layer( $layer )
572             and $self->is_open and $self->$_sane_binmode( $layer );
573              
574 7         15 return $self;
575             }
576              
577             sub block_size {
578 4 100   4 1 57 defined $_[ 1 ] and $_[ 0 ]->_block_size( $_[ 1 ] ); return $_[ 0 ];
  4         99  
579             }
580              
581             sub buffer {
582 163     163 1 105 my $self = shift;
583              
584 163 100       198 if (@_) {
585 2 100       6 my $buffer_ref = ref $_[ 0 ] ? $_[ 0 ] : \$_[ 0 ];
586              
587 2 100       2 defined ${ $buffer_ref } or ${ $buffer_ref } = NUL;
  1         2  
  2         5  
588 2         3 $self->{buffer} = $buffer_ref;
589 2         3 return $self;
590             }
591              
592 161 100       197 exists $self->{buffer} or $self->{buffer} = do { my $x = NUL; \$x };
  1         2  
  1         2  
593              
594 161         732 return $self->{buffer};
595             }
596              
597             sub canonpath {
598 2     2 1 27 return File::Spec->canonpath( $_[ 0 ]->name );
599             }
600              
601             sub catdir {
602 3     3 1 4 my ($self, @args) = @_; return $self->child( @args )->dir;
  3         7  
603             }
604              
605             sub catfile {
606 5     5 1 7 my ($self, @args) = @_; return $self->child( @args )->file;
  5         18  
607             }
608              
609             sub child {
610 10     10 1 13 my ($self, @args) = @_;
611              
612 10 100       24 my $params = (is_hashref $args[ -1 ]) ? pop @args : {};
613 10 100       138 my $args = [ grep { defined and CORE::length } $self->name, @args ];
  26         110  
614              
615 10         23 return $self->$_constructor( $args, $params );
616             }
617              
618             sub chmod {
619 4     4 1 1215 my ($self, $perms) = @_;
620              
621 4   100     17 $perms //= $self->_perms; # uncoverable condition false
622 4         71 CORE::chmod $perms, $self->name;
623 4         96 return $self;
624             }
625              
626             sub chomp {
627 5     5 1 110 $_[ 0 ]->_chomp( TRUE ); return $_[ 0 ];
  5         106  
628             }
629              
630             sub chown {
631 4     4 1 4281 my ($self, $uid, $gid) = @_;
632              
633 4 100 100     28 (defined $uid and defined $gid)
634             or $self->$_throw( Unspecified, [ 'user or group id' ] );
635              
636 2 50       44 1 == CORE::chown $uid, $gid, $self->name
637             or $self->$_throw( 'Path [_1 chown failed to [_2]/[_3]',
638             [ $self->name, $uid, $gid ] );
639 2         77 return $self;
640             }
641              
642             sub clear {
643 35     35 1 32 ${ $_[ 0 ]->buffer } = NUL; return $_[ 0 ];
  35         44  
  35         40  
644             }
645              
646             sub close {
647 892 100   892 1 7003 my $self = shift; $self->is_open or return $self;
  892         2008  
648              
649 273 50       577 if (is_ntfs) { # uncoverable branch true
650 0         0 $self->$_close_and_rename; # uncoverable statement
651 273         442 } else { $self->$_rename_and_close }
652              
653 273         6875 $self->_set_io_handle( undef );
654 273         6973 $self->_set_is_open ( FALSE );
655 273         6560 $self->_set_mode ( 'r' );
656 273         3541 return $self;
657             }
658              
659             sub copy {
660 4     4 1 64 my ($self, $to) = @_;
661              
662 4 50       14 $to or $self->$_throw( Unspecified, [ 'copy to' ] );
663              
664 4 100 100     64 (blessed $to and $to->isa( __PACKAGE__ ))
665             or $to = $self->$_constructor( $to );
666              
667 4 50       51 File::Copy::copy( $self->name, $to->pathname )
668             or $self->$_throw( 'Cannot copy [_1] to [_2]',
669             [ $self->name, $to->pathname ] );
670              
671 4         1001 return $to;
672             }
673              
674             sub cwd {
675 1     1 1 1 my $self = shift; return $self->$_constructor( Cwd::getcwd(), @_ );
  1         10  
676             }
677              
678             sub deep {
679 10     10 1 156 $_[ 0 ]->_deep( TRUE ); return $_[ 0 ];
  10         230  
680             }
681              
682             sub delete {
683 21     21 1 113 my $self = shift; my $path = $self->$_get_atomic_path;
  21         45  
684              
685 21 100 100     290 $self->_atomic and -f $path and unlink $path;
686              
687 21         461 return $self->close;
688             }
689              
690             sub delete_tmp_files {
691 2   100 2 1 526 my ($self, $tmplt) = @_; $tmplt //= '%6.6d....';
  2         13  
692              
693 2         15 my $pat = sprintf $tmplt, $PID;
694              
695 2         6 while (my $entry = $self->next) {
696 48 50       240 $entry->filename =~ m{ \A $pat \z }mx and unlink $entry->pathname;
697             }
698              
699 2         7 return $self->close;
700             }
701              
702             sub digest { # Robbed from Path::Tiny
703 4     4 1 6 my ($self, @args) = @_; my $n = 0; $n++ while (defined $args[ $n ]);
  4         6  
  4         13  
704              
705             my $args = ( $n == 0) ? { algorithm => 'SHA-256' }
706             : (is_hashref $args[ 0 ]) ? { algorithm => 'SHA-256',
707 1         4 %{ $args[ 0 ] } }
708             : ( $n == 1) ? { algorithm => $args[ 0 ] }
709             : { algorithm => $args[ 0 ],
710 4 100       15 %{ $args[ 1 ] } };
  1 100       4  
    100          
711              
712 4         11 ensure_class_loaded 'Digest'; my $digest = Digest->new( $args->{algorithm} );
  4         79  
713              
714 4 100       3290 if ($args->{block_size}) {
715 2         5 $self->binmode( ':unix' )->lock->block_size( $args->{block_size} );
716              
717 2         4 while ($self->read) { $digest->add( ${ $self->buffer } ); $self->clear; }
  20         12  
  20         22  
  20         25  
718             }
719 2         6 else { $digest->add( $self->binmode( ':unix' )->lock->all ) }
720              
721 4         58 return $digest;
722             }
723              
724             sub dir {
725 172     172 1 2524 return shift->$_init( 'dir', @_ );
726             }
727              
728             sub dirname {
729 5 50   5 1 63 return CORE::length $_[ 0 ]->name ? File::Basename::dirname( $_[ 0 ]->name )
730             : NUL;
731             }
732              
733             sub encoding {
734 4     4 1 9 my ($self, $encoding) = @_;
735              
736 4 100       16 $encoding or $self->$_throw( Unspecified, [ 'encoding value' ] );
737 3 50 33     14 $self->$_push_layer( ":encoding($encoding)" )
738             and $self->is_open and $self->$_sane_binmode( ":encoding($encoding)" );
739 3         6 return $self;
740             }
741              
742             sub error_check {
743 147     147 1 148 my $self = shift;
744              
745 147 50 33     1040 $self->io_handle->can( 'error' )
746             and $self->io_handle->error
747             and $self->$_throw( 'IO error: [_1]', [ $OS_ERROR ] );
748              
749 147         153 return $self;
750             }
751              
752             sub exists {
753 1079 100 100 1079 1 14052 return (CORE::length $_[ 0 ]->name && -e $_[ 0 ]->name) ? TRUE : FALSE;
754             }
755              
756             sub fdopen {
757 1     1 1 20 my ($self, $fd, $mode) = @_;
758              
759 1         5 $self->io_handle->fdopen( $fd, $mode );
760 1         321 $self->_set_is_open( $self->io_handle->opened );
761 1         139 $self->_set_mode( $mode );
762 1         32 $self->_set_name( NUL );
763 1         49 $self->_set_type( undef );
764 1         14 return $self;
765             }
766              
767             sub file {
768 240     240 1 3605 return shift->$_init( 'file', @_ );
769             }
770              
771             sub filename {
772 504     504 1 487 my $self = shift; my $file;
  504         343  
773              
774 504         6500 (undef, undef, $file) = File::Spec->splitpath( $self->name );
775              
776 504         6401 return $file;
777             }
778              
779             sub filepath {
780 111     111 1 78 my $self = shift; my ($volume, $dir) = File::Spec->splitpath( $self->name );
  111         1355  
781              
782 111         1705 return File::Spec->catpath( $volume, $dir, NUL );
783             }
784              
785             sub filter {
786 33 50   33 1 874 defined $_[ 1 ] and $_[ 0 ]->_filter( $_[ 1 ] ); return $_[ 0 ];
  33         394  
787             }
788              
789             sub getline {
790 19     19 1 25 my ($self, $separator) = @_;
791              
792 19 100       289 $self->_backwards and return $self->$_getline_backwards;
793              
794 18         114 my $line; $self->assert_open;
  18         30  
795              
796 18   66     24 { local $RS = $separator // $self->_separator; # uncoverable condition false
  18         283  
797 18         375 $line = $self->io_handle->getline;
798 18 50 66     730 defined $line and $self->_chomp and CORE::chomp $line;
799             }
800              
801 18         121 $self->error_check;
802 18 100       52 defined $line and return $line;
803 1 50       17 $self->autoclose and $self->close;
804 1         1 return;
805             }
806              
807             sub getlines {
808 7     7 1 606 my ($self, $separator) = @_;
809              
810 7 100       100 $self->_backwards and return $self->$_getlines_backwards;
811              
812 6         27 my @lines; $self->assert_open;
  6         11  
813              
814 6   66     9 { local $RS = $separator // $self->_separator; # uncoverable condition false
  6         67  
815 6         146 @lines = $self->io_handle->getlines;
816              
817 6 100       570 if ($self->_chomp) { CORE::chomp for @lines }
  5         83  
818             }
819              
820 6         16 $self->error_check;
821 6 100       58 scalar @lines and return (@lines);
822 1 50       15 $self->autoclose and $self->close;
823 1         3 return ();
824             }
825              
826             sub head {
827 2   100 2 1 3 my ($self, $lines) = @_; my @res; $lines //= 10; $self->close;
  2         2  
  2         9  
  2         4  
828              
829 2         5 while ($lines--) {
830 13 50       18 defined (my $l = $self->getline) or last; push @res, $l;
  13         27  
831             }
832              
833 2         7 $self->close;
834 2 50       20 return wantarray ? @res : join NUL, @res;
835             }
836              
837             sub hexdigest {
838 4     4 1 11 my ($self, @args) = @_; return $self->digest( @args )->hexdigest;
  4         10  
839             }
840              
841             sub is_absolute {
842 2     2 1 28 return File::Spec->file_name_is_absolute( $_[ 0 ]->name );
843             }
844              
845             sub is_dir {
846 674 100   674 1 2186 my $self = shift; CORE::length $self->name or return FALSE;
  674         8676  
847              
848 672 100 100     4163 $self->type or $self->$_init_type_from_fs or return FALSE;
849              
850 671 100       3214 return $self->type eq 'dir' ? TRUE : FALSE;
851             }
852              
853             sub is_empty {
854 43     43 1 53 my $self = shift; my $name = $self->name; my $empty;
  43         611  
  43         175  
855              
856 43 100       89 $self->exists or $self->$_throw( PathNotFound, [ $name ] );
857 40 100       1196 $self->is_file and return -z $name ? TRUE : FALSE;
    100          
858 2 50       6 $empty = $self->next ? FALSE : TRUE; $self->close;
  2         5  
859 2         6 return $empty;
860             }
861              
862             *empty = \&is_empty; # Deprecated
863              
864             sub is_executable {
865 3 100 100 3 1 1299 return (CORE::length $_[ 0 ]->name) && -x $_[ 0 ]->name ? TRUE : FALSE;
866             }
867              
868             sub is_file {
869 42 100   42 1 57 my $self = shift; CORE::length $self->name or return FALSE;
  42         553  
870              
871 41 100 100     311 $self->type or $self->$_init_type_from_fs or return FALSE;
872              
873 40 100       533 return $self->type eq 'file' ? TRUE : FALSE;
874             }
875              
876             sub is_link {
877 463 100 100 463 1 6057 return (CORE::length $_[ 0 ]->name) && -l $_[ 0 ]->name ? TRUE : FALSE;
878             }
879              
880             sub is_readable {
881 2 100 66 2 1 38 return (CORE::length $_[ 0 ]->name) && -r $_[ 0 ]->name ? TRUE : FALSE;
882             }
883              
884             sub is_reading {
885 96   66 96 1 537 my $mode = $_[ 1 ] // $_[ 0 ]->mode; return first { $_ eq $mode } qw( r r+ );
  61     61   396  
  61         279  
886             }
887              
888             sub is_writable {
889 5 100 66 5 1 220 return (CORE::length $_[ 0 ]->name) && -w $_[ 0 ]->name ? TRUE : FALSE;
890             }
891              
892             sub is_writing {
893 140   66 140 1 607 my $mode = $_[ 1 ] // $_[ 0 ]->mode;
894              
895 140     489   626 return first { $_ eq $mode } qw( a a+ w w+ );
  489         1801  
896             }
897              
898             sub iterator {
899 5     5 1 36 my ($self, $args) = @_;
900              
901 5 50       9 $self->is_dir
902             or $self->$_throw( "Path [_1] is not a directory", [ $self->name ] );
903              
904 5         13 my @dirs = ( $self );
905 5         64 my $filter = $self->_filter;
906 5   100     76 my $deep = $args->{recurse} // $self->_deep;
907 5   100     75 my $follow = $args->{follow_symlinks} // not $self->_no_follow;
908              
909             return sub {
910 40     40   544 while (@dirs) {
911 51         292 while (defined (my $path = $dirs[ 0 ]->next)) {
912 44 100 100     105 $deep and $path->is_dir and ($follow or not $path->is_link)
      100        
      66        
913             and unshift @dirs, $path;
914 44 100       165 $_should_include_path->( $filter, $path ) and return $path;
915             }
916              
917 15         156 shift @dirs;
918             }
919              
920 4         8 return;
921 5         44 };
922             }
923              
924             sub length {
925 53     53 1 126 return CORE::length ${ $_[ 0 ]->buffer };
  53         61  
926             }
927              
928             sub lock {
929 70   100 70 1 1310 $_[ 0 ]->_lock( $_[ 1 ] // LOCK_BLOCKING ); return $_[ 0 ];
  70         1556  
930             }
931              
932             sub mkdir {
933 2   33 2 1 22 my ($self, $perms) = @_; $perms ||= $self->$_mkdir_perms;
  2         10  
934              
935 2         6 $self->$_umask_push( oct '07777' );
936              
937 2         30 CORE::mkdir( $self->name, $perms );
938              
939 2         141 $self->$_umask_pop;
940              
941 2 50       36 -d $self->name or $self->$_throw( 'Path [_1] cannot create: [_2]',
942             [ $self->name, $OS_ERROR ] );
943 2         30 return $self;
944             }
945              
946             sub mkpath {
947 1   33 1 1 2 my ($self, $perms) = @_; $perms ||= $self->$_mkdir_perms;
  1         6  
948              
949 1         3 $self->$_umask_push( oct '07777' ); ensure_class_loaded 'File::Path';
  1         5  
950              
951 1         60 File::Path::make_path( $self->name, { mode => $perms } );
952              
953 1         302 $self->$_umask_pop;
954              
955 1 50       18 -d $self->name or $self->$_throw( 'Path [_1] cannot create: [_2]',
956             [ $self->name, $OS_ERROR ] );
957 1         16 return $self;
958             }
959              
960             sub move {
961 3     3 1 27 my ($self, $to) = @_;
962              
963 3 50       11 $to or $self->$_throw( Unspecified, [ 'move to' ] );
964              
965 3 100 100     32 (blessed $to and $to->isa( __PACKAGE__ ))
966             or $to = $self->$_constructor( $to );
967              
968 3 50       38 File::Copy::move( $self->name, $to->pathname )
969             or $self->$_throw( 'Cannot move [_1] to [_2]',
970             [ $self->name, $to->pathname ] );
971              
972 3         203 return $to;
973             }
974              
975             sub next {
976 457 100   457 1 1911 my $self = shift; defined (my $name = $self->read_dir) or return;
  457         644  
977              
978 333         4389 my $io = $self->$_constructor( [ $self->name, $name ], {
979             reverse => $self->reverse, sort => $self->sort } );
980              
981 333 100       4623 defined $self->_filter and $io->filter( $self->_filter );
982              
983 333         1673 return $io;
984             }
985              
986             sub no_follow {
987 2     2 1 31 $_[ 0 ]->_no_follow( TRUE ); return $_[ 0 ];
  2         40  
988             }
989              
990             sub open {
991 669   66 669 1 1189 my ($self, $mode, $perms) = @_; $mode //= $self->mode;
  669         1042  
992              
993 669 100 100     1961 $self->is_open
994             and first_char $mode eq first_char $self->mode
995             and return $self;
996 263 50 100     555 $self->is_open
      50        
      66        
      33        
997             and 'r' eq first_char $mode
998             and '+' eq (substr $self->mode, 1, 1) || NUL
999             and $self->seek( 0, SEEK_SET )
1000             and return $self;
1001 263 100       548 $self->type or $self->$_init_type_from_fs; $self->type or $self->file;
  262 100       833  
1002 262 100       508 $self->is_open and $self->close;
1003              
1004 262 100       401 return $self->is_dir
1005             ? $self->$_open_dir ( $self->$_open_args( $mode, $perms ) )
1006             : $self->$_open_file( $self->$_open_args( $mode, $perms ) );
1007             }
1008              
1009             sub parent {
1010 3   100 3 1 2 my ($self, $count) = @_; my $parent = $self; $count ||= 1;
  3         3  
  3         9  
1011              
1012 3         10 $parent = $self->$_constructor( $parent->dirname ) while ($count--);
1013              
1014 3         28 return $parent;
1015             }
1016              
1017             sub pathname {
1018 11     11 1 181 return $_[ 0 ]->name;
1019             }
1020              
1021             sub perms {
1022 11 50   11 1 406 defined $_[ 1 ] and $_[ 0 ]->_set__perms( $_[ 1 ] ); return $_[ 0 ];
  11         349  
1023             }
1024              
1025             sub print {
1026 43     43 1 1356 return shift->assert_open( 'w' )->$_print( @_ );
1027             }
1028              
1029             sub println {
1030 16     16 1 454 return shift->assert_open( 'w' )->$_println( @_ );
1031             }
1032              
1033             sub read {
1034 38     38 1 295 my ($self, @args) = @_; $self->assert_open;
  38         45  
1035              
1036             my $length = @args || $self->is_dir
1037             ? $self->io_handle->read( @args )
1038 38 50 33     98 : $self->io_handle->read( ${ $self->buffer },
  38         50  
1039             $self->_block_size, $self->length );
1040              
1041 38         309 $self->error_check;
1042              
1043 38   66     133 return $length || $self->autoclose && $self->close && 0;
1044             }
1045              
1046             sub read_dir {
1047 458 100   458 1 306 my $self = shift; $self->type or $self->dir; $self->assert_open;
  458         820  
  458         603  
1048              
1049 457 50 66     871 $self->is_link and $self->_no_follow and $self->close and return;
      33        
1050              
1051 457         18866 my $dir_pat = $self->_dir_pattern; my $name;
  457         2720  
1052              
1053 457 100       758 if (wantarray) {
1054 1         7 my @names = grep { $_ !~ $dir_pat } $self->io_handle->read;
  7         26  
1055              
1056 1         3 $self->close; return @names;
  1         5  
1057             }
1058              
1059 456   100     939 while (not defined $name or $name =~ $dir_pat) {
1060 704 100       3945 unless (defined ($name = $self->io_handle->read)) {
1061 123         876 $self->close; return;
  123         506  
1062             }
1063             }
1064              
1065 333         3673 return $name;
1066             }
1067              
1068             sub rel2abs {
1069 4     4 1 20 my ($self, $base) = @_;
1070              
1071 4 100       47 return File::Spec->rel2abs( $self->name, defined $base ? "${base}" : undef );
1072             }
1073              
1074             sub relative {
1075 47     47 1 606 $_[ 0 ]->_set_name( $_[ 0 ]->abs2rel ); return $_[ 0 ];
  47         1841  
1076             }
1077              
1078             sub reset {
1079 1     1 1 2 my $self = shift; $self->close;
  1         3  
1080              
1081 1         13 $self->_assert( FALSE ); $self->_atomic( FALSE ); $self->_chomp ( FALSE );
  1         32  
  1         29  
1082 1         33 $self->_deep ( FALSE ); $self->_lock ( FALSE ); $self->_no_follow( FALSE );
  1         29  
  1         33  
1083 1         21 return $self;
1084             }
1085              
1086             sub rmdir {
1087 2     2 1 4 my $self = shift;
1088              
1089 2 100       44 CORE::rmdir $self->name
1090             or $self->$_throw( 'Path [_1] not removed: [_2]',
1091             [ $self->name, $OS_ERROR ] );
1092 1         67 return $self;
1093             }
1094              
1095             sub rmtree {
1096 2     2 1 4 my ($self, @args) = @_; ensure_class_loaded 'File::Path';
  2         7  
1097              
1098 2         91 return File::Path::remove_tree( $self->name, @args );
1099             }
1100              
1101             sub seek {
1102 5     5 1 56 my ($self, $posn, $whence) = @_;
1103              
1104 5 50       17 $self->is_open or $self->assert_open( is_mswin ? 'r' : 'r+' );
    100          
1105 5         44 CORE::seek $self->io_handle, $posn, $whence; $self->error_check;
  5         12  
1106 5         7 return $self;
1107             }
1108              
1109             sub separator {
1110 1 50   1 1 18 defined $_[ 1 ] and $_[ 0 ]->_separator( $_[ 1 ] ); return $_[ 0 ];
  1         22  
1111             }
1112              
1113             sub set_binmode {
1114 144     144 1 137 my $self = shift;
1115              
1116 144 50       343 is_ntfs and $self->$_push_layer(); # uncoverable branch true
1117              
1118 144         136 $self->$_sane_binmode( $_ ) for (@{ $self->_layers });
  144         359  
1119              
1120 144         17703 return $self;
1121             }
1122              
1123             sub set_lock {
1124 144 100   144 1 162 my $self = shift; $self->_lock or return;
  144         1910  
1125              
1126 67 100       1002 my $async = $self->_lock == LOCK_NONBLOCKING ? TRUE : FALSE;
1127 67 100       387 my $mode = $self->mode eq 'r' ? LOCK_SH : LOCK_EX;
1128              
1129 67 100       122 $async and $mode |= LOCK_NB;
1130 67 50       1306 $self->_set_have_lock( (flock $self->io_handle, $mode) ? TRUE : FALSE );
1131 67         988 return $self;
1132             }
1133              
1134             sub sibling {
1135 1     1 1 2 my $self = shift; return $self->parent->child( @_ );
  1         3  
1136             }
1137              
1138             sub slurp {
1139 13     13 1 39 my $self = shift; my $slurp = $self->all;
  13         29  
1140              
1141 12 100       67 wantarray or return $slurp; local $RS = $self->_separator;
  2         51  
1142              
1143 2 50       36 $self->_chomp or return split m{ (?<=\Q$RS\E) }mx, $slurp;
1144              
1145 2         3323 return map { CORE::chomp; $_ } split m{ (?<=\Q$RS\E) }mx, $slurp;
  1450         826  
  1450         1176  
1146             }
1147              
1148             sub splitdir {
1149 1     1 1 13 return File::Spec->splitdir( $_[ 0 ]->name );
1150             }
1151              
1152             sub splitpath {
1153 1     1 1 13 return File::Spec->splitpath( $_[ 0 ]->name );
1154             }
1155              
1156             sub stat {
1157 347 100 66 347 1 1098 my $self = shift; $self->exists or $self->is_open or return;
  347         502  
1158              
1159 345         10172 my %stat_hash = ( id => $self->filename );
1160              
1161 345 50       526 @stat_hash{ STAT_FIELDS() }
1162             = stat( $self->exists ? $self->name : $self->io_handle );
1163              
1164 345         1198 return \%stat_hash;
1165             }
1166              
1167             sub substitute {
1168 4   100 4 1 10 my ($self, $search, $replace) = @_; $replace //= NUL;
  4         10  
1169              
1170 4 100 100     24 (defined $search and CORE::length $search) or return $self;
1171              
1172 2         4 my $perms = $self->$_untainted_perms;
1173 2         29 my $wtr = $self->$_constructor( $self->name )->atomic;
1174              
1175 2 50       7 $perms and $wtr->perms( $perms );
1176              
1177 2         6 for ($self->getlines) { s{ $search }{$replace}gmx; $wtr->print( $_ ) }
  6         35  
  6         11  
1178              
1179 2         7 $self->close; $wtr->close;
  2         5  
1180 2         35 return $self;
1181             }
1182              
1183             sub tail {
1184 3   100 3 1 8 my ($self, $lines, @args) = @_; my @res; $lines //= 10; $self->close;
  3         4  
  3         11  
  3         6  
1185              
1186 3         8 while ($lines--) {
1187 14   50     297 unshift @res, ($self->$_getline_backwards( @args ) or last);
1188             }
1189              
1190 2         17 $self->close;
1191 2 50       17 return wantarray ? @res : join NUL, @res;
1192             }
1193              
1194             sub tell {
1195 3     3 1 7 my $self = shift;
1196              
1197 3 0       9 $self->is_open or $self->assert_open( is_mswin ? 'r' : 'r+' );
    50          
1198              
1199 3         14 return CORE::tell $self->io_handle;
1200             }
1201              
1202             sub tempfile {
1203 2     2 1 22 my ($self, $tmplt) = @_; my $tempdir;
  2         3  
1204              
1205 2   50     7 ensure_class_loaded 'File::Temp'; $tmplt ||= '%6.6dXXXX';
  2         116  
1206              
1207 2 100 66     45 ($tempdir = $self->name and -d $tempdir) or $tempdir = File::Spec->tmpdir;
1208              
1209 2         75 my $tmpfh = File::Temp->new
1210             ( DIR => $tempdir, TEMPLATE => (sprintf $tmplt, $PID) );
1211 2         904 my $t = $self->$_constructor( $tmpfh->filename )->file;
1212              
1213 2         26 $t->_set_io_handle( $tmpfh ); $t->_set_is_open( TRUE );
  2         45  
1214 2         42 $t->_set_mode( 'w+' );
1215 2         24 return $t;
1216             }
1217              
1218             sub touch {
1219 7 50 66 7 1 59 my ($self, $time) = @_; CORE::length $self->name or return; $time //= time;
  7         96  
  7         61  
1220              
1221 7 50       86 -e $self->name or $self->$_open_file( $self->$_open_args( 'w' ) )->close;
1222              
1223 7         94 utime $time, $time, $self->name;
1224 7         124 return $self;
1225             }
1226              
1227             sub unlink {
1228 27     27 1 1820 return unlink $_[ 0 ]->name;
1229             }
1230              
1231             sub unlock {
1232 286 100   286 1 265 my $self = shift; $self->_lock or return; my $handle = $self->io_handle;
  286         3931  
  69         390  
1233              
1234 69 100 66     302 $handle and $handle->opened and flock $handle, LOCK_UN;
1235 69         2646 $self->_set_have_lock( FALSE );
1236 69         1028 return $self;
1237             }
1238              
1239             sub utf8 {
1240 1     1 1 3 $_[ 0 ]->encoding( 'UTF-8' ); return $_[ 0 ];
  1         4  
1241             }
1242              
1243             sub visit {
1244 1     1 1 11 my ($self, $cb, $args) = @_;
1245              
1246 1         4 my $iter = $self->iterator( $args ); my $state = {};
  1         2  
1247              
1248 1         3 while (defined (my $entry = $iter->())) {
1249 9         10 local $_ = $entry; my $r = $cb->( $entry, $state );
  9         16  
1250              
1251 9 100 100     51 ref $r and not ${ $r } and last;
  8         78  
1252             }
1253              
1254 1         21 return $state;
1255             }
1256              
1257             sub write {
1258 15     15 1 42 my ($self, @args) = @_; $self->assert_open( 'w' );
  15         21  
1259              
1260             my $length = @args
1261             ? $self->io_handle->write( @args )
1262 15 50       32 : $self->io_handle->write( ${ $self->buffer }, $self->length );
  15         17  
1263              
1264 15 50       347 $self->error_check; scalar @args or $self->clear;
  15         33  
1265 15         22 return $length;
1266             }
1267              
1268             # Method installer
1269             my $_proxy = sub { # Methods handled by the IO::Handle object
1270             my ($proxy, $chain, $mode) = @_;
1271              
1272             my $package = caller; $package->can( $proxy ) and return;
1273              
1274             install_sub { into => $package, as => $proxy, code => sub {
1275 9 100   9   2097 my $self = shift; defined $mode and $self->assert_open( $mode );
  9         28  
1276              
1277 9 100       29 defined $self->io_handle or throw InvocantUndefined, [ $proxy ];
1278              
1279 8         66 my @results = $self->io_handle->$proxy( @_ ); # Mustn't copy stack args
1280              
1281 8 100       137 $self->error_check; $chain and return $self;
  8         20  
1282              
1283 7 50       32 return wantarray ? @results : $results[ 0 ];
1284             } };
1285             };
1286              
1287             $_proxy->( 'autoflush', TRUE );
1288             $_proxy->( 'eof' );
1289             $_proxy->( 'fileno' );
1290             $_proxy->( 'flush', TRUE );
1291             $_proxy->( 'getc', FALSE, 'r' );
1292             $_proxy->( 'sysread', FALSE, O_RDONLY );
1293             $_proxy->( 'syswrite', FALSE, O_CREAT | O_WRONLY );
1294             $_proxy->( 'truncate', TRUE );
1295              
1296             1;
1297              
1298             __END__