I was testing this code from the GNU libc manual:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int
main (void)
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if (c == '\004') /* C-d */
break;
else
putchar (c);
}
return EXIT_SUCCESS;
}
Even though the terminal was set to be in non-canonical mode, I still need to press enter for input to be received.
However, if I change: putchar(c)
to write(STDOUT_FILENO, &c, sizeof(char))
, it works properly as I thought.
You are being trumped by user buffering! putchar(3)
is part of libc I/O API, while write(2)
is a system call -- well, not quite a system call, but for simplicity's sake, let's consider it is for now.
There are three types of buffering in libc: unbuffered, block buffered, and line buffered. If a stream is unbuffered, data goes to the underlying file (or terminal) as soon as it is written; if it's block buffered, data is saved in the memory block until it fills up and then it's written all at once; however, if it's line buffered, data is transmitted to the file (or terminal) when a newline character is found.
If a stream is connected to a terminal, as normally is the case of standard output, it's line buffered. So, this is your case: when you press enter, the newline character \n
causes the (line) buffer to be written to standard output. However, when you call write(2)
, libc user buffering is bypassed and data is written to the corresponding file descriptor (STDOUT_FILENO).
So, as I said earlier, write(2)
is a system call; but in truth, when you call write
, you are calling a library wrapper to the system call, which handles the strict protocol the system call follows (e.g., where it expects arguments, etc).
By the way, everything I said here can be found in the man pages for putchar(3)
, write(2)
, setbuf(3)
. The numbers in parentheses refer to the section in the manual: 2
is for system calls, 3
is for library functions (man man
should give you a list of sections and their topic).
Hope it helps.