在没有getch 依赖和避免过时的getpass 的情况下,推荐的方法是通过termios 使用来禁用终端ECHO。经过几次搜索以找到一个固定的灵活密码例程,令我惊讶的是,很少有人可以与 C 一起独立使用。不是简单地使用 termios c_lflag 选项重新编码getch,而是稍微更通用的方法只需要添加一些内容。除了替换 getch 之外,任何例程都应强制指定最大长度以防止溢出,如果用户尝试输入超出最大值则截断,并在以某种方式发生截断时发出警告。
下面,添加的内容将允许从任何FILE * 输入流中读取,将长度限制为指定长度,在接受输入时提供最小的编辑(退格)能力,允许指定或完全禁用字符掩码,最后返回输入密码的长度。当输入的密码被截断到最大或指定长度时添加警告。
希望它对寻找类似解决方案的其他人有用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#define MAXPW 32
/* read a string from fp into pw masking keypress with mask char.
getpasswd will read upto sz - 1 chars into pw, null-terminating
the resulting string. On success, the number of characters in
pw are returned, -1 otherwise.
*/
ssize_t getpasswd (char **pw, size_t sz, int mask, FILE *fp)
{
if (!pw || !sz || !fp) return -1; /* validate input */
#ifdef MAXPW
if (sz > MAXPW) sz = MAXPW;
#endif
if (*pw == NULL) { /* reallocate if no address */
void *tmp = realloc (*pw, sz * sizeof **pw);
if (!tmp)
return -1;
memset (tmp, 0, sz); /* initialize memory to 0 */
*pw = (char*) tmp;
}
size_t idx = 0; /* index, number of chars in read */
int c = 0;
struct termios old_kbd_mode; /* orig keyboard settings */
struct termios new_kbd_mode;
if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings */
fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
return -1;
} /* copy old to new */
memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); /* new kbd flags */
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
/* read chars from fp, mask if valid char specified */
while (((c = fgetc (fp)) != '\n' && c != EOF && idx < sz - 1) ||
(idx == sz - 1 && c == 127))
{
if (c != 127) {
if (31 < mask && mask < 127) /* valid ascii char */
fputc (mask, stdout);
(*pw)[idx++] = c;
}
else if (idx > 0) { /* handle backspace (del) */
if (31 < mask && mask < 127) {
fputc (0x8, stdout);
fputc (' ', stdout);
fputc (0x8, stdout);
}
(*pw)[--idx] = 0;
}
}
(*pw)[idx] = 0; /* null-terminate */
/* reset original keyboard */
if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
if (idx == sz - 1 && c != '\n') /* warn if pw truncated */
fprintf (stderr, " (%s() warning: truncated at %zu chars.)\n",
__func__, sz - 1);
return idx; /* number of chars in passwd */
}
显示使用的简单程序如下。如果使用静态字符数组来保存密码,只需确保将指针传递给函数。
int main (void ) {
char pw[MAXPW] = {0};
char *p = pw;
FILE *fp = stdin;
ssize_t nchr = 0;
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, '*', fp);
printf ("\n you entered : %s (%zu chars)\n", p, nchr);
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, 0, fp);
printf ("\n you entered : %s (%zu chars)\n\n", p, nchr);
return 0;
}
示例输出
$ ./bin/getpasswd2
Enter password: ******
you entered : 123456 (6 chars)
Enter password:
you entered : abcdef (6 chars)