1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2001-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <deque>
31 #include <fstream>
32 #include <iomanip>
33 #include <iostream>
34 #include <limits>
35 #include <set>
36 #include <string>
37 
38 #include "dNDArray.h"
39 
40 #include "bp-table.h"
41 #include "defun.h"
42 #include "error.h"
43 #include "errwarn.h"
44 #include "file-ops.h"
45 #include "help.h"
46 #include "input.h"
47 #include "interpreter-private.h"
48 #include "interpreter.h"
49 #include "lo-sysdep.h"
50 #include "octave-preserve-stream-state.h"
51 #include "ov-usr-fcn.h"
52 #include "ov.h"
53 #include "ovl.h"
54 #include "pager.h"
55 #include "parse.h"
56 #include "pt-eval.h"
57 #include "unwind-prot.h"
58 #include "utils.h"
59 #include "utils.h"
60 #include "variables.h"
61 
62 static octave_value
intmap_to_ov(const octave::bp_table::intmap & line)63 intmap_to_ov (const octave::bp_table::intmap& line)
64 {
65   int idx = 0;
66 
67   NDArray retval (dim_vector (1, line.size ()));
68 
69   for (std::size_t i = 0; i < line.size (); i++)
70     {
71       octave::bp_table::const_intmap_iterator p = line.find (i);
72 
73       if (p != line.end ())
74         {
75           int lineno = p->second;
76           retval(idx++) = lineno;
77         }
78     }
79 
80   retval.resize (dim_vector (1, idx));
81 
82   return retval;
83 }
84 
85 DEFMETHOD (dbstop, interp, args, ,
86            doc: /* -*- texinfo -*-
87 @deftypefn  {} {} dbstop @var{func}
88 @deftypefnx {} {} dbstop @var{func} @var{line}
89 @deftypefnx {} {} dbstop @var{func} @var{line1} @var{line2} @dots{}
90 @deftypefnx {} {} dbstop @var{line1} @dots{}
91 @deftypefnx {} {} dbstop in @var{func}
92 @deftypefnx {} {} dbstop in @var{func} at @var{line}
93 @deftypefnx {} {} dbstop in @var{func} at @var{line} if "@var{condition}"
94 @deftypefnx {} {} dbstop in @var{class} at @var{method}
95 @deftypefnx {} {} dbstop if @var{event}
96 @deftypefnx {} {} dbstop if @var{event} @var{ID}
97 @deftypefnx {} {} dbstop (@var{bp_struct})
98 @deftypefnx {} {@var{rline} =} dbstop @dots{}
99 
100 Set breakpoints for the built-in debugger.
101 
102 @var{func} is the name of a function on the current @code{path}.  When
103 already in debug mode the @var{func} argument can be omitted and the current
104 function will be used.  Breakpoints at subfunctions are set with the scope
105 operator @samp{>}.  For example, If @file{file.m} has a subfunction
106 @code{func2}, then a breakpoint in @code{func2} can be specified by
107 @code{file>func2}.
108 
109 @var{line} is the line number at which to break.  If @var{line} is not
110 specified, it defaults to the first executable line in the file
111 @file{func.m}.  Multiple lines can be specified in a single command; when
112 function syntax is used, the lines may also be passed as a single vector
113 argument (@code{[@var{line1}, @var{line2}, @dots{}]}).
114 
115 @var{condition} is any Octave expression that can be evaluated in the code
116 context that exists at the breakpoint.  When the breakpoint is encountered,
117 @var{condition} will be evaluated, and execution will stop if
118 @var{condition} is true.  If @var{condition} cannot be evaluated, for
119 example because it refers to an undefined variable, an error will be thrown.
120  Expressions with side effects (such as @code{y++ > 1}) will alter
121 variables, and should generally be avoided.  Conditions containing quotes
122 (@samp{"}, @samp{'}) or comment characters (@samp{#}, @samp{%}) must be
123 enclosed in quotes.  (This does not apply to conditions entered from the
124 editor's context menu.)  For example:
125 
126 @example
127 dbstop in axis at 246 if 'any (opt == "x")'
128 @end example
129 
130 The form specifying @var{event} does not cause a specific breakpoint at a
131 given function and line number.  Instead it causes debug mode to be entered
132 when certain unexpected events are encountered.  Possible values are
133 
134 @table @code
135 @item error
136 Stop when an error is reported.  This is equivalent to specifying
137 both @code{debug_on_error (true)} and @code{debug_on_interrupt (true)}.
138 
139 @item caught error
140 Stop when an error is caught by a try-catch block (not yet implemented).
141 
142 @item interrupt
143 Stop when an interrupt (@kbd{Ctrl-C}) occurs.
144 
145 @item naninf
146 Stop when code returns a non-finite value (not yet implemented).
147 
148 @item warning
149 Stop when a warning is reported.  This is equivalent to specifying
150 @code{debug_on_warning (true)}.
151 @end table
152 
153 The events @code{error}, @code{caught error}, and @code{warning} can all be
154 followed by a string specifying an error ID or warning ID@.  If that is
155 done, only errors with the specified ID will cause execution to stop.  To
156 stop on one of a set of IDs, multiple @code{dbstop} commands must be
157 issued.
158 
159 Breakpoints and events can be removed using the @code{dbclear} command with
160 the same syntax.
161 
162 It is possible to save all breakpoints and restore them at once by issuing
163 the commands @code{bp_state = dbstatus; @dots{}; dbstop (bp_state)}.
164 
165 The optional output @var{rline} is the real line number where the breakpoint
166 was set.  This can differ from the specified line if the line is not
167 executable.  For example, if a breakpoint attempted on a blank line then
168 Octave will set the real breakpoint at the next executable line.
169 
170 When a file is re-parsed, such as when it is modified outside the GUI,
171 all breakpoints within the file are cleared.
172 
173 @seealso{dbclear, dbstatus, dbstep, debug_on_error, debug_on_warning, debug_on_interrupt}
174 @end deftypefn */)
175 {
176   octave::bp_table::intmap retmap;
177   std::string symbol_name = "";  // stays empty for "dbstop if error" etc
178   std::string class_name = "";
179   octave::bp_table::intmap lines;
180   std::string condition = "";
181   octave_value retval;
182 
183   octave::tree_evaluator& tw = interp.get_evaluator ();
184 
185   octave::bp_table& bptab = tw.get_bp_table ();
186 
187   if (args.length() >= 1 && ! args(0).isstruct ())
188     {
189       // explicit function / line / condition
190       bptab.parse_dbfunction_params ("dbstop", args, symbol_name,
191                                      class_name, lines, condition);
192 
193       if (lines.size () == 0)
194         lines[0] = 1;
195 
196       if (symbol_name != "")
197         {
198           retmap = bptab.add_breakpoint (symbol_name, class_name,
199                                          lines, condition);
200           retval = intmap_to_ov (retmap);
201         }
202     }
203   else if (args.length () != 1)
204     {
205       print_usage ();
206     }
207   else  // structure of the form output by dbstatus
208     {
209       octave_map mv = args(0).map_value ();
210       if (mv.isfield ("bkpt") || mv.isfield ("errs") || mv.isfield ("warn")
211           || mv.isfield ("intr"))
212         {
213           bptab.dbstop_process_map_args (mv);
214 
215           // Replace mv by "bkpt", to use the processing below.
216           octave_value bkpt = mv.getfield ("bkpt");
217           if (bkpt.isempty ())
218             mv = octave_map ();
219           else
220             {
221               if (bkpt.iscell () && bkpt.cell_value ().numel () > 0
222                   && bkpt.cell_value () (0).isstruct ())
223                 mv = bkpt.cell_value () (0).map_value ();
224               else
225                 {
226                   error ("dbstop: invalid 'bkpt' field");
227                   mv = octave_map ();
228                 }
229             }
230         }
231       if (mv.isempty ())
232         {
233           // no changes requested.  Occurs if "errs" non-empty but "bkpt" empty
234         }
235       else if (! mv.isfield ("name") || ! mv.isfield ("line"))
236         {
237           error ("dbstop: Cell array must contain fields 'name' and 'line'");
238           retval = octave_value (0);
239         }
240       else
241         {
242           bool use_cond = mv.isfield ("cond");
243           Cell name = mv.getfield ("name");
244           Cell line = mv.getfield ("line");
245           Cell cond = (use_cond ? mv.getfield ("cond") : Cell ());
246           std::string unconditional = "";
247           for (octave_idx_type i = 0; i < line.numel (); i++)
248             {
249               lines [0] = line(i).double_value ();
250               bptab.add_breakpoint (name(i).string_value (), "", lines,
251                                     (use_cond
252                                      ? cond(i).string_value ()
253                                      : unconditional));
254             }
255           retval = octave_value (line.numel ());
256         }
257     }
258 
259   // If we add a breakpoint, we also need to reset debug_mode.
260   tw.reset_debug_state ();
261 
262   return retval;
263 }
264 
265 DEFMETHOD (dbclear, interp, args, ,
266            doc: /* -*- texinfo -*-
267 @deftypefn  {} {} dbclear @var{func}
268 @deftypefnx {} {} dbclear @var{func} @var{line}
269 @deftypefnx {} {} dbclear @var{func} @var{line1} @var{line2} @dots{}
270 @deftypefnx {} {} dbclear @var{line} @dots{}
271 @deftypefnx {} {} dbclear all
272 @deftypefnx {} {} dbclear in @var{func}
273 @deftypefnx {} {} dbclear in @var{func} at @var{line}
274 @deftypefnx {} {} dbclear if @var{event}
275 @deftypefnx {} {} dbclear ("@var{func}")
276 @deftypefnx {} {} dbclear ("@var{func}", @var{line})
277 @deftypefnx {} {} dbclear ("@var{func}", @var{line1}, @var{line2}, @dots{})
278 @deftypefnx {} {} dbclear ("@var{func}", @var{line1}, @dots{})
279 @deftypefnx {} {} dbclear (@var{line}, @dots{})
280 @deftypefnx {} {} dbclear ("all")
281 Delete a breakpoint at line number @var{line} in the function @var{func}.
282 
283 Arguments are
284 
285 @table @var
286 @item func
287 Function name as a string variable.  When already in debug mode this
288 argument can be omitted and the current function will be used.
289 
290 @item line
291 Line number from which to remove a breakpoint.  Multiple lines may be given
292 as separate arguments or as a vector.
293 
294 @item event
295 An event such as @code{error}, @code{interrupt}, or @code{warning}
296 (@pxref{XREFdbstop,,dbstop} for details).
297 @end table
298 
299 When called without a line number specification all breakpoints in the named
300 function are cleared.
301 
302 If the requested line is not a breakpoint no action is performed.
303 
304 The special keyword @qcode{"all"} will clear all breakpoints from all
305 files.
306 @seealso{dbstop, dbstatus, dbwhere}
307 @end deftypefn */)
308 {
309   std::string symbol_name = "";  // stays empty for "dbclear if error" etc
310   std::string class_name = "";
311   octave::bp_table::intmap lines;
312   std::string dummy;             // "if" condition -- only used for dbstop
313 
314   int nargin = args.length ();
315 
316   octave::tree_evaluator& tw = interp.get_evaluator ();
317 
318   octave::bp_table& bptab = tw.get_bp_table ();
319 
320   bptab.parse_dbfunction_params ("dbclear", args, symbol_name, class_name, lines, dummy);
321 
322   if (nargin == 1 && symbol_name == "all")
323     {
324       bptab.remove_all_breakpoints ();
325       bptab.dbclear_all_signals ();
326     }
327   else
328     {
329       if (symbol_name != "")
330         bptab.remove_breakpoint (symbol_name, lines);
331     }
332 
333   // If we remove a breakpoint, we also need to reset debug_mode.
334   tw.reset_debug_state ();
335 
336   return ovl ();
337 }
338 
339 DEFMETHOD (dbstatus, interp, args, nargout,
340            doc: /* -*- texinfo -*-
341 @deftypefn  {} {} dbstatus
342 @deftypefnx {} {} dbstatus @var{func}
343 @deftypefnx {} {@var{bp_list} =} dbstatus @dots{}
344 Report the location of active breakpoints.
345 
346 When called with no input or output arguments, print the list of all
347 functions with breakpoints and the line numbers where those breakpoints are
348 set.
349 
350 If a function name @var{func} is specified then only report breakpoints
351 for the named function and its subfunctions.
352 
353 The optional return argument @var{bp_list} is a struct array with the
354 following fields.
355 
356 @table @asis
357 @item name
358 The name of the function with a breakpoint.  A subfunction, say @code{func2}
359 within an m-file, say @file{file.m}, is specified as @code{file>func2}.
360 
361 @item file
362 The name of the m-file where the function code is located.
363 
364 @item line
365 The line number with the breakpoint.
366 
367 @item cond
368 The condition that must be satisfied for the breakpoint to be active, or
369 the empty string for unconditional breakpoints.
370 @end table
371 
372 @c Note: When @code{dbstatus} is called from the debug prompt within a function,
373 @c the list of breakpoints is automatically trimmed to the breakpoints in the
374 @c current function.
375 If @code{dbstop if error} is true but no explicit IDs are specified, the
376 return value will have an empty field called @qcode{"errs"}.  If IDs are
377 specified, the @code{errs} field will have one row per ID@.  If
378 @code{dbstop if error} is false, there is no @qcode{"errs"} field.
379 The @qcode{"warn"} field is set similarly by @code{dbstop if warning}.
380 
381 @seealso{dbstop, dbclear, dbwhere, dblist, dbstack}
382 @end deftypefn */)
383 {
384   int nargin = args.length ();
385 
386   if (nargin != 0 && nargin != 1)
387     error ("dbstatus: only zero or one arguments accepted\n");
388 
389   octave_value_list fcn_list;
390   octave::bp_table::fname_bp_map bp_list;
391   std::string symbol_name;
392 
393   octave::tree_evaluator& tw = interp.get_evaluator ();
394 
395   octave::bp_table& bptab = tw.get_bp_table ();
396 
397   if (nargin == 1)
398     {
399       if (! args(0).is_string ())
400         err_wrong_type_arg ("dbstatus", args(0));
401 
402       symbol_name = args(0).string_value ();
403       fcn_list(0) = symbol_name;
404       bp_list = bptab.get_breakpoint_list (fcn_list);
405     }
406   else
407     {
408 /*
409       if (tw.in_debug_repl ())
410         {
411           octave_user_code *dbg_fcn = tw.get_user_code ();
412           if (dbg_fcn)
413             {
414               symbol_name = dbg_fcn->name ();
415               fcn_list(0) = symbol_name;
416             }
417         }
418 */
419 
420       bp_list = bptab.get_breakpoint_list (fcn_list);
421     }
422 
423   if (nargout == 0)
424     {
425       // Print out the breakpoint information.
426 
427       for (auto& fnm_bp_p: bp_list)
428         {
429           std::list<octave::bp_type> m = fnm_bp_p.second;
430 
431           // print unconditional breakpoints, if any, on a single line
432 
433           // first, check to see if there are any
434           int have_unconditional = 0;
435           for (const auto& bp : m)
436             {
437               if (bp.cond == "")
438                 {
439                   if (have_unconditional++)
440                     break;                   // stop once we know its plural
441                 }
442             }
443           // If we actually have some, print line numbers only
444           if (have_unconditional)
445             {
446               const char *_s_ = (have_unconditional > 1) ? "s" : "";
447               octave_stdout << "breakpoint" << _s_ << " in " << fnm_bp_p.first
448                             << " at line" << _s_ << ' ';
449 
450               for (const auto& bp : m)
451                 {
452                   if (bp.cond == "")
453                     octave_stdout << bp.line << ' ';
454                 }
455               octave_stdout << std::endl;
456             }
457 
458           // print conditional breakpoints, one per line, with conditions
459           for (const auto& bp : m)
460             {
461               if (bp.cond != "")
462                 octave_stdout << "breakpoint in " << fnm_bp_p.first
463                               << " at line " << bp.line
464                               << " if " << bp.cond << "\n";
465             }
466         }
467 
468       bptab.stop_on_err_warn_status (true);
469 
470       return ovl ();
471     }
472   else
473     {
474       octave::help_system& help_sys = interp.get_help_system ();
475 
476       // Fill in an array for return.
477       int i = 0;
478       octave_map retmap;
479       octave_value retval;
480 
481       // count the number of breakpoints in all files
482       int count = 0;
483       for (const auto& fnm_bp_p : bp_list)
484         count += fnm_bp_p.second.size ();
485 
486       Cell names (dim_vector (count, 1));
487       Cell file  (dim_vector (count, 1));
488       Cell line  (dim_vector (count, 1));
489       Cell cond  (dim_vector (count, 1));
490 
491       for (const auto& fnm_bp_p : bp_list)
492         {
493           std::string filename = fnm_bp_p.first;
494           const char *sub_fun = strchr (filename.c_str (), '>');
495           if (sub_fun)
496             filename = filename.substr(0, sub_fun - filename.c_str ());
497           octave_value path_name;
498           path_name
499             = octave::sys::canonicalize_file_name (help_sys.which (filename));
500 
501           for (const auto& bp : fnm_bp_p.second)
502             {
503               names(i) = fnm_bp_p.first;
504               file(i) = path_name;
505               line(i) = octave_value (bp.line);
506               cond(i) = octave_value (bp.cond);
507               i++;
508             }
509         }
510 
511       retmap.assign ("name", names);
512       retmap.assign ("file", file);
513       retmap.assign ("line", line);
514       retmap.assign ("cond", cond);
515 
516       const octave_map ew = bptab.stop_on_err_warn_status (false);
517       if (ew.isempty ())
518         {
519           retval = octave_value (retmap);
520         }
521       else
522         {
523           octave_map outer (dim_vector (3,1));
524           outer.assign ("bkpt", Cell (retmap));
525           for (auto f = ew.begin (); f != ew.end (); f++)
526             outer.setfield (f->first, ew.contents (f));
527 
528           retval = octave_value (outer);
529         }
530 
531       return retval;
532     }
533 }
534 
535 /*
536 %!test
537 %! if (isguirunning ())
538 %!   orig_show_dbg = __event_manager_gui_preference__ ("editor/show_dbg_file",
539 %!                                                   "0");
540 %! endif
541 %! unwind_protect
542 %!   dbclear all;   # Clear out breakpoints before test
543 %!   dbstop @ftp/dir;
544 %!   dbstop @audioplayer/set 70;
545 %!   dbstop quantile>__quantile__;
546 %!   dbstop ls;
547 %!   s = dbstatus;
548 %!   dbclear all;
549 %!   assert (s(1).name, "@audioplayer/set>setproperty");
550 %!   assert (s(2).name, "@ftp/dir");
551 %!   assert (s(3).name, "ls");
552 %!   assert (s(4).name, "quantile>__quantile__");
553 %!   assert (s(2).file(end-10:end), [filesep "@ftp" filesep "dir.m"]);
554 %! unwind_protect_cleanup
555 %!   if (isguirunning ())
556 %!     __event_manager_gui_preference__ ("editor/show_dbg_file", orig_show_dbg);
557 %!   endif
558 %! end_unwind_protect
559 */
560 
561 DEFMETHOD (dbwhere, interp, , ,
562            doc: /* -*- texinfo -*-
563 @deftypefn {} {} dbwhere
564 In debugging mode, report the current file and line number where execution
565 is stopped.
566 @seealso{dbstack, dblist, dbstatus, dbcont, dbstep, dbup, dbdown}
567 @end deftypefn */)
568 {
569   octave::tree_evaluator& tw = interp.get_evaluator ();
570 
571   tw.debug_where (octave_stdout);
572 
573   return ovl ();
574 }
575 
576 static void
do_dbtype(std::ostream & os,const std::string & name,int start,int end)577 do_dbtype (std::ostream& os, const std::string& name, int start, int end)
578 {
579   std::string ff = octave::fcn_file_in_path (name);
580 
581   if (ff.empty ())
582     os << "dbtype: unknown function " << name << "\n";
583   else
584     {
585       std::ifstream fs = octave::sys::ifstream (ff.c_str (), std::ios::in);
586 
587       if (! fs)
588         os << "dbtype: unable to open '" << ff << "' for reading!\n";
589       else
590         {
591           int line = 1;
592           std::string text;
593 
594           while (std::getline (fs, text) && line <= end)
595             {
596               if (line >= start)
597                 os << line << "\t" << text << "\n";
598 
599               line++;
600             }
601         }
602     }
603 
604   os.flush ();
605 }
606 
607 DEFMETHOD (dbtype, interp, args, ,
608            doc: /* -*- texinfo -*-
609 @deftypefn  {} {} dbtype
610 @deftypefnx {} {} dbtype @var{lineno}
611 @deftypefnx {} {} dbtype @var{startl:endl}
612 @deftypefnx {} {} dbtype @var{startl:end}
613 @deftypefnx {} {} dbtype @var{func}
614 @deftypefnx {} {} dbtype @var{func} @var{lineno}
615 @deftypefnx {} {} dbtype @var{func} @var{startl:endl}
616 @deftypefnx {} {} dbtype @var{func} @var{startl:end}
617 Display a script file with line numbers.
618 
619 When called with no arguments in debugging mode, display the script file
620 currently being debugged.
621 
622 An optional range specification can be used to list only a portion of the
623 file.  The special keyword @qcode{"end"} is a valid line number
624 specification for the last line of the file.
625 
626 When called with the name of a function, list that script file with line
627 numbers.
628 @seealso{dblist, dbwhere, dbstatus, dbstop}
629 @end deftypefn */)
630 {
631   octave_user_code *dbg_fcn;
632 
633   string_vector argv = args.make_argv ("dbtype");
634 
635   octave::tree_evaluator& tw = interp.get_evaluator ();
636 
637   switch (args.length ())
638     {
639     case 0:  // dbtype
640       dbg_fcn = tw.get_user_code ();
641 
642       if (! dbg_fcn)
643         error ("dbtype: must be inside a user function to give no arguments to dbtype\n");
644 
645       do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
646                  0, std::numeric_limits<int>::max ());
647 
648       break;
649 
650     case 1:  // (dbtype start:end) || (dbtype func) || (dbtype lineno)
651       {
652         std::string arg = argv[1];
653 
654         std::size_t ind = arg.find (':');
655 
656         if (ind != std::string::npos)  // (dbtype start:end)
657           {
658             dbg_fcn = tw.get_user_code ();
659 
660             if (dbg_fcn)
661               {
662                 std::string start_str = arg.substr (0, ind);
663                 std::string end_str = arg.substr (ind + 1);
664 
665                 int start, end;
666                 start = atoi (start_str.c_str ());
667                 if (end_str == "end")
668                   end = std::numeric_limits<int>::max ();
669                 else
670                   end = atoi (end_str.c_str ());
671 
672                 if (std::min (start, end) <= 0)
673                   error ("dbtype: start and end lines must be >= 1\n");
674 
675                 if (start > end)
676                   error ("dbtype: start line must be less than end line\n");
677 
678                 do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
679                            start, end);
680               }
681           }
682         else  // (dbtype func) || (dbtype lineno)
683           {
684             int line = atoi (arg.c_str ());
685 
686             if (line == 0)  // (dbtype func)
687               {
688                 dbg_fcn = tw.get_user_code (arg);
689 
690                 if (! dbg_fcn)
691                   error ("dbtype: function <%s> not found\n", arg.c_str ());
692 
693                 do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
694                            0, std::numeric_limits<int>::max ());
695               }
696             else  // (dbtype lineno)
697               {
698                 if (line <= 0)
699                   error ("dbtype: start and end lines must be >= 1\n");
700 
701                 dbg_fcn = tw.get_user_code ();
702 
703                 if (dbg_fcn)
704                   do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (),
705                              line, line);
706               }
707           }
708       }
709       break;
710 
711     case 2:  // (dbtype func start:end) || (dbtype func start)
712       {
713         dbg_fcn = tw.get_user_code (argv[1]);
714 
715         if (! dbg_fcn)
716           error ("dbtype: function <%s> not found\n", argv[1].c_str ());
717 
718         std::string arg = argv[2];
719         int start, end;
720         std::size_t ind = arg.find (':');
721 
722         if (ind != std::string::npos)
723           {
724             std::string start_str = arg.substr (0, ind);
725             std::string end_str = arg.substr (ind + 1);
726 
727             start = atoi (start_str.c_str ());
728             if (end_str == "end")
729               end = std::numeric_limits<int>::max ();
730             else
731               end = atoi (end_str.c_str ());
732           }
733         else
734           {
735             start = atoi (arg.c_str ());
736             end = start;
737           }
738 
739         if (std::min (start, end) <= 0)
740           error ("dbtype: start and end lines must be >= 1\n");
741 
742         if (start > end)
743           error ("dbtype: start line must be less than end line\n");
744 
745         do_dbtype (octave_stdout, dbg_fcn->fcn_file_name (), start, end);
746       }
747       break;
748 
749     default:
750       error ("dbtype: expecting zero, one, or two arguments\n");
751     }
752 
753   return ovl ();
754 }
755 
756 DEFMETHOD (dblist, interp, args, ,
757            doc: /* -*- texinfo -*-
758 @deftypefn  {} {} dblist
759 @deftypefnx {} {} dblist @var{n}
760 In debugging mode, list @var{n} lines of the function being debugged
761 centered around the current line to be executed.
762 
763 If unspecified @var{n} defaults to 10 (+/- 5 lines)
764 @seealso{dbwhere, dbtype, dbstack}
765 @end deftypefn */)
766 {
767   int n = 10;
768 
769   if (args.length () == 1)
770     {
771       octave_value arg = args(0);
772 
773       if (arg.is_string ())
774         {
775           std::string s_arg = arg.string_value ();
776 
777           n = atoi (s_arg.c_str ());
778         }
779       else
780         n = args(0).int_value ();
781 
782       if (n < 0)
783         error ("dblist: N must be a non-negative integer");
784     }
785 
786   octave::tree_evaluator& tw = interp.get_evaluator ();
787 
788   octave_user_code *dbg_fcn = tw.get_user_code ();
789 
790   if (! dbg_fcn)
791     error ("dblist: must be inside a user function to use dblist\n");
792 
793   bool have_file = true;
794 
795   std::string name = dbg_fcn->fcn_file_name ();
796 
797   if (name.empty ())
798     {
799       have_file = false;
800       name = dbg_fcn->name ();
801     }
802 
803   int l = tw.debug_user_code_line ();
804 
805   if (l > 0)
806     {
807       if (have_file)
808         {
809           int l_min = std::max (l - n/2, 0);
810           int l_max = l + n/2;
811           do_dbtype (octave_stdout, name, l_min, l-1);
812 
813           std::string line = dbg_fcn->get_code_line (l);
814 
815           if (! line.empty ())
816             octave_stdout << l << "-->\t" << line << std::endl;
817 
818           do_dbtype (octave_stdout, name, l+1, l_max);
819         }
820     }
821   else
822     {
823       octave_stdout << "dblist: unable to determine source code line"
824                     << std::endl;
825     }
826 
827   return ovl ();
828 }
829 
830 static octave_value_list
do_dbstack(octave::interpreter & interp,const octave_value_list & args,int nargout,std::ostream & os)831 do_dbstack (octave::interpreter& interp, const octave_value_list& args,
832             int nargout, std::ostream& os)
833 {
834   int nargin = args.length ();
835 
836   if (nargin > 2)
837     print_usage ();
838 
839   octave_value_list retval;
840 
841   octave::unwind_protect frame;
842 
843   octave_idx_type curr_frame = -1;
844 
845   octave_idx_type nskip = 0;
846 
847   if (nargin == 1 || nargin == 2)
848     {
849       int n = 0;
850 
851       for (octave_idx_type i = 0; i < nargin; i++)
852         {
853           octave_value arg = args(i);
854 
855           if (arg.is_string ())
856             {
857               std::string s_arg = arg.string_value ();
858 
859               // Skip "-completenames", octave returns full names anyway.
860               if (s_arg == "-completenames")
861                 continue;
862 
863               n = atoi (s_arg.c_str ());
864             }
865           else
866             n = arg.int_value ();
867 
868           if (n <= 0)
869             error ("dbstack: N must be a non-negative integer");
870         }
871 
872       if (n > 0)
873         nskip = n;
874     }
875 
876   octave::tree_evaluator& tw = interp.get_evaluator ();
877 
878   if (nargout == 0)
879     {
880       octave_map stk = tw.backtrace (curr_frame);
881       octave_idx_type nframes = stk.numel ();
882 
883       if (nframes > 0)
884         {
885           octave::preserve_stream_state stream_state (os);
886 
887           os << "stopped in:\n\n";
888 
889           Cell names = stk.contents ("name");
890           Cell files = stk.contents ("file");
891           Cell lines = stk.contents ("line");
892 
893           bool show_top_level = true;
894 
895           std::size_t max_name_len = 0;
896 
897           for (octave_idx_type i = nskip; i < nframes; i++)
898             {
899               std::string name = names(i).string_value ();
900 
901               max_name_len = std::max (name.length (), max_name_len);
902             }
903 
904           for (octave_idx_type i = nskip; i < nframes; i++)
905             {
906               std::string name = names(i).string_value ();
907               std::string file = files(i).string_value ();
908               int line = lines(i).int_value ();
909 
910               if (show_top_level && i == curr_frame)
911                 show_top_level = false;
912 
913               os << (i == curr_frame ? "  --> " : "      ")
914                  << std::setw (max_name_len) << name
915                  << " at line " << line
916                  << " [" << file << ']'
917                  << std::endl;
918             }
919 
920           if (tw.at_top_level () && show_top_level)
921             os << "  --> top level" << std::endl;
922         }
923     }
924   else
925     {
926       octave_map stk = tw.backtrace (curr_frame, false);
927 
928       octave_idx_type num_skip = std::min (nskip, stk.numel ());
929 
930       idx_vector first = idx_vector (static_cast<octave_idx_type> (0));
931 
932       for (octave_idx_type i = 0; i < num_skip; i++)
933         stk.delete_elements (first);
934 
935       curr_frame -= num_skip;
936       curr_frame = (curr_frame < 0 ? 0 : curr_frame + 1);
937 
938       retval = ovl (stk, curr_frame);
939     }
940 
941   return retval;
942 }
943 
944 // A function that can be easily called from a debugger print the Octave stack.
945 // This can be useful for finding what line of code the interpreter is
946 // currently executing when the debugger is stopped in some C++ function,
947 // for example.
948 
949 void
show_octave_dbstack(void)950 show_octave_dbstack (void)
951 {
952   do_dbstack (octave::__get_interpreter__ ("show_octave_dbstack"),
953               octave_value_list (), 0, std::cerr);
954 }
955 
956 DEFMETHOD (dbstack, interp, args, nargout,
957            doc: /* -*- texinfo -*-
958 @deftypefn  {} {} dbstack
959 @deftypefnx {} {} dbstack @var{n}
960 @deftypefnx {} {} dbstack @var{-completenames}
961 @deftypefnx {} {[@var{stack}, @var{idx}] =} dbstack (@dots{})
962 Display or return current debugging function stack information.
963 
964 With optional argument @var{n}, omit the @var{n} innermost stack frames.
965 
966 Although accepted, the argument @var{-completenames} is silently ignored.
967 Octave always returns absolute filenames.
968 
969 The arguments @var{n} and @var{-completenames} can be both specified in any
970 order.
971 
972 The optional return argument @var{stack} is a struct array with the
973 following fields:
974 
975 @table @asis
976 @item file
977 The name of the m-file where the function code is located.
978 
979 @item name
980 The name of the function with a breakpoint.
981 
982 @item line
983 The line number of an active breakpoint.
984 
985 @item column
986 The column number of the line where the breakpoint begins.
987 
988 @item scope
989 Undocumented.
990 
991 @item context
992 Undocumented.
993 @end table
994 
995 The return argument @var{idx} specifies which element of the @var{stack}
996 struct array is currently active.
997 @seealso{dbup, dbdown, dbwhere, dblist, dbstatus}
998 @end deftypefn */)
999 {
1000   return do_dbstack (interp, args, nargout, octave_stdout);
1001 }
1002 
1003 static void
do_dbupdown(octave::interpreter & interp,const octave_value_list & args,const std::string & who)1004 do_dbupdown (octave::interpreter& interp, const octave_value_list& args,
1005              const std::string& who)
1006 {
1007   int n = 1;
1008 
1009   if (args.length () == 1)
1010     {
1011       octave_value arg = args(0);
1012 
1013       if (arg.is_string ())
1014         {
1015           std::string s_arg = arg.string_value ();
1016 
1017           n = atoi (s_arg.c_str ());
1018         }
1019       else
1020         n = args(0).int_value ();
1021     }
1022 
1023   if (who == "dbup")
1024     n = -n;
1025 
1026   octave::tree_evaluator& tw = interp.get_evaluator ();
1027 
1028   tw.dbupdown (n, true);
1029 }
1030 
1031 DEFMETHOD (dbup, interp, args, ,
1032            doc: /* -*- texinfo -*-
1033 @deftypefn  {} {} dbup
1034 @deftypefnx {} {} dbup @var{n}
1035 In debugging mode, move up the execution stack @var{n} frames.
1036 
1037 If @var{n} is omitted, move up one frame.
1038 @seealso{dbstack, dbdown}
1039 @end deftypefn */)
1040 {
1041   do_dbupdown (interp, args, "dbup");
1042 
1043   return ovl ();
1044 }
1045 
1046 DEFMETHOD (dbdown, interp, args, ,
1047            doc: /* -*- texinfo -*-
1048 @deftypefn  {} {} dbdown
1049 @deftypefnx {} {} dbdown @var{n}
1050 In debugging mode, move down the execution stack @var{n} frames.
1051 
1052 If @var{n} is omitted, move down one frame.
1053 @seealso{dbstack, dbup}
1054 @end deftypefn */)
1055 {
1056   do_dbupdown (interp, args, "dbdown");
1057 
1058   return ovl ();
1059 }
1060 
1061 DEFMETHOD (dbstep, interp, args, ,
1062            doc: /* -*- texinfo -*-
1063 @deftypefn  {} {} dbstep
1064 @deftypefnx {} {} dbstep @var{n}
1065 @deftypefnx {} {} dbstep in
1066 @deftypefnx {} {} dbstep out
1067 @deftypefnx {} {} dbnext @dots{}
1068 In debugging mode, execute the next @var{n} lines of code.
1069 
1070 If @var{n} is omitted, execute the next single line of code.  If the next
1071 line of code is itself defined in terms of an m-file remain in the existing
1072 function.
1073 
1074 Using @code{dbstep in} will cause execution of the next line to step into
1075 any m-files defined on the next line.
1076 
1077 Using @code{dbstep out} will cause execution to continue until the current
1078 function returns.
1079 
1080 @code{dbnext} is an alias for @code{dbstep}.
1081 @seealso{dbcont, dbquit}
1082 @end deftypefn */)
1083 {
1084   octave::tree_evaluator& tw = interp.get_evaluator ();
1085 
1086   if (! tw.in_debug_repl ())
1087     error ("dbstep: can only be called in debug mode");
1088 
1089   int nargin = args.length ();
1090 
1091   if (nargin > 1)
1092     print_usage ();
1093 
1094   int n = 0;
1095 
1096   if (nargin == 1)
1097     {
1098       std::string arg
1099         = args(0).xstring_value ("dbstep: input argument must be a string");
1100 
1101       if (arg == "in")
1102         n = -1;
1103       else if (arg == "out")
1104         n = -2;
1105       else
1106         {
1107           n = atoi (arg.c_str ());
1108 
1109           if (n < 1)
1110             error ("dbstep: invalid argument");
1111         }
1112     }
1113   else
1114     n = 1;
1115 
1116   if (n != 0)
1117     {
1118       tw.set_dbstep_flag (n);
1119 
1120       // If we set the dbstep flag, we also need to reset debug_mode.
1121       tw.reset_debug_state ();
1122 
1123     }
1124 
1125   return ovl ();
1126 }
1127 
1128 DEFALIAS (dbnext, dbstep);
1129 
1130 DEFMETHOD (dbcont, interp, args, ,
1131            doc: /* -*- texinfo -*-
1132 @deftypefn {} {} dbcont
1133 Leave command-line debugging mode and continue code execution normally.
1134 @seealso{dbstep, dbquit}
1135 @end deftypefn */)
1136 {
1137   octave::tree_evaluator& tw = interp.get_evaluator ();
1138 
1139   if (! tw.in_debug_repl ())
1140     error ("dbcont: can only be called in debug mode");
1141 
1142   if (args.length () != 0)
1143     print_usage ();
1144 
1145   tw.dbcont ();
1146 
1147   return ovl ();
1148 }
1149 
1150 DEFMETHOD (dbquit, interp, args, ,
1151            doc: /* -*- texinfo -*-
1152 @deftypefn  {} {} dbquit
1153 @deftypefnx {} {} dbquit all
1154 Quit debugging mode immediately without further code execution.  With no
1155 arguments, exit the current debugging level.  With argument @code{all},
1156 exit all debugging levels and return to the Octave prompt.
1157 @seealso{dbcont, dbstep}
1158 @end deftypefn */)
1159 {
1160   octave::tree_evaluator& tw = interp.get_evaluator ();
1161 
1162   if (! tw.in_debug_repl ())
1163     error ("dbquit: can only be called in debug mode");
1164 
1165   int nargin = args.length ();
1166 
1167   if (nargin > 1)
1168     print_usage ();
1169 
1170   if (nargin == 1)
1171     {
1172       std::string arg
1173         = args(0).xstring_value ("dbquit: input argument must be a string");
1174 
1175       if (arg == "all")
1176         tw.dbquit (true);
1177       else
1178         error ("dbquit: unrecognized argument '%s'", arg.c_str ());
1179     }
1180   else
1181     tw.dbquit ();
1182 
1183   return ovl ();
1184 }
1185 
1186 DEFMETHOD (isdebugmode, interp, args, ,
1187            doc: /* -*- texinfo -*-
1188 @deftypefn {} {} isdebugmode ()
1189 Return true if in debugging mode, otherwise false.
1190 @seealso{dbwhere, dbstack, dbstatus}
1191 @end deftypefn */)
1192 {
1193   if (args.length () != 0)
1194     print_usage ();
1195 
1196   octave::tree_evaluator& tw = interp.get_evaluator ();
1197 
1198   return ovl (tw.in_debug_repl ());
1199 }
1200 
1201 DEFMETHOD (__db_next_breakpoint_quiet__, interp, args, ,
1202            doc: /* -*- texinfo -*-
1203 @deftypefn  {} {} __db_next_breakpoint_quiet__ ()
1204 @deftypefnx {} {} __db_next_breakpoint_quiet__ (@var{flag})
1205 Disable line info printing at the next breakpoint.
1206 
1207 With a logical argument @var{flag}, set the state on or off.
1208 @end deftypefn */)
1209 {
1210   int nargin = args.length ();
1211 
1212   if (nargin > 1)
1213     print_usage ();
1214 
1215   bool state = true;
1216 
1217   if (nargin == 1)
1218     state = args(0).bool_value ();
1219 
1220   octave::tree_evaluator& tw = interp.get_evaluator ();
1221 
1222   tw.quiet_breakpoint_flag (state);
1223 
1224   return ovl ();
1225 }
1226