;;;; breakpoints.el - find marked breakpoints in buffers
;;; Ed L. Cashin releases this version of breakpoints.el into
;;; the public domain without any warranties.
;;; 
;;; I noticed I was wasting a lot of time setting breakpoints
;;; at specific lines in files I had open in emacs.  These
;;; simple lisp routines find those breakpoints, which it 
;;; expects to be two lines below an "elc-break:" mark, and
;;; returns a string of gdb commands.
;;;
;;; do "M-x cmds-breakpoints RET" to insert the gdb commands
;;; into the buffer you're visiting.
;;;
;;; You will have to set bkpt-dbg-files to the right locations
;;; for your files.  Also, unless you really want to use "elc-break:" 
;;; to mark breakpoints in your kernel source, you'll need to set
;;; bkpt-marker-string too.
;;;
;;; Here's one way to load this stuff in your .emacs:
;;; 
;;; (let ((f "~/kernel/linux-2.4.18-kgdb/scripts/breakpoints.el"))
;;;   (if (file-readable-p f)
;;;       (let ((b (substring f 0 -3)))
;;;         (message ".emacs: loading linux debugging support library")
;;;         (load-library b))))
;;; 

(defvar bkpt-dbg-files
  nil
  ; '("/home/ecashin/kernel/linux-2.4.18-kgdb/mm/memory.c"
  ;   "/home/ecashin/kernel/linux-2.4.18-kgdb/kernel/fork.c"
  ;   "/home/ecashin/kernel/linux-2.4.18-kgdb/kernel/dclone.c")
  "the files I wuv to debug")

(defvar bkpt-dbg-excluded-files
  '("/home/ecashin/kernel/linux-2.4.18-kgdb/scripts/breakpoints.el")
  "the files I don't want to debug")

(defvar bkpt-dbg-file-prefixes
  '("/home/ecashin/kernel/linux-2.4.18-kgdb")
  "all files with names starting with one of these are to be examined")

(defvar bkpt-marker-string "elc-break:"
  "the string that appears two lines above the breakpoint") 

;;; comment this out if not debugging:
;; (setq bkpt-dbg-buffer 
;;       (get-buffer-create "bkpt-dbg"))
(defvar bkpt-dbg-buffer nil
  "the buffer where debugging info will go (no debugging if nil)")

(defun dbgprt (s)
  (if bkpt-dbg-buffer
      (with-current-buffer bkpt-dbg-buffer
	(insert s))))

(require 'cl)				; oh, to do labels without it!
(defun bkpt-prefix-match (fname)
  "tell whether FNAME has a prefix in bkpt-dbg-file-prefixes"
  (let ((flen (length fname))
	(count 0))
    (labels
	((rec (prefixes)
	   (incf count)
	   (dbgprt (format "rec %4d: %d prefixes\n" count (length prefixes)))
	   (if prefixes
	       (let* ((p (car prefixes))
		      (plen (length p)))
		 (or (and (>= flen plen)
			  (equal (substring fname 0 plen) p))
		     (rec (cdr prefixes)))))))
      (rec bkpt-dbg-file-prefixes))))

(defun bkpt-bkp-name (base)
  "string from base-revt-date"
  (let ((date-time (format-time-string "%Y%m%d-%H%M%S")))
    (format "%s-bkpt-%s" base date-time)))

(defun get-breakpoints-buf (buf auto-revt)
  "return list of all breakpoints in BUF as \"filename:lineno\" strings"
  (with-current-buffer buf
    (save-excursion
      (let ((pos)
	    (fname (car (last (split-string buffer-file-name "/+"))))
	    (bpts nil))
	(if (not (verify-visited-file-modtime buf))
	    (if auto-revt
		(let ((bkp-fname (bkpt-bkp-name fname)))
		  (message
		   (format "backing up obsolete buffer %s to %s before revert"
			   fname bkp-fname))
		  (write-file bkp-fname)
		  (set-visited-file-name fname t) ; write-file changed it.
		  (revert-buffer nil t))
		(error "buffer for %s is obsolete" fname)))
	(goto-char (point-min))
	(while (setq pos (search-forward bkpt-marker-string nil t))
	  (dbgprt (format "get-breakpoints-buf: in %s found %d" fname pos))
	  (forward-line 3)
	  (let ((entry (format "break %s:%d"
			       fname
			       (count-lines (point-min) (point)))))
	    (setq bpts (cons entry bpts))))
	bpts))))

(defun get-breakpoints (auto-revt)
  "find all breakpoints in the bkpt-dbg-files"
  (let ((acc)
	(count 0))
    (mapc (lambda (buf)
	    (let ((fname (with-current-buffer buf
			   buffer-file-name)))
	      (setq count (1+ count))
	      (if (and fname bkpt-dbg-buffer)
		  (dbgprt (format "%3d: %s\n" count fname)))
	      (if (and (not (member fname bkpt-dbg-excluded-files))
		       (or (bkpt-prefix-match fname)
			   (member fname bkpt-dbg-files)))
		  (progn
		    (dbgprt (format "%3d: %s MEMBER\n" count fname))
		    (setq acc
			  (append (get-breakpoints-buf buf auto-revt) acc))))))
	  (buffer-list))
    acc))

(defun cmds-breakpoints (user-prefix)
  "list gdb commands for breakpoints found in open files from bkpt-dbg-files"
  (interactive "p")
  (insert (let* ((auto-revt (> user-prefix 1))
		 (bpts (get-breakpoints auto-revt)))
	    (with-temp-buffer
	      (mapc (lambda (s) 
		      (insert (concat s "\n")))
		    bpts)
	      (let ((s (buffer-string)))
		(if (> (length s) 0)
		    (substring s 0 -1)
		    ""))))))
