module Open4

def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)

def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
  pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
  verbose = $VERBOSE
  begin
    $VERBOSE = nil
    cid = fork {
      if closefds
        exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
        ObjectSpace.each_object(IO){|io|
          io.close if (not io.closed?) and (not exlist.include? io.fileno) rescue nil
        }
      end
      pw.last.close
      STDIN.reopen pw.first
      pw.first.close
      pr.first.close
      STDOUT.reopen pr.last
      pr.last.close
      pe.first.close
      STDERR.reopen pe.last
      pe.last.close
      STDOUT.sync = STDERR.sync = true
      begin
        cmd.call(ps)
      rescue Exception => e
        Marshal.dump(e, ps.last)
        ps.last.flush
      ensure
        ps.last.close unless ps.last.closed?
      end
      exit!
    }
  ensure
    $VERBOSE = verbose
  end
  [ pw.first, pr.last, pe.last, ps.last ].each { |fd| fd.close }
  Open4.propagate_exception cid, ps.first if exception_propagation_at == :init
  pw.last.sync = true
  pi = [ pw.last, pr.first, pe.first ]
  begin
    return [cid, *pi] unless b
    begin
      b.call(cid, *pi)
    ensure
      pi.each { |fd| fd.close unless fd.closed? }
    end
    Open4.propagate_exception cid, ps.first if exception_propagation_at == :block
    Process.waitpid2(cid).last
  ensure
    ps.first.close unless ps.first.closed?
  end
end

def self.propagate_exception(cid, ps_read)

def self.propagate_exception(cid, ps_read)
  e = Marshal.load ps_read
  raise Exception === e ? e : "unknown failure!"
rescue EOFError
  # Child process did not raise exception.

rescue
  # Child process raised exception; wait it in order to avoid a zombie.

  Process.waitpid2 cid
  raise
ensure
  ps_read.close
end

def alive pid

def alive pid
  pid = Integer pid
  begin
    Process.kill 0, pid
    true
  rescue Errno::ESRCH
    false
  end
end

def background arg, *argv

def background arg, *argv 
  require 'thread'
  q = Queue.new
  opts = { 'pid' => q, :pid => q }
  case argv.last
    when Hash
      argv.last.update opts
    else
      argv.push opts
  end
  thread = Thread.new(arg, argv){|_arg, _argv| spawn _arg, *_argv}
  sc = class << thread; self; end
  sc.module_eval {
    define_method(:pid){ @pid ||= q.pop }
    define_method(:spawn_status){ @spawn_status ||= value }
    define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
  }
  thread
end

def chdir cwd, &block

def chdir cwd, &block
  return(block.call Dir.pwd) unless cwd
  Dir.chdir cwd, &block
end

def getopts opts = {}

def getopts opts = {}
  lambda do |*args|
    keys, default, _ = args
    catch(:opt) do
      [keys].flatten.each do |key|
        [key, key.to_s, key.to_s.intern].each do |_key|
          throw :opt, opts[_key] if opts.has_key?(_key)
        end
      end
      default
    end
  end
end

def maim pid, opts = {}

def maim pid, opts = {}
  getopt = getopts opts
  sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
  suspend = getopt[ 'suspend', 4 ]
  pid = Integer pid
  existed = false
  sigs.each do |sig|
    begin
      Process.kill sig, pid
      existed = true 
    rescue Errno::ESRCH
      return(existed ? nil : true)
    end
    return true unless alive? pid
    sleep suspend
    return true unless alive? pid
  end
  return(not alive?(pid)) 
end

def new_thread *a, &b

def new_thread *a, &b
  cur = Thread.current
  Thread.new(*a) do |*_a|
    begin
      b[*_a]
    rescue Exception => e
      cur.raise e
    end
  end
end

def pfork4(fun, &b)

def pfork4(fun, &b)
  Open4.do_popen(b, :block) do |ps_read, _|
    ps_read.close
    begin
      fun.call
    rescue SystemExit => e
      # Make it seem to the caller that calling Kernel#exit in +fun+ kills

      # the child process normally. Kernel#exit! bypasses this rescue

      # block.

      exit! e.status
    else
      exit! 0
    end
  end
end

def popen4(*cmd, &b)

def popen4(*cmd, &b)
  Open4.do_popen(b, :init) do |ps_read, ps_write|
    ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
    ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
    exec(*cmd)
    raise 'forty-two'   # Is this really needed?

  end
end

def popen4ext(closefds=false, *cmd, &b)

def popen4ext(closefds=false, *cmd, &b)
  Open4.do_popen(b, :init, closefds) do |ps_read, ps_write|
    ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
    ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
    exec(*cmd)
    raise 'forty-two'   # Is this really needed?

  end
end

def relay src, dst = nil, t = nil

def relay src, dst = nil, t = nil
  send_dst =
    if dst.respond_to?(:call)
      lambda{|buf| dst.call(buf)}
    elsif dst.respond_to?(:<<)
      lambda{|buf| dst << buf }
    else
      lambda{|buf| buf }
    end
  unless src.nil?
    if src.respond_to? :gets
      while buf = to(t){ src.gets }
        send_dst[buf]
      end
    elsif src.respond_to? :each
      q = Queue.new
      th = nil
      timer_set = lambda do |_t|
        th = new_thread{ to(_t){ q.pop } }
      end
      timer_cancel = lambda do |_t|
        th.kill if th rescue nil
      end
      timer_set[t]
      begin
        src.each do |_buf|
          timer_cancel[t]
          send_dst[_buf]
          timer_set[t]
        end
      ensure
        timer_cancel[t]
      end
    elsif src.respond_to? :read
      buf = to(t){ src.read }
      send_dst[buf]
    else
      buf = to(t){ src.to_s }
      send_dst[buf]
    end
  end
end

def spawn arg, *argv

def spawn arg, *argv 
  argv.unshift(arg)
  opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
  argv.flatten!
  cmd = argv.join(' ')
  getopt = getopts opts
  ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
  ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
  exitstatus = getopt[ %w( exitstatus exit_status status ) ]
  stdin = getopt[ %w( stdin in i 0 ) << 0 ]
  stdout = getopt[ %w( stdout out o 1 ) << 1 ]
  stderr = getopt[ %w( stderr err e 2 ) << 2 ]
  pid = getopt[ 'pid' ]
  timeout = getopt[ %w( timeout spawn_timeout ) ]
  stdin_timeout = getopt[ %w( stdin_timeout ) ]
  stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
  stderr_timeout = getopt[ %w( stderr_timeout ) ]
  status = getopt[ %w( status ) ]
  cwd = getopt[ %w( cwd dir ) ]
  closefds = getopt[ %w( close_fds ) ]
  exitstatus =
    case exitstatus
      when TrueClass, FalseClass
        ignore_exit_failure = true if exitstatus
        [0]
      else
        [*(exitstatus || 0)].map{|i| Integer i}
    end
  stdin ||= '' if stdin_timeout
  stdout ||= '' if stdout_timeout
  stderr ||= '' if stderr_timeout
  started = false
  status =
    begin
      chdir(cwd) do
        Timeout::timeout(timeout) do
          popen4ext(closefds, *argv) do |c, i, o, e|
            started = true
            %w( replace pid= << push update ).each do |msg|
              break(pid.send(msg, c)) if pid.respond_to? msg 
            end
            te = ThreadEnsemble.new c
            te.add_thread(i, stdin) do |_i, _stdin|
              relay _stdin, _i, stdin_timeout
              _i.close rescue nil
            end
            te.add_thread(o, stdout) do |_o, _stdout|
              relay _o, _stdout, stdout_timeout
            end
            te.add_thread(e, stderr) do |_o, _stderr| # HACK: I think this is a bug

              relay e, _stderr, stderr_timeout
            end
            te.run
          end
        end
      end
    rescue
      raise unless(not started and ignore_exec_failure)
    end
  raise SpawnError.new(cmd, status) unless
    (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
  status
end

def to timeout = nil

def to timeout = nil
  Timeout.timeout(timeout){ yield }
end