aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2023-07-07 21:27:03 -0700
committerF. Jason Park2023-07-13 18:45:31 -0700
commitb95bb644ec2b9bb9b0aa3ba2a88c828c3c33705a (patch)
treecfb4f0215dfe9067f7cb1781af9b3a8e4c0781a5
parent4e8d579f3da93f3f4cb5ae52c179623e75957ee4 (diff)
downloademacs-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.el21
-rw-r--r--lisp/erc/erc-dcc.el2
-rw-r--r--lisp/erc/erc.el36
-rw-r--r--test/lisp/erc/erc-dcc-tests.el23
-rw-r--r--test/lisp/erc/erc-tests.el46
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