1

Is there a way to echo "*" or some other character when processing passwords? Using STDIN.noecho works but doesn't provide any user feedback. console.cr only provides raw and noecho.

Thanks. Still looking...

lewis
  • 131
  • 1
  • 9

1 Answers1

2

You're on the right path with STDIN.noecho! Effectively what you needed to do is read from STDIN in raw mode and echo out * every time a key is pressed. Then you'd listen for backspace so you can move the cursor back one and replace with a space, then replace that space with a * when the user typed another character. You also needed to check for the enter and Control+C being pressed as the terminal captures and gives that all to you in raw mode.

I've based this off the bash implementation found here in the second comment by user Alfe. Update: Changed the code to be inside blocks to hopefully eliminate possible terminal issues.

# Fix for crystal bug
STDIN.blocking = true

# STDIN chars without buffering
STDIN.raw do
    # Dont echo out to the terminal
    STDIN.noecho do
        final_password = ""
        while char = STDIN.read_char
            # If we have a backspace
            if char == '\u{7f}'
                # Move the cursor back 1 char
                if final_password != ""
                    STDOUT << "\b \b"
                    STDOUT.flush
                    final_password = final_password[0...-1]
                end
                next
            elsif char == '\r'
                # Enter was pressed we're finished!
                break
            elsif char == '\u{3}'
                # Control + C was pressed. Get outta here
                Process.exit 0
            elsif char.ascii_control?
                # A Control + [] char was pressed. Not valid for a password
                next
            end

            STDOUT << "*"
            STDOUT.flush
            final_password += char
        end

        puts final_password
    end
end

# Resetting when finished
STDIN.blocking = false

If this helps you please be sure to mark this as answered!

jaitaiwan
  • 190
  • 1
  • 12
  • This works but leaves my terminal in an unstable state. Resetting the terminal doesn't fix the problem. Your answer gets me so close. Thank you very much for the insight. – lewis Jan 25 '18 at 21:54
  • Hey be great if you specified what you're doing and how it breaks your terminal state. [Check out this code snipped though](https://github.com/kwang40/ECE391/blob/master/mp1/missile-command.c#L176) Apparently this is designed to not leave the term in an unusable state. Just needs porting to crystal haha – jaitaiwan Jan 26 '18 at 23:20
  • I also just took a look and I think maybe you're experiencing [this bug](https://github.com/crystal-lang/crystal/issues/2713) of which the solution is to add `STDIN.blocking = true` to the top of this solution. – jaitaiwan Jan 26 '18 at 23:37
  • I also found that and applied the suggested patch. Basically, when the code terminates my terminal will not echo any keystrokes and I don't get a newline if I just hit enter. Also the terminal -reset and clear- doesn't help. I have to close that terminal and open a new one. `Enter password: ****[~]$ [~]$ [~]$ [~]$ [~]$ [~]$ [~]$ [~]$ [~]$ ` – lewis Jan 30 '18 at 01:16
  • 1
    Both noecho and raw can work with a block... perhaps try putting the above code inside noecho and raw blocks? https://crystal-lang.org/api/0.23.1/IO/FileDescriptor.html#noecho%28%26block%29-instance-method – jaitaiwan Jan 30 '18 at 03:48