What goes into the terminal's 'cbreak' and 'raw' modes

December 2, 2012

Recently, Eevee tweeted:

things i never thought i'd need to know: the difference between 'raw' and 'cbreak' is not just some flag. it's like 20! [link]

This inspires me to talk about what 'cbreak' and 'raw' modes are, both at a high level and then at the low level of exactly what terminal settings go into each mode.

The traditional 'raw' mode is the easier one to explain; it does no in-kernel processing of input or output at all. All characters are returned immediately when typed and all output is produced as-is. The traditional 'cbreak' mode is used for things like password entry; it returns characters immediately as they're typed, doesn't echo characters, and doesn't do any in-kernel line editing (which mostly means that your program can actually see the various editing characters). At a high level, there are two major differences between 'cbreak' and 'raw'. First, cbreak leaves output handling unchanged, which may be relatively 'cooked'. Second, cbreak still allows you to interrupt the program with ^C, suspend it, and so on. You can see this in action with most programs (such as passwd, su, or sudo) that ask for a password; you can immediately interrupt them with ^C in a way that, eg, vi does not respond to.

The low-level settings for cbreak are:

  • disable ECHO; this stops typed characters from being echoed.
  • disable ICANON; this turns off line editing.
  • set VMIN to 1 and VTIME to 0; this makes it so that a read() returns immediately once there's (at least) one character available.

This is about the minimum you can do to have anything like this so pretty much everyone is going to agree on them. The low-level settings for raw mode start with cbreak's changes and add a bunch more, but there can be some variation in exactly what settings get added; I'm going to use the Python version from eevee's link. This disables a bunch of additional tty options:

  • BRKINT: serial breaks are ignored and converted to null bytes. In the modern world where most ttys are pseudo-ttys instead of serial lines, this generally isn't going to make any difference.
  • ICRNL: with this disabled, carriage returns (^J ^M, '\r') are not turned into newlines (^M ^J, '\n') on input (normally you can't tell them apart and both will terminate the current line).
  • INPCK: input parity checking is disabled. Again, not an option that is relevant on pseudo-ttys.
  • IXON: with this disabled, ^S and ^Q do not pause and then restart output.
  • OPOST: disables any 'implementation-defined' output processing. On Linux (and probably many others) this is the setting that normally turns a newline into a CR-NL sequence.
  • PARENB: disables parity generation on output and apparently also parity checking on input, making it overlap a bit with INPCK.
  • IEXTEN: disables extra additional input processing and line editing characters. Things like word erase were not part of the original Unix tty line editing, so they have to be enabled separately from the basic line editing characters that are covered by ICANON. It's common for extended line editing to only be enabled only if both ICANON and IEXTEN are on.

    (Unixes vary on what effect IEXTEN has beyond enabling the additional line editing characters. Linux pretty much only uses it for that, but Solaris does additional stuff with it.)

  • ISIG: with this disabled, things like ^C do not generate interrupts when they're typed.

Raw mode also does stuff with CSIZE, which is unusual because it's a mask instead of a flag; it's the set of bits in one of the fields that are used to determine the bitsize of characters. You mask off the CSIZE bits first and then set one of the available settings of bits; 'raw' mode sets CS8, for 8-bit characters.

(This is a little bit confusing in the Python code, which masks off the CSIZE bits at the same time as it's disabling PARENB.)

Because Unix tty handling has a huge amount of historical baggage this collection of flags is split across four fields (input, output, 'control', and 'local'). Which field a flag is in is somewhat arbitrary and generally confusing.

(Update: as eevee notes, pretty much all the detailed documentation you could ask for is in termios(3).)

Update, July 1st 2014: I've now noticed that I flipped ^J and ^M in my description of ICRNL. Oops. Fixed.

Written on 02 December 2012.
« All kernel messages should be usefully ratelimited. No exceptions.
A reason for detailed commit messages: as a guard against errors »

Page tools: View Source.
Search:
Login: Password:

Last modified: Sun Dec 2 01:16:10 2012
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.