I'm using Linux console and I would like to do a program which outputs random characters until ESC is pressed. How can I make such a keyboard handler?
The line discipline for a terminal device often works in canonical mode by default. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enter key is pressed).
You can set the terminal into raw (non-canonical) mode by using tcsetattr() to manipulate the termios structure. Clearing the ECHO and ICANON flags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIME and VMIN to zero in the c_cc array causes the read request (fgetc()) to return immediately rather than block; effectively polling stdin. The call to fgetc() will return EOF if a character is not available in the stream.
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
int getkey() {
int character;
struct termios orig_term_attr;
struct termios new_term_attr;
/* set the terminal to raw mode */
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
new_term_attr.c_cc[VTIME] = 0;
new_term_attr.c_cc[VMIN] = 0;
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
/* read a character from the stdin stream without blocking */
/* returns EOF (-1) if no character is available */
character = fgetc(stdin);
/* restore the original terminal attributes */
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return character;
}
int main()
{
int key;
/* initialize the random number generator */
srand(time(NULL));
for (;;) {
key = getkey();
/* terminate loop on ESC (0x1B) or Ctrl-D (0x04) on STDIN */
if (key == 0x1B || key == 0x04) {
break;
}
else {
/* print random ASCII character between 0x20 - 0x7F */
key = (rand() % 0x7F);
printf("%c", ((key < 0x20) ? (key + 0x20) : key));
}
}
return 0;
}
Note: This code omits error checking for simplicity.
change the tty settings for one key press:
int getch(void) {
int c=0;
struct termios org_opts, new_opts;
int res=0;
//----- store old settings -----------
res=tcgetattr(STDIN_FILENO, &org_opts);
assert(res==0);
//---- set new terminal parms --------
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
c=getchar();
//------ restore old settings ---------
res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
assert(res==0);
return(c);
}
getch() from Curses library perhaps? Also, you will need to use notimeout() to tell getch() not to wait for next keypress.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
char * me = "Parent";
void sigkill(int signum)
{
//printf("=== %s EXIT SIGNAL %d ===\n", me, signum);
exit(0);
}
main()
{
int pid = fork();
signal(SIGINT, sigkill);
signal(SIGQUIT, sigkill);
signal(SIGTERM, sigkill);
if(pid == 0) //IF CHILD
{
int ch;
me = "Child";
while(1)
{
ch = (rand() % 26) + 'A'; // limit range to ascii A-Z
printf("%c",ch);
fflush(stdout); // flush output buffer
sleep(2); // don't overwhelm
if (1 == getppid())
{
printf("=== CHILD EXIT SINCE PARENT DIED ===\n");
exit(0);
}
}
printf("==CHILD EXIT NORMAL==\n");
}
else //PARENT PROCESS
{
int ch;
if((ch = getchar())==27)
kill(pid, SIGINT);
//printf("==PARENT EXIT NORMAL (ch=%d)==\n", ch);
}
return(0);
}
In this program u will only need to press enter after esc char,because getchar()is a blocking function.
Also u may remove or decrease sleep time for child process as ur need.
Related
I am writing a small C program that asks a user for a password, as the user enters characters via keyboard, these characters will be displayed as asterisks and when they hit enter the actual password is displayed. I have tried fiddling with getchar() and assigning variables but I am not getting the desired solution. Any guidance is appreciated, thanks.
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
char *get_password();
int main() {
struct termios info;
tcgetattr(0, &info);
// info.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &info);
printf("Create a password: ");
int c = getchar();
c = '*';
char *password = get_password();
printf("You entered: %s\n", password);
tcgetattr(0, &info);
info.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &info);
}
#define BUFSIZE 100
char buf[BUFSIZE];
char *get_password() {
int c, len = 0;
while ((c = getchar()) != EOF && c != '\n') {
buf[len++] = c;
if (len == BUFSIZE - 1)
break;
}
buf[len] = 0;
putchar('\n');
return buf;
}
As #chqrlie points out, proper password processing requires that echoing be disabled and buffering as well. Here are the quick steps to do that:
#include <termios.h>
#include <sys/ioctl.h>
struct termios termstat;
tcgetattr(STDIN_FILENO,&termstat);
termstat.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);
then after you're done with the password handling, restore the normal behavior with:
termstat.c_lflag |= (ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);
Reading a password without echo is a bit more tricky than just fiddling with getchar():
you must disable the buffering in stdin or read bytes directly from the low level system handle 0*;
you must temporarily disable the terminal echo and buffering features, using system dependent API calls;
to avoid leaving the terminal in a bad state, you should also disable signal processing and handle the signal characters explicitly;
upon reading the bytes from handle 0, you must output stars and flush the output to the terminal;
you should handle the backspace key as users will expect to be able to correct typing errors;
you must restore the terminal state after input completes.
*) Reading from system handle 2 (stderr) is a useful alternative to get the password from the user even if stdin is redirected from a file.
Here is a commented implementation:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
char *get_password(void) {
struct termios info, save;
static char buf[100];
size_t len = 0;
char c;
int res;
tcgetattr(0, &info);
save = info;
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
info.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
INLCR | IGNCR | ICRNL | IXON);
/* output modes - disable post processing */
info.c_oflag &= ~(OPOST);
/* local modes - echoing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
info.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
/* control modes - set 8 bit chars, disable parity handling */
info.c_cflag &= ~(CSIZE | PARENB);
info.c_cflag |= CS8;
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
info.c_cc[VMIN] = 1; /* 1 byte */
info.c_cc[VTIME] = 0; /* no timer */
tcsetattr(0, TCSANOW, &info);
for (;;) {
fflush(stdout);
/* read a single byte from stdin, bypassing stream handling */
res = read(0, &c, 1);
if (res != 1) {
/* special case EINTR and restart */
if (res == -1 && errno == EINTR)
continue;
/* other cases: read failure or end of file */
break;
}
if (c == '\n' || c == '\r') {
/* user hit enter */
break;
}
if (c == '\b' || c == 127) {
/* user hit the backspace or delete key */
if (len > 0) {
printf("\b \b");
len--;
}
continue;
}
if (c == 3) {
/* user hit ^C: should abort the program */
tcsetattr(0, TCSANOW, &save);
printf("^C\n");
fflush(stdout);
return NULL;
}
if (c < ' ') {
/* ignore other control characters */
continue;
}
if (len >= sizeof(buf) - 1) {
putchar(7); /* beep */
} else {
putchar('*');
buf[len++] = c;
}
}
tcsetattr(0, TCSANOW, &save);
putchar('\n');
fflush(stdout);
buf[len] = '\0';
return buf;
}
int main() {
char *password;
printf("Create a password: ");
password = get_password();
if (password) {
printf("You entered: %s\n", password);
} else {
printf("You hit ^C, program aborted\n");
return 1;
}
return 0;
}
#include <stdio.h>
/* copy input to output */
main()
{
int c;
c = getchar();
while(c != EOF)
{
putchar(c);
c = getchar();
}
return 0;
}
In the given code, the program displays the input character.
It reads every character one by one (into the variable 'c') and outputs the same read characters simultaneously. The program terminates when the EOF character is given as input.
When I ran the code in my IDE (Code::Blocks 16.01) and input a string,
eg: Hi! My name is C.\n
The output is displayed after '\n' and not simultaneously.
Isn't the output supposed to be as - "HHii!! MMyy nnaammee iiss CC.."?
Bold letters indicate the output.
include:
#include <unistd.h>
#include <termios.h>
before your code in main insert:
struct termios term, term_orig;
if(tcgetattr(STDIN_FILENO, &term_orig)) {
printf("tcgetattr failed\n");
return -1;
}
term = term_orig;
term.c_lflag &= ~ICANON;
if (tcsetattr(STDIN_FILENO, TCSANOW, &term)) {
printf("tcsetattr failed\n");
return -1;
}
after your code insert:
if (tcsetattr(STDIN_FILENO, TCSANOW, &term_orig)) {
printf("tcsetattr failed\n");
return -1;
}
Because by default, input from terminal is line-buffered. So your program get a whole line after you press Enter.
To disable buffering in Unix & Linux systems, try this:
#include <unistd.h>
#include <termios.h>
int disableLineBuffer(void){
struct termios term, oldterm;
if (tcgetattr(STDIN_FILENO, &term)) return -1;
oldterm = term;
term.c_lflag &= ~(ECHO | ICANON);
if (tcsetattr(STDIN_FILENO, TCSANOW, &term)) return -1;
return 0;
}
In Windows, go and do this instead:
#include <windows.h>
BOOL WINAPI DisableLineBuffer(void){
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
if (!GetConsoleMode(hInput, &mode)) return FALSE;
mode &= ~ENABLE_LINE_INPUT;
if (!SetConsoleMode(hInput, mode)) return FALSE;
return TRUE;
}
Be sure to revert changes to the console before your program exits, or you may have trouble doing anything else in your terminal.
The user entries different characters: uppercase and lowercase letters and numbers. Entering ends with a period (.) The program should then calculate and print sum of all entered numbers, and ignore everything else. Program won't end with a period (.)
#include <stdio.h>
int main() {
int s=0, a;
char n;
printf("Unesite znakove: ");
for ( ; ; ) {
scanf ("%c", &n);
if (n!='.') {
a=(int)n;
if (a>47 && a<58) {
a=a-48;
s+=a;
}
else continue;
}
else {
break;
}
}
printf ("\nSuma je: %d", s);
return 0;
}
stdin is usually line buffered.
Code does not see any input until '\n' is entered. Entering "123." is not enough. Code needs a final Enter: '\n' (or closure of stdin).
the stdin is being input in 'cooked' mode, which means nothing is actually visible to the program until either EOF or is entered by the user.
If you want the data to be available to the program, keystroke by keystroke, then the terminal needs to be in 'raw' mode
the following code demonstrates how to set the terminal into 'raw' mode
Note: code swiped from: http://www.minek.com/files/unix_examples/raw.html
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
struct termios oldtermios;
int ttyraw(int fd)
{
/* Set terminal mode as follows:
Noncanonical mode - turn off ICANON.
Turn off signal-generation (ISIG)
including BREAK character (BRKINT).
Turn off any possible preprocessing of input (IEXTEN).
Turn ECHO mode off.
Disable CR-to-NL mapping on input.
Disable input parity detection (INPCK).
Disable stripptcsetattr(fd, TCSAFLUSH, &newtermios)ing of eighth bit on input (ISTRIP).
Disable flow control (IXON).
Use eight bit characters (CS8).
Disable parity checking (PARENB).
Disable any implementation-dependent output processing (OPOST).
One byte at a time input (MIN=1, TIME=0).
*/
struct termios newtermios;
if(tcgetattr(fd, &oldtermios) < 0)
return(-1);
memcpy( newtermios, oldtermios, sizeof(struct termios) );
newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* OK, why IEXTEN? If IEXTEN is on, the DISCARD character
is recognized and is not passed to the process. This
character causes output to be suspended until another
DISCARD is received. The DSUSP character for job control,
the LNEXT character that removes any special meaning of
the following character, the REPRINT character, and some
others are also in this category.
*/
newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* If an input character arrives with the wrong parity, then INPCK
is checked. If this flag is set, then IGNPAR is checked
to see if input bytes with parity errors should be ignored.
If it shouldn't be ignored, then PARMRK determines what
character sequence the process will actually see.
When we turn off IXON, the start and stop characters can be read.
*/
newtermios.c_cflag &= ~(CSIZE | PARENB);
/* CSIZE is a mask that determines the number of bits per byte.
PARENB enables parity checking on input and parity generation
on output.
*/
newtermios.c_cflag |= CS8;
/* Set 8 bits per character. */
newtermios.c_oflag &= ~(OPOST);
/* This includes things like expanding tabs to spaces. */
newtermios.c_cc[VMIN] = 1;
newtermios.c_cc[Vtcsetattr(fd, TCSAFLUSH, &newtermios)TIME] = 0;
/* You tell me why TCSAFLUSH. */
if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0)
return(-1);
return(0);
}
int ttyreset(int fd)
{
if(tcsetattr(fd, TCSAFLUSH, &oldtermios) < 0)
return(-1);
return(0);
}
void sigcatch(int sig)
{
ttyreset(0);
exit(0);
}
void main()
{
int i;
char c;
/* Catch the most popular signals. */
if((int) signal(SIGINT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int)signal(SIGQUIT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int) signal(SIGTERM,sigcatch) < 0)
{
perror("signal");
exit(1);
}
/* Set raw mode on stdin. */
if(ttyraw(0) < 0)
{
fprintf(stderr,"Can't go to raw mode.\n");
exit(1);
}
while( (i = read(0, &c, 1)) == 1)
{
if( (c &= 255) == 0177) /* ASCII DELETE */
break;
printf( "%o\n\r", c);
}
if(ttyreset(0) < 0)
{
fprintf(stderr, "Cannot reset terminal!\n");
exit(-1);
}tcsetattr(fd, TCSAFLUSH, &newtermios)
if( i < 0)
{
fprintf(stderr,"Read error.\n");
exit(-1);
}
exit(0);
}
the main criteria is to declare a struct termios, use tcgetattr(fd, &oldtermios) to acquire the current terminal settings, (for safety, now make a copy of the original terminal settings.) copy oldtermios to a newtermios and modify the newtermios settings for the desired mode, then use tcsetattr(fd, TCSAFLUSH, &newtermios) to modify the terminal to the 'raw' mode.
When done, be sure to call tcsetattr(fd, TCSAFLUSH, &oldtermios); to return the terminal mode to 'cooked'
Note: signal() is obsolete/unreliable, replace with sigaction()
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);
}
while(ch != 'q')
{
printf("looping\n");
sleep(1);
if(kbhit())
{
ch = readch();
printf("you hit %c\n",ch);
}
}
This code gives me a blocking getch() like functionality. I am trying to use this code to capture up down arrow keys.
Added:
Trying to capture key codes of up arrow gives me 3 chars 27, 91 and 65.
Using if/else I am trying pattern matching but I only get 2 chars. Next one is captured when next key is pressed.
I want to capture full words using getchar() while always looking for certain keys all the time(esc, del etc.).
I can't reproduce Your problem:
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include "kbhit.h" /* http://linux-sxs.org/programming/kbhit.html */
int main(){
init_keyboard();
char ch='x';
while( ch != 'q' ){
printf("looping\n");
sleep(1);
if( kbhit() ){
printf("you hit");
do{
ch = readch();
printf(" '%c'(%i)", isprint(ch)?ch:'?', (int)ch );
}while( kbhit() );
puts("");
}
}
close_keyboard();
}
This example could help:
raw.c - raw mode demonstration
/* Raw mode demo */
/* See exactly what is being transmitted from the terminal. To do this
we have to be more careful. */
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
struct termios oldtermios;
int ttyraw(int fd)
{
/* Set terminal mode as follows:
Noncanonical mode - turn off ICANON.
Turn off signal-generation (ISIG)
including BREAK character (BRKINT).
Turn off any possible preprocessing of input (IEXTEN).
Turn ECHO mode off.
Disable CR-to-NL mapping on input.
Disable input parity detection (INPCK).
Disable stripping of eighth bit on input (ISTRIP).
Disable flow control (IXON).
Use eight bit characters (CS8).
Disable parity checking (PARENB).
Disable any implementation-dependent output processing (OPOST).
One byte at a time input (MIN=1, TIME=0).
*/
struct termios newtermios;
if(tcgetattr(fd, &oldtermios) < 0)
return(-1);
newtermios = oldtermios;
newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* OK, why IEXTEN? If IEXTEN is on, the DISCARD character
is recognized and is not passed to the process. This
character causes output to be suspended until another
DISCARD is received. The DSUSP character for job control,
the LNEXT character that removes any special meaning of
the following character, the REPRINT character, and some
others are also in this category.
*/
newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* If an input character arrives with the wrong parity, then INPCK
is checked. If this flag is set, then IGNPAR is checked
to see if input bytes with parity errors should be ignored.
If it shouldn't be ignored, then PARMRK determines what
character sequence the process will actually see.
When we turn off IXON, the start and stop characters can be read.
*/
newtermios.c_cflag &= ~(CSIZE | PARENB);
/* CSIZE is a mask that determines the number of bits per byte.
PARENB enables parity checking on input and parity generation
on output.
*/
newtermios.c_cflag |= CS8;
/* Set 8 bits per character. */
newtermios.c_oflag &= ~(OPOST);
/* This includes things like expanding tabs to spaces. */
newtermios.c_cc[VMIN] = 1;
newtermios.c_cc[VTIME] = 0;
/* You tell me why TCSAFLUSH. */
if(tcsetattr(fd, TCSAFLUSH, &newtermios) < 0)
return(-1);
return(0);
}
int ttyreset(int fd)
{
if(tcsetattr(fd, TCSAFLUSH, &oldtermios) < 0)
return(-1);
return(0);
}
void sigcatch(int sig)
{
ttyreset(0);
exit(0);
}
int main()
{
int i;
char c;
/* Catch the most popular signals. */
if((int) signal(SIGINT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int)signal(SIGQUIT,sigcatch) < 0)
{
perror("signal");
exit(1);
}
if((int) signal(SIGTERM,sigcatch) < 0)
{
perror("signal");
exit(1);
}
/* Set raw mode on stdin. */
if(ttyraw(0) < 0)
{
fprintf(stderr,"Can't go to raw mode.\n");
exit(1);
}
while( (i = read(0, &c, 1)) == 1)
{
if( (c &= 255) == 0177) /* ASCII DELETE */
break;
printf( "%o\n\r", c);
}
if(ttyreset(0) < 0)
{
fprintf(stderr, "Cannot reset terminal!\n");
exit(-1);
}
if( i < 0)
{
fprintf(stderr,"Read error.\n");
exit(-1);
}
return 0;
}
(backspace to stop the demo, origin)