diff --git a/books/bookvol7.pamphlet b/books/bookvol7.pamphlet
index 9dcb878..7d7cb57 100644
--- a/books/bookvol7.pamphlet
+++ b/books/bookvol7.pamphlet
@@ -194,9 +194,7871 @@ November 10, 2003 ((iHy))
 \pagenumbering{arabic}
 \setcounter{chapter}{0} % Chapter 1
 \chapter{Overview}
-This book contains the hyperdoc routines for Axiom.
-\chapter{Support Routines}
-\chapter{Hyperdoc}
+
+This book covers 5 top level commands that make up the Axiom Hyperdoc
+browser. The primary command is the hypertex command which can be run
+as a standalone program to browse the Axiom documentation. It can also
+be run by Axiom to enable lookup of information in the Axiom runtime.
+
+\section{hypertex}
+\begin{verbatim}
+Usage: hypertex [-s]
+\end{verbatim}
+
+\section{htsearch}
+Construct a page with a menu of references to the word.
+The syntax of the command is:
+\begin{verbatim}
+Usage: htsearch word
+\end{verbatim}
+
+\section{spadbuf}
+\begin{verbatim}
+Usage: spadbuf page_name [completion_files] 
+\end{verbatim}
+
+\section{hthits}
+\begin{verbatim}
+Usage: hthits pattern htdb-file
+\end{verbatim}
+
+\section{ex2ht}
+\begin{verbatim}
+Usage: ex2ht exfile.ht ...
+\end{verbatim}
+
+\section{htadd}
+HyperDoc database file manager
+\begin{verbatim}
+Usage: htadd [-s|-l|-f db-directory] [-d|-n] filenames
+\end{verbatim}
+
+\chapter{The Hyperdoc Browser}
+\section{addfile.h}
+<<addfile.h>>=
+#ifndef _ADDFILE_H_
+#define _ADDFILE_H_ 1
+
+extern char *gDatabasePath;
+
+#endif
+@
+\section{addfile.c}
+<<addfile.c>>=
+#define _ADDFILE_C
+#include "debug.h"
+
+<<hyper.h>>
+<<addfile.h>>
+
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "all-hyper-proto.h1"
+
+
+char *gDatabasePath = NULL;
+
+static int
+strpostfix(char *s, char *t)
+{
+    int slen = strlen(s), tlen = strlen(t);
+
+    if (tlen > slen)
+        return 0;
+    while (tlen > 0)
+        if (s[--slen] != t[--tlen])
+            return 0;
+    return 1;
+}
+
+/* extend_ht : just checks the name and adds a .ht if needed */
+
+void
+extend_ht(char *name)
+{
+
+    if (!strpostfix(name, ".ht") && !strpostfix(name, ".pht"))
+        strcat(name, ".ht");
+    return;
+}
+
+#define cwd(n) ((n[0] == '.' && n[1] == '/')?(1):(0))
+
+/*
+ * This procedure is sent a filename, and from it tries to build the full
+ * filename, this it returns in the fullname variable. If the file is not
+ * found, then it returns a -1. The fname is the fullpath name for the file,
+ * including the .ht extension. The aname is the filename minus the added .ht
+ * extension, and the pathname.
+ */
+
+static int
+build_ht_filename(char *fname, char *aname, char *name)
+{
+    char cdir[256];
+    char *c_dir;
+    char *HTPATH;
+    char *trace;
+    char *trace2;
+    int ht_file;
+
+    if (cwd(name)) {
+        /* user wants to use the current working directory */
+        c_dir = (char *) getcwd(cdir, 254);
+        strcpy(fname, c_dir);
+
+        /* Now add the rest of the filename */
+        strcat(fname, "/");
+        strcat(fname, &name[2]);
+
+        /** now copy the actual file name to addname **/
+        for (trace = &name[strlen(name)]; trace != name &&
+             (*trace != '/'); trace--);
+        if (trace == name) {
+            fprintf(stderr, "ht_open_file: Didn't expect a filename like %s\n",
+                    name);
+            exit(-1);
+        }
+        trace++;
+        strcpy(aname, trace);
+
+        /** add  the .ht extension if needed **/
+        extend_ht(aname);
+        extend_ht(fname);
+
+        /* Now just try to access the file */
+        return (access(fname, R_OK));
+    }
+    else if (pathname(name)) {
+        /* filename already has the path specified */
+        strcpy(fname, name);
+
+        /** now copy the actual file name to addname **/
+        for (trace = &name[strlen(name)]; trace != name &&
+             (*trace != '/'); trace--);
+        if (trace == name) {
+            fprintf(stderr, "ht_open_file: Didn't expect a filename like %s\n",
+                    name);
+            exit(-1);
+        }
+        trace++;
+        strcpy(aname, trace);
+
+        /** add  the .ht extension if needed **/
+        extend_ht(aname);
+        extend_ht(fname);
+
+        /* Now just try to access the file */
+        return (access(fname, R_OK));
+    }
+    else {/** If not I am going to have to append path names to it **/
+        HTPATH = (char *) getenv("HTPATH");
+        if (HTPATH == NULL) {
+        /** The user does not have a HTPATH, so I will use the the directory
+        $AXIOM/doc/hypertex/pages/ as the default path ***/
+          char *spad = (char *) getenv("AXIOM");
+          if (spad == NULL) {
+            fprintf(stderr,
+            "ht_file_open:Cannot find ht data base: setenv HTPATH or AXIOM\n");
+             exit(-1);
+          }
+          HTPATH = (char *) halloc(1024 * sizeof(char), "HTPATH");
+          strcpy(HTPATH, spad);
+          strcat(HTPATH, "/doc/hypertex/pages");
+        }
+
+        /** Now that I have filled HTPATH, I should try to open a file by the
+          given name **/
+        strcpy(aname, name);
+        extend_ht(aname);
+        for (ht_file = -1, trace2 = HTPATH;
+             ht_file == -1 && *trace2 != '\0';) {
+            for (trace = fname; *trace2 != '\0' && (*trace2 != ':');)
+                *trace++ = *trace2++;
+            *trace++ = '/';
+            *trace = 0;
+            if (!strcmp(fname, "./")) {
+                /** The person wishes me to check the current directory too **/
+                getcwd(fname, 256);
+                strcat(fname, "/");
+            }
+            if (*trace2)
+                trace2++;
+            strcat(fname, aname);
+            ht_file = access(fname, R_OK);
+        }
+        return (ht_file);
+    }
+}
+
+static int
+pathname(char *name)
+{
+    while (*name)
+        if (*name++ == '/')
+            return 1;
+
+    return 0;
+}
+
+/** This procedure opens the proper HT file **/
+
+FILE *
+ht_file_open(char *fname, char *aname, char *name)
+{
+    FILE *ht_fp;
+    int ret_value;
+
+    ret_value = build_ht_filename(fname, aname, name);
+    if (ret_value == -1) {
+        fprintf(stderr, "ht_file_open: Unknown file %s\n", fname);
+        exit(-1);
+    }
+
+    ht_fp = fopen(fname, "r");
+    if (ht_fp == NULL) {
+        perror("ht_file_open");
+        exit(-1);
+    }
+    return (ht_fp);
+}
+
+/*
+ * This function is responsible for actually opening the database file. For the
+ * moment it gets the $AXIOM environment variable, and appends to it
+ * "doc/hypertex/ht.db", and then opens it
+ */
+
+/*
+ * Modified on 12/3/89 to take a second argument. This argument tells the
+ * open routine whether it is reading the db file, or writing it. If writing
+ * is true, then I should check to insure I have proper write access.
+ * -JMW
+ */
+
+/*
+ * Modified again on 12/9/89 so that it now uses HTPATH as the path name. Now
+ * it initially loads up the path name into a static variable. Then upon
+ * every trip, it gets the next ht.db found. It returns NULL when no ht.db is
+ * found. -JMW
+ */
+
+
+FILE *
+db_file_open(char *db_file)
+{
+    static char *db_path_trace = NULL;
+    char *db_file_trace;
+    FILE *db_fp;
+    char *spad;
+
+    /*
+     * The first time through is the only time this could be true. If so, then
+     * create the default HTPATH for gDatabasePath.
+     */
+/*    fprintf(stderr,"addfile:db_file_open: entered db_file=%s\n",db_file);*/
+    if (gDatabasePath == NULL) {
+        gDatabasePath = (char *) getenv("HTPATH");
+        if (gDatabasePath == NULL) {
+            spad = (char *) getenv("AXIOM");
+            if (spad == NULL) {
+/*                fprintf(stderr,
+                   "addfile:db_file_open: Cannot find ht data base path:\n");*/
+                exit(-1);
+            }
+            gDatabasePath = (char *) halloc(sizeof(char) * 1024, "db_file_open");
+            strcpy(gDatabasePath, spad);
+            strcat(gDatabasePath, "/doc/hypertex/pages");
+        }
+        db_path_trace = gDatabasePath;
+    }
+/*fprintf(stderr,"addfile:db_file_open: db_path_trace=%s\n",db_path_trace);*/
+    /*
+     * Now Loop until I find one with okay filename
+     */
+
+    for (db_fp = NULL; db_fp == NULL && *db_path_trace != '\0';) {
+        for (db_file_trace = db_file; *db_path_trace != ':' &&
+             *db_path_trace != '\0'; db_path_trace++)
+            *db_file_trace++ = *db_path_trace;
+        *db_file_trace = '\0';
+        strcat(db_file_trace, "/ht.db");
+/*fprintf(stderr,"addfile:db_file_open: db_file_trace=%s\n",db_file_trace);*/
+/*fprintf(stderr,"addfile:db_file_open: db_file=%s\n",db_file);*/
+
+        db_fp = fopen(db_file, "r");
+
+        if (*db_path_trace != '\0')
+            db_path_trace++;
+    }
+/*    if (db_fp == NULL)
+      fprintf(stderr,"addfile:db_file_open: exit (null)\n");
+    else
+      fprintf(stderr,"addfile:db_file_open: exit opened\n");
+*/
+    return (db_fp);
+}
+
+
+FILE *
+temp_file_open(char *temp_db_file)
+{
+    FILE *temp_db_fp;
+
+    /** Just make the name and open it **/
+
+    strcpy(temp_db_file, temp_dir);
+    strcat(temp_db_file, "ht2.db" /* db_file_name */ );
+    temp_db_fp = fopen(temp_db_file, "w");
+
+    if (temp_db_fp == NULL) {
+        perror("temp_file_open");
+        exit(-1);
+    }
+    return temp_db_fp;
+}
+@
+\section{cond.c}
+<<cond.c>>=
+/******************************************************************************
+ *
+ * cond.c:  Routines for handling "cond" nodes.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _COND_C
+#include "debug.h"
+
+<<hyper.h>>
+
+#include "all-hyper-proto.h1"
+#include "sockio-c.h1"
+
+
+void
+insert_cond(char *label, char *cond)
+{
+    CondNode *condnode = (CondNode *) hash_find(gWindow->fCondHashTable, label);
+
+    /*
+     * This routine creates a new cond node and inserts it into the
+     * current cond table
+     */
+    if (condnode) {
+        fprintf(stderr, "Error: \\%s is declared twice \n", label);
+        print_page_and_filename();
+        jump();
+    }
+    condnode = alloc_condnode();
+    condnode->label = halloc(strlen(label) + 1, "Condnode->label");
+    condnode->cond = halloc(strlen(cond) + 1, "Condnode->cond");
+    strcpy(condnode->label, label);
+    strcpy(condnode->cond, cond);
+    hash_insert(gWindow->fCondHashTable, (char *) condnode, condnode->label);
+}
+
+void
+change_cond(char *label, char *newcond)
+{
+    CondNode *condnode = (CondNode *) hash_find(gWindow->fCondHashTable, label);
+
+    if (condnode == NULL) {
+        fprintf(stderr, "Error: Tried to set an uncreated cond %s\n", label);
+    }
+    else {
+        free(condnode->cond);
+        condnode->cond = halloc(strlen(newcond) + 1, "Condnode->cond");
+        strcpy(condnode->cond, newcond);
+    }
+}
+
+static int
+check_memostack(TextNode *node)
+{
+    char *buffer;
+    int stackp = gWindow->fMemoStackIndex;
+    int found = 0;
+    HyperDocPage *page;
+
+    buffer = print_to_string(node->data.node);
+
+    /*
+     * Once we have done that much, search down the stack for the
+     * proper page
+     */
+
+    while (!found && stackp > 0) {
+        page = gWindow->fMemoStack[--stackp];
+        if (!strcmp(page->name, buffer))
+            found = 1;
+    }
+    return found;
+}
+
+int
+check_condition(TextNode *node)
+{
+    CondNode *cond;
+    InputBox *box;
+    int ret_val;
+
+    /* checks the condition presented and returns a 1 or a 0 */
+    switch (node->type) {
+      case Cond:
+        cond = (CondNode *) hash_find(gWindow->fCondHashTable, node->data.text);
+        if (!strcmp("0", cond->cond))
+            return 0;
+        else
+            return 1;
+      case Boxcond:
+        box = (InputBox *) hash_find(gWindow->page->box_hash, node->data.text);
+        return (box->picked);
+      case Haslisp:
+        if (spad_socket != NULL) {
+            ret_val = send_int(spad_socket, TestLine);
+            return (ret_val + 1);
+        }
+        else
+            return 0;
+      case Hasup:
+        return need_up_button;
+      case Hasreturn:
+        return gWindow->fMemoStackIndex;
+      case Hasreturnto:
+        return (check_memostack(node));
+      case Lastwindow:
+        return (gSessionHashTable.num_entries == 1 || gParentWindow == gWindow);
+      default:
+        return 0;
+    }
+}
+@
+\section{debug.c}
+<<debug.c>>=
+#define  _DEBUG_C
+#include "debug.h"
+
+#ifdef free
+#undef free
+hfree(char *p) {
+  free(p);
+}
+#endif
+
+@
+\section{dialog.h}
+<<dialog.h>>=
+#ifndef _DIALOG_H_
+#define _DIALOG_H_ 1
+
+<<hyper.h>>
+
+#endif
+@
+\section{dialog.c}
+<<dialog.c>>=
+/******************************************************************************
+ *
+ * dialog.c:
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _DIALOG_C
+#include "debug.h"
+
+<<dialog.h>>
+<<keyin.h>>
+<<mem.h>>
+<<display.h>>
+<<group.h>>
+
+#include <X11/keysym.h>
+
+#define min(x,y)     ( (x<y)?(x):(y))
+
+#include "all-hyper-proto.h1"
+
+
+static void
+redraw_win(void)
+{
+    XUnmapSubwindows(gXDisplay, gWindow->fMainWindow);
+    XUnmapSubwindows(gXDisplay, gWindow->fScrollWindow);
+    XFlush(gXDisplay);
+    show_page(gWindow->page);
+}
+
+static char *
+mystrncpy(char *buff1, char *buff2, int n)
+{
+    /*
+     * copies the characters from buff1 to buff2 starting at position buff2 +
+     * n and buff1 + n
+     */
+
+    int i;
+
+    for (i = n - 1; i >= 0; i--)
+        *(buff1 + i) = *(buff2 + i);
+    return buff2;
+}
+
+static void
+inc_line_numbers(LineStruct *line)
+{
+    for (; line != NULL; line = line->next)
+        line->line_number++;
+}
+
+static void
+dec_line_numbers(LineStruct *line)
+{
+    for (; line != NULL; line = line->next)
+        line->line_number--;
+    return;
+}
+
+static void
+decrease_line_numbers(LineStruct *line, int am)
+{
+    for (; line != NULL; line = line->next)
+        line->line_number -= am;
+}
+
+static void
+overwrite_buffer(char *buffer, InputItem *item)
+{
+    LineStruct *newline;
+    LineStruct *addline = item->curr_line;
+    /*int bufflen = strlen(buffer);*/
+    int nl = 0;
+    int cursor_y;
+    int size = item->size;
+
+    /* add a single character */
+
+    cursor_y = (addline->line_number - 1) * line_height;
+    if (addline->buff_pntr == size) {
+        clear_cursor(item);
+        if (addline->len <= size) {
+            nl = 1;
+            addline->buffer[size] = '_';
+            addline->buffer[size + 1] = 0;
+            addline->len = size + 1;
+            newline = (LineStruct *) alloc_inputline(size + 2);
+            newline->line_number = addline->line_number + 1;
+            inc_line_numbers(addline->next);
+            newline->next = addline->next;
+            newline->prev = addline;
+            if (addline->next)
+                addline->next->prev = newline;
+            addline->next = newline;
+            item->num_lines++;
+            cursor_y += line_height;
+            item->curr_line = addline = newline;
+        }
+        else {
+            item->curr_line = addline = addline->next;
+        }
+        addline->len = 1;
+        addline->buff_pntr = 1;
+        addline->buffer[0] = buffer[0];
+    }
+    else {
+        addline->buffer[addline->buff_pntr] = buffer[0];
+        clear_cursor(item);
+        if (++addline->buff_pntr > addline->len)
+            addline->len++;
+    }
+
+    /* now set up the current line */
+    if (item->curr_line->buff_pntr >= item->size &&
+        item->curr_line->next != NULL && !item->curr_line->next->len) {
+        /* I should actually be on the next line */
+        item->curr_line->buffer[item->size] = '_';
+        item->curr_line->len = item->size + 1;
+        XDrawString(gXDisplay, item->win, gWindow->fInputGC, start_x,
+                    cursor_y + start_y,
+                    addline->buffer,
+                    addline->len);
+        item->curr_line = item->curr_line->next;
+        item->curr_line->buff_pntr = 0;
+        item->curr_line->changed = 1;
+    }
+
+    if (!nl) {
+        XDrawString(gXDisplay, item->win, gWindow->fInputGC, start_x,
+                    cursor_y + start_y,
+                    addline->buffer,
+                    addline->len);
+        draw_cursor(item);
+    }
+    else
+        redraw_win();
+}
+
+/*
+ * This routine takes the current line and moves it num forward. The
+ * only way I have to move any other lines forward is if this line has length
+ * > size
+ */
+
+static int
+move_sym_forward(LineStruct *line, int num, int size, InputItem *sym)
+{
+    LineStruct *newline;
+    int diff;
+    int nl = 0;
+
+    if (line->len > size) {
+        nl = move_sym_forward(line->next, num, size, sym);
+        strncpy(line->next->buffer,
+                &line->buffer[sym->size - num], line->len);
+        strncpy(&line->buffer[num],
+                line->buffer, num);
+        line->changed = 1;
+        return nl;
+    }
+    else {
+        if (line->len + num > size) {
+            diff = line->len + num - size;
+            newline = alloc_inputline(size);
+            newline->len = diff;
+            newline->line_number = line->line_number++;
+            inc_line_numbers(line->next);
+            sym->num_lines++;
+            newline->next = line->next;
+            newline->prev = line;
+            if (line->next)
+                line->next->prev = newline;
+            line->next = newline;
+            strncpy(newline->buffer, &line->buffer[size - diff], diff);
+            strncpy(&line->buffer[num], line->buffer, num);
+            line->buffer[size] = '_';
+            line->buffer[size + 1] = 0;
+            line->len = size + 1;
+            return 1;
+        }
+        else {
+            strncpy(&line->buffer[num], line->buffer, line->len);
+            line->len += num;
+            line->changed = 1;
+            return 0;
+        }
+    }
+}
+
+static void
+clear_cursorline(InputItem *sym)
+{
+    XCharStruct extents;
+    int dir, asc, des;
+    int cursor_y;
+
+    XTextExtents(gInputFont, sym->curr_line->buffer,
+                 sym->curr_line->buff_pntr,
+                 &dir, &asc, &des, &extents);
+    cursor_y = (sym->curr_line->line_number - 1) * line_height;
+    sym->cursor_x = start_x + extents.width;
+    XClearArea(gXDisplay, sym->win, sym->cursor_x, cursor_y,
+               gWindow->width, line_height, False);
+    XDrawString(gXDisplay, sym->win, gWindow->fInputGC, start_x, cursor_y + start_y,
+                sym->curr_line->buffer,
+                sym->curr_line->len);
+}
+
+static void
+insert_buffer(char *buffer, InputItem *sym)
+{
+    /*int num = strlen(buffer);*/
+    LineStruct *line = sym->curr_line;
+    LineStruct *newline;
+    int nl = 0;
+    int size = sym->size;
+
+    if (line->len < size) {
+        /* they will all fit where I am so just copy them forward */
+        line->len++;
+        mystrncpy(&(line->buffer[line->buff_pntr + 1]),
+                  &(line->buffer[line->buff_pntr]),
+                  line->len - line->buff_pntr + 1);
+        line->buffer[line->buff_pntr] = buffer[0];
+        clear_cursorline(sym);
+        line->buff_pntr++;
+        draw_cursor(sym);
+        return;
+    }
+
+    if (line->len > sym->size) {
+        nl = move_sym_forward(line->next, 1, size, sym);
+        if (line->buff_pntr > size) {
+            line->changed = 1;
+            line = line->next;
+            line->buffer[0] = buffer[0];
+            line->len++;
+            line->buff_pntr = 1;
+            line->changed = 1;
+        }
+        else {
+            line->next->buffer[0] = line->buffer[size - 1];
+            line->changed = 1;
+            strncpy(&line->buffer[line->buff_pntr + 1],
+                &line->buffer[line->buff_pntr], size - line->buff_pntr - 1);
+            line->buffer[line->buff_pntr++] = buffer[0];
+            line->changed = 1;
+            if (line->buff_pntr >= size) {
+                sym->curr_line = line->next;
+                sym->curr_line->buff_pntr = 0;
+            }
+        }
+    }
+    else {
+        nl = 1;
+        newline = alloc_inputline(size);
+        newline->line_number = line->line_number + 1;
+        inc_line_numbers(line->next);
+        sym->num_lines++;
+        newline->next = line->next;
+        newline->prev = line;
+        if (line->next)
+            line->next->prev = newline;
+        line->next = newline;
+
+        /*
+         * was line->buff_pntr++;
+         */
+        if (line->buff_pntr >= size) {
+            /* we are the leaders of the line */
+            newline->buff_pntr = 1;
+            newline->buffer[0] = buffer[0];
+            newline->len = 1;
+            sym->curr_line = newline;
+        }
+        else {
+            /* we are not the leaders */
+            newline->buffer[0] = line->buffer[size - 1];
+            newline->len = 1;
+            strncpy(&line->buffer[line->buff_pntr + 1],
+                    &line->buffer[line->buff_pntr], size - line->buff_pntr);
+            if (line->buff_pntr < size - 1) {
+                line->buffer[line->buff_pntr++] = buffer[0];
+            }
+            else {
+                line->buffer[line->buff_pntr] = buffer[0];
+                newline->buff_pntr = 0;
+                sym->curr_line = newline;
+            }
+
+        }
+        line->buffer[size] = '_';
+        line->buffer[size + 1] = 0;
+        line->len = size + 1;
+    }
+    if (nl)
+        redraw_win();
+    else
+        update_inputsymbol(sym);
+
+}
+
+void
+add_buffer_to_sym(char *buffer,InputItem *sym)
+{
+    if (gInInsertMode)
+        insert_buffer(buffer, sym);
+    else
+        overwrite_buffer(buffer, sym);
+}
+
+void
+draw_inputsymbol(InputItem *sym)
+{
+    int y_spot = start_y;
+    LineStruct *cline;
+    XCharStruct extents;
+    int dir, asc, des;
+
+
+#if 0
+    int cursor_y;
+    cursor_y = (sym->curr_line->line_number - 1) * line_height;
+#endif
+
+    XClearWindow(gXDisplay, sym->win);
+
+    XTextExtents(gInputFont, sym->curr_line->buffer,
+                 sym->curr_line->buff_pntr,
+                 &dir, &asc, &des, &extents);
+    sym->cursor_x = start_x + extents.width;
+
+    /*
+     * While the list of input strings is not NULL, I should just keep
+     * drawing them
+     */
+    for (cline = sym->lines; cline != NULL;
+         cline = cline->next, y_spot += line_height) {
+        /* Now I should draw the initial string ** */
+        cline->changed = 0;
+        XDrawString(gXDisplay, sym->win, gWindow->fInputGC, start_x, y_spot,
+                    cline->buffer,
+                    cline->len);
+
+    }
+    if (gWindow->page->current_item == sym)
+        draw_cursor(sym);
+}
+
+void
+update_inputsymbol(InputItem *sym)
+{
+    int y_spot = start_y;
+    LineStruct *cline;
+    XCharStruct extents;
+    int dir, asc, des;
+    /*int cleared = 0;*/
+    int clear_y;
+    int clear_width;
+    int clear_height;
+
+#if 0
+    int cursor_y;
+    cursor_y = (sym->curr_line->line_number - 1) * line_height;
+#endif
+
+    clear_width = (sym->size + 1) * gInputFont->max_bounds.width + 10;
+    clear_height = line_height;
+    clear_y = 0;
+
+
+    XTextExtents(gInputFont, sym->curr_line->buffer,
+                 sym->curr_line->buff_pntr,
+                 &dir, &asc, &des, &extents);
+    sym->cursor_x = start_x + extents.width;
+
+    /*
+     * While the list of input strings is not NULL, I should just keep
+     * drawing them
+     */
+    for (cline = sym->lines; cline != NULL;
+         cline = cline->next, y_spot += line_height, clear_y += line_height)
+        /* Now I should draw the initial string ** */
+        if (cline->changed) {
+            cline->changed = 0;
+            XClearArea(gXDisplay, sym->win, 0, clear_y,
+                       clear_width, clear_height, False);
+            XDrawString(gXDisplay, sym->win, gWindow->fInputGC, start_x, y_spot,
+                        cline->buffer,
+                        cline->len);
+        }
+    draw_cursor(sym);
+}
+
+
+static void
+draw_cursor(InputItem *sym)
+{
+    int cursor_y;
+    XCharStruct extents;
+    int dir, asc, des;
+
+
+    cursor_y = (sym->curr_line->line_number - 1) * line_height;
+    XTextExtents(gInputFont, sym->curr_line->buffer,
+                 sym->curr_line->buff_pntr,
+                 &dir, &asc, &des, &extents);
+    sym->cursor_x = start_x + extents.width;
+    /* now draw the cursor */
+    if (gInInsertMode) {
+        XFillRectangle(gXDisplay, sym->win, gWindow->fInputGC,
+                       sym->cursor_x,
+                       out_cursor_y + cursor_y,
+                       out_cursor_width,
+                       out_cursor_height);
+
+        /* Now draw the character currently under the cursor */
+
+        XDrawString(gXDisplay, sym->win, gWindow->fCursorGC,
+                    sym->cursor_x, cursor_y + start_y,
+                    &sym->curr_line->buffer[sym->curr_line->buff_pntr],
+                    1);
+    }
+    else
+        XFillRectangle(gXDisplay, sym->win, gWindow->fInputGC,
+                       sym->cursor_x,
+                       in_cursor_y + cursor_y,
+                       in_cursor_width,
+                       in_cursor_height);
+}
+
+static void
+move_cursor_home(InputItem *sym)
+{
+    LineStruct *trace = sym->curr_line;
+
+    /* now move the cursor  to the beginning of the current line */
+    clear_cursor(sym);
+    for (; trace && trace->prev && trace->prev->len > sym->size;)
+        trace = trace->prev;
+    sym->curr_line = trace;
+    trace->buff_pntr = 0;
+    draw_cursor(sym);
+}
+
+static void
+move_cursor_end(InputItem *sym)
+{
+    LineStruct *trace = sym->curr_line;
+
+    /* now move the cursor  to the beginning of the current line */
+    clear_cursor(sym);
+    for (; trace && trace->next && trace->len > sym->size;)
+        trace = trace->next;
+    sym->curr_line = trace;
+    trace->buff_pntr = trace->len;
+    draw_cursor(sym);
+}
+
+static void
+move_cursor_forward(InputItem *sym)
+{
+    if (sym->curr_line->buff_pntr == sym->curr_line->len &&
+        !sym->curr_line->next) {
+        BeepAtTheUser();
+        return;
+    }
+
+
+    if (sym->curr_line->buff_pntr == sym->curr_line->len ||
+        sym->curr_line->buff_pntr == sym->size - 1)
+    {
+
+        /* I have to move down to a new line */
+
+        if (sym->curr_line->next == NULL) {
+            /* now where to move */
+            BeepAtTheUser();
+            return;
+        }
+
+        /* move down line */
+
+        clear_cursor(sym);
+        sym->curr_line = sym->curr_line->next;
+        sym->curr_line->buff_pntr = 0;
+    }
+    else {
+        clear_cursor(sym);
+        sym->curr_line->buff_pntr++;
+    }
+
+    draw_cursor(sym);
+}
+
+static void
+move_cursor_down(InputItem *sym)
+{
+    int bp = sym->curr_line->buff_pntr;
+    /*int size = sym->size;*/
+    LineStruct *trace;
+
+    /* get to the end of the current line */
+
+    for (trace = sym->curr_line; trace->len > sym->size; trace = trace->next)
+        ;
+
+    if (!trace->next)
+        BeepAtTheUser();
+    else {
+        clear_cursor(sym);
+        sym->curr_line = trace->next;
+        if (bp > sym->curr_line->len)
+            sym->curr_line->buff_pntr = sym->curr_line->len;
+        else
+            sym->curr_line->buff_pntr = bp;
+        draw_cursor(sym);
+    }
+}
+
+static void
+move_cursor_up(InputItem *sym)
+{
+    int bp = sym->curr_line->buff_pntr;
+    /*int size = sym->size;*/
+    LineStruct *trace;
+
+    /* get to the end of the current line */
+    for (trace = sym->curr_line;
+         trace->prev && trace->prev->len > sym->size;
+         trace = trace->prev)
+            ;
+
+    if (!trace->prev)
+        BeepAtTheUser();
+    else {
+        clear_cursor(sym);
+        sym->curr_line = trace->prev;
+        if (bp > sym->curr_line->len)
+            sym->curr_line->buff_pntr = sym->curr_line->len;
+        else
+            sym->curr_line->buff_pntr = bp;
+        draw_cursor(sym);
+    }
+}
+
+static void
+clear_cursor(InputItem *sym)
+{
+    XCharStruct extents;
+    int dir, asc, des;
+    int cursor_y;
+
+    XTextExtents(gInputFont, sym->curr_line->buffer,
+                 sym->curr_line->buff_pntr,
+                 &dir, &asc, &des, &extents);
+    cursor_y = (sym->curr_line->line_number - 1) * line_height;
+    sym->cursor_x = start_x + extents.width;
+    XClearArea(gXDisplay, sym->win, sym->cursor_x, cursor_y,
+               in_cursor_width, line_height, False);
+
+    XDrawString(gXDisplay, sym->win, gWindow->fInputGC,
+                start_x, cursor_y + start_y,
+                sym->curr_line->buffer,
+                sym->curr_line->len);
+}
+
+static void
+move_cursor_backward(InputItem *sym)
+{
+    if (sym->curr_line->buff_pntr == 0) {
+        if (sym->curr_line->prev == NULL) {
+            /* now where to move */
+            BeepAtTheUser();
+            return;
+        }
+        else {
+            clear_cursor(sym);
+            /* move up to the previous line */
+            sym->curr_line = sym->curr_line->prev;
+            if (sym->curr_line->len > sym->size)
+                sym->curr_line->buff_pntr = sym->size - 1;
+            else
+                sym->curr_line->buff_pntr = sym->curr_line->len;
+        }
+    }
+    else {                      /* just slide back a char. on the current
+                                 * line */
+        clear_cursor(sym);
+        sym->curr_line->buff_pntr--;
+    }
+    draw_cursor(sym);
+}
+
+static char
+move_rest_back(LineStruct *line, int size)
+{
+    char c = '\000';
+
+    if (line != NULL && line->len != 0)
+        c = line->buffer[0];
+    else
+        return c;
+
+    while (line->next != NULL && line->len > size) {
+        strncpy(line->buffer, &(line->buffer[1]), size - 1);
+        line->buffer[size - 1] = line->next->buffer[0];
+        line->changed = 1;
+        line = line->next;
+    }
+
+    /*
+     * once I get here I should be one the last line, so I can just copy all
+     * the characters back one and then return from whence I came                                                  ***
+     */
+    if (line->len > 0) {
+        line->changed = 1;
+        if (line->len > 1)
+            strncpy(line->buffer, &(line->buffer[1]), line->len - 1);
+        line->buffer[--line->len] = 0;
+        if (line->len == 0) {
+            /* I have to fix the previous line */
+            line->prev->len = size;
+            line->prev->buffer[size] = 0;
+        }
+    }
+    return c;
+}
+
+static void
+delete_rest_of_line(InputItem *sym)
+{
+    LineStruct *curr_line = sym->curr_line;
+    LineStruct *line=NULL;
+    LineStruct *trash;
+    LineStruct *trace;
+    int num_changed = 0, i;
+
+    if (curr_line->len > sym->size) {
+        for (line = curr_line->next, num_changed = 0;
+             line != NULL && line->len > 0 && line->len > sym->size;
+             line = line->next, num_changed++) {
+            line->len = 0;
+            line->buffer[0] = 0;
+            line->changed = 1;
+        }
+        num_changed++;
+    }
+
+    if (num_changed == 0 && curr_line->buff_pntr == curr_line->len) {
+        if (curr_line->len == 0 && curr_line->next) {
+            curr_line->next->prev = curr_line->prev;
+            if (curr_line->prev)
+                curr_line->prev->next = curr_line->next;
+            else
+                sym->lines = curr_line->next;
+            dec_line_numbers(curr_line->next);
+            sym->num_lines--;
+            sym->curr_line = curr_line->next;
+            sym->curr_line->buff_pntr = 0;
+            free(curr_line->buffer);
+            free(curr_line);
+            redraw_win();
+        }
+        else
+            BeepAtTheUser();
+        return;
+    }
+
+    curr_line->len = curr_line->buff_pntr;
+
+    /* curr_line->buffer[curr_line->len] = NULL; */
+
+    for (i = curr_line->len; i <= sym->size + 2; i++)
+        curr_line->buffer[i] = 0;
+
+    curr_line->changed = 1;
+
+    if (num_changed) {
+        /* I should get rid of all these lines */
+        trace = curr_line->next;
+        curr_line->next = line->next;
+        if (line->next)
+            line->next->prev = curr_line;
+        for (; trace && trace != line->next;) {
+            trash = trace;
+            trace = trace->next;
+            free(trash->buffer);
+            free(trash);
+        }
+        decrease_line_numbers(curr_line->next, num_changed);
+        sym->num_lines -= num_changed;
+        redraw_win();
+    }
+    else
+        update_inputsymbol(sym);
+}
+
+static void
+back_over_eoln(InputItem *sym)
+{
+    /*
+     * This routine is very similar to a tough enter except it starts
+     * combining lines with sym->curr_line->pre
+     */
+
+    char buff[1024];
+    LineStruct *trace;
+    LineStruct *last = NULL;
+    char *tr = buff;
+    int bp;
+    int size = sym->size;
+
+    /* copy all the stuff into the buffer */
+    for (trace = sym->curr_line;
+         trace->len > sym->size; trace = trace->next)
+        for (bp = 0; bp < size; bp++)
+            *tr++ = trace->buffer[bp];
+
+    /* copy the last line */
+    for (bp = 0; bp < trace->len; bp++)
+        *tr++ = trace->buffer[bp];
+    trace->len = 0;
+    *tr = 0;
+
+    /* Now that I have the buffer, let's put it back where it belongs. */
+    last = trace;
+    for (trace = sym->curr_line; trace != last; trace = trace->next);
+    trace = sym->curr_line = sym->curr_line->prev;
+    trace->buff_pntr = trace->len;
+    trace->changed = 1;
+    for (bp = trace->len, tr = buff; bp < size && *tr; bp++)
+        trace->buffer[bp] = *tr++;
+
+    if (!*tr) {
+        trace->len = bp;
+    }
+    else {
+        trace->len = size + 1;
+        trace->buffer[size] = '_';
+        trace->buffer[size + 1] = 0;
+        for (trace = trace->next; *tr;) {
+            for (bp = 0; bp < size && *tr; bp++)
+                trace->buffer[bp] = *tr++;
+            if (*tr) {
+                trace->len = size + 1;
+                trace->changed = 1;
+                trace->buffer[size + 1] = 0;
+                trace->buffer[size] = '_';
+                trace = trace->next;
+            }
+            else {
+                trace->len = bp;
+                trace->buffer[bp] = 0;
+            }
+        }
+    }
+    /* Now once I am here, let me see if I can bag a line */
+    if (last->len == 0) {
+        /* rid myself of this line */
+        last->prev->next = last->next;
+        if (last->next)
+            last->next->prev = last->prev;
+        dec_line_numbers(last->next);
+        sym->num_lines--;
+        free(last->buffer);
+        free(last);
+        redraw_win();
+    }
+    else
+        update_inputsymbol(sym);
+
+}
+
+static int
+move_back_one_char(InputItem *sym)
+{
+    char c = '\000', d = '\000';
+    int dl = 0;
+
+    /* This routine moves all the characters back one */
+    LineStruct *line = sym->curr_line;
+
+    if (line->len > sym->size)
+        c = move_rest_back(line->next, sym->size);
+
+    line->changed = 1;
+
+    if (line->buff_pntr == 0) { /* I am at the front of the line */
+        if (line->prev == 0) {
+            BeepAtTheUser();
+            return 0;
+        }
+        else if (line->prev->len <= sym->size) {
+            back_over_eoln(sym);
+            return 1;
+        }
+        else if (line->len > 0) {
+            d = line->buffer[0];
+            if (line->len <= sym->size) {
+                strncpy(line->buffer, &(line->buffer[1]), line->len - 1);
+                if (c == 0) {
+                    line->len--;
+                    line->buffer[line->len] = 0;
+                }
+                else
+                    line->buffer[line->len - 1] = c;
+            }
+            else {
+                strncpy(line->buffer, &(line->buffer[1]), sym->size - 2);
+                if (c == 0) {
+                    line->buffer[sym->size - 1] = 0;
+                    line->len--;
+                }
+                else {
+                    line->buffer[sym->size - 1] = c;
+                }
+            }
+        }
+        else {
+            /* the line is just going to be thrown away */
+            if (line->next)
+                line->next->prev = line->prev;
+            line->prev->next = line->next;
+            dec_line_numbers(line->next);
+            sym->num_lines--;
+            free(line->buffer);
+            free(line);
+            dl = 1;
+        }
+        c = d;
+        sym->curr_line = line = line->prev;
+        line->changed = 1;
+        line->buff_pntr = sym->size;
+    }
+
+
+    if (line->len <= sym->size) {
+        strncpy(&line->buffer[line->buff_pntr - 1],
+                &(line->buffer[line->buff_pntr]),
+                line->len - line->buff_pntr);
+        if (c == 0)
+            line->buffer[--line->len] = 0;
+        else
+            line->buffer[line->len - 1] = c;
+    }
+    else {
+        strncpy(&(line->buffer[line->buff_pntr - 1]),
+                &(line->buffer[line->buff_pntr]),
+                sym->size - line->buff_pntr);
+        if (c == 0) {
+            line->buffer[sym->size - 1] = 0;
+            line->len = sym->size - 1;
+        }
+        else {
+            if (line->next->len == 0) {
+                line->buffer[sym->size] = 0;
+                line->len = sym->size;
+            }
+            line->buffer[sym->size - 1] = c;
+        }
+    }
+    line->buff_pntr--;
+    if (dl)
+        redraw_win();
+    else
+        update_inputsymbol(sym);
+    return 1;
+}
+
+static void
+back_over_char(InputItem *sym)
+{
+    if (move_back_one_char(sym))
+        update_inputsymbol(sym);
+}
+
+static void
+delete_eoln(InputItem *sym)
+{
+    /* much the same as back_over eoln except my perspective has changed */
+    char buff[1024];
+    LineStruct *trace;
+    LineStruct *last = 0;
+    char *tr = buff;
+    int bp;
+    int size = sym->size;
+
+    /* copy all the stuff into the buffer */
+    for (trace = sym->curr_line->next;
+         trace->len > sym->size; trace = trace->next)
+        for (bp = 0; bp < size; bp++)
+            *tr++ = trace->buffer[bp];
+
+    /* copy the last line */
+    for (bp = 0; bp < trace->len; bp++)
+        *tr++ = trace->buffer[bp];
+    trace->len = 0;
+    *tr = 0;
+
+    /* Now that I have the buffer, let's put it back where it belongs. */
+    last = trace;
+    trace = sym->curr_line;
+    trace->changed = 1;
+    for (bp = trace->len, tr = buff; bp < size && *tr; bp++)
+        trace->buffer[bp] = *tr++;
+
+    if (!*tr)
+        trace->len = bp;
+    else {
+        trace->len = size + 1;
+        trace->buffer[size] = '_';
+        trace->buffer[size + 1] = 0;
+        for (trace = trace->next; *tr;) {
+            for (bp = 0; bp < size && *tr; bp++)
+                trace->buffer[bp] = *tr++;
+            if (*tr) {
+                trace->len = size + 1;
+                trace->changed = 1;
+                trace->buffer[size + 1] = 0;
+                trace->buffer[size] = '_';
+                trace = trace->next;
+            }
+            else {
+                trace->len = bp;
+                trace->buffer[bp] = 0;
+            }
+        }
+    }
+    /* Now once I am here, let me see if I can bag a line */
+    if (last->len == 0) {
+        /* rid myself of this line */
+        last->prev->next = last->next;
+        if (last->next)
+            last->next->prev = last->prev;
+        dec_line_numbers(last->next);
+        sym->num_lines--;
+        free(last->buffer);
+        free(last);
+        redraw_win();
+    }
+    else
+        update_inputsymbol(sym);
+
+}
+
+static int
+delete_one_char(InputItem *sym)
+{
+    char c = '\000';
+
+    /* This routine moves all the characters back one */
+    LineStruct *line = sym->curr_line;
+
+    if (line->len > sym->size)
+        c = move_rest_back(line->next, sym->size);
+
+    if (c == 0 && line->len == line->buff_pntr) {
+        if (line->next == 0) {
+            BeepAtTheUser();
+            return 0;
+        }
+        else {
+            delete_eoln(sym);
+            return 1;
+        }
+    }
+
+    /*
+     * let me just try to do the copy and put the stupid character c if it
+     * exists at the end
+     */
+    if (line->len <= sym->size) {
+        strncpy(&line->buffer[line->buff_pntr],
+                &(line->buffer[line->buff_pntr + 1]),
+                line->len - line->buff_pntr);
+        if (c == 0)
+            line->buffer[--line->len] = 0;
+        else
+            line->buffer[line->len - 1] = c;
+    }
+    else {
+        strncpy(&(line->buffer[line->buff_pntr]),
+                &(line->buffer[line->buff_pntr + 1]),
+                sym->size - line->buff_pntr);
+        if (c == 0) {
+            line->buffer[sym->size - 1] = 0;
+            line->len = sym->size - 1;
+        }
+        else {
+            if (line->next->len == 0) {
+                line->buffer[sym->size] = 0;
+                line->len = sym->size;
+            }
+            line->buffer[sym->size - 1] = c;
+        }
+    }
+    line->changed = 1;
+    return 1;
+}
+
+static void
+delete_char(InputItem *sym)
+{
+    if (delete_one_char(sym))
+        update_inputsymbol(sym);
+}
+
+static void
+tough_enter(InputItem *sym)
+{
+    char buff[1024];
+
+    /*
+     * This routine takes all the characters from the current cursor
+     * on, and copies them into a temp buffer, from which they are recopied
+     * back starting at the next line.
+     */
+
+    LineStruct *trace;
+    LineStruct *last = 0;
+    LineStruct *newline;
+    char *tr = buff;
+    int bp = sym->curr_line->buff_pntr;
+    int size = sym->size;
+
+    /* Copy the stuff from the current line */
+    for (; bp < size; bp++)
+        *tr++ = sym->curr_line->buffer[bp];
+
+    /* now get the stuff from the rest of the lines */
+    for (trace = sym->curr_line->next;
+         trace->len > sym->size; trace = trace->next)
+        for (bp = 0; bp < size; bp++)
+            *tr++ = trace->buffer[bp];
+
+    /* copy the last line */
+    for (bp = 0; bp < trace->len; bp++)
+        *tr++ = trace->buffer[bp];
+    *tr = 0;
+
+    /* Now that I have the buffer, let's put it back where it belongs. */
+    last = trace;
+    trace = sym->curr_line;
+    trace->len = trace->buff_pntr;
+    trace->buffer[trace->len] = 0;
+    trace->changed = 1;
+
+    tr = buff;
+    for (trace = trace->next; trace != last; trace = trace->next) {
+        for (bp = 0; bp < size; bp++)
+            trace->buffer[bp] = *tr++;
+        trace->len = size + 1;
+        trace->buffer[size + 1] = 0;
+        trace->buffer[size] = '_';
+        trace->changed = 1;
+    }
+
+    /* Once I am here, I should be able to copy this last line */
+    for (bp = 0; bp < size && *tr; bp++)
+        trace->buffer[bp] = *tr++;
+    trace->changed = 1;
+
+    /* If I still have more to copy, then do so onto a new line */
+    if (*tr) {
+        trace->len = size + 1;
+        trace->buffer[size + 1] = 0;
+        trace->buffer[size] = '_';
+        newline = alloc_inputline(size);
+        sym->num_lines++;
+        newline->line_number = last->line_number + 1;
+        inc_line_numbers(newline->next);
+        for (bp = 0; *tr; bp++)
+            newline->buffer[bp] = *tr++;
+        newline->len = bp;
+        newline->next = last->next;
+        newline->prev = last;
+        last->next = newline;
+        if (newline->next)
+            newline->next->prev = newline;
+    }
+    else {
+        trace->len = bp;
+        trace->buffer[bp] = 0;
+    }
+    /* Last but not least change the curr_line */
+    sym->curr_line = sym->curr_line->next;
+    sym->curr_line->buff_pntr = 0;
+}
+
+static void
+enter_new_line(InputItem *sym)
+{
+    LineStruct *newline;
+    LineStruct *trace;
+    LineStruct *prev;
+    LineStruct *line = sym->curr_line;
+    int bp = line->buff_pntr;
+    int l = line->len;
+    int size = sym->size;
+
+    /*
+     * At this point the user has hit a return. Let me just be naive, and
+     * take everything from the current spot on, and put it on a new line
+     */
+
+    if (bp == 0) {
+        if (line->prev->len > size) {
+            /* just add a return to the end of the last line */
+            prev = line->prev;
+            prev->buffer[size] = 0;
+            prev->len = size;
+            prev->changed = 1;
+        }
+        else {
+            newline = alloc_inputline(size);
+            newline->next = sym->curr_line;
+            newline->prev = sym->curr_line->prev;
+            line->prev = newline;
+            sym->num_lines++;
+            if (newline->prev)
+                newline->prev->next = newline;
+            newline->len = newline->buff_pntr = 0;
+            newline->line_number = line->line_number;
+            if (sym->curr_line == sym->lines)
+                sym->lines = newline;
+            for (trace = newline->next; trace != 0; trace = trace->next)
+                trace->line_number++;
+        }
+    }
+    else if (bp == size &&
+             line->len > size) {
+        /* line->next; */
+        newline = alloc_inputline(size);
+        if (line->next)
+            line->next->prev = newline;
+        newline->prev = sym->curr_line;
+        line->next = newline;
+        newline->len = 0;
+        newline->buff_pntr = 0;
+        sym->num_lines++;
+        sym->curr_line = newline;
+        newline->line_number = newline->prev->line_number + 1;
+        for (trace = newline->next; trace != 0; trace = trace->next)
+            trace->line_number++;
+    }
+    else {
+        if (line->len > size)
+            tough_enter(sym);
+        else {
+            newline = alloc_inputline(size);
+            strncpy(newline->buffer, &sym->curr_line->buffer[bp], l - bp);
+            sym->curr_line->len = bp;
+            sym->curr_line->buffer[bp] = '\0';
+            newline->next = sym->curr_line->next;
+            if (sym->curr_line->next)
+                sym->curr_line->next->prev = newline;
+            newline->prev = sym->curr_line;
+            sym->curr_line->next = newline;
+            newline->len = l - bp;
+            newline->buff_pntr = 0;
+            sym->num_lines++;
+            sym->curr_line = newline;
+            newline->line_number = newline->prev->line_number + 1;
+            for (trace = newline->next; trace != 0; trace = trace->next)
+                trace->line_number++;
+        }
+    }
+    redraw_win();
+}
+
+void
+dialog(XEvent *event, KeySym keysym, char *buffer)
+{
+    InputItem *item;
+
+    item = gWindow->page->current_item;
+    if (item == 0) {
+        if (!((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)))
+            /** if something other than a modifier key was hit **/
+            BeepAtTheUser();
+        return;
+    }
+
+
+    /*
+     * First check if the user had hit an enter key
+     */
+
+    if ((keysym == XK_Return) || (keysym == XK_KP_Enter))
+        enter_new_line(item);
+    /*
+     * Else did the user actual type a character I can understand
+     */
+
+    else if (((keysym >= XK_KP_Space) && (keysym <= XK_KP_9))
+             || ((keysym >= XK_space) && (keysym <= XK_asciitilde)))
+    {
+        /* only handle normal keys */
+
+        if (event->xkey.state & UnsupportedModMask)
+            BeepAtTheUser();
+        else
+            add_buffer_to_sym(buffer, item);
+    }
+
+    else if ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R))
+        ;
+
+    /*
+     * do nothing, a modifier was hit
+     */
+
+    else if ((keysym >= XK_F2) && (keysym <= XK_F35)) {
+
+        /*
+         * A function key was hit
+         */
+
+        if (strlen(buffer) == 0)
+            BeepAtTheUser();
+        else
+            /* If I got characters then add it to the buffer */
+
+            add_buffer_to_sym(buffer, item);
+    }
+    else
+        switch (keysym) {
+          case XK_Escape:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else {
+                move_cursor_home(item);
+                delete_rest_of_line(item);
+            }
+            break;
+          case XK_F1:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else {
+                gWindow->page->helppage = alloc_string(InputAreaHelpPage);
+                helpForHyperDoc();
+            }
+            break;
+          case XK_Up:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_up(item);
+            break;
+          case XK_Down:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_down(item);
+            break;
+          case XK_Delete:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                delete_char(item);
+            break;
+          case XK_BackSpace:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                back_over_char(item);
+            break;
+          case XK_Left:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_backward(item);
+            break;
+          case XK_Right:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_forward(item);
+            break;
+          case XK_Insert:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else {
+                gInInsertMode = ((gInInsertMode) ? (0) : (1));
+                item->curr_line->changed = 1;
+                update_inputsymbol(item);
+            }
+            break;
+          case XK_Home:
+            if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_home(item);
+            break;
+          case XK_End:
+            if (event->xkey.state & ControlMask)
+                /* delete from here to the end of the line */
+
+                delete_rest_of_line(item);
+            else if (event->xkey.state & ModifiersMask)
+                BeepAtTheUser();
+            else
+                move_cursor_end(item);
+            break;
+          default:
+            BeepAtTheUser();
+            break;
+        }
+}
+@
+\section{display.h}
+<<display.h>>=
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_ 1
+
+<<hyper.h>>
+
+extern short int    gDisplayRegion;
+extern int          gRegionOffset;
+
+#endif
+@
+\section{display.c}
+<<display.c>>=
+/******************************************************************************
+ *
+ * display.c:  HyperDoc functions to format and display a page.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+
+/*
+ *  Notes:
+ *      Display is performed in two steps.  First the page is formatted
+ *      assuming that we have an infinitely long window.  In this stage
+ *      we compute and store the coordinates of every text node.  Next
+ *      the page is actually drawn on the screen.  In this process we
+ *      use the value of page->y_off as an offset into the scrolling
+ *      region to compute what is actually to be displayed on the page.
+ */
+#define _DISPLAY_C
+#include "debug.h"
+
+
+<<extent.h>>
+<<mem.h>>
+<<display.h>>
+<<group.h>>
+<<scrollbar.h>>
+<<titlebar.h>>
+<<show-types.h>>
+
+#include "all-hyper-proto.h1"
+
+
+extern ItemStack *gTopOfItemStack;
+short int gDisplayRegion = 0;
+int gRegionOffset = 0;
+
+
+/* Display a HyperDoc page in the top-level window */
+
+void
+show_page(HyperDocPage *page)
+{
+    XWindowChanges wc;
+    int doShowScrollBars = 1;
+
+    init_top_group();
+
+    /* Clear the areas so we can rewrite the page */
+
+    XClearWindow(gXDisplay, gWindow->fMainWindow);
+    XClearWindow(gXDisplay, gWindow->fScrollWindow);
+
+    /* Free the active button list */
+
+    free_button_list(page->s_button_list);
+    page->s_button_list = NULL;
+    free_button_list(page->button_list);
+    page->button_list = NULL;
+
+    /* The compute the text extents  */
+    compute_title_extent(page);
+    compute_header_extent(page);
+    compute_footer_extent(page);
+    compute_scrolling_extent(page);
+
+    /*
+     * Now that we have all the extents computed, reconfigure and map the
+     * scroll window
+     */
+
+    if (page->scrolling) {
+        int width, height;
+        calculateScrollBarMeasures();
+        wc.x = 0;
+        wc.y = page->top_scroll_margin + scroll_top_margin;
+
+        wc.height = gWindow->scrollheight;
+        if (gWindow->page->scrolling->height <= gWindow->scrollheight) {
+            gWindow->page->scroll_off = 0;
+            wc.width = gWindow->width;
+        }
+        else
+            wc.width = gWindow->width - gScrollbarWidth;
+
+        getScrollBarMinimumSize(&width, &height);
+        if (height > wc.height) {
+            wc.height = gWindow->scrollheight = 1;
+            doShowScrollBars = 0;
+        }
+        else
+            gWindow->scrollwidth = wc.width;
+
+        if (doShowScrollBars) {
+            XConfigureWindow(gXDisplay, gWindow->fScrollWindow, CWX | CWY | CWHeight | CWWidth, &wc);
+            XMapWindow(gXDisplay, gWindow->fScrollWindow);
+        }
+        else {
+            XUnmapWindow(gXDisplay, gWindow->fScrollWindow);
+            hideScrollBars(gWindow);
+        }
+    }
+    /* clear the group stack */
+
+    while (pop_group_stack() >= 0)
+        ;
+
+    /* Now start displaying all the text */
+
+    gWindow->fDisplayedWindow = gWindow->fMainWindow;
+    gRegionOffset = 0;
+    y_off = 0;
+    gDisplayRegion = Header;
+    show_text(page->header->next, Endheader);
+
+    if (doShowScrollBars && page->scrolling) {
+        /* Show the footer  */
+        if (page->footer->next) {
+            gDisplayRegion = Footer;
+            gRegionOffset = gWindow->page->bot_scroll_margin +
+                (!((gWindow->page->page_flags & NOLINES)) ? ((int) line_height / 2) : (0));
+            show_text(page->footer->next, Endfooter);
+            /* Show the scrolling region */
+            if (page->scrolling->next)
+                gDisplayRegion = Scrolling;
+            gRegionOffset = 0;
+            gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+            y_off = gWindow->page->scroll_off;
+            show_text(page->scrolling->next, Endscrolling);
+            showScrollBars(gWindow);
+        }
+        drawScrollLines();
+    }
+    if (gTopOfItemStack != NULL) {
+        fprintf(stderr, "warning: unbalanced \\beginitems .. \\enditems\n");
+        gTopOfItemStack = NULL;
+    }
+    showTitleBar();
+    XFlush(gXDisplay);
+}
+
+void
+expose_page(HyperDocPage *page)
+{
+    int width, height, doShowScrollBars = 1;
+    init_top_group();
+
+    /*
+     * Now start displaying all the text
+     */
+
+    y_off = 0;
+    gWindow->fDisplayedWindow = gWindow->fMainWindow;
+    gRegionOffset = 0;
+    gDisplayRegion = Header;
+    show_text(page->header->next, Endheader);
+    getScrollBarMinimumSize(&width, &height);
+
+    /*
+     * Now see If I have anything left to display
+     */
+    if (page->scrolling) {
+        if (page->footer->next) {
+            gDisplayRegion = Footer;
+            gRegionOffset = gWindow->page->bot_scroll_margin +
+                (!((gWindow->page->page_flags & NOLINES)) ? ((int) line_height / 2) : (0));
+            show_text(page->footer->next, Endfooter);
+        }
+
+        if (height > gWindow->scrollheight) {
+            gWindow->scrollheight = 1;
+            doShowScrollBars = 0;
+            XUnmapWindow(gXDisplay, gWindow->fScrollWindow);
+            hideScrollBars(gWindow);
+        }
+
+        if (page->scrolling->next) {
+            gRegionOffset = page->top_scroll_margin;
+            gDisplayRegion = Scrolling;
+            gRegionOffset = 0;
+            gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+            y_off = gWindow->page->scroll_off;
+            show_text(page->scrolling->next, Endscrolling);
+            if (doShowScrollBars)
+                showScrollBars(gWindow);
+        }
+        if (doShowScrollBars)
+            drawScrollLines();
+    }
+    showTitleBar();
+    XFlush(gXDisplay);
+}
+
+void
+scroll_page(HyperDocPage *page)
+{
+    init_top_group();
+    /* free the active button list */
+    free_button_list(page->s_button_list);
+    page->s_button_list = NULL;
+    /** Clear the scrolling area */
+    XUnmapSubwindows(gXDisplay, gWindow->fScrollWindow);
+    gDisplayRegion = Scrolling;
+    gRegionOffset = 0;
+    gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+    y_off = gWindow->page->scroll_off;
+    show_text(page->scrolling->next, Endscrolling);
+    moveScroller(gWindow);
+    XFlush(gXDisplay);
+}
+
+void
+paste_page(TextNode *node)
+{
+    int width, height;
+    int old_off = gWindow->page->scroll_off;
+
+    /* free the active button list */
+    free_button_list(gWindow->page->s_button_list);
+    gWindow->page->s_button_list = NULL;
+    free_button_list(gWindow->page->button_list);
+    gWindow->page->button_list = NULL;
+    XUnmapSubwindows(gXDisplay, gWindow->fScrollWindow);
+
+    init_top_group();
+
+    /* recompute the extent of the scrolling region */
+
+    compute_scrolling_extent(gWindow->page);
+
+    calculateScrollBarMeasures();
+    getScrollBarMinimumSize(&width, &height);
+
+    /* get ready to show the scrolling area */
+    gRegionOffset = 0;
+    y_off = gWindow->page->scroll_off;
+    gDisplayRegion = Scrolling;
+    gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+    if (gWindow->page->scroll_off == old_off) {
+        XClearArea(gXDisplay, gWindow->fScrollWindow, 0,
+                   node->y - line_height + gRegionOffset + y_off,
+                   gWindow->width,
+                   gWindow->scrollheight - node->y + line_height - y_off,
+                   False);
+        XFlush(gXDisplay);
+    }
+    else
+        XClearWindow(gXDisplay, gWindow->fScrollWindow);
+
+    show_text(gWindow->page->scrolling->next, Endscrolling);
+    XFlush(gXDisplay);
+    hideScrollBars(gWindow);
+
+    if (height > gWindow->scrollheight) {
+        gWindow->scrollheight = 1;
+        XUnmapWindow(gXDisplay, gWindow->fScrollWindow);
+    }
+    else {
+        showScrollBars(gWindow);
+        drawScrollLines();
+        /* moveScroller(); */
+    }
+    XFlush(gXDisplay);
+}
+@
+\section{event.h}
+<<event.h>>=
+#ifndef _EVENT_H_
+#define _EVENT_H_ 1
+
+<<hyper.h>>
+
+extern Window gActiveWindow;
+extern int    gNeedIconName;
+
+#endif
+@
+\section{event.c}
+<<event.c>>=
+#define _EVENT_C
+#include "debug.h"
+
+
+<<hyper.h>>
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <sys/signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef SGIplatform
+#include <bstring.h>
+#endif
+
+<<event.h>>
+<<keyin.h>>
+<<mem.h>>
+<<display.h>>
+<<parse.h>>
+<<parse-paste.h>>
+<<initx.h>>
+<<scrollbar.h>>
+<<group.h>>
+
+#include "all-hyper-proto.h1"
+#include "sockio-c.h1"
+
+jmp_buf env;
+Window gActiveWindow;
+int motion = 0;
+int gNeedIconName = 0;
+unsigned long bigmask= 0xffffffff;
+static HyperLink *gSavedInputAreaLink = NULL;
+
+
+
+/*
+ * This is the main X loop. It keeps grabbing events. Since the only way the
+ * window can die is through an event, it never actually end. One of the
+ * subroutines it calls is responsible for killing everything
+ */
+
+void
+mainEventLoop(void)
+{
+    XEvent event;
+    int  Xcon;
+    fd_set rd, dum1, dum2;
+    motion = 0;
+    gActiveWindow = -1;
+    set_error_handlers();
+    Xcon = ConnectionNumber(gXDisplay);
+
+    while (1) {
+        /*fprintf(stderr,"event:mainEventLoop: loop top\n");*/
+        while (gSessionHashTable.num_entries == 0)
+            pause();
+
+        /* XFlush(gXDisplay);      */
+
+        if (!motion)
+            init_cursor_states();
+        motion = 0;
+
+        if (!spad_socket == 0) {
+            FD_ZERO(&rd);
+            FD_ZERO(&dum1);
+            FD_ZERO(&dum2);
+            FD_CLR(0, &dum1);
+            FD_CLR(0, &dum2);
+            FD_CLR(0, &rd);
+            FD_SET(spad_socket->socket, &rd);
+            FD_SET(Xcon, &rd);
+            if (!session_server == 0) {
+                FD_SET(session_server->socket, &rd);
+            }
+            if (XEventsQueued(gXDisplay, QueuedAlready)) {
+                XNextEvent(gXDisplay, &event);
+                handle_event(&event);
+            }
+            else {
+              select(FD_SETSIZE,(void *)&rd,(void *)&dum1,(void *)&dum2,NULL);
+              if (FD_ISSET(Xcon, &rd) || 
+                  XEventsQueued(gXDisplay, QueuedAfterFlush)) {
+                    XNextEvent(gXDisplay, &event);
+                    handle_event(&event);
+                }
+                else if FD_ISSET
+                    (spad_socket->socket, &rd)
+                    /*
+                     * Axiom Socket do what handle_event does The 100 is
+                     * $SpadStuff in hypertex.boot
+                     */
+                {
+                    if (100 == get_int(spad_socket)) {
+                        set_window(gParentWindow->fMainWindow);
+                        make_busy_cursors();
+                        get_new_window();
+                    }
+                }
+                /*
+                 * Session Socket Telling us about the death of a spadbuf
+                 * (plus maybe more later) service_session_socket in
+                 * spadint.c
+                 */
+                else 
+                 if (session_server && FD_ISSET(session_server->socket, &rd)) {
+                    service_session_socket();
+                 }
+            }
+        }
+        else {
+            XNextEvent(gXDisplay, &event);
+            handle_event(&event);
+        }
+    }
+}
+
+static void
+handle_event(XEvent * event)
+{
+    XWindowAttributes wa;
+/*    fprintf(stderr,"event:handle_event entered\n");*/
+    set_window(event->xany.window);
+    if (event->type == MotionNotify) {
+/*        fprintf(stderr,"event:handle_event type=MotionNotify\n");*/
+        handle_motion_event((XMotionEvent *)event);
+        motion = 1;
+        return;
+    }
+    make_busy_cursors();
+    switch (event->type) {
+      case DestroyNotify:
+/*        fprintf(stderr,"event:handle_event type=DestroyNotify\n");*/
+        break;
+      case Expose:
+/*        fprintf(stderr,"event:handle_event type=Expose\n");*/
+        XGetWindowAttributes(gXDisplay, gWindow->fMainWindow, &wa);
+        if ((gWindow->width == 0 && gWindow->height == 0) ||
+            (wa.width != gWindow->width || wa.height != gWindow->height)) {
+            gWindow->width = wa.width;
+            gWindow->height = wa.height;
+            display_page(gWindow->page);
+            gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+        }
+        else                    /** just redraw the thing **/
+            expose_page(gWindow->page);
+        XFlush(gXDisplay);
+        clear_exposures(gWindow->fMainWindow);
+        clear_exposures(gWindow->fScrollWindow);
+        break;
+      case ButtonPress:
+/*        fprintf(stderr,"event:handle_event type=ButtonPress\n");*/
+        handle_button(event->xbutton.button, (XButtonEvent *)event);
+        XFlush(gXDisplay);
+        if (gWindow) {
+            while (XCheckTypedWindowEvent(gXDisplay, gWindow->fMainWindow,
+                                          Expose, event));
+            while (XCheckTypedWindowEvent(gXDisplay, gWindow->fScrollWindow,
+                                          Expose, event));
+        }
+        break;
+      case KeyPress:
+/*        fprintf(stderr,"event:handle_event type=KeyPress\n");*/
+        handle_key(event);
+        if (gWindow) {
+            while (XCheckTypedWindowEvent(gXDisplay, gWindow->fMainWindow,
+                                          Expose, event));
+            while (XCheckTypedWindowEvent(gXDisplay, gWindow->fScrollWindow,
+                                          Expose, event));
+        }
+        break;
+      case MapNotify:
+/*        fprintf(stderr,"event:handle_event type=MapNotify\n");*/
+        create_window();
+        break;
+
+      case SelectionNotify:
+/*        fprintf(stderr,"event:handle_event type=SelectionNotify\n");*/
+        /* this is in response to a previous request in an input area */
+        if ( gSavedInputAreaLink ) {
+            XSelectionEvent *pSelEvent;
+            Atom dataProperty;
+            pSelEvent = (XSelectionEvent *) event;
+            dataProperty = XInternAtom(gXDisplay, "PASTE_SELECTION", False);
+            /* change the input focus */
+
+        /*  change_input_focus(gSavedInputAreaLink); */
+
+            /* try to get the selection as a window property */
+
+            if ( pSelEvent->requestor == gWindow->fMainWindow &&
+                 pSelEvent->selection == XA_PRIMARY &&
+            /*   pSelEvent->time      == CurrentTime && */
+                 pSelEvent->target    == XA_STRING &&
+                 pSelEvent->property == dataProperty )
+            {
+                Atom actual_type;
+                int  actual_format;
+                unsigned long nitems, leftover;
+                char *pSelection = NULL;
+
+                if (Success == XGetWindowProperty(gXDisplay,
+                    gWindow->fMainWindow,
+                    pSelEvent->property, 0L, 100000000L, True,
+                    AnyPropertyType, &actual_type, &actual_format,
+                    &nitems, &leftover, (unsigned char **) &pSelection) )
+                {
+                    char *pBuffer;
+                    InputItem *item = gSavedInputAreaLink->reference.string;
+
+                    for (pBuffer = pSelection; *pBuffer; ++pBuffer)
+                        add_buffer_to_sym(pBuffer, item);
+
+                    XFree(pSelection);
+                }
+            }
+
+            /* clear the link info */
+
+            gSavedInputAreaLink = NULL;
+        }
+        break;
+
+      default:
+/*        fprintf(stderr,"event:handle_event type=default\n");*/
+        break;
+    }
+
+}
+
+static void
+create_window(void)
+{
+    XWindowAttributes wa;
+
+    XGetWindowAttributes(gXDisplay, gWindow->fMainWindow, &wa);
+
+    gWindow->width = wa.width;
+    gWindow->height = wa.height;
+    display_page(gWindow->page);
+    gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+
+    /* then select for the events I normally would like to catch */
+    XSelectInput(gXDisplay, gWindow->fMainWindow, ButtonPress | KeyPressMask |
+                 PointerMotionMask |
+                 ExposureMask /* | EnterWindowMask | LeaveWindowMask */ );
+    XSelectInput(gXDisplay, gWindow->fScrollWindow, ExposureMask);
+
+}
+
+/*
+ * This routine is called when the quitbutton is hit. For the moment I am
+ * just going to leave it all behind
+ */
+
+void
+quitHyperDoc(void)
+{
+    HyperDocPage *page;
+
+    if (gSessionHashTable.num_entries == 1 || gParentWindow == gWindow) {
+        if (!strcmp(gWindow->page->name, "ProtectedQuitPage")){
+	exitHyperDoc();
+		}
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ProtectedQuitPage");
+        if (page == NULL) {
+            fprintf(stderr, "Unknown page name %s\n", "ProtectedQuitPage");
+            exitHyperDoc();
+            return;
+        }
+        if (gWindow->fDownLinkStackIndex == MaxDownlinkDepth)
+            fprintf(stderr, "exceeded maximum link nesting level\n");
+        else
+            gWindow->fDownLinkStack[gWindow->fDownLinkStackIndex++] = gWindow->page;
+        gWindow->page = page;
+        display_page(gWindow->page);
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+    }
+    else
+        exitHyperDoc();
+}
+
+
+/*
+ * find_page takes as an argument the HyperDoc for a page name and returns
+ * the associated page
+ */
+
+static HyperDocPage *
+find_page(TextNode * node)
+{
+    char *page_name;
+    HyperDocPage *page;
+
+    /* try and find the page name */
+    page_name = print_to_string(node);
+    page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page_name);
+
+    if (page == NULL) {
+        /* try to find the unknown page */
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "UnknownPage");
+        if (page == NULL) {
+            /* Yikes, Even that could not be found */
+            fprintf(stderr, "Unknown page name %s\n", page_name);
+        }
+        else {
+            if (page->type == UnloadedPageType)
+                page->type = UlUnknownPage;
+            else
+                page->type = UnknownPage;
+        }
+    }
+    return page;
+}
+
+/*
+ * These are macros for taking care of the downlink stack, and the memolink
+ * stack.
+ */
+
+#define NotSpecial(t) ((t == Quitbutton || t == Returnbutton || \
+                        t == Upbutton || t == UnknownPage || \
+                        t == UlUnknownPage || t == ErrorPage) ?(0):(1))
+
+/* pushes a page onto the down link stack */
+
+static void
+downlink(void)
+{
+    if (gWindow->fDownLinkStackIndex == MaxDownlinkDepth)
+        fprintf(stderr, "exceeded maximum link nesting level\n");
+    else
+        gWindow->fDownLinkStack[gWindow->fDownLinkStackIndex++] = gWindow->page;
+}
+
+static void
+memolink(void)
+{
+    if (gWindow->fMemoStackIndex == MaxMemoDepth)
+        fprintf(stderr, "exceeded maximum link nesting level\n");
+    else {
+        gWindow->fMemoStack[gWindow->fMemoStackIndex] = gWindow->page;
+        gWindow->fDownLinkStackTop[gWindow->fMemoStackIndex++] = gWindow->fDownLinkStackIndex;
+    }
+}
+
+static void
+killAxiomPage(HyperDocPage * page)
+{
+    char command[512];
+
+    sprintf(command, "(|htpDestroyPage| '%s)", page->name);
+    send_lisp_command(command);
+}
+
+static void
+kill_page(HyperDocPage * page)
+{
+    page->scroll_off = 0;
+    if (page->type == SpadGen) {
+        hash_delete(gWindow->fPageHashTable, page->name);
+        killAxiomPage(page);
+        free_page(page);
+    }
+}
+
+/* pops the memo stack */
+
+static HyperDocPage *
+returnlink(void)
+{
+    int i;
+
+    if (gWindow->fMemoStackIndex == 0) {
+        BeepAtTheUser();
+        return NULL;
+    }
+    else {
+        kill_page(gWindow->page);
+        for (i = gWindow->fDownLinkStackIndex - 1;
+             i >= gWindow->fDownLinkStackTop[gWindow->fMemoStackIndex - 1];
+             i--)
+        {
+            kill_page(gWindow->fDownLinkStack[i]);
+        }
+        gWindow->fDownLinkStackIndex =
+            gWindow->fDownLinkStackTop[--gWindow->fMemoStackIndex];
+        return (gWindow->fMemoStack[gWindow->fMemoStackIndex]);
+    }
+}
+
+/* pops a page if it can from the downlink stack */
+
+static HyperDocPage *
+uplink(void)
+{
+    if (gWindow->fDownLinkStackIndex == 0)
+        return returnlink();
+    else {
+        kill_page(gWindow->page);
+        return (gWindow->fDownLinkStack[--gWindow->fDownLinkStackIndex]);
+    }
+}
+
+static void
+windowlink_handler(TextNode * node)
+{
+    char *page_name;
+
+    /* first try and find the page */
+    page_name = print_to_string(node);
+
+    if (init_top_window(page_name) == -1) {
+        return;
+    }
+/*    gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;*/
+}
+
+void
+make_window_link(char *name)
+{
+    if (init_top_window(name) != -1)
+{}/*        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable; */
+}
+
+
+static void
+lispwindowlink_handler(HyperLink * link)
+{
+
+    /*
+     * Since we are popping up a new window, then we had better change all
+     * the cursors right away. We won't get another chance at it.
+     */
+
+    if (init_top_window(NULL) != -1) {
+        HyperDocPage *page = NULL;
+        int frame = gWindow->fAxiomFrame;
+
+        page = issue_server_command(link);
+        gWindow->fAxiomFrame = frame;
+        gWindow->page = page;
+/*        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;*/
+    }
+}
+
+static HyperDocPage *
+paste_button(PasteNode * paste)
+{
+    HyperDocPage *page = NULL;
+    int pastewhere=paste->where;
+
+
+    if ( paste->end_node ==NULL || paste->begin_node==NULL || paste->arg_node==NULL ){
+	BeepAtTheUser();
+	return NULL;
+	}
+
+    page=parse_patch(paste);
+/* paste has changed after this call so use pastewhere*/
+
+    if (pastewhere && page ) {
+        if (0 == strcmp(page->name, "ErrorPage"))
+            page = NULL;
+    }
+    else
+        BeepAtTheUser();
+
+    return page;
+}
+
+void
+helpForHyperDoc(void)
+{
+    HyperDocPage *page = NULL;
+
+    /* do not do anything if we are already at the "no more help" page */
+
+    if (0 == strcmp(gWindow->page->name, NoMoreHelpPage))
+        return;
+
+    /* if no help page recorded, use the standard "no more help" page */
+
+    if (!gWindow->page->helppage)
+        gWindow->page->helppage = alloc_string(NoMoreHelpPage);
+
+    /* if we are on the main help page, use "no more help" page */
+
+    if (0 == strcmp(gWindow->page->name, TopLevelHelpPage))
+        gWindow->page->helppage = alloc_string(NoMoreHelpPage);
+
+    page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, gWindow->page->helppage);
+
+    if (page)
+        make_window_link(gWindow->page->helppage);
+    else
+        BeepAtTheUser();
+}
+
+static HyperLink *
+findButtonInList(HDWindow * window, int x, int y)
+{
+    ButtonList *bl;
+
+    if (!window || window->page->type == UnloadedPageType)
+        return NULL;
+    for (bl = window->page->s_button_list; bl != NULL; bl = bl->next)
+        if (x >= bl->x0 && x <= bl->x1 && y >= bl->y0 && y <= bl->y1)
+            return bl->link;
+    for (bl = window->page->button_list; bl != NULL; bl = bl->next)
+        if (x >= bl->x0 && x <= bl->x1 && y >= bl->y0 && y <= bl->y1)
+            return bl->link;
+    return NULL;
+}
+
+static HyperLink *
+get_hyper_link(XButtonEvent * event)
+{
+    HyperLink *l1, *l2;
+
+    l1 = (HyperLink *) hash_find(gWindow->fWindowHashTable, (char *)&(event->window));
+    if (l1)
+        return l1;
+    l2 = findButtonInList(gWindow, event->x, event->y);
+    return l2;
+}
+
+/*
+ * Handle a button pressed event. window is the subwindow in which the event
+ * occured, and button is the button which was pressed
+ */
+
+static void
+handle_button(int button, XButtonEvent * event)
+{
+    HyperLink *link;
+    HyperDocPage *page = NULL;
+    char *page_name;
+
+    /* find page name from sub-window handle */
+
+    link = get_hyper_link(event);
+
+    if (link == NULL) {         /* user clicked on an inactive area */
+/*      BeepAtTheUser();    */  /* I always thought this was annoying. RSS */
+        return;
+    }
+
+    switch (link->type) {
+      case Pastebutton:
+        page = paste_button(link->reference.paste);
+        break;
+      case Link:
+        page_name = print_to_string(link->reference.node);
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page_name);
+        break;
+      case Helpbutton:
+        helpForHyperDoc();
+        page = NULL;
+        break;
+      case Scrollbar:
+        scrollScroller(event);
+        break;
+      case Scrollupbutton:
+        scrollUp();
+        break;
+      case Scrolldownbutton:
+        scrollDown();
+        break;
+
+      case Inputstring:
+        /* We must be changing input focus or getting a selection */
+
+        change_input_focus(link);
+        if ( button == Button2 ) {
+            XConvertSelection(gXDisplay, XA_PRIMARY, XA_STRING,
+                XInternAtom(gXDisplay, "PASTE_SELECTION", False),
+                gWindow->fMainWindow, CurrentTime);
+            gSavedInputAreaLink = link;
+        }
+        break;
+
+      case SimpleBox:
+        page = NULL;
+        toggle_input_box(link);
+        break;
+      case Radiobox:
+        page = NULL;
+        toggle_radio_box(link);
+        break;
+      case Quitbutton:
+        quitHyperDoc();
+        break;
+      case Returnbutton:        /* pop memo information */
+        page = returnlink();
+        break;
+      case Upbutton:            /* pop downlink information */
+        page = uplink();
+        break;
+      case Downlink:
+        page = find_page(link->reference.node);
+        if (page  && NotSpecial(page->type))
+            downlink();
+        break;
+      case Memolink:
+        page = find_page(link->reference.node);
+        if (page && NotSpecial(page->type))
+            memolink();
+        break;
+      case Windowlink:
+        page = find_page(link->reference.node);
+        if (page && NotSpecial(page->type)) {
+            windowlink_handler(link->reference.node);
+            gNeedIconName = 1;
+            page = NULL;
+        }
+        break;
+      case Lispwindowlink:
+        lispwindowlink_handler(link);
+        gNeedIconName = 1;
+        page = NULL;
+        break;
+      case LispMemoLink:
+      case Spadmemolink:
+        page = issue_server_command(link);
+        if (page && NotSpecial(page->type))
+            memolink();
+        break;
+      case LispDownLink:
+      case Spaddownlink:
+        page = issue_server_command(link);
+        if (page && NotSpecial(page->type))
+            downlink();
+        break;
+      case Spadlink:
+      case Lisplink:
+        page = issue_server_command(link);
+        break;
+      case Lispcommand:
+      case Qspadcall:
+      case Spadcall:
+        page = issue_server_command(link);
+        break;
+      case Lispcommandquit:
+      case Spadcallquit:
+      case Qspadcallquit:
+        page = issue_server_command(link);
+        exitHyperDoc();
+        break;
+      case Spadcommand:
+      case Spadgraph:
+      case Spadsrc:
+        issue_spadcommand(gWindow->page, link->reference.node,
+                          button == Button1, link->type);
+        break;
+      case Unixlink:
+        page = issue_unixlink(link->reference.node);
+        if (page && NotSpecial(page->type)) {
+            downlink();
+        }
+        break;
+      case Unixcommand:
+        issue_unixcommand(link->reference.node);
+        break;
+      default:
+        break;
+    }
+
+    if (page) {
+        switch (page->type) {   /* check for special button types */
+          case Quitbutton:
+            exitHyperDoc();
+            return;
+          case Returnbutton:
+            gWindow->page = returnlink();
+            break;
+          case Upbutton:
+            gWindow->page = uplink();
+            break;
+          case ErrorPage:
+          case UnknownPage:
+          case UlUnknownPage:
+            if (page->type == UlUnknownPage)
+                page->type = UnloadedPageType;
+            downlink();
+            gWindow->page = page;
+            break;
+          default:              /* a normal link */
+            gWindow->page = page;
+            break;
+        }
+        if (link->type != Pastebutton)
+            display_page(gWindow->page);
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;      /* reset the window hash */
+    }
+}
+
+
+void
+exitHyperDoc(void)
+{
+    XEvent event;
+
+
+    if (gSessionHashTable.num_entries == 1 || gParentWindow == gWindow) {
+        free_hd_window(gWindow);
+        exit(0);
+    }
+    hash_delete(&gSessionHashTable, (char *)&gWindow->fMainWindow);
+
+    /*
+     * Now we quickly want to flush all the events associated with this
+     * window from existence
+     */
+
+    XFlush(gXDisplay);
+    while (XCheckWindowEvent(gXDisplay, gWindow->fMainWindow, bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->fScrollWindow,bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->fDisplayedWindow, bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->fScrollUpWindow, bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->fScrollDownWindow, bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->scrollbar, bigmask, &event)) {
+    }
+    while (XCheckWindowEvent(gXDisplay, gWindow->scroller, bigmask, &event)) {
+    }
+
+    XDestroyWindow(gXDisplay, gWindow->fMainWindow);
+    free_hd_window(gWindow);
+    gWindow = NULL;
+    gActiveWindow = -1;
+    XFlush(gXDisplay);
+}
+
+static int
+set_window(Window window)
+{
+    Window root, parent, *children, grandparent,myarg;
+    HDWindow *htw;
+    unsigned int nchildren;
+    int st;
+
+    myarg=window;
+    nchildren = 0;
+    htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&myarg);
+    if (htw != NULL) {
+        gWindow = htw;
+        return 1;
+    }
+    st = XQueryTree(gXDisplay, myarg, &root, &parent, &children, &nchildren);
+    if (st==0) goto ERROR;
+    if (nchildren > 0)
+        XFree(children);
+    htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&parent);
+    if (htw != NULL) {
+        gWindow = htw;
+        return 1;
+
+    }
+    else {
+        /* check for a grandparent */
+        st = XQueryTree(gXDisplay, parent, &root, &grandparent, &children, &nchildren);
+	if (st==0) goto ERROR;
+        if (nchildren > 0)
+            XFree(children);
+        htw = (HDWindow *) hash_find(&gSessionHashTable, (char *)&grandparent);
+        if (htw != NULL) {
+            gWindow = htw;
+            return 1;
+        }
+    }
+
+    /*
+     * fprintf(stderr, "window(%d) and it's parent(%d) aren't in
+     * gSessionHashTable\n", window, parent);
+     
+     we never found that window. this happens if (not iff) we exit from 
+     an unfocused non-main window under certain wm's and click-to-type. the program returns here with
+     the window handle that was just destroyed. So let's set the global gWindow
+     to the main window.
+     */
+
+ERROR:
+    gWindow=gParentWindow;
+    return 0;
+}
+
+/*
+ * This procedure whips thru the stack and clears all expose events for the
+ * given routine
+ */
+static void
+clear_exposures(Window w)
+{
+    XEvent report;
+
+    XFlush(gXDisplay);
+    while (XCheckTypedWindowEvent(gXDisplay, w, Expose, &report));
+}
+void
+get_new_window(void)
+{
+
+    int val;
+    char buf[128];
+    int frame;
+    Window wid;
+    HDWindow *htw;
+    HyperDocPage *hpage;
+
+
+    /*
+     * If I am going to try and start a new window, then I should make sure I
+     * have a coonection to listen on
+     *
+     * BUT This code is entered when a socket selects
+     *
+     * if (spad_socket == NULL) { spad_socket =
+     * connect_to_local_server(SpadServer, MenuServer, 10); if (spad_socket
+     * == NULL) { fprintf(stderr, "Get_new_window: Couldn't Connect to
+     * SpadServer\n"); return -1; } }
+     *
+     */
+
+
+    frame = get_int(spad_socket);
+    val = get_int(spad_socket);
+    switch (val) {
+      case StartPage:
+        init_top_window(NULL);
+        val = get_int(spad_socket);
+        init_scanner();
+        input_type = FromSpadSocket;
+        input_string = "";
+        gWindow->page = parse_page_from_socket();
+        gWindow->fAxiomFrame = frame;
+        XFlush(gXDisplay);
+        break;
+      case LinkToPage:
+        get_string_buf(spad_socket, buf, 128);
+        if (init_top_window(buf) == -1) {
+            fprintf(stderr, "get_new_window: Did not find page %s\n", buf);
+            /* return -1; */
+        }
+        gWindow->fAxiomFrame = frame;
+        break;
+      case PopUpPage:
+        val = get_int(spad_socket);
+        init_form_window(NULL, val);
+        send_int(spad_socket, gWindow->fMainWindow);
+        init_scanner();
+        input_type = FromSpadSocket;
+        input_string = "";
+        gWindow->page = parse_page_from_socket();
+        compute_form_page(gWindow->page);
+
+        XMapWindow(gXDisplay, gWindow->fMainWindow);
+
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+        gWindow->fAxiomFrame = frame;
+        XFlush(gXDisplay);
+        break;
+      case PopUpNamedPage:
+        val = get_int(spad_socket);
+        get_string_buf(spad_socket, buf, 128);
+
+        if (init_form_window(buf, val) == -1) {
+            send_int(spad_socket, -1);
+            break;
+        }
+        load_page(gWindow->page);
+        compute_form_page(gWindow->page);
+
+        XMapWindow(gXDisplay, gWindow->fMainWindow);
+
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+        gWindow->fAxiomFrame = frame;
+        XFlush(gXDisplay);
+        send_int(spad_socket, gWindow->fMainWindow);
+        /* fprintf(stderr, "Window Id was %d\n", gWindow->fMainWindow); */
+        break;
+      case ReplaceNamedPage:
+        wid = (Window) get_int(spad_socket);
+        get_string_buf(spad_socket, buf, 128);
+
+        htw = (HDWindow *) hash_find(&gSessionHashTable,(char *)&wid);
+        if (htw == NULL) break;
+        hpage = (HyperDocPage *) hash_find(gWindow->fPageHashTable, buf);
+        if (hpage == NULL) break;
+        gWindow = htw;
+        gWindow->page = hpage;
+        display_page(gWindow->page);
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+        clear_exposures(gWindow->fMainWindow);
+        clear_exposures(gWindow->fScrollWindow);
+        XFlush(gXDisplay);
+        break;
+      case ReplacePage:
+        wid = (Window) get_int(spad_socket);
+        set_window(wid);
+        init_scanner();
+        input_type = FromSpadSocket;
+        input_string = "";
+        gWindow->page = parse_page_from_socket();
+        display_page(gWindow->page);
+        gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+        clear_exposures(gWindow->fMainWindow);
+        clear_exposures(gWindow->fScrollWindow);
+        XFlush(gXDisplay);
+        break;
+      case KillPage:
+        /* Here the user wishes to kill the page */
+        wid = (Window) get_int(spad_socket);
+        htw = (HDWindow *) hash_find(&gSessionHashTable,(char *)&wid);
+        if (htw !=NULL) {
+            gWindow = htw;
+            exitHyperDoc();
+            break;
+          }
+        break;
+    }
+  }
+static void
+set_cursor(HDWindow *window,Cursor state)
+{
+    if (state == gBusyCursor)
+        XDefineCursor(gXDisplay, window->fMainWindow, gBusyCursor);
+    else if (state == gActiveCursor)
+        XDefineCursor(gXDisplay, window->fMainWindow, gActiveCursor);
+    else
+        XDefineCursor(gXDisplay, window->fMainWindow, gNormalCursor);
+    XFlush(gXDisplay);
+}
+
+static void
+change_cursor(Cursor state, HDWindow *window)
+{
+    if (window->fDisplayedCursor == state)
+        return;
+    window->fDisplayedCursor = state;
+    set_cursor(window, state);
+}
+
+static void
+handle_motion_event(XMotionEvent *event)
+{
+    if (!gWindow)
+        return;
+    if (findButtonInList(gWindow, event->x, event->y) != NULL)
+        change_cursor(gActiveCursor, gWindow);
+    else
+        change_cursor(gNormalCursor, gWindow);
+}
+
+static void
+init_cursor_state(HDWindow *window)
+{
+    if (window) {
+        int x, y, rx, ry, but;
+        Window r, c;
+
+        XQueryPointer(gXDisplay, window->fMainWindow,
+                             &r, &c, &rx, &ry, &x, &y,(unsigned int *) &but);
+        if (findButtonInList(window, x, y) != NULL)
+            change_cursor(gActiveCursor, window);
+        else
+            change_cursor(gNormalCursor, window);
+    }
+}
+
+static void
+init_cursor_states(void)
+{
+    hash_map(&gSessionHashTable,(MappableFunction) init_cursor_state);
+}
+
+
+static void
+make_busy_cursor(HDWindow *window)
+{
+    change_cursor(gBusyCursor, window);
+}
+
+static void
+make_busy_cursors(void)
+{
+    hash_map(&gSessionHashTable, (MappableFunction)make_busy_cursor);
+}
+
+static int
+HyperDocErrorHandler(Display *display, XErrorEvent *xe)
+{
+    if (xe->request_code != 15) {
+        char buf[1024];
+
+        XGetErrorText(display, xe->error_code, buf, sizeof(buf));
+
+        fprintf(stderr, "error code = %d\n", xe->error_code);
+        fprintf(stderr, "major op code = %d\n", xe->request_code);
+        fprintf(stderr, "minor op code = %d\n", xe->minor_code);
+        fprintf(stderr, "XID = %ld\n", xe->resourceid);
+        fprintf(stderr, "%s\n", buf);
+
+        if (xe->request_code != 15)
+            exit(-1);
+      }
+    return(0);
+}
+
+
+
+static void
+set_error_handlers(void)
+{
+    XSetErrorHandler(HyperDocErrorHandler);
+}
+@
+\section{ex2ht.c}
+<<ex2ht.c>>=
+/* ex2ht creates a cover page for structured HyperDoc example pages */
+
+
+#define _EX2HT_C
+#include "debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+
+
+#if defined(SUN4OS5platform)||defined(SGIplatform)
+/* can't find a prototype anywhere */
+extern int utimes(const char *, const struct timeval [2]);
+#endif
+
+
+#define MaxLineLength 512
+#define MaxFiles      100
+
+char *files[MaxFiles];
+int numFiles = 0;
+struct timeval latest_date[2] ={{0,0},{0,0}};
+
+#include "ex2ht.h1"
+
+
+int
+main(int argc, char **argv)
+{
+    int i;
+
+    if (argc == 1) {
+        fprintf(stderr, "usage: %s exfile.ht ...\n", argv[0]);
+        return (-1);
+    }
+    openCoverPage();
+    for (i = 1; i < argc; i++)
+        exToHt(argv[i]);
+    closeCoverPage();
+    for (i = 0; i < numFiles; i++)
+        addFile(files[i]);
+    closeCoverFile();
+    return 0;
+}
+
+char *
+allocString(char *s)
+{
+    char *t = (char *) malloc(strlen(s) + 1);
+
+    strcpy(t, s);
+    return t;
+}
+
+char *
+strPrefix(char *prefix, char *s)
+{
+    while (*prefix != '\0' && *prefix == *s) {
+        prefix++;
+        s++;
+    }
+    if (*prefix == '\0')
+        return s;
+    return NULL;
+}
+
+char *
+getExTitle(FILE *inFile, char *line)
+{
+    char *title;
+
+    while (fgets(line, MaxLineLength, inFile) != NULL)
+        if ((title = strPrefix("% Title: ", line))) {
+            title[strlen(title) - 1] = '\0';
+            return title;
+        }
+    fprintf(stderr, "No Title title line in the file!\n");
+    return NULL;
+}
+
+void 
+exToHt(char *filename)
+{
+    char line[MaxLineLength], *line2;
+    char *title, *pagename;
+    FILE *inFile = fopen(filename, "r");
+    FILE *outFile;
+    int len, i;
+    struct timeval  tvp;
+    struct stat buf;
+
+    if (inFile == NULL) {
+        fprintf(stderr, "couldn't open %s for reading.\n", filename);
+        return;
+    }
+    strcpy(line, "Menu");
+    strcat(line, filename);
+    len = strlen(line);
+    for (i = 0; i < len; i++)
+        if (line[i] == '.') {
+            line[i] = '\0';
+            break;
+        }
+    outFile = fopen(line, "w");
+    if (outFile == NULL) {
+        fprintf(stderr, "couldn't open %s for writing.\n", line);
+        return;
+    }
+    pagename = allocString(line);
+    title = getExTitle(inFile, line);
+    if (title == NULL) {
+        return;
+    }
+    files[numFiles++] = pagename;
+    emitCoverLink(pagename, title);
+    emitHeader(outFile, pagename, title);
+    while (fgets(line, MaxLineLength, inFile) != NULL) {
+        if ((line2 = strPrefix("\\begin{page}{", line)))
+            emitMenuEntry(line2, outFile);
+        else if ((line2 = strPrefix("\\spadcommand{", line)))
+            emitSpadCommand(line2, "\\spadcommand{", outFile);
+        else if ((line2 = strPrefix("\\spadpaste{", line)))
+            emitSpadCommand(line2, "\\spadpaste{", outFile);
+        else if ((line2 = strPrefix("\\example{", line)))
+            emitSpadCommand(line2, "\\example{", outFile);
+        else if ((line2 = strPrefix("\\graphpaste{", line)))
+            emitSpadCommand(line2, "\\graphpaste{", outFile);
+    }
+    emitFooter(outFile);
+    fclose(inFile);
+    fclose(outFile);
+    stat(filename,&buf);
+    tvp.tv_sec =buf.st_mtime;
+    tvp.tv_usec =0;
+    if timercmp(&tvp,&latest_date[1],>){ 
+	latest_date[1].tv_sec=buf.st_mtime;
+	}
+}
+
+void 
+emitHeader(FILE *outFile, char *pageName, char *pageTitle)
+{
+    fprintf(outFile, "\\begin{page}{%s}{%s}\n", pageName, pageTitle);
+    fprintf(outFile, "\\beginscroll\\beginmenu\n");
+}
+
+void 
+emitFooter(FILE *outFile)
+{
+    fprintf(outFile, "\\endmenu\\endscroll\\end{page}\n");
+}
+
+/* s is pageName}{title} */
+void
+emitMenuEntry(char *line, FILE *outFile)
+{
+    char pageName[MaxLineLength], title[MaxLineLength];
+    char *p = pageName, *t = title;
+
+    while (*line != '}')
+        *p++ = *line++;
+    *p = '\0';
+    line++;
+    while (*line != '}')
+        *t++ = *line++;
+    *t = '\0';
+    fprintf(outFile, "\\menudownlink%s}{%s}\n", title, pageName);
+}
+
+void
+emitSpadCommand(char *line, char *prefix, FILE *outFile)
+{
+    int braceCount = 1;
+    char command[MaxLineLength], *t = command;
+
+    while (1) {
+        if (*line == '}')
+            braceCount--;
+        if (braceCount == 0)
+            break;
+        if (*line == '{')
+            braceCount++;
+        *t++ = *line++;
+    }
+    *t = '\0';
+    fprintf(outFile, "%s%s}\n", prefix, command);
+}
+
+/* cover page functions */
+
+FILE *coverFile;
+
+void
+openCoverPage(void)
+{
+    coverFile = fopen("coverex.ht", "w");
+    if (coverFile == NULL) {
+        fprintf(stderr, "couldn't open coverex.ht for writing\n");
+        exit(-1);
+    }
+    fprintf(coverFile, "%% DO NOT EDIT! Created by ex2ht.\n\n");
+    fprintf(coverFile, "\\begin{page}{ExampleCoverPage}{Examples Of AXIOM Commands}\n");
+    fprintf(coverFile, "\\beginscroll\\table{\n");
+}
+
+void
+closeCoverPage(void)
+{
+    fprintf(coverFile, "}\\endscroll\\end{page}\n\n");
+}
+
+void
+closeCoverFile(void)
+{
+    fclose(coverFile);
+#ifdef HP9platform
+    times("coverex.ht",latest_date);
+#else
+    utimes("coverex.ht",latest_date);
+#endif
+}
+
+void
+emitCoverLink(char *name, char *title)
+{
+    fprintf(coverFile, "{\\downlink{%s}{%s}}\n", title, name);
+}
+
+void
+addFile(char *filename)
+{
+    FILE *file = fopen(filename, "r");
+    int c;
+
+    if (file == NULL) {
+        fprintf(stderr, "Couln't open %s for reading\n", filename);
+        exit(-1);
+    }
+    while ((c = getc(file)) != EOF)
+        putc(c, coverFile);
+    putc('\n', coverFile);
+    fclose(file);
+    unlink(filename);
+}
+@
+\section{extent1.c}
+<<extent1.c>>=
+/******************************************************************************
+ *
+ * extent1.h:  HyperDoc extent computation routines
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _EXTENT1_C
+#include "debug.h"
+
+
+<<extent.h>>
+<<hyper.h>>
+<<group.h>>
+<<titlebar.h>>
+<<scrollbar.h>>
+
+#include "all-hyper-proto.h1"
+
+
+/*
+ * Now we declare all the values which are shared among the extent routines
+ * and the showing routines
+ */
+
+int noop_count;
+
+TextNode *link_node = NULL;
+TextNode *paste_node = NULL;
+TextNode *spad_node = NULL;
+TextNode *if_node = NULL;
+
+
+short int gExtentRegion;
+
+
+short int gInDesc;
+short int gInLine;               /* true iff there have been words printed  */
+short int gInItem;               /* true iff we are in a \item */
+short int gInAxiomCommand;            /* true iff we are in a \spadcommand */
+short int gInTable;
+
+/* Variables for the formatting state */
+
+int right_margin_space;
+int right_margin;
+int indent;
+int item_indent;
+int text_x;
+int text_y;
+int y_off;
+int scroll_bot;
+int need_scroll_up_button;
+int need_scroll_down_button;
+int item_space;
+int present_line_height;
+int past_line_height;
+int line_height;                /* space between lines              */
+int normal_text_height;         /* space between lines              */
+int space_width;                /* the maximum width of a character */
+int word_off_height;            /* the diff between text height and */
+
+TextNode *gLineNode;
+
+/*
+ * Computes the extent of the input string or box
+ */
+
+static void
+compute_input_extent(TextNode * node)
+{
+    InputItem *item;
+    int t_width;
+    int num_lines;
+
+    /* search the symbol table for the proper entry */
+
+    item = node->link->reference.string;
+    num_lines = item->num_lines;
+
+    /*
+     * Once we have gotten this far, we should just be able to calculate the
+     * width using the normal font
+     */
+
+    t_width = (item->size + 1) * gInputFont->max_bounds.width + 10;
+
+    if (gInLine)
+        text_x += inter_word_space;
+
+    if (text_x + t_width > right_margin) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+
+    /* now figure out the height of the current window */
+
+    node->height = line_height * (num_lines);
+    node->y = text_y - line_height + node->height - 1;
+    if (node->height > present_line_height)
+        present_line_height = plh(node->height);
+    node->width = t_width;
+    gInLine = 1;
+    text_x += t_width;
+}
+
+static void
+compute_punctuation_extent(TextNode * node)
+{
+    int twidth;
+    int nextwidth;
+    int incwidth;
+
+    node->height = normal_text_height;
+    node->width = strlen(node->data.text);
+    incwidth = twidth = XTextWidth(gTopOfGroupStack->cur_font, node->data.text,
+                                   node->width);
+
+    /* always check to see if there was some space in front of us */
+
+    if (gInLine && (node->space & FRONTSPACE))
+        twidth += inter_word_space;
+
+    /*
+     * now calcualte the width of the next one if it needs to be considered
+     */
+
+    if (!(node->space & BACKSPACE))
+        nextwidth = total_width(node->next, Endtokens);
+    else
+        nextwidth = 0;
+
+    if ((!(node->space & BACKSPACE)) &&
+        (text_x + twidth + nextwidth > right_margin) && gInLine) {
+        start_newline(present_line_height, node);
+        if (gInAxiomCommand) {
+            text_x = indent + spadcom_indent;
+        }
+        else
+            text_x = indent;
+    }
+
+    if (node->space & FRONTSPACE)
+        text_x += inter_word_space;
+
+    node->x = text_x;
+
+    /*
+     * Now try to see if we should leave space after myself. Always leave
+     * space when there is space
+     */
+
+    if (node->space & BACKSPACE) {
+        switch (node->data.text[0]) {
+          case '.':
+          case '?':
+          case '!':
+            text_x += term_punct_space;
+            break;
+        }
+    }
+
+    text_x += incwidth;
+    node->y = text_y - word_off_height;
+    gInLine = 1;
+}
+
+static void
+compute_word_extent(TextNode * node)
+{
+    int twidth;
+    int nextwidth;
+    int incwidth;
+
+    node->height = normal_text_height;
+    node->width = strlen(node->data.text);
+    incwidth = twidth = XTextWidth(gTopOfGroupStack->cur_font, node->data.text,
+                                   node->width);
+
+    /*
+     * Now if we should drop some space in front of me, then add it to twidth
+     */
+
+    if (gInLine && node->space)
+        twidth += inter_word_space;
+
+    /*
+     * Now what we should do is find all the things after us that have no
+     * space in front and add there width on.
+     */
+
+    nextwidth = total_width(node->next, Endtokens);
+
+
+    /*
+     * Should we start a new line?
+     */
+
+    if (text_x + twidth + nextwidth > right_margin && gInLine) {
+        start_newline(present_line_height, node);
+        if (gInAxiomCommand) {
+            text_x = indent + spadcom_indent;
+        }
+        else
+            text_x = indent;
+    }
+
+    /*
+     * Now see if we am on the beginning of a line, and if not add some space
+     * if we need to
+     */
+
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    node->x = text_x;
+    node->y = text_y - word_off_height;
+    text_x += incwidth;
+    gInLine = 1;
+}
+
+static void
+compute_verbatim_extent(TextNode *node)
+{
+    node->height = normal_text_height;
+    node->width = strlen(node->data.text);
+
+    node->x = text_x;
+    node->y = text_y - word_off_height;
+    gInLine = 1;
+    return;
+}
+
+static void
+compute_spadsrctxt_extent(TextNode *node)
+{
+    node->height = normal_text_height;
+    node->width = strlen(node->data.text);
+
+    if (gInLine) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y - word_off_height;
+    gInLine = 1;
+    return;
+}
+
+static void
+compute_dash_extent(TextNode *node)
+{
+    int num_dashes;
+    int twidth;
+    int nextwidth;
+
+    node->height = normal_text_height;
+
+    num_dashes = strlen(node->data.text);
+
+    if (num_dashes > 1)
+        twidth = node->width = num_dashes * dash_width;
+    else
+        twidth = node->width = XTextWidth(gTopOfGroupStack->cur_font,
+                                          node->data.text, 1);
+
+    if (gInLine && node->space)
+        twidth += inter_word_space;
+
+    /*
+     * Now what we should do is find all the things after us that have no
+     * space in front and add there width on.
+     */
+
+    nextwidth = total_width(node->next, Endtokens);
+
+    /*
+     * Should we start a new line?
+     */
+
+    if (text_x + twidth + nextwidth > right_margin) {
+        start_newline(present_line_height, node);
+        if (gInAxiomCommand) {
+            text_x = indent + spadcom_indent;
+        }
+        else
+            text_x = indent;
+    }
+
+    /*
+     * Now see if we am on the beginning of a line, and if not add some space
+     * if we need to
+     */
+
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    node->x = text_x;
+    if (num_dashes > 1)
+        node->y = text_y - dash_y;
+    else
+        node->y = text_y - word_off_height;
+    text_x += node->width;
+    gInLine = 1;
+    return;
+}
+
+void
+compute_text_extent(TextNode *node)
+{
+    for (; node != NULL; node = node->next) {
+        switch (node->type) {
+          case Endpastebutton:
+            endpastebutton_extent(node);
+            break;
+          case Paste:
+            compute_paste_extent(node);
+            break;
+          case Endpaste:
+            if (gInLine) {
+                start_newline(present_line_height, node);
+                text_x = indent;
+            }
+            break;
+          case Pastebutton:
+            compute_pastebutton_extent(node);
+            break;
+          case Ifcond:
+            compute_ifcond_extent(node);
+            break;
+          case Fi:
+            break;
+          case Endif:
+            if (if_node == NULL) {
+                return;
+            }
+            else
+                endif_extent(node);
+            break;
+          case Endcenter:
+            start_newline(present_line_height, node->next);
+            pop_group_stack();
+            text_x = indent;
+            break;
+          case Pound:
+          case Macro:
+            /* check to see if we had space in front of me, if so add it */
+
+            if (node->space && gInLine)
+                text_x += inter_word_space;
+            break;
+          case Punctuation:
+            compute_punctuation_extent(node);
+            break;
+          case Endmath:
+            break;
+          case Endverbatim:
+            if (gInLine) {
+                start_newline(present_line_height, node);
+                text_x = indent;
+            }
+            break;
+          case Spadsrctxt:
+            compute_spadsrctxt_extent(node);
+            break;
+          case Math:
+            compute_word_extent(node);
+            break;
+          case Verbatim:
+            compute_verbatim_extent(node);
+            break;
+          case WindowId:
+          case Word:
+          case Lsquarebrace:
+          case Rsquarebrace:
+            compute_word_extent(node);
+            break;
+          case Dash:
+            compute_dash_extent(node);
+            break;
+          case HSpace:
+            node->height = line_height;
+            node->x = text_x;
+            node->y = text_y;
+            if (gInLine) {
+                text_x +=
+                    (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            }
+            break;
+          case VSpace:
+            node->height = line_height;
+            node->x = text_x;
+            node->y = text_y + present_line_height;;
+            text_y +=
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1) +
+                present_line_height;
+            past_line_height = (node->data.node != NULL ?
+                                atoi(node->data.node->data.text) : 1)
+                + present_line_height;
+
+            present_line_height = line_height;
+            break;
+          case Space:
+            node->height = line_height;
+            node->x = text_x;
+            node->y = text_y;
+            text_x += (gTopOfGroupStack->cur_font->max_bounds.width) *
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            break;
+          case Tab:
+            node->height = line_height;
+            text_x = indent + (gTopOfGroupStack->cur_font->max_bounds.width) *
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            gInLine = 0;
+            break;
+          case Par:
+            node->height = line_height;
+            if (gInItem)
+                text_x = indent;
+            else
+                text_x = indent + paragraph_space;
+            if (gInLine) {
+                start_newline(present_line_height, node);
+            }
+            break;
+          case Newline:
+            if (gInLine) {
+                start_newline(present_line_height, node);
+                text_x = indent;
+            }
+            break;
+          case Horizontalline:
+            if (gInLine) {
+                start_newline(present_line_height, node);
+                text_x = indent;
+            }
+            node->height = line_height;
+            gInLine = 0;
+            node->y = text_y - line_height / 2;
+            node->x = text_x;
+            start_newline(present_line_height, node);
+            break;
+          case Center:
+            compute_center_extent(node);
+            break;
+          case Box:
+            compute_box_extent(node);
+            break;
+          case Mbox:
+            compute_mbox_extent(node);
+            break;
+          case Beginitems:
+          case Begintitems:
+            compute_begin_items_extent(node);
+            break;
+          case Enditems:
+          case Endtitems:
+            pop_item_stack();
+            if (gInLine) {
+                start_newline(present_line_height, node);
+            }
+            text_x = indent;
+            break;
+          case Titem:
+            if (gInLine) {
+                start_newline(present_line_height, node);
+            }
+            text_x = indent - item_space;
+            break;
+          case Item:
+            compute_item_extent(node);
+            break;
+          case Mitem:
+            compute_mitem_extent(node);
+            break;
+          case Upbutton:
+          case Returnbutton:
+          case Memolink:
+          case Downlink:
+          case Link:
+          case Windowlink:
+            compute_button_extent(node);
+            break;
+          case Unixlink:
+          case Lisplink:
+          case Lispwindowlink:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case LispDownLink:
+          case LispMemoLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Unixcommand:
+            compute_button_extent(node);
+            break;
+          case Endbutton:
+            endbutton_extent(node);
+            break;
+          case Endlink:
+            if (link_node == NULL)
+                return;
+            else
+                endbutton_extent(node);
+            break;
+          case Spadsrc:
+            compute_spadsrc_extent(node);
+            break;
+          case Spadcommand:
+          case Spadgraph:
+            compute_spadcommand_extent(node);
+            break;
+          case Endspadsrc:
+            end_spadsrc_extent(node);
+            break;
+          case Endspadcommand:
+            end_spadcommand_extent(node);
+            break;
+          case Indent:
+            indent = left_margin +
+                atoi(node->data.node->data.text) *
+                (gTopOfGroupStack->cur_font->max_bounds.width);
+            if (!gInLine)
+                text_x = indent;
+            break;
+          case Indentrel:
+            indent += atoi(node->data.node->data.text) *
+                (gTopOfGroupStack->cur_font->max_bounds.width);
+            if (!gInLine)
+                text_x = indent;
+            break;
+          case Group:
+            push_group_stack();
+            node->y = text_y;
+            if (gInLine && node->space)
+                text_x += inter_word_space;
+            break;
+          case Endgroup:
+            pop_group_stack();
+            break;
+          case Tableitem:
+            push_group_stack();
+            node->y = text_y;
+            if (gInLine && node->space)
+                text_x += inter_word_space;
+            break;
+          case Endtableitem:
+            pop_group_stack();
+            return;
+          case Controlbitmap:
+          case Inputbitmap:
+            if (node->width == -1)
+                insert_bitmap_file(node);
+            compute_image_extent(node);
+            break;
+          case Inputpixmap:
+            if (node->width == -1)
+                insert_pixmap_file(node);
+            compute_image_extent(node);
+            break;
+          case Table:
+            compute_table_extent(&node);
+            break;
+          case BoldFace:
+            compute_bf_extent(node);
+            break;
+          case Emphasize:
+            compute_em_extent(node);
+            break;
+          case It:
+            compute_it_extent(node);
+            break;
+          case Rm:
+          case Sl:
+          case Tt:
+            compute_rm_extent(node);
+            break;
+          case Inputstring:
+            compute_input_extent(node);
+            break;
+          case SimpleBox:
+          case Radiobox:
+            compute_ir_extent(node);
+            break;
+          case Endbox:
+            text_x += box_width;
+            break;
+          case Endmacro:
+          case Endparameter:
+            break;
+          case Description:
+            bf_top_group();
+            break;
+          case Enddescription:
+            pop_group_stack();
+            if (gInDesc)
+                return;
+            break;
+          case Endscrolling:
+
+            /*
+             * What we should do here is if we am in the middle of a line, we
+             * should end it here an now.
+             */
+
+            if (gInLine)
+                start_newline(present_line_height, node);
+            break;
+          case Noop:
+            noop_count++;
+            break;
+          case Endinputbox:
+          case Endheader:
+          case Endtitle:
+          case Endfooter:
+          case Rbrace:
+          case Free:
+          case Bound:
+          case Beep:
+          case 0:
+            break;
+          default:
+            fprintf(stderr, "Compute_text_extent: Unknown node type %d\n",
+                    node->type);
+            break;
+        }
+    }
+}
+
+static void
+compute_begin_items_extent(TextNode * node)
+{
+    int store_x, store_y, lh;
+
+    /*
+     * This routine pushes the current item_stack, and then tries to set the
+     * item_indent, and the indent level. It checks for an optional argument
+     * to begin{items} and if found uses its width.
+     */
+    if (gInLine) {
+        start_newline(present_line_height, node);
+    }
+    store_x = text_x, store_y = text_y, lh = present_line_height;
+    text_x = indent;
+    push_item_stack();
+    gInItem++;
+    item_indent = indent;
+    if (node->data.node != NULL) {
+        /* we have a desc */
+        gInDesc = 1;
+        compute_text_extent(node->data.node);
+        gInDesc = 0;
+        item_space = text_width(node->data.node, Enddescription);
+        text_x = store_x;
+        text_y = store_y;
+        present_line_height = lh;
+        indent = item_indent + item_space;
+    }
+    else
+        indent = item_indent + 30;
+    gInLine = 0;
+}
+
+static void
+compute_item_extent(TextNode * node)
+{
+    if (gInLine)
+        start_newline(present_line_height, node);
+    text_x = item_indent;
+}
+
+static void
+compute_mitem_extent(TextNode *node)
+{
+    if (gInLine) {
+        start_newline(present_line_height, node);
+    }
+    text_x = item_indent;
+}
+
+static void
+endif_extent(TextNode *node)
+{
+    /*
+     * This node has the responsibilty for updating text_x and text_y so that
+     * they are the maxaimum width of teh else and then statements
+     */
+
+    text_x = if_node->x;
+    text_y = if_node->y;
+    if_node = NULL;
+}
+
+static void
+compute_ifcond_extent(TextNode *node)
+{
+    TextNode *condnode = node->data.ifnode->cond;
+    TextNode *tln = gLineNode;
+    int store_x = text_x, store_y = text_y, lh = present_line_height;
+    int then_x, then_y;
+
+    /*
+     * This routine checks the value of the condition and swaps in the else
+     * or the then depending
+     */
+
+    /*
+     * we have to compute the maximum width and height of the rest of the
+     * text and stuff
+     */
+    push_group_stack();
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    compute_text_extent(node->data.ifnode->thennode);
+    then_x = text_x;
+    then_y = text_y;
+    text_x = store_x;
+    text_y = store_y;
+    present_line_height = lh;
+    gLineNode = tln;
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    compute_text_extent(node->data.ifnode->elsenode);
+    /* Now choose the best one that is biggest and put it into ifnode */
+    if (then_y > text_y) {
+        node->y = then_y;
+        node->x = then_x;
+    }
+    else if (text_y > then_y) {
+        node->y = text_y;
+        node->x = text_x;
+    }
+    else if (text_x > then_x) {
+        node->y = text_y;
+        node->x = text_x;
+    }
+    else {
+        node->y = then_y;
+        node->x = then_x;
+    }
+    /* restore everything */
+    text_x = store_x;
+    text_y = store_y;
+    present_line_height = lh;
+    gLineNode = tln;
+    node->width = 0;
+
+    if_node = node;
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    if (check_condition(condnode)) {
+        node->next = node->data.ifnode->thennode;
+    }
+    else {
+        node->next = node->data.ifnode->elsenode;
+    }
+    pop_group_stack();
+}
+
+static void
+compute_center_extent(TextNode * node)
+{
+    if (gInLine)
+        start_newline(present_line_height, node);
+
+    center_top_group();
+
+    if (gLineNode)
+        text_x = indent;
+    else {
+        fprintf(stderr, "(HyperDoc) Internal error: unexpected state in compute_center_extent.\n");
+        exit(-1);
+    }
+}
+
+static void
+compute_bf_extent(TextNode *node)
+{
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    node->x = text_x;
+    node->y = text_y;
+    bf_top_group();
+}
+
+static void
+compute_em_extent(TextNode *node)
+{
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    node->x = text_x;
+    node->y = text_y;
+    if (gTopOfGroupStack->cur_font == gEmFont)
+        rm_top_group();
+    else
+        em_top_group();
+}
+
+static void
+compute_it_extent(TextNode *node)
+{
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    node->x = text_x;
+    node->y = text_y;
+}
+
+static void
+compute_rm_extent(TextNode *node)
+{
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    node->x = text_x;
+    node->y = text_y;
+    rm_top_group();
+}
+
+static void
+compute_button_extent(TextNode *node)
+{
+    int twidth;
+    /*int store_x = text_x;*/
+    /*int store_y = text_y;*/
+    /*int lh = present_line_height;*/
+
+    push_active_group();
+
+    /* First see if we should leave a little space in front of myself * */
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    twidth = text_width(node->next, Endbutton);
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    if (text_x + twidth > right_margin && gInLine) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+    link_node = node;
+}
+
+static void
+endbutton_extent(TextNode *node)
+{
+    int temp;
+    int height;
+    int twidth;
+    int y;
+    int maxx;
+
+    maxx = max_x(link_node, Endbutton);
+    link_node->width = twidth = text_width(link_node->next, Endbutton);
+    height = link_node->y;
+    temp = text_height(link_node->next, Endbutton);
+    link_node->height = temp - link_node->y + line_height;
+
+    if (gInLine)
+        y = text_y;
+    else
+        y = text_y - past_line_height;
+    if (y > height) {
+        link_node->y = temp;    /* height + link_node->height -
+                                 * normal_text_height; */
+        link_node->width = maxx - indent;
+        if (gInLine) {
+            start_newline(present_line_height, node);
+            text_x = indent;
+        }
+    }
+    else {
+        link_node->width = twidth;
+        link_node->y = text_y + link_node->height - line_height;
+    }
+    pop_group_stack();
+    link_node = NULL;
+}
+
+static void
+compute_pastebutton_extent(TextNode *node)
+{
+    int twidth;
+
+    push_active_group();
+
+    /*
+    First see if we should leave a little space in front of myself * */
+
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    twidth = text_width(node->next, Endpastebutton);
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    if (text_x + twidth > right_margin && gInLine) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+    paste_node = node;
+    return;
+}
+
+static void
+endpastebutton_extent(TextNode *node)
+{
+    int temp;
+    int height;
+    int twidth;
+
+    paste_node->width = twidth = text_width(paste_node->next, Endpastebutton);
+    height = paste_node->y;
+    temp = text_height(paste_node->next, Endpastebutton);
+    paste_node->height = temp - paste_node->y + line_height;
+    if (text_y > height) {
+        paste_node->y = temp;
+        paste_node->width = right_margin - indent;
+        if (gInLine) {
+            start_newline(present_line_height, node);
+            text_x = indent;
+        }
+    }
+    else {
+        paste_node->width = twidth;
+        paste_node->y = text_y + paste_node->height - line_height;
+    }
+    pop_group_stack();
+    paste_node = NULL;
+    gInLine = 1;
+}
+
+static void
+compute_paste_extent(TextNode *node)
+{
+    if (gInLine) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+    node->height = line_height;
+}
+
+/* Compute the text extent of a spadcommand node */
+
+static void
+compute_spadcommand_extent(TextNode *node)
+{
+    /*
+     * From now on if there is an example which will take over a line, then
+     * it will start and end with a newline
+     */
+
+    /*int height;*/
+    int t_width;
+    /*int store_x = text_x;*/
+    /*int store_y = text_y;*/
+    /*int lh = present_line_height;*/
+
+    gInAxiomCommand = 1;
+
+    push_spad_group();
+
+    /* Check to see if we should space in front of myself         */
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+    t_width = text_width(node->next, Endspadcommand);
+    if (gInLine && ((text_x + t_width) > right_margin)) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+    spad_node = node;
+
+}
+
+static void
+compute_spadsrc_extent(TextNode *node)
+{
+    /*
+     * From now on if there is an example which will take over a line, then
+     * it will start and end with a newline
+     */
+
+    /*int store_x = text_x;*/
+    /*int store_y = text_y;*/
+    /*int lh = present_line_height;*/
+
+    gInAxiomCommand = 1;
+
+    push_spad_group();
+
+    if (gInLine) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+    spad_node = node;
+
+}
+
+static void
+end_spadcommand_extent(TextNode *node)
+{
+    int temp;
+    int height;
+    int twidth;
+    int maxx;
+    /*int y = (gInLine) ? (text_y) : (text_y - past_line_height);*/
+
+    maxx = max_x(spad_node, Endspadcommand);
+    twidth = spad_node->width = text_width(spad_node->next, Endspadcommand);
+    height = spad_node->y;
+    temp = text_height(spad_node->next, Endspadcommand);
+
+    spad_node->height = temp - height + line_height;
+    if (text_y > height && gInLine) {
+        spad_node->y = temp;
+        spad_node->width = maxx - indent;
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    else {
+        spad_node->width = twidth;
+        spad_node->y = text_y - line_height + spad_node->height;
+    }
+    pop_group_stack();
+    gInAxiomCommand = 0;
+    spad_node = NULL;
+}
+
+static void
+end_spadsrc_extent(TextNode *node)
+{
+    int temp;
+    int height;
+    int twidth;
+    int maxx;
+    int y = (gInLine) ? (text_y) : (text_y - past_line_height);
+
+    maxx = max_x(spad_node, Endspadsrc);
+
+    twidth = spad_node->width = text_width(spad_node->next, Endspadsrc);
+    height = spad_node->y;
+    temp = text_height(spad_node->next, Endspadsrc);
+    spad_node->height = temp - height + line_height;
+    if (y > height && gInLine) {
+        spad_node->y = temp;
+        spad_node->width = maxx - indent;
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    else {
+        spad_node->width = twidth;
+        spad_node->y = text_y - line_height + spad_node->height;
+    }
+    pop_group_stack();
+    gInAxiomCommand = 0;
+    spad_node = NULL;
+}
+
+static void
+compute_mbox_extent(TextNode *node)
+{
+
+    node->width = text_width(node->next, Endmbox);
+    if (node->space)
+        text_x += inter_word_space;
+    if (text_x + node->width > right_margin) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    node->y = text_y;
+}
+
+static void
+compute_box_extent(TextNode *node)
+{
+    int t_width;
+
+    /*
+     * First thing we do is see if we need to skip some space in front of the
+     * word
+     */
+
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    /* Calculate the actual width of the box */
+
+    t_width = text_width(node->next, Endbox) + 2 * box_width;
+
+    if (text_x + t_width > right_margin) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    text_x = text_x + box_width;
+    node->y = text_y - 2;
+    node->width = t_width;
+    node->height = line_height - 2;
+    gInLine = 1;
+}
+
+static void
+compute_ir_extent(TextNode *node)
+{
+    int t_width;
+
+    /*
+     * First thing we do is see if we need to skip some space in front of the
+     * word
+     */
+
+    if (gInLine && node->space)
+        text_x += inter_word_space;
+
+    /* Calculate the actual width of the box */
+
+    t_width = node->width;
+
+    if (text_x + t_width > right_margin) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    if (node->height > line_height) {
+        node->height = present_line_height = plh(node->height + inter_line_space);
+        node->y = text_y + node->height - normal_text_height;
+    }
+    else {
+        node->y = text_y - line_height + node->height;
+    }
+    gInLine = 1;
+    text_x += node->width;
+}
+
+/* read a bitmap file into memory */
+
+static void
+compute_image_extent(TextNode *node)
+{
+    if (text_x + node->width > right_margin) {
+        start_newline(present_line_height, node);
+        text_x = indent;
+    }
+    node->x = text_x;
+    if (node->height > line_height) {
+        present_line_height = plh(node->height + inter_line_space);
+        node->y = text_y + node->height - line_height;
+    }
+    else {
+        node->y = text_y - line_height + node->height;
+    }
+    text_x += node->width;
+    gInLine = 1;
+}
+
+/*
+ * compute the coordinates of the entries in a table
+ */
+
+static void
+compute_table_extent(TextNode **node)
+{
+    int num_cols, num_lines;
+    int max_width = 0, node_width, col_width;
+    int x, y, num_entries = 0,/* n=0, */ screen_width, table_top;
+    TextNode *front = *node;
+    TextNode *tn;
+
+    gInTable = 1;
+    front->x = text_x;
+    front->y = text_y;
+    for (tn = front->next; tn->type != Endtable; num_entries++, tn = tn->next) {
+        /* Now we need to scan the table group by group */
+        node_width = text_width(tn->next, Endtableitem);
+        if (node_width > max_width)
+            max_width = node_width;
+        /* Get to the beginning og the next group */
+        for (; tn->type != Endtableitem; tn = tn->next);
+    }
+    col_width = max_width + min_inter_column_space;
+    screen_width = gWindow->width - right_margin_space - indent;
+    num_cols = screen_width / col_width;
+    if (num_cols == 0)
+        num_cols = 1;
+    num_lines = num_entries / num_cols;
+    if (num_entries % num_cols != 0)
+        ++num_lines;
+    if (gInLine) {
+        start_newline(present_line_height, *node);
+    }
+    table_top = text_y;
+    num_cols = num_entries / num_lines;
+    if (num_entries % num_lines != 0)
+        ++num_cols;
+    col_width = screen_width / num_cols;
+    for (tn = front->next, x = 0; x < num_cols; x++)
+        for (y = 0; y < num_lines && tn->type != Endtable; y++) {
+            if (num_cols == 1 && y > 0)
+                text_y += line_height;
+            else
+                text_y = table_top + y * line_height;
+            text_x = indent + x * col_width;
+            gInLine = 0;
+            compute_text_extent(tn->next);
+            for (; tn->type != Endtableitem; tn = tn->next);
+            tn = tn->next;
+        }
+    front->height = num_lines * line_height;
+    front->width = screen_width;
+    text_x = indent;
+    if (num_cols == 1)
+        text_y += line_height;
+    else
+        text_y = table_top + front->height;
+    *node = tn;
+    gInLine = 0;
+}
+
+void
+compute_title_extent(HyperDocPage *page)
+{
+    right_margin_space = non_scroll_right_margin_space;
+    page->title->height = twheight + gWindow->border_width;
+    page->title->x = gWindow->border_width + 2 * twwidth + (int) gWindow->border_width / 2;
+    gLineNode = page->title->next;
+    init_title_extents(page);
+    text_y = top_margin + line_height;
+    compute_text_extent(page->title->next);
+    page->title->height = max(text_height(page->title->next, Endtitle),
+                              twheight);
+}
+
+void
+compute_header_extent(HyperDocPage *page)
+{
+
+    /*
+     * Hopefully we will soon be able to actually compute the needed height
+     * for the header here
+     */
+
+    int ty; /* UNUSED */
+
+    gExtentRegion = Header;
+    right_margin_space = non_scroll_right_margin_space;
+    init_extents();
+    ty = text_y = 3 * top_margin + line_height + max(page->title->height, twheight);
+    gLineNode = page->header->next;
+    compute_text_extent(page->header->next);
+    page->header->height = text_height(page->header->next, Endheader);
+    if (page->header->height) {
+        page->header->height += 1 / 2 * line_height;
+        page->top_scroll_margin = (gInLine) ? text_y : text_y - past_line_height;
+        if (!(page->page_flags & NOLINES))
+            page->top_scroll_margin += (int) line_height / 2;
+        page->top_scroll_margin += gWindow->border_width + 2 * top_margin;
+    }
+    else {
+        page->top_scroll_margin = page->title->height + gWindow->border_width +
+            2 * scroll_top_margin;
+    }
+}
+
+void
+compute_footer_extent(HyperDocPage * page)
+{
+    if (page->footer) {
+        gExtentRegion = Footer;
+        right_margin_space = non_scroll_right_margin_space;
+        init_extents();
+        present_line_height = line_height;
+        text_y = line_height;
+        gLineNode = page->footer->next;
+        compute_text_extent(page->footer->next);
+        page->footer->height = text_height(page->footer->next, Endfooter);
+        if (page->footer->height) {
+            if ((!page->page_flags & NOLINES))
+                page->footer->height += (int) line_height / 2;
+            page->bot_scroll_margin = gWindow->height -
+                page->footer->height - bottom_margin
+                - gWindow->border_width + top_margin;
+        }
+        else
+            page->bot_scroll_margin = gWindow->height;
+    }
+}
+
+void
+compute_scrolling_extent(HyperDocPage *page)
+{
+    /* Check to see if there is a scrolling region  */
+
+    if (!page->scrolling) {
+        return;
+    }
+    noop_count = 0;
+
+    /* If there is then compute all the proper locations */
+    gExtentRegion = Scrolling;
+    right_margin_space = non_scroll_right_margin_space + gScrollbarWidth;
+    init_extents();
+    text_y = line_height;
+    gLineNode = page->scrolling->next;
+    compute_text_extent(page->scrolling->next);
+
+    /*
+     * the following is an attempt to fix the bug where one cannot scroll
+     * down to a bitmap that is opened at the bottom of a page.
+     */
+
+    /*
+     * TTT trial if(!gInLine)
+     */
+    if (0) {
+        text_y = text_y - past_line_height;
+    }
+    else if (present_line_height > line_height)
+        text_y = text_y + present_line_height - line_height;
+    page->scrolling->height = text_y;
+
+}
+@
+\section{extent2.c}
+<<extent2.c>>=
+/******************************************************************************
+ *
+ * extent2.h:  HyperDoc extent computation routines
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _EXTENT2_C
+#include "debug.h"
+
+
+<<extent.h>>
+<<group.h>>
+<<titlebar.h>>
+
+#include "all-hyper-proto.h1"
+#include "pixmap.h1"
+
+
+static int cur_height = 0;
+static int max_x_value = 0;
+
+/*
+ * start_newline updates the current header node, and also allocates if needed
+ * memory for the next Line Header. It also assigns the first TextNode on the
+ * line to the structure, because this is the last time I will be able to do
+ * this
+ */
+
+void
+start_newline(int distance, TextNode * node)
+{
+    if (gLineNode != NULL) {
+        if (gTopOfGroupStack->center)
+            center_nodes(gLineNode, node);
+        gLineNode = node;
+    }
+    text_y += distance;
+    past_line_height = distance;
+    present_line_height = line_height;
+    gInLine = 0;
+}
+
+/*
+ * center_nodes goes through and centers all the text between the two
+ * given nodes.
+ */
+
+static void
+center_nodes(TextNode * begin_node, TextNode * end_node)
+{
+    int begin_x, end_x, wmid_x, offset, mid_x;
+    TextNode *node;
+
+    end_x = text_x;
+    begin_x = x_value(begin_node);
+    mid_x = (int) (end_x + begin_x) / 2;
+    wmid_x = (int) (right_margin + indent) / 2;
+
+    if (mid_x > wmid_x)
+        offset = 0;
+    else
+        offset = wmid_x - mid_x;
+
+    for (node = begin_node; node != end_node; node = node->next)
+        if (node->x > 0)
+            node->x += offset;
+}
+
+static int
+punctuation_width(TextNode * node)
+{
+    int twidth, width = strlen(node->data.text);
+
+    twidth = XTextWidth(gTopOfGroupStack->cur_font, node->data.text, width);
+
+    /* check to see if there was some space in front */
+
+    if (gInLine && (node->space & FRONTSPACE))
+        twidth += inter_word_space;
+
+# if 0
+    if (node->space & BACKSPACE) {
+        switch (node->data.text[0]) {
+            case '.':
+            case '?':
+            case '!':
+                twidth += term_punct_space;
+                break;
+        }
+    }
+#endif
+
+    return twidth;
+}
+
+static int
+input_string_width(TextNode * node)
+{
+    InputItem *item;
+    int t_width;
+
+    /** search the symbol table for the proper entry **/
+
+    item = node->link->reference.string;
+
+    /** Once I have gotten this far, I should just be able to calculate
+      the width using the normal font **/
+
+    t_width = (item->size + 1) * gInputFont->max_bounds.width + 10;
+    return t_width;
+
+}
+
+static int
+word_width(TextNode * node)
+{
+    int twidth, len = strlen(node->data.text);
+
+    twidth = XTextWidth(gTopOfGroupStack->cur_font, node->data.text, len);
+    if (node->space & FRONTSPACE)
+        twidth += inter_word_space;
+
+    return twidth;
+}
+
+static int
+verbatim_width(TextNode * node)
+{
+    int twidth, len = strlen(node->data.text);
+
+    twidth = XTextWidth(gTopOfGroupStack->cur_font, node->data.text, len);
+    if (node->space)
+        twidth += inter_word_space;
+
+    return twidth;
+}
+
+static int
+width_of_dash(TextNode * node)
+{
+    int num_dashes, twidth;
+
+    num_dashes = strlen(node->data.text);
+    if (num_dashes > 1)
+        twidth = node->width = num_dashes * dash_width;
+    else
+        twidth = node->width = XTextWidth(gTopOfGroupStack->cur_font,
+                                          node->data.text, 1);
+    if (node->space)
+        twidth += inter_word_space;
+    return twidth;
+}
+
+/*
+ * return the gWindow->width in pixels of the given text node, when
+ * displayed
+ */
+
+int
+text_width(TextNode * node, int Ender)
+{
+    int twidth = 0, num_words;
+
+    for (num_words = 0; node != NULL; num_words++, node = node->next) {
+        if (Ender == Endtokens) {
+            if (node->type == Endtokens)
+                return twidth;
+        }
+        else if (node->type == Ender)
+            return twidth;
+
+        switch (node->type) {
+          case Macro:
+          case Pound:
+            if (node->space && gInLine)
+                twidth += inter_word_space;
+            break;
+          case Punctuation:
+            twidth += punctuation_width(node);
+            break;
+          case Dash:
+            if (gInLine && node->space)
+                twidth += inter_word_space;
+            twidth += width_of_dash(node);
+            break;
+          case Verbatim:
+          case Spadsrctxt:
+            twidth += verbatim_width(node);
+            break;
+          case Lsquarebrace:
+          case Rsquarebrace:
+          case Word:
+            twidth += word_width(node);
+            break;
+          case Box:
+            twidth += 2 * box_space;
+            break;
+          case Link:
+          case Downlink:
+          case Memolink:
+          case Windowlink:
+          case LispMemoLink:
+          case Lispwindowlink:
+          case Lisplink:
+          case Unixlink:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case LispDownLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Unixcommand:
+          case Upbutton:
+          case Returnbutton:
+          case Description:
+            push_active_group();
+            break;
+          case Endbutton:
+          case Endspadcommand:
+          case Enddescription:
+            pop_group_stack();
+            break;
+          case Endlink:
+            pop_group_stack();
+            break;
+          case Inputstring:
+            twidth += input_string_width(node);
+            break;
+          case SimpleBox:
+          case Radiobox:
+            twidth += node->width + ((node->space) ? inter_word_space : 0);
+            break;
+          case Spadcommand:
+          case Spadgraph:
+            push_spad_group();
+            break;
+          case VSpace:
+            break;
+          case HSpace:
+            twidth +=
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            break;
+          case Space:
+            twidth += (gTopOfGroupStack->cur_font->max_bounds.width) *
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            break;
+          case Tab:
+            twidth = (gTopOfGroupStack->cur_font->max_bounds.width) *
+                (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+            break;
+          case Table:
+            twidth = gWindow->width - left_margin - right_margin_space;
+            break;
+          case Tableitem:
+          case Group:
+            twidth += (node->space) ? inter_word_space : 0;
+            push_group_stack();
+            break;
+          case BoldFace:
+            if (node->space)
+                twidth += inter_word_space;
+            bf_top_group();
+            break;
+          case Emphasize:
+            if (node->space)
+                twidth += inter_word_space;
+            if (gTopOfGroupStack->cur_font == gRmFont)
+                em_top_group();
+            else
+                rm_top_group();
+            break;
+          case It:
+            if (node->space)
+                twidth += inter_word_space;
+            em_top_group();
+            break;
+          case Rm:
+          case Sl:
+          case Tt:
+            if (node->space)
+                twidth += inter_word_space;
+            rm_top_group();
+            break;
+          case Endgroup:
+            pop_group_stack();
+            break;
+          case Controlbitmap:
+          case Inputbitmap:
+            if (node->width == -1)
+                insert_bitmap_file(node);
+            twidth += node->width;
+            break;
+          case Inputpixmap:
+            if (node->width == -1)
+                insert_pixmap_file(node);
+            twidth += node->width;
+            break;
+          case Mbox:
+          case Indent:
+          case Endmacro:
+          case Free:
+          case Bound:
+          case Beep:
+          case Item:
+          case Titem:
+          case Beginitems:
+          case Noop:
+          case Endinputbox:
+          case Fi:
+          case Ifcond:
+          case Endif:
+          case Begintitems:
+          case Enditems:
+          case Endtitems:
+          case Endtableitem:
+          case Endtable:
+          case Endparameter:
+          case Endbox:
+          case Endheader:
+          case Endfooter:
+          case Endscrolling:
+          case Endverbatim:
+          case Endspadsrc:
+            break;
+          case Newline:
+            /* WOw, I guess I should ertunr a really big number */
+            twidth += gWindow->width;
+            break;
+          default:
+
+            /*
+             * fprintf(stderr, "Unknown nodetype %d in text_width\n",
+             * node->type);
+             */
+            break;
+        }
+    }
+    return twidth;
+}
+
+/*
+ * total_width traces through the nodes, until it finds a blank space. It is
+ * used by compute_word_extent, and compute_punctuation extent to determine
+ * How far we go before we actually see white space.
+ */
+
+int
+total_width(TextNode * node, int Ender)
+{
+    int twidth = 0;
+
+    for (; (node != NULL); node = node->next) {
+        if (Ender == Endtokens) {
+            if (node->type >= Endtokens)
+                return twidth;
+        }
+        else if (node->type == Ender)
+            return twidth;
+
+        /*
+         * The first thing we check for is to see if there was space in front
+         * of the current node, if so we are done
+         */
+
+        if (node->space)
+            return twidth;
+
+        /*** Else depending on the node type ***/
+
+        switch (node->type) {
+          case Noop:
+          case Endinputbox:
+          case Pound:
+          case Ifcond:
+          case Fi:
+          case Endif:
+            break;
+          case Rsquarebrace:
+          case Punctuation:
+          case Word:
+          case Dash:
+            twidth += XTextWidth(gTopOfGroupStack->cur_font, node->data.text,
+                                 strlen(node->data.text));
+            break;
+          case Box:
+          case Link:
+          case Downlink:
+          case Memolink:
+          case Windowlink:
+          case LispMemoLink:
+          case Lispwindowlink:
+          case Lisplink:
+          case Unixlink:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case LispDownLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Unixcommand:
+          case Inputstring:
+          case SimpleBox:
+          case Radiobox:
+          case Upbutton:
+          case Returnbutton:
+          case Spadcommand:
+          case Spadgraph:
+          case VSpace:
+          case HSpace:
+          case Space:
+          case Table:
+          case Group:
+          case Controlbitmap:
+          case Inputbitmap:
+          case Inputpixmap:
+          case Free:
+          case Beep:
+          case Bound:
+          case Lsquarebrace:
+          case BoldFace:
+          case Emphasize:
+          case It:
+          case Rm:
+          case Sl:
+          case Tt:
+          case Newline:
+          case Verbatim:
+          case Spadsrctxt:
+            return twidth;
+          default:
+            break;
+        }
+    }
+    return twidth;
+}
+
+/*
+ * init_extents initialize some text size variables
+ */
+
+void
+init_extents(void)
+{
+    present_line_height = line_height;
+    gInLine = 0;
+    gInItem = 0;
+    gInAxiomCommand = 0;
+    item_indent = 0;
+    gInDesc = 0;
+    indent = left_margin;
+    text_x = indent;
+    gTopOfGroupStack->cur_font = gRmFont;
+    gTopOfGroupStack->cur_color = gRmColor;
+    right_margin = gWindow->width - right_margin_space;
+    clear_item_stack();
+}
+
+/*
+ * init_title_extents initialize some title text size variables
+ */
+
+void
+init_title_extents(HyperDocPage * page)
+{
+    present_line_height = line_height;
+    gInLine = 0;
+    gInAxiomCommand = 0;
+    item_indent = 0;
+    gInDesc = 0;
+    indent = left_margin + page->title->x;
+    text_x = indent;
+    gTopOfGroupStack->cur_font = gRmFont;
+    gTopOfGroupStack->cur_color = gRmColor;
+    right_margin = gWindow->width - right_margin_space - gWindow->border_width -
+        2 * twwidth;
+    clear_item_stack();
+}
+
+/*
+ * init_text initialize some text size variables
+ */
+
+void
+init_text(void)
+{
+    normal_text_height = gRmFont->ascent + gRmFont->descent;
+    line_height = gRmFont->ascent + gRmFont->descent + inter_line_space;
+    word_off_height = line_height - normal_text_height;
+    space_width = gRmFont->max_bounds.width;
+}
+
+/*
+ * text_height returns the height of a piece of formatted text in pixels
+ */
+
+int
+text_height(TextNode * node, int Ender)
+{
+    cur_height = 0;
+    return text_height1(node, Ender);
+}
+
+/*
+ * text_height1 is the recursive part of text_height
+ */
+
+static int
+text_height1(TextNode * node, int Ender)
+{
+    for (; node != NULL; node = node->next) {
+        if (Ender == Endtokens) {
+            if (node->type > -Endtokens)
+                return cur_height;
+        }
+        else if (node->type == Ender)
+            return cur_height;
+        switch (node->type) {
+          case Center:
+          case Downlink:
+          case Link:
+          case Spadcommand:
+          case Spadgraph:
+          case Upbutton:
+          case Returnbutton:
+          case Windowlink:
+          case Memolink:
+          case Lispwindowlink:
+          case Lisplink:
+          case Unixlink:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case LispDownLink:
+          case LispMemoLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Unixcommand:
+          case SimpleBox:
+          case Radiobox:
+          case Group:
+          case Box:
+          case Controlbitmap:
+          case Inputbitmap:
+          case Inputpixmap:
+          case Horizontalline:
+          case Punctuation:
+          case Lsquarebrace:
+          case Rsquarebrace:
+          case Word:
+          case Verbatim:
+          case Math:
+          case Spadsrctxt:
+          case Dash:
+          case Inputstring:
+            cur_height = max(node->y, cur_height);
+            break;
+          case Mbox:
+          case Macro:
+          case Pound:
+          case Emphasize:
+          case BoldFace:
+          case It:
+          case Rm:
+          case Sl:
+          case Tt:
+          case Endparameter:
+          case Description:
+          case Enddescription:
+          case Noop:
+          case Fi:
+          case Ifcond:
+          case Endif:
+          case Endinputbox:
+          case Tab:
+          case Newline:
+          case Space:
+          case VSpace:
+          case HSpace:
+          case Beginitems:
+          case Begintitems:
+          case Endtitems:
+          case Titem:
+          case Enditems:
+          case Endtable:
+          case Endtableitem:
+          case Item:
+          case Par:
+          case Beep:
+          case Free:
+          case Bound:
+          case Endgroup:
+          case Endcenter:
+          case Endbutton:
+          case Endmacro:
+          case Tableitem:
+          case Endlink:
+          case Endspadcommand:
+          case Indent:
+          case Indentrel:
+          case Endbox:
+          case Endmbox:
+          case Table:
+          case Endverbatim:
+          case Endmath:
+          case Spadsrc:
+          case Endspadsrc:
+            break;
+          case Beginscroll:
+          case Endscroll:
+            break;
+          case Endscrolling:
+            return cur_height;
+          default:
+
+            /*
+             * fprintf(stderr, "Text_height1: Unknown Node Type %d\n",
+             * node->type);
+             */
+            break;
+        }
+    }
+    return cur_height;
+}
+
+/*
+ * max_x returns the height of a piece of formatted text in pixels
+ */
+
+int
+max_x(TextNode * node, int Ender)
+{
+    max_x_value = 0;
+    for (; node != NULL; node = node->next) {
+        if (Ender == Endtokens) {
+            if (node->type >= Endtokens)
+                return max_x_value;
+        }
+        else if (node->type == Ender)
+            return max_x_value;
+        switch (node->type) {
+          case Lsquarebrace:
+          case Rsquarebrace:
+          case Word:
+            max_x_value = max(max_x_value, node->x + word_width(node));
+            break;
+          case Verbatim:
+          case Spadsrctxt:
+            max_x_value = max(max_x_value, node->x + verbatim_width(node));
+            break;
+          case Punctuation:
+            max_x_value = max(max_x_value, node->x + punctuation_width(node));
+            break;
+          case Dash:
+            max_x_value = max(max_x_value, node->x + width_of_dash(node));
+            break;
+          case HSpace:
+            max_x_value = max(max_x_value, node->x +
+                              (node->data.node != NULL ? atoi(node->data.node->data.text) : 1));
+            break;
+          case Space:
+            max_x_value = max(max_x_value, node->x +
+                           (gTopOfGroupStack->cur_font->max_bounds.width) *
+                              (node->data.node != NULL ? atoi(node->data.node->data.text) : 1));
+            break;
+          case Group:
+            push_group_stack();
+            break;
+          case BoldFace:
+            bf_top_group();
+            break;
+          case Emphasize:
+            if (gTopOfGroupStack->cur_font == gRmFont)
+                em_top_group();
+            else
+                rm_top_group();
+            break;
+          case It:
+            em_top_group();
+            break;
+          case Rm:
+          case Sl:
+          case Tt:
+            rm_top_group();
+            break;
+          case Endgroup:
+            pop_group_stack();
+            break;
+          case Controlbitmap:
+          case Inputbitmap:
+            if (node->width == -1)
+                insert_bitmap_file(node);
+            max_x_value = max(max_x_value, node->x + node->width);
+            break;
+          case Inputpixmap:
+            if (node->width == -1)
+                insert_pixmap_file(node);
+            max_x_value = max(max_x_value, node->y + node->width);
+            break;
+          default:
+            break;
+        }
+    }
+    return cur_height;
+}
+
+static int
+x_value(TextNode * node)
+{
+    for (; node != NULL; node = node->next) {
+        switch (node->type) {
+          case Controlbitmap:
+          case Inputbitmap:
+          case Inputpixmap:
+          case Lsquarebrace:
+          case Rsquarebrace:
+          case Word:
+          case Verbatim:
+          case Spadsrctxt:
+          case Dash:
+          case Punctuation:
+          case VSpace:
+          case HSpace:
+          case Horizontalline:
+          case Box:
+          case Downlink:
+          case Link:
+          case Lispwindowlink:
+          case Lisplink:
+          case Unixlink:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case LispDownLink:
+          case LispMemoLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Spadcommand:
+          case Spadgraph:
+          case Unixcommand:
+          case Space:
+          case SimpleBox:
+          case Radiobox:
+            return node->x;
+          default:
+#ifdef DEBUG
+            fprintf(stderr, "X_value did not know x value of type %d\n", node->type);
+#endif
+            return x_value(node->next);
+        }
+    }
+    return 0;
+}
+
+/*
+ * trailing_space computes the length of the trailing spaces of a node
+ */
+
+int
+trailing_space(TextNode * node)
+{
+    int space = 0;
+
+    for (; node->type < Endtokens; node = node->next);
+    if (node->type == Space)
+        space += inter_word_space *
+            (node->data.node != NULL ? atoi(node->data.node->data.text) : 1);
+    return space;
+}
+
+/*
+ * insert_bitmap_file reads a bitmap file into memory
+ */
+
+void
+insert_bitmap_file(TextNode * node)
+{
+    char *filename = node->data.text;
+    int bm_width, bm_height;
+    XImage *im;
+    ImageStruct *image;
+
+    if (*filename == ' ')
+        filename++;
+    if (node->image.pm == 0) {
+        if (
+        ((image = (ImageStruct *) hash_find(&gImageHashTable, filename)) == NULL)
+            || (getenv("HTCACHE"))) {
+
+            /*
+             * read the bitmap if not already in memory or if the environment
+             * variable HTCACHE is set (NAG addition).
+             */
+
+            im = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                  &bm_width, &bm_height);
+
+            /** now add the image to the gImageHashTable **/
+            image = (ImageStruct *) halloc(sizeof(ImageStruct), "ImageStruct");
+            image->image.xi = im;
+            image->width = image->image.xi->width;
+            image->height = image->image.xi->height;
+            image->filename = (char *) halloc(sizeof(char) * strlen(filename) +1,"Image Filename");
+            /* strcpy(image->filename, filename); */
+            sprintf(image->filename, "%s", filename);
+            hash_insert(&gImageHashTable, (char *)image, image->filename);
+        }
+        node->width = image->width;
+        node->height = image->height;
+        node->image.xi = image->image.xi;
+    }
+}
+
+/*
+ * insert_pixmap_file reads a pixmap file into memory
+ */
+
+void
+insert_pixmap_file(TextNode * node)
+{
+    char *filename = node->data.text;
+    int bm_width, bm_height, ret_val;
+    XImage *xi;
+    ImageStruct *image;
+
+    if (*filename == ' ')
+        filename++;
+    if (node->image.xi == 0) {
+        if ((image = (ImageStruct *) hash_find(&gImageHashTable, filename)) == NULL) {
+            ret_val = read_pixmap_file(gXDisplay, gXScreenNumber, filename, &xi,
+                                       &bm_width, &bm_height);
+            switch (ret_val) {
+              case(-1):
+                gSwitch_to_mono = 1;
+                return;
+              case BitmapFileInvalid:
+                fprintf(stderr, "File %s contains invalid bitmap data\n", filename);
+                return;
+              case BitmapOpenFailed:
+                fprintf(stderr, "couldn't open bitmap file %s\n", filename);
+                return;
+              case BitmapNoMemory:
+                fprintf(stderr, "not enough memory to store bitmap\n");
+                return;
+            }
+            image = (ImageStruct *) halloc(sizeof(ImageStruct), "ImageStruct");
+            image->width = bm_width;
+            image->height = bm_height;
+            image->filename = (char *) halloc(sizeof(char) * strlen(filename) +1,
+                                              "insert_pixmap--filename");
+            /* strcpy(image->filename, filename); */
+            sprintf(image->filename, "%s", filename);
+            image->image.xi = xi;
+            hash_insert(&gImageHashTable, (char *)image, image->filename);
+        }
+        node->width = image->width;
+        node->height = plh(image->height + inter_line_space);
+        node->image.xi = image->image.xi;
+    }
+}
+
+/*
+ * plh calculates the closet value of line_height > height
+ */
+
+int
+plh(int height)
+{
+    int rheight = height;
+
+    if (gExtentRegion == Scrolling) {
+        for (rheight = line_height; rheight < height; rheight += line_height)
+            ;
+    }
+    return rheight;
+}
+@
+\section{extent.h}
+<<extent.h>>=
+#ifndef _EXTENT_H_
+#define _EXTENT_H_ 1
+
+<<hyper.h>>
+
+/*
+ * This file contains global macros extern declarations for the extent
+ * computation routines found in extent1.c and extent2.c.
+ */
+
+/*
+ * Definitions of standard text formatting dimensions, etc.
+ *  dimensions given in pixels
+ */
+
+#define left_margin 20
+#define non_scroll_right_margin_space 20
+#define scroll_right_margin_space 40
+#define bottom_margin 15
+#define top_margin 5
+#define scroll_top_margin top_margin
+#define scrollingTopMargin 5
+#define inter_line_space 5
+#define inter_word_space 5
+#define term_punct_space 5
+#define paragraph_space 30
+#define box_space 3
+#define horiz_line_space 3
+#define spadcom_indent 30
+#define min_inter_column_space 10
+#define box_width 3
+#define dash_width 5
+#define dash_y	   4
+
+
+/* next two from display.h. Reorg! */
+
+extern short int gDisplayRegion;
+extern int gRegionOffset;
+
+#define not_in_scroll (!(gDisplayRegion == Scrolling))
+
+#define visible(y, h) \
+  (not_in_scroll  || ((y) + gRegionOffset + gWindow->page->scroll_off \
+		  <= gWindow->scrollheight   && \
+		  (y) + gRegionOffset + gWindow->page->scroll_off - (h) >=  0))
+
+#define pix_visible(y, h) \
+  (not_in_scroll  || ((y) + gRegionOffset + gWindow->page->scroll_off - h +  \
+		   line_height	< gWindow->page->bot_scroll_margin  \
+		      - gWindow->page->top_scroll_margin  && \
+		  (y) + gRegionOffset + gWindow->page->scroll_off >=  0))
+
+#define above(y) ((y) +	 gWindow->page->scroll_off < gWindow->page->top_scroll_margin)
+#define below(y) ((y) + gWindow->page->scroll_off >= gWindow->page->bot_scroll_margin)
+
+
+/* Variables for the formatting state */
+
+extern int right_margin_space;
+extern int right_margin;
+extern int indent;
+extern int item_indent;
+extern int text_x;
+extern int text_y;
+extern int y_off;
+extern int scroll_bot;
+extern int need_scroll_up_button;
+extern int need_scroll_down_button;
+extern int item_space;
+extern int present_line_height;
+extern int past_line_height;
+extern int line_height;		       /* space between lines		   */
+extern int normal_text_height;	       /* space between lines		   */
+extern int space_width;		       /* the maximum width of a character */
+extern int word_off_height;	       /* the diff between text height and */
+
+
+/*
+ * externs from extent1.c
+ */
+
+extern short int gExtentRegion;
+
+extern short int gInAxiomCommand;     /* true iff we are in a \spadcommand */
+extern short int gInDesc;
+extern short int gInItem;	 /* true iff we are in a \item */
+extern short int gInLine;	 /* true iff there have been words printed  */
+extern short int gInTable;
+
+extern TextNode *gLineNode;
+
+#endif
+@
+\section{form\_ext.c}
+<<form-ext.c>>=
+#define _FORM_EXT_C
+#include "debug.h"
+
+<<extent.h>>
+<<group.h>>
+<<scrollbar.h>>
+
+#include "all-hyper-proto.h1"
+
+
+
+/*
+ * A few routines used to help with form extents
+ */
+
+void
+compute_form_page(HyperDocPage *page)
+{
+
+    /*
+     * To solve the problem of improperly nested \em, I will have to keep and
+     * always initialize the top of the stack
+     */
+    while (pop_group_stack() >= 0);
+
+    /*
+     * The compute the text extents
+     */
+    form_header_extent(page);
+    form_footer_extent(page);
+    form_scrolling_extent(page);
+    gWindow->height = window_height(gWindow->page);
+
+}
+
+/*
+ * A simple function that returns the width needed to store show the number
+ * of columns given
+ */
+int
+window_width(int cols)
+{
+    return (left_margin + cols * space_width + non_scroll_right_margin_space);
+}
+
+
+static int
+window_height(HyperDocPage *page)
+{
+    int temp;
+
+    temp = page->header->height + top_margin + bottom_margin;
+
+    if (page->scrolling)
+        temp += page->scrolling->height + page->footer->height;
+
+    return (temp);
+}
+
+
+static void
+form_header_extent(HyperDocPage *page)
+{
+
+    /*
+     * Hopefully I will soon be able to actually compute the needed height
+     * for the header here
+     */
+    gExtentRegion = Header;
+    right_margin_space = non_scroll_right_margin_space;
+    init_extents();
+    text_y = top_margin + line_height;
+    compute_text_extent(page->header->next);
+    page->header->height = (gInLine) ? text_y : text_y - past_line_height;
+    if (!(page->page_flags & NOLINES))
+        page->header->height += (int) line_height / 2;
+    page->header->height += gWindow->border_width;
+}
+
+static void
+form_footer_extent(HyperDocPage *page)
+{
+    if (page->footer) {
+        gExtentRegion = Footer;
+        right_margin_space = non_scroll_right_margin_space;
+        init_extents();
+
+        compute_text_extent(page->footer->next);
+
+        /*
+         * I inserted the 2nd arg to text_height below because it
+         * was missing. Perhaps there is a better value for it.
+         */
+
+        page->footer->height = text_height(page->footer->next,
+            page->footer->next->type);
+        if ((!page->page_flags & NOLINES))
+            page->footer->height += (int) line_height / 2;
+    }
+}
+
+static void
+form_scrolling_extent(HyperDocPage *page)
+{
+
+    /*
+     * Check to see if there is a scrolling region
+     */
+
+    if (page->scrolling) {
+        /*
+         * If there is then compute all the proper locations
+         */
+
+        gExtentRegion = Scrolling;
+        right_margin_space = non_scroll_right_margin_space + gScrollbarWidth;
+        init_extents();
+        text_y = line_height;
+        compute_text_extent(page->scrolling->next);
+        if (!gInLine)
+            text_y = text_y - past_line_height;
+        else if (present_line_height > line_height)
+            text_y = text_y + present_line_height - line_height;
+        page->scrolling->height = text_y;
+    }
+}
+
+
+
+@
+\section{group.h}
+<<group.h>>=
+#ifndef _GROUP_H_
+#define _GROUP_H_ 1
+
+<<hyper.h>>
+
+extern GroupItem   *gTopOfGroupStack;
+
+#endif
+@
+\section{group.c}
+<<group.c>>=
+/******************************************************************************
+ *
+ * group.c: Routines for managing the HyperDoc group stack.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _GROUP_C
+#include "debug.h"
+
+
+<<group.h>>
+<<initx.h>>
+
+#include "all-hyper-proto.h1"
+
+GroupItem *gTopOfGroupStack = NULL;
+
+
+
+int
+pop_group_stack(void)
+{
+    /* This routine pops the top of the current group stack */
+    GroupItem *junk;
+
+    /*
+     * If the the stack has only a single item, then pop it anyway so the
+     * user can see the problem
+     */
+    if (! gTopOfGroupStack->next)
+        return -1;
+
+    /* Else, Pop the thing */
+
+    junk = gTopOfGroupStack;
+    gTopOfGroupStack = gTopOfGroupStack->next;
+    junk->next = NULL;
+
+    free(junk);
+
+    /* Now change the font to the cur_font and the cur_color */
+
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+    return 1;
+
+}
+
+void
+push_group_stack(void)
+{
+    /*
+     * This routine makes room by pushing a new item on the stack
+     */
+    GroupItem *newgp;
+
+    newgp = (GroupItem *) halloc(sizeof(GroupItem), "Push Group Stack");
+    newgp->cur_font = gTopOfGroupStack->cur_font;
+    newgp->cur_color = gTopOfGroupStack->cur_color;
+    newgp->center = gTopOfGroupStack->center;
+    newgp->next = gTopOfGroupStack;
+
+    gTopOfGroupStack = newgp;
+}
+
+void
+init_group_stack(void)
+{
+    gTopOfGroupStack = (GroupItem *) halloc(sizeof(GroupItem), "Push Group Stack");
+    gTopOfGroupStack->center = 0;
+    gTopOfGroupStack->next = NULL;
+    gTopOfGroupStack->cur_color = 0;
+    gTopOfGroupStack->cur_font = NULL;
+}
+
+void
+em_top_group(void)
+{
+    if (! gTopOfGroupStack->next)
+        push_group_stack();
+    gTopOfGroupStack->cur_color = gEmColor;
+    gTopOfGroupStack->cur_font = gEmFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+rm_top_group(void)
+{
+    if (! gTopOfGroupStack->next)
+        push_group_stack();
+    gTopOfGroupStack->cur_color = gRmColor;
+    gTopOfGroupStack->cur_font = gRmFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+
+}
+
+void
+line_top_group(void)
+{
+    if (! gTopOfGroupStack->next)
+        push_group_stack();
+    gTopOfGroupStack->cur_color = gBorderColor;
+    gTopOfGroupStack->cur_font = gRmFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+
+}
+
+void
+bf_top_group(void)
+{
+    /*
+     * Just in case the person is tryin a \em without a grouping
+     */
+
+    if (! gTopOfGroupStack->next)
+        push_group_stack();
+    gTopOfGroupStack->cur_color = gBfColor;
+    gTopOfGroupStack->cur_font = gBfFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+tt_top_group(void)
+{
+    if (! gTopOfGroupStack->next)
+        push_group_stack();
+    gTopOfGroupStack->cur_color = gTtColor;
+    gTopOfGroupStack->cur_font = gTtFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+push_active_group(void)
+{
+    push_group_stack();
+    gTopOfGroupStack->cur_font = gActiveFont;
+    gTopOfGroupStack->cur_color = gActiveColor;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+push_spad_group(void)
+{
+    push_group_stack();
+    gTopOfGroupStack->cur_font = gAxiomFont;
+    gTopOfGroupStack->cur_color = gAxiomColor;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+init_top_group(void)
+{
+    /* clear the group stack */
+    while (pop_group_stack() >= 0)
+        ;
+
+    /* then set the colors to be normal */
+
+    gTopOfGroupStack->cur_color = gRmColor;
+    gTopOfGroupStack->cur_font = gRmFont;
+    change_text(gTopOfGroupStack->cur_color, gTopOfGroupStack->cur_font);
+}
+
+void
+center_top_group(void)
+{
+    push_group_stack();
+    gTopOfGroupStack->center = 1;
+}
+
+GroupItem *
+copy_group_stack(void)
+{
+    GroupItem *newgp = NULL;
+    GroupItem *first = NULL;
+    GroupItem *prev  = NULL;
+    GroupItem *trace = gTopOfGroupStack;
+
+    while (trace) {
+        newgp = (GroupItem *) halloc(sizeof(GroupItem), "Copy Group Stack");
+        newgp->cur_font = trace->cur_font;
+        newgp->cur_color = trace->cur_color;
+        newgp->center = trace->center;
+        if (!first)
+            first = newgp;
+        else
+            prev->next = newgp;
+        prev = newgp;
+        trace = trace->next;
+    }
+    if (newgp)
+        newgp->next = NULL;
+    return first;
+}
+
+void
+free_group_stack(GroupItem *g)
+{
+    GroupItem *trace = g;
+
+    while (trace) {
+        GroupItem *junk = trace;
+        trace = trace->next;
+        free(junk);
+    }
+}
+@
+\section{halloc.c}
+<<halloc.c>>=
+#define _HALLOC_C
+#include "debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#if !defined(BSDplatform)
+#include <malloc.h>
+#endif
+
+FILE *fp;
+
+#include "halloc.h1"
+
+/* allocate memory and bomb if none left (HyperDoc alloc) */
+
+char *
+halloc(int bytes, char *msg)
+{
+    static char buf[200];
+    char *result;
+
+#ifdef DEBUG
+    static int first = 1;
+
+    if (first) {
+        fp = fopen("/tmp/hallocs", "w");
+        first = 0;
+    }
+#endif
+    result = (char *) malloc(bytes);
+#ifdef DEBUG
+    fprintf(fp, "%d\tAlocating %d Bytes for %s\n", result,bytes, msg);
+#endif
+    if (result == NULL) {
+        sprintf(buf, "Ran out of memory allocating %s.\b", msg);
+        fprintf(stderr, "%s\n", buf);
+        exit(-1);
+    }
+    return result;
+}
+@
+\section{hash.c}
+<<hash.c>>=
+#define _HASH_C
+#include "debug.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hash.h"
+
+#include "hash.h1"
+#include "halloc.h1"
+
+/* initialize a hash table */
+
+void
+hash_init(HashTable *table, int size, EqualFunction equal,
+          HashcodeFunction hash_code)
+{
+    int i;
+
+    table->table =
+        (HashEntry **) halloc(size * sizeof(HashEntry *), "HashEntry");
+    for (i = 0; i < size; i++)
+        table->table[i] = NULL;
+    table->size = size;
+    table->equal = equal;
+    table->hash_code = hash_code;
+    table->num_entries = 0;
+}
+
+void
+free_hash(HashTable *table, FreeFunction free_fun)
+{
+  if (table) {
+    int i;
+
+    for (i = 0; i < table->size; i++) {
+      HashEntry *e, *next;
+
+      for (e = table->table[i]; e != NULL;) {
+        next = e->next;
+        (*free_fun) (e->data);
+	(*e).data=0;
+        free(e);
+        e = next;
+      }
+    }
+    free(table->table);
+  }
+}
+
+/* insert an entry into a hash table */
+
+void
+hash_insert(HashTable *table, char *data, char *key)
+{
+    HashEntry *entry = (HashEntry *) halloc(sizeof(HashEntry), "HashEntry");
+    int code;
+
+    entry->data = data;
+    entry->key = key;
+    code = (*table->hash_code) (key, table->size) % table->size;
+#ifdef DEBUG
+    fprintf(stderr, "Hash value = %d\n", code);
+#endif
+    entry->next = table->table[code];
+    table->table[code] = entry;
+    table->num_entries++;
+}
+
+char *
+hash_find(HashTable *table, char *key)
+{
+    HashEntry *entry;
+    int code = table->hash_code(key, table->size) % table->size;
+
+    for (entry = table->table[code]; entry != NULL; entry = entry->next)
+        if ((*table->equal) (entry->key, key))
+            return entry->data;
+    return NULL;
+}
+
+char *
+hash_replace(HashTable *table, char *data, char *key)
+{
+    HashEntry *entry;
+    int code = table->hash_code(key, table->size) % table->size;
+
+    for (entry = table->table[code]; entry != NULL; entry = entry->next)
+        if ((*table->equal) (entry->key, key)) {
+            entry->data = data;
+            return entry->data;
+        }
+    return NULL;
+}
+
+void
+hash_delete(HashTable *table, char *key)
+{
+    HashEntry **entry;
+    int code = table->hash_code(key, table->size) % table->size;
+
+    for (entry = &table->table[code]; *entry != NULL; entry = &((*entry)->next))
+        if ((*table->equal) ((*entry)->key, key)) {
+            *entry = (*entry)->next;
+            table->num_entries--;
+            return;
+        }
+}
+
+void
+hash_map(HashTable *table, MappableFunction func)
+{
+    int i;
+    HashEntry *e;
+
+    if (table == NULL)
+        return;
+    for (i = 0; i < table->size; i++)
+        for (e = table->table[i]; e != NULL; e = e->next)
+            (*func) (e->data);
+}
+
+HashEntry *
+hash_copy_entry(HashEntry *e)
+{
+    HashEntry *ne;
+
+    if (e == NULL)
+        return e;
+    ne = (HashEntry *) halloc(sizeof(HashEntry), "HashEntry");
+    ne->data = e->data;
+    ne->key = e->key;
+    ne->next = hash_copy_entry(e->next);
+    return ne;
+}
+
+/* copy a hash table */
+HashTable *
+hash_copy_table(HashTable *table)
+{
+    HashTable *nt = (HashTable *) halloc(sizeof(HashTable), "copy hash table");
+    int i;
+
+    nt->size = table->size;
+    nt->num_entries = table->num_entries;
+    nt->equal = table->equal;
+    nt->hash_code = table->hash_code;
+    nt->table = (HashEntry **) halloc(nt->size * sizeof(HashEntry *),
+                                      "copy table");
+    for (i = 0; i < table->size; i++)
+        nt->table[i] = hash_copy_entry(table->table[i]);
+    return nt;
+}
+
+/* hash code function for strings */
+int
+string_hash(char *s, int size)
+{
+    int c = 0;
+    char *p =s;
+
+
+    while (*p)
+        c += *p++;
+    return c % size;
+}
+
+/* test strings for equality */
+
+int
+string_equal(char *s1, char *s2)
+{
+    return (strcmp(s1, s2) == 0);
+}
+
+/* make a fresh copy of the given string */
+char *
+alloc_string(char *str)
+{
+    char * result;
+    result = halloc(strlen(str)+1,"String");
+    strcpy(result,str);
+    return (result);
+}
+@
+\section{htadd.c}
+The [[htadd]] function can manipulate the database of hypertex pages.
+To rebuild the hypertex database changes to the [[$AXIOM/doc/hypertex]] 
+subdirectory and type:
+\begin{verbatim}
+htadd -f pages -n pages/*
+\end{verbatim}
+This will create a file called [[pages/ht.db]] which contains entries
+similar to:
+\begin{verbatim}
+	algebra.ht 1102052108
+\page AlgebraPage 216 9
+\page NumberTheoryPage 763 28
+	ALIST.ht 1102052108
+\newcommand AssociationListXmpTitle 140 3
+\newcommand AssociationListXmpNumber 195 4
+\page AssociationListXmpPage 313 7
+	ALIST.pht 1102052108
+\patch AssociationListXmpPagePatch1 0 1
+\patch AssociationListXmpPageEmpty1 447 11
+...
+\end{verbatim}
+<<htadd.c>>=
+/* HyperDoc database file manager */
+
+
+#define _HTADD_C
+<<hyper.h>>
+#include <sys/stat.h>
+#include <errno.h>
+#include <setjmp.h>
+
+<<lex.h>>
+
+#include "htadd.h1"
+#include "addfile.h1"
+#include "halloc.h1"
+#include "hash.h1"
+#include "hterror.h1"
+#include "lex.h1"
+
+
+
+
+/*
+ * These are variables that htadd needs to have declared because it shares
+ * the lexical analyzer with HyperDoc
+ */
+
+int gTtFontIs850=0;
+HDWindow *gWindow = NULL;
+extern int line_number;         /* keeps track of which line a page starts on
+                                 * in a file. This way someone can start
+                                 * including a line number counter into
+                                 * HyperDoc. */
+/* for compatibility with HyperDoc */
+Sock *spad_socket = NULL;
+Sock *session_server = NULL;
+int MenuServerOpened;
+Display *gXDisplay;
+int      gXScreenNumber;
+int fresh = 0;
+
+#define Delete 1
+#define System 2
+#define Current  4
+#define Named  8
+
+#define usage "usage: htadd [-s|-l|-f db-directory] [-d|-n] filenames"
+
+
+int
+main(int argc, char **argv)
+{
+    /*int i;*/
+    char db_dir[256];           /* the directory where the db file is */
+    char dbfilename[256];       /* the database filename */
+    char *filenames[1000];      /* the files to be added */
+    char **fnames = filenames;
+    short flag;                 /* flag for deleting or adding */
+
+    parse_args(argv, db_dir, filenames, &flag);
+
+    if (!filenames[0]) {
+        fprintf(stderr, "%s\n", usage);
+        return -1;
+    }
+
+    parser_init();
+
+    build_db_filename(flag, db_dir, dbfilename);
+
+    if (fresh)
+        unlink(dbfilename);
+
+    if (flag & Delete)
+        while (*fnames)
+            delete_file(dbfilename, *fnames++);
+    else
+        while (*fnames)
+            add_file(dbfilename, *fnames++, fresh);
+    return 0;
+}
+
+/*
+ * This routine parses the command line arguments. It parses
+ * the command line arguments. It returns a flag which tells the calling
+ * routine what database file to use, and whether or not to delete files.
+ */
+
+
+static void
+parse_args(char **argv, char *db_dir, char **filenames, short *fl)
+{
+    *fl = 0;
+
+    while (*++argv) {
+        if (!strcmp(*argv, "-d"))
+            *fl |= Delete;
+        else if (!strcmp(*argv, "-s")) {
+            if (*fl & Current || *fl & Named) {
+                fprintf(stderr, "%s\n", usage);
+                exit(-1);
+            }
+            *fl |= System;
+        }
+        else if (!strcmp(*argv, "-n")) {
+            fresh = 1;
+        }
+        else if (!strcmp(*argv, "-l")) {
+            if (*fl & System || *fl & Named) {
+                fprintf(stderr, "%s\n", usage);
+                exit(-1);
+            }
+            *fl |= Current;
+        }
+        else if (!strcmp(*argv, "-f")) {
+            if (*fl & System || *fl & Current) {
+                fprintf(stderr, "%s\n", usage);
+                exit(-1);
+            }
+            *fl |= Named;
+            strcpy(db_dir, *++argv);
+        }
+        else
+            *filenames++ = *argv;
+    }
+
+    *filenames = NULL;
+}
+
+
+
+static int
+writable(struct stat buff)
+{
+#ifdef DEBUG
+    unsigned short uid = geteuid(), gid = getegid();
+
+    fprintf(stderr, "Uid = %d and Gid = %d\n", uid, gid);
+#endif
+
+    /*
+     * Checks the status structure sent against the user id, and goup id
+     */
+    if ((buff.st_uid == geteuid()) && (buff.st_mode & S_IWUSR))
+        return 1;
+    else if ((buff.st_gid == getegid()) && (buff.st_mode & S_IWGRP))
+        return 1;
+    else if ((buff.st_mode & S_IWOTH))
+        return 1;
+    return 0;
+}
+
+/* check to see if the user has permission */
+
+/*
+ * This procedure builds the db filename. Subsequently, it is passed onto all
+ * the add files that are called.
+ */
+
+
+static int
+build_db_filename(short flag, char *db_dir, char *dbfilename)
+{
+    int ret_status;
+    struct stat buff;
+    char *SPAD;
+    char path[256];
+
+
+    if (flag & System) {
+        SPAD = (char *) getenv("AXIOM");
+        if (SPAD == NULL) {
+            fprintf(stderr,
+                    "Build_db_filename: Defaulting on $AXIOM\n");
+            SPAD = (char *) def_spad;
+        }
+        sprintf(dbfilename, "%s/doc/hypertex/pages/%s", SPAD, db_file_name);
+        sprintf(path, "%s/doc/hypertex/pages", SPAD);
+    }
+    else if (flag & Named) {
+        sprintf(dbfilename, "%s/%s", db_dir, db_file_name);
+        strcpy(path, db_dir);
+    }
+    else {                      /* use the current directory */
+        sprintf(dbfilename, "./%s", db_file_name);
+        sprintf(path, "./");
+    }
+/*    fprintf(stderr,"htadd:build_db_filename:dbfilename=%s\n",dbfilename);*/
+    /* Now see if I can write to the file  */
+    ret_status = stat(dbfilename, &buff);
+    if (ret_status == -1) {
+        if (errno == ENOENT) {
+            /* If the file does not exist, then check it's path */
+            ret_status = stat(path, &buff);
+        }
+        if (ret_status == -1) {
+            perror("build_db_file");
+            exit(-1);
+        }
+    }
+
+    /* check the status */
+
+    if (writable(buff))
+        return 1;
+
+    fprintf(stderr, "build_db_filename: Database file name is not writable\n");
+    exit(-1);
+    return 0;
+}
+
+
+/***
+
+   This procedure now works as follows:
+     (1) It adds the files to the db_file without full pathnames.
+           Two names are going to be used when adding a file -
+             addname <-- The name without any paths
+             fullname <-- The name with a path prepended to it
+     (2) If the user specifies a pathname, then it is the path name that
+           is used. If the user does not dpecify a path name, then possible
+           paths are found as follows:
+              (i) If the user has an environment variable HTPATH set, the
+              paths mentioned are used.
+              (ii) If not, then the $AXIOM environment variable is used.
+****/
+
+static void
+add_file(char *dbname, char *name, int fresh)
+{
+    char fullname[256];
+    char temp_db_file[256];
+    FILE *db_fp = NULL;
+    FILE *temp_db_fp = NULL;
+    FILE *ht_fp = NULL;
+    char addname[100];
+    /*char *HTPATH;*/
+    /*char *trace;*/
+    /*char *spad;*/
+
+
+    /** First thing I should do is find the proper file and open it **/
+    ht_fp = ht_file_open(fullname, addname, name);
+
+    /*
+     * Now I should try to open the two database files. The one to work with,
+     * and the temporary one; Send it a 1 so it checks for write access
+     */
+    if (fresh) {
+        if ((db_fp = fopen(dbname, "a")) == NULL) {
+            fprintf(stderr, "Can't open database: %s file for appending\n", dbname);
+            exit(-1);
+        }
+    }
+    else {
+        if ((db_fp = fopen(dbname, "r")) == NULL) {
+        }
+    }
+    if (!fresh)
+        temp_db_fp = temp_file_open(temp_db_file);
+
+    /** Now actually update the file by adding the changes ***/
+    update_db(db_fp, temp_db_fp, ht_fp, addname, fullname, fresh);
+
+    if (!fresh)
+        fclose(temp_db_fp);
+    fclose(ht_fp);
+    if (db_fp != NULL)
+        fclose(db_fp);
+    if (!fresh) {
+        copy_file(temp_db_file, dbname);
+        unlink(temp_db_file);
+    }
+}
+
+static void
+update_db(FILE *db, FILE *temp_db, FILE *new_file,
+          char *addname, char *fullname, int fresh)
+{
+    char *fname;
+    int c, file_there = 0, mtime;
+
+    if (fresh) {
+        add_new_pages(db, new_file, addname, fullname);
+        return;
+    }
+    if (db == NULL) {
+        add_new_pages(temp_db, new_file, addname, fullname);
+        return;
+    }
+    init_scanner();
+    cfile = db;
+    c = get_char();
+    do {
+        if (c == '\t') {
+            get_filename();
+            fname = alloc_string(token.id);
+            get_token();
+            mtime = atoi(token.id);
+            if (strcmp(fname, addname) == 0) {
+                save_scanner_state();
+                add_new_pages(temp_db, new_file, addname, fullname);
+                restore_scanner_state();
+                file_there = 1;
+                while ((c = get_char()) != EOF) {
+                    if (c == '\t')
+                        break;
+                }
+            }
+            else {
+                fprintf(temp_db, "\t%s %d", fname, mtime);
+                while ((c = get_char()) != EOF) {
+                    if (c == '\t')
+                        break;
+                    putc(c, temp_db);
+                }
+            }
+            free(fname);
+        }
+        else
+            c = get_char();
+    } while (c != EOF);
+    if (!file_there) {
+        add_new_pages(temp_db, new_file, addname, fullname);
+    }
+}
+
+#define Special(t) (( t == Page || t == NewCommand || t == Patch )?(1):(0))
+#define ptype(c, t) (strcpy(c, t));
+
+static void
+add_new_pages(FILE *temp_db, FILE *new_file, char *addname, char *fullname)
+{
+    char type[15];
+    int pos;
+    int present_type;
+    int pages = 0;
+    struct stat fstats;
+
+    stat(fullname, &fstats);
+    fprintf(temp_db, "\t%s %d\n", addname, (int)fstats.st_mtime);
+    cfile = new_file;
+    init_scanner();
+    while (get_token() != EOF) {
+        if (Special(token.type)) {
+            ptype(type, token.id);
+            present_type = token.type;
+            pos = keyword_fpos;
+            get_token();
+            if (token.type != Lbrace) {
+                fprintf(stderr, "missing left brace after a page, macro or patch \
+                         declaration\n");
+                fprintf(stderr, "In the file %s on line %d\n", fullname, line_number);
+                exit(-1);
+            }
+            get_token();
+            if (present_type == Page && token.type != Word) {
+                fprintf(stderr, "missing page name after \\begin{page}\n");
+                fprintf(stderr, "In the file %s on line %d\n", fullname, line_number);
+                exit(-1);
+            }
+            else if (present_type == Macro && token.type != Macro) {
+                fprintf(stderr, "Expected a \\macro name after newcommand, got %s\n",
+                        token.id);
+                fprintf(stderr, "In the file %s on line %d\n", fullname, line_number);
+                exit(-1);
+            }
+            else if (present_type == Patch && token.type != Word) {
+                fprintf(stderr, "Missing patch name after a \\begin{patch}\n");
+                fprintf(stderr, "In the file %s on line %d\n", fullname, line_number);
+                exit(-1);
+            }
+            fprintf(temp_db, "\\%s %s %d %d\n", type,
+                    token.id, pos, line_number);
+            pages++;
+        }
+    }
+    printf("Added %3d pages and/or macros from %s\n", pages, addname);
+}
+
+static void
+copy_file(char *f1, char *f2)
+{
+    FILE *fp1, *fp2;
+    int c;
+
+    fp1 = fopen(f1, "r");
+    fp2 = fopen(f2, "w");
+    while ((c = getc(fp1)) != EOF) {
+        putc(c, fp2);
+    }
+    fclose(fp2);
+    fclose(fp1);
+}
+
+
+
+#define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define delim(c) \
+  (whitespace(c) )
+
+
+static void
+get_filename(void)
+{
+    int c, ws;
+    static char buffer[256];
+    char *buf = buffer;
+
+    do {
+        keyword_fpos = fpos;
+        c = get_char();
+        ws = whitespace(c);
+    } while (ws);
+    switch (c) {
+      case EOF:
+        fprintf(stderr, "Error trying to read ht.db, unexpected EOF\n");
+        exit(-1);
+      case '%':
+      case '\\':
+      case '{':
+      case '}':
+        fprintf(stderr, "Error unexpexted character %c\n",c);
+        exit(-1);
+      default:
+        do {
+            *buf++ = c;
+        } while ((c = get_char()) != EOF && !delim(c));
+        unget_char(c);
+        *buf = '\0';
+        token.type = Word;
+        token.id = buffer;
+        break;
+    }
+}
+
+static int
+delete_file(char *dbname, char *name)
+{
+    char temp_db_file[256];
+    FILE *db_fp, *temp_db_fp;
+    char dname[256];
+
+
+    strcpy(dname, name);
+    extend_ht(dname);
+
+    /* Open both the tmp database and the real one */
+    if ((db_fp = fopen(dbname, "r")) == NULL) {
+        fprintf(stderr, "database file is empty, nothing to delete\n");
+        return 1;
+    }
+
+    temp_db_fp = temp_file_open(temp_db_file);
+
+    /** Now actually update the file by deleting the pages */
+    delete_db(db_fp, temp_db_fp, dname);
+
+    fclose(temp_db_fp);
+    if (db_fp != NULL)
+        fclose(db_fp);
+    copy_file(temp_db_file, dbname);
+    unlink(temp_db_file);
+    return 0;
+}
+
+static void
+delete_db(FILE *db, FILE *temp_db, char *name)
+{
+    char *fname;
+    int c/*, file_there = 0*/, mtime;
+
+    init_scanner();
+    cfile = db;
+    c = get_char();
+    do {
+        if (c == '\t') {
+            get_filename();
+            fname = alloc_string(token.id);
+            get_token();
+            mtime = atoi(token.id);
+            if (strcmp(fname, name) == 0) {
+                while ((c = get_char()) != EOF) {
+                    if (c == '\t')
+                        break;
+                }
+            }
+            else {
+                fprintf(temp_db, "\t%s %d", fname, mtime);
+                while ((c = get_char()) != EOF) {
+                    if (c == '\t')
+                        break;
+                    putc(c, temp_db);
+                }
+            }
+            free(fname);
+        }
+        else
+            c = get_char();
+    } while (c != EOF);
+}
+@
+\section{hterror.h}
+<<hterror.h>>=
+#define HTCONDNODE 1	/* unrecognized condition node */
+#define KEYTYPE	   2	/* unrecognized keyword found in lex.c */
+#define Numerrors  2
+
+#ifdef HTERROR
+char *errmess[] =  {
+  "place holder",
+  "parsing condition node",
+  "unrecognized keyword" };
+#endif
+@
+\section{hterror.c}
+<<hterror.c>>=
+#define _HTERROR_C
+#define HTERROR
+
+#include "debug.h"
+
+<<lex.h>>
+<<parse.h>>
+
+#include "all-hyper-proto.h1"
+
+char ebuffer[128];
+jmp_buf jmpbuf;
+
+/*
+ * This file is the error handling routine in AXIOM. The main routine is
+ * called htperror(): arguments: msg - like perror it accepts an error
+ * message to be printed errno - the errno which occurred. This is so an
+ * appropriate error message can be printed.
+ *
+ * The prints out the page name, and then the filename in which the error
+ * occurred. If possible it also tries to print out the next ten tokens.
+ */
+
+void
+jump(void)
+{
+    if (gWindow == NULL)
+        exit(-1);
+    longjmp(jmpbuf, 1);
+    fprintf(stderr, "(HyperDoc) Long Jump failed, Exiting\n");
+    exit(-1);
+}
+
+void
+print_page_and_filename(void)
+{
+    char obuff[128];
+
+    if (gPageBeingParsed->type == Normal) {
+
+        /*
+         * Now try to inform the user as close to possible where the error
+         * occurred
+         */
+        sprintf(obuff, "(HyperDoc) While parsing %s on line %d\n\tin the file %s\n",
+                gPageBeingParsed->name, line_number,
+                gPageBeingParsed->filename);
+    }
+    else if (gPageBeingParsed->type == SpadGen) {
+        sprintf(obuff, "While parsing %s from the Spad socket\n",
+                gPageBeingParsed->name);
+    }
+    else if (gPageBeingParsed->type == Unixfd) {
+        sprintf(obuff, "While parsing %s from a Unixpipe\n",
+                gPageBeingParsed->name);
+    }
+    else {
+        /* Unknown page type */
+        sprintf(obuff, "While parsing %s\n", gPageBeingParsed->name);
+    }
+    fprintf(stderr, "%s", obuff);
+}
+
+
+void
+print_next_ten_tokens(void)
+{
+    int i;
+    int v;
+
+    fprintf(stderr, "Trying to print the next ten tokens\n");
+    for (i = 0; i < 10; i++) {
+        v = get_token();
+        if (v == EOF)
+            break;
+        print_token();
+    }
+    fprintf(stderr, "\n");
+}
+
+/* print out a token value */
+void
+print_token(void)
+{
+    if (token.type == Word)
+        printf("%s ", token.id);
+    else {
+        token_name(token.type);
+        printf("\\%s ", ebuffer);
+    }
+    fflush(stdout);
+}
+
+
+void
+token_name(int type)
+{
+    if (type <= NumberUserTokens)
+        strcpy(ebuffer, token_table[type]);
+    else {
+        switch (type) {
+          case Lbrace:
+            strcpy(ebuffer, "{");
+            break;
+          case Rbrace:
+            strcpy(ebuffer, "}");
+            break;
+          case Macro:
+            strcpy(ebuffer, token.id);
+            break;
+          case Group:
+            strcpy(ebuffer, "{");
+            break;
+          case Pound:
+            strcpy(ebuffer, "#");
+            break;
+          case Lsquarebrace:
+            strcpy(ebuffer, "[");
+            break;
+          case Rsquarebrace:
+            strcpy(ebuffer, "]");
+            break;
+          case Punctuation:
+            strcpy(ebuffer, token.id);
+            break;
+          case Dash:
+            strcpy(ebuffer, token.id);
+            break;
+          case Verbatim:
+            strcpy(ebuffer, "\\begin{verbatim}");
+            break;
+          case Scroll:
+            strcpy(ebuffer, "\\begin{scroll}");
+            break;
+          case Dollar:
+            strcpy(ebuffer, "$");
+            break;
+          case Percent:
+            strcpy(ebuffer, "%");
+            break;
+          case Carrot:
+            strcpy(ebuffer, "^");
+            break;
+          case Underscore:
+            strcpy(ebuffer, "_");
+            break;
+          case Tilde:
+            strcpy(ebuffer, "~");
+            break;
+          case Cond:
+            sprintf(ebuffer, "\\%s", token.id);
+            break;
+          case Icorrection:
+            strcpy(ebuffer, "\\/");
+            break;
+          case Paste:
+            strcpy(ebuffer, "\\begin{paste}");
+            break;
+          case Patch:
+            strcpy(ebuffer, "\\begin{patch}");
+            break;
+          default:
+            sprintf(ebuffer, " %d ", type);
+        }
+        /*return 1;*/
+    }
+}
+void
+htperror(char *msg, int errno)
+{
+    char obuff[256];
+
+    /* The first thing I do is create the error message */
+
+    if (errno <= Numerrors) {
+        sprintf(obuff, "%s:%s\n", msg, errmess[errno]);
+    }
+    else {
+        sprintf(obuff, "%s:\n", msg);
+        fprintf(stderr, "Unknown error type %d\n", errno);
+    }
+    fprintf(stderr, "%s", obuff);
+
+    print_page_and_filename();
+
+    print_next_ten_tokens();
+}
+@
+\section{hthits.c}
+
+This source file implements HyperDoc's ability to scan files for a
+given pattern.  For that purpose it needs a ``regex'' for string
+pattern matching.  
+
+This source file used to rely on [[<regexp.h>]],
+which was originally part of the X/Open System Interface and Headers
+Issue 2.  However, since then, it has been withdrawn and no longer
+always available on newer platfroms.  Consequently,
+we need to use a different, portable regex library.  The POSIX
+definition provides one, namely through [[<regex.h>]].  That is what we
+use now.  Its availability is tested at configure time.
+
+<<hthits.c>>=
+/*
+ * hthits pattern htdb-file
+ *
+ * Scan HyperDoc files for a given pattern.
+ *
+ * The output contains lines of the form:
+ *
+ * page-name`title`n
+ *
+ * The title and body of each page are scanned but the name is not. It is
+ * possible that the title matches but not any lines. The number of matches
+ * in the page (n) is given last.
+ *
+ * SMW Feb 91
+ */
+#define _HTHITS_C
+
+#include "debug.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <regex.h>
+
+/*
+ * For fixed-size arrays.
+ */
+#define MAX_HTDB_LINE   1024
+#define MAX_ENTRY_TYPE  30      /* I.e. \page \newcommand \patch ... */
+#define MAX_ENTRY_NAME  1024    /* E.g. DifferentialCalculusPage */
+#define MAX_COMP_REGEX  1024
+
+typedef struct pgInfo {
+    char name[MAX_ENTRY_NAME];
+    long start, size;
+} PgInfo ;
+
+#include "hthits.h1"
+
+/*
+ * Global variables set according to the command line.
+ */
+
+char *progName;
+char *pattern;
+char *htdbFName;
+int gverifydates=0;
+regex_t reg_pattern;
+
+int
+main(int argc,char ** argv)
+{
+    cmdline(argc, argv);
+
+    regcomp(&reg_pattern, pattern, REG_NEWLINE);
+
+    handleHtdb();
+    return(0);
+}
+
+void
+cmdline(int argc,char ** argv)
+{
+    progName = argv[0];
+
+    if (argc != 3) {
+        fprintf(stderr, "Usage: %s pattern htdb-file\n", progName);
+        exit(1);
+    }
+
+    pattern = argv[1];
+    htdbFName = argv[2];
+}
+
+void
+handleHtdb(void)
+{
+    FILE *htdbFile;
+    int c;
+
+    htdbFile = fopen(htdbFName, "r");
+    if (htdbFile == NULL)
+        badDB();
+
+    while ((c = getc(htdbFile)) != EOF) {
+        if (c != '\t')
+            badDB();
+        ungetc(c, htdbFile);
+
+        handleFile(htdbFile);
+    }
+    fclose(htdbFile);
+}
+
+
+void
+handleFile(FILE *htdbFile)
+{
+    static PgInfo *pgInfoV = 0;
+    static int pgInfoC = 0;
+
+    char htdbLine[MAX_HTDB_LINE];
+    char htfname[MAX_HTDB_LINE];
+    time_t httime;
+    long htsize;
+    struct stat htstat;
+
+    long fstart, fend;
+    int rc, i, npages;
+
+    char entname[MAX_ENTRY_NAME], enttype[MAX_ENTRY_TYPE];
+    long entoffset, entlineno;
+
+    fgets(htdbLine, MAX_HTDB_LINE, htdbFile);
+
+    sscanf(htdbLine, " %s %ld", htfname, &httime);
+
+    /*
+     * 1. Verify file: get size and check modification time.
+     */
+    rc = stat(htfname, &htstat);
+    if (rc == -1) {
+        fprintf(stderr, "%s: Cannot access %s\n", progName, htfname);
+        exit(1);
+    }
+    if (gverifydates && (htstat.st_mtime != httime)) {
+
+        fprintf(stderr, "%s: Out of date file %s\n", progName, htfname);
+        exit(1);
+    }
+    htsize = htstat.st_size;
+
+    /*
+     * 2. Count the pages in the file.
+     */
+    npages = 0;
+    fstart = ftell(htdbFile);
+    fend = ftell(htdbFile);
+
+    while (fgets(htdbLine, MAX_HTDB_LINE, htdbFile) != NULL) {
+        if (htdbLine[0] == '\t')
+            break;
+        if (!strncmp(htdbLine, "\\page", 5))
+            npages++;
+        fend = ftell(htdbFile);
+    }
+
+    /*
+     * 3. Find offset and size of each \page (skipping \newcommands etc.)
+     */
+    if (npages > pgInfoC) {
+        if (pgInfoV)
+            free(pgInfoV);
+
+        pgInfoC = npages;
+        pgInfoV = (PgInfo *)
+            malloc(npages * sizeof(PgInfo));
+
+        if (!pgInfoV) {
+            fprintf(stderr, "%s: out of memory\n", progName);
+            exit(1);
+        }
+    }
+
+    fseek(htdbFile, fstart, 0);
+
+    for (i = 0; fgets(htdbLine, MAX_HTDB_LINE, htdbFile) != NULL;) {
+        if (htdbLine[0] == '\t')
+            break;
+
+        sscanf(htdbLine, "%s %s %ld %ld",
+               enttype, entname, &entoffset, &entlineno);
+
+        if (i > 0 && pgInfoV[i - 1].size == -1)
+            pgInfoV[i - 1].size = entoffset - pgInfoV[i - 1].start;
+
+        if (!strcmp(enttype, "\\page")) {
+            strncpy(pgInfoV[i].name, entname, MAX_ENTRY_NAME);
+            pgInfoV[i].start = entoffset;
+            pgInfoV[i].size = -1;
+
+            i++;
+        }
+    }
+    if (i > 0 && pgInfoV[i - 1].size == -1)
+        pgInfoV[i - 1].size = htsize - pgInfoV[i - 1].start;
+
+    if (i != npages)
+        badDB();
+
+    /*
+     * 4. Position database input to read next file-description
+     */
+    fseek(htdbFile, fend, 0);
+
+    /*
+     * 5. Process the pages of the file.
+     */
+    handleFilePages(htfname, npages, pgInfoV);
+}
+
+void
+handleFilePages(char *fname, int pgc, PgInfo *pgv)
+{
+    FILE *infile;
+    int i;
+
+    infile = fopen(fname, "r");
+    if (infile == NULL) {
+        fprintf(stderr, "%s: Cannot read file %s\n", progName, fname);
+        exit(1);
+    }
+
+
+    for (i = 0; i < pgc; i++)
+        handlePage(infile, pgv + i);
+
+    fclose(infile);
+
+}
+
+void
+handlePage(FILE *infile,PgInfo * pg)
+{
+    static char *pgBuf = 0;
+    static int pgBufSize = 0;
+
+    char *title, *body;
+
+    if (pg->size > pgBufSize - 1) {
+        if (pgBuf)
+            free(pgBuf);
+        pgBufSize = pg->size + 20000;
+        pgBuf = (char *)malloc(pgBufSize);
+
+        if (!pgBuf) {
+            fprintf(stderr,"%s: Out of memory\n", progName);
+            exit(1);
+        }
+    }
+
+    fseek(infile, pg->start, 0);
+    fread(pgBuf, pg->size, 1, infile);
+    pgBuf[pg->size] = 0;
+
+    splitpage(pgBuf, &title, &body);
+    /*untexbuf(title);*/
+    untexbuf(body);
+
+#ifdef DEBUG
+    printf("-------------- %s -------------\n%s", pg->name, pgBuf);
+    printf("============== %s =============\n", title);
+    printf("%s", body);
+#endif
+
+    searchPage(pg->name, title, body);
+
+}
+
+void 
+searchPage(char *pgname,char * pgtitle,char * pgbody)
+{
+    char *bodyrest;
+    regmatch_t match_pos;
+    int nhits = 0;
+
+    if (!regexec(&reg_pattern, pgtitle, 1, &match_pos, 0)) 
+        nhits++;
+
+    bodyrest = pgbody;
+    while (!regexec(&reg_pattern, bodyrest, 1, &match_pos, 0)) {
+        nhits++;
+        bodyrest += match_pos.rm_eo;
+    }
+    if (nhits) {
+        printf("\\newsearchresultentry{%d}{%s}",nhits, pgtitle);
+        squirt(pgname, strlen(pgname));
+        printf("\n");
+    }
+}
+
+/*
+ * Given string s and length n, output ` followed by the first n characters
+ * of s with ` and newline converted to blanks. This function destructively
+ * modifies s.
+ */
+
+void 
+squirt(char *s, int n)
+{
+    register char *t, *e;
+    int c;
+
+    c = s[n];
+
+    for (t = s, e = s + n; t < e; t++)
+        if (*t == '`' || *t == '\n')
+            *t = ' ';
+
+    if (s[n] != 0) {
+        s[n] = 0;
+    }
+    printf("{%.*s}", n, s);
+    s[n] = c;
+}
+
+/*
+ * Any newlines and separator characters in the title are changed to blanks.
+ */
+void 
+splitpage(char *buf, char **ptitle, char **pbody)
+{
+    int n, depth, tno;
+    char *s;
+
+    switch (buf[1]) {
+      case 'p':
+        tno = 2;
+        break;                  /* \page{Name}{Title} */
+      case 'b':
+        tno = 3;
+        break;                  /* \begin{page}{Name}{Title} */
+      default:
+        fprintf(stderr, "%s: Invalid page format: %s\n", progName, buf);
+        exit(1);
+    }
+
+    n = 0;
+    depth = 0;
+
+    for (s = buf; *s; s++) {
+        if (*s == '{')
+            if (++depth == 1 && ++n == tno)
+                *ptitle = s + 1;
+        if (*s == '}')
+            if (depth-- == 1 && n == tno) {
+                *s = 0;
+                *pbody = s + 1;
+                break;
+            }
+    }
+}
+
+
+void 
+untexbuf(register char *s)
+{
+    register char *d = s;
+
+    while (*s)
+        switch (*s) {
+          case '\\':
+            *d++ = ' ';
+            s++;
+            if (*s != '%')
+                while (isalpha(*s))
+                    s++;
+            break;
+          case '%':
+            *d++ = ' ';
+            s++;
+            while (*s && *s != '\n')
+                s++;
+            break;
+          case '{':
+          case '}':
+          case '#':
+            *d++ = ' ';
+            s++;
+            break;
+          default:
+            *d++ = *s++;
+        }
+    *d = 0;
+}
+
+void 
+badDB(void)
+{
+    fprintf(stderr, "%s:  bad database file %s\n", progName, htdbFName);
+    exit(1);
+}
+
+void
+regerr(int code)
+{
+    fprintf(stderr, "%s: regular expression error %d for \"%s\"\n",
+            progName, code, pattern);
+}
+@
+\section{htinp.c}
+<<htinp.c>>=
+#define _HTINP_C
+#include "debug.h"
+
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <setjmp.h>
+
+<<hyper.h>>
+<<mem.h>>
+<<group.h>>
+<<parse.h>>
+#include "bsdsignal.h"
+
+#include "all-hyper-proto.h1"
+#include "sockio-c.h1"
+#include "bsdsignal.h1"
+
+extern char **input_file_list;
+extern int input_file_count;
+extern int make_patch_files;
+extern int kill_spad;
+extern jmp_buf jmpbuf;
+
+
+#define MaxInputFiles 256
+char *active_file_list[MaxInputFiles];
+int num_active_files = 0;
+char *inactive_file_list[MaxInputFiles];
+int num_inactive_files = 0;
+int include_bf = 0;
+char buf_for_record_commands[256];
+
+
+
+void
+make_record(void)
+{
+  int i;
+  for (i=0;i<input_file_count;i++){
+    send_lisp_command("(|clearCmdCompletely|)");
+    send_lisp_command("(setq |$testingSystem| T)");
+    send_lisp_command("(setq |$printLoadMsgs| NIL)");
+    send_lisp_command("(setq |$BreakMode| '|resume|)");
+    sprintf(buf_for_record_commands,"(|inputFile2RecordFile| '\"%s\")",input_file_list[i]);
+    fprintf(stderr,"%s\n",buf_for_record_commands);
+    send_lisp_command(buf_for_record_commands);
+  }
+  if (kill_spad){
+    i = connect_spad();
+    if (i != NotConnected && i != SpadBusy)
+      send_int(spad_socket, KillLispSystem);
+  }
+
+}
+
+void
+verify_record(void)
+{
+  int i;
+  for (i=0;i<input_file_count;i++){
+    send_lisp_command("(|clearCmdCompletely|)");
+    send_lisp_command("(setq |$testingSystem| T)");
+    send_lisp_command("(setq |$printLoadMsgs| NIL)");
+    send_lisp_command("(setq |$BreakMode| '|resume|)");
+    sprintf(buf_for_record_commands,"(|verifyRecordFile| '\"%s\")",input_file_list[i]);
+    fprintf(stderr,"%s\n",buf_for_record_commands);
+    send_lisp_command(buf_for_record_commands);
+  }
+  if (kill_spad) {
+    i = connect_spad();
+    if (i != NotConnected && i != SpadBusy)
+      send_int(spad_socket, KillLispSystem);
+  }
+}
+
+
+void
+ht2_input(void)
+{
+  HashTable *table;
+  HashEntry *entry;
+  int i;
+
+  bsdSignal(SIGUSR2, SIG_IGN,RestartSystemCalls);
+  gWindow = alloc_hd_window();
+  init_group_stack();
+  table = gWindow->fPageHashTable;
+  make_input_file_list();
+  for (i = 0; i < table->size; i++)
+    for (entry = table->table[i]; entry != NULL; entry = entry->next)
+      make_the_input_file((UnloadedPage *) entry->data);
+  if (kill_spad){
+    i = connect_spad();
+    if (i != NotConnected && i != SpadBusy)
+      send_int(spad_socket, KillLispSystem);
+  }
+}
+
+static char *
+make_input_file_name(char *buf, char *filename)
+{
+    char *b, *c;
+
+    strcpy(buf, filename);
+    for (b = buf + strlen(buf) - 1; b != buf && *b != '/'; b--);
+    if (b != buf)
+        b = b + 1;
+    for (c = b; *c != '.' || c[1] != 'h' || c[2] != 't'; c++);
+    strcpy(c, ".input");
+    return b;
+}
+
+static char *
+make_paste_file_name(char *buf, char *filename)
+{
+    char *b, *c;
+
+    strcpy(buf, filename);
+    for (b = buf + strlen(buf) - 1; b != buf && *b != '/'; b--);
+    if (b != buf)
+        b = b + 1;
+    for (c = b; *c != '.' || c[1] != 'h' || c[2] != 't'; c++);
+    strcpy(c, ".pht");
+    return b;
+}
+
+static void
+make_the_input_file(UnloadedPage *page)
+{
+    char buf[1024], *b;
+
+    if (!page->fpos.name)
+        return;
+    b = make_input_file_name(buf, page->fpos.name);
+    if (inListAndNewer(b, page->fpos.name)) {
+        printf("parsing: %s\n", page->name);
+        if (setjmp(jmpbuf)) {
+            printf("Syntax error!\n");
+        }
+        else {
+            load_page((HyperDocPage *)page);
+            make_input_file_from_page(gWindow->page);
+        }
+    }
+}
+
+int example_number;
+
+static void
+make_input_file_from_page(HyperDocPage *page)
+{
+  TextNode *node;
+  int starting_file = 1,/* i,*/ /*len,*/ ret_val;
+  char *buf, buf2[1024], buf3[1024];
+  char *b, *c, *com;
+  FILE *file = NULL;
+  FILE *pfile = NULL;
+  static HyperDocPage *op = NULL;
+
+  if (op == page)
+    return;
+  op = page;
+  if (page == NULL)
+    return;
+  b = make_input_file_name(buf2, page->filename);
+  c = make_paste_file_name(buf3, page->filename);
+  if (inListAndNewer(b, page->filename)) {
+    /* open and prepare the input file */
+    file = fopen(b, "a");
+    if (file == NULL) {
+      fprintf(stderr, "couldn't open output file %s\n", b);
+      exit(-1);
+    }
+    fprintf(file, "\n-- Input for page %s\n", page->name);
+    fprintf(file, ")clear all\n\n");
+
+    for (node = page->scrolling; node != NULL; node = node->next)
+      if (node->type == Spadcommand || node->type == Spadgraph
+	  || node->type == Spadsrc) {
+	if (starting_file) {
+	  example_number = 1;
+	  if (make_patch_files) {
+	    send_lisp_command("(|clearCmdAll|)");
+	    send_lisp_command("(|resetWorkspaceVariables|)");
+	    send_lisp_command("(setq $linelength 55)");
+	    send_lisp_command("(|setOutputCharacters| '(default))");
+	    send_lisp_command("(setq |$printLoadMsgs| NIL)");
+	    send_lisp_command("(setq |$UserLevel| '|development|)");
+	    send_lisp_command("(verbos 0)");
+	  }
+	  if (make_patch_files) {
+	    pfile = fopen(c, "a");
+	    if (pfile == NULL) {
+	      fprintf(stderr, "couldn't open output file %s\n", c);
+	      exit(-1);
+	    }
+	  }
+	  starting_file = 0;
+	}
+	else
+	  example_number++;
+	buf = print_to_string(node->next);
+	com = alloc_string(buf);
+	fprintf(file, "%s\n", buf);
+	fflush(file);
+	fprintf(stderr, "writing:\t%s\n", buf);
+	include_bf = 1;
+	buf = print_to_string(node->next);
+	include_bf = 0;
+	if (make_patch_files) {
+	  if (node->type == Spadcommand || node->type == Spadsrc)
+	    print_paste(pfile, com, buf, page->name, node->type);
+	  else
+	    print_graph_paste(pfile, com, buf, page->name, node->type);
+	}
+      }
+    if (!starting_file && make_patch_files) {
+      ret_val = fclose(pfile);
+      if (ret_val == -1) {
+	fprintf(stderr, "couldn't close file %s\n", b);
+	exit(-1);
+      }
+    }
+    ret_val = fclose(file);
+    if (ret_val == -1) {
+      fprintf(stderr, "couldn't close file %s\n", b);
+      exit(-1);
+    }  
+  }
+}
+
+char *
+strCopy(char *s)
+{
+    char *b = halloc(strlen(s) + 1,"String");
+
+    strcpy(b, s);
+    return b;
+}
+
+static int
+inListAndNewer(char *inputFile, char *htFile)
+{
+    int ret_val, found = 0, i;
+    struct stat htBuf, inputBuf;
+
+    for (i = 0; i < num_active_files; i++) {
+        if (strcmp(active_file_list[i], inputFile) == 0) {
+            found = 1;
+            break;
+        }
+    }
+    if (found)
+        return 1;
+    found = 0;
+    for (i = 0; i < num_inactive_files; i++)
+        if (strcmp(inactive_file_list[i], inputFile) == 0) {
+            found = 1;
+            break;
+        }
+    if (found)
+        return 0;
+    found = 0;
+    for (i = 0; i < input_file_count; i++)
+        if (strcmp(input_file_list[i], inputFile) == 0) {
+            found = 1;
+            break;
+        }
+    if (!found) {
+        inactive_file_list[num_inactive_files++] = strCopy(inputFile);
+        return 0;
+    }
+    ret_val = stat(inputFile, &inputBuf);
+    if (ret_val == -1) {
+        active_file_list[num_active_files++] = input_file_list[i];
+        printf("making %s\n", inputFile);
+        return 1;
+    }
+    ret_val = stat(htFile, &htBuf);
+    if (ret_val == -1) {
+        inactive_file_list[num_inactive_files++] = strCopy(inputFile);
+        return 0;
+    }
+    ret_val = htBuf.st_mtime > inputBuf.st_mtime;
+    ret_val = 1;
+    if (ret_val) {
+        active_file_list[num_active_files++] = input_file_list[i];
+        printf("making %s\n", inputFile);
+        unlink(inputFile);
+    }
+    else
+        inactive_file_list[num_inactive_files++] = input_file_list[i];
+    return ret_val;
+}
+
+static void
+make_input_file_list(void)
+{
+    int i;
+    char buf[256], *name;
+
+    for (i = 0; i < input_file_count; i++) {
+        name = make_input_file_name(buf, input_file_list[i]);
+        input_file_list[i] = (char *) halloc(strlen(name) + 1,"Input Filename");
+        strcpy(input_file_list[i], name);
+    }
+}
+
+void
+print_paste_line(FILE *pfile,char *str)
+{
+    char *free = "\\free", *bound = "\\bound", *f = free, *b = bound;
+    int justSaw = 0;
+
+    for (; *str; str++) {
+        if (*f == '\0')
+            justSaw = 2;
+        if (*b == '\0')
+            justSaw = 2;
+        if (*b == *str)
+            b++;
+        else
+            b = bound;
+        if (*f == *str)
+            f++;
+        else
+            f = free;
+        if (*str == '%' || *str == '{' || *str == '}' || *str == '#') {
+            if (*str == '{' && justSaw)
+                justSaw--;
+            else if (*str == '}' && justSaw)
+                justSaw--;
+            else
+                putc('\\', pfile);
+        }
+        putc(*str, pfile);
+    }
+}
+
+
+
+void
+get_spad_output(FILE *pfile,char *command,int com_type)
+{
+    int n, i;
+    char buf[1024];
+
+    send_command(command, com_type);
+    n = get_int(spad_socket);
+    for (i = 0; i < n; i++) {
+        get_string_buf(spad_socket, buf, 1024);
+        fprintf(pfile, "%s\n", buf);
+    }
+    unescape_string(command);
+}
+
+/*
+ * THEMOS says: There is a problem here in that we issue the (|close|) and
+ * then go on. If this is the last command ,we will soon send a SIGTERM and
+ * the whole thing will collapse maybe BEFORE the writing out has finished.
+ * Fix: Call a Lisp function that checks (with \axiomOp{key} ps and grep) the
+ * health of the viewport. We do this after the (|close|).
+ */
+void
+get_graph_output(char *command,char *pagename,int com_type)
+{
+    int n, i;
+    char buf[1024];
+
+    send_command(command, com_type);
+    n = get_int(spad_socket);
+    for (i = 0; i < n; i++) {
+        get_string_buf(spad_socket, buf, 1024);
+    }
+    unescape_string(command);
+    sprintf(buf, "(|processInteractive| '(|write| |%s| \"%s%d\" \"image\") NIL)", "%",
+            pagename, example_number);
+    send_lisp_command(buf);
+    send_lisp_command("(|setViewportProcess|)");
+    send_lisp_command("(|processInteractive| '(|close| (|%%| -3)) NIL)");
+    send_lisp_command("(|waitForViewport|)");
+    get_int(spad_socket);
+}
+static void
+send_command(char *command,int com_type)
+{
+    char buf[1024];
+
+    if (com_type != Spadsrc) {
+        escape_string(command);
+        sprintf(buf, "(|parseAndEvalToHypertex| '\"%s\")", command);
+        send_lisp_command(buf);
+    }
+    else {
+        FILE *f;
+        char name[512], str[512]/*, *c*/;
+
+        sprintf(name, "/tmp/hyper%s.input", getenv("SPADNUM"));
+        f = fopen(name, "w");
+        if (f == NULL) {
+            fprintf(stderr, "Can't open temporary input file %s\n", name);
+            return;
+        }
+        fprintf(f, "%s", command);
+        fclose(f);
+        sprintf(str, "(|parseAndEvalToHypertex| '\")read %s\")", name);
+        send_lisp_command(str);
+    }
+}
+
+static void
+print_paste(FILE *pfile,char *realcom,char *command,
+            char *pagename,int com_type)
+{
+    fprintf(pfile, "\\begin{patch}{%sPatch%d}\n", pagename, example_number);
+    fprintf(pfile, "\\begin{paste}{%sFull%d}{%sEmpty%d}\n",
+            pagename, example_number, pagename, example_number);
+    fprintf(pfile, "\\pastebutton{%sFull%d}{\\hidepaste}\n",
+            pagename, example_number);
+    fprintf(pfile, "\\tab{5}\\spadcommand{");
+    print_paste_line(pfile, command);
+    fprintf(pfile, "}\n");
+    fprintf(pfile, "\\indentrel{3}\\begin{verbatim}\n");
+    get_spad_output(pfile, realcom, com_type);
+    fprintf(pfile, "\\end{verbatim}\n");
+    fprintf(pfile, "\\indentrel{-3}\\end{paste}\\end{patch}\n\n");
+
+    fprintf(pfile, "\\begin{patch}{%sEmpty%d}\n", pagename, example_number);
+    fprintf(pfile, "\\begin{paste}{%sEmpty%d}{%sPatch%d}\n",
+            pagename, example_number, pagename, example_number);
+    fprintf(pfile, "\\pastebutton{%sEmpty%d}{\\showpaste}\n",
+            pagename, example_number);
+    fprintf(pfile, "\\tab{5}\\spadcommand{");
+    print_paste_line(pfile, command);
+    fprintf(pfile, "}\n");
+    fprintf(pfile, "\\end{paste}\\end{patch}\n\n");
+    fflush(pfile);
+}
+static void
+print_graph_paste(FILE *pfile,char *realcom,
+                  char *command,char *pagename,int com_type)
+{
+    fprintf(pfile, "\\begin{patch}{%sPatch%d}\n", pagename, example_number);
+    fprintf(pfile, "\\begin{paste}{%sFull%d}{%sEmpty%d}\n",
+            pagename, example_number, pagename, example_number);
+    fprintf(pfile, "\\pastebutton{%sFull%d}{\\hidepaste}\n",
+            pagename, example_number);
+    fprintf(pfile, "\\tab{5}\\spadgraph{");
+    print_paste_line(pfile, command);
+    fprintf(pfile, "}\n");
+    fprintf(pfile, "\\center{\\unixcommand{\\inputimage{\\env{AXIOM}");
+    fprintf(pfile, "/doc/viewports/%s%d.view/image}}",
+                   pagename,example_number);
+    fprintf(pfile, "{viewalone\\space{1} \\env{AXIOM}");
+    fprintf(pfile,"/doc/viewports/%s%d}}\n", pagename, example_number);
+    get_graph_output(realcom, pagename, com_type);
+    fprintf(pfile, "\\end{paste}\\end{patch}\n\n");
+
+    fprintf(pfile, "\\begin{patch}{%sEmpty%d}\n", pagename, example_number);
+    fprintf(pfile, "\\begin{paste}{%sEmpty%d}{%sPatch%d}\n",
+            pagename, example_number, pagename, example_number);
+    fprintf(pfile, "\\pastebutton{%sEmpty%d}{\\showpaste}\n",
+            pagename, example_number);
+    fprintf(pfile, "\\tab{5}\\spadgraph{");
+    print_paste_line(pfile, command);
+    fprintf(pfile, "}\n");
+    fprintf(pfile, "\\end{paste}\\end{patch}\n\n");
+    fflush(pfile);
+}
+@
 \section{hyper.h}
 The [[hypertex]] function, of which this is the top level, is a browser
 for Axiom information. It works off a database of pages. The pages are
@@ -219,7 +8081,6 @@ default. This can be over-ridden by setting the [[HTPATH]] shell
 variable to point to the desired directory containing the pages and
 the ht.db file.
 <<hyper.h>>=
-<<license>>
 #ifndef _HYPER_H_
 #define _HYPER_H_ 1
 
@@ -233,7 +8094,7 @@ the ht.db file.
 #include <X11/Xos.h>
 
 #include "com.h"
-#include "token.h"
+<<token.h>>
 #include "hash.h"
 
 #define boolean unsigned short int
@@ -775,7 +8636,7 @@ typedef struct parameter_list_type {
 #include "debug.h"
 
 
-#include "hyper.h"
+<<hyper.h>>
 
 #include <sys/signal.h>
 #include <sys/types.h>
@@ -783,10 +8644,10 @@ typedef struct parameter_list_type {
 #include <setjmp.h>
 #include <X11/cursorfont.h>
 
-#include "keyin.h"
-#include "initx.h"
-#include "event.h"
-#include "parse-aux.h"
+<<keyin.h>>
+<<initx.h>>
+<<event.h>>
+<<parse-aux.h>>
 #include "bsdsignal.h"
 
 #include "all-hyper-proto.h1"
@@ -1133,12 +8994,15 @@ make_server_connections(void)
      */
 
     if (open_server(MenuServerName) == -2) {
-        fprintf(stderr, "(HyperDoc) Warning: Not connected to AXIOM Server!\n");
-        MenuServerOpened = 0;
+       fprintf(stderr, "(HyperDoc) Warning: Not connected to AXIOM Server!\n");
+       MenuServerOpened = 0;
+    }
+    else {
+     /* In order to allow hyperdoc restarts from the console we clean up
+      * the socket on exit */
+       atexit(&clean_socket);
+       MenuServerOpened = 1;
     }
-    else
-        MenuServerOpened = 1;
-
 
     /*
      * If I have opened the MenuServer socket, then I should also try to open
@@ -1213,46 +9077,11070 @@ make_server_connections(void)
     }
 }
 @
-\section{License}
-<<license>>=
+\section{initx.h}
+<<initx.h>>=
+#ifndef _INITX_H_
+#define _INITX_H_ 1
+
+<<hyper.h>>
+extern int gBorderColor;
+
+#endif
+@
+\section{initx.c}
+<<initx.c>>=
+/****************************************************************
+ *
+ * initx.h:  HyperDoc X Window window initialization code
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+
+/* #define DEBUG  1 */
+
+#define _INITX_C
+#include "debug.h"
+
+<<initx.h>>
+
+#include <unistd.h>
+#include <sys/signal.h>
+#include <setjmp.h>
+#include <X11/cursorfont.h>
+#include <X11/Xresource.h>
+#include <X11/Xatom.h>
+
+#ifdef SUN4OS5platform
+extern int gethostname(char *, int );
+#endif
+
+#define ht_icon_width 40
+#define ht_icon_height 40
+#define ht_icon_x_hot -1
+#define ht_icon_y_hot -1
+static char ht_icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00,
+   0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xef, 0x7b, 0x3c, 0xe7, 0xff,
+   0xef, 0x7f, 0x7e, 0xff, 0xff, 0xe7, 0xef, 0xe7, 0xfe, 0xe7, 0x6e, 0xe7,
+   0xe7, 0xde, 0xe7, 0x7e, 0xe7, 0xff, 0x0e, 0xe7, 0x3c, 0xe7, 0x07, 0x0e,
+   0xe7, 0x3c, 0xf7, 0xcf, 0x0e, 0xf7, 0x18, 0x7f, 0xfe, 0x1f, 0x00, 0x1c,
+   0x3f, 0x7c, 0x1f, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x00,
+   0x00, 0x00, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00,
+   0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x77,
+   0x00, 0x00, 0x00, 0x00, 0x77, 0x3e, 0xdc, 0x00, 0x00, 0x77, 0x7f, 0xfe,
+   0x00, 0x00, 0xf7, 0xe3, 0xef, 0x00, 0x00, 0xf7, 0xe3, 0xc7, 0x00, 0x00,
+   0xf7, 0xe3, 0x07, 0x00, 0x00, 0xf7, 0xe3, 0x07, 0x00, 0x00, 0xf7, 0xe3,
+   0xcf, 0x00, 0x80, 0x7f, 0x7f, 0xfe, 0x00, 0x80, 0x3f, 0x3e, 0x7c, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+<<extent.h>>
+<<group.h>>
+<<mem.h>>
+<<scrollbar.h>>
+<<titlebar.h>>
+
+#include "all-hyper-proto.h1"
+#include "util.h1"
+
+#include "spadcolors.h"
+#include "spadcolors.h1"
+
+
+#define mouseBitmap_width 16
+#define mouseBitmap_height 16
+#define mouseBitmap_x_hot 8
+#define mouseBitmap_y_hot 0
+static char mouseBitmap_bits[] = {
+   0x00, 0x01, 0x00, 0x01, 0x80, 0x02, 0x40, 0x04, 0xc0, 0x06, 0x20, 0x08,
+   0x20, 0x08, 0x30, 0x18, 0x50, 0x14, 0x58, 0x34, 0x90, 0x12, 0x20, 0x08,
+   0xc0, 0x47, 0x00, 0x21, 0x80, 0x10, 0x00, 0x0f};
+#define mouseMask_width 16
+#define mouseMask_height 16
+static char mouseMask_bits[] = {
+   0x00, 0x01, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xc0, 0x07, 0xe0, 0x0f,
+   0xe0, 0x0f, 0xf0, 0x1f, 0xf0, 0x1f, 0xf8, 0x3f, 0xf0, 0x1f, 0xe0, 0x0f,
+   0xc0, 0x47, 0x00, 0x21, 0x80, 0x10, 0x00, 0x0f};
+
+static GContext server_font;
+unsigned long *spadColors;
+int scrn;  /* used in spad_colors */
+
+extern int received_window_request;     /* true iff Spad wants a pop-up */
+extern int in_next_event;       /* true when in XNextEvent      */
+
+extern int gTtFontIs850;
+
+#define MIN_WINDOW_SIZE 300
+
+
+int gActiveColor,
+    gAxiomColor,
+    gBackgroundColor,
+    gBfColor,
+    gControlBackgroundColor,
+    gControlForegroundColor,
+    gEmColor,
+    gInputBackgroundColor,
+    gInputForegroundColor,
+    gItColor,
+    gRmColor,
+    gSlColor,
+    gTtColor;
+
+XFontStruct *gAxiomFont,
+            *gActiveFont,
+            *gBfFont,
+            *gEmFont,
+            *gInputFont,
+            *gItFont,
+            *gRmFont,
+            *gSlFont,
+            *gTitleFont,
+            *gTtFont;
+
+XrmDatabase rDB;
+int gBorderColor;     /* The Border Color */
+
+/* Initialize the X Window System  */
+
+void
+initializeWindowSystem(void)
+{
+    char *display_name = NULL;
+    XColor fg, bg;
+#if 0 
+    XColor rgbdef;
+#endif
+    Colormap cmap;
+    Pixmap  mousebits, mousemask;
+/*    fprintf(stderr,"initx:initializeWindowSystem:entered\n");*/
+    /* Try to open the display */
+/*    fprintf(stderr,"initx:initializeWindowSystem:XOpenDisplay\n");*/
+    if ((gXDisplay = XOpenDisplay(display_name)) == NULL) {
+        fprintf(stderr, "(HyperDoc) Cannot connect to the X11 server!\n");
+        exit(-1);
+    }
+
+    /* Get the screen */
+/*    fprintf(stderr,"initx:initializeWindowSystem:DefaultScreen\n");*/
+    gXScreenNumber = scrn = DefaultScreen(gXDisplay);
+/*    fprintf(stderr,"initx:initializeWindowSystem:XGContextFromGC\n");*/
+    server_font =XGContextFromGC(DefaultGC(gXDisplay, gXScreenNumber));
+
+    /* Get the cursors we need. */
+
+/*    fprintf(stderr,"initx:initializeWindowSystem:DefaultColormap\n");*/
+    cmap = DefaultColormap(gXDisplay, gXScreenNumber);
+/*    fprintf(stderr,"initx:initializeWindowSystem:WhitePixel\n");*/
+    fg.pixel = WhitePixel(gXDisplay,gXScreenNumber);
+/*    fprintf(stderr,"initx:initializeWindowSystem:XQueryColor\n");*/
+    XQueryColor(gXDisplay, cmap, &fg );
+/*    fprintf(stderr,"initx:initializeWindowSystem:BlackPixel\n");*/
+    bg.pixel = BlackPixel(gXDisplay,gXScreenNumber);
+/*    fprintf(stderr,"initx:initializeWindowSystem:XQueryColor2\n");*/
+    XQueryColor(gXDisplay, cmap, &bg );
+#if 0 
+    XAllocNamedColor(gXDisplay, cmap, "Black", &fg, &rgbdef);
+    XAllocNamedColor(gXDisplay, cmap, "White", &bg, &rgbdef);
+#endif
+
+#ifdef USE_BORING_OLD_CURSORS
+    gActiveCursor = XCreateFontCursor(gXDisplay, XC_circle);
+    gNormalCursor = XCreateFontCursor(gXDisplay, XC_dot);
+#else
+/*  fprintf(stderr,"initx:initializeWindowSystem:XCreateBitmapFromData 1\n");*/
+    mousebits = XCreateBitmapFromData(gXDisplay,
+        RootWindow(gXDisplay, gXScreenNumber),
+        mouseBitmap_bits, mouseBitmap_width,mouseBitmap_height);
+/* fprintf(stderr,"initx:initializeWindowSystem:XCreateBitmapFromData 2\n");*/
+    mousemask = XCreateBitmapFromData(gXDisplay,
+        RootWindow(gXDisplay, gXScreenNumber),
+        mouseMask_bits, mouseMask_width,mouseMask_height);
+/* fprintf(stderr,"initx:initializeWindowSystem:XCreateBitmapFromData 2\n");*/
+    gActiveCursor = XCreatePixmapCursor(gXDisplay,
+        mousebits, mousemask, &fg, &bg,
+        mouseBitmap_x_hot,mouseBitmap_y_hot);
+
+/*    fprintf(stderr,"initx:initializeWindowSystem:XCreateFontCursor\n");*/
+    gNormalCursor = XCreateFontCursor(gXDisplay, XC_left_ptr);
+#endif
+
+/*    fprintf(stderr,"initx:initializeWindowSystem:XCreateFontCursor 2\n");*/
+    gBusyCursor = XCreateFontCursor(gXDisplay, XC_watch);
+
+    /* Now initialize all the colors and fonts */
+
+/*    fprintf(stderr,"initx:initializeWindowSystem:ingItColors_and_fonts\n");*/
+    ingItColors_and_fonts();
+/*    fprintf(stderr,"initx:initializeWindowSystem:init_text\n");*/
+    init_text();
+/*    fprintf(stderr,"initx:initializeWindowSystem:exited\n");*/
+
+}
+
 /*
-Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
-All rights reserved.
+ * This routine is responsible for initializing a HyperDoc Window. At this
+ * point, all the fonts have been loaded, and X has been initialized. All I
+ * need worry about is starting up the window, and creating some of its
+ * children.
+ */
 
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
 
-    - Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
+/*
+ * init_top_window tries to start up a window with the page name. If the
+ * page name is NULL,
+ * it doesn't try to find it in the Hash Table, but rather just allocates a
+ * page of no name
+ */
 
-    - Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in
-      the documentation and/or other materials provided with the
-      distribution.
+int
+init_top_window(char *name)
+{
+    HyperDocPage *page;
+    XSetWindowAttributes wa;    /* The X attributes structure */
+    HDWindow *old_win = gWindow;
 
-    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
-      names of its contributors may be used to endorse or promote products
-      derived from this software without specific prior written permission.
+    gWindow = alloc_hd_window();
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+    if (name == NULL) {
+        /** Then allocate an empty page, and assign it to gWindow->page */
+        page = alloc_page((char *) NULL);
+    }
+    else {
+        /* Try to find the page in the page hash table */
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, name);
+        if (page == NULL) {
+            fprintf(stderr, "(HyperDoc)  Couldn\'t find page %s in page hash table \n",
+                    name);
+            if (gParentWindow == NULL)
+                /* Gaak, This is a start up error */
+                exit(-1);
+            else {
+                gWindow = old_win;
+                return -1;
+            }
+        }
+    }
+
+    /* First allocate memory for the new window structure   */
+    gWindow->page = page;
+
+    if (old_win == NULL)
+        open_window(0);
+    else
+        open_window(old_win->fMainWindow);
+
+    get_GCs(gWindow);
+    XMapWindow(gXDisplay, gWindow->fMainWindow);
+    hash_insert(&gSessionHashTable, (char *)gWindow,(char *) &gWindow->fMainWindow);
+
+    change_text(gRmColor, gRmFont);
+    wa.background_pixel = gBackgroundColor;
+    XChangeWindowAttributes(gXDisplay, gWindow->fMainWindow, CWBackPixel, &wa);
+    XChangeWindowAttributes(gXDisplay, gWindow->fScrollWindow, CWBackPixel,&wa);
+    return 1;
+}
+
+/* Create and initialize a form HyperDoc window */
+
+static void
+open_form_window(void)
+{
+    int x, y, width, height;
+    unsigned int fwidth = 0, fheight = 0;
+    unsigned int xadder = 0, yadder = 0;
+    /*char *window_name = "HyperDoc";*/
+    /*char *icon_name = "HT";*/
+    XrmValue value;
+    char *str_type[50];
+    XSizeHints size_hints;
+    int userSpecified = 0;
+
+    char userdefaults[50], progdefaults[50];
+
+    strcpy(progdefaults, "=950x450+0+0");
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.FormGeometry",
+        "Axiom.hyperdoc.FormGeometry", str_type, &value) == True)
+    {
+        strncpy(userdefaults, value.addr, (int) value.size);
+        userSpecified = 1;
+    }
+    else
+        strcpy(userdefaults, progdefaults);
+
+    XGeometry(gXDisplay, gXScreenNumber, userdefaults, progdefaults,
+              0, fwidth, fheight, xadder, yadder,
+              &x, &y, &width, &height);
+
+    gWindow->border_width = get_border_properties();
+
+    gWindow->width = 1;
+    gWindow->height = 1;
+
+    gWindow->fMainWindow = XCreateSimpleWindow(gXDisplay, RootWindow(gXDisplay, gXScreenNumber),
+                                    x, y, width, height, gWindow->border_width,
+                                    gBorderColor,
+                                    WhitePixel(gXDisplay, gXScreenNumber));
+    gWindow->fScrollWindow = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                         1, 1, 1, 1, 0,
+                                         BlackPixel(gXDisplay, gXScreenNumber),
+                                         WhitePixel(gXDisplay, gXScreenNumber));
+    makeScrollBarWindows();
+    makeTitleBarWindows();
+
+    set_name_and_icon();
+
+    XSelectInput(gXDisplay, gWindow->fScrollWindow, PointerMotionMask);
+    XSelectInput(gXDisplay, gWindow->fMainWindow, StructureNotifyMask | PointerMotionMask);
+    XDefineCursor(gXDisplay, gWindow->fMainWindow, gNormalCursor);
+
+    /* now give the window manager some hints */
+
+    size_hints.flags = 0;
+
+    size_hints.min_width  = width;
+    size_hints.min_height = height;
+    size_hints.flags |= PMinSize;
+
+    size_hints.width  = width;
+    size_hints.height = height;
+    size_hints.flags |= (userSpecified ? USSize : PSize);
+
+    size_hints.x = x;
+    size_hints.y = y;
+    size_hints.flags |= (userSpecified ? USPosition : PPosition);
+
+    XSetNormalHints(gXDisplay, gWindow->fMainWindow, &size_hints);
+    XFlush(gXDisplay);
+}
+
+
+int
+init_form_window(char *name, int cols)
+{
+    XSetWindowAttributes wa;    /* The X attributes structure */
+
+    /* First allocate memory for the new window structure   */
+
+    gWindow = alloc_hd_window();
+    open_form_window();
+    gWindow->width = window_width(cols);
+
+    if (name == NULL) {
+        /** Then allocate an empty page, and assign it to gWindow->page */
+        gWindow->page = alloc_page((char *) NULL);
+    }
+    else {
+        /* Try to find the page in the page hash table */
+        gWindow->page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, name);
+        if (gWindow->page == NULL) {
+            fprintf(stderr, "Couldn't find page %s\n", name);
+            return (-1);
+        }
+    }
+
+    get_GCs(gWindow);
+    hash_insert(&gSessionHashTable, (char *)gWindow,(char *) &gWindow->fMainWindow);
+
+    wa.background_pixel = gBackgroundColor;
+    XChangeWindowAttributes(gXDisplay, gWindow->fMainWindow, CWBackPixel, &wa);
+    XChangeWindowAttributes(gXDisplay, gWindow->fScrollWindow, CWBackPixel,&wa);
+    return 1;
+}
+
+
+static void
+set_name_and_icon(void)
+{
+    char *icon_name = "HyperDoc";
+    char *s;
+    Pixmap icon_pixmap;
+    XWMHints wmhints;
+    XClassHint ch;
+
+    ch.res_name = "HyperDoc";
+    ch.res_class = gArgv[0];
+    for (s = gArgv[0] + strlen(gArgv[0]) - 1; s != gArgv[0]; s--) {
+        if (*s == '/') {
+            ch.res_class = s + 1;
+            break;
+        }
+    }
+    XSetClassHint(gXDisplay, gWindow->fMainWindow, &ch);
+
+    XStoreName(gXDisplay, gWindow->fMainWindow, "HyperDoc");
+
+    /* define and assign the pixmap for the icon */
+    icon_pixmap = XCreateBitmapFromData(gXDisplay, gWindow->fMainWindow, ht_icon_bits,
+                                        ht_icon_width, ht_icon_height);
+    wmhints.icon_pixmap = icon_pixmap;
+    wmhints.flags = IconPixmapHint;
+
+    XSetWMHints(gXDisplay, gWindow->fMainWindow, &wmhints);
+
+    /* name the icon */
+    XSetIconName(gXDisplay, gWindow->fMainWindow, icon_name);
+}
+
+static int
+get_border_properties(void)
+{
+    char *bwidth;
+    /*char *bc = NULL;*/
+    int bw;
+    /*XColor color_def, color_db;*/
+    Colormap cmap;
+    /*int ret_val;*/
+
+
+    bwidth = "2";  /* XGetDefault(gXDisplay, "Axiom.hyperdoc", "BorderWidth") */
+
+    if (bwidth == NULL)
+        bw = 1;
+    else {
+        bw = atoi(bwidth);
+        if (bw < 1) {
+            fprintf(stderr,
+                    "%s: The line width value must be greater than zero\n", "Axiom.hyperdoc");
+            bw = 1;
+        }
+    }
+
+    /* Now try to find the user preferred border color */
+
+    if (DisplayPlanes(gXDisplay, gXScreenNumber) == 1)
+        gBorderColor = BlackPixel(gXDisplay, gXScreenNumber);
+    else {
+        cmap = DefaultColormap(gXDisplay, gXScreenNumber);
+        gBorderColor = get_color("BorderColor", "Foreground",
+            BlackPixel(gXDisplay, gXScreenNumber), &cmap);
+    }
+    return bw;
+}
+
+
+/* Create and initialize the HyperDoc window */
+
+static void
+open_window(Window w)
+{
+    int x = 0, y = 0;
+    /*int border_width = 2;*/
+    unsigned int width = 1;
+    unsigned int height = 1;
+    unsigned int fwidth = 0, fheight = 0;
+    unsigned int xadder = 0, yadder = 0;
+    char *str_type[50];
+    XrmValue value;
+
+    char userdefaults[50], progdefaults[50];
+
+    strcpy(progdefaults, "=700x450+0+0");
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.Geometry",
+        "Axiom.hyperdoc.Geometry", str_type, &value) == True)
+    {
+        strncpy(userdefaults, value.addr, (int) value.size);
+    }
+    else
+        strcpy(userdefaults, progdefaults);
+
+    XGeometry(gXDisplay, gXScreenNumber, userdefaults, progdefaults,
+              0, fwidth, fheight, xadder, yadder,
+              &x, &y, ( int *)&width,( int *) &height);
+
+    gWindow->border_width = get_border_properties();
+
+    gWindow->fMainWindow = XCreateSimpleWindow(gXDisplay, RootWindow(gXDisplay, gXScreenNumber),
+                                    x, y, width, height, gWindow->border_width,
+                                    gBorderColor,
+                                    WhitePixel(gXDisplay, gXScreenNumber));
+
+    gWindow->fScrollWindow = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                         1, 1, 1, 1, 0,
+                                         gBorderColor,
+                                         WhitePixel(gXDisplay, gXScreenNumber));
+
+
+    makeScrollBarWindows();
+    makeTitleBarWindows();
+
+    /* Now set all the little properties for the top level window */
+
+    set_name_and_icon();
+    set_size_hints(w);
+    XSelectInput(gXDisplay, gWindow->fScrollWindow, PointerMotionMask);
+    XSelectInput(gXDisplay, gWindow->fMainWindow, StructureNotifyMask | PointerMotionMask);
+    XDefineCursor(gXDisplay, gWindow->fMainWindow, gNormalCursor);
+}
+
+/***
+  This routine gets and sets the size for a new window. If the w paramter
+  is null, it means that this is the initial window. Thus the user
+  preferences are checked. If this is not the first window, then the
+  window w is used as a guidline, and the new window is placed on top of
+  it.
+  ***/
+
+static void
+set_size_hints(Window w)
+{
+    int x, y;
+    unsigned int width, height;
+    char userdefaults[50];
+    char progdefaults[50];
+    char *str_type[50];
+    unsigned int fwidth = 0, fheight = 0;
+    unsigned int xadder = 0, yadder = 0;
+    int geo = 0;                /* return flag from XGetGeometry */
+    unsigned int depth, bw=0;
+    Window root;
+    XSizeHints size_hints;
+    XPoint xp;
+    XrmValue value;
+
+    size_hints.flags = 0;
+
+    strcpy(progdefaults, "=600x450+0+0");
+
+    if (w) {
+        /*
+         * The window should be queried for it's size and position. Then the
+         * new window should be given almost the same locations
+         */
+
+        if (XGetGeometry(gXDisplay, w, &root, &x, &y, &width, &height, &bw, &depth))
+        {
+            xp = getWindowPositionXY(gXDisplay, w);
+            x = xp.x + 40;
+            y = xp.y + 40;
+            if (x < 0)
+                x = 0;
+            if (y < 0)
+                y = 0;
+            size_hints.flags |= (USSize | USPosition);
+        }
+        else {
+            fprintf(stderr, "(HyperDoc) Error Querying window configuration: %ld.\n", w);
+            x = y = 0;
+            width = 600;
+            height = 450;
+            size_hints.flags |= (PSize | PPosition);
+        }
+    }
+    else {
+        /* this is the first window, so lets try to find a nice spot for it */
+
+        if (XrmGetResource(rDB, "Axiom.hyperdoc.Geometry", "Axiom.hyperdoc.Geometry",
+            str_type, &value) == True)
+        {
+            strncpy(userdefaults, value.addr, (int) value.size);
+            geo = XParseGeometry(userdefaults, &x, &y, &width, &height);
+        }
+        else
+            strcpy(userdefaults, progdefaults);
+
+        size_hints.flags |= (geo & (WidthValue | HeightValue)) ? USSize : PSize;
+        size_hints.flags |= (geo & (XValue | YValue)) ? USPosition : PPosition;
+
+        geo = XGeometry(gXDisplay, gXScreenNumber, userdefaults, progdefaults,
+                        bw, fwidth, fheight, xadder, yadder,
+                        &x, &y, (int *)&width, (int *)&height);
+    }
+
+    size_hints.x = x;
+    size_hints.y = y;
+    size_hints.width = width;
+    size_hints.height = height;
+
+    getTitleBarMinimumSize(&(size_hints.min_width), &(size_hints.min_height));
+#if 0
+    size_hints.min_width  = MIN_WINDOW_SIZE;
+    size_hints.min_height = MIN_WINDOW_SIZE;
+#endif
+    size_hints.flags |= PMinSize;
+
+    XSetNormalHints(gXDisplay, gWindow->fMainWindow, &size_hints);
+    /* just in case a hint isn't enough ... */
+    XFlush(gXDisplay);
+/*  XMoveResizeWindow(gXDisplay, gWindow->fMainWindow, x, y, width, height); */
+}
+
+#define stipple_width 4
+#define stipple_height 4
+static char stipple_bits[] = {
+                              0xff, 0xff, 0xff, 0xff};
+Pixmap stipple;
+
+/* Create the graphics contexts to be used for all drawing operations */
+
+static void
+get_GCs(HDWindow *window)
+{
+    /*unsigned long valuemask = 0;*/
+    XGCValues values;
+
+    values.background = gBackgroundColor;
+    window->fStandardGC = XCreateGC(gXDisplay, window->fMainWindow, GCBackground, &values);
+
+    XSetLineAttributes(gXDisplay, window->fStandardGC, window->border_width,
+                       LineSolid, CapButt, JoinMiter);
+
+
+    /* create the stipple for the gc */
+
+    stipple = XCreateBitmapFromData(gXDisplay,
+        RootWindow(gXDisplay, gXScreenNumber),
+        stipple_bits, stipple_width, stipple_height);
+
+    values.background = gInputBackgroundColor;
+    values.foreground = gInputForegroundColor;
+
+    values.font = gInputFont->fid;
+
+    if (values.font == server_font )
+        window->fInputGC = XCreateGC(gXDisplay, window->fMainWindow,
+            GCBackground | GCForeground, &values);
+    else {
+        window->fInputGC  = XCreateGC(gXDisplay, window->fMainWindow,
+            GCBackground | GCForeground | GCFont, &values);
+    }
+
+    window->fCursorGC = XCreateGC(gXDisplay, window->fMainWindow, 0, NULL);
+
+    if (values.font != server_font)
+        XSetFont(gXDisplay,   window->fCursorGC, gInputFont->fid);
+
+    XSetBackground(gXDisplay, window->fCursorGC, gInputForegroundColor);
+    XSetForeground(gXDisplay, window->fCursorGC, gInputBackgroundColor);
+
+    window->fControlGC = XCreateGC(gXDisplay, window->fMainWindow, 0, NULL);
+    XSetBackground(gXDisplay, window->fControlGC, gControlBackgroundColor);
+    XSetForeground(gXDisplay, window->fControlGC, gControlForegroundColor);
+}
+
+/* Load a font and store the information in the font_info parameter */
+
+static void
+load_font(XFontStruct **font_info, char *fontname)
+{
+   if ((*font_info = XLoadQueryFont(gXDisplay, fontname)) == NULL) {
+        fprintf(stderr, "(HyperDoc) Cannot load font %s ; using default.\n",
+            fontname);
+
+        if ((*font_info = XQueryFont(gXDisplay,
+               XGContextFromGC(DefaultGC(gXDisplay, gXScreenNumber)))) == NULL)
+        {
+            fprintf(stderr, "(HyperDoc) Cannot get default font ; exiting.\n");
+            exit(-1);
+        }
+   }
+}
+
+
+/*
+ * This routine initializes all the colors and fonts that the user wishes to
+ * use. It checks for all the following properties in $HOME/.Xdefaults.
+ *
+ *  Axiom.hyperdoc.ActiveColor:
+ *  Axiom.hyperdoc.Background:
+ *  Axiom.hyperdoc.EmphasizeColor:
+ *  Axiom.hyperdoc.EmphasizeFont:
+ *  Axiom.hyperdoc.Foreground:
+ *  Axiom.hyperdoc.InputBackground:
+ *  Axiom.hyperdoc.InputForeground:
+ *  Axiom.hyperdoc.SpadColor:
+ *  Axiom.hyperdoc.SpadFont:
+ */
+
+static void
+ingItColors_and_fonts(void)
+{
+    char property[256];
+    char *prop = &property[0];
+    char *str_type[50];
+    XrmValue value;
+    Colormap cmap;
+    int ts;
+
+    /** get the color map for the display **/
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:entered\n");*/
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:DefaultColorMap\n");*/
+    cmap = DefaultColormap(gXDisplay, gXScreenNumber);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:init_group_stack\n");*/
+    init_group_stack();
+
+
+    /** then start getting the fonts **/
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:mergeDatabases\n");*/
+    mergeDatabases();
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.RmFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True)
+        (void) strncpy(prop, value.addr, (int) value.size);
+    else
+        (void) strcpy(prop, RmFontDefault);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 1\n");*/
+    load_font(&gRmFont, prop);
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 2\n");*/
+    load_font(&gInputFont, prop);
+
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource 2\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.TtFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True)
+        (void) strncpy(prop, value.addr, (int) value.size);
+    else
+        (void) strcpy(prop, TtFontDefault);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 3\n");*/
+    load_font(&gTtFont, prop);
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:is_it_850\n");*/
+    gTtFontIs850=is_it_850(gTtFont);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource 5\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.ActiveFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True)
+        (void) strncpy(prop, value.addr, (int) value.size);
+    else
+        (void) strcpy(prop, ActiveFontDefault);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 4\n");*/
+    load_font(&gActiveFont, prop);
+
+    /* maintain backwards compatibility */
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource 6\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.AxiomFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True)
+        (void) strncpy(prop, value.addr, (int) value.size);
+    else {
+        if (XrmGetResource(rDB, "Axiom.hyperdoc.SpadFont", 
+                           "Axiom.hyperdoc.Font", str_type, &value) == True) 
+        {
+            (void) strncpy(prop, value.addr, (int) value.size);
+        }
+        else {
+            (void) strcpy(prop, AxiomFontDefault);
+        }
+    }
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 5\n");*/
+    load_font(&gAxiomFont, prop);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource 7\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.EmphasizeFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True) 
+    {
+        (void) strncpy(prop, value.addr, (int) value.size);
+    }
+    else {
+        (void) strcpy(prop, EmphasizeFontDefault);
+    }
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 6\n");*/
+    load_font(&gEmFont, prop);
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:XrmGetResource 8\n");*/
+    if (XrmGetResource(rDB, "Axiom.hyperdoc.BoldFont", 
+                            "Axiom.hyperdoc.Font", str_type, &value) == True) 
+    {
+        (void) strncpy(prop, value.addr, (int) value.size);
+    }
+    else {
+        (void) strcpy(prop, BoldFontDefault);
+    }
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:load_font 7\n");*/
+    load_font(&gBfFont, prop);
+
+
+    /*
+     * If we are on a monochrome screen, then we ignore user preferences, and
+     * set the foreground and background as I wish
+     */
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:DisplayPlanes\n");*/
+    if (DisplayPlanes(gXDisplay, gXScreenNumber) == 1) {
+        gActiveColor       = gAxiomColor
+                            = gControlBackgroundColor
+                            = gInputBackgroundColor
+                            = gBfColor
+                            = gEmColor
+                            = gRmColor
+                            = gSlColor
+                            = gTtColor
+                            = BlackPixel(gXDisplay, gXScreenNumber);
+
+        gBackgroundColor   = gInputForegroundColor
+                            = gControlForegroundColor
+                            = WhitePixel(gXDisplay, gXScreenNumber);
+    }
+    else {
+
+        /*
+         * If I have gotten here, then we must be on a color screen, so see
+         * what the user likes, and set it up
+         */
+
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 1\n");*/
+        gRmColor =
+            get_color("RmColor", "Foreground", 
+                      BlackPixel(gXDisplay, gXScreenNumber), &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 2\n");*/
+        gBackgroundColor =
+            get_color("Background", "Background", 
+                      WhitePixel(gXDisplay, gXScreenNumber), &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 3\n");*/
+        gActiveColor =
+            get_color("ActiveColor", "Foreground", 
+                       BlackPixel(gXDisplay, gXScreenNumber), &cmap);
+
+        /*
+         * for next two, I want name arg = class arg, ie do not want
+         * Background and Foreground.
+         */
+
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 4\n");*/
+        gControlBackgroundColor = get_color("ControlBackground",
+            "ControlBackground", WhitePixel(gXDisplay, gXScreenNumber), &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 5\n");*/
+        gControlForegroundColor = get_color("ControlForeground",
+            "ControlForeground", BlackPixel(gXDisplay, gXScreenNumber), &cmap);
+
+        /* maintain backwards compatibility */
+
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 6\n");*/
+        gAxiomColor = get_color("AxiomColor", "Foreground", 0, &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 7\n");*/
+        if (gAxiomColor == 0)
+            gAxiomColor = get_color("SpadColor", "Foreground",
+                BlackPixel(gXDisplay, gXScreenNumber), &cmap);
+
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 8\n");*/
+        gInputBackgroundColor =
+            get_color("InputBackground", "Foreground", gRmColor, &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 9\n");*/
+        gInputForegroundColor =
+           get_color("InputForeground", "Background", gBackgroundColor, &cmap);
+
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 10\n");*/
+        gEmColor =
+            get_color("EmphasizeColor", "Foreground", gRmColor, &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 11\n");*/
+        gTtColor =
+            get_color("TtColor", "Foreground", gRmColor, &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 12\n");*/
+        gSlColor =
+            get_color("EmphasizeColor", "Foreground", gRmColor, &cmap);
+/*        fprintf(stderr,"initx:ingItColors_and_fonts:get_color 13\n");*/
+        gBfColor =
+            get_color("BoldColor", "Foreground", gRmColor, &cmap);
+    }
+
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:makeColors\n");*/
+    makeColors(gXDisplay, gXScreenNumber, &cmap, &spadColors, &ts);
+    /*
+     * Now set the current color and font, so I never have to do it again
+     */
+
+    gTopOfGroupStack->cur_color = gRmColor;
+    gTopOfGroupStack->cur_font = gRmFont;
+/*    fprintf(stderr,"initx:ingItColors_and_fonts:exited\n");*/
+}
+
+void
+change_text(int color, XFontStruct *font)
+{
+    if (font) {
+        XGCValues gcv;
+        gcv.foreground = color;
+        gcv.background = gBackgroundColor;
+
+        XChangeGC(gXDisplay, gWindow->fStandardGC, GCForeground | GCBackground , &gcv);
+
+        if (font->fid != server_font)
+            XSetFont(gXDisplay, gWindow->fStandardGC, font->fid);
+    }
+}
+
+/*
+ * This routine checks the .Xdefaults file of the user for the
+ * specified color. If found it allocates a place in the color map for it. If
+ * not found, or if an error occurrs, it writes an error message, and
+ * uses the given default value
+ */
+
+static int
+get_color(char *name, char *class, int def, Colormap *map)
+{
+    char fullname[256];
+    char fullclass[256];
+    char property[256];
+    char *prop = &property[0];
+    char *str_type[50];
+    XrmValue value;
+    int ret_val;
+    XColor color_def, color_db;
+
+#ifdef DEBUG
+    printf("get_color: %s %s %d -> ", name, class, def);
+#endif
+
+    strcpy(fullname, "Axiom.hyperdoc.");
+    strcat(fullname, name);
+    strcpy(fullclass,"Axiom.hyperdoc.");
+    strcat(fullclass,class);
+
+    if (XrmGetResource(rDB, fullname, fullclass, str_type, &value) == True) {
+        (void) strncpy(prop, value.addr, (int) value.size);
+        ret_val = XAllocNamedColor(gXDisplay, *map, prop, &color_def, &color_db);
+        if (ret_val) {
+#ifdef DEBUG
+            printf("%d\n", color_def.pixel);
+#endif
+            return (color_def.pixel);
+        }
+        else {
+            fprintf(stderr,
+                "(HyperDoc) Defaulting on color for %s. Unknown color is %s.\n",
+                    name, prop);
+#ifdef DEBUG
+            printf("%d\n", def);
+#endif
+            return (def);
+        }
+    }
+    else {
+#ifdef DEBUG
+        printf("%d\n", def);
+#endif
+        return (def);
+    }
+}
+
+
+static void
+mergeDatabases(void)
+{
+    XrmDatabase homeDB, serverDB, applicationDB;
+    char filenamebuf[1024];
+    char *filename = &filenamebuf[0];
+    char *classname = "Axiom";
+    char name[255];
+
+/*    fprintf(stderr,"initx:mergeDatabases:entered\n");*/
+/*    fprintf(stderr,"initx:mergeDatabases:XrmInitialize\n");*/
+    (void) XrmInitialize();
+    (void) strcpy(name, "/usr/lib/X11/app-defaults/");
+    (void) strcat(name, classname);
+/*  fprintf(stderr,"initx:mergeDatabases:XrmGetFileDatabase name=%s\n",name);*/
+    applicationDB = XrmGetFileDatabase(name);
+/*    fprintf(stderr,"initx:mergeDatabases:XrmMergeDatabases\n");*/
+    (void) XrmMergeDatabases(applicationDB, &rDB);
+
+/*    fprintf(stderr,"initx:mergeDatabases:XrmGetStringDatabase\n");*/
+    if (XResourceManagerString(gXDisplay) != NULL) {
+        serverDB = XrmGetStringDatabase(XResourceManagerString(gXDisplay));
+    }
+    else {
+        (void) strcpy(filename, getenv("HOME"));
+        (void) strcat(filename, "/.Xdefaults");
+/*        fprintf(stderr,"initx:mergeDatabases:XrmGetFileDatase\n");*/
+        serverDB = XrmGetFileDatabase(filename);
+    }
+/*    fprintf(stderr,"initx:mergeDatabases:XrmMergeDatabases 2\n");*/
+    XrmMergeDatabases(serverDB, &rDB);
+    if (getenv("XENVIRONMENT") == NULL) {
+        int len;
+
+        (void) strcpy(filename, getenv("HOME"));
+        (void) strcat(filename, "/.Xdefaults-");
+        len = strlen(filename);
+        (void) gethostname(filename + len, 1024 - len);
+    }
+    else {
+        (void) strcpy(filename, getenv("XENVIRONMENT"));
+    }
+/*    fprintf(stderr,"initx:mergeDatabases:filename=%s\n",filename);*/
+    homeDB = XrmGetFileDatabase(filename);
+/*    fprintf(stderr,"initx:mergeDatabases:XrmMergeDatabases 3\n");*/
+    XrmMergeDatabases(homeDB, &rDB);
+}
+
+
+
+int 
+is_it_850(XFontStruct *fontarg)
+{
+ char *s;
+ int i,val;
+ static struct {
+      char *name;
+      Atom format;
+      Atom atom;
+      } proptbl = { "CHARSET_ENCODING", XA_ATOM };
+ proptbl.atom = XInternAtom(gXDisplay,proptbl.name,0);
+ for (i=0;i<fontarg->n_properties;i++)
+  {
+    if (fontarg->properties[i].name != proptbl.atom) continue; 
+
+
+/* return 1 if it is 850 */
+
+    s = XGetAtomName(gXDisplay,(Atom)fontarg->properties[i].card32);
+    val = !( strcmp("850",s) * strcmp("ibm-850",s));
+    XFree(s);
+    return( val );
+  }
+ return(0);
+}
+
+@
+\section{input.c}
+<<input.c>>=
+#define _INPUT_C
+#include "debug.h"
+
+<<hyper.h>>
+<<dialog.h>>
+<<mem.h>>
+
+#include "all-hyper-proto.h1"
+
+
+void
+fill_box(Window w,ImageStruct * image)
+{
+    XClearWindow(gXDisplay, w);
+    XPutImage(gXDisplay, w, gWindow->fControlGC,
+              image->image.xi, 0, 0, 0, 0,
+              image->width,
+              image->height);
+}
+
+void
+toggle_input_box(HyperLink *link)
+{
+    InputBox *box;
+
+    box = link->reference.box;
+
+    if (box->picked) {
+        box->picked = 0;
+        unpick_box(box);
+    }
+    else {
+        box->picked = 1;
+        pick_box(box);
+    }
+
+}
+void
+toggle_radio_box(HyperLink *link)
+{
+    InputBox *box;
+
+    box = link->reference.box;
+
+    if (box->picked) {
+
+        /*
+         * box->picked = 0; unpick_box(box);
+         */
+    }
+    else {
+        /* the first thing I do is clear his buddies */
+        clear_rbs(box->rbs->boxes);
+        box->picked = 1;
+        pick_box(box);
+    }
+}
+
+static void
+clear_rbs(InputBox *list)
+{
+    InputBox *trace = list;
+
+    while (trace && !trace->picked)
+        trace = trace->next;
+
+    if (trace != NULL) {
+        trace->picked = 0;
+        unpick_box(trace);
+    }
+}
+void
+change_input_focus(HyperLink *link)
+{
+    InputItem *new_item = link->reference.string;
+    InputItem *old_item = gWindow->page->current_item;
+    XWindowChanges wc;
+
+    /** first thing I should do is see if the user has clicked in the same
+      window that I am in                                         ****/
+    if (old_item == new_item)
+        return;
+
+    /**  Now change the current pointer **/
+    gWindow->page->current_item = new_item;
+
+    /** Now I have to change the border width of the selected input window **/
+    wc.border_width = 1;
+    XConfigureWindow(gXDisplay, new_item->win,
+                     CWBorderWidth,
+                     &wc);
+
+    wc.border_width = 0;
+    XConfigureWindow(gXDisplay, new_item->win,
+                     CWBorderWidth,
+                     &wc);
+    update_inputsymbol(old_item);
+    update_inputsymbol(new_item);
+}
+void
+next_input_focus(void)
+{
+    InputItem *old_item = gWindow->page->current_item, *new_item, *trace;
+
+    if (gWindow->page->current_item == NULL ||
+        (gWindow->page->current_item->next == NULL
+         && gWindow->page->current_item == gWindow->page->input_list)) {
+        BeepAtTheUser();
+        return;
+    }
+
+    /*
+     * Now I should  find the new item
+     */
+    new_item = NULL;
+    trace = old_item->next;
+
+    if (trace == NULL)
+        new_item = gWindow->page->input_list;
+    else
+        new_item = trace;
+
+    gWindow->page->current_item = new_item;
+    draw_inputsymbol(old_item);
+    draw_inputsymbol(new_item);
+}
+void
+prev_input_focus(void)
+{
+    InputItem *old_item = gWindow->page->current_item, *new_item, *trace;
+
+    if (gWindow->page->current_item == NULL) {
+        BeepAtTheUser();
+        return;
+    }
+
+    /*
+     * Now I should  find the new item
+     */
+    new_item = NULL;
+    trace = gWindow->page->input_list;
+
+    if (trace == old_item) {
+
+        /*
+         * I started at the front of the list, so move forward until I hit
+         * the end
+         */
+        while (trace->next != NULL)
+            trace = trace->next;
+        new_item = trace;
+    }
+    else {
+        while (trace->next != old_item)
+            trace = trace->next;
+        new_item = trace;
+    }
+
+    gWindow->page->current_item = new_item;
+    draw_inputsymbol(old_item);
+    draw_inputsymbol(new_item);
+
+}
+
+InputItem *
+return_item(char *name)
+{
+    InputItem *list;
+
+    list = gWindow->page->input_list;
+    while (list != NULL) {
+        if (!strcmp(name, list->name))
+            return list;
+        list = list->next;
+    }
+    return NULL;
+}
+int
+delete_item(char *name)
+{
+    InputItem *list;
+    InputItem *prev = NULL;
+
+    list = gWindow->page->input_list;
+    while (list != NULL) {
+        if (!strcmp(name, list->name)) {
+            if (prev)
+                prev->next = list->next;
+            else
+                gWindow->page->input_list = list->next;
+            if (gWindow->page->current_item == list)
+                gWindow->page->current_item = gWindow->page->input_list;
+            free_input_item(list, 1);
+            free(list);
+            return 1;
+        }
+        prev = list;
+        list = list->next;
+    }
+    fprintf(stderr, "Can't delete input item %s\n", name);
+    return 0;
+}
+
+@
+\section{item.c}
+<<item.c>>=
+#define _ITEM_C
+#include "debug.h"
+<<extent.h>>
+
+#include "all-hyper-proto.h1"
+
+/*
+ * Here are structures needed for manipulating the item stack
+ */
+ItemStack *gTopOfItemStack = NULL;
+
+
+void
+push_item_stack(void)
+{
+    ItemStack *is = (ItemStack *) halloc(sizeof(ItemStack), "Item stack");
+
+    is->indent = indent;
+    is->item_indent = item_indent;
+    is->next = gTopOfItemStack;
+    is->in_item = gInItem;
+    gTopOfItemStack = is;
+    return;
+}
+void
+clear_item_stack(void)
+{
+    ItemStack *is = gTopOfItemStack, *chuck;
+
+    while (is != NULL) {
+        chuck = is;
+        is = is->next;
+        free(chuck);
+    }
+    return;
+}
+void
+pop_item_stack(void)
+{
+    ItemStack *chuck;
+
+    if (gTopOfItemStack == NULL) {
+        fprintf(stderr, "Tried to pop an empty item stack\n");
+        return;
+    }
+    chuck = gTopOfItemStack;
+    gTopOfItemStack = gTopOfItemStack->next;
+    indent = chuck->indent;
+    item_indent = chuck->item_indent;
+    gInItem = chuck->in_item;
+    free(chuck);
+}
+
+ItemStack *
+copy_item_stack(void)
+{
+    ItemStack *new = NULL;
+    ItemStack *prev = NULL;
+    ItemStack *trace = gTopOfItemStack;
+    ItemStack *first = NULL;
+
+    while (trace) {
+        new = (ItemStack *) halloc(sizeof(ItemStack), "Item stack");
+        new->indent = trace->indent;
+        new->item_indent = trace->item_indent;
+        new->in_item = gInItem;
+        if (!first)
+            first = new;
+        else
+            prev->next = new;
+        prev = new;
+        trace = trace->next;
+    }
+    if (new)
+        new->next = NULL;
+    return first;
+}
+
+void
+free_item_stack(ItemStack *is)
+{
+    ItemStack *junk = NULL;
+    ItemStack *trace = is;
+
+    while (trace) {
+        junk = trace;
+        trace = trace->next;
+        free(junk);
+    }
+}
+@
+\section{keyin.h}
+<<keyin.h>>=
+#ifndef _KEYIN_H_
+#define _KEYIN_H_ 1
+
+extern int in_cursor_height;
+extern int in_cursor_width;
+extern int out_cursor_height;
+extern int out_cursor_width;
+extern int in_cursor_y;
+extern int out_cursor_y;
+extern int start_x;
+extern int start_y;
+extern int simple_box_width;
+
+extern int gInInsertMode;
+
+extern unsigned int ModifiersMask;
+extern unsigned int UnsupportedModMask;
+
+
+#endif
+@
+\section{keyin.c}
+<<keyin.c>>=
+/******************************************************************************
+ *
+ * keyin.c:
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _KEYIN_C
+#include "debug.h"
+
+
+<<hyper.h>>
+<<keyin.h>>
+<<event.h>>
+<<dialog.h>>
+<<parse.h>>
+<<parse-aux.h>>
+<<scrollbar.h>>
+
+#include "all-hyper-proto.h1"
+#include <X11/keysym.h>
+
+
+#define min(x,y)     ( (x<y)?(x):(y))
+
+int in_cursor_height;
+int in_cursor_width;
+int out_cursor_height;
+int out_cursor_width;
+int in_cursor_y;
+int out_cursor_y;
+int start_x;
+int start_y;
+int simple_box_width;
+int gInInsertMode = 0;
+
+unsigned int ModifiersMask = ShiftMask | LockMask | ControlMask
+    | Mod1Mask | Mod2Mask | Mod3Mask
+    | Mod4Mask | Mod5Mask;
+
+unsigned int UnsupportedModMask = LockMask | ControlMask
+    | Mod1Mask | Mod2Mask | Mod3Mask
+    | Mod4Mask | Mod5Mask;
+
+
+/*
+ * Since the user can't tell me directly what name to use here, I am going to
+ * let it be a default property. This way the user can link to whatever page
+ * he/she wants. If it is a link right to the quit  page, then I will just
+ * quit right away. Otherwise I will try to find the page, and display it.
+ */
+
+static char *protected_quit;
+
+HyperLink *quitLink;            /** the global link to the quit page ***/
+
+void
+handle_key(XEvent *event)
+{
+  char key_buffer[20];
+  int key_buffer_size = 20;
+  KeySym keysym;
+  XComposeStatus compstatus;
+  int charcount;
+  int display_again = 0;
+  char *name;
+  char *filename;
+  /*char *head = "echo htadd -l ";*/
+  /*char *blank1 = "                                        ";*/
+  /*char *blank2 = "                                       \n";*/
+  char buffer[180];
+  FILE *filehandle;
+
+  charcount = XLookupString((XKeyEvent *)event, key_buffer, key_buffer_size, &keysym ,&compstatus); /* 5 args */
+
+  key_buffer[charcount] = '\0';
+  switch (keysym) {
+  case XK_Prior:
+  case XK_F29:
+    scrollUpPage();
+    break;
+  case XK_Next:
+  case XK_F35:
+    scrollDownPage();
+    break;
+  case XK_F3:
+  case XK_F12:
+    quitHyperDoc();
+    break;
+  case XK_F5:
+    if (event->xkey.state & ShiftMask) {
+      name = gWindow->page->name;
+      filename = gWindow->page->filename;
+      sprintf(buffer, "htadd -l %s\n", filename);
+      system(buffer);
+      filehandle = (FILE *) hash_find(&gFileHashTable, filename);
+      fclose(filehandle);
+      hash_delete(&gFileHashTable, filename);
+      gWindow->fMacroHashTable =
+	(HashTable *) halloc(sizeof(HashTable), "macro hash");
+      hash_init(
+		gWindow->fMacroHashTable, 
+		MacroHashSize, 
+		(EqualFunction ) string_equal, 
+		(HashcodeFunction) string_hash);
+      gWindow->fPatchHashTable = (HashTable *) halloc(sizeof(HashTable), "patch hash");
+      hash_init(
+		gWindow->fPatchHashTable, 
+		PatchHashSize, 
+		(EqualFunction ) string_equal, 
+		(HashcodeFunction) string_hash);
+      gWindow->fPasteHashTable = (HashTable *) halloc(sizeof(HashTable), "paste hash");
+      hash_init(gWindow->fPasteHashTable, 
+		PasteHashSize,
+                (EqualFunction ) string_equal, 
+		(HashcodeFunction) string_hash);
+      gWindow->fCondHashTable = (HashTable *) halloc(sizeof(HashTable), "cond hash");
+      hash_init(
+		gWindow->fCondHashTable, 
+		CondHashSize,
+                (EqualFunction ) string_equal, 
+		(HashcodeFunction) string_hash);
+      gWindow->fPageHashTable = (HashTable *) halloc(sizeof(HashTable), "page hash");
+      hash_init(
+		gWindow->fPageHashTable, 
+		PageHashSize,
+                (EqualFunction ) string_equal, 
+		(HashcodeFunction) string_hash);
+      make_special_pages(gWindow->fPageHashTable);
+      read_ht_db(
+		 gWindow->fPageHashTable, 
+		 gWindow->fMacroHashTable,
+		 gWindow->fPatchHashTable);
+      gWindow->page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, name);
+      if (gWindow->page == NULL) {
+	fprintf(stderr, "lose...gWindow->page for %s is null\n", name);
+	exit(-1);
+      }
+      display_again = 1;
+    }
+    break;
+  case XK_F9:
+    make_window_link(KeyDefsHelpPage);
+    break;
+  case XK_Tab:
+    if (event->xkey.state & ShiftMask)
+      prev_input_focus();
+    else if (event->xkey.state & ModifiersMask)
+      BeepAtTheUser();
+    else
+      next_input_focus();
+    break;
+  case XK_Return:
+    if (!(event->xkey.state & ShiftMask)) {
+      next_input_focus();
+      break;
+    }
+
+    /* next ones fall through to input area handling */
+
+  case XK_Escape:
+    if (!gWindow->page->current_item)
+      break;
+  case XK_F1:
+    if (!gWindow->page->current_item) {
+      gWindow->page->helppage = alloc_string(NoMoreHelpPage);
+      helpForHyperDoc();
+      break;
+    }
+  case XK_Home:
+    if (!gWindow->page->current_item) {
+      scrollToFirstPage();
+      break;
+    }
+  case XK_Up:
+    if (!gWindow->page->current_item) {
+      scrollUp();
+      break;
+    }
+  case XK_Down:
+    if (!gWindow->page->current_item) {
+      scrollDown();
+      break;
+    }
+
+  default:
+    display_again = 0;
+    dialog(event, keysym, key_buffer);
+    XFlush(gXDisplay);
+    break;
+  }
+
+  if (display_again) {
+    display_page(gWindow->page);
+    gWindow->fWindowHashTable = gWindow->page->fLinkHashTable;
+  }
+}
+
+/*
+ * This routine returns the modifier mask associated
+ * to a key symbol
+ */
+
+static unsigned int
+get_modifier_mask(KeySym sym)
+{
+    unsigned int       i, mask;
+    XModifierKeymap    *mod;
+    KeyCode            kcode;
+    const int          masks[8] = {
+        ShiftMask, LockMask, ControlMask,
+            Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
+    };
+
+    mod = XGetModifierMapping(gXDisplay);
+    kcode = XKeysymToKeycode(gXDisplay,sym);
+
+    if (mod) {
+        for (i = 0; i < (8 * mod->max_keypermod); i++){
+             if (!mod->modifiermap[i]) continue;
+             else if (kcode == mod->modifiermap[i]){
+                 mask = masks[i / mod->max_keypermod];
+                 XFreeModifiermap(mod);
+                 return mask;
+             }
+        }
+        XFreeModifiermap(mod);
+    }
+    return 0;
+}
+
+
+
+/*
+ * This routine initializes some of the variables needed by the input
+ * strings, and boxes.
+ */
+
+void
+init_keyin(void)
+{
+    char *prop;
+    unsigned int nlm;
+
+    nlm = get_modifier_mask(XK_Num_Lock);
+    UnsupportedModMask &= ~nlm;
+    ModifiersMask &= ~nlm;
+
+    /*
+     * First set all the values for when the active cursor is in the window
+     */
+
+    in_cursor_height = 2;
+    in_cursor_y = gInputFont->max_bounds.ascent +
+        gInputFont->max_bounds.descent;
+    in_cursor_width = gInputFont->max_bounds.width;
+
+    /*
+     * Now for when the cursor is empty
+     */
+
+    out_cursor_height = gInputFont->max_bounds.ascent +
+        gInputFont->max_bounds.descent;
+    out_cursor_y = 2;
+    out_cursor_width = in_cursor_width;
+
+    start_x = 5;
+
+    start_y = gInputFont->max_bounds.ascent;
+
+    /*
+     * Find out How big I should make the simple boxes
+     */
+
+    simple_box_width = XTextWidth(gInputFont, "X", 1) + 5;
+
+    prop = XGetDefault(gXDisplay, gArgv[0], "ProtectedQuit");
+
+    if (prop == NULL) {
+        protected_quit = (char *) halloc(strlen("ProtectedPage") + 1,
+                                         "protected_quit");
+        strcpy(protected_quit, "ProtectedPage");
+    }
+    else {
+        protected_quit = (char *) halloc(strlen(prop) + 1, "protected_quit");
+        strcpy(protected_quit, prop);
+    }
+
+
+}
+@
+\section{dumpToken function}
+We need a function to print the token object for debugging.
+
+To use this function the caller provides its own name and the
+token to be printed. For instance, a call would look like:
+\begin{verbatim}
+dumpToken("fnname",token)
+\end{verbatim}
+There is no return value.
+<<dumptoken function>>=
+void
+dumpToken(char *caller, Token t)
+{ fprintf(stderr,"%s:dumpToken type=%s id=%s\n",
+    caller,token_table[t.type],t.id);
+}
+
+@
+
+\section{lex.h}
+<<lex.h>>=
+#ifndef _LEX_H_
+#define _LEX_H_ 1
+
+<<hyper.h>>
+
+extern HyperDocPage *gPageBeingParsed;
+
+extern short int gInSpadsrc;
+extern short int gInVerbatim;
+
+#endif
+@
+\section{lex.c}
+<<lex.c>>=
+/*
+ * Lexical analyzer stuff. Exported functions: parser_init()       --
+ * initialize the parser tables with keywords init_scanner()       --
+ * initialize scanner for reading a new page get_token()                   --
+ * sets the "token" variable to be the next -- token in the current input
+ * stream save_scanner_state(   )  -- save the current state of scanner so
+ * that -- the scanner input mode may be switched restore_scanner_state() --
+ * undo the saved state
+ *
+ * Note: The scanner reads from three seperate input locations depending on the
+ * value of the variable "input_type".  If this variable is:
+ *
+ * FromFile       -- it read from the file pointed to by "cfile". FromString
+ * -- It reads from the string "input_string". FromSpadSocket -- It reads
+ * from the socket pointed to by spad_socket FromFD       -- It reads from a
+ * file descriptor
+ *
+ *
+ * New variable useAscii -- tells us if we we should translate 
+ * graphics characters on the fly 
+ * initialised in init_scanner
+ *
+ */
+#define _LEX_C
+#include "debug.h"
+
+int useAscii;
+
+#define PARSER 1
+
+<<hyper.h>>
+<<hterror.h>>
+<<lex.h>>
+
+#include "all-hyper-proto.h1"
+#include "sockio-c.h1"
+
+
+#include <ctype.h>
+#include <setjmp.h>
+
+extern int gTtFontIs850;
+
+
+StateNode *top_state_node;
+HyperDocPage *gPageBeingParsed;      /* page currently being parsed    */
+extern jmp_buf jmpbuf;
+extern char ebuffer[];
+short int gInSpadsrc = 0;
+short int gInVerbatim;
+
+/* Parser variables */
+long fpos;                      /* Position of pointer in file in characters */
+long page_start_fpos;           /* where the current pages fpos started      */
+long keyword_fpos;              /* fpos of beginning of most recent keyword */
+Token token;                    /* most recently read token */
+int last_token;                 /* most recently read token for unget_token */
+int input_type;                 /* indicates where to read input */
+char *input_string;             /* input string read when from_string is true */
+int last_ch;                    /* last character read, for unget_char */
+int last_command;               /* the last socket command */
+int keyword;                    /* the last command was a keyword, or a group */
+int cfd;                        /* current file decriptor */
+FILE *cfile;                    /* currently active file pointer */
+FILE *unixfd;
+int line_number;
+
+char sock_buf[1024];            /* buffer for socket input */
+
+#define TokenHashSize   100
+
+static HashTable tokenHashTable;           /* hash table of parser tokens */
+
+<<dumptoken function>>
+
+/* initialize the parser keyword hash table */
+void
+parser_init(void)
+{
+    int i;
+    Token *toke;
+
+    /* First I initialize the hash table for the tokens */
+
+    hash_init(
+	      &tokenHashTable, 
+	      TokenHashSize, 
+	      (EqualFunction)string_equal, 
+	      (HashcodeFunction)string_hash);
+    for (i = 2; i <= NumberUserTokens; i++) {
+        toke = (Token *) halloc(sizeof(Token), "Token");
+        toke->type = i;
+        toke->id = token_table[i];
+        hash_insert(&tokenHashTable, (char *)toke, toke->id);
+    }
+
+}
+
+/* initialize the lexical scanner to read from a file */
+void
+init_scanner(void)
+{
+    if (getenv("HTASCII")) {
+        useAscii = (strcmp(getenv("HTASCII"), "yes") == 0);
+    }
+    else {
+	if(gTtFontIs850==1) useAscii = 0;
+        else useAscii = 1;
+    }
+    keyword = 0;
+    last_ch = NoChar;
+    last_token = 0;
+    input_type = FromFile;
+    fpos = 0;
+    keyword_fpos = 0;
+    last_command = -1;
+    line_number = 1;
+}
+
+/*
+ * variables to save current state of scanner.  Currently only one level of
+ * saving is allowed.  In the future we should allow nested saves
+ */
+
+/* save the current state of the scanner */
+void
+save_scanner_state(void)
+{
+    StateNode *new_item = (StateNode *) halloc((sizeof(StateNode)), "StateNode");
+
+    new_item->page_start_fpos = page_start_fpos;
+    new_item->fpos = fpos;
+    new_item->keyword_fpos = keyword_fpos;
+    new_item->last_ch = last_ch;
+    new_item->last_token = last_token;
+    new_item->token = token;
+    new_item->input_type = input_type;
+    new_item->input_string = input_string;
+    new_item->cfile = cfile;
+    new_item->next = top_state_node;
+    new_item->keyword = keyword;
+    top_state_node = new_item;
+}
+
+/* restore the saved scanner state */
+void
+restore_scanner_state(void)
+{
+    StateNode *x = top_state_node;
+
+    if (top_state_node == NULL) {
+        fprintf(stderr, "Restore Scanner State: State empty\n");
+        exit(-1);
+    }
+    top_state_node = top_state_node->next;
+    page_start_fpos = x->page_start_fpos;
+    fpos = x->fpos;
+    keyword_fpos = x->keyword_fpos;
+    last_ch = x->last_ch;
+    last_token = x->last_token;
+    token = x->token;
+    input_type = x->input_type;
+    input_string = x->input_string;
+    cfile = x->cfile;
+    keyword = x->keyword;
+    if (cfile != NULL)
+        fseek(cfile, fpos + page_start_fpos, 0);
+    /** Once that is done, lets throw away some memory **/
+    free(x);
+}
+
+/* return the character to the input stream. */
+void
+unget_char(int c)
+{
+    if (c == '\n')
+        line_number--;
+    last_ch = c;
+}
+
+int
+get_char(void)
+{
+    int c;
+
+    c = get_char1();
+    if (useAscii) {
+        switch (c) {
+          case 'Ä':
+            c = '-';
+            break;
+          case 'Ú':
+            c = '+';
+            break;
+          case 'Ã':
+            c = '[';
+            break;
+          case 'À':
+            c = '+';
+            break;
+          case 'Â':
+            c = '-';
+            break;
+          case 'Å':
+            c = '+';
+            break;
+          case 'Á':
+            c = '-';
+            break;
+          case '¿':
+            c = '+';
+            break;
+          case '´':
+            c = ']';
+            break;
+          case 'Ù':
+            c = '+';
+            break;
+          case '³':
+            c = '|';
+            break;
+          default:
+            break;
+        }
+    }
+    return c;
+}
+
+char * read_again = 0;
+
+/* return the next character in the input stream */
+static int
+get_char1(void)
+{
+    int c;
+    int cmd;
+
+    if (last_ch != NoChar) {
+        c = last_ch;
+        last_ch = NoChar;
+        if (c == '\n')
+            line_number++;
+        return c;
+    }
+    switch (input_type) {
+      case FromUnixFD:
+        c = getc(unixfd);
+        if (c == '\n')
+            line_number++;
+        return c;
+      case FromString:
+        c = (*input_string ? *input_string++ : EOF);
+        if (c == '\n')
+            line_number++;
+        return c;
+      case FromFile:
+        c = getc(cfile);
+        fpos++;
+        if (c == '\n')
+            line_number++;
+        return c;
+      case FromSpadSocket:
+AGAIN:
+        if (*input_string) {
+            /* this should never happen for the first character */
+            c = *input_string++;
+            if (c == '\n')
+                line_number++;
+            return c;
+        }
+        if (last_command == EndOfPage)
+            return EOF;
+        if (read_again == NULL) {
+            last_command = cmd = get_int(spad_socket);
+            if (cmd == EndOfPage)
+                return EOF;
+#ifndef HTADD
+            if (cmd == SpadError)
+                spad_error_handler();
+#endif
+        }
+        read_again = get_string_buf(spad_socket, sock_buf, 1023);
+        /* this will be null if this is the last time*/
+        input_string = sock_buf;
+	goto AGAIN;
+      default:
+        fprintf(stderr, "Get Char: Unknown type of input: %d\n", input_type);
+        return -1;
+    }
+}
+
+
+#define special(c) ((c) == '{' || (c) == '}' || (c) == '#' || (c) == '%' || \
+                    (c) == '\\'  || (c) == '[' || (c) == ']' || (c) == '_' || \
+                    (c) == ' ' || (c) == '$' || (c) == '~' || (c) == '^' ||  \
+                    (c) == '&')
+
+#define punctuation(c) ((c)== '`' || (c) == '\'' || (c) == ','  || \
+                        (c) == '.' || (c) == '?' || (c) == '"' || \
+                        (c)  == ';' || (c) == ':' || (c) == '-')
+
+#define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define delim(c) \
+  (whitespace(c) || special(c) || punctuation(c))
+
+
+
+Token unget_toke;
+
+/* return current token to the input stream */
+void
+unget_token(void)
+{
+    last_token = 1;
+    unget_toke.type = token.type;
+    unget_toke.id = alloc_string(token.id - 1);
+}
+
+
+int
+get_token(void)
+{
+    int c, ws;
+    int nls = 0;
+    static int seen_white = 0;
+    static char buffer[1024];
+    char *buf = buffer;
+
+    if (last_token) {
+        last_token = 0;
+        token.type = unget_toke.type;
+        strcpy(buffer, unget_toke.id);
+        free(unget_toke.id);
+        token.id = buffer + 1;
+        if (token.type == EOF)
+            return EOF;
+        else
+            return 0;
+    }
+    seen_white = nls = 0;
+    do {
+        c = get_char();
+        ws = whitespace(c);
+        if (ws)
+            seen_white++;
+        if (c == '\n') {
+            if (nls) {
+                token.type = Par;
+                return 0;
+            }
+            else
+                nls++;
+        }
+    } while (ws);
+
+    /* first character of string indicates number of spaces before token */
+
+    if (!keyword)
+        *buf++ = seen_white;
+    else
+        *buf++ = 0;
+
+    keyword = 0;
+    if (input_type != FromSpadSocket && c == '%') {
+        while ((c = get_char()) != '\n' && c != EOF);
+/* trying to fix the comment problem: a comment line forces words on either side together*/
+/* try returning the eol */
+        unget_char(c);
+        return get_token();
+    }
+    if (input_type == FromFile && c == '$') {
+        token.type = Dollar;
+        return 0;
+    }
+    switch (c) {
+      case EOF:
+        token.type = -1;
+        return EOF;
+      case '\\':
+        keyword_fpos = fpos - 1;
+        c = get_char();
+        if (!isalpha(c)) {
+            *buf++ = c;
+            token.type = Word;
+            *buf = '\0';
+            seen_white = 0;
+        }
+        else {
+            do {
+                *buf++ = c;
+            } while ((c = get_char()) != EOF && isalpha(c));
+
+            unget_char(c);
+            *buf = '\0';
+            keyword = 1;
+            token.id = buffer + 1;
+            return (keyword_type());
+        }
+        break;
+      case '{':
+        token.type = Lbrace;
+        break;
+      case '}':
+        token.type = Rbrace;
+        break;
+      case '[':
+        token.type = Lsquarebrace;
+        *buf++ = c;
+        *buf = '\0';
+        token.id = buffer + 1;
+        break;
+      case ']':
+        token.type = Rsquarebrace;
+        *buf++ = c;
+        *buf = '\0';
+        token.id = buffer + 1;
+        break;
+      case '#':
+        token.type = Pound;
+
+        /*
+         * if I get a pound then what I do is parse until I get something
+         * that is not an integer
+         */
+        c = get_char();
+        while (isdigit(c) && (c != EOF)) {
+            *buf++ = c;
+            c = get_char();
+        }
+        unget_char(c);
+        *buf = '\0';
+        token.id = buffer + 1;
+        break;
+      case '`':
+      case '\'':
+      case ',':
+      case '.':
+      case '!':
+      case '?':
+      case '"':
+      case ':':
+      case ';':
+        token.type = Punctuation;
+        *buf++ = c;
+        *buf = '\0';
+        /** Now I should set the buffer[0] as my flag for whether I had
+          white-space in front of me, and whether I had white space
+          behind me **/
+        if (buffer[0])
+            buffer[0] = FRONTSPACE;
+        c = get_char();
+        if (whitespace(c))
+            buffer[0] |= BACKSPACE;
+        unget_char(c);
+        token.id = buffer + 1;
+        break;
+      case '-':
+        do {
+            *buf++ = c;
+        } while (((c = get_char()) != EOF) && (c == '-'));
+        unget_char(c);
+        *buf = '\0';
+        token.type = Dash;
+        token.id = buffer + 1;
+        break;
+      default:
+        do {
+            *buf++ = c;
+        } while ((c = get_char()) != EOF && !delim(c));
+        unget_char(c);
+        *buf = '\0';
+        token.type = Word;
+        token.id = buffer + 1;
+        break;
+    }
+/*    dumpToken("get_token",token);*/
+    return 0;
+}
+
+
+/*
+ * Here are the structures and stuff needed for the begin and end routines.
+ * The stack stores the begin types that have been seen and the end
+ * pops them off and checks to insure that they are reversed properly.
+ */
+
+typedef struct be_struct {
+    int type;
+    char *id;
+    struct be_struct *next;
+}   BeStruct;
+
+BeStruct *top_be_stack;
+
+
+void
+push_be_stack(int type,char * id)
+{
+    BeStruct *be = (BeStruct *) halloc(sizeof(BeStruct), "BeginENd stack");
+
+    if (gWindow != NULL) {
+        be->type = type;
+        be->next = top_be_stack;
+        be->id = alloc_string(id);
+        top_be_stack = be;
+    }
+    return;
+}
+void
+check_and_pop_be_stack(int type,char * id)
+{
+    BeStruct *x;
+
+    /*
+     * this routine pops the be stack and compares types. If they are
+     * the same then I am okay and return a 1. Else I return a two and try to
+     * print a meaningful message
+     */
+    if (gWindow == NULL)
+        return;
+    if (top_be_stack == NULL) { /* tried to pop when I shouldn't have */
+        fprintf(stderr, "Unexpected \\end{%s} \n", token.id);
+        print_page_and_filename();
+        print_next_ten_tokens();
+        jump();
+    }
+    x = top_be_stack;
+    if (x->type == type) {
+        top_be_stack = top_be_stack->next;
+        free(x->id);
+        free(x);
+        return;
+    }
+    /* else I didn't have a match. Lets try to write a sensible message */
+    fprintf(stderr, "\\begin{%s} ended with \\end{%s} \n", x->id, id);
+    print_page_and_filename();
+    print_next_ten_tokens();
+    jump();
+}
+
+int
+clear_be_stack(void)
+{
+    BeStruct *x = top_be_stack, *y;
+
+    top_be_stack = NULL;
+    while (x != NULL) {
+        y = x->next;
+        free(x);
+        x = y;
+    }
+    return 1;
+}
+
+int
+be_type(char *which)
+{
+    Token store;
+
+    get_expected_token(Lbrace);
+    get_expected_token(Word);
+    switch (token.id[0]) {
+      case 't':
+        if (!strcmp(token.id, "titems")) {
+            token.type = Begintitems;
+        }
+        else {
+            return -1;
+        }
+        break;
+      case 'p':
+        if (!strcmp(token.id, "page")) {
+            token.type = Page;
+        }
+        else if (!strcmp(token.id, "paste")) {
+            token.type = Paste;
+        }
+        else if (!strcmp(token.id, "patch")) {
+            token.type = Patch;
+        }
+        else {
+            return -1;
+        }
+        break;
+      case 'v':         /* possibly a verbatim mode */
+        if (!strcmp(token.id, "verbatim")) {
+            token.type = Verbatim;
+        }
+        else {
+            return -1;
+        }
+        break;
+      case 's':         /* possibly a scroll mode */
+        if (!strcmp("scroll", token.id)) {
+            token.type = Beginscroll;
+        }
+        else if (!strcmp(token.id, "spadsrc")) {
+            token.type = Spadsrc;
+        }
+        else {
+            return -1;
+        }
+        break;
+      case 'i':         /* possibly a item */
+        if (!strcmp("items", token.id)) {
+            token.type = Beginitems;
+        }
+        else {
+            return -1;
+        }
+        break;
+      default:
+        return -1;
+    }
+    store.type = token.type;
+    /* store.id = alloc_string(token.id); */
+    get_expected_token(Rbrace);
+    token.type = store.type;
+
+    /*
+     * strcpy(token.id, store.id); free(store.id);
+     */
+    return 0;
+
+}
+int
+begin_type(void)
+{
+    /*Token store;*/
+    int ret_val;
+
+    /*
+     * This routine parses a statement of the form \begin{word}. Once it has
+     * read the word it tries to assign it a type. Once that is done it sends
+     * the word id, and the type to push_be_stack and then returns the type.
+     * For the moment I amnot even going to use a has_table, although in the
+     * future this may be needed
+     */
+    ret_val = be_type("begin");
+    if (ret_val == -1) {
+        if (gWindow == NULL || gInVerbatim)
+            return 1;
+        else {
+            fprintf(stderr, "Unknown begin type \\begin{%s} \n", token.id);
+            print_page_and_filename();
+            print_next_ten_tokens();
+            jump();
+        }
+    }
+    else {
+        if (gWindow != NULL && !gInVerbatim && token.type != Verbatim
+            && token.type != Spadsrc) {
+            /* Now here I should push the needed info and then get */
+            push_be_stack(token.type, token.id);
+        }
+        return 1;
+    }
+    return 1;
+}
+
+
+int
+end_type(void)
+{
+    int ret;
+
+    /*
+     * This routine gets the end type just as the begin_type routine does,
+     * But then it checks to see if recieved the proper end_type. By a clever
+     * trick, the proper end type is 3000 + type. When environments this will
+     * have to change
+     */
+    ret = be_type("end");
+    if (ret == -1) {
+        /* unrecognized end token */
+        if (gWindow == NULL || gInVerbatim) {
+            return 1;
+        }
+        else {
+            fprintf(stderr, "Unknown begin type \\begin{%s} \n", token.id);
+            print_page_and_filename();
+            print_next_ten_tokens();
+            jump();
+        }
+    }
+    else {
+        if (gWindow != NULL && !gInVerbatim) {
+            check_and_pop_be_stack(token.type, token.id);
+            token.type += 3000;
+            return 1;
+        }
+        else {
+            if (gWindow != NULL && ((gInVerbatim && token.type == Verbatim) ||
+                                (gInSpadsrc && token.type == Spadsrc))) {
+                check_and_pop_be_stack(token.type, token.id);
+                token.type += 3000;
+                return 1;
+            }
+            else {
+                token.type += 3000;
+                return 1;
+            }
+        }
+    }
+    return 1;
+}
+
+
+
+static int
+keyword_type(void)
+{
+    Token *token_ent;
+
+    /* first check to see if it is a reserved token */
+    token_ent = (Token *) hash_find(&tokenHashTable, token.id);
+    if (token_ent != NULL) {
+        token.type = token_ent->type;
+
+        /*
+         * if I am a keyword I also have to check to see if I am a begin or
+         * an end
+         */
+        if (token.type == Begin)
+            return begin_type();
+        if (token.type == End)
+            return end_type();
+        /* next check to see if it is a macro */
+    }
+    else if (gWindow != NULL) {
+        if (hash_find(gWindow->fMacroHashTable, token.id) != NULL)
+            token.type = Macro;
+        else if (gPageBeingParsed->box_hash != NULL &&
+                 hash_find(gPageBeingParsed->box_hash, token.id) != NULL)
+        {
+            token.type = Boxcond;
+        }
+        else if (hash_find(gWindow->fCondHashTable, token.id) != NULL)
+            token.type = Cond;
+        else                    /* We have no idea what we've got */
+            token.type = Unkeyword;
+    }
+    else {                      /* We am probably in htadd so just return. It
+                                 * is only concerned with pages anyway */
+        token.type = Unkeyword;
+    }
+    return 0;
+}
+
+/* read a token, and report a syntax error if it has the wrong type */
+void
+get_expected_token(int type)
+{
+    get_token();
+    if (token.type != type) {
+        token_name(type);
+        fprintf(stderr, "syntax error: expected a %s\n", ebuffer);
+        if (token.type == EOF) {
+            print_page_and_filename();
+            fprintf(stderr, "Unexpected EOF\n");
+        }
+        else {
+            token_name(token.type);
+            fprintf(stderr, "not a %s\n", ebuffer);
+            print_page_and_filename();
+            print_next_ten_tokens();
+        }
+        longjmp(jmpbuf, 1);
+        fprintf(stderr, "Could not jump to Error Page\n");
+        exit(-1);
+    }
+}
+
+
+#ifndef HTADD
+static void
+spad_error_handler(void)
+{
+    /* fprintf(stderr, "got a spad error\n"); */
+    longjmp(jmpbuf, 1);
+    fprintf(stderr, "(HyperDoc) Fatal Error: Could not jump to Error Page.\n");
+    exit(-1);
+}
+
+extern int still_reading, str_len;
+void
+reset_connection(void)
+{
+    if (spad_socket) {
+        FD_CLR(spad_socket->socket, &socket_mask);
+        purpose_table[spad_socket->purpose] = NULL;
+        close(spad_socket->socket);
+        spad_socket->socket = 0;
+        spad_socket = NULL;
+        if (input_string)
+            input_string[0] = '\0';
+        read_again = 0;
+        str_len = 0;
+        still_reading = 0;
+        connect_spad();
+    }
+}
+#endif
+
+
+/* returns true if spad is currently computing */
+int
+spad_busy(void)
+{
+    if (session_server == NULL)
+        return 1;
+    send_int(session_server, QuerySpad);
+    return get_int(session_server);
+}
+
+/* connect to AXIOM , return 0 if succesful, 1 if not */
+int
+connect_spad(void)
+{
+    if (!MenuServerOpened) {
+        fprintf(stderr, "(HyperDoc) Warning: Not connected to AXIOM Server!\n");
+        LoudBeepAtTheUser();
+        return NotConnected;
+    }
+    if (spad_socket == NULL) {
+        spad_socket = connect_to_local_server(SpadServer, MenuServer, Forever);
+        if (spad_socket == NULL) {
+            fprintf(stderr, "(HyperDoc) Warning: Could not connect to AXIOM Server!\n");
+            LoudBeepAtTheUser();
+            return NotConnected;
+        }
+    }
+    /* if (spad_busy()) return SpadBusy; */
+    return Connected;
+}
+@
+\section{macro.c}
+<<macro.c>>=
+#define _MACRO_C
+#include "debug.h"
+
+<<parse.h>>
+<<parse-aux.h>>
+<<mem.h>>
+
+#include "all-hyper-proto.h1"
+
+
+/* #define DEBUG 1 */
+extern FILE *cfile;
+
+
+/*
+ * This routine keeps scanning until it reaches it pops off 1 more
+ * right brace then left brace
+ */
+void
+scan_HyperDoc(void)
+{
+    HDWindow *twin = gWindow;
+    int ret_val;
+    int number_of_left_braces = 1;
+
+    gWindow = NULL;
+    while (number_of_left_braces) {
+        ret_val = get_token();
+        if (ret_val == EOF && number_of_left_braces) {
+            fprintf(stderr, "Scan_Hypertex: Unexpected End of File\n");
+            longjmp(jmpbuf, 1);
+        }
+        switch (token.type) {
+          case Page:
+            fprintf(stderr, "scan_HyperDoc: Unexpected Page Declaration\n");
+            break;
+          case NewCommand:
+            fprintf(stderr, "scan_HyperDoc: Unexpected Macro Declaration\n");
+            break;
+          case Lbrace:
+            number_of_left_braces++;
+            break;
+          case Endpatch:
+          case Rbrace:
+            number_of_left_braces--;
+            break;
+          default:
+            break;
+        }
+    }
+    gWindow = twin;
+}
+
+int
+number(char *str)
+{
+    char *t = str;
+
+    while (*t)
+        if (!isdigit(*t++))
+            return 0;
+    return 1;
+}
+
+/* Parse a given macro given the pointer to the unlaoded macro ** */
+
+static char *
+load_macro(MacroStore *macro)
+{
+    int ret_val;
+    long start_fpos;
+    int size = 0;
+    char *trace;
+    char *macro_buff;
+
+    save_scanner_state();
+    cfile = find_fp(macro->fpos);
+
+
+    init_scanner();
+
+    /** First thing I should do is make sure that the name is correct ***/
+    get_expected_token(NewCommand);
+    get_expected_token(Lbrace);
+    get_expected_token(Macro);
+    if (strcmp(token.id, macro->name)) {
+        /** WOW, Somehow I had the location of the wrong macro **/
+        fprintf(stderr, "Expected macro name %s got insted %s in load_macro\n",
+                macro->name, token.id);
+        longjmp(jmpbuf, 1);
+    }
+    get_expected_token(Rbrace);
+
+    /** Next I should check to see if I have any parameters **/
+    get_token();
+    if (token.type == Lsquarebrace) {
+        /** The person is telling me the number of macros he is going to use **/
+        get_expected_token(Word);
+        if (!number(token.id)) {
+            fprintf(stderr, "load_macro: Expected A Value Instead Got %s\n",
+                    token.id);
+            longjmp(jmpbuf, 1);
+        }
+        /** if it is a number, then I should store it in the parameter number
+          member of the macro structure **/
+        macro->number_parameters = atoi(token.id);
+#ifdef DEBUG
+        fprintf(stderr,
+              "The number of parameters is %d\n", macro->number_parameters);
+#endif
+        get_expected_token(Rsquarebrace);
+        get_token();
+    }
+    else
+        macro->number_parameters = 0;
+
+    /*** Now I should be able to check the token, and insure that I have read
+      a leftbrace, then the string will follow                    ****/
+    if (token.type != Lbrace) {
+        /** The macro is not in a group, uh oh **/
+        fprintf(stderr, "load_macro:Expected a Left Brace got type %d\n",
+                token.type);
+        longjmp(jmpbuf, 1);
+    }
+    start_fpos = fpos;
+    scan_HyperDoc();
+    ret_val = fseek(cfile, macro->fpos.pos + start_fpos, 0);
+    size = fpos - start_fpos;
+    macro_buff = (char *) halloc((size + 1) * sizeof(char), "Macro_buf");
+    for (size = 0, trace = macro_buff; size < fpos - (start_fpos) - 1; size++)
+        *trace++ = getc(cfile);
+    *trace = '\0';
+    macro->loaded = 1;
+    restore_scanner_state();
+    return macro_buff;
+}
+
+
+/** Here are the functions and declarations for the parameter stack **/
+ParameterList parameters = NULL;
+
+ParameterList
+init_parameter_elem(int number)
+{
+    ParameterList new;
+    int count;
+
+    /** allocate the space neeeded **/
+    new = (ParameterList) halloc(sizeof(struct parameter_list_type),
+                                 "ParameterList");
+    /** now allocate the memeory  for the pointers to the  parameters **/
+    if (number) {
+        new->list = (char **) halloc(number * sizeof(char *), "Parameter List");
+
+        /** initialize my pointers **/
+        for (count = 0; count < number; count++)
+            (new->list)[count] = NULL;
+    }
+    new->number = number;
+    return new;
+}
+
+int
+push_parameters(ParameterList new)
+{
+
+    if (new == NULL) {
+        fprintf(stderr, "Tried pushing a null list onto the parameter stack\n");
+        longjmp(jmpbuf, 1);
+    }
+
+    new->next = parameters;
+    parameters = new;
+    return 1;
+}
+int
+pop_parameters(void)
+{
+    /** Simply pops the top of the parameter list, being good and freeing
+      all the memory **/
+    ParameterList old;
+    int count;
+
+    if (!parameters) {
+        return 0;
+    }
+
+    old = parameters;
+    parameters = old->next;
+
+    /** Free the parameter text and pointers **/
+    if (old->number >0) {
+	for (count = 0; count < old->number; count++)
+		if ( (old->list)[count] )  free((char *) (old->list)[count]);
+	free(old->list);
+	}
+
+    free(old);                  /** free the parameter **/
+
+    return 1;
+}
+
+int
+parse_macro(void)
+{
+
+    /*
+     * This routine loads a macro if needed, and then parses it from the
+     * string
+     */
+    MacroStore *macro;
+    int s;
+
+    curr_node->type = Macro;
+    curr_node->space = token.id[-1];
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    macro = (MacroStore *) hash_find(gWindow->fMacroHashTable, token.id);
+    if (macro != NULL) {
+        if (!macro->loaded)
+            macro->macro_string = load_macro(macro);
+        get_parameter_strings(macro->number_parameters, macro->name);
+        parse_from_string(macro->macro_string);
+        if (gEndedPage) {
+            s = curr_node->type;
+            curr_node->type = Endmacro;
+            curr_node->next = alloc_node();
+            curr_node = curr_node->next;
+            curr_node->type = s;
+        }
+        else
+            curr_node->type = Endmacro;
+        if (pop_parameters())
+            return 1;
+        else {
+            fprintf(stderr,
+                    "parse_macro: Tried to pop an empty paramter stack\n");
+            longjmp(jmpbuf, 1);
+        }
+    }
+    else {
+        fprintf(stderr, "parse_macro: Unknown keyword %s\n", token.id);
+        longjmp(jmpbuf, 1);
+    }
+}
+
+#define numeric(c) ((c >= '0' && c <= '9')?1:0)
+
+static void
+get_parameter_strings(int number,char * macro_name)
+{
+    static char buffer[4096];
+    char *buffer_pntr;
+    int count;
+    int lbrace_counter;
+    char c;
+    int size;
+    ParameterList new = init_parameter_elem(number);
+    int pnum;
+    char pnum_chars[5];
+    int pc;
+
+    if (!number) {              /* nothing to be done */
+        push_parameters(new);
+        return;
+    }
+    for (count = 0; count < number; count++) {
+        get_token();
+        if (token.type != Lbrace) {
+            /** The macro is not in a group, uh oh **/
+            fprintf(stderr, "Wrong number of arguments to the macro %s\n",
+                    macro_name);
+            jump();
+        }
+        for (lbrace_counter = 1, buffer_pntr = buffer;
+             lbrace_counter;) {
+            switch (c = get_char()) {
+              case EOF:
+                fprintf(stderr, "GetParameterStrings: Unexpected EOF\n");
+                longjmp(jmpbuf, 1);
+              case '}':
+                lbrace_counter--;
+                if (lbrace_counter)
+                    *buffer_pntr++ = c;
+                break;
+              case '{':
+                lbrace_counter++;
+                *buffer_pntr++ = c;
+                break;
+              case '#':
+                /* uh oh, I have a paramter reference inside a paramter */
+                /* get the number */
+                if (parameters == NULL) {
+                    *buffer_pntr++ = c;
+                    break;
+                }
+                if (
+                    ((buffer_pntr > buffer + 1) &&
+                     *(buffer_pntr - 1) == '\\' &&
+                     *(buffer_pntr - 2) != '\\') ||
+                    ((buffer_pntr > buffer) &&
+                     *(buffer_pntr - 1) == '\\')) {
+                    /* I had a \# */
+                    *buffer_pntr++ = c;
+                }
+                else {
+                    c = get_char();
+                    for (pc = 0; numeric(c); pc++) {
+                        pnum_chars[pc] = c;
+                        c = get_char();
+                    }
+                    unget_char(c);
+                    pnum_chars[pc] = '\0';
+                    pnum = atoi(pnum_chars);
+                    pc = 0;
+                    /* Now copy the paramter */
+                    while ((parameters->list)[pnum - 1][pc] != '\0')
+                        *buffer_pntr++ = (parameters->list)[pnum - 1][pc++];
+                }
+                break;
+              default:
+                *buffer_pntr++ = c;
+                break;
+            }
+        }
+        *buffer_pntr = '\0';
+        /*** Now add it to the current parameter list **/
+        size = strlen(buffer) + 1;
+        new->list[count] = (char *) halloc(size, "Parameter Strings");
+        strcpy(new->list[count], buffer);
+    }
+    push_parameters(new);
+    return ;
+}
+void
+parse_parameters(void)
+{
+    int value;
+
+    if (!number(token.id)) {
+        fprintf(stderr,
+                "Parse_parameter: Error Expected a number, got %s instead\n", 
+                token.id);
+        longjmp(jmpbuf, 1);
+    }
+
+    if ((value = atoi(token.id)) > parameters->number) {
+        /** had a bad parameter number **/
+        fprintf(stderr,
+                "Parse_parameter: Had a bad parameter number %d\n", value);
+        longjmp(jmpbuf, 1);
+    }
+
+    parse_from_string((parameters->list)[value - 1]);
+    curr_node->type = Endparameter;
+    return;
+}
+@
+\section{mem.h}
+<<mem.h>>=
+#ifndef _MEM_H_
+#define _MEM_H_ 1
+
+<<hyper.h>>
+
+
+#endif
+@
+\section{mem.c}
+<<mem.c>>=
+/******************************************************************************
+ *
+ * mem.c:  HyperDoc Memory Management Routines.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _MEM_C
+#include "debug.h"
+
+
+<<mem.h>>
+<<group.h>>
+<<event.h>>
+<<parse-aux.h>>
+
+#include "all-hyper-proto.h1"
+
+
+
+extern HashTable init_page_hash;
+extern HashTable init_macro_hash;
+extern HashTable init_patch_hash;
+
+
+static void
+free_if_non_NULL(void *p)
+{
+  if (p){
+    free(p);
+  }
+}
+
+
+/* allocate an HDWindow Structure and initialize it */
+
+HDWindow *
+alloc_hd_window(void)
+{
+  HDWindow *w = (HDWindow *) halloc(sizeof(HDWindow), "HDWindow");
+  /*char haslisp[10];*/
+
+  w->fMemoStack = (HyperDocPage **)
+    halloc(MaxMemoDepth * sizeof(HyperDocPage *), "Memo Stack");
+  w->fDownLinkStack = (HyperDocPage **)
+    halloc(MaxDownlinkDepth * sizeof(HyperDocPage *), "downlink stack");
+  w->fDownLinkStackTop =
+    (int *) halloc(MaxDownlinkDepth * sizeof(int), "top downlink stack");
+  w->fAxiomFrame = 0;
+  init_page_structs(w);
+
+  /* Now I initialize the hash tables for the page */
+  w->fCondHashTable = (HashTable *) halloc(sizeof(HashTable), "cond hash");
+  hash_init(
+	    w->fCondHashTable, 
+	    CondHashSize, 
+	    (EqualFunction) string_equal, 
+	    (HashcodeFunction) string_hash);
+
+  w->fPasteHashTable = (HashTable *) halloc(sizeof(HashTable), "paste hash");
+  hash_init(
+	    w->fPasteHashTable, 
+	    PasteHashSize, 
+	    (EqualFunction) string_equal, 
+	    (HashcodeFunction) string_hash);
+  w->fPageHashTable = hash_copy_table(&init_page_hash);
+  w->fPatchHashTable = hash_copy_table(&init_patch_hash);
+  w->fMacroHashTable = hash_copy_table(&init_macro_hash);
+
+  gWindow = w;
+  /*sprintf(haslisp, "%1d\0", MenuServerOpened);*/
+  make_special_pages(w->fPageHashTable);
+  w->fDisplayedCursor = 0;
+  return w;
+}
+
+void
+free_hd_window(HDWindow *w)
+{
+  if (w) {
+    free(w->fMemoStack);
+    free(w->fDownLinkStack);
+    free(w->fDownLinkStackTop);
+    /*
+      free(w->fWindowHashTable); will be taken care of by freeing 
+      free_hash(w->fPageHashTable, free_page); below
+      cf free_page
+      */
+    free_hash(w->fMacroHashTable, (FreeFunction)dont_free); 
+    free_hash(w->fPasteHashTable, (FreeFunction)dont_free); 
+    free_hash(w->fPatchHashTable, (FreeFunction)dont_free); 
+
+    free_hash(w->fCondHashTable, (FreeFunction)free_cond);
+    free_hash(w->fPageHashTable, (FreeFunction)free_page);
+    free(w->fPageHashTable);
+    free(w->fPatchHashTable);
+    free(w->fMacroHashTable);
+    XFreeGC(gXDisplay, w->fStandardGC);
+    XFreeGC(gXDisplay, w->fInputGC);
+    XFreeGC(gXDisplay, w->fCursorGC);
+    XFreeGC(gXDisplay, w->fControlGC);
+    free(w);
+  }
+}
+
+
+/* Allocate an empty text node */
+
+TextNode *
+alloc_node(void)
+{
+  TextNode *temp_node;
+
+  temp_node = (TextNode *) halloc(sizeof(TextNode), "Text Node");
+  temp_node->type = 0;
+  temp_node->space = 0;
+  temp_node->height = 0;
+  temp_node->width = 0;
+  temp_node->x = -1;
+  temp_node->y = -1;
+  temp_node->data.node = NULL;
+  temp_node->next = NULL;
+  temp_node->link = NULL;
+  temp_node->image.pm = 0;
+  return temp_node;
+}
+
+void
+free_node(TextNode *node, short int des)
+{
+
+  if (node == NULL)
+    return;
+
+  switch (node->type) {
+  case Paste:
+    free_pastearea(node, des);
+    free_node(node->next, des);
+    break;
+  case Pastebutton:
+    free_pastebutton(node, des);
+    free_node(node->next, des);
+    break;
+  case Ifcond:
+    free_node(node->data.ifnode->cond, des);
+    free_node(node->data.ifnode->thennode, des);
+    free_node(node->data.ifnode->elsenode, des);
+    break;
+  case Dash:
+  case Lsquarebrace:
+  case Word:
+  case WindowId:
+  case Punctuation:
+  case Lbrace:
+  case Rbrace:
+  case SimpleBox:
+  case Verbatim:
+  case Math:
+  case Spadsrctxt:
+  case Spadsrc:
+    free_if_non_NULL(node->data.text);
+    free_node(node->next, des);
+    break;
+  case Inputstring:
+    if (des)
+      delete_item(node->data.text);
+    free_if_non_NULL(node->data.text);
+    free_node(node->next, des);
+    break;
+  case It:
+  case Sl:
+  case Tt:
+  case Rm:
+  case Emphasize:
+  case Beep:
+  case BoldFace:
+  case Par:
+  case Newline:
+  case Horizontalline:
+  case Item:
+  case Beginscroll:
+  case Endscroll:
+  case Group:
+  case Table:
+  case Macro:
+  case Pound:
+  case Center:
+  case Box:
+  case Mbox:
+  case Tableitem:
+  case Scrollingnode:
+  case Headernode:
+  case Titlenode:
+  case Footernode:
+  case Controlbitmap:
+  case Fi:
+  case Description:
+  case Rsquarebrace:
+  case Endpaste:
+  case Endpastebutton:
+    free_node(node->next, des);
+    break;
+  case Inputbitmap:
+  case Inputpixmap:
+    free_if_non_NULL(node->data.text);
+    free_node(node->next, des);
+    break;
+  case Quitbutton:
+  case Helpbutton:
+  case Upbutton:
+  case Returnbutton:
+    if (des && node->link->win) {
+      hash_delete(gWindow->page->fLinkHashTable,(char *) &node->link->win);
+      XDestroyWindow(gXDisplay, node->link->win);
+    }
+    free_if_non_NULL(node->link);
+    free_node(node->next, des);
+    break;
+  case Memolink:
+  case Downlink:
+  case Windowlink:
+  case Link:
+  case Lisplink:
+  case Lispwindowlink:
+  case Spadcall:
+  case Spadcallquit:
+  case LispMemoLink:
+  case Lispcommand:
+  case Lispcommandquit:
+  case LispDownLink:
+  case Unixlink:
+  case Spadlink:
+  case Spadmemolink:
+  case Spaddownlink:
+  case Unixcommand:
+  case Spadcommand:
+  case Spadgraph:
+    if (des && node->link->win) {
+      hash_delete(gWindow->page->fLinkHashTable,(char *) &node->link->win);
+      XDestroyWindow(gXDisplay, node->link->win);
+    }
+    /* TTT don't free the link before freeing nodes off it */
+    /*	free_node(node->link->reference.node);*/
+    free_if_non_NULL(node->link);
+    free_node(node->next, des);
+    break;
+  case Free:
+  case Indent:
+  case Indentrel:
+  case HSpace:
+  case Space:
+  case VSpace:
+  case Button:
+  case Bound:
+  case Tab:
+    free_node(node->next, des);
+    free_node(node->data.node, des);
+    break;
+  case End:
+  case Endcenter:
+  case Endlink:
+  case Endgroup:
+  case Endbox:
+  case Endmbox:
+  case Endspadcommand:
+  case Endpix:
+  case Endmacro:
+  case Endparameter:
+  case Endtable:
+  case Endtableitem:
+  case Noop:
+  case Endinputbox:
+  case Enddescription:
+  case Endif:
+  case Endtitems:
+  case Enditems:
+  case Endverbatim:
+  case Endmath:
+  case Endspadsrc:
+    free_node(node->next, des);
+    break;
+  case Endheader:
+  case Endtitle:
+  case Endfooter:
+  case Endscrolling:
+  case Endarg:
+    break;
+  case Endbutton:
+  case Beginitems:
+    free_if_non_NULL(node->data.text);
+    free_node(node->next, des);
+    break;
+
+  default:
+
+    /*        printf("don't know how to free type %d\n", node->type); */
+    return;
+  }
+  free(node);
+}
+
+IfNode *
+alloc_ifnode(void)
+{
+  IfNode *tempif;
+
+  tempif = (IfNode *) halloc(sizeof(struct if_node), "IfNode");
+  tempif->thennode = tempif->elsenode = tempif->cond = NULL;
+  return tempif;
+}
+
+CondNode *
+alloc_condnode(void)
+{
+  CondNode *temp;
+
+  temp = (CondNode *) halloc(sizeof(struct cond_node), "Cond Node");
+  temp->cond = temp->label = NULL;
+  return temp;
+}
+
+static void
+free_cond(CondNode *cond)
+{
+  if (cond) {
+    free(cond->label);
+    if (cond->cond)
+      free(cond->cond);
+    free(cond);
+  }
+}
+
+/* Allocate a new HyperDoc page */
+
+HyperDocPage *
+alloc_page(char *name)
+{
+  HyperDocPage *page;
+
+  page = (HyperDocPage *) halloc(sizeof(HyperDocPage), "HyperDocPage");
+  page->name = name;
+  page->header = page->scrolling = page->footer = page->title = NULL;
+  page->scroll_off = 0;
+  page->sock = NULL;
+  page->box_hash = page->depend_hash = NULL;
+  page->fLinkHashTable = (HashTable *) halloc(sizeof(HashTable), "Page->fLinkHashTable");
+  page->input_list = page->current_item = NULL;
+  page->page_flags = 0000000;
+  page->filename = NULL;
+  page->helppage = alloc_string(TopLevelHelpPage);
+  page->radio_boxes = NULL;
+  page->button_list = NULL;
+  page->s_button_list = NULL;
+  return page;
+}
+
+void
+free_page(HyperDocPage *page)
+{
+  /*
+   * This routine now checks for an environment variable NOFREE. If found
+   * it returns.
+   */
+
+  if (page == NULL)
+    return;
+
+  switch (page->type) {
+  case UlUnknownPage:
+  case UnknownPage:
+  case ErrorPage:
+  case Unixfd:
+  case SpadGen:
+  case Normal:
+
+    /*
+     * if(page->name) free(page->name); if(page->filename)
+     * free(page->filename);
+     */
+    free_node(page->scrolling, 0);
+    free_node(page->header, 0);
+    free_node(page->footer, 0);
+    free_node(page->title, 0);
+    free_button_list(page->s_button_list);
+    free_button_list(page->button_list);
+/*
+     if (page->sock != NULL)
+      free(page->sock);
 */
+    free_hash(page->depend_hash, (FreeFunction)free_depend);
+    /* TTT line below causes freeing of freed memory and freed memory reads
+       links should have been freed by the recursive free_node's above (cf.free_node)
+       this is apparently because we are called from free_hd_window
+       and we had made a call to free w->fWindowHashTable which is made
+       to point to the same thing 
+       so we do it HERE not THERE
+       */
+    free_hash(page->fLinkHashTable, (FreeFunction)dont_free);
+    free_hash(page->box_hash, (FreeFunction)free_input_box);
+    free_input_list(page->input_list);
+    free_radio_boxes(page->radio_boxes);
+    free(page->helppage);
+    free(page);
+    break;
+  case UnloadedPageType:
+    break;
+  default:
+    /* fprintf(stderr, "Unknown Page type: %d\n", page->type); */
+    break;
+  }
+}
+
+static void
+free_paste(PasteNode *paste, short int des)
+{
+  if (paste) {
+    free_group_stack(paste->group);
+    free_item_stack(paste->item_stack);
+    free_node(paste->arg_node, des);
+    free(paste);
+  }
+}
+
+static void
+free_pastebutton(TextNode *node, short int des)
+{
+  /*
+   * if I am freeing from within parse patch, then I have to do some
+   * special things first
+   */
+
+
+  /* the following seems to be unused */
+  if (gActiveWindow == node->link->win)
+    gActiveWindow = -1;
+
+
+
+  if (des) {
+    PasteNode *paste;
+    paste = (PasteNode *) hash_find(gWindow->fPasteHashTable, node->data.text);
+
+    if (!paste->haspaste) {
+      /* squash this thing */
+
+      hash_delete(gWindow->fPasteHashTable, (char *)node->data.text);
+      free_paste(paste, des);
+      hash_delete(gWindow->page->fLinkHashTable,(char *) &node->link->win);
+
+      XDestroyWindow(gXDisplay, node->link->win);
+    }
+    else
+      paste->hasbutton = 0;
+  }
+
+  free_if_non_NULL(node->data.text);
+
+}
+
+static void
+free_pastearea(TextNode *node, short int des)
+{
+  if (des) {
+    PasteNode *paste;
+    paste = (PasteNode *) hash_find(gWindow->fPasteHashTable, node->data.text);
+    if (paste) {
+      if (!paste->hasbutton) {
+	/* squash this thing */
+	hash_delete(gWindow->fPasteHashTable, node->data.text);
+	free_paste(paste, des);
+      }
+      else
+	paste->haspaste = 0;
+    }
+  }
+
+  free_if_non_NULL(node->data.text);
+}
+
+
+void
+free_string(char *str)
+{
+  free_if_non_NULL(str);
+}
+
+static void
+free_depend(SpadcomDepend *sd)
+{
+  free_if_non_NULL((char *) sd);
+}
+
+static void
+dont_free(void  *link)
+{
+  return;
+}
+
+#if 0 
+----------- NOT USED
+static void
+free_link(HyperLink *link)
+{
+  printf("Link type %d\n",link->type);
+  free_if_non_NULL((char *) link);
+}
+----------- NOT USED
+#endif
+
+static void
+free_lines(LineStruct *lines)
+{
+  if (lines->prev != NULL)
+    lines->prev->next = NULL;
+  while (lines != NULL) {
+    LineStruct *del;
+    del = lines;
+    lines = lines->next;
+    free(del->buffer);
+    free(del);
+  }
+}
+
+void
+free_input_item(InputItem *sym, short int des)
+{
+  free_if_non_NULL(sym->name);
+  free_lines(sym->lines);
+  if (des)
+    XDestroyWindow(gXDisplay, sym->win);
+}
+
+void
+free_input_list(InputItem *il)
+{
+  while (il) {
+    InputItem *trash = il;
+    il = il->next;
+    free_input_item(trash, 0);
+    free(trash);
+  }
+}
+
+static void
+free_input_box(InputBox *box)
+{
+  if (box) {
+    free_if_non_NULL(box->name);
+    free(box);
+  }
+}
+
+static void
+free_radio_boxes(RadioBoxes *radio)
+{
+  if (radio) {
+    free_radio_boxes(radio->next);
+    free_if_non_NULL(radio->name);
+    free(radio);
+  }
+}
+
+#if 0
+--------------- NOT USED
+static void
+free_image(ImageStruct *image)
+{
+  free(image->image.xi->data);
+  free(image->image.xi);
+  free(image);
+}
+--------------- NOT USED
+#endif
+
+#if 0
+--------------- NOT USED
+static void
+free_macro(MacroStore *macro)
+{
+  if (macro) {
+    free(macro->name);
+    if (macro->macro_string)
+      free(macro->macro_string);
+    free(macro);
+  }
+}
+--------------- NOT USED
+#endif 
+
+
+
+LineStruct *
+alloc_inputline(int size)
+{
+  int i;
+  LineStruct *line =
+    (LineStruct *) halloc(sizeof(LineStruct), "Line Structure");
+
+  line->prev = line->next = NULL;
+  line->buffer = (char *) halloc(sizeof(char) * size + 2, "symbol buffer");
+  for (i = 0; i < size + 2; i++)
+    line->buffer[i] = 0;
+  line->buff_pntr = line->len = 0;
+  return line;
+}
+
+PasteNode *
+alloc_paste_node(char *name)
+{
+  PasteNode *pastenode =
+    (PasteNode *) halloc(sizeof(PasteNode), "PasteNode");
+
+  pastenode->group = NULL;
+  pastenode->item_stack = NULL;
+  pastenode->arg_node = NULL;
+  pastenode->end_node = NULL;
+  pastenode->name = alloc_string(name);
+  pastenode->haspaste = pastenode->hasbutton = 0;
+  return pastenode;
+}
+
+PatchStore *
+alloc_patchstore(void)
+{
+  PatchStore *p = (PatchStore *) halloc(sizeof(PatchStore), "PatchStore");
+
+  p->loaded = 0;
+  p->string = NULL;
+  return p;
+}
+
+void
+free_patch(PatchStore *p)
+{
+  if (p) {
+    if (p->name)
+      free(p->name);
+    if (p->fpos.name)
+      free(p->fpos.name);
+    if (p->string)
+      free(p->string);
+    free(p);
+  }
+}
+
+InputBox *
+alloc_inputbox(void)
+{
+  InputBox *box = (InputBox *) halloc(sizeof(InputBox), "InputBox");
+
+  box->picked = 0;
+  box->next = NULL;
+  box->rbs = NULL;
+  return box;
+}
+
+RadioBoxes *
+alloc_rbs(void)
+{
+  RadioBoxes *newrb = (RadioBoxes *) halloc(sizeof(RadioBoxes), "Radio Boxes");
+
+  newrb->next = NULL;
+  newrb->boxes = NULL;
+  return newrb;
+}
+
+ButtonList *
+alloc_button_list(void)
+{
+  ButtonList *newbl = (ButtonList *) halloc(sizeof(ButtonList), "Button List");
+
+  newbl->link = NULL;
+  newbl->x0 = newbl->y0 = newbl->x1 = newbl->y1 = 0;
+  newbl->next = NULL;
+  return newbl;
+}
+
+void
+free_button_list(ButtonList *bl)
+{
+  while (bl) {
+    ButtonList *nbl = bl->next;
+    free(bl);
+    bl = nbl;
+  }
+}
+
+
+/* resizable static buffers */
+
+#define BufferSlop      0
+
+char *
+resizeBuffer(int size, char *oldBuf, int *oldSize)
+{
+  char *newBuf;
+  int newSize;
+
+  if (size <= *oldSize)
+    return oldBuf;
+
+  newSize = size + BufferSlop;
+  newBuf = (char *) halloc(newSize,"Buffer");
+  memset(newBuf,'\0',newSize);
+  if (oldBuf) {
+    memcpy(newBuf, oldBuf, *oldSize);
+    free(oldBuf);
+  }
+  *oldSize = newSize;
+
+  return newBuf;
+}
+@
+\section{parse-aux.h}
+<<parse-aux.h>>=
+#ifndef _PARSE_AUX_H_
+#define _PARSE_AUX_H_ 1
+
+<<hyper.h>>
 
+
+#endif
 @
-<<*>>=
-<<license>>
-<<hyper.c>>
+\section{parse-aux.c}
+<<parse-aux.c>>=
+#define _PARSE_AUX_C
+#include "debug.h"
+
+<<parse.h>>
+<<parse-aux.h>>
+<<addfile.h>>
+<<lex.h>>
+<<mem.h>>
+
+#include "all-hyper-proto.h1"
+
+
+extern FILE *cfile;
+extern int make_input_file;
+extern int gverify_dates;
+
+InputBox *rb_list;
+InputBox *end_rb_list;
+
+HashTable ht_gFileHashTable;
+
+#define htfhSize 100
+
+/* Hash functions for active link windows */
+
+int
+window_equal(Window *w1, Window *w2)
+{
+    return *w1 == *w2;
+}
+
+/* hash code for a window */
+
+int
+window_code(Window *w, int size)
+{
+    return (*w) % size;
+}
+
+char *
+window_id(Window w)
+{
+    char *ret;
+    char buff[32];
+    int length;
+
+    sprintf(buff, "%ld", w);
+    length = strlen(buff);
+    ret = (char *) halloc(length * sizeof(char) + 1, "windowid");
+    strcpy(ret, buff);
+    return (ret);
+}
+
+/*
+ * This procedure reads the ht database. It makes repeated calls to
+ * db_file_open, and while the returned pointer is not null, it continues to
+ * read the presented data base files
+ */
+void
+read_ht_db(HashTable *page_hash, HashTable *macro_hash, HashTable *patch_hash)
+{
+    FILE *db_fp;
+    char db_file[256];
+    int i = 0;
+
+    gDatabasePath = NULL;
+
+    hash_init(
+	      page_hash, 
+	      PageHashSize, 
+	      (EqualFunction) string_equal, 
+	      (HashcodeFunction) string_hash);
+    hash_init(
+	      macro_hash, 
+	      MacroHashSize, 
+	      (EqualFunction) string_equal, 
+	      (HashcodeFunction) string_hash);
+    hash_init(
+	      patch_hash, 
+	      PatchHashSize, 
+	      (EqualFunction) string_equal, 
+	      (HashcodeFunction) string_hash);
+
+    /* Lets initialize the FileHashTable         */
+    hash_init(
+	      &ht_gFileHashTable, 
+	      htfhSize, 
+	      (EqualFunction) string_equal, 
+	      (HashcodeFunction) string_hash);
+
+    while ((db_fp = db_file_open(db_file)) != NULL) {
+        i++;
+        read_ht_file(page_hash, macro_hash, patch_hash, db_fp, db_file);
+        fclose(db_fp);
+    }
+
+    if (!i) {
+        fprintf(stderr, 
+          "(HyperDoc) read_ht_db: No %s file found\n", db_file_name);
+        exit(-1);
+    }
+
+    free_hash(&ht_gFileHashTable, (FreeFunction)free_string);
+}
+
+/*
+ * This procedure reads a single HyperDoc database file. It is passed an already
+ * initilaized file pointer. It reads the whole file, updating the
+ * page hash, or the macro hash only when a previous entry with the same name
+ * is not found
+ */
+
+static void
+read_ht_file(HashTable *page_hash, HashTable *macro_hash, 
+             HashTable *patch_hash, FILE *db_fp, char *db_file)
+{
+    char filename[256];
+    char *fullname = filename;
+    UnloadedPage *page;
+    MacroStore *macro;
+    PatchStore *patch;
+    int pages = 0, c, mtime, ret_val;
+    struct stat fstats;
+    /*short time_ok = 1;*/
+/*    fprintf(stderr,"parse-aux:read_ht_file: dp_file=%s\n",db_file);*/
+    cfile = db_fp;
+    init_scanner();
+    ret_val = strlen(db_file) - 1;
+    for (; ret_val >= 0; ret_val--)
+        if (db_file[ret_val] == '/') {
+            db_file[ret_val] = '\0';
+            break;
+        }
+    c = getc(db_fp);
+    do {
+        if (c == '\t') {
+            get_filename();
+            fullname = alloc_string(token.id);
+            if (fullname[0] != '/') {
+                strcpy(filename, db_file);
+                strcat(filename, "/");
+                strcat(filename, fullname);
+                free(fullname);
+                fullname = alloc_string(filename);
+            }
+
+            /*
+             * Until I get a filename that I have not seen before, just keep
+             * reading
+             */
+            while (hash_find(&ht_gFileHashTable, fullname) != NULL) {
+                do {
+                    c = getc(db_fp);
+                } while ((c != EOF) && (c != '\t'));
+                if (c == EOF)
+                    return;
+                get_filename();
+                fullname = alloc_string(token.id);
+                if (fullname[0] != '/') {
+                    strcpy(filename, db_file);
+                    strcat(filename, "/");
+                    strcat(filename, fullname);
+                    free(fullname);
+                    fullname = alloc_string(filename);
+                }
+            }
+/*          fprintf(stderr,"parse-aux:read_ht_file: fullname=%s\n",fullname);*/
+            /* If I got here, then I must have a good filename  */
+            hash_insert(&ht_gFileHashTable, fullname, fullname);
+
+            ret_val = stat(fullname, &fstats);
+            if (ret_val == -1) {
+                char buffer[300];
+
+                sprintf(buffer, "(HyperDoc) read_ht_db: Unable To Open %s :", fullname);
+                perror(buffer);
+                exit(-1);
+            }
+            get_token();
+            mtime = atoi(token.id);
+            if (gverify_dates & (fstats.st_mtime > mtime)) {
+                fprintf(stderr, "(HyperDoc) read_ht_file: HyperDoc file %s has been updated\n",
+
+                        fullname);
+                fprintf(stderr, "(HyperDoc) Issue htadd %s to update database\n", fullname);
+                exit(-1);
+            }
+            while ((c = getc(db_fp)) != EOF) {
+                if (c == '\t')
+                    break;
+                ungetc(c, db_fp);
+                get_token();
+                switch (token.type) {
+                  case Page:
+                    get_token();
+
+                    /*
+                     * now check to see if the page has already been
+                     * loaded
+                     */
+                    page = (UnloadedPage *) halloc(sizeof(UnloadedPage),
+                                                   "UnloadedPage");
+                    page->fpos.name = alloc_string(fullname);
+                    page->name = alloc_string(token.id);
+                    get_token();
+                    if (hash_find(page_hash, page->name) != NULL) {
+                        fprintf(stderr, "(HyperDoc) Page name %s  occurred twice\n", page->name);
+                        fprintf(stderr, "(HyperDoc) The Version in %s is being ignored \n",
+                                page->fpos.name);
+                        free(page);
+                        get_token();
+                        break;
+                    }
+                    page->fpos.pos = atoi(token.id);
+                    get_token();
+                    page->fpos.ln = atoi(token.id);
+                    page->type = UnloadedPageType;
+                    hash_insert(page_hash, (char *)page, page->name);
+                    pages++;
+                    break;
+                  case NewCommand:
+                    get_token();
+                    macro = (MacroStore *) halloc(sizeof(MacroStore), "MacroStore");
+                    macro->fpos.name = alloc_string(fullname);
+                    macro->name = alloc_string(token.id);
+                    macro->macro_string = NULL;
+                    get_token();
+                    if (hash_find(macro_hash, macro->name) != NULL) {
+                        if (strcmp(macro->name, "localinfo") != 0) {
+                            fprintf(stderr, "(HyperDoc) Macro name %s  occurred twice\n",
+                                    macro->name);
+                            fprintf(stderr, "(HyperDoc) The Version in %s is being ignored \n",
+                                    macro->fpos.name);
+                        }
+                        get_token();
+                        free(macro);
+                        break;
+                    }
+                    macro->fpos.pos = atoi(token.id);
+                    get_token();
+                    macro->fpos.ln = atoi(token.id);
+                    macro->loaded = 0;
+                    hash_insert(macro_hash, (char *)macro, macro->name);
+                    break;
+                  case Patch:
+                    get_token();
+                    patch = (PatchStore *) alloc_patchstore();
+                    patch->fpos.name = alloc_string(fullname);
+                    patch->name = alloc_string(token.id);
+                    get_token();
+                    patch->fpos.pos = atoi(token.id);
+                    get_token();
+                    patch->fpos.ln = atoi(token.id);
+                    if (hash_find(patch_hash, patch->name) != NULL) {
+                        fprintf(stderr, "(HyperDoc) Patch name %s  occurred twice\n", patch->name);
+                        fprintf(stderr, "(HyperDoc) The version in %s is being ignored \n",
+                                patch->fpos.name);
+                        free_patch(patch);
+                        break;
+                    }
+                    hash_insert(patch_hash, (char *)patch, patch->name);
+                    break;
+                  default:
+                    fprintf(stderr, "(HyperDoc) read_ht_db: Unknown type %s in ht.db\n", token.id);
+                    exit(-1);
+                    break;
+                }
+            }
+        }
+        else
+            c = getc(db_fp);
+    } while (c != EOF);
+/*    fprintf(stderr,
+     "parse-aux:read_ht_file:read %d pages from database\n", pages); */
+}
+
+
+/* create an unmapped input-only window for an active screen area */
+
+HyperLink *
+make_link_window(TextNode *link_node, int type, int isSubWin)
+{
+    HyperLink *link;
+    XSetWindowAttributes at;
+
+    if (make_input_file)
+        switch (type) {
+          case Downlink:
+          case Memolink:
+          case Windowlink:{
+                char *name;
+                HyperDocPage *p;
+
+                name = print_to_string(link_node);
+                p = (HyperDocPage *) hash_find(gWindow->fPageHashTable, name);
+                if (!p)
+                    printf("undefined link to %s\n", name);
+                break;
+            }
+        }
+    else {
+        link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+        if (link == NULL) {
+            fprintf(stderr, "(HyperDoc) Ran out of memory allocating a hypertext link!\n");
+            exit(-1);
+        }
+        at.cursor = gActiveCursor;
+        at.event_mask = ButtonPress;
+        if (isSubWin)
+            link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow, 0, 0, 100, 100, 0,
+                                      0, InputOnly, CopyFromParent,
+                                      CWEventMask | CWCursor, &at);
+        else
+            link->win = 0;
+        link->type = type;
+        link->x = link->y = 0;
+        link->reference.node = link_node;
+        hash_insert(gLinkHashTable, (char *)link,(char *)&link->win);
+        return link;
+    }
+    return 0;
+}
+
+HyperLink *
+make_paste_window(PasteNode *paste)
+{
+    HyperLink *link;
+    XSetWindowAttributes at;
+
+    if (!make_input_file) {
+        link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+        if (link == NULL) {
+            fprintf(stderr, "(HyperDoc) Ran out of memory allocating a hypertext link!\n");
+            exit(-1);
+        }
+        at.cursor = gActiveCursor;
+        at.event_mask = ButtonPress;
+        link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow,
+                                  0, 0, 100, 100, 0,
+                                  0, InputOnly, CopyFromParent,
+                                  CWEventMask | CWCursor, &at);
+        link->type = Pastebutton;
+        link->x = link->y = 0;
+        link->reference.paste = paste;
+        hash_insert(gLinkHashTable, (char *)link,(char *) &link->win);
+        return link;
+    }
+    return 0;
+}
+
+
+
+/* create a HyperDoc page structure with the given type and name */
+
+static HyperDocPage *
+make_special_page(int type, char *name)
+{
+    HyperDocPage *page = alloc_page(name);
+
+    if (page == NULL) {
+        fprintf(stderr, "(HyperDoc) Ran out of memory allocating page.\n");
+        exit(-1);
+    }
+    page->type = type;
+    free(page->fLinkHashTable);
+    page->fLinkHashTable = NULL;
+    return page;
+}
+
+
+/* insert the special button page types into the page hash table */
+
+void
+make_special_pages(HashTable *pageHashTable)
+{
+    hash_insert(pageHashTable, (char *)make_special_page(Quitbutton, "QuitPage"),
+                "QuitPage");
+    hash_insert(pageHashTable, (char *)make_special_page(Returnbutton, "ReturnPage"),
+                "ReturnPage");
+    hash_insert(pageHashTable, (char *)make_special_page(Upbutton, "UpPage"),
+                "UpPage");
+}
+
+
+/* Here is where I put the item into the pages linked list */
+
+/* Parse the \bound{varlist} command, and add vars to dependency table */
+
+void
+add_dependencies(void)
+{
+    TextNode *bound_node = curr_node;
+    TextNode *node;
+    SpadcomDepend *depend;
+
+    if (cur_spadcom == NULL) {
+        fprintf(stderr, "(HyperDoc) \\bound occuring outside a \\spadcom\n");
+        print_page_and_filename();
+        exit(-1);
+    }
+    curr_node->type = Bound;
+    curr_node->data.node = alloc_node();
+    curr_node = curr_node->data.node;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+    curr_node = bound_node;
+
+    if (gPageBeingParsed->depend_hash == NULL) {
+        gPageBeingParsed->depend_hash =
+            (HashTable *) halloc(sizeof(HashTable), "Hash Table");
+        hash_init(
+		  gPageBeingParsed->depend_hash, 
+		  DependHashSize,
+                  (EqualFunction) string_equal, 
+		  (HashcodeFunction) string_hash);
+    }
+    for (node = bound_node->data.node; node->type != Endarg; node = node->next) {
+        if (node->type == Word) {
+            depend = (SpadcomDepend *) halloc(sizeof(SpadcomDepend), "SpadcomDepend");
+            depend->label = alloc_string(node->data.text);
+            depend->spadcom = cur_spadcom;
+            depend->executed = 0;
+            hash_insert(gPageBeingParsed->depend_hash, (char *)depend, depend->label);
+        }
+    }
+}
+
+/* Returns true iff the TextNode contains a single integer */
+
+int
+is_number(char * str)
+{
+    char *s;
+
+    for (s = str; *s != '\0'; s++) {
+        if (!(isdigit(*s) || *s == '-'))
+            return 0;
+    }
+    return 1;
+}
+void
+parser_error(char *str)
+{
+    /** this procedure is called by the parser when an error occurs. It prints
+      the error message, followed by the next 10 tokens to ease finding the
+      error for the user.                                               *****/
+
+    int i, v;
+
+    fprintf(stderr, " %s\n", str);
+    fprintf(stderr, "Here are the next 10 tokens:\n");
+    for (i = 0; i < 10; i++) {
+        v = get_token();
+        if (v == EOF)
+            break;
+        print_token();
+    }
+    fprintf(stderr, "\n");
+    exit(-1);
+}
+
+
+#define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define delim(c) \
+  (whitespace(c))
+
+
+/* advance token to the next token in the input stream.  */
+int
+get_filename(void)
+{
+    int c, ws;
+    static int seen_white = 0; /*UNUSED */
+    static char buffer[256];
+    char *buf = buffer;
+
+    if (last_token) {
+        last_token = 0;
+        return 0;
+    }
+    do {
+        keyword_fpos = fpos;
+        c = get_char();
+        ws = whitespace(c);
+        if (ws)
+            seen_white = 1;
+    } while (ws);
+    switch (c) {
+      case EOF:
+        fprintf(stderr, "(HyperDoc) Error trying to read %s, unexpected end-of-file.\n",db_file_name);
+        exit(-1);
+      case '%':
+      case '\\':
+      case '{':
+      case '}':
+        fprintf(stderr, "(HyperDoc) Error unexpected character %c.\n",c);
+        exit(-1);
+      default:
+        do {
+            *buf++ = c;
+        } while ((c = get_char()) != EOF && !delim(c));
+        unget_char(c);
+        *buf = '\0';
+        token.type = Word;
+        token.id = buffer;
+        seen_white = 0;
+        break;
+    }
+    return 1;
+}
+
+char *
+get_input_string(void)
+{
+    char *string;
+    TextNode *string_node,*save_node;
+
+    save_node = curr_node;
+    /* Get the nodes that make up the string */
+    string_node = alloc_node();
+    curr_node = string_node;
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+
+    /* Once here we print to string to get the actual name */
+    string = print_to_string(string_node);
+    free_node(string_node, 0);
+    curr_node=save_node;
+    return string;
+}
+
+/*
+ * tries to determine if there is an optional argument for where I should be
+ * parsing from. If so it then tries to determine which
+ */
+int
+get_where(void)
+{
+    int tw;
+
+    get_token();
+    if (token.type != Word)
+        return -1;
+
+    /* Now try to determine if it is a good type */
+    if (!strcmp(token.id, "lisp")) {
+        tw = FromSpadSocket;
+    }
+    else if (!strcmp(token.id, "unix")) {
+        tw = FromUnixFD;
+    }
+    else if (!strcmp(token.id, "ht")) {
+        tw = FromFile;
+    }
+    else {
+        return -1;
+    }
+
+    /* now check to see if I got a closing square brace */
+    get_token();
+    if (token.type != Rsquarebrace)
+        return -1;
+
+    return tw;
+}
+
+
+FILE *
+find_fp(FilePosition fp)
+{
+    FILE *lfile;
+    char fullname[256], addname[256];
+    int ret_val;
+
+    /* find the source file in the file hash table, if not there, open it */
+    lfile = (FILE *) hash_find(&gFileHashTable, fp.name);
+    if (lfile == NULL) {
+        lfile = ht_file_open(fullname, addname, fp.name);
+        hash_insert(&gFileHashTable, (char *)lfile, fp.name);
+    }
+
+    /* seek to beginning fp.pos */
+    ret_val = fseek(lfile, fp.pos, 0);
+    if (ret_val == -1) {
+        perror("fseeking to a page");
+        longjmp(jmpbuf, 1);
+    }
+
+    /* now set some global values */
+    page_start_fpos = fp.pos;
+    line_number = fp.ln;
+    return lfile;
+}
+@
+\section{parse-input.c}
+<<parse-input.c>>=
+#define _PARSE_INPUT_C
+/***
+  Contains all the code needed to parse input items,
+  InputString
+  SimpleBox
+  RadioBox.
+  ****/
+
+#include "debug.h"
+
+<<parse.h>>
+<<parse-aux.h>>
+<<lex.h>>
+<<mem.h>>
+
+#include "all-hyper-proto.h1"
+
+/* create an unmapped input window for getting strings * */
+extern int make_input_file;
+
+HyperLink *
+make_input_window(InputItem * item)
+{
+  HyperLink *link;
+  XSetWindowAttributes at;
+
+  if (!make_input_file) {
+    link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+    if (link == NULL) {
+      fprintf(stderr, "Ran out of memory allocating a hyper link!\n");
+      exit(-1);
+    }
+    at.cursor = gActiveCursor;
+    at.background_pixel = gInputBackgroundColor;
+    at.border_pixel = gActiveColor;
+    link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow, 0, 0, 100, 100, 0,
+			      0, InputOutput, CopyFromParent,
+			      CWCursor | CWBackPixel | CWBorderPixel, &at);
+    XSelectInput(gXDisplay, link->win, ButtonPressMask);
+    link->type = Inputstring;
+    link->x = link->y = 0;
+    /** This way when I click in an input window, I need only use reference
+      to get a pointer to the item                             ***/
+    link->reference.string = item;
+    hash_insert(gLinkHashTable,(char *) link,(char *) &link->win);
+
+    return link;
+  }
+  return 0;
+}
+
+/* create an unmapped input window for boxes */
+HyperLink *
+make_box_window(InputBox * box, int type)
+{
+  HyperLink *link = 0;
+  XSetWindowAttributes at;
+
+  if (!make_input_file) {
+    link = (HyperLink *) halloc(sizeof(HyperLink), "Make_box_window");
+    if (link == NULL) {
+      fprintf(stderr, "Ran out of memory allocating a hyper link!\n");
+      exit(-1);
+    }
+    at.cursor = gActiveCursor;
+    at.background_pixel = gInputBackgroundColor;
+    link->win = XCreateWindow(gXDisplay, gWindow->fDisplayedWindow,
+			      0, 0, 100, 100, 0,
+			      0, InputOutput, CopyFromParent,
+			      CWCursor | CWBackPixel, &at);
+    XSelectInput(gXDisplay, link->win, ButtonPressMask);
+    link->type = type;
+    link->x = link->y = 0;
+    /** This way when I click in an input window, I need only use reference
+      to get a pointer to the item                             ***/
+    link->reference.box = box;
+    hash_insert(gLinkHashTable, (char *)link,(char *) &link->win);
+  }
+
+  return link;
+}
+
+void
+initialize_default(InputItem *item,char * buff)
+{
+  LineStruct *newline;
+  LineStruct *curr_line;
+  int size = item->size;
+  int bp;
+
+  item->curr_line = item->lines = alloc_inputline(size);
+  curr_line = item->lines;
+  item->num_lines = 1;
+  curr_line->line_number = 1;
+  /* while I still have lines to fill */
+  for (bp = 0; *buff;) {
+    if (*buff == '\n') {
+      curr_line->len = bp;
+      curr_line->buffer[bp] = 0;
+      newline = alloc_inputline(size);
+      newline->line_number = ++(item->num_lines);
+      curr_line->next = newline;
+      newline->prev = curr_line;
+      curr_line = newline;
+      bp = 0;
+      buff++;
+    }
+    else if (bp == size) {
+      curr_line->len = size + 1;
+      curr_line->buffer[size] = '_';
+      curr_line->buffer[size + 1] = 0;
+      newline = alloc_inputline(size);
+      newline->line_number = ++(item->num_lines);
+      curr_line->next = newline;
+      newline->prev = curr_line;
+      bp = 0;
+      curr_line = newline;
+    }
+    else {
+      curr_line->buffer[bp++] = *buff++;
+    }
+  }
+  curr_line->buff_pntr = curr_line->len = bp;
+  item->curr_line = curr_line;
+}
+
+
+
+/* Parse the input string statement * */
+void
+parse_inputstring(void)
+{
+  TextNode *input_node = curr_node;
+  char *name;
+  InputItem *item;
+  int size;
+  char *default_value;
+
+  gStringValueOk = 0;
+
+  /* first get the name */
+  input_node->type = token.type;
+  get_expected_token(Lbrace);
+  name = get_input_string();
+  input_node->data.text = alloc_string(name);
+  /* now get the width */
+  get_expected_token(Lbrace);
+  get_expected_token(Word);
+  get_expected_token(Rbrace);
+  size = atoi(token.id);
+  if (size < 0) {
+    fprintf(stderr, "Illegal size in Input string\n");
+    longjmp(jmpbuf, 1);
+  }
+
+  /* get the default value */
+  get_expected_token(Lbrace);
+  default_value = get_input_string();
+
+  /** now I need to malloc space for the input stuff **/
+  item = (InputItem *) halloc(sizeof(InputItem), "InputItem");
+
+  /* Now store all the string info */
+  item->name = (char *)
+    halloc((strlen(input_node->data.text) + 1) * (sizeof(char)),"parse_inputstring");
+  strcpy(item->name, input_node->data.text);
+  item->size = size;
+  item->entered = 0;
+  item->next = NULL;
+  initialize_default(item, default_value);
+
+  /** Now that I have all the structures made, lets make the window, and
+    add the item to the list                                 ****/
+
+  input_node->link = make_input_window(item);
+  if (!make_input_file)
+    item->win = input_node->link->win;      /* TTT */
+  insert_item(item);
+  gStringValueOk = 1;
+  curr_node = input_node;
+  return ;
+}
+
+void
+parse_simplebox(void)
+{
+  InputBox *box;
+  char *name;
+  short int picked = 0;
+  char *filename;
+  TextNode *input_box = curr_node;
+
+  gStringValueOk = 0;
+
+  /* set the type and space fields  */
+  input_box->type = SimpleBox;
+  input_box->space = token.id[-1];
+
+  /* IS it selected? */
+  get_token();
+  if (token.type == Lsquarebrace) {
+    get_expected_token(Word);
+    if (!is_number(token.id)) {
+      fprintf(stderr,
+	      "parse_simple_box: Expected a value not %s\n", token.id);
+      print_page_and_filename();
+      jump();
+    }
+    else if (!strcmp(token.id, "1"))
+      picked = 1;
+    else if (!strcmp(token.id, "0"))
+      picked = 0;
+    else {
+      fprintf(stderr, "parse_simple_box: Unexpected Value %s\n", token.id);
+      print_page_and_filename();
+      jump();
+    }
+    get_expected_token(Rsquarebrace);
+    get_token();
+  }
+
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+
+  name = get_input_string();
+  if (gPageBeingParsed->box_hash && hash_find(gPageBeingParsed->box_hash, name)) {
+    fprintf(stderr, "Input box name %s is not unique \n", name);
+    print_page_and_filename();
+    jump();
+  }
+
+  box = alloc_inputbox();
+  box->name = alloc_string(name);
+  input_box->data.text = alloc_string(name);
+  box->picked = picked;
+
+  /* Get the filename for the selected and unselected bitmaps */
+  get_expected_token(Lbrace);
+  filename = get_input_string();
+  if (!make_input_file)
+    box->selected = insert_image_struct(filename);
+  get_expected_token(Lbrace);
+  filename = get_input_string();
+  if (!make_input_file) {
+    box->unselected = insert_image_struct(filename);
+    /* set the width and height for the maximaum of the two */
+    input_box->height = max(box->selected->height, box->unselected->height);
+    input_box->width = max(box->selected->width, box->unselected->width);
+    /* Make the window and stuff */
+    input_box->link = make_box_window(box, SimpleBox);
+    box->win = input_box->link->win;
+
+    /* Now add the box to the box_has table for this window */
+    if (gPageBeingParsed->box_hash == NULL) {
+      gPageBeingParsed->box_hash = (HashTable *) halloc(sizeof(HashTable),
+							"Box Hash");
+      hash_init(
+		gPageBeingParsed->box_hash, 
+		BoxHashSize, 
+		(EqualFunction) string_equal, 
+		(HashcodeFunction) string_hash);
+    }
+    hash_insert(gPageBeingParsed->box_hash, (char *)box, box->name);
+  }
+
+  /* reset the curr_node and then return */
+  curr_node = input_box;
+  gStringValueOk = 1;
+  return;
+}
+void
+parse_radiobox(void)
+{
+  InputBox *box;
+  char *name;
+  char *group_name;
+  short int picked = 0;
+  TextNode *input_box = curr_node;
+
+  gStringValueOk = 0;
+
+  /* set the type and space fields  */
+  input_box->type = Radiobox;
+  input_box->space = token.id[-1];
+
+  /* IS it selected? */
+  get_token();
+  if (token.type == Lsquarebrace) {
+    get_expected_token(Word);
+    if (!is_number(token.id)) {
+      fprintf(stderr,
+	      "parse_simple_box: Expected a value not %s\n", token.id);
+      print_page_and_filename();
+      jump();
+    }
+    else if (!strcmp(token.id, "1"))
+      picked = 1;
+    else if (!strcmp(token.id, "0"))
+      picked = 0;
+    else {
+      fprintf(stderr, "parse_simple_box: Unexpected Value %s\n", token.id);
+      print_page_and_filename();
+      jump();
+    }
+    get_expected_token(Rsquarebrace);
+    get_token();
+  }
+
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+
+  name = get_input_string();
+  if (gPageBeingParsed->box_hash && hash_find(gPageBeingParsed->box_hash, name)) {
+    fprintf(stderr, "Input box name %s is not unique \n", name);
+    print_page_and_filename();
+    jump();
+  }
+
+  box = alloc_inputbox();
+  box->name = alloc_string(name);
+  input_box->data.text = alloc_string(name);
+  box->picked = picked;
+
+  /* Now what I need to do is get the group name */
+  get_token();
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "parse_inputbox was expecting a { not a %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+  group_name = get_input_string();
+
+  /*
+   * Now call a routine which searches the radio box list for the current
+   * group name, and if found adds this box to it
+   */
+  add_box_to_rb_list(group_name, box);
+
+  input_box->width = box->rbs->width;
+  input_box->height = box->rbs->height;
+  /* Make the window and stuff */
+  input_box->link = make_box_window(box, Radiobox);
+  if (!make_input_file)
+    box->win = input_box->link->win;        /* TTT */
+
+
+  /* Now add the box to the box_has table for this window */
+  if (gPageBeingParsed->box_hash == NULL) {
+    gPageBeingParsed->box_hash = (HashTable *) halloc(sizeof(HashTable),
+						      "Box Hash");
+    hash_init(
+	      gPageBeingParsed->box_hash, 
+	      BoxHashSize, 
+	      (EqualFunction) string_equal, 
+	      (HashcodeFunction) string_hash);
+  }
+  hash_insert(gPageBeingParsed->box_hash, (char *)box, box->name);
+
+  /* reset the curr_node and then return */
+  curr_node = input_box;
+  gStringValueOk = 1;
+  return;
+}
+static void
+add_box_to_rb_list(char *name,InputBox *box)
+{
+  RadioBoxes *trace = gPageBeingParsed->radio_boxes;
+  InputBox *list;
+  /*int found = 0;*/
+
+  while (trace != NULL && strcmp(trace->name, name))
+    trace = trace->next;
+
+  if (!trace) {
+    fprintf(stderr, "Tried to add a radio box to a non-existent group %s\n",
+	    name);
+    print_page_and_filename();
+    jump();
+  }
+
+  /* now add the box to the list */
+  list = trace->boxes;
+  box->next = list;
+  trace->boxes = box;
+
+  if (box->picked && check_others(box->next)) {
+    fprintf(stderr, "Only a single radio button can be picked\n");
+    print_page_and_filename();
+    box->picked = 0;
+  }
+  box->selected = trace->selected;
+  box->unselected = trace->unselected;
+  box->rbs = trace;
+
+  return;
+}
+static int
+check_others(InputBox *list)
+{
+  InputBox *trace = list;
+
+  while (trace != NULL && !trace->picked)
+    trace = trace->next;
+
+  if (trace != NULL)
+    return 1;
+  else
+    return 0;
+}
+
+
+/* inserts an item into the current input list */
+static void
+insert_item(InputItem *item)
+{
+  InputItem *trace = gPageBeingParsed->input_list;
+
+  if (gPageBeingParsed->current_item == NULL) {
+    gPageBeingParsed->current_item = item;
+  }
+  if (trace == NULL) {
+    /** Insert at the front of the list **/
+    gPageBeingParsed->input_list = item;
+    return;
+  }
+  else {
+    /** find the end of the list **/
+    while (trace->next != NULL)
+      trace = trace->next;
+    trace->next = item;
+    return;
+  }
+}
+
+InputItem *save_item;
+
+void
+init_paste_item(InputItem *item)
+{
+  InputItem *trace = gPageBeingParsed->input_list;
+
+  if (!item) {
+    gPageBeingParsed->input_list = NULL;
+    gPageBeingParsed->current_item = NULL;
+    save_item = NULL;
+  }
+  else {
+    save_item = item->next;
+    trace->next = NULL;
+  }
+}
+void
+repaste_item(void)
+{
+  InputItem *trace;
+
+  if (save_item) {
+    for (trace = gPageBeingParsed->input_list; trace && trace->next != NULL;
+	 trace = trace->next);
+    if (trace) {
+      trace->next = save_item;
+    }
+    else {
+      gWindow->page->input_list = save_item;
+      gWindow->page->current_item = save_item;
+    }
+  }
+  save_item = NULL;
+}
+
+InputItem *
+current_item(void)
+{
+  InputItem *trace = gPageBeingParsed->input_list;
+
+  if (trace) {
+    for (; trace->next != NULL; trace = trace->next);
+    return trace;
+  }
+  else
+    return NULL;
+}
+int
+already_there(char *name)
+{
+  RadioBoxes *trace = gPageBeingParsed->radio_boxes;
+
+  while (trace && strcmp(trace->name, name))
+    trace = trace->next;
+
+  if (trace)
+    return 1;
+  else
+    return 0;
+}
+void
+parse_radioboxes(void)
+{
+  TextNode *return_node = curr_node;
+  RadioBoxes *newrb;
+  char *fname;
+
+  /* I really don't need this node, it just sets up some parsing stuff */
+  return_node->type = Noop;
+
+  newrb = alloc_rbs();
+
+  get_token();
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+
+  newrb->name = alloc_string(get_input_string());
+
+  /* quick search for the name in the current list */
+  if (already_there(newrb->name)) {
+    free(newrb->name);
+    free(newrb);
+    fprintf(stderr, "Tried to redefine radioboxes %s\n", newrb->name);
+    print_page_and_filename();
+    jump();
+  }
+  /* now I have to get the selected and unslected bitmaps */
+  get_token();
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+  fname = get_input_string();
+  if (!make_input_file)
+    newrb->selected = insert_image_struct(fname);
+
+  get_token();
+  if (token.type != Lbrace) {
+    token_name(token.type);
+    fprintf(stderr, "\\radioboxes was expecting a name not %s\n", ebuffer);
+    print_page_and_filename();
+    jump();
+  }
+  fname = get_input_string();
+  if (!make_input_file) {
+    newrb->unselected = insert_image_struct(fname);
+    newrb->height = max(newrb->selected->height, newrb->unselected->height);
+    newrb->width = max(newrb->selected->width, newrb->unselected->width);
+    /* now add the thing to the current list of radio boxes */
+  }
+  newrb->next = gPageBeingParsed->radio_boxes;
+  gPageBeingParsed->radio_boxes = newrb;
+
+  curr_node = return_node;
+  return;
+}
+@
+\section{parse.h}
+<<parse.h>>=
+#ifndef _PARSE_H_
+#define _PARSE_H_ 1
+
+<<hterror.h>>
+
+#ifdef SUNplatform
+#include <sys/types.h>
+#endif
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <ctype.h>
+<<hyper.h>>
+
+#include <setjmp.h>
+
+extern jmp_buf  jmpbuf;
+extern int      vbuff;
+
+extern TextNode *cur_spadcom;   /* spad command being parsed *** */
+extern TextNode *curr_node;
+extern long     page_start_fpos;/* tells the character position of the start
+                                 * of the page, needed to find the current
+                                 * position when restoring the scanner */
+
+/*
+ * Default sizes of hash tables
+ */
+
+#define LinkHashSize    25
+#define DependHashSize  20
+
+extern HashTable *gLinkHashTable;    /* the hash table of active link windows */
+
+/*
+ * Flags and defines for the modes the parser can be in
+ */
+
+#define AllMode            0
+#define NoVerticalMode     1
+#define SimpleMode         2
+
+extern short int gParserMode;
+
+/*
+ * Flags and defines for telling us what part of the page is being parsed.
+ */
+
+extern short int gParserRegion;
+extern short int gStringValueOk;
+extern boolean   gEndedPage;
+
+extern int      line_number;
+
+/*
+ * Things for handling macro parameters
+ */
+
+
+
+extern ParameterList parameters;
+
+
+/*
+ * The error buffer
+ */
+
+extern char     ebuffer[];
+
+#endif
+@
+\section{parse.c}
+<<parse.c>>=
+#define _PARSE_C
+#include "debug.h"
+
+<<parse.h>>
+<<parse-aux.h>>
+<<parse-paste.h>>
+<<parse-types.h>>
+<<lex.h>>
+<<mem.h>>
+<<extent.h>>
+<<event.h>>
+<<display.h>>
+<<group.h>>
+<<scrollbar.h>>
+<<titlebar.h>>
+
+#include "all-hyper-proto.h1"
+
+
+
+TextNode *curr_node;            /* current node being parsed. It is to be the
+                                 * next one filled   */
+HashTable *gLinkHashTable;           /* the hash table of active link windows   */
+TextNode *cur_spadcom;          /* The current AXIOM command   */
+
+short int gParserMode;           /* Parser mode flag */
+short int gParserRegion;         /* Parser Region flag scrolling etc */
+short int gStringValueOk;        /* is a string or box value ok */
+boolean gEndedPage;
+
+extern int example_number;             /* sequence example number */
+
+
+int ret_val;                    /* The return value from get_token */
+
+HyperDocPage *cur_page;
+
+char *replace_page;             /* true if dynamic page is link to static one */
+
+/*
+ * These routines are used for storing an restoring the parser mode. When
+ * I start to parse from string, or from a macro, I need to restore the
+ * parser mode and region once done. These routines do that
+ *
+ */
+
+typedef struct mr_stack {
+    /** The structure for storing parser mode and region **/
+    short int fParserMode;
+    short int fParserRegion;
+    struct mr_stack *fNext;
+}   MR_Stack;
+
+MR_Stack *top_mr_stack = NULL;  /** Declaration for the stack  **/
+
+static void
+Push_MR(void)
+{
+    MR_Stack *newStackItem = (MR_Stack *) halloc(sizeof(MR_Stack), "Mode Region Stack");
+
+    newStackItem->fParserMode = gParserMode;
+    newStackItem->fParserRegion = gParserRegion;
+    newStackItem->fNext = top_mr_stack;
+    top_mr_stack = newStackItem;
+}
+
+static void
+Pop_MR(void)
+{
+    MR_Stack *old = top_mr_stack;
+
+    if (old == NULL) {
+        fprintf(stderr, "(HyperDoc) Parser Error: Tried to pop empty MR Stack\n");
+        exit(-1);
+    }
+    else {
+        gParserMode = old->fParserMode;
+        gParserRegion = old->fParserRegion;
+        top_mr_stack = old->fNext;
+        free(old);
+    }
+}
+
+void
+load_page(HyperDocPage *page)
+{
+    if (page->type == UnloadedPageType) {
+        HyperDocPage *new_page;
+        init_scanner();
+        new_page = format_page((UnloadedPage *)page);
+        gWindow->page = new_page;
+        /* free(page); */
+        page = new_page;
+    }
+}
+
+HyperDocPage *formatpage;
+
+/* Display a HyperDoc page with the given name, parsing it if needed */
+
+void
+display_page(HyperDocPage *page)
+{
+    HyperDocPage *new_page;
+
+    XUnmapSubwindows(gXDisplay, gWindow->fMainWindow);
+    XUnmapSubwindows(gXDisplay, gWindow->fScrollWindow);
+    XFlush(gXDisplay);
+
+    if (setjmp(jmpbuf)) {
+
+        /*
+         * since I did not finish formatting the page, let me get rid of what
+         * I had
+         */
+        free_page(formatpage);
+        /* Replace the buggy page with what I started with */
+        hash_replace(gWindow->fPageHashTable, (char *)page, formatpage->name);
+        if (!strcmp(formatpage->name, "ErrorPage")) {
+            fprintf(stderr, "(HyperDoc) Oops the error page is buggy\n");
+            exit(-1);
+        }
+        gWindow->page = page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage");
+        if (page == NULL) {
+            fprintf(stderr, "(HyperDoc) No error page found, exiting\n");
+            exit(-1);
+        }
+        reset_connection();
+    }
+    if (page->type == UnloadedPageType || page->type == ErrorPage) {
+        /* Gack! (page should be a union!) */
+        init_scanner();
+        new_page = format_page((UnloadedPage *)page);
+        gWindow->page = new_page;
+        /* free(page); */
+        page = new_page;
+    }
+    show_page(page);
+}
+
+
+/* Parse a given HyperDoc Page, from the top */
+
+static HyperDocPage *
+format_page(UnloadedPage *ulpage)
+{
+    /*int ret_val;*/
+    HyperDocPage *page = alloc_page(ulpage->name);
+
+    /*
+     * In case of an error I will have to get at this page so I can free the
+     * waisted memory
+     */
+    formatpage = page;
+    page->type = Normal;
+    hash_replace(gWindow->fPageHashTable, (char *)page, ulpage->name);
+
+    cfile = find_fp(ulpage->fpos);
+
+
+    page->filename = alloc_string(ulpage->fpos.name);
+    parse_page(page);
+    return page;
+}
+
+/* parse the HyperDoc statements in the given string */
+
+void
+parse_from_string(char *str)
+{
+    save_scanner_state();
+    last_ch = NoChar;
+    last_token = 0;
+    input_string = str;
+    input_type = FromString;
+    parse_HyperDoc();
+    restore_scanner_state();
+}
+
+static void
+parse_title(HyperDocPage *page)
+{
+    TextNode *node;
+
+    Push_MR();
+    gParserRegion = Title;
+    get_expected_token(Lbrace);
+    node = alloc_node();
+    page->title = node;
+    node->type = Titlenode;
+    node->next = alloc_node();
+    node = node->next;
+    node->type = Center;
+    node->next = alloc_node();
+    curr_node = node->next;
+    parse_HyperDoc();
+    curr_node->type = Endcenter;
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    curr_node->type = Endtitle;
+    curr_node->next = NULL;
+    if (gNeedIconName) {
+        char *title = print_to_string(page->title);
+
+        XSetIconName(gXDisplay, gWindow->fMainWindow, title);
+        gNeedIconName = 0;
+    }
+    if (token.type != Rbrace) {
+        fprintf(stderr, "(HyperDoc) Parse title was expecting a closing brace\n");
+        print_page_and_filename();
+        jump();
+    }
+    linkTitleBarWindows();
+    Pop_MR();
+}
+
+static void
+parse_header(HyperDocPage *page)
+{
+    TextNode *node;
+
+    Push_MR();
+    gParserRegion = Header;
+    node = alloc_node();
+    page->header = node;
+    node->type = Headernode;
+    node->next = alloc_node();
+    curr_node = node->next;
+    parse_HyperDoc();
+}
+
+/*
+ * parse a page from the top level
+ */
+
+static void
+init_parse_page(HyperDocPage *page)
+{
+    gEndedPage = gInDesc = gStringValueOk = gInIf =
+        gInButton = gInOptional = gInVerbatim = gInPaste = gInItems =
+        gInSpadsrc = FALSE;
+    example_number = 1;
+    cur_page = page;
+    gParserMode = AllMode;
+    /* Now I should set the input list to be null */
+    free_input_list(page->input_list);
+    page->input_list = page->current_item = NULL;
+
+    init_top_group();
+    clear_be_stack();
+
+    cur_spadcom = NULL;
+    gLinkHashTable = page->fLinkHashTable;
+    hash_init(
+	      gLinkHashTable, 
+	      LinkHashSize, 
+	      (EqualFunction) window_equal, 
+	      (HashcodeFunction) window_code);
+    gPageBeingParsed = page;
+
+}
+
+void
+init_parse_patch(HyperDocPage *page)
+{
+    gEndedPage = gInDesc = gStringValueOk = gInIf =
+        gInButton = gInOptional = gInVerbatim = gInPaste = gInItems =
+        gInSpadsrc = FALSE;
+    gParserMode = AllMode;
+    gParserRegion = Scrolling;
+
+    init_top_group();
+    clear_be_stack();
+
+    cur_spadcom = NULL;
+    gLinkHashTable = page->fLinkHashTable;
+    gPageBeingParsed = page;
+}
+
+#define end_page(t) ((t == Page || t == NewCommand ||t == Endpage)?1:0)
+
+static void
+parse_page(HyperDocPage *page)
+{
+    init_parse_page(page);
+
+    /* Get the name of the page */
+
+    get_expected_token(Page);
+    get_expected_token(Lbrace);
+    get_expected_token(Word);
+    if (page->name == NULL)
+        page->name = alloc_string(token.id);
+    get_expected_token(Rbrace);
+    /* parse the title */
+    gWindow->fDisplayedWindow = gWindow->fMainWindow;
+    parse_title(page);
+
+    /*
+     * Now start parsing the header region
+     */
+    parse_header(page);
+}
+
+char *ExpectedBeginScroll =
+"Parser Error: Unexpected new page, expecting a begin scroll\n", *ExpectedEndScroll =
+"Parser Error: Unexpected new page, expected an end scroll\n";
+
+/*
+ * The general HyperDoc parsing function.  expects to see anything. This
+ * function will parse until it sees either: 1) A new page starting 2) An end
+ * of file 3) a closing bracket "}"
+ */
+
+void
+parse_HyperDoc(void)
+{
+    TextNode *node = NULL /*, *save_node = NULL, *arg_node = NULL*/ ;
+
+    for(;;) {
+        ret_val = get_token();
+
+        if (ret_val == EOF)
+            return;
+
+        switch (token.type) {
+          case Spadsrc:
+            parse_spadsrc(curr_node);
+            break;
+          case Helppage:
+            parse_help();
+            break;
+          case Endpatch:
+          case Endpaste:
+          case Rbrace:
+            return;
+          case Paste:
+            parse_paste();
+            break;
+          case Pastebutton:
+            parse_pastebutton();
+            break;
+          case Endpage:
+          case NewCommand:
+          case Page:
+            end_a_page();
+            return;
+          case EndScroll:
+            token.type = Endscroll;
+          case Endscroll:
+            start_footer();
+            break;
+          case Beginscroll:
+            start_scrolling();
+            break;
+          case Thispage:        /* it really is just a word */
+            curr_node->type = Word;
+            curr_node->data.text = alloc_string(gPageBeingParsed->name);
+            break;
+          case Icorrection:
+            node->type = Noop;
+            break;
+          case Newcond:
+            parse_newcond();
+            break;
+          case Setcond:
+            parse_setcond();
+            break;
+          case Dollar:
+            parse_verbatim(Math);
+            break;
+          case Verbatim:
+            parse_verbatim(Verbatim);
+            break;
+          case Ifcond:
+            parse_ifcond();
+            break;
+          case Fi:
+            if (gInIf)
+                return;
+            else {
+                curr_node->type = Noop;
+                /* Oops I had a problem parsing this puppy */
+                fprintf(stderr, "(HyperDoc) \\fi found without macthing if?\n");
+                longjmp(jmpbuf, 1);
+                fprintf(stderr, "(HyperDoc) Longjmp failed -- Exiting \n");
+                exit(-1);
+            }
+          case Else:
+            if (gInIf)
+                return;
+            else {
+                /* Oops I had a problem parsing this puppy */
+                curr_node->type = Noop;
+                fprintf(stderr, "(HyperDoc) \\else found without macthing if?\n");
+                longjmp(jmpbuf, 1);
+                fprintf(stderr, "(HyperDoc) Longjmp failed -- Exiting \n");
+                exit(-1);
+            }
+          case Macro:
+            parse_macro();
+            break;
+          case Env:
+            /** In this case, get the environment value, and make it a word **/
+            parse_env(curr_node);
+            break;
+          case WindowId:
+            curr_node->type = WindowId;
+            curr_node->space = token.id[-1];
+            curr_node->data.text = window_id(gWindow->fMainWindow);
+            break;
+          case Punctuation:
+          case Word:
+          case Lsquarebrace:
+          case Dash:
+            curr_node->type = token.type;
+            curr_node->space = token.id[-1];
+            curr_node->data.text = alloc_string(token.id);
+            break;
+          case Pagename:
+            {
+                char *str;
+
+                curr_node->type = Word;
+                curr_node->space = 0;
+                str = halloc(strlen(cur_page->name) + 1, "parse");
+                sprintf(str, "%s", cur_page->name);
+                curr_node->data.text = alloc_string(str);
+                break;
+            }
+          case Examplenumber:
+            {
+                char *str;
+
+                curr_node->type = Word;
+                curr_node->space = 0;
+                str = halloc(5, "parse");
+                sprintf(str, "%d", example_number);
+                curr_node->data.text = alloc_string(str);
+                break;
+            }
+          case Rsquarebrace:
+            if (gInOptional)
+                return;
+            else {
+                curr_node->type = token.type;
+                curr_node->space = token.id[-1];
+                curr_node->data.text = alloc_string(token.id);
+            }
+            break;
+          case EndTitems:
+            token.type = Endtitems;
+          case Endtitems:
+            if (gParserMode != AllMode) {
+                curr_node->type = Noop;
+                fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]);
+                longjmp(jmpbuf, 1);
+            }
+            else {
+                curr_node->type = token.type;
+                break;
+            }
+          case EndItems:
+            token.type = Enditems;
+          case Enditems:
+            gInItems--;
+          case Horizontalline:
+          case Par:
+          case Newline:
+          case Titem:
+            if (gParserMode != AllMode) {
+                curr_node->type = Noop;
+                fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]);
+                longjmp(jmpbuf, 1);
+            }
+            else {
+                curr_node->type = token.type;
+                break;
+            }
+          case Begintitems:
+          case Beginitems:
+            if (gParserMode != AllMode) {
+                curr_node->type = Noop;
+                fprintf(stderr, "(HyperDoc) Found a bad token %s\n", token_table[token.type]);
+                longjmp(jmpbuf, 1);
+            }
+            else {
+                parse_begin_items();
+                break;
+            }
+          case Item:
+            parse_item();
+            break;
+          case Mitem:
+            parse_mitem();
+            break;
+          case VSpace:
+          case Tab:
+          case HSpace:
+          case Indent:
+          case Indentrel:
+            parse_value1();
+            break;
+          case Space:
+            parse_value2();
+            break;
+          case Lbrace:
+            curr_node->type = Group;
+            curr_node->space = token.id[-1];
+            push_group_stack();
+            node = alloc_node();
+            curr_node->next = node;
+            curr_node = curr_node->next;
+            parse_HyperDoc();
+            curr_node->type = Endgroup;
+            pop_group_stack();
+            break;
+          case Upbutton:
+          case Returnbutton:
+          case Link:
+          case Downlink:
+          case Memolink:
+          case Windowlink:
+            parse_button();
+            break;
+          case Unixlink:
+          case LispMemoLink:
+          case LispDownLink:
+          case Lisplink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Spadlink:
+          case Spaddownlink:
+          case Spadmemolink:
+          case Unixcommand:
+          case Spadcall:
+          case Spadcallquit:
+          case Qspadcall:
+          case Qspadcallquit:
+          case Lispwindowlink:
+            parse_command();
+            break;
+          case Controlbitmap:
+          case Inputbitmap:
+          case Inputpixmap:
+          case Inputimage:
+            parse_input_pix();
+            break;
+          case Box:
+            parse_box();
+            break;
+          case Mbox:
+            parse_mbox();
+            break;
+          case Free:
+            parse_free();
+            break;
+          case Center:
+            parse_centerline();
+            break;
+          case Bound:
+            add_dependencies();
+            break;
+          case Spadcommand:
+          case Spadgraph:
+            parse_spadcommand(curr_node);
+            break;
+          case Table:
+            parse_table();
+            break;
+          case Beep:
+          case Emphasize:
+          case BoldFace:
+          case Rm:
+          case It:
+          case Tt:
+          case Sl:
+            curr_node->type = token.type;
+            curr_node->space = token.id[-1];
+            break;
+          case Inputstring:
+            parse_inputstring();
+            break;
+          case SimpleBox:
+            parse_simplebox();
+            break;
+          case BoxValue:
+          case StringValue:
+            if (!gStringValueOk) {
+                strcpy(ebuffer,"(HyperDoc): Unexpected Value Command:");
+                strcat(ebuffer, token.id);
+
+                parser_error(ebuffer);
+                curr_node->type = Noop;
+                longjmp(jmpbuf, 1);
+            }
+            curr_node->type = token.type;
+            curr_node->space = token.id[-1];
+            get_expected_token(Lbrace);
+            get_expected_token(Word);
+            curr_node->data.text = alloc_string(token.id);
+            get_expected_token(Rbrace);
+            break;
+          case NoLines:
+            gPageBeingParsed->page_flags |= NOLINES;
+            break;
+          case Pound:
+            curr_node->type = Pound;
+            curr_node->space = token.id[-1];
+            curr_node->next = alloc_node();
+            curr_node = curr_node->next;
+            parse_parameters();
+            break;
+          case Radiobox:
+            parse_radiobox();
+            break;
+          case Radioboxes:
+            parse_radioboxes();
+            break;
+          case Replacepage:
+            parse_replacepage();
+            break;
+          default:
+            fprintf(stderr, "(HyperDoc) Keyword not currently supported: %s\n", token.id);
+            print_page_and_filename();
+            curr_node->type = Noop;
+            break;
+        }
+        if (gEndedPage)
+            return;
+        if (curr_node->type != Noop) {
+            node = alloc_node();
+            curr_node->next = node;
+            curr_node = node;
+        }
+    }
+}
+
+
+/* parse a page from a socket source */
+
+HyperDocPage *
+parse_page_from_socket(void)
+{
+    HyperDocPage *page = alloc_page((char *) NULL);
+    HyperDocPage *hpage;
+
+    init_scanner();
+    input_type = FromSpadSocket;
+    input_string = "";
+    cur_spadcom = NULL;
+    gLinkHashTable = page->fLinkHashTable;
+    hash_init(
+	      gLinkHashTable, 
+	      LinkHashSize, 
+	      (EqualFunction) window_equal, 
+	      (HashcodeFunction) window_code);
+    gPageBeingParsed = page;
+    replace_page = NULL;
+    if (setjmp(jmpbuf)) {
+        /* Ooops, somewhere I had an error */
+        free_page(page);
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage");
+        reset_connection();
+    }
+    else {
+        parse_page(page);
+        page->type = SpadGen;
+        page->filename = NULL;
+        /* just for kicks, let me add this thing to the hash file */
+        hpage = (HyperDocPage *) hash_find(gWindow->fPageHashTable, page->name);
+        if (hpage)
+            hash_replace(gWindow->fPageHashTable, (char *)page, page->name);
+        else {
+            hash_insert(gWindow->fPageHashTable, (char *)page, page->name);
+        }
+    }
+    if (replace_page != NULL) {
+        free_page(page);
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, replace_page);
+        if (page == NULL)
+            fprintf(stderr, "(HyperDoc) Unknown page: %s\n", replace_page);
+    }
+    return page;
+}
+
+HyperDocPage *
+parse_page_from_unixfd(void)
+{
+    HyperDocPage *page = alloc_page((char *) NULL);
+
+    init_scanner();
+    input_type = FromUnixFD;
+    cur_spadcom = NULL;
+    gLinkHashTable = page->fLinkHashTable;
+    hash_init(
+	      gLinkHashTable, 
+	      LinkHashSize, 
+	      (EqualFunction) window_equal, 
+	      (HashcodeFunction) window_code);
+    gPageBeingParsed = page;
+    if (setjmp(jmpbuf)) {
+        /* Ooops, somewhere I had an error */
+        free_page(page);
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "ErrorPage");
+        reset_connection();
+    }
+    else {
+        parse_page(page);
+        page->type = Unixfd;
+        page->filename = NULL;
+    }
+    return page;
+
+}
+
+static void
+start_scrolling(void)
+{
+
+    /*
+     * if I am here than I had a begin scroll. This means I should end the
+     * header, and then start parsing the footer
+     */
+
+    if (gParserRegion != Header) {
+        curr_node->type = Noop;
+        fprintf(stderr, "(HyperDoc) Parser Error: Unexpected BeginScrollFound\n");
+        longjmp(jmpbuf, 1);
+        fprintf(stderr, "(HyperDoc) Longjump failed exiting\n");
+    }
+    curr_node->type = Endheader;
+    curr_node->next = NULL;
+    Pop_MR();
+
+    Push_MR();
+    gParserRegion = Scrolling;
+    gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+    curr_node = alloc_node();
+    gPageBeingParsed->scrolling = curr_node;
+    curr_node->type = Scrollingnode;
+}
+
+static void
+start_footer(void)
+{
+    /*
+     * This ends the parsing of the scrolling region, and then starts to
+     * parse the footer
+     */
+
+    if (gParserRegion != Scrolling) {
+        curr_node->type = Noop;
+        fprintf(stderr, "(HyperDoc) Parser Error: Unexpected Endscroll Found\n");
+        print_page_and_filename();
+        longjmp(jmpbuf, 1);
+        fprintf(stderr, "(HyperDoc) Longjump failed exiting\n");
+    }
+
+    curr_node->type = Endscrolling;
+    curr_node->next = NULL;
+    Pop_MR();
+    linkScrollBars();
+
+    Push_MR();
+    gParserRegion = Footer;
+    curr_node = alloc_node();
+    curr_node->type = Footernode;
+    gPageBeingParsed->footer = curr_node;
+    gWindow->fDisplayedWindow = gWindow->fMainWindow;
+}
+
+static void
+end_a_page(void)
+{
+    if (gParserRegion == Scrolling) {
+        fprintf(stderr, "%s\n",
+                "(HyperDoc) end_a_page: Unexpected End of Page occurred \
+                   inside a \beginscroll");
+        print_page_and_filename();
+        jump();
+    }
+    gEndedPage = TRUE;
+    if (gParserRegion == Footer) {
+        /* the person had all the regions, I basically just have to leave */
+        curr_node->type = Endscrolling;
+        curr_node->next = NULL;
+        Pop_MR();
+    }
+    else if (gParserRegion == Header) {
+        /* person had a header. So just end it and return */
+        curr_node->type = Endheader;
+        curr_node->next = NULL;
+        Pop_MR();
+        gPageBeingParsed->scrolling = NULL;
+        gPageBeingParsed->footer = NULL;
+    }
+}
+
+static void
+parse_replacepage(void)
+{
+    get_expected_token(Lbrace);
+    get_token();
+    replace_page = alloc_string(token.id);
+    get_expected_token(Rbrace);
+}
+@
+\section{parse-paste.h}
+<<parse-paste.h>>=
+#ifndef _PARSE_PASTE_H_
+#define _PARSE_PASTE_H_ 1
+
+<<hyper.h>>
+
+extern short int gInPaste;
+
+#endif
+@
+\section{parse-paste.c}
+<<parse-paste.c>>=
+/******************************************************************************
+ *
+ * parse_paste.c: HyperDoc routines for paste-in areas.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _PARSE_PASTE_C
+#include "debug.h"
+
+
+<<parse.h>>
+<<parse-aux.h>>
+<<mem.h>>
+<<display.h>>
+<<group.h>>
+
+#include "all-hyper-proto.h1"
+
+
+extern FILE *cfile;
+short int gInPaste;
+
+
+void
+parse_paste(void)
+{
+    TextNode *pn = curr_node;
+    PasteNode *paste;
+    int where;
+
+    if (gParserRegion != Scrolling) {
+        fprintf(stderr, "(HyperDoc) Paste areas are only allowed in the scrolling area:");
+        print_page_and_filename();
+        jump();
+    }
+    gInPaste++;
+
+    /* now I need to get the name */
+    get_token();
+    if (token.type != Lbrace) {
+        fprintf(stderr, "(HyperDoc) A paste area needs a name:\n");
+        print_next_ten_tokens();
+        print_page_and_filename();
+        jump();
+    }
+    pn->data.text = alloc_string(get_input_string());
+    pn->type = Paste;
+
+    /*
+     * now see if there is already an entry in the hash_table for this thing,
+     * if not create it and put it there.
+     */
+    paste = (PasteNode *) hash_find(gWindow->fPasteHashTable, pn->data.text);
+    if (paste == 0) {
+        paste = alloc_paste_node(pn->data.text);
+        hash_insert(gWindow->fPasteHashTable, (char *)paste, paste->name);
+    }
+    else if (paste->haspaste) {
+        fprintf(stderr, "(HyperDoc) Tried to redefine paste area %s\n", paste->name);
+        print_page_and_filename();
+        /* jump(); */
+    }
+    paste->haspaste = 1;
+    paste->paste_item = current_item();
+    get_token();
+    if (token.type == Lsquarebrace) {
+        /* user wishes to specify a where to send the command */
+        where = get_where();
+        if (where == -1) {
+            paste->where = -1;
+            fprintf(stderr, "(HyperDoc) \\begin{paste} was expecting [lisp|unix|ht]\n");
+            print_next_ten_tokens();
+            print_page_and_filename();
+            jump();
+        }
+        else
+            paste->where = where;
+        get_token();
+    }
+    else
+        paste->where = FromFile;
+
+    /* now try to get the command argument or page name */
+    if (token.type != Lbrace) {
+        paste->where = 0;
+        fprintf(stderr, "(HyperDoc) \\begin{paste} was expecting an argument\n");
+        print_next_ten_tokens();
+        print_page_and_filename();
+        jump();
+    }
+    paste->arg_node = alloc_node();
+    curr_node = paste->arg_node;
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+
+    gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+
+    /* Now try to find the displaying text */
+    pn->next = alloc_node();
+    curr_node = pn->next;
+    parse_HyperDoc();
+    curr_node->type = Endpaste;
+    paste->end_node = curr_node;
+
+    paste->begin_node = pn;
+    gInPaste--;
+}
+
+void
+parse_pastebutton(void)
+{
+    PasteNode *paste;
+    TextNode *pb;
+
+    /*
+     * this routine parse a \pastebutton expression. The syntax is
+     * \pastebutton{name}
+     */
+    pb = curr_node;
+    pb->type = Pastebutton;
+
+    /* first thing I should do is get the name */
+    get_token();
+    if (token.type != Lbrace) {
+        fprintf(stderr, "(HyperDoc) \\pastebutton needs a name\n");
+        print_page_and_filename();
+        print_next_ten_tokens();
+        jump();
+    }
+    pb->data.text = alloc_string(get_input_string());
+
+    /*
+     * now I should see if the paste area has already been parsed, and if not
+     * I should create a spot in the hash table for it
+     */
+    paste = (PasteNode *) hash_find(gWindow->fPasteHashTable, pb->data.text);
+    if (paste == 0) {
+        paste = alloc_paste_node(pb->data.text);
+        hash_insert(gWindow->fPasteHashTable,(char *) paste, paste->name);
+    }
+    else if (paste->hasbutton) {
+        fprintf(stderr, "(HyperDoc) Tried to redefine paste area %s\n", paste->name);
+        print_page_and_filename();
+        /* jump(); */
+    }
+    paste->hasbutton = 1;
+
+    /* Now we need to parse the HyperDoc and for the displayed text */
+
+    get_token();
+    if (token.type != Lbrace) {
+        fprintf(stderr, "(HyperDoc) \\pastebutton was expecting a { \n");
+        print_page_and_filename();
+        print_next_ten_tokens();
+        jump();
+    }
+    pb->next = alloc_node();
+    curr_node = pb->next;
+    parse_HyperDoc();
+    curr_node->type = Endpastebutton;
+
+    /* once that is done I need only make the window for this link */
+    pb->link = make_paste_window(paste);
+}
+
+
+/*
+ * this routine is responsible for parsing a patch from a file. To do this I
+ * guess er will init_scanner, then parse, the parsed piece of text
+ * will replace the current PasteNode which will be squashed down to
+ * nothing, and then discarded.
+ */
+
+HyperDocPage *
+parse_patch(PasteNode *paste)
+{
+    TextNode *new;
+    TextNode *end_node;
+    TextNode *begin_node;
+    TextNode *arg_node;
+    TextNode *throw;
+    TextNode *next_node;
+    InputItem *paste_item = paste->paste_item;
+    int where = paste->where;
+    GroupItem *g = paste->group;
+    ItemStack *is = paste->item_stack;
+    PatchStore *patch;
+    char *patch_name;
+    int ret_value = 1;
+
+    /* prepare to throw away the current paste node */
+    end_node = paste->end_node;
+    next_node = end_node->next;
+    begin_node = paste->begin_node;
+    arg_node = paste->arg_node;
+    throw = begin_node->next;
+
+    /* now read the new stuff and add it in between all this stuff */
+
+    switch (where) {
+      case FromFile:
+        patch_name = print_to_string(arg_node);
+        patch = (PatchStore *) hash_find(gWindow->fPatchHashTable, patch_name);
+        if (!patch) {
+            fprintf(stderr, "(HyperDoc) Unknown patch name %s\n", patch_name);
+            BeepAtTheUser();
+            return 0;
+        }
+        if (!patch->loaded)
+            load_patch(patch);
+        input_type = FromString;
+        input_string = patch->string;
+        break;
+      case FromSpadSocket:
+        input_type = FromSpadSocket;
+        ret_value = issue_serverpaste(arg_node);
+        if (ret_value < 0) {
+            paste->where = where;
+            paste->end_node = end_node;
+            paste->arg_node = arg_node;
+            paste->group = g;
+            paste->item_stack = is;
+            paste->haspaste = 1;
+            return 0;
+        }
+        break;
+      case FromUnixFD:
+        input_type = FromUnixFD;
+        issue_unixpaste(arg_node);
+        break;
+      default:
+        fprintf(stderr, "(HyperDoc) \\parsebutton error: Unknown where\n");
+        exit(-1);
+        break;
+    }
+
+    paste->where = 0;
+    paste->end_node = paste->arg_node = paste->begin_node = 0;
+    paste->group = 0;
+    paste->item_stack = 0;
+    paste->haspaste = 0;
+    paste->paste_item = 0;
+
+
+    /* set the jump buffer in case it is needed */
+    if (setjmp(jmpbuf)) {
+        /*** OOOPS, an error occurred ****/
+        fprintf(stderr, "(HyperDoc) Had an error parsing a patch: Goodbye!\n");
+        exit(-1);
+    }
+
+
+    end_node->next = 0;
+    free_node(throw, 1);
+
+    init_parse_patch(gWindow->page);
+    init_paste_item(paste_item);
+    get_token();
+    if (token.type != Patch) {
+        fprintf(stderr, "(HyperDoc) Pastebutton %s was expecting a patch\n",
+                paste->name);
+        jump();
+    }
+    if (input_type == FromString) {
+        get_token();
+        if (token.type != Lbrace) {
+            token_name(token.type);
+            fprintf(stderr, "(HyperDoc) Unexpected %s \n", ebuffer);
+            print_page_and_filename();
+            jump();
+        }
+
+        get_token();
+        if (token.type != Word) {
+            token_name(token.type);
+            fprintf(stderr, "(HyperDoc) Unexpected %s \n", ebuffer);
+            print_page_and_filename();
+            jump();
+        }
+
+        get_token();
+        if (token.type != Rbrace) {
+            token_name(token.type);
+            fprintf(stderr, "(HyperDoc) Unexpected %s \n", ebuffer);
+            print_page_and_filename();
+            jump();
+        }
+    }
+    new = alloc_node();
+    curr_node = new;
+    parse_HyperDoc();
+
+    /* Once I am back, I need only reallign all the text structures */
+    curr_node->type = Noop;
+    curr_node->next = next_node;
+    begin_node->next = new;
+    begin_node->type = Noop;
+    free(begin_node->data.text);
+    begin_node->data.text = 0;
+
+    gWindow->fDisplayedWindow = gWindow->fScrollWindow;
+
+    repaste_item();
+
+    paste_page(begin_node);
+
+    /* so now I should just be able to disappear */
+    return gWindow->page;
+}
+
+static void
+load_patch(PatchStore *patch)
+{
+    long start_fpos;
+    int size = 0;
+    int limsize;
+    char *trace;
+
+
+    save_scanner_state();
+    cfile = find_fp(patch->fpos);
+
+    init_scanner();
+
+    /** First thing I should do is make sure that the name is correct ***/
+    start_fpos = fpos;
+    get_expected_token(Patch);
+    get_expected_token(Lbrace);
+    get_expected_token(Word);
+    if (strcmp(token.id, patch->name)) {
+        /** WOW, Somehow I had the location of the wrong macro **/
+        fprintf(stderr, "(HyperDoc) Expected patch name %s: got instead %s in load_patch\n",
+                patch->name, token.id);
+        jump();
+    }
+    get_expected_token(Rbrace);
+
+    scan_HyperDoc();
+    fseek(cfile, patch->fpos.pos + start_fpos, 0);
+    limsize = fpos - start_fpos + 1;
+    patch->string = (char *) halloc((limsize + 1) * sizeof(char), "Patch String");
+    for (size = 1, trace = patch->string; size < limsize; size++)
+        *trace++ = getc(cfile);
+    *trace = '\0';
+    patch->loaded = 1;
+    restore_scanner_state();
+}
+
+
+@
+\section{parse-types.h}
+<<parse-types.h>>=
+#ifndef _PARSE_TYPES_H_
+#define _PARSE_TYPES_H_ 1
+
+<<hyper.h>>
+
+extern boolean gInButton;
+extern boolean gInIf;
+extern boolean gInItems;
+extern boolean gInOptional;
+
+
+#endif
+@
+\section{parse-types.c}
+<<parse-types.c>>=
+/******************************************************************************
+ *
+ * parse_types.h: HyperDoc parsing routines for node types.
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _PARSE_TYPES_C
+#include "debug.h"
+
+<<parse.h>>
+<<parse-aux.h>>
+<<parse-types.h>>
+<<hyper.h>>
+<<lex.h>>
+<<mem.h>>
+<<extent.h>>
+<<hterror.h>>
+
+#include "all-hyper-proto.h1"
+
+boolean gInButton = FALSE;
+boolean gInIf = FALSE;
+boolean gInItems = FALSE;
+boolean gInOptional = FALSE;
+
+void
+parse_ifcond(void)
+{
+    TextNode *ifnode = curr_node;
+    TextNode *endif;
+    TextNode *condnode;
+
+    /*
+     * parse a conditional. At first I am just going to parse if
+     * <hypertext> fi
+     */
+    if (gInIf) {
+        curr_node->type = Noop;
+        fprintf(stderr, "\\if found within \\if \n");
+        longjmp(jmpbuf, 1);
+        fprintf(stderr, "Longjump failed, Exiting\n");
+        exit(-1);
+    }
+    gInIf++;
+    curr_node->type = Ifcond;
+    curr_node->space = token.id[-1];
+    curr_node->data.ifnode = alloc_ifnode();
+    /* Now get the cond node I hope */
+
+    condnode = curr_node->data.ifnode->cond = alloc_node();
+    curr_node = condnode;
+    parse_condnode();
+
+    endif = alloc_node();
+    endif->type = Endif;
+    ifnode->data.ifnode->thennode = alloc_node();
+    curr_node = ifnode->data.ifnode->thennode;
+    parse_HyperDoc();
+    if (token.type == Fi) {
+        curr_node->type = Fi;
+        curr_node->next = endif;
+        ifnode->data.ifnode->elsenode = endif;
+    }
+    else if (token.type == Else) {
+        /* first finish up the then part */
+        curr_node->type = Fi;
+        curr_node->next = endif;
+        /* the go and parse the else part */
+        ifnode->data.ifnode->elsenode = alloc_node();
+        curr_node = ifnode->data.ifnode->elsenode;
+        parse_HyperDoc();
+        if (token.type != Fi) {
+            token_name(token.type);
+            curr_node->type = Noop;
+            fprintf(stderr, "Expected a \\fi not a %s", ebuffer);
+            longjmp(jmpbuf, 1);
+            fprintf(stderr, "Longjump failed, Exiting\n");
+            exit(-1);
+        }
+        curr_node->type = Fi;
+        curr_node->next = endif;
+    }
+    else {
+        curr_node->type = Noop;
+        token_name(token.type);
+        fprintf(stderr, "Expected a \\fi not a %s", ebuffer);
+        longjmp(jmpbuf, 1);
+        fprintf(stderr, "Longjump failed, Exiting\n");
+        exit(-1);
+    }
+    ifnode->next = ifnode->data.ifnode->thennode;
+    ifnode->width = -1;         /* A flag for compute if extents */
+    curr_node = endif;
+    gInIf--;
+}
+
+static void
+parse_condnode(void)
+{
+    get_token();
+
+    switch (token.type) {
+      case Cond:
+        curr_node->type = Cond;
+        curr_node->data.text = alloc_string(token.id);
+        break;
+      case Haslisp:
+      case Hasreturn:
+      case Lastwindow:
+      case Hasup:
+        curr_node->type = token.type;
+        break;
+      case Boxcond:
+        curr_node->type = Boxcond;
+        curr_node->data.text = alloc_string(token.id);
+        break;
+      case Hasreturnto:
+        parse_hasreturnto();
+        break;
+      default:
+        {
+            char eb[128];
+            token_name(token.type);
+            sprintf(eb, "Unexpected Token %s\n", eb);
+            htperror(eb, HTCONDNODE);
+        }
+        break;
+    }
+}
+
+static void
+parse_hasreturnto(void)
+{
+    TextNode *hrt = curr_node, *arg_node = alloc_node();
+
+    curr_node->type = Hasreturnto;
+    curr_node = arg_node;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+    hrt->data.node = arg_node;
+    curr_node = hrt;
+}
+
+void
+parse_newcond(void)
+{
+    char label[256];
+
+    get_expected_token(Lbrace);
+    get_expected_token(Unkeyword);
+    strcpy(label, token.id);
+    get_expected_token(Rbrace);
+    insert_cond(label, "0");
+    curr_node->type = Noop;
+}
+
+void
+parse_setcond(void)
+{
+    char label[256], cond[256];
+
+    get_expected_token(Lbrace);
+    get_expected_token(Cond);
+    strcpy(label, token.id);
+    get_expected_token(Rbrace);
+    get_expected_token(Lbrace);
+    get_expected_token(Word);
+    strcpy(cond, token.id);
+    get_expected_token(Rbrace);
+    change_cond(label, cond);
+    curr_node->type = Noop;
+}
+
+void
+parse_begin_items(void)
+{
+    TextNode *bi = curr_node;
+
+    /*
+     * This procedure parses a begin item. It sets the current
+     * node and sees if there is an optional argument for the itemspace
+     */
+
+    bi->type = token.type;
+    get_token();
+    if (token.type == Lsquarebrace) {
+        bi->data.node = alloc_node();
+        curr_node = bi->data.node;
+        gInOptional++;
+        parse_HyperDoc();
+        gInOptional--;
+        curr_node->type = Enddescription;
+        if (token.type != Rsquarebrace) {
+            fprintf(stderr, "(HyperDoc) Optional arguments must end with ].\n");
+            print_next_ten_tokens();
+            print_page_and_filename();
+            jump();
+        }
+        curr_node = bi;
+    }
+    else
+        unget_token();
+    gInItems++;
+}
+
+void
+parse_item(void)
+{
+    if (!gInItems) {
+        fprintf(stderr, "\\item found outside an items environment\n");
+        print_page_and_filename();
+        print_next_ten_tokens();
+        jump();
+    }
+    curr_node->type = Item;
+    get_token();
+    if (token.type == Lsquarebrace) {
+        /* I should parse the optional argument */
+        curr_node->next = alloc_node();
+        curr_node = curr_node->next;
+        curr_node->type = Description;
+        curr_node->next = alloc_node();
+        curr_node = curr_node->next;
+        gInOptional++;
+        parse_HyperDoc();
+        gInOptional--;
+        curr_node->type = Enddescription;
+        if (token.type != Rsquarebrace) {
+            fprintf(stderr, "(HyperDoc) Optional arguments must end with ].\n");
+            print_next_ten_tokens();
+            print_page_and_filename();
+            jump();
+        }
+    }
+    else {
+        unget_token();
+    }
+}
+
+void
+parse_mitem(void)
+{
+    if (!gInItems) {
+        fprintf(stderr, "\\mitem found outside an items environment\n");
+        print_page_and_filename();
+        print_next_ten_tokens();
+        jump();
+    }
+    curr_node->type = Mitem;
+}
+
+char *vbuf = NULL;
+int vbuf_size = 0;
+
+#define VbufSlop 10
+#define resizeVbuf()\
+  if (size == vbuf_size) { \
+                             vbuf = resizeBuffer(size + VbufSlop, vbuf, &vbuf_size); \
+                               vb = vbuf + size; \
+                               }
+
+#define new_verb_node() \
+  resizeVbuf(); \
+  *vb = '\0'; \
+  curr_node->data.text = alloc_string(vbuf); \
+  curr_node->next = alloc_node(); \
+  curr_node = curr_node->next; \
+  curr_node->type = Newline; \
+  curr_node->next = alloc_node(); \
+  curr_node = curr_node->next; \
+  curr_node->type = type; \
+  if (*end_string == '\n') es = end_string+1; \
+  else es = end_string; \
+  size = 0; \
+  vb = vbuf;
+
+void
+parse_verbatim(int type)
+{
+    int size = 0, c;
+    char *end_string, *vb = vbuf, *es;
+
+    curr_node->type = type;
+    if (token.id[-1])
+        curr_node->space = 1;
+    if (type == Spadsrctxt) {
+        es = end_string = "\n\\end{spadsrc}";
+    }
+    else if (type == Math)
+        es = end_string = "$";
+    else
+        es = end_string = "\\end{verbatim}";
+    while ((c = get_char()) != EOF) {
+        resizeVbuf();
+        size++;
+        if (c == '\n') {
+            new_verb_node();
+            continue;
+        }
+        *vb++ = c;
+        if (*es++ != c)
+            es = end_string;
+        if (!*es)
+            break;
+    }
+    if (c == EOF) {
+        fprintf(stderr, "parse_verbatim: Unexpected EOF found\n");
+        longjmp(jmpbuf, 1);
+    }
+    resizeVbuf();
+    if (*end_string == '\n')
+        es = end_string + 1;
+    else
+        es = end_string;
+    vbuf[size - strlen(es)] = '\0';
+    if (*vbuf) {
+        curr_node->data.text = alloc_string(vbuf);
+        curr_node->next = alloc_node();
+        curr_node = curr_node->next;
+    }
+    if (type == Spadsrctxt)
+        curr_node->type = Endspadsrc;
+    else if (type == Math)
+        curr_node->type = Endmath;
+    else
+        curr_node->type = Endverbatim;
+}
+
+void
+parse_input_pix(void)
+{
+    TextNode *pixnode;
+    char *filename;
+
+    pixnode = curr_node;
+    pixnode->type = token.type;
+    pixnode->space = token.id[-1];
+    pixnode->width = -1;
+    get_expected_token(Lbrace);
+    filename = get_input_string();
+    pixnode->data.text = alloc_string(filename);
+    curr_node = pixnode;
+    if (pixnode->type == Inputimage) {
+        char f[256];
+        char *p;
+
+        if ((gXDisplay && DisplayPlanes(gXDisplay, gXScreenNumber) == 1) || gSwitch_to_mono ==1) {
+            pixnode->type = Inputbitmap;
+            strcpy(f, pixnode->data.text);
+            strcat(f, ".bm");
+            p=pixnode->data.text;
+            pixnode->data.text = alloc_string(f);
+            free(p);
+        }
+        else {
+            pixnode->type = Inputpixmap;
+            strcpy(f, pixnode->data.text);
+#ifdef OLD
+            strcat(f, ".pm");
+#endif
+            strcat(f, ".xpm.Z");
+	    p=pixnode->data.text;
+            pixnode->data.text = alloc_string(f);
+	    free(p);
+        }
+    }
+}
+
+void
+parse_centerline(void)
+{
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+    curr_node->width = -1;
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    if (token.type != Rbrace) {
+        curr_node->type = Noop;
+        fprintf(stderr, "(HyperdDoc) \\centerline was expecting a }\n");
+        print_page_and_filename();
+        print_next_ten_tokens();
+        longjmp(jmpbuf, 1);
+    }
+    curr_node->type = Endcenter;
+}
+
+void
+parse_command(void)
+{
+    TextNode *link_node, *save_node, *arg_node;
+
+    gInButton++;
+    if (gParserMode == SimpleMode) {
+        curr_node->type = Noop;
+        fprintf(stderr, "Parser Error token %s unexpected\n",
+                token_table[token.type]);
+        longjmp(jmpbuf, 1);
+    }
+    gStringValueOk = 1;
+
+    /* set the values for the current node */
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+
+    /* now parse for the label */
+    link_node = curr_node;
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endbutton;
+    save_node = curr_node;
+    arg_node = alloc_node();
+    curr_node = arg_node;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+    link_node->link = make_link_window(arg_node, link_node->type, 0);
+    gStringValueOk = 0;
+    curr_node = save_node;
+    gInButton--;
+}
+
+void
+parse_button(void)
+{
+    TextNode *link_node, *save_node;
+
+    gInButton++;
+    if (gParserMode == SimpleMode) {
+        curr_node->type = Noop;
+        fprintf(stderr, "Parser Error token %s unexpected\n",
+                token_table[token.type]);
+        longjmp(jmpbuf, 1);
+    }
+    /* fill the node */
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+
+    /* the save the current node for creating the link and stuff */
+    link_node = curr_node;
+
+    /* then parse the label */
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endbutton;
+
+    /* now try to get the argument node */
+    save_node = curr_node;
+    get_expected_token(Lbrace);
+    save_node->data.node = alloc_node();
+    curr_node = save_node->data.node;
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+
+    /*
+     * buffer[0] = '\0'; print_to_string(arg_node, buffer + 1);
+     */
+    link_node->link =
+        make_link_window(save_node->data.node, link_node->type, 0);
+    curr_node = save_node;
+    gInButton--;
+}
+
+extern int example_number;
+
+void
+parse_spadcommand(TextNode *spad_node)
+{
+    /*TextNode *node = NULL;*/
+
+    example_number++;
+    gInButton++;
+    spad_node->type = token.type;
+    spad_node->space = token.id[-1];
+    get_expected_token(Lbrace);
+    cur_spadcom = curr_node;
+
+    spad_node->next = alloc_node();
+    curr_node = spad_node->next;
+    parse_HyperDoc();
+    curr_node->type = Endspadcommand;
+    cur_spadcom = NULL;
+    spad_node->link = make_link_window(spad_node->next, spad_node->type, 1);
+    gInButton--;
+}
+
+void
+parse_spadsrc(TextNode *spad_node)
+{
+    char buf[512], *c = buf;
+    int ch, start_opts = 0;
+    /*TextNode *node = NULL;*/
+
+    example_number++;
+    gInButton++;
+    gInSpadsrc++;
+    spad_node->type = Spadsrc;
+    spad_node->space = token.id[-1];
+
+    cur_spadcom = curr_node;
+    spad_node->next = alloc_node();
+    curr_node = spad_node->next;
+
+    do {
+        ch = get_char();
+        if (ch == ']')
+            start_opts = 0;
+        if (start_opts)
+            *c++ = ch;
+        if (ch == '[')
+            start_opts = 1;
+    } while (ch != '\n');
+    *c = '\0';
+    parse_verbatim(Spadsrctxt);
+    parse_from_string(buf);
+
+    curr_node->type = Endspadsrc;
+    cur_spadcom = NULL;
+    spad_node->link = make_link_window(spad_node->next, Spadsrc, 1);
+    gInButton--;
+    gInSpadsrc--;
+}
+
+void
+parse_env(TextNode *node)
+{
+    char *env;
+    char buff[256];
+    char *buff_pntr = &buff[1];
+    int  noEnv = 0;
+
+    get_expected_token(Lbrace);
+    get_expected_token(Word);
+    env = getenv(token.id);
+
+    if (env == NULL) {
+        /** The environment variable was not found **/
+
+        fprintf(stderr, "(HyperDoc) Warning: environment variable \'%s\' was not found.\n",
+                token.id);
+
+        env = halloc(1, "string");
+        env[0] = '\0';
+        noEnv = 1;
+    }
+
+    buff[0] = token.id[-1];
+    strcpy(buff_pntr, env);
+
+    if (noEnv)
+        free(env);
+
+    node->data.text = alloc_string(buff_pntr);
+    node->type = Word;
+
+    get_expected_token(Rbrace);
+}
+
+/*
+ * This parse_value routine accepts an empty {} but makes it a zero instead
+ * of a one. Thus \indent{} is equivelant to \indent{0}
+ */
+
+void
+parse_value1(void)
+{
+    TextNode *value_node, *ocn = curr_node;
+    char *s;
+
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+
+    value_node = alloc_node();
+    value_node->type = Word;
+    curr_node->data.node = value_node;
+    get_expected_token(Lbrace);
+    s = get_input_string();
+    if (!is_number(s)) {
+        fprintf(stderr,
+           "Parser Error: parse for value was expecting a numeric value\n");
+        strcpy(value_node->data.text, "0");
+    }
+    else {
+        value_node->data.text = alloc_string(s);
+    }
+    curr_node = ocn;
+}
+
+/*
+ * This command accepts an empty argument command. Thus \space{} is
+ * equivelant \space{1}
+ */
+
+void
+parse_value2(void)
+{
+    TextNode *value_node, *ocn = curr_node;
+    char *s;
+
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+
+    value_node = alloc_node();
+    value_node->type = Word;
+    curr_node->data.node = value_node;
+    get_expected_token(Lbrace);
+    s = get_input_string();
+    if (!is_number(s)) {
+        fprintf(stderr,
+           "Parser Error: parse for value was expecting a numeric value\n");
+        strcpy(value_node->data.text, "1");
+    }
+    else {
+        value_node->data.text = alloc_string(s);
+    }
+    curr_node = ocn;
+}
+
+
+/* parse a \table sommand */
+
+void
+parse_table(void)
+{
+    TextNode *tn = curr_node;
+
+    if (gParserMode != AllMode) {
+        curr_node->type = Noop;
+        fprintf(stderr, "Parser Error token %s unexpected\n",
+                token_table[token.type]);
+        longjmp(jmpbuf, 1);
+    }
+    curr_node->type = Table;
+    get_expected_token(Lbrace);
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+
+    get_token();
+    if (token.type == Lbrace) {
+        while (token.type != Rbrace) {
+            curr_node->type = Tableitem;
+            curr_node->next = alloc_node();
+            curr_node = curr_node->next;
+            parse_HyperDoc();
+            curr_node->type = Endtableitem;
+            curr_node->next = alloc_node();
+            curr_node = curr_node->next;
+            get_token();
+        }
+        curr_node->type = Endtable;
+    }
+    else {                      /* a patch for SG for empty tables */
+        if (token.type != Rbrace) {
+            token_name(token.type);
+            fprintf(stderr,
+                    "Unexpected Token %s found while parsing a table\n",
+                    ebuffer);
+            print_page_and_filename();
+            jump();
+        }
+        tn->type = Noop;
+        tn->next = NULL;
+        free(curr_node);
+        curr_node = tn;
+    }
+}
+
+void
+parse_box(void)
+{
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+    curr_node->width = -1;
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endbox;
+}
+
+void
+parse_mbox(void)
+{
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+    curr_node->width = -1;
+    curr_node->next = alloc_node();
+    curr_node = curr_node->next;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endbox;
+}
+
+void
+parse_free(void)
+{
+    TextNode *free_node = curr_node;
+
+    curr_node->type = token.type;
+    curr_node->space = token.id[-1];
+    curr_node->width = -1;
+    curr_node->data.node = alloc_node();
+    curr_node = curr_node->data.node;
+    get_expected_token(Lbrace);
+    parse_HyperDoc();
+    curr_node->type = Endarg;
+    curr_node = free_node;
+}
+
+void
+parse_help(void)
+{
+    curr_node->type = Noop;
+    get_token();
+    if (token.type != Lbrace) {
+        token_name(token.type);
+        fprintf(stderr,"\\helppage was expecting a { and not a %s\n", ebuffer);
+        print_page_and_filename();
+        jump();
+    }
+
+/* before we clobber this pointer we better free the contents (cf. alloc_page) */
+    free(gPageBeingParsed->helppage);
+    gPageBeingParsed->helppage = alloc_string(get_input_string());
+
+    if (token.type != Rbrace) {
+        token_name(token.type);
+        fprintf(stderr, "\\helppage was expecting a } and not a %s\n",
+                ebuffer);
+        print_page_and_filename();
+        jump();
+    }
+}
+@
+\section{readbitmap.c}
+\subsection{zzopen change}
+The [[zzopen]] function used to be called [[zopen]] but this name has
+been picked up by Unix so we change it globally.
+<<zzopen change>>=
+    if (!(fd = zzopen(filename, "r"))) {
+        fprintf(stderr, "ReadBitmapFile: File >%s< not found\n", filename);
+        exit(-1);
+    }
+
+@
+\subsection{readbitmap.c}
+<<readbitmap.c>>=
+#define _READBITMAP_C
+
+#include "debug.h"
+<<hyper.h>>
+
+#include "all-hyper-proto.h1"
+#include "pixmap.h1"
+
+#define MAXLINE      256
+
+/*
+ * This file was produced by J.M. Wiley with some help from the bitmap editor
+ * routine. It reads in a bitmap file, and calls XCreatePixmapFromBitmapData
+ * to transform it into a Pixmap. He did this because the routine
+ * XReadBitmapFile does not seeem to work to well (whatecer that means).
+ */
+
+XImage *
+HTReadBitmapFile(Display *display,int screen,char * filename, 
+                 int *width, int *height)
+{
+    XImage *image;
+    FILE *fd;
+    char Line[256], Buff[256];
+    int num_chars;
+    char *ptr;
+    int rch;
+    int version;
+    int padding, chars_line, file_chars_line, file_chars;
+    int bytes;
+    int x_hot, y_hot;
+
+
+    image = XCreateImage(display, DefaultVisual(display, screen), 1,
+                         XYBitmap, 0, NULL, 0, 0, 8, 0);
+
+
+    (image)->byte_order = LSBFirst;     /* byte_order    */
+    (image)->bitmap_unit = 8;   /* bitmap-unit   */
+    (image)->bitmap_bit_order = LSBFirst;       /* bitmap-bit-order */
+
+<<zzopen change>>
+    /*
+     * Once it is open, lets get the width and height
+     */
+
+    if ((read_w_and_h(fd, (unsigned int *)width,(unsigned int *) height)) < 0) {
+        fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+        exit(-1);
+    }
+
+
+    /*
+     * Now get the next line, and see if it is hot spots or bits
+     */
+    if (fgets(Line, MAXLINE, fd) == NULL) {
+        fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+        exit(-1);
+    }
+
+    /*
+     * Now check the first character to see if it is a # or an s
+     */
+
+    if (Line[0] == '#') {
+        if ((read_hot(fd, Line, &x_hot, &y_hot)) < 0) {
+            fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+            exit(-1);
+        }
+    }
+
+    (image)->width = *width;
+    (image)->height = *height;
+
+    /*
+     * figure out what version
+     */
+
+    if (sscanf(Line, "static short %s = {", Buff) == 1)
+        version = 10;
+    else if (sscanf(Line, "static unsigned char %s = {", Buff) == 1)
+        version = 11;
+    else if (sscanf(Line, "static char %s = {", Buff) == 1)
+        version = 11;
+    else {
+        fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+        exit(-1);
+    }
+
+    padding = 0;
+    if ((*width % 16) && ((*width % 16) < 9) && (version == 10))
+        padding = 1;
+
+    (image)->bytes_per_line = chars_line = (*width + 7) / 8;
+    file_chars_line = chars_line + padding;
+
+    num_chars = chars_line * (*height);
+    file_chars = file_chars_line * (*height);
+    (image)->data = (char *) halloc((image)->bytes_per_line * (image)->height,
+                                    "Read Pixmap--Image data");
+
+    /*
+     * Since we are just holding the first line of the declaration, we can
+     * just start reading from fd
+     */
+
+    if (version == 10)
+        for (bytes = 0, ptr = (image)->data; bytes < file_chars; (bytes += 2)) {
+            if (fscanf(fd, " 0x%x%*[,}]%*[ \n]", &rch) != 1) {
+                fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+                exit(-1);
+            }
+            *(ptr++) = rch & 0xff;
+            if (!padding || ((bytes + 2) % file_chars_line))
+                *(ptr++) = rch >> 8;
+        }
+    else
+        for (bytes = 0, ptr = (image)->data; bytes < file_chars; bytes++, ptr++) {
+            if (fscanf(fd, " 0x%x%*[,}]%*[ \n]", &rch) != 1) {
+                fprintf(stderr, "ReadBitmapFile: Bad file format in %s\n", filename);
+                exit(-1);
+            }
+            *ptr = rch;
+        }
+
+    fclose(fd);
+
+    return image;
+}
+
+static int
+read_hot(FILE *fd,char Line[],int *x_hot,int *y_hot)
+{
+    char Buff[256];
+
+    /*
+     * Works much the same as get width and height, just new variables
+     */
+
+    if (sscanf(Line, "#define %s %d", Buff, x_hot) != 2)
+        return -1;
+
+    if (fgets(Line, MAXLINE, fd) == NULL)
+        return -1;
+
+    if (sscanf(Line, "#define %s %d", Buff, y_hot) != 2)
+        return -1;
+
+    if (fgets(Line, MAXLINE, fd) == NULL)
+        return -1;
+    return 1;
+}
+
+static int
+read_w_and_h(FILE *fd,unsigned int *width,unsigned int *height)
+{
+    char Line[256], Buff[256];
+
+    if (fgets(Line, MAXLINE, fd) == NULL)
+        return -1;
+
+    /*
+     * Once we have the line, scan it for the width
+     */
+
+    if (sscanf(Line, "#define %s %d", Buff, width) != 2)
+        return -1;
+
+    /*
+     * Hopefully we have the width, now get the height the same way
+     */
+
+    if (fgets(Line, MAXLINE, fd) == NULL)
+        return -1;
+
+
+    /*
+     * Once we have the line, scan it for the height
+     */
+
+    if (sscanf(Line, "#define %s %d", Buff, height) != 2)
+        return -1;
+
+    return 1;
+}
+
+
+/* read a bitmap file into memory */
+
+ImageStruct *
+insert_image_struct(char *filename)
+{
+    int bm_width, bm_height;
+    XImage *im;
+    ImageStruct *image;
+
+    if (*filename == ' ')
+        filename++;
+    if ((image = (ImageStruct *) hash_find(&gImageHashTable, filename)) == NULL) {
+        im = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                              &bm_width, &bm_height);
+
+        /*
+         * now add the image to the gImageHashTable
+         */
+
+        image = (ImageStruct *) halloc(sizeof(ImageStruct), "ImageStruct");
+        image->image.xi = im;
+        image->width = image->image.xi->width;
+        image->height = image->image.xi->height;
+        image->filename = (char *) halloc(sizeof(char) * strlen(filename) +1,
+                                          "insert_image--filename");
+
+        /* strcpy(image->filename, filename); */
+
+        sprintf(image->filename, "%s", filename);
+        hash_insert(&gImageHashTable,(char *) image, image->filename);
+    }
+    return image;
+}
+@
+\section{scrollbar.h}
+<<scrollbar.h>>=
+#ifndef _SCROLLBAR_H_
+#define _SCROLLBAR_H_ 1
+
+<<hyper.h>>
+
+extern int  gScrollbarWidth;
+
+#endif
+@
+\section{scrollbar.c}
+<<scrollbar.c>>=
+/******************************************************************************
+ *
+ * scrollbar.c:  HyperDoc Scrollbar routines
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _SCROLLBAR_C
+#include "debug.h"
+
+<<extent.h>>
+<<display.h>>
+<<group.h>>
+<<initx.h>>
+<<scrollbar.h>>
+<<parse.h>>
+
+#include "all-hyper-proto.h1"
+
+/*************************************************************************
+  Scrollbar Comments                                    10/08/89
+
+  The scrollbar is displayed on the side of the HyperDoc display, if needed.
+  It is composed of four windows
+
+  fScrollUpWindow --  the arrowed box at the top of the scrollbar. Scrolls the
+  window up a line at a time.
+  fScrollDownWindow --  Located at the bottom of the window, it is used to scroll
+  down a single line at a time.
+  scrollbar -- this is the window which does the variable scrolling. It
+  houses the actual scroller.
+  scroller -- This is the scroller inside the scroll bar.
+
+  The procedure below, makes all these windows, and also makes three bitmaps,
+  sup -- The up arrow for the fScrollUpWindow.
+  sdown -- the down arrow for the fScrollDownWindow.
+  scroller -- the scroller stipple.
+  It then fills the window with the proper Pixmap background.
+
+  The scrollbar and scroller works as follows. The size of the scroller is
+  calculated as
+
+  size of scroller            size of visible text
+  -----------------  ===  ------------------------------  .
+  size of scrollbar       size of whole scrolling region
+
+  The top of the scroller shows the relative position in the page of
+  the top of the scrolling region. This way the user knows how far
+  down the page he or she has moved.
+  When the user clicks in the scrollbar, the center of the
+  scroller, if possible, is placed at the point of the click.
+
+  See the routines
+  showScrollBars --  to see how the scroll bars are actually realized.
+  moveScroller --  to see how the scroller is moved when the user scrolls
+
+
+  **************************************************************************/
+
+static int  ch(int height);
+static void changeWindowBackgroundPixmap(Window window, Pixmap pixmap);
+
+static Pixmap sup = 0, sdown = 0, sup_pressed = 0, sdown_pressed = 0, scroller = 0, scrollbar_pix = 0;
+
+#define sdown3d_width 21
+#define sdown3d_height 21
+static char sdown3d_bits[] = {
+   0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x15, 0x02, 0x00, 0x0c, 0x51, 0x55, 0x15,
+   0xaa, 0xaa, 0x0e, 0x51, 0x5f, 0x15, 0xaa, 0xae, 0x0e, 0x51, 0x5f, 0x15,
+   0xaa, 0xae, 0x0e, 0x51, 0x5f, 0x15, 0xea, 0xff, 0x0e, 0xd1, 0x7f, 0x15,
+   0xaa, 0xbf, 0x0e, 0x51, 0x5f, 0x15, 0xaa, 0xae, 0x0e, 0x51, 0x55, 0x15,
+   0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x15, 0xfe, 0xff, 0x0f, 0x55, 0x55, 0x15,
+   0xaa, 0xaa, 0x0a};
+#define sdown3dpr_width 21
+#define sdown3dpr_height 21
+static char sdown3dpr_bits[] = {
+   0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x15, 0xfe, 0xff, 0x0f, 0x55, 0x55, 0x11,
+   0xae, 0xaa, 0x0a, 0x55, 0x55, 0x11, 0xae, 0xbe, 0x0a, 0x55, 0x5d, 0x11,
+   0xae, 0xbe, 0x0a, 0x55, 0x5d, 0x11, 0xae, 0xbe, 0x0a, 0xd5, 0xff, 0x11,
+   0xae, 0xff, 0x0a, 0x55, 0x7f, 0x11, 0xae, 0xbe, 0x0a, 0x55, 0x5d, 0x11,
+   0xae, 0xaa, 0x0a, 0x55, 0x55, 0x11, 0x06, 0x00, 0x08, 0x55, 0x55, 0x15,
+   0xaa, 0xaa, 0x0a};
+
+#define sup3d_width 21
+#define sup3d_height 21
+static char sup3d_bits[] = {
+   0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x15, 0x02, 0x00, 0x0c, 0x51, 0x55, 0x15,
+   0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x15, 0xaa, 0xae, 0x0e, 0x51, 0x5f, 0x15,
+   0xaa, 0xbf, 0x0e, 0xd1, 0x7f, 0x15, 0xea, 0xff, 0x0e, 0x51, 0x5f, 0x15,
+   0xaa, 0xae, 0x0e, 0x51, 0x5f, 0x15, 0xaa, 0xae, 0x0e, 0x51, 0x5f, 0x15,
+   0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x15, 0xfa, 0xff, 0x0f, 0x55, 0x55, 0x15,
+   0xaa, 0xaa, 0x0a};
+#define sup3dpr_width 21
+#define sup3dpr_height 21
+static char sup3dpr_bits[] = {
+   0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x15, 0xfe, 0xff, 0x0f, 0x55, 0x55, 0x11,
+   0xae, 0xaa, 0x0a, 0x55, 0x55, 0x11, 0xae, 0xaa, 0x0a, 0x55, 0x5d, 0x11,
+   0xae, 0xbe, 0x0a, 0x55, 0x7f, 0x11, 0xae, 0xff, 0x0a, 0xd5, 0xff, 0x11,
+   0xae, 0xbe, 0x0a, 0x55, 0x5d, 0x11, 0xae, 0xbe, 0x0a, 0x55, 0x5d, 0x11,
+   0xae, 0xbe, 0x0a, 0x55, 0x55, 0x11, 0x06, 0x00, 0x08, 0x55, 0x55, 0x15,
+   0xaa, 0xaa, 0x0a};
+
+#define sup_width  sup3d_width
+#define sup_height sup3d_height
+#define sup_bits sup3d_bits
+
+#define sdown_width  sdown3d_width
+#define sdown_height sdown3d_height
+#define sdown_bits   sdown3d_bits
+
+#define BACKCOLOR gControlBackgroundColor
+#define FORECOLOR gControlForegroundColor
+
+#define scroller_width 2
+#define scroller_height 2
+static char scroller_bits[] = {
+                               0x01, 0x02};
+
+
+
+static int supheight = sup_height;
+static int supwidth = sup_width;
+
+#define scrollbar_pix_width 3
+#define scrollbar_pix_height 3
+static char scrollbar_pix_bits[] = {0x00, 0x03, 0x00};
+
+
+
+int gScrollbarWidth = sup_width + 2;
+
+
+
+void
+makeScrollBarWindows(void)
+{
+    XSetWindowAttributes at;
+
+    at.cursor = gActiveCursor;
+    at.event_mask = ButtonPress;
+    /** create the bitmaps **/
+    if (supwidth != sdown_width || supheight != sdown_height) {
+        fprintf(stderr,
+                "Scrollbar error, up and down pointers must have the same dimensions\n");
+        exit(-1);
+    }
+
+    if (sup == 0)
+        sup = XCreatePixmapFromBitmapData(
+                                          gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                                          sup_bits, supwidth, supheight,
+                                          FORECOLOR, BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+
+    if (sdown == 0)
+        sdown = XCreatePixmapFromBitmapData(
+                                            gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                                      sdown_bits, sdown_width, sdown_height,
+                                            FORECOLOR, BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+
+    sup_pressed = XCreatePixmapFromBitmapData(
+                                                  gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                                sup3dpr_bits, sup3dpr_width, sup3dpr_height,
+                                                  FORECOLOR, BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+    sdown_pressed = XCreatePixmapFromBitmapData(
+                                                    gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                          sdown3dpr_bits, sdown3dpr_width, sdown3dpr_height,
+                                                    FORECOLOR, BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+
+    gWindow->fScrollUpWindow = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                                1, 1, supwidth, supheight,
+                                                gWindow->border_width,
+                                                gBorderColor,
+                                                BACKCOLOR);
+
+    gWindow->fScrollDownWindow = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                            1, 1, sdown_width, sdown_height,
+                                                  gWindow->border_width,
+                                                  gBorderColor,
+                                                  BACKCOLOR);
+
+    gWindow->scrollbar = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                              1, 1, 1, 1,
+                                              gWindow->border_width,
+                                              gBorderColor,
+                                              BACKCOLOR);
+    gWindow->scroller = XCreateSimpleWindow(gXDisplay, gWindow->scrollbar,
+                                             1, 1, 1, 1, 0,
+                                             gBorderColor,
+                                             BACKCOLOR);
+
+#ifdef DEBUG
+    fprintf(stderr, "Changing Window Attributes in scrollbar.c #2\n");
+#endif
+
+    at.background_pixmap = sup;
+    XChangeWindowAttributes(gXDisplay, gWindow->fScrollUpWindow,
+                            CWBackPixmap | CWEventMask | CWCursor, &at);
+
+    at.background_pixmap = sdown;
+    XChangeWindowAttributes(gXDisplay, gWindow->fScrollDownWindow,
+                            CWBackPixmap | CWEventMask | CWCursor, &at);
+
+    XChangeWindowAttributes(gXDisplay, gWindow->scrollbar,
+                            CWEventMask | CWCursor, &at);
+
+
+    if (scroller == 0)
+        scroller = XCreatePixmapFromBitmapData(gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                                               scroller_bits, scroller_width,
+                                               scroller_height,
+                                               FORECOLOR,
+                                               BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+    if (scrollbar_pix == 0)
+        scrollbar_pix = XCreatePixmapFromBitmapData(gXDisplay,
+                                    RootWindow(gXDisplay, gXScreenNumber),
+                                                    scrollbar_pix_bits,
+                                                    scrollbar_pix_width,
+                                                    scrollbar_pix_height,
+                                                    FORECOLOR,
+                                                    BACKCOLOR,
+                                 DefaultDepth(gXDisplay, gXScreenNumber));
+
+    at.background_pixmap = scroller;
+    XChangeWindowAttributes(gXDisplay, gWindow->scroller,
+                            CWBackPixmap | CWCursor, &at);
+    at.background_pixmap = scrollbar_pix;
+    XChangeWindowAttributes(gXDisplay, gWindow->scrollbar,
+                            CWBackPixmap, &at);
+}
+
+static void
+drawScroller3DEffects(HDWindow * hdWindow, int x1, int y1, int x2, int y2)
+{
+    XClearWindow(gXDisplay, hdWindow->scroller);
+
+    /* draw right "black" line */
+
+    XDrawLine(gXDisplay, hdWindow->scroller, hdWindow->fControlGC,
+              x2 - 3, y1 + 2, x2 - 3, y2 - 3);
+
+    /* draw bottom "black" line */
+
+    XDrawLine(gXDisplay, hdWindow->scroller, hdWindow->fControlGC,
+              x1 + 2, y2 - 3, x2 - 3, y2 - 3);
+
+    /* flip foreground and background colors */
+
+    XSetBackground(gXDisplay, hdWindow->fControlGC, gControlForegroundColor);
+    XSetForeground(gXDisplay, hdWindow->fControlGC, gControlBackgroundColor);
+
+    /* draw top "white" line */
+
+    XDrawLine(gXDisplay, hdWindow->scroller, hdWindow->fControlGC,
+              x1 + 2, y1 + 2, x2 - 3, y1 + 2);
+
+    /* draw left "white" line */
+
+    XDrawLine(gXDisplay, hdWindow->scroller, hdWindow->fControlGC,
+              x1 + 2, y1 + 2, x1 + 2, y2 - 3);
+
+    /* reset colors */
+
+    XSetBackground(gXDisplay, hdWindow->fControlGC, gControlBackgroundColor);
+    XSetForeground(gXDisplay, hdWindow->fControlGC, gControlForegroundColor);
+}
+
+void
+showScrollBars(HDWindow * hdWindow)
+{
+    XWindowChanges wc;
+    /*int src_x = 0, src_y = 0;*/
+    /*unsigned int width = supwidth, height = supheight;*/
+    /*int dest_x = 0, dest_y = 0;*/
+
+    /* see if we even need scroll bars */
+
+    if (hdWindow->page->scrolling->height <= hdWindow->scrollheight)
+        return;
+
+    wc.x = hdWindow->scrollx;
+    wc.y = hdWindow->scrollupy;
+    wc.height = supheight;
+    wc.width = supwidth;
+    XConfigureWindow(gXDisplay, hdWindow->fScrollUpWindow, CWX | CWY | CWHeight
+                     | CWWidth, &wc);
+    wc.y = hdWindow->scrolldowny;
+    XConfigureWindow(gXDisplay, hdWindow->fScrollDownWindow,
+                     CWX | CWY | CWHeight | CWWidth,
+                     &wc);
+    wc.height = hdWindow->fScrollBarHeight;
+    wc.y = hdWindow->scrollbary;
+    XConfigureWindow(gXDisplay, hdWindow->scrollbar,
+                     CWX | CWY | CWHeight | CWWidth,
+                     &wc);
+    wc.x = 0;
+    wc.y = hdWindow->fScrollerTopPos;
+    wc.width = supwidth;
+    wc.height = hdWindow->fScrollerHeight;
+    XConfigureWindow(gXDisplay, hdWindow->scroller,
+                     CWX | CWY | CWHeight | CWWidth,
+                     &wc);
+
+    /*
+     * Now we map the windows, since the bitmaps are the backgrounds for the
+     * windows, we need to worry about redrawing them.
+     */
+
+    XMapWindow(gXDisplay, hdWindow->fScrollUpWindow);
+    XMapWindow(gXDisplay, hdWindow->fScrollDownWindow);
+    XMapWindow(gXDisplay, hdWindow->scrollbar);
+    XMapWindow(gXDisplay, hdWindow->scroller);
+
+    drawScroller3DEffects(hdWindow, 0, 0, wc.width, wc.height);
+}
+
+
+/************************************************************************
+
+  Moves the scroller to its proper place within the scrollbar. It
+  calculates how far down the page we are, and then moves the scroller
+  accordingly
+
+  **************************************************************************/
+
+void
+moveScroller(HDWindow * hdWindow)
+{
+    XWindowChanges wc;
+
+    /** moves the scroller to it's proper place **/
+
+    int t = (int) (hdWindow->fScrollBarHeight * (-hdWindow->page->scroll_off));
+    hdWindow->fScrollerTopPos = (int) (t / hdWindow->page->scrolling->height);
+    wc.x = 0;
+    wc.y = hdWindow->fScrollerTopPos;
+    wc.width = supwidth;
+    wc.height = hdWindow->fScrollerHeight;
+    XConfigureWindow(gXDisplay, hdWindow->scroller,
+                     CWX | CWY | CWHeight | CWWidth,
+                     &wc);
+    drawScroller3DEffects(hdWindow, 0, 0, wc.width, wc.height);
+}
+
+#define tophalf(y) ((y % 2 == 0)?(y/2):(y/2) + 1)
+#define bothalf(y) (y/2)
+
+void
+drawScrollLines(void)
+{
+    /* Checks the page_flags to see if we need a top, or a bottom line.   */
+    /* These are the horizontal lines framing a scrolling region when the */
+    /* scrolling region is not the entire window.                         */
+
+    if (!(gWindow->page->page_flags & NOLINES)) {
+        line_top_group();
+        if (gWindow->page->header->height) {
+            XDrawLine(gXDisplay, gWindow->fMainWindow, gWindow->fStandardGC,
+                      0,
+                      gWindow->page->top_scroll_margin - tophalf(gWindow->border_width)
+                      - 2 * scroll_top_margin,
+                      gWindow->scrollwidth,
+                      gWindow->page->top_scroll_margin - tophalf(gWindow->border_width)
+                      - 2 * scroll_top_margin);
+        }
+        if (gWindow->page->footer->height) {
+            XDrawLine(gXDisplay, gWindow->fMainWindow, gWindow->fStandardGC,
+                      0,
+                      gWindow->page->bot_scroll_margin + bothalf(gWindow->border_width) - 1,
+                      gWindow->scrollwidth,
+                      gWindow->page->bot_scroll_margin + bothalf(gWindow->border_width) - 1);
+        }
+        pop_group_stack();
+    }
+}
+
+
+/*
+ * Calculates all the measures for the scrollbars
+ */
+
+void
+calculateScrollBarMeasures(void)
+{
+    int t;
+
+    /*
+     * The scrollhieght is the height of the scrolling region visible in the
+     * HT window. Notice how it is a multiple of line height. This was needed
+     * to make everything scroll nicely.
+     */
+
+    gWindow->scrollheight = gWindow->page->bot_scroll_margin -
+        gWindow->page->top_scroll_margin - scroll_top_margin;
+    gWindow->scrollheight = gWindow->scrollheight - gWindow->scrollheight % line_height;
+
+    /*
+     * Now do a quick check to see if I really need a scroll bar, and if not,
+     * just return right away
+     */
+
+    if (gWindow->scrollheight >= gWindow->page->scrolling->height) {
+        gWindow->page->scroll_off = 0;
+        return;
+    }
+
+    /*
+     * The height of the scrollbar region, extends form the top page margin
+     * all the way to the bottom, excluding the room needed for the up and
+     * down windows
+     */
+
+    gWindow->fScrollBarHeight = gWindow->page->bot_scroll_margin -
+        gWindow->page->top_scroll_margin - 2 * supheight -
+        2 * gWindow->border_width;
+
+    gWindow->scrollupy = gWindow->page->top_scroll_margin - gWindow->border_width;
+    gWindow->scrollupy -= 2 * scroll_top_margin;
+    gWindow->scrolldowny = gWindow->page->bot_scroll_margin
+        - supheight - gWindow->border_width;
+    gWindow->scrollbary = gWindow->scrollupy + supheight + gWindow->border_width;
+    gWindow->scrollx = gWindow->width - supwidth - gWindow->border_width;
+
+    /*
+     * the scroller height is calculated from the following formula
+     *
+     * fScrollerHeight                    scrollheight --------------       ==
+     * --------- ------------- fScrollBarHeight
+     * page->scrolling_height
+     *
+     */
+
+    gWindow->fScrollerHeight = 1 + 2 * scroll_top_margin +        /** possible integer error correction **/
+        (int) (gWindow->fScrollBarHeight * gWindow->scrollheight / gWindow->page->scrolling->height);
+
+    /*
+     * Check the scroll offset, to see if it is too Large
+     */
+
+    if (-(gWindow->page->scroll_off) >
+        (gWindow->page->scrolling->height - gWindow->scrollheight))
+        gWindow->page->scroll_off =
+            -(gWindow->page->scrolling->height - gWindow->scrollheight);
+
+    /*
+     * Then move the top of the scroller to it's proper position
+     */
+
+    gWindow->fScrollBarHeight += 2 * scroll_top_margin;
+    t = (int) (gWindow->fScrollBarHeight * (-gWindow->page->scroll_off));
+    gWindow->fScrollerTopPos = (int) (t / (gWindow->page->scrolling->height));
+}
+
+void
+linkScrollBars(void)
+{
+    HyperLink *uplink = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+    HyperLink *downlink = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+    HyperLink *barlink = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+
+    uplink->win = gWindow->fScrollUpWindow;
+    downlink->win = gWindow->fScrollDownWindow;
+    barlink->win = gWindow->scrollbar;
+    uplink->type = Scrollupbutton;
+    downlink->type = Scrolldownbutton;
+    barlink->type = Scrollbar;
+    barlink->x = barlink->y = 0;
+    uplink->x = uplink->y = 0;
+    downlink->x = downlink->y = 0;
+    uplink->reference.node = NULL;
+    downlink->reference.node = NULL;
+    hash_insert(gLinkHashTable, (char *)uplink,(char *) &uplink->win);
+    hash_insert(gLinkHashTable, (char *)barlink,(char *) &barlink->win);
+    hash_insert(gLinkHashTable, (char *)downlink,(char *) &downlink->win);
+}
+
+void
+scrollUp(void)
+{
+
+    if (gWindow->page->scroll_off == 0);       /* BeepAtTheUser(); *//* The
+                                                 * beeping annoyed me. RSS */
+    else {
+        changeWindowBackgroundPixmap(gWindow->fScrollUpWindow, sup_pressed);
+
+        gWindow->page->scroll_off += line_height;      /* Scroll a line */
+        if (gWindow->page->scroll_off > 0)
+            gWindow->page->scroll_off = 0;
+        XCopyArea(gXDisplay, gWindow->fScrollWindow, gWindow->fScrollWindow, gWindow->fStandardGC,
+                  0, 0,
+            gWindow->scrollwidth, gWindow->scrollheight - line_height + 1,
+                  0, line_height);
+        XClearArea(gXDisplay, gWindow->fScrollWindow, 0, 0,
+                   gWindow->scrollwidth,
+                   line_height, False);
+        scroll_page(gWindow->page);
+
+        changeWindowBackgroundPixmap(gWindow->fScrollUpWindow, sup);
+    }
+
+}
+
+void
+scrollUpPage(void)
+{
+    if (gWindow->page->scroll_off == 0);       /* BeepAtTheUser(); */
+    else {
+        /* Scroll a page */
+
+        gWindow->page->scroll_off += ch(gWindow->scrollheight) - line_height;
+        if (gWindow->page->scroll_off > 0)
+            gWindow->page->scroll_off = 0;
+
+        XClearWindow(gXDisplay, gWindow->fScrollWindow);
+        scroll_page(gWindow->page);
+    }
+}
+
+void
+scrollToFirstPage(void)
+{
+    if (gWindow->page->scroll_off == 0);       /* BeepAtTheUser(); */
+    else {
+        gWindow->page->scroll_off = 0;
+        XClearWindow(gXDisplay, gWindow->fScrollWindow);
+        scroll_page(gWindow->page);
+    }
+}
+
+void
+scrollDown(void)
+{
+
+    if (-(gWindow->page->scroll_off) >=
+        (gWindow->page->scrolling->height - gWindow->scrollheight)) {
+        ;                       /* BeepAtTheUser(); */
+    }
+    else {
+        changeWindowBackgroundPixmap(gWindow->fScrollDownWindow, sdown_pressed);
+
+        gWindow->page->scroll_off -= line_height;      /* Scroll a line */
+
+        XCopyArea(gXDisplay, gWindow->fScrollWindow, gWindow->fScrollWindow, gWindow->fStandardGC,
+                  0, line_height,
+            gWindow->scrollwidth, gWindow->scrollheight - line_height + 1,
+                  0, 0);
+        XClearArea(gXDisplay, gWindow->fScrollWindow, 0,
+                   gWindow->scrollheight - line_height,
+                   gWindow->scrollwidth,
+                   line_height, False);
+        scroll_page(gWindow->page);
+
+        changeWindowBackgroundPixmap(gWindow->fScrollDownWindow, sdown);
+    }
+}
+
+
+void
+scrollDownPage(void)
+{
+    if (gWindow->page->scrolling == NULL || (-(gWindow->page->scroll_off) >=
+            (gWindow->page->scrolling->height - gWindow->scrollheight))) {
+        ;                       /* BeepAtTheUser(); */
+    }
+    else {
+        gWindow->page->scroll_off -= ch(gWindow->scrollheight) - line_height;
+
+        if (-(gWindow->page->scroll_off) >
+            (gWindow->page->scrolling->height - gWindow->scrollheight))
+            gWindow->page->scroll_off = -
+                (gWindow->page->scrolling->height - gWindow->scrollheight);
+
+        XClearWindow(gXDisplay, gWindow->fScrollWindow);
+
+        scroll_page(gWindow->page);
+    }
+}
+
+void
+scrollScroller(XButtonEvent * event)
+{
+
+    /*
+     * This routine checks to see where in the window the button press
+     * occured. It then tries to move the scroller so that the top of the
+     * scroller is at the spot of the event
+     */
+
+    int y = event->y;
+    int top = y;
+
+    if (top < 0) {
+        top = 0;
+        if (gWindow->fScrollerTopPos == 0)
+            return;
+        gWindow->page->scroll_off = 0;
+    }
+    else if ((top + gWindow->fScrollerHeight) > gWindow->fScrollBarHeight) {
+        top = gWindow->fScrollBarHeight - gWindow->fScrollerHeight;
+        if (top == gWindow->fScrollerTopPos)
+            return;
+        gWindow->page->scroll_off =
+            -(gWindow->page->scrolling->height - gWindow->scrollheight);
+        gWindow->page->scroll_off -= gWindow->page->scroll_off % line_height;
+    }
+    else {                      /** top is in an ok spot **/
+        int t;
+
+        t = -(gWindow->page->scrolling->height) * top;
+        t = t / (gWindow->fScrollBarHeight);
+        if (gWindow->page->scroll_off == (t -= t % line_height))
+            return;
+        gWindow->page->scroll_off = t;
+        gWindow->fScrollerTopPos = top;
+    }
+    XClearWindow(gXDisplay, gWindow->fScrollWindow);
+    scroll_page(gWindow->page);
+}
+
+
+void
+hideScrollBars(HDWindow * hdWindow)
+{
+    XUnmapWindow(gXDisplay, hdWindow->fScrollDownWindow);
+    XUnmapWindow(gXDisplay, hdWindow->fScrollUpWindow);
+    XUnmapWindow(gXDisplay, hdWindow->scrollbar);
+    XUnmapWindow(gXDisplay, hdWindow->scroller);
+}
+
+void
+getScrollBarMinimumSize(int *width, int *height)
+{
+    (*width)  = sup_width + 4;
+    (*height) = sup_height + sdown_height + 5;
+}
+
+static int
+ch(int height)
+{
+    /*int rheight;*/
+    int rem = height % line_height;
+
+    if (rem == 0)
+        return height;
+    return height - rem + line_height;
+}
+
+static void
+changeWindowBackgroundPixmap(Window window, Pixmap pixmap)
+{
+    if (pixmap) {
+        XSetWindowAttributes at;
+
+        at.background_pixmap = pixmap;
+        XChangeWindowAttributes(gXDisplay, window, CWBackPixmap, &at);
+        XClearWindow(gXDisplay, window);
+    }
+}
+@
+\section{search.h}
+Construct a page with a menu of references to the word.
+The syntax of the command is:
+\begin{verbatim}
+htsearch word
+\end{verbatim}
+<<htsearch>>=
+#!/bin/sh
+
+htbindir=$AXIOM/lib
+htpagedir=$AXIOM/doc/hypertex/pages
+
+
+if test -z "$1"
+then 
+	echo ""|$htbindir/presea case=1 -
+else
+( cd $htpagedir; $htbindir/hthits "$1" $htpagedir/ht.db | sort -r -n -k 1.22 | $htbindir/presea case=0 expr="$1" -)
+fi
 @ 
+This is part of 'presea' which is is run on output
+ of 'hthits'.  'hthits' outputs looks like:
+\begin{verbatim}
+ \newsearchresultentry{1}{Asp24 Example Code}{Asp24ExampleCode}
+ \newsearchresultentry{1}{Asp27 Example Code}{Asp27ExampleCode}
+ ....
+\end{verbatim}
+after splitting on ``[[{]]'' the first field is [['\newsearchresultentry']] 
+and the second is number of occurences of search term in the page.  The
+test for [['j >= 2']] is just to tolerate garbage.  presea is supposed
+to count the number of matches and put it in the header for search
+results.  The previous version reported no matches in the header.
+This used to read:
+\begin{verbatim}
+	a[n] = $0;
+	n=n+1;
+        j=split($0,b,"{");
+        m=m+substr(b[j],1,length(b[j])-1);
+\end{verbatim}
+<<presea>>=
+#!/bin/awk -f
+BEGIN {n=0;m=0
+}
+
+{
+	a[n] = $0;
+	n=n+1;
+        j=split($0,b,"{");
+        if (j >= 2)
+          m=m+substr(b[2],1,length(b[2])-1);
+}
+
+END {
+ printf ("\\begin{page}{staticsearchpage}");
+ if (case==1)  
+  printf ("{No matches found}\n")
+ else if ( n==0 || m==0 ) 
+   printf ("{No matches found for {\\em %s}}\n",expr)
+  else 
+   printf ("{%d matches found in %d pages for {\\em %s}}\n",m,n,expr);
+ printf ("Matches\\tab{8}in Page\n");
+ printf "\\beginscroll\n";
+ printf "\\beginmenu\n";
+ for(i=0;i<n;i++) printf ("%s\n",a[i]);
+ printf "\\endmenu\n";
+ printf "\\endscroll\n";
+ printf "\\end{page}\n";
+}
+	
+@
+\section{show-types.h}
+<<show-types.h>>=
+#ifndef _SHOW_TYPES_H_
+#define _SHOW_TYPES_H_ 1
+
+<<hyper.h>>
+
+#endif
+@
+\section{show-types.c}
+<<show-types.c>>=
+/******************************************************************************
+ *
+ * show_types.c:  Show the various types of things that can show up in text
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _SHOW_TYPES_C
+#include "debug.h"
+
+
+<<show-types.h>>
+<<dialog.h>>
+<<display.h>>
+<<extent.h>>
+<<group.h>>
+<<mem.h>>
+
+#include "all-hyper-proto.h1"
+
+
+
+/*
+ * Display the page whose extent has been computed, using the actual size of
+ * the window, and y_off to determine clipped areas
+ */
+
+void
+show_text(TextNode *node, int Ender)
+{
+    /*int twidth, len;*/
+    /*int otext_x, otext_y, t;*/
+    /*XFontStruct *old_font;*/
+    /*int old_color;*/
+
+    for (; node != NULL; node = node->next) {
+        switch (node->type) {
+          case 0:
+          case Beginitems:
+          case Begintitems:
+          case Bound:
+          case Center:
+          case Free:
+          case HSpace:
+          case Indent:
+          case Indentrel:
+          case Item:
+          case Macro:
+          case Mbox:
+          case Newline:
+          case Noop:
+          case Par:
+          case Pound:
+          case Rbrace:
+          case Space:
+          case Tab:
+          case Table:
+          case Titem:
+          case VSpace:
+            break;
+
+          case Dash:
+          case Fi:
+          case Ifcond:
+            if (visible(node->y, node->height)) {
+                if (strlen(node->data.text) > 1) {
+                    XDrawLine(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC, node->x,
+                              node->y + gRegionOffset + y_off
+                              - gTopOfGroupStack->cur_font->descent -
+                              word_off_height,
+                              node->x + node->width,
+                         node->y + gRegionOffset + y_off - word_off_height -
+                              gTopOfGroupStack->cur_font->descent);
+                }
+                else {
+                    XDrawString(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC, node->x, node->y +
+                       gRegionOffset - gTopOfGroupStack->cur_font->descent + y_off,
+                                node->data.text, 1);
+                }
+            }
+            else {
+                if (above(node->y))
+                    need_scroll_up_button = 1;
+                else if (below(node->y))
+                    need_scroll_down_button = 1;
+            }
+            break;
+
+          case Lsquarebrace:
+          case Math:
+          case Punctuation:
+          case Rsquarebrace:
+          case Spadsrctxt:
+          case WindowId:
+          case Word:
+            if (visible(node->y, node->height))
+                XDrawString(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC, node->x, node->y +
+                       gRegionOffset - gTopOfGroupStack->cur_font->descent + y_off,
+                            node->data.text, node->width);
+            else {
+                if (above(node->y))
+                    need_scroll_up_button = 1;
+                else if (below(node->y))
+                    need_scroll_down_button = 1;
+            }
+            break;
+
+          case Verbatim:
+            push_group_stack();
+            tt_top_group();
+            if (visible(node->y, node->height))
+                XDrawString(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC, node->x, node->y +
+                       gRegionOffset - gTopOfGroupStack->cur_font->descent + y_off,
+                            node->data.text, node->width);
+            else {
+                if (above(node->y))
+                    need_scroll_up_button = 1;
+                else if (below(node->y))
+                    need_scroll_down_button = 1;
+            }
+            pop_group_stack();
+            break;
+
+          case Horizontalline:
+            if (visible(node->y, node->height)) {
+                line_top_group();
+                XDrawLine(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC, 0,
+                          node->y + gRegionOffset + y_off,
+                          gWindow->width,
+                          node->y + gRegionOffset + y_off);
+                pop_group_stack();
+            }
+            else {
+                if (above(node->y))
+                    need_scroll_up_button = 1;
+                else if (below(node->y))
+                    need_scroll_down_button = 1;
+            }
+            break;
+
+          case Box:
+            if (visible(node->y, node->height))
+                XDrawRectangle(gXDisplay, gWindow->fDisplayedWindow, gWindow->fStandardGC,
+                               node->x,
+                             node->y + gRegionOffset + y_off - node->height,
+                               node->width,
+                               node->height);
+            else {
+                if (above(node->y))
+                    need_scroll_up_button = 1;
+                else if (below(node->y))
+                    need_scroll_down_button = 1;
+            }
+            break;
+
+
+          case Downlink:
+          case Link:
+          case LispDownLink:
+          case LispMemoLink:
+          case Lispcommand:
+          case Lispcommandquit:
+          case Lisplink:
+          case Lispwindowlink:
+          case Memolink:
+          case Qspadcall:
+          case Qspadcallquit:
+          case Returnbutton:
+          case Spadcall:
+          case Spadcallquit:
+          case Spaddownlink:
+          case Spadlink:
+          case Spadmemolink:
+          case Unixcommand:
+          case Unixlink:
+          case Upbutton:
+          case Windowlink:
+            if (pix_visible(node->y, node->height))
+                show_link(node);
+            break;
+
+          case Spadcommand:
+          case Spadgraph:
+          case Spadsrc:
+            show_spadcommand(node);
+            break;
+
+          case Pastebutton:
+            if (visible(node->y, node->height))
+                show_pastebutton(node);
+            break;
+
+          case Paste:
+            show_paste(node);
+            break;
+
+          case Group:
+          case Tableitem:
+            push_group_stack();
+            break;
+
+          case Controlbitmap:
+            show_image(node, gWindow->fControlGC);
+            break;
+
+          case Inputbitmap:
+            show_image(node, gWindow->fStandardGC);
+            break;
+
+          case Inputpixmap:
+            show_image(node, gWindow->fStandardGC);
+            break;
+
+          case BoldFace:
+            bf_top_group();
+            break;
+
+          case Emphasize:
+            if (gTopOfGroupStack->cur_font == gRmFont)
+                em_top_group();
+            else
+                rm_top_group();
+            break;
+
+          case It:
+            em_top_group();
+            break;
+
+          case Sl:
+          case Rm:
+            rm_top_group();
+            break;
+
+          case Tt:
+            tt_top_group();
+            break;
+
+          case Inputstring:
+            show_input(node);
+            break;
+
+          case Radiobox:
+          case SimpleBox:
+            show_simple_box(node);
+            break;
+
+          case Beep:
+            LoudBeepAtTheUser();
+            break;
+
+          case Description:
+            bf_top_group();
+            break;
+
+          case Endspadsrc:
+          case Endspadcommand:
+            gInAxiomCommand = 1;
+          case Endtableitem:
+          case Enddescription:
+          case Endpastebutton:
+          case Endlink:
+          case Endbutton:
+          case Endgroup:
+            pop_group_stack();
+          case Endverbatim:
+          case Endmath:
+          case Endbox:
+          case Endtable:
+          case Endmbox:
+          case Endparameter:
+          case Endpaste:
+          case Endinputbox:
+          case Endcenter:
+          case Endmacro:
+          case Endif:
+          case Endtitems:
+          case Enditems:
+
+            /*
+             * Now since I can show specific regions of the text, then at
+             * this point I should check to see if I am the end
+             */
+            if (node->type == Ender)
+                return;
+            break;
+          case Endfooter:
+          case Endscrolling:
+          case Endheader:
+          case Endtitle:
+
+            /*
+             * regardless of what ender I have, I always terminate showing
+             * with one of these
+             */
+            return;
+          default:
+            fprintf(stderr, "Show_text: Unknown Node Type %d\n", node->type);
+            break;
+        }
+    }
+}
+
+static void
+show_link(TextNode *node)
+{
+    /* XFontStruct *old_font;*/
+    XWindowChanges wc;
+    /*int twidth, boxwidth, old_color;*/
+    int active;
+
+    switch (node->type) {
+      case Upbutton:
+        if (!need_up_button) {
+            XClearArea(gXDisplay, gWindow->fDisplayedWindow, node->x,
+                       node->y - node->height + gRegionOffset,
+                       node->width, node->height, 0);
+            active = 0;
+        }
+        else
+            active = 1;
+        break;
+      case Returnbutton:
+        if (!need_return_button) {
+            XClearArea(gXDisplay, gWindow->fDisplayedWindow, node->x,
+                       node->y - node->height + gRegionOffset,
+                       node->width, node->height, 0);
+            active = 0;
+        }
+        else
+            active = 1;
+        break;
+      case Helpbutton:
+        if (!need_help_button) {
+            XClearArea(gXDisplay, gWindow->fDisplayedWindow, node->x,
+                       node->y - node->height + gRegionOffset,
+                       node->width, node->height, 0);
+            active = 0;
+        }
+        else
+            active = 1;
+        break;
+      default:
+        active = 1;
+        break;
+    }
+
+    if (active) {
+        ButtonList *bl = alloc_button_list();
+
+        push_active_group();
+        wc.x = node->x;
+        wc.y = node->y - node->height + y_off + gRegionOffset;
+        wc.height = node->height;
+        wc.width = node->width - trailing_space(node->next);
+        bl->x0 = wc.x;
+        bl->y0 = wc.y;
+        bl->x1 = bl->x0 + wc.width;
+        bl->y1 = bl->y0 + wc.height;
+        bl->link = node->link;
+        if (!not_in_scroll) {
+            bl->y0 += gWindow->page->top_scroll_margin + scroll_top_margin;
+            bl->y1 += gWindow->page->top_scroll_margin + scroll_top_margin;
+            bl->next = gWindow->page->s_button_list;
+            gWindow->page->s_button_list = bl;
+        }
+        else {
+            bl->next = gWindow->page->button_list;
+            gWindow->page->button_list = bl;
+        }
+    }
+    else
+        rm_top_group();
+}
+
+static void
+show_paste(TextNode *node)
+{
+    PasteNode *paste;
+
+    if (!(paste = (PasteNode *) hash_find(gWindow->fPasteHashTable,
+        node->data.text)))
+            return;
+
+    /*
+     * Once I have got this far, then I had better save the current group
+     * stack and the item stack
+     */
+    if (paste->group)
+        free_group_stack(paste->group);
+    paste->group = (GroupItem *) copy_group_stack();
+    if (paste->item_stack)
+        free_item_stack(paste->item_stack);
+    paste->item_stack = (ItemStack *) copy_item_stack();
+}
+
+static void
+show_pastebutton(TextNode *node)
+{
+    /*XFontStruct *old_font;*/
+    XWindowChanges wc;
+    /*int twidth, boxwidth, old_color;*/
+    /*int active;*/
+
+    push_active_group();
+    wc.x = node->x;
+    wc.y = node->y - node->height + y_off + gRegionOffset;
+    wc.height = node->height;
+    wc.width = node->width - trailing_space(node->next);
+#ifdef DEBUG
+    fprintf(stderr, "Configure in  show_link %d %d %d %d\n",
+            wc.x, wc.y, wc.width, wc.height);
+#endif
+    XConfigureWindow(gXDisplay, node->link->win,
+        CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, node->link->win);
+}
+
+/* display an input string window */
+
+static void
+show_input(TextNode *node)
+{
+    /*XFontStruct *old_font;*/
+    XWindowChanges wc;
+    /*int twidth, boxwidth, old_color;*/
+    /*Window root, child;*/
+    /*int root_x, root_y, win_x, win_y, buttons;*/
+    InputItem *item;
+    char *inpbuffer;
+
+    item = node->link->reference.string;
+    inpbuffer = item->curr_line->buffer;
+
+    wc.border_width = 0;
+    wc.x = node->x;
+    wc.y = node->y + gRegionOffset + y_off - node->height + 2;
+    wc.height = node->height - 2;
+    wc.width = node->width;
+    if (pix_visible(node->y, node->height)) {
+        XConfigureWindow(gXDisplay, node->link->win,
+                         CWX | CWY | CWHeight | CWWidth | CWBorderWidth,
+                         &wc);
+        XMapWindow(gXDisplay, node->link->win);
+    }
+    XFlush(gXDisplay);
+    draw_inputsymbol(item);
+}
+
+static void
+show_simple_box(TextNode *node)
+{
+    XWindowChanges wc;
+    InputBox *box;
+
+    /* first configure the box size properly */
+    box = node->link->reference.box;
+    wc.x = node->x;
+    wc.y = node->y + gRegionOffset + y_off - node->height;
+    wc.height = ((box->picked) ?
+                 (box->selected->height) : (box->unselected->height));
+    wc.width = node->width;
+    if (visible(node->y + gTopOfGroupStack->cur_font->ascent, node->height)) {
+        XConfigureWindow(gXDisplay, node->link->win, CWX | CWY | CWHeight | CWWidth,
+                         &wc);
+        XMapWindow(gXDisplay, node->link->win);
+        if (box->picked)
+            pick_box(box);
+        else
+            unpick_box(box);
+    }
+}
+
+/* display a spad command node */
+
+static void
+show_spadcommand(TextNode *node)
+{
+    XWindowChanges wc;
+
+    gInAxiomCommand = 1;
+
+    push_spad_group();
+    wc.x = node->x;
+    if (node->type == Spadsrc)
+        wc.y = node->y + gRegionOffset + y_off - 2 * node->height;
+    else
+        wc.y = node->y + gRegionOffset + y_off - node->height;
+    wc.height = node->height;
+    wc.width = node->width;
+#ifdef DEBUG
+    fprintf(stderr, "Spadcommand configured %d x %d -- (%d, %d)\n",
+            wc.width, wc.height, wc.x, wc.y);
+#endif
+    XConfigureWindow(gXDisplay, node->link->win,
+        CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, node->link->win);
+}
+
+
+/* display a pixmap */
+
+static void
+show_image(TextNode *node, GC gc)
+{
+    int src_x, src_y, src_width, src_height, dest_x, dest_y, ret_val;
+
+    if (!pix_visible(node->y, node->height))
+        return;
+    if (node->image.xi == NULL)
+        return;
+
+    dest_x = node->x;
+    src_x = 0;
+    src_y = 0;
+    dest_y = node->y + gRegionOffset - node->height + y_off;
+    need_scroll_up_button = 1;
+    if (node->width > (right_margin - node->x))
+        src_width = right_margin - node->x;
+    else
+        src_width = node->width;
+
+    if (gDisplayRegion != Scrolling) {
+        src_y = 0;
+        src_height = node->image.xi->height;
+    }
+    else {
+        /* I may have only a partial image */
+        if (dest_y < 0) {       /* the top is cut off */
+            src_y = -dest_y;
+            dest_y = 0;
+            src_height = node->image.xi->height - src_y;
+        }
+        else if (dest_y + node->image.xi->height > gWindow->scrollheight) {
+            /* the bottom is cut off */
+            src_y = 0;
+            src_height = gWindow->scrollheight - dest_y;
+        }
+        else {                  /* the whole thing is visible */
+            src_y = 0;
+            src_height = node->image.xi->height;
+        }
+    }
+
+    ret_val = XPutImage(gXDisplay, gWindow->fDisplayedWindow, gc,
+            node->image.xi, src_x, src_y, dest_x, dest_y,
+            src_width, src_height);
+
+    switch (ret_val) {
+      case BadDrawable:
+        fprintf(stderr, "(HyperDoc: show_image) bad drawable\n");
+        break;
+      case BadGC:
+        fprintf(stderr, "(HyperDoc: show_image) bad GC");
+        break;
+      case BadMatch:
+        fprintf(stderr, "(HyperDoc: show_image) bad match");
+        break;
+      case BadValue:
+#ifndef HP9platform
+        fprintf(stderr, "(HyperDoc: show_image) bad value");
+#endif /* HP complains about this*/
+        break;
+    }
+}
+@
+\section{spadbuf.c}
+<<spadbuf.c>>=
+#define _SPADBUF_C
+#include "debug.h"
+
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#ifdef SGIplatform
+#include <bstring.h>
+#endif
+
+#include "bsdsignal.h"
+#include "edible.h"
+#include "com.h"
+
+#include "spadbuf.h1"
+#include "bsdsignal.h1"
+#include "sockio-c.h1"
+#include "edin.h1"
+#include "wct.h1"
+#include "prt.h1"
+#include "cursor.h1"
+#include "fnct-key.h1"
+
+
+
+unsigned char _INTR, _QUIT, _ERASE, _KILL, _EOF, _EOL, _RES1, _RES2;
+int contNum;                    /* do reading and all the other fun stuff
+                                 * depend on this for all there ioctl's */
+int num_read;
+
+/*
+ * Here are the term structures I need for setting and resetting the terminal
+ * characteristics.
+ */
+struct termios oldbuf;     /* the initial settings */
+struct termios canonbuf;   /* set it to be canonical */
+struct termios childbuf;
+
+short INS_MODE;            /* Flag for insert mode */
+short ECHOIT;              /* Flag for echoing */
+short PTY;
+int MODE;                  /* Am I in cbreak, raw, or canonical */
+
+char in_buff[1024];        /* buffer for storing characters read 
+			      until they are processed */
+char buff[MAXLINE];	   /* Buffers for collecting input and */
+int  buff_flag[MAXLINE];   /* flags for whether buff chars
+			      are printing or non-printing */
+int (*old_handler) ();
+Sock *session_sock, *menu_sock;
+char *buff_name = NULL;    /* name for the aixterm */
+
+/*
+ * This routine used to be used to send sigint onto spad, but now they go
+ * through just fine on their own reinstated for AIX V3.2
+ */
+
+static void
+spadbuf_inter_handler(int sig)
+{
+    send_signal(session_sock, SIGUSR2);
+}
+
+static void
+spadbuf_function_chars(void)
+{
+    /** once I have that get the special characters         ****/
+    _INTR = oldbuf.c_cc[VINTR];
+    _QUIT = oldbuf.c_cc[VQUIT];
+    _ERASE = oldbuf.c_cc[VERASE];
+    _KILL = oldbuf.c_cc[VKILL];
+    _EOF = oldbuf.c_cc[VEOF];
+    _EOL = oldbuf.c_cc[VEOL];
+    return;
+}
+
+/* act as terminal session for sock connected to stdin 
+   and stdout of another process */
+static void
+interp_io(void)
+{
+    char buf[1024];
+    fd_set rd;
+    int len, command;
+
+    while (1) {
+        FD_ZERO(&rd);
+        FD_SET(menu_sock->socket, &rd);
+        FD_SET(session_sock->socket, &rd);
+        FD_SET(1, &rd);
+        len = sselect(FD_SETSIZE, &rd, 0, 0, NULL);
+        if (len == -1) {
+            perror("stdio select");
+            return;
+        }
+        if (FD_ISSET(session_sock->socket, &rd)) {
+            len = sread(session_sock, buf, 1024, "stdio");
+            if (len == -1)
+                return;
+            else {
+                write(1, buf, len);
+            }
+        }
+        if (FD_ISSET(menu_sock->socket, &rd)) {
+            command = get_int(menu_sock);
+            switch (command) {
+              case -1:
+                exit(0);
+              case ReceiveInputLine:
+                get_string_buf(menu_sock, in_buff, 1024);
+                num_read = strlen(in_buff);
+                clear_buff();
+                do_reading();
+                break;
+              case TestLine:
+                break;
+              default:
+                break;
+            }
+        }
+        if (FD_ISSET(1, &rd)) {
+            num_read = read(0, in_buff, 1024);
+            do_reading();
+        }
+    }
+}
+
+static void
+init_parent(void)
+{
+
+    /** get the original termio settings, so I never have to check again **/
+    if (tcgetattr(0,&oldbuf) == -1) {
+        perror("Clef Trying to get terms initial settings");
+        exit(-1);
+    }
+
+    /** get the settings for my different modes ***/
+    if (tcgetattr(0,&canonbuf) == -1) {
+        perror("Clef Getting terminal settings");
+        exit(-1);
+    }
+
+    /*** set the buffer to read before an eoln is typed ***/
+    canonbuf.c_lflag &= ~(ICANON | ECHO | ISIG);
+    canonbuf.c_lflag |= ISIG;
+
+    /***  Accordingly tell it we want every character ***/
+    canonbuf.c_cc[VMIN] = 1;          /* we want every character  */
+    canonbuf.c_cc[VTIME] = 1;         /* these may require tweaking */
+
+    if (tcsetattr(0, TCSAFLUSH, &canonbuf) == -1) {
+        perror("Spadbuf setting parent to canon");
+        exit(0);
+    }
+
+    /*
+     * This routine is in edin.c and sets the users preferences for function
+     * keys. In order to use it I have to set childbuf to be the same as
+     * oldbuf
+     */
+
+
+    spadbuf_function_chars();
+    INS_MODE = 0;
+    ECHOIT = 1;
+    Cursor_shape(2);
+}
+
+int
+main(int argc,char **  argv)
+{
+    /*int name_found;*/
+    /*FILE *junk;*/
+    FILE *fopen();
+
+    /*
+     * Modified on 6/13/90 for the command line completion abiltities of
+     * Since I am only calling this program from within spadint, I decided
+     * that the usage should be
+     *
+     * spadbuf page_name [completion_ files]
+     *
+     */
+    if (argc < 2) {
+        fprintf(stderr, "Usage : spadbuf page_name [completion_files] \n");
+        exit(-1);
+    }
+    buff_name = *++argv;
+
+    while (*++argv) {
+        load_wct_file(*argv);
+    }
+    skim_wct();
+
+    session_sock = connect_to_local_server(SessionServer, InterpWindow, Forever);
+    menu_sock = connect_to_local_server(MenuServerName, InterpWindow, Forever);
+
+    bsdSignal(SIGINT, spadbuf_inter_handler,RestartSystemCalls);
+
+    /*
+     * set contNum so it is pointing down the socket to the childs
+     */
+    contNum = session_sock->socket;
+    send_string(menu_sock, buff_name);
+    init_parent();
+    define_function_keys();
+    init_reader();
+    PTY = 0;
+    interp_io();
+    return(1);
+}
+
+
+
+@
+\section{spadint.c}
+<<spadint.c>>=
+/* Still a problem with close_client */
+
+/* Communication interface for external AXIOM buffers */
+#define _SPADINT_C
+#include "debug.h"
+
+#include <signal.h>
+
+<<hyper.h>>
+<<mem.h>>
+<<parse.h>>
+#include "bsdsignal.h"
+
+#include "all-hyper-proto.h1"
+#include "sockio-c.h1"
+#include "bsdsignal.h1"
+
+
+typedef struct sock_list {      /* linked list of Sock */
+    Sock Socket;
+    struct sock_list *next;
+}   Sock_List;
+
+Sock_List *plSock = (Sock_List *) 0;
+Sock *spad_socket = (Sock *) 0; /* to_server socket for SpadServer */
+
+/* issue a AXIOM command to the buffer associated with a page */
+void
+issue_spadcommand(HyperDocPage *page, TextNode *command, 
+                  int immediate, int type)
+{
+    char *buf;
+    int ret_val;
+
+    ret_val = connect_spad();
+    if (ret_val == NotConnected || ret_val == SpadBusy)
+        return;
+
+    if (page->sock == NULL)
+        start_user_buffer(page);
+    ret_val = send_int(page->sock, TestLine);
+    if (ret_val == -1) {
+        page->sock = NULL;
+        clear_execution_marks(page->depend_hash);
+        issue_spadcommand(page, command, immediate, type);
+        return;
+    }
+    issue_dependent_commands(page, command, type);
+    ret_val = send_int(page->sock, ReceiveInputLine);
+    buf = print_to_string(command);
+    if (immediate) {
+        buf[strlen(buf) + 1] = '\0';
+        buf[strlen(buf)] = '\n';
+    }
+    if (type == Spadsrc)
+        send_pile(page->sock, buf);
+    else
+        send_string(page->sock, buf);
+    mark_as_executed(page, command, type);
+    gIsEndOfOutput = 0;
+}
+static void
+send_pile(Sock *sock,char * str)
+{
+    FILE *f;
+    char name[512], command[512];
+
+    sprintf(name, "/tmp/hyper%s.input", getenv("SPADNUM"));
+    f = fopen(name, "w");
+    if (f == NULL) {
+        fprintf(stderr, "Can't open temporary input file %s\n", name);
+        return;
+    }
+    fprintf(f, "%s", str);
+    fclose(f);
+    sprintf(command, ")read %s\n", name);
+    send_string(sock, command);
+}
+static void
+issue_dependent_commands(HyperDocPage *page, TextNode *command,int type)
+{
+    TextNode *node, *depend_label;
+    SpadcomDepend *depend;
+    int end_type = (type == Spadcommand || type == Spadgraph) ?
+    (Endspadcommand) : (Endspadsrc);
+
+    for (node = command->next; node->type != end_type;
+         node = node->next)
+        if (node->type == Free)
+            for (depend_label = node->data.node; depend_label != NULL;
+                 depend_label = depend_label->next)
+                if (depend_label->type == Word) {
+                    depend = (SpadcomDepend *)
+                        hash_find(page->depend_hash, depend_label->data.text);
+                    if (depend == NULL) {
+                        fprintf(stderr, "Error: dependency on undefined label: %s\n",
+                                depend_label->data.text);
+                        continue;
+                    }
+                    if (!depend->executed) {
+                        issue_spadcommand(page, depend->spadcom->next, 1,
+                                          depend->spadcom->type);
+                        while (!gIsEndOfOutput)
+                            pause();
+                        sleep(1);
+                    }
+                }
+}
+static void
+mark_as_executed(HyperDocPage *page, TextNode *command,int type)
+{
+    TextNode *node, *depend_label;
+    SpadcomDepend *depend;
+    int end_type = (type == Spadcommand || type == Spadgraph)
+    ? (Endspadcommand) : (Endspadsrc);
+
+    for (node = command; node->type != end_type; node = node->next)
+        if (node->type == Bound)
+            for (depend_label = node->data.node; depend_label != NULL;
+                 depend_label = depend_label->next)
+                if (depend_label->type == Word) {
+                    depend = (SpadcomDepend *)
+                        hash_find(page->depend_hash, depend_label->data.text);
+                    if (depend == NULL) {
+                        fprintf(stderr, "No dependency entry for label: %s\n",
+                                depend_label->data.text);
+                        continue;
+                    }
+                    depend->executed = 1;
+                }
+}
+
+/* start a spad buffer for the page associated with the give */
+static void
+start_user_buffer(HyperDocPage *page)
+{
+    char buf[1024], *title;
+    char *SPAD;
+    char spadbuf[250];
+    char complfile[250];
+    int ret_val;
+
+    SPAD = (char *) getenv("AXIOM");
+    if (SPAD == NULL) {
+        sprintf(SPAD, "/spad/mnt/rios");
+    }
+    sprintf(spadbuf, "%s/lib/spadbuf", SPAD);
+    sprintf(complfile, "%s/lib/command.list", SPAD);
+    title = print_to_string(page->title);
+    if (access(complfile, R_OK) == 0)
+
+        /*
+         * TTT says : why not invoke with "-name axiomclient" and set any
+         * defaults in the usual way
+         */
+#ifdef RIOSplatform
+        sprintf(buf,
+                "aixterm -sb -sl 500 -name axiomclient -n '%s' -T '%s'  -e  %s %s %s&",
+                title, title, spadbuf, page->name, complfile);
+    else
+        sprintf(buf,
+         "aixterm -sb -sl 500 -name axiomclient -n '%s' -T '%s' -e  %s %s&",
+                title, title, spadbuf, page->name);
+#else
+#ifdef SUNplatform
+        sprintf(buf,
+        "xterm -sb -sl 500 -name axiomclient -n '%s' -T '%s' -e  %s %s %s&",
+                title, title, spadbuf, page->name, complfile);
+    else
+        sprintf(buf,
+           "xterm -sb -sl 500 -name axiomclient -n '%s' -T '%s' -e  %s %s&",
+                title, title, spadbuf, page->name);
+#else
+        sprintf(buf,
+        "xterm -sb -sl 500 -name axiomclient -n '%s' -T '%s' -e  %s %s %s&",
+                title, title, spadbuf, page->name, complfile);
+    else
+        sprintf(buf,
+         "xterm -sb -sl 500 -name axiomclient -n '%s' -T '%s' -e  %s '%s'&",
+                title, title, spadbuf, page->name);
+#endif
+#endif
+    ret_val = system(buf);
+    if (ret_val == -1 || ret_val == 127) {
+
+        /*
+         * perror("running the xterm spadbuf program"); exit(-1);
+         */
+    }
+    accept_menu_server_connection(page);
+    sleep(2);
+}
+
+/* Clears the execution marks in a hash table when a buffer has been killed */
+static void
+clear_execution_marks(HashTable *depend_hash)
+{
+    int i;
+    HashEntry *h;
+    SpadcomDepend *depend;
+
+    if (depend_hash == 0)
+        return;
+    for (i = 0; i < depend_hash->size; i++)
+        for (h = depend_hash->table[i]; h != NULL; h = h->next) {
+            depend = (SpadcomDepend *) h->data;
+            depend->executed = 0;
+        }
+}
+
+Sock *
+accept_menu_connection(Sock *server_sock)
+{
+    int sock_fd /*, session, ret_code*/;
+    Sock_List *pls;
+    /*Sock local_sock;*/
+
+    /* Could only be InterpWindow */
+
+    pls = (Sock_List *) halloc(sizeof(Sock_List),"SockList");
+    sock_fd = accept(server_sock->socket, 0, 0);
+    if (sock_fd == -1) {
+        perror("session : accepting connection");
+        return 0;
+    }
+    (pls->Socket).socket = sock_fd;
+    get_socket_type((Sock *) pls);
+
+#ifdef DEBUG
+    fprintf(stderr,
+            "session: accepted InterpWindow , fd = %d\n", sock_fd);
+#endif
+
+
+    /* put new item at head of list */
+
+    if (plSock == (Sock_List *) 0) {
+        plSock = pls;
+        plSock->next = (Sock_List *) 0;
+    }
+    else {
+        pls->next = plSock;
+        plSock = pls;
+    }
+
+    /* need to maintain socket_mask since we roll our own accept */
+
+    FD_SET(plSock->Socket.socket, &socket_mask);
+    return (Sock *) plSock;
+}
+
+static void
+accept_menu_server_connection(HyperDocPage *page)
+{
+
+    /*
+     * TTT thinks this code should just provide a Sock to the page. The only
+     * client assumed is a spadbuf. Since spadbuf was invoked with the page
+     * name, it just passes it back here as a check (get_string line).
+     */
+    int ret_code/*, i*/;
+    fd_set rd;
+    Sock *sock;
+    char *buf_name;
+    HyperDocPage *npage;
+
+    if (page->sock != NULL)
+        return;
+    while (1) {
+        rd = server_mask;
+        ret_code = sselect(FD_SETSIZE, &rd, 0, 0, NULL);
+        if (ret_code == -1) {
+            perror("Session manager select");
+            continue;
+        }
+
+        if (server[1].socket > 0 && FD_ISSET(server[1].socket, &rd)) {
+            sock = accept_menu_connection(server + 1);
+            if (sock == 0)
+                return;
+            if (sock->purpose == InterpWindow) {
+                buf_name = get_string(sock);
+                npage = (HyperDocPage *)
+                    hash_find(gWindow->fPageHashTable, buf_name);
+                if (npage == NULL) {
+
+                    /*
+                     * Lets just try using the current page TTT doesn't know
+                     * why this could be detrimental
+                     *
+                     * fprintf(stderr, "connecting spadbuf to unknown page:
+                     * %s\n", buf_name);
+                     */
+                    page->sock = sock;
+                    return;
+                }
+                else {
+
+                    /*
+                     * For some reason npage and page may be different TTT
+                     * thinks this happens when a dynamic page has the same
+                     * name as an existing static page.
+                     */
+                    npage->sock = sock;
+                    page->sock = sock;
+                }
+                if (!strcmp(buf_name, page->name)) {
+                    return;
+                }
+            }
+        }
+    }
+}
+
+
+/*
+ * This procedure takes a HyperDoc node, and expands it into string
+ */
+
+char *p2sBuf = NULL;
+int p2sBufSize = 0;
+
+/*
+ * This routine takes a text node and creates a string out of it. This is for
+ * use with things such as spad commands. There are  a very limited set of
+ * node types it can handle, so be careful
+ */
+
+char *
+print_to_string(TextNode *command)
+{
+    int len = 0;
+
+    print_to_string1(command, &len);
+    p2sBuf = resizeBuffer(len, p2sBuf, &p2sBufSize);
+    return print_to_string1(command, NULL);
+}
+
+/* 
+see the code in ht-util.boot
+	$funnyQuote := char 127
+	$funnyBacks := char 128
+*/
+#define funnyEscape(c)  ((c) == '"' ? '\177' : ((c) == '\\' ? '\200' : c))
+#define funnyUnescape(c) ((c) == '\177' ? '"' : ((c) == '\200' ? '\\' : c))
+#define storeChar(ch) if (sizeBuf) (*sizeBuf)++; else  *c++ = (ch)
+#define storeString(str) for (s=str;*s;s++) {storeChar(*s);}
+
+extern int include_bf;
+
+char *
+print_to_string1(TextNode *command,int * sizeBuf)
+{
+    char *c = p2sBuf;
+    char *s;
+    InputItem *item;
+    LineStruct *curr_line;
+    int lcount;
+    InputBox *box;
+    int num_spaces;
+    int count;
+    TextNode *node;
+
+    /*
+     * Init the stack of text nodes, things are pushed on here when I trace
+     * through a nodes data.node. This way I always no where my next is.
+     */
+
+    for (node = command; node != NULL;) {
+        switch (node->type) {
+          case Newline:
+            storeChar('\n');
+            node = node->next;
+            break;
+          case Ifcond:
+            if (check_condition(node->data.ifnode->cond))
+                node = node->data.ifnode->thennode;
+            else
+                node = node->data.ifnode->elsenode;
+            break;
+          case Endarg:
+          case Endspadcommand:
+          case Endspadsrc:
+          case Endpix:
+            storeChar('\0');
+            return p2sBuf;
+          case Endverbatim:
+          case Endif:
+          case Fi:
+          case Endmacro:
+          case Endparameter:
+          case Rbrace:
+          case Endgroup:
+            node = node->next;
+            break;
+          case Punctuation:
+
+            /*
+             * Simply copy the piece of text
+             */
+            if (node->space & FRONTSPACE) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            node = node->next;
+            break;
+          case WindowId:
+
+            /*
+             * Simply copy the piece of text
+             */
+            if (node->space) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            storeChar(' ');
+            node = node->next;
+            break;
+          case Verbatim:
+          case Spadsrctxt:
+
+            /*
+             * Simply copy the piece of text
+             */
+            if (node->space) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+
+            /*
+             * now add the eol
+             */
+
+            /*
+             * if(node->next && node->next->type != Endspadsrc)
+             * storeChar('\n');
+             */
+            node = node->next;
+            break;
+          case Dash:
+          case Rsquarebrace:
+          case Lsquarebrace:
+          case Word:
+
+            /*
+             * Simply copy the piece of text
+             */
+            if (node->space) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            node = node->next;
+            break;
+          case BoxValue:
+            box = 
+             (InputBox *) hash_find(gWindow->page->box_hash, node->data.text);
+            if (box == NULL) {
+                fprintf(stderr, 
+                        "Print_to_string:Box %s Has no symbol table entry\n",
+                        node->data.text);
+                exit(-1);
+            }
+            storeChar(' ');
+            if (box->picked) {
+                storeChar('t');
+            }
+            else {
+                storeChar('n');
+                storeChar('i');
+                storeChar('l');
+            }
+            node = node->next;
+            break;
+          case StringValue:
+            item = return_item(node->data.text);
+            if (item != NULL) {
+                if (node->space)
+                    storeChar(' ');
+                curr_line = item->lines;
+                while (curr_line != NULL) {
+                    for (lcount = 0, s = curr_line->buffer; *s && lcount < item->size;
+                         s++, lcount++) {
+                        storeChar(funnyUnescape(*s));
+                    }
+                    if (curr_line->len <= item->size && curr_line->next)
+                        storeChar('\n');
+                    curr_line = curr_line->next;
+                }
+            }
+            else if ((box = (InputBox *) hash_find(gWindow->page->box_hash,
+                                                node->data.text)) != NULL) {
+                if (node->space) { storeChar(' '); }
+                if (box->picked) {
+                    storeChar('t');
+                }
+                else {
+                    storeChar('n');
+                    storeChar('i');
+                    storeChar('l');
+                }
+            }
+            else {
+                fprintf(stderr, "Error, Symbol %s has no symbol table entry\n",
+                        node->data.text);
+                exit(-1);
+            }
+            node = node->next;
+            break;
+          case Space:
+            num_spaces = (node->data.node != NULL ?
+                          atoi(node->data.node->data.text) : 1);
+            for (count = 0; count < num_spaces; count++)
+                storeChar(' ');
+            node = node->next;
+            break;
+          case Titlenode:
+          case Endtitle:
+          case Center:
+          case Endcenter:
+          case BoldFace:
+          case Emphasize:
+          case Indentrel:
+            node = node->next;
+            break;
+          case Bound:
+            if (include_bf) {
+                int len, i;
+                TextNode *n2 = node->data.node;
+
+                storeChar('\\');
+                storeChar('b');
+                storeChar('o');
+                storeChar('u');
+                storeChar('n');
+                storeChar('d');
+                storeChar('{');
+                for (; n2->type != Endarg; n2 = n2->next) {
+                    if (n2->type == Word) {
+                        len = strlen(n2->data.text);
+                        for (i = 0; i < len; i++)
+                            storeChar(n2->data.text[i]);
+                        storeChar(' ');
+                    }
+                }
+                storeChar('}');
+            }
+            node = node->next;
+            break;
+          case Free:
+            if (include_bf) {
+                int len, i;
+                TextNode *n2 = node->data.node;
+
+                storeChar('\\');
+                storeChar('f');
+                storeChar('r');
+                storeChar('e');
+                storeChar('e');
+                storeChar('{');
+                for (; n2->type != Endarg; n2 = n2->next) {
+                    if (n2->type == Word) {
+                        len = strlen(n2->data.text);
+                        for (i = 0; i < len; i++)
+                            storeChar(n2->data.text[i]);
+                        storeChar(' ');
+                    }
+                }
+                storeChar('}');
+            }
+            node = node->next;
+            break;
+          case Macro:
+            node = node->next;
+            break;
+          case Pound:
+            if (node->space) { storeChar(' '); }
+            node = node->next;
+            break;
+          case Group:
+            node = node->next;
+            break;
+          case Indent:
+            num_spaces = (node->data.node != NULL ?
+                          atoi(node->data.node->data.text) : 1);
+            for (count = 0; count < num_spaces; count++)
+                storeChar(' ');
+            node = node->next;
+            break;
+          default:
+            fprintf(stderr,
+                    "Print_to_string: Unrecognized Keyword Type %d\n",
+                    node->type);
+	    node=node->next;
+            break;
+        }
+    }
+    storeChar('\0');
+    return p2sBuf;
+}
+
+/*
+ * Send a lisp or spad command to the AXIOM server for execution , if
+ * type is link, then we wait for a HyperDoc card to be returned
+ */
+
+HyperDocPage *
+issue_server_command(HyperLink *link)
+{
+    TextNode *command = (TextNode *) link->reference.node;
+    int ret_val;
+    char *buf;
+    HyperDocPage *page;
+
+    ret_val = connect_spad();
+    if (ret_val == NotConnected) {
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "SpadNotConnectedPage");
+        if (page == NULL)
+            fprintf(stderr, "No SpadNotConnectedPage found\n");
+        return page;
+    }
+    if (ret_val == SpadBusy) {
+        page = (HyperDocPage *) hash_find(gWindow->fPageHashTable, "SpadBusyPage");
+        if (page == NULL)
+            fprintf(stderr, "No SpadBusyPage found\n");
+        return page;
+    }
+    switch_frames();
+    switch (link->type) {
+      case Qspadcall:
+      case Qspadcallquit:
+      case Spadlink:
+      case Spaddownlink:
+      case Spadmemolink:
+        send_int(spad_socket, QuietSpadCommand);
+        break;
+      case Spadcall:
+      case Spadcallquit:
+        send_int(spad_socket, SpadCommand);
+        break;
+      default:
+        send_int(spad_socket, LispCommand);
+        break;
+    }
+    buf = print_to_string(command);
+    send_string(spad_socket, buf);
+    if (link->type == Lispcommand || link->type == Spadcall
+        || link->type == Spadcallquit || link->type == Qspadcallquit
+        || link->type == Qspadcall || link->type == Lispcommandquit)
+        return NULL;
+    page = parse_page_from_socket();
+    return page;
+}
+
+int
+issue_serverpaste(TextNode *command)
+{
+    char *buf;
+    int ret_val;
+
+    ret_val = connect_spad();
+    if (ret_val == NotConnected || ret_val == SpadBusy)
+        return 1;
+    switch_frames();
+    send_int(spad_socket, LispCommand);
+    buf = print_to_string(command);
+    send_string(spad_socket, buf);
+    return 1;
+}
+
+/*
+ * issue a unix command
+ */
+void
+issue_unixcommand(TextNode *node)
+{
+    char *buf;
+    char *copy;
+
+
+    buf = print_to_string(node);
+    copy =(char *) halloc((strlen(buf)+2)*sizeof(char),"Unixcommand");
+    strcpy(copy,buf);
+    copy[strlen(buf) + 1] = '\0';
+    copy[strlen(buf)] = '&';
+    system(copy);
+    free(copy);
+    return;
+}
+
+HyperDocPage *
+issue_unixlink(TextNode *node)
+{
+    HyperDocPage *page;
+    char *buf;
+
+    buf = print_to_string(node);
+    if ((unixfd = popen(buf, "r")) == NULL) {
+        fprintf(stderr, "Error popening %s\n", buf);
+        exit(-1);
+    }
+    bsdSignal(SIGUSR2,SIG_IGN,0);
+    page = parse_page_from_unixfd();
+    bsdSignal(SIGUSR2,sigusr2_handler,0);
+    return page;
+}
+
+int
+issue_unixpaste(TextNode *node)
+{
+    char *buf;
+
+    buf = print_to_string(node);
+    if ((unixfd = popen(buf, "r")) == NULL) {
+        fprintf(stderr, "Error popening %s\n", buf);
+        exit(-1);
+    }
+    return 1;
+}
+
+
+/*
+ * called when session_server selects
+ */
+void
+service_session_socket(void)
+{
+    int cmd, pid;
+
+    cmd = get_int(session_server);
+    switch (cmd) {
+      case CloseClient:
+        pid = get_int(session_server);
+        if (pid != -1)
+            close_client(pid);
+        break;
+      default:
+        fprintf(stderr,
+                "(HyperDoc) Unknown command from SessionServer %d\n", cmd);
+        break;
+    }
+}
+
+
+/*
+ * let spad know which frame to issue command via
+ */
+static void
+switch_frames(void)
+{
+    if (session_server == NULL) {
+        fprintf(stderr, "(HyperDoc) No session manager connected!\n");
+        return;
+    }
+    if (gWindow->fAxiomFrame == -1) {
+        fprintf(stderr, "(HyperDoc) No AXIOM frame associated with top level window!\n");
+        return;
+    }
+    send_int(session_server, SwitchFrames);
+    send_int(session_server, gWindow->fAxiomFrame);
+}
+void
+send_lisp_command(char *command)
+{
+    int ret_val;
+
+    ret_val = connect_spad();
+    if (ret_val == NotConnected || ret_val == SpadBusy) {
+        return;
+    }
+    send_int(spad_socket, LispCommand);
+    send_string(spad_socket, command);
+}
+void
+escape_string(char *s)
+{
+    char *st;
+
+    for (st = s; *st; st++)
+        *st = funnyEscape(*st);
+}
+void
+unescape_string(char *s)
+{
+    char *st;
+
+    for (st = s; *st; st++)
+        *st = funnyUnescape(*st);
+}
+static void
+close_client(int pid)
+{
+    /*int i;*/
+    Sock_List *pSock, *locSock;
+
+    /*
+     * just need to drop the list item
+     */
+
+    if (plSock == (Sock_List *) 0)
+        return;
+
+    /*
+     * first check head
+     */
+
+    if ((plSock->Socket.pid == pid)) {
+        locSock = plSock;
+        if ((*plSock).next == (Sock_List *) 0) {
+            plSock = (Sock_List *) 0;
+        }
+        else {
+            plSock = plSock->next;
+        }
+        free(locSock);
+    }
+
+    /*
+     * now check the rest
+     */
+
+    else {
+        for (pSock = plSock; pSock->next != (Sock_List *) 0; pSock = pSock->next)
+            if (pSock->next->Socket.pid == pid) {
+                locSock = pSock->next;
+                if (pSock->next->next == (Sock_List *) 0) {
+                    pSock->next = (Sock_List *) 0;
+                }
+                else {
+                    pSock->next = pSock->next->next;
+                }
+                free(locSock);
+                break;
+            }
+    }
+}
+
+
+
+char *
+print_source_to_string(TextNode *command)
+{
+    int len = 0;
+
+    print_source_to_string1(command, &len);
+    p2sBuf = resizeBuffer(len, p2sBuf, &p2sBufSize);
+    return print_source_to_string1(command, NULL);
+}
+char *
+print_source_to_string1(TextNode *command,int * sizeBuf)
+{
+    char *c = p2sBuf;
+    char *s;
+    InputItem *item;
+    LineStruct *curr_line;
+    int lcount;
+    InputBox *box;
+    int num_spaces;
+    int count;
+    TextNode *node;
+
+    /*
+	print out HyperDoc source for what you see
+     */
+
+    for (node = command; node != NULL;) {
+        switch (node->type) {
+          case Newline:
+            storeString("\\newline\n");
+            node = node->next;
+            break;
+          case Par:
+ 	    storeString("\n\n");
+            node = node->next;
+            break;
+          case Indentrel:
+            storeString("\\indentrel{");
+	    storeString(node->data.node->data.text);
+            storeChar('}');
+            node = node->next;
+            break;
+          case Tab:
+            storeString("\\tab{");
+	    storeString(node->data.node->data.text);
+            storeChar('}');
+            node = node->next;
+            break;
+          case Ifcond:
+            if (check_condition(node->data.ifnode->cond))
+                node = node->data.ifnode->thennode;
+            else
+                node = node->data.ifnode->elsenode;
+            break;
+          case Endarg:
+          case Endspadsrc:
+          case Endpix:
+          case Endbutton:
+            storeChar('}');
+            node = node->next;
+	    break;
+          case Endverbatim:
+          case Endif:
+          case Fi:
+          case Endmacro:
+          case Endparameter:
+          case Rbrace:
+            node = node->next;
+            break;
+          case Punctuation:
+
+            /*
+             * Simply copy the piece of text
+             */
+            if (node->space & FRONTSPACE) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            node = node->next;
+            break;
+          case WindowId:
+            storeString("\\windowid ");
+            node = node->next;
+            break;
+          case Verbatim:
+          case Spadsrctxt:
+            if (node->space) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            node = node->next;
+            break;
+          case Dash:
+          case Rsquarebrace:
+          case Lsquarebrace:
+          case Word:
+            if (node->space) { storeChar(' '); }
+            for (s = node->data.text; *s; s++) { storeChar(*s); }
+            node = node->next;
+            break;
+          case BoxValue:
+            box = (InputBox *) hash_find(gWindow->page->box_hash, node->data.text);
+            if (box == NULL) {
+                fprintf(stderr, "Print_to_string:Box %s Has no symbol table entry\n",
+                        node->data.text);
+                exit(-1);
+            }
+            storeChar(' ');
+            if (box->picked) {
+                storeChar('t');
+            }
+            else {
+                storeChar('n');
+                storeChar('i');
+                storeChar('l');
+            }
+            node = node->next;
+            break;
+          case StringValue:
+            item = return_item(node->data.text);
+            if (item != NULL) {
+                if (node->space) {  storeChar(' '); }
+                curr_line = item->lines;
+                while (curr_line != NULL) {
+                    for (lcount = 0, s = curr_line->buffer; 
+                         *s && lcount < item->size;
+                         s++, lcount++) {
+                        storeChar(funnyUnescape(*s));
+                    }
+                    if (curr_line->len <= item->size && curr_line->next)
+                        storeChar('\n');
+                    curr_line = curr_line->next;
+                }
+            }
+            else if ((box = (InputBox *) hash_find(gWindow->page->box_hash,
+                                                node->data.text)) != NULL) {
+                if (node->space) { storeChar(' '); }
+                if (box->picked) {
+                    storeChar('t');
+                }
+                else {
+                    storeChar('n');
+                    storeChar('i');
+                    storeChar('l');
+                }
+            }
+            else {
+                fprintf(stderr, "Error, Symbol %s has no symbol table entry\n",
+                        node->data.text);
+                exit(-1);
+            }
+            node = node->next;
+            break;
+          case Space:
+            num_spaces = (node->data.node != NULL ?
+                          atoi(node->data.node->data.text) : 1);
+            for (count = 0; count < num_spaces; count++)
+                storeChar(' ');
+            node = node->next;
+            break;
+          case Emphasize:
+            storeString("\\em ");
+            node = node->next;
+            break;
+          case BoldFace:
+            storeString("\\bf ");
+            node = node->next;
+            break;
+          case Sl:
+            storeString("\\it ");
+            node = node->next;
+            break;
+          case Rm:
+            storeString("\\rm ");
+            node = node->next;
+            break;
+          case It:
+            storeString("\\it ");
+            node = node->next;
+            break;
+          case Tt:
+            storeString("\\tt ");
+            node = node->next;
+            break;
+          case Group:
+/* skip {} */
+            if (node->next->type==Endgroup){
+               node=node->next->next;
+               break;
+		}
+            storeChar('{');
+            node = node->next;
+            break;
+          case Endgroup:
+            storeChar('}');
+            node = node->next;
+            break;
+          case Box:
+            storeString("\\box{");
+            node = node->next;
+            break;
+          case Endbox:
+            storeChar('}');
+            node = node->next;
+            break;
+          case Center:
+            storeString("\\center{");
+            node = node->next;
+            break;
+          case Endcenter:
+            storeString("}");
+            storeChar('\n');
+            node = node->next;
+            break;
+          case Titlenode:
+          case Endtitle:
+            node = node->next;
+            break;
+          case Bound:
+            {
+                TextNode *n2 = node->data.node;
+
+                storeString("\\bound{");
+                for (; n2->type != Endarg; n2 = n2->next) {
+                    if (n2->type == Word) {
+                        storeString(n2->data.text);
+                        storeChar(' ');
+                        }
+                    }
+                storeChar('}');
+            }
+            node = node->next;
+            break;
+          case Free:
+	    {
+                TextNode *n2 = node->data.node;
+
+                storeString("\\free{");
+                for (; n2->type != Endarg; n2 = n2->next) {
+                    if (n2->type == Word) {
+			storeString(n2->data.text);
+                        storeChar(' ');
+                	}
+                    }
+                storeChar('}');
+		}
+            node = node->next;
+            break;
+          case Macro:
+	    storeChar(' ');
+            node = node->next;
+            break;
+          case Pound:
+            if (node->space) { storeChar(' '); }
+            node = node->next;
+            break;
+          case Indent:
+            num_spaces = (node->data.node != NULL ?
+                          atoi(node->data.node->data.text) : 1);
+            for (count = 0; count < num_spaces; count++)
+                storeChar(' ');
+            node = node->next;
+            break;
+          case Inputbitmap:
+            storeString("\\inputbitmap{");
+            storeString(node->data.text); 
+	    storeString("}\n");
+            node = node->next;
+            break;
+          case Endscrolling:
+            storeString("\\end{scroll}\n");
+            node = node->next;
+            break;
+          case Scrollingnode:
+            storeString("\\begin{scroll}\n");
+            storeString("% This is the scrolling area\n");
+            node = node->next;
+            break;
+          case Horizontalline:
+            storeString("\\horizontalline\n");
+            node = node->next;
+            break;
+          case Endtable:
+            storeChar('}');
+            node = node->next;
+            break;
+          case Table:
+            storeString("\\table{");
+            node = node->next;
+            break;
+          case Tableitem:
+            storeChar('{');
+            node = node->next;
+            break;
+          case Endtableitem:
+            storeChar('}');
+            node = node->next;
+            break;
+          case Beginitems:
+            storeString("\\begin{items}");
+            node = node->next;
+            break;
+          case Item:
+            storeString("\n\\item");
+            node = node->next;
+            break;
+          case Enditems:
+            storeString("\n\\end{items}");
+            node = node->next;
+            break;
+/*** LINKS ***/
+/* all these guys are ended by Endbutton 
+we close the brace then */
+          case Spadlink:
+            storeString("\\fauxspadlink{");
+            node = node->next;
+            break;
+          case Unixlink:
+            storeString("\\fauxunixlink{");
+            node = node->next;
+            break;
+          case Lisplink:
+            storeString("\\fauxlisplink{");
+            node = node->next;
+            break;
+          case Link:
+            storeString("\\fauxlink{");
+            node = node->next;
+            break;
+          case LispDownLink:
+            storeString("\\fauxlispdownlink{");
+            node = node->next;
+            break;
+          case LispMemoLink:
+            storeString("\\fauxlispmemolink{");
+            node = node->next;
+            break;
+          case Memolink:
+            storeString("\\fauxmemolink{");
+            node = node->next;
+            break;
+          case Windowlink:
+            storeString("\\fauxwindowlink{");
+            node = node->next;
+            break;
+          case Downlink:
+            storeString("\\fauxdownlink{");
+            node = node->next;
+            break;
+/** END OF LINKS **/
+          case Unixcommand:
+            storeString("\\unixcommand{");
+            node = node->next;
+            break;
+          case Lispcommand:
+            storeString("\\lispcommand{");
+            node = node->next;
+            break;
+          case Spadgraph:
+            storeString("\\spadgraph{");
+            node = node->next;
+            break;
+          case Spadcommand:
+            storeString("\\spadcommand{");
+            node = node->next;
+            break;
+          case Endspadcommand:
+            storeChar('}');
+            node = node->next;
+            break;
+          case Footernode:
+            storeString("% This is the footer\n");
+            node = node->next;
+            break;
+          case Endfooter:
+            storeString("% This is the end of the footer\n");
+            node = node->next;
+            break;
+          case Endheader:
+            storeString("% This is the end of the header\n");
+            node = node->next;
+            break;
+          case Headernode:
+            storeString("% This is the header\n");
+            node = node->next;
+            break;
+          default:
+            fprintf(stderr,
+                    "Print_to_string: Unrecognized Keyword Type %d\n",
+                    node->type);
+	    node=node->next;
+            break;
+        }
+    }
+    storeChar('\0');
+    return p2sBuf;
+}
+@
+\section{titlebar.h}
+<<titlebar.h>>=
+#ifndef _TITLEBAR_H_
+#define _TITLEBAR_H_ 1
+
+<<hyper.h>>
+
+extern int  twwidth, twheight;  /* the width and height for all windows in the */
+                                /* title bar */
+
+#endif
+@
+\section{titlebar.c}
+<<titlebar.c>>=
+/******************************************************************************
+ *
+ * titlebar.c:  Produces HyperDoc titlebar
+ *
+ * Copyright The Numerical Algorithms Group Limited 1991, 1992, 1993.
+ *
+ ****************************************************************************/
+#define _TITLEBAR_C
+#include "debug.h"
+
+#include <stdlib.h>
+
+<<titlebar.h>>
+<<display.h>>
+<<group.h>>
+<<initx.h>>
+<<show-types.h>>
+<<parse.h>>
+
+#include "all-hyper-proto.h1"
+
+extern int y_off;               /* y offset for scrolling regions */
+
+/* Images for the title bar windows */
+
+static XImage *tw1image = NULL,
+              *tw2image = NULL,
+              *tw3image = NULL,
+              *tw4image = NULL,
+              *noopimage = NULL;
+
+static char *tw1file  = "exit3d.bitmap";
+static char *tw2file  = "help3d.bitmap";
+static char *tw3file  = "home3d.bitmap";
+static char *tw4file  = "up3d.bitmap";
+static char *noopfile = "noop3d.bitmap";
+
+#define BACKCOLOR gControlBackgroundColor
+#define BUTTGC    fControlGC
+
+int twwidth, twheight;   /* the width and height for all windows in the */
+                         /* title bar */
+
+void
+makeTitleBarWindows(void)
+{
+    XSetWindowAttributes at;
+    unsigned long valuemask = 0L;
+
+    /* read the images if we don't have them already */
+
+    if (tw1image == NULL)
+        readTitleBarImages();
+
+    /* set the window attributes */
+
+    at.cursor = gActiveCursor;
+    valuemask |= CWCursor;
+    at.event_mask = ButtonPress;
+    valuemask |= CWEventMask;
+
+    /* create the windows for the buttons */
+
+    gWindow->fTitleBarButton1 = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                   1, 1, twwidth, twheight,
+                                   0, gBorderColor, BACKCOLOR);
+    XChangeWindowAttributes(gXDisplay, gWindow->fTitleBarButton1, valuemask, &at);
+
+    gWindow->fTitleBarButton2 = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                   1, 1, twwidth, twheight,
+                                   0, gBorderColor, BACKCOLOR);
+    XChangeWindowAttributes(gXDisplay, gWindow->fTitleBarButton2, valuemask, &at);
+
+    gWindow->fTitleBarButton3 = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                   1, 1, twwidth, twheight,
+                                   0, gBorderColor, BACKCOLOR);
+    XChangeWindowAttributes(gXDisplay, gWindow->fTitleBarButton3, valuemask, &at);
+
+    gWindow->fTitleBarButton4 = XCreateSimpleWindow(gXDisplay, gWindow->fMainWindow,
+                                   1, 1, twwidth, twheight,
+                                   0, gBorderColor, BACKCOLOR);
+    XChangeWindowAttributes(gXDisplay, gWindow->fTitleBarButton4, valuemask, &at);
+}
+
+void
+showTitleBar(void)
+{
+    XWindowChanges wc;
+    int height, hbw = (int) gWindow->border_width / 2;
+    XImage *image;
+
+    /*
+     * the first thing we do is pop up all the windows and
+     * place them properly
+     */
+
+    if (gWindow->page->title->height != twheight)
+        height = gWindow->page->title->height;
+    else
+        height = twheight;
+
+    push_active_group();
+
+    /* configure and map button number 1 */
+
+    wc.x = 0;
+    wc.y = 0;
+    wc.height = twheight;
+    wc.width = twwidth;
+    XConfigureWindow(gXDisplay, gWindow->fTitleBarButton1, CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, gWindow->fTitleBarButton1);
+
+    image = tw1image;
+    XPutImage(gXDisplay, gWindow->fTitleBarButton1, gWindow->BUTTGC,
+              image, 0, 0, 0, 0,
+              image->width,
+              image->height);
+
+    /* configure and map button number 2 */
+
+    wc.x += twwidth + gWindow->border_width;
+    XConfigureWindow(gXDisplay, gWindow->fTitleBarButton2, CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, gWindow->fTitleBarButton2);
+
+    image = need_help_button ? tw2image : noopimage;
+    XPutImage(gXDisplay, gWindow->fTitleBarButton2, gWindow->BUTTGC,
+              image, 0, 0, 0, 0,
+              image->width,
+              image->height);
+
+    /* configure and map button number 4 */
+
+    wc.x = gWindow->width - twwidth;
+    XConfigureWindow(gXDisplay, gWindow->fTitleBarButton4, CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, gWindow->fTitleBarButton4);
+
+    image = need_up_button ? tw4image : noopimage;
+    XPutImage(gXDisplay, gWindow->fTitleBarButton4, gWindow->BUTTGC,
+              image, 0, 0, 0, 0,
+              image->width,
+              image->height);
+
+    /* configure and map button number 3 */
+
+    wc.x = wc.x - twwidth - gWindow->border_width;
+    XConfigureWindow(gXDisplay, gWindow->fTitleBarButton3, CWX | CWY | CWHeight | CWWidth, &wc);
+    XMapWindow(gXDisplay, gWindow->fTitleBarButton3);
+
+    image = need_return_button ? tw3image : noopimage;
+    XPutImage(gXDisplay, gWindow->fTitleBarButton3, gWindow->BUTTGC,
+              image, 0, 0, 0, 0,
+              image->width,
+              image->height);
+
+    gWindow->fDisplayedWindow = gWindow->fMainWindow;
+    gDisplayRegion = Title;
+    gRegionOffset = 0;
+    y_off = 0;
+
+    pop_group_stack();
+
+    show_text(gWindow->page->title->next, Endheader);
+
+    /* Now draw the box around the title */
+
+    line_top_group();
+
+    XDrawLine(gXDisplay, gWindow->fMainWindow, gWindow->fStandardGC, 0, height + hbw,
+              gWindow->width, height + hbw);
+
+    pop_group_stack();
+
+}
+
+void
+linkTitleBarWindows(void)
+{
+    HyperLink *tw1link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink"),
+              *tw2link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink"),
+              *tw3link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink"),
+              *tw4link = (HyperLink *) halloc(sizeof(HyperLink), "HyperLink");
+
+    tw1link->win = gWindow->fTitleBarButton1;
+    tw1link->type = Quitbutton;
+    tw1link->reference.node = NULL;
+    tw1link->x = tw1link->y = 0;
+
+    tw2link->win = gWindow->fTitleBarButton2;
+    tw2link->type = Helpbutton;
+    tw2link->reference.node = NULL;
+    tw2link->x = tw2link->y = 0;
+
+    tw3link->win = gWindow->fTitleBarButton3;
+    tw3link->type = Returnbutton;
+    tw3link->reference.node = NULL;
+    tw3link->x = tw3link->y = 0;
+
+    tw4link->win = gWindow->fTitleBarButton4;
+    tw4link->type = Upbutton;
+    tw4link->reference.node = NULL;
+    tw4link->x = tw4link->y = 0;
+
+    hash_insert(gLinkHashTable, (char *)tw1link,(char *) &tw1link->win);
+    hash_insert(gLinkHashTable, (char *)tw2link,(char *) &tw2link->win);
+    hash_insert(gLinkHashTable, (char *)tw3link,(char *) &tw3link->win);
+    hash_insert(gLinkHashTable, (char *)tw4link,(char *) &tw4link->win);
+}
+
+static void
+readTitleBarImages(void)
+{
+    int w, h;
+    char filename[128];
+    char *axiomEnvVar = NULL;
+
+    axiomEnvVar = getenv("AXIOM");
+
+    if (axiomEnvVar)
+        sprintf(filename, "%s/doc/hypertex/bitmaps/%s", axiomEnvVar, tw1file);
+    else
+        sprintf(filename, "%s", tw1file);
+    tw1image = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                &twwidth, &twheight);
+
+    if (axiomEnvVar)
+        sprintf(filename, "%s/doc/hypertex/bitmaps/%s", axiomEnvVar, tw2file);
+    else
+        sprintf(filename, "%s", tw2file);
+    tw2image = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                &w, &h);
+    twwidth = ((twwidth >= w) ? (twwidth) : (w));
+
+    if (axiomEnvVar)
+        sprintf(filename, "%s/doc/hypertex/bitmaps/%s", axiomEnvVar, tw3file);
+    else
+        sprintf(filename, "%s", tw3file);
+    tw3image = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                &w, &h);
+    twwidth = ((twwidth >= w) ? (twwidth) : (w));
+
+    if (axiomEnvVar)
+        sprintf(filename, "%s/doc/hypertex/bitmaps/%s", axiomEnvVar, tw4file);
+    else
+        sprintf(filename, "%s", tw4file);
+    tw4image = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                &w, &h);
+    twwidth = ((twwidth >= w) ? (twwidth) : (w));
+
+
+    if (axiomEnvVar)
+        sprintf(filename, "%s/doc/hypertex/bitmaps/%s", axiomEnvVar, noopfile);
+    else
+        sprintf(filename, "%s", noopfile);
+    noopimage = HTReadBitmapFile(gXDisplay, gXScreenNumber, filename,
+                                 &twwidth, &twheight);
+}
+
+void
+getTitleBarMinimumSize(int *width, int *height)
+{
+    (*width)  = 4 * twwidth + 40;
+    (*height) = twheight + 2;
+}
+@
+\section{token.h}
+<<token.h>>=
+#ifndef _TOKEN_H_
+#define _TOKEN_H_ 1
+
+/*
+ Here are a couple of flags added for whitespace stuff. They tell
+      punctuation if there was space in front of it or not
+*/
+
+#define FRONTSPACE 0001
+#define BACKSPACE  0002
+
+/* HyperDoc parser tokens */
+
+typedef struct toke {
+  int type;		/* token type.	One of those listed below */
+  char *id;		/* string value if type == Identifier */
+} Token;
+
+/*
+    User tokens. ie, these can be found on a page
+*/
+
+#define Word		      1
+#define Page		      2
+#define Lispcommandquit	      3
+#define BoldFace	      4
+#define Link		      5
+#define Downlink	      6
+#define Beginscroll	      7
+#define Spadcommand	      8
+#define NoLines		      9
+#define Env		     10
+#define Par		     11
+#define Center		     12
+#define Begin		     13
+#define Beginitems	     14
+#define Item		     15
+#define Table		     16
+#define Box		     17
+#define Tab		     18
+#define Space		     19
+#define Indent		     20
+#define Horizontalline	     21
+#define Newline		     22
+#define Enditems	     23
+#define Returnbutton	     24
+#define Memolink	     25
+#define Upbutton	     26
+#define Endscroll	     27
+#define Thispage	     28
+#define Returnto	     29
+#define Free		     30
+#define Bound		     31
+#define Lisplink	     32
+#define Unixlink	     33
+#define Mbox		     34
+#define Inputstring	     35
+#define StringValue	     36
+#define Spadlink	     37
+#define Inputbitmap	     38
+#define Inputpixmap	     39
+#define Unixcommand	     40
+#define Emphasize	     41
+#define Lispcommand	     42
+#define LispMemoLink	     43
+#define LispDownLink	     44
+#define Spadcall	     45
+#define Spadcallquit	     46
+#define Spaddownlink	     47
+#define Spadmemolink	     48
+#define Qspadcall	     49
+#define Qspadcallquit	     50
+#define SimpleBox	     51
+#define Radioboxes	     52
+#define BoxValue	     53
+#define VSpace		     54
+#define HSpace		     55
+#define NewCommand	     56
+#define WindowId	     57
+#define Beep		     58
+#define Quitbutton	     59
+#define Begintitems	     60
+#define Titem		     61
+#define End		     62
+#define It		     63
+#define Sl		     64
+#define Tt		     65
+#define Rm		     66
+#define Ifcond		     67
+#define Else		     68
+#define Fi		     69
+#define Newcond		     70
+#define Setcond		     71
+#define Button		     72
+#define Windowlink	     73
+#define Haslisp		     74
+#define Hasup		     75
+#define Hasreturn	     76
+#define Hasreturnto	     77
+#define Lastwindow	     78
+#define Endtitems	     79
+#define Lispwindowlink	     80
+#define Beginpile	     81
+#define Endpile		     82
+#define Nextline	     83
+#define Pastebutton	     84
+#define Color		     85
+#define Helppage	     86
+#define Patch		     87
+#define Radiobox	     88
+#define ifrecond	     89
+#define Math		     90
+#define Mitem		     91
+#define Pagename	     92
+#define Examplenumber	     93
+#define Replacepage	     94
+#define Inputimage	     95
+#define Spadgraph	     96
+#define Indentrel	     97
+#define Controlbitmap	     98
+
+#define NumberUserTokens     98
+
+
+extern char *token_table[];
+
+#ifdef PARSER
+char *token_table[] = {
+  "",           /* Dummy token name */
+  "word",
+  "page",
+  "lispcommandquit",
+  "bf",
+  "link",
+  "downlink",
+  "beginscroll",
+  "spadcommand",
+  "nolines",
+  "env",
+  "par",
+  "centerline",
+  "begin",
+  "beginitems",
+  "item",
+  "table",
+  "fbox",
+  "tab",
+  "space",
+  "indent",
+  "horizontalline",
+  "newline",
+  "enditems",
+  "returnbutton",
+  "memolink",
+  "upbutton",
+  "endscroll",
+  "thispage",
+  "returnto",
+  "free",
+  "bound",
+  "lisplink",
+  "unixlink",
+  "mbox",
+  "inputstring",
+  "stringvalue",
+  "spadlink",
+  "inputbitmap",
+  "inputpixmap",
+  "unixcommand",
+  "em",
+  "lispcommand",
+  "lispmemolink",
+  "lispdownlink",
+  "spadcall",
+  "spadcallquit",
+  "spaddownlink",
+  "spadmemolink",
+  "qspadcall",
+  "qspadcallquit",
+  "inputbox",
+  "radioboxes",
+  "boxvalue",
+  "vspace",
+  "hspace",
+  "newcommand",
+  "windowid",
+  "beep",
+  "quitbutton",
+  "begintitems",
+  "titem",
+  "end",
+  "it",
+  "sl",
+  "tt",
+  "rm",
+  "ifcond",
+  "else",
+  "fi",
+  "newcond",
+  "setcond" ,
+  "button",
+  "windowlink",
+  "haslisp",
+  "hasup",
+  "hasreturn",
+  "hasreturnto",
+  "lastwindow",
+  "endtitems",
+  "lispwindowlink",
+  "beginpile",
+  "endpile",
+  "nextline",
+  "pastebutton",
+  "color",
+  "helppage",
+  "patch",
+  "radiobox",
+  "ifrecond",
+  "math",
+  "mitem",
+  "pagename",
+  "examplenumber",
+  "replacepage",
+  "inputimage",
+  "spadgraph",
+  "indentrel",
+  "controlbitmap"
+  };
+#endif
+
+
+/* places from which input may be read */
+#define FromFile	1
+#define FromString	2
+#define FromSpadSocket	3
+#define FromUnixFD	4
+
+extern FILE *unixfd;
+
+/*
+ * Here are the system tokens. These are used internally to help
+ * with parsing and displaying of text
+ */
+
+#define SystemTokens   1001
+#define Lbrace	       1001
+#define Rbrace	       1002
+#define Macro	       1003
+#define Group	       1004
+#define Scrollbar      1005
+#define Pound	       1006
+#define Lsquarebrace   1007
+#define Rsquarebrace   1008
+#define Punctuation    1009
+#define Dash	       1010
+#define Tableitem      1011
+#define Scrollingnode  1012
+#define Headernode     1013
+#define Footernode     1014
+#define Verbatim       1015
+#define Scroll	       1016
+#define Dollar	       1017
+#define Percent	       1018
+#define Carrot	       1019
+#define Underscore     1020
+#define Tilde	       1021
+#define Cond	       1022
+#define Noop	       1023
+#define Description    1024
+#define Icorrection    1025
+#define Boxcond	       1026
+#define Unkeyword      1027
+#define Titlenode      1028
+#define Paste	       1029
+#define Spadsrc	       1030
+#define Helpbutton     1031
+#define Spadsrctxt     1032
+
+
+/*
+ * Here are the tokens used to mark the end to some sort of group of
+ * tokens. ie, the tokens found in a centerline command
+ */
+
+#define Endtokens      2000
+#define End1	       2001
+#define End2	       2002
+#define Endbutton      2003
+#define Endlink	       2004
+#define Endheader      2005
+#define Endfooter      2006
+#define Endscrolling   2007
+#define Endgroup       2008
+#define Endarg	       2009
+#define Endbox	       2010
+#define Endmbox	       2011
+#define Endspadcommand 2012
+#define Endpix	       2013
+#define Endmacro       2014
+#define Endparameter   2015
+#define Endtable       2016
+#define Endtableitem   2017
+#define End3	       2018
+#define Endif	       2019
+#define Enddescription 2020
+#define Endinputbox    2021
+#define Endtitle       2022
+#define Endpastebutton 2023
+
+#define Endtypes       3000
+#define Endpage	       3002
+#define EndScroll      3007	   /* had to use a S because Endscroll is
+				       already a keyword      */
+
+#define Endcenter      3012
+#define EndItems       3014	   /* Same thing here as EndScroll except
+					  with the i	      */
+#define EndTitems      3060	   /* Ibid for the T	      */
+#define Endpatch       3087
+#define Endverbatim    4015
+#define Endmath	       4016
+#define Endpaste       4029
+#define Endspadsrc     4030
+
+#endif
+@
+\chapter{The Bitmaps}
+\section{ht\_icon}
+<<hticon>>=
+#define ht_icon_width 40
+#define ht_icon_height 40
+#define ht_icon_x_hot -1
+#define ht_icon_y_hot -1
+static char ht_icon_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00,
+   0x00, 0xe7, 0x00, 0x00, 0x00, 0x00, 0xe7, 0xef, 0x7b, 0x3c, 0xe7, 0xff,
+   0xef, 0x7f, 0x7e, 0xff, 0xff, 0xe7, 0xef, 0xe7, 0xfe, 0xe7, 0x6e, 0xe7,
+   0xe7, 0xde, 0xe7, 0x7e, 0xe7, 0xff, 0x0e, 0xe7, 0x3c, 0xe7, 0x07, 0x0e,
+   0xe7, 0x3c, 0xf7, 0xcf, 0x0e, 0xf7, 0x18, 0x7f, 0xfe, 0x1f, 0x00, 0x1c,
+   0x3f, 0x7c, 0x1f, 0x00, 0x0e, 0x07, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x00,
+   0x00, 0x00, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00,
+   0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x77,
+   0x00, 0x00, 0x00, 0x00, 0x77, 0x3e, 0xdc, 0x00, 0x00, 0x77, 0x7f, 0xfe,
+   0x00, 0x00, 0xf7, 0xe3, 0xef, 0x00, 0x00, 0xf7, 0xe3, 0xc7, 0x00, 0x00,
+   0xf7, 0xe3, 0x07, 0x00, 0x00, 0xf7, 0xe3, 0x07, 0x00, 0x00, 0xf7, 0xe3,
+   0xcf, 0x00, 0x80, 0x7f, 0x7f, 0xfe, 0x00, 0x80, 0x3f, 0x3e, 0x7c, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+@
+\section{exit.bitmap}
+<<exit.bitmap>>=
+#define exit_width 60
+#define exit_height 30
+#define exit_x_hot -1
+#define exit_y_hot -1
+static char exit_bits[] = {
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xcf, 0x3f,
+   0xcf, 0x03, 0xc0, 0xff, 0x3f, 0x00, 0x8e, 0x3f, 0x8e, 0x03, 0x80, 0xff,
+   0x3f, 0x00, 0x1e, 0x1f, 0x8f, 0x07, 0x80, 0xff, 0x3f, 0xfe, 0x1f, 0x1f,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x3f, 0x8e, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0xfe, 0x3f, 0x8e, 0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x7f, 0xc4,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x7f, 0xc4, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0xfe, 0xff, 0xe0, 0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0x80, 0xff, 0xe0,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0x00, 0xff, 0xf1, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0x00, 0xff, 0xf1, 0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0xff, 0xe0,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0xff, 0xe0, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0xfe, 0x7f, 0xc4, 0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x7f, 0xc4,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x3f, 0x8e, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0xfe, 0x3f, 0x8e, 0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0xfe, 0x1f, 0x1f,
+   0x8f, 0x7f, 0xfc, 0xff, 0x3f, 0x00, 0x1f, 0x1f, 0x8f, 0x7f, 0xfc, 0xff,
+   0x3f, 0x00, 0x8e, 0x3f, 0x8e, 0x7f, 0xfc, 0xff, 0x7f, 0x00, 0x9e, 0x7f,
+   0x9e, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+@
+\section{help2.bitmap}
+<<help2.bitmap>>=
+#define help2_width 60
+#define help2_height 30
+#define help2_x_hot -1
+#define help2_y_hot -1
+static char help2_bits[] = {
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x9f, 0x07, 0xf0,
+   0xfc, 0x0f, 0xf0, 0xff, 0x1f, 0x1f, 0x07, 0xe0, 0xf8, 0x0f, 0xe0, 0xff,
+   0x1f, 0x1f, 0x07, 0xe0, 0xf8, 0x0f, 0xc0, 0xff, 0x1f, 0x1f, 0xc7, 0xff,
+   0xf8, 0x8f, 0x87, 0xff, 0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0x8f, 0xff,
+   0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0x8f, 0xff, 0x1f, 0x1f, 0xc7, 0xff,
+   0xf8, 0x8f, 0x8f, 0xff, 0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0x8f, 0xff,
+   0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0x8f, 0xff, 0x1f, 0x00, 0x07, 0xf8,
+   0xf8, 0x8f, 0x87, 0xff, 0x1f, 0x00, 0x07, 0xf0, 0xf8, 0x0f, 0xc0, 0xff,
+   0x1f, 0x00, 0x07, 0xf0, 0xf8, 0x0f, 0xe0, 0xff, 0x1f, 0x1f, 0xc7, 0xff,
+   0xf8, 0x0f, 0xf0, 0xff, 0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0xff, 0xff,
+   0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0xff, 0xff, 0x1f, 0x1f, 0xc7, 0xff,
+   0xf8, 0x8f, 0xff, 0xff, 0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0xff, 0xff,
+   0x1f, 0x1f, 0xc7, 0xff, 0xf8, 0x8f, 0xff, 0xff, 0x1f, 0x1f, 0xc7, 0xff,
+   0xf8, 0x8f, 0xff, 0xff, 0x1f, 0x1f, 0x07, 0xf0, 0x00, 0x8f, 0xff, 0xff,
+   0x1f, 0x1f, 0x07, 0xe0, 0x00, 0x8e, 0xff, 0xff, 0x3f, 0x3f, 0x0f, 0xe0,
+   0x01, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+@
+\section{return3.bitmap}
+<<return3.bitmap>>=
+#define return3_width 60
+#define return3_height 30
+#define return3_x_hot -1
+#define return3_y_hot -1
+static char return3_bits[] = {
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x9f, 0x0f, 0xf8,
+   0xfc, 0x79, 0x00, 0xff, 0x1f, 0x1f, 0x07, 0xf0, 0xf8, 0x71, 0x00, 0xfe,
+   0x1f, 0x1f, 0x07, 0xe0, 0xf0, 0x70, 0x00, 0xfe, 0x1f, 0x1f, 0xc7, 0xe3,
+   0xf0, 0x70, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3, 0xf0, 0x70, 0xfc, 0xff,
+   0x1f, 0x1f, 0xc7, 0xe3, 0x60, 0x70, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3,
+   0x60, 0x70, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3, 0x00, 0x70, 0xfc, 0xff,
+   0x1f, 0x1f, 0xc7, 0xe3, 0x08, 0x71, 0xfc, 0xff, 0x1f, 0x00, 0xc7, 0xe3,
+   0x08, 0x71, 0x80, 0xff, 0x1f, 0x00, 0xc7, 0xe3, 0x98, 0x71, 0x00, 0xff,
+   0x1f, 0x00, 0xc7, 0xe3, 0x98, 0x71, 0x00, 0xff, 0x1f, 0x1f, 0xc7, 0xe3,
+   0xf8, 0x71, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3, 0xf8, 0x71, 0xfc, 0xff,
+   0x1f, 0x1f, 0xc7, 0xe3, 0xf8, 0x71, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3,
+   0xf8, 0x71, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3, 0xf8, 0x71, 0xfc, 0xff,
+   0x1f, 0x1f, 0xc7, 0xe3, 0xf8, 0x71, 0xfc, 0xff, 0x1f, 0x1f, 0xc7, 0xe3,
+   0xf8, 0x71, 0xfc, 0xff, 0x1f, 0x1f, 0x07, 0xe0, 0xf8, 0x71, 0x00, 0xff,
+   0x1f, 0x1f, 0x0f, 0xe0, 0xf8, 0x71, 0x00, 0xfe, 0x3f, 0x3f, 0x1f, 0xf0,
+   0xf9, 0xf3, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+@
+\section{up3.bitmap}
+<<up3.bitmap>>=
+#define up3_width 60
+#define up3_height 30
+static char up3_bits[] = {
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xfc, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0x7f, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00,
+   0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0x01, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
+   0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
+   0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+@
+\section{noop.bitmap}
+<<noop.bitmap>>=
+#define noop_width 60
+#define noop_height 30
+#define noop_x_hot -1
+#define noop_y_hot -1
+static char noop_bits[] = {
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+@
+\section{exit3d.bitmap}
+<<exit3d.bitmap>>=
+#define exit3d.bitmap_width 60
+#define exit3d.bitmap_height 30
+static char exit3d.bitmap_bits[] = {
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0xd1, 0xff, 0x55, 0x55,
+   0x5d, 0x55, 0x55, 0x07, 0xaa, 0xff, 0xaa, 0xaa, 0xbe, 0xaa, 0xaa, 0x0e,
+   0xd1, 0xd7, 0x55, 0x55, 0x5f, 0xd5, 0x55, 0x07, 0xaa, 0xab, 0xaa, 0xaa,
+   0xae, 0xea, 0xaa, 0x0e, 0xd1, 0x57, 0x55, 0x55, 0x55, 0xf5, 0x55, 0x07,
+   0xaa, 0xab, 0xaa, 0xaa, 0xaa, 0xea, 0xaa, 0x0e, 0xd1, 0x77, 0x7d, 0x5f,
+   0x5f, 0xfd, 0x5f, 0x07, 0xaa, 0xbf, 0xbe, 0xae, 0xbe, 0xfa, 0xaf, 0x0e,
+   0xd1, 0x7f, 0x7d, 0x57, 0x5d, 0xf5, 0x55, 0x07, 0xaa, 0xab, 0xfa, 0xab,
+   0xbe, 0xea, 0xaa, 0x0e, 0xd1, 0x57, 0xf5, 0x55, 0x5d, 0xf5, 0x55, 0x07,
+   0xaa, 0xab, 0xea, 0xab, 0xbe, 0xea, 0xaa, 0x0e, 0xd1, 0x57, 0xd5, 0x57,
+   0x5d, 0xf5, 0x55, 0x07, 0xaa, 0xab, 0xea, 0xaf, 0xbe, 0xea, 0xaa, 0x0e,
+   0xd1, 0xd7, 0x75, 0x5f, 0x5d, 0xf5, 0x55, 0x07, 0xaa, 0xff, 0xba, 0xbe,
+   0xbe, 0xea, 0xaf, 0x0e, 0xd1, 0xff, 0x7d, 0x5f, 0x7f, 0xd5, 0x57, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0x0f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05};
+@
+\section{help3d.bitmap}
+<<help3d.bitmap>>=
+#define help3d.bitmap_width 60
+#define help3d.bitmap_height 30
+static char help3d.bitmap_bits[] = {
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0xd1, 0xf7, 0x55, 0x55,
+   0x5f, 0x55, 0x55, 0x07, 0xaa, 0xeb, 0xaa, 0xaa, 0xbe, 0xaa, 0xaa, 0x0e,
+   0xd1, 0xf7, 0x55, 0x55, 0x5d, 0x55, 0x55, 0x07, 0xaa, 0xeb, 0xaa, 0xaa,
+   0xbe, 0xaa, 0xaa, 0x0e, 0xd1, 0xf7, 0x55, 0x55, 0x5d, 0x55, 0x55, 0x07,
+   0xaa, 0xeb, 0xaa, 0xaa, 0xbe, 0xaa, 0xaa, 0x0e, 0xd1, 0xf7, 0xf5, 0x57,
+   0x5d, 0xdd, 0x57, 0x07, 0xaa, 0xff, 0xfa, 0xaf, 0xbe, 0xfa, 0xaf, 0x0e,
+   0xd1, 0xff, 0x7d, 0x5f, 0x5d, 0x7d, 0x5f, 0x07, 0xaa, 0xeb, 0xbe, 0xae,
+   0xbe, 0xba, 0xbe, 0x0e, 0xd1, 0xf7, 0xfd, 0x5f, 0x5d, 0x7d, 0x5d, 0x07,
+   0xaa, 0xeb, 0xfe, 0xaf, 0xbe, 0xba, 0xbe, 0x0e, 0xd1, 0xf7, 0x5d, 0x55,
+   0x5d, 0x7d, 0x5d, 0x07, 0xaa, 0xeb, 0xbe, 0xaa, 0xbe, 0xba, 0xbe, 0x0e,
+   0xd1, 0xf7, 0x7d, 0x5d, 0x5d, 0x7d, 0x5f, 0x07, 0xaa, 0xeb, 0xfa, 0xaf,
+   0xbe, 0xfa, 0xaf, 0x0e, 0xd1, 0xf7, 0xf5, 0x57, 0x7f, 0xfd, 0x57, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xba, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x7d, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xba, 0xaa, 0x0e,
+   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0x0f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05};
+@
+\section{home3d.bitmap}
+<<home3d.bitmap>>=
+#define home3d.bitmap_width 60
+#define home3d.bitmap_height 30
+static char home3d.bitmap_bits[] = {
+   0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xef, 0xab, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0xd7, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xef, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0xd7, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xef, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0xd7, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xef, 0xeb, 0xaf,
+   0xbb, 0xeb, 0xaf, 0x0e, 0x51, 0xff, 0xf5, 0xdf, 0xff, 0xf7, 0x5f, 0x07,
+   0xaa, 0xff, 0xfb, 0xae, 0xbb, 0xfb, 0xbe, 0x0e, 0x51, 0xd7, 0x7d, 0xdd,
+   0xff, 0x7f, 0x5d, 0x07, 0xaa, 0xef, 0xbb, 0xbe, 0xbb, 0xfb, 0xbf, 0x0e,
+   0x51, 0xd7, 0x7d, 0xdd, 0xff, 0xff, 0x5f, 0x07, 0xaa, 0xef, 0xbb, 0xbe,
+   0xbb, 0xbb, 0xaa, 0x0e, 0x51, 0xd7, 0x7d, 0xdd, 0xff, 0x7f, 0x55, 0x07,
+   0xaa, 0xef, 0xfb, 0xae, 0xbb, 0xfb, 0xba, 0x0e, 0x51, 0xd7, 0xf5, 0xdf,
+   0xff, 0xf7, 0x5f, 0x07, 0xaa, 0xef, 0xeb, 0xaf, 0xbb, 0xeb, 0xaf, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a};
+@
+\section{up3d.bitmap}
+<<up3d.bitmap>>=
+#define up3_width 60
+#define up3_height 30
+static char up3_bits[] = {
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xfa,
+   0xab, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0xfd, 0x57, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xff, 0xbf, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0xd5, 0xff,
+   0x7f, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xfa, 0xff, 0xff, 0xab, 0xaa, 0x0e,
+   0x51, 0x55, 0xfd, 0xff, 0xff, 0x57, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xff,
+   0xbf, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0xd5, 0xff, 0x7f, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xff, 0xbf, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0xd5, 0xff,
+   0x7f, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xff, 0xbf, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0x0f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05};
+@
+\section{noop3d.bitmap}
+<<noop3d.bitmap>>=
+#define noop_width 60
+#define noop_height 30
+static char noop_bits[] = {
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x55, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa,
+   0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x07,
+   0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e, 0x51, 0x55, 0x55, 0x55,
+   0x55, 0x55, 0x55, 0x07, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0e,
+   0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xfe, 0xff, 0xff, 0xff,
+   0xff, 0xff, 0xff, 0x0f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05};
+@
+\section{Makefile}
+<<*>>=
+BOOK=${SPD}/books/bookvol7.pamphlet
+IN=${SRC}/hyper
+
+# this is the intermediate place 
+MID=    ${INT}/hyper
+
+# this is the intermediate place 
+MIDOBJ=    ${OBJ}/${SYS}/hyper
+
+# this is where to put the various commands
+OUT=	${MNT}/${SYS}/bin
+OUTLIB=	${MNT}/${SYS}/lib
+
+# this is where the include files live
+INC=    ${SRC}/include
+
+# this is where we hid the libspad library
+LIB=	${OBJ}/${SYS}/lib
+
+# this is where the hypertex documentation files are
+HYPER=${MNT}/${SYS}/doc/hypertex
+
+# this is where the hyperdoc pages are
+PAGES=${HYPER}/pages
+
+CFLAGS=	${CCF} -I${MID}
+LDFLAGS= -L${LIB} -lspad ${LDF}
+
+HTADD=${OUT}/htadd
+
+HYPER_OBJS = \
+	${MIDOBJ}/addfile.o   ${MIDOBJ}/cond.o      ${MIDOBJ}/dialog.o \
+        ${MIDOBJ}/display.o   ${MIDOBJ}/event.o     ${MIDOBJ}/extent1.o \
+        ${MIDOBJ}/extent2.o   ${MIDOBJ}/form-ext.o  ${MIDOBJ}/group.o \
+        ${MIDOBJ}/halloc.o    ${MIDOBJ}/hash.o      ${MIDOBJ}/hterror.o \
+        ${MIDOBJ}/htinp.o     ${MIDOBJ}/hyper.o     ${MIDOBJ}/initx.o \
+	${MIDOBJ}/input.o     ${MIDOBJ}/item.o      ${MIDOBJ}/keyin.o \
+        ${MIDOBJ}/lex.o       ${MIDOBJ}/macro.o     ${MIDOBJ}/mem.o \
+        ${MIDOBJ}/parse.o     ${MIDOBJ}/parse-aux.o ${MIDOBJ}/parse-input.o \
+        ${MIDOBJ}/parse-paste.o ${MIDOBJ}/parse-types.o \
+        ${MIDOBJ}/readbitmap.o  ${MIDOBJ}/scrollbar.o   \
+        ${MIDOBJ}/show-types.o  ${MIDOBJ}/spadint.o \
+        ${MIDOBJ}/titlebar.o
+
+EX2HT_OBJS=${MIDOBJ}/ex2ht.o
+
+HTADD_OBJS = \
+	${MIDOBJ}/addfile.o ${MIDOBJ}/halloc.o  ${MIDOBJ}/hash.o \
+        ${MIDOBJ}/htadd.o   ${MIDOBJ}/hterror.o ${MIDOBJ}/lex.o
+
+HTHITS_OBJS = ${MIDOBJ}/hthits.o
+
+SPADBUF_OBJS = ${MIDOBJ}/spadbuf.o
+
+OBJS= \
+	${HYPER_OBJS} ${EX2HT_OBJS} ${HTADD_OBJS} \
+        ${HTHITS_OBJS} ${SPADBUF_OBJS} ${MIDOBJ}/debug.o
+
+SCRIPTS=${OUTLIB}/htsearch ${OUTLIB}/presea
+
+BINFILES= ${OUT}/hypertex ${OUT}/htadd ${OUT}/htsearch ${OUTLIB}/spadbuf \
+          ${OUTLIB}/hthits ${OUTLIB}/ex2ht
+
+all: ${OBJS} ${SCRIPTS} ${BINFILES} \
+     ${PAGES}/ht.db ${HYPER}/rootpage.xhtml \
+     ${HYPER}/axbook \
+     ${HYPER}/bigbayou.png ${HYPER}/doctitle.png
+	@cp ${IN}/bitmaps/* ${HYPER}/bitmaps
+	@ echo 155 finished ${IN}
+
+clean:
+	@echo skipping ${MNT}/${SYS}/doc/bookvol11.dvi 
+	@echo 156 cleaning ${SRC}/hyper
+
+${HYPER}/rootpage.xhtml: ${IN}/bookvol11.pamphlet
+	@ echo 150 making ${HYPER}/xhtml from ${IN}/bookvol11.pamphlet
+	( cd ${HYPER} ; \
+          cp ${IN}/bookvol11.pamphlet . ; \
+          ${TANGLE} -t8 bookvol11.pamphlet >Makefile.pages ; \
+          make -j 10 -f Makefile.pages ; \
+          rm -f Makefile.pages ; \
+          rm -f bookvol11.pamphlet )
+
+${HYPER}/bigbayou.png: ${IN}/bigbayou.png
+	@ echo 151 making ${HYPER}/bigbayou.png from ${IN}/bigbayou.png
+	@ cp ${IN}/bigbayou.png ${HYPER}/bigbayou.png
+
+${HYPER}/doctitle.png: ${IN}/doctitle.png
+	@ echo 152 making ${HYPER}/doctitle.png from ${IN}/doctitle.png
+	@ cp ${IN}/doctitle.png ${HYPER}/doctitle.png
+
+${HYPER}/axbook: ${IN}/axbook.tgz
+	@ echo 154 making ${HYPER}/axbook/xhtml from ${IN}/axbook.tgz
+	@( cd ${HYPER} ; tar -zxf ${IN}/axbook.tgz )
+
+
+${MIDOBJ}/addfile.o: ${BOOK}
+	@ echo 3 making ${MIDOBJ}/addfile.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"addfile.c" ${BOOK} >addfile.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/addfile.c )
+
+${MIDOBJ}/cond.o: ${BOOK}
+	@ echo 17 making ${MIDOBJ}/cond.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"cond.c" ${BOOK} >cond.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/cond.c )
+
+${MIDOBJ}/debug.o: ${BOOK}
+	@ echo 20 making ${MIDOBJ}/debug.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"debug.c" ${BOOK} >debug.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/debug.c )
+
+${MIDOBJ}/dialog.o: ${BOOK}
+	@ echo 24 making ${MIDOBJ}/dialog.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"dialog.c" ${BOOK} >dialog.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/dialog.c )
+
+${MIDOBJ}/display.o: ${BOOK}
+	@ echo 28 making ${MIDOBJ}/display.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"display.c" ${BOOK} >display.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/display.c )
+
+${MIDOBJ}/event.o: ${BOOK}
+	@ echo 32 making ${MIDOBJ}/event.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"event.c" ${BOOK} >event.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/event.c )
+
+${MIDOBJ}/ex2ht.o: ${BOOK}
+	@ echo 35 making ${MIDOBJ}/ex2ht.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"ex2ht.c" ${BOOK} >ex2ht.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/ex2ht.c )
+
+${MIDOBJ}/extent1.o: ${BOOK}
+	@ echo 39 making ${MIDOBJ}/extent1.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"extent1.c" ${BOOK} >extent1.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/extent1.c )
+
+${MIDOBJ}/extent2.o: ${BOOK}
+	@ echo 42 making ${MIDOBJ}/extent2.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"extent2.c" ${BOOK} >extent2.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/extent2.c )
+
+${MIDOBJ}/form-ext.o: ${BOOK}
+	@ echo 45 making ${MIDOBJ}/form-ext.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"form-ext.c" ${BOOK} >form-ext.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/form-ext.c )
+
+${MIDOBJ}/group.o: ${BOOK}
+	@ echo 49 making ${MIDOBJ}/group.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"group.c" ${BOOK} >group.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/group.c )
+
+${MIDOBJ}/halloc.o: ${BOOK}
+	@ echo 52 making ${MIDOBJ}/halloc.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"halloc.c" ${BOOK} >halloc.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/halloc.c )
+
+${MIDOBJ}/hash.o: ${BOOK}
+	@ echo 55 making ${MIDOBJ}/hash.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"hash.c" ${BOOK} >hash.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/hash.c )
+
+${MIDOBJ}/htadd.o: ${BOOK}
+	@ echo 58 making ${MIDOBJ}/htadd.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"htadd.c" ${BOOK} >htadd.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/htadd.c )
+
+${MIDOBJ}/hterror.o: ${BOOK}
+	@ echo 62 making ${MIDOBJ}/hterror.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"hterror.c" ${BOOK} >hterror.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/hterror.c )
+
+${MIDOBJ}/hthits.o: ${BOOK}
+	@ echo 65 making ${MIDOBJ}/hthits.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"hthits.c" ${BOOK} >hthits.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/hthits.c )
+
+${MIDOBJ}/htinp.o: ${BOOK}
+	@ echo 68 making ${MIDOBJ}/htinp.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"htinp.c" ${BOOK} >htinp.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/htinp.c )
+
+${MIDOBJ}/hyper.o: ${BOOK}
+	@ echo 72 making ${MIDOBJ}/hyper.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"hyper.c" ${BOOK} >hyper.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/hyper.c )
+
+${MIDOBJ}/initx.o: ${BOOK}
+	@ echo 76 making ${MIDOBJ}/initx.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"initx.c" ${BOOK} >initx.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/initx.c )
+
+${MIDOBJ}/input.o: ${BOOK}
+	@ echo 79 making ${MIDOBJ}/input.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"input.c" ${BOOK} >input.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/input.c )
+
+${MIDOBJ}/item.o: ${BOOK}
+	@ echo 82 making ${MIDOBJ}/item.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"item.c" ${BOOK} >item.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/item.c )
+
+${MIDOBJ}/keyin.o: ${BOOK}
+	@ echo 86 making ${MIDOBJ}/keyin.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"keyin.c" ${BOOK} >keyin.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/keyin.c )
+
+${MIDOBJ}/lex.o: ${BOOK}
+	@ echo 90 making ${MIDOBJ}/lex.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"lex.c" ${BOOK} >lex.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/lex.c )
+
+${MIDOBJ}/macro.o: ${BOOK}
+	@ echo 93 making ${MIDOBJ}/macro.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"macro.c" ${BOOK} >macro.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/macro.c )
+
+${MIDOBJ}/mem.o: ${BOOK}
+	@ echo 97 making ${MIDOBJ}/mem.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"mem.c" ${BOOK} >mem.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/mem.c )
+
+${PAGES}/ht.db: ${IN}/pages/*.ht ${IN}/pages/*.pht
+	@echo 149 making ${PAGES} from ${SRC}/pages directory
+	@ mkdir -p ${PAGES}
+	@ ( cd ${IN}; cp -pr pages/*.ht ${PAGES} )
+	@ ( cd ${IN} ; cp -pr pages/*.pht ${PAGES} )
+	@ (cd ${PAGES} ; \
+           rm -f ht.db ; \
+           rm -f *~ ; \
+	   ${HTADD} *.ht *.pht )
+	@ (cd ${IN} ; cp -pr bitmaps ${HYPER} )
+	@ (cd ${IN} ; cp -pr viewports ${MNT}/${SYS}/doc )
+
+${MIDOBJ}/parse.o: ${BOOK}
+	@ echo 101 making ${MIDOBJ}/parse.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"parse.c" ${BOOK} >parse.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/parse.c )
+
+${MIDOBJ}/parse-aux.o: ${BOOK}
+	@ echo 105 making ${MIDOBJ}/parse-aux.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"parse-aux.c" ${BOOK} >parse-aux.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/parse-aux.c )
+
+${MIDOBJ}/parse-input.o: ${BOOK}
+	@ echo 108 making ${MIDOBJ}/parse-input.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"parse-input.c" ${BOOK} >parse-input.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/parse-input.c )
+
+${MIDOBJ}/parse-paste.o: ${BOOK}
+	@ echo 112 making ${MIDOBJ}/parse-paste.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"parse-paste.c" ${BOOK} >parse-paste.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/parse-paste.c )
+
+${MIDOBJ}/parse-types.o: ${BOOK}
+	@ echo 116 making ${MIDOBJ}/parse-types.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"parse-types.c" ${BOOK} >parse-types.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/parse-types.c )
+
+${MIDOBJ}/readbitmap.o: ${BOOK}
+	@ echo 119 making ${MIDOBJ}/readbitmap.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"readbitmap.c" ${BOOK} >readbitmap.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/readbitmap.c )
+
+${MIDOBJ}/scrollbar.o: ${BOOK}
+	@ echo 123 making ${MIDOBJ}/scrollbar.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"scrollbar.c" ${BOOK} >scrollbar.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/scrollbar.c )
+
+${OUTLIB}/htsearch: ${BOOK}
+	@echo 125 making ${OUTLIB}/htsearch from ${BOOK}
+	@${TANGLE} -R"htsearch" ${BOOK} >${OUTLIB}/htsearch
+	@chmod a+x ${OUTLIB}/htsearch
+
+${OUTLIB}/presea: ${BOOK}
+	@echo 126 making ${OUTLIB}/presea from ${BOOK}
+	@${TANGLE} -R"presea" ${BOOK} >${OUTLIB}/presea
+	@chmod a+x ${OUTLIB}/presea
+
+${MIDOBJ}/show-types.o: ${BOOK}
+	@ echo 130 making ${MIDOBJ}/show-types.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"show-types.c" ${BOOK} >show-types.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/show-types.c )
+
+${MIDOBJ}/spadbuf.o: ${BOOK}
+	@ echo 133 making ${MIDOBJ}/spadbuf.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"spadbuf.c" ${BOOK} >spadbuf.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/spadbuf.c )
+
+${MIDOBJ}/spadint.o: ${BOOK}
+	@ echo 136 making ${MIDOBJ}/spadint.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"spadint.c" ${BOOK} >spadint.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/spadint.c )
+
+${MIDOBJ}/titlebar.o: ${BOOK}
+	@ echo 140 making ${MIDOBJ}/titlebar.o from ${BOOK}
+	@ (cd ${MID} ; ${TANGLE} -R"titlebar.c" ${BOOK} >titlebar.c )
+	@ ( cd ${MIDOBJ} ; \
+	    ${CC} -I${INC} -I${MID} -c ${CFLAGS} ${MID}/titlebar.c )
+
+${OUTLIB}/ex2ht: ${EX2HT_OBJS} 
+	@ echo 143 linking ${OUTLIB}/ex2ht
+	@ ${CC} ${EX2HT_OBJS} -o ${OUTLIB}/ex2ht ${LDFLAGS}
+
+${OUT}/htadd: ${HTADD_OBJS}   ${LIB}/sockio-c.o ${LIB}/bsdsignal.o
+	@ echo 144 linking ${OUT}/htadd
+	@ ${CC} ${HTADD_OBJS} -o ${OUT}/htadd ${LIB}/sockio-c.o \
+                ${LIB}/bsdsignal.o ${LDFLAGS}
+
+${OUTLIB}/hthits: ${HTHITS_OBJS} 
+	@ echo 145 linking ${OUTLIB}/hthits
+	@ ${CC} ${HTHITS_OBJS} -o ${OUTLIB}/hthits ${LDFLAGS}
+
+${OUT}/htsearch: ${OUTLIB}/htsearch ${OUTLIB}/presea
+	@ echo 147 making ${OUT}/htsearch
+	@ cp -p ${OUTLIB}/htsearch ${OUT}/htsearch
+	@ cp -p ${OUTLIB}/presea ${OUT}/presea
+
+
+${OUT}/hypertex: ${HYPER_OBJS} ${LIB}/sockio-c.o ${LIB}/pixmap.o \
+                    ${LIB}/spadcolors.o ${LIB}/util.o ${LIB}/bsdsignal.o
+	@ echo 146 linking ${OUT}/hypertex
+	@ (cd ${OUT} ; \
+	 ${CC} -g ${HYPER_OBJS} -o ${OUT}/hypertex ${LIB}/sockio-c.o \
+	   ${LIB}/pixmap.o ${LIB}/spadcolors.o ${LIB}/util.o \
+           ${LIB}/bsdsignal.o ${LDFLAGS} -lX11 -lm -L${LIB} )
+
+${OUTLIB}/spadbuf: ${SPADBUF_OBJS} ${LIB}/sockio-c.o ${LIB}/bsdsignal.o \
+                   ${LIB}/wct.o ${LIB}/edin.o ${LIB}/prt.o ${LIB}/cursor.o \
+                   ${LIB}/fnct-key.o
+	@ echo 148 making ${OUTLIB}/spadbuf
+	@ (cd ${OUTLIB} ; \
+	   ${CC} ${SPADBUF_OBJS} -o ${OUTLIB}/spadbuf ${LIB}/sockio-c.o \
+	         ${LIB}/bsdsignal.o ${LIB}/wct.o ${LIB}/edin.o ${LIB}/prt.o \
+	         ${LIB}/cursor.o ${LIB}/fnct-key.o ${LDFLAGS} )
+
+
+@
 \eject
 \begin{thebibliography}{99}
 \bibitem{1} Jenks, R.J. and Sutor, R.S. 
@@ -1263,8 +20151,8 @@ ISBN 0-387-97855-0
 Center for the Study of Language and Information
 ISBN 0-937073-81-4
 Stanford CA (1992) 
-\bibitem{3} Page, William, ``The Axiom Wiki Website''\\
-{\bf http://wiki.axiom-developer.org}
+\bibitem{3} Daly, Timothy, ``The Axiom Wiki Website''\\
+{\bf http://axiom.axiom-developer.org}
 \bibitem{4} Watt, Stephen, ``Aldor'',\\
 {\bf http://www.aldor.org}
 \bibitem{5} Lamport, Leslie, ``Latex -- A Document Preparation System'',
diff --git a/changelog b/changelog
index e4f1b86..5c784cb 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,6 @@
+20080609 tpd src/Makefile make hypertex (use bookvol7)
+20080609 tpd books/bookvol7 make hypertex 
+20080608 tpd books/Makefile call makeindex on bookvol8
 20080608 tpd src/Makefile remove src/graph (use bookvol8)
 20080608 tpd books/bookvol8 remove src/graph (use bookvol8)
 20080608 tpd src/graph/gdraws/Makefile deleted, (use bookvol8)
diff --git a/src/Makefile.pamphlet b/src/Makefile.pamphlet
index 5644071..39f7ae3 100644
--- a/src/Makefile.pamphlet
+++ b/src/Makefile.pamphlet
@@ -145,8 +145,8 @@ smanclean: ${SRC}/sman/Makefile
 \subsection{The hyper directory}
 Hyperdoc is the Axiom document browser.
 <<hyperdir>>=
-hyperdir: ${SRC}/hyper/Makefile
-	@echo 13 making ${SRC}/hyper
+hyperdir: ${SPD}/books/bookvol7.pamphlet
+	@echo 13 making hyperdoc from bookvol7
 	@mkdir -p ${INT}/hyper
 	@mkdir -p ${OBJ}/${SYS}/hyper
 	@mkdir -p ${OBJ}/${SYS}/bin
@@ -154,25 +154,9 @@ hyperdir: ${SRC}/hyper/Makefile
 	@mkdir -p ${MNT}/${SYS}/doc/hypertex/bitmaps
 	@mkdir -p ${MNT}/${SYS}/doc/hypertex/pages
 	@mkdir -p ${MNT}/${SYS}/doc/src/hyper
-	@(cd hyper ; ${ENV} ${MAKE} )
-
-${SRC}/hyper/Makefile: ${SRC}/hyper/Makefile.pamphlet
-	@echo 14 making ${SRC}/hyper/Makefile from \
-                 ${SRC}/hyper/Makefile.pamphlet
-	@( cd hyper ; \
-	   ${DOCUMENT} ${NOISE} Makefile ; \
-	   cp Makefile.dvi ${MNT}/${SYS}/doc/src/hyper.Makefile.dvi )
-
-hyperdocument: ${SRC}/hyper/Makefile
-	@echo 15 documenting ${SRC}/hyper
-	@mkdir -p ${INT}/doc/src/hyper
-	@( cd hyper ; ${ENV} ${MAKE} document )
-
-hyperclean: ${SRC}/hyper/Makefile
-	@echo 16 cleaning ${SRC}/hyper
-	@( cd hyper ; ${ENV} ${MAKE} clean )
-	@rm -f ${SRC}/hyper/Makefile
-	@rm -f ${SRC}/hyper/Makefile.dvi
+	@(cd ${INT}/hyper ; \
+	  ${TANGLE} -t8 ${SPD}/books/bookvol7.pamphlet >Makefile ; \
+	  ${ENV} ${MAKE} )
 
 @
 \subsection{The share directory}
