[comp.emacs] dbx.el

rkc@XN.LL.MIT.EDU (rkc) (02/22/89)

I am having trouble with dbx.el.  In short, my problems are:
	1. run-dbx doesn't correctly change to the directory I want to work
in.  Althouth the run-dbx routine does 
	(setq default-directory (file-name-directory path))
	dbx comes up in my home directory.
	2. Dbx-where fails saying:
	Search failed: "stopped in .* at line \\([0-9]*\\) in file
\"\\([^\"]*\\)\""

Any suggestions?
	Thanks,
	-Rob
	

mac@ardent.com (Michael McNamara) (02/23/89)

>  Path: ardent!ubvax!vsi1!apple!bloom-beacon!mit-eddie!ll-xn!rkc
>  From: rkc@XN.LL.MIT.EDU (rkc)
>  Newsgroups: comp.emacs
>  Date: 22 Feb 89 14:25:21 GMT
>  Organization: MIT Lincoln Laboratory, Lexington, MA
>  Lines: 13
>  
>  I am having trouble with dbx.el.  In short, my problems are:
>  	1. run-dbx doesn't correctly change to the directory I want to work
>  in.  Althouth the run-dbx routine does 
>  	(setq default-directory (file-name-directory path))
>  	dbx comes up in my home directory.
>  	2. Dbx-where fails saying:
>  	Search failed: "stopped in .* at line \\([0-9]*\\) in file
>  \"\\([^\"]*\\)\""
>  
>  Any suggestions?
>  	Thanks,
>  	-Rob
>  	

	Run the following patch:

*** /grgr/emacs/lisp/dbx.el~	Thu Feb 11 23:14:30 1988
--- /grgr/emacs/lisp/dbx.el	Wed Feb 22 13:36:20 1989
***************
*** 92,98 ****
    (interactive "fProgram to debug: ")
    (let ((file (file-name-nondirectory path)))
      (switch-to-buffer (concat "*dbx-" file "*"))
!     (setq default-directory (file-name-directory path))
      (switch-to-buffer (make-shell (concat "dbx-" file) "dbx" nil file)))
    (set-process-filter (get-buffer-process (current-buffer)) 'dbx-filter)
    (inferior-dbx-mode))
--- 92,99 ----
    (interactive "fProgram to debug: ")
    (let ((file (file-name-nondirectory path)))
      (switch-to-buffer (concat "*dbx-" file "*"))
! ;;  (setq default-directory (file-name-directory path))  ;; deleted mac@ardent
!     (setq default-directory (expand-file-name (file-name-directory path))) ;; added mac@ardent
      (switch-to-buffer (make-shell (concat "dbx-" file) "dbx" nil file)))
    (set-process-filter (get-buffer-process (current-buffer)) 'dbx-filter)
    (inferior-dbx-mode))
--
Michael McNamara 
  mac@ardent.com

rkc@XN.LL.MIT.EDU (rkc) (03/07/89)

In the dbx.el file kindly provided by Robert Lupton, if you change the
variable dbx-break-point to 

	(defvar dbx-break-point
	  "in .* at line \\([0-9]*\\) in file \"\\([^\"]*\\)\""
	  "Regexp of pattern that dbx writes at break point.")

Then dbx-where will work for segmentation violations as well as for break
points.

Now for the question:
	dbx-where doesn't print out the overlay-arrow-string.
	It looks like the code is trying to--but it doesn't succeed.

What's wrong?
	-Rob

lupton@uhccux.uhcc.hawaii.edu (Robert Lupton) (04/29/89)

Here is my occasional posting on dbx-mode. I have added

  Support for multiple directories (the use command)
  Understands `where', `up', and `down'
  Understands segmentation violations etc. (thank you, whoever suggested
this, I'm afraid that I lost your name)
  Added dbx-step, dbx-next, and dbx-cont to step through the source without
a *dbx-proc* window on the screen (and without scribbling on one)
  An alias `dbx' for `run-dbx' (trivial, but annoying!)

It's shorter to repost than to post diffs, so here it is:

			Robert Lupton

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Cut Here -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
;; Run dbx under Emacs
;; Copyright (C) 1988 Free Software Foundation, Inc.
;; Main author Masanobu UMEDA (umerin@flab.fujitsu.junet)

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;
;   Added dbx-use to follow `use' commands.
;      RHL March 1989
;   If tracing is enabled, it now understands up/down/where and
; bus errors, segmentation violations, etc.
;   Added dbx-command which can be bound to keys, to allow stepping
; through source without having a *dbx-proc* window
;      RHL April 1989

(require 'shell)

(defvar dbx-trace-flag nil
  "Dbx trace switch.")

(defvar dbx-process nil
  "Dbx process name.")

(defvar dbx-dir-list nil
  "List of directories searched by dbx-where, set by dbx-use")

(defvar dbx-break-point
  "\\( in .* at\\|),\\) line \\([0-9]*\\) in \\(file \\)?\"\\([^\"]*\\)\""
  "Regexp of pattern that dbx writes at break points, etc.")

(defvar inferior-dbx-mode-map nil)
(if inferior-dbx-mode-map
    nil
  (setq inferior-dbx-mode-map (copy-keymap shell-mode-map))
  (define-key inferior-dbx-mode-map "\C-cu" 'dbx-use)
  (define-key inferior-dbx-mode-map "\C-cw" 'dbx-where)
  (define-key inferior-dbx-mode-map "\C-c\C-t" 'dbx-trace-mode)
  (define-key ctl-x-map "\C-@" 'dbx-stop-at))

(defun inferior-dbx-mode ()
  "Major mode for interacting with an inferior Dbx process.
(Note that dbx is an alias for run-dbx)

The following commands are available:
\\{inferior-dbx-mode-map}

Entry to this mode calls the value of dbx-mode-hook with no arguments,
if that value is non-nil.  Likewise with the value of shell-mode-hook.
dbx-mode-hook is called after shell-mode-hook.

You can display the debugging program in other window and point out
where you are looking at using the command \\[dbx-where].

\\[dbx-trace-mode] toggles dbx-trace mode. In dbx-trace mode,
debugging program is automatically traced using output from dbx.
The dbx commands `down', `up' and `where' are also traced. Using
the command dbx-command (e.g. M-x dbx-command \"step\") allows you to
step through the source without using the *dbx-proc* buffer. The
commands dbx-cont, dbx-next, and dbx-step use this.

The command \\[dbx-stop-at] sets break point at current line of the
program in the buffer. Major mode name of the buffer must be in
dbx-language-mode-list.

You should use the command `dbx-use' (in the mini-buffer, or \\[dbx-use])
rather than `use' so dbx-mode can find your source directories. (Using
use, then dbx-use with no directories specified also works).

Commands:

Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.
\\[shell-send-eof] sends end-of-file as input.
\\[kill-shell-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
\\[interrupt-shell-subjob] interrupts the shell or its current subjob if any.
\\[stop-shell-subjob] stops, likewise. \\[quit-shell-subjob] sends quit signal, likewise.
\\[dbx-use] tells dbx mode about any use commands you have issued
\\[dbx-where] displays debugging program in other window and
 points out where you are looking at.
\\[dbx-trace-mode] toggles dbx-trace mode.
\\[dbx-stop-at] sets break point at current line."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'inferior-dbx-mode)
  (setq mode-name "Inferior Dbx")
  (setq mode-line-process '(": %s"))
  (use-local-map inferior-dbx-mode-map)
  (make-local-variable 'last-input-start)
  (setq last-input-start (make-marker))
  (make-local-variable 'last-input-end)
  (setq last-input-end (make-marker))
  (make-local-variable 'dbx-trace-flag)
  (setq dbx-trace-flag nil)
  (make-variable-buffer-local 'shell-prompt-pattern)
  (setq shell-prompt-pattern "^[^)]*dbx) *") ;Set dbx prompt pattern
  (or (assq 'dbx-trace-flag minor-mode-alist)
      (setq minor-mode-alist
	    (cons '(dbx-trace-flag " Trace") minor-mode-alist)))
  (run-hooks 'shell-mode-hook 'dbx-mode-hook))

(defun dbx (path) "An alias for run-dbx"
  (interactive "fProgram to debug: ")
  (run-dbx path))

(defun run-dbx (path)
  "Run inferior Dbx process on PROGRAM, with I/O via buffer *dbx-PROGRAM*."
  (interactive "fProgram to debug: ")
  (setq path (expand-file-name path))
  (let ((file (file-name-nondirectory path)))
    (switch-to-buffer (concat "*dbx-" file "*"))
    (setq default-directory (file-name-directory path))
    (switch-to-buffer (make-shell (concat "dbx-" file) "dbx" nil file)))
  (setq dbx-process (get-buffer-process (current-buffer)))
  (setq dbx-dir-list (list "."))
  (set-process-filter dbx-process 'dbx-filter)
  (set-process-sentinel dbx-process 'dbx-sentinel)
  (inferior-dbx-mode))

(defun dbx-trace-mode (arg)
  "Toggle dbx-trace mode.
With arg, turn dbx-trace mode on iff arg is positive.
In dbx-trace mode, user program is automatically traced."
  (interactive "P")
  (if (not (eql major-mode 'inferior-dbx-mode))
      (error "Dbx-trace mode is effective in inferior-dbx mode only."))
  (setq dbx-trace-flag
	(if (null arg)
	    (not dbx-trace-flag)
	  (> (prefix-numeric-value arg) 0)))
  (if (not dbx-trace-flag)
      (set-marker overlay-arrow-position nil))
  ;; Force mode line redisplay
  (set-buffer-modified-p (buffer-modified-p)))

(defun dbx-filter (process string)
  "Trace debugging program automatically if dbx-trace-flag is not nil.
Checks two flags: dbx-no-output suppresses output to *dbx-proc* if it exists
and is non-nil, and dbx-wait is set to nil (if it exists) when the dbx prompt
is received."
  (save-excursion
    (set-buffer (process-buffer process))
    (goto-char (point-max))
    (insert string)
    (if (process-mark process)
	(set-marker (process-mark process) (point-max)))
    (goto-char (point-max))
    (if (string-match "(dbx)" string)	; the next prompt
	(progn
	  (if (boundp 'dbx-wait) (setq dbx-wait nil))
	  (if dbx-trace-flag
	      (dbx-where
	       (let ((end (point))
		     (beg (progn
			    (re-search-backward "^(dbx)" 0 t 2) (match-end 0)))
		     (str))
		 (setq str (buffer-substring beg end))
		 (if (and (boundp 'dbx-no-output) dbx-no-output)
		     (progn
		       (delete-region (+ 1 beg) end)
		       (goto-char (point-max))))
		 str))))))
  (if (eq (process-buffer process) (current-buffer))
      (goto-char (point-max))))
  
(defun dbx-sentinel (process string)
  "Catch dbx exiting and clean up"
  (if (string-equal string "finished\n")
      (progn
	(insert "\nProcess has finished\n")
	(if overlay-arrow-position
	    (set-marker overlay-arrow-position nil)))))

(defun dbx-where (&optional string)
 "Display dbx'ed program in other window and point out what you are looking at.
STRING contains dbx's output."
  (interactive)
  (let (file line)
    (if (string-match dbx-break-point string)
	(progn
	  (setq line (substring string (match-beginning 2) (match-end 2)))
	  (setq file (substring string (match-beginning 4) (match-end 4)))
    (if (and file line)			;Find break point?
	(progn
; Look for the file in each of the dbx-dir-list directories
	  (let ((dir dbx-dir-list))
	    (while (and dir (not (file-readable-p
		    (expand-file-name (concat (car dir) "/" file) nil))))
	      (setq dir (cdr dir))
	      )
	    (if (not dir)
		(error (format "Can't find file %s" file)))
	    (setq file (expand-file-name (concat (car dir) "/" file) nil)))
	  (set-buffer (window-buffer))
	  (let ( (looking-at-file (and (buffer-file-name)
		  (string-equal (buffer-file-name) file))) )
	    (if (not looking-at-file) (find-file-other-window file))
	    (goto-line (string-to-int line))
	    (beginning-of-line)
	    (setq overlay-arrow-string "=>")
	    (or overlay-arrow-position 
		(setq overlay-arrow-position (make-marker)))
	    (set-marker overlay-arrow-position (point) (current-buffer))
	    (if (not looking-at-file) (other-window 1))
      )))))))

(defun dbx-stop-at ()
  "Set break point at current line."
  (interactive)
  (let ((file-name (file-name-nondirectory buffer-file-name))
	(line (save-restriction
		(widen)
		(1+ (count-lines
		     1 (save-excursion (beginning-of-line) (point)))))))
    (send-string dbx-process
		 (concat "stop at \"" file-name "\":" line "\n")))
    (accept-process-output dbx-process)
    ; now move point to end of buffer:
    (set-buffer (get-buffer (process-buffer dbx-process)))
    (save-window-excursion
      (switch-to-buffer-other-window (current-buffer))
      (goto-char (point-max)))
    (if (process-mark dbx-process)
	(set-marker (process-mark dbx-process) (point-max))))

(defun dbx-use (dirs)
  "Run dbx's `use' command and parse the output to make a list of
directories for the use of dbx-where. If no directories are specified,
get the current list and parse that"
  (interactive "suse: ")
  (setq dbx-dir-list nil)
  (save-window-excursion
    (if (string-match "[^ \t]" dirs)
	(progn
	  (send-string dbx-process (concat "use " dirs "\n"))
	  (delete-region (save-excursion (beginning-of-line) (dot)) (dot))))
    (send-string dbx-process "use\n")
    (accept-process-output dbx-process)
    (switch-to-buffer (process-buffer dbx-process))
    (end-of-buffer)
    (previous-line 1)
    (beginning-of-line)
    (if (looking-at shell-prompt-pattern)
	(goto-char (match-end 0)))
    (while (looking-at "[ \t]*[^ \t\n]+")
      (progn 
      (setq dbx-dir-list
	    (append dbx-dir-list (list (buffer-substring
					(match-beginning 0) (match-end 0)))))
      (goto-char (match-end 0))
      (skip-chars-forward " \t"))))
      (end-of-buffer))

(defun dbx-command (command)
  "Run a dbx COMMAND from the current buffer, suppressing output to *dbx-proc*.
If you have enabled tracing this enables you to step through the source.
This is probably most useful for `next', `step', and `cont'."
  (interactive "sCommand: ")
  (let ( (dbx-no-output t)		; used by dbx-filter to kill output
	 (dbx-wait t))			; set to nil when next prompt is ready
    (send-string dbx-process (concat command "\n"))
    (while dbx-wait
      (accept-process-output dbx-process))
    )
  (let ( (mark-buffer (marker-buffer overlay-arrow-position))
	 (bw))
    (setq bw (get-buffer-window mark-buffer))
    (if bw (select-window bw)
      (switch-to-buffer mark-buffer)))
  (goto-char (marker-position overlay-arrow-position)))

(defun dbx-cont ()
  "Key-bindable interface to dbx-command, for `cont' command"
  (interactive)
  (dbx-command "cont"))

(defun dbx-next ()
  "Key-bindable interface to dbx-command, for `next' command"
  (interactive)
  (dbx-command "next"))

(defun dbx-step ()
  "Key-bindable interface to dbx-command, for `step' command"
  (interactive)
  (dbx-command "step"))

lupton@uhccux.uhcc.hawaii.edu (Robert Lupton) (07/27/89)

Here's (yet another) repost of dbx.el. I've been doing some debugging
(of C that is, not lisp) and improved it somewhat. The diffs are
bigger than the patches, so it's all here. Thanks to Brian for alpha
testing some of it.

The main changes are:
		(In my previous posting)

;   Added dbx-use to follow `use' commands.
;   If tracing is enabled, it now understands up/down/where and
; bus errors, segmentation violations, etc. (providing that no-one has
; messed with dbx-error messages. E.g. Sun).
;   Added dbx-command which can be bound to keys, to allow stepping
; through source without having a *dbx* window
;   Now runs dbx in current directory, but adds source directory to default
; dbx-dir-list. This allows post-mortem debugging.
;   dbx-command takes an optional repeat-count; this may be provided as
; a prefix argument to dbx-step etc.

		(new in this posting)

;   dbx-print will pass a string or the region to dbx's `print' command,
; and provide the answer in the minibuffer; dbx-print-word will print the
; value of the word under the cursor
;   Only one dbx process is now permitted
;   A prefix argument to dbx-where will try to show the stack frame under the
; cursor (good for going through `where' commands)
;   Added dbx-compile which is the same as compile (q.v.), except that
; it first saves dbx's status (break etc.), then runs compile, and then
; restarts dbx

   Plus sundry other changes (some to the documentation).

Bug fixes/Flames (surely not!)/Improvements to lupton@uhccux.uhcc.hawaii.edu


				Robert

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
;; Run dbx under Emacs
;; Copyright (C) 1988 Free Software Foundation, Inc.
;; Main author Masanobu UMEDA (umerin@flab.fujitsu.junet)
;; Secondary Author Robert Lupton (lupton@uhifa.ifa.hawaii.edu)

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.
;
;   Added dbx-use to follow `use' commands.
;      RHL March 1989
;   If tracing is enabled, it now understands up/down/where and
; bus errors, segmentation violations, etc. (providing that no-one has
; messed with dbx-error messages. E.g. Sun).
;   Added dbx-command which can be bound to keys, to allow stepping
; through source without having a *dbx* window
;      RHL April 1989
;   Now runs dbx in current directory, but adds source directory to default
; dbx-dir-list. This allows post-mortem debugging.
;   dbx-command takes an optional repeat-count; this may be provided as
; a prefix argument to dbx-step etc.
;   dbx-print will pass a string or the region to dbx's `print' command,
; and provide the answer in the minibuffer; dbx-print-word will print the
; value of the word under the cursor
;   Only one dbx process is now permitted
;      RHL June 1989
;   A prefix argument to dbx-where will try to show the stack frame under the
; cursor (good for going through `where' commands)
;   Added dbx-compile which is the same as compile (q.v.), except that
; it first saves dbx's status (break etc.), then runs compile, and then
; restarts dbx
;
(require 'shell)

(defvar dbx-trace-flag nil
  "Dbx trace switch.")

(defvar dbx-process nil
  "Dbx process name.")

(defvar dbx-dir-list nil
  "List of directories searched by dbx-where, set by dbx-use")

(defvar dbx-break-point
  "\\( in .* at\\|),\\) line \\([0-9]*\\)[^\"]*\"?\\([^\"]*\\)?\"?"
  "Regexp of pattern that dbx writes at break points, etc.
the second regular expression in \\\\(\\\\) should give the line number,
the third the file name")

(defvar dbx-last-output
  "The last output from dbx that was suppressed by setting dbx-no-output")

(defvar dbx-downcase-for-print nil
  "*If set, convert strings to lowercase before submitting to dbx's `print'")

(defvar inferior-dbx-mode-map nil)
(if inferior-dbx-mode-map
    nil
  (setq inferior-dbx-mode-map (copy-keymap shell-mode-map))
  (define-key inferior-dbx-mode-map "\C-cu" 'dbx-use)
  (define-key inferior-dbx-mode-map "\C-cw" 'dbx-where)
  (define-key inferior-dbx-mode-map "\C-c\C-t" 'dbx-trace-mode)
  (define-key ctl-x-map "\C-@" 'dbx-stop-at))

(defun inferior-dbx-mode ()
  "Major mode for interacting with an inferior Dbx process.
(Note that dbx is an alias for run-dbx)

The following commands are available:
\\{inferior-dbx-mode-map}

You can display the debugging program in other window and point out
where you are looking at using the command \\[dbx-where]. With a
prefix argument, it'll try to display the position described by the
text under the cursor (this is meant to be used to examine a stack
trace produced by the `where' command; put the cursor on the level you
want to see, and type ^U\\[dbx-where]).

\\[dbx-trace-mode] toggles dbx-trace mode. In dbx-trace mode, the
debugging program is automatically traced using output from dbx.  The
dbx commands `down', `up' and `where' are also traced (If no-one has
messed with error formats, e.g. Sun)

Using the command dbx-command (e.g. M-x dbx-command \"step\") allows
you to step through the source without using the *dbx* buffer.
The commands dbx-cont, dbx-next, and dbx-step use this. They take an
optional prefix argument to indicate how many steps to take. If no
dbx-prompt is emitted (e.g. control is returned to your code) these
may appear to hang: either concatenate your prompt onto the
shell-prompt-pattern string, or hit ^G.

The command dbx-print passes a string to dbx's print command, and
displays the answer in the mini-buffer. If the string is either nil or
empty the current region will be used (i.e. from dot to mark).
dbx-print-word uses this command to print the value of the word under
the cursor. If the variable dbx-downcase-for-print is set then the
string will be converted to lowercase before use.

All of the commands based on dbx-command assume that you have a dbx prompt
in the *dbx* buffer, as they use the dbx process to do their work.

The command \\[dbx-stop-at] sets a break point at the current line of
the program in the buffer. Major mode name of the buffer must be in
dbx-language-mode-list.

You should use the command `dbx-use' (in the mini-buffer, or \\[dbx-use])
rather than `use' so dbx-mode can find your source directories. (Using
use, then dbx-use with no directories specified also works).

If you need to recompile, dbx-compile will first save dbx's environment
(using the status command), then run compile (q.v.), then restart dbx
restoring breakpoints, tracing, etc.

Commands:

Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.
\\[shell-send-eof] sends end-of-file as input.
\\[kill-shell-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
\\[interrupt-shell-subjob] interrupts the shell or its current subjob if any.
\\[stop-shell-subjob] stops, likewise. \\[quit-shell-subjob] sends quit signal, likewise.
\\[dbx-use] tells dbx mode about any use commands you have issued
\\[dbx-where] displays debugging program in other window and
 points out where you are looking at.
\\[dbx-trace-mode] toggles dbx-trace mode.
\\[dbx-stop-at] sets break point at current line."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'inferior-dbx-mode)
  (setq mode-name "Dbx")
  (setq mode-line-process '(": %s"))
  (use-local-map inferior-dbx-mode-map)
  (make-local-variable 'last-input-start)
  (setq last-input-start (make-marker))
  (make-local-variable 'last-input-end)
  (setq last-input-end (make-marker))
  (setq dbx-trace-flag nil)
  (make-variable-buffer-local 'shell-prompt-pattern)
  (setq dbx-prompt ".*(dbx) *")
  (setq shell-prompt-pattern dbx-prompt) ;Set dbx prompt pattern
  (or (assq 'dbx-trace-flag minor-mode-alist)
      (setq minor-mode-alist
	    (cons '(dbx-trace-flag " Trace") minor-mode-alist))))

(defun dbx () "An alias for run-dbx"
  (interactive)
  (call-interactively 'run-dbx))

(defun run-dbx (path)
  "Run inferior Dbx process on PROGRAM, with I/O via buffer *dbx*.

   Running this function calls the value of dbx-mode-hook with no
arguments, if that value is non-nil. Likewise with the value of
shell-mode-hook. dbx-mode-hook is called after shell-mode-hook.  When
the hooks are called, the variable path contains the full path name of
the programme being debugged."

  (interactive "fProgram to debug: ")
  (if (and dbx-process
	   (let ( (status (process-status dbx-process)) )
	     (not (or (equal status 'exit) (equal status 'signal)))))
      (progn
	(let ( (pbuff (process-buffer dbx-process)) pwind )
	  (if (setq pwind (get-buffer-window pbuff))
	      (select-window pwind)
	    (switch-to-buffer pbuff)))
	(if (yes-or-no-p "You already running dbx: kill it? ")
	    (progn
	      (delete-region (point-min) (point-max))
	      (delete-process dbx-process))
	  (error ""))))
  (setq path (expand-file-name path))
  (switch-to-buffer "*dbx*")
  (switch-to-buffer (make-shell "dbx" "dbx" nil path))
  (setq dbx-process (get-buffer-process (current-buffer)))
  (set-process-filter dbx-process 'dbx-filter)
  (set-process-sentinel dbx-process 'dbx-sentinel)
  (inferior-dbx-mode)
  (let ( (dbx-wait t) )
    (while dbx-wait
      (accept-process-output dbx-process)))
  (goto-char (point-max))
  (dbx-use (concat ". " (file-name-directory path)))
  (let ( ( tracing dbx-trace-flag) )
    (dbx-trace-mode 1)
    (if (and (boundp 'dbx-restart-file) (file-readable-p dbx-restart-file))
	(dbx-command (concat "source " dbx-restart-file) 1 t))
    (if (not tracing)
	(dbx-trace-mode -1)))
  (run-hooks 'shell-mode-hook 'dbx-mode-hook))

(defun dbx-trace-mode (arg)
  "Toggle dbx-trace mode.
With arg, turn dbx-trace mode on iff arg is positive.
In dbx-trace mode, user program is automatically traced."
  (interactive "P")
  (setq dbx-trace-flag
	(if (null arg)
	    (not dbx-trace-flag)
	  (> (prefix-numeric-value arg) 0)))
  (if (and (not dbx-trace-flag) overlay-arrow-position)
      (set-marker overlay-arrow-position nil))
  ;; Force mode line redisplay
  (set-buffer-modified-p (buffer-modified-p)))

(defun dbx-filter (process string)
  "Trace debugging program automatically if dbx-trace-flag is not nil.
Checks two flags: dbx-no-output suppresses output to buffer *dbx* if it exists
and is non-nil, and dbx-wait is set to nil (if it exists) when the dbx prompt
is received. If your programme has some prompt of its own then you should
concatenate it with dbx-prompt, i.e.
 (setq shell-prompt-pattern (concat dbx-prompt \"\\\\|my_prompt\")
if you don't want dbx-no-output to throw away all output until the next
dbx-prompt appears."
  (save-excursion
    (set-buffer (process-buffer process))
    (goto-char (point-max))
    (insert string)
    (if (process-mark process)
	(set-marker (process-mark process) (point-max)))
    (goto-char (point-max))
    (if (string-match shell-prompt-pattern string)	; the next prompt
	(progn
	  (if (and (boundp 'dbx-no-output)
		   (not (string-match dbx-prompt string)))
	      (setq dbx-no-output nil))	; we do want output at programme prompt
	  (if (boundp 'dbx-wait) (setq dbx-wait nil))
	  (let ((end (point))
		(beg)
		(search-start (save-excursion (forward-line -20) (point)))
		(str))
	    (re-search-backward shell-prompt-pattern search-start t)
	    (if (re-search-backward shell-prompt-pattern search-start t)
		(setq beg (match-end 0))
	      (setq beg search-start))
	    (setq str (buffer-substring beg end))
	    (if (and (boundp 'dbx-no-output) dbx-no-output)
		(progn
		  (setq dbx-last-output str)
		  (delete-region beg end)
		  (goto-char (point-max))))
	    (if dbx-trace-flag (dbx-show-where str))))))
  (if (eq (process-buffer process) (current-buffer))
      (goto-char (point-max))))
  
(defun dbx-sentinel (process string)
  "Catch dbx exiting and clean up"
  (if (equal string "finished\n")
      (progn
	(insert "\nProcess has finished\n")
	(dbx-trace-mode -1)
	(if overlay-arrow-position
	    (set-marker overlay-arrow-position nil))
	(if (and (boundp 'dbx-restart-file)
		 (file-readable-p dbx-restart-file))
	    (delete-file dbx-restart-file)))))

(defun dbx-where (arg)
  "Display dbx'ed program in other window and point out what you are
looking at.  With a prefix argument, try to interpret the line under
the cursor as part of a stack trace as produced by the `where'
command) and display it."
  (interactive "p")
  (if (= arg 1)				; just run where
      (dbx-command "where" 1 t)
    (let ( (beg (save-excursion (beginning-of-line) (point))) )
      (dbx-show-where (buffer-substring beg (point-max))))))

(defun dbx-show-where (string)
  "Display dbx'ed program in other window and point out what you are
looking at.  STRING contains dbx's output."
;
; This function can be recursive via dbx-filter, hence dbx-file is not 
; inside a (let ...)
;
  (let (file line)
    (if (string-match dbx-break-point string)
	(progn
	  (setq line (substring string (match-beginning 2) (match-end 2)))
	  (setq file (substring string (match-beginning 3) (match-end 3)))
	  (if (equal file "")		; don't know where we are
	      (if (boundp 'dbx-loop)
		  (error "Sorry, I can't figure out which file you are in")
		(let ((dbx-loop nil))	; no file name: try a `where'
		  (dbx-command "where" 1 t)
		  (string-match dbx-break-point dbx-last-output)))
	    (if (string-match "/[^/]*$" file)
		(setq file (substring file (+ 1 (match-beginning 0)) nil)))
	    (setq dbx-file file))
	  (if (and dbx-file line)			;Found break point?
	      (progn
		;; Look for the file in each of the dbx-dir-list directories
		(let ((dir dbx-dir-list))
		  (while (and dir (not (file-readable-p
		      (expand-file-name (concat (car dir) "/" dbx-file) nil))))
		    (setq dir (cdr dir)))
		  (if (not dir)
		      (error (format "Can't find file %s" dbx-file)))
		  (setq dbx-file
		       (expand-file-name (concat (car dir) "/" dbx-file) nil)))
		(set-buffer (window-buffer))
		(let ( (looking-at-file
			(and (buffer-file-name)
			     (equal (buffer-file-name) dbx-file))) )
		  (if (not looking-at-file)
		      (find-file-other-window dbx-file))
		  (goto-line (string-to-int line))
		  (beginning-of-line)
		  (setq overlay-arrow-string "=>")
		  (or overlay-arrow-position 
		      (setq overlay-arrow-position (make-marker)))
		  (set-marker overlay-arrow-position (point) (current-buffer))
		  (if (not looking-at-file) (other-window 1)))))))))

(defun dbx-stop-at ()
  "Set break point at current line."
  (interactive)
  (let ((file-name (file-name-nondirectory buffer-file-name))
	(line (save-restriction
		(widen)
		(1+ (count-lines
		     1 (save-excursion (beginning-of-line) (point)))))))
    (send-string dbx-process
		 (concat "stop at \"" file-name "\":" line "\n")))
    (accept-process-output dbx-process)
    ; now move point to end of buffer:
    (set-buffer (get-buffer (process-buffer dbx-process)))
    (save-window-excursion
      (switch-to-buffer-other-window (current-buffer))
      (goto-char (point-max)))
    (if (process-mark dbx-process)
	(set-marker (process-mark dbx-process) (point-max))))

(defun dbx-use (dirs)
  "Run dbx's `use' command and parse the output to make a list of
directories for the use of dbx-where. If no directories are specified,
get the current list and parse that"
  (interactive "suse: ")
  (setq dbx-dir-list nil)
  (save-window-excursion
    (let ( (dbx-wait t) )		; set to nil when next prompt is ready
      (if (string-match "[^ \t]" dirs)
	  (let ( (dbx-no-output t) )	; used by dbx-filter to kill output
	    (send-string dbx-process (concat "use " dirs "\n"))
	    (while dbx-wait (accept-process-output dbx-process))))
      (setq dbx-wait t)
      (send-string dbx-process "use\n")
      (while dbx-wait (accept-process-output dbx-process)))
    (switch-to-buffer (process-buffer dbx-process))
    (end-of-buffer)
    (previous-line 1)
    (beginning-of-line)
    (while (looking-at shell-prompt-pattern)
	(goto-char (match-end 0)))
    (while (looking-at "[ \t]*[^ \t\n]+")
      (progn 
	(goto-char (match-end 0))
	(skip-chars-forward " \t")
	(setq dbx-dir-list
	      (append dbx-dir-list
		      (list
		       (let ((dir (buffer-substring
				   (match-beginning 0) (match-end 0))))
			 (if (string-match "/$" dir)
			     (setq dir (substring dir 0 -1))
			   dir))))))))
  (end-of-buffer)
  (kill-region (progn (previous-line 1) (dot)) (point-max)))

(defun dbx-command (command &optional num no-trace)
  "Run a dbx COMMAND from the current buffer, suppressing output to *dbx*.
If you have enabled tracing this enables you to step through the source.
This is probably most useful for `next', `step', and `cont'.
Optionally, provide a repeat count NUM. If NO-TRACE is t, don't trace
position in source"
  (interactive "sCommand: ")
  (if (not dbx-trace-flag)
      (error "This command only works if tracing is turned on"))
  (if (not num) (setq num 1))
  (while (>= (setq num (- num 1)) 0)
    (let ( (dbx-no-output t)		; used by dbx-filter to kill output
	   (dbx-wait t))		; set to nil when next prompt is ready
      (send-string dbx-process (concat command "\n"))
      (while dbx-wait
	(accept-process-output dbx-process))
      )
    (if (not no-trace)
    (let ( (mark-buffer) (bw) )
      (if (and overlay-arrow-position
	       (setq mark-buffer (marker-buffer overlay-arrow-position)))
	  (progn
	    (setq bw (get-buffer-window mark-buffer))
	    (if bw (select-window bw)
	      (switch-to-buffer mark-buffer))
	    (goto-char (marker-position overlay-arrow-position))))))))

(defun dbx-cont (num)
  "Key-bindable interface to dbx-command, for `cont' command"
  (interactive "p")
  (dbx-command "cont" num))

(defun dbx-next (num)
  "Key-bindable interface to dbx-command, for `next' command"
  (interactive "p")
  (dbx-command "next" num))

(defun dbx-step (num)
  "Key-bindable interface to dbx-command, for `step' command"
  (interactive "p")
  (dbx-command "step" num))

(defun dbx-print ( &optional expr)
  "Feed an expression to dbx as the subject of a `print' command, and
output the answer in the minibuffer. If the expression is nil or empty
print the region in the current buffer. If the variable dbx-downcase-for-print
is set, convert the expression to lowercase before using it (this may
be useful for languages like fortran). See dbx-print-word for printing
the word under the cursor."
  (interactive "sExpression: ")
  (if (or (not expr) (equal expr ""))
      (setq expr (buffer-substring (region-beginning) (region-end))))
  (if dbx-downcase-for-print
      (setq expr (downcase expr)))
  (dbx-command (concat "print " expr) 1 t)
  (string-match dbx-prompt dbx-last-output)
  (let ( (val (substring dbx-last-output 0 (- (match-beginning 0) 1))) )
    (if (string-match "= *" val)
	(setq val (substring val (match-end 0) nil)))
    (message (format "%s : %s" expr val))))

(defun dbx-print-word (num)
  "print the value of the word under the cursor in the mini-buffer,
with a prefix-argument simply call dbx-print"
  (interactive "p")
  (if (not (= num 1))			; there's a prefix argument
      (call-interactively 'dbx-print)
    (save-excursion
      (let ( (beg (progn (forward-word 1) (dot)))
	     (end (progn (forward-word -1) (dot))) )
	(dbx-print (buffer-substring beg end))))))

(defun dbx-compile ()
  "Compile a programme. Just like compile, except that it first saves
the state of the current dbx session, then compiles, then restarts
dbx. If the compilation fails the status should be recovered when a
dbx-compile finally succeeds. If you exit dbx (`quit'), then the
status information is lost, but dbx-compile will still attempt to
restart the same programme."
  (interactive)
  (if (and (boundp 'dbx-process) (equal (process-status dbx-process) 'run))
      (progn
	(if (and (boundp 'dbx-restart-file)
		 (file-readable-p dbx-restart-file))
	    (delete-file dbx-restart-file))
	(setq dbx-restart-file (make-temp-name "/tmp/dbx_"))
	(let ( (dbx-wait t)
	       (pt (point)))
	  (send-string dbx-process (concat "status > " dbx-restart-file "\n"))
	  (while dbx-wait
	    (accept-process-output dbx-process))
	  (set-process-sentinel dbx-process nil) ; don't delete restart file
	  (kill-process dbx-process)
	  (delete-region (point) pt))))
  (call-interactively 'compile)
  (set-process-sentinel compilation-process 'compilation-sentinel2))
;
; Replace the compilation sentinel by one that restarts dbx
;
(defun compilation-sentinel2 (proc msg)
  (compilation-sentinel proc msg)
  (if (or (equal msg "exit\n") (equal msg "finished\n"))
      (if (y-or-n-p "restart dbx? ")
	  (let ( (pgm (car (reverse (process-command dbx-process)))))
	    (set-buffer (process-buffer dbx-process))
	    (goto-char (point-max))
	    (insert "\nIf line numbers have changed, break points may be wrong\n")
	    (run-dbx pgm)))))