summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Porter <jporterbugs@gmail.com>2022-02-27 21:04:30 -0800
committerLars Ingebrigtsen <larsi@gnus.org>2022-03-03 14:59:33 +0100
commitcccee7e840102488e01f9bb7c2220392d358f4f0 (patch)
tree1077dd929cf7908fd8c76b63d135b6fe1834bb60
parentae1acb601764009fc2551819f9193aa6e9441be4 (diff)
downloademacs-cccee7e840102488e01f9bb7c2220392d358f4f0.tar.gz
emacs-cccee7e840102488e01f9bb7c2220392d358f4f0.tar.bz2
emacs-cccee7e840102488e01f9bb7c2220392d358f4f0.zip
Fix Eshell dollar interpolation inside of double-quotes
For example, echo "${echo hi}" previously tried to run the program named 'echo hi', instead of 'echo' with the argument 'hi'. * lisp/eshell/esh-arg.el (eshell-parse-inner-double-quote): New function. * lisp/eshell/esh-var.el (eshell-parse-variable-ref): Support parsing when wrapped in double-quiotes. * test/lisp/eshell/esh-var-tests.el (esh-var-test/interp-var) (esh-var-test/interp-quoted-var) (esh-var-test/interp-quoted-var-concat) (esh-var-test/quoted-interp-var) (esh-var-test/quoted-interp-quoted-var) (esh-var-test/quoted-interp-lisp, esh-var-test/quoted-interp-cmd) (esh-var-test/quoted-interp-temp-cmd): New tests.
-rw-r--r--lisp/eshell/esh-arg.el24
-rw-r--r--lisp/eshell/esh-var.el27
-rw-r--r--test/lisp/eshell/esh-var-tests.el49
3 files changed, 91 insertions, 9 deletions
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index 1a2f2a57e8e..e19481c4ba9 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -354,6 +354,30 @@ after are both returned."
(list 'eshell-escape-arg arg))))
(goto-char (1+ end)))))))
+(defun eshell-parse-inner-double-quote (bound)
+ "Parse the inner part of a double quoted string.
+The string to parse starts at point and ends at BOUND.
+
+If Eshell is currently parsing a quoted string and there are any
+backslash-escaped characters, this will return the unescaped
+string, updating point to BOUND. Otherwise, this returns nil and
+leaves point where it was."
+ (when eshell-current-quoted
+ (let (strings
+ (start (point))
+ (special-char
+ (rx-to-string
+ `(seq "\\" (group (any ,@eshell-special-chars-inside-quoting))))))
+ (while (re-search-forward special-char bound t)
+ (push (concat (buffer-substring start (match-beginning 0))
+ (match-string 1))
+ strings)
+ (setq start (match-end 0)))
+ (when strings
+ (push (buffer-substring start bound) strings)
+ (goto-char bound)
+ (apply #'concat (nreverse strings))))))
+
(defun eshell-parse-special-reference ()
"Parse a special syntax reference, of the form `#<args>'.
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index ee3ffbc6475..24fdbde3cfb 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -440,18 +440,16 @@ Possible options are:
(let ((end (eshell-find-delimiter ?\{ ?\})))
(if (not end)
(throw 'eshell-incomplete ?\{)
+ (forward-char)
(prog1
`(eshell-convert
(eshell-command-to-value
(eshell-as-subcommand
- ,(eshell-parse-command (cons (1+ (point)) end)))))
+ ,(let ((subcmd (or (eshell-parse-inner-double-quote end)
+ (cons (point) end)))
+ (eshell-current-quoted nil))
+ (eshell-parse-command subcmd)))))
(goto-char (1+ end))))))
- ((memq (char-after) '(?\' ?\"))
- (let ((name (if (eq (char-after) ?\')
- (eshell-parse-literal-quote)
- (eshell-parse-double-quote))))
- (if name
- `(eshell-get-variable ,(eval name) indices))))
((eq (char-after) ?\<)
(let ((end (eshell-find-delimiter ?\< ?\>)))
(if (not end)
@@ -463,7 +461,9 @@ Possible options are:
`(let ((eshell-current-handles
(eshell-create-handles ,temp 'overwrite)))
(progn
- (eshell-as-subcommand ,(eshell-parse-command cmd))
+ (eshell-as-subcommand
+ ,(let ((eshell-current-quoted nil))
+ (eshell-parse-command cmd)))
(ignore
(nconc eshell-this-command-hook
;; Quote this lambda; it will be evaluated
@@ -478,9 +478,18 @@ Possible options are:
(condition-case nil
`(eshell-command-to-value
(eshell-lisp-command
- ',(read (current-buffer))))
+ ',(read (or (eshell-parse-inner-double-quote (point-max))
+ (current-buffer)))))
(end-of-file
(throw 'eshell-incomplete ?\())))
+ ((looking-at (rx (or "'" "\"" "\\\"")))
+ (eshell-with-temp-command (or (eshell-parse-inner-double-quote (point-max))
+ (cons (point) (point-max)))
+ (let ((name (if (eq (char-after) ?\')
+ (eshell-parse-literal-quote)
+ (eshell-parse-double-quote))))
+ (when name
+ `(eshell-get-variable ,(eval name) indices)))))
((assoc (char-to-string (char-after))
eshell-variable-aliases-list)
(forward-char)
diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el
index 8d803e5ca49..7ec6a975198 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -37,6 +37,25 @@
;; Variable interpolation
+(ert-deftest esh-var-test/interp-var ()
+ "Interpolate variable"
+ (should (equal (eshell-test-command-result "echo $user-login-name")
+ user-login-name)))
+
+(ert-deftest esh-var-test/interp-quoted-var ()
+ "Interpolate quoted variable"
+ (should (equal (eshell-test-command-result "echo $'user-login-name'")
+ user-login-name))
+ (should (equal (eshell-test-command-result "echo $\"user-login-name\"")
+ user-login-name)))
+
+(ert-deftest esh-var-test/interp-quoted-var-concat ()
+ "Interpolate and concat quoted variable"
+ (should (equal (eshell-test-command-result "echo $'user-login-name'-foo")
+ (concat user-login-name "-foo")))
+ (should (equal (eshell-test-command-result "echo $\"user-login-name\"-foo")
+ (concat user-login-name "-foo"))))
+
(ert-deftest esh-var-test/interp-lisp ()
"Interpolate Lisp form evaluation"
(should (equal (eshell-test-command-result "+ $(+ 1 2) 3") 6)))
@@ -79,6 +98,36 @@
(eshell-command-result-p "echo ${echo hi}-${*echo there}"
"hi-there\n")))
+(ert-deftest esh-var-test/quoted-interp-var ()
+ "Interpolate variable inside double-quotes"
+ (should (equal (eshell-test-command-result "echo \"$user-login-name\"")
+ user-login-name)))
+
+(ert-deftest esh-var-test/quoted-interp-quoted-var ()
+ "Interpolate quoted variable inside double-quotes"
+ (should (equal (eshell-test-command-result
+ "echo \"hi, $'user-login-name'\"")
+ (concat "hi, " user-login-name)))
+ (should (equal (eshell-test-command-result
+ "echo \"hi, $\\\"user-login-name\\\"\"")
+ (concat "hi, " user-login-name))))
+
+(ert-deftest esh-var-test/quoted-interp-lisp ()
+ "Interpolate Lisp form evaluation inside double-quotes"
+ (should (equal (eshell-test-command-result
+ "echo \"hi $(concat \\\"the\\\" \\\"re\\\")\"")
+ "hi there")))
+
+(ert-deftest esh-var-test/quoted-interp-cmd ()
+ "Interpolate command result inside double-quotes"
+ (should (equal (eshell-test-command-result
+ "echo \"hi ${echo \\\"there\\\"}\"")
+ "hi there")))
+
+(ert-deftest esh-var-test/quoted-interp-temp-cmd ()
+ "Interpolate command result redirected to temp file inside double-quotes"
+ (should (equal (eshell-test-command-result "cat \"$<echo hi>\"") "hi")))
+
;; Built-in variables