File Coverage

blib/lib/IO/Die.pm
Criterion Covered Total %
statement 93 368 25.2
branch 44 202 21.7
condition 4 24 16.6
subroutine 18 55 32.7
pod 10 48 20.8
total 169 697 24.2


line stmt bran cond sub pod time code
1             package IO::Die;
2              
3 2     2   149197 use 5.006;
  2         7  
  2         83  
4 2     2   9 use strict;
  2         2  
  2         7828  
5              
6             #not in production
7             #use warnings;
8              
9             =head1 NAME
10              
11             IO::Die - Namespaced, error-checked I/O
12              
13             =head1 VERSION
14              
15             Version 0.055
16              
17             =cut
18              
19             our $VERSION = '0.055';
20              
21             #----------------------------------------------------------------------
22             #PROTECTED
23              
24             #Override in subclasses as needed
25             sub _CREATE_ERROR {
26 11     11   15 shift;
27 11 50       29 return shift() . ": " . join( ' ', map { defined() ? $_ : q<> } @_ );
  70         180  
28             }
29              
30 12     12   151 sub _DO_WITH_ERROR { die $_[1] }
31              
32             #----------------------------------------------------------------------
33             #PRIVATES
34              
35             sub __THROW {
36 12     12   37 my ( $NS, $type, @args ) = @_;
37              
38 12         45 $NS->_DO_WITH_ERROR(
39             $NS->_CREATE_ERROR(
40             $type,
41             @args,
42             OS_ERROR => $!,
43             EXTENDED_OS_ERROR => $^E,
44             )
45             );
46             }
47              
48             sub __is_a_fh {
49 5     5   10 my ($thing) = @_;
50              
51 5         5 my $is_fh;
52              
53             #Every file handle is a GLOB reference. This would be sufficient, except
54             #GLOBs can also be symbol table references.
55 5 100       29 if ( UNIVERSAL::isa( $thing, 'GLOB' ) ) {
56              
57             # You can’t tie() a symbol table reference, so if we’re tied(),
58             # then this is a filehandle.
59             #
60             # If we’re not tied(), then we have to check fileno().
61 2   33     2 $is_fh = ( tied *{$thing} ) || defined( CORE::fileno($thing) );
62             }
63              
64 5         15 return $is_fh;
65             }
66              
67             #----------------------------------------------------------------------
68              
69             #NOTE: This function does not attempt to support every possible way of calling
70             #Perl’s open() built-in, but to support the minimal syntax required to do
71             #everything that is useful to do with open(), with preference given to those
72             #forms that may (somewhat arbitrarily) be considered "better".
73             #
74             #For example, this function does NOT allow one-arg or two-arg open() except for
75             #the more "useful" cases like when MODE is '-|' or '|-'.
76             #
77             #On the other hand, open($fh, '-') seems harder to understand than its 3-arg
78             #equivalent, open($fh, '<&=', STDIN), so that two-arg form is unsupported.
79             #
80             #Current forms of open() that this supports are:
81             # - any form of 3 or more arguments
82             # - 2-arg when the MODE is '-|' or '|-'
83             #
84             #NOTE: Bareword file handles DO NOT WORK. (Auto-vivification does, though.)
85             #
86             sub open {
87 4     4 1 11370 my ( $NS, $mode, $expr, @list ) = ( shift, @_[ 1 .. $#_ ] );
88              
89             #https://github.com/pjcj/Devel--Cover/issues/125
90             #my ( $NS, $handle_r, $mode, $expr, @list ) = ( shift, \shift, @_ );
91              
92 4 50 33     35 die "Avoid bareword file handles." if !ref $_[0] && defined $_[0] && length $_[0];
      33        
93 4 50       13 die "Avoid one-argument open()." if !$mode;
94              
95 4         18 local ( $!, $^E );
96 4 50       11 if ( !defined $expr ) {
97 0 0 0     0 if ( $mode eq '|-' or $mode eq '-|' ) {
98              
99             #NOTE: Avoid // for compatibility with old Perl versions.
100 0         0 my $open = CORE::open( $_[0], $mode );
101 0 0       0 if ( !defined $open ) {
102 0         0 $NS->__THROW('Fork');
103             }
104              
105 0         0 return $open;
106             }
107              
108 0         0 my $file = __FILE__;
109 0         0 die "Avoid most forms of two-argument open(). (See $file and its tests for allowable forms.)";
110             }
111              
112 4 50       112 my $ok = CORE::open( $_[0], $mode, $expr, @list ) or do {
113 0 0 0     0 if ( $mode eq '|-' || $mode eq '-|' ) {
114 0         0 my $cmd = $expr;
115              
116             #If the EXPR (cf. perldoc -f open) has spaces and no LIST
117             #is given, then Perl interprets EXPR as a space-delimited
118             #shell command, the first component of which is the actual
119             #command.
120 0 0       0 if ( !@list ) {
121 0         0 ($cmd) = ( $cmd =~ m<\A(\S+)> );
122             }
123              
124 0         0 $NS->__THROW( 'Exec', path => $cmd, arguments => \@list );
125             }
126              
127 0 0       0 if ( 'SCALAR' eq ref $expr ) {
128 0         0 $NS->__THROW('ScalarOpen');
129             }
130              
131 0         0 $NS->__THROW( 'FileOpen', mode => $mode, path => $expr );
132             };
133              
134 4         19 return $ok;
135             }
136              
137             sub sysopen {
138 0     0 0 0 my ( $NS, @post_handle_args ) = ( shift, @_[ 1 .. $#_ ] );
139              
140             #https://github.com/pjcj/Devel--Cover/issues/125
141             #my ( $NS, $handle_r, @post_handle_args ) = ( shift, \shift, @_ );
142              
143 0         0 my ( $path, $mode, $perms ) = @post_handle_args;
144              
145 0         0 local ( $!, $^E );
146              
147 0         0 my $ret;
148 0 0       0 if ( @post_handle_args < 3 ) {
149 0         0 $ret = CORE::sysopen( $_[0], $path, $mode );
150             }
151             else {
152 0         0 $ret = CORE::sysopen( $_[0], $path, $mode, $perms );
153             }
154              
155             #XXX: Perl bug? $! is often set here even when $ret is truthy.
156              
157 0 0       0 if ( !$ret ) {
158 0         0 $NS->__THROW( 'FileOpen', path => $path, mode => $mode, mask => $perms );
159             }
160              
161 0         0 return $ret;
162             }
163              
164             sub chroot {
165 0     0 0 0 my ( $NS, $filename ) = @_;
166              
167 0         0 local ( $!, $^E );
168              
169 0 0       0 if ( !defined $filename ) {
170 0         0 $filename = $_;
171             }
172              
173 0 0       0 my $ok = CORE::chroot($filename) or do {
174 0         0 $NS->__THROW( 'Chroot', filename => $filename );
175             };
176              
177 0         0 return $ok;
178             }
179              
180             sub chdir {
181 8     8 0 7151 my ( $NS, @args ) = @_;
182              
183 8         33 local ( $!, $^E );
184              
185 8         9 my $ret;
186              
187 8 100       19 if (@args) {
188 4 100       54 $ret = CORE::chdir( $args[0] ) or do {
189 1 50       4 if ( __is_a_fh( $args[0] ) ) {
190 0         0 $NS->__THROW('Chdir');
191             }
192             else {
193 1         4 $NS->__THROW( 'Chdir', path => $args[0] );
194             }
195             };
196             }
197             else {
198 4 100       60 $ret = CORE::chdir or do {
199 2         5 my $path = _get_what_chdir_took_as_homedir();
200              
201 2 100       7 if ( !defined $path ) {
202 1         3 $NS->__THROW('Chdir');
203             }
204              
205 1         5 $NS->__THROW( 'Chdir', path => $path );
206             };
207             }
208              
209 5         27 return $ret;
210             }
211              
212             sub _get_what_chdir_took_as_homedir {
213 2     2   5 my $path = $ENV{'HOME'};
214 2 100       10 if ( !defined $path ) {
215 1         2 $path = $ENV{'LOGDIR'};
216              
217 1 50 33     11 if ( !defined($path) && $^O eq 'VMS' ) {
218 0         0 $path = $ENV{'SYS$LOGIN'};
219             }
220             }
221              
222 2         5 return $path;
223             }
224              
225             #A bit more restrictive than Perl’s built-in print():
226             # - A file handle is still optional, but it MUST be a reference.
227             #
228             #This does still fall back to $_ and does still use the default file handle
229             #if either the LIST or FILEHANDLE is omitted (cf. perldoc -f print).
230             #
231             sub print {
232 0     0 0 0 my ( $NS, $args_ar ) = ( shift, \@_ );
233              
234 0         0 local ( $!, $^E );
235              
236 0         0 my $ret;
237 0 0       0 if ( __is_a_fh( $args_ar->[0] ) ) {
238 0 0       0 $ret = CORE::print { shift @$args_ar } ( @$args_ar ? @$args_ar : $_ );
  0         0  
239             }
240             else {
241 0 0       0 $ret = CORE::print( @$args_ar ? @$args_ar : $_ );
242             }
243              
244 0 0       0 if ($^E) {
245              
246             #Figure out the "length" to report to the exception object.
247 0         0 my $length;
248 0 0       0 if (@$args_ar) {
249 0         0 $length = 0;
250 0         0 $length += length for @$args_ar;
251             }
252             else {
253 0         0 $length = length;
254             }
255              
256 0         0 $NS->__THROW( 'Write', length => $length );
257             }
258              
259 0         0 return $ret;
260             }
261              
262             sub syswrite {
263 0     0 0 0 my ( $NS, $fh, @length_offset ) = ( shift, shift, @_[ 1 .. $#_ ] );
264              
265             #https://github.com/pjcj/Devel--Cover/issues/125
266             #my ( $NS, $fh, $buffer_sr, @length_offset ) = ( shift, shift, \shift, @_ );
267              
268 0         0 my ( $length, $offset ) = @length_offset;
269              
270 0         0 local ( $!, $^E );
271              
272 0         0 my $ret;
273 0 0       0 if ( @length_offset > 1 ) {
    0          
274 0         0 $ret = CORE::syswrite( $fh, $_[0], $length, $offset );
275             }
276             elsif (@length_offset) {
277 0         0 $ret = CORE::syswrite( $fh, $_[0], $length );
278             }
279             else {
280 0         0 $ret = CORE::syswrite( $fh, $_[0] );
281             }
282              
283 0 0       0 if ( !defined $ret ) {
284 0         0 my $real_length = length $_[0];
285              
286 0 0       0 if ($offset) {
287 0 0       0 if ( $offset > 0 ) {
288 0         0 $real_length -= $offset;
289             }
290             else {
291 0         0 $real_length = 0 - $offset;
292             }
293             }
294              
295 0 0 0     0 if ( defined $length && $length < $real_length ) {
296 0         0 $real_length = $length;
297             }
298              
299 0         0 $NS->__THROW( 'Write', length => $real_length );
300             }
301              
302 0         0 return $ret;
303             }
304              
305             #----------------------------------------------------------------------
306             #NOTE: read() and sysread() implementations are exactly the same except
307             #for the CORE:: function call. Alas, Perl’s prototyping stuff seems to
308             #make it impossible not to duplicate code here.
309              
310             sub read {
311 1     1 0 35631 my ( $NS, $fh, @length_offset ) = ( shift, shift, @_[ 1 .. $#_ ] );
312              
313             #https://github.com/pjcj/Devel--Cover/issues/125
314             #my ( $NS, $fh, $buffer_sr, @length_offset ) = ( shift, shift, \shift, @_ );
315              
316 1         3 my ( $length, $offset ) = @length_offset;
317              
318 1         5 local ( $!, $^E );
319              
320             #NOTE: Perl’s prototypes can throw errors on things like:
321             #(@length_offset > 1) ? $offset : ()
322             #...so the following writes out the two forms of read():
323              
324 1         3 my $ret;
325 1 50       4 if ( @length_offset > 1 ) {
326 0         0 $ret = CORE::read( $fh, $_[0], $length, $offset );
327             }
328             else {
329 1         21 $ret = CORE::read( $fh, $_[0], $length );
330             }
331              
332 1 50       5 if ( !defined $ret ) {
333 1         9 $NS->__THROW( 'Read', length => $length );
334             }
335              
336 0         0 return $ret;
337             }
338              
339             sub sysread {
340 0     0 0 0 my ( $NS, $fh, @length_offset ) = ( shift, shift, @_[ 1 .. $#_ ] );
341              
342             #https://github.com/pjcj/Devel--Cover/issues/125
343             #my ( $NS, $fh, $buffer_sr, @length_offset ) = ( shift, shift, \shift, @_ );
344              
345 0         0 my ( $length, $offset ) = @length_offset;
346              
347 0         0 local ( $!, $^E );
348              
349             #NOTE: Perl’s prototypes can throw errors on things like:
350             #(@length_offset > 1) ? $offset : ()
351             #...so the following writes out the two forms of sysread():
352              
353 0         0 my $ret;
354 0 0       0 if ( @length_offset > 1 ) {
355 0         0 $ret = CORE::sysread( $fh, $_[0], $length, $offset );
356             }
357             else {
358 0         0 $ret = CORE::sysread( $fh, $_[0], $length );
359             }
360              
361 0 0       0 if ( !defined $ret ) {
362 0         0 $NS->__THROW( 'Read', length => $length );
363             }
364              
365 0         0 return $ret;
366             }
367              
368             sub close {
369 6     6 0 8359 my ( $NS, $fh ) = @_;
370              
371 6         42 local ( $!, $^E );
372 6 100       68 my $ok = ( $fh ? CORE::close($fh) : CORE::close() ) or do {
    100          
373 2         12 $NS->__THROW('Close');
374             };
375              
376 4         21 return $ok;
377             }
378              
379             #NOTE: See above about read/sysread; the same duplicated code problem
380             #applies to seek/sysseek.
381              
382             sub seek {
383 0     0 0 0 my ( $NS, $fh, $pos, $whence ) = @_;
384              
385 0         0 local ( $!, $^E );
386 0 0       0 my $ok = CORE::seek( $fh, $pos, $whence ) or do {
387 0         0 $NS->__THROW( 'FileSeek', whence => $whence, position => $pos );
388             };
389              
390 0         0 return $ok;
391             }
392              
393             sub sysseek {
394 0     0 0 0 my ( $NS, $fh, $pos, $whence ) = @_;
395              
396 0         0 local ( $!, $^E );
397 0 0       0 my $ok = CORE::sysseek( $fh, $pos, $whence ) or do {
398 0         0 $NS->__THROW( 'FileSeek', whence => $whence, position => $pos );
399             };
400              
401 0         0 return $ok;
402             }
403              
404             sub truncate {
405 0     0 0 0 my ( $NS, $fh_or_expr, $length ) = @_;
406              
407 0         0 local ( $!, $^E );
408 0 0       0 my $ok = CORE::truncate( $fh_or_expr, $length ) or do {
409 0         0 $NS->__THROW( 'FileTruncate', length => $length );
410             };
411              
412 0         0 return $ok;
413             }
414              
415             #----------------------------------------------------------------------
416              
417             sub opendir {
418 1     1 0 1844 my ( $NS, $dir ) = ( shift, @_[ 1 .. $#_ ] );
419              
420             #https://github.com/pjcj/Devel--Cover/issues/125
421             #my ( $NS, $dh_r, $dir ) = ( shift, \shift, shift );
422              
423 1         6 local ( $!, $^E );
424 1 50       29 my $ok = CORE::opendir( $_[0], $dir ) or do {
425 0         0 $NS->__THROW( 'DirectoryOpen', path => $dir );
426             };
427              
428 1         4 return $ok;
429             }
430              
431             sub rewinddir {
432 0     0 0 0 my ( $NS, $dh ) = @_;
433              
434 0         0 local ( $!, $^E );
435 0 0       0 my $ok = CORE::rewinddir($dh) or do {
436 0         0 $NS->__THROW('DirectoryRewind');
437             };
438              
439 0         0 return $ok;
440             }
441              
442             sub closedir {
443 2     2 0 914 my ( $NS, $dh ) = @_;
444              
445 2         9 local ( $!, $^E );
446 2 100       11 my $ok = CORE::closedir($dh) or do {
447 1         4 $NS->__THROW('DirectoryClose');
448             };
449              
450 1         29 return $ok;
451             }
452              
453             #----------------------------------------------------------------------
454              
455             #NOTE: To get stat(_), do stat(\*_).
456             sub stat {
457 7     7 0 2793 my ( $NS, $path_or_fh ) = @_;
458              
459 7         42 local ( $!, $^E );
460              
461 7 50       144 my $ret = wantarray ? [ CORE::stat($path_or_fh) ] : CORE::stat($path_or_fh);
462              
463 7 50       24 if ($^E) {
464 0 0       0 if ( __is_a_fh($path_or_fh) ) {
465 0         0 $NS->__THROW('Stat');
466             }
467              
468 0         0 $NS->__THROW( 'Stat', path => $path_or_fh );
469             }
470              
471 7 50       51 return wantarray ? @$ret : $ret;
472             }
473              
474             #NOTE: To get lstat(_), do lstat(\*_).
475             sub lstat {
476 0     0 0 0 my ( $NS, $path_or_fh ) = @_;
477              
478 0         0 local ( $!, $^E );
479              
480 0 0       0 my $ret = wantarray ? [ CORE::lstat($path_or_fh) ] : CORE::lstat($path_or_fh);
481              
482 0 0       0 if ($^E) {
483 0 0       0 if ( __is_a_fh($path_or_fh) ) {
484 0         0 $NS->__THROW('Stat');
485             }
486              
487 0         0 $NS->__THROW( 'Stat', path => $path_or_fh );
488             }
489              
490 0 0       0 return wantarray ? @$ret : $ret;
491             }
492              
493             #----------------------------------------------------------------------
494              
495             sub fileno {
496 0     0 0 0 my ( $NS, $fh ) = @_;
497              
498 0         0 local ( $!, $^E );
499 0         0 my $fileno = CORE::fileno($fh);
500              
501 0 0       0 if ( !defined $fileno ) {
502 0         0 $NS->__THROW('Fileno');
503             }
504              
505 0         0 return $fileno;
506             }
507              
508             sub flock {
509 0     0 0 0 my ( $NS, $fh, $operation ) = @_;
510              
511 0         0 local ( $!, $^E );
512 0 0       0 my $ok = CORE::flock( $fh, $operation ) or do {
513 0         0 $NS->__THROW( 'Flock', operation => $operation );
514             };
515              
516 0         0 return $ok;
517             }
518              
519             #NOTE: This will only chmod() one thing at a time. It refuses to support
520             #multiple chmod() operations within the same call. This is in order to provide
521             #reliable error reporting.
522             #
523             #You, of course, can still do: IO::Die->chmod() for @items;
524             #
525             sub chmod {
526 5     5 1 5683 my ( $NS, $mode, $target, @too_many_args ) = @_;
527              
528             #This is here because it’s impossible to do reliable error-checking when
529             #you operate on >1 filesystem node at once.
530 5 100       26 die "Only one path at a time!" if @too_many_args;
531              
532             #NOTE: This breaks chmod’s error reporting when a file handle is passed in.
533             #cf. https://rt.perl.org/Ticket/Display.html?id=122703
534 4         17 local ( $!, $^E );
535              
536 4 100       79 my $ok = CORE::chmod( $mode, $target ) or do {
537 2 50       6 if ( __is_a_fh($target) ) {
538 0         0 $NS->__THROW( 'Chmod', permissions => $mode );
539             }
540              
541 2         8 $NS->__THROW( 'Chmod', permissions => $mode, path => $target );
542             };
543              
544 2         8 return $ok;
545             }
546              
547             #NOTE: This will only chown() one thing at a time. It refuses to support
548             #multiple chown() operations within the same call. This is in order to provide
549             #reliable error reporting.
550             #
551             #You, of course, can still do: IO::Die->chown() for @items;
552             #
553             sub chown {
554 5     5 1 5647 my ( $NS, $uid, $gid, $target, @too_many_args ) = @_;
555              
556             #This is here because it’s impossible to do reliable error-checking when
557             #you operate on >1 filesystem node at once.
558 5 100       24 die "Only one path at a time!" if @too_many_args;
559              
560 4         17 local ( $!, $^E );
561              
562 4 100       93 my $ok = CORE::chown( $uid, $gid, $target ) or do {
563 2 50       6 if ( __is_a_fh($target) ) {
564 0         0 $NS->__THROW( 'Chown', uid => $uid, gid => $gid );
565             }
566              
567 2         8 $NS->__THROW( 'Chown', uid => $uid, gid => $gid, path => $target );
568             };
569              
570 2         8 return $ok;
571             }
572              
573             sub link {
574 0     0 0 0 my ( $NS, $old, $new ) = @_;
575              
576 0         0 local ( $!, $^E );
577 0   0     0 my $ok = CORE::link( $old, $new ) || do {
578             $NS->__THROW( 'Link', oldpath => $old, newpath => $new );
579             };
580              
581 0         0 return $ok;
582             }
583              
584             sub symlink {
585 0     0 0 0 my ( $NS, $old, $new ) = @_;
586              
587 0         0 local ( $!, $^E );
588 0 0       0 my $ok = CORE::symlink( $old, $new ) or do {
589 0         0 $NS->__THROW( 'SymlinkCreate', oldpath => $old, newpath => $new );
590             };
591              
592 0         0 return $ok;
593             }
594              
595             sub readlink {
596 0     0 0 0 my $NS = shift;
597 0 0       0 my $path = @_ ? shift : $_;
598              
599 0         0 local ( $!, $^E );
600 0 0       0 my $ok = CORE::readlink($path) or do {
601 0         0 $NS->__THROW( 'SymlinkRead', path => $path );
602             };
603              
604 0         0 return $ok;
605             }
606              
607             sub rename {
608 0     0 0 0 my ( $NS, $old, $new ) = @_;
609              
610 0         0 local ( $!, $^E );
611 0 0       0 my $ok = CORE::rename( $old, $new ) or do {
612 0         0 $NS->__THROW( 'Rename', oldpath => $old, newpath => $new );
613             };
614              
615 0         0 return $ok;
616             }
617              
618             #NOTE: This will only unlink() one file at a time. It refuses to support
619             #multiple unlink() operations within the same call. This is in order to provide
620             #reliable error reporting.
621             #
622             #You, of course, can still do: IO::Die->unlink() for @files;
623             #
624             sub unlink {
625 0     0 1 0 my ( $NS, @paths ) = @_;
626              
627             #This is here because it’s impossible to do reliable error-checking when
628             #you operate on >1 filesystem node at once.
629 0 0       0 die "Only one path at a time!" if @paths > 1;
630              
631 0 0       0 if ( !@paths ) {
632 0         0 @paths = ($_);
633             }
634              
635 0         0 local ( $!, $^E );
636 0 0       0 my $ok = CORE::unlink(@paths) or do {
637 0         0 $NS->__THROW( 'Unlink', path => $paths[0] );
638             };
639              
640 0         0 return $ok;
641             }
642              
643             sub mkdir {
644 0     0 0 0 my ( $NS, @args ) = @_;
645              
646 0         0 local ( $!, $^E );
647              
648 0         0 my $ret;
649 0 0       0 if ( @args > 1 ) {
650 0         0 $ret = CORE::mkdir $args[0], $args[1];
651             }
652             else {
653 0 0       0 if ( !@args ) {
654 0         0 @args = ($_);
655             }
656              
657 0         0 $ret = CORE::mkdir( $args[0] );
658             }
659              
660 0 0       0 if ( !$ret ) {
661 0         0 $NS->__THROW( 'DirectoryCreate', path => $args[0], mask => $args[1] );
662             }
663              
664 0         0 return $ret;
665             }
666              
667             sub rmdir {
668 0     0 0 0 my ( $NS, @args ) = @_;
669              
670             #Perl's rmdir() doesn’t actually allow batching like this,
671             #but we might as well prevent anyone from trying.
672 0 0       0 die "Only one path at a time!" if @args > 1;
673              
674 0 0       0 if ( !@args ) {
675 0         0 @args = ($_);
676             }
677              
678 0         0 local ( $!, $^E );
679 0 0       0 my $ok = CORE::rmdir( $args[0] ) or do {
680 0         0 $NS->__THROW( 'DirectoryDelete', path => $args[0] );
681             };
682              
683 0         0 return $ok;
684             }
685              
686             sub fork {
687 0     0 1 0 my ($NS) = @_;
688              
689 0         0 my $pid = fork;
690              
691 0 0       0 $NS->__THROW('Fork') if !defined $pid;
692              
693 0         0 return $pid;
694             }
695              
696             sub kill {
697 0     0 1 0 my ( $NS, $sig, @list ) = @_;
698              
699 0 0       0 die "Only 1 process!" if @list > 1;
700              
701 0         0 local ( $!, $^E );
702 0         0 my $ret = CORE::kill( $sig, @list );
703 0 0       0 if ($!) {
704 0         0 $NS->__THROW( 'Kill', signal => $sig, process => $list[0] );
705             }
706              
707 0         0 return $ret;
708             }
709              
710             sub exec {
711 1     1 1 1579 my ( $NS, $progname, @args ) = @_;
712              
713 1 0       3 my $ok = CORE::exec {$progname} $progname, @args or do {
  1         0  
714 0         0 $NS->__THROW( 'Exec', program => $progname, arguments => \@args );
715             };
716              
717 0         0 return $ok;
718             }
719              
720             sub pipe {
721 0     0 0 0 my ($NS) = (shift);
722              
723             #https://github.com/pjcj/Devel--Cover/issues/125
724             #my ( $NS, $read_r, $write_r ) = ( shift, \shift, \shift );
725              
726 0         0 local ( $!, $^E );
727 0 0       0 my $ok = CORE::pipe( $_[0], $_[1] ) or do {
728 0         0 $NS->__THROW('Pipe');
729             };
730              
731 0         0 return $ok;
732             }
733              
734             my $DEFAULT_BINMODE_LAYER = ':raw'; #cf. perldoc -f binmode
735              
736             sub binmode {
737 2     2 0 3375 my ( $NS, $fh_r, $layer ) = @_;
738              
739 2 50       7 if ( !defined $layer ) {
740 2         4 $layer = $DEFAULT_BINMODE_LAYER;
741             }
742              
743 2         7 local ( $!, $^E );
744 2 100       13 my $ok = CORE::binmode( $fh_r, $layer ) or do {
745 1         4 $NS->__THROW( 'Binmode', layer => $layer );
746             };
747              
748 1         7 return $ok;
749             }
750              
751             #NOTE: This will only utime() one thing at a time. It refuses to support
752             #multiple utime() operations within the same call. This is in order to provide
753             #reliable error reporting.
754             #
755             #You, of course, can still do: IO::Die->utime() for @items;
756             #
757             sub utime {
758 0     0 1   my ( $NS, $atime, $mtime, $target, @too_many_args ) = @_;
759              
760 0 0         die "Only one utime() at a time!" if @too_many_args;
761              
762 0           local ( $!, $^E );
763 0 0         my $ok = CORE::utime( $atime, $mtime, $target ) or do {
764 0 0         if ( __is_a_fh($target) ) {
765 0           $NS->__THROW( 'Utime', atime => $atime, mtime => $mtime, path => $target );
766             }
767              
768 0           $NS->__THROW( 'Utime', atime => $atime, mtime => $mtime );
769             };
770              
771 0           return $ok;
772             }
773              
774             sub fcntl {
775 0     0 0   my ( $NS, $fh, $func, $scalar ) = @_;
776              
777 0           local ( $!, $^E );
778 0 0         my $ok = CORE::fcntl( $fh, $func, $scalar ) or do {
779 0           $NS->__THROW( 'Fcntl', function => $func, scalar => $scalar );
780             };
781              
782 0           return $ok;
783             }
784              
785             sub select {
786 0     0 1   my ( $NS, $timeout ) = ( shift, $_[3] );
787              
788             #Perl::Critic says not to use one-arg select() anyway.
789 0 0         die "Need four args!" if @_ < 4;
790              
791 0           local ( $!, $^E );
792 0           my ( $nfound, $timeleft ) = CORE::select( $_[0], $_[1], $_[2], $timeout );
793              
794 0 0         if ($^E) {
795 0           $NS->__THROW('Select');
796             }
797              
798 0 0         return wantarray ? ( $nfound, $timeleft ) : $nfound;
799             }
800              
801             #----------------------------------------------------------------------
802              
803             sub socket {
804 0     0 0   my ( $NS, $domain, $type, $protocol ) = ( shift, @_[ 1 .. $#_ ] );
805              
806             #https://github.com/pjcj/Devel--Cover/issues/125
807             #my ( $NS, $socket_r, $domain, $type, $protocol ) = ( shift, \shift, shift, shift, shift );
808              
809 0           local ( $!, $^E );
810 0 0         my $ok = CORE::socket( $_[0], $domain, $type, $protocol ) or do {
811 0           $NS->__THROW( 'SocketOpen', domain => $domain, type => $type, protocol => $protocol );
812             };
813              
814 0           return $ok;
815             }
816              
817             sub socketpair {
818 0     0 0   my ( $NS, $domain, $type, $protocol ) = ( shift, @_[ 2 .. $#_ ] );
819              
820             #https://github.com/pjcj/Devel--Cover/issues/125
821             #my ( $NS, $socket1_r, $socket2_r, $domain, $type, $protocol ) = ( \shift, \shift, shift, shift );
822              
823 0           local ( $!, $^E );
824 0 0         my $ok = CORE::socketpair( $_[0], $_[1], $domain, $type, $protocol ) or do {
825 0           $NS->__THROW( 'SocketPair', domain => $domain, type => $type, protocol => $protocol );
826             };
827              
828 0           return $ok;
829             }
830              
831             sub bind {
832 0     0 0   my ( $NS, $socket, $name ) = @_;
833              
834 0           local ( $!, $^E );
835 0 0         my $ok = CORE::bind( $socket, $name ) or do {
836 0           $NS->__THROW( 'SocketBind', name => $name );
837             };
838              
839 0           return $ok;
840             }
841              
842             sub connect {
843 0     0 0   my ( $NS, $socket, $name ) = @_;
844              
845 0           local ( $!, $^E );
846 0 0         my $ok = CORE::connect( $socket, $name ) or do {
847 0           $NS->__THROW( 'SocketConnect', name => $name );
848             };
849              
850 0           return $ok;
851             }
852              
853             sub accept {
854 0     0 0   my ( $NS, $generic_socket ) = @_[ 0, 2 ];
855              
856             #https://github.com/pjcj/Devel--Cover/issues/125
857             #my ( $NS, $new_socket, $generic_socket ) = @_;
858              
859 0           local ( $!, $^E );
860 0 0         my $ok = CORE::accept( $_[1], $generic_socket ) or do {
861 0           $NS->__THROW('SocketAccept');
862             };
863              
864 0           return $ok;
865             }
866              
867             sub getsockopt {
868 0     0 0   my ( $NS, $socket, $level, $optname ) = @_;
869              
870 0           local ( $!, $^E );
871 0           my $res = CORE::getsockopt( $socket, $level, $optname );
872 0 0         if ( !defined $res ) {
873 0           $NS->__THROW( 'SocketGetOpt', level => $level, optname => $optname );
874             }
875              
876 0           return $res;
877             }
878              
879             sub setsockopt {
880 0     0 0   my ( $NS, $socket, $level, $optname, $optval ) = @_;
881              
882 0           local ( $!, $^E );
883 0           my $res = CORE::setsockopt( $socket, $level, $optname, $optval );
884 0 0         if ( !defined $res ) {
885 0           $NS->__THROW( 'SocketSetOpt', level => $level, optname => $optname, optval => $optval );
886             }
887              
888 0           return $res;
889             }
890              
891             sub listen {
892 0     0 0   my ( $NS, $socket, $queuesize ) = @_;
893              
894 0           local ( $!, $^E );
895 0 0         my $ok = CORE::listen( $socket, $queuesize ) or do {
896 0           $NS->__THROW( 'SocketListen', queuesize => $queuesize );
897             };
898              
899 0           return $ok;
900             }
901              
902             sub recv {
903 0     0 0   my ( $NS, $socket, $length, $flags ) = ( shift, shift, @_[ 1 .. $#_ ] );
904              
905             #https://github.com/pjcj/Devel--Cover/issues/125
906             #my ( $NS, $socket, $scalar_r, $length, $flags ) = ( shift, shift, \shift, @_ );
907              
908 0           local ( $!, $^E );
909 0           my $res = CORE::recv( $socket, $_[0], $length, $flags );
910 0 0         if ( !defined $res ) {
911 0           $NS->__THROW( 'SocketReceive', length => $length, flags => $flags );
912             }
913              
914 0           return $res;
915             }
916              
917             sub send {
918 0     0 0   my ( $NS, $socket, $flags, $to ) = ( shift, shift, @_[ 1 .. $#_ ] );
919              
920             #https://github.com/pjcj/Devel--Cover/issues/125
921             #my ( $NS, $socket, $msg_r, $flags, $to ) = ( shift, shift, \shift, @_ );
922              
923 0           local ( $!, $^E );
924 0           my $res;
925 0 0         if ( defined $to ) {
926 0           $res = CORE::send( $socket, $_[0], $flags, $to );
927             }
928             else {
929 0           $res = CORE::send( $socket, $_[0], $flags );
930             }
931              
932 0 0         if ( !defined $res ) {
933 0           $NS->__THROW( 'SocketSend', length => length( $_[0] ), flags => $flags );
934             }
935              
936 0           return $res;
937             }
938              
939             sub shutdown {
940 0     0 0   my ( $NS, $socket, $how ) = @_;
941              
942 0           local ( $!, $^E );
943              
944 0           my $res = CORE::shutdown( $socket, $how );
945 0 0         if ( !$res ) {
946 0 0         die "Invalid filehandle!" if !defined $res;
947 0           $NS->__THROW( 'SocketShutdown', how => $how );
948             }
949              
950 0           return $res;
951             }
952              
953             #----------------------------------------------------------------------
954             # CONVENIENCE
955             #
956              
957             my $Fcntl_SEEK_CUR = 1;
958              
959             #Note that, since we die() on error, this does NOT return "0 but true"
960             #as sysseek() does; instead it returns just a plain 0.
961             sub systell {
962 0     0 1   my ( $NS, $fh ) = @_;
963              
964             #cf. perldoc -f tell
965 0           return 0 + $NS->sysseek( $fh, 0, $Fcntl_SEEK_CUR );
966             }
967              
968             #----------------------------------------------------------------------
969              
970             __END__