Commit Diff


commit - a7eb134e8717c2ea831066891314cf74fa4a6ad3
commit + aa83d77271f0d2be72067058f78abd1780f3b69e
blob - /dev/null
blob + d06e9a0599966ad285596916dc7df96701312b2f (mode 755)
--- /dev/null
+++ bin/tref
@@ -0,0 +1,31 @@
+awk '
+BEGIN{
+	print ".nr Rp 1"	# supress ... Rx lines
+	first=1
+}
+
+#these come first
+$1=="..." && $2=="Rx"{
+	ref[$4] = $3
+	next
+}
+
+first {
+	printf(".lf %d %s\n", 1, inputfile)
+	lineoffset=NR-1
+	first=0
+}
+
+$1==".Rf"{
+	if($2 in ref)
+		$2=ref[$2]
+	else
+		printf("tref: %s:%d: no ref for %s\n",
+			inputfile, NR-lineoffset, $2) >"/dev/stderr"
+}
+
+{
+	print
+}
+
+' $*
blob - /dev/null
blob + afd1c379b8d5fe5d18015ab0577080ab7b1efe40 (mode 644)
--- /dev/null
+++ src/cmd/index/README
@@ -0,0 +1,80 @@
+
+The indexing programs here are modified from the versions printed in
+Bentley & Kernighan, EP-ODD V1 #1.  The programs are also described in
+AT&T Bell Laboratories Computing Science Technical Report No. 128,
+``Tools for Printing Indexes''.
+
+Changes from the published version derive from further experience
+after the paper was frozen, plus some cleanup and corrections by Joe
+Kruskal (to whom many thanks), plus some very local features for
+printing the AMPL book.
+
+
+USING THE PROGRAMS
+
+install
+	makes the appropriate files executable.  since this file
+	is not executable, use by typing "sh <install"
+ix.test
+see.terms
+	these two files provide test input.  make.index produces
+	standard output and files
+		foo[1-9] foo.regular foo.see foo.hier foo.all
+	from ix.test and see.terms.  to make sure things
+	work when you first unbundle this file, type
+		sh <install
+		make.index ix.test >foo.ix
+		troff -ms foo.ix >foo.out
+	and then examine the troff output foo.out
+cleanup
+	removes the garbage files left around for debugging
+
+
+CHANGES FROM THE PAPER
+
+make.index
+	handles "see" file see.terms.  A line like
+		algorithms<tab>searching, sorting
+	generates in the final index
+		algorithms see searching, sorting
+	a 3rd field of %also makes it
+		algorithms see also searching, sorting
+doclean
+deroman
+range.prep
+	minor change to defend against bug in some versions of "sort"
+rotate
+	moved here (and changed as necessary) to remove subtle bug.
+	see check.data below
+range.sort
+	-u option on sort removes duplicate entries on same page
+range.collapse
+reroman
+	page number concatenation removed from here ...
+num.collapse
+	and moved to here.   also commas between numbers now
+	inserted here (to make see terms easier)
+gen.key
+	literals protected differently in gsub commands.
+	rules for non-alpha index terms slightly richer:
+		purely nonalphabetic lines first
+		lines with leading digits next
+		ordinary lines last
+see.prep
+	changed to match changes above, and to rely on font-changing {}
+final.sort
+	uses -d option for "telephone directory" order.
+hierarchy
+	a rather special purpose version to replace runs of items
+	with a common one or two word prefix and replace them by
+	a head word and indented lines.
+	this also does some rearrangement to bring see terms to the top,
+	and terms with formatting info to the bottom;  this is not
+	always the right thing to do.
+format
+	letter changes (.YY) determined by first letter.
+	minor rearrangement of how output line is created.
+	commas no longer added here.
+	[Some systems have a disk-formatting program called format.]
+check.data
+	new program that catches subtle errors in the data
blob - /dev/null
blob + cad2edf320e15fbd2d158e8804cbee8427ebb267 (mode 644)
--- /dev/null
+++ src/cmd/index/cleanup
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+rm foo[1-9] foo.*
blob - /dev/null
blob + 5129e5110dcb2bf484af450a58519f7a86adeb14 (mode 644)
--- /dev/null
+++ src/cmd/index/deroman
@@ -0,0 +1,18 @@
+awk ' # deroman
+#   Input:  string (tab) [arab or roman]
+#   Output: string (tab) [arab]
+
+#	Roman numeral n is replaced by arab n-1000 (e.g., iii -> -997)
+BEGIN		{ FS = OFS = "\t"
+		  # set a["i"] = 1, a["ii"] = 2, ...
+		  s =   "i ii iii iv v vi vii viii ix x"
+		  s = s " xi xii xiii xiv xv xvi xvii xviii xix xx"
+		  s = s " xxi xxii xxiii xxiv xxv xxvi xxvii xxviii xxix xxx"
+		  n = split(s, b, " ")
+		  for (i = 1; i <= n; i++) a[b[i]] = i
+		}
+$2~/^[ivxlc]+$/	{ if ($2 in a) $2 = -1000 + a[$2]
+		  else print "deroman: bad number: " $0 | "cat 1>&2"
+		}
+		{ print }
+' $*
blob - /dev/null
blob + c1763fd948dc283fa3d0314f8e6a6af2732dac36 (mode 644)
--- /dev/null
+++ src/cmd/index/doclean
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+awk ' # doclean
+
+#   Input:  "number tab IX string
+#	107     IX self-reference #1186 -
+#	281     TL APPENDIX A  AMPL Reference Manual   #26 -
+#   Output:        string (tab) number
+#	excess spaces are removed output string
+#	note reversal of order;  rest of programs expect it
+
+# This contains some special pleading for the AMPL book
+
+BEGIN	      { FS = OFS = "\t" }
+
+/\t(TL|H1|H2|H3|LASTPAGE)/	{ next }	# zap expected noise
+
+$0 !~ /[0-9ixv]+\tIX / {
+	print "doclean: non index line: " $0 | "cat 1>&2"; next
+}
+
+{	sub(/IX +/, "", $2)	# zap "IX "
+        sub(/ +#[0-9]+ .*$/, "", $2)	# zap trailing blanks, slug, file
+        gsub(/  +/, " ", $2)	# compress internal blanks
+        print $2, $1		# item (tab) page number
+}
+' $*
blob - /dev/null
blob + 37c68a24b54c310a723117a43f284f32047cc323 (mode 644)
--- /dev/null
+++ src/cmd/index/final.sort
@@ -0,0 +1,5 @@
+# final.sort
+#   Input/Output: sort key (tab) string (tab) numlist
+#   Sort by $1 (string)
+
+sort -t'	' +0fd -1 +0f -1 $*
blob - /dev/null
blob + 87e1a385fb35e0132f79ab174acf27a003b80d81 (mode 644)
--- /dev/null
+++ src/cmd/index/format
@@ -0,0 +1,64 @@
+awk ' # format
+#   Input:  sort key (tab) string (tab) numlist
+#   Output: troff format, commands interpreted
+
+BEGIN {	FS = "\t"
+	s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz "
+	# set upper["a"] = "A"
+	for (i = 1; i <= 27; i++) upper[substr(s,i+27,1)] = substr(s,i,1)
+	# set lower["a"] =  lower["A"] ="a"
+	for (i = 1; i <= 27; i++) {
+		lower[substr(s,i,1)] = substr(s,i+27,1)
+		lower[substr(s,i+27,1)] = substr(s,i+27,1)
+	}
+}
+{ # mark change between letters with .YY
+	# find first non-punctuation char
+	for (i = 1; (c = substr($1,i,1)) != ""; i++)
+		if (c ~ /[a-zA-Z0-9 ]/)
+			break
+	this = c
+	if (!(this in lower)) lower[this] = " "
+	this = lower[this]
+	if (this != last)  # && this != " ")
+		print ".YY", this, upper[last=this]
+	quoted = 0
+
+  # interpret font change language
+
+	if ($2 ~ /^   /)	# different macro, to avoid bad breaks in hier
+		print ".ZZ"
+	else
+		print ".XX"
+	if (NF == 3)
+		$0 = $2 "," "  " $3	# discard sort key, leave term .. numlist
+	else
+		$0 = $2
+
+	if ($0 ~ /%/) {
+		quoted = 1
+		gsub(/%%/,  "QQ0QQ", $0)
+		gsub(/%\[/, "QQ1QQ", $0)
+		gsub(/%\]/, "QQ2QQ", $0)
+		gsub(/%\{/, "QQ3QQ", $0)
+		gsub(/%\}/, "QQ4QQ", $0)
+		gsub(/%~/,  "QQ5QQ", $0)
+	}
+	gsub(/%e/, "\\e", $0) # %e -> \e
+	gsub(/~/, " ", $0)    # unpaddable spaces go away at last
+	if (gsub(/\[/, "\\\\&\\f(CW", $0))
+		gsub(/\]/, "\\fP",   $0)
+	if (gsub(/\{/, "\\f2",   $0))
+		gsub(/\}/, "\\fP",   $0)
+	if (quoted) {
+		gsub(/%/, "", $0)
+		gsub(/QQ0QQ/, "%", $0)
+		gsub(/QQ1QQ/, "[", $0)
+		gsub(/QQ2QQ/, "]", $0)
+		gsub(/QQ3QQ/, "{", $0)
+		gsub(/QQ4QQ/, "}", $0)
+		gsub(/QQ5QQ/, "~", $0)
+	}
+	printf "\\&%s\n", $0
+}
+' $*
blob - /dev/null
blob + a79227638e80f976ac2ac8c7145bb9bdd3e0292e (mode 644)
--- /dev/null
+++ src/cmd/index/gen.key
@@ -0,0 +1,57 @@
+awk ' # gen.key
+#   Input: Each input line has one of the following two forms:
+#	string                   (tab) numlist
+#	string " %key " sort.key (tab) numlist
+#   Output: Each output line has the form:
+#	sort.key (tab) string (tab) numlist
+
+BEGIN {	FS = OFS = "\t" }
+
+/ %key / { # use sort.key if it is provided
+	   i = index($1, " %key ")
+	   print substr($1, i+6), substr($1, 1, i-1), $2
+	   next
+	 }
+
+	{ # generate sort.key (in $2, by modifying string) if it is not provided
+	$3 = $2
+	$2 = $1
+
+	#Modify sort.key
+	# Remove some troff commands
+	gsub(/\\f\(..|\\f.|\\s[+-][0-9]|\\s[0-9][0-9]?/, "", $2)
+
+	# underscore -> 0, so "foo_gorp" sorts before "food"
+	gsub(/_/, "0", $2)
+
+	# quote character is %, space character is ~
+	quoted = 0
+	if ($2 ~ /%/) {  # hide quoted literals in Q
+		quoted = 1
+		gsub(/%%/,  "QQ0QQ", $2)
+		gsub(/%\[/, "QQ1QQ", $2)
+		gsub(/%\]/, "QQ2QQ", $2)
+		gsub(/%\{/, "QQ3QQ", $2)
+		gsub(/%\}/, "QQ4QQ", $2)
+		gsub(/%~/,  "QQ5QQ", $2)
+	}
+	gsub(/%e/, "\\", $2)		# implement troff escape
+	gsub(/~/, " ", $2)		# remove tildes
+	gsub(/[%\[\]\{\}]/, "", $2)	# remove % and font-changing []{}
+	if (quoted) {  # restore literals but without escape charcter
+		gsub(/QQ0QQ/, "%", $2)
+		gsub(/QQ1QQ/, "[", $2)
+		gsub(/QQ2QQ/, "]", $2)
+		gsub(/QQ3QQ/, "{", $2)
+		gsub(/QQ4QQ/, "}", $2)
+		gsub(/QQ5QQ/, "~", $2)
+	}
+	if ($2 ~ /^[^a-zA-Z]+$/)	# purely nonalphabetic lines go first
+		$2 = "  " $2
+	else if ($2 ~ /^[0-9]/)		# lines with eading digits come next
+		$2 = " " $2
+					# otherwise whatever final.sort does
+}
+
+	{ print $2, $1, $3 } 
+' $*
blob - /dev/null
blob + 16cda592a81335f002739aaae607437fbc33f07f (mode 644)
--- /dev/null
+++ src/cmd/index/hierarchy
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+# input:
+#	key (tab) string (tab) page numbers
+#		command	command	123
+#		command, data	command, [data]	11
+#		command, display	command, [display]	11, 54, 63, 75
+#		command, model	command, [model]	11
+#		command, quit	command, [quit]	5, 16
+# output:
+#	key (tab) string (tab) page numbers
+#		key	command  123
+#		key	   [data]  11
+#		key	   [display] ...
+#		key	   [model] ...
+#		key	   [quit] ...
+
+awk '
+BEGIN	{ FS = OFS = "\t" }
+
+{	line[NR] = $0; x[NR] = $2 "\t" $3; y[NR] = $1 }
+
+# find a sequence that have the same prefix
+# dump prefix, then each instance with spaces instead of prefix
+END {
+	for (i = 1; i <= NR; i = j+1) {
+		j = findrun(i)		# returns last elem of run
+		if (j > i)
+			printrun(i, j)
+		else
+			print y[i], x[i]
+	}
+}
+
+function findrun(s,	j, p, np) {	# find y[s],y[s+1]... with same prefix
+	p = prefix(y[s])
+	np = length(p)
+	for (j = s+1; j <= NR; j++) {
+		if (y[j] == p)			# same, so include
+			continue
+		if (index(y[j], p) != 1)	# no match
+			break
+		c = substr(y[j], np+1, 1)
+		if (c != " " && c != ",")	# has to be whole word prefix
+			break
+	}
+	return j-1
+}
+
+function prefix(s,	n) {	# find 1st word of s: same sort key, minus ,
+	gsub(/,/, "", s)
+	n = index(s, " ")
+	if (n > 0)
+		return substr(s, 1, n-1)
+	else
+		return s
+}
+
+function printrun(s, e,		i) {	# move [...] to end, "see" to front
+	s1 = 0; e1 = 0; p1 = 0; i1 = 0
+	for (i = s; i <= e; i++) {
+		if (x[i] ~ /{see/) {		# see, see also
+			sx[s1] = x[i]
+			sy[s1] = y[i]
+			s1++
+		} else if (x[i] ~ /^\[/) {	# prefix word is [...]
+			px[p1] = x[i]
+			py[p1] = y[i]
+			p1++
+		} else if (x[i] ~ /\[.*\]/) {	# [...] somewhere else
+			ex[e1] = x[i]
+			ey[e1] = y[i]
+			e1++
+		} else {			# none of the above
+			ix[i1] = x[i]
+			iy[i1] = y[i]
+			i1++
+		}
+	}
+	if (e-s+1 != s1 + p1 + i1 + e1) print "oh shit" >"/dev/stderr"
+
+	for (i = 0; i < s1; i++)	# "see", one/line
+		print sy[i], sx[i]
+	if (i1 > 1)
+		printgroup(ix,iy,0,i1)	# non [...] items
+	else if (i1 == 1)
+		print iy[0], ix[0]
+	if (e1 > 1)
+		printgroup(ex,ey,0,e1)	# prefix [...] items
+	else if (e1 == 1)
+		print ey[0], ex[0]
+	# for (i = 0; i < p1; i++)	# [prefix] ... items
+	# 	print py[i], px[i]
+	if (p1 > 1)
+		printgroup(px,py,0,p1)	# [prefix] ... items
+	else if (p1 == 1)
+		print py[0], px[0]
+}
+
+function printgroup(x, y, s, e,		i, j) {
+	split(x[s], f23)
+	if (split(f23[1], temp, " ") > 1) {
+		pfx = temp[1] " " temp[2]	# 2-word prefix
+		for (i = s+1; i < e; i++) {
+			if (index(x[i], pfx) != 1)
+				break
+			c = substr(x[i], length(pfx)+1, 1)
+			if (c != " " && c != ",")	# has to be whole word prefix
+				break
+		}
+		if (i == e) {
+			# print "got a run with", pfx
+			sub(/ /, "@", f23[1])
+			for (i = s; i < e; i++)
+				sub(/ /, "@", x[i])	# take @ out later
+		}
+	}
+	n = sub(/,?[ ~]+.*/, "", f23[1]) # zap rest of line
+
+	sub(/,$/, "", f23[1])
+	if (n > 0) {	# some change, so not a single word
+		sub(/@/, " ", f23[1])
+		print y[s], f23[1]	# print main entry
+	}
+	for (j = s; j < e; j++) {
+		split(x[j], f23)
+		sub(/^[^, ]+[, ]+/, "   ", f23[1])
+		sub(/@/, " ", f23[1])
+		print y[s], f23[1], f23[2]
+	}
+}
+
+' $*
blob - /dev/null
blob + 0cd264f946463787e706a6bce9b54a407457814b (mode 644)
--- /dev/null
+++ src/cmd/index/install
@@ -0,0 +1,3 @@
+chmod +x check.data cleanup deroman doclean final.sort format \
+	gen.key hierarchy make.index num.collapse range.collapse \
+	range.prep range.sort reroman rotate see.prep
blob - /dev/null
blob + d8d17d9d593e189ff1f017c37a732bc41ae41cca (mode 644)
--- /dev/null
+++ src/cmd/index/ix.macro
@@ -0,0 +1,4 @@
+.de ix
+.ie '\\n(.z'' .tm ix: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9	\\n%
+.el \\!.ix \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+..
blob - /dev/null
blob + 41f824acd83d263b02eb1e45548c3bab10fa3444 (mode 644)
--- /dev/null
+++ src/cmd/index/ix.test
@@ -0,0 +1,17 @@
+ix: line	1
+ix: line	2
+ix: line	2
+ix: line	3
+ix: %begin line	11
+ix: line	15
+ix: %end line	19
+ix: \f(CWps\fP \f(CW-a\fP	34
+ix: \f(CWps\fP command	34
+ix: \f(CWPS1\fP shell variable	36
+ix: sorting~a book~index	25
+ix: [pr]~[-]{n} command	31
+ix: [%[^]...[%]] regular~expression	32
+ix: [printf] [%%d] specification	35
+ix: T\v'.17m'\h'-.12m'E\h'-.12m'\v'-.17m'X %key TEX	40
+ix: CONTENTS 7 Input and Output
+ix: [%~] tilde	41
blob - /dev/null
blob + aa89993d9189b10772d41db6474267568046e445 (mode 644)
--- /dev/null
+++ src/cmd/index/make.index
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#check.data $*
+
+doclean $* >foo1
+deroman foo1 >foo2
+range.prep foo2 >foo3
+rotate foo3 >foo4
+range.sort foo4 >foo5
+range.collapse foo5 >foo6
+reroman foo6 >foo7
+num.collapse foo7 >foo8
+gen.key foo8 >foo9
+see.prep see.terms | gen.key >foo.see
+final.sort foo9 foo.see >foo.regular
+
+hierarchy foo.regular >foo.hier
+
+format foo.hier >foo.all
+
+cat foo.all
+
+#cleanup
blob - /dev/null
blob + 47203f522a8507f5146dbd9dbea196168dee31f2 (mode 644)
--- /dev/null
+++ src/cmd/index/num.collapse
@@ -0,0 +1,21 @@
+awk ' # num.collapse
+#   Input:  lines of form: string (tab) num1 [(space) num2]
+#   Output: lines of form: string (tab) fancy.num.list
+#
+#   fancy.num.list contains items, separated by ", ", of form: num or num-num
+#	Sequence of input lines with same value of string is combined
+#	into a single output line.  Each input line contributes either
+#	num or num-num to output line.
+
+BEGIN	{ FS = OFS = "\t" }
+
+	{ sub(/ /, "\\(en", $2) }  # use - if there is no en dash
+
+$1 != p	{ p = $1
+	  if (NR > 1) printf "\n"
+	  printf "%s\t%s", $1, $2
+	  next
+	}
+	{ printf ", %s", $2 }
+END	{ if (NR > 0) printf "\n" }
+' $*
blob - /dev/null
blob + 9119cea681c91ca41d7217411e3638c05b3acfe7 (mode 644)
--- /dev/null
+++ src/cmd/index/range.collapse
@@ -0,0 +1,35 @@
+awk ' # range.collapse
+#   Input:  lines of form: string (tab) ["b"|"e"|"a"] (tab) number
+#   Output: lines of form: string (tab) num [(space) num]
+#	In sequence of lines with same value of string:
+#	b line and following e line are combined into single line:
+#		string (tab) num num
+#	a line disappears if between paired b and e 
+#	a line otherwise becomes single line:
+#		string (tab) num
+
+function error(s) {
+	print "range.collapse: " s " near pp " rlo "-" rhi | "cat 1>&2"
+}
+function printoldrange() {
+	if (range == 1) { error("no %end for " term); rhi = "XXX" }
+	if (NR > 1) {
+		if (rlo == rhi)
+			print term, rlo
+		else
+			print term, (rlo " " rhi)
+	}
+	rlo = rhi = $3	# bounds of current range
+}
+
+BEGIN	      { FS = OFS = "\t" }
+$1 != term    {	printoldrange(); term = $1; range = 0 }
+$2 == "e"     {	if (range == 1) { range = 0; rhi = $3 }
+		else { printoldrange(); error("no %begin for " term); rlo = "XXX" }
+		next
+	      }
+$3 <= rhi + 1 { rhi = $3}
+$3 >  rhi + 1 { if (range == 0) printoldrange() }
+$2 == "b"     {	if (range == 1) error("multiple %begin for " term); range = 1 }
+END	      {	if (NR == 1) NR = 2; printoldrange() }
+' $*
blob - /dev/null
blob + 16ae3cc83585ebe6cbd619fe8a4747e6b6652d9d (mode 644)
--- /dev/null
+++ src/cmd/index/range.prep
@@ -0,0 +1,10 @@
+awk ' # range.prep
+#   Input:  ["%begin"|"%end"|""] string (tab) number
+#   Output: string (tab) ["b"|"e"|"a"]  (tab) number
+
+BEGIN		{ FS = OFS = "\t" }
+		{ f2 = "a" }
+$1 ~ /^%begin/	{ f2 = "b"; sub(/^%begin */, "", $1) }
+$1 ~ /^%end/	{ f2 = "e"; sub(/^%end */, "", $1) }
+		{ print $1, f2, $2 }
+' $*
blob - /dev/null
blob + e2cdd493abca17cb3b5b47b2f8eee793beeec31c (mode 644)
--- /dev/null
+++ src/cmd/index/range.sort
@@ -0,0 +1,5 @@
+# range.sort
+#   Input/Output: string (tab) ["b"|"e"|"a"] (tab) number
+#   Sort by $1 (string), $3 (number), then $2 ("b"|"e"|"a")
+
+sort -u '-t	' +0 -1   +2n   +1 -2   $*
blob - /dev/null
blob + 0d9fa00b2c61484f042c33f6311c99931ca1e019 (mode 644)
--- /dev/null
+++ src/cmd/index/reroman
@@ -0,0 +1,23 @@
+awk ' # reroman
+#   Output: string (tab) arab1            [(space) arab2]
+#   Input:  string (tab) arab1 or roman1  [(space) arab2 or roman2]
+
+BEGIN	{ FS = OFS = "\t"
+	  # set a[1] = "i", a[2] = "ii", ...
+	  s =   "i ii iii iv v vi vii viii ix x"
+	  s = s " xi xii xiii xiv xv xvi xvii xviii xix xx"
+	  s = s " xxi xxii xxiii xxiv xxv xxvi xxvii xxviii xxix xxx"
+	  split(s, a, " ")
+	}
+$2 < 0	{ n = split($2, b, " ")
+	  for (i = 1; i <= n; i++) {
+		if (b[i] >= 0) continue
+	  	j = 1000 + b[i]
+	  	if (j in a) b[i] = a[j]
+	  	else print "reroman: bad number: " $0  | "cat 1>&2"
+	  }
+	  $2 = b[1]
+	  if (n > 1) $2 = b[1] " " b[2]
+	}
+	{ print }
+' $*
blob - /dev/null
blob + 76c0274c3a56475125c340152475565f88ec465b (mode 644)
--- /dev/null
+++ src/cmd/index/rotate
@@ -0,0 +1,31 @@
+awk ' # rotate
+#   Input line: string (tab) ["b"|"e"|"a"] (tab) number
+#   Output several lines:
+#	        string (tab) ["b"|"e"|"a"] (tab) number
+#	rotated string (tab) ["b"|"e"|"a"] (tab) number
+#	rotated string (tab) ["b"|"e"|"a"] (tab) number
+#		...
+#
+#   In the output strings, tildes are replaced by spaces
+
+BEGIN	 { FS = OFS = "\t" }
+
+/ %key / { # if explicit sort.key is provided, do not rotate
+	   print $0
+	   next
+	 }
+
+	 {
+	   t1 = $1	#t1 will be $1 with tildes changed to spaces
+	   gsub(/%~/,  "QQ5QQ", t1)	#hide real tildes
+	   gsub(/~/, " ", t1)		#change tildes to spaces
+	   gsub(/QQ5QQ/,  "%~", t1)	#restore real tildes
+	   print t1, $2, $3
+	   i = 1
+	   while ((j = index(substr($1, i+1), " ")) > 0) {
+		i += j
+		printf("%s, %s\t%s\t%s\n", \
+			substr(t1, i+1), substr(t1, 1, i-1), $2, $3)
+	   }
+	 }
+' $*
blob - /dev/null
blob + 33d4c2df956745610445d8612e47796d23ceec42 (mode 644)
--- /dev/null
+++ src/cmd/index/see.prep
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+awk ' # see.prep
+#   Input:  string "\t"  	     string
+#   Output: string "\t{see [also]} " string
+
+BEGIN { FS = "\t" }
+$3 ~ /%also/	{ print $1 "\t{see also} " $2; next }
+		{ print $1 "\t{see} " $2 }
+' $*
blob - /dev/null
blob + 8a8aa6b744e8e506f014159c11209386725b2b33 (mode 644)
--- /dev/null
+++ src/cmd/index/see.terms
@@ -0,0 +1,69 @@
+soft constraints	penalty function
+[>>> <<<]	error messages
+omitted data value	default symbol
+data value, omitted	default symbol
+["].\|.\|.["], ['].\|.\|.[']	quotes
+[..]	arithmetic progression
+[=] attribute	defined variables
+[.body]	constraint body
+built-in function	function
+Cartesian product	cross product
+conditional expression	logical expression, [if]-[then]-[else]
+conditional declaration	[if] indexing expression
+[.dat] file	file
+[.dual]	dual value
+Lagrange multiplier	dual variables
+index	dummy index
+iterated operator	operator
+comparison operators	relational operators
+[maximize]	objective declaration
+[minimize]	objective declaration
+model file	file
+[.mod] file	file
+price, shadow	dual value
+problem	model	%also
+[.rc]	reduced cost
+data reset	[reset] [data] command
+scaling along arc	loss in network flow
+scope	dummy index	%also
+set of sets	indexed collection
+shadow price	dual value
+marginal~value	dual value
+simplification of expression	presolve	%also
+starting guess	initial values of variables
+[subject] [to]	constraint declaration
+[s.t.]	constraint declaration
+symmetric difference	[symdiff]
+set difference	[diff], [symdiff]
+syntax error	error messages
+messages	error messages
+space requirement	memory use
+viewing data	[display] command
+[;]	semicolon statement terminator
+array	one-dimensional set, parameter
+vector	one-dimensional set, parameter
+matrix	two-dimensional set, parameter
+command mode	model mode
+[>], [>>]	output to file
+redirection	output to file
+qualified name	[.] suffix
+member, dummy	dummy index
+membership test	[in], [within]
+subset test	[in], [within]
+such-that condition	logical condition
+set intersection	[inter]
+intersection, set	[inter]
+slope	piecewise-linear expression
+breakpoint	piecewise-linear expression
+command	statement	%also
+statement	command, declaration	%also
+declaration	statement	%also
+traffic flow	maximum flow model
+flow	network flow
+roll trim problem	cutting-stock problem
+argument	command-line argument
+zero-one programming	integer programming
+reading files	[model], [data]
+file inclusion	[model], [data]
+[include]	[model], [data]	%also
+transposition	table transposition
blob - 9e567989cdde37bfb0fb91e6c8be1c5a80dc0d5b
blob + 1dc365764305d63021d44850fca37b6b66cfa006
--- src/cmd/mkfile
+++ src/cmd/mkfile
@@ -5,7 +5,7 @@ SHORTLIB=sec fs mux regexp9 thread bio 9
 
 <$PLAN9/src/mkmany
 
-BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|vncv|postscript|mnihongo|mpm'
+BUGGERED='CVS|faces|factotum|mailfs|scat|upas|vac|venti|vncv|postscript|mnihongo|mpm|index'
 DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"`
 
 <$PLAN9/src/mkdirs