gud.el 130 KB
Newer Older
Juanma Barranquero's avatar
Juanma Barranquero committed
1 2
;;; gud.el --- Grand Unified Debugger mode for running GDB and other debuggers

3
;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001, 2002, 2003,
Glenn Morris's avatar
Glenn Morris committed
4
;;  2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5

Juanma Barranquero's avatar
Juanma Barranquero committed
6 7 8 9 10 11
;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
;; Maintainer: FSF
;; Keywords: unix, tools

;; This file is part of GNU Emacs.

12
;; GNU Emacs is free software: you can redistribute it and/or modify
Juanma Barranquero's avatar
Juanma Barranquero committed
13
;; it under the terms of the GNU General Public License as published by
14 15
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Juanma Barranquero's avatar
Juanma Barranquero committed
16 17 18 19 20 21 22

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
23
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Juanma Barranquero's avatar
Juanma Barranquero committed
24 25 26

;;; Commentary:

Juanma Barranquero's avatar
Juanma Barranquero committed
27 28
;; The ancestral gdb.el was by W. Schelter <wfs@rascal.ics.utexas.edu>.
;; It was later rewritten by rms.  Some ideas were due to Masanobu.  Grand
29 30 31 32 33 34 35 36 37
;; Unification (sdb/dbx support) by Eric S. Raymond <esr@thyrsus.com> Barry
;; Warsaw <bwarsaw@cen.com> hacked the mode to use comint.el.  Shane Hartman
;; <shane@spr.com> added support for xdb (HPUX debugger).  Rick Sladkey
;; <jrs@world.std.com> wrote the GDB command completion code.  Dave Love
;; <d.love@dl.ac.uk> added the IRIX kluge, re-implemented the Mips-ish variant
;; and added a menu. Brian D. Carlstrom <bdc@ai.mit.edu> combined the IRIX
;; kluge with the gud-xdb-directories hack producing gud-dbx-directories.
;; Derek L. Davies <ddavies@world.std.com> added support for jdb (Java
;; debugger.)
Juanma Barranquero's avatar
Juanma Barranquero committed
38 39 40

;;; Code:

41 42
(eval-when-compile (require 'cl)) ; for case macro

Juanma Barranquero's avatar
Juanma Barranquero committed
43 44
(require 'comint)

45 46 47 48
(defvar gdb-active-process)
(defvar gdb-define-alist)
(defvar gdb-macro-info)
(defvar gdb-show-changed-values)
49
(defvar gdb-source-window)
50
(defvar gdb-var-list)
51
(defvar gdb-speedbar-auto-raise)
52 53 54
(defvar gud-tooltip-mode)
(defvar hl-line-mode)
(defvar hl-line-sticky-flag)
55 56
(defvar tool-bar-map)

57

Juanma Barranquero's avatar
Juanma Barranquero committed
58 59 60 61 62
;; ======================================================================
;; GUD commands must be visible in C buffers visited by GUD

(defgroup gud nil
  "Grand Unified Debugger mode for gdb and other debuggers under Emacs.
63
Supported debuggers include gdb, sdb, dbx, xdb, perldb, pdb (Python) and jdb."
64
  :group 'processes
Juanma Barranquero's avatar
Juanma Barranquero committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  :group 'tools)


(defcustom gud-key-prefix "\C-x\C-a"
  "Prefix of all GUD commands valid in C buffers."
  :type 'string
  :group 'gud)

(global-set-key (concat gud-key-prefix "\C-l") 'gud-refresh)
(define-key ctl-x-map " " 'gud-break)	;; backward compatibility hack

(defvar gud-marker-filter nil)
(put 'gud-marker-filter 'permanent-local t)
(defvar gud-find-file nil)
(put 'gud-find-file 'permanent-local t)

(defun gud-marker-filter (&rest args)
  (apply gud-marker-filter args))

(defvar gud-minor-mode nil)
(put 'gud-minor-mode 'permanent-local t)

87 88
(defvar gud-comint-buffer nil)

Juanma Barranquero's avatar
Juanma Barranquero committed
89 90 91 92
(defvar gud-keep-buffer nil)

(defun gud-symbol (sym &optional soft minor-mode)
  "Return the symbol used for SYM in MINOR-MODE.
93
MINOR-MODE defaults to `gud-minor-mode'.
Juanma Barranquero's avatar
Juanma Barranquero committed
94 95 96 97 98 99 100 101 102 103 104 105
The symbol returned is `gud-<MINOR-MODE>-<SYM>'.
If SOFT is non-nil, returns nil if the symbol doesn't already exist."
  (unless (or minor-mode gud-minor-mode) (error "Gud internal error"))
  (funcall (if soft 'intern-soft 'intern)
	   (format "gud-%s-%s" (or minor-mode gud-minor-mode) sym)))

(defun gud-val (sym &optional minor-mode)
  "Return the value of `gud-symbol' SYM.  Default to nil."
  (let ((sym (gud-symbol sym t minor-mode)))
    (if (boundp sym) (symbol-value sym))))

(defvar gud-running nil
Nick Roberts's avatar
Nick Roberts committed
106 107
  "Non-nil if debugged program is running.
Used to grey out relevant toolbar icons.")
Juanma Barranquero's avatar
Juanma Barranquero committed
108

109 110 111
(defvar gud-target-name "--unknown--"
  "The apparent name of the program being debugged in a gud buffer.")

112
;; Use existing Info buffer, if possible.
Nick Roberts's avatar
Nick Roberts committed
113 114 115
(defun gud-goto-info ()
  "Go to relevant Emacs info node."
  (interactive)
116 117 118 119 120 121 122 123 124 125 126
  (let ((same-window-regexps same-window-regexps)
	(display-buffer-reuse-frames t))
    (catch 'info-found
      (walk-windows
       '(lambda (window)
	  (if (eq (window-buffer window) (get-buffer "*info*"))
	      (progn
		(setq same-window-regexps nil)
		(throw 'info-found nil))))
       nil 0)
      (select-frame (make-frame)))
127
    (if (eq gud-minor-mode 'gdbmi)
128 129
	(info "(emacs)GDB Graphical Interface")
      (info "(emacs)Debuggers"))))
Nick Roberts's avatar
Nick Roberts committed
130

131 132
(defun gud-tool-bar-item-visible-no-fringe ()
  (not (or (eq (buffer-local-value 'major-mode (window-buffer)) 'speedbar-mode)
133 134
	   (eq (buffer-local-value 'major-mode (window-buffer)) 'gdb-memory-mode)
	   (and (eq gud-minor-mode 'gdbmi)
135 136
		(> (car (window-fringes)) 0)))))

137 138
(declare-function gdb-gud-context-command "gdb-mi.el")

139 140
(defun gud-stop-subjob ()
  (interactive)
141
  (with-current-buffer gud-comint-buffer
142 143 144 145 146 147
    (cond ((string-equal gud-target-name "emacs")
           (comint-stop-subjob))
          ((eq gud-minor-mode 'jdb)
           (gud-call "suspend"))
          ((eq gud-minor-mode 'gdbmi)
           (gud-call (gdb-gud-context-command "-exec-interrupt")))
148
          (t
149
           (comint-interrupt-subjob)))))
150

Juanma Barranquero's avatar
Juanma Barranquero committed
151
(easy-mmode-defmap gud-menu-map
Nick Roberts's avatar
Nick Roberts committed
152
  '(([help]     "Info (debugger)" . gud-goto-info)
153
    ([tooltips] menu-item "Show GUD tooltips" gud-tooltip-mode
154 155 156
                  :enable (and (not emacs-basic-display)
			       (display-graphic-p)
			       (fboundp 'x-show-tip))
Nick Roberts's avatar
Nick Roberts committed
157
		  :visible (memq gud-minor-mode
158
				'(gdbmi dbx sdb xdb pdb))
159
	          :button (:toggle . gud-tooltip-mode))
160
    ([refresh]	"Refresh" . gud-refresh)
Juanma Barranquero's avatar
Juanma Barranquero committed
161
    ([run]	menu-item "Run" gud-run
Nick Roberts's avatar
Nick Roberts committed
162
                  :enable (not gud-running)
Nick Roberts's avatar
Nick Roberts committed
163
		  :visible (memq gud-minor-mode '(gdbmi gdb dbx jdb)))
164
    ([go]	menu-item (if gdb-active-process "Continue" "Run") gud-go
165 166
		  :visible (and (eq gud-minor-mode 'gdbmi)
                                (gdb-show-run-p)))
167
    ([stop]	menu-item "Stop" gud-stop-subjob
168 169
		  :visible (or (not (memq gud-minor-mode '(gdbmi pdb)))
			       (gdb-show-stop-p)))
170
    ([until]	menu-item "Continue to selection" gud-until
Nick Roberts's avatar
Nick Roberts committed
171
                  :enable (not gud-running)
172
		  :visible (and (memq gud-minor-mode '(gdbmi gdb perldb))
Nick Roberts's avatar
Nick Roberts committed
173
				(gud-tool-bar-item-visible-no-fringe)))
Juanma Barranquero's avatar
Juanma Barranquero committed
174
    ([remove]	menu-item "Remove Breakpoint" gud-remove
175
                  :enable (not gud-running)
176
		  :visible (gud-tool-bar-item-visible-no-fringe))
Juanma Barranquero's avatar
Juanma Barranquero committed
177
    ([tbreak]	menu-item "Temporary Breakpoint" gud-tbreak
Nick Roberts's avatar
Nick Roberts committed
178 179
                  :enable (not gud-running)
		  :visible (memq gud-minor-mode
180
				'(gdbmi gdb sdb xdb)))
Juanma Barranquero's avatar
Juanma Barranquero committed
181
    ([break]	menu-item "Set Breakpoint" gud-break
182
                  :enable (not gud-running)
183
		  :visible (gud-tool-bar-item-visible-no-fringe))
Juanma Barranquero's avatar
Juanma Barranquero committed
184
    ([up]	menu-item "Up Stack" gud-up
Nick Roberts's avatar
Nick Roberts committed
185 186
		  :enable (not gud-running)
		  :visible (memq gud-minor-mode
187
				 '(gdbmi gdb dbx xdb jdb pdb)))
Juanma Barranquero's avatar
Juanma Barranquero committed
188
    ([down]	menu-item "Down Stack" gud-down
Nick Roberts's avatar
Nick Roberts committed
189 190
		  :enable (not gud-running)
		  :visible (memq gud-minor-mode
191
				 '(gdbmi gdb dbx xdb jdb pdb)))
192
    ([pp]	menu-item "Print S-expression" gud-pp
193
                  :enable (and (not gud-running)
194
				  gdb-active-process)
195 196 197
		  :visible (and (string-equal
				 (buffer-local-value
				  'gud-target-name gud-comint-buffer) "emacs")
198 199 200 201
				(eq gud-minor-mode 'gdbmi)))
    ([print*]	menu-item (if (eq gud-minor-mode 'jdb)
			      "Dump object"
			    "Print Dereference") gud-pstar
Nick Roberts's avatar
Nick Roberts committed
202
                  :enable (not gud-running)
203
		  :visible (memq gud-minor-mode '(gdbmi gdb jdb)))
Juanma Barranquero's avatar
Juanma Barranquero committed
204
    ([print]	menu-item "Print Expression" gud-print
205
                  :enable (not gud-running))
206
    ([watch]	menu-item "Watch Expression" gud-watch
Nick Roberts's avatar
Nick Roberts committed
207
		  :enable (not gud-running)
208
	  	  :visible (eq gud-minor-mode 'gdbmi))
209
    ([finish]	menu-item "Finish Function" gud-finish
Nick Roberts's avatar
Nick Roberts committed
210 211
                  :enable (not gud-running)
		  :visible (memq gud-minor-mode
212
				 '(gdbmi gdb xdb jdb pdb)))
Juanma Barranquero's avatar
Juanma Barranquero committed
213
    ([stepi]	menu-item "Step Instruction" gud-stepi
Nick Roberts's avatar
Nick Roberts committed
214
                  :enable (not gud-running)
215
		  :visible (memq gud-minor-mode '(gdbmi gdb dbx)))
Juanma Barranquero's avatar
Juanma Barranquero committed
216
    ([nexti]	menu-item "Next Instruction" gud-nexti
Nick Roberts's avatar
Nick Roberts committed
217
                  :enable (not gud-running)
218
		  :visible (memq gud-minor-mode '(gdbmi gdb dbx)))
Juanma Barranquero's avatar
Juanma Barranquero committed
219
    ([step]	menu-item "Step Line" gud-step
220
                  :enable (not gud-running))
Juanma Barranquero's avatar
Juanma Barranquero committed
221
    ([next]	menu-item "Next Line" gud-next
222
                  :enable (not gud-running))
Juanma Barranquero's avatar
Juanma Barranquero committed
223
    ([cont]	menu-item "Continue" gud-cont
224
                  :enable (not gud-running)
225
		  :visible (not (eq gud-minor-mode 'gdbmi))))
Juanma Barranquero's avatar
Juanma Barranquero committed
226 227 228 229
  "Menu for `gud-mode'."
  :name "Gud")

(easy-mmode-defmap gud-minor-mode-map
Nick Roberts's avatar
Nick Roberts committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
  (append
     `(([menu-bar debug] . ("Gud" . ,gud-menu-map)))
     ;; Get tool bar like functionality from the menu bar on a text only
     ;; terminal.
   (unless window-system
     `(([menu-bar down]
	. (,(propertize "down" 'face 'font-lock-doc-face) . gud-down))
       ([menu-bar up]
	. (,(propertize "up" 'face 'font-lock-doc-face) . gud-up))
       ([menu-bar finish]
	. (,(propertize "finish" 'face 'font-lock-doc-face) . gud-finish))
       ([menu-bar step]
	. (,(propertize "step" 'face 'font-lock-doc-face) . gud-step))
       ([menu-bar next]
	. (,(propertize "next" 'face 'font-lock-doc-face) . gud-next))
       ([menu-bar until] menu-item
	,(propertize "until" 'face 'font-lock-doc-face) gud-until
247
		  :visible (memq gud-minor-mode '(gdbmi gdb perldb)))
Nick Roberts's avatar
Nick Roberts committed
248
       ([menu-bar cont] menu-item
Nick Roberts's avatar
Nick Roberts committed
249
	,(propertize "cont" 'face 'font-lock-doc-face) gud-cont
250
	:visible (not (eq gud-minor-mode 'gdbmi)))
Nick Roberts's avatar
Nick Roberts committed
251 252 253
       ([menu-bar run] menu-item
	,(propertize "run" 'face 'font-lock-doc-face) gud-run
	:visible (memq gud-minor-mode '(gdbmi gdb dbx jdb)))
254
       ([menu-bar go] menu-item
255
	,(propertize " go " 'face 'font-lock-doc-face) gud-go
256 257
	:visible (and (eq gud-minor-mode 'gdbmi)
                      (gdb-show-run-p)))
Nick Roberts's avatar
Nick Roberts committed
258 259
       ([menu-bar stop] menu-item
	,(propertize "stop" 'face 'font-lock-doc-face) gud-stop-subjob
260 261 262
	:visible (or (and (eq gud-minor-mode 'gdbmi)
                          (gdb-show-stop-p))
		     (not (eq gud-minor-mode 'gdbmi))))
Nick Roberts's avatar
Nick Roberts committed
263 264 265 266 267 268 269
       ([menu-bar print]
	. (,(propertize "print" 'face 'font-lock-doc-face) . gud-print))
       ([menu-bar tools] . undefined)
       ([menu-bar buffer] . undefined)
       ([menu-bar options] . undefined)
       ([menu-bar edit] . undefined)
       ([menu-bar file] . undefined))))
Juanma Barranquero's avatar
Juanma Barranquero committed
270 271 272 273 274 275 276 277 278 279 280 281
  "Map used in visited files.")

(let ((m (assq 'gud-minor-mode minor-mode-map-alist)))
  (if m (setcdr m gud-minor-mode-map)
    (push (cons 'gud-minor-mode gud-minor-mode-map) minor-mode-map-alist)))

(defvar gud-mode-map
  ;; Will inherit from comint-mode via define-derived-mode.
  (make-sparse-keymap)
  "`gud-mode' keymap.")

(defvar gud-tool-bar-map
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
  (let ((map (make-sparse-keymap)))
    (dolist (x '((gud-break . "gud/break")
		 (gud-remove . "gud/remove")
		 (gud-print . "gud/print")
		 (gud-pstar . "gud/pstar")
		 (gud-pp . "gud/pp")
		 (gud-watch . "gud/watch")
		 (gud-run . "gud/run")
		 (gud-go . "gud/go")
		 (gud-stop-subjob . "gud/stop")
		 (gud-cont . "gud/cont")
		 (gud-until . "gud/until")
		 (gud-next . "gud/next")
		 (gud-step . "gud/step")
		 (gud-finish . "gud/finish")
		 (gud-nexti . "gud/nexti")
		 (gud-stepi . "gud/stepi")
		 (gud-up . "gud/up")
		 (gud-down . "gud/down")
		 (gud-goto-info . "info"))
	       map)
      (tool-bar-local-item-from-menu
       (car x) (cdr x) map gud-minor-mode-map))))
Juanma Barranquero's avatar
Juanma Barranquero committed
305 306 307 308

(defun gud-file-name (f)
  "Transform a relative file name to an absolute file name.
Uses `gud-<MINOR-MODE>-directories' to find the source files."
309 310 311 312 313
  ;; When `default-directory' is a remote file name, prepend its
  ;; remote part to f, which is the local file name.  Fortunately,
  ;; `file-remote-p' returns exactly this remote file name part (or
  ;; nil otherwise).
  (setq f (concat (or (file-remote-p default-directory) "") f))
Juanma Barranquero's avatar
Juanma Barranquero committed
314 315 316 317 318 319 320 321 322 323 324
  (if (file-exists-p f) (expand-file-name f)
    (let ((directories (gud-val 'directories))
	  (result nil))
      (while directories
	(let ((path (expand-file-name f (car directories))))
	  (if (file-exists-p path)
	      (setq result path
		    directories nil)))
	(setq directories (cdr directories)))
      result)))

325
(declare-function gdb-create-define-alist "gdb-mi" ())
Glenn Morris's avatar
Glenn Morris committed
326

Juanma Barranquero's avatar
Juanma Barranquero committed
327 328 329 330 331 332 333 334 335 336 337 338 339
(defun gud-find-file (file)
  ;; Don't get confused by double slashes in the name that comes from GDB.
  (while (string-match "//+" file)
    (setq file (replace-match "/" t t file)))
  (let ((minor-mode gud-minor-mode)
	(buf (funcall (or gud-find-file 'gud-file-name) file)))
    (when (stringp buf)
      (setq buf (and (file-readable-p buf) (find-file-noselect buf 'nowarn))))
    (when buf
      ;; Copy `gud-minor-mode' to the found buffer to turn on the menu.
      (with-current-buffer buf
	(set (make-local-variable 'gud-minor-mode) minor-mode)
	(set (make-local-variable 'tool-bar-map) gud-tool-bar-map)
340
	(when (and gud-tooltip-mode
341
		   (eq gud-minor-mode 'gdbmi))
342 343 344
	  (make-local-variable 'gdb-define-alist)
	  (unless  gdb-define-alist (gdb-create-define-alist))
	  (add-hook 'after-save-hook 'gdb-create-define-alist nil t))
Juanma Barranquero's avatar
Juanma Barranquero committed
345 346 347 348 349 350 351 352 353 354
	(make-local-variable 'gud-keep-buffer))
      buf)))

;; ======================================================================
;; command definition

;; This macro is used below to define some basic debugger interface commands.
;; Of course you may use `gud-def' with any other debugger command, including
;; user defined ones.

355 356
;; A macro call like (gud-def FUNC CMD KEY DOC) expands to a form
;; which defines FUNC to send the command CMD to the debugger, gives
Juanma Barranquero's avatar
Juanma Barranquero committed
357 358 359 360 361
;; it the docstring DOC, and binds that function to KEY in the GUD
;; major mode.  The function is also bound in the global keymap with the
;; GUD prefix.

(defmacro gud-def (func cmd key &optional doc)
362
  "Define FUNC to be a command sending CMD and bound to KEY, with
Juanma Barranquero's avatar
Juanma Barranquero committed
363 364 365
optional doc string DOC.  Certain %-escapes in the string arguments
are interpreted specially if present.  These are:

Nick Roberts's avatar
Nick Roberts committed
366 367 368 369 370 371 372 373 374
  %f -- Name (without directory) of current source file.
  %F -- Name (without directory or extension) of current source file.
  %d -- Directory of current source file.
  %l -- Number of current source line.
  %e -- Text of the C lvalue or function-call expression surrounding point.
  %a -- Text of the hexadecimal address surrounding point.
  %p -- Prefix argument to the command (if any) as a number.
  %c -- Fully qualified class name derived from the expression
        surrounding point (jdb only).
Juanma Barranquero's avatar
Juanma Barranquero committed
375 376 377 378 379 380 381 382 383 384 385

  The `current' source file is the file of the current buffer (if
we're in a C file) or the source file current at the last break or
step (if we're in the GUD buffer).
  The `current' line is that of the current buffer (if we're in a
source file) or the source line number at the last break or step (if
we're in the GUD buffer)."
  `(progn
     (defun ,func (arg)
       ,@(if doc (list doc))
       (interactive "p")
386 387 388 389
       (if (not gud-running)
	 ,(if (stringp cmd)
	      `(gud-call ,cmd arg)
	    cmd)))
Juanma Barranquero's avatar
Juanma Barranquero committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
     ,(if key `(local-set-key ,(concat "\C-c" key) ',func))
     ,(if key `(global-set-key (vconcat gud-key-prefix ,key) ',func))))

;; Where gud-display-frame should put the debugging arrow; a cons of
;; (filename . line-number).  This is set by the marker-filter, which scans
;; the debugger's output for indications of the current program counter.
(defvar gud-last-frame nil)

;; Used by gud-refresh, which should cause gud-display-frame to redisplay
;; the last frame, even if it's been called before and gud-last-frame has
;; been set to nil.
(defvar gud-last-last-frame nil)

;; All debugger-specific information is collected here.
;; Here's how it works, in case you ever need to add a debugger to the mode.
;;
;; Each entry must define the following at startup:
;;
;;<name>
;; comint-prompt-regexp
;; gud-<name>-massage-args
;; gud-<name>-marker-filter
;; gud-<name>-find-file
;;
;; The job of the massage-args method is to modify the given list of
;; debugger arguments before running the debugger.
;;
;; The job of the marker-filter method is to detect file/line markers in
;; strings and set the global gud-last-frame to indicate what display
;; action (if any) should be triggered by the marker.  Note that only
;; whatever the method *returns* is displayed in the buffer; thus, you
;; can filter the debugger's output, interpreting some and passing on
;; the rest.
;;
;; The job of the find-file method is to visit and return the buffer indicated
;; by the car of gud-tag-frame.  This may be a file name, a tag name, or
;; something else.

;; ======================================================================
;; speedbar support functions and variables.
(eval-when-compile (require 'speedbar))	;For speedbar-with-attached-buffer.

(defvar gud-last-speedbar-stackframe nil
  "Description of the currently displayed GUD stack.
434
The value t means that there is no stack, and we are in display-file mode.")
Juanma Barranquero's avatar
Juanma Barranquero committed
435 436 437 438

(defvar gud-speedbar-key-map nil
  "Keymap used when in the buffers display mode.")

439 440 441
(defun gud-speedbar-item-info ()
  "Display the data type of the watch expression element."
  (let ((var (nth (- (line-number-at-pos (point)) 2) gdb-var-list)))
442 443
    (if (nth 7 var)
	(speedbar-message "%s: %s" (nth 7 var) (nth 3 var))
444
      (speedbar-message "%s" (nth 3 var)))))
445

Juanma Barranquero's avatar
Juanma Barranquero committed
446 447 448 449 450 451 452 453
(defun gud-install-speedbar-variables ()
  "Install those variables used by speedbar to enhance gud/gdb."
  (if gud-speedbar-key-map
      nil
    (setq gud-speedbar-key-map (speedbar-make-specialized-keymap))

    (define-key gud-speedbar-key-map "j" 'speedbar-edit-line)
    (define-key gud-speedbar-key-map "e" 'speedbar-edit-line)
454
    (define-key gud-speedbar-key-map "\C-m" 'speedbar-edit-line)
455
    (define-key gud-speedbar-key-map " " 'speedbar-toggle-line-expansion)
456 457
    (define-key gud-speedbar-key-map "D" 'gdb-var-delete)
    (define-key gud-speedbar-key-map "p" 'gud-pp))
458 459 460

  (speedbar-add-expansion-list '("GUD" gud-speedbar-menu-items
				 gud-speedbar-key-map
461 462
				 gud-expansion-speedbar-buttons))

463
  (add-to-list
464 465 466
   'speedbar-mode-functions-list
   '("GUD" (speedbar-item-info . gud-speedbar-item-info)
     (speedbar-line-directory . ignore))))
467

Juanma Barranquero's avatar
Juanma Barranquero committed
468
(defvar gud-speedbar-menu-items
469
  '(["Jump to stack frame" speedbar-edit-line
470 471
     :visible (not (eq (buffer-local-value 'gud-minor-mode gud-comint-buffer)
		    'gdbmi))]
472
    ["Edit value" speedbar-edit-line
473 474
     :visible (eq (buffer-local-value 'gud-minor-mode gud-comint-buffer)
		    'gdbmi)]
475
    ["Delete expression" gdb-var-delete
476 477
     :visible (eq (buffer-local-value 'gud-minor-mode gud-comint-buffer)
		    'gdbmi)]
478 479
    ["Auto raise frame" gdb-speedbar-auto-raise
     :style toggle :selected gdb-speedbar-auto-raise
480 481
     :visible (eq (buffer-local-value 'gud-minor-mode gud-comint-buffer)
		    'gdbmi)]
482
    ("Output Format"
483 484
     :visible (eq (buffer-local-value 'gud-minor-mode gud-comint-buffer)
		    'gdbmi)
485 486 487
     ["Binary" (gdb-var-set-format "binary") t]
     ["Natural" (gdb-var-set-format  "natural") t]
     ["Hexadecimal" (gdb-var-set-format "hexadecimal") t]))
Juanma Barranquero's avatar
Juanma Barranquero committed
488 489 490 491 492 493 494
  "Additional menu items to add to the speedbar frame.")

;; Make sure our special speedbar mode is loaded
(if (featurep 'speedbar)
    (gud-install-speedbar-variables)
  (add-hook 'speedbar-load-hook 'gud-install-speedbar-variables))

495
(defun gud-expansion-speedbar-buttons (directory zero)
496 497
  "Wrapper for call to `speedbar-add-expansion-list'.
DIRECTORY and ZERO are not used, but are required by the caller."
498 499
  (gud-speedbar-buttons gud-comint-buffer))

Juanma Barranquero's avatar
Juanma Barranquero committed
500 501 502
(defun gud-speedbar-buttons (buffer)
  "Create a speedbar display based on the current state of GUD.
If the GUD BUFFER is not running a supported debugger, then turn
503
off the specialized speedbar mode.  BUFFER is not used, but is
504
required by the caller."
505
  (when (and gud-comint-buffer
506 507
	     ;; gud-comint-buffer might be killed
	     (buffer-name gud-comint-buffer))
508 509
    (let* ((minor-mode (with-current-buffer buffer gud-minor-mode))
	  (window (get-buffer-window (current-buffer) 0))
510
	  (start (window-start window))
511
	  (p (window-point window)))
512
      (cond
513
       ((eq minor-mode 'gdbmi)
514 515 516 517 518 519
	(erase-buffer)
	(insert "Watch Expressions:\n")
	(let ((var-list gdb-var-list) parent)
	  (while var-list
	    (let* (char (depth 0) (start 0) (var (car var-list))
			(varnum (car var)) (expr (nth 1 var))
520
			(type (if (nth 3 var) (nth 3 var) " "))
521 522
			(value (nth 4 var)) (status (nth 5 var))
			(has-more (nth 6 var)))
523 524 525 526 527 528 529 530
	      (put-text-property
	       0 (length expr) 'face font-lock-variable-name-face expr)
	      (put-text-property
	       0 (length type) 'face font-lock-type-face type)
	      (while (string-match "\\." varnum start)
		(setq depth (1+ depth)
		      start (1+ (match-beginning 0))))
	      (if (eq depth 0) (setq parent nil))
531 532 533 534
	      (if (and (or (not has-more) (string-equal has-more "0"))
		       (or (equal (nth 2 var) "0")
			   (and (equal (nth 2 var) "1")
			   (string-match "char \\*$" type)) ))
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
		  (speedbar-make-tag-line
		   'bracket ?? nil nil
		   (concat expr "\t" value)
		   (if (or parent (eq status 'out-of-scope))
		       nil 'gdb-edit-value)
		   nil
		   (if gdb-show-changed-values
		       (or parent (case status
				    (changed 'font-lock-warning-face)
				    (out-of-scope 'shadow)
				    (t t)))
		     t)
		   depth)
		(if (eq status 'out-of-scope) (setq parent 'shadow))
		(if (and (nth 1 var-list)
			 (string-match (concat varnum "\\.")
				       (car (nth 1 var-list))))
		    (setq char ?-)
		  (setq char ?+))
554
		(if (string-match "\\*$\\|\\*&$" type)
555
		    (speedbar-make-tag-line
556 557 558
		     'bracket char
		     'gdb-speedbar-expand-node varnum
		     (concat expr "\t" type "\t" value)
559 560 561 562 563 564 565
		     (if (or parent (eq status 'out-of-scope))
			 nil 'gdb-edit-value)
		     nil
		     (if gdb-show-changed-values
			 (or parent (case status
				      (changed 'font-lock-warning-face)
				      (out-of-scope 'shadow)
566 567 568
				      (t t)))
		       t)
		     depth)
569 570 571 572 573 574 575 576 577
		  (speedbar-make-tag-line
		   'bracket char
		   'gdb-speedbar-expand-node varnum
		   (concat expr "\t" type)
		   nil nil
		   (if (and (or parent status) gdb-show-changed-values)
		       'shadow t)
		   depth))))
	    (setq var-list (cdr var-list)))))
578 579 580 581
       (t (unless (and (save-excursion
			 (goto-char (point-min))
			 (looking-at "Current Stack:"))
		       (equal gud-last-last-frame gud-last-speedbar-stackframe))
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	    (let ((gud-frame-list
	    (cond ((eq minor-mode 'gdb)
		   (gud-gdb-get-stackframe buffer))
		  ;; Add more debuggers here!
		  (t (speedbar-remove-localized-speedbar-support buffer)
		     nil))))
	      (erase-buffer)
	      (if (not gud-frame-list)
		  (insert "No Stack frames\n")
		(insert "Current Stack:\n"))
	      (dolist (frame gud-frame-list)
		(insert (nth 1 frame) ":\n")
		(if (= (length frame) 2)
		(progn
		  (speedbar-insert-button (car frame)
					  'speedbar-directory-face
					  nil nil nil t))
		(speedbar-insert-button
		 (car frame)
		 'speedbar-file-face
		 'speedbar-highlight-face
603
		 (cond ((memq minor-mode '(gdbmi gdb))
604 605 606
			'gud-gdb-goto-stackframe)
		       (t (error "Should never be here")))
		 frame t))))
607
	    (setq gud-last-speedbar-stackframe gud-last-last-frame))))
608
      (set-window-start window start)
609
      (set-window-point window p))))
Juanma Barranquero's avatar
Juanma Barranquero committed
610 611 612 613 614 615 616 617


;; ======================================================================
;; gdb functions

;; History of argument lists passed to gdb.
(defvar gud-gdb-history nil)

618 619
(defcustom gud-gud-gdb-command-name "gdb --fullname"
  "Default command to run an executable under GDB in text command mode.
620
The option \"--fullname\" must be included in this value."
Juanma Barranquero's avatar
Juanma Barranquero committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
   :type 'string
   :group 'gud)

(defvar gud-gdb-marker-regexp
  ;; This used to use path-separator instead of ":";
  ;; however, we found that on both Windows 32 and MSDOS
  ;; a colon is correct here.
  (concat "\032\032\\(.:?[^" ":" "\n]*\\)" ":"
	  "\\([0-9]*\\)" ":" ".*\n"))

;; There's no guarantee that Emacs will hand the filter the entire
;; marker at once; it could be broken up across several strings.  We
;; might even receive a big chunk with several markers in it.  If we
;; receive a chunk of text which looks like it might contain the
;; beginning of a marker, we save it here between calls to the
;; filter.
(defvar gud-marker-acc "")
(make-variable-buffer-local 'gud-marker-acc)

(defun gud-gdb-marker-filter (string)
  (setq gud-marker-acc (concat gud-marker-acc string))
  (let ((output ""))

    ;; Process all the complete markers in this chunk.
    (while (string-match gud-gdb-marker-regexp gud-marker-acc)
      (setq

       ;; Extract the frame position from the marker.
       gud-last-frame (cons (match-string 1 gud-marker-acc)
650
			    (string-to-number (match-string 2 gud-marker-acc)))
Juanma Barranquero's avatar
Juanma Barranquero committed
651 652 653 654 655 656 657 658 659

       ;; Append any text before the marker to the output we're going
       ;; to return - we don't include the marker in this text.
       output (concat output
		      (substring gud-marker-acc 0 (match-beginning 0)))

       ;; Set the accumulator to the remaining text.
       gud-marker-acc (substring gud-marker-acc (match-end 0))))

660
    (while (string-match "\n\032\032\\(.*\\)\n" gud-marker-acc)
661
      (let ((match (match-string 1 gud-marker-acc)))
662

663 664 665 666 667
	(setq
	 ;; Append any text before the marker to the output we're going
	 ;; to return - we don't include the marker in this text.
	 output (concat output
			(substring gud-marker-acc 0 (match-beginning 0)))
668

669
	 ;; Set the accumulator to the remaining text.
670

671
	 gud-marker-acc (substring gud-marker-acc (match-end 0)))))
672

Juanma Barranquero's avatar
Juanma Barranquero committed
673 674
    ;; Does the remaining text look like it might end with the
    ;; beginning of another marker?  If it does, then keep it in
675
    ;; gud-marker-acc until we receive the rest of it.  Since we
Juanma Barranquero's avatar
Juanma Barranquero committed
676 677
    ;; know the full marker regexp above failed, it's pretty simple to
    ;; test for marker starts.
678
    (if (string-match "\n\\(\032.*\\)?\\'" gud-marker-acc)
Juanma Barranquero's avatar
Juanma Barranquero committed
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
	(progn
	  ;; Everything before the potential marker start can be output.
	  (setq output (concat output (substring gud-marker-acc
						 0 (match-beginning 0))))

	  ;; Everything after, we save, to combine with later input.
	  (setq gud-marker-acc
		(substring gud-marker-acc (match-beginning 0))))

      (setq output (concat output gud-marker-acc)
	    gud-marker-acc ""))

    output))

(easy-mmode-defmap gud-minibuffer-local-map
  '(("\C-i" . comint-dynamic-complete-filename))
  "Keymap for minibuffer prompting of gud startup command."
  :inherit minibuffer-local-map)

(defun gud-query-cmdline (minor-mode &optional init)
699
  (let* ((hist-sym (gud-symbol 'history nil minor-mode))
Juanma Barranquero's avatar
Juanma Barranquero committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
	 (cmd-name (gud-val 'command-name minor-mode)))
    (unless (boundp hist-sym) (set hist-sym nil))
    (read-from-minibuffer
     (format "Run %s (like this): " minor-mode)
     (or (car-safe (symbol-value hist-sym))
	 (concat (or cmd-name (symbol-name minor-mode))
		 " "
		 (or init
		     (let ((file nil))
		       (dolist (f (directory-files default-directory) file)
			 (if (and (file-executable-p f)
				  (not (file-directory-p f))
				  (or (not file)
				      (file-newer-than-file-p f file)))
			     (setq file f)))))))
     gud-minibuffer-local-map nil
     hist-sym)))

718
(defvar gdb-first-prompt t)
719

720 721 722
(defvar gud-filter-pending-text nil
  "Non-nil means this is text that has been saved for later in `gud-filter'.")

723 724
;; If in gdb mode, gdb-mi is loaded.
(declare-function gdb-restore-windows "gdb-mi" ())
Glenn Morris's avatar
Glenn Morris committed
725

726
;; The old gdb command (text command mode).  The new one is in gdb-mi.el.
Juanma Barranquero's avatar
Juanma Barranquero committed
727
;;;###autoload
728
(defun gud-gdb (command-line)
Juanma Barranquero's avatar
Juanma Barranquero committed
729
  "Run gdb on program FILE in buffer *gud-FILE*.
Nick Roberts's avatar
Nick Roberts committed
730
The directory containing FILE becomes the initial working
731
directory and source-file directory for your debugger."
732
  (interactive (list (gud-query-cmdline 'gud-gdb)))
Juanma Barranquero's avatar
Juanma Barranquero committed
733

734
  (when (and gud-comint-buffer
735
	   (buffer-name gud-comint-buffer)
736
	   (get-buffer-process gud-comint-buffer)
737 738 739 740
	   (with-current-buffer gud-comint-buffer (eq gud-minor-mode 'gdbmi)))
    (gdb-restore-windows)
    (error
     "Multiple debugging requires restarting in text command mode"))
741

742 743
  (gud-common-init command-line nil 'gud-gdb-marker-filter)
  (set (make-local-variable 'gud-minor-mode) 'gdb)
Juanma Barranquero's avatar
Juanma Barranquero committed
744 745

  (gud-def gud-break  "break %f:%l"  "\C-b" "Set breakpoint at current line.")
746 747 748 749 750 751 752 753 754
  (gud-def gud-tbreak "tbreak %f:%l" "\C-t"
	   "Set temporary breakpoint at current line.")
  (gud-def gud-remove "clear %f:%l" "\C-d" "Remove breakpoint at current line")
  (gud-def gud-step   "step %p"     "\C-s" "Step one source line with display.")
  (gud-def gud-stepi  "stepi %p"    "\C-i" "Step one instruction with display.")
  (gud-def gud-next   "next %p"     "\C-n" "Step one line (skip functions).")
  (gud-def gud-nexti  "nexti %p" nil   "Step one instruction (skip functions).")
  (gud-def gud-cont   "cont"     "\C-r" "Continue with display.")
  (gud-def gud-finish "finish"   "\C-f" "Finish executing current function.")
Nick Roberts's avatar
Nick Roberts committed
755 756 757
  (gud-def gud-jump
	   (progn (gud-call "tbreak %f:%l") (gud-call "jump %f:%l"))
	   "\C-j" "Set execution address to current line.")
Juanma Barranquero's avatar
Juanma Barranquero committed
758

759 760 761 762
  (gud-def gud-up     "up %p"     "<" "Up N stack frames (numeric arg).")
  (gud-def gud-down   "down %p"   ">" "Down N stack frames (numeric arg).")
  (gud-def gud-print  "print %e"  "\C-p" "Evaluate C expression at point.")
  (gud-def gud-pstar  "print* %e" nil
763
	   "Evaluate C dereferenced pointer expression at point.")
764

765
  ;; For debugging Emacs only.
766 767 768 769
  (gud-def gud-pv "pv1 %e"      "\C-v" "Print the value of the lisp variable.")

  (gud-def gud-until  "until %l" "\C-u" "Continue to current line.")
  (gud-def gud-run    "run"	 nil    "Run the program.")
Juanma Barranquero's avatar
Juanma Barranquero committed
770 771 772 773

  (local-set-key "\C-i" 'gud-gdb-complete-command)
  (setq comint-prompt-regexp "^(.*gdb[+]?) *")
  (setq paragraph-start comint-prompt-regexp)
774
  (setq gdb-first-prompt t)
Nick Roberts's avatar
Nick Roberts committed
775
  (setq gud-running nil)
776
  (setq gud-filter-pending-text nil)
777
  (run-hooks 'gud-gdb-mode-hook))
Juanma Barranquero's avatar
Juanma Barranquero committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794

;; One of the nice features of GDB is its impressive support for
;; context-sensitive command completion.  We preserve that feature
;; in the GUD buffer by using a GDB command designed just for Emacs.

;; The completion process filter indicates when it is finished.
(defvar gud-gdb-fetch-lines-in-progress)

;; Since output may arrive in fragments we accumulate partials strings here.
(defvar gud-gdb-fetch-lines-string)

;; We need to know how much of the completion to chop off.
(defvar gud-gdb-fetch-lines-break)

;; The completion list is constructed by the process filter.
(defvar gud-gdb-fetched-lines)

795
(defun gud-gdb-complete-command (&optional command a b)
Juanma Barranquero's avatar
Juanma Barranquero committed
796 797 798 799
  "Perform completion on the GDB command preceding point.
This is implemented using the GDB `complete' command which isn't
available with older versions of GDB."
  (interactive)
800 801 802 803 804 805 806
  (if command
      ;; Used by gud-watch in mini-buffer.
      (setq command (concat "p " command))
    ;; Used in GUD buffer.
    (let ((end (point)))
      (setq command (buffer-substring (comint-line-beginning-position) end))))
  (let* ((command-word
Juanma Barranquero's avatar
Juanma Barranquero committed
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
	  ;; Find the word break.  This match will always succeed.
	  (and (string-match "\\(\\`\\| \\)\\([^ ]*\\)\\'" command)
	       (substring command (match-beginning 2))))
	 (complete-list
	  (gud-gdb-run-command-fetch-lines (concat "complete " command)
					   (current-buffer)
					   ;; From string-match above.
					   (match-beginning 2))))
    ;; Protect against old versions of GDB.
    (and complete-list
	 (string-match "^Undefined command: \"complete\"" (car complete-list))
	 (error "This version of GDB doesn't support the `complete' command"))
    ;; Sort the list like readline.
    (setq complete-list (sort complete-list (function string-lessp)))
    ;; Remove duplicates.
    (let ((first complete-list)
	  (second (cdr complete-list)))
      (while second
	(if (string-equal (car first) (car second))
	    (setcdr first (setq second (cdr second)))
	  (setq first second
		second (cdr second)))))
    ;; Add a trailing single quote if there is a unique completion
    ;; and it contains an odd number of unquoted single quotes.
    (and (= (length complete-list) 1)
	 (let ((str (car complete-list))
	       (pos 0)
	       (count 0))
	   (while (string-match "\\([^'\\]\\|\\\\'\\)*'" str pos)
	     (setq count (1+ count)
		   pos (match-end 0)))
	   (and (= (mod count 2) 1)
		(setq complete-list (list (concat str "'"))))))
    ;; Let comint handle the rest.
    (comint-dynamic-simple-complete command-word complete-list)))

;; The completion process filter is installed temporarily to slurp the
;; output of GDB up to the next prompt and build the completion list.
(defun gud-gdb-fetch-lines-filter (string filter)
  "Filter used to read the list of lines output by a command.
STRING is the output to filter.
It is passed through FILTER before we look at it."
  (setq string (funcall filter string))
  (setq string (concat gud-gdb-fetch-lines-string string))
  (while (string-match "\n" string)
    (push (substring string gud-gdb-fetch-lines-break (match-beginning 0))
	  gud-gdb-fetched-lines)
    (setq string (substring string (match-end 0))))
  (if (string-match comint-prompt-regexp string)
      (progn
	(setq gud-gdb-fetch-lines-in-progress nil)
	string)
    (progn
      (setq gud-gdb-fetch-lines-string string)
      "")))

;; gdb speedbar functions

(defun gud-gdb-goto-stackframe (text token indent)
  "Goto the stackframe described by TEXT, TOKEN, and INDENT."
  (speedbar-with-attached-buffer
   (gud-basic-call (concat "server frame " (nth 1 token)))
   (sit-for 1)))

(defvar gud-gdb-fetched-stack-frame nil
  "Stack frames we are fetching from GDB.")

;(defun gud-gdb-get-scope-data (text token indent)
;  ;; checkdoc-params: (indent)
;  "Fetch data associated with a stack frame, and expand/contract it.
;Data to do this is retrieved from TEXT and TOKEN."
;  (let ((args nil) (scope nil))
;    (gud-gdb-run-command-fetch-lines "info args")
;
;    (gud-gdb-run-command-fetch-lines "info local")
;
;    ))

(defun gud-gdb-get-stackframe (buffer)
  "Extract the current stack frame out of the GUD GDB BUFFER."
  (let ((newlst nil)
	(fetched-stack-frame-list
	 (gud-gdb-run-command-fetch-lines "server backtrace" buffer)))
    (if (and (car fetched-stack-frame-list)
	     (string-match "No stack" (car fetched-stack-frame-list)))
	;; Go into some other mode???
	nil
      (dolist (e fetched-stack-frame-list)
	(let ((name nil) (num nil))
	  (if (not (or
		    (string-match "^#\\([0-9]+\\) +[0-9a-fx]+ in \\([:0-9a-zA-Z_]+\\) (" e)
		    (string-match "^#\\([0-9]+\\) +\\([:0-9a-zA-Z_]+\\) (" e)))
	      (if (not (string-match
900
			"at \\([-0-9a-zA-Z_/.]+\\):\\([0-9]+\\)$" e))
Juanma Barranquero's avatar
Juanma Barranquero committed
901 902 903 904 905 906 907 908 909 910 911
		  nil
		(setcar newlst
			(list (nth 0 (car newlst))
			      (nth 1 (car newlst))
			      (match-string 1 e)
			      (match-string 2 e))))
	    (setq num (match-string 1 e)
		  name (match-string 2 e))
	    (setq newlst
		  (cons
		   (if (string-match
912
			"at \\([-0-9a-zA-Z_/.]+\\):\\([0-9]+\\)$" e)
Juanma Barranquero's avatar
Juanma Barranquero committed
913 914 915 916 917 918 919 920 921 922 923 924
		       (list name num (match-string 1 e)
			     (match-string 2 e))
		     (list name num))
		   newlst)))))
      (nreverse newlst))))

;(defun gud-gdb-selected-frame-info (buffer)
;  "Learn GDB information for the currently selected stack frame in BUFFER."
;  )

(defun gud-gdb-run-command-fetch-lines (command buffer &optional skip)
  "Run COMMAND, and return the list of lines it outputs.
925
BUFFER is the current buffer which may be the GUD buffer in which to run.
926
SKIP is the number of chars to skip on each line, it defaults to 0."
927 928 929 930 931 932
  (with-current-buffer gud-comint-buffer
    (if (and (eq gud-comint-buffer buffer)
	     (save-excursion
	       (goto-char (point-max))
	       (forward-line 0)
	       (not (looking-at comint-prompt-regexp))))
Juanma Barranquero's avatar
Juanma Barranquero committed
933 934 935 936 937 938 939 940
	nil
      ;; Much of this copied from GDB complete, but I'm grabbing the stack
      ;; frame instead.
      (let ((gud-gdb-fetch-lines-in-progress t)
	    (gud-gdb-fetched-lines nil)
	    (gud-gdb-fetch-lines-string nil)
	    (gud-gdb-fetch-lines-break (or skip 0))
	    (gud-marker-filter
941 942
	     `(lambda (string)
		(gud-gdb-fetch-lines-filter string ',gud-marker-filter))))
Juanma Barranquero's avatar
Juanma Barranquero committed
943 944 945 946
	;; Issue the command to GDB.
	(gud-basic-call command)
	;; Slurp the output.
	(while gud-gdb-fetch-lines-in-progress
947
	  (accept-process-output (get-buffer-process gud-comint-buffer)))
Juanma Barranquero's avatar
Juanma Barranquero committed
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
	(nreverse gud-gdb-fetched-lines)))))


;; ======================================================================
;; sdb functions

;; History of argument lists passed to sdb.
(defvar gud-sdb-history nil)

(defvar gud-sdb-needs-tags (not (file-exists-p "/var"))
  "If nil, we're on a System V Release 4 and don't need the tags hack.")

(defvar gud-sdb-lastfile nil)

(defun gud-sdb-marker-filter (string)
  (setq gud-marker-acc
	(if gud-marker-acc (concat gud-marker-acc string) string))
  (let (start)
    ;; Process all complete markers in this chunk
    (while
	(cond
	 ;; System V Release 3.2 uses this format
	 ((string-match "\\(^\\|\n\\)\\*?\\(0x\\w* in \\)?\\([^:\n]*\\):\\([0-9]*\\):.*\n"
			gud-marker-acc start)
	  (setq gud-last-frame
		(cons (match-string 3 gud-marker-acc)
974
		      (string-to-number (match-string 4 gud-marker-acc)))))
Juanma Barranquero's avatar
Juanma Barranquero committed
975 976 977 978 979 980
	 ;; System V Release 4.0 quite often clumps two lines together
	 ((string-match "^\\(BREAKPOINT\\|STEPPED\\) process [0-9]+ function [^ ]+ in \\(.+\\)\n\\([0-9]+\\):"
			gud-marker-acc start)
	  (setq gud-sdb-lastfile (match-string 2 gud-marker-acc))
	  (setq gud-last-frame
		(cons gud-sdb-lastfile
981
		      (string-to-number (match-string 3 gud-marker-acc)))))
Juanma Barranquero's avatar
Juanma Barranquero committed
982 983 984 985 986 987 988 989
	 ;; System V Release 4.0
	 ((string-match "^\\(BREAKPOINT\\|STEPPED\\) process [0-9]+ function [^ ]+ in \\(.+\\)\n"
			gud-marker-acc start)
	  (setq gud-sdb-lastfile (match-string 2 gud-marker-acc)))
	 ((and gud-sdb-lastfile (string-match "^\\([0-9]+\\):"
					      gud-marker-acc start))
	       (setq gud-last-frame
		     (cons gud-sdb-lastfile
990
			   (string-to-number (match-string 1 gud-marker-acc)))))
Juanma Barranquero's avatar
Juanma Barranquero committed
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
	 (t
	  (setq gud-sdb-lastfile nil)))
      (setq start (match-end 0)))

    ;; Search for the last incomplete line in this chunk
    (while (string-match "\n" gud-marker-acc start)
      (setq start (match-end 0)))

    ;; If we have an incomplete line, store it in gud-marker-acc.
    (setq gud-marker-acc (substring gud-marker-acc (or start 0))))
  string)

(defun gud-sdb-find-file (f)
  (if gud-sdb-needs-tags (find-tag-noselect f) (find-file-noselect f)))

;;;###autoload
(defun sdb (command-line)
  "Run sdb on program FILE in buffer *gud-FILE*.
The directory containing FILE becomes the initial working directory
and source-file directory for your debugger."
  (interactive (list (gud-query-cmdline 'sdb)))

1013
  (if gud-sdb-needs-tags (require 'etags))
Juanma Barranquero's avatar
Juanma Barranquero committed
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
  (if (and gud-sdb-needs-tags
	   (not (and (boundp 'tags-file-name)
		     (stringp tags-file-name)
		     (file-exists-p tags-file-name))))
      (error "The sdb support requires a valid tags table to work"))

  (gud-common-init command-line nil 'gud-sdb-marker-filter 'gud-sdb-find-file)
  (set (make-local-variable 'gud-minor-mode) 'sdb)

  (gud-def gud-break  "%l b" "\C-b"   "Set breakpoint at current line.")
  (gud-def gud-tbreak "%l c" "\C-t"   "Set temporary breakpoint at current line.")
  (gud-def gud-remove "%l d" "\C-d"   "Remove breakpoint at current line")
  (gud-def gud-step   "s %p" "\C-s"   "Step one source line with display.")
  (gud-def gud-stepi  "i %p" "\C-i"   "Step one instruction with display.")
  (gud-def gud-next   "S %p" "\C-n"   "Step one line (skip functions).")
  (gud-def gud-cont   "c"    "\C-r"   "Continue with display.")
  (gud-def gud-print  "%e/"  "\C-p"   "Evaluate C expression at point.")

  (setq comint-prompt-regexp  "\\(^\\|\n\\)\\*")
  (setq paragraph-start comint-prompt-regexp)
  (run-hooks 'sdb-mode-hook)
  )

;; ======================================================================
;; dbx functions

;; History of argument lists passed to dbx.
(defvar gud-dbx-history nil)

(defcustom gud-dbx-directories nil
  "*A list of directories that dbx should search for source code.
If nil, only source files in the program directory
will be known to dbx.

The file names should be absolute, or relative to the directory
containing the executable being debugged."
  :type '(choice (const :tag "Current Directory" nil)
		 (repeat :value ("")
			 directory))
  :group 'gud)

(defun gud-dbx-massage-args (file args)
  (nconc (let ((directories gud-dbx-directories)
	       (result nil))
	   (while directories
	     (setq result (cons (car directories) (cons "-I" result)))
	     (setq directories (cdr directories)))
	   (nreverse result))
	 args))

(defun gud-dbx-marker-filter (string)
  (setq gud-marker-acc (if gud-marker-acc (concat gud-marker-acc string) string))

  (let (start)
    ;; Process all complete markers in this chunk.
    (while (or (string-match
		"stopped in .* at line \\([0-9]*\\) in file \"\\([^\"]*\\)\""
		gud-marker-acc start)
	       (string-match
		"signal .* in .* at line \\([0-9]*\\) in file \"\\([^\"]*\\)\""
		gud-marker-acc start))
      (setq gud-last-frame
	    (cons (match-string 2 gud-marker-acc)
1077
		  (string-to-number (match-string 1 gud-marker-acc)))
Juanma Barranquero's avatar
Juanma Barranquero committed
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
	    start (match-end 0)))

    ;; Search for the last incomplete line in this chunk
    (while (string-match "\n" gud-marker-acc start)
      (setq start (match-end 0)))

    ;; If the incomplete line APPEARS to begin with another marker, keep it
    ;; in the accumulator.  Otherwise, clear the accumulator to avoid an
    ;; unnecessary concat during the next call.
    (setq gud-marker-acc
	  (if (string-match "\\(stopped\\|signal\\)" gud-marker-acc start)
	      (substring gud-marker-acc (match-beginning 0))
	    nil)))
  string)

;; Functions for Mips-style dbx.  Given the option `-emacs', documented in
;; OSF1, not necessarily elsewhere, it produces markers similar to gdb's.
(defvar gud-mips-p
  (or (string-match "^mips-[^-]*-ultrix" system-configuration)
      ;; We haven't tested gud on this system:
      (string-match "^mips-[^-]*-riscos" system-configuration)
      ;; It's documented on OSF/1.3
      (string-match "^mips-[^-]*-osf1" system-configuration)
      (string-match "^alpha[^-]*-[^-]*-osf" system-configuration))
  "Non-nil to assume the MIPS/OSF dbx conventions (argument `-emacs').")

(defvar gud-dbx-command-name
  (concat "dbx" (if gud-mips-p " -emacs")))

;; This is just like the gdb one except for the regexps since we need to cope
;; with an optional breakpoint number in [] before the ^Z^Z
(defun gud-mipsdbx-marker-filter (string)
  (setq gud-marker-acc (concat gud-marker-acc string))
  (let ((output ""))

    ;; Process all the complete markers in this chunk.
    (while (string-match
	    ;; This is like th gdb marker but with an optional
	    ;; leading break point number like `[1] '
	    "[][ 0-9]*\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n"
	    gud-marker-acc)
      (setq

       ;; Extract the frame position from the marker.
       gud-last-frame
       (cons (match-string 1 gud-marker-acc)
1124
	     (string-to-number (match-string 2 gud-marker-acc)))
Juanma Barranquero's avatar
Juanma Barranquero committed
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178

       ;; Append any text before the marker to the output we're going
       ;; to return - we don't include the marker in this text.
       output (concat output
		      (substring gud-marker-acc 0 (match-beginning 0)))

       ;; Set the accumulator to the remaining text.
       gud-marker-acc (substring gud-marker-acc (match-end 0))))

    ;; Does the remaining text look like it might end with the
    ;; beginning of another marker?  If it does, then keep it in
    ;; gud-marker-acc until we receive the rest of it.  Since we
    ;; know the full marker regexp above failed, it's pretty simple to
    ;; test for marker starts.
    (if (string-match "[][ 0-9]*\032.*\\'" gud-marker-acc)
	(progn
	  ;; Everything before the potential marker start can be output.
	  (setq output (concat output (substring gud-marker-acc
						 0 (match-beginning 0))))

	  ;; Everything after, we save, to combine with later input.
	  (setq gud-marker-acc
		(substring gud-marker-acc (match-beginning 0))))

      (setq output (concat output gud-marker-acc)
	    gud-marker-acc ""))

    output))

;; The dbx in IRIX is a pain.  It doesn't print the file name when
;; stopping at a breakpoint (but you do get it from the `up' and
;; `down' commands...).  The only way to extract the information seems
;; to be with a `file' command, although the current line number is
;; available in $curline.  Thus we have to look for output which
;; appears to indicate a breakpoint.  Then we prod the dbx sub-process
;; to output the information we want with a combination of the
;; `printf' and `file' commands as a pseudo marker which we can
;; recognise next time through the marker-filter.  This would be like
;; the gdb marker but you can't get the file name without a newline...
;; Note that gud-remove won't work since Irix dbx expects a breakpoint
;; number rather than a line number etc.  Maybe this could be made to
;; work by listing all the breakpoints and picking the one(s) with the
;; correct line number, but life's too short.
;;   d.love@dl.ac.uk (Dave Love) can be blamed for this

(defvar gud-irix-p
  (and (string-match "^mips-[^-]*-irix" system-configuration)
       (not (string-match "irix[6-9]\\.[1-9]" system-configuration)))
  "Non-nil to assume the interface appropriate for IRIX dbx.
This works in IRIX 4, 5 and 6, but `gud-dbx-use-stopformat-p' provides
a better solution in 6.1 upwards.")
(defvar gud-dbx-use-stopformat-p
  (string-match "irix[6-9]\\.[1-9]" system-configuration)
  "Non-nil to use the dbx feature present at least from Irix 6.1