summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/battery.el675
-rw-r--r--lisp/cedet/semantic/complete.el8
-rw-r--r--lisp/dired-aux.el16
-rw-r--r--lisp/emacs-lisp/benchmark.el2
-rw-r--r--lisp/emacs-lisp/generator.el8
-rw-r--r--lisp/emacs-lisp/gv.el2
-rw-r--r--lisp/emacs-lisp/package.el12
-rw-r--r--lisp/emacs-lisp/rx.el2
-rw-r--r--lisp/emulation/cua-base.el2
-rw-r--r--lisp/emulation/cua-rect.el6
-rw-r--r--lisp/erc/erc-match.el19
-rw-r--r--lisp/erc/erc.el21
-rw-r--r--lisp/eshell/em-pred.el8
-rw-r--r--lisp/faces.el35
-rw-r--r--lisp/format-spec.el183
-rw-r--r--lisp/gnus/gnus-cloud.el54
-rw-r--r--lisp/gnus/gnus-eform.el18
-rw-r--r--lisp/gnus/gnus-sieve.el10
-rw-r--r--lisp/gnus/gssapi.el11
-rw-r--r--lisp/gnus/mail-source.el30
-rw-r--r--lisp/gnus/message.el137
-rw-r--r--lisp/icomplete.el5
-rw-r--r--lisp/image-dired.el1
-rw-r--r--lisp/image-mode.el32
-rw-r--r--lisp/net/dbus.el499
-rw-r--r--lisp/net/eww.el18
-rw-r--r--lisp/net/imap.el30
-rw-r--r--lisp/net/network-stream.el13
-rw-r--r--lisp/net/shr.el25
-rw-r--r--lisp/net/tramp-adb.el102
-rw-r--r--lisp/net/tramp-archive.el4
-rw-r--r--lisp/net/tramp-compat.el17
-rw-r--r--lisp/net/tramp-crypt.el206
-rw-r--r--lisp/net/tramp-gvfs.el74
-rw-r--r--lisp/net/tramp-rclone.el7
-rw-r--r--lisp/net/tramp-sh.el180
-rw-r--r--lisp/net/tramp-smb.el13
-rw-r--r--lisp/net/tramp-sudoedit.el37
-rw-r--r--lisp/net/tramp.el105
-rw-r--r--lisp/obsolete/tls.el16
-rw-r--r--lisp/progmodes/bug-reference.el221
-rw-r--r--lisp/progmodes/project.el204
-rw-r--r--lisp/progmodes/python.el4
-rw-r--r--lisp/progmodes/sql.el2
-rw-r--r--lisp/progmodes/verilog-mode.el10
-rw-r--r--lisp/progmodes/xref.el6
-rw-r--r--lisp/simple.el16
-rw-r--r--lisp/tab-bar.el15
-rw-r--r--lisp/tab-line.el16
-rw-r--r--lisp/term/tty-colors.el58
-rw-r--r--lisp/textmodes/bibtex.el10
-rw-r--r--lisp/textmodes/tex-mode.el3
-rw-r--r--lisp/vc/vc-bzr.el9
-rw-r--r--lisp/vc/vc-git.el29
-rw-r--r--lisp/vc/vc-hg.el8
-rw-r--r--lisp/vc/vc-svn.el9
-rw-r--r--lisp/vc/vc.el17
57 files changed, 1774 insertions, 1506 deletions
diff --git a/lisp/battery.el b/lisp/battery.el
index b8855a8ce37..e568ab52460 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -23,18 +23,18 @@
;;; Commentary:
-;; There is at present support for GNU/Linux, macOS, and Windows.
+;; There is at present support for GNU/Linux, BSD, macOS, and Windows.
;; This library supports:
;; - UPower (https://upower.freedesktop.org) via D-Bus API.
-;; - the `/sys/class/power_supply/' files of Linux >= 2.6.39.
-;; - the `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
-;; - the `/proc/apm' file format of Linux version 1.3.58 or newer.
+;; - The `/sys/class/power_supply/' files of Linux >= 2.6.39.
+;; - The `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
+;; - The `/proc/apm' file format of Linux version 1.3.58 or newer.
+;; - BSD by using the `apm' program.
;; - Darwin (macOS) by using the `pmset' program.
;; - Windows via the GetSystemPowerStatus API call.
;;; Code:
-(require 'timer)
(require 'dbus)
(eval-when-compile (require 'cl-lib))
@@ -44,53 +44,74 @@
:group 'hardware)
(defcustom battery-upower-device nil
- "UPower device of the `:battery' type.
-Use `battery-upower-device-list' to list all available UPower devices.
-If set to nil, then autodetect `:battery' device."
+ "Preferred UPower device name(s).
+When `battery-status-function' is set to `battery-upower', this
+user option specifies which power sources to query for status
+information and merge into a single report.
+
+When nil (the default), `battery-upower' queries all present
+battery and line power devices as determined by the UPower
+EnumerateDevices method. A string or a nonempty list of strings
+names particular devices to query instead. UPower battery and
+line power device names typically follow the patterns
+\"battery_BATN\" and \"line_power_ACN\", respectively, with N
+starting at 0 when present. Device names should not include the
+leading D-Bus path \"/org/freedesktop/UPower/devices/\"."
:version "28.1"
- :type '(choice string (const :tag "Autodetect" nil)))
-
-(defcustom battery-upower-line-power-device nil
- "UPower device of the `:line-power' type.
-Use `battery-upower-device-list' to list all available UPower devices.
-If set to nil, then autodetect `:battery' device."
+ :type '(choice (const :tag "Autodetect all devices" nil)
+ (string :tag "Device")
+ (repeat :tag "Devices" string)))
+
+(defcustom battery-upower-subscribe t
+ "Whether to subscribe to UPower device change signals.
+When nil, battery status information is polled every
+`battery-update-interval' seconds. When non-nil (the default),
+the battery status is also updated whenever a power source is
+added or removed, or when the system starts or stops running on
+battery power.
+
+This only takes effect when `battery-status-function' is set to
+`battery-upower' before enabling `display-battery-mode'."
:version "28.1"
- :type '(choice string (const :tag "Autodetect" nil)))
+ :type 'boolean)
+
+(defconst battery-upower-service "org.freedesktop.UPower"
+ "Well-known name of the UPower D-Bus service.
+See URL `https://upower.freedesktop.org/docs/ref-dbus.html'.")
-(defconst battery-upower-dbus-service "org.freedesktop.UPower"
- "Well-known UPower service name for the D-Bus system.")
+(defun battery--files (dir)
+ "Return a list of absolute file names in DIR or nil on error.
+Value does not include \".\" or \"..\"."
+ (ignore-errors (directory-files dir t directory-files-no-dot-files-regexp)))
(defun battery--find-linux-sysfs-batteries ()
- (let ((dirs nil))
- (dolist (file (directory-files "/sys/class/power_supply/" t))
- (when (and (or (file-directory-p file)
- (file-symlink-p file))
- (file-exists-p (expand-file-name "capacity" file)))
- (push file dirs)))
+ "Return a list of all sysfs battery directories."
+ (let (dirs)
+ (dolist (dir (battery--files "/sys/class/power_supply/"))
+ (when (file-exists-p (expand-file-name "capacity" dir))
+ (push dir dirs)))
(nreverse dirs)))
(defcustom battery-status-function
- (cond ((dbus-ping :system battery-upower-dbus-service)
+ (cond ((member battery-upower-service (dbus-list-activatable-names))
#'battery-upower)
((and (eq system-type 'gnu/linux)
- (file-readable-p "/proc/apm"))
- #'battery-linux-proc-apm)
+ (battery--find-linux-sysfs-batteries))
+ #'battery-linux-sysfs)
((and (eq system-type 'gnu/linux)
(file-directory-p "/proc/acpi/battery"))
#'battery-linux-proc-acpi)
((and (eq system-type 'gnu/linux)
- (file-directory-p "/sys/class/power_supply/")
- (battery--find-linux-sysfs-batteries))
- #'battery-linux-sysfs)
+ (file-readable-p "/proc/apm"))
+ #'battery-linux-proc-apm)
((and (eq system-type 'berkeley-unix)
(file-executable-p "/usr/sbin/apm"))
#'battery-bsd-apm)
((and (eq system-type 'darwin)
- (condition-case nil
- (with-temp-buffer
- (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
- (> (buffer-size) 0)))
- (error nil)))
+ (ignore-errors
+ (with-temp-buffer
+ (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
+ (not (bobp))))))
#'battery-pmset)
((fboundp 'w32-battery-status)
#'w32-battery-status))
@@ -102,6 +123,7 @@ Its cons cells are of the form
CONVERSION is the character code of a \"conversion specification\"
introduced by a `%' character in a control string."
+ :version "28.1"
:type '(choice (const nil) function))
(defcustom battery-echo-area-format
@@ -113,15 +135,19 @@ string are substituted as defined by the current value of the variable
`battery-status-function'. Here are the ones generally available:
%c Current capacity (mAh or mWh)
%r Current rate of charge or discharge
+%L AC line status (verbose)
%B Battery status (verbose)
%b Battery status: empty means high, `-' means low,
`!' means critical, and `+' means charging
%d Temperature (in degrees Celsius)
-%L AC line status (verbose)
%p Battery load percentage
+%s Remaining time (to charge or discharge) in seconds
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
-%t Remaining time (to charge or discharge) in the form `h:min'"
+%t Remaining time (to charge or discharge) in the form `h:min'
+
+The full `format-spec' formatting syntax is supported."
+ :link '(info-link "(elisp) Custom Format Strings")
:type '(choice string (const nil)))
(defvar battery-mode-line-string nil
@@ -134,7 +160,7 @@ string are substituted as defined by the current value of the variable
:type 'integer)
(defcustom battery-mode-line-format
- (cond ((eq battery-status-function 'battery-linux-proc-acpi)
+ (cond ((eq battery-status-function #'battery-linux-proc-acpi)
"[%b%p%%,%d°C]")
(battery-status-function
"[%b%p%%]"))
@@ -145,15 +171,19 @@ string are substituted as defined by the current value of the variable
`battery-status-function'. Here are the ones generally available:
%c Current capacity (mAh or mWh)
%r Current rate of charge or discharge
+%L AC line status (verbose)
%B Battery status (verbose)
%b Battery status: empty means high, `-' means low,
`!' means critical, and `+' means charging
%d Temperature (in degrees Celsius)
-%L AC line status (verbose)
%p Battery load percentage
+%s Remaining time (to charge or discharge) in seconds
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
-%t Remaining time (to charge or discharge) in the form `h:min'"
+%t Remaining time (to charge or discharge) in the form `h:min'
+
+The full `format-spec' formatting syntax is supported."
+ :link '(info-link "(elisp) Custom Format Strings")
:type '(choice string (const nil)))
(defcustom battery-update-interval 60
@@ -170,6 +200,18 @@ A battery load percentage below this number is considered low."
A battery load percentage below this number is considered critical."
:type 'integer)
+(defface battery-load-low
+ '((t :inherit warning))
+ "Face used in mode line string when battery load is low.
+See the option `battery-load-low'."
+ :version "28.1")
+
+(defface battery-load-critical
+ '((t :inherit error))
+ "Face used in mode line string when battery load is critical.
+See the option `battery-load-critical'."
+ :version "28.1")
+
(defvar battery-update-timer nil
"Interval timer object.")
@@ -196,13 +238,17 @@ seconds."
(setq battery-mode-line-string "")
(or global-mode-string (setq global-mode-string '("")))
(and battery-update-timer (cancel-timer battery-update-timer))
+ (battery--upower-unsubscribe)
(if (and battery-status-function battery-mode-line-format)
(if (not display-battery-mode)
(setq global-mode-string
(delq 'battery-mode-line-string global-mode-string))
(add-to-list 'global-mode-string 'battery-mode-line-string t)
+ (and (eq battery-status-function #'battery-upower)
+ battery-upower-subscribe
+ (battery--upower-subsribe))
(setq battery-update-timer (run-at-time nil battery-update-interval
- 'battery-update-handler))
+ #'battery-update-handler))
(battery-update))
(message "Battery status not available")
(setq display-battery-mode nil)))
@@ -214,34 +260,42 @@ seconds."
(defun battery-update ()
"Update battery status information in the mode line."
(let* ((data (and battery-status-function (funcall battery-status-function)))
- (percentage (car (read-from-string (cdr (assq ?p data))))))
- (setq battery-mode-line-string
- (propertize (if (and battery-mode-line-format
- (numberp percentage)
- (<= percentage battery-mode-line-limit))
- (battery-format battery-mode-line-format data)
- "")
- 'face
- (and (numberp percentage)
- (<= percentage battery-load-critical)
- 'error)
- 'help-echo "Battery status information")))
- (force-mode-line-update))
+ (percentage (car (read-from-string (cdr (assq ?p data)))))
+ (res (and battery-mode-line-format
+ (or (not (numberp percentage))
+ (<= percentage battery-mode-line-limit))
+ (battery-format battery-mode-line-format data)))
+ (len (length res)))
+ (unless (zerop len)
+ (cond ((not (numberp percentage)))
+ ((< percentage battery-load-critical)
+ (add-face-text-property 0 len 'battery-load-critical t res))
+ ((< percentage battery-load-low)
+ (add-face-text-property 0 len 'battery-load-low t res)))
+ (put-text-property 0 len 'help-echo "Battery status information" res))
+ (setq battery-mode-line-string (or res "")))
+ (force-mode-line-update t))
+
;;; `/proc/apm' interface for Linux.
-(defconst battery-linux-proc-apm-regexp
- (concat "^\\([^ ]+\\)" ; Driver version.
- " \\([^ ]+\\)" ; APM BIOS version.
- " 0x\\([0-9a-f]+\\)" ; APM BIOS flags.
- " 0x\\([0-9a-f]+\\)" ; AC line status.
- " 0x\\([0-9a-f]+\\)" ; Battery status.
- " 0x\\([0-9a-f]+\\)" ; Battery flags.
- " \\(-?[0-9]+\\)%" ; Load percentage.
- " \\(-?[0-9]+\\)" ; Remaining time.
- " \\(.*\\)" ; Time unit.
- "$")
+;; Regular expression matching contents of `/proc/apm'.
+(rx-define battery--linux-proc-apm
+ (: bol (group (+ (not ?\s))) ; Driver version.
+ " " (group (+ (not ?\s))) ; APM BIOS version.
+ " 0x" (group (+ xdigit)) ; APM BIOS flags.
+ " 0x" (group (+ xdigit)) ; AC line status.
+ " 0x" (group (+ xdigit)) ; Battery status.
+ " 0x" (group (+ xdigit)) ; Battery flags.
+ " " (group (? ?-) (+ digit)) ?% ; Load percentage.
+ " " (group (? ?-) (+ digit)) ; Remaining time.
+ " " (group (* nonl)) ; Time unit
+ eol))
+
+(defconst battery-linux-proc-apm-regexp (rx battery--linux-proc-apm)
"Regular expression matching contents of `/proc/apm'.")
+(make-obsolete-variable 'battery-linux-proc-apm-regexp
+ "it is no longer used." "28.1")
(defun battery-linux-proc-apm ()
"Get APM status information from Linux (the kernel).
@@ -261,12 +315,12 @@ The following %-sequences are provided:
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
%t Remaining time (to charge or discharge) in the form `h:min'"
- (let (driver-version bios-version bios-interface line-status
- battery-status battery-status-symbol load-percentage
- seconds minutes hours remaining-time tem)
+ (let ( driver-version bios-version bios-interface line-status
+ battery-status battery-status-symbol load-percentage
+ seconds minutes hours remaining-time tem )
(with-temp-buffer
(ignore-errors (insert-file-contents "/proc/apm"))
- (when (re-search-forward battery-linux-proc-apm-regexp)
+ (when (re-search-forward (rx battery--linux-proc-apm) nil t)
(setq driver-version (match-string 1))
(setq bios-version (match-string 2))
(setq tem (string-to-number (match-string 3) 16))
@@ -279,9 +333,7 @@ The following %-sequences are provided:
(cond ((= tem 0) (setq line-status "off-line"))
((= tem 1) (setq line-status "on-line"))
((= tem 2) (setq line-status "on backup")))
- (setq tem (string-to-number (match-string 6) 16))
- (if (= tem 255)
- (setq battery-status "N/A")
+ (unless (= (string-to-number (match-string 6) 16) 255)
(setq tem (string-to-number (match-string 5) 16))
(cond ((= tem 0) (setq battery-status "high"
battery-status-symbol ""))
@@ -298,7 +350,7 @@ The following %-sequences are provided:
(setq minutes (/ seconds 60)
hours (/ seconds 3600))
(setq remaining-time
- (format "%d:%02d" hours (- minutes (* 60 hours))))))))
+ (format "%d:%02d" hours (% minutes 60)))))))
(list (cons ?v (or driver-version "N/A"))
(cons ?V (or bios-version "N/A"))
(cons ?I (or bios-interface "N/A"))
@@ -306,27 +358,31 @@ The following %-sequences are provided:
(cons ?B (or battery-status "N/A"))
(cons ?b (or battery-status-symbol ""))
(cons ?p (or load-percentage "N/A"))
- (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
- (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
- (cons ?h (or (and hours (number-to-string hours)) "N/A"))
+ (cons ?s (if seconds (number-to-string seconds) "N/A"))
+ (cons ?m (if minutes (number-to-string minutes) "N/A"))
+ (cons ?h (if hours (number-to-string hours) "N/A"))
(cons ?t (or remaining-time "N/A")))))
;;; `/proc/acpi/' interface for Linux.
+(rx-define battery--acpi-rate (&rest hour)
+ (: (group (+ digit)) " " (group ?m (in "AW") hour)))
+(rx-define battery--acpi-capacity (battery--acpi-rate ?h))
+
(defun battery-linux-proc-acpi ()
"Get ACPI status information from Linux (the kernel).
-This function works only with the `/proc/acpi/' format introduced
-in Linux version 2.4.20 and 2.6.0.
+This function works only with the `/proc/acpi/' interface
+introduced in Linux version 2.4.20 and 2.6.0.
The following %-sequences are provided:
%c Current capacity (mAh)
-%r Current rate
+%r Current rate of charge or discharge
+%L AC line status (verbose)
%B Battery status (verbose)
%b Battery status, empty means high, `-' means low,
`!' means critical, and `+' means charging
%d Temperature (in degrees Celsius)
-%L AC line status (verbose)
%p Battery load percentage
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
@@ -342,45 +398,51 @@ The following %-sequences are provided:
;; information together since displaying for a variable amount of
;; batteries seems overkill for format-strings.
(with-temp-buffer
- (dolist (dir (ignore-errors (directory-files "/proc/acpi/battery/"
- t "\\`[^.]")))
- (erase-buffer)
- (ignore-errors (insert-file-contents (expand-file-name "state" dir)))
- (when (re-search-forward "present: +yes$" nil t)
- (and (re-search-forward "charging state: +\\(.*\\)$" nil t)
+ (dolist (dir (battery--files "/proc/acpi/battery/"))
+ (ignore-errors
+ (insert-file-contents (expand-file-name "state" dir) nil nil nil t))
+ (goto-char (point-min))
+ (when (re-search-forward (rx "present:" (+ space) "yes" eol) nil t)
+ (and (re-search-forward (rx "charging state:" (+ space)
+ (group (not space) (* nonl)) eol)
+ nil t)
(member charging-state '("unknown" "charged" nil))
;; On most multi-battery systems, most of the time only one
;; battery is "charging"/"discharging", the others are
;; "unknown".
(setq charging-state (match-string 1)))
- (when (re-search-forward "present rate: +\\([0-9]+\\) \\(m[AW]\\)$"
+ (when (re-search-forward (rx "present rate:" (+ space)
+ (battery--acpi-rate) eol)
nil t)
(setq rate (+ (or rate 0) (string-to-number (match-string 1))))
(when (> rate 0)
- (setq rate-type (or (and rate-type
- (if (string= rate-type (match-string 2))
- rate-type
- (error
- "Inconsistent rate types (%s vs. %s)"
- rate-type (match-string 2))))
- (match-string 2)))))
- (when (re-search-forward "remaining capacity: +\\([0-9]+\\) m[AW]h$"
+ (cond ((not rate-type)
+ (setq rate-type (match-string 2)))
+ ((not (string= rate-type (match-string 2)))
+ (error "Inconsistent rate types (%s vs. %s)"
+ rate-type (match-string 2))))))
+ (when (re-search-forward (rx "remaining capacity:" (+ space)
+ battery--acpi-capacity eol)
nil t)
(setq capacity
(+ (or capacity 0) (string-to-number (match-string 1))))))
(goto-char (point-max))
(ignore-errors (insert-file-contents (expand-file-name "info" dir)))
- (when (re-search-forward "present: +yes$" nil t)
- (when (re-search-forward "design capacity: +\\([0-9]+\\) m[AW]h$"
+ (when (re-search-forward (rx "present:" (+ space) "yes" eol) nil t)
+ (when (re-search-forward (rx "design capacity:" (+ space)
+ battery--acpi-capacity eol)
nil t)
(cl-incf design-capacity (string-to-number (match-string 1))))
- (when (re-search-forward "last full capacity: +\\([0-9]+\\) m[AW]h$"
+ (when (re-search-forward (rx "last full capacity:" (+ space)
+ battery--acpi-capacity eol)
nil t)
(cl-incf last-full-capacity (string-to-number (match-string 1))))
- (when (re-search-forward
- "design capacity warning: +\\([0-9]+\\) m[AW]h$" nil t)
+ (when (re-search-forward (rx "design capacity warning:" (+ space)
+ battery--acpi-capacity eol)
+ nil t)
(cl-incf warn (string-to-number (match-string 1))))
- (when (re-search-forward "design capacity low: +\\([0-9]+\\) m[AW]h$"
+ (when (re-search-forward (rx "design capacity low:" (+ space)
+ battery--acpi-capacity eol)
nil t)
(cl-incf low (string-to-number (match-string 1)))))))
(setq full-capacity (if (> last-full-capacity 0)
@@ -394,79 +456,70 @@ The following %-sequences are provided:
60)
rate))
hours (/ minutes 60)))
- (list (cons ?c (or (and capacity (number-to-string capacity)) "N/A"))
+ (list (cons ?c (if capacity (number-to-string capacity) "N/A"))
(cons ?L (or (battery-search-for-one-match-in-files
- (mapcar (lambda (e) (concat e "/state"))
- (ignore-errors
- (directory-files "/proc/acpi/ac_adapter/"
- t "\\`[^.]")))
- "state: +\\(.*\\)$" 1)
-
+ (mapcar (lambda (d) (expand-file-name "state" d))
+ (battery--files "/proc/acpi/ac_adapter/"))
+ (rx "state:" (+ space) (group (not space) (* nonl)) eol)
+ 1)
"N/A"))
(cons ?d (or (battery-search-for-one-match-in-files
- (mapcar (lambda (e) (concat e "/temperature"))
- (ignore-errors
- (directory-files "/proc/acpi/thermal_zone/"
- t "\\`[^.]")))
- "temperature: +\\([0-9]+\\) C$" 1)
-
+ (mapcar (lambda (d) (expand-file-name "temperature" d))
+ (battery--files "/proc/acpi/thermal_zone/"))
+ (rx "temperature:" (+ space) (group (+ digit)) " C" eol)
+ 1)
"N/A"))
- (cons ?r (or (and rate (concat (number-to-string rate) " "
- rate-type)) "N/A"))
+ (cons ?r (if rate
+ (concat (number-to-string rate) " " rate-type)
+ "N/A"))
(cons ?B (or charging-state "N/A"))
- (cons ?b (or (and (string= charging-state "charging") "+")
- (and capacity (< capacity low) "!")
- (and capacity (< capacity warn) "-")
- ""))
- (cons ?h (or (and hours (number-to-string hours)) "N/A"))
- (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
- (cons ?t (or (and minutes
- (format "%d:%02d" hours (- minutes (* 60 hours))))
- "N/A"))
- (cons ?p (or (and full-capacity capacity
- (> full-capacity 0)
- (number-to-string
- (floor (* 100 capacity) full-capacity)))
- "N/A")))))
+ (cons ?b (cond ((string= charging-state "charging") "+")
+ ((and capacity (< capacity low)) "!")
+ ((and capacity (< capacity warn)) "-")
+ ("")))
+ (cons ?h (if hours (number-to-string hours) "N/A"))
+ (cons ?m (if minutes (number-to-string minutes) "N/A"))
+ (cons ?t (if minutes (format "%d:%02d" hours (% minutes 60)) "N/A"))
+ (cons ?p (if (and full-capacity capacity (> full-capacity 0))
+ (number-to-string (floor (* 100 capacity) full-capacity))
+ "N/A")))))
;;; `/sys/class/power_supply/BATN' interface for Linux.
(defun battery-linux-sysfs ()
- "Get ACPI status information from Linux kernel.
+ "Get sysfs status information from Linux kernel.
This function works only with the new `/sys/class/power_supply/'
-format introduced in Linux version 2.4.25.
+interface introduced in Linux version 2.4.25.
The following %-sequences are provided:
%c Current capacity (mAh or mWh)
-%r Current rate
+%r Current rate of charge or discharge
+%L Power source (verbose)
%B Battery status (verbose)
%b Battery status, empty means high, `-' means low,
`!' means critical, and `+' means charging
%d Temperature (in degrees Celsius)
%p Battery load percentage
-%L AC line status (verbose)
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
%t Remaining time (to charge or discharge) in the form `h:min'"
- (let (charging-state temperature hours percentage-now
- ;; Some batteries report charges and current, other energy and power.
+ (let (;; Some batteries report charges and current, others energy and power.
;; In order to reliably be able to combine those data, we convert them
;; all to energy/power (since we can't combine different charges if
;; they're not at the same voltage).
(energy-full 0.0)
(energy-now 0.0)
(power-now 0.0)
- (voltage-now 10.8)) ;Arbitrary default, in case the info is missing.
+ (voltage-now 10.8) ; Arbitrary default, in case the info is missing.
+ charging-state temperature hours percentage-now)
;; SysFS provides information about each battery present in the
;; system in a separate subdirectory. We are going to merge the
;; available information together.
(with-temp-buffer
- (dolist (dir (ignore-errors
- (battery--find-linux-sysfs-batteries)))
- (erase-buffer)
- (ignore-errors (insert-file-contents
- (expand-file-name "uevent" dir)))
+ (dolist (dir (battery--find-linux-sysfs-batteries))
+ (ignore-errors
+ (insert-file-contents (expand-file-name "uevent" dir) nil nil nil t))
(goto-char (point-min))
(when (re-search-forward
"POWER_SUPPLY_VOLTAGE_NOW=\\([0-9]*\\)$" nil t)
@@ -502,7 +555,7 @@ The following %-sequences are provided:
voltage-now))
(cl-incf energy-now (* (string-to-number now-string)
voltage-now)))
- ((and (progn (goto-char (point-min)) t)
+ ((and (goto-char (point-min))
(re-search-forward
"POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t)
(setq full-string (match-string 1))
@@ -511,7 +564,6 @@ The following %-sequences are provided:
(setq now-string (match-string 1)))
(cl-incf energy-full (string-to-number full-string))
(cl-incf energy-now (string-to-number now-string)))))
- (goto-char (point-min))
(unless (zerop power-now)
(let ((remaining (if (string= charging-state "Discharging")
energy-now
@@ -519,9 +571,9 @@ The following %-sequences are provided:
(setq hours (/ remaining power-now)))))))
(when (and (> energy-full 0) (> energy-now 0))
(setq percentage-now (/ (* 100 energy-now) energy-full)))
- (list (cons ?c (cond ((or (> energy-full 0) (> energy-now 0))
- (number-to-string (/ energy-now voltage-now)))
- (t "N/A")))
+ (list (cons ?c (if (or (> energy-full 0) (> energy-now 0))
+ (number-to-string (/ energy-now voltage-now))
+ "N/A"))
(cons ?r (if (> power-now 0.0)
(format "%.1f" (/ power-now 1000000.0))
"N/A"))
@@ -532,162 +584,205 @@ The following %-sequences are provided:
"N/A"))
(cons ?d (or temperature "N/A"))
(cons ?B (or charging-state "N/A"))
- (cons ?b (or (and (string= charging-state "Charging") "+")
- (and percentage-now (< percentage-now battery-load-critical) "!")
- (and percentage-now (< percentage-now battery-load-low) "-")
- ""))
- (cons ?p (cond
- ((and percentage-now (format "%.1f" percentage-now)))
- (t "N/A")))
- (cons ?L (cond
- ((battery-search-for-one-match-in-files
- (list "/sys/class/power_supply/AC/online"
- "/sys/class/power_supply/ACAD/online"
- "/sys/class/power_supply/ADP1/online")
- "1" 0)
- "AC")
- ((battery-search-for-one-match-in-files
- (list "/sys/class/power_supply/AC/online"
- "/sys/class/power_supply/ACAD/online"
- "/sys/class/power_supply/ADP1/online")
- "0" 0)
- "BAT")
- (t "N/A"))))))
+ (cons ?b (cond ((string= charging-state "Charging") "+")
+ ((not percentage-now) "")
+ ((< percentage-now battery-load-critical) "!")
+ ((< percentage-now battery-load-low) "-")
+ ("")))
+ (cons ?p (if percentage-now (format "%.1f" percentage-now) "N/A"))
+ (cons ?L (pcase (battery-search-for-one-match-in-files
+ '("/sys/class/power_supply/AC/online"
+ "/sys/class/power_supply/ACAD/online"
+ "/sys/class/power_supply/ADP1/online")
+ (rx (in "01")) 0)
+ ("0" "BAT")
+ ("1" "AC")
+ (_ "N/A"))))))
-;;; `upowerd' interface.
-(defconst battery-upower-dbus-interface "org.freedesktop.UPower"
- "The interface to UPower.
-See URL `https://upower.freedesktop.org/docs/'.")
+;;; UPower interface.
+
+(defconst battery-upower-interface "org.freedesktop.UPower"
+ "Name of the UPower D-Bus interface.
+See URL `https://upower.freedesktop.org/docs/UPower.html'.")
-(defconst battery-upower-dbus-path "/org/freedesktop/UPower"
- "D-Bus path to talk to UPower service.")
+(defconst battery-upower-path "/org/freedesktop/UPower"
+ "D-Bus object providing `battery-upower-interface'.")
-(defconst battery-upower-dbus-device-interface
- (concat battery-upower-dbus-interface ".Device")
- "The Device interface of the UPower.
+(defconst battery-upower-device-interface "org.freedesktop.UPower.Device"
+ "Name of the UPower Device D-Bus interface.
See URL `https://upower.freedesktop.org/docs/Device.html'.")
-(defconst battery-upower-dbus-device-path
- (concat battery-upower-dbus-path "/devices")
- "D-Bus path to talk to devices part of the UPower service.")
-
-(defconst battery-upower-types
- '((0 . :unknown) (1 . :line-power) (2 . :battery)
- (3 . :ups) (4 . :monitor) (5 . :mouse)
- (6 . :keyboard) (7 . :pda) (8 . :phone))
- "Type of the device.")
-
-(defconst battery-upower-states
- '((0 . "unknown") (1 . "charging") (2 . "discharging")
- (3 . "empty") (4 . "fully-charged") (5 . "pending-charge")
- (6 . "pending-discharge"))
- "Alist of battery power states.
-Only valid for `:battery' devices.")
-
-(defun battery-upower-device-property (device property)
- "Get value of the single PROPERTY for the UPower DEVICE."
- (dbus-get-property
- :system battery-upower-dbus-service
- (expand-file-name device battery-upower-dbus-device-path)
- battery-upower-dbus-device-interface
- property))
-
-(defun battery-upower-device-all-properties (device)
+(defconst battery-upower-device-path "/org/freedesktop/UPower/devices"
+ "D-Bus object providing `battery-upower-device-interface'.")
+
+(defvar battery--upower-signals nil
+ "Handles for UPower signal subscriptions.")
+
+(defun battery--upower-signal-handler (&rest _)
+ "Update battery status on receiving a UPower D-Bus signal."
+ (timer-event-handler battery-update-timer))
+
+(defun battery--upower-props-changed (_interface changed _invalidated)
+ "Update status when system starts/stops running on battery.
+Intended as a UPower PropertiesChanged signal handler."
+ (when (assoc "OnBattery" changed)
+ (battery--upower-signal-handler)))
+
+(defun battery--upower-unsubscribe ()
+ "Unsubscribe from UPower device change signals."
+ (mapc #'dbus-unregister-object battery--upower-signals)
+ (setq battery--upower-signals ()))
+
+(defun battery--upower-subsribe ()
+ "Subscribe to UPower device change signals."
+ (push (dbus-register-signal :system battery-upower-service
+ battery-upower-path
+ dbus-interface-properties
+ "PropertiesChanged"
+ #'battery--upower-props-changed)
+ battery--upower-signals)
+ (dolist (method '("DeviceAdded" "DeviceRemoved"))
+ (push (dbus-register-signal :system battery-upower-service
+ battery-upower-path
+ battery-upower-interface
+ method #'battery--upower-signal-handler)
+ battery--upower-signals)))
+
+(defun battery--upower-device-properties (device)
"Return value for all available properties for the UPower DEVICE."
(dbus-get-all-properties
- :system battery-upower-dbus-service
- (expand-file-name device battery-upower-dbus-device-path)
- battery-upower-dbus-device-interface))
-
-(defun battery-upower-device-list ()
- "Return list of all available UPower devices.
-Each element is the cons cell in form: (DEVICE . DEVICE-TYPE)."
- (mapcar (lambda (device-path)
- (let* ((device (file-relative-name
- device-path battery-upower-dbus-device-path))
- (type-num (battery-upower-device-property device "Type")))
- (cons device (or (cdr (assq type-num battery-upower-types))
- :unknown))))
- (dbus-call-method :system battery-upower-dbus-service
- battery-upower-dbus-path
- battery-upower-dbus-interface
- "EnumerateDevices")))
-
-(defun battery-upower-device-autodetect (device-type)
- "Return first matching UPower device of DEVICE-TYPE."
- (car (rassq device-type (battery-upower-device-list))))
+ :system battery-upower-service
+ (expand-file-name device battery-upower-device-path)
+ battery-upower-device-interface))
+
+(defun battery--upower-devices ()
+ "List all UPower devices according to `battery-upower-device'."
+ (cond ((stringp battery-upower-device)
+ (list battery-upower-device))
+ (battery-upower-device)
+ ((dbus-call-method :system battery-upower-service
+ battery-upower-path
+ battery-upower-interface
+ "EnumerateDevices"))))
+
+(defun battery--upower-state (props state)
+ "Merge the UPower battery state in PROPS with STATE.
+This is an extension of the UPower DisplayDevice algorithm for
+merging multiple battery states into one. PROPS is an alist of
+battery properties from `battery-upower-device-interface', and
+STATE is a symbol representing the state to merge with."
+ ;; Map UPower enum into our printable symbols.
+ (let* ((new (pcase (cdr (assoc "State" props))
+ (1 'charging)
+ (2 'discharging)
+ (3 'empty)
+ (4 'fully-charged)
+ (5 'pending-charge)
+ (6 'pending-discharge)))
+ ;; Unknown state represented by nil.
+ (either (delq nil (list new state))))
+ ;; Earlier states override later ones.
+ (car (cond ((memq 'charging either))
+ ((memq 'discharging either))
+ ((memq 'pending-charge either))
+ ((memq 'pending-discharge either))
+ ;; Only options left are full or empty,
+ ;; but if they conflict return nil.
+ ((null (cdr either)) either)
+ ((apply #'eq either) either)))))
(defun battery-upower ()
- "Get battery status from dbus Upower interface.
-This function works only in systems with `upowerd' daemon
-running.
+ "Get battery status from UPower D-Bus interface.
+This function works only in systems that provide a UPower D-Bus
+service.
The following %-sequences are provided:
%c Current capacity (mWh)
-%p Battery load percentage
-%r Current rate
+%r Current rate of charge or discharge
+%L AC line status (verbose)
%B Battery status (verbose)
%b Battery status: empty means high, `-' means low,
`!' means critical, and `+' means charging
-%L AC line status (verbose)
+%d Temperature (in degrees Celsius)
+%p Battery load percentage
%s Remaining time (to charge or discharge) in seconds
%m Remaining time (to charge or discharge) in minutes
%h Remaining time (to charge or discharge) in hours
%t Remaining time (to charge or discharge) in the form `h:min'"
- (let* ((bat-device (or battery-upower-device
- (battery-upower-device-autodetect :battery)))
- (bat-props (when bat-device
- (battery-upower-device-all-properties bat-device)))
- (percents (cdr (assoc "Percentage" bat-props)))
- (time-to-empty (cdr (assoc "TimeToEmpty" bat-props)))
- (time-to-full (cdr (assoc "TimeToFull" bat-props)))
- (state (cdr (assoc "State" bat-props)))
- (level (cdr (assoc "BatteryLevel" bat-props)))
- (energy (cdr (assoc "Energy" bat-props)))
- (energy-rate (cdr (assoc "EnergyRate" bat-props)))
- (lp-device (or battery-upower-line-power-device
- (battery-upower-device-autodetect :line-power)))
- (online-p (when lp-device
- (battery-upower-device-property lp-device "Online")))
- (seconds (if online-p time-to-full time-to-empty))
- (minutes (when seconds (/ seconds 60)))
- (hours (when minutes (/ minutes 60)))
- (remaining-time (when hours
- (format "%d:%02d" hours (mod minutes 60)))))
- (list (cons ?c (if energy (number-to-string (round (* 1000 energy))) "N/A"))
- (cons ?p (if percents (number-to-string (round percents)) "N/A"))
- (cons ?r (if energy-rate
- (concat (number-to-string energy-rate) " W")
+ (let ((count 0) props type line-status state load temperature
+ secs mins hrs total-energy total-rate total-tte total-ttf)
+ ;; Merge information from all available or specified UPower
+ ;; devices like other `battery-status-function's.
+ (dolist (device (battery--upower-devices))
+ (setq props (battery--upower-device-properties device))
+ (setq type (cdr (assoc "Type" props)))
+ (cond
+ ((and (eq type 1) (not (eq line-status 'online)))
+ ;; It's a line power device: `online' if currently providing
+ ;; power, any other non-nil value if simply present.
+ (setq line-status (if (cdr (assoc "Online" props)) 'online t)))
+ ((and (eq type 2) (cdr (assoc "IsPresent" props)))
+ ;; It's a battery.
+ (setq count (1+ count))
+ (setq state (battery--upower-state props state))
+ (let ((energy (cdr (assoc "Energy" props)))
+ (rate (cdr (assoc "EnergyRate" props)))
+ (percent (cdr (assoc "Percentage" props)))
+ (temp (cdr (assoc "Temperature" props)))
+ (tte (cdr (assoc "TimeToEmpty" props)))
+ (ttf (cdr (assoc "TimeToFull" props))))
+ (when energy (setq total-energy (+ (or total-energy 0) energy)))
+ (when rate (setq total-rate (+ (or total-rate 0) rate)))
+ (when percent (setq load (+ (or load 0) percent)))
+ (when temp (setq temperature (+ (or temperature 0) temp)))
+ (when tte (setq total-tte (+ (or total-tte 0) tte)))
+ (when ttf (setq total-ttf (+ (or total-ttf 0) ttf)))))))
+ (when (> count 1)
+ ;; Averages over multiple batteries.
+ (when load (setq load (/ load count)))
+ (when temperature (setq temperature (/ temperature count))))
+ (when (setq secs (if (eq line-status 'online) total-ttf total-tte))
+ (setq mins (/ secs 60))
+ (setq hrs (/ secs 3600)))
+ (list (cons ?c (if total-energy
+ (format "%.0f" (* total-energy 1000))
"N/A"))
- (cons ?B (if state
- (cdr (assq state battery-upower-states))
- "unknown"))
- (cons ?b (cond ((= level 3) "-")
- ((= level 4) "!")
- (online-p "+")
- (t "")))
- (cons ?L (if online-p "on-line" (if lp-device "off-line" "unknown")))
- (cons ?s (if seconds (number-to-string seconds) "N/A"))
- (cons ?m (if minutes (number-to-string minutes) "N/A"))
- (cons ?h (if hours (number-to-string hours) "N/A"))
- (cons ?t (or remaining-time "N/A")))))
+ (cons ?r (if total-rate (format "%.1f W" total-rate) "N/A"))
+ (cons ?L (cond ((eq line-status 'online) "on-line")
+ (line-status "off-line")
+ ("N/A")))
+ (cons ?B (format "%s" (or state 'unknown)))
+ (cons ?b (cond ((eq state 'charging) "+")
+ ((and load (< load battery-load-critical)) "!")
+ ((and load (< load battery-load-low)) "-")
+ ("")))
+ ;; Zero usually means unknown.
+ (cons ?d (if (and temperature (/= temperature 0))
+ (format "%.0f" temperature)
+ "N/A"))
+ (cons ?p (if load (format "%.0f" load) "N/A"))
+ (cons ?s (if secs (number-to-string secs) "N/A"))
+ (cons ?m (if mins (number-to-string mins) "N/A"))
+ (cons ?h (if hrs (number-to-string hrs) "N/A"))
+ (cons ?t (if hrs (format "%d:%02d" hrs (% mins 60)) "N/A")))))
;;; `apm' interface for BSD.
+
(defun battery-bsd-apm ()
"Get APM status information from BSD apm binary.
The following %-sequences are provided:
+%P Advanced power saving mode state (verbose)
%L AC line status (verbose)
%B Battery status (verbose)
%b Battery status, empty means high, `-' means low,
- `!' means critical, and `+' means charging
-%P Advanced power saving mode state (verbose)
-%p Battery charge percentage
-%s Remaining battery charge time in seconds
-%m Remaining battery charge time in minutes
-%h Remaining battery charge time in hours
-%t Remaining battery charge time in the form `h:min'"
+ `!' means critical, and `+' means charging
+%p Battery load percentage
+%s Remaining time (to charge or discharge) in seconds
+%m Remaining time (to charge or discharge) in minutes
+%h Remaining time (to charge or discharge) in hours
+%t Remaining time (to charge or discharge) in the form `h:min'"
(let* ((os-name (car (split-string
;; FIXME: Can't we use something like `system-type'?
(shell-command-to-string "/usr/bin/uname"))))
@@ -753,7 +848,7 @@ The following %-sequences are provided:
(setq seconds (string-to-number battery-life)
minutes (truncate seconds 60)))
(setq hours (truncate minutes 60)
- remaining-time (format "%d:%02d" hours (mod minutes 60))))
+ remaining-time (format "%d:%02d" hours (% minutes 60))))
(list (cons ?L (or line-status "N/A"))
(cons ?B (or (car battery-status) "N/A"))
(cons ?b (or (cdr battery-status) "N/A"))
@@ -761,9 +856,9 @@ The following %-sequences are provided:
"N/A"
battery-percentage))
(cons ?P (or apm-mode "N/A"))
- (cons ?s (or (and seconds (number-to-string seconds)) "N/A"))
- (cons ?m (or (and minutes (number-to-string minutes)) "N/A"))
- (cons ?h (or (and hours (number-to-string hours)) "N/A"))
+ (cons ?s (if seconds (number-to-string seconds) "N/A"))
+ (cons ?m (if minutes (number-to-string minutes) "N/A"))
+ (cons ?h (if hours (number-to-string hours) "N/A"))
(cons ?t (or remaining-time "N/A")))))
@@ -778,21 +873,25 @@ The following %-sequences are provided:
%b Battery status, empty means high, `-' means low,
`!' means critical, and `+' means charging
%p Battery load percentage
-%h Remaining time in hours
-%m Remaining time in minutes
-%t Remaining time in the form `h:min'"
- (let (power-source load-percentage battery-status battery-status-symbol
- remaining-time hours minutes)
+%m Remaining time (to charge or discharge) in minutes
+%h Remaining time (to charge or discharge) in hours
+%t Remaining time (to charge or discharge) in the form `h:min'"
+ (let ( power-source load-percentage battery-status battery-status-symbol
+ remaining-time hours minutes )
(with-temp-buffer
(ignore-errors (call-process "pmset" nil t nil "-g" "ps"))
(goto-char (point-min))
- (when (re-search-forward "\\(?:Currentl?y\\|Now\\) drawing from '\\(AC\\|Battery\\) Power'" nil t)
+ (when (re-search-forward ;; Handle old typo in output.
+ "\\(?:Currentl?y\\|Now\\) drawing from '\\(AC\\|Battery\\) Power'"
+ nil t)
(setq power-source (match-string 1))
- (when (re-search-forward "^ -InternalBattery-0\\([ \t]+(id=[0-9]+)\\)*[ \t]+" nil t)
+ (when (re-search-forward (rx bol " -InternalBattery-0" (+ space)
+ (* "(id=" (+ digit) ")" (+ space)))
+ nil t)
(when (looking-at "\\([0-9]\\{1,3\\}\\)%")
(setq load-percentage (match-string 1))
(goto-char (match-end 0))
- (cond ((looking-at "; charging")
+ (cond ((looking-at-p "; charging")
(setq battery-status "charging"
battery-status-symbol "+"))
((< (string-to-number load-percentage) battery-load-critical)
@@ -823,13 +922,7 @@ The following %-sequences are provided:
(defun battery-format (format alist)
"Substitute %-sequences in FORMAT."
- (replace-regexp-in-string
- "%."
- (lambda (str)
- (let ((char (aref str 1)))
- (if (eq char ?%) "%"
- (or (cdr (assoc char alist)) ""))))
- format t t))
+ (format-spec format alist 'delete))
(defun battery-search-for-one-match-in-files (files regexp match-num)
"Search REGEXP in the content of the files listed in FILES.
diff --git a/lisp/cedet/semantic/complete.el b/lisp/cedet/semantic/complete.el
index 7abc4360f64..b262ab710f6 100644
--- a/lisp/cedet/semantic/complete.el
+++ b/lisp/cedet/semantic/complete.el
@@ -1635,10 +1635,10 @@ This will not happen if you directly set this variable via `setq'."
:group 'semantic
:version "24.3"
:type 'integer
- :set '(lambda (sym var)
- (set-default sym var)
- (when (boundp 'x-max-tooltip-size)
- (setcdr x-max-tooltip-size (max (1+ var) (cdr x-max-tooltip-size))))))
+ :set (lambda (sym var)
+ (set-default sym var)
+ (when (boundp 'x-max-tooltip-size)
+ (setcdr x-max-tooltip-size (max (1+ var) (cdr x-max-tooltip-size))))))
(defclass semantic-displayer-tooltip (semantic-displayer-traditional)
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 24ebfa4b0de..efb214088d8 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1064,8 +1064,6 @@ corresponding command.
Within CMD, %i denotes the input file(s), and %o denotes the
output file. %i path(s) are relative, while %o is absolute.")
-(declare-function format-spec "format-spec.el" (format specification))
-
;;;###autoload
(defun dired-do-compress-to ()
"Compress selected files and directories to an archive.
@@ -1073,7 +1071,6 @@ Prompt for the archive file name.
Choose the archiving command based on the archive file-name extension
and `dired-compress-files-alist'."
(interactive)
- (require 'format-spec)
(let* ((in-files (dired-get-marked-files nil nil nil nil t))
(out-file (expand-file-name (read-file-name "Compress to: ")))
(rule (cl-find-if
@@ -1093,12 +1090,12 @@ and `dired-compress-files-alist'."
(when (zerop
(dired-shell-command
(format-spec (cdr rule)
- `((?\o . ,(shell-quote-argument out-file))
- (?\i . ,(mapconcat
- (lambda (file-desc)
- (shell-quote-argument (file-name-nondirectory
- file-desc)))
- in-files " "))))))
+ `((?o . ,(shell-quote-argument out-file))
+ (?i . ,(mapconcat
+ (lambda (in-file)
+ (shell-quote-argument
+ (file-name-nondirectory in-file)))
+ in-files " "))))))
(message (ngettext "Compressed %d file to %s"
"Compressed %d files to %s"
(length in-files))
@@ -3087,6 +3084,7 @@ in the Dired buffer."
(declare-function vc-compatible-state "vc")
+;;;###autoload
(defun dired-vc-deduce-fileset (&optional state-model-only-files not-state-changing)
(let ((backend (vc-responsible-backend default-directory))
(files (dired-get-marked-files nil nil nil nil t))
diff --git a/lisp/emacs-lisp/benchmark.el b/lisp/emacs-lisp/benchmark.el
index a7fcc5cb8f2..2fa5a878801 100644
--- a/lisp/emacs-lisp/benchmark.el
+++ b/lisp/emacs-lisp/benchmark.el
@@ -81,7 +81,7 @@ result. The overhead of the `lambda's is accounted for."
(gcs (make-symbol "gcs"))
(gc (make-symbol "gc"))
(code (byte-compile `(lambda () ,@forms)))
- (lambda-code (byte-compile '(lambda ()))))
+ (lambda-code (byte-compile '(lambda ()))))
`(let ((,gc gc-elapsed)
(,gcs gcs-done))
(list ,(if (or (symbolp repetitions) (> repetitions 1))
diff --git a/lisp/emacs-lisp/generator.el b/lisp/emacs-lisp/generator.el
index ba344eb5150..c95c758a571 100644
--- a/lisp/emacs-lisp/generator.el
+++ b/lisp/emacs-lisp/generator.el
@@ -673,7 +673,7 @@ When called as a function, NAME returns an iterator value that
encapsulates the state of a computation that produces a sequence
of values. Callers can retrieve each value using `iter-next'."
(declare (indent defun)
- (debug (&define name lambda-list lambda-doc def-body))
+ (debug (&define name lambda-list lambda-doc &rest sexp))
(doc-string 3))
(cl-assert lexical-binding)
(let* ((parsed-body (macroexp-parse-body body))
@@ -687,14 +687,14 @@ of values. Callers can retrieve each value using `iter-next'."
"Return a lambda generator.
`iter-lambda' is to `iter-defun' as `lambda' is to `defun'."
(declare (indent defun)
- (debug (&define lambda-list lambda-doc def-body)))
+ (debug (&define lambda-list lambda-doc &rest sexp)))
(cl-assert lexical-binding)
`(lambda ,arglist
,(cps-generate-evaluator body)))
(defmacro iter-make (&rest body)
"Return a new iterator."
- (declare (debug t))
+ (declare (debug (&rest sexp)))
(cps-generate-evaluator body))
(defconst iter-empty (lambda (_op _val) (signal 'iter-end-of-sequence nil))
@@ -720,7 +720,7 @@ is blocked."
Evaluate BODY with VAR bound to each value from ITERATOR.
Return the value with which ITERATOR finished iteration."
(declare (indent 1)
- (debug ((symbolp form) body)))
+ (debug ((symbolp form) &rest sexp)))
(let ((done-symbol (cps--gensym "iter-do-iterator-done"))
(condition-symbol (cps--gensym "iter-do-condition"))
(it-symbol (cps--gensym "iter-do-iterator"))
diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el
index 096036a0ffa..513bd328899 100644
--- a/lisp/emacs-lisp/gv.el
+++ b/lisp/emacs-lisp/gv.el
@@ -224,7 +224,7 @@ The first arg in ARGLIST (the one that receives VAL) receives an expression
which can do arbitrary things, whereas the other arguments are all guaranteed
to be pure and copyable. Example use:
(gv-define-setter aref (v a i) \\=`(aset ,a ,i ,v))"
- (declare (indent 2) (debug (&define name sexp def-body)))
+ (declare (indent 2) (debug (&define name :name gv-setter sexp def-body)))
`(gv-define-expander ,name
(lambda (do &rest args)
(declare-function
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 0171fd56ffd..c349b5d49f6 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -441,9 +441,9 @@ synchronously."
&aux
(name (intern name-string))
(version (version-to-list version-string))
- (reqs (mapcar #'(lambda (elt)
- (list (car elt)
- (version-to-list (cadr elt))))
+ (reqs (mapcar (lambda (elt)
+ (list (car elt)
+ (version-to-list (cadr elt))))
(if (eq 'quote (car requirements))
(nth 1 requirements)
requirements)))
@@ -3900,9 +3900,9 @@ If VERSION is nil or the empty string, show all packages."
(package-menu--generate t t)
(package-menu--filter-by
(let ((fun (pcase predicate
- ('= 'version-list-=)
- ('< 'version-list-<)
- ('> '(lambda (a b) (not (version-list-<= a b))))
+ ('= #'version-list-=)
+ ('< #'version-list-<)
+ ('> (lambda (a b) (not (version-list-<= a b))))
(_ (error "Unknown predicate: %s" predicate))))
(ver (version-to-list version)))
(lambda (pkg-desc)
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index aa4b2addd47..88bb0a8bd6c 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -1381,7 +1381,7 @@ To make local rx extensions, use `rx-let' for `rx',
For more details, see Info node `(elisp) Extending Rx'.
\(fn NAME [(ARGS...)] RX)"
- (declare (indent 1))
+ (declare (indent defun))
`(eval-and-compile
(put ',name 'rx-definition ',(rx--make-binding name definition))
',name))
diff --git a/lisp/emulation/cua-base.el b/lisp/emulation/cua-base.el
index 26a1a8955f4..c4dcb76446e 100644
--- a/lisp/emulation/cua-base.el
+++ b/lisp/emulation/cua-base.el
@@ -860,7 +860,7 @@ With numeric prefix arg, copy to register 0-9 instead."
(defun cua-cancel ()
"Cancel the active region, rectangle, or global mark."
(interactive)
- (setq mark-active nil)
+ (deactivate-mark)
(if (fboundp 'cua--cancel-rectangle)
(cua--cancel-rectangle)))
diff --git a/lisp/emulation/cua-rect.el b/lisp/emulation/cua-rect.el
index e99bb33dfb1..663995a0a11 100644
--- a/lisp/emulation/cua-rect.el
+++ b/lisp/emulation/cua-rect.el
@@ -1412,7 +1412,7 @@ With prefix arg, indent to that column."
(add-function :around region-extract-function
#'cua--rectangle-region-extract)
(add-function :around region-insert-function
- #'cua--insert-rectangle)
+ #'cua--rectangle-region-insert)
(add-function :around redisplay-highlight-region-function
#'cua--rectangle-highlight-for-redisplay)
@@ -1422,6 +1422,10 @@ With prefix arg, indent to that column."
;; already do it elsewhere.
(funcall redisplay-unhighlight-region-function (nth 3 args))))
+(defun cua--rectangle-region-insert (orig &rest args)
+ (if (not cua--rectangle) (apply orig args)
+ (funcall #'cua--insert-rectangle (car args))))
+
(defun cua--rectangle-region-extract (orig &optional delete)
(cond
((not cua--rectangle)
diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el
index 3107ff2ccd1..0e98f2bc613 100644
--- a/lisp/erc/erc-match.el
+++ b/lisp/erc/erc-match.el
@@ -555,16 +555,15 @@ See `erc-log-match-format'."
(and (eq erc-log-matches-flag 'away)
(erc-away-time)))
match-buffer-name)
- (let ((line (format-spec erc-log-match-format
- (format-spec-make
- ?n nick
- ?t (format-time-string
- (or (and (boundp 'erc-timestamp-format)
- erc-timestamp-format)
- "[%Y-%m-%d %H:%M] "))
- ?c (or (erc-default-target) "")
- ?m message
- ?u nickuserhost))))
+ (let ((line (format-spec
+ erc-log-match-format
+ `((?n . ,nick)
+ (?t . ,(format-time-string
+ (or (bound-and-true-p erc-timestamp-format)
+ "[%Y-%m-%d %H:%M] ")))
+ (?c . ,(or (erc-default-target) ""))
+ (?m . ,message)
+ (?u . ,nickuserhost)))))
(with-current-buffer (erc-log-matches-make-buffer match-buffer-name)
(let ((inhibit-read-only t))
(goto-char (point-max))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index cfde84e19aa..38807787945 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -6391,17 +6391,16 @@ if `erc-away' is non-nil."
(defun erc-update-mode-line-buffer (buffer)
"Update the mode line in a single ERC buffer BUFFER."
(with-current-buffer buffer
- (let ((spec (format-spec-make
- ?a (erc-format-away-status)
- ?l (erc-format-lag-time)
- ?m (erc-format-channel-modes)
- ?n (or (erc-current-nick) "")
- ?N (erc-format-network)
- ?o (or (erc-controls-strip erc-channel-topic) "")
- ?p (erc-port-to-string erc-session-port)
- ?s (erc-format-target-and/or-server)
- ?S (erc-format-target-and/or-network)
- ?t (erc-format-target)))
+ (let ((spec `((?a . ,(erc-format-away-status))
+ (?l . ,(erc-format-lag-time))
+ (?m . ,(erc-format-channel-modes))
+ (?n . ,(or (erc-current-nick) ""))
+ (?N . ,(erc-format-network))
+ (?o . ,(or (erc-controls-strip erc-channel-topic) ""))
+ (?p . ,(erc-port-to-string erc-session-port))
+ (?s . ,(erc-format-target-and/or-server))
+ (?S . ,(erc-format-target-and/or-network))
+ (?t . ,(erc-format-target))))
(process-status (cond ((and (erc-server-process-alive)
(not erc-server-connected))
":connecting")
diff --git a/lisp/eshell/em-pred.el b/lisp/eshell/em-pred.el
index 7219af45f54..c26f654e278 100644
--- a/lisp/eshell/em-pred.el
+++ b/lisp/eshell/em-pred.el
@@ -451,11 +451,9 @@ resultant list of strings."
`(lambda (file)
(let ((attrs (file-attributes file)))
(if attrs
- (,(if (eq qual ?-)
- 'time-less-p
- (if (eq qual ?+)
- '(lambda (a b) (time-less-p b a))
- 'time-equal-p))
+ (,(cond ((eq qual ?-) #'time-less-p)
+ ((eq qual ?+) (lambda (a b) (time-less-p b a)))
+ (#'time-equal-p))
,when (nth ,attr-index attrs)))))))
(defun eshell-pred-file-type (type)
diff --git a/lisp/faces.el b/lisp/faces.el
index 8c3e464cb86..ba85973bf10 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1794,6 +1794,12 @@ on which one provides better contrast with COLOR."
(color-values color)))
"#ffffff" "black"))
+(defconst color-luminance-dark-limit 0.325
+ "The relative luminance below which a color is considered 'dark'.
+A 'dark' color in this sense provides better contrast with white
+than with black; see `color-dark-p'.
+This value was determined experimentally.")
+
(defun color-dark-p (rgb)
"Whether RGB is more readable against white than black.
RGB is a 3-element list (R G B), each component in the range [0,1].
@@ -1814,7 +1820,7 @@ contrast colour with RGB as background and as foreground."
(g (expt sg 2.2))
(b (expt sb 2.2))
(y (+ (* r 0.2126) (* g 0.7152) (* b 0.0722))))
- (< y (eval-when-compile (expt 0.6 2.2)))))
+ (< y color-luminance-dark-limit)))
(declare-function xw-color-defined-p "xfns.c" (color &optional frame))
@@ -2758,6 +2764,33 @@ Note: Other faces cannot inherit from the cursor face."
:version "21.1"
:group 'basic-faces)
+(defface tab-bar
+ '((((class color) (min-colors 88))
+ :inherit variable-pitch
+ :background "grey85"
+ :foreground "black")
+ (((class mono))
+ :background "grey")
+ (t
+ :inverse-video t))
+ "Tab bar face."
+ :version "27.1"
+ :group 'basic-faces)
+
+(defface tab-line
+ '((((class color) (min-colors 88))
+ :inherit variable-pitch
+ :height 0.9
+ :background "grey85"
+ :foreground "black")
+ (((class mono))
+ :background "grey")
+ (t
+ :inverse-video t))
+ "Tab line face."
+ :version "27.1"
+ :group 'basic-faces)
+
(defface menu
'((((type tty))
:inverse-video t)
diff --git a/lisp/format-spec.el b/lisp/format-spec.el
index 9278bd74c42..6af79a44167 100644
--- a/lisp/format-spec.el
+++ b/lisp/format-spec.el
@@ -1,4 +1,4 @@
-;;; format-spec.el --- functions for formatting arbitrary formatting strings
+;;; format-spec.el --- format arbitrary formatting strings -*- lexical-binding: t -*-
;; Copyright (C) 1999-2020 Free Software Foundation, Inc.
@@ -24,10 +24,8 @@
;;; Code:
-(eval-when-compile
- (require 'subr-x))
-
-(defun format-spec (format specification &optional only-present)
+;;;###autoload
+(defun format-spec (format specification &optional ignore-missing)
"Return a string based on FORMAT and SPECIFICATION.
FORMAT is a string containing `format'-like specs like \"su - %u %k\".
SPECIFICATION is an alist mapping format specification characters
@@ -39,22 +37,22 @@ For instance:
\\=`((?u . ,(user-login-name))
(?l . \"ls\")))
-Each %-spec may contain optional flag and width modifiers, as
-follows:
+Each %-spec may contain optional flag, width, and precision
+modifiers, as follows:
- %<flags><width>character
+ %<flags><width><precision>character
The following flags are allowed:
* 0: Pad to the width, if given, with zeros instead of spaces.
* -: Pad to the width, if given, on the right instead of the left.
-* <: Truncate to the width, if given, on the left.
-* >: Truncate to the width, if given, on the right.
+* <: Truncate to the width and precision, if given, on the left.
+* >: Truncate to the width and precision, if given, on the right.
* ^: Convert to upper case.
* _: Convert to lower case.
-The width modifier behaves like the corresponding one in `format'
-when applied to %s.
+The width and truncation modifiers behave like the corresponding
+ones in `format' when applied to %s.
For example, \"%<010b\" means \"substitute into the output the
value associated with ?b in SPECIFICATION, either padding it with
@@ -64,89 +62,108 @@ characters wide\".
Any text properties of FORMAT are copied to the result, with any
text properties of a %-spec itself copied to its substitution.
-ONLY-PRESENT indicates how to handle %-spec characters not
+IGNORE-MISSING indicates how to handle %-spec characters not
present in SPECIFICATION. If it is nil or omitted, emit an
-error; otherwise leave those %-specs and any occurrences of
-\"%%\" in FORMAT verbatim in the result, including their text
-properties, if any."
+error; if it is the symbol `ignore', leave those %-specs verbatim
+in the result, including their text properties, if any; if it is
+the symbol `delete', remove those %-specs from the result;
+otherwise do the same as for the symbol `ignore', but also leave
+any occurrences of \"%%\" in FORMAT verbatim in the result."
(with-temp-buffer
(insert format)
(goto-char (point-min))
(while (search-forward "%" nil t)
(cond
- ;; Quoted percent sign.
- ((eq (char-after) ?%)
- (unless only-present
- (delete-char 1)))
- ;; Valid format spec.
- ((looking-at "\\([-0 _^<>]*\\)\\([0-9.]*\\)\\([a-zA-Z]\\)")
- (let* ((modifiers (match-string 1))
- (num (match-string 2))
- (spec (string-to-char (match-string 3)))
- (val (assq spec specification)))
- (if (not val)
- (unless only-present
- (error "Invalid format character: `%%%c'" spec))
- (setq val (cdr val)
- modifiers (format-spec--parse-modifiers modifiers))
- ;; Pad result to desired length.
- (let ((text (format "%s" val)))
- (when num
- (setq num (string-to-number num))
- (setq text (format-spec--pad text num modifiers))
- (when (> (length text) num)
- (cond
- ((memq :chop-left modifiers)
- (setq text (substring text (- (length text) num))))
- ((memq :chop-right modifiers)
- (setq text (substring text 0 num))))))
- (when (memq :uppercase modifiers)
- (setq text (upcase text)))
- (when (memq :lowercase modifiers)
- (setq text (downcase text)))
- ;; Insert first, to preserve text properties.
- (insert-and-inherit text)
- ;; Delete the specifier body.
- (delete-region (+ (match-beginning 0) (length text))
- (+ (match-end 0) (length text)))
- ;; Delete the percent sign.
- (delete-region (1- (match-beginning 0)) (match-beginning 0))))))
- ;; Signal an error on bogus format strings.
- (t
- (unless only-present
- (error "Invalid format string")))))
+ ;; Quoted percent sign.
+ ((= (following-char) ?%)
+ (when (memq ignore-missing '(nil ignore delete))
+ (delete-char 1)))
+ ;; Valid format spec.
+ ((looking-at (rx (? (group (+ (in " 0<>^_-"))))
+ (? (group (+ digit)))
+ (? (group ?. (+ digit)))
+ (group alpha)))
+ (let* ((beg (point))
+ (end (match-end 0))
+ (flags (match-string 1))
+ (width (match-string 2))
+ (trunc (match-string 3))
+ (char (string-to-char (match-string 4)))
+ (text (assq char specification)))
+ (cond (text
+ ;; Handle flags.
+ (setq text (format-spec--do-flags
+ (format "%s" (cdr text))
+ (format-spec--parse-flags flags)
+ (and width (string-to-number width))
+ (and trunc (car (read-from-string trunc 1)))))
+ ;; Insert first, to preserve text properties.
+ (insert-and-inherit text)
+ ;; Delete the specifier body.
+ (delete-region (point) (+ end (length text)))
+ ;; Delete the percent sign.
+ (delete-region (1- beg) beg))
+ ((eq ignore-missing 'delete)
+ ;; Delete the whole format spec.
+ (delete-region (1- beg) end))
+ ((not ignore-missing)
+ (error "Invalid format character: `%%%c'" char)))))
+ ;; Signal an error on bogus format strings.
+ ((not ignore-missing)
+ (error "Invalid format string"))))
(buffer-string)))
-(defun format-spec--pad (text total-length modifiers)
- (if (> (length text) total-length)
- ;; The text is longer than the specified length; do nothing.
- text
- (let ((padding (make-string (- total-length (length text))
- (if (memq :zero-pad modifiers)
- ?0
- ?\s))))
- (if (memq :right-pad modifiers)
- (concat text padding)
- (concat padding text)))))
-
-(defun format-spec--parse-modifiers (modifiers)
+(defun format-spec--do-flags (str flags width trunc)
+ "Return STR formatted according to FLAGS, WIDTH, and TRUNC.
+FLAGS is a list of keywords as returned by
+`format-spec--parse-flags'. WIDTH and TRUNC are either nil or
+string widths corresponding to `format-spec' modifiers."
+ (let (diff str-width)
+ ;; Truncate original string first, like `format' does.
+ (when trunc
+ (setq str-width (string-width str))
+ (when (> (setq diff (- str-width trunc)) 0)
+ (setq str (if (memq :chop-left flags)
+ (truncate-string-to-width str str-width diff)
+ (format (format "%%.%ds" trunc) str))
+ ;; We know the new width so save it for later.
+ str-width trunc)))
+ ;; Pad or chop to width.
+ (when width
+ (setq str-width (or str-width (string-width str))
+ diff (- width str-width))
+ (cond ((zerop diff))
+ ((> diff 0)
+ (let ((pad (make-string diff (if (memq :pad-zero flags) ?0 ?\s))))
+ (setq str (if (memq :pad-right flags)
+ (concat str pad)
+ (concat pad str)))))
+ ((memq :chop-left flags)
+ (setq str (truncate-string-to-width str str-width (- diff))))
+ ((memq :chop-right flags)
+ (setq str (format (format "%%.%ds" width) str))))))
+ ;; Fiddle case.
+ (cond ((memq :upcase flags)
+ (upcase str))
+ ((memq :downcase flags)
+ (downcase str))
+ (str)))
+
+(defun format-spec--parse-flags (flags)
+ "Convert sequence of FLAGS to list of human-readable keywords."
(mapcan (lambda (char)
- (when-let ((modifier
- (pcase char
- (?0 :zero-pad)
- (?\s :space-pad)
- (?^ :uppercase)
- (?_ :lowercase)
- (?- :right-pad)
- (?< :chop-left)
- (?> :chop-right))))
- (list modifier)))
- modifiers))
+ (pcase char
+ (?0 (list :pad-zero))
+ (?- (list :pad-right))
+ (?< (list :chop-left))
+ (?> (list :chop-right))
+ (?^ (list :upcase))
+ (?_ (list :downcase))))
+ flags))
(defun format-spec-make (&rest pairs)
"Return an alist suitable for use in `format-spec' based on PAIRS.
-PAIRS is a list where every other element is a character and a value,
-starting with a character."
+PAIRS is a property list with characters as keys."
(let (alist)
(while pairs
(unless (cdr pairs)
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index da6231d7330..5028da5e8df 100644
--- a/lisp/gnus/gnus-cloud.el
+++ b/lisp/gnus/gnus-cloud.el
@@ -223,13 +223,10 @@ easy interactive way to set this from the Server buffer."
(t
(gnus-message 1 "Unknown type %s; ignoring" type))))))
-(defun gnus-cloud-update-newsrc-data (group elem &optional force-older)
- "Update the newsrc data for GROUP from ELEM.
-Use old data if FORCE-OLDER is not nil."
+(defun gnus-cloud-update-newsrc-data (group elem)
+ "Update the newsrc data for GROUP from ELEM."
(let* ((contents (plist-get elem :contents))
(date (or (plist-get elem :timestamp) "0"))
- (now (gnus-cloud-timestamp nil))
- (newer (string-lessp date now))
(group-info (gnus-get-info group)))
(if (and contents
(stringp (nth 0 contents))
@@ -238,15 +235,13 @@ Use old data if FORCE-OLDER is not nil."
(if (equal (format "%S" group-info)
(format "%S" contents))
(gnus-message 3 "Skipping cloud update of group %s, the info is the same" group)
- (if (and newer (not force-older))
- (gnus-message 3 "Skipping outdated cloud info for group %s, the info is from %s (now is %s)" group date now)
- (when (or (not gnus-cloud-interactive)
- (gnus-y-or-n-p
- (format "%s has older different info in the cloud as of %s, update it here? "
- group date)))
- (gnus-message 2 "Installing cloud update of group %s" group)
- (gnus-set-info group contents)
- (gnus-group-update-group group))))
+ (when (or (not gnus-cloud-interactive)
+ (gnus-y-or-n-p
+ (format "%s has different info in the cloud from %s, update it here? "
+ group date)))
+ (gnus-message 2 "Installing cloud update of group %s" group)
+ (gnus-set-info group contents)
+ (gnus-group-update-group group)))
(gnus-error 1 "Sorry, group %s is not subscribed" group))
(gnus-error 1 "Sorry, could not update newsrc for group %s (invalid data %S)"
group elem))))
@@ -380,8 +375,9 @@ When FULL is t, upload everything, not just a difference from the last full."
(gnus-cloud-files-to-upload full)
(gnus-cloud-collect-full-newsrc)))
(group (gnus-group-full-name gnus-cloud-group-name gnus-cloud-method)))
+ (setq gnus-cloud-sequence (1+ (or gnus-cloud-sequence 0)))
(insert (format "Subject: (sequence: %s type: %s storage-method: %s)\n"
- (or gnus-cloud-sequence "UNKNOWN")
+ gnus-cloud-sequence
(if full :full :partial)
gnus-cloud-storage-method))
(insert "From: nobody@gnus.cloud.invalid\n")
@@ -390,7 +386,6 @@ When FULL is t, upload everything, not just a difference from the last full."
(if (gnus-request-accept-article gnus-cloud-group-name gnus-cloud-method
t t)
(progn
- (setq gnus-cloud-sequence (1+ (or gnus-cloud-sequence 0)))
(gnus-cloud-add-timestamps elems)
(gnus-message 3 "Uploaded Gnus Cloud data successfully to %s" group)
(gnus-group-refresh-group group))
@@ -459,18 +454,21 @@ instead of `gnus-cloud-sequence'.
When UPDATE is t, returns the result of calling `gnus-cloud-update-all'.
Otherwise, returns the Gnus Cloud data chunks."
(let ((articles nil)
+ (highest-sequence-seen gnus-cloud-sequence)
chunks)
(dolist (header (gnus-cloud-available-chunks))
- (when (> (gnus-cloud-chunk-sequence (mail-header-subject header))
- (or sequence-override gnus-cloud-sequence -1))
-
- (if (string-match (format "storage-method: %s" gnus-cloud-storage-method)
- (mail-header-subject header))
- (push (mail-header-number header) articles)
- (gnus-message 1 "Skipping article %s because it didn't match the Gnus Cloud method %s: %s"
- (mail-header-number header)
- gnus-cloud-storage-method
- (mail-header-subject header)))))
+ (let ((this-sequence (gnus-cloud-chunk-sequence (mail-header-subject header))))
+ (when (> this-sequence (or sequence-override gnus-cloud-sequence -1))
+
+ (if (string-match (format "storage-method: %s" gnus-cloud-storage-method)
+ (mail-header-subject header))
+ (progn
+ (push (mail-header-number header) articles)
+ (setq highest-sequence-seen (max highest-sequence-seen this-sequence)))
+ (gnus-message 1 "Skipping article %s because it didn't match the Gnus Cloud method %s: %s"
+ (mail-header-number header)
+ gnus-cloud-storage-method
+ (mail-header-subject header))))))
(when articles
(nnimap-request-articles (nreverse articles) gnus-cloud-group-name)
(with-current-buffer nntp-server-buffer
@@ -480,7 +478,9 @@ Otherwise, returns the Gnus Cloud data chunks."
(push (gnus-cloud-parse-chunk) chunks)
(forward-line 1))))
(if update
- (mapcar #'gnus-cloud-update-all chunks)
+ (progn
+ (mapc #'gnus-cloud-update-all chunks)
+ (setq gnus-cloud-sequence highest-sequence-seen))
chunks)))
(defun gnus-cloud-server-p (server)
diff --git a/lisp/gnus/gnus-eform.el b/lisp/gnus/gnus-eform.el
index 54118aad1e6..1bc1261ee8f 100644
--- a/lisp/gnus/gnus-eform.el
+++ b/lisp/gnus/gnus-eform.el
@@ -50,13 +50,13 @@
(defvar gnus-edit-form-buffer "*Gnus edit form*")
(defvar gnus-edit-form-done-function nil)
-(defvar gnus-edit-form-mode-map nil)
-(unless gnus-edit-form-mode-map
- (setq gnus-edit-form-mode-map (make-sparse-keymap))
- (set-keymap-parent gnus-edit-form-mode-map emacs-lisp-mode-map)
- (gnus-define-keys gnus-edit-form-mode-map
- "\C-c\C-c" gnus-edit-form-done
- "\C-c\C-k" gnus-edit-form-exit))
+(defvar gnus-edit-form-mode-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map emacs-lisp-mode-map)
+ (gnus-define-keys map
+ "\C-c\C-c" gnus-edit-form-done
+ "\C-c\C-k" gnus-edit-form-exit)
+ map))
(defun gnus-edit-form-make-menu-bar ()
(unless (boundp 'gnus-edit-form-menu)
@@ -67,9 +67,9 @@
["Exit" gnus-edit-form-exit t]))
(gnus-run-hooks 'gnus-edit-form-menu-hook)))
-(define-derived-mode gnus-edit-form-mode fundamental-mode "Edit Form"
+(define-derived-mode gnus-edit-form-mode lisp-data-mode "Edit Form"
"Major mode for editing forms.
-It is a slightly enhanced emacs-lisp-mode.
+It is a slightly enhanced `lisp-data-mode'.
\\{gnus-edit-form-mode-map}"
(when (gnus-visual-p 'group-menu 'menu)
diff --git a/lisp/gnus/gnus-sieve.el b/lisp/gnus/gnus-sieve.el
index 278e3a5d6f3..5d8f9b55deb 100644
--- a/lisp/gnus/gnus-sieve.el
+++ b/lisp/gnus/gnus-sieve.el
@@ -29,8 +29,6 @@
(require 'gnus)
(require 'gnus-sum)
-(require 'format-spec)
-(autoload 'sieve-mode "sieve-mode")
(eval-when-compile
(require 'sieve))
@@ -88,10 +86,10 @@ See the documentation for these variables and functions for details."
(save-buffer)
(shell-command
(format-spec gnus-sieve-update-shell-command
- (format-spec-make ?f gnus-sieve-file
- ?s (or (cadr (gnus-server-get-method
- nil gnus-sieve-select-method))
- "")))))
+ `((?f . ,gnus-sieve-file)
+ (?s . ,(or (cadr (gnus-server-get-method
+ nil gnus-sieve-select-method))
+ ""))))))
;;;###autoload
(defun gnus-sieve-generate ()
diff --git a/lisp/gnus/gssapi.el b/lisp/gnus/gssapi.el
index 218a1542e3a..485d58ad94e 100644
--- a/lisp/gnus/gssapi.el
+++ b/lisp/gnus/gssapi.el
@@ -25,8 +25,6 @@
;;; Code:
-(require 'format-spec)
-
(defcustom gssapi-program (list
(concat "gsasl %s %p "
"--mechanism GSSAPI "
@@ -53,12 +51,9 @@ tried until a successful connection is made."
(coding-system-for-write 'binary)
(process (start-process
name buffer shell-file-name shell-command-switch
- (format-spec
- cmd
- (format-spec-make
- ?s server
- ?p (number-to-string port)
- ?l user))))
+ (format-spec cmd `((?s . ,server)
+ (?p . ,(number-to-string port))
+ (?l . ,user)))))
response)
(when process
(while (and (memq (process-status process) '(open run))
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index acf35a376a9..43180726c45 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -24,7 +24,6 @@
;;; Code:
-(require 'format-spec)
(eval-when-compile
(require 'cl-lib)
(require 'imap))
@@ -769,14 +768,14 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
"Fetcher for single-file sources."
(mail-source-bind (file source)
(mail-source-run-script
- prescript (format-spec-make ?t mail-source-crash-box)
+ prescript `((?t . ,mail-source-crash-box))
prescript-delay)
(let ((mail-source-string (format "file:%s" path)))
(if (mail-source-movemail path mail-source-crash-box)
(prog1
(mail-source-callback callback path)
(mail-source-run-script
- postscript (format-spec-make ?t mail-source-crash-box))
+ postscript `((?t . ,mail-source-crash-box)))
(mail-source-delete-crash-box))
0))))
@@ -784,7 +783,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
"Fetcher for directory sources."
(mail-source-bind (directory source)
(mail-source-run-script
- prescript (format-spec-make ?t path) prescript-delay)
+ prescript `((?t . ,path)) prescript-delay)
(let ((found 0)
(mail-source-string (format "directory:%s" path)))
(dolist (file (directory-files
@@ -793,7 +792,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(funcall predicate file)
(mail-source-movemail file mail-source-crash-box))
(cl-incf found (mail-source-callback callback file))
- (mail-source-run-script postscript (format-spec-make ?t path))
+ (mail-source-run-script postscript `((?t . ,path)))
(mail-source-delete-crash-box)))
found)))
@@ -803,8 +802,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
;; fixme: deal with stream type in format specs
(mail-source-run-script
prescript
- (format-spec-make ?p password ?t mail-source-crash-box
- ?s server ?P port ?u user)
+ `((?p . ,password) (?t . ,mail-source-crash-box)
+ (?s . ,server) (?P . ,port) (?u . ,user))
prescript-delay)
(let ((from (format "%s:%s:%s" server user port))
(mail-source-string (format "pop:%s@%s" user server))
@@ -825,8 +824,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(mail-source-fetch-with-program
(format-spec
program
- (format-spec-make ?p password ?t mail-source-crash-box
- ?s server ?P port ?u user))))
+ `((?p . ,password) (?t . ,mail-source-crash-box)
+ (?s . ,server) (?P . ,port) (?u . ,user)))))
(function
(funcall function mail-source-crash-box))
;; The default is to use pop3.el.
@@ -863,8 +862,8 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
(setq mail-source-new-mail-available nil))
(mail-source-run-script
postscript
- (format-spec-make ?p password ?t mail-source-crash-box
- ?s server ?P port ?u user))
+ `((?p . ,password) (?t . ,mail-source-crash-box)
+ (?s . ,server) (?P . ,port) (?u . ,user)))
(mail-source-delete-crash-box)))
;; We nix out the password in case the error
;; was because of a wrong password being given.
@@ -1077,8 +1076,9 @@ This only works when `display-time' is enabled."
"Fetcher for imap sources."
(mail-source-bind (imap source)
(mail-source-run-script
- prescript (format-spec-make ?p password ?t mail-source-crash-box
- ?s server ?P port ?u user)
+ prescript
+ `((?p . ,password) (?t . ,mail-source-crash-box)
+ (?s . ,server) (?P . ,port) (?u . ,user))
prescript-delay)
(let ((from (format "%s:%s:%s" server user port))
(found 0)
@@ -1143,8 +1143,8 @@ This only works when `display-time' is enabled."
(kill-buffer buf)
(mail-source-run-script
postscript
- (format-spec-make ?p password ?t mail-source-crash-box
- ?s server ?P port ?u user))
+ `((?p . ,password) (?t . ,mail-source-crash-box)
+ (?s . ,server) (?P . ,port) (?u . ,user)))
found)))
(provide 'mail-source)
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 5a6827af762..fb560f0eab8 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -42,13 +42,12 @@
(require 'mail-parse)
(require 'mml)
(require 'rfc822)
-(require 'format-spec)
(require 'dired)
(require 'mm-util)
(require 'rfc2047)
(require 'puny)
-(require 'rmc) ; read-multiple-choice
-(eval-when-compile (require 'subr-x)) ; when-let*
+(require 'rmc) ; read-multiple-choice
+(eval-when-compile (require 'subr-x))
(autoload 'mailclient-send-it "mailclient")
@@ -440,8 +439,8 @@ whitespace)."
(defcustom message-elide-ellipsis "\n[...]\n\n"
"The string which is inserted for elided text.
-This is a format-spec string, and you can use %l to say how many
-lines were removed, and %c to say how many characters were
+This is a `format-spec' string, and you can use %l to say how
+many lines were removed, and %c to say how many characters were
removed."
:type 'string
:link '(custom-manual "(message)Various Commands")
@@ -3977,7 +3976,6 @@ This function uses `mail-citation-hook' if that is non-nil."
"Cite function in the standard Message manner."
(message-cite-original-1 nil))
-(autoload 'format-spec "format-spec")
(autoload 'gnus-date-get-time "gnus-util")
(defun message-insert-formatted-citation-line (&optional from date tz)
@@ -4002,20 +4000,18 @@ See `message-citation-line-format'."
(when (or message-reply-headers (and from date))
(unless from
(setq from (mail-header-from message-reply-headers)))
- (let* ((data (condition-case ()
- (funcall (if (boundp 'gnus-extract-address-components)
- gnus-extract-address-components
- 'mail-extract-address-components)
- from)
- (error nil)))
+ (let* ((data (ignore-errors
+ (funcall (or (bound-and-true-p
+ gnus-extract-address-components)
+ #'mail-extract-address-components)
+ from)))
(name (car data))
(fname name)
(lname name)
- (net (car (cdr data)))
- (name-or-net (or (car data)
- (car (cdr data)) from))
+ (net (cadr data))
+ (name-or-net (or name net from))
(time
- (when (string-match "%[^fnNFL]" message-citation-line-format)
+ (when (string-match-p "%[^FLNfn]" message-citation-line-format)
(cond ((numberp (car-safe date)) date) ;; backward compatibility
(date (gnus-date-get-time date))
(t
@@ -4024,68 +4020,53 @@ See `message-citation-line-format'."
(tz (or tz
(when (stringp date)
(nth 8 (parse-time-string date)))))
- (flist
- (let ((i ?A) lst)
- (when (stringp name)
- ;; Guess first name and last name:
- (let* ((names (delq
- nil
- (mapcar
- (lambda (x)
- (if (string-match "\\`\\(\\w\\|[-.]\\)+\\'"
- x)
- x
- nil))
- (split-string name "[ \t]+"))))
- (count (length names)))
- (cond ((= count 1)
- (setq fname (car names)
- lname ""))
- ((or (= count 2) (= count 3))
- (setq fname (car names)
- lname (mapconcat 'identity (cdr names) " ")))
- ((> count 3)
- (setq fname (mapconcat 'identity
- (butlast names (- count 2))
- " ")
- lname (mapconcat 'identity
- (nthcdr 2 names)
- " "))))
- (when (string-match "\\(.*\\),\\'" fname)
- (let ((newlname (match-string 1 fname)))
- (setq fname lname lname newlname)))))
- ;; The following letters are not used in `format-time-string':
- (push ?E lst) (push "<E>" lst)
- (push ?F lst) (push (or fname name-or-net) lst)
- ;; We might want to use "" instead of "<X>" later.
- (push ?J lst) (push "<J>" lst)
- (push ?K lst) (push "<K>" lst)
- (push ?L lst) (push lname lst)
- (push ?N lst) (push name-or-net lst)
- (push ?O lst) (push "<O>" lst)
- (push ?P lst) (push "<P>" lst)
- (push ?Q lst) (push "<Q>" lst)
- (push ?f lst) (push from lst)
- (push ?i lst) (push "<i>" lst)
- (push ?n lst) (push net lst)
- (push ?o lst) (push "<o>" lst)
- (push ?q lst) (push "<q>" lst)
- (push ?t lst) (push "<t>" lst)
- (push ?v lst) (push "<v>" lst)
- ;; Delegate the rest to `format-time-string':
- (while (<= i ?z)
- (when (and (not (memq i lst))
- ;; Skip (Z,a)
- (or (<= i ?Z)
- (>= i ?a)))
- (push i lst)
- (push (condition-case nil
- (format-time-string (format "%%%c" i) time tz)
- (error (format ">%c<" i)))
- lst))
- (setq i (1+ i)))
- (reverse lst)))
- (spec (apply 'format-spec-make flist)))
+ spec)
+ (when (stringp name)
+ ;; Guess first name and last name:
+ (let* ((names (seq-filter
+ (lambda (s)
+ (string-match-p (rx bos (+ (in word ?. ?-)) eos) s))
+ (split-string name "[ \t]+")))
+ (count (length names)))
+ (cond ((= count 1)
+ (setq fname (car names)
+ lname ""))
+ ((or (= count 2) (= count 3))
+ (setq fname (car names)
+ lname (string-join (cdr names) " ")))
+ ((> count 3)
+ (setq fname (string-join (butlast names (- count 2))
+ " ")
+ lname (string-join (nthcdr 2 names) " "))))
+ (when (string-match "\\(.*\\),\\'" fname)
+ (let ((newlname (match-string 1 fname)))
+ (setq fname lname lname newlname)))))
+ ;; The following letters are not used in `format-time-string':
+ (push (cons ?E "<E>") spec)
+ (push (cons ?F (or fname name-or-net)) spec)
+ ;; We might want to use "" instead of "<X>" later.
+ (push (cons ?J "<J>") spec)
+ (push (cons ?K "<K>") spec)
+ (push (cons ?L lname) spec)
+ (push (cons ?N name-or-net) spec)
+ (push (cons ?O "<O>") spec)
+ (push (cons ?P "<P>") spec)
+ (push (cons ?Q "<Q>") spec)
+ (push (cons ?f from) spec)
+ (push (cons ?i "<i>") spec)
+ (push (cons ?n net) spec)
+ (push (cons ?o "<o>") spec)
+ (push (cons ?q "<q>") spec)
+ (push (cons ?t "<t>") spec)
+ (push (cons ?v "<v>") spec)
+ ;; Delegate the rest to `format-time-string':
+ (dolist (c (nconc (number-sequence ?A ?Z)
+ (number-sequence ?a ?z)))
+ (unless (assq c spec)
+ (push (cons c (condition-case nil
+ (format-time-string (format "%%%c" c) time tz)
+ (error (format ">%c<" c))))
+ spec)))
(insert (format-spec message-citation-line-format spec)))
(newline)))
diff --git a/lisp/icomplete.el b/lisp/icomplete.el
index 60ef0247bae..3747ae3d281 100644
--- a/lisp/icomplete.el
+++ b/lisp/icomplete.el
@@ -300,7 +300,10 @@ if that doesn't produce a completion match."
(interactive)
(if (and (eq (char-before) ?/)
(eq (icomplete--category) 'file))
- (zap-up-to-char -1 ?/)
+ (save-excursion
+ (goto-char (1- (point)))
+ (when (search-backward "/" (point-min) t)
+ (delete-region (1+ (point)) (point-max))))
(call-interactively 'backward-delete-char)))
(defvar icomplete-fido-mode-map
diff --git a/lisp/image-dired.el b/lisp/image-dired.el
index 1cc38ba714b..6f297672caf 100644
--- a/lisp/image-dired.el
+++ b/lisp/image-dired.el
@@ -149,7 +149,6 @@
;;; Code:
(require 'dired)
-(require 'format-spec)
(require 'image-mode)
(require 'widget)
diff --git a/lisp/image-mode.el b/lisp/image-mode.el
index 480b2e6b26e..1bb213c2489 100644
--- a/lisp/image-mode.el
+++ b/lisp/image-mode.el
@@ -456,6 +456,7 @@ call."
(define-key map "sb" 'image-transform-fit-both)
(define-key map "ss" 'image-transform-set-scale)
(define-key map "sr" 'image-transform-set-rotation)
+ (define-key map "so" 'image-transform-original)
(define-key map "s0" 'image-transform-reset)
;; Multi-frame keys
@@ -521,8 +522,10 @@ call."
:help "Rotate the image"]
["Set Rotation..." image-transform-set-rotation
:help "Set rotation angle of the image"]
- ["Reset Transformations" image-transform-reset
- :help "Reset all image transformations"]
+ ["Original Size" image-transform-original
+ :help "Reset image to actual size"]
+ ["Reset to Default Size" image-transform-reset
+ :help "Reset all image transformations to initial size"]
"--"
["Show Thumbnails"
(lambda ()
@@ -807,8 +810,12 @@ was inserted."
filename))
;; If we have a `fit-width' or a `fit-height', don't limit
;; the size of the image to the window size.
- (edges (and (eq image-transform-resize t)
- (window-inside-pixel-edges (get-buffer-window))))
+ (edges (when (eq image-transform-resize t)
+ (window-inside-pixel-edges (get-buffer-window))))
+ (max-width (when edges
+ (- (nth 2 edges) (nth 0 edges))))
+ (max-height (when edges
+ (- (nth 3 edges) (nth 1 edges))))
(type (if (image--imagemagick-wanted-p filename)
'imagemagick
(image-type file-or-data nil data-p)))
@@ -824,14 +831,18 @@ was inserted."
(ignore-error exif-error
(exif-parse-buffer)))
0.0)))
+ ;; Swap width and height when changing orientation
+ ;; between portrait and landscape.
+ (when (and edges (zerop (mod (+ image-transform-rotation 90) 180)))
+ (setq max-width (prog1 max-height (setq max-height max-width))))
;; :scale 1: If we do not set this, create-image will apply
;; default scaling based on font size.
(setq image (if (not edges)
(create-image file-or-data type data-p :scale 1)
(create-image file-or-data type data-p :scale 1
- :max-width (- (nth 2 edges) (nth 0 edges))
- :max-height (- (nth 3 edges) (nth 1 edges)))))
+ :max-width max-width
+ :max-height max-height)))
;; Discard any stale image data before looking it up again.
(image-flush image)
@@ -1382,8 +1393,15 @@ ROTATION should be in degrees."
(setq image-transform-rotation (float (mod rotation 360)))
(image-toggle-display-image))
+(defun image-transform-original ()
+ "Display the current image with the original (actual) size and rotation."
+ (interactive)
+ (setq image-transform-resize nil
+ image-transform-scale 1)
+ (image-toggle-display-image))
+
(defun image-transform-reset ()
- "Display the current image with the default size and rotation."
+ "Display the current image with the default (initial) size and rotation."
(interactive)
(setq image-transform-resize image-auto-resize
image-transform-rotation 0.0
diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el
index 4538399c751..fdd726ff613 100644
--- a/lisp/net/dbus.el
+++ b/lisp/net/dbus.el
@@ -51,9 +51,6 @@
(unless (boundp 'dbus-debug)
(defvar dbus-debug nil))
-;; Pacify byte compiler.
-(eval-when-compile (require 'cl-lib))
-
(require 'xml)
(defconst dbus-service-dbus "org.freedesktop.DBus"
@@ -169,10 +166,7 @@ Otherwise, return result of last form in BODY, or all other errors."
`(condition-case err
(progn ,@body)
(dbus-error (when dbus-debug (signal (car err) (cdr err))))))
-(font-lock-add-keywords 'emacs-lisp-mode '("\\<dbus-ignore-errors\\>"))
-(define-obsolete-variable-alias 'dbus-event-error-hooks
- 'dbus-event-error-functions "24.3")
(defvar dbus-event-error-functions '(dbus-notice-synchronous-call-errors)
"Functions to be called when a D-Bus error happens in the event handler.
Every function must accept two arguments, the event and the error variable
@@ -181,8 +175,8 @@ caught in `condition-case' by `dbus-error'.")
;;; Basic D-Bus message functions.
-(defvar dbus-return-values-table (make-hash-table :test 'equal)
- "Hash table for temporary storing arguments of reply messages.
+(defvar dbus-return-values-table (make-hash-table :test #'equal)
+ "Hash table for temporarily storing arguments of reply messages.
A key in this hash table is a list (:serial BUS SERIAL), like in
`dbus-registered-objects-table'. BUS is either a Lisp symbol,
`:system' or `:session', or a string denoting the bus address.
@@ -225,10 +219,10 @@ SERVICE is the D-Bus service name to be used. PATH is the D-Bus
object path SERVICE is registered at. INTERFACE is an interface
offered by SERVICE. It must provide METHOD.
-If the parameter `:timeout' is given, the following integer TIMEOUT
-specifies the maximum number of milliseconds the method call must
-return. The default value is 25,000. If the method call doesn't
-return in time, a D-Bus error is raised.
+If the parameter `:timeout' is given, the following integer
+TIMEOUT specifies the maximum number of milliseconds before the
+method call must return. The default value is 25,000. If the
+method call doesn't return in time, a D-Bus error is raised.
All other arguments ARGS are passed to METHOD as arguments. They are
converted into D-Bus types via the following rules:
@@ -248,14 +242,14 @@ Lisp objects. The type conversion happens the other direction as for
input arguments. It follows the mapping rules:
DBUS_TYPE_BOOLEAN => t or nil
- DBUS_TYPE_BYTE => number
- DBUS_TYPE_UINT16 => number
+ DBUS_TYPE_BYTE => natural number
+ DBUS_TYPE_UINT16 => natural number
DBUS_TYPE_INT16 => integer
- DBUS_TYPE_UINT32 => number or float
- DBUS_TYPE_UNIX_FD => number or float
- DBUS_TYPE_INT32 => integer or float
- DBUS_TYPE_UINT64 => number or float
- DBUS_TYPE_INT64 => integer or float
+ DBUS_TYPE_UINT32 => natural number
+ DBUS_TYPE_UNIX_FD => natural number
+ DBUS_TYPE_INT32 => integer
+ DBUS_TYPE_UINT64 => natural number
+ DBUS_TYPE_INT64 => integer
DBUS_TYPE_DOUBLE => float
DBUS_TYPE_STRING => string
DBUS_TYPE_OBJECT_PATH => string
@@ -268,9 +262,9 @@ input arguments. It follows the mapping rules:
Example:
\(dbus-call-method
- :session \"org.gnome.seahorse\" \"/org/gnome/seahorse/keys/openpgp\"
- \"org.gnome.seahorse.Keys\" \"GetKeyField\"
- \"openpgp:657984B8C7A966DD\" \"simple-name\")
+ :session \"org.gnome.seahorse\" \"/org/gnome/seahorse/keys/openpgp\"
+ \"org.gnome.seahorse.Keys\" \"GetKeyField\"
+ \"openpgp:657984B8C7A966DD\" \"simple-name\")
=> (t (\"Philip R. Zimmermann\"))
@@ -278,9 +272,9 @@ If the result of the METHOD call is just one value, the converted Lisp
object is returned instead of a list containing this single Lisp object.
\(dbus-call-method
- :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
- \"org.freedesktop.Hal.Device\" \"GetPropertyString\"
- \"system.kernel.machine\")
+ :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
+ \"org.freedesktop.Hal.Device\" \"GetPropertyString\"
+ \"system.kernel.machine\")
=> \"i686\""
@@ -301,8 +295,8 @@ object is returned instead of a list containing this single Lisp object.
(check-interval 0.001)
(key
(apply
- 'dbus-message-internal dbus-message-type-method-call
- bus service path interface method 'dbus-call-method-handler args))
+ #'dbus-message-internal dbus-message-type-method-call
+ bus service path interface method #'dbus-call-method-handler args))
(result (cons :pending nil)))
;; Wait until `dbus-call-method-handler' has put the result into
@@ -338,10 +332,6 @@ object is returned instead of a list containing this single Lisp object.
(cdr result))
(remhash key dbus-return-values-table))))
-;; `dbus-call-method' works non-blocking now.
-(defalias 'dbus-call-method-non-blocking 'dbus-call-method)
-(make-obsolete 'dbus-call-method-non-blocking 'dbus-call-method "24.3")
-
(defun dbus-call-method-asynchronously
(bus service path interface method handler &rest args)
"Call METHOD on the D-Bus BUS asynchronously.
@@ -357,10 +347,10 @@ HANDLER is a Lisp function, which is called when the corresponding
return message has arrived. If HANDLER is nil, no return message
will be expected.
-If the parameter `:timeout' is given, the following integer TIMEOUT
-specifies the maximum number of milliseconds the method call must
-return. The default value is 25,000. If the method call doesn't
-return in time, a D-Bus error is raised.
+If the parameter `:timeout' is given, the following integer
+TIMEOUT specifies the maximum number of milliseconds before the
+method call must return. The default value is 25,000. If the
+method call doesn't return in time, a D-Bus error is raised.
All other arguments ARGS are passed to METHOD as arguments. They are
converted into D-Bus types via the following rules:
@@ -377,19 +367,19 @@ type symbols, see Info node `(dbus)Type Conversion'.
If HANDLER is a Lisp function, the function returns a key into the
hash table `dbus-registered-objects-table'. The corresponding entry
-in the hash table is removed, when the return message has been arrived,
+in the hash table is removed, when the return message arrives,
and HANDLER is called.
Example:
\(dbus-call-method-asynchronously
- :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
- \"org.freedesktop.Hal.Device\" \"GetPropertyString\" \\='message
- \"system.kernel.machine\")
+ :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\"
+ \"org.freedesktop.Hal.Device\" \"GetPropertyString\" \\='message
+ \"system.kernel.machine\")
- => (:serial :system 2)
+ -| i686
- -| i686"
+ => (:serial :system 2)"
(or (featurep 'dbusbind)
(signal 'dbus-error (list "Emacs not compiled with dbus support")))
@@ -406,7 +396,7 @@ Example:
(or (null handler) (functionp handler)
(signal 'wrong-type-argument (list 'functionp handler)))
- (apply 'dbus-message-internal dbus-message-type-method-call
+ (apply #'dbus-message-internal dbus-message-type-method-call
bus service path interface method handler args))
(defun dbus-send-signal (bus service path interface signal &rest args)
@@ -438,8 +428,8 @@ type symbols, see Info node `(dbus)Type Conversion'.
Example:
\(dbus-send-signal
- :session nil \"/org/gnu/Emacs\" \"org.gnu.Emacs.FileManager\"
- \"FileModified\" \"/home/albinus/.emacs\")"
+ :session nil \"/org/gnu/Emacs\" \"org.gnu.Emacs.FileManager\"
+ \"FileModified\" \"/home/albinus/.emacs\")"
(or (featurep 'dbusbind)
(signal 'dbus-error (list "Emacs not compiled with dbus support")))
@@ -454,7 +444,7 @@ Example:
(or (stringp signal)
(signal 'wrong-type-argument (list 'stringp signal)))
- (apply 'dbus-message-internal dbus-message-type-signal
+ (apply #'dbus-message-internal dbus-message-type-signal
bus service path interface signal args))
(defun dbus-method-return-internal (bus service serial &rest args)
@@ -470,7 +460,7 @@ This is an internal function, it shall not be used outside dbus.el."
(or (natnump serial)
(signal 'wrong-type-argument (list 'natnump serial)))
- (apply 'dbus-message-internal dbus-message-type-method-return
+ (apply #'dbus-message-internal dbus-message-type-method-return
bus service serial args))
(defun dbus-method-error-internal (bus service serial &rest args)
@@ -486,7 +476,7 @@ This is an internal function, it shall not be used outside dbus.el."
(or (natnump serial)
(signal 'wrong-type-argument (list 'natnump serial)))
- (apply 'dbus-message-internal dbus-message-type-error
+ (apply #'dbus-message-internal dbus-message-type-error
bus service serial args))
@@ -552,13 +542,13 @@ placed in the queue.
`:already-owner': Service is already the primary owner."
;; Add Peer handler.
- (dbus-register-method
- bus service nil dbus-interface-peer "Ping" 'dbus-peer-handler 'dont-register)
+ (dbus-register-method bus service nil dbus-interface-peer "Ping"
+ #'dbus-peer-handler 'dont-register)
;; Add ObjectManager handler.
(dbus-register-method
bus service nil dbus-interface-objectmanager "GetManagedObjects"
- 'dbus-managed-objects-handler 'dont-register)
+ #'dbus-managed-objects-handler 'dont-register)
(let ((arg 0)
reply)
@@ -625,17 +615,17 @@ SERVICE is the D-Bus service name used by the sending D-Bus object.
It can be either a known name or the unique name of the D-Bus object
sending the signal.
-PATH is the D-Bus object path SERVICE is registered. INTERFACE
-is an interface offered by SERVICE. It must provide SIGNAL.
-HANDLER is a Lisp function to be called when the signal is
-received. It must accept as arguments the values SIGNAL is
+PATH is the D-Bus object path SERVICE is registered at.
+INTERFACE is an interface offered by SERVICE. It must provide
+SIGNAL. HANDLER is a Lisp function to be called when the signal
+is received. It must accept as arguments the values SIGNAL is
sending.
SERVICE, PATH, INTERFACE and SIGNAL can be nil. This is
interpreted as a wildcard for the respective argument.
The remaining arguments ARGS can be keywords or keyword string pairs.
-The meaning is as follows:
+Their meaning is as follows:
`:argN' STRING:
`:pathN' STRING: This stands for the Nth argument of the
@@ -643,8 +633,9 @@ signal. `:pathN' arguments can be used for object path wildcard
matches as specified by D-Bus, while an `:argN' argument
requires an exact match.
-`:arg-namespace' STRING: Register for the signals, which first
-argument defines the service or interface namespace STRING.
+`:arg-namespace' STRING: Register for those signals, whose first
+argument names a service or interface within the namespace
+STRING.
`:path-namespace' STRING: Register for the object path namespace
STRING. All signals sent from an object path, which has STRING as
@@ -660,8 +651,8 @@ Example:
(message \"Device %s added\" device))
\(dbus-register-signal
- :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/Manager\"
- \"org.freedesktop.Hal.Manager\" \"DeviceAdded\" \\='my-signal-handler)
+ :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/Manager\"
+ \"org.freedesktop.Hal.Manager\" \"DeviceAdded\" \\='my-signal-handler)
=> ((:signal :system \"org.freedesktop.Hal.Manager\" \"DeviceAdded\")
(\"org.freedesktop.Hal\" \"/org/freedesktop/Hal/Manager\" my-signal-handler))
@@ -680,7 +671,7 @@ Example:
(if (and (stringp service)
(not (zerop (length service)))
(not (string-equal service dbus-service-dbus))
- (not (string-match "^:" service)))
+ (/= (string-to-char service) ?:))
(setq uname (dbus-get-name-owner bus service))
(setq uname service))
@@ -709,7 +700,7 @@ Example:
;; `:arg0' .. `:arg63', `:path0' .. `:path63'.
((and (keywordp key)
(string-match
- "^:\\(arg\\|path\\)\\([[:digit:]]+\\)$"
+ "\\`:\\(arg\\|path\\)\\([[:digit:]]+\\)\\'"
(symbol-name key)))
(setq counter (match-string 2 (symbol-name key))
args (cdr args)
@@ -725,9 +716,7 @@ Example:
"path" "")
value))
;; `:arg-namespace', `:path-namespace'.
- ((and (keywordp key)
- (string-match
- "^:\\(arg\\|path\\)-namespace$" (symbol-name key)))
+ ((memq key '(:arg-namespace :path-namespace))
(setq args (cdr args)
value (car args))
(unless (stringp value)
@@ -735,8 +724,7 @@ Example:
(list "Wrong argument" key value)))
(format
",%s='%s'"
- (if (string-equal (match-string 1 (symbol-name key)) "path")
- "path_namespace" "arg0namespace")
+ (if (eq key :path-namespace) "path_namespace" "arg0namespace")
value))
;; `:eavesdrop'.
((eq key :eavesdrop)
@@ -750,11 +738,11 @@ Example:
bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
"AddMatch" rule)
(dbus-error
- (if (not (string-match "eavesdrop" rule))
+ (if (not (string-match-p "eavesdrop" rule))
(signal (car err) (cdr err))
;; The D-Bus spec says we shall fall back to a rule without eavesdrop.
(when dbus-debug (message "Removing eavesdrop from rule %s" rule))
- (setq rule (replace-regexp-in-string ",eavesdrop='true'" "" rule))
+ (setq rule (replace-regexp-in-string ",eavesdrop='true'" "" rule t t))
(dbus-call-method
bus dbus-service-dbus dbus-path-dbus dbus-interface-dbus
"AddMatch" rule))))
@@ -773,24 +761,24 @@ Example:
(defun dbus-register-method
(bus service path interface method handler &optional dont-register-service)
- "Register for method METHOD on the D-Bus BUS.
+ "Register METHOD on the D-Bus BUS.
BUS is either a Lisp symbol, `:system' or `:session', or a string
denoting the bus address.
SERVICE is the D-Bus service name of the D-Bus object METHOD is
-registered for. It must be a known name (See discussion of
+registered for. It must be a known name (see discussion of
DONT-REGISTER-SERVICE below).
-PATH is the D-Bus object path SERVICE is registered (See discussion of
-DONT-REGISTER-SERVICE below). INTERFACE is the interface offered by
-SERVICE. It must provide METHOD.
+PATH is the D-Bus object path SERVICE is registered at (see
+discussion of DONT-REGISTER-SERVICE below). INTERFACE is the
+interface offered by SERVICE. It must provide METHOD.
HANDLER is a Lisp function to be called when a method call is
received. It must accept the input arguments of METHOD. The return
value of HANDLER is used for composing the returning D-Bus message.
-In case HANDLER shall return a reply message with an empty argument
-list, HANDLER must return the symbol `:ignore'.
+If HANDLER returns a reply message with an empty argument list,
+HANDLER must return the symbol `:ignore'.
When DONT-REGISTER-SERVICE is non-nil, the known name SERVICE is not
registered. This means that other D-Bus clients have no way of
@@ -888,26 +876,21 @@ association to the service from D-Bus."
;;; D-Bus type conversion.
(defun dbus-string-to-byte-array (string)
- "Transform STRING to list (:array :byte c1 :byte c2 ...).
-STRING shall be UTF8 coded."
+ "Transform STRING to list (:array :byte C1 :byte C2 ...).
+STRING shall be UTF-8 coded."
(if (zerop (length string))
'(:array :signature "y")
- (let (result)
- (dolist (elt (string-to-list string) (append '(:array) result))
- (setq result (append result (list :byte elt)))))))
+ (cons :array (mapcan (lambda (c) (list :byte c)) string))))
(defun dbus-byte-array-to-string (byte-array &optional multibyte)
- "Transform BYTE-ARRAY into UTF8 coded string.
+ "Transform BYTE-ARRAY into UTF-8 coded string.
BYTE-ARRAY must be a list of structure (c1 c2 ...), or a byte
array as produced by `dbus-string-to-byte-array'. The resulting
string is unibyte encoded, unless MULTIBYTE is non-nil."
(apply
- (if multibyte 'string 'unibyte-string)
- (if (equal byte-array '(:array :signature "y"))
- nil
- (let (result)
- (dolist (elt byte-array result)
- (when (characterp elt) (setq result (append result `(,elt)))))))))
+ (if multibyte #'string #'unibyte-string)
+ (unless (equal byte-array '(:array :signature "y"))
+ (seq-filter #'characterp byte-array))))
(defun dbus-escape-as-identifier (string)
"Escape an arbitrary STRING so it follows the rules for a C identifier.
@@ -920,18 +903,18 @@ lower-case hex digits:
\"0123abc_xyz\\x01\\xff\" -> \"_30123abc_5fxyz_01_ff\"
-i.e. similar to URI encoding, but with \"_\" taking the role of \"%\",
-and a smaller allowed set. As a special case, \"\" is escaped to
-\"_\".
+i.e. similar to URI encoding, but with \"_\" taking the role of
+\"%\", and a smaller allowed set. As a special case, \"\" is
+escaped to \"_\".
Returns the escaped string. Algorithm taken from
telepathy-glib's `tp_escape_as_identifier'."
(if (zerop (length string))
"_"
(replace-regexp-in-string
- "^[0-9]\\|[^A-Za-z0-9]"
+ "\\`[0-9]\\|[^A-Za-z0-9]"
(lambda (x) (format "_%2x" (aref x 0)))
- string)))
+ string nil t)))
(defun dbus-unescape-from-identifier (string)
"Retrieve the original string from the encoded STRING as unibyte string.
@@ -941,7 +924,7 @@ STRING must have been encoded with `dbus-escape-as-identifier'."
(replace-regexp-in-string
"_.."
(lambda (x) (byte-to-string (string-to-number (substring x 1) 16)))
- string)))
+ string nil t)))
;;; D-Bus events.
@@ -963,8 +946,8 @@ the function which has been registered for this message. ARGS
are the arguments passed to HANDLER, when it is called during
event handling in `dbus-handle-event'.
-This function raises a `dbus-error' signal in case the event is
-not well formed."
+This function signals a `dbus-error' if the event is not well
+formed."
(when dbus-debug (message "DBus-Event %s" event))
(unless (and (listp event)
(eq (car event) 'dbus-event)
@@ -1019,7 +1002,7 @@ If the HANDLER returns a `dbus-error', it is propagated as return message."
(if (eq result :ignore)
(dbus-method-return-internal
(nth 1 event) (nth 4 event) (nth 3 event))
- (apply 'dbus-method-return-internal
+ (apply #'dbus-method-return-internal
(nth 1 event) (nth 4 event) (nth 3 event)
(if (consp result) result (list result)))))))
;; Error handling.
@@ -1038,16 +1021,16 @@ If the HANDLER returns a `dbus-error', it is propagated as return message."
"Return the bus name the event is coming from.
The result is either a Lisp symbol, `:system' or `:session', or a
string denoting the bus address. EVENT is a D-Bus event, see
-`dbus-check-event'. This function raises a `dbus-error' signal
-in case the event is not well formed."
+`dbus-check-event'. This function signals a `dbus-error' if the
+event is not well formed."
(dbus-check-event event)
(nth 1 event))
(defun dbus-event-message-type (event)
"Return the message type of the corresponding D-Bus message.
The result is a number. EVENT is a D-Bus event, see
-`dbus-check-event'. This function raises a `dbus-error' signal
-in case the event is not well formed."
+`dbus-check-event'. This function signals a `dbus-error' if the
+event is not well formed."
(dbus-check-event event)
(nth 2 event))
@@ -1055,41 +1038,40 @@ in case the event is not well formed."
"Return the serial number of the corresponding D-Bus message.
The result is a number. The serial number is needed for
generating a reply message. EVENT is a D-Bus event, see
-`dbus-check-event'. This function raises a `dbus-error' signal
-in case the event is not well formed."
+`dbus-check-event'. This function signals a `dbus-error' if the
+event is not well formed."
(dbus-check-event event)
(nth 3 event))
(defun dbus-event-service-name (event)
"Return the name of the D-Bus object the event is coming from.
The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
-This function raises a `dbus-error' signal in case the event is
-not well formed."
+This function signals a `dbus-error' if the event is not well
+formed."
(dbus-check-event event)
(nth 4 event))
(defun dbus-event-path-name (event)
"Return the object path of the D-Bus object the event is coming from.
The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
-This function raises a `dbus-error' signal in case the event is
-not well formed."
+This function signals a `dbus-error' if the event is not well
+formed."
(dbus-check-event event)
(nth 5 event))
(defun dbus-event-interface-name (event)
"Return the interface name of the D-Bus object the event is coming from.
The result is a string. EVENT is a D-Bus event, see `dbus-check-event'.
-This function raises a `dbus-error' signal in case the event is
-not well formed."
+This function signals a `dbus-error' if the event is not well
+formed."
(dbus-check-event event)
(nth 6 event))
(defun dbus-event-member-name (event)
"Return the member name the event is coming from.
-It is either a signal name or a method name. The result is a
+It is either a signal name or a method name. The result is a
string. EVENT is a D-Bus event, see `dbus-check-event'. This
-function raises a `dbus-error' signal in case the event is not
-well formed."
+function signals a `dbus-error' if the event is not well formed."
(dbus-check-event event)
(nth 7 event))
@@ -1097,10 +1079,10 @@ well formed."
;;; D-Bus registered names.
(defun dbus-list-activatable-names (&optional bus)
- "Return the D-Bus service names which can be activated as list.
-If BUS is left nil, `:system' is assumed. The result is a list
-of strings, which is nil when there are no activatable service
-names at all."
+ "Return a list of the D-Bus service names which can be activated.
+BUS defaults to `:system' when nil or omitted. The result is a
+list of strings, which is nil when there are no activatable
+service names at all."
(dbus-ignore-errors
(dbus-call-method
(or bus :system) dbus-service-dbus
@@ -1119,15 +1101,14 @@ unique names for services."
(defun dbus-list-known-names (bus)
"Retrieve all services which correspond to a known name in BUS.
A service has a known name if it doesn't start with \":\"."
- (let (result)
- (dolist (name (dbus-list-names bus) (nreverse result))
- (unless (string-equal ":" (substring name 0 1))
- (push name result)))))
+ (seq-remove (lambda (name)
+ (= (string-to-char name) ?:))
+ (dbus-list-names bus)))
(defun dbus-list-queued-owners (bus service)
"Return the unique names registered at D-Bus BUS and queued for SERVICE.
-The result is a list of strings, or nil when there are no
-queued name owners service names at all."
+The result is a list of strings, or nil when there are no queued
+name owner service names at all."
(dbus-ignore-errors
(dbus-call-method
bus dbus-service-dbus dbus-path-dbus
@@ -1144,13 +1125,13 @@ The result is either a string, or nil if there is no name owner."
(defun dbus-ping (bus service &optional timeout)
"Check whether SERVICE is registered for D-Bus BUS.
TIMEOUT, a nonnegative integer, specifies the maximum number of
-milliseconds `dbus-ping' must return. The default value is 25,000.
+milliseconds before `dbus-ping' must return. The default value
+is 25,000.
-Note, that this autoloads SERVICE if it is not running yet. If
-it shall be checked whether SERVICE is already running, one shall
-apply
+Note, that this autoloads SERVICE if it is not running yet. To
+check whether SERVICE is already running, you can instead write
- (member service \(dbus-list-known-names bus))"
+ (member service (dbus-list-known-names bus))"
;; "Ping" raises a D-Bus error if SERVICE does not exist.
;; Otherwise, it returns silently with nil.
(condition-case nil
@@ -1182,6 +1163,18 @@ It will be registered for all objects created by `dbus-register-service'."
;;; D-Bus introspection.
+(defsubst dbus--introspect-names (object tag)
+ "Return the names of the children of OBJECT with TAG."
+ (mapcar (lambda (elt)
+ (dbus-introspect-get-attribute elt "name"))
+ (xml-get-children object tag)))
+
+(defsubst dbus--introspect-name (object tag name)
+ "Return the first child of OBJECT with TAG, whose name is NAME."
+ (seq-find (lambda (elt)
+ (string-equal (dbus-introspect-get-attribute elt "name") name))
+ (xml-get-children object tag)))
+
(defun dbus-introspect (bus service path)
"Return all interfaces and sub-nodes of SERVICE,
registered at object path PATH at bus BUS.
@@ -1197,17 +1190,25 @@ XML format."
bus service path dbus-interface-introspectable "Introspect"
:timeout 1000)))
+(defalias 'dbus--parse-xml-buffer
+ (if (libxml-available-p)
+ (lambda ()
+ (xml-remove-comments (point-min) (point-max))
+ (libxml-parse-xml-region (point-min) (point-max)))
+ (lambda ()
+ (car (xml-parse-region (point-min) (point-max)))))
+ "Compatibility shim for `libxml-parse-xml-region'.")
+
(defun dbus-introspect-xml (bus service path)
"Return the introspection data of SERVICE in D-Bus BUS at object path PATH.
The data are a parsed list. The root object is a \"node\",
representing the object path PATH. The root object can contain
\"interface\" and further \"node\" objects."
- ;; We don't want to raise errors.
- (xml-node-name
- (ignore-errors
- (with-temp-buffer
- (insert (dbus-introspect bus service path))
- (xml-parse-region (point-min) (point-max))))))
+ (with-temp-buffer
+ ;; We don't want to raise errors.
+ (ignore-errors
+ (insert (dbus-introspect bus service path))
+ (dbus--parse-xml-buffer))))
(defun dbus-introspect-get-attribute (object attribute)
"Return the ATTRIBUTE value of D-Bus introspection OBJECT.
@@ -1219,190 +1220,137 @@ the D-Bus specification."
"Return all node names of SERVICE in D-Bus BUS at object path PATH.
It returns a list of strings. The node names stand for further
object paths of the D-Bus service."
- (let ((object (dbus-introspect-xml bus service path))
- result)
- (dolist (elt (xml-get-children object 'node) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names (dbus-introspect-xml bus service path) 'node))
(defun dbus-introspect-get-all-nodes (bus service path)
"Return all node names of SERVICE in D-Bus BUS at object path PATH.
It returns a list of strings, which are further object paths of SERVICE."
- (let ((result (list path)))
- (dolist (elt
- (dbus-introspect-get-node-names bus service path)
- result)
- (setq elt (expand-file-name elt path))
- (setq result
- (append result (dbus-introspect-get-all-nodes bus service elt))))))
+ (cons path (mapcan (lambda (elt)
+ (setq elt (expand-file-name elt path))
+ (dbus-introspect-get-all-nodes bus service elt))
+ (dbus-introspect-get-node-names bus service path))))
(defun dbus-introspect-get-interface-names (bus service path)
"Return all interface names of SERVICE in D-Bus BUS at object path PATH.
It returns a list of strings.
-There will be always the default interface
-\"org.freedesktop.DBus.Introspectable\". Another default
-interface is \"org.freedesktop.DBus.Properties\". If present,
-\"interface\" objects can also have \"property\" objects as
-children, beside \"method\" and \"signal\" objects."
- (let ((object (dbus-introspect-xml bus service path))
- result)
- (dolist (elt (xml-get-children object 'interface) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+The default interface \"org.freedesktop.DBus.Introspectable\" is
+always present. Another default interface is
+\"org.freedesktop.DBus.Properties\". If present, \"interface\"
+objects can also have \"property\" objects as children, beside
+\"method\" and \"signal\" objects."
+ (dbus--introspect-names (dbus-introspect-xml bus service path) 'interface))
(defun dbus-introspect-get-interface (bus service path interface)
"Return the INTERFACE of SERVICE in D-Bus BUS at object path PATH.
-The return value is an XML object. INTERFACE must be a string,
-element of the list returned by `dbus-introspect-get-interface-names'.
-The resulting \"interface\" object can contain \"method\", \"signal\",
+The return value is an XML object. INTERFACE must be a string
+and a member of the list returned by
+`dbus-introspect-get-interface-names'. The resulting
+\"interface\" object can contain \"method\", \"signal\",
\"property\" and \"annotation\" children."
- (let ((elt (xml-get-children
- (dbus-introspect-xml bus service path) 'interface)))
- (while (and elt
- (not (string-equal
- interface
- (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+ (dbus--introspect-name (dbus-introspect-xml bus service path)
+ 'interface interface))
(defun dbus-introspect-get-method-names (bus service path interface)
"Return a list of strings of all method names of INTERFACE.
SERVICE is a service of D-Bus BUS at object path PATH."
- (let ((object (dbus-introspect-get-interface bus service path interface))
- result)
- (dolist (elt (xml-get-children object 'method) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names
+ (dbus-introspect-get-interface bus service path interface) 'method))
(defun dbus-introspect-get-method (bus service path interface method)
- "Return method METHOD of interface INTERFACE as XML object.
+ "Return method METHOD of interface INTERFACE as an XML object.
It must be located at SERVICE in D-Bus BUS at object path PATH.
-METHOD must be a string, element of the list returned by
+METHOD must be a string and a member of the list returned by
`dbus-introspect-get-method-names'. The resulting \"method\"
object can contain \"arg\" and \"annotation\" children."
- (let ((elt (xml-get-children
- (dbus-introspect-get-interface bus service path interface)
- 'method)))
- (while (and elt
- (not (string-equal
- method (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+ (dbus--introspect-name
+ (dbus-introspect-get-interface bus service path interface)
+ 'method method))
(defun dbus-introspect-get-signal-names (bus service path interface)
"Return a list of strings of all signal names of INTERFACE.
SERVICE is a service of D-Bus BUS at object path PATH."
- (let ((object (dbus-introspect-get-interface bus service path interface))
- result)
- (dolist (elt (xml-get-children object 'signal) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names
+ (dbus-introspect-get-interface bus service path interface) 'signal))
(defun dbus-introspect-get-signal (bus service path interface signal)
- "Return signal SIGNAL of interface INTERFACE as XML object.
+ "Return signal SIGNAL of interface INTERFACE as an XML object.
It must be located at SERVICE in D-Bus BUS at object path PATH.
SIGNAL must be a string, element of the list returned by
`dbus-introspect-get-signal-names'. The resulting \"signal\"
object can contain \"arg\" and \"annotation\" children."
- (let ((elt (xml-get-children
- (dbus-introspect-get-interface bus service path interface)
- 'signal)))
- (while (and elt
- (not (string-equal
- signal (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+ (dbus--introspect-name
+ (dbus-introspect-get-interface bus service path interface)
+ 'signal signal))
(defun dbus-introspect-get-property-names (bus service path interface)
"Return a list of strings of all property names of INTERFACE.
SERVICE is a service of D-Bus BUS at object path PATH."
- (let ((object (dbus-introspect-get-interface bus service path interface))
- result)
- (dolist (elt (xml-get-children object 'property) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names
+ (dbus-introspect-get-interface bus service path interface) 'property))
(defun dbus-introspect-get-property (bus service path interface property)
- "Return PROPERTY of INTERFACE as XML object.
+ "Return PROPERTY of INTERFACE as an XML object.
It must be located at SERVICE in D-Bus BUS at object path PATH.
-PROPERTY must be a string, element of the list returned by
+PROPERTY must be a string and a member of the list returned by
`dbus-introspect-get-property-names'. The resulting PROPERTY
object can contain \"annotation\" children."
- (let ((elt (xml-get-children
- (dbus-introspect-get-interface bus service path interface)
- 'property)))
- (while (and elt
- (not (string-equal
- property
- (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+ (dbus--introspect-name
+ (dbus-introspect-get-interface bus service path interface)
+ 'property property))
(defun dbus-introspect-get-annotation-names
(bus service path interface &optional name)
- "Return all annotation names as list of strings.
+ "Return all annotation names as a list of strings.
If NAME is nil, the annotations are children of INTERFACE,
otherwise NAME must be a \"method\", \"signal\", or \"property\"
object, where the annotations belong to."
- (let ((object
- (if name
- (or (dbus-introspect-get-method bus service path interface name)
- (dbus-introspect-get-signal bus service path interface name)
- (dbus-introspect-get-property bus service path interface name))
- (dbus-introspect-get-interface bus service path interface)))
- result)
- (dolist (elt (xml-get-children object 'annotation) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names
+ (if name
+ (or (dbus-introspect-get-method bus service path interface name)
+ (dbus-introspect-get-signal bus service path interface name)
+ (dbus-introspect-get-property bus service path interface name))
+ (dbus-introspect-get-interface bus service path interface))
+ 'annotation))
(defun dbus-introspect-get-annotation
(bus service path interface name annotation)
- "Return ANNOTATION as XML object.
+ "Return ANNOTATION as an XML object.
If NAME is nil, ANNOTATION is a child of INTERFACE, otherwise
NAME must be the name of a \"method\", \"signal\", or
\"property\" object, where the ANNOTATION belongs to."
- (let ((elt (xml-get-children
- (if name
- (or (dbus-introspect-get-method
- bus service path interface name)
- (dbus-introspect-get-signal
- bus service path interface name)
- (dbus-introspect-get-property
- bus service path interface name))
- (dbus-introspect-get-interface bus service path interface))
- 'annotation)))
- (while (and elt
- (not (string-equal
- annotation
- (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+ (dbus--introspect-name
+ (if name
+ (or (dbus-introspect-get-method bus service path interface name)
+ (dbus-introspect-get-signal bus service path interface name)
+ (dbus-introspect-get-property bus service path interface name))
+ (dbus-introspect-get-interface bus service path interface))
+ 'annotation annotation))
(defun dbus-introspect-get-argument-names (bus service path interface name)
- "Return a list of all argument names as list of strings.
+ "Return a list of all argument names as a list of strings.
NAME must be a \"method\" or \"signal\" object.
Argument names are optional, the function can return nil
therefore, even if the method or signal has arguments."
- (let ((object
- (or (dbus-introspect-get-method bus service path interface name)
- (dbus-introspect-get-signal bus service path interface name)))
- result)
- (dolist (elt (xml-get-children object 'arg) (nreverse result))
- (push (dbus-introspect-get-attribute elt "name") result))))
+ (dbus--introspect-names
+ (or (dbus-introspect-get-method bus service path interface name)
+ (dbus-introspect-get-signal bus service path interface name))
+ 'arg))
(defun dbus-introspect-get-argument (bus service path interface name arg)
"Return argument ARG as XML object.
-NAME must be a \"method\" or \"signal\" object. ARG must be a string,
-element of the list returned by `dbus-introspect-get-argument-names'."
- (let ((elt (xml-get-children
- (or (dbus-introspect-get-method bus service path interface name)
- (dbus-introspect-get-signal bus service path interface name))
- 'arg)))
- (while (and elt
- (not (string-equal
- arg (dbus-introspect-get-attribute (car elt) "name"))))
- (setq elt (cdr elt)))
- (car elt)))
+NAME must be a \"method\" or \"signal\" object. ARG must be a
+string and a member of the list returned by
+`dbus-introspect-get-argument-names'."
+ (dbus--introspect-name
+ (or (dbus-introspect-get-method bus service path interface name)
+ (dbus-introspect-get-signal bus service path interface name))
+ 'arg arg))
(defun dbus-introspect-get-signature
(bus service path interface name &optional direction)
- "Return signature of a `method' or `signal', represented by NAME, as string.
+ "Return signature of a `method' or `signal' represented by NAME as a string.
If NAME is a `method', DIRECTION can be either \"in\" or \"out\".
If DIRECTION is nil, \"in\" is assumed.
@@ -1450,9 +1398,8 @@ valid D-Bus value, or nil if there is no PROPERTY."
(defun dbus-set-property (bus service path interface property value)
"Set value of PROPERTY of INTERFACE to VALUE.
-It will be checked at BUS, SERVICE, PATH. When the value has
-been set successful, the result is VALUE. Otherwise, nil is
-returned."
+It will be checked at BUS, SERVICE, PATH. When the value is
+successfully set return VALUE. Otherwise, return nil."
(dbus-ignore-errors
;; "Set" requires a variant.
(dbus-call-method
@@ -1468,26 +1415,23 @@ name of the property, and its value. If there are no properties,
nil is returned."
(dbus-ignore-errors
;; "GetAll" returns "a{sv}".
- (let (result)
- (dolist (dict
- (dbus-call-method
- bus service path dbus-interface-properties
- "GetAll" :timeout 500 interface)
- (nreverse result))
- (push (cons (car dict) (cl-caadr dict)) result)))))
+ (mapcar (lambda (dict)
+ (cons (car dict) (caadr dict)))
+ (dbus-call-method bus service path dbus-interface-properties
+ "GetAll" :timeout 500 interface))))
(defun dbus-register-property
(bus service path interface property access value
&optional emits-signal dont-register-service)
- "Register property PROPERTY on the D-Bus BUS.
+ "Register PROPERTY on the D-Bus BUS.
BUS is either a Lisp symbol, `:system' or `:session', or a string
denoting the bus address.
SERVICE is the D-Bus service name of the D-Bus. It must be a
-known name (See discussion of DONT-REGISTER-SERVICE below).
+known name (see discussion of DONT-REGISTER-SERVICE below).
-PATH is the D-Bus object path SERVICE is registered (See
+PATH is the D-Bus object path SERVICE is registered at (see
discussion of DONT-REGISTER-SERVICE below). INTERFACE is the
name of the interface used at PATH, PROPERTY is the name of the
property of INTERFACE. ACCESS indicates, whether the property
@@ -1519,13 +1463,13 @@ clients from discovering the still incomplete interface."
;; Add handlers for the three property-related methods.
(dbus-register-method
bus service path dbus-interface-properties "Get"
- 'dbus-property-handler 'dont-register)
+ #'dbus-property-handler 'dont-register)
(dbus-register-method
bus service path dbus-interface-properties "GetAll"
- 'dbus-property-handler 'dont-register)
+ #'dbus-property-handler 'dont-register)
(dbus-register-method
bus service path dbus-interface-properties "Set"
- 'dbus-property-handler 'dont-register)
+ #'dbus-property-handler 'dont-register)
;; Register SERVICE.
(unless (or dont-register-service (member service (dbus-list-names bus)))
@@ -1625,8 +1569,8 @@ It will be registered for all objects created by `dbus-register-property'."
"Return all objects at BUS, SERVICE, PATH, and the children of PATH.
The result is a list of objects. Every object is a cons of an
existing path name, and the list of available interface objects.
-An interface object is another cons, which car is the interface
-name, and the cdr is the list of properties as returned by
+An interface object is another cons, whose car is the interface
+name and cdr is the list of properties as returned by
`dbus-get-all-properties' for that path and interface. Example:
\(dbus-get-all-managed-objects :session \"org.gnome.SettingsDaemon\" \"/\")
@@ -1672,7 +1616,7 @@ and \"org.freedesktop.DBus.Properties.GetAll\", which is slow."
(if (cadr entry2)
;; "sv".
(dolist (entry3 (cadr entry2))
- (setcdr entry3 (cl-caadr entry3)))
+ (setcdr entry3 (caadr entry3)))
(setcdr entry2 nil)))))
;; Fallback: collect the information. Slooow!
@@ -1729,7 +1673,7 @@ It will be registered for all objects created by `dbus-register-service'."
(append
(butlast last-input-event 4)
(list object dbus-interface-properties
- "GetAll" 'dbus-property-handler))))
+ "GetAll" #'dbus-property-handler))))
(dbus-property-handler interface))))
(cdr (assoc object result)))))))))
dbus-registered-objects-table)
@@ -1782,12 +1726,13 @@ can be a string denoting the address of the corresponding bus. For
the system and session buses, this function is called when loading
`dbus.el', there is no need to call it again.
-The function returns a number, which counts the connections this Emacs
-session has established to the BUS under the same unique name (see
-`dbus-get-unique-name'). It depends on the libraries Emacs is linked
-with, and on the environment Emacs is running. For example, if Emacs
-is linked with the gtk toolkit, and it runs in a GTK-aware environment
-like Gnome, another connection might already be established.
+The function returns the number of connections this Emacs session
+has established to the BUS under the same unique name (see
+`dbus-get-unique-name'). It depends on the libraries Emacs is
+linked with, and on the environment Emacs is running. For
+example, if Emacs is linked with the GTK+ toolkit, and it runs in
+a GTK+-aware environment like GNOME, another connection might
+already be established.
When PRIVATE is non-nil, a new connection is established instead of
reusing an existing one. It results in a new unique name at the bus.
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 2a70560ca7b..2f6528de948 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -25,14 +25,14 @@
;;; Code:
(require 'cl-lib)
-(require 'format-spec)
+(require 'mm-url)
+(require 'puny)
(require 'shr)
+(require 'text-property-search)
+(require 'thingatpt)
(require 'url)
(require 'url-queue)
-(require 'thingatpt)
-(require 'mm-url)
-(require 'puny)
-(eval-when-compile (require 'subr-x)) ;; for string-trim
+(eval-when-compile (require 'subr-x))
(defgroup eww nil
"Emacs Web Wowser"
@@ -543,10 +543,10 @@ Currently this means either text/html or application/xhtml+xml."
(goto-char point))
(shr-target-id
(goto-char (point-min))
- (let ((point (next-single-property-change
- (point-min) 'shr-target-id)))
- (when point
- (goto-char point))))
+ (let ((match (text-property-search-forward
+ 'shr-target-id shr-target-id t)))
+ (when match
+ (goto-char (prop-match-beginning match)))))
(t
(goto-char (point-min))
;; Don't leave point inside forms, because the normal eww
diff --git a/lisp/net/imap.el b/lisp/net/imap.el
index aa10f0291fd..a492dc8c798 100644
--- a/lisp/net/imap.el
+++ b/lisp/net/imap.el
@@ -136,7 +136,6 @@
;;; Code:
(eval-when-compile (require 'cl-lib))
-(require 'format-spec)
(require 'utf7)
(require 'rfc2104)
;; Hmm... digest-md5 is not part of Emacs.
@@ -517,12 +516,9 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
- (format-spec
- cmd
- (format-spec-make
- ?s server
- ?p (number-to-string port)
- ?l imap-default-user))))
+ (format-spec cmd `((?s . ,server)
+ (?p . ,(number-to-string port))
+ (?l . ,imap-default-user)))))
response)
(when process
(with-current-buffer buffer
@@ -583,12 +579,9 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
- (format-spec
- cmd
- (format-spec-make
- ?s server
- ?p (number-to-string port)
- ?l imap-default-user))))
+ (format-spec cmd `((?s . ,server)
+ (?p . ,(number-to-string port))
+ (?l . ,imap-default-user)))))
response)
(when process
(with-current-buffer buffer
@@ -701,13 +694,10 @@ sure of changing the value of `foo'."
(process-connection-type imap-process-connection-type)
(process (start-process
name buffer shell-file-name shell-command-switch
- (format-spec
- cmd
- (format-spec-make
- ?s server
- ?g imap-shell-host
- ?p (number-to-string port)
- ?l imap-default-user)))))
+ (format-spec cmd `((?s . ,server)
+ (?g . ,imap-shell-host)
+ (?p . ,(number-to-string port))
+ (?l . ,imap-default-user))))))
(when process
(while (and (memq (process-status process) '(open run))
(set-buffer buffer) ;; XXX "blue moon" nntp.el bug
diff --git a/lisp/net/network-stream.el b/lisp/net/network-stream.el
index 1d5cf382a84..1c371f59870 100644
--- a/lisp/net/network-stream.el
+++ b/lisp/net/network-stream.el
@@ -170,8 +170,8 @@ a greeting from the server.
:nowait, if non-nil, says the connection should be made
asynchronously, if possible.
-:shell-command is a format-spec string that can be used if :type
-is `shell'. It has two specs, %s for host and %p for port
+:shell-command is a `format-spec' string that can be used if
+:type is `shell'. It has two specs, %s for host and %p for port
number. Example: \"ssh gateway nc %s %p\".
:tls-parameters is a list that should be supplied if you're
@@ -453,11 +453,7 @@ gnutls-boot (as returned by `gnutls-boot-parameters')."
(network-stream-command stream capability-command eo-capa)
'tls)))))))
-(declare-function format-spec "format-spec" (format spec))
-(declare-function format-spec-make "format-spec" (&rest pairs))
-
(defun network-stream-open-shell (name buffer host service parameters)
- (require 'format-spec)
(let* ((capability-command (plist-get parameters :capability-command))
(eoc (plist-get parameters :end-of-command))
(start (with-current-buffer buffer (point)))
@@ -467,9 +463,8 @@ gnutls-boot (as returned by `gnutls-boot-parameters')."
shell-command-switch
(format-spec
(plist-get parameters :shell-command)
- (format-spec-make
- ?s host
- ?p service))))))
+ `((?s . ,host)
+ (?p . ,service)))))))
(when coding (if (consp coding)
(set-process-coding-system stream
(car coding)
diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index 03260c9e70a..a3f04968a27 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -185,13 +185,15 @@ and other things:
(defvar shr-depth 0)
(defvar shr-warning nil)
(defvar shr-ignore-cache nil)
-(defvar shr-target-id nil)
(defvar shr-table-separator-length 1)
(defvar shr-table-separator-pixel-width 0)
(defvar shr-table-id nil)
(defvar shr-current-font nil)
(defvar shr-internal-bullet nil)
+(defvar shr-target-id nil
+ "Target fragment identifier anchor.")
+
(defvar shr-map
(let ((map (make-sparse-keymap)))
(define-key map "a" 'shr-show-alt-text)
@@ -526,13 +528,13 @@ size, and full-buffer size."
(funcall function dom))
(t
(shr-generic dom)))
- (when (and shr-target-id
- (equal (dom-attr dom 'id) shr-target-id))
+ (when-let* ((id (dom-attr dom 'id)))
;; If the element was empty, we don't have anything to put the
;; anchor on. So just insert a dummy character.
(when (= start (point))
- (insert "*"))
- (put-text-property start (1+ start) 'shr-target-id shr-target-id))
+ (insert ?*)
+ (put-text-property (1- (point)) (point) 'display ""))
+ (put-text-property start (1+ start) 'shr-target-id id))
;; If style is set, then this node has set the color.
(when style
(shr-colorize-region
@@ -1486,14 +1488,13 @@ ones, in case fg and bg are nil."
(start (point))
shr-start)
(shr-generic dom)
- (when (and shr-target-id
- (equal (dom-attr dom 'name) shr-target-id))
- ;; We have a zero-length <a name="foo"> element, so just
- ;; insert... something.
+ (when-let* ((id (unless (dom-attr dom 'id) ; Handled by `shr-descend'.
+ (dom-attr dom 'name)))) ; Obsolete since HTML5.
+ ;; We have an empty element, so just insert... something.
(when (= start (point))
- (shr-ensure-newline)
- (insert " "))
- (put-text-property start (1+ start) 'shr-target-id shr-target-id))
+ (insert ?\s)
+ (put-text-property (1- (point)) (point) 'display ""))
+ (put-text-property start (1+ start) 'shr-target-id id))
(when url
(shr-urlify (or shr-start start) (shr-expand-url url) title))))
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index b4a080ee0f6..a7a5047ed49 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -136,7 +136,7 @@ It is used for TCP/IP devices."
(file-selinux-context . tramp-handle-file-selinux-context)
(file-symlink-p . tramp-handle-file-symlink-p)
(file-system-info . tramp-adb-handle-file-system-info)
- (file-truename . tramp-adb-handle-file-truename)
+ (file-truename . tramp-handle-file-truename)
(file-writable-p . tramp-adb-handle-file-writable-p)
(find-backup-file-name . tramp-handle-find-backup-file-name)
;; `get-file-buffer' performed by default handler.
@@ -160,6 +160,8 @@ It is used for TCP/IP devices."
(start-file-process . tramp-handle-start-file-process)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . ignore)
+ (tramp-get-remote-uid . ignore)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
@@ -227,104 +229,6 @@ ARGUMENTS to pass to the OPERATION."
(string-to-number (match-string 2))))
(* 1024 (string-to-number (match-string 3)))))))))
-;; This is derived from `tramp-sh-handle-file-truename'. Maybe the
-;; code could be shared?
-(defun tramp-adb-handle-file-truename (filename)
- "Like `file-truename' for Tramp files."
- ;; Preserve trailing "/".
- (funcall
- (if (directory-name-p filename) #'file-name-as-directory #'identity)
- ;; Quote properly.
- (funcall
- (if (tramp-compat-file-name-quoted-p filename)
- #'tramp-compat-file-name-quote #'identity)
- (with-parsed-tramp-file-name
- (tramp-compat-file-name-unquote (expand-file-name filename)) nil
- (tramp-make-tramp-file-name
- v
- (with-tramp-file-property v localname "file-truename"
- (let (result) ; result steps in reverse order
- (tramp-message v 4 "Finding true name for `%s'" filename)
- (let* ((steps (split-string localname "/" 'omit))
- (localnamedir (tramp-run-real-handler
- 'file-name-as-directory (list localname)))
- (is-dir (string= localname localnamedir))
- (thisstep nil)
- (numchase 0)
- ;; Don't make the following value larger than
- ;; necessary. People expect an error message in a
- ;; timely fashion when something is wrong; otherwise
- ;; they might think that Emacs is hung. Of course,
- ;; correctness has to come first.
- (numchase-limit 20)
- symlink-target)
- (while (and steps (< numchase numchase-limit))
- (setq thisstep (pop steps))
- (tramp-message
- v 5 "Check %s"
- (string-join
- (append '("") (reverse result) (list thisstep)) "/"))
- (setq symlink-target
- (tramp-compat-file-attribute-type
- (file-attributes
- (tramp-make-tramp-file-name
- v
- (string-join
- (append
- '("") (reverse result) (list thisstep)) "/")))))
- (cond ((string= "." thisstep)
- (tramp-message v 5 "Ignoring step `.'"))
- ((string= ".." thisstep)
- (tramp-message v 5 "Processing step `..'")
- (pop result))
- ((stringp symlink-target)
- ;; It's a symlink, follow it.
- (tramp-message v 5 "Follow symlink to %s" symlink-target)
- (setq numchase (1+ numchase))
- (when (file-name-absolute-p symlink-target)
- (setq result nil))
- ;; If the symlink was absolute, we'll get a string
- ;; like "/user@host:/some/target"; extract the
- ;; "/some/target" part from it.
- (when (tramp-tramp-file-p symlink-target)
- (unless (tramp-equal-remote filename symlink-target)
- (tramp-error
- v 'file-error
- "Symlink target `%s' on wrong host" symlink-target))
- (setq symlink-target localname))
- (setq steps
- (append (split-string symlink-target "/" 'omit)
- steps)))
- (t
- ;; It's a file.
- (setq result (cons thisstep result)))))
- (when (>= numchase numchase-limit)
- (tramp-error
- v 'file-error
- "Maximum number (%d) of symlinks exceeded" numchase-limit))
- (setq result (reverse result))
- ;; Combine list to form string.
- (setq result
- (if result
- (string-join (cons "" result) "/")
- "/"))
- (when (and is-dir (or (string-empty-p result)
- (not (string= (substring result -1) "/"))))
- (setq result (concat result "/"))))
-
- ;; Detect cycle.
- (when (and (file-symlink-p filename)
- (string-equal result localname))
- (tramp-error
- v 'file-error
- "Apparent cycle of symbolic links for %s" filename))
- ;; If the resulting localname looks remote, we must quote it
- ;; for security reasons.
- (when (file-remote-p result)
- (setq result (tramp-compat-file-name-quote result 'top)))
- (tramp-message v 4 "True name of `%s' is `%s'" localname result)
- result)))))))
-
(defun tramp-adb-handle-file-attributes (filename &optional id-format)
"Like `file-attributes' for Tramp files."
(unless id-format (setq id-format 'integer))
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el
index 24ee6fa51f3..9502cc35300 100644
--- a/lisp/net/tramp-archive.el
+++ b/lisp/net/tramp-archive.el
@@ -279,7 +279,9 @@ It must be supported by libarchive(3).")
(start-file-process . tramp-archive-handle-not-implemented)
;; `substitute-in-file-name' performed by default handler.
(temporary-file-directory . tramp-archive-handle-temporary-file-directory)
- ;; `tramp-set-file-uid-gid' performed by default handler.
+ (tramp-get-remote-gid . ignore)
+ (tramp-get-remote-uid . ignore)
+ (tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
index f0131d59852..218594b551c 100644
--- a/lisp/net/tramp-compat.el
+++ b/lisp/net/tramp-compat.el
@@ -43,6 +43,7 @@
;; `temporary-file-directory' as function is introduced with Emacs 26.1.
(declare-function tramp-handle-temporary-file-directory "tramp")
+(defvar tramp-temp-name-prefix)
;; For not existing functions, obsolete functions, or functions with a
;; changed argument list, there are compiler warnings. We want to
@@ -61,15 +62,19 @@ It is the default value of `temporary-file-directory'."
;; into an infloop.
(eval (car (get 'temporary-file-directory 'standard-value))))
+(defsubst tramp-compat-make-temp-name ()
+ "Generate a local temporary file name (compat function)."
+ (make-temp-name
+ (expand-file-name
+ tramp-temp-name-prefix (tramp-compat-temporary-file-directory))))
+
(defsubst tramp-compat-make-temp-file (f &optional dir-flag)
"Create a local temporary file (compat function).
Add the extension of F, if existing."
- (let* (file-name-handler-alist
- (prefix (expand-file-name
- (symbol-value 'tramp-temp-name-prefix)
- (tramp-compat-temporary-file-directory)))
- (extension (file-name-extension f t)))
- (make-temp-file prefix dir-flag extension)))
+ (make-temp-file
+ (expand-file-name
+ tramp-temp-name-prefix (tramp-compat-temporary-file-directory))
+ dir-flag (file-name-extension f t)))
;; `temporary-file-directory' as function is introduced with Emacs 26.1.
(defalias 'tramp-compat-temporary-file-directory-function
diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el
index 664f4413473..c9788fcff52 100644
--- a/lisp/net/tramp-crypt.el
+++ b/lisp/net/tramp-crypt.el
@@ -24,7 +24,7 @@
;;; Commentary:
;; Access functions for crypted remote files. It uses encfs to
-;; encrypt/ decrypt the files on a remote directory. A remote
+;; encrypt / decrypt the files on a remote directory. A remote
;; directory, which shall include crypted files, must be declared in
;; `tramp-crypt-directories' via command `tramp-crypt-add-directory'.
;; All files in that directory, including all subdirectories, are
@@ -145,8 +145,8 @@ If NAME doesn't belong to a crypted remote directory, retun nil."
;; New handlers should be added here.
;;;###tramp-autoload
(defconst tramp-crypt-file-name-handler-alist
- '(;; (access-file . tramp-crypt-handle-access-file)
- ;; (add-name-to-file . tramp-crypt-handle-not-implemented)
+ '((access-file . tramp-crypt-handle-access-file)
+ (add-name-to-file . tramp-handle-add-name-to-file)
;; `byte-compiler-base-file-name' performed by default handler.
(copy-directory . tramp-handle-copy-directory)
(copy-file . tramp-crypt-handle-copy-file)
@@ -182,15 +182,15 @@ If NAME doesn't belong to a crypted remote directory, retun nil."
(file-notify-add-watch . ignore)
(file-notify-rm-watch . ignore)
(file-notify-valid-p . ignore)
- ;; (file-ownership-preserved-p . ignore)
+ (file-ownership-preserved-p . tramp-crypt-handle-file-ownership-preserved-p)
(file-readable-p . tramp-crypt-handle-file-readable-p)
(file-regular-p . tramp-handle-file-regular-p)
;; `file-remote-p' performed by default handler.
(file-selinux-context . ignore)
(file-symlink-p . tramp-handle-file-symlink-p)
(file-system-info . tramp-crypt-handle-file-system-info)
- ;; (file-truename . tramp-crypt-handle-file-truename)
- ;; (file-writable-p . ignore)
+ ;; `file-truename' performed by default handler.
+ (file-writable-p . tramp-crypt-handle-file-writable-p)
(find-backup-file-name . tramp-handle-find-backup-file-name)
;; `get-file-buffer' performed by default handler.
(insert-directory . tramp-crypt-handle-insert-directory)
@@ -198,8 +198,8 @@ If NAME doesn't belong to a crypted remote directory, retun nil."
(load . tramp-handle-load)
(make-auto-save-file-name . tramp-handle-make-auto-save-file-name)
(make-directory . tramp-crypt-handle-make-directory)
- ;; (make-directory-internal . tramp-crypt-handle-not-implemented)
- ;; (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
+ (make-directory-internal . ignore)
+ (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
(make-process . ignore)
(make-symbolic-link . tramp-handle-make-symbolic-link)
(process-file . ignore)
@@ -212,9 +212,11 @@ If NAME doesn't belong to a crypted remote directory, retun nil."
(shell-command . ignore)
(start-file-process . ignore)
;; `substitute-in-file-name' performed by default handler.
- ;; (temporary-file-directory . tramp-crypt-handle-temporary-file-directory)
+ (temporary-file-directory . tramp-handle-temporary-file-directory)
+ ;; `tramp-get-remote-gid' performed by default handler.
+ ;; `tramp-get-remote-uid' performed by default handler.
(tramp-set-file-uid-gid . tramp-crypt-handle-set-file-uid-gid)
- ;; (unhandled-file-name-directory . ignore)
+ (unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
(write-region . tramp-handle-write-region))
@@ -223,9 +225,14 @@ Operations not mentioned here will be handled by the default Emacs primitives.")
(defsubst tramp-crypt-file-name-for-operation (operation &rest args)
"Like `tramp-file-name-for-operation', but for crypted remote files."
- (cl-letf (((symbol-function #'tramp-tramp-file-p)
- #'tramp-crypt-file-name-p))
- (apply #'tramp-file-name-for-operation operation args)))
+ (let ((tfnfo (apply #'tramp-file-name-for-operation operation args)))
+ ;; `tramp-file-name-for-operation' returns already the first argument
+ ;; if it is remote. So we check a possible second argument.
+ (unless (tramp-crypt-file-name-p tfnfo)
+ (setq tfnfo (apply
+ #'tramp-file-name-for-operation operation
+ (cons (tramp-compat-temporary-file-directory) (cdr args)))))
+ tfnfo))
(defun tramp-crypt-run-real-handler (operation args)
"Invoke normal file name handler for OPERATION.
@@ -244,7 +251,8 @@ arguments to pass to the OPERATION."
"Invoke the crypted remote file related OPERATION.
First arg specifies the OPERATION, second arg ARGS is a list of
arguments to pass to the OPERATION."
- (if-let ((filename (apply #'tramp-file-name-for-operation operation args))
+ (if-let ((filename
+ (apply #'tramp-crypt-file-name-for-operation operation args))
(fn (and (tramp-crypt-file-name-p filename)
(assoc operation tramp-crypt-file-name-handler-alist))))
(save-match-data (apply (cdr fn) args))
@@ -351,10 +359,11 @@ connection if a previous connection has died for some reason."
(defun tramp-crypt-send-command (vec &rest args)
"Send encfsctl command to connection VEC.
-ARGS are the arguments."
+ARGS are the arguments. It returns t if ran successful, and nil otherwise."
(tramp-crypt-maybe-open-connection vec)
(with-current-buffer (tramp-get-connection-buffer vec)
- (erase-buffer))
+ (erase-buffer)
+ (set-buffer-multibyte nil))
(with-temp-buffer
(let* (;; Don't check for a proper method.
(non-essential t)
@@ -380,11 +389,12 @@ ARGS are the arguments."
;; Save the password.
(ignore-errors
(and (functionp tramp-password-save-function)
- (funcall tramp-password-save-function)))))))
+ (funcall tramp-password-save-function)))
+ t))))
(defun tramp-crypt-do-encrypt-or-decrypt-file-name (op name)
- "Return encrypted/ decrypted NAME if NAME belongs to a crypted directory.
-OP must be `encrypt' or `decrypt'.
+ "Return encrypted / decrypted NAME if NAME belongs to a crypted directory.
+OP must be `encrypt' or `decrypt'. Raise an error if this fails.
Otherwise, return NAME."
(if-let ((tramp-crypt-enabled t)
(dir (tramp-crypt-file-name-p name))
@@ -399,9 +409,12 @@ Otherwise, return NAME."
(unless (string-equal localname "/")
(with-tramp-file-property
crypt-vec localname (concat (symbol-name op) "-file-name")
- (tramp-crypt-send-command
- crypt-vec (if (eq op 'encrypt) "encode" "decode")
- (tramp-compat-temporary-file-directory) localname)
+ (unless (tramp-crypt-send-command
+ crypt-vec (if (eq op 'encrypt) "encode" "decode")
+ (tramp-compat-temporary-file-directory) localname)
+ (tramp-error
+ crypt-vec 'file-error "%s of file name %s failed."
+ (if (eq op 'encrypt) "Encoding" "Decoding") name))
(with-current-buffer (tramp-get-connection-buffer crypt-vec)
(goto-char (point-min))
(buffer-substring (point-min) (point-at-eol)))))))
@@ -419,9 +432,10 @@ Otherwise, return NAME."
(tramp-crypt-do-encrypt-or-decrypt-file-name 'decrypt name))
(defun tramp-crypt-do-encrypt-or-decrypt-file (op root infile outfile)
- "Encrypt/ decrypt file INFILE to OUTFILE according to crypted directory ROOT.
+ "Encrypt / decrypt file INFILE to OUTFILE according to crypted directory ROOT.
Both files must be local files. OP must be `encrypt' or `decrypt'.
-If OP ist `decrypt', the basename of INFILE must be an encrypted file name."
+If OP ist `decrypt', the basename of INFILE must be an encrypted file name.
+Raise an error if this fails."
(when-let ((tramp-crypt-enabled t)
(dir (tramp-crypt-file-name-p root))
(crypt-vec (tramp-crypt-dissect-file-name dir)))
@@ -429,10 +443,13 @@ If OP ist `decrypt', the basename of INFILE must be an encrypted file name."
(if (eq op 'decrypt) 'binary coding-system-for-read))
(coding-system-for-write
(if (eq op 'encrypt) 'binary coding-system-for-write)))
- (tramp-crypt-send-command
- crypt-vec "cat" (and (eq op 'encrypt) "--reverse")
- (file-name-directory infile)
- (concat "/" (file-name-nondirectory infile)))
+ (unless (tramp-crypt-send-command
+ crypt-vec "cat" (and (eq op 'encrypt) "--reverse")
+ (file-name-directory infile)
+ (concat "/" (file-name-nondirectory infile)))
+ (tramp-error
+ crypt-vec 'file-error "%s of file %s failed."
+ (if (eq op 'encrypt) "Encrypting" "Decrypting") infile))
(with-current-buffer (tramp-get-connection-buffer crypt-vec)
(write-region nil nil outfile)))))
@@ -457,6 +474,8 @@ directory. File names will be also encrypted."
(tramp-user-error nil "Feature is not enabled."))
(unless (and (tramp-tramp-file-p name) (file-directory-p name))
(tramp-user-error nil "%s must be an existing remote directory." name))
+ (when (tramp-compat-file-name-quoted-p name)
+ (tramp-user-error nil "%s must not be quoted." name))
(setq name (file-name-as-directory (expand-file-name name)))
(unless (member name tramp-crypt-directories)
(setq tramp-crypt-directories (cons name tramp-crypt-directories)))
@@ -499,6 +518,21 @@ localname."
;; File name primitives.
+(defun tramp-crypt-handle-access-file (filename string)
+ "Like `access-file' for Tramp files."
+ (let* ((encrypt-filename (tramp-crypt-encrypt-file-name filename))
+ (encrypt-regexp (concat (regexp-quote encrypt-filename) "\\'"))
+ tramp-crypt-enabled)
+ (condition-case err
+ (access-file encrypt-filename string)
+ (error
+ (when (and (eq (car err) 'file-missing) (stringp (cadr err))
+ (string-match-p encrypt-regexp (cadr err)))
+ (setcar
+ (cdr err)
+ (replace-regexp-in-string encrypt-regexp filename (cadr err))))
+ (signal (car err) (cdr err))))))
+
(defun tramp-crypt-do-copy-or-rename-file
(op filename newname &optional ok-if-already-exists keep-date
preserve-uid-gid preserve-extended-attributes)
@@ -520,16 +554,17 @@ absolute file names."
(error "Unknown operation `%s', must be `copy' or `rename'" op))
(setq filename (file-truename filename))
- (if (file-directory-p filename)
- (progn
- (copy-directory filename newname keep-date t)
- (when (eq op 'rename) (delete-directory filename 'recursive)))
-
- (let ((t1 (tramp-crypt-file-name-p filename))
- (t2 (tramp-crypt-file-name-p newname))
- (encrypt-filename (tramp-crypt-encrypt-file-name filename))
- (encrypt-newname (tramp-crypt-encrypt-file-name newname))
- (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
+ (let ((t1 (tramp-crypt-file-name-p filename))
+ (t2 (tramp-crypt-file-name-p newname))
+ (encrypt-filename (tramp-crypt-encrypt-file-name filename))
+ (encrypt-newname (tramp-crypt-encrypt-file-name newname))
+ (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
+
+ (if (file-directory-p filename)
+ (progn
+ (copy-directory filename newname keep-date t)
+ (when (eq op 'rename)
+ (delete-directory filename 'recursive)))
(with-parsed-tramp-file-name (if t1 filename newname) nil
(unless (file-exists-p filename)
@@ -563,6 +598,14 @@ absolute file names."
(file-name-nondirectory encrypt-newname) tmpdir))
tramp-crypt-enabled)
(cond
+ ;; Source and target file are on a crypted remote directory.
+ ((and t1 t2)
+ (if (eq op 'copy)
+ (copy-file
+ encrypt-filename encrypt-newname ok-if-already-exists
+ keep-date preserve-uid-gid preserve-extended-attributes)
+ (rename-file
+ encrypt-filename encrypt-newname ok-if-already-exists)))
;; Source file is on a crypted remote directory.
(t1
(if (eq op 'copy)
@@ -581,15 +624,15 @@ absolute file names."
(rename-file filename tmpfile1 t))
(tramp-crypt-encrypt-file t2 tmpfile1 tmpfile2)
(rename-file tmpfile2 encrypt-newname ok-if-already-exists)))
- (delete-directory tmpdir 'recursive)))
+ (delete-directory tmpdir 'recursive))))))
- (when (and t1 (eq op 'rename))
- (with-parsed-tramp-file-name filename v1
- (tramp-flush-file-properties v1 v1-localname)))
+ (when (and t1 (eq op 'rename))
+ (with-parsed-tramp-file-name filename v1
+ (tramp-flush-file-properties v1 v1-localname)))
- (when t2
- (with-parsed-tramp-file-name newname v2
- (tramp-flush-file-properties v2 v2-localname))))))))
+ (when t2
+ (with-parsed-tramp-file-name newname v2
+ (tramp-flush-file-properties v2 v2-localname)))))
(defun tramp-crypt-handle-copy-file
(filename newname &optional ok-if-already-exists keep-date
@@ -621,8 +664,8 @@ absolute file names."
"Like `delete-file' for Tramp files."
(with-parsed-tramp-file-name (expand-file-name filename) nil
(tramp-flush-file-properties v localname)
- (tramp-crypt-run-real-handler
- #'delete-file (list (tramp-crypt-encrypt-file-name filename) trash))))
+ (let (tramp-crypt-enabled)
+ (delete-file (tramp-crypt-encrypt-file-name filename) trash))))
(defun tramp-crypt-handle-directory-files (directory &optional full match nosort)
"Like `directory-files' for Tramp files."
@@ -657,8 +700,8 @@ absolute file names."
(defun tramp-crypt-handle-file-attributes (filename &optional id-format)
"Like `file-attributes' for Tramp files."
- (tramp-crypt-run-real-handler
- #'file-attributes (list (tramp-crypt-encrypt-file-name filename) id-format)))
+ (let (tramp-crypt-enabled)
+ (file-attributes (tramp-crypt-encrypt-file-name filename) id-format)))
(defun tramp-crypt-handle-file-executable-p (filename)
"Like `file-executable-p' for Tramp files."
@@ -685,43 +728,56 @@ absolute file names."
(let (tramp-crypt-enabled)
(file-readable-p (tramp-crypt-encrypt-file-name filename))))
+(defun tramp-crypt-handle-file-ownership-preserved-p (filename &optional group)
+ "Like `file-ownership-preserved-p' for Tramp files."
+ (let (tramp-crypt-enabled)
+ (file-ownership-preserved-p (tramp-crypt-encrypt-file-name filename) group)))
+
(defun tramp-crypt-handle-file-system-info (filename)
"Like `file-system-info' for Tramp files."
- (tramp-crypt-run-real-handler
- ;; `file-system-info' exists since Emacs 27.1. Then, we can use
- ;; #'file-system-info.
- 'file-system-info (list (tramp-crypt-encrypt-file-name filename))))
+ (let (tramp-crypt-enabled)
+ ;; `file-system-info' exists since Emacs 27.1.
+ (tramp-compat-funcall
+ 'file-system-info (tramp-crypt-encrypt-file-name filename))))
+
+(defun tramp-crypt-handle-file-writable-p (filename)
+ "Like `file-writable-p' for Tramp files."
+ (let (tramp-crypt-enabled)
+ (file-writable-p (tramp-crypt-encrypt-file-name filename))))
(defun tramp-crypt-handle-insert-directory
(filename switches &optional wildcard full-directory-p)
- "Like `insert-directory' for Tramp files."
- (let (tramp-crypt-enabled)
- (tramp-handle-insert-directory
- (tramp-crypt-encrypt-file-name filename)
- switches wildcard full-directory-p)
- (let* ((filename (file-name-as-directory filename))
- (enc (tramp-crypt-encrypt-file-name filename))
- match string)
- (goto-char (point-min))
- (while (setq match (text-property-search-forward 'dired-filename t t))
- (setq string
- (buffer-substring
- (prop-match-beginning match) (prop-match-end match))
- string (if (file-name-absolute-p string)
- (tramp-crypt-decrypt-file-name string)
- (substring
- (tramp-crypt-decrypt-file-name (concat enc string))
- (length filename))))
- (delete-region (prop-match-beginning match) (prop-match-end match))
- (insert (propertize string 'dired-filename t))))))
+ "Like `insert-directory' for Tramp files.
+WILDCARD is not supported."
+ ;; This package has been added to Emacs 27.1.
+ (when (load "text-property-search" 'noerror 'nomessage)
+ (let (tramp-crypt-enabled)
+ (tramp-handle-insert-directory
+ (tramp-crypt-encrypt-file-name filename)
+ switches wildcard full-directory-p)
+ (let* ((filename (file-name-as-directory filename))
+ (enc (tramp-crypt-encrypt-file-name filename))
+ match string)
+ (goto-char (point-min))
+ (while (setq match (text-property-search-forward 'dired-filename t t))
+ (setq string
+ (buffer-substring
+ (prop-match-beginning match) (prop-match-end match))
+ string (if (file-name-absolute-p string)
+ (tramp-crypt-decrypt-file-name string)
+ (substring
+ (tramp-crypt-decrypt-file-name (concat enc string))
+ (length filename))))
+ (delete-region (prop-match-beginning match) (prop-match-end match))
+ (insert (propertize string 'dired-filename t)))))))
(defun tramp-crypt-handle-make-directory (dir &optional parents)
"Like `make-directory' for Tramp files."
(with-parsed-tramp-file-name (expand-file-name dir) nil
(when (and (null parents) (file-exists-p dir))
(tramp-error v 'file-already-exists "Directory already exists %s" dir))
- (tramp-crypt-run-real-handler
- #'make-directory (list (tramp-crypt-encrypt-file-name dir) parents))
+ (let (tramp-crypt-enabled)
+ (make-directory (tramp-crypt-encrypt-file-name dir) parents))
;; When PARENTS is non-nil, DIR could be a chain of non-existent
;; directories a/b/c/... Instead of checking, we simply flush the
;; whole cache.
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index 704d65cd55e..dce6edd19c4 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -821,6 +821,8 @@ It has been changed in GVFS 1.14.")
(start-file-process . ignore)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . tramp-gvfs-handle-get-remote-gid)
+ (tramp-get-remote-uid . tramp-gvfs-handle-get-remote-uid)
(tramp-set-file-uid-gid . tramp-gvfs-handle-set-file-uid-gid)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
@@ -949,6 +951,10 @@ is no information where to trace the message.")
(tramp-error tramp-gvfs-dbus-event-vector 'file-error "%s" (cadr err))))
(add-hook 'dbus-event-error-functions #'tramp-gvfs-dbus-event-error)
+(add-hook 'tramp-gvfs-unload-hook
+ (lambda ()
+ (remove-hook 'dbus-event-error-functions
+ #'tramp-gvfs-dbus-event-error)))
;; File name primitives.
@@ -1506,7 +1512,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
;; If the user is different from what we guess to be
;; the user, we don't know. Let's check, whether
;; access is restricted explicitly.
- (and (/= (tramp-gvfs-get-remote-uid v 'integer)
+ (and (/= (tramp-get-remote-uid v 'integer)
(tramp-compat-file-attribute-user-id
(file-attributes filename 'integer)))
(not
@@ -1589,7 +1595,27 @@ If FILE-SYSTEM is non-nil, return file system attributes."
(current-time)
time)))))
-(defun tramp-gvfs-set-file-uid-gid (filename &optional uid gid)
+(defun tramp-gvfs-handle-get-remote-uid (vec id-format)
+ "The uid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (if (equal id-format 'string)
+ (tramp-file-name-user vec)
+ (when-let
+ ((localname (tramp-get-connection-property vec "default-location" nil)))
+ (tramp-compat-file-attribute-user-id
+ (file-attributes
+ (tramp-make-tramp-file-name vec localname) id-format)))))
+
+(defun tramp-gvfs-handle-get-remote-gid (vec id-format)
+ "The gid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (when-let
+ ((localname (tramp-get-connection-property vec "default-location" nil)))
+ (tramp-compat-file-attribute-group-id
+ (file-attributes
+ (tramp-make-tramp-file-name vec localname) id-format))))
+
+(defun tramp-gvfs-handle-set-file-uid-gid (filename &optional uid gid)
"Like `tramp-set-file-uid-gid' for Tramp files."
(with-parsed-tramp-file-name filename nil
(tramp-flush-file-properties v localname)
@@ -2057,39 +2083,6 @@ and \"org.gtk.Private.RemoteVolumeMonitor.VolumeRemoved\" signals."
;; Connection functions.
-(defun tramp-gvfs-get-remote-uid (vec id-format)
- "The uid of the remote connection VEC, in ID-FORMAT.
-ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "uid-%s" id-format)
- (let ((user (tramp-file-name-user vec))
- (localname
- (tramp-get-connection-property vec "default-location" nil)))
- (cond
- ((and (equal id-format 'string) user))
- (localname
- (tramp-compat-file-attribute-user-id
- (file-attributes
- (tramp-make-tramp-file-name vec localname) id-format)))
- ((equal id-format 'integer) tramp-unknown-id-integer)
- ((equal id-format 'string) tramp-unknown-id-string)))))
-
-(defun tramp-gvfs-get-remote-gid (vec id-format)
- "The gid of the remote connection VEC, in ID-FORMAT.
-ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "gid-%s" id-format)
- (let ((localname
- (tramp-get-connection-property vec "default-location" nil)))
- (cond
- (localname
- (tramp-compat-file-attribute-group-id
- (file-attributes
- (tramp-make-tramp-file-name vec localname) id-format)))
- ((equal id-format 'integer) tramp-unknown-id-integer)
- ((equal id-format 'string) tramp-unknown-id-string)))))
-
-(defvar tramp-gvfs-get-remote-uid-gid-in-progress nil
- "Indication, that remote uid and gid determination is in progress.")
-
(defun tramp-gvfs-get-remote-prefix (vec)
"The prefix of the remote connection VEC.
This is relevant for GNOME Online Accounts."
@@ -2229,16 +2222,7 @@ connection if a previous connection has died for some reason."
;; Mark it as connected.
(tramp-set-connection-property
- (tramp-get-connection-process vec) "connected" t))))
-
- ;; In `tramp-check-cached-permissions', the connection properties
- ;; "{uid,gid}-{integer,string}" are used. We set them to proper values.
- (unless tramp-gvfs-get-remote-uid-gid-in-progress
- (let ((tramp-gvfs-get-remote-uid-gid-in-progress t))
- (tramp-gvfs-get-remote-uid vec 'integer)
- (tramp-gvfs-get-remote-gid vec 'integer)
- (tramp-gvfs-get-remote-uid vec 'string)
- (tramp-gvfs-get-remote-gid vec 'string))))
+ (tramp-get-connection-process vec) "connected" t)))))
(defun tramp-gvfs-gio-tool-p (vec)
"Check, whether the gio tool is available."
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el
index 08bba33afed..3701bfc22c9 100644
--- a/lisp/net/tramp-rclone.el
+++ b/lisp/net/tramp-rclone.el
@@ -135,6 +135,8 @@
(start-file-process . ignore)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . ignore)
+ (tramp-get-remote-uid . ignore)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
@@ -457,7 +459,7 @@ file names."
;; to cache a nil result.
(or (tramp-get-connection-property
(tramp-get-connection-process vec) "mounted" nil)
- (let* ((default-directory temporary-file-directory)
+ (let* ((default-directory (tramp-compat-temporary-file-directory))
(mount (shell-command-to-string "mount -t fuse.rclone")))
(tramp-message vec 6 "%s" "mount -t fuse.rclone")
(tramp-message vec 6 "\n%s" mount)
@@ -483,7 +485,8 @@ file names."
;; crash Emacs for some processes. So we use
;; "pidof", which might not work everywhere.
(if (<= emacs-major-version 25)
- (let ((default-directory temporary-file-directory))
+ (let ((default-directory
+ (tramp-compat-temporary-file-directory)))
(mapcar
#'string-to-number
(split-string
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index a3ce436e42a..89e5dc9e658 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -1039,6 +1039,8 @@ of command line.")
(start-file-process . tramp-handle-start-file-process)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . tramp-sh-handle-get-remote-gid)
+ (tramp-get-remote-uid . tramp-sh-handle-get-remote-uid)
(tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid)
(unhandled-file-name-directory . ignore)
(vc-registered . tramp-sh-handle-vc-registered)
@@ -1153,59 +1155,9 @@ component is used as the target of the symlink."
(tramp-shell-quote-argument localname)))))
;; Do it yourself.
- (t (let ((steps (split-string localname "/" 'omit))
- (thisstep nil)
- (numchase 0)
- ;; Don't make the following value larger than
- ;; necessary. People expect an error message in a
- ;; timely fashion when something is wrong;
- ;; otherwise they might think that Emacs is hung.
- ;; Of course, correctness has to come first.
- (numchase-limit 20)
- symlink-target)
- (while (and steps (< numchase numchase-limit))
- (setq thisstep (pop steps))
- (tramp-message
- v 5 "Check %s"
- (string-join
- (append '("") (reverse result) (list thisstep)) "/"))
- (setq symlink-target
- (tramp-compat-file-attribute-type
- (file-attributes
- (tramp-make-tramp-file-name
- v
- (string-join
- (append
- '("") (reverse result) (list thisstep)) "/")
- 'nohop))))
- (cond ((string= "." thisstep)
- (tramp-message v 5 "Ignoring step `.'"))
- ((string= ".." thisstep)
- (tramp-message v 5 "Processing step `..'")
- (pop result))
- ((stringp symlink-target)
- ;; It's a symlink, follow it.
- (tramp-message
- v 5 "Follow symlink to %s" symlink-target)
- (setq numchase (1+ numchase))
- (when (file-name-absolute-p symlink-target)
- (setq result nil))
- (setq steps
- (append
- (split-string symlink-target "/" 'omit)
- steps)))
- (t
- ;; It's a file.
- (setq result (cons thisstep result)))))
- (when (>= numchase numchase-limit)
- (tramp-error
- v 'file-error
- "Maximum number (%d) of symlinks exceeded" numchase-limit))
- (setq result (reverse result)
- ;; Combine list to form string.
- result
- (if result (string-join (cons "" result) "/") "/"))
- (when (string-empty-p result) (setq result "/")))))
+ (t (setq
+ result
+ (tramp-file-local-name (tramp-handle-file-truename filename)))))
;; Detect cycle.
(when (and (file-symlink-p filename)
@@ -1379,18 +1331,11 @@ component is used as the target of the symlink."
(format
(eval-when-compile
(concat
- ;; On Opsware, pdksh (which is the true name of ksh there)
- ;; doesn't parse correctly the sequence "((". Therefore, we
- ;; add a space. Apostrophes in the stat output are masked as
+ ;; Apostrophes in the stat output are masked as
;; `tramp-stat-marker', in order to make a proper shell escape
;; of them in file names.
- "( (%s %s || %s -h %s) && (%s -c "
- "'((%s%%N%s) %%h %s %s %%X %%Y %%Z %%s %s%%A%s t %%i -1)' "
- "%s | sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g') || echo nil)"))
- (tramp-get-file-exists-command vec)
- (tramp-shell-quote-argument localname)
- (tramp-get-test-command vec)
- (tramp-shell-quote-argument localname)
+ "(%s -c '((%s%%N%s) %%h %s %s %%X %%Y %%Z %%s %s%%A%s t %%i -1)' %s |"
+ " sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g')"))
(tramp-get-remote-stat vec)
tramp-stat-marker tramp-stat-marker
(if (eq id-format 'integer)
@@ -1401,7 +1346,8 @@ component is used as the target of the symlink."
(eval-when-compile (concat tramp-stat-marker "%G" tramp-stat-marker)))
tramp-stat-marker tramp-stat-marker
(tramp-shell-quote-argument localname)
- tramp-stat-quoted-marker)))
+ tramp-stat-quoted-marker)
+ 'noerror))
(defun tramp-sh-handle-set-visited-file-modtime (&optional time-list)
"Like `set-visited-file-modtime' for Tramp files."
@@ -1517,6 +1463,26 @@ of."
(if (eq flag 'nofollow) "-h" "")
(tramp-shell-quote-argument localname)))))))
+(defun tramp-sh-handle-get-remote-uid (vec id-format)
+ "The uid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (ignore-errors
+ (cond
+ ((tramp-get-remote-id vec) (tramp-get-remote-uid-with-id vec id-format))
+ ((tramp-get-remote-perl vec) (tramp-get-remote-uid-with-perl vec id-format))
+ ((tramp-get-remote-python vec)
+ (tramp-get-remote-uid-with-python vec id-format)))))
+
+(defun tramp-sh-handle-get-remote-gid (vec id-format)
+ "The gid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (ignore-errors
+ (cond
+ ((tramp-get-remote-id vec) (tramp-get-remote-gid-with-id vec id-format))
+ ((tramp-get-remote-perl vec) (tramp-get-remote-gid-with-perl vec id-format))
+ ((tramp-get-remote-python vec)
+ (tramp-get-remote-gid-with-python vec id-format)))))
+
(defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid)
"Like `tramp-set-file-uid-gid' for Tramp files."
;; Modern Unices allow chown only for root. So we might need
@@ -1719,8 +1685,10 @@ of."
(defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group)
"Like `file-ownership-preserved-p' for Tramp files."
(with-parsed-tramp-file-name filename nil
- (with-tramp-file-property v localname "file-ownership-preserved-p"
- (let ((attributes (file-attributes filename)))
+ (with-tramp-file-property
+ v localname
+ (format "file-ownership-preserved-p%s" (if group "-group" ""))
+ (let ((attributes (file-attributes filename 'integer)))
;; Return t if the file doesn't exist, since it's true that no
;; information would be lost by an (attempted) delete and create.
(or (null attributes)
@@ -3024,16 +2992,16 @@ STDERR can also be a file name."
;; the process is deleted.
(when (bufferp stderr)
(with-current-buffer stderr
- (insert-file-contents-literally
- remote-tmpstderr 'visit))
+ (insert-file-contents-literally remote-tmpstderr))
;; Delete tmpstderr file.
(add-function
:after (process-sentinel p)
(lambda (_proc _msg)
- (with-current-buffer stderr
- (insert-file-contents-literally
- remote-tmpstderr 'visit nil nil 'replace))
- (delete-file remote-tmpstderr))))
+ (when (file-exists-p remote-tmpstderr)
+ (with-current-buffer stderr
+ (insert-file-contents-literally
+ remote-tmpstderr nil nil nil 'replace))
+ (delete-file remote-tmpstderr)))))
;; Return process.
p)))
@@ -4636,11 +4604,7 @@ Goes through the list `tramp-local-coding-commands' and
?o (tramp-get-remote-od vec)))
value (replace-regexp-in-string "%" "%%" value)))
(when (string-match-p "\\(^\\|[^%]\\)%t" value)
- (setq tmpfile
- (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix
- (tramp-get-remote-tmpdir vec)))
+ (setq tmpfile (tramp-make-tramp-temp-name vec)
value
(format-spec
value
@@ -5079,10 +5043,7 @@ connection if a previous connection has died for some reason."
(tmpfile
(with-tramp-connection-property
(tramp-get-process vec) "temp-file"
- (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix
- (tramp-compat-temporary-file-directory)))))
+ (tramp-compat-make-temp-name)))
spec r-shell)
;; Add arguments for asynchronous processes.
@@ -5302,7 +5263,10 @@ raises an error."
command marker (buffer-string))))))
;; Read the expression.
(condition-case nil
- (prog1 (read (current-buffer))
+ (prog1
+ (let ((signal-hook-function
+ (unless noerror signal-hook-function)))
+ (read (current-buffer)))
;; Error handling.
(when (re-search-forward "\\S-" (point-at-eol) t)
(error nil)))
@@ -5710,10 +5674,7 @@ This command is returned only if `delete-by-moving-to-trash' is non-nil."
(tramp-message vec 5 "Finding a suitable `touch' command")
(let ((result (tramp-find-executable
vec "touch" (tramp-get-remote-path vec)))
- (tmpfile
- (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix (tramp-get-remote-tmpdir vec)))))
+ (tmpfile (tramp-make-tramp-temp-name vec)))
;; Busyboxes do support the "-t" option only when they have been
;; built with the DESKTOP config option. Let's check it.
(when result
@@ -5828,27 +5789,6 @@ This command is returned only if `delete-by-moving-to-trash' is non-nil."
"import os; print (os.getuid())"
"import os, pwd; print ('\\\"' + pwd.getpwuid(os.getuid())[0] + '\\\"')"))))
-(defun tramp-get-remote-uid (vec id-format)
- "The uid of the remote connection VEC, in ID-FORMAT.
-ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "uid-%s" id-format)
- (let ((res
- (ignore-errors
- (cond
- ((tramp-get-remote-id vec)
- (tramp-get-remote-uid-with-id vec id-format))
- ((tramp-get-remote-perl vec)
- (tramp-get-remote-uid-with-perl vec id-format))
- ((tramp-get-remote-python vec)
- (tramp-get-remote-uid-with-python vec id-format))))))
- ;; Ensure there is a valid result.
- (cond
- ((and (equal id-format 'integer) (not (integerp res)))
- tramp-unknown-id-integer)
- ((and (equal id-format 'string) (not (stringp res)))
- tramp-unknown-id-string)
- (t res)))))
-
(defun tramp-get-remote-gid-with-id (vec id-format)
"Implement `tramp-get-remote-gid' for Tramp files using `id'."
(tramp-send-command-and-read
@@ -5879,27 +5819,6 @@ ID-FORMAT valid values are `string' and `integer'."
"import os; print (os.getgid())"
"import os, grp; print ('\\\"' + grp.getgrgid(os.getgid())[0] + '\\\"')"))))
-(defun tramp-get-remote-gid (vec id-format)
- "The gid of the remote connection VEC, in ID-FORMAT.
-ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "gid-%s" id-format)
- (let ((res
- (ignore-errors
- (cond
- ((tramp-get-remote-id vec)
- (tramp-get-remote-gid-with-id vec id-format))
- ((tramp-get-remote-perl vec)
- (tramp-get-remote-gid-with-perl vec id-format))
- ((tramp-get-remote-python vec)
- (tramp-get-remote-gid-with-python vec id-format))))))
- ;; Ensure there is a valid result.
- (cond
- ((and (equal id-format 'integer) (not (integerp res)))
- tramp-unknown-id-integer)
- ((and (equal id-format 'string) (not (stringp res)))
- tramp-unknown-id-string)
- (t res)))))
-
(defun tramp-get-remote-busybox (vec)
"Determine remote `busybox' command."
(with-tramp-connection-property vec "busybox"
@@ -5945,10 +5864,7 @@ ID-FORMAT valid values are `string' and `integer'."
"Check whether remote `chmod' supports nofollow argument."
(with-tramp-connection-property vec "chmod-h"
(tramp-message vec 5 "Finding a suitable `chmod' command with nofollow")
- (let ((tmpfile
- (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix (tramp-get-remote-tmpdir vec)))))
+ (let ((tmpfile (tramp-make-tramp-temp-name vec)))
(prog1
(tramp-send-command-and-check
vec
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el
index 2088d236288..357e9a220ce 100644
--- a/lisp/net/tramp-smb.el
+++ b/lisp/net/tramp-smb.el
@@ -293,6 +293,8 @@ See `tramp-actions-before-shell' for more info.")
(start-file-process . tramp-smb-handle-start-file-process)
(substitute-in-file-name . tramp-smb-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . ignore)
+ (tramp-get-remote-uid . ignore)
(tramp-set-file-uid-gid . ignore)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
@@ -436,11 +438,7 @@ pass to the OPERATION."
(cond
;; We must use a local temporary directory.
((and t1 t2)
- (let ((tmpdir
- (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix
- (tramp-compat-temporary-file-directory)))))
+ (let ((tmpdir (tramp-compat-make-temp-name)))
(unwind-protect
(progn
(make-directory tmpdir)
@@ -468,10 +466,7 @@ pass to the OPERATION."
(localname (file-name-as-directory
(replace-regexp-in-string
"\\\\" "/" (tramp-smb-get-localname v))))
- (tmpdir (make-temp-name
- (expand-file-name
- tramp-temp-name-prefix
- (tramp-compat-temporary-file-directory))))
+ (tmpdir (tramp-compat-make-temp-name))
(args (list (concat "//" host "/" share) "-E"))
(options tramp-smb-options))
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 68e68a242c9..05242ffd970 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -132,6 +132,8 @@ See `tramp-actions-before-shell' for more info.")
(start-file-process . ignore)
(substitute-in-file-name . tramp-handle-substitute-in-file-name)
(temporary-file-directory . tramp-handle-temporary-file-directory)
+ (tramp-get-remote-gid . tramp-sudoedit-handle-get-remote-gid)
+ (tramp-get-remote-uid . tramp-sudoedit-handle-get-remote-uid)
(tramp-set-file-uid-gid . tramp-sudoedit-handle-set-file-uid-gid)
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
@@ -689,21 +691,19 @@ component is used as the target of the symlink."
(tramp-flush-file-property v localname "file-selinux-context"))
t)))))
-(defun tramp-sudoedit-get-remote-uid (vec id-format)
+(defun tramp-sudoedit-handle-get-remote-uid (vec id-format)
"The uid of the remote connection VEC, in ID-FORMAT.
ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "uid-%s" id-format)
- (if (equal id-format 'integer)
- (tramp-sudoedit-send-command-and-read vec "id" "-u")
- (tramp-sudoedit-send-command-string vec "id" "-un"))))
+ (if (equal id-format 'integer)
+ (tramp-sudoedit-send-command-and-read vec "id" "-u")
+ (tramp-sudoedit-send-command-string vec "id" "-un")))
-(defun tramp-sudoedit-get-remote-gid (vec id-format)
+(defun tramp-sudoedit-handle-get-remote-gid (vec id-format)
"The gid of the remote connection VEC, in ID-FORMAT.
ID-FORMAT valid values are `string' and `integer'."
- (with-tramp-connection-property vec (format "gid-%s" id-format)
- (if (equal id-format 'integer)
- (tramp-sudoedit-send-command-and-read vec "id" "-g")
- (tramp-sudoedit-send-command-string vec "id" "-gn"))))
+ (if (equal id-format 'integer)
+ (tramp-sudoedit-send-command-and-read vec "id" "-g")
+ (tramp-sudoedit-send-command-string vec "id" "-gn")))
(defun tramp-sudoedit-handle-set-file-uid-gid (filename &optional uid gid)
"Like `tramp-set-file-uid-gid' for Tramp files."
@@ -711,8 +711,8 @@ ID-FORMAT valid values are `string' and `integer'."
(tramp-sudoedit-send-command
v "chown"
(format "%d:%d"
- (or uid (tramp-sudoedit-get-remote-uid v 'integer))
- (or gid (tramp-sudoedit-get-remote-gid v 'integer)))
+ (or uid (tramp-get-remote-uid v 'integer))
+ (or gid (tramp-get-remote-gid v 'integer)))
(tramp-unquote-file-local-name filename))))
(defun tramp-sudoedit-handle-write-region
@@ -721,10 +721,10 @@ ID-FORMAT valid values are `string' and `integer'."
(with-parsed-tramp-file-name filename nil
(let* ((uid (or (tramp-compat-file-attribute-user-id
(file-attributes filename 'integer))
- (tramp-sudoedit-get-remote-uid v 'integer)))
+ (tramp-get-remote-uid v 'integer)))
(gid (or (tramp-compat-file-attribute-group-id
(file-attributes filename 'integer))
- (tramp-sudoedit-get-remote-gid v 'integer)))
+ (tramp-get-remote-gid v 'integer)))
(flag (and (eq mustbenew 'excl) 'nofollow))
(modes (tramp-default-file-modes filename flag)))
(prog1
@@ -785,14 +785,7 @@ connection if a previous connection has died for some reason."
(tramp-set-connection-local-variables vec)
;; Mark it as connected.
- (tramp-set-connection-property p "connected" t))
-
- ;; In `tramp-check-cached-permissions', the connection properties
- ;; "{uid,gid}-{integer,string}" are used. We set them to proper values.
- (tramp-sudoedit-get-remote-uid vec 'integer)
- (tramp-sudoedit-get-remote-gid vec 'integer)
- (tramp-sudoedit-get-remote-uid vec 'string)
- (tramp-sudoedit-get-remote-gid vec 'string)))
+ (tramp-set-connection-property p "connected" t))))
(defun tramp-sudoedit-send-command (vec &rest args)
"Send commands ARGS to connection VEC.
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index f1db6a7be29..1566162feaf 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -64,6 +64,7 @@
;; Pacify byte-compiler.
(require 'cl-lib)
+(declare-function file-notify-rm-watch "filenotify")
(declare-function netrc-parse "netrc")
(defvar auto-save-file-name-transforms)
@@ -1780,6 +1781,10 @@ ARGUMENTS to actually emit the message (if applicable)."
(put #'tramp-debug-message 'tramp-suppress-trace t)
+(defvar tramp-inhibit-progress-reporter nil
+ "Show Tramp progress reporter in the minibuffer.
+This variable is used to disable concurrent progress reporter messages.")
+
(defsubst tramp-message (vec-or-proc level fmt-string &rest arguments)
"Emit a message depending on verbosity level.
VEC-OR-PROC identifies the Tramp buffer to use. It can be either a
@@ -1795,8 +1800,9 @@ control string and the remaining ARGUMENTS to actually emit the message (if
applicable)."
(ignore-errors
(when (<= level tramp-verbose)
- ;; Display only when there is a minimum level.
- (when (<= level 3)
+ ;; Display only when there is a minimum level, and the progress
+ ;; reporter doesn't suppress further messages.
+ (when (and (<= level 3) (null tramp-inhibit-progress-reporter))
(apply #'message
(concat
(cond
@@ -2014,7 +2020,12 @@ without a visible progress reporter."
(run-at-time 3 0.1 #'tramp-progress-reporter-update pr))))
(unwind-protect
;; Execute the body.
- (prog1 (progn ,@body) (setq cookie "done"))
+ (prog1
+ ;; Suppress concurrent progress reporter messages.
+ (let ((tramp-inhibit-progress-reporter
+ (or tramp-inhibit-progress-reporter tm)))
+ ,@body)
+ (setq cookie "done"))
;; Stop progress reporter.
(if tm (cancel-timer tm))
(tramp-message ,vec ,level "%s...%s" ,message cookie)))))
@@ -2248,7 +2259,7 @@ Must be handled by the callers."
file-newer-than-file-p rename-file))
(cond
((tramp-tramp-file-p (nth 0 args)) (nth 0 args))
- ((tramp-tramp-file-p (nth 1 args)) (nth 1 args))
+ ((file-name-absolute-p (nth 1 args)) (nth 1 args))
(t default-directory)))
;; FILE DIRECTORY resp FILE1 FILE2.
((eq operation 'expand-file-name)
@@ -2280,6 +2291,9 @@ Must be handled by the callers."
(when (processp (nth 0 args))
(with-current-buffer (process-buffer (nth 0 args))
default-directory)))
+ ;; VEC.
+ ((member operation '(tramp-get-remote-gid tramp-get-remote-uid))
+ (tramp-make-tramp-file-name (nth 0 args)))
;; Unknown file primitive.
(t (error "Unknown file I/O primitive: %s" operation))))
@@ -2436,6 +2450,8 @@ Falls back to normal file name handler if no Tramp file name handler exists."
"Load Tramp file name handler, and perform OPERATION."
(tramp-unload-file-name-handlers)
(when tramp-mode
+ ;; We cannot use `tramp-compat-temporary-file-directory' here due
+ ;; to autoload.
(let ((default-directory temporary-file-directory))
(load "tramp" 'noerror 'nomessage)))
(apply operation args)))
@@ -3381,6 +3397,8 @@ User is always nil."
;; something is wrong; otherwise they might think that Emacs
;; is hung. Of course, correctness has to come first.
(numchase-limit 20)
+ ;; Unquoting could enable encryption.
+ tramp-crypt-enabled
symlink-target)
(with-parsed-tramp-file-name result v1
;; We cache only the localname.
@@ -3612,7 +3630,8 @@ User is always nil."
v tramp-file-missing "Cannot load nonexistent file `%s'" file))
(if (not (file-exists-p file))
nil
- (let ((inhibit-message nomessage))
+ (let ((signal-hook-function (unless noerror signal-hook-function))
+ (inhibit-message (or inhibit-message nomessage)))
(with-tramp-progress-reporter v 0 (format "Loading %s" file)
(let ((local-copy (file-local-copy file)))
(unwind-protect
@@ -3900,7 +3919,13 @@ of."
(let ((tmpfile (tramp-compat-make-temp-file filename))
(modes (tramp-default-file-modes
- filename (and (eq mustbenew 'excl) 'nofollow))))
+ filename (and (eq mustbenew 'excl) 'nofollow)))
+ (uid (or (tramp-compat-file-attribute-user-id
+ (file-attributes filename 'integer))
+ (tramp-get-remote-uid v 'integer)))
+ (gid (or (tramp-compat-file-attribute-group-id
+ (file-attributes filename 'integer))
+ (tramp-get-remote-gid v 'integer))))
(when (and append (file-exists-p filename))
(copy-file filename tmpfile 'ok))
;; The permissions of the temporary file should be set. If
@@ -3919,15 +3944,18 @@ of."
(error
(delete-file tmpfile)
(tramp-error
- v 'file-error "Couldn't write region to `%s'" filename))))
+ v 'file-error "Couldn't write region to `%s'" filename)))
- (tramp-flush-file-properties v localname)
+ (tramp-flush-file-properties v localname)
- ;; Set file modification time.
- (when (or (eq visit t) (stringp visit))
- (set-visited-file-modtime
- (tramp-compat-file-attribute-modification-time
- (file-attributes filename))))
+ ;; Set file modification time.
+ (when (or (eq visit t) (stringp visit))
+ (set-visited-file-modtime
+ (tramp-compat-file-attribute-modification-time
+ (file-attributes filename))))
+
+ ;; Set the ownership.
+ (tramp-set-file-uid-gid filename uid gid))
;; The end.
(when (and (null noninteractive)
@@ -3981,7 +4009,7 @@ of."
"Call `file-notify-rm-watch'."
(unless (process-live-p proc)
(tramp-message proc 5 "Sentinel called: `%S' `%s'" proc event)
- (tramp-compat-funcall 'file-notify-rm-watch proc)))
+ (file-notify-rm-watch proc)))
;;; Functions for establishing connection:
@@ -4603,12 +4631,8 @@ be granted."
(concat "file-attributes-" suffix) nil)
(file-attributes
(tramp-make-tramp-file-name vec) (intern suffix))))
- (remote-uid
- (tramp-get-connection-property
- vec (concat "uid-" suffix) nil))
- (remote-gid
- (tramp-get-connection-property
- vec (concat "gid-" suffix) nil))
+ (remote-uid (tramp-get-remote-uid vec (intern suffix)))
+ (remote-gid (tramp-get-remote-gid vec (intern suffix)))
(unknown-id
(if (string-equal suffix "string")
tramp-unknown-id-string tramp-unknown-id-integer)))
@@ -4642,6 +4666,32 @@ be granted."
(tramp-compat-file-attribute-group-id
file-attr))))))))))))
+(defun tramp-get-remote-uid (vec id-format)
+ "The uid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (with-tramp-connection-property vec (format "uid-%s" id-format)
+ (or (when-let
+ ((handler
+ (find-file-name-handler
+ (tramp-make-tramp-file-name vec) 'tramp-get-remote-uid)))
+ (funcall handler #'tramp-get-remote-uid vec id-format))
+ ;; Ensure there is a valid result.
+ (and (equal id-format 'integer) tramp-unknown-id-integer)
+ (and (equal id-format 'string) tramp-unknown-id-string))))
+
+(defun tramp-get-remote-gid (vec id-format)
+ "The gid of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+ (with-tramp-connection-property vec (format "gid-%s" id-format)
+ (or (when-let
+ ((handler
+ (find-file-name-handler
+ (tramp-make-tramp-file-name vec) 'tramp-get-remote-uid)))
+ (funcall handler #'tramp-get-remote-gid vec id-format))
+ ;; Ensure there is a valid result.
+ (and (equal id-format 'integer) tramp-unknown-id-integer)
+ (and (equal id-format 'string) tramp-unknown-id-string))))
+
(defun tramp-local-host-p (vec)
"Return t if this points to the local host, nil otherwise.
This handles also chrooted environments, which are not regarded as local."
@@ -4664,9 +4714,7 @@ This handles also chrooted environments, which are not regarded as local."
vec (tramp-compat-temporary-file-directory) 'nohop))
;; On some systems, chown runs only for root.
(or (zerop (user-uid))
- ;; This is defined in tramp-sh.el. Let's assume this is
- ;; loaded already.
- (zerop (tramp-compat-funcall 'tramp-get-remote-uid vec 'integer))))))
+ (zerop (tramp-get-remote-uid vec 'integer))))))
(defun tramp-get-remote-tmpdir (vec)
"Return directory for temporary files on the remote host identified by VEC."
@@ -4679,18 +4727,21 @@ This handles also chrooted environments, which are not regarded as local."
(tramp-error vec 'file-error "Directory %s not accessible" dir))
dir)))
+(defun tramp-make-tramp-temp-name (vec)
+ "Generate a temporary file name on the remote host identified by VEC."
+ (make-temp-name
+ (expand-file-name tramp-temp-name-prefix (tramp-get-remote-tmpdir vec))))
+
(defun tramp-make-tramp-temp-file (vec)
"Create a temporary file on the remote host identified by VEC.
Return the local name of the temporary file."
- (let ((prefix (expand-file-name
- tramp-temp-name-prefix (tramp-get-remote-tmpdir vec)))
- result)
+ (let (result)
(while (not result)
;; `make-temp-file' would be the natural choice for
;; implementation. But it calls `write-region' internally,
;; which also needs a temporary file - we would end in an
;; infinite loop.
- (setq result (make-temp-name prefix))
+ (setq result (tramp-make-tramp-temp-name vec))
(if (file-exists-p result)
(setq result nil)
;; This creates the file by side effect.
diff --git a/lisp/obsolete/tls.el b/lisp/obsolete/tls.el
index cd091c0108e..d1b215cbfb8 100644
--- a/lisp/obsolete/tls.el
+++ b/lisp/obsolete/tls.el
@@ -47,9 +47,6 @@
(require 'gnutls)
-(autoload 'format-spec "format-spec")
-(autoload 'format-spec-make "format-spec")
-
(defgroup tls nil
"Transport Layer Security (TLS) parameters."
:group 'comm)
@@ -224,14 +221,11 @@ Fourth arg PORT is an integer specifying a port to connect to."
(while (and (not done) (setq cmd (pop cmds)))
(let ((process-connection-type tls-process-connection-type)
(formatted-cmd
- (format-spec
- cmd
- (format-spec-make
- ?t (car (gnutls-trustfiles))
- ?h host
- ?p (if (integerp port)
- (int-to-string port)
- port)))))
+ (format-spec cmd `((?t . ,(car (gnutls-trustfiles)))
+ (?h . ,host)
+ (?p . ,(if (integerp port)
+ (number-to-string port)
+ port))))))
(message "Opening TLS connection with `%s'..." formatted-cmd)
(setq process (start-process
name buffer shell-file-name shell-command-switch
diff --git a/lisp/progmodes/bug-reference.el b/lisp/progmodes/bug-reference.el
index 02af263ec34..9df51c1242a 100644
--- a/lisp/progmodes/bug-reference.el
+++ b/lisp/progmodes/bug-reference.el
@@ -139,12 +139,232 @@ The second subexpression should match the bug reference (usually a number)."
(when url
(browse-url url))))))
+(defun bug-reference--maybe-setup-from-vc (url url-rx bug-rx bug-url-fmt)
+ (when (string-match url-rx url)
+ (setq-local bug-reference-bug-regexp bug-rx)
+ (setq-local bug-reference-url-format
+ (let (groups)
+ (dotimes (i (/ (length (match-data)) 2))
+ (push (match-string i url) groups))
+ (funcall bug-url-fmt (nreverse groups))))))
+
+(defvar bug-reference-setup-from-vc-alist
+ `(;;
+ ;; GNU projects on savannah.
+ ;;
+ ;; Not all of them use debbugs but that doesn't really matter
+ ;; because the auto-setup is only performed if
+ ;; `bug-reference-url-format' and `bug-reference-bug-regexp'
+ ;; aren't set already.
+ ("git\\.\\(?:sv\\|savannah\\)\\.gnu\\.org:"
+ "\\<\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\>"
+ ,(lambda (_) "https://debbugs.gnu.org/%s"))
+ ;;
+ ;; GitHub projects.
+ ;;
+ ;; Here #17 may refer to either an issue or a pull request but
+ ;; visiting the issue/17 web page will automatically redirect to
+ ;; the pull/17 page if 17 is a PR. Explicit user/project#17 links
+ ;; to possibly different projects are also supported.
+ ("[/@]github.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git"
+ "\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>"
+ ,(lambda (groups)
+ (let ((ns-project (nth 1 groups)))
+ (lambda ()
+ (concat "https://github.com/"
+ (or
+ ;; Explicit user/proj#18 link.
+ (match-string 1)
+ ns-project)
+ "/issues/"
+ (match-string 2))))))
+ ;;
+ ;; GitLab projects.
+ ;;
+ ;; Here #18 is an issue and !17 is a merge request. Explicit
+ ;; namespace/project#18 or namespace/project!17 references to
+ ;; possibly different projects are also supported.
+ ("[/@]gitlab.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git"
+ "\\(?1:[.A-Za-z0-9_/-]+\\)?\\(?3:[#!]\\)\\(?2:[0-9]+\\)\\>"
+ ,(lambda (groups)
+ (let ((ns-project (nth 1 groups)))
+ (lambda ()
+ (concat "https://gitlab.com/"
+ (or (match-string 1)
+ ns-project)
+ "/-/"
+ (if (string= (match-string 3) "#")
+ "issues/"
+ "merge_requests/")
+ (match-string 2)))))))
+ "An alist for setting up `bug-reference-mode' based on VC URL.
+
+Each element has the form (URL-REGEXP BUG-REGEXP URL-FORMAT-FN).
+
+URL-REGEXP is matched against the version control URL of the
+current buffer's file. If it matches, BUG-REGEXP is set as
+`bug-reference-bug-regexp'. URL-FORMAT-FN is a function of one
+argument that receives a list of the groups 0 to N of matching
+URL-REGEXP against the VCS URL and returns the value to be set as
+`bug-reference-url-format'.")
+
+(defun bug-reference-try-setup-from-vc ()
+ "Try setting up `bug-reference-mode' based on VC information.
+Test each configuration in `bug-reference-setup-from-vc-alist'
+and apply it if applicable."
+ (let ((file-or-dir (or buffer-file-name
+ ;; Catches modes such as vc-dir and Magit.
+ default-directory)))
+ (when file-or-dir
+ (let* ((backend (vc-responsible-backend file-or-dir t))
+ (url
+ (or (ignore-errors
+ (vc-call-backend backend 'repository-url "upstream"))
+ (ignore-errors
+ (vc-call-backend backend 'repository-url)))))
+ (when url
+ (catch 'found
+ (dolist (config bug-reference-setup-from-vc-alist)
+ (when (apply #'bug-reference--maybe-setup-from-vc
+ url config)
+ (throw 'found t)))))))))
+
+(defvar bug-reference-setup-from-mail-alist
+ `((,(regexp-opt '("emacs" "auctex" "gnus") 'words)
+ ,(regexp-opt '("@debbugs.gnu.org" "-devel@gnu.org"
+ ;; List-Id of Gnus devel mailing list.
+ "ding.gnus.org"))
+ "\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)"
+ "https://debbugs.gnu.org/%s"))
+ "An alist for setting up `bug-reference-mode' in mail modes.
+
+This takes action if `bug-reference-mode' is enabled in group and
+message buffers of Emacs mail clients. Currently, only Gnus is
+supported.
+
+Each element has the form
+
+ (GROUP-REGEXP HEADER-REGEXP BUG-REGEXP URL-FORMAT)
+
+GROUP-REGEXP is a regexp matched against the current mail folder
+or newsgroup name. HEADER-REGEXP is a regexp matched against the
+From, To, Cc, Newsgroup, and List-ID header values of the current
+mail or newsgroup message. If any of those matches, BUG-REGEXP
+is set as `bug-reference-bug-regexp' and URL-FORMAT is set as
+`bug-reference-url-format'.
+
+Note: In Gnus, if a summary buffer has been set up based on
+GROUP-REGEXP, all article buffers opened from there will get the
+same `bug-reference-url-format' and `bug-reference-url-format'.")
+
+(defvar gnus-newsgroup-name)
+
+(defun bug-reference--maybe-setup-from-mail (group header-values)
+ "Set up according to mail GROUP or HEADER-VALUES.
+Group is a mail group/folder name and HEADER-VALUES is a list of
+mail header values, e.g., the values of From, To, Cc, List-ID,
+and Newsgroup.
+
+If any GROUP-REGEXP or HEADER-REGEXP of
+`bug-reference-setup-from-mail-alist' matches GROUP or any
+element in HEADER-VALUES, the corresponding BUG-REGEXP and
+URL-FORMAT are set."
+ (catch 'setup-done
+ (dolist (config bug-reference-setup-from-mail-alist)
+ (when (or
+ (and group
+ (car config)
+ (string-match-p (car config) group))
+ (and header-values
+ (nth 1 config)
+ (catch 'matching-header
+ (dolist (h header-values)
+ (when (and h (string-match-p (nth 1 config) h))
+ (throw 'matching-header t))))))
+ (setq-local bug-reference-bug-regexp (nth 2 config))
+ (setq-local bug-reference-url-format (nth 3 config))
+ (throw 'setup-done t)))))
+
+(defun bug-reference-try-setup-from-gnus ()
+ "Try setting up `bug-reference-mode' based on Gnus group or article.
+Test each configuration in `bug-reference-setup-from-mail-alist'
+and set it if applicable."
+ (when (and (derived-mode-p 'gnus-summary-mode)
+ (bound-and-true-p gnus-newsgroup-name))
+ ;; Gnus reuses its article buffer so we have to check whenever the
+ ;; article changes.
+ (add-hook 'gnus-article-prepare-hook
+ #'bug-reference--try-setup-gnus-article)
+ (bug-reference--maybe-setup-from-mail gnus-newsgroup-name nil)))
+
+(defvar gnus-article-buffer)
+(defvar gnus-original-article-buffer)
+(defvar gnus-summary-buffer)
+
+(defun bug-reference--try-setup-gnus-article ()
+ (with-demoted-errors
+ "Error in bug-reference--try-setup-gnus-article: %S"
+ (when (and bug-reference-mode ;; Only if enabled in article buffers.
+ (derived-mode-p
+ 'gnus-article-mode
+ ;; Apparently, gnus-article-prepare-hook is run in the
+ ;; summary buffer...
+ 'gnus-summary-mode)
+ gnus-article-buffer
+ gnus-original-article-buffer
+ (buffer-live-p (get-buffer gnus-article-buffer))
+ (buffer-live-p (get-buffer gnus-original-article-buffer)))
+ (with-current-buffer gnus-article-buffer
+ (catch 'setup-done
+ ;; Copy over the values from the summary buffer.
+ (when (and gnus-summary-buffer
+ (buffer-live-p gnus-summary-buffer))
+ (setq-local bug-reference-bug-regexp
+ (with-current-buffer gnus-summary-buffer
+ bug-reference-bug-regexp))
+ (setq-local bug-reference-url-format
+ (with-current-buffer gnus-summary-buffer
+ bug-reference-url-format))
+ (when (and bug-reference-bug-regexp
+ bug-reference-url-format)
+ (throw 'setup-done t)))
+ ;; If the summary had no values, try setting according to
+ ;; the values of the From, To, and Cc headers.
+ (let (header-values)
+ (with-current-buffer
+ (get-buffer gnus-original-article-buffer)
+ (save-excursion
+ (goto-char (point-min))
+ ;; The Newsgroup is omitted because we already matched
+ ;; based on group name in the summary buffer.
+ (dolist (field '("list-id" "to" "from" "cc"))
+ (let ((val (mail-fetch-field field)))
+ (when val
+ (push val header-values))))))
+ (bug-reference--maybe-setup-from-mail nil header-values)))))))
+
+(defun bug-reference--run-auto-setup ()
+ (when (or bug-reference-mode
+ bug-reference-prog-mode)
+ ;; Automatic setup only if the variables aren't already set, e.g.,
+ ;; by a local variables section in the file.
+ (unless (and bug-reference-bug-regexp
+ bug-reference-url-format)
+ (with-demoted-errors
+ "Error during bug-reference auto-setup: %S"
+ (catch 'setup
+ (dolist (f (list #'bug-reference-try-setup-from-vc
+ #'bug-reference-try-setup-from-gnus))
+ (when (funcall f)
+ (throw 'setup t))))))))
+
;;;###autoload
(define-minor-mode bug-reference-mode
"Toggle hyperlinking bug references in the buffer (Bug Reference mode)."
nil
""
nil
+ :after-hook (bug-reference--run-auto-setup)
(if bug-reference-mode
(jit-lock-register #'bug-reference-fontify)
(jit-lock-unregister #'bug-reference-fontify)
@@ -158,6 +378,7 @@ The second subexpression should match the bug reference (usually a number)."
nil
""
nil
+ :after-hook (bug-reference--run-auto-setup)
(if bug-reference-prog-mode
(jit-lock-register #'bug-reference-fontify)
(jit-lock-unregister #'bug-reference-fontify)
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index f3df44fa7ba..bfbe2362721 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1,7 +1,7 @@
;;; project.el --- Operations on the current project -*- lexical-binding: t; -*-
;; Copyright (C) 2015-2020 Free Software Foundation, Inc.
-;; Version: 0.3.0
+;; Version: 0.4.0
;; Package-Requires: ((emacs "26.3"))
;; This is a GNU ELPA :core package. Avoid using functionality that
@@ -195,7 +195,7 @@ subset of the project root and external roots.
The default implementation uses `find-program'. PROJECT is used
to find the list of ignores for each directory."
- (cl-mapcan
+ (mapcan
(lambda (dir)
(project--files-in-directory dir
(project--dir-ignores project dir)))
@@ -306,7 +306,7 @@ backend implementation of `project-external-roots'.")
(if (and
;; FIXME: Invalidate the cache when the value
;; of this variable changes.
- project-vc-merge-submodules
+ (project--vc-merge-submodules-p root)
(project--submodule-p root))
(let* ((parent (file-name-directory
(directory-file-name root))))
@@ -351,7 +351,7 @@ backend implementation of `project-external-roots'.")
(list (project-root project))))
(cl-defmethod project-files ((project (head vc)) &optional dirs)
- (cl-mapcan
+ (mapcan
(lambda (dir)
(let (backend)
(if (and (file-equal-p dir (cdr project))
@@ -396,19 +396,20 @@ backend implementation of `project-external-roots'.")
(split-string
(apply #'vc-git--run-command-string nil "ls-files" args)
"\0" t)))
- ;; Unfortunately, 'ls-files --recurse-submodules' conflicts with '-o'.
- (let* ((submodules (project--git-submodules))
- (sub-files
- (mapcar
- (lambda (module)
- (when (file-directory-p module)
- (project--vc-list-files
- (concat default-directory module)
- backend
- extra-ignores)))
- submodules)))
- (setq files
- (apply #'nconc files sub-files)))
+ (when (project--vc-merge-submodules-p default-directory)
+ ;; Unfortunately, 'ls-files --recurse-submodules' conflicts with '-o'.
+ (let* ((submodules (project--git-submodules))
+ (sub-files
+ (mapcar
+ (lambda (module)
+ (when (file-directory-p module)
+ (project--vc-list-files
+ (concat default-directory module)
+ backend
+ extra-ignores)))
+ submodules)))
+ (setq files
+ (apply #'nconc files sub-files))))
;; 'git ls-files' returns duplicate entries for merge conflicts.
;; XXX: Better solutions welcome, but this seems cheap enough.
(delete-consecutive-dups files)))
@@ -429,6 +430,11 @@ backend implementation of `project-external-roots'.")
(lambda (s) (concat default-directory s))
(split-string (buffer-string) "\0" t)))))))
+(defun project--vc-merge-submodules-p (dir)
+ (project--value-in-dir
+ 'project-vc-merge-submodules
+ dir))
+
(defun project--git-submodules ()
;; 'git submodule foreach' is much slower.
(condition-case nil
@@ -484,6 +490,28 @@ DIRS must contain directory names."
;; Sidestep the issue of expanded/abbreviated file names here.
(cl-set-difference files dirs :test #'file-in-directory-p))
+
+;;; Project commands
+
+;;;###autoload
+(defvar project-prefix-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "f" 'project-find-file)
+ (define-key map "b" 'project-switch-to-buffer)
+ (define-key map "s" 'project-shell)
+ (define-key map "d" 'project-dired)
+ (define-key map "v" 'project-vc-dir)
+ (define-key map "c" 'project-compile)
+ (define-key map "e" 'project-eshell)
+ (define-key map "k" 'project-kill-buffers)
+ (define-key map "p" 'project-switch-project)
+ (define-key map "g" 'project-find-regexp)
+ (define-key map "r" 'project-query-replace-regexp)
+ map)
+ "Keymap for project commands.")
+
+;;;###autoload (define-key ctl-x-map "p" project-prefix-map)
+
(defun project--value-in-dir (var dir)
(with-temp-buffer
(setq default-directory dir)
@@ -644,6 +672,7 @@ PREDICATE, HIST, and DEFAULT have the same meaning as in
(defun project-find-file-in (filename dirs project)
"Complete FILENAME in DIRS in PROJECT and visit the result."
(let* ((all-files (project-files project dirs))
+ (completion-ignore-case read-file-name-completion-ignore-case)
(file (funcall project-read-file-name-function
"Find file" all-files nil nil
filename)))
@@ -674,30 +703,53 @@ PREDICATE, HIST, and DEFAULT have the same meaning as in
;;;###autoload
(defun project-dired ()
- "Open Dired in the current project."
+ "Start Dired in the current project's root."
(interactive)
(dired (project-root (project-current t))))
;;;###autoload
(defun project-vc-dir ()
- "Open VC-Dir in the current project."
+ "Run VC-Dir in the current project's root."
(interactive)
(vc-dir (project-root (project-current t))))
;;;###autoload
(defun project-shell ()
- "Open Shell in the current project."
+ "Start an inferior shell in the current project's root directory.
+If a buffer already exists for running a shell in the project's root,
+switch to it. Otherwise, create a new shell buffer.
+With \\[universal-argument] prefix arg, create a new inferior shell buffer even
+if one already exist."
(interactive)
- (let ((default-directory (project-root (project-current t))))
- ;; Use ‘create-file-buffer’ to uniquify shell buffer names.
- (shell (create-file-buffer "*shell*"))))
+ (let* ((default-directory (project-root (project-current t)))
+ (default-project-shell-name
+ (concat "*" (file-name-nondirectory
+ (directory-file-name
+ (file-name-directory default-directory)))
+ "-shell*"))
+ (shell-buffer (get-buffer default-project-shell-name)))
+ (if (and shell-buffer (not current-prefix-arg))
+ (pop-to-buffer shell-buffer)
+ (shell (generate-new-buffer-name default-project-shell-name)))))
;;;###autoload
(defun project-eshell ()
- "Open Eshell in the current project."
+ "Start Eshell in the current project's root directory.
+If a buffer already exists for running Eshell in the project's root,
+switch to it. Otherwise, create a new Eshell buffer.
+With \\[universal-argument] prefix arg, create a new Eshell buffer even
+if one already exist."
(interactive)
- (let ((default-directory (project-root (project-current t))))
- (eshell t)))
+ (let* ((default-directory (project-root (project-current t)))
+ (eshell-buffer-name
+ (concat "*" (file-name-nondirectory
+ (directory-file-name
+ (file-name-directory default-directory)))
+ "-eshell*"))
+ (eshell-buffer (get-buffer eshell-buffer-name)))
+ (if (and eshell-buffer (not current-prefix-arg))
+ (pop-to-buffer eshell-buffer)
+ (eshell t))))
(declare-function fileloop-continue "fileloop" ())
@@ -744,11 +796,76 @@ Arguments the same as in `compile'."
(default-directory (project-root pr)))
(compile command comint)))
+;;;###autoload
+(defun project-switch-to-buffer ()
+ "Switch to another buffer that is related to the current project.
+A buffer is related to a project if its `default-directory'
+is inside the directory hierarchy of the project's root."
+ (interactive)
+ (let* ((root (project-root (project-current t)))
+ (current-buffer (current-buffer))
+ (other-buffer (other-buffer current-buffer))
+ (other-name (buffer-name other-buffer))
+ (predicate
+ (lambda (buffer)
+ ;; BUFFER is an entry (BUF-NAME . BUF-OBJ) of Vbuffer_alist.
+ (and (not (eq (cdr buffer) current-buffer))
+ (when-let ((file (buffer-local-value 'default-directory
+ (cdr buffer))))
+ (file-in-directory-p file root))))))
+ (switch-to-buffer
+ (read-buffer
+ "Switch to buffer: "
+ (when (funcall predicate (cons other-name other-buffer))
+ other-name)
+ t
+ predicate))))
+
+(defcustom project-kill-buffers-skip-conditions
+ '("\\*Help\\*")
+ "Conditions for buffers `project-kill-buffers' should not kill.
+Each condition is either a regular expression matching a buffer
+name, or a predicate function that takes a buffer object as
+argument and returns non-nil if it matches. Buffers that match
+any of the conditions will not be killed."
+ :type '(repeat (choice regexp function))
+ :version "28.1")
+
+(defun project--buffer-list (pr)
+ "Return the list of all buffers in project PR."
+ (let ((root (project-root pr))
+ bufs)
+ (dolist (buf (buffer-list))
+ (let ((filename (or (buffer-file-name buf)
+ (buffer-local-value 'default-directory buf))))
+ (when (and filename (file-in-directory-p filename root))
+ (push buf bufs))))
+ (nreverse bufs)))
+
+;;;###autoload
+(defun project-kill-buffers ()
+ "Kill all live buffers belonging to the current project.
+Certain buffers may be \"spared\", see `project-kill-buffers-skip-conditions'."
+ (interactive)
+ (let ((pr (project-current t)) bufs)
+ (dolist (buf (project--buffer-list pr))
+ (unless (seq-some
+ (lambda (c)
+ (cond ((stringp c)
+ (string-match-p c (buffer-name buf)))
+ ((functionp c)
+ (funcall c buf))))
+ project-kill-buffers-skip-conditions)
+ (push buf bufs)))
+ (when (yes-or-no-p (format "Kill %d buffers in %s? "
+ (length bufs) (project-root pr)))
+ (mapc #'kill-buffer bufs))))
+
;;; Project list
(defcustom project-list-file (locate-user-emacs-file "projects")
- "File to save the list of known projects."
+ "File in which to save the list of known projects."
:type 'file
:version "28.1"
:group 'project)
@@ -757,7 +874,7 @@ Arguments the same as in `compile'."
"List of known project directories.")
(defun project--read-project-list ()
- "Initialize `project--list' from the project list file."
+ "Initialize `project--list' using contents of `project-list-file'."
(let ((filename project-list-file))
(setq project--list
(when (file-exists-p filename)
@@ -766,12 +883,12 @@ Arguments the same as in `compile'."
(read (current-buffer)))))))
(defun project--ensure-read-project-list ()
- "Initialize `project--list' if it hasn't already been."
+ "Initialize `project--list' if it isn't already initialized."
(when (eq project--list 'unset)
(project--read-project-list)))
(defun project--write-project-list ()
- "Persist `project--list' to the project list file."
+ "Save `project--list' in `project-list-file'."
(let ((filename project-list-file))
(with-temp-buffer
(insert ";;; -*- lisp-data -*-\n")
@@ -780,7 +897,7 @@ Arguments the same as in `compile'."
(defun project--add-to-project-list-front (pr)
"Add project PR to the front of the project list.
-Save the result to disk if the project list was changed."
+Save the result in `project-list-file' if the list of projects has changed."
(project--ensure-read-project-list)
(let ((dir (project-root pr)))
(unless (equal (caar project--list) dir)
@@ -789,9 +906,10 @@ Save the result to disk if the project list was changed."
(project--write-project-list))))
(defun project--remove-from-project-list (pr-dir)
- "Remove directory PR-DIR from the project list.
+ "Remove directory PR-DIR of a missing project from the project list.
If the directory was in the list before the removal, save the
-result to disk."
+result in `project-list-file'. Announce the project's removal
+from the list."
(project--ensure-read-project-list)
(when (assoc pr-dir project--list)
(setq project--list (assoc-delete-all pr-dir project--list))
@@ -799,9 +917,10 @@ result to disk."
(project--write-project-list)))
(defun project-prompt-project-dir ()
- "Prompt the user for a directory from known project roots.
-The project is chosen among projects known from the project list.
-It's also possible to enter an arbitrary directory."
+ "Prompt the user for a directory that is one of the known project roots.
+The project is chosen among projects known from the project list,
+see `project-list-file'.
+It's also possible to enter an arbitrary directory not in the list."
(project--ensure-read-project-list)
(let* ((dir-choice "... (choose a dir)")
(choices
@@ -820,18 +939,17 @@ It's also possible to enter an arbitrary directory."
;;;###autoload
(defvar project-switch-commands
'((?f "Find file" project-find-file)
- (?r "Find regexp" project-find-regexp)
+ (?g "Find regexp" project-find-regexp)
(?d "Dired" project-dired)
(?v "VC-Dir" project-vc-dir)
- (?s "Shell" project-shell)
(?e "Eshell" project-eshell))
"Alist mapping keys to project switching menu entries.
Used by `project-switch-project' to construct a dispatch menu of
commands available upon \"switching\" to another project.
-Each element looks like (KEY LABEL COMMAND), where COMMAND is the
+Each element is of the form (KEY LABEL COMMAND), where COMMAND is the
command to run when KEY is pressed. LABEL is used to distinguish
-the choice in the dispatch menu.")
+the menu entries in the dispatch menu.")
(defun project--keymap-prompt ()
"Return a prompt for the project swithing dispatch menu."
@@ -845,9 +963,9 @@ the choice in the dispatch menu.")
;;;###autoload
(defun project-switch-project ()
- "\"Switch\" to another project by running a chosen command.
-The available commands are picked from `project-switch-commands'
-and presented in a dispatch menu."
+ "\"Switch\" to another project by running an Emacs command.
+The available commands are presented as a dispatch menu
+made from `project-switch-commands'."
(interactive)
(let ((dir (project-prompt-project-dir))
(choice nil))
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 1ca9f019638..22248f04402 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -633,6 +633,8 @@ builtins.")
(,(lambda (limit)
(let ((re (python-rx (group (+ (any word ?. ?_)))
(? ?\[ (+ (not (any ?\]))) ?\]) (* space)
+ ;; A type, like " : int ".
+ (? ?: (* space) (+ (any word ?. ?_)) (* space))
assignment-operator))
(res nil))
(while (and (setq res (re-search-forward re limit t))
@@ -2090,7 +2092,7 @@ executed through tramp connections."
This variable, when set to a string, makes the environment to be
modified such that shells are started within the specified
virtualenv."
- :type '(choice (const nil) string)
+ :type '(choice (const nil) directory)
:group 'python)
(defcustom python-shell-setup-codes nil
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index 6fd750d3963..c86fc59ac16 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -2683,7 +2683,7 @@ highlighting rules in SQL mode.")
nil 'require-match
init 'sql-product-history init))))
-(defun sql-add-product (product display &optional plist)
+(defun sql-add-product (product display &rest plist)
"Add support for a database product in `sql-mode'.
Add PRODUCT to `sql-product-alist' which enables `sql-mode' to
diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el
index 87f901ae113..6400e1e6cd9 100644
--- a/lisp/progmodes/verilog-mode.el
+++ b/lisp/progmodes/verilog-mode.el
@@ -605,7 +605,7 @@ are lineup only when \\[verilog-pretty-declarations] is typed."
(function :tag "Other"))
:group 'verilog-mode-indent )
(put 'verilog-auto-lineup 'safe-local-variable
- '(lambda (x) (memq x '(nil all assignments declarations))))
+ (lambda (x) (memq x '(nil all assignments declarations))))
(defcustom verilog-indent-level 3
"Indentation of Verilog statements with respect to containing block."
@@ -1118,7 +1118,7 @@ SystemVerilog designs."
:type 'boolean
:group 'verilog-mode-auto)
(put 'verilog-auto-reset-widths 'safe-local-variable
- '(lambda (x) (memq x '(nil t unbased))))
+ (lambda (x) (memq x '(nil t unbased))))
(defcustom verilog-assignment-delay ""
"Text used for delays in delayed assignments. Add a trailing space if set."
@@ -1138,7 +1138,7 @@ line."
(const :tag "Line up Assignment statements" single))
:group 'verilog-mode-auto)
(put 'verilog-auto-arg-format 'safe-local-variable
- '(lambda (x) (memq x '(packed single))))
+ (lambda (x) (memq x '(packed single))))
(defcustom verilog-auto-arg-sort nil
"Non-nil means AUTOARG signal names will be sorted, not in declaration order.
@@ -1263,7 +1263,7 @@ otherwise no vectors if sizes match (like using nil)."
:group 'verilog-mode-auto
:type '(choice (const nil) (const t) (const unsigned)))
(put 'verilog-auto-inst-vector 'safe-local-variable
- '(lambda (x) (memq x '(nil t unsigned))))
+ (lambda (x) (memq x '(nil t unsigned))))
(defcustom verilog-auto-inst-template-numbers nil
"If true, when creating templated ports with AUTOINST, add a comment.
@@ -1280,7 +1280,7 @@ won't merge conflict."
:group 'verilog-mode-auto
:type '(choice (const nil) (const t) (const lhs)))
(put 'verilog-auto-inst-template-numbers 'safe-local-variable
- '(lambda (x) (memq x '(nil t lhs))))
+ (lambda (x) (memq x '(nil t lhs))))
(defcustom verilog-auto-inst-template-required nil
"If non-nil, when creating a port with AUTOINST, require a template.
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 5b5fb4bc47a..3e3a37f6da5 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -264,7 +264,7 @@ be found, return nil.
The default implementation uses `semantic-symref-tool-alist' to
find a search tool; by default, this uses \"find | grep\" in the
`project-current' roots."
- (cl-mapcan
+ (mapcan
(lambda (dir)
(xref-references-in-directory identifier dir))
(let ((pr (project-current t)))
@@ -1383,8 +1383,8 @@ Such as the current syntax table and the applied syntax properties."
(let (xref--last-file-buffer
(tmp-buffer (generate-new-buffer " *xref-temp*")))
(unwind-protect
- (cl-mapcan (lambda (hit) (xref--collect-matches hit regexp tmp-buffer))
- hits)
+ (mapcan (lambda (hit) (xref--collect-matches hit regexp tmp-buffer))
+ hits)
(kill-buffer tmp-buffer))))
(defun xref--collect-matches (hit regexp tmp-buffer)
diff --git a/lisp/simple.el b/lisp/simple.el
index 0fe8a1025ce..a28d10fd4a5 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -212,7 +212,7 @@ all other buffers."
(const :tag "Single next-error capable buffer on selected frame"
next-error-buffer-on-selected-frame)
(const :tag "Current buffer if next-error capable and outside navigation"
- next-error-no-navigation-try-current)
+ next-error-buffer-unnavigated-current)
(function :tag "Other function"))
:group 'next-error
:version "28.1")
@@ -242,10 +242,9 @@ from which next-error navigated, and a target buffer TO-BUFFER."
(if (eq (length window-buffers) 1)
(car window-buffers))))
-(defun next-error-no-navigation-try-current (&optional
- avoid-current
- extra-test-inclusive
- extra-test-exclusive)
+(defun next-error-buffer-unnavigated-current (&optional avoid-current
+ extra-test-inclusive
+ extra-test-exclusive)
"Try the current buffer when outside navigation.
But return nil if we navigated to the current buffer by the means
of `next-error' command. Othewise, return it if it's next-error
@@ -3951,7 +3950,12 @@ is used for ERROR-BUFFER.
Optional seventh arg DISPLAY-ERROR-BUFFER, if non-nil, means to
display the error buffer if there were any errors. When called
-interactively, this is t."
+interactively, this is t.
+
+Non-nil REGION-NONCONTIGUOUS-P means that the region is composed of
+noncontiguous pieces. The most common example of this is a
+rectangular region, where the pieces are separated by newline
+characters."
(interactive (let (string)
(unless (mark)
(user-error "The mark is not set now, so there is no region"))
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index eb3ad72db43..b54258a4e4a 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -44,25 +44,12 @@
:group 'convenience
:version "27.1")
-(defgroup tab-bar-faces nil
+(defgroup tab-bar-faces '((tab-bar custom-face)) ; tab-bar is defined in faces.el
"Faces used in the tab bar."
:group 'tab-bar
:group 'faces
:version "27.1")
-(defface tab-bar
- '((((class color) (min-colors 88))
- :inherit variable-pitch
- :background "grey85"
- :foreground "black")
- (((class mono))
- :background "grey")
- (t
- :inverse-video t))
- "Tab bar face."
- :version "27.1"
- :group 'tab-bar-faces)
-
(defface tab-bar-tab
'((default
:inherit tab-bar)
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index 7a2bdc0b72f..e8c4dc4d93c 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -35,26 +35,12 @@
:group 'convenience
:version "27.1")
-(defgroup tab-line-faces nil
+(defgroup tab-line-faces '((tab-line custom-face)) ; tab-line is defined in faces.el
"Faces used in the tab line."
:group 'tab-line
:group 'faces
:version "27.1")
-(defface tab-line
- '((((class color) (min-colors 88))
- :inherit variable-pitch
- :height 0.9
- :background "grey85"
- :foreground "black")
- (((class mono))
- :background "grey")
- (t
- :inverse-video t))
- "Tab line face."
- :version "27.1"
- :group 'tab-line-faces)
-
(defface tab-line-tab
'((default
:inherit tab-line)
diff --git a/lisp/term/tty-colors.el b/lisp/term/tty-colors.el
index 39ca2d36276..73e2431822e 100644
--- a/lisp/term/tty-colors.el
+++ b/lisp/term/tty-colors.el
@@ -923,62 +923,8 @@ The returned value reflects the standard Emacs definition of
COLOR (see the info node `(emacs) Colors'), regardless of whether
the terminal can display it, so the return value should be the
same regardless of what display is being used."
- (let ((len (length color)))
- (cond ((and (>= len 4) ;; HTML/CSS/SVG-style "#XXYYZZ" color spec
- (eq (aref color 0) ?#)
- (member (aref color 1)
- '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
- ?a ?b ?c ?d ?e ?f
- ?A ?B ?C ?D ?E ?F)))
- ;; Translate the string "#XXYYZZ" into a list of numbers
- ;; (XX YY ZZ), scaling each to the {0..65535} range. This
- ;; follows the HTML color convention, where both "#fff" and
- ;; "#ffffff" represent the same color, white.
- (let* ((ndig (/ (- len 1) 3))
- (maxval (1- (ash 1 (* 4 ndig))))
- (i1 1)
- (i2 (+ i1 ndig))
- (i3 (+ i2 ndig))
- (i4 (+ i3 ndig)))
- (list
- (/ (* (string-to-number
- (substring color i1 i2) 16)
- 65535)
- maxval)
- (/ (* (string-to-number
- (substring color i2 i3) 16)
- 65535)
- maxval)
- (/ (* (string-to-number
- (substring color i3 i4) 16)
- 65535)
- maxval))))
- ((and (>= len 9) ;; X-style rgb:xx/yy/zz color spec
- (string= (substring color 0 4) "rgb:"))
- ;; Translate the string "rgb:XX/YY/ZZ" into a list of
- ;; numbers (XX YY ZZ), scaling each to the {0..65535}
- ;; range. "rgb:F/F/F" is white.
- (let* ((ndig (/ (- len 3) 3))
- (maxval (1- (ash 1 (* 4 (- ndig 1)))))
- (i1 4)
- (i2 (+ i1 ndig))
- (i3 (+ i2 ndig))
- (i4 (+ i3 ndig)))
- (list
- (/ (* (string-to-number
- (substring color i1 (- i2 1)) 16)
- 65535)
- maxval)
- (/ (* (string-to-number
- (substring color i2 (- i3 1)) 16)
- 65535)
- maxval)
- (/ (* (string-to-number
- (substring color i3 (1- i4)) 16)
- 65535)
- maxval))))
- (t
- (cdr (assoc color color-name-rgb-alist))))))
+ (or (internal-color-values-from-color-spec color)
+ (cdr (assoc color color-name-rgb-alist))))
(defun tty-color-translate (color &optional frame)
"Given a color COLOR, return the index of the corresponding TTY color.
diff --git a/lisp/textmodes/bibtex.el b/lisp/textmodes/bibtex.el
index 4712f314080..0018b89d858 100644
--- a/lisp/textmodes/bibtex.el
+++ b/lisp/textmodes/bibtex.el
@@ -850,11 +850,11 @@ Predefined dialects include BibTeX and biblatex."
To interactively change the dialect use the command `bibtex-set-dialect'."
:group 'bibtex
:version "24.1"
- :set '(lambda (symbol value)
- (set-default symbol value)
- ;; `bibtex-set-dialect' is undefined during loading (no problem)
- (if (fboundp 'bibtex-set-dialect)
- (bibtex-set-dialect value)))
+ :set (lambda (symbol value)
+ (set-default symbol value)
+ ;; `bibtex-set-dialect' is undefined during loading (no problem).
+ (if (fboundp 'bibtex-set-dialect)
+ (bibtex-set-dialect value)))
:type '(choice (const BibTeX)
(const biblatex)
(symbol :tag "Custom")))
diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el
index 1b302e34a73..e3d5759579a 100644
--- a/lisp/textmodes/tex-mode.el
+++ b/lisp/textmodes/tex-mode.el
@@ -2295,9 +2295,6 @@ FILE is typically the output DVI or PDF file."
(setq uptodate nil)))))
uptodate)))
-
-(autoload 'format-spec "format-spec")
-
(defvar tex-executable-cache nil)
(defun tex-executable-exists-p (name)
"Like `executable-find' but with a cache."
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index e5d307e7ede..f98730ed221 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -1316,6 +1316,15 @@ stream. Standard error output is discarded."
vc-bzr-revision-keywords))
string pred)))))
+(defun vc-bzr-repository-url (file-or-dir &optional _remote-name)
+ (let ((default-directory (vc-bzr-root file-or-dir)))
+ (with-temp-buffer
+ (vc-bzr-command "info" (current-buffer) 0 nil)
+ (goto-char (point-min))
+ (if (re-search-forward "parent branch: \\(.*\\)$" nil t)
+ (match-string 1)
+ (error "Cannot determine Bzr repository URL")))))
+
(provide 'vc-bzr)
;;; vc-bzr.el ends here
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index dcb52282656..b5cb842aeee 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -101,6 +101,7 @@
;; - rename-file (old new) OK
;; - find-file-hook () OK
;; - conflicted-files OK
+;; - repository-url (file-or-dir) OK
;;; Code:
@@ -734,6 +735,7 @@ or an empty string if none."
(with-current-buffer standard-output
(vc-git--out-ok "symbolic-ref" "HEAD"))))
(stash-list (vc-git-stash-list))
+ (default-directory dir)
branch remote remote-url stash-button stash-string)
(if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
@@ -746,14 +748,8 @@ or an empty string if none."
(concat "branch." branch ".remote")))))
(when (string-match "\\([^\n]+\\)" remote)
(setq remote (match-string 1 remote)))
- (when remote
- (setq remote-url
- (with-output-to-string
- (with-current-buffer standard-output
- (vc-git--out-ok "config"
- (concat "remote." remote ".url"))))))
- (when (string-match "\\([^\n]+\\)" remote-url)
- (setq remote-url (match-string 1 remote-url))))
+ (when (> (length remote) 0)
+ (setq remote-url (vc-git-repository-url dir remote))))
(setq branch "not (detached HEAD)"))
(when stash-list
(let* ((len (length stash-list))
@@ -808,7 +804,7 @@ or an empty string if none."
(propertize "Branch : " 'face 'font-lock-type-face)
(propertize branch
'face 'font-lock-variable-name-face)
- (when remote
+ (when remote-url
(concat
"\n"
(propertize "Remote : " 'face 'font-lock-type-face)
@@ -820,10 +816,10 @@ or an empty string if none."
(when (file-exists-p (expand-file-name ".git/rebase-apply" (vc-git-root dir)))
(propertize "\nRebase : in progress" 'face 'font-lock-warning-face))
(if stash-list
- (concat
- (propertize "\nStash : " 'face 'font-lock-type-face)
- stash-button
- stash-string)
+ (concat
+ (propertize "\nStash : " 'face 'font-lock-type-face)
+ stash-button
+ stash-string)
(concat
(propertize "\nStash : " 'face 'font-lock-type-face)
(propertize "Nothing stashed"
@@ -1082,6 +1078,13 @@ This prompts for a branch to merge from."
"DU" "AA" "UU"))
(push (expand-file-name file directory) files)))))))
+(defun vc-git-repository-url (file-or-dir &optional remote-name)
+ (let ((default-directory (vc-git-root file-or-dir)))
+ (with-temp-buffer
+ (vc-git-command (current-buffer) 0 nil "remote" "get-url"
+ (or remote-name "origin"))
+ (buffer-substring-no-properties (point-min) (1- (point-max))))))
+
;; Everywhere but here, follows vc-git-command, which uses vc-do-command
;; from vc-dispatcher.
(autoload 'vc-resynch-buffer "vc-dispatcher")
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index 40d75738063..95ced7b8d09 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -1525,6 +1525,14 @@ This function differs from vc-do-command in that it invokes
(defun vc-hg-root (file)
(vc-find-root file ".hg"))
+(defun vc-hg-repository-url (file-or-dir &optional remote-name)
+ (let ((default-directory (vc-hg-root file-or-dir)))
+ (with-temp-buffer
+ (vc-hg-command (current-buffer) 0 nil
+ "config"
+ (concat "paths." (or remote-name "default")))
+ (buffer-substring-no-properties (point-min) (1- (point-max))))))
+
(provide 'vc-hg)
;;; vc-hg.el ends here
diff --git a/lisp/vc/vc-svn.el b/lisp/vc/vc-svn.el
index d039bf3c6a3..e108b3a340f 100644
--- a/lisp/vc/vc-svn.el
+++ b/lisp/vc/vc-svn.el
@@ -816,7 +816,14 @@ Set file properties accordingly. If FILENAME is non-nil, return its status."
(push (match-string 1 loglines) vc-svn-revisions)
(setq start (+ start (match-end 0)))
(setq loglines (buffer-substring-no-properties start (point-max)))))
- vc-svn-revisions)))
+ vc-svn-revisions)))
+
+(defun vc-svn-repository-url (file-or-dir &optional _remote-name)
+ (let ((default-directory (vc-svn-root file-or-dir)))
+ (with-temp-buffer
+ (vc-svn-command (current-buffer) 0 nil
+ "info" "--show-item" "repos-root-url")
+ (buffer-substring-no-properties (point-min) (1- (point-max))))))
(provide 'vc-svn)
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index c640ba0420e..9b12d449785 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -553,6 +553,13 @@
;; Return the list of files where conflict resolution is needed in
;; the project that contains DIR.
;; FIXME: what should it do with non-text conflicts?
+;;
+;; - repository-url (file-or-dir &optional remote-name)
+;;
+;; Returns the URL of the repository of the current checkout
+;; containing FILE-OR-DIR. The optional REMOTE-NAME specifies the
+;; remote (in Git parlance) whose URL is to be returned. It has
+;; only a meaning for distributed VCS and is ignored otherwise.
;;; Changes from the pre-25.1 API:
;;
@@ -957,7 +964,7 @@ use."
(throw 'found bk))))
;;;###autoload
-(defun vc-responsible-backend (file)
+(defun vc-responsible-backend (file &optional no-error)
"Return the name of a backend system that is responsible for FILE.
If FILE is already registered, return the
@@ -967,7 +974,10 @@ responsible for FILE is returned.
Note that if FILE is a symbolic link, it will not be resolved --
the responsible backend system for the symbolic link itself will
-be reported."
+be reported.
+
+If NO-ERROR is nil, signal an error that no VC backend is
+responsible for the given file."
(or (and (not (file-directory-p file)) (vc-backend file))
(catch 'found
;; First try: find a responsible backend. If this is for registration,
@@ -975,7 +985,8 @@ be reported."
(dolist (backend vc-handled-backends)
(and (vc-call-backend backend 'responsible-p file)
(throw 'found backend))))
- (error "No VC backend is responsible for %s" file)))
+ (unless no-error
+ (error "No VC backend is responsible for %s" file))))
(defun vc-expand-dirs (file-or-dir-list backend)
"Expands directories in a file list specification.