1

I've been pulling my hair out on this for many hours now. Basically, I have a program that asks the user to enter his password (123), and if the user doesn't enter anything for 5 seconds then the program will exit (game over). I've been trying to use time(NULL) and clock() but still no luck. Can anyone point me in the right direction, please? Thanks a lot in advance!

Here's my code:

#include <stdio.h>
#include <time.h>

 int main(){
   int password = 0;
   int num = 0;
   printf("%s\n", "Please enter your password");
   scanf("%d", &password);
   // Here I need to check if user didnt enter anything for 5 seconds,
   // and if he didnt enter anything then exit out of the program
   // I tried using time
   // time_t start = time(NULL);
   // time_t stop = time(NULL);
   //   if(((stop - start) * 1000) > 5000){
   //  printf("%s\n", "Game Over");
   //  break;          
  //  }

   printf("%s\n", "Thank you for entering your password, now enter any number");
   scanf("%d", &num);
   return 0;
 }
Devmix
  • 1,599
  • 5
  • 36
  • 73
  • 1
    my 2 cents is that process or thread is needed for this task – Soner from The Ottoman Empire Aug 21 '18 at 22:07
  • Dont use `break` use `return 0` for starters, `scanf` is also a blocking call, you will need to use `fork` or something similar to accomplish what you want – Mitch Aug 21 '18 at 22:07
  • @snr how to implement the thread? – Devmix Aug 21 '18 at 22:08
  • @Mitchel0022 after using return 0 how to get it to work and exit program after 5 seconds if user doesnt enter anything? – Devmix Aug 21 '18 at 22:09
  • @progx create a thread counting behind the curtain the seconds while the user is entering password, just google it – Soner from The Ottoman Empire Aug 21 '18 at 22:10
  • Possible duplicate of [Wait only 5 second for user input](https://stackoverflow.com/questions/19185958/wait-only-5-second-for-user-input) – PM 77-1 Aug 21 '18 at 22:11
  • @PM77-1 yes I came across the answer but then I started having issue by not being able to use on visual studio and other things. Do you have another way of accomplishing this? – Devmix Aug 21 '18 at 22:13
  • @progx yes, of course, https://stackoverflow.com/questions/1981459/using-threads-in-c-on-windows-simple-example – Soner from The Ottoman Empire Aug 21 '18 at 22:14
  • https://stackoverflow.com/questions/21197977/how-can-i-prevent-scanf-to-wait-forever-for-an-input-character – PM 77-1 Aug 21 '18 at 22:16
  • 1
    It's fairly trivial to start a pthread that sleeps for 5 sec and then checks an atomic 'user entered something' flag and terminates the process if not set. It's easier to do that than mess around with select(). – Martin James Aug 21 '18 at 23:04

1 Answers1

3

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.

David Collins
  • 2,852
  • 9
  • 13