| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 56 |  |  | 56 |  | 246 | use strict; use warnings; | 
|  | 56 |  |  | 56 |  | 75 |  | 
|  | 56 |  |  |  |  | 1809 |  | 
|  | 56 |  |  |  |  | 246 |  | 
|  | 56 |  |  |  |  | 73 |  | 
|  | 56 |  |  |  |  | 2828 |  | 
| 2 |  |  |  |  |  |  | package IO::All::File; | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 56 |  |  | 56 |  | 20649 | use IO::All::Filesys -base; | 
|  | 56 |  |  |  |  | 113 |  | 
|  | 56 |  |  |  |  | 701 |  | 
| 5 | 56 |  |  | 56 |  | 303 | use IO::All -base; | 
|  | 56 |  |  |  |  | 83 |  | 
|  | 56 |  |  |  |  | 388 |  | 
| 6 | 56 |  |  | 56 |  | 29734 | use IO::File; | 
|  | 56 |  |  |  |  | 104139 |  | 
|  | 56 |  |  |  |  | 7371 |  | 
| 7 | 56 |  |  | 56 |  | 30301 | use File::Copy (); | 
|  | 56 |  |  |  |  | 122596 |  | 
|  | 56 |  |  |  |  | 79847 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | #=============================================================================== | 
| 10 |  |  |  |  |  |  | const type => 'file'; | 
| 11 |  |  |  |  |  |  | field tied_file => undef; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | #=============================================================================== | 
| 14 |  |  |  |  |  |  | sub file { | 
| 15 | 333 |  |  | 333 | 1 | 394 | my $self = shift; | 
| 16 | 333 |  |  |  |  | 642 | bless $self, __PACKAGE__; | 
| 17 |  |  |  |  |  |  | # should we die here if $self->name is already set and there are args? | 
| 18 | 333 | 100 | 100 |  |  | 1904 | if (@_ && @_ > 1) { | 
|  |  | 100 |  |  |  |  |  | 
| 19 | 9 |  |  |  |  | 69 | $self->name( $self->_spec_class->catfile( @_ ) ) | 
| 20 |  |  |  |  |  |  | } elsif (@_) { | 
| 21 | 304 |  |  |  |  | 954 | $self->name($_[0]) | 
| 22 |  |  |  |  |  |  | } | 
| 23 | 333 |  |  |  |  | 949 | return $self->_init; | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | sub file_handle { | 
| 27 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 28 | 0 |  |  |  |  | 0 | bless $self, __PACKAGE__; | 
| 29 | 0 | 0 |  |  |  | 0 | $self->_handle(shift) if @_; | 
| 30 | 0 |  |  |  |  | 0 | return $self->_init; | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | #=============================================================================== | 
| 34 |  |  |  |  |  |  | sub assert_filepath { | 
| 35 | 4 |  |  | 4 | 0 | 5 | my $self = shift; | 
| 36 | 4 | 50 |  |  |  | 11 | my $name = $self->pathname | 
| 37 |  |  |  |  |  |  | or return; | 
| 38 | 4 |  |  |  |  | 6 | my $directory; | 
| 39 | 4 |  |  |  |  | 8 | (undef, $directory) = File::Spec->splitpath($self->pathname); | 
| 40 | 4 |  |  |  |  | 25 | $self->_assert_dirpath($directory); | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | sub assert_open_backwards { | 
| 44 | 8 |  |  | 8 | 0 | 3 | my $self = shift; | 
| 45 | 8 | 100 |  |  |  | 16 | return if $self->is_open; | 
| 46 | 2 |  |  |  |  | 12 | require File::ReadBackwards; | 
| 47 | 2 |  |  |  |  | 6 | my $file_name = $self->pathname; | 
| 48 | 2 | 50 |  |  |  | 11 | my $io_handle = File::ReadBackwards->new($file_name) | 
| 49 |  |  |  |  |  |  | or $self->throw("Can't open $file_name for backwards:\n$!"); | 
| 50 | 2 |  |  |  |  | 121 | $self->io_handle($io_handle); | 
| 51 | 2 |  |  |  |  | 3 | $self->is_open(1); | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub _assert_open { | 
| 55 | 218 |  |  | 218 |  | 214 | my $self = shift; | 
| 56 | 218 | 100 |  |  |  | 433 | return if $self->is_open; | 
| 57 | 101 | 50 |  |  |  | 384 | $self->mode(shift) unless $self->mode; | 
| 58 | 101 |  |  |  |  | 203 | $self->open; | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub assert_tied_file { | 
| 62 | 6 |  |  | 6 | 0 | 9 | my $self = shift; | 
| 63 | 6 |  | 66 |  |  | 18 | return $self->tied_file || do { | 
| 64 |  |  |  |  |  |  | eval {require Tie::File}; | 
| 65 |  |  |  |  |  |  | $self->throw("Tie::File required for file array operations:\n$@") | 
| 66 |  |  |  |  |  |  | if $@; | 
| 67 |  |  |  |  |  |  | my $array_ref = do { my @array; \@array }; | 
| 68 |  |  |  |  |  |  | my $name = $self->pathname; | 
| 69 |  |  |  |  |  |  | my @options = $self->_rdonly ? (mode => O_RDONLY) : (); | 
| 70 |  |  |  |  |  |  | push @options, (recsep => $self->separator); | 
| 71 |  |  |  |  |  |  | tie @$array_ref, 'Tie::File', $name, @options; | 
| 72 |  |  |  |  |  |  | $self->throw("Can't tie 'Tie::File' to '$name':\n$!") | 
| 73 |  |  |  |  |  |  | unless tied @$array_ref; | 
| 74 |  |  |  |  |  |  | $self->tied_file($array_ref); | 
| 75 |  |  |  |  |  |  | }; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub open { | 
| 79 | 116 |  |  | 116 | 1 | 131 | my $self = shift; | 
| 80 | 116 |  |  |  |  | 242 | $self->is_open(1); | 
| 81 | 116 | 100 |  |  |  | 388 | $self->assert_filepath if $self->_assert; | 
| 82 | 116 |  |  |  |  | 162 | my ($mode, $perms) = @_; | 
| 83 | 116 | 100 |  |  |  | 330 | $self->mode($mode) if defined $mode; | 
| 84 | 116 | 100 |  |  |  | 288 | $self->mode('<') unless defined $self->mode; | 
| 85 | 116 | 50 |  |  |  | 294 | $self->perms($perms) if defined $perms; | 
| 86 | 116 |  |  |  |  | 321 | my @args = ($self->mode); | 
| 87 | 116 | 50 |  |  |  | 420 | push @args, $self->perms if defined $self->perms; | 
| 88 | 116 | 50 | 0 |  |  | 349 | if (defined $self->pathname) { | 
|  |  | 0 |  |  |  |  |  | 
| 89 | 116 |  |  |  |  | 549 | $self->io_handle(IO::File->new); | 
| 90 | 116 | 100 |  |  |  | 244 | $self->io_handle->open($self->pathname, @args) | 
| 91 |  |  |  |  |  |  | or $self->throw($self->open_msg); | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | elsif (defined $self->_handle and | 
| 94 |  |  |  |  |  |  | not $self->io_handle->opened | 
| 95 |  |  |  |  |  |  | ) { | 
| 96 |  |  |  |  |  |  | # XXX Not tested | 
| 97 | 0 |  |  |  |  | 0 | $self->io_handle->fdopen($self->_handle, @args); | 
| 98 |  |  |  |  |  |  | } | 
| 99 | 115 |  |  |  |  | 7911 | $self->set_lock; | 
| 100 | 115 |  |  |  |  | 476 | $self->_set_binmode; | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 0 |  |  | 0 | 1 | 0 | sub exists { -f shift->pathname } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | my %mode_msg = ( | 
| 106 |  |  |  |  |  |  | '>' => 'output', | 
| 107 |  |  |  |  |  |  | '<' => 'input', | 
| 108 |  |  |  |  |  |  | '>>' => 'append', | 
| 109 |  |  |  |  |  |  | ); | 
| 110 |  |  |  |  |  |  | sub open_msg { | 
| 111 | 1 |  |  | 1 | 0 | 43 | my $self = shift; | 
| 112 | 1 | 50 |  |  |  | 2 | my $name = defined $self->pathname | 
| 113 |  |  |  |  |  |  | ? " '" . $self->pathname . "'" | 
| 114 |  |  |  |  |  |  | : ''; | 
| 115 | 1 | 50 |  |  |  | 3 | my $direction = defined $mode_msg{$self->mode} | 
| 116 |  |  |  |  |  |  | ? ' for ' . $mode_msg{$self->mode} | 
| 117 |  |  |  |  |  |  | : ''; | 
| 118 | 1 |  |  |  |  | 17 | return qq{Can't open file$name$direction:\n$!}; | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | #=============================================================================== | 
| 122 |  |  |  |  |  |  | sub copy { | 
| 123 | 1 |  |  | 1 | 1 | 6 | my ($self, $new) = @_; | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 1 | 50 |  |  |  | 2 | File::Copy::copy($self->name, $new) | 
| 126 |  |  |  |  |  |  | or die "failed to copy $self to $new: $!"; | 
| 127 | 1 |  |  |  |  | 433 | $self->file($new) | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub close { | 
| 131 | 126 |  |  | 126 | 1 | 11722 | my $self = shift; | 
| 132 | 126 | 100 |  |  |  | 276 | return unless $self->is_open; | 
| 133 | 120 |  |  |  |  | 269 | $self->is_open(0); | 
| 134 | 120 |  |  |  |  | 237 | my $io_handle = $self->io_handle; | 
| 135 | 120 |  |  |  |  | 436 | $self->unlock; | 
| 136 | 120 |  |  |  |  | 242 | $self->io_handle(undef); | 
| 137 | 120 |  |  |  |  | 282 | $self->mode(undef); | 
| 138 | 120 | 100 |  |  |  | 349 | if (my $tied_file = $self->tied_file) { | 
| 139 | 3 | 100 |  |  |  | 16 | if (ref($tied_file) eq 'ARRAY') { | 
| 140 | 1 |  |  |  |  | 12 | untie @$tied_file; | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  | else { | 
| 143 | 2 |  |  |  |  | 24 | untie %$tied_file; | 
| 144 |  |  |  |  |  |  | } | 
| 145 | 3 |  |  |  |  | 75 | $self->tied_file(undef); | 
| 146 | 3 |  |  |  |  | 31 | return 1; | 
| 147 |  |  |  |  |  |  | } | 
| 148 | 117 | 50 |  |  |  | 692 | $io_handle->close(@_) | 
| 149 |  |  |  |  |  |  | if defined $io_handle; | 
| 150 | 117 |  |  |  |  | 3381 | return $self; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub empty { | 
| 154 | 1 |  |  | 1 | 1 | 3 | my $self = shift; | 
| 155 | 1 |  |  |  |  | 3 | -z $self->pathname; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub filepath { | 
| 159 | 1 |  |  | 1 | 1 | 2 | my $self = shift; | 
| 160 | 1 |  |  |  |  | 12 | my ($volume, $path) = $self->splitpath; | 
| 161 | 1 |  |  |  |  | 49 | return File::Spec->catpath($volume, $path, ''); | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | sub getline_backwards { | 
| 165 | 8 |  |  | 8 | 0 | 7 | my $self = shift; | 
| 166 | 8 |  |  |  |  | 11 | $self->assert_open_backwards; | 
| 167 | 8 |  |  |  |  | 25 | return $self->io_handle->readline; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | sub getlines_backwards { | 
| 171 | 1 |  |  | 1 | 0 | 4 | my $self = shift; | 
| 172 | 1 |  |  |  |  | 1 | my @lines; | 
| 173 | 1 |  |  |  |  | 2 | while (defined (my $line = $self->getline_backwards)) { | 
| 174 | 3 |  |  |  |  | 87 | push @lines, $line; | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 1 |  |  |  |  | 12 | return @lines; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub head { | 
| 180 | 2 |  |  | 2 | 1 | 3 | my $self = shift; | 
| 181 | 2 |  | 100 |  |  | 9 | my $lines = shift || 10; | 
| 182 | 2 |  |  |  |  | 2 | my @return; | 
| 183 | 2 |  |  |  |  | 5 | $self->close; | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | LINES: | 
| 186 | 2 |  |  |  |  | 7 | while ($lines--) { | 
| 187 | 15 | 50 |  |  |  | 44 | if (defined (my $l = $self->getline)) { | 
| 188 | 15 |  |  |  |  | 47 | push @return, $l; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | else { | 
| 191 | 0 |  |  |  |  | 0 | last LINES; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 2 |  |  |  |  | 6 | $self->close; | 
| 196 | 2 | 50 |  |  |  | 31 | return wantarray ? @return : join '', @return; | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | sub tail { | 
| 200 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 201 | 0 |  | 0 |  |  | 0 | my $lines = shift || 10; | 
| 202 | 0 |  |  |  |  | 0 | my @return; | 
| 203 | 0 |  |  |  |  | 0 | $self->close; | 
| 204 | 0 |  |  |  |  | 0 | while ($lines--) { | 
| 205 | 0 |  | 0 |  |  | 0 | unshift @return, ($self->getline_backwards or last); | 
| 206 |  |  |  |  |  |  | } | 
| 207 | 0 |  |  |  |  | 0 | $self->close; | 
| 208 | 0 | 0 |  |  |  | 0 | return wantarray ? @return : join '', @return; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub touch { | 
| 212 | 2 |  |  | 2 | 1 | 5 | my $self = shift; | 
| 213 | 2 | 100 |  |  |  | 13 | return $self->SUPER::touch(@_) | 
| 214 |  |  |  |  |  |  | if -e $self->pathname; | 
| 215 | 1 | 50 |  |  |  | 9 | return $self if $self->is_open; | 
| 216 | 1 |  |  |  |  | 10 | my $mode = $self->mode; | 
| 217 | 1 |  |  |  |  | 4 | $self->mode('>>')->open->close; | 
| 218 | 1 |  |  |  |  | 4 | $self->mode($mode); | 
| 219 | 1 |  |  |  |  | 5 | return $self; | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | sub unlink { | 
| 223 | 3 |  |  | 3 | 1 | 3326 | my $self = shift; | 
| 224 | 3 |  |  |  |  | 67 | unlink $self->pathname; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | #=============================================================================== | 
| 228 |  |  |  |  |  |  | sub _overload_table { | 
| 229 | 63 |  |  | 63 |  | 64 | my $self = shift; | 
| 230 |  |  |  |  |  |  | ( | 
| 231 | 63 |  |  |  |  | 254 | $self->SUPER::_overload_table(@_), | 
| 232 |  |  |  |  |  |  | 'file > file' => '_overload_file_to_file', | 
| 233 |  |  |  |  |  |  | 'file < file' => '_overload_file_from_file', | 
| 234 |  |  |  |  |  |  | '${} file' => '_overload_file_as_scalar', | 
| 235 |  |  |  |  |  |  | '@{} file' => '_overload_file_as_array', | 
| 236 |  |  |  |  |  |  | '%{} file' => '_overload_file_as_dbm', | 
| 237 |  |  |  |  |  |  | ) | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | sub _overload_file_to_file { | 
| 241 | 2 |  |  | 2 |  | 13 | require File::Copy; | 
| 242 | 2 |  |  |  |  | 6 | File::Copy::copy($_[1]->pathname, $_[2]->pathname); | 
| 243 | 2 |  |  |  |  | 501 | $_[2]; | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | sub _overload_file_from_file { | 
| 247 | 2 |  |  | 2 |  | 10 | require File::Copy; | 
| 248 | 2 |  |  |  |  | 6 | File::Copy::copy($_[2]->pathname, $_[1]->pathname); | 
| 249 | 2 |  |  |  |  | 453 | $_[1]; | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | sub _overload_file_as_array { | 
| 253 | 6 |  |  | 6 |  | 21 | $_[1]->assert_tied_file; | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | sub _overload_file_as_dbm { | 
| 257 | 5 | 50 |  | 5 |  | 24 | $_[1]->dbm | 
| 258 |  |  |  |  |  |  | unless $_[1]->isa('IO::All::DBM'); | 
| 259 | 5 |  |  |  |  | 13 | $_[1]->_assert_open; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | sub _overload_file_as_scalar { | 
| 263 | 10 |  |  | 10 |  | 69 | my $scalar = $_[1]->scalar; | 
| 264 | 10 |  |  |  |  | 63 | return \$scalar; | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | 1; |