/* MI Command Set - breakpoint and watchpoint commands.
   Copyright (C) 2000, Free Software Foundation, Inc.
   Contributed by Cygnus Solutions (a Red Hat company).

   This file is part of GDB.

   This program 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 2 of the License, or
   (at your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "defs.h"
#include "mi-cmds.h"
#include "ui-out.h"
#include "mi-out.h"
#include "breakpoint.h"
#include "gdb_string.h"
#include "mi-getopt.h"
#include "gdb-events.h"
#include "interpreter.h"

/* There really ought to be a global mi .h file for this stuff... */
extern struct gdb_interpreter *mi_interp;

/* Convenience macro for allocting typesafe memory. */

#undef XMALLOC
#define XMALLOC(TYPE) (TYPE*) xmalloc (sizeof (TYPE))

enum
  {
    FROM_TTY = 0
  };

/* Output a single breakpoint. */

static void
breakpoint_notify (int b)
{
  gdb_breakpoint_query (b);
}


struct gdb_events breakpoint_hooks =
{
  breakpoint_notify,
  breakpoint_notify,
  breakpoint_notify,
};


enum bp_type
  {
    REG_BP,
    HW_BP,
    FUT_BP,
    REGEXP_BP
  };

/* Insert a breakpoint. The type of breakpoint is specified by the
   first argument: 
   -break-insert <location> --> insert a regular breakpoint.  
   -break-insert -t <location> --> insert a temporary breakpoint.  
   -break-insert -h <location> --> insert an hardware breakpoint.  
   -break-insert -t -h <location> --> insert a temporary hw bp.
   -break-insert -f <location> --> insert a future breakpoint.  
   -break-insert -r <regexp> --> insert a bp at functions matching
   <regexp> */

enum mi_cmd_result
mi_cmd_break_insert (char *command, char **argv, int argc)
{
  char *address = NULL;
  enum bp_type type = REG_BP;
  int temp_p = 0;
  int thread = -1;
  int ignore_count = 0;
  char *condition = NULL;
  enum gdb_rc rc;
  struct gdb_events *old_hooks;
  enum opt
    {
      HARDWARE_OPT, TEMP_OPT, FUTURE_OPT /*, REGEXP_OPT */ , CONDITION_OPT,
      IGNORE_COUNT_OPT, THREAD_OPT
    };
  static struct mi_opt opts[] =
  {
    {"h", HARDWARE_OPT, 0},
    {"t", TEMP_OPT, 0},
    {"f", FUTURE_OPT, 0},
    {"c", CONDITION_OPT, 1},
    {"i", IGNORE_COUNT_OPT, 1},
    {"p", THREAD_OPT, 1},
    0
  };

  /* Parse arguments. It could be -r or -h or -t, <location> or ``--''
     to denote the end of the option list. */
  int optind = 0;
  char *optarg;
  while (1)
    {
      int opt = mi_getopt ("mi_cmd_break_insert", argc, argv, opts, &optind, &optarg);
      if (opt < 0)
	break;
      switch ((enum opt) opt)
	{
	case TEMP_OPT:
	  temp_p = 1;
	  break;
	case HARDWARE_OPT:
	  type = HW_BP;
	  break;
	case FUTURE_OPT:
	  type = FUT_BP;
	  break;
#if 0
	case REGEXP_OPT:
	  type = REGEXP_BP;
	  break;
#endif
	case CONDITION_OPT:
	  condition = optarg;
	  break;
	case IGNORE_COUNT_OPT:
	  ignore_count = atol (optarg);
	  break;
	case THREAD_OPT:
	  thread = atol (optarg);
	  break;
	}
    }

  if (optind >= argc)
    error ("mi_cmd_break_insert: Missing <location>");
  if (optind < argc - 1)
    error ("mi_cmd_break_insert: Garbage following <location>");
  address = argv[optind];

  /* Now we have what we need, let's insert the breakpoint! */
  old_hooks = set_gdb_event_hooks (&breakpoint_hooks);
  switch (type)
    {
    case REG_BP:
      rc = gdb_breakpoint (address, condition,
			   0 /*hardwareflag */ , temp_p,
			   thread, ignore_count, 0);
      break;
    case HW_BP:
      rc = gdb_breakpoint (address, condition,
			   1 /*hardwareflag */ , temp_p,
			   thread, ignore_count, 0);
      break;
    case FUT_BP:
      rc = gdb_breakpoint (address, condition,
			   0, temp_p,
			   thread, ignore_count, 1);
      break;

#if 0
    case REGEXP_BP:
      if (temp_p)
	error ("mi_cmd_break_insert: Unsupported tempoary regexp breakpoint");
      else
	rbreak_command_wrapper (address, FROM_TTY);
      return MI_CMD_DONE;
      break;
#endif
    default:
      internal_error ("mi_cmd_break_insert: Bad switch.");
    }
  set_gdb_event_hooks (old_hooks);

  if (rc == GDB_RC_FAIL)
    return MI_CMD_CAUGHT_ERROR;
  else
    return MI_CMD_DONE;
}

enum wp_type
{
  REG_WP,
  READ_WP,
  ACCESS_WP
};

/* Insert a watchpoint. The type of watchpoint is specified by the
   first argument: 
   -break-watch <expr> --> insert a regular wp.  
   -break-watch -r <expr> --> insert a read watchpoint.
   -break-watch -a <expr> --> insert an access wp. */

enum mi_cmd_result
mi_cmd_break_watch (char *command, char **argv, int argc)
{
  char *expr = NULL;
  enum wp_type type = REG_WP;
  enum opt
    {
      READ_OPT, ACCESS_OPT
    };
  static struct mi_opt opts[] =
  {
    {"r", READ_OPT, 0},
    {"a", ACCESS_OPT, 0},
    0
  };

  /* Parse arguments. */
  int optind = 0;
  char *optarg;
  while (1)
    {
      int opt = mi_getopt ("mi_cmd_break_watch", argc, argv, opts, &optind, &optarg);
      if (opt < 0)
	break;
      switch ((enum opt) opt)
	{
	case READ_OPT:
	  type = READ_WP;
	  break;
	case ACCESS_OPT:
	  type = ACCESS_WP;
	  break;
	}
    }
  if (optind >= argc)
    error ("mi_cmd_break_watch: Missing <expression>");
  if (optind < argc - 1)
    error ("mi_cmd_break_watch: Garbage following <expression>");
  expr = argv[optind];

  /* Now we have what we need, let's insert the watchpoint! */
  switch (type)
    {
    case REG_WP:
#ifdef UI_OUT
      watch_command_wrapper (expr, FROM_TTY);
#endif
      break;
    case READ_WP:
#ifdef UI_OUT
      rwatch_command_wrapper (expr, FROM_TTY);
#endif
      break;
    case ACCESS_WP:
#ifdef UI_OUT
      awatch_command_wrapper (expr, FROM_TTY);
#endif
      break;
    default:
      error ("mi_cmd_break_watch: Unknown watchpoint type.");
    }
  return MI_CMD_DONE;
}

void
mi_interp_create_breakpoint_hook (struct breakpoint *bpt)
{
  CORE_ADDR unusued_addr;
  struct ui_out *saved_ui_out = uiout;
  struct mi_out *tmp_mi_out;

  uiout = mi_interp->interpreter_out;

  /* This is a little inefficient, but it probably isn't worth adding
     a gdb_breakpoint_query that takes a bpt structure... */

  ui_out_list_begin (uiout, "MI_HOOK_RESULT");
  ui_out_field_string (uiout, "HOOK_TYPE", "breakpoint_create");
  gdb_breakpoint_query (bpt->number);
  ui_out_list_end (uiout);
  uiout = saved_ui_out; 
}

void
mi_interp_modify_breakpoint_hook (struct breakpoint *bpt)
{

  CORE_ADDR unusued_addr;
  struct ui_out *saved_ui_out = uiout;
  struct mi_out *tmp_mi_out;

  uiout = mi_interp->interpreter_out;

  /* This is a little inefficient, but it probably isn't worth adding
     a gdb_breakpoint_query that takes a bpt structure... */

  ui_out_list_begin (uiout, "MI_HOOK_RESULT");
  ui_out_field_string (uiout, "HOOK_TYPE", "breakpoint_modify");
  gdb_breakpoint_query (bpt->number);
  ui_out_list_end (uiout);
  uiout = saved_ui_out; 
}

void
mi_interp_delete_breakpoint_hook (struct breakpoint *bpt)
{
  CORE_ADDR unusued_addr;
  struct ui_out *saved_ui_out = uiout;
  struct mi_out *tmp_mi_out;

  uiout = mi_interp->interpreter_out;

  /* This is a little inefficient, but it probably isn't worth adding
     a gdb_breakpoint_query that takes a bpt structure... */

  ui_out_list_begin (uiout, "MI_HOOK_RESULT");
  ui_out_field_string (uiout, "HOOK_TYPE", "breakpoint_delete");
  ui_out_field_int (uiout, "bkptno", bpt->number);
  ui_out_list_end (uiout);
  uiout = saved_ui_out; 

}
