A working line editor in 119 lines of ISO C23, written to be as unreadable as the standard permits while still compiling clean and doing its job.
It edits text. It has append, insert, delete, print, substitute, read and write. It handles files of any size and lines of any length. It also has exactly one error message, no undo, no unsaved-changes warning, and no way to look at a single line. Both halves of that are on purpose.
$ wtee notes.txt
*a
The quick brown fox
jumps over the lazy dog
.
*p
1 The quick brown fox
2 jumps over the lazy dog
*w
2
*q
The build system is 138 lines. The program is 119. This is also on purpose.
This is an obfuscation exercise, not a tool. The goal was code that is hostile to read but honest to run — no undefined behaviour, no memory leaks, no "it works on my machine". Every construct below is load-bearing; nothing is dead code added for decoration.
Digraphs, because trigraphs are gone. C23 removed trigraphs (N2940), so
??< is no longer available to anyone. Digraphs survived. Braces are <% and
%>, subscripts are <: and :>, and preprocessor directives use %: for
# and %:%: for ##. All of it is hidden behind macros named OPEN,
SHUT, AT() and WELD(), so the digraphs aren't visible until you
preprocess the file.
A seven-level pointer. The line buffer is a char **, but it is never
named directly. O1 through O5 form a chain of addresses terminating in
static char *******O5, and every access goes through
#define l1 (*****NECRO(5)) — five dereferences of a seven-star pointer,
reached through a token-pasting macro. realloc works correctly through the
chain, which is the genuinely upsetting part.
Function pointers past the point of usefulness. Command dispatch is a
128-entry table of int (*)(char *) reached via:
static int(*(*(*top(void))(void))(int))(char*);A function returning a pointer to a function returning a pointer to a function
returning a pointer to a function returning int. The call site reads
top()()(c)(args). None of the intermediate levels do anything.
Everything else. Indentation uncorrelated with nesting. Comma operators
where statement sequences belong. Numeric constants in binary. A goto inside
a loop that could be a continue. OOPS is a macro that expands to a return.
main ends with return free(l1),free(FN),0;.
C23 features used, since the point was to use them: nullptr, constexpr,
auto type inference, typeof, [[noreturn]], [[maybe_unused]],
single-argument static_assert, and strdup — which only became standard
library in C23.
cmake -B build
cmake --build build
ctest --test-dir build --output-on-failure
Needs CMake 3.21 or newer (the first release with C_STANDARD 23) and a
compiler with C23 support. GCC 13 and earlier spell the standard -std=c2x;
CMake translates that for you, which is why the build file sets
C_STANDARD 23 rather than hardcoding a flag. Tested on GCC 13 and GCC 14.
Clang 18+ should work. MSVC does not, and is not supported.
Installing puts the binary somewhere useful:
cmake --install build --prefix ~/.local
The build suppresses -Wmisleading-indentation. Five instances of it are
intentional. It is otherwise warning-clean under -Wall -Wextra.
Disproportionate by design. A 119-line joke does not need a feature probe, a smoke test and an in-source-build guard, which is precisely why it has them. Everything in it does something real, though — none of it is padding.
| Option | Default | Effect |
|---|---|---|
CMAKE_BUILD_TYPE |
Debug |
Set automatically for single-config generators if you don't pass one, since the CMake default of no build type means no optimisation and no debug info. Left alone for Visual Studio and Ninja Multi-Config, which choose at build time. |
BUILD_TESTING |
ON |
Registers the smoke test with CTest. -DBUILD_TESTING=OFF removes it. |
CMAKE_INSTALL_PREFIX |
platform default | Where cmake --install puts things. --prefix ~/.local for a per-user install. |
CMAKE_EXPORT_COMPILE_COMMANDS |
forced ON |
Writes compile_commands.json. Without it clangd has no idea what -std this file needs, and the digraphs confuse it completely. |
WTEE_SOURCE near the top of CMakeLists.txt names the source file. It's a
plain variable, not a cache entry, so edit the line rather than passing -D.
Change it if your file is called worst_text_editor_ever.c.
Version range. cmake_minimum_required(VERSION 3.21...4.2) — 3.21 is the
genuine floor, 4.2 is the newest version whose policy defaults were tested.
The range form means people on distro CMake can still configure the project,
while newer CMake still uses modern policies.
A C23 feature probe. Compiles a throwaway program using nullptr,
constexpr, auto, typeof, attributes and strdup before building
anything. If it fails, configuration stops with a message naming what's
missing. This is the one piece worth stealing: without it, a compiler lacking
typeof desynchronises the parser and produces dozens of errors on the wrong
lines, and the digraphs make the result completely unreadable.
An in-source build guard. Stops cmake . from scattering CMakeCache.txt,
CMakeFiles/ and object files next to your .git.
A smoke test. Drives a real session — append, insert-before, substitute,
delete, write — and diffs the resulting file against a known-good one. It
covers 1-based numbering, insert positioning, first-match-only substitution and
range deletion, which are exactly the things this code invites you to break.
The test input and the stdin-redirect helper are generated into the build
directory at configure time, so one test doesn't earn a tests/ directory.
A summary line. Prints compiler, version, standard and build type at the end of configure, so you can see what you actually got.
The wtee run configuration works out of the box. For the CTest ones:
- Tools → CMake → Reset Cache and Reload Project. Tests registered by
add_testonly show up in CLion after a reload; adding them mid-session isn't picked up. - Build
wteeat least once first. The test invokescmake -Prather than a target binary, so CLion can't infer that it needs to build the editor before running the test. If the binary doesn't exist yet the test fails with a confusing error rather than an obviously missing file. - To fix that permanently: Edit Configurations → the CTest configuration →
Before launch → Add → Build → target
wtee.
If it still won't go, run ctest --test-dir cmake-build-debug --output-on-failure in the terminal. That tells you whether the problem is the
test or the IDE, which is worth knowing before spending an evening on it.
| File | |
|---|---|
wtee.c |
The entire program |
CMakeLists.txt |
Build system. Longer than the program. |
MANUAL.md |
Full manual — every command, in detail |
README.md |
The README file you're reading right now |
.gitignore |
The gitignore file |
LICENSE |
The license (MIT) |
Start with MANUAL.md. It documents the behaviour that was verified against the compiled binary rather than the behaviour the commands look like they should have, and the difference is larger than you'd expect.
This was written by Claude Opus 5, an AI model made by Anthropic, from a prompt asking for the most unreadable C23 program that could still be called a text editor. The code was compiled and the commands were exercised before delivery, but it has had no review beyond that and no use beyond that.
Treat it accordingly. It is a joke that happens to work — fine to read, run and
laugh at, unwise to trust with anything you would be upset to lose. It has one
smoke test, which is one more than it deserves, and no coverage beyond that.
The q command discards your buffer without asking.
Do not copy patterns from wtee.c into real code. Every technique in it is one
that a maintainer would be right to reject in review, and that is the whole
point of it. CMakeLists.txt is the exception — that one is fine to steal.
This project is licensed under the MIT License (MIT). See the LICENSE file for details.