File Coverage

blib/lib/IO/ReStoreFH.pm
Criterion Covered Total %
statement 60 61 98.3
branch 20 28 71.4
condition 7 12 58.3
subroutine 15 16 93.7
pod 3 3 100.0
total 105 120 87.5


line stmt bran cond sub pod time code
1             package IO::ReStoreFH;
2              
3             # ABSTRACT: store/restore file handles
4              
5 4     4   1014900 use 5.10.0;
  4         43  
6              
7 4     4   47 use strict;
  4         8  
  4         76  
8 4     4   17 use warnings;
  4         8  
  4         258  
9              
10             our $VERSION = '0.11';
11              
12             # In Perl 5.10.1 a use or require of FileHandle or something in the
13             # FileHandle hierarchy (like FileHandle::Fmode, below) will cause the
14             # compiler to creat a stash for FileHandle. Then, there's some
15             # code in Perl_newio which checks if FileHandle has been loaded (just
16             # by checking for the stash) and aliases it to IO::Handle.
17             #
18             # This it mucks up method calls on filehandles if FileHandle isn't
19             # actually loaded, resulting in errors such as
20             #
21             # Can't locate object method "getline" via package "FileHandle"
22             #
23             # see http://perlmonks.org/?node_id=1073753, and tobyink's reply
24              
25             # So, we explicitly load FileHandle on 5.10.x to avoid these action
26             # at a distance problems.
27 4   33 4   2677 use if $^V >= v5.10.0 && $^V <= v5.11.0, 'FileHandle';
  4         52  
  4         89  
28              
29 4     4   1907 use FileHandle::Fmode ();
  4         5838  
  4         109  
30 4     4   27 use POSIX ();
  4         7  
  4         109  
31 4     4   632 use IO::Handle;
  4         6390  
  4         154  
32 4     4   23 use Scalar::Util;
  4         7  
  4         136  
33 4     4   2198 use Try::Tiny ();
  4         8300  
  4         2610  
34              
35             sub _croak {
36 4     4   93 require Carp;
37 4         524 goto &Carp::croak;
38             }
39              
40             sub new {
41 10     10 1 31869 my $class = shift;
42              
43 10         35 my $obj = bless { dups => [] }, $class;
44 10         39 $obj->store( $_ ) for @_;
45 6         18 return $obj;
46             }
47              
48             sub store {
49 10     10 1 41 my ( $self, $fh ) = @_;
50              
51             # if $fh is a reference, or a GLOB, it's probably
52             # a filehandle object of somesort
53              
54 10 100 100     161 if ( ref( $fh ) || 'GLOB' eq ref( \$fh ) ) {
    100 66        
55              
56             # now that we are sure that everything is loaded,
57             # check if it is an open filehandle; this doesn't disambiguate
58             # between objects that aren't filehandles or closed filehandles.
59 8 100       28 _croak( "\$fh is not an open filehandle\n" )
60             unless FileHandle::Fmode::is_FH( $fh );
61              
62             # get access mode; open documentation says mode must
63             # match that of original filehandle; do the best we can
64 5 50 33     92 my $mode
    100          
    50          
65             = FileHandle::Fmode::is_RO( $fh ) ? '<'
66             : FileHandle::Fmode::is_WO( $fh ) ? '>'
67             : FileHandle::Fmode::is_W( $fh )
68             && FileHandle::Fmode::is_R( $fh ) ? '+<'
69             : undef;
70              
71             # give up
72 5 50       312 _croak( "inexplicable error: unable to determine mode for \$fh;\n" )
73             if !defined $mode;
74              
75 5 100       39 $mode .= '>' if FileHandle::Fmode::is_A( $fh );
76              
77             # dup the filehandle
78 5 50       235 open my $dup, $mode . q{&}, $fh
79             or _croak( "error fdopening \$fh: $!\n" );
80              
81 5         15 push @{ $self->{dups} }, { fh => $fh, mode => $mode, dup => $dup };
  5         47  
82             }
83              
84             elsif (Scalar::Util::looks_like_number( $fh )
85             && POSIX::ceil( $fh ) == POSIX::floor( $fh ) )
86             {
87              
88             # as the caller specifically used an fd, don't go through Perl's
89             # IO system
90 1 50       16 my $dup = POSIX::dup( $fh )
91             or _croak( "error dup'ing file descriptor $fh: $!\n" );
92              
93 1         3 push @{ $self->{dups} }, { fd => $fh, dup => $dup };
  1         17  
94             }
95              
96             else {
97 1         6 _croak( '$fh must be opened Perl filehandle or object or integer file descriptor', );
98             }
99              
100 6         22 return;
101             }
102              
103             sub restore {
104 10     10 1 17 my $self = shift;
105              
106 10         18 my $dups = $self->{dups};
107 10         19 while ( my $dup = pop @{$dups} ) {
  16         60  
108              
109 6 100       57 if ( exists $dup->{fd} ) {
110             POSIX::dup2( $dup->{dup}, $dup->{fd} )
111 1 50       15 or _croak( "error restoring file descriptor $dup->{fd}: $!\n" );
112 1         10 POSIX::close( $dup->{dup} );
113             }
114              
115             else {
116             open( $dup->{fh}, $dup->{mode} . q{&}, $dup->{dup} )
117 5 50       618 or _croak( "error restoring file handle $dup->{fh}: $!" );
118 5 50       76 close( $dup->{dup} ) or _croak( q{error closing dup'ed filehandle} );
119             }
120             }
121 10         65 return;
122             }
123              
124             sub DESTROY {
125 10     10   839 my $self = shift;
126 10     10   582 Try::Tiny::try { $self->restore }
127 10     0   77 Try::Tiny::catch { _croak $_ };
  0         0  
128 10         207 return;
129             }
130              
131             1;
132              
133             #
134             # This file is part of IO-ReStoreFH
135             #
136             # This software is Copyright (c) 2012 by Smithsonian Astrophysical Observatory.
137             #
138             # This is free software, licensed under:
139             #
140             # The GNU General Public License, Version 3, June 2007
141             #
142              
143             __END__