summaryrefslogtreecommitdiff
path: root/web/nuweb/msdos
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/nuweb/msdos
Initial commit
Diffstat (limited to 'web/nuweb/msdos')
-rw-r--r--web/nuweb/msdos/arena.c51
-rw-r--r--web/nuweb/msdos/input.c164
-rw-r--r--web/nuweb/msdos/latex.c361
-rw-r--r--web/nuweb/msdos/literate.bib119
-rw-r--r--web/nuweb/msdos/main.c144
-rw-r--r--web/nuweb/msdos/makefile45
-rw-r--r--web/nuweb/msdos/names.c422
-rw-r--r--web/nuweb/msdos/nuweb.1107
-rw-r--r--web/nuweb/msdos/nuweb.bbl58
-rw-r--r--web/nuweb/msdos/nuweb.tex3897
-rw-r--r--web/nuweb/msdos/nuweb.toc43
-rw-r--r--web/nuweb/msdos/nuweb.w2723
-rw-r--r--web/nuweb/msdos/output.c101
-rw-r--r--web/nuweb/msdos/pass1.c116
-rw-r--r--web/nuweb/msdos/readme28
-rw-r--r--web/nuweb/msdos/readme.txt14
-rw-r--r--web/nuweb/msdos/scraps.c542
17 files changed, 8935 insertions, 0 deletions
diff --git a/web/nuweb/msdos/arena.c b/web/nuweb/msdos/arena.c
new file mode 100644
index 0000000000..ce0f738a3c
--- /dev/null
+++ b/web/nuweb/msdos/arena.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct chunk {
+ struct chunk *next;
+ char *limit;
+ char *avail;
+} Chunk;
+Chunk first = { NULL, NULL, NULL };
+Chunk *arena = &first;
+void *arena_getmem(n)
+ int n;
+{
+ char *q;
+ char *p = arena->avail;
+ n = (n + 3) & ~3; /* ensuring alignment to 4 bytes */
+ q = p + n;
+ if (q <= arena->limit) {
+ arena->avail = q;
+ return p;
+ }
+ {
+ Chunk *ap = arena;
+ Chunk *np = ap->next;
+ while (np) {
+ char *v = sizeof(Chunk) + (char *) np;
+ if (v + n <= np->limit) {
+ np->avail = v + n;
+ arena = np;
+ return v;
+ }
+ ap = np;
+ np = ap->next;
+ }
+ {
+ int m = n + 10000;
+ np = (Chunk *) malloc(m);
+ np->limit = m + (char *) np;
+ np->avail = n + sizeof(Chunk) + (char *) np;
+ np->next = NULL;
+ ap->next = np;
+ arena = np;
+ return sizeof(Chunk) + (char *) np;
+ }
+ }
+}
+void arena_free()
+{
+ arena = &first;
+}
diff --git a/web/nuweb/msdos/input.c b/web/nuweb/msdos/input.c
new file mode 100644
index 0000000000..48e74b3432
--- /dev/null
+++ b/web/nuweb/msdos/input.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+static FILE *source_file; /* the current input file */
+static int source_peek;
+static int double_at;
+static int include_depth;
+struct {
+ FILE *file;
+ char *name;
+ int line;
+} stack[10];
+int source_get()
+{
+ int c = source_peek;
+ switch (c) {
+ case EOF: {
+ fclose(source_file);
+ if (include_depth) {
+ include_depth--;
+ source_file = stack[include_depth].file;
+ source_line = stack[include_depth].line;
+ source_name = stack[include_depth].name;
+ source_peek = getc(source_file);
+ c = source_get();
+ }
+ }
+ return c;
+ case '@': {
+ c = getc(source_file);
+ if (double_at) {
+ source_peek = c;
+ double_at = FALSE;
+ c = '@';
+ }
+ else
+ switch (c) {
+ case 'i': {
+ if (include_depth < 10) {
+ char name[100];
+ {
+ char *p = name;
+ do
+ c = getc(source_file);
+ while (c == ' ' || c == '\t');
+ while (isgraph(c)) {
+ *p++ = c;
+ c = getc(source_file);
+ }
+ *p = '\0';
+ if (c != '\n') {
+ fprintf(stderr, "%s: unexpected characters after file name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ }
+ stack[include_depth].name = source_name;
+ stack[include_depth].file = source_file;
+ stack[include_depth].line = source_line + 1;
+ include_depth++;
+ source_line = 1;
+ source_name = save_string(name);
+ source_file = fopen(source_name, "r");
+ if (!source_file) {
+ fprintf(stderr, "%s: can't open include file %s\n",
+ command_name, source_name);
+ exit(-1);
+ }
+ source_peek = getc(source_file);
+ c = source_get();
+ }
+ else {
+ fprintf(stderr, "%s: include nesting too deep (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ }
+ break;
+ case 'f': case 'm': case 'u':
+ case 'd': case 'o': case 'D': case 'O':
+ case '{': case '}': case '<': case '>': case '|':
+ source_peek = c;
+ c = '@';
+ break;
+ case '@': source_peek = c;
+ double_at = TRUE;
+ break;
+ default: fprintf(stderr, "%s: bad @ sequence (%s, line %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ }
+ return c;
+ case '\n': source_line++;
+ default: source_peek = getc(source_file);
+ return c;
+ }
+}
+void source_open(name)
+ char *name;
+{
+ source_file = fopen(name, "r");
+ if (!source_file) {
+ fprintf(stderr, "%s: couldn't open %s\n", command_name, name);
+ exit(-1);
+ }
+ source_name = name;
+ source_line = 1;
+ source_peek = getc(source_file);
+ double_at = FALSE;
+ include_depth = 0;
+}
diff --git a/web/nuweb/msdos/latex.c b/web/nuweb/msdos/latex.c
new file mode 100644
index 0000000000..05cdd4de65
--- /dev/null
+++ b/web/nuweb/msdos/latex.c
@@ -0,0 +1,361 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+static void copy_scrap(); /* formats the body of a scrap */
+static void print_scrap_numbers(); /* formats a list of scrap numbers */
+static void format_entry(); /* formats an index entry */
+static void format_user_entry();
+void write_tex(file_name, tex_name)
+ char *file_name;
+ char *tex_name;
+{
+ FILE *tex_file = fopen(tex_name, "w");
+ if (tex_file) {
+ source_open(file_name);
+ {
+ int scraps = 1;
+ int c = source_get();
+ while (c != EOF) {
+ if (c == '@')
+ {
+ int big_definition = FALSE;
+ c = source_get();
+ switch (c) {
+ case 'O': big_definition = TRUE;
+ case 'o': {
+ Name *name = collect_file_name();
+ {
+ fputs("\\begin{flushleft}\n", tex_file);
+ if (!big_definition)
+ fputs("\\begin{minipage}{\\linewidth}\n", tex_file);
+ }
+ fprintf(tex_file, "\\verb@\"%s\"@", name->spelling);
+ fprintf(tex_file, " {\\footnotesize %d }$\\equiv$\n", scraps++);
+ {
+ fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);
+ copy_scrap(tex_file);
+ fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);
+ }
+ {
+ if (name->defs->next) {
+ fputs("{\\footnotesize File defined by scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->defs);
+ fputs("}\n", tex_file);
+ }
+ }
+ {
+ if (!big_definition)
+ fputs("\\end{minipage}\\\\[4ex]\n", tex_file);
+ fputs("\\end{flushleft}\n", tex_file);
+ do
+ c = source_get();
+ while (isspace(c));
+ }
+ }
+ break;
+ case 'D': big_definition = TRUE;
+ case 'd': {
+ Name *name = collect_macro_name();
+ {
+ fputs("\\begin{flushleft}\n", tex_file);
+ if (!big_definition)
+ fputs("\\begin{minipage}{\\linewidth}\n", tex_file);
+ }
+ fprintf(tex_file, "$\\langle$%s", name->spelling);
+ fprintf(tex_file, " {\\footnotesize %d}$\\rangle\\equiv$\n", scraps++);
+ {
+ fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);
+ copy_scrap(tex_file);
+ fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);
+ }
+ {
+ fputs("{\\footnotesize ", tex_file);
+ if (name->defs->next) {
+ fputs("Macro defined by scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->defs);
+ fputs("\\\\[-1ex]\n", tex_file);
+ }
+ }
+ {
+ if (name->uses) {
+ if (name->uses->next) {
+ fputs("Macro referenced in scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->uses);
+ fputs("}\n", tex_file);
+ }
+ else
+ fprintf(tex_file, "Macro referenced in scrap %d.}\n", name->uses->scrap);
+ }
+ else {
+ fputs("Macro never referenced.}\n", tex_file);
+ fprintf(stderr, "%s: <%s> never referenced.\n",
+ command_name, name->spelling);
+ }
+ }
+ {
+ if (!big_definition)
+ fputs("\\end{minipage}\\\\[4ex]\n", tex_file);
+ fputs("\\end{flushleft}\n", tex_file);
+ do
+ c = source_get();
+ while (isspace(c));
+ }
+ }
+ break;
+ case 'f': {
+ if (file_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_entry(file_names, tex_file, TRUE);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+ }
+ break;
+ case 'm': {
+ if (macro_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_entry(macro_names, tex_file, FALSE);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+ }
+ break;
+ case 'u': {
+ if (user_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_user_entry(user_names, tex_file);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+ }
+ break;
+ case '@': putc(c, tex_file);
+ default: c = source_get();
+ break;
+ }
+ }
+ else {
+ putc(c, tex_file);
+ c = source_get();
+ }
+ }
+ }
+ fclose(tex_file);
+ }
+ else
+ fprintf(stderr, "%s: can't open %s\n", command_name, tex_name);
+}
+static void print_scrap_numbers(tex_file, scraps)
+ FILE *tex_file;
+ Scrap_Node *scraps;
+{
+ fprintf(tex_file, "%d", scraps->scrap);
+ scraps = scraps->next;
+ while (scraps->next) {
+ fprintf(tex_file, ", %d", scraps->scrap);
+ scraps = scraps->next;
+ }
+ fprintf(tex_file, " and~%d.", scraps->scrap);
+}
+static void copy_scrap(file)
+ FILE *file;
+{
+ int indent = 0;
+ int c = source_get();
+ fputs("\\verb@", file);
+ while (1) {
+ switch (c) {
+ case '@': {
+ c = source_get();
+ switch (c) {
+ case '@': fputs("@{\\tt @}\\verb@", file);
+ break;
+ case '|': {
+ do {
+ do
+ c = source_get();
+ while (c != '@');
+ c = source_get();
+ } while (c != '}');
+ }
+ case '}': putc('@', file);
+ return;
+ case '<': {
+ Name *name = collect_scrap_name();
+ fprintf(file, "@$\\langle$%s {\\footnotesize ", name->spelling);
+ if (name->defs)
+ fprintf(file, "%d", name->defs->scrap);
+ else {
+ putc('?', file);
+ fprintf(stderr, "%s: scrap never defined <%s>\n",
+ command_name, name->spelling);
+ }
+ fputs("}$\\rangle$\\verb@", file);
+ }
+ break;
+ default: /* ignore these since pass1 will have warned about them */
+ break;
+ }
+ }
+ break;
+ case '\n': fputs("@\\\\\n\\verb@", file);
+ indent = 0;
+ break;
+ case '\t': {
+ int delta = 8 - (indent % 8);
+ indent += delta;
+ while (delta > 0) {
+ putc(' ', file);
+ delta--;
+ }
+ }
+ break;
+ default: putc(c, file);
+ indent++;
+ break;
+ }
+ c = source_get();
+ }
+}
+static void format_entry(name, tex_file, file_flag)
+ Name *name;
+ FILE *tex_file;
+ int file_flag;
+{
+ while (name) {
+ format_entry(name->llink, tex_file, file_flag);
+ {
+ fputs("\\item \\hspace{-\\leftmargin}", tex_file);
+ if (file_flag) {
+ fprintf(tex_file, "\\verb@\"%s\"@ ", name->spelling);
+ {
+ Scrap_Node *p = name->defs;
+ fputs("{\\footnotesize Defined by scrap", tex_file);
+ if (p->next) {
+ fputs("s ", tex_file);
+ print_scrap_numbers(tex_file, p);
+ }
+ else
+ fprintf(tex_file, " %d.", p->scrap);
+ putc('}', tex_file);
+ }
+ }
+ else {
+ fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);
+ {
+ Scrap_Node *p = name->defs;
+ if (p) {
+ while (1) {
+ fprintf(tex_file, "%d", p->scrap);
+ p = p->next;
+ if (!p) break;
+ fputs(", ", tex_file);
+ }
+ }
+ else
+ putc('?', tex_file);
+ }
+ fputs("}$\\rangle$ ", tex_file);
+ {
+ Scrap_Node *p = name->uses;
+ fputs("{\\footnotesize ", tex_file);
+ if (p) {
+ fputs("Referenced in scrap", tex_file);
+ if (p->next) {
+ fputs("s ", tex_file);
+ print_scrap_numbers(tex_file, p);
+ }
+ else
+ fprintf(tex_file, " %d.", p->scrap);
+ }
+ else
+ fputs("Not referenced.", tex_file);
+ putc('}', tex_file);
+ }
+ }
+ putc('\n', tex_file);
+ }
+ name = name->rlink;
+ }
+}
+static void format_user_entry(name, tex_file)
+ Name *name;
+ FILE *tex_file;
+{
+ while (name) {
+ format_user_entry(name->llink, tex_file);
+ {
+ Scrap_Node *uses = name->uses;
+ Scrap_Node *defs = name->defs;
+ fprintf(tex_file, "\\item \\hspace{-\\leftmargin}\\verb@%s@: ",
+ name->spelling);
+ do {
+ if (uses && (!defs || uses->scrap < defs->scrap)) {
+ fprintf(tex_file, "%d", uses->scrap);
+ uses = uses->next;
+ }
+ else if (defs && (!uses || defs->scrap <= uses->scrap)) {
+ if (uses && defs->scrap == uses->scrap)
+ uses = uses->next;
+ fprintf(tex_file, "\\underline{%d}", defs->scrap);
+ defs = defs->next;
+ }
+ if (uses || defs) fputs(", ", tex_file);
+ } while (uses || defs);
+ fputs(".\n", tex_file);
+ }
+ name = name->rlink;
+ }
+}
diff --git a/web/nuweb/msdos/literate.bib b/web/nuweb/msdos/literate.bib
new file mode 100644
index 0000000000..6c60823bad
--- /dev/null
+++ b/web/nuweb/msdos/literate.bib
@@ -0,0 +1,119 @@
+
+% abbreviations known to bibtex 0.99c
+% acms ACM Computing Surveys
+% acta Acta Informatica
+% cacm Communications of the ACM
+% ibmjrd IBM Journal of Research and Development
+% ibmsj IBM Systems Journal
+% ieeese IEEE Transactions on Software Engineering
+% ieeetc IEEE Transactions on Computers
+% ieeetcad IEEE Transactions on Computer-Aided Design of ICs
+% ipl Information Processing Letters
+% jacm Journal of the ACM
+% jcss Journal of Computer and System Sciences
+% scp Science of Computer Programming
+% sicomp SIAM Journal on Computing
+% tocs ACM Transactions on Computer Systems
+% tods ACM Transactions on Database Systems
+% tog ACM Transactions on Graphics
+% toms ACM Transactions on Mathematical Software
+% toois ACM Transactions on Office Information Systems
+% toplas ACM Transactions on Programming Languages and Systems
+% tcs Theoretical Computer Science
+
+@string{spe="Software -- Practice and Experience"}
+
+
+@unpublished{noweb,
+ author="Norman Ramsey",
+ title="Literate-Programming Tools Need Not Be Complex",
+ month=aug,
+ year=1992,
+ note="Submitted to IEEE Software"
+}
+
+@article{aho:75,
+ author="Alfred V. Aho and Margaret J. Corasick",
+ title="Efficient String Matching: An Aid to Bibliographic Search",
+ pages="333--340",
+ journal=cacm,
+ year=1975,
+ month=jun,
+ volume=18,
+ number=6
+}
+
+
+@article{knuth:84,
+ author="Donald E. Knuth",
+ title="Literate Programming",
+ pages="97--111",
+ journal="The Computer Journal",
+ year="1984",
+ month=may,
+ volume=27,
+ number=2
+}
+
+@techreport{levy:90,
+ author="Silvio Levy and Donald E. Knuth",
+ title="{{\tt CWEB}} User Manual: The {{\small CWEB}} System of
+ Structured Documentation",
+ year="1990",
+ month=oct,
+ institution="Stanford University",
+ number="{\small STAN}-{\small CS}-83-977",
+ note="Available for anonymous ftp from {\tt labrea.stanford.edu} in
+ directory {\tt pub/cweb}"
+}
+
+@book{texbook,
+ author="Donald E. Knuth",
+ title="The {{\TeX}}book",
+ publisher="Addison-Wesley",
+ year=1986,
+ series="Computers \& Typesetting"
+}
+
+@book{tex:program,
+ author="Donald E. Knuth",
+ title="{{\TeX}}: The Program",
+ publisher="Addison-Wesley",
+ year=1986,
+ series="Computers \& Typesetting"
+}
+
+@book{metafont:program,
+ author="Donald E. Knuth",
+ title="{{\small\sf METAFONT:}} The Program",
+ publisher="Addison-Wesley",
+ year=1986,
+ series="Computers \& Typesetting"
+}
+
+@book{latex,
+ author="Leslie Lamport",
+ title="{{\LaTeX:}} A Document Preparation System",
+ publisher="Addison-Wesley",
+ year=1986
+}
+
+@booklet{funnelweb,
+ title="{FunnelWeb} User's Manual",
+ author="Ross N. Williams",
+ note="Available for anonymous ftp from {\tt sirius.itd.adelaide.edu.au}
+ in directory {\tt pub/funnelweb}",
+ month=may,
+ year=1992
+}
+
+@article{hanson:90,
+ title="Fast Allocation and Deallocation of Memory Based on Object Lifetimes",
+ author="David R. Hanson",
+ journal=spe,
+ year=1990,
+ month=jan,
+ volume=20,
+ number=1,
+ pages="5--12"
+}
diff --git a/web/nuweb/msdos/main.c b/web/nuweb/msdos/main.c
new file mode 100644
index 0000000000..089df94eec
--- /dev/null
+++ b/web/nuweb/msdos/main.c
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+void main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int arg = 1;
+ tex_flag = TRUE;
+ output_flag = TRUE;
+ compare_flag = TRUE;
+ command_name = argv[0];
+ while (arg < argc) {
+ char *s = argv[arg];
+ if (*s++ == '-') {
+ {
+ char c = *s++;
+ while (c) {
+ switch (c) {
+ case 'c': compare_flag = FALSE;
+ break;
+ case 'o': output_flag = FALSE;
+ break;
+ case 't': tex_flag = FALSE;
+ break;
+ case 'v': printf("nuweb version 0.6\n");
+ break;
+ default: fprintf(stderr, "%s: unexpected argument ignored. ",
+ command_name);
+ fprintf(stderr, "Usage is: %s [-cot] file...\n",
+ command_name);
+ break;
+ }
+ c = *s++;
+ }
+ }
+ arg++;
+ }
+ else break;
+ }
+ if (arg < argc)
+ do {
+ {
+ char source_name[100];
+ char tex_name[100];
+ {
+ char *p = argv[arg];
+ char *q = source_name;
+ char *trim = q;
+ char *dot = NULL;
+ char c = *p++;
+ while (c) {
+ *q++ = c;
+ if (c == '/') {
+ trim = q;
+ dot = NULL;
+ }
+ else if (c == '.')
+ dot = q - 1;
+ c = *p++;
+ }
+ *q = '\0';
+ if (dot) {
+ *dot = '\0';
+ sprintf(tex_name, "%s.tex", trim);
+ *dot = '.';
+ }
+ else {
+ sprintf(tex_name, "%s.tex", trim);
+ *q++ = '.';
+ *q++ = 'w';
+ *q = '\0';
+ }
+ }
+ {
+ pass1(source_name);
+ if (tex_flag)
+ write_tex(source_name, tex_name);
+ if (output_flag)
+ write_files(file_names);
+ arena_free();
+ }
+ }
+ arg++;
+ } while (arg < argc);
+ else {
+ fprintf(stderr, "%s: expected a file name. ", command_name);
+ fprintf(stderr, "Usage is: %s [-cot] file-name...\n", command_name);
+ exit(-1);
+ }
+ exit(0);
+}
diff --git a/web/nuweb/msdos/makefile b/web/nuweb/msdos/makefile
new file mode 100644
index 0000000000..0a99d9a892
--- /dev/null
+++ b/web/nuweb/msdos/makefile
@@ -0,0 +1,45 @@
+CC = cc
+
+CFLAGS = -O
+
+TARGET = nuweb
+
+OBJS = main.o pass1.o latex.o output.o input.o scraps.o names.o arena.o
+
+LIBS =
+
+.SUFFIXES: .tex .dvi .w
+
+.w.tex:
+ nuweb $*
+
+.tex.dvi:
+ latex $*
+
+.w.dvi:
+ $(MAKE) $*.tex
+ $(MAKE) $*.dvi
+
+all:
+ $(MAKE) $(TARGET).tex
+ $(MAKE) $(TARGET)
+
+shar: $(TARGET).tex
+ shar -o $(TARGET).shar Makefile README literate.bib nuweb.w \
+ nuweb.tex arena.c input.c latex.c main.c names.c \
+ output.c pass1.c scraps.c
+
+clean:
+ rm -f *.tex *.log *.dvi *~ *.blg
+
+veryclean:
+ rm -f *.o *.c *.h *.tex *.log *.dvi *~ *.blg
+
+view: $(TARGET).dvi
+ xdvi $(TARGET).dvi
+
+print: $(TARGET).dvi
+ lpr -d $(TARGET).dvi
+
+$(TARGET): $(OBJS)
+ $(CC) -o $(TARGET) $(OBJS)
diff --git a/web/nuweb/msdos/names.c b/web/nuweb/msdos/names.c
new file mode 100644
index 0000000000..b89e77cfe2
--- /dev/null
+++ b/web/nuweb/msdos/names.c
@@ -0,0 +1,422 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+enum { LESS, GREATER, EQUAL, PREFIX, EXTENSION };
+
+static int compare(x, y)
+ char *x;
+ char *y;
+{
+ int len, result;
+ int xl = strlen(x);
+ int yl = strlen(y);
+ int xp = x[xl - 1] == ' ';
+ int yp = y[yl - 1] == ' ';
+ if (xp) xl--;
+ if (yp) yl--;
+ len = xl < yl ? xl : yl;
+ result = strncmp(x, y, len);
+ if (result < 0) return GREATER;
+ else if (result > 0) return LESS;
+ else if (xl < yl) {
+ if (xp) return EXTENSION;
+ else return LESS;
+ }
+ else if (xl > yl) {
+ if (yp) return PREFIX;
+ else return GREATER;
+ }
+ else return EQUAL;
+}
+char *save_string(s)
+ char *s;
+{
+ char *new = (char *) arena_getmem((strlen(s) + 1) * sizeof(char));
+ strcpy(new, s);
+ return new;
+}
+Name *prefix_add(root, spelling)
+ Name **root;
+ char *spelling;
+{
+ Name *node = *root;
+ while (node) {
+ switch (compare(node->spelling, spelling)) {
+ case GREATER: root = &node->rlink;
+ break;
+ case LESS: root = &node->llink;
+ break;
+ case EQUAL: return node;
+ case EXTENSION: node->spelling = save_string(spelling);
+ return node;
+ case PREFIX: {
+ if (ambiguous_prefix(node->llink, spelling) ||
+ ambiguous_prefix(node->rlink, spelling))
+ fprintf(stderr,
+ "%s: ambiguous prefix @<%s...@> (%s, line %d)\n",
+ command_name, spelling, source_name, source_line);
+ }
+ return node;
+ }
+ node = *root;
+ }
+ {
+ node = (Name *) arena_getmem(sizeof(Name));
+ node->spelling = save_string(spelling);
+ node->mark = FALSE;
+ node->llink = NULL;
+ node->rlink = NULL;
+ node->uses = NULL;
+ node->defs = NULL;
+ node->tab_flag = TRUE;
+ node->indent_flag = TRUE;
+ node->debug_flag = FALSE;
+ *root = node;
+ return node;
+ }
+}
+static int ambiguous_prefix(node, spelling)
+ Name *node;
+ char *spelling;
+{
+ while (node) {
+ switch (compare(node->spelling, spelling)) {
+ case GREATER: node = node->rlink;
+ break;
+ case LESS: node = node->llink;
+ break;
+ case EQUAL:
+ case EXTENSION:
+ case PREFIX: return TRUE;
+ }
+ }
+ return FALSE;
+}
+static int robs_strcmp(x, y)
+ char *x;
+ char *y;
+{
+ char *xx = x;
+ char *yy = y;
+ int xc = toupper(*xx);
+ int yc = toupper(*yy);
+ while (xc == yc && xc) {
+ xx++;
+ yy++;
+ xc = toupper(*xx);
+ yc = toupper(*yy);
+ }
+ if (xc != yc) return xc - yc;
+ xc = *x;
+ yc = *y;
+ while (xc == yc && xc) {
+ x++;
+ y++;
+ xc = *x;
+ yc = *y;
+ }
+ if (isupper(xc) && islower(yc))
+ return xc * 2 - (toupper(yc) * 2 + 1);
+ if (islower(xc) && isupper(yc))
+ return toupper(xc) * 2 + 1 - yc * 2;
+ return xc - yc;
+}
+Name *name_add(root, spelling)
+ Name **root;
+ char *spelling;
+{
+ Name *node = *root;
+ while (node) {
+ int result = robs_strcmp(node->spelling, spelling);
+ if (result > 0)
+ root = &node->llink;
+ else if (result < 0)
+ root = &node->rlink;
+ else
+ return node;
+ node = *root;
+ }
+ {
+ node = (Name *) arena_getmem(sizeof(Name));
+ node->spelling = save_string(spelling);
+ node->mark = FALSE;
+ node->llink = NULL;
+ node->rlink = NULL;
+ node->uses = NULL;
+ node->defs = NULL;
+ node->tab_flag = TRUE;
+ node->indent_flag = TRUE;
+ node->debug_flag = FALSE;
+ *root = node;
+ return node;
+ }
+}
+Name *collect_file_name()
+{
+ Name *new_name;
+ char name[100];
+ char *p = name;
+ int start_line = source_line;
+ int c = source_get();
+ while (isspace(c))
+ c = source_get();
+ while (isgraph(c)) {
+ *p++ = c;
+ c = source_get();
+ }
+ if (p == name) {
+ fprintf(stderr, "%s: expected file name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+ *p = '\0';
+ new_name = name_add(&file_names, name);
+ {
+ while (1) {
+ while (isspace(c))
+ c = source_get();
+ if (c == '-') {
+ c = source_get();
+ do {
+ switch (c) {
+ case 't': new_name->tab_flag = FALSE;
+ break;
+ case 'd': new_name->debug_flag = TRUE;
+ break;
+ case 'i': new_name->indent_flag = FALSE;
+ break;
+ default : fprintf(stderr, "%s: unexpected per-file flag (%s, %d)\n",
+ command_name, source_name, source_line);
+ break;
+ }
+ c = source_get();
+ } while (!isspace(c));
+ }
+ else break;
+ }
+ }
+ if (c == '@' && source_get() == '{')
+ return new_name;
+ else {
+ fprintf(stderr, "%s: expected @{ after file name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+}
+Name *collect_macro_name()
+{
+ char name[100];
+ char *p = name;
+ int start_line = source_line;
+ int c = source_get();
+ while (isspace(c))
+ c = source_get();
+ while (c != EOF) {
+ switch (c) {
+ case '@': {
+ c = source_get();
+ switch (c) {
+ case '@': *p++ = c;
+ break;
+ case '{': {
+ if (p > name && p[-1] == ' ')
+ p--;
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ if (p == name || name[0] == ' ') {
+ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+ }
+ default: fprintf(stderr,
+ "%s: unexpected @%c in macro name (%s, %d)\n",
+ command_name, c, source_name, start_line);
+ exit(-1);
+ }
+ }
+ break;
+ case '\t':
+ case ' ': *p++ = ' ';
+ do
+ c = source_get();
+ while (c == ' ' || c == '\t');
+ break;
+ case '\n': {
+ do
+ c = source_get();
+ while (isspace(c));
+ if (c == '@' && source_get() == '{')
+ {
+ if (p > name && p[-1] == ' ')
+ p--;
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ if (p == name || name[0] == ' ') {
+ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+ }
+ else {
+ fprintf(stderr, "%s: expected @{ after macro name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+ }
+ default: *p++ = c;
+ c = source_get();
+ break;
+ }
+ }
+ fprintf(stderr, "%s: expected macro name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+}
+Name *collect_scrap_name()
+{
+ char name[100];
+ char *p = name;
+ int c = source_get();
+ while (c == ' ' || c == '\t')
+ c = source_get();
+ while (c != EOF) {
+ switch (c) {
+ case '@': {
+ c = source_get();
+ switch (c) {
+ case '@': *p++ = c;
+ c = source_get();
+ break;
+ case '>': {
+ if (p > name && p[-1] == ' ')
+ p--;
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ if (p == name || name[0] == ' ') {
+ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+ }
+ default: fprintf(stderr,
+ "%s: unexpected @%c in macro name (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+ }
+ break;
+ case '\t':
+ case ' ': *p++ = ' ';
+ do
+ c = source_get();
+ while (c == ' ' || c == '\t');
+ break;
+ default: if (isgraph(c)) {
+ *p++ = c;
+ c = source_get();
+ }
+ else {
+ fprintf(stderr,
+ "%s: unexpected character in macro name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ break;
+ }
+ }
+ fprintf(stderr, "%s: unexpected end of file (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+}
+static Scrap_Node *reverse(); /* a forward declaration */
+
+void reverse_lists(names)
+ Name *names;
+{
+ while (names) {
+ reverse_lists(names->llink);
+ names->defs = reverse(names->defs);
+ names->uses = reverse(names->uses);
+ names = names->rlink;
+ }
+}
+static Scrap_Node *reverse(a)
+ Scrap_Node *a;
+{
+ if (a) {
+ Scrap_Node *b = a->next;
+ a->next = NULL;
+ while (b) {
+ Scrap_Node *c = b->next;
+ b->next = a;
+ a = b;
+ b = c;
+ }
+ }
+ return a;
+}
diff --git a/web/nuweb/msdos/nuweb.1 b/web/nuweb/msdos/nuweb.1
new file mode 100644
index 0000000000..5fbc0df273
--- /dev/null
+++ b/web/nuweb/msdos/nuweb.1
@@ -0,0 +1,107 @@
+.TH NUWEB 1 "local 2/26/93"
+.SH NAME
+Nuweb, a literate programming tool
+.SH SYNOPSIS
+.B nuweb
+.br
+\fBnuweb\fP [\fB-t\fP|\fB-c\fP|\fB-o\fP] [file] ...
+.SH DESCRIPTION
+.I Nuweb
+is a literate programming tool like Knuth's
+.I WEB,
+only simpler.
+A
+.I nuweb
+file contains program source code interleaved with documentation.
+When
+.I nuweb
+is given a
+.I nuweb
+file, it writes the program file(s),
+and also
+produces,
+.I LaTeX
+source for typeset documentation.
+.SH COMMAND LINE OPTIONS
+.br
+\fB-t\fP Suppress generation of the \fB.tex\fP file.
+.br
+\fB-o\fP Suppress generation of the output file(s).
+.br
+\fB-c\fP Avoid testing output files for change before updating them.
+.br
+.SH FORMAT OF NUWEB FILES
+A
+.I nuweb
+file contains mostly ordinary
+.I LaTeX.
+The file is read and copied to output (.tex file) unless a
+.I nuweb
+command is encountered. All
+.I nuweb
+commands start with an ``at-sign'' (@).
+Files and macros are defined with the following commands:
+.PP
+@o \fIfile-name flags scrap\fP where scrap is smaller than one page.
+.br
+@O \fIfile-name flags scrap\fP where scrap is bigger than one page.
+.br
+@d \fImacro-name scrap\fP. Where scrap is smallar than one page.
+.br
+@D \fImacro-name scrap\fP. Where scrap is bigger than one page.
+.PP
+Scraps have specific begin and end
+markers: \fB@{\fP and \fB@}\fP.
+Any amount of whitespace
+(including carriage returns) may appear between a name and the
+begining of a scrap.
+.PP
+Several code/file scraps may have the same name;
+.I nuweb
+concatenates their definitions to produce a single scrap.
+Code scrap definitions are like macro definitions;
+.I nuweb
+extracts a program by expanding one scrap.
+The definition of that scrap contains references to other scraps, which are
+themselves expanded, and so on.
+\fINuweb\fP's output is readable; it preserves the indentation of expanded
+scraps with respect to the scraps in which they appear.
+.PP
+.SH PER FILE OPTIONS
+When defining an output file, the programmer has the option of using flags
+to control the output.
+.PP
+\fB-d\fR option,
+.I Nuweb
+will emit line number indications at scrap boundaries.
+.br
+\fB-i\fR option,
+.I Nuweb
+supresses the indentation of macros (useful for \fBFortran\fR).
+.br
+\fB-t\fP option makes \fInuweb\fP
+copy tabs untouched from input to output.
+.PP
+.SH MINOR COMMANDS
+.br
+@@ Causes a single ``at-sign'' to be copied into the output.
+.br
+@i \fBfilename\fR causes the file named to be included.
+.br
+@f Creates an index of output files.
+.br
+@m Creates an index of macros.
+.br
+@u Creates an index of user-specified identifiers.
+.PP
+To mark an identifier for inclusion in the index, it must be mentioned
+at the end of the scrap it was defined in. The line starts
+with @| and ends with the \fBend of scrap\fP mark \fB@}\fP.
+.PP
+.SH ERROR MESSAGES
+.PP
+.SH BUGS
+.PP
+.SH AUTHOR
+Preston Briggs.
+Internet address \fBpreston@cs.rice.edu\fP.
diff --git a/web/nuweb/msdos/nuweb.bbl b/web/nuweb/msdos/nuweb.bbl
new file mode 100644
index 0000000000..224d2050b3
--- /dev/null
+++ b/web/nuweb/msdos/nuweb.bbl
@@ -0,0 +1,58 @@
+\begin{thebibliography}{10}
+
+\bibitem{aho:75}
+Alfred~V. Aho and Margaret~J. Corasick.
+\newblock Efficient string matching: An aid to bibliographic search.
+\newblock {\em Communications of the ACM}, 18(6):333--340, June 1975.
+
+\bibitem{hanson:90}
+David~R. Hanson.
+\newblock Fast allocation and deallocation of memory based on object lifetimes.
+\newblock {\em Software -- Practice and Experience}, 20(1):5--12, January 1990.
+
+\bibitem{knuth:84}
+Donald~E. Knuth.
+\newblock Literate programming.
+\newblock {\em The Computer Journal}, 27(2):97--111, May 1984.
+
+\bibitem{metafont:program}
+Donald~E. Knuth.
+\newblock {\em {{\small\sf METAFONT:}} The Program}.
+\newblock Computers \& Typesetting. Addison-Wesley, 1986.
+
+\bibitem{tex:program}
+Donald~E. Knuth.
+\newblock {\em {{\TeX}}: The Program}.
+\newblock Computers \& Typesetting. Addison-Wesley, 1986.
+
+\bibitem{texbook}
+Donald~E. Knuth.
+\newblock {\em The {{\TeX}}book}.
+\newblock Computers \& Typesetting. Addison-Wesley, 1986.
+
+\bibitem{latex}
+Leslie Lamport.
+\newblock {\em {{\LaTeX:}} A Document Preparation System}.
+\newblock Addison-Wesley, 1986.
+
+\bibitem{levy:90}
+Silvio Levy and Donald~E. Knuth.
+\newblock {{\tt CWEB}} user manual: The {{\small CWEB}} system of structured
+ documentation.
+\newblock Technical Report {\small STAN}-{\small CS}-83-977, Stanford
+ University, October 1990.
+\newblock Available for anonymous ftp from {\tt labrea.stanford.edu} in
+ directory {\tt pub/cweb}.
+
+\bibitem{noweb}
+Norman Ramsey.
+\newblock Literate-programming tools need not be complex.
+\newblock Submitted to IEEE Software, August 1992.
+
+\bibitem{funnelweb}
+Ross~N. Williams.
+\newblock {FunnelWeb} user's manual, May 1992.
+\newblock Available for anonymous ftp from {\tt sirius.itd.adelaide.edu.au} in
+ directory {\tt pub/funnelweb}.
+
+\end{thebibliography}
diff --git a/web/nuweb/msdos/nuweb.tex b/web/nuweb/msdos/nuweb.tex
new file mode 100644
index 0000000000..3f3c3906d7
--- /dev/null
+++ b/web/nuweb/msdos/nuweb.tex
@@ -0,0 +1,3897 @@
+\documentstyle{report}
+
+\setlength{\oddsidemargin}{0in}
+\setlength{\evensidemargin}{0in}
+\setlength{\topmargin}{0in}
+\addtolength{\topmargin}{-\headheight}
+\addtolength{\topmargin}{-\headsep}
+\setlength{\textheight}{8.9in}
+\setlength{\textwidth}{6.5in}
+\setlength{\marginparwidth}{0.5in}
+
+\title{Nuweb \\ A Simple Literate Programming Tool}
+\author{\sl Preston Briggs}
+\date{(Version 0.6pc)}
+
+\begin{document}
+\maketitle
+\tableofcontents
+
+\chapter{Introduction}
+
+In 1984, Knuth introduced the idea of {\em literate programming\/} and
+described a pair of tools to support the practise~\cite{knuth:84}.
+His approach was to combine Pascal code with \TeX\ documentation to
+produce a new language, {\tt WEB}, that offered programmers a superior
+approach to programming. He wrote several programs in {\tt WEB},
+including {\tt weave} and {\tt tangle}, the programs used to support
+literate programming.
+The idea was that a programmer wrote one document, the web file, that
+combined documentation (written in \TeX~\cite{texbook}) with code
+(written in Pascal).
+
+Running {\tt tangle} on the web file would produce a complete
+Pascal program, ready for compilation by an ordinary Pascal compiler.
+The primary function of {\tt tangle} is to allow the programmer to
+present elements of the program in any desired order, regardless of
+the restrictions imposed by the programming language. Thus, the
+programmer is free to present his program in a top-down fashion,
+bottom-up fashion, or whatever seems best in terms of promoting
+understanding and maintenance.
+
+Running {\tt weave} on the web file would produce a \TeX\ file, ready
+to be processed by \TeX\@. The resulting document included a variety of
+automatically generated indices and cross-references that made it much
+easier to navigate the code. Additionally, all of the code sections
+were automatically pretty printed, resulting in a quite impressive
+document.
+
+Knuth also wrote the programs for \TeX\ and {\small\sf METAFONT}
+entirely in {\tt WEB}, eventually publishing them in book
+form~\cite{tex:program,metafont:program}. These are probably the
+largest programs ever published in a readable form.
+
+Inspired by Knuth's example, many people have experimented with
+{\tt WEB}\@. Some people have even built web-like tools for their own
+favorite combinations of programming language and typesetting
+language. For example, {\tt CWEB}, Knuth's current system of choice,
+works with a combination of C and \TeX~\cite{levy:90}. Another system,
+FunnelWeb, is independent of any programming language and only mildly
+dependent on \TeX~\cite{funnelweb}. Inspired by the versatility
+of FunnelWeb and by the daunting size of its documentation, I decided
+to write my own, very simple, tool for literate programming.%
+\footnote{There is another system similar to mine, written by Norman
+Ramsey, called {\em noweb}~\cite{noweb}. It perhaps suffers from being
+overly Unix-dependent and requiring several programs to use. On the
+other hand, its command syntax is very nice. In any
+case, nuweb certainly owes its name to his inspiration.}
+
+
+\section{Nuweb}
+
+Nuweb works with any programming language and \LaTeX~\cite{latex}. I
+wanted to use \LaTeX\ because it supports a multi-level sectioning
+scheme and has facilities for drawing figures. I wanted to be able to
+work with arbitrary programming languages because my friends and I
+write programs in many languages (and sometimes combinations of
+several languages), {\em e.g.,} C, Fortran, C++, yacc, lex, Scheme,
+assembly, Postscript, and so forth. The need to support arbitrary
+programming languages has many consequences:
+\begin{description}
+\item[No pretty printing] Both {\tt WEB} and {\tt CWEB} are able to
+ pretty print the code sections of their documents because they
+ understand the language well enough to parse it. Since we want to use
+ {\em any\/} language, we've got to abandon this feature.
+\item[No index of identifiers] Because {\tt WEB} knows about Pascal,
+ it is able to construct an index of all the identifiers occurring in
+ the code sections (filtering out keywords and the standard type
+ identifiers). Unfortunately, this isn't as easy in our case. We don't
+ know what an identifiers looks like in each language and we certainly
+ don't know all the keywords. (On the other hand, see the end of
+ Section~1.3)
+\end{description}
+Of course, we've got to have some compensation for our losses or the
+whole idea would be a waste. Here are the advantages I can see:
+\begin{description}
+\item[Simplicity] The majority of the commands in {\tt WEB} are
+ concerned with control of the automatic pretty printing. Since we
+ don't pretty print, many commands are eliminated. A further set of
+ commands is subsumed by \LaTeX\ and may also be eliminated. As a
+ result, our set of commands is reduced to only four members (explained
+ in the next section). This simplicity is also reflected in
+ the size of this tool, which is quite a bit smaller than the tools
+ used with other approaches.
+\item[No pretty printing] Everyone disagrees about how their code
+ should look, so automatic formatting annoys many people. One approach
+ is to provide ways to control the formatting. Our approach is simpler
+ -- we perform no automatic formatting and therefore allow the
+ programmer complete control of code layout.
+\item[Control] We also offer the programmer complete control of the
+ layout of his output files (the files generated during tangling). Of
+ course, this is essential for languages that are sensitive to layout;
+ but it is also important in many practical situations, {\em e.g.,}
+ debugging.
+\item[Speed] Since nuweb doesn't do to much, the nuweb tool runs
+ quickly. I combine the functions of {\tt tangle} and {\tt weave} into
+ a single program that performs both functions at once.
+\item[Multiple file output] The programmer may specify more than one
+ output file in a single nuweb file. This is required when constructing
+ programs in a combination of languages (say, Fortran and C)\@. It's also
+ an advantage when constructing very large programs that would require
+ a lot of compile time.
+\end{description}
+This last point is very important. By allowing the creation of
+multiple output files, we avoid the need for monolithic programs.
+Thus we support the creation of very large programs by groups of
+people.
+
+A further reduction in compilation time is achieved by first
+writing each output file to a temporary location, then comparing the
+temporary file with the old version of the file. If there is no
+difference, the temporary file can be deleted. If the files differ,
+the old version is deleted and the temporary file renamed. This
+approach works well in combination with {\tt make} (or similar tools),
+since {\tt make} will avoid recompiling untouched output files.
+
+
+\section{Writing Nuweb}
+
+The bulk of a nuweb file will be ordinary \LaTeX\@. In fact, any \LaTeX\
+file can serve as input to nuweb and will be simply copied through
+unchanged to the {\tt .tex} file -- unless a nuweb command is
+discovered. All nuweb commands begin with an ``at-sign'' ({\tt @}).
+Therefore, a file without at-signs will be copied unchanged.
+Nuweb commands are used to specify {\em output files,} define
+{\em macros,} and delimit {\em scraps}. These are the basic features
+of interest to the nuweb tool -- all else is simply text to be copied
+to the {\tt .tex} file.
+
+\subsection{The Major Commands}
+
+Files and macros are defined with the following commands:
+\begin{description}
+\item[\tt @o {\em file-name flags scrap\/}] Output a file. The file name is
+ terminated by whitespace.
+\item[\tt @d {\em macro-name scrap\/}] Define a macro. The macro name
+ is terminated by a return or the beginning of a scrap.
+\end{description}
+A specific file may be specified several times, with each definition
+being written out, one after the other, in the order they appear.
+The definitions of macros may be similarly divided.
+
+\subsubsection{Scraps}
+
+Scraps have specific begin markers and end markers to allow precise
+control over the contents and layout. Note that any amount of
+whitespace (including carriage returns) may appear between a name and
+the beginning of a scrap.
+\begin{description}
+\item[\tt @\{{\em anything\/}@\}] where the scrap body includes every
+ character in {\em anything\/} -- all the blanks, all the tabs, all the
+ carriage returns.
+\end{description}
+Inside a scrap, we may invoke a macro.
+\begin{description}
+\item[\tt @<{\em macro-name\/}@>] Causes the macro {\em macro-name\/}
+ to be expanded inline as the code is written out to a file. It is an
+ error to specify recursive macro invocations.
+\end{description}
+Note that macro names may be abbreviated, either during invocation or
+definition. For example, it would be very tedious to have to
+repeatedly type the macro name
+\begin{quote}
+{\tt @d Check for terminating at-sequence and return name if found}
+\end{quote}
+Therefore, we provide a mechanism (stolen from Knuth) of indicating
+abbreviated names.
+\begin{quote}
+{\tt @d Check for terminating...}
+\end{quote}
+Basically, the programmer need only type enough characters to uniquely
+identify the macro name, followed by three periods. An abbreviation
+may even occur before the full version; nuweb simply preserves the
+longest version of a macro name. Note also that blanks and tabs are
+insignificant in a macro name; any string of them are replaced by a
+single blank.
+
+When scraps are written to an output or {\tt .tex} file, tabs are
+expanded into spaces by default. Currently, I assume tab stops are set
+every eight characters. Furthermore, when a macro is expanded in a scrap,
+the body of the macro is indented to match the indentation of the
+macro invocation. Therefore, care must be taken with languages
+({\em e.g.,} Fortran) that are sensitive to indentation.
+These default behaviors may be changed for each output file (see
+below).
+
+\subsubsection{Flags}
+
+When defining an output file, the programmer has the option of using
+flags to control output of a particular file. The flags are intended
+to make life a little easier for programmers using certain languages.
+They introduce little language dependences; however, they do so only
+for a particular file. Thus it is still easy to mix languages within a
+single document. There are three ``per-file'' flags:
+\begin{description}
+\item[\tt -d] Forces the creation of \verb|#line| directives in the
+ output file. These are useful with C (and sometimes C++ and Fortran) on
+ many Unix systems since they cause the compiler's error messages to
+ refer to the web file rather than the output file. Similarly, they
+ allow source debugging in terms of the web file.
+\item[\tt -i] Suppresses the indentation of macros. That is, when a
+ macro is expanded in a scrap, it will {\em not\/} be indented to
+ match the indentation of the macro invocation. This flag would seem
+ most useful for Fortran programmers.
+\item[\tt -t] Suppresses expansion of tabs in the output file. This
+ feature seems important when generating {\tt make} files.
+\end{description}
+
+
+\subsection{The Minor Commands}
+
+We have two very low-level utility commands that may appear anywhere
+in the web file.
+\begin{description}
+\item[\tt @@] Causes a single ``at sign'' to be copied into the output.
+\item[\tt @i {\em file-name\/}] Includes a file. Includes may be
+ nested, though there is currently a limit of 10~levels. The file name
+ should be complete (no extension will be appended) and should be
+ terminated by a carriage return.
+\end{description}
+Finally, there are three commands used to create indices to the macro
+names, file definitions, and user-specified identifiers.
+\begin{description}
+\item[\tt @f] Create an index of file names.
+\item[\tt @m] Create an index of macro name.
+\item[\tt @u] Create an index of user-specified identifiers.
+\end{description}
+I usually put these in their own section
+in the \LaTeX\ document; for example, see Chapter~\ref{indices}.
+
+Identifiers must be explicitely specified for inclusion in the
+{\tt @u} index. By convention, each identifier is marked at the point
+of its definition; all references to each identifier (inside scraps)
+will be discovered automatically. To ``mark'' an identifier for
+inclusion in the index, we must mention it at the end of a scrap.
+For example,
+\begin{quote}
+\begin{verbatim}
+@d a scrap @{
+Let's pretend we're declaring the variables FOO and BAR
+inside this scrap.
+@| FOO BAR @}
+\end{verbatim}
+\end{quote}
+I've used alphabetic identifiers in this example, but any string of
+characters (not including whitespace) will do. Therefore, it's
+possible to add index entries for things like {\tt <<=} if desired.
+An identifier may be declared in more than one scrap.
+
+In the generated index, each identifier appears with a list of all the
+scraps using and defining it, where the defining scraps are
+distinguished by underlining. Note that the identifier doesn't
+actually have to appear in the defining scrap; it just has to be in
+the list of definitions at the end of a scrap.
+
+
+\section{Running Nuweb}
+
+Nuweb is invoked using the following command:
+\begin{quote}
+{\tt nuweb} {\em flags file-name}\ldots
+\end{quote}
+One or more files may be processed at a time. If a file name has no
+extension, {\tt .w} will be appended. While a file name may specify a
+file in another directory, the resulting {\tt .tex} file will always
+be created in the current directory. For example,
+\begin{quote}
+{\tt nuweb /foo/bar/quux}
+\end{quote}
+will take as input the file {\tt /foo/bar/quux.w} and will create the
+file {\tt quux.tex} in the current directory.
+
+By default, nuweb performs both tangling and weaving at the same time.
+Normally, this is not a bottleneck in the compilation process;
+however, it's possible to achieve slightly faster throughput by
+avoiding one or another of the default functions using command-line
+flags. There are currently three possible flags:
+\begin{description}
+\item[\tt -t] Suppress generation of the {\tt .tex} file.
+\item[\tt -o] Suppress generation of the output files.
+\item[\tt -c] Avoid testing output files for change before updating them.
+\end{description}
+Thus, the command
+\begin{quote}
+{\tt nuweb -to /foo/bar/quux}
+\end{quote}
+would simply scan the input and produce no output at all.
+
+
+\section{Restrictions and Extensions}
+
+Because nuweb is intended to be a simple tool, I've established a few
+restrictions. Over time, some of these may be eliminated; others seem
+fundamental.
+\begin{itemize}
+\item The scraps don't print well with older versions of \TeX\
+ (pre~3.0). I'm not sure why and I'm not sure it's always true
+ (I don't have a version to experiment with).
+\item The handling of errors is not completely ideal. In some cases, I
+ simply warn of a problem and continue; in other cases I halt
+ immediately. This behavior should be regularized.
+\item I warn about references to macros that haven't been defined, but
+ don't halt. This seems most convenient for development, but may change
+ in the future.
+\item File names and index entries should not contain any {\tt @}
+ signs.
+\item Macro names may be (almost) any well-formed \TeX\ string.
+ It makes sense to change fonts or use math mode; however, care should
+ be taken to ensure matching braces, brackets, and dollar signs.
+\item Anything is allowed in the body of a scrap; however, very
+ long scraps (horizontally or vertically) may not typeset well.
+\end{itemize}
+Very long scraps may be allowed to break across a page if declared
+with {\tt @O} or {\tt @D} (instead of {\tt @o} or {\tt @d}).
+This doesn't work very well as a default, since far too many short
+scraps will be broken across pages; however, as a user-controlled
+option, it seems very useful.
+
+
+\chapter{The Overall Structure}
+
+Processing a web requires three major steps:
+\begin{enumerate}
+\item Read the source, accumulating file names, macro names, scraps,
+and lists of cross-references.
+\item Reread the source, copying out to the {\tt .tex} file, with
+protection and cross-reference information for all the scraps.
+\item Traverse the list of files names. For each file name:
+\begin{enumerate}
+\item Dump all the defining scraps into a temporary file.
+\item If the file already exists and is unchanged, delete the
+temporary file; otherwise, rename the temporary file.
+\end{enumerate}
+\end{enumerate}
+
+
+\section{Files}
+
+I have divided the program into several files for quicker
+recompilation during development. Rather than use {\tt .h} files
+for shared declarations, I'll simply create a shared scrap containing
+all the global declarations. This isn't really best from the point of
+view of disk space; but it does simplify the makefile (and allow me to
+play with my new toy).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Shared declarations {\footnotesize 1}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Include files {\footnotesize 2}$\rangle$\verb@@\\
+\verb@@$\langle$Type declarations {\footnotesize 3}$\rangle$\verb@@\\
+\verb@@$\langle$Global variables {\footnotesize 13}$\rangle$\verb@@\\
+\verb@@$\langle$Function prototypes {\footnotesize 23}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 4, 5, 6, 7, 8, 9 and~10.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+We'll need at least two of the standard system include files.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Include files {\footnotesize 2}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@#include <stdio.h>@\\
+\verb@#include <string.h>@\\
+\verb@#include <ctype.h>@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 1 and~11.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+I also like to use {\tt TRUE} and {\tt FALSE} in my code.
+I'd use an {\tt enum} here, except that some systems seem to provide
+definitions of {\tt TRUE} and {\tt FALSE} be default. The following
+code seems to work on all the local systems.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Type declarations {\footnotesize 3}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@#ifndef FALSE@\\
+\verb@#define FALSE 0@\\
+\verb@#endif@\\
+\verb@#ifndef TRUE@\\
+\verb@#define TRUE (!0)@\\
+\verb@#endif@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 3, 100 and~101.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{The Main Files}
+
+The code is divided into four main files (introduced here) and five
+support files (introduced in the next section).
+The file {\tt main.c} will contain the driver for the whole program
+(see Section~\ref{main-routine}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"main.c"@ {\footnotesize 4 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 4 and~12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The first pass over the source file is contained in {\tt pass1.c}.
+It handles collection of all the file names, macros names, and scraps
+(see Section~\ref{pass-one}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"pass1.c"@ {\footnotesize 5 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 5 and~24.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The {\tt .tex} file is created during a second pass over the source
+file. The file {\tt latex.c} contains the code controlling the
+construction of the {\tt .tex} file
+(see Section~\ref{latex-file}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 6 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The code controlling the creation of the output files is in {\tt output.c}
+(see Section~\ref{output-files}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"output.c"@ {\footnotesize 7 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 7 and~61.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Support Files}
+
+The support files contain a variety of support routines used to define
+and manipulate the major data abstractions.
+The file {\tt input.c} holds all the routines used for referring to
+source files (see Section~\ref{source-files}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"input.c"@ {\footnotesize 8 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 8, 66, 67, 68 and~73.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Creation and lookup of scraps is handled by routines in {\tt scraps.c}
+(see Section~\ref{scraps}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 9 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The handling of file names and macro names is detailed in {\tt names.c}
+(see Section~\ref{names}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 10 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Shared declarations {\footnotesize 1}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Memory allocation and deallocation is handled by routines in {\tt arena.c}
+(see Section~\ref{memory-management}). I don't bother including all
+the shared declarations since they aren't required.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"arena.c"@ {\footnotesize 11 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@$\langle$Include files {\footnotesize 2}$\rangle$\verb@@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 11, 131, 132, 133 and~136.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{The Main Routine} \label{main-routine}
+
+The main routine is quite simple in structure.
+It wades through the optional command-line arguments,
+then handles any files listed on the command line.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"main.c"@ {\footnotesize 12 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void main(argc, argv)@\\
+\verb@ int argc;@\\
+\verb@ char **argv;@\\
+\verb@{@\\
+\verb@ int arg = 1;@\\
+\verb@ @$\langle$Interpret command-line arguments {\footnotesize 14}$\rangle$\verb@@\\
+\verb@ @$\langle$Process the remaining arguments (file names) {\footnotesize 19}$\rangle$\verb@@\\
+\verb@ exit(0);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 4 and~12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Command-Line Arguments}
+
+There are three possible command-line arguments:
+\begin{description}
+\item[{\tt -t}] Suppresses generation of the {\tt .tex} file.
+\item[{\tt -o}] Suppresses generation of the output files.
+\item[{\tt -c}] Forces output files to overwrite old files of the same
+name without comparing for equality first.
+\item[{\tt -v}] Prints the current version of {\tt nuweb}.
+\end{description}
+Global flags are declared for each of the arguments.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Global variables {\footnotesize 13}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@int tex_flag; /* if FALSE, don't emit the .tex file */@\\
+\verb@int output_flag; /* if FALSE, don't emit the output files */@\\
+\verb@int compare_flag; /* if FALSE, overwrite without comparison */@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 13, 15, 65 and~102.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+The flags are all initialized to {\tt TRUE} for correct default behavior.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret command-line arguments {\footnotesize 14}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@tex_flag = TRUE;@\\
+\verb@output_flag = TRUE;@\\
+\verb@compare_flag = TRUE;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 14, 16 and~17.\\[-1ex]
+Macro referenced in scrap 12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+We save the invocation name of the command in a global variable
+{\tt command\_name} for use in error messages.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Global variables {\footnotesize 15}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@char *command_name;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 13, 15, 65 and~102.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The invocation name is conventionally passed in {\tt argv[0]}.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret command-line arguments {\footnotesize 16}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@command_name = argv[0];@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 14, 16 and~17.\\[-1ex]
+Macro referenced in scrap 12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+We need to examine the remaining entries in {\tt argv}, looking for
+command-line arguments.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret command-line arguments {\footnotesize 17}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@while (arg < argc) {@\\
+\verb@ char *s = argv[arg];@\\
+\verb@ if (*s++ == '-') {@\\
+\verb@ @$\langle$Interpret the argument string {\tt s} {\footnotesize 18}$\rangle$\verb@@\\
+\verb@ arg++;@\\
+\verb@ }@\\
+\verb@ else break;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 14, 16 and~17.\\[-1ex]
+Macro referenced in scrap 12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+Several flags can be stacked behind a single minus sign; therefore,
+we've got to loop through the string, handling them all.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret the argument string {\tt s} {\footnotesize 18}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char c = *s++;@\\
+\verb@ while (c) {@\\
+\verb@ switch (c) {@\\
+\verb@ case 'c': compare_flag = FALSE;@\\
+\verb@ break;@\\
+\verb@ case 'o': output_flag = FALSE;@\\
+\verb@ break;@\\
+\verb@ case 't': tex_flag = FALSE;@\\
+\verb@ break;@\\
+\verb@ case 'v': printf("nuweb version 0.6\n");@\\
+\verb@ break;@\\
+\verb@ default: fprintf(stderr, "%s: unexpected argument ignored. ",@\\
+\verb@ command_name);@\\
+\verb@ fprintf(stderr, "Usage is: %s [-cot] file...\n",@\\
+\verb@ command_name);@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ c = *s++;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 17.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{File Names}
+
+We expect at least one file name. While a missing file name might be
+ignored without causing any problems, we take the opportunity to report
+the usage convention.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Process the remaining arguments (file names) {\footnotesize 19}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@if (arg < argc)@\\
+\verb@ do {@\\
+\verb@ @$\langle$Handle the file name in {\tt argv[arg]} {\footnotesize 20}$\rangle$\verb@@\\
+\verb@ arg++;@\\
+\verb@ } while (arg < argc);@\\
+\verb@else {@\\
+\verb@ fprintf(stderr, "%s: expected a file name. ", command_name);@\\
+\verb@ fprintf(stderr, "Usage is: %s [-cot] file-name...\n", command_name);@\\
+\verb@ exit(-1);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 12.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+The code to handle a particular file name is rather more tedious than
+the actual processing of the file. A file name may be an arbitrarily
+complicated path name, with an optional extension. If no extension is
+present, we add {\tt .w} as a default. The extended path name will be
+kept in a local variable {\tt source\_name}. The resulting {\tt .tex}
+file will be written in the current directory; its name will be kept
+in the variable {\tt tex\_name}.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle the file name in {\tt argv[arg]} {\footnotesize 20}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char source_name[100];@\\
+\verb@ char tex_name[100];@\\
+\verb@ @$\langle$Build {\tt source\_name} and {\tt tex\_name} {\footnotesize 21}$\rangle$\verb@@\\
+\verb@ @$\langle$Process a file {\footnotesize 22}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 19.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+I bump the pointer {\tt p} through all the characters in {\tt argv[arg]},
+copying all the characters into {\tt source\_name} (via the pointer
+{\tt q}).
+
+At each slash, I update {\tt trim} to point just past the
+slash in {\tt source\_name}. The effect is that {\tt trim} will point
+at the file name without any leading directory specifications.
+
+The pointer {\tt dot} is made to point at the file name extension, if
+present. If there is no extension, we add {\tt .w} to the source name.
+In any case, we create the {\tt tex\_name} from {\tt trim}, taking
+care to get the correct extension.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Build {\tt source\_name} and {\tt tex\_name} {\footnotesize 21}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char *p = argv[arg];@\\
+\verb@ char *q = source_name;@\\
+\verb@ char *trim = q;@\\
+\verb@ char *dot = NULL;@\\
+\verb@ char c = *p++;@\\
+\verb@ while (c) {@\\
+\verb@ *q++ = c;@\\
+\verb@ if (c == '/') {@\\
+\verb@ trim = q;@\\
+\verb@ dot = NULL;@\\
+\verb@ }@\\
+\verb@ else if (c == '.')@\\
+\verb@ dot = q - 1;@\\
+\verb@ c = *p++;@\\
+\verb@ }@\\
+\verb@ *q = '\0';@\\
+\verb@ if (dot) {@\\
+\verb@ *dot = '\0';@\\
+\verb@ sprintf(tex_name, "%s.tex", trim);@\\
+\verb@ *dot = '.';@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ sprintf(tex_name, "%s.tex", trim);@\\
+\verb@ *q++ = '.';@\\
+\verb@ *q++ = 'w';@\\
+\verb@ *q = '\0';@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 20.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Now that we're finally ready to process a file, it's not really too
+complex. We bundle most of the work into three routines {\tt pass1}
+(see Section~\ref{pass-one}), {\tt write\_tex} (see
+Section~\ref{latex-file}), and {\tt write\_files} (see
+Section~\ref{output-files}). After we're finished with a
+particular file, we must remember to release its storage (see
+Section~\ref{memory-management}).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Process a file {\footnotesize 22}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ pass1(source_name);@\\
+\verb@ if (tex_flag)@\\
+\verb@ write_tex(source_name, tex_name);@\\
+\verb@ if (output_flag)@\\
+\verb@ write_files(file_names);@\\
+\verb@ arena_free();@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 20.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Pass One} \label{pass-one}
+
+During the first pass, we scan the file, recording the definitions of
+each macro and file and accumulating all the scraps.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 23}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void pass1();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The routine {\tt pass1} takes a single argument, the name of the
+source file. It opens the file, then initializes the scrap structures
+(see Section~\ref{scraps}) and the roots of the file-name tree, the
+macro-name tree, and the tree of user-specified index entries (see
+Section~\ref{names}). After completing all the
+necessary preparation, we make a pass over the file, filling in all
+our data structures. Next, we seach all the scraps for references to
+the user-specified index entries. Finally, we must reverse all the
+cross-reference lists accumulated while scanning the scraps.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"pass1.c"@ {\footnotesize 24 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void pass1(file_name)@\\
+\verb@ char *file_name;@\\
+\verb@{@\\
+\verb@ source_open(file_name);@\\
+\verb@ init_scraps();@\\
+\verb@ macro_names = NULL;@\\
+\verb@ file_names = NULL;@\\
+\verb@ user_names = NULL;@\\
+\verb@ @$\langle$Scan the source file, looking for at-sequences {\footnotesize 25}$\rangle$\verb@@\\
+\verb@ if (tex_flag)@\\
+\verb@ search();@\\
+\verb@ @$\langle$Reverse cross-reference lists {\footnotesize 30}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 5 and~24.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+The only thing we look for in the first pass are the command
+sequences. All ordinary text is skipped entirely.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Scan the source file, looking for at-sequences {\footnotesize 25}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int c = source_get();@\\
+\verb@ while (c != EOF) {@\\
+\verb@ if (c == '@{\tt @}\verb@')@\\
+\verb@ @$\langle$Scan at-sequence {\footnotesize 26}$\rangle$\verb@@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 24.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Only four of the at-sequences are interesting during the first pass.
+We skip past others immediately; warning if unexpected sequences are
+discovered.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Scan at-sequence {\footnotesize 26}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case 'O':@\\
+\verb@ case 'o': @$\langle$Build output file definition {\footnotesize 27}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'D':@\\
+\verb@ case 'd': @$\langle$Build macro definition {\footnotesize 28}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '@{\tt @}\verb@':@\\
+\verb@ case 'u':@\\
+\verb@ case 'm':@\\
+\verb@ case 'f': /* ignore during this pass */@\\
+\verb@ break;@\\
+\verb@ default: fprintf(stderr,@\\
+\verb@ "%s: unexpected @{\tt @}\verb@ sequence ignored (%s, line %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 25.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Accumulating Definitions}
+
+There are three steps required to handle a definition:
+\begin{enumerate}
+\item Build an entry for the name so we can look it up later.
+\item Collect the scrap and save it in the table of scraps.
+\item Attach the scrap to the name.
+\end{enumerate}
+We go through the same steps for both file names and macro names.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Build output file definition {\footnotesize 27}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_file_name(); /* returns a pointer to the name entry */@\\
+\verb@ int scrap = collect_scrap(); /* returns an index to the scrap */@\\
+\verb@ @$\langle$Add {\tt scrap} to {\tt name}'s definition list {\footnotesize 29}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 26.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Build macro definition {\footnotesize 28}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_macro_name();@\\
+\verb@ int scrap = collect_scrap();@\\
+\verb@ @$\langle$Add {\tt scrap} to {\tt name}'s definition list {\footnotesize 29}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 26.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Since a file or macro may be defined by many scraps, we maintain them
+in a simple linked list. The list is actually built in reverse order,
+with each new definition being added to the head of the list.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Add {\tt scrap} to {\tt name}'s definition list {\footnotesize 29}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\
+\verb@ def->scrap = scrap;@\\
+\verb@ def->next = name->defs;@\\
+\verb@ name->defs = def;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 27 and~28.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Fixing the Cross References}
+
+Since the definition and reference lists for each name are accumulated
+in reverse order, we take the time at the end of {\tt pass1} to
+reverse them all so they'll be simpler to print out prettily.
+The code for {\tt reverse\_lists} appears in Section~\ref{names}.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Reverse cross-reference lists {\footnotesize 30}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ reverse_lists(file_names);@\\
+\verb@ reverse_lists(macro_names);@\\
+\verb@ reverse_lists(user_names);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 24.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Writing the Latex File} \label{latex-file}
+
+The second pass (invoked via a call to {\tt write\_tex}) copies most of
+the text from the source file straight into a {\tt .tex} file.
+Definitions are formatted slightly and cross-reference information is
+printed out.
+
+Note that all the formatting is handled in this section.
+If you don't like the format of definitions or indices or whatever,
+it'll be in this section somewhere. Similarly, if someone wanted to
+modify nuweb to work with a different typesetting system, this would
+be the place to look.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 31}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void write_tex();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+We need a few local function declarations before we get into the body
+of {\tt write\_tex}.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 32 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void copy_scrap(); /* formats the body of a scrap */@\\
+\verb@static void print_scrap_numbers(); /* formats a list of scrap numbers */@\\
+\verb@static void format_entry(); /* formats an index entry */@\\
+\verb@static void format_user_entry();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The routine {\tt write\_tex} takes two file names as parameters: the
+name of the web source file and the name of the {\tt .tex} output file.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 33 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void write_tex(file_name, tex_name)@\\
+\verb@ char *file_name;@\\
+\verb@ char *tex_name;@\\
+\verb@{@\\
+\verb@ FILE *tex_file = fopen(tex_name, "w");@\\
+\verb@ if (tex_file) {@\\
+\verb@ source_open(file_name);@\\
+\verb@ @$\langle$Copy {\tt source\_file} into {\tt tex\_file} {\footnotesize 34}$\rangle$\verb@@\\
+\verb@ fclose(tex_file);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ fprintf(stderr, "%s: can't open %s\n", command_name, tex_name);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+We make our second (and final) pass through the source web, this time
+copying characters straight into the {\tt .tex} file. However, we keep
+an eye peeled for {\tt @}~characters, which signal a command sequence.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Copy {\tt source\_file} into {\tt tex\_file} {\footnotesize 34}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int scraps = 1;@\\
+\verb@ int c = source_get();@\\
+\verb@ while (c != EOF) {@\\
+\verb@ if (c == '@{\tt @}\verb@')@\\
+\verb@ @$\langle$Interpret at-sequence {\footnotesize 35}$\rangle$\verb@@\\
+\verb@ else {@\\
+\verb@ putc(c, tex_file);@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 33.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret at-sequence {\footnotesize 35}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int big_definition = FALSE;@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case 'O': big_definition = TRUE;@\\
+\verb@ case 'o': @$\langle$Write output file definition {\footnotesize 36}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'D': big_definition = TRUE;@\\
+\verb@ case 'd': @$\langle$Write macro definition {\footnotesize 37}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'f': @$\langle$Write index of file names {\footnotesize 50}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'm': @$\langle$Write index of macro names {\footnotesize 51}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'u': @$\langle$Write index of user-specified names {\footnotesize 57}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '@{\tt @}\verb@': putc(c, tex_file);@\\
+\verb@ default: c = source_get();@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 34.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Formatting Definitions}
+
+We go through a fair amount of effort to format a file definition.
+I've derived most of the \LaTeX\ commands experimentally; it's quite
+likely that an expert could do a better job. The \LaTeX\ for
+the previous macro definition should look like this (perhaps modulo
+the scrap numbers):
+\begin{verbatim}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret at-sequence {\footnotesize 35}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case 'O': big_definition = TRUE;@\\
+\verb@ case 'o': @$\langle$Write output file definition {\footnotesize 36}$\rangle$\verb@@\\
+\end{verbatim}
+\vdots
+\begin{verbatim}
+\verb@ case '@{\tt @}\verb@': putc(c, tex_file);@\\
+\verb@ default: c = source_get();@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 34.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\end{verbatim}
+The {\em flushleft\/} environment is used to avoid \LaTeX\ warnings
+about underful lines. The {\em minipage\/} environment is used to
+avoid page breaks in the middle of scraps. The {\em verb\/} command
+allows arbitrary characters to be printed (however, note the special
+handling of the {\tt @} case in the switch statement).
+
+Macro and file definitions are formatted nearly identically.
+I've factored the common parts out into separate scraps.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write output file definition {\footnotesize 36}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_file_name();@\\
+\verb@ @$\langle$Begin the scrap environment {\footnotesize 38}$\rangle$\verb@@\\
+\verb@ fprintf(tex_file, "\\verb@{\tt @}\verb@\"%s\"@{\tt @}\verb@", name->spelling);@\\
+\verb@ fprintf(tex_file, " {\\footnotesize %d }$\\equiv$\n", scraps++);@\\
+\verb@ @$\langle$Fill in the middle of the scrap environment {\footnotesize 39}$\rangle$\verb@@\\
+\verb@ @$\langle$Write file defs {\footnotesize 41}$\rangle$\verb@@\\
+\verb@ @$\langle$Finish the scrap environment {\footnotesize 40}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 35.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+I don't format a macro name at all specially, figuring the programmer
+might want to use italics or bold face in the midst of the name.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write macro definition {\footnotesize 37}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_macro_name();@\\
+\verb@ @$\langle$Begin the scrap environment {\footnotesize 38}$\rangle$\verb@@\\
+\verb@ fprintf(tex_file, "$\\langle$%s", name->spelling);@\\
+\verb@ fprintf(tex_file, " {\\footnotesize %d}$\\rangle\\equiv$\n", scraps++);@\\
+\verb@ @$\langle$Fill in the middle of the scrap environment {\footnotesize 39}$\rangle$\verb@@\\
+\verb@ @$\langle$Write macro defs {\footnotesize 42}$\rangle$\verb@@\\
+\verb@ @$\langle$Write macro refs {\footnotesize 43}$\rangle$\verb@@\\
+\verb@ @$\langle$Finish the scrap environment {\footnotesize 40}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 35.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Begin the scrap environment {\footnotesize 38}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ fputs("\\begin{flushleft}\n", tex_file);@\\
+\verb@ if (!big_definition)@\\
+\verb@ fputs("\\begin{minipage}{\\linewidth}\n", tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 36 and~37.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+The interesting things here are the $\Diamond$ inserted at the end of
+each scrap and the various spacing commands. The diamond helps to
+clearly indicate the end of a scrap. The spacing commands were derived
+empirically; they may be adjusted to taste.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Fill in the middle of the scrap environment {\footnotesize 39}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);@\\
+\verb@ copy_scrap(tex_file);@\\
+\verb@ fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 36 and~37.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\newpage
+\noindent
+We've got one last spacing command, controlling the amount of white
+space after a scrap.
+
+Note also the whitespace eater. I use it to remove any blank lines
+that appear after a scrap in the source file. This way, text following
+a scrap will not be indented. Again, this is a matter of personal taste.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Finish the scrap environment {\footnotesize 40}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (!big_definition)@\\
+\verb@ fputs("\\end{minipage}\\\\[4ex]\n", tex_file);@\\
+\verb@ fputs("\\end{flushleft}\n", tex_file);@\\
+\verb@ do@\\
+\verb@ c = source_get();@\\
+\verb@ while (isspace(c));@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 36 and~37.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsubsection{Formatting Cross References}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write file defs {\footnotesize 41}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (name->defs->next) {@\\
+\verb@ fputs("{\\footnotesize File defined by scraps ", tex_file);@\\
+\verb@ print_scrap_numbers(tex_file, name->defs);@\\
+\verb@ fputs("}\n", tex_file);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 36.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write macro defs {\footnotesize 42}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ fputs("{\\footnotesize ", tex_file);@\\
+\verb@ if (name->defs->next) {@\\
+\verb@ fputs("Macro defined by scraps ", tex_file);@\\
+\verb@ print_scrap_numbers(tex_file, name->defs);@\\
+\verb@ fputs("\\\\[-1ex]\n", tex_file);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 37.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write macro refs {\footnotesize 43}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (name->uses) {@\\
+\verb@ if (name->uses->next) {@\\
+\verb@ fputs("Macro referenced in scraps ", tex_file);@\\
+\verb@ print_scrap_numbers(tex_file, name->uses);@\\
+\verb@ fputs("}\n", tex_file);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ fprintf(tex_file, "Macro referenced in scrap %d.}\n", name->uses->scrap);@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fputs("Macro never referenced.}\n", tex_file);@\\
+\verb@ fprintf(stderr, "%s: <%s> never referenced.\n",@\\
+\verb@ command_name, name->spelling);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 37.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 44 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void print_scrap_numbers(tex_file, scraps)@\\
+\verb@ FILE *tex_file;@\\
+\verb@ Scrap_Node *scraps;@\\
+\verb@{@\\
+\verb@ fprintf(tex_file, "%d", scraps->scrap);@\\
+\verb@ scraps = scraps->next;@\\
+\verb@ while (scraps->next) {@\\
+\verb@ fprintf(tex_file, ", %d", scraps->scrap);@\\
+\verb@ scraps = scraps->next;@\\
+\verb@ }@\\
+\verb@ fprintf(tex_file, " and~%d.", scraps->scrap);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsubsection{Formatting a Scrap}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 45 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void copy_scrap(file)@\\
+\verb@ FILE *file;@\\
+\verb@{@\\
+\verb@ int indent = 0;@\\
+\verb@ int c = source_get();@\\
+\verb@ fputs("\\verb@{\tt @}\verb@", file);@\\
+\verb@ while (1) {@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Check at-sequence for end-of-scrap {\footnotesize 47}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '\n': fputs("@{\tt @}\verb@\\\\\n\\verb@{\tt @}\verb@", file);@\\
+\verb@ indent = 0;@\\
+\verb@ break;@\\
+\verb@ case '\t': @$\langle$Expand tab into spaces {\footnotesize 46}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default: putc(c, file);@\\
+\verb@ indent++;@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Expand tab into spaces {\footnotesize 46}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int delta = 8 - (indent % 8);@\\
+\verb@ indent += delta;@\\
+\verb@ while (delta > 0) {@\\
+\verb@ putc(' ', file);@\\
+\verb@ delta--;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 45 and~97.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Check at-sequence for end-of-scrap {\footnotesize 47}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': fputs("@{\tt @}\verb@{\\tt @{\tt @}\verb@}\\verb@{\tt @}\verb@", file);@\\
+\verb@ break;@\\
+\verb@ case '|': @$\langle$Skip over index entries {\footnotesize 48}$\rangle$\verb@@\\
+\verb@ case '}': putc('@{\tt @}\verb@', file);@\\
+\verb@ return;@\\
+\verb@ case '<': @$\langle$Format macro name {\footnotesize 49}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default: /* ignore these since pass1 will have warned about them */@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 45.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+There's no need to check for errors here, since we will have already
+pointed out any during the first pass.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Skip over index entries {\footnotesize 48}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ do {@\\
+\verb@ do@\\
+\verb@ c = source_get();@\\
+\verb@ while (c != '@{\tt @}\verb@');@\\
+\verb@ c = source_get();@\\
+\verb@ } while (c != '}');@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 47.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Format macro name {\footnotesize 49}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_scrap_name();@\\
+\verb@ fprintf(file, "@{\tt @}\verb@$\\langle$%s {\\footnotesize ", name->spelling);@\\
+\verb@ if (name->defs)@\\
+\verb@ fprintf(file, "%d", name->defs->scrap);@\\
+\verb@ else {@\\
+\verb@ putc('?', file);@\\
+\verb@ fprintf(stderr, "%s: scrap never defined <%s>\n",@\\
+\verb@ command_name, name->spelling);@\\
+\verb@ }@\\
+\verb@ fputs("}$\\rangle$\\verb@{\tt @}\verb@", file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 47.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Generating the Indices}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write index of file names {\footnotesize 50}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (file_names) {@\\
+\verb@ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);@\\
+\verb@ format_entry(file_names, tex_file, TRUE);@\\
+\verb@ fputs("\\end{list}", tex_file);@\\
+\verb@ }@\\
+\verb@ c = source_get();@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 35.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write index of macro names {\footnotesize 51}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (macro_names) {@\\
+\verb@ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);@\\
+\verb@ format_entry(macro_names, tex_file, FALSE);@\\
+\verb@ fputs("\\end{list}", tex_file);@\\
+\verb@ }@\\
+\verb@ c = source_get();@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 35.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 52 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void format_entry(name, tex_file, file_flag)@\\
+\verb@ Name *name;@\\
+\verb@ FILE *tex_file;@\\
+\verb@ int file_flag;@\\
+\verb@{@\\
+\verb@ while (name) {@\\
+\verb@ format_entry(name->llink, tex_file, file_flag);@\\
+\verb@ @$\langle$Format an index entry {\footnotesize 53}$\rangle$\verb@@\\
+\verb@ name = name->rlink;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Format an index entry {\footnotesize 53}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ fputs("\\item \\hspace{-\\leftmargin}", tex_file);@\\
+\verb@ if (file_flag) {@\\
+\verb@ fprintf(tex_file, "\\verb@{\tt @}\verb@\"%s\"@{\tt @}\verb@ ", name->spelling);@\\
+\verb@ @$\langle$Write file's defining scrap numbers {\footnotesize 54}$\rangle$\verb@@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);@\\
+\verb@ @$\langle$Write defining scrap numbers {\footnotesize 55}$\rangle$\verb@@\\
+\verb@ fputs("}$\\rangle$ ", tex_file);@\\
+\verb@ @$\langle$Write referencing scrap numbers {\footnotesize 56}$\rangle$\verb@@\\
+\verb@ }@\\
+\verb@ putc('\n', tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 52.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write file's defining scrap numbers {\footnotesize 54}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Scrap_Node *p = name->defs;@\\
+\verb@ fputs("{\\footnotesize Defined by scrap", tex_file);@\\
+\verb@ if (p->next) {@\\
+\verb@ fputs("s ", tex_file);@\\
+\verb@ print_scrap_numbers(tex_file, p);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ fprintf(tex_file, " %d.", p->scrap);@\\
+\verb@ putc('}', tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 53.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write defining scrap numbers {\footnotesize 55}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Scrap_Node *p = name->defs;@\\
+\verb@ if (p) {@\\
+\verb@ while (1) {@\\
+\verb@ fprintf(tex_file, "%d", p->scrap);@\\
+\verb@ p = p->next;@\\
+\verb@ if (!p) break;@\\
+\verb@ fputs(", ", tex_file);@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ putc('?', tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 53.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write referencing scrap numbers {\footnotesize 56}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Scrap_Node *p = name->uses;@\\
+\verb@ fputs("{\\footnotesize ", tex_file);@\\
+\verb@ if (p) {@\\
+\verb@ fputs("Referenced in scrap", tex_file);@\\
+\verb@ if (p->next) {@\\
+\verb@ fputs("s ", tex_file);@\\
+\verb@ print_scrap_numbers(tex_file, p);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ fprintf(tex_file, " %d.", p->scrap);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ fputs("Not referenced.", tex_file);@\\
+\verb@ putc('}', tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 53.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write index of user-specified names {\footnotesize 57}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (user_names) {@\\
+\verb@ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);@\\
+\verb@ format_user_entry(user_names, tex_file);@\\
+\verb@ fputs("\\end{list}", tex_file);@\\
+\verb@ }@\\
+\verb@ c = source_get();@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 35.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"latex.c"@ {\footnotesize 58 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void format_user_entry(name, tex_file)@\\
+\verb@ Name *name;@\\
+\verb@ FILE *tex_file;@\\
+\verb@{@\\
+\verb@ while (name) {@\\
+\verb@ format_user_entry(name->llink, tex_file);@\\
+\verb@ @$\langle$Format a user index entry {\footnotesize 59}$\rangle$\verb@@\\
+\verb@ name = name->rlink;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Format a user index entry {\footnotesize 59}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Scrap_Node *uses = name->uses;@\\
+\verb@ Scrap_Node *defs = name->defs;@\\
+\verb@ fprintf(tex_file, "\\item \\hspace{-\\leftmargin}\\verb@{\tt @}\verb@%s@{\tt @}\verb@: ",@\\
+\verb@ name->spelling);@\\
+\verb@ do {@\\
+\verb@ if (uses && (!defs || uses->scrap < defs->scrap)) {@\\
+\verb@ fprintf(tex_file, "%d", uses->scrap);@\\
+\verb@ uses = uses->next;@\\
+\verb@ }@\\
+\verb@ else if (defs && (!uses || defs->scrap <= uses->scrap)) {@\\
+\verb@ if (uses && defs->scrap == uses->scrap)@\\
+\verb@ uses = uses->next;@\\
+\verb@ fprintf(tex_file, "\\underline{%d}", defs->scrap);@\\
+\verb@ defs = defs->next;@\\
+\verb@ }@\\
+\verb@ if (uses || defs) fputs(", ", tex_file);@\\
+\verb@ } while (uses || defs);@\\
+\verb@ fputs(".\n", tex_file);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 58.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Writing the Output Files} \label{output-files}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 60}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void write_files();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"output.c"@ {\footnotesize 61 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void write_files(files)@\\
+\verb@ Name *files;@\\
+\verb@{@\\
+\verb@ while (files) {@\\
+\verb@ write_files(files->llink);@\\
+\verb@ @$\langle$Write out {\tt files->spelling} {\footnotesize 62}$\rangle$\verb@@\\
+\verb@ files = files->rlink;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 7 and~61.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Due to the filename restrictions on the PC, just use {\tt nuweb~~~.tmp} as
+the temporary filename. Since files are processed serially, this should
+cause any harm to unix platforms.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Write out {\tt files->spelling} {\footnotesize 62}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ FILE *temp_file;@\\
+\verb@ char temp_name[12]="nuweb~~~.tmp";@\\
+\verb@/* sprintf(temp_name, "%s~", files->spelling); */@\\
+\verb@ temp_file = fopen(temp_name, "w");@\\
+\verb@ if (temp_file) {@\\
+\verb@ char indent_chars[500];@\\
+\verb@ write_scraps(temp_file, files->defs, 0, indent_chars,@\\
+\verb@ files->debug_flag, files->tab_flag, files->indent_flag);@\\
+\verb@ fclose(temp_file);@\\
+\verb@ if (compare_flag)@\\
+\verb@ @$\langle$Compare the temp file and the old file {\footnotesize 63}$\rangle$\verb@@\\
+\verb@ else@\\
+\verb@ rename(temp_name, files->spelling);@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr, "%s: can't open %s\n", command_name, temp_name);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 61.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Compare the temp file and the old file {\footnotesize 63}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ FILE *old_file = fopen(files->spelling, "r");@\\
+\verb@ if (old_file) {@\\
+\verb@ int x, y;@\\
+\verb@ temp_file = fopen(temp_name, "r");@\\
+\verb@ do {@\\
+\verb@ x = getc(old_file);@\\
+\verb@ y = getc(temp_file);@\\
+\verb@ } while (x == y && x != EOF);@\\
+\verb@ fclose(old_file);@\\
+\verb@ fclose(temp_file);@\\
+\verb@ if (x == y)@\\
+\verb@ unlink(temp_name);@\\
+\verb@ else@\\
+\verb@ rename(temp_name, files->spelling);@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ rename(temp_name, files->spelling);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 62.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\chapter{The Support Routines}
+
+\section{Source Files} \label{source-files}
+
+\subsection{Global Declarations}
+
+We need two routines to handle reading the source files.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 64}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void source_open(); /* pass in the name of the source file */@\\
+\verb@int source_get(); /* no args; returns the next char or EOF */@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+There are also two global variables maintained for use in error
+messages and such.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Global variables {\footnotesize 65}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@char *source_name; /* name of the current file */@\\
+\verb@int source_line; /* current line in the source file */@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 13, 15, 65 and~102.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Local Declarations}
+
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"input.c"@ {\footnotesize 66 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static FILE *source_file; /* the current input file */@\\
+\verb@static int source_peek;@\\
+\verb@static int double_at;@\\
+\verb@static int include_depth;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 8, 66, 67, 68 and~73.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"input.c"@ {\footnotesize 67 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@struct {@\\
+\verb@ FILE *file;@\\
+\verb@ char *name;@\\
+\verb@ int line;@\\
+\verb@} stack[10];@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 8, 66, 67, 68 and~73.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Reading a File}
+
+The routine {\tt source\_get} returns the next character from the
+current source file. It notices newlines and keeps the line counter
+{\tt source\_line} up to date. It also catches {\tt EOF} and watches
+for {\tt @} characters. All other characters are immediately returned.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"input.c"@ {\footnotesize 68 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@int source_get()@\\
+\verb@{@\\
+\verb@ int c = source_peek;@\\
+\verb@ switch (c) {@\\
+\verb@ case EOF: @$\langle$Handle {\tt EOF} {\footnotesize 72}$\rangle$\verb@@\\
+\verb@ return c;@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Handle an ``at'' character {\footnotesize 69}$\rangle$\verb@@\\
+\verb@ return c;@\\
+\verb@ case '\n': source_line++;@\\
+\verb@ default: source_peek = getc(source_file);@\\
+\verb@ return c;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 8, 66, 67, 68 and~73.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+This whole {\tt @}~character handling mess is pretty annoying.
+I want to recognize {\tt @i} so I can handle include files correctly.
+At the same time, it makes sense to recognize illegal {\tt @}~sequences
+and complain; this avoids ever having to check anywhere else.
+Unfortunately, I need to avoid tripping over the {\tt @@}~sequence;
+hence this whole unsatisfactory {\tt double\_at} business.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle an ``at'' character {\footnotesize 69}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = getc(source_file);@\\
+\verb@ if (double_at) {@\\
+\verb@ source_peek = c;@\\
+\verb@ double_at = FALSE;@\\
+\verb@ c = '@{\tt @}\verb@';@\\
+\verb@ }@\\
+\verb@ else@\\
+\verb@ switch (c) {@\\
+\verb@ case 'i': @$\langle$Open an include file {\footnotesize 70}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case 'f': case 'm': case 'u':@\\
+\verb@ case 'd': case 'o': case 'D': case 'O':@\\
+\verb@ case '{': case '}': case '<': case '>': case '|':@\\
+\verb@ source_peek = c;@\\
+\verb@ c = '@{\tt @}\verb@';@\\
+\verb@ break;@\\
+\verb@ case '@{\tt @}\verb@': source_peek = c;@\\
+\verb@ double_at = TRUE;@\\
+\verb@ break;@\\
+\verb@ default: fprintf(stderr, "%s: bad @{\tt @}\verb@ sequence (%s, line %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 68.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Open an include file {\footnotesize 70}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (include_depth < 10) {@\\
+\verb@ char name[100];@\\
+\verb@ @$\langle$Collect include-file name {\footnotesize 71}$\rangle$\verb@@\\
+\verb@ stack[include_depth].name = source_name;@\\
+\verb@ stack[include_depth].file = source_file;@\\
+\verb@ stack[include_depth].line = source_line + 1;@\\
+\verb@ include_depth++;@\\
+\verb@ source_line = 1;@\\
+\verb@ source_name = save_string(name);@\\
+\verb@ source_file = fopen(source_name, "r");@\\
+\verb@ if (!source_file) {@\\
+\verb@ fprintf(stderr, "%s: can't open include file %s\n",@\\
+\verb@ command_name, source_name);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ source_peek = getc(source_file);@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr, "%s: include nesting too deep (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 69.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Collect include-file name {\footnotesize 71}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char *p = name;@\\
+\verb@ do @\\
+\verb@ c = getc(source_file);@\\
+\verb@ while (c == ' ' || c == '\t');@\\
+\verb@ while (isgraph(c)) {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = getc(source_file);@\\
+\verb@ }@\\
+\verb@ *p = '\0';@\\
+\verb@ if (c != '\n') {@\\
+\verb@ fprintf(stderr, "%s: unexpected characters after file name (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 70.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+If an {\tt EOF} is discovered, the current file must be closed and
+input from the next stacked file must be resumed. If no more files are
+on the stack, the {\tt EOF} is returned.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle {\tt EOF} {\footnotesize 72}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ fclose(source_file);@\\
+\verb@ if (include_depth) {@\\
+\verb@ include_depth--;@\\
+\verb@ source_file = stack[include_depth].file;@\\
+\verb@ source_line = stack[include_depth].line;@\\
+\verb@ source_name = stack[include_depth].name;@\\
+\verb@ source_peek = getc(source_file);@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 68.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Opening a File}
+
+The routine {\tt source\_open} takes a file name and tries to open the
+file. If unsuccessful, it complains and halts. Otherwise, it sets
+{\tt source\_name}, {\tt source\_line}, and {\tt double\_at}.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"input.c"@ {\footnotesize 73 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void source_open(name)@\\
+\verb@ char *name;@\\
+\verb@{@\\
+\verb@ source_file = fopen(name, "r");@\\
+\verb@ if (!source_file) {@\\
+\verb@ fprintf(stderr, "%s: couldn't open %s\n", command_name, name);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ source_name = name;@\\
+\verb@ source_line = 1;@\\
+\verb@ source_peek = getc(source_file);@\\
+\verb@ double_at = FALSE;@\\
+\verb@ include_depth = 0;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 8, 66, 67, 68 and~73.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Scraps} \label{scraps}
+
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 74 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@#define SLAB_SIZE 500@\\
+\verb@@\\
+\verb@typedef struct slab {@\\
+\verb@ struct slab *next;@\\
+\verb@ char chars[SLAB_SIZE];@\\
+\verb@} Slab;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 75 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@typedef struct {@\\
+\verb@ char *file_name;@\\
+\verb@ int file_line;@\\
+\verb@ Slab *slab;@\\
+\verb@} ScrapEntry;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 76 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@ScrapEntry *SCRAP[256];@\\
+\verb@@\\
+\verb@#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255]@\\
+\verb@@\\
+\verb@int scraps;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 77}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void init_scraps();@\\
+\verb@int collect_scrap();@\\
+\verb@int write_scraps();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 78 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void init_scraps()@\\
+\verb@{@\\
+\verb@ scraps = 1;@\\
+\verb@ SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 79 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@typedef struct {@\\
+\verb@ Slab *scrap;@\\
+\verb@ int index;@\\
+\verb@} Manager;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 80 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void push(c, manager)@\\
+\verb@ char c;@\\
+\verb@ Manager *manager;@\\
+\verb@{@\\
+\verb@ Slab *scrap = manager->scrap;@\\
+\verb@ int index = manager->index;@\\
+\verb@ scrap->chars[index++] = c;@\\
+\verb@ if (index == SLAB_SIZE) {@\\
+\verb@ Slab *new = (Slab *) arena_getmem(sizeof(Slab));@\\
+\verb@ scrap->next = new;@\\
+\verb@ manager->scrap = new;@\\
+\verb@ index = 0;@\\
+\verb@ }@\\
+\verb@ manager->index = index;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 81 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void pushs(s, manager)@\\
+\verb@ char *s;@\\
+\verb@ Manager *manager;@\\
+\verb@{@\\
+\verb@ while (*s)@\\
+\verb@ push(*s++, manager);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 82 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@int collect_scrap()@\\
+\verb@{@\\
+\verb@ Manager writer;@\\
+\verb@ @$\langle$Create new scrap, managed by {\tt writer} {\footnotesize 83}$\rangle$\verb@@\\
+\verb@ @$\langle$Accumulate scrap and return {\tt scraps++} {\footnotesize 84}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Create new scrap, managed by {\tt writer} {\footnotesize 83}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Slab *scrap = (Slab *) arena_getmem(sizeof(Slab));@\\
+\verb@ if ((scraps & 255) == 0)@\\
+\verb@ SCRAP[scraps >> 8] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));@\\
+\verb@ scrap_array(scraps).slab = scrap;@\\
+\verb@ scrap_array(scraps).file_name = save_string(source_name);@\\
+\verb@ scrap_array(scraps).file_line = source_line;@\\
+\verb@ writer.scrap = scrap;@\\
+\verb@ writer.index = 0;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 82.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Accumulate scrap and return {\tt scraps++} {\footnotesize 84}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int c = source_get();@\\
+\verb@ while (1) {@\\
+\verb@ switch (c) {@\\
+\verb@ case EOF: fprintf(stderr, "%s: unexpect EOF in scrap (%s, %d)\n",@\\
+\verb@ command_name, scrap_array(scraps).file_name,@\\
+\verb@ scrap_array(scraps).file_line);@\\
+\verb@ exit(-1);@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Handle at-sign during scrap accumulation {\footnotesize 85}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default: push(c, &writer);@\\
+\verb@ c = source_get();@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 82.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle at-sign during scrap accumulation {\footnotesize 85}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': pushs("@{\tt @}\verb@@{\tt @}\verb@", &writer);@\\
+\verb@ c = source_get();@\\
+\verb@ break;@\\
+\verb@ case '|': @$\langle$Collect user-specified index entries {\footnotesize 86}$\rangle$\verb@@\\
+\verb@ case '}': push('\0', &writer);@\\
+\verb@ return scraps++;@\\
+\verb@ case '<': @$\langle$Handle macro invocation in scrap {\footnotesize 87}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default : fprintf(stderr, "%s: unexpected @{\tt @}\verb@%c in scrap (%s, %d)\n",@\\
+\verb@ command_name, c, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 84.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Collect user-specified index entries {\footnotesize 86}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ do {@\\
+\verb@ char new_name[100];@\\
+\verb@ char *p = new_name;@\\
+\verb@ do @\\
+\verb@ c = source_get();@\\
+\verb@ while (isspace(c));@\\
+\verb@ if (c != '@{\tt @}\verb@') {@\\
+\verb@ Name *name;@\\
+\verb@ do {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = source_get();@\\
+\verb@ } while (c != '@{\tt @}\verb@' && !isspace(c));@\\
+\verb@ *p = '\0';@\\
+\verb@ name = name_add(&user_names, new_name);@\\
+\verb@ if (!name->defs || name->defs->scrap != scraps) {@\\
+\verb@ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\
+\verb@ def->scrap = scraps;@\\
+\verb@ def->next = name->defs;@\\
+\verb@ name->defs = def;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ } while (c != '@{\tt @}\verb@');@\\
+\verb@ c = source_get();@\\
+\verb@ if (c != '}') {@\\
+\verb@ fprintf(stderr, "%s: unexpected @{\tt @}\verb@%c in scrap (%s, %d)\n",@\\
+\verb@ command_name, c, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 85.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle macro invocation in scrap {\footnotesize 87}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = collect_scrap_name();@\\
+\verb@ @$\langle$Save macro name {\footnotesize 88}$\rangle$\verb@@\\
+\verb@ @$\langle$Add current scrap to {\tt name}'s uses {\footnotesize 89}$\rangle$\verb@@\\
+\verb@ c = source_get();@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 85.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Save macro name {\footnotesize 88}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char *s = name->spelling;@\\
+\verb@ int len = strlen(s) - 1;@\\
+\verb@ pushs("@{\tt @}\verb@<", &writer);@\\
+\verb@ while (len > 0) {@\\
+\verb@ push(*s++, &writer);@\\
+\verb@ len--;@\\
+\verb@ }@\\
+\verb@ if (*s == ' ')@\\
+\verb@ pushs("...", &writer);@\\
+\verb@ else@\\
+\verb@ push(*s, &writer);@\\
+\verb@ pushs("@{\tt @}\verb@>", &writer);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 87.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Add current scrap to {\tt name}'s uses {\footnotesize 89}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (!name->uses || name->uses->scrap != scraps) {@\\
+\verb@ Scrap_Node *use = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\
+\verb@ use->scrap = scraps;@\\
+\verb@ use->next = name->uses;@\\
+\verb@ name->uses = use;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 87.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 90 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static char pop(manager)@\\
+\verb@ Manager *manager;@\\
+\verb@{@\\
+\verb@ Slab *scrap = manager->scrap;@\\
+\verb@ int index = manager->index;@\\
+\verb@ char c = scrap->chars[index++];@\\
+\verb@ if (index == SLAB_SIZE) {@\\
+\verb@ manager->scrap = scrap->next;@\\
+\verb@ index = 0;@\\
+\verb@ }@\\
+\verb@ manager->index = index;@\\
+\verb@ return c;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 91 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static Name *pop_scrap_name(manager)@\\
+\verb@ Manager *manager;@\\
+\verb@{@\\
+\verb@ char name[100];@\\
+\verb@ char *p = name;@\\
+\verb@ int c = pop(manager);@\\
+\verb@ while (TRUE) {@\\
+\verb@ if (c == '@{\tt @}\verb@')@\\
+\verb@ @$\langle$Check for end of scrap name and return {\footnotesize 92}$\rangle$\verb@@\\
+\verb@ else {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = pop(manager);@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Check for end of scrap name and return {\footnotesize 92}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = pop(manager);@\\
+\verb@ if (c == '@{\tt @}\verb@') {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = pop(manager);@\\
+\verb@ }@\\
+\verb@ else if (c == '>') {@\\
+\verb@ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {@\\
+\verb@ p[-3] = ' ';@\\
+\verb@ p -= 2;@\\
+\verb@ }@\\
+\verb@ *p = '\0';@\\
+\verb@ return prefix_add(&macro_names, name);@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr, "%s: found an internal problem (1)\n", command_name);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 91.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 93 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@int write_scraps(file, defs, global_indent, indent_chars,@\\
+\verb@ debug_flag, tab_flag, indent_flag)@\\
+\verb@ FILE *file;@\\
+\verb@ Scrap_Node *defs;@\\
+\verb@ int global_indent;@\\
+\verb@ char *indent_chars;@\\
+\verb@ char debug_flag;@\\
+\verb@ char tab_flag;@\\
+\verb@ char indent_flag;@\\
+\verb@{@\\
+\verb@ int indent = 0;@\\
+\verb@ while (defs) {@\\
+\verb@ @$\langle$Copy {\tt defs->scrap} to {\tt file} {\footnotesize 94}$\rangle$\verb@@\\
+\verb@ defs = defs->next;@\\
+\verb@ }@\\
+\verb@ return indent + global_indent;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Copy {\tt defs->scrap} to {\tt file} {\footnotesize 94}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ char c;@\\
+\verb@ Manager reader;@\\
+\verb@ int line_number = scrap_array(defs->scrap).file_line;@\\
+\verb@ @$\langle$Insert debugging information if required {\footnotesize 95}$\rangle$\verb@@\\
+\verb@ reader.scrap = scrap_array(defs->scrap).slab;@\\
+\verb@ reader.index = 0;@\\
+\verb@ c = pop(&reader);@\\
+\verb@ while (c) {@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Check for macro invocation in scrap {\footnotesize 98}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '\n': putc(c, file);@\\
+\verb@ line_number++;@\\
+\verb@ @$\langle$Insert appropriate indentation {\footnotesize 96}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '\t': @$\langle$Handle tab characters on output {\footnotesize 97}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default: putc(c, file);@\\
+\verb@ indent_chars[global_indent + indent] = ' ';@\\
+\verb@ indent++;@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ c = pop(&reader);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 93.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Insert debugging information if required {\footnotesize 95}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@if (debug_flag) {@\\
+\verb@ fprintf(file, "\n#line %d \"%s\"\n",@\\
+\verb@ line_number, scrap_array(defs->scrap).file_name);@\\
+\verb@ @$\langle$Insert appropriate indentation {\footnotesize 96}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 94 and~98.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Insert appropriate indentation {\footnotesize 96}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (indent_flag) {@\\
+\verb@ if (tab_flag)@\\
+\verb@ for (indent=0; indent<global_indent; indent++)@\\
+\verb@ putc(' ', file);@\\
+\verb@ else@\\
+\verb@ for (indent=0; indent<global_indent; indent++)@\\
+\verb@ putc(indent_chars[indent], file);@\\
+\verb@ }@\\
+\verb@ indent = 0;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 94 and~95.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle tab characters on output {\footnotesize 97}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (tab_flag)@\\
+\verb@ @$\langle$Expand tab into spaces {\footnotesize 46}$\rangle$\verb@@\\
+\verb@ else {@\\
+\verb@ putc('\t', file);@\\
+\verb@ indent_chars[global_indent + indent] = '\t';@\\
+\verb@ indent++;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 94.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Check for macro invocation in scrap {\footnotesize 98}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = pop(&reader);@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': putc(c, file);@\\
+\verb@ indent_chars[global_indent + indent] = ' ';@\\
+\verb@ indent++;@\\
+\verb@ break;@\\
+\verb@ case '<': @$\langle$Copy macro into {\tt file} {\footnotesize 99}$\rangle$\verb@@\\
+\verb@ @$\langle$Insert debugging information if required {\footnotesize 95}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ default: /* ignore, since we should already have a warning */@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 94.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Copy macro into {\tt file} {\footnotesize 99}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Name *name = pop_scrap_name(&reader);@\\
+\verb@ if (name->mark) {@\\
+\verb@ fprintf(stderr, "%s: recursive macro discovered involving <%s>\n",@\\
+\verb@ command_name, name->spelling);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ if (name->defs) {@\\
+\verb@ name->mark = TRUE;@\\
+\verb@ indent = write_scraps(file, name->defs, global_indent + indent,@\\
+\verb@ indent_chars, debug_flag, tab_flag, indent_flag);@\\
+\verb@ indent -= global_indent;@\\
+\verb@ name->mark = FALSE;@\\
+\verb@ }@\\
+\verb@ else if (!tex_flag)@\\
+\verb@ fprintf(stderr, "%s: macro never defined <%s>\n",@\\
+\verb@ command_name, name->spelling);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 98.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Names} \label{names}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Type declarations {\footnotesize 100}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@typedef struct scrap_node {@\\
+\verb@ struct scrap_node *next;@\\
+\verb@ int scrap;@\\
+\verb@} Scrap_Node;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 3, 100 and~101.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Type declarations {\footnotesize 101}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@typedef struct name {@\\
+\verb@ char *spelling;@\\
+\verb@ struct name *llink;@\\
+\verb@ struct name *rlink;@\\
+\verb@ Scrap_Node *defs;@\\
+\verb@ Scrap_Node *uses;@\\
+\verb@ int mark;@\\
+\verb@ char tab_flag;@\\
+\verb@ char indent_flag;@\\
+\verb@ char debug_flag;@\\
+\verb@} Name;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 3, 100 and~101.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Global variables {\footnotesize 102}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *file_names;@\\
+\verb@Name *macro_names;@\\
+\verb@Name *user_names;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 13, 15, 65 and~102.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 103}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *collect_file_name();@\\
+\verb@Name *collect_macro_name();@\\
+\verb@Name *collect_scrap_name();@\\
+\verb@Name *name_add();@\\
+\verb@Name *prefix_add();@\\
+\verb@char *save_string();@\\
+\verb@void reverse_lists();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 104 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@enum { LESS, GREATER, EQUAL, PREFIX, EXTENSION };@\\
+\verb@@\\
+\verb@static int compare(x, y)@\\
+\verb@ char *x;@\\
+\verb@ char *y;@\\
+\verb@{@\\
+\verb@ int len, result;@\\
+\verb@ int xl = strlen(x);@\\
+\verb@ int yl = strlen(y);@\\
+\verb@ int xp = x[xl - 1] == ' ';@\\
+\verb@ int yp = y[yl - 1] == ' ';@\\
+\verb@ if (xp) xl--;@\\
+\verb@ if (yp) yl--;@\\
+\verb@ len = xl < yl ? xl : yl;@\\
+\verb@ result = strncmp(x, y, len);@\\
+\verb@ if (result < 0) return GREATER;@\\
+\verb@ else if (result > 0) return LESS;@\\
+\verb@ else if (xl < yl) {@\\
+\verb@ if (xp) return EXTENSION;@\\
+\verb@ else return LESS;@\\
+\verb@ }@\\
+\verb@ else if (xl > yl) {@\\
+\verb@ if (yp) return PREFIX;@\\
+\verb@ else return GREATER;@\\
+\verb@ }@\\
+\verb@ else return EQUAL;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 105 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@char *save_string(s)@\\
+\verb@ char *s;@\\
+\verb@{@\\
+\verb@ char *new = (char *) arena_getmem((strlen(s) + 1) * sizeof(char));@\\
+\verb@ strcpy(new, s);@\\
+\verb@ return new;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 106 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *prefix_add(root, spelling)@\\
+\verb@ Name **root;@\\
+\verb@ char *spelling;@\\
+\verb@{@\\
+\verb@ Name *node = *root;@\\
+\verb@ while (node) {@\\
+\verb@ switch (compare(node->spelling, spelling)) {@\\
+\verb@ case GREATER: root = &node->rlink;@\\
+\verb@ break;@\\
+\verb@ case LESS: root = &node->llink;@\\
+\verb@ break;@\\
+\verb@ case EQUAL: return node;@\\
+\verb@ case EXTENSION: node->spelling = save_string(spelling);@\\
+\verb@ return node;@\\
+\verb@ case PREFIX: @$\langle$Check for ambiguous prefix {\footnotesize 107}$\rangle$\verb@@\\
+\verb@ return node;@\\
+\verb@ }@\\
+\verb@ node = *root;@\\
+\verb@ }@\\
+\verb@ @$\langle$Create new name entry {\footnotesize 111}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Since a very short prefix might match more than one macro name, I need
+to check for other matches to avoid mistakes. Basically, I simply
+continue the search down {\em both\/} branches of the tree.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Check for ambiguous prefix {\footnotesize 107}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (ambiguous_prefix(node->llink, spelling) ||@\\
+\verb@ ambiguous_prefix(node->rlink, spelling))@\\
+\verb@ fprintf(stderr,@\\
+\verb@ "%s: ambiguous prefix @{\tt @}\verb@<%s...@{\tt @}\verb@> (%s, line %d)\n",@\\
+\verb@ command_name, spelling, source_name, source_line);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 106.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 108 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static int ambiguous_prefix(node, spelling)@\\
+\verb@ Name *node;@\\
+\verb@ char *spelling;@\\
+\verb@{@\\
+\verb@ while (node) {@\\
+\verb@ switch (compare(node->spelling, spelling)) {@\\
+\verb@ case GREATER: node = node->rlink;@\\
+\verb@ break;@\\
+\verb@ case LESS: node = node->llink;@\\
+\verb@ break;@\\
+\verb@ case EQUAL:@\\
+\verb@ case EXTENSION:@\\
+\verb@ case PREFIX: return TRUE;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ return FALSE;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Rob Shillingsburg suggested that I organize the index of
+user-specified identifiers more traditionally; that is, not relying on
+strict {\small ASCII} comparisons via {\tt strcmp}. Ideally, we'd like
+to see the index ordered like this:
+\begin{quote}
+\begin{flushleft}
+aardvark \\
+Adam \\
+atom \\
+Atomic \\
+atoms
+\end{flushleft}
+\end{quote}
+The function {\tt robs\_strcmp} implements the desired predicate.
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 109 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static int robs_strcmp(x, y)@\\
+\verb@ char *x;@\\
+\verb@ char *y;@\\
+\verb@{@\\
+\verb@ char *xx = x;@\\
+\verb@ char *yy = y;@\\
+\verb@ int xc = toupper(*xx);@\\
+\verb@ int yc = toupper(*yy);@\\
+\verb@ while (xc == yc && xc) {@\\
+\verb@ xx++;@\\
+\verb@ yy++;@\\
+\verb@ xc = toupper(*xx);@\\
+\verb@ yc = toupper(*yy);@\\
+\verb@ }@\\
+\verb@ if (xc != yc) return xc - yc;@\\
+\verb@ xc = *x;@\\
+\verb@ yc = *y;@\\
+\verb@ while (xc == yc && xc) {@\\
+\verb@ x++;@\\
+\verb@ y++;@\\
+\verb@ xc = *x;@\\
+\verb@ yc = *y;@\\
+\verb@ }@\\
+\verb@ if (isupper(xc) && islower(yc))@\\
+\verb@ return xc * 2 - (toupper(yc) * 2 + 1);@\\
+\verb@ if (islower(xc) && isupper(yc))@\\
+\verb@ return toupper(xc) * 2 + 1 - yc * 2;@\\
+\verb@ return xc - yc;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 110 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *name_add(root, spelling)@\\
+\verb@ Name **root;@\\
+\verb@ char *spelling;@\\
+\verb@{@\\
+\verb@ Name *node = *root;@\\
+\verb@ while (node) {@\\
+\verb@ int result = robs_strcmp(node->spelling, spelling);@\\
+\verb@ if (result > 0)@\\
+\verb@ root = &node->llink;@\\
+\verb@ else if (result < 0)@\\
+\verb@ root = &node->rlink;@\\
+\verb@ else@\\
+\verb@ return node;@\\
+\verb@ node = *root;@\\
+\verb@ }@\\
+\verb@ @$\langle$Create new name entry {\footnotesize 111}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Create new name entry {\footnotesize 111}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ node = (Name *) arena_getmem(sizeof(Name));@\\
+\verb@ node->spelling = save_string(spelling);@\\
+\verb@ node->mark = FALSE;@\\
+\verb@ node->llink = NULL;@\\
+\verb@ node->rlink = NULL;@\\
+\verb@ node->uses = NULL;@\\
+\verb@ node->defs = NULL;@\\
+\verb@ node->tab_flag = TRUE;@\\
+\verb@ node->indent_flag = TRUE;@\\
+\verb@ node->debug_flag = FALSE;@\\
+\verb@ *root = node;@\\
+\verb@ return node;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 106 and~110.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Name terminated by whitespace. Also check for ``per-file'' flags. Keep
+skipping white space until we reach scrap.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 112 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *collect_file_name()@\\
+\verb@{@\\
+\verb@ Name *new_name;@\\
+\verb@ char name[100];@\\
+\verb@ char *p = name;@\\
+\verb@ int start_line = source_line;@\\
+\verb@ int c = source_get();@\\
+\verb@ while (isspace(c))@\\
+\verb@ c = source_get();@\\
+\verb@ while (isgraph(c)) {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@ if (p == name) {@\\
+\verb@ fprintf(stderr, "%s: expected file name (%s, %d)\n",@\\
+\verb@ command_name, source_name, start_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ *p = '\0';@\\
+\verb@ new_name = name_add(&file_names, name);@\\
+\verb@ @$\langle$Handle optional per-file flags {\footnotesize 113}$\rangle$\verb@@\\
+\verb@ if (c == '@{\tt @}\verb@' && source_get() == '{')@\\
+\verb@ return new_name;@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr, "%s: expected @{\tt @}\verb@{ after file name (%s, %d)\n",@\\
+\verb@ command_name, source_name, start_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Handle optional per-file flags {\footnotesize 113}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ while (1) {@\\
+\verb@ while (isspace(c))@\\
+\verb@ c = source_get();@\\
+\verb@ if (c == '-') {@\\
+\verb@ c = source_get();@\\
+\verb@ do {@\\
+\verb@ switch (c) {@\\
+\verb@ case 't': new_name->tab_flag = FALSE;@\\
+\verb@ break;@\\
+\verb@ case 'd': new_name->debug_flag = TRUE;@\\
+\verb@ break;@\\
+\verb@ case 'i': new_name->indent_flag = FALSE;@\\
+\verb@ break;@\\
+\verb@ default : fprintf(stderr, "%s: unexpected per-file flag (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ c = source_get();@\\
+\verb@ } while (!isspace(c));@\\
+\verb@ }@\\
+\verb@ else break;@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 112.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Name terminated by \verb+\n+ or \verb+@{+; but keep skipping until \verb+@{+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 114 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *collect_macro_name()@\\
+\verb@{@\\
+\verb@ char name[100];@\\
+\verb@ char *p = name;@\\
+\verb@ int start_line = source_line;@\\
+\verb@ int c = source_get();@\\
+\verb@ while (isspace(c))@\\
+\verb@ c = source_get();@\\
+\verb@ while (c != EOF) {@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Check for terminating at-sequence and return name {\footnotesize 115}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '\t':@\\
+\verb@ case ' ': *p++ = ' ';@\\
+\verb@ do@\\
+\verb@ c = source_get();@\\
+\verb@ while (c == ' ' || c == '\t');@\\
+\verb@ break;@\\
+\verb@ case '\n': @$\langle$Skip until scrap begins, then return name {\footnotesize 117}$\rangle$\verb@@\\
+\verb@ default: *p++ = c;@\\
+\verb@ c = source_get();@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ fprintf(stderr, "%s: expected macro name (%s, %d)\n",@\\
+\verb@ command_name, source_name, start_line);@\\
+\verb@ exit(-1);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Check for terminating at-sequence and return name {\footnotesize 115}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': *p++ = c;@\\
+\verb@ break;@\\
+\verb@ case '{': @$\langle$Cleanup and install name {\footnotesize 116}$\rangle$\verb@@\\
+\verb@ default: fprintf(stderr,@\\
+\verb@ "%s: unexpected @{\tt @}\verb@%c in macro name (%s, %d)\n",@\\
+\verb@ command_name, c, source_name, start_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 114.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Cleanup and install name {\footnotesize 116}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ if (p > name && p[-1] == ' ')@\\
+\verb@ p--;@\\
+\verb@ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {@\\
+\verb@ p[-3] = ' ';@\\
+\verb@ p -= 2;@\\
+\verb@ }@\\
+\verb@ if (p == name || name[0] == ' ') {@\\
+\verb@ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ *p = '\0';@\\
+\verb@ return prefix_add(&macro_names, name);@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scraps 115, 117 and~119.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Skip until scrap begins, then return name {\footnotesize 117}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ do@\\
+\verb@ c = source_get();@\\
+\verb@ while (isspace(c));@\\
+\verb@ if (c == '@{\tt @}\verb@' && source_get() == '{')@\\
+\verb@ @$\langle$Cleanup and install name {\footnotesize 116}$\rangle$\verb@@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr, "%s: expected @{\tt @}\verb@{ after macro name (%s, %d)\n",@\\
+\verb@ command_name, source_name, start_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 114.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Terminated by \verb+@>+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 118 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Name *collect_scrap_name()@\\
+\verb@{@\\
+\verb@ char name[100];@\\
+\verb@ char *p = name;@\\
+\verb@ int c = source_get();@\\
+\verb@ while (c == ' ' || c == '\t')@\\
+\verb@ c = source_get();@\\
+\verb@ while (c != EOF) {@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': @$\langle$Look for end of scrap name and return {\footnotesize 119}$\rangle$\verb@@\\
+\verb@ break;@\\
+\verb@ case '\t':@\\
+\verb@ case ' ': *p++ = ' ';@\\
+\verb@ do@\\
+\verb@ c = source_get();@\\
+\verb@ while (c == ' ' || c == '\t');@\\
+\verb@ break;@\\
+\verb@ default: if (isgraph(c)) {@\\
+\verb@ *p++ = c;@\\
+\verb@ c = source_get();@\\
+\verb@ }@\\
+\verb@ else {@\\
+\verb@ fprintf(stderr,@\\
+\verb@ "%s: unexpected character in macro name (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@ break;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ fprintf(stderr, "%s: unexpected end of file (%s, %d)\n",@\\
+\verb@ command_name, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Look for end of scrap name and return {\footnotesize 119}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ c = source_get();@\\
+\verb@ switch (c) {@\\
+\verb@ case '@{\tt @}\verb@': *p++ = c;@\\
+\verb@ c = source_get();@\\
+\verb@ break;@\\
+\verb@ case '>': @$\langle$Cleanup and install name {\footnotesize 116}$\rangle$\verb@@\\
+\verb@ default: fprintf(stderr,@\\
+\verb@ "%s: unexpected @{\tt @}\verb@%c in macro name (%s, %d)\n",@\\
+\verb@ command_name, c, source_name, source_line);@\\
+\verb@ exit(-1);@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 118.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 120 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static Scrap_Node *reverse(); /* a forward declaration */@\\
+\verb@@\\
+\verb@void reverse_lists(names)@\\
+\verb@ Name *names;@\\
+\verb@{@\\
+\verb@ while (names) {@\\
+\verb@ reverse_lists(names->llink);@\\
+\verb@ names->defs = reverse(names->defs);@\\
+\verb@ names->uses = reverse(names->uses);@\\
+\verb@ names = names->rlink;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+Just for fun, here's a non-recursive version of the traditional list
+reversal code. Note that it reverses the list in place; that is, it
+does no new allocations.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"names.c"@ {\footnotesize 121 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static Scrap_Node *reverse(a)@\\
+\verb@ Scrap_Node *a;@\\
+\verb@{@\\
+\verb@ if (a) {@\\
+\verb@ Scrap_Node *b = a->next;@\\
+\verb@ a->next = NULL;@\\
+\verb@ while (b) {@\\
+\verb@ Scrap_Node *c = b->next;@\\
+\verb@ b->next = a;@\\
+\verb@ a = b;@\\
+\verb@ b = c;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ return a;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Searching for Index Entries} \label{search}
+
+Given the array of scraps and a set of index entries, we need to
+search all the scraps for occurences of each entry. The obvious
+approach to this problem would be quite expensive for large documents;
+however, there is an interesting paper describing an efficient
+solution~\cite{aho:75}.
+
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 122 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@\\
+\verb@typedef struct name_node {@\\
+\verb@ struct name_node *next;@\\
+\verb@ Name *name;@\\
+\verb@} Name_Node;@\\
+\verb@@\\
+\verb@typedef struct goto_node {@\\
+\verb@ struct goto_node *next; /* next goto node with same depth */@\\
+\verb@ struct goto_node *fail;@\\
+\verb@ struct move_node *moves;@\\
+\verb@ Name_Node *output;@\\
+\verb@} Goto_Node;@\\
+\verb@@\\
+\verb@typedef struct move_node {@\\
+\verb@ struct move_node *next;@\\
+\verb@ Goto_Node *state;@\\
+\verb@ char c;@\\
+\verb@} Move_Node;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 123 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@\\
+\verb@static Goto_Node *root[128];@\\
+\verb@static int max_depth;@\\
+\verb@static Goto_Node **depths;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 124 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static Goto_Node *goto_lookup(c, g)@\\
+\verb@ char c;@\\
+\verb@ Goto_Node *g;@\\
+\verb@{@\\
+\verb@ Move_Node *m = g->moves;@\\
+\verb@ while (m && m->c != c)@\\
+\verb@ m = m->next;@\\
+\verb@ if (m)@\\
+\verb@ return m->state;@\\
+\verb@ else@\\
+\verb@ return NULL;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Building the Automata}
+
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 125 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@\\
+\verb@static void build_gotos();@\\
+\verb@@\\
+\verb@void search()@\\
+\verb@{@\\
+\verb@ int i;@\\
+\verb@ for (i=0; i<128; i++)@\\
+\verb@ root[i] = NULL;@\\
+\verb@ max_depth = 10;@\\
+\verb@ depths = (Goto_Node **) arena_getmem(max_depth * sizeof(Goto_Node *));@\\
+\verb@ for (i=0; i<max_depth; i++)@\\
+\verb@ depths[i] = NULL;@\\
+\verb@ build_gotos(user_names);@\\
+\verb@ @$\langle$Build failure functions {\footnotesize 128}$\rangle$\verb@@\\
+\verb@ @$\langle$Search scraps {\footnotesize 129}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"scraps.c"@ {\footnotesize 126 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@static void build_gotos(tree)@\\
+\verb@ Name *tree;@\\
+\verb@{@\\
+\verb@ while (tree) {@\\
+\verb@ @$\langle$Extend goto graph with {\tt tree->spelling} {\footnotesize 127}$\rangle$\verb@@\\
+\verb@ build_gotos(tree->rlink);@\\
+\verb@ tree = tree->llink;@\\
+\verb@ }@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Extend goto graph with {\tt tree->spelling} {\footnotesize 127}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int depth = 2;@\\
+\verb@ char *p = tree->spelling;@\\
+\verb@ char c = *p++;@\\
+\verb@ Goto_Node *q = root[c];@\\
+\verb@ if (!q) {@\\
+\verb@ q = (Goto_Node *) arena_getmem(sizeof(Goto_Node));@\\
+\verb@ root[c] = q;@\\
+\verb@ q->moves = NULL;@\\
+\verb@ q->fail = NULL;@\\
+\verb@ q->moves = NULL;@\\
+\verb@ q->output = NULL;@\\
+\verb@ q->next = depths[1];@\\
+\verb@ depths[1] = q;@\\
+\verb@ }@\\
+\verb@ while (c = *p++) {@\\
+\verb@ Goto_Node *new = goto_lookup(c, q);@\\
+\verb@ if (!new) {@\\
+\verb@ Move_Node *new_move = (Move_Node *) arena_getmem(sizeof(Move_Node));@\\
+\verb@ new = (Goto_Node *) arena_getmem(sizeof(Goto_Node));@\\
+\verb@ new->moves = NULL;@\\
+\verb@ new->fail = NULL;@\\
+\verb@ new->moves = NULL;@\\
+\verb@ new->output = NULL;@\\
+\verb@ new_move->state = new;@\\
+\verb@ new_move->c = c;@\\
+\verb@ new_move->next = q->moves;@\\
+\verb@ q->moves = new_move;@\\
+\verb@ if (depth == max_depth) {@\\
+\verb@ int i;@\\
+\verb@ Goto_Node **new_depths =@\\
+\verb@ (Goto_Node **) arena_getmem(2*depth*sizeof(Goto_Node *));@\\
+\verb@ max_depth = 2 * depth;@\\
+\verb@ for (i=0; i<depth; i++)@\\
+\verb@ new_depths[i] = depths[i];@\\
+\verb@ depths = new_depths;@\\
+\verb@ for (i=depth; i<max_depth; i++)@\\
+\verb@ depths[i] = NULL;@\\
+\verb@ }@\\
+\verb@ new->next = depths[depth];@\\
+\verb@ depths[depth] = new;@\\
+\verb@ }@\\
+\verb@ q = new;@\\
+\verb@ depth++;@\\
+\verb@ }@\\
+\verb@ q->output = (Name_Node *) arena_getmem(sizeof(Name_Node));@\\
+\verb@ q->output->next = NULL;@\\
+\verb@ q->output->name = tree;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 126.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Build failure functions {\footnotesize 128}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int depth;@\\
+\verb@ for (depth=1; depth<max_depth; depth++) {@\\
+\verb@ Goto_Node *r = depths[depth];@\\
+\verb@ while (r) {@\\
+\verb@ Move_Node *m = r->moves;@\\
+\verb@ while (m) {@\\
+\verb@ char a = m->c;@\\
+\verb@ Goto_Node *s = m->state;@\\
+\verb@ Goto_Node *state = r->fail;@\\
+\verb@ while (state && !goto_lookup(a, state))@\\
+\verb@ state = state->fail;@\\
+\verb@ if (state)@\\
+\verb@ s->fail = goto_lookup(a, state);@\\
+\verb@ else@\\
+\verb@ s->fail = root[a];@\\
+\verb@ if (s->fail) {@\\
+\verb@ Name_Node *p = s->fail->output;@\\
+\verb@ while (p) {@\\
+\verb@ Name_Node *q = (Name_Node *) arena_getmem(sizeof(Name_Node));@\\
+\verb@ q->name = p->name;@\\
+\verb@ q->next = s->output;@\\
+\verb@ s->output = q;@\\
+\verb@ p = p->next;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@ m = m->next;@\\
+\verb@ }@\\
+\verb@ r = r->next;@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 125.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Searching the Scraps}
+
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Search scraps {\footnotesize 129}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ for (i=1; i<scraps; i++) {@\\
+\verb@ char c;@\\
+\verb@ Manager reader;@\\
+\verb@ Goto_Node *state = NULL;@\\
+\verb@ reader.scrap = scrap_array(i).slab;@\\
+\verb@ reader.index = 0;@\\
+\verb@ c = pop(&reader);@\\
+\verb@ while (c) {@\\
+\verb@ while (state && !goto_lookup(c, state))@\\
+\verb@ state = state->fail;@\\
+\verb@ if (state)@\\
+\verb@ state = goto_lookup(c, state);@\\
+\verb@ else@\\
+\verb@ state = root[c];@\\
+\verb@ if (state && state->output) {@\\
+\verb@ Name_Node *p = state->output;@\\
+\verb@ do {@\\
+\verb@ Name *name = p->name;@\\
+\verb@ if (!name->uses || name->uses->scrap != i) {@\\
+\verb@ Scrap_Node *new_use =@\\
+\verb@ (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\
+\verb@ new_use->scrap = i;@\\
+\verb@ new_use->next = name->uses;@\\
+\verb@ name->uses = new_use;@\\
+\verb@ }@\\
+\verb@ p = p->next;@\\
+\verb@ } while (p);@\\
+\verb@ }@\\
+\verb@ c = pop(&reader);@\\
+\verb@ }@\\
+\verb@ }@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 125.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\section{Memory Management} \label{memory-management}
+
+I manage memory using a simple scheme inspired by Hanson's idea of
+{\em arenas\/}~\cite{hanson:90}.
+Basically, I allocate all the storage required when processing a
+source file (primarily for names and scraps) using calls to
+{\tt arena\_getmem(n)}, where {\tt n} specifies the number of bytes to
+be allocated. When the storage is no longer required, the entire arena
+is freed with a single call to {\tt arena\_free()}. Both operations
+are quite fast.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Function prototypes {\footnotesize 130}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void *arena_getmem();@\\
+\verb@void arena_free();@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro defined by scraps 23, 31, 60, 64, 77, 103 and~130.\\[-1ex]
+Macro referenced in scrap 1.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"arena.c"@ {\footnotesize 131 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@typedef struct chunk {@\\
+\verb@ struct chunk *next;@\\
+\verb@ char *limit;@\\
+\verb@ char *avail;@\\
+\verb@} Chunk;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 11, 131, 132, 133 and~136.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+We define an empty chunk called {\tt first}. The variable {\tt arena} points
+at the current chunk of memory; it's initially pointed at {\tt first}.
+As soon as some storage is required, a ``real'' chunk of memory will
+be allocated and attached to {\tt first->next}; storage will be
+allocated from the new chunk (and later chunks if necessary).
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"arena.c"@ {\footnotesize 132 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@Chunk first = { NULL, NULL, NULL };@\\
+\verb@Chunk *arena = &first;@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 11, 131, 132, 133 and~136.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Allocating Memory}
+
+The routine {\tt arena\_getmem(n)} returns a pointer to (at least)
+{\tt n} bytes of memory. Note that {\tt n} is rounded up to ensure
+that returned pointers are always aligned.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"arena.c"@ {\footnotesize 133 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void *arena_getmem(n)@\\
+\verb@ int n;@\\
+\verb@{@\\
+\verb@ char *q;@\\
+\verb@ char *p = arena->avail;@\\
+\verb@ n = (n + 3) & ~3; /* ensuring alignment to 4 bytes */@\\
+\verb@ q = p + n;@\\
+\verb@ if (q <= arena->limit) {@\\
+\verb@ arena->avail = q;@\\
+\verb@ return p;@\\
+\verb@ }@\\
+\verb@ @$\langle$Find a new chunk of memory {\footnotesize 134}$\rangle$\verb@@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 11, 131, 132, 133 and~136.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+If the current chunk doesn't have adequate space (at least {\tt n}
+bytes) we examine the rest of the list of chunks (starting at
+{\tt arena->next}) looking for a chunk with adequate space. If {\tt n}
+is very large, we may not find it right away or we may not find a
+suitable chunk at all.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Find a new chunk of memory {\footnotesize 134}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ Chunk *ap = arena;@\\
+\verb@ Chunk *np = ap->next;@\\
+\verb@ while (np) {@\\
+\verb@ char *v = sizeof(Chunk) + (char *) np;@\\
+\verb@ if (v + n <= np->limit) {@\\
+\verb@ np->avail = v + n;@\\
+\verb@ arena = np;@\\
+\verb@ return v;@\\
+\verb@ }@\\
+\verb@ ap = np;@\\
+\verb@ np = ap->next;@\\
+\verb@ }@\\
+\verb@ @$\langle$Allocate a new chunk of memory {\footnotesize 135}$\rangle$\verb@@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 133.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+If there isn't a suitable chunk of memory on the free list, then we
+need to allocate a new one.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Allocate a new chunk of memory {\footnotesize 135}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@{@\\
+\verb@ int m = n + 10000;@\\
+\verb@ np = (Chunk *) malloc(m);@\\
+\verb@ np->limit = m + (char *) np;@\\
+\verb@ np->avail = n + sizeof(Chunk) + (char *) np;@\\
+\verb@ np->next = NULL;@\\
+\verb@ ap->next = np;@\\
+\verb@ arena = np;@\\
+\verb@ return sizeof(Chunk) + (char *) np;@\\
+\verb@}@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 134.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\subsection{Freeing Memory}
+
+To free all the memory in the arena, we need only point {\tt arena}
+back to the first empty chunk.
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+\verb@"arena.c"@ {\footnotesize 136 }$\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@void arena_free()@\\
+\verb@{@\\
+\verb@ arena = &first;@\\
+\verb@}@\\
+\verb@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize File defined by scraps 11, 131, 132, 133 and~136.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\chapter{Indices} \label{indices}
+
+Three sets of indices can be created automatically: an index of file
+names, an index of macro names, and an index of user-specified
+identifiers. An index entry includes the name of the entry, where it
+was defined, and where it was referenced.
+
+
+
+\section{Files}
+
+
+\begin{list}{}{\setlength{\itemsep}{-\parsep}}
+\item \hspace{-\leftmargin}\verb@"arena.c"@ {\footnotesize Defined by scraps 11, 131, 132, 133 and~136.}
+\item \hspace{-\leftmargin}\verb@"input.c"@ {\footnotesize Defined by scraps 8, 66, 67, 68 and~73.}
+\item \hspace{-\leftmargin}\verb@"latex.c"@ {\footnotesize Defined by scraps 6, 32, 33, 44, 45, 52 and~58.}
+\item \hspace{-\leftmargin}\verb@"main.c"@ {\footnotesize Defined by scraps 4 and~12.}
+\item \hspace{-\leftmargin}\verb@"names.c"@ {\footnotesize Defined by scraps 10, 104, 105, 106, 108, 109, 110, 112, 114, 118, 120 and~121.}
+\item \hspace{-\leftmargin}\verb@"output.c"@ {\footnotesize Defined by scraps 7 and~61.}
+\item \hspace{-\leftmargin}\verb@"pass1.c"@ {\footnotesize Defined by scraps 5 and~24.}
+\item \hspace{-\leftmargin}\verb@"scraps.c"@ {\footnotesize Defined by scraps 9, 74, 75, 76, 78, 79, 80, 81, 82, 90, 91, 93, 122, 123, 124, 125 and~126.}
+\end{list}
+
+\section{Macros}
+
+
+\begin{list}{}{\setlength{\itemsep}{-\parsep}}
+\item \hspace{-\leftmargin}$\langle$Accumulate scrap and return {\tt scraps++} {\footnotesize 84}$\rangle$ {\footnotesize Referenced in scrap 82.}
+\item \hspace{-\leftmargin}$\langle$Add current scrap to {\tt name}'s uses {\footnotesize 89}$\rangle$ {\footnotesize Referenced in scrap 87.}
+\item \hspace{-\leftmargin}$\langle$Add {\tt scrap} to {\tt name}'s definition list {\footnotesize 29}$\rangle$ {\footnotesize Referenced in scraps 27 and~28.}
+\item \hspace{-\leftmargin}$\langle$Allocate a new chunk of memory {\footnotesize 135}$\rangle$ {\footnotesize Referenced in scrap 134.}
+\item \hspace{-\leftmargin}$\langle$Begin the scrap environment {\footnotesize 38}$\rangle$ {\footnotesize Referenced in scraps 36 and~37.}
+\item \hspace{-\leftmargin}$\langle$Build failure functions {\footnotesize 128}$\rangle$ {\footnotesize Referenced in scrap 125.}
+\item \hspace{-\leftmargin}$\langle$Build macro definition {\footnotesize 28}$\rangle$ {\footnotesize Referenced in scrap 26.}
+\item \hspace{-\leftmargin}$\langle$Build output file definition {\footnotesize 27}$\rangle$ {\footnotesize Referenced in scrap 26.}
+\item \hspace{-\leftmargin}$\langle$Build {\tt source\_name} and {\tt tex\_name} {\footnotesize 21}$\rangle$ {\footnotesize Referenced in scrap 20.}
+\item \hspace{-\leftmargin}$\langle$Check at-sequence for end-of-scrap {\footnotesize 47}$\rangle$ {\footnotesize Referenced in scrap 45.}
+\item \hspace{-\leftmargin}$\langle$Check for ambiguous prefix {\footnotesize 107}$\rangle$ {\footnotesize Referenced in scrap 106.}
+\item \hspace{-\leftmargin}$\langle$Check for end of scrap name and return {\footnotesize 92}$\rangle$ {\footnotesize Referenced in scrap 91.}
+\item \hspace{-\leftmargin}$\langle$Check for macro invocation in scrap {\footnotesize 98}$\rangle$ {\footnotesize Referenced in scrap 94.}
+\item \hspace{-\leftmargin}$\langle$Check for terminating at-sequence and return name {\footnotesize 115}$\rangle$ {\footnotesize Referenced in scrap 114.}
+\item \hspace{-\leftmargin}$\langle$Cleanup and install name {\footnotesize 116}$\rangle$ {\footnotesize Referenced in scraps 115, 117 and~119.}
+\item \hspace{-\leftmargin}$\langle$Collect include-file name {\footnotesize 71}$\rangle$ {\footnotesize Referenced in scrap 70.}
+\item \hspace{-\leftmargin}$\langle$Collect user-specified index entries {\footnotesize 86}$\rangle$ {\footnotesize Referenced in scrap 85.}
+\item \hspace{-\leftmargin}$\langle$Compare the temp file and the old file {\footnotesize 63}$\rangle$ {\footnotesize Referenced in scrap 62.}
+\item \hspace{-\leftmargin}$\langle$Copy macro into {\tt file} {\footnotesize 99}$\rangle$ {\footnotesize Referenced in scrap 98.}
+\item \hspace{-\leftmargin}$\langle$Copy {\tt defs->scrap} to {\tt file} {\footnotesize 94}$\rangle$ {\footnotesize Referenced in scrap 93.}
+\item \hspace{-\leftmargin}$\langle$Copy {\tt source\_file} into {\tt tex\_file} {\footnotesize 34}$\rangle$ {\footnotesize Referenced in scrap 33.}
+\item \hspace{-\leftmargin}$\langle$Create new name entry {\footnotesize 111}$\rangle$ {\footnotesize Referenced in scraps 106 and~110.}
+\item \hspace{-\leftmargin}$\langle$Create new scrap, managed by {\tt writer} {\footnotesize 83}$\rangle$ {\footnotesize Referenced in scrap 82.}
+\item \hspace{-\leftmargin}$\langle$Expand tab into spaces {\footnotesize 46}$\rangle$ {\footnotesize Referenced in scraps 45 and~97.}
+\item \hspace{-\leftmargin}$\langle$Extend goto graph with {\tt tree->spelling} {\footnotesize 127}$\rangle$ {\footnotesize Referenced in scrap 126.}
+\item \hspace{-\leftmargin}$\langle$Fill in the middle of the scrap environment {\footnotesize 39}$\rangle$ {\footnotesize Referenced in scraps 36 and~37.}
+\item \hspace{-\leftmargin}$\langle$Find a new chunk of memory {\footnotesize 134}$\rangle$ {\footnotesize Referenced in scrap 133.}
+\item \hspace{-\leftmargin}$\langle$Finish the scrap environment {\footnotesize 40}$\rangle$ {\footnotesize Referenced in scraps 36 and~37.}
+\item \hspace{-\leftmargin}$\langle$Format a user index entry {\footnotesize 59}$\rangle$ {\footnotesize Referenced in scrap 58.}
+\item \hspace{-\leftmargin}$\langle$Format an index entry {\footnotesize 53}$\rangle$ {\footnotesize Referenced in scrap 52.}
+\item \hspace{-\leftmargin}$\langle$Format macro name {\footnotesize 49}$\rangle$ {\footnotesize Referenced in scrap 47.}
+\item \hspace{-\leftmargin}$\langle$Function prototypes {\footnotesize 23, 31, 60, 64, 77, 103, 130}$\rangle$ {\footnotesize Referenced in scrap 1.}
+\item \hspace{-\leftmargin}$\langle$Global variables {\footnotesize 13, 15, 65, 102}$\rangle$ {\footnotesize Referenced in scrap 1.}
+\item \hspace{-\leftmargin}$\langle$Handle an ``at'' character {\footnotesize 69}$\rangle$ {\footnotesize Referenced in scrap 68.}
+\item \hspace{-\leftmargin}$\langle$Handle at-sign during scrap accumulation {\footnotesize 85}$\rangle$ {\footnotesize Referenced in scrap 84.}
+\item \hspace{-\leftmargin}$\langle$Handle macro invocation in scrap {\footnotesize 87}$\rangle$ {\footnotesize Referenced in scrap 85.}
+\item \hspace{-\leftmargin}$\langle$Handle optional per-file flags {\footnotesize 113}$\rangle$ {\footnotesize Referenced in scrap 112.}
+\item \hspace{-\leftmargin}$\langle$Handle tab characters on output {\footnotesize 97}$\rangle$ {\footnotesize Referenced in scrap 94.}
+\item \hspace{-\leftmargin}$\langle$Handle the file name in {\tt argv[arg]} {\footnotesize 20}$\rangle$ {\footnotesize Referenced in scrap 19.}
+\item \hspace{-\leftmargin}$\langle$Handle {\tt EOF} {\footnotesize 72}$\rangle$ {\footnotesize Referenced in scrap 68.}
+\item \hspace{-\leftmargin}$\langle$Include files {\footnotesize 2}$\rangle$ {\footnotesize Referenced in scraps 1 and~11.}
+\item \hspace{-\leftmargin}$\langle$Insert appropriate indentation {\footnotesize 96}$\rangle$ {\footnotesize Referenced in scraps 94 and~95.}
+\item \hspace{-\leftmargin}$\langle$Insert debugging information if required {\footnotesize 95}$\rangle$ {\footnotesize Referenced in scraps 94 and~98.}
+\item \hspace{-\leftmargin}$\langle$Interpret at-sequence {\footnotesize 35}$\rangle$ {\footnotesize Referenced in scrap 34.}
+\item \hspace{-\leftmargin}$\langle$Interpret command-line arguments {\footnotesize 14, 16, 17}$\rangle$ {\footnotesize Referenced in scrap 12.}
+\item \hspace{-\leftmargin}$\langle$Interpret the argument string {\tt s} {\footnotesize 18}$\rangle$ {\footnotesize Referenced in scrap 17.}
+\item \hspace{-\leftmargin}$\langle$Look for end of scrap name and return {\footnotesize 119}$\rangle$ {\footnotesize Referenced in scrap 118.}
+\item \hspace{-\leftmargin}$\langle$Open an include file {\footnotesize 70}$\rangle$ {\footnotesize Referenced in scrap 69.}
+\item \hspace{-\leftmargin}$\langle$Process a file {\footnotesize 22}$\rangle$ {\footnotesize Referenced in scrap 20.}
+\item \hspace{-\leftmargin}$\langle$Process the remaining arguments (file names) {\footnotesize 19}$\rangle$ {\footnotesize Referenced in scrap 12.}
+\item \hspace{-\leftmargin}$\langle$Reverse cross-reference lists {\footnotesize 30}$\rangle$ {\footnotesize Referenced in scrap 24.}
+\item \hspace{-\leftmargin}$\langle$Save macro name {\footnotesize 88}$\rangle$ {\footnotesize Referenced in scrap 87.}
+\item \hspace{-\leftmargin}$\langle$Scan at-sequence {\footnotesize 26}$\rangle$ {\footnotesize Referenced in scrap 25.}
+\item \hspace{-\leftmargin}$\langle$Scan the source file, looking for at-sequences {\footnotesize 25}$\rangle$ {\footnotesize Referenced in scrap 24.}
+\item \hspace{-\leftmargin}$\langle$Search scraps {\footnotesize 129}$\rangle$ {\footnotesize Referenced in scrap 125.}
+\item \hspace{-\leftmargin}$\langle$Shared declarations {\footnotesize 1}$\rangle$ {\footnotesize Referenced in scraps 4, 5, 6, 7, 8, 9 and~10.}
+\item \hspace{-\leftmargin}$\langle$Skip over index entries {\footnotesize 48}$\rangle$ {\footnotesize Referenced in scrap 47.}
+\item \hspace{-\leftmargin}$\langle$Skip until scrap begins, then return name {\footnotesize 117}$\rangle$ {\footnotesize Referenced in scrap 114.}
+\item \hspace{-\leftmargin}$\langle$Type declarations {\footnotesize 3, 100, 101}$\rangle$ {\footnotesize Referenced in scrap 1.}
+\item \hspace{-\leftmargin}$\langle$Write defining scrap numbers {\footnotesize 55}$\rangle$ {\footnotesize Referenced in scrap 53.}
+\item \hspace{-\leftmargin}$\langle$Write file defs {\footnotesize 41}$\rangle$ {\footnotesize Referenced in scrap 36.}
+\item \hspace{-\leftmargin}$\langle$Write file's defining scrap numbers {\footnotesize 54}$\rangle$ {\footnotesize Referenced in scrap 53.}
+\item \hspace{-\leftmargin}$\langle$Write index of file names {\footnotesize 50}$\rangle$ {\footnotesize Referenced in scrap 35.}
+\item \hspace{-\leftmargin}$\langle$Write index of macro names {\footnotesize 51}$\rangle$ {\footnotesize Referenced in scrap 35.}
+\item \hspace{-\leftmargin}$\langle$Write index of user-specified names {\footnotesize 57}$\rangle$ {\footnotesize Referenced in scrap 35.}
+\item \hspace{-\leftmargin}$\langle$Write macro definition {\footnotesize 37}$\rangle$ {\footnotesize Referenced in scrap 35.}
+\item \hspace{-\leftmargin}$\langle$Write macro defs {\footnotesize 42}$\rangle$ {\footnotesize Referenced in scrap 37.}
+\item \hspace{-\leftmargin}$\langle$Write macro refs {\footnotesize 43}$\rangle$ {\footnotesize Referenced in scrap 37.}
+\item \hspace{-\leftmargin}$\langle$Write out {\tt files->spelling} {\footnotesize 62}$\rangle$ {\footnotesize Referenced in scrap 61.}
+\item \hspace{-\leftmargin}$\langle$Write output file definition {\footnotesize 36}$\rangle$ {\footnotesize Referenced in scrap 35.}
+\item \hspace{-\leftmargin}$\langle$Write referencing scrap numbers {\footnotesize 56}$\rangle$ {\footnotesize Referenced in scrap 53.}
+\end{list}
+
+\section{Identifiers}
+
+Knuth prints his index of indentifiers in a two-column format.
+I could force this automatically by emitting the \verb|\twocolumn|
+command; but this has the side effect of forcing a new page.
+Therefore, it seems better to leave it this up to the user.
+
+
+\begin{list}{}{\setlength{\itemsep}{-\parsep}}
+\item \hspace{-\leftmargin}\verb@arena@: 22, 29, 78, 80, 83, 86, 89, 105, 111, 125, 127, 128, 129, 130, \underline{132}, 133, 134, 135, 136.
+\item \hspace{-\leftmargin}\verb@arena_free@: 22, 130, \underline{136}.
+\item \hspace{-\leftmargin}\verb@arena_getmem@: 29, 78, 80, 83, 86, 89, 105, 111, 125, 127, 128, 129, 130, \underline{133}.
+\item \hspace{-\leftmargin}\verb@build_gotos@: 125, \underline{126}.
+\item \hspace{-\leftmargin}\verb@Chunk@: \underline{131}, 132, 134, 135.
+\item \hspace{-\leftmargin}\verb@collect_file_name@: 27, 36, 103, \underline{112}.
+\item \hspace{-\leftmargin}\verb@collect_macro_name@: 28, 37, 103, \underline{114}.
+\item \hspace{-\leftmargin}\verb@collect_scrap@: 27, 28, 49, 77, \underline{82}, 87, 103, 118.
+\item \hspace{-\leftmargin}\verb@collect_scrap_name@: 49, 87, 103, \underline{118}.
+\item \hspace{-\leftmargin}\verb@command_name@: \underline{15}, 16, 18, 19, 26, 33, 43, 49, 62, 69, 70, 71, 73, 84, 85, 86, 92, 99, 107, 112, 113, 114, 115, 116, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@compare@: 13, 14, 18, 62, \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@compare_flag@: \underline{13}, 14, 18, 62.
+\item \hspace{-\leftmargin}\verb@copy_scrap@: 32, 39, \underline{45}.
+\item \hspace{-\leftmargin}\verb@double_at@: \underline{66}, 69, 73.
+\item \hspace{-\leftmargin}\verb@EQUAL@: \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@exit@: \underline{2}, 12, 19, 62, 69, 70, 71, 73, 84, 85, 86, 92, 99, 112, 114, 115, 116, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@EXTENSION@: \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@FALSE@: \underline{3}, 13, 18, 35, 51, 69, 73, 99, 108, 111, 113.
+\item \hspace{-\leftmargin}\verb@fclose@: \underline{2}, 33, 62, 63, 72.
+\item \hspace{-\leftmargin}\verb@FILE@: \underline{2}, 33, 44, 45, 52, 58, 62, 63, 66, 67, 93.
+\item \hspace{-\leftmargin}\verb@file_names@: 22, 24, 30, 50, \underline{102}, 112.
+\item \hspace{-\leftmargin}\verb@first@: \underline{132}, 136.
+\item \hspace{-\leftmargin}\verb@fopen@: \underline{2}, 33, 62, 63, 70, 73.
+\item \hspace{-\leftmargin}\verb@format_entry@: 32, 50, 51, \underline{52}.
+\item \hspace{-\leftmargin}\verb@format_user_entry@: 32, 57, \underline{58}.
+\item \hspace{-\leftmargin}\verb@fprintf@: \underline{2}, 18, 19, 26, 33, 36, 37, 43, 44, 49, 53, 54, 55, 56, 59, 62, 69, 70, 71, 73, 84, 85, 86, 92, 95, 99, 107, 112, 113, 114, 115, 116, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@fputs@: \underline{2}, 38, 39, 40, 41, 42, 43, 45, 47, 49, 50, 51, 53, 54, 55, 56, 57, 59.
+\item \hspace{-\leftmargin}\verb@getc@: \underline{2}, 63, 68, 69, 70, 71, 72, 73.
+\item \hspace{-\leftmargin}\verb@goto_lookup@: \underline{124}, 127, 128, 129.
+\item \hspace{-\leftmargin}\verb@Goto_Node@: \underline{122}, 123, 124, 125, 127, 128, 129.
+\item \hspace{-\leftmargin}\verb@GREATER@: \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@include_depth@: \underline{66}, 70, 72, 73.
+\item \hspace{-\leftmargin}\verb@init_scraps@: 24, 77, \underline{78}.
+\item \hspace{-\leftmargin}\verb@isgraph@: \underline{2}, 71, 112, 118.
+\item \hspace{-\leftmargin}\verb@islower@: \underline{2}, 109.
+\item \hspace{-\leftmargin}\verb@isspace@: \underline{2}, 40, 86, 112, 113, 114, 117.
+\item \hspace{-\leftmargin}\verb@isupper@: \underline{2}, 109.
+\item \hspace{-\leftmargin}\verb@LESS@: \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@macro_names@: 24, 30, 51, 92, \underline{102}, 116.
+\item \hspace{-\leftmargin}\verb@main@: \underline{12}.
+\item \hspace{-\leftmargin}\verb@Manager@: \underline{79}, 80, 81, 82, 90, 91, 94, 129.
+\item \hspace{-\leftmargin}\verb@max_depth@: \underline{123}, 125, 127, 128.
+\item \hspace{-\leftmargin}\verb@Move_Node@: \underline{122}, 124, 127, 128.
+\item \hspace{-\leftmargin}\verb@Name@: 27, 28, 36, 37, 49, 52, 58, 61, 86, 87, 91, 99, \underline{101}, 102, 103, 106, 108, 110, 111, 112, 114, 118, 120, 122, 126, 127, 128, 129.
+\item \hspace{-\leftmargin}\verb@name_add@: 86, 103, \underline{110}, 112.
+\item \hspace{-\leftmargin}\verb@Name_Node@: \underline{122}, 127, 128, 129.
+\item \hspace{-\leftmargin}\verb@output_flag@: \underline{13}, 14, 18, 22.
+\item \hspace{-\leftmargin}\verb@pass1@: 22, 23, \underline{24}, 47.
+\item \hspace{-\leftmargin}\verb@pop@: \underline{90}, 91, 92, 94, 98, 99, 129.
+\item \hspace{-\leftmargin}\verb@pop_scrap_name@: \underline{91}, 99.
+\item \hspace{-\leftmargin}\verb@PREFIX@: \underline{104}, 106, 108.
+\item \hspace{-\leftmargin}\verb@prefix_add@: 92, 103, \underline{106}, 116.
+\item \hspace{-\leftmargin}\verb@print_scrap_numbers@: 32, 41, 42, 43, \underline{44}, 54, 56.
+\item \hspace{-\leftmargin}\verb@push@: \underline{80}, 81, 84, 85, 88.
+\item \hspace{-\leftmargin}\verb@pushs@: \underline{81}, 85, 88.
+\item \hspace{-\leftmargin}\verb@putc@: \underline{2}, 34, 35, 45, 46, 47, 49, 53, 54, 55, 56, 94, 96, 97, 98.
+\item \hspace{-\leftmargin}\verb@reverse@: 30, 103, 120, \underline{121}.
+\item \hspace{-\leftmargin}\verb@reverse_lists@: 30, 103, \underline{120}.
+\item \hspace{-\leftmargin}\verb@robs_strcmp@: \underline{109}, 110.
+\item \hspace{-\leftmargin}\verb@root@: 106, 110, 111, \underline{123}, 125, 127, 128, 129.
+\item \hspace{-\leftmargin}\verb@save_string@: 70, 83, 103, \underline{105}, 106, 111.
+\item \hspace{-\leftmargin}\verb@SCRAP@: \underline{76}, 78, 83.
+\item \hspace{-\leftmargin}\verb@ScrapEntry@: \underline{75}, 76, 78, 83.
+\item \hspace{-\leftmargin}\verb@scraps@: 24, 34, 36, 37, 41, 42, 43, 44, 62, \underline{76}, 77, 78, 82, 83, 84, 85, 86, 89, 93, 99, 125, 129.
+\item \hspace{-\leftmargin}\verb@scrap_array@: \underline{76}, 83, 84, 94, 95, 129.
+\item \hspace{-\leftmargin}\verb@Scrap_Node@: 29, 44, 54, 55, 56, 59, 86, 89, 93, \underline{100}, 101, 120, 121, 129.
+\item \hspace{-\leftmargin}\verb@search@: 24, \underline{125}.
+\item \hspace{-\leftmargin}\verb@Slab@: \underline{74}, 75, 79, 80, 83, 90.
+\item \hspace{-\leftmargin}\verb@SLAB_SIZE@: \underline{74}, 80, 90.
+\item \hspace{-\leftmargin}\verb@source_file@: \underline{66}, 68, 69, 70, 71, 72, 73.
+\item \hspace{-\leftmargin}\verb@source_get@: 25, 26, 34, 35, 40, 45, 47, 48, 50, 51, 57, 64, \underline{68}, 70, 72, 84, 85, 86, 87, 112, 113, 114, 115, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@source_line@: 26, \underline{65}, 68, 69, 70, 71, 72, 73, 83, 85, 86, 107, 112, 113, 114, 116, 118, 119.
+\item \hspace{-\leftmargin}\verb@source_name@: 20, 21, 22, 26, \underline{65}, 69, 70, 71, 72, 73, 83, 85, 86, 107, 112, 113, 114, 115, 116, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@source_open@: 24, 33, 64, \underline{73}.
+\item \hspace{-\leftmargin}\verb@source_peek@: \underline{66}, 68, 69, 70, 72, 73.
+\item \hspace{-\leftmargin}\verb@stack@: \underline{67}, 70, 72.
+\item \hspace{-\leftmargin}\verb@stderr@: \underline{2}, 18, 19, 26, 33, 43, 49, 62, 69, 70, 71, 73, 84, 85, 86, 92, 99, 107, 112, 113, 114, 115, 116, 117, 118, 119.
+\item \hspace{-\leftmargin}\verb@strlen@: \underline{2}, 88, 104, 105.
+\item \hspace{-\leftmargin}\verb@tex_flag@: \underline{13}, 14, 18, 22, 24, 99.
+\item \hspace{-\leftmargin}\verb@toupper@: \underline{2}, 109.
+\item \hspace{-\leftmargin}\verb@TRUE@: \underline{3}, 14, 35, 50, 69, 91, 99, 108, 111, 113.
+\item \hspace{-\leftmargin}\verb@user_names@: 24, 30, 57, 86, \underline{102}, 125.
+\item \hspace{-\leftmargin}\verb@write_files@: 22, 60, \underline{61}.
+\item \hspace{-\leftmargin}\verb@write_scraps@: 62, 77, \underline{93}, 99.
+\item \hspace{-\leftmargin}\verb@write_tex@: 22, 31, \underline{33}.
+\end{list}
+
+\bibliographystyle{plain}
+\bibliography{literate}
+
+\end{document}
diff --git a/web/nuweb/msdos/nuweb.toc b/web/nuweb/msdos/nuweb.toc
new file mode 100644
index 0000000000..fae9cbb274
--- /dev/null
+++ b/web/nuweb/msdos/nuweb.toc
@@ -0,0 +1,43 @@
+\contentsline {chapter}{\numberline {1}Introduction}{3}
+\contentsline {section}{\numberline {1.1}Nuweb}{3}
+\contentsline {section}{\numberline {1.2}Writing Nuweb}{4}
+\contentsline {subsection}{\numberline {1.2.1}The Major Commands}{4}
+\contentsline {subsubsection}{Scraps}{5}
+\contentsline {subsubsection}{Flags}{5}
+\contentsline {subsection}{\numberline {1.2.2}The Minor Commands}{5}
+\contentsline {section}{\numberline {1.3}Running Nuweb}{6}
+\contentsline {section}{\numberline {1.4}Restrictions and Extensions}{7}
+\contentsline {chapter}{\numberline {2}The Overall Structure}{8}
+\contentsline {section}{\numberline {2.1}Files}{8}
+\contentsline {subsection}{\numberline {2.1.1}The Main Files}{9}
+\contentsline {subsection}{\numberline {2.1.2}Support Files}{9}
+\contentsline {section}{\numberline {2.2}The Main Routine}{10}
+\contentsline {subsection}{\numberline {2.2.1}Command-Line Arguments}{10}
+\contentsline {subsection}{\numberline {2.2.2}File Names}{12}
+\contentsline {section}{\numberline {2.3}Pass One}{14}
+\contentsline {subsection}{\numberline {2.3.1}Accumulating Definitions}{15}
+\contentsline {subsection}{\numberline {2.3.2}Fixing the Cross References}{16}
+\contentsline {section}{\numberline {2.4}Writing the Latex File}{16}
+\contentsline {subsection}{\numberline {2.4.1}Formatting Definitions}{18}
+\contentsline {subsubsection}{Formatting Cross References}{20}
+\contentsline {subsubsection}{Formatting a Scrap}{22}
+\contentsline {subsection}{\numberline {2.4.2}Generating the Indices}{24}
+\contentsline {section}{\numberline {2.5}Writing the Output Files}{27}
+\contentsline {chapter}{\numberline {3}The Support Routines}{29}
+\contentsline {section}{\numberline {3.1}Source Files}{29}
+\contentsline {subsection}{\numberline {3.1.1}Global Declarations}{29}
+\contentsline {subsection}{\numberline {3.1.2}Local Declarations}{29}
+\contentsline {subsection}{\numberline {3.1.3}Reading a File}{30}
+\contentsline {subsection}{\numberline {3.1.4}Opening a File}{32}
+\contentsline {section}{\numberline {3.2}Scraps}{32}
+\contentsline {section}{\numberline {3.3}Names}{41}
+\contentsline {section}{\numberline {3.4}Searching for Index Entries}{51}
+\contentsline {subsection}{\numberline {3.4.1}Building the Automata}{53}
+\contentsline {subsection}{\numberline {3.4.2}Searching the Scraps}{56}
+\contentsline {section}{\numberline {3.5}Memory Management}{56}
+\contentsline {subsection}{\numberline {3.5.1}Allocating Memory}{57}
+\contentsline {subsection}{\numberline {3.5.2}Freeing Memory}{58}
+\contentsline {chapter}{\numberline {4}Indices}{59}
+\contentsline {section}{\numberline {4.1}Files}{59}
+\contentsline {section}{\numberline {4.2}Macros}{59}
+\contentsline {section}{\numberline {4.3}Identifiers}{61}
diff --git a/web/nuweb/msdos/nuweb.w b/web/nuweb/msdos/nuweb.w
new file mode 100644
index 0000000000..751e87e3ec
--- /dev/null
+++ b/web/nuweb/msdos/nuweb.w
@@ -0,0 +1,2723 @@
+\documentstyle{report}
+
+\setlength{\oddsidemargin}{0in}
+\setlength{\evensidemargin}{0in}
+\setlength{\topmargin}{0in}
+\addtolength{\topmargin}{-\headheight}
+\addtolength{\topmargin}{-\headsep}
+\setlength{\textheight}{8.9in}
+\setlength{\textwidth}{6.5in}
+\setlength{\marginparwidth}{0.5in}
+
+\title{Nuweb \\ A Simple Literate Programming Tool}
+\author{\sl Preston Briggs}
+\date{(Version 0.6pc)}
+
+\begin{document}
+\maketitle
+\tableofcontents
+
+\chapter{Introduction}
+
+In 1984, Knuth introduced the idea of {\em literate programming\/} and
+described a pair of tools to support the practise~\cite{knuth:84}.
+His approach was to combine Pascal code with \TeX\ documentation to
+produce a new language, {\tt WEB}, that offered programmers a superior
+approach to programming. He wrote several programs in {\tt WEB},
+including {\tt weave} and {\tt tangle}, the programs used to support
+literate programming.
+The idea was that a programmer wrote one document, the web file, that
+combined documentation (written in \TeX~\cite{texbook}) with code
+(written in Pascal).
+
+Running {\tt tangle} on the web file would produce a complete
+Pascal program, ready for compilation by an ordinary Pascal compiler.
+The primary function of {\tt tangle} is to allow the programmer to
+present elements of the program in any desired order, regardless of
+the restrictions imposed by the programming language. Thus, the
+programmer is free to present his program in a top-down fashion,
+bottom-up fashion, or whatever seems best in terms of promoting
+understanding and maintenance.
+
+Running {\tt weave} on the web file would produce a \TeX\ file, ready
+to be processed by \TeX\@@. The resulting document included a variety of
+automatically generated indices and cross-references that made it much
+easier to navigate the code. Additionally, all of the code sections
+were automatically pretty printed, resulting in a quite impressive
+document.
+
+Knuth also wrote the programs for \TeX\ and {\small\sf METAFONT}
+entirely in {\tt WEB}, eventually publishing them in book
+form~\cite{tex:program,metafont:program}. These are probably the
+largest programs ever published in a readable form.
+
+Inspired by Knuth's example, many people have experimented with
+{\tt WEB}\@@. Some people have even built web-like tools for their own
+favorite combinations of programming language and typesetting
+language. For example, {\tt CWEB}, Knuth's current system of choice,
+works with a combination of C and \TeX~\cite{levy:90}. Another system,
+FunnelWeb, is independent of any programming language and only mildly
+dependent on \TeX~\cite{funnelweb}. Inspired by the versatility
+of FunnelWeb and by the daunting size of its documentation, I decided
+to write my own, very simple, tool for literate programming.%
+\footnote{There is another system similar to mine, written by Norman
+Ramsey, called {\em noweb}~\cite{noweb}. It perhaps suffers from being
+overly Unix-dependent and requiring several programs to use. On the
+other hand, its command syntax is very nice. In any
+case, nuweb certainly owes its name to his inspiration.}
+
+
+\section{Nuweb}
+
+Nuweb works with any programming language and \LaTeX~\cite{latex}. I
+wanted to use \LaTeX\ because it supports a multi-level sectioning
+scheme and has facilities for drawing figures. I wanted to be able to
+work with arbitrary programming languages because my friends and I
+write programs in many languages (and sometimes combinations of
+several languages), {\em e.g.,} C, Fortran, C++, yacc, lex, Scheme,
+assembly, Postscript, and so forth. The need to support arbitrary
+programming languages has many consequences:
+\begin{description}
+\item[No pretty printing] Both {\tt WEB} and {\tt CWEB} are able to
+ pretty print the code sections of their documents because they
+ understand the language well enough to parse it. Since we want to use
+ {\em any\/} language, we've got to abandon this feature.
+\item[No index of identifiers] Because {\tt WEB} knows about Pascal,
+ it is able to construct an index of all the identifiers occurring in
+ the code sections (filtering out keywords and the standard type
+ identifiers). Unfortunately, this isn't as easy in our case. We don't
+ know what an identifiers looks like in each language and we certainly
+ don't know all the keywords. (On the other hand, see the end of
+ Section~1.3)
+\end{description}
+Of course, we've got to have some compensation for our losses or the
+whole idea would be a waste. Here are the advantages I can see:
+\begin{description}
+\item[Simplicity] The majority of the commands in {\tt WEB} are
+ concerned with control of the automatic pretty printing. Since we
+ don't pretty print, many commands are eliminated. A further set of
+ commands is subsumed by \LaTeX\ and may also be eliminated. As a
+ result, our set of commands is reduced to only four members (explained
+ in the next section). This simplicity is also reflected in
+ the size of this tool, which is quite a bit smaller than the tools
+ used with other approaches.
+\item[No pretty printing] Everyone disagrees about how their code
+ should look, so automatic formatting annoys many people. One approach
+ is to provide ways to control the formatting. Our approach is simpler
+ -- we perform no automatic formatting and therefore allow the
+ programmer complete control of code layout.
+\item[Control] We also offer the programmer complete control of the
+ layout of his output files (the files generated during tangling). Of
+ course, this is essential for languages that are sensitive to layout;
+ but it is also important in many practical situations, {\em e.g.,}
+ debugging.
+\item[Speed] Since nuweb doesn't do to much, the nuweb tool runs
+ quickly. I combine the functions of {\tt tangle} and {\tt weave} into
+ a single program that performs both functions at once.
+\item[Multiple file output] The programmer may specify more than one
+ output file in a single nuweb file. This is required when constructing
+ programs in a combination of languages (say, Fortran and C)\@@. It's also
+ an advantage when constructing very large programs that would require
+ a lot of compile time.
+\end{description}
+This last point is very important. By allowing the creation of
+multiple output files, we avoid the need for monolithic programs.
+Thus we support the creation of very large programs by groups of
+people.
+
+A further reduction in compilation time is achieved by first
+writing each output file to a temporary location, then comparing the
+temporary file with the old version of the file. If there is no
+difference, the temporary file can be deleted. If the files differ,
+the old version is deleted and the temporary file renamed. This
+approach works well in combination with {\tt make} (or similar tools),
+since {\tt make} will avoid recompiling untouched output files.
+
+
+\section{Writing Nuweb}
+
+The bulk of a nuweb file will be ordinary \LaTeX\@@. In fact, any \LaTeX\
+file can serve as input to nuweb and will be simply copied through
+unchanged to the {\tt .tex} file -- unless a nuweb command is
+discovered. All nuweb commands begin with an ``at-sign'' ({\tt @@}).
+Therefore, a file without at-signs will be copied unchanged.
+Nuweb commands are used to specify {\em output files,} define
+{\em macros,} and delimit {\em scraps}. These are the basic features
+of interest to the nuweb tool -- all else is simply text to be copied
+to the {\tt .tex} file.
+
+\subsection{The Major Commands}
+
+Files and macros are defined with the following commands:
+\begin{description}
+\item[\tt @@o {\em file-name flags scrap\/}] Output a file. The file name is
+ terminated by whitespace.
+\item[\tt @@d {\em macro-name scrap\/}] Define a macro. The macro name
+ is terminated by a return or the beginning of a scrap.
+\end{description}
+A specific file may be specified several times, with each definition
+being written out, one after the other, in the order they appear.
+The definitions of macros may be similarly divided.
+
+\subsubsection{Scraps}
+
+Scraps have specific begin markers and end markers to allow precise
+control over the contents and layout. Note that any amount of
+whitespace (including carriage returns) may appear between a name and
+the beginning of a scrap.
+\begin{description}
+\item[\tt @@\{{\em anything\/}@@\}] where the scrap body includes every
+ character in {\em anything\/} -- all the blanks, all the tabs, all the
+ carriage returns.
+\end{description}
+Inside a scrap, we may invoke a macro.
+\begin{description}
+\item[\tt @@<{\em macro-name\/}@@>] Causes the macro {\em macro-name\/}
+ to be expanded inline as the code is written out to a file. It is an
+ error to specify recursive macro invocations.
+\end{description}
+Note that macro names may be abbreviated, either during invocation or
+definition. For example, it would be very tedious to have to
+repeatedly type the macro name
+\begin{quote}
+{\tt @@d Check for terminating at-sequence and return name if found}
+\end{quote}
+Therefore, we provide a mechanism (stolen from Knuth) of indicating
+abbreviated names.
+\begin{quote}
+{\tt @@d Check for terminating...}
+\end{quote}
+Basically, the programmer need only type enough characters to uniquely
+identify the macro name, followed by three periods. An abbreviation
+may even occur before the full version; nuweb simply preserves the
+longest version of a macro name. Note also that blanks and tabs are
+insignificant in a macro name; any string of them are replaced by a
+single blank.
+
+When scraps are written to an output or {\tt .tex} file, tabs are
+expanded into spaces by default. Currently, I assume tab stops are set
+every eight characters. Furthermore, when a macro is expanded in a scrap,
+the body of the macro is indented to match the indentation of the
+macro invocation. Therefore, care must be taken with languages
+({\em e.g.,} Fortran) that are sensitive to indentation.
+These default behaviors may be changed for each output file (see
+below).
+
+\subsubsection{Flags}
+
+When defining an output file, the programmer has the option of using
+flags to control output of a particular file. The flags are intended
+to make life a little easier for programmers using certain languages.
+They introduce little language dependences; however, they do so only
+for a particular file. Thus it is still easy to mix languages within a
+single document. There are three ``per-file'' flags:
+\begin{description}
+\item[\tt -d] Forces the creation of \verb|#line| directives in the
+ output file. These are useful with C (and sometimes C++ and Fortran) on
+ many Unix systems since they cause the compiler's error messages to
+ refer to the web file rather than the output file. Similarly, they
+ allow source debugging in terms of the web file.
+\item[\tt -i] Suppresses the indentation of macros. That is, when a
+ macro is expanded in a scrap, it will {\em not\/} be indented to
+ match the indentation of the macro invocation. This flag would seem
+ most useful for Fortran programmers.
+\item[\tt -t] Suppresses expansion of tabs in the output file. This
+ feature seems important when generating {\tt make} files.
+\end{description}
+
+
+\subsection{The Minor Commands}
+
+We have two very low-level utility commands that may appear anywhere
+in the web file.
+\begin{description}
+\item[\tt @@@@] Causes a single ``at sign'' to be copied into the output.
+\item[\tt @@i {\em file-name\/}] Includes a file. Includes may be
+ nested, though there is currently a limit of 10~levels. The file name
+ should be complete (no extension will be appended) and should be
+ terminated by a carriage return.
+\end{description}
+Finally, there are three commands used to create indices to the macro
+names, file definitions, and user-specified identifiers.
+\begin{description}
+\item[\tt @@f] Create an index of file names.
+\item[\tt @@m] Create an index of macro name.
+\item[\tt @@u] Create an index of user-specified identifiers.
+\end{description}
+I usually put these in their own section
+in the \LaTeX\ document; for example, see Chapter~\ref{indices}.
+
+Identifiers must be explicitely specified for inclusion in the
+{\tt @@u} index. By convention, each identifier is marked at the point
+of its definition; all references to each identifier (inside scraps)
+will be discovered automatically. To ``mark'' an identifier for
+inclusion in the index, we must mention it at the end of a scrap.
+For example,
+\begin{quote}
+\begin{verbatim}
+@@d a scrap @@{
+Let's pretend we're declaring the variables FOO and BAR
+inside this scrap.
+@@| FOO BAR @@}
+\end{verbatim}
+\end{quote}
+I've used alphabetic identifiers in this example, but any string of
+characters (not including whitespace) will do. Therefore, it's
+possible to add index entries for things like {\tt <<=} if desired.
+An identifier may be declared in more than one scrap.
+
+In the generated index, each identifier appears with a list of all the
+scraps using and defining it, where the defining scraps are
+distinguished by underlining. Note that the identifier doesn't
+actually have to appear in the defining scrap; it just has to be in
+the list of definitions at the end of a scrap.
+
+
+\section{Running Nuweb}
+
+Nuweb is invoked using the following command:
+\begin{quote}
+{\tt nuweb} {\em flags file-name}\ldots
+\end{quote}
+One or more files may be processed at a time. If a file name has no
+extension, {\tt .w} will be appended. While a file name may specify a
+file in another directory, the resulting {\tt .tex} file will always
+be created in the current directory. For example,
+\begin{quote}
+{\tt nuweb /foo/bar/quux}
+\end{quote}
+will take as input the file {\tt /foo/bar/quux.w} and will create the
+file {\tt quux.tex} in the current directory.
+
+By default, nuweb performs both tangling and weaving at the same time.
+Normally, this is not a bottleneck in the compilation process;
+however, it's possible to achieve slightly faster throughput by
+avoiding one or another of the default functions using command-line
+flags. There are currently three possible flags:
+\begin{description}
+\item[\tt -t] Suppress generation of the {\tt .tex} file.
+\item[\tt -o] Suppress generation of the output files.
+\item[\tt -c] Avoid testing output files for change before updating them.
+\end{description}
+Thus, the command
+\begin{quote}
+{\tt nuweb -to /foo/bar/quux}
+\end{quote}
+would simply scan the input and produce no output at all.
+
+
+\section{Restrictions and Extensions}
+
+Because nuweb is intended to be a simple tool, I've established a few
+restrictions. Over time, some of these may be eliminated; others seem
+fundamental.
+\begin{itemize}
+\item The scraps don't print well with older versions of \TeX\
+ (pre~3.0). I'm not sure why and I'm not sure it's always true
+ (I don't have a version to experiment with).
+\item The handling of errors is not completely ideal. In some cases, I
+ simply warn of a problem and continue; in other cases I halt
+ immediately. This behavior should be regularized.
+\item I warn about references to macros that haven't been defined, but
+ don't halt. This seems most convenient for development, but may change
+ in the future.
+\item File names and index entries should not contain any {\tt @@}
+ signs.
+\item Macro names may be (almost) any well-formed \TeX\ string.
+ It makes sense to change fonts or use math mode; however, care should
+ be taken to ensure matching braces, brackets, and dollar signs.
+\item Anything is allowed in the body of a scrap; however, very
+ long scraps (horizontally or vertically) may not typeset well.
+\end{itemize}
+Very long scraps may be allowed to break across a page if declared
+with {\tt @@O} or {\tt @@D} (instead of {\tt @@o} or {\tt @@d}).
+This doesn't work very well as a default, since far too many short
+scraps will be broken across pages; however, as a user-controlled
+option, it seems very useful.
+
+
+\chapter{The Overall Structure}
+
+Processing a web requires three major steps:
+\begin{enumerate}
+\item Read the source, accumulating file names, macro names, scraps,
+and lists of cross-references.
+\item Reread the source, copying out to the {\tt .tex} file, with
+protection and cross-reference information for all the scraps.
+\item Traverse the list of files names. For each file name:
+\begin{enumerate}
+\item Dump all the defining scraps into a temporary file.
+\item If the file already exists and is unchanged, delete the
+temporary file; otherwise, rename the temporary file.
+\end{enumerate}
+\end{enumerate}
+
+
+\section{Files}
+
+I have divided the program into several files for quicker
+recompilation during development. Rather than use {\tt .h} files
+for shared declarations, I'll simply create a shared scrap containing
+all the global declarations. This isn't really best from the point of
+view of disk space; but it does simplify the makefile (and allow me to
+play with my new toy).
+@d Shared declarations
+@{@<Include files@>
+@<Type declarations@>
+@<Global variables@>
+@<Function prototypes@>
+@}
+
+We'll need at least two of the standard system include files.
+@d Include files
+@{#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+@| FILE stderr exit fprintf fputs fopen fclose getc putc strlen
+toupper isupper islower isgraph isspace @}
+
+\newpage
+\noindent
+I also like to use {\tt TRUE} and {\tt FALSE} in my code.
+I'd use an {\tt enum} here, except that some systems seem to provide
+definitions of {\tt TRUE} and {\tt FALSE} be default. The following
+code seems to work on all the local systems.
+@d Type dec...
+@{#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+@| FALSE TRUE @}
+
+
+\subsection{The Main Files}
+
+The code is divided into four main files (introduced here) and five
+support files (introduced in the next section).
+The file {\tt main.c} will contain the driver for the whole program
+(see Section~\ref{main-routine}).
+@o main.c
+@{@<Shared...@>
+@}
+
+
+The first pass over the source file is contained in {\tt pass1.c}.
+It handles collection of all the file names, macros names, and scraps
+(see Section~\ref{pass-one}).
+@o pass1.c
+@{@<Shared...@>
+@}
+
+The {\tt .tex} file is created during a second pass over the source
+file. The file {\tt latex.c} contains the code controlling the
+construction of the {\tt .tex} file
+(see Section~\ref{latex-file}).
+@o latex.c
+@{@<Shared...@>
+@}
+
+
+The code controlling the creation of the output files is in {\tt output.c}
+(see Section~\ref{output-files}).
+@o output.c
+@{@<Shared...@>
+@}
+
+
+\subsection{Support Files}
+
+The support files contain a variety of support routines used to define
+and manipulate the major data abstractions.
+The file {\tt input.c} holds all the routines used for referring to
+source files (see Section~\ref{source-files}).
+@o input.c
+@{@<Shared...@>
+@}
+
+Creation and lookup of scraps is handled by routines in {\tt scraps.c}
+(see Section~\ref{scraps}).
+@o scraps.c
+@{@<Shared...@>
+@}
+
+
+The handling of file names and macro names is detailed in {\tt names.c}
+(see Section~\ref{names}).
+@o names.c
+@{@<Shared...@>
+@}
+
+Memory allocation and deallocation is handled by routines in {\tt arena.c}
+(see Section~\ref{memory-management}). I don't bother including all
+the shared declarations since they aren't required.
+@o arena.c
+@{@<Include...@>
+@}
+
+
+\section{The Main Routine} \label{main-routine}
+
+The main routine is quite simple in structure.
+It wades through the optional command-line arguments,
+then handles any files listed on the command line.
+@o main.c
+@{void main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int arg = 1;
+ @<Interpret command-line arguments@>
+ @<Process the remaining arguments (file names)@>
+ exit(0);
+}
+@| main @}
+
+
+\subsection{Command-Line Arguments}
+
+There are three possible command-line arguments:
+\begin{description}
+\item[{\tt -t}] Suppresses generation of the {\tt .tex} file.
+\item[{\tt -o}] Suppresses generation of the output files.
+\item[{\tt -c}] Forces output files to overwrite old files of the same
+name without comparing for equality first.
+\item[{\tt -v}] Prints the current version of {\tt nuweb}.
+\end{description}
+Global flags are declared for each of the arguments.
+@d Global...
+@{int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+@| tex_flag output_flag compare_flag @}
+
+\newpage
+\noindent
+The flags are all initialized to {\tt TRUE} for correct default behavior.
+@d Interpret com...
+@{tex_flag = TRUE;
+output_flag = TRUE;
+compare_flag = TRUE;
+@}
+
+
+We save the invocation name of the command in a global variable
+{\tt command\_name} for use in error messages.
+@d Global...
+@{char *command_name;
+@| command_name @}
+
+The invocation name is conventionally passed in {\tt argv[0]}.
+@d Interpret com...
+@{command_name = argv[0];
+@}
+
+
+We need to examine the remaining entries in {\tt argv}, looking for
+command-line arguments.
+@d Interpret com...
+@{while (arg < argc) {
+ char *s = argv[arg];
+ if (*s++ == '-') {
+ @<Interpret the argument string {\tt s}@>
+ arg++;
+ }
+ else break;
+}@}
+
+\newpage
+\noindent
+Several flags can be stacked behind a single minus sign; therefore,
+we've got to loop through the string, handling them all.
+@d Interpret the...
+@{{
+ char c = *s++;
+ while (c) {
+ switch (c) {
+ case 'c': compare_flag = FALSE;
+ break;
+ case 'o': output_flag = FALSE;
+ break;
+ case 't': tex_flag = FALSE;
+ break;
+ case 'v': printf("nuweb version 0.6\n");
+ break;
+ default: fprintf(stderr, "%s: unexpected argument ignored. ",
+ command_name);
+ fprintf(stderr, "Usage is: %s [-cot] file...\n",
+ command_name);
+ break;
+ }
+ c = *s++;
+ }
+}@}
+
+
+\subsection{File Names}
+
+We expect at least one file name. While a missing file name might be
+ignored without causing any problems, we take the opportunity to report
+the usage convention.
+@d Process the remaining...
+@{if (arg < argc)
+ do {
+ @<Handle the file name in {\tt argv[arg]}@>
+ arg++;
+ } while (arg < argc);
+else {
+ fprintf(stderr, "%s: expected a file name. ", command_name);
+ fprintf(stderr, "Usage is: %s [-cot] file-name...\n", command_name);
+ exit(-1);
+}@}
+
+\newpage
+\noindent
+The code to handle a particular file name is rather more tedious than
+the actual processing of the file. A file name may be an arbitrarily
+complicated path name, with an optional extension. If no extension is
+present, we add {\tt .w} as a default. The extended path name will be
+kept in a local variable {\tt source\_name}. The resulting {\tt .tex}
+file will be written in the current directory; its name will be kept
+in the variable {\tt tex\_name}.
+@d Handle the file...
+@{{
+ char source_name[100];
+ char tex_name[100];
+ @<Build {\tt source\_name} and {\tt tex\_name}@>
+ @<Process a file@>
+}@}
+
+
+I bump the pointer {\tt p} through all the characters in {\tt argv[arg]},
+copying all the characters into {\tt source\_name} (via the pointer
+{\tt q}).
+
+At each slash, I update {\tt trim} to point just past the
+slash in {\tt source\_name}. The effect is that {\tt trim} will point
+at the file name without any leading directory specifications.
+
+The pointer {\tt dot} is made to point at the file name extension, if
+present. If there is no extension, we add {\tt .w} to the source name.
+In any case, we create the {\tt tex\_name} from {\tt trim}, taking
+care to get the correct extension.
+@d Build {\tt sou...
+@{{
+ char *p = argv[arg];
+ char *q = source_name;
+ char *trim = q;
+ char *dot = NULL;
+ char c = *p++;
+ while (c) {
+ *q++ = c;
+ if (c == '/') {
+ trim = q;
+ dot = NULL;
+ }
+ else if (c == '.')
+ dot = q - 1;
+ c = *p++;
+ }
+ *q = '\0';
+ if (dot) {
+ *dot = '\0';
+ sprintf(tex_name, "%s.tex", trim);
+ *dot = '.';
+ }
+ else {
+ sprintf(tex_name, "%s.tex", trim);
+ *q++ = '.';
+ *q++ = 'w';
+ *q = '\0';
+ }
+}@}
+
+Now that we're finally ready to process a file, it's not really too
+complex. We bundle most of the work into three routines {\tt pass1}
+(see Section~\ref{pass-one}), {\tt write\_tex} (see
+Section~\ref{latex-file}), and {\tt write\_files} (see
+Section~\ref{output-files}). After we're finished with a
+particular file, we must remember to release its storage (see
+Section~\ref{memory-management}).
+@d Process a file
+@{{
+ pass1(source_name);
+ if (tex_flag)
+ write_tex(source_name, tex_name);
+ if (output_flag)
+ write_files(file_names);
+ arena_free();
+}@}
+
+
+\section{Pass One} \label{pass-one}
+
+During the first pass, we scan the file, recording the definitions of
+each macro and file and accumulating all the scraps.
+
+@d Function pro...
+@{void pass1();
+@}
+
+
+The routine {\tt pass1} takes a single argument, the name of the
+source file. It opens the file, then initializes the scrap structures
+(see Section~\ref{scraps}) and the roots of the file-name tree, the
+macro-name tree, and the tree of user-specified index entries (see
+Section~\ref{names}). After completing all the
+necessary preparation, we make a pass over the file, filling in all
+our data structures. Next, we seach all the scraps for references to
+the user-specified index entries. Finally, we must reverse all the
+cross-reference lists accumulated while scanning the scraps.
+@o pass1.c
+@{void pass1(file_name)
+ char *file_name;
+{
+ source_open(file_name);
+ init_scraps();
+ macro_names = NULL;
+ file_names = NULL;
+ user_names = NULL;
+ @<Scan the source file, looking for at-sequences@>
+ if (tex_flag)
+ search();
+ @<Reverse cross-reference lists@>
+}
+@| pass1 @}
+
+\newpage
+\noindent
+The only thing we look for in the first pass are the command
+sequences. All ordinary text is skipped entirely.
+@d Scan the source file, look...
+@{{
+ int c = source_get();
+ while (c != EOF) {
+ if (c == '@@')
+ @<Scan at-sequence@>
+ c = source_get();
+ }
+}@}
+
+Only four of the at-sequences are interesting during the first pass.
+We skip past others immediately; warning if unexpected sequences are
+discovered.
+@d Scan at-sequence
+@{{
+ c = source_get();
+ switch (c) {
+ case 'O':
+ case 'o': @<Build output file definition@>
+ break;
+ case 'D':
+ case 'd': @<Build macro definition@>
+ break;
+ case '@@':
+ case 'u':
+ case 'm':
+ case 'f': /* ignore during this pass */
+ break;
+ default: fprintf(stderr,
+ "%s: unexpected @@ sequence ignored (%s, line %d)\n",
+ command_name, source_name, source_line);
+ break;
+ }
+}@}
+
+
+\subsection{Accumulating Definitions}
+
+There are three steps required to handle a definition:
+\begin{enumerate}
+\item Build an entry for the name so we can look it up later.
+\item Collect the scrap and save it in the table of scraps.
+\item Attach the scrap to the name.
+\end{enumerate}
+We go through the same steps for both file names and macro names.
+@d Build output file definition
+@{{
+ Name *name = collect_file_name(); /* returns a pointer to the name entry */
+ int scrap = collect_scrap(); /* returns an index to the scrap */
+ @<Add {\tt scrap} to...@>
+}@}
+
+
+@d Build macro definition
+@{{
+ Name *name = collect_macro_name();
+ int scrap = collect_scrap();
+ @<Add {\tt scrap} to...@>
+}@}
+
+Since a file or macro may be defined by many scraps, we maintain them
+in a simple linked list. The list is actually built in reverse order,
+with each new definition being added to the head of the list.
+@d Add {\tt scrap} to {\tt name}'s definition list
+@{{
+ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ def->scrap = scrap;
+ def->next = name->defs;
+ name->defs = def;
+}@}
+
+
+\subsection{Fixing the Cross References}
+
+Since the definition and reference lists for each name are accumulated
+in reverse order, we take the time at the end of {\tt pass1} to
+reverse them all so they'll be simpler to print out prettily.
+The code for {\tt reverse\_lists} appears in Section~\ref{names}.
+@d Reverse cross-reference lists
+@{{
+ reverse_lists(file_names);
+ reverse_lists(macro_names);
+ reverse_lists(user_names);
+}@}
+
+
+
+\section{Writing the Latex File} \label{latex-file}
+
+The second pass (invoked via a call to {\tt write\_tex}) copies most of
+the text from the source file straight into a {\tt .tex} file.
+Definitions are formatted slightly and cross-reference information is
+printed out.
+
+Note that all the formatting is handled in this section.
+If you don't like the format of definitions or indices or whatever,
+it'll be in this section somewhere. Similarly, if someone wanted to
+modify nuweb to work with a different typesetting system, this would
+be the place to look.
+
+@d Function...
+@{void write_tex();
+@}
+
+\newpage
+\noindent
+We need a few local function declarations before we get into the body
+of {\tt write\_tex}.
+
+@o latex.c
+@{static void copy_scrap(); /* formats the body of a scrap */
+static void print_scrap_numbers(); /* formats a list of scrap numbers */
+static void format_entry(); /* formats an index entry */
+static void format_user_entry();
+@}
+
+The routine {\tt write\_tex} takes two file names as parameters: the
+name of the web source file and the name of the {\tt .tex} output file.
+@o latex.c
+@{void write_tex(file_name, tex_name)
+ char *file_name;
+ char *tex_name;
+{
+ FILE *tex_file = fopen(tex_name, "w");
+ if (tex_file) {
+ source_open(file_name);
+ @<Copy {\tt source\_file} into {\tt tex\_file}@>
+ fclose(tex_file);
+ }
+ else
+ fprintf(stderr, "%s: can't open %s\n", command_name, tex_name);
+}
+@| write_tex @}
+
+We make our second (and final) pass through the source web, this time
+copying characters straight into the {\tt .tex} file. However, we keep
+an eye peeled for {\tt @@}~characters, which signal a command sequence.
+
+@d Copy {\tt source\_file}...
+@{{
+ int scraps = 1;
+ int c = source_get();
+ while (c != EOF) {
+ if (c == '@@')
+ @<Interpret at-sequence@>
+ else {
+ putc(c, tex_file);
+ c = source_get();
+ }
+ }
+}@}
+
+@d Interpret at-sequence
+@{{
+ int big_definition = FALSE;
+ c = source_get();
+ switch (c) {
+ case 'O': big_definition = TRUE;
+ case 'o': @<Write output file definition@>
+ break;
+ case 'D': big_definition = TRUE;
+ case 'd': @<Write macro definition@>
+ break;
+ case 'f': @<Write index of file names@>
+ break;
+ case 'm': @<Write index of macro names@>
+ break;
+ case 'u': @<Write index of user-specified names@>
+ break;
+ case '@@': putc(c, tex_file);
+ default: c = source_get();
+ break;
+ }
+}@}
+
+
+\subsection{Formatting Definitions}
+
+We go through a fair amount of effort to format a file definition.
+I've derived most of the \LaTeX\ commands experimentally; it's quite
+likely that an expert could do a better job. The \LaTeX\ for
+the previous macro definition should look like this (perhaps modulo
+the scrap numbers):
+\begin{verbatim}
+\begin{flushleft}
+\begin{minipage}{\linewidth}
+$\langle$Interpret at-sequence {\footnotesize 35}$\rangle\equiv$
+\vspace{-1.5ex}
+\begin{quote}
+\verb@@{@@\\
+\verb@@ c = source_get();@@\\
+\verb@@ switch (c) {@@\\
+\verb@@ case 'O': big_definition = TRUE;@@\\
+\verb@@ case 'o': @@$\langle$Write output file definition {\footnotesize 36}$\rangle$\verb@@@@\\
+\end{verbatim}
+\vdots
+\begin{verbatim}
+\verb@@ case '@@{\tt @@}\verb@@': putc(c, tex_file);@@\\
+\verb@@ default: c = source_get();@@\\
+\verb@@ break;@@\\
+\verb@@ }@@\\
+\verb@@}@@$\Diamond$
+\end{quote}
+\vspace{-2ex}
+{\footnotesize Macro referenced in scrap 34.}
+\end{minipage}\\[4ex]
+\end{flushleft}
+\end{verbatim}
+The {\em flushleft\/} environment is used to avoid \LaTeX\ warnings
+about underful lines. The {\em minipage\/} environment is used to
+avoid page breaks in the middle of scraps. The {\em verb\/} command
+allows arbitrary characters to be printed (however, note the special
+handling of the {\tt @@} case in the switch statement).
+
+Macro and file definitions are formatted nearly identically.
+I've factored the common parts out into separate scraps.
+
+@d Write output file definition
+@{{
+ Name *name = collect_file_name();
+ @<Begin the scrap environment@>
+ fprintf(tex_file, "\\verb@@\"%s\"@@", name->spelling);
+ fprintf(tex_file, " {\\footnotesize %d }$\\equiv$\n", scraps++);
+ @<Fill in the middle of the scrap environment@>
+ @<Write file defs@>
+ @<Finish the scrap environment@>
+}@}
+
+I don't format a macro name at all specially, figuring the programmer
+might want to use italics or bold face in the midst of the name.
+
+@d Write macro definition
+@{{
+ Name *name = collect_macro_name();
+ @<Begin the scrap environment@>
+ fprintf(tex_file, "$\\langle$%s", name->spelling);
+ fprintf(tex_file, " {\\footnotesize %d}$\\rangle\\equiv$\n", scraps++);
+ @<Fill in the middle of the scrap environment@>
+ @<Write macro defs@>
+ @<Write macro refs@>
+ @<Finish the scrap environment@>
+}@}
+
+
+@d Begin the scrap environment
+@{{
+ fputs("\\begin{flushleft}\n", tex_file);
+ if (!big_definition)
+ fputs("\\begin{minipage}{\\linewidth}\n", tex_file);
+}@}
+
+The interesting things here are the $\Diamond$ inserted at the end of
+each scrap and the various spacing commands. The diamond helps to
+clearly indicate the end of a scrap. The spacing commands were derived
+empirically; they may be adjusted to taste.
+
+@d Fill in the middle of the scrap environment
+@{{
+ fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);
+ copy_scrap(tex_file);
+ fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);
+}@}
+
+\newpage
+\noindent
+We've got one last spacing command, controlling the amount of white
+space after a scrap.
+
+Note also the whitespace eater. I use it to remove any blank lines
+that appear after a scrap in the source file. This way, text following
+a scrap will not be indented. Again, this is a matter of personal taste.
+
+@d Finish the scrap environment
+@{{
+ if (!big_definition)
+ fputs("\\end{minipage}\\\\[4ex]\n", tex_file);
+ fputs("\\end{flushleft}\n", tex_file);
+ do
+ c = source_get();
+ while (isspace(c));
+}@}
+
+
+\subsubsection{Formatting Cross References}
+
+@d Write file defs
+@{{
+ if (name->defs->next) {
+ fputs("{\\footnotesize File defined by scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->defs);
+ fputs("}\n", tex_file);
+ }
+}@}
+
+@d Write macro defs
+@{{
+ fputs("{\\footnotesize ", tex_file);
+ if (name->defs->next) {
+ fputs("Macro defined by scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->defs);
+ fputs("\\\\[-1ex]\n", tex_file);
+ }
+}@}
+
+@d Write macro refs
+@{{
+ if (name->uses) {
+ if (name->uses->next) {
+ fputs("Macro referenced in scraps ", tex_file);
+ print_scrap_numbers(tex_file, name->uses);
+ fputs("}\n", tex_file);
+ }
+ else
+ fprintf(tex_file, "Macro referenced in scrap %d.}\n", name->uses->scrap);
+ }
+ else {
+ fputs("Macro never referenced.}\n", tex_file);
+ fprintf(stderr, "%s: <%s> never referenced.\n",
+ command_name, name->spelling);
+ }
+}@}
+
+
+@o latex.c
+@{static void print_scrap_numbers(tex_file, scraps)
+ FILE *tex_file;
+ Scrap_Node *scraps;
+{
+ fprintf(tex_file, "%d", scraps->scrap);
+ scraps = scraps->next;
+ while (scraps->next) {
+ fprintf(tex_file, ", %d", scraps->scrap);
+ scraps = scraps->next;
+ }
+ fprintf(tex_file, " and~%d.", scraps->scrap);
+}
+@| print_scrap_numbers @}
+
+
+\subsubsection{Formatting a Scrap}
+
+@o latex.c
+@{static void copy_scrap(file)
+ FILE *file;
+{
+ int indent = 0;
+ int c = source_get();
+ fputs("\\verb@@", file);
+ while (1) {
+ switch (c) {
+ case '@@': @<Check at-sequence for end-of-scrap@>
+ break;
+ case '\n': fputs("@@\\\\\n\\verb@@", file);
+ indent = 0;
+ break;
+ case '\t': @<Expand tab into spaces@>
+ break;
+ default: putc(c, file);
+ indent++;
+ break;
+ }
+ c = source_get();
+ }
+}
+@| copy_scrap @}
+
+
+@d Expand tab into spaces
+@{{
+ int delta = 8 - (indent % 8);
+ indent += delta;
+ while (delta > 0) {
+ putc(' ', file);
+ delta--;
+ }
+}@}
+
+@d Check at-sequence...
+@{{
+ c = source_get();
+ switch (c) {
+ case '@@': fputs("@@{\\tt @@}\\verb@@", file);
+ break;
+ case '|': @<Skip over index entries@>
+ case '}': putc('@@', file);
+ return;
+ case '<': @<Format macro name@>
+ break;
+ default: /* ignore these since pass1 will have warned about them */
+ break;
+ }
+}@}
+
+There's no need to check for errors here, since we will have already
+pointed out any during the first pass.
+@d Skip over index entries
+@{{
+ do {
+ do
+ c = source_get();
+ while (c != '@@');
+ c = source_get();
+ } while (c != '}');
+}@}
+
+
+@d Format macro name
+@{{
+ Name *name = collect_scrap_name();
+ fprintf(file, "@@$\\langle$%s {\\footnotesize ", name->spelling);
+ if (name->defs)
+ fprintf(file, "%d", name->defs->scrap);
+ else {
+ putc('?', file);
+ fprintf(stderr, "%s: scrap never defined <%s>\n",
+ command_name, name->spelling);
+ }
+ fputs("}$\\rangle$\\verb@@", file);
+}@}
+
+
+
+\subsection{Generating the Indices}
+
+@d Write index of file names
+@{{
+ if (file_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_entry(file_names, tex_file, TRUE);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+}@}
+
+
+@d Write index of macro names
+@{{
+ if (macro_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_entry(macro_names, tex_file, FALSE);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+}@}
+
+
+@o latex.c
+@{static void format_entry(name, tex_file, file_flag)
+ Name *name;
+ FILE *tex_file;
+ int file_flag;
+{
+ while (name) {
+ format_entry(name->llink, tex_file, file_flag);
+ @<Format an index entry@>
+ name = name->rlink;
+ }
+}
+@| format_entry @}
+
+
+@d Format an index entry
+@{{
+ fputs("\\item \\hspace{-\\leftmargin}", tex_file);
+ if (file_flag) {
+ fprintf(tex_file, "\\verb@@\"%s\"@@ ", name->spelling);
+ @<Write file's defining scrap numbers@>
+ }
+ else {
+ fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);
+ @<Write defining scrap numbers@>
+ fputs("}$\\rangle$ ", tex_file);
+ @<Write referencing scrap numbers@>
+ }
+ putc('\n', tex_file);
+}@}
+
+
+@d Write file's defining scrap numbers
+@{{
+ Scrap_Node *p = name->defs;
+ fputs("{\\footnotesize Defined by scrap", tex_file);
+ if (p->next) {
+ fputs("s ", tex_file);
+ print_scrap_numbers(tex_file, p);
+ }
+ else
+ fprintf(tex_file, " %d.", p->scrap);
+ putc('}', tex_file);
+}@}
+
+@d Write defining scrap numbers
+@{{
+ Scrap_Node *p = name->defs;
+ if (p) {
+ while (1) {
+ fprintf(tex_file, "%d", p->scrap);
+ p = p->next;
+ if (!p) break;
+ fputs(", ", tex_file);
+ }
+ }
+ else
+ putc('?', tex_file);
+}@}
+
+@d Write referencing scrap numbers
+@{{
+ Scrap_Node *p = name->uses;
+ fputs("{\\footnotesize ", tex_file);
+ if (p) {
+ fputs("Referenced in scrap", tex_file);
+ if (p->next) {
+ fputs("s ", tex_file);
+ print_scrap_numbers(tex_file, p);
+ }
+ else
+ fprintf(tex_file, " %d.", p->scrap);
+ }
+ else
+ fputs("Not referenced.", tex_file);
+ putc('}', tex_file);
+}@}
+
+
+@d Write index of user-specified names
+@{{
+ if (user_names) {
+ fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
+ format_user_entry(user_names, tex_file);
+ fputs("\\end{list}", tex_file);
+ }
+ c = source_get();
+}@}
+
+
+@o latex.c
+@{static void format_user_entry(name, tex_file)
+ Name *name;
+ FILE *tex_file;
+{
+ while (name) {
+ format_user_entry(name->llink, tex_file);
+ @<Format a user index entry@>
+ name = name->rlink;
+ }
+}
+@| format_user_entry @}
+
+
+@d Format a user index entry
+@{{
+ Scrap_Node *uses = name->uses;
+ Scrap_Node *defs = name->defs;
+ fprintf(tex_file, "\\item \\hspace{-\\leftmargin}\\verb@@%s@@: ",
+ name->spelling);
+ do {
+ if (uses && (!defs || uses->scrap < defs->scrap)) {
+ fprintf(tex_file, "%d", uses->scrap);
+ uses = uses->next;
+ }
+ else if (defs && (!uses || defs->scrap <= uses->scrap)) {
+ if (uses && defs->scrap == uses->scrap)
+ uses = uses->next;
+ fprintf(tex_file, "\\underline{%d}", defs->scrap);
+ defs = defs->next;
+ }
+ if (uses || defs) fputs(", ", tex_file);
+ } while (uses || defs);
+ fputs(".\n", tex_file);
+}@}
+
+
+
+\section{Writing the Output Files} \label{output-files}
+
+@d Function pro...
+@{void write_files();
+@}
+
+@o output.c
+@{void write_files(files)
+ Name *files;
+{
+ while (files) {
+ write_files(files->llink);
+ @<Write out {\tt files->spelling}@>
+ files = files->rlink;
+ }
+}
+@| write_files @}
+
+Due to the filename restrictions on the PC, just use {\tt nuweb~~~.tmp} as
+the temporary filename. Since files are processed serially, this should
+cause any harm to unix platforms.
+
+@d Write out {\tt files->spelling}
+@{{
+ FILE *temp_file;
+ char temp_name[12]="nuweb~~~.tmp";
+/* sprintf(temp_name, "%s~", files->spelling); */
+ temp_file = fopen(temp_name, "w");
+ if (temp_file) {
+ char indent_chars[500];
+ write_scraps(temp_file, files->defs, 0, indent_chars,
+ files->debug_flag, files->tab_flag, files->indent_flag);
+ fclose(temp_file);
+ if (compare_flag)
+ @<Compare the temp file and the old file@>
+ else
+ rename(temp_name, files->spelling);
+ }
+ else {
+ fprintf(stderr, "%s: can't open %s\n", command_name, temp_name);
+ exit(-1);
+ }
+}@}
+
+
+@d Compare the temp file...
+@{{
+ FILE *old_file = fopen(files->spelling, "r");
+ if (old_file) {
+ int x, y;
+ temp_file = fopen(temp_name, "r");
+ do {
+ x = getc(old_file);
+ y = getc(temp_file);
+ } while (x == y && x != EOF);
+ fclose(old_file);
+ fclose(temp_file);
+ if (x == y)
+ unlink(temp_name);
+ else
+ rename(temp_name, files->spelling);
+ }
+ else
+ rename(temp_name, files->spelling);
+}@}
+
+
+
+\chapter{The Support Routines}
+
+\section{Source Files} \label{source-files}
+
+\subsection{Global Declarations}
+
+We need two routines to handle reading the source files.
+@d Function pro...
+@{void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+@}
+
+
+There are also two global variables maintained for use in error
+messages and such.
+@d Global...
+@{char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+@| source_name source_line @}
+
+\subsection{Local Declarations}
+
+
+@o input.c
+@{static FILE *source_file; /* the current input file */
+static int source_peek;
+static int double_at;
+static int include_depth;
+@| source_peek source_file double_at include_depth @}
+
+
+@o input.c
+@{struct {
+ FILE *file;
+ char *name;
+ int line;
+} stack[10];
+@| stack @}
+
+
+\subsection{Reading a File}
+
+The routine {\tt source\_get} returns the next character from the
+current source file. It notices newlines and keeps the line counter
+{\tt source\_line} up to date. It also catches {\tt EOF} and watches
+for {\tt @@} characters. All other characters are immediately returned.
+@o input.c
+@{int source_get()
+{
+ int c = source_peek;
+ switch (c) {
+ case EOF: @<Handle {\tt EOF}@>
+ return c;
+ case '@@': @<Handle an ``at'' character@>
+ return c;
+ case '\n': source_line++;
+ default: source_peek = getc(source_file);
+ return c;
+ }
+}
+@| source_get @}
+
+
+This whole {\tt @@}~character handling mess is pretty annoying.
+I want to recognize {\tt @@i} so I can handle include files correctly.
+At the same time, it makes sense to recognize illegal {\tt @@}~sequences
+and complain; this avoids ever having to check anywhere else.
+Unfortunately, I need to avoid tripping over the {\tt @@@@}~sequence;
+hence this whole unsatisfactory {\tt double\_at} business.
+@d Handle an ``at''...
+@{{
+ c = getc(source_file);
+ if (double_at) {
+ source_peek = c;
+ double_at = FALSE;
+ c = '@@';
+ }
+ else
+ switch (c) {
+ case 'i': @<Open an include file@>
+ break;
+ case 'f': case 'm': case 'u':
+ case 'd': case 'o': case 'D': case 'O':
+ case '{': case '}': case '<': case '>': case '|':
+ source_peek = c;
+ c = '@@';
+ break;
+ case '@@': source_peek = c;
+ double_at = TRUE;
+ break;
+ default: fprintf(stderr, "%s: bad @@ sequence (%s, line %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+@d Open an include file
+@{{
+ if (include_depth < 10) {
+ char name[100];
+ @<Collect include-file name@>
+ stack[include_depth].name = source_name;
+ stack[include_depth].file = source_file;
+ stack[include_depth].line = source_line + 1;
+ include_depth++;
+ source_line = 1;
+ source_name = save_string(name);
+ source_file = fopen(source_name, "r");
+ if (!source_file) {
+ fprintf(stderr, "%s: can't open include file %s\n",
+ command_name, source_name);
+ exit(-1);
+ }
+ source_peek = getc(source_file);
+ c = source_get();
+ }
+ else {
+ fprintf(stderr, "%s: include nesting too deep (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+@d Collect include-file name
+@{{
+ char *p = name;
+ do
+ c = getc(source_file);
+ while (c == ' ' || c == '\t');
+ while (isgraph(c)) {
+ *p++ = c;
+ c = getc(source_file);
+ }
+ *p = '\0';
+ if (c != '\n') {
+ fprintf(stderr, "%s: unexpected characters after file name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+If an {\tt EOF} is discovered, the current file must be closed and
+input from the next stacked file must be resumed. If no more files are
+on the stack, the {\tt EOF} is returned.
+@d Handle {\tt EOF}
+@{{
+ fclose(source_file);
+ if (include_depth) {
+ include_depth--;
+ source_file = stack[include_depth].file;
+ source_line = stack[include_depth].line;
+ source_name = stack[include_depth].name;
+ source_peek = getc(source_file);
+ c = source_get();
+ }
+}@}
+
+
+\subsection{Opening a File}
+
+The routine {\tt source\_open} takes a file name and tries to open the
+file. If unsuccessful, it complains and halts. Otherwise, it sets
+{\tt source\_name}, {\tt source\_line}, and {\tt double\_at}.
+@o input.c
+@{void source_open(name)
+ char *name;
+{
+ source_file = fopen(name, "r");
+ if (!source_file) {
+ fprintf(stderr, "%s: couldn't open %s\n", command_name, name);
+ exit(-1);
+ }
+ source_name = name;
+ source_line = 1;
+ source_peek = getc(source_file);
+ double_at = FALSE;
+ include_depth = 0;
+}
+@| source_open @}
+
+
+
+
+\section{Scraps} \label{scraps}
+
+
+@o scraps.c
+@{#define SLAB_SIZE 500
+
+typedef struct slab {
+ struct slab *next;
+ char chars[SLAB_SIZE];
+} Slab;
+@| Slab SLAB_SIZE @}
+
+
+@o scraps.c
+@{typedef struct {
+ char *file_name;
+ int file_line;
+ Slab *slab;
+} ScrapEntry;
+@| ScrapEntry @}
+
+@o scraps.c
+@{ScrapEntry *SCRAP[256];
+
+#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255]
+
+int scraps;
+@| scraps scrap_array SCRAP @}
+
+
+@d Function pro...
+@{void init_scraps();
+int collect_scrap();
+int write_scraps();
+@}
+
+
+@o scraps.c
+@{void init_scraps()
+{
+ scraps = 1;
+ SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));
+}
+@| init_scraps @}
+
+
+@o scraps.c
+@{typedef struct {
+ Slab *scrap;
+ int index;
+} Manager;
+@| Manager @}
+
+
+
+@o scraps.c
+@{static void push(c, manager)
+ char c;
+ Manager *manager;
+{
+ Slab *scrap = manager->scrap;
+ int index = manager->index;
+ scrap->chars[index++] = c;
+ if (index == SLAB_SIZE) {
+ Slab *new = (Slab *) arena_getmem(sizeof(Slab));
+ scrap->next = new;
+ manager->scrap = new;
+ index = 0;
+ }
+ manager->index = index;
+}
+@| push @}
+
+@o scraps.c
+@{static void pushs(s, manager)
+ char *s;
+ Manager *manager;
+{
+ while (*s)
+ push(*s++, manager);
+}
+@| pushs @}
+
+
+@o scraps.c
+@{int collect_scrap()
+{
+ Manager writer;
+ @<Create new scrap...@>
+ @<Accumulate scrap and return {\tt scraps++}@>
+}
+@| collect_scrap @}
+
+@d Create new scrap, managed by {\tt writer}
+@{{
+ Slab *scrap = (Slab *) arena_getmem(sizeof(Slab));
+ if ((scraps & 255) == 0)
+ SCRAP[scraps >> 8] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));
+ scrap_array(scraps).slab = scrap;
+ scrap_array(scraps).file_name = save_string(source_name);
+ scrap_array(scraps).file_line = source_line;
+ writer.scrap = scrap;
+ writer.index = 0;
+}@}
+
+
+@d Accumulate scrap...
+@{{
+ int c = source_get();
+ while (1) {
+ switch (c) {
+ case EOF: fprintf(stderr, "%s: unexpect EOF in scrap (%s, %d)\n",
+ command_name, scrap_array(scraps).file_name,
+ scrap_array(scraps).file_line);
+ exit(-1);
+ case '@@': @<Handle at-sign during scrap accumulation@>
+ break;
+ default: push(c, &writer);
+ c = source_get();
+ break;
+ }
+ }
+}@}
+
+
+@d Handle at-sign during scrap accumulation
+@{{
+ c = source_get();
+ switch (c) {
+ case '@@': pushs("@@@@", &writer);
+ c = source_get();
+ break;
+ case '|': @<Collect user-specified index entries@>
+ case '}': push('\0', &writer);
+ return scraps++;
+ case '<': @<Handle macro invocation in scrap@>
+ break;
+ default : fprintf(stderr, "%s: unexpected @@%c in scrap (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+
+@d Collect user-specified index entries
+@{{
+ do {
+ char new_name[100];
+ char *p = new_name;
+ do
+ c = source_get();
+ while (isspace(c));
+ if (c != '@@') {
+ Name *name;
+ do {
+ *p++ = c;
+ c = source_get();
+ } while (c != '@@' && !isspace(c));
+ *p = '\0';
+ name = name_add(&user_names, new_name);
+ if (!name->defs || name->defs->scrap != scraps) {
+ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ def->scrap = scraps;
+ def->next = name->defs;
+ name->defs = def;
+ }
+ }
+ } while (c != '@@');
+ c = source_get();
+ if (c != '}') {
+ fprintf(stderr, "%s: unexpected @@%c in scrap (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+
+@d Handle macro invocation in scrap
+@{{
+ Name *name = collect_scrap_name();
+ @<Save macro name@>
+ @<Add current scrap to {\tt name}'s uses@>
+ c = source_get();
+}@}
+
+
+@d Save macro name
+@{{
+ char *s = name->spelling;
+ int len = strlen(s) - 1;
+ pushs("@@<", &writer);
+ while (len > 0) {
+ push(*s++, &writer);
+ len--;
+ }
+ if (*s == ' ')
+ pushs("...", &writer);
+ else
+ push(*s, &writer);
+ pushs("@@>", &writer);
+}@}
+
+
+@d Add current scrap to...
+@{{
+ if (!name->uses || name->uses->scrap != scraps) {
+ Scrap_Node *use = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ use->scrap = scraps;
+ use->next = name->uses;
+ name->uses = use;
+ }
+}@}
+
+
+@o scraps.c
+@{static char pop(manager)
+ Manager *manager;
+{
+ Slab *scrap = manager->scrap;
+ int index = manager->index;
+ char c = scrap->chars[index++];
+ if (index == SLAB_SIZE) {
+ manager->scrap = scrap->next;
+ index = 0;
+ }
+ manager->index = index;
+ return c;
+}
+@| pop @}
+
+
+
+@o scraps.c
+@{static Name *pop_scrap_name(manager)
+ Manager *manager;
+{
+ char name[100];
+ char *p = name;
+ int c = pop(manager);
+ while (TRUE) {
+ if (c == '@@')
+ @<Check for end of scrap name and return@>
+ else {
+ *p++ = c;
+ c = pop(manager);
+ }
+ }
+}
+@| pop_scrap_name @}
+
+
+@d Check for end of scrap name...
+@{{
+ c = pop(manager);
+ if (c == '@@') {
+ *p++ = c;
+ c = pop(manager);
+ }
+ else if (c == '>') {
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+ }
+ else {
+ fprintf(stderr, "%s: found an internal problem (1)\n", command_name);
+ exit(-1);
+ }
+}@}
+
+
+@o scraps.c
+@{int write_scraps(file, defs, global_indent, indent_chars,
+ debug_flag, tab_flag, indent_flag)
+ FILE *file;
+ Scrap_Node *defs;
+ int global_indent;
+ char *indent_chars;
+ char debug_flag;
+ char tab_flag;
+ char indent_flag;
+{
+ int indent = 0;
+ while (defs) {
+ @<Copy {\tt defs->scrap} to {\tt file}@>
+ defs = defs->next;
+ }
+ return indent + global_indent;
+}
+@| write_scraps @}
+
+
+@d Copy {\tt defs->scrap}...
+@{{
+ char c;
+ Manager reader;
+ int line_number = scrap_array(defs->scrap).file_line;
+ @<Insert debugging information if required@>
+ reader.scrap = scrap_array(defs->scrap).slab;
+ reader.index = 0;
+ c = pop(&reader);
+ while (c) {
+ switch (c) {
+ case '@@': @<Check for macro invocation in scrap@>
+ break;
+ case '\n': putc(c, file);
+ line_number++;
+ @<Insert appropriate indentation@>
+ break;
+ case '\t': @<Handle tab...@>
+ break;
+ default: putc(c, file);
+ indent_chars[global_indent + indent] = ' ';
+ indent++;
+ break;
+ }
+ c = pop(&reader);
+ }
+}@}
+
+
+@d Insert debugging information if required
+@{if (debug_flag) {
+ fprintf(file, "\n#line %d \"%s\"\n",
+ line_number, scrap_array(defs->scrap).file_name);
+ @<Insert appropr...@>
+}@}
+
+
+@d Insert approp...
+@{{
+ if (indent_flag) {
+ if (tab_flag)
+ for (indent=0; indent<global_indent; indent++)
+ putc(' ', file);
+ else
+ for (indent=0; indent<global_indent; indent++)
+ putc(indent_chars[indent], file);
+ }
+ indent = 0;
+}@}
+
+
+@d Handle tab characters on output
+@{{
+ if (tab_flag)
+ @<Expand tab...@>
+ else {
+ putc('\t', file);
+ indent_chars[global_indent + indent] = '\t';
+ indent++;
+ }
+}@}
+
+
+
+@d Check for macro invocation...
+@{{
+ c = pop(&reader);
+ switch (c) {
+ case '@@': putc(c, file);
+ indent_chars[global_indent + indent] = ' ';
+ indent++;
+ break;
+ case '<': @<Copy macro into {\tt file}@>
+ @<Insert debugging information if required@>
+ break;
+ default: /* ignore, since we should already have a warning */
+ break;
+ }
+}@}
+
+
+@d Copy macro into...
+@{{
+ Name *name = pop_scrap_name(&reader);
+ if (name->mark) {
+ fprintf(stderr, "%s: recursive macro discovered involving <%s>\n",
+ command_name, name->spelling);
+ exit(-1);
+ }
+ if (name->defs) {
+ name->mark = TRUE;
+ indent = write_scraps(file, name->defs, global_indent + indent,
+ indent_chars, debug_flag, tab_flag, indent_flag);
+ indent -= global_indent;
+ name->mark = FALSE;
+ }
+ else if (!tex_flag)
+ fprintf(stderr, "%s: macro never defined <%s>\n",
+ command_name, name->spelling);
+}@}
+
+
+\section{Names} \label{names}
+
+@d Type de...
+@{typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+@| Scrap_Node @}
+
+
+@d Type de...
+@{typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+@| Name @}
+
+@d Global...
+@{Name *file_names;
+Name *macro_names;
+Name *user_names;
+@| file_names macro_names user_names @}
+
+@d Function pro...
+@{Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+@}
+
+@o names.c
+@{enum { LESS, GREATER, EQUAL, PREFIX, EXTENSION };
+
+static int compare(x, y)
+ char *x;
+ char *y;
+{
+ int len, result;
+ int xl = strlen(x);
+ int yl = strlen(y);
+ int xp = x[xl - 1] == ' ';
+ int yp = y[yl - 1] == ' ';
+ if (xp) xl--;
+ if (yp) yl--;
+ len = xl < yl ? xl : yl;
+ result = strncmp(x, y, len);
+ if (result < 0) return GREATER;
+ else if (result > 0) return LESS;
+ else if (xl < yl) {
+ if (xp) return EXTENSION;
+ else return LESS;
+ }
+ else if (xl > yl) {
+ if (yp) return PREFIX;
+ else return GREATER;
+ }
+ else return EQUAL;
+}
+@| compare LESS GREATER EQUAL PREFIX EXTENSION @}
+
+
+@o names.c
+@{char *save_string(s)
+ char *s;
+{
+ char *new = (char *) arena_getmem((strlen(s) + 1) * sizeof(char));
+ strcpy(new, s);
+ return new;
+}
+@| save_string @}
+
+@o names.c
+@{Name *prefix_add(root, spelling)
+ Name **root;
+ char *spelling;
+{
+ Name *node = *root;
+ while (node) {
+ switch (compare(node->spelling, spelling)) {
+ case GREATER: root = &node->rlink;
+ break;
+ case LESS: root = &node->llink;
+ break;
+ case EQUAL: return node;
+ case EXTENSION: node->spelling = save_string(spelling);
+ return node;
+ case PREFIX: @<Check for ambiguous prefix@>
+ return node;
+ }
+ node = *root;
+ }
+ @<Create new name entry@>
+}
+@| prefix_add @}
+
+Since a very short prefix might match more than one macro name, I need
+to check for other matches to avoid mistakes. Basically, I simply
+continue the search down {\em both\/} branches of the tree.
+
+@d Check for ambiguous prefix
+@{{
+ if (ambiguous_prefix(node->llink, spelling) ||
+ ambiguous_prefix(node->rlink, spelling))
+ fprintf(stderr,
+ "%s: ambiguous prefix @@<%s...@@> (%s, line %d)\n",
+ command_name, spelling, source_name, source_line);
+}@}
+
+@o names.c
+@{static int ambiguous_prefix(node, spelling)
+ Name *node;
+ char *spelling;
+{
+ while (node) {
+ switch (compare(node->spelling, spelling)) {
+ case GREATER: node = node->rlink;
+ break;
+ case LESS: node = node->llink;
+ break;
+ case EQUAL:
+ case EXTENSION:
+ case PREFIX: return TRUE;
+ }
+ }
+ return FALSE;
+}
+@}
+
+Rob Shillingsburg suggested that I organize the index of
+user-specified identifiers more traditionally; that is, not relying on
+strict {\small ASCII} comparisons via {\tt strcmp}. Ideally, we'd like
+to see the index ordered like this:
+\begin{quote}
+\begin{flushleft}
+aardvark \\
+Adam \\
+atom \\
+Atomic \\
+atoms
+\end{flushleft}
+\end{quote}
+The function {\tt robs\_strcmp} implements the desired predicate.
+
+@o names.c
+@{static int robs_strcmp(x, y)
+ char *x;
+ char *y;
+{
+ char *xx = x;
+ char *yy = y;
+ int xc = toupper(*xx);
+ int yc = toupper(*yy);
+ while (xc == yc && xc) {
+ xx++;
+ yy++;
+ xc = toupper(*xx);
+ yc = toupper(*yy);
+ }
+ if (xc != yc) return xc - yc;
+ xc = *x;
+ yc = *y;
+ while (xc == yc && xc) {
+ x++;
+ y++;
+ xc = *x;
+ yc = *y;
+ }
+ if (isupper(xc) && islower(yc))
+ return xc * 2 - (toupper(yc) * 2 + 1);
+ if (islower(xc) && isupper(yc))
+ return toupper(xc) * 2 + 1 - yc * 2;
+ return xc - yc;
+}
+@| robs_strcmp @}
+
+@o names.c
+@{Name *name_add(root, spelling)
+ Name **root;
+ char *spelling;
+{
+ Name *node = *root;
+ while (node) {
+ int result = robs_strcmp(node->spelling, spelling);
+ if (result > 0)
+ root = &node->llink;
+ else if (result < 0)
+ root = &node->rlink;
+ else
+ return node;
+ node = *root;
+ }
+ @<Create new name entry@>
+}
+@| name_add @}
+
+
+@d Create new name...
+@{{
+ node = (Name *) arena_getmem(sizeof(Name));
+ node->spelling = save_string(spelling);
+ node->mark = FALSE;
+ node->llink = NULL;
+ node->rlink = NULL;
+ node->uses = NULL;
+ node->defs = NULL;
+ node->tab_flag = TRUE;
+ node->indent_flag = TRUE;
+ node->debug_flag = FALSE;
+ *root = node;
+ return node;
+}@}
+
+
+Name terminated by whitespace. Also check for ``per-file'' flags. Keep
+skipping white space until we reach scrap.
+@o names.c
+@{Name *collect_file_name()
+{
+ Name *new_name;
+ char name[100];
+ char *p = name;
+ int start_line = source_line;
+ int c = source_get();
+ while (isspace(c))
+ c = source_get();
+ while (isgraph(c)) {
+ *p++ = c;
+ c = source_get();
+ }
+ if (p == name) {
+ fprintf(stderr, "%s: expected file name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+ *p = '\0';
+ new_name = name_add(&file_names, name);
+ @<Handle optional per-file flags@>
+ if (c == '@@' && source_get() == '{')
+ return new_name;
+ else {
+ fprintf(stderr, "%s: expected @@{ after file name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+}
+@| collect_file_name @}
+
+@d Handle optional per-file flags
+@{{
+ while (1) {
+ while (isspace(c))
+ c = source_get();
+ if (c == '-') {
+ c = source_get();
+ do {
+ switch (c) {
+ case 't': new_name->tab_flag = FALSE;
+ break;
+ case 'd': new_name->debug_flag = TRUE;
+ break;
+ case 'i': new_name->indent_flag = FALSE;
+ break;
+ default : fprintf(stderr, "%s: unexpected per-file flag (%s, %d)\n",
+ command_name, source_name, source_line);
+ break;
+ }
+ c = source_get();
+ } while (!isspace(c));
+ }
+ else break;
+ }
+}@}
+
+
+
+Name terminated by \verb+\n+ or \verb+@@{+; but keep skipping until \verb+@@{+
+@o names.c
+@{Name *collect_macro_name()
+{
+ char name[100];
+ char *p = name;
+ int start_line = source_line;
+ int c = source_get();
+ while (isspace(c))
+ c = source_get();
+ while (c != EOF) {
+ switch (c) {
+ case '@@': @<Check for terminating at-sequence and return name@>
+ break;
+ case '\t':
+ case ' ': *p++ = ' ';
+ do
+ c = source_get();
+ while (c == ' ' || c == '\t');
+ break;
+ case '\n': @<Skip until scrap begins, then return name@>
+ default: *p++ = c;
+ c = source_get();
+ break;
+ }
+ }
+ fprintf(stderr, "%s: expected macro name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+}
+@| collect_macro_name @}
+
+
+@d Check for termina...
+@{{
+ c = source_get();
+ switch (c) {
+ case '@@': *p++ = c;
+ break;
+ case '{': @<Cleanup and install name@>
+ default: fprintf(stderr,
+ "%s: unexpected @@%c in macro name (%s, %d)\n",
+ command_name, c, source_name, start_line);
+ exit(-1);
+ }
+}@}
+
+
+@d Cleanup and install name
+@{{
+ if (p > name && p[-1] == ' ')
+ p--;
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ if (p == name || name[0] == ' ') {
+ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+}@}
+
+
+@d Skip until scrap...
+@{{
+ do
+ c = source_get();
+ while (isspace(c));
+ if (c == '@@' && source_get() == '{')
+ @<Cleanup and install name@>
+ else {
+ fprintf(stderr, "%s: expected @@{ after macro name (%s, %d)\n",
+ command_name, source_name, start_line);
+ exit(-1);
+ }
+}@}
+
+
+Terminated by \verb+@@>+
+@o names.c
+@{Name *collect_scrap_name()
+{
+ char name[100];
+ char *p = name;
+ int c = source_get();
+ while (c == ' ' || c == '\t')
+ c = source_get();
+ while (c != EOF) {
+ switch (c) {
+ case '@@': @<Look for end of scrap name and return@>
+ break;
+ case '\t':
+ case ' ': *p++ = ' ';
+ do
+ c = source_get();
+ while (c == ' ' || c == '\t');
+ break;
+ default: if (isgraph(c)) {
+ *p++ = c;
+ c = source_get();
+ }
+ else {
+ fprintf(stderr,
+ "%s: unexpected character in macro name (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+ }
+ break;
+ }
+ }
+ fprintf(stderr, "%s: unexpected end of file (%s, %d)\n",
+ command_name, source_name, source_line);
+ exit(-1);
+}
+@| collect_scrap_name @}
+
+
+@d Look for end of scrap name...
+@{{
+ c = source_get();
+ switch (c) {
+ case '@@': *p++ = c;
+ c = source_get();
+ break;
+ case '>': @<Cleanup and install name@>
+ default: fprintf(stderr,
+ "%s: unexpected @@%c in macro name (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+}@}
+
+
+@o names.c
+@{static Scrap_Node *reverse(); /* a forward declaration */
+
+void reverse_lists(names)
+ Name *names;
+{
+ while (names) {
+ reverse_lists(names->llink);
+ names->defs = reverse(names->defs);
+ names->uses = reverse(names->uses);
+ names = names->rlink;
+ }
+}
+@| reverse_lists @}
+
+Just for fun, here's a non-recursive version of the traditional list
+reversal code. Note that it reverses the list in place; that is, it
+does no new allocations.
+@o names.c
+@{static Scrap_Node *reverse(a)
+ Scrap_Node *a;
+{
+ if (a) {
+ Scrap_Node *b = a->next;
+ a->next = NULL;
+ while (b) {
+ Scrap_Node *c = b->next;
+ b->next = a;
+ a = b;
+ b = c;
+ }
+ }
+ return a;
+}
+@| reverse @}
+
+
+\section{Searching for Index Entries} \label{search}
+
+Given the array of scraps and a set of index entries, we need to
+search all the scraps for occurences of each entry. The obvious
+approach to this problem would be quite expensive for large documents;
+however, there is an interesting paper describing an efficient
+solution~\cite{aho:75}.
+
+
+@o scraps.c
+@{
+typedef struct name_node {
+ struct name_node *next;
+ Name *name;
+} Name_Node;
+
+typedef struct goto_node {
+ struct goto_node *next; /* next goto node with same depth */
+ struct goto_node *fail;
+ struct move_node *moves;
+ Name_Node *output;
+} Goto_Node;
+
+typedef struct move_node {
+ struct move_node *next;
+ Goto_Node *state;
+ char c;
+} Move_Node;
+@| Goto_Node Move_Node Name_Node @}
+
+@o scraps.c
+@{
+static Goto_Node *root[128];
+static int max_depth;
+static Goto_Node **depths;
+@| root max_depth @}
+
+
+@o scraps.c
+@{static Goto_Node *goto_lookup(c, g)
+ char c;
+ Goto_Node *g;
+{
+ Move_Node *m = g->moves;
+ while (m && m->c != c)
+ m = m->next;
+ if (m)
+ return m->state;
+ else
+ return NULL;
+}
+@| goto_lookup @}
+
+
+\subsection{Building the Automata}
+
+
+@o scraps.c
+@{
+static void build_gotos();
+
+void search()
+{
+ int i;
+ for (i=0; i<128; i++)
+ root[i] = NULL;
+ max_depth = 10;
+ depths = (Goto_Node **) arena_getmem(max_depth * sizeof(Goto_Node *));
+ for (i=0; i<max_depth; i++)
+ depths[i] = NULL;
+ build_gotos(user_names);
+ @<Build failure functions@>
+ @<Search scraps@>
+}
+@| search @}
+
+
+
+@o scraps.c
+@{static void build_gotos(tree)
+ Name *tree;
+{
+ while (tree) {
+ @<Extend goto graph with {\tt tree->spelling}@>
+ build_gotos(tree->rlink);
+ tree = tree->llink;
+ }
+}
+@| build_gotos @}
+
+@d Extend goto...
+@{{
+ int depth = 2;
+ char *p = tree->spelling;
+ char c = *p++;
+ Goto_Node *q = root[c];
+ if (!q) {
+ q = (Goto_Node *) arena_getmem(sizeof(Goto_Node));
+ root[c] = q;
+ q->moves = NULL;
+ q->fail = NULL;
+ q->moves = NULL;
+ q->output = NULL;
+ q->next = depths[1];
+ depths[1] = q;
+ }
+ while (c = *p++) {
+ Goto_Node *new = goto_lookup(c, q);
+ if (!new) {
+ Move_Node *new_move = (Move_Node *) arena_getmem(sizeof(Move_Node));
+ new = (Goto_Node *) arena_getmem(sizeof(Goto_Node));
+ new->moves = NULL;
+ new->fail = NULL;
+ new->moves = NULL;
+ new->output = NULL;
+ new_move->state = new;
+ new_move->c = c;
+ new_move->next = q->moves;
+ q->moves = new_move;
+ if (depth == max_depth) {
+ int i;
+ Goto_Node **new_depths =
+ (Goto_Node **) arena_getmem(2*depth*sizeof(Goto_Node *));
+ max_depth = 2 * depth;
+ for (i=0; i<depth; i++)
+ new_depths[i] = depths[i];
+ depths = new_depths;
+ for (i=depth; i<max_depth; i++)
+ depths[i] = NULL;
+ }
+ new->next = depths[depth];
+ depths[depth] = new;
+ }
+ q = new;
+ depth++;
+ }
+ q->output = (Name_Node *) arena_getmem(sizeof(Name_Node));
+ q->output->next = NULL;
+ q->output->name = tree;
+}@}
+
+
+@d Build failure functions
+@{{
+ int depth;
+ for (depth=1; depth<max_depth; depth++) {
+ Goto_Node *r = depths[depth];
+ while (r) {
+ Move_Node *m = r->moves;
+ while (m) {
+ char a = m->c;
+ Goto_Node *s = m->state;
+ Goto_Node *state = r->fail;
+ while (state && !goto_lookup(a, state))
+ state = state->fail;
+ if (state)
+ s->fail = goto_lookup(a, state);
+ else
+ s->fail = root[a];
+ if (s->fail) {
+ Name_Node *p = s->fail->output;
+ while (p) {
+ Name_Node *q = (Name_Node *) arena_getmem(sizeof(Name_Node));
+ q->name = p->name;
+ q->next = s->output;
+ s->output = q;
+ p = p->next;
+ }
+ }
+ m = m->next;
+ }
+ r = r->next;
+ }
+ }
+}@}
+
+
+\subsection{Searching the Scraps}
+
+@d Search scraps
+@{{
+ for (i=1; i<scraps; i++) {
+ char c;
+ Manager reader;
+ Goto_Node *state = NULL;
+ reader.scrap = scrap_array(i).slab;
+ reader.index = 0;
+ c = pop(&reader);
+ while (c) {
+ while (state && !goto_lookup(c, state))
+ state = state->fail;
+ if (state)
+ state = goto_lookup(c, state);
+ else
+ state = root[c];
+ if (state && state->output) {
+ Name_Node *p = state->output;
+ do {
+ Name *name = p->name;
+ if (!name->uses || name->uses->scrap != i) {
+ Scrap_Node *new_use =
+ (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ new_use->scrap = i;
+ new_use->next = name->uses;
+ name->uses = new_use;
+ }
+ p = p->next;
+ } while (p);
+ }
+ c = pop(&reader);
+ }
+ }
+}@}
+
+
+\section{Memory Management} \label{memory-management}
+
+I manage memory using a simple scheme inspired by Hanson's idea of
+{\em arenas\/}~\cite{hanson:90}.
+Basically, I allocate all the storage required when processing a
+source file (primarily for names and scraps) using calls to
+{\tt arena\_getmem(n)}, where {\tt n} specifies the number of bytes to
+be allocated. When the storage is no longer required, the entire arena
+is freed with a single call to {\tt arena\_free()}. Both operations
+are quite fast.
+@d Function p...
+@{void *arena_getmem();
+void arena_free();
+@}
+
+
+@o arena.c
+@{typedef struct chunk {
+ struct chunk *next;
+ char *limit;
+ char *avail;
+} Chunk;
+@| Chunk @}
+
+
+We define an empty chunk called {\tt first}. The variable {\tt arena} points
+at the current chunk of memory; it's initially pointed at {\tt first}.
+As soon as some storage is required, a ``real'' chunk of memory will
+be allocated and attached to {\tt first->next}; storage will be
+allocated from the new chunk (and later chunks if necessary).
+@o arena.c
+@{Chunk first = { NULL, NULL, NULL };
+Chunk *arena = &first;
+@| first arena @}
+
+
+\subsection{Allocating Memory}
+
+The routine {\tt arena\_getmem(n)} returns a pointer to (at least)
+{\tt n} bytes of memory. Note that {\tt n} is rounded up to ensure
+that returned pointers are always aligned.
+@o arena.c
+@{void *arena_getmem(n)
+ int n;
+{
+ char *q;
+ char *p = arena->avail;
+ n = (n + 3) & ~3; /* ensuring alignment to 4 bytes */
+ q = p + n;
+ if (q <= arena->limit) {
+ arena->avail = q;
+ return p;
+ }
+ @<Find a new chunk of memory@>
+}
+@| arena_getmem @}
+
+
+If the current chunk doesn't have adequate space (at least {\tt n}
+bytes) we examine the rest of the list of chunks (starting at
+{\tt arena->next}) looking for a chunk with adequate space. If {\tt n}
+is very large, we may not find it right away or we may not find a
+suitable chunk at all.
+@d Find a new chunk...
+@{{
+ Chunk *ap = arena;
+ Chunk *np = ap->next;
+ while (np) {
+ char *v = sizeof(Chunk) + (char *) np;
+ if (v + n <= np->limit) {
+ np->avail = v + n;
+ arena = np;
+ return v;
+ }
+ ap = np;
+ np = ap->next;
+ }
+ @<Allocate a new chunk of memory@>
+}@}
+
+
+If there isn't a suitable chunk of memory on the free list, then we
+need to allocate a new one.
+@d Allocate a new ch...
+@{{
+ int m = n + 10000;
+ np = (Chunk *) malloc(m);
+ np->limit = m + (char *) np;
+ np->avail = n + sizeof(Chunk) + (char *) np;
+ np->next = NULL;
+ ap->next = np;
+ arena = np;
+ return sizeof(Chunk) + (char *) np;
+}@}
+
+
+\subsection{Freeing Memory}
+
+To free all the memory in the arena, we need only point {\tt arena}
+back to the first empty chunk.
+@o arena.c
+@{void arena_free()
+{
+ arena = &first;
+}
+@| arena_free @}
+
+
+\chapter{Indices} \label{indices}
+
+Three sets of indices can be created automatically: an index of file
+names, an index of macro names, and an index of user-specified
+identifiers. An index entry includes the name of the entry, where it
+was defined, and where it was referenced.
+
+
+
+\section{Files}
+
+@f
+
+\section{Macros}
+
+@m
+
+\section{Identifiers}
+
+Knuth prints his index of indentifiers in a two-column format.
+I could force this automatically by emitting the \verb|\twocolumn|
+command; but this has the side effect of forcing a new page.
+Therefore, it seems better to leave it this up to the user.
+
+@u
+
+\bibliographystyle{plain}
+\bibliography{literate}
+
+\end{document}
diff --git a/web/nuweb/msdos/output.c b/web/nuweb/msdos/output.c
new file mode 100644
index 0000000000..150722ca71
--- /dev/null
+++ b/web/nuweb/msdos/output.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+void write_files(files)
+ Name *files;
+{
+ while (files) {
+ write_files(files->llink);
+ {
+ FILE *temp_file;
+ char temp_name[12]="nuweb~~~.tmp";
+ /* sprintf(temp_name, "%s~", files->spelling); */
+ temp_file = fopen(temp_name, "w");
+ if (temp_file) {
+ char indent_chars[500];
+ write_scraps(temp_file, files->defs, 0, indent_chars,
+ files->debug_flag, files->tab_flag, files->indent_flag);
+ fclose(temp_file);
+ if (compare_flag)
+ {
+ FILE *old_file = fopen(files->spelling, "r");
+ if (old_file) {
+ int x, y;
+ temp_file = fopen(temp_name, "r");
+ do {
+ x = getc(old_file);
+ y = getc(temp_file);
+ } while (x == y && x != EOF);
+ fclose(old_file);
+ fclose(temp_file);
+ if (x == y)
+ unlink(temp_name);
+ else
+ rename(temp_name, files->spelling);
+ }
+ else
+ rename(temp_name, files->spelling);
+ }
+ else
+ rename(temp_name, files->spelling);
+ }
+ else {
+ fprintf(stderr, "%s: can't open %s\n", command_name, temp_name);
+ exit(-1);
+ }
+ }
+ files = files->rlink;
+ }
+}
diff --git a/web/nuweb/msdos/pass1.c b/web/nuweb/msdos/pass1.c
new file mode 100644
index 0000000000..1c8edcf9c4
--- /dev/null
+++ b/web/nuweb/msdos/pass1.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+void pass1(file_name)
+ char *file_name;
+{
+ source_open(file_name);
+ init_scraps();
+ macro_names = NULL;
+ file_names = NULL;
+ user_names = NULL;
+ {
+ int c = source_get();
+ while (c != EOF) {
+ if (c == '@')
+ {
+ c = source_get();
+ switch (c) {
+ case 'O':
+ case 'o': {
+ Name *name = collect_file_name(); /* returns a pointer to the name entry */
+ int scrap = collect_scrap(); /* returns an index to the scrap */
+ {
+ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ def->scrap = scrap;
+ def->next = name->defs;
+ name->defs = def;
+ }
+ }
+ break;
+ case 'D':
+ case 'd': {
+ Name *name = collect_macro_name();
+ int scrap = collect_scrap();
+ {
+ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ def->scrap = scrap;
+ def->next = name->defs;
+ name->defs = def;
+ }
+ }
+ break;
+ case '@':
+ case 'u':
+ case 'm':
+ case 'f': /* ignore during this pass */
+ break;
+ default: fprintf(stderr,
+ "%s: unexpected @ sequence ignored (%s, line %d)\n",
+ command_name, source_name, source_line);
+ break;
+ }
+ }
+ c = source_get();
+ }
+ }
+ if (tex_flag)
+ search();
+ {
+ reverse_lists(file_names);
+ reverse_lists(macro_names);
+ reverse_lists(user_names);
+ }
+}
diff --git a/web/nuweb/msdos/readme b/web/nuweb/msdos/readme
new file mode 100644
index 0000000000..605ae53c90
--- /dev/null
+++ b/web/nuweb/msdos/readme
@@ -0,0 +1,28 @@
+Here's an intermediate version of nuweb.
+I've simply bundled up the web source (nuweb.w),
+the generated .c files, the generated nuweb.tex file,
+and the assorted auxiliary files for latex'ing.
+
+The "user's manual" and sales pitch is in Chapter 1 of the
+web. If you latex up the nuweb.tex and print pages 0-6
+(or the whole thing), you can read about it.
+
+To actually build the thing, first
+
+ touch nuweb.tex
+
+Then
+
+ make
+
+should build an executable file called "nuweb".
+After that, you should be able to use the makefile
+to control everything very nicely.
+
+If you have questions or comments
+(or just want the latest), please let me know.
+I'm going to continue working on the documentation for
+at least a week or two. Eventually, I'll put it out
+for ftp access.
+
+My e-mail address is preston@cs.rice.edu
diff --git a/web/nuweb/msdos/readme.txt b/web/nuweb/msdos/readme.txt
new file mode 100644
index 0000000000..21916808cf
--- /dev/null
+++ b/web/nuweb/msdos/readme.txt
@@ -0,0 +1,14 @@
+This is a direct installation of Preston Briggs' Nuweb Program, Version
+0.6 for PC. It is compiled with the DJGPP GCC Compiler and combined
+with the GO32 extender. Up to now I recognized only the following
+restrictions:
+ - Due to the GO32 extender it will not run under windows. A new go32
+ is in the making with DMI support (will run in windows dos box).
+ - The program only will run on 386/486 PCs
+ - The output filenames specified with the @o command should conform
+ with the DOS filename restrictions.
+ - I made a small change to the scrap 62 to fix temporary file name
+ foulup in PCs. Now any regular dos filename should work.
+ - Also included nuweb.1 man page. On the pc use either groff or
+ cawf to process it.
+Osman Buyukisik
diff --git a/web/nuweb/msdos/scraps.c b/web/nuweb/msdos/scraps.c
new file mode 100644
index 0000000000..ecc35ea856
--- /dev/null
+++ b/web/nuweb/msdos/scraps.c
@@ -0,0 +1,542 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+typedef struct scrap_node {
+ struct scrap_node *next;
+ int scrap;
+} Scrap_Node;
+typedef struct name {
+ char *spelling;
+ struct name *llink;
+ struct name *rlink;
+ Scrap_Node *defs;
+ Scrap_Node *uses;
+ int mark;
+ char tab_flag;
+ char indent_flag;
+ char debug_flag;
+} Name;
+
+int tex_flag; /* if FALSE, don't emit the .tex file */
+int output_flag; /* if FALSE, don't emit the output files */
+int compare_flag; /* if FALSE, overwrite without comparison */
+char *command_name;
+char *source_name; /* name of the current file */
+int source_line; /* current line in the source file */
+Name *file_names;
+Name *macro_names;
+Name *user_names;
+
+void pass1();
+void write_tex();
+void write_files();
+void source_open(); /* pass in the name of the source file */
+int source_get(); /* no args; returns the next char or EOF */
+void init_scraps();
+int collect_scrap();
+int write_scraps();
+Name *collect_file_name();
+Name *collect_macro_name();
+Name *collect_scrap_name();
+Name *name_add();
+Name *prefix_add();
+char *save_string();
+void reverse_lists();
+void *arena_getmem();
+void arena_free();
+
+
+#define SLAB_SIZE 500
+
+typedef struct slab {
+ struct slab *next;
+ char chars[SLAB_SIZE];
+} Slab;
+typedef struct {
+ char *file_name;
+ int file_line;
+ Slab *slab;
+} ScrapEntry;
+ScrapEntry *SCRAP[256];
+
+#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255]
+
+int scraps;
+void init_scraps()
+{
+ scraps = 1;
+ SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));
+}
+typedef struct {
+ Slab *scrap;
+ int index;
+} Manager;
+static void push(c, manager)
+ char c;
+ Manager *manager;
+{
+ Slab *scrap = manager->scrap;
+ int index = manager->index;
+ scrap->chars[index++] = c;
+ if (index == SLAB_SIZE) {
+ Slab *new = (Slab *) arena_getmem(sizeof(Slab));
+ scrap->next = new;
+ manager->scrap = new;
+ index = 0;
+ }
+ manager->index = index;
+}
+static void pushs(s, manager)
+ char *s;
+ Manager *manager;
+{
+ while (*s)
+ push(*s++, manager);
+}
+int collect_scrap()
+{
+ Manager writer;
+ {
+ Slab *scrap = (Slab *) arena_getmem(sizeof(Slab));
+ if ((scraps & 255) == 0)
+ SCRAP[scraps >> 8] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));
+ scrap_array(scraps).slab = scrap;
+ scrap_array(scraps).file_name = save_string(source_name);
+ scrap_array(scraps).file_line = source_line;
+ writer.scrap = scrap;
+ writer.index = 0;
+ }
+ {
+ int c = source_get();
+ while (1) {
+ switch (c) {
+ case EOF: fprintf(stderr, "%s: unexpect EOF in scrap (%s, %d)\n",
+ command_name, scrap_array(scraps).file_name,
+ scrap_array(scraps).file_line);
+ exit(-1);
+ case '@': {
+ c = source_get();
+ switch (c) {
+ case '@': pushs("@@", &writer);
+ c = source_get();
+ break;
+ case '|': {
+ do {
+ char new_name[100];
+ char *p = new_name;
+ do
+ c = source_get();
+ while (isspace(c));
+ if (c != '@') {
+ Name *name;
+ do {
+ *p++ = c;
+ c = source_get();
+ } while (c != '@' && !isspace(c));
+ *p = '\0';
+ name = name_add(&user_names, new_name);
+ if (!name->defs || name->defs->scrap != scraps) {
+ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ def->scrap = scraps;
+ def->next = name->defs;
+ name->defs = def;
+ }
+ }
+ } while (c != '@');
+ c = source_get();
+ if (c != '}') {
+ fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+ }
+ case '}': push('\0', &writer);
+ return scraps++;
+ case '<': {
+ Name *name = collect_scrap_name();
+ {
+ char *s = name->spelling;
+ int len = strlen(s) - 1;
+ pushs("@<", &writer);
+ while (len > 0) {
+ push(*s++, &writer);
+ len--;
+ }
+ if (*s == ' ')
+ pushs("...", &writer);
+ else
+ push(*s, &writer);
+ pushs("@>", &writer);
+ }
+ {
+ if (!name->uses || name->uses->scrap != scraps) {
+ Scrap_Node *use = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ use->scrap = scraps;
+ use->next = name->uses;
+ name->uses = use;
+ }
+ }
+ c = source_get();
+ }
+ break;
+ default : fprintf(stderr, "%s: unexpected @%c in scrap (%s, %d)\n",
+ command_name, c, source_name, source_line);
+ exit(-1);
+ }
+ }
+ break;
+ default: push(c, &writer);
+ c = source_get();
+ break;
+ }
+ }
+ }
+}
+static char pop(manager)
+ Manager *manager;
+{
+ Slab *scrap = manager->scrap;
+ int index = manager->index;
+ char c = scrap->chars[index++];
+ if (index == SLAB_SIZE) {
+ manager->scrap = scrap->next;
+ index = 0;
+ }
+ manager->index = index;
+ return c;
+}
+static Name *pop_scrap_name(manager)
+ Manager *manager;
+{
+ char name[100];
+ char *p = name;
+ int c = pop(manager);
+ while (TRUE) {
+ if (c == '@')
+ {
+ c = pop(manager);
+ if (c == '@') {
+ *p++ = c;
+ c = pop(manager);
+ }
+ else if (c == '>') {
+ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {
+ p[-3] = ' ';
+ p -= 2;
+ }
+ *p = '\0';
+ return prefix_add(&macro_names, name);
+ }
+ else {
+ fprintf(stderr, "%s: found an internal problem (1)\n", command_name);
+ exit(-1);
+ }
+ }
+ else {
+ *p++ = c;
+ c = pop(manager);
+ }
+ }
+}
+int write_scraps(file, defs, global_indent, indent_chars,
+ debug_flag, tab_flag, indent_flag)
+ FILE *file;
+ Scrap_Node *defs;
+ int global_indent;
+ char *indent_chars;
+ char debug_flag;
+ char tab_flag;
+ char indent_flag;
+{
+ int indent = 0;
+ while (defs) {
+ {
+ char c;
+ Manager reader;
+ int line_number = scrap_array(defs->scrap).file_line;
+ if (debug_flag) {
+ fprintf(file, "\n#line %d \"%s\"\n",
+ line_number, scrap_array(defs->scrap).file_name);
+ {
+ if (indent_flag) {
+ if (tab_flag)
+ for (indent=0; indent<global_indent; indent++)
+ putc(' ', file);
+ else
+ for (indent=0; indent<global_indent; indent++)
+ putc(indent_chars[indent], file);
+ }
+ indent = 0;
+ }
+ }
+ reader.scrap = scrap_array(defs->scrap).slab;
+ reader.index = 0;
+ c = pop(&reader);
+ while (c) {
+ switch (c) {
+ case '@': {
+ c = pop(&reader);
+ switch (c) {
+ case '@': putc(c, file);
+ indent_chars[global_indent + indent] = ' ';
+ indent++;
+ break;
+ case '<': {
+ Name *name = pop_scrap_name(&reader);
+ if (name->mark) {
+ fprintf(stderr, "%s: recursive macro discovered involving <%s>\n",
+ command_name, name->spelling);
+ exit(-1);
+ }
+ if (name->defs) {
+ name->mark = TRUE;
+ indent = write_scraps(file, name->defs, global_indent + indent,
+ indent_chars, debug_flag, tab_flag, indent_flag);
+ indent -= global_indent;
+ name->mark = FALSE;
+ }
+ else if (!tex_flag)
+ fprintf(stderr, "%s: macro never defined <%s>\n",
+ command_name, name->spelling);
+ }
+ if (debug_flag) {
+ fprintf(file, "\n#line %d \"%s\"\n",
+ line_number, scrap_array(defs->scrap).file_name);
+ {
+ if (indent_flag) {
+ if (tab_flag)
+ for (indent=0; indent<global_indent; indent++)
+ putc(' ', file);
+ else
+ for (indent=0; indent<global_indent; indent++)
+ putc(indent_chars[indent], file);
+ }
+ indent = 0;
+ }
+ }
+ break;
+ default: /* ignore, since we should already have a warning */
+ break;
+ }
+ }
+ break;
+ case '\n': putc(c, file);
+ line_number++;
+ {
+ if (indent_flag) {
+ if (tab_flag)
+ for (indent=0; indent<global_indent; indent++)
+ putc(' ', file);
+ else
+ for (indent=0; indent<global_indent; indent++)
+ putc(indent_chars[indent], file);
+ }
+ indent = 0;
+ }
+ break;
+ case '\t': {
+ if (tab_flag)
+ {
+ int delta = 8 - (indent % 8);
+ indent += delta;
+ while (delta > 0) {
+ putc(' ', file);
+ delta--;
+ }
+ }
+ else {
+ putc('\t', file);
+ indent_chars[global_indent + indent] = '\t';
+ indent++;
+ }
+ }
+ break;
+ default: putc(c, file);
+ indent_chars[global_indent + indent] = ' ';
+ indent++;
+ break;
+ }
+ c = pop(&reader);
+ }
+ }
+ defs = defs->next;
+ }
+ return indent + global_indent;
+}
+
+typedef struct name_node {
+ struct name_node *next;
+ Name *name;
+} Name_Node;
+
+typedef struct goto_node {
+ struct goto_node *next; /* next goto node with same depth */
+ struct goto_node *fail;
+ struct move_node *moves;
+ Name_Node *output;
+} Goto_Node;
+
+typedef struct move_node {
+ struct move_node *next;
+ Goto_Node *state;
+ char c;
+} Move_Node;
+
+static Goto_Node *root[128];
+static int max_depth;
+static Goto_Node **depths;
+static Goto_Node *goto_lookup(c, g)
+ char c;
+ Goto_Node *g;
+{
+ Move_Node *m = g->moves;
+ while (m && m->c != c)
+ m = m->next;
+ if (m)
+ return m->state;
+ else
+ return NULL;
+}
+
+static void build_gotos();
+
+void search()
+{
+ int i;
+ for (i=0; i<128; i++)
+ root[i] = NULL;
+ max_depth = 10;
+ depths = (Goto_Node **) arena_getmem(max_depth * sizeof(Goto_Node *));
+ for (i=0; i<max_depth; i++)
+ depths[i] = NULL;
+ build_gotos(user_names);
+ {
+ int depth;
+ for (depth=1; depth<max_depth; depth++) {
+ Goto_Node *r = depths[depth];
+ while (r) {
+ Move_Node *m = r->moves;
+ while (m) {
+ char a = m->c;
+ Goto_Node *s = m->state;
+ Goto_Node *state = r->fail;
+ while (state && !goto_lookup(a, state))
+ state = state->fail;
+ if (state)
+ s->fail = goto_lookup(a, state);
+ else
+ s->fail = root[a];
+ if (s->fail) {
+ Name_Node *p = s->fail->output;
+ while (p) {
+ Name_Node *q = (Name_Node *) arena_getmem(sizeof(Name_Node));
+ q->name = p->name;
+ q->next = s->output;
+ s->output = q;
+ p = p->next;
+ }
+ }
+ m = m->next;
+ }
+ r = r->next;
+ }
+ }
+ }
+ {
+ for (i=1; i<scraps; i++) {
+ char c;
+ Manager reader;
+ Goto_Node *state = NULL;
+ reader.scrap = scrap_array(i).slab;
+ reader.index = 0;
+ c = pop(&reader);
+ while (c) {
+ while (state && !goto_lookup(c, state))
+ state = state->fail;
+ if (state)
+ state = goto_lookup(c, state);
+ else
+ state = root[c];
+ if (state && state->output) {
+ Name_Node *p = state->output;
+ do {
+ Name *name = p->name;
+ if (!name->uses || name->uses->scrap != i) {
+ Scrap_Node *new_use =
+ (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));
+ new_use->scrap = i;
+ new_use->next = name->uses;
+ name->uses = new_use;
+ }
+ p = p->next;
+ } while (p);
+ }
+ c = pop(&reader);
+ }
+ }
+ }
+}
+static void build_gotos(tree)
+ Name *tree;
+{
+ while (tree) {
+ {
+ int depth = 2;
+ char *p = tree->spelling;
+ char c = *p++;
+ Goto_Node *q = root[c];
+ if (!q) {
+ q = (Goto_Node *) arena_getmem(sizeof(Goto_Node));
+ root[c] = q;
+ q->moves = NULL;
+ q->fail = NULL;
+ q->moves = NULL;
+ q->output = NULL;
+ q->next = depths[1];
+ depths[1] = q;
+ }
+ while (c = *p++) {
+ Goto_Node *new = goto_lookup(c, q);
+ if (!new) {
+ Move_Node *new_move = (Move_Node *) arena_getmem(sizeof(Move_Node));
+ new = (Goto_Node *) arena_getmem(sizeof(Goto_Node));
+ new->moves = NULL;
+ new->fail = NULL;
+ new->moves = NULL;
+ new->output = NULL;
+ new_move->state = new;
+ new_move->c = c;
+ new_move->next = q->moves;
+ q->moves = new_move;
+ if (depth == max_depth) {
+ int i;
+ Goto_Node **new_depths =
+ (Goto_Node **) arena_getmem(2*depth*sizeof(Goto_Node *));
+ max_depth = 2 * depth;
+ for (i=0; i<depth; i++)
+ new_depths[i] = depths[i];
+ depths = new_depths;
+ for (i=depth; i<max_depth; i++)
+ depths[i] = NULL;
+ }
+ new->next = depths[depth];
+ depths[depth] = new;
+ }
+ q = new;
+ depth++;
+ }
+ q->output = (Name_Node *) arena_getmem(sizeof(Name_Node));
+ q->output->next = NULL;
+ q->output->name = tree;
+ }
+ build_gotos(tree->rlink);
+ tree = tree->llink;
+ }
+}