line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
916
|
|
|
916
|
|
36285554
|
use 5.006; |
|
916
|
|
|
|
|
4038
|
|
|
916
|
|
|
|
|
37506
|
|
2
|
916
|
|
|
916
|
|
4767
|
use strict; |
|
916
|
|
|
|
|
1744
|
|
|
916
|
|
|
|
|
24715
|
|
3
|
916
|
|
|
916
|
|
4763
|
use warnings; |
|
916
|
|
|
|
|
1470
|
|
|
916
|
|
|
|
|
50491
|
|
4
|
|
|
|
|
|
|
package Capture::Tiny::Extended; |
5
|
|
|
|
|
|
|
our $VERSION = '0.114'; # VERSION |
6
|
|
|
|
|
|
|
# ABSTRACT: Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras) |
7
|
916
|
|
|
916
|
|
5033
|
use Carp (); |
|
916
|
|
|
|
|
1463
|
|
|
916
|
|
|
|
|
16551
|
|
8
|
916
|
|
|
916
|
|
4666
|
use Exporter (); |
|
916
|
|
|
|
|
1735
|
|
|
916
|
|
|
|
|
11809
|
|
9
|
916
|
|
|
916
|
|
5386039
|
use IO::Handle (); |
|
916
|
|
|
|
|
8837305
|
|
|
916
|
|
|
|
|
20602
|
|
10
|
916
|
|
|
916
|
|
7960
|
use File::Spec (); |
|
916
|
|
|
|
|
1742
|
|
|
916
|
|
|
|
|
18491
|
|
11
|
916
|
|
|
916
|
|
2171910
|
use File::Temp qw/tempfile tmpnam/; |
|
916
|
|
|
|
|
25248161
|
|
|
916
|
|
|
|
|
127049
|
|
12
|
|
|
|
|
|
|
# Get PerlIO or fake it |
13
|
|
|
|
|
|
|
BEGIN { |
14
|
916
|
|
|
916
|
|
2198
|
local $@; |
15
|
916
|
|
|
|
|
8151
|
eval { require PerlIO; PerlIO->can('get_layers') } |
|
916
|
|
|
|
|
473112
|
|
16
|
916
|
50
|
|
|
|
2197
|
or *PerlIO::get_layers = sub { return () }; |
|
0
|
|
|
|
|
0
|
|
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our @ISA = qw/Exporter/; |
20
|
|
|
|
|
|
|
our @EXPORT_OK = qw/capture capture_merged tee tee_merged capture_files/; |
21
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $IS_WIN32 = $^O eq 'MSWin32'; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG}; |
26
|
|
|
|
|
|
|
my $DEBUGFH; |
27
|
|
|
|
|
|
|
open $DEBUGFH, ">&STDERR" if $DEBUG; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0}; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $TIMEOUT = 30; |
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
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
39
|
|
|
|
|
|
|
my @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
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub _relayer { |
50
|
315208
|
|
|
315208
|
|
856752
|
my ($fh, $layers) = @_; |
51
|
315208
|
|
|
|
|
1040827
|
_debug("# requested layers (@{$layers}) to $fh\n"); |
|
315208
|
|
|
|
|
4083962
|
|
52
|
315208
|
|
|
|
|
4820113
|
my %seen = ( unix => 1, perlio => 1 ); # filter these out |
53
|
315208
|
|
|
|
|
869431
|
my @unique = grep { !$seen{$_}++ } @$layers; |
|
768506
|
|
|
|
|
2847506
|
|
54
|
315208
|
|
|
|
|
1721780
|
_debug("# applying unique layers (@unique) to $fh\n"); |
55
|
315208
|
|
|
|
|
4664490
|
binmode($fh, join(":", ":raw", @unique)); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _name { |
59
|
2253248
|
|
|
2253248
|
|
3920691
|
my $glob = shift; |
60
|
916
|
|
|
916
|
|
5676
|
no strict 'refs'; ## no critic |
|
916
|
|
|
|
|
1921
|
|
|
916
|
|
|
|
|
4883128
|
|
61
|
2253248
|
|
|
|
|
2710630
|
return *{$glob}{NAME}; |
|
2253248
|
|
|
|
|
20194731
|
|
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub _open { |
65
|
891969
|
50
|
|
891969
|
|
59270499
|
open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!"; |
66
|
891969
|
50
|
|
|
|
3240722
|
_debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" ); |
|
1783938
|
|
|
|
|
5689195
|
|
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub _close { |
70
|
385600
|
50
|
|
385600
|
|
9689422
|
close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!"; |
71
|
385600
|
50
|
|
|
|
1508048
|
_debug( "# closed " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . "\n" ); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my %dup; # cache this so STDIN stays fd0 |
75
|
|
|
|
|
|
|
my %proxy_count; |
76
|
|
|
|
|
|
|
sub _proxy_std { |
77
|
88390
|
|
|
88390
|
|
168356
|
my %proxies; |
78
|
88390
|
100
|
|
|
|
307666
|
if ( ! defined fileno STDIN ) { |
79
|
16443
|
|
|
|
|
31596
|
$proxy_count{stdin}++; |
80
|
16443
|
100
|
|
|
|
49082
|
if (defined $dup{stdin}) { |
81
|
2205
|
|
|
|
|
11930
|
_open \*STDIN, "<&=" . fileno($dup{stdin}); |
82
|
2205
|
50
|
|
|
|
12389
|
_debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
else { |
85
|
14238
|
|
|
|
|
394599
|
_open \*STDIN, "<" . File::Spec->devnull; |
86
|
14238
|
50
|
|
|
|
101268
|
_debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
87
|
14238
|
|
|
|
|
241064
|
_open $dup{stdin} = IO::Handle->new, "<&=STDIN"; |
88
|
|
|
|
|
|
|
} |
89
|
16443
|
|
|
|
|
54216
|
$proxies{stdin} = \*STDIN; |
90
|
16443
|
50
|
|
|
|
144443
|
binmode(STDIN, ':utf8') if $] >= 5.008; |
91
|
|
|
|
|
|
|
} |
92
|
88390
|
100
|
|
|
|
397842
|
if ( ! defined fileno STDOUT ) { |
93
|
14238
|
|
|
|
|
37503
|
$proxy_count{stdout}++; |
94
|
14238
|
50
|
|
|
|
59762
|
if (defined $dup{stdout}) { |
95
|
0
|
|
|
|
|
0
|
_open \*STDOUT, ">&=" . fileno($dup{stdout}); |
96
|
0
|
0
|
|
|
|
0
|
_debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
else { |
99
|
14238
|
|
|
|
|
366258
|
_open \*STDOUT, ">" . File::Spec->devnull; |
100
|
14238
|
50
|
|
|
|
128708
|
_debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
101
|
14238
|
|
|
|
|
271347
|
_open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; |
102
|
|
|
|
|
|
|
} |
103
|
14238
|
|
|
|
|
47732
|
$proxies{stdout} = \*STDOUT; |
104
|
14238
|
50
|
|
|
|
126567
|
binmode(STDOUT, ':utf8') if $] >= 5.008; |
105
|
|
|
|
|
|
|
} |
106
|
88390
|
100
|
|
|
|
277135
|
if ( ! defined fileno STDERR ) { |
107
|
14238
|
|
|
|
|
33649
|
$proxy_count{stderr}++; |
108
|
14238
|
50
|
|
|
|
46787
|
if (defined $dup{stderr}) { |
109
|
0
|
|
|
|
|
0
|
_open \*STDERR, ">&=" . fileno($dup{stderr}); |
110
|
0
|
0
|
|
|
|
0
|
_debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
else { |
113
|
14238
|
|
|
|
|
364169
|
_open \*STDERR, ">" . File::Spec->devnull; |
114
|
14238
|
50
|
|
|
|
78779
|
_debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
115
|
14238
|
|
|
|
|
408914
|
_open $dup{stderr} = IO::Handle->new, ">&=STDERR"; |
116
|
|
|
|
|
|
|
} |
117
|
14238
|
|
|
|
|
46681
|
$proxies{stderr} = \*STDERR; |
118
|
14238
|
50
|
|
|
|
172997
|
binmode(STDERR, ':utf8') if $] >= 5.008; |
119
|
|
|
|
|
|
|
} |
120
|
88390
|
|
|
|
|
369696
|
return %proxies; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub _unproxy { |
124
|
86586
|
|
|
86586
|
|
463196
|
my (%proxies) = @_; |
125
|
86586
|
|
|
|
|
521933
|
_debug( "# unproxing " . join(" ", keys %proxies) . "\n" ); |
126
|
86586
|
|
|
|
|
577005
|
for my $p ( keys %proxies ) { |
127
|
44289
|
|
|
|
|
120024
|
$proxy_count{$p}--; |
128
|
44289
|
|
|
|
|
270978
|
_debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); |
129
|
44289
|
100
|
|
|
|
162330
|
if ( ! $proxy_count{$p} ) { |
130
|
42174
|
|
|
|
|
133895
|
_close $proxies{$p}; |
131
|
42174
|
50
|
|
|
|
280836
|
_close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup |
132
|
42174
|
|
|
|
|
1241657
|
delete $dup{$p}; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub _copy_std { |
138
|
88390
|
|
|
88390
|
|
183628
|
my %handles = map { $_, IO::Handle->new } qw/stdin stdout stderr/; |
|
265170
|
|
|
|
|
6340855
|
|
139
|
88390
|
|
|
|
|
2731829
|
_debug( "# copying std handles ...\n" ); |
140
|
88390
|
|
|
|
|
260794
|
_open $handles{stdin}, "<&STDIN"; |
141
|
88390
|
|
|
|
|
272441
|
_open $handles{stdout}, ">&STDOUT"; |
142
|
88390
|
|
|
|
|
412507
|
_open $handles{stderr}, ">&STDERR"; |
143
|
88390
|
|
|
|
|
416682
|
return \%handles; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub _open_std { |
147
|
174976
|
|
|
174976
|
|
515640
|
my ($handles) = @_; |
148
|
174976
|
|
|
|
|
2654605
|
_open \*STDIN, "<&" . fileno $handles->{stdin}; |
149
|
174976
|
|
|
|
|
1331450
|
_open \*STDOUT, ">&" . fileno $handles->{stdout}; |
150
|
174976
|
|
|
|
|
981929
|
_open \*STDERR, ">&" . fileno $handles->{stderr}; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
154
|
|
|
|
|
|
|
# private subs |
155
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _start_tee { |
158
|
41855
|
|
|
41855
|
|
328203
|
my ($which, $stash) = @_; |
159
|
|
|
|
|
|
|
# setup pipes |
160
|
41855
|
|
|
|
|
2136454
|
$stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; |
161
|
41855
|
|
|
|
|
11094047
|
pipe $stash->{reader}{$which}, $stash->{tee}{$which}; |
162
|
41855
|
|
|
|
|
921857
|
_debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " " |
163
|
|
|
|
|
|
|
. fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which}) |
164
|
|
|
|
|
|
|
. " " . fileno( $stash->{reader}{$which}) . "\n" ); |
165
|
41855
|
|
|
|
|
1361656
|
select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush |
166
|
|
|
|
|
|
|
# setup desired redirection for parent and child |
167
|
41855
|
|
|
|
|
191986
|
$stash->{new}{$which} = $stash->{tee}{$which}; |
168
|
41855
|
|
|
|
|
434068
|
$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
|
41855
|
|
|
|
|
1903988
|
$stash->{flag_files}{$which} = scalar tmpnam(); |
175
|
|
|
|
|
|
|
# execute @cmd as a separate process |
176
|
41855
|
50
|
|
|
|
29662314
|
if ( $IS_WIN32 ) { |
177
|
0
|
|
|
|
|
0
|
local $@; |
178
|
0
|
|
|
|
|
0
|
eval "use Win32API::File qw/CloseHandle GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ "; |
179
|
0
|
0
|
|
|
|
0
|
_debug( "# Win32API::File loaded\n") unless $@; |
180
|
0
|
|
|
|
|
0
|
my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} ); |
181
|
0
|
0
|
0
|
|
|
0
|
_debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE(); |
182
|
0
|
0
|
|
|
|
0
|
if ( SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0) ) { |
183
|
0
|
|
|
|
|
0
|
_debug( "# set no-inherit flag on $which tee\n" ); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
else { |
186
|
0
|
|
|
|
|
0
|
_debug( "# can't disable tee handle flag inherit: " . fileLastError() . "\n"); |
187
|
|
|
|
|
|
|
} |
188
|
0
|
|
|
|
|
0
|
_open_std( $stash->{child}{$which} ); |
189
|
0
|
|
|
|
|
0
|
$stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which}); |
190
|
|
|
|
|
|
|
# not restoring std here as it all gets redirected again shortly anyway |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
else { # use fork |
193
|
41855
|
|
|
|
|
215054
|
_fork_exec( $which, $stash ); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub _fork_exec { |
198
|
41855
|
|
|
41855
|
|
78378
|
my ($which, $stash) = @_; |
199
|
41855
|
|
|
|
|
136928610
|
my $pid = fork; |
200
|
41855
|
50
|
|
|
|
1681537
|
if ( not defined $pid ) { |
|
|
100
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
Carp::confess "Couldn't fork(): $!"; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
elsif ($pid == 0) { # child |
204
|
902
|
|
|
|
|
153237
|
_debug( "# in child process ...\n" ); |
205
|
902
|
|
|
|
|
44072
|
untie *STDIN; untie *STDOUT; untie *STDERR; |
|
902
|
|
|
|
|
28588
|
|
|
902
|
|
|
|
|
16603
|
|
206
|
902
|
|
|
|
|
86266
|
_close $stash->{tee}{$which}; |
207
|
902
|
|
|
|
|
26957
|
_debug( "# redirecting handles in child ...\n" ); |
208
|
902
|
|
|
|
|
34140
|
_open_std( $stash->{child}{$which} ); |
209
|
902
|
|
|
|
|
19470
|
_debug( "# calling exec on command ...\n" ); |
210
|
902
|
|
|
|
|
0
|
exec @cmd, $stash->{flag_files}{$which}; |
211
|
|
|
|
|
|
|
} |
212
|
40953
|
|
|
|
|
6172042
|
$stash->{pid}{$which} = $pid |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
1648132196
|
|
100
|
1648132196
|
|
29818225143
|
sub _files_exist { -f $_ || return 0 for @_; return 1 } |
|
42302
|
|
|
|
|
320187
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub _wait_for_tees { |
218
|
21151
|
|
|
21151
|
|
101462
|
my ($stash) = @_; |
219
|
21151
|
|
|
|
|
82694
|
my $start = time; |
220
|
21151
|
|
|
|
|
45490
|
my @files = values %{$stash->{flag_files}}; |
|
21151
|
|
|
|
|
1595870
|
|
221
|
21151
|
50
|
|
|
|
412806
|
my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} |
222
|
|
|
|
|
|
|
? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; |
223
|
21151
|
|
33
|
|
|
315123
|
1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); |
|
|
|
66
|
|
|
|
|
224
|
21151
|
50
|
|
|
|
150952
|
Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); |
225
|
21151
|
|
|
|
|
25668269
|
unlink $_ for @files; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub _kill_tees { |
229
|
21151
|
|
|
21151
|
|
58567
|
my ($stash) = @_; |
230
|
21151
|
50
|
|
|
|
243521
|
if ( $IS_WIN32 ) { |
231
|
0
|
|
|
|
|
0
|
_debug( "# closing handles with CloseHandle\n"); |
232
|
0
|
|
|
|
|
0
|
CloseHandle( GetOsFHandle($_) ) for values %{ $stash->{tee} }; |
|
0
|
|
|
|
|
0
|
|
233
|
0
|
|
|
|
|
0
|
_debug( "# waiting for subprocesses to finish\n"); |
234
|
0
|
|
|
|
|
0
|
my $start = time; |
235
|
0
|
|
0
|
|
|
0
|
1 until wait == -1 || (time - $start > 30); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
else { |
238
|
21151
|
|
|
|
|
48089
|
_close $_ for values %{ $stash->{tee} }; |
|
21151
|
|
|
|
|
250854
|
|
239
|
21151
|
|
|
|
|
73191
|
waitpid $_, 0 for values %{ $stash->{pid} }; |
|
21151
|
|
|
|
|
34828656
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _slurp { |
244
|
156702
|
|
|
156702
|
|
1691090
|
seek $_[0],0,0; local $/; return scalar readline $_[0]; |
|
156702
|
|
|
|
|
976022
|
|
|
156702
|
|
|
|
|
7359160
|
|
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
248
|
|
|
|
|
|
|
# _capture_tee() -- generic main sub for capturing or teeing |
249
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub _capture_tee { |
252
|
88390
|
|
|
88390
|
|
1047125
|
_debug( "# starting _capture_tee with (@_)...\n" ); |
253
|
88390
|
|
|
|
|
571489
|
my ($tee_stdout, $tee_stderr, $merge, $code, $files) = @_; |
254
|
|
|
|
|
|
|
# save existing filehandles and setup captures |
255
|
88390
|
|
|
|
|
582347
|
local *CT_ORIG_STDIN = *STDIN ; |
256
|
88390
|
|
|
|
|
456505
|
local *CT_ORIG_STDOUT = *STDOUT; |
257
|
88390
|
|
|
|
|
375987
|
local *CT_ORIG_STDERR = *STDERR; |
258
|
|
|
|
|
|
|
# find initial layers |
259
|
88390
|
|
|
|
|
1793124
|
my %layers = ( |
260
|
|
|
|
|
|
|
stdin => [PerlIO::get_layers(\*STDIN) ], |
261
|
|
|
|
|
|
|
stdout => [PerlIO::get_layers(\*STDOUT)], |
262
|
|
|
|
|
|
|
stderr => [PerlIO::get_layers(\*STDERR)], |
263
|
|
|
|
|
|
|
); |
264
|
88390
|
|
|
|
|
639592
|
_debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
|
265170
|
|
|
|
|
1450927
|
|
265
|
|
|
|
|
|
|
# bypass scalar filehandles and tied handles |
266
|
88390
|
|
|
|
|
195212
|
my %localize; |
267
|
88390
|
100
|
|
|
|
154967
|
$localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}}; |
|
155423
|
|
|
|
|
675006
|
|
|
88390
|
|
|
|
|
282129
|
|
268
|
88390
|
100
|
|
|
|
180228
|
$localize{stdout}++, local(*STDOUT) if grep { $_ eq 'scalar' } @{$layers{stdout}}; |
|
182312
|
|
|
|
|
613983
|
|
|
88390
|
|
|
|
|
250121
|
|
269
|
88390
|
100
|
|
|
|
156975
|
$localize{stderr}++, local(*STDERR) if grep { $_ eq 'scalar' } @{$layers{stderr}}; |
|
182282
|
|
|
|
|
521827
|
|
|
88390
|
|
|
|
|
216891
|
|
270
|
88390
|
100
|
66
|
|
|
540341
|
$localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if tied *STDOUT && $] >= 5.008; |
271
|
88390
|
100
|
66
|
|
|
423495
|
$localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if tied *STDERR && $] >= 5.008; |
272
|
88390
|
|
|
|
|
472944
|
_debug( "# localized $_\n" ) for keys %localize; |
273
|
88390
|
|
|
|
|
327846
|
my %proxy_std = _proxy_std(); |
274
|
88390
|
|
|
|
|
169086
|
_debug( "# proxy std is @{ [%proxy_std] }\n" ); |
|
88390
|
|
|
|
|
537745
|
|
275
|
88390
|
|
|
|
|
386259
|
my $stash = { old => _copy_std() }; |
276
|
|
|
|
|
|
|
# update layers after any proxying |
277
|
88390
|
|
|
|
|
2249898
|
%layers = ( |
278
|
|
|
|
|
|
|
stdin => [PerlIO::get_layers(\*STDIN) ], |
279
|
|
|
|
|
|
|
stdout => [PerlIO::get_layers(\*STDOUT)], |
280
|
|
|
|
|
|
|
stderr => [PerlIO::get_layers(\*STDERR)], |
281
|
|
|
|
|
|
|
); |
282
|
88390
|
|
|
|
|
550110
|
_debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
|
265170
|
|
|
|
|
1377859
|
|
283
|
|
|
|
|
|
|
# get handles for capture and apply existing IO layers |
284
|
88390
|
|
|
|
|
413830
|
$stash->{new}{$_} = $stash->{capture}{$_} = _capture_file( $_, $files ) for qw/stdout stderr/; |
285
|
88390
|
|
|
|
|
46599705
|
_debug("# will capture $_ on " .fileno($stash->{capture}{$_})."\n" ) for qw/stdout stderr/; |
286
|
|
|
|
|
|
|
# tees may change $stash->{new} |
287
|
88390
|
100
|
|
|
|
400212
|
_start_tee( stdout => $stash ) if $tee_stdout; |
288
|
87849
|
100
|
|
|
|
2406876
|
_start_tee( stderr => $stash ) if $tee_stderr; |
289
|
87488
|
100
|
66
|
|
|
2939822
|
_wait_for_tees( $stash ) if $tee_stdout || $tee_stderr; |
290
|
|
|
|
|
|
|
# finalize redirection |
291
|
87488
|
100
|
|
|
|
369504
|
$stash->{new}{stderr} = $stash->{new}{stdout} if $merge; |
292
|
87488
|
|
|
|
|
1874161
|
$stash->{new}{stdin} = $stash->{old}{stdin}; |
293
|
87488
|
|
|
|
|
253794
|
_debug( "# redirecting in parent ...\n" ); |
294
|
87488
|
|
|
|
|
576987
|
_open_std( $stash->{new} ); |
295
|
|
|
|
|
|
|
# execute user provided code |
296
|
87488
|
|
|
|
|
220690
|
my ($exit_code, $inner_error, $outer_error, @user_code_result); |
297
|
|
|
|
|
|
|
{ |
298
|
87488
|
100
|
|
|
|
148514
|
local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN |
|
87488
|
|
|
|
|
330062
|
|
299
|
87488
|
100
|
|
|
|
293486
|
local *STDERR = *STDOUT if $merge; # minimize buffer mixups during $code |
300
|
87488
|
|
|
|
|
274684
|
_debug( "# finalizing layers ...\n" ); |
301
|
87488
|
|
|
|
|
521888
|
_relayer(\*STDOUT, $layers{stdout}); |
302
|
87488
|
100
|
|
|
|
404558
|
_relayer(\*STDERR, $layers{stderr}) unless $merge; |
303
|
87488
|
|
|
|
|
382229
|
_debug( "# running code $code ...\n" ); |
304
|
87488
|
|
|
|
|
144810
|
local $@; |
305
|
87488
|
|
|
|
|
196616
|
@user_code_result = eval { |
306
|
87488
|
|
|
|
|
1177552
|
my @res = $code->(); |
307
|
86585
|
|
|
|
|
444926926
|
$inner_error = $@; |
308
|
86585
|
|
|
|
|
771029
|
return @res; |
309
|
|
|
|
|
|
|
}; |
310
|
86586
|
|
|
|
|
782203
|
$exit_code = $?; # save this for later |
311
|
86586
|
|
|
|
|
643881
|
$outer_error = $@; # save this for later |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
# restore prior filehandles and shut down tees |
314
|
86586
|
|
|
|
|
376053
|
_debug( "# restoring ...\n" ); |
315
|
86586
|
|
|
|
|
971744
|
_open_std( $stash->{old} ); |
316
|
86586
|
|
|
|
|
223371
|
_close( $_ ) for values %{$stash->{old}}; # don't leak fds |
|
86586
|
|
|
|
|
969864
|
|
317
|
86586
|
|
|
|
|
914612
|
_unproxy( %proxy_std ); |
318
|
86586
|
100
|
66
|
|
|
1763131
|
_kill_tees( $stash ) if $tee_stdout || $tee_stderr; |
319
|
|
|
|
|
|
|
# return captured output |
320
|
86586
|
|
|
|
|
812720
|
_relayer($stash->{capture}{stdout}, $layers{stdout}); |
321
|
86586
|
100
|
|
|
|
625982
|
_relayer($stash->{capture}{stderr}, $layers{stderr}) unless $merge; |
322
|
86586
|
|
|
|
|
556245
|
_debug( "# slurping captured $_ with layers: @{[PerlIO::get_layers($stash->{capture}{$_})]}\n") for qw/stdout stderr/; |
|
173172
|
|
|
|
|
2408166
|
|
323
|
86586
|
|
|
|
|
580195
|
my $got_out = _slurp($stash->{capture}{stdout}); |
324
|
86586
|
100
|
|
|
|
486405
|
my $got_err = $merge ? q() : _slurp($stash->{capture}{stderr}); |
325
|
86586
|
50
|
66
|
|
|
517625
|
print CT_ORIG_STDOUT $got_out if $localize{stdout} && $tee_stdout; |
326
|
86586
|
50
|
100
|
|
|
897895
|
print CT_ORIG_STDERR $got_err if !$merge && $localize{stderr} && $tee_stdout; |
|
|
|
66
|
|
|
|
|
327
|
86586
|
|
|
|
|
249956
|
$? = $exit_code; |
328
|
86586
|
100
|
|
|
|
253831
|
$@ = $inner_error if $inner_error; |
329
|
86586
|
100
|
|
|
|
254733
|
die $outer_error if $outer_error; |
330
|
86585
|
|
|
|
|
730201
|
_debug( "# ending _capture_tee with (@_)...\n" ); |
331
|
86585
|
50
|
|
|
|
818941
|
return wantarray ? ($got_out, @user_code_result) : $got_out if $merge; |
|
|
100
|
|
|
|
|
|
332
|
70115
|
100
|
|
|
|
2790542
|
return wantarray ? ($got_out, $got_err, @user_code_result) : $got_out; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
336
|
|
|
|
|
|
|
# capture to files |
337
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub _capture_file { |
340
|
176780
|
|
|
176780
|
|
74521194
|
my ( $target, $files ) = @_; |
341
|
|
|
|
|
|
|
|
342
|
176780
|
50
|
|
|
|
2676877
|
return File::Temp->new if !$files->{$target}; |
343
|
|
|
|
|
|
|
|
344
|
0
|
0
|
0
|
|
|
0
|
Carp::confess "$target file '$files->{$target}' already exists, set clobber => 1 to override" |
345
|
|
|
|
|
|
|
if $files->{new_files} and _files_exist( $files->{$target} ); |
346
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
0
|
my $mode = "+>>"; |
348
|
0
|
0
|
|
|
|
0
|
$mode = "+>" if $files->{clobber}; |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
0
|
my $fh = Symbol::gensym; |
351
|
0
|
|
|
|
|
0
|
_open $fh, "$mode$files->{$target}"; |
352
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
0
|
return $fh; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
0
|
|
|
0
|
1
|
0
|
sub capture_files { return { @_ }; } |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
359
|
|
|
|
|
|
|
# create API subroutines from [tee STDOUT flag, tee STDERR, merge flag] |
360
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
my %api = ( |
363
|
|
|
|
|
|
|
capture => [0,0,0], |
364
|
|
|
|
|
|
|
capture_merged => [0,0,1], |
365
|
|
|
|
|
|
|
tee => [1,1,0], |
366
|
|
|
|
|
|
|
tee_merged => [1,0,1], # don't tee STDOUT since merging |
367
|
|
|
|
|
|
|
); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
for my $sub ( keys %api ) { |
370
|
|
|
|
|
|
|
my $args = join q{, }, @{$api{$sub}}; |
371
|
51577
|
|
|
51577
|
0
|
272783181
|
eval "sub $sub(&;\$) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic |
|
51577
|
|
|
14760
|
0
|
738187
|
|
|
14760
|
|
|
20163
|
0
|
64452923
|
|
|
14760
|
|
|
1890
|
0
|
115818
|
|
|
20163
|
|
|
|
|
1787887
|
|
|
20163
|
|
|
|
|
177136
|
|
|
1890
|
|
|
|
|
99044
|
|
|
1890
|
|
|
|
|
17817
|
|
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
1; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=pod |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=head1 NAME |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
Capture::Tiny::Extended - Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras) |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head1 VERSION |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
version 0.114 |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head1 SYNOPSIS |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged/; |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# capture return values |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my ($stdout, $stderr, @return) = capture { |
395
|
|
|
|
|
|
|
# your code here |
396
|
|
|
|
|
|
|
return system( 'ls' ); |
397
|
|
|
|
|
|
|
}; |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
($merged, @return) = capture_merged { |
400
|
|
|
|
|
|
|
# your code here |
401
|
|
|
|
|
|
|
return system( 'ls' ); |
402
|
|
|
|
|
|
|
}; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# or use explicit capture files |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
($stdout, $stderr, @return) = capture( |
407
|
|
|
|
|
|
|
sub { # your code here }, |
408
|
|
|
|
|
|
|
{ stdout => 'stdout.log' } |
409
|
|
|
|
|
|
|
); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# or with sugar |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged capture_files/; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
($stdout, $stderr, @return) = capture { |
416
|
|
|
|
|
|
|
# your code here |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
capture_files ( |
419
|
|
|
|
|
|
|
stdout => 'stdout.log', |
420
|
|
|
|
|
|
|
stderr => 'stderr.log', |
421
|
|
|
|
|
|
|
); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head1 DESCRIPTION |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Capture::Tiny::Extended is a fork of L. It is functionally |
426
|
|
|
|
|
|
|
identical with the parent module, except for the differences documented in this |
427
|
|
|
|
|
|
|
POD. Please see the documentation of L for details on standard |
428
|
|
|
|
|
|
|
usage. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Please note that this can be considered an experimental module in some respects. |
431
|
|
|
|
|
|
|
I am not as experienced with the subject matter (and in general) as David Golden |
432
|
|
|
|
|
|
|
and mostly implemented these features here because i needed them fast and did |
433
|
|
|
|
|
|
|
not have the time to spare to wait for them to get into L. If you |
434
|
|
|
|
|
|
|
need capture functionality for mission-critical parts, consider whether |
435
|
|
|
|
|
|
|
L might be enough for the job. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Of course I will however make all efforts to make this as stable and useful as |
438
|
|
|
|
|
|
|
possible by keeping it up-to-date (as my time permits) with changes and bugfixes |
439
|
|
|
|
|
|
|
applied to L, as well as responding and addressing and change |
440
|
|
|
|
|
|
|
requests or bug reports for this module. |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=for Pod::Coverage capture capture_merged tee tee_merged |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head1 DIFFERENCES |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 Capturing Return Values |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
When executing code within a capture you sometimes want to also keep the return |
449
|
|
|
|
|
|
|
value, for example when capturing a system() call. In Capture::Tiny this has to |
450
|
|
|
|
|
|
|
be done like this: |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
use Capture::Tiny 'capture'; |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
my $res; |
455
|
|
|
|
|
|
|
my ( $out, $err ) = capture { |
456
|
|
|
|
|
|
|
$res = system( 'ls' ); |
457
|
|
|
|
|
|
|
}; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Capture::Tiny::Extended automatically captures return values and returns them |
460
|
|
|
|
|
|
|
after the second return value (or first if you're using the merged functions). |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture { system( 'ls' ) }; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head2 Teeing In Realtime |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
Sometimes you want to use Capture::Tiny to capture any and all output of an |
469
|
|
|
|
|
|
|
action and dump it into a log file, while also displaying it on the screen and |
470
|
|
|
|
|
|
|
then post-process the results later on (for example for sending status mails). |
471
|
|
|
|
|
|
|
The only way to do this with Capture::Tiny is code like this: |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
use Capture::Tiny 'capture'; |
474
|
|
|
|
|
|
|
use File::Slurp; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $res; |
477
|
|
|
|
|
|
|
my ( $out, $err ) = capture { |
478
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
479
|
|
|
|
|
|
|
$res = system( 'long_running_program' ); |
480
|
|
|
|
|
|
|
}; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
file_write 'out.log', $out; |
483
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
This has a very big disadvantage. If the long-running program runs too long, and |
486
|
|
|
|
|
|
|
the perl script is started by something like crontab there is no way for you to |
487
|
|
|
|
|
|
|
get at the log output. You will have to wait for it to complete before the |
488
|
|
|
|
|
|
|
captured output is written to the file. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Capture::Tiny::Extended gives you the option to provide filenames for it to use |
491
|
|
|
|
|
|
|
as capture buffers. This means the output from the captured code will appear on |
492
|
|
|
|
|
|
|
the screen and in the file in realtime, and will afterwards be available to your |
493
|
|
|
|
|
|
|
Perl script in the variables returned by the capture function: |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture( |
498
|
|
|
|
|
|
|
sub { |
499
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
500
|
|
|
|
|
|
|
return system( 'long_running_program' ); |
501
|
|
|
|
|
|
|
}, |
502
|
|
|
|
|
|
|
{ |
503
|
|
|
|
|
|
|
stdout => 'out.log', |
504
|
|
|
|
|
|
|
stderr => 'err.log', |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
); |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=head2 capture_files |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
Since using hashes in that way breaks a bit of the syntax magic of the capture |
513
|
|
|
|
|
|
|
functions (or makes them harder to read), there exists a sugar function to take |
514
|
|
|
|
|
|
|
the file arguments and pass it on to the capture functions: |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw( capture capture_files ); |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture { |
519
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
520
|
|
|
|
|
|
|
return system( 'long_running_program' ); |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
capture_files { |
523
|
|
|
|
|
|
|
stdout => 'out.log', |
524
|
|
|
|
|
|
|
stderr => 'err.log', |
525
|
|
|
|
|
|
|
}; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 Capture File Mode Options |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
For purposes of avoiding data loss, the default behavior is to append to the |
532
|
|
|
|
|
|
|
specified files. The key 'new_files' can be set to a true value on the extra |
533
|
|
|
|
|
|
|
file hash parameter to instruct Capture::Tiny::Extended to attempt to make |
534
|
|
|
|
|
|
|
files. It will die however if the specified files already exist. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
my $out = capture_merged( |
539
|
|
|
|
|
|
|
sub { system( 'ls' ) }, |
540
|
|
|
|
|
|
|
{ stdout => 'out.log', new_files => 1 } |
541
|
|
|
|
|
|
|
); |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
If existing files should always be overwritten, no matter what, the key |
544
|
|
|
|
|
|
|
'clobber' can be set instead: |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
my $out = capture_merged( |
549
|
|
|
|
|
|
|
sub { system( 'ls' ) }, |
550
|
|
|
|
|
|
|
{ stdout => 'out.log', clobber => 1 } |
551
|
|
|
|
|
|
|
); |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head1 WHY A FORK? |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
The realtime teeing feature was very important for one of my current projects |
556
|
|
|
|
|
|
|
and i needed it on CPAN to be able to easily distribute it to many systems. |
557
|
|
|
|
|
|
|
I had provided a patch for the return value capturing on Github to David Golden |
558
|
|
|
|
|
|
|
a long while ago, but due to being busy with real life, family and more |
559
|
|
|
|
|
|
|
important projects than this he was not able to find time to proof and integrate |
560
|
|
|
|
|
|
|
it and in the foreseeable future won't be able to either. At the same time i |
561
|
|
|
|
|
|
|
lack the Perl file handle, descriptor and layer chops to take full |
562
|
|
|
|
|
|
|
responsibility for Capture::Tiny itself. Usually i would have just written a |
563
|
|
|
|
|
|
|
subclass of the original, but since Capture::Tiny is written in functional style |
564
|
|
|
|
|
|
|
this was not possible. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
As such a fork seemed to be the best option to get these features out there. I'd |
567
|
|
|
|
|
|
|
be more than happy to see them integrated into C::T someday and will keep my git |
568
|
|
|
|
|
|
|
repository in such a state as to make this as easy as possible. (Lots of |
569
|
|
|
|
|
|
|
rebasing.) |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
Capture::Tiny is an invaluable tool that uses practically indecent amounts of |
574
|
|
|
|
|
|
|
creativity to solve decidedly nontrivial problems and circumvents many cliffs |
575
|
|
|
|
|
|
|
the ordinary coder (and most certainly me) would inevitably crash against. |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
Many thanks to David Golden for taking the time and braving all those traps of |
578
|
|
|
|
|
|
|
insanity to create Capture::Tiny. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head1 SUPPORT |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=head2 Bugs / Feature Requests |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Please report any bugs or feature requests by email to C, or through |
587
|
|
|
|
|
|
|
the web interface at L. You will be automatically notified of any |
588
|
|
|
|
|
|
|
progress on the request by the system. |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 Source Code |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
This is open source software. The code repository is available for |
593
|
|
|
|
|
|
|
public review and contribution under the terms of the license. |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
L |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
git clone https://github.com/wchristian/capture-tiny |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head1 AUTHORS |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=over 4 |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=item * |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
Christian Walde |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=item * |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
David Golden |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=back |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
This software is Copyright (c) 2009 by David Golden. |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
This is free software, licensed under: |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
The Apache License, Version 2.0, January 2004 |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=cut |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
__END__ |