Life in UNIX® V7: an attempt at a simple task

(This is a guest post by xorhash.)

1. Introduction

ChuckMcM on Hacker News ( reacted to my previous entry here about trying to typeset old troff sources with groff. It was said that ‘‘you really can’t appreciate troff (and runoff and scribe) unless you do all of your document preparation on a fixed width font 24 line by 80 column terminal’’.

‘‘Challenge accepted’’ I said to myself. However, it would be quite boring to just do my document preparation in this kind of situation. Thus, I raised the ante: I will do my document preparation on a fixed width font 24 line by 80 column terminal on an ancient UNIX . That document is the one you are reading here.

2. Getting an old version of UNIX

While it would have been interesting to run my experiment on SIMH with a genuine UNIX , I was feeling far too lazy for that. Another constraint I made for myself is that I wanted to use the Internet as little as possible. Past the installation phase, only resources that are on the filesystem or part of the Seventh Edition Manual should be consulted. However, if I have to work with SIMH, chances are I’d be possibly fighting the emulator and the old emulated hardware much more than the software.

2.1. FreeBSD 1.0

My first thought was that I could just go for FreeBSD 1.0 or something. FreeBSD 1.0 dates from around 1993. That was surprisingly recent, but I needed a way to get the data off this thing again, so I did want networking. As luck would have it, FreeBSD 1.0 refused to install, giving me a hard read error when trying to read the floppy. FreeBSD 2.0 was from 1995 and already had colorful menus to install itself (!). That’s no use for an exercise in masochism.

2.2. V7/x86

I turned to browsing for a bit, hoping to find something to work with. Lo and behold, it pointed me to! V7/x86 is a port of UNIX version 7 to the x86. It made some changes to V7, among those are:


including the more pager,


including the vi editor,


providing a console for the screen, rather than expecting a teletype, and


including an installation script.

The version of vi that ships with it is surprisingly usable, even by today’s standards. I believe I would’ve gone mad if I’d had to use ed to write this text with.

3. Installing V7/x86

The V7/x86 installer requires that a partition exists with the correct partition type. It ships with a tool called ptdisk to do that, but because /boot/mbr does not exist on the installation environment, it cannot initialize a disk that does not already have a partition table ( Thus I used a (recent) release of FreeBSD to create it. At first, FreeBSD couldn’t find its own CD-ROM, which left me quite confused. As it turns out, it being unable to find the CD-ROM was a side effect of assigning only 64 megabytes of RAM to the virtual machine. Once I’d bumped the RAM to 1GB, the FreeBSD booting procedure worked and I could create the partition for V7/x86. V7/x86 itself comes packaged on a standard ISO file and with a simple installation script. It seems it requires an IDE drive, but I did not investigate support for other types of hard drives, in particular SATA drives, much further. There seem to be no USB drivers, so USB keyboards may not work, either.

During the installation, my hard drive started making a lot of scary noises for a few minutes, so I aborted the installation procedure. After moving the disk image to a RAM disk (thank you, Linux, for giving me the power of tmpfs), I restarted the installation and it went in a flash. The scary noises were probably related to copying data with a block size of 20, which I assume was 20 bytes per block: The virtual hard disk was opened with O_DIRECT, i.e., all writes got flushed to it immediately. Rewriting the hard drive sector 20 bytes at a time must’ve been rather stressful for the drive.

4. Using V7/x86

I thought I knew my UNIX , but the 70s apparently had a few things to teach me. Fortunately, getting the system into a usable state was fairly simple because got me started. The most important notes are:


V7 boots in single-user mode by default. Only when you exit single-user mode, /etc/rc is actually run and the system comes up in multi-user mode.


Using su is recommended because root has an insane environment by default. To erase, # is used, rather than backspace (^H). The TERM variable is not set, breaking vi. /usr/ucb is not on the path, making more unavailable.


The character to interrupt a running command is DEL, not ^C. It does not seem possible to remap this.

more is a necessity on a console. I do not have a teletype, meaning I cannot just ‘‘scroll’’ by reading the text on the sheet so far. Therefore, man is fairly useless without also piping its output to more.

Creating a user account was simple enough, though: Edit /etc/passwd, run passwd for the new user, make the home directory, done. However, my first attempt failed hard because I was not aware of the stty erase situation. I now have a directory in /usr that reads ‘‘xorhash’’, but is definitely not the ASCII string ‘‘xorhash’’. It’s ‘‘o^Hxorhash’’. The same problem applies to hitting the arrow keys out of habit to access the command history, only to butcher the partial command you were writing that way.

Another mild inconvenience is the lack of alternative keyboard layouts. There is only the standard US English keyboard layout. I’m not used to it and it took me a while to figure out where some relevant keys ($, ^, &, / and – in particular) are. Though I suppose if I really wanted to, I could mess around with the kernel and the console driver, which is probably the intended way to change the keyboard layout in the first place.

5. Writing a document

Equipped with a new user, I turned to writing this text down before my memory fails me on the installation details.

5.1. The vi Editor

I am infinitely thankful for having vi in the V7/x86 distribution. Truly, I cannot express enough gratitude after just seeing a glimpse of ed in the V7/x86 introduction document. It has some quirks compared to my daily vim setup, though. Backspacing across lines is not possible. c only shows you until where you’re deleting by marking the end with $. You only get one undo, and undoing the undo is its own undo entry. And of course, there’s no syntax highlighting in that day and age.

5.2. troff/nroff

And now for the guests of this show for which the whole exercise was undertaken. The information in volume 2A of the 7th Edition manuals was surprisingly useful to get me started with the ms macros. I didn’t bother reading the troff/nroff User’s Manual as I only wanted to use the program, not write a macro package myself. The ms macro set seemed to be the way to go for that. In this case, nroff did much more heavy lifting than troff. After all, troff is designed the Graphic Systems C/A/T phototypesetter. I don’t have one of those. M. E. Lesk’s Typing Documents on the UNIX System: Using the −ms Macros with Troff and Nroff and Brian W. Kernighan’s A TROFF Tutorial proved invaluable trying to get this text formatted in nroff.

The ‘‘testing’’ cycle is fairly painful, too. When reading the nroff output, some formatting information (italics, bold) is lost. more can only advance pagewise, which makes it difficult to observe paragraphs in their entirety. It also cannot jump or scroll very fast so that finding issues in the later pages becomes infuriating, which I solved by splitting the file up into multiple files, one for each section heading.

5.3. The refer program and V7/x86

Since I was writing this in roff anyway, I figured I might as well take advantage of its capabilities – I wanted to use refer. It is meant to keep a list of references (think BibTeX). Trying to run it, I got this:

$ /bin/refer
/bin/refer: syntax error at line 1: ‘)’

The system was trying to run the file as shell script. This also happens for tbl. It was actually an executable for which support got removed during the port (see I contacted Robert Nordier about this; he suggested I remove the -i and -n flags and recompile refer. Now it runs, exhibiting strange behavior instead: For all intents and purposes, refer is quite unusable like this. Fixing this is beyond my capacity, unfortunately, and (understandably) Robert Nordier does not feel up to diving into it, either. Thus, we’ll have to live without the luxury of a list of references.

6. Getting the Data off the Disk

I’m writing this text on V7/x86 in a virtual machine. There are multiple ways I could try to get it off the disk image, such as via a floppy image or something. However, that sounds like effort. I’ll try to search for it in the raw disk image instead and just copy it out from there. Update: I’ve had to go through the shared floppy route. The data in this file is split up on the underlying file system. Fortunately, /dev/ entries are just really fancy files. Therefore, I could just write with tar to the floppy directly without having to first create an actual file system. The host could then use that “floppy” as a tar file directly.

Even when I have these roff sources, I still need to get them in a readable format. I’ll have to cheat and use groff -Thtml to generate an HTML version to put on the blog. However, to preserve some semblance of authenticity, I’ll also put the raw roff source up, along with the result of running nroff over it on the version running on V7/x86. That version of nroff attributes the trademark to Bell Laboratories. This is wrong. UNIX is a registered trademark of The Open Group.

7. Impressions

ChuckMcM was right. When you’re grateful for vi, staring at a blob of text with no syntax highlighting and with limited space, you start appreciating troff/nroff much more. In particular, LaTeX tends to have fairly verbose commands. Scanning through those without syntax highlighting becomes more difficult. However, ‘‘parsing’’ troff/nroff syntax is much easier on one’s mind. Additionally, the terse commands help because


stands out much less than


That can be helped by adding whitespace, but then you remove some precious context on your tiny 80×24 screen. troff/nroff are very much children of their time, but they’re not as bad as I may have made them look last time. Having said that, there’s no way you’ll ever convince me to actually touch troff/nroff macros.

As for the system as a whole, I was positively surprised how usable it was by today’s standards. The biggest challenge is getting the system up and shutting it down again, as well as moving data to and from it. I did miss having a search function whenever I was looking for information on roff in volume 2A of the manual.

I have the greatest of respect for the V7/x86 project. Porting an ancient operating system that hardcoded various aspects of the PDP-11 in scattered places must have been extremely frustrating. The drivers were written in the ancient version of C that is used on V7 (see /usr/sys/dev).


26 thoughts on “Life in UNIX® V7: an attempt at a simple task

    • Mr. Nordier told me that he’d never touched the UUCP stuff, so this may be an issue specific to V7/x86. Though UUCP has traditionally always been a very special hell.

  1. I started with v6 UNIX. It was so much better than others I had used. And then when we got v7, it was a great step forward. An example is that v7 introduced the lseek syscall which doubled the seek offset to 32-bits instead of v6’s 16-bit seek syscall. Blocks were 512 bytes, so a block size of 20 is 10KB. Our editor of choice was a slightly enhanced ed. Using it wasn’t too bad.

    However, I confess that when I tried out the V7/x86 port, it felt like wearing a strait jacket compared to what’s available today.

    • I also started on v6. V7 introduced the last major changes in C before the ANSI standard. In v6 C, variable initialization didn’t use = signs:

      int i 42;

      And the assignment operators were in the other order:

      i =+ 2;

      which created problems with =- and lead to the change.

      i = -2; /* Assign -2 to i */
      i =- 2; /* Decrement i by 2 */

      There were other differences I forget now.

  2. I don’t suppose you asked Robert Nordier if he intends to update v7/x86 in the future. I’ve had fun playing with it but it does have bugs…

    • I’ve got a longer post about backspace coming up. tl;dr: not exactly, but I was an idiot and did things the a-lot-harder-than-it-should-have-been way.

      • Lol, I look forward to reading it. I’ve been playing UNIX for more than 25 years and I don’t miss the days of terminals (things like backspace was one of the many headaches moving between the various flavours).

        • I’m somewhat curious about the copyright status of the files distributed there. Somehow, I don’t think AT&T would be quite that thrilled to see its intellectual property on there, though I suppose they wouldn’t bother with a lawsuit or even DMCA takedown.

            • TUHS also had a special arrangement prior to that, where individuals could get source access all the way up to SYSIII. It was $100, and I’m do glad that I did get one, before they stopped issuing them.

              So this entitles me to both 16bit and 32bit versions of SYSIII. It was great fun getting the 32bit VAX version running on SIMH.

              I was almost thinking of starting up a zoo again….

              • AFAIK AT&T’s DWB (Documenter’s Workbench) was not part of AT&T UNIX. On the vetusware (no direct link!!! ) there are a sources for the DWB in the Dynix PTX and National Semiconductor 32000 SYSVR2 sources.

          • The copyright situation with UNIX is complicated and interesting. AT&T no longer owns the UNIX copyrights, and hasn’t for a very long time. Novell owned them for quite a while, following AT&T’s sale of Unix System Laboratories to Novell in 1995. Most recently, Novell was purchased by Micro Focus in 2014. I’m not aware of Novell ever giving up the UNIX copyrights (see: SCO vs. Novell), so I presume the copyrights now belong to Micro Focus.

  3. If your nroff doesn’t have a Bell Labs copy right, then it is hardly authentic for V7. The Open Group wasn’t even formed until 1996 and I remember writting TM’s at the Labs with nroff in the late 70’s when V7 is already on its way out. I was an assembler programmer and troff still makes my head hurt. If you are just interested in writing a paper as opposed to playing with layout, nroff is the way to go.

    • Truth. Bell Labs is the true originator and thus copyright holder of UNIX. The Open Group came much, much later, an ungrateful heir.

      • I suppose that was bad wording on my part. I meant it that Bell Labs no longer has the trademark. Trademark misattribution would possibly anger the current trademark holder (The Open Group), and that’s not a legal risk I wanted to carry.

        • The only thing open about the open group is their willingness to take that 100,000 USD and call anything a Unix.

          Remember when some Chinese outfit paid it to have Linux called Unix?

  4. Well, after all, you are not one of those “real programmers” that did the think. When I begun to read your post I was astonished of hearing that there’s a guy like me, that loves old programming practices. My first decept comes when you say that you have assigned 16Mb ram to the thing.

    Had you run it in simh, you’d get that a pdp-11/45 holds for an amount of around 256Kb max, and a virtual memory space of 64Kb text, and 64Kb data/stack. This probably makes vi completely useless, so you’d finnally end working with ed.

    ed has a problem at first. You tend to think you’ll never end the task. But after just two days working with it, you’ll begin getting confident and you’ll have a better idea of your document, will begin to write commands like

    /printf(“Hello,/s/”Hello/”Good bye,/

    and the like. You say the interrupt key cannot be adjusted. You are right, but a little digging will show you that it is implemented in the kernel, so a short patch to stty(1) command will allow you to solve the problem (As I did on mine, but using ed(1)) Below is the patch. Just run
    $ ed <stty.c.patch stty.c

    and here is the patch file:

    $ diff -e stty.c $HOME/stty.c
    ioctl(1, TIOCSETC, &cchars);
    if (eq("intr")) {
    if (**++argv = '^')
    cchars.t_intrc = (*argv)[1] & 037;
    cchars.t_intrc = **argv;
    ioctl(1, TIOCGETC, &cchars);
    struct tchars cchars;

    after that, you'll have the possibility of setting the interrupt key to whatever you want.

    Another thing that surprised me is how to import/export things from that unix. Well, the copy I'm running on simh allows me to write tapes, and I wrote a simple program to convert the tape format of simh (fully documented on the simh documentation, which, by the way, is misteriously written in Microsoft Word format 🙂 ) and this way I was able to produce and export tape files from my unix v7.

    The version I'm running is a Dennis Ritchie prepared tape, found in, under the name unix_v7_rl.dsk, I can provide you a full compliant simh configuration file and the disk images I use (already patched if you wish) As today, they are under Caldera License (and not the Open Group) but the Open Group didn't exist at the date of release of V7 (1978)

    My approach was different than yours, I wanted the original code, to see (and to show to anyone interested on it) how all these brave men did it. Vi is very interesting. But some practice on ed will give you a different view of the thing. Editing roff source files is quite easy with ed, as everything is left adjusted in source. Editing C source files is a different thing, mostly if you want your source to be legible after all.

    Another thing you need to patch is the date(1) command. It doesn't allow you to enter dates after 1999 (it uses only two digits for the year, and it doesn't convert years after 1999 —nobody expected it to be alive after that date 🙂 ) My first edit was to patch the date command, and I have to admit, I loved these men, that were able in such conditions to write the full unix kernel. You just have to insert a line that says:

    if (year < 70) year += 100;

    at some point in date.c (you'll find it by yourself) but just give it a try, and try to conserve the source code legibility. I get completely annoyed when I listen at some guys saying that old unix code sucks, because these guys used s (for source) and t (for target) as variable names, or the like. You need some ed(1) training is what I normally answer.

    Well, this was my two cents contribution. Now you have for a next part blog. This time write things using ed (after some practice, you get to it) I have also patched getty.c to allow for /etc/issue showing at your console, and LOGIN and USER environment variables defined in login.c Your TERM variable, although has to wait to be initialized in your .profile file, but trying to fit vi(1) in 64Kb+64Kb is just a pain (i will probably do that someday, but for the moment I prefer to export this to my host and use git to maintain source code versions 🙂 For now I'm trying to be able to compile a kernel version adapted to use larger disks (rp) than rl disks (max 10Mb) so I can fit all the system plus sources in a single disk and use rl disks for swapping.

    If you need some support, don't hesitate to ask me.

Leave a Reply to Sevan Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.