>>>>> On Tue, 29 Jan 2002 10:38:14 +0900,
>>>>> nobu nokada <nobu.nokada / softhome.net> (nn) writes:

nn> Like this?

nn>     th = Thread.current
nn>     Thread.new {
nn>       Process.waitpid(pid)
nn>       $? == 0 or th.raise(ChildDied.new($?, cmd))
nn>     }

Why the use of Thread?  I wrote a script that used this open3 to run
another script that ended with 'exit 1'.  I then ran this mess like
this:

  while :; do ./runcmd.rb ; done

Well, sometimes the exception was raised, and sometimes it wasn't.
So I fiddled with it some and came up with this.  It also passes the
pipes back so that they can be accessed in the rescue clause.

module Open3
  class ChildDied < StandardError
    def initialize(code, cmd, pipes)
      @code = code
      @cmd = cmd
      @pipes = pipes
    end
    attr_reader :code, :cmd, :pipes
  end

  def popen3(*cmd)
    pw = IO::pipe
    pr = IO::pipe
    pe = IO::pipe
    pid = fork {
      pw[1].close; STDIN.reopen(pw[0]); pw[0].close
      pr[0].close; STDOUT.reopen(pr[1]); pr[1].close
      pe[0].close; STDERR.reopen(pe[1]); pe[1].close
      exec(*cmd)
    }
    pw[0].close
    pr[1].close
    pe[1].close
    pi = [pw[1], pr[0], pe[0]]
    Process.waitpid(pid)
    $? == 0 or raise(ChildDied.new($?, cmd, pi))
    defined?(yield) or return pi
    begin
      yield(*pi)
    ensure
      pi.each{|p| p.close unless p.closed?}
    end
  end
  module_function :popen3
end