Tomas Vik

New lines and terminals: CR vs. LF

I’m building a terminal emulator in go: gritty. I noticed that /bin/sh is sending me the ASCII CR character back as a part of the response. I tend to absolutely ignore the CR because in my mind it’s a “Windows thing”.

But seeing the CR made me realise that I don’t have a clear idea about how new lines work. In my mind, new line = \n but it might be more complex than that.

name ASCII hex code abbreviation escape sequence* Caret notation
Line Feed 0x0A LF \n ^J
Carriage Return 0x0D CR \r ^M
  • Line Feed - Move the cursor one line down
  • Carriage Return - Move the cursor to the beginning of the line

Historically, these two actions had to happen together so you or the computer can start typing text on the next line.

So why did I see the CR character when working on my terminal emulator? Well it shows that we live in the past, at least when it comes to terminals.

graph RL; subgraph "unix - LF-only land" SHELL[Shell] end subgraph "terminal - CR-LF land" TERM[Terminal emulator] end SHELL --"\n"--> PTY PTY --"\r\n"--> TERM

When shell sends a line feed character to the PTY, the TTY driver translates that to \r\n for the terminal (emulator). This is dependent on the TTY driver settings.

stty -a

  • icrnl (-icrnl) - Map (do not map) CR to NL on input.
  • onlcr (-onlcr) Map (do not map) NL to CR-NL on output.

So in summary, the terminals and the terminal emulators are still using \r even on unix systems (for example when you press enter, terminal sends \r to the shell). That’s why I saw it in my

  • How do you call the string \n (backslash followed by lower case n)? It is called “escape sequence for newline character”. The escape sequence comes originally from C and JavaScript and Go both adopt it.

linux - Wrong newline character over serial port (CR instead of LF) - Super User

ASCII - Wikipedia