Reading Raw Input

Switching between cooked and raw modes requires access to the terminal’s configuration data. In Linux, these settings are manipulated at the command prompt by using the stty ccommand. In a C program, you use various functions available in the standard library and defined in the termios.h header file.

I’ve written about modifying the terminal configuration before, specifically to suppress a terminal’s character echo. The process involves obtaining the current terminal configuration, duplicating it, then modifying the duplicate to reflect new settings. The original terminal configuration can then be restored.

The tcgetattr() function obtains the current terminal configuration. Here’s the man page format:

int tcgetattr(int fd, struct termios *termios_p);

Argument fd is a file descriptor. This value isn’t the same as stdin. Instead, use fileno(stdin) or the STDIN_FILENO defined constant (from unistd.h).

The termios_p argument is the address of a termios structure. Its members hold various terminal attributes, including local echo and canonical (raw/cooked) modes.

After manipulating members of the termios structure, use the tcsetattr() function to apply modifications to the terminal’s behavior. Here is this function’s man page format:

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

As with tcgetattr(), the arguments start with the file descriptor, fd, which is the same for the tcgetattr() function when modifying the current terminal. The optional_actions argument is a defined constant that determines when the changes take effect. Most often TCSANOW is used to have the changes take place immediately. The final argument is the address of the termios structure containing any terminal modifications.

The man page lists the members of the termios structure and the host of options that you can mess with. For switching to raw input mode, the ICANON constant is manipulated in the c_clfag member of the termios structure, which is demonstrated in the following code:

2026_01_17-Lesson.c

#include <stdio.h>
#include <termios.h>

/* read input and write output */
void in_out(void)
{
    int x;
    char buffer[BUFSIZ];

    printf("Type some text: ");
    /* read input */
    fgets(buffer,BUFSIZ,stdin);

    /* send output */
    printf("Buffer: ");
    for( x=0; buffer[x]; x++ )
        printf("%02X ",buffer[x]);
    putchar('\n');
}

int main()
{
    struct termios original,raw;

    /* first read stdin cooked */
    puts("Cooked mode on");
    in_out();

    /* disable cannonical terminal mode */
    tcgetattr(fileno(stdin),&original);
    raw = original;
    raw.c_lflag = raw.c_lflag ^ ICANON;
    tcsetattr(fileno(stdin),TCSANOW,&raw);

    /* read stdin raw */
    puts("Raw mode on");
    in_out();

    /* restore and clean-up */
    tcsetattr(fileno(stdin),TCSANOW,&original);
    return 0;
}

The main() function calls function in_out() to test input and output in cooked mode. In the in_out() function, text is read into a buffer and then output as a series of hexadecimal values.

After cooked output is examined, the main() function obtains the current terminal’s configuration:

tcgetattr(fileno(stdin),&original);

A duplicate is made of the original termios structure and saved in structure raw. Then the c_clflag is set to disable canonical mode:

raw.c_lflag = raw.c_lflag ^ ICANON;

The new terminal attributes are applied with tcsetattr(fileno(stdin),TCSANOW,&raw), and the in_out() function called again to examine input.

At Line 40, the tcsetattr() function restores the original terminal configuration, original. This configuration may be restored automatically when the program quits. If not, use the reset command in the terminal window to bring things back to normal.

Here’s a sample run:

Cooked mode on
Type some text: he
Buffer: 68 65 0A
Raw mode on
Type some text: hello^?^?^?
Buffer: 68 65 6C 6C 6F 7F 7F 7F 0A

In the first run, I typed hello and then backspaced three times. The backspace (DEL) characters aren’t retained in the input. But when raw input is activated, you see the DEL characters (^?) in the output as well as how they were saved in the buffer (7F).

Entering raw mode may seem silly, but circumstances exist where it’s necessary to fetch uncooked input and have your program process it directly. I’ll provide an example in a future Lesson.

In next week’s Lesson, I cover direct terminal input, which can be confused with raw mode due to some quirky output.

Leave a Reply