1use strict;
2use warnings;
3
4package IO::CaptureOutput;
5# ABSTRACT: (DEPRECATED) capture STDOUT and STDERR from Perl code, subprocesses or XS
6
7our $VERSION = '1.1105';
8
9use vars qw/@ISA @EXPORT_OK %EXPORT_TAGS $CarpLevel/;
10use Exporter;
11use Carp qw/croak/;
12@ISA = 'Exporter';
13@EXPORT_OK = qw/capture capture_exec qxx capture_exec_combined qxy/;
14%EXPORT_TAGS = (all => \@EXPORT_OK);
15$CarpLevel = 0; # help capture report errors at the right level
16
17sub _capture (&@) { ## no critic
18    my ($code, $output, $error, $output_file, $error_file) = @_;
19
20    # check for valid combinations of input
21    {
22      local $Carp::CarpLevel = 1;
23      my $error = _validate($output, $error, $output_file, $error_file);
24      croak $error if $error;
25    }
26
27    # if either $output or $error are defined, then we need a variable for
28    # results; otherwise we only capture to files and don't waste memory
29    if ( defined $output || defined $error ) {
30      for ($output, $error) {
31          $_ = \do { my $s; $s = ''} unless ref $_;
32          $$_ = '' if $_ != \undef && !defined($$_);
33      }
34    }
35
36    # merge if same refs for $output and $error or if both are undef --
37    # i.e. capture \&foo, undef, undef, $merged_file
38    # this means capturing into separate files *requires* at least one
39    # capture variable
40    my $should_merge =
41      (defined $error && defined $output && $output == $error) ||
42      ( !defined $output && !defined $error ) ||
43      0;
44
45    my ($capture_out, $capture_err);
46
47    # undef means capture anonymously; anything other than \undef means
48    # capture to that ref; \undef means skip capture
49    if ( !defined $output || $output != \undef ) {
50        $capture_out = IO::CaptureOutput::_proxy->new(
51            'STDOUT', $output, undef, $output_file
52        );
53    }
54    if ( !defined $error || $error != \undef ) {
55        $capture_err = IO::CaptureOutput::_proxy->new(
56            'STDERR', $error, ($should_merge ? 'STDOUT' : undef), $error_file
57        );
58    }
59
60    # now that output capture is setup, call the subroutine
61    # results get read when IO::CaptureOutput::_proxy objects go out of scope
62    &$code();
63}
64
65# Extra indirection for symmetry with capture_exec, etc.  Gets error reporting
66# to the right level
67sub capture (&@) { ## no critic
68    return &_capture;
69}
70
71sub capture_exec {
72    my @args = @_;
73    my ($output, $error);
74    my $exit = _capture sub { system _shell_quote(@args) }, \$output, \$error;
75    my $success = ($exit == 0 ) ? 1 : 0 ;
76    $? = $exit;
77    return wantarray ? ($output, $error, $success, $exit) : $output;
78}
79
80*qxx = \&capture_exec;
81
82sub capture_exec_combined {
83    my @args = @_;
84    my $output;
85    my $exit = _capture sub { system _shell_quote(@args) }, \$output, \$output;
86    my $success = ($exit == 0 ) ? 1 : 0 ;
87    $? = $exit;
88    return wantarray ? ($output, $success, $exit) : $output;
89}
90
91*qxy = \&capture_exec_combined;
92
93# extra quoting required on Win32 systems
94*_shell_quote = ($^O =~ /MSWin32/) ? \&_shell_quote_win32 : sub {@_};
95sub _shell_quote_win32 {
96    my @args;
97    for (@_) {
98        if (/[ \"]/) { # TODO: check if ^ requires escaping
99            (my $escaped = $_) =~ s/([\"])/\\$1/g;
100            push @args, '"' . $escaped . '"';
101            next;
102        }
103        push @args, $_
104    }
105    return @args;
106}
107
108# detect errors and return an error message or empty string;
109sub _validate {
110    my ($output, $error, $output_file, $error_file) = @_;
111
112    # default to "ok"
113    my $msg = q{};
114
115    # \$out, \$out, $outfile, $errfile
116    if (    defined $output && defined $error
117        &&  defined $output_file && defined $error_file
118        &&  $output == $error
119        &&  $output != \undef
120        &&  $output_file ne $error_file
121    ) {
122      $msg = "Merged STDOUT and STDERR, but specified different output and error files";
123    }
124    # undef, undef, $outfile, $errfile
125    elsif ( !defined $output && !defined $error
126        &&  defined $output_file && defined $error_file
127        &&  $output_file ne $error_file
128    ) {
129      $msg = "Merged STDOUT and STDERR, but specified different output and error files";
130    }
131
132    return $msg;
133}
134
135# Captures everything printed to a filehandle for the lifetime of the object
136# and then transfers it to a scalar reference
137package IO::CaptureOutput::_proxy;
138use File::Temp 0.16 'tempfile';
139use File::Basename qw/basename/;
140use Symbol qw/gensym qualify qualify_to_ref/;
141use Carp;
142
143sub _is_wperl { $^O eq 'MSWin32' && basename($^X) eq 'wperl.exe' }
144
145sub new {
146    my $class = shift;
147    my ($orig_fh, $capture_var, $merge_fh, $capture_file) = @_;
148    $orig_fh       = qualify($orig_fh);         # e.g. main::STDOUT
149    my $fhref = qualify_to_ref($orig_fh);  # e.g. \*STDOUT
150
151    # Duplicate the filehandle
152    my $saved_fh;
153    {
154        no strict 'refs'; ## no critic - needed for 5.005
155        if ( defined fileno($orig_fh) && ! _is_wperl() ) {
156            $saved_fh = gensym;
157            open $saved_fh, ">&$orig_fh" or croak "Can't redirect <$orig_fh> - $!";
158        }
159    }
160
161    # Create replacement filehandle if not merging
162    my ($newio_fh, $newio_file);
163    if ( ! $merge_fh ) {
164        $newio_fh = gensym;
165        if ($capture_file) {
166            $newio_file = $capture_file;
167        } else {
168            (undef, $newio_file) = tempfile;
169        }
170        open $newio_fh, "+>$newio_file" or croak "Can't write temp file for $orig_fh - $!";
171    }
172    else {
173        $newio_fh = qualify($merge_fh);
174    }
175
176    # Redirect (or merge)
177    {
178        no strict 'refs'; ## no critic -- needed for 5.005
179        open $fhref, ">&".fileno($newio_fh) or croak "Can't redirect $orig_fh - $!";
180    }
181
182    bless [$$, $orig_fh, $saved_fh, $capture_var, $newio_fh, $newio_file, $capture_file], $class;
183}
184
185sub DESTROY {
186    my $self = shift;
187
188    my ($pid, $orig_fh, $saved_fh, $capture_var, $newio_fh,
189      $newio_file, $capture_file) = @$self;
190    return unless $pid eq $$; # only cleanup in the process that is capturing
191
192    # restore the original filehandle
193    my $fh_ref = Symbol::qualify_to_ref($orig_fh);
194    select((select ($fh_ref), $|=1)[0]);
195    if (defined $saved_fh) {
196        open $fh_ref, ">&". fileno($saved_fh) or croak "Can't restore $orig_fh - $!";
197    }
198    else {
199        close $fh_ref;
200    }
201
202    # transfer captured data to the scalar reference if we didn't merge
203    # $newio_file is undef if this file handle is merged to another
204    if (ref $capture_var && $newio_file) {
205        # some versions of perl complain about reading from fd 1 or 2
206        # which could happen if STDOUT and STDERR were closed when $newio
207        # was opened, so we just squelch warnings here and continue
208        local $^W;
209        seek $newio_fh, 0, 0;
210        $$capture_var = do {local $/; <$newio_fh>};
211    }
212    close $newio_fh if $newio_file;
213
214    # Cleanup
215    return unless defined $newio_file && -e $newio_file;
216    return if $capture_file; # the "temp" file was explicitly named
217    unlink $newio_file or carp "Couldn't remove temp file '$newio_file' - $!";
218}
219
2201;
221
222__END__
223
224=pod
225
226=encoding UTF-8
227
228=head1 NAME
229
230IO::CaptureOutput - (DEPRECATED) capture STDOUT and STDERR from Perl code, subprocesses or XS
231
232=head1 VERSION
233
234version 1.1105
235
236=head1 SYNOPSIS
237
238    use IO::CaptureOutput qw(capture qxx qxy);
239
240    # STDOUT and STDERR separately
241    capture { noisy_sub(@args) } \$stdout, \$stderr;
242
243    # STDOUT and STDERR together
244    capture { noisy_sub(@args) } \$combined, \$combined;
245
246    # STDOUT and STDERR from external command
247    ($stdout, $stderr, $success) = qxx( @cmd );
248
249    # STDOUT and STDERR together from external command
250    ($combined, $success) = qxy( @cmd );
251
252=head1 DESCRIPTION
253
254B<This module is no longer recommended by the maintainer> - see
255L<Capture::Tiny> instead.
256
257This module provides routines for capturing STDOUT and STDERR from perl
258subroutines, forked system calls (e.g. C<system()>, C<fork()>) and from XS
259or C modules.
260
261=head1 NAME
262
263=head1 FUNCTIONS
264
265The following functions will be exported on demand.
266
267=head2 capture()
268
269    capture \&subroutine, \$stdout, \$stderr;
270
271Captures everything printed to C<STDOUT> and C<STDERR> for the duration of
272C<&subroutine>. C<$stdout> and C<$stderr> are optional scalars that will
273contain C<STDOUT> and C<STDERR> respectively.
274
275C<capture()> uses a code prototype so the first argument can be specified
276directly within brackets if desired.
277
278    # shorthand with prototype
279    capture C< print __PACKAGE__ > \$stdout, \$stderr;
280
281Returns the return value(s) of C<&subroutine>. The sub is called in the
282same context as C<capture()> was called e.g.:
283
284    @rv = capture C< wantarray > ; # returns true
285    $rv = capture C< wantarray > ; # returns defined, but not true
286    capture C< wantarray >;       # void, returns undef
287
288C<capture()> is able to capture output from subprocesses and C code, which
289traditional C<tie()> methods of output capture are unable to do.
290
291B<Note:> C<capture()> will only capture output that has been written or
292flushed to the filehandle.
293
294If the two scalar references refer to the same scalar, then C<STDERR> will
295be merged to C<STDOUT> before capturing and the scalar will hold the
296combined output of both.
297
298    capture \&subroutine, \$combined, \$combined;
299
300Normally, C<capture()> uses anonymous, temporary files for capturing
301output.  If desired, specific file names may be provided instead as
302additional options.
303
304    capture \&subroutine, \$stdout, \$stderr, $out_file, $err_file;
305
306Files provided will be clobbered, overwriting any previous data, but will
307persist after the call to C<capture()> for inspection or other
308manipulation.
309
310By default, when no references are provided to hold STDOUT or STDERR,
311output is captured and silently discarded.
312
313    # Capture STDOUT, discard STDERR
314    capture \&subroutine, \$stdout;
315
316    # Discard STDOUT, capture STDERR
317    capture \&subroutine, undef, \$stderr;
318
319However, even when using C<undef>, output can be captured to specific
320files.
321
322    # Capture STDOUT to a specific file, discard STDERR
323    capture \&subroutine, \$stdout, undef, $outfile;
324
325    # Discard STDOUT, capture STDERR to a specific file
326    capture \&subroutine, undef, \$stderr, undef, $err_file;
327
328    # Discard both, capture merged output to a specific file
329    capture \&subroutine, undef, undef, $mergedfile;
330
331It is a fatal error to merge STDOUT and STDERR and request separate,
332specific files for capture.
333
334    # ERROR:
335    capture \&subroutine, \$stdout, \$stdout, $out_file, $err_file;
336    capture \&subroutine, undef, undef, $out_file, $err_file;
337
338If either STDOUT or STDERR should be passed through to the terminal instead
339of captured, provide a reference to undef -- C<\undef> -- instead of a
340capture variable.
341
342    # Capture STDOUT, display STDERR
343    capture \&subroutine, \$stdout, \undef;
344
345    # Display STDOUT, capture STDERR
346    capture \&subroutine, \undef, \$stderr;
347
348=head2 capture_exec()
349
350    ($stdout, $stderr, $success, $exit_code) = capture_exec(@args);
351
352Captures and returns the output from C<system(@args)>. In scalar context,
353C<capture_exec()> will return what was printed to C<STDOUT>. In list
354context, it returns what was printed to C<STDOUT> and C<STDERR> as well as
355a success flag and the exit value.
356
357    $stdout = capture_exec('perl', '-e', 'print "hello world"');
358
359    ($stdout, $stderr, $success, $exit_code) =
360        capture_exec('perl', '-e', 'warn "Test"');
361
362C<capture_exec> passes its arguments to C<system()> and on MSWin32 will
363protect arguments with shell quotes if necessary.  This makes it a handy
364and slightly more portable alternative to backticks, piped C<open()> and
365C<IPC::Open3>.
366
367The C<$success> flag returned will be true if the command ran successfully
368and false if it did not (if the command could not be run or if it ran and
369returned a non-zero exit value).  On failure, the raw exit value of the
370C<system()> call is available both in the C<$exit_code> returned and in the
371C<$?> variable.
372
373  ($stdout, $stderr, $success, $exit_code) =
374      capture_exec('perl', '-e', 'warn "Test" and exit 1');
375
376  if ( ! $success ) {
377      print "The exit code was " . ($exit_code >> 8) . "\n";
378  }
379
380See L<perlvar> for more information on interpreting a child process exit
381code.
382
383=head2 capture_exec_combined()
384
385    ($combined, $success, $exit_code) = capture_exec_combined(
386        'perl', '-e', 'print "hello\n"', 'warn "Test\n"
387    );
388
389This is just like C<capture_exec()>, except that it merges C<STDERR> with
390C<STDOUT> before capturing output.
391
392B<Note:> there is no guarantee that text printed to C<STDOUT> and C<STDERR>
393in the subprocess will be appear in order. The actual order will depend on
394how IO buffering is handled in the subprocess.
395
396=head2 qxx()
397
398This is an alias for C<capture_exec()>.
399
400=head2 qxy()
401
402This is an alias for C<capture_exec_combined()>.
403
404=head1 SEE ALSO
405
406=over 4
407
408=item *
409
410L<Capture::Tiny>
411
412=item *
413
414L<IPC::Open3>
415
416=item *
417
418L<IO::Capture>
419
420=item *
421
422L<IO::Utils>
423
424=item *
425
426L<IPC::System::Simple>
427
428=back
429
430=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
431
432=head1 SUPPORT
433
434=head2 Bugs / Feature Requests
435
436Please report any bugs or feature requests through the issue tracker
437at L<https://github.com/dagolden/IO-CaptureOutput/issues>.
438You will be notified automatically of any progress on your issue.
439
440=head2 Source Code
441
442This is open source software.  The code repository is available for
443public review and contribution under the terms of the license.
444
445L<https://github.com/dagolden/IO-CaptureOutput>
446
447  git clone https://github.com/dagolden/IO-CaptureOutput.git
448
449=head1 AUTHORS
450
451=over 4
452
453=item *
454
455Simon Flack <simonflk@cpan.org>
456
457=item *
458
459David Golden <dagolden@cpan.org>
460
461=back
462
463=head1 CONTRIBUTORS
464
465=for stopwords David Golden José Joaquín Atria Mike Latimer Olivier Mengué Tony Cook
466
467=over 4
468
469=item *
470
471David Golden <xdg@xdg.me>
472
473=item *
474
475José Joaquín Atria <jjatria@gmail.com>
476
477=item *
478
479Mike Latimer <mlatimer@suse.com>
480
481=item *
482
483Olivier Mengué <dolmen@cpan.org>
484
485=item *
486
487Tony Cook <tony@develop-help.com>
488
489=back
490
491=head1 COPYRIGHT AND LICENSE
492
493This software is copyright (c) 2019 by Simon Flack and David Golden.
494
495This is free software; you can redistribute it and/or modify it under
496the same terms as the Perl 5 programming language system itself.
497
498=cut
499