]> git.eshelyaron.com Git - emacs.git/commitdiff
Move package-x.el to lisp/obsolete
authorStefan Kangas <stefankangas@gmail.com>
Wed, 12 Feb 2025 16:32:33 +0000 (17:32 +0100)
committerEshel Yaron <me@eshelyaron.com>
Thu, 13 Feb 2025 11:51:11 +0000 (12:51 +0100)
* lisp/emacs-lisp/package-x.el: Move from here...
* lisp/obsolete/package-x.el: ...to here.  (Bug#76194)

(cherry picked from commit f7867e685659ede4fafa8691381cfe08becc6ad6)

lisp/emacs-lisp/package-x.el [deleted file]
lisp/obsolete/package-x.el [new file with mode: 0644]

diff --git a/lisp/emacs-lisp/package-x.el b/lisp/emacs-lisp/package-x.el
deleted file mode 100644 (file)
index 68a62df..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-;;; package-x.el --- Package extras  -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2007-2025 Free Software Foundation, Inc.
-
-;; Author: Tom Tromey <tromey@redhat.com>
-;; Created: 10 Mar 2007
-;; Keywords: tools
-;; Package: package
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file currently contains parts of the package system that many
-;; won't need, such as package uploading.
-
-;; To upload to an archive, first set `package-archive-upload-base' to
-;; some desired directory.  For testing purposes, you can specify any
-;; directory you want, but if you want the archive to be accessible to
-;; others via http, this is typically a directory in the /var/www tree
-;; (possibly one on a remote machine, accessed via Tramp).
-
-;; Then call M-x package-upload-file, which prompts for a file to
-;; upload.  Alternatively, M-x package-upload-buffer uploads the
-;; current buffer, if it's visiting a package file.
-
-;; Once a package is uploaded, users can access it via the Package
-;; Menu, by adding the archive to `package-archives'.
-
-;;; Code:
-
-(require 'package)
-(defvar gnus-article-buffer)
-
-(defcustom package-archive-upload-base "/path/to/archive"
-  "The base location of the archive to which packages are uploaded.
-The commands in the package-x library will use this as base
-location.
-This should be an absolute directory name.  If the archive is on
-another machine, you may specify a remote name in the usual way,
-e.g. \"/ssh:foo@example.com:/var/www/packages/\".
-See Info node `(emacs)Remote Files'.
-
-Unlike `package-archives', you can't specify a HTTP URL."
-  :type 'directory
-  :group 'package
-  :version "24.1")
-
-(defvar package-update-news-on-upload nil
-  "Whether uploading a package should also update NEWS and RSS feeds.")
-
-(defun package--encode (string)
-  "Encode a string by replacing some characters with XML entities."
-  ;; We need a special case for translating "&" to "&amp;".
-  (let ((index))
-    (while (setq index (string-match "[&]" string index))
-      (setq string (replace-match "&amp;" t nil string))
-      (setq index (1+ index))))
-  (while (string-match "[<]" string)
-    (setq string (replace-match "&lt;" t nil string)))
-  (while (string-match "[>]" string)
-    (setq string (replace-match "&gt;" t nil string)))
-  (while (string-match "[']" string)
-    (setq string (replace-match "&apos;" t nil string)))
-  (while (string-match "[\"]" string)
-    (setq string (replace-match "&quot;" t nil string)))
-  string)
-
-(defun package--make-rss-entry (title text archive-url)
-  (let ((date-string (format-time-string "%a, %d %B %Y %T %z")))
-    (concat "<item>\n"
-           "<title>" (package--encode title) "</title>\n"
-           ;; FIXME: should have a link in the web page.
-           "<link>" archive-url "news.html</link>\n"
-           "<description>" (package--encode text) "</description>\n"
-           "<pubDate>" date-string "</pubDate>\n"
-           "</item>\n")))
-
-(defun package--make-html-entry (title text)
-  (concat "<li> " (format-time-string "%B %e") " - "
-         title " - " (package--encode text)
-         " </li>\n"))
-
-(defun package--update-file (file tag text)
-  "Update the package archive file named FILE.
-FILE should be relative to `package-archive-upload-base'.
-TAG is a string that can be found within the file; TEXT is
-inserted after its first occurrence in the file."
-  (setq file (expand-file-name file package-archive-upload-base))
-  (save-excursion
-    (let ((old-buffer (find-buffer-visiting file)))
-      (with-current-buffer (let ((find-file-visit-truename t))
-                            (or old-buffer (find-file-noselect file)))
-       (goto-char (point-min))
-       (search-forward tag)
-       (forward-line)
-       (insert text)
-       (let ((file-precious-flag t))
-         (save-buffer))
-       (unless old-buffer
-         (kill-buffer (current-buffer)))))))
-
-(defun package--archive-contents-from-url (archive-url)
-  "Parse archive-contents file at ARCHIVE-URL.
-Return the file contents, as a string, or nil if unsuccessful."
-  (when archive-url
-    (with-temp-buffer
-      (ignore-errors
-       (url-insert-file-contents (concat archive-url "archive-contents"))
-       (package-read-from-string
-        (buffer-substring-no-properties (point-min) (point-max)))))))
-
-(defun package--archive-contents-from-file ()
-  "Parse the archive-contents at `package-archive-upload-base'."
-  (let ((file (expand-file-name "archive-contents"
-                               package-archive-upload-base)))
-    (if (not (file-exists-p file))
-       ;; No existing archive-contents means a new archive.
-       (list package-archive-version)
-      (let ((dont-kill (find-buffer-visiting file)))
-       (with-current-buffer (let ((find-file-visit-truename t))
-                              (find-file-noselect file))
-         (prog1
-             (package-read-from-string
-              (buffer-substring-no-properties (point-min) (point-max)))
-           (unless dont-kill
-             (kill-buffer (current-buffer)))))))))
-
-(defun package-maint-add-news-item (title description archive-url)
-  "Add a news item to the webpages associated with the package archive.
-TITLE is the title of the news item.
-DESCRIPTION is the text of the news item."
-  (interactive "sTitle: \nsText: ")
-  (package--update-file "elpa.rss"
-                       "<description>"
-                       (package--make-rss-entry title description archive-url))
-  (package--update-file "news.html"
-                       "New entries go here"
-                       (package--make-html-entry title description)))
-
-(defun package--update-news (package version description archive-url)
-  "Update the ELPA web pages when a package is uploaded."
-  (package-maint-add-news-item (concat package " version " version)
-                              description
-                              archive-url))
-
-(declare-function lm-commentary "lisp-mnt" (&optional file))
-(defvar tar-data-buffer)
-
-(defun package-upload-buffer-internal (pkg-desc extension &optional archive-url)
-  "Upload a package whose contents are in the current buffer.
-PKG-DESC is the `package-desc'.
-EXTENSION is the file extension, a string.  It can be either
-\"el\" or \"tar\".
-
-The upload destination is given by `package-archive-upload-base'.
-If its value is invalid, prompt for a directory.
-
-Optional arg ARCHIVE-URL is the URL of the destination archive.
-If it is non-nil, compute the new \"archive-contents\" file
-starting from the existing \"archive-contents\" at that URL.  In
-addition, if `package-update-news-on-upload' is non-nil, call
-`package--update-news' to add a news item at that URL.
-
-If ARCHIVE-URL is nil, compute the new \"archive-contents\" file
-from the \"archive-contents\" at `package-archive-upload-base',
-if it exists."
-  (let ((package-archive-upload-base package-archive-upload-base))
-    ;; Check if `package-archive-upload-base' is valid.
-    (when (or (not (stringp package-archive-upload-base))
-             (equal package-archive-upload-base
-                    (custom--standard-value 'package-archive-upload-base)))
-      (setq package-archive-upload-base
-           (read-directory-name
-            "Base directory for package archive: ")))
-    (unless (file-directory-p package-archive-upload-base)
-      (if (y-or-n-p (format "%s does not exist; create it? "
-                           package-archive-upload-base))
-         (make-directory package-archive-upload-base t)
-       (error "Aborted")))
-    (save-excursion
-      (save-restriction
-       (let* ((file-type (package-desc-kind pkg-desc))
-              (pkg-name (package-desc-name pkg-desc))
-              (requires (package-desc-reqs pkg-desc))
-              (desc (if (eq (package-desc-summary pkg-desc)
-                             package--default-summary)
-                        (read-string "Description of package: ")
-                      (package-desc-summary pkg-desc)))
-              (split-version (package-desc-version pkg-desc))
-              (commentary
-                (pcase file-type
-                  ('single (lm-commentary))
-                  ('tar nil))) ;; FIXME: Get it from the README file.
-               (extras (package-desc-extras pkg-desc))
-              (pkg-version (package-version-join split-version))
-              (pkg-buffer (current-buffer)))
-
-          ;; `package-upload-file' will error if given a directory,
-          ;; but we check it here as well just in case.
-          (when (eq 'dir file-type)
-            (user-error "Can't upload directory, tar it instead"))
-         ;; Get archive-contents from ARCHIVE-URL if it's non-nil, or
-         ;; from `package-archive-upload-base' otherwise.
-         (let ((contents (or (package--archive-contents-from-url archive-url)
-                             (package--archive-contents-from-file)))
-               (new-desc (package-make-ac-desc
-                           split-version requires desc file-type extras)))
-           (if (> (car contents) package-archive-version)
-               (error "Unrecognized archive version %d" (car contents)))
-           (let ((elt (assq pkg-name (cdr contents))))
-             (if elt
-                 (if (version-list-<= split-version
-                                      (package--ac-desc-version (cdr elt)))
-                     (error "New package has smaller version: %s" pkg-version)
-                   (setcdr elt new-desc))
-               (setq contents (cons (car contents)
-                                    (cons (cons pkg-name new-desc)
-                                          (cdr contents))))))
-
-           ;; Now CONTENTS is the updated archive contents.  Upload
-           ;; this and the package itself.  For now we assume ELPA is
-           ;; writable via file primitives.
-           (let ((print-level nil)
-                  (print-quoted t)
-                 (print-length nil))
-             (write-region (concat (pp-to-string contents) "\n")
-                           nil
-                           (expand-file-name "archive-contents"
-                                             package-archive-upload-base)))
-
-           ;; If there is a commentary section, write it.
-           (when commentary
-             (write-region commentary nil
-                            (expand-file-name
-                             (concat (symbol-name pkg-name) "-readme.txt")
-                             package-archive-upload-base)))
-
-           (set-buffer (if (eq file-type 'tar) tar-data-buffer pkg-buffer))
-           (write-region (point-min) (point-max)
-                         (expand-file-name
-                          (format "%s-%s.%s" pkg-name pkg-version extension)
-                          package-archive-upload-base)
-                         nil nil nil 'excl)
-
-           ;; Write a news entry.
-           (and package-update-news-on-upload
-                archive-url
-                (package--update-news (format "%s.%s" pkg-name extension)
-                                      pkg-version desc archive-url))
-
-           ;; special-case "package": write a second copy so that the
-           ;; installer can easily find the latest version.
-           (if (eq pkg-name 'package)
-               (write-region (point-min) (point-max)
-                             (expand-file-name
-                              (format "%s.%s" pkg-name extension)
-                              package-archive-upload-base)
-                             nil nil nil 'ask))))))))
-
-(defun package-upload-buffer ()
-  "Upload the current buffer as a single-file Emacs Lisp package.
-If `package-archive-upload-base' does not specify a valid upload
-destination, prompt for one.
-Signal an error if the current buffer is not visiting a simple
-package (a \".el\" file)."
-  (interactive)
-  (save-excursion
-    (save-restriction
-      ;; Find the package in this buffer.
-      (let ((pkg-desc (package-buffer-info)))
-       (package-upload-buffer-internal pkg-desc "el")))))
-
-;;;###autoload
-(defun package-upload-file (file)
-  "Upload the Emacs Lisp package FILE to the package archive.
-Interactively, prompt for FILE.  The package is considered a
-single-file package if FILE ends in \".el\", and a multi-file
-package if FILE ends in \".tar\".
-Automatically extract package attributes and update the archive's
-contents list with this information.
-If `package-archive-upload-base' does not specify a valid upload
-destination, prompt for one.  If the directory does not exist, it
-is created.  The directory need not have any initial contents
-\(i.e., you can use this command to populate an initially empty
-archive)."
-  (interactive "fPackage file name: ")
-  (with-temp-buffer
-    (insert-file-contents file)
-    (let ((pkg-desc
-           (cond
-            ((string-match "\\.tar\\'" file)
-             (tar-mode) (package-tar-file-info))
-            ((string-match "\\.el\\'" file) (package-buffer-info))
-            (t (error "Unrecognized extension `%s'"
-                      (file-name-extension file))))))
-      (package-upload-buffer-internal pkg-desc (file-name-extension file)))))
-
-(defun package-gnus-summary-upload ()
-  "Upload a package contained in the current *Article* buffer.
-This should be invoked from the gnus *Summary* buffer."
-  (interactive)
-  (with-current-buffer gnus-article-buffer
-    (package-upload-buffer)))
-
-(provide 'package-x)
-
-;;; package-x.el ends here
diff --git a/lisp/obsolete/package-x.el b/lisp/obsolete/package-x.el
new file mode 100644 (file)
index 0000000..68a62df
--- /dev/null
@@ -0,0 +1,321 @@
+;;; package-x.el --- Package extras  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2007-2025 Free Software Foundation, Inc.
+
+;; Author: Tom Tromey <tromey@redhat.com>
+;; Created: 10 Mar 2007
+;; Keywords: tools
+;; Package: package
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file currently contains parts of the package system that many
+;; won't need, such as package uploading.
+
+;; To upload to an archive, first set `package-archive-upload-base' to
+;; some desired directory.  For testing purposes, you can specify any
+;; directory you want, but if you want the archive to be accessible to
+;; others via http, this is typically a directory in the /var/www tree
+;; (possibly one on a remote machine, accessed via Tramp).
+
+;; Then call M-x package-upload-file, which prompts for a file to
+;; upload.  Alternatively, M-x package-upload-buffer uploads the
+;; current buffer, if it's visiting a package file.
+
+;; Once a package is uploaded, users can access it via the Package
+;; Menu, by adding the archive to `package-archives'.
+
+;;; Code:
+
+(require 'package)
+(defvar gnus-article-buffer)
+
+(defcustom package-archive-upload-base "/path/to/archive"
+  "The base location of the archive to which packages are uploaded.
+The commands in the package-x library will use this as base
+location.
+This should be an absolute directory name.  If the archive is on
+another machine, you may specify a remote name in the usual way,
+e.g. \"/ssh:foo@example.com:/var/www/packages/\".
+See Info node `(emacs)Remote Files'.
+
+Unlike `package-archives', you can't specify a HTTP URL."
+  :type 'directory
+  :group 'package
+  :version "24.1")
+
+(defvar package-update-news-on-upload nil
+  "Whether uploading a package should also update NEWS and RSS feeds.")
+
+(defun package--encode (string)
+  "Encode a string by replacing some characters with XML entities."
+  ;; We need a special case for translating "&" to "&amp;".
+  (let ((index))
+    (while (setq index (string-match "[&]" string index))
+      (setq string (replace-match "&amp;" t nil string))
+      (setq index (1+ index))))
+  (while (string-match "[<]" string)
+    (setq string (replace-match "&lt;" t nil string)))
+  (while (string-match "[>]" string)
+    (setq string (replace-match "&gt;" t nil string)))
+  (while (string-match "[']" string)
+    (setq string (replace-match "&apos;" t nil string)))
+  (while (string-match "[\"]" string)
+    (setq string (replace-match "&quot;" t nil string)))
+  string)
+
+(defun package--make-rss-entry (title text archive-url)
+  (let ((date-string (format-time-string "%a, %d %B %Y %T %z")))
+    (concat "<item>\n"
+           "<title>" (package--encode title) "</title>\n"
+           ;; FIXME: should have a link in the web page.
+           "<link>" archive-url "news.html</link>\n"
+           "<description>" (package--encode text) "</description>\n"
+           "<pubDate>" date-string "</pubDate>\n"
+           "</item>\n")))
+
+(defun package--make-html-entry (title text)
+  (concat "<li> " (format-time-string "%B %e") " - "
+         title " - " (package--encode text)
+         " </li>\n"))
+
+(defun package--update-file (file tag text)
+  "Update the package archive file named FILE.
+FILE should be relative to `package-archive-upload-base'.
+TAG is a string that can be found within the file; TEXT is
+inserted after its first occurrence in the file."
+  (setq file (expand-file-name file package-archive-upload-base))
+  (save-excursion
+    (let ((old-buffer (find-buffer-visiting file)))
+      (with-current-buffer (let ((find-file-visit-truename t))
+                            (or old-buffer (find-file-noselect file)))
+       (goto-char (point-min))
+       (search-forward tag)
+       (forward-line)
+       (insert text)
+       (let ((file-precious-flag t))
+         (save-buffer))
+       (unless old-buffer
+         (kill-buffer (current-buffer)))))))
+
+(defun package--archive-contents-from-url (archive-url)
+  "Parse archive-contents file at ARCHIVE-URL.
+Return the file contents, as a string, or nil if unsuccessful."
+  (when archive-url
+    (with-temp-buffer
+      (ignore-errors
+       (url-insert-file-contents (concat archive-url "archive-contents"))
+       (package-read-from-string
+        (buffer-substring-no-properties (point-min) (point-max)))))))
+
+(defun package--archive-contents-from-file ()
+  "Parse the archive-contents at `package-archive-upload-base'."
+  (let ((file (expand-file-name "archive-contents"
+                               package-archive-upload-base)))
+    (if (not (file-exists-p file))
+       ;; No existing archive-contents means a new archive.
+       (list package-archive-version)
+      (let ((dont-kill (find-buffer-visiting file)))
+       (with-current-buffer (let ((find-file-visit-truename t))
+                              (find-file-noselect file))
+         (prog1
+             (package-read-from-string
+              (buffer-substring-no-properties (point-min) (point-max)))
+           (unless dont-kill
+             (kill-buffer (current-buffer)))))))))
+
+(defun package-maint-add-news-item (title description archive-url)
+  "Add a news item to the webpages associated with the package archive.
+TITLE is the title of the news item.
+DESCRIPTION is the text of the news item."
+  (interactive "sTitle: \nsText: ")
+  (package--update-file "elpa.rss"
+                       "<description>"
+                       (package--make-rss-entry title description archive-url))
+  (package--update-file "news.html"
+                       "New entries go here"
+                       (package--make-html-entry title description)))
+
+(defun package--update-news (package version description archive-url)
+  "Update the ELPA web pages when a package is uploaded."
+  (package-maint-add-news-item (concat package " version " version)
+                              description
+                              archive-url))
+
+(declare-function lm-commentary "lisp-mnt" (&optional file))
+(defvar tar-data-buffer)
+
+(defun package-upload-buffer-internal (pkg-desc extension &optional archive-url)
+  "Upload a package whose contents are in the current buffer.
+PKG-DESC is the `package-desc'.
+EXTENSION is the file extension, a string.  It can be either
+\"el\" or \"tar\".
+
+The upload destination is given by `package-archive-upload-base'.
+If its value is invalid, prompt for a directory.
+
+Optional arg ARCHIVE-URL is the URL of the destination archive.
+If it is non-nil, compute the new \"archive-contents\" file
+starting from the existing \"archive-contents\" at that URL.  In
+addition, if `package-update-news-on-upload' is non-nil, call
+`package--update-news' to add a news item at that URL.
+
+If ARCHIVE-URL is nil, compute the new \"archive-contents\" file
+from the \"archive-contents\" at `package-archive-upload-base',
+if it exists."
+  (let ((package-archive-upload-base package-archive-upload-base))
+    ;; Check if `package-archive-upload-base' is valid.
+    (when (or (not (stringp package-archive-upload-base))
+             (equal package-archive-upload-base
+                    (custom--standard-value 'package-archive-upload-base)))
+      (setq package-archive-upload-base
+           (read-directory-name
+            "Base directory for package archive: ")))
+    (unless (file-directory-p package-archive-upload-base)
+      (if (y-or-n-p (format "%s does not exist; create it? "
+                           package-archive-upload-base))
+         (make-directory package-archive-upload-base t)
+       (error "Aborted")))
+    (save-excursion
+      (save-restriction
+       (let* ((file-type (package-desc-kind pkg-desc))
+              (pkg-name (package-desc-name pkg-desc))
+              (requires (package-desc-reqs pkg-desc))
+              (desc (if (eq (package-desc-summary pkg-desc)
+                             package--default-summary)
+                        (read-string "Description of package: ")
+                      (package-desc-summary pkg-desc)))
+              (split-version (package-desc-version pkg-desc))
+              (commentary
+                (pcase file-type
+                  ('single (lm-commentary))
+                  ('tar nil))) ;; FIXME: Get it from the README file.
+               (extras (package-desc-extras pkg-desc))
+              (pkg-version (package-version-join split-version))
+              (pkg-buffer (current-buffer)))
+
+          ;; `package-upload-file' will error if given a directory,
+          ;; but we check it here as well just in case.
+          (when (eq 'dir file-type)
+            (user-error "Can't upload directory, tar it instead"))
+         ;; Get archive-contents from ARCHIVE-URL if it's non-nil, or
+         ;; from `package-archive-upload-base' otherwise.
+         (let ((contents (or (package--archive-contents-from-url archive-url)
+                             (package--archive-contents-from-file)))
+               (new-desc (package-make-ac-desc
+                           split-version requires desc file-type extras)))
+           (if (> (car contents) package-archive-version)
+               (error "Unrecognized archive version %d" (car contents)))
+           (let ((elt (assq pkg-name (cdr contents))))
+             (if elt
+                 (if (version-list-<= split-version
+                                      (package--ac-desc-version (cdr elt)))
+                     (error "New package has smaller version: %s" pkg-version)
+                   (setcdr elt new-desc))
+               (setq contents (cons (car contents)
+                                    (cons (cons pkg-name new-desc)
+                                          (cdr contents))))))
+
+           ;; Now CONTENTS is the updated archive contents.  Upload
+           ;; this and the package itself.  For now we assume ELPA is
+           ;; writable via file primitives.
+           (let ((print-level nil)
+                  (print-quoted t)
+                 (print-length nil))
+             (write-region (concat (pp-to-string contents) "\n")
+                           nil
+                           (expand-file-name "archive-contents"
+                                             package-archive-upload-base)))
+
+           ;; If there is a commentary section, write it.
+           (when commentary
+             (write-region commentary nil
+                            (expand-file-name
+                             (concat (symbol-name pkg-name) "-readme.txt")
+                             package-archive-upload-base)))
+
+           (set-buffer (if (eq file-type 'tar) tar-data-buffer pkg-buffer))
+           (write-region (point-min) (point-max)
+                         (expand-file-name
+                          (format "%s-%s.%s" pkg-name pkg-version extension)
+                          package-archive-upload-base)
+                         nil nil nil 'excl)
+
+           ;; Write a news entry.
+           (and package-update-news-on-upload
+                archive-url
+                (package--update-news (format "%s.%s" pkg-name extension)
+                                      pkg-version desc archive-url))
+
+           ;; special-case "package": write a second copy so that the
+           ;; installer can easily find the latest version.
+           (if (eq pkg-name 'package)
+               (write-region (point-min) (point-max)
+                             (expand-file-name
+                              (format "%s.%s" pkg-name extension)
+                              package-archive-upload-base)
+                             nil nil nil 'ask))))))))
+
+(defun package-upload-buffer ()
+  "Upload the current buffer as a single-file Emacs Lisp package.
+If `package-archive-upload-base' does not specify a valid upload
+destination, prompt for one.
+Signal an error if the current buffer is not visiting a simple
+package (a \".el\" file)."
+  (interactive)
+  (save-excursion
+    (save-restriction
+      ;; Find the package in this buffer.
+      (let ((pkg-desc (package-buffer-info)))
+       (package-upload-buffer-internal pkg-desc "el")))))
+
+;;;###autoload
+(defun package-upload-file (file)
+  "Upload the Emacs Lisp package FILE to the package archive.
+Interactively, prompt for FILE.  The package is considered a
+single-file package if FILE ends in \".el\", and a multi-file
+package if FILE ends in \".tar\".
+Automatically extract package attributes and update the archive's
+contents list with this information.
+If `package-archive-upload-base' does not specify a valid upload
+destination, prompt for one.  If the directory does not exist, it
+is created.  The directory need not have any initial contents
+\(i.e., you can use this command to populate an initially empty
+archive)."
+  (interactive "fPackage file name: ")
+  (with-temp-buffer
+    (insert-file-contents file)
+    (let ((pkg-desc
+           (cond
+            ((string-match "\\.tar\\'" file)
+             (tar-mode) (package-tar-file-info))
+            ((string-match "\\.el\\'" file) (package-buffer-info))
+            (t (error "Unrecognized extension `%s'"
+                      (file-name-extension file))))))
+      (package-upload-buffer-internal pkg-desc (file-name-extension file)))))
+
+(defun package-gnus-summary-upload ()
+  "Upload a package contained in the current *Article* buffer.
+This should be invoked from the gnus *Summary* buffer."
+  (interactive)
+  (with-current-buffer gnus-article-buffer
+    (package-upload-buffer)))
+
+(provide 'package-x)
+
+;;; package-x.el ends here