File Coverage

blib/lib/IO/Interactive.pm
Criterion Covered Total %
statement 36 71 50.7
branch 5 22 22.7
condition 1 12 8.3
subroutine 11 12 91.6
pod 3 3 100.0
total 56 120 46.6


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