File Coverage

blib/lib/IO/Interactive.pm
Criterion Covered Total %
statement 34 69 49.2
branch 5 22 22.7
condition 1 12 8.3
subroutine 10 11 90.9
pod 3 3 100.0
total 53 117 45.3


line stmt bran cond sub pod time code
1             package IO::Interactive;
2              
3 4     4   27005 use strict;
  4         5  
  4         85  
4 4     4   12 use warnings;
  4         4  
  4         505  
5              
6             $IO::Interactive::VERSION = '1.022';
7              
8             sub is_interactive {
9 5     5 1 22 my ($out_handle) = (@_, select); # Default to default output handle
10              
11             # Not interactive if output is not to terminal...
12 5 50       40 return 0 if not -t $out_handle;
13              
14             # If *ARGV is opened, we're interactive if...
15 0 0 0     0 if ( tied(*ARGV) or defined(fileno(ARGV)) ) { # this is what 'Scalar::Util::openhandle *ARGV' boils down to
16              
17             # ...it's currently opened to the magic '-' file
18 0 0 0     0 return -t *STDIN if defined $ARGV && $ARGV eq '-';
19              
20             # ...it's at end-of-file and the next file is the magic '-' file
21 0 0 0     0 return @ARGV>0 && $ARGV[0] eq '-' && -t *STDIN if eof *ARGV;
22              
23             # ...it's directly attached to the terminal
24 0         0 return -t *ARGV;
25             }
26              
27             # If *ARGV isn't opened, it will be interactive if *STDIN is attached
28             # to a terminal.
29             else {
30 0         0 return -t *STDIN;
31             }
32             }
33              
34             local (*DEV_NULL, *DEV_NULL2);
35             my $dev_null;
36             BEGIN {
37 4 50   4   59 pipe *DEV_NULL, *DEV_NULL2
38             or die "Internal error: can't create null filehandle";
39 4         579 $dev_null = \*DEV_NULL;
40             }
41              
42             sub interactive {
43 2     2 1 19 my ($out_handle) = (@_, \*STDOUT); # Default to STDOUT
44 2 50       4 return &is_interactive ? $out_handle : $dev_null;
45             }
46              
47             sub _input_pending_on {
48 0     0   0 my ($fh) = @_;
49 0         0 my $read_bits = "";
50 0         0 my $bit = fileno($fh);
51 0 0       0 return if $bit < 0;
52 0         0 vec($read_bits, fileno($fh), 1) = 1;
53 0         0 select $read_bits, undef, undef, 0.1;
54 0         0 return $read_bits;
55             }
56              
57             sub busy (&) {
58 1     1 1 12 my ($block_ref) = @_;
59              
60             # Non-interactive busy-ness is easy...just do it
61 1 50       2 if (!is_interactive()) {
62 1         2 $block_ref->();
63 1     1   3000226 open my $fh, '<', \ "";
  1         858  
  1         10  
  1         4  
64 1         708 return $fh;
65             }
66              
67             # Otherwise fork off an interceptor process...
68 0         0 my ($read, $write);
69 0         0 pipe $read, $write;
70 0         0 my $child = fork;
71              
72             # Within that interceptor process...
73 0 0       0 if (!$child) {
74             # Prepare to send back any intercepted input...
75 4     4   1926 use IO::Handle;
  4         18023  
  4         635  
76 0         0 close $read;
77 0         0 $write->autoflush(1);
78              
79             # Intercept that input...
80 0         0 while (1) {
81 0 0       0 if (_input_pending_on(\*ARGV)) {
82             # Read it...
83 0         0 my $res = ;
84              
85             # Send it back to the parent...
86 0         0 print {$write} $res;
  0         0  
87              
88             # Admonish them for not waiting...
89 0         0 print {*STDERR} "That input was ignored. ",
  0         0  
90             "Please don't press any keys yet.\n";
91             }
92             }
93 0         0 exit;
94             }
95              
96             # Meanwhile, back in the parent...
97 0         0 close $write;
98              
99             # Temporarily close the input...
100 0         0 local *ARGV;
101 0         0 open *ARGV, '<', \ "";
102              
103             # Do the job...
104 0         0 $block_ref->();
105              
106             # Take down the interceptor...
107 0         0 kill 9, $child;
108 0         0 wait;
109              
110             # Return whatever the interceptor caught...
111 0         0 return $read;
112             }
113              
114             sub import {
115 4     4   26 my ($package) = shift;
116 4         4 my $caller = caller;
117              
118             # Export each sub if it's requested...
119 4         13 for my $request ( @_ ) {
120 4     4   18 no strict 'refs';
  4         4  
  4         395  
121 3         3 my $impl = *{$package.'::'.$request}{CODE};
  3         10  
122 3 50 33     23 if(!$impl || $request =~ m/\A _/xms) {
123 0         0 require Carp;
124 0         0 Carp::croak("Unknown subroutine ($request()) requested");
125             }
126 3         4 *{$caller.'::'.$request} = $impl;
  3         3303  
127             }
128             }
129              
130             1; # Magic true value required at end of module
131             __END__