2

So I want a method to controlling the amount of time the input prompt will wait for the user to enter something.

For example in the following code:

#include <stdio.h>

int main(void){

    int i, value;
    for (i=0;i<10;i++){
            scanf(" %d", &value);
    }
}

How can I make the program to break the for loop if the user doesn't enter any input after 5 seconds?

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
Msegade
  • 476
  • 7
  • 17

3 Answers3

2

Here is a general purpose version of scanf, sync_scanf, witch should wait for seconds you choose or will return -2 as timeout:

int sync_scanf(time_t sec, const char *format, ...);

example:

#include <stdio.h>
#include <stdarg.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int sync_scanf(time_t sec, const char *format, ...);

int main(int argc, char **argv) {

    int i;
    int value;
    int ret;

    for (i = 0 ; i < 10 ; i++ ) {
        ret = sync_scanf(3, "%d", &value);

        if( ret > 0 ) {
            printf("OK %d\n", value);
        } else if( ret == -2 ) {
            printf("3 seconds passed and you typed nothing!\n");
            break;
        } else {
            printf("No enough arguments\n");
            break;
        }
    }

    return 0;
}

int sync_scanf(time_t sec, const char *format, ...) {
    int re;
    va_list arg;
    fd_set readfds;
    struct timeval timeout = {0};

    timeout.tv_sec = sec;

    FD_ZERO(&readfds);
    FD_SET(0, &readfds);

    re = select(1, &readfds, NULL, NULL, &timeout);

    if( re == -1 ) {
        perror("Error");
        return -1;
    }
    else if( re == 0 ) {
        return -2;
    }

    va_start(arg, format);
    re = vfscanf(stdin, format, arg);
    va_end(arg);

    return re;
}

demo:

$ gcc -Wall sample.c 
$ ./a.out 
232
OK 232
3 seconds passed and you typed nothing!
$ ./a.out 
32
OK 32
fewf
No enough arguments
$ 

It works as scanf but you pass the timeout first in seconds:

int sync_scanf(time_t sec, const char *format, ...);

It returns:

  • -1 on failure.
  • -2 on timeout.
  • Other than that it returns as scanf would does.
2

You can implement what you want using select (monitor stdin for some time to check if the input is available for reading), fgets (safely read input data to the buffer) and strtol (convert the buffer string to the long integer if possible).

Sample code is given below (check man pages e.g. to extend error handling):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    fd_set rfds;
    struct timeval tv;
    int i, val, retval;
    char *endptr, buff[255];

    for (i=0;i<10;i++){

        /* Watch stdin (fd 0) to see when it has input. */
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);

        /* Wait up to five seconds. */
        tv.tv_sec = 5;
        tv.tv_usec = 0;

        retval = select(1, &rfds, NULL, NULL, &tv);

        if (retval == -1){
            perror("select()");
            exit(EXIT_FAILURE);
        }
        else if (retval){
            /* FD_ISSET(0, &rfds) is true so input is available now. */

            /* Read data from stdin using fgets. */
            fgets(buff, sizeof(buff), stdin);

            /* Convert data stored in the buffer to long int. */
            errno = 0;
            val = strtol(buff, &endptr, 10);

            /* First, check for various possible errors. */
            if (errno != 0 && val == 0) {
                perror("strtol()");
                exit(EXIT_FAILURE);
            }
            if (endptr == buff) {
                fprintf(stderr, "No digits were found.\n");
                exit(EXIT_FAILURE);
            }

            /* If we got here, strtol successfully parsed a number. */
            printf("%d was read from stdin.\n", val);
        }
        else{
            printf("No data within five seconds.\n");
            break; 
        }
    }

    exit(EXIT_SUCCESS);
}
jwaliszko
  • 16,942
  • 22
  • 92
  • 158
1
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>

int flag; 
static sigjmp_buf jmpbuf;
static void sig_arm(int signo) 
{
    if(flag == -1)
        return;
    else if(flag == 0)
        siglongjmp(jmpbuf,1);
}   

int main(void){


    int i, value;
    signal(SIGALRM,sig_arm);
    for (i=0;i<10;i++){
        if(sigsetjmp(jmpbuf,0)  == 0)   
        {
           flag = 0;
           alarm(5);
           scanf(" %d", &value);
           flag = 1;
         }
         if(flag == 0) // 5 second out
             break;
    }
    flag = -1;
    alarm(0);
}
Lidong Guo
  • 2,817
  • 2
  • 19
  • 31