Creating stdin without blocking

I have an exercise in which I need to print a file slowly (1 second intervals) until the file runs out if the user types a character.

While the program displays the file in one period of time, which is fine, but when I type in a character, nothing happens. I guess I'm somehow choosing the wrong one.

This is the last program I sent.

#include <stdio.h> #include <stdlib.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main(void) { FILE* infile; char str[100]; fd_set readset; struct timeval tv; // open a file if((infile = fopen("infile", "r")) == NULL) { (void)printf("Couldn't open the file\n"); exit(1); } // file was opened successfully else { // while we are not at the end of a file while(fgets(str, 100, infile) != NULL) { FD_ZERO(&readset); FD_SET(fileno(stdin), &readset); // set the time value to 1 second tv.tv_sec = 1; tv.tv_usec = 0; select(fileno(infile)+1, &readset, NULL, NULL, &tv); // the user typed a character so exit if(FD_ISSET(fileno(stdin), &readset)) { fclose(infile); exit(0); } // the user didn't type a character so print the next line else { fgets(str, 100, stdin); puts(str); } } // clean up fclose(infile); } // report success return 0; } 

Thanks for the help!

+4
source share
4 answers

This is the working version using tcgetattr / tcsetattr:

 #include <stdio.h> #include <stdlib.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main(void) { FILE* infile; char str[100]; fd_set readset; struct timeval tv; struct termios ttystate, ttysave; // open a file if((infile = fopen("infile", "r")) == NULL) { (void)printf("Couldn't open the file\n"); exit(1); } // file was opened successfully //get the terminal state tcgetattr(STDIN_FILENO, &ttystate); ttysave = ttystate; //turn off canonical mode and echo ttystate.c_lflag &= ~(ICANON | ECHO); //minimum of number input read. ttystate.c_cc[VMIN] = 1; //set the terminal attributes. tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); // while we are not at the end of a file while(fgets (str, 100, infile)) { // set the time value to 1 second tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&readset); FD_SET(fileno(stdin), &readset); select(fileno(stdin)+1, &readset, NULL, NULL, &tv); // the user typed a character so exit if(FD_ISSET(fileno(stdin), &readset)) { fgetc (stdin); // discard character break; } // the user didn't type a character so print the next line else { puts(str); // not needed: sleep(1); } } // clean up fclose(infile); ttystate.c_lflag |= ICANON | ECHO; //set the terminal attributes. tcsetattr(STDIN_FILENO, TCSANOW, &ttysave); // report success return 0; } 

sleep(1); no longer required.

+6
source

The terminal is a buffer line. It does not send text to the program until you press Enter . There may be a way to disable terminal line buffering, but I suppose this is beyond your scope.

It stops when you press Enter . However, he does not leave immediately. This is what you want to fix. Get rid of this sleep(1) .

Now your program spam text! You gave select one second timeout, right?

 // set the time value to 1 second tv.tv_sec = 1; tv.tv_usec = 0; 

The reason the timeout is not saved is because select changes the timeout value. From the man page :

On Linux, select () changes the timeout to display the amount of time it did not sleep; most other implementations do not. (POSIX.1-2001 allows either behavior.) This causes problems both when using Linux code that reads the timeout, it is ported to other operating systems, and when the code is ported to Linux, which reuses struct timeval for several select () s in a loop without reinitialization. Consider the undefined timeout after select () returns.

You will need to initialize the timeval before each call to select, not just once at the beginning of the program.

+3
source

You want to make your program multithreaded. Create a stream that prints a file every 1 second, and your main stream will receive input from stdin, and then signal another stream to stop printing whenever you get input

0
source

Part of your problem is that you are using sleep(1) , which will cause this line to execute a full second to execute. If the user types a character, he will have to wait until a full second before your program answers. Therefore, even after you receive the non-blocking part, you will still have problems.

The solution is to use nanosleep or usleep to pause the program for less than 1 second. My recommendation would be to sleep 1/100 of a second * using one of these functions) and check the keystroke every time. For the 100th time, output the next part of the file. Thus, the file still has the correct speed, but the user can stop it when he wants, and the program will respond very quickly to their command.

0
source

All Articles