Ctrl+D doesn't stop application from executing in command window - c

I've written a program to encrypt a given message by XOR. It works, but It doesn't end. Here is the code.(I have created 3 files):
encrypt.h :
void encrypt(char *message);
message_hider.c:
#include <stdio.h>
#include "encrypt.h"
int main() {
char msg[80];
while (fgets(msg, 80, stdin)){
encrypt(msg);
printf("%s", msg);
}
return 0;
}
encrypt.c :
#include "encrypt.h"
void encrypt(char *message) {
while (*message) {
*message++ ^= 0x1f;
}
}
As I mentioned above, It works. but I can't stop it. When I pressed Ctrl+D to stop it (in cmd) It encrypts it also.(I need this code stop after it encrypt a message). Please explain me about this case.

When I pressed Ctrl+D to stop it (in cmd)
If that's the cmd from Windows you probably want Ctrl+Z.

Ctrl-D is used for the console EOF on Unix systems.
Ctrl-Z is used for the console EOF on Windows systems.

isprint() can help:
#include <stdio.h>
#include <ctype.h>
void encrypt(char *message)
{
while (*message) {
*message = *message ^ 31;
message++;
}
}
int main(void)
{
char msg[80];
while (fgets(msg, 80, stdin) != NULL) {
if (!isprint((unsigned char)*msg)) break;
encrypt(msg);
printf("%s", msg);
}
return 0;
}

Add an exit condition:
if( c < 0x20 ) break;
You may need to add other checks also to support backspace without encoding it...
http://www.asciitable.com/

Just run
$> kill -l
To see the list of signals in Linux. You will not find SIGKILL (Ctrl + D) signal there :(
Ctrl + D is SIGKILL (0) signal in Linux which is not documented anywhere.
Ctrl + Z is for Windows which tell EOF and we need to press "Enter" to close.

Related

How to process KEY_HOME and KEY_END reliably under ncurses

I am struggling to have ncurses generate KEY_HOME or KEY_END events, instead the raw escape sequence is coming through as a sequence of characters.
The following simple C program illustrates the problem:
#define _XOPEN_SOURCE 700
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
void clean(void)
{
echo();
nl();
nocbreak();
endwin();
}
int main(int argc, char *argv[])
{
setvbuf(stderr, NULL, _IONBF, 0);
initscr();
cbreak();
nonl();
noecho();
atexit(clean);
keypad(stdscr, TRUE);
clear();
refresh();
int ch = getch();
if (ch == ERR)
errx(EXIT_FAILURE, "getch");
warnx("read: %x", ch);
halfdelay(1);
while((ch = getch()) != ERR)
{
warnx("read: %x", ch);
}
exit(EXIT_SUCCESS);
}
Compile with -lncurses, and redirect stderr to a log file.
When pressing HOME:
test: read: 1b
test: read: 5b
test: read: 31
test: read: 7e
When pressing UP
test: read: 103
How come HOME and END (and indeed F1 etc.) are not parsed by ncurses into KEY_HOME?
You probably have set TERM to a value which does not match the terminal's behavior. For instance, the linux terminal description has khome=\E[1~ (which corresponds to the example output), while xterm has khome=\E[OH. You can see this using
infocmp linux xterm | grep khome
If the terminal description does not match the actual behavior, ncurses will not match the incoming bytes, and will behave as shown.

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 do I port this program from conio to curses?

I wrote this simple program on Windows. Since Windows has conio, it worked just fine.
#include <stdio.h>
#include <conio.h>
int main()
{
char input;
for(;;)
{
if(kbhit())
{
input = getch();
printf("%c", input);
}
}
}
Now I want to port it to Linux, and curses/ncurses seems like the right way to do it. How would I accomplish the same using those libraries in place of conio?
#include <stdio.h>
#include <ncurses.h>
int main(int argc, char *argv)
{
char input;
initscr(); // entering ncurses mode
raw(); // CTRL-C and others do not generate signals
noecho(); // pressed symbols wont be printed to screen
cbreak(); // disable line buffering
while (1) {
erase();
mvprintw(1,0, "Enter symbol, please");
input = getch();
mvprintw(2,0, "You have entered %c", input);
getch(); // press any key to continue
}
endwin(); // leaving ncurses mode
return 0;
}
When building your program do not forget to link with ncurses lib (-L lncurses) flag to gcc
gcc -g -o sample sample.c -L lncurses
And here you can see kbhit() implementation for linux.

Resources