# Cursor Backward Tabulation (CBT)

> Move the cursor back to the previous tab stop, n times.

- **Sequence:** `CSI Pn Z`
- **Defaults:** Pn = 1
- **ECMA-48:** [§8.3.7 CBT – Cursor Backward Tabulation, p. 34](https://archive.org/details/ecma-48-5th-edition-june-1991/page/n47/mode/1up)
- **VT510:** [CBT—Cursor Backward Tabulation](https://vt100.net/docs/vt510-rm/CBT.html)

Move the cursor left to the previous tab stop, and repeat that `Pn` times. A
parameter of `0` is treated as `1`.

The cursor never moves left of the first column, and if
origin mode and left and right margins are both enabled, never left of
the left margin. When that limit is reached the cursor stays there, however
many tab stops remained.

Unlike the cursor movements, CBT does not cancel a pending wrap. DEC STD 070
lists the controls that should reset the pending wrap, which it calls the
*Last Column Flag* ([§D.6.1, p. D-13](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381/page/n1199/mode/1up)); [HT](https://control-codes.page/c0/ht/index.html.md) is on the list,
but CBT is not, and DEC STD 070 adds that "existing products vary widely" in
how they handle it. xterm moves
the cursor in [`TabToPrevStop`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/tabs.c#L165-L178) without clearing the
flag, and its [`dotext`](https://github.com/ThomasDickey/xterm-snapshots/blob/xterm-411a/charproc.c#L7065-L7074) wraps whenever the flag is
set, wherever the cursor now is. So after CBT from the last column, the next
character printed still starts a new line. libghostty-vt does the same.
Ghostty's own [VT reference](https://ghostty.org/docs/vt/csi/cbt) expects the
opposite in its second validation case, so there Ghostty's documentation and
its implementation disagree.

Tab stops are set with [HTS](https://control-codes.page/esc/hts/index.html.md) and cleared with [TBC](https://control-codes.page/csi/tbc/index.html.md). Most terminals,
libghostty-vt included, start with one every eight columns, and `CSI ? 5 W`
puts them back to that.

## Validation

### CBT-1: Stops at the first column

Input, one step per line:

```text
\e[?5W    # reset tab stops
\e[10Z
A
```

Expected screen:

```text
|A_________|
cursor 1,2
```

### CBT-2: A pending wrap survives

Input, one step per line:

```text
\e[?5W    # reset tab stops
\e[1;10H  # last column
X         # wrap is now pending
\e[Z      # back to the tab stop at column 9; still pending
A         # so this starts the next line
```

Expected screen:

```text
|_________X|
|A_________|
cursor 2,2
```

### CBT-3: From exactly on a tab stop

Input, one step per line:

```text
\e[?5W    # reset tab stops
\e[1;9H   # the tab stop at column 9
X
\e[1;9H
\e[Z
A
```

Expected screen:

```text
|A_______X_|
```

### CBT-4: Inside a left margin, with origin mode

Input, one step per line:

```text
\e[?5W    # reset tab stops
\e[?6h    # origin mode
\e[?69h   # allow left and right margins
\e[3;6s   # margins at columns 3 and 6
\e[1;2H   # column 2 of the margins, column 4 of the screen
X
\e[Z
A
```

Expected screen:

```text
|__AX______|
```

---

This is the Markdown version of <https://control-codes.page/csi/cbt/>. 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>.
