Subject: bug#10585: Move general pcomplete/ commands from eshell to pcmpl-unix

From: Stefan Kangas <[email-masked]>
Date: Wed, 2 Sep 2020 15:30:27 +0000

Glenn Morris <[email-masked]> writes:

> em-unix defines the following commands in the general pcomplete/
> namespace:
>
> pcomplete/ftp
> pcomplete/ncftp
> pcomplete/ping
> pcomplete/rlogin
> pcomplete/telnet
> pcomplete/rsh
>
> These should all be moved to pcmpl-unix (and the dependency on eshell
> removed).
>
> Similarly for em-xtra.el and pcomplete/bcc32, pcomplete/bcc.

I've done that in the attached patches.  Any comments?
From d0d3fcef37afbeb9fd51818d952ab7d7a2ce34f6 Mon Sep 17 00:00:00 2001
From: Stefan Kangas <[email-masked]>
Date: Wed, 2 Sep 2020 02:34:43 +0200
Subject: [PATCH 1/2] Move pcomplete/bcc32 from eshell to pcmpl-x (Bug#10585)

* lisp/eshell/em-xtra.el (pcomplete/bcc32, pcomplete/bcc): Move
from here...
* lisp/pcmpl-x.el (pcomplete/bcc32, pcomplete/bcc): ...to here.
---
 lisp/eshell/em-xtra.el | 30 ------------------------------
 lisp/pcmpl-x.el        | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/lisp/eshell/em-xtra.el b/lisp/eshell/em-xtra.el
index d55986c49b..3c038edfd1 100644
--- a/lisp/eshell/em-xtra.el
+++ b/lisp/eshell/em-xtra.el
@@ -94,36 +94,6 @@ eshell/set-exclusive-or
 (defalias 'eshell/ff 'find-name-dired)
 (defalias 'eshell/gf 'find-grep-dired)
 
-(defun pcomplete/bcc32 ()
-  "Completion function for Borland's C++ compiler."
-  (let ((cur (pcomplete-arg 0)))
-    (cond
-     ((string-match "\\`-w\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
-      (pcomplete-here
-       '("ali" "amb" "amp" "asc" "asm" "aus" "bbf" "bei" "big" "ccc"
-	 "cln" "cod" "com" "cpt" "csu" "def" "dig" "dpu" "dsz" "dup"
-	 "eas" "eff" "ext" "hch" "hid" "ias" "ibc" "ifr" "ill" "nil"
-	 "lin" "lvc" "mcs" "mes" "mpc" "mpd" "msg" "nak" "ncf" "nci"
-	 "ncl" "nfd" "ngu" "nin" "nma" "nmu" "nod" "nop" "npp" "nsf"
-	 "nst" "ntd" "nto" "nvf" "obi" "obs" "ofp" "osh" "ovf" "par"
-	 "pch" "pck" "pia" "pin" "pow" "prc" "pre" "pro" "rch" "ret"
-	 "rng" "rpt" "rvl" "sig" "spa" "stl" "stu" "stv" "sus" "tai"
-	 "tes" "thr" "ucp" "use" "voi" "zdi") (match-string 2 cur)))
-     ((string-match "\\`-[LIn]\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
-      (pcomplete-here (pcomplete-dirs) (match-string 2 cur)))
-     ((string-match "\\`-[Ee]\\(.*\\)\\'" cur)
-      (pcomplete-here (pcomplete-dirs-or-entries "\\.[Ee][Xx][Ee]\\'")
-		      (match-string 1 cur)))
-     ((string-match "\\`-o\\(.*\\)\\'" cur)
-      (pcomplete-here (pcomplete-dirs-or-entries "\\.[Oo][Bb][Jj]\\'")
-		      (match-string 1 cur)))
-     (t
-      (pcomplete-opt "3456ABCDEHIKLMNOPRSTUVXabcdefgijklnoptuvwxyz"))))
-  (while (pcomplete-here
-	  (pcomplete-dirs-or-entries "\\.[iCc]\\([Pp][Pp]\\)?\\'"))))
-
-(defalias 'pcomplete/bcc 'pcomplete/bcc32)
-
 (provide 'em-xtra)
 
 ;; Local Variables:
diff --git a/lisp/pcmpl-x.el b/lisp/pcmpl-x.el
index 5244ada523..6e96a67b7b 100644
--- a/lisp/pcmpl-x.el
+++ b/lisp/pcmpl-x.el
@@ -286,5 +286,37 @@ pcomplete/ag
                                     (pcmpl-x-ag-options))))
       (pcomplete-here* (pcomplete-dirs-or-entries)))))
 
+;;;###autoload
+(defun pcomplete/bcc32 ()
+  "Completion function for Borland's C++ compiler."
+  (let ((cur (pcomplete-arg 0)))
+    (cond
+     ((string-match "\\`-w\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
+      (pcomplete-here
+       '("ali" "amb" "amp" "asc" "asm" "aus" "bbf" "bei" "big" "ccc"
+         "cln" "cod" "com" "cpt" "csu" "def" "dig" "dpu" "dsz" "dup"
+         "eas" "eff" "ext" "hch" "hid" "ias" "ibc" "ifr" "ill" "nil"
+         "lin" "lvc" "mcs" "mes" "mpc" "mpd" "msg" "nak" "ncf" "nci"
+         "ncl" "nfd" "ngu" "nin" "nma" "nmu" "nod" "nop" "npp" "nsf"
+         "nst" "ntd" "nto" "nvf" "obi" "obs" "ofp" "osh" "ovf" "par"
+         "pch" "pck" "pia" "pin" "pow" "prc" "pre" "pro" "rch" "ret"
+         "rng" "rpt" "rvl" "sig" "spa" "stl" "stu" "stv" "sus" "tai"
+         "tes" "thr" "ucp" "use" "voi" "zdi") (match-string 2 cur)))
+     ((string-match "\\`-[LIn]\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
+      (pcomplete-here (pcomplete-dirs) (match-string 2 cur)))
+     ((string-match "\\`-[Ee]\\(.*\\)\\'" cur)
+      (pcomplete-here (pcomplete-dirs-or-entries "\\.[Ee][Xx][Ee]\\'")
+                      (match-string 1 cur)))
+     ((string-match "\\`-o\\(.*\\)\\'" cur)
+      (pcomplete-here (pcomplete-dirs-or-entries "\\.[Oo][Bb][Jj]\\'")
+                      (match-string 1 cur)))
+     (t
+      (pcomplete-opt "3456ABCDEHIKLMNOPRSTUVXabcdefgijklnoptuvwxyz"))))
+  (while (pcomplete-here
+          (pcomplete-dirs-or-entries "\\.[iCc]\\([Pp][Pp]\\)?\\'"))))
+
+;;;###autoload
+(defalias 'pcomplete/bcc 'pcomplete/bcc32)
+
 (provide 'pcmpl-x)
 ;;; pcmpl-x.el ends here
-- 
2.28.0

From b5f375f7ea55779d5dee89d8e9f186f09cbd6f3a Mon Sep 17 00:00:00 2001
From: Stefan Kangas <[email-masked]>
Date: Wed, 2 Sep 2020 02:45:39 +0200
Subject: [PATCH 2/2] Move several completions from eshell to pcomplete
 (Bug#10585)

* lisp/eshell/em-unix.el (eshell-complete-hostname)
(pcomplete/ftp, pcomplete/ncftp, pcomplete/ping)
(pcomplete/rlogin, pcomplete/telnet, pcomplete/rsh):
Move from here...
* lisp/pcmpl-unix.el (pcmpl-unix-complete-hostname)
(pcomplete/ftp, pcomplete/ncftp, pcomplete/ping)
(pcomplete/rlogin, pcomplete/telnet, pcomplete/rsh):
...to here.  Make old names into aliases.

* lisp/eshell/esh-util.el (eshell-hosts-file)
(eshell-host-names, eshell-host-timestamp)
(eshell-read-hosts-file, eshell-read-hosts)
(eshell-read-host-names): Move from here...
* lisp/pcomplete.el (pcomplete-hosts-file)
(pcomplete--host-name-cache)
(pcomplete--host-name-cache-timestamp)
(pcomplete-read-hosts-file, pcomplete-read-hosts)
(pcomplete-read-host-names): ...to here.  Make old names into
aliases.

* lisp/eshell/em-unix.el (eshell-complete-host-reference): Update
caller.
---
 lisp/eshell/em-unix.el  | 27 +++----------------
 lisp/eshell/esh-util.el | 59 ++++++++++++++++-------------------------
 lisp/pcmpl-unix.el      | 23 ++++++++++++++++
 lisp/pcomplete.el       | 44 ++++++++++++++++++++++++++++++
 4 files changed, 94 insertions(+), 59 deletions(-)

diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el
index fbd3cfbb6f..fd4cd6716d 100644
--- a/lisp/eshell/em-unix.el
+++ b/lisp/eshell/em-unix.el
@@ -787,9 +787,9 @@ eshell/glimpse
 
 ;; completions rules for some common UNIX commands
 
-(defsubst eshell-complete-hostname ()
-  "Complete a command that wants a hostname for an argument."
-  (pcomplete-here (eshell-read-host-names)))
+(autoload 'pcmpl-unix-complete-hostname "pcmpl-unix")
+(define-obsolete-function-alias 'eshell-complete-hostname
+  #'pcmpl-unix-complete-hostname "28.1")
 
 (defun eshell-complete-host-reference ()
   "If there is a host reference, complete it."
@@ -798,26 +798,7 @@ eshell-complete-host-reference
     (when (setq index (string-match "@[a-z.]*\\'" arg))
       (setq pcomplete-stub (substring arg (1+ index))
 	    pcomplete-last-completion-raw t)
-      (throw 'pcomplete-completions (eshell-read-host-names)))))
-
-(defalias 'pcomplete/ftp    'eshell-complete-hostname)
-(defalias 'pcomplete/ncftp  'eshell-complete-hostname)
-(defalias 'pcomplete/ping   'eshell-complete-hostname)
-(defalias 'pcomplete/rlogin 'eshell-complete-hostname)
-
-(defun pcomplete/telnet ()
-  (require 'pcmpl-unix)
-  (pcomplete-opt "xl(pcmpl-unix-user-names)")
-  (eshell-complete-hostname))
-
-(defun pcomplete/rsh ()
-  "Complete `rsh', which, after the user and hostname, is like xargs."
-  (require 'pcmpl-unix)
-  (pcomplete-opt "l(pcmpl-unix-user-names)")
-  (eshell-complete-hostname)
-  (pcomplete-here (funcall pcomplete-command-completion-function))
-  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
-	       pcomplete-default-completion-function)))
+      (throw 'pcomplete-completions (pcomplete-read-host-names)))))
 
 (defvar block-size)
 (defvar by-bytes)
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index ab030ede05..0122f9be48 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -51,9 +51,15 @@ eshell-passwd-file
   :group 'eshell-util)
 
 (defcustom eshell-hosts-file "/etc/hosts"
-  "The name of the /etc/hosts file."
+  "The name of the /etc/hosts file.
+Use `pcomplete-hosts-file' instead; this variable is obsolete and
+has no effect."
   :type '(choice (const :tag "No hosts file" nil) file)
   :group 'eshell-util)
+;; Don't make it into an alias, because it doesn't really work with
+;; custom and risks creating duplicate entries.  Just point users to
+;; the other variable, which is less frustrating.
+(make-obsolete-variable 'eshell-hosts-file nil "28.1")
 
 (defcustom eshell-handle-errors t
   "If non-nil, Eshell will handle errors itself.
@@ -127,11 +133,14 @@ eshell-user-names
 (defvar eshell-user-timestamp nil
   "A timestamp of when the user file was read.")
 
-(defvar eshell-host-names nil
-  "A cache the names of frequently accessed hosts.")
+;;; Obsolete variables:
 
-(defvar eshell-host-timestamp nil
-  "A timestamp of when the hosts file was read.")
+(define-obsolete-variable-alias 'eshell-host-names
+  'pcomplete--host-name-cache "28.1")
+(define-obsolete-variable-alias 'eshell-host-timestamp
+  'pcomplete--host-name-cache-timestamp "28.1")
+(defvar pcomplete--host-name-cache)
+(defvar pcomplete--host-name-cache-timestamp)
 
 ;;; Functions:
 
@@ -479,37 +488,15 @@ eshell-user-id
 
 (defalias 'eshell-user-name 'user-login-name)
 
-(defun eshell-read-hosts-file (filename)
-  "Read in the hosts from FILENAME, default `eshell-hosts-file'."
-  (let (hosts)
-    (with-temp-buffer
-      (insert-file-contents (or filename eshell-hosts-file))
-      (goto-char (point-min))
-      (while (re-search-forward
-              ;; "^ \t\\([^# \t\n]+\\)[ \t]+\\([^ \t\n]+\\)\\([ \t]*\\([^ \t\n]+\\)\\)?"
-	      "^[ \t]*\\([^# \t\n]+\\)[ \t]+\\([^ \t\n].+\\)" nil t)
-        (push (cons (match-string 1)
-                    (split-string (match-string 2)))
-              hosts)))
-    (nreverse hosts)))
-
-(defun eshell-read-hosts (file result-var timestamp-var)
-  "Read the contents of /etc/hosts for host names."
-  (if (or (not (symbol-value result-var))
-	  (not (symbol-value timestamp-var))
-	  (time-less-p
-	   (symbol-value timestamp-var)
-	   (file-attribute-modification-time (file-attributes file))))
-      (progn
-	(set result-var (apply #'nconc (eshell-read-hosts-file file)))
-	(set timestamp-var (current-time))))
-  (symbol-value result-var))
-
-(defun eshell-read-host-names ()
-  "Read the contents of /etc/hosts for host names."
-  (if eshell-hosts-file
-      (eshell-read-hosts eshell-hosts-file 'eshell-host-names
-			 'eshell-host-timestamp)))
+(autoload 'pcomplete-read-hosts-file "pcomplete")
+(autoload 'pcomplete-read-hosts "pcomplete")
+(autoload 'pcomplete-read-host-names "pcomplete")
+(define-obsolete-function-alias 'eshell-read-hosts-file
+  #'pcomplete-read-hosts-file "28.1")
+(define-obsolete-function-alias 'eshell-read-hosts
+  #'pcomplete-read-hosts "28.1")
+(define-obsolete-function-alias 'eshell-read-host-names
+  #'pcomplete-read-host-names "28.1")
 
 (defsubst eshell-copy-environment ()
   "Return an unrelated copy of `process-environment'."
diff --git a/lisp/pcmpl-unix.el b/lisp/pcmpl-unix.el
index 822f6f37e7..74f45b9523 100644
--- a/lisp/pcmpl-unix.el
+++ b/lisp/pcmpl-unix.el
@@ -217,6 +217,29 @@ pcomplete/scp
                                        (pcmpl-ssh-hosts)))))))
                 (complete-with-action action table string pred))))))
 
+(defsubst pcmpl-unix-complete-hostname ()
+  "Complete a command that wants a hostname for an argument."
+  (pcomplete-here (pcomplete-read-host-names)))
+
+(defalias 'pcomplete/ftp    'pcmpl-unix-complete-hostname)
+(defalias 'pcomplete/ncftp  'pcmpl-unix-complete-hostname)
+(defalias 'pcomplete/ping   'pcmpl-unix-complete-hostname)
+(defalias 'pcomplete/rlogin 'pcmpl-unix-complete-hostname)
+
+;;;###autoload
+(defun pcomplete/telnet ()
+  (pcomplete-opt "xl(pcmpl-unix-user-names)")
+  (pcmpl-unix-complete-hostname))
+
+;;;###autoload
+(defun pcomplete/rsh ()
+  "Complete `rsh', which, after the user and hostname, is like xargs."
+  (pcomplete-opt "l(pcmpl-unix-user-names)")
+  (pcmpl-unix-complete-hostname)
+  (pcomplete-here (funcall pcomplete-command-completion-function))
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+               pcomplete-default-completion-function)))
+
 (provide 'pcmpl-unix)
 
 ;;; pcmpl-unix.el ends here
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 32e61e84e0..014f9628b9 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -325,6 +325,10 @@ pcomplete-termination-string
 modified to be an empty string, or the desired separation string."
   :type 'string)
 
+(defcustom pcomplete-hosts-file "/etc/hosts"
+  "The name of the /etc/hosts file."
+  :type '(choice (const :tag "No hosts file" nil) file))
+
 ;;; Internal Variables:
 
 ;; for cycling completion support
@@ -1289,6 +1293,46 @@ pcomplete-process-result
     (skip-chars-backward "\n")
     (buffer-substring (point-min) (point))))
 
+;; hostname completion
+
+(defvar pcomplete--host-name-cache nil
+  "A cache the names of frequently accessed hosts.")
+
+(defvar pcomplete--host-name-cache-timestamp nil
+  "A timestamp of when the hosts file was read.")
+
+(defun pcomplete-read-hosts-file (filename)
+  "Read in the hosts from FILENAME, default `pcomplete-hosts-file'."
+  (let (hosts)
+    (with-temp-buffer
+      (insert-file-contents (or filename pcomplete-hosts-file))
+      (goto-char (point-min))
+      (while (re-search-forward
+              ;; "^ \t\\([^# \t\n]+\\)[ \t]+\\([^ \t\n]+\\)\\([ \t]*\\([^ \t\n]+\\)\\)?"
+              "^[ \t]*\\([^# \t\n]+\\)[ \t]+\\([^ \t\n].+\\)" nil t)
+        (push (cons (match-string 1)
+                    (split-string (match-string 2)))
+              hosts)))
+    (nreverse hosts)))
+
+(defun pcomplete-read-hosts (file result-var timestamp-var)
+  "Read the contents of /etc/hosts for host names."
+  (if (or (not (symbol-value result-var))
+          (not (symbol-value timestamp-var))
+          (time-less-p
+           (symbol-value timestamp-var)
+           (file-attribute-modification-time (file-attributes file))))
+      (progn
+        (set result-var (apply #'nconc (pcomplete-read-hosts-file file)))
+        (set timestamp-var (current-time))))
+  (symbol-value result-var))
+
+(defun pcomplete-read-host-names ()
+  "Read the contents of /etc/hosts for host names."
+  (if pcomplete-hosts-file
+      (pcomplete-read-hosts pcomplete-hosts-file 'pcomplete--host-name-cache
+                   'pcomplete--host-name-cache-timestamp)))
+
 ;; create a set of aliases which allow completion functions to be not
 ;; quite so verbose
 
-- 
2.28.0