GNU bug report logs - #28969
27.0.50; dired: Confirmation prompt for wildcard not surrounded by whitespace

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: emacs; Reported by: Michael Heerdegen <michael_heerdegen@HIDDEN>; Keywords: patch; merged with #35564; dated Tue, 24 Oct 2017 16:42:02 UTC; Maintainer for emacs is bug-gnu-emacs@HIDDEN.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 10 Oct 2019 18:45:31 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Thu Oct 10 14:45:31 2019
Received: from localhost ([127.0.0.1]:57139 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1iIdRI-0008NG-Gu
	for submit <at> debbugs.gnu.org; Thu, 10 Oct 2019 14:45:30 -0400
Received: from mail-wr1-f51.google.com ([209.85.221.51]:38828)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <kevin.legouguec@HIDDEN>)
 id 1iIdRE-0008My-Kn; Thu, 10 Oct 2019 14:45:26 -0400
Received: by mail-wr1-f51.google.com with SMTP id w12so9127645wro.5;
 Thu, 10 Oct 2019 11:45:24 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
 h=from:to:cc:subject:references:date:in-reply-to:message-id
 :user-agent:mime-version;
 bh=LVRBztbo5TPq2dk6MGtV1MQvuQ0wgV7rFB1h47DB6aQ=;
 b=XZPvwe44Mb2yJzd7g5ofYcHfJWMIYo1Pg79F0vp4IbiM8adxzxxyZ02cp4jKJdMVZE
 f9/xrKXYmLTHM8tIQFnTMl+hLN0cQpFFYby5+NsGyzUmlFL2YcVb3DPcAWKfuKJO5HXt
 zL/Wf294IaeSLBa489l1/svtPp2+f9MJDAS3MJfHgaB0EnPlas0lpgtW6YF6CbhDTNMr
 eoEiQDa60qSkBFj+Y6I5HPJ/EhwKJjVq9XyIHta74RtS5HU3gImJ5o00wQzxVe5m3EFV
 a2pNMo8wJDByDG0UlS7xzqAKXVqL2Cg0fspS369hWQuMOvbJHxfnnHMgzQqPC0Cml3vA
 8m9Q==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to
 :message-id:user-agent:mime-version;
 bh=LVRBztbo5TPq2dk6MGtV1MQvuQ0wgV7rFB1h47DB6aQ=;
 b=iCs1wBN/+qIdyWL6WbabTas5fw9Ub7/SAx8aUm1tEBku04u835FlVcDrwroW4AWHK+
 Qedg4tbE4qQ01gTauICJ8a9NFFwKyPLd5jm0U0b+OBfH/eA03kn3B0tvt0QKjB47mZrr
 B8ZuEWTJf8tuHehO+7Z+KReIiQfrNCcm3bcYsWBj86hYez0gAyxAdO1hJm8iwOhco5mv
 o4sNFvCiLufBM95Ntd23VDVxPM+jPknGgSOfrmwktBzuZIlot9yBFUlINsjnAyafPPMT
 +GUFMv8XLHnYvUMMTyRxBUaKVFgb0n/5tp0UIYBSDy/Of4fTiSFaQYhdKJXGMO8tax6q
 K/4Q==
X-Gm-Message-State: APjAAAV6sxpkvOu7nFCGHRvLK/TPiAAwimXsYk9d3ztFBpsyWQ9ZXE06
 sIIlLEnAUjia5MWu5uyeRFs=
X-Google-Smtp-Source: APXvYqw+rBf/HmYmTA7nG0Dgn7aEcJOgZUHttQRc09fB9HAnYp2wQPlLKBRNUJ8s8MEYJ9+a1FYB/Q==
X-Received: by 2002:adf:f18a:: with SMTP id h10mr9503634wro.145.1570733118759; 
 Thu, 10 Oct 2019 11:45:18 -0700 (PDT)
Received: from my-little-tumbleweed (71.142.13.109.rev.sfr.net.
 [109.13.142.71])
 by smtp.gmail.com with ESMTPSA id m18sm9066657wrg.97.2019.10.10.11.45.16
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Thu, 10 Oct 2019 11:45:17 -0700 (PDT)
From: =?utf-8?Q?K=C3=A9vin_Le_Gouguec?= <kevin.legouguec@HIDDEN>
To: 35564 <at> debbugs.gnu.org
Subject: bug#35564: [PATCH v5] Tweak dired warning about "wildcard" characters
References: <87zho2cd4f.fsf@HIDDEN> <87wohvf22u.fsf@HIDDEN>
 <87h88cvpkj.fsf_-_@HIDDEN> <87imsinbmr.fsf_-_@HIDDEN>
Date: Thu, 10 Oct 2019 20:45:14 +0200
In-Reply-To: <87imsinbmr.fsf_-_@HIDDEN> (=?utf-8?Q?=22K=C3=A9vin?= Le
 Gouguec"'s message of "Wed, 03 Jul 2019 21:47:40 +0200")
Message-ID: <87o8yoign9.fsf_-_@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: Michael Heerdegen <michael_heerdegen@HIDDEN>,
 Noam Postavsky <npostavs@HIDDEN>, Juri Linkov <juri@HIDDEN>,
 Stefan Monnier <monnier@HIDDEN>, Eli Zaretskii <eliz@HIDDEN>,
 28969 <at> debbugs.gnu.org, Drew Adams <drew.adams@HIDDEN>
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

--=-=-=
Content-Type: text/plain

Finally got around to try out rmc.el.

A brief recap of the issue: dired-do-shell-command looks out for any
non-isolated metacharacters[1], and prompts the user when it finds some.
The problem is that the prompt is downright misleading under some
circumstances.  E.g. after marking some files in a Dired buffer:

    ! sed 's/?/!/g' RET
    => Confirm--do you mean to use `?' as a wildcard?

The answer a user must input to proceed is "yes", despite '?' not being
a wildcard in this situation; the answer some users may give intuitively
is "no" (or, in my case, "whaaa?").


This patch series initially tried to shove the command in the prompt,
highlight the non-isolated characters, and re-phrase the prompt to be
more accurate (i.e. not talk about wildcards).

It went through a several iterations for a few reasons[2]; most recently
Michael suggested using read-multiple-choice [bug#35564#136]; I looked
at how nsm.el uses it, saw that is was good, and got distracted for two
months.

Here is the new series:


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0001-Tweak-dired-warning-about-wildcard-characters.patch

From 0c0b1570623a69141ebd31b8e3dffdeef5273c7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Fri, 7 Jun 2019 17:19:44 +0200
Subject: [PATCH 1/5] Tweak dired warning about "wildcard" characters

Non-isolated '?' and '*' characters may be quoted, or
backslash-escaped; we do not know for a fact that the shell will
interpret them as wildcards.

Rephrase the prompt and highlight the characters so that the user sees
exactly what we are talking about.

* lisp/dired-aux.el (dired--isolated-char-p)
(dired--highlight-nosubst-char, dired--no-subst-prompt): New
functions.
(dired-do-shell-command): Use them.

* test/lisp/dired-aux-tests.el (dired-test-isolated-char-p)
(dired-test-highlight-metachar): Test the new functions.

(Bug#35564)
---
 lisp/dired-aux.el            | 42 ++++++++++++++++++++++++++++++++----
 test/lisp/dired-aux-tests.el | 28 ++++++++++++++++++++++++
 2 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index bfc37c5cde..409f028e2b 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -79,6 +79,42 @@ dired--star-or-qmark-p
                (funcall (if keep #'string-match-p #'string-match) x string))
              regexps)))
 
+(defun dired--isolated-char-p (command pos)
+  "Assert whether the character at POS is isolated within COMMAND.
+A character is isolated if:
+- it is surrounded by whitespace, the start of the command, or
+  the end of the command,
+- it is surrounded by `\\=`' characters."
+  (let ((start (max 0 (1- pos)))
+        (char (string (aref command pos))))
+    (and (string-match
+          (rx (or (seq (or bos blank)
+                       (group-n 1 (literal char))
+                       (or eos blank))
+                  (seq ?` (group-n 1 (literal char)) ?`)))
+          command start)
+         (= pos (match-beginning 1)))))
+
+(defun dired--highlight-nosubst-char (command char)
+  "Highlight occurences of CHAR that are not isolated in COMMAND.
+These occurences will not be substituted; they will be sent as-is
+to the shell, which may interpret them as wildcards."
+  (save-match-data
+    (let ((highlighted (substring-no-properties command))
+          (pos 0))
+      (while (string-match (regexp-quote char) command pos)
+        (let ((start (match-beginning 0))
+              (end (match-end 0)))
+          (unless (dired--isolated-char-p command start)
+            (add-face-text-property start end 'warning nil highlighted))
+          (setq pos end)))
+      highlighted)))
+
+(defun dired--no-subst-prompt (command char)
+  (let ((highlighted-command (dired--highlight-nosubst-char command char))
+        (prompt "Confirm--the highlighted characters will not be substituted:"))
+    (format-message "%s\n%s\nProceed?" prompt highlighted-command)))
+
 ;;;###autoload
 (defun dired-diff (file &optional switches)
   "Compare file at point with FILE using `diff'.
@@ -761,11 +797,9 @@ dired-do-shell-command
          (ok (cond ((not (or on-each no-subst))
 	            (error "You can not combine `*' and `?' substitution marks"))
 	           ((need-confirm-p command "*")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `*' as a wildcard? ")))
+	            (y-or-n-p (dired--no-subst-prompt command "*")))
 	           ((need-confirm-p command "?")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `?' as a wildcard? ")))
+	            (y-or-n-p (dired--no-subst-prompt command "?")))
 	           (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index ccd3192792..80b6393931 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -114,6 +114,34 @@ dired-test-bug30624
         (mapc #'delete-file `(,file1 ,file2))
         (kill-buffer buf)))))
 
+(ert-deftest dired-test-isolated-char-p ()
+  (should (dired--isolated-char-p "?" 0))
+  (should (dired--isolated-char-p "? " 0))
+  (should (dired--isolated-char-p " ?" 1))
+  (should (dired--isolated-char-p " ? " 1))
+  (should (dired--isolated-char-p "foo bar ? baz" 8))
+  (should (dired--isolated-char-p "foo -i`?`" 7))
+  (should-not (dired--isolated-char-p "foo `bar`?" 9))
+  (should-not (dired--isolated-char-p "foo 'bar?'" 8))
+  (should-not (dired--isolated-char-p "foo bar?baz" 7))
+  (should-not (dired--isolated-char-p "foo bar?" 7)))
+
+(ert-deftest dired-test-highlight-metachar ()
+  "Check that non-isolated meta-characters are highlighted"
+  (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
+         (result (dired--highlight-nosubst-char command "?")))
+    (should-not (text-property-not-all 1 14 'face nil result))
+    (should (equal 'warning (get-text-property 15 'face result)))
+    (should-not (text-property-not-all 16 28 'face nil result))
+    (should (equal 'warning (get-text-property 29 'face result)))
+    (should-not (text-property-not-all 30 39 'face nil result)))
+  (let* ((command "sed -e 's/o*/a/' -e 's/o*/a/'")
+         (result (dired--highlight-nosubst-char command "*")))
+    (should-not (text-property-not-all 1 10 'face nil result))
+    (should (equal 'warning (get-text-property 11 'face result)))
+    (should-not (text-property-not-all 12 23 'face nil result))
+    (should (equal 'warning (get-text-property 24 'face result)))
+    (should-not (text-property-not-all 25 29 'face nil result))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0002-Dedup-dired-aux-isolated-char-searching-Bug-35564.patch

From b80d55bf5307cf95ae0804cc1dfe66b40b012ba6 Mon Sep 17 00:00:00 2001
From: Noam Postavsky <npostavs@HIDDEN>
Date: Thu, 27 Jun 2019 19:15:56 -0400
Subject: [PATCH 2/5] Dedup dired-aux isolated char searching (Bug#35564)

* lisp/dired-aux.el (dired-isolated-string-re): Use explicitly
numbered groups.
(dired--star-or-qmark-p): Add START parameter.  Make sure to return
the first isolated match.
(dired--no-subst-prompt): Operate on a list of positions rather than
searching again for isolated chars.  Shorten prompt, and include the
character being asked about in the question (to make it clearer, and
in case the user can't see the fontification for whatever reason,
e.g., screen reader).
(dired--isolated-char-p): Remove.
(dired--need-confirm-positions): New function.
(dired-do-shell-command): Use it.
* test/lisp/dired-aux-tests.el (dired-test-isolated-char-p): Remove.
(dired-test-highlight-metachar): Adjust to new functions.  Make sure
that `*` isn't considered isolated.
---
 lisp/dired-aux.el            | 113 ++++++++++++++++-------------------
 test/lisp/dired-aux-tests.el |  31 +++++-----
 2 files changed, 67 insertions(+), 77 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 409f028e2b..c13cbcf2e3 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -60,60 +60,60 @@ dired-isolated-string-re
 of a string followed/prefixed with an space.
 The regexp capture the preceding blank, STRING and the following blank as
 the groups 1, 2 and 3 respectively."
-  (format "\\(\\`\\|[ \t]\\)\\(%s\\)\\([ \t]\\|\\'\\)" string))
+  (format "\\(?1:\\`\\|[ \t]\\)\\(?2:%s\\)\\(?3:[ \t]\\|\\'\\)" string))
 
-(defun dired--star-or-qmark-p (string match &optional keep)
+(defun dired--star-or-qmark-p (string match &optional keep start)
   "Return non-nil if STRING contains isolated MATCH or `\\=`?\\=`'.
 MATCH should be the strings \"?\", `\\=`?\\=`', \"*\" or nil.  The latter
 means STRING contains either \"?\" or `\\=`?\\=`' or \"*\".
 If optional arg KEEP is non-nil, then preserve the match data.  Otherwise,
 this function changes it and saves MATCH as the second match group.
+START is the position to start matching from.
 
 Isolated means that MATCH is surrounded by spaces or at the beginning/end
 of STRING followed/prefixed with an space.  A match to `\\=`?\\=`',
 isolated or not, is also valid."
-  (let ((regexps (list (dired-isolated-string-re (if match (regexp-quote match) "[*?]")))))
+  (let ((regexp (dired-isolated-string-re (if match (regexp-quote match) "[*?]"))))
     (when (or (null match) (equal match "?"))
-      (setq regexps (append (list "\\(\\)\\(`\\?`\\)\\(\\)") regexps)))
-    (cl-some (lambda (x)
-               (funcall (if keep #'string-match-p #'string-match) x string))
-             regexps)))
-
-(defun dired--isolated-char-p (command pos)
-  "Assert whether the character at POS is isolated within COMMAND.
-A character is isolated if:
-- it is surrounded by whitespace, the start of the command, or
-  the end of the command,
-- it is surrounded by `\\=`' characters."
-  (let ((start (max 0 (1- pos)))
-        (char (string (aref command pos))))
-    (and (string-match
-          (rx (or (seq (or bos blank)
-                       (group-n 1 (literal char))
-                       (or eos blank))
-                  (seq ?` (group-n 1 (literal char)) ?`)))
-          command start)
-         (= pos (match-beginning 1)))))
-
-(defun dired--highlight-nosubst-char (command char)
-  "Highlight occurences of CHAR that are not isolated in COMMAND.
-These occurences will not be substituted; they will be sent as-is
-to the shell, which may interpret them as wildcards."
-  (save-match-data
-    (let ((highlighted (substring-no-properties command))
-          (pos 0))
-      (while (string-match (regexp-quote char) command pos)
-        (let ((start (match-beginning 0))
-              (end (match-end 0)))
-          (unless (dired--isolated-char-p command start)
-            (add-face-text-property start end 'warning nil highlighted))
-          (setq pos end)))
-      highlighted)))
-
-(defun dired--no-subst-prompt (command char)
-  (let ((highlighted-command (dired--highlight-nosubst-char command char))
-        (prompt "Confirm--the highlighted characters will not be substituted:"))
-    (format-message "%s\n%s\nProceed?" prompt highlighted-command)))
+      (cl-callf concat regexp "\\|\\(?1:\\)\\(?2:`\\?`\\)\\(?3:\\)"))
+    (funcall (if keep #'string-match-p #'string-match) regexp string start)))
+
+(defun dired--need-confirm-positions (command string)
+  "Search for non-isolated matches of STRING in COMMAND.
+Return a list of positions that match STRING, but would not be
+considered \"isolated\" by `dired--star-or-qmark-p'."
+  (cl-assert (= (length string) 1))
+  (let ((start 0)
+        (isolated-char-positions nil)
+        (confirm-positions nil)
+        (regexp (regexp-quote string)))
+    ;; Collect all ? and * surrounded by spaces and `?`.
+    (while (dired--star-or-qmark-p command string nil start)
+      (push (cons (match-beginning 2) (match-end 2))
+            isolated-char-positions)
+      (setq start (match-end 2)))
+    ;; Now collect any remaining ? and *.
+    (setq start 0)
+    (while (string-match regexp command start)
+      (unless (cl-member (match-beginning 0) isolated-char-positions
+                         :test (lambda (pos match)
+                                 (<= (car match) pos (cdr match))))
+        (push (match-beginning 0) confirm-positions))
+      (setq start (match-end 0)))
+    confirm-positions))
+
+(defun dired--no-subst-prompt (char-positions command)
+  (cl-callf substring-no-properties command)
+  (dolist (pos char-positions)
+    (add-face-text-property pos (1+ pos) 'warning nil command))
+  (concat command "\n"
+          (format-message
+           (ngettext "Send %d occurrence of `%s' as-is to shell?"
+                     "Send %d occurrences of `%s' as-is to shell?"
+                     (length char-positions))
+           (length char-positions)
+           (propertize (string (aref command (car char-positions)))
+                       'face 'warning))))
 
 ;;;###autoload
 (defun dired-diff (file &optional switches)
@@ -781,26 +781,19 @@ dired-do-shell-command
       (dired-read-shell-command "! on %s: " current-prefix-arg files)
       current-prefix-arg
       files)))
-  (cl-flet ((need-confirm-p
-             (cmd str)
-             (let ((res cmd)
-                   (regexp (regexp-quote str)))
-               ;; Drop all ? and * surrounded by spaces and `?`.
-               (while (and (string-match regexp res)
-                           (dired--star-or-qmark-p res str))
-                 (setq res (replace-match "" t t res 2)))
-               (string-match regexp res))))
   (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep)))
 	 (no-subst (not (dired--star-or-qmark-p command "?" 'keep)))
+         (confirmations nil)
          ;; Get confirmation for wildcards that may have been meant
          ;; to control substitution of a file name or the file name list.
-         (ok (cond ((not (or on-each no-subst))
-	            (error "You can not combine `*' and `?' substitution marks"))
-	           ((need-confirm-p command "*")
-	            (y-or-n-p (dired--no-subst-prompt command "*")))
-	           ((need-confirm-p command "?")
-	            (y-or-n-p (dired--no-subst-prompt command "?")))
-	           (t))))
+         (ok (cond
+              ((not (or on-each no-subst))
+               (error "You can not combine `*' and `?' substitution marks"))
+              ((setq confirmations (dired--need-confirm-positions command "*"))
+               (y-or-n-p (dired--no-subst-prompt confirmations command)))
+              ((setq confirmations (dired--need-confirm-positions command "?"))
+               (y-or-n-p (dired--no-subst-prompt confirmations command)))
+              (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
            (if on-each
@@ -811,7 +804,7 @@ dired-do-shell-command
 	                          nil file-list)
 	     ;; execute the shell command
 	     (dired-run-shell-command
-	      (dired-shell-stuff-it command file-list nil arg))))))))
+              (dired-shell-stuff-it command file-list nil arg)))))))
 
 ;; Might use {,} for bash or csh:
 (defvar dired-mark-prefix ""
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index 80b6393931..ff18edddb6 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -114,34 +114,31 @@ dired-test-bug30624
         (mapc #'delete-file `(,file1 ,file2))
         (kill-buffer buf)))))
 
-(ert-deftest dired-test-isolated-char-p ()
-  (should (dired--isolated-char-p "?" 0))
-  (should (dired--isolated-char-p "? " 0))
-  (should (dired--isolated-char-p " ?" 1))
-  (should (dired--isolated-char-p " ? " 1))
-  (should (dired--isolated-char-p "foo bar ? baz" 8))
-  (should (dired--isolated-char-p "foo -i`?`" 7))
-  (should-not (dired--isolated-char-p "foo `bar`?" 9))
-  (should-not (dired--isolated-char-p "foo 'bar?'" 8))
-  (should-not (dired--isolated-char-p "foo bar?baz" 7))
-  (should-not (dired--isolated-char-p "foo bar?" 7)))
-
 (ert-deftest dired-test-highlight-metachar ()
   "Check that non-isolated meta-characters are highlighted"
   (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
-         (result (dired--highlight-nosubst-char command "?")))
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "?")
+                  command))
+         (result (and (string-match (regexp-quote command) prompt)
+                      (match-string 0 prompt))))
     (should-not (text-property-not-all 1 14 'face nil result))
     (should (equal 'warning (get-text-property 15 'face result)))
     (should-not (text-property-not-all 16 28 'face nil result))
     (should (equal 'warning (get-text-property 29 'face result)))
     (should-not (text-property-not-all 30 39 'face nil result)))
-  (let* ((command "sed -e 's/o*/a/' -e 's/o*/a/'")
-         (result (dired--highlight-nosubst-char command "*")))
+  ;; Note that `?` is considered isolated, but `*` is not.
+  (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "*")
+                  command))
+         (result (and (string-match (regexp-quote command) prompt)
+                      (match-string 0 prompt))))
     (should-not (text-property-not-all 1 10 'face nil result))
     (should (equal 'warning (get-text-property 11 'face result)))
     (should-not (text-property-not-all 12 23 'face nil result))
-    (should (equal 'warning (get-text-property 24 'face result)))
-    (should-not (text-property-not-all 25 29 'face nil result))))
+    (should (equal 'warning (get-text-property 25 'face result)))
+    (should-not (text-property-not-all 26 32 'face nil result))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0003-Add-markers-below-non-isolated-chars-in-dired-prompt.patch

From cd41c96d0631275d1fc24367663cf891a17cad47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Wed, 3 Jul 2019 21:17:57 +0200
Subject: [PATCH 3/5] Add '^' markers below non-isolated chars in dired prompt

* lisp/dired-aux.el (dired--mark-positions): New function.
(dired--no-subst-prompt): Use it to show chars without overly
relying on highlighting.
(dired-do-shell-command): When the echo area is wide enough to
display the command without wrapping it, add the markers.

* test/lisp/dired-aux-tests.el (dired-test-highlight-metachar):
Add assertion for '^' marker positions.

(Bug#35564)
---
 lisp/dired-aux.el            | 43 +++++++++++++++++++++--------
 test/lisp/dired-aux-tests.el | 53 ++++++++++++++++++++++++------------
 2 files changed, 68 insertions(+), 28 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index c13cbcf2e3..01c1b92595 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -102,18 +102,35 @@ dired--need-confirm-positions
       (setq start (match-end 0)))
     confirm-positions))
 
-(defun dired--no-subst-prompt (char-positions command)
+(defun dired--mark-positions (positions)
+  (let ((markers (make-string
+                  (1+ (apply #'max positions))
+                  ?\s)))
+    (dolist (pos positions)
+      (setf (aref markers pos) ?^))
+    markers))
+
+(defun dired--no-subst-prompt (char-positions command add-markers)
   (cl-callf substring-no-properties command)
   (dolist (pos char-positions)
     (add-face-text-property pos (1+ pos) 'warning nil command))
-  (concat command "\n"
-          (format-message
-           (ngettext "Send %d occurrence of `%s' as-is to shell?"
-                     "Send %d occurrences of `%s' as-is to shell?"
-                     (length char-positions))
-           (length char-positions)
-           (propertize (string (aref command (car char-positions)))
-                       'face 'warning))))
+  ;; `y-or-n-p' adds some text to the beginning of the prompt when the
+  ;; user fails to answer 'y' or 'n'.  The highlighted command thus
+  ;; cannot be put on the first line of the prompt, since the added
+  ;; text will shove the command to the right, and the '^' markers
+  ;; will become misaligned.
+  (apply #'concat
+         `("Confirm:\n"
+           ,command "\n"
+           ,@(when add-markers
+               (list (dired--mark-positions char-positions) "\n"))
+           ,(format-message
+             (ngettext "Send %d occurrence of `%s' as-is to shell?"
+                       "Send %d occurrences of `%s' as-is to shell?"
+                       (length char-positions))
+             (length char-positions)
+             (propertize (string (aref command (car char-positions)))
+                         'face 'warning)))))
 
 ;;;###autoload
 (defun dired-diff (file &optional switches)
@@ -784,15 +801,19 @@ dired-do-shell-command
   (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep)))
 	 (no-subst (not (dired--star-or-qmark-p command "?" 'keep)))
          (confirmations nil)
+         (short-enough (< (length command)
+                          (window-width (minibuffer-window))))
          ;; Get confirmation for wildcards that may have been meant
          ;; to control substitution of a file name or the file name list.
          (ok (cond
               ((not (or on-each no-subst))
                (error "You can not combine `*' and `?' substitution marks"))
               ((setq confirmations (dired--need-confirm-positions command "*"))
-               (y-or-n-p (dired--no-subst-prompt confirmations command)))
+               (y-or-n-p (dired--no-subst-prompt confirmations command
+                                                 short-enough)))
               ((setq confirmations (dired--need-confirm-positions command "?"))
-               (y-or-n-p (dired--no-subst-prompt confirmations command)))
+               (y-or-n-p (dired--no-subst-prompt confirmations command
+                                                 short-enough)))
               (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index ff18edddb6..174c27052e 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -115,30 +115,49 @@ dired-test-bug30624
         (kill-buffer buf)))))
 
 (ert-deftest dired-test-highlight-metachar ()
-  "Check that non-isolated meta-characters are highlighted"
+  "Check that non-isolated meta-characters are highlighted."
   (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
+         (markers "               ^             ^")
          (prompt (dired--no-subst-prompt
                   (dired--need-confirm-positions command "?")
-                  command))
-         (result (and (string-match (regexp-quote command) prompt)
-                      (match-string 0 prompt))))
-    (should-not (text-property-not-all 1 14 'face nil result))
-    (should (equal 'warning (get-text-property 15 'face result)))
-    (should-not (text-property-not-all 16 28 'face nil result))
-    (should (equal 'warning (get-text-property 29 'face result)))
-    (should-not (text-property-not-all 30 39 'face nil result)))
+                  command
+                  t))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 4))
+    (should (string-match (regexp-quote command) highlit-command))
+    (should (string-match (regexp-quote markers) (nth 2 lines)))
+    (should-not (text-property-not-all 1 14 'face nil highlit-command))
+    (should (equal 'warning (get-text-property 15 'face highlit-command)))
+    (should-not (text-property-not-all 16 28 'face nil highlit-command))
+    (should (equal 'warning (get-text-property 29 'face highlit-command)))
+    (should-not (text-property-not-all 30 39 'face nil highlit-command)))
   ;; Note that `?` is considered isolated, but `*` is not.
   (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
+         (markers "           ^             ^")
          (prompt (dired--no-subst-prompt
                   (dired--need-confirm-positions command "*")
-                  command))
-         (result (and (string-match (regexp-quote command) prompt)
-                      (match-string 0 prompt))))
-    (should-not (text-property-not-all 1 10 'face nil result))
-    (should (equal 'warning (get-text-property 11 'face result)))
-    (should-not (text-property-not-all 12 23 'face nil result))
-    (should (equal 'warning (get-text-property 25 'face result)))
-    (should-not (text-property-not-all 26 32 'face nil result))))
+                  command
+                  t))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 4))
+    (should (string-match (regexp-quote command) highlit-command))
+    (should (string-match (regexp-quote markers) (nth 2 lines)))
+    (should-not (text-property-not-all 1 10 'face nil highlit-command))
+    (should (equal 'warning (get-text-property 11 'face highlit-command)))
+    (should-not (text-property-not-all 12 23 'face nil highlit-command))
+    (should (equal 'warning (get-text-property 25 'face highlit-command)))
+    (should-not (text-property-not-all 26 32 'face nil highlit-command)))
+  (let* ((command "sed 's/\\?/!/'")
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "?")
+                  command
+                  nil))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 3))
+    (should (string-match (regexp-quote command) highlit-command))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0004-Simplify-highlighting-assertions.patch

From 7a884e189fa18cd903c6c684090860cf8ebb7f7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Fri, 12 Jul 2019 16:10:54 +0200
Subject: [PATCH 4/5] Simplify highlighting assertions

* test/lisp/dired-aux-tests.el (dired-test--check-highlighting):
New function.
(dired-test-highlight-metachar): Use it.

(Bug#35564)
---
 test/lisp/dired-aux-tests.el | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index 174c27052e..ba10c54332 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -114,6 +114,15 @@ dired-test-bug30624
         (mapc #'delete-file `(,file1 ,file2))
         (kill-buffer buf)))))
 
+(defun dired-test--check-highlighting (command positions)
+  (let ((start 1))
+    (dolist (pos positions)
+      (should-not (text-property-not-all start (1- pos) 'face nil command))
+      (should (equal 'warning (get-text-property pos 'face command)))
+      (setq start (1+ pos)))
+    (should-not (text-property-not-all
+                 start (length command) 'face nil command))))
+
 (ert-deftest dired-test-highlight-metachar ()
   "Check that non-isolated meta-characters are highlighted."
   (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
@@ -127,11 +136,7 @@ dired-test-highlight-metachar
     (should (= (length lines) 4))
     (should (string-match (regexp-quote command) highlit-command))
     (should (string-match (regexp-quote markers) (nth 2 lines)))
-    (should-not (text-property-not-all 1 14 'face nil highlit-command))
-    (should (equal 'warning (get-text-property 15 'face highlit-command)))
-    (should-not (text-property-not-all 16 28 'face nil highlit-command))
-    (should (equal 'warning (get-text-property 29 'face highlit-command)))
-    (should-not (text-property-not-all 30 39 'face nil highlit-command)))
+    (dired-test--check-highlighting highlit-command '(15 29)))
   ;; Note that `?` is considered isolated, but `*` is not.
   (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
          (markers "           ^             ^")
@@ -144,11 +149,7 @@ dired-test-highlight-metachar
     (should (= (length lines) 4))
     (should (string-match (regexp-quote command) highlit-command))
     (should (string-match (regexp-quote markers) (nth 2 lines)))
-    (should-not (text-property-not-all 1 10 'face nil highlit-command))
-    (should (equal 'warning (get-text-property 11 'face highlit-command)))
-    (should-not (text-property-not-all 12 23 'face nil highlit-command))
-    (should (equal 'warning (get-text-property 25 'face highlit-command)))
-    (should-not (text-property-not-all 26 32 'face nil highlit-command)))
+    (dired-test--check-highlighting highlit-command '(11 25)))
   (let* ((command "sed 's/\\?/!/'")
          (prompt (dired--no-subst-prompt
                   (dired--need-confirm-positions command "?")
@@ -157,7 +158,8 @@ dired-test-highlight-metachar
          (lines (split-string prompt "\n"))
          (highlit-command (nth 1 lines)))
     (should (= (length lines) 3))
-    (should (string-match (regexp-quote command) highlit-command))))
+    (should (string-match (regexp-quote command) highlit-command))
+    (dired-test--check-highlighting highlit-command '(8))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0005-Hide-detailed-explanations-in-a-togglable-help-buffe.patch

From 9fa3a93492c6c4d6553cff163d0203253bdb2eb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Wed, 2 Oct 2019 22:04:01 +0200
Subject: [PATCH 5/5] Hide detailed explanations in a togglable help buffer

* test/lisp/dired-aux-tests.el (dired-test-bug27496):
(dired-test-highlight-metachar): Adapt to new prompt.

* lisp/dired-aux.el (dired--no-subst-prompt): Split into...
(dired--highlight-no-subst-chars): add warning face and possibly
'^' markers to command,
(dired--no-subst-explain): fill in help buffer with detailed
explanations,
(dired--no-subst-ask): setup read-multiple-choice,
(dired--no-subst-confirm): loop until we know what to do.
(dired-do-shell-command): Call new function
'dired--no-subst-confirm.'

(bug#28969, bug#35564)
---
 lisp/dired-aux.el            | 102 ++++++++++++++++++++++++++---------
 test/lisp/dired-aux-tests.el |  39 +++++++-------
 2 files changed, 95 insertions(+), 46 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 01c1b92595..6b33f4ebfb 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -110,27 +110,83 @@ dired--mark-positions
       (setf (aref markers pos) ?^))
     markers))
 
-(defun dired--no-subst-prompt (char-positions command add-markers)
+(defun dired--highlight-no-subst-chars (positions command mark)
   (cl-callf substring-no-properties command)
-  (dolist (pos char-positions)
+  (dolist (pos positions)
     (add-face-text-property pos (1+ pos) 'warning nil command))
-  ;; `y-or-n-p' adds some text to the beginning of the prompt when the
-  ;; user fails to answer 'y' or 'n'.  The highlighted command thus
-  ;; cannot be put on the first line of the prompt, since the added
-  ;; text will shove the command to the right, and the '^' markers
-  ;; will become misaligned.
-  (apply #'concat
-         `("Confirm:\n"
-           ,command "\n"
-           ,@(when add-markers
-               (list (dired--mark-positions char-positions) "\n"))
-           ,(format-message
-             (ngettext "Send %d occurrence of `%s' as-is to shell?"
-                       "Send %d occurrences of `%s' as-is to shell?"
-                       (length char-positions))
-             (length char-positions)
-             (propertize (string (aref command (car char-positions)))
-                         'face 'warning)))))
+  (if mark
+      (concat command "\n" (dired--mark-positions positions))
+    command))
+
+(defun dired--no-subst-explain (buf char-positions command mark-positions)
+  (with-current-buffer buf
+    (erase-buffer)
+    (insert
+     (format-message "\
+If your command contains occurrences of `*' surrounded by
+whitespace, `dired-do-shell-command' substitutes them for the
+entire file list to process.  Otherwise, if your command contains
+occurrences of `?' surrounded by whitespace or `%s', Dired will
+run the command once for each file, substituting `?' for each
+file name.
+
+Your command contains occurrences of `%s' that will not be
+substituted, and will be passed through normally to the shell.
+
+%s
+"
+   "`"
+   (string (aref command (car char-positions)))
+   (dired--highlight-no-subst-chars char-positions command mark-positions)))))
+
+(defun dired--no-subst-ask (char nb-occur details)
+  (let ((hilit-char (propertize (string char) 'face 'warning)))
+    (car
+     (read-multiple-choice
+      (format-message
+       (ngettext
+        "Warning: %d occurrence of `%s' will not be substituted.  Proceed?"
+        "Warning: %d occurrences of `%s' will not be substituted.  Proceed?"
+        nb-occur)
+       nb-occur hilit-char)
+      `((?y "yes" "Send shell command without substituting.")
+        (?n "no" "Abort.")
+        (?d "toggle details" ,(format-message
+                               "Show/hide occurrences of `%s'" hilit-char))
+        ,@(when details
+            '((?m "toggle markers" "Show/hide `^' markers"))))))))
+
+(defun dired--no-subst-confirm (char-positions command)
+  (let ((help-buf (get-buffer-create "*Dired help*"))
+        (char (aref command (car char-positions)))
+        (nb-occur (length char-positions))
+        (done nil)
+        (details nil)
+        (markers nil)
+        proceed)
+    (dired--no-subst-explain help-buf char-positions command nil)
+    (unwind-protect
+        (save-window-excursion
+          (while (not done)
+            (cl-case (dired--no-subst-ask char nb-occur details)
+              (?y
+               (setq done t
+                     proceed t))
+              (?n
+               (setq done t
+                     proceed nil))
+              (?d
+               (if details
+                   (progn
+                     (quit-window nil details)
+                     (setq details nil))
+                 (setq details (display-buffer help-buf))))
+              (?m
+               (setq markers (not markers))
+               (dired--no-subst-explain
+                help-buf char-positions command markers)))))
+      (kill-buffer help-buf))
+    proceed))
 
 ;;;###autoload
 (defun dired-diff (file &optional switches)
@@ -801,19 +857,15 @@ dired-do-shell-command
   (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep)))
 	 (no-subst (not (dired--star-or-qmark-p command "?" 'keep)))
          (confirmations nil)
-         (short-enough (< (length command)
-                          (window-width (minibuffer-window))))
          ;; Get confirmation for wildcards that may have been meant
          ;; to control substitution of a file name or the file name list.
          (ok (cond
               ((not (or on-each no-subst))
                (error "You can not combine `*' and `?' substitution marks"))
               ((setq confirmations (dired--need-confirm-positions command "*"))
-               (y-or-n-p (dired--no-subst-prompt confirmations command
-                                                 short-enough)))
+               (dired--no-subst-confirm confirmations command))
               ((setq confirmations (dired--need-confirm-positions command "?"))
-               (y-or-n-p (dired--no-subst-prompt confirmations command
-                                                 short-enough)))
+               (dired--no-subst-confirm confirmations command))
               (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index ba10c54332..e1d9eefbea 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -28,7 +28,7 @@ dired-test-bug27496
   (let* ((foo (make-temp-file "foo"))
          (files (list foo)))
     (unwind-protect
-        (cl-letf (((symbol-function 'y-or-n-p) 'error))
+        (cl-letf (((symbol-function 'read-multiple-choice) 'error))
           (dired temporary-file-directory)
           (dired-goto-file foo)
           ;; `dired-do-shell-command' returns nil on success.
@@ -127,39 +127,36 @@ dired-test-highlight-metachar
   "Check that non-isolated meta-characters are highlighted."
   (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
          (markers "               ^             ^")
-         (prompt (dired--no-subst-prompt
+         (result (dired--highlight-no-subst-chars
                   (dired--need-confirm-positions command "?")
                   command
                   t))
-         (lines (split-string prompt "\n"))
-         (highlit-command (nth 1 lines)))
-    (should (= (length lines) 4))
-    (should (string-match (regexp-quote command) highlit-command))
-    (should (string-match (regexp-quote markers) (nth 2 lines)))
-    (dired-test--check-highlighting highlit-command '(15 29)))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 2))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (should (string-match (regexp-quote markers) (nth 1 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(15 29)))
   ;; Note that `?` is considered isolated, but `*` is not.
   (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
          (markers "           ^             ^")
-         (prompt (dired--no-subst-prompt
+         (result (dired--highlight-no-subst-chars
                   (dired--need-confirm-positions command "*")
                   command
                   t))
-         (lines (split-string prompt "\n"))
-         (highlit-command (nth 1 lines)))
-    (should (= (length lines) 4))
-    (should (string-match (regexp-quote command) highlit-command))
-    (should (string-match (regexp-quote markers) (nth 2 lines)))
-    (dired-test--check-highlighting highlit-command '(11 25)))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 2))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (should (string-match (regexp-quote markers) (nth 1 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(11 25)))
   (let* ((command "sed 's/\\?/!/'")
-         (prompt (dired--no-subst-prompt
+         (result (dired--highlight-no-subst-chars
                   (dired--need-confirm-positions command "?")
                   command
                   nil))
-         (lines (split-string prompt "\n"))
-         (highlit-command (nth 1 lines)))
-    (should (= (length lines) 3))
-    (should (string-match (regexp-quote command) highlit-command))
-    (dired-test--check-highlighting highlit-command '(8))))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 1))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(8))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/plain


Highlights:

- removed the patch for y-or-n-p, since we don't need it anymore,
- (squashed Noam's patch with my fixups,)
- the last patch contains the new stuff:
    - the default prompt is now as concise as the old one,
    - pressing 'd' toggles a help buffer which highlights occurrences
      using the warning face,
    - when the help buffer is enabled, pressing 'm' toggles the '^'
      markers.

Squashed patch for convenience:


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0001-Tweak-dired-warning-about-wildcard-characters.patch

From 8a51df696ef4d1b794ea75d94b1137f1e1ff536f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Thu, 10 Oct 2019 20:20:41 +0200
Subject: [PATCH] Tweak dired warning about "wildcard" characters

Non-isolated '?' and '*' characters may be quoted, or
backslash-escaped; we do not know for a fact that the shell will
interpret them as wildcards.

Rephrase the prompt and offer to highlight the characters so that the
user sees exactly what we are talking about.

* lisp/dired-aux.el (dired-isolated-string-re): Use explicitly
numbered groups.
(dired--star-or-qmark-p): Add START parameter.  Make sure to
return the first isolated match.
(dired--need-confirm-positions, dired--mark-positions)
(dired--highlight-no-subst-chars, dired--no-subst-explain)
(dired--no-subst-ask, dired--no-subst-confirm): New functions.
(dired-do-shell-command): Use them.

* test/lisp/dired-aux-tests.el (dired-test-bug27496): Adapt to new
prompt.
(dired-test--check-highlighting): New test helper.
(dired-test-highlight-metachar): New tests.

Co-authored-by: Noam Postavsky <npostavs@HIDDEN>

(bug#28969, bug#35564)
---
 lisp/dired-aux.el            | 152 +++++++++++++++++++++++++++++------
 test/lisp/dired-aux-tests.el |  45 ++++++++++-
 2 files changed, 170 insertions(+), 27 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index bfc37c5cde..6b33f4ebfb 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -60,24 +60,133 @@ dired-isolated-string-re
 of a string followed/prefixed with an space.
 The regexp capture the preceding blank, STRING and the following blank as
 the groups 1, 2 and 3 respectively."
-  (format "\\(\\`\\|[ \t]\\)\\(%s\\)\\([ \t]\\|\\'\\)" string))
+  (format "\\(?1:\\`\\|[ \t]\\)\\(?2:%s\\)\\(?3:[ \t]\\|\\'\\)" string))
 
-(defun dired--star-or-qmark-p (string match &optional keep)
+(defun dired--star-or-qmark-p (string match &optional keep start)
   "Return non-nil if STRING contains isolated MATCH or `\\=`?\\=`'.
 MATCH should be the strings \"?\", `\\=`?\\=`', \"*\" or nil.  The latter
 means STRING contains either \"?\" or `\\=`?\\=`' or \"*\".
 If optional arg KEEP is non-nil, then preserve the match data.  Otherwise,
 this function changes it and saves MATCH as the second match group.
+START is the position to start matching from.
 
 Isolated means that MATCH is surrounded by spaces or at the beginning/end
 of STRING followed/prefixed with an space.  A match to `\\=`?\\=`',
 isolated or not, is also valid."
-  (let ((regexps (list (dired-isolated-string-re (if match (regexp-quote match) "[*?]")))))
+  (let ((regexp (dired-isolated-string-re (if match (regexp-quote match) "[*?]"))))
     (when (or (null match) (equal match "?"))
-      (setq regexps (append (list "\\(\\)\\(`\\?`\\)\\(\\)") regexps)))
-    (cl-some (lambda (x)
-               (funcall (if keep #'string-match-p #'string-match) x string))
-             regexps)))
+      (cl-callf concat regexp "\\|\\(?1:\\)\\(?2:`\\?`\\)\\(?3:\\)"))
+    (funcall (if keep #'string-match-p #'string-match) regexp string start)))
+
+(defun dired--need-confirm-positions (command string)
+  "Search for non-isolated matches of STRING in COMMAND.
+Return a list of positions that match STRING, but would not be
+considered \"isolated\" by `dired--star-or-qmark-p'."
+  (cl-assert (= (length string) 1))
+  (let ((start 0)
+        (isolated-char-positions nil)
+        (confirm-positions nil)
+        (regexp (regexp-quote string)))
+    ;; Collect all ? and * surrounded by spaces and `?`.
+    (while (dired--star-or-qmark-p command string nil start)
+      (push (cons (match-beginning 2) (match-end 2))
+            isolated-char-positions)
+      (setq start (match-end 2)))
+    ;; Now collect any remaining ? and *.
+    (setq start 0)
+    (while (string-match regexp command start)
+      (unless (cl-member (match-beginning 0) isolated-char-positions
+                         :test (lambda (pos match)
+                                 (<= (car match) pos (cdr match))))
+        (push (match-beginning 0) confirm-positions))
+      (setq start (match-end 0)))
+    confirm-positions))
+
+(defun dired--mark-positions (positions)
+  (let ((markers (make-string
+                  (1+ (apply #'max positions))
+                  ?\s)))
+    (dolist (pos positions)
+      (setf (aref markers pos) ?^))
+    markers))
+
+(defun dired--highlight-no-subst-chars (positions command mark)
+  (cl-callf substring-no-properties command)
+  (dolist (pos positions)
+    (add-face-text-property pos (1+ pos) 'warning nil command))
+  (if mark
+      (concat command "\n" (dired--mark-positions positions))
+    command))
+
+(defun dired--no-subst-explain (buf char-positions command mark-positions)
+  (with-current-buffer buf
+    (erase-buffer)
+    (insert
+     (format-message "\
+If your command contains occurrences of `*' surrounded by
+whitespace, `dired-do-shell-command' substitutes them for the
+entire file list to process.  Otherwise, if your command contains
+occurrences of `?' surrounded by whitespace or `%s', Dired will
+run the command once for each file, substituting `?' for each
+file name.
+
+Your command contains occurrences of `%s' that will not be
+substituted, and will be passed through normally to the shell.
+
+%s
+"
+   "`"
+   (string (aref command (car char-positions)))
+   (dired--highlight-no-subst-chars char-positions command mark-positions)))))
+
+(defun dired--no-subst-ask (char nb-occur details)
+  (let ((hilit-char (propertize (string char) 'face 'warning)))
+    (car
+     (read-multiple-choice
+      (format-message
+       (ngettext
+        "Warning: %d occurrence of `%s' will not be substituted.  Proceed?"
+        "Warning: %d occurrences of `%s' will not be substituted.  Proceed?"
+        nb-occur)
+       nb-occur hilit-char)
+      `((?y "yes" "Send shell command without substituting.")
+        (?n "no" "Abort.")
+        (?d "toggle details" ,(format-message
+                               "Show/hide occurrences of `%s'" hilit-char))
+        ,@(when details
+            '((?m "toggle markers" "Show/hide `^' markers"))))))))
+
+(defun dired--no-subst-confirm (char-positions command)
+  (let ((help-buf (get-buffer-create "*Dired help*"))
+        (char (aref command (car char-positions)))
+        (nb-occur (length char-positions))
+        (done nil)
+        (details nil)
+        (markers nil)
+        proceed)
+    (dired--no-subst-explain help-buf char-positions command nil)
+    (unwind-protect
+        (save-window-excursion
+          (while (not done)
+            (cl-case (dired--no-subst-ask char nb-occur details)
+              (?y
+               (setq done t
+                     proceed t))
+              (?n
+               (setq done t
+                     proceed nil))
+              (?d
+               (if details
+                   (progn
+                     (quit-window nil details)
+                     (setq details nil))
+                 (setq details (display-buffer help-buf))))
+              (?m
+               (setq markers (not markers))
+               (dired--no-subst-explain
+                help-buf char-positions command markers)))))
+      (kill-buffer help-buf))
+    proceed))
 
 ;;;###autoload
 (defun dired-diff (file &optional switches)
@@ -745,28 +854,19 @@ dired-do-shell-command
       (dired-read-shell-command "! on %s: " current-prefix-arg files)
       current-prefix-arg
       files)))
-  (cl-flet ((need-confirm-p
-             (cmd str)
-             (let ((res cmd)
-                   (regexp (regexp-quote str)))
-               ;; Drop all ? and * surrounded by spaces and `?`.
-               (while (and (string-match regexp res)
-                           (dired--star-or-qmark-p res str))
-                 (setq res (replace-match "" t t res 2)))
-               (string-match regexp res))))
   (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep)))
 	 (no-subst (not (dired--star-or-qmark-p command "?" 'keep)))
+         (confirmations nil)
          ;; Get confirmation for wildcards that may have been meant
          ;; to control substitution of a file name or the file name list.
-         (ok (cond ((not (or on-each no-subst))
-	            (error "You can not combine `*' and `?' substitution marks"))
-	           ((need-confirm-p command "*")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `*' as a wildcard? ")))
-	           ((need-confirm-p command "?")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `?' as a wildcard? ")))
-	           (t))))
+         (ok (cond
+              ((not (or on-each no-subst))
+               (error "You can not combine `*' and `?' substitution marks"))
+              ((setq confirmations (dired--need-confirm-positions command "*"))
+               (dired--no-subst-confirm confirmations command))
+              ((setq confirmations (dired--need-confirm-positions command "?"))
+               (dired--no-subst-confirm confirmations command))
+              (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
            (if on-each
@@ -777,7 +877,7 @@ dired-do-shell-command
 	                          nil file-list)
 	     ;; execute the shell command
 	     (dired-run-shell-command
-	      (dired-shell-stuff-it command file-list nil arg))))))))
+              (dired-shell-stuff-it command file-list nil arg)))))))
 
 ;; Might use {,} for bash or csh:
 (defvar dired-mark-prefix ""
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index ccd3192792..e1d9eefbea 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -28,7 +28,7 @@ dired-test-bug27496
   (let* ((foo (make-temp-file "foo"))
          (files (list foo)))
     (unwind-protect
-        (cl-letf (((symbol-function 'y-or-n-p) 'error))
+        (cl-letf (((symbol-function 'read-multiple-choice) 'error))
           (dired temporary-file-directory)
           (dired-goto-file foo)
           ;; `dired-do-shell-command' returns nil on success.
@@ -114,6 +114,49 @@ dired-test-bug30624
         (mapc #'delete-file `(,file1 ,file2))
         (kill-buffer buf)))))
 
+(defun dired-test--check-highlighting (command positions)
+  (let ((start 1))
+    (dolist (pos positions)
+      (should-not (text-property-not-all start (1- pos) 'face nil command))
+      (should (equal 'warning (get-text-property pos 'face command)))
+      (setq start (1+ pos)))
+    (should-not (text-property-not-all
+                 start (length command) 'face nil command))))
+
+(ert-deftest dired-test-highlight-metachar ()
+  "Check that non-isolated meta-characters are highlighted."
+  (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
+         (markers "               ^             ^")
+         (result (dired--highlight-no-subst-chars
+                  (dired--need-confirm-positions command "?")
+                  command
+                  t))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 2))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (should (string-match (regexp-quote markers) (nth 1 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(15 29)))
+  ;; Note that `?` is considered isolated, but `*` is not.
+  (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
+         (markers "           ^             ^")
+         (result (dired--highlight-no-subst-chars
+                  (dired--need-confirm-positions command "*")
+                  command
+                  t))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 2))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (should (string-match (regexp-quote markers) (nth 1 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(11 25)))
+  (let* ((command "sed 's/\\?/!/'")
+         (result (dired--highlight-no-subst-chars
+                  (dired--need-confirm-positions command "?")
+                  command
+                  nil))
+         (lines (split-string result "\n")))
+    (should (= (length lines) 1))
+    (should (string-match (regexp-quote command) (nth 0 lines)))
+    (dired-test--check-highlighting (nth 0 lines) '(8))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.23.0


--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable


To try the changes out, it's enough to reload dired-aux.el, mark a few
files in Dired, type e.g.

    ! sed 's/?/!/g' RET

=E2=80=A6 and play with the new prompt.

Let me know if this UI looks OK, and how the implementation may be
improved.  Thank you for your patience.


Not addressed in this patch series:

- letting the user iterate over non-isolated occurrences and
  selectively substitute them,
- allowing '*' to be substituted when surrounded by backquotes, just
  like '?'.

I do find these features valuable (or at least worthy of discussion),
however the current bug reports were motivated merely by an inaccurate
warning; I'd like to close this first before considering further
changes.


[1] '?' when not surrounded by whitespace or backquotes,
    '*' when not surrounded by whitespace.

[2] Trying to find the right balance between concision and accurate
    explanation, considering that some users may not know about the
    file-substitution feature; also trying to make the highlighting
    "accessible", i.e. not just relying on colored faces.

--=-=-=--




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 16 Jul 2019 05:53:23 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Jul 16 01:53:23 2019
Received: from localhost ([127.0.0.1]:49519 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hnGOx-0001eU-Fc
	for submit <at> debbugs.gnu.org; Tue, 16 Jul 2019 01:53:23 -0400
Received: from mail-wr1-f49.google.com ([209.85.221.49]:41643)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <kevin.legouguec@HIDDEN>) id 1hnGOu-0001eF-UA
 for 28969 <at> debbugs.gnu.org; Tue, 16 Jul 2019 01:53:21 -0400
Received: by mail-wr1-f49.google.com with SMTP id c2so16253858wrm.8
 for <28969 <at> debbugs.gnu.org>; Mon, 15 Jul 2019 22:53:20 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
 h=from:to:cc:subject:references:date:in-reply-to:message-id
 :user-agent:mime-version:content-transfer-encoding;
 bh=EczI2kEAOMNDHzTU/FItQabo/JWkHaI6k8RvBTQSQvo=;
 b=hFNdcM4GAvqMK/UL7KeZgU26NHgSb7YO4D9y2gPSyU/l26pIr0NfWDGK6aCNJhnp7u
 V+0K1t1kOPXb+f4DBKLOwF46LEtRk2LRArIBOvPUAUj7ezmEmlOD+LemHdSRxa/fPGs+
 GuXrrub/JSA/oPoPhKEyk0TVwawhm/NnvaOfrLmYCuYfe654mHhxi0TZFzvajQjIoTs0
 YYpqEuce7zPFvxkeaDEGNgYdBcGnmk63vQf6QkEm2Hb7igKfxAuFYFOiZnsac9trrAna
 enweogtKLijRg4vZIzjzvET3R7EjsjCswW5F99G7fj5BO8OXLCqWCNd/EXMfjPvtHR4/
 wC8Q==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to
 :message-id:user-agent:mime-version:content-transfer-encoding;
 bh=EczI2kEAOMNDHzTU/FItQabo/JWkHaI6k8RvBTQSQvo=;
 b=ZbLn/PR+b7mbD1GtBEHaWNCkEsK/3VRo68dXpgJb4n6NekckPCEfcVZf6g//fv0kOh
 nshiVouJu8VTg5o5DjdJSXJ7btAXS6Eu+6SHP92Bu0X6BA0GvmyW8sdgbZ8wiQjZlwKz
 gaDAo6mOk3Ws7OEgEDyHGrZECvJsO06WKTOA68v1h7AI9oMlMmyuS/+rTuvc1HwkDOgw
 evH6pi0s2ly8N8zdMILHyz0JtWKRa6ZkpdRVWyZpPV6270MjgR4iCc7QT4LVSB+Ghirm
 gd9z8FTiTBV0Wjajwt+N6lIsqf2TszDdSJn3a893nkuD36rC4qAmcMSG00gOuDQEZj2X
 i7ow==
X-Gm-Message-State: APjAAAUBp3j0Lce03ksLE91pElMZwwnJiGWBll5Cd+A7QP5hIDZnBKFh
 x7JopOXHbZfm6oSPkKH+Rvw=
X-Google-Smtp-Source: APXvYqwcoFzlrJ24q0MkD4E1q0ETYe6uTeCoO0a7Ix/mfYHI7fU8mAIpSNrtucGY+I5WOF2IlJGvwA==
X-Received: by 2002:adf:f646:: with SMTP id x6mr35426101wrp.18.1563256394932; 
 Mon, 15 Jul 2019 22:53:14 -0700 (PDT)
Received: from my-little-tumbleweed (71.142.13.109.rev.sfr.net.
 [109.13.142.71])
 by smtp.gmail.com with ESMTPSA id t6sm19831531wmb.29.2019.07.15.22.53.13
 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256);
 Mon, 15 Jul 2019 22:53:14 -0700 (PDT)
From: =?utf-8?Q?K=C3=A9vin_Le_Gouguec?= <kevin.legouguec@HIDDEN>
To: Michael Heerdegen <michael_heerdegen@HIDDEN>
Subject: Re: bug#28969: 27.0.50;
 dired: Confirmation prompt for wildcard not surrounded by whitespace
References: <87she833e1.fsf@HIDDEN> <877e8kwbsn.fsf@HIDDEN>
 <87o91wgjxj.fsf@HIDDEN> <87blxvp0ly.fsf@HIDDEN>
 <87wogjxbsr.fsf@HIDDEN>
Date: Tue, 16 Jul 2019 07:53:09 +0200
In-Reply-To: <87wogjxbsr.fsf@HIDDEN> (Michael Heerdegen's message of "Mon, 15
 Jul 2019 22:50:12 +0200")
Message-ID: <87lfwyzfsq.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: Lars Ingebrigtsen <larsi@HIDDEN>, 28969 <at> debbugs.gnu.org,
 Noam Postavsky <npostavs@HIDDEN>, Drew Adams <drew.adams@HIDDEN>
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Michael Heerdegen <michael_heerdegen@HIDDEN> writes:

> BTW, in the docstring of `dired-do-shell-command',
>
> (1) In this sentence:
>
> | `*' and `?' when not surrounded by whitespace nor ``' have no special
>
> can we avoid that ` gets linked to the backquote macro?

No idea how to fix that off the top of my head.

> (2) "If you want to use `*' as a shell wildcard with whitespace around
> it, write `*\"\"' in place of just `*'."
>
> does that really mean *"" or rather "*"?

I think it really means *"".  From some quick testing in a Dired buffer:

    M-! touch foo bar baz RET
    g                           ; Assuming point is now on "bar"
    ! echo quux "*" corge RET y ; =E2=87=92 quux * corge bar
    ! echo quux *"" corge RET y ; =E2=87=92 quux bar baz foo corge bar

*'' also works.  AFAICT it's a way to work around Dired's isolation
detection (* is not surrounded with spaces, so it's not isolated) while
exploiting the fact that the quoted empty string will disappear once
"expanded" by the shell.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 15 Jul 2019 20:50:40 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Mon Jul 15 16:50:40 2019
Received: from localhost ([127.0.0.1]:49275 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hn7vj-0001yt-0z
	for submit <at> debbugs.gnu.org; Mon, 15 Jul 2019 16:50:40 -0400
Received: from mout.web.de ([212.227.15.14]:51649)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1hn7ve-0001yP-7t
 for 28969 <at> debbugs.gnu.org; Mon, 15 Jul 2019 16:50:35 -0400
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=web.de;
 s=dbaedf251592; t=1563223815;
 bh=utVHLPD+taUnDeXAJ9rWOxG+JYFa6++R8x5T9xfLPDk=;
 h=X-UI-Sender-Class:From:To:Cc:Subject:References:Date:In-Reply-To;
 b=STCS5CpKG6Q1YLF0WvTdSt2kyQRw4rjIwd3K41Z0VLGAYCRa4aoTMfMocgw0+VZzi
 hhKAjb5+jO7io4hRUYYcolZdnzvC2yQXfEYl0L3Dn1SriPImEpywPmtkVrw1ONBwnE
 mVz91NyGpJPsWw8JFmP5uFAIBKihhO/Di9ep9B/E=
X-UI-Sender-Class: c548c8c5-30a9-4db5-a2e7-cb6cb037b8f9
Received: from drachen.dragon ([92.208.178.213]) by smtp.web.de (mrweb003
 [213.165.67.108]) with ESMTPSA (Nemesis) id 0MQvhG-1ht4lV0RCQ-00UKkt; Mon, 15
 Jul 2019 22:50:15 +0200
From: Michael Heerdegen <michael_heerdegen@HIDDEN>
To: =?utf-8?Q?K=C3=A9vin?= Le Gouguec <kevin.legouguec@HIDDEN>
Subject: Re: bug#28969: 27.0.50; dired: Confirmation prompt for wildcard not
 surrounded by whitespace
References: <87she833e1.fsf@HIDDEN> <877e8kwbsn.fsf@HIDDEN>
 <87o91wgjxj.fsf@HIDDEN> <87blxvp0ly.fsf@HIDDEN>
Date: Mon, 15 Jul 2019 22:50:12 +0200
In-Reply-To: <87blxvp0ly.fsf@HIDDEN> (=?utf-8?Q?=22K=C3=A9vin?= Le
 Gouguec"'s message of "Mon, 15 Jul 2019 21:19:05 +0200")
Message-ID: <87wogjxbsr.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Provags-ID: V03:K1:O7xmE6Gfmx9B1xw6sTKFFc1lTj5xalwe8vLtodLulJGu2niYNkp
 rCE2wB+Jm7qIv4ypAWQifgS91f9G7B4cVxPbyGMXdbeG3FXc4dVCC192A4/s73Wp6j7aHHh
 STEDhrsqLb/vOUQCRK4AKh9ayOtlLK2l5L9gCLPEPtkl62FXLgpmY5PKHrL2jiZcgCHd0nE
 Hw57jWfY0ZmUhTjNQHhUA==
X-Spam-Flag: NO
X-UI-Out-Filterresults: notjunk:1;V03:K0:jAa3XLt8D60=:U+jN4SmAHowg7Fdz9H3GDJ
 S4VMY8XREckN/Tm9tqbiFUQBPoG3gwPBK+10JdCPn2yIEXgD8YezNcEkV0huSRoiiCQ2SExDM
 4mfK9n1t9yhf14JXaxW9he449+P36IOIYwQ13h2La1vzgNnDll7lZNhnZouIazY1CElq5AtmA
 +RTPrwElddS23Dp3SXIe2SMGtb9Qe+5MvzTs7XDY5xPbHi7uLZjN83BjY4TIx5mM28OoEroUj
 w2OWnwvXu6+Ks45MUfNQPSxz8iokVsdoCiTka1n7UrainDGQMwJ31HIGgYlreNjdEbH/+L74S
 SPA/k6gppA1f1ChHOpoc6YVAljkDaHc6qWIJUfoB8C/BV3TRvuFNydQdIxEWvFBRsIFgsDhwP
 wrNCN/2etWeo7sW2XBnRN6Qf77NufPonRCdpUeSZSYZJMHdD+V774Yfyknrq6UfFoMt7J5/tz
 pHCW5Tznc6i1Jl++S2EywGNBkwqZ3/PenOYxANbsUdZjjE99sjMVqm3APKEYvTSjOJZd3q/1r
 nbwtBvV7y8VlCajc/Yf/YQVSkNi2LrkygbhkOj7bIPCuaC2PKM4C6jsa/HAZo3bDWCOIZedQA
 2Iepkt7de4YkArj7zzK9IVIXRo/ElqkLeIx2GwNERFP6pBs6Ru5siVncXEPdYBPjlE3UzHcv9
 omiJNbaMeYMS2cxVwhtxbXXAJTkaQ9mIvSxdYBOeRHXhdwHXhb1BbOjvabliVsB7rd7l+i1Q3
 58QeJJ+2IEXwvmcYHUDkEecbUculIiqnirWy//5AtIf/Km4rC0cB5Lh8BjjsPTQ5nRPhm/hNV
 lbf1K5EBpGHRZpnLpsvHqNCHozCJZGRlxGBSMfk9IguOYPH4El7HVH1HcheVgSZHeJXBjZvYT
 A1e6/N4UAj8z19AltiJpWixGA+g7oEMKUenLfH53PuvftJIQwuLtvUI0MtYaosQ2hEPbani2y
 bvLJy/eNQEdADTakiG4dsPQb7cgeonhQaS2oWqFUXK8fbC4hYz/dbzXlsD6slJqrjCwrUbWWK
 Kws9HWpfGp+RxJfnWDcElkYdq48A+QmTKyJp1bh9zjtZaP2ZE6RRA8x/RCXIU4TBJ9RJvzzC2
 Yp8AaCh8jb/jR3LMRanJdjYi0PV2UBKuLoj
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: Lars Ingebrigtsen <larsi@HIDDEN>, 28969 <at> debbugs.gnu.org,
 Noam Postavsky <npostavs@HIDDEN>, Drew Adams <drew.adams@HIDDEN>
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

K=C3=A9vin Le Gouguec <kevin.legouguec@HIDDEN> writes:

> Yup, that's what I set out to do in bug#35564.  Here is the patch
> series, condensed into a single patch for convenience.

Ok, will try it.

> Although note that you can already tell Dired that your '?' is meant to
> be substituted, by surrounding it with backquotes.  E.g. try to mark
> some files, then
>
>     ! echo 'foo`?`bar'

Maybe this is already enough.

> It's not implemented for '*' though.

Dunno if that has use cases.


BTW, in the docstring of `dired-do-shell-command',

(1) In this sentence:

| `*' and `?' when not surrounded by whitespace nor ``' have no special

can we avoid that ` gets linked to the backquote macro?

And

(2) "If you want to use `*' as a shell wildcard with whitespace around
it, write `*\"\"' in place of just `*'."

does that really mean *"" or rather "*"?

Michael.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 15 Jul 2019 19:19:21 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Mon Jul 15 15:19:21 2019
Received: from localhost ([127.0.0.1]:49139 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hn6VM-0005B7-C1
	for submit <at> debbugs.gnu.org; Mon, 15 Jul 2019 15:19:20 -0400
Received: from mail-wr1-f49.google.com ([209.85.221.49]:33291)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <kevin.legouguec@HIDDEN>) id 1hn6VK-0005Ar-4i
 for 28969 <at> debbugs.gnu.org; Mon, 15 Jul 2019 15:19:18 -0400
Received: by mail-wr1-f49.google.com with SMTP id n9so18365222wru.0
 for <28969 <at> debbugs.gnu.org>; Mon, 15 Jul 2019 12:19:18 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
 h=from:to:cc:subject:references:date:in-reply-to:message-id
 :user-agent:mime-version;
 bh=69mGZ4EpGfYlGtW8FMMOof/VW6Xd+xOKI+GYh4m1Egc=;
 b=UMsRQiyXpach8Oo2gIayncSQZaA8gHRK9hvaXEHUDX1Ah1HjLRr2ZHqvVZxdRH+W7+
 Z93C1VQ+c6dxf/S+L88WLfRCJLtZjc0biaRTvwR0Bx90kSCBlDZYXHRPyUY0KHJAiQCI
 IctaKRpZDbLSyNJpexZofWMgTGdz9DMdn482S5RAU3cM5Ow++J+t9aeRf7+UuKgwVOXu
 6Y0dHW75Oh0c7sDwhGDy/Ii/UF5QbFLsNTzG/0ibxqsABSSoAh4RElm98g4wXnQXV9kE
 QUhCqRoZ6NRtmXdmFAwpNnIRqij49D7oQ3pBcxt8uMAX3f67vRpdWyKMZzf6SaYXC0KG
 /gGQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to
 :message-id:user-agent:mime-version;
 bh=69mGZ4EpGfYlGtW8FMMOof/VW6Xd+xOKI+GYh4m1Egc=;
 b=PUsTGj2XtiBg6GMfZqR7MvdVM3Y7ZAwXlaZFsC/MHqObqIXl+QmhdbODjO19Gge1BO
 BQKsQCC/kUPqptFnyulk4S/aj/Gt9casSy04QPZC3VSvi/Hv5SYjMW3S38QEy6mleg/P
 QrnxjeZwmA0Iu57wYSkeqtTmv0W42/Shhx+C1C/7XGt5/CdMevFb8t+CqNCjrFnOnW2h
 MPy+XSPZQNZhgwij2asm53WGa6iA0e35fvpqboURV3dfJwT/HC9ia7eUyxvMvjVh+tOz
 +86eOSH94wK0TxU6bLo1cBhCMqly/+Fj8wKvRjDwnuVXc45zAs6++7cSMHFicivXxKq4
 Gq8Q==
X-Gm-Message-State: APjAAAWCb4CneILuMa1FFM57GZ4GVf/iiBbMooRyHYQJTN1tLGnDwA72
 qEQ52tLmPZJ8CLracMiA5YA=
X-Google-Smtp-Source: APXvYqxC+SBgacGZEIGbZtxULbfxwiF8bAQlROwOCKyZGGrHAWkryNER6T/PLm4JPdEUyhSLkvJoww==
X-Received: by 2002:a5d:6408:: with SMTP id z8mr15785205wru.246.1563218352310; 
 Mon, 15 Jul 2019 12:19:12 -0700 (PDT)
Received: from my-little-tumbleweed (71.142.13.109.rev.sfr.net.
 [109.13.142.71])
 by smtp.gmail.com with ESMTPSA id f70sm19895404wme.22.2019.07.15.12.19.07
 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256);
 Mon, 15 Jul 2019 12:19:09 -0700 (PDT)
From: =?utf-8?Q?K=C3=A9vin_Le_Gouguec?= <kevin.legouguec@HIDDEN>
To: Michael Heerdegen <michael_heerdegen@HIDDEN>
Subject: Re: bug#28969: 27.0.50;
 dired: Confirmation prompt for wildcard not surrounded by whitespace
References: <87she833e1.fsf@HIDDEN> <877e8kwbsn.fsf@HIDDEN>
 <87o91wgjxj.fsf@HIDDEN>
Date: Mon, 15 Jul 2019 21:19:05 +0200
In-Reply-To: <87o91wgjxj.fsf@HIDDEN> (Michael Heerdegen's message of "Mon, 15
 Jul 2019 03:34:16 +0200")
Message-ID: <87blxvp0ly.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: Lars Ingebrigtsen <larsi@HIDDEN>, 28969 <at> debbugs.gnu.org,
 Noam Postavsky <npostavs@HIDDEN>, Drew Adams <drew.adams@HIDDEN>
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

--=-=-=
Content-Type: text/plain

Michael Heerdegen <michael_heerdegen@HIDDEN> writes:

> In my example (in my
> initial report), also the shell did not interpret it as wildcard, but I
> had to say "y" to get it executed.  This is very confusing.  It would be
> better to ask "confirm - pass literal `*' to the shell?" or so.

Yup, that's what I set out to do in bug#35564.  Here is the patch
series, condensed into a single patch for convenience.


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment;
 filename=0001-Tweak-dired-warning-about-wildcard-characters.patch

From 593096b329a65466c075599697fdaccd64b3ada4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@HIDDEN>
Date: Fri, 7 Jun 2019 17:03:59 +0200
Subject: [PATCH] Tweak dired warning about "wildcard" characters

Non-isolated '?' and '*' characters may be quoted, or
backslash-escaped; we do not know for a fact that the shell will
interpret them as wildcards.

Rephrase the prompt and highlight the characters so that the user
sees exactly what we are talking about.

* lisp/subr.el (read--propertize-prompt): New function to append
the prompt face to a string.
(y-or-n-p): Use it instead of discarding potential text
properties.

* lisp/dired-aux.el (dired-isolated-string-re): Use explicitly
numbered groups.
(dired--star-or-qmark-p): Add START parameter.  Make sure to
return the first isolated match.
(dired--need-confirm-positions, dired--mark-positions)
(dired--no-subst-prompt): New functions.
(dired-do-shell-command): Use them to display the command and
highlight the non-isolated chars.  Underline these chars with '^'
markers if the minibuffer window is wide enough to show the
command without line-wrapping it.

* test/lisp/dired-aux-tests.el (dired-test--check-highlighting):
New tests.

Co-authored-by: Noam Postavsky <npostavs@HIDDEN>

(bug#28969, bug#35564)
---
 lisp/dired-aux.el            | 100 ++++++++++++++++++++++++++---------
 lisp/subr.el                 |  15 +++---
 test/lisp/dired-aux-tests.el |  46 ++++++++++++++++
 3 files changed, 129 insertions(+), 32 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 6a1ebcced9..cc3903ab15 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -60,24 +60,77 @@ dired-isolated-string-re
 of a string followed/prefixed with an space.
 The regexp capture the preceding blank, STRING and the following blank as
 the groups 1, 2 and 3 respectively."
-  (format "\\(\\`\\|[ \t]\\)\\(%s\\)\\([ \t]\\|\\'\\)" string))
+  (format "\\(?1:\\`\\|[ \t]\\)\\(?2:%s\\)\\(?3:[ \t]\\|\\'\\)" string))
 
-(defun dired--star-or-qmark-p (string match &optional keep)
+(defun dired--star-or-qmark-p (string match &optional keep start)
   "Return non-nil if STRING contains isolated MATCH or `\\=`?\\=`'.
 MATCH should be the strings \"?\", `\\=`?\\=`', \"*\" or nil.  The latter
 means STRING contains either \"?\" or `\\=`?\\=`' or \"*\".
 If optional arg KEEP is non-nil, then preserve the match data.  Otherwise,
 this function changes it and saves MATCH as the second match group.
+START is the position to start matching from.
 
 Isolated means that MATCH is surrounded by spaces or at the beginning/end
 of STRING followed/prefixed with an space.  A match to `\\=`?\\=`',
 isolated or not, is also valid."
-  (let ((regexps (list (dired-isolated-string-re (if match (regexp-quote match) "[*?]")))))
+  (let ((regexp (dired-isolated-string-re (if match (regexp-quote match) "[*?]"))))
     (when (or (null match) (equal match "?"))
-      (setq regexps (append (list "\\(\\)\\(`\\?`\\)\\(\\)") regexps)))
-    (cl-some (lambda (x)
-               (funcall (if keep #'string-match-p #'string-match) x string))
-             regexps)))
+      (cl-callf concat regexp "\\|\\(?1:\\)\\(?2:`\\?`\\)\\(?3:\\)"))
+    (funcall (if keep #'string-match-p #'string-match) regexp string start)))
+
+(defun dired--need-confirm-positions (command string)
+  "Search for non-isolated matches of STRING in COMMAND.
+Return a list of positions that match STRING, but would not be
+considered \"isolated\" by `dired--star-or-qmark-p'."
+  (cl-assert (= (length string) 1))
+  (let ((start 0)
+        (isolated-char-positions nil)
+        (confirm-positions nil)
+        (regexp (regexp-quote string)))
+    ;; Collect all ? and * surrounded by spaces and `?`.
+    (while (dired--star-or-qmark-p command string nil start)
+      (push (cons (match-beginning 2) (match-end 2))
+            isolated-char-positions)
+      (setq start (match-end 2)))
+    ;; Now collect any remaining ? and *.
+    (setq start 0)
+    (while (string-match regexp command start)
+      (unless (cl-member (match-beginning 0) isolated-char-positions
+                         :test (lambda (pos match)
+                                 (<= (car match) pos (cdr match))))
+        (push (match-beginning 0) confirm-positions))
+      (setq start (match-end 0)))
+    confirm-positions))
+
+(defun dired--mark-positions (positions)
+  (let ((markers (make-string
+                  (1+ (apply #'max positions))
+                  ?\s)))
+    (dolist (pos positions)
+      (setf (aref markers pos) ?^))
+    markers))
+
+(defun dired--no-subst-prompt (char-positions command add-markers)
+  (cl-callf substring-no-properties command)
+  (dolist (pos char-positions)
+    (add-face-text-property pos (1+ pos) 'warning nil command))
+  ;; `y-or-n-p' adds some text to the beginning of the prompt when the
+  ;; user fails to answer 'y' or 'n'.  The highlighted command thus
+  ;; cannot be put on the first line of the prompt, since the added
+  ;; text will shove the command to the right, and the '^' markers
+  ;; will become misaligned.
+  (apply #'concat
+         `("Confirm:\n"
+           ,command "\n"
+           ,@(when add-markers
+               (list (dired--mark-positions char-positions) "\n"))
+           ,(format-message
+             (ngettext "Send %d occurrence of `%s' as-is to shell?"
+                       "Send %d occurrences of `%s' as-is to shell?"
+                       (length char-positions))
+             (length char-positions)
+             (propertize (string (aref command (car char-positions)))
+                         'face 'warning)))))
 
 ;;;###autoload
 (defun dired-diff (file &optional switches)
@@ -745,28 +798,23 @@ dired-do-shell-command
       (dired-read-shell-command "! on %s: " current-prefix-arg files)
       current-prefix-arg
       files)))
-  (cl-flet ((need-confirm-p
-             (cmd str)
-             (let ((res cmd)
-                   (regexp (regexp-quote str)))
-               ;; Drop all ? and * surrounded by spaces and `?`.
-               (while (and (string-match regexp res)
-                           (dired--star-or-qmark-p res str))
-                 (setq res (replace-match "" t t res 2)))
-               (string-match regexp res))))
   (let* ((on-each (not (dired--star-or-qmark-p command "*" 'keep)))
 	 (no-subst (not (dired--star-or-qmark-p command "?" 'keep)))
+         (confirmations nil)
+         (short-enough (< (length command)
+                          (window-width (minibuffer-window))))
          ;; Get confirmation for wildcards that may have been meant
          ;; to control substitution of a file name or the file name list.
-         (ok (cond ((not (or on-each no-subst))
-	            (error "You can not combine `*' and `?' substitution marks"))
-	           ((need-confirm-p command "*")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `*' as a wildcard? ")))
-	           ((need-confirm-p command "?")
-	            (y-or-n-p (format-message
-			       "Confirm--do you mean to use `?' as a wildcard? ")))
-	           (t))))
+         (ok (cond
+              ((not (or on-each no-subst))
+               (error "You can not combine `*' and `?' substitution marks"))
+              ((setq confirmations (dired--need-confirm-positions command "*"))
+               (y-or-n-p (dired--no-subst-prompt confirmations command
+                                                 short-enough)))
+              ((setq confirmations (dired--need-confirm-positions command "?"))
+               (y-or-n-p (dired--no-subst-prompt confirmations command
+                                                 short-enough)))
+              (t))))
     (cond ((not ok) (message "Command canceled"))
           (t
            (if on-each
@@ -777,7 +825,7 @@ dired-do-shell-command
 	                          nil file-list)
 	     ;; execute the shell command
 	     (dired-run-shell-command
-	      (dired-shell-stuff-it command file-list nil arg))))))))
+              (dired-shell-stuff-it command file-list nil arg)))))))
 
 ;; Might use {,} for bash or csh:
 (defvar dired-mark-prefix ""
diff --git a/lisp/subr.el b/lisp/subr.el
index 4a1649f601..c59f13b24c 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2338,6 +2338,9 @@ memory-limit
 
 ;;;; Input and display facilities.
 
+(defun read--propertize-prompt (prompt)
+  (add-face-text-property 0 (length prompt) 'minibuffer-prompt t prompt))
+
 (defconst read-key-empty-map (make-sparse-keymap))
 
 (defvar read-key-delay 0.01) ;Fast enough for 100Hz repeat rate, hopefully.
@@ -2675,14 +2678,14 @@ y-or-n-p
           (let* ((scroll-actions '(recenter scroll-up scroll-down
 				   scroll-other-window scroll-other-window-down))
 		 (key
-                  (let ((cursor-in-echo-area t))
+                  (let ((cursor-in-echo-area t)
+                        (prompt (if (memq answer scroll-actions)
+                                    prompt
+                                  (concat "Please answer y or n.  " prompt))))
                     (when minibuffer-auto-raise
                       (raise-frame (window-frame (minibuffer-window))))
-                    (read-key (propertize (if (memq answer scroll-actions)
-                                              prompt
-                                            (concat "Please answer y or n.  "
-                                                    prompt))
-                                          'face 'minibuffer-prompt)))))
+                    (read--propertize-prompt prompt)
+                    (read-key prompt))))
             (setq answer (lookup-key query-replace-map (vector key) t))
             (cond
 	     ((memq answer '(skip act)) nil)
diff --git a/test/lisp/dired-aux-tests.el b/test/lisp/dired-aux-tests.el
index ccd3192792..ba10c54332 100644
--- a/test/lisp/dired-aux-tests.el
+++ b/test/lisp/dired-aux-tests.el
@@ -114,6 +114,52 @@ dired-test-bug30624
         (mapc #'delete-file `(,file1 ,file2))
         (kill-buffer buf)))))
 
+(defun dired-test--check-highlighting (command positions)
+  (let ((start 1))
+    (dolist (pos positions)
+      (should-not (text-property-not-all start (1- pos) 'face nil command))
+      (should (equal 'warning (get-text-property pos 'face command)))
+      (setq start (1+ pos)))
+    (should-not (text-property-not-all
+                 start (length command) 'face nil command))))
+
+(ert-deftest dired-test-highlight-metachar ()
+  "Check that non-isolated meta-characters are highlighted."
+  (let* ((command "sed -r -e 's/oo?/a/' -e 's/oo?/a/' ? `?`")
+         (markers "               ^             ^")
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "?")
+                  command
+                  t))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 4))
+    (should (string-match (regexp-quote command) highlit-command))
+    (should (string-match (regexp-quote markers) (nth 2 lines)))
+    (dired-test--check-highlighting highlit-command '(15 29)))
+  ;; Note that `?` is considered isolated, but `*` is not.
+  (let* ((command "sed -e 's/o*/a/' -e 's/o`*` /a/'")
+         (markers "           ^             ^")
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "*")
+                  command
+                  t))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 4))
+    (should (string-match (regexp-quote command) highlit-command))
+    (should (string-match (regexp-quote markers) (nth 2 lines)))
+    (dired-test--check-highlighting highlit-command '(11 25)))
+  (let* ((command "sed 's/\\?/!/'")
+         (prompt (dired--no-subst-prompt
+                  (dired--need-confirm-positions command "?")
+                  command
+                  nil))
+         (lines (split-string prompt "\n"))
+         (highlit-command (nth 1 lines)))
+    (should (= (length lines) 3))
+    (should (string-match (regexp-quote command) highlit-command))
+    (dired-test--check-highlighting highlit-command '(8))))
 
 (provide 'dired-aux-tests)
 ;; dired-aux-tests.el ends here
-- 
2.22.0


--=-=-=
Content-Type: text/plain


It is a bit more involved than a simple rewording, mainly because I
could not find a concise sentence that sounded 100%-unambiguous
(e.g. "literal" might be taken to mean "suitably backslash-escaped or
quoted").

> BTW, I had several use cases where * or ?, don't remember, was not
> isolated, and I wanted to answer "n" to still get the substitution by
> the command and was disappointed that Emacs just canceled.  Maybe one of
> the suggested patches also improves that, I haven't checked yet.

Allowing the user to substitute non-isolated characters is something
Drew also suggested in bug#35564.

I haven't tackled that yet (haven't met the use-case).  What would a
good UI look like?  Successive prompting for each non-isolated
character?  Something like:

> Substitute highlighted occurrence of `?'? ([y]es, [n]o, [a]bort)

Although note that you can already tell Dired that your '?' is meant to
be substituted, by surrounding it with backquotes.  E.g. try to mark
some files, then

    ! echo 'foo`?`bar'

It's not implemented for '*' though.

--=-=-=--




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 15 Jul 2019 01:34:32 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Jul 14 21:34:32 2019
Received: from localhost ([127.0.0.1]:46209 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hmpsu-0006K6-0M
	for submit <at> debbugs.gnu.org; Sun, 14 Jul 2019 21:34:32 -0400
Received: from mout.web.de ([217.72.192.78]:58483)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1hmpss-0006Jq-Ep
 for 28969 <at> debbugs.gnu.org; Sun, 14 Jul 2019 21:34:31 -0400
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=web.de;
 s=dbaedf251592; t=1563154459;
 bh=bQlR5Y3Ur+9k4UuOx8EIqtlsjuP3esUO0TXUC5Uhv5U=;
 h=X-UI-Sender-Class:From:To:Cc:Subject:References:Date:In-Reply-To;
 b=NH6H3ZGRiGJu2T/5i9DM0foZpTjI7O0pGISXWxf+CI3htj+/TKvqTBMoikbcvgeIY
 A/bgoWwx+D4IX1WQVJJoIBNOpW3A2UDmooleBL+8k7m81W+jj8w/vcCIsohY178BUZ
 kygUu88yP7LxPRHm4ui5vwA1j/IDBIoPJsS8pteY=
X-UI-Sender-Class: c548c8c5-30a9-4db5-a2e7-cb6cb037b8f9
Received: from drachen.dragon ([92.208.178.213]) by smtp.web.de (mrweb102
 [213.165.67.124]) with ESMTPSA (Nemesis) id 0ML8X7-1hmYtm2W2L-000MRx; Mon, 15
 Jul 2019 03:34:19 +0200
From: Michael Heerdegen <michael_heerdegen@HIDDEN>
To: Lars Ingebrigtsen <larsi@HIDDEN>
Subject: Re: bug#28969: 27.0.50; dired: Confirmation prompt for wildcard not
 surrounded by whitespace
References: <87she833e1.fsf@HIDDEN> <877e8kwbsn.fsf@HIDDEN>
Date: Mon, 15 Jul 2019 03:34:16 +0200
In-Reply-To: <877e8kwbsn.fsf@HIDDEN> (Lars Ingebrigtsen's message of
 "Sun, 14 Jul 2019 23:23:20 +0200")
Message-ID: <87o91wgjxj.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: text/plain
X-Provags-ID: V03:K1:ja7pLuxSbCVs9fLJeOsDXFp+a2CPOLBMaGJEoOuwnynz7en/8fp
 pPwVeKzARuylnlC9PYkgKbEcCgM8vyi3DNz+WwLM4l+CyfrWlqPR44pqc4qvKCgIWqgncTh
 c5DiJMsnxqi/MRXYuVZk9gR5U7onmlTZ7MogIxUmI1FNwmMHklBzhLb9t3kt4+zmuSSU/or
 DmRdtzJG0U7aKWSA1JefQ==
X-Spam-Flag: NO
X-UI-Out-Filterresults: notjunk:1;V03:K0:r1ZJC4OfZns=:REbMEnTcTr7ao0S78pJlQE
 xcxOeSiB3upYk+stOzwEGt/dL1efs3e6GVNT1i/XCaUvWEsm9cJW9T983eYPU50Au8bfxbwVn
 VbAa+dEZlbLU0DL9FY2rm9phf3A6Nu+dz6I2VJsevvqDM0DQTq+Yb6BPB6qfMEEL2CEFqejUR
 IRjZJ7Fq649/6yfabNPzi2CgprqAcly2IjPNAY6hKBEgi2yk0UGOyCUZ8lcgU0jBOwqQfjGuK
 8vgSTmcNse8wVrp+VSVGvqCkMMpx4ugJkozWHkIKj4/LQ3CW2ZQtBIgCRu2CqgYLZDrg+qfZT
 POYkPDInUcAsNMyDYUhqMNfixarqejU35gXvkK1NioRFsQIWOWCYRWm7ZAIsP35AaT7e28oW1
 Wq6tH173vaDZ8GTtj88W4upV4Pvb80d53W24R1r/t43GVVLcHoukEMDenPa78iPw+8h9df08D
 feF1ruB3pnyynx3ekFheS/lQvSLbP8KxG0C4FOs8UEyb/tAMOI1Mt77KUC/UTrpbPgvILg+pP
 eo9OYxPVSkHioUudbDNEePwLQYus5KzhwzDuCcdGJf0pKiPtrY5JxtsrnmIaGnEzezWdG9TqS
 W+PQ+NsUkcv+MT6ieyLfRUTIgHsOowD8DrKf1mUejSJ7VNiAudISkbeENjmJoUcqErzlEgWVA
 30KUYJM4DooMLN0c+8GpBWujFHX/AwoMvTjiuy8XSupTlalZ9cDz3V6BaBY/BpMCeZP8uIT3E
 iPu6gfbqQWXj5jtON5ePdZe+6gGt7tpOIdykzgv+d7Z8oaODah/Zi0sa5JObCR8/a0qKo5g9i
 Zb6DlR12F3AwJK3lI6mHYCPsjv5jg8dQJi7oUKLGH0T60N6jggvEnbWXqpzbpHfoac4xsEVKI
 qGR9FE9Y/RcXIXT2pStooC8o/IkqvC+4JxJSPEahwIE8WbRogzEqfcujo1bfgMNEI1TzUL1vw
 hfdKXLtQ9VQ/2idwWzkm/wXckKzqxwu1dCKYlfsfVVeMe50cwCnXXnrAbgsH4KS6yfKbohFZS
 zKkanJ9/QFVVNwR162lXIaksonRrpFn/1TJUK+Nb/+4p+9BhcXURlj+OKvEVV9/FhQFscTmrM
 FvQ05lHScf8TEscV4kLvfL+hEuCpj2oeFE2
X-Spam-Score: -0.7 (/)
X-Debbugs-Envelope-To: 28969
Cc: 28969 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.7 (-)

Lars Ingebrigtsen <larsi@HIDDEN> writes:

> > | `*' and `?' when not surrounded by whitespace nor `\\=`' have no special
> > | significance for `dired-do-shell-command', and are passed through
> > | normally to the shell, but you must confirm first.
> >
> > However, the `y-or-n-p' prompts asks:
> >
> >   "Confirm--do you mean to use `*' as a wildcard? "
> >
> > and
> >
> >   "Confirm--do you mean to use `?' as a wildcard? "
> >
> > and you must answer with 'y' to let these not be treated as wildcards -
> > if you answer with 'n' as the docstring suggests, the operation is
> > aborted.  So, with other words, I think the questions must be inverted.
>
> Hm...  I don't quite follow you here...  It says it has no significance
> for the command, but just passes it through to the shell.  Where, of
> course, it has great significance.

The docstring is good I think, but the questions are bad IMHO.  First
it's not clear if "wildcard" is meant with respect to the command or to
the shell (this is answered by the doc, yes, still, you have to remember
it), and second, it depends on the concrete command string if the shell
would interpret * or ? as a wildcard at all.  In my example (in my
initial report), also the shell did not interpret it as wildcard, but I
had to say "y" to get it executed.  This is very confusing.  It would be
better to ask "confirm - pass literal `*' to the shell?" or so.

BTW, I had several use cases where * or ?, don't remember, was not
isolated, and I wanted to answer "n" to still get the substitution by
the command and was disappointed that Emacs just canceled.  Maybe one of
the suggested patches also improves that, I haven't checked yet.

Michael.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.
Merged 28969 35564. Request was from Noam Postavsky <npostavs@HIDDEN> to control <at> debbugs.gnu.org. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 14 Jul 2019 22:38:10 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Jul 14 18:38:10 2019
Received: from localhost ([127.0.0.1]:46180 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hmn8E-0008Ox-3w
	for submit <at> debbugs.gnu.org; Sun, 14 Jul 2019 18:38:10 -0400
Received: from mail-io1-f49.google.com ([209.85.166.49]:36274)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <npostavs@HIDDEN>)
 id 1hmn8C-0008Og-Jq; Sun, 14 Jul 2019 18:38:09 -0400
Received: by mail-io1-f49.google.com with SMTP id o9so31288190iom.3;
 Sun, 14 Jul 2019 15:38:08 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
 h=from:to:cc:subject:references:date:in-reply-to:message-id
 :user-agent:mime-version;
 bh=gVd6MgjY2f5nyK1eMu92+mm51aecyGVgcihQnyVBH9s=;
 b=Pf1m5KvElGbAx8EAnJsptkp1CUkypMB9eXRGL9eJwku7MUkZYwtKbYhpIMfYvXYc1+
 S4Rya/ffp47JttzQ4JT3ke6PTwQY94LeCzm/myt0YiTq11kGKEhWobkv7bqxlPiwK3X2
 a4PHFzA1rw8k1XUNMkQbudRz2gCy6OyCnFSeE1ylBWO0GZGw3poAhphDuFuq7/m/d2gO
 z6Fz25jiEXS820Ty3guuaeVSEzDP2NX77RrOtanDerS5D16gbw1XeoHxCsJedyHKeank
 G2GMiRKedwQEkMZR7qjM59ItTgz1J9+ida0hb8S6V9nOs3RDEwfEOGPafr45PmSo23AG
 /MQA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to
 :message-id:user-agent:mime-version;
 bh=gVd6MgjY2f5nyK1eMu92+mm51aecyGVgcihQnyVBH9s=;
 b=X8JnenhMP/JMAQ8vAWew0NH8FWwy2tdwJZKLq9zbGEJqddhsQ01WnDaZkgWr+tCyMg
 6Afo06zBBRZkN4FutfoNwCz5Z0lMh8bvldFFfvKAmj7dsvXECkSQjDrnW02gqjoejRif
 LTJFwVSaknV2KFkf3eqr4bk+ZwrvqnOvvh4L3izaim2AFp5Y5EA/8V7k2UCBOr5w/Sdz
 xwuvstP8HLKXljLYRrn7CHWFWr9o4PvRbxb6C1pyhGxT1b0sPwpnM2Nl9MKBAUTfyMrA
 Ua8wJm6D9Qd1YZanq6N++4q9zuXj6yYt9F7brtAtNIHcODitg2uEhOb6R+H3CvbKGmMj
 vF6Q==
X-Gm-Message-State: APjAAAXH+uxLZr1NYjBjFHZPhDsFe4NfQ7tDhzAGR0jiKFvyb6UA5IE4
 OL+Hwe4J1Q1mHTZ85K2ucVW7YNOs
X-Google-Smtp-Source: APXvYqwW6ALUciZbBZWaPhvGURlWP+R76RkGbwM/uPJseDSbiIxh3es1VkpHHXXC5+/sBHzAxAgT9A==
X-Received: by 2002:a02:cb4b:: with SMTP id k11mr24103469jap.109.1563143882804; 
 Sun, 14 Jul 2019 15:38:02 -0700 (PDT)
Received: from minid (cbl-45-2-119-34.yyz.frontiernetworks.ca. [45.2.119.34])
 by smtp.gmail.com with ESMTPSA id
 y20sm12797465iol.34.2019.07.14.15.38.02
 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256);
 Sun, 14 Jul 2019 15:38:02 -0700 (PDT)
From: Noam Postavsky <npostavs@HIDDEN>
To: Lars Ingebrigtsen <larsi@HIDDEN>
Subject: Re: bug#28969: 27.0.50;
 dired: Confirmation prompt for wildcard not surrounded by whitespace
References: <87she833e1.fsf@HIDDEN> <877e8kwbsn.fsf@HIDDEN>
Date: Sun, 14 Jul 2019 18:38:01 -0400
In-Reply-To: <877e8kwbsn.fsf@HIDDEN> (Lars Ingebrigtsen's message of
 "Sun, 14 Jul 2019 23:23:20 +0200")
Message-ID: <871rys1bue.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2.90 (gnu/linux)
MIME-Version: 1.0
Content-Type: text/plain
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: Michael Heerdegen <michael_heerdegen@HIDDEN>, 28969 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

merge 28969 35564
quit

Lars Ingebrigtsen <larsi@HIDDEN> writes:

> Michael Heerdegen <michael_heerdegen@HIDDEN> writes:
>
>> the docstring of `dired-do-shell-command' says:
>>
>> | `*' and `?' when not surrounded by whitespace nor `\\=`' have no special
>> | significance for `dired-do-shell-command', and are passed through
>> | normally to the shell, but you must confirm first.
>>
>> However, the `y-or-n-p' prompts asks:
>>
>>   "Confirm--do you mean to use `*' as a wildcard? "
>>
>> and
>>
>>   "Confirm--do you mean to use `?' as a wildcard? "
>>
>> and you must answer with 'y' to let these not be treated as wildcards -
>> if you answer with 'n' as the docstring suggests, the operation is
>> aborted.  So, with other words, I think the questions must be inverted.
>
> Hm...  I don't quite follow you here...  It says it has no significance
> for the command, but just passes it through to the shell.  Where, of
> course, it has great significance.
>
> If you create the file 1-1, put "bar" in it, and say "! cat 1*1" on the
> file, after you've answered "y", you'll get a buffer with "bar bar" in
> it.
>
> So I think all this is correct?  Unless I'm misreading you.

The meaning of "wildcard" is a bit ambiguous in the prompt itself.
There is another report about this in #35564, and patches to greatly
enhance the prompt.

https://debbugs.gnu.org/35564#104




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at 28969 <at> debbugs.gnu.org:


Received: (at 28969) by debbugs.gnu.org; 14 Jul 2019 21:23:28 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Jul 14 17:23:28 2019
Received: from localhost ([127.0.0.1]:46107 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1hmlxw-00007j-Gt
	for submit <at> debbugs.gnu.org; Sun, 14 Jul 2019 17:23:28 -0400
Received: from quimby.gnus.org ([80.91.231.51]:58632)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <larsi@HIDDEN>) id 1hmlxu-00007a-J2
 for 28969 <at> debbugs.gnu.org; Sun, 14 Jul 2019 17:23:27 -0400
Received: from cm-84.212.202.86.getinternet.no ([84.212.202.86] helo=marnie)
 by quimby.gnus.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.89) (envelope-from <larsi@HIDDEN>)
 id 1hmlxo-0006Ff-Os; Sun, 14 Jul 2019 23:23:23 +0200
From: Lars Ingebrigtsen <larsi@HIDDEN>
To: Michael Heerdegen <michael_heerdegen@HIDDEN>
Subject: Re: bug#28969: 27.0.50; dired: Confirmation prompt for wildcard not
 surrounded by whitespace
References: <87she833e1.fsf@HIDDEN>
Date: Sun, 14 Jul 2019 23:23:20 +0200
In-Reply-To: <87she833e1.fsf@HIDDEN> (Michael Heerdegen's message of "Tue, 24
 Oct 2017 18:40:54 +0200")
Message-ID: <877e8kwbsn.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)
MIME-Version: 1.0
Content-Type: text/plain
X-Spam-Report: Spam detection software, running on the system "quimby.gnus.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 @@CONTACT_ADDRESS@@ for details.
 Content preview:  Michael Heerdegen <michael_heerdegen@HIDDEN> writes: > the
 docstring of `dired-do-shell-command' says: > > | `*' and `?' when not
 surrounded
 by whitespace nor `\\=`' have no special > | significance for
 `dired-do-shell-command', and are passed through > [...] 
 Content analysis details:   (-2.9 points, 5.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -1.0 ALL_TRUSTED            Passed through trusted hosts only via SMTP
 -1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%
 [score: 0.0000]
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 28969
Cc: 28969 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Michael Heerdegen <michael_heerdegen@HIDDEN> writes:

> the docstring of `dired-do-shell-command' says:
>
> | `*' and `?' when not surrounded by whitespace nor `\\=`' have no special
> | significance for `dired-do-shell-command', and are passed through
> | normally to the shell, but you must confirm first.
>
> However, the `y-or-n-p' prompts asks:
>
>   "Confirm--do you mean to use `*' as a wildcard? "
>
> and
>
>   "Confirm--do you mean to use `?' as a wildcard? "
>
> and you must answer with 'y' to let these not be treated as wildcards -
> if you answer with 'n' as the docstring suggests, the operation is
> aborted.  So, with other words, I think the questions must be inverted.

Hm...  I don't quite follow you here...  It says it has no significance
for the command, but just passes it through to the shell.  Where, of
course, it has great significance.

If you create the file 1-1, put "bar" in it, and say "! cat 1*1" on the
file, after you've answered "y", you'll get a buffer with "bar bar" in
it.

So I think all this is correct?  Unless I'm misreading you.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 24 Oct 2017 16:41:17 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Oct 24 12:41:17 2017
Received: from localhost ([127.0.0.1]:60102 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1e72GT-00030O-4n
	for submit <at> debbugs.gnu.org; Tue, 24 Oct 2017 12:41:17 -0400
Received: from eggs.gnu.org ([208.118.235.92]:33128)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1e72GS-000305-CF
 for submit <at> debbugs.gnu.org; Tue, 24 Oct 2017 12:41:16 -0400
Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1e72GJ-0000uD-3j
 for submit <at> debbugs.gnu.org; Tue, 24 Oct 2017 12:41:11 -0400
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org
X-Spam-Level: 
X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM
 autolearn=disabled version=3.3.2
Received: from lists.gnu.org ([2001:4830:134:3::11]:50337)
 by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
 (Exim 4.71) (envelope-from <michael_heerdegen@HIDDEN>)
 id 1e72GI-0000u5-WF
 for submit <at> debbugs.gnu.org; Tue, 24 Oct 2017 12:41:07 -0400
Received: from eggs.gnu.org ([2001:4830:134:3::10]:52314)
 by lists.gnu.org with esmtp (Exim 4.71)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1e72GH-0001a5-K8
 for bug-gnu-emacs@HIDDEN; Tue, 24 Oct 2017 12:41:06 -0400
Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)
 (envelope-from <michael_heerdegen@HIDDEN>) id 1e72GD-0000q9-N3
 for bug-gnu-emacs@HIDDEN; Tue, 24 Oct 2017 12:41:05 -0400
Received: from mout.web.de ([212.227.15.3]:56905)
 by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16)
 (Exim 4.71) (envelope-from <michael_heerdegen@HIDDEN>)
 id 1e72GD-0000og-Du
 for bug-gnu-emacs@HIDDEN; Tue, 24 Oct 2017 12:41:01 -0400
Received: from drachen.dragon ([94.217.121.116]) by smtp.web.de (mrweb002
 [213.165.67.108]) with ESMTPSA (Nemesis) id 0MXYnu-1djIcB3wVa-00WZpP for
 <bug-gnu-emacs@HIDDEN>; Tue, 24 Oct 2017 18:40:58 +0200
From: Michael Heerdegen <michael_heerdegen@HIDDEN>
To: bug-gnu-emacs@HIDDEN
Subject: 27.0.50;
 dired: Confirmation prompt for wildcard not surrounded by whitespace
Date: Tue, 24 Oct 2017 18:40:54 +0200
Message-ID: <87she833e1.fsf@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain
X-Provags-ID: V03:K0:IwFA1eLhsFTprt/NEfDQBNEWHA8rY61ww6zvAJ1scHj3HNN5HO2
 3la71q8b8zfRcEH+V3odrUrLmxPR2fN3PnfGyzAxCQg5QwFKuZ0PrtT1KhQb4R6/QenoD1Z
 3ck7XbOASHh7xrVMTBLIJmHHoJFYcOGLJrMRAgBeo/WXkz/R2/ir3tOE657YeYraEqf1AfY
 L0jryyPDYV6MFDc9Dwufg==
X-UI-Out-Filterresults: notjunk:1;V01:K0:7Agk8B5ZPJA=:V+DzGrO+LgaQJzRgJvmBa1
 r2M9pdWYJmPvLMcKErBoHHBeGJ5m9WHX3jKU24wfsqpT7y/WrznkN+PWsWC2jb/xuegOYLXv7
 QrtzSkFaIFPbdAltO3N1C2fv95XZWoFEN9ZgjJ0Jw+86jxc0LMCU41TyjHlvJS6xmGlBK3eYP
 slELC/vU5BXZhTcsYPR6UMXoY7nGDnCRzBGpaS3KmqMjrP+Tat/lYpwO+Y9ujKktRmnUdE/Nk
 QTEL9L8jXF/WMsqAgP8ZGcqQZ86nWrgpk9CagnAnLy5LDgfeti+yv840FBNeTZaTo7yJECzLc
 IkHRsoCG6ki0pAvEbTBkYIySI7FOTTayaCA/DRg2SSsHm62J4YhDz+I27KUGIXk+ovwqNvWtY
 vBql/2jFbWd8ijYQ0UR04CDctCdN7sDibHqeSqMGqYtxYwkkpUTgwamGqleCcosIL+fZohtdx
 kn6a4m3t7ruYvi8+Y+vMbwimwBbCJ30pgWQGaXKLCNsRJGrCv8SHNMtaS4YDSivIKSyYEfFVl
 z3n4kx7ojvHN9lhvdjrdw54AoaPC81Edofr44+CcrwebhB7QgrAmFZto3NkOM30WZnYtTR2tt
 m7xoRWvufdyt3OhsVvdfLiiacX97AKrbKK9cBOlF9XgZqIHYdnNt3/amq0cDUASJs3zRF6aPO
 RY17QT22vtCCL7Sw1go2diLQEPyMTWzg1N3NXLXG0owqqdst6cMICT+9PcOdBfOUgtfIEKJbW
 84YdvJ9yaYy7Mpl5lEqUGX22fG7GWp8jt6WdD+GxdWMfJB8zpsFSKhJXrHumvfblWMraCgMcr
 6BpG9TGe6QWMmD2KopEcW5FBjbQhlqnu1wGoPwz0z1GI0yhMIw=
X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic]
 [fuzzy]
X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x
X-Received-From: 2001:4830:134:3::11
X-Spam-Score: -4.1 (----)
X-Debbugs-Envelope-To: submit
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -4.1 (----)


Hello,

the docstring of `dired-do-shell-command' says:

| `*' and `?' when not surrounded by whitespace nor `\\=`' have no special
| significance for `dired-do-shell-command', and are passed through
| normally to the shell, but you must confirm first.

However, the `y-or-n-p' prompts asks:

  "Confirm--do you mean to use `*' as a wildcard? "

and

  "Confirm--do you mean to use `?' as a wildcard? "

and you must answer with 'y' to let these not be treated as wildcards -
if you answer with 'n' as the docstring suggests, the operation is
aborted.  So, with other words, I think the questions must be inverted.

A simple test case is to create a symlink 1*1 -> xedit and to try to
open some textfile with "1*1".


TIA,

Michael.


In GNU Emacs 27.0.50 (build 3, x86_64-pc-linux-gnu, GTK+ Version 3.22.24)
 of 2017-10-19 built on drachen
Repository revision: 658853aebb0ae2ee243276e04a7672fa7525ec5c
Windowing system distributor 'The X.Org Foundation', version 11.0.11905000
System Description:	Debian GNU/Linux testing (buster)





Acknowledgement sent to Michael Heerdegen <michael_heerdegen@HIDDEN>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs@HIDDEN. Full text available.
Report forwarded to bug-gnu-emacs@HIDDEN:
bug#28969; Package emacs. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Mon, 25 Nov 2019 12:00:02 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.