class Session::AbstractSession

def execute(command, redirects = {})

def execute(command, redirects = {})
  $session_command = command if @debug
  raise(PipeError, command) unless ready? 
# clear buffers
  clear
# setup redirects
  rerr = redirects[:e] || redirects[:err] || redirects[:stderr] || 
         redirects['stderr'] || redirects['e'] || redirects['err'] ||
         redirects[2] || redirects['2']
  rout = redirects[:o] || redirects[:out] || redirects[:stdout] || 
         redirects['stdout'] || redirects['o'] || redirects['out'] ||
         redirects[1] || redirects['1']
# create cmd object and add to history
  cmd = Command::new command.to_s
# store cmd if tracking history
  history << cmd if track_history
# mutex for accessing shared data
  mutex = Mutex::new
# io data for stderr and stdout 
  err = {
    :io        => stderr,
    :cmd       => cmd.err,
    :name      => 'stderr',
    :begin     => false,
    :end       => false,
    :begin_pat => cmd.begin_err_pat,
    :end_pat   => cmd.end_err_pat,
    :redirect  => rerr,
    :proc      => errproc,
    :yield     => lambda{|buf| yield(nil, buf)},
    :mutex     => mutex,
  }
  out = {
    :io        => stdout,
    :cmd       => cmd.out,
    :name      => 'stdout',
    :begin     => false,
    :end       => false,
    :begin_pat => cmd.begin_out_pat,
    :end_pat   => cmd.end_out_pat,
    :redirect  => rout,
    :proc      => outproc,
    :yield     => lambda{|buf| yield(buf, nil)},
    :mutex     => mutex,
  }
begin
  # send command in the background so we can begin processing output
  # immediately - thanks to tanaka akira for this suggestion
    threads << Thread::new { send_command cmd }
  # init 
    main       = Thread::current
    exceptions = []
  # fire off reader threads
    [err, out].each do |iodat|
      threads <<
        Thread::new(iodat, main) do |iodat, main|
          loop do
            main.raise(PipeError, command) unless ready? 
            main.raise ExecutionError, iodat[:name] if iodat[:end] and not iodat[:begin]
            break if iodat[:end] or iodat[:io].eof?
            line = iodat[:io].gets
            # In case their are weird chars, this will avoid a "invalid byte sequence in US-ASCII" error
            line.force_encoding("binary") if line.respond_to? :force_encoding
            buf = nil
            case line
              when iodat[:end_pat]
                iodat[:end] = true
              # handle the special case of non-newline terminated output
                if((m = %r/(.+)__CMD/o.match(line)) and (pre = m[1]))
                  buf = pre
                end
              when iodat[:begin_pat]
                iodat[:begin] = true
              else
                next unless iodat[:begin] and not iodat[:end] # ignore chaff
                buf = line
            end
            if buf
              iodat[:mutex].synchronize do
                iodat[:cmd] << buf
                iodat[:redirect] << buf if iodat[:redirect]
                iodat[:proc].call buf  if iodat[:proc]
                iodat[:yield].call buf  if block_given?
              end
            end
          end
          true
      end
    end
  ensure
  # reap all threads - accumulating and rethrowing any exceptions
    begin
      while((t = threads.shift))
        t.join
        raise ExecutionError, 'iodat thread failure' unless t.value
      end
    rescue => e
      exceptions << e
      retry unless threads.empty?
    ensure
      unless exceptions.empty?
        meta_message = '<' << exceptions.map{|e| "#{ e.message } - (#{ e.class })"}.join('|') << '>'
        meta_backtrace = exceptions.map{|e| e.backtrace}.flatten
        raise ExecutionError, meta_message, meta_backtrace 
      end
    end
  end
# this should only happen if eof was reached before end pat
  [err, out].each do |iodat|
    raise ExecutionError, iodat[:name] unless iodat[:begin] and iodat[:end]
  end
# get the exit status
  get_status if respond_to? :get_status
  out = err = iodat = nil
  return [cmd.out, cmd.err]
end