From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 988721F463 for ; Thu, 19 Dec 2019 08:38:51 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH] testcommon: dup original handles via open Date: Thu, 19 Dec 2019 08:38:51 +0000 Message-Id: <20191219083851.4829-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: I was surprised when I noticed "local *STDIN = *STDIN" worked, but that was with Perl 5.28.1. Unfortunately, we can't rely on users or testers having such a recent Perl version. Even worse, it was silently a no-op on Perl 5.24.1 on Debian 9.x. So, save the original handle and manually restore the original via open, when we're done. --- lib/PublicInbox/TestCommon.pm | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm index 85cda031..d9b21337 100644 --- a/lib/PublicInbox/TestCommon.pm +++ b/lib/PublicInbox/TestCommon.pm @@ -67,12 +67,25 @@ sub key2script ($) { 'blib/script/'.$key; } +my @io_mode = ([ *STDIN{IO}, '<&' ], [ *STDOUT{IO}, '>&' ], [ *STDERR{IO}, '>&' ]); sub _prepare_redirects ($) { my ($fhref) = @_; - my @x = ([ \*STDIN, '<&' ], [ \*STDOUT, '>&' ], [ \*STDERR, '>&' ]); - for (my $fd = 0; $fd <= $#x; $fd++) { + my $orig_io = []; + for (my $fd = 0; $fd <= $#io_mode; $fd++) { my $fh = $fhref->[$fd] or next; - my ($oldfh, $mode) = @{$x[$fd]}; + my ($oldfh, $mode) = @{$io_mode[$fd]}; + open my $orig, $mode, $oldfh or die "$$oldfh $mode stash: $!"; + $orig_io->[$fd] = $orig; + open $oldfh, $mode, $fh or die "$$oldfh $mode redirect: $!"; + } + $orig_io; +} + +sub _undo_redirects ($) { + my ($orig_io) = @_; + for (my $fd = 0; $fd <= $#io_mode; $fd++) { + my $fh = $orig_io->[$fd] or next; + my ($oldfh, $mode) = @{$io_mode[$fd]}; open $oldfh, $mode, $fh or die "$$oldfh $mode redirect: $!"; } } @@ -174,14 +187,14 @@ sub run_script ($;$$) { $r == $pid or die "waitpid: expected $pid, got $r"; } } else { # localize and run everything in the same process: - local *STDIN = *STDIN; - local *STDOUT = *STDOUT; - local *STDERR = *STDERR; + # note: "local *STDIN = *STDIN;" and so forth did not work in + # old versions of perl local %ENV = $env ? (%ENV, %$env) : %ENV; local %SIG = %SIG; local $0 = join(' ', @$cmd); - _prepare_redirects($fhref); + my $orig_io = _prepare_redirects($fhref); _run_sub($sub, $key, \@argv); + _undo_redirects($orig_io); } # slurp the redirects back into user-supplied strings