]> 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 184f9de518cc4597bdc7fe224a707bb8eabbb610..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,
-;;   2004, 2005, 2006, 2007 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>
 
 ;; 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
-;; 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
@@ -21,9 +21,7 @@
 ;; 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:
 
@@ -81,6 +79,7 @@
 ;;; Code:
 
 (require 'pp)
+(eval-when-compile (require 'cl))
 
 ;;; Misc comments:
 ;;
 
 
 (defcustom bookmark-use-annotations nil
-  "*If non-nil, saving a bookmark queries for an annotation in a buffer."
+  "If non-nil, saving a bookmark queries for an annotation in a buffer."
   :type 'boolean
   :group 'bookmark)
 
 
 (defcustom bookmark-save-flag t
-  "*Controls when Emacs saves bookmarks to a file.
+  "Controls when Emacs saves bookmarks to a file.
 --> nil means never save bookmarks, except when `bookmark-save' is
     explicitly called \(\\[bookmark-save]\).
 --> t means save bookmarks when Emacs is killed.
@@ -127,7 +126,7 @@ To specify the file in which to save them, modify the variable
 
 
 (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:
@@ -139,13 +138,13 @@ To specify the file in which to save them, modify the variable
       ;; In case user set `bookmark-file' in her .emacs:
       bookmark-file
     (convert-standard-filename "~/.emacs.bmk"))
-  "*File in which to save bookmarks by default."
+  "File in which to save bookmarks by default."
   :type 'file
   :group 'bookmark)
 
 
 (defcustom bookmark-version-control 'nospecial
-  "*Whether or not to make numbered backups of the bookmark file.
+  "Whether or not to make numbered backups of the bookmark file.
 It can have four values: t, nil, `never', and `nospecial'.
 The first three have the same meaning that they do for the
 variable `version-control', and the final value `nospecial' means just
@@ -156,13 +155,13 @@ use the value of `version-control'."
 
 
 (defcustom bookmark-completion-ignore-case t
-  "*Non-nil means bookmark functions ignore case in completion."
+  "Non-nil means bookmark functions ignore case in completion."
   :type 'boolean
   :group 'bookmark)
 
 
 (defcustom bookmark-sort-flag t
-  "*Non-nil means that bookmarks will be displayed sorted by bookmark name.
+  "Non-nil means that bookmarks will be displayed sorted by bookmark name.
 Otherwise they will be displayed in LIFO order (that is, most
 recently set ones come first, oldest ones come last)."
   :type 'boolean
@@ -170,20 +169,20 @@ recently set ones come first, oldest ones come last)."
 
 
 (defcustom bookmark-automatically-show-annotations t
-  "*Non-nil means show annotations when jumping to a bookmark."
+  "Non-nil means show annotations when jumping to a bookmark."
   :type 'boolean
   :group 'bookmark)
 
 
 (defcustom bookmark-bmenu-file-column 30
-  "*Column at which to display filenames in a buffer listing bookmarks.
+  "Column at which to display filenames in a buffer listing bookmarks.
 You can toggle whether files are shown with \\<bookmark-bmenu-mode-map>\\[bookmark-bmenu-toggle-filenames]."
   :type 'integer
   :group 'bookmark)
 
 
 (defcustom bookmark-bmenu-toggle-filenames t
-  "*Non-nil means show filenames when listing bookmarks.
+  "Non-nil means show filenames when listing bookmarks.
 This may result in truncated bookmark names.  To disable this, put the
 following in your `.emacs' file:
 
@@ -193,7 +192,7 @@ following in your `.emacs' file:
 
 
 (defcustom bookmark-menu-length 70
-  "*Maximum length of a bookmark name displayed on a popup menu."
+  "Maximum length of a bookmark name displayed on a popup menu."
   :type 'integer
   :group 'bookmark)
 
@@ -219,43 +218,35 @@ 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.
 
-;;;###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 nil
+(defvar bookmark-map
+  (let ((map (make-sparse-keymap)))
+    ;; Read the help on all of these functions for details...
+    (define-key map "x" 'bookmark-set)
+    (define-key map "m" 'bookmark-set) ;"m"ark
+    (define-key map "j" 'bookmark-jump)
+    (define-key map "g" 'bookmark-jump) ;"g"o
+    (define-key map "o" 'bookmark-jump-other-window)
+    (define-key map "i" 'bookmark-insert)
+    (define-key map "e" 'edit-bookmarks)
+    (define-key map "f" 'bookmark-insert-location) ;"f"ind
+    (define-key map "r" 'bookmark-rename)
+    (define-key map "d" 'bookmark-delete)
+    (define-key map "l" 'bookmark-load)
+    (define-key map "w" 'bookmark-write)
+    (define-key map "s" 'bookmark-save)
+    map)
   "Keymap containing bindings to bookmark functions.
 It is not bound to any key by default: to bind it
 so that you have a bookmark prefix, just use `global-set-key' and bind a
 key of your choice to `bookmark-map'.  All interactive bookmark
 functions have a binding in this keymap.")
 
-;;;###autoload (define-prefix-command 'bookmark-map)
-
-;; Read the help on all of these functions for details...
-;;;###autoload (define-key bookmark-map "x" 'bookmark-set)
-;;;###autoload (define-key bookmark-map "m" 'bookmark-set) ;"m"ark
-;;;###autoload (define-key bookmark-map "j" 'bookmark-jump)
-;;;###autoload (define-key bookmark-map "g" 'bookmark-jump) ;"g"o
-;;;###autoload (define-key bookmark-map "o" 'bookmark-jump-other-window)
-;;;###autoload (define-key bookmark-map "i" 'bookmark-insert)
-;;;###autoload (define-key bookmark-map "e" 'edit-bookmarks)
-;;;###autoload (define-key bookmark-map "f" 'bookmark-insert-location) ;"f"ind
-;;;###autoload (define-key bookmark-map "r" 'bookmark-rename)
-;;;###autoload (define-key bookmark-map "d" 'bookmark-delete)
-;;;###autoload (define-key bookmark-map "l" 'bookmark-load)
-;;;###autoload (define-key bookmark-map "w" 'bookmark-write)
-;;;###autoload (define-key bookmark-map "s" 'bookmark-save)
-
-
-;;; The annotation maps.
-(defvar bookmark-read-annotation-mode-map (copy-keymap text-mode-map)
-  "Keymap for composing an annotation for a bookmark.")
-
-(define-key bookmark-read-annotation-mode-map "\C-c\C-c"
-  'bookmark-send-annotation)
-
+;;;###autoload (fset 'bookmark-map bookmark-map)
 
 \f
 ;;; Core variables and data structures:
@@ -270,16 +261,16 @@ The format of the alist is
 
 where each BOOKMARK is of the form
 
-\(NAME
-  \(filename . FILE\)
-  \(front-context-string . FRONT-STR\)
-  \(rear-context-string  . REAR-STR\)
-  \(position . POS\)
-  \(info-node . POS\)
-  \(annotation . ANNOTATION\)\)
+  (NAME PARAM-ALIST) or (NAME . PARAM-ALIST)
+
+where the first form is the old deprecated one and the second is
+the new favored one.  PARAM-ALIST is typically of the form:
 
-So the cdr of each bookmark is an alist too.
-`info-node' is optional, by the way.")
+ ((filename . FILE)
+  (front-context-string . FRONT-STR)
+  (rear-context-string  . REAR-STR)
+  (position . POS)
+  (annotation . ANNOTATION)))")
 
 
 (defvar bookmarks-already-loaded nil)
@@ -307,7 +298,6 @@ through a file easier.")
 (defvar bookmark-yank-point 0)
 (defvar bookmark-current-buffer nil)
 
-(defvar Info-current-node)
 (defvar Info-suffix-list)
 \f
 ;; Helper functions.
@@ -316,6 +306,7 @@ through a file easier.")
 ;; know anything about the format of bookmark-alist entries.
 ;; Everyone else should go through them.
 
+
 (defun bookmark-name-from-full-record (full-record)
   "Return name of FULL-RECORD \(an alist element instead of a string\)."
   (car full-record))
@@ -324,23 +315,28 @@ through a file easier.")
 (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."
-  (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)
@@ -349,100 +345,69 @@ That is, all information but the name."
    (if (stringp bookmark) (bookmark-get-bookmark bookmark) bookmark)
    newname))
 
+(defun bookmark-prop-get (bookmark prop)
+  "Return the property PROP of BOOKMARK, or nil if none."
+  (cdr (assq prop (bookmark-get-bookmark-record bookmark))))
+
+(defun bookmark-prop-set (bookmark prop val)
+  "Set the property PROP of BOOKMARK to VAL."
+  (let ((cell (assq prop (bookmark-get-bookmark-record bookmark))))
+    (if cell
+        (setcdr cell val)
+      (nconc (bookmark-get-bookmark-record bookmark)
+             (list (cons prop val))))))
 
 (defun bookmark-get-annotation (bookmark)
   "Return the annotation of BOOKMARK, or nil if none."
-  (cdr (assq 'annotation (bookmark-get-bookmark-record bookmark))))
-
+  (bookmark-prop-get bookmark 'annotation))
 
 (defun bookmark-set-annotation (bookmark ann)
   "Set the annotation of BOOKMARK to ANN."
-  (let ((cell (assq 'annotation (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell ann)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons 'annotation ann))))))
+  (bookmark-prop-set bookmark 'annotation ann))
 
 
 (defun bookmark-get-filename (bookmark)
   "Return the full filename of BOOKMARK."
-  (cdr (assq 'filename (bookmark-get-bookmark-record bookmark))))
+  (bookmark-prop-get bookmark 'filename))
 
 
 (defun bookmark-set-filename (bookmark filename)
   "Set the full filename of BOOKMARK to FILENAME."
-  (let ((cell (assq 'filename (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell filename)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons '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)
   "Return the position \(i.e.: point\) of BOOKMARK."
-  (cdr (assq 'position (bookmark-get-bookmark-record bookmark))))
+  (bookmark-prop-get bookmark 'position))
 
 
 (defun bookmark-set-position (bookmark position)
   "Set the position \(i.e.: point\) of BOOKMARK to POSITION."
-  (let ((cell (assq 'position (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell position)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons 'position position))))))
+  (bookmark-prop-set bookmark 'position position))
 
 
 (defun bookmark-get-front-context-string (bookmark)
   "Return the front-context-string of BOOKMARK."
-  (cdr (assq 'front-context-string (bookmark-get-bookmark-record bookmark))))
+  (bookmark-prop-get bookmark 'front-context-string))
 
 
 (defun bookmark-set-front-context-string (bookmark string)
   "Set the front-context-string of BOOKMARK to STRING."
-  (let ((cell (assq 'front-context-string
-                    (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell string)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons 'front-context-string string))))))
+  (bookmark-prop-set bookmark 'front-context-string string))
 
 
 (defun bookmark-get-rear-context-string (bookmark)
   "Return the rear-context-string of BOOKMARK."
-  (cdr (assq 'rear-context-string (bookmark-get-bookmark-record bookmark))))
+  (bookmark-prop-get bookmark 'rear-context-string))
 
 
 (defun bookmark-set-rear-context-string (bookmark string)
   "Set the rear-context-string of BOOKMARK to STRING."
-  (let ((cell (assq 'rear-context-string
-                    (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell string)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons 'rear-context-string string))))))
-
-
-(defun bookmark-get-info-node (bookmark)
-  "Get the info node associated with BOOKMARK."
-  (cdr (assq 'info-node (bookmark-get-bookmark-record bookmark))))
+  (bookmark-prop-set bookmark 'rear-context-string string))
 
 
-(defun bookmark-set-info-node (bookmark node)
-  "Set the Info node of BOOKMARK to NODE."
-  (let ((cell (assq 'info-node
-                    (bookmark-get-bookmark-record bookmark))))
-    (if cell
-        (setcdr cell node)
-      (nconc (bookmark-get-bookmark-record bookmark)
-             (list (cons 'info-node node)))))
-
-  (message "%S" (assq 'info-node (bookmark-get-bookmark-record bookmark)))
-  (sit-for 4))
-
+(defun bookmark-get-handler (bookmark)
+  (bookmark-prop-get bookmark 'handler))
 
 (defvar bookmark-history nil
   "The history list for bookmark functions.")
@@ -480,102 +445,114 @@ menus, so `completing-read' never gets a chance to set `bookmark-history'."
     (interactive-p)
     (setq bookmark-history (cons ,string bookmark-history))))
 
-
-(defun bookmark-make (name &optional annotation overwrite info-node)
-  "Make a bookmark named NAME.
-Optional second arg ANNOTATION gives it an annotation.
-Optional third arg OVERWRITE means replace any existing bookmarks with
-this name.
-Optional fourth arg INFO-NODE means this bookmark is at info node
-INFO-NODE, so record this fact in the bookmark's entry."
+(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.
+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
+`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
+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."
+  (let ((record (funcall bookmark-make-record-function)))
+    ;; Set up default name.
+    (if (stringp (car record))
+        ;; The function already provided a default name.
+        record
+      (if (car record) (push nil record))
+      (setcar record (or bookmark-current-bookmark (bookmark-buffer-name)))
+      record)))
+
+(defun bookmark-store (name alist no-overwrite)
+  "Store the bookmark NAME with data ALIST.
+If NO-OVERWRITE is non-nil and another bookmark of the same name already
+exists in `bookmark-alist', record the new bookmark without throwing away the
+old one."
   (bookmark-maybe-load-default-file)
   (let ((stripped-name (copy-sequence name)))
     (or (featurep 'xemacs)
         ;; 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 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
-        (setcdr (bookmark-get-bookmark stripped-name)
-                (list (bookmark-make-cell annotation info-node)))
+        ;; 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...)
 
-      (setq bookmark-alist
-            (cons
-             (list stripped-name
-                   (bookmark-make-cell annotation info-node))
-             bookmark-alist)))
+      (push (cons stripped-name alist) bookmark-alist))
 
     ;; Added by db
     (setq bookmark-current-bookmark stripped-name)
     (setq bookmark-alist-modification-count
           (1+ bookmark-alist-modification-count))
     (if (bookmark-time-to-save-p)
-        (bookmark-save))))
+        (bookmark-save))
 
+    (setq bookmark-current-bookmark stripped-name)
+    (bookmark-bmenu-surreptitiously-rebuild-list)))
 
-(defun bookmark-make-cell (annotation &optional info-node)
-  "Return the record part of a new bookmark, given ANNOTATION.
+(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
-being set.  This might change someday.
-Optional second arg INFO-NODE means this bookmark is at info node
-INFO-NODE, so record this fact in the bookmark's entry."
-  (let ((the-record
-         `((filename . ,(bookmark-buffer-file-name))
-           (front-context-string
-            . ,(if (>= (- (point-max) (point)) bookmark-search-size)
-                   (buffer-substring-no-properties
-                    (point)
-                    (+ (point) bookmark-search-size))
-                   nil))
-           (rear-context-string
-            . ,(if (>= (- (point) (point-min)) bookmark-search-size)
-                   (buffer-substring-no-properties
-                    (point)
-                    (- (point) bookmark-search-size))
-                   nil))
-           (position . ,(point)))))
-
-    ;; Now fill in the optional parts:
-
-    ;; Take no chances with text properties
-    (set-text-properties 0 (length annotation) nil annotation)
-    (set-text-properties 0 (length info-node) nil info-node)
-
-    (if annotation
-        (nconc the-record (list (cons 'annotation annotation))))
-    (if info-node
-        (nconc the-record (list (cons 'info-node info-node))))
-
-    ;; Finally, return the completed record.
-    the-record))
-
+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
+             (point)
+             (+ (point) bookmark-search-size))
+          nil))
+    (rear-context-string
+     . ,(if (>= (- (point) (point-min)) bookmark-search-size)
+            (buffer-substring-no-properties
+             (point)
+             (- (point) bookmark-search-size))
+          nil))
+    (position . ,(point))))
 
 \f
 ;;; File format stuff
 
 ;; 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:
 ;;
-;;       ((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)
+;;                       ...
+;;                       ))
 ;;        ...)
 ;;
 ;;
@@ -723,6 +700,16 @@ This expects to be called from `point-min' in a bookmark file."
 \f
 ;;; Core code:
 
+(defvar bookmark-minibuffer-read-name-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map minibuffer-local-map)
+    (define-key map "\C-w" 'bookmark-yank-word)
+    ;; This C-u binding might not be very useful any more now that we
+    ;; provide access to the default via the standard M-n binding.
+    ;; Maybe we should just remove it?  --Stef-08
+    (define-key map "\C-u" 'bookmark-insert-current-bookmark)
+    map))
+
 ;;;###autoload
 (defun bookmark-set (&optional name parg)
   "Set a bookmark named NAME inside a file.
@@ -747,43 +734,29 @@ Use \\[bookmark-delete] to remove bookmarks \(you give it a name,
 and it removes only the first instance of a bookmark with that name from
 the list of bookmarks.\)"
   (interactive (list nil current-prefix-arg))
-  (or
-   (bookmark-buffer-file-name)
-   (error "Buffer not visiting a file or directory"))
-
-  (bookmark-maybe-load-default-file)
-
-  (setq bookmark-current-point (point))
-  (setq bookmark-yank-point (point))
-  (setq bookmark-current-buffer (current-buffer))
-
-  (let* ((default (or bookmark-current-bookmark
-                      (bookmark-buffer-name)))
-        (str
-         (or name
-              (read-from-minibuffer
-               (format "Set bookmark (%s): " default)
-               nil
-               (let ((now-map (copy-keymap minibuffer-local-map)))
-                 (define-key now-map "\C-w" 'bookmark-yank-word)
-                 (define-key now-map "\C-u" 'bookmark-insert-current-bookmark)
-                 now-map))))
-        (annotation nil))
-    (and (string-equal str "") (setq str default))
-    ;; Ask for an annotation buffer for this bookmark
-    (if bookmark-use-annotations
-       (bookmark-read-annotation parg str)
-      (bookmark-make str annotation parg (bookmark-info-current-node))
-      (setq bookmark-current-bookmark str)
-      (bookmark-bmenu-surreptitiously-rebuild-list)
-      (goto-char bookmark-current-point))))
-
-
-(defun bookmark-info-current-node ()
-  "If in Info-mode, return current node name (a string), else nil."
-  (if (eq major-mode 'Info-mode)
-      Info-current-node))
-
+  (let* ((record (bookmark-make-record))
+         (default (car record)))
+
+    (bookmark-maybe-load-default-file)
+
+    (setq bookmark-current-point (point))
+    (setq bookmark-yank-point (point))
+    (setq bookmark-current-buffer (current-buffer))
+
+    (let ((str
+           (or name
+               (read-from-minibuffer
+                (format "Set bookmark (%s): " default)
+                nil
+                bookmark-minibuffer-read-name-map
+                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)
+        (goto-char bookmark-current-point)))))
 
 (defun bookmark-kill-line (&optional newline-too)
   "Kill from point to end of line.
@@ -796,44 +769,10 @@ Does not affect the kill ring."
 
 
 ;; Defvars to avoid compilation warnings:
-(defvar bookmark-annotation-paragraph nil)
-(defvar bookmark-annotation-name nil)
-(defvar bookmark-annotation-buffer nil)
-(defvar bookmark-annotation-file nil)
-(defvar bookmark-annotation-point nil)
-
-
-(defun bookmark-send-annotation ()
-  "Use buffer contents as the annotation for a bookmark.
-Exclude lines that begin with `#'.
-Store the annotation text in the bookmark list with
-the bookmark (and file, and point) specified in buffer local variables."
-  (interactive)
-  (if (not (eq major-mode 'bookmark-read-annotation-mode))
-      (error "Not in bookmark-read-annotation-mode"))
-  (goto-char (point-min))
-  (while (< (point) (point-max))
-    (if (looking-at "^#")
-        (bookmark-kill-line t)
-      (forward-line 1)))
-  (let ((annotation (buffer-string))
-       (parg bookmark-annotation-paragraph)
-       (bookmark bookmark-annotation-name)
-       (pt bookmark-annotation-point)
-       (buf bookmark-annotation-buffer))
-    ;; for bookmark-make-cell to work, we need to be
-    ;; in the relevant buffer, at the relevant point.
-    ;; Actually, bookmark-make-cell should probably be re-written,
-    ;; to avoid this need.  Should I handle the error if a buffer is
-    ;; killed between "C-x r m" and a "C-c C-c" in the annotation buffer?
-    (save-excursion
-      (pop-to-buffer buf)
-      (goto-char pt)
-      (bookmark-make bookmark annotation parg (bookmark-info-current-node))
-      (setq bookmark-current-bookmark bookmark))
-    (bookmark-bmenu-surreptitiously-rebuild-list)
-    (goto-char bookmark-current-point))
-  (kill-buffer (current-buffer)))
+(defvar bookmark-annotation-name nil
+  "Variable holding the name of the bookmark.
+This is used in `bookmark-edit-annotation' to record the bookmark
+whose annotation is being edited.")
 
 
 (defun bookmark-default-annotation-text (bookmark)
@@ -845,59 +784,25 @@ the bookmark (and file, and point) specified in buffer local variables."
          "#  Date:    " (current-time-string) "\n"))
 
 
-(defvar bookmark-read-annotation-text-func 'bookmark-default-annotation-text
+(defvar bookmark-edit-annotation-text-func 'bookmark-default-annotation-text
   "Function to return default text to use for a bookmark annotation.
 It takes one argument, the name of the bookmark, as a string.")
-
-(defun bookmark-read-annotation-mode (buf point parg bookmark)
-  "Mode for composing annotations for a bookmark.
-Wants BUF, POINT, PARG, and BOOKMARK.
-When you have finished composing, type \\[bookmark-send-annotation] to send
-the annotation.
-
-\\{bookmark-read-annotation-mode-map}
-"
-  (interactive)
-  (kill-all-local-variables)
-  (make-local-variable 'bookmark-annotation-paragraph)
-  (make-local-variable 'bookmark-annotation-name)
-  (make-local-variable 'bookmark-annotation-buffer)
-  (make-local-variable 'bookmark-annotation-file)
-  (make-local-variable 'bookmark-annotation-point)
-  (setq bookmark-annotation-paragraph parg)
-  (setq bookmark-annotation-name bookmark)
-  (setq bookmark-annotation-buffer buf)
-  (setq bookmark-annotation-file (buffer-file-name buf))
-  (setq bookmark-annotation-point point)
-  (use-local-map bookmark-read-annotation-mode-map)
-  (setq major-mode 'bookmark-read-annotation-mode)
-  (insert (funcall bookmark-read-annotation-text-func bookmark))
-  (run-mode-hooks 'text-mode-hook))
-
-
-(defun bookmark-read-annotation (parg bookmark)
-  "Pop up a buffer for entering a bookmark annotation.
-Text surrounding the bookmark is PARG; the bookmark name is BOOKMARK."
-  (let ((buf (current-buffer))
-       (point (point)))
-    (pop-to-buffer (generate-new-buffer-name "*Bookmark Annotation Compose*"))
-    (bookmark-read-annotation-mode buf point parg bookmark)))
-
-
-(defvar bookmark-edit-annotation-mode-map (copy-keymap text-mode-map)
+(define-obsolete-variable-alias 'bookmark-read-annotation-text-func
+  'bookmark-edit-annotation-text-func "23.1")
+
+(defvar bookmark-edit-annotation-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map text-mode-map)
+    (define-key map "\C-c\C-c" 'bookmark-send-edited-annotation)
+    map)
   "Keymap for editing an annotation of a bookmark.")
 
 
-(define-key bookmark-edit-annotation-mode-map "\C-c\C-c"
-  'bookmark-send-edited-annotation)
-
-
 (defun bookmark-edit-annotation-mode (bookmark)
   "Mode for editing the annotation of bookmark BOOKMARK.
 When you have finished composing, type \\[bookmark-send-annotation].
 
-\\{bookmark-edit-annotation-mode-map}
-"
+\\{bookmark-edit-annotation-mode-map}"
   (interactive)
   (kill-all-local-variables)
   (make-local-variable 'bookmark-annotation-name)
@@ -905,7 +810,7 @@ When you have finished composing, type \\[bookmark-send-annotation].
   (use-local-map bookmark-edit-annotation-mode-map)
   (setq major-mode 'bookmark-edit-annotation-mode
         mode-name "Edit Bookmark Annotation")
-  (insert (funcall bookmark-read-annotation-text-func bookmark))
+  (insert (funcall bookmark-edit-annotation-text-func bookmark))
   (let ((annotation (bookmark-get-annotation bookmark)))
     (if (and annotation (not (string-equal annotation "")))
        (insert annotation)))
@@ -923,7 +828,8 @@ Lines beginning with `#' are ignored."
     (if (looking-at "^#")
         (bookmark-kill-line t)
       (forward-line 1)))
-  (let ((annotation (buffer-string))
+  ;; Take no chances with text properties.
+  (let ((annotation (buffer-substring-no-properties (point-min) (point-max)))
        (bookmark bookmark-annotation-name))
     (bookmark-set-annotation bookmark annotation)
     (bookmark-bmenu-surreptitiously-rebuild-list)
@@ -942,29 +848,15 @@ Lines beginning with `#' are ignored."
 Default to file name if it's nil."
   (interactive)
   (let ((str
-        (save-excursion
-          (set-buffer bookmark-current-buffer)
-          bookmark-current-bookmark)))
-    (if str (insert str) (bookmark-insert-buffer-name))))
-
-
-(defun bookmark-insert-buffer-name ()
-  "Insert the current file name into the bookmark name being set.
-The directory part of the file name is not used."
-  (interactive)
-  (let ((str
-         (save-excursion
-           (set-buffer bookmark-current-buffer)
-           (bookmark-buffer-name))))
+        (with-current-buffer bookmark-current-buffer
+          (or bookmark-current-bookmark
+               (bookmark-buffer-name)))))
     (insert str)))
 
 
 (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
-   ;; Are we in Info?
-   ((string-equal mode-name "Info") Info-current-node)
    ;; Or are we a file?
    (buffer-file-name (file-name-nondirectory buffer-file-name))
    ;; Or are we a directory?
@@ -987,34 +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.
-  (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)))
 
-
-(defvar Info-current-file)
-
 (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
-   ((eq major-mode 'Info-mode)
-    Info-current-file)
-   (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)))))
+  "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 ()
@@ -1050,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'.")
 
+(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).
@@ -1068,15 +969,7 @@ of the old one in the permanent bookmark record."
   (unless bookmark
     (error "No bookmark specified"))
   (bookmark-maybe-historicize-string bookmark)
-  (let ((cell (bookmark-jump-noselect bookmark)))
-    (and cell
-         (switch-to-buffer (car cell))
-         (goto-char (cdr cell))
-        (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
@@ -1090,14 +983,7 @@ See `bookmark-jump'."
          (list bkm) bkm)))
   (when bookmark
     (bookmark-maybe-historicize-string bookmark)
-    (let ((cell (bookmark-jump-noselect bookmark)))
-      (and cell
-           (switch-to-buffer-other-window (car cell))
-           (goto-char (cdr cell))
-           (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)
@@ -1120,61 +1006,88 @@ be retrieved from a VC backend, else return nil."
      ;; Last possibility: try VC
      (if (vc-backend file) file))))
 
+(defun bookmark-jump-noselect (bookmark)
+  "Return the location pointed to by the bookmark BOOKMARK.
+The return value has the form (BUFFER . POINT).
 
-(defun bookmark-jump-noselect (str)
-  ;; a leetle helper for bookmark-jump :-)
-  ;; returns (BUFFER . POINT)
-  (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))
-         (info-node              (bookmark-get-info-node str))
-         (orig-file              file)
-         )
-    (if (setq file (bookmark-file-or-variation-thereof file))
-        (save-excursion
-          (save-window-excursion
-            (if info-node
-                ;; Info nodes must be visited with care.
-                (progn
-                  (require 'info)
-                 (with-no-warnings
-                   (Info-find-node file info-node)))
-              ;; Else no Info.  Can do an ordinary find-file:
-              (set-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))))
-            ;; added by db
-            (setq bookmark-current-bookmark str)
-            (cons (current-buffer) (point))))
-
-      ;; Else unable to find the marked file, so ask if user wants to
-      ;; relocate the bookmark, else remind them to consider deletion.
-      (ding)
-      (if (y-or-n-p (concat (file-name-nondirectory orig-file)
-                            " nonexistent.  Relocate \""
-                            str
-                            "\"? "))
-          (progn
-            (bookmark-relocate str)
-            ;; gasp!  It's a recursive function call in Emacs Lisp!
-            (bookmark-jump-noselect str))
-        (message
-         "Bookmark not relocated; consider removing it \(%s\)." str)
-        nil))))
-
+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
+    (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)
@@ -1191,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)
+    (setq bookmark-alist-modification-count
+          (1+ bookmark-alist-modification-count))
+    (if (bookmark-time-to-save-p)
+        (bookmark-save))
     (bookmark-bmenu-surreptitiously-rebuild-list)))
 
 
@@ -1273,10 +1190,10 @@ this."
   (bookmark-maybe-historicize-string bookmark)
   (bookmark-maybe-load-default-file)
   (let ((orig-point (point))
-        (str-to-insert
-         (save-excursion
-           (set-buffer (car (bookmark-jump-noselect bookmark)))
-           (buffer-string))))
+       (str-to-insert
+        (save-current-buffer
+           (bookmark-handle-bookmark bookmark)
+          (buffer-string))))
     (insert str-to-insert)
     (push-mark)
     (goto-char orig-point)))
@@ -1296,11 +1213,11 @@ probably because we were called from there."
                                   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
-    (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
@@ -1377,29 +1294,26 @@ for a file, defaulting to the file defined by variable
 
 \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)
@@ -1462,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))
-  (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
-;;; 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)
@@ -1904,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))
-       (let* ((pair (bookmark-jump-noselect bmrk))
-               (buff (car pair))
-               (pos  (cdr pair)))
-          (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))))
 
 
@@ -1924,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)
-       (let* ((pair (bookmark-jump-noselect bookmark))
-               (buff (car pair))
-               (pos  (cdr pair)))
-         (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 ()
@@ -1942,23 +1846,13 @@ The current window remains selected."
         same-window-buffer-names
         same-window-regexps)
     (if (bookmark-bmenu-check-position)
-       (let* ((pair (bookmark-jump-noselect bookmark))
-               (buff (car pair))
-               (pos  (cdr pair)))
-         (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")
-  (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))))
@@ -2179,11 +2073,11 @@ strings returned are not."
 ;;;; end bookmark menu stuff ;;;;
 
 \f
-;;; Load Hook
+;; Load Hook
 (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.")
 
@@ -2199,10 +2093,16 @@ This also runs `bookmark-exit-hook'."
 
 (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)
 
 (provide 'bookmark)
 
-;;; arch-tag: 139f519a-dd0c-4b8d-8b5d-f9fcf53ca8f6
+;; arch-tag: 139f519a-dd0c-4b8d-8b5d-f9fcf53ca8f6
 ;;; bookmark.el ends here