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 }