File Coverage

blib/lib/IO/Die.pm
Criterion Covered Total %
statement 9 213 4.2
branch 0 96 0.0
condition 0 72 0.0
subroutine 3 39 7.6
pod n/a
total 12 420 2.8


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