tMore troff bits; if you want them elsewhere, feel free to repo copy them. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit aa83d77271f0d2be72067058f78abd1780f3b69e
 (DIR) parent a7eb134e8717c2ea831066891314cf74fa4a6ad3
 (HTM) Author: wkj <devnull@localhost>
       Date:   Mon, 17 May 2004 03:22:35 +0000
       
       More troff bits; if you want them elsewhere, feel free to repo copy them.
       
       Diffstat:
         A bin/tref                            |      31 +++++++++++++++++++++++++++++++
         A src/cmd/index/README                |      80 +++++++++++++++++++++++++++++++
         A src/cmd/index/cleanup               |       3 +++
         A src/cmd/index/deroman               |      18 ++++++++++++++++++
         A src/cmd/index/doclean               |      27 +++++++++++++++++++++++++++
         A src/cmd/index/final.sort            |       5 +++++
         A src/cmd/index/format                |      64 +++++++++++++++++++++++++++++++
         A src/cmd/index/gen.key               |      57 +++++++++++++++++++++++++++++++
         A src/cmd/index/hierarchy             |     133 +++++++++++++++++++++++++++++++
         A src/cmd/index/install               |       3 +++
         A src/cmd/index/ix.macro              |       4 ++++
         A src/cmd/index/ix.test               |      17 +++++++++++++++++
         A src/cmd/index/make.index            |      23 +++++++++++++++++++++++
         A src/cmd/index/num.collapse          |      21 +++++++++++++++++++++
         A src/cmd/index/range.collapse        |      35 +++++++++++++++++++++++++++++++
         A src/cmd/index/range.prep            |      10 ++++++++++
         A src/cmd/index/range.sort            |       5 +++++
         A src/cmd/index/reroman               |      23 +++++++++++++++++++++++
         A src/cmd/index/rotate                |      31 +++++++++++++++++++++++++++++++
         A src/cmd/index/see.prep              |      10 ++++++++++
         A src/cmd/index/see.terms             |      69 ++++++++++++++++++++++++++++++
         M src/cmd/mkfile                      |       2 +-
       
       22 files changed, 670 insertions(+), 1 deletion(-)
       ---
 (DIR) diff --git a/bin/tref b/bin/tref
       t@@ -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
       +}
       +
       +' $*
 (DIR) diff --git a/src/cmd/index/README b/src/cmd/index/README
       t@@ -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
 (DIR) diff --git a/src/cmd/index/cleanup b/src/cmd/index/cleanup
       t@@ -0,0 +1,3 @@
       +#!/bin/sh
       +
       +rm foo[1-9] foo.*
 (DIR) diff --git a/src/cmd/index/deroman b/src/cmd/index/deroman
       t@@ -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 }
       +' $*
 (DIR) diff --git a/src/cmd/index/doclean b/src/cmd/index/doclean
       t@@ -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
       +}
       +' $*
 (DIR) diff --git a/src/cmd/index/final.sort b/src/cmd/index/final.sort
       t@@ -0,0 +1,5 @@
       +# final.sort
       +#   Input/Output: sort key (tab) string (tab) numlist
       +#   Sort by $1 (string)
       +
       +sort -t'        ' +0fd -1 +0f -1 $*
 (DIR) diff --git a/src/cmd/index/format b/src/cmd/index/format
       t@@ -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
       +}
       +' $*
 (DIR) diff --git a/src/cmd/index/gen.key b/src/cmd/index/gen.key
       t@@ -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 } 
       +' $*
 (DIR) diff --git a/src/cmd/index/hierarchy b/src/cmd/index/hierarchy
       t@@ -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]
       +        }
       +}
       +
       +' $*
 (DIR) diff --git a/src/cmd/index/install b/src/cmd/index/install
       t@@ -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
 (DIR) diff --git a/src/cmd/index/ix.macro b/src/cmd/index/ix.macro
       t@@ -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
       +..
 (DIR) diff --git a/src/cmd/index/ix.test b/src/cmd/index/ix.test
       t@@ -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
 (DIR) diff --git a/src/cmd/index/make.index b/src/cmd/index/make.index
       t@@ -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
 (DIR) diff --git a/src/cmd/index/num.collapse b/src/cmd/index/num.collapse
       t@@ -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" }
       +' $*
 (DIR) diff --git a/src/cmd/index/range.collapse b/src/cmd/index/range.collapse
       t@@ -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() }
       +' $*
 (DIR) diff --git a/src/cmd/index/range.prep b/src/cmd/index/range.prep
       t@@ -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 }
       +' $*
 (DIR) diff --git a/src/cmd/index/range.sort b/src/cmd/index/range.sort
       t@@ -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   $*
 (DIR) diff --git a/src/cmd/index/reroman b/src/cmd/index/reroman
       t@@ -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 }
       +' $*
 (DIR) diff --git a/src/cmd/index/rotate b/src/cmd/index/rotate
       t@@ -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)
       +           }
       +         }
       +' $*
 (DIR) diff --git a/src/cmd/index/see.prep b/src/cmd/index/see.prep
       t@@ -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 }
       +' $*
 (DIR) diff --git a/src/cmd/index/see.terms b/src/cmd/index/see.terms
       t@@ -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
 (DIR) diff --git a/src/cmd/mkfile b/src/cmd/mkfile
       t@@ -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