Display something every x seconds with different output - c

I want to output the ASCII Code of the last key I pressed, every x second.
As example:
If i press a(97), the terminal should show the 97 every x second. When I now press the w(119), the program now should print the 119 instead of the 97.
So far my program just prints the first key I've pressed.
Here are the main and the other method:
int main(int argc, char const *argv[]){
printf("Hello World!");
while(1){
movePlayer();
fflush(stdout);
sleep(1);
}
return 0;
}
void movePlayer(){
system("/bin/stty raw");
int input = getchar(); //support_readkey(1000);
//fprintf(stdout, "\033[2J");
//fprintf(stdout, "\033[1;1H");
printf("\b%d",input);
system("/bin/stty cooked");
}
EDIT:
With a little bit of testing i've now a method which solves my problem
int read_the_key(int timeout_ms) {
struct timeval tv = { 0L, timeout_ms * 1000L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
int r = select(1, &fds, NULL, NULL, &tv);
if (!r) return 0;
return getchar();
}

getchar() waits for only one character, so this:
while(1){
movePlayer(); // getchar() and printf() here
fflush(stdout);
sleep(1);
}
causes this behavior. You read one character, you print it in movePlayer(). Then you flush the output buffer and go to sleep. Then you just repeat, which means that you have to input again.
Store the input and print it again, if you wish. However, your function will always wait for new input to arrive.
Here is an attempt with read() as suggested, but it will have similar behavior to your code as it is now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int old_c = -1;
char c[1] = {0};
void movePlayer();
int main(int argc, char const *argv[]){
while(1) {
movePlayer();
fflush(stdout);
sleep(1);
}
return 0;
}
void movePlayer(){
system("/bin/stty raw");
if(read(STDIN_FILENO, c, sizeof(c)) > 0)
old_c = (int)c[0];
if(old_c == -1)
old_c = (int)c[0];
printf("\b%d", old_c);
system("/bin/stty cooked");
}
Please read read() from stdin to go on. You can tell read() for how many characters to wait and then return, but how will you know if the user intends to input a new character to command read() to wait for user's input?
As a result, I would say that you can not do what you wish, at least as far as I can tell, with a simple approach. You could have your program feed the stale input to stdin, so that your program has the impression it reads user's input. However, in case the user actually inputs fresh input, your program should handle that case carefully.

You can setup SIGALARM handler, setup alarm after x seconds and display what getchar returns in handler

Related

How to start counting 5 seconds before exiting from program?

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;
}
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.

Exit a program using a specific keypress combination? [duplicate]

I want to capture the Ctrl+D signal in my program and write a signal handler for it.
How can I do that?
I am working on C and using a Linux system.
As others have already said, to handle Control+D, handle "end of file"s.
Control+D is a piece of communication between the user and the pseudo-file that you see as stdin. It does not mean specifically "end of file", but more generally "flush the input I typed so far". Flushing means that any read() call on stdin in your program returns with the length of the input typed since the last flush. If the line is nonempty, the input becomes available to your program although the user did not type "return" yet. If the line is empty, then read() returns with zero, and that is interpreted as "end of file".
So when using Control+D to end a program, it only works at the beginning of a line, or if you do it twice (first time to flush, second time for read() to return zero).
Try it:
$ cat
foo
(type Control-D once)
foofoo (read has returned "foo")
(type Control-D again)
$
Ctrl+D is not a signal, it's EOF (End-Of-File). It closes the stdin pipe. If read(STDIN) returns 0, it means stdin closed, which means Ctrl+D was hit (assuming there is a keyboard at the other end of the pipe).
A minimalistic example:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
int main(){
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
signal( SIGINT, sig_hnd );
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios);
char line[256]; int len;
do{
len=read(0,line,256); line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) printf("(VEOF)");
if( len >0 ){
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
}
}while( line[0] != 'q' );
tcsetattr(0,TCSANOW,&old_termios);
}
The program change the VEOF char (from Ctrl-D) to Ctrl-C and the VINTR char (from Ctrl-C) to Ctrl-D. If You press Ctrl-D then the terminal driver will send a SIGINT to the signal handler of the program.
Note: pressing VINTR will erase the terminal input buffer so You can not read the characters typed in the line before the VINTR key pressed.
There's no need to process signals.
You need to ensure ISIG is not set on the terminal flags, that's all.
Here's a complete contained example using select to avoid blocking on stdin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>
#define STDIN_FILENO 0
struct termios org_opts;
/** Select to check if stdin has pending input */
int pending_input(void) {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
struct termios new_opts;
tcgetattr(STDIN_FILENO, &org_opts);
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
/** Shutdown terminal mode */
void reset_terminal(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}
/** Return next input or -1 if none */
int next_input(void) {
if (!pending_input())
return -1;
int rtn = fgetc(stdin);
printf("Found: %d\n", rtn);
return(rtn);
}
int main()
{
setup_terminal();
printf("Press Q to quit...\n");
for (;;) {
int key = next_input();
if (key != -1) {
if ((key == 113) || (key == 81)) {
printf("\nNormal exit\n");
break;
}
}
}
reset_terminal();
return 0;
}
Output:
doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113
Normal exit
NB. 3 is control C and 4 is control D; 26 is control z. 113 is 'q'.
See: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters for a full table.
As far as I know Ctrl+D is translated by the system to end of standard input so your app won't get any signal.
I think that the only way to intercept Ctrl+D is to work directly with the system api (like accessing tty)
You can use poll() and watch for POLLHUP on fd #1, because the TTY layer translates ^D to EOF.
Ctrl + D value in ascci table is 4 and is a non printable characters. So your can capture it in a terminal with the following code.
When getline function get Ctrl + D an error occur and return value is -1.
Your can make a condition on the return value.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *buf = malloc(sizeof(char) * 500);
size_t size = 500;
int nb = getline(&buf, &size, stdin);
if (nb == -1)
printf("CTRL + D captured\n");
free(buf);
return (0);
}
You can check if stdin is not of with the feof method like so:
if (feof(stdin))
{
// some code
exit(0);
}
See this for more details/
According to the man page, getline() will return -1 on failure to read a line (including the end-of-file condition).
This means:
When getline() encounters a failure it will return -1
When getline() reads CTRL+D it will return -1
Since getline() returns a value of type ssize_t which is defined as a signed integer value, you can construct your code in such a way to test for the -1 value which will be returned in the event of an end-of-file condition or failure to read a line.
#include <stdio.h>
#include <stdlib>
int main(void)
{
char *buffer = NULL;
size_t bufsize = 0;
ssize_t characters;
characters = getline(&buffer, &bufsize, stdin);
if (characters == -1)
{
printf("You entered CTRL+D\n");
}
free(buffer);
return (0);
}

How to flush EOF and enable scanf to get new input without starting over the program in C [duplicate]

I want to capture the Ctrl+D signal in my program and write a signal handler for it.
How can I do that?
I am working on C and using a Linux system.
As others have already said, to handle Control+D, handle "end of file"s.
Control+D is a piece of communication between the user and the pseudo-file that you see as stdin. It does not mean specifically "end of file", but more generally "flush the input I typed so far". Flushing means that any read() call on stdin in your program returns with the length of the input typed since the last flush. If the line is nonempty, the input becomes available to your program although the user did not type "return" yet. If the line is empty, then read() returns with zero, and that is interpreted as "end of file".
So when using Control+D to end a program, it only works at the beginning of a line, or if you do it twice (first time to flush, second time for read() to return zero).
Try it:
$ cat
foo
(type Control-D once)
foofoo (read has returned "foo")
(type Control-D again)
$
Ctrl+D is not a signal, it's EOF (End-Of-File). It closes the stdin pipe. If read(STDIN) returns 0, it means stdin closed, which means Ctrl+D was hit (assuming there is a keyboard at the other end of the pipe).
A minimalistic example:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
int main(){
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
signal( SIGINT, sig_hnd );
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios);
char line[256]; int len;
do{
len=read(0,line,256); line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) printf("(VEOF)");
if( len >0 ){
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
}
}while( line[0] != 'q' );
tcsetattr(0,TCSANOW,&old_termios);
}
The program change the VEOF char (from Ctrl-D) to Ctrl-C and the VINTR char (from Ctrl-C) to Ctrl-D. If You press Ctrl-D then the terminal driver will send a SIGINT to the signal handler of the program.
Note: pressing VINTR will erase the terminal input buffer so You can not read the characters typed in the line before the VINTR key pressed.
There's no need to process signals.
You need to ensure ISIG is not set on the terminal flags, that's all.
Here's a complete contained example using select to avoid blocking on stdin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>
#define STDIN_FILENO 0
struct termios org_opts;
/** Select to check if stdin has pending input */
int pending_input(void) {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
struct termios new_opts;
tcgetattr(STDIN_FILENO, &org_opts);
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
/** Shutdown terminal mode */
void reset_terminal(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}
/** Return next input or -1 if none */
int next_input(void) {
if (!pending_input())
return -1;
int rtn = fgetc(stdin);
printf("Found: %d\n", rtn);
return(rtn);
}
int main()
{
setup_terminal();
printf("Press Q to quit...\n");
for (;;) {
int key = next_input();
if (key != -1) {
if ((key == 113) || (key == 81)) {
printf("\nNormal exit\n");
break;
}
}
}
reset_terminal();
return 0;
}
Output:
doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113
Normal exit
NB. 3 is control C and 4 is control D; 26 is control z. 113 is 'q'.
See: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters for a full table.
As far as I know Ctrl+D is translated by the system to end of standard input so your app won't get any signal.
I think that the only way to intercept Ctrl+D is to work directly with the system api (like accessing tty)
You can use poll() and watch for POLLHUP on fd #1, because the TTY layer translates ^D to EOF.
Ctrl + D value in ascci table is 4 and is a non printable characters. So your can capture it in a terminal with the following code.
When getline function get Ctrl + D an error occur and return value is -1.
Your can make a condition on the return value.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *buf = malloc(sizeof(char) * 500);
size_t size = 500;
int nb = getline(&buf, &size, stdin);
if (nb == -1)
printf("CTRL + D captured\n");
free(buf);
return (0);
}
You can check if stdin is not of with the feof method like so:
if (feof(stdin))
{
// some code
exit(0);
}
See this for more details/
According to the man page, getline() will return -1 on failure to read a line (including the end-of-file condition).
This means:
When getline() encounters a failure it will return -1
When getline() reads CTRL+D it will return -1
Since getline() returns a value of type ssize_t which is defined as a signed integer value, you can construct your code in such a way to test for the -1 value which will be returned in the event of an end-of-file condition or failure to read a line.
#include <stdio.h>
#include <stdlib>
int main(void)
{
char *buffer = NULL;
size_t bufsize = 0;
ssize_t characters;
characters = getline(&buffer, &bufsize, stdin);
if (characters == -1)
{
printf("You entered CTRL+D\n");
}
free(buffer);
return (0);
}

How to stop Ctrl-D from exiting in my own shell [duplicate]

I want to capture the Ctrl+D signal in my program and write a signal handler for it.
How can I do that?
I am working on C and using a Linux system.
As others have already said, to handle Control+D, handle "end of file"s.
Control+D is a piece of communication between the user and the pseudo-file that you see as stdin. It does not mean specifically "end of file", but more generally "flush the input I typed so far". Flushing means that any read() call on stdin in your program returns with the length of the input typed since the last flush. If the line is nonempty, the input becomes available to your program although the user did not type "return" yet. If the line is empty, then read() returns with zero, and that is interpreted as "end of file".
So when using Control+D to end a program, it only works at the beginning of a line, or if you do it twice (first time to flush, second time for read() to return zero).
Try it:
$ cat
foo
(type Control-D once)
foofoo (read has returned "foo")
(type Control-D again)
$
Ctrl+D is not a signal, it's EOF (End-Of-File). It closes the stdin pipe. If read(STDIN) returns 0, it means stdin closed, which means Ctrl+D was hit (assuming there is a keyboard at the other end of the pipe).
A minimalistic example:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
int main(){
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
signal( SIGINT, sig_hnd );
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios);
char line[256]; int len;
do{
len=read(0,line,256); line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) printf("(VEOF)");
if( len >0 ){
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
}
}while( line[0] != 'q' );
tcsetattr(0,TCSANOW,&old_termios);
}
The program change the VEOF char (from Ctrl-D) to Ctrl-C and the VINTR char (from Ctrl-C) to Ctrl-D. If You press Ctrl-D then the terminal driver will send a SIGINT to the signal handler of the program.
Note: pressing VINTR will erase the terminal input buffer so You can not read the characters typed in the line before the VINTR key pressed.
There's no need to process signals.
You need to ensure ISIG is not set on the terminal flags, that's all.
Here's a complete contained example using select to avoid blocking on stdin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>
#define STDIN_FILENO 0
struct termios org_opts;
/** Select to check if stdin has pending input */
int pending_input(void) {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
struct termios new_opts;
tcgetattr(STDIN_FILENO, &org_opts);
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
/** Shutdown terminal mode */
void reset_terminal(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}
/** Return next input or -1 if none */
int next_input(void) {
if (!pending_input())
return -1;
int rtn = fgetc(stdin);
printf("Found: %d\n", rtn);
return(rtn);
}
int main()
{
setup_terminal();
printf("Press Q to quit...\n");
for (;;) {
int key = next_input();
if (key != -1) {
if ((key == 113) || (key == 81)) {
printf("\nNormal exit\n");
break;
}
}
}
reset_terminal();
return 0;
}
Output:
doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113
Normal exit
NB. 3 is control C and 4 is control D; 26 is control z. 113 is 'q'.
See: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters for a full table.
As far as I know Ctrl+D is translated by the system to end of standard input so your app won't get any signal.
I think that the only way to intercept Ctrl+D is to work directly with the system api (like accessing tty)
You can use poll() and watch for POLLHUP on fd #1, because the TTY layer translates ^D to EOF.
Ctrl + D value in ascci table is 4 and is a non printable characters. So your can capture it in a terminal with the following code.
When getline function get Ctrl + D an error occur and return value is -1.
Your can make a condition on the return value.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *buf = malloc(sizeof(char) * 500);
size_t size = 500;
int nb = getline(&buf, &size, stdin);
if (nb == -1)
printf("CTRL + D captured\n");
free(buf);
return (0);
}
You can check if stdin is not of with the feof method like so:
if (feof(stdin))
{
// some code
exit(0);
}
See this for more details/
According to the man page, getline() will return -1 on failure to read a line (including the end-of-file condition).
This means:
When getline() encounters a failure it will return -1
When getline() reads CTRL+D it will return -1
Since getline() returns a value of type ssize_t which is defined as a signed integer value, you can construct your code in such a way to test for the -1 value which will be returned in the event of an end-of-file condition or failure to read a line.
#include <stdio.h>
#include <stdlib>
int main(void)
{
char *buffer = NULL;
size_t bufsize = 0;
ssize_t characters;
characters = getline(&buffer, &bufsize, stdin);
if (characters == -1)
{
printf("You entered CTRL+D\n");
}
free(buffer);
return (0);
}

How to capture Control+D signal?

I want to capture the Ctrl+D signal in my program and write a signal handler for it.
How can I do that?
I am working on C and using a Linux system.
As others have already said, to handle Control+D, handle "end of file"s.
Control+D is a piece of communication between the user and the pseudo-file that you see as stdin. It does not mean specifically "end of file", but more generally "flush the input I typed so far". Flushing means that any read() call on stdin in your program returns with the length of the input typed since the last flush. If the line is nonempty, the input becomes available to your program although the user did not type "return" yet. If the line is empty, then read() returns with zero, and that is interpreted as "end of file".
So when using Control+D to end a program, it only works at the beginning of a line, or if you do it twice (first time to flush, second time for read() to return zero).
Try it:
$ cat
foo
(type Control-D once)
foofoo (read has returned "foo")
(type Control-D again)
$
Ctrl+D is not a signal, it's EOF (End-Of-File). It closes the stdin pipe. If read(STDIN) returns 0, it means stdin closed, which means Ctrl+D was hit (assuming there is a keyboard at the other end of the pipe).
A minimalistic example:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
int main(){
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
signal( SIGINT, sig_hnd );
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios);
char line[256]; int len;
do{
len=read(0,line,256); line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) printf("(VEOF)");
if( len >0 ){
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
}
}while( line[0] != 'q' );
tcsetattr(0,TCSANOW,&old_termios);
}
The program change the VEOF char (from Ctrl-D) to Ctrl-C and the VINTR char (from Ctrl-C) to Ctrl-D. If You press Ctrl-D then the terminal driver will send a SIGINT to the signal handler of the program.
Note: pressing VINTR will erase the terminal input buffer so You can not read the characters typed in the line before the VINTR key pressed.
There's no need to process signals.
You need to ensure ISIG is not set on the terminal flags, that's all.
Here's a complete contained example using select to avoid blocking on stdin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>
#define STDIN_FILENO 0
struct termios org_opts;
/** Select to check if stdin has pending input */
int pending_input(void) {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
struct termios new_opts;
tcgetattr(STDIN_FILENO, &org_opts);
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
/** Shutdown terminal mode */
void reset_terminal(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}
/** Return next input or -1 if none */
int next_input(void) {
if (!pending_input())
return -1;
int rtn = fgetc(stdin);
printf("Found: %d\n", rtn);
return(rtn);
}
int main()
{
setup_terminal();
printf("Press Q to quit...\n");
for (;;) {
int key = next_input();
if (key != -1) {
if ((key == 113) || (key == 81)) {
printf("\nNormal exit\n");
break;
}
}
}
reset_terminal();
return 0;
}
Output:
doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113
Normal exit
NB. 3 is control C and 4 is control D; 26 is control z. 113 is 'q'.
See: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters for a full table.
As far as I know Ctrl+D is translated by the system to end of standard input so your app won't get any signal.
I think that the only way to intercept Ctrl+D is to work directly with the system api (like accessing tty)
You can use poll() and watch for POLLHUP on fd #1, because the TTY layer translates ^D to EOF.
Ctrl + D value in ascci table is 4 and is a non printable characters. So your can capture it in a terminal with the following code.
When getline function get Ctrl + D an error occur and return value is -1.
Your can make a condition on the return value.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *buf = malloc(sizeof(char) * 500);
size_t size = 500;
int nb = getline(&buf, &size, stdin);
if (nb == -1)
printf("CTRL + D captured\n");
free(buf);
return (0);
}
You can check if stdin is not of with the feof method like so:
if (feof(stdin))
{
// some code
exit(0);
}
See this for more details/
According to the man page, getline() will return -1 on failure to read a line (including the end-of-file condition).
This means:
When getline() encounters a failure it will return -1
When getline() reads CTRL+D it will return -1
Since getline() returns a value of type ssize_t which is defined as a signed integer value, you can construct your code in such a way to test for the -1 value which will be returned in the event of an end-of-file condition or failure to read a line.
#include <stdio.h>
#include <stdlib>
int main(void)
{
char *buffer = NULL;
size_t bufsize = 0;
ssize_t characters;
characters = getline(&buffer, &bufsize, stdin);
if (characters == -1)
{
printf("You entered CTRL+D\n");
}
free(buffer);
return (0);
}

Resources