Merge from emacs-24; up to 2012-04-21T14:12:27Z!sdl.web@gmail.com
[bpt/emacs.git] / lisp / emacs-lisp / package.el
index bdb40dd..66370c6 100644 (file)
@@ -1,10 +1,10 @@
 ;;; package.el --- Simple package system for Emacs
 
-;; Copyright (C) 2007-2011 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2012 Free Software Foundation, Inc.
 
 ;; Author: Tom Tromey <tromey@redhat.com>
 ;; Created: 10 Mar 2007
-;; Version: 0.9
+;; Version: 1.0
 ;; Keywords: tools
 
 ;; This file is part of GNU Emacs.
 ;;    can see what packages are available.  This will automatically
 ;;    fetch the latest list of packages from ELPA.
 ;;
-;; M-x package-list-packages-no-fetch
-;;    Like package-list-packages, but does not automatically fetch the
-;;    new list of packages.
-;;
 ;; M-x package-install-from-buffer
 ;;    Install a package consisting of a single .el file that appears
 ;;    in the current buffer.  This only works for packages which
 
 ;;; ToDo:
 
+;; - a trust mechanism, since compiling a package can run arbitrary code.
+;;   For example, download package signatures and check that they match.
 ;; - putting info dirs at the start of the info path means
 ;;   users see a weird ordering of categories.  OTOH we want to
 ;;   override later entries.  maybe emacs needs to enforce
@@ -228,7 +226,10 @@ Each element has the form (ID . LOCATION).
  LOCATION specifies the base location for the archive.
   If it starts with \"http:\", it is treated as a HTTP URL;
   otherwise it should be an absolute directory name.
-  (Other types of URL are currently not supported.)"
+  (Other types of URL are currently not supported.)
+
+Only add locations that you trust, since fetching and installing
+a package can run arbitrary code."
   :type '(alist :key-type (string :tag "Archive name")
                 :value-type (string :tag "URL or directory name"))
   :risky t
@@ -281,7 +282,7 @@ contrast, `package-user-dir' contains packages for personal use."
   :version "24.1")
 
 ;; The value is precomputed in finder-inf.el, but don't load that
-;; until it's needed (i.e. when `package-intialize' is called).
+;; until it's needed (i.e. when `package-initialize' is called).
 (defvar package--builtins nil
   "Alist of built-in packages.
 The actual value is initialized by loading the library
@@ -313,7 +314,7 @@ The vector DESC has the form [VERSION-LIST REQS DOCSTRING].
 This variable is set automatically by `package-load-descriptor',
 called via `package-initialize'.  To change which packages are
 loaded and/or activated, customize `package-load-list'.")
-(put 'package-archive-contents 'risky-local-variable t)
+(put 'package-alist 'risky-local-variable t)
 
 (defvar package-activated-list nil
   "List of the names of currently activated packages.")
@@ -381,30 +382,37 @@ controls which package subdirectories may be loaded.
 In each valid package subdirectory, this function loads the
 description file containing a call to `define-package', which
 updates `package-alist' and `package-obsolete-alist'."
-  (let ((all (memq 'all package-load-list))
-       (regexp (concat "\\`" package-subdirectory-regexp "\\'"))
-       name version force)
+  (let ((regexp (concat "\\`" package-subdirectory-regexp "\\'")))
     (dolist (dir (cons package-user-dir package-directory-list))
       (when (file-directory-p dir)
        (dolist (subdir (directory-files dir))
-         (when (and (file-directory-p (expand-file-name subdir dir))
-                    (string-match regexp subdir))
-           (setq name    (intern (match-string 1 subdir))
-                 version (match-string 2 subdir)
-                 force   (assq name package-load-list))
-           (when (cond
-                  ((null force)
-                   all) ; not in package-load-list
-                  ((null (setq force (cadr force)))
-                   nil) ; disabled
-                  ((eq force t)
-                   t)
-                  ((stringp force) ; held
-                   (version-list-= (version-to-list version)
-                                   (version-to-list force)))
-                  (t
-                   (error "Invalid element in `package-load-list'")))
-             (package-load-descriptor dir subdir))))))))
+         (when (string-match regexp subdir)
+           (package-maybe-load-descriptor (match-string 1 subdir)
+                                          (match-string 2 subdir)
+                                          dir)))))))
+
+(defun package-maybe-load-descriptor (name version dir)
+  "Maybe load a specific package from directory DIR.
+NAME and VERSION are the package's name and version strings.
+This function checks `package-load-list', before actually loading
+the package by calling `package-load-descriptor'."
+  (let ((force (assq (intern name) package-load-list))
+       (subdir (concat name "-" version)))
+    (and (file-directory-p (expand-file-name subdir dir))
+        ;; Check `package-load-list':
+        (cond ((null force)
+               (memq 'all package-load-list))
+              ((null (setq force (cadr force)))
+               nil) ; disabled
+              ((eq force t)
+               t)
+              ((stringp force) ; held
+               (version-list-= (version-to-list version)
+                               (version-to-list force)))
+              (t
+               (error "Invalid element in `package-load-list'")))
+        ;; Actually load the descriptor:
+        (package-load-descriptor dir subdir))))
 
 (defsubst package-desc-vers (desc)
   "Extract version from a package description vector."
@@ -461,8 +469,11 @@ NAME and VERSION are both strings."
 Optional arg MIN-VERSION, if non-nil, should be a version list
 specifying the minimum acceptable version."
   (require 'finder-inf nil t) ; For `package--builtins'.
-  (let ((elt (assq package package--builtins)))
-    (and elt (min-version-<= min-version (package-desc-vers (cdr elt))))))
+  (if (eq package 'emacs)
+      (version-list-<= min-version (version-to-list emacs-version))
+    (let ((elt (assq package package--builtins)))
+      (and elt (version-list-<= min-version
+                               (package-desc-vers (cdr elt)))))))
 
 ;; This function goes ahead and activates a newer version of a package
 ;; if an older one was already activated.  This is not ideal; we'd at
@@ -516,7 +527,7 @@ Required package `%s-%s' is unavailable"
 
 (defun define-package (name-string version-string
                                &optional docstring requirements
-                               &rest extra-properties)
+                               &rest _extra-properties)
   "Define a new package.
 NAME-STRING is the name of the package, as a string.
 VERSION-STRING is the version of the package, as a string.
@@ -574,11 +585,11 @@ EXTRA-PROPERTIES is currently unused."
   file)
 
 (defun package-generate-autoloads (name pkg-dir)
+  (require 'autoload)         ;Load before we let-bind generated-autoload-file!
   (let* ((auto-name (concat name "-autoloads.el"))
-        (ignore-name (concat name "-pkg.el"))
+        ;;(ignore-name (concat name "-pkg.el"))
         (generated-autoload-file (expand-file-name auto-name pkg-dir))
         (version-control 'never))
-    (require 'autoload)
     (unless (fboundp 'autoload-ensure-default-file)
       (package-autoload-ensure-default-file generated-autoload-file))
     (update-directory-autoloads pkg-dir)))
@@ -599,16 +610,25 @@ untar into a directory named DIR; otherwise, signal an error."
        (error "Package does not untar cleanly into directory %s/" dir))))
   (tar-untar-buffer))
 
-(defun package-unpack (name version)
-  (let* ((dirname (concat (symbol-name name) "-" version))
+(defun package-unpack (package version)
+  (let* ((name (symbol-name package))
+        (dirname (concat name "-" version))
         (pkg-dir (expand-file-name dirname package-user-dir)))
     (make-directory package-user-dir t)
     ;; FIXME: should we delete PKG-DIR if it exists?
     (let* ((default-directory (file-name-as-directory package-user-dir)))
       (package-untar-buffer dirname)
-      (package-generate-autoloads (symbol-name name) pkg-dir)
-      (let ((load-path (cons pkg-dir load-path)))
-       (byte-recompile-directory pkg-dir 0 t)))))
+      (package--make-autoloads-and-compile name pkg-dir))))
+
+(defun package--make-autoloads-and-compile (name pkg-dir)
+  "Generate autoloads and do byte-compilation for package named NAME.
+PKG-DIR is the name of the package directory."
+  (package-generate-autoloads name pkg-dir)
+  (let ((load-path (cons pkg-dir load-path)))
+    ;; We must load the autoloads file before byte compiling, in
+    ;; case there are magic cookies to set up non-trivial paths.
+    (load (expand-file-name (concat name "-autoloads") pkg-dir) nil t)
+    (byte-recompile-directory pkg-dir 0 t)))
 
 (defun package--write-file-no-coding (file-name)
   (let ((buffer-file-coding-system 'no-conversion))
@@ -648,9 +668,7 @@ untar into a directory named DIR; otherwise, signal an error."
         nil
         pkg-file
         nil nil nil 'excl))
-      (package-generate-autoloads file-name pkg-dir)
-      (let ((load-path (cons pkg-dir load-path)))
-       (byte-recompile-directory pkg-dir 0 t)))))
+      (package--make-autoloads-and-compile file-name pkg-dir))))
 
 (defmacro package--with-work-buffer (location file &rest body)
   "Run BODY in a buffer containing the contents of FILE at LOCATION.
@@ -742,7 +760,8 @@ not included in this list."
              hold)
          (when (setq hold (assq next-pkg package-load-list))
            (setq hold (cadr hold))
-           (cond ((eq hold nil)
+           (cond ((eq hold t))
+                 ((eq hold nil)
                   (error "Required package '%s' is disabled"
                          (symbol-name next-pkg)))
                  ((null (stringp hold))
@@ -824,13 +843,19 @@ If the archive version is too new, signal an error."
   "Add the PACKAGE from the given ARCHIVE if necessary.
 Also, add the originating archive to the end of the package vector."
   (let* ((name    (car package))
-         (version (aref (cdr package) 0))
-         (entry   (cons (car package)
+         (version (package-desc-vers (cdr package)))
+         (entry   (cons name
                        (vconcat (cdr package) (vector archive))))
-         (existing-package (cdr (assq name package-archive-contents))))
-    (when (or (not existing-package)
-              (version-list-< (aref existing-package 0) version))
-      (add-to-list 'package-archive-contents entry))))
+         (existing-package (assq name package-archive-contents)))
+    (cond ((not existing-package)
+          (add-to-list 'package-archive-contents entry))
+         ((version-list-< (package-desc-vers (cdr existing-package))
+                          version)
+          ;; Replace the entry with this one.
+          (setq package-archive-contents
+                (cons entry
+                      (delq existing-package
+                            package-archive-contents)))))))
 
 (defun package-download-transaction (package-list)
   "Download and install all the packages in PACKAGE-LIST.
@@ -854,29 +879,43 @@ using `package-compute-transaction'."
                                 (package-desc-doc desc)
                                 (package-desc-reqs desc)))
        (t
-       (error "Unknown package kind: %s" (symbol-name kind)))))))
+       (error "Unknown package kind: %s" (symbol-name kind))))
+      ;; If package A depends on package B, then A may `require' B
+      ;; during byte compilation.  So we need to activate B before
+      ;; unpacking A.
+      (package-maybe-load-descriptor (symbol-name elt) v-string
+                                    package-user-dir)
+      (package-activate elt (version-to-list v-string)))))
+
+(defvar package--initialized nil)
 
 ;;;###autoload
 (defun package-install (name)
   "Install the package named NAME.
-Interactively, prompt for the package name.
-The package is found on one of the archives in `package-archives'."
+NAME should be the name of one of the available packages in an
+archive in `package-archives'.  Interactively, prompt for NAME."
   (interactive
-   (list (intern (completing-read "Install package: "
-                                 (mapcar (lambda (elt)
-                                           (cons (symbol-name (car elt))
-                                                 nil))
-                                         package-archive-contents)
-                                 nil t))))
+   (progn
+     ;; Initialize the package system to get the list of package
+     ;; symbols for completion.
+     (unless package--initialized
+       (package-initialize t))
+     (unless package-archive-contents
+       (package-refresh-contents))
+     (list (intern (completing-read
+                   "Install package: "
+                   (mapcar (lambda (elt)
+                             (cons (symbol-name (car elt))
+                                   nil))
+                           package-archive-contents)
+                   nil t)))))
   (let ((pkg-desc (assq name package-archive-contents)))
     (unless pkg-desc
       (error "Package `%s' is not available for installation"
             (symbol-name name)))
     (package-download-transaction
      (package-compute-transaction (list name)
-                                 (package-desc-reqs (cdr pkg-desc)))))
-  ;; Try to activate it.
-  (package-initialize))
+                                 (package-desc-reqs (cdr pkg-desc))))))
 
 (defun package-strip-rcs-id (str)
   "Strip RCS version ID from the version string STR.
@@ -907,7 +946,7 @@ If the buffer does not contain a conforming package, signal an
 error.  If there is a package, narrow the buffer to the file's
 boundaries."
   (goto-char (point-min))
-  (unless (re-search-forward "^;;; \\([^ ]*\\)\\.el --- \\(.*\\)$" nil t)
+  (unless (re-search-forward "^;;; \\([^ ]*\\)\\.el ---[ \t]*\\(.*?\\)[ \t]*\\(-\\*-.*-\\*-[ \t]*\\)?$" nil t)
     (error "Packages lacks a file header"))
   (let ((file-name (match-string-no-properties 1))
        (desc      (match-string-no-properties 2))
@@ -1066,6 +1105,7 @@ similar to an entry in `package-alist'.  Save the cached copy to
        (let ((version-control 'never))
          (save-buffer))))))
 
+;;;###autoload
 (defun package-refresh-contents ()
   "Download the ELPA archive description if needed.
 This informs Emacs about the latest versions of all packages, and
@@ -1074,14 +1114,12 @@ makes them available for download."
   (unless (file-exists-p package-user-dir)
     (make-directory package-user-dir t))
   (dolist (archive package-archives)
-    (condition-case-no-debug nil
+    (condition-case-unless-debug nil
        (package--download-one-archive archive "archive-contents")
       (error (message "Failed to download `%s' archive."
                      (car archive)))))
   (package-read-all-archive-contents))
 
-(defvar package--initialized nil)
-
 ;;;###autoload
 (defun package-initialize (&optional no-activate)
   "Load Emacs Lisp packages, and activate them.
@@ -1267,6 +1305,7 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages."
     (define-key map "\177" 'package-menu-backup-unmark)
     (define-key map "d" 'package-menu-mark-delete)
     (define-key map "i" 'package-menu-mark-install)
+    (define-key map "U" 'package-menu-mark-upgrades)
     (define-key map "r" 'package-menu-refresh)
     (define-key map "~" 'package-menu-mark-obsolete-for-deletion)
     (define-key map "x" 'package-menu-execute)
@@ -1288,27 +1327,30 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages."
       '(menu-item "Unmark" package-menu-mark-unmark
                  :help "Clear any marks on a package and move to the next line"))
     (define-key menu-map [munm]
-      '(menu-item "Unmark backwards" package-menu-backup-unmark
+      '(menu-item "Unmark Backwards" package-menu-backup-unmark
                  :help "Back up one line and clear any marks on that package"))
     (define-key menu-map [md]
-      '(menu-item "Mark for deletion" package-menu-mark-delete
+      '(menu-item "Mark for Deletion" package-menu-mark-delete
                  :help "Mark a package for deletion and move to the next line"))
     (define-key menu-map [mi]
-      '(menu-item "Mark for install" package-menu-mark-install
+      '(menu-item "Mark for Install" package-menu-mark-install
                  :help "Mark a package for installation and move to the next line"))
+    (define-key menu-map [mupgrades]
+      '(menu-item "Mark Upgradable Packages" package-menu-mark-upgrades
+                 :help "Mark packages that have a newer version for upgrading"))
     (define-key menu-map [s3] '("--"))
     (define-key menu-map [mg]
-      '(menu-item "Update package list" revert-buffer
+      '(menu-item "Update Package List" revert-buffer
                  :help "Update the list of packages"))
     (define-key menu-map [mr]
-      '(menu-item "Refresh package list" package-menu-refresh
+      '(menu-item "Refresh Package List" package-menu-refresh
                  :help "Download the ELPA archive"))
     (define-key menu-map [s4] '("--"))
     (define-key menu-map [mt]
-      '(menu-item "Mark obsolete packages" package-menu-mark-obsolete-for-deletion
+      '(menu-item "Mark Obsolete Packages" package-menu-mark-obsolete-for-deletion
                  :help "Mark all obsolete packages for deletion"))
     (define-key menu-map [mx]
-      '(menu-item "Execute actions" package-menu-execute
+      '(menu-item "Execute Actions" package-menu-execute
                  :help "Perform all the marked actions"))
     (define-key menu-map [s5] '("--"))
     (define-key menu-map [mh]
@@ -1344,38 +1386,45 @@ a symbol and VERSION-LIST is a version list."
      (unless (assoc key ,listname)
        (push (list key ,status (package-desc-doc ,desc)) ,listname))))
 
-(defun package-menu--generate (&optional remember-pos)
+(defun package-menu--generate (remember-pos packages)
   "Populate the Package Menu.
-Optional argument REMEMBER-POS, if non-nil, means to move point
-to the entry as before."
+If REMEMBER-POS is non-nil, keep point on the same entry.
+PACKAGES should be t, which means to display all known packages,
+or a list of package names (symbols) to display."
   ;; Construct list of ((PACKAGE . VERSION) STATUS DESCRIPTION).
-  (let (info-list name builtin)
+  (let (info-list name)
     ;; Installed packages:
     (dolist (elt package-alist)
       (setq name (car elt))
-      (package--push name (cdr elt)
-                    (if (stringp (cadr (assq name package-load-list)))
-                        "held" "installed")
-                    info-list))
+      (when (or (eq packages t) (memq name packages))
+       (package--push name (cdr elt)
+                      (if (stringp (cadr (assq name package-load-list)))
+                          "held" "installed")
+                      info-list)))
 
     ;; Built-in packages:
     (dolist (elt package--builtins)
       (setq name (car elt))
-      (unless (eq name 'emacs) ; Hide the `emacs' package.
+      (when (and (not (eq name 'emacs)) ; Hide the `emacs' package.
+                (or (eq packages t) (memq name packages)))
        (package--push name (cdr elt) "built-in" info-list)))
 
     ;; Available and disabled packages:
     (dolist (elt package-archive-contents)
       (setq name (car elt))
-      (let ((hold (assq name package-load-list)))
-       (package--push name (cdr elt)
-                      (if (and hold (null (cadr hold))) "disabled" "available")
-                      info-list)))
+      (when (or (eq packages t) (memq name packages))
+       (let ((hold (assq name package-load-list)))
+         (package--push name (cdr elt)
+                        (if (and hold (null (cadr hold)))
+                            "disabled"
+                          "available")
+                        info-list))))
 
     ;; Obsolete packages:
     (dolist (elt package-obsolete-alist)
       (dolist (inner-elt (cdr elt))
-       (package--push (car elt) (cdr inner-elt) "obsolete" info-list)))
+       (when (or (eq packages t) (memq (car elt) packages))
+         (package--push (car elt) (cdr inner-elt) "obsolete" info-list))))
 
     ;; Print the result.
     (setq tabulated-list-entries (mapcar 'package-menu--print-info info-list))
@@ -1413,10 +1462,10 @@ identifier (NAME . VERSION-LIST)."
 This fetches the contents of each archive specified in
 `package-archives', and then refreshes the package menu."
   (interactive)
-  (unless (eq major-mode 'package-menu-mode)
+  (unless (derived-mode-p 'package-menu-mode)
     (error "The current buffer is not a Package Menu"))
   (package-refresh-contents)
-  (package-menu--generate t))
+  (package-menu--generate t t))
 
 (defun package-menu-describe-package (&optional button)
   "Describe the current package.
@@ -1428,21 +1477,21 @@ If optional arg BUTTON is non-nil, describe its associated package."
        (describe-package package))))
 
 ;; fixme numeric argument
-(defun package-menu-mark-delete (num)
+(defun package-menu-mark-delete (&optional _num)
   "Mark a package for deletion and move to the next line."
   (interactive "p")
-  (if (string-equal (package-menu-get-status) "installed")
+  (if (member (package-menu-get-status) '("installed" "obsolete"))
       (tabulated-list-put-tag "D" t)
     (forward-line)))
 
-(defun package-menu-mark-install (num)
+(defun package-menu-mark-install (&optional _num)
   "Mark a package for installation and move to the next line."
   (interactive "p")
   (if (string-equal (package-menu-get-status) "available")
       (tabulated-list-put-tag "I" t)
     (forward-line)))
 
-(defun package-menu-mark-unmark (num)
+(defun package-menu-mark-unmark (&optional _num)
   "Clear any marks on a package and move to the next line."
   (interactive "p")
   (tabulated-list-put-tag " " t))
@@ -1458,9 +1507,8 @@ If optional arg BUTTON is non-nil, describe its associated package."
   (interactive)
   (save-excursion
     (goto-char (point-min))
-    (forward-line 2)
     (while (not (eobp))
-      (if (looking-at ".*\\s obsolete\\s ")
+      (if (equal (package-menu-get-status) "obsolete")
          (tabulated-list-put-tag "D" t)
        (forward-line 1)))))
 
@@ -1473,17 +1521,65 @@ If optional arg BUTTON is non-nil, describe its associated package."
   'package-menu-view-commentary 'package-menu-describe-package "24.1")
 
 (defun package-menu-get-status ()
-  (save-excursion
-    (if (looking-at ". [^ \t]*[ \t]*[^ \t]*[ \t]*\\([^ \t]*\\)")
-       (match-string 1)
+  (let* ((pkg (tabulated-list-get-id))
+        (entry (and pkg (assq pkg tabulated-list-entries))))
+    (if entry
+       (aref (cadr entry) 2)
       "")))
 
+(defun package-menu--find-upgrades ()
+  (let (installed available upgrades)
+    ;; Build list of installed/available packages in this buffer.
+    (dolist (entry tabulated-list-entries)
+      ;; ENTRY is ((NAME . VERSION) [NAME VERSION STATUS DOC])
+      (let ((pkg (car entry))
+           (status (aref (cadr entry) 2)))
+       (cond ((equal status "installed")
+              (push pkg installed))
+             ((equal status "available")
+              (push pkg available)))))
+    ;; Loop through list of installed packages, finding upgrades
+    (dolist (pkg installed)
+      (let ((avail-pkg (assq (car pkg) available)))
+       (and avail-pkg
+            (version-list-< (cdr pkg) (cdr avail-pkg))
+            (push avail-pkg upgrades))))
+    upgrades))
+
+(defun package-menu-mark-upgrades ()
+  "Mark all upgradable packages in the Package Menu.
+For each installed package with a newer version available, place
+an (I)nstall flag on the available version and a (D)elete flag on
+the installed version.  A subsequent \\[package-menu-execute]
+call will upgrade the package."
+  (interactive)
+  (unless (derived-mode-p 'package-menu-mode)
+    (error "The current buffer is not a Package Menu"))
+  (let ((upgrades (package-menu--find-upgrades)))
+    (if (null upgrades)
+       (message "No packages to upgrade.")
+      (widen)
+      (save-excursion
+       (goto-char (point-min))
+       (while (not (eobp))
+         (let* ((pkg (tabulated-list-get-id))
+                (upgrade (assq (car pkg) upgrades)))
+           (cond ((null upgrade)
+                  (forward-line 1))
+                 ((equal pkg upgrade)
+                  (package-menu-mark-install))
+                 (t
+                  (package-menu-mark-delete))))))
+      (message "%d package%s marked for upgrading."
+              (length upgrades)
+              (if (= (length upgrades) 1) "" "s")))))
+
 (defun package-menu-execute ()
   "Perform marked Package Menu actions.
 Packages marked for installation are downloaded and installed;
 packages marked for deletion are removed."
   (interactive)
-  (unless (eq major-mode 'package-menu-mode)
+  (unless (derived-mode-p 'package-menu-mode)
     (error "The current buffer is not in Package Menu mode"))
   (let (install-list delete-list cmd id)
     (save-excursion
@@ -1500,6 +1596,14 @@ packages marked for deletion are removed."
                ((eq cmd ?I)
                 (push (car id) install-list))))
        (forward-line)))
+    (when install-list
+      (if (yes-or-no-p
+          (if (= (length install-list) 1)
+              (format "Install package `%s'? " (car install-list))
+            (format "Install these %d packages (%s)? "
+                    (length install-list)
+                    (mapconcat 'symbol-name install-list ", "))))
+         (mapc 'package-install install-list)))
     ;; Delete packages, prompting if necessary.
     (when delete-list
       (if (yes-or-no-p
@@ -1514,24 +1618,16 @@ packages marked for deletion are removed."
                                delete-list
                                ", "))))
          (dolist (elt delete-list)
-           (condition-case-no-debug err
+           (condition-case-unless-debug err
                (package-delete (car elt) (cdr elt))
              (error (message (cadr err)))))
        (error "Aborted")))
-    (when install-list
-      (if (yes-or-no-p
-          (if (= (length install-list) 1)
-              (format "Install package `%s'? " (car install-list))
-            (format "Install these %d packages (%s)? "
-                    (length install-list)
-                    (mapconcat 'symbol-name install-list ", "))))
-         (mapc 'package-install install-list)))
     ;; If we deleted anything, regenerate `package-alist'.  This is done
     ;; automatically if we installed a package.
     (and delete-list (null install-list)
         (package-initialize))
     (if (or delete-list install-list)
-       (package-menu--generate t)
+       (package-menu--generate t t)
       (message "No operations specified."))))
 
 (defun package-menu--version-predicate (A B)
@@ -1585,14 +1681,34 @@ The list is displayed in a buffer named `*Packages*'."
   (let ((buf (get-buffer-create "*Packages*")))
     (with-current-buffer buf
       (package-menu-mode)
-      (package-menu--generate))
+      (package-menu--generate nil t))
     ;; The package menu buffer has keybindings.  If the user types
     ;; `M-x list-packages', that suggests it should become current.
-    (switch-to-buffer buf)))
+    (switch-to-buffer buf))
+  (let ((upgrades (package-menu--find-upgrades)))
+    (if upgrades
+       (message "%d package%s can be upgraded; type `%s' to mark %s for upgrading."
+                (length upgrades)
+                (if (= (length upgrades) 1) "" "s")
+                (substitute-command-keys "\\[package-menu-mark-upgrades]")
+                (if (= (length upgrades) 1) "it" "them")))))
 
 ;;;###autoload
 (defalias 'package-list-packages 'list-packages)
 
+;; Used in finder.el
+(defun package-show-package-list (packages)
+  "Display PACKAGES in a *Packages* buffer.
+This is similar to `list-packages', but it does not fetch the
+updated list of packages, and it only displays packages with
+names in PACKAGES (which should be a list of symbols)."
+  (require 'finder-inf nil t)
+  (let ((buf (get-buffer-create "*Packages*")))
+    (with-current-buffer buf
+      (package-menu-mode)
+      (package-menu--generate nil packages))
+    (switch-to-buffer buf)))
+
 (defun package-list-packages-no-fetch ()
   "Display a list of packages.
 Does not fetch the updated list of packages before displaying.