diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/noweb/src/lib |
Initial commit
Diffstat (limited to 'web/noweb/src/lib')
-rw-r--r-- | web/noweb/src/lib/Makefile | 38 | ||||
-rwxr-xr-x | web/noweb/src/lib/btdefn | 16 | ||||
-rwxr-xr-x | web/noweb/src/lib/emptydefn | 9 | ||||
-rwxr-xr-x | web/noweb/src/lib/h2a | 17 | ||||
-rw-r--r-- | web/noweb/src/lib/noxref.nw | 215 | ||||
-rw-r--r-- | web/noweb/src/lib/noxref.sh | 65 | ||||
-rwxr-xr-x | web/noweb/src/lib/nwmtime | 32 | ||||
-rwxr-xr-x | web/noweb/src/lib/pipedocs | 23 | ||||
-rwxr-xr-x | web/noweb/src/lib/toascii | 131 | ||||
-rw-r--r-- | web/noweb/src/lib/toascii.nw | 262 | ||||
-rwxr-xr-x | web/noweb/src/lib/unmarkup | 48 |
11 files changed, 856 insertions, 0 deletions
diff --git a/web/noweb/src/lib/Makefile b/web/noweb/src/lib/Makefile new file mode 100644 index 0000000000..47cb6f40fe --- /dev/null +++ b/web/noweb/src/lib/Makefile @@ -0,0 +1,38 @@ +# Copyright 1993 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +LIB=/dev/null +SHELL=/bin/sh +CINAME= +CIMSG= +RCSFILES=emptydefn toascii.nw unmarkup Makefile nwmtime pipedocs h2a btdefn + +all: toascii + chmod +x unmarkup emptydefn toascii nwmtime pipedocs h2a btdefn + +install: all + cp unmarkup emptydefn nwmtime btdefn $(LIB) + sed 's@|LIBDIR|@$(LIB)@g' pipedocs > $(LIB)/pipedocs + sed 's@|LIBDIR|@$(LIB)@g' toascii > $(LIB)/toascii + sed 's@|LIBDIR|@$(LIB)@g' h2a > $(LIB)/h2a + chmod +x $(LIB)/pipedocs $(LIB)/toascii $(LIB)/h2a + +uninstall: + for i in unmarkup emptydefn toascii nwmtime h2a btdefn; do rm -f $(LIB)/$$i; done + rm -f $(LIB)/pipedocs + +checkin: + ci -l $(CINAME) $(CIMSG) $(RCSFILES) + +source: toascii +touch: toascii + touch toascii +boot: + touch toascii + +toascii: toascii.nw + notangle -Rtoascii toascii.nw > toascii + +clean: ; rm -f *.log *.blg *.dvi *.toc *.aux *.tex *~ +clobber: clean + rm -f toascii diff --git a/web/noweb/src/lib/btdefn b/web/noweb/src/lib/btdefn new file mode 100755 index 0000000000..a7b2d1678d --- /dev/null +++ b/web/noweb/src/lib/btdefn @@ -0,0 +1,16 @@ +#!/bin/sh + +# turn backticks into definitions + +nawk ' +/^@begin code / { code = 1 } +/^@end code / { code = 0 } +code && /^@text .*".*`.*"/ { print; next } +code && /^@text .*`/ { + while (i = match($0, /`[a-zA-Z_][a-zA-Z0-9_]*/)) { + id = substr($0, i+1, RLENGTH-1) + sub(/`[a-zA-Z_][a-zA-Z0-9_]*/, id) + print "@index defn " id + } +} +{print}' diff --git a/web/noweb/src/lib/emptydefn b/web/noweb/src/lib/emptydefn new file mode 100755 index 0000000000..29499b147b --- /dev/null +++ b/web/noweb/src/lib/emptydefn @@ -0,0 +1,9 @@ +#!/bin/sh +# +# notangle filter that makes the definition of an empty chunk <<>>= +# stand for a continuation of the previous chunk definition. + +nawk 'BEGIN { lastdefn = "@defn " } +/^@defn $/ { print lastdefn; next } +/^@defn / { lastdefn = $0 } +{ print }' "$@" diff --git a/web/noweb/src/lib/h2a b/web/noweb/src/lib/h2a new file mode 100755 index 0000000000..9aec5dc416 --- /dev/null +++ b/web/noweb/src/lib/h2a @@ -0,0 +1,17 @@ +#!/bin/sh + +LIB=|LIBDIR| +opts="-nolist" + +case $1 in + -[0-9]*) opts="$opts -width=`expr 0 - $1`" ; shift ;; +esac + +html=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1; } +trap 'rm -f $html; exit 1' 1 2 15 # clean up files + +awk ' +/^ *$/ { if (NR == 1) { print ; next } } +{ print > "'"$html"'" }' +if [ -r $html ]; then lynx -dump $opts $html | sed '1d'; fi +rm -f $html diff --git a/web/noweb/src/lib/noxref.nw b/web/noweb/src/lib/noxref.nw new file mode 100644 index 0000000000..3eec706bc7 --- /dev/null +++ b/web/noweb/src/lib/noxref.nw @@ -0,0 +1,215 @@ +\section{Cross-referencing tool} +This filter adds labels and cross-references to chunk definitions and uses, +and it converts the line [[\nowebchunks]] into an alphabetical list of chunks. + +Following the lead of J. G. Krom, I make it possible to generate the +file [[noxref]] for the Unix environment, and the files [[noxref.bat]] +and [[noxref.awk]] for the MS-DOS environment. + +<<*>>= +#!/bin/sh +nawk '<<cross-reference awk program>>' +<<noxref.awk>>= +<<cross-reference awk program>> +<<noxref.bat>>= +@echo off +REM The NOWEB environment variable must be set to the directory +REM where NOXREF.AWK is. It must end in '/' or '\' as required +REM by the AWK interpreter in use. +awk -f %NOWEB%noxref.awk +@ +The function [[modid]] turns a chunk name into a unique key acceptable +for use as a {\LaTeX} label. +The first step maps the name into a 6-character key; the second step +appends an integer (from [[keycounts]]) used to distinguish different +chunk names that map to the same key. +[[idtable]] is used to cache the results so the computation is +performed just once per chunk name. +<<function definitions>>= +function modid(name, key) { + if (idtable[name] == "") { + key = name + gsub(/[\[\]\\{} `#%&~_^-]/, "*", key) # ditch special chars + if (length(key) > 6) key = substr(key,1,3) substr(key, length(key)-2, 3) + keycounts[key] = keycounts[key] + 1 + if (keycounts[key] > 1) + idtable[name] = key "-" keycounts[key] + else + idtable[name] = key + } + return idtable[name] +} +<<initialization>>= +idtable[0] = 0 ; keycounts[0] = 0 +@ +The complete program runs in two passes. +The first pass saves all the input and builds up list of definitions +and uses. +The second pass writes out all the lines with suitable +cross-referencing markup. +The first pass saves information in the following tables, which are +indexed by chunk name. +\begin{itemize} +\item[\tt defns] Lists labels of all defining instances. +\item[\tt firstdef] Label of the first defining instance. +\item[\tt dcounts] Counts the number of defining instances. +\item[\tt uses] Lists labels of all uses. +\item[\tt dcounts] Counts the number of uses. +\end{itemize} +<<initialization>>= +defns[0] = 0 ; firstdef[0] = 0 ; dcounts[0] = 0 ; uses[0] = 0 ; ucounts[0] = 0 +@ The first pass logs both definitions ([[which == "DEFN"]]) and uses +([[which == "USE"]]) by calling [[logname]]. +<<function definitions>>= +function logname(which, tbl, counts, name, id, label) { + counts[name] = counts[name] + 1 + id = which ":" curfile ":" modid(name) "-" counts[name] + tbl[name] = tbl[name] id " " + if (which == "DEFN" && counts[name] == 1) label = "sublabel" + else label = "label" + lines[nextline++] = "@literal \\" label "{" id "}" + if (which == "DEFN" && firstdef[name] == "") firstdef[name] = id +} +@ +The structure of the full program, showing both passes, is: +<<cross-reference awk program>>= +<<function definitions>> +BEGIN { <<initialization>> } +{ lines[nextline++] = $0 } +/^@defn / { logname("DEFN", defns, dcounts, substr($0, 7)) } +/^@use / { logname("USE", uses, ucounts, substr($0, 6)) } +/^@file / { curfile = modid(substr($0, 7) substr($0, 10, 3)) } +END { <<write out the program with cross-reference info>> } +@ +The second pass just copies lines to its output, except for +definitions, uses and the [[\nowebchunks]] control sequence. +It also puts a bunch of cross-reference information at the end of each +code chunk. +<<write out the program with cross-reference info>>= +for (i=0; i < nextline; i++) { + name = substr(lines[i], 2) + if (index(name, " ")) { + name = substr(name, 1, index(name, " ")-1) + arg = substr(lines[i], length(name)+3) + } else { + arg = "" + } + if (name == "defn") { + thischunk = arg + <<emit definition>> + } else if (name == "use") { + <<emit use>> + } else if (name == "begin") { + chunktype = substr(arg, 1, 4) + print lines[i] + } else if (name == "quote") { + quote = 1 + print lines[i] + } else if (name == "endquote") { + quote = 0 + print lines[i] + } else if (name == "end") { + if (chunktype == "code") { + <<emit cross-reference info for [[thischunk]]>> + } + print lines[i] + } else if (name == "text" && arg == "\\nowebchunks" && chunktype == "docs" && quote == 0) { + <<emit sorted list of chunks>> + } else { + print lines[i] + } + delete lines[i] +} +@ +\maybreak{1in} +<<emit definition>>= +printf "@defn %s~{\\footnotesize\\Rm\\subpageref{%s}}\n", arg, firstdef[arg] +<<emit use>>= +if (firstdef[arg] != "") + printf "@use %s~{\\footnotesize\\Rm\\subpageref{%s}}\n", arg, firstdef[arg] +else + printf "@use %s~{\\footnotesize\\Rm (never defined)}\n", arg +@ +Cross reference information is emitted only at the first definition of each +chunk, as tracked by [[firstdefnout]]. +<<initialization>>= +firstdefnout[0] = 0 +<<emit cross-reference info for [[thischunk]]>>= +if (firstdefnout[thischunk] == 0) { + firstdefnout[thischunk] = 1 + n = split(defns[thischunk], a) + if (n > 1) { + printf "@literal \\nwalsodefined{" + for (j = 2; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n" + } + if (uses[thischunk] != "") { + printf "@literal \\nwused{" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n" + } else + printf "@literal \\nwnotused{%s}\n", escapeTeXspecials(thischunk) +} +@ \subsection{Sorting} +Each chunk name is mapped to a sorting key. +Ideally such a key would contain only lower-case letters and spaces, +and it would discount \TeX{} control sequences, and more. +Such computations are so excruciating in awk that I content myself +with eliminating nonletters. +<<function definitions>>= +function sortkey(name, s) { + s = name + gsub(/[^a-zA-Z ]/, "", s) + return s +} +@ I maintain a two parallel lists, one containing sort keys and the other +containing chunk names. +<<function definitions>>= +function insertchunk(name, i, tmp) { + sortcount = sortcount + 1 + sortnames[sortcount] = name + sortkeys[sortcount] = sortkey(name) + i = sortcount + while (i > 1 && (sortkeys[i] < sortkeys[i-1] || + sortkeys[i] == sortkeys[i-1] && sortnames[i] < sortnames[i-1])) { + tmp = sortkeys [i]; sortkeys [i] = sortkeys [i-1]; sortkeys [i-1] = tmp + tmp = sortnames[i]; sortnames[i] = sortnames[i-1]; sortnames[i-1] = tmp + i = i - 1 + } +} +@ +That's enough machinery to do the job. +<<emit sorted list of chunks>>= +if (sortcount == 0) { + <<insert all chunk names into [[sortnames]]>> +} +for (j = 1; j <= sortcount; j++) { + if (firstdef[sortnames[j]] != "") + print "@use " sortnames[j] "~{\\footnotesize\\Rm\\subpageref{" firstdef[sortnames[j]] "}}" + else + print "@use " sortnames[j] "~{\\footnotesize\\Rm (never defined)}" + if (j < sortcount) + print "@literal \\\\" +} +<<insert all chunk names into [[sortnames]]>>= +delete defns[0] +for (c in defns) insertchunk(c) +delete uses[0] +for (c in uses) if (!(c in defns)) insertchunk(c) +@ +<<function definitions>>= +function escapeTeXspecials(name, l, r) { + r = name + l = "" + while (match(r, /[{}$%&#^_~\\]/) > 0) { + l = l substr(r, 1, RSTART-1) "\\" substr(r, RSTART, 1) + r = substr(r, RSTART+1) + } + return l r +} +@ +\subsection{List of chunk names} +\nowebchunks diff --git a/web/noweb/src/lib/noxref.sh b/web/noweb/src/lib/noxref.sh new file mode 100644 index 0000000000..da09c46a55 --- /dev/null +++ b/web/noweb/src/lib/noxref.sh @@ -0,0 +1,65 @@ +#!/bin/sh +nawk 'BEGIN { defns[0] = 0 ; uses[0] = 0 ; dcounts[0] = 0 ; firstdef[0] = 0; + ucounts[0] = 0 ; idtable[0] = 0 ; keycounts[0] = 0 ; firstdefnout[0] = 0; + filetable[0] = 0 } +{ lines[nextline++] = $0 } +/^@defn / { logname("DEFN", defns, dcounts, substr($0, 7)) } +/^@use / { logname("USE", uses, ucounts, substr($0, 6)) } +/^@file / { curfile = modid(substr($0, 7)) } + +function logname(which, tbl, counts, name, id) { + counts[name] = counts[name] + 1 + id = which curfile "-" modid(name) "-" counts[name] + tbl[name] = tbl[name] id " " + lines[nextline++] = "@literal \\label{" id "}" + if (which == "DEFN" && firstdef[name] == "") firstdef[name] = id +} + +function modid(name, key) { + if (idtable[name] == "") { + key = name + gsub(/[\[\]\\{} -]/, "*", key) + if (length(key) > 6) key = substr(key,1,3) substr(key, length(key)-2, 3) + keycounts[key] = keycounts[key] + 1 + idtable[name] = key "-" keycounts[key] + } + return idtable[name] +} + +END { + for (i=0; i < nextline; i++) { + name = substr(lines[i], 2) + name = substr(name, 1, index(name, " ")-1) + arg = substr(lines[i], length(name)+3) + if (name == "defn") { + thischunk = arg + printf "@defn %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + } else if (name == "use") { + if (firstdef[arg] != "") + printf "@use %s~{\\footnotesize\\rm\\pageref{%s}}\n", arg, firstdef[arg] + else + printf "@use %s~{\\footnotesize\\rm (never defined)}\n", arg + } else if (name == "end") { + if (substr(arg, 1, 4) == "code" && firstdefnout[thischunk] == 0) { + firstdefnout[thischunk] = 1 + n = split(defns[thischunk], a) + if (n > 1) { + printf "@literal \\alsodefined{" + for (j = 2; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } + if (uses[thischunk] != "") { + printf "@literal \\used{" + n = split(uses[thischunk], a) + for (j = 1; j <= n; j++) + printf "\\\\{%s}", a[j] + printf "}\n@nl\n" + } else + printf "@literal \\notused\n@nl\n" + } + print lines[i] + } else + print lines[i] + } +}' diff --git a/web/noweb/src/lib/nwmtime b/web/noweb/src/lib/nwmtime new file mode 100755 index 0000000000..0d65b8a4b6 --- /dev/null +++ b/web/noweb/src/lib/nwmtime @@ -0,0 +1,32 @@ +#!/bin/sh +# +# nwmtime -- emit defn of modification time of first file +# + +eval 'exec perl -S $0 ${1+"$@"}' + if $running_under_some_shell; + +$first = 1; +while(<>) { + print; + if ($first && /\@file\s*(\S+)/) { + $mtime_file = $1; + $first = 0; + } +} + +if (!$first) { + my $filename = $mtime_file; + my $mtime; + + # localtime if you really want local time... + $mtime = gmtime((stat $filename)[9]); + print <<EOF; +\@begin code 000 +\@defn modification time of first \@file +\@nl +\@text $mtime +\@nl +\@end code 000 +EOF +} diff --git a/web/noweb/src/lib/pipedocs b/web/noweb/src/lib/pipedocs new file mode 100755 index 0000000000..4205475a4f --- /dev/null +++ b/web/noweb/src/lib/pipedocs @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +nawk ' +BEGIN { + rcsid = "$I$" + rcsname = "$Name $" + cmd = "sed '"'"'/^@begin docs /s/[0-9][0-9]*/0/'"'"' | |LIBDIR|/unmarkup | '"$*"' | |LIBDIR|/markup | grep -v '"'"'^@file $'"'"'" +} +/^@begin docs / { piping = 1 } +{ if (piping) + print | cmd + else + print +} +/^@end docs / { + piping = 0 + close(cmd) +}' + + diff --git a/web/noweb/src/lib/toascii b/web/noweb/src/lib/toascii new file mode 100755 index 0000000000..7df1ee32fb --- /dev/null +++ b/web/noweb/src/lib/toascii @@ -0,0 +1,131 @@ +#!/bin/sh +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toascii" 1>&2 ; exit 1 ;; + esac +done +awkfile=$(mktemp) +textfile=$(mktemp) +tagsfile=$(mktemp) +export awkfile textfile tagsfile +trap 'rm -f $awkfile $textfile $tagsfile' 0 1 2 10 14 15 +nawk 'BEGIN { textfile=ENVIRON["textfile"] + tagsfile=ENVIRON["tagsfile"] } + /^@begin code/ { ++secno } + /^@xref label/ { print $3, secno >tagsfile } + /^@((begin|end) (docs|code))/ { print >textfile } + /^@(text|nl|defn|use)/ { print >textfile } + /^@xref (ref|notused)/ { print >textfile } + /^@xref (begin|end)(defs|uses)/ { print >textfile } + /^@xref (def|use)item/ { print >textfile} + /^@xref ((begin|end)chunks)|(chunk(begin|use|defn|end))/ { print >textfile } + /^@index (begin|end)(defs|uses)/ { print >textfile } + /^@index (is(us|defin)ed)|((def|use)item)/ { print >textfile } + /^@index ((begin|end)index)|(entry(begin|use|defn|end))/ { print >textfile }' +nawk 'BEGIN { + textfile = ENVIRON["textfile"] + tagsfile = ENVIRON["tagsfile"] + tfmt="detex | fmt -79" + cfmt="expand -4 | fold -75 | sed \"s/^/ /\"" + xfmt="fold -75 | sed \"s/^/ /\"" + zfmt="cat" + while (getline <tagsfile > 0) + tag[$1] = $2 + close(tagsfile) + } + /^@begin docs/ { out = tfmt } + /^@end docs/ { close(out) } + /^@begin code/ { out = cfmt; code = 1; ++secno } + /^@end code/ { endcode(); close(out); printf "\n" } + /^@text/ { printf "%s", substr($0, 7) | out } + /^@nl/ { # printf "(->%s)", formatname(out) | out ; + printf "\n" | out } + /^@xref ref/ { lastxrefref = tag[substr($0, 11)] } + /^@defn/ { name = convquote(substr($0, 7)) + printf "\n### %d ### %s%s=", + secno, chunkname(name, lastxrefref), defn[name] + defn[name] = "+" } + /^@use/ { name = convquote(substr($0, 6)) + printf "%s", chunkname(name, lastxrefref) | out } + /^@xref begindefs/ { endcode() + printf "This definition continued in" | out } + /^@xref beginuses/ { endcode() + printf "This code used in" | out } + /^@xref notused/ { endcode() + print "This code not used in this document." | out } + /^@xref (def|use)item/ { addlist(tag[$3]) } + /^@xref end(defs|uses)/ { printlist() } + $0 ~ /^@index begindefs/ && !noindex { + endcode() + print "Defines:" | out } + + $0 ~ /^@index isused/ && !noindex { + if (tag[$3] != lastxrefref) addlist(tag[$3]) } + + $0 ~ /^@index defitem/ && !noindex { + printf " %s,", $3 | out + if (nlist == 0) printf " not used in this document.\n" | out + else { printf " used in" | out; printlist() } } + $0 ~ /^@index beginuses/ && !noindex { endcode(); printf "Uses" | out } + $0 ~ /^@index isdefined/ && !noindex { lastuse = tag[$3] } + $0 ~ /^@index useitem/ && !noindex { addlist(sprintf("%s %s", $3, lastuse)) } + $0 ~ /^@index enduses/ && !noindex { printlist() } + /^@xref beginchunks/ { close(out); out = zfmt + print "List of code chunks\n" | out } + /^@xref chunkbegin/ { name = convquote(substr($0, length($3) + 19)) + printf "%s\n", chunkname(name, tag[$3]) | out } + /^@xref chunkuse/ { addlist(tag[$3]) } + /^@xref chunkdefn/ { } + /^@xref chunkend/ { if (nlist == 0) + print " Not used in this document." | out + else { printf " Used in" | out; printlist() } } + /^@xref endchunks/ { } + $0 ~ /^@index beginindex/ && !noindex { print "\nList of identifiers (defini" \ + "tion in parentheses)\n" | out } + $0 ~ /^@index entrybegin/ && !noindex { name = substr($0, length($3 + 19)) + lastdefn = tag[$3] + printf "%s: ", $4 | out } + $0 ~ /^@index entryuse/ && !noindex { addlist(tag[$3]) } + $0 ~ /^@index entrydefn/ && !noindex { } + $0 ~ /^@index entryend/ && !noindex { for (i = 1; i <= nlist; i++) + if (list[i] == lastdefn) + sub(/.*/, "(&)", list[i]) + if (nlist == 0) + print "Not used." | out + else printlist() } + $0 ~ /^@index endindex/ && !noindex { } + /^@fatal / { exit 1 } + END { + close(out) + } + function endcode() { + if (code == 1) { + code = 0 + close(out) + out = xfmt + printf "\n" | out } } + function addlist(s, i) { + for (i = 1; i <= nlist; i++) + if (s == list[i]) return + list[++nlist] = s } + + function printlist( i) { + if (nlist == 1) printf " %s.\n", list[1] | out + else if (nlist == 2) printf " %s and %s.\n", list[1], list[2] | out + else { + for (i = 1; i < nlist; i++) + printf " %s,", list[i] | out + printf " and %s.\n", list[nlist] | out } + for (i in list) delete list[i] + nlist = 0 } + function convquote(s) { gsub(/\[\[|\]\]/, "", s); return s } + function chunkname(name, number) { + if (number == 0) + return sprintf("<%s>", name) + else + return sprintf("<%s %d>", name, number) + }' noindex=$noindex $textfile +exit $? diff --git a/web/noweb/src/lib/toascii.nw b/web/noweb/src/lib/toascii.nw new file mode 100644 index 0000000000..c76a7fd2ff --- /dev/null +++ b/web/noweb/src/lib/toascii.nw @@ -0,0 +1,262 @@ +[[Toascii]] is a [[noweb]] back end for formatting text as a plain ascii file. +It was written by Phil Bewig (pbewig@netcom.com) on March 31, 1995, and +contributed to Norman Ramsey's [[noweb]] literate programming system. +@ +The main program is shown below. Option [[-delay]] is processed, for +compatibility with other back ends, but ignored; since the initial document +chunk used with [[-delay]] normally contains only [[TeX]] formatting commands +in limbo, and since those commands will be deleted before formatting, there is +no need to handle [[-delay]]. +<<toascii>>= +#!/bin/sh +delay=0 noindex=0 +for i do + case $i in + -delay) delay=1 ;; + -noindex) noindex=1 ;; + *) echo "This can't happen -- $i passed to toascii" 1>&2 ; exit 1 ;; + esac +done +<<arrange temporary files>> +<<invoke first pass using parameter>> +<<invoke second pass using parameter>> +exit $? +@ %def delay noindex +[[Toascii]] uses two temporary files, one for storing the text between passes +and one for communicating the conversion of labels to tags. The files are +named here, and disposal of the file on exit from [[toascii]] is arranged. +Also arranged here is a temporary file for storage of the awk program on an +ugly system, as discussed below. +<<arrange temporary files>>= +awkfile=$(mktemp) +textfile=$(mktemp) +tagsfile=$(mktemp) +export awkfile textfile tagsfile +trap 'rm -f $awkfile $textfile $tagsfile' 0 1 2 10 14 15 +@ %def textfile tagsfile awkfile +The actual formatting of the text, code, and index entries is done by various +unix text processing commands in pipelines. There are four formatting pipes: +tfmt, which formats text, cfmt, which formats code, xfmt, which formats index +entries within the running text, and zfmt, which formats the lists of chunks +and identifiers at the end of the text. The formatters established below are +only suggestions, and may be modified to suit local taste (and the presence of +various text processing commands on the local machine!); in particular, [[c]] +programmers may want to format code with cb or indent. The sed patterns below +insert four blank spaces at the beginning of the line. +<<initialize formatters>>= +tfmt="detex | fmt -79" +cfmt="expand -4 | fold -75 | sed \"s/^/ /\"" +xfmt="fold -75 | sed \"s/^/ /\"" +zfmt="cat" +@ %def tfmt cfmt xfmt zfmt +Forgiving systems allow the awk program to be specified as a parameter to the +awk interpreter; ugly systems require that it be placed in a temporary file. +The chunks below implement both options. +<<invoke first pass using parameter>>= +nawk '<<first pass>>' +<<invoke second pass using parameter>>= +nawk '<<second pass>>' noindex=$noindex $textfile +<<invoke first pass using file>>= +cat > $awkfile << 'EOF' +<<first pass>> +EOF +nawk -f $awkfile +<<invoke second pass using file>>= +cat > $awkfile << 'EOF' +<<second pass>> +EOF +nawk -f $awkfile noindex=$noindex $textfile +@ +The first pass is responsible for extracting [[label]]s and assigning them +section numbers, which are used for cross-referencing in the second pass. The +first pass also removes from the input file those lines which are not used by +the second pass. +<<first pass>>= +BEGIN { textfile=ENVIRON["textfile"] + tagsfile=ENVIRON["tagsfile"] } +/^@begin code/ { ++secno } +/^@xref label/ { print $3, secno >tagsfile } +/^@((begin|end) (docs|code))/ { print >textfile } +/^@(text|nl|defn|use)/ { print >textfile } +/^@xref (ref|notused)/ { print >textfile } +/^@xref (begin|end)(defs|uses)/ { print >textfile } +/^@xref (def|use)item/ { print >textfile} +/^@xref ((begin|end)chunks)|(chunk(begin|use|defn|end))/ { print >textfile } +/^@index (begin|end)(defs|uses)/ { print >textfile } +/^@index (is(us|defin)ed)|((def|use)item)/ { print >textfile } +/^@index ((begin|end)index)|(entry(begin|use|defn|end))/ { print >textfile } +@ +The second pass performs formatting. After looking up the temp file names and +formatters in the environment and reading the [[tagsfile]] created in the first +pass, the second pass processes each input command in the body of the awk +[[pattern-action]] processing loop. +<<second pass>>= +BEGIN { + textfile = ENVIRON["textfile"] + tagsfile = ENVIRON["tagsfile"] + <<initialize formatters>> + while (getline <tagsfile > 0) + tag[$1] = $2 + close(tagsfile) +} +<<process [[noweb]] commands>> +/^@fatal / { exit 1 } +END { + close(out) +} +<<functions>> +@ %def tag +The rest of the program consists of a series of awk [[pattern-action]] +statements which each process a particular type of [[noweb]] pipeline command. +They are discussed in related groups, and all collected in a single chunk. We +begin with the commands that process the text of the document and code chunks. +The basic strategy is always write text to [[out]] and open and close various +pipes as needed. Variable [[code]] is true only within code chunks, and +[[secno]] numbers the sections as they appear. Function [[endcode()]] closes +the code pipeline at the end of a code section or whenever the first indexing +command appears. +<<process [[noweb]] commands>>= +/^@begin docs/ { out = tfmt } +/^@end docs/ { close(out) } +/^@begin code/ { out = cfmt; code = 1; ++secno } +/^@end code/ { endcode(); close(out); printf "\n" } +/^@text/ { printf "%s", substr($0, 7) | out } +/^@nl/ { # printf "(->%s)", formatname(out) | out ; + printf "\n" | out } +@ %def out secno code +<<functions>>= +function endcode() { + if (code == 1) { + code = 0 + close(out) + out = xfmt + printf "\n" | out } } +@ %def endcode +Definitions and uses of code chunks are handled below. Variable [[defn[name]]] +is set to a plus sign after a definition is printed, so that continuations of +the definition are properly identified. Variable [[lastxrefref]] is the tag +associated with the most-recently-seen cross-reference label, and refers to the +section number of the original definition of the code chunk. Definition lines +are printed directly, without passing through any of the formatters defined +above. +<<process [[noweb]] commands>>= +/^@xref ref/ { lastxrefref = tag[substr($0, 11)] } +/^@defn/ { name = convquote(substr($0, 7)) + printf "\n### %d ### %s%s=", + secno, chunkname(name, lastxrefref), defn[name] + defn[name] = "+" } +/^@use/ { name = convquote(substr($0, 6)) + printf "%s", chunkname(name, lastxrefref) | out } +@ %def lastxref name defn +There are three messages related to the definition and use of code chunks which +may appear in the output: "This definition continued in ...", "This code used +in ...", and "This code not used in this document." These messages are printed +by the following code. +<<process [[noweb]] commands>>= +/^@xref begindefs/ { endcode() + printf "This definition continued in" | out } +/^@xref beginuses/ { endcode() + printf "This code used in" | out } +/^@xref notused/ { endcode() + print "This code not used in this document." | out } +/^@xref (def|use)item/ { addlist(tag[$3]) } +/^@xref end(defs|uses)/ { printlist() } +@ +Processing of the [[noweb]] commands which produce the identifier definition +message "Defines: ... used in ..." is performed by the following code. The +[[if]] in [[@index isused]] prevents index definitions from pointing to +themselves. +<<process [[noweb]] commands>>= +$0 ~ /^@index begindefs/ && !noindex { + endcode() + print "Defines:" | out } + +$0 ~ /^@index isused/ && !noindex { + if (tag[$3] != lastxrefref) addlist(tag[$3]) } + +$0 ~ /^@index defitem/ && !noindex { + printf " %s,", $3 | out + if (nlist == 0) printf " not used in this document.\n" | out + else { printf " used in" | out; printlist() } } +@ +Processing of the [[noweb]] commands which produce the identifier usage message +"Uses ..." is performed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginuses/ && !noindex { endcode(); printf "Uses" | out } +$0 ~ /^@index isdefined/ && !noindex { lastuse = tag[$3] } +$0 ~ /^@index useitem/ && !noindex { addlist(sprintf("%s %s", $3, lastuse)) } +$0 ~ /^@index enduses/ && !noindex { printlist() } +@ %def lastuse +The [[noweb]] commands which print the list of chunks at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +/^@xref beginchunks/ { close(out); out = zfmt + print "List of code chunks\n" | out } +/^@xref chunkbegin/ { name = convquote(substr($0, length($3) + 19)) + printf "%s\n", chunkname(name, tag[$3]) | out } +/^@xref chunkuse/ { addlist(tag[$3]) } +/^@xref chunkdefn/ { } +/^@xref chunkend/ { if (nlist == 0) + print " Not used in this document." | out + else { printf " Used in" | out; printlist() } } +/^@xref endchunks/ { } +@ +The [[noweb]] commands which print the list of identifiers at the end of the +document are processed by the following code. +<<process [[noweb]] commands>>= +$0 ~ /^@index beginindex/ && !noindex { print "\nList of identifiers (defini" \ + "tion in parentheses)\n" | out } +$0 ~ /^@index entrybegin/ && !noindex { name = substr($0, length($3 + 19)) + lastdefn = tag[$3] + printf "%s: ", $4 | out } +$0 ~ /^@index entryuse/ && !noindex { addlist(tag[$3]) } +$0 ~ /^@index entrydefn/ && !noindex { } +$0 ~ /^@index entryend/ && !noindex { for (i = 1; i <= nlist; i++) + if (list[i] == lastdefn) + sub(/.*/, "(&)", list[i]) + if (nlist == 0) + print "Not used." | out + else printlist() } +$0 ~ /^@index endindex/ && !noindex { } +@ +Several of the cross-reference and indexing commands use the [[addlist(s)]] and +[[printlist()]] functions to manage the printing of lists of code sections and +variable names: [[addlist(s)]] adds string [[s]] to a queued [[list]] waiting +to be printed and [[printlist()]] prints the [[list]], appropriately formatted +with commas. These two functions are described below. +<<functions>>= +function addlist(s, i) { + for (i = 1; i <= nlist; i++) + if (s == list[i]) return + list[++nlist] = s } + +function printlist( i) { + if (nlist == 1) printf " %s.\n", list[1] | out + else if (nlist == 2) printf " %s and %s.\n", list[1], list[2] | out + else { + for (i = 1; i < nlist; i++) + printf " %s,", list[i] | out + printf " and %s.\n", list[nlist] | out } + for (i in list) delete list[i] + nlist = 0 } +@ %def list nlist addlist printlist +Chunk names which appear in definitions and uses of chunks consist of text +which may contain quoted code embedded between double square brackets. Quoted +code in text chunks are handled by the [[@quote ... @endquote]] mechanism, but +quoted code in chunk names must be handled explicitly by the back end. The +function below does what is needed. +<<functions>>= +function convquote(s) { gsub(/\[\[|\]\]/, "", s); return s } +@ %def convquote + +<<functions>>= +function chunkname(name, number) { + if (number == 0) + return sprintf("<%s>", name) + else + return sprintf("<%s %d>", name, number) +} +@ %def chunkname +@ +<nowebchunks> +<nowebindex> diff --git a/web/noweb/src/lib/unmarkup b/web/noweb/src/lib/unmarkup new file mode 100755 index 0000000000..761fc14897 --- /dev/null +++ b/web/noweb/src/lib/unmarkup @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright 1991 by Norman Ramsey. All rights reserved. +# See file COPYRIGHT for more information. + +nawk ' +BEGIN { + rcsid = "$Id: unmarkup,v 1.20 2008/10/06 01:03:05 nr Exp $" + rcsname = "$Name: $" + firstdoc = 1 +} +/^@begin docs / { if (!firstdoc) printf "@ "; firstdoc = 0 } +/^@begin code / { code = 1 } +/^@end [cd]o[dc][es] / { + code = 0 + if (dangling_text) printf "\n" + dangling_text = 0 + printf "%s", deflines + if (defline != "") printf "%s\n", defline + deflines = "" ; defline = "" + } +/^@defn / { printf "<<%s>>=", substr($0,7) } +/^@text $/ {next} +/^@text / { + gsub("<<", "@<<"); + gsub(">>", "@>>"); + if (!(code || quoting)) { + gsub(/\[\[/, "@[["); + gsub(/\]\]/, "@]]"); + } + printf "%s", substr($0,7) + dangling_text = 1 +} +/^@quote$/ { printf("[["); dangling_text = 1; quoting = 1 } +/^@endquote$/ { printf("]]"); dangling_text = 1; quoting = 0 } +/^@nl$/ { printf "\n"; dangling_text = 0} + +/^@index defn / { + if (defline == "") defline = "@ %def" + defline = defline " " substr($0, 13) +} +/^@index nl$/ { + deflines = deflines defline "\n" + defline = "" +} +/^@use / { printf "<<%s>>", substr($0,6) + dangling_text = 1 + }' "$@" | sed 's/^@ $/@/' |