gaynor@paul.rutgers.edu (Silver) (12/16/89)
I've got something that works fairly well. Reconfigurable to some extent on a
character by character basis, sends passwords without echo (sorry, no fix for
the recent-keys problem).
Regards, [Ag]
_______________________________________________________________________________
(provide 'shell-filter)
;; When logging in remotely, you might want to give the command "stty -echo" to
;; suppress the echoing of commands as they're executed. For tcsh, you may
;; also want to unset editmode and/or filec. Up to you, though...
;; The regexp is a little convoluted, but I think it covers most of the bases.
(defvar shell-filter-password-prompt "\\<passw\\(or\\)?d[\ \t]*[:>][\ \t]*\\'"
"Regular expression used to determine whether shell output contains a request
for a password. The successful candidate will match the end of the buffer,
handle whitespace appropriately, and various convolutions of `password'. The
ambient value of case-fold-search is non-nil during matching.")
(defun read-string-no-echo (&optional prompt)
"Read and return a string without echoing it. Newline and return characters
terminate input. If optional PROMPT is non-nil, it is displayed and the cursor
placed in the minibuffer while reading. \(Warning: view-lossage/recent-keys
can access the last 100 characters typed.\)"
(interactive)
(save-window-excursion
(let ((echo-keystrokes 0)
(string "")
char)
(if prompt
(progn (select-window (minibuffer-window))
(set-window-buffer (selected-window)
(get-buffer-create " *Temporary*"))
(erase-buffer)
(insert prompt)))
;; Grossly inefficient. BFD.
(while (not (memq (setq char (read-char)) '(?\r ?\n)))
(setq string (concat string (char-to-string char))))
string)))
(defun shell-filter-read-password ()
"Read a password in-line (without display, of course) and return it."
(read-string-no-echo))
(defun shell-filter-nuke-1 () (delete-char 1))
(defun shell-filter-ding () (delete-char 1) (ding 'continue))
(defun shell-filter-backspace () (delete-char 1) (delete-char (if (eq ?_ (preceding-char)) -1 1)))
;; I would have done this by regexp instead of character, but I think that this
;; would be putting more computation and effort than the task warrants.
(defvar shell-filter-specials-alist
'((?\C-m . shell-filter-nuke-1)
(?\C-l . shell-filter-nuke-1)
(?\C-g . shell-filter-ding)
(?\C-h . shell-filter-backspace))
"Alist of (CHARACTER . ACTION). When CHARACTER is encountered in shell output,
call ACTION with no parameters.")
(defun shell-filter (process string)
"Output filter for shell-mode buffers. See shell-filter-specials-alist for
information about special character handling. See shell-filter-password-prompt
and shell-filter-read-password for information about password handling."
(save-excursion
(set-buffer (process-buffer process))
(goto-char (marker-position (process-mark process)))
(let ((begin (point))
(end (progn (insert-before-markers string) (point)))
(case-fold-search t)
(specials (concat "^" (mapconcat (function (lambda (el)
(char-to-string (car el))))
shell-filter-specials-alist ""))))
(goto-char begin)
;; Alternatively, things could be based around re-search-foward. There's
;; no need for the added overhead, imho.
(while (progn (skip-chars-forward specials end) (< (point) end))
(funcall (cdr (assoc (following-char) shell-filter-specials-alist))))
;; It might be more `correct' to match against string instead of the buffer.
(if (re-search-backward shell-filter-password-prompt begin t)
(progn (goto-char (match-end 0))
(process-send-string process (concat (shell-filter-read-password) "\n")))))))
;; RU's site-init contains a function named add-hook. I don't know if it's
;; standard, but its intent is fairly obvious.
(add-hook 'shell-mode-hook
(function (lambda ()
;; Uncomment this if you want shells to die easily.
;; (process-kill-without-query (get-buffer-process (current-buffer)))
(set-process-filter (get-buffer-process (current-buffer))
(function shell-filter)))))