File Coverage

blib/lib/IO/Pager.pm
Criterion Covered Total %
statement 92 150 61.3
branch 29 66 43.9
condition 16 48 33.3
subroutine 19 31 61.2
pod 3 5 60.0
total 159 300 53.0


line stmt bran cond sub pod time code
1             package IO::Pager;
2             our $VERSION = "2.10"; #Untouched since 1.03
3              
4 8     8   628605 use 5.008; #At least, for decent perlio, and other modernisms
  8         83  
5 8     8   50 use strict;
  8         16  
  8         213  
6 8     8   42 use warnings;
  8         16  
  8         271  
7 8     8   42 use base qw( Tie::Handle );
  8         13  
  8         3967  
8 8     8   20512 use Env qw( PAGER );
  8         17746  
  8         47  
9 8     8   1299 use File::Spec;
  8         18  
  8         161  
10 8     8   40 use PerlIO;
  8         13  
  8         57  
11 8     8   2776 use Symbol;
  8         4485  
  8         524  
12              
13 8     8   4643 use overload '+' => "PID", bool=> "PID";
  8         3871  
  8         53  
14              
15             our $SIGPIPE;
16             #use Carp; $SIG{__WARN__} = sub{ print STDERR @_, Carp::longmess(),"\n\n"; };
17              
18             sub find_pager {
19             # Return the name (or path) of a pager that IO::Pager can use
20 14     14 0 115754 my $io_pager;
21              
22             #Permit explicit use of pure perl pager
23 14         31 local $_ = 'IO::Pager::less';
24 14 50 33     124 return $_ if (defined($_[0]) && ($_[0] eq $_)) or
      66        
      33        
25             (defined($PAGER) && ($PAGER eq $_));
26              
27             # Use File::Which if available (strongly recommended)
28 14         159 my $which = eval { require File::Which };
  14         3766  
29              
30             # Look for pager in PAGER first
31 14 100       8220 if ($PAGER) {
32             # Strip arguments e.g. 'less --quiet'
33 4         36 my ($pager, @options) = (split ' ', $PAGER);
34 4         29 $pager = _check_pagers([$pager], $which);
35 4 100       21 $io_pager = join ' ', ($pager, @options) if defined $pager;
36             }
37              
38             # Then search pager amongst usual suspects
39 14 100       133 if (not defined $io_pager) {
40 11         32 my @pagers = ('/etc/alternatives/pager',
41             '/usr/local/bin/less', '/usr/bin/less', '/usr/bin/more');
42 11         32 $io_pager = _check_pagers(\@pagers, $which)
43             }
44              
45             # Then check PATH for other pagers
46 14 50 33     53 if ( (not defined $io_pager) && $which ) {
47 0         0 my @pagers = ('less', 'most', 'w3m', 'lv', 'pg', 'more');
48 0         0 $io_pager = _check_pagers(\@pagers, $which );
49             }
50              
51             # If all else fails, default to more (actually IO::Pager::less first)
52 14   50     34 $io_pager ||= 'more';
53              
54 14         61 return $io_pager;
55             }
56              
57             sub _check_pagers {
58 15     15   32 my ($pagers, $which) = @_;
59             # Return the first pager in the list that is usable. For each given pager,
60             # given a pager name, try to finds its full path with File::Which if possible.
61             # Given a pager path, verify that it exists.
62 15         28 my $io_pager = undef;
63 15         32 for my $pager (@$pagers) {
64             # Get full path
65 15         21 my $loc;
66 15 100 66     241 if ( $which && (not File::Spec->file_name_is_absolute($pager)) ) {
67 2         8 $loc = File::Which::which($pager);
68             } else {
69 13         26 $loc = $pager;
70             }
71             # Test that full path is valid (some platforms don't do -x so we use -e)
72 15 100 66     795 if ( defined($loc) && (-e $loc) ) {
73 14         38 $io_pager = $loc;
74 14         33 last;
75             }
76             }
77 15         37 return $io_pager;
78             }
79              
80             #Should have this as first block for clarity, but not with its use of a sub
81             BEGIN { # Set the $ENV{PAGER} to something reasonable
82 8   50 8   3806 our $oldPAGER = $PAGER || '';
83 8         197 $PAGER = find_pager();
84            
85 8 50 33     134 if( ($PAGER =~ 'more' and $oldPAGER ne 'more') or
      33        
86             $PAGER eq 'IO::Pager::less' ){
87 0         0 my $io_pager = $PAGER;
88 0         0 eval "use IO::Pager::less";
89 0 0 0     0 $PAGER = $io_pager if $@ or not defined $PAGER;
90             }
91             }
92              
93              
94             #Factory
95             sub open(*;$@) { # FH, [MODE], [CLASS]
96 0     0 1 0 my $args = {procedural=>1};
97 0 0       0 $args->{mode} = splice(@_, 1, 1) if scalar(@_) == 3;
98 0 0       0 $args->{subclass} = pop if scalar(@_) == 2;
99 0         0 &new(undef, @_, $args);
100             }
101              
102             #Alternate entrance: drop class but leave FH, subclass
103             sub new(*;$@) { # FH, [MODE], [CLASS]
104 4     4 1 113079 shift;
105              
106 4         8 my %args;
107 4 50       30 if( ref($_[-1]) eq 'HASH' ){
    100          
108 0         0 %args = %{pop()};
  0         0  
109             #warn "REMAINDER? (@_)", scalar @_;
110 0         0 push(@_, $args{procedural});
111             }
112             elsif( defined($_[1]) ){
113 2 50       9 $args{mode} = splice(@_, 1, 1) if $_[1] =~ /^:/;
114 2 50       10 $args{subclass} = pop if exists($_[1]);
115             }
116              
117             #Leave filehandle in @_ for pass by reference to allow gensym
118 4   100     26 $args{subclass} ||= 'IO::Pager::Unbuffered';
119 4         15 $args{subclass} =~ s/^(?!IO::Pager::)/IO::Pager::/;
120 4 50       251 eval "require $args{subclass}" or die "Could not load $args{subclass}: $@\n";
121 4         27 my $token = $args{subclass}->new(@_);
122              
123 2 50       181 if( defined($args{mode}) ){
124 0         0 $args{mode} =~ s/^\|-//;
125 0         0 $token->BINMODE($args{mode});
126             }
127 2         171 return $token;
128             }
129              
130              
131             sub _init{ # CLASS, [FH] ## Note reversal of order due to CLASS from new()
132             #Assign by reference if empty scalar given as filehandle
133 4 100   4   18 $_[1] = gensym() if !defined($_[1]);
134              
135 8     8   3252 no strict 'refs';
  8         17  
  8         1643  
136 4   33     44 $_[1] ||= *{select()};
  0         0  
137              
138             # Are we on a TTY? STDOUT & STDERR are separately bound
139 4 100       30 if ( defined( my $FHn = fileno($_[1]) ) ) {
140 2 50       12 if ( $FHn == fileno(STDOUT) ) {
141 2 50       38 die '!TTY' unless -t $_[1];
142             }
143 0 0       0 if ( $FHn == fileno(STDERR) ) {
144 0 0       0 die '!TTY' unless -t $_[1];
145             }
146             }
147              
148             #XXX This allows us to have multiple pseudo-STDOUT
149             #return 0 unless -t STDOUT;
150              
151 2         8 return ($_[0], $_[1]);
152             }
153              
154              
155             # Methods required for implementing a tied filehandle class
156              
157             sub TIEHANDLE {
158 2     2   6 my ($class, $tied_fh) = @_;
159 2 100       13 unless ( $PAGER ){
160 1         28 die "The PAGER environment variable is not defined, you may need to set it manually.";
161             }
162 1         11 my($real_fh, $child, $dupe_fh);
163             # XXX What about localized GLOBs?!
164             # if( $tied_fh =~ /\*(?:\w+::)?STD(?:OUT|ERR)$/ ){
165             # open($dupe_fh, '>&', $tied_fh) or warn "Unable to dupe $tied_fh";
166             # }
167 8     8   54 do{ no warnings; $child = CORE::open($real_fh, '|-', $PAGER) };
  8         16  
  8         6544  
  1         3  
  1         7  
168 1 50       1995 if ( $child ){
169 0         0 my @oLayers = PerlIO::get_layers($tied_fh, details=>1, output=>1);
170 0         0 my $layers = '';
171 0         0 for(my $i=0;$i<$#oLayers;$i+=3){
172             #An extra base layer requires more keystrokes to exit
173 0 0 0     0 next if $oLayers[$i] =~ /unix|stdio/ && !defined($oLayers[+1]);
174              
175 0         0 $layers .= ":$oLayers[$i]";
176 0 0       0 $layers .= '(' . ($oLayers[$i+1]) . ')' if defined($oLayers[$i+1]);
177             }
178 0         0 CORE::binmode($real_fh, $layers);
179             }
180             else{
181 1         51 die "Could not pipe to PAGER ('$PAGER'): $!\n";
182             }
183 0           return bless {
184             'real_fh' => $real_fh,
185             # 'dupe_fh' => $dupe_fh,
186             'tied_fh' => "$tied_fh", #Avoid self-reference leak
187             'child' => $child,
188             'pager' => $PAGER,
189             }, $class;
190             }
191              
192              
193             sub BINMODE {
194 0     0     my ($self, $layer) = @_;
195 0 0         if( $layer =~ /^:LOG\((>{0,2})(.*)\)$/ ){
196 0 0 0       CORE::open($self->{LOG}, $1||'>', $2||"$$.log") or die $!;
      0        
197             }
198             else{
199 0   0       CORE::binmode($self->{real_fh}, $layer||':raw');
200             }
201             }
202              
203             sub WNOHANG();
204             sub EOF {
205 0     0     my $self = shift;
206              
207 0 0         unless( defined($SIGPIPE) ){
208 0           eval 'use POSIX ":sys_wait_h";';
209 0           $SIGPIPE = 0;
210             }
211              
212 0 0   0     $SIG{PIPE} = sub { $SIGPIPE = 1 unless $ENV{IP_EOF};
213 0           CORE::close($self->{real_fh});
214 0           waitpid($self->{child}, WNOHANG);
215 0           CORE::open($self->{real_fh}, '>&1');
216              
217 0           close($self->{LOG});
218 0           };
219 0           return $SIGPIPE;
220             }
221              
222              
223             sub PRINT {
224 0     0     my ($self, @args) = @_;
225 0 0         CORE::print {$self->{LOG}} @args if exists($self->{LOG});
  0            
226 0 0         CORE::print {$self->{real_fh}} @args or die "Could not print to PAGER: $!\n";
  0            
227             }
228              
229             sub PRINTF {
230 0     0     my ($self, $format, @args) = @_;
231 0           $self->PRINT(sprintf($format, @args));
232             }
233              
234              
235             sub say {
236 0     0 0   my ($self, @args) = @_;
237 0           $args[-1] .= "\n";
238 0           $self->PRINT(@args);
239             }
240              
241             sub WRITE {
242 0     0     my ($self, $scalar, $length, $offset) = @_;
243 0   0       $self->PRINT(substr($scalar, $offset||0, $length));
244             }
245              
246              
247             sub TELL {
248             #Buffered classes provide their own, and others may use this in another way
249 0     0     return undef;
250             }
251              
252              
253             sub FILENO {
254 0     0     CORE::fileno($_[0]->{real_fh});
255             }
256              
257             sub CLOSE {
258 0     0     my ($self) = @_;
259 0           CORE::close($self->{real_fh});
260             # untie($self->{tied_fh});
261             # *{$self->{tied_fh}} = *{$self->{dupe_fh}};
262             }
263              
264 8     8   81 { no warnings 'once'; *DESTROY = \&CLOSE; }
  8         17  
  8         824  
265              
266              
267             #Non-IO methods
268             sub PID{
269 0     0 1   my ($self) = @_;
270 0           return $self->{child};
271             }
272              
273              
274             #Provide lowercase aliases for accessors
275             foreach my $method ( qw(BINMODE CLOSE EOF PRINT PRINTF TELL WRITE PID) ){
276 8     8   85 no strict 'refs';
  8         26  
  8         594  
277             *{lc($method)} = \&{$method};
278             }
279              
280              
281             1;
282              
283             __END__