Your main challenge is that scanf()
- as well as getchar()
and similar commands - are blocking. An unknown interval of time could elapse before the user actually enters any input - and your five seconds might already be up at that stage.
select()
- monitor file descriptors with timeout
I think one of the most feasible options is to use select()
- which monitors for activity on certain sets of file descriptors. Specifically, you want to monitor for activity on the stdin
file descriptor.
The following accomplishes something close to what you need I believe.
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
int main(void) {
char buf[16] = {'\0'};
char *pass = buf;
time_t time_update = 0, time_now = 0;
struct timeval tm;
int res = 0;
struct termios term_attr, new_attr;
fd_set rset;
// Change terminal attributes (We don't want line-buffered mode.)
tcgetattr(fileno(stdin), &term_attr);
tcgetattr(fileno(stdin), &new_attr);
new_attr.c_lflag &= ~(ICANON | ECHO);
tcsetattr(fileno(stdin), TCSANOW, &new_attr);
printf("Enter password: ");
time_update = time(NULL);
while (1) {
tm.tv_sec = 0;
tm.tv_usec = 50000;
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
res = select(fileno(stdin) + 1, &rset, NULL, NULL, &tm);
if (FD_ISSET(STDIN_FILENO, &rset)) {
time_update = time(NULL);
int c = getchar();
if (c == '\n') {
break;
}
*pass = c;
pass++;
}
time_now = time(NULL);
if (time_now - time_update >= 5) {
puts("Timed out ...");
break;
}
}
pass = buf;
printf("You entered: %s \n", pass);
// Restore original terminal attributes
tcsetattr(fileno(stdin), TCSANOW, &term_attr);
return 0;
}
Notes:
- The last argument to
select()
is a struct timeval
which specifies how long to wait for activity on the specified file descriptors. In this case I have specified a timeout of 50 milliseconds.
- The terminal needs to be placed in character buffer mode rather than line-buffered mode. (Otherwise you will need to press enter every time there is a new character.)
Operating system support
select()
is part of the POSIX specification, but I do not know if it is implemented on Windows. Maybe someone can clarify?
Also ... I do not know if setting the terminal attributes will work as expected on Windows either. (I have only tested on Linux.)
I realize this solution might be a little longer / more complex than you hoped - but I am not aware of an easier way.