diff --git a/.cvsignore b/.cvsignore
index bc713b6a2115ca9f03520381ec211cb0da9121a3..2601178bec34ef0df63afd1677325c18b3897ce5 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -11,3 +11,4 @@ bootblock
 bootother
 bootother.out
 parport.out
+fmt
diff --git a/Makefile b/Makefile
index 24583bdf653e66429e513faa18a7e6b6abb83ab5..c1099d4512022c6e402b3ab4d9b7e03401c55820 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,7 @@ tags: $(OBJS) bootother.S init
 	etags *.S *.c
 
 PRINT =	\
+	runoff.list \
 	README\
 	types.h param.h defs.h x86.h asm.h elf.h mmu.h spinlock.h\
 	bootasm.S bootother.S main.c init.c spinlock.c\
@@ -69,15 +70,11 @@ PRINT =	\
 	console.c\
 	string.c\
 
-# make a print, but the resulting xv6.ind need some editing
-print: $(PRINT)
-	lgrind -d ./lgrindef $(PRINT) > xv6.tex 
-	latex xv6.tex
-	makeindex xv6.idx
-	latex xv6.tex
-	dvips -o xv61.ps xv6.dvi
-	psnup -2 xv61.ps > xv6.ps
-	rm -f xv61.ps
+# make a printout
+xv6.pdf : $(PRINT)
+	./runoff
+
+print : xv6.pdf
 
 vectors.S : vectors.pl
 	perl vectors.pl > vectors.S
diff --git a/runoff b/runoff
new file mode 100755
index 0000000000000000000000000000000000000000..96cbb18ffc9c9401114279e6e1f9b6d92741b3f8
--- /dev/null
+++ b/runoff
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+echo This script takes a minute to run.  Be patient. 1>&2
+
+# pad stdin to multiple of 120 lines
+pad()
+{
+	awk '{print} END{for(; NR%120!=0; NR++) print ""}'
+}
+
+# create formatted (numbered) files
+mkdir -p fmt
+rm fmt/*
+cp README fmt
+files=`grep -v '^#' runoff.list | awk '{print $1}'`
+n=99
+for i in $files
+do
+	awk -v 'n='$n '
+		BEGIN{n=int((n+49)/50)*50-1; nb=0; nr=0}
+		NF==0 { nb++; next } 
+		{for(i=0; i<nb; i++) printf("%04d\n", n+ ++nr); nb=0; printf("%04d %s\n", n+ ++nr, $0)}
+		END{for(nr++; nr%50 != 1; nr++) printf("%04d\n", n+nr);}
+		' $i >fmt/$i
+	n=`tail -1 fmt/$i | awk '{print $1}'`
+done
+
+# create table of contents
+pr -e8 -t runoff.list | awk '
+/^[a-z]/ {
+	s=$0
+	f="fmt/"$1
+	getline<f
+	close(f)
+	n=$1
+	printf("%02d %s\n", n/100, s);
+	next
+}
+{
+	print
+}' >fmt/toc
+
+# make definition list
+cd fmt
+awk '
+	/^[0-9]+ [A-Za-z0-9_]+ .*[A-Za-z0-9_].*;/ {
+		s=$0;
+		sub(/\[.*/, "", s);
+		sub(/\(.*/, "", s);
+		sub(/ *=.*/, "", s);
+		sub(/.* \**/, "", s);
+		sub(/;.*/, "", s);
+		print $1, s
+	}
+	$2=="#define" { 
+		if($3 ~ /\(/){
+			sub(/\(.*/, "", $3); print $1, $3
+		} else {
+			s = ""
+			for(i=4; i<=NF; i++){
+				s = s $i
+			}
+			print $1, $3, s
+		}
+	}
+	$2=="enum" { inenum = 1; v=-1; } 
+	$2 == "};" { inenum = 0; }
+	inenum && $2 ~ /^[A-Z][a-zA-Z0-9_]+$/ {
+		if($3 == "="){
+			s = ""
+			for(i=4; i<=NF; i++){
+				s = s " " $i
+			}
+			sub(/,$/, "", s);
+			sub(/^ /, "", s);
+			v = s;
+		}else
+			v++;
+		print $1, $2, v;
+	}
+	$2=="struct" && $3 ~ /^[A-Z][a-zA-Z0-9_]+$/ {
+		print $1, $3;
+	}
+' $files >defs
+9 sed -n 's/^([0-9]+ [a-zA-Z0-9_]+)(.*)$/\1/p' $files |
+	egrep -v ' (usage|main|if|for)$' >>defs
+(
+>s.defs
+
+# make reference list
+for i in `awk '{print $2}' defs | sort -fu`
+do
+	defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'`
+	echo $i $defs >>s.defs
+	uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' $files | awk '{print $1}'`
+	echo $i $defs
+	echo $uses |fmt -24 | sed 's/^/    /'
+done
+) >refs
+
+# build defs list
+awk '
+{
+	printf("%04d %s\n", $2, $1);
+	for(i=3; i<=NF; i++)
+		printf("%04d    \" \n", $i);
+}
+' s.defs > t.defs
+
+# format the whole thing
+(
+	pr -l60 -e4 README
+	pr -l60 -e4 toc
+	pr -l60 -h "definitions" -2 t.defs | pad
+	pr -l60 -h "cross-references" -2 refs | pad 
+	pr -l60 -e4 $files 
+) | mpage -m50t50b -o -bLetter -t -2 -FCourier -L60 >all.ps
+grep Pages: all.ps
+
+# if we have the nice font, use it
+nicefont=/home/am8/rsc/plan9/sys/lib/postscript/font/LucidaSans-Typewriter83
+if [ -f $nicefont ]
+then
+	(sed 1q all.ps; cat $nicefont; sed '1d; s/Courier/LucidaSans-Typewriter83/' all.ps) >allf.ps
+else
+	cp all.ps allf.ps
+fi
+ps2pdf allf.ps ../xv6.pdf
diff --git a/runoff.list b/runoff.list
new file mode 100644
index 0000000000000000000000000000000000000000..2fb524d021a80ebca114a7d45cb1d3398b6a1733
--- /dev/null
+++ b/runoff.list
@@ -0,0 +1,50 @@
+# basic headers
+types.h
+param.h
+defs.h
+x86.h
+asm.h
+elf.h
+mmu.h
+spinlock.h
+
+# low level startup
+bootasm.S
+bootother.S
+main.c
+init.c
+spinlock.c
+proc.h
+proc.c
+setjmp.S
+kalloc.c
+syscall.h
+trapasm.S
+traps.h
+trap.c
+vectors.pl
+syscall.c
+
+# file system
+buf.h
+dev.h
+fcntl.h
+stat.h
+fd.h
+fs.h
+fsvar.h
+fd.c
+fs.c
+bio.c
+ide.c
+pipe.c
+
+# mp and other "uninteresting" things
+mp.h
+ioapic.h
+mp.c
+lapic.c
+ioapic.c
+picirq.c
+console.c
+string.c