1 module tagion.tools.secretinput; 2 3 import core.stdc.stdio : getchar; 4 import std.stdio; 5 import tagion.utils.Term; 6 import std.algorithm; 7 import std.format; 8 import std.range; 9 import std.array; 10 11 KeyStroke.KeyCode getSecret(string text, out char[] passwd) { 12 enum NUL = char(0); 13 char[] result; 14 KeyStroke key; 15 KeyStroke.KeyCode keycode; 16 enum MAX_SIZE = 0x100; 17 char[MAX_SIZE] password; 18 scope (exit) { 19 password[] = 0; 20 } 21 password[] = 0; 22 int pos; 23 bool show_password; 24 size_t passwordSize() { 25 const count = (cast(ubyte[]) password).countUntil!(b => b == 0); 26 if (count < 0) { 27 return password.length; 28 } 29 return cast(size_t) count; 30 31 } 32 33 void display() { 34 char[] show; 35 if (show_password) { 36 show = password[0 .. passwordSize]; 37 } 38 else { 39 show = repeat('*', passwordSize).array; 40 } 41 writef("\r%s%s%s%s", CLEAREOL, text, 42 show, 43 linePos(pos + text.length)); 44 stdout.flush; 45 } 46 47 InputLoop: for (;;) { 48 display; 49 int ch; 50 keycode = key.getKey(ch); 51 if (keycode == KeyStroke.KeyCode.ENTER) { 52 break; 53 } 54 with (KeyStroke.KeyCode) { 55 switch (keycode) { 56 case ENTER: 57 break InputLoop; 58 case LEFT: 59 pos -= (pos > 0); 60 break; 61 case RIGHT: 62 pos += (pos < password.length); 63 break; 64 case DELETE: 65 if (pos < password.length) { 66 password = password[0 .. pos] ~ password[pos + 1 .. $] ~ NUL; 67 } 68 break; 69 case BACKSPACE: 70 if (pos > 0) { 71 password = password[0 .. pos - 1] ~ password[pos .. $] ~ NUL; 72 pos--; 73 } 74 75 break; 76 case CTRL_A: 77 show_password = !show_password; 78 break; 79 case CTRL_C, CTRL_D: 80 return keycode; 81 82 case NONE: 83 if (ch > 0x20 && ch < 127 && pos < password.length) { 84 password[pos] = cast(char) ch; 85 pos += (pos < password.length); 86 } 87 break; 88 default: 89 } 90 } 91 } 92 show_password = false; 93 display; 94 writeln; 95 passwd = password[0 .. passwordSize].dup; 96 return KeyStroke.KeyCode.NONE; 97 }