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.00"; #Untouched since 1.03
3              
4 8     8   687591 use 5.008; #At least, for decent perlio, and other modernisms
  8         80  
5 8     8   81 use strict;
  8         17  
  8         229  
6 8     8   42 use warnings;
  8         17  
  8         297  
7 8     8   64 use base qw( Tie::Handle );
  8         17  
  8         4210  
8 8     8   22098 use Env qw( PAGER );
  8         18945  
  8         80  
9 8     8   1421 use File::Spec;
  8         16  
  8         175  
10 8     8   43 use PerlIO;
  8         13  
  8         68  
11 8     8   2994 use Symbol;
  8         4668  
  8         550  
12              
13 8     8   4790 use overload '+' => "PID", bool=> "PID";
  8         3968  
  8         50  
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 115737 my $io_pager;
21              
22             #Permit explicit use of pure perl pager
23 14         31 local $_ = 'IO::Pager::less';
24 14 50 33     118 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         174 my $which = eval { require File::Which };
  14         4033  
29              
30             # Look for pager in PAGER first
31 14 100       8575 if ($PAGER) {
32             # Strip arguments e.g. 'less --quiet'
33 4         35 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       143 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     59 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     37 $io_pager ||= 'more';
53              
54 14         65 return $io_pager;
55             }
56              
57             sub _check_pagers {
58 15     15   34 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         35 for my $pager (@$pagers) {
64             # Get full path
65 15         18 my $loc;
66 15 100 66     237 if ( $which && (not File::Spec->file_name_is_absolute($pager)) ) {
67 2         8 $loc = File::Which::which($pager);
68             } else {
69 13         23 $loc = $pager;
70             }
71             # Test that full path is valid (some platforms don't do -x so we use -e)
72 15 100 66     860 if ( defined($loc) && (-e $loc) ) {
73 14         43 $io_pager = $loc;
74 14         33 last;
75             }
76             }
77 15         97 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   4360 our $oldPAGER = $PAGER || '';
83 8         226 $PAGER = find_pager();
84            
85 8 50 33     140 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 113740 shift;
105              
106 4         7 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       12 $args{mode} = splice(@_, 1, 1) if $_[1] =~ /^:/;
114 2 50       9 $args{subclass} = pop if exists($_[1]);
115             }
116              
117             #Leave filehandle in @_ for pass by reference to allow gensym
118 4   100     22 $args{subclass} ||= 'IO::Pager::Unbuffered';
119 4         18 $args{subclass} =~ s/^(?!IO::Pager::)/IO::Pager::/;
120 4 50       245 eval "require $args{subclass}" or die "Could not load $args{subclass}: $@\n";
121 4         29 my $token = $args{subclass}->new(@_);
122              
123 2 50       192 if( defined($args{mode}) ){
124 0         0 $args{mode} =~ s/^\|-//;
125 0         0 $token->BINMODE($args{mode});
126             }
127 2         170 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   22 $_[1] = gensym() if !defined($_[1]);
134              
135 8     8   3463 no strict 'refs';
  8         17  
  8         1556  
136 4   33     46 $_[1] ||= *{select()};
  0         0  
137              
138             # Are we on a TTY? STDOUT & STDERR are separately bound
139 4 100       27 if ( defined( my $FHn = fileno($_[1]) ) ) {
140 2 50       10 if ( $FHn == fileno(STDOUT) ) {
141 2 50       40 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         6 return ($_[0], $_[1]);
152             }
153              
154              
155             # Methods required for implementing a tied filehandle class
156              
157             sub TIEHANDLE {
158 2     2   7 my ($class, $tied_fh) = @_;
159 2 100       12 unless ( $PAGER ){
160 1         28 die "The PAGER environment variable is not defined, you may need to set it manually.";
161             }
162 1         10 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   70 do{ no warnings; $child = CORE::open($real_fh, '|-', $PAGER) };
  8         15  
  8         6872  
  1         3  
  1         7  
168 1 50       2212 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         56 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   73 { no warnings 'once'; *DESTROY = \&CLOSE; }
  8         18  
  8         856  
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   91 no strict 'refs';
  8         25  
  8         645  
277             *{lc($method)} = \&{$method};
278             }
279              
280              
281             1;
282              
283             __END__