From 64484ad751bfebc638afaf87b2cc8be63c550ba8 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sun, 27 Feb 2022 21:04:30 -0800 Subject: [PATCH 3/5] 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. --- lisp/eshell/esh-arg.el | 24 +++++++++++++++ lisp/eshell/esh-var.el | 27 +++++++++++------ test/lisp/eshell/esh-var-tests.el | 49 +++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 9 deletions(-) diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el index 1a2f2a57e8..e19481c4ba 100644 --- a/lisp/eshell/esh-arg.el +++ b/lisp/eshell/esh-arg.el @@ -354,6 +354,30 @@ eshell-parse-double-quote (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 `#'. diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index ee3ffbc647..24fdbde3cf 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -440,18 +440,16 @@ eshell-parse-variable-ref (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 @@ eshell-parse-variable-ref `(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 @@ eshell-parse-variable-ref (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 8d803e5ca4..7ec6a97519 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 @@ esh-var-test/interp-concat-cmd-external (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 \"$\"") "hi"))) + ;; Built-in variables -- 2.25.1