Без getch
, на который можно положиться и избегать устаревшего getpass
, рекомендуемый подход состоит в отключении терминала ECHO при использовании termios
.После нескольких поисков, чтобы найти стандартную процедуру гибкого пароля, я был удивлен, что очень мало для автономного использования с C. Вместо простого перекодирования getch
с опциями termios c_lflag
, немного более обобщенный подход требует всего лишь нескольких дополнений.Помимо замены getch
любая подпрограмма должна обеспечивать указанную максимальную длину, чтобы предотвратить переполнение, усекать, если пользователь пытается войти за пределы максимума, и предупреждать, если усечение происходит каким-либо образом.
Ниже, дополнения позволят прочитатьиз любого FILE *
входного потока, ограничивая длину до указанной длины, предоставляя минимальную возможность редактирования (возврата) при получении ввода, позволяя полностью или полностью отключить маску символов и, наконец, вернуть длину введенного пароля.Предупреждение было добавлено, когда введенный пароль был усечен до максимальной или указанной длины.
Надеемся, что этот вопрос окажется полезным для других, ищущих аналогичное решение:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#include <errno.h> /* for errno */
#include <unistd.h> /* for EINTR */
#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 = 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)