# Controls Inside Sequences

> What a control character does when it arrives in the middle of a control sequence or a string.

- **Sequence:** `CSI P… BS P… F`
- **DEC STD 070:** [§3.5.1.1 Precedence of Control Functions, p. 3-18](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n122/mode/1up)
- **xterm:** [Definitions](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

A control sequence is not always unbroken. A program can be interrupted, or
a sequence can be written in two pieces with something else in between, and
the terminal then meets a control character part-way through one.

**In a control sequence** a C0 control is carried out at once, and the
sequence then carries on as if it had not been there: `CSI 2 BS C` moves
back one column and then forward two. DEC STD 070 asks for this: control
characters take precedence over the sequences around them
([§3.5.1.1, p. 3-18](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n122/mode/1up)).
xterm's parse tables do it, giving each C0 control its usual action in every state
of a control sequence
([`csi_table`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/VTPrsTbl.c#L477-L512)).
DEL, `7F`, is ignored. The exceptions are [CAN](https://control-codes.page/c0/can/index.html.md) and
[SUB](https://control-codes.page/c0/sub/index.html.md), which abandon the sequence, and [ESC](https://control-codes.page/c0/esc/index.html.md), which
abandons it and starts another.

**In a string**, the text of an OSC, DCS, SOS, PM or APC, xterm ignores every
C0 control except the ones that end or abandon it: they are left out of the
string, so `OSC 2 ; a LF b ST` sets the title `ab`, and so is DEL
([`sos_table`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/VTPrsTbl.c#L7619-L7780)).
[BEL](https://control-codes.page/c0/bel/index.html.md) ends an OSC, as xterm has always allowed, and rings the
bell inside a DCS. An ESC ends the string only as the first half of
[ST](https://control-codes.page/esc/st/index.html.md), `ESC \`. Followed by anything else, it abandons the string
and starts an escape sequence, and the string is thrown away: xterm keeps
collecting it only while it waits for the byte after the ESC, and carries
out an OSC only on its terminator
([`doparsing`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L3484-L3488)).

libghostty-vt does the same, with two exceptions. It keeps a DEL in a
string, so it ends up in the title. And an ESC that is not part of ST ends
an OSC *and carries it out* before starting the escape sequence, so a title
cut short that way is still set. Those cases are known differences.

## Validation

### CTL-1: Backspace inside a control sequence

Input, one step per line:

```text
ABCD
\e[2\bC   # BS, then CUF 2
X
```

Expected screen:

```text
|ABCD_X__|
cursor 1,7
```

### CTL-2: Carriage return

Input, one step per line:

```text
ABCD
\e[\r2C   # CR, then CUF 2
X
```

Expected screen:

```text
|ABXD____|
cursor 1,4
```

### CTL-3: Line feed, between the parameters

Input, one step per line:

```text
AB
\e[\n2C
X
```

Expected screen:

```text
|AB______|
|____X___|
cursor 2,6
```

### CTL-4: DEL is ignored

Input, one step per line:

```text
AB
\e[1\x7f;5H
X
```

Expected screen:

```text
|AB__X___|
cursor 1,6
```

### CTL-5: Controls are left out of a string

Input, one step per line:

```text
\e]2;a\bb\nc\x00d\x1fe\a
```

Expected screen:

```text
|________|
title "abcde"
```

### CTL-6: And so is DEL

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

```text
\e]2;a\x7fb\a
```

Expected screen:

```text
|________|
title "ab"
```

### CTL-7: An ESC abandons a string

*A known difference: libghostty-vt does not pass this case.*

Input, one step per line:

```text
\e]2;cut\e7   # ESC 7, DECSC, not ST
X
```

Expected screen:

```text
|X_______|
title ""
```

---

This is the Markdown version of <https://control-codes.page/parser/controls/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.

The validation cases are written in the notation described in <https://control-codes.page/notation/index.html.md>.
