diff options
| author | F. Jason Park | 2023-07-07 21:27:03 -0700 |
|---|---|---|
| committer | F. Jason Park | 2023-07-13 18:45:31 -0700 |
| commit | b95bb644ec2b9bb9b0aa3ba2a88c828c3c33705a (patch) | |
| tree | cfb4f0215dfe9067f7cb1781af9b3a8e4c0781a5 | |
| parent | 4e8d579f3da93f3f4cb5ae52c179623e75957ee4 (diff) | |
| download | emacs-b95bb644ec2b9bb9b0aa3ba2a88c828c3c33705a.tar.gz emacs-b95bb644ec2b9bb9b0aa3ba2a88c828c3c33705a.zip | |
Fix command-line parsing regression in erc-cmd-DCC
* lisp/erc/erc-compat.el (erc-compat--28-split-string-shell-command,
erc-compat--split-string-shell-command): Remove unused function and
macro.
* lisp/erc/erc-dcc.el (erc-cmd-DCC): Use own arg-parsing function.
* lisp/erc/erc.el (erc--shell-parse-regexp,
erc--split-string-shell-cmd): New regexp constant and arg-parsing
function based on those in shell.el.
* test/lisp/erc/erc-dcc-tests.el
(erc-dcc-tests--erc-dcc-do-GET-command): Accept new `nuh' argument
representing message source/sender.
(erc-dcc-do-GET-command): Add tests for regression involving pipe
character.
* test/lisp/erc/erc-tests.el (erc--split-string-shell-cmd): New test.
(Bug#62444)
Thanks to Fernando de Morais for reporting this bug.
| -rw-r--r-- | lisp/erc/erc-compat.el | 21 | ||||
| -rw-r--r-- | lisp/erc/erc-dcc.el | 2 | ||||
| -rw-r--r-- | lisp/erc/erc.el | 36 | ||||
| -rw-r--r-- | test/lisp/erc/erc-dcc-tests.el | 23 | ||||
| -rw-r--r-- | test/lisp/erc/erc-tests.el | 46 |
5 files changed, 99 insertions, 29 deletions
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index 29892b78a39..f451aaee754 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el | |||
| @@ -445,27 +445,6 @@ If START or END is negative, it counts from the end." | |||
| 445 | existing)))))) | 445 | existing)))))) |
| 446 | 446 | ||
| 447 | 447 | ||
| 448 | ;;;; Misc 28.1 | ||
| 449 | |||
| 450 | (defvar comint-file-name-quote-list) | ||
| 451 | (defvar shell-file-name-quote-list) | ||
| 452 | (declare-function shell--parse-pcomplete-arguments "shell" nil) | ||
| 453 | |||
| 454 | (defun erc-compat--28-split-string-shell-command (string) | ||
| 455 | (require 'comint) | ||
| 456 | (require 'shell) | ||
| 457 | (with-temp-buffer | ||
| 458 | (insert string) | ||
| 459 | (let ((comint-file-name-quote-list shell-file-name-quote-list)) | ||
| 460 | (car (shell--parse-pcomplete-arguments))))) | ||
| 461 | |||
| 462 | (defmacro erc-compat--split-string-shell-command (string) | ||
| 463 | ;; Autoloaded in Emacs 28. | ||
| 464 | (list (if (fboundp 'split-string-shell-command) | ||
| 465 | 'split-string-shell-command | ||
| 466 | 'erc-compat--28-split-string-shell-command) | ||
| 467 | string)) | ||
| 468 | |||
| 469 | (provide 'erc-compat) | 448 | (provide 'erc-compat) |
| 470 | 449 | ||
| 471 | ;;; erc-compat.el ends here | 450 | ;;; erc-compat.el ends here |
diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el index cc2dcc9a788..f05ae41fc51 100644 --- a/lisp/erc/erc-dcc.el +++ b/lisp/erc/erc-dcc.el | |||
| @@ -399,7 +399,7 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc." | |||
| 399 | (if compat-args | 399 | (if compat-args |
| 400 | (setq cmd line | 400 | (setq cmd line |
| 401 | args compat-args) | 401 | args compat-args) |
| 402 | (setq args (delete "" (erc-compat--split-string-shell-command line)) | 402 | (setq args (delete "" (erc--split-string-shell-cmd line)) |
| 403 | cmd (pop args))) | 403 | cmd (pop args))) |
| 404 | (let ((fn (intern-soft (concat "erc-dcc-do-" (upcase cmd) "-command")))) | 404 | (let ((fn (intern-soft (concat "erc-dcc-do-" (upcase cmd) "-command")))) |
| 405 | (if fn | 405 | (if fn |
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index e23185934f7..1786c8924bd 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el | |||
| @@ -3213,6 +3213,42 @@ this function from interpreting the line as a command." | |||
| 3213 | (erc-display-message nil 'error (current-buffer) 'no-target) | 3213 | (erc-display-message nil 'error (current-buffer) 'no-target) |
| 3214 | nil))))) | 3214 | nil))))) |
| 3215 | 3215 | ||
| 3216 | (defconst erc--shell-parse-regexp | ||
| 3217 | (rx (or (+ (not (any ?\s ?\t ?\n ?\\ ?\" ?' ?\;))) | ||
| 3218 | (: ?' (group (* (not ?'))) (? ?')) | ||
| 3219 | (: ?\" (group (* (or (not (any ?\" ?\\)) (: ?\\ nonl)))) (? ?\")) | ||
| 3220 | (: ?\\ (group (? (or nonl ?\n))))))) | ||
| 3221 | |||
| 3222 | (defun erc--split-string-shell-cmd (string) | ||
| 3223 | "Parse whitespace-separated arguments in STRING." | ||
| 3224 | ;; From `shell--parse-pcomplete-arguments' and friends. Quirk: | ||
| 3225 | ;; backslash-escaped characters appearing within spans of double | ||
| 3226 | ;; quotes are unescaped. | ||
| 3227 | (with-temp-buffer | ||
| 3228 | (insert string) | ||
| 3229 | (let ((end (point)) | ||
| 3230 | args) | ||
| 3231 | (goto-char (point-min)) | ||
| 3232 | (while (and (skip-chars-forward " \t") (< (point) end)) | ||
| 3233 | (let (arg) | ||
| 3234 | (while (looking-at erc--shell-parse-regexp) | ||
| 3235 | (goto-char (match-end 0)) | ||
| 3236 | (cond ((match-beginning 3) ; backslash escape | ||
| 3237 | (push (if (= (match-beginning 3) (match-end 3)) | ||
| 3238 | "\\" | ||
| 3239 | (match-string 3)) | ||
| 3240 | arg)) | ||
| 3241 | ((match-beginning 2) ; double quote | ||
| 3242 | (push (replace-regexp-in-string (rx ?\\ (group nonl)) | ||
| 3243 | "\\1" (match-string 2)) | ||
| 3244 | arg)) | ||
| 3245 | ((match-beginning 1) ; single quote | ||
| 3246 | (push (match-string 1) arg)) | ||
| 3247 | (t (push (match-string 0) arg)))) | ||
| 3248 | (push (string-join (nreverse arg)) args))) | ||
| 3249 | (nreverse args)))) | ||
| 3250 | |||
| 3251 | |||
| 3216 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 3252 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 3217 | ;; Input commands handlers | 3253 | ;; Input commands handlers |
| 3218 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | 3254 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
diff --git a/test/lisp/erc/erc-dcc-tests.el b/test/lisp/erc/erc-dcc-tests.el index f02ddf228a2..a750c96c80f 100644 --- a/test/lisp/erc/erc-dcc-tests.el +++ b/test/lisp/erc/erc-dcc-tests.el | |||
| @@ -99,10 +99,11 @@ | |||
| 99 | (ert-deftest erc-dcc-handle-ctcp-send--turbo () | 99 | (ert-deftest erc-dcc-handle-ctcp-send--turbo () |
| 100 | (erc-dcc-tests--dcc-handle-ctcp-send t)) | 100 | (erc-dcc-tests--dcc-handle-ctcp-send t)) |
| 101 | 101 | ||
| 102 | (defun erc-dcc-tests--erc-dcc-do-GET-command (file &optional sep) | 102 | (defun erc-dcc-tests--erc-dcc-do-GET-command (file &optional sep nuh) |
| 103 | (unless nuh (setq nuh "tester!~tester@fake.irc")) | ||
| 103 | (with-temp-buffer | 104 | (with-temp-buffer |
| 104 | (let* ((proc (start-process "fake" (current-buffer) "sleep" "10")) | 105 | (let* ((proc (start-process "fake" (current-buffer) "sleep" "10")) |
| 105 | (elt (list :nick "tester!~tester@fake.irc" | 106 | (elt (list :nick nuh |
| 106 | :type 'GET | 107 | :type 'GET |
| 107 | :peer nil | 108 | :peer nil |
| 108 | :parent proc | 109 | :parent proc |
| @@ -110,6 +111,7 @@ | |||
| 110 | :port "9899" | 111 | :port "9899" |
| 111 | :file file | 112 | :file file |
| 112 | :size 1405135128)) | 113 | :size 1405135128)) |
| 114 | (nic (erc-extract-nick nuh)) | ||
| 113 | (erc-dcc-list (list elt)) | 115 | (erc-dcc-list (list elt)) |
| 114 | ;; | 116 | ;; |
| 115 | erc-accidental-paste-threshold-seconds | 117 | erc-accidental-paste-threshold-seconds |
| @@ -130,7 +132,7 @@ | |||
| 130 | (ert-info ("No turbo") | 132 | (ert-info ("No turbo") |
| 131 | (should-not (plist-member elt :turbo)) | 133 | (should-not (plist-member elt :turbo)) |
| 132 | (goto-char erc-input-marker) | 134 | (goto-char erc-input-marker) |
| 133 | (insert "/dcc GET tester " (or sep "") (prin1-to-string file)) | 135 | (insert "/dcc GET " nic " " (or sep "") (prin1-to-string file)) |
| 134 | (erc-send-current-line) | 136 | (erc-send-current-line) |
| 135 | (should-not (plist-member (car erc-dcc-list) :turbo)) | 137 | (should-not (plist-member (car erc-dcc-list) :turbo)) |
| 136 | (should (equal (pop calls) (list elt file proc)))) | 138 | (should (equal (pop calls) (list elt file proc)))) |
| @@ -138,7 +140,7 @@ | |||
| 138 | (ert-info ("Arg turbo in pos 2") | 140 | (ert-info ("Arg turbo in pos 2") |
| 139 | (should-not (plist-member elt :turbo)) | 141 | (should-not (plist-member elt :turbo)) |
| 140 | (goto-char erc-input-marker) | 142 | (goto-char erc-input-marker) |
| 141 | (insert "/dcc GET -t tester " (or sep "") (prin1-to-string file)) | 143 | (insert "/dcc GET -t " nic " " (or sep "") (prin1-to-string file)) |
| 142 | (erc-send-current-line) | 144 | (erc-send-current-line) |
| 143 | (should (eq t (plist-get (car erc-dcc-list) :turbo))) | 145 | (should (eq t (plist-get (car erc-dcc-list) :turbo))) |
| 144 | (should (equal (pop calls) (list elt file proc)))) | 146 | (should (equal (pop calls) (list elt file proc)))) |
| @@ -147,7 +149,7 @@ | |||
| 147 | (setq elt (plist-put elt :turbo nil) | 149 | (setq elt (plist-put elt :turbo nil) |
| 148 | erc-dcc-list (list elt)) | 150 | erc-dcc-list (list elt)) |
| 149 | (goto-char erc-input-marker) | 151 | (goto-char erc-input-marker) |
| 150 | (insert "/dcc GET tester -t " (or sep "") (prin1-to-string file)) | 152 | (insert "/dcc GET " nic " -t " (or sep "") (prin1-to-string file)) |
| 151 | (erc-send-current-line) | 153 | (erc-send-current-line) |
| 152 | (should (eq t (plist-get (car erc-dcc-list) :turbo))) | 154 | (should (eq t (plist-get (car erc-dcc-list) :turbo))) |
| 153 | (should (equal (pop calls) (list elt file proc)))) | 155 | (should (equal (pop calls) (list elt file proc)))) |
| @@ -156,7 +158,7 @@ | |||
| 156 | (setq elt (plist-put elt :turbo nil) | 158 | (setq elt (plist-put elt :turbo nil) |
| 157 | erc-dcc-list (list elt)) | 159 | erc-dcc-list (list elt)) |
| 158 | (goto-char erc-input-marker) | 160 | (goto-char erc-input-marker) |
| 159 | (insert "/dcc GET tester " (prin1-to-string file) " -t" (or sep "")) | 161 | (insert "/dcc GET " nic " " (prin1-to-string file) " -t" (or sep "")) |
| 160 | (erc-send-current-line) | 162 | (erc-send-current-line) |
| 161 | (should (eq (if sep nil t) (plist-get (car erc-dcc-list) :turbo))) | 163 | (should (eq (if sep nil t) (plist-get (car erc-dcc-list) :turbo))) |
| 162 | (should (equal (pop calls) (if sep nil (list elt file proc))))))))) | 164 | (should (equal (pop calls) (if sep nil (list elt file proc))))))))) |
| @@ -165,7 +167,14 @@ | |||
| 165 | (erc-dcc-tests--erc-dcc-do-GET-command "foo.bin") | 167 | (erc-dcc-tests--erc-dcc-do-GET-command "foo.bin") |
| 166 | (erc-dcc-tests--erc-dcc-do-GET-command "foo - file.bin") | 168 | (erc-dcc-tests--erc-dcc-do-GET-command "foo - file.bin") |
| 167 | (erc-dcc-tests--erc-dcc-do-GET-command "foo -t file.bin") | 169 | (erc-dcc-tests--erc-dcc-do-GET-command "foo -t file.bin") |
| 168 | (erc-dcc-tests--erc-dcc-do-GET-command "-t" "-- ")) | 170 | (erc-dcc-tests--erc-dcc-do-GET-command "-t" "-- ") |
| 171 | |||
| 172 | ;; Regression involving pipe character in nickname. | ||
| 173 | (let ((nuh "test|r!~test|r@fake.irc")) | ||
| 174 | (erc-dcc-tests--erc-dcc-do-GET-command "foo.bin" nil nuh) | ||
| 175 | (erc-dcc-tests--erc-dcc-do-GET-command "foo - file.bin" nil nuh) | ||
| 176 | (erc-dcc-tests--erc-dcc-do-GET-command "foo -t file.bin" nil nuh) | ||
| 177 | (erc-dcc-tests--erc-dcc-do-GET-command "-t" "-- " nuh))) | ||
| 169 | 178 | ||
| 170 | (defun erc-dcc-tests--pcomplete-common (test-fn &optional file) | 179 | (defun erc-dcc-tests--pcomplete-common (test-fn &optional file) |
| 171 | (with-current-buffer (get-buffer-create "*erc-dcc-do-GET-command*") | 180 | (with-current-buffer (get-buffer-create "*erc-dcc-do-GET-command*") |
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 80c7c708fc5..f5c900df408 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el | |||
| @@ -1218,6 +1218,52 @@ | |||
| 1218 | 1218 | ||
| 1219 | (should-not calls)))))) | 1219 | (should-not calls)))))) |
| 1220 | 1220 | ||
| 1221 | (ert-deftest erc--split-string-shell-cmd () | ||
| 1222 | |||
| 1223 | ;; Leading and trailing space | ||
| 1224 | (should (equal (erc--split-string-shell-cmd "1 2 3") '("1" "2" "3"))) | ||
| 1225 | (should (equal (erc--split-string-shell-cmd " 1 2 3 ") '("1" "2" "3"))) | ||
| 1226 | |||
| 1227 | ;; Empty string | ||
| 1228 | (should (equal (erc--split-string-shell-cmd "\"\"") '(""))) | ||
| 1229 | (should (equal (erc--split-string-shell-cmd " \"\" ") '(""))) | ||
| 1230 | (should (equal (erc--split-string-shell-cmd "1 \"\"") '("1" ""))) | ||
| 1231 | (should (equal (erc--split-string-shell-cmd "1 \"\" ") '("1" ""))) | ||
| 1232 | (should (equal (erc--split-string-shell-cmd "\"\" 1") '("" "1"))) | ||
| 1233 | (should (equal (erc--split-string-shell-cmd " \"\" 1") '("" "1"))) | ||
| 1234 | |||
| 1235 | (should (equal (erc--split-string-shell-cmd "''") '(""))) | ||
| 1236 | (should (equal (erc--split-string-shell-cmd " '' ") '(""))) | ||
| 1237 | (should (equal (erc--split-string-shell-cmd "1 ''") '("1" ""))) | ||
| 1238 | (should (equal (erc--split-string-shell-cmd "1 '' ") '("1" ""))) | ||
| 1239 | (should (equal (erc--split-string-shell-cmd "'' 1") '("" "1"))) | ||
| 1240 | (should (equal (erc--split-string-shell-cmd " '' 1") '("" "1"))) | ||
| 1241 | |||
| 1242 | ;; Backslash | ||
| 1243 | (should (equal (erc--split-string-shell-cmd "\\ ") '(" "))) | ||
| 1244 | (should (equal (erc--split-string-shell-cmd " \\ ") '(" "))) | ||
| 1245 | (should (equal (erc--split-string-shell-cmd "1\\ ") '("1 "))) | ||
| 1246 | (should (equal (erc--split-string-shell-cmd "1\\ 2") '("1 2"))) | ||
| 1247 | |||
| 1248 | ;; Embedded | ||
| 1249 | (should (equal (erc--split-string-shell-cmd "\"\\\"\"") '("\""))) | ||
| 1250 | (should (equal (erc--split-string-shell-cmd "1 \"2 \\\" \\\" 3\"") | ||
| 1251 | '("1" "2 \" \" 3"))) | ||
| 1252 | (should (equal (erc--split-string-shell-cmd "1 \"2 ' ' 3\"") | ||
| 1253 | '("1" "2 ' ' 3"))) | ||
| 1254 | (should (equal (erc--split-string-shell-cmd "1 '2 \" \" 3'") | ||
| 1255 | '("1" "2 \" \" 3"))) | ||
| 1256 | (should (equal (erc--split-string-shell-cmd "1 '2 \\ 3'") | ||
| 1257 | '("1" "2 \\ 3"))) | ||
| 1258 | (should (equal (erc--split-string-shell-cmd "1 \"2 \\\\ 3\"") | ||
| 1259 | '("1" "2 \\ 3"))) ; see comment re ^ | ||
| 1260 | |||
| 1261 | ;; Realistic | ||
| 1262 | (should (equal (erc--split-string-shell-cmd "GET bob \"my file.txt\"") | ||
| 1263 | '("GET" "bob" "my file.txt"))) | ||
| 1264 | (should (equal (erc--split-string-shell-cmd "GET EXAMPLE|bob \"my file.txt\"") | ||
| 1265 | '("GET" "EXAMPLE|bob" "my file.txt")))) ; regression | ||
| 1266 | |||
| 1221 | 1267 | ||
| 1222 | ;; The behavior of `erc-pre-send-functions' differs between versions | 1268 | ;; The behavior of `erc-pre-send-functions' differs between versions |
| 1223 | ;; in how hook members see and influence a trailing newline that's | 1269 | ;; in how hook members see and influence a trailing newline that's |