File Coverage

blib/lib/File/KDBX/IO.pm
Criterion Covered Total %
statement 123 207 59.4
branch 31 58 53.4
condition 19 53 35.8
subroutine 30 63 47.6
pod 15 38 39.4
total 218 419 52.0


line stmt bran cond sub pod time code
1             package File::KDBX::IO;
2             # ABSTRACT: Base IO class for KDBX-related streams
3              
4 10     2707   4334 use warnings;
  10         19  
  10         283  
5 10     1341   45 use strict;
  10         23  
  10         168  
6              
7 10     10   1330 use Devel::GlobalDestruction;
  10         1771  
  10         53  
8 10     10   2290 use File::KDBX::Constants qw(:bool);
  10         16  
  10         845  
9 10     10   53 use File::KDBX::Util qw(:class :empty);
  10         20  
  10         908  
10 10     10   57 use List::Util qw(sum0);
  10         14  
  10         490  
11 10     10   51 use Ref::Util qw(is_blessed_ref is_ref is_scalarref);
  10         15  
  10         425  
12 10     10   1110 use Symbol qw(gensym);
  10         2298  
  10         446  
13 10     10   64 use namespace::clean;
  10         17  
  10         59  
14              
15             extends 'IO::Handle';
16              
17             our $VERSION = '0.905'; # VERSION
18              
19 0     0   0 sub _croak { require Carp; goto &Carp::croak }
  0         0  
20              
21             my %ATTRS = (
22             _append_output => 0,
23             _buffer_in => sub { [] },
24             _buffer_out => sub { [] },
25             _error => undef,
26             _fh => undef,
27             _mode => '',
28             );
29             while (my ($attr, $default) = each %ATTRS) {
30 10     10   3701 no strict 'refs'; ## no critic (ProhibitNoStrict)
  10         15  
  10         19323  
31             *$attr = sub {
32 2595     2595   2867 my $self = shift;
33 2595 100       3936 *$self->{$attr} = shift if @_;
34 2595 100 100     10509 *$self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
35             };
36             }
37              
38             sub new {
39 94   33 94 1 252 my $class = shift || (caller)[0];
40 94   33     336 my $self = bless gensym, ref($class) || $class;
41 94 50       2595 tie *$self, $self if 5.005 <= $];
42 94         241 return $self;
43             }
44              
45             sub DESTROY {
46 94 50   94   21327 return if in_global_destruction;
47 94         1291 local ($., $@, $!, $^E, $?);
48 94         151 my $self = shift;
49 94         228 $self->close;
50             }
51              
52             sub close {
53 147     147 0 302 my $self = shift;
54 147   100     287 my $fh = $self->_fh // return TRUE;
55 94         347 $self->_POPPED($fh);
56 94         219 $self->_fh(undef);
57 94         348 return $fh->close;
58             }
59             sub eof {
60 0     0 0 0 my $self = shift;
61 0 0       0 return FALSE if @{$self->_buffer_in};
  0         0  
62 0   0     0 my $fh = $self->_fh // return TRUE;
63 0         0 local *$self->{_error} = *$self->{_error};
64 0   0     0 my $char = $self->getc || return TRUE;
65 0         0 $self->ungetc($char);
66             }
67 322     322 0 25990 sub read { shift->sysread(@_) }
68             sub print {
69 86     86 0 174 my $self = shift;
70 86         156 for my $buf (@_) {
71 86 50       256 return FALSE if !$self->write($buf, length($buf));
72             }
73 86         290 return TRUE;
74             }
75 0     0 0 0 sub printf { shift->print(sprintf(@_)) }
76 0     0 0 0 sub say { shift->print(@_, "\n") }
77 0 0 0 0 0 0 sub getc { my $c; (shift->read($c, 1) // 0) == 1 ? $c : undef }
  0         0  
78             sub sysread {
79 322     322 0 407 my $self = shift;
80 322         99134 my ($out, $len, $offset) = @_;
81 322 50       8313 $out = \$_[0] if !is_scalarref($out);
82 322   50     1118 $offset //= 0;
83              
84 322 100       610 $self->_mode('r') if !$self->_mode;
85              
86 322 50       601 my $fh = $self->_fh or return 0;
87 322 50 66     820 return 0 if defined $len && $len == 0;
88              
89 322         609 my $append = $self->_append_output;
90 322 100       617 if (!$append) {
    100          
91 256 50       351 if (!$offset) {
92 256         343 $$out = '';
93             }
94             else {
95 0 0       0 if (length($$out) < $offset) {
96 0         0 $$out .= "\0" x ($offset - length($$out));
97             }
98             else {
99 0         0 substr($$out, $offset) = '';
100             }
101             }
102             }
103             elsif (!defined $$out) {
104 11         19 $$out = '';
105             }
106              
107 322   100     611 $len ||= 0;
108              
109 322         563 my $buffer = $self->_buffer_in;
110 322         596 my $buffer_len = $self->_buffer_in_length;
111              
112 322 50 66     654 if (!$len && !$offset) {
113 66 50       115 if (@$buffer) {
114 0         0 my $blen = length($buffer->[0]);
115 0 0       0 if ($append) {
116 0         0 $$out .= shift @$buffer;
117             }
118             else {
119 0         0 $$out = shift @$buffer;
120             }
121 0         0 return $blen;
122             }
123             else {
124 66 100       202 my $fill = $self->_FILL($fh) or return 0;
125 55 50       120 if ($append) {
126 55         21133 $$out .= $fill;
127             }
128             else {
129 0         0 $$out = $fill;
130             }
131 55         333 return length($fill);
132             }
133             }
134              
135 256         442 while ($buffer_len < $len) {
136 160         389 my $fill = $self->_FILL($fh);
137 160 100       364 last if empty $fill;
138 52         202 $self->_buffer_in_add($fill);
139 52         129 $buffer_len += length($fill);
140             }
141              
142 256         349 my $read_len = 0;
143 256   100     726 while ($read_len < $len && @$buffer) {
144 183         223 my $wanted = $len - $read_len;
145 183         262 my $read = shift @$buffer;
146 183 100       288 if ($wanted < length($read)) {
147 134         333 $$out .= substr($read, 0, $wanted, '');
148 134         312 unshift @$buffer, $read;
149 134         291 $read_len += $wanted;
150             }
151             else {
152 49         92 $$out .= $read;
153 49         130 $read_len += length($read);
154             }
155             }
156              
157 256         674 return $read_len;
158             }
159             sub syswrite {
160 116     116 0 199 my ($self, $buf, $len, $offset) = @_;
161 116   33     211 $len //= length($buf);
162 116   50     387 $offset //= 0;
163              
164 116 100       224 $self->_mode('w') if !$self->_mode;
165              
166 116         325 return $self->_WRITE(substr($buf, $offset, $len), $self->_fh);
167             }
168              
169             sub autoflush {
170 0     0 0 0 my $self = shift;
171 0   0     0 my $fh = $self->_fh // return FALSE;
172 0         0 return $fh->autoflush(@_);
173             }
174              
175             sub opened {
176 0     0 1 0 my $self = shift;
177 0   0     0 my $fh = $self->_fh // return FALSE;
178 0         0 return TRUE;
179             }
180             sub getline {
181 11     11 1 25 my $self = shift;
182              
183 11 50 0     32 if (!defined $/) { # SLURP
    0 0        
    0          
184 11         43 local *$self->{_append_output} = 1;
185 11         17 my $data;
186 11         125 1 while 0 < $self->read($data);
187 11         19974 return $data;
188             }
189 0         0 elsif (is_scalarref($/) && ${$/} =~ /^\d+$/ && 0 < ${$/}) {
  0         0  
190             # RECORD MODE
191 0         0 goto &_not_implemented;
192             }
193             elsif (length $/ == 0) {
194             # PARAGRAPH MODE
195 0         0 goto &_not_implemented;
196             }
197             else {
198             # LINE MODE
199 0         0 goto &_not_implemented;
200             }
201             }
202             sub getlines {
203 0     0 1 0 my $self = shift;
204 0 0       0 wantarray or _croak 'Must call getlines in list context';
205 0         0 my @lines;
206 0         0 while (defined (my $line = $self->getline)) {
207 0         0 push @lines, $line;
208             }
209 0         0 return @lines;
210             }
211             sub ungetc {
212 0     0 1 0 my ($self, $ord) = @_;
213 0         0 unshift @{$self->_buffer_in}, chr($ord);
  0         0  
214 0         0 return;
215             }
216             sub write {
217 116     116 1 3894 my ($self, $buf, $len, $offset) = @_;
218 116         289 return $self->syswrite($buf, $len, $offset) == $len;
219             }
220             sub error {
221 6     6 1 812 my $self = shift;
222 6         62 return !!$self->_error;
223             }
224             sub clearerr {
225 0     0 1 0 my $self = shift;
226 0   0     0 my $fh = $self->_fh // return -1;
227 0         0 $self->_error(undef);
228 0         0 return;
229             }
230             sub sync {
231 0     0 1 0 my $self = shift;
232 0   0     0 my $fh = $self->_fh // return undef;
233 0         0 return $fh->sync;
234             }
235             sub flush {
236 0     0 1 0 my $self = shift;
237 0   0     0 my $fh = $self->_fh // return undef;
238 0         0 $self->_FLUSH($fh);
239 0         0 return $fh->flush;
240             }
241             sub printflush {
242 0     0 1 0 my $self = shift;
243 0         0 my $orig = $self->autoflush;
244 0         0 my $r = $self->print(@_);
245 0         0 $self->autoflush($orig);
246 0         0 return $r;
247             }
248             sub blocking {
249 0     0 1 0 my $self = shift;
250 0   0     0 my $fh = $self->_fh // return TRUE;
251 0         0 return $fh->blocking(@_);
252             }
253              
254 0     0 0 0 sub format_write { goto &_not_implemented }
255 0     0 1 0 sub new_from_fd { goto &_not_implemented }
256 0     0 0 0 sub fcntl { goto &_not_implemented }
257 0     0 0 0 sub fileno { goto &_not_implemented }
258 0     0 0 0 sub ioctl { goto &_not_implemented }
259 0     0 0 0 sub stat { goto &_not_implemented }
260 0     0 0 0 sub truncate { goto &_not_implemented }
261 0     0 0 0 sub format_page_number { goto &_not_implemented }
262 0     0 0 0 sub format_lines_per_page { goto &_not_implemented }
263 0     0 0 0 sub format_lines_left { goto &_not_implemented }
264 0     0 0 0 sub format_name { goto &_not_implemented }
265 0     0 0 0 sub format_top_name { goto &_not_implemented }
266 0     0 0 0 sub input_line_number { goto &_not_implemented }
267 0     0 1 0 sub fdopen { goto &_not_implemented }
268 0     0 1 0 sub untaint { goto &_not_implemented }
269              
270             ##############################################################################
271              
272 52     52   65 sub _buffer_in_add { push @{shift->_buffer_in}, @_ }
  52         92  
273 322     322   392 sub _buffer_in_length { sum0 map { length($_) } @{shift->_buffer_in} }
  131         348  
  322         432  
274              
275 78     78   109 sub _buffer_out_add { push @{shift->_buffer_out}, @_ }
  78         184  
276 0     0   0 sub _buffer_out_length { sum0 map { length($_) } @{shift->_buffer_out} }
  0         0  
  0         0  
277              
278 0     0   0 sub _not_implemented { _croak 'Operation not supported' }
279              
280             ##############################################################################
281              
282             sub TIEHANDLE {
283 94 50   94   503 return $_[0] if is_blessed_ref($_[0]);
284 0         0 die 'wat';
285             }
286              
287             sub UNTIE {
288 0     0   0 my $self = shift;
289             }
290              
291             sub READLINE {
292 11 50   11   164 goto &getlines if wantarray;
293 11         81 goto &getline;
294             }
295              
296 35     35 0 22146 sub binmode { 1 }
297              
298             {
299 10     10   72 no warnings 'once';
  10         16  
  10         1966  
300              
301             *READ = \&read;
302             # *READLINE = \&getline;
303             *GETC = \&getc;
304             *FILENO = \&fileno;
305             *PRINT = \&print;
306             *PRINTF = \&printf;
307             *WRITE = \&syswrite;
308             # *SEEK = \&seek;
309             # *TELL = \&tell;
310             *EOF = \&eof;
311             *CLOSE = \&close;
312             *BINMODE = \&binmode;
313             }
314              
315 0     0     sub _FILL { die 'Not implemented' }
316              
317             ##############################################################################
318              
319             if ($ENV{DEBUG_IO}) {
320             my %debug = (level => 0);
321             for my $method (qw{
322             new
323             new_from_fd
324             close
325             eof
326             fcntl
327             fileno
328             format_write
329             getc
330             ioctl
331             read
332             print
333             printf
334             say
335             stat
336             sysread
337             syswrite
338             truncate
339              
340             autoflush
341             format_page_number
342             format_lines_per_page
343             format_lines_left
344             format_name
345             format_top_name
346             input_line_number
347              
348             fdopen
349             opened
350             getline
351             getlines
352             ungetc
353             write
354             error
355             clearerr
356             sync
357             flush
358             printflush
359             blocking
360              
361             untaint
362             }) {
363 10     10   66 no strict 'refs'; ## no critic (ProhibitNoStrict)
  10         18  
  10         283  
364 10     10   49 no warnings 'redefine';
  10         30  
  10         1811  
365             my $orig = *$method{CODE};
366             *$method = sub {
367             local $debug{level} = $debug{level} + 2;
368             my $indented_method = (' ' x $debug{level}) . $method;
369             my $self = shift;
370             print STDERR sprintf('%-20s -> %s (%s)', $indented_method, $self,
371             join(', ', map { defined $_ ? substr($_, 0, 16) : 'undef' } @_)), "\n";
372             my $r = $orig->($self, @_) // 'undef';
373             print STDERR sprintf('%-20s <- %s [%s]', $indented_method, $self, $r), "\n";
374             return $r;
375             };
376             }
377             }
378              
379             1;
380              
381             __END__