This patch separates the two POSIX complement modes implemented by `tr`:
- `-c` complements byte values.
- `-C` complements characters.
It also uses byte processing for all operations when `LC_CTYPE` resolves to
`C` or `POSIX`.
## Problem
The unpatched implementation decodes both operands and standard input as
UTF-8 runes. Consequently, this command does not preserve selected byte values:
```sh
LC_ALL=C tr -cd '\011\012\015\040-\176\200-\377'
```
Invalid standalone bytes, Unicode noncharacters, and out-of-range UTF-8 byte
sequences can be removed or rewritten as `U+FFFD`. This differs from the byte
semantics expected from lowercase `-c` and from operation in the C locale.
## Changes
The patch:
- distinguishes `-c` from `-C`;
- enables byte mode for lowercase `-c`;
- enables byte mode in the `C` and `POSIX` locales;
- parses octal escapes as byte values in byte mode;
- reads and writes byte mode with `fgetc()` and `fputc()`;
- uses `<ctype.h>` for byte-mode classes and case conversion;
- keeps rune processing for character mode outside the C/POSIX locale;
- documents the distinction in `tr.1`;
- adds `tests/0053-tr.sh`.
## Base revision
```text
c546c3a5724c81cee9a11d816a38ccdf17472129
```
## Apply
From the root of the sbase source tree:
```sh
patch -p1 < sbase-tr-c-byte-mode.patch
```
## Build
Use the normal sbase build:
```sh
make tr
```
## Test
Run the upstream test suite or the added regression test:
```sh
make test
```
The standalone reproducer can also test any `tr` binary:
```sh
./tr-c-byte-regression.sh ./tr
```
Expected output:
```text
PASS: tr -c preserves selected byte values
```
The standalone test requires `mktemp`, `cmp`, `od`, and a POSIX shell.
## Scope
The patch changes byte/character selection only. It does not modify `libutf` or
the existing rune behavior used outside byte mode.
Received on Wed Jul 15 2026 - 19:28:06 CEST