/*
Copyright (C) 2005, 2007-2008, 2015, 2020 R. Bernstein <rocky@gnu.org>
This file is part of GNU Make (remake variant).

GNU Make 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, or (at your option)
any later version.

GNU Make 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 GNU Make; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/** debugger command stack routines. */

#include <assert.h>
#include "break.h"
#include "msg.h"
#include "filedef.h"
#include "print.h"

/*! Node for an item in the target call stack */
struct breakpoint_node
{
  file_t            *p_target;
  unsigned int      i_num;
  brkpt_mask_t      brkpt_mask;
  breakpoint_node_t *p_next;
};

/** Pointers to top/bottom of current breakpoint target stack */
breakpoint_node_t *p_breakpoint_top    = NULL;
breakpoint_node_t *p_breakpoint_bottom = NULL;

unsigned int i_breakpoints = 0;

/*! Add "p_target" to the list of breakpoints. Return true if
    there were no errors
*/
bool
add_breakpoint (file_t *p_target, const brkpt_mask_t brkpt_mask)
{
  breakpoint_node_t *p_new   = CALLOC (breakpoint_node_t, 1);

  if (!p_new) return false;

  /* Add breakpoint to list of breakpoints. */
  if (!p_breakpoint_top) {
    assert(!p_breakpoint_bottom);
    p_breakpoint_top            = p_breakpoint_bottom = p_new;
  } else {
    p_breakpoint_bottom->p_next = p_new;
  }
  p_breakpoint_bottom           = p_new;
  p_new->p_target               = p_target;
  p_new->i_num                  = ++i_breakpoints;
  p_new->brkpt_mask             = brkpt_mask;


  /* Finally, note that we are tracing this target. */
  if (p_target->tracing & (BRK_BEFORE_PREREQ & brkpt_mask)) {
    dbg_msg(_("Note: prerequisite breakpoint already set at target %s."),
            p_target->name);
  }
  if (p_target->tracing & (BRK_AFTER_PREREQ & brkpt_mask)) {
    dbg_msg(_("Note: command breakpoint already set at target %s."),
            p_target->name);
  }
  if (p_target->tracing & (BRK_AFTER_CMD & brkpt_mask)) {
    dbg_msg(_("Note: target end breakpont set at target %s."),
            p_target->name);
  }
  p_target->tracing = brkpt_mask;
  printf(_("Breakpoint %u on target `%s', mask 0x%02x"), i_breakpoints,
         p_target->name, brkpt_mask);
  if (p_target->floc.filenm)
    dbg_msg(": file %s, line %lu.", p_target->floc.filenm,
            p_target->floc.lineno);
  else
    printf(".\n");
  if (p_target->updated)
      dbg_msg("Warning: target is already updated; so it might not get stopped at again.");
  else if (p_target->updating && (brkpt_mask & (BRK_BEFORE_PREREQ | BRK_AFTER_PREREQ))) {
      dbg_msg("Warning: target is in the process of being updated;");
      dbg_msg("so it might not get stopped at again.");
  }
  return true;

}

/*! Remove breakpoint i from the list of breakpoints. Return true if
    there were no errors
*/
bool
remove_breakpoint (unsigned int i, bool silent)
{
  if (!i) {
    dbg_errmsg(_("Invalid Breakpoint number 0."));
    return false;
  }
  if (i > i_breakpoints) {
    dbg_errmsg(_("Breakpoint number %u is too high. "
                 "%u is the highest breakpoint number."), i, i_breakpoints);
    return false;
  } else {
    /* Find breakpoint i */
    breakpoint_node_t *p_prev = NULL;
    breakpoint_node_t *p;
    for (p = p_breakpoint_top; p && p->i_num != i; p = p->p_next) {
      p_prev = p;
    }

    if (p && p->i_num == i) {
      /* Delete breakpoint */
      if (!p->p_next) p_breakpoint_bottom = p_prev;
      if (p == p_breakpoint_top) p_breakpoint_top = p->p_next;

      if (p_prev) p_prev->p_next = p->p_next;

      if (p->p_target->tracing) {
	p->p_target->tracing = BRK_NONE;
	dbg_msg(_("Breakpoint %u on target `%s' cleared."),
	       i, p->p_target->name);
	free(p);
	return true;
      } else {
	dbg_msg(_("No breakpoint at target `%s'; nothing cleared."),
	       p->p_target->name);
	free(p);
	return false;
      }
    } else {
      if (!silent)
        dbg_errmsg(_("No Breakpoint number %u set."), i);
      return false;
    }
  }
}

/*! List breakpoints.*/
void
list_breakpoints (void)
{
  breakpoint_node_t *p;

  if (!p_breakpoint_top) {
    dbg_msg(_("No breakpoints."));
    return;
  }

  dbg_msg(  "Num Type           Disp Enb Mask Target  Location");
  for (p = p_breakpoint_top; p; p = p->p_next) {
    printf("%3u breakpoint     keep   y 0x%02x %s",
	   p->i_num,
	   p->brkpt_mask,
           p->p_target->name);
    if (p->p_target->floc.filenm) {
	printf(" at ");
	print_floc_prefix(&(p->p_target->floc));
    }
    printf("\n");
  }
}
/*
 * Local variables:
 * eval: (c-set-style "gnu")
 * indent-tabs-mode: nil
 * End:
 */
