From 2473256d7b660edc5c1525163b2e9917d6ebf18d Mon Sep 17 00:00:00 2001
From: Stefan Monnier <monnier@iro.umontreal.ca>
Date: Tue, 15 May 2012 12:58:35 -0400
Subject: [PATCH] Only handle ".." and '..' quoting in shell-mode. *
 lisp/shell.el (shell--unquote&requote-argument, shell--unquote-argument)
 (shell--requote-argument): New functions. (shell-completion-vars): Use them.
 (shell--parse-pcomplete-arguments): Rename from
 shell-parse-pcomplete-arguments. * lisp/comint.el (comint-word): Obey
 comint-file-name-quote-list.  Simplify. (comint--unquote&requote-argument):
 Don't handle ".." and '..' quoting. Obey comint-file-name-quote-list.

Fixes: debbugs:11466
---
 lisp/ChangeLog | 10 +++++++++
 lisp/comint.el | 38 +++++++++++++------------------
 lisp/shell.el  | 61 +++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 80 insertions(+), 29 deletions(-)

diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 865bdd7c6a3..de2cfc6f0ac 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,15 @@
 2012-05-15  Stefan Monnier  <monnier@iro.umontreal.ca>
 
+	Only handle ".." and '..' quoting in shell-mode (bug#11466).
+	* shell.el (shell--unquote&requote-argument, shell--unquote-argument)
+	(shell--requote-argument): New functions.
+	(shell-completion-vars): Use them.
+	(shell--parse-pcomplete-arguments): Rename from
+	shell-parse-pcomplete-arguments.
+	* comint.el (comint-word): Obey comint-file-name-quote-list.  Simplify.
+	(comint--unquote&requote-argument): Don't handle ".." and '..' quoting.
+	Obey comint-file-name-quote-list.
+
 	* emacs-lisp/smie.el (smie-indent--bolp-1): New function.
 	(smie-indent-keyword): Use it.
 
diff --git a/lisp/comint.el b/lisp/comint.el
index 43e42c87be7..db0f5b8b460 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -2968,19 +2968,20 @@ This is a good thing to set in mode hooks.")
   "Return the word of WORD-CHARS at point, or nil if none is found.
 Word constituents are considered to be those in WORD-CHARS, which is like the
 inside of a \"[...]\" (see `skip-chars-forward'), plus all non-ASCII characters."
+  ;; FIXME: Need to handle "..." and '...' quoting in shell.el!
+  ;; This should be combined with pomplete-parsing somehow.
   (save-excursion
     (let ((here (point))
 	  giveup)
       (while (not giveup)
 	(let ((startpoint (point)))
 	  (skip-chars-backward (concat "\\\\" word-chars))
-	  ;; Fixme: This isn't consistent with Bash, at least -- not
-	  ;; all non-ASCII chars should be word constituents.
-	  (if (and (> (- (point) 2) (point-min))
-		   (= (char-after (- (point) 2)) ?\\))
+	  (if (and comint-file-name-quote-list
+		   (eq (char-before (1- (point))) ?\\))
 	      (forward-char -2))
-	  (if (and (> (- (point) 1) (point-min))
-		   (>= (char-after (- (point) 1)) 128))
+	  ;; FIXME: This isn't consistent with Bash, at least -- not
+	  ;; all non-ASCII chars should be word constituents.
+	  (if (and (not (bobp)) (>= (char-before) 128))
 	      (forward-char -1))
 	  (if (= (point) startpoint)
 	      (setq giveup t))))
@@ -3012,14 +3013,14 @@ See `comint-word'."
 (defun comint--unquote&requote-argument (qstr &optional upos)
   (unless upos (setq upos 0))
   (let* ((qpos 0)
-         (dquotes nil)
          (ustrs '())
          (re (concat
-              "[\"']\\|\\\\\\(.\\)"
-              "\\|\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
-              "\\|{\\(?2:[^{}]+\\)}\\)"
+              "\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
+              "\\|{\\(?1:[^{}]+\\)}\\)"
               (when (memq system-type '(ms-dos windows-nt))
-                "\\|%\\(?2:[^\\\\/]*\\)%")))
+                "\\|%\\(?1:[^\\\\/]*\\)%")
+              (when comint-file-name-quote-list
+                "\\|\\\\\\(.\\)")))
          (qupos nil)
          (push (lambda (str end)
                  (push str ustrs)
@@ -3030,18 +3031,9 @@ See `comint-word'."
     (while (setq match (string-match re qstr qpos))
       (funcall push (substring qstr qpos match) match)
       (cond
-       ((match-beginning 1) (funcall push (match-string 1 qstr) (match-end 0)))
-       ((match-beginning 2) (funcall push (getenv (match-string 2 qstr))
+       ((match-beginning 2) (funcall push (match-string 2 qstr) (match-end 0)))
+       ((match-beginning 1) (funcall push (getenv (match-string 1 qstr))
                                      (- (match-end 0))))
-       ((eq (aref qstr match) ?\") (setq dquotes (not dquotes)))
-       ((eq (aref qstr match) ?\')
-        (cond
-         (dquotes (funcall push "'" (match-end 0)))
-         ((< match (1+ (length qstr)))
-          (let ((end (string-match "'" qstr (1+ match))))
-            (funcall push (substring qstr (1+ match) end)
-                     (or end (length qstr)))))
-         (t nil)))
        (t (error "Unexpected case in comint--unquote&requote-argument!")))
       (setq qpos (match-end 0)))
     (funcall push (substring qstr qpos) (length qstr))
@@ -3140,7 +3132,7 @@ Returns t if successful."
 See `completion-table-with-quoting' and `comint-requote-function'.")
 (defvar comint-requote-function #'comint--requote-argument
   "Function to use for completion of quoted data.
-See `completion-table-with-quoting' and `comint-requote-function'.")
+See `completion-table-with-quoting' and `comint-unquote-function'.")
 
 (defun comint--complete-file-name-data ()
   "Return the completion data for file name at point."
diff --git a/lisp/shell.el b/lisp/shell.el
index 1784188f6ad..ca238a443f3 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -372,8 +372,57 @@ Thus, this does not include the shell's current directory.")
 
 ;;; Basic Procedures
 
-(defun shell-parse-pcomplete-arguments ()
+(defun shell--unquote&requote-argument (qstr &optional upos)
+  (unless upos (setq upos 0))
+  (let* ((qpos 0)
+         (dquotes nil)
+         (ustrs '())
+         (re (concat
+              "[\"']"
+              "\\|\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)"
+              "\\|{\\(?1:[^{}]+\\)}\\)"
+              (when (memq system-type '(ms-dos windows-nt))
+                "\\|%\\(?1:[^\\\\/]*\\)%")
+              (when comint-file-name-quote-list
+                "\\|\\\\\\(.\\)")))
+         (qupos nil)
+         (push (lambda (str end)
+                 (push str ustrs)
+                 (setq upos (- upos (length str)))
+                 (unless (or qupos (> upos 0))
+                   (setq qupos (if (< end 0) (- end) (+ upos end))))))
+         match)
+    (while (setq match (string-match re qstr qpos))
+      (funcall push (substring qstr qpos match) match)
+      (cond
+       ((match-beginning 2) (funcall push (match-string 2 qstr) (match-end 0)))
+       ((match-beginning 1) (funcall push (getenv (match-string 1 qstr))
+                                     (- (match-end 0))))
+       ((eq (aref qstr match) ?\") (setq dquotes (not dquotes)))
+       ((eq (aref qstr match) ?\')
+        (cond
+         (dquotes (funcall push "'" (match-end 0)))
+         ((< match (1+ (length qstr)))
+          (let ((end (string-match "'" qstr (1+ match))))
+            (funcall push (substring qstr (1+ match) end)
+                     (or end (length qstr)))))
+         (t nil)))
+       (t (error "Unexpected case in shell--unquote&requote-argument!")))
+      (setq qpos (match-end 0)))
+    (funcall push (substring qstr qpos) (length qstr))
+    (list (mapconcat #'identity (nreverse ustrs) "")
+          qupos #'comint-quote-filename)))
+
+(defun shell--unquote-argument (str)
+  (car (shell--unquote&requote-argument str)))
+(defun shell--requote-argument (upos qstr)
+  ;; See `completion-table-with-quoting'.
+  (let ((res (shell--unquote&requote-argument qstr upos)))
+    (cons (nth 1 res) (nth 2 res))))
+
+(defun shell--parse-pcomplete-arguments ()
   "Parse whitespace separated arguments in the current region."
+  ;; FIXME: share code with shell--unquote&requote-argument.
   (let ((begin (save-excursion (shell-backward-command 1) (point)))
 	(end (point))
 	begins args)
@@ -394,13 +443,13 @@ Thus, this does not include the shell's current directory.")
             (cond
              ((match-beginning 3)       ;Backslash escape.
               (push (cond
-                     ((null pcomplete-arg-quote-list)
+                     ((null comint-file-name-quote-list)
                       (goto-char (match-beginning 3)) "\\")
                      ((= (match-beginning 3) (match-end 3)) "\\")
                      (t (match-string 3)))
                     arg))
              ((match-beginning 2)       ;Double quote.
-              (push (if (null pcomplete-arg-quote-list) (match-string 2)
+              (push (if (null comint-file-name-quote-list) (match-string 2)
                       (replace-regexp-in-string
                        "\\\\\\(.\\)" "\\1" (match-string 2)))
                     arg))
@@ -430,10 +479,10 @@ Shell buffers.  It implements `shell-completion-execonly' for
        shell-file-name-quote-list)
   (set (make-local-variable 'comint-dynamic-complete-functions)
        shell-dynamic-complete-functions)
+  (setq-local comint-unquote-function #'shell--unquote-argument)
+  (setq-local comint-requote-function #'shell--requote-argument)
   (set (make-local-variable 'pcomplete-parse-arguments-function)
-       #'shell-parse-pcomplete-arguments)
-  (set (make-local-variable 'pcomplete-arg-quote-list)
-       comint-file-name-quote-list)
+       #'shell--parse-pcomplete-arguments)
   (set (make-local-variable 'pcomplete-termination-string)
        (cond ((not comint-completion-addsuffix) "")
              ((stringp comint-completion-addsuffix)
-- 
2.39.5