@@ -736,6 +736,56 @@ proc getch*(): char =
736736 result = stdin.readChar ()
737737 discard fd.tcsetattr (TCSADRAIN , addr oldMode)
738738
739+ when defined (windows):
740+ from unicode import toUTF8, Rune , runeLenAt
741+
742+ proc readPasswordFromStdin * (prompt: string , password: var TaintedString ):
743+ bool {.tags : [ReadIOEffect , WriteIOEffect ].} =
744+ # # Reads a `password` from stdin without printing it. `password` must not
745+ # # be ``nil``! Returns ``false`` if the end of the file has been reached,
746+ # # ``true`` otherwise.
747+ password.string .setLen (0 )
748+ stdout.write (prompt)
749+ while true :
750+ let c = getch ()
751+ case c.char
752+ of '\r ' , chr (0x A ):
753+ break
754+ of '\b ' :
755+ # ensure we delete the whole UTF-8 character:
756+ var i = 0
757+ var x = 1
758+ while i < password.len:
759+ x = runeLenAt (password.string , i)
760+ inc i, x
761+ password.string .setLen (max (password.len - x, 0 ))
762+ else :
763+ password.string .add (toUTF8 (c.Rune ))
764+ stdout.write " \n "
765+
766+ else :
767+ import termios
768+
769+ proc readPasswordFromStdin * (prompt: string , password: var TaintedString ):
770+ bool {.tags : [ReadIOEffect , WriteIOEffect ].} =
771+ password.string .setLen (0 )
772+ let fd = stdin.getFileHandle ()
773+ var cur, old: Termios
774+ discard fd.tcgetattr (cur.addr )
775+ old = cur
776+ cur.c_lflag = cur.c_lflag and not Cflag (ECHO )
777+ discard fd.tcsetattr (TCSADRAIN , cur.addr )
778+ stdout.write prompt
779+ result = stdin.readLine (password)
780+ stdout.write " \n "
781+ discard fd.tcsetattr (TCSADRAIN , old.addr )
782+
783+ proc readPasswordFromStdin * (prompt = " password: " ): TaintedString =
784+ # # Reads a password from stdin without printing it.
785+ result = TaintedString (" " )
786+ discard readPasswordFromStdin (prompt, result )
787+
788+
739789# Wrappers assuming output to stdout:
740790template hideCursor * () = hideCursor (stdout)
741791template showCursor * () = showCursor (stdout)
0 commit comments