diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/nuweb |
Initial commit
Diffstat (limited to 'web/nuweb')
36 files changed, 20826 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(¯o_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(¯o_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(¯o_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(¯o_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(¯o_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(¯o_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(¯o_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(¯o_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; + } +} diff --git a/web/nuweb/nuweb0.87b/Makefile b/web/nuweb/nuweb0.87b/Makefile new file mode 100644 index 0000000000..38b337e475 --- /dev/null +++ b/web/nuweb/nuweb0.87b/Makefile @@ -0,0 +1,63 @@ +CC = cc + +CFLAGS = -O + +TARGET = nuweb +VERSION = 0.87b + +OBJS = main.o pass1.o latex.o html.o output.o input.o scraps.o names.o \ + arena.o global.o + +SRCS = main.c pass1.c latex.c html.c output.c input.c scraps.c names.c \ + arena.c global.c + +.SUFFIXES: .dvi .tex .w .hw + +.w.tex: + nuweb $*.w + +.hw.tex: + nuweb $*.hw + +.tex.dvi: + latex $*.tex + +.w.dvi: + $(MAKE) $*.tex + $(MAKE) $*.dvi + +all: + $(MAKE) $(TARGET).tex + $(MAKE) $(TARGET) + +shar: $(TARGET)doc.tex + shar Makefile README literate.bib nuweb.w \ + $(TARGET)doc.tex $(SRCS) global.h \ + > $(TARGET)$(VERSION).sh + +tar: $(TARGET)doc.tex + tar -cf $(TARGET)$(VERSION).tar Makefile README literate.bib nuweb.w \ + $(TARGET)doc.tex $(SRCS) global.h + +$(TARGET)doc.tex: $(TARGET).tex + sed 's/\\showcodetrue/\\showcodefalse/' $(TARGET).tex > $@ + +clean: + -rm -f *.tex *.log *.dvi *~ *.blg *.lint + +veryclean: + -rm -f *.o *.c *.h *.tex *.log *.dvi *~ *.blg *.lint + +view: $(TARGET).dvi + xdvi $(TARGET).dvi + +print: $(TARGET).dvi + lpr -d $(TARGET).dvi + +lint: + lint $(SRCS) > nuweb.lint + +$(OBJS): global.h + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) diff --git a/web/nuweb/nuweb0.87b/README b/web/nuweb/nuweb0.87b/README new file mode 100644 index 0000000000..93c08f3262 --- /dev/null +++ b/web/nuweb/nuweb0.87b/README @@ -0,0 +1,36 @@ +Here's an intermediate version of nuweb. I've simply bundled up the +web source (nuweb.w), the generated .c files, an abbreviated nuweb.tex +file, and the assorted auxiliary files for latex'ing. + +Note that it's still not completely portable to every system without +change. In particular, it expect file-names to use '/' to delimit +directories and it still thinks tab stops are set every eight spaces. +It also uses tempnam() which may not be available everywhere. +tmpnam() might be made to work in some systems. If all else fails, +just use some adequately unlikely filename. + +To print the documentation, you must first run latex on the file +nuwebdoc.tex. To fix up all the citations, you'll need several runs. + + latex nuwebdoc + bibtex nuwebdoc + latex nuwebdoc + latex nuwebdoc + +Note that the distributed nuwebdoc.tex is basically Chapter 1 of the +complete nuweb.tex generated by running nuweb against itself. + +To actually build the nuweb executable, type "make nuweb" which should build +an executable file called "nuweb". After that, you should be able to +use the makefile to control everything very nicely. + +To view the nuweb sources using an HTML viewer, copy nuweb.w to +nuweb.hw, and then add the html document-style option to nuweb.hw. +After producing the LaTeX source file nuweb.tex, convert it to HTML +using LaTeX2HTML. + +If you have questions or comments (or just want the latest version), +please let me know. I expect to continue working on the documentation +for quite a while. + +My e-mail address is preston@cs.rice.edu diff --git a/web/nuweb/nuweb0.87b/arena.c b/web/nuweb/nuweb0.87b/arena.c new file mode 100644 index 0000000000..09224f21db --- /dev/null +++ b/web/nuweb/nuweb0.87b/arena.c @@ -0,0 +1,48 @@ +#include "global.h" +typedef struct chunk { + struct chunk *next; + char *limit; + char *avail; +} Chunk; +static Chunk first = { NULL, NULL, NULL }; +static Chunk *arena = &first; +void *arena_getmem(n) + size_t n; +{ + char *q; + char *p = arena->avail; + n = (n + 7) & ~7; /* ensuring alignment to 8 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; + } + { + size_t 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/nuweb0.87b/global.c b/web/nuweb/nuweb0.87b/global.c new file mode 100644 index 0000000000..6c6e872de8 --- /dev/null +++ b/web/nuweb/nuweb0.87b/global.c @@ -0,0 +1,15 @@ +#include "global.h" +int tex_flag = TRUE; +int html_flag = FALSE; +int output_flag = TRUE; +int compare_flag = TRUE; +int verbose_flag = FALSE; +int number_flag = FALSE; +char *command_name = NULL; +char *source_name = NULL; +int source_line = 0; +int already_warned = 0; +Name *file_names = NULL; +Name *macro_names = NULL; +Name *user_names = NULL; + diff --git a/web/nuweb/nuweb0.87b/global.h b/web/nuweb/nuweb0.87b/global.h new file mode 100644 index 0000000000..c811c5a8b7 --- /dev/null +++ b/web/nuweb/nuweb0.87b/global.h @@ -0,0 +1,64 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#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; + +extern int tex_flag; /* if FALSE, don't emit the documentation file */ +extern int html_flag; /* if TRUE, emit HTML instead of LaTeX scraps. */ +extern int output_flag; /* if FALSE, don't emit the output files */ +extern int compare_flag; /* if FALSE, overwrite without comparison */ +extern int verbose_flag; /* if TRUE, write progress information */ +extern int number_flag; /* if TRUE, use a sequential numbering scheme */ +extern char *command_name; +extern char *source_name; /* name of the current file */ +extern int source_line; /* current line in the source file */ +extern int already_warned; +extern Name *file_names; +extern Name *macro_names; +extern Name *user_names; + +extern void pass1(); +extern void write_tex(); +extern void write_html(); +extern void write_files(); +extern void source_open(); /* pass in the name of the source file */ +extern int source_get(); /* no args; returns the next char or EOF */ +extern void init_scraps(); +extern int collect_scrap(); +extern int write_scraps(); +extern void write_scrap_ref(); +extern void write_single_scrap_ref(); +extern void collect_numbers(); +extern Name *collect_file_name(); +extern Name *collect_macro_name(); +extern Name *collect_scrap_name(); +extern Name *name_add(); +extern Name *prefix_add(); +extern char *save_string(); +extern void reverse_lists(); +extern void search(); +extern void *arena_getmem(); +extern void arena_free(); + diff --git a/web/nuweb/nuweb0.87b/html.c b/web/nuweb/nuweb0.87b/html.c new file mode 100644 index 0000000000..b48a61e95e --- /dev/null +++ b/web/nuweb/nuweb0.87b/html.c @@ -0,0 +1,335 @@ +#include "global.h" +static void copy_scrap(); /* formats the body of a scrap */ +static void display_scrap_ref(); /* formats a scrap reference */ +static void display_scrap_numbers(); /* formats a list of scrap numbers */ +static void print_scrap_numbers(); /* pluralizes scrap formats list */ +static void format_entry(); /* formats an index entry */ +static void format_user_entry(); +void write_html(file_name, html_name) + char *file_name; + char *html_name; +{ + FILE *html_file = fopen(html_name, "w"); + if (html_file) { + if (verbose_flag) + fprintf(stderr, "writing %s\n", html_name); + source_open(file_name); + { + int scraps = 1; + int c = source_get(); + while (c != EOF) { + if (c == '@') + { + c = source_get(); + switch (c) { + case 'O': + case 'o': { + Name *name = collect_file_name(); + { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<pre>\n", html_file); + } + fputs("<a name=\"nuweb", html_file); + write_single_scrap_ref(html_file, scraps); + fprintf(html_file, "\"><code>\"%s\"</code> ", name->spelling); + write_single_scrap_ref(html_file, scraps); + fputs("</a> =\n", html_file); + + scraps++; + { + copy_scrap(html_file); + fputs("<></pre>\n", html_file); + } + { + if (name->defs->next) { + fputs("File defined by ", html_file); + print_scrap_numbers(html_file, name->defs); + fputs("<br>\n", html_file); + } + } + { + fputs("\\end{rawhtml}\n", html_file); + c = source_get(); /* Get rid of current at command. */ + } + } + break; + case 'D': + case 'd': { + Name *name = collect_macro_name(); + { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<pre>\n", html_file); + } + fputs("<a name=\"nuweb", html_file); + write_single_scrap_ref(html_file, scraps); + fprintf(html_file, "\"><%s ", name->spelling); + write_single_scrap_ref(html_file, scraps); + fputs("></a> =\n", html_file); + + scraps++; + { + copy_scrap(html_file); + fputs("<></pre>\n", html_file); + } + { + if (name->defs->next) { + fputs("Macro defined by ", html_file); + print_scrap_numbers(html_file, name->defs); + fputs("<br>\n", html_file); + } + } + { + if (name->uses) { + fputs("Macro referenced in ", html_file); + print_scrap_numbers(html_file, name->uses); + } + else { + fputs("Macro never referenced.\n", html_file); + fprintf(stderr, "%s: <%s> never referenced.\n", + command_name, name->spelling); + } + fputs("<br>\n", html_file); + } + { + fputs("\\end{rawhtml}\n", html_file); + c = source_get(); /* Get rid of current at command. */ + } + } + break; + case 'f': { + if (file_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_entry(file_names, html_file, TRUE); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); + } + break; + case 'm': { + if (macro_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_entry(macro_names, html_file, FALSE); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); + } + break; + case 'u': { + if (user_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_user_entry(user_names, html_file); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); + } + break; + case '@': putc(c, html_file); + default: c = source_get(); + break; + } + } + else { + putc(c, html_file); + c = source_get(); + } + } + } + fclose(html_file); + } + else + fprintf(stderr, "%s: can't open %s\n", command_name, html_name); +} +static void display_scrap_ref(html_file, num) + FILE *html_file; + int num; +{ + fputs("<a href=\"#nuweb", html_file); + write_single_scrap_ref(html_file, num); + fputs("\">", html_file); + write_single_scrap_ref(html_file, num); + fputs("</a>", html_file); +} +static void display_scrap_numbers(html_file, scraps) + FILE *html_file; + Scrap_Node *scraps; +{ + display_scrap_ref(html_file, scraps->scrap); + scraps = scraps->next; + while (scraps) { + fputs(", ", html_file); + display_scrap_ref(html_file, scraps->scrap); + scraps = scraps->next; + } +} +static void print_scrap_numbers(html_file, scraps) + FILE *html_file; + Scrap_Node *scraps; +{ + fputs("scrap", html_file); + if (scraps->next) fputc('s', html_file); + fputc(' ', html_file); + display_scrap_numbers(html_file, scraps); + fputs(".\n", html_file); +} +static void copy_scrap(file) + FILE *file; +{ + int indent = 0; + int c = source_get(); + while (1) { + switch (c) { + case '@': { + c = source_get(); + switch (c) { + case '@': fputc(c, file); + break; + case '|': { + do { + do + c = source_get(); + while (c != '@'); + c = source_get(); + } while (c != '}'); + } + case '}': return; + case '<': { + Name *name = collect_scrap_name(); + fprintf(file, "<%s ", name->spelling); + if (name->defs) + { + Scrap_Node *p = name->defs; + display_scrap_ref(file, p->scrap); + if (p->next) + fputs(", ... ", file); + } + else { + putc('?', file); + fprintf(stderr, "%s: scrap never defined <%s>\n", + command_name, name->spelling); + } + fputs(">", file); + } + break; + default: /* ignore these since pass1 will have warned about them */ + break; + } + } + break; + case '<' : fputs("<", file); + indent++; + break; + case '>' : fputs(">", file); + indent++; + break; + case '&' : fputs("&", file); + indent++; + break; + case '\n': fputc(c, 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, html_file, file_flag) + Name *name; + FILE *html_file; + int file_flag; +{ + while (name) { + format_entry(name->llink, html_file, file_flag); + { + fputs("<dt> ", html_file); + if (file_flag) { + fprintf(html_file, "<code>\"%s\"</code>\n<dd> ", name->spelling); + { + fputs("Defined by ", html_file); + print_scrap_numbers(html_file, name->defs); + } + } + else { + fprintf(html_file, "<%s ", name->spelling); + { + if (name->defs) + display_scrap_numbers(html_file, name->defs); + else + putc('?', html_file); + } + fputs(">\n<dd> ", html_file); + { + Scrap_Node *p = name->uses; + if (p) { + fputs("Referenced in ", html_file); + print_scrap_numbers(html_file, p); + } + else + fputs("Not referenced.\n", html_file); + } + } + putc('\n', html_file); + } + name = name->rlink; + } +} +static void format_user_entry(name, html_file) + Name *name; + FILE *html_file; +{ + while (name) { + format_user_entry(name->llink, html_file); + { + Scrap_Node *uses = name->uses; + if (uses) { + Scrap_Node *defs = name->defs; + fprintf(html_file, "<dt><code>%s</code>:\n<dd> ", name->spelling); + if (uses->scrap < defs->scrap) { + display_scrap_ref(html_file, uses->scrap); + uses = uses->next; + } + else { + if (defs->scrap == uses->scrap) + uses = uses->next; + fputs("<strong>", html_file); + display_scrap_ref(html_file, defs->scrap); + fputs("</strong>", html_file); + defs = defs->next; + } + while (uses || defs) { + fputs(", ", html_file); + if (uses && (!defs || uses->scrap < defs->scrap)) { + display_scrap_ref(html_file, uses->scrap); + uses = uses->next; + } + else { + if (uses && defs->scrap == uses->scrap) + uses = uses->next; + fputs("<strong>", html_file); + display_scrap_ref(html_file, defs->scrap); + fputs("</strong>", html_file); + defs = defs->next; + } + } + fputs(".\n", html_file); + } + } + name = name->rlink; + } +} diff --git a/web/nuweb/nuweb0.87b/input.c b/web/nuweb/nuweb0.87b/input.c new file mode 100644 index 0000000000..90843e5cc7 --- /dev/null +++ b/web/nuweb/nuweb0.87b/input.c @@ -0,0 +1,108 @@ +#include "global.h" +static FILE *source_file; /* the current input file */ +static int source_peek; +static int double_at; +static int include_depth; +static 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': { + char name[100]; + if (include_depth >= 10) { + fprintf(stderr, "%s: include nesting too deep (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + { + 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(); + } + 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/nuweb0.87b/latex.c b/web/nuweb/nuweb0.87b/latex.c new file mode 100644 index 0000000000..1d4aeec03b --- /dev/null +++ b/web/nuweb/nuweb0.87b/latex.c @@ -0,0 +1,361 @@ +#include "global.h" +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) { + if (verbose_flag) + fprintf(stderr, "writing %s\n", tex_name); + 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} \\small", tex_file); + if (!big_definition) + fputs("\n\\begin{minipage}{\\linewidth}", tex_file); + fprintf(tex_file, " \\label{scrap%d}\n", scraps); + } + fprintf(tex_file, "\\verb@\"%s\"@ {\\footnotesize ", name->spelling); + write_single_scrap_ref(tex_file, scraps++); + fputs(" }$\\equiv$\n", tex_file); + { + fputs("\\vspace{-1ex}\n\\begin{list}{}{} \\item\n", tex_file); + copy_scrap(tex_file); + fputs("$\\Diamond$\n\\end{list}\n", tex_file); + } + { + if (name->defs->next) { + fputs("\\vspace{-1ex}\n", tex_file); + fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file); + fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file); + fputs("\\item File defined by scraps ", tex_file); + print_scrap_numbers(tex_file, name->defs); + fputs("\\end{list}\n", tex_file); + } + else + fputs("\\vspace{-2ex}\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} \\small", tex_file); + if (!big_definition) + fputs("\n\\begin{minipage}{\\linewidth}", tex_file); + fprintf(tex_file, " \\label{scrap%d}\n", scraps); + } + fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling); + write_single_scrap_ref(tex_file, scraps++); + fputs("}$\\rangle\\equiv$\n", tex_file); + { + fputs("\\vspace{-1ex}\n\\begin{list}{}{} \\item\n", tex_file); + copy_scrap(tex_file); + fputs("$\\Diamond$\n\\end{list}\n", tex_file); + } + { + fputs("\\vspace{-1ex}\n", tex_file); + fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file); + fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file); + if (name->defs->next) { + fputs("\\item Macro defined by scraps ", tex_file); + print_scrap_numbers(tex_file, name->defs); + } + } + { + if (name->uses) { + if (name->uses->next) { + fputs("\\item Macro referenced in scraps ", tex_file); + print_scrap_numbers(tex_file, name->uses); + } + else { + fputs("\\item Macro referenced in scrap ", tex_file); + write_single_scrap_ref(tex_file, name->uses->scrap); + fputs(".\n", tex_file); + } + } + else { + fputs("\\item Macro never referenced.\n", tex_file); + fprintf(stderr, "%s: <%s> never referenced.\n", + command_name, name->spelling); + } + fputs("\\end{list}\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 'f': { + if (file_names) { + fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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; +{ + int page; + write_scrap_ref(tex_file, scraps->scrap, TRUE, &page); + scraps = scraps->next; + while (scraps) { + write_scrap_ref(tex_file, scraps->scrap, FALSE, &page); + scraps = scraps->next; + } + fputs(".\n", tex_file); +} +static void copy_scrap(file) + FILE *file; +{ + int indent = 0; + int c = source_get(); + fputs("\\mbox{}\\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) + { + Scrap_Node *p = name->defs; + write_single_scrap_ref(file, p->scrap); + p = p->next; + if (p) + fputs(", \\ldots\\ ", file); + } + 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\\mbox{}\\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 ", 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 { + putc(' ', tex_file); + write_single_scrap_ref(tex_file, p->scrap); + putc('.', tex_file); + } + putc('}', tex_file); + } + } + else { + fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling); + { + Scrap_Node *p = name->defs; + if (p) { + int page; + write_scrap_ref(tex_file, p->scrap, TRUE, &page); + p = p->next; + while (p) { + write_scrap_ref(tex_file, p->scrap, FALSE, &page); + p = p->next; + } + } + 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 { + putc(' ', tex_file); + write_single_scrap_ref(tex_file, p->scrap); + putc('.', tex_file); + } + } + 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; + if (uses) { + int page; + Scrap_Node *defs = name->defs; + fprintf(tex_file, "\\item \\verb@%s@: ", name->spelling); + if (uses->scrap < defs->scrap) { + write_scrap_ref(tex_file, uses->scrap, TRUE, &page); + uses = uses->next; + } + else { + if (defs->scrap == uses->scrap) + uses = uses->next; + fputs("\\underline{", tex_file); + write_single_scrap_ref(tex_file, defs->scrap); + putc('}', tex_file); + page = -2; + defs = defs->next; + } + while (uses || defs) { + if (uses && (!defs || uses->scrap < defs->scrap)) { + write_scrap_ref(tex_file, uses->scrap, FALSE, &page); + uses = uses->next; + } + else { + if (uses && defs->scrap == uses->scrap) + uses = uses->next; + fputs(", \\underline{", tex_file); + write_single_scrap_ref(tex_file, defs->scrap); + putc('}', tex_file); + page = -2; + defs = defs->next; + } + } + fputs(".\n", tex_file); + } + } + name = name->rlink; + } +} diff --git a/web/nuweb/nuweb0.87b/literate.bib b/web/nuweb/nuweb0.87b/literate.bib new file mode 100644 index 0000000000..dd454a041d --- /dev/null +++ b/web/nuweb/nuweb0.87b/literate.bib @@ -0,0 +1,129 @@ + +% 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" +} + +@booklet{drakos:94, + title="The {LaTeX2HTML} Translator", + author="Nikos Drakos", + note="Available from + {\tt http://clb.leeds.ac.uk/nikos/tex2html/latex2html.tar}", + month=jan, + year=1994 +} + diff --git a/web/nuweb/nuweb0.87b/main.c b/web/nuweb/nuweb0.87b/main.c new file mode 100644 index 0000000000..e792ffc8cd --- /dev/null +++ b/web/nuweb/nuweb0.87b/main.c @@ -0,0 +1,105 @@ +#include "global.h" +int main(argc, argv) + int argc; + char **argv; +{ + int arg = 1; + 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 'n': number_flag = TRUE; + break; + case 'o': output_flag = FALSE; + break; + case 't': tex_flag = FALSE; + break; + case 'v': verbose_flag = TRUE; + break; + default: fprintf(stderr, "%s: unexpected argument ignored. ", + command_name); + fprintf(stderr, "Usage is: %s [-cnotv] file...\n", + command_name); + break; + } + c = *s++; + } + } + arg++; + } + else break; + } + { + if (arg >= argc) { + fprintf(stderr, "%s: expected a file name. ", command_name); + fprintf(stderr, "Usage is: %s [-cnotv] file-name...\n", command_name); + exit(-1); + } + do { + { + char source_name[100]; + char tex_name[100]; + char aux_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'; /* produce HTML when the file extension is ".hw" */ + html_flag = dot[1] == 'h' && dot[2] == 'w' && dot[3] == '\0'; + sprintf(tex_name, "%s.tex", trim); + sprintf(aux_name, "%s.aux", trim); + *dot = '.'; + } + else { + sprintf(tex_name, "%s.tex", trim); + sprintf(aux_name, "%s.aux", trim); + *q++ = '.'; + *q++ = 'w'; + *q = '\0'; + } + } + { + pass1(source_name); + if (tex_flag) { + if (html_flag) { + int saved_number_flag = number_flag; + number_flag = TRUE; + collect_numbers(aux_name); + write_html(source_name, tex_name); + number_flag = saved_number_flag; + } + else { + collect_numbers(aux_name); + write_tex(source_name, tex_name); + } + } + if (output_flag) + write_files(file_names); + arena_free(); + } + } + arg++; + } while (arg < argc); + } + exit(0); +} diff --git a/web/nuweb/nuweb0.87b/names.c b/web/nuweb/nuweb0.87b/names.c new file mode 100644 index 0000000000..448d209113 --- /dev/null +++ b/web/nuweb/nuweb0.87b/names.c @@ -0,0 +1,368 @@ +#include "global.h" +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; +} +static int ambiguous_prefix(); + +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() != '{') { + fprintf(stderr, "%s: expected @{ after file name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + return new_name; +} +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(¯o_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() != '{') { + fprintf(stderr, "%s: expected @{ after macro name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + { + 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(¯o_names, 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); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +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(¯o_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)) { + fprintf(stderr, + "%s: unexpected character in macro name (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + *p++ = c; + c = source_get(); + break; + } + } + fprintf(stderr, "%s: unexpected end of file (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +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/nuweb0.87b/nuweb.w b/web/nuweb/nuweb0.87b/nuweb.w new file mode 100644 index 0000000000..1ecda0c408 --- /dev/null +++ b/web/nuweb/nuweb0.87b/nuweb.w @@ -0,0 +1,3599 @@ +\documentstyle{report} +\newif\ifshowcode +\showcodetrue + +\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 Version 0.87b \\ A Simple Literate Programming Tool} +\date{} +\author{Preston Briggs\thanks{This work has been supported by ARPA, +through ONR grant N00014-91-J-1989.} +\\ \sl preston@@cs.rice.edu +\\ HTML scrap generator by John D. Ramsdell +\\ \sl ramsdell@@mitre.org} + +\begin{document} +\pagenumbering{roman} +\maketitle +\tableofcontents + +\chapter{Introduction} +\pagenumbering{arabic} + +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, \verb|WEB|, that offered programmers a superior +approach to programming. He wrote several programs in \verb|WEB|, +including \verb|weave| and \verb|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 \verb|tangle| on the web file would produce a complete +Pascal program, ready for compilation by an ordinary Pascal compiler. +The primary function of \verb|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 \verb|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 \verb|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 +\verb|WEB|\@@. Some people have even built web-like tools for their +own favorite combinations of programming language and typesetting +language. For example, \verb|CWEB|, Knuth's current system of choice, +works with a combination of C (or 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 and a number of features 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 \verb|WEB| and \verb|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 \verb|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 \verb|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 \verb|tangle| and \verb|weave| into + a single program that performs both functions at once. +\item[Page numbers] Inspired by the example of noweb, nuweb refers to + all scraps by page number to simplify navigation. If there are + multiple scraps on a page (say page~17), they are distinguished by + lower-case letters ({\em e.g.,} 17a, 17b, and so forth). +\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 \verb|make| (or similar tools), +since \verb|make| will avoid recompiling untouched output files. + +\subsection{Nuweb and HTML} + +In addition to producing {\LaTeX} source, nuweb can be used to +generate HyperText Markup Language (HTML), the markup language used by +the World Wide Web. HTML provides hypertext links. When a HTML +document is viewed online, a user can navigate within the document by +activating the links. The tools which generate HTML automatically +produce hypertext links from a nuweb source. + + +\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 documentation file---unless a nuweb command +is discovered. All nuweb commands begin with an ``at-sign'' +(\verb|@@|). 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 documentation 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} +\verb|@@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} +\verb|@@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 a program file or a documentation 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 \verb|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 explicitly specified for inclusion in the +\verb|@@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 or \verb|@@| characters) will do. +Therefore, it's possible to add index entries for things like +\verb|<<=| 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, \verb|.w| will be appended. {\LaTeX} suitable for +translation into HTML by {\LaTeX}2HTML will be produced from +files whose name ends with \verb|.hw|, otherwise, ordinary {\LaTeX} will be +produced. While a file name may specify a file in another directory, +the resulting documentation 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 \verb|/foo/bar/quux.w| and will create the +file \verb|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 documentation 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} +\verb|nuweb -to /foo/bar/quux| +\end{quote} +would simply scan the input and produce no output at all. + +There are two additional command-line flags: +\begin{description} +\item[\tt -v] For ``verbose,'' causes nuweb to write information about + its progress to \verb|stderr|. +\item[\tt -n] Forces scraps to be numbered sequentially from~1 + (instead of using page numbers). This form is perhaps more desirable + for small webs. +\end{description} + +\section{Generating HTML} + +Nikos Drakos' {\LaTeX}2HTML Version 0.5.3~\cite{drakos:94} can be used +to translate {\LaTeX} with embedded HTML scraps into HTML\@@. Be sure +to include the document-style option \verb|html| so that {\LaTeX} will +understand the hypertext commands. When translating into HTML, do not +allow a document to be split by specifying ``\verb|-split 0|''. +You need not generate navigation links, so also specify +``\verb|-no_navigation|''. + +While preparing a web, you may want to view the program's scraps without +taking the time to run {\LaTeX}2HTML\@@. Simply rename the generated +{\LaTeX} source so that its file name ends with \verb|.html|, and view +that file. The documentations section will be jumbled, but the +scraps will be clear. + +\section{Restrictions} + +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 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 \verb|@@| + 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. + When producing HTML, macros are displayed in a preformatted element + (PRE), so macros may contain one or more A, B, I, U, or P elements + or data characters. +\item Anything is allowed in the body of a scrap; however, very + long scraps (horizontally or vertically) may not typeset well. +\item Temporary files (created for comparison to the eventual + output files) are placed in the current directory. Since they may be + renamed to an output file name, all the output files should be on the + same file system as the current directory. +\item Because page numbers cannot be determined until the document has + been typeset, we have to rerun nuweb after \LaTeX\ to obtain a clean + version of the document (very similar to the way we sometimes have + to rerun \LaTeX\ to obtain an up-to-date table of contents after + significant edits). Nuweb will warn (in most cases) when this needs + to be done; in the remaining cases, \LaTeX\ will warn that labels + may have changed. +\end{itemize} +Very long scraps may be allowed to break across a page if declared +with \verb|@@O| or \verb|@@D| (instead of \verb|@@o| and \verb|@@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. No distinction is made between the +upper case and lower case forms of these commands when generating +HTML\@@. + +\section{Acknowledgements} + +Several people have contributed their times, ideas, and debugging +skills. In particular, I'd like to acknowledge the contributions of +Osman Buyukisik, Manuel Carriba, Adrian Clarke, Tim Harvey, Michael +Lewis, Walter Ravenek, Rob Shillingsburg, Kayvan Sylvan, Dominique +de~Waleffe, and Scott Warren. Of course, most of these people would +never have heard or nuweb (or many other tools) without the efforts of +George Greenwade. + + +\ifshowcode +\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 documentation 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. +@o global.h +@{@<Include files@> +@<Type declarations@> +@<Global variable declarations@> +@<Function prototypes@> +@} + +We'll need at least three of the standard system include files. +@d Include files +@{#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +@| FILE stderr exit fprintf fputs fopen fclose getc putc strlen +toupper isupper islower isgraph isspace tempnam remove malloc size_t @} + +\newpage +\noindent +I also like to use \verb|TRUE| and \verb|FALSE| in my code. +I'd use an \verb|enum| here, except that some systems seem to provide +definitions of \verb|TRUE| and \verb|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 1 +#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 \verb|main.c| will contain the driver for the whole program +(see Section~\ref{main-routine}). +@o main.c +@{#include "global.h" +@} + +The first pass over the source file is contained in \verb|pass1.c|. +It handles collection of all the file names, macros names, and scraps +(see Section~\ref{pass-one}). +@o pass1.c +@{#include "global.h" +@} + +The \verb|.tex| file is created during a second pass over the source +file. The file \verb|latex.c| contains the code controlling the +construction of the \verb|.tex| file +(see Section~\ref{latex-file}). +@o latex.c +@{#include "global.h" +@} + +The file \verb|html.c| contains the code controlling the +construction of the \verb|.tex| file appropriate for use with {\LaTeX}2HTML +(see Section~\ref{html-file}). +@o html.c +@{#include "global.h" +@} + +The code controlling the creation of the output files is in \verb|output.c| +(see Section~\ref{output-files}). +@o output.c +@{#include "global.h" +@} + +\newpage +\subsection{Support Files} + +The support files contain a variety of support routines used to define +and manipulate the major data abstractions. +The file \verb|input.c| holds all the routines used for referring to +source files (see Section~\ref{source-files}). +@o input.c +@{#include "global.h" +@} + +Creation and lookup of scraps is handled by routines in \verb|scraps.c| +(see Section~\ref{scraps}). +@o scraps.c +@{#include "global.h" +@} + + +The handling of file names and macro names is detailed in \verb|names.c| +(see Section~\ref{names}). +@o names.c +@{#include "global.h" +@} + +Memory allocation and deallocation is handled by routines in \verb|arena.c| +(see Section~\ref{memory-management}). +@o arena.c +@{#include "global.h" +@} + +Finally, for best portability, I seem to need a file containing +(useless!) definitions of all the global variables. +@o global.c +@{#include "global.h" +@<Global variable definitions@> +@} + +\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 +@{int 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 five 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] The verbose flag. Forces output of progress reports. +\item[\tt -n] Forces sequential numbering of scraps (instead of page + numbers). +\end{description} + +\noindent +Global flags are declared for each of the arguments. +@d Global variable dec... +@{extern int tex_flag; /* if FALSE, don't emit the documentation file */ +extern int html_flag; /* if TRUE, emit HTML instead of LaTeX scraps. */ +extern int output_flag; /* if FALSE, don't emit the output files */ +extern int compare_flag; /* if FALSE, overwrite without comparison */ +extern int verbose_flag; /* if TRUE, write progress information */ +extern int number_flag; /* if TRUE, use a sequential numbering scheme */ +@| tex_flag html_flag output_flag compare_flag verbose_flag number_flag @} + +The flags are all initialized for correct default behavior. + +@d Global variable def... +@{int tex_flag = TRUE; +int html_flag = FALSE; +int output_flag = TRUE; +int compare_flag = TRUE; +int verbose_flag = FALSE; +int number_flag = FALSE; +@} + + +We save the invocation name of the command in a global variable +\verb|command_name| for use in error messages. +@d Global variable dec... +@{extern char *command_name; +@| command_name @} + +@d Global variable def... +@{char *command_name = NULL; +@} + +The invocation name is conventionally passed in \verb|argv[0]|. +@d Interpret com... +@{command_name = argv[0]; +@} + +We need to examine the remaining entries in \verb|argv|, looking for +command-line arguments. +@d Interpret com... +@{while (arg < argc) { + char *s = argv[arg]; + if (*s++ == '-') { + @<Interpret the argument string \verb|s|@> + arg++; + } + else break; +}@} + + +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 'n': number_flag = TRUE; + break; + case 'o': output_flag = FALSE; + break; + case 't': tex_flag = FALSE; + break; + case 'v': verbose_flag = TRUE; + break; + default: fprintf(stderr, "%s: unexpected argument ignored. ", + command_name); + fprintf(stderr, "Usage is: %s [-cnotv] 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) { + fprintf(stderr, "%s: expected a file name. ", command_name); + fprintf(stderr, "Usage is: %s [-cnotv] file-name...\n", command_name); + exit(-1); + } + do { + @<Handle the file name in \verb|argv[arg]|@> + arg++; + } while (arg < argc); +}@} + +\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 \verb|.w| as a default. The extended path name will be +kept in a local variable \verb|source_name|. The resulting documentation +file will be written in the current directory; its name will be kept +in the variable \verb|tex_name|. +@d Handle the file... +@{{ + char source_name[100]; + char tex_name[100]; + char aux_name[100]; + @<Build \verb|source_name| and \verb|tex_name|@> + @<Process a file@> +}@} + + +I bump the pointer \verb|p| through all the characters in \verb|argv[arg]|, +copying all the characters into \verb|source_name| (via the pointer +\verb|q|). + +At each slash, I update \verb|trim| to point just past the +slash in \verb|source_name|. The effect is that \verb|trim| will point +at the file name without any leading directory specifications. + +The pointer \verb|dot| is made to point at the file name extension, if +present. If there is no extension, we add \verb|.w| to the source name. +In any case, we create the \verb|tex_name| from \verb|trim|, taking +care to get the correct extension. The \verb|html_flag| is set in +this scrap. +@d Build \verb|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'; /* produce HTML when the file extension is ".hw" */ + html_flag = dot[1] == 'h' && dot[2] == 'w' && dot[3] == '\0'; + sprintf(tex_name, "%s.tex", trim); + sprintf(aux_name, "%s.aux", trim); + *dot = '.'; + } + else { + sprintf(tex_name, "%s.tex", trim); + sprintf(aux_name, "%s.aux", 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 four routines \verb|pass1| +(see Section~\ref{pass-one}), \verb|write_tex| (see +Section~\ref{latex-file}), \verb|write_html| (see +Section~\ref{html-file}), and \verb|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}). The sequential numbering of scraps +is forced when generating HTML. +@d Process a file +@{{ + pass1(source_name); + if (tex_flag) { + if (html_flag) { + int saved_number_flag = number_flag; + number_flag = TRUE; + collect_numbers(aux_name); + write_html(source_name, tex_name); + number_flag = saved_number_flag; + } + else { + collect_numbers(aux_name); + write_tex(source_name, tex_name); + } + } + if (output_flag) + write_files(file_names); + arena_free(); +}@} + + +\newpage +\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... +@{extern void pass1(); +@} + + +The routine \verb|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; +{ + if (verbose_flag) + fprintf(stderr, "reading %s\n", 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 @} + +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 \verb|scrap| to...@> +}@} + + +@d Build macro definition +@{{ + Name *name = collect_macro_name(); + int scrap = collect_scrap(); + @<Add \verb|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 \verb|scrap| to \verb|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 \verb|pass1| to +reverse them all so they'll be simpler to print out prettily. +The code for \verb|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 \verb|write_tex|) copies most of +the text from the source file straight into a \verb|.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... +@{extern void write_tex(); +@} + +We need a few local function declarations before we get into the body +of \verb|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 \verb|write_tex| takes two file names as parameters: the +name of the web source file and the name of the \verb|.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) { + if (verbose_flag) + fprintf(stderr, "writing %s\n", tex_name); + source_open(file_name); + @<Copy \verb|source_file| into \verb|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 \verb|.tex| file. However, we keep +an eye peeled for \verb|@@|~characters, which signal a command sequence. + +@d Copy \verb|source_file| into \verb|tex_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 references): +{\small +\begin{verbatim} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap37} +$\langle$Interpret at-sequence {\footnotesize 18}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@{@@\\ +\mbox{}\verb@@ int big_definition = FALSE;@@\\ +\mbox{}\verb@@ c = source_get();@@\\ +\mbox{}\verb@@ switch (c) {@@\\ +\mbox{}\verb@@ case 'O': big_definition = TRUE;@@\\ +\mbox{}\verb@@ case 'o': @@$\langle$Write output file definition {\footnotesize 19a}$\rangle$\verb@@@@\\ +\end{verbatim} +\vdots +\begin{verbatim} +\mbox{}\verb@@ case '@@{\tt @@}\verb@@': putc(c, tex_file);@@\\ +\mbox{}\verb@@ default: c = source_get();@@\\ +\mbox{}\verb@@ break;@@\\ +\mbox{}\verb@@ }@@\\ +\mbox{}\verb@@}@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 17b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\end{verbatim}} + +\noindent +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 \verb|@@| 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\"@@ {\\footnotesize ", name->spelling); + write_single_scrap_ref(tex_file, scraps++); + fputs(" }$\\equiv$\n", tex_file); + @<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 {\\footnotesize ", name->spelling); + write_single_scrap_ref(tex_file, scraps++); + fputs("}$\\rangle\\equiv$\n", tex_file); + @<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} \\small", tex_file); + if (!big_definition) + fputs("\n\\begin{minipage}{\\linewidth}", tex_file); + fprintf(tex_file, " \\label{scrap%d}\n", scraps); +}@} + +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{-1ex}\n\\begin{list}{}{} \\item\n", tex_file); + copy_scrap(tex_file); + fputs("$\\Diamond$\n\\end{list}\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("\\vspace{-1ex}\n", tex_file); + fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file); + fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file); + fputs("\\item File defined by scraps ", tex_file); + print_scrap_numbers(tex_file, name->defs); + fputs("\\end{list}\n", tex_file); + } + else + fputs("\\vspace{-2ex}\n", tex_file); +}@} + +@d Write macro defs +@{{ + fputs("\\vspace{-1ex}\n", tex_file); + fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file); + fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file); + if (name->defs->next) { + fputs("\\item Macro defined by scraps ", tex_file); + print_scrap_numbers(tex_file, name->defs); + } +}@} + +@d Write macro refs +@{{ + if (name->uses) { + if (name->uses->next) { + fputs("\\item Macro referenced in scraps ", tex_file); + print_scrap_numbers(tex_file, name->uses); + } + else { + fputs("\\item Macro referenced in scrap ", tex_file); + write_single_scrap_ref(tex_file, name->uses->scrap); + fputs(".\n", tex_file); + } + } + else { + fputs("\\item Macro never referenced.\n", tex_file); + fprintf(stderr, "%s: <%s> never referenced.\n", + command_name, name->spelling); + } + fputs("\\end{list}\n", tex_file); +}@} + + +@o latex.c +@{static void print_scrap_numbers(tex_file, scraps) + FILE *tex_file; + Scrap_Node *scraps; +{ + int page; + write_scrap_ref(tex_file, scraps->scrap, TRUE, &page); + scraps = scraps->next; + while (scraps) { + write_scrap_ref(tex_file, scraps->scrap, FALSE, &page); + scraps = scraps->next; + } + fputs(".\n", tex_file); +} +@| print_scrap_numbers @} + + +\subsubsection{Formatting a Scrap} + +We add a \verb|\mbox{}| at the beginning of each line to avoid +problems with older versions of \TeX. + +@o latex.c +@{static void copy_scrap(file) + FILE *file; +{ + int indent = 0; + int c = source_get(); + fputs("\\mbox{}\\verb@@", file); + while (1) { + switch (c) { + case '@@': @<Check at-sequence for end-of-scrap@> + break; + case '\n': fputs("@@\\\\\n\\mbox{}\\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) + @<Write abbreviated definition list@> + else { + putc('?', file); + fprintf(stderr, "%s: scrap never defined <%s>\n", + command_name, name->spelling); + } + fputs("}$\\rangle$\\verb@@", file); +}@} + + +@d Write abbreviated definition list +@{{ + Scrap_Node *p = name->defs; + write_single_scrap_ref(file, p->scrap); + p = p->next; + if (p) + fputs(", \\ldots\\ ", file); +}@} + + +\subsection{Generating the Indices} + +@d Write index of file names +@{{ + if (file_names) { + fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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 ", 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 { + putc(' ', tex_file); + write_single_scrap_ref(tex_file, p->scrap); + putc('.', tex_file); + } + putc('}', tex_file); +}@} + +@d Write defining scrap numbers +@{{ + Scrap_Node *p = name->defs; + if (p) { + int page; + write_scrap_ref(tex_file, p->scrap, TRUE, &page); + p = p->next; + while (p) { + write_scrap_ref(tex_file, p->scrap, FALSE, &page); + p = p->next; + } + } + 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 { + putc(' ', tex_file); + write_single_scrap_ref(tex_file, p->scrap); + putc('.', tex_file); + } + } + else + fputs("Not referenced.", tex_file); + putc('}', tex_file); +}@} + + +@d Write index of user-specified names +@{{ + if (user_names) { + fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", + tex_file); + fputs("\\setlength{\\itemindent}{-\\leftmargin}}\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; + if (uses) { + int page; + Scrap_Node *defs = name->defs; + fprintf(tex_file, "\\item \\verb@@%s@@: ", name->spelling); + if (uses->scrap < defs->scrap) { + write_scrap_ref(tex_file, uses->scrap, TRUE, &page); + uses = uses->next; + } + else { + if (defs->scrap == uses->scrap) + uses = uses->next; + fputs("\\underline{", tex_file); + write_single_scrap_ref(tex_file, defs->scrap); + putc('}', tex_file); + page = -2; + defs = defs->next; + } + while (uses || defs) { + if (uses && (!defs || uses->scrap < defs->scrap)) { + write_scrap_ref(tex_file, uses->scrap, FALSE, &page); + uses = uses->next; + } + else { + if (uses && defs->scrap == uses->scrap) + uses = uses->next; + fputs(", \\underline{", tex_file); + write_single_scrap_ref(tex_file, defs->scrap); + putc('}', tex_file); + page = -2; + defs = defs->next; + } + } + fputs(".\n", tex_file); + } +}@} + +\section{Writing the LaTeX File with HTML Scraps} \label{html-file} + +The HTML generated is patterned closely upon the {\LaTeX} generated in +the previous section.\footnote{\relax While writing this section, I +tried to follow Preston's style as displayed in +Section~\ref{latex-file}---J. D. R.} When a file name ends in +\verb|.hw|, the second pass (invoked via a call to \verb|write_html|) +copies most of the text from the source file straight into a +\verb|.tex| file. Definitions are formatted slightly and +cross-reference information is printed out. + +@d Function... +@{extern void write_html(); +@} + +We need a few local function declarations before we get into the body +of \verb|write_html|. + +@o html.c +@{static void copy_scrap(); /* formats the body of a scrap */ +static void display_scrap_ref(); /* formats a scrap reference */ +static void display_scrap_numbers(); /* formats a list of scrap numbers */ +static void print_scrap_numbers(); /* pluralizes scrap formats list */ +static void format_entry(); /* formats an index entry */ +static void format_user_entry(); +@} + + +The routine \verb|write_html| takes two file names as parameters: the +name of the web source file and the name of the \verb|.tex| output file. +@o html.c +@{void write_html(file_name, html_name) + char *file_name; + char *html_name; +{ + FILE *html_file = fopen(html_name, "w"); + if (html_file) { + if (verbose_flag) + fprintf(stderr, "writing %s\n", html_name); + source_open(file_name); + @<Copy \verb|source_file| into \verb|html_file|@> + fclose(html_file); + } + else + fprintf(stderr, "%s: can't open %s\n", command_name, html_name); +} +@| write_html @} + + +We make our second (and final) pass through the source web, this time +copying characters straight into the \verb|.tex| file. However, we keep +an eye peeled for \verb|@@|~characters, which signal a command sequence. + +@d Copy \verb|source_file| into \verb|html_file| +@{{ + int scraps = 1; + int c = source_get(); + while (c != EOF) { + if (c == '@@') + @<Interpret HTML at-sequence@> + else { + putc(c, html_file); + c = source_get(); + } + } +}@} + +@d Interpret HTML at-sequence +@{{ + c = source_get(); + switch (c) { + case 'O': + case 'o': @<Write HTML output file definition@> + break; + case 'D': + case 'd': @<Write HTML macro definition@> + break; + case 'f': @<Write HTML index of file names@> + break; + case 'm': @<Write HTML index of macro names@> + break; + case 'u': @<Write HTML index of user-specified names@> + break; + case '@@': putc(c, html_file); + default: c = source_get(); + break; + } +}@} + + +\subsection{Formatting Definitions} + +We go through only a little amount of effort to format a definition. +The HTML for the previous macro definition should look like this +(perhaps modulo the scrap references): + +\begin{verbatim} +<pre> +<a name="nuweb68"><Interpret HTML at-sequence 68></a> = +{ + c = source_get(); + switch (c) { + case 'O': + case 'o': <Write HTML output file definition <a href="#nuweb69">69</a>> + break; + case 'D': + case 'd': <Write HTML macro definition <a href="#nuweb71">71</a>> + break; + case 'f': <Write HTML index of file names <a href="#nuweb86">86</a>> + break; + case 'm': <Write HTML index of macro names <a href="#nuweb87">87</a>> + break; + case 'u': <Write HTML index of user-specified names <a href="#nuweb93">93</a>> + break; + case '@@': putc(c, html_file); + default: c = source_get(); + break; + } +}<></pre> +Macro referenced in scrap <a href="#nuweb67">67</a>. +<br> +\end{verbatim} + +Macro and file definitions are formatted nearly identically. +I've factored the common parts out into separate scraps. + +@d Write HTML output file definition +@{{ + Name *name = collect_file_name(); + @<Begin HTML scrap environment@> + @<Write HTML output file declaration@> + scraps++; + @<Fill in the middle of HTML scrap environment@> + @<Write HTML file defs@> + @<Finish HTML scrap environment@> +}@} + +@d Write HTML output file declaration +@{ fputs("<a name=\"nuweb", html_file); + write_single_scrap_ref(html_file, scraps); + fprintf(html_file, "\"><code>\"%s\"</code> ", name->spelling); + write_single_scrap_ref(html_file, scraps); + fputs("</a> =\n", html_file); +@} + +@d Write HTML macro definition +@{{ + Name *name = collect_macro_name(); + @<Begin HTML scrap environment@> + @<Write HTML macro declaration@> + scraps++; + @<Fill in the middle of HTML scrap environment@> + @<Write HTML macro defs@> + @<Write HTML macro refs@> + @<Finish HTML 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. Note +that in this implementation, programmers may only use directives in +macro names that are recognized in preformatted text elements (PRE). + +@d Write HTML macro declaration +@{ fputs("<a name=\"nuweb", html_file); + write_single_scrap_ref(html_file, scraps); + fprintf(html_file, "\"><%s ", name->spelling); + write_single_scrap_ref(html_file, scraps); + fputs("></a> =\n", html_file); +@} + +@d Begin HTML scrap environment +@{{ + fputs("\\begin{rawhtml}\n", html_file); + fputs("<pre>\n", html_file); +}@} + +The end of a scrap is marked with the characters \verb|<>|. +@d Fill in the middle of HTML scrap environment +@{{ + copy_scrap(html_file); + fputs("<></pre>\n", html_file); +}@} + +The only task remaining is to get rid of the current at command and +end the paragraph. + +@d Finish HTML scrap environment +@{{ + fputs("\\end{rawhtml}\n", html_file); + c = source_get(); /* Get rid of current at command. */ +}@} + + +\subsubsection{Formatting Cross References} + +@d Write HTML file defs +@{{ + if (name->defs->next) { + fputs("File defined by ", html_file); + print_scrap_numbers(html_file, name->defs); + fputs("<br>\n", html_file); + } +}@} + +@d Write HTML macro defs +@{{ + if (name->defs->next) { + fputs("Macro defined by ", html_file); + print_scrap_numbers(html_file, name->defs); + fputs("<br>\n", html_file); + } +}@} + +@d Write HTML macro refs +@{{ + if (name->uses) { + fputs("Macro referenced in ", html_file); + print_scrap_numbers(html_file, name->uses); + } + else { + fputs("Macro never referenced.\n", html_file); + fprintf(stderr, "%s: <%s> never referenced.\n", + command_name, name->spelling); + } + fputs("<br>\n", html_file); +}@} + +@o html.c +@{static void display_scrap_ref(html_file, num) + FILE *html_file; + int num; +{ + fputs("<a href=\"#nuweb", html_file); + write_single_scrap_ref(html_file, num); + fputs("\">", html_file); + write_single_scrap_ref(html_file, num); + fputs("</a>", html_file); +} +@| display_scrap_ref @} + +@o html.c +@{static void display_scrap_numbers(html_file, scraps) + FILE *html_file; + Scrap_Node *scraps; +{ + display_scrap_ref(html_file, scraps->scrap); + scraps = scraps->next; + while (scraps) { + fputs(", ", html_file); + display_scrap_ref(html_file, scraps->scrap); + scraps = scraps->next; + } +} +@| display_scrap_numbers @} + +@o html.c +@{static void print_scrap_numbers(html_file, scraps) + FILE *html_file; + Scrap_Node *scraps; +{ + fputs("scrap", html_file); + if (scraps->next) fputc('s', html_file); + fputc(' ', html_file); + display_scrap_numbers(html_file, scraps); + fputs(".\n", html_file); +} +@| print_scrap_numbers @} + + +\subsubsection{Formatting a Scrap} + +We must translate HTML special keywords into entities in scraps. + +@o html.c +@{static void copy_scrap(file) + FILE *file; +{ + int indent = 0; + int c = source_get(); + while (1) { + switch (c) { + case '@@': @<Check HTML at-sequence for end-of-scrap@> + break; + case '<' : fputs("<", file); + indent++; + break; + case '>' : fputs(">", file); + indent++; + break; + case '&' : fputs("&", file); + indent++; + break; + case '\n': fputc(c, file); + indent = 0; + break; + case '\t': @<Expand tab into spaces@> + break; + default: putc(c, file); + indent++; + break; + } + c = source_get(); + } +} +@| copy_scrap @} + +@d Check HTML at-sequence... +@{{ + c = source_get(); + switch (c) { + case '@@': fputc(c, file); + break; + case '|': @<Skip over index entries@> + case '}': return; + case '<': @<Format HTML 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 Format HTML macro name +@{{ + Name *name = collect_scrap_name(); + fprintf(file, "<%s ", name->spelling); + if (name->defs) + @<Write HTML abbreviated definition list@> + else { + putc('?', file); + fprintf(stderr, "%s: scrap never defined <%s>\n", + command_name, name->spelling); + } + fputs(">", file); +}@} + + +@d Write HTML abbreviated definition list +@{{ + Scrap_Node *p = name->defs; + display_scrap_ref(file, p->scrap); + if (p->next) + fputs(", ... ", file); +}@} + + +\subsection{Generating the Indices} + +@d Write HTML index of file names +@{{ + if (file_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_entry(file_names, html_file, TRUE); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); +}@} + + +@d Write HTML index of macro names +@{{ + if (macro_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_entry(macro_names, html_file, FALSE); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); +}@} + + +@o html.c +@{static void format_entry(name, html_file, file_flag) + Name *name; + FILE *html_file; + int file_flag; +{ + while (name) { + format_entry(name->llink, html_file, file_flag); + @<Format an HTML index entry@> + name = name->rlink; + } +} +@| format_entry @} + + +@d Format an HTML index entry +@{{ + fputs("<dt> ", html_file); + if (file_flag) { + fprintf(html_file, "<code>\"%s\"</code>\n<dd> ", name->spelling); + @<Write HTML file's defining scrap numbers@> + } + else { + fprintf(html_file, "<%s ", name->spelling); + @<Write HTML defining scrap numbers@> + fputs(">\n<dd> ", html_file); + @<Write HTML referencing scrap numbers@> + } + putc('\n', html_file); +}@} + + +@d Write HTML file's defining scrap numbers +@{{ + fputs("Defined by ", html_file); + print_scrap_numbers(html_file, name->defs); +}@} + +@d Write HTML defining scrap numbers +@{{ + if (name->defs) + display_scrap_numbers(html_file, name->defs); + else + putc('?', html_file); +}@} + +@d Write HTML referencing scrap numbers +@{{ + Scrap_Node *p = name->uses; + if (p) { + fputs("Referenced in ", html_file); + print_scrap_numbers(html_file, p); + } + else + fputs("Not referenced.\n", html_file); +}@} + + +@d Write HTML index of user-specified names +@{{ + if (user_names) { + fputs("\\begin{rawhtml}\n", html_file); + fputs("<dl compact>\n", html_file); + format_user_entry(user_names, html_file); + fputs("</dl>\n", html_file); + fputs("\\end{rawhtml}\n", html_file); + } + c = source_get(); +}@} + + +@o html.c +@{static void format_user_entry(name, html_file) + Name *name; + FILE *html_file; +{ + while (name) { + format_user_entry(name->llink, html_file); + @<Format a user HTML index entry@> + name = name->rlink; + } +} +@| format_user_entry @} + + +@d Format a user HTML index entry +@{{ + Scrap_Node *uses = name->uses; + if (uses) { + Scrap_Node *defs = name->defs; + fprintf(html_file, "<dt><code>%s</code>:\n<dd> ", name->spelling); + if (uses->scrap < defs->scrap) { + display_scrap_ref(html_file, uses->scrap); + uses = uses->next; + } + else { + if (defs->scrap == uses->scrap) + uses = uses->next; + fputs("<strong>", html_file); + display_scrap_ref(html_file, defs->scrap); + fputs("</strong>", html_file); + defs = defs->next; + } + while (uses || defs) { + fputs(", ", html_file); + if (uses && (!defs || uses->scrap < defs->scrap)) { + display_scrap_ref(html_file, uses->scrap); + uses = uses->next; + } + else { + if (uses && defs->scrap == uses->scrap) + uses = uses->next; + fputs("<strong>", html_file); + display_scrap_ref(html_file, defs->scrap); + fputs("</strong>", html_file); + defs = defs->next; + } + } + fputs(".\n", html_file); + } +}@} + +\section{Writing the Output Files} \label{output-files} + +@d Function pro... +@{extern void write_files(); +@} + +@o output.c +@{void write_files(files) + Name *files; +{ + while (files) { + write_files(files->llink); + @<Write out \verb|files->spelling|@> + files = files->rlink; + } +} +@| write_files @} + +We call \verb|tempnam|, causing it to create a file name in the +current directory. This could cause a problem for \verb|rename| if +the eventual output file will reside on a different file system. +Perhaps it would be better to examine \verb|files->spelling| to find +any directory information. + +Note the superfluous call to \verb|remove| before \verb|rename|. +We're using it get around a bug in some implementations of +\verb|rename|. + +@d Write out \verb|files->spelling| +@{{ + char indent_chars[500]; + FILE *temp_file; + char *temp_name = tempnam(".", 0); + temp_file = fopen(temp_name, "w"); + if (!temp_file) { + fprintf(stderr, "%s: can't create %s for a temporary file\n", + command_name, temp_name); + exit(-1); + } + if (verbose_flag) + fprintf(stderr, "writing %s\n", files->spelling); + 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 { + remove(files->spelling); + rename(temp_name, files->spelling); + } +}@} + +Again, we use a call to \verb|remove| before \verb|rename|. +@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) + remove(temp_name); + else { + remove(files->spelling); + 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... +@{extern void source_open(); /* pass in the name of the source file */ +extern 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 variable dec... +@{extern char *source_name; /* name of the current file */ +extern int source_line; /* current line in the source file */ +@| source_name source_line @} + +@d Global variable def... +@{char *source_name = NULL; +int source_line = 0; +@} + +\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 +@{static struct { + FILE *file; + char *name; + int line; +} stack[10]; +@| stack @} + + +\subsection{Reading a File} + +The routine \verb|source_get| returns the next character from the +current source file. It notices newlines and keeps the line counter +\verb|source_line| up to date. It also catches \verb|EOF| and watches +for \verb|@@|~characters. All other characters are immediately returned. +@o input.c +@{int source_get() +{ + int c = source_peek; + switch (c) { + case EOF: @<Handle \verb|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 \verb|@@|~character handling mess is pretty annoying. +I want to recognize \verb|@@i| so I can handle include files correctly. +At the same time, it makes sense to recognize illegal \verb|@@|~sequences +and complain; this avoids ever having to check anywhere else. +Unfortunately, I need to avoid tripping over the \verb|@@@@|~sequence; +hence this whole unsatisfactory \verb|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 +@{{ + char name[100]; + if (include_depth >= 10) { + fprintf(stderr, "%s: include nesting too deep (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + @<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(); +}@} + +@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 \verb|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 \verb|EOF| is returned. +@d Handle \verb|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 \verb|source_open| takes a file name and tries to open the +file. If unsuccessful, it complains and halts. Otherwise, it sets +\verb|source_name|, \verb|source_line|, and \verb|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; + int page; + char letter; + Slab *slab; +} ScrapEntry; +@| ScrapEntry @} + +@o scraps.c +@{static ScrapEntry *SCRAP[256]; + +#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255] + +static int scraps; +@| scraps scrap_array SCRAP @} + + +@d Function pro... +@{extern void init_scraps(); +extern int collect_scrap(); +extern int write_scraps(); +extern void write_scrap_ref(); +extern void write_single_scrap_ref(); +@} + + +@o scraps.c +@{void init_scraps() +{ + scraps = 1; + SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry)); +} +@| init_scraps @} + + +@o scraps.c +@{void write_scrap_ref(file, num, first, page) + FILE *file; + int num; + int first; + int *page; +{ + if (scrap_array(num).page >= 0) { + if (first) + fprintf(file, "%d", scrap_array(num).page); + else if (scrap_array(num).page != *page) + fprintf(file, ", %d", scrap_array(num).page); + if (scrap_array(num).letter > 0) + fputc(scrap_array(num).letter, file); + } + else { + if (first) + putc('?', file); + else + fputs(", ?", file); + @<Warn (only once) about needing to rerun after Latex@> + } + *page = scrap_array(num).page; +} +@| write_scrap_ref @} + +@o scraps.c +@{void write_single_scrap_ref(file, num) + FILE *file; + int num; +{ + int page; + write_scrap_ref(file, num, TRUE, &page); +} +@| write_single_scrap_ref @} + + +@d Warn (only once) about needing to... +@{{ + if (!already_warned) { + fprintf(stderr, "%s: you'll need to rerun nuweb after running latex\n", + command_name); + already_warned = TRUE; + } +}@} + +@d Global variable dec... +@{extern int already_warned; +@| already_warned @} + +@d Global variable def... +@{int already_warned = 0; +@} + +@o scraps.c +@{typedef struct { + Slab *scrap; + Slab *prev; + 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 \verb|scraps++|@> +} +@| collect_scrap @} + +@d Create new scrap, managed by \verb|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; + scrap_array(scraps).page = -1; + scrap_array(scraps).letter = 0; + 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 \verb|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->prev = scrap; + 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(¯o_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 \verb|defs->scrap| to \verb|file|@> + defs = defs->next; + } + return indent + global_indent; +} +@| write_scraps @} + + +@d Copy \verb|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 \verb|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); +}@} + + +\subsection{Collecting Page Numbers} + +@d Function... +@{extern void collect_numbers(); +@} + +@o scraps.c +@{void collect_numbers(aux_name) + char *aux_name; +{ + if (number_flag) { + int i; + for (i=1; i<scraps; i++) + scrap_array(i).page = i; + } + else { + FILE *aux_file = fopen(aux_name, "r"); + already_warned = FALSE; + if (aux_file) { + char aux_line[500]; + while (fgets(aux_line, 500, aux_file)) { + int scrap_number; + int page_number; + char dummy[50]; + if (3 == sscanf(aux_line, "\\newlabel{scrap%d}{%[^}]}{%d}", + &scrap_number, dummy, &page_number)) { + if (scrap_number < scraps) + scrap_array(scrap_number).page = page_number; + else + @<Warn...@> + } + } + fclose(aux_file); + @<Add letters to scraps with duplicate page numbers@> + } + } +} +@| collect_numbers @} + +@d Add letters to scraps with... +@{{ + int scrap; + for (scrap=2; scrap<scraps; scrap++) { + if (scrap_array(scrap-1).page == scrap_array(scrap).page) { + if (!scrap_array(scrap-1).letter) + scrap_array(scrap-1).letter = 'a'; + scrap_array(scrap).letter = scrap_array(scrap-1).letter + 1; + } + } +}@} + + +\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 variable dec... +@{extern Name *file_names; +extern Name *macro_names; +extern Name *user_names; +@| file_names macro_names user_names @} + +@d Global variable def... +@{Name *file_names = NULL; +Name *macro_names = NULL; +Name *user_names = NULL; +@} + +@d Function pro... +@{extern Name *collect_file_name(); +extern Name *collect_macro_name(); +extern Name *collect_scrap_name(); +extern Name *name_add(); +extern Name *prefix_add(); +extern char *save_string(); +extern 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 +@{static int ambiguous_prefix(); + +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 \verb|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 \verb|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() != '{') { + fprintf(stderr, "%s: expected @@{ after file name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + return new_name; +} +@| 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); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +@| 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(¯o_names, name); +}@} + + +@d Skip until scrap... +@{{ + do + c = source_get(); + while (isspace(c)); + if (c != '@@' || source_get() != '{') { + fprintf(stderr, "%s: expected @@{ after macro name (%s, %d)\n", + command_name, source_name, start_line); + exit(-1); + } + @<Cleanup and install name@> +}@} + + +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)) { + fprintf(stderr, + "%s: unexpected character in macro name (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + } + *p++ = c; + c = source_get(); + break; + } + } + fprintf(stderr, "%s: unexpected end of file (%s, %d)\n", + command_name, source_name, source_line); + exit(-1); + return NULL; /* unreachable return to avoid warnings on some compilers */ +} +@| 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 occurrences 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; +@| Name_Node @} + +@o scraps.c +@{typedef struct goto_node { + Name_Node *output; /* list of words ending in this state */ + struct move_node *moves; /* list of possible moves */ + struct goto_node *fail; /* and where to go when no move fits */ + struct goto_node *next; /* next goto node with same depth */ +} Goto_Node; +@| Goto_Node @} + +@o scraps.c +@{typedef struct move_node { + struct move_node *next; + Goto_Node *state; + char c; +} Move_Node; +@| Move_Node @} + +@o scraps.c +@{static Goto_Node *root[128]; +static int max_depth; +static Goto_Node **depths; +@| root max_depth depths @} + + +@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} + + +@d Function pro... +@{extern void search(); +@} + +@o scraps.c +@{static void build_gotos(); +static int reject_match(); + +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 \verb|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.prev = 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]; + c = pop(&reader); + if (state && state->output) { + Name_Node *p = state->output; + do { + Name *name = p->name; + if (!reject_match(name, c, &reader) && + (!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); + } + } + } +}@} + + +\subsubsection{Rejecting Matches} + +A problem with simple substring matching is that the string ``he'' +would match longer strings like ``she'' and ``her.'' Norman Ramsey +suggested examining the characters occurring immediately before and +after a match and rejecting the match if it appears to be part of a +longer token. Of course, the concept of {\sl token\/} is +language-dependent, so we may be occasionally mistaken. +For the present, we'll consider the mechanism an experiment. + +@o scraps.c +@{#define sym_char(c) (isalnum(c) || (c) == '_') + +static int op_char(c) + char c; +{ + switch (c) { + case '!': case '@@': case '#': case '%': case '$': case '^': + case '&': case '*': case '-': case '+': case '=': case '/': + case '|': case '~': case '<': case '>': + return TRUE; + default: + return FALSE; + } +} +@| sym_char op_char @} + +@o scraps.c +@{static int reject_match(name, post, reader) + Name *name; + char post; + Manager *reader; +{ + int len = strlen(name->spelling); + char first = name->spelling[0]; + char last = name->spelling[len - 1]; + char prev = '\0'; + len = reader->index - len - 2; + if (len >= 0) + prev = reader->scrap->chars[len]; + else if (reader->prev) + prev = reader->scrap->chars[SLAB_SIZE - len]; + if (sym_char(last) && sym_char(post)) return TRUE; + if (sym_char(first) && sym_char(prev)) return TRUE; + if (op_char(last) && op_char(post)) return TRUE; + if (op_char(first) && op_char(prev)) return TRUE; + return FALSE; +} +@| reject_match @} + + + + + +\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 +\verb|arena_getmem(n)|, where \verb|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 \verb|arena_free()|. Both operations +are quite fast. +@d Function p... +@{extern void *arena_getmem(); +extern void arena_free(); +@} + + +@o arena.c +@{typedef struct chunk { + struct chunk *next; + char *limit; + char *avail; +} Chunk; +@| Chunk @} + + +We define an empty chunk called \verb|first|. The variable \verb|arena| points +at the current chunk of memory; it's initially pointed at \verb|first|. +As soon as some storage is required, a ``real'' chunk of memory will +be allocated and attached to \verb|first->next|; storage will be +allocated from the new chunk (and later chunks if necessary). +@o arena.c +@{static Chunk first = { NULL, NULL, NULL }; +static Chunk *arena = &first; +@| first arena @} + + +\subsection{Allocating Memory} + +The routine \verb|arena_getmem(n)| returns a pointer to (at least) +\verb|n| bytes of memory. Note that \verb|n| is rounded up to ensure +that returned pointers are always aligned. We align to the nearest +8~byte segment, since that'll satisfy the more common 2-byte and +4-byte alignment restrictions too. + +@o arena.c +@{void *arena_getmem(n) + size_t n; +{ + char *q; + char *p = arena->avail; + n = (n + 7) & ~7; /* ensuring alignment to 8 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 \verb|n| +bytes) we examine the rest of the list of chunks (starting at +\verb|arena->next|) looking for a chunk with adequate space. If \verb|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... +@{{ + size_t 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 \verb|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 identifiers 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 + +\fi + +\bibliographystyle{plain} +\bibliography{literate} + +\end{document} diff --git a/web/nuweb/nuweb0.87b/nuwebdoc.tex b/web/nuweb/nuweb0.87b/nuwebdoc.tex new file mode 100644 index 0000000000..3d6a836d76 --- /dev/null +++ b/web/nuweb/nuweb0.87b/nuwebdoc.tex @@ -0,0 +1,5796 @@ +\documentstyle{report} +\newif\ifshowcode +\showcodefalse + +\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 Version 0.87b \\ A Simple Literate Programming Tool} +\date{} +\author{Preston Briggs\thanks{This work has been supported by ARPA, +through ONR grant N00014-91-J-1989.} +\\ \sl preston@cs.rice.edu +\\ HTML scrap generator by John D. Ramsdell +\\ \sl ramsdell@mitre.org} + +\begin{document} +\pagenumbering{roman} +\maketitle +\tableofcontents + +\chapter{Introduction} +\pagenumbering{arabic} + +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, \verb|WEB|, that offered programmers a superior +approach to programming. He wrote several programs in \verb|WEB|, +including \verb|weave| and \verb|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 \verb|tangle| on the web file would produce a complete +Pascal program, ready for compilation by an ordinary Pascal compiler. +The primary function of \verb|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 \verb|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 \verb|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 +\verb|WEB|\@. Some people have even built web-like tools for their +own favorite combinations of programming language and typesetting +language. For example, \verb|CWEB|, Knuth's current system of choice, +works with a combination of C (or 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 and a number of features 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 \verb|WEB| and \verb|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 \verb|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 \verb|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 \verb|tangle| and \verb|weave| into + a single program that performs both functions at once. +\item[Page numbers] Inspired by the example of noweb, nuweb refers to + all scraps by page number to simplify navigation. If there are + multiple scraps on a page (say page~17), they are distinguished by + lower-case letters ({\em e.g.,} 17a, 17b, and so forth). +\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 \verb|make| (or similar tools), +since \verb|make| will avoid recompiling untouched output files. + +\subsection{Nuweb and HTML} + +In addition to producing {\LaTeX} source, nuweb can be used to +generate HyperText Markup Language (HTML), the markup language used by +the World Wide Web. HTML provides hypertext links. When a HTML +document is viewed online, a user can navigate within the document by +activating the links. The tools which generate HTML automatically +produce hypertext links from a nuweb source. + + +\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 documentation file---unless a nuweb command +is discovered. All nuweb commands begin with an ``at-sign'' +(\verb|@|). 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 documentation 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} +\verb|@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} +\verb|@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 a program file or a documentation 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 \verb|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 explicitly specified for inclusion in the +\verb|@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 or \verb|@| characters) will do. +Therefore, it's possible to add index entries for things like +\verb|<<=| 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, \verb|.w| will be appended. {\LaTeX} suitable for +translation into HTML by {\LaTeX}2HTML will be produced from +files whose name ends with \verb|.hw|, otherwise, ordinary {\LaTeX} will be +produced. While a file name may specify a file in another directory, +the resulting documentation 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 \verb|/foo/bar/quux.w| and will create the +file \verb|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 documentation 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} +\verb|nuweb -to /foo/bar/quux| +\end{quote} +would simply scan the input and produce no output at all. + +There are two additional command-line flags: +\begin{description} +\item[\tt -v] For ``verbose,'' causes nuweb to write information about + its progress to \verb|stderr|. +\item[\tt -n] Forces scraps to be numbered sequentially from~1 + (instead of using page numbers). This form is perhaps more desirable + for small webs. +\end{description} + +\section{Generating HTML} + +Nikos Drakos' {\LaTeX}2HTML Version 0.5.3~\cite{drakos:94} can be used +to translate {\LaTeX} with embedded HTML scraps into HTML\@. Be sure +to include the document-style option \verb|html| so that {\LaTeX} will +understand the hypertext commands. When translating into HTML, do not +allow a document to be split by specifying ``\verb|-split 0|''. +You need not generate navigation links, so also specify +``\verb|-no_navigation|''. + +While preparing a web, you may want to view the program's scraps without +taking the time to run {\LaTeX}2HTML\@. Simply rename the generated +{\LaTeX} source so that its file name ends with \verb|.html|, and view +that file. The documentations section will be jumbled, but the +scraps will be clear. + +\section{Restrictions} + +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 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 \verb|@| + 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. + When producing HTML, macros are displayed in a preformatted element + (PRE), so macros may contain one or more A, B, I, U, or P elements + or data characters. +\item Anything is allowed in the body of a scrap; however, very + long scraps (horizontally or vertically) may not typeset well. +\item Temporary files (created for comparison to the eventual + output files) are placed in the current directory. Since they may be + renamed to an output file name, all the output files should be on the + same file system as the current directory. +\item Because page numbers cannot be determined until the document has + been typeset, we have to rerun nuweb after \LaTeX\ to obtain a clean + version of the document (very similar to the way we sometimes have + to rerun \LaTeX\ to obtain an up-to-date table of contents after + significant edits). Nuweb will warn (in most cases) when this needs + to be done; in the remaining cases, \LaTeX\ will warn that labels + may have changed. +\end{itemize} +Very long scraps may be allowed to break across a page if declared +with \verb|@O| or \verb|@D| (instead of \verb|@o| and \verb|@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. No distinction is made between the +upper case and lower case forms of these commands when generating +HTML\@. + +\section{Acknowledgements} + +Several people have contributed their times, ideas, and debugging +skills. In particular, I'd like to acknowledge the contributions of +Osman Buyukisik, Manuel Carriba, Adrian Clarke, Tim Harvey, Michael +Lewis, Walter Ravenek, Rob Shillingsburg, Kayvan Sylvan, Dominique +de~Waleffe, and Scott Warren. Of course, most of these people would +never have heard or nuweb (or many other tools) without the efforts of +George Greenwade. + + +\ifshowcode +\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 documentation 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. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap1} +\verb@"global.h"@ {\footnotesize 7a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@@$\langle$Include files {\footnotesize 7b}$\rangle$\verb@@\\ +\mbox{}\verb@@$\langle$Type declarations {\footnotesize 8a, \ldots\ }$\rangle$\verb@@\\ +\mbox{}\verb@@$\langle$Global variable declarations {\footnotesize 10a, \ldots\ }$\rangle$\verb@@\\ +\mbox{}\verb@@$\langle$Function prototypes {\footnotesize 14a, \ldots\ }$\rangle$\verb@@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-2ex} +\end{minipage}\\[4ex] +\end{flushleft} +We'll need at least three of the standard system include files. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap2} +$\langle$Include files {\footnotesize 7b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include <stdlib.h>@\\ +\mbox{}\verb@#include <stdio.h>@\\ +\mbox{}\verb@#include <string.h>@\\ +\mbox{}\verb@#include <ctype.h>@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\newpage +\noindent +I also like to use \verb|TRUE| and \verb|FALSE| in my code. +I'd use an \verb|enum| here, except that some systems seem to provide +definitions of \verb|TRUE| and \verb|FALSE| be default. The following +code seems to work on all the local systems. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap3} +$\langle$Type declarations {\footnotesize 8a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#ifndef FALSE@\\ +\mbox{}\verb@#define FALSE 0@\\ +\mbox{}\verb@#endif@\\ +\mbox{}\verb@#ifndef TRUE@\\ +\mbox{}\verb@#define TRUE 1@\\ +\mbox{}\verb@#endif@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 8a, 52c, 53a. +\item Macro referenced in scrap 7a. +\end{list} +\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 \verb|main.c| will contain the driver for the whole program +(see Section~\ref{main-routine}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap4} +\verb@"main.c"@ {\footnotesize 8b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8b, 9f. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The first pass over the source file is contained in \verb|pass1.c|. +It handles collection of all the file names, macros names, and scraps +(see Section~\ref{pass-one}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap5} +\verb@"pass1.c"@ {\footnotesize 8c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8c, 14b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The \verb|.tex| file is created during a second pass over the source +file. The file \verb|latex.c| contains the code controlling the +construction of the \verb|.tex| file +(see Section~\ref{latex-file}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap6} +\verb@"latex.c"@ {\footnotesize 8d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The file \verb|html.c| contains the code controlling the +construction of the \verb|.tex| file appropriate for use with {\LaTeX}2HTML +(see Section~\ref{html-file}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap7} +\verb@"html.c"@ {\footnotesize 8e }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The code controlling the creation of the output files is in \verb|output.c| +(see Section~\ref{output-files}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap8} +\verb@"output.c"@ {\footnotesize 8f }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8f, 37c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\newpage +\subsection{Support Files} + +The support files contain a variety of support routines used to define +and manipulate the major data abstractions. +The file \verb|input.c| holds all the routines used for referring to +source files (see Section~\ref{source-files}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap9} +\verb@"input.c"@ {\footnotesize 9a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9a, 39d, 40ab, 42c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Creation and lookup of scraps is handled by routines in \verb|scraps.c| +(see Section~\ref{scraps}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap10} +\verb@"scraps.c"@ {\footnotesize 9b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The handling of file names and macro names is detailed in \verb|names.c| +(see Section~\ref{names}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap11} +\verb@"names.c"@ {\footnotesize 9c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Memory allocation and deallocation is handled by routines in \verb|arena.c| +(see Section~\ref{memory-management}). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap12} +\verb@"arena.c"@ {\footnotesize 9d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9d, 70abc, 71c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Finally, for best portability, I seem to need a file containing +(useless!) definitions of all the global variables. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap13} +\verb@"global.c"@ {\footnotesize 9e }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#include "global.h"@\\ +\mbox{}\verb@@$\langle$Global variable definitions {\footnotesize 10b, \ldots\ }$\rangle$\verb@@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-2ex} +\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} \small +\begin{minipage}{\linewidth} \label{scrap14} +\verb@"main.c"@ {\footnotesize 9f }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int main(argc, argv)@\\ +\mbox{}\verb@ int argc;@\\ +\mbox{}\verb@ char **argv;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int arg = 1;@\\ +\mbox{}\verb@ @$\langle$Interpret command-line arguments {\footnotesize 10e, \ldots\ }$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Process the remaining arguments (file names) {\footnotesize 11c}$\rangle$\verb@@\\ +\mbox{}\verb@ exit(0);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8b, 9f. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Command-Line Arguments} + +There are five 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] The verbose flag. Forces output of progress reports. +\item[\tt -n] Forces sequential numbering of scraps (instead of page + numbers). +\end{description} + +\noindent +Global flags are declared for each of the arguments. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap15} +$\langle$Global variable declarations {\footnotesize 10a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern int tex_flag; /* if FALSE, don't emit the documentation file */@\\ +\mbox{}\verb@extern int html_flag; /* if TRUE, emit HTML instead of LaTeX scraps. */@\\ +\mbox{}\verb@extern int output_flag; /* if FALSE, don't emit the output files */@\\ +\mbox{}\verb@extern int compare_flag; /* if FALSE, overwrite without comparison */@\\ +\mbox{}\verb@extern int verbose_flag; /* if TRUE, write progress information */@\\ +\mbox{}\verb@extern int number_flag; /* if TRUE, use a sequential numbering scheme */@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10ac, 39b, 44d, 53b. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The flags are all initialized for correct default behavior. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap16} +$\langle$Global variable definitions {\footnotesize 10b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int tex_flag = TRUE;@\\ +\mbox{}\verb@int html_flag = FALSE;@\\ +\mbox{}\verb@int output_flag = TRUE;@\\ +\mbox{}\verb@int compare_flag = TRUE;@\\ +\mbox{}\verb@int verbose_flag = FALSE;@\\ +\mbox{}\verb@int number_flag = FALSE;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10bd, 39c, 45a, 53c. +\item Macro referenced in scrap 9e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We save the invocation name of the command in a global variable +\verb|command_name| for use in error messages. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap17} +$\langle$Global variable declarations {\footnotesize 10c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern char *command_name;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10ac, 39b, 44d, 53b. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap18} +$\langle$Global variable definitions {\footnotesize 10d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@char *command_name = NULL;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10bd, 39c, 45a, 53c. +\item Macro referenced in scrap 9e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The invocation name is conventionally passed in \verb|argv[0]|. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap19} +$\langle$Interpret command-line arguments {\footnotesize 10e}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@command_name = argv[0];@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10e, 11a. +\item Macro referenced in scrap 9f. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We need to examine the remaining entries in \verb|argv|, looking for +command-line arguments. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap20} +$\langle$Interpret command-line arguments {\footnotesize 11a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@while (arg < argc) {@\\ +\mbox{}\verb@ char *s = argv[arg];@\\ +\mbox{}\verb@ if (*s++ == '-') {@\\ +\mbox{}\verb@ @$\langle$Interpret the argument string \verb|s| {\footnotesize 11b}$\rangle$\verb@@\\ +\mbox{}\verb@ arg++;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else break;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10e, 11a. +\item Macro referenced in scrap 9f. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Several flags can be stacked behind a single minus sign; therefore, +we've got to loop through the string, handling them all. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap21} +$\langle$Interpret the argument string \verb|s| {\footnotesize 11b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char c = *s++;@\\ +\mbox{}\verb@ while (c) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'c': compare_flag = FALSE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'n': number_flag = TRUE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'o': output_flag = FALSE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 't': tex_flag = FALSE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'v': verbose_flag = TRUE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: fprintf(stderr, "%s: unexpected argument ignored. ",@\\ +\mbox{}\verb@ command_name);@\\ +\mbox{}\verb@ fprintf(stderr, "Usage is: %s [-cnotv] file...\n",@\\ +\mbox{}\verb@ command_name);@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = *s++;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 11a. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap22} +$\langle$Process the remaining arguments (file names) {\footnotesize 11c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (arg >= argc) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: expected a file name. ", command_name);@\\ +\mbox{}\verb@ fprintf(stderr, "Usage is: %s [-cnotv] file-name...\n", command_name);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ @$\langle$Handle the file name in \verb|argv[arg]| {\footnotesize 12a}$\rangle$\verb@@\\ +\mbox{}\verb@ arg++;@\\ +\mbox{}\verb@ } while (arg < argc);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 9f. +\end{list} +\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 \verb|.w| as a default. The extended path name will be +kept in a local variable \verb|source_name|. The resulting documentation +file will be written in the current directory; its name will be kept +in the variable \verb|tex_name|. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap23} +$\langle$Handle the file name in \verb|argv[arg]| {\footnotesize 12a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char source_name[100];@\\ +\mbox{}\verb@ char tex_name[100];@\\ +\mbox{}\verb@ char aux_name[100];@\\ +\mbox{}\verb@ @$\langle$Build \verb|source_name| and \verb|tex_name| {\footnotesize 12b}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Process a file {\footnotesize 13}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 11c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +I bump the pointer \verb|p| through all the characters in \verb|argv[arg]|, +copying all the characters into \verb|source_name| (via the pointer +\verb|q|). + +At each slash, I update \verb|trim| to point just past the +slash in \verb|source_name|. The effect is that \verb|trim| will point +at the file name without any leading directory specifications. + +The pointer \verb|dot| is made to point at the file name extension, if +present. If there is no extension, we add \verb|.w| to the source name. +In any case, we create the \verb|tex_name| from \verb|trim|, taking +care to get the correct extension. The \verb|html_flag| is set in +this scrap. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap24} +$\langle$Build \verb|source_name| and \verb|tex_name| {\footnotesize 12b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *p = argv[arg];@\\ +\mbox{}\verb@ char *q = source_name;@\\ +\mbox{}\verb@ char *trim = q;@\\ +\mbox{}\verb@ char *dot = NULL;@\\ +\mbox{}\verb@ char c = *p++;@\\ +\mbox{}\verb@ while (c) {@\\ +\mbox{}\verb@ *q++ = c;@\\ +\mbox{}\verb@ if (c == '/') {@\\ +\mbox{}\verb@ trim = q;@\\ +\mbox{}\verb@ dot = NULL;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else if (c == '.')@\\ +\mbox{}\verb@ dot = q - 1;@\\ +\mbox{}\verb@ c = *p++;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *q = '\0';@\\ +\mbox{}\verb@ if (dot) {@\\ +\mbox{}\verb@ *dot = '\0'; /* produce HTML when the file extension is ".hw" */@\\ +\mbox{}\verb@ html_flag = dot[1] == 'h' && dot[2] == 'w' && dot[3] == '\0';@\\ +\mbox{}\verb@ sprintf(tex_name, "%s.tex", trim);@\\ +\mbox{}\verb@ sprintf(aux_name, "%s.aux", trim);@\\ +\mbox{}\verb@ *dot = '.';@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ sprintf(tex_name, "%s.tex", trim);@\\ +\mbox{}\verb@ sprintf(aux_name, "%s.aux", trim);@\\ +\mbox{}\verb@ *q++ = '.';@\\ +\mbox{}\verb@ *q++ = 'w';@\\ +\mbox{}\verb@ *q = '\0';@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 12a. +\end{list} +\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 four routines \verb|pass1| +(see Section~\ref{pass-one}), \verb|write_tex| (see +Section~\ref{latex-file}), \verb|write_html| (see +Section~\ref{html-file}), and \verb|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}). The sequential numbering of scraps +is forced when generating HTML. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap25} +$\langle$Process a file {\footnotesize 13}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ pass1(source_name);@\\ +\mbox{}\verb@ if (tex_flag) {@\\ +\mbox{}\verb@ if (html_flag) {@\\ +\mbox{}\verb@ int saved_number_flag = number_flag; @\\ +\mbox{}\verb@ number_flag = TRUE;@\\ +\mbox{}\verb@ collect_numbers(aux_name);@\\ +\mbox{}\verb@ write_html(source_name, tex_name);@\\ +\mbox{}\verb@ number_flag = saved_number_flag;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ collect_numbers(aux_name);@\\ +\mbox{}\verb@ write_tex(source_name, tex_name);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (output_flag)@\\ +\mbox{}\verb@ write_files(file_names);@\\ +\mbox{}\verb@ arena_free();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 12a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\newpage +\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} \small +\begin{minipage}{\linewidth} \label{scrap26} +$\langle$Function prototypes {\footnotesize 14a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void pass1();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The routine \verb|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} \small +\begin{minipage}{\linewidth} \label{scrap27} +\verb@"pass1.c"@ {\footnotesize 14b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void pass1(file_name)@\\ +\mbox{}\verb@ char *file_name;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (verbose_flag)@\\ +\mbox{}\verb@ fprintf(stderr, "reading %s\n", file_name);@\\ +\mbox{}\verb@ source_open(file_name);@\\ +\mbox{}\verb@ init_scraps();@\\ +\mbox{}\verb@ macro_names = NULL;@\\ +\mbox{}\verb@ file_names = NULL;@\\ +\mbox{}\verb@ user_names = NULL;@\\ +\mbox{}\verb@ @$\langle$Scan the source file, looking for at-sequences {\footnotesize 14c}$\rangle$\verb@@\\ +\mbox{}\verb@ if (tex_flag)@\\ +\mbox{}\verb@ search();@\\ +\mbox{}\verb@ @$\langle$Reverse cross-reference lists {\footnotesize 16b}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8c, 14b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The only thing we look for in the first pass are the command +sequences. All ordinary text is skipped entirely. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap28} +$\langle$Scan the source file, looking for at-sequences {\footnotesize 14c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (c != EOF) {@\\ +\mbox{}\verb@ if (c == '@{\tt @}\verb@')@\\ +\mbox{}\verb@ @$\langle$Scan at-sequence {\footnotesize 15a}$\rangle$\verb@@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 14b. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap29} +$\langle$Scan at-sequence {\footnotesize 15a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'O':@\\ +\mbox{}\verb@ case 'o': @$\langle$Build output file definition {\footnotesize 15b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'D':@\\ +\mbox{}\verb@ case 'd': @$\langle$Build macro definition {\footnotesize 15c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '@{\tt @}\verb@':@\\ +\mbox{}\verb@ case 'u':@\\ +\mbox{}\verb@ case 'm':@\\ +\mbox{}\verb@ case 'f': /* ignore during this pass */@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: fprintf(stderr,@\\ +\mbox{}\verb@ "%s: unexpected @{\tt @}\verb@ sequence ignored (%s, line %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 14c. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap30} +$\langle$Build output file definition {\footnotesize 15b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_file_name(); /* returns a pointer to the name entry */@\\ +\mbox{}\verb@ int scrap = collect_scrap(); /* returns an index to the scrap */@\\ +\mbox{}\verb@ @$\langle$Add \verb|scrap| to \verb|name|'s definition list {\footnotesize 16a}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 15a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap31} +$\langle$Build macro definition {\footnotesize 15c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_macro_name();@\\ +\mbox{}\verb@ int scrap = collect_scrap();@\\ +\mbox{}\verb@ @$\langle$Add \verb|scrap| to \verb|name|'s definition list {\footnotesize 16a}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 15a. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap32} +$\langle$Add \verb|scrap| to \verb|name|'s definition list {\footnotesize 16a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\ +\mbox{}\verb@ def->scrap = scrap;@\\ +\mbox{}\verb@ def->next = name->defs;@\\ +\mbox{}\verb@ name->defs = def;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 15bc. +\end{list} +\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 \verb|pass1| to +reverse them all so they'll be simpler to print out prettily. +The code for \verb|reverse_lists| appears in Section~\ref{names}. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap33} +$\langle$Reverse cross-reference lists {\footnotesize 16b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ reverse_lists(file_names);@\\ +\mbox{}\verb@ reverse_lists(macro_names);@\\ +\mbox{}\verb@ reverse_lists(user_names);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 14b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\section{Writing the Latex File} \label{latex-file} + +The second pass (invoked via a call to \verb|write_tex|) copies most of +the text from the source file straight into a \verb|.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} \small +\begin{minipage}{\linewidth} \label{scrap34} +$\langle$Function prototypes {\footnotesize 16c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void write_tex();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We need a few local function declarations before we get into the body +of \verb|write_tex|. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap35} +\verb@"latex.c"@ {\footnotesize 16d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void copy_scrap(); /* formats the body of a scrap */@\\ +\mbox{}\verb@static void print_scrap_numbers(); /* formats a list of scrap numbers */@\\ +\mbox{}\verb@static void format_entry(); /* formats an index entry */@\\ +\mbox{}\verb@static void format_user_entry();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The routine \verb|write_tex| takes two file names as parameters: the +name of the web source file and the name of the \verb|.tex| output file. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap36} +\verb@"latex.c"@ {\footnotesize 17a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void write_tex(file_name, tex_name)@\\ +\mbox{}\verb@ char *file_name;@\\ +\mbox{}\verb@ char *tex_name;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ FILE *tex_file = fopen(tex_name, "w");@\\ +\mbox{}\verb@ if (tex_file) {@\\ +\mbox{}\verb@ if (verbose_flag)@\\ +\mbox{}\verb@ fprintf(stderr, "writing %s\n", tex_name);@\\ +\mbox{}\verb@ source_open(file_name);@\\ +\mbox{}\verb@ @$\langle$Copy \verb|source_file| into \verb|tex_file| {\footnotesize 17b}$\rangle$\verb@@\\ +\mbox{}\verb@ fclose(tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fprintf(stderr, "%s: can't open %s\n", command_name, tex_name);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We make our second (and final) pass through the source web, this time +copying characters straight into the \verb|.tex| file. However, we keep +an eye peeled for \verb|@|~characters, which signal a command sequence. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap37} +$\langle$Copy \verb|source_file| into \verb|tex_file| {\footnotesize 17b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int scraps = 1;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (c != EOF) {@\\ +\mbox{}\verb@ if (c == '@{\tt @}\verb@')@\\ +\mbox{}\verb@ @$\langle$Interpret at-sequence {\footnotesize 18}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc(c, tex_file);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 17a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap38} +$\langle$Interpret at-sequence {\footnotesize 18}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int big_definition = FALSE;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'O': big_definition = TRUE;@\\ +\mbox{}\verb@ case 'o': @$\langle$Write output file definition {\footnotesize 19a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'D': big_definition = TRUE;@\\ +\mbox{}\verb@ case 'd': @$\langle$Write macro definition {\footnotesize 19b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'f': @$\langle$Write index of file names {\footnotesize 23d}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'm': @$\langle$Write index of macro names {\footnotesize 24a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'u': @$\langle$Write index of user-specified names {\footnotesize 26a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': putc(c, tex_file);@\\ +\mbox{}\verb@ default: c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 17b. +\end{list} +\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 references): +{\small +\begin{verbatim} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap37} +$\langle$Interpret at-sequence {\footnotesize 18}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int big_definition = FALSE;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'O': big_definition = TRUE;@\\ +\mbox{}\verb@ case 'o': @$\langle$Write output file definition {\footnotesize 19a}$\rangle$\verb@@\\ +\end{verbatim} +\vdots +\begin{verbatim} +\mbox{}\verb@ case '@{\tt @}\verb@': putc(c, tex_file);@\\ +\mbox{}\verb@ default: c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 17b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\end{verbatim}} + +\noindent +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 \verb|@| 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} \small +\begin{minipage}{\linewidth} \label{scrap39} +$\langle$Write output file definition {\footnotesize 19a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_file_name();@\\ +\mbox{}\verb@ @$\langle$Begin the scrap environment {\footnotesize 19c}$\rangle$\verb@@\\ +\mbox{}\verb@ fprintf(tex_file, "\\verb@{\tt @}\verb@\"%s\"@{\tt @}\verb@ {\\footnotesize ", name->spelling);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, scraps++);@\\ +\mbox{}\verb@ fputs(" }$\\equiv$\n", tex_file);@\\ +\mbox{}\verb@ @$\langle$Fill in the middle of the scrap environment {\footnotesize 19d}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write file defs {\footnotesize 20b}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Finish the scrap environment {\footnotesize 20a}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 18. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap40} +$\langle$Write macro definition {\footnotesize 19b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_macro_name();@\\ +\mbox{}\verb@ @$\langle$Begin the scrap environment {\footnotesize 19c}$\rangle$\verb@@\\ +\mbox{}\verb@ fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, scraps++);@\\ +\mbox{}\verb@ fputs("}$\\rangle\\equiv$\n", tex_file);@\\ +\mbox{}\verb@ @$\langle$Fill in the middle of the scrap environment {\footnotesize 19d}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write macro defs {\footnotesize 20c}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write macro refs {\footnotesize 21a}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Finish the scrap environment {\footnotesize 20a}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 18. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap41} +$\langle$Begin the scrap environment {\footnotesize 19c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\begin{flushleft} \\small", tex_file);@\\ +\mbox{}\verb@ if (!big_definition)@\\ +\mbox{}\verb@ fputs("\n\\begin{minipage}{\\linewidth}", tex_file);@\\ +\mbox{}\verb@ fprintf(tex_file, " \\label{scrap%d}\n", scraps);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 19ab. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap42} +$\langle$Fill in the middle of the scrap environment {\footnotesize 19d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\vspace{-1ex}\n\\begin{list}{}{} \\item\n", tex_file);@\\ +\mbox{}\verb@ copy_scrap(tex_file);@\\ +\mbox{}\verb@ fputs("$\\Diamond$\n\\end{list}\n", tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 19ab. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap43} +$\langle$Finish the scrap environment {\footnotesize 20a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (!big_definition)@\\ +\mbox{}\verb@ fputs("\\end{minipage}\\\\[4ex]\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\end{flushleft}\n", tex_file);@\\ +\mbox{}\verb@ do@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (isspace(c));@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 19ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsubsection{Formatting Cross References} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap44} +$\langle$Write file defs {\footnotesize 20b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->defs->next) {@\\ +\mbox{}\verb@ fputs("\\vspace{-1ex}\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file);@\\ +\mbox{}\verb@ fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\item File defined by scraps ", tex_file);@\\ +\mbox{}\verb@ print_scrap_numbers(tex_file, name->defs);@\\ +\mbox{}\verb@ fputs("\\end{list}\n", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fputs("\\vspace{-2ex}\n", tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 19a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap45} +$\langle$Write macro defs {\footnotesize 20c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\vspace{-1ex}\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\footnotesize\\addtolength{\\baselineskip}{-1ex}\n", tex_file);@\\ +\mbox{}\verb@ fputs("\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}", tex_file);@\\ +\mbox{}\verb@ fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file);@\\ +\mbox{}\verb@ if (name->defs->next) {@\\ +\mbox{}\verb@ fputs("\\item Macro defined by scraps ", tex_file);@\\ +\mbox{}\verb@ print_scrap_numbers(tex_file, name->defs);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 19b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap46} +$\langle$Write macro refs {\footnotesize 21a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->uses) {@\\ +\mbox{}\verb@ if (name->uses->next) {@\\ +\mbox{}\verb@ fputs("\\item Macro referenced in scraps ", tex_file);@\\ +\mbox{}\verb@ print_scrap_numbers(tex_file, name->uses);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fputs("\\item Macro referenced in scrap ", tex_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, name->uses->scrap);@\\ +\mbox{}\verb@ fputs(".\n", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fputs("\\item Macro never referenced.\n", tex_file);@\\ +\mbox{}\verb@ fprintf(stderr, "%s: <%s> never referenced.\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs("\\end{list}\n", tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 19b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap47} +\verb@"latex.c"@ {\footnotesize 21b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void print_scrap_numbers(tex_file, scraps)@\\ +\mbox{}\verb@ FILE *tex_file;@\\ +\mbox{}\verb@ Scrap_Node *scraps;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int page;@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, scraps->scrap, TRUE, &page);@\\ +\mbox{}\verb@ scraps = scraps->next;@\\ +\mbox{}\verb@ while (scraps) {@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, scraps->scrap, FALSE, &page);@\\ +\mbox{}\verb@ scraps = scraps->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs(".\n", tex_file);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsubsection{Formatting a Scrap} + +We add a \verb|\mbox{}| at the beginning of each line to avoid +problems with older versions of \TeX. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap48} +\verb@"latex.c"@ {\footnotesize 22a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void copy_scrap(file)@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int indent = 0;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ fputs("\\mbox{}\\verb@{\tt @}\verb@", file);@\\ +\mbox{}\verb@ while (1) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Check at-sequence for end-of-scrap {\footnotesize 22c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\n': fputs("@{\tt @}\verb@\\\\\n\\mbox{}\\verb@{\tt @}\verb@", file);@\\ +\mbox{}\verb@ indent = 0;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\t': @$\langle$Expand tab into spaces {\footnotesize 22b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: putc(c, file);@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap49} +$\langle$Expand tab into spaces {\footnotesize 22b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int delta = 8 - (indent % 8);@\\ +\mbox{}\verb@ indent += delta;@\\ +\mbox{}\verb@ while (delta > 0) {@\\ +\mbox{}\verb@ putc(' ', file);@\\ +\mbox{}\verb@ delta--;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 22a, 33a, 51a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap50} +$\langle$Check at-sequence for end-of-scrap {\footnotesize 22c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': fputs("@{\tt @}\verb@{\\tt @{\tt @}\verb@}\\verb@{\tt @}\verb@", file);@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '|': @$\langle$Skip over index entries {\footnotesize 23a}$\rangle$\verb@@\\ +\mbox{}\verb@ case '}': putc('@{\tt @}\verb@', file);@\\ +\mbox{}\verb@ return;@\\ +\mbox{}\verb@ case '<': @$\langle$Format macro name {\footnotesize 23b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: /* ignore these since pass1 will have warned about them */@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 22a. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap51} +$\langle$Skip over index entries {\footnotesize 23a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ do@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (c != '@{\tt @}\verb@');@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ } while (c != '}');@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 22c, 33b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap52} +$\langle$Format macro name {\footnotesize 23b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_scrap_name();@\\ +\mbox{}\verb@ fprintf(file, "@{\tt @}\verb@$\\langle$%s {\\footnotesize ", name->spelling);@\\ +\mbox{}\verb@ if (name->defs)@\\ +\mbox{}\verb@ @$\langle$Write abbreviated definition list {\footnotesize 23c}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc('?', file);@\\ +\mbox{}\verb@ fprintf(stderr, "%s: scrap never defined <%s>\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs("}$\\rangle$\\verb@{\tt @}\verb@", file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 22c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap53} +$\langle$Write abbreviated definition list {\footnotesize 23c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->defs;@\\ +\mbox{}\verb@ write_single_scrap_ref(file, p->scrap);@\\ +\mbox{}\verb@ p = p->next;@\\ +\mbox{}\verb@ if (p)@\\ +\mbox{}\verb@ fputs(", \\ldots\\ ", file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 23b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Generating the Indices} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap54} +$\langle$Write index of file names {\footnotesize 23d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (file_names) {@\\ +\mbox{}\verb@ fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}",@\\ +\mbox{}\verb@ tex_file);@\\ +\mbox{}\verb@ fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file);@\\ +\mbox{}\verb@ format_entry(file_names, tex_file, TRUE);@\\ +\mbox{}\verb@ fputs("\\end{list}}", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 18. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap55} +$\langle$Write index of macro names {\footnotesize 24a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (macro_names) {@\\ +\mbox{}\verb@ fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}",@\\ +\mbox{}\verb@ tex_file);@\\ +\mbox{}\verb@ fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file);@\\ +\mbox{}\verb@ format_entry(macro_names, tex_file, FALSE);@\\ +\mbox{}\verb@ fputs("\\end{list}}", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 18. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap56} +\verb@"latex.c"@ {\footnotesize 24b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void format_entry(name, tex_file, file_flag)@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ FILE *tex_file;@\\ +\mbox{}\verb@ int file_flag;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (name) {@\\ +\mbox{}\verb@ format_entry(name->llink, tex_file, file_flag);@\\ +\mbox{}\verb@ @$\langle$Format an index entry {\footnotesize 24c}$\rangle$\verb@@\\ +\mbox{}\verb@ name = name->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap57} +$\langle$Format an index entry {\footnotesize 24c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\item ", tex_file);@\\ +\mbox{}\verb@ if (file_flag) {@\\ +\mbox{}\verb@ fprintf(tex_file, "\\verb@{\tt @}\verb@\"%s\"@{\tt @}\verb@ ", name->spelling);@\\ +\mbox{}\verb@ @$\langle$Write file's defining scrap numbers {\footnotesize 25a}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);@\\ +\mbox{}\verb@ @$\langle$Write defining scrap numbers {\footnotesize 25b}$\rangle$\verb@@\\ +\mbox{}\verb@ fputs("}$\\rangle$ ", tex_file);@\\ +\mbox{}\verb@ @$\langle$Write referencing scrap numbers {\footnotesize 25c}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ putc('\n', tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 24b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap58} +$\langle$Write file's defining scrap numbers {\footnotesize 25a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->defs;@\\ +\mbox{}\verb@ fputs("{\\footnotesize Defined by scrap", tex_file);@\\ +\mbox{}\verb@ if (p->next) {@\\ +\mbox{}\verb@ fputs("s ", tex_file);@\\ +\mbox{}\verb@ print_scrap_numbers(tex_file, p);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc(' ', tex_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, p->scrap);@\\ +\mbox{}\verb@ putc('.', tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ putc('}', tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 24c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap59} +$\langle$Write defining scrap numbers {\footnotesize 25b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->defs;@\\ +\mbox{}\verb@ if (p) {@\\ +\mbox{}\verb@ int page;@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, p->scrap, TRUE, &page);@\\ +\mbox{}\verb@ p = p->next;@\\ +\mbox{}\verb@ while (p) {@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, p->scrap, FALSE, &page);@\\ +\mbox{}\verb@ p = p->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ putc('?', tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 24c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap60} +$\langle$Write referencing scrap numbers {\footnotesize 25c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->uses;@\\ +\mbox{}\verb@ fputs("{\\footnotesize ", tex_file);@\\ +\mbox{}\verb@ if (p) {@\\ +\mbox{}\verb@ fputs("Referenced in scrap", tex_file);@\\ +\mbox{}\verb@ if (p->next) {@\\ +\mbox{}\verb@ fputs("s ", tex_file);@\\ +\mbox{}\verb@ print_scrap_numbers(tex_file, p);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc(' ', tex_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, p->scrap);@\\ +\mbox{}\verb@ putc('.', tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fputs("Not referenced.", tex_file);@\\ +\mbox{}\verb@ putc('}', tex_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 24c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap61} +$\langle$Write index of user-specified names {\footnotesize 26a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (user_names) {@\\ +\mbox{}\verb@ fputs("\n{\\small\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}",@\\ +\mbox{}\verb@ tex_file);@\\ +\mbox{}\verb@ fputs("\\setlength{\\itemindent}{-\\leftmargin}}\n", tex_file);@\\ +\mbox{}\verb@ format_user_entry(user_names, tex_file);@\\ +\mbox{}\verb@ fputs("\\end{list}}", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 18. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap62} +\verb@"latex.c"@ {\footnotesize 26b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void format_user_entry(name, tex_file)@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ FILE *tex_file;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (name) {@\\ +\mbox{}\verb@ format_user_entry(name->llink, tex_file);@\\ +\mbox{}\verb@ @$\langle$Format a user index entry {\footnotesize 27a}$\rangle$\verb@@\\ +\mbox{}\verb@ name = name->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap63} +$\langle$Format a user index entry {\footnotesize 27a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *uses = name->uses;@\\ +\mbox{}\verb@ if (uses) {@\\ +\mbox{}\verb@ int page;@\\ +\mbox{}\verb@ Scrap_Node *defs = name->defs;@\\ +\mbox{}\verb@ fprintf(tex_file, "\\item \\verb@{\tt @}\verb@%s@{\tt @}\verb@: ", name->spelling);@\\ +\mbox{}\verb@ if (uses->scrap < defs->scrap) {@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, uses->scrap, TRUE, &page);@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ if (defs->scrap == uses->scrap)@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ fputs("\\underline{", tex_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, defs->scrap);@\\ +\mbox{}\verb@ putc('}', tex_file);@\\ +\mbox{}\verb@ page = -2;@\\ +\mbox{}\verb@ defs = defs->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ while (uses || defs) {@\\ +\mbox{}\verb@ if (uses && (!defs || uses->scrap < defs->scrap)) {@\\ +\mbox{}\verb@ write_scrap_ref(tex_file, uses->scrap, FALSE, &page);@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ if (uses && defs->scrap == uses->scrap)@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ fputs(", \\underline{", tex_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(tex_file, defs->scrap);@\\ +\mbox{}\verb@ putc('}', tex_file);@\\ +\mbox{}\verb@ page = -2;@\\ +\mbox{}\verb@ defs = defs->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs(".\n", tex_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 26b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\section{Writing the LaTeX File with HTML Scraps} \label{html-file} + +The HTML generated is patterned closely upon the {\LaTeX} generated in +the previous section.\footnote{\relax While writing this section, I +tried to follow Preston's style as displayed in +Section~\ref{latex-file}---J. D. R.} When a file name ends in +\verb|.hw|, the second pass (invoked via a call to \verb|write_html|) +copies most of the text from the source file straight into a +\verb|.tex| file. Definitions are formatted slightly and +cross-reference information is printed out. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap64} +$\langle$Function prototypes {\footnotesize 27b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void write_html();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We need a few local function declarations before we get into the body +of \verb|write_html|. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap65} +\verb@"html.c"@ {\footnotesize 28a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void copy_scrap(); /* formats the body of a scrap */@\\ +\mbox{}\verb@static void display_scrap_ref(); /* formats a scrap reference */@\\ +\mbox{}\verb@static void display_scrap_numbers(); /* formats a list of scrap numbers */@\\ +\mbox{}\verb@static void print_scrap_numbers(); /* pluralizes scrap formats list */@\\ +\mbox{}\verb@static void format_entry(); /* formats an index entry */@\\ +\mbox{}\verb@static void format_user_entry();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The routine \verb|write_html| takes two file names as parameters: the +name of the web source file and the name of the \verb|.tex| output file. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap66} +\verb@"html.c"@ {\footnotesize 28b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void write_html(file_name, html_name)@\\ +\mbox{}\verb@ char *file_name;@\\ +\mbox{}\verb@ char *html_name;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ FILE *html_file = fopen(html_name, "w");@\\ +\mbox{}\verb@ if (html_file) {@\\ +\mbox{}\verb@ if (verbose_flag)@\\ +\mbox{}\verb@ fprintf(stderr, "writing %s\n", html_name);@\\ +\mbox{}\verb@ source_open(file_name);@\\ +\mbox{}\verb@ @$\langle$Copy \verb|source_file| into \verb|html_file| {\footnotesize 28c}$\rangle$\verb@@\\ +\mbox{}\verb@ fclose(html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fprintf(stderr, "%s: can't open %s\n", command_name, html_name);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We make our second (and final) pass through the source web, this time +copying characters straight into the \verb|.tex| file. However, we keep +an eye peeled for \verb|@|~characters, which signal a command sequence. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap67} +$\langle$Copy \verb|source_file| into \verb|html_file| {\footnotesize 28c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int scraps = 1;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (c != EOF) {@\\ +\mbox{}\verb@ if (c == '@{\tt @}\verb@')@\\ +\mbox{}\verb@ @$\langle$Interpret HTML at-sequence {\footnotesize 29}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc(c, html_file);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 28b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap68} +$\langle$Interpret HTML at-sequence {\footnotesize 29}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'O': @\\ +\mbox{}\verb@ case 'o': @$\langle$Write HTML output file definition {\footnotesize 30a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'D': @\\ +\mbox{}\verb@ case 'd': @$\langle$Write HTML macro definition {\footnotesize 30c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'f': @$\langle$Write HTML index of file names {\footnotesize 34c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'm': @$\langle$Write HTML index of macro names {\footnotesize 34d}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'u': @$\langle$Write HTML index of user-specified names {\footnotesize 36b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': putc(c, html_file);@\\ +\mbox{}\verb@ default: c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 28c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Formatting Definitions} + +We go through only a little amount of effort to format a definition. +The HTML for the previous macro definition should look like this +(perhaps modulo the scrap references): + +\begin{verbatim} +<pre> +<a name="nuweb68"><Interpret HTML at-sequence 68></a> = +{ + c = source_get(); + switch (c) { + case 'O': + case 'o': <Write HTML output file definition <a href="#nuweb69">69</a>> + break; + case 'D': + case 'd': <Write HTML macro definition <a href="#nuweb71">71</a>> + break; + case 'f': <Write HTML index of file names <a href="#nuweb86">86</a>> + break; + case 'm': <Write HTML index of macro names <a href="#nuweb87">87</a>> + break; + case 'u': <Write HTML index of user-specified names <a href="#nuweb93">93</a>> + break; + case '@': putc(c, html_file); + default: c = source_get(); + break; + } +}<></pre> +Macro referenced in scrap <a href="#nuweb67">67</a>. +<br> +\end{verbatim} + +Macro and file definitions are formatted nearly identically. +I've factored the common parts out into separate scraps. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap69} +$\langle$Write HTML output file definition {\footnotesize 30a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_file_name();@\\ +\mbox{}\verb@ @$\langle$Begin HTML scrap environment {\footnotesize 30e}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write HTML output file declaration {\footnotesize 30b}$\rangle$\verb@@\\ +\mbox{}\verb@ scraps++;@\\ +\mbox{}\verb@ @$\langle$Fill in the middle of HTML scrap environment {\footnotesize 31a}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write HTML file defs {\footnotesize 31c}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Finish HTML scrap environment {\footnotesize 31b}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 29. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap70} +$\langle$Write HTML output file declaration {\footnotesize 30b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@ fputs("<a name=\"nuweb", html_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, scraps);@\\ +\mbox{}\verb@ fprintf(html_file, "\"><code>\"%s\"</code> ", name->spelling);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, scraps);@\\ +\mbox{}\verb@ fputs("</a> =\n", html_file);@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 30a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap71} +$\langle$Write HTML macro definition {\footnotesize 30c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_macro_name();@\\ +\mbox{}\verb@ @$\langle$Begin HTML scrap environment {\footnotesize 30e}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write HTML macro declaration {\footnotesize 30d}$\rangle$\verb@@\\ +\mbox{}\verb@ scraps++;@\\ +\mbox{}\verb@ @$\langle$Fill in the middle of HTML scrap environment {\footnotesize 31a}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write HTML macro defs {\footnotesize 31d}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Write HTML macro refs {\footnotesize 31e}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Finish HTML scrap environment {\footnotesize 31b}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 29. +\end{list} +\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. Note +that in this implementation, programmers may only use directives in +macro names that are recognized in preformatted text elements (PRE). + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap72} +$\langle$Write HTML macro declaration {\footnotesize 30d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@ fputs("<a name=\"nuweb", html_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, scraps);@\\ +\mbox{}\verb@ fprintf(html_file, "\"><%s ", name->spelling);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, scraps);@\\ +\mbox{}\verb@ fputs("></a> =\n", html_file);@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 30c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap73} +$\langle$Begin HTML scrap environment {\footnotesize 30e}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\begin{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ fputs("<pre>\n", html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 30ac. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The end of a scrap is marked with the characters \verb|<>|. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap74} +$\langle$Fill in the middle of HTML scrap environment {\footnotesize 31a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ copy_scrap(html_file);@\\ +\mbox{}\verb@ fputs("<></pre>\n", html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 30ac. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +The only task remaining is to get rid of the current at command and +end the paragraph. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap75} +$\langle$Finish HTML scrap environment {\footnotesize 31b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("\\end{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ c = source_get(); /* Get rid of current at command. */@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 30ac. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsubsection{Formatting Cross References} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap76} +$\langle$Write HTML file defs {\footnotesize 31c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->defs->next) {@\\ +\mbox{}\verb@ fputs("File defined by ", html_file);@\\ +\mbox{}\verb@ print_scrap_numbers(html_file, name->defs);@\\ +\mbox{}\verb@ fputs("<br>\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 30a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap77} +$\langle$Write HTML macro defs {\footnotesize 31d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->defs->next) {@\\ +\mbox{}\verb@ fputs("Macro defined by ", html_file);@\\ +\mbox{}\verb@ print_scrap_numbers(html_file, name->defs);@\\ +\mbox{}\verb@ fputs("<br>\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 30c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap78} +$\langle$Write HTML macro refs {\footnotesize 31e}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->uses) {@\\ +\mbox{}\verb@ fputs("Macro referenced in ", html_file);@\\ +\mbox{}\verb@ print_scrap_numbers(html_file, name->uses);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fputs("Macro never referenced.\n", html_file);@\\ +\mbox{}\verb@ fprintf(stderr, "%s: <%s> never referenced.\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs("<br>\n", html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 30c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap79} +\verb@"html.c"@ {\footnotesize 32a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void display_scrap_ref(html_file, num)@\\ +\mbox{}\verb@ FILE *html_file;@\\ +\mbox{}\verb@ int num;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("<a href=\"#nuweb", html_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, num);@\\ +\mbox{}\verb@ fputs("\">", html_file);@\\ +\mbox{}\verb@ write_single_scrap_ref(html_file, num);@\\ +\mbox{}\verb@ fputs("</a>", html_file);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap80} +\verb@"html.c"@ {\footnotesize 32b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void display_scrap_numbers(html_file, scraps)@\\ +\mbox{}\verb@ FILE *html_file;@\\ +\mbox{}\verb@ Scrap_Node *scraps;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ display_scrap_ref(html_file, scraps->scrap);@\\ +\mbox{}\verb@ scraps = scraps->next;@\\ +\mbox{}\verb@ while (scraps) {@\\ +\mbox{}\verb@ fputs(", ", html_file);@\\ +\mbox{}\verb@ display_scrap_ref(html_file, scraps->scrap);@\\ +\mbox{}\verb@ scraps = scraps->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap81} +\verb@"html.c"@ {\footnotesize 32c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void print_scrap_numbers(html_file, scraps)@\\ +\mbox{}\verb@ FILE *html_file;@\\ +\mbox{}\verb@ Scrap_Node *scraps;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("scrap", html_file);@\\ +\mbox{}\verb@ if (scraps->next) fputc('s', html_file);@\\ +\mbox{}\verb@ fputc(' ', html_file);@\\ +\mbox{}\verb@ display_scrap_numbers(html_file, scraps);@\\ +\mbox{}\verb@ fputs(".\n", html_file);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsubsection{Formatting a Scrap} + +We must translate HTML special keywords into entities in scraps. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap82} +\verb@"html.c"@ {\footnotesize 33a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void copy_scrap(file)@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int indent = 0;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (1) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Check HTML at-sequence for end-of-scrap {\footnotesize 33b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '<' : fputs("<", file);@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '>' : fputs(">", file);@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '&' : fputs("&", file);@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\n': fputc(c, file);@\\ +\mbox{}\verb@ indent = 0;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\t': @$\langle$Expand tab into spaces {\footnotesize 22b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: putc(c, file);@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap83} +$\langle$Check HTML at-sequence for end-of-scrap {\footnotesize 33b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': fputc(c, file);@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '|': @$\langle$Skip over index entries {\footnotesize 23a}$\rangle$\verb@@\\ +\mbox{}\verb@ case '}': return;@\\ +\mbox{}\verb@ case '<': @$\langle$Format HTML macro name {\footnotesize 34a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: /* ignore these since pass1 will have warned about them */@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 33a. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap84} +$\langle$Format HTML macro name {\footnotesize 34a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_scrap_name();@\\ +\mbox{}\verb@ fprintf(file, "<%s ", name->spelling);@\\ +\mbox{}\verb@ if (name->defs)@\\ +\mbox{}\verb@ @$\langle$Write HTML abbreviated definition list {\footnotesize 34b}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc('?', file);@\\ +\mbox{}\verb@ fprintf(stderr, "%s: scrap never defined <%s>\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs(">", file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 33b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap85} +$\langle$Write HTML abbreviated definition list {\footnotesize 34b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->defs;@\\ +\mbox{}\verb@ display_scrap_ref(file, p->scrap);@\\ +\mbox{}\verb@ if (p->next)@\\ +\mbox{}\verb@ fputs(", ... ", file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 34a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Generating the Indices} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap86} +$\langle$Write HTML index of file names {\footnotesize 34c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (file_names) {@\\ +\mbox{}\verb@ fputs("\\begin{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ fputs("<dl compact>\n", html_file);@\\ +\mbox{}\verb@ format_entry(file_names, html_file, TRUE);@\\ +\mbox{}\verb@ fputs("</dl>\n", html_file);@\\ +\mbox{}\verb@ fputs("\\end{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 29. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap87} +$\langle$Write HTML index of macro names {\footnotesize 34d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (macro_names) {@\\ +\mbox{}\verb@ fputs("\\begin{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ fputs("<dl compact>\n", html_file);@\\ +\mbox{}\verb@ format_entry(macro_names, html_file, FALSE);@\\ +\mbox{}\verb@ fputs("</dl>\n", html_file);@\\ +\mbox{}\verb@ fputs("\\end{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 29. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap88} +\verb@"html.c"@ {\footnotesize 35a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void format_entry(name, html_file, file_flag)@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ FILE *html_file;@\\ +\mbox{}\verb@ int file_flag;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (name) {@\\ +\mbox{}\verb@ format_entry(name->llink, html_file, file_flag);@\\ +\mbox{}\verb@ @$\langle$Format an HTML index entry {\footnotesize 35b}$\rangle$\verb@@\\ +\mbox{}\verb@ name = name->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap89} +$\langle$Format an HTML index entry {\footnotesize 35b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("<dt> ", html_file);@\\ +\mbox{}\verb@ if (file_flag) {@\\ +\mbox{}\verb@ fprintf(html_file, "<code>\"%s\"</code>\n<dd> ", name->spelling);@\\ +\mbox{}\verb@ @$\langle$Write HTML file's defining scrap numbers {\footnotesize 35c}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fprintf(html_file, "<%s ", name->spelling);@\\ +\mbox{}\verb@ @$\langle$Write HTML defining scrap numbers {\footnotesize 35d}$\rangle$\verb@@\\ +\mbox{}\verb@ fputs(">\n<dd> ", html_file);@\\ +\mbox{}\verb@ @$\langle$Write HTML referencing scrap numbers {\footnotesize 36a}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ putc('\n', html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 35a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap90} +$\langle$Write HTML file's defining scrap numbers {\footnotesize 35c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fputs("Defined by ", html_file);@\\ +\mbox{}\verb@ print_scrap_numbers(html_file, name->defs);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 35b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap91} +$\langle$Write HTML defining scrap numbers {\footnotesize 35d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (name->defs)@\\ +\mbox{}\verb@ display_scrap_numbers(html_file, name->defs);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ putc('?', html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 35b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap92} +$\langle$Write HTML referencing scrap numbers {\footnotesize 36a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *p = name->uses;@\\ +\mbox{}\verb@ if (p) {@\\ +\mbox{}\verb@ fputs("Referenced in ", html_file);@\\ +\mbox{}\verb@ print_scrap_numbers(html_file, p);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fputs("Not referenced.\n", html_file);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 35b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap93} +$\langle$Write HTML index of user-specified names {\footnotesize 36b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (user_names) {@\\ +\mbox{}\verb@ fputs("\\begin{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ fputs("<dl compact>\n", html_file);@\\ +\mbox{}\verb@ format_user_entry(user_names, html_file);@\\ +\mbox{}\verb@ fputs("</dl>\n", html_file);@\\ +\mbox{}\verb@ fputs("\\end{rawhtml}\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 29. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap94} +\verb@"html.c"@ {\footnotesize 36c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void format_user_entry(name, html_file)@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ FILE *html_file;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (name) {@\\ +\mbox{}\verb@ format_user_entry(name->llink, html_file);@\\ +\mbox{}\verb@ @$\langle$Format a user HTML index entry {\footnotesize 37a}$\rangle$\verb@@\\ +\mbox{}\verb@ name = name->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap95} +$\langle$Format a user HTML index entry {\footnotesize 37a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Scrap_Node *uses = name->uses;@\\ +\mbox{}\verb@ if (uses) {@\\ +\mbox{}\verb@ Scrap_Node *defs = name->defs;@\\ +\mbox{}\verb@ fprintf(html_file, "<dt><code>%s</code>:\n<dd> ", name->spelling);@\\ +\mbox{}\verb@ if (uses->scrap < defs->scrap) {@\\ +\mbox{}\verb@ display_scrap_ref(html_file, uses->scrap);@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ if (defs->scrap == uses->scrap)@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ fputs("<strong>", html_file);@\\ +\mbox{}\verb@ display_scrap_ref(html_file, defs->scrap);@\\ +\mbox{}\verb@ fputs("</strong>", html_file);@\\ +\mbox{}\verb@ defs = defs->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ while (uses || defs) {@\\ +\mbox{}\verb@ fputs(", ", html_file);@\\ +\mbox{}\verb@ if (uses && (!defs || uses->scrap < defs->scrap)) {@\\ +\mbox{}\verb@ display_scrap_ref(html_file, uses->scrap);@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ if (uses && defs->scrap == uses->scrap)@\\ +\mbox{}\verb@ uses = uses->next;@\\ +\mbox{}\verb@ fputs("<strong>", html_file);@\\ +\mbox{}\verb@ display_scrap_ref(html_file, defs->scrap);@\\ +\mbox{}\verb@ fputs("</strong>", html_file);@\\ +\mbox{}\verb@ defs = defs->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fputs(".\n", html_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 36c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\section{Writing the Output Files} \label{output-files} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap96} +$\langle$Function prototypes {\footnotesize 37b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void write_files();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap97} +\verb@"output.c"@ {\footnotesize 37c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void write_files(files)@\\ +\mbox{}\verb@ Name *files;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (files) {@\\ +\mbox{}\verb@ write_files(files->llink);@\\ +\mbox{}\verb@ @$\langle$Write out \verb|files->spelling| {\footnotesize 38a}$\rangle$\verb@@\\ +\mbox{}\verb@ files = files->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 8f, 37c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We call \verb|tempnam|, causing it to create a file name in the +current directory. This could cause a problem for \verb|rename| if +the eventual output file will reside on a different file system. +Perhaps it would be better to examine \verb|files->spelling| to find +any directory information. + +Note the superfluous call to \verb|remove| before \verb|rename|. +We're using it get around a bug in some implementations of +\verb|rename|. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap98} +$\langle$Write out \verb|files->spelling| {\footnotesize 38a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char indent_chars[500];@\\ +\mbox{}\verb@ FILE *temp_file;@\\ +\mbox{}\verb@ char *temp_name = tempnam(".", 0);@\\ +\mbox{}\verb@ temp_file = fopen(temp_name, "w");@\\ +\mbox{}\verb@ if (!temp_file) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: can't create %s for a temporary file\n",@\\ +\mbox{}\verb@ command_name, temp_name);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ } @\\ +\mbox{}\verb@ if (verbose_flag)@\\ +\mbox{}\verb@ fprintf(stderr, "writing %s\n", files->spelling);@\\ +\mbox{}\verb@ write_scraps(temp_file, files->defs, 0, indent_chars,@\\ +\mbox{}\verb@ files->debug_flag, files->tab_flag, files->indent_flag);@\\ +\mbox{}\verb@ fclose(temp_file);@\\ +\mbox{}\verb@ if (compare_flag)@\\ +\mbox{}\verb@ @$\langle$Compare the temp file and the old file {\footnotesize 38b}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ remove(files->spelling);@\\ +\mbox{}\verb@ rename(temp_name, files->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 37c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Again, we use a call to \verb|remove| before \verb|rename|. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap99} +$\langle$Compare the temp file and the old file {\footnotesize 38b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ FILE *old_file = fopen(files->spelling, "r");@\\ +\mbox{}\verb@ if (old_file) {@\\ +\mbox{}\verb@ int x, y;@\\ +\mbox{}\verb@ temp_file = fopen(temp_name, "r");@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ x = getc(old_file);@\\ +\mbox{}\verb@ y = getc(temp_file);@\\ +\mbox{}\verb@ } while (x == y && x != EOF);@\\ +\mbox{}\verb@ fclose(old_file);@\\ +\mbox{}\verb@ fclose(temp_file);@\\ +\mbox{}\verb@ if (x == y)@\\ +\mbox{}\verb@ remove(temp_name);@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ remove(files->spelling);@\\ +\mbox{}\verb@ rename(temp_name, files->spelling);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ rename(temp_name, files->spelling);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 38a. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap100} +$\langle$Function prototypes {\footnotesize 39a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void source_open(); /* pass in the name of the source file */@\\ +\mbox{}\verb@extern int source_get(); /* no args; returns the next char or EOF */@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +There are also two global variables maintained for use in error +messages and such. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap101} +$\langle$Global variable declarations {\footnotesize 39b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern char *source_name; /* name of the current file */@\\ +\mbox{}\verb@extern int source_line; /* current line in the source file */@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10ac, 39b, 44d, 53b. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap102} +$\langle$Global variable definitions {\footnotesize 39c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@char *source_name = NULL;@\\ +\mbox{}\verb@int source_line = 0;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10bd, 39c, 45a, 53c. +\item Macro referenced in scrap 9e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Local Declarations} + + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap103} +\verb@"input.c"@ {\footnotesize 39d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static FILE *source_file; /* the current input file */@\\ +\mbox{}\verb@static int source_peek;@\\ +\mbox{}\verb@static int double_at;@\\ +\mbox{}\verb@static int include_depth;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9a, 39d, 40ab, 42c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap104} +\verb@"input.c"@ {\footnotesize 40a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static struct {@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@ char *name;@\\ +\mbox{}\verb@ int line;@\\ +\mbox{}\verb@} stack[10];@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9a, 39d, 40ab, 42c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Reading a File} + +The routine \verb|source_get| returns the next character from the +current source file. It notices newlines and keeps the line counter +\verb|source_line| up to date. It also catches \verb|EOF| and watches +for \verb|@|~characters. All other characters are immediately returned. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap105} +\verb@"input.c"@ {\footnotesize 40b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int source_get()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int c = source_peek;@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case EOF: @$\langle$Handle \verb|EOF| {\footnotesize 42b}$\rangle$\verb@@\\ +\mbox{}\verb@ return c;@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Handle an ``at'' character {\footnotesize 41a}$\rangle$\verb@@\\ +\mbox{}\verb@ return c;@\\ +\mbox{}\verb@ case '\n': source_line++;@\\ +\mbox{}\verb@ default: source_peek = getc(source_file);@\\ +\mbox{}\verb@ return c;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9a, 39d, 40ab, 42c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +This whole \verb|@|~character handling mess is pretty annoying. +I want to recognize \verb|@i| so I can handle include files correctly. +At the same time, it makes sense to recognize illegal \verb|@|~sequences +and complain; this avoids ever having to check anywhere else. +Unfortunately, I need to avoid tripping over the \verb|@@|~sequence; +hence this whole unsatisfactory \verb|double_at| business. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap106} +$\langle$Handle an ``at'' character {\footnotesize 41a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = getc(source_file);@\\ +\mbox{}\verb@ if (double_at) {@\\ +\mbox{}\verb@ source_peek = c;@\\ +\mbox{}\verb@ double_at = FALSE;@\\ +\mbox{}\verb@ c = '@{\tt @}\verb@';@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 'i': @$\langle$Open an include file {\footnotesize 41b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'f': case 'm': case 'u':@\\ +\mbox{}\verb@ case 'd': case 'o': case 'D': case 'O':@\\ +\mbox{}\verb@ case '{': case '}': case '<': case '>': case '|':@\\ +\mbox{}\verb@ source_peek = c;@\\ +\mbox{}\verb@ c = '@{\tt @}\verb@';@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': source_peek = c;@\\ +\mbox{}\verb@ double_at = TRUE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: fprintf(stderr, "%s: bad @{\tt @}\verb@ sequence (%s, line %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 40b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap107} +$\langle$Open an include file {\footnotesize 41b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char name[100];@\\ +\mbox{}\verb@ if (include_depth >= 10) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: include nesting too deep (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Collect include-file name {\footnotesize 42a}$\rangle$\verb@@\\ +\mbox{}\verb@ stack[include_depth].name = source_name;@\\ +\mbox{}\verb@ stack[include_depth].file = source_file;@\\ +\mbox{}\verb@ stack[include_depth].line = source_line + 1;@\\ +\mbox{}\verb@ include_depth++;@\\ +\mbox{}\verb@ source_line = 1;@\\ +\mbox{}\verb@ source_name = save_string(name);@\\ +\mbox{}\verb@ source_file = fopen(source_name, "r");@\\ +\mbox{}\verb@ if (!source_file) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: can't open include file %s\n",@\\ +\mbox{}\verb@ command_name, source_name);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ source_peek = getc(source_file);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 41a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap108} +$\langle$Collect include-file name {\footnotesize 42a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *p = name;@\\ +\mbox{}\verb@ do @\\ +\mbox{}\verb@ c = getc(source_file);@\\ +\mbox{}\verb@ while (c == ' ' || c == '\t');@\\ +\mbox{}\verb@ while (isgraph(c)) {@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = getc(source_file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *p = '\0';@\\ +\mbox{}\verb@ if (c != '\n') {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: unexpected characters after file name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 41b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +If an \verb|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 \verb|EOF| is returned. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap109} +$\langle$Handle \verb|EOF| {\footnotesize 42b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ fclose(source_file);@\\ +\mbox{}\verb@ if (include_depth) {@\\ +\mbox{}\verb@ include_depth--;@\\ +\mbox{}\verb@ source_file = stack[include_depth].file;@\\ +\mbox{}\verb@ source_line = stack[include_depth].line;@\\ +\mbox{}\verb@ source_name = stack[include_depth].name;@\\ +\mbox{}\verb@ source_peek = getc(source_file);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 40b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Opening a File} + +The routine \verb|source_open| takes a file name and tries to open the +file. If unsuccessful, it complains and halts. Otherwise, it sets +\verb|source_name|, \verb|source_line|, and \verb|double_at|. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap110} +\verb@"input.c"@ {\footnotesize 42c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void source_open(name)@\\ +\mbox{}\verb@ char *name;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ source_file = fopen(name, "r");@\\ +\mbox{}\verb@ if (!source_file) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: couldn't open %s\n", command_name, name);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ source_name = name;@\\ +\mbox{}\verb@ source_line = 1;@\\ +\mbox{}\verb@ source_peek = getc(source_file);@\\ +\mbox{}\verb@ double_at = FALSE;@\\ +\mbox{}\verb@ include_depth = 0;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9a, 39d, 40ab, 42c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\section{Scraps} \label{scraps} + + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap111} +\verb@"scraps.c"@ {\footnotesize 43a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#define SLAB_SIZE 500@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@typedef struct slab {@\\ +\mbox{}\verb@ struct slab *next;@\\ +\mbox{}\verb@ char chars[SLAB_SIZE];@\\ +\mbox{}\verb@} Slab;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap112} +\verb@"scraps.c"@ {\footnotesize 43b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct {@\\ +\mbox{}\verb@ char *file_name;@\\ +\mbox{}\verb@ int file_line;@\\ +\mbox{}\verb@ int page;@\\ +\mbox{}\verb@ char letter;@\\ +\mbox{}\verb@ Slab *slab;@\\ +\mbox{}\verb@} ScrapEntry;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap113} +\verb@"scraps.c"@ {\footnotesize 43c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static ScrapEntry *SCRAP[256];@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255]@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@static int scraps;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap114} +$\langle$Function prototypes {\footnotesize 43d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void init_scraps();@\\ +\mbox{}\verb@extern int collect_scrap();@\\ +\mbox{}\verb@extern int write_scraps();@\\ +\mbox{}\verb@extern void write_scrap_ref();@\\ +\mbox{}\verb@extern void write_single_scrap_ref();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap115} +\verb@"scraps.c"@ {\footnotesize 43e }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void init_scraps()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ scraps = 1;@\\ +\mbox{}\verb@ SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap116} +\verb@"scraps.c"@ {\footnotesize 44a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void write_scrap_ref(file, num, first, page)@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@ int num;@\\ +\mbox{}\verb@ int first;@\\ +\mbox{}\verb@ int *page;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (scrap_array(num).page >= 0) {@\\ +\mbox{}\verb@ if (first)@\\ +\mbox{}\verb@ fprintf(file, "%d", scrap_array(num).page);@\\ +\mbox{}\verb@ else if (scrap_array(num).page != *page)@\\ +\mbox{}\verb@ fprintf(file, ", %d", scrap_array(num).page);@\\ +\mbox{}\verb@ if (scrap_array(num).letter > 0)@\\ +\mbox{}\verb@ fputc(scrap_array(num).letter, file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ if (first)@\\ +\mbox{}\verb@ putc('?', file);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ fputs(", ?", file);@\\ +\mbox{}\verb@ @$\langle$Warn (only once) about needing to rerun after Latex {\footnotesize 44c}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *page = scrap_array(num).page;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap117} +\verb@"scraps.c"@ {\footnotesize 44b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void write_single_scrap_ref(file, num)@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@ int num;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int page;@\\ +\mbox{}\verb@ write_scrap_ref(file, num, TRUE, &page);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap118} +$\langle$Warn (only once) about needing to rerun after Latex {\footnotesize 44c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (!already_warned) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: you'll need to rerun nuweb after running latex\n",@\\ +\mbox{}\verb@ command_name);@\\ +\mbox{}\verb@ already_warned = TRUE;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 44a, 52a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap119} +$\langle$Global variable declarations {\footnotesize 44d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern int already_warned;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10ac, 39b, 44d, 53b. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap120} +$\langle$Global variable definitions {\footnotesize 45a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int already_warned = 0;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10bd, 39c, 45a, 53c. +\item Macro referenced in scrap 9e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap121} +\verb@"scraps.c"@ {\footnotesize 45b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct {@\\ +\mbox{}\verb@ Slab *scrap;@\\ +\mbox{}\verb@ Slab *prev;@\\ +\mbox{}\verb@ int index;@\\ +\mbox{}\verb@} Manager;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap122} +\verb@"scraps.c"@ {\footnotesize 45c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void push(c, manager)@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@ Manager *manager;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Slab *scrap = manager->scrap;@\\ +\mbox{}\verb@ int index = manager->index;@\\ +\mbox{}\verb@ scrap->chars[index++] = c;@\\ +\mbox{}\verb@ if (index == SLAB_SIZE) {@\\ +\mbox{}\verb@ Slab *new = (Slab *) arena_getmem(sizeof(Slab));@\\ +\mbox{}\verb@ scrap->next = new;@\\ +\mbox{}\verb@ manager->scrap = new;@\\ +\mbox{}\verb@ index = 0;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ manager->index = index;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap123} +\verb@"scraps.c"@ {\footnotesize 45d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void pushs(s, manager)@\\ +\mbox{}\verb@ char *s;@\\ +\mbox{}\verb@ Manager *manager;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (*s)@\\ +\mbox{}\verb@ push(*s++, manager);@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap124} +\verb@"scraps.c"@ {\footnotesize 45e }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int collect_scrap()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Manager writer;@\\ +\mbox{}\verb@ @$\langle$Create new scrap, managed by \verb|writer| {\footnotesize 46a}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Accumulate scrap and return \verb|scraps++| {\footnotesize 46b}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap125} +$\langle$Create new scrap, managed by \verb|writer| {\footnotesize 46a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Slab *scrap = (Slab *) arena_getmem(sizeof(Slab));@\\ +\mbox{}\verb@ if ((scraps & 255) == 0)@\\ +\mbox{}\verb@ SCRAP[scraps >> 8] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));@\\ +\mbox{}\verb@ scrap_array(scraps).slab = scrap;@\\ +\mbox{}\verb@ scrap_array(scraps).file_name = save_string(source_name);@\\ +\mbox{}\verb@ scrap_array(scraps).file_line = source_line;@\\ +\mbox{}\verb@ scrap_array(scraps).page = -1;@\\ +\mbox{}\verb@ scrap_array(scraps).letter = 0;@\\ +\mbox{}\verb@ writer.scrap = scrap;@\\ +\mbox{}\verb@ writer.index = 0;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 45e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap126} +$\langle$Accumulate scrap and return \verb|scraps++| {\footnotesize 46b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (1) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case EOF: fprintf(stderr, "%s: unexpect EOF in scrap (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, scrap_array(scraps).file_name,@\\ +\mbox{}\verb@ scrap_array(scraps).file_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Handle at-sign during scrap accumulation {\footnotesize 46c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: push(c, &writer);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 45e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap127} +$\langle$Handle at-sign during scrap accumulation {\footnotesize 46c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': pushs("@{\tt @}\verb@@{\tt @}\verb@", &writer);@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '|': @$\langle$Collect user-specified index entries {\footnotesize 47a}$\rangle$\verb@@\\ +\mbox{}\verb@ case '}': push('\0', &writer);@\\ +\mbox{}\verb@ return scraps++;@\\ +\mbox{}\verb@ case '<': @$\langle$Handle macro invocation in scrap {\footnotesize 47b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default : fprintf(stderr, "%s: unexpected @{\tt @}\verb@%c in scrap (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, c, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 46b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap128} +$\langle$Collect user-specified index entries {\footnotesize 47a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ char new_name[100];@\\ +\mbox{}\verb@ char *p = new_name;@\\ +\mbox{}\verb@ do @\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (isspace(c));@\\ +\mbox{}\verb@ if (c != '@{\tt @}\verb@') {@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ } while (c != '@{\tt @}\verb@' && !isspace(c));@\\ +\mbox{}\verb@ *p = '\0';@\\ +\mbox{}\verb@ name = name_add(&user_names, new_name);@\\ +\mbox{}\verb@ if (!name->defs || name->defs->scrap != scraps) {@\\ +\mbox{}\verb@ Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\ +\mbox{}\verb@ def->scrap = scraps;@\\ +\mbox{}\verb@ def->next = name->defs;@\\ +\mbox{}\verb@ name->defs = def;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ } while (c != '@{\tt @}\verb@');@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ if (c != '}') {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: unexpected @{\tt @}\verb@%c in scrap (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, c, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 46c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap129} +$\langle$Handle macro invocation in scrap {\footnotesize 47b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = collect_scrap_name();@\\ +\mbox{}\verb@ @$\langle$Save macro name {\footnotesize 47c}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Add current scrap to \verb|name|'s uses {\footnotesize 48a}$\rangle$\verb@@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 46c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap130} +$\langle$Save macro name {\footnotesize 47c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *s = name->spelling;@\\ +\mbox{}\verb@ int len = strlen(s) - 1;@\\ +\mbox{}\verb@ pushs("@{\tt @}\verb@<", &writer);@\\ +\mbox{}\verb@ while (len > 0) {@\\ +\mbox{}\verb@ push(*s++, &writer);@\\ +\mbox{}\verb@ len--;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (*s == ' ')@\\ +\mbox{}\verb@ pushs("...", &writer);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ push(*s, &writer);@\\ +\mbox{}\verb@ pushs("@{\tt @}\verb@>", &writer);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 47b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap131} +$\langle$Add current scrap to \verb|name|'s uses {\footnotesize 48a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (!name->uses || name->uses->scrap != scraps) {@\\ +\mbox{}\verb@ Scrap_Node *use = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\ +\mbox{}\verb@ use->scrap = scraps;@\\ +\mbox{}\verb@ use->next = name->uses;@\\ +\mbox{}\verb@ name->uses = use;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 47b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap132} +\verb@"scraps.c"@ {\footnotesize 48b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static char pop(manager)@\\ +\mbox{}\verb@ Manager *manager;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Slab *scrap = manager->scrap;@\\ +\mbox{}\verb@ int index = manager->index;@\\ +\mbox{}\verb@ char c = scrap->chars[index++];@\\ +\mbox{}\verb@ if (index == SLAB_SIZE) {@\\ +\mbox{}\verb@ manager->prev = scrap;@\\ +\mbox{}\verb@ manager->scrap = scrap->next;@\\ +\mbox{}\verb@ index = 0;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ manager->index = index;@\\ +\mbox{}\verb@ return c;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap133} +\verb@"scraps.c"@ {\footnotesize 48c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Name *pop_scrap_name(manager)@\\ +\mbox{}\verb@ Manager *manager;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char name[100];@\\ +\mbox{}\verb@ char *p = name;@\\ +\mbox{}\verb@ int c = pop(manager);@\\ +\mbox{}\verb@ while (TRUE) {@\\ +\mbox{}\verb@ if (c == '@{\tt @}\verb@')@\\ +\mbox{}\verb@ @$\langle$Check for end of scrap name and return {\footnotesize 49a}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = pop(manager);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap134} +$\langle$Check for end of scrap name and return {\footnotesize 49a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = pop(manager);@\\ +\mbox{}\verb@ if (c == '@{\tt @}\verb@') {@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = pop(manager);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else if (c == '>') {@\\ +\mbox{}\verb@ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {@\\ +\mbox{}\verb@ p[-3] = ' ';@\\ +\mbox{}\verb@ p -= 2;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *p = '\0';@\\ +\mbox{}\verb@ return prefix_add(¯o_names, name);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: found an internal problem (1)\n", command_name);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 48c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap135} +\verb@"scraps.c"@ {\footnotesize 49b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@int write_scraps(file, defs, global_indent, indent_chars,@\\ +\mbox{}\verb@ debug_flag, tab_flag, indent_flag)@\\ +\mbox{}\verb@ FILE *file;@\\ +\mbox{}\verb@ Scrap_Node *defs;@\\ +\mbox{}\verb@ int global_indent;@\\ +\mbox{}\verb@ char *indent_chars;@\\ +\mbox{}\verb@ char debug_flag;@\\ +\mbox{}\verb@ char tab_flag;@\\ +\mbox{}\verb@ char indent_flag;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int indent = 0;@\\ +\mbox{}\verb@ while (defs) {@\\ +\mbox{}\verb@ @$\langle$Copy \verb|defs->scrap| to \verb|file| {\footnotesize 50a}$\rangle$\verb@@\\ +\mbox{}\verb@ defs = defs->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ return indent + global_indent;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap136} +$\langle$Copy \verb|defs->scrap| to \verb|file| {\footnotesize 50a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@ Manager reader;@\\ +\mbox{}\verb@ int line_number = scrap_array(defs->scrap).file_line;@\\ +\mbox{}\verb@ @$\langle$Insert debugging information if required {\footnotesize 50b}$\rangle$\verb@@\\ +\mbox{}\verb@ reader.scrap = scrap_array(defs->scrap).slab;@\\ +\mbox{}\verb@ reader.index = 0;@\\ +\mbox{}\verb@ c = pop(&reader);@\\ +\mbox{}\verb@ while (c) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Check for macro invocation in scrap {\footnotesize 51b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\n': putc(c, file);@\\ +\mbox{}\verb@ line_number++;@\\ +\mbox{}\verb@ @$\langle$Insert appropriate indentation {\footnotesize 50c}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\t': @$\langle$Handle tab characters on output {\footnotesize 51a}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: putc(c, file);@\\ +\mbox{}\verb@ indent_chars[global_indent + indent] = ' ';@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = pop(&reader);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 49b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap137} +$\langle$Insert debugging information if required {\footnotesize 50b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@if (debug_flag) {@\\ +\mbox{}\verb@ fprintf(file, "\n#line %d \"%s\"\n",@\\ +\mbox{}\verb@ line_number, scrap_array(defs->scrap).file_name);@\\ +\mbox{}\verb@ @$\langle$Insert appropriate indentation {\footnotesize 50c}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 50a, 51b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap138} +$\langle$Insert appropriate indentation {\footnotesize 50c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (indent_flag) {@\\ +\mbox{}\verb@ if (tab_flag)@\\ +\mbox{}\verb@ for (indent=0; indent<global_indent; indent++)@\\ +\mbox{}\verb@ putc(' ', file);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ for (indent=0; indent<global_indent; indent++)@\\ +\mbox{}\verb@ putc(indent_chars[indent], file);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ indent = 0;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 50ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap139} +$\langle$Handle tab characters on output {\footnotesize 51a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (tab_flag)@\\ +\mbox{}\verb@ @$\langle$Expand tab into spaces {\footnotesize 22b}$\rangle$\verb@@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ putc('\t', file);@\\ +\mbox{}\verb@ indent_chars[global_indent + indent] = '\t';@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 50a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap140} +$\langle$Check for macro invocation in scrap {\footnotesize 51b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = pop(&reader);@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': putc(c, file);@\\ +\mbox{}\verb@ indent_chars[global_indent + indent] = ' ';@\\ +\mbox{}\verb@ indent++;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '<': @$\langle$Copy macro into \verb|file| {\footnotesize 51c}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Insert debugging information if required {\footnotesize 50b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: /* ignore, since we should already have a warning */@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 50a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap141} +$\langle$Copy macro into \verb|file| {\footnotesize 51c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *name = pop_scrap_name(&reader);@\\ +\mbox{}\verb@ if (name->mark) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: recursive macro discovered involving <%s>\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (name->defs) {@\\ +\mbox{}\verb@ name->mark = TRUE;@\\ +\mbox{}\verb@ indent = write_scraps(file, name->defs, global_indent + indent,@\\ +\mbox{}\verb@ indent_chars, debug_flag, tab_flag, indent_flag);@\\ +\mbox{}\verb@ indent -= global_indent;@\\ +\mbox{}\verb@ name->mark = FALSE;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else if (!tex_flag)@\\ +\mbox{}\verb@ fprintf(stderr, "%s: macro never defined <%s>\n",@\\ +\mbox{}\verb@ command_name, name->spelling);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 51b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Collecting Page Numbers} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap142} +$\langle$Function prototypes {\footnotesize 51d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void collect_numbers();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap143} +\verb@"scraps.c"@ {\footnotesize 52a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void collect_numbers(aux_name)@\\ +\mbox{}\verb@ char *aux_name;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (number_flag) {@\\ +\mbox{}\verb@ int i;@\\ +\mbox{}\verb@ for (i=1; i<scraps; i++)@\\ +\mbox{}\verb@ scrap_array(i).page = i;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else {@\\ +\mbox{}\verb@ FILE *aux_file = fopen(aux_name, "r");@\\ +\mbox{}\verb@ already_warned = FALSE;@\\ +\mbox{}\verb@ if (aux_file) {@\\ +\mbox{}\verb@ char aux_line[500];@\\ +\mbox{}\verb@ while (fgets(aux_line, 500, aux_file)) {@\\ +\mbox{}\verb@ int scrap_number;@\\ +\mbox{}\verb@ int page_number;@\\ +\mbox{}\verb@ char dummy[50];@\\ +\mbox{}\verb@ if (3 == sscanf(aux_line, "\\newlabel{scrap%d}{%[^}]}{%d}",@\\ +\mbox{}\verb@ &scrap_number, dummy, &page_number)) {@\\ +\mbox{}\verb@ if (scrap_number < scraps)@\\ +\mbox{}\verb@ scrap_array(scrap_number).page = page_number;@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ @$\langle$Warn (only once) about needing to rerun after Latex {\footnotesize 44c}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fclose(aux_file);@\\ +\mbox{}\verb@ @$\langle$Add letters to scraps with duplicate page numbers {\footnotesize 52b}$\rangle$\verb@@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap144} +$\langle$Add letters to scraps with duplicate page numbers {\footnotesize 52b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int scrap;@\\ +\mbox{}\verb@ for (scrap=2; scrap<scraps; scrap++) {@\\ +\mbox{}\verb@ if (scrap_array(scrap-1).page == scrap_array(scrap).page) {@\\ +\mbox{}\verb@ if (!scrap_array(scrap-1).letter)@\\ +\mbox{}\verb@ scrap_array(scrap-1).letter = 'a';@\\ +\mbox{}\verb@ scrap_array(scrap).letter = scrap_array(scrap-1).letter + 1;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 52a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\section{Names} \label{names} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap145} +$\langle$Type declarations {\footnotesize 52c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct scrap_node {@\\ +\mbox{}\verb@ struct scrap_node *next;@\\ +\mbox{}\verb@ int scrap;@\\ +\mbox{}\verb@} Scrap_Node;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 8a, 52c, 53a. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap146} +$\langle$Type declarations {\footnotesize 53a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct name {@\\ +\mbox{}\verb@ char *spelling;@\\ +\mbox{}\verb@ struct name *llink;@\\ +\mbox{}\verb@ struct name *rlink;@\\ +\mbox{}\verb@ Scrap_Node *defs;@\\ +\mbox{}\verb@ Scrap_Node *uses;@\\ +\mbox{}\verb@ int mark;@\\ +\mbox{}\verb@ char tab_flag;@\\ +\mbox{}\verb@ char indent_flag;@\\ +\mbox{}\verb@ char debug_flag;@\\ +\mbox{}\verb@} Name;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 8a, 52c, 53a. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap147} +$\langle$Global variable declarations {\footnotesize 53b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern Name *file_names;@\\ +\mbox{}\verb@extern Name *macro_names;@\\ +\mbox{}\verb@extern Name *user_names;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10ac, 39b, 44d, 53b. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap148} +$\langle$Global variable definitions {\footnotesize 53c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@Name *file_names = NULL;@\\ +\mbox{}\verb@Name *macro_names = NULL;@\\ +\mbox{}\verb@Name *user_names = NULL;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 10bd, 39c, 45a, 53c. +\item Macro referenced in scrap 9e. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap149} +$\langle$Function prototypes {\footnotesize 53d}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern Name *collect_file_name();@\\ +\mbox{}\verb@extern Name *collect_macro_name();@\\ +\mbox{}\verb@extern Name *collect_scrap_name();@\\ +\mbox{}\verb@extern Name *name_add();@\\ +\mbox{}\verb@extern Name *prefix_add();@\\ +\mbox{}\verb@extern char *save_string();@\\ +\mbox{}\verb@extern void reverse_lists();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap150} +\verb@"names.c"@ {\footnotesize 54a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@enum { LESS, GREATER, EQUAL, PREFIX, EXTENSION };@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@static int compare(x, y)@\\ +\mbox{}\verb@ char *x;@\\ +\mbox{}\verb@ char *y;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int len, result;@\\ +\mbox{}\verb@ int xl = strlen(x);@\\ +\mbox{}\verb@ int yl = strlen(y);@\\ +\mbox{}\verb@ int xp = x[xl - 1] == ' ';@\\ +\mbox{}\verb@ int yp = y[yl - 1] == ' ';@\\ +\mbox{}\verb@ if (xp) xl--;@\\ +\mbox{}\verb@ if (yp) yl--;@\\ +\mbox{}\verb@ len = xl < yl ? xl : yl;@\\ +\mbox{}\verb@ result = strncmp(x, y, len);@\\ +\mbox{}\verb@ if (result < 0) return GREATER;@\\ +\mbox{}\verb@ else if (result > 0) return LESS;@\\ +\mbox{}\verb@ else if (xl < yl) {@\\ +\mbox{}\verb@ if (xp) return EXTENSION;@\\ +\mbox{}\verb@ else return LESS;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else if (xl > yl) {@\\ +\mbox{}\verb@ if (yp) return PREFIX;@\\ +\mbox{}\verb@ else return GREATER;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else return EQUAL;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap151} +\verb@"names.c"@ {\footnotesize 54b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@char *save_string(s)@\\ +\mbox{}\verb@ char *s;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *new = (char *) arena_getmem((strlen(s) + 1) * sizeof(char));@\\ +\mbox{}\verb@ strcpy(new, s);@\\ +\mbox{}\verb@ return new;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap152} +\verb@"names.c"@ {\footnotesize 55a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static int ambiguous_prefix();@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@Name *prefix_add(root, spelling)@\\ +\mbox{}\verb@ Name **root;@\\ +\mbox{}\verb@ char *spelling;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *node = *root;@\\ +\mbox{}\verb@ while (node) {@\\ +\mbox{}\verb@ switch (compare(node->spelling, spelling)) {@\\ +\mbox{}\verb@ case GREATER: root = &node->rlink;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case LESS: root = &node->llink;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case EQUAL: return node;@\\ +\mbox{}\verb@ case EXTENSION: node->spelling = save_string(spelling);@\\ +\mbox{}\verb@ return node;@\\ +\mbox{}\verb@ case PREFIX: @$\langle$Check for ambiguous prefix {\footnotesize 55b}$\rangle$\verb@@\\ +\mbox{}\verb@ return node;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ node = *root;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Create new name entry {\footnotesize 58a}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap153} +$\langle$Check for ambiguous prefix {\footnotesize 55b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (ambiguous_prefix(node->llink, spelling) ||@\\ +\mbox{}\verb@ ambiguous_prefix(node->rlink, spelling))@\\ +\mbox{}\verb@ fprintf(stderr,@\\ +\mbox{}\verb@ "%s: ambiguous prefix @{\tt @}\verb@<%s...@{\tt @}\verb@> (%s, line %d)\n",@\\ +\mbox{}\verb@ command_name, spelling, source_name, source_line);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 55a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap154} +\verb@"names.c"@ {\footnotesize 56 }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static int ambiguous_prefix(node, spelling)@\\ +\mbox{}\verb@ Name *node;@\\ +\mbox{}\verb@ char *spelling;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (node) {@\\ +\mbox{}\verb@ switch (compare(node->spelling, spelling)) {@\\ +\mbox{}\verb@ case GREATER: node = node->rlink;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case LESS: node = node->llink;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case EQUAL:@\\ +\mbox{}\verb@ case EXTENSION:@\\ +\mbox{}\verb@ case PREFIX: return TRUE;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ return FALSE;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\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 \verb|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 \verb|robs_strcmp| implements the desired predicate. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap155} +\verb@"names.c"@ {\footnotesize 57a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static int robs_strcmp(x, y)@\\ +\mbox{}\verb@ char *x;@\\ +\mbox{}\verb@ char *y;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *xx = x;@\\ +\mbox{}\verb@ char *yy = y;@\\ +\mbox{}\verb@ int xc = toupper(*xx);@\\ +\mbox{}\verb@ int yc = toupper(*yy);@\\ +\mbox{}\verb@ while (xc == yc && xc) {@\\ +\mbox{}\verb@ xx++;@\\ +\mbox{}\verb@ yy++;@\\ +\mbox{}\verb@ xc = toupper(*xx);@\\ +\mbox{}\verb@ yc = toupper(*yy);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (xc != yc) return xc - yc;@\\ +\mbox{}\verb@ xc = *x;@\\ +\mbox{}\verb@ yc = *y;@\\ +\mbox{}\verb@ while (xc == yc && xc) {@\\ +\mbox{}\verb@ x++;@\\ +\mbox{}\verb@ y++;@\\ +\mbox{}\verb@ xc = *x;@\\ +\mbox{}\verb@ yc = *y;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (isupper(xc) && islower(yc))@\\ +\mbox{}\verb@ return xc * 2 - (toupper(yc) * 2 + 1);@\\ +\mbox{}\verb@ if (islower(xc) && isupper(yc))@\\ +\mbox{}\verb@ return toupper(xc) * 2 + 1 - yc * 2;@\\ +\mbox{}\verb@ return xc - yc;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap156} +\verb@"names.c"@ {\footnotesize 57b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@Name *name_add(root, spelling)@\\ +\mbox{}\verb@ Name **root;@\\ +\mbox{}\verb@ char *spelling;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *node = *root;@\\ +\mbox{}\verb@ while (node) {@\\ +\mbox{}\verb@ int result = robs_strcmp(node->spelling, spelling);@\\ +\mbox{}\verb@ if (result > 0)@\\ +\mbox{}\verb@ root = &node->llink;@\\ +\mbox{}\verb@ else if (result < 0)@\\ +\mbox{}\verb@ root = &node->rlink;@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ return node;@\\ +\mbox{}\verb@ node = *root;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Create new name entry {\footnotesize 58a}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap157} +$\langle$Create new name entry {\footnotesize 58a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ node = (Name *) arena_getmem(sizeof(Name));@\\ +\mbox{}\verb@ node->spelling = save_string(spelling);@\\ +\mbox{}\verb@ node->mark = FALSE;@\\ +\mbox{}\verb@ node->llink = NULL;@\\ +\mbox{}\verb@ node->rlink = NULL;@\\ +\mbox{}\verb@ node->uses = NULL;@\\ +\mbox{}\verb@ node->defs = NULL;@\\ +\mbox{}\verb@ node->tab_flag = TRUE;@\\ +\mbox{}\verb@ node->indent_flag = TRUE;@\\ +\mbox{}\verb@ node->debug_flag = FALSE;@\\ +\mbox{}\verb@ *root = node;@\\ +\mbox{}\verb@ return node;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 55a, 57b. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap158} +\verb@"names.c"@ {\footnotesize 58b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@Name *collect_file_name()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Name *new_name;@\\ +\mbox{}\verb@ char name[100];@\\ +\mbox{}\verb@ char *p = name;@\\ +\mbox{}\verb@ int start_line = source_line;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (isspace(c))@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (isgraph(c)) {@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (p == name) {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: expected file name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, start_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *p = '\0';@\\ +\mbox{}\verb@ new_name = name_add(&file_names, name);@\\ +\mbox{}\verb@ @$\langle$Handle optional per-file flags {\footnotesize 59}$\rangle$\verb@@\\ +\mbox{}\verb@ if (c != '@{\tt @}\verb@' || source_get() != '{') {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: expected @{\tt @}\verb@{ after file name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, start_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ return new_name;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap159} +$\langle$Handle optional per-file flags {\footnotesize 59}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (1) {@\\ +\mbox{}\verb@ while (isspace(c))@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ if (c == '-') {@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case 't': new_name->tab_flag = FALSE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'd': new_name->debug_flag = TRUE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case 'i': new_name->indent_flag = FALSE;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default : fprintf(stderr, "%s: unexpected per-file flag (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ } while (!isspace(c));@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ else break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 58b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Name terminated by \verb+\n+ or \verb+@{+; but keep skipping until \verb+@{+ +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap160} +\verb@"names.c"@ {\footnotesize 60a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@Name *collect_macro_name()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char name[100];@\\ +\mbox{}\verb@ char *p = name;@\\ +\mbox{}\verb@ int start_line = source_line;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (isspace(c))@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (c != EOF) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Check for terminating at-sequence and return name {\footnotesize 60b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\t':@\\ +\mbox{}\verb@ case ' ': *p++ = ' ';@\\ +\mbox{}\verb@ do@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (c == ' ' || c == '\t');@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\n': @$\langle$Skip until scrap begins, then return name {\footnotesize 61b}$\rangle$\verb@@\\ +\mbox{}\verb@ default: *p++ = c;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fprintf(stderr, "%s: expected macro name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, start_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ return NULL; /* unreachable return to avoid warnings on some compilers */@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap161} +$\langle$Check for terminating at-sequence and return name {\footnotesize 60b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': *p++ = c;@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '{': @$\langle$Cleanup and install name {\footnotesize 61a}$\rangle$\verb@@\\ +\mbox{}\verb@ default: fprintf(stderr,@\\ +\mbox{}\verb@ "%s: unexpected @{\tt @}\verb@%c in macro name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, c, source_name, start_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 60a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap162} +$\langle$Cleanup and install name {\footnotesize 61a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (p > name && p[-1] == ' ')@\\ +\mbox{}\verb@ p--;@\\ +\mbox{}\verb@ if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') {@\\ +\mbox{}\verb@ p[-3] = ' ';@\\ +\mbox{}\verb@ p -= 2;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ if (p == name || name[0] == ' ') {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: empty scrap name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *p = '\0';@\\ +\mbox{}\verb@ return prefix_add(¯o_names, name);@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scraps 60b, 61b, 62b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap163} +$\langle$Skip until scrap begins, then return name {\footnotesize 61b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ do@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (isspace(c));@\\ +\mbox{}\verb@ if (c != '@{\tt @}\verb@' || source_get() != '{') {@\\ +\mbox{}\verb@ fprintf(stderr, "%s: expected @{\tt @}\verb@{ after macro name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, start_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Cleanup and install name {\footnotesize 61a}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 60a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +Terminated by \verb+@>+ +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap164} +\verb@"names.c"@ {\footnotesize 62a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@Name *collect_scrap_name()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char name[100];@\\ +\mbox{}\verb@ char *p = name;@\\ +\mbox{}\verb@ int c = source_get();@\\ +\mbox{}\verb@ while (c == ' ' || c == '\t')@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (c != EOF) {@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': @$\langle$Look for end of scrap name and return {\footnotesize 62b}$\rangle$\verb@@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '\t':@\\ +\mbox{}\verb@ case ' ': *p++ = ' ';@\\ +\mbox{}\verb@ do@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ while (c == ' ' || c == '\t');@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ default: if (!isgraph(c)) {@\\ +\mbox{}\verb@ fprintf(stderr,@\\ +\mbox{}\verb@ "%s: unexpected character in macro name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ *p++ = c;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ fprintf(stderr, "%s: unexpected end of file (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ return NULL; /* unreachable return to avoid warnings on some compilers */@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap165} +$\langle$Look for end of scrap name and return {\footnotesize 62b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '@{\tt @}\verb@': *p++ = c;@\\ +\mbox{}\verb@ c = source_get();@\\ +\mbox{}\verb@ break;@\\ +\mbox{}\verb@ case '>': @$\langle$Cleanup and install name {\footnotesize 61a}$\rangle$\verb@@\\ +\mbox{}\verb@ default: fprintf(stderr,@\\ +\mbox{}\verb@ "%s: unexpected @{\tt @}\verb@%c in macro name (%s, %d)\n",@\\ +\mbox{}\verb@ command_name, c, source_name, source_line);@\\ +\mbox{}\verb@ exit(-1);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 62a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap166} +\verb@"names.c"@ {\footnotesize 63a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Scrap_Node *reverse(); /* a forward declaration */@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@void reverse_lists(names)@\\ +\mbox{}\verb@ Name *names;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (names) {@\\ +\mbox{}\verb@ reverse_lists(names->llink);@\\ +\mbox{}\verb@ names->defs = reverse(names->defs);@\\ +\mbox{}\verb@ names->uses = reverse(names->uses);@\\ +\mbox{}\verb@ names = names->rlink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap167} +\verb@"names.c"@ {\footnotesize 63b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Scrap_Node *reverse(a)@\\ +\mbox{}\verb@ Scrap_Node *a;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ if (a) {@\\ +\mbox{}\verb@ Scrap_Node *b = a->next;@\\ +\mbox{}\verb@ a->next = NULL;@\\ +\mbox{}\verb@ while (b) {@\\ +\mbox{}\verb@ Scrap_Node *c = b->next;@\\ +\mbox{}\verb@ b->next = a;@\\ +\mbox{}\verb@ a = b;@\\ +\mbox{}\verb@ b = c;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ return a;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +\end{list} +\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 occurrences 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} \small +\begin{minipage}{\linewidth} \label{scrap168} +\verb@"scraps.c"@ {\footnotesize 63c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct name_node {@\\ +\mbox{}\verb@ struct name_node *next;@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@} Name_Node;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap169} +\verb@"scraps.c"@ {\footnotesize 64a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct goto_node {@\\ +\mbox{}\verb@ Name_Node *output; /* list of words ending in this state */@\\ +\mbox{}\verb@ struct move_node *moves; /* list of possible moves */@\\ +\mbox{}\verb@ struct goto_node *fail; /* and where to go when no move fits */@\\ +\mbox{}\verb@ struct goto_node *next; /* next goto node with same depth */@\\ +\mbox{}\verb@} Goto_Node;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap170} +\verb@"scraps.c"@ {\footnotesize 64b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct move_node {@\\ +\mbox{}\verb@ struct move_node *next;@\\ +\mbox{}\verb@ Goto_Node *state;@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@} Move_Node;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap171} +\verb@"scraps.c"@ {\footnotesize 64c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Goto_Node *root[128];@\\ +\mbox{}\verb@static int max_depth;@\\ +\mbox{}\verb@static Goto_Node **depths;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap172} +\verb@"scraps.c"@ {\footnotesize 64d }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Goto_Node *goto_lookup(c, g)@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@ Goto_Node *g;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ Move_Node *m = g->moves;@\\ +\mbox{}\verb@ while (m && m->c != c)@\\ +\mbox{}\verb@ m = m->next;@\\ +\mbox{}\verb@ if (m)@\\ +\mbox{}\verb@ return m->state;@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ return NULL;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Building the Automata} + + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap173} +$\langle$Function prototypes {\footnotesize 64e}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void search();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap174} +\verb@"scraps.c"@ {\footnotesize 65a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void build_gotos();@\\ +\mbox{}\verb@static int reject_match();@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@void search()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int i;@\\ +\mbox{}\verb@ for (i=0; i<128; i++)@\\ +\mbox{}\verb@ root[i] = NULL;@\\ +\mbox{}\verb@ max_depth = 10;@\\ +\mbox{}\verb@ depths = (Goto_Node **) arena_getmem(max_depth * sizeof(Goto_Node *));@\\ +\mbox{}\verb@ for (i=0; i<max_depth; i++)@\\ +\mbox{}\verb@ depths[i] = NULL;@\\ +\mbox{}\verb@ build_gotos(user_names);@\\ +\mbox{}\verb@ @$\langle$Build failure functions {\footnotesize 67}$\rangle$\verb@@\\ +\mbox{}\verb@ @$\langle$Search scraps {\footnotesize 68}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap175} +\verb@"scraps.c"@ {\footnotesize 65b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static void build_gotos(tree)@\\ +\mbox{}\verb@ Name *tree;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ while (tree) {@\\ +\mbox{}\verb@ @$\langle$Extend goto graph with \verb|tree->spelling| {\footnotesize 66}$\rangle$\verb@@\\ +\mbox{}\verb@ build_gotos(tree->rlink);@\\ +\mbox{}\verb@ tree = tree->llink;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap176} +$\langle$Extend goto graph with \verb|tree->spelling| {\footnotesize 66}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int depth = 2;@\\ +\mbox{}\verb@ char *p = tree->spelling;@\\ +\mbox{}\verb@ char c = *p++;@\\ +\mbox{}\verb@ Goto_Node *q = root[c];@\\ +\mbox{}\verb@ if (!q) {@\\ +\mbox{}\verb@ q = (Goto_Node *) arena_getmem(sizeof(Goto_Node));@\\ +\mbox{}\verb@ root[c] = q;@\\ +\mbox{}\verb@ q->moves = NULL;@\\ +\mbox{}\verb@ q->fail = NULL;@\\ +\mbox{}\verb@ q->moves = NULL;@\\ +\mbox{}\verb@ q->output = NULL;@\\ +\mbox{}\verb@ q->next = depths[1];@\\ +\mbox{}\verb@ depths[1] = q;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ while (c = *p++) {@\\ +\mbox{}\verb@ Goto_Node *new = goto_lookup(c, q);@\\ +\mbox{}\verb@ if (!new) {@\\ +\mbox{}\verb@ Move_Node *new_move = (Move_Node *) arena_getmem(sizeof(Move_Node));@\\ +\mbox{}\verb@ new = (Goto_Node *) arena_getmem(sizeof(Goto_Node));@\\ +\mbox{}\verb@ new->moves = NULL;@\\ +\mbox{}\verb@ new->fail = NULL;@\\ +\mbox{}\verb@ new->moves = NULL;@\\ +\mbox{}\verb@ new->output = NULL;@\\ +\mbox{}\verb@ new_move->state = new;@\\ +\mbox{}\verb@ new_move->c = c;@\\ +\mbox{}\verb@ new_move->next = q->moves;@\\ +\mbox{}\verb@ q->moves = new_move;@\\ +\mbox{}\verb@ if (depth == max_depth) {@\\ +\mbox{}\verb@ int i;@\\ +\mbox{}\verb@ Goto_Node **new_depths =@\\ +\mbox{}\verb@ (Goto_Node **) arena_getmem(2*depth*sizeof(Goto_Node *));@\\ +\mbox{}\verb@ max_depth = 2 * depth;@\\ +\mbox{}\verb@ for (i=0; i<depth; i++)@\\ +\mbox{}\verb@ new_depths[i] = depths[i];@\\ +\mbox{}\verb@ depths = new_depths;@\\ +\mbox{}\verb@ for (i=depth; i<max_depth; i++)@\\ +\mbox{}\verb@ depths[i] = NULL;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ new->next = depths[depth];@\\ +\mbox{}\verb@ depths[depth] = new;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ q = new;@\\ +\mbox{}\verb@ depth++;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ q->output = (Name_Node *) arena_getmem(sizeof(Name_Node));@\\ +\mbox{}\verb@ q->output->next = NULL;@\\ +\mbox{}\verb@ q->output->name = tree;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 65b. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap177} +$\langle$Build failure functions {\footnotesize 67}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ int depth;@\\ +\mbox{}\verb@ for (depth=1; depth<max_depth; depth++) {@\\ +\mbox{}\verb@ Goto_Node *r = depths[depth];@\\ +\mbox{}\verb@ while (r) {@\\ +\mbox{}\verb@ Move_Node *m = r->moves;@\\ +\mbox{}\verb@ while (m) {@\\ +\mbox{}\verb@ char a = m->c;@\\ +\mbox{}\verb@ Goto_Node *s = m->state;@\\ +\mbox{}\verb@ Goto_Node *state = r->fail;@\\ +\mbox{}\verb@ while (state && !goto_lookup(a, state))@\\ +\mbox{}\verb@ state = state->fail;@\\ +\mbox{}\verb@ if (state)@\\ +\mbox{}\verb@ s->fail = goto_lookup(a, state);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ s->fail = root[a];@\\ +\mbox{}\verb@ if (s->fail) {@\\ +\mbox{}\verb@ Name_Node *p = s->fail->output;@\\ +\mbox{}\verb@ while (p) {@\\ +\mbox{}\verb@ Name_Node *q = (Name_Node *) arena_getmem(sizeof(Name_Node));@\\ +\mbox{}\verb@ q->name = p->name;@\\ +\mbox{}\verb@ q->next = s->output;@\\ +\mbox{}\verb@ s->output = q;@\\ +\mbox{}\verb@ p = p->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ m = m->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ r = r->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 65a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Searching the Scraps} + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap178} +$\langle$Search scraps {\footnotesize 68}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ for (i=1; i<scraps; i++) {@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@ Manager reader;@\\ +\mbox{}\verb@ Goto_Node *state = NULL;@\\ +\mbox{}\verb@ reader.prev = NULL;@\\ +\mbox{}\verb@ reader.scrap = scrap_array(i).slab;@\\ +\mbox{}\verb@ reader.index = 0;@\\ +\mbox{}\verb@ c = pop(&reader);@\\ +\mbox{}\verb@ while (c) {@\\ +\mbox{}\verb@ while (state && !goto_lookup(c, state))@\\ +\mbox{}\verb@ state = state->fail;@\\ +\mbox{}\verb@ if (state)@\\ +\mbox{}\verb@ state = goto_lookup(c, state);@\\ +\mbox{}\verb@ else@\\ +\mbox{}\verb@ state = root[c];@\\ +\mbox{}\verb@ c = pop(&reader);@\\ +\mbox{}\verb@ if (state && state->output) {@\\ +\mbox{}\verb@ Name_Node *p = state->output;@\\ +\mbox{}\verb@ do {@\\ +\mbox{}\verb@ Name *name = p->name;@\\ +\mbox{}\verb@ if (!reject_match(name, c, &reader) &&@\\ +\mbox{}\verb@ (!name->uses || name->uses->scrap != i)) {@\\ +\mbox{}\verb@ Scrap_Node *new_use =@\\ +\mbox{}\verb@ (Scrap_Node *) arena_getmem(sizeof(Scrap_Node));@\\ +\mbox{}\verb@ new_use->scrap = i;@\\ +\mbox{}\verb@ new_use->next = name->uses;@\\ +\mbox{}\verb@ name->uses = new_use;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ p = p->next;@\\ +\mbox{}\verb@ } while (p);@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 65a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsubsection{Rejecting Matches} + +A problem with simple substring matching is that the string ``he'' +would match longer strings like ``she'' and ``her.'' Norman Ramsey +suggested examining the characters occurring immediately before and +after a match and rejecting the match if it appears to be part of a +longer token. Of course, the concept of {\sl token\/} is +language-dependent, so we may be occasionally mistaken. +For the present, we'll consider the mechanism an experiment. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap179} +\verb@"scraps.c"@ {\footnotesize 69a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@#define sym_char(c) (isalnum(c) || (c) == '_')@\\ +\mbox{}\verb@@\\ +\mbox{}\verb@static int op_char(c)@\\ +\mbox{}\verb@ char c;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ switch (c) {@\\ +\mbox{}\verb@ case '!': case '@{\tt @}\verb@': case '#': case '%': case '$': case '^': @\\ +\mbox{}\verb@ case '&': case '*': case '-': case '+': case '=': case '/':@\\ +\mbox{}\verb@ case '|': case '~': case '<': case '>':@\\ +\mbox{}\verb@ return TRUE;@\\ +\mbox{}\verb@ default:@\\ +\mbox{}\verb@ return FALSE;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap180} +\verb@"scraps.c"@ {\footnotesize 69b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static int reject_match(name, post, reader)@\\ +\mbox{}\verb@ Name *name;@\\ +\mbox{}\verb@ char post;@\\ +\mbox{}\verb@ Manager *reader;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ int len = strlen(name->spelling);@\\ +\mbox{}\verb@ char first = name->spelling[0];@\\ +\mbox{}\verb@ char last = name->spelling[len - 1];@\\ +\mbox{}\verb@ char prev = '\0';@\\ +\mbox{}\verb@ len = reader->index - len - 2;@\\ +\mbox{}\verb@ if (len >= 0)@\\ +\mbox{}\verb@ prev = reader->scrap->chars[len];@\\ +\mbox{}\verb@ else if (reader->prev)@\\ +\mbox{}\verb@ prev = reader->scrap->chars[SLAB_SIZE - len];@\\ +\mbox{}\verb@ if (sym_char(last) && sym_char(post)) return TRUE;@\\ +\mbox{}\verb@ if (sym_char(first) && sym_char(prev)) return TRUE;@\\ +\mbox{}\verb@ if (op_char(last) && op_char(post)) return TRUE;@\\ +\mbox{}\verb@ if (op_char(first) && op_char(prev)) return TRUE;@\\ +\mbox{}\verb@ return FALSE;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +\end{list} +\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 +\verb|arena_getmem(n)|, where \verb|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 \verb|arena_free()|. Both operations +are quite fast. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap181} +$\langle$Function prototypes {\footnotesize 69c}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@extern void *arena_getmem();@\\ +\mbox{}\verb@extern void arena_free();@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro defined by scraps 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c. +\item Macro referenced in scrap 7a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap182} +\verb@"arena.c"@ {\footnotesize 70a }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@typedef struct chunk {@\\ +\mbox{}\verb@ struct chunk *next;@\\ +\mbox{}\verb@ char *limit;@\\ +\mbox{}\verb@ char *avail;@\\ +\mbox{}\verb@} Chunk;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9d, 70abc, 71c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +We define an empty chunk called \verb|first|. The variable \verb|arena| points +at the current chunk of memory; it's initially pointed at \verb|first|. +As soon as some storage is required, a ``real'' chunk of memory will +be allocated and attached to \verb|first->next|; storage will be +allocated from the new chunk (and later chunks if necessary). +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap183} +\verb@"arena.c"@ {\footnotesize 70b }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@static Chunk first = { NULL, NULL, NULL };@\\ +\mbox{}\verb@static Chunk *arena = &first;@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9d, 70abc, 71c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Allocating Memory} + +The routine \verb|arena_getmem(n)| returns a pointer to (at least) +\verb|n| bytes of memory. Note that \verb|n| is rounded up to ensure +that returned pointers are always aligned. We align to the nearest +8~byte segment, since that'll satisfy the more common 2-byte and +4-byte alignment restrictions too. + +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap184} +\verb@"arena.c"@ {\footnotesize 70c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void *arena_getmem(n)@\\ +\mbox{}\verb@ size_t n;@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ char *q;@\\ +\mbox{}\verb@ char *p = arena->avail;@\\ +\mbox{}\verb@ n = (n + 7) & ~7; /* ensuring alignment to 8 bytes */@\\ +\mbox{}\verb@ q = p + n;@\\ +\mbox{}\verb@ if (q <= arena->limit) {@\\ +\mbox{}\verb@ arena->avail = q;@\\ +\mbox{}\verb@ return p;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Find a new chunk of memory {\footnotesize 71a}$\rangle$\verb@@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9d, 70abc, 71c. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +If the current chunk doesn't have adequate space (at least \verb|n| +bytes) we examine the rest of the list of chunks (starting at +\verb|arena->next|) looking for a chunk with adequate space. If \verb|n| +is very large, we may not find it right away or we may not find a +suitable chunk at all. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap185} +$\langle$Find a new chunk of memory {\footnotesize 71a}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ Chunk *ap = arena;@\\ +\mbox{}\verb@ Chunk *np = ap->next;@\\ +\mbox{}\verb@ while (np) {@\\ +\mbox{}\verb@ char *v = sizeof(Chunk) + (char *) np;@\\ +\mbox{}\verb@ if (v + n <= np->limit) {@\\ +\mbox{}\verb@ np->avail = v + n;@\\ +\mbox{}\verb@ arena = np;@\\ +\mbox{}\verb@ return v;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ ap = np;@\\ +\mbox{}\verb@ np = ap->next;@\\ +\mbox{}\verb@ }@\\ +\mbox{}\verb@ @$\langle$Allocate a new chunk of memory {\footnotesize 71b}$\rangle$\verb@@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 70c. +\end{list} +\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} \small +\begin{minipage}{\linewidth} \label{scrap186} +$\langle$Allocate a new chunk of memory {\footnotesize 71b}$\rangle\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@{@\\ +\mbox{}\verb@ size_t m = n + 10000;@\\ +\mbox{}\verb@ np = (Chunk *) malloc(m);@\\ +\mbox{}\verb@ np->limit = m + (char *) np;@\\ +\mbox{}\verb@ np->avail = n + sizeof(Chunk) + (char *) np;@\\ +\mbox{}\verb@ np->next = NULL;@\\ +\mbox{}\verb@ ap->next = np;@\\ +\mbox{}\verb@ arena = np;@\\ +\mbox{}\verb@ return sizeof(Chunk) + (char *) np;@\\ +\mbox{}\verb@}@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item Macro referenced in scrap 71a. +\end{list} +\end{minipage}\\[4ex] +\end{flushleft} +\subsection{Freeing Memory} + +To free all the memory in the arena, we need only point \verb|arena| +back to the first empty chunk. +\begin{flushleft} \small +\begin{minipage}{\linewidth} \label{scrap187} +\verb@"arena.c"@ {\footnotesize 71c }$\equiv$ +\vspace{-1ex} +\begin{list}{}{} \item +\mbox{}\verb@void arena_free()@\\ +\mbox{}\verb@{@\\ +\mbox{}\verb@ arena = &first;@\\ +\mbox{}\verb@}@\\ +\mbox{}\verb@@$\Diamond$ +\end{list} +\vspace{-1ex} +\footnotesize\addtolength{\baselineskip}{-1ex} +\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item File defined by scraps 9d, 70abc, 71c. +\end{list} +\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} + + +{\small\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item \verb@"arena.c"@ {\footnotesize Defined by scraps 9d, 70abc, 71c. +} +\item \verb@"global.c"@ {\footnotesize Defined by scrap 9e.} +\item \verb@"global.h"@ {\footnotesize Defined by scrap 7a.} +\item \verb@"html.c"@ {\footnotesize Defined by scraps 8e, 28ab, 32abc, 33a, 35a, 36c. +} +\item \verb@"input.c"@ {\footnotesize Defined by scraps 9a, 39d, 40ab, 42c. +} +\item \verb@"latex.c"@ {\footnotesize Defined by scraps 8d, 16d, 17a, 21b, 22a, 24b, 26b. +} +\item \verb@"main.c"@ {\footnotesize Defined by scraps 8b, 9f. +} +\item \verb@"names.c"@ {\footnotesize Defined by scraps 9c, 54ab, 55a, 56, 57ab, 58b, 60a, 62a, 63ab. +} +\item \verb@"output.c"@ {\footnotesize Defined by scraps 8f, 37c. +} +\item \verb@"pass1.c"@ {\footnotesize Defined by scraps 8c, 14b. +} +\item \verb@"scraps.c"@ {\footnotesize Defined by scraps 9b, 43abce, 44ab, 45bcde, 48bc, 49b, 52a, 63c, 64abcd, 65ab, 69ab. +} +\end{list}} + +\section{Macros} + + +{\small\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item $\langle$Accumulate scrap and return \verb|scraps++| {\footnotesize 46b}$\rangle$ {\footnotesize Referenced in scrap 45e.} +\item $\langle$Add \verb|scrap| to \verb|name|'s definition list {\footnotesize 16a}$\rangle$ {\footnotesize Referenced in scraps 15bc. +} +\item $\langle$Add current scrap to \verb|name|'s uses {\footnotesize 48a}$\rangle$ {\footnotesize Referenced in scrap 47b.} +\item $\langle$Add letters to scraps with duplicate page numbers {\footnotesize 52b}$\rangle$ {\footnotesize Referenced in scrap 52a.} +\item $\langle$Allocate a new chunk of memory {\footnotesize 71b}$\rangle$ {\footnotesize Referenced in scrap 71a.} +\item $\langle$Begin HTML scrap environment {\footnotesize 30e}$\rangle$ {\footnotesize Referenced in scraps 30ac. +} +\item $\langle$Begin the scrap environment {\footnotesize 19c}$\rangle$ {\footnotesize Referenced in scraps 19ab. +} +\item $\langle$Build \verb|source_name| and \verb|tex_name| {\footnotesize 12b}$\rangle$ {\footnotesize Referenced in scrap 12a.} +\item $\langle$Build failure functions {\footnotesize 67}$\rangle$ {\footnotesize Referenced in scrap 65a.} +\item $\langle$Build macro definition {\footnotesize 15c}$\rangle$ {\footnotesize Referenced in scrap 15a.} +\item $\langle$Build output file definition {\footnotesize 15b}$\rangle$ {\footnotesize Referenced in scrap 15a.} +\item $\langle$Check HTML at-sequence for end-of-scrap {\footnotesize 33b}$\rangle$ {\footnotesize Referenced in scrap 33a.} +\item $\langle$Check at-sequence for end-of-scrap {\footnotesize 22c}$\rangle$ {\footnotesize Referenced in scrap 22a.} +\item $\langle$Check for ambiguous prefix {\footnotesize 55b}$\rangle$ {\footnotesize Referenced in scrap 55a.} +\item $\langle$Check for end of scrap name and return {\footnotesize 49a}$\rangle$ {\footnotesize Referenced in scrap 48c.} +\item $\langle$Check for macro invocation in scrap {\footnotesize 51b}$\rangle$ {\footnotesize Referenced in scrap 50a.} +\item $\langle$Check for terminating at-sequence and return name {\footnotesize 60b}$\rangle$ {\footnotesize Referenced in scrap 60a.} +\item $\langle$Cleanup and install name {\footnotesize 61a}$\rangle$ {\footnotesize Referenced in scraps 60b, 61b, 62b. +} +\item $\langle$Collect include-file name {\footnotesize 42a}$\rangle$ {\footnotesize Referenced in scrap 41b.} +\item $\langle$Collect user-specified index entries {\footnotesize 47a}$\rangle$ {\footnotesize Referenced in scrap 46c.} +\item $\langle$Compare the temp file and the old file {\footnotesize 38b}$\rangle$ {\footnotesize Referenced in scrap 38a.} +\item $\langle$Copy \verb|defs->scrap| to \verb|file| {\footnotesize 50a}$\rangle$ {\footnotesize Referenced in scrap 49b.} +\item $\langle$Copy \verb|source_file| into \verb|html_file| {\footnotesize 28c}$\rangle$ {\footnotesize Referenced in scrap 28b.} +\item $\langle$Copy \verb|source_file| into \verb|tex_file| {\footnotesize 17b}$\rangle$ {\footnotesize Referenced in scrap 17a.} +\item $\langle$Copy macro into \verb|file| {\footnotesize 51c}$\rangle$ {\footnotesize Referenced in scrap 51b.} +\item $\langle$Create new name entry {\footnotesize 58a}$\rangle$ {\footnotesize Referenced in scraps 55a, 57b. +} +\item $\langle$Create new scrap, managed by \verb|writer| {\footnotesize 46a}$\rangle$ {\footnotesize Referenced in scrap 45e.} +\item $\langle$Expand tab into spaces {\footnotesize 22b}$\rangle$ {\footnotesize Referenced in scraps 22a, 33a, 51a. +} +\item $\langle$Extend goto graph with \verb|tree->spelling| {\footnotesize 66}$\rangle$ {\footnotesize Referenced in scrap 65b.} +\item $\langle$Fill in the middle of HTML scrap environment {\footnotesize 31a}$\rangle$ {\footnotesize Referenced in scraps 30ac. +} +\item $\langle$Fill in the middle of the scrap environment {\footnotesize 19d}$\rangle$ {\footnotesize Referenced in scraps 19ab. +} +\item $\langle$Find a new chunk of memory {\footnotesize 71a}$\rangle$ {\footnotesize Referenced in scrap 70c.} +\item $\langle$Finish HTML scrap environment {\footnotesize 31b}$\rangle$ {\footnotesize Referenced in scraps 30ac. +} +\item $\langle$Finish the scrap environment {\footnotesize 20a}$\rangle$ {\footnotesize Referenced in scraps 19ab. +} +\item $\langle$Format HTML macro name {\footnotesize 34a}$\rangle$ {\footnotesize Referenced in scrap 33b.} +\item $\langle$Format a user HTML index entry {\footnotesize 37a}$\rangle$ {\footnotesize Referenced in scrap 36c.} +\item $\langle$Format a user index entry {\footnotesize 27a}$\rangle$ {\footnotesize Referenced in scrap 26b.} +\item $\langle$Format an HTML index entry {\footnotesize 35b}$\rangle$ {\footnotesize Referenced in scrap 35a.} +\item $\langle$Format an index entry {\footnotesize 24c}$\rangle$ {\footnotesize Referenced in scrap 24b.} +\item $\langle$Format macro name {\footnotesize 23b}$\rangle$ {\footnotesize Referenced in scrap 22c.} +\item $\langle$Function prototypes {\footnotesize 14a, 16c, 27b, 37b, 39a, 43d, 51d, 53d, 64e, 69c}$\rangle$ {\footnotesize Referenced in scrap 7a.} +\item $\langle$Global variable declarations {\footnotesize 10ac, 39b, 44d, 53b}$\rangle$ {\footnotesize Referenced in scrap 7a.} +\item $\langle$Global variable definitions {\footnotesize 10bd, 39c, 45a, 53c}$\rangle$ {\footnotesize Referenced in scrap 9e.} +\item $\langle$Handle \verb|EOF| {\footnotesize 42b}$\rangle$ {\footnotesize Referenced in scrap 40b.} +\item $\langle$Handle an ``at'' character {\footnotesize 41a}$\rangle$ {\footnotesize Referenced in scrap 40b.} +\item $\langle$Handle at-sign during scrap accumulation {\footnotesize 46c}$\rangle$ {\footnotesize Referenced in scrap 46b.} +\item $\langle$Handle macro invocation in scrap {\footnotesize 47b}$\rangle$ {\footnotesize Referenced in scrap 46c.} +\item $\langle$Handle optional per-file flags {\footnotesize 59}$\rangle$ {\footnotesize Referenced in scrap 58b.} +\item $\langle$Handle tab characters on output {\footnotesize 51a}$\rangle$ {\footnotesize Referenced in scrap 50a.} +\item $\langle$Handle the file name in \verb|argv[arg]| {\footnotesize 12a}$\rangle$ {\footnotesize Referenced in scrap 11c.} +\item $\langle$Include files {\footnotesize 7b}$\rangle$ {\footnotesize Referenced in scrap 7a.} +\item $\langle$Insert appropriate indentation {\footnotesize 50c}$\rangle$ {\footnotesize Referenced in scraps 50ab. +} +\item $\langle$Insert debugging information if required {\footnotesize 50b}$\rangle$ {\footnotesize Referenced in scraps 50a, 51b. +} +\item $\langle$Interpret HTML at-sequence {\footnotesize 29}$\rangle$ {\footnotesize Referenced in scrap 28c.} +\item $\langle$Interpret at-sequence {\footnotesize 18}$\rangle$ {\footnotesize Referenced in scrap 17b.} +\item $\langle$Interpret command-line arguments {\footnotesize 10e, 11a}$\rangle$ {\footnotesize Referenced in scrap 9f.} +\item $\langle$Interpret the argument string \verb|s| {\footnotesize 11b}$\rangle$ {\footnotesize Referenced in scrap 11a.} +\item $\langle$Look for end of scrap name and return {\footnotesize 62b}$\rangle$ {\footnotesize Referenced in scrap 62a.} +\item $\langle$Open an include file {\footnotesize 41b}$\rangle$ {\footnotesize Referenced in scrap 41a.} +\item $\langle$Process a file {\footnotesize 13}$\rangle$ {\footnotesize Referenced in scrap 12a.} +\item $\langle$Process the remaining arguments (file names) {\footnotesize 11c}$\rangle$ {\footnotesize Referenced in scrap 9f.} +\item $\langle$Reverse cross-reference lists {\footnotesize 16b}$\rangle$ {\footnotesize Referenced in scrap 14b.} +\item $\langle$Save macro name {\footnotesize 47c}$\rangle$ {\footnotesize Referenced in scrap 47b.} +\item $\langle$Scan at-sequence {\footnotesize 15a}$\rangle$ {\footnotesize Referenced in scrap 14c.} +\item $\langle$Scan the source file, looking for at-sequences {\footnotesize 14c}$\rangle$ {\footnotesize Referenced in scrap 14b.} +\item $\langle$Search scraps {\footnotesize 68}$\rangle$ {\footnotesize Referenced in scrap 65a.} +\item $\langle$Skip over index entries {\footnotesize 23a}$\rangle$ {\footnotesize Referenced in scraps 22c, 33b. +} +\item $\langle$Skip until scrap begins, then return name {\footnotesize 61b}$\rangle$ {\footnotesize Referenced in scrap 60a.} +\item $\langle$Type declarations {\footnotesize 8a, 52c, 53a}$\rangle$ {\footnotesize Referenced in scrap 7a.} +\item $\langle$Warn (only once) about needing to rerun after Latex {\footnotesize 44c}$\rangle$ {\footnotesize Referenced in scraps 44a, 52a. +} +\item $\langle$Write HTML abbreviated definition list {\footnotesize 34b}$\rangle$ {\footnotesize Referenced in scrap 34a.} +\item $\langle$Write HTML defining scrap numbers {\footnotesize 35d}$\rangle$ {\footnotesize Referenced in scrap 35b.} +\item $\langle$Write HTML file defs {\footnotesize 31c}$\rangle$ {\footnotesize Referenced in scrap 30a.} +\item $\langle$Write HTML file's defining scrap numbers {\footnotesize 35c}$\rangle$ {\footnotesize Referenced in scrap 35b.} +\item $\langle$Write HTML index of file names {\footnotesize 34c}$\rangle$ {\footnotesize Referenced in scrap 29.} +\item $\langle$Write HTML index of macro names {\footnotesize 34d}$\rangle$ {\footnotesize Referenced in scrap 29.} +\item $\langle$Write HTML index of user-specified names {\footnotesize 36b}$\rangle$ {\footnotesize Referenced in scrap 29.} +\item $\langle$Write HTML macro declaration {\footnotesize 30d}$\rangle$ {\footnotesize Referenced in scrap 30c.} +\item $\langle$Write HTML macro definition {\footnotesize 30c}$\rangle$ {\footnotesize Referenced in scrap 29.} +\item $\langle$Write HTML macro defs {\footnotesize 31d}$\rangle$ {\footnotesize Referenced in scrap 30c.} +\item $\langle$Write HTML macro refs {\footnotesize 31e}$\rangle$ {\footnotesize Referenced in scrap 30c.} +\item $\langle$Write HTML output file declaration {\footnotesize 30b}$\rangle$ {\footnotesize Referenced in scrap 30a.} +\item $\langle$Write HTML output file definition {\footnotesize 30a}$\rangle$ {\footnotesize Referenced in scrap 29.} +\item $\langle$Write HTML referencing scrap numbers {\footnotesize 36a}$\rangle$ {\footnotesize Referenced in scrap 35b.} +\item $\langle$Write abbreviated definition list {\footnotesize 23c}$\rangle$ {\footnotesize Referenced in scrap 23b.} +\item $\langle$Write defining scrap numbers {\footnotesize 25b}$\rangle$ {\footnotesize Referenced in scrap 24c.} +\item $\langle$Write file defs {\footnotesize 20b}$\rangle$ {\footnotesize Referenced in scrap 19a.} +\item $\langle$Write file's defining scrap numbers {\footnotesize 25a}$\rangle$ {\footnotesize Referenced in scrap 24c.} +\item $\langle$Write index of file names {\footnotesize 23d}$\rangle$ {\footnotesize Referenced in scrap 18.} +\item $\langle$Write index of macro names {\footnotesize 24a}$\rangle$ {\footnotesize Referenced in scrap 18.} +\item $\langle$Write index of user-specified names {\footnotesize 26a}$\rangle$ {\footnotesize Referenced in scrap 18.} +\item $\langle$Write macro definition {\footnotesize 19b}$\rangle$ {\footnotesize Referenced in scrap 18.} +\item $\langle$Write macro defs {\footnotesize 20c}$\rangle$ {\footnotesize Referenced in scrap 19b.} +\item $\langle$Write macro refs {\footnotesize 21a}$\rangle$ {\footnotesize Referenced in scrap 19b.} +\item $\langle$Write out \verb|files->spelling| {\footnotesize 38a}$\rangle$ {\footnotesize Referenced in scrap 37c.} +\item $\langle$Write output file definition {\footnotesize 19a}$\rangle$ {\footnotesize Referenced in scrap 18.} +\item $\langle$Write referencing scrap numbers {\footnotesize 25c}$\rangle$ {\footnotesize Referenced in scrap 24c.} +\end{list}} + +\section{Identifiers} + +Knuth prints his index of identifiers 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. + + +{\small\begin{list}{}{\setlength{\itemsep}{-\parsep}\setlength{\itemindent}{-\leftmargin}} +\item \verb@already_warned@: 44c, \underline{44d}, 45a, 52a. +\item \verb@arena@: \underline{70b}, 70c, 71abc. +\item \verb@arena_free@: 13, 69c, \underline{71c}. +\item \verb@arena_getmem@: 16a, 43e, 45c, 46a, 47a, 48a, 54b, 58a, 65a, 66, 67, 68, 69c, \underline{70c}. +\item \verb@build_gotos@: 65a, \underline{65b}. +\item \verb@Chunk@: \underline{70a}, 70b, 71ab. +\item \verb@collect_file_name@: 15b, 19a, 30a, 53d, \underline{58b}. +\item \verb@collect_macro_name@: 15c, 19b, 30c, 53d, \underline{60a}. +\item \verb@collect_numbers@: 13, 51d, \underline{52a}. +\item \verb@collect_scrap@: 15bc, 43d, \underline{45e}. +\item \verb@collect_scrap_name@: 23b, 34a, 47b, 53d, \underline{62a}. +\item \verb@command_name@: \underline{10c}, 10de, 11bc, 15a, 17a, 21a, 23b, 28b, 31e, 34a, 38a, 41ab, 42ac, 44c, 46bc, 47a, 49a, 51c, 55b, 58b, 59, 60ab, 61ab, 62ab. +\item \verb@compare@: \underline{54a}, 55a, 56. +\item \verb@compare_flag@: \underline{10a}, 10b, 11b, 38a. +\item \verb@copy_scrap@: 16d, 19d, \underline{22a}, 28a, 31a, \underline{33a}. +\item \verb@depths@: \underline{64c}, 65a, 66, 67. +\item \verb@display_scrap_numbers@: 28a, \underline{32b}, 32c, 35d. +\item \verb@display_scrap_ref@: 28a, \underline{32a}, 32b, 34b, 37a. +\item \verb@double_at@: \underline{39d}, 41a, 42c. +\item \verb@EQUAL@: \underline{54a}, 55a, 56. +\item \verb@exit@: \underline{7b}, 9f, 11c, 38a, 41ab, 42ac, 46bc, 47a, 49a, 51c, 58b, 60ab, 61ab, 62ab. +\item \verb@EXTENSION@: \underline{54a}, 55a, 56. +\item \verb@FALSE@: \underline{8a}, 10ab, 11b, 18, 21b, 24a, 25b, 27a, 34d, 41a, 42c, 51c, 52a, 56, 58a, 59, 69ab. +\item \verb@fclose@: \underline{7b}, 17a, 28b, 38ab, 42b, 52a. +\item \verb@FILE@: \underline{7b}, 17a, 21b, 22a, 24b, 26b, 28b, 32abc, 33a, 35a, 36c, 38ab, 39d, 40a, 44ab, 49b, 52a. +\item \verb@file_names@: 13, 14b, 16b, 23d, 34c, \underline{53b}, 53c, 58b. +\item \verb@first@: 44a, 69b, \underline{70b}, 71c. +\item \verb@fopen@: \underline{7b}, 17a, 28b, 38ab, 41b, 42c, 52a. +\item \verb@format_entry@: 16d, 23d, 24a, \underline{24b}, 28a, 34cd, \underline{35a}. +\item \verb@format_user_entry@: 16d, 26a, \underline{26b}, 28a, 36b, \underline{36c}. +\item \verb@fprintf@: \underline{7b}, 11bc, 14b, 15a, 17a, 19abc, 21a, 23b, 24c, 27a, 28b, 30bd, 31e, 34a, 35b, 37a, 38a, 41ab, 42ac, 44ac, 46bc, 47a, 49a, 50b, 51c, 55b, 58b, 59, 60ab, 61ab, 62ab. +\item \verb@fputs@: \underline{7b}, 19abcd, 20abc, 21ab, 22ac, 23bcd, 24ac, 25ac, 26a, 27a, 30bde, 31abcde, 32abc, 33a, 34abcd, 35bc, 36ab, 37a, 44a. +\item \verb@getc@: \underline{7b}, 38b, 40b, 41ab, 42abc. +\item \verb@goto_lookup@: \underline{64d}, 66, 67, 68. +\item \verb@Goto_Node@: \underline{64a}, 64bcd, 65a, 66, 67, 68. +\item \verb@GREATER@: \underline{54a}, 55a, 56. +\item \verb@html_flag@: \underline{10a}, 10b, 12b, 13. +\item \verb@include_depth@: \underline{39d}, 41b, 42bc. +\item \verb@init_scraps@: 14b, 43d, \underline{43e}. +\item \verb@isgraph@: \underline{7b}, 42a, 58b, 62a. +\item \verb@islower@: \underline{7b}, 57a. +\item \verb@isspace@: \underline{7b}, 20a, 47a, 58b, 59, 60a, 61b. +\item \verb@isupper@: \underline{7b}, 57a. +\item \verb@LESS@: \underline{54a}, 55a, 56. +\item \verb@macro_names@: 14b, 16b, 24a, 34d, 49a, \underline{53b}, 53c, 61a. +\item \verb@main@: \underline{9f}. +\item \verb@malloc@: \underline{7b}, 71b. +\item \verb@Manager@: \underline{45b}, 45cde, 48bc, 50a, 68, 69b. +\item \verb@max_depth@: \underline{64c}, 65a, 66, 67. +\item \verb@Move_Node@: \underline{64b}, 64d, 66, 67. +\item \verb@Name@: 15bc, 19ab, 23b, 24b, 26b, 30ac, 34a, 35a, 36c, 37c, 47ab, 48c, 51c, \underline{53a}, 53bcd, 55a, 56, 57b, 58ab, 60a, 62a, 63ac, 65b, 68, 69b. +\item \verb@name_add@: 47a, 53d, \underline{57b}, 58b. +\item \verb@Name_Node@: \underline{63c}, 64a, 66, 67, 68. +\item \verb@number_flag@: \underline{10a}, 10b, 11b, 13, 52a. +\item \verb@op_char@: \underline{69a}, 69b. +\item \verb@output_flag@: \underline{10a}, 10b, 11b, 13. +\item \verb@pass1@: 13, 14a, \underline{14b}, 22c, 33b. +\item \verb@pop@: \underline{48b}, 48c, 49a, 50a, 51b, 68. +\item \verb@pop_scrap_name@: \underline{48c}, 51c. +\item \verb@PREFIX@: \underline{54a}, 55a, 56. +\item \verb@prefix_add@: 49a, 53d, \underline{55a}, 61a. +\item \verb@print_scrap_numbers@: 16d, 20bc, 21a, \underline{21b}, 25ac, 28a, 31cde, \underline{32c}, 35c, 36a. +\item \verb@push@: \underline{45c}, 45d, 46bc, 47c. +\item \verb@pushs@: \underline{45d}, 46c, 47c. +\item \verb@putc@: \underline{7b}, 17b, 18, 22abc, 23b, 24c, 25abc, 27a, 28c, 29, 33a, 34a, 35bd, 44a, 50ac, 51ab. +\item \verb@reject_match@: 65a, 68, \underline{69b}. +\item \verb@remove@: \underline{7b}, 38ab. +\item \verb@reverse@: 63a, \underline{63b}. +\item \verb@reverse_lists@: 16b, 53d, \underline{63a}. +\item \verb@robs_strcmp@: \underline{57a}, 57b. +\item \verb@root@: 55a, 57b, 58a, \underline{64c}, 65a, 66, 67, 68. +\item \verb@save_string@: 41b, 46a, 53d, \underline{54b}, 55a, 58a. +\item \verb@SCRAP@: \underline{43c}, 43e, 46a. +\item \verb@ScrapEntry@: \underline{43b}, 43ce, 46a. +\item \verb@scraps@: 10a, 17b, 19abc, 20bc, 21ab, 28c, 30abcd, 32bc, \underline{43c}, 43e, 45e, 46abc, 47a, 48a, 52ab, 65a, 68. +\item \verb@scrap_array@: \underline{43c}, 44a, 46ab, 50ab, 52ab, 68. +\item \verb@Scrap_Node@: 16a, 21b, 23c, 25abc, 27a, 32bc, 34b, 36a, 37a, 47a, 48a, 49b, \underline{52c}, 53a, 63ab, 68. +\item \verb@search@: 14b, 64e, \underline{65a}. +\item \verb@size_t@: \underline{7b}, 70c, 71b. +\item \verb@Slab@: \underline{43a}, 43b, 45bc, 46a, 48b. +\item \verb@SLAB_SIZE@: \underline{43a}, 45c, 48b, 69b. +\item \verb@source_file@: 17a, 28b, \underline{39d}, 40b, 41ab, 42abc. +\item \verb@source_get@: 14c, 15a, 17b, 18, 20a, 22ac, 23ad, 24a, 26a, 28c, 29, 31b, 33ab, 34cd, 36b, 39a, \underline{40b}, 41b, 42b, 46bc, 47ab, 58b, 59, 60ab, 61b, 62ab. +\item \verb@source_line@: 15a, \underline{39b}, 39c, 40b, 41ab, 42abc, 46ac, 47a, 55b, 58b, 59, 60a, 61a, 62ab. +\item \verb@source_name@: 12ab, 13, 15a, \underline{39b}, 39c, 41ab, 42abc, 46ac, 47a, 55b, 58b, 59, 60ab, 61ab, 62ab. +\item \verb@source_open@: 14b, 17a, 28b, 39a, \underline{42c}. +\item \verb@source_peek@: \underline{39d}, 40b, 41ab, 42bc. +\item \verb@stack@: \underline{40a}, 41b, 42b. +\item \verb@stderr@: \underline{7b}, 11bc, 14b, 15a, 17a, 21a, 23b, 28b, 31e, 34a, 38a, 41ab, 42ac, 44c, 46bc, 47a, 49a, 51c, 55b, 58b, 59, 60ab, 61ab, 62ab. +\item \verb@strlen@: \underline{7b}, 47c, 54ab, 69b. +\item \verb@sym_char@: \underline{69a}, 69b. +\item \verb@tempnam@: \underline{7b}, 38a. +\item \verb@tex_flag@: \underline{10a}, 10b, 11b, 13, 14b, 51c. +\item \verb@toupper@: \underline{7b}, 57a. +\item \verb@TRUE@: \underline{8a}, 10ab, 11b, 13, 18, 21b, 23d, 25b, 27a, 34c, 41a, 44bc, 48c, 51c, 56, 58a, 59, 69ab. +\item \verb@user_names@: 14b, 16b, 26a, 36b, 47a, \underline{53b}, 53c, 65a. +\item \verb@verbose_flag@: \underline{10a}, 10b, 11b, 14b, 17a, 28b, 38a. +\item \verb@write_files@: 13, 37b, \underline{37c}. +\item \verb@write_html@: 13, 27b, \underline{28b}. +\item \verb@write_scraps@: 38a, 43d, \underline{49b}, 51c. +\item \verb@write_scrap_ref@: 21b, 25b, 27a, 43d, \underline{44a}, 44b. +\item \verb@write_single_scrap_ref@: 19ab, 21a, 23c, 25ac, 27a, 30bd, 32a, 43d, \underline{44b}. +\item \verb@write_tex@: 13, 16c, \underline{17a}. +\end{list}} + +\fi + +\bibliographystyle{plain} +\bibliography{literate} + +\end{document} diff --git a/web/nuweb/nuweb0.87b/output.c b/web/nuweb/nuweb0.87b/output.c new file mode 100644 index 0000000000..7277fe7104 --- /dev/null +++ b/web/nuweb/nuweb0.87b/output.c @@ -0,0 +1,51 @@ +#include "global.h" +void write_files(files) + Name *files; +{ + while (files) { + write_files(files->llink); + { + char indent_chars[500]; + FILE *temp_file; + char *temp_name = tempnam(".", 0); + temp_file = fopen(temp_name, "w"); + if (!temp_file) { + fprintf(stderr, "%s: can't create %s for a temporary file\n", + command_name, temp_name); + exit(-1); + } + if (verbose_flag) + fprintf(stderr, "writing %s\n", files->spelling); + 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) + remove(temp_name); + else { + remove(files->spelling); + rename(temp_name, files->spelling); + } + } + else + rename(temp_name, files->spelling); + } + else { + remove(files->spelling); + rename(temp_name, files->spelling); + } + } + files = files->rlink; + } +} diff --git a/web/nuweb/nuweb0.87b/pass1.c b/web/nuweb/nuweb0.87b/pass1.c new file mode 100644 index 0000000000..0d9d140f75 --- /dev/null +++ b/web/nuweb/nuweb0.87b/pass1.c @@ -0,0 +1,64 @@ +#include "global.h" +void pass1(file_name) + char *file_name; +{ + if (verbose_flag) + fprintf(stderr, "reading %s\n", 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/nuweb0.87b/scraps.c b/web/nuweb/nuweb0.87b/scraps.c new file mode 100644 index 0000000000..dfa94db636 --- /dev/null +++ b/web/nuweb/nuweb0.87b/scraps.c @@ -0,0 +1,607 @@ +#include "global.h" +#define SLAB_SIZE 500 + +typedef struct slab { + struct slab *next; + char chars[SLAB_SIZE]; +} Slab; +typedef struct { + char *file_name; + int file_line; + int page; + char letter; + Slab *slab; +} ScrapEntry; +static ScrapEntry *SCRAP[256]; + +#define scrap_array(i) SCRAP[(i) >> 8][(i) & 255] + +static int scraps; +void init_scraps() +{ + scraps = 1; + SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry)); +} +void write_scrap_ref(file, num, first, page) + FILE *file; + int num; + int first; + int *page; +{ + if (scrap_array(num).page >= 0) { + if (first) + fprintf(file, "%d", scrap_array(num).page); + else if (scrap_array(num).page != *page) + fprintf(file, ", %d", scrap_array(num).page); + if (scrap_array(num).letter > 0) + fputc(scrap_array(num).letter, file); + } + else { + if (first) + putc('?', file); + else + fputs(", ?", file); + { + if (!already_warned) { + fprintf(stderr, "%s: you'll need to rerun nuweb after running latex\n", + command_name); + already_warned = TRUE; + } + } + } + *page = scrap_array(num).page; +} +void write_single_scrap_ref(file, num) + FILE *file; + int num; +{ + int page; + write_scrap_ref(file, num, TRUE, &page); +} +typedef struct { + Slab *scrap; + Slab *prev; + 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; + scrap_array(scraps).page = -1; + scrap_array(scraps).letter = 0; + 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->prev = scrap; + 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(¯o_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; +} +void collect_numbers(aux_name) + char *aux_name; +{ + if (number_flag) { + int i; + for (i=1; i<scraps; i++) + scrap_array(i).page = i; + } + else { + FILE *aux_file = fopen(aux_name, "r"); + already_warned = FALSE; + if (aux_file) { + char aux_line[500]; + while (fgets(aux_line, 500, aux_file)) { + int scrap_number; + int page_number; + char dummy[50]; + if (3 == sscanf(aux_line, "\\newlabel{scrap%d}{%[^}]}{%d}", + &scrap_number, dummy, &page_number)) { + if (scrap_number < scraps) + scrap_array(scrap_number).page = page_number; + else + { + if (!already_warned) { + fprintf(stderr, "%s: you'll need to rerun nuweb after running latex\n", + command_name); + already_warned = TRUE; + } + } + } + } + fclose(aux_file); + { + int scrap; + for (scrap=2; scrap<scraps; scrap++) { + if (scrap_array(scrap-1).page == scrap_array(scrap).page) { + if (!scrap_array(scrap-1).letter) + scrap_array(scrap-1).letter = 'a'; + scrap_array(scrap).letter = scrap_array(scrap-1).letter + 1; + } + } + } + } + } +} +typedef struct name_node { + struct name_node *next; + Name *name; +} Name_Node; +typedef struct goto_node { + Name_Node *output; /* list of words ending in this state */ + struct move_node *moves; /* list of possible moves */ + struct goto_node *fail; /* and where to go when no move fits */ + struct goto_node *next; /* next goto node with same depth */ +} 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(); +static int reject_match(); + +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.prev = 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]; + c = pop(&reader); + if (state && state->output) { + Name_Node *p = state->output; + do { + Name *name = p->name; + if (!reject_match(name, c, &reader) && + (!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); + } + } + } + } +} +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; + } +} +#define sym_char(c) (isalnum(c) || (c) == '_') + +static int op_char(c) + char c; +{ + switch (c) { + case '!': case '@': case '#': case '%': case '$': case '^': + case '&': case '*': case '-': case '+': case '=': case '/': + case '|': case '~': case '<': case '>': + return TRUE; + default: + return FALSE; + } +} +static int reject_match(name, post, reader) + Name *name; + char post; + Manager *reader; +{ + int len = strlen(name->spelling); + char first = name->spelling[0]; + char last = name->spelling[len - 1]; + char prev = '\0'; + len = reader->index - len - 2; + if (len >= 0) + prev = reader->scrap->chars[len]; + else if (reader->prev) + prev = reader->scrap->chars[SLAB_SIZE - len]; + if (sym_char(last) && sym_char(post)) return TRUE; + if (sym_char(first) && sym_char(prev)) return TRUE; + if (op_char(last) && op_char(post)) return TRUE; + if (op_char(first) && op_char(prev)) return TRUE; + return FALSE; +} diff --git a/web/nuweb/nuweb_ami/ami_nuweb0.90.lha b/web/nuweb/nuweb_ami/ami_nuweb0.90.lha Binary files differnew file mode 100644 index 0000000000..78f1fe31f2 --- /dev/null +++ b/web/nuweb/nuweb_ami/ami_nuweb0.90.lha diff --git a/web/nuweb/nuweb_ami/ami_nuweb0.90.readme b/web/nuweb/nuweb_ami/ami_nuweb0.90.readme new file mode 100644 index 0000000000..0bbdd7d12d --- /dev/null +++ b/web/nuweb/nuweb_ami/ami_nuweb0.90.readme @@ -0,0 +1,61 @@ +Short: NUWEB 0.90 - a simple literate programming language and tool +Type: dev/misc +Files: ami_nuweb0.90.lha, ami_nuweb0.90.readme +Uploader: Andreas Scherer (scherer@genesis.informatik.rwth-aachen.de) +Author: Preston Briggs (preston@cs.rice.edu) + +This is a version of Preston Briggs's NUWEB system, a simple literate +programming language and tool. + +Here is what Preston says: + + NUWEB works with any programming language and 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), e.g., C, Fortran, C++, yacc, lex, Scheme, + assembly, Postscript, and so forth. + +This is revision 0.90, for the Commodore Amiga. + +This distribution includes the Ami_NuWeb executable, all source files +(web and C), documentation and the (short) .dvi output of the user manual. + +Contents: + Ami_NuWeb The executable for any Amiga hardware, supports coprocessor + Makefile The original UNIX Makefile, unmodified for historic reasons + SMakefile The Makefile for SAS/C++ 6.55 + README Preston Briggs's original README file + ami_nuweb0.90.readme This file + literate.bib A BibTeX bibliography of literate programming references + nuweb.w The NUWEB source for NUWEB + nuweb.ch A change file for better ANSI compliance and Amiga support + nuweb.1 troff manual by Marc Mengel + ami_nuweb.w The merged result of nuweb.w and nuweb.ch + ami_nuwebdoc.dvi The short-form NUWEB user manual in .dvi form + global.h A header file used globally + main.c pass1.c latex.c html.c output.c input.c scraps.c + names.c arena.c global.c + Source files generated by NUWEB + catalogs/nuweb.cd Catalog description with the English texts + catalogs/nuweb.d.ct Catalog translation with the German texts + catalogs/nuweb.h Header file with the English texts + catalogs/deutsch/nuweb.catalog German system catalog + wc.w UNIX word-count utility as an example of NUWEB programming + +This port was done and completely tested with SAS/C++ 6.55 on an Amiga 2000 +by Andreas Scherer, Roland-Straße 16, 52070 Aachen, Germany. +I started from Tomas Willis' work, switched to NUWEB 0.87b, and added some +parts on my own. The current version 0.90 is based on the distribution +maintained by Marc Mengel. Thanks to Preston Briggs for NUWEB and to +Tomas Willis for the (S)Makefile and the `temp_file+=2' hack (this has +been superseded due to compatibility problems with DEC Ultrix 4.4 CC). + +Concerning the use of NUWEB with other human languages than English, I +implemented full localization of all strings used for terminal output as +well as LaTeX output for the Amiga version of NUWEB only. Should someone +create translations for other languages as well, I would appreciate to +receive a copy for inclusion in future distributions. + +March 28, 1995 diff --git a/web/nuweb/os2/nuweb087.txt b/web/nuweb/os2/nuweb087.txt new file mode 100644 index 0000000000..376c0dd0fa --- /dev/null +++ b/web/nuweb/os2/nuweb087.txt @@ -0,0 +1,81 @@ + +I have uploaded nuweb087.zip including sources by Preson Briggs and +the os/2 binary to : + +hobbes (ftp-os2.nmsu.edu), +now: /incoming +suggested: /os2/unix/tex + +cdrom (ftp-os2.cdrom.com), +now: /os2/incoming +suggested: /os2/tex + +and CTAN (ftp.shsu.edu): +now: /incoming +suggested: tex-archive/web/nuweb/os2/ (needs to be created). + +Replaces: Nothing really, except hopefully DOS versions of nuweb, + running on some OS/2 machines. + +Brief description: + +I was looking in news groups for a tool for literate programming (web) +that'd allow me to use Fortran. (Extended) Pascal, and C and was portable +to or available for OS/2. I came accross an interesting FAQ for the +comp.literate.programming, and I got his personal reply: + +------------------------------------------------------------------------ +Preston Briggs (preston@cs.rice.edu) wrote: +: >Also, my desire to have a version that (i) runs on OS/2 and (ii) +: >possibly incorporates Extended ANSI Pasal, while (iii) being based on +: >fweb to allow also g77 and gcc codes, made me think the -- for me very +: >unusual -- cross posting would enlarge my cross section and hence the +: >chance for an event ... + +: Lots of us have given up on the idea of pretty printing all our code, +: and use language-independent tools. For OS/2, you might check out +: nuweb and funnelweb. I wrote nuweb and unsurprisingly prefer it. I +: use it with C, C++, Fortran, Scheme, and Makefiles. Others have used +: it with Perl and such, but since it's language independent, you won't +: have any trouble with any flavor of Pascal, etc. + +: You can grab a copy via anonymous ftp from cs.rice.edu, in the +: directory public/preston. +------------------------------------------------------------------------ + + +To make it short: here is the working OS/2 version, with executable, +sources, and docs. + +All you need to use it is: +a) LaTeX installed on your system (2.09 or 2e are fine) +b) print the nuwebman.dvi or nuwebman.ps files, read them +c) an editor +d) the emx runtime system (emxrt.zip, from ftp-os2.cdrom.com, or + ftp-os2.nmsu.edu, for instance), version 0.9a fix pack 06 or + later installed. If you don't have it already installed + fetch the index of one of these sites, and search (grep) for + the pattern "emxrt.zip", or, if you can use regular expressions, + "^emxrt.zip". Then download and istall it. It contains the runtime + system in a dynamic link library which is shared by many other + programmes (say, gnu file utils, vi, emacs, less, probably even + emTeX programmes). This allows the executables to stay small and lean + but they won't run without it. +e) put nuweb.exe in your path. + +It seems that this is the right tool for cs people who don't crave nice, +mathematical pretty print stuff (like some physicists prefer), and for +projects that involve mixing of languages, or involve languages that +other webs don't know about. (I like fweb, too, but fweb can't do +Extended Pascal, as an example, and so nuweb seems to be it for that +case.) + +=============================================================================== +Stefan A. Deutscher 8-Mar-1996 | (+1-423-) voice fax +The University of Tennessee, Knoxville | UTK : 974-7838 974-7843 +Department of Physics and Astronomy | ORNL : 574-5897 574-1118 +401, A. H. Nielsen Building | home : 522-7845 522-7845 +Knoxville, T.N. 37996-1200, USA | email: sad@utk.edu + ... in Germany: | stefand@elphy.irz.hu-berlin.de +=============================================================================== + |