Subject: bug#17839: 24.4.50; read-passwd echoes password input in non-interactive sessions

From: Michael Albinus <[email-masked]>
Date: Thu, 10 Jul 2014 16:36:43 +0200

Stefan Monnier <[email-masked]> writes:

> If someone is motivated, I would even accept a patch that turns echo off
> temporarily.

Well, I've tried this 'cos I believe it is important. The idea is to
give the prompt in read-passwd the text property 'hide-chars. In
noninteractive mode, emacs writes "." instead of echoing the password
while typing. You can test it with

# emacs -batch -eval '(progn (message (read-string "Prompt1: ")) (message (read-passwd "Prompt2: ")) (message (read-string "Prompt3: ")))'

The patch is not perfect (it doesn't handled multi-byte chars, and I
have tested it only under Gnu/Linux), but it is a first step.

Comments?

>         Stefan

Best regards, Michael.

=== modified file 'lisp/subr.el'
--- lisp/subr.el	2014-06-28 17:27:29 +0000
+++ lisp/subr.el	2014-07-10 13:53:13 +0000
@@ -2058,7 +2058,7 @@
             (let ((enable-recursive-minibuffers t))
               (read-string
                (if noninteractive
-                   (format "%s[INPUT WILL NOT BE HIDDEN!] " prompt) ; bug#17839
+		   (propertize prompt 'hide-chars t) ; bug#17839
                  prompt)
                nil t default)) ; t = "no history"
           (when (buffer-live-p minibuf)

=== modified file 'src/minibuf.c'
--- src/minibuf.c	2014-05-26 02:28:09 +0000
+++ src/minibuf.c	2014-07-10 14:19:50 +0000
@@ -35,6 +35,10 @@
 #include "keymap.h"
 #include "termhooks.h"

+#include "systty.h"
+extern void emacs_get_tty (int, struct emacs_tty *);
+extern int emacs_set_tty (int, struct emacs_tty *, bool);
+
 /* List of buffers for use as minibuffers.
    The first element of the list is used for the outermost minibuffer
    invocation, the next element is used for a recursive minibuffer
@@ -209,6 +213,8 @@
 }

+static Lisp_Object Qhide_chars;
+
 /* Like read_minibuf but reading from stdin.  This function is called
    from read_minibuf to do the job if noninteractive.  */

@@ -224,6 +230,20 @@
   char *line;
   Lisp_Object val;
   int c;
+  bool hide_chars;
+  struct emacs_tty old, new;
+
+  /* Check, whether we need to suppress echoing.  */
+  hide_chars = ! (NILP (Fget_text_property
+			(make_number (0), Qhide_chars, prompt)));
+  if (hide_chars)
+    {
+      emacs_get_tty (fileno (stdin), &old);
+      new = old;
+      new.main.c_lflag &= ~ICANON;	/* Disable buffering */
+      new.main.c_lflag &= ~ECHO;	/* Disable echoing */
+      emacs_set_tty (fileno (stdin), &new, 0);
+    }

   fprintf (stdout, "%s", SDATA (prompt));
   fflush (stdout);
@@ -240,8 +260,17 @@
 	  if (errno != EINTR)
 	    break;
 	}
+      else if (hide_chars && (c == 127)) /* DEL */
+	{
+	  /* Unfortunately, we cannot edit stdout.  */
+	  // fprintf (stdout, "%c", c);
+	  /* Hmm, this doesn't work for multi-byte characters.  */
+	  (len > 0) && len--;
+	}
       else
 	{
+	  if (hide_chars)
+	    fprintf (stdout, ".");
 	  if (len == size)
 	    {
 	      if (STRING_BYTES_BOUND / 2 < size)
@@ -253,6 +282,13 @@
 	}
     }

+  /* Reset tty.  */
+  if (hide_chars)
+    {
+      fprintf (stdout, "\n");
+      emacs_set_tty (fileno (stdin), &old, 0);
+    }
+
   if (len || c == '\n')
     {
       val = make_string (line, len);
@@ -1935,6 +1971,7 @@
   DEFSYM (Qactivate_input_method, "activate-input-method");
   DEFSYM (Qcase_fold_search, "case-fold-search");
   DEFSYM (Qmetadata, "metadata");
+  DEFSYM (Qhide_chars, "hide-chars");

   DEFVAR_LISP ("read-expression-history", Vread_expression_history,
 	       doc: /* A history list for arguments that are Lisp expressions to evaluate.

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2014-06-09 14:50:57 +0000
+++ src/sysdep.c	2014-07-10 10:07:05 +0000
@@ -105,8 +105,8 @@
 #include "syssignal.h"
 #include "systime.h"

-static void emacs_get_tty (int, struct emacs_tty *);
-static int emacs_set_tty (int, struct emacs_tty *, bool);
+void emacs_get_tty (int, struct emacs_tty *) EXTERNALLY_VISIBLE;
+int emacs_set_tty (int, struct emacs_tty *, bool) EXTERNALLY_VISIBLE;

 /* ULLONG_MAX is missing on Red Hat Linux 7.3; see Bug#11781.  */
 #ifndef ULLONG_MAX
@@ -779,7 +779,7 @@

 /* Set *TC to the parameters associated with the terminal FD,
    or clear it if the parameters are not available.  */
-static void
+void
 emacs_get_tty (int fd, struct emacs_tty *settings)
 {
   /* Retrieve the primary parameters - baud rate, character size, etcetera.  */
@@ -795,7 +795,7 @@
    *SETTINGS.  If FLUSHP, discard input.
    Return 0 if all went well, and -1 (setting errno) if anything failed.  */

-static int
+int
 emacs_set_tty (int fd, struct emacs_tty *settings, bool flushp)
 {
   /* Set the primary parameters - baud rate, character size, etcetera.  */