diff libinterp/parse-tree/bp-table.cc @ 24738:3695c2cd69b8

don't use singleton pattern for bp_table * bp-table.h, bp-table.cc (class bp_table): Don't use singleton pattern. Move inside octave namespace. Change all uses. * interpreter-private.h, interpreter-private.cc (__get_bp_table__): New function. * interpreter.h, interpreter.cc (interpreter::m_bp_table): New data member. (interpreter::interpreter): Initialize it. (interpreter::get_bp_table): New function.
author John W. Eaton <jwe@octave.org>
date Mon, 12 Feb 2018 00:58:31 -0500
parents 194eb4bd202b
children 6652d3823428
line wrap: on
line diff
--- a/libinterp/parse-tree/bp-table.cc	Sun Feb 11 10:26:22 2018 -0800
+++ b/libinterp/parse-tree/bp-table.cc	Mon Feb 12 00:58:31 2018 -0500
@@ -36,7 +36,6 @@
 #include <iostream>
 
 #include "file-ops.h"
-#include "singleton-cleanup.h"
 
 #include "bp-table.h"
 #include "defun-int.h"
@@ -56,940 +55,919 @@
 #include "sighandlers.h"
 #include "symtab.h"
 
-// Initialize the singleton object
-bp_table *bp_table::instance = nullptr;
-
-std::set<std::string> bp_table::errors_that_stop;
-std::set<std::string> bp_table::caught_that_stop;
-std::set<std::string> bp_table::warnings_that_stop;
-
-// Return a pointer to the user-defined function FNAME.  If FNAME is empty,
-// search backward for the first user-defined function in the
-// current call stack.
-
-octave_user_code *
-get_user_code (const std::string& fname)
-{
-  octave_user_code *dbg_fcn = nullptr;
-
-  if (fname.empty ())
-    {
-      octave::call_stack& cs = octave::__get_call_stack__ ("get_user_code");
-
-      dbg_fcn = cs.debug_user_code ();
-    }
-  else
-    {
-      std::string name = fname;
-
-      if (octave::sys::file_ops::dir_sep_char () != '/' && name[0] == '@')
-        {
-          auto beg = name.begin () + 2;  // never have @/method
-          auto end = name.end () - 1;    // never have trailing '/'
-          std::replace (beg, end, '/', octave::sys::file_ops::dir_sep_char ());
-        }
-
-      size_t name_len = name.length ();
-
-      if (name_len > 2 && name.substr (name_len-2) == ".m")
-        name = name.substr (0, name_len-2);
-
-      octave::symbol_table& symtab =
-        octave::__get_symbol_table__ ("get_user_code");
-
-      octave_value fcn = symtab.find_function (name);
-
-      if (fcn.is_defined () && fcn.is_user_code ())
-        dbg_fcn = fcn.user_code_value ();
-    }
-
-  return dbg_fcn;
-}
-
-// Return true if there is a valid breakpoint table, false otherwise.
-// If no table exists, one is created; false is only returned if this fails.
-bool
-bp_table::instance_ok (void)
-{
-  if (! instance)
-    {
-      instance = new bp_table ();
-
-      if (instance)
-        singleton_cleanup_list::add (cleanup_instance);
-    }
-
-  if (! instance)
-    error ("unable to create breakpoint table!");
-
-  return true;
-}
-
-// Clear all reasons to stop, other than breakpoints
-void
-bp_table::dbclear_all_signals (void)
-{
-  Vdebug_on_error = false;
-  bp_table::errors_that_stop.clear ();
-
-  Vdebug_on_caught = false;
-  bp_table::caught_that_stop.clear ();
-
-  Vdebug_on_warning = false;
-  bp_table::warnings_that_stop.clear ();
-
-  octave::Vdebug_on_interrupt = false;
-}
-
-// Process the "warn", "errs", "caught" and "intr" fields for a call of
-// "dbstop (p)".
-void
-bp_table::dbstop_process_map_args (const octave_map& mv)
+namespace octave
 {
-  // process errs
-  // why so many levels of indirection needed?
-  bool fail = false;
-  Cell U = mv.contents ("errs");
-  if (U.numel () != 1)
-    fail = (U.numel () > 1);
-  else
-    {
-      Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
-      if (W.isempty () || W(0).isempty ())
-        Vdebug_on_error = 1;    // like "dbstop if error" with no identifier
-      else if (! W(0).iscell ())
-        fail = true;
-      else
-        {
-          Cell V = W(0).cell_value ();
-          for (int i = 0; i < V.numel (); i++)
-            {
-              errors_that_stop.insert (V(i).string_value ());
-              Vdebug_on_error = 1;
-            }
-        }
-    }
-  if (fail)
-    error ("dbstop: invalid 'errs' field");
+  // Clear all reasons to stop, other than breakpoints.
+
+  void bp_table::dbclear_all_signals (void)
+  {
+    Vdebug_on_error = false;
+    bp_table::m_errors_that_stop.clear ();
+
+    Vdebug_on_caught = false;
+    bp_table::m_caught_that_stop.clear ();
+
+    Vdebug_on_warning = false;
+    bp_table::m_warnings_that_stop.clear ();
+
+    octave::Vdebug_on_interrupt = false;
+  }
+
+  // Process the "warn", "errs", "caught" and "intr" fields for a call of
+  // "dbstop (p)".
 
-  // process caught
-  // why so many levels of indirection needed?
-  fail = false;
-  U = mv.contents ("caught");
-  if (U.numel () != 1)
-    fail = (U.numel () > 1);
-  else
-    {
-      Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
-      if (W.isempty () || W(0).isempty ())
-        Vdebug_on_caught = 1;    // like "dbstop if caught error" with no ID
-      else if (! W(0).iscell ())
-        fail = true;
-      else
-        {
-          Cell V = W(0).cell_value ();
-          for (int i = 0; i < V.numel (); i++)
-            {
-              caught_that_stop.insert (V(i).string_value ());
-              Vdebug_on_caught = 1;
-            }
-        }
-    }
-  if (fail)
-    error ("dbstop: invalid 'caught' field");
+  void bp_table::dbstop_process_map_args (const octave_map& mv)
+  {
+    // process errs
+    // why so many levels of indirection needed?
+    bool fail = false;
+    Cell U = mv.contents ("errs");
+    if (U.numel () != 1)
+      fail = (U.numel () > 1);
+    else
+      {
+        Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
+        if (W.isempty () || W(0).isempty ())
+          Vdebug_on_error = 1;    // like "dbstop if error" with no identifier
+        else if (! W(0).iscell ())
+          fail = true;
+        else
+          {
+            Cell V = W(0).cell_value ();
+            for (int i = 0; i < V.numel (); i++)
+              {
+                m_errors_that_stop.insert (V(i).string_value ());
+                Vdebug_on_error = 1;
+              }
+          }
+      }
+    if (fail)
+      error ("dbstop: invalid 'errs' field");
+
+    // process caught
+    // why so many levels of indirection needed?
+    fail = false;
+    U = mv.contents ("caught");
+    if (U.numel () != 1)
+      fail = (U.numel () > 1);
+    else
+      {
+        Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
+        if (W.isempty () || W(0).isempty ())
+          Vdebug_on_caught = 1;    // like "dbstop if caught error" with no ID
+        else if (! W(0).iscell ())
+          fail = true;
+        else
+          {
+            Cell V = W(0).cell_value ();
+            for (int i = 0; i < V.numel (); i++)
+              {
+                m_caught_that_stop.insert (V(i).string_value ());
+                Vdebug_on_caught = 1;
+              }
+          }
+      }
+    if (fail)
+      error ("dbstop: invalid 'caught' field");
 
-  // process warn
-  // why so many levels of indirection needed?
-  fail = false;
-  U = mv.contents ("warn");
-  if (U.numel () != 1)
-    fail = (U.numel () > 1);
-  else
-    {
-      Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
-      if (W.isempty () || W(0).isempty ())
-        Vdebug_on_warning = 1;    // like "dbstop if warning" with no identifier
-      else if (! W(0).iscell ())
-        fail = true;
-      else
-        {
-          Cell V = W(0).cell_value ();
-          for (int i = 0; i < V.numel (); i++)
-            {
-              warnings_that_stop.insert (V(i).string_value ());
-              Vdebug_on_warning = 1;
-            }
-        }
-    }
-  if (fail)
-    error ("dbstop: invalid 'warn' field");
+    // process warn
+    // why so many levels of indirection needed?
+    fail = false;
+    U = mv.contents ("warn");
+    if (U.numel () != 1)
+      fail = (U.numel () > 1);
+    else
+      {
+        Array<octave_value> W = U.index (static_cast<octave_idx_type> (0));
+        if (W.isempty () || W(0).isempty ())
+          Vdebug_on_warning = 1;    // like "dbstop if warning" with no identifier
+        else if (! W(0).iscell ())
+          fail = true;
+        else
+          {
+            Cell V = W(0).cell_value ();
+            for (int i = 0; i < V.numel (); i++)
+              {
+                m_warnings_that_stop.insert (V(i).string_value ());
+                Vdebug_on_warning = 1;
+              }
+          }
+      }
+    if (fail)
+      error ("dbstop: invalid 'warn' field");
 
-  // process interrupt
-  if (mv.isfield ("intr"))
-    octave::Vdebug_on_interrupt = 1;
-}
+    // process interrupt
+    if (mv.isfield ("intr"))
+      octave::Vdebug_on_interrupt = 1;
+  }
+
+  // Insert a breakpoint in function fcn at line within file fname,
+  // to stop only when condition is true.
+  // Record in m_bp_set that fname contains a breakpoint.
+
+  bool bp_table::add_breakpoint_1 (octave_user_code *fcn,
+                                   const std::string& fname,
+                                   const bp_table::intmap& line,
+                                   const std::string& condition,
+                                   bp_table::intmap& retval)
+  {
+    bool found = false;
+
+    octave::tree_statement_list *cmds = fcn->body ();
+
+    std::string file = fcn->fcn_file_name ();
+
+    if (cmds)
+      {
+        retval = cmds->add_breakpoint (file, line, condition);
 
-// Insert a breakpoint in function fcn at line within file fname,
-// to stop only when condition is true.
-// Record in bp_set that fname contains a breakpoint.
-bool
-bp_table::do_add_breakpoint_1 (octave_user_code *fcn,
-                               const std::string& fname,
-                               const bp_table::intmap& line,
-                               const std::string& condition,
-                               bp_table::intmap& retval)
-{
-  bool found = false;
+        for (auto& idx_line_p : retval)
+          {
+            if (idx_line_p.second != 0)
+              {
+                // Normalize to store only the file name.
+                // Otherwise, there can be an entry for both
+                // file>subfunction and file, which causes a crash on
+                // dbclear all
+                const char *s = strchr (fname.c_str (), '>');
+                if (s)
+                  m_bp_set.insert (fname.substr (0, s - fname.c_str ()));
+                else
+                  m_bp_set.insert (fname);
+                found = true;
+                break;
+              }
+          }
+      }
 
-  octave::tree_statement_list *cmds = fcn->body ();
-
-  std::string file = fcn->fcn_file_name ();
+    return found;
+  }
 
-  if (cmds)
-    {
-      retval = cmds->add_breakpoint (file, line, condition);
+  // Cursory check that cond is a valid condition to use for a breakpoint.
+  // Currently allows conditions with side-effects, like 'y+=10' and 'y++';
+  // it is odd that the former is not flagged by "is_assignment_expression".
+  // Throws an exception if not valid.
 
-      for (auto& idx_line_p : retval)
-        {
-          if (idx_line_p.second != 0)
-            {
-              // Normalize to store only the file name.
-              // Otherwise, there can be an entry for both file>subfunction and
-              // file, which causes a crash on dbclear all
-              const char *s = strchr (fname.c_str (), '>');
-              if (s)
-                bp_set.insert (fname.substr (0, s - fname.c_str ()));
-              else
-                bp_set.insert (fname);
-              found = true;
-              break;
-            }
-        }
-    }
+  bool bp_table::condition_valid (const std::string& cond)
+  {
+    if (cond.length () > 0)
+      {
+        octave::parser parser (cond + " ;"); // ; to reject partial expr like "y=="
+        parser.reset ();
+        int parse_status = parser.run ();
+        if (parse_status)
+          error ("dbstop: Cannot parse condition '%s'", cond.c_str ());
+        else
+          {
+            octave::tree_statement *stmt = nullptr;
+            if (! parser.m_stmt_list)
+              error ("dbstop: "
+                     "condition is not empty, but has nothing to evaluate");
+            else
+              {
+                if (parser.m_stmt_list->length () == 1
+                    && (stmt = parser.m_stmt_list->front ())
+                    && stmt->is_expression ())
+                  {
+                    octave::tree_expression *expr = stmt->expression ();
+                    if (expr->is_assignment_expression ())
+                      error ("dbstop: condition cannot be an assignment.  "
+                             "Did you mean '=='?");
+                  }
+                else
+                  error ("dbstop: condition must be an expression");
+              }
+          }
+      }
+    return true;
+  }
 
-  return found;
-}
-
-// Cursory check that cond is a valid condition to use for a breakpoint.
-// Currently allows conditions with side-effects, like 'y+=10' and 'y++';
-// it is odd that the former is not flagged by "is_assignment_expression".
-// Throws an exception if not valid.
-bool
-bp_table::condition_valid (const std::string& cond)
-{
-  if (cond.length () > 0)
+  enum dbstop_args
     {
-      octave::parser parser (cond + " ;"); // ; to reject partial expr like "y=="
-      parser.reset ();
-      int parse_status = parser.run ();
-      if (parse_status)
-        error ("dbstop: Cannot parse condition '%s'", cond.c_str ());
-      else
-        {
-          octave::tree_statement *stmt = nullptr;
-          if (! parser.m_stmt_list)
-            error ("dbstop: "
-                   "condition is not empty, but has nothing to evaluate");
-          else
-            {
-              if (parser.m_stmt_list->length () == 1
-                  && (stmt = parser.m_stmt_list->front ())
-                  && stmt->is_expression ())
-                {
-                  octave::tree_expression *expr = stmt->expression ();
-                  if (expr->is_assignment_expression ())
-                    error ("dbstop: condition cannot be an assignment.  "
-                           "Did you mean '=='?");
-                }
-              else
-                error ("dbstop: condition must be an expression");
-            }
-        }
-    }
-  return true;
-}
+      dbstop_in,
+      dbstop_at,
+      dbstop_if,
+      dbstop_none
+    };
+
+  // Parse parameters (args) of dbstop and dbclear commands.
+  // For dbstop, who=="dbstop"; for dbclear, who=="dbclear".
+  // The syntax is: dbstop [[in] symbol] [[at] line [line [...]]] [if condition]
+  // where the form of condition depends on whether or not a file or line has
+  // been seen.
+  // Also execute "if [error|warning|interrupt|naninf]" clauses.
+
+  void bp_table::parse_dbfunction_params (const char *who,
+                                          const octave_value_list& args,
+                                          std::string& symbol_name,
+                                          bp_table::intmap& lines,
+                                          std::string& cond)
+  {
+    int nargin = args.length ();
+    int list_idx = 0;
+    symbol_name = "";
+    lines = bp_table::intmap ();
+
+    if (nargin == 0 || ! args(0).is_string ())
+      print_usage (who);
 
-enum
-dbstop_args {dbstop_in, dbstop_at, dbstop_if, dbstop_none};
-
-// Parse parameters (args) of dbstop and dbclear commands.
-// For dbstop, who=="dbstop"; for dbclear, who=="dbclear".
-// The syntax is: dbstop [[in] symbol] [[at] line [line [...]]] [if condition]
-// where the form of condition depends on whether or not a file or line has
-// been seen.
-// Also execute "if [error|warning|interrupt|naninf]" clauses.
-void
-bp_table::parse_dbfunction_params (const char *who,
-                                   const octave_value_list& args,
-                                   std::string& symbol_name,
-                                   bp_table::intmap& lines,
-                                   std::string& cond)
-{
-  int nargin = args.length ();
-  int list_idx = 0;
-  symbol_name = "";
-  lines = bp_table::intmap ();
-
-  if (nargin == 0 || ! args(0).is_string ())
-    print_usage (who);
+    // elements already processed
+    bool seen_in = false, seen_at = false, seen_if = false;
+    int pos = 0;
+    dbstop_args tok = dbstop_none;
+    while (pos < nargin)
+      {
+        // allow "in" and "at" to be implicit
+        if (args(pos).is_string ())
+          {
+            std::string arg = args(pos).string_value ();
+            if (arg == "in")
+              {
+                tok = dbstop_in;
+                pos++;
+              }
+            else if (arg == "at")
+              {
+                tok = dbstop_at;
+                pos++;
+              }
+            else if (arg == "if")
+              {
+                tok = dbstop_if;
+                pos++;
+              }
+            else if (atoi (args(pos).string_value ().c_str ()) > 0)
+              tok = dbstop_at;
+            else
+              tok = dbstop_in;
+          }
+        else
+          tok = dbstop_at;
 
-  // elements already processed
-  bool seen_in = false, seen_at = false, seen_if = false;
-  int pos = 0;
-  dbstop_args tok = dbstop_none;
-  while (pos < nargin)
-    {
-      // allow "in" and "at" to be implicit
-      if (args(pos).is_string ())
-        {
-          std::string arg = args(pos).string_value ();
-          if (arg == "in")
-            {
-              tok = dbstop_in;
-              pos++;
-            }
-          else if (arg == "at")
-            {
-              tok = dbstop_at;
-              pos++;
-            }
-          else if (arg == "if")
-            {
-              tok = dbstop_if;
-              pos++;
-            }
-          else if (atoi (args(pos).string_value ().c_str ()) > 0)
-            tok = dbstop_at;
-          else
-            tok = dbstop_in;
-        }
-      else
-        tok = dbstop_at;
+        if (pos >= nargin)
+          error ("%s: '%s' missing argument", who,
+                 (tok == dbstop_in
+                  ? "in" : (tok == dbstop_at ? "at" : "if")));
+
+        // process the actual arguments
+        switch (tok)
+          {
+          case dbstop_in:
+            symbol_name = args(pos).string_value ();
+            if (seen_in)
+              error ("%s: Too many function names specified -- %s",
+                     who, symbol_name.c_str ());
+            else if (seen_at || seen_if)
+              error ("%s: function name must come before line number and 'if'",
+                     who);
+            seen_in = true;
+            pos++;
+            break;
+
+          case dbstop_at:
+            if (seen_at)
+              error ("%s: Only one 'at' clause is allowed -- %s",
+                     who, args(pos).string_value ().c_str ());
+            else if (seen_if)
+              error ("%s: line number must come before 'if' clause\n");
+            seen_at = true;
 
-      if (pos >= nargin)
-        error ("%s: '%s' missing argument", who,
-               (tok == dbstop_in
-                ? "in" : (tok == dbstop_at ? "at" : "if")));
+            if (! seen_in)
+              {
+                // It was a line number.  Get function name from debugger.
+                if (Vdebugging)
+                  symbol_name = get_user_code ()->profiler_name ();
+                else
+                  error ("%s: function name must come before line number "
+                         "and 'if'", who);
+                seen_in = true;
+              }
+            else if (seen_if)
+              error ("%s: line number must come before 'if' clause\n");
 
-      // process the actual arguments
-      switch (tok)
-        {
-        case dbstop_in:
-          symbol_name = args(pos).string_value ();
-          if (seen_in)
-            error ("%s: Too many function names specified -- %s",
-                   who, symbol_name.c_str ());
-          else if (seen_at || seen_if)
-            error ("%s: function name must come before line number and 'if'",
-                   who);
-          seen_in = true;
-          pos++;
-          break;
+            // Read a list of line numbers (or arrays thereof)
+            for ( ; pos < nargin; pos++)
+              {
+                if (args(pos).is_string ())
+                  {
+                    int line = atoi (args(pos).string_value ().c_str ());
 
-        case dbstop_at:
-          if (seen_at)
-            error ("%s: Only one 'at' clause is allowed -- %s",
-                   who, args(pos).string_value ().c_str ());
-          else if (seen_if)
-            error ("%s: line number must come before 'if' clause\n");
-          seen_at = true;
+                    if (line > 0)
+                      lines[list_idx++] = line;
+                    else
+                      break;        // may be "if"
+                  }
+                else if (args(pos).isnumeric ())
+                  {
+                    const NDArray arg = args(pos).array_value ();
+
+                    for (octave_idx_type j = 0; j < arg.numel (); j++)
+                      lines[list_idx++] = static_cast<int> (arg.elem (j));
+                  }
+                else
+                  error ("%s: Invalid argument type %s",
+                         args(pos).type_name ().c_str ());
+              }
+            break;
 
-          if (! seen_in)
-            {
-              // It was a line number.  Get function name from debugger.
-              if (Vdebugging)
-                symbol_name = get_user_code ()->profiler_name ();
-              else
-                error ("%s: function name must come before line number "
-                       "and 'if'", who);
-              seen_in = true;
-            }
-          else if (seen_if)
-            error ("%s: line number must come before 'if' clause\n");
+          case dbstop_if:
+            if (seen_in)    // conditional breakpoint
+              {
+                cond = "";  // remaining arguments form condition
+                for (; pos < nargin; pos++)
+                  {
+                    if (args(pos).is_string ())
+                      cond += ' ' + args(pos).string_value ();
+                    else
+                      error ("%s: arguments to 'if' must all be strings", who);
+                  }
 
-          // Read a list of line numbers (or arrays thereof)
-          for ( ; pos < nargin; pos++)
-            {
-              if (args(pos).is_string ())
-                {
-                  int line = atoi (args(pos).string_value ().c_str ());
+                cond = cond.substr (1);   // omit initial space
+              }
+            else    // stop on event (error, warning, interrupt, NaN/inf)
+              {
+                std::string condition = args(pos).string_value ();
+                int on_off = ! strcmp(who, "dbstop");
 
-                  if (line > 0)
-                    lines[list_idx++] = line;
-                  else
-                    break;        // may be "if"
-                }
-              else if (args(pos).isnumeric ())
-                {
-                  const NDArray arg = args(pos).array_value ();
+                // list of error/warning IDs to update
+                std::set<std::string> *id_list = nullptr;
+                bool *stop_flag = nullptr;         // Vdebug_on_... flag
 
-                  for (octave_idx_type j = 0; j < arg.numel (); j++)
-                    lines[list_idx++] = static_cast<int> (arg.elem (j));
-                }
-              else
-                error ("%s: Invalid argument type %s",
-                       args(pos).type_name ().c_str ());
-            }
-          break;
-
-        case dbstop_if:
-          if (seen_in)    // conditional breakpoint
-            {
-              cond = "";  // remaining arguments form condition
-              for (; pos < nargin; pos++)
-                {
-                  if (args(pos).is_string ())
-                    cond += ' ' + args(pos).string_value ();
-                  else
-                    error ("%s: arguments to 'if' must all be strings", who);
-                }
-
-              cond = cond.substr (1);   // omit initial space
-            }
-          else    // stop on event (error, warning, interrupt, NaN/inf)
-            {
-              std::string condition = args(pos).string_value ();
-              int on_off = ! strcmp(who, "dbstop");
-
-              // list of error/warning IDs to update
-              std::set<std::string> *id_list = nullptr;
-              bool *stop_flag = nullptr;         // Vdebug_on_... flag
+                if (condition == "error")
+                  {
+                    id_list = &m_errors_that_stop;
+                    stop_flag = &Vdebug_on_error;
+                  }
+                else if (condition == "warning")
+                  {
+                    id_list = &m_warnings_that_stop;
+                    stop_flag = &Vdebug_on_warning;
+                  }
+                else if (condition == "caught" && nargin > pos+1
+                         && args(pos+1).string_value () == "error")
+                  {
+                    id_list = &m_caught_that_stop;
+                    stop_flag = &Vdebug_on_caught;
+                    pos++;
+                  }
+                else if (condition == "interrupt")
+                  {
+                    octave::Vdebug_on_interrupt = on_off;
+                  }
+                else if (condition == "naninf")
+                  {
+#if defined (DBSTOP_NANINF)
+                    Vdebug_on_naninf = on_off;
+                    enable_fpe (on_off);
+#else
+                    warning ("%s: condition '%s' not yet supported",
+                             who, condition.c_str ());
+#endif
+                  }
+                else
+                  error ("%s: invalid condition %s",
+                         who, condition.c_str ());
 
-              if (condition == "error")
-                {
-                  id_list = &bp_table::errors_that_stop;
-                  stop_flag = &Vdebug_on_error;
-                }
-              else if (condition == "warning")
-                {
-                  id_list = &bp_table::warnings_that_stop;
-                  stop_flag = &Vdebug_on_warning;
-                }
-              else if (condition == "caught" && nargin > pos+1
-                       && args(pos+1).string_value () == "error")
-                {
-                  id_list = &bp_table::caught_that_stop;
-                  stop_flag = &Vdebug_on_caught;
-                  pos++;
-                }
-              else if (condition == "interrupt")
-                {
-                  octave::Vdebug_on_interrupt = on_off;
-                }
-              else if (condition == "naninf")
-                {
-#if defined (DBSTOP_NANINF)
-                  Vdebug_on_naninf = on_off;
-                  enable_fpe (on_off);
-#else
-                  warning ("%s: condition '%s' not yet supported",
-                           who, condition.c_str ());
-#endif
-                }
-              else
-                error ("%s: invalid condition %s",
-                       who, condition.c_str ());
+                // process ID list for "dbstop if error <error_ID>" etc
+                if (id_list)
+                  {
+                    pos++;
+                    if (pos < nargin)       // only affect a single error ID
+                      {
+                        if (! args(pos).is_string () || nargin > pos+1)
+                          error ("%s: ID must be a single string", who);
+                        else if (on_off == 1)
+                          {
+                            id_list->insert (args(pos).string_value ());
+                            *stop_flag = true;
+                          }
+                        else
+                          {
+                            id_list->erase (args(pos).string_value ());
+                            if (id_list->empty ())
+                              *stop_flag = false;
+                          }
+                      }
+                    else   // unqualified.  Turn all on or off
+                      {
+                        id_list->clear ();
+                        *stop_flag = on_off;
+                        if (stop_flag == &Vdebug_on_error)
+                          {
+                            // Matlab stops on both.
+                            octave::Vdebug_on_interrupt = on_off;
+                          }
+                      }
+                  }
+
+                pos = nargin;
+              }
+            break;
 
-              // process ID list for "dbstop if error <error_ID>" etc
-              if (id_list != nullptr)
-                {
-                  pos++;
-                  if (pos < nargin)       // only affect a single error ID
-                    {
-                      if (! args(pos).is_string () || nargin > pos+1)
-                        error ("%s: ID must be a single string", who);
-                      else if (on_off == 1)
-                        {
-                          id_list->insert (args(pos).string_value ());
-                          *stop_flag = true;
-                        }
-                      else
-                        {
-                          id_list->erase (args(pos).string_value ());
-                          if (id_list->empty ())
-                            *stop_flag = false;
-                        }
-                    }
-                  else   // unqualified.  Turn all on or off
-                    {
-                      id_list->clear ();
-                      *stop_flag = on_off;
-                      if (stop_flag == &Vdebug_on_error)
-                        {
-                          // Matlab stops on both.
-                          octave::Vdebug_on_interrupt = on_off;
-                        }
-                    }
-                }
+          default:      // dbstop_none should never occur
+            break;
+          }
+      }
+  }
+
+  /*
+    %!test
+    %! dbclear all;   # Clear out breakpoints before test
+    %! dbstop help;
+    %! dbstop in ls;
+    %! dbstop help at 100;
+    %! dbstop in ls 100;
+    %! dbstop help 201 if a==5;
+    %! dbstop if error Octave:undefined-function;
+    %! s = dbstatus;
+    %! dbclear all;
+    %! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"});
+    %! assert ([s.bkpt(:).line], [48, 100, 201, 58, 100]);
+    %! assert (s.errs, {"Octave:undefined-function"});
+  */
+
+  // Return the sub/nested/main function of MAIN_FCN that contains
+  // line number LINENO of the source file.
+  // If END_LINE != 0, *END_LINE is set to last line of the returned function.
+
+  static octave_user_code * find_fcn_by_line (octave_user_code *main_fcn,
+                                              int lineno, int *end_line = nullptr)
+  {
+    octave_user_code *retval = nullptr;
+    octave_user_code *next_fcn = nullptr;  // 1st function starting after lineno
+
+    // Find innermost nested (or parent) function containing lineno.
+    int earliest_end = std::numeric_limits<int>::max ();
+
+    std::map<std::string, octave_value> subfcns = main_fcn->subfunctions ();
+    for (const auto& str_val_p : subfcns)
+      {
+        if (str_val_p.second.is_user_function ())
+          {
+            auto *dbg_subfcn = str_val_p.second.user_function_value ();
 
-              pos = nargin;
-            }
-          break;
+            // Check if lineno is within dbg_subfcn.
+            // FIXME: we could break when beginning_line() > lineno,
+            // but that makes the code "fragile"
+            // if the order of walking subfcns changes,
+            // for a minor speed improvement in non-critical code.
+            if (dbg_subfcn->ending_line () < earliest_end
+                && dbg_subfcn->ending_line () >= lineno
+                && dbg_subfcn->beginning_line () <= lineno)
+              {
+                earliest_end = dbg_subfcn->ending_line ();
+                retval = find_fcn_by_line (dbg_subfcn, lineno, &earliest_end);
+              }
 
-        default:      // dbstop_none should never occur
-          break;
-        }
-    }
-}
+            // Find the first fcn starting after lineno.
+            // This is used if line is not inside any function.
+            if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn)
+              next_fcn = dbg_subfcn;
+          }
+      }
+
+    // The breakpoint is either in the subfunction found above,
+    // or in the main function, which we check now.
+    if (main_fcn->is_user_function ())
+      {
+        int e = dynamic_cast<octave_user_function *> (main_fcn)->ending_line ();
+        if (e >= lineno && e < earliest_end)
+          retval = main_fcn;
 
-/*
-%!test
-%! dbclear all;   # Clear out breakpoints before test
-%! dbstop help;
-%! dbstop in ls;
-%! dbstop help at 100;
-%! dbstop in ls 100;
-%! dbstop help 201 if a==5;
-%! dbstop if error Octave:undefined-function;
-%! s = dbstatus;
-%! dbclear all;
-%! assert ({s.bkpt(:).name}, {"help", "help", "help>do_contents", "ls", "ls"});
-%! assert ([s.bkpt(:).line], [48, 100, 201, 58, 100]);
-%! assert (s.errs, {"Octave:undefined-function"});
-*/
+        if (! retval)
+          retval = next_fcn;
+      }
+    else  // main_fcn is a script.
+      {
+        if (! retval)
+          retval = main_fcn;
+      }
+
+    if (end_line && earliest_end < *end_line)
+      *end_line = earliest_end;
+
+    return retval;
+  }
 
-// Return the sub/nested/main function of MAIN_FCN that contains
-// line number LINENO of the source file.
-// If END_LINE != 0, *END_LINE is set to last line of the returned function.
-static octave_user_code*
-find_fcn_by_line (octave_user_code *main_fcn, int lineno, int *end_line = nullptr)
-{
-  octave_user_code *retval = nullptr;
-  octave_user_code *next_fcn = nullptr;  // 1st function starting after lineno
+  // Given file name fname, find the subfunction at line and create
+  // a breakpoint there.  Put the system into debug_mode.
+  bp_table::intmap bp_table::add_breakpoint (const std::string& fname,
+                                             const bp_table::intmap& line,
+                                             const std::string& condition)
+  {
+    octave_user_code *main_fcn = get_user_code (fname);
 
-  // Find innermost nested (or parent) function containing lineno.
-  int earliest_end = std::numeric_limits<int>::max ();
+    if (! main_fcn)
+      error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ());
 
-  std::map<std::string, octave_value> subfcns = main_fcn->subfunctions ();
-  for (const auto& str_val_p : subfcns)
-    {
-      if (str_val_p.second.is_user_function ())
-        {
-          auto *dbg_subfcn = str_val_p.second.user_function_value ();
+    condition_valid (condition);  // Throw error if condition not valid.
+
+    intmap retval;
 
-          // Check if lineno is within dbg_subfcn.
-          // FIXME: we could break when beginning_line() > lineno,
-          // but that makes the code "fragile"
-          // if the order of walking subfcns changes,
-          // for a minor speed improvement in non-critical code.
-          if (dbg_subfcn->ending_line () < earliest_end
-              && dbg_subfcn->ending_line () >= lineno
-              && dbg_subfcn->beginning_line () <= lineno)
-            {
-              earliest_end = dbg_subfcn->ending_line ();
-              retval = find_fcn_by_line (dbg_subfcn, lineno, &earliest_end);
-            }
+    octave_idx_type len = line.size ();
+
+    for (int i = 0; i < len; i++)
+      {
+        const_intmap_iterator m = line.find (i);
+
+        if (m != line.end ())
+          {
+            int lineno = m->second;
+
+            octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno);
 
-          // Find the first fcn starting after lineno.
-          // This is used if line is not inside any function.
-          if (dbg_subfcn->beginning_line () >= lineno && ! next_fcn)
-            next_fcn = dbg_subfcn;
-        }
-    }
+            // We've found the right (sub)function.  Now insert the breakpoint.
+            // We insert all breakpoints.
+            // If multiple are in the same function, we insert multiple times.
+            intmap ret_one;
+            if (dbg_fcn
+                && add_breakpoint_1 (dbg_fcn, fname, line, condition, ret_one))
+              retval.insert (std::pair<int,int> (i, ret_one.find (i)->second));
+          }
+      }
 
-  // The breakpoint is either in the subfunction found above,
-  // or in the main function, which we check now.
-  if (main_fcn->is_user_function ())
-    {
-      int e = dynamic_cast<octave_user_function *> (main_fcn)->ending_line ();
-      if (e >= lineno && e < earliest_end)
-        retval = main_fcn;
+    octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
+      || Vdebugging;
+
+    return retval;
+  }
 
-      if (! retval)
-        retval = next_fcn;
-    }
-  else  // main_fcn is a script.
-    {
-      if (! retval)
-        retval = main_fcn;
-    }
+  int bp_table::remove_breakpoint_1 (octave_user_code *fcn,
+                                     const std::string& fname,
+                                     const bp_table::intmap& line)
+  {
+    int retval = 0;
 
-  if (end_line != nullptr && earliest_end < *end_line)
-    *end_line = earliest_end;
+    std::string file = fcn->fcn_file_name ();
 
-  return retval;
-}
+    octave::tree_statement_list *cmds = fcn->body ();
+
+    // FIXME: move the operation on cmds to the tree_statement_list class?
 
-// Given file name fname, find the subfunction at line and create
-// a breakpoint there.  Put the system into debug_mode.
-bp_table::intmap
-bp_table::do_add_breakpoint (const std::string& fname,
-                             const bp_table::intmap& line,
-                             const std::string& condition)
-{
-  octave_user_code *main_fcn = get_user_code (fname);
+    if (cmds)
+      {
+        octave_value_list results = cmds->list_breakpoints ();
 
-  if (! main_fcn)
-    error ("add_breakpoint: unable to find function '%s'\n", fname.c_str ());
+        if (results.length () > 0)
+          {
+            octave_idx_type len = line.size ();
 
-  condition_valid (condition);  // Throw error if condition not valid.
-
-  intmap retval;
-
-  octave_idx_type len = line.size ();
+            for (int i = 0; i < len; i++)
+              {
+                const_intmap_iterator p = line.find (i);
 
-  for (int i = 0; i < len; i++)
-    {
-      const_intmap_iterator m = line.find (i);
+                if (p != line.end ())
+                  {
+                    int lineno = p->second;
 
-      if (m != line.end ())
-        {
-          int lineno = m->second;
+                    cmds->delete_breakpoint (lineno);
 
-          octave_user_code *dbg_fcn = find_fcn_by_line (main_fcn, lineno);
+                    if (! file.empty ())
+                      octave_link::update_breakpoint (false, file, lineno);
+                  }
+              }
 
-          // We've found the right (sub)function.  Now insert the breakpoint.
-          // We insert all breakpoints.
-          // If multiple are in the same function, we insert multiple times.
-          intmap ret_one;
-          if (dbg_fcn
-              && do_add_breakpoint_1 (dbg_fcn, fname, line, condition, ret_one))
-            retval.insert (std::pair<int,int> (i, ret_one.find (i)->second));
-        }
-    }
+            results = cmds->list_breakpoints ();
+
+            bp_set_iterator it = m_bp_set.find (fname);
+            if (results.empty () && it != m_bp_set.end ())
+              m_bp_set.erase (it);
+          }
 
-  octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
-                                       || Vdebugging;
+        retval = results.length ();
+      }
 
-  return retval;
-}
+    return retval;
+  }
 
-int
-bp_table::do_remove_breakpoint_1 (octave_user_code *fcn,
-                                  const std::string& fname,
-                                  const bp_table::intmap& line)
-{
-  int retval = 0;
+  int bp_table::remove_breakpoint (const std::string& fname,
+                                   const bp_table::intmap& line)
+  {
+    int retval = 0;
 
-  std::string file = fcn->fcn_file_name ();
+    octave_idx_type len = line.size ();
 
-  octave::tree_statement_list *cmds = fcn->body ();
-
-  // FIXME: move the operation on cmds to the tree_statement_list class?
+    if (len == 0)
+      {
+        intmap results = remove_all_breakpoints_in_file (fname);
+        retval = results.size ();
+      }
+    else
+      {
+        octave_user_code *dbg_fcn = get_user_code (fname);
 
-  if (cmds)
-    {
-      octave_value_list results = cmds->list_breakpoints ();
+        if (! dbg_fcn)
+          error ("remove_breakpoint: unable to find function %s\n",
+                 fname.c_str ());
 
-      if (results.length () > 0)
-        {
-          octave_idx_type len = line.size ();
+        retval = remove_breakpoint_1 (dbg_fcn, fname, line);
 
-          for (int i = 0; i < len; i++)
-            {
-              const_intmap_iterator p = line.find (i);
+        // Search subfunctions in the order they appear in the file.
 
-              if (p != line.end ())
-                {
-                  int lineno = p->second;
+        const std::list<std::string> subfcn_names
+          = dbg_fcn->subfunction_names ();
+
+        std::map<std::string, octave_value> subfcns
+          = dbg_fcn->subfunctions ();
 
-                  cmds->delete_breakpoint (lineno);
+        for (const auto& subf_nm : subfcn_names)
+          {
+            const auto q = subfcns.find (subf_nm);
 
-                  if (! file.empty ())
-                    octave_link::update_breakpoint (false, file, lineno);
-                }
-            }
-
-          results = cmds->list_breakpoints ();
+            if (q != subfcns.end ())
+              {
+                octave_user_code *dbg_subfcn = q->second.user_code_value ();
 
-          bp_set_iterator it = bp_set.find (fname);
-          if (results.empty () && it != bp_set.end ())
-            bp_set.erase (it);
-        }
+                retval += remove_breakpoint_1 (dbg_subfcn, fname, line);
+              }
+          }
+      }
 
-      retval = results.length ();
-    }
+    octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
+      || Vdebugging;
 
-  return retval;
-}
+    return retval;
+  }
+
+  // Remove all breakpoints from a file, including those in subfunctions.
 
-int
-bp_table::do_remove_breakpoint (const std::string& fname,
-                                const bp_table::intmap& line)
-{
-  int retval = 0;
-
-  octave_idx_type len = line.size ();
+  bp_table::intmap
+  bp_table::remove_all_breakpoints_in_file (const std::string& fname,
+                                            bool silent)
+  {
+    intmap retval;
 
-  if (len == 0)
-    {
-      intmap results = remove_all_breakpoints_in_file (fname);
-      retval = results.size ();
-    }
-  else
-    {
-      octave_user_code *dbg_fcn = get_user_code (fname);
+    octave_user_code *dbg_fcn = get_user_code (fname);
+
+    if (dbg_fcn)
+      {
+        std::string file = dbg_fcn->fcn_file_name ();
+
+        octave::tree_statement_list *cmds = dbg_fcn->body ();
 
-      if (! dbg_fcn)
-        error ("remove_breakpoint: unable to find function %s\n",
-               fname.c_str ());
-
-      retval = do_remove_breakpoint_1 (dbg_fcn, fname, line);
+        if (cmds)
+          {
+            retval = cmds->remove_all_breakpoints (file);
 
-      // Search subfunctions in the order they appear in the file.
+            bp_set_iterator it = m_bp_set.find (fname);
+            if (it != m_bp_set.end ())
+              m_bp_set.erase (it);
+          }
+      }
+    else if (! silent)
+      error ("remove_all_breakpoint_in_file: "
+             "unable to find function %s\n", fname.c_str ());
 
-      const std::list<std::string> subfcn_names
-        = dbg_fcn->subfunction_names ();
-
-      std::map<std::string, octave_value> subfcns
-        = dbg_fcn->subfunctions ();
+    octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
+      || Vdebugging;
 
-      for (const auto& subf_nm : subfcn_names)
-        {
-          const auto q = subfcns.find (subf_nm);
-
-          if (q != subfcns.end ())
-            {
-              octave_user_code *dbg_subfcn = q->second.user_code_value ();
+    return retval;
+  }
 
-              retval += do_remove_breakpoint_1 (dbg_subfcn, fname, line);
-            }
-        }
-    }
+  void bp_table::remove_all_breakpoints (void)
+  {
+    // Odd loop structure required because delete will invalidate m_bp_set iterators
+    for (const_bp_set_iterator it = m_bp_set.begin (), it_next = it;
+         it != m_bp_set.end ();
+         it = it_next)
+      {
+        ++it_next;
+        remove_all_breakpoints_in_file (*it);
+      }
 
-  octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
-                                       || Vdebugging;
-
-  return retval;
-}
+    octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
+      || Vdebugging;
+  }
 
-// Remove all breakpoints from a file, including those in subfunctions
-bp_table::intmap
-bp_table::do_remove_all_breakpoints_in_file (const std::string& fname,
-                                             bool silent)
-{
-  intmap retval;
-
-  octave_user_code *dbg_fcn = get_user_code (fname);
+  std::string find_bkpt_list (octave_value_list slist, std::string match)
+  {
+    std::string retval;
 
-  if (dbg_fcn)
-    {
-      std::string file = dbg_fcn->fcn_file_name ();
+    for (int i = 0; i < slist.length (); i++)
+      {
+        if (slist(i).string_value () == match)
+          {
+            retval = slist(i).string_value ();
+            break;
+          }
+      }
 
-      octave::tree_statement_list *cmds = dbg_fcn->body ();
+    return retval;
+  }
 
-      if (cmds)
-        {
-          retval = cmds->remove_all_breakpoints (file);
+  bp_table::fname_bp_map
+  bp_table::get_breakpoint_list (const octave_value_list& fname_list)
+  {
+    fname_bp_map retval;
+
+    // make copy since changes may invalidate iters of m_bp_set.
+    std::set<std::string> tmp_bp_set = m_bp_set;
 
-          bp_set_iterator it = bp_set.find (fname);
-          if (it != bp_set.end ())
-            bp_set.erase (it);
-        }
-    }
-  else if (! silent)
-    error ("remove_all_breakpoint_in_file: "
-           "unable to find function %s\n", fname.c_str ());
+    for (auto& bp_fname : tmp_bp_set)
+      {
+        if (fname_list.empty ()
+            || find_bkpt_list (fname_list, bp_fname) != "")
+          {
+            octave_user_code *dbg_fcn = get_user_code (bp_fname);
 
-  octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
-                                       || Vdebugging;
-
-  return retval;
-}
+            if (dbg_fcn)
+              {
+                octave::tree_statement_list *cmds = dbg_fcn->body ();
 
-void
-bp_table::do_remove_all_breakpoints (void)
-{
-  // Odd loop structure required because delete will invalidate bp_set iterators
-  for (const_bp_set_iterator it=bp_set.begin (), it_next=it;
-       it != bp_set.end ();
-       it=it_next)
-    {
-      ++it_next;
-      remove_all_breakpoints_in_file (*it);
-    }
+                // FIXME: move the operation on cmds to the
+                //        tree_statement_list class?
+                if (cmds)
+                  {
+                    std::list<bp_type> bkpts = cmds->breakpoints_and_conds ();
 
-  octave::tree_evaluator::debug_mode = bp_table::have_breakpoints ()
-                                       || Vdebugging;
-}
+                    if (! bkpts.empty ())
+                      retval[bp_fname] = bkpts;
+                  }
 
-std::string
-do_find_bkpt_list (octave_value_list slist, std::string match)
-{
-  std::string retval;
+                // look for breakpoints in subfunctions
+                const std::list<std::string> subf_nm
+                  = dbg_fcn->subfunction_names ();
+
+                std::map<std::string, octave_value> subfcns
+                  = dbg_fcn->subfunctions ();
 
-  for (int i = 0; i < slist.length (); i++)
-    {
-      if (slist(i).string_value () == match)
-        {
-          retval = slist(i).string_value ();
-          break;
-        }
-    }
+                for (const auto& subfcn_nm : subf_nm)
+                  {
+                    const auto q = subfcns.find (subfcn_nm);
 
-  return retval;
-}
+                    if (q != subfcns.end ())
+                      {
+                        octave_user_code *dbg_subfcn
+                          = q->second.user_code_value ();
 
-bp_table::fname_bp_map
-bp_table::do_get_breakpoint_list (const octave_value_list& fname_list)
-{
-  fname_bp_map retval;
-
-  // make copy since changes may invalidate iters of bp_set.
-  std::set<std::string> tmp_bp_set = bp_set;
+                        cmds = dbg_subfcn->body ();
+                        if (cmds)
+                          {
+                            std::list<bp_type> bkpts
+                              = cmds->breakpoints_and_conds ();
 
-  for (auto& bp_fname : tmp_bp_set)
-    {
-      if (fname_list.empty ()
-          || do_find_bkpt_list (fname_list, bp_fname) != "")
-        {
-          octave_user_code *dbg_fcn = get_user_code (bp_fname);
+                            if (! bkpts.empty ())
+                              {
+                                std::string key
+                                  = bp_fname + '>' + dbg_subfcn->name ();
 
-          if (dbg_fcn)
-            {
-              octave::tree_statement_list *cmds = dbg_fcn->body ();
+                                retval[key] = bkpts;
+                              }
+                          }
+                      }
+                  }
+              }
+          }
+      }
 
-              // FIXME: move the operation on cmds to the
-              //        tree_statement_list class?
-              if (cmds)
-                {
-                  std::list<bp_type> bkpts = cmds->breakpoints_and_conds ();
+    return retval;
+  }
 
-                  if (! bkpts.empty ())
-                    retval[bp_fname] = bkpts;
-                }
+  // Report the status of "dbstop if error ..." and "dbstop if warning ..."
+  // If to_screen is true, the output goes to octave_stdout; otherwise it is
+  // returned.
+  // If dbstop if error is true but no explicit IDs are specified, the return
+  // value will have an empty field called "errs".  If IDs are specified, the
+  // "errs" field will have a row per ID.  If dbstop if error is false, there
+  // is no "errs" field.  The "warn" field is set similarly by dbstop if warning
 
-              // look for breakpoints in subfunctions
-              const std::list<std::string> subf_nm
-                = dbg_fcn->subfunction_names ();
+  octave_map bp_table::stop_on_err_warn_status (bool to_screen)
+  {
+    octave_map retval;
 
-              std::map<std::string, octave_value> subfcns
-                = dbg_fcn->subfunctions ();
-
-              for (const auto& subfcn_nm : subf_nm)
-                {
-                  const auto q = subfcns.find (subfcn_nm);
-
-                  if (q != subfcns.end ())
-                    {
-                      octave_user_code *dbg_subfcn
-                        = q->second.user_code_value ();
-
-                      cmds = dbg_subfcn->body ();
-                      if (cmds)
-                        {
-                          std::list<bp_type> bkpts
-                            = cmds->breakpoints_and_conds ();
+    // print dbstop if error information
+    if (Vdebug_on_error)
+      {
+        if (m_errors_that_stop.empty ())
+          {
+            if (to_screen)
+              octave_stdout << "stop if error\n";
+            else
+              retval.assign ("errs", octave_value(""));
+          }
+        else
+          {
+            Cell errs (dim_vector (bp_table::m_errors_that_stop.size (), 1));
+            int i = 0;
 
-                          if (! bkpts.empty ())
-                            {
-                              std::string key
-                                = bp_fname + '>' + dbg_subfcn->name ();
+            for (const auto& e : m_errors_that_stop)
+              {
+                if (to_screen)
+                  octave_stdout << "stop if error " << e << "\n";
+                else
+                  errs(i++) = e;
+              }
+            if (! to_screen)
+              retval.assign ("errs", octave_value (errs));
+          }
+      }
 
-                              retval[key] = bkpts;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-  return retval;
-}
+    // print dbstop if caught error information
+    if (Vdebug_on_caught)
+      {
+        if (m_caught_that_stop.empty ())
+          {
+            if (to_screen)
+              octave_stdout << "stop if caught error\n";
+            else
+              retval.assign ("caught", octave_value(""));
+          }
+        else
+          {
+            Cell errs (dim_vector (m_caught_that_stop.size (), 1));
+            int i = 0;
 
-// Report the status of "dbstop if error ..." and "dbstop if warning ..."
-// If to_screen is true, the output goes to octave_stdout; otherwise it is
-// returned.
-// If dbstop if error is true but no explicit IDs are specified, the return
-// value will have an empty field called "errs".  If IDs are specified, the
-// "errs" field will have a row per ID.  If dbstop if error is false, there
-// is no "errs" field.  The "warn" field is set similarly by dbstop if warning
-octave_map
-bp_table::stop_on_err_warn_status (bool to_screen)
-{
-  octave_map retval;
+            for (const auto& e : m_caught_that_stop)
+              {
+                if (to_screen)
+                  octave_stdout << "stop if caught error " << e << "\n";
+                else
+                  errs(i++) = e;
+              }
+            if (! to_screen)
+              retval.assign ("caught", octave_value (errs));
+          }
+      }
 
-  // print dbstop if error information
-  if (Vdebug_on_error)
-    {
-      if (errors_that_stop.empty ())
-        {
-          if (to_screen)
-            octave_stdout << "stop if error\n";
-          else
-            retval.assign ("errs", octave_value(""));
-        }
-      else
-        {
-          Cell errs (dim_vector (bp_table::errors_that_stop.size (), 1));
-          int i = 0;
-
-          for (const auto& e : errors_that_stop)
-            {
-              if (to_screen)
-                octave_stdout << "stop if error " << e << "\n";
-              else
-                errs(i++) = e;
-            }
-          if (! to_screen)
-            retval.assign ("errs", octave_value (errs));
-        }
-    }
+    // print dbstop if warning information
+    if (Vdebug_on_warning)
+      {
+        if (m_warnings_that_stop.empty ())
+          {
+            if (to_screen)
+              octave_stdout << "stop if warning\n";
+            else
+              retval.assign ("warn", octave_value(""));
+          }
+        else
+          {
+            Cell warn (dim_vector (m_warnings_that_stop.size (), 1));
+            int i = 0;
 
-  // print dbstop if caught error information
-  if (Vdebug_on_caught)
-    {
-      if (caught_that_stop.empty ())
-        {
-          if (to_screen)
-            octave_stdout << "stop if caught error\n";
-          else
-            retval.assign ("caught", octave_value(""));
-        }
-      else
-        {
-          Cell errs (dim_vector (caught_that_stop.size (), 1));
-          int i = 0;
+            for (const auto& w : m_warnings_that_stop)
+              {
+                if (to_screen)
+                  octave_stdout << "stop if warning " << w << "\n";
+                else
+                  warn(i++) = w;
+              }
+            if (! to_screen)
+              retval.assign ("warn", octave_value (warn));
+          }
+      }
 
-          for (const auto& e : caught_that_stop)
-            {
-              if (to_screen)
-                octave_stdout << "stop if caught error " << e << "\n";
-              else
-                errs(i++) = e;
-            }
-          if (! to_screen)
-            retval.assign ("caught", octave_value (errs));
-        }
-    }
+    // print dbstop if interrupt information
+    if (octave::Vdebug_on_interrupt)
+      {
+        if (to_screen)
+          octave_stdout << "stop if interrupt\n";
+        else
+          retval.assign ("intr", octave_value ());
+      }
+
+    return retval;
+  }
+
+  // Return a pointer to the user-defined function FNAME.  If FNAME is empty,
+  // search backward for the first user-defined function in the
+  // current call stack.
+
+  octave_user_code *
+  get_user_code (const std::string& fname)
+  {
+    octave_user_code *dbg_fcn = nullptr;
 
-  // print dbstop if warning information
-  if (Vdebug_on_warning)
-    {
-      if (warnings_that_stop.empty ())
-        {
-          if (to_screen)
-            octave_stdout << "stop if warning\n";
-          else
-            retval.assign ("warn", octave_value(""));
-        }
-      else
-        {
-          Cell warn (dim_vector (warnings_that_stop.size (), 1));
-          int i = 0;
+    if (fname.empty ())
+      {
+        octave::call_stack& cs = octave::__get_call_stack__ ("get_user_code");
+
+        dbg_fcn = cs.debug_user_code ();
+      }
+    else
+      {
+        std::string name = fname;
+
+        if (octave::sys::file_ops::dir_sep_char () != '/' && name[0] == '@')
+          {
+            auto beg = name.begin () + 2;  // never have @/method
+            auto end = name.end () - 1;    // never have trailing '/'
+            std::replace (beg, end, '/', octave::sys::file_ops::dir_sep_char ());
+          }
 
-          for (const auto& w : warnings_that_stop)
-            {
-              if (to_screen)
-                octave_stdout << "stop if warning " << w << "\n";
-              else
-                warn(i++) = w;
-            }
-          if (! to_screen)
-            retval.assign ("warn", octave_value (warn));
-        }
-    }
+        size_t name_len = name.length ();
+
+        if (name_len > 2 && name.substr (name_len-2) == ".m")
+          name = name.substr (0, name_len-2);
+
+        octave::symbol_table& symtab =
+          octave::__get_symbol_table__ ("get_user_code");
 
-  // print dbstop if interrupt information
-  if (octave::Vdebug_on_interrupt)
-    {
-      if (to_screen)
-        octave_stdout << "stop if interrupt\n";
-      else
-        retval.assign ("intr", octave_value ());
-    }
+        octave_value fcn = symtab.find_function (name);
 
-  return retval;
+        if (fcn.is_defined () && fcn.is_user_code ())
+          dbg_fcn = fcn.user_code_value ();
+      }
+
+    return dbg_fcn;
+  }
 }
-