(diff-end-of-hunk): Revert 2008-01-08 change.
[bpt/emacs.git] / lisp / desktop.el
index f56e220..4837aee 100644 (file)
@@ -1,10 +1,9 @@
 ;;; desktop.el --- save partial status of Emacs when killed
 
 ;; Copyright (C) 1993, 1994, 1995, 1997, 2000, 2001, 2002, 2003,
-;;   2004, 2005, 2006 Free Software Foundation, Inc.
+;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Morten Welinder <terra@diku.dk>
-;; Maintainter: Lars Hansen <larsh@soem.dk>
 ;; Keywords: convenience
 ;; Favourite-brand-of-beer: None, I hate beer.
 
@@ -12,7 +11,7 @@
 
 ;; 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 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -46,9 +45,9 @@
 ;; "Saving Emacs Sessions" in the GNU Emacs Manual.
 
 ;; When the desktop module is loaded, the function `desktop-kill' is
-;; added to the `kill-emacs-hook'. This function is responsible for
+;; added to the `kill-emacs-hook'.  This function is responsible for
 ;; saving the desktop when Emacs is killed.  Furthermore an anonymous
-;; function is added to the `after-init-hook'. This function is
+;; function is added to the `after-init-hook'.  This function is
 ;; responsible for loading the desktop when Emacs is started.
 
 ;; Special handling.
 ;; Variables `desktop-buffer-mode-handlers' and `desktop-minor-mode-handlers'
 ;; are supplied to handle special major and minor modes respectively.
 ;; `desktop-buffer-mode-handlers' is an alist of major mode specific functions
-;; to restore a desktop buffer. Elements must have the form
+;; to restore a desktop buffer.  Elements must have the form
 ;;
 ;;    (MAJOR-MODE . RESTORE-BUFFER-FUNCTION).
 ;;
 ;; Functions listed are called by `desktop-create-buffer' when `desktop-read'
-;; evaluates the desktop file. Buffers with a major mode not specified here,
+;; evaluates the desktop file.  Buffers with a major mode not specified here,
 ;; are restored by the default handler `desktop-restore-file-buffer'.
 ;; `desktop-minor-mode-handlers' is an alist of functions to restore
 ;; non-standard minor modes.  Elements must have the form
@@ -86,7 +85,7 @@
 ;;                 '(bar-mode . bar-desktop-restore))
 
 ;; in the module itself, and make shure that the mode function is
-;; autoloaded. See the docstrings of `desktop-buffer-mode-handlers' and
+;; autoloaded.  See the docstrings of `desktop-buffer-mode-handlers' and
 ;; `desktop-minor-mode-handlers' for more info.
 
 ;; Minor modes.
 ;; The variables `desktop-minor-mode-table' and `desktop-minor-mode-handlers'
 ;; are used to handle non-conventional minor modes.  `desktop-save' uses
 ;; `desktop-minor-mode-table' to map minor mode variables to minor mode
-;; functions before writing `desktop-minor-modes'. If a minor mode has a
+;; functions before writing `desktop-minor-modes'.  If a minor mode has a
 ;; variable name that is different form its function name, an entry
 
 ;;    (NAME RESTORE-FUNCTION)
 
 ;;; Code:
 
+(defvar uniquify-managed)
+
 (defvar desktop-file-version "206"
   "Version number of desktop file format.
 Written into the desktop file and used at desktop read to provide
@@ -153,8 +154,9 @@ backward compatibility.")
 (define-minor-mode desktop-save-mode
   "Toggle desktop saving mode.
 With numeric ARG, turn desktop saving on if ARG is positive, off
-otherwise.  See variable `desktop-save' for a description of when the
-desktop is saved."
+otherwise.  If desktop saving is turned on, the state of Emacs is
+saved from one session to another.  See variable `desktop-save'
+and function `desktop-read' for details."
   :global t
   :group 'desktop)
 
@@ -162,6 +164,10 @@ desktop is saved."
 (define-obsolete-variable-alias 'desktop-enable
                                 'desktop-save-mode "22.1")
 
+(defun desktop-save-mode-off ()
+  "Disable `desktop-save-mode'.  Provided for use in hooks."
+  (desktop-save-mode 0))
+
 (defcustom desktop-save 'ask-if-new
   "*Specifies whether the desktop should be saved when it is killed.
 A desktop is killed when the user changes desktop or quits Emacs.
@@ -175,7 +181,8 @@ Possible values are:
 The desktop is never saved when `desktop-save-mode' is nil.
 The variables `desktop-dirname' and `desktop-base-file-name'
 determine where the desktop is saved."
-  :type '(choice
+  :type
+  '(choice
     (const :tag "Always save" t)
     (const :tag "Always ask" ask)
     (const :tag "Ask if desktop file is new, else do save" ask-if-new)
@@ -185,6 +192,22 @@ determine where the desktop is saved."
   :group 'desktop
   :version "22.1")
 
+(defcustom desktop-load-locked-desktop 'ask
+  "Specifies whether the desktop should be loaded if locked.
+Possible values are:
+   t    -- load anyway.
+   nil  -- don't load.
+   ask  -- ask the user.
+If the value is nil, or `ask' and the user chooses not to load the desktop,
+the normal hook `desktop-not-loaded-hook' is run."
+  :type
+  '(choice
+    (const :tag "Load anyway" t)
+    (const :tag "Don't load" nil)
+    (const :tag "Ask the user" ask))
+  :group 'desktop
+  :version "22.2")
+
 (defcustom desktop-base-file-name
   (convert-standard-filename ".emacs.desktop")
   "Name of file for Emacs desktop, excluding the directory part."
@@ -193,6 +216,13 @@ determine where the desktop is saved."
 (define-obsolete-variable-alias 'desktop-basefilename
                                 'desktop-base-file-name "22.1")
 
+(defcustom desktop-base-lock-name
+  (convert-standard-filename ".emacs.desktop.lock")
+  "Name of lock file for Emacs desktop, excluding the directory part."
+  :type 'file
+  :group 'desktop
+  :version "22.2")
+
 (defcustom desktop-path '("." "~")
   "List of directories to search for the desktop file.
 The base name of the file is specified in `desktop-base-file-name'."
@@ -201,7 +231,7 @@ The base name of the file is specified in `desktop-base-file-name'."
   :version "22.1")
 
 (defcustom desktop-missing-file-warning nil
-  "*If non-nil then `desktop-read' asks if a non-existent file should be recreated.
+  "If non-nil, offer to recreate the buffer of a deleted file.
 Also pause for a moment to display message about errors signaled in
 `desktop-buffer-mode-handlers'.
 
@@ -212,21 +242,34 @@ If nil, just print error messages in the message buffer."
 
 (defcustom desktop-no-desktop-file-hook nil
   "Normal hook run when `desktop-read' can't find a desktop file.
+Run in the directory in which the desktop file was sought.
 May be used to show a dired buffer."
   :type 'hook
   :group 'desktop
   :version "22.1")
 
+(defcustom desktop-not-loaded-hook nil
+  "Normal hook run when the user declines to re-use a desktop file.
+Run in the directory in which the desktop file was found.
+May be used to deal with accidental multiple Emacs jobs."
+  :type 'hook
+  :group 'desktop
+  :options '(desktop-save-mode-off save-buffers-kill-emacs)
+  :version "22.2")
+
 (defcustom desktop-after-read-hook nil
   "Normal hook run after a successful `desktop-read'.
 May be used to show a buffer list."
   :type 'hook
   :group 'desktop
+  :options '(list-buffers)
   :version "22.1")
 
 (defcustom desktop-save-hook nil
   "Normal hook run before the desktop is saved in a desktop file.
-This is useful for truncating history lists, for example."
+Run with the desktop buffer current with only the header present.
+May be used to add to the desktop code or to truncate history lists,
+for example."
   :type 'hook
   :group 'desktop)
 
@@ -282,6 +325,7 @@ these won't be deleted."
     size-indication-mode
     buffer-file-coding-system
     indent-tabs-mode
+    tab-width
     indicate-buffer-boundaries
     indicate-empty-lines
     show-trailing-whitespace)
@@ -412,12 +456,13 @@ Furthermore the major mode function must be autoloaded.")
 (defcustom desktop-minor-mode-table
   '((auto-fill-function auto-fill-mode)
     (vc-mode nil)
-    (vc-dired-mode nil))
+    (vc-dired-mode nil)
+    (erc-track-minor-mode nil))
   "Table mapping minor mode variables to minor mode functions.
 Each entry has the form (NAME RESTORE-FUNCTION).
 NAME is the name of the buffer-local variable indicating that the minor
 mode is active.  RESTORE-FUNCTION is the function to activate the minor mode.
-called.  RESTORE-FUNCTION nil means don't try to restore the minor mode.
+RESTORE-FUNCTION nil means don't try to restore the minor mode.
 Only minor modes for which the name of the buffer-local variable
 and the name of the minor mode function are different have to be added to
 this table.  See also `desktop-minor-mode-handlers'."
@@ -475,6 +520,16 @@ See also `desktop-minor-mode-table'.")
 (defvar desktop-dirname nil
   "The directory in which the desktop file should be saved.")
 
+(defun desktop-full-file-name (&optional dirname)
+  "Return the full name of the desktop file in DIRNAME.
+DIRNAME omitted or nil means use `desktop-dirname'."
+  (expand-file-name desktop-base-file-name (or dirname desktop-dirname)))
+
+(defun desktop-full-lock-name (&optional dirname)
+  "Return the full name of the desktop lock file in DIRNAME.
+DIRNAME omitted or nil means use `desktop-dirname'."
+  (expand-file-name desktop-base-lock-name (or dirname desktop-dirname)))
+
 (defconst desktop-header
 ";; --------------------------------------------------------------------------
 ;; Desktop File for Emacs
@@ -484,14 +539,48 @@ See also `desktop-minor-mode-table'.")
 (defvar desktop-delay-hook nil
   "Hooks run after all buffers are loaded; intended for internal use.")
 
+;; ----------------------------------------------------------------------------
+;; Desktop file conflict detection
+(defvar desktop-file-modtime nil
+  "When the desktop file was last modified to the knowledge of this Emacs.
+Used to detect desktop file conflicts.")
+
+(defun desktop-owner (&optional dirname)
+  "Return the PID of the Emacs process that owns the desktop file in DIRNAME.
+Return nil if no desktop file found or no Emacs process is using it.
+DIRNAME omitted or nil means use `desktop-dirname'."
+  (let (owner)
+    (and (file-exists-p (desktop-full-lock-name dirname))
+        (condition-case nil
+            (with-temp-buffer
+              (insert-file-contents-literally (desktop-full-lock-name dirname))
+              (goto-char (point-min))
+              (setq owner (read (current-buffer)))
+              (integerp owner))
+          (error nil))
+        owner)))
+
+(defun desktop-claim-lock (&optional dirname)
+  "Record this Emacs process as the owner of the desktop file in DIRNAME.
+DIRNAME omitted or nil means use `desktop-dirname'."
+  (write-region (number-to-string (emacs-pid)) nil
+               (desktop-full-lock-name dirname)))
+
+(defun desktop-release-lock (&optional dirname)
+  "Remove the lock file for the desktop in DIRNAME.
+DIRNAME omitted or nil means use `desktop-dirname'."
+  (let ((file (desktop-full-lock-name dirname)))
+    (when (file-exists-p file) (delete-file file))))
+
 ;; ----------------------------------------------------------------------------
 (defun desktop-truncate (list n)
   "Truncate LIST to at most N elements destructively."
   (let ((here (nthcdr (1- n) list)))
-    (if (consp here)
-       (setcdr here nil))))
+    (when (consp here)
+      (setcdr here nil))))
 
 ;; ----------------------------------------------------------------------------
+;;;###autoload
 (defun desktop-clear ()
   "Empty the Desktop.
 This kills all buffers except for internal ones and those with names matched by
@@ -501,7 +590,7 @@ Furthermore, it clears the variables listed in `desktop-globals-to-clear'."
   (desktop-lazy-abort)
   (dolist (var desktop-globals-to-clear)
     (if (symbolp var)
-      (eval `(setq-default ,var nil))
+       (eval `(setq-default ,var nil))
       (eval `(setq-default ,(car var) ,(cdr var)))))
   (let ((buffers (buffer-list))
         (preserve-regexp (concat "^\\("
@@ -528,29 +617,26 @@ Furthermore, it clears the variables listed in `desktop-globals-to-clear'."
   "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
 If the desktop should be saved and `desktop-dirname'
 is nil, ask the user where to save the desktop."
-  (when
-    (and
-      desktop-save-mode
-      (let ((exists (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))))
-        (or
-          (eq desktop-save t)
-          (and exists (memq desktop-save '(ask-if-new if-exists)))
-          (and
-            (or
-              (memq desktop-save '(ask ask-if-new))
-              (and exists (eq desktop-save 'ask-if-exists)))
-            (y-or-n-p "Save desktop? ")))))
+  (when (and desktop-save-mode
+             (let ((exists (file-exists-p (desktop-full-file-name))))
+               (or (eq desktop-save t)
+                   (and exists (memq desktop-save '(ask-if-new if-exists)))
+                   (and
+                    (or (memq desktop-save '(ask ask-if-new))
+                        (and exists (eq desktop-save 'ask-if-exists)))
+                    (y-or-n-p "Save desktop? ")))))
     (unless desktop-dirname
       (setq desktop-dirname
-        (file-name-as-directory
-          (expand-file-name
-            (call-interactively
-              (lambda (dir) (interactive "DDirectory for desktop file: ") dir))))))
+            (file-name-as-directory
+             (expand-file-name
+             (read-directory-name "Directory for desktop file: " nil nil t)))))
     (condition-case err
-      (desktop-save desktop-dirname)
+       (desktop-save desktop-dirname t)
       (file-error
-        (unless (yes-or-no-p "Error while saving the desktop.  Ignore? ")
-          (signal (car err) (cdr err)))))))
+       (unless (yes-or-no-p "Error while saving the desktop.  Ignore? ")
+        (signal (car err) (cdr err))))))
+  ;; If we own it, we don't anymore.
+  (when (eq (emacs-pid) (desktop-owner)) (desktop-release-lock)))
 
 ;; ----------------------------------------------------------------------------
 (defun desktop-list* (&rest args)
@@ -564,6 +650,50 @@ is nil, ask the user where to save the desktop."
        (setq args (cdr args)))
       value)))
 
+;; ----------------------------------------------------------------------------
+(declare-function uniquify-item-base "uniquify" (cl-x) t) ; defstruct
+
+(defun desktop-buffer-info (buffer)
+  (set-buffer buffer)
+  (list
+   ;; basic information
+   (desktop-file-name (buffer-file-name) desktop-dirname)
+   (if (bound-and-true-p uniquify-managed)
+       (uniquify-item-base (car uniquify-managed))
+     (buffer-name))
+   major-mode
+   ;; minor modes
+   (let (ret)
+     (mapc
+      #'(lambda (minor-mode)
+         (and (boundp minor-mode)
+              (symbol-value minor-mode)
+              (let* ((special (assq minor-mode desktop-minor-mode-table))
+                     (value (cond (special (cadr special))
+                                  ((functionp minor-mode) minor-mode))))
+                (when value (add-to-list 'ret value)))))
+      (mapcar #'car minor-mode-alist))
+     ret)
+   ;; point and mark, and read-only status
+   (point)
+   (list (mark t) mark-active)
+   buffer-read-only
+   ;; auxiliary information
+   (when (functionp desktop-save-buffer)
+     (funcall desktop-save-buffer desktop-dirname))
+   ;; local variables
+   (let ((locals desktop-locals-to-save)
+        (loclist (buffer-local-variables))
+        (ll))
+     (while locals
+       (let ((here (assq (car locals) loclist)))
+        (if here
+            (setq ll (cons here ll))
+          (when (member (car locals) loclist)
+            (setq ll (cons (car locals) ll)))))
+       (setq locals (cdr locals)))
+     ll)))
+
 ;; ----------------------------------------------------------------------------
 (defun desktop-internal-v2s (value)
   "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
@@ -571,78 +701,77 @@ TXT is a string that when read and evaluated yields value.
 QUOTE may be `may' (value may be quoted),
 `must' (values must be quoted), or nil (value may not be quoted)."
   (cond
-   ((or (numberp value) (null value) (eq t value) (keywordp value))
-    (cons 'may (prin1-to-string value)))
-   ((stringp value)
-    (let ((copy (copy-sequence value)))
-      (set-text-properties 0 (length copy) nil copy)
-      ;; Get rid of text properties because we cannot read them
-      (cons 'may (prin1-to-string copy))))
-   ((symbolp value)
-    (cons 'must (prin1-to-string value)))
-   ((vectorp value)
-    (let* ((special nil)
-          (pass1 (mapcar
-                  (lambda (el)
-                    (let ((res (desktop-internal-v2s el)))
-                      (if (null (car res))
-                          (setq special t))
-                      res))
-                  value)))
-      (if special
-         (cons nil (concat "(vector "
-                           (mapconcat (lambda (el)
-                                        (if (eq (car el) 'must)
-                                            (concat "'" (cdr el))
-                                          (cdr el)))
-                                      pass1
-                                      " ")
-                           ")"))
-       (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
-   ((consp value)
-    (let ((p value)
-         newlist
-         use-list*
-         anynil)
-      (while (consp p)
-       (let ((q.txt (desktop-internal-v2s (car p))))
-         (or anynil (setq anynil (null (car q.txt))))
-         (setq newlist (cons q.txt newlist)))
-       (setq p (cdr p)))
-      (if p
-         (let ((last (desktop-internal-v2s p))
-               (el (car newlist)))
-           (or anynil (setq anynil (null (car last))))
-           (or anynil
-               (setq newlist (cons '(must . ".") newlist)))
-           (setq use-list* t)
-           (setq newlist (cons last newlist))))
-      (setq newlist (nreverse newlist))
-      (if anynil
-         (cons nil
-               (concat (if use-list* "(desktop-list* "  "(list ")
-                       (mapconcat (lambda (el)
-                                    (if (eq (car el) 'must)
-                                        (concat "'" (cdr el))
-                                      (cdr el)))
-                                  newlist
-                                  " ")
-                       ")"))
-       (cons 'must
-             (concat "(" (mapconcat 'cdr newlist " ") ")")))))
-   ((subrp value)
-    (cons nil (concat "(symbol-function '"
-                     (substring (prin1-to-string value) 7 -1)
-                     ")")))
-   ((markerp value)
-    (let ((pos (prin1-to-string (marker-position value)))
-         (buf (prin1-to-string (buffer-name (marker-buffer value)))))
-      (cons nil (concat "(let ((mk (make-marker)))"
-                       " (add-hook 'desktop-delay-hook"
-                       " (list 'lambda '() (list 'set-marker mk "
-                       pos " (get-buffer " buf ")))) mk)"))))
-   (t                                  ; save as text
-    (cons 'may "\"Unprintable entity\""))))
+    ((or (numberp value) (null value) (eq t value) (keywordp value))
+     (cons 'may (prin1-to-string value)))
+    ((stringp value)
+     (let ((copy (copy-sequence value)))
+       (set-text-properties 0 (length copy) nil copy)
+       ;; Get rid of text properties because we cannot read them
+       (cons 'may (prin1-to-string copy))))
+    ((symbolp value)
+     (cons 'must (prin1-to-string value)))
+    ((vectorp value)
+     (let* ((special nil)
+           (pass1 (mapcar
+                   (lambda (el)
+                     (let ((res (desktop-internal-v2s el)))
+                       (if (null (car res))
+                           (setq special t))
+                       res))
+                   value)))
+       (if special
+          (cons nil (concat "(vector "
+                            (mapconcat (lambda (el)
+                                         (if (eq (car el) 'must)
+                                             (concat "'" (cdr el))
+                                           (cdr el)))
+                                       pass1
+                                       " ")
+                            ")"))
+        (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
+    ((consp value)
+     (let ((p value)
+          newlist
+          use-list*
+          anynil)
+       (while (consp p)
+        (let ((q.txt (desktop-internal-v2s (car p))))
+          (or anynil (setq anynil (null (car q.txt))))
+          (setq newlist (cons q.txt newlist)))
+        (setq p (cdr p)))
+       (if p
+          (let ((last (desktop-internal-v2s p)))
+            (or anynil (setq anynil (null (car last))))
+            (or anynil
+                (setq newlist (cons '(must . ".") newlist)))
+            (setq use-list* t)
+            (setq newlist (cons last newlist))))
+       (setq newlist (nreverse newlist))
+       (if anynil
+          (cons nil
+                (concat (if use-list* "(desktop-list* "  "(list ")
+                        (mapconcat (lambda (el)
+                                     (if (eq (car el) 'must)
+                                         (concat "'" (cdr el))
+                                       (cdr el)))
+                                   newlist
+                                   " ")
+                        ")"))
+        (cons 'must
+              (concat "(" (mapconcat 'cdr newlist " ") ")")))))
+    ((subrp value)
+     (cons nil (concat "(symbol-function '"
+                      (substring (prin1-to-string value) 7 -1)
+                      ")")))
+    ((markerp value)
+     (let ((pos (prin1-to-string (marker-position value)))
+          (buf (prin1-to-string (buffer-name (marker-buffer value)))))
+       (cons nil (concat "(let ((mk (make-marker)))"
+                        " (add-hook 'desktop-delay-hook"
+                        " (list 'lambda '() (list 'set-marker mk "
+                        pos " (get-buffer " buf ")))) mk)"))))
+    (t                                  ; save as text
+     (cons 'may "\"Unprintable entity\""))))
 
 ;; ----------------------------------------------------------------------------
 (defun desktop-value-to-string (value)
@@ -668,17 +797,16 @@ which means to truncate VAR's value to at most MAX-SIZE elements
     (if (consp varspec)
        (setq var (car varspec) size (cdr varspec))
       (setq var varspec))
-    (if (boundp var)
-       (progn
-         (if (and (integerp size)
-                  (> size 0)
-                  (listp (eval var)))
-             (desktop-truncate (eval var) size))
-         (insert "(setq "
-                 (symbol-name var)
-                 " "
-                 (desktop-value-to-string (symbol-value var))
-                 ")\n")))))
+    (when (boundp var)
+      (when (and (integerp size)
+                (> size 0)
+                (listp (eval var)))
+       (desktop-truncate (eval var) size))
+      (insert "(setq "
+             (symbol-name var)
+             " "
+             (desktop-value-to-string (symbol-value var))
+             ")\n"))))
 
 ;; ----------------------------------------------------------------------------
 (defun desktop-save-buffer-p (filename bufname mode &rest dummy)
@@ -715,102 +843,80 @@ DIRNAME must be the directory in which the desktop file will be saved."
     (t (expand-file-name filename))))
 
 ;; ----------------------------------------------------------------------------
-(defun desktop-save (dirname)
+;;;###autoload
+(defun desktop-save (dirname &optional release)
   "Save the desktop in a desktop file.
 Parameter DIRNAME specifies where to save the desktop file.
+Optional parameter RELEASE says whether we're done with this desktop.
 See also `desktop-base-file-name'."
   (interactive "DDirectory to save desktop file in: ")
-  (run-hooks 'desktop-save-hook)
-  (setq dirname (file-name-as-directory (expand-file-name dirname)))
+  (setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
   (save-excursion
-    (let ((filename (expand-file-name desktop-base-file-name dirname))
-          (info
-            (mapcar
-              #'(lambda (b)
-                  (set-buffer b)
-                  (list
-                    (desktop-file-name (buffer-file-name) dirname)
-                    (buffer-name)
-                    major-mode
-                    ;; minor modes
-                    (let (ret)
-                      (mapc
-                        #'(lambda (minor-mode)
-                          (and
-                            (boundp minor-mode)
-                            (symbol-value minor-mode)
-                            (let* ((special (assq minor-mode desktop-minor-mode-table))
-                                   (value (cond (special (cadr special))
-                                                ((functionp minor-mode) minor-mode))))
-                              (when value (add-to-list 'ret value)))))
-                        (mapcar #'car minor-mode-alist))
-                      ret)
-                    (point)
-                    (list (mark t) mark-active)
-                    buffer-read-only
-                    ;; Auxiliary information
-                    (when (functionp desktop-save-buffer)
-                      (funcall desktop-save-buffer dirname))
-                    (let ((locals desktop-locals-to-save)
-                          (loclist (buffer-local-variables))
-                          (ll))
-                      (while locals
-                        (let ((here (assq (car locals) loclist)))
-                          (if here
-                            (setq ll (cons here ll))
-                            (when (member (car locals) loclist)
-                              (setq ll (cons (car locals) ll)))))
-                        (setq locals (cdr locals)))
-                      ll)))
-              (buffer-list)))
-          (eager desktop-restore-eager)
-          (buf (get-buffer-create "*desktop*")))
-      (set-buffer buf)
-      (erase-buffer)
-
-      (insert
-        ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
-        desktop-header
-        ";; Created " (current-time-string) "\n"
-        ";; Desktop file format version " desktop-file-version "\n"
-        ";; Emacs version " emacs-version "\n\n"
-        ";; Global section:\n")
-      (mapc (function desktop-outvar) desktop-globals-to-save)
-      (if (memq 'kill-ring desktop-globals-to-save)
-        (insert
-          "(setq kill-ring-yank-pointer (nthcdr "
-          (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
-          " kill-ring))\n"))
-
-      (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
-      (mapc #'(lambda (l)
-                (when (apply 'desktop-save-buffer-p l)
-                  (insert "("
-                          (if (or (not (integerp eager))
-                                  (unless (zerop eager)
-                                    (setq eager (1- eager))
-                                    t))
-                              "desktop-create-buffer"
-                            "desktop-append-buffer-args")
-                          " "
-                          desktop-file-version)
-                  (mapc #'(lambda (e)
-                            (insert "\n  " (desktop-value-to-string e)))
-                        l)
-                  (insert ")\n\n")))
-            info)
-      (setq default-directory dirname)
-      (let ((coding-system-for-write 'emacs-mule))
-        (write-region (point-min) (point-max) filename nil 'nomessage))))
-  (setq desktop-dirname dirname))
+    (let ((eager desktop-restore-eager)
+         (new-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
+      (when
+         (or (not new-modtime)         ; nothing to overwrite
+             (equal desktop-file-modtime new-modtime)
+             (yes-or-no-p (if desktop-file-modtime
+                              (if (> (float-time new-modtime) (float-time desktop-file-modtime))
+                                  "Desktop file is more recent than the one loaded.  Save anyway? "
+                                "Desktop file isn't the one loaded.  Overwrite it? ")
+                            "Current desktop was not loaded from a file.  Overwrite this desktop file? "))
+             (unless release (error "Desktop file conflict")))
+
+       ;; If we're done with it, release the lock.
+       ;; Otherwise, claim it if it's unclaimed or if we created it.
+       (if release
+           (desktop-release-lock)
+         (unless (and new-modtime (desktop-owner)) (desktop-claim-lock)))
+
+       (with-temp-buffer
+         (insert
+          ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
+          desktop-header
+          ";; Created " (current-time-string) "\n"
+          ";; Desktop file format version " desktop-file-version "\n"
+          ";; Emacs version " emacs-version "\n")
+         (save-excursion (run-hooks 'desktop-save-hook))
+         (goto-char (point-max))
+         (insert "\n;; Global section:\n")
+         (mapc (function desktop-outvar) desktop-globals-to-save)
+         (when (memq 'kill-ring desktop-globals-to-save)
+           (insert
+            "(setq kill-ring-yank-pointer (nthcdr "
+            (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
+            " kill-ring))\n"))
+
+         (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
+         (dolist (l (mapcar 'desktop-buffer-info (buffer-list)))
+           (when (apply 'desktop-save-buffer-p l)
+             (insert "("
+                     (if (or (not (integerp eager))
+                             (if (zerop eager)
+                                 nil
+                               (setq eager (1- eager))))
+                         "desktop-create-buffer"
+                       "desktop-append-buffer-args")
+                     " "
+                     desktop-file-version)
+             (dolist (e l)
+               (insert "\n  " (desktop-value-to-string e)))
+             (insert ")\n\n")))
+
+         (setq default-directory desktop-dirname)
+         (let ((coding-system-for-write 'emacs-mule))
+           (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
+         ;; We remember when it was modified (which is presumably just now).
+         (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))))))
 
 ;; ----------------------------------------------------------------------------
+;;;###autoload
 (defun desktop-remove ()
   "Delete desktop file in `desktop-dirname'.
 This function also sets `desktop-dirname' to nil."
   (interactive)
   (when desktop-dirname
-    (let ((filename (expand-file-name desktop-base-file-name desktop-dirname)))
+    (let ((filename (desktop-full-file-name)))
       (setq desktop-dirname nil)
       (when (file-exists-p filename)
         (delete-file filename)))))
@@ -833,52 +939,73 @@ It returns t if a desktop file was loaded, nil otherwise."
   (interactive)
   (unless noninteractive
     (setq desktop-dirname
-      (file-name-as-directory
-        (expand-file-name
-          (or
-            ;; If DIRNAME is specified, use it.
-            (and (< 0 (length dirname)) dirname)
-            ;; Otherwise search desktop file in desktop-path.
-            (let ((dirs desktop-path))
-              (while
-                (and
-                  dirs
-                  (not
-                    (file-exists-p (expand-file-name desktop-base-file-name (car dirs)))))
-                (setq dirs (cdr dirs)))
-              (and dirs (car dirs)))
-            ;; If not found and `desktop-path' is non-nil, use its first element.
-            (and desktop-path (car desktop-path))
-            ;; Default: Home directory.
-            "~"))))
-    (if (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
-      ;; Desktop file found, process it.
-      (let ((desktop-first-buffer nil)
-            (desktop-buffer-ok-count 0)
-            (desktop-buffer-fail-count 0))
-        (setq desktop-lazy-timer nil)
-        ;; Evaluate desktop buffer.
-        (load (expand-file-name desktop-base-file-name desktop-dirname) t t t)
-        ;; `desktop-create-buffer' puts buffers at end of the buffer list.
-        ;; We want buffers existing prior to evaluating the desktop (and not reused)
-        ;; to be placed at the end of the buffer list, so we move them here.
-        (mapc 'bury-buffer
-              (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
-        (switch-to-buffer (car (buffer-list)))
-        (run-hooks 'desktop-delay-hook)
-        (setq desktop-delay-hook nil)
-        (run-hooks 'desktop-after-read-hook)
-        (message "Desktop: %d buffer%s restored%s%s."
-                 desktop-buffer-ok-count
-                 (if (= 1 desktop-buffer-ok-count) "" "s")
-                 (if (< 0 desktop-buffer-fail-count)
-                     (format ", %d failed to restore" desktop-buffer-fail-count)
-                   "")
-                 (if desktop-buffer-args-list
-                     (format ", %d to restore lazily"
-                             (length desktop-buffer-args-list))
-                   ""))
-        t)
+          (file-name-as-directory
+           (expand-file-name
+            (or
+             ;; If DIRNAME is specified, use it.
+             (and (< 0 (length dirname)) dirname)
+             ;; Otherwise search desktop file in desktop-path.
+             (let ((dirs desktop-path))
+               (while (and dirs
+                           (not (file-exists-p
+                                 (desktop-full-file-name (car dirs)))))
+                 (setq dirs (cdr dirs)))
+               (and dirs (car dirs)))
+             ;; If not found and `desktop-path' is non-nil, use its first element.
+             (and desktop-path (car desktop-path))
+             ;; Default: Home directory.
+             "~"))))
+    (if (file-exists-p (desktop-full-file-name))
+       ;; Desktop file found, but is it already in use?
+       (let ((desktop-first-buffer nil)
+             (desktop-buffer-ok-count 0)
+             (desktop-buffer-fail-count 0)
+             (owner (desktop-owner))
+             ;; Avoid desktop saving during evaluation of desktop buffer.
+             (desktop-save nil))
+         (if (and owner
+                  (memq desktop-load-locked-desktop '(nil ask))
+                  (or (null desktop-load-locked-desktop)
+                      (not (y-or-n-p (format "Warning: desktop file appears to be in use by PID %s.\n\
+Using it may cause conflicts.  Use it anyway? " owner)))))
+             (progn
+               (let ((default-directory desktop-dirname))
+                 (run-hooks 'desktop-not-loaded-hook))
+               (setq desktop-dirname nil)
+               (message "Desktop file in use; not loaded."))
+           (desktop-lazy-abort)
+           ;; Evaluate desktop buffer and remember when it was modified.
+           (load (desktop-full-file-name) t t t)
+           (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
+           ;; If it wasn't already, mark it as in-use, to bother other
+           ;; desktop instances.
+           (unless owner
+             (condition-case nil
+                 (desktop-claim-lock)
+               (file-error (message "Couldn't record use of desktop file")
+                           (sit-for 1))))
+
+           ;; `desktop-create-buffer' puts buffers at end of the buffer list.
+           ;; We want buffers existing prior to evaluating the desktop (and
+           ;; not reused) to be placed at the end of the buffer list, so we
+           ;; move them here.
+           (mapc 'bury-buffer
+                 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
+           (switch-to-buffer (car (buffer-list)))
+           (run-hooks 'desktop-delay-hook)
+           (setq desktop-delay-hook nil)
+           (run-hooks 'desktop-after-read-hook)
+           (message "Desktop: %d buffer%s restored%s%s."
+                    desktop-buffer-ok-count
+                    (if (= 1 desktop-buffer-ok-count) "" "s")
+                    (if (< 0 desktop-buffer-fail-count)
+                        (format ", %d failed to restore" desktop-buffer-fail-count)
+                      "")
+                    (if desktop-buffer-args-list
+                        (format ", %d to restore lazily"
+                                (length desktop-buffer-args-list))
+                      ""))
+           t))
       ;; No desktop file found.
       (desktop-clear)
       (let ((default-directory desktop-dirname))
@@ -928,41 +1055,40 @@ directory DIRNAME."
   (interactive)
   (unless desktop-dirname
     (error "Unknown desktop directory"))
-  (unless (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
+  (unless (file-exists-p (desktop-full-file-name))
     (error "No desktop file found"))
   (desktop-clear)
   (desktop-read desktop-dirname))
 
+(defvar desktop-buffer-major-mode)
+(defvar desktop-buffer-locals)
 ;; ----------------------------------------------------------------------------
 (defun desktop-restore-file-buffer (desktop-buffer-file-name
                                     desktop-buffer-name
                                     desktop-buffer-misc)
   "Restore a file buffer."
-  (eval-when-compile ; Just to silence the byte compiler
-    (defvar desktop-buffer-major-mode)
-    (defvar desktop-buffer-locals))
-  (if desktop-buffer-file-name
-      (if (or (file-exists-p desktop-buffer-file-name)
-              (let ((msg (format "Desktop: File \"%s\" no longer exists."
-                                 desktop-buffer-file-name)))
-                 (if desktop-missing-file-warning
-                    (y-or-n-p (concat msg " Re-create? "))
-                   (message "%s" msg)
-                   nil)))
-         (let* ((auto-insert nil) ; Disable auto insertion
-                (coding-system-for-read
-                 (or coding-system-for-read
-                     (cdr (assq 'buffer-file-coding-system
-                                desktop-buffer-locals))))
-                (buf (find-file-noselect desktop-buffer-file-name)))
-           (condition-case nil
-               (switch-to-buffer buf)
-             (error (pop-to-buffer buf)))
-           (and (not (eq major-mode desktop-buffer-major-mode))
-                (functionp desktop-buffer-major-mode)
-                (funcall desktop-buffer-major-mode))
-           buf)
-       nil)))
+  (when desktop-buffer-file-name
+    (if (or (file-exists-p desktop-buffer-file-name)
+           (let ((msg (format "Desktop: File \"%s\" no longer exists."
+                              desktop-buffer-file-name)))
+             (if desktop-missing-file-warning
+                 (y-or-n-p (concat msg " Re-create buffer? "))
+               (message "%s" msg)
+               nil)))
+       (let* ((auto-insert nil) ; Disable auto insertion
+              (coding-system-for-read
+               (or coding-system-for-read
+                   (cdr (assq 'buffer-file-coding-system
+                              desktop-buffer-locals))))
+              (buf (find-file-noselect desktop-buffer-file-name)))
+         (condition-case nil
+             (switch-to-buffer buf)
+           (error (pop-to-buffer buf)))
+         (and (not (eq major-mode desktop-buffer-major-mode))
+              (functionp desktop-buffer-major-mode)
+              (funcall desktop-buffer-major-mode))
+         buf)
+      nil)))
 
 (defun desktop-load-file (function)
   "Load the file where auto loaded FUNCTION is defined."
@@ -977,8 +1103,12 @@ directory DIRNAME."
 ;; called from Desktop file only.
 
 ;; Just to silence the byte compiler.
-(eval-when-compile
-  (defvar desktop-first-buffer)) ; Dynamically bound in `desktop-read'
+
+(defvar desktop-first-buffer)          ; Dynamically bound in `desktop-read'
+
+;; Bound locally in `desktop-read'.
+(defvar desktop-buffer-ok-count)
+(defvar desktop-buffer-fail-count)
 
 (defun desktop-create-buffer
   (desktop-file-version
@@ -992,10 +1122,6 @@ directory DIRNAME."
    desktop-buffer-misc
    &optional
    desktop-buffer-locals)
-  ;; Just to silence the byte compiler. Bound locally in `desktop-read'.
-  (eval-when-compile
-    (defvar desktop-buffer-ok-count)
-    (defvar desktop-buffer-fail-count))
   ;; To make desktop files with relative file names possible, we cannot
   ;; allow `default-directory' to change. Therefore we save current buffer.
   (save-current-buffer
@@ -1030,45 +1156,46 @@ directory DIRNAME."
           (setq desktop-first-buffer result))
         (set-buffer result)
         (unless (equal (buffer-name) desktop-buffer-name)
-          (rename-buffer desktop-buffer-name))
+          (rename-buffer desktop-buffer-name t))
         ;; minor modes
         (cond ((equal '(t) desktop-buffer-minor-modes) ; backwards compatible
                (auto-fill-mode 1))
               ((equal '(nil) desktop-buffer-minor-modes) ; backwards compatible
                (auto-fill-mode 0))
               (t
-               (mapc #'(lambda (minor-mode)
-                         ;; Give minor mode module a chance to add a handler.
-                         (desktop-load-file minor-mode)
-                         (let ((handler (cdr (assq minor-mode desktop-minor-mode-handlers))))
-                           (if handler
-                               (funcall handler desktop-buffer-locals)
-                             (when (functionp minor-mode)
-                               (funcall minor-mode 1)))))
-                    desktop-buffer-minor-modes)))
-        ;; Even though point and mark are non-nil when written by `desktop-save',
-        ;; they may be modified by handlers wanting to set point or mark themselves.
+               (dolist (minor-mode desktop-buffer-minor-modes)
+                 ;; Give minor mode module a chance to add a handler.
+                 (desktop-load-file minor-mode)
+                 (let ((handler (cdr (assq minor-mode desktop-minor-mode-handlers))))
+                   (if handler
+                       (funcall handler desktop-buffer-locals)
+                     (when (functionp minor-mode)
+                       (funcall minor-mode 1)))))))
+        ;; Even though point and mark are non-nil when written by
+        ;; `desktop-save', they may be modified by handlers wanting to set
+        ;; point or mark themselves.
         (when desktop-buffer-point
           (goto-char
             (condition-case err
-                ;; Evaluate point. Thus point can be something like '(search-forward ...
+                ;; Evaluate point.  Thus point can be something like
+                ;; '(search-forward ...
                 (eval desktop-buffer-point)
               (error (message "%s" (error-message-string err)) 1))))
         (when desktop-buffer-mark
           (if (consp desktop-buffer-mark)
-            (progn
-              (set-mark (car desktop-buffer-mark))
-              (setq mark-active (car (cdr desktop-buffer-mark))))
+             (progn
+               (set-mark (car desktop-buffer-mark))
+               (setq mark-active (car (cdr desktop-buffer-mark))))
             (set-mark desktop-buffer-mark)))
         ;; Never override file system if the file really is read-only marked.
-        (if desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
+        (when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
         (while desktop-buffer-locals
           (let ((this (car desktop-buffer-locals)))
             (if (consp this)
-              ;; an entry of this form `(symbol . value)'
-              (progn
-                (make-local-variable (car this))
-                (set (car this) (cdr this)))
+               ;; an entry of this form `(symbol . value)'
+               (progn
+                 (make-local-variable (car this))
+                 (set (car this) (cdr this)))
               ;; an entry of the form `symbol'
               (make-local-variable this)
               (makunbound this)))
@@ -1159,7 +1286,7 @@ If there are no buffers left to create, kill the timer."
 ;; functions are processed after `after-init-hook'.
 (add-hook
   'after-init-hook
-  '(lambda ()
+  (lambda ()
     (let ((key "--no-desktop"))
       (when (member key command-line-args)
         (setq command-line-args (delete key command-line-args))
@@ -1168,5 +1295,5 @@ If there are no buffers left to create, kill the timer."
 
 (provide 'desktop)
 
-;;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9
+;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9
 ;;; desktop.el ends here