]> code.delx.au - gnu-emacs/blobdiff - lisp/gud.el
(while): Comment out converter.
[gnu-emacs] / lisp / gud.el
index 411277c80612962c6293f2443a3c4b3f216f0846..9d7ccdb8b00f4ffa33c070cdb2a131f06cf1a197 100644 (file)
@@ -1,4 +1,5 @@
-;;; gud.el --- Grand Unified Debugger mode for gdb, sdb, or dbx under Emacs
+;;; gud.el --- Grand Unified Debugger mode for gdb, sdb, dbx, or xdb
+;;;            under Emacs
 
 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
 ;; Version: 1.3
@@ -28,7 +29,8 @@
 ;; It was later rewritten by rms.  Some ideas were due to Masanobu. 
 ;; Grand Unification (sdb/dbx support) by Eric S. Raymond <esr@thyrsus.com>
 ;; The overloading code was then rewritten by Barry Warsaw <bwarsaw@cen.com>,
-;; who also hacked the mode to use comint.el.
+;; who also hacked the mode to use comint.el.  Shane Hartman <shane@spr.com>
+;; added support for xdb (HPUX debugger).
 
 ;;; Code:
 
@@ -55,7 +57,7 @@ This association list has elements of the form
    (function (lambda (p) (fset (car p) (symbol-function (cdr p)))))
    gud-overload-alist))
 
-(defun gud-debugger-startup (file args)
+(defun gud-massage-args (file args)
   (error "GUD not properly entered."))
 
 (defun gud-marker-filter (str)
@@ -114,6 +116,11 @@ we're in the GUD buffer)."
 ;; 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)
+
 ;; All debugger-specific information is collected here.
 ;; Here's how it works, in case you ever need to add a debugger to the mode.
 ;;
@@ -121,12 +128,12 @@ we're in the GUD buffer)."
 ;;
 ;;<name>
 ;; comint-prompt-regexp
-;; gud-<name>-debugger-startup
+;; gud-<name>-massage-args
 ;; gud-<name>-marker-filter
 ;; gud-<name>-find-file
 ;;
-;; The job of the startup-command method is to fire up a copy of the debugger,
-;; given a list of debugger arguments.
+;; 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
@@ -142,8 +149,11 @@ we're in the GUD buffer)."
 ;; ======================================================================
 ;; gdb functions
 
-(defun gud-gdb-debugger-startup (file args)
-  (apply 'make-comint (concat "gud-" file) "gdb" nil "-fullname" args))
+;;; History of argument lists passed to gdb.
+(defvar gud-gdb-history nil)
+
+(defun gud-gdb-massage-args (file args)
+  (cons "-fullname" (cons file args)))
 
 (defun gud-gdb-marker-filter (string)
   (if (string-match  "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n" string)
@@ -165,17 +175,23 @@ we're in the GUD buffer)."
   (find-file-noselect f))
 
 ;;;###autoload
-(defun gdb (args)
+(defun gdb (command-line)
   "Run gdb on program FILE in buffer *gud-FILE*.
 The directory containing FILE becomes the initial working directory
 and source-file directory for your debugger."
-  (interactive "sRun gdb (like this): gdb ")
-  (gud-overload-functions '((gud-debugger-startup . gud-gdb-debugger-startup)
-                           (gud-marker-filter    . gud-gdb-marker-filter)
-                           (gud-find-file        . gud-gdb-find-file)
+  (interactive
+   (list (read-from-minibuffer "Run gdb (like this): "
+                              (if (consp gud-gdb-history)
+                                  (car gud-gdb-history)
+                                "gdb ")
+                              nil nil
+                              '(gud-gdb-history . 1))))
+  (gud-overload-functions '((gud-massage-args . gud-gdb-massage-args)
+                           (gud-marker-filter . gud-gdb-marker-filter)
+                           (gud-find-file . gud-gdb-find-file)
                            ))
 
-  (gud-common-init args)
+  (gud-common-init command-line)
 
   (gud-def gud-break  "break %f:%l"  "\C-b" "Set breakpoint at current line.")
   (gud-def gud-tbreak "tbreak %f:%l" "\C-t" "Set breakpoint at current line.")
@@ -197,13 +213,16 @@ and source-file directory for your debugger."
 ;; ======================================================================
 ;; 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-debugger-startup (file args)
-  (apply 'make-comint (concat "gud-" file) "sdb" nil args))
+(defun gud-sdb-massage-args (file args)
+  (cons file args))
 
 (defun gud-sdb-marker-filter (string)
   (cond 
@@ -236,20 +255,26 @@ and source-file directory for your debugger."
     (find-file-noselect f)))
 
 ;;;###autoload
-(defun sdb (args)
+(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 "sRun sdb (like this): sdb ")
+  (interactive
+   (list (read-from-minibuffer "Run sdb (like this): "
+                              (if (consp gud-sdb-history)
+                                  (car gud-sdb-history)
+                                "sdb ")
+                              nil nil
+                              '(gud-sdb-history . 1))))
   (if (and gud-sdb-needs-tags
           (not (and (boundp 'tags-file-name) (file-exists-p tags-file-name))))
       (error "The sdb support requires a valid tags table to work."))
-  (gud-overload-functions '((gud-debugger-startup . gud-sdb-debugger-startup)
-                           (gud-marker-filter    . gud-sdb-marker-filter)
-                           (gud-find-file        . gud-sdb-find-file)
+  (gud-overload-functions '((gud-massage-args . gud-sdb-massage-args)
+                           (gud-marker-filter . gud-sdb-marker-filter)
+                           (gud-find-file . gud-sdb-find-file)
                            ))
 
-  (gud-common-init args)
+  (gud-common-init command-line)
 
   (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.")
@@ -267,8 +292,11 @@ and source-file directory for your debugger."
 ;; ======================================================================
 ;; dbx functions
 
-(defun gud-dbx-debugger-startup (file args)
-  (apply 'make-comint (concat "gud-" file) "dbx" nil args))
+;;; History of argument lists passed to dbx.
+(defvar gud-dbx-history nil)
+
+(defun gud-dbx-massage-args (file args)
+  (cons file args))
 
 (defun gud-dbx-marker-filter (string)
   (if (string-match
@@ -284,17 +312,23 @@ and source-file directory for your debugger."
   (find-file-noselect f))
 
 ;;;###autoload
-(defun dbx (args)
+(defun dbx (command-line)
   "Run dbx on program FILE in buffer *gud-FILE*.
 The directory containing FILE becomes the initial working directory
 and source-file directory for your debugger."
-  (interactive "sRun dbx (like this): dbx")
-  (gud-overload-functions '((gud-debugger-startup . gud-dbx-debugger-startup)
-                           (gud-marker-filter    . gud-dbx-marker-filter)
-                           (gud-find-file        . gud-dbx-find-file)
+  (interactive
+   (list (read-from-minibuffer "Run dbx (like this): "
+                              (if (consp gud-dbx-history)
+                                  (car gud-dbx-history)
+                                "dbx ")
+                              nil nil
+                              '(gud-dbx-history . 1))))
+  (gud-overload-functions '((gud-massage-args . gud-dbx-massage-args)
+                           (gud-marker-filter . gud-dbx-marker-filter)
+                           (gud-find-file . gud-dbx-find-file)
                            ))
 
-  (gud-common-init args)
+  (gud-common-init command-line)
 
   (gud-def gud-break  "stop at \"%f\":%l"
                                  "\C-b" "Set breakpoint at current line.")
@@ -311,6 +345,107 @@ and source-file directory for your debugger."
   (run-hooks 'dbx-mode-hook)
   )
 
+;; ======================================================================
+;; xdb (HP PARISC debugger) functions
+
+;;; History of argument lists passed to xdb.
+(defvar gud-xdb-history nil)
+
+(defvar gud-xdb-directories nil
+  "*A list of directories that xdb should search for source code.
+If nil, only source files in the program directory
+will be known to xdb.
+
+The file names should be absolute, or relative to the directory
+containing the executable being debugged.")
+
+(defun gud-xdb-massage-args (file args)
+  (nconc (let ((directories gud-xdb-directories)
+              (result nil))
+          (while directories
+            (setq result (cons (car directories) (cons "-d" result)))
+            (setq directories (cdr directories)))
+          (nreverse (cons file result)))
+        args))
+
+(defun gud-xdb-file-name (f)
+  "Transform a relative pathname to a full pathname in xdb mode"
+  (let ((result nil))
+    (if (file-exists-p f)
+        (setq result (expand-file-name f))
+      (let ((directories gud-xdb-directories))
+        (while directories
+          (let ((path (concat (car directories) "/" f)))
+            (if (file-exists-p path)
+                (setq result (expand-file-name path)
+                      directories nil)))
+          (setq directories (cdr directories)))))
+    result))
+
+;; xdb does not print the lines all at once, so we have to accumulate them
+(defvar gud-xdb-accumulation "")
+
+(defun gud-xdb-marker-filter (string)
+  (let (result)
+    (if (or (string-match comint-prompt-regexp string)
+            (string-match ".*\012" string))
+        (setq result (concat gud-xdb-accumulation string)
+              gud-xdb-accumulation "")
+      (setq gud-xdb-accumulation (concat gud-xdb-accumulation string)))
+    (if result
+        (if (or (string-match "\\([^\n \t:]+\\): [^:]+: \\([0-9]+\\):" result)
+                (string-match "[^: \t]+:[ \t]+\\([^:]+\\): [^:]+: \\([0-9]+\\):"
+                              result))
+            (let ((line (string-to-int 
+                         (substring result (match-beginning 2) (match-end 2))))
+                  (file (gud-xdb-file-name
+                         (substring result (match-beginning 1) (match-end 1)))))
+              (if file
+                  (setq gud-last-frame (cons file line))))))
+    (or result "")))    
+               
+(defun gud-xdb-find-file (f)
+  (let ((realf (gud-xdb-file-name f)))
+    (if realf (find-file-noselect realf))))
+
+;;;###autoload
+(defun xdb (command-line)
+  "Run xdb on program FILE in buffer *gud-FILE*.
+The directory containing FILE becomes the initial working directory
+and source-file directory for your debugger.
+
+You can set the variable 'gud-xdb-directories' to a list of program source
+directories if your program contains sources from more than one directory."
+  (interactive
+   (list (read-from-minibuffer "Run xdb (like this): "
+                              (if (consp gud-xdb-history)
+                                  (car gud-xdb-history)
+                                "xdb ")
+                              nil nil
+                              '(gud-xdb-history . 1))))
+  (gud-overload-functions '((gud-massage-args . gud-xdb-massage-args)
+                           (gud-marker-filter . gud-xdb-marker-filter)
+                           (gud-find-file . gud-xdb-find-file)))
+
+  (gud-common-init command-line)
+
+  (gud-def gud-break  "b %f:%l"    "\C-b" "Set breakpoint at current line.")
+  (gud-def gud-tbreak "b %f:%l\\t" "\C-t"
+           "Set temporary breakpoint at current line.")
+  (gud-def gud-remove "db"         "\C-d" "Remove breakpoint at current line")
+  (gud-def gud-step   "s %p"      "\C-s" "Step one line 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-up     "up %p"     "<"    "Up (numeric arg) stack frames.")
+  (gud-def gud-down   "down %p"           ">"    "Down (numeric arg) stack frames.")
+  (gud-def gud-finish "bu\\t"      "\C-f" "Finish executing current function.")
+  (gud-def gud-print  "p %e"       "\C-p" "Evaluate C expression at point.")
+
+  (setq comint-prompt-regexp  "^>")
+  (make-local-variable 'gud-xdb-accumulation)
+  (setq gud-xdb-accumulation "")
+  (run-hooks 'xdb-mode-hook))
+
 ;;
 ;; End of debugger-specific information
 ;;
@@ -361,9 +496,9 @@ and source-file directory for your debugger."
 (defun gud-mode ()
   "Major mode for interacting with an inferior debugger process.
 
-   You start it up with one of the commands M-x gdb, M-x sdb, or
-M-x dbx.  Each entry point finishes by executing a hook; `gdb-mode-hook',
-`sdb-mode-hook' or `dbx-mode-hook' respectively.
+   You start it up with one of the commands M-x gdb, M-x sdb, M-x dbx,
+or M-x xdb.  Each entry point finishes by executing a hook; `gdb-mode-hook',
+`sdb-mode-hook', `dbx-mode-hook' or `xdb-mode-hook' respectively.
 
 After startup, the following commands are available in both the GUD
 interaction buffer and any source buffer GUD visits due to a breakpoint stop
@@ -386,16 +521,17 @@ and then update the source window with the current file and position.
 \\[gud-print] tries to find the largest C lvalue or function-call expression
 around point, and sends it to the debugger for value display.
 
-The above commands are common to all supported debuggers.
+The above commands are common to all supported debuggers except xdb which
+does not support stepping instructions.
 
-Under gdb and sdb, \\[gud-tbreak] behaves exactly like \\[gud-break],
+Under gdb, sdb and xdb, \\[gud-tbreak] behaves exactly like \\[gud-break],
 except that the breakpoint is temporary; that is, it is removed when
 execution stops on it.
 
-Under gdb and dbx, \\[gud-up] pops up through an enclosing stack
+Under gdb, dbx, and xdb, \\[gud-up] pops up through an enclosing stack
 frame.  \\[gud-down] drops back down through one.
 
-If you are using gdb, \\[gdb-finish] runs execution to the return from
+If you are using gdb or xdb, \\[gud-finish] runs execution to the return from
 the current function and stops.
 
 All the keystrokes above are accessible in the GUD buffer
@@ -426,37 +562,41 @@ comint mode, which see."
 
 (defvar gud-comint-buffer nil)
 
-(defun gud-common-init (args)
-  ;; Perform initializations common to all debuggers
-  ;; There *must* be a cleaner way to lex the arglist...
-  (let (file i)
-    (if (string= args "")
-       (setq args nil)
-      (save-excursion
-       (set-buffer (get-buffer-create "*gud-scratch*"))
-       (erase-buffer)
-       (insert args)
-       (goto-char (point-max))
-       (insert "\")")
-       (goto-char (point-min))
-       (insert "(\"")
-       (while (re-search-forward " +" nil t)
-         (replace-match "\" \"" nil nil))
-       (goto-char (point-min))
-       (while (re-search-forward "\"\"" nil t)
-         (replace-match "" nil nil))
-       (setq args (read (buffer-string)))
-       (kill-buffer (current-buffer))))
-    (setq i (1- (length args)))
-    (while (and (>= i 0) (not (= (aref (nth i args) 0) ?-)))
-      (setq file (nth i args)) (setq i (1- i)))
-    (let* ((path (expand-file-name file))
-          (filepart (file-name-nondirectory path)))
+;; Chop STRING into words separated by SPC or TAB and return a list of them.
+(defun gud-chop-words (string)
+  (let ((i 0) (beg 0)
+       (len (length string))
+       (words nil))
+    (while (< i len)
+      (if (memq (aref string i) '(?\t ? ))
+         (progn
+           (setq words (cons (substring string beg i) words)
+                 beg (1+ i))
+           (while (and (< beg len) (memq (aref string beg) '(?\t ? )))
+             (setq beg (1+ beg)))
+           (setq i (1+ beg)))
+       (setq i (1+ i))))
+    (if (< beg len)
+       (setq words (cons (substring string beg) words)))
+    (nreverse words)))
+
+;; Perform initializations common to all debuggers.
+(defun gud-common-init (command-line)
+  (let* ((words (gud-chop-words command-line))
+        (program (car words))
+        (file-word (let ((w (cdr words)))
+                     (while (and w (= ?- (aref (car w) 0)))
+                       (setq w (cdr w)))
+                     (car w)))
+        (args (delq file-word (cdr words)))
+        (file (expand-file-name file-word))
+        (filepart (file-name-nondirectory file)))
       (switch-to-buffer (concat "*gud-" filepart "*"))
-      (setq default-directory (file-name-directory path))
+      (setq default-directory (file-name-directory file))
       (or (bolp) (newline))
       (insert "Current directory is " default-directory "\n")
-      (gud-debugger-startup filepart args)))
+      (apply 'make-comint (concat "gud-" filepart) program nil
+            (gud-massage-args file args)))
   (gud-mode)
   (set-process-filter (get-buffer-process (current-buffer)) 'gud-filter)
   (set-process-sentinel (get-buffer-process (current-buffer)) 'gud-sentinel)
@@ -540,7 +680,8 @@ Obeying it means displaying in another window the specified file and line."
    (progn
      (gud-set-buffer)
      (gud-display-line (car gud-last-frame) (cdr gud-last-frame))
-     (setq gud-last-frame nil))))
+     (setq gud-last-last-frame gud-last-frame
+          gud-last-frame nil))))
 
 ;; Make sure the file named TRUE-FILE is in a buffer that appears on the screen
 ;; and that its line LINE is visible.
@@ -665,6 +806,7 @@ Obeying it means displaying in another window the specified file and line."
   "Fix up a possibly garbled display, and redraw the arrow."
   (interactive "P")
   (recenter arg)
+  (or gud-last-frame (setq gud-last-frame gud-last-last-frame))
   (gud-display-frame))
 \f
 ;;; Code for parsing expressions out of C code.  The single entry point is
@@ -805,4 +947,6 @@ Link exprs of the form:
         )
      (t nil))))
 
+(provide 'gud)
+
 ;;; gud.el ends here