Blob Blame History Raw
From 8ebe48760cfdc78fbf4fc46413dde9470121b99e Mon Sep 17 00:00:00 2001
From: Roderich Schupp <roderich.schupp@gmail.com>
Date: Sun, 29 Sep 2013 18:12:03 +0200
Subject: [PATCH 5/5] test and fix for RT #52317: Calling run3 garbles STDIN

---
 lib/IPC/Run3.pm    | 26 ++++++-------------------
 t/preserve_stdin.t | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 20 deletions(-)
 create mode 100644 t/preserve_stdin.t

diff --git a/lib/IPC/Run3.pm b/lib/IPC/Run3.pm
index 12c9d8a..777e290 100644
--- a/lib/IPC/Run3.pm
+++ b/lib/IPC/Run3.pm
@@ -363,14 +363,12 @@ sub run3 {
             $options if defined $stderr;
 
     # this should make perl close these on exceptions
-#    local *STDIN_SAVE;
+    local *STDIN_SAVE;
     local *STDOUT_SAVE;
     local *STDERR_SAVE;
 
-    my $saved_fd0 = dup( 0 ) if defined $in_fh;
-
-#    open STDIN_SAVE,  "<&STDIN"#  or croak "run3(): $! saving STDIN"
-#        if defined $in_fh;
+    open STDIN_SAVE,  "<&STDIN"  or croak "run3(): $! saving STDIN"
+        if defined $in_fh;
     open STDOUT_SAVE, ">&STDOUT" or croak "run3(): $! saving STDOUT"
         if defined $out_fh;
     open STDERR_SAVE, ">&STDERR" or croak "run3(): $! saving STDERR"
@@ -378,17 +376,10 @@ sub run3 {
 
     my $errno;
     my $ok = eval {
-        # The open() call here seems to not force fd 0 in some cases;
-        # I ran in to trouble when using this in VCP, not sure why.
-        # the dup2() seems to work.
-        dup2( fileno $in_fh, 0 )
-#        open STDIN,  "<&=" . fileno $in_fh
+        open STDIN,  "<&=" . fileno $in_fh
             or croak "run3(): $! redirecting STDIN"
             if defined $in_fh;
 
-#        close $in_fh or croak "$! closing STDIN temp file"
-#            if ref $stdin;
-
         open STDOUT, ">&" . fileno $out_fh
             or croak "run3(): $! redirecting STDOUT"
             if defined $out_fh;
@@ -428,13 +419,8 @@ sub run3 {
 
     my @errs;
 
-    if ( defined $saved_fd0 ) {
-        dup2( $saved_fd0, 0 );
-        POSIX::close( $saved_fd0 );
-    }
-
-#    open STDIN,  "<&STDIN_SAVE"#  or push @errs, "run3(): $! restoring STDIN"
-#        if defined $in_fh;
+    open STDIN,  "<&STDIN_SAVE"  or push @errs, "run3(): $! restoring STDIN"
+        if defined $in_fh;
     open STDOUT, ">&STDOUT_SAVE" or push @errs, "run3(): $! restoring STDOUT"
         if defined $out_fh;
     open STDERR, ">&STDERR_SAVE" or push @errs, "run3(): $! restoring STDERR"
diff --git a/t/preserve_stdin.t b/t/preserve_stdin.t
new file mode 100644
index 0000000..8e090ee
--- /dev/null
+++ b/t/preserve_stdin.t
@@ -0,0 +1,57 @@
+#!perl -w
+
+## test whether reading from STDIN is preserved when
+## run3 is called in between reads
+
+use Test::More;
+use IPC::Run3;
+use File::Temp qw(tempfile);
+use strict;
+
+# call run3 at different lines (problems might manifest itself 
+# on different lines, probably due to different buffering of input)
+my @check_at = (5, 10, 50, 100, 200, 500);
+plan tests => @check_at * 3;
+
+# create a test file for input containing 1000 lines
+my $nlines = 1000;
+my @exp_lines;
+my ($fh, $file) = tempfile(UNLINK => 1);
+for (my $i = 1; $i <= $nlines; $i++)
+{
+    my $line = "this is line $i";
+    push @exp_lines, $line;
+    print $fh $line, "\n";
+}
+close $fh;
+
+
+my ( $in, $out, $err );
+
+foreach my $n (@check_at)
+{
+    my $nread = 0;
+    my $unexpected;
+    open STDIN, "<", $file or die "can't open file $file: $!";
+    while (<STDIN>)
+    {
+	chomp;
+	$unexpected = qq[line $nread: expected "$exp_lines[$nread]", got "$_"\n]
+	    unless $exp_lines[$nread] eq $_ || $unexpected;
+	$nread++;
+
+	if ($nread == $n)
+	{
+            $in = "checking at line $n";
+	    run3 [ $^X, '-e', 'print uc $_ while <>' ], \$in, \$out, \$err;
+	    die "command failed" unless $? == 0;
+            is($out, uc $in);
+	}
+    }
+    close STDIN;
+
+    is($nread, $nlines, "STDIN was read completely");
+    ok(!$unexpected, "STDIN as expected") or diag($unexpected);
+}
+
+
-- 
1.8.5.3