If you have ever ventured into the archives of old UNIX books and mailing lists, you will have undoubtedly come across the legend of ed
. ed
(pronounced /iː diː/) is a text editor just like vim
and emacs
. However, contrary to its counterparts, ed
comes with what an interface that could be best summarised as…
$ ed
q
?
q
?
q
?
qqqqqqqqqqqqqqqqqq
?
q
?
q
?
q
$
In spite of all of this, legend has it: “ed is the standard text editor”. So much so that most Linux distributions set /bin/ed
to a Priority
of -100
.
$ sudo update-alternatives --config editor
There are 5 choices for the alternative editor (providing ∕usr∕bin∕editor).
Selection Path Priority Status
------------------------------------------------------------
* 0 ∕usr∕bin∕vim.gtk3 50 auto mode
1 ∕bin∕ed -100 manual mode
2 ∕bin∕nano 40 manual mode
3 ∕usr∕bin∕code 0 manual mode
4 ∕usr∕bin∕vim.gtk3 50 manual mode
5 ∕usr∕bin∕vim.tiny 15 manual mode
Jokes aside, despite being a frequent micro
and Visual Studio Code user, I find myself toying around with ed
and its source code at times. Most notably the GNU implementation of ed
.
This is the story of a bug feature in GNU ed
I stumbled across last year while reviewing the source code which could result in one losing their work.
A brief history in time
To understand the significance of this design flaw, we must briefly venture back to the origin of the GNU ed
editor. In 1976, Brian Kernighan and P.J. Plauger published a book titled “Software Tools in Pascal“ which explored writing good code using the Pascal programming language. This book contains brilliant passages such as:
“In real life, by the way, you would certainly name the routine
sort
, notbubble
, so you could change the algorithm without upsetting users.”— Brian Kernighan & P.J. Plauger, Software Tools in Pascal in reference to “Bubble Sort”
Why is this book of any significance? As stated in GNU’s info
page on ed
, the GNU implementation of ed
drew inspiration from Chapter 6, “Editing”. The first GNU implementation was designed according to Kernighan and Plauger’s specification.
ed-0.1 ❯ cat THANKS
[...]
GNU ed originated with the editor algorithm from Brian W. Kernighan & P.
J. Plauger's wonderful book "Software Tools in Pascal," Addison-Wesley,
1981. [...]
While reading the book, this passage in particular stood out to me (emphasis mine):
“Error recovery is a second major influence on the design of the editor. […]
edit
maintains precious files, so it must be cautious. […] It must recover gracefully, for otherwise some trifling mistake could cause the loss of valuable information.”— Brian Kernighan & P.J. Plauger, Software Tools in Pascal
Keep this quote in mind. We will come back to this passage in just a bit.
Straight to the source
To examine how GNU ed
works under the hood, grab a copy of the latest GNU release (version 1.18
as of writing this blog post). The source code is easy to navigate with only 8 C files.
❯ find . -name "*.c"
./buffer.c
./io.c
./carg_parser.c
./main_loop.c
./main.c
./signal.c
./global.c
./regex.c
GNU ed
has a SIGHUP handler in signal.c
which generates a backup file whenever the ed
process encounters a SIGHUP signal. This ensures one does not lose their work if the ed
process crashes. This is similar to vim
swap files. 1
I have taken the liberty of including my own comments in the sighup_handler()
function below to help guide the reader.
static void sighup_handler(int signum)
{
// [...]
// Listen for SIGHUP signal
if (mutex)
{
sighup_pending = true;
return;
}
sighup_pending = false;
// Hardcode backup filename
const char hb[] = "ed.hup";
// If there are unsaved changes, write current buffer to ed.hup
// in current working directory
if (last_addr() <= 0 || !modified() ||
write_file(hb, "w", 1, last_addr()) >= 0)
exit(0); // Exit here if write was successful
// Otherwise continue executing code and attempt to write to home directory
// Get home directory location from $HOME environment variable
char *const s = getenv("HOME");
// Check if $HOME environment variable is set
if (!s || !s[0])
exit(1);
// Get=span>