leah blogs

January 2021

20jan2021 · Remembering the work of David M. Tilbrook and the QED editor

Last week, I learned that David M. Tilbrook has died, which made me sad. I did not know him personally and cannot say much about his life, but I studied his publications and software ideas a lot, and consider them interesting, especially from a historic perspective.

Unfortunately, most of these things are on websites taken down already, so this post will refer to pages on the Internet Archive extensively. [Update 2020-01-16: His son Joe Tilbrook sent me a mail stating that http://qef.com/ is up again.]

I first came across David when I researched the history of the QED editor, the direct predecessor of the Unix standard text editor ed. The history of QED is well documented by Dennis Ritchie. However, for a long time the QED source code was hard to find, and I heard that David still maintained a version.

Around 2016, I swooped a copy of Caltech qed from USENIX tape 80.1 and tried making it work on modern platforms, with moderate success. Thanks to efforts by Arnold Robbins there is now an QED Archive which also features a copy of 1992 QED from Toronto, which contains contributions by Tom Duff, Robert Pike, Hugh Redelmeier and David Tilbrook. If you want to run it yourself, there is a modernized, UTF-8 aware version available now!

I do not know what David exactly contributed to QED, but from his writings its clear he was a heavy user of QED and wrote many scripts in it. Remember that in the early 80’s, awk was quite limited and Perl did not exist, so general-purpose string processing on Unix was difficult. We will have an example of a small QED script at the end of this post.

David’s opus magnum was a suite of tools called QEF, quod erat faciendum. Euclid wrote this at the end of geometric constructions, and in a software sense, we want a build system to produce what was to be made. At its time of creation, David was responsible for maintaining a (for the time) large system, essentially a Unix distribution. Tooling was stuck in the era of 1977’s make(1). For the details and basic ideas, see Tilbrook and Place (1986), “Tools for the Maintenance and Installation of a Large Software Distribution” (Huge thanks to Alan Grosskurth for making a copy available.) My favorite footnote is the one about their Prolog prototype of a build system: “Is the cray free? I need to reinstall /bin/true!”

Back then, Unix software development was plagued with all kinds of incompatibilities and vendor-specific workarounds, and QEF was one of the first tools to provide concepts like out-of-tree builds, automatic dependency detection, and provided portable abstractions for things like creating shared libraries, which required performing occult rituals back then.

The QEF whitepaper from 1996 explains the system at a more developed state.

What is intriguing is that the whole toolkit is created in classic Unix manner from small tools and little languages. I acquired an evaluation copy of QEF in 2015, but sadly it had no copy of QED included. However, I could read the full manpages for many of his tools.

Looking at these tools was instructive; many are obsoleted now because the features have been added to standard tools, or we can afford using more general, inefficient tools now. But for example, his go tool directly influenced the user interface of my nq tool, and reading about rls, lls, fexists, and ftest inspired my lr. His rpl lives on as my mend.

There are also additional resources and talks by David worth checking out.

Well, I promised you some QED code earlier, so let’s do fizzbuzz. (I heard this is useful in job interviews, so next time why not ask whether you can use QED for the task!)

It turns out this does not get as obscure as expected, but I’m not claiming what I wrote is idiomatic QED. I hacked it together while studying the tutorial a little bit.

ba
"loop
zc#+1

a\N\N.
za#:\zc%3=0 yf s/$/fizz/
za#:\zc%5=0 yf s/$/buzz/
s/^$/\zc/

zc#=100 yf`loop

,p
Q

We can run this using the convenient -x flag:

% qed -x fizzbuzz.qed
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
    ...

So, how does it work? First, we switch to buffer a of QED’s 56 available buffers (ba). By default we are in the script buffer named ~ due to the -x flag, which made me scratch my head in the beginning because the script always printed its own source at first!

Next, we set up a loop. QED provides structured programming in the form of a while loop, but that requires fitting the loop body on the same line. Instead, we will use jumps. We start with a label "loop and increment the c register using zc#+1. The # enables arithmetic mode for registers, they also can be strings. Since the register is empty, it will be regarded as zero. The end of the loop is the zc#=100 yf`loop line, which checks whether the c register equals 100, and if not (yf) jumps back to the label loop. Curiously, QED has different commands for jumping forwards (') and backwards (`). Having explicit directions is, I assume, an implementation detail of the buffer-based program storage, but compare with Unix’s goto or TECO’s O command, which both search from the top.

Inside the loop, we first append an empty line (a\N\N.) This works like in ed:

a

.

But QED allows using \N as a newline, so we can save some screen space.

Then, we need to do the fizzbuzz logic. The command za#:\zc%3=0 will copy the c register to the a register (we need to do this, as all arithmetic operations store their result in a register), then we take the a register modulo 3 and check if the register is then zero. If it isn’t, we jump a line forward (yf). If we didn’t jump, we append fizz to the line, using the s command (pretty much like in ed).

We deal with the buzz case the same way, but modulo 5.

If the line is still empty, we have to say the number, so we append it using s again and this time insert the c register into the input stream using \zc. Note that this not interpolated by the s command, but by QED itself. You can write \zc (or \N) essentially everywhere and it will work as if you’d have typed it in! A \ can be quoted as \c, so we can do this:

za:foo\czb
zb:bar
a
\za
.
p
foobar

Note how the insertion of the a register resulted in the b register being inserted as well! Luckily, the people involved with QED did better when they wrote shells later.

At the end of the loop, we have the fizzbuzz output in the buffer, so we can just print out the whole thing (,p) and quit QED with no questions asked (Q).

That wasn’t so bad, was it? Just like the tutorial says:

By striking a harmonious balance between Qed and UNIX’s other tools, the intelligent user will find Qed powerful, flexible, easy to master and fun!

NP: Talking Heads—Seen And Not Seen

Copyright © 2004–2022