\documentstyle[noweb]{article}
\pagestyle{noweb}
\begin{document}
@
\section{Cross-reference and index support}
Here is is.
<<noidx>>=
#!/bin/rc
#
# Translated to rc by Russ Cox
# bugs -> rsc@plan9.bell-labs.com

delay=0
anchordist=0
while(! ~ $#* 0) {
	switch($1){
	case -delay
		delay=1
	case -docanchor
		anchordist=$2
		shift
	case *
		echo 'cannot happen -- '^$1^' passed to noidx' >[1=2]
		exit cannothappen
	}
	shift
}
awk -f /sys/lib/noweb/noidx.awk -v 'delay='$delay -v 'anchordist='$anchordist
@
<<noidx.awk>>=
<<functions>>
BEGIN { <<initialization>> nextline = 0 }
<<first pass>>
{ lines[nextline] = $0; nextline++ }
END { 
  for (i = 0; i < nextline; i ++) {
    <<second pass over [[lines[i]]]>>
    delete lines[i]
  }
  if (!delay) <<write trailers>>
}
@ %def lines nextline
<<initialization>>=
curfile = "standard input?"
lastchunkbegin = "never any chunks?" ;
<<initialization>>=
allchunks[0] = 0 ; allidents[0] = 0 ; indexlabels[0] = 0 
defanchors[0] = 0 ; uses[0] = 0 ; anchorlabel[0] = 0 ; indexanchorlabel[0] = 0
@ %def  allchunks allidents indexlabels defanchors uses anchorlabel indexancho
label
<<first pass>>=
/^@file /     { curfile = uniqueid(substr($0, 7)) }
/^@begin /    { lastchunkbegin = $0 }
/^@end docs / { if (anchordist > 0) <<insert and set [[lastanchorlabel]]>> }
/^@end code / { lastanchorlabel = "" }
@ %def curfile lastchunkbegin lastanchorlabel
<<first pass>>=
/^@defn /     { arg = substr($0, 7)
                allchunks[arg] = 1
                lastdefnlabel = newdefnlabel(arg)
                slipin("@xref label " lastdefnlabel)
                if (lastanchorlabel == "") lastanchorlabel = lastdefnlabel
                if (anchorlabel[arg] == "") anchorlabel[arg] = lastanchorlabel
		addlabel(defanchors, arg, lastanchorlabel)
                addud(chunkud, "defn", arg, lastanchorlabel)
		thisusecount = 0
              }
/^@use /      { if (lastchunkbegin ~ /^@begin code /) {
                  arg = substr($0, 6)
                  allchunks[arg] = 1
                  slipin("@xref label " lastdefnlabel "-u" (++thisusecount))
                  addlabel(uses, arg, lastanchorlabel)
                  addud(chunkud, "use", arg, lastanchorlabel)
                }
              }
@ %def allchunks lastdefnlabel
<<first pass>>=
/^@index use /  { arg = substr($0, 12)
                  allidents[arg] = 1
                  if (lastanchorlabel != "") addud(indexud, "use", arg, lastan
horlabel)
                }
/^@index defn / { arg = substr($0, 13)
	          <<handle index definition of [[arg]]>>
                }
/^@index localdefn / { arg = substr($0, 18)
	          <<handle index definition of [[arg]]>>
                }
<<handle index definition of [[arg]]>>=
allidents[arg] = 1
if (lastanchorlabel != "") {
  l = lastanchorlabel
} else {
  l = newdocslabel()
  slipin("@xref label " l)
}
addud(indexud, "defn", arg, l)
if (indexanchorlabel[arg] == "") indexanchorlabel[arg] = l
slipin("@xref ref "  l) # bug fix
@ %def allidents indexanchorlabel
The bug fix\label{multi-def-bug}
alluded to above occurs when there are multiple definitions of an identifier.
In this case, we can't just use [[indexanchorlabel[id]]], because that refers 
nly to 
the first definition.  In the {\TeX} back end, that leads to bogus
tags like \hbox{\it x \small\rm 7b 7b 7b} instead of \hbox{\it x
\small\rm 7b 9 12a}; the first tag is repeated again and again.
Because the tag for the current [[@defn]] is lost by the time pass~2
rolls around, we have to slip it in on pass~1.
@
<<insert and set [[lastanchorlabel]]>>=
{ n = anchordist
  lastanchorlabel = newdocslabel()
  for(i = nextline - 1; i >= 0; i--) {
    if (n == 0 || lines[i] ~ /^@begin docs /) {
      insertafter(i, "@xref label " lastanchorlabel)
      i = -1    # cause loop to terminate
    } else if (lines[i] == "@nl") {
      n--
    }
  }
}
<<functions>>=
function insertafter(i, s, n) {
  for(n = nextline++; n - 1 > i; n--) lines[n] = lines[n-1]
  lines[n] = s
}
@
In the awk version, [[slipin]] is called {\em before} the current line is
added to [[lines]].
<<functions>>=
function slipin(s) {
  lines[nextline++] = s
}
<<initialization>>=
thesedefns[0] = 0; theseuses[0] = 0 ;
<<second pass over [[lines[i]]]>>=
line = lines[i]
if (line ~ /^@begin /) {
    if (delay && lastchunkbegin == line) <<write trailers>>
    print line
    for (x in thesedefns) delete thesedefns[x]
    for (x in theseuses) delete theseuses[x]
    thischunk = ""
} else if (line ~ /^@defn /) {
    thischunk = substr(line, 7)
    printf "@xref ref %s\n", anchorlabel[thischunk]
    print line
} else if (line ~ /^@use /) {
    arg = substr(line, 6)
    printf "@xref ref %s\n", (anchorlabel[arg] == "" ? "nw@notdef" : anchorlab
l[arg])
    print line
} else if (line ~ /^@index defn /) {
    arg = substr(line, 13)
    thesedefns[arg] = 1
    # no xref ref because of bug fix
    # if (indexanchorlabel[arg] != "") 
    #   printf "@xref ref %s\n", indexanchorlabel[arg]
    print line
} else if (line ~ /^@index localdefn /) {
    arg = substr(line, 18)
    thesedefns[arg] = 1
    # no xref ref because of bug fix
    # if (indexanchorlabel[arg] != "") 
    #   printf "@xref ref %s\n", indexanchorlabel[arg]
    print line
} else if (line ~ /^@index use /) {
    arg = substr(line, 12)
    theseuses[arg] = 1
    if (indexanchorlabel[arg] != "") 
      printf "@xref ref %s\n", indexanchorlabel[arg]
    print line
} else if (line ~ /^@end code/) {
    <<write cross-reference>>
    print line
} else if (line ~ /^@text /) {
    # grotesque hack to get indexes in HTML
    if (thischunk == "") { # docs mode
      arg = substr(line, 7)
      if      (arg == "<nowebchunks>") lognowebchunks()
      else if (arg == "<nowebindex>")  lognowebindex()
      else print line
    } else {
      print line
    }
} else {
    print line
}
@ %def thesedefns theseuses
The case of the [[@index defn]] is the one case where we don't emit a
reference, because the reference has to go in earlier.  See
page~\pageref{multi-def-bug} for an explanation.
<<write cross-reference>>=
defout[thischunk]++
<<write index cross-reference>>
if (defout[thischunk] == 1) {<<write chunk cross-reference>>}
if (defout[thischunk] > 1)
  printf "@xref prevdef %s\n", listget(defanchors[thischunk], defout[thischunk
-1)
if (defout[thischunk] < defcount[thischunk])
  printf "@xref nextdef %s\n", listget(defanchors[thischunk], defout[thischunk
+1)
<<write index cross-reference>>=
for (x in thesedefns)
  delete theseuses[x]
delete thesedefns[0]
n = alphasort(thesedefns)
if (n > 0) {
  print "@index begindefs"
  for (j = 0; j < n; j++) {
    m = split(indexud[sorted[j]], a)
    for (k = 1; k <= m; k++) 
      if (a[k] ~ /^use/) 
        printf "@index isused %s\n", substr(a[k], 5, length(a[k])-5)
    printf "@index defitem %s\n", sorted[j]
    delete sorted[j]
  }
  print "@index enddefs"
}
<<write index cross-reference>>=
delete theseuses[0]
n = alphasort(theseuses)
if (n > 0) {
  print "@index beginuses"
  for (j = 0; j < n; j++) {
    m = split(indexud[sorted[j]], a)
    for (k = 1; k <= m; k++) 
      if (a[k] ~ /^defn/) 
        printf "@index isdefined %s\n", substr(a[k], 6, length(a[k])-6)
    printf "@index useitem %s\n", sorted[j]
    delete sorted[j]
  }
  print "@index enduses"
}
<<write chunk cross-reference>>=
if (defcount[thischunk] > 1) {
  print "@xref begindefs"
  n = split(defanchors[thischunk], a)
  for (j = 2; j <= n; j++) printf "@xref defitem %s\n", a[j]
  print "@xref enddefs"

}
if (uses[thischunk] != "") {
  print "@xref beginuses"
  n = split(uses[thischunk], a)
  for (j = 1; j <= n; j++) printf "@xref useitem %s\n", a[j]
  print "@xref enduses"
} else {
  printf "@xref notused %s\n", thischunk
}
<<functions>>=
function newdefnlabel(arg, label) {
  defcount[arg] = defcount[arg] + 1
  label = "NW" curfile "-" uniqueid(arg) "-" alphacode(defcount[arg])
  return label
}
@ %def newdefnlabel
<<initialization>>=
defcount[0] = 0 ;
<<functions>>=
function newdocslabel() {
  newdocslabelcount++
  return "NWD" alphacode(newdocslabelcount)
}
@ %def newdocslabel
<<functions>>=
function addlabel(tbl, arg, label, marker) {
  marker = " " label
  if (!tailmatch(tbl[arg], marker)) 
    tbl[arg] = tbl[arg] marker
  return label
}
@ %def addlabel
<<functions>>=
function tailmatch(string, tail, pos) {
  pos = length(string) - length(tail) + 1
  if (pos > 0 && substr(string, pos) == tail)
    return 1
  else
    return 0
}
@ %def tailmatch
<<functions>>=      
function addud(udlist, name, arg, label, s) {
  s = " " name "{" label "}"
  if (!tailmatch(udlist[arg], s))
    udlist[arg] = udlist[arg] s
}
@ %def addud
<<functions>>=
function listget(l, i, n, a) {
  n = split(l, a)
  return a[i]
}
@ %def listget
<<initialization>>=
udlist[0] = 0 ;
@
[[uniqueid]] eliminates both {\TeX} and HTML specials.
Escaping the [[/]] in the character class in the regexp pattern works
around a bug in many awks.
Unpalatable, but what can one do?
<<functions>>=      
function uniqueid(name, key) {
  if (uidtable[name] == "") {
    key = make_key(name)
    # gsub(/[\]\[ \\{}`#%&~_^<>"-]/, "*", key)  # old
    gsub(/[^a-zA-Z0-9!$()*+,.\/:;=?@|]/, "*", key)
    keycounts[key] = keycounts[key] + 1
    uidtable[name] = key 
    if (keycounts[key] > 1)
      uidtable[name] = uidtable[name] "." alphacode(keycounts[key])
  }
  return uidtable[name]
}
@ %def uniqueid
<<functions>>=
function make_key(name,   key, l) {
   l = length(name)
   sub(/^.*\//, "", name)
   key = substr(name, 1, 3)
   if (l >= 3) key = key alphacode(l)
   return key
}
<<initialization>>=
uidtable[0] = 0
keycounts[0] = 0 ;
<<write trailers>>=
{ print "@nl"
  print "@nl"
  lognowebchunks()
  lognowebindex() 
}
@
Now, a special hack, so we can write this stuff in the right place on pass 2.
<<functions>>=
function lognowebchunks(l, j, n, x) {
  if (loggednowebchunks > 0) return
  loggednowebchunks = 1
  delete allchunks[0]
  n = alphasort(allchunks)
  print "@xref beginchunks"
  for (j = 0; j < n; j++) {
    name = sorted[j]; delete sorted[j]
    printf "@xref chunkbegin %s %s\n", 
	(anchorlabel[name] != "" ? anchorlabel[name] : "nw@notdef"), name
    m = split(chunkud[name], a)
    for (k = 1; k <= m; k++) 
      if (a[k] ~ /^use/) 
        printf "@xref chunkuse %s\n", substr(a[k], 5, length(a[k])-5)
      else if (a[k] ~ /^defn/) 
        printf "@xref chunkdefn %s\n", substr(a[k], 6, length(a[k])-6)
    print "@xref chunkend"
  }
  print "@xref endchunks"
}
@ %def lognowebchunks
<<functions>>=
function lognowebindex(l, j, n, x) {
  if (loggednowebindex > 0) return
  loggednowebindex = 1
  delete allidents[0]
  n = alphasort(allidents)
  print "@index beginindex"
  for (j = 0; j < n; j++) {
    name = sorted[j]; delete sorted[j]
    printf "@index entrybegin %s %s\n", 
	(indexanchorlabel[name] != "" ? indexanchorlabel[name] : "nw@notdef"), 
ame
    m = split(indexud[name], a)
    for (k = 1; k <= m; k++) 
      if (a[k] ~ /^use/) 
        printf "@index entryuse %s\n", substr(a[k], 5, length(a[k])-5)
      else if (a[k] ~ /^defn/) 
        printf "@index entrydefn %s\n", substr(a[k], 6, length(a[k])-6)
    print "@index entryend"
  }
  print "@index endindex"
}
@ %def lognowebindex
<<functions>>=
function alphasort(a, x, n) {
  n = 0
  for (x in a) 
    n = insertitem(x, n)
  return n
}
function insertitem(x, n, i, tmp) {
  sorted[n] = x
  sortkeys[n] = sortkey(x)
  i = n
  while (i > 0 && (sortkeys[i] <  sortkeys[i-1] ||
                   sortkeys[i] == sortkeys[i-1] && sorted[i] < sorted[i-1])) {
    tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp
    tmp = sorted[i]; sorted[i] = sorted[i-1]; sorted[i-1] = tmp
    i = i - 1
  }
  return n + 1
}
@ %def alphasort insertitem
<<initialization>>=
sorted[0] = 0; sortkeys[0] = 0;
<<functions>>=
function sortkey(name, s) {
  s = name
  gsub(/[^a-zA-Z ]/, "", s)
  return s
}
@ %def sortkey
<<functions>>=
function alphacode(n) {
  if (n < 0) 
    return "-" alphacode(-n)
  else if (n >= alphacodelen) 
    return alphacode(n / alphacodelen) alphacode(n % alphacodelen)
  else
    return substr(alphacodes, n+1, 1)
}
@ %def alphacode
<<initialization>>=
alphacodes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
alphacodelen = length(alphacodes) ;
@
\section{List of chunks}
\nowebchunks

\twocolumn
\section{Index}
\nowebindex
@
\end{document}
