#!/bin/sh
# Hide shell commands from expect \
[ -n "$BASH" ] || exec bash -p $0 "$@";\
#set -x;\
exec expect -- "$0" "$@"

proc help exitcode \
{
  puts -nonewline {Usage: ggdb [-a program args] [-f|-g file of gdb commands]}
  puts "... \\" ;# Must use dbl-quote because of trlg backslash
  puts -nonewline {            [-p <PID of process to debug>]}
  puts " \\"
  puts {            [-t controlling terminal] [<program>] [gdb command]...}
  puts {       ggdb -h}
  puts {}
  puts {Exactly one of <program> or -p <PID> must be given.}
  puts {-g commands run before program is started; -f after main breakpoints}
  puts {or attach completes.}
  exit $exitcode
}

# Set up
# === ==

#exp_internal 1
#strace 2
global pending file_index
set pending 0
set file_index(dummy) x
set PID 0

proc sndrcv {cmd} \
{
  global pending
  if {$pending} \
    {expect -re "\[(\]gdb\[)\] |\r\n\[^>\]*>$| \\(y or \\\[n\\\]\\) "}
  exp_send ${cmd}\r
  set pending 1
}

proc addfile {which} \
{
  global file_index argc argv file_name

  if {![info exists file_index($which)]} {set file_index($which) 0}
  set F [lindex $argv 1]
  set file_name($file_index($which),$which) $F
  set argv [lreplace $argv 0 1]
  incr argc -2
  if {![file readable $F]} \
  {
    puts "$F: not readable"
    exit 1
  }
  file stat $F stat
  if {[string compare $stat(type) file]} \
  {
    puts "$F: not a regular file"
    exit 1
  }
  incr file_index($which)
}

#puts "$argc args: $argv"
if {$argc == 0} \
{
  puts stderr "No program name supplied"
  help 1
}

# Get options if any
set args {}
while {$argc > 2 || ($argc == 2 && [string tolower [lindex $argv 0]] == {-p})} \
{
  switch -- [string tolower [lindex $argv 0]] \
  {
    -a \
    {
      # Arguments to target program
      set args [lindex $argv 1]
      set argv [lreplace $argv 0 1]
      incr argc -2
    }

    -f {addfile f}

    -g {addfile g}

    -p \
    {
      # Process to attach
      set PID [lindex $argv 1]
      set argv [lreplace $argv 0 1]
      incr argc -2
      if {![string is integer $PID]} \
      {
        puts stderr "$PID (process ID) is not an integer"
        exit 1
      }
    }

    -t \
    {
      # Controlling tty
      # (Cygwin can't do this via redirection on run)
      set tty [lindex $argv 1]
      set argv [lreplace $argv 0 1]
      incr argc -2
    }

    -- \
    {
      # Code after the switch deals with this
      break
    }

    default {break}
  }
}

# Unless -p PID was given earlier,
#   next arg must be program name, -h (help) or -- (edit next arg)
if {$PID==0} \
{
  if {[string equal -nocase -length 2 [lindex $argv 0] -h]} {help 0}
  if {$argc >= 2 && [lindex $argv 0] == "--" } \
  {
    set argv [lreplace $argv 0 0]
    incr argc -1
  }
  set progname [lindex $argv 0]
  set argv [lreplace $argv 0 0]
  incr argc -1
  if {![file exists $progname]} \
  {
    puts stderr "No such file or directory: $progname"
    exit 1
  }
  if {![file executable $progname] || ![file isfile $progname]} \
  {
    puts stderr "$progname is not a program"
    exit 1
  }
}

proc docmds {which} \
{
# Do gdb commands from file(s) in order
  global file_index file_name

  if {[info exists file_index($which)]} \
  {
    for {set i 0} {$i < $file_index($which)} {incr i} \
    {
      set chan [open $file_name($i,$which)]
      while {[gets $chan line] >= 0} {sndrcv $line}
      close $chan
    }
  }
}

# start gdb, and put in some nice settings
if {[info exists tty]} {spawn gdb -n -q --tty=$tty} {spawn gdb -n -q}
expect {(gdb) }
if {$PID==0} \
{
  exp_send "file $progname\r"
  expect {(gdb) }
} \
{
  exp_send "attach $PID\n"
  expect \
  {
    ptrace: {exit 1}
    {(gdb) }
  }
}
exp_send "set print null-stop\r"
expect {(gdb) }
exp_send "set print sevenbit-strings\r"
expect {(gdb) }
exp_send "set print elements 0\r"
expect {(gdb) }
exp_send "set height 0\r"
expect {(gdb) }
exp_send "set print pretty\r"
expect {(gdb) }
exp_send "set unwindonsignal on\r"
expect {(gdb) }
exp_send "set debuginfod enabled off\r"
expect {(gdb) }
if {[info exists tty]} \
{
  exp_send "set inferior-tty $tty\r"
  expect {(gdb) }
}
docmds g
if {$PID==0} \
{
  exp_send "b main\r"
  expect {(gdb) }
  set timeout 50
  exp_send "run $args\r"
  expect {(gdb) }
  set timeout 10
}
docmds f

# Do gdb commands from the command line
for {set i 0} {$i < $argc} {incr i} {sndrcv [lindex $argv $i]}

interact -echo \\\\ \
{
  puts {}
  exit
}
