Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / Capture / Tiny.pm
CommitLineData
3fea05b9 1# Copyright (c) 2009 by David Golden. All rights reserved.
2# Licensed under Apache License, Version 2.0 (the "License").
3# You may not use this file except in compliance with the License.
4# A copy of the License was distributed with this file or you may obtain a
5# copy of the License from http://www.apache.org/licenses/LICENSE-2.0
6
7package Capture::Tiny;
8use 5.006;
9use strict;
10use warnings;
11use Carp ();
12use Exporter ();
13use IO::Handle ();
14use File::Spec ();
15use File::Temp qw/tempfile tmpnam/;
16# Get PerlIO or fake it
17BEGIN { eval { require PerlIO; 1 } or *PerlIO::get_layers = sub { return () }; }
18
19our $VERSION = '0.06';
20$VERSION = eval $VERSION; ## no critic
21our @ISA = qw/Exporter/;
22our @EXPORT_OK = qw/capture capture_merged tee tee_merged/;
23our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
24
25my $IS_WIN32 = $^O eq 'MSWin32';
26
27our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG};
28my $DEBUGFH;
29open $DEBUGFH, ">&STDERR" if $DEBUG;
30
31*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0};
32
33#--------------------------------------------------------------------------#
34# command to tee output -- the argument is a filename that must
35# be opened to signal that the process is ready to receive input.
36# This is annoying, but seems to be the best that can be done
37# as a simple, portable IPC technique
38#--------------------------------------------------------------------------#
39my @cmd = ($^X, '-e', '$SIG{HUP}=sub{exit}; '
40 . 'if( my $fn=shift ){ open my $fh, qq{>$fn}; print {$fh} $$; close $fh;} '
41 . 'my $buf; while (sysread(STDIN, $buf, 2048)) { '
42 . 'syswrite(STDOUT, $buf); syswrite(STDERR, $buf)}'
43);
44
45#--------------------------------------------------------------------------#
46# filehandle manipulation
47#--------------------------------------------------------------------------#
48
49sub _relayer {
50 my ($fh, $layers) = @_;
51 _debug("# requested layers (@{$layers}) to $fh\n");
52 my %seen;
53 my @unique = grep { $_ ne 'unix' and $_ ne 'perlio' and !$seen{$_}++ } @$layers;
54 _debug("# applying unique layers (@unique) to $fh\n");
55 binmode($fh, join(":", "", "raw", @unique));
56}
57
58sub _name {
59 my $glob = shift;
60 no strict 'refs'; ## no critic
61 return *{$glob}{NAME};
62}
63
64sub _open {
65 open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!";
66 _debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" );
67}
68
69sub _close {
70 close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!";
71 _debug( "# closed " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . "\n" );
72}
73
74my %dup; # cache this so STDIN stays fd0
75my %proxy_count;
76sub _proxy_std {
77 my %proxies;
78 if ( ! defined fileno STDIN ) {
79 $proxy_count{stdin}++;
80 if (defined $dup{stdin}) {
81 _open \*STDIN, "<&=" . fileno($dup{stdin});
82 _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" );
83 }
84 else {
85 _open \*STDIN, "<" . File::Spec->devnull;
86 _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" );
87 _open $dup{stdin} = IO::Handle->new, "<&=STDIN";
88 }
89 $proxies{stdin} = \*STDIN;
90 binmode(STDIN, ':utf8') if $] >= 5.008;
91 }
92 if ( ! defined fileno STDOUT ) {
93 $proxy_count{stdout}++;
94 if (defined $dup{stdout}) {
95 _open \*STDOUT, ">&=" . fileno($dup{stdout});
96 _debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" );
97 }
98 else {
99 _open \*STDOUT, ">" . File::Spec->devnull;
100 _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" );
101 _open $dup{stdout} = IO::Handle->new, ">&=STDOUT";
102 }
103 $proxies{stdout} = \*STDOUT;
104 binmode(STDOUT, ':utf8') if $] >= 5.008;
105 }
106 if ( ! defined fileno STDERR ) {
107 $proxy_count{stderr}++;
108 if (defined $dup{stderr}) {
109 _open \*STDERR, ">&=" . fileno($dup{stderr});
110 _debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" );
111 }
112 else {
113 _open \*STDERR, ">" . File::Spec->devnull;
114 _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" );
115 _open $dup{stderr} = IO::Handle->new, ">&=STDERR";
116 }
117 $proxies{stderr} = \*STDERR;
118 binmode(STDERR, ':utf8') if $] >= 5.008;
119 }
120 return %proxies;
121}
122
123sub _unproxy {
124 my (%proxies) = @_;
125 _debug( "# unproxing " . join(" ", keys %proxies) . "\n" );
126 for my $p ( keys %proxies ) {
127 $proxy_count{$p}--;
128 _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" );
129 if ( ! $proxy_count{$p} ) {
130 _close $proxies{$p};
131 _close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup
132 delete $dup{$p};
133 }
134 }
135}
136
137sub _copy_std {
138 my %handles = map { $_, IO::Handle->new } qw/stdin stdout stderr/;
139 _debug( "# copying std handles ...\n" );
140 _open $handles{stdin}, "<&STDIN";
141 _open $handles{stdout}, ">&STDOUT";
142 _open $handles{stderr}, ">&STDERR";
143 return \%handles;
144}
145
146sub _open_std {
147 my ($handles) = @_;
148 _open \*STDIN, "<&" . fileno $handles->{stdin};
149 _open \*STDOUT, ">&" . fileno $handles->{stdout};
150 _open \*STDERR, ">&" . fileno $handles->{stderr};
151}
152
153#--------------------------------------------------------------------------#
154# private subs
155#--------------------------------------------------------------------------#
156
157sub _start_tee {
158 my ($which, $stash) = @_;
159 # setup pipes
160 $stash->{$_}{$which} = IO::Handle->new for qw/tee reader/;
161 pipe $stash->{reader}{$which}, $stash->{tee}{$which};
162 _debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " "
163 . fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which})
164 . " " . fileno( $stash->{reader}{$which}) . "\n" );
165 select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush
166 # setup desired redirection for parent and child
167 $stash->{new}{$which} = $stash->{tee}{$which};
168 $stash->{child}{$which} = {
169 stdin => $stash->{reader}{$which},
170 stdout => $stash->{old}{$which},
171 stderr => $stash->{capture}{$which},
172 };
173 # flag file is used to signal the child is ready
174 $stash->{flag_files}{$which} = scalar tmpnam();
175 # execute @cmd as a separate process
176 if ( $IS_WIN32 ) {
177 eval "use Win32API::File qw/CloseHandle GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ ";
178 _debug( "# Win32API::File loaded\n") unless $@;
179 my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} );
180 _debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE();
181 if ( SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0) ) {
182 _debug( "# set no-inherit flag on $which tee\n" );
183 }
184 else {
185 _debug( "# can't disable tee handle flag inherit: " . fileLastError() . "\n");
186 }
187 _open_std( $stash->{child}{$which} );
188 $stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which});
189 # not restoring std here as it all gets redirected again shortly anyway
190 }
191 else { # use fork
192 _fork_exec( $which, $stash );
193 }
194}
195
196sub _fork_exec {
197 my ($which, $stash) = @_;
198 my $pid = fork;
199 if ( not defined $pid ) {
200 Carp::confess "Couldn't fork(): $!";
201 }
202 elsif ($pid == 0) { # child
203 _debug( "# in child process ...\n" );
204 untie *STDIN; untie *STDOUT; untie *STDERR;
205 _close $stash->{tee}{$which};
206 _debug( "# redirecting handles in child ...\n" );
207 _open_std( $stash->{child}{$which} );
208 _debug( "# calling exec on command ...\n" );
209 exec @cmd, $stash->{flag_files}{$which};
210 }
211 $stash->{pid}{$which} = $pid
212}
213
214sub _files_exist { -f $_ || return 0 for @_; return 1 }
215
216sub _wait_for_tees {
217 my ($stash) = @_;
218 my $start = time;
219 my @files = values %{$stash->{flag_files}};
220 1 until _files_exist(@files) || (time - $start > 30);
221 Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files);
222 unlink $_ for @files;
223}
224
225sub _kill_tees {
226 my ($stash) = @_;
227 if ( $IS_WIN32 ) {
228 _debug( "# closing handles with CloseHandle\n");
229 CloseHandle( GetOsFHandle($_) ) for values %{ $stash->{tee} };
230 _debug( "# waiting for subprocesses to finish\n");
231 my $start = time;
232 1 until wait == -1 || (time - $start > 30);
233 }
234 else {
235 _close $_ for values %{ $stash->{tee} };
236 waitpid $_, 0 for values %{ $stash->{pid} };
237 }
238}
239
240sub _slurp {
241 seek $_[0],0,0; local $/; return scalar readline $_[0];
242}
243
244#--------------------------------------------------------------------------#
245# _capture_tee() -- generic main sub for capturing or teeing
246#--------------------------------------------------------------------------#
247
248sub _capture_tee {
249 _debug( "# starting _capture_tee with (@_)...\n" );
250 my ($tee_stdout, $tee_stderr, $merge, $code) = @_;
251 # save existing filehandles and setup captures
252 local *CT_ORIG_STDIN = *STDIN ;
253 local *CT_ORIG_STDOUT = *STDOUT;
254 local *CT_ORIG_STDERR = *STDERR;
255 # find initial layers
256 my %layers = (
257 stdin => [PerlIO::get_layers(\*STDIN) ],
258 stdout => [PerlIO::get_layers(\*STDOUT)],
259 stderr => [PerlIO::get_layers(\*STDERR)],
260 );
261 _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
262 # bypass scalar filehandles and tied handles
263 my %localize;
264 $localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}};
265 $localize{stdout}++, local(*STDOUT) if grep { $_ eq 'scalar' } @{$layers{stdout}};
266 $localize{stderr}++, local(*STDERR) if grep { $_ eq 'scalar' } @{$layers{stderr}};
267 $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if tied *STDOUT && $] >= 5.008;
268 $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if tied *STDERR && $] >= 5.008;
269 _debug( "# localized $_\n" ) for keys %localize;
270 my %proxy_std = _proxy_std();
271 _debug( "# proxy std is @{ [%proxy_std] }\n" );
272 my $stash = { old => _copy_std() };
273 # update layers after any proxying
274 %layers = (
275 stdin => [PerlIO::get_layers(\*STDIN) ],
276 stdout => [PerlIO::get_layers(\*STDOUT)],
277 stderr => [PerlIO::get_layers(\*STDERR)],
278 );
279 _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
280 # get handles for capture and apply existing IO layers
281 $stash->{new}{$_} = $stash->{capture}{$_} = File::Temp->new for qw/stdout stderr/;
282 _debug("# will capture $_ on " .fileno($stash->{capture}{$_})."\n" ) for qw/stdout stderr/;
283 # tees may change $stash->{new}
284 _start_tee( stdout => $stash ) if $tee_stdout;
285 _start_tee( stderr => $stash ) if $tee_stderr;
286 _wait_for_tees( $stash ) if $tee_stdout || $tee_stderr;
287 # finalize redirection
288 $stash->{new}{stderr} = $stash->{new}{stdout} if $merge;
289 $stash->{new}{stdin} = $stash->{old}{stdin};
290 _debug( "# redirecting in parent ...\n" );
291 _open_std( $stash->{new} );
292 # execute user provided code
293 my $exit_code;
294 {
295 local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN
296 local *STDERR = *STDOUT if $merge; # minimize buffer mixups during $code
297 _debug( "# finalizing layers ...\n" );
298 _relayer(\*STDOUT, $layers{stdout});
299 _relayer(\*STDERR, $layers{stderr}) unless $merge;
300 _debug( "# running code $code ...\n" );
301 $code->();
302 $exit_code = $?; # save this for later
303 }
304 # restore prior filehandles and shut down tees
305 _debug( "# restoring ...\n" );
306 _open_std( $stash->{old} );
307 _close( $_ ) for values %{$stash->{old}}; # don't leak fds
308 _unproxy( %proxy_std );
309 _kill_tees( $stash ) if $tee_stdout || $tee_stderr;
310 # return captured output
311 _relayer($stash->{capture}{stdout}, $layers{stdout});
312 _relayer($stash->{capture}{stderr}, $layers{stderr}) unless $merge;
313 _debug( "# slurping captured $_ with layers: @{[PerlIO::get_layers($stash->{capture}{$_})]}\n") for qw/stdout stderr/;
314 my $got_out = _slurp($stash->{capture}{stdout});
315 my $got_err = $merge ? q() : _slurp($stash->{capture}{stderr});
316 print CT_ORIG_STDOUT $got_out if $localize{stdout} && $tee_stdout;
317 print CT_ORIG_STDERR $got_err if !$merge && $localize{stderr} && $tee_stdout;
318 $? = $exit_code;
319 _debug( "# ending _capture_tee with (@_)...\n" );
320 return $got_out if $merge;
321 return wantarray ? ($got_out, $got_err) : $got_out;
322}
323
324#--------------------------------------------------------------------------#
325# create API subroutines from [tee STDOUT flag, tee STDERR, merge flag]
326#--------------------------------------------------------------------------#
327
328my %api = (
329 capture => [0,0,0],
330 capture_merged => [0,0,1],
331 tee => [1,1,0],
332 tee_merged => [1,0,1], # don't tee STDOUT since merging
333);
334
335for my $sub ( keys %api ) {
336 my $args = join q{, }, @{$api{$sub}};
337 eval "sub $sub(&) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic
338}
339
3401;
341
342__END__
343
344=begin wikidoc
345
346= NAME
347
348Capture::Tiny - Capture STDOUT and STDERR from Perl, XS or external programs
349
350= VERSION
351
352This documentation describes version %%VERSION%%.
353
354= SYNOPSIS
355
356 use Capture::Tiny qw/capture tee capture_merged tee_merged/;
357
358 ($stdout, $stderr) = capture {
359 # your code here
360 };
361
362 ($stdout, $stderr) = tee {
363 # your code here
364 };
365
366 $merged = capture_merged {
367 # your code here
368 };
369
370 $merged = tee_merged {
371 # your code here
372 };
373
374= DESCRIPTION
375
376Capture::Tiny provides a simple, portable way to capture anything sent to
377STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or
378from an external program. Optionally, output can be teed so that it is
379captured while being passed through to the original handles. Yes, it even
380works on Windows. Stop guessing which of a dozen capturing modules to use in
381any particular situation and just use this one.
382
383This module was heavily inspired by [IO::CaptureOutput], which provides
384similar functionality without the ability to tee output and with more
385complicated code and API.
386
387= USAGE
388
389The following functions are available. None are exported by default.
390
391== capture
392
393 ($stdout, $stderr) = capture \&code;
394 $stdout = capture \&code;
395
396The {capture} function takes a code reference and returns what is sent to
397STDOUT and STDERR. In scalar context, it returns only STDOUT. If no output
398was received, returns an empty string. Regardless of context, all output is
399captured -- nothing is passed to the existing handles.
400
401It is prototyped to take a subroutine reference as an argument. Thus, it
402can be called in block form:
403
404 ($stdout, $stderr) = capture {
405 # your code here ...
406 };
407
408== capture_merged
409
410 $merged = capture_merged \&code;
411
412The {capture_merged} function works just like {capture} except STDOUT and
413STDERR are merged. (Technically, STDERR is redirected to STDOUT before
414executing the function.) If no output was received, returns an empty string.
415As with {capture} it may be called in block form.
416
417Caution: STDOUT and STDERR output in the merged result are not guaranteed to be
418properly ordered due to buffering.
419
420== tee
421
422 ($stdout, $stderr) = tee \&code;
423 $stdout = tee \&code;
424
425The {tee} function works just like {capture}, except that output is captured
426as well as passed on to the original STDOUT and STDERR. As with {capture} it
427may be called in block form.
428
429== tee_merged
430
431 $merged = tee_merged \&code;
432
433The {tee_merged} function works just like {capture_merged} except that output
434is captured as well as passed on to STDOUT. As with {capture} it may be called
435in block form.
436
437Caution: STDOUT and STDERR output in the merged result are not guaranteed to be
438properly ordered due to buffering.
439
440= LIMITATIONS
441
442== Portability
443
444Portability is a goal, not a guarantee. {tee} requires fork, except on
445Windows where {system(1, @cmd)} is used instead. Not tested on any
446particularly esoteric platforms yet.
447
448== PerlIO layers
449
450Capture::Tiny does it's best to preserve PerlIO layers such as ':utf8' or
451':crlf' when capturing. Layers should be applied to STDOUT or STDERR ~before~
452the call to {capture} or {tee}.
453
454== Closed STDIN, STDOUT or STDERR
455
456Capture::Tiny will work even if STDIN, STDOUT or STDERR have been previously
457closed. However, since they may be reopened to capture or tee output, any code
458within the captured block that depends on finding them closed will, of course,
459not find them to be closed. If they started closed, Capture::Tiny will reclose
460them again when the capture block finishes.
461
462== Scalar filehandles and STDIN, STDOUT or STDERR
463
464If STDOUT or STDERR are reopened to scalar filehandles prior to the call to
465{capture} or {tee}, then Capture::Tiny will override the output handle for the
466duration of the {capture} or {tee} call and then send captured output to the
467output handle after the capture is complete. (Requires Perl 5.8)
468
469Capture::Tiny attempts to preserve the semantics of STDIN opened to a scalar
470reference.
471
472== Tied STDIN, STDOUT or STDERR
473
474If STDOUT or STDERR are tied prior to the call to {capture} or {tee}, then
475Capture::Tiny will attempt to override the tie for the duration of the
476{capture} or {tee} call and then send captured output to the tied handle after
477the capture is complete. (Requires Perl 5.8)
478
479Capture::Tiny does not (yet) support resending utf8 encoded data to a tied
480STDOUT or STDERR handle. Characters will appear as bytes.
481
482Capture::Tiny attempts to preserve the semantics of tied STDIN, but capturing
483or teeing when STDIN is tied is currently broken on Windows.
484
485== Modifiying STDIN, STDOUT or STDERR during a capture
486
487Attempting to modify STDIN, STDOUT or STDERR ~during~ {capture} or {tee} is
488almost certainly going to cause problems. Don't do that.
489
490= BUGS
491
492Please report any bugs or feature requests using the CPAN Request Tracker.
493Bugs can be submitted through the web interface at
494[http://rt.cpan.org/Dist/Display.html?Queue=Capture-Tiny]
495
496When submitting a bug or request, please include a test-file or a patch to an
497existing test-file that illustrates the bug or desired feature.
498
499= SEE ALSO
500
501This is a selection of CPAN modules that provide some sort of output capture,
502albeit with various limitations that make them appropriate only in particular
503circumstances. I'm probably missing some. The long list is provided to show
504why I felt Capture::Tiny was necessary.
505
506* [IO::Capture]
507* [IO::Capture::Extended]
508* [IO::CaptureOutput]
509* [IPC::Capture]
510* [IPC::Cmd]
511* [IPC::Open2]
512* [IPC::Open3]
513* [IPC::Open3::Simple]
514* [IPC::Open3::Utils]
515* [IPC::Run]
516* [IPC::Run::SafeHandles]
517* [IPC::Run::Simple]
518* [IPC::Run3]
519* [IPC::System::Simple]
520* [Tee]
521* [IO::Tee]
522* [File::Tee]
523* [Filter::Handle]
524* [Tie::STDERR]
525* [Tie::STDOUT]
526* [Test::Output]
527
528= AUTHOR
529
530David A. Golden (DAGOLDEN)
531
532= COPYRIGHT AND LICENSE
533
534Copyright (c) 2009 by David A. Golden. All rights reserved.
535
536Licensed under Apache License, Version 2.0 (the "License"). You may not use
537this file except in compliance with the License. A copy of the License was
538distributed with this file or you may obtain a copy of the License from
539http://www.apache.org/licenses/LICENSE-2.0
540
541Files produced as output though the use of this software, shall not be
542considered Derivative Works, but shall be considered the original work of the
543Licensor.
544
545Unless required by applicable law or agreed to in writing, software
546distributed under the License is distributed on an "AS IS" BASIS,
547WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
548See the License for the specific language governing permissions and
549limitations under the License.
550
551=end wikidoc
552
553=cut
554