]> code.delx.au - gnu-emacs/blobdiff - lisp/bookmark.el
term/ns-win.el (composition-function-table) (script-representative-chars): Don't...
[gnu-emacs] / lisp / bookmark.el
index d8d90d6d6f7d9b792b9f9b8e696a848c4ac78c16..debfac03140022fdd148501e31e1fb27bfd845b7 100644 (file)
@@ -1,7 +1,7 @@
 ;;; bookmark.el --- set bookmarks, maybe annotate them, jump to them later
 
 ;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 2001, 2002, 2003,
 ;;; bookmark.el --- set bookmarks, maybe annotate them, jump to them later
 
 ;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
 ;; Author: Karl Fogel <kfogel@red-bean.com>
 ;; Maintainer: Karl Fogel <kfogel@red-bean.com>
 
 ;; Author: Karl Fogel <kfogel@red-bean.com>
 ;; Maintainer: Karl Fogel <kfogel@red-bean.com>
 
 ;; This file is part of GNU Emacs.
 
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 
 ;;; Commentary:
 
@@ -81,6 +79,7 @@
 ;;; Code:
 
 (require 'pp)
 ;;; Code:
 
 (require 'pp)
+(eval-when-compile (require 'cl))
 
 ;;; Misc comments:
 ;;
 
 ;;; Misc comments:
 ;;
@@ -127,7 +126,7 @@ To specify the file in which to save them, modify the variable
 
 
 (defconst bookmark-old-default-file "~/.emacs-bkmrks"
 
 
 (defconst bookmark-old-default-file "~/.emacs-bkmrks"
-  "*The `.emacs.bmk' file used to be called this name.")
+  "The `.emacs.bmk' file used to be called this name.")
 
 
 ;; defvarred to avoid a compilation warning:
 
 
 ;; defvarred to avoid a compilation warning:
@@ -219,9 +218,9 @@ following in your `.emacs' file:
 ;; Set up these bindings dumping time *only*;
 ;; if the user alters them, don't override the user when loading bookmark.el.
 
 ;; Set up these bindings dumping time *only*;
 ;; if the user alters them, don't override the user when loading bookmark.el.
 
-;;;###autoload (define-key ctl-x-map "rb" 'bookmark-jump)
-;;;###autoload (define-key ctl-x-map "rm" 'bookmark-set)
-;;;###autoload (define-key ctl-x-map "rl" 'bookmark-bmenu-list)
+;;;###autoload (define-key ctl-x-r-map "b" 'bookmark-jump)
+;;;###autoload (define-key ctl-x-r-map "m" 'bookmark-set)
+;;;###autoload (define-key ctl-x-r-map "l" 'bookmark-bmenu-list)
 
 ;;;###autoload
 (defvar bookmark-map
 
 ;;;###autoload
 (defvar bookmark-map
@@ -260,16 +259,18 @@ The format of the alist is
 
        \(BOOKMARK1 BOOKMARK2 ...\)
 
 
        \(BOOKMARK1 BOOKMARK2 ...\)
 
-where each BOOKMARK is typically of the form
+where each BOOKMARK is of the form
 
 
-\(NAME
- (\(filename . FILE\)
-  \(front-context-string . FRONT-STR\)
-  \(rear-context-string  . REAR-STR\)
-  \(position . POS\)
-  \(annotation . ANNOTATION\)\))
+  (NAME PARAM-ALIST) or (NAME . PARAM-ALIST)
 
 
-So the cdr of each bookmark is an alist too.")
+where the first form is the old deprecated one and the second is
+the new favored one.  PARAM-ALIST is typically of the form:
+
+ ((filename . FILE)
+  (front-context-string . FRONT-STR)
+  (rear-context-string  . REAR-STR)
+  (position . POS)
+  (annotation . ANNOTATION)))")
 
 
 (defvar bookmarks-already-loaded nil)
 
 
 (defvar bookmarks-already-loaded nil)
@@ -297,7 +298,6 @@ through a file easier.")
 (defvar bookmark-yank-point 0)
 (defvar bookmark-current-buffer nil)
 
 (defvar bookmark-yank-point 0)
 (defvar bookmark-current-buffer nil)
 
-(defvar Info-current-node)
 (defvar Info-suffix-list)
 \f
 ;; Helper functions.
 (defvar Info-suffix-list)
 \f
 ;; Helper functions.
@@ -315,23 +315,28 @@ through a file easier.")
 (defun bookmark-all-names ()
   "Return a list of all current bookmark names."
   (bookmark-maybe-load-default-file)
 (defun bookmark-all-names ()
   "Return a list of all current bookmark names."
   (bookmark-maybe-load-default-file)
-  (mapcar
-   (lambda (full-record)
-     (bookmark-name-from-full-record full-record))
-   bookmark-alist))
+  (mapcar 'bookmark-name-from-full-record bookmark-alist))
 
 
 
 
-(defun bookmark-get-bookmark (bookmark)
-  "Return the full entry for BOOKMARK in `bookmark-alist'.
-If BOOKMARK is not a string, return nil."
-  (when (stringp bookmark)
-    (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)))
+(defun bookmark-get-bookmark (bookmark &optional noerror)
+  "Return the bookmark record corresponding to BOOKMARK.
+If BOOKMARK is already a bookmark record, just return it,
+Otherwise look for the corresponding bookmark in `bookmark-alist'."
+  (cond
+   ((consp bookmark) bookmark)
+   ((stringp bookmark)
+    (or (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)
+        (unless noerror (error "Invalid bookmark %s" bookmark))))))
 
 
 (defun bookmark-get-bookmark-record (bookmark)
   "Return the guts of the entry for BOOKMARK in `bookmark-alist'.
 That is, all information but the name."
 
 
 (defun bookmark-get-bookmark-record (bookmark)
   "Return the guts of the entry for BOOKMARK in `bookmark-alist'.
 That is, all information but the name."
-  (car (cdr (bookmark-get-bookmark bookmark))))
+  (let ((alist (cdr (bookmark-get-bookmark bookmark))))
+    ;; The bookmark objects can either look like (NAME ALIST) or
+    ;; (NAME . ALIST), so we have to distinguish the two here.
+    (if (and (null (cdr alist)) (consp (caar alist)))
+        (car alist) alist)))
 
 
 (defun bookmark-set-name (bookmark newname)
 
 
 (defun bookmark-set-name (bookmark newname)
@@ -368,11 +373,7 @@ That is, all information but the name."
 
 (defun bookmark-set-filename (bookmark filename)
   "Set the full filename of BOOKMARK to FILENAME."
 
 (defun bookmark-set-filename (bookmark filename)
   "Set the full filename of BOOKMARK to FILENAME."
-  (bookmark-prop-set bookmark 'filename filename)
-  (setq bookmark-alist-modification-count
-        (1+ bookmark-alist-modification-count))
-  (if (bookmark-time-to-save-p)
-      (bookmark-save)))
+  (bookmark-prop-set bookmark 'filename filename))
 
 
 (defun bookmark-get-position (bookmark)
 
 
 (defun bookmark-get-position (bookmark)
@@ -444,26 +445,26 @@ menus, so `completing-read' never gets a chance to set `bookmark-history'."
     (interactive-p)
     (setq bookmark-history (cons ,string bookmark-history))))
 
     (interactive-p)
     (setq bookmark-history (cons ,string bookmark-history))))
 
-(defvar bookmark-make-record-function 'bookmark-make-record-for-text-file
+(defvar bookmark-make-record-function 'bookmark-make-record-default
   "A function that should be called to create a bookmark record.
 Modes may set this variable buffer-locally to enable bookmarking of
 locations that should be treated specially, such as Info nodes,
 news posts, images, pdf documents, etc.
 
 The function will be called with no arguments.
   "A function that should be called to create a bookmark record.
 Modes may set this variable buffer-locally to enable bookmarking of
 locations that should be treated specially, such as Info nodes,
 news posts, images, pdf documents, etc.
 
 The function will be called with no arguments.
+It should signal a user error if it is unable to construct a record for
+the current location.
 
 The returned record should be a cons cell of the form (NAME . ALIST)
 where ALIST is as described in `bookmark-alist' and may typically contain
 a special cons (handler . SOME-FUNCTION) which sets the handler function
 that should be used to open this bookmark instead of
 
 The returned record should be a cons cell of the form (NAME . ALIST)
 where ALIST is as described in `bookmark-alist' and may typically contain
 a special cons (handler . SOME-FUNCTION) which sets the handler function
 that should be used to open this bookmark instead of
-`bookmark-default-handler'.  The handler should return an alist like the
-one that function returns, and (of course) should likewise
-not select the buffer.
-It should signal a user error if it is unable to construct a record for the current
-location.
+`bookmark-default-handler'.  The handler should follow the same calling
+convention as the one used by `bookmark-default-handler'.
 
 NAME is a suggested name for the constructed bookmark.  It can be nil
 
 NAME is a suggested name for the constructed bookmark.  It can be nil
-in which case a default heuristic will be used.")
+in which case a default heuristic will be used.  The function can also
+equivalently just return ALIST without NAME.")
 
 (defun bookmark-make-record ()
   "Return a new bookmark record (NAME . ALIST) for the current location."
 
 (defun bookmark-make-record ()
   "Return a new bookmark record (NAME . ALIST) for the current location."
@@ -487,16 +488,18 @@ old one."
         ;; XEmacs's `set-text-properties' doesn't work on
         ;; free-standing strings, apparently.
         (set-text-properties 0 (length stripped-name) nil stripped-name))
         ;; XEmacs's `set-text-properties' doesn't work on
         ;; free-standing strings, apparently.
         (set-text-properties 0 (length stripped-name) nil stripped-name))
-    (if (and (bookmark-get-bookmark stripped-name) (not no-overwrite))
+    (if (and (not no-overwrite)
+             (bookmark-get-bookmark stripped-name 'noerror))
         ;; already existing bookmark under that name and
         ;; no prefix arg means just overwrite old bookmark
         ;; already existing bookmark under that name and
         ;; no prefix arg means just overwrite old bookmark
-        (setcdr (bookmark-get-bookmark stripped-name) (list alist))
+        ;; Use the new (NAME . ALIST) format.
+        (setcdr (bookmark-get-bookmark stripped-name) alist)
 
       ;; otherwise just cons it onto the front (either the bookmark
       ;; doesn't exist already, or there is no prefix arg.  In either
       ;; case, we want the new bookmark consed onto the alist...)
 
 
       ;; otherwise just cons it onto the front (either the bookmark
       ;; doesn't exist already, or there is no prefix arg.  In either
       ;; case, we want the new bookmark consed onto the alist...)
 
-      (push (list stripped-name alist) bookmark-alist))
+      (push (cons stripped-name alist) bookmark-alist))
 
     ;; Added by db
     (setq bookmark-current-bookmark stripped-name)
 
     ;; Added by db
     (setq bookmark-current-bookmark stripped-name)
@@ -508,11 +511,13 @@ old one."
     (setq bookmark-current-bookmark stripped-name)
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
     (setq bookmark-current-bookmark stripped-name)
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
-(defun bookmark-make-record-for-text-file ()
+(defun bookmark-make-record-default (&optional point-only)
   "Return the record describing the location of a new bookmark.
 Must be at the correct position in the buffer in which the bookmark is
   "Return the record describing the location of a new bookmark.
 Must be at the correct position in the buffer in which the bookmark is
-being set (this might change someday)."
-  `((filename . ,(bookmark-buffer-file-name))
+being set.
+If POINT-ONLY is non-nil, then only return the subset of the
+record that pertains to the location within the buffer."
+  `(,@(unless point-only `((filename . ,(bookmark-buffer-file-name))))
     (front-context-string
      . ,(if (>= (- (point-max) (point)) bookmark-search-size)
             (buffer-substring-no-properties
     (front-context-string
      . ,(if (>= (- (point-max) (point)) bookmark-search-size)
             (buffer-substring-no-properties
@@ -532,22 +537,22 @@ being set (this might change someday)."
 
 ;; The OLD format of the bookmark-alist was:
 ;;
 
 ;; The OLD format of the bookmark-alist was:
 ;;
-;;       ((bookmark-name (filename
-;;                        string-in-front
-;;                        string-behind
-;;                        point))
+;;       ((BOOKMARK-NAME . (FILENAME
+;;                          STRING-IN-FRONT
+;;                          STRING-BEHIND
+;;                          POINT))
 ;;        ...)
 ;;
 ;; The NEW format of the bookmark-alist is:
 ;;
 ;;        ...)
 ;;
 ;; The NEW format of the bookmark-alist is:
 ;;
-;;       ((bookmark-name ((filename . FILENAME)
-;;                        (front-context-string . string-in-front)
-;;                        (rear-context-string  . string-behind)
-;;                        (position . POINT)
-;;                        (annotation . annotation)
-;;                        (whatever   . VALUE)
-;;                        ...
-;;                        ))
+;;       ((BOOKMARK-NAME (filename   . FILENAME)
+;;                       (front-context-string . STRING-IN-FRONT)
+;;                       (rear-context-string  . STRING-BEHIND)
+;;                       (position   . POINT)
+;;                       (annotation . ANNOTATION)
+;;                       (whatever   . VALUE)
+;;                       ...
+;;                       ))
 ;;        ...)
 ;;
 ;;
 ;;        ...)
 ;;
 ;;
@@ -747,7 +752,7 @@ the list of bookmarks.\)"
                 nil nil default))))
       (and (string-equal str "") (setq str default))
       (bookmark-store str (cdr record) parg)
                 nil nil default))))
       (and (string-equal str "") (setq str default))
       (bookmark-store str (cdr record) parg)
-      
+
       ;; Ask for an annotation buffer for this bookmark
       (if bookmark-use-annotations
           (bookmark-edit-annotation str)
       ;; Ask for an annotation buffer for this bookmark
       (if bookmark-use-annotations
           (bookmark-edit-annotation str)
@@ -850,8 +855,7 @@ Default to file name if it's nil."
 
 
 (defun bookmark-buffer-name ()
 
 
 (defun bookmark-buffer-name ()
-  "Return the name of the current buffer's file, non-directory.
-In Info, return the current node."
+  "Return the name of the current buffer's file, non-directory."
   (cond
    ;; Or are we a file?
    (buffer-file-name (file-name-nondirectory buffer-file-name))
   (cond
    ;; Or are we a file?
    (buffer-file-name (file-name-nondirectory buffer-file-name))
@@ -875,33 +879,28 @@ In Info, return the current node."
   (interactive)
   ;; get the next word from the buffer and append it to the name of
   ;; the bookmark currently being set.
   (interactive)
   ;; get the next word from the buffer and append it to the name of
   ;; the bookmark currently being set.
-  (let ((string (save-excursion
-                    (set-buffer bookmark-current-buffer)
-                    (goto-char bookmark-yank-point)
-                    (buffer-substring-no-properties
-                     (point)
-                     (progn
-                       (forward-word 1)
-                       (setq bookmark-yank-point (point)))))))
+  (let ((string (with-current-buffer bookmark-current-buffer
+                  (goto-char bookmark-yank-point)
+                  (buffer-substring-no-properties
+                   (point)
+                   (progn
+                     (forward-word 1)
+                     (setq bookmark-yank-point (point)))))))
     (insert string)))
 
     (insert string)))
 
-
-(defvar Info-current-file)
-
 (defun bookmark-buffer-file-name ()
 (defun bookmark-buffer-file-name ()
-  "Return the current buffer's file in a way useful for bookmarks.
-For example, if this is a Info buffer, return the Info file's name."
-  (cond
-   (buffer-file-name
-    ;; Abbreviate the path, both so it's shorter and so it's more
-    ;; portable.  E.g., the user's home dir might be a different
-    ;; path on different machines, but "~/" will still reach it.
-    (abbreviate-file-name buffer-file-name))
-   ((and (boundp 'dired-directory) dired-directory)
-    (if (stringp dired-directory)
-        dired-directory
-      (car dired-directory)))
-   (t (error "Buffer not visiting a file or directory"))))
+  "Return the current buffer's file in a way useful for bookmarks."
+  ;; Abbreviate the path, both so it's shorter and so it's more
+  ;; portable.  E.g., the user's home dir might be a different
+  ;; path on different machines, but "~/" will still reach it.
+  (abbreviate-file-name
+   (cond
+    (buffer-file-name buffer-file-name)
+    ((and (boundp 'dired-directory) dired-directory)
+     (if (stringp dired-directory)
+         dired-directory
+       (car dired-directory)))
+    (t (error "Buffer not visiting a file or directory")))))
 
 
 (defun bookmark-maybe-load-default-file ()
 
 
 (defun bookmark-maybe-load-default-file ()
@@ -937,6 +936,21 @@ For example, if this is a Info buffer, return the Info file's name."
   "Hook run after `bookmark-jump' jumps to a bookmark.
 Useful for example to unhide text in `outline-mode'.")
 
   "Hook run after `bookmark-jump' jumps to a bookmark.
 Useful for example to unhide text in `outline-mode'.")
 
+(defun bookmark--jump-via (bookmark display-function)
+  (bookmark-handle-bookmark bookmark)
+  (save-current-buffer
+    (funcall display-function (current-buffer)))
+  (let ((win (get-buffer-window (current-buffer) 0)))
+    (if win (set-window-point win (point))))
+  ;; FIXME: we used to only run bookmark-after-jump-hook in
+  ;; `bookmark-jump' itself, but in none of the other commands.
+  (run-hooks 'bookmark-after-jump-hook)
+  (if bookmark-automatically-show-annotations
+      ;; if there is an annotation for this bookmark,
+      ;; show it in a buffer.
+      (bookmark-show-annotation bookmark)))
+
+
 ;;;###autoload
 (defun bookmark-jump (bookmark)
   "Jump to bookmark BOOKMARK (a point in some file).
 ;;;###autoload
 (defun bookmark-jump (bookmark)
   "Jump to bookmark BOOKMARK (a point in some file).
@@ -955,15 +969,7 @@ of the old one in the permanent bookmark record."
   (unless bookmark
     (error "No bookmark specified"))
   (bookmark-maybe-historicize-string bookmark)
   (unless bookmark
     (error "No bookmark specified"))
   (bookmark-maybe-historicize-string bookmark)
-  (let ((alist (bookmark-jump-noselect bookmark)))
-    (and alist
-         (switch-to-buffer (cadr (assq 'buffer alist)))
-         (goto-char (cadr (assq 'position alist)))
-        (progn (run-hooks 'bookmark-after-jump-hook) t)
-        (if bookmark-automatically-show-annotations
-             ;; if there is an annotation for this bookmark,
-             ;; show it in a buffer.
-             (bookmark-show-annotation bookmark)))))
+  (bookmark--jump-via bookmark 'switch-to-buffer))
 
 
 ;;;###autoload
 
 
 ;;;###autoload
@@ -977,14 +983,7 @@ See `bookmark-jump'."
          (list bkm) bkm)))
   (when bookmark
     (bookmark-maybe-historicize-string bookmark)
          (list bkm) bkm)))
   (when bookmark
     (bookmark-maybe-historicize-string bookmark)
-    (let ((alist (bookmark-jump-noselect bookmark)))
-      (and alist
-           (switch-to-buffer-other-window (cadr (assq 'buffer alist)))
-           (goto-char (cadr (assq 'position alist)))
-           (if bookmark-automatically-show-annotations
-               ;; if there is an annotation for this bookmark,
-               ;; show it in a buffer.
-               (bookmark-show-annotation bookmark))))))
+    (bookmark--jump-via bookmark 'switch-to-buffer-other-window)))
 
 
 (defun bookmark-file-or-variation-thereof (file)
 
 
 (defun bookmark-file-or-variation-thereof (file)
@@ -1008,66 +1007,87 @@ be retrieved from a VC backend, else return nil."
      (if (vc-backend file) file))))
 
 (defun bookmark-jump-noselect (bookmark)
      (if (vc-backend file) file))))
 
 (defun bookmark-jump-noselect (bookmark)
-  "Call BOOKMARK's handler or `bookmark-default-handler' if it has none."
-  (let ((found (funcall (or (bookmark-get-handler bookmark)
-                          'bookmark-default-handler)
-                      bookmark)))
-    (unless found
-      ;; Else unable to find the marked file, so ask if user wants to
-      ;; relocate the bookmark, else remind them to consider deletion.
-      (let ((file (bookmark-get-filename bookmark)))
-        (when file         ;Don't know how to relocate if there's no `file'.
-          (setq file (expand-file-name file))
-          (ding)
-          (if (y-or-n-p (concat (file-name-nondirectory file)
-                                " nonexistent.  Relocate \""
-                                bookmark
-                                "\"? "))
-              (progn
-                (bookmark-relocate bookmark)
-                ;; Try again.
-                (setq found (funcall (or (bookmark-get-handler bookmark)
-                                         'bookmark-default-handler)
-                                     bookmark)))
-            (message
-             "Bookmark not relocated; consider removing it \(%s\)." bookmark)))))
-    (when found
-      ;; Added by db.
-      (setq bookmark-current-bookmark bookmark)
-      found)))
-
-(defun bookmark-default-handler (str)
-  ;; Helper for bookmark-jump.  STR is a bookmark name, of the sort
-  ;; accepted by `bookmark-get-bookmark'.
-  ;;
-  ;; Return an alist '((buffer BUFFER) (position POSITION) ...)
-  ;; indicating the bookmarked point within the specied buffer.  Any
-  ;; elements not documented here should be ignored.
-  (bookmark-maybe-load-default-file)
-  (let* ((file (expand-file-name (bookmark-get-filename str)))
-         (forward-str            (bookmark-get-front-context-string str))
-         (behind-str             (bookmark-get-rear-context-string str))
-         (place                  (bookmark-get-position str)))
+  "Return the location pointed to by the bookmark BOOKMARK.
+The return value has the form (BUFFER . POINT).
+
+Note: this function is deprecated and is present for Emacs 22
+compatibility only."
+  (save-excursion
+    (bookmark-handle-bookmark bookmark)
+    (cons (current-buffer) (point))))
+
+(make-obsolete 'bookmark-jump-noselect 'bookmark-handle-bookmark "23.1")
+
+(defun bookmark-handle-bookmark (bookmark)
+  "Call BOOKMARK's handler or `bookmark-default-handler' if it has none.
+Changes current buffer and point and returns nil, or signals a `file-error'.
+BOOKMARK can be a bookmark record used internally by some other
+elisp package, or the name of a bookmark to be found in `bookmark-alist'."
+  (condition-case err
+      (funcall (or (bookmark-get-handler bookmark)
+                   'bookmark-default-handler)
+               (bookmark-get-bookmark bookmark))
+    (file-error
+     ;; We were unable to find the marked file, so ask if user wants to
+     ;; relocate the bookmark, else remind them to consider deletion.
+     (when (stringp bookmark)
+       ;; `bookmark' can either be a bookmark name (found in
+       ;; `bookmark-alist') or a bookmark object.  If it's an object, we
+       ;; assume it's a bookmark used internally by some other package.
+       (let ((file (bookmark-get-filename bookmark)))
+         (when file        ;Don't know how to relocate if there's no `file'.
+           (setq file (expand-file-name file))
+           (ding)
+           (if (y-or-n-p (concat (file-name-nondirectory file)
+                                 " nonexistent.  Relocate \""
+                                 bookmark
+                                 "\"? "))
+               (progn
+                 (bookmark-relocate bookmark)
+                 ;; Try again.
+                 (funcall (or (bookmark-get-handler bookmark)
+                              'bookmark-default-handler)
+                          (bookmark-get-bookmark bookmark)))
+             (message
+              "Bookmark not relocated; consider removing it \(%s\)." bookmark)
+             (signal (car err) (cdr err))))))))
+  ;; Added by db.
+  (when (stringp bookmark)
+    (setq bookmark-current-bookmark bookmark))
+  nil)
+
+(defun bookmark-default-handler (bmk)
+  "Default handler to jump to a particular bookmark location.
+BMK is a bookmark record.
+Changes current buffer and point and returns nil, or signals a `file-error'."
+  (let* ((file                   (bookmark-get-filename bmk))
+         (buf                    (bookmark-prop-get bmk 'buffer))
+         (forward-str            (bookmark-get-front-context-string bmk))
+         (behind-str             (bookmark-get-rear-context-string bmk))
+         (place                  (bookmark-get-position bmk)))
     ;; FIXME: bookmark-file-or-variation-thereof was needed for Info files,
     ;; but now that Info bookmarks are handled elsewhere it seems that we
     ;; should be able to get rid of it.  --Stef
     ;; FIXME: bookmark-file-or-variation-thereof was needed for Info files,
     ;; but now that Info bookmarks are handled elsewhere it seems that we
     ;; should be able to get rid of it.  --Stef
-    (if (setq file (bookmark-file-or-variation-thereof file))
-        (with-current-buffer (find-file-noselect file)
-          (goto-char place)
-
-          ;; Go searching forward first.  Then, if forward-str exists and
-          ;; was found in the file, we can search backward for behind-str.
-          ;; Rationale is that if text was inserted between the two in the
-          ;; file, it's better to be put before it so you can read it,
-          ;; rather than after and remain perhaps unaware of the changes.
-          (if forward-str
-              (if (search-forward forward-str (point-max) t)
-                  (goto-char (match-beginning 0))))
-          (if behind-str
-              (if (search-backward behind-str (point-min) t)
-                  (goto-char (match-end 0))))
-          `((buffer ,(current-buffer)) (position ,(point)))))))
-
+    (if (not (if buf (buffer-live-p buf)
+               (setq file (bookmark-file-or-variation-thereof file))))
+        (signal 'file-error
+                `("Jumping to bookmark" "No such file or directory"
+                  (bookmark-get-filename bmk)))
+      (set-buffer (or buf (find-file-noselect file)))
+      (if place (goto-char place))
+
+      ;; Go searching forward first.  Then, if forward-str exists and
+      ;; was found in the file, we can search backward for behind-str.
+      ;; Rationale is that if text was inserted between the two in the
+      ;; file, it's better to be put before it so you can read it,
+      ;; rather than after and remain perhaps unaware of the changes.
+      (if forward-str
+          (if (search-forward forward-str (point-max) t)
+              (goto-char (match-beginning 0))))
+      (if behind-str
+          (if (search-backward behind-str (point-min) t)
+              (goto-char (match-end 0)))))
+    nil))
 
 ;;;###autoload
 (defun bookmark-relocate (bookmark)
 
 ;;;###autoload
 (defun bookmark-relocate (bookmark)
@@ -1084,6 +1104,10 @@ after a bookmark was set in it."
                    (format "Relocate %s to: " bookmark)
                    (file-name-directory bmrk-filename)))))
     (bookmark-set-filename bookmark newloc)
                    (format "Relocate %s to: " bookmark)
                    (file-name-directory bmrk-filename)))))
     (bookmark-set-filename bookmark newloc)
+    (setq bookmark-alist-modification-count
+          (1+ bookmark-alist-modification-count))
+    (if (bookmark-time-to-save-p)
+        (bookmark-save))
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
 
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
 
@@ -1167,8 +1191,8 @@ this."
   (bookmark-maybe-load-default-file)
   (let ((orig-point (point))
        (str-to-insert
   (bookmark-maybe-load-default-file)
   (let ((orig-point (point))
        (str-to-insert
-        (save-excursion
-          (set-buffer (cadr (assq 'buffer (bookmark-jump-noselect bookmark))))
+        (save-current-buffer
+           (bookmark-handle-bookmark bookmark)
           (buffer-string))))
     (insert str-to-insert)
     (push-mark)
           (buffer-string))))
     (insert str-to-insert)
     (push-mark)
@@ -1189,11 +1213,11 @@ probably because we were called from there."
                                   bookmark-current-bookmark)))
   (bookmark-maybe-historicize-string bookmark)
   (bookmark-maybe-load-default-file)
                                   bookmark-current-bookmark)))
   (bookmark-maybe-historicize-string bookmark)
   (bookmark-maybe-load-default-file)
-  (let ((will-go (bookmark-get-bookmark bookmark)))
+  (let ((will-go (bookmark-get-bookmark bookmark 'noerror)))
     (setq bookmark-alist (delq will-go bookmark-alist))
     ;; Added by db, nil bookmark-current-bookmark if the last
     ;; occurrence has been deleted
     (setq bookmark-alist (delq will-go bookmark-alist))
     ;; Added by db, nil bookmark-current-bookmark if the last
     ;; occurrence has been deleted
-    (or (bookmark-get-bookmark bookmark-current-bookmark)
+    (or (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
         (setq bookmark-current-bookmark nil)))
   ;; Don't rebuild the list
   (if batch
         (setq bookmark-current-bookmark nil)))
   ;; Don't rebuild the list
   (if batch
@@ -1270,29 +1294,26 @@ for a file, defaulting to the file defined by variable
 
 \f
 (defun bookmark-write-file (file)
 
 \f
 (defun bookmark-write-file (file)
-  (save-excursion
-    (save-window-excursion
-      (bookmark-maybe-message "Saving bookmarks to file %s..." file)
-      (set-buffer (get-buffer-create " *Bookmarks*"))
-      (goto-char (point-min))
-      (delete-region (point-min) (point-max))
-      (let ((print-length nil)
-           (print-level nil))
-       (bookmark-insert-file-format-version-stamp)
-       (pp bookmark-alist (current-buffer))
-       (let ((version-control
-              (cond
-               ((null bookmark-version-control) nil)
-               ((eq 'never bookmark-version-control) 'never)
-               ((eq 'nospecial bookmark-version-control) version-control)
-               (t
-                t))))
-          (condition-case nil
-              (write-region (point-min) (point-max) file)
-            (file-error (message "Can't write %s" file)))
-         (kill-buffer (current-buffer))
-          (bookmark-maybe-message
-           "Saving bookmarks to file %s...done" file))))))
+  (bookmark-maybe-message "Saving bookmarks to file %s..." file)
+  (with-current-buffer (get-buffer-create " *Bookmarks*")
+    (goto-char (point-min))
+    (delete-region (point-min) (point-max))
+    (let ((print-length nil)
+          (print-level nil))
+      (bookmark-insert-file-format-version-stamp)
+      (pp bookmark-alist (current-buffer))
+      (let ((version-control
+             (cond
+              ((null bookmark-version-control) nil)
+              ((eq 'never bookmark-version-control) 'never)
+              ((eq 'nospecial bookmark-version-control) version-control)
+              (t t))))
+        (condition-case nil
+            (write-region (point-min) (point-max) file)
+          (file-error (message "Can't write %s" file)))
+        (kill-buffer (current-buffer))
+        (bookmark-maybe-message
+         "Saving bookmarks to file %s...done" file)))))
 
 
 (defun bookmark-import-new-list (new-list)
 
 
 (defun bookmark-import-new-list (new-list)
@@ -1355,41 +1376,39 @@ method buffers use to resolve name collisions."
           ;;I guess it's better than none at all.
           "~/" bookmark-default-file 'confirm)))
   (setq file (expand-file-name file))
           ;;I guess it's better than none at all.
           "~/" bookmark-default-file 'confirm)))
   (setq file (expand-file-name file))
-  (if (file-readable-p file)
-      (save-excursion
-        (save-window-excursion
-          (if (null no-msg)
-              (bookmark-maybe-message "Loading bookmarks from %s..." file))
-          (set-buffer (let ((enable-local-variables nil))
-                        (find-file-noselect file)))
-          (goto-char (point-min))
-          (bookmark-maybe-upgrade-file-format)
-          (let ((blist (bookmark-alist-from-buffer)))
-            (if (listp blist)
-                (progn
-                  (if overwrite
-                      (progn
-                        (setq bookmark-alist blist)
-                        (setq bookmark-alist-modification-count 0))
-                    ;; else
-                    (bookmark-import-new-list blist)
-                    (setq bookmark-alist-modification-count
-                          (1+ bookmark-alist-modification-count)))
-                  (if (string-equal
-                       (expand-file-name bookmark-default-file)
-                       file)
-                      (setq bookmarks-already-loaded t))
-                  (bookmark-bmenu-surreptitiously-rebuild-list))
-              (error "Invalid bookmark list in %s" file)))
-          (kill-buffer (current-buffer)))
-       (if (null no-msg)
-            (bookmark-maybe-message "Loading bookmarks from %s...done" file)))
-    (error "Cannot read bookmark file %s" file)))
+  (if (not (file-readable-p file))
+      (error "Cannot read bookmark file %s" file)
+    (if (null no-msg)
+        (bookmark-maybe-message "Loading bookmarks from %s..." file))
+    (with-current-buffer (let ((enable-local-variables nil))
+                           (find-file-noselect file))
+      (goto-char (point-min))
+      (bookmark-maybe-upgrade-file-format)
+      (let ((blist (bookmark-alist-from-buffer)))
+        (if (listp blist)
+            (progn
+              (if overwrite
+                  (progn
+                    (setq bookmark-alist blist)
+                    (setq bookmark-alist-modification-count 0))
+                ;; else
+                (bookmark-import-new-list blist)
+                (setq bookmark-alist-modification-count
+                      (1+ bookmark-alist-modification-count)))
+              (if (string-equal
+                   (expand-file-name bookmark-default-file)
+                   file)
+                  (setq bookmarks-already-loaded t))
+              (bookmark-bmenu-surreptitiously-rebuild-list))
+          (error "Invalid bookmark list in %s" file)))
+      (kill-buffer (current-buffer)))
+    (if (null no-msg)
+        (bookmark-maybe-message "Loading bookmarks from %s...done" file))))
 
 
 \f
 
 
 \f
-;;; Code supporting the dired-like bookmark menu.  Prefix is
-;;; "bookmark-bmenu" for "buffer-menu":
+;;; Code supporting the dired-like bookmark menu.
+;; Prefix is "bookmark-bmenu" for "buffer-menu":
 
 
 (defvar bookmark-bmenu-bookmark-column nil)
 
 
 (defvar bookmark-bmenu-bookmark-column nil)
@@ -1797,11 +1816,8 @@ With a prefix arg, prompts for a file to save them in."
             (pop-up-windows t))
         (delete-other-windows)
         (switch-to-buffer (other-buffer))
             (pop-up-windows t))
         (delete-other-windows)
         (switch-to-buffer (other-buffer))
-       (let* ((alist (bookmark-jump-noselect bmrk))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-          (pop-to-buffer buff)
-          (goto-char pos))
+        (let ((bookmark-automatically-show-annotations nil)) ;FIXME: needed?
+          (bookmark--jump-via bmrk 'pop-to-buffer))
         (bury-buffer menu))))
 
 
         (bury-buffer menu))))
 
 
@@ -1817,13 +1833,8 @@ With a prefix arg, prompts for a file to save them in."
   (interactive)
   (let ((bookmark (bookmark-bmenu-bookmark)))
     (if (bookmark-bmenu-check-position)
   (interactive)
   (let ((bookmark (bookmark-bmenu-bookmark)))
     (if (bookmark-bmenu-check-position)
-       (let* ((alist (bookmark-jump-noselect bookmark))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-         (switch-to-buffer-other-window buff)
-          (goto-char pos)
-          (set-window-point (get-buffer-window buff) pos)
-         (bookmark-show-annotation bookmark)))))
+        (let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
+          (bookmark--jump-via bookmark 'switch-to-buffer-other-window)))))
 
 
 (defun bookmark-bmenu-switch-other-window ()
 
 
 (defun bookmark-bmenu-switch-other-window ()
@@ -1835,23 +1846,13 @@ The current window remains selected."
         same-window-buffer-names
         same-window-regexps)
     (if (bookmark-bmenu-check-position)
         same-window-buffer-names
         same-window-regexps)
     (if (bookmark-bmenu-check-position)
-       (let* ((alist (bookmark-jump-noselect bookmark))
-               (buff (cadr (assq 'buffer alist)))
-               (pos  (cadr (assq 'position alist))))
-         (display-buffer buff)
-          (let ((o-buffer (current-buffer)))
-            ;; save-excursion won't do
-            (set-buffer buff)
-            (goto-char pos)
-            (set-window-point (get-buffer-window buff) pos)
-            (set-buffer o-buffer))
-         (bookmark-show-annotation bookmark)))))
+        (let ((bookmark-automatically-show-annotations t)) ;FIXME: needed?
+          (bookmark--jump-via bookmark 'display-buffer)))))
 
 (defun bookmark-bmenu-other-window-with-mouse (event)
   "Select bookmark at the mouse pointer in other window, leaving bookmark menu visible."
   (interactive "e")
 
 (defun bookmark-bmenu-other-window-with-mouse (event)
   "Select bookmark at the mouse pointer in other window, leaving bookmark menu visible."
   (interactive "e")
-  (save-excursion
-    (set-buffer (window-buffer (posn-window (event-end event))))
+  (with-current-buffer (window-buffer (posn-window (event-end event)))
     (save-excursion
       (goto-char (posn-point (event-end event)))
       (bookmark-bmenu-other-window))))
     (save-excursion
       (goto-char (posn-point (event-end event)))
       (bookmark-bmenu-other-window))))
@@ -2072,11 +2073,11 @@ strings returned are not."
 ;;;; end bookmark menu stuff ;;;;
 
 \f
 ;;;; end bookmark menu stuff ;;;;
 
 \f
-;;; Load Hook
+;; Load Hook
 (defvar bookmark-load-hook nil
   "Hook run at the end of loading bookmark.")
 
 (defvar bookmark-load-hook nil
   "Hook run at the end of loading bookmark.")
 
-;;; Exit Hook, called from kill-emacs-hook
+;; Exit Hook, called from kill-emacs-hook
 (defvar bookmark-exit-hook nil
   "Hook run when Emacs exits.")
 
 (defvar bookmark-exit-hook nil
   "Hook run when Emacs exits.")
 
@@ -2092,6 +2093,12 @@ This also runs `bookmark-exit-hook'."
 
 (add-hook 'kill-emacs-hook 'bookmark-exit-hook-internal)
 
 
 (add-hook 'kill-emacs-hook 'bookmark-exit-hook-internal)
 
+(defun bookmark-unload-function ()
+  "Unload the Bookmark library."
+  (when bookmark-save-flag (bookmark-save))
+  ;; continue standard unloading
+  nil)
+
 
 (run-hooks 'bookmark-load-hook)
 
 
 (run-hooks 'bookmark-load-hook)