From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- web/nuweb/msdos/arena.c | 51 + web/nuweb/msdos/input.c | 164 ++ web/nuweb/msdos/latex.c | 361 ++++ web/nuweb/msdos/literate.bib | 119 ++ web/nuweb/msdos/main.c | 144 ++ web/nuweb/msdos/makefile | 45 + web/nuweb/msdos/names.c | 422 +++++ web/nuweb/msdos/nuweb.1 | 107 ++ web/nuweb/msdos/nuweb.bbl | 58 + web/nuweb/msdos/nuweb.tex | 3897 ++++++++++++++++++++++++++++++++++++++++++ web/nuweb/msdos/nuweb.toc | 43 + web/nuweb/msdos/nuweb.w | 2723 +++++++++++++++++++++++++++++ web/nuweb/msdos/output.c | 101 ++ web/nuweb/msdos/pass1.c | 116 ++ web/nuweb/msdos/readme | 28 + web/nuweb/msdos/readme.txt | 14 + web/nuweb/msdos/scraps.c | 542 ++++++ 17 files changed, 8935 insertions(+) create mode 100644 web/nuweb/msdos/arena.c create mode 100644 web/nuweb/msdos/input.c create mode 100644 web/nuweb/msdos/latex.c create mode 100644 web/nuweb/msdos/literate.bib create mode 100644 web/nuweb/msdos/main.c create mode 100644 web/nuweb/msdos/makefile create mode 100644 web/nuweb/msdos/names.c create mode 100644 web/nuweb/msdos/nuweb.1 create mode 100644 web/nuweb/msdos/nuweb.bbl create mode 100644 web/nuweb/msdos/nuweb.tex create mode 100644 web/nuweb/msdos/nuweb.toc create mode 100644 web/nuweb/msdos/nuweb.w create mode 100644 web/nuweb/msdos/output.c create mode 100644 web/nuweb/msdos/pass1.c create mode 100644 web/nuweb/msdos/readme create mode 100644 web/nuweb/msdos/readme.txt create mode 100644 web/nuweb/msdos/scraps.c (limited to 'web/nuweb/msdos') 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 +#include +#include + +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 +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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 @\\ +\verb@#include @\\ +\verb@#include @\\ +\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; indentmark) {@\\ +\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; ispelling} {\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; inext = 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; depthmoves;@\\ +\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; ifail;@\\ +\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 +@{@ +@ +@ +@ +@} + +We'll need at least two of the standard system include files. +@d Include files +@{#include +#include +#include +@| 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 +@{@ +@} + + +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 +@{@ +@} + +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 +@{@ +@} + + +The code controlling the creation of the output files is in {\tt output.c} +(see Section~\ref{output-files}). +@o output.c +@{@ +@} + + +\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 +@{@ +@} + +Creation and lookup of scraps is handled by routines in {\tt scraps.c} +(see Section~\ref{scraps}). +@o scraps.c +@{@ +@} + + +The handling of file names and macro names is detailed in {\tt names.c} +(see Section~\ref{names}). +@o names.c +@{@ +@} + +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 +@{@ +@} + + +\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; + @ + @ + 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++ == '-') { + @ + 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 { + @ + 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]; + @ + @ +}@} + + +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; + @ + if (tex_flag) + search(); + @ +} +@| 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 == '@@') + @ + 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': @ + break; + case 'D': + case 'd': @ + 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 */ + @ +}@} + + +@d Build macro definition +@{{ + Name *name = collect_macro_name(); + int scrap = collect_scrap(); + @ +}@} + +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); + @ + 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 == '@@') + @ + 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': @ + break; + case 'D': big_definition = TRUE; + case 'd': @ + break; + case 'f': @ + break; + case 'm': @ + break; + case 'u': @ + 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(); + @ + fprintf(tex_file, "\\verb@@\"%s\"@@", name->spelling); + fprintf(tex_file, " {\\footnotesize %d }$\\equiv$\n", scraps++); + @ + @ + @ +}@} + +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(); + @ + fprintf(tex_file, "$\\langle$%s", name->spelling); + fprintf(tex_file, " {\\footnotesize %d}$\\rangle\\equiv$\n", scraps++); + @ + @ + @ + @ +}@} + + +@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 '@@': @ + break; + case '\n': fputs("@@\\\\\n\\verb@@", file); + indent = 0; + break; + case '\t': @ + 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 '|': @ + case '}': putc('@@', file); + return; + case '<': @ + 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); + @ + 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); + @ + } + else { + fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling); + @ + fputs("}$\\rangle$ ", tex_file); + @ + } + 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); + @ + 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); + @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) + @ + 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: @ + return c; + case '@@': @ + 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': @ + 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]; + @ + 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; + @ + @ +} +@| 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 '@@': @ + 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 '|': @ + case '}': push('\0', &writer); + return scraps++; + case '<': @ + 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(); + @ + @ + 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 == '@@') + @ + 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) { + @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; + @ + reader.scrap = scrap_array(defs->scrap).slab; + reader.index = 0; + c = pop(&reader); + while (c) { + switch (c) { + case '@@': @ + break; + case '\n': putc(c, file); + line_number++; + @ + break; + case '\t': @ + 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); + @ +}@} + + +@d Insert approp... +@{{ + if (indent_flag) { + if (tab_flag) + for (indent=0; indent + 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 '<': @ + @ + 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: @ + return node; + } + node = *root; + } + @ +} +@| 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; + } + @ +} +@| 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); + @ + 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 '@@': @ + break; + case '\t': + case ' ': *p++ = ' '; + do + c = source_get(); + while (c == ' ' || c == '\t'); + break; + case '\n': @ + 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 '{': @ + 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() == '{') + @ + 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 '@@': @ + 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 '>': @ + 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 + @ +} +@| search @} + + + +@o scraps.c +@{static void build_gotos(tree) + Name *tree; +{ + while (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; inext = 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; depthmoves; + 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; ifail; + 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; + } + @ +} +@| 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; + } + @ +}@} + + +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 +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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; indentscrap).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 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; imoves; + 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; ifail; + 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; inext = 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; + } +} -- cgit v1.2.3