What are Terminal Flags?
When you type something in a Linux terminal, a lot of invisible processing happens behind the scenes. The terminal driver controls this processing using a set of flags stored inside a structure called struct termios.
Think of terminal flags as a list of ON/OFF switches. Each switch controls one small behavior โ like “should input characters be echoed back?”, “should CR be converted to NL?”, “should parity errors be ignored?” etc.
The struct termios has four flag fields:
| Field | Full Name | Controls |
|---|---|---|
c_iflag |
Input flags | How incoming characters are processed before being given to the program |
c_oflag |
Output flags | How characters going out to the terminal screen are processed |
c_cflag |
Control flags | Hardware-level settings: baud rate, parity, stop bits, character size |
c_lflag |
Local flags | High-level input behavior: echo, canonical mode, signal generation |
These flags are read and written using two system calls: tcgetattr() (to fetch current settings) and tcsetattr() (to push new settings).
Here is how data flows between your keyboard, the terminal driver, and your program:
| Keyboard Input | โ | c_iflag processing | โ | Line Buffer / Program read() |
| (Input side: c_iflag transforms characters coming IN) | ||||
| Program write() | โ | c_oflag processing | โ | Terminal Screen Output |
| (Output side: c_oflag transforms characters going OUT) | ||||
The c_cflag handles serial-line level things (baud rate, parity), and c_lflag sits above all of them managing line discipline behavior (echo, canonical mode, etc.).
These flags control what happens to characters after they arrive at the terminal driver from the keyboard but before they reach your program. Think of it as pre-processing for input.
| Flag | Default | What it does (plain English) |
|---|---|---|
BRKINT |
ON | When a BREAK condition occurs on the line, send SIGINT to the foreground process group |
ICRNL |
ON | Convert Carriage Return (CR, \r) to Newline (NL, \n) on input. This is why pressing Enter works correctly even on terminals that send CR. |
IGNBRK |
OFF | Completely ignore BREAK conditions (if ON, BRKINT is irrelevant) |
IGNCR |
OFF | Ignore (throw away) CR characters on input |
IGNPAR |
OFF | Ignore characters that have parity errors |
IMAXBEL |
(ON) | Ring the terminal bell when the input queue is full โ not widely used today |
INLCR |
OFF | Convert NL to CR on input (opposite of ICRNL) |
INPCK |
OFF | Enable input parity checking. Works with PARENB in c_cflag. |
ISTRIP |
OFF | Strip the 8th bit (bit 7) from every input character. Useful for 7-bit ASCII terminals. |
IUTF8 |
OFF | Tell the driver input is in UTF-8 encoding (Linux 2.6.4+). Helps correct backspace in multibyte sequences. |
IUCLC |
OFF | Map uppercase to lowercase on input. Only works if IEXTEN is also set. Legacy flag for old uppercase-only terminals. |
IXANY |
OFF | Allow any character (not just XON/START) to restart stopped output |
IXOFF |
OFF | Enable start/stop input flow control. Driver sends STOP/START bytes to sender when buffer is full/empty. |
IXON |
ON | Enable start/stop output flow control. Ctrl+S pauses output, Ctrl+Q resumes it. |
PARMRK |
OFF | Mark parity errors with a 2-byte prefix (0377 + 0) so the program can detect them |
ICRNL โ CR to NL Mapping
When you press Enter on a keyboard, the terminal physically sends a Carriage Return (CR, ASCII 13, \r). But Unix programs expect a Newline (NL, ASCII 10, \n) to mark end-of-line. When ICRNL is ON (default), the terminal driver silently converts every incoming CR into NL. This is why Unix programs work normally even though the keyboard hardware sends CR.
IXON โ Output Flow Control (Ctrl+S / Ctrl+Q)
When IXON is set (default ON), the terminal supports software flow control for output. If output is scrolling too fast:
- Press Ctrl+S โ sends STOP character โ terminal pauses output
- Press Ctrl+Q โ sends START character โ terminal resumes output
This is a pure software mechanism. The characters are not passed to the program โ the driver handles them internally.
ISTRIP โ Strip 8th Bit
Old 7-bit terminals could not send or receive 8-bit characters. When ISTRIP is ON, the driver sets the 8th bit of every input character to 0 (strips it). Modern terminals are 8-bit, so this is usually OFF.
INPCK / IGNPAR / PARMRK โ Parity Handling
Parity is an error-detection mechanism used in serial communications. These three flags work together:
| Flag | Effect on parity error |
|---|---|
IGNPAR ON |
Discard the bad character entirely |
PARMRK ON |
Pass the bad character to the program prefixed by \377\0 so program knows it was bad |
| Neither | Replace the bad character with a NUL byte (\0) |
Below is a complete C example showing how to read current terminal input flags, disable CR-to-NL mapping (ICRNL), and restore original settings on exit.
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
struct termios orig, modified;
/* Step 1: Read current terminal attributes */
if (tcgetattr(STDIN_FILENO, &orig) == -1) {
perror("tcgetattr");
exit(EXIT_FAILURE);
}
/* Step 2: Copy original and modify c_iflag */
modified = orig;
/*
* Disable ICRNL: stop the driver from mapping CR (\r) to NL (\n).
* After this, pressing Enter sends \r to the program, not \n.
*/
modified.c_iflag &= ~ICRNL;
/*
* Also disable IXON: stop Ctrl+S / Ctrl+Q from working.
* Now those keystrokes will be passed to the program.
*/
modified.c_iflag &= ~IXON;
/* Step 3: Apply the modified settings (take effect after output flushed) */
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &modified) == -1) {
perror("tcsetattr");
exit(EXIT_FAILURE);
}
printf("ICRNL disabled. Enter sends CR (\\r) now.\n");
printf("Press any key (Ctrl+C to test if IXON is really off):\n");
char buf[10];
ssize_t n = read(STDIN_FILENO, buf, sizeof(buf));
if (n > 0) {
printf("Read %zd bytes. First byte = 0x%02X\n", n, (unsigned char)buf[0]);
/* 0x0D = CR (\r), 0x0A = NL (\n) */
}
/* Step 4: Always restore original settings before exit */
if (tcsetattr(STDIN_FILENO, TCSANOW, &orig) == -1) {
perror("tcsetattr restore");
}
return 0;
}
Compile and run:
gcc -o iflag_demo iflag_demo.c
./iflag_demo
struct termios and restore it when done. Failing to restore leaves the terminal in a broken state for the user.The second argument to tcsetattr() controls when the new settings take effect:
| Constant | Meaning | When to use |
|---|---|---|
TCSANOW |
Change takes effect immediately | Quick changes that can apply right away (e.g., restoring settings) |
TCSADRAIN |
Change takes effect after all pending output is written | When output must finish before changing (safe for output flags) |
TCSAFLUSH |
Change after pending output written; discard unread input | Best choice when changing input flags โ discards stale input |
Q1. What is struct termios and what are its four flag fields?
struct termios is the data structure used by Linux to store all terminal I/O settings. Its four flag fields are: c_iflag (input processing), c_oflag (output processing), c_cflag (hardware/control settings), and c_lflag (local/line discipline settings like echo and canonical mode).
Q2. Why does pressing Enter on a Linux terminal typically send \n to programs, even though the keyboard sends \r?
Because the ICRNL flag in c_iflag is ON by default. The terminal driver automatically converts CR (\r, ASCII 13) into NL (\n, ASCII 10) before passing the character to the reading process.
Q3. What does Ctrl+S do in a Linux terminal and how is it implemented?
Ctrl+S pauses terminal output. It is implemented through software flow control using the IXON flag in c_iflag. When IXON is ON, pressing Ctrl+S sends a STOP character which tells the driver to halt output. Pressing Ctrl+Q sends the START character to resume output.
Q4. What is the difference between TCSANOW, TCSADRAIN, and TCSAFLUSH?
TCSANOW applies changes immediately. TCSADRAIN waits until all pending output has been transmitted, then applies changes. TCSAFLUSH does the same as TCSADRAIN but also discards any unread input โ making it the safest choice when changing input-related flags.
Q5. What is the purpose of ISTRIP flag and when would you use it?
ISTRIP strips the 8th bit (makes it 0) from every incoming character. It was needed for old 7-bit ASCII terminals that couldn’t handle 8-bit data. On modern 8-bit terminals it should be OFF. It is rarely needed today but may appear in legacy serial communication code.
Q6. How do IGNPAR, PARMRK, and INPCK interact for parity error handling?
INPCK must be ON to enable parity checking. If IGNPAR is ON, characters with parity errors are silently discarded. If PARMRK is ON (and IGNPAR is OFF), the bad character is passed to the program prefixed by the two bytes 0377 and 0, so the program can identify it. If neither flag is set, parity-error characters are replaced with NUL (\0).
Part 2: Output & Control Flags โ Part 3: Local Flags & Echo
