proc terminal:password:get {str} {
terminal:canonicalOff
terminal:echoOff
puts -nonewline $str
flush stdout
set chr ""
set inputStr ""
while 1 {
set chr [read stdin 1]
#Backspace
if {$chr == "\b"} {
if {[string length $inputStr] > 0} {
puts -nonewline "\x1b\[D"
puts -nonewline " "
puts -nonewline "\x1b\[1D"
set lastChar [expr {[string length $inputStr] - 2}]
set inputStr [string range $inputStr 0 $lastChar]
flush stdout
}
continue
}
#eat up escape characters
#example: ESCc ESC\[D ESC\[1D ESC\[11D
if {$chr == "\x1b"} {
set nextChar [read stdin 1]
if {$nextChar == "\["} {
#This isn't a simple 2 char escape sequence
#It could be ESC\[D or ESC\[= or ESC\[1D
set nextChar [read stdin 1]
if {[string is digit $nextChar] || $nextChar == "="} {
while 1 {
#eat up the digits
set nextChar [read stdin 1]
if {[string is digit $nextChar]} {
continue
} else {
#We read a char that wasn't a digit, so we are at the end.
#If the string we had was ESC\[22D we just read D
break
}
}
}
}
continue
}
if {$chr == "\n" || $chr == "\r"} {
break
}
append inputStr $chr
puts -nonewline *
flush stdout
}
terminal:canonicalOn
terminal:echoOn
#DEBUG
#puts "\n$inputStr"
if {[string length $inputStr] <= 0} {
return -code error "Please specify one or more characters for your password.\n"
}
return $inputStr
}DKF: Here's the cheap-and-cheerful version using stty tricks. :^) It does a bit less, but uses far less code. Unix only.
proc terminal:password:get {promptString} {
# Turn off echoing, but leave newlines on. That looks better.
# Note that the terminal is left in cooked mode, so people can still use backspace
exec stty -echo echonl <@stdin
# Print the prompt
puts -nonewline stdout $promptString
flush stdout
# Read that password! :^)
gets stdin password
# Reset the terminal
exec stty echo -echonl <@stdin
return $password
}RJ 03/26/05 Yet another version uses Expect and echoes the "*". Uses raw mode for tty. Again, *NIX only.
proc terminal:password:get {pwprompt} {
set oldmode [stty -echo raw] ;# set to raw and no echo
send_user "\n $pwprompt"
set timeout -1 ;# wait as long as it takes for input - infinite timeout
set p ""
# read user input one char at a time
expect_user {
-re "\177" {
# handle backspace/delete translation
set lastchar [string index $p end]
set p [string range $p 0 [expr [string length $p] - 2]]
send_user "\010 \010"
exp_continue
}
"\r" {}
# password entry complete - drop out of expect_user
"\003" { exit }
# cntl-C entered - abort altogether
-re "." {
# character entry - add to pw variable and echo a "*"
append p $expect_out(buffer)
send_user "*"
exp_continue
}
}
send_user "\n" ;# send the user the carriage return eaten above by expect_user
eval stty $oldmode ;# restore echo and cooked mode saved above
return [string trimright $p "\r"] ;# strip carriage return and pass back pw
}DKF See also Echo-free Password Entry, and Reading a single character from the keyboard using Tcl (the techniques used there apply here too, though you need to apply them slightly differently).

