// $Id$ -*- C++ -*-
// Communicate with separate GDB process

// Copyright (C) 1995 Technische Universitaet Braunschweig, Germany.
// Copyright (C) 2000 Universitaet Passau, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@gnu.org>.
// 
// This file is part of DDD.
// 
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
// 
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, see <http://www.gnu.org/licenses/>.
// 
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page, 
// `http://www.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

//-----------------------------------------------------------------------------
// A GDBAgent creates a connection to an inferior GDB via a TTYAgent.
// There are two ways to send commands to GDB:
// 1. send_user_cmd: is used for user input at the GDB prompt.  GDB output
//    (partial answers) are processed immediately.
// 2. send_qu_array: issues a list of queries to GDB.  The passed function
//    is called as soon as the last query is processed.  The passed function
//    must free the memory claimed by the query array.
//-----------------------------------------------------------------------------

#ifndef _DDD_GDBAgent_h
#define _DDD_GDBAgent_h

#include "TTYAgent.h"
#include "HandlerL.h"
#include "strclass.h"
#include "bool.h"
#include "assert.h"
#include "StringA.h"
#include "VoidArray.h"

//-----------------------------------------------------------------------------
// Debugger types
//-----------------------------------------------------------------------------

enum DebuggerType { BASH, DBG, DBX, GDB, JDB, PERL, PYDB, XDB, MAKE };
enum DebuggerCPU { cpu_intel, cpu_unknown};
//-----------------------------------------------------------------------------
// Program language
//-----------------------------------------------------------------------------

#ifdef LANGUAGE_C
#undef LANGUAGE_C		// DEC defines this
#endif

enum ProgramLanguage { 
    LANGUAGE_ADA,		// Ada (GNAT), as supported by GDB
    LANGUAGE_BASH,		// Bash, as supported by bashdb.
    LANGUAGE_C,			// C-like: C, C++
    LANGUAGE_CHILL,		// Chill, as supported by GDB.
    LANGUAGE_FORTRAN,		// FORTRAN, as supported by GDB.
    LANGUAGE_JAVA,		// Java, as supported by GDB.
    LANGUAGE_MAKE,		// Make, as supported by mdb.
    LANGUAGE_PASCAL,		// Pascal-like: Pascal, Modula...
    LANGUAGE_PERL,		// Perl, as supported by Perl.
    LANGUAGE_PHP,		// PHP, as supported by DBG.
    LANGUAGE_PYTHON,		// Python, as supported by PYDB.
    LANGUAGE_OTHER		// Others
};

//-----------------------------------------------------------------------------
// Watch modes
//-----------------------------------------------------------------------------

typedef int WatchMode;

const int WATCH_CHANGE = 1;	// break if value changes
const int WATCH_READ   = 2;	// break if value is read
const int WATCH_WRITE  = 4;	// break if value is written

const int WATCH_ACCESS = WATCH_READ | WATCH_WRITE; // shorthand

//-----------------------------------------------------------------------------
// Procedure types.
//-----------------------------------------------------------------------------

// Called as soon as an ANSWER on send_user_cmd (or a part of it)
// is received
typedef void (* OAProc) (const string& answer,
			 void* user_data);

// Called after the whole answer to send_user_cmd has been received
typedef void (* OACProc) (void* user_data);

// Called from send_qu_array with the complete answers
typedef void (* OQACProc) (StringArray& complete_answers,
			   const VoidArray& user_datas,
			   void *user_data);

//-----------------------------------------------------------------------------

// Create appropriate call for the given debugger
string build_gdb_call(DebuggerType debugger_type,
		      const string& debugger_command,
		      const string& init_file,
		      int argc, char *argv[],
		      string myArguments = "");

// Handlers
const unsigned ReadyForQuestion = TTYAgent_NTypes;
const unsigned ReadyForCmd      = ReadyForQuestion + 1;
const unsigned LanguageChanged  = ReadyForCmd + 1;
const unsigned ReplyRequired    = LanguageChanged + 1;
const unsigned YesNoRequired    = ReplyRequired + 1;
const unsigned EchoDetected     = YesNoRequired + 1;
const unsigned AsyncAnswer      = EchoDetected + 1;
const unsigned ExceptionState   = AsyncAnswer + 1;
const unsigned Recording        = ExceptionState + 1;
const unsigned GDBAgent_NTypes  = Recording + 1;

// Handler info
struct ReplyRequiredInfo {
    string question;		// Question asked by GDB
    string reply;		// Our reply

    ReplyRequiredInfo()
	: question(), reply()
    {}
};

//-----------------------------------------------------------------------------
// The GDBAgent class.
//-----------------------------------------------------------------------------

class GDBAgent: public TTYAgent {
public:
    DECLARE_TYPE_INFO
    DebuggerCPU cpu;

protected:
    enum State {
	ReadyWithPrompt,
	BusyOnCmd, 
	BusyOnQuArray,
	BusyOnInitialCmds
    };
    State state;		// Current state

private:
    DebuggerType    _type;	// Debugger type
    void*           _user_data;	// used in callbacks etc.

    bool _has_frame_command;	// Debugger properties
    bool _has_func_command;	
    bool _has_file_command;
    bool _has_run_io_command;
    bool _has_print_r_option;
    bool _has_output_command;
    bool _has_where_h_option;
    bool _has_display_command;
    bool _has_clear_command;
    bool _has_handler_command;
    bool _has_pwd_command;
    bool _has_setenv_command;
    bool _has_edit_command;
    bool _has_make_command;
    bool _has_jump_command;
    bool _has_regs_command;
    WatchMode _has_watch_command;
    bool _has_named_values;
    bool _has_when_command;
    bool _has_when_semicolon;
    bool _wants_delete_comma;
    bool _has_err_redirection;
    bool _has_givenfile_command;
    bool _has_cont_sig_command;
    bool _has_examine_command;
    bool _has_rerun_command;
    bool _rerun_clears_args;
    bool _has_attach_command;
    bool _has_addproc_command;
    bool _has_debug_command;
    bool _is_windriver_gdb;

    ProgramLanguage _program_language; // Current program language

    bool _verbatim;		// True if in verbatim mode
    bool _recording;		// True if we are recording commands
    bool _detect_echos;		// True if echos are to be detected
    bool _buffer_gdb_output;	// True if GDB output is to be buffered
    bool _flush_next_output;	// True if next GDB output is to be flushed

    string last_prompt;		// Last prompt received
    string last_written;	// Last command sent
    int echoed_characters;      // # of echoed characters so far (-1: no echo)

    bool exception_state;	// True if exception encountered

    GDBAgent& operator = (const GDBAgent&);

protected:
    // Return PREFIX + EXPR, parenthesizing EXPR if needed
    static string prepend_prefix(const string& prefix, const string& expr);
    static string prepend_prefix(const char *prefix, const string& expr);

    // Return EXPR + SUFFIX, parenthesizing EXPR if needed
    static string append_suffix(const string& expr, const string& suffix);
    static string append_suffix(const string& expr, const char *suffix);

    // General trace function
    void trace(const char *prefix, void *call_data) const;

    // Perl specials
    static void munch_perl_array(string& value, bool hash);
    static void munch_perl_scalar(string& value);

public:
    // Constructor
    GDBAgent (XtAppContext app_context,
	      const string& gdb_call,
	      DebuggerType type,
	      unsigned nTypes = GDBAgent_NTypes);

    // Duplicator
    GDBAgent (const GDBAgent& gdb);
    virtual Agent *dup() const { return new GDBAgent(*this); }

    ~GDBAgent ();

    // Start new process
    void do_start (OAProc  on_answer,
		   OACProc on_answer_completion,
		   void*   user_data);

    // After receiving the first GDB prompt, send commands to GDB.
    // Processing is very much like send_user_cmd_plus.
    void start_plus (OAProc   on_answer,
		     OACProc  on_answer_completion,
		     void*    user_data,
		     const StringArray& cmds,
		     const VoidArray& user_datas,
		     int      qu_count,
		     OQACProc on_qu_array_completion,
		     void*    qa_data,
		     bool&    qa_data_registered);

    // true iff command was sent.
    // If user_data == 0, _user_data remains unchanged.
    bool send_user_cmd      (string cmd, void* user_data = 0);  
    bool send_user_ctrl_cmd (const string& cmd, void* user_data = 0);


    // Order of tasks:
    // 1. Send USER_CMD to GDB, as in send_user_cmd
    //    (state: ReadyWithPrompt --> BusyOnCmd)
    // 2. Call OAProc when answer comes in
    //    (state:BusyOnCmd --> BusyOnQuArray)
    // 3. Send CMDS, as in send_qu_array.
    // 4. When all replies have come in: call OACProc and OQACProc.
    //
    bool send_user_cmd_plus (const StringArray& cmds,
			     const VoidArray& qu_datas,
			     int      qu_count,
			     OQACProc on_qu_array_completion,
			     void*    qa_data,
			     bool&    qa_data_registered,
			     string user_cmd,
			     void* user_data = 0);

    bool send_qu_array (const StringArray& cmds,
			const VoidArray& qu_datas,
			int      qu_count,
			OQACProc on_qu_array_completion,
			void*    qa_data,
			bool&    qa_data_registered);


    // Resources
    DebuggerType type()       const { return _type; }
    bool isSunDBX()           const {
#ifdef HAVE_SUNDBX
                                      return type() == DBX;
#else
                                      return false;
#endif
    }
    const string& title()     const;
    bool isReadyWithPrompt()  const { return state == ReadyWithPrompt; }
    const string& prompt()    const { return last_prompt; }


    // Debugger properties
    // True if debugger has `frame' command
    bool has_frame_command() const    { return _has_frame_command; }	
    bool has_frame_command(bool val)  { return _has_frame_command = val; }

    // True if debugger has `file' command
    bool has_file_command() const    { return _has_file_command; }
    bool has_file_command(bool val)  { return _has_file_command = val; }

    // True if debugger has `func' command
    bool has_func_command() const    { return _has_func_command; }	
    bool has_func_command(bool val)  { return _has_func_command = val; }

    // True if debugger has `run_io' command
    bool has_run_io_command() const    { return _has_run_io_command; }
    bool has_run_io_command(bool val)  { return _has_run_io_command = val; }

    // True if debugger has `-r' option in `print' and `display'
    bool has_print_r_option() const   { return _has_print_r_option; }
    bool has_print_r_option(bool val) { return _has_print_r_option = val; }

    // True if debugger has `output' command
    bool has_output_command() const   { return _has_output_command; }
    bool has_output_command(bool val) { return _has_output_command = val; }

    // True if debugger has `-h' option in `where'
    bool has_where_h_option() const   { return _has_where_h_option; }
    bool has_where_h_option(bool val) { return _has_where_h_option = val; }

    // True if debugger has `display' command
    bool has_display_command() const   { return _has_display_command; }
    bool has_display_command(bool val) { return _has_display_command = val; }

    // True if debugger has `clear' command
    bool has_clear_command() const     { return _has_clear_command; }
    bool has_clear_command(bool val)   { return _has_clear_command = val; }

    // True if debugger has `handler' command
    bool has_handler_command() const   { return _has_handler_command; }
    bool has_handler_command(bool val) { return _has_handler_command = val; }

    // True if debugger has `pwd' command
    bool has_pwd_command() const       { return _has_pwd_command; }
    bool has_pwd_command(bool val)     { return _has_pwd_command = val; }

    // True if debugger has `setenv' command
    bool has_setenv_command() const    { return _has_setenv_command; }
    bool has_setenv_command(bool val)  { return _has_setenv_command = val; }

    // True if debugger has `edit' command
    bool has_edit_command() const      { return _has_edit_command; }
    bool has_edit_command(bool val)    { return _has_edit_command = val; }

    // True if debugger has `make' command
    bool has_make_command() const      { return _has_make_command; }
    bool has_make_command(bool val)    { return _has_make_command = val; }

    // True if debugger has `jump' command
    bool has_jump_command() const      { return _has_jump_command; }
    bool has_jump_command(bool val)    { return _has_jump_command = val; }

    // True if debugger has `regs' command
    bool has_regs_command() const      { return _has_regs_command; }
    bool has_regs_command(bool val)    { return _has_regs_command = val; }

    // Non-zero if debugger has `watch' command; also indicates
    // `watch' capabilities
    WatchMode has_watch_command() const { return _has_watch_command; }
    WatchMode has_watch_command(WatchMode val)
    { 
	return _has_watch_command = val;
    }

    // True if debugger issues `NAME = VALUE' upon `print' commands
    bool has_named_values() const      { return _has_named_values; }
    bool has_named_values(bool val)    { return _has_named_values = val; }

    // True if debugger has `when' command
    bool has_when_command() const      { return _has_when_command; }
    bool has_when_command(bool val)    { return _has_when_command = val; }

    // True if debugger wants `;' at the end of `when' command specs
    bool has_when_semicolon() const    { return _has_when_semicolon; }
    bool has_when_semicolon(bool val)  { return _has_when_semicolon = val; }

    // True if debugger wants events separated by `,'
    bool wants_delete_comma() const      { return _wants_delete_comma; }
    bool wants_delete_comma(bool val)    { return _wants_delete_comma = val; }

    // True if debugger has stderr redirection using `>&'
    bool has_err_redirection() const   { return _has_err_redirection; }
    bool has_err_redirection(bool val) { return _has_err_redirection = val; }

    // True if debugger has `givenfile' command
    bool has_givenfile_command() const   { return _has_givenfile_command; }
    bool has_givenfile_command(bool val) 
    {
	return _has_givenfile_command = val;
    }

    // True if debugger has `cont sig' command
    bool has_cont_sig_command() const   { return _has_cont_sig_command; }
    bool has_cont_sig_command(bool val) { return _has_cont_sig_command = val; }

    // True if debugger has `examine' or `x' command
    bool has_examine_command() const   { return _has_examine_command; }
    bool has_examine_command(bool val) { return _has_examine_command = val; }

    // True if debugger has `rerun' command
    bool has_rerun_command() const   { return _has_rerun_command; }
    bool has_rerun_command(bool val) { return _has_rerun_command = val; }

    // True if `rerun' command clears current arguments
    bool rerun_clears_args() const   { return _rerun_clears_args; }
    bool rerun_clears_args(bool val) { return _rerun_clears_args = val; }

    // True if debugger has `attach' command
    bool has_attach_command() const   { return _has_attach_command; }
    bool has_attach_command(bool val) { return _has_attach_command = val; }

    // True if debugger has `addproc' command
    bool has_addproc_command() const   { return _has_addproc_command; }
    bool has_addproc_command(bool val) { return _has_addproc_command = val; }

    // True if debugger has `debug' command
    bool has_debug_command() const   { return _has_debug_command; }
    bool has_debug_command(bool val) { return _has_debug_command = val; }

    // True if debugger is the Windriver variant of GDB
    bool is_windriver_gdb() const   { return _is_windriver_gdb; }
    bool is_windriver_gdb(bool val) { return _is_windriver_gdb = val; }

    // Current program language
    ProgramLanguage program_language() const   { return _program_language; }
    ProgramLanguage program_language(ProgramLanguage val) 
    {
	if (_program_language != val)
	{
	    _program_language = val;
	    callHandlers(LanguageChanged, (void *)val);
	}
	return program_language();
    }
    ProgramLanguage program_language(string text);

    // True if debugger can enable breakpoints
    bool has_disassembly() const
    { 
      return type() == GDB 
	// || type() == PYDB  // In theory this could be done.
	;
    }

    // True if debugger can enable breakpoints
    bool has_enable_command() const
    { 
      return type() == BASH || type() == DBG || type() == GDB  
	|| type() == PYDB || type() == XDB || has_handler_command();
    }
    bool has_disable_command() const
    {
	return has_enable_command();
    }

    // True if debugger can set ignore counts on breakpoints
    bool has_ignore_command() const
    {
	return type() == DBG || type() == GDB || type() == PYDB 
	    || type() == XDB || has_handler_command();
    }

    // True if debugger can set conditions on breakpoints
    bool has_condition_command() const
    {
      return type() == BASH || type() == DBG || type() == GDB 
  	  || type() == PYDB ;
    }

    // True if debugger can delete breakpoints by number
    bool has_delete_command() const
    {
      return type() == BASH || type() == DBG || type() == DBX || type() == GDB 
	  || type() == PYDB || type() == XDB ;
    }

    // True if debugger has volatile breakpoints (i.e. breakpoints may
    // change at any time)
    bool has_volatile_breakpoints() const
    {
	return type() == DBG  || type() == DBX  || type() == GDB 
	  ||   type() == MAKE || type() == PERL || type() == PYDB 
	  ||   type() == XDB;
    }

    // True if debugger supports I/O redirection
    bool has_redirection() const
    {
	return type() == GDB || type() == XDB || 
	       type() == DBX || type() == PERL;
    }

    // True if debugger supports assignments
    bool has_assign_command() const
    {
	// All debuggers except JDB 1.1 can assign
	return !(type() == JDB && has_debug_command());
    }

    // True if debugger supports calling system functions
    bool has_system_calls() const
    {
	return type() == GDB || type() == XDB || type() == DBX || 
	  type() == PYDB || type() == PERL;
    }

    // True if debugger supports loading and examining executables
    bool has_exec_files() const
    {
	return type() == GDB || type() == XDB || type() == DBX;
    }

    // True if debugger supports loading and examining classes
    bool has_classes() const
    {
	return type() == JDB;
    }

    // True if debugger supports loading and examining core files
    bool has_core_files() const
    {
	return type() == GDB || type() == XDB || type() == DBX;
    }

    // True if debugger supports separate processes (attach, detach)
    bool has_processes() const
    {
	return type() == GDB || type() == XDB || type() == DBX;
    }

    // True if debugger supports `cd'
    bool has_cd_command() const
    {
      return type() == BASH || type() == GDB || type() == DBX 
	|| type() == PERL || type() == PYDB  || type() == XDB;
    }

    // True if debugger supports `shell'
    bool has_shell_command() const
    {
      return type() == BASH 
	|| type() == DBX 
	|| type() == GDB 
	|| type() == PERL 
	|| type() == PYDB
	|| type() == MAKE
	|| type() == XDB;
    }

    // True if debugger has numbered breakpoints
    bool has_numbered_breakpoints() const
    {
      return type() == BASH || type() == DBG || type() == DBX || type() == GDB
	  || type() == PYDB || type() == XDB;
    }

    // True if debugger supports temporary breakpoints
    bool has_temporary_breakpoints() const
    {
      return type() == BASH || type() == DBG  || type() == GDB 
	  || type() == PERL || type() == PYDB || type() == XDB 
	  || has_when_command() ;
    }

    // True if debugger supports breakpoint conditions
    bool has_breakpoint_conditions() const
    {
      return type() == BASH || type() == DBG  || type() == DBX 
	  || type() == GDB  || type() == PERL || type() == PYDB 
 	  || type() == XDB;
    }

    // True if debugger supports breakpoint commands
    bool has_breakpoint_commands() const
    {
        return type() == BASH || type() == GDB 
	  || type() == MAKE || has_when_command() || type() == PERL 
	  || type() == PYDB || type() == XDB 
	  ;
    }

    // True if debugger has typed pointers, as in `(TYPE)0x0'
    bool has_typed_pointers() const
    {
	return type() == GDB || type() == JDB;
    }

    // True if debugger has typed structs, as in `(TYPE)0x1234 { ... }'
    bool has_typed_structs() const
    {
	return type() == JDB;
    }

    // True if debugger uses `(scalar = ...)' for out-of-range enumerations
    bool has_scalars() const
    {
	return type() == DBX;
    }

    // True if debugger can use quotes for complex expressions
    bool has_quotes() const
    {
	return type() == GDB;
    }

    // True if `display X' automatically prints X
    bool display_prints_values() const
    {
      return type() == GDB || type() == PYDB ;
    }

    // True if debugger can enable displays
    bool has_enable_display_command() const
    {
	return type() == GDB || type() == PYDB || type() == DBG;
    }
    bool has_disable_display_command() const
    {
	return has_enable_display_command();
    }

    // True if debugger has numbered displays
    bool has_numbered_displays() const
    {
	return type() == GDB || type() == PYDB || type() == DBG;
    }

    // True if debugger wants displays separated by `,'
    bool wants_display_comma() const
    {
	return type() == DBX;
    }

    // True if debugger has `info displays' command
    bool has_info_display_command() const
    {
	return !display_command().empty();
    }

    // True if `undisplay' redisplays remaining values
    bool has_redisplaying_undisplay() const
    {
	return type() == DBX;
    }

    // True if debugger supports breakpoint conditions
    bool has_conditions() const
    {
	return has_condition_command() || type() != JDB;
    }

    // True if arrays are contained in braces
    bool has_array_braces() const { return !is_ladebug(); }

    // True if debugger can disable breakpoints
    bool can_disable() const
    {
	return has_disable_command() || has_conditions();
    }

    // True if debugger can enable breakpoints
    bool can_enable() const
    {
	return has_enable_command() || has_conditions();
    }

    // True if Ladebug
    bool is_ladebug() const;

    // True if a minimum indent is required.  This is true for
    // scripting languages.
    bool requires_script_indent() const
    {
	return program_language() == LANGUAGE_PERL ||
	    program_language() == LANGUAGE_PYTHON;
    }

    // True if DBX output is to be processed verbatim
    bool verbatim() const        { return _verbatim; }
    bool verbatim(bool val)      { return _verbatim = val; }

    // True if we are recording commands
    bool recording() const         { return _recording; }
    bool recording(bool val);

    // True if checking for echos
    bool detect_echos() const        { return _detect_echos; }
    bool detect_echos(bool val)      { return _detect_echos = val; }

    // True if buffering partial answers
    bool buffer_gdb_output() const       { return _buffer_gdb_output; }
    bool buffer_gdb_output(bool val)     { return _buffer_gdb_output = val; }

    // True if next answer is to be flushed
    bool flush_next_output() const       { return _flush_next_output; }
    bool flush_next_output(bool val)     { return _flush_next_output = val; }

    // Place quotes around filename FILE if needed
    string quote_file(const string& file) const;

    // Several commands
				                    // GDB command
						    // -----------------------
    string print_command(const char *expr = "",     // print|output EXP
			 bool internal = true) const;
    string print_command(const string& expr,
			 bool internal = true) const {
      return print_command(expr.chars(), internal);
    }
    string assign_command(const string& var,        // set variable VAR = EXPR
			  const string& expr) const;
    string display_command(const char *expr = "") const; // display EXPR
    string display_command(const string& expr) const {
      return display_command(expr.chars());
    }
    string where_command(int count = 0) const;	    // where COUNT
    string pwd_command() const;	                    // pwd
    string frame_command(int number) const;         // frame NUMBER
    string relative_frame_command(int offset) const;// up|down OFFSET
    string frame_command() const;                   // frame
    string func_command() const;                    // func
    string echo_command(const string& text) const;         // echo TEXT
    string whatis_command(const string& expr) const;       // whatis EXPR
    string dereferenced_expr(const string& expr) const;    // *EXPR
    string address_expr(string expr) const;         // &EXPR
    string index_expr(const string& expr, const string& index) const; // EXPR[INDEX]
    int default_index_base() const;                 // 0 in C, else 1
    string member_separator() const;                // " = " in C

    string info_locals_command() const;	            // info locals
    string info_args_command() const;	            // info args
    string info_display_command() const;	    // info display
    string disassemble_command(string start, const char *end = "") const;
                                                    // disassemble START END
    string disassemble_command(const string &start, const string& end ) const {
      return disassemble_command(start, end.chars() );
    }
    string make_command(const string& target) const;       // make TARGET
    string jump_command(const string& pc) const;    // jump PC
    string regs_command(bool all = true) const;	    // info registers
    string watch_command(const string& expr, WatchMode w = WATCH_CHANGE) const;
				                    // watch EXPR
    string kill_command() const;                    // kill
    string enable_command(string bp = "") const;    // enable BP
    string disable_command(string bp = "") const;   // disable BP
    string delete_command(string bp = "") const;    // delete BP
    string ignore_command(const string& bp, int count) const; 
                                                    // ignore BP COUNT
    string condition_command(const string& bp, const string& expr) const {
      return condition_command(bp, expr.chars() );
    }
    string condition_command(const string& bp, const char *expr) const;
				                    // cond BP EXPR
    string shell_command(const string& cmd) const;	    // shell CMD
    string debug_command(const char *file = "",          // file FILE
			 string args = "") const;
    string debug_command(const string& file, string args = "") const {
      return debug_command( file.chars(), args );
    }
    string signal_command(int sig) const;           // signal SIG
    string nop_command(const char *comment = "") const;  // # comment
    string nop_command(const string& comment) const {
      return nop_command(comment.chars());
    }

    // Bring VALUE of VAR into a form understood by DDD
    void munch_value(string& value, const string& var) const;

    // Split a Perl variable into its components
    static void split_perl_var(const string& var,
			       string& prefix, string& package, string& name);

    // Run program with given arguments
    string run_command(string args) const;	    // set args ARGS\nrun

    // Run program, re-using current arguments
    string rerun_command() const;                   // run

    // Attach to process id PID
    string attach_command(int pid, const string& file) const;  // attach PID
    string detach_command(int pid) const;                      // detach

    // Default history file
    string history_file() const;                    // GDB: ~/.gdb_history

    // Send DATA to process
    virtual int write(const char *data, int length);

    // Custom function
    int write(const string& data)
    {
	return write(data.chars(), data.length());
    }

    // Write a command (not debuggee interaction or control characters)
    int write_cmd(const string& cmd);

    // True if ANSWER ends in a prompt
    bool ends_with_prompt(const string& answer);
    bool ends_with_secondary_prompt(const string& answer) const;
    bool ends_with_yn(const string& answer) const;

    // True if exception error message
    bool is_exception_answer(const string& answer) const;
    void set_exception_state(bool state);

    // Helpers
    string cmd() const;		// Actual command being executed
    string debugger() const;	// Debugger of command
    string args() const;	// Debugger args of command
    string program() const;	// Program from debugger args

private:
    bool questions_waiting;

    void* _qu_data;

    int     qu_index;
    int    _qu_count;
    StringArray cmd_array;
    StringArray complete_answers;
    VoidArray _qu_datas;
    void*   _qa_data;

    OAProc   _on_answer;
    OACProc  _on_answer_completion;
    OQACProc _on_qu_array_completion;

    void    init_qu_array (const StringArray& cmds,
			   const VoidArray& qu_datas,
			   int      qu_count,
			   OQACProc on_qu_array_completion,
			   void*    qa_data);

    string requires_reply(const string& answer);

    void cut_off_prompt(string& answer) const;
    void strip_dbx_comments(string& answer) const;
    void strip_control(string& answer) const;

    void normalize_answer(string& answer) const;
    void normalize_address(string& addr) const;

    void handle_echo(string& answer);
    void handle_more(string& answer);
    void handle_reply(string& answer);
    void handle_input(string& answer);
    void handle_died();

protected:
    string complete_answer;

    static void InputHP  (Agent *, void *, void *);
    static void PanicHP  (Agent *, void *, void *);
    static void StrangeHP(Agent *, void *, void *);
    static void DiedHP   (Agent *, void *, void *);

    static void traceInputHP (Agent *, void *, void *);
    static void traceOutputHP(Agent *, void *, void *);
    static void traceErrorHP (Agent *, void *, void *);

public:    
    // Terminator
    virtual void abort();
};

#endif // _DDD_GDBAgent_h
// DON'T ADD ANYTHING BEHIND THIS #endif
