I've got a loop running that increments the value of a variable each iteration, and I want to be able to hit a key on the keyboard to stop the loop and report the final value of the variable. Thing is, I can't figure out how to do this in C. I feel stupid because it seems like I'm overlooking some really simple and easy way of doing this, but everything I try stops the loop until I hit a key on the keyboard, which is essentially the exact opposite of what I want.
Essentially what I want to do is something like this:
while (key is not pressed)
increment value
print final value
Does that make sense? Anyways, any tips on how to do this in C?
If you are trying to read a single character at a time (without pressing enter) from a terminal in linux then you will need to set the terminal for un-buffered input.
See this example:
Non buffered getc(3) under GNU/Linux
If you are permitted to specify what is pressed and you are on a POSIX.1-compliant system, you could set up a signal handler to catch SIGINT (sent by Ctrl+C). Have your handler change the value of a variable such that you fall out of the while loop.
If you choose this approach, be careful. If an incorrect implementation results in an infinite loop and a caught SIGINT, you will not be able to terminate your program via Ctrl+C. You would need to use kill(1) to terminate your program in this case.
If you're working on windows, and are using MSVC, you might want getch() and kbhit(), something like so
#include <conio.h>
while( looping ) {
// do regular loop stuff
// check if a key is hit, w/o blocking, using kbhit()
if( kbhit() ) {
// only runs when user has hit a key
// so display stuff here,
// and wait for permission to resume with getch()
getch();
}
}
It depends on your platform. The C language doesn't define stuff like this.
Windows? linux? (gnome app? kde app? terminal?) something else?
This is one place where the C standard leaves programmers hanging out to dry. The most portable solution to this problem is to do your I/O using the curses library, which handles so-called "raw" keyboard input (which is what you want) plus a whole lot more. The learning curve is a little steep, but there are good tutorials, especially in the BSD programmers' documentation.
The function you are calling to read a character is blocking. If there is no character there you want it to return instead.
Magic search terms here are non-blocking and non/unbufferred input.
Why would counting in loop until somebody press the key make the sense?
You if you realy want something like than check the timings for like 10k iterrations.
Then make waiting in non blocking itteration whit sleep(). When you get the time you can use it to approximate what the "final value" is
Depending upon the system and how it works, you could try to use the select function with the STDIN as a file handle. You can set the time on the select statement to zero to poll to see if there is data or set it to a time to wait.
You can look at the link http://www.gnu.org/s/libc/manual/html_node/Waiting-for-I_002fO.html for an example of using the select statement with sockets.
I have modified that example to use STDIN as the file descriptor. The function will return a 0 if there is no pending input, 1 if there is pending input (i.e. someone hit a key on the keyboard of input), or -1 if there was an error of some nature
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
int waitForKBInput(unsigned int seconds)
{
/* File descriptor set on which to wait */
fd_set set;
/* time structure which indicate the amount of time to
wait. 0 will perform a poll */
struct timeval timeout;
/* Initialize the file descriptor set. */
FD_ZERO (&set);
/* Use the Standard Input as the descriptor on which
to wait */
FD_SET (STDIN, &set);
/* Initialize the timeout data structure. */
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
/* select returns 0 if timeout, 1 if input available, -1 if error. */
/* and is only waiting on the input selection */
return select (FD_SETSIZE,
&set, NULL, NULL,
&timeout));
}
I know this will not work on VMS system, as I tried this and they implemented the Select and STDIN differently so it would not work (had to use other means to detect keyboard input).
For Visual C/C++ could could use the function kbhit which would indicate if there is keyboard inputs to be read.
I will try to look not at the verbatim question that you are asking, but at your intent - apparently you want to present the result of computation, triggered by some user reaction.
Checking 'is the key pressed?' at each and every iteration is fairly wasteful - your CPU could spend all that time doing more useful things.
So, the best approach here is using signals, in my opinion - namely, SIGINT that is triggered when you hit "Ctrl-C". Here's a code that will print the value of the variable when you hit the Ctrl-C, and will exit after you do it three times:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
long globalvar = 0;
int interrupts_before_exit = 3;
void ctrl_c_handler(int x) {
printf("Value of the variable: %ld\n", globalvar);
if(--interrupts_before_exit) {
printf("Press Ctrl-C %d more times to stop\n", interrupts_before_exit);
} else {
printf("Computation interrupted!\n");
exit(0);
}
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = ctrl_c_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGINT, &act, NULL) >= 0) {
while (1) {
/* The work happens here */
globalvar++;
}
}
exit(1);
}
You can pass the signals between the programs, so in fact you could fork the program that does the "work", and then the second program would lazily monitor the keyboard - and as soon as the key is pressed there it would send the signal to the first one, which would print the result.
Some people have mentioned blocking. In *nix environments you can try setting the standard input file descriptor to non-blocking mode. Here is an example:
/* testbrk
Test breaking an infinite loop with a keystroke.
Hit the <Enter> key to break this loop.
*/
/* Include block */
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*---testbrk main loop---*/
int main ( int argc , char ** argv )
{
/* Variables: file flags , return value , buffer */
int f ;
ssize_t r ;
char b ;
/*
Switch standard input to non-blocking mode:
-Get existing flags from the file descriptor
-Bitwise OR to activate non-blocking bit
-Apply new flags
*/
if ( f = fcntl( STDIN_FILENO , F_GETFL , 0 ) == -1 )
{
perror ( "fcntl F_GETFL" ) ;
exit ( EXIT_FAILURE ) ;
}
f |= O_NONBLOCK ;
if ( f = fcntl( STDIN_FILENO , F_SETFL , f ) == -1 )
{
perror ( "fcntl F_SETFL" ) ;
exit ( EXIT_FAILURE ) ;
}
/*
Infinite loop
-try reading from standard input directly via its file
descriptor , NOT the standard input stream
-if the number of bytes returned is > zero then break
-if bytes returned is -1 then check errno for EAGAIN,
meaning that read would block but the file is nonblocking
-if zero is returned then print message immediately
*/
while ( (r = read( STDIN_FILENO , &b , sizeof( b ) )) < 1 )
{
if ( r == -1 && errno != EAGAIN )
{
perror ( "read" ) ;
exit ( EXIT_FAILURE ) ;
}
fprintf ( stderr , "\nInfinite loop" ) ;
} /* inf loop */
/* Done */
printf ( "\n\nBroken loop\n\n" ) ;
exit ( EXIT_SUCCESS ) ;
} /* testbrk */
Related
Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.
Good day,
I'm writing my own shell in C for my school which has to resemble bash as closely as possible.
I have to handle signals such as Ctrl-\ and Ctrl-C as bash does; for this reason I'm allowed to use signal function. It works fine, but the thing is whenever a Ctrl-C signal is caught (starting from the second catch), a ^C is printed.
On the net, I've found a workaround suggesting printing "\b \b\b \b\nminishell$ " whenever a Ctrl-C is caught, which will devour the two symbols. The thing is, since at the very first time ^C is not printed, the print devours two symbols of my prompting, making it just minishell instead of minishell$ , with the cursor incorrectly displayed.
Now I've come up with another workaround for this workaround which is to declare a static boolean to not print the baskspaces at the very first call. This doesn't help in case of Ctrl-\ though; Ctrl-\ proceeds to move my cursor to right when I attempt to write the two whitespaces that must replace the ^\.
I don't like these workarounds and would like to know whether there is a way to instruct the terminal not to output this stuff? I'm allowed to use tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs, tcsetattr, tcgetattr, have read their man pages but nothing seems to be helpful.
When you type a key on a terminal, two things happen
the character is echoed (displayed) on this terminal
the character is sent (over the line) to the attached program
Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)
Demonstration: using tcsetattr() to control the echoing of the terminal:
#include <stdio.h>
#include <stdlib.h>
#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>
struct termios termios_save;
void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}
sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}
int main (void)
{
int rc;
int ch;
struct termios termios_new;
rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }
rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }
termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }
signal(SIGINT, handle_the_stuff);
printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}
exit (0);
}
The way to set the console such a SW may intercept all typed chars is to set the terminal in RAW MODE. The problems this way may present are that all keys that aren't in the ASCII 0-255 space, such as è, ì, à will be received from the console as a bytes sequence and all the function and control keys included cursors and backspace will not accomplish any action, some code such as CR, LF and some ANSI sequence may accomplish actions when are read from the input channel and rewritten on the output channel.
To set the terminal in raw mode you have to use the function cfmakeraw followed by the function tcsetattr.
The code below implements a simple but not very good implemented terminal, anyway I think this code is a good point to start. In any case, the code flow and the error control must be at least better arranged.
The code writes all sequence of ASCII char that enter into the console when a key is typed. All chars that have value smaller then 32 or greater then 126 will be written as [HEX-CODE]
I.E. hitting Esc on the console will be written [1B], the code of Ctrl+C will be written as [03], F1 will be [1B]OP, F11 will be [1B][23~, Enter will be [0D].
If you will hit Ctrl+X [18] will be written and the program stops, but this behaviour is under SW control as you can see in the code.
Here the code:
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <sys/ioctl.h> // Used for TCGETS2, which is required for custom baud rates
#include <sys/select.h> // might be used to manage select
int setAttr(int ch, int resetToOld);
#define IN 0
#define OUT 1
typedef struct TermCap
{
int fd;
struct termios oldTermios;
struct termios newTermios;
// fd_set fds; // might be used to manage select
} TermCap;
TermCap m_termCap[2];
int main()
{
int i,ex=0;
char msg;
char buff[20];
m_termCap[IN].fd=STDIN_FILENO;
m_termCap[OUT].fd=STDOUT_FILENO;
// Gets STDIN config and set raw config
setAttr(IN,0);
// Gets STDOUT config and set raw config
setAttr(OUT,0);
// Console loop ... the console terminates when ^X is intercepted.
do {
do {
i=read(m_termCap[IN].fd,&msg,1);
if (i>0){
if (msg<32 || msg>126) {
sprintf(buff,"[%02X]",(unsigned char)msg);
write(m_termCap[OUT].fd,buff,4);
if (msg==24)
ex=1;
}else{
write(m_termCap[OUT].fd,&msg,i);
}
}
usleep(10000); // a minimal delay of 10 millisec
} while(i>0 && !ex);
} while(!ex);
// Reset console to initial state.
setAttr(IN,1);
setAttr(OUT,1);
printf("\r\n\nThe end!");
return 0;
}
int setAttr(int ch, int resetToOld)
{
int retVal=0;
int i;
if (!resetToOld) {
// Read old term config
i=tcgetattr(m_termCap[ch].fd, &m_termCap[ch].oldTermios);
if (i==-1) {
return 1;
}
}
m_termCap[ch].newTermios = m_termCap[ch].oldTermios;
if (!resetToOld) {
// Terminal in raw mode
cfmakeraw(&m_termCap[ch].newTermios);
}
i=tcsetattr(m_termCap[ch].fd, TCSANOW, &m_termCap[ch].newTermios);
if (i==-1) {
retVal = 2;
}
return retVal;
}
Wouldn't this work?
void signalHandler(int signo){
if(signo==SIGINT){
printf("\b\b \b\b");
fflush(NULL);
printf("\nHello World\n");
}
}
In my shell it seems to work fine. The first printf and fflush is what you have to implement in your handler. The printf after that is just a way for me to show you that you can, then, do whatever you want after the ^C not appearing.
Why does this make it not appear? In the first printf I erase the characters by using backspaces and spaces. As stdout is buffered by default and I didn't want to use a newline character, I flushed the buffer manually.
I need a command in cmd that works like pause but I can code to continue.
e.g.
system("pause");
some lines of code;`
The problem with system("pause") is that "some lines of code" will not work until the user press sth.
I want to continue cmd with some command.
I want something that run the code but update cmd only when I give the
permission to it.
If I understand correctly, the code shall produce output which you don't want to be shown before you press a key. If you don't mind to have the output paged, you could use something like
FILE *stream = popen("PAUSE<CON&&MORE", "w");
and let the code output to stream (with fprintf(stream, ...) etc.).
Don't ever use system() if you can avoid it. It's crude, error-prone, and non-portable.
C11 introduces threading support, including thrd_sleep(). That should be your preferred solution (if supported by your compiler setup).
If your compiler vendor does not support C11, bugger him about it. That standard is almost four years old now.
WinAPI defines the Sleep() function:
VOID WINAPI Sleep(
_In_ DWORD dwMilliseconds
);
This function causes a thread to relinquish the remainder of its time
slice and become unrunnable for an interval based on the value of
dwMilliseconds.
#include <windows.h>
int main()
{
Sleep( 5000 ); // pause execution for at least 5 seconds
some_lines_of_code;
return 0;
}
I think what you're looking for is a method to check if stdin contains data ready to read; you want to use some non-blocking or asynchronous I/O so that you can read input when it becomes available, and perform other tasks until then.
You won't find a whole heap about non-blocking/asynchronous I/O in standard C, but in POSIX C you can set STDIN_FILENO as non-blocking using fcntl. As an example, here's a program which prompts you to press enter (like pause does) and busy-loops, allowing your code to conduct other (preferably non-blocking) actions inside the loop while it waits for the keystroke (ahemm, byte, since stdin is technically a file):
#include <stdio.h>
#include <fcntl.h>
int main(void) {
char c;
puts("Press any key to continue...");
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK);
while (read(STDIN_FILENO, 1, &c) != 1 && errno == EAGAIN) {
/* code in here will execute repeatedly until a key is struck or a byte is sent */
errno = 0;
}
if (errno) {
/* code down here will execute when an input error occurs */
}
else {
/* code down here will execute when that precious byte is finally sent */
}
}
That's non-blocking I/O. Other alternatives include using asynchronous I/O or extra threads. You should probably use non-blocking I/O or asynchronous I/O (i.e. epoll or kqueue) for this task in particular; using extra threads just to determine when a character is sent to stdin is likely a little bit too hefty.
I have a infinite loop like the following one, and within this loop, I want to continuously check the keyboard to see if the escape key (ESC) has been pressed or not. If it is pressed, then the loop should be broken. How I can do this in C? (I am using gcc, and do access to pthreads as well in case this must be done via threads)
while(1){
//do something
//check for the ESC key
}
This is heavily system dependent. In Unix/Linux systems, the default terminal handler gathers lines and only notifies the program when a full line is available (after Enter is hit.) If you instead want keystrokes immediately, you need to put the terminal into non-canonical mode:
#include <termios.h>
struct termios info;
tcgetattr(0, &info); /* get current terminal attirbutes; 0 is the file descriptor for stdin */
info.c_lflag &= ~ICANON; /* disable canonical mode */
info.c_cc[VMIN] = 1; /* wait until at least one keystroke available */
info.c_cc[VTIME] = 0; /* no timeout */
tcsetattr(0, TCSANOW, &info); /* set immediately */
Once you've done that, you can use any calls that read from stdin and they will return keys without waiting for the end of the line. You can in addition set c_cc[VMIN] = 0 to cause it to not wait for keystrokes at all when you read from stdin.
If, however, you're reading stdin with stdio FILE related calls (getchar, etc), setting VMIN = 0 will make it think you've reached EOF whenever there are no keys available, so you'll have to call clearerr after that happens to try to read more characters. You can use a loop like:
int ch;
while((ch = getchar()) != 27 /* ascii ESC */) {
if (ch < 0) {
if (ferror(stdin)) { /* there was an error... */ }
clearerr(stdin);
/* do other stuff */
} else {
/* some key OTHER than ESC was hit, do something about it? */
}
}
After you're done, you probably want to be sure to set the terminal back into canonical mode, lest other programs (such as your shell) get confused:
tcgetattr(0, &info);
info.c_lflag |= ICANON;
tcsetattr(0, TCSANOW, &info);
There are also other things you can do with tcsetattr -- see then manual page for details. One thing that might suffice for your purposes is setting an alternative EOL character.
If the main job you're doing can be placed within this main loop, you could go for using STDIN in non-blocking mode. You still have a problem with the terminal which does line-buffering normally. You shall put the terminal to raw mode as well.
What about using Ctrl-C (interrupt)?
Non-blocking means that the read() system call always returns immediately even if there are no new bytes in the file. On Linux/Unix you can make STDIN nonblocking this way:
#include <unistd.h>
#include <fcntl.h>
fcntl(0, F_SETFL, O_NONBLOCK); /* 0 is the stdin file decriptor */
This is what you want:
#include <stdio.h>
#include <conio.h>
void main() {
int c;
while((c = getch()) != EOF )
if(c == 27) break;
/* 27 is the ASCII code for Esc */
}
Platform: Linux 3.2.0 x86 (Debian 7)
Compiler: GCC 4.7.2 (Debian 4.7.2-5)
I am writing a function that reads a single character from stdin if a character is already present in stdin. If stdin is empty the function is suppose to do nothing and return -1. I googled nonblocking input and was pointed to poll() or select(). First I tried to use select() but I could not get it to work so I tried poll() and reached the same conclusion. I am not sure what these functions do exactly but from what I understand of poll()'s documentation if I call it like so:
struct pollfd pollfds;
pollfds = STDIN_FILENO;
pollfds.events = POLLIN;
poll(pollfds, 1, 0);
if(pollfds.revents & POLLIN) will be true if "Data other than high-priority data may be read without blocking.". But poll() always times out in my test situation. How I test the function could be the problem but the functionality I want is exactly what I am testing for. Here is the function currently and the test situation as well.
#include <poll.h>
#include <stdio.h>
#include <unistd.h>
int ngetc(char *c)
{
struct pollfd pollfds;
pollfds.fd = STDIN_FILENO;
pollfds.events = POLLIN;
poll(&pollfds, 1, 0);
if(pollfds.revents & POLLIN)
{
//Bonus points to the persons that can tell me if
//read() will change the value of '*c' if an error
//occurs during the read
read(STDIN_FILENO, c, 1);
return 0;
}
else return -1;
}
//Test Situation:
//Try to read a character left in stdin by an fgets() call
int main()
{
int ret = 0;
char c = 0;
char str[256];
//Make sure to enter more than 2 characters so that the excess
//is left in stdin by fgets()
fgets(str, 2, stdin);
ret = ngetc(&c);
printf("ret = %i\nc = %c\n", ret, c);
return 0;
}
You're doing IO incorrectly, the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE *s and file descriptors. You have very blatantly broken this rule. This rule is in place because FILE *s use buffering an this means that after a call to fgets there will be nothing left for read to get because fgets already read all pending data into a buffer that is kept in the FILE * structure.
So since there's no way to check if an ISO C IO method will block, we have to use file descriptors only.
Since we know that STDIN_FILENO is just the number 0, we can use
fcntl (0, F_SETFL, O_NONBLOCK);
this will turn all reads on file descriptor 0 to non-blocking mode, if you want to use a different file descriptor so that you can leave 0 alone then just use dup to duplicate it.
This way, you can stay away from poll completely and implement ngetc as
ssize_t
ngetc (char *c)
{
return read (0, c, 1);
}
or better yet, a macro
#define ngetc(c) (read (0, (c), 1))
Thus you get a simple implementation for what you're looking for.
Edit: If you are still worried about the terminal buffering the input, you can always change the terminal's settings, see How to disable line buffering of input in xterm from program? for more information on how to do this.
Edit: The reason that one could not use fgetc instead of read is for the same reason that using fgets won't work. When one of the FILE * IO functions is run, it reads all the data from the associated file descriptor. But once that happens, poll will never return because it's waiting on a file descriptor that's always empty, and the same thing will happen with read. Thus, I suggest that you follow the advice of the documentation and never mix streams (IO using fgets, fgetc, etc.) and file descriptors (IO using read, write, etc.)
There are two problems in your code.
According to manual of poll, assigning 0 to timeout will return immediately
If the value of timeout is 0, poll() shall return immediately. If the value of timeout is -1, poll() shall block until a requested event occurs or until the call is interrupted.
fgets does not do what you expect, it is from stdio library and will buffer reads. Suppose you entered 3 letters and press enter, after fgets, the third letter won't be available to poll.
So comment out the fgets line and assign -1 to timeout in poll, and run it again to see if that's what you want.
I did not get the expected behavior with the answer above, and I actually had to take into account this answer as well
which set the TTY in non canonical mode.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
int main(int argc, char *argv[])
{
struct termios t;
tcgetattr(0, &t);
t.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &t);
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
printf("Starting loop (press i or q)...\n");
for (int i = 0; ; i++) {
char c = 0;
read (0, &c, 1);
switch (c) {
case 'i':
printf("\niteration: %d\n", i);
break;
case 'q':
printf("\n");
exit(0);
}
}
return 0;
}