Printing while reading characters in C - c

I'm trying to write a simple little snippet of code to respond to an arrow key press.
I know that up is represented by ^[[A, and I have the following code that checks for that sequence:
while( 1 )
{
input_char = fgetc( stdin );
if( input_char == EOF || input_char == '\n' )
{
break;
}
/* Escape sequence */
if( input_char == 27 )
{
input_char = getc( stdin );
if( input_char == '[' )
{
switch( getc( stdin ) )
{
case 'A':
printf("Move up\n");
break;
}
}
}
}
Whenever I hit "up", the escape sequence (^[[A) shows up on the screen, but "Move up" doesn't appear until I hit enter.
The end goal is to replace the text on the current line with some other data, and so I tried to do
printf("\r%s", "New Text");
in place of "Move up", but it still doesn't show up until after enter is pressed.
Is there something wrong with the way I'm reading in characters?
Thanks!
EDIT Quick note, it's for *nix systems.
SOLUTION
Thanks for the pointers everyone. I went with stepanbujnak's solution because it was rather straightforward. The one thing I noticed is that a lot of the behavior of keys that modify the string ( backspace, etc ) is different than you would expect. It will backspace through ANYTHING on the line (including printf'd stuff), and I had to account for that. After that it wasn't too bad getting the rest to fall in line :)

stdin is line buffered and hence getc(stdin) or fgetc(stdin) don't get to see those characters till you press ENTER See this link for more details
EDIT: If you don't want to get into ncurses there are other useful methods like setting the terminal to raw mode etc to overcome this limitation. Check this nice SO post
Capture characters from standard input without waiting for enter to be pressed

You actually only need to disable line buffering using termios
Here's an example of doing it:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
int main() {
struct termios old_term, new_term;
char c;
/* Get old terminal settings for further restoration */
tcgetattr(0, &old_term);
/* Copy the settings to the new value */
new_term = old_term;
/* Disable echo of the character and line buffering */
new_term.c_lflag &= (~ICANON & ~ECHO);
/* Set new settings to the terminal */
tcsetattr(0, TCSANOW, &new_term);
while ((c = getchar()) != 'q') {
printf("You pressed: %c\n", c);
}
/* Restore old settings */
tcsetattr(0, TCSANOW, &old_term);
return 0;
}

Look at the curses library for capturing escape sequences such as arrow keys.
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/keys.html
On most systems keys such as arrow keys, home, page up, break etc are escaped keys they use an escape sequence to discern themselves. Something like 0x1B + Sequence, if you wanted to capture it raw you'd need to read the input directly from the file descriptor and listen for the sequences. The alternative is above using ncurses.
Outside of using curses the following illustrates how to accomplish this using system call(s) such as read
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd = 0x0; /* STDIN */
size_t bytes_read;
char buf[12];
bytes_read = read(fd, buf, 3);
printf("%02x %02x %02x\n", buf[0], buf[1], buf[2]);
return 0;
}
output after pressing UP
Lukes-ASA-Macbook:tmp luke$ gcc -o out test.c
Lukes-ASA-Macbook:tmp luke$ ./out
^[[A
1b 5b 41
This should get you on your way.
You can buffer the input looking for 0x1b and then enable a parsing flag to look for an escape sequence of characters in lieu of single character parsing.

Related

Accepting a single character in C?

What I intend to do is to get the character entered and used it as a pattern. I've tried using getchar() but it won't work. I've hear of using scanf but it skips and stops whenever I press "shift" for the special characters on my keyboard.
int i, j, n;
char c;
c = getchar();
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
getchar();
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You need to assign the value returned by getchar to the variable c, and you had a redundent call to getchar that's why it skips reading the desired input:
int i, j, n;
char c;
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
scanf(" %c", &c);
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You can use %c with scanf:
scanf("%d %c", &n, %c);
This eliminates the need for the two getchar calls.
The space is required; it tells scanf to skip whitespace.
The problem you have is that your assumptions on getchar(3) are incorrect. You think getchar() is going to return the next key pressed in the input stream, but you are incorrectly assuming that it will be done without buffering or system processing (the terminal driver gives the program complete lines, or even worse, if you are reading from a file, complete buffer blocks, that have to be buffered so you miss no characters from the input stream)
You are assuming incorrectly that the end of line you need to press for the input to be feeded to the program does not count in the input stream.
What actually happens is:
you feed a complete line (because the kernel driver works that way) so you press your character, and then you see nothing, not after you have pressed the return key.
once you press it, you have more than one character (depending on how many you pressed before hitting the return key) that will stay in the buffer, until they are so consumed by the program. Normally this happens when you have executed more getchar() or scanf() statements.
The idea of this buffering mechanism is to allow a programmer to process character by charcacter large amounts of text, without the overhead of making a system call per character reading (this is a costly operation) so think of getchar() not as a sample function to get new users introduced to the world of programming, but as a hint to experienced programmers to use efficiently without having to think on buffering large amounts of text.
With stdio package, every character counts, so you have to think slowly and minuciously when you feed input to getchar(3).
The next question is: Right, then how can I solve and stop my program until I press some key? The first answer, with the set of tools you have exposed here is, be careful on what you input, instead of asking for any key, ask the user to press the return key, and then, do something like:
printf("Hit <ENTER> to continue"); fflush(stdout); /* so we get the line out, bypassing the buffering mechanism */
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received */
}
/* c == '\n' || c == EOF, so we can continue */
or, if you prefer, you can write a function just to do this (as there can be so many criteria to implement it, nobody included such a function in the standard C library, my apologies for that. ;) )
void wait_for_enter()
{
/* I use stderr, for two reasons:
* o stderr is normally unbuffered, so there's no need to fflush()
* o stdout can be redirected, so the prompt will not be visible in
* case you want to save the output of your program.
*/
fprintf(stderr, "Hit <ENTER> to continue");
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received
* until we get the end of file (ctrl-d at the terminal)
* or a new line */
}
/* c == '\n' || c == EOF, so we can continue */
/* it's assumed that the user pressed the enter key, so the echoed
* enter already did a newline, no need to do it here */
} /* wait_for_enter */
In order to wait for any character and in raw mode, you need first to ensure your input comes from a terminal (you cannot do the following on a normal file), then you have to switch the terminal driver to raw mode, so each character is given immediately to the program and no line editing processing is done, and then set the stdin descriptor to no buffering at all. Only then, you can receive individual characters with getchar(3), one by one, as they are keyed in. I think this is far out of the scope of this question, as the code to do that is far more complex than the above.
EDIT
Following is a complete sample of a program that uses raw input to process characters as they are keyed in.
/* pru.c -- program to show raw input from the terminal.
* Author: Luis Colorado <luiscoloradourcola#gmail.com>
* Date: Fri Sep 20 08:46:06 EEST 2019
* Copyright: (C) 2019 Luis Colorado. All rights reserved.
* License: BSD.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h> /* see termios(3) for a description on terminal conf */
#define F(_fmt) __FILE__":%d:%s: " _fmt, __LINE__, __func__
/* this function switches the terminal into raw mode and returns a malloc(3)ed
* terminal configuration, so it can be later restored. BEWARE that the returned
* configuration info must be deallocated by free(3) once it's not needed anymore.
* In case of failure of any system call, the function returns NULL, and errno is
* set to the failing cause. */
struct termios *set_raw(int fd)
{
struct termios *ret = malloc(sizeof *ret), cfg;
if (!ret) return NULL;
int res = tcgetattr(fd, &cfg);
if (res < 0) goto error;
*ret = cfg; /* save it for return */
cfmakeraw(&cfg);
/* set it after all buffered characters in the driver have drained out */
res = tcsetattr(fd, TCSADRAIN, &cfg);
if (res < 0) goto error;
return ret;
error:
free(ret);
return NULL;
} /* set_raw */
/* restores the configuration back to the associated file descriptor */
int restore_cfg(int fd, struct termios *cf)
{
/* set it after all buffered characters in the driver have drained out */
return tcsetattr(fd, TCSADRAIN, cf);
} /* restore_cfg */
int main()
{
struct termios *cfg = set_raw(fileno(stdin));
if (!cfg) {
fprintf(stderr, F("stdin: %s\n"),
strerror(errno));
}
setbuf(stdin, NULL); /* stdin unbuffered */
setbuf(stdout, NULL); /* stdout unbuffered */
/* BEWARE that raw mode doesn't process any characters, so no Ctrl-C(interrupt), Ctrl-D(EOF), etc.
* will be available, only if you read from a file, you'll get EOF, but you'll not be able to produce
* that on the terminal, you'll need to swith to another console and kill the process. */
int c;
while ((c = getchar()) != EOF && c != '\033') { /* ESCAPE key('\033') is a safeguard to end input */
/* print the input char as an hex number */
printf("[%02x]", c);
}
if (cfg) { /* if we were able to set the terminal to raw mode */
/* restore config */
restore_cfg(fileno(stdin), cfg);
/* and free it */
free(cfg);
}
exit(EXIT_SUCCESS);
} /* main */
The full source code can be also downloaded from here.
You can use this program to see how input keys get mapped into characters, as you'll note that when you press the enter key, the raw input is [0d] (ascii char 13, CARRY RETURN) while in normal line mode you get '\n' which is [0a] or ASCII LINE FEED, instead (you can check this if you redirect input from the pru.c text file). Also you'll see that you are unable to specify EOF from the terminal driver with Ctrl-D and that Ctrl-C does not come to help. Well, I have included a safeguard, by ending the program in case you press the ESC key, which generates an ASCII ESCAPE character (\033). This is also commented in the source code.
All of this processing is done by the kernel driver, so all unix implementations get the same line end characters or interpret the control characters the same way.

C - Flush stdin input buffer without 'enter' [duplicate]

In the next code:
#include <stdio.h>
int main(void) {
int c;
while ((c=getchar())!= EOF)
putchar(c);
return 0;
}
I have to press Enter to print all the letters I entered with getchar, but I don't want to do this, what I want to do is to press the letter and immediately see the the letter I introduced repeated without pressing Enter. For example, if I press the letter 'a' I want to see an other 'a' next to it, and so on:
aabbccddeeff.....
But when I press 'a' nothing happens, I can write other letters and the copy appears only when I press Enter:
abcdef
abcdef
How can I do this?
I am using the command cc -o example example.c under Ubuntu for compiling.
This depends on your OS, if you are in a UNIX like environment the ICANON flag is enabled by default, so input is buffered until the next '\n' or EOF. By disabling the canonical mode you will get the characters immediately. This is also possible on other platforms, but there is no straight forward cross-platform solution.
EDIT: I see you specified that you use Ubuntu. I just posted something similar yesterday, but be aware that this will disable many default behaviors of your terminal.
#include<stdio.h>
#include <termios.h> //termios, TCSANOW, ECHO, ICANON
#include <unistd.h> //STDIN_FILENO
int main(void){
int c;
static struct termios oldt, newt;
/*tcgetattr gets the parameters of the current terminal
STDIN_FILENO will tell tcgetattr that it should write the settings
of stdin to oldt*/
tcgetattr( STDIN_FILENO, &oldt);
/*now the settings will be copied*/
newt = oldt;
/*ICANON normally takes care that one line at a time will be processed
that means it will return if it sees a "\n" or an EOF or an EOL*/
newt.c_lflag &= ~(ICANON);
/*Those new settings will be set to STDIN
TCSANOW tells tcsetattr to change attributes immediately. */
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
/*This is your part:
I choose 'e' to end input. Notice that EOF is also turned off
in the non-canonical mode*/
while((c=getchar())!= 'e')
putchar(c);
/*restore the old settings*/
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
return 0;
}
You will notice, that every character appears twice. This is because the input is immediately echoed back to the terminal and then your program puts it back with putchar() too. If you want to disassociate the input from the output, you also have to turn of the ECHO flag. You can do this by simply changing the appropriate line to:
newt.c_lflag &= ~(ICANON | ECHO);
On a linux system, you can modify terminal behaviour using the stty command. By default, the terminal will buffer all information until Enter is pressed, before even sending it to the C program.
A quick, dirty, and not-particularly-portable example to change the behaviour from within the program itself:
#include<stdio.h>
#include<stdlib.h>
int main(void){
int c;
/* use system call to make terminal send all keystrokes directly to stdin */
system ("/bin/stty raw");
while((c=getchar())!= '.') {
/* type a period to break out of the loop, since CTRL-D won't work raw */
putchar(c);
}
/* use system call to set terminal behaviour to more normal behaviour */
system ("/bin/stty cooked");
return 0;
}
Please note that this isn't really optimal, since it just sort of assumes that stty cooked is the behaviour you want when the program exits, rather than checking what the original terminal settings were. Also, since all special processing is skipped in raw mode, many key sequences (such as CTRL-C or CTRL-D) won't actually work as you expect them to without explicitly processing them in the program.
You can man stty for more control over the terminal behaviour, depending exactly on what you want to achieve.
getchar() is a standard function that on many platforms requires you to press ENTER to get the input, because the platform buffers input until that key is pressed. Many compilers/platforms support the non-standard getch() that does not care about ENTER (bypasses platform buffering, treats ENTER like just another key).
I/O is an operating system function. In many cases, the operating system won't pass typed character to a program until ENTER is pressed. This allows the user to modify the input (such as backspacing and retyping) before sending it to the program. For most purposes, this works well, presents a consistent interface to the user, and relieves the program from having to deal with this. In some cases, it's desirable for a program to get characters from keys as they are pressed.
The C library itself deals with files, and doesn't concern itself with how data gets into the input file. Therefore, there's no way in the language itself to get keys as they are pressed; instead, this is platform-specific. Since you haven't specified OS or compiler, we can't look it up for you.
Also, the standard output is normally buffered for efficiency. This is done by the C libraries, and so there is a C solution, which is to fflush(stdout); after each character written. After that, whether the characters are displayed immediately is up to the operating system, but all the OSes I'm familiar with will display the output immediately, so that's not normally a problem.
I like Lucas answer, but I would like to elaborate it a bit. There is a built-in function in termios.h named cfmakeraw() which man describes as:
cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]
This basically does the same as what Lucas suggested and more, you can see the exact flags it sets in the man pages: termios(3).
Use case
int c = 0;
static struct termios oldTermios, newTermios;
tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;
cfmakeraw(&newTermios);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);
switch (c) {
case 113: // q
printf("\n\n");
exit(0);
break;
case 105: // i
printf("insert\n");
break;
default:
break;
Since you are working on a Unix derivative (Ubuntu), here is one way to do it - not recommended, but it will work (as long as you can type commands accurately):
echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program
Use interrupt to stop the program when you are bored with it.
sh restore-sanity
The 'echo' line saves the current terminal settings as a shell script that will restore them.
The 'stty' line turns off most of the special processing (so Control-D has no effect, for example) and sends characters to the program as soon as they are available. It means you cannot edit your typing any more.
The 'sh' line reinstates your original terminal settings.
You can economize if 'stty sane' restores your settings sufficiently accurately for your purposes. The format of '-g' is not portable across versions of 'stty' (so what is generated on Solaris 10 won't work on Linux, or vice versa), but the concept works everywhere. The 'stty sane' option is not universally available, AFAIK (but is on Linux).
You could include the 'ncurses' library, and use getch() instead of getchar().
"How to avoid pressing Enter with getchar()?"
First of all, terminal input is commonly either line or fully buffered. This means that the operating system stores the actual input from the terminal in a buffer. Usually, this buffer is flushed to the program when e.g. \n was signalized/provided in stdin. This is f.e. made by a press to Enter.
getchar() is just at the end of the chain. It has no ability to actually influence the buffering process.
"How can I do this?"
Ditch getchar() in the first place, if you don´t want to use specific system calls to change the behavior of the terminal explicitly like well explained in the other answers.
There is unfortunately no standard library function and with that no portable way to flush the buffer at single character input. However, there are implementation-based and non-portable solutions.
In Windows/MS-DOS, there are the getch() and getche() functions in the conio.h header file, which do exactly the thing you want - read a single character without the need to wait for the newline to flush the buffer.
The main difference between getch() and getche() is that getch() does not immediately output the actual input character in the console, while getche() does. The additional "e" stands for echo.
Example:
#include <stdio.h>
#include <conio.h>
int main (void)
{
int c;
while ((c = getche()) != EOF)
{
if (c == '\n')
{
break;
}
printf("\n");
}
return 0;
}
In Linux, a way to obtain direct character processing and output is to use the cbreak() and echo() options and the getch() and refresh() routines in the ncurses-library.
Note, that you need to initialize the so called standard screen with the initscr() and close the same with the endwin() routines.
Example:
#include <stdio.h>
#include <ncurses.h>
int main (void)
{
int c;
cbreak();
echo();
initscr();
while ((c = getch()) != ERR)
{
if (c == '\n')
{
break;
}
printf("\n");
refresh();
}
endwin();
return 0;
}
Note: You need to invoke the compiler with the -lncurses option, so that the linker can search and find the ncurses-library.
yes you can do this on windows too, here's the code below, using the conio.h library
#include <iostream> //basic input/output
#include <conio.h> //provides non standard getch() function
using namespace std;
int main()
{
cout << "Password: ";
string pass;
while(true)
{
char ch = getch();
if(ch=='\r'){ //when a carriage return is found [enter] key
cout << endl << "Your password is: " << pass <<endl;
break;
}
pass+=ch;
cout << "*";
}
getch();
return 0;
}
I've had this problem/question come up in an assignment that I'm currently working on.
It also depends on which input you are grabbing from.
I am using
/dev/tty
to get input while the program is running, so that needs to be the filestream associated with the command.
On the ubuntu machine I have to test/target, it required more than just
system( "stty -raw" );
or
system( "stty -icanon" );
I had to add the --file flag, as well as path to the command, like so:
system( "/bin/stty --file=/dev/tty -icanon" );
Everything is copacetic now.
This code worked for me. Attention : this is not part of the standard library, even if most compilers (I use GCC) supports it.
#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
char a = getch();
printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
return 0;
}
This code detects the first key press.
Can create a new function that checks for Enter:
#include <stdio.h>
char getChar()
{
printf("Please enter a char:\n");
char c = getchar();
if (c == '\n')
{
c = getchar();
}
return c;
}
int main(int argc, char *argv[])
{
char ch;
while ((ch = getChar()) != '.')
{
printf("Your char: %c\n", ch);
}
return 0;
}
By default, the C library buffers the output until it sees a return. To print out the results immediately, use fflush:
while((c=getchar())!= EOF)
{
putchar(c);
fflush(stdout);
}
you can use _getch() instead of getch() from <conio.h>

Wait for user input in C?

I'm trying to make a simple command that pauses for user input. I think it'll be useful in Bash scripts.
Here's my code:
#include <stdio.h>
int main() {
char key[1];
puts("Press any key to continue...");
fgets(key,1,stdin);
}
It doesn't even pause for user input.
I tried earlier to use getch() (ncurses). What happened is, the screen went blank and when I pressed a key, it went back to what was originally on the screen, and I saw:
$ ./pause
Press any key to continue...
$
It's somewhat what I wanted. But all I want is the equivalent of the pause command in DOS/Windows (I use Linux).
From the GNU C Library Manual:
Function: char * fgets (char *s, int count, FILE *stream)
The fgets
function reads characters from the stream stream up to and including a
newline character and stores them in the string s, adding a null
character to mark the end of the string. You must supply count
characters worth of space in s, but the number of characters read is
at most count − 1. The extra character space is used to hold the null
character at the end of the string.
So, fgets(key,1,stdin); reads 0 characters and returns. (read: immediately)
Use getchar or getline instead.
Edit: fgets also doesn't return once count characters are available on the stream, it keeps waiting for a newline and then reads count characters, so "any key" might not be the correct wording in this case then.
You can use this example to avoid line-buffering:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
int mygetch ( void )
{
int ch;
struct termios oldt, newt;
tcgetattr ( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
int main()
{
printf("Press any key to continue.\n");
mygetch();
printf("Bye.\n");
}
I'm not sure how to add a comment under a post so this isn't really an answer but a comment,
In linux Stdin is buffered so you need to flush it which is what pressing 'enter' does on your terminal. It seems you want to read from an unbuffered stream i.e you want to react to a keypress imediately (without the need to explicitly flush it).
You can create your own unbuffered stream using a file discriptor and then read from it using "getc", you may have to use termios to setup unbuffered input as others have suggested.
This is a simple method that worked for me on Windows 10:
#include <stdlib.h>
void pause(void);
int main(void)
{
printf("Testing pause.");
}
void pause(void)
{
system("pause");
}

How to get around no backspace when ICANON in non-canonical

I am using termios as suggested in a previous question I asked but now am asking if there is a way get backspace to work whilst using termios in non-canonical mode. I am using termios to have not have an echo If I use &=ECHO and &=ICANON this is the result I want, the keyboard input is sent to putchar() as soon as the key is press and displayed but the '\b' key is display as hex, if I do the opposite I can't see the text till enter is pressed but '\b' works.
I have looked up the manual and some other forums that and they said " not possible just don't make any mistakes", this would make sense seeing as how when I don't enter my password correctly in in a terminal on Ubuntu I can't backspace and change it. But I was making sure I haven't missed anything in the manual.
Code is to get input from stdin and not display empty lines.
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#define ECHOFLAGS (ECHO)
int setecho(int fd, int onflag);
int first_line(int *ptrc);
int main(void){
struct termios old;
tcgetattr(STDIN_FILENO,&old);
setecho(STDIN_FILENO,0);
int c;
while((c = getchar())!= 4) //no end of file in non-canionical match to control D
first_line(&c);
tcsetattr(STDIN_FILENO,&old);
return 0;
}
int setecho(int fd, int onflag){
int error;
struct termios term;
if(tcgetattr(fd, &term) == -1)
return -1;
if(onflag){ printf("onflag\n");
term.c_lflag &= ECHOFLAGS ; // I know the onflag is always set to 0 just
term.c_lflag &=ICANON; // testing at this point
}
else{ printf("else\n");
term.c_lflag &= ECHO;
term.c_lflag &=ICANON;
}
while (((error = tcsetattr(fd, TCSAFLUSH, &term)) ==-1 && (errno == EINTR)))
return error;
}
int first_line(int *ptrc){
if (*ptrc != '\n' && *ptrc != '\r'){
putchar(*ptrc);
while (*ptrc != '\n'){
*ptrc = getchar();
putchar(*ptrc);
}
}
else return 0;
return 0;
}
Thanks Lachlan
P.S on a side point in my research I noticed someone saying Termios isn't "Standard C" is this because it is system dependant? (only for comments)
How would you expect this to work? If the input characters are sent to your program immediately, then by the time the backspace character is recieved it's simply too late for the terminal to handle backspace - your program has already seen the previous character, so it can't be taken back.
For example, say the user presses A. Your program will receieve 'A' from getchar() and process it. Now the user presses backspace - now what should the terminal do?
So this implies that the only place you can handle backspace in non-canonical mode is in your program itself. When you receive the '\b' character from getchar(), you can handle it specially (just like you have special handling for '\n') - for example, remove the most recently entered character from a buffer.
It's implementation-dependent. On my machine, pressing backspace led to the byte 127 being read by read(). This code worked on my machine.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
#include <stdlib.h>
#define MAXBUFSIZE (10000U)
#define DEL (127)
int main(void) {
char buf[MAXBUFSIZE];
char c;
size_t top;
struct termios curterm;
tcgetattr(STDIN_FILENO, &curterm);
curterm.c_lflag &= ~(ICANON| ECHO);
curterm.c_cc[VTIME] = 0;
curterm.c_cc[VMIN] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &curterm);
top = 0;
while (read(STDIN_FILENO, &c, sizeof c) == 1) {
switch (c) {
case DEL:
if (top) {
--top;
const char delbuf[] = "\b \b";
write(STDOUT_FILENO, delbuf, strlen(delbuf));
}
break;
case '\n':
write(STDOUT_FILENO, &c, sizeof c);
write(STDOUT_FILENO, buf, top);
top = 0;
break;
default:
buf[top++] = c;
write(STDOUT_FILENO, &c, sizeof c);
break;
}
}
return 0;
}
When the user presses backspace the application receives some control code. The application has to interpret the control code and take a character out of its buffer (application buffer since you are not using the kernel buffer). If the application is doing any kind of echoing (eg: echoing stars in place of the password characters) then it will have to send some other control codes to move the cursor left and blank out the last star.
Both the codes received for delete or backspace and the codes you send to move the cursor depend on the type of terminal the user has, so before you can do any of this jiggery-pokery you have to detect the terminal type as well. Most programmers don't want to spend time reading the manual for hundreds of different types of terminal, so they generally use a library that hides all of this from them. One such library is curses.

How to avoid pressing Enter with getchar() for reading a single character only?

In the next code:
#include <stdio.h>
int main(void) {
int c;
while ((c=getchar())!= EOF)
putchar(c);
return 0;
}
I have to press Enter to print all the letters I entered with getchar, but I don't want to do this, what I want to do is to press the letter and immediately see the the letter I introduced repeated without pressing Enter. For example, if I press the letter 'a' I want to see an other 'a' next to it, and so on:
aabbccddeeff.....
But when I press 'a' nothing happens, I can write other letters and the copy appears only when I press Enter:
abcdef
abcdef
How can I do this?
I am using the command cc -o example example.c under Ubuntu for compiling.
This depends on your OS, if you are in a UNIX like environment the ICANON flag is enabled by default, so input is buffered until the next '\n' or EOF. By disabling the canonical mode you will get the characters immediately. This is also possible on other platforms, but there is no straight forward cross-platform solution.
EDIT: I see you specified that you use Ubuntu. I just posted something similar yesterday, but be aware that this will disable many default behaviors of your terminal.
#include<stdio.h>
#include <termios.h> //termios, TCSANOW, ECHO, ICANON
#include <unistd.h> //STDIN_FILENO
int main(void){
int c;
static struct termios oldt, newt;
/*tcgetattr gets the parameters of the current terminal
STDIN_FILENO will tell tcgetattr that it should write the settings
of stdin to oldt*/
tcgetattr( STDIN_FILENO, &oldt);
/*now the settings will be copied*/
newt = oldt;
/*ICANON normally takes care that one line at a time will be processed
that means it will return if it sees a "\n" or an EOF or an EOL*/
newt.c_lflag &= ~(ICANON);
/*Those new settings will be set to STDIN
TCSANOW tells tcsetattr to change attributes immediately. */
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
/*This is your part:
I choose 'e' to end input. Notice that EOF is also turned off
in the non-canonical mode*/
while((c=getchar())!= 'e')
putchar(c);
/*restore the old settings*/
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
return 0;
}
You will notice, that every character appears twice. This is because the input is immediately echoed back to the terminal and then your program puts it back with putchar() too. If you want to disassociate the input from the output, you also have to turn of the ECHO flag. You can do this by simply changing the appropriate line to:
newt.c_lflag &= ~(ICANON | ECHO);
On a linux system, you can modify terminal behaviour using the stty command. By default, the terminal will buffer all information until Enter is pressed, before even sending it to the C program.
A quick, dirty, and not-particularly-portable example to change the behaviour from within the program itself:
#include<stdio.h>
#include<stdlib.h>
int main(void){
int c;
/* use system call to make terminal send all keystrokes directly to stdin */
system ("/bin/stty raw");
while((c=getchar())!= '.') {
/* type a period to break out of the loop, since CTRL-D won't work raw */
putchar(c);
}
/* use system call to set terminal behaviour to more normal behaviour */
system ("/bin/stty cooked");
return 0;
}
Please note that this isn't really optimal, since it just sort of assumes that stty cooked is the behaviour you want when the program exits, rather than checking what the original terminal settings were. Also, since all special processing is skipped in raw mode, many key sequences (such as CTRL-C or CTRL-D) won't actually work as you expect them to without explicitly processing them in the program.
You can man stty for more control over the terminal behaviour, depending exactly on what you want to achieve.
getchar() is a standard function that on many platforms requires you to press ENTER to get the input, because the platform buffers input until that key is pressed. Many compilers/platforms support the non-standard getch() that does not care about ENTER (bypasses platform buffering, treats ENTER like just another key).
I/O is an operating system function. In many cases, the operating system won't pass typed character to a program until ENTER is pressed. This allows the user to modify the input (such as backspacing and retyping) before sending it to the program. For most purposes, this works well, presents a consistent interface to the user, and relieves the program from having to deal with this. In some cases, it's desirable for a program to get characters from keys as they are pressed.
The C library itself deals with files, and doesn't concern itself with how data gets into the input file. Therefore, there's no way in the language itself to get keys as they are pressed; instead, this is platform-specific. Since you haven't specified OS or compiler, we can't look it up for you.
Also, the standard output is normally buffered for efficiency. This is done by the C libraries, and so there is a C solution, which is to fflush(stdout); after each character written. After that, whether the characters are displayed immediately is up to the operating system, but all the OSes I'm familiar with will display the output immediately, so that's not normally a problem.
I like Lucas answer, but I would like to elaborate it a bit. There is a built-in function in termios.h named cfmakeraw() which man describes as:
cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]
This basically does the same as what Lucas suggested and more, you can see the exact flags it sets in the man pages: termios(3).
Use case
int c = 0;
static struct termios oldTermios, newTermios;
tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;
cfmakeraw(&newTermios);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);
switch (c) {
case 113: // q
printf("\n\n");
exit(0);
break;
case 105: // i
printf("insert\n");
break;
default:
break;
Since you are working on a Unix derivative (Ubuntu), here is one way to do it - not recommended, but it will work (as long as you can type commands accurately):
echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program
Use interrupt to stop the program when you are bored with it.
sh restore-sanity
The 'echo' line saves the current terminal settings as a shell script that will restore them.
The 'stty' line turns off most of the special processing (so Control-D has no effect, for example) and sends characters to the program as soon as they are available. It means you cannot edit your typing any more.
The 'sh' line reinstates your original terminal settings.
You can economize if 'stty sane' restores your settings sufficiently accurately for your purposes. The format of '-g' is not portable across versions of 'stty' (so what is generated on Solaris 10 won't work on Linux, or vice versa), but the concept works everywhere. The 'stty sane' option is not universally available, AFAIK (but is on Linux).
You could include the 'ncurses' library, and use getch() instead of getchar().
"How to avoid pressing Enter with getchar()?"
First of all, terminal input is commonly either line or fully buffered. This means that the operating system stores the actual input from the terminal in a buffer. Usually, this buffer is flushed to the program when e.g. \n was signalized/provided in stdin. This is f.e. made by a press to Enter.
getchar() is just at the end of the chain. It has no ability to actually influence the buffering process.
"How can I do this?"
Ditch getchar() in the first place, if you don´t want to use specific system calls to change the behavior of the terminal explicitly like well explained in the other answers.
There is unfortunately no standard library function and with that no portable way to flush the buffer at single character input. However, there are implementation-based and non-portable solutions.
In Windows/MS-DOS, there are the getch() and getche() functions in the conio.h header file, which do exactly the thing you want - read a single character without the need to wait for the newline to flush the buffer.
The main difference between getch() and getche() is that getch() does not immediately output the actual input character in the console, while getche() does. The additional "e" stands for echo.
Example:
#include <stdio.h>
#include <conio.h>
int main (void)
{
int c;
while ((c = getche()) != EOF)
{
if (c == '\n')
{
break;
}
printf("\n");
}
return 0;
}
In Linux, a way to obtain direct character processing and output is to use the cbreak() and echo() options and the getch() and refresh() routines in the ncurses-library.
Note, that you need to initialize the so called standard screen with the initscr() and close the same with the endwin() routines.
Example:
#include <stdio.h>
#include <ncurses.h>
int main (void)
{
int c;
cbreak();
echo();
initscr();
while ((c = getch()) != ERR)
{
if (c == '\n')
{
break;
}
printf("\n");
refresh();
}
endwin();
return 0;
}
Note: You need to invoke the compiler with the -lncurses option, so that the linker can search and find the ncurses-library.
yes you can do this on windows too, here's the code below, using the conio.h library
#include <iostream> //basic input/output
#include <conio.h> //provides non standard getch() function
using namespace std;
int main()
{
cout << "Password: ";
string pass;
while(true)
{
char ch = getch();
if(ch=='\r'){ //when a carriage return is found [enter] key
cout << endl << "Your password is: " << pass <<endl;
break;
}
pass+=ch;
cout << "*";
}
getch();
return 0;
}
I've had this problem/question come up in an assignment that I'm currently working on.
It also depends on which input you are grabbing from.
I am using
/dev/tty
to get input while the program is running, so that needs to be the filestream associated with the command.
On the ubuntu machine I have to test/target, it required more than just
system( "stty -raw" );
or
system( "stty -icanon" );
I had to add the --file flag, as well as path to the command, like so:
system( "/bin/stty --file=/dev/tty -icanon" );
Everything is copacetic now.
This code worked for me. Attention : this is not part of the standard library, even if most compilers (I use GCC) supports it.
#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
char a = getch();
printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
return 0;
}
This code detects the first key press.
Can create a new function that checks for Enter:
#include <stdio.h>
char getChar()
{
printf("Please enter a char:\n");
char c = getchar();
if (c == '\n')
{
c = getchar();
}
return c;
}
int main(int argc, char *argv[])
{
char ch;
while ((ch = getChar()) != '.')
{
printf("Your char: %c\n", ch);
}
return 0;
}
By default, the C library buffers the output until it sees a return. To print out the results immediately, use fflush:
while((c=getchar())!= EOF)
{
putchar(c);
fflush(stdout);
}
you can use _getch() instead of getch() from <conio.h>

Resources