
# GDB/MI (the GDB interface for IDEs and such) wrapper.
# Very simple for now.
class GDBMI
  @@debug = false
  def initialize(pid)
    #return self
    puts "attaching gdb to #{pid}" if @@debug
    pw = IO::pipe
    pr = IO::pipe
    @pid = fork do
      pw[1].close; STDIN.reopen(pw[0]);  pw[0].close
      pr[0].close; STDOUT.reopen(pr[1]); pr[1].close
      exec("gdb --interpreter mi2 -p #{pid}")
    end
    @write = pw[1]; pw[0].close
    @read = pr[0]; pr[1].close

    # read the GDB greeting.
    read_response(false)
  end

  # GDB responses are C strings.
  # This should do a lot more...
  def self.parse_c_string str
    str =~ /"(.*)"/
    str = $1
    str.gsub!('\n', "\n")
    str.gsub!('\"', '"')
    return str
  end

  # Read a response from GDB.
  # A response is multiple lines, and the last one is just "(gdb)".
  def read_response(echo=false)
    loop do
      puts "waiting for gdb line" if @@debug
      line = @read.gets.strip
      puts "GDB> " + line if @@debug
      break if line == '(gdb)'

      case line[0,1]
      when '^'
        puts "GDBMI done> #{line[1..-1]}" if @@debug
      when '&'  # "log stream"
        puts "GDBMI status> #{line[1..-1]}" if @@debug
        print(GDBMI.parse_c_string(line[1..-1])) if echo
      when '~'  # "console stream"
        print(GDBMI.parse_c_string(line[1..-1])) if echo
      else
        puts "GDBMI other> #{line}" if echo
      end
    end
    puts "done waiting for gdb line" if @@debug
  end

  # Run a command through GDB.
  def run(cmd, echo=true)
    puts "->GDB> " + cmd if @@debug
    @write.puts cmd
    read_response(echo)
  end

  # Detach the GDB.
  def detach
    # XXX is there a better way to do this?
    @write.puts "q"  # "quit"
    @write.puts "y"  # "yes, I really mean to quit"
  end

  def self.attach_run_detach(pid, cmd)
    gdb = GDBMI.new(pid)
    gdb.run cmd
    gdb.detach
  end
end


