added mk and troff to 9base (unfinished yet, DO NOT USE) - 9base - revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log
Files
Refs
README
LICENSE
---
commit a08a2a9000cf4cb01ac165f410dbe99a64191edd
parent c0a69251c8988bdbabf4f3d3e20f40e363990c6c
Author: Anselm R Garbe 
Date:   Mon, 24 Aug 2009 19:23:45 +0100

added mk and troff to 9base (unfinished yet, DO NOT USE)
Diffstat:
  M Makefile                            |       8 ++++----
  M config.mk                           |       2 +-
  A mk/Makefile                         |      11 +++++++++++
  A mk/NOTICE                           |      27 +++++++++++++++++++++++++++
  A mk/README                           |       7 +++++++
  A mk/arc.c                            |      52 +++++++++++++++++++++++++++++++
  A mk/archive.c                        |     253 +++++++++++++++++++++++++++++++
  A mk/bufblock.c                       |      88 +++++++++++++++++++++++++++++++
  A mk/env.c                            |     149 +++++++++++++++++++++++++++++++
  A mk/file.c                           |      90 +++++++++++++++++++++++++++++++
  A mk/fns.h                            |      88 +++++++++++++++++++++++++++++++
  A mk/graph.c                          |     279 +++++++++++++++++++++++++++++++
  A mk/job.c                            |      33 +++++++++++++++++++++++++++++++
  A mk/lex.c                            |     146 +++++++++++++++++++++++++++++++
  A mk/main.c                           |     287 +++++++++++++++++++++++++++++++
  A mk/match.c                          |      49 +++++++++++++++++++++++++++++++
  A mk/mk.1                             |     691 +++++++++++++++++++++++++++++++
  A mk/mk.c                             |     234 +++++++++++++++++++++++++++++++
  A mk/mk.h                             |     185 ++++++++++++++++++++++++++++++
  A mk/mkfile                           |      37 +++++++++++++++++++++++++++++++
  A mk/mkfile.test                      |      12 ++++++++++++
  A mk/parse.c                          |     318 +++++++++++++++++++++++++++++++
  A mk/rc.c                             |     194 ++++++++++++++++++++++++++++++
  A mk/recipe.c                         |     117 +++++++++++++++++++++++++++++++
  A mk/rule.c                           |     112 +++++++++++++++++++++++++++++++
  A mk/run.c                            |     296 +++++++++++++++++++++++++++++++
  A mk/sh.c                             |     206 +++++++++++++++++++++++++++++++
  A mk/shell.c                          |      80 +++++++++++++++++++++++++++++++
  A mk/shprint.c                        |     125 +++++++++++++++++++++++++++++++
  A mk/symtab.c                         |      97 ++++++++++++++++++++++++++++++
  A mk/sys.h                            |       5 +++++
  A mk/sys.std.h                        |      27 +++++++++++++++++++++++++++
  A mk/unix.c                           |     341 +++++++++++++++++++++++++++++++
  A mk/var.c                            |      41 +++++++++++++++++++++++++++++++
  A mk/varsub.c                         |     252 +++++++++++++++++++++++++++++++
  A mk/word.c                           |     189 +++++++++++++++++++++++++++++++
  A troff/FIXES                         |     821 ++++++++++++++++++++++++++++++
  A troff/Makefile                      |      11 +++++++++++
  A troff/README                        |      31 +++++++++++++++++++++++++++++++
  A troff/cvt                           |      45 +++++++++++++++++++++++++++++++
  A troff/dwbinit.c                     |     317 +++++++++++++++++++++++++++++++
  A troff/dwbinit.h                     |      19 +++++++++++++++++++
  A troff/ext.h                         |     187 +++++++++++++++++++++++++++++++
  A troff/find                          |       1 +
  A troff/fns.h                         |     389 +++++++++++++++++++++++++++++++
  A troff/hytab.c                       |     126 +++++++++++++++++++++++++++++++
  A troff/mbwc.c                        |     165 +++++++++++++++++++++++++++++++
  A troff/mkfile                        |      57 +++++++++++++++++++++++++++++++
  A troff/n1.c                          |    1134 +++++++++++++++++++++++++++++++
  A troff/n10.c                         |     549 +++++++++++++++++++++++++++++++
  A troff/n2.c                          |     325 +++++++++++++++++++++++++++++++
  A troff/n3.c                          |     954 +++++++++++++++++++++++++++++++
  A troff/n4.c                          |     828 ++++++++++++++++++++++++++++++
  A troff/n5.c                          |    1150 +++++++++++++++++++++++++++++++
  A troff/n6.c                          |     363 +++++++++++++++++++++++++++++++
  A troff/n7.c                          |     837 +++++++++++++++++++++++++++++++
  A troff/n8.c                          |     545 +++++++++++++++++++++++++++++++
  A troff/n9.c                          |     489 +++++++++++++++++++++++++++++++
  A troff/ni.c                          |     390 +++++++++++++++++++++++++++++++
  A troff/suftab.c                      |     612 +++++++++++++++++++++++++++++++
  A troff/t10.c                         |     513 +++++++++++++++++++++++++++++++
  A troff/t11.c                         |     260 +++++++++++++++++++++++++++++++
  A troff/t6.c                          |     889 ++++++++++++++++++++++++++++++
  A troff/tdef.h                        |     673 +++++++++++++++++++++++++++++++
  A troff/troff.1                       |     199 +++++++++++++++++++++++++++++++
  A troff/unansi                        |      49 +++++++++++++++++++++++++++++++

66 files changed, 18051 insertions(+), 5 deletions(-)
---
diff --git a/Makefile b/Makefile
@@ -1,10 +1,10 @@
-# 9base - awk basename cal cat cleanname du echo grep rc sed seq sleep
-#         hoc sort tee test touch tr uniq from Plan 9
+# 9base - awk basename bc cal cat cleanname dc du echo grep mk rc sed seq sleep
+#         troff hoc sort tee test touch tr uniq from Plan 9
 
 include config.mk
 
-SUBDIRS  = lib9 yacc awk basename bc dc du cal cat cleanname date echo grep ls \
-                hoc rc read sed seq sleep sort tee test touch tr uniq
+SUBDIRS  = lib9 mk yacc awk basename bc dc du cal cat cleanname date echo grep ls \
+           hoc rc read sed seq sleep sort tee test touch tr troff uniq
 
 all:
         @echo 9base build options:
diff --git a/config.mk b/config.mk
@@ -4,7 +4,7 @@
 PREFIX      = /usr/local/plan9
 MANPREFIX   = ${PREFIX}/share/man
 
-VERSION     = 3
+VERSION     = 4
 OBJTYPE     = 386
 #OBJTYPE     = arm
 #OBJTYPE     = x86_64
diff --git a/mk/Makefile b/mk/Makefile
@@ -0,0 +1,11 @@
+# mk - mk unix port from plan9
+# Depends on ../lib9
+
+TARG      = mk
+
+OFILES    = arc.o archive.o bufblock.o env.o file.o graph.o job.o lex.o \
+            main.o match.o mk.o parse.o recipe.o rc.o rule.o run.o sh.o \
+            shell.o shprint.o symtab.o var.o varsub.o word.o unix.o
+MANFILES  = mk.1
+
+include ../std.mk
diff --git a/mk/NOTICE b/mk/NOTICE
@@ -0,0 +1,27 @@
+Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+Portions Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk).  All rights reserved.
+Portions Copyright © 1997-1999 Vita Nuova Limited.  All rights reserved.
+Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
+
+Under a licence agreement with Lucent Technologies Inc. effective 1st March 2000,
+Vita Nuova Holdings Limited has the right to determine (within a specified scope)
+the form and content of sublicences for this software.
+
+Vita Nuova Holdings Limited now makes this software available as Free
+Software under the terms of the `GNU General Public LIcense, Version 2'
+(see the file LICENCE or http://www.fsf.org/copyleft/gpl.html for
+the full terms and conditions).  One of the conditions of that licence
+is that you must keep intact all notices that refer to that licence and to the absence of
+of any warranty: for this software, note that includes this NOTICE file in particular.
+  
+This suite of programs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+`GNU General Public License' for more details.
+
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices above.
diff --git a/mk/README b/mk/README
@@ -0,0 +1,7 @@
+This is a Unix port of mk,
+originally done for the Inferno operating system.
+
+Russ Cox repackaged this to build as a standalone
+Unix program.  Send comments about packaging to
+Russ Cox 
+
diff --git a/mk/arc.c b/mk/arc.c
@@ -0,0 +1,52 @@
+#include        "mk.h"
+
+Arc *
+newarc(Node *n, Rule *r, char *stem, Resub *match)
+{
+        Arc *a;
+
+        a = (Arc *)Malloc(sizeof(Arc));
+        a->n = n;
+        a->r = r;
+        a->stem = strdup(stem);
+        rcopy(a->match, match, NREGEXP);
+        a->next = 0;
+        a->flag = 0;
+        a->prog = r->prog;
+        return(a);
+}
+
+void
+dumpa(char *s, Arc *a)
+{
+        char buf[1024];
+
+        Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'",
+                s, a, a->n, a->r, a->flag, a->stem);
+        if(a->prog)
+                Bprint(&bout, " prog='%s'", a->prog);
+        Bprint(&bout, "\n");
+
+        if(a->n){
+                snprint(buf, sizeof(buf), "%s    ", (*s == ' ')? s:"");
+                dumpn(buf, a->n);
+        }
+}
+
+void
+nrep(void)
+{
+        Symtab *sym;
+        Word *w;
+
+        sym = symlook("NREP", S_VAR, 0);
+        if(sym){
+                w = sym->u.ptr;
+                if (w && w->s && *w->s)
+                        nreps = atoi(w->s);
+        }
+        if(nreps < 1)
+                nreps = 1;
+        if(DEBUG(D_GRAPH))
+                Bprint(&bout, "nreps = %d\n", nreps);
+}
diff --git a/mk/archive.c b/mk/archive.c
@@ -0,0 +1,253 @@
+#include        "mk.h"
+#define        ARMAG        "!\n"
+#define        SARMAG        8
+
+#define        ARFMAG        "`\n"
+#define SARNAME        16
+
+struct        ar_hdr
+{
+        char        name[SARNAME];
+        char        date[12];
+        char        uid[6];
+        char        gid[6];
+        char        mode[8];
+        char        size[10];
+        char        fmag[2];
+};
+#define        SAR_HDR        (SARNAME+44)
+
+static int dolong = 1;
+
+static void atimes(char *);
+static char *split(char*, char**);
+
+long
+readn(int f, void *av, long n)
+{
+        char *a;
+        long m, t;
+
+        a = av;
+        t = 0;
+        while(t < n){
+                m = read(f, a+t, n-t);
+                if(m <= 0){
+                        if(t == 0)
+                                return m;
+                        break;
+                }
+                t += m;
+        }
+        return t;
+}
+long
+atimeof(int force, char *name)
+{
+        Symtab *sym;
+        long t;
+        char *archive, *member, buf[512];
+
+        archive = split(name, &member);
+        if(archive == 0)
+                Exit();
+
+        t = mtime(archive);
+        sym = symlook(archive, S_AGG, 0);
+        if(sym){
+                if(force || (t > sym->u.value)){
+                        atimes(archive);
+                        sym->u.value = t;
+                }
+        }
+        else{
+                atimes(archive);
+                /* mark the aggegate as having been done */
+                symlook(strdup(archive), S_AGG, "")->u.value = t;
+        }
+                /* truncate long member name to sizeof of name field in archive header */
+        if(dolong)
+                snprint(buf, sizeof(buf), "%s(%s)", archive, member);
+        else
+                snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member);
+        sym = symlook(buf, S_TIME, 0);
+        if (sym)
+                return sym->u.value;
+        return 0;
+}
+
+void
+atouch(char *name)
+{
+        char *archive, *member;
+        int fd, i;
+        struct ar_hdr h;
+        long t;
+
+        archive = split(name, &member);
+        if(archive == 0)
+                Exit();
+
+        fd = open(archive, ORDWR);
+        if(fd < 0){
+                fd = create(archive, OWRITE, 0666);
+                if(fd < 0){
+                        fprint(2, "create %s: %r\n", archive);
+                        Exit();
+                }
+                write(fd, ARMAG, SARMAG);
+        }
+        if(symlook(name, S_TIME, 0)){
+                /* hoon off and change it in situ */
+                LSEEK(fd, SARMAG, 0);
+                while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){
+                        for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--)
+                                        ;
+                        h.name[i+1]=0;
+                        if(strcmp(member, h.name) == 0){
+                                t = SARNAME-sizeof(h);        /* ughgghh */
+                                LSEEK(fd, t, 1);
+                                fprint(fd, "%-12ld", time(0));
+                                break;
+                        }
+                        t = atol(h.size);
+                        if(t&01) t++;
+                        LSEEK(fd, t, 1);
+                }
+        }
+        close(fd);
+}
+
+static void
+atimes(char *ar)
+{
+        struct ar_hdr h;
+        long t;
+        int fd, i, namelen;
+        char buf[2048], *p, *strings;
+        char name[1024];
+        Symtab *sym;
+
+        strings = nil;
+        fd = open(ar, OREAD);
+        if(fd < 0)
+                return;
+
+        if(read(fd, buf, SARMAG) != SARMAG){
+                close(fd);
+                return;
+        }
+        while(readn(fd, (char *)&h, sizeof(h)) == sizeof(h)){
+                t = atol(h.date);
+                if(t == 0)        /* as it sometimes happens; thanks ken */
+                        t = 1;
+                namelen = 0;
+                if(memcmp(h.name, "#1/", 3) == 0){        /* BSD */
+                        namelen = atoi(h.name+3);
+                        if(namelen >= sizeof name){
+                                namelen = 0;
+                                goto skip;
+                        }
+                        if(readn(fd, name, namelen) != namelen)
+                                break;
+                        name[namelen] = 0;
+                }else if(memcmp(h.name, "// ", 2) == 0){ /* GNU */
+                        /* date, uid, gid, mode all ' ' */
+                        for(i=2; i<16+12+6+6+8; i++)
+                                if(h.name[i] != ' ')
+                                        goto skip;
+                        t = atol(h.size);
+                        if(t&01)
+                                t++;
+                        free(strings);
+                        strings = malloc(t+1);
+                        if(strings){
+                                if(readn(fd, strings, t) != t){
+                                        free(strings);
+                                        strings = nil;
+                                        break;
+                                }
+                                strings[t] = 0;
+                                continue;
+                        }
+                        goto skip;
+                }else if(strings && h.name[0]=='/' && isdigit((uchar)h.name[1])){
+                        i = strtol(h.name+1, &p, 10);
+                        if(*p != ' ' || i >= strlen(strings))
+                                goto skip;
+                        p = strings+i;
+                        for(; *p && *p != '/'; p++)
+                                ;
+                        namelen = p-(strings+i);
+                        if(namelen >= sizeof name){
+                                namelen = 0;
+                                goto skip;
+                        }
+                        memmove(name, strings+i, namelen);
+                        name[namelen] = 0;
+                        namelen = 0;
+                }else{
+                        strncpy(name, h.name, sizeof(h.name));
+                        for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--)
+                                        ;
+                        if(name[i] == '/')                /* system V bug */
+                                i--;
+                        name[i+1]=0;
+                }
+                snprint(buf, sizeof buf, "%s(%s)", ar, name);
+                sym = symlook(strdup(buf), S_TIME, (void *)t);
+                sym->u.value = t;
+        skip:
+                t = atol(h.size);
+                if(t&01) t++;
+                t -= namelen;
+                LSEEK(fd, t, 1);
+        }
+        close(fd);
+        free(strings);
+}
+
+static int
+type(char *file)
+{
+        int fd;
+        char buf[SARMAG];
+
+        fd = open(file, OREAD);
+        if(fd < 0){
+                if(symlook(file, S_BITCH, 0) == 0){
+                        if(strlen(file) < 2 || strcmp(file+strlen(file)-2, ".a") != 0)
+                                Bprint(&bout, "%s doesn't exist: assuming it will be an archive\n", file);
+                        symlook(file, S_BITCH, (void *)file);
+                }
+                return 1;
+        }
+        if(read(fd, buf, SARMAG) != SARMAG){
+                close(fd);
+                return 0;
+        }
+        close(fd);
+        return !strncmp(ARMAG, buf, SARMAG);
+}
+
+static char*
+split(char *name, char **member)
+{
+        char *p, *q;
+
+        p = strdup(name);
+        q = utfrune(p, '(');
+        if(q){
+                *q++ = 0;
+                if(member)
+                        *member = q;
+                q = utfrune(q, ')');
+                if (q)
+                        *q = 0;
+                if(type(p))
+                        return p;
+                free(p);
+                fprint(2, "mk: '%s' is not an archive\n", name);
+        }
+        return 0;
+}
diff --git a/mk/bufblock.c b/mk/bufblock.c
@@ -0,0 +1,88 @@
+#include        "mk.h"
+
+static Bufblock *freelist;
+#define        QUANTA        4096
+
+Bufblock *
+newbuf(void)
+{
+        Bufblock *p;
+
+        if (freelist) {
+                p = freelist;
+                freelist = freelist->next;
+        } else {
+                p = (Bufblock *) Malloc(sizeof(Bufblock));
+                p->start = Malloc(QUANTA*sizeof(*p->start));
+                p->end = p->start+QUANTA;
+        }
+        p->current = p->start;
+        *p->start = 0;
+        p->next = 0;
+        return p;
+}
+
+void
+freebuf(Bufblock *p)
+{
+        p->next = freelist;
+        freelist = p;
+}
+
+void
+growbuf(Bufblock *p)
+{
+        int n;
+        Bufblock *f;
+        char *cp;
+
+        n = p->end-p->start+QUANTA;
+                /* search the free list for a big buffer */
+        for (f = freelist; f; f = f->next) {
+                if (f->end-f->start >= n) {
+                        memcpy(f->start, p->start, p->end-p->start);
+                        cp = f->start;
+                        f->start = p->start;
+                        p->start = cp;
+                        cp = f->end;
+                        f->end = p->end;
+                        p->end = cp;
+                        f->current = f->start;
+                        break;
+                }
+        }
+        if (!f) {                /* not found - grow it */
+                p->start = Realloc(p->start, n);
+                p->end = p->start+n;
+        }
+        p->current = p->start+n-QUANTA;
+}
+
+void
+bufcpy(Bufblock *buf, char *cp, int n)
+{
+
+        while (n--)
+                insert(buf, *cp++);
+}
+
+void
+insert(Bufblock *buf, int c)
+{
+
+        if (buf->current >= buf->end)
+                growbuf(buf);
+        *buf->current++ = c;
+}
+
+void
+rinsert(Bufblock *buf, Rune r)
+{
+        int n;
+
+        n = runelen(r);
+        if (buf->current+n > buf->end)
+                growbuf(buf);
+        runetochar(buf->current, &r);
+        buf->current += n;
+}
diff --git a/mk/env.c b/mk/env.c
@@ -0,0 +1,149 @@
+#include        "mk.h"
+
+enum {
+        ENVQUANTA=10
+};
+
+Envy        *envy;
+static int nextv;
+
+static char        *myenv[] =
+{
+        "target",
+        "stem",
+        "prereq",
+        "pid",
+        "nproc",
+        "newprereq",
+        "alltarget",
+        "newmember",
+        "stem0",                /* must be in order from here */
+        "stem1",
+        "stem2",
+        "stem3",
+        "stem4",
+        "stem5",
+        "stem6",
+        "stem7",
+        "stem8",
+        "stem9",
+        0
+};
+
+void
+initenv(void)
+{
+        char **p;
+
+        for(p = myenv; *p; p++)
+                symlook(*p, S_INTERNAL, (void *)"");
+        readenv();                                /* o.s. dependent */
+}
+
+static void
+envinsert(char *name, Word *value)
+{
+        static int envsize;
+
+        if (nextv >= envsize) {
+                envsize += ENVQUANTA;
+                envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy));
+        }
+        envy[nextv].name = name;
+        envy[nextv++].values = value;
+}
+
+static void
+envupd(char *name, Word *value)
+{
+        Envy *e;
+
+        for(e = envy; e->name; e++)
+                if(strcmp(name, e->name) == 0){
+                        delword(e->values);
+                        e->values = value;
+                        return;
+                }
+        e->name = name;
+        e->values = value;
+        envinsert(0,0);
+}
+
+static void
+ecopy(Symtab *s)
+{
+        char **p;
+
+        if(symlook(s->name, S_NOEXPORT, 0))
+                return;
+        for(p = myenv; *p; p++)
+                if(strcmp(*p, s->name) == 0)
+                        return;
+        envinsert(s->name, s->u.ptr);
+}
+
+void
+execinit(void)
+{
+        char **p;
+
+        nextv = 0;
+        for(p = myenv; *p; p++)
+                envinsert(*p, stow(""));
+
+        symtraverse(S_VAR, ecopy);
+        envinsert(0, 0);
+}
+
+Envy*
+buildenv(Job *j, int slot)
+{
+        char **p, *cp, *qp;
+        Word *w, *v, **l;
+        int i;
+        char buf[256];
+
+        envupd("target", wdup(j->t));
+        if(j->r->attr®EXP)
+                envupd("stem",newword(""));
+        else
+                envupd("stem", newword(j->stem));
+        envupd("prereq", wdup(j->p));
+        sprint(buf, "%d", getpid());
+        envupd("pid", newword(buf));
+        sprint(buf, "%d", slot);
+        envupd("nproc", newword(buf));
+        envupd("newprereq", wdup(j->np));
+        envupd("alltarget", wdup(j->at));
+        l = &v;
+        v = w = wdup(j->np);
+        while(w){
+                cp = strchr(w->s, '(');
+                if(cp){
+                        qp = strchr(cp+1, ')');
+                        if(qp){
+                                *qp = 0;
+                                strcpy(w->s, cp+1);
+                                l = &w->next;
+                                w = w->next;
+                                continue;
+                        }
+                }
+                *l = w->next;
+                free(w->s);
+                free(w);
+                w = *l;
+        }
+        envupd("newmember", v);
+                /* update stem0 -> stem9 */
+        for(p = myenv; *p; p++)
+                if(strcmp(*p, "stem0") == 0)
+                        break;
+        for(i = 0; *p; i++, p++){
+                if((j->r->attr®EXP) && j->match[i])
+                        envupd(*p, newword(j->match[i]));
+                else 
+                        envupd(*p, newword(""));
+        }
+        return envy;
+}
diff --git a/mk/file.c b/mk/file.c
@@ -0,0 +1,90 @@
+#include        "mk.h"
+
+/* table-driven version in bootes dump of 12/31/96 */
+
+long
+mtime(char *name)
+{
+        return mkmtime(name);
+}
+
+long
+timeof(char *name, int force)
+{
+        Symtab *sym;
+        long t;
+
+        if(utfrune(name, '('))
+                return atimeof(force, name);        /* archive */
+
+        if(force)
+                return mtime(name);
+
+
+        sym = symlook(name, S_TIME, 0);
+        if (sym)
+                return sym->u.value;
+
+        t = mtime(name);
+        if(t == 0)
+                return 0;
+
+        symlook(name, S_TIME, (void*)t);                /* install time in cache */
+        return t;
+}
+
+void
+touch(char *name)
+{
+        Bprint(&bout, "touch(%s)\n", name);
+        if(nflag)
+                return;
+
+        if(utfrune(name, '('))
+                atouch(name);                /* archive */
+        else if(chgtime(name) < 0) {
+                fprint(2, "%s: %r\n", name);
+                Exit();
+        }
+}
+
+void
+delete(char *name)
+{
+        if(utfrune(name, '(') == 0) {                /* file */
+                if(remove(name) < 0)
+                        fprint(2, "remove %s: %r\n", name);
+        } else
+                fprint(2, "hoon off; mk can'tdelete archive members\n");
+}
+
+void
+timeinit(char *s)
+{
+        long t;
+        char *cp;
+        Rune r;
+        int c, n;
+
+        t = time(0);
+        while (*s) {
+                cp = s;
+                do{
+                        n = chartorune(&r, s);
+                        if (r == ' ' || r == ',' || r == '\n')
+                                break;
+                        s += n;
+                } while(*s);
+                c = *s;
+                *s = 0;
+                symlook(strdup(cp), S_TIME, (void *)t)->u.value = t;
+                if (c)
+                        *s++ = c;
+                while(*s){
+                        n = chartorune(&r, s);
+                        if(r != ' ' && r != ',' && r != '\n')
+                                break;
+                        s += n;
+                }
+        }
+}
diff --git a/mk/fns.h b/mk/fns.h
@@ -0,0 +1,88 @@
+#undef waitfor
+#define waitfor mkwaitfor
+
+void        addrule(char*, Word*, char*, Word*, int, int, char*);
+void        addrules(Word*, Word*, char*, int, int, char*);
+void        addw(Word*, char*);
+void        assert(char*, int);
+int        assline(Biobuf *, Bufblock *);
+long        atimeof(int,char*);
+void        atouch(char*);
+void        bufcpy(Bufblock *, char *, int);
+Envy        *buildenv(Job*, int);
+void        catchnotes(void);
+int        chgtime(char*);
+void        clrmade(Node*);
+void        delete(char*);
+void        delword(Word*);
+int        dorecipe(Node*);
+void        dumpa(char*, Arc*);
+void        dumpj(char*, Job*, int);
+void        dumpn(char*, Node*);
+void        dumpr(char*, Rule*);
+void        dumpv(char*);
+void        dumpw(char*, Word*);
+void        execinit(void);
+int        execsh(char*, char*, Bufblock*, Envy*, Shell*, Word*);
+void        Exit(void);
+void        expunge(int, char*);
+void        freebuf(Bufblock*);
+void        front(char*);
+Node        *graph(char*);
+void        growbuf(Bufblock *);
+void        initenv(void);
+void        initshell(void);
+void        insert(Bufblock *, int);
+void        ipop(void);
+void        ipush(void);
+void        killchildren(char*);
+void        *Malloc(int);
+char        *maketmp(int*);
+int        match(char*, char*, char*, Shell*);
+char *membername(char*, int, char*);
+void        mk(char*);
+unsigned long        mkmtime(char*);
+long        mtime(char*);
+Arc        *newarc(Node*, Rule*, char*, Resub*);
+Bufblock *newbuf(void);
+Job        *newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*);
+Word        *newword(char*);
+int        nextrune(Biobuf*, int);
+int        nextslot(void);
+void        nproc(void);
+void        nrep(void);
+int        outofdate(Node*, Arc*, int);
+void        parse(char*, int, int);
+int        pipecmd(char*, Envy*, int*, Shell*, Word*);
+void        popshell(void);
+void        prusage(void);
+void        pushshell(void);
+void        rcopy(char**, Resub*, int);
+void        readenv(void);
+void        *Realloc(void*, int);
+void        rinsert(Bufblock *, Rune);
+char        *rulecnt(void);
+void        run(Job*);
+char        *setshell(Word*);
+void        setvar(char*, void*);
+int        shargv(Word*, int, char***);
+char        *shname(char*);
+void        shprint(char*, Envy*, Bufblock*, Shell*);
+Word        *stow(char*);
+void        subst(char*, char*, char*);
+void        symdel(char*, int);
+void        syminit(void);
+Symtab        *symlook(char*, int, void*);
+void        symstat(void);
+void        symtraverse(int, void(*)(Symtab*));
+void        timeinit(char*);
+long        timeof(char*, int);
+void        touch(char*);
+void        update(int, Node*);
+void        usage(void);
+Word        *varsub(char**);
+int        waitfor(char*);
+int        waitup(int, int*);
+Word        *wdup(Word*);
+int        work(Node*, Node*, Arc*);
+char        *wtos(Word*, int);
diff --git a/mk/graph.c b/mk/graph.c
@@ -0,0 +1,279 @@
+#include        "mk.h"
+
+static Node *applyrules(char *, char *);
+static void togo(Node *);
+static int vacuous(Node *);
+static Node *newnode(char *);
+static void trace(char *, Arc *);
+static void cyclechk(Node *);
+static void ambiguous(Node *);
+static void attribute(Node *);
+
+Node *
+graph(char *target)
+{
+        Node *node;
+        char *cnt;
+
+        cnt = rulecnt();
+        node = applyrules(target, cnt);
+        free(cnt);
+        cyclechk(node);
+        node->flags |= PROBABLE;        /* make sure it doesn't get deleted */
+        vacuous(node);
+        ambiguous(node);
+        attribute(node);
+        return(node);
+}
+
+static Node *
+applyrules(char *target, char *cnt)
+{
+        Symtab *sym;
+        Node *node;
+        Rule *r;
+        Arc head, *a = &head;
+        Word *w;
+        char stem[NAMEBLOCK], buf[NAMEBLOCK];
+        Resub rmatch[NREGEXP];
+
+/*        print("applyrules(%lux='%s')\n", target, target); */
+        sym = symlook(target, S_NODE, 0);
+        if(sym)
+                return sym->u.ptr;
+        target = strdup(target);
+        node = newnode(target);
+        head.n = 0;
+        head.next = 0;
+        sym = symlook(target, S_TARGET, 0);
+        memset((char*)rmatch, 0, sizeof(rmatch));
+        for(r = sym? sym->u.ptr:0; r; r = r->chain){
+                if(r->attr&META) continue;
+                if(strcmp(target, r->target)) continue;
+                if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue;        /* no effect; ignore */
+                if(cnt[r->rule] >= nreps) continue;
+                cnt[r->rule]++;
+                node->flags |= PROBABLE;
+
+/*                if(r->attr&VIR)
+ *                        node->flags |= VIRTUAL;
+ *                if(r->attr&NOREC)
+ *                        node->flags |= NORECIPE;
+ *                if(r->attr&DEL)
+ *                        node->flags |= DELETE;
+ */
+                if(!r->tail || !r->tail->s || !*r->tail->s) {
+                        a->next = newarc((Node *)0, r, "", rmatch);
+                        a = a->next;
+                } else
+                        for(w = r->tail; w; w = w->next){
+                                a->next = newarc(applyrules(w->s, cnt), r, "", rmatch);
+                                a = a->next;
+                }
+                cnt[r->rule]--;
+                head.n = node;
+        }
+        for(r = metarules; r; r = r->next){
+                if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue;        /* no effect; ignore */
+                if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR))
+                        continue;
+                if(r->attr®EXP){
+                        stem[0] = 0;
+                        patrule = r;
+                        memset((char*)rmatch, 0, sizeof(rmatch));
+                        if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
+                                continue;
+                } else {
+                        if(!match(node->name, r->target, stem, r->shellt)) continue;
+                }
+                if(cnt[r->rule] >= nreps) continue;
+                cnt[r->rule]++;
+
+/*                if(r->attr&VIR)
+ *                        node->flags |= VIRTUAL;
+ *                if(r->attr&NOREC)
+ *                        node->flags |= NORECIPE;
+ *                if(r->attr&DEL)
+ *                        node->flags |= DELETE;
+ */
+
+                if(!r->tail || !r->tail->s || !*r->tail->s) {
+                        a->next = newarc((Node *)0, r, stem, rmatch);
+                        a = a->next;
+                } else
+                        for(w = r->tail; w; w = w->next){
+                                if(r->attr®EXP)
+                                        regsub(w->s, buf, sizeof buf, rmatch, NREGEXP);
+                                else
+                                        subst(stem, w->s, buf);
+                                a->next = newarc(applyrules(buf, cnt), r, stem, rmatch);
+                                a = a->next;
+                        }
+                cnt[r->rule]--;
+        }
+        a->next = node->prereqs;
+        node->prereqs = head.next;
+        return(node);
+}
+
+static void
+togo(Node *node)
+{
+        Arc *la, *a;
+
+        /* delete them now */
+        la = 0;
+        for(a = node->prereqs; a; la = a, a = a->next)
+                if(a->flag&TOGO){
+                        if(a == node->prereqs)
+                                node->prereqs = a->next;
+                        else
+                                la->next = a->next, a = la;
+                }
+}
+
+static int
+vacuous(Node *node)
+{
+        Arc *la, *a;
+        int vac = !(node->flags&PROBABLE);
+
+        if(node->flags&READY)
+                return(node->flags&VACUOUS);
+        node->flags |= READY;
+        for(a = node->prereqs; a; a = a->next)
+                if(a->n && vacuous(a->n) && (a->r->attr&META))
+                        a->flag |= TOGO;
+                else
+                        vac = 0;
+        /* if a rule generated arcs that DON'T go; no others from that rule go */
+        for(a = node->prereqs; a; a = a->next)
+                if((a->flag&TOGO) == 0)
+                        for(la = node->prereqs; la; la = la->next)
+                                if((la->flag&TOGO) && (la->r == a->r)){
+                                        la->flag &= ~TOGO;
+                                }
+        togo(node);
+        if(vac)
+                node->flags |= VACUOUS;
+        return(vac);
+}
+
+static Node *
+newnode(char *name)
+{
+        register Node *node;
+
+        node = (Node *)Malloc(sizeof(Node));
+        symlook(name, S_NODE, (void *)node);
+        node->name = name;
+        node->time = timeof(name, 0);
+        node->prereqs = 0;
+        node->flags = node->time? PROBABLE : 0;
+        node->next = 0;
+        return(node);
+}
+
+void
+dumpn(char *s, Node *n)
+{
+        char buf[1024];
+        Arc *a;
+
+        snprint(buf, sizeof buf, "%s   ", (*s == ' ')? s:"");
+        Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n",
+                s, n->name, n, n->time, n->flags, n->next);
+        for(a = n->prereqs; a; a = a->next)
+                dumpa(buf, a);
+}
+
+static void
+trace(char *s, Arc *a)
+{
+        fprint(2, "\t%s", s);
+        while(a){
+                fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line,
+                        a->n? a->n->name:"");
+                if(a->n){
+                        for(a = a->n->prereqs; a; a = a->next)
+                                if(*a->r->recipe) break;
+                } else
+                        a = 0;
+        }
+        fprint(2, "\n");
+}
+
+static void
+cyclechk(Node *n)
+{
+        Arc *a;
+
+        if((n->flags&CYCLE) && n->prereqs){
+                fprint(2, "mk: cycle in graph detected at target %s\n", n->name);
+                Exit();
+        }
+        n->flags |= CYCLE;
+        for(a = n->prereqs; a; a = a->next)
+                if(a->n)
+                        cyclechk(a->n);
+        n->flags &= ~CYCLE;
+}
+
+static void
+ambiguous(Node *n)
+{
+        Arc *a;
+        Rule *r = 0;
+        Arc *la;
+        int bad = 0;
+
+        la = 0;
+        for(a = n->prereqs; a; a = a->next){
+                if(a->n)
+                        ambiguous(a->n);
+                if(*a->r->recipe == 0) continue;
+                if(r == 0)
+                        r = a->r, la = a;
+                else{
+                        if(r->recipe != a->r->recipe){
+                                if((r->attr&META) && !(a->r->attr&META)){
+                                        la->flag |= TOGO;
+                                        r = a->r, la = a;
+                                } else if(!(r->attr&META) && (a->r->attr&META)){
+                                        a->flag |= TOGO;
+                                        continue;
+                                }
+                        }
+                        if(r->recipe != a->r->recipe){
+                                if(bad == 0){
+                                        fprint(2, "mk: ambiguous recipes for %s:\n", n->name);
+                                        bad = 1;
+                                        trace(n->name, la);
+                                }
+                                trace(n->name, a);
+                        }
+                }
+        }
+        if(bad)
+                Exit();
+        togo(n);
+}
+
+static void
+attribute(Node *n)
+{
+        register Arc *a;
+
+        for(a = n->prereqs; a; a = a->next){
+                if(a->r->attr&VIR)
+                        n->flags |= VIRTUAL;
+                if(a->r->attr&NOREC)
+                        n->flags |= NORECIPE;
+                if(a->r->attr&DEL)
+                        n->flags |= DELETE;
+                if(a->n)
+                        attribute(a->n);
+        }
+        if(n->flags&VIRTUAL)
+                n->time = 0;
+}
diff --git a/mk/job.c b/mk/job.c
@@ -0,0 +1,33 @@
+#include        "mk.h"
+
+Job *
+newjob(Rule *r, Node *nlist, char *stem, char **match, Word *pre, Word *npre, Word *tar, Word *atar)
+{
+        register Job *j;
+
+        j = (Job *)Malloc(sizeof(Job));
+        j->r = r;
+        j->n = nlist;
+        j->stem = stem;
+        j->match = match;
+        j->p = pre;
+        j->np = npre;
+        j->t = tar;
+        j->at = atar;
+        j->nproc = -1;
+        j->next = 0;
+        return(j);
+}
+
+void
+dumpj(char *s, Job *j, int all)
+{
+        Bprint(&bout, "%s\n", s);
+        while(j){
+                Bprint(&bout, "job@%ld: r=%ld n=%ld stem='%s' nproc=%d\n",
+                        j, j->r, j->n, j->stem, j->nproc);
+                Bprint(&bout, "\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n",
+                        wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wtos(j->np, ' '));
+                j = all? j->next : 0;
+        }
+}
diff --git a/mk/lex.c b/mk/lex.c
@@ -0,0 +1,146 @@
+#include        "mk.h"
+
+static        int        bquote(Biobuf*, Bufblock*);
+
+/*
+ *        Assemble a line skipping blank lines, comments, and eliding
+ *        escaped newlines
+ */
+int
+assline(Biobuf *bp, Bufblock *buf)
+{
+        int c;
+        int lastc;
+
+        buf->current=buf->start;
+        while ((c = nextrune(bp, 1)) >= 0){
+                switch(c)
+                {
+                case '\r':                /* consumes CRs for Win95 */
+                        continue;
+                case '\n':
+                        if (buf->current != buf->start) {
+                                insert(buf, 0);
+                                return 1;
+                        }
+                        break;                /* skip empty lines */
+                case '\\':
+                case '\'':
+                case '"':
+                        rinsert(buf, c);
+                        if (shellt->escapetoken(bp, buf, 1, c) == 0)
+                                Exit();
+                        break;
+                case '`':
+                        if (bquote(bp, buf) == 0)
+                                Exit();
+                        break;
+                case '#':
+                        lastc = '#';
+                        while ((c = Bgetc(bp)) != '\n') {
+                                if (c < 0)
+                                        goto eof;
+                                if(c != '\r')
+                                        lastc = c;
+                        }
+                        mkinline++;
+                        if (lastc == '\\')
+                                break;                /* propagate escaped newlines??*/
+                        if (buf->current != buf->start) {
+                                insert(buf, 0);
+                                return 1;
+                        }
+                        break;
+                default:
+                        rinsert(buf, c);
+                        break;
+                }
+        }
+eof:
+        insert(buf, 0);
+        return *buf->start != 0;
+}
+
+/*
+ *        assemble a back-quoted shell command into a buffer
+ */
+static int
+bquote(Biobuf *bp, Bufblock *buf)
+{
+        int c, line, term;
+        int start;
+
+        line = mkinline;
+        while((c = Bgetrune(bp)) == ' ' || c == '\t')
+                        ;
+        if(c == '{'){
+                term = '}';                /* rc style */
+                while((c = Bgetrune(bp)) == ' ' || c == '\t')
+                        ;
+        } else
+                term = '`';                /* sh style */
+
+        start = buf->current-buf->start;
+        for(;c > 0; c = nextrune(bp, 0)){
+                if(c == term){
+                        insert(buf, '\n');
+                        insert(buf,0);
+                        buf->current = buf->start+start;
+                        execinit();
+                        execsh(0, buf->current, buf, envy, shellt, shellcmd);
+                        return 1;
+                }
+                if(c == '\n')
+                        break;
+                if(c == '\'' || c == '"' || c == '\\'){
+                        insert(buf, c);
+                        if(!shellt->escapetoken(bp, buf, 1, c))
+                                return 0;
+                        continue;
+                }
+                rinsert(buf, c);
+        }
+        SYNERR(line);
+        fprint(2, "missing closing %c after `\n", term);
+        return 0;
+}
+
+/*
+ *        get next character stripping escaped newlines
+ *        the flag specifies whether escaped newlines are to be elided or
+ *        replaced with a blank.
+ */
+int
+nextrune(Biobuf *bp, int elide)
+{
+        int c, c2;
+        static int savec;
+
+        if(savec){
+                c = savec;
+                savec = 0;
+                return c;
+        }
+
+        for (;;) {
+                c = Bgetrune(bp);
+                if (c == '\\') {
+                        c2 = Bgetrune(bp);
+                        if(c2 == '\r'){
+                                savec = c2;
+                                c2 = Bgetrune(bp);
+                        }
+                        if (c2 == '\n') {
+                                savec = 0;
+                                mkinline++;
+                                if (elide)
+                                        continue;
+                                return ' ';
+                        }
+                        Bungetrune(bp);
+                }
+                if (c == '\n')
+                        mkinline++;
+                return c;
+        }
+}
diff --git a/mk/main.c b/mk/main.c
@@ -0,0 +1,287 @@
+#include        "mk.h"
+
+#define                MKFILE                "mkfile"
+
+int debug;
+Rule *rules, *metarules;
+int nflag = 0;
+int tflag = 0;
+int iflag = 0;
+int kflag = 0;
+int aflag = 0;
+int uflag = 0;
+char *explain = 0;
+Word *target1;
+int nreps = 1;
+Job *jobs;
+Biobuf bout;
+Rule *patrule;
+void badusage(void);
+#ifdef        PROF
+short buf[10000];
+#endif
+
+int
+main(int argc, char **argv)
+{
+        Word *w;
+        char *s, *temp;
+        char *files[256], **f = files, **ff;
+        int sflag = 0;
+        int i;
+        int tfd = -1;
+        Biobuf tb;
+        Bufblock *buf;
+        Bufblock *whatif;
+
+        /*
+         *  start with a copy of the current environment variables
+         *  instead of sharing them
+         */
+
+        Binit(&bout, 1, OWRITE);
+        buf = newbuf();
+        whatif = 0;
+        USED(argc);
+        for(argv++; *argv && (**argv == '-'); argv++)
+        {
+                bufcpy(buf, argv[0], strlen(argv[0]));
+                insert(buf, ' ');
+                switch(argv[0][1])
+                {
+                case 'a':
+                        aflag = 1;
+                        break;
+                case 'd':
+                        if(*(s = &argv[0][2]))
+                                while(*s) switch(*s++)
+                                {
+                                case 'p':        debug |= D_PARSE; break;
+                                case 'g':        debug |= D_GRAPH; break;
+                                case 'e':        debug |= D_EXEC; break;
+                                }
+                        else
+                                debug = 0xFFFF;
+                        break;
+                case 'e':
+                        explain = &argv[0][2];
+                        break;
+                case 'f':
+                        if(*++argv == 0)
+                                badusage();
+                        *f++ = *argv;
+                        bufcpy(buf, argv[0], strlen(argv[0]));
+                        insert(buf, ' ');
+                        break;
+                case 'i':
+                        iflag = 1;
+                        break;
+                case 'k':
+                        kflag = 1;
+                        break;
+                case 'n':
+                        nflag = 1;
+                        break;
+                case 's':
+                        sflag = 1;
+                        break;
+                case 't':
+                        tflag = 1;
+                        break;
+                case 'u':
+                        uflag = 1;
+                        break;
+                case 'w':
+                        if(whatif == 0)
+                                whatif = newbuf();
+                        else
+                                insert(whatif, ' ');
+                        if(argv[0][2])
+                                bufcpy(whatif, &argv[0][2], strlen(&argv[0][2]));
+                        else {
+                                if(*++argv == 0)
+                                        badusage();
+                                bufcpy(whatif, &argv[0][0], strlen(&argv[0][0]));
+                        }
+                        break;
+                default:
+                        badusage();
+                }
+        }
+#ifdef        PROF
+        {
+                extern etext();
+                monitor(main, etext, buf, sizeof buf, 300);
+        }
+#endif
+
+        if(aflag)
+                iflag = 1;
+        usage();
+        syminit();
+        initshell();
+        initenv();
+        usage();
+
+        /*
+                assignment args become null strings
+        */
+        temp = 0;
+        for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){
+                bufcpy(buf, argv[i], strlen(argv[i]));
+                insert(buf, ' ');
+                if(tfd < 0){
+                        temp = maketmp(&tfd);
+                        if(temp == 0) {
+                                fprint(2, "temp file: %r\n");
+                                Exit();
+                        }
+                        Binit(&tb, tfd, OWRITE);
+                }
+                Bprint(&tb, "%s\n", argv[i]);
+                *argv[i] = 0;
+        }
+        if(tfd >= 0){
+                Bflush(&tb);
+                LSEEK(tfd, 0L, 0);
+                parse("command line args", tfd, 1);
+                remove(temp);
+        }
+
+        if (buf->current != buf->start) {
+                buf->current--;
+                insert(buf, 0);
+        }
+        symlook("MKFLAGS", S_VAR, (void *) stow(buf->start));
+        buf->current = buf->start;
+        for(i = 0; argv[i]; i++){
+                if(*argv[i] == 0) continue;
+                if(i)
+                        insert(buf, ' ');
+                bufcpy(buf, argv[i], strlen(argv[i]));
+        }
+        insert(buf, 0);
+        symlook("MKARGS", S_VAR, (void *) stow(buf->start));
+        freebuf(buf);
+
+        if(f == files){
+                if(access(MKFILE, 4) == 0)
+                        parse(MKFILE, open(MKFILE, 0), 0);
+        } else
+                for(ff = files; ff < f; ff++)
+                        parse(*ff, open(*ff, 0), 0);
+        if(DEBUG(D_PARSE)){
+                dumpw("default targets", target1);
+                dumpr("rules", rules);
+                dumpr("metarules", metarules);
+                dumpv("variables");
+        }
+        if(whatif){
+                insert(whatif, 0);
+                timeinit(whatif->start);
+                freebuf(whatif);
+        }
+        execinit();
+        /* skip assignment args */
+        while(*argv && (**argv == 0))
+                argv++;
+
+        catchnotes();
+        if(*argv == 0){
+                if(target1)
+                        for(w = target1; w; w = w->next)
+                                mk(w->s);
+                else {
+                        fprint(2, "mk: nothing to mk\n");
+                        Exit();
+                }
+        } else {
+                if(sflag){
+                        for(; *argv; argv++)
+                                if(**argv)
+                                        mk(*argv);
+                } else {
+                        Word *head, *tail, *t;
+
+                        /* fake a new rule with all the args as prereqs */
+                        tail = 0;
+                        t = 0;
+                        for(; *argv; argv++)
+                                if(**argv){
+                                        if(tail == 0)
+                                                tail = t = newword(*argv);
+                                        else {
+                                                t->next = newword(*argv);
+                                                t = t->next;
+                                        }
+                                }
+                        if(tail->next == 0)
+                                mk(tail->s);
+                        else {
+                                head = newword("command line arguments");
+                                addrules(head, tail, strdup(""), VIR, mkinline, 0);
+                                mk(head->s);
+                        }
+                }
+        }
+        if(uflag)
+                prusage();
+        exits(0);
+        return 0;
+}
+
+void
+badusage(void)
+{
+
+        fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n");
+        Exit();
+}
+
+void *
+Malloc(int n)
+{
+        register void *s;
+
+        s = malloc(n);
+        if(!s) {
+                fprint(2, "mk: cannot alloc %d bytes\n", n);
+                Exit();
+        }
+        return(s);
+}
+
+void *
+Realloc(void *s, int n)
+{
+        if(s)
+                s = realloc(s, n);
+        else
+                s = malloc(n);
+        if(!s) {
+                fprint(2, "mk: cannot alloc %d bytes\n", n);
+                Exit();
+        }
+        return(s);
+}
+
+void
+assert(char *s, int n)
+{
+        if(!n){
+                fprint(2, "mk: Assertion ``%s'' failed.\n", s);
+                Exit();
+        }
+}
+
+void
+regerror(char *s)
+{
+        if(patrule)
+                fprint(2, "mk: %s:%d: regular expression error; %s\n",
+                        patrule->file, patrule->line, s);
+        else
+                fprint(2, "mk: %s:%d: regular expression error; %s\n",
+                        infile, mkinline, s);
+        Exit();
+}
diff --git a/mk/match.c b/mk/match.c
@@ -0,0 +1,49 @@
+#include        "mk.h"
+
+int
+match(char *name, char *template, char *stem, Shell *sh)
+{
+        Rune r;
+        int n;
+
+        while(*name && *template){
+                n = chartorune(&r, template);
+                if (PERCENT(r))
+                        break;
+                while (n--)
+                        if(*name++ != *template++)
+                                return 0;
+        }
+        if(!PERCENT(*template))
+                return 0;
+        n = strlen(name)-strlen(template+1);
+        if (n < 0)
+                return 0;
+        if (strcmp(template+1, name+n))
+                return 0;
+        strncpy(stem, name, n);
+        stem[n] = 0;
+        if(*template == '&')
+                return !sh->charin(stem, "./");
+        return 1;
+}
+
+void
+subst(char *stem, char *template, char *dest)
+{
+        Rune r;
+        char *s;
+        int n;
+
+        while(*template){
+                n = chartorune(&r, template);
+                if (PERCENT(r)) {
+                        template += n;
+                        for (s = stem; *s; s++)
+                                *dest++ = *s;
+                } else
+                        while (n--)
+                                *dest++ = *template++;
+        }
+        *dest = 0;
+}
diff --git a/mk/mk.1 b/mk/mk.1
@@ -0,0 +1,691 @@
+.TH MK 1
+.SH NAME
+mk \- maintain (make) related files
+.SH SYNOPSIS
+.B mk
+[
+.B -f
+.I mkfile
+] ...
+[
+.I option ...
+]
+[
+.I target ...
+]
+.SH DESCRIPTION
+.I Mk
+uses the dependency rules specified in
+.I mkfile
+to control the update (usually by compilation) of
+.I targets
+(usually files)
+from the source files upon which they depend.
+The
+.I mkfile
+(default
+.LR mkfile )
+contains a
+.I rule
+for each target that identifies the files and other
+targets upon which it depends and an
+.IR sh (1)
+script, a
+.IR recipe ,
+to update the target.
+The script is run if the target does not exist
+or if it is older than any of the files it depends on.
+.I Mkfile
+may also contain
+.I meta-rules
+that define actions for updating implicit targets.
+If no
+.I target
+is specified, the target of the first rule (not meta-rule) in
+.I mkfile
+is updated.
+.PP
+The environment variable
+.B $NPROC
+determines how many targets may be updated simultaneously;
+Some operating systems, e.g., Plan 9, set
+.B $NPROC
+automatically to the number of CPUs on the current machine.
+.PP
+Options are:
+.TP \w'\fL-d[egp]\ 'u
+.B -a
+Assume all targets to be out of date.
+Thus, everything is updated.
+.PD 0
+.TP
+.BR -d [ egp ]
+Produce debugging output
+.RB ( p
+is for parsing,
+.B g
+for graph building,
+.B e
+for execution).
+.TP
+.B -e
+Explain why each target is made.
+.TP
+.B -i
+Force any missing intermediate targets to be made.
+.TP
+.B -k
+Do as much work as possible in the face of errors.
+.TP
+.B -n
+Print, but do not execute, the commands
+needed to update the targets.
+.TP
+.B -s
+Make the command line arguments sequentially rather than in parallel.
+.TP
+.B -t
+Touch (update the modified date of) file targets, without
+executing any recipes.
+.TP
+.BI -w target1 , target2,...
+Pretend the modify time for each
+.I target
+is the current time; useful in conjunction with
+.B -n
+to learn what updates would be triggered by
+modifying the
+.IR targets .
+.PD
+.SS The \fLmkfile\fP
+A
+.I mkfile
+consists of
+.I assignments
+(described under `Environment') and
+.IR rules .
+A rule contains
+.I targets
+and a
+.IR tail .
+A target is a literal string
+and is normally a file name.
+The tail contains zero or more 
+.I prerequisites
+and an optional
+.IR recipe ,
+which is an
+.B shell
+script.
+Each line of the recipe must begin with white space.
+A rule takes the form
+.IP
+.EX
+target: prereq1 prereq2
+        \f2recipe using\fP prereq1, prereq2 \f2to build\fP target
+.EE
+.PP
+When the recipe is executed,
+the first character on every line is elided.
+.PP
+After the colon on the target line, a rule may specify
+.IR attributes ,
+described below.
+.PP
+A
+.I meta-rule 
+has a target of the form
+.IB A % B
+where
+.I A
+and
+.I B
+are (possibly empty) strings.
+A meta-rule acts as a rule for any potential target whose
+name matches
+.IB A % B
+with
+.B %
+replaced by an arbitrary string, called the
+.IR stem .
+In interpreting a meta-rule,
+the stem is substituted for all occurrences of
+.B %
+in the prerequisite names.
+In the recipe of a meta-rule, the environment variable
+.B $stem
+contains the string matched by the
+.BR % .
+For example, a meta-rule to compile a C program using
+.IR 9c (1)
+might be:
+.IP
+.EX
+%:    %.c
+        9c -c $stem.c
+        9l -o $stem $stem.o
+.EE
+.PP
+Meta-rules may contain an ampersand
+.B &
+rather than a percent sign
+.BR % .
+A
+.B %
+matches a maximal length string of any characters;
+an
+.B &
+matches a maximal length string of any characters except period
+or slash.
+.PP
+The text of the
+.I mkfile
+is processed as follows.
+Lines beginning with
+.B <
+followed by a file name are replaced by the contents of the named
+file.
+Lines beginning with
+.B "<|"
+followed by a file name are replaced by the output
+of the execution of the named
+file.
+Blank lines and comments, which run from unquoted
+.B #
+characters to the following newline, are deleted.
+The character sequence backslash-newline is deleted,
+so long lines in
+.I mkfile
+may be folded.
+Non-recipe lines are processed by substituting for
+.BI `{ command }
+the output of the
+.I command
+when run by
+.IR sh .
+References to variables are replaced by the variables' values.
+Special characters may be quoted using single quotes
+.BR \&''
+as in
+.IR sh (1).
+.PP
+Assignments and rules are distinguished by
+the first unquoted occurrence of
+.B :
+(rule)
+or
+.B =
+(assignment).
+.PP
+A later rule may modify or override an existing rule under the
+following conditions:
+.TP
+\-
+If the targets of the rules exactly match and one rule
+contains only a prerequisite clause and no recipe, the
+clause is added to the prerequisites of the other rule.
+If either or both targets are virtual, the recipe is
+always executed.
+.TP
+\-
+If the targets of the rules match exactly and the
+prerequisites do not match and both rules
+contain recipes,
+.I mk
+reports an ``ambiguous recipe'' error.
+.TP
+\-
+If the target and prerequisites of both rules match exactly,
+the second rule overrides the first.
+.SS Environment
+Rules may make use of
+shell
+environment variables.
+A legal reference of the form
+.B $OBJ
+or
+.B ${name}
+is expanded as in
+.IR sh (1).
+A reference of the form
+.BI ${name: A % B = C\fL%\fID\fL}\fR,
+where
+.I A, B, C, D
+are (possibly empty) strings,
+has the value formed by expanding
+.B $name
+and substituting
+.I C
+for
+.I A
+and
+.I D
+for
+.I B
+in each word in
+.B $name
+that matches pattern
+.IB A % B\f1.
+.PP
+Variables can be set by
+assignments of the form
+.I
+        var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR
+.br
+Blanks in the
+.I value
+break it into words.
+Such variables are exported
+to the environment of
+recipes as they are executed, unless
+.BR U ,
+the only legal attribute
+.IR attr ,
+is present.
+The initial value of a variable is
+taken from (in increasing order of precedence)
+the default values below,
+.I mk's
+environment, the
+.IR mkfiles ,
+and any command line assignment as an argument to
+.IR mk .
+A variable assignment argument overrides the first (but not any subsequent)
+assignment to that variable.
+.PP
+The variable
+.B MKFLAGS
+contains all the option arguments (arguments starting with
+.L -
+or containing
+.LR = )
+and
+.B MKARGS
+contains all the targets in the call to
+.IR mk .
+.PP
+The variable
+.B MKSHELL
+contains the shell command line
+.I mk
+uses to run recipes.
+If the first word of the command ends in
+.B rc
+or
+.BR rcsh ,
+.I mk
+uses
+.IR rc (1)'s
+quoting rules; otherwise it uses
+.IR sh (1)'s.
+The
+.B MKSHELL
+variable is consulted when the mkfile is read, not when it is executed,
+so that different shells can be used within a single mkfile:
+.IP
+.EX
+MKSHELL=$PLAN9/bin/rc
+use-rc:V:
+        for(i in a b c) echo $i
+
+MKSHELL=sh
+use-sh:V:
+        for i in a b c; do echo $i; done
+.EE
+.LP
+Mkfiles included via
+.B <
+or
+.B <|
+.RI ( q.v. )
+see their own private copy of
+.BR MKSHELL ,
+which always starts set to
+.B sh .
+.PP
+Dynamic information may be included in the mkfile by using a line of the form
+.IP
+\fR<|\fIcommand\fR \fIargs\fR
+.LP
+This runs the command 
+.I command
+with the given arguments
+.I args
+and pipes its standard output to
+.I mk
+to be included as part of the mkfile. For instance, the Inferno kernels
+use this technique
+to run a shell command with an awk script and a configuration
+file as arguments in order for
+the
+.I awk
+script to process the file and output a set of variables and their values.
+.SS Execution
+.PP
+During execution,
+.I mk
+determines which targets must be updated, and in what order,
+to build the
+.I names
+specified on the command line.
+It then runs the associated recipes.
+.PP
+A target is considered up to date if it has no prerequisites or
+if all its prerequisites are up to date and it is newer
+than all its prerequisites.
+Once the recipe for a target has executed, the target is
+considered up to date.
+.PP
+The date stamp
+used to determine if a target is up to date is computed
+differently for different types of targets.
+If a target is
+.I virtual
+(the target of a rule with the
+.B V
+attribute),
+its date stamp is initially zero; when the target is
+updated the date stamp is set to
+the most recent date stamp of its prerequisites.
+Otherwise, if a target does not exist as a file,
+its date stamp is set to the most recent date stamp of its prerequisites,
+or zero if it has no prerequisites.
+Otherwise, the target is the name of a file and
+the target's date stamp is always that file's modification date.
+The date stamp is computed when the target is needed in
+the execution of a rule; it is not a static value.
+.PP
+Nonexistent targets that have prerequisites
+and are themselves prerequisites are treated specially.
+Such a target
+.I t
+is given the date stamp of its most recent prerequisite
+and if this causes all the targets which have
+.I t
+as a prerequisite to be up to date,
+.I t
+is considered up to date.
+Otherwise,
+.I t
+is made in the normal fashion.
+The
+.B -i
+flag overrides this special treatment.
+.PP
+Files may be made in any order that respects
+the preceding restrictions.
+.PP
+A recipe is executed by supplying the recipe as standard input to
+the command
+.BR /bin/sh .
+(Note that unlike
+.IR make ,
+.I mk
+feeds the entire recipe to the shell rather than running each line
+of the recipe separately.)
+The environment is augmented by the following variables:
+.TP 14
+.B $alltarget
+all the targets of this rule.
+.TP
+.B $newprereq
+the prerequisites that caused this rule to execute.
+.TP
+.B $newmember
+the prerequisites that are members of an aggregate
+that caused this rule to execute.
+When the prerequisites of a rule are members of an
+aggregate,
+.B $newprereq
+contains the name of the aggregate and out of date
+members, while
+.B $newmember
+contains only the name of the members.
+.TP
+.B $nproc
+the process slot for this recipe.
+It satisfies
+.RB 0≤ $nproc < $NPROC .
+.TP
+.B $pid
+the process id for the
+.I mk
+executing the recipe.
+.TP
+.B $prereq
+all the prerequisites for this rule.
+.TP
+.B $stem
+if this is a meta-rule,
+.B $stem
+is the string that matched
+.B %
+or
+.BR & .
+Otherwise, it is empty.
+For regular expression meta-rules (see below), the variables
+.LR stem0 ", ...,"
+.L stem9
+are set to the corresponding subexpressions.
+.TP
+.B $target
+the targets for this rule that need to be remade.
+.PP
+These variables are available only during the execution of a recipe,
+not while evaluating the
+.IR mkfile .
+.PP
+Unless the rule has the
+.B Q
+attribute,
+the recipe is printed prior to execution
+with recognizable environment variables expanded.
+Commands returning error status
+cause
+.I mk
+to terminate.
+.PP
+Recipes and backquoted
+.B rc
+commands in places such as assignments
+execute in a copy of
+.I mk's
+environment; changes they make to
+environment variables are not visible from
+.IR mk .
+.PP
+Variable substitution in a rule is done when
+the rule is read; variable substitution in the recipe is done
+when the recipe is executed.  For example:
+.IP
+.EX
+bar=a.c
+foo:        $bar
+        $CC -o foo $bar
+bar=b.c
+.EE
+.PP
+will compile
+.B b.c
+into
+.BR foo ,
+if
+.B a.c
+is newer than
+.BR foo .
+.SS Aggregates
+Names of the form
+.IR a ( b )
+refer to member
+.I b
+of the aggregate
+.IR a .
+Currently, the only aggregates supported are
+.I 9ar
+(see
+.IR 9c (1))
+archives.
+.SS Attributes
+The colon separating the target from the prerequisites
+may be
+immediately followed by
+.I attributes
+and another colon.
+The attributes are:
+.TP
+.B D
+If the recipe exits with a non-null status, the target is deleted.
+.TP
+.B E
+Continue execution if the recipe draws errors.
+.TP
+.B N
+If there is no recipe, the target has its time updated.
+.TP
+.B n
+The rule is a meta-rule that cannot be a target of a virtual rule.
+Only files match the pattern in the target.
+.TP
+.B P
+The characters after the
+.B P
+until the terminating
+.B :
+are taken as a program name.
+It will be invoked as
+.B "sh -c prog 'arg1' 'arg2'"
+and should return a zero exit status
+if and only if arg1 is up to date with respect to arg2.
+Date stamps are still propagated in the normal way.
+.TP
+.B Q
+The recipe is not printed prior to execution.
+.TP
+.B R
+The rule is a meta-rule using regular expressions.
+In the rule,
+.B %
+has no special meaning.
+The target is interpreted as a regular expression as defined in
+.IR regexp (7).
+The prerequisites may contain references
+to subexpressions in form
+.BI \e n\f1,
+as in the substitute command of
+.IR sed (1).
+.TP
+.B U
+The targets are considered to have been updated
+even if the recipe did not do so.
+.TP
+.B V
+The targets of this rule are marked as virtual.
+They are distinct from files of the same name.
+.PD
+.SH EXAMPLES
+A simple mkfile to compile a program:
+.IP
+.EX
+.ta 8n +8n +8n +8n +8n +8n +8n
+
diff --git a/mk/mk.c b/mk/mk.c
@@ -0,0 +1,234 @@
+#include        "mk.h"
+
+int runerrs;
+
+void
+mk(char *target)
+{
+        Node *node;
+        int did = 0;
+
+        nproc();                /* it can be updated dynamically */
+        nrep();                        /* it can be updated dynamically */
+        runerrs = 0;
+        node = graph(target);
+        if(DEBUG(D_GRAPH)){
+                dumpn("new target\n", node);
+                Bflush(&bout);
+        }
+        clrmade(node);
+        while(node->flags&NOTMADE){
+                if(work(node, (Node *)0, (Arc *)0))
+                        did = 1;        /* found something to do */
+                else {
+                        if(waitup(1, (int *)0) > 0){
+                                if(node->flags&(NOTMADE|BEINGMADE)){
+                                        assert("must be run errors", runerrs);
+                                        break;        /* nothing more waiting */
+                                }
+                        }
+                }
+        }
+        if(node->flags&BEINGMADE)
+                waitup(-1, (int *)0);
+        while(jobs)
+                waitup(-2, (int *)0);
+        assert("target didn't get done", runerrs || (node->flags&MADE));
+        if(did == 0)
+                Bprint(&bout, "mk: '%s' is up to date\n", node->name);
+}
+
+void
+clrmade(Node *n)
+{
+        Arc *a;
+
+        n->flags &= ~(CANPRETEND|PRETENDING);
+        if(strchr(n->name, '(') ==0 || n->time)
+                n->flags |= CANPRETEND;
+        MADESET(n, NOTMADE);
+        for(a = n->prereqs; a; a = a->next)
+                if(a->n)
+                        clrmade(a->n);
+}
+
+static void
+unpretend(Node *n)
+{
+        MADESET(n, NOTMADE);
+        n->flags &= ~(CANPRETEND|PRETENDING);
+        n->time = 0;
+}
+
+static char*
+dir(void)
+{
+        static char buf[1024];
+
+        return getcwd(buf, sizeof buf);
+}
+
+int
+work(Node *node, Node *p, Arc *parc)
+{
+        Arc *a, *ra;
+        int weoutofdate;
+        int ready;
+        int did = 0;
+
+        /*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time); */
+        if(node->flags&BEINGMADE)
+                return(did);
+        if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){
+                if(explain)
+                        fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n",
+                                node->name, node->time, p->name, p->time);
+                unpretend(node);
+        }
+        /*
+                have a look if we are pretending in case
+                someone has been unpretended out from underneath us
+        */
+        if(node->flags&MADE){
+                if(node->flags&PRETENDING){
+                        node->time = 0;
+                }else
+                        return(did);
+        }
+        /* consider no prerequsite case */
+        if(node->prereqs == 0){
+                if(node->time == 0){
+                        fprint(2, "mk: don't know how to make '%s' in %s\n", node->name, dir());
+                        if(kflag){
+                                node->flags |= BEINGMADE;
+                                runerrs++;
+                        } else
+                                Exit();
+                } else
+                        MADESET(node, MADE);
+                return(did);
+        }
+        /*
+                now see if we are out of date or what
+        */
+        ready = 1;
+        weoutofdate = aflag;
+        ra = 0;
+        for(a = node->prereqs; a; a = a->next)
+                if(a->n){
+                        did = work(a->n, node, a) || did;
+                        if(a->n->flags&(NOTMADE|BEINGMADE))
+                                ready = 0;
+                        if(outofdate(node, a, 0)){
+                                weoutofdate = 1;
+                                if((ra == 0) || (ra->n == 0)
+                                                || (ra->n->time < a->n->time))
+                                        ra = a;
+                        }
+                } else {
+                        if(node->time == 0){
+                                if(ra == 0)
+                                        ra = a;
+                                weoutofdate = 1;
+                        }
+                }
+        if(ready == 0)        /* can't do anything now */
+                return(did);
+        if(weoutofdate == 0){
+                MADESET(node, MADE);
+                return(did);
+        }
+        /*
+                can we pretend to be made?
+        */
+        if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND))
+                        && p && ra->n && !outofdate(p, ra, 0)){
+                node->flags &= ~CANPRETEND;
+                MADESET(node, MADE);
+                if(explain && ((node->flags&PRETENDING) == 0))
+                        fprint(1, "pretending %s has time %ld\n", node->name, node->time);
+                node->flags |= PRETENDING;
+                return(did);
+        }
+        /*
+                node is out of date and we REALLY do have to do something.
+                quickly rescan for pretenders
+        */
+        for(a = node->prereqs; a; a = a->next)
+                if(a->n && (a->n->flags&PRETENDING)){
+                        if(explain)
+                                Bprint(&bout, "unpretending %s because of %s because of %s\n",
+                                a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites");
+
+                        unpretend(a->n);
+                        did = work(a->n, node, a) || did;
+                        ready = 0;
+                }
+        if(ready == 0)        /* try later unless nothing has happened for -k's sake */
+                return(did || work(node, p, parc));
+        did = dorecipe(node) || did;
+        return(did);
+}
+
+void
+update(int fake, Node *node)
+{
+        Arc *a;
+
+        MADESET(node, fake? BEINGMADE : MADE);
+        if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){
+                node->time = timeof(node->name, 1);
+                node->flags &= ~(CANPRETEND|PRETENDING);
+                for(a = node->prereqs; a; a = a->next)
+                        if(a->prog)
+                                outofdate(node, a, 1);
+        } else {
+                node->time = 1;
+                for(a = node->prereqs; a; a = a->next)
+                        if(a->n && outofdate(node, a, 1))
+                                node->time = a->n->time;
+        }
+/*        print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);*/
+}
+
+static int
+pcmp(char *prog, char *p, char *q, Shell *sh, Word *shcmd)
+{
+        char buf[3*NAMEBLOCK];
+        int pid;
+
+        Bflush(&bout);
+        snprint(buf, sizeof buf, "%s '%s' '%s'\n", prog, p, q);
+        pid = pipecmd(buf, 0, 0, sh, shcmd);
+        while(waitup(-3, &pid) >= 0)
+                ;
+        return(pid? 2:1);
+}
+
+int
+outofdate(Node *node, Arc *arc, int eval)
+{
+        char buf[3*NAMEBLOCK], *str;
+        Symtab *sym;
+        int ret;
+
+        str = 0;
+        if(arc->prog){
+                snprint(buf, sizeof buf, "%s%c%s", node->name, 0377, arc->n->name);
+                sym = symlook(buf, S_OUTOFDATE, 0);
+                if(sym == 0 || eval){
+                        if(sym == 0)
+                                str = strdup(buf);
+                        ret = pcmp(arc->prog, node->name, arc->n->name, arc->r->shellt, arc->r->shellcmd);
+                        if(sym)
+                                sym->u.value = ret;
+                        else
+                                symlook(str, S_OUTOFDATE, (void *)(uintptr)ret);
+                } else
+                        ret = sym->u.value;
+                return(ret-1);
+        } else if(strchr(arc->n->name, '(') && arc->n->time == 0)  /* missing archive member */
+                return 1;
+        else
+                return node->time <= arc->n->time;
+}
diff --git a/mk/mk.h b/mk/mk.h
@@ -0,0 +1,185 @@
+#include "sys.h"
+
+#undef assert
+#define        assert        mkassert
+extern Biobuf bout;
+
+typedef struct Bufblock
+{
+        struct Bufblock *next;
+        char                 *start;
+        char                 *end;
+        char                 *current;
+} Bufblock;
+
+typedef struct Word
+{
+        char                 *s;
+        struct Word         *next;
+} Word;
+
+typedef struct Envy
+{
+        char                 *name;
+        Word                 *values;
+} Envy;
+
+extern Envy *envy;
+
+typedef struct Shell
+{
+        char *name;
+        char        *termchars;        /* used in parse.c to isolate assignment attribute */
+        int        iws;                        /* inter-word separator in environment */
+        char        *(*charin)(char*, char*);        /* search for unescaped characters */
+        char        *(*expandquote)(char*, Rune, Bufblock*);        /* extract escaped token */
+        int        (*escapetoken)(Biobuf*, Bufblock*, int, int);        /* input escaped token */
+        char        *(*copyq)(char*, Rune, Bufblock*);        /* check for quoted strings */
+        int        (*matchname)(char*);        /* does name match */
+} Shell;
+
+typedef struct Rule
+{
+        char                 *target;        /* one target */
+        Word                 *tail;                /* constituents of targets */
+        char                 *recipe;        /* do it ! */
+        short                 attr;                /* attributes */
+        short                 line;                /* source line */
+        char                 *file;                /* source file */
+        Word                 *alltargets;        /* all the targets */
+        int                 rule;                /* rule number */
+        Reprog                *pat;                /* reg exp goo */
+        char                *prog;                /* to use in out of date */
+        struct Rule        *chain;                /* hashed per target */
+        struct Rule        *next;
+        Shell                *shellt;        /* shell to use with this rule */
+        Word        *shellcmd;
+} Rule;
+
+extern Rule *rules, *metarules, *patrule;
+
+/*        Rule.attr        */
+#define                META                0x0001
+#define                UNUSED                0x0002
+#define                UPD                0x0004
+#define                QUIET                0x0008
+#define                VIR                0x0010
+#define                REGEXP                0x0020
+#define                NOREC                0x0040
+#define                DEL                0x0080
+#define                NOVIRT                0x0100
+
+#define                NREGEXP                10
+
+typedef struct Arc
+{
+        short                flag;
+        struct Node        *n;
+        Rule                *r;
+        char                *stem;
+        char                *prog;
+        char                *match[NREGEXP];
+        struct Arc        *next;
+} Arc;
+
+        /* Arc.flag */
+#define                TOGO                1
+
+typedef struct Node
+{
+        char                *name;
+        long                time;
+        unsigned short        flags;
+        Arc                *prereqs;
+        struct Node        *next;                /* list for a rule */
+} Node;
+
+        /* Node.flags */
+#define                VIRTUAL                0x0001
+#define                CYCLE                0x0002
+#define                READY                0x0004
+#define                CANPRETEND        0x0008
+#define                PRETENDING        0x0010
+#define                NOTMADE                0x0020
+#define                BEINGMADE        0x0040
+#define                MADE                0x0080
+#define                MADESET(n,m)        n->flags = (n->flags&~(NOTMADE|BEINGMADE|MADE))|(m)
+#define                PROBABLE        0x0100
+#define                VACUOUS                0x0200
+#define                NORECIPE        0x0400
+#define                DELETE                0x0800
+#define                NOMINUSE        0x1000
+
+typedef struct Job
+{
+        Rule                *r;        /* master rule for job */
+        Node                *n;        /* list of node targets */
+        char                *stem;
+        char                **match;
+        Word                *p;        /* prerequistes */
+        Word                *np;        /* new prerequistes */
+        Word                *t;        /* targets */
+        Word                *at;        /* all targets */
+        int                nproc;        /* slot number */
+        struct Job        *next;
+} Job;
+extern Job *jobs;
+
+typedef struct Symtab
+{
+        short                space;
+        char                *name;
+        union {
+                void        *ptr;
+                uintptr        value;
+        } u;
+        struct Symtab        *next;
+} Symtab;
+
+enum {
+        S_VAR,                /* variable -> value */
+        S_TARGET,        /* target -> rule */
+        S_TIME,                /* file -> time */
+        S_PID,                /* pid -> products */
+        S_NODE,                /* target name -> node */
+        S_AGG,                /* aggregate -> time */
+        S_BITCH,        /* bitched about aggregate not there */
+        S_NOEXPORT,        /* var -> noexport */
+        S_OVERRIDE,        /* can't override */
+        S_OUTOFDATE,        /* n1\377n2 -> 2(outofdate) or 1(not outofdate) */
+        S_MAKEFILE,        /* target -> node */
+        S_MAKEVAR,        /* dumpable mk variable */
+        S_EXPORTED,        /* var -> current exported value */
+        S_WESET,        /* variable; we set in the mkfile */
+        S_INTERNAL        /* an internal mk variable (e.g., stem, target) */
+};
+
+extern        int        debug;
+extern        int        nflag, tflag, iflag, kflag, aflag, mflag;
+extern        int        mkinline;
+extern        char        *infile;
+extern        int        nreps;
+extern        char        *explain;
+extern        Shell        *shellt;
+extern        Word        *shellcmd;
+
+extern        Shell        shshell, rcshell;
+
+#define        SYNERR(l)        (fprint(2, "mk: %s:%d: syntax error; ", infile, ((l)>=0)?(l):mkinline))
+#define        RERR(r)                (fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line))
+#define        NAMEBLOCK        1000
+#define        BIGBLOCK        20000
+
+#define        SEP(c)                (((c)==' ')||((c)=='\t')||((c)=='\n'))
+#define WORDCHR(r)        ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r)))
+
+#define        DEBUG(x)        (debug&(x))
+#define                D_PARSE                0x01
+#define                D_GRAPH                0x02
+#define                D_EXEC                0x04
+
+#define        LSEEK(f,o,p)        seek(f,o,p)
+
+#define        PERCENT(ch)        (((ch) == '%') || ((ch) == '&'))
+
+#include        "fns.h"
diff --git a/mk/mkfile b/mk/mkfile
@@ -0,0 +1,37 @@
+<$PLAN9/src/mkhdr
+
+TARG=mk
+
+OFILES=\
+        arc.$O\
+        archive.$O\
+        bufblock.$O\
+        env.$O\
+        file.$O\
+        graph.$O\
+        job.$O\
+        lex.$O\
+        main.$O\
+        match.$O\
+        mk.$O\
+        parse.$O\
+        recipe.$O\
+        rc.$O\
+        rule.$O\
+        run.$O\
+        sh.$O\
+        shell.$O\
+        shprint.$O\
+        symtab.$O\
+        var.$O\
+        varsub.$O\
+        word.$O\
+        unix.$O\
+
+HFILES=\
+        mk.h\
+        fns.h\
+
+
+<$PLAN9/src/mkone
+
diff --git a/mk/mkfile.test b/mk/mkfile.test
@@ -0,0 +1,12 @@
+MKSHELL=$PLAN9/bin/rc
+use-rc:V:
+        for(i in a b c)
+                echo $i
+
+MKSHELL=/bin/sh
+use-sh:V:
+        for i in a b c
+        do
+                echo $i
+        done
+
diff --git a/mk/parse.c b/mk/parse.c
@@ -0,0 +1,318 @@
+#include        "mk.h"
+
+char *infile;
+int mkinline;
+static int rhead(char *, Word **, Word **, int *, char **);
+static char *rbody(Biobuf*);
+extern Word *target1;
+
+void
+parse(char *f, int fd, int varoverride)
+{
+        int hline;
+        char *body;
+        Word *head, *tail;
+        int attr, set, pid;
+        char *prog, *p;
+        int newfd;
+        Biobuf in;
+        Bufblock *buf;
+        char *err;
+
+        if(fd < 0){
+                fprint(2, "open %s: %r\n", f);
+                Exit();
+        }
+        pushshell();
+        ipush();
+        infile = strdup(f);
+        mkinline = 1;
+        Binit(&in, fd, OREAD);
+        buf = newbuf();
+        while(assline(&in, buf)){
+                hline = mkinline;
+                switch(rhead(buf->start, &head, &tail, &attr, &prog))
+                {
+                case '<':
+                        p = wtos(tail, ' ');
+                        if(*p == 0){
+                                SYNERR(-1);
+                                fprint(2, "missing include file name\n");
+                                Exit();
+                        }
+                        newfd = open(p, OREAD);
+                        if(newfd < 0){
+                                fprint(2, "warning: skipping missing include file %s: %r\n", p);
+                        } else
+                                parse(p, newfd, 0);
+                        break;
+                case '|':
+                        p = wtos(tail, ' ');
+                        if(*p == 0){
+                                SYNERR(-1);
+                                fprint(2, "missing include program name\n");
+                                Exit();
+                        }
+                        execinit();
+                        pid=pipecmd(p, envy, &newfd, shellt, shellcmd);
+                        if(newfd < 0){
+                                fprint(2, "warning: skipping missing program file %s: %r\n", p);
+                        } else
+                                parse(p, newfd, 0);
+                        while(waitup(-3, &pid) >= 0)
+                                ;
+                        if(pid != 0){
+                                fprint(2, "bad include program status\n");
+                                Exit();
+                        }
+                        break;
+                case ':':
+                        body = rbody(&in);
+                        addrules(head, tail, body, attr, hline, prog);
+                        break;
+                case '=':
+                        if(head->next){
+                                SYNERR(-1);
+                                fprint(2, "multiple vars on left side of assignment\n");
+                                Exit();
+                        }
+                        if(symlook(head->s, S_OVERRIDE, 0)){
+                                set = varoverride;
+                        } else {
+                                set = 1;
+                                if(varoverride)
+                                        symlook(head->s, S_OVERRIDE, (void *)"");
+                        }
+                        if(set){
+/*
+char *cp;
+dumpw("tail", tail);
+cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
+*/
+                                setvar(head->s, (void *) tail);
+                                symlook(head->s, S_WESET, (void *)"");
+                                if(strcmp(head->s, "MKSHELL") == 0){
+                                        if((err = setshell(tail)) != nil){
+                                                SYNERR(hline);
+                                                fprint(2, "%s\n", err);
+                                                Exit();
+                                                break;
+                                        }
+                                }
+                        }
+                        if(attr)
+                                symlook(head->s, S_NOEXPORT, (void *)"");
+                        break;
+                default:
+                        SYNERR(hline);
+                        fprint(2, "expected one of :<=\n");
+                        Exit();
+                        break;
+                }
+        }
+        close(fd);
+        freebuf(buf);
+        ipop();
+        popshell();
+}
+
+void
+addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog)
+{
+        Word *w;
+
+        assert("addrules args", head && body);
+                /* tuck away first non-meta rule as default target*/
+        if(target1 == 0 && !(attr®EXP)){
+                for(w = head; w; w = w->next)
+                        if(shellt->charin(w->s, "%&"))
+                                break;
+                if(w == 0)
+                        target1 = wdup(head);
+        }
+        for(w = head; w; w = w->next)
+                addrule(w->s, tail, body, head, attr, hline, prog);
+}
+
+static int
+rhead(char *line, Word **h, Word **t, int *attr, char **prog)
+{
+        char *p;
+        char *pp;
+        int sep;
+        Rune r;
+        int n;
+        Word *w;
+
+        p = shellt->charin(line,":=<");
+        if(p == 0)
+                return('?');
+        sep = *p;
+        *p++ = 0;
+        if(sep == '<' && *p == '|'){
+                sep = '|';
+                p++;
+        }
+        *attr = 0;
+        *prog = 0;
+        if(sep == '='){
+                pp = shellt->charin(p, shellt->termchars);        /* termchars is shell-dependent */
+                if (pp && *pp == '=') {
+                        while (p != pp) {
+                                n = chartorune(&r, p);
+                                switch(r)
+                                {
+                                default:
+                                        SYNERR(-1);
+                                        fprint(2, "unknown attribute '%c'\n",*p);
+                                        Exit();
+                                case 'U':
+                                        *attr = 1;
+                                        break;
+                                }
+                                p += n;
+                        }
+                        p++;                /* skip trailing '=' */
+                }
+        }
+        if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){
+                while (*p) {
+                        n = chartorune(&r, p);
+                        if (r == ':')
+                                break;
+                        p += n;
+                        switch(r)
+                        {
+                        default:
+                                SYNERR(-1);
+                                fprint(2, "unknown attribute '%c'\n", p[-1]);
+                                Exit();
+                        case 'D':
+                                *attr |= DEL;
+                                break;
+                        case 'E':
+                                *attr |= NOMINUSE;
+                                break;
+                        case 'n':
+                                *attr |= NOVIRT;
+                                break;
+                        case 'N':
+                                *attr |= NOREC;
+                                break;
+                        case 'P':
+                                pp = utfrune(p, ':');
+                                if (pp == 0 || *pp == 0)
+                                        goto eos;
+                                *pp = 0;
+                                *prog = strdup(p);
+                                *pp = ':';
+                                p = pp;
+                                break;
+                        case 'Q':
+                                *attr |= QUIET;
+                                break;
+                        case 'R':
+                                *attr |= REGEXP;
+                                break;
+                        case 'U':
+                                *attr |= UPD;
+                                break;
+                        case 'V':
+                                *attr |= VIR;
+                                break;
+                        }
+                }
+                if (*p++ != ':') {
+        eos:
+                        SYNERR(-1);
+                        fprint(2, "missing trailing :\n");
+                        Exit();
+                }
+        }
+        *h = w = stow(line);
+        if(*w->s == 0 && sep != '<' && sep != '|' && sep != 'S') {
+                SYNERR(mkinline-1);
+                fprint(2, "no var on left side of assignment/rule\n");
+                Exit();
+        }
+        *t = stow(p);
+        return(sep);
+}
+
+static char *
+rbody(Biobuf *in)
+{
+        Bufblock *buf;
+        int r, lastr;
+        char *p;
+
+        lastr = '\n';
+        buf = newbuf();
+        for(;;){
+                r = Bgetrune(in);
+                if (r < 0)
+                        break;
+                if (lastr == '\n') {
+                        if (r == '#')
+                                rinsert(buf, r);
+                        else if (r != ' ' && r != '\t') {
+                                Bungetrune(in);
+                                break;
+                        }
+                } else
+                        rinsert(buf, r);
+                lastr = r;
+                if (r == '\n')
+                        mkinline++;
+        }
+        insert(buf, 0);
+        p = strdup(buf->start);
+        freebuf(buf);
+        return p;
+}
+
+struct input
+{
+        char *file;
+        int line;
+        struct input *next;
+};
+static struct input *inputs = 0;
+
+void
+ipush(void)
+{
+        struct input *in, *me;
+
+        me = (struct input *)Malloc(sizeof(*me));
+        me->file = infile;
+        me->line = mkinline;
+        me->next = 0;
+        if(inputs == 0)
+                inputs = me;
+        else {
+                for(in = inputs; in->next; )
+                        in = in->next;
+                in->next = me;
+        }
+}
+
+void
+ipop(void)
+{
+        struct input *in, *me;
+
+        assert("pop input list", inputs != 0);
+        if(inputs->next == 0){
+                me = inputs;
+                inputs = 0;
+        } else {
+                for(in = inputs; in->next->next; )
+                        in = in->next;
+                me = in->next;
+                in->next = 0;
+        }
+        infile = me->file;
+        mkinline = me->line;
+        free((char *)me);
+}
diff --git a/mk/rc.c b/mk/rc.c
@@ -0,0 +1,194 @@
+#include        "mk.h"
+
+/*
+ *        This file contains functions that depend on rc's syntax.  Most
+ *        of the routines extract strings observing rc's escape conventions
+ */
+
+
+/*
+ *        skip a token in single quotes.
+ */
+static char *
+squote(char *cp)
+{
+        Rune r;
+        int n;
+
+        while(*cp){
+                n = chartorune(&r, cp);
+                if(r == '\'') {
+                        n += chartorune(&r, cp+n);
+                        if(r != '\'')
+                                return(cp);
+                }
+                cp += n;
+        }
+        SYNERR(-1);                /* should never occur */
+        fprint(2, "missing closing '\n");
+        return 0;
+}
+
+/*
+ *        search a string for characters in a pattern set
+ *        characters in quotes and variable generators are escaped
+ */
+char *
+rccharin(char *cp, char *pat)
+{
+        Rune r;
+        int n, vargen;
+
+        vargen = 0;
+        while(*cp){
+                n = chartorune(&r, cp);
+                switch(r){
+                case '\'':                        /* skip quoted string */
+                        cp = squote(cp+1);        /* n must = 1 */
+                        if(!cp)
+                                return 0;
+                        break;
+                case '$':
+                        if(*(cp+1) == '{')
+                                vargen = 1;
+                        break;
+                case '}':
+                        if(vargen)
+                                vargen = 0;
+                        else if(utfrune(pat, r))
+                                return cp;
+                        break;
+                default:
+                        if(vargen == 0 && utfrune(pat, r))
+                                return cp;
+                        break;
+                }
+                cp += n;
+        }
+        if(vargen){
+                SYNERR(-1);
+                fprint(2, "missing closing } in pattern generator\n");
+        }
+        return 0;
+}
+
+/*
+ *        extract an escaped token.  Possible escape chars are single-quote,
+ *        double-quote,and backslash.  Only the first is valid for rc. the
+ *        others are just inserted into the receiving buffer.
+ */
+char*
+rcexpandquote(char *s, Rune r, Bufblock *b)
+{
+        if (r != '\'') {
+                rinsert(b, r);
+                return s;
+        }
+
+        while(*s){
+                s += chartorune(&r, s);
+                if(r == '\'') {
+                        if(*s == '\'')
+                                s++;
+                        else
+                                return s;
+                }
+                rinsert(b, r);
+        }
+        return 0;
+}
+
+/*
+ *        Input an escaped token.  Possible escape chars are single-quote,
+ *        double-quote and backslash.  Only the first is a valid escape for
+ *        rc; the others are just inserted into the receiving buffer.
+ */
+int
+rcescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+        int c, line;
+
+        if(esc != '\'')
+                return 1;
+
+        line = mkinline;
+        while((c = nextrune(bp, 0)) > 0){
+                if(c == '\''){
+                        if(preserve)
+                                rinsert(buf, c);
+                        c = Bgetrune(bp);
+                        if (c < 0)
+                                break;
+                        if(c != '\''){
+                                Bungetrune(bp);
+                                return 1;
+                        }
+                }
+                rinsert(buf, c);
+        }
+        SYNERR(line); fprint(2, "missing closing %c\n", esc);
+        return 0;
+}
+
+/*
+ *        copy a single-quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Bufblock *buf)
+{
+        Rune r;
+
+        while(*s){
+                s += chartorune(&r, s);
+                rinsert(buf, r);
+                if(r == '\'')
+                        break;
+        }
+        return s;
+}
+/*
+ *        check for quoted strings.  backquotes are handled here; single quotes above.
+ *        s points to char after opening quote, q.
+ */
+char *
+rccopyq(char *s, Rune q, Bufblock *buf)
+{
+        if(q == '\'')                                /* copy quoted string */
+                return copysingle(s, buf);
+
+        if(q != '`')                                /* not quoted */
+                return s;
+
+        while(*s){                                /* copy backquoted string */
+                s += chartorune(&q, s);
+                rinsert(buf, q);
+                if(q == '}')
+                        break;
+                if(q == '\'')
+                        s = copysingle(s, buf);        /* copy quoted string */
+        }
+        return s;
+}
+
+static int
+rcmatchname(char *name)
+{
+        char *p;
+
+        if((p = strrchr(name, '/')) != nil)
+                name = p+1;
+        if(name[0] == 'r' && name[1] == 'c')
+                return 1;
+        return 0;
+}
+
+Shell rcshell = {
+        "rc",
+        "'= \t",
+        '\1',
+        rccharin,
+        rcexpandquote,
+        rcescapetoken,
+        rccopyq,
+        rcmatchname
+};
diff --git a/mk/recipe.c b/mk/recipe.c
@@ -0,0 +1,117 @@
+#include        "mk.h"
+
+int
+dorecipe(Node *node)
+{
+        char buf[BIGBLOCK];
+        register Node *n;
+        Rule *r = 0;
+        Arc *a, *aa;
+        Word head, ahead, lp, ln, *w, *ww, *aw;
+        Symtab *s;
+        int did = 0;
+
+        aa = 0;
+        /*
+                pick up the rule
+        */
+        for(a = node->prereqs; a; a = a->next)
+                if(*a->r->recipe)
+                        r = (aa = a)->r;
+        /*
+                no recipe? go to buggery!
+        */
+        if(r == 0){
+                if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){
+                        fprint(2, "mk: no recipe to make '%s'\n", node->name);
+                        Exit();
+                }
+                if(strchr(node->name, '(') && node->time == 0)
+                        MADESET(node, MADE);
+                else
+                        update(0, node);
+                if(tflag){
+                        if(!(node->flags&VIRTUAL))
+                                touch(node->name);
+                        else if(explain)
+                                Bprint(&bout, "no touch of virtual '%s'\n", node->name);
+                }
+                return(did);
+        }
+        /*
+                build the node list
+        */
+        node->next = 0;
+        head.next = 0;
+        ww = &head;
+        ahead.next = 0;
+        aw = &ahead;
+        if(r->attr®EXP){
+                ww->next = newword(node->name);
+                aw->next = newword(node->name);
+        } else {
+                for(w = r->alltargets; w; w = w->next){
+                        if(r->attr&META)
+                                subst(aa->stem, w->s, buf);
+                        else
+                                strcpy(buf, w->s);
+                        aw->next = newword(buf);
+                        aw = aw->next;
+                        if((s = symlook(buf, S_NODE, 0)) == 0)
+                                continue;        /* not a node we are interested in */
+                        n = s->u.ptr;
+                        if(aflag == 0 && n->time) {
+                                for(a = n->prereqs; a; a = a->next)
+                                        if(a->n && outofdate(n, a, 0))
+                                                break;
+                                if(a == 0)
+                                        continue;
+                        }
+                        ww->next = newword(buf);
+                        ww = ww->next;
+                        if(n == node) continue;
+                        n->next = node->next;
+                        node->next = n;
+                }
+        }
+        for(n = node; n; n = n->next)
+                if((n->flags&READY) == 0)
+                        return(did);
+        /*
+                gather the params for the job
+        */
+        lp.next = ln.next = 0;
+        for(n = node; n; n = n->next){
+                for(a = n->prereqs; a; a = a->next){
+                        if(a->n){
+                                addw(&lp, a->n->name);
+                                if(outofdate(n, a, 0)){
+                                        addw(&ln, a->n->name);
+                                        if(explain)
+                                                fprint(1, "%s(%ld) < %s(%ld)\n",
+                                                        n->name, n->time, a->n->name, a->n->time);
+                                }
+                        } else {
+                                if(explain)
+                                        fprint(1, "%s has no prerequisites\n",
+                                                        n->name);
+                        }
+                }
+                MADESET(n, BEINGMADE);
+        }
+        /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*/
+        run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next));
+        return(1);
+}
+
+void
+addw(Word *w, char *s)
+{
+        Word *lw;
+
+        for(lw = w; w = w->next; lw = w){
+                if(strcmp(s, w->s) == 0)
+                        return;
+        }
+        lw->next = newword(s);
+}
diff --git a/mk/rule.c b/mk/rule.c
@@ -0,0 +1,112 @@
+#include        "mk.h"
+
+static Rule *lr, *lmr;
+static int rcmp(Rule *r, char *target, Word *tail);
+static int nrules = 0;
+
+void
+addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, char *prog)
+{
+        Rule *r;
+        Rule *rr;
+        Symtab *sym;
+        int reuse;
+
+        r = 0;
+        reuse = 0;
+        if(sym = symlook(head, S_TARGET, 0)){
+                for(r = sym->u.ptr; r; r = r->chain)
+                        if(rcmp(r, head, tail) == 0){
+                                reuse = 1;
+                                break;
+                        }
+        }
+        if(r == 0)
+                r = (Rule *)Malloc(sizeof(Rule));
+        r->shellt = shellt;
+        r->shellcmd = shellcmd;
+        r->target = head;
+        r->tail = tail;
+        r->recipe = body;
+        r->line = hline;
+        r->file = infile;
+        r->attr = attr;
+        r->alltargets = ahead;
+        r->prog = prog;
+        r->rule = nrules++;
+        if(!reuse){
+                rr = symlook(head, S_TARGET, (void *)r)->u.ptr;
+                if(rr != r){
+                        r->chain = rr->chain;
+                        rr->chain = r;
+                } else
+                        r->chain = 0;
+        }
+        if(!reuse)
+                r->next = 0;
+        if((attr®EXP) || shellt->charin(head, "%&")){
+                r->attr |= META;
+                if(reuse)
+                        return;
+                if(attr®EXP){
+                        patrule = r;
+                        r->pat = regcomp(head);
+                }
+                if(metarules == 0)
+                        metarules = lmr = r;
+                else {
+                        lmr->next = r;
+                        lmr = r;
+                }
+        } else {
+                if(reuse)
+                        return;
+                r->pat = 0;
+                if(rules == 0)
+                        rules = lr = r;
+                else {
+                        lr->next = r;
+                        lr = r;
+                }
+        }
+}
+
+void
+dumpr(char *s, Rule *r)
+{
+        if(r == nil)
+                return;
+        Bprint(&bout, "%s: start=%ld shelltype=%s shellcmd=%s\n", 
+                s, r, r->shellt->name, wtos(r->shellcmd, ' '));
+        for(; r; r = r->next){
+                Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'",
+                        r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' '));
+                if(r->prog)
+                        Bprint(&bout, " prog='%s'", r->prog);
+                Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' '));
+                Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe);
+        }
+}
+
+static int
+rcmp(Rule *r, char *target, Word *tail)
+{
+        Word *w;
+
+        if(strcmp(r->target, target))
+                return 1;
+        for(w = r->tail; w && tail; w = w->next, tail = tail->next)
+                if(strcmp(w->s, tail->s))
+                        return 1;
+        return(w || tail);
+}
+
+char *
+rulecnt(void)
+{
+        char *s;
+
+        s = Malloc(nrules);
+        memset(s, 0, nrules);
+        return(s);
+}
diff --git a/mk/run.c b/mk/run.c
@@ -0,0 +1,296 @@
+#include        "mk.h"
+
+typedef struct Event
+{
+        int pid;
+        Job *job;
+} Event;
+static Event *events;
+static int nevents, nrunning, nproclimit;
+
+typedef struct Process
+{
+        int pid;
+        int status;
+        struct Process *b, *f;
+} Process;
+static Process *phead, *pfree;
+static void sched(void);
+static void pnew(int, int), pdelete(Process *);
+
+int pidslot(int);
+
+void
+run(Job *j)
+{
+        Job *jj;
+
+        if(jobs){
+                for(jj = jobs; jj->next; jj = jj->next)
+                        ;
+                jj->next = j;
+        } else 
+                jobs = j;
+        j->next = 0;
+        /* this code also in waitup after parse redirect */
+        if(nrunning < nproclimit)
+                sched();
+}
+
+static void
+sched(void)
+{
+        char *flags;
+        Job *j;
+        Bufblock *buf;
+        int slot;
+        Node *n;
+        Envy *e;
+
+        if(jobs == 0){
+                usage();
+                return;
+        }
+        j = jobs;
+        jobs = j->next;
+        if(DEBUG(D_EXEC))
+                fprint(1, "firing up job for target %s\n", wtos(j->t, ' '));
+        slot = nextslot();
+        events[slot].job = j;
+        buf = newbuf();
+        e = buildenv(j, slot);
+        shprint(j->r->recipe, e, buf, j->r->shellt);
+        if(!tflag && (nflag || !(j->r->attr&QUIET)))
+                Bwrite(&bout, buf->start, (long)strlen(buf->start));
+        freebuf(buf);
+        if(nflag||tflag){
+                for(n = j->n; n; n = n->next){
+                        if(tflag){
+                                if(!(n->flags&VIRTUAL))
+                                        touch(n->name);
+                                else if(explain)
+                                        Bprint(&bout, "no touch of virtual '%s'\n", n->name);
+                        }
+                        n->time = time((long *)0);
+                        MADESET(n, MADE);
+                }
+        } else {
+                if(DEBUG(D_EXEC))
+                        fprint(1, "recipe='%s'", j->r->recipe);/**/
+                Bflush(&bout);
+                if(j->r->attr&NOMINUSE)
+                        flags = 0;
+                else
+                        flags = "-e";
+                events[slot].pid = execsh(flags, j->r->recipe, 0, e, j->r->shellt, j->r->shellcmd);
+                usage();
+                nrunning++;
+                if(DEBUG(D_EXEC))
+                        fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid);
+        }
+}
+
+int
+waitup(int echildok, int *retstatus)
+{
+        Envy *e;
+        int pid;
+        int slot;
+        Symtab *s;
+        Word *w;
+        Job *j;
+        char buf[ERRMAX];
+        Bufblock *bp;
+        int uarg = 0;
+        int done;
+        Node *n;
+        Process *p;
+        extern int runerrs;
+
+        /* first check against the proces slist */
+        if(retstatus)
+                for(p = phead; p; p = p->f)
+                        if(p->pid == *retstatus){
+                                *retstatus = p->status;
+                                pdelete(p);
+                                return(-1);
+                        }
+again:                /* rogue processes */
+        pid = waitfor(buf);
+        if(pid == -1){
+                if(echildok > 0)
+                        return(1);
+                else {
+                        fprint(2, "mk: (waitup %d): %r\n", echildok);
+                        Exit();
+                }
+        }
+        if(DEBUG(D_EXEC))
+                fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf);
+        if(retstatus && pid == *retstatus){
+                *retstatus = buf[0]? 1:0;
+                return(-1);
+        }
+        slot = pidslot(pid);
+        if(slot < 0){
+                if(DEBUG(D_EXEC))
+                        fprint(2, "mk: wait returned unexpected process %d\n", pid);
+                pnew(pid, buf[0]? 1:0);
+                goto again;
+        }
+        j = events[slot].job;
+        usage();
+        nrunning--;
+        events[slot].pid = -1;
+        if(buf[0]){
+                e = buildenv(j, slot);
+                bp = newbuf();
+                shprint(j->r->recipe, e, bp, j->r->shellt);
+                front(bp->start);
+                fprint(2, "mk: %s: exit status=%s", bp->start, buf);
+                freebuf(bp);
+                for(n = j->n, done = 0; n; n = n->next)
+                        if(n->flags&DELETE){
+                                if(done++ == 0)
+                                        fprint(2, ", deleting");
+                                fprint(2, " '%s'", n->name);
+                                delete(n->name);
+                        }
+                fprint(2, "\n");
+                if(kflag){
+                        runerrs++;
+                        uarg = 1;
+                } else {
+                        jobs = 0;
+                        Exit();
+                }
+        }
+        for(w = j->t; w; w = w->next){
+                if((s = symlook(w->s, S_NODE, 0)) == 0)
+                        continue;        /* not interested in this node */
+                update(uarg, s->u.ptr);
+        }
+        if(nrunning < nproclimit)
+                sched();
+        return(0);
+}
+
+void
+nproc(void)
+{
+        Symtab *sym;
+        Word *w;
+
+        if(sym = symlook("NPROC", S_VAR, 0)) {
+                w = sym->u.ptr;
+                if (w && w->s && w->s[0])
+                        nproclimit = atoi(w->s);
+        }
+        if(nproclimit < 1)
+                nproclimit = 1;
+        if(DEBUG(D_EXEC))
+                fprint(1, "nprocs = %d\n", nproclimit);
+        if(nproclimit > nevents){
+                if(nevents)
+                        events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event));
+                else
+                        events = (Event *)Malloc(nproclimit*sizeof(Event));
+                while(nevents < nproclimit)
+                        events[nevents++].pid = 0;
+        }
+}
+
+int
+nextslot(void)
+{
+        int i;
+
+        for(i = 0; i < nproclimit; i++)
+                if(events[i].pid <= 0) return i;
+        assert("out of slots!!", 0);
+        return 0;        /* cyntax */
+}
+
+int
+pidslot(int pid)
+{
+        int i;
+
+        for(i = 0; i < nevents; i++)
+                if(events[i].pid == pid) return(i);
+        if(DEBUG(D_EXEC))
+                fprint(2, "mk: wait returned unexpected process %d\n", pid);
+        return(-1);
+}
+
+
+static void
+pnew(int pid, int status)
+{
+        Process *p;
+
+        if(pfree){
+                p = pfree;
+                pfree = p->f;
+        } else
+                p = (Process *)Malloc(sizeof(Process));
+        p->pid = pid;
+        p->status = status;
+        p->f = phead;
+        phead = p;
+        if(p->f)
+                p->f->b = p;
+        p->b = 0;
+}
+
+static void
+pdelete(Process *p)
+{
+        if(p->f)
+                p->f->b = p->b;
+        if(p->b)
+                p->b->f = p->f;
+        else
+                phead = p->f;
+        p->f = pfree;
+        pfree = p;
+}
+
+void
+killchildren(char *msg)
+{
+        Process *p;
+
+        kflag = 1;        /* to make sure waitup doesn't exit */
+        jobs = 0;        /* make sure no more get scheduled */
+        for(p = phead; p; p = p->f)
+                expunge(p->pid, msg);
+        while(waitup(1, (int *)0) == 0)
+                ;
+        Bprint(&bout, "mk: %s\n", msg);
+        Exit();
+}
+
+static long tslot[1000];
+static long tick;
+
+void
+usage(void)
+{
+        long t;
+
+        time(&t);
+        if(tick)
+                tslot[nrunning] += (t-tick);
+        tick = t;
+}
+
+void
+prusage(void)
+{
+        int i;
+
+        usage();
+        for(i = 0; i <= nevents; i++)
+                fprint(1, "%d: %ld\n", i, tslot[i]);
+}
diff --git a/mk/sh.c b/mk/sh.c
@@ -0,0 +1,206 @@
+#include        "mk.h"
+
+/*
+ *        This file contains functions that depend on the shell's syntax.  Most
+ *        of the routines extract strings observing the shell's escape conventions.
+ */
+
+
+/*
+ *        skip a token in quotes.
+ */
+static char *
+squote(char *cp, int c)
+{
+        Rune r;
+        int n;
+
+        while(*cp){
+                n = chartorune(&r, cp);
+                if(r == c)
+                        return cp;
+                if(r == '\\')
+                        n += chartorune(&r, cp+n);
+                cp += n;
+        }
+        SYNERR(-1);                /* should never occur */
+        fprint(2, "missing closing '\n");
+        return 0;
+}
+/*
+ *        search a string for unescaped characters in a pattern set
+ */
+static char *
+shcharin(char *cp, char *pat)
+{
+        Rune r;
+        int n, vargen;
+
+        vargen = 0;
+        while(*cp){
+                n = chartorune(&r, cp);
+                switch(r){
+                case '\\':                        /* skip escaped char */
+                        cp += n;
+                        n = chartorune(&r, cp);
+                        break;
+                case '\'':                        /* skip quoted string */
+                case '"':
+                        cp = squote(cp+1, r);        /* n must = 1 */
+                        if(!cp)
+                                return 0;
+                        break;
+                case '$':
+                        if(*(cp+1) == '{')
+                                vargen = 1;
+                        break;
+                case '}':
+                        if(vargen)
+                                vargen = 0;
+                        else if(utfrune(pat, r))
+                                return cp;
+                        break;
+                default:
+                        if(vargen == 0 && utfrune(pat, r))
+                                return cp;
+                        break;
+                }
+                cp += n;
+        }
+        if(vargen){
+                SYNERR(-1);
+                fprint(2, "missing closing } in pattern generator\n");
+        }
+        return 0;
+}
+
+/*
+ *        extract an escaped token.  Possible escape chars are single-quote,
+ *        double-quote,and backslash.
+ */
+static char*
+shexpandquote(char *s, Rune esc, Bufblock *b)
+{
+        Rune r;
+
+        if (esc == '\\') {
+                s += chartorune(&r, s);
+                rinsert(b, r);
+                return s;
+        }
+
+        while(*s){
+                s += chartorune(&r, s);
+                if(r == esc)
+                        return s;
+                if (r == '\\') {
+                        rinsert(b, r);
+                        s += chartorune(&r, s);
+                }
+                rinsert(b, r);
+        }
+        return 0;
+}
+
+/*
+ *        Input an escaped token.  Possible escape chars are single-quote,
+ *        double-quote and backslash.
+ */
+static int
+shescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+        int c, line;
+
+        if(esc == '\\') {
+                c = Bgetrune(bp);
+                if(c == '\r')
+                        c = Bgetrune(bp);
+                if (c == '\n')
+                        mkinline++;
+                rinsert(buf, c);
+                return 1;
+        }
+
+        line = mkinline;
+        while((c = nextrune(bp, 0)) >= 0){
+                if(c == esc){
+                        if(preserve)
+                                rinsert(buf, c);
+                        return 1;
+                }
+                if(c == '\\') {
+                        rinsert(buf, c);
+                        c = Bgetrune(bp);
+                        if(c == '\r')
+                                c = Bgetrune(bp);
+                        if (c < 0)
+                                break;
+                        if (c == '\n')
+                                mkinline++;
+                }
+                rinsert(buf, c);
+        }
+        SYNERR(line); fprint(2, "missing closing %c\n", esc);
+        return 0;
+}
+
+/*
+ *        copy a quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Rune q, Bufblock *buf)
+{
+        Rune r;
+
+        while(*s){
+                s += chartorune(&r, s);
+                rinsert(buf, r);
+                if(r == q)
+                        break;
+        }
+        return s;
+}
+/*
+ *        check for quoted strings.  backquotes are handled here; single quotes above.
+ *        s points to char after opening quote, q.
+ */
+static char *
+shcopyq(char *s, Rune q, Bufblock *buf)
+{
+        if(q == '\'' || q == '"')                /* copy quoted string */
+                return copysingle(s, q, buf);
+
+        if(q != '`')                                /* not quoted */
+                return s;
+
+        while(*s){                                /* copy backquoted string */
+                s += chartorune(&q, s);
+                rinsert(buf, q);
+                if(q == '`')
+                        break;
+                if(q == '\'' || q == '"')
+                        s = copysingle(s, q, buf);        /* copy quoted string */
+        }
+        return s;
+}
+
+static int
+shmatchname(char *name)
+{
+        USED(name);
+
+        return 1;
+}
+
+
+Shell shshell = {
+        "sh",
+        "\"'= \t",        /*used in parse.c to isolate assignment attribute*/
+        ' ',        /* inter-word separator in env */
+        shcharin,
+        shexpandquote,
+        shescapetoken,
+        shcopyq,
+        shmatchname
+};
+
diff --git a/mk/shell.c b/mk/shell.c
@@ -0,0 +1,80 @@
+#include "mk.h"
+
+static Shell *shells[] = {
+        &rcshell,
+        &shshell
+};
+
+Shell *shelldefault = &shshell;
+
+Shell *shellt;
+Word *shellcmd;
+
+typedef struct Shellstack Shellstack;
+struct Shellstack
+{
+        Shell *t;
+        Word *w;
+        Shellstack *next;
+};
+
+Shellstack *shellstack;
+
+char*
+setshell(Word *w)
+{
+        int i;
+
+        if(w->s == nil)
+                return "shell name not found on line";
+
+        for(i=0; imatchname(w->s))
+                        break;
+        if(i == nelem(shells))
+                return "cannot determine shell type";
+        shellt = shells[i];
+        shellcmd = w;
+        return nil;
+}
+
+void
+initshell(void)
+{
+        shellcmd = stow(shelldefault->name);
+        shellt = shelldefault;
+        setvar("MKSHELL", shellcmd);
+}
+
+void
+pushshell(void)
+{
+        Shellstack *s;
+
+        /* save */
+        s = Malloc(sizeof *s);
+        s->t = shellt;
+        s->w = shellcmd;
+        s->next = shellstack;
+        shellstack = s;
+
+        initshell();        /* reset to defaults */
+}
+
+void
+popshell(void)
+{
+        Shellstack *s;
+
+        if(shellstack == nil){
+                fprint(2, "internal shellstack error\n");
+                Exit();
+        }
+
+        s = shellstack;
+        shellstack = s->next;
+        shellt = s->t;
+        shellcmd = s->w;
+        setvar("MKSHELL", shellcmd);
+        free(s);
+}
diff --git a/mk/shprint.c b/mk/shprint.c
@@ -0,0 +1,125 @@
+#include        "mk.h"
+
+static char *vexpand(char*, Envy*, Bufblock*);
+
+#define getfields mkgetfields
+
+static int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+        Rune r;
+        int nr, intok, narg;
+
+        if(max <= 0)
+                return 0;
+
+        narg = 0;
+        args[narg] = str;
+        if(!mflag)
+                narg++;
+        intok = 0;
+        for(;; str += nr) {
+                nr = chartorune(&r, str);
+                if(r == 0)
+                        break;
+                if(utfrune(set, r)) {
+                        if(narg >= max)
+                                break;
+                        *str = 0;
+                        intok = 0;
+                        args[narg] = str + nr;
+                        if(!mflag)
+                                narg++;
+                } else {
+                        if(!intok && mflag)
+                                narg++;
+                        intok = 1;
+                }
+        }
+        return narg;
+}
+
+void
+shprint(char *s, Envy *env, Bufblock *buf, Shell *sh)
+{
+        int n;
+        Rune r;
+
+        while(*s) {
+                n = chartorune(&r, s);
+                if (r == '$')
+                        s = vexpand(s, env, buf);
+                else {
+                        rinsert(buf, r);
+                        s += n;
+                        s = sh->copyq(s, r, buf);        /*handle quoted strings*/
+                }
+        }
+        insert(buf, 0);
+}
+
+static char *
+mygetenv(char *name, Envy *env)
+{
+        if (!env)
+                return 0;
+        if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == 0)
+                return 0;
+                /* only resolve internal variables and variables we've set */
+        for(; env->name; env++){
+                if (strcmp(env->name, name) == 0)
+                        return wtos(env->values, ' ');
+        }
+        return 0;
+}
+
+static char *
+vexpand(char *w, Envy *env, Bufblock *buf)
+{
+        char *s, carry, *p, *q;
+
+        assert("vexpand no $", *w == '$');
+        p = w+1;        /* skip dollar sign */
+        if(*p == '{') {
+                p++;
+                q = utfrune(p, '}');
+                if (!q)
+                        q = strchr(p, 0);
+        } else
+                q = shname(p);
+        carry = *q;
+        *q = 0;
+        s = mygetenv(p, env);
+        *q = carry;
+        if (carry == '}')
+                q++;
+        if (s) {
+                bufcpy(buf, s, strlen(s));
+                free(s);
+        } else                 /* copy name intact*/
+                bufcpy(buf, w, q-w);
+        return(q);
+}
+
+void
+front(char *s)
+{
+        char *t, *q;
+        int i, j;
+        char *flds[512];
+
+        q = strdup(s);
+        i = getfields(q, flds, 512, 0, " \t\n");
+        if(i > 5){
+                flds[4] = flds[i-1];
+                flds[3] = "...";
+                i = 5;
+        }
+        t = s;
+        for(j = 0; j < i; j++){
+                for(s = flds[j]; *s; *t++ = *s++);
+                *t++ = ' ';
+        }
+        *t = 0;
+        free(q);
+}
diff --git a/mk/symtab.c b/mk/symtab.c
@@ -0,0 +1,97 @@
+#include        "mk.h"
+
+#define        NHASH        4099
+#define        HASHMUL        79L        /* this is a good value */
+static Symtab *hash[NHASH];
+
+void
+syminit(void)
+{
+        Symtab **s, *ss, *next;
+
+        for(s = hash; s < &hash[NHASH]; s++){
+                for(ss = *s; ss; ss = next){
+                        next = ss->next;
+                        free((char *)ss);
+                }
+                *s = 0;
+        }
+}
+
+Symtab *
+symlook(char *sym, int space, void *install)
+{
+        long h;
+        char *p;
+        Symtab *s;
+
+        for(p = sym, h = space; *p; h += *p++)
+                h *= HASHMUL;
+        if(h < 0)
+                h = ~h;
+        h %= NHASH;
+        for(s = hash[h]; s; s = s->next)
+                if((s->space == space) && (strcmp(s->name, sym) == 0))
+                        return(s);
+        if(install == 0)
+                return(0);
+        s = (Symtab *)Malloc(sizeof(Symtab));
+        s->space = space;
+        s->name = sym;
+        s->u.ptr = install;
+        s->next = hash[h];
+        hash[h] = s;
+        return(s);
+}
+
+void
+symdel(char *sym, int space)
+{
+        long h;
+        char *p;
+        Symtab *s, *ls;
+
+        /* multiple memory leaks */
+
+        for(p = sym, h = space; *p; h += *p++)
+                h *= HASHMUL;
+        if(h < 0)
+                h = ~h;
+        h %= NHASH;
+        for(s = hash[h], ls = 0; s; ls = s, s = s->next)
+                if((s->space == space) && (strcmp(s->name, sym) == 0)){
+                        if(ls)
+                                ls->next = s->next;
+                        else
+                                hash[h] = s->next;
+                        free((char *)s);
+                }
+}
+
+void
+symtraverse(int space, void (*fn)(Symtab*))
+{
+        Symtab **s, *ss;
+
+        for(s = hash; s < &hash[NHASH]; s++)
+                for(ss = *s; ss; ss = ss->next)
+                        if(ss->space == space)
+                                (*fn)(ss);
+}
+
+void
+symstat(void)
+{
+        Symtab **s, *ss;
+        int n;
+        int l[1000];
+
+        memset((char *)l, 0, sizeof(l));
+        for(s = hash; s < &hash[NHASH]; s++){
+                for(ss = *s, n = 0; ss; ss = ss->next)
+                        n++;
+                l[n]++;
+        }
+        for(n = 0; n < 1000; n++)
+                if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n);
+}
diff --git a/mk/sys.h b/mk/sys.h
@@ -0,0 +1,5 @@
+#include 
+#include 
+#include 
+#include 
+
diff --git a/mk/sys.std.h b/mk/sys.std.h
@@ -0,0 +1,27 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OREAD                O_RDONLY
+#define OWRITE        O_WRONLY
+#define ORDWR        O_RDWR
+#define nil 0
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#define seek lseek
+#define remove unlink
+#define exits(x)        exit(x && *(char*)x ? 1 : 0)
+#define USED(x)        if(x){}else
+#define create(name, mode, perm)        open(name, mode|O_CREAT, perm)
+#define ERRMAX        256
+
+typedef uintptr_t uintptr;
+#define uchar mk_uchar
+typedef unsigned char uchar;
diff --git a/mk/unix.c b/mk/unix.c
@@ -0,0 +1,341 @@
+#define NOPLAN9DEFINES
+#include        "mk.h"
+#include        
+#include        
+#include        
+#include        
+
+char        *shell = "/bin/sh";
+char        *shellname = "sh";
+
+extern char **environ;
+
+static void
+mkperror(char *s)
+{
+        fprint(2, "%s: %r\n", s);
+}
+
+void
+readenv(void)
+{
+        char **p, *s;
+        Word *w;
+
+        for(p = environ; *p; p++){
+/* rsc 5/5/2004 -- This misparses fn#cd={whatever} 
+                s = shname(*p);
+                if(*s == '=') {
+                        *s = 0;
+                        w = newword(s+1);
+                } else
+                        w = newword("");
+*/
+                s = strchr(*p, '=');
+                if(s){
+                        *s = 0;
+                        w = newword(s+1);
+                } else
+                        w = newword("");
+                if (symlook(*p, S_INTERNAL, 0))
+                        continue;
+                s = strdup(*p);
+                setvar(s, (void *)w);
+                symlook(s, S_EXPORTED, (void*)"")->u.ptr = "";
+        }
+}
+
+/*
+ *        done on child side of fork, so parent's env is not affected
+ *        and we don't care about freeing memory because we're going
+ *        to exec immediately after this.
+ */
+void
+exportenv(Envy *e, Shell *sh)
+{
+        int i;
+        char **p;
+        static char buf[16384];
+
+        p = 0;
+        for(i = 0; e->name; e++, i++) {
+                p = (char**) Realloc(p, (i+2)*sizeof(char*));
+                if(e->values)
+                        snprint(buf, sizeof buf, "%s=%s", e->name,  wtos(e->values, sh->iws));
+                else
+                        snprint(buf, sizeof buf, "%s=", e->name);
+                p[i] = strdup(buf);
+        }
+        p[i] = 0;
+        environ = p;
+}
+
+int
+waitfor(char *msg)
+{
+        int status;
+        int pid;
+
+        *msg = 0;
+        pid = wait(&status);
+        if(pid > 0) {
+                if(status&0x7f) {
+                        if(status&0x80)
+                                snprint(msg, ERRMAX, "signal %d, core dumped", status&0x7f);
+                        else
+                                snprint(msg, ERRMAX, "signal %d", status&0x7f);
+                } else if(status&0xff00)
+                        snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff);
+        }
+        return pid;
+}
+
+void
+expunge(int pid, char *msg)
+{
+        if(strcmp(msg, "interrupt"))
+                kill(pid, SIGINT);
+        else
+                kill(pid, SIGHUP);
+}
+
+int mypid;
+
+int
+shargv(Word *cmd, int extra, char ***pargv)
+{
+        char **argv;
+        int i, n;
+        Word *w;
+
+        n = 0;
+        for(w=cmd; w; w=w->next)
+                n++;
+        
+        argv = Malloc((n+extra+1)*sizeof(argv[0]));
+        i = 0;
+        for(w=cmd; w; w=w->next)
+                argv[i++] = w->s;
+        argv[n] = 0;
+        *pargv = argv;
+        return n;
+}        
+
+int
+execsh(char *args, char *cmd, Bufblock *buf, Envy *e, Shell *sh, Word *shellcmd)
+{
+        char *p, **argv;
+        int tot, n, pid, in[2], out[2];
+
+        if(buf && pipe(out) < 0){
+                mkperror("pipe");
+                Exit();
+        }
+        pid = fork();
+        mypid = getpid();
+        if(pid < 0){
+                mkperror("mk fork");
+                Exit();
+        }
+        if(pid == 0){
+                if(buf)
+                        close(out[0]);
+                if(pipe(in) < 0){
+                        mkperror("pipe");
+                        Exit();
+                }
+                pid = fork();
+                if(pid < 0){
+                        mkperror("mk fork");
+                        Exit();
+                }
+                if(pid != 0){
+                        dup2(in[0], 0);
+                        if(buf){
+                                dup2(out[1], 1);
+                                close(out[1]);
+                        }
+                        close(in[0]);
+                        close(in[1]);
+                        if (e)
+                                exportenv(e, sh);
+                        n = shargv(shellcmd, 1, &argv);
+                        argv[n++] = args;
+                        argv[n] = 0;
+                        execvp(argv[0], argv);
+                        mkperror(shell);
+                        _exit(1);
+                }
+                close(out[1]);
+                close(in[0]);
+                if(DEBUG(D_EXEC))
+                        fprint(1, "starting: %s\n", cmd);
+                p = cmd+strlen(cmd);
+                while(cmd < p){
+                        n = write(in[1], cmd, p-cmd);
+                        if(n < 0)
+                                break;
+                        cmd += n;
+                }
+                close(in[1]);
+                _exit(0);
+        }
+        if(buf){
+                close(out[1]);
+                tot = 0;
+                for(;;){
+                        if (buf->current >= buf->end)
+                                growbuf(buf);
+                        n = read(out[0], buf->current, buf->end-buf->current);
+                        if(n <= 0)
+                                break;
+                        buf->current += n;
+                        tot += n;
+                }
+                if (tot && buf->current[-1] == '\n')
+                        buf->current--;
+                close(out[0]);
+        }
+        return pid;
+}
+
+int
+pipecmd(char *cmd, Envy *e, int *fd, Shell *sh, Word *shellcmd)
+{
+        int pid, pfd[2];
+        int n;
+        char **argv;
+
+        if(DEBUG(D_EXEC))
+                fprint(1, "pipecmd='%s'\n", cmd);/**/
+
+        if(fd && pipe(pfd) < 0){
+                mkperror("pipe");
+                Exit();
+        }
+        pid = fork();
+        if(pid < 0){
+                mkperror("mk fork");
+                Exit();
+        }
+        if(pid == 0){
+                if(fd){
+                        close(pfd[0]);
+                        dup2(pfd[1], 1);
+                        close(pfd[1]);
+                }
+                if(e)
+                        exportenv(e, sh);
+                n = shargv(shellcmd, 2, &argv);
+                argv[n++] = "-c";
+                argv[n++] = cmd;
+                argv[n] = 0;
+                execvp(argv[0], argv);
+                mkperror(shell);
+                _exit(1);
+        }
+        if(fd){
+                close(pfd[1]);
+                *fd = pfd[0];
+        }
+        return pid;
+}
+
+void
+Exit(void)
+{
+        while(wait(0) >= 0)
+                ;
+        exits("error");
+}
+
+static        struct
+{
+        int        sig;
+        char        *msg;
+}        sigmsgs[] =
+{
+        SIGALRM,        "alarm",
+        SIGFPE,                "sys: fp: fptrap",
+        SIGPIPE,        "sys: write on closed pipe",
+        SIGILL,                "sys: trap: illegal instruction",
+/*        SIGSEGV,        "sys: segmentation violation", */
+        0,                0
+};
+
+static void
+notifyf(int sig)
+{
+        int i;
+
+        for(i = 0; sigmsgs[i].msg; i++)
+                if(sigmsgs[i].sig == sig)
+                        killchildren(sigmsgs[i].msg);
+
+        /* should never happen */
+        signal(sig, SIG_DFL);
+        kill(getpid(), sig);
+}
+
+void
+catchnotes(void)
+{
+        int i;
+
+        for(i = 0; sigmsgs[i].msg; i++)
+                signal(sigmsgs[i].sig, notifyf);
+}
+
+char*
+maketmp(int *pfd)
+{
+        static char temp[] = "/tmp/mkargXXXXXX";
+        static char buf[100];
+        int fd;
+
+        strcpy(buf, temp);
+        fd = mkstemp(buf);
+        if(fd < 0)
+                return 0;
+        *pfd = fd;
+        return buf;
+}
+
+int
+chgtime(char *name)
+{
+        if(access(name, 0) >= 0)
+                return utimes(name, 0);
+        return close(creat(name, 0666));
+}
+
+void
+rcopy(char **to, Resub *match, int n)
+{
+        int c;
+        char *p;
+
+        *to = match->s.sp;                /* stem0 matches complete target */
+        for(to++, match++; --n > 0; to++, match++){
+                if(match->s.sp && match->e.ep){
+                        p = match->e.ep;
+                        c = *p;
+                        *p = 0;
+                        *to = strdup(match->s.sp);
+                        *p = c;
+                }
+                else
+                        *to = 0;
+        }
+}
+
+unsigned long
+mkmtime(char *name)
+{
+        struct stat st;
+
+        if(stat(name, &st) < 0)
+                return 0;
+
+        return st.st_mtime;
+}
diff --git a/mk/var.c b/mk/var.c
@@ -0,0 +1,41 @@
+#include        "mk.h"
+
+void
+setvar(char *name, void *ptr)
+{
+        symlook(name, S_VAR, ptr)->u.ptr = ptr;
+        symlook(name, S_MAKEVAR, (void*)"");
+}
+
+static void
+print1(Symtab *s)
+{
+        Word *w;
+
+        Bprint(&bout, "\t%s=", s->name);
+        for (w = s->u.ptr; w; w = w->next)
+                Bprint(&bout, "'%s'", w->s);
+        Bprint(&bout, "\n");
+}
+
+void
+dumpv(char *s)
+{
+        Bprint(&bout, "%s:\n", s);
+        symtraverse(S_VAR, print1);
+}
+
+char *
+shname(char *a)
+{
+        Rune r;
+        int n;
+
+        while (*a) {
+                n = chartorune(&r, a);
+                if (!WORDCHR(r))
+                        break;
+                a += n;
+        }
+        return a;
+}
diff --git a/mk/varsub.c b/mk/varsub.c
@@ -0,0 +1,252 @@
+#include        "mk.h"
+
+static        Word                *subsub(Word*, char*, char*);
+static        Word                *expandvar(char**);
+static        Bufblock        *varname(char**);
+static        Word                *extractpat(char*, char**, char*, char*);
+static        int                submatch(char*, Word*, Word*, int*, char**);
+static        Word                *varmatch(char *);
+
+Word *
+varsub(char **s)
+{
+        Bufblock *b;
+        Word *w;
+
+        if(**s == '{')                /* either ${name} or ${name: A%B==C%D}*/
+                return expandvar(s);
+
+        b = varname(s);
+        if(b == 0)
+                return 0;
+
+        w = varmatch(b->start);
+        freebuf(b);
+        return w;
+}
+
+/*
+ *        extract a variable name
+ */
+static Bufblock*
+varname(char **s)
+{
+        Bufblock *b;
+        char *cp;
+        Rune r;
+        int n;
+
+        b = newbuf();
+        cp = *s;
+        for(;;){
+                n = chartorune(&r, cp);
+                if (!WORDCHR(r))
+                        break;
+                rinsert(b, r);
+                cp += n;
+        }
+        if (b->current == b->start){
+                SYNERR(-1);
+                fprint(2, "missing variable name <%s>\n", *s);
+                freebuf(b);
+                return 0;
+        }
+        *s = cp;
+        insert(b, 0);
+        return b;
+}
+
+static Word*
+varmatch(char *name)
+{
+        Word *w;
+        Symtab *sym;
+        
+        sym = symlook(name, S_VAR, 0);
+        if(sym){
+                        /* check for at least one non-NULL value */
+                for (w = sym->u.ptr; w; w = w->next)
+                        if(w->s && *w->s)
+                                return wdup(w);
+        }
+        return 0;
+}
+
+static Word*
+expandvar(char **s)
+{
+        Word *w;
+        Bufblock *buf;
+        Symtab *sym;
+        char *cp, *begin, *end;
+
+        begin = *s;
+        (*s)++;                                                /* skip the '{' */
+        buf = varname(s);
+        if (buf == 0)
+                return 0;
+        cp = *s;
+        if (*cp == '}') {                                /* ${name} variant*/
+                (*s)++;                                        /* skip the '}' */
+                w = varmatch(buf->start);
+                freebuf(buf);
+                return w;
+        }
+        if (*cp != ':') {
+                SYNERR(-1);
+                fprint(2, "bad variable name <%s>\n", buf->start);
+                freebuf(buf);
+                return 0;
+        }
+        cp++;
+        end = shellt->charin(cp , "}");
+        if(end == 0){
+                SYNERR(-1);
+                fprint(2, "missing '}': %s\n", begin);
+                Exit();
+        }
+        *end = 0;
+        *s = end+1;
+        
+        sym = symlook(buf->start, S_VAR, 0);
+        if(sym == 0 || sym->u.ptr == 0)
+                w = newword(buf->start);
+        else
+                w = subsub(sym->u.ptr, cp, end);
+        freebuf(buf);
+        return w;
+}
+
+static Word*
+extractpat(char *s, char **r, char *term, char *end)
+{
+        int save;
+        char *cp;
+        Word *w;
+
+        cp = shellt->charin(s, term);
+        if(cp){
+                *r = cp;
+                if(cp == s)
+                        return 0;
+                save = *cp;
+                *cp = 0;
+                w = stow(s);
+                *cp = save;
+        } else {
+                *r = end;
+                w = stow(s);
+        }
+        return w;
+}
+
+static Word*
+subsub(Word *v, char *s, char *end)
+{
+        int nmid;
+        Word *head, *tail, *w, *h;
+        Word *a, *b, *c, *d;
+        Bufblock *buf;
+        char *cp, *enda;
+
+        a = extractpat(s, &cp, "=%&", end);
+        b = c = d = 0;
+        if(PERCENT(*cp))
+                b = extractpat(cp+1, &cp, "=", end);
+        if(*cp == '=')
+                c = extractpat(cp+1, &cp, "&%", end);
+        if(PERCENT(*cp))
+                d = stow(cp+1);
+        else if(*cp)
+                d = stow(cp);
+
+        head = tail = 0;
+        buf = newbuf();
+        for(; v; v = v->next){
+                h = w = 0;
+                if(submatch(v->s, a, b, &nmid, &enda)){
+                        /* enda points to end of A match in source;
+                         * nmid = number of chars between end of A and start of B
+                         */
+                        if(c){
+                                h = w = wdup(c);
+                                while(w->next)
+                                        w = w->next;
+                        }
+                        if(PERCENT(*cp) && nmid > 0){        
+                                if(w){
+                                        bufcpy(buf, w->s, strlen(w->s));
+                                        bufcpy(buf, enda, nmid);
+                                        insert(buf, 0);
+                                        free(w->s);
+                                        w->s = strdup(buf->start);
+                                } else {
+                                        bufcpy(buf, enda, nmid);
+                                        insert(buf, 0);
+                                        h = w = newword(buf->start);
+                                }
+                                buf->current = buf->start;
+                        }
+                        if(d && *d->s){
+                                if(w){
+
+                                        bufcpy(buf, w->s, strlen(w->s));
+                                        bufcpy(buf, d->s, strlen(d->s));
+                                        insert(buf, 0);
+                                        free(w->s);
+                                        w->s = strdup(buf->start);
+                                        w->next = wdup(d->next);
+                                        while(w->next)
+                                                w = w->next;
+                                        buf->current = buf->start;
+                                } else
+                                        h = w = wdup(d);
+                        }
+                }
+                if(w == 0)
+                        h = w = newword(v->s);
+        
+                if(head == 0)
+                        head = h;
+                else
+                        tail->next = h;
+                tail = w;
+        }
+        freebuf(buf);
+        delword(a);
+        delword(b);
+        delword(c);
+        delword(d);
+        return head;
+}
+
+static int
+submatch(char *s, Word *a, Word *b, int *nmid, char **enda)
+{
+        Word *w;
+        int n;
+        char *end;
+
+        n = 0;
+        for(w = a; w; w = w->next){
+                n = strlen(w->s);
+                if(strncmp(s, w->s, n) == 0)
+                        break;
+        }
+        if(a && w == 0)                /*  a == NULL matches everything*/
+                return 0;
+
+        *enda = s+n;                /* pointer to end a A part match */
+        *nmid = strlen(s)-n;        /* size of remainder of source */
+        end = *enda+*nmid;
+        for(w = b; w; w = w->next){
+                n = strlen(w->s);
+                if(strcmp(w->s, end-n) == 0){
+                        *nmid -= n;
+                        break;
+                }
+        }
+        if(b && w == 0)                /* b == NULL matches everything */
+                return 0;
+        return 1;
+}
diff --git a/mk/word.c b/mk/word.c
@@ -0,0 +1,189 @@
+#include        "mk.h"
+
+static        Word        *nextword(char**);
+
+Word*
+newword(char *s)
+{
+        Word *w;
+
+        w = (Word *)Malloc(sizeof(Word));
+        w->s = strdup(s);
+        w->next = 0;
+        return(w);
+}
+
+Word *
+stow(char *s)
+{
+        Word *head, *w, *new;
+
+        w = head = 0;
+        while(*s){
+                new = nextword(&s);
+                if(new == 0)
+                        break;
+                if (w)
+                        w->next = new;
+                else
+                        head = w = new;
+                while(w->next)
+                        w = w->next;
+                
+        }
+        if (!head)
+                head = newword("");
+        return(head);
+}
+
+char *
+wtos(Word *w, int sep)
+{
+        Bufblock *buf;
+        char *cp;
+
+        buf = newbuf();
+        for(; w; w = w->next){
+                for(cp = w->s; *cp; cp++)
+                        insert(buf, *cp);
+                if(w->next)
+                        insert(buf, sep);
+        }
+        insert(buf, 0);
+        cp = strdup(buf->start);
+        freebuf(buf);
+        return(cp);
+}
+
+Word*
+wdup(Word *w)
+{
+        Word *v, *new, *base;
+
+        v = base = 0;
+        while(w){
+                new = newword(w->s);
+                if(v)
+                        v->next = new;
+                else
+                        base = new;
+                v = new;
+                w = w->next;
+        }
+        return base;
+}
+
+void
+delword(Word *w)
+{
+        Word *v;
+
+        while(v = w){
+                w = w->next;
+                if(v->s)
+                        free(v->s);
+                free(v);
+        }
+}
+
+/*
+ *        break out a word from a string handling quotes, executions,
+ *        and variable expansions.
+ */
+static Word*
+nextword(char **s)
+{
+        Bufblock *b;
+        Word *head, *tail, *w;
+        Rune r;
+        char *cp;
+        int empty;
+
+        cp = *s;
+        b = newbuf();
+restart:
+        head = tail = 0;
+        while(*cp == ' ' || *cp == '\t')                /* leading white space */
+                cp++;
+        empty = 1;
+        while(*cp){
+                cp += chartorune(&r, cp);
+                switch(r)
+                {
+                case ' ':
+                case '\t':
+                case '\n':
+                        goto out;
+                case '\\':
+                case '\'':
+                case '"':
+                        empty = 0;
+                        cp = shellt->expandquote(cp, r, b);
+                        if(cp == 0){
+                                fprint(2, "missing closing quote: %s\n", *s);
+                                Exit();
+                        }
+                        break;
+                case '$':
+                        w = varsub(&cp);
+                        if(w == 0){
+                                if(empty)
+                                        goto restart;
+                                break;
+                        }
+                        empty = 0;
+                        if(b->current != b->start){
+                                bufcpy(b, w->s, strlen(w->s));
+                                insert(b, 0);
+                                free(w->s);
+                                w->s = strdup(b->start);
+                                b->current = b->start;
+                        }
+                        if(head){
+                                bufcpy(b, tail->s, strlen(tail->s));
+                                bufcpy(b, w->s, strlen(w->s));
+                                insert(b, 0);
+                                free(tail->s);
+                                tail->s = strdup(b->start);
+                                tail->next = w->next;
+                                free(w->s);
+                                free(w);
+                                b->current = b->start;
+                        } else
+                                tail = head = w;
+                        while(tail->next)
+                                tail = tail->next;
+                        break;
+                default:
+                        empty = 0;
+                        rinsert(b, r);
+                        break;
+                }
+        }
+out:
+        *s = cp;
+        if(b->current != b->start){
+                if(head){
+                        cp = b->current;
+                        bufcpy(b, tail->s, strlen(tail->s));
+                        bufcpy(b, b->start, cp-b->start);
+                        insert(b, 0);
+                        free(tail->s);
+                        tail->s = strdup(cp);
+                } else {
+                        insert(b, 0);
+                        head = newword(b->start);
+                }
+        }
+        freebuf(b);
+        return head;
+}
+
+void
+dumpw(char *s, Word *w)
+{
+        Bprint(&bout, "%s", s);
+        for(; w; w = w->next)
+                Bprint(&bout, " '%s'", w->s);
+        Bputc(&bout, '\n');
+}
diff --git a/troff/FIXES b/troff/FIXES
@@ -0,0 +1,821 @@
+March 11, 1994
+
+        If we are just plain old nroff (and not doing UNICODE) we should
+        only Lookup characters, not Install when we don't know them.
+        If we are troff, we Install them anyway
+
+March 8, 1994
+
+        Nroff had problems with parsing quoted white space as options or
+        character code in some terminals tables. Changed by having scanf
+        include white space when necessary as suggested by Rich.
+
+March 1, 1994
+
+        Made sanity check for terminal type depending on the trace level;
+        trace level set with -tn flag at start up
+
+22 Feb, 1994
+
+        More pointer shuffling fixes.
+
+18 Feb, 1994
+
+        More disabling of multibyte stuff. Fixed bug in n5.c: casetm didn'
+        know about the new format in the fontables.
+
+Feb 17, 1994
+
+        Removed extra include  from n1.c
+
+        Fixed dubious pointer shuffling in n7.c, t10.c & n8.c. Thanks Rich!
+
+Feb 10, 1994
+
+        Disabled the multybyte stuff; only plan 9 will get it.
+
+Jan 24, 1994
+
+        Fixed nasty bug discovered by td, which caused core dumps on
+        \D'l-0.002775i 0i' and apparently all numbers closer to 0
+        than -.002775. Fixed in storeline() and storeword() (n7.c).
+
+Dec 16, 1993
+
+        nroff & troff -N were looking for the TYPESETTER variable, causing
+
+        troff: cannot open /sys/lib/troff/term/tab.202; line 1, file stdin
+
+        fixed my moving getenv("TYPESETTER") to t10.c in t_ptinit(void).
+
+Dec 3, 1993:
+
+        The sequence \s+2\H'+10' came sometimes out in the wrong order
+        (x H before s), so there wasn't a difference bewteen \s+2\H'+10'
+        and \H'+10'\s+2. Now the fonts bits of the CHARHT are used to
+        register the current pontsize, so we can issue a s10 in t10.c
+        if needed. A bit sneaky.
+
+        Try to prevent double slashes in path names. Especially under
+        plan9 things started to look ugly.
+
+        Exception word list now grows dynamic.
+
+Nov 30, 1993:
+
+        Allow multiple calls to .pi, requested by Rob.
+                .pi cat
+                .pi dogs
+        is now equivalent with
+                .pi cat | dogs
+
+
+        .ab now takes also optional error code:
+                .ab [n] [string]
+        If n and string, n is exit code, string is message
+        If n, n is exit code, ``User Abort, exit code n" is message
+        If !n and string, standard exit code, string is message
+        If !n and ! string, standard exit code, "User Abort" is message
+
+Nov 24, 1993:
+
+        Reordered code to keep the UNASNI scripts happy.
+
+        Nroff dumped core reading terminal tables: apparenty under plan 9,
+        scanf includes the '\n'; added test for '\0' in parse in n10.c.
+
+        Relative tab settings (.ta +1C +2C) didn't work; anding the
+        previous value with TABMASK fixes this (caseta).
+
+Nov 23, 1993:
+
+        Included code, originally done by bwk for plan 9, to handle
+        multi-byte characters.
+
+Nov 3, 1993:
+
+        ``pair internal'' two char names by shifting 16 bits. Will allow
+        the use of 16 bit characters sets (Unicode in plan9 etc.) for
+        macro's etc.
+
+Oct 20, 1993:
+
+        Word & line buffers are now dynamic: No more word or line overflow
+        unless when we run out of memory.
+
+Oct 11, 1993:
+
+        lost diversion warning pops up regularly with man macro's. Due
+        to a possible macro coding problem. Triggered by something like
+        troff -man:
+                .TP
+                .TP
+                foo
+                .ex
+            Minimal code:
+                .di aa
+                throw away this diversion (aa) while being defined.
+                .rm aa
+                .br
+                .di
+
+        Fixed by disallowing .rm to throw away current diversion. The
+        rn request will complain with:
+
+                cannot remove diversion aa during definition; etc.
+
+Sep 29, 1993:
+
+        Some long standing fixes which never went back in the source.
+        Thanks to Janet & Rich.
+
+Sep 28, 1993:
+
+        Changed getach() (n1.c), so it does't consider truncated
+        special characters as (8-bit) ascii.  STX ETX ENQ ACK and BELL
+        are still allowed for the ultimate backwards compatibility.
+
+        Some code changes, so real ANSI compilers like the SGI version
+        (acc from Sun is a poor excuse for an ANSI compiler) don't
+        barf.  Some compromises (static Tchar wbuf in n9.c) allowed so
+        the unansified stuff for non-ansi compilers (cc on Sun's) will
+        work as well.
+
+Sep 9, 1993:
+
+        Be nice to Gerard. Now also word spaces in .tl and after
+        tabs/fleids etc.
+
+Aug 12, 1993:
+
+        Tabs setting can now be humongous. We also allow 99 tabs to
+        accomodate tbl. As a side effect, NTM buffers are now 1K
+
+Aug 11, 1993:
+
+        .R register, now contains maximum number of addessable
+        registers minus the number actually used.
+
+        Small esthetic changes in error messages; removed a statement
+        which wasn't reached anyway.
+
+Aug 10, 1993:
+
+        Some more speed hacks: be smarter doing the linear table
+        lookups in alloc() and finds().
+
+        The real name of the det diversion size macro is now gd.
+
+Aug 9, 1993:
+
+        A much faster way to find the end of a string/macro, by
+        remembering that when defined.
+
+Aug 6, 1993:
+
+         Slightly more eficient way of skipping to the end of a
+         string/macro
+
+Aug 5, 1993:
+
+        Prevent character sign extension for 8-bit charnames diversions
+        etc. by unpair
+
+Aug 4, 1993:
+
+        Growing the dynamical macro/strings name space and registers
+        space (See the experiment of 21 July) now with bigger
+        increments. Casts added to satisfy non-ANSI compilers.
+
+Aug 3, 1993:
+
+        Should check return value in alloc (n3.c), to prevent core dump
+        when memory gets tight.
+
+July 28, 1993:
+
+        New request: .sg 
sets the dn and dl registers to the size + of the diversion named in the argument. Doesn't do anything + when the named diversion doesn't exist. The name sg is + temporary until we find a better one. + +July 21, 1993: + + Experiment: Macro space & registers name allocated + dynamically. Note that current reallocation occurs in + increments of 1, to force the code to be executed a lot; a kind + of stress testing. Also, eight bit characters allowed in + macro/string names. + +July 21, 1993: + + Turn on the escape mode if the end macro is called. + +July 20, 1993: + + Tracing mode now default off + + Don't print s stackdump either when a file specfied on the + command line argument cannot be opened + +July 15, 1993: + + Don't print useless line & current file informations when a + file specfied on the command line argument cannot be opened. + + Sun ansi compiler doesn't default adhere to standards. Undid + the kludge in tdef.h + +July 14, 1993: + + Coding error made the tab type R not function properly + +July 12, 1993: + + Fixed a typo in the version stuff, noticed by Rich + +July 9, 1993: + + Added the dwb home configuration stuff, thanks RIch. Also, + NCHARS is big enough. Added a fflush to casetm, so .fm + will be up to date. + +June 25, 1993 (Rich): + + -t option + + reinstated for the sake of compatibility. Some old + shells scripts and man(1) from SunOs want this, sigh + + Compiler and system dependencies + + Some systems pull in sys/types.h via #include and then + the compiler complains about two ushort typedefs. Therefore, + ushort is now Ushort (and uchar Uchar). + + The SVID specifies a strdup, POSIX doesn't, anyway, troff + provides its own version, slightly different then the standard + one. A To prevent name clashes with that definion, renamed to + strdupl. + +June 24, 1993 (Rich): + + -V option added for DWB3.4 (rich) + +May 18, 1993: + + Trivial fix (.cf) request for troff -a + + issuing + + .cf /dev/null + + with troff -a gives some spurious output: + + H720 + H720 + s10 + f1 + + fixed by checking for ascii mode it ptesc(), ptps() and + ptfont() in t10.c + + + Enhancement + + Added a .tm request to roff. Works just like .tm, but now + it will do it to file. The name is coined by Carmela. Great + for creating indeces & toc's (we hope). + +May 18 1993: + + Compatibilty change + + Somebody complained that his favorite macro didn't work: + it had a BELL (^G) in the name. This was a non-documented + feature of earlier versions of troff (although the + documentation actually doesn't say that you can. (They can + only be used for delimiters or with the tr request), so it + isn't that important). + + But the sake of eternal backward compatibilaty I allowed + some control characters like, STX, ACK, etc. also be part + of a macro/string name. + + While at it, I made it also possible to have eight bit + characters be part of the name. It might be that this screws + up the way users think about these things. For UNICODE + versions, they probably want to do that as well, and that + won't work as easy, (because these characters are 16-bits + wide), so it is dubious whether we actually want this. + + BTW. Now + + .de \(ts\ts + .tm terminal sigma macro + .. + .\(ts\(ts + + also works, as long the internal cookie for ts isn't more then + eight bits. + +May 12, 1993: + + Syntax change + + Some requests accept tabs as a separator, some don't and + this can be a nuisance. Now a tab is also recognized as + an argument separator for requests, this makes + + .so /dev/null + + works. + + To be more precise, any motion character is allowed, so + + .so\h'5i'/dev/null + + will work as well, if one really wants that. + + It will be a problem for users who really relied on this as in + + .ds x string + + and expect the tab to become part of the string a, but I haven't + seen any use of that (obscure trick). + +May 6, 1993: + + Eileen count fixed + + Troff sometimes went in a loop, and exited with: ``job + looping; check abuse of macros'' (also known as the Eileen's + loop). It can be forced with the next trivial programme: + + .de ff + .di xx + .. + .wh -1 ff + .bp + + Basically what happens is that a page transition now will + happen in a diversion, which doesn't make sense. Wat really + happens is that eject() (in n7.c) doesn't eject the frame + because we are in a diversion. This cause the loop in n1.c + (because now always stack->pname <= ejl). Adding check on + whether we are not in a diversion takes care of the problem. + +March 30, 1993: + + Need request, .ne + + When there is a begin of page trap set, and the first thing + in the file is a .ne request, the trap gets fired, but, + the x font R etc. cookies doen't come out, because the + troff thinks that the first page pseudo transition already + took place. Fixed by forcing the start of the first page + in the casene request with the same code as in casetl (which + caused a similar problem quite some time ago). + + Change to .cf request ``Here document'' + + If the argument of .cf starts with a <<, the rest of it is taken + as an EOF token. It will reat the rest of the input until it hits + the EOF token and copies it to the output. This is similar as + the shell's ``here document'' mechanisme and put in place to + improve the kludgy way picasso, picpack etc. now include + postscript. + + Using troff -TLatin1 (DWB version) and \N'...' caused core dump + + In t11, in chadd, it should test on NCHARS - ALPHABET to see + whether we run out of table space (and we probably should beaf + up NCHARS for the DWB version). + +March 16, 1993: + + Diversion rename bug fix + + It is possible to get troff in an infinite loop by renaming a + diversion in progress, and calling it later with the + new name (as in .di xx, .rn xx yy, .yy). The effect depends on + whether troff already put stuff in the diversion or not. + + Fix by having .rn also rename the current diversion (if + there is any and when appropriate). If the diversion calls + itself by the new name and given the fix made on 11 nov + 1992, this will now result in an error. (BTW, the fix from + 11 nov is improved: diversions nest, so we have to account + for that). + +December 18, 1992: + Some people have complete novels as comments, so we need + to skip comments while checking the legality of font files. + thaks Rixh + +December 16, 1992 + + Some people rely on the order that -r arguments are given, + so that troff -rC1 -rC3 ends up setting register C to 3. + Because cpushback() pushes things in a LIFO order back, we + have to do the same to get -r args in a FIFO order. + +Nov 17, 1992: + + Giving a -rL8 option cuased the string .nr L 8 to be printed + on the output, using the wonderful 3b2. Some garbage was + left in buf[100] in main(). Fixed by setting buf[0] explicitly + to 0 (because some C-compilers complain about ``no automatic + aggregate initialization''). + +Nov 11, 1992: + + Diversion bug fix + + If a diversion was being read and the input is faulty so + the diversion was reading in itself, it caused troff to + loop undefinitely. This was easily fixed by a test in + control(a,b) in n1.c. + + Something similar things might happen with macros causing + the ``eileenct problem'', but I didn't look for that. We + have to wait until it happens. + +Oct 26, 1992: + + Numeric arguments: + + Illegal argments are treated as missing arguments. This + changed the semantics of .ll, .ls, .in, .lg, .ul, .cu .lt + (which acted as if the argument was 0) and .ps which was + simply ignored with an illegal argument. + + Tidied up number parsing in atoi1(). This prevents arguments + like .x or 1.2.3.4 being interpret as a legal number (nonumb = 0) + + Numeric arguments error reporting: + + Controlled by .pt, illegal numbers are now reported (default + trace mode is 1). This is also true for the escapes: + \h'..', \v'..' \H'..', \S'..', \N'..', \D'..', \l'.., \L'.. + and \x'..'. + + \D'c' is the only drawing request which doesn't take a pair + of numbers as arguments, so a special case is put here in + setdraw() (This code actually could use an overhaul to get + better parsing. As long as the \D'..' cookies are machine + generated it is low on the priority list). + + Don't generate an error if the illegal argument to a request + is a \}. It is too painful to do right (although it can be + done, but it would clutter getch() and getcho() even more). + + Input line numbers (.c register) bug fixes: + + In not taken branches of .if or .ie, the input line # + (numtab[CD].val) should be raised when necessary (in eatblk()). + + For concealed newlines, we still should count the line for input. + + Setfield (n9.c) sometimes pushes the rest of the line back to + the input (including \n), without adjusting numtab[CD].val + + Because .c (and so numtab[CD].val) is the number of lines read + and the error might actually happen in the current line + (before seeing the '\n), we need to apply correction in + errprint when nlflg set. (This correction needs to be undone + when inside a macro because the nlflg is set by reading the + args to the macro). + + Line number setting (.lf) request bug fixes: + + I interpret that the .c register will contain the number of + read lines, not including the current one. + + Also, don't change the input line number when the first + argument of .lf is not a number. + + As a net effect, the next input + + .EQ + .EN + .ab + + will generate the same output whether eqn has been used or not. + + If request bug fix: + + A ``.if page .tm foo'' caused the next line being ignored; + This bcause when the 2nd delimiter of a string couldn't be + found in cmpstr, the next line was always eaten. Solution: + in caseif1, if the condition is false, we should check + nlflg before eating a block. (Note: We might have eaten + \{\ as well. We could disallow the \{\ in a string to be + compared to prevent that but that might break other things). + + Enhancement to .pt: + + The .pt now pops the previous values when no argument is + specified. Turned out to be handy when chasing for problems. + Just ``bracked'' the code with .pt 7 and .pt and you get + a trace of only that block. The meaning of the arguments + is now: + 01 trace numeric arguments (default on) + 02 trace requests + 04 trace macros + + Abort request (.ab) beautification: + + Don't print the extra carriage return when .ab is called + without an argument. + +Oct 12, 1992: + + (Comments & spelling errors from this day on by jaap) + + replaced 32767 by INT_MAX in several places to allow for very + long pages (on 32-but machines). + + The ``.fp 1 R \"COMMENT'' complains about ``./troff: Can't + open font file /usr/lib/font/devpost/h'' on some systems. It + sees the tab as part of the optional font file. Apparently it + is system dependent whether isgraph() includes the tab + character. Fixed by using getach() in getname() in n1.c + instead. + +Aug 28, 1992: + removed call to popi from rdtty(); it was eating up the + rest of the macro if it was used from within one. (thanks, jaap) + + +Jul 21, 1992: + added extra test in nextfile() to pop current input file + only if not in .nx command. thanks to jaap. + + added test in getword() to avoid hyphenating after \z character, + which prevents any hyphenation inside \X'...'. thanks to jaap. + + added, then removed, code in getword() to prevent hyphenating + anything shorter than 6 characters. looks like it changed a + lot more than i thought. + +Jul 12, 1992: + added .pt request to trace macros and requests (from jaap). + .pt N Print trace of macros (N=1), requests (N=2) or both (N=3) + +Jun 5, 1992: + added tests to t.twrest and t.twinit to avoid 0 deref in + n2 and n10, for nroff -t xxxxx. thanks to Rich Drechsler. + +May 22, 1992: + added extern decls to e.g., void Tchar (*hmot)(void) in tdef.h + and added definition to ni.c, so pointers are defined explicitly. + makes it work on turbo c++ and probably others. + + changed a couple of isdigit's and isgraph(getch()) to avoid + multiple evaluation (even though it shouldn't happen). + + Made /usr/bin/nroff a shell script. + +May 12, 1992: + n1.c: need p++ after strrchr to skip / in program name. + thanks to Rich Drechsler. + +Apr 17, 1992: + casefi(), n5.c: .u register should be 0 or 1, not incremented + with each .fi. + +Apr 5, 1992: + fiddled n7.c and added _nmwid to the environment, to add a + 5th argument to .nm: the maximum number of digits in any + line number. default is 3, which was previously hardwired in. + + added jaap's code for yet another register which actually delivers + a string, called .S (so it can easily go in the switch in setn() + in n4.c); it delivers the current tabstop and alignment modes in + a format suitable for a subsequent .ta \n(.S command: + .ds T \n(.S + ... + .ta \*T + +Mar 30, 1992: + added test in getword to avoid hyphenating things with motions + (and avoid a core dump sometimes too). + +Mar 13, 1992: + \n(sb initialized wrong in setwd(). + + TYPESETTER=foo troff -Tpost used foo instead of post. + +Mar 12, 1992: + rearranged tests in popf so that .so is closed properly before + moving on to the next macro package. + +Mar 1, 1992: + input mechanism rearranged to use getc() instead of stack of + explicit input buffers. 5-10% slowdown. + +Jan 28, 1992: + fixed .tm \(mi to print something sensible. thanks to jaap. + +Jan 2, 1992: + fiddle setfp so doesn't put out font stuff if -a turned on. + +Dec 17, 1991: + copy 3rd argument in .fp commands to x font ... lines when it contains + a /, for testing fonts locally. + +Dec 13, 1991: + parameterize the font directories, etc., so can be set in makefiles. + added -N argument to run as nroff. + +Nov 8, 1991: + add a maplow(towlower...) in n8.c to handle brain-damaged libraries. + +Nov 2, 1991: + merged nroff into troff, based on Ken's plan 9 version. + merged nii.c into ni.c, removed tw.h, etc. more work needed + to make this stuff cleaner. + +July 27, 1991: + added test in setn in n4 to fix bug that permitted things like + \n (ab to work "properly". thanks to jaap for finding and fixing. + + added paranoid testing in t11 to make sure font files look ok. + +May 13, 1991: + moved evaluation of \(xx from copy mode to non-copy mode, so that + weird character names wouldn't get reevaluated in argument parsing. + installed july 27. + +May 6, 1991: + increased size of hyphenation exception buffer to 512 from 128 + +Apr 14, 1991: + added an extra redundant call of ptfont in setfp, since it appears + that some versions of adobe transcript assume that an "x font" command + means to change the actual font as well. the fix preserves the current font. + thanks to david brailsford and friends for spotting the problem. + + fixed up tests in alpha() in n8 to defend isalpha() against too-big inputs. + punct() argument had wrong type too. thanks to rich drexler and peter nelson. + +Mar 19, 1991: + fixed bug that prevented .rd from working with new corebuf organization. + + fixed bug that caused .ig inside diversions to give bad storage + allocation. thanks to arthur david olson, whose fix was on netnews + 3 years earlier. + +Mar 5, 1991: + huge table sizes for kanji. + +Feb ??, 1991: + working on dealing with large alphabets, notably kanji. + added "defaultwidth" to font descriptions, for characters + not given an explicit width. + +Jan, 1991: + added tex hyphenation, using standard tex data files, but not the + elaborate compressed trie, which is a lot of trouble to save maybe + 40k bytes. this appears to run at exactly the same speed as before. + + so far this stuff reads into a fixed size array; that should change. + it should also be possible to deal with multiple languages. + + the command .ha sets the algorithm. .ha 1 => tex, with troff rules + if tex doesn't hyphenate; .ha 0 gives troff rules, and .ha resets + to the default, which is tex. the hyphenation algorithm is part of + the environment, a nod to a future in which i handle more than one + language. + + replaced the fixed size corebuf array for string/macro storage by + a dynamic structure that can grow. + + this appears to slow things down by maybe 3%. the code is about + the same complexity. + +Dec 27, 1990: + converted to ansi c, based on some work by ken thompson, but not + as thoroughly as he did. there is a shell script unansi and an awk + program cvt that will help you step back in time if you do not have + an ansi c compiler. + + moved the special-name characters up to 256 instead of 128, although + done in terms of ALPHABET, so one can pass 8 bit characters through. + removed lots of 0177's and similar numbers. input is now not filtered, + and if a character with the 8th bit on comes in, it will go out again. + + fixed t11.c to read character names in hex or octal as well as + single-character ascii. + + unknown characters are now carried through with width = spacewidth. + needs a way to set widths. + + removed all signal handling from troff. you signal, you die. + + added -d option to print version number. + +Dec 7, 1990: + .fp 3 V VERYLONGNAME used to truncate the name to 10 chars; fixed. + + increased the limit on FBUFSZ for tables with very long fields. + + changed atoi1() to use double to avoid intermediate overflow. + + moved filenames like /usr/lib/font into tdef.h for easy change. + removed some dreggish definitions. + + cleaned up non-portable error printing stuff; fixed up some messages. + +Dec 12, 1989: + Removed the .! command, an undocumented synonym for .sy. + +Dec 4, 1989: + Another wart to the \X code, to try to preserve blanks in all situations. + +Nov 17, 1989: + A number of small changes preparatory to getting rid of nroff. + The argument -Tnroff or -Tnroff-12 changes some internal values + so that the predicate .if n is true and certain arithmetic operations + are done as if nroff. This design is not yet final. + +Nov 7, 1989: + Fixed hyphenation for nov-ice, ad-vice, de-vice, ser-vice, *-vice. + +Oct 11, 1989: + It is now permitted to do an explicit change to font S. + It is not clear what will break (though nothing seems to have). + +Oct 10, 1989: + Modified flush code to always put out \nH instead of sometimes h. + This makes it easier to parse the output for positioning. + +Sep 9, 1989: + Fixed internal representation of \D'~...' so that it + is immune to .tr ~ and variations. No external change. + +Aug 9, 1989: + Changed .tm so it outputs \e, \%, \-, \&, \(blank). + This might break indexing code. + Only in the new version, as are all subsequent fixes. + +July, 1989: + A major internal change: font information is read in ascii + instead of the weird binary format of makedev (which is now dead). + character names need not all appear in DESC; new names that + appear when a font is used become part of the set of known names. + + There are some flaky bits here (it's conceivable that some \N + number will collide with a real name), and it's probably 10-15% + slower. Tant pis. + + As a by-product, nroff no longer compiles. I'll probably get + back to this, but an alternative is to bag it once and for all. + +May 25, 1989: + Another bug in \l, this time when width is 0. Not installed, + since it's in the new font version. + +Apr 23, 1989: + Fixed bug in n9 that caused core dump with unterminated + \l command, like \l'1.5i + + ptflush no longer called when -a is on. + +Apr 12, 1989: + fixed bug in n2 that failed to suppress printing of \! + output when a -o was in effect. + +Apr 5, 1989: + .fl and \X now cause output of size, font, hpos and vpos. + this is necesary for postprocessors that intend to insert + independent material, such as postscript. + +Feb 1, 1989: + wait for .pi pipe to empty before exiting + +Oct 2, 1988: + default is now -Tpost + +Sep 19, 1988: + added abortive code to handle built-up characters by + passing something through as \D'b...'. never used. + +Jul 4, 1988: + replaced the sbrk nonsense in n3.c by calls to malloc. + + \N now tests against proper font size. + + installed Jaap Akkerhuis's code (mutatis mutandis) for + permitting up to 99 fonts, swapping them into font pos 0 + as needed. fixes the long-standing problem of having + multiple font changes on a single output line. + +Jul 2, 1988: + \X now preserves spaces even when contents are diverted. + + \N code safer -- NTRTAB and NWIDCACHE enlarged. + +Jul 14, 1987: + Fixed obscure bug causing incorrect indentation of .mc output.
diff --git a/troff/Makefile b/troff/Makefile
@@ -0,0 +1,11 @@
+# mk - mk unix port from plan9
+# Depends on ../lib9
+
+TARG      = troff
+
+OFILES    = n1.o n2.o n3.o n4.o n5.o t6.o n6.o n7.o n8.o n9.o t10.o\
+            n10.o t11.o ni.o hytab.o suftab.o dwbinit.o mbwc.o
+MANFILES  = troff.1
+CFLAGS    = -DUNICODE -DTMACDIR=\"tmac/tmac.\" -DTDEVNAME=\"utf\" -DFONTDIR=\"troff/font\" -DNTERMDIR=\"troff/term/tab.\" -DTEXHYPHENS=\"#9/lib/hyphen.tex\" -DALTHYPHENS=\"lib/hyphen.tex\" -DDWBHOME=\"#9/\"
+
+include ../std.mk
diff --git a/troff/README b/troff/README
@@ -0,0 +1,31 @@
+To make troff (actually a.out):
+
+        make
+
+You will also need to write a driver for your favorite output device.
+d202.c provides a model, although it is specialized to a machine no
+one has.  There are also a variety of postscript drivers that are the
+best thing to use if you have a postscript device.
+
+You will also have to make a DESC file for your typesetter and some
+font description files; see dev202 for examples.  These describe the
+named characters, widths, kerning information, and output codes.
+
+Nroff is the same program as troff, so you should
+
+        cp a.out /usr/bin/troff
+        ln /usr/bin/troff /usr/bin/nroff
+
+or the equivalent.
+
+You will also need terminal description files for your terminals; see
+tab.37, tab.450 and tab.lp for examples.
+
+Troff uses files that are normally stored in /usr/lib/font;
+macro packages are in /usr/lib/tmac; and nroff tables are in
+/usr/lib/term.  You can edit tdef.h to change these assumptions.
+
+There have been a few features since the last version, and a number of
+significant internal changes.  Not all are improvements, of course.
+Most of the more recent changes, including bug fixes, are in FIXES,
+which you should read also.
diff --git a/troff/cvt b/troff/cvt
@@ -0,0 +1,45 @@
+
+awk '
+
+/^{/ {
+        if (prev != "") {
+                # comments can be trouble (e.g. ffree())
+                if ( (c = match(prev, /\/\*.*\*\/$/)) != 0 ) {
+                        comment = substr(prev, c)
+                        sub(/\/\*.*\*\/$/, "", prev)
+                } else comment = ""
+
+                x = prev
+
+                # isolate argument list
+                sub(/^[^(]*\(/, "", x)
+                sub(/\)[^)]*$/, "", x)
+
+                # find the names in it
+                n = split(x, args)
+                arglist = ""
+                for (i = 2; i <= n; i += 2)
+                        arglist = arglist args[i]
+                gsub(/\(\*f\)\(Tchar\)/, "f", arglist)        # special case for n4.c
+                gsub(/\[[0-9]+\]/, "", arglist)                #     for n8.c
+                gsub(/[*()\[\]]/, "", arglist)                # discard noise characters *()[]
+                gsub(/,/, ", ", arglist)                # space nicely
+                sub(/\(.*\)/, "(" arglist ")", prev)        # reconstruct
+                print prev comment
+
+                # argument declarations
+                gsub(/,/, ";", x)
+                gsub(/\(\*f\)\(Tchar\)/, "(*f)()", x)        # special case for n4.c
+                if (x != "")
+                        print "\t" x ";"
+        }
+        prev = $0
+        next
+}
+
+{        print prev
+        prev = $0
+}
+
+END { print prev }
+' $*
diff --git a/troff/dwbinit.c b/troff/dwbinit.c
@@ -0,0 +1,317 @@
+/*
+ *
+ * Pathname management routines for DWB C programs.
+ *
+ * Applications should initialize a dwbinit array with the string
+ * pointers and arrays that need to be updated, and then hand that
+ * array to DWBinit before much else happens in their main program.
+ * DWBinit calls DWBhome to get the current home directory. DWBhome
+ * uses the last definition of DWBENV (usually "DWBHOME") in file
+ * DWBCONFIG (e.g., /usr/lib/dwb3.4) or the value assigned to that
+ * variable in the environment if the DWBCONFIG file doesn't exist,
+ * can't be read, or doesn't define DWBENV.
+ *
+ * DWBCONFIG must be a simple shell script - comments, a definition
+ * of DWBHOME, and perhaps an export or echo is about all that's
+ * allowed. The parsing in DWBhome is simple and makes no attempt
+ * to duplicate the shell. It only looks for DWBHOME= as the first
+ * non-white space string on a line, so
+ *
+ *        #
+ *        # A sample DWBCONFIG shell script
+ *        #
+ *
+ *        DWBHOME=/usr/add-on/dwb3.4
+ *        export DWBHOME
+ *
+ * means DWBhome would return "/usr/add-on/dwb3.4" for the DWB home
+ * directory. A DWBCONFIG file means there can only be one working
+ * copy of a DWB release on a system, which seems like a good idea.
+ * Using DWBCONFIG also means programs will always include correct
+ * versions of files (e.g., prologues or macro packages).
+ *
+ * Relying on an environment variable guarantees nothing. You could
+ * execute a version of dpost, but your environment might point at
+ * incorrect font tables or prologues. Despite the obvious problems
+ * we've also implemented an environment variable approach, but it's
+ * only used if there's no DWBCONFIG file.
+ *
+ * DWBinit calls DWBhome to get the DWB home directory prefix and
+ * then marches through its dwbinit argument, removing the default
+ * home directory and prepending the new home. DWBinit stops when
+ * it reaches an element that has NULL for its address and value
+ * fields. Pointers in a dwbinit array are reallocated and properly
+ * initialized; arrays are simply reinitialized if there's room.
+ * All pathnames that are to be adjusted should be relative. For
+ * example,
+ *
+ *        char        *fontdir = "lib/font";
+ *        char        xyzzy[25] = "etc/xyzzy";
+ *
+ * would be represented in a dwbinit array as,
+ *
+ *        dwbinit allpaths[] = {
+ *                &fontdir, NULL, 0,
+ *                NULL, xyzzy, sizeof(xyzzy),
+ *                NULL, NULL, 0
+ *        };
+ *                
+ * The last element must have NULL entries for the address and
+ * value fields. The main() routine would then do,
+ *
+ *        #include "dwbinit.h"
+ *
+ *        main() {
+ *
+ *                DWBinit("program name", allpaths);
+ *                ...
+ *        }
+ *
+ * Debugging is enabled if DWBDEBUG is in the environment and has
+ * the value ON. Output is occasionally useful and probably should
+ * be documented.
+ *
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "dwbinit.h"
+
+#ifndef DWBCONFIG
+#define DWBCONFIG        "/dev/null"
+#endif
+
+#ifndef DWBENV
+#define DWBENV                "DWBHOME"
+#endif
+
+#ifndef DWBHOME
+#define DWBHOME                ""
+#endif
+
+#ifndef DWBDEBUG
+#define DWBDEBUG        "DWBDEBUG"
+#endif
+
+#ifndef DWBPREFIX
+#define DWBPREFIX        "\\*(.P"
+#endif
+
+/*****************************************************************************/
+
+void DWBdebug(dwbinit *ptr, int level)
+{
+
+    char        *path;
+    char        *home;
+    static char        *debug = NULL;
+
+/*
+ *
+ * Debugging output, but only if DWBDEBUG is defined to be ON in the
+ * environment. Dumps general info the first time through.
+ *
+ */
+
+    if ( debug == NULL && (debug = getenv(DWBDEBUG)) == NULL )
+        debug = "OFF";
+
+    if ( strcmp(debug, "ON") == 0 ) {
+        if ( level == 0 ) {
+            fprintf(stderr, "Environment variable: %s\n", DWBENV);
+            fprintf(stderr, "Configuration file: %s\n", DWBCONFIG);
+            fprintf(stderr, "Default home: %s\n", DWBHOME);
+            if ( (home = DWBhome()) != NULL )
+                fprintf(stderr, "Current home: %s\n", home);
+        }   /* End if */
+
+        fprintf(stderr, "\n%s pathnames:\n", level == 0 ? "Original" : "Final");
+        for ( ; ptr->value != NULL || ptr->address != NULL; ptr++ ) {
+            if ( (path = ptr->value) == NULL ) {
+                path = *ptr->address;
+                fprintf(stderr, " pointer: %s\n", path);
+            } else fprintf(stderr, " array[%d]: %s\n", ptr->length, path);
+            if ( level == 0 && *path == '/' )
+                fprintf(stderr, "  WARNING - absolute path\n");
+        }   /* End for */
+    }        /* End if */
+
+}   /* End of DWBdebug */
+
+/*****************************************************************************/
+
+extern        char        *unsharp(char*);
+
+char *DWBhome(void)
+{
+
+    FILE        *fp;
+    char        *ptr;
+    char        *path;
+    int                len;
+    char        buf[200];
+    char        *home = NULL;
+
+/*
+ *
+ * Return the DWB home directory. Uses the last definition of DWBENV
+ * (usually "DWBHOME") in file DWBCONFIG (perhaps /usr/lib/dwb3.4) or
+ * the value assigned to the variable named by the DWBENV string in
+ * the environment if DWBCONFIG doesn't exist or doesn't define DWBENV.
+ * Skips the file lookup if DWBCONFIG can't be read. Returns NULL if
+ * there's no home directory.
+ *
+ */
+
+    if ( (fp = fopen(DWBCONFIG, "r")) != NULL ) {
+        len = strlen(DWBENV);
+        while ( fgets(buf, sizeof(buf), fp) != NULL ) {
+            for ( ptr = buf; isspace((uchar)*ptr); ptr++ ) ;
+            if ( strncmp(ptr, DWBENV, len) == 0 && *(ptr+len) == '=' ) {
+                path = ptr + len + 1;
+                for ( ptr = path; !isspace((uchar)*ptr) && *ptr != ';'; ptr++ ) ;
+                *ptr = '\0';
+                if ( home != NULL )
+                    free(home);
+                if ( (home = malloc(strlen(path)+1)) != NULL )
+                    strcpy(home, path);
+            }        /* End if */
+        }   /* End while */
+        fclose(fp);
+    }   /* End if */
+
+    if ( home == NULL ) {
+        if ( (home = getenv(DWBENV)) == NULL ) {
+            if ( (home = DWBHOME) == NULL || *home == '\0' || *home == ' ' )
+                home = NULL;
+        }   /* End if */
+        home = unsharp(home);
+    }        /* End if */
+
+    while (home && *home == '/' && *(home +1) == '/')        /* remove extra slashes */
+        home++;
+    return(home);
+
+}   /* End of DWBhome */
+
+/*****************************************************************************/
+
+void DWBinit(char *prog, dwbinit *paths)
+{
+
+    char        *prefix;
+    char        *value;
+    char        *path;
+    int                plen;
+    int                length;
+    dwbinit        *opaths = paths;
+
+/*
+ *
+ * Adjust the pathnames listed in paths, using the home directory
+ * returned by DWBhome(). Stops when it reaches an element that has
+ * NULL address and value fields. Assumes pathnames are relative,
+ * but changes everything. DWBdebug issues a warning if an original
+ * path begins with a /.
+ *
+ * A non-NULL address refers to a pointer, which is reallocated and
+ * then reinitialized. A NULL address implies a non-NULL value field
+ * and describes a character array that we only reinitialize. The
+ * length field for an array is the size of that array. The length
+ * field of a pointer is an increment that's added to the length
+ * required to store the new pathname string - should help when we
+ * want to change character arrays to pointers in applications like
+ * troff.
+ *
+ */
+
+    if ( (prefix = DWBhome()) == NULL ) {
+        fprintf(stderr, "%s: no DWB home directory\n", prog);
+        exit(1);
+    }        /* End if */
+
+    DWBdebug(opaths, 0);
+    plen = strlen(prefix);
+
+    for ( ; paths->value != NULL || paths->address != NULL; paths++ ) {
+        if ( paths->address == NULL ) {
+            length = 0;
+            value = paths->value;
+        } else {
+            length = paths->length;
+            value = *paths->address;
+        }   /* End else */
+
+        length += plen + 1 + strlen(value);        /* +1 is for the '/' */
+
+        if ( (path = malloc(length+1)) == NULL ) {
+            fprintf(stderr, "%s: can't allocate pathname memory\n", prog);
+            exit(1);
+        }   /* End if */
+
+        if ( *value != '\0' ) {
+            char *eop = prefix;
+            while(*eop++)
+                ;
+            eop -= 2;
+            if (*value != '/' && *eop != '/') {
+                sprintf(path, "%s/%s", prefix, value);
+            } else if (*value == '/' && *eop == '/') {
+                value++;
+                sprintf(path, "%s%s", prefix, value);
+            } else
+                sprintf(path, "%s%s", prefix, value);
+        } else
+                sprintf(path, "%s", prefix);
+
+        if ( paths->address == NULL ) {
+            if ( strlen(path) >= paths->length ) {
+                fprintf(stderr, "%s: no room for %s\n", prog, path);
+                exit(1);
+            }        /* End if */
+            strcpy(paths->value, path);
+            free(path);
+        } else *paths->address = path;
+    }        /* End for */
+
+    DWBdebug(opaths, 1);
+
+}   /* End of DWBinit */
+
+/*****************************************************************************/
+
+void DWBprefix( char *prog, char *path, int length)
+{
+
+    char        *home;
+    char        buf[512];
+    int                len = strlen(DWBPREFIX);
+
+/*
+ *
+ * Replace a leading DWBPREFIX string in path by the current DWBhome().
+ * Used by programs that pretend to handle .so requests. Assumes path
+ * is an array with room for length characters. The implementation is
+ * not great, but should be good enough for now. Also probably should
+ * have DWBhome() only do the lookup once, and remember the value if
+ * called again.
+ * 
+ */
+
+    if ( strncmp(path, DWBPREFIX, len) == 0 ) {
+        if ( (home = DWBhome()) != NULL ) {
+            if ( strlen(home) + strlen(path+len) < length ) {
+                sprintf(buf, "%s%s", home, path+len);
+                strcpy(path, buf);                /* assuming there's room in path */
+            } else fprintf(stderr, "%s: no room to grow path %s", prog, path);
+        }   /* End if */
+    }        /* End if */
+
+}   /* End of DWBprefix */
+
+/*****************************************************************************/
+
diff --git a/troff/dwbinit.h b/troff/dwbinit.h
@@ -0,0 +1,19 @@
+/*
+ *
+ * A structure used to adjust pathnames in DWB C code. Pointers
+ * set the address field, arrays use the value field and must
+ * also set length to the number elements in the array. Pointers
+ * are always reallocated and then reinitialized; arrays are only
+ * reinitialized, if there's room.
+ *
+ */
+
+typedef struct {
+        char        **address;
+        char        *value;
+        int        length;
+} dwbinit;
+
+extern void        DWBinit(char *, dwbinit *);
+extern char*        DWBhome(void);
+extern void        DWBprefix(char *, char *, int);
diff --git a/troff/ext.h b/troff/ext.h
@@ -0,0 +1,187 @@
+#define        devname        p9_devname
+
+extern        int        TROFF;
+
+extern        int        alphabet;
+extern        char        **argp;
+extern        char        *eibuf;
+extern        char        *ibufp;
+extern        char        *obufp;
+extern        char        *unlkp;
+extern        char        *xbufp;
+extern        char        *xeibuf;
+extern        char        cfname[NSO+1][NS];
+extern  int        trace;
+extern        char        devname[];
+extern        char        ibuf[IBUFSZ];
+extern        char        mfiles[NMF][NS];
+extern        char        nextf[];
+extern        char        obuf[];
+extern        char        termtab[];
+extern        char        fontdir[];
+extern        Font        fonts[MAXFONTS+1];
+extern        char        xbuf[IBUFSZ];
+extern        Offset        apptr;
+extern        Offset        ip;
+extern        Offset        nextb;
+extern        Offset        offset;
+extern        Offset        woff;
+extern        Numerr        numerr;
+extern        int        *pnp;
+extern        int        pstab[];
+extern        int        nsizes;
+extern        int        app;
+extern        int        ascii;
+extern        int        bd;
+extern        int        bdtab[];
+extern        int        ccs;
+extern        char        *chnames[];        /* chnames[n-ALPHABET] -> name of char n */
+extern        int        copyf;
+extern        int        cs;
+extern        int        dfact;
+extern        int        dfactd;
+extern        int        diflg;
+extern        int        dilev;
+extern        int        donef;
+extern        int        dotT;
+extern        int        dpn;
+extern        int        ds;
+extern        int        ejf;
+extern        int        em;
+extern        int        eqflg;
+extern        int        error;
+extern        int        esc;
+extern        int        eschar;
+extern        int        ev;
+extern        int        evi;
+extern        int        evlist[EVLSZ];
+extern        int        fc;
+extern        int        flss;
+extern        int        fontlab[];
+extern        int        hflg;
+extern        int        ibf;
+extern        int        ifi;
+extern        int        iflg;
+extern        int        init;
+extern        int        lead;
+extern        int        lg;
+extern        int        lgf;
+extern        int        macerr;
+extern        int        mflg;
+extern        int        mfont;
+extern        int        mlist[NTRAP];
+extern        int        mpts;
+extern        int        nchnames;
+extern        int        ndone;
+extern        int        newmn;
+extern        int        nflush;
+extern        int        nfo;
+extern        int        nfonts;
+extern        int        nform;
+extern        int        nhyp;
+extern        int        nlflg;
+extern        int        nlist[NTRAP];
+extern        int        nmfi;
+extern        int        nonumb;
+extern        int        noscale;
+extern        int        npn;
+extern        int        npnflg;
+extern        int        nx;
+extern        int        oldbits;
+extern        int        oldmn;
+extern        int        over;
+extern        int        padc;
+extern        int        pfont;
+extern        int        pfrom;
+extern        int        pipeflg;
+extern        int        pl;
+extern        int        pnlist[];
+extern        int        po1;
+extern        int        po;
+extern        int        ppts;
+#define        print        troffprint
+extern        int        print;
+extern        FILE        *ptid;
+extern        int        pto;
+extern        int        quiet;
+extern        int        ralss;
+extern        int        rargc;
+extern        int        raw;
+extern        int        res;
+extern        int        sbold;
+extern        int        setwdf;
+extern        int        sfont;
+extern        int        smnt;
+extern        int        stdi;
+extern        int        stop;
+extern        int        sv;
+extern        int        tabch,        ldrch;
+extern        int        tflg;
+extern        int        totout;
+extern        int        trap;
+extern        Ushort        trtab[];
+extern        int        tty;
+extern        int        ulfont;
+extern        int        vflag;
+extern        int        whichroff;
+extern        int        widthp;
+extern        int        xfont;
+extern        int        xpts;
+extern        Stack        *ejl;
+extern        Stack        *frame;
+extern        Stack        *stk;
+extern        Stack        *nxf;
+extern        Tchar        **hyp;
+extern        Tchar        *olinep;
+extern        Tchar        pbbuf[NC];
+extern        Tchar        *pbp;
+extern        Tchar        *lastpbp;
+extern        Tchar        ch;
+extern        Tchar        nrbits;
+extern        Tbuf        _oline;
+extern        Wcache        widcache[];
+extern        char        gchtab[];
+extern        Diver        d[NDI];
+extern        Diver        *dip;
+
+
+extern        char        xchname[];
+extern        short        xchtab[];
+extern        char        *codestr;
+extern        char        *chnamep;
+extern        short        *chtab;
+extern        int        nchtab;
+
+extern Numtab *numtabp;
+
+/* these characters are used as various signals or values
+/* in miscellaneous places.
+/* values are set in specnames in t10.c
+*/
+
+extern int        c_hyphen;
+extern int        c_emdash;
+extern int        c_rule;
+extern int        c_minus;
+extern int        c_fi;
+extern int        c_fl;
+extern int        c_ff;
+extern int        c_ffi;
+extern int        c_ffl;
+extern int        c_acute;
+extern int        c_grave;
+extern int        c_under;
+extern int        c_rooten;
+extern int        c_boxrule;
+extern int        c_lefthand;
+extern int        c_dagger;
+extern int        c_isalnum;
+
+/*
+ * String pointers for DWB pathname management.
+ */
+
+extern char        *DWBfontdir;
+extern char        *DWBntermdir;
+extern char        *DWBalthyphens;
+
diff --git a/troff/find b/troff/find
@@ -0,0 +1 @@
+grep $1 *.[ch]
diff --git a/troff/fns.h b/troff/fns.h
@@ -0,0 +1,389 @@
+#define getline p9getline
+
+/*
+ * other
+ */
+#ifdef NOTDEF
+int        pclose(FILE*);
+long        filesize(int fd);
+int        open(char *, int);
+int        read(int, char *, int);
+int        lseek(int, long, int);
+int        close(int);
+int        getpid(void);
+#endif
+char        *unsharp(char*);
+
+/*
+ * c1.c
+ */
+void        init0(void);
+void        init2(void);
+void        cvtime(void);
+void        errprint(void);
+int        control(int a, int b);
+void        casept(void);
+int        getrq(void);
+Tchar        getch(void);
+void        setxon(void);
+Tchar        getch0(void);
+Tchar        get1ch(FILE *);
+void        pushback(Tchar *b);
+void        cpushback(char *b);
+int        nextfile(void);
+int        popf(void);
+void        flushi(void);
+int        getach(void);
+void        casenx(void);
+int        getname(void);
+void        caseso(void);
+void        caself(void);
+void        casecf(void);
+void        getline(char *s, int n);
+void        casesy(void);
+void        getpn(char *a);
+void        setrpt(void);
+
+/*
+ * n2.c
+ */
+int        pchar(Tchar i);
+void        pchar1(Tchar i);
+int        pchar2(Tchar i);
+int        flusho(void);
+void        casedone(void);
+void        caseex(void);
+void        done(int x);
+void        done1(int x);
+void        done2(int x);
+void        done3(int x);
+void        edone(int x);
+void        casepi(void);
+
+/*
+ * c3.c
+ */
+void        blockinit(void);
+char*        grow(char *, int, int);
+void        mnspace(void);
+void        caseig(void);
+void        casern(void);
+void        maddhash(Contab *rp);
+void        munhash(Contab *mp);
+void        mrehash(void);
+void        caserm(void);
+void        caseas(void);
+void        caseds(void);
+void        caseam(void);
+void        casede(void);
+int        findmn(int i);
+void        clrmn(int i);
+Offset        finds(int mn);
+int        skip(void);
+int        copyb(void);
+void        copys(void);
+Offset        alloc(void);
+void        ffree(Offset i);
+void        wbf(Tchar i);
+Tchar        rbf(void);
+Tchar        popi(void);
+Offset        pushi(Offset newip, int mname);
+void*        setbrk(int x);
+int        getsn(void);
+Offset        setstr(void);
+void        collect(void);
+void        seta(void);
+void        caseda(void);
+void        casegd(void);
+void        casedi(void);
+void        casedt(void);
+void        casetl(void);
+void        casepc(void);
+void        casepm(void);
+void        stackdump(void);
+
+/*
+ * c4.c
+ */
+void        setn(void);
+int        wrc(Tchar i);
+void        setn1(int i, int form, Tchar bits);
+void        nnspace(void);
+void        nrehash(void);
+void        nunhash(Numtab *rp);
+int        findr(int i);
+int        usedr(int i);
+int        fnumb(int i, int (*f)(Tchar));
+int        decml(int i, int (*f)(Tchar));
+int        roman(int i, int (*f)(Tchar));
+int        roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp);
+int        abc(int i, int (*f)(Tchar));
+int        abc0(int i, int (*f)(Tchar));
+long        atoi0(void);
+long        ckph(void);
+long        atoi1(Tchar ii);
+void        caserr(void);
+void        casenr(void);
+void        caseaf(void);
+void        setaf(void);
+int        vnumb(int *i);
+int        hnumb(int *i);
+int        inumb(int *n);
+int        quant(int n, int m);
+
+/*
+ * c5.c
+ */
+void        casead(void);
+void        casena(void);
+void        casefi(void);
+void        casenf(void);
+void        casers(void);
+void        casens(void);
+int        chget(int c);
+void        casecc(void);
+void        casec2(void);
+void        casehc(void);
+void        casetc(void);
+void        caselc(void);
+void        casehy(void);
+int        max(int aa, int bb);
+void        casenh(void);
+void        casece(void);
+void        casein(void);
+void        casell(void);
+void        caselt(void);
+void        caseti(void);
+void        casels(void);
+void        casepo(void);
+void        casepl(void);
+void        casewh(void);
+void        casech(void);
+int        findn(int i);
+void        casepn(void);
+void        casebp(void);
+void        casextm(void);
+void        casetm(void);
+void        casefm(void);
+void        casetm1(int ab, FILE *out);
+void        casesp(void);
+void        casesp1(int a);
+void        casert(void);
+void        caseem(void);
+void        casefl(void);
+void        caseev(void);
+void        envcopy(Env *e1, Env *e2);
+void        caseel(void);
+void        caseie(void);
+void        casexif(void);
+void        caseif(void);
+void        caseif1(int);
+void        eatblk(int inblk);
+int        cmpstr(Tchar c);
+void        caserd(void);
+int        rdtty(void);
+void        caseec(void);
+void        caseeo(void);
+void        caseta(void);
+void        casene(void);
+void        casetr(void);
+void        casecu(void);
+void        caseul(void);
+void        caseuf(void);
+void        caseit(void);
+void        casemc(void);
+void        casemk(void);
+void        casesv(void);
+void        caseos(void);
+void        casenm(void);
+void        getnm(int *p, int min);
+void        casenn(void);
+void        caseab(void);
+void        save_tty(void);
+void        restore_tty(void);
+void        set_tty(void);
+void        echo_off(void);
+void        echo_on(void);
+
+/*
+ * t6.c
+ */
+int        t_width(Tchar j);
+void        zapwcache(int s);
+int        onfont(int n, int f);
+int        getcw(int i);
+void        xbits(Tchar i, int bitf);
+Tchar        t_setch(int c);
+Tchar        t_setabs(void);
+int        t_findft(int i);
+void        caseps(void);
+void        casps1(int i);
+int        findps(int i);
+void        t_mchbits(void);
+void        t_setps(void);
+Tchar        t_setht(void);
+Tchar        t_setslant(void);
+void        caseft(void);
+void        t_setfont(int a);
+void        t_setwd(void);
+Tchar        t_vmot(void);
+Tchar        t_hmot(void);
+Tchar        t_mot(void);
+Tchar        t_sethl(int k);
+Tchar        t_makem(int i);
+Tchar        getlg(Tchar i);
+void        caselg(void);
+void        casefp(void);
+char        *strdupl(const char *);
+int        setfp(int pos, int f, char *truename, int print);
+void        casecs(void);
+void        casebd(void);
+void        casevs(void);
+void        casess(void);
+Tchar        t_xlss(void);
+Uchar*        unpair(int i);
+void        outascii(Tchar i);
+
+/*
+ * c7.c
+ */
+void        tbreak(void);
+void        donum(void);
+void        text(void);
+void        nofill(void);
+void        callsp(void);
+void        ckul(void);
+void        storeline(Tchar c, int w);
+void        newline(int a);
+int        findn1(int a);
+void        chkpn(void);
+int        findt(int a);
+int        findt1(void);
+void        eject(Stack *a);
+int        movword(void);
+void        horiz(int i);
+void        setnel(void);
+int        getword(int x);
+void        storeword(Tchar c, int w);
+Tchar        gettch(void);
+
+/*
+ * c8.c
+ */
+void        hyphen(Tchar *wp);
+int        punct(Tchar i);
+int        alph(int i);
+void        caseha(void);
+void        caseht(void);
+void        casehw(void);
+int        exword(void);
+int        suffix(void);
+int        maplow(int i);
+int        vowel(int i);
+Tchar*        chkvow(Tchar *w);
+void        digram(void);
+int        dilook(int a, int b, char t[26][13]);
+
+/*
+ * c9.c
+ */
+Tchar        setz(void);
+void        setline(void);
+int        eat(int c);
+void        setov(void);
+void        setbra(void);
+void        setvline(void);
+void        setdraw(void);
+void        casefc(void);
+Tchar        setfield(int x);
+
+/*
+ * t10.c
+ */
+void        t_ptinit(void);
+void        t_specnames(void);
+void        t_ptout(Tchar i);
+int        ptout0(Tchar *pi);
+void        ptchname(int);
+void        ptflush(void);
+void        ptps(void);
+void        ptfont(void);
+void        ptfpcmd(int f, char *s, char *fn);
+void        t_ptlead(void);
+void        ptesc(void);
+void        ptpage(int n);
+void        pttrailer(void);
+void        ptstop(void);
+void        t_ptpause(void);
+
+/*
+ * t11.c
+ */
+int        getdesc(char *name);
+int        getfont(char *name, int pos);
+int        chadd(char *s, int, int);
+char*        chname(int n);
+int        getlig(FILE *fin);
+
+/*
+ * n6.c
+ */
+int        n_width(Tchar j);
+Tchar        n_setch(int c);
+Tchar        n_setabs(void);
+int        n_findft(int i);
+void        n_mchbits(void);
+void        n_setps(void);
+Tchar        n_setht(void);
+Tchar        n_setslant(void);
+void        n_caseft(void);
+void        n_setfont(int a);
+void        n_setwd(void);
+Tchar        n_vmot(void);
+Tchar        n_hmot(void);
+Tchar        n_mot(void);
+Tchar        n_sethl(int k);
+Tchar        n_makem(int i);
+void        n_casefp(void);
+void        n_casebd(void);
+void        n_casevs(void);
+Tchar        n_xlss(void);
+
+/*
+ * n10.c
+ */
+void        n_ptinit(void);
+char*        skipstr(char *s);
+char*        getstr(char *s, char *t);
+char*        getint(char *s, int *pn);
+void        twdone(void);
+void        n_specnames(void);
+int        findch(char *s);
+void        n_ptout(Tchar i);
+void        ptout1(void);
+char*        plot(char *x);
+void        move(void);
+void        n_ptlead(void);
+void        n_ptpause(void);
+
+/*
+ * indirect calls on TROFF/!TROFF.  these are variables!
+ */
+extern Tchar        (*hmot)(void);
+extern Tchar        (*makem)(int i);
+extern Tchar        (*setabs)(void);
+extern Tchar        (*setch)(int c);
+extern Tchar        (*sethl)(int k);
+extern Tchar        (*setht)(void);
+extern Tchar        (*setslant)(void);
+extern Tchar        (*vmot)(void);
+extern Tchar        (*xlss)(void);
+extern int        (*findft)(int i);
+extern int        (*width)(Tchar j);
+extern void        (*mchbits)(void);
+extern void        (*ptlead)(void);
+extern void        (*ptout)(Tchar i);
+extern void        (*ptpause)(void);
+extern void        (*setfont)(int a);
+extern void        (*setps)(void);
+extern void        (*setwd)(void);
diff --git a/troff/hytab.c b/troff/hytab.c
@@ -0,0 +1,126 @@
+/*
+ * Hyphenation digram tables
+ */
+
+typedef unsigned char Uchar;
+
+
+Uchar        bxh[26][13] = {
+        0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0040,0000,0040
+};
+
+Uchar        hxx[26][13] = {
+        0006,0042,0041,0123,0021,0024,0063,0042,0002,0043,0021,0001,0022,
+        0140,0000,0200,0003,0260,0006,0000,0160,0007,0000,0140,0000,0320,
+        0220,0000,0160,0005,0240,0010,0000,0100,0006,0000,0200,0000,0320,
+        0240,0000,0120,0003,0140,0000,0000,0240,0010,0000,0220,0000,0160,
+        0042,0023,0041,0040,0040,0022,0043,0041,0030,0064,0021,0000,0041,
+        0100,0000,0140,0000,0220,0006,0000,0140,0003,0000,0200,0000,0000,
+        0200,0000,0120,0002,0220,0010,0000,0160,0006,0000,0140,0000,0320,
+        0020,0000,0020,0000,0020,0000,0000,0020,0000,0000,0020,0000,0000,
+        0043,0163,0065,0044,0022,0043,0104,0042,0061,0146,0061,0000,0007,
+        0100,0000,0140,0000,0040,0000,0000,0100,0000,0000,0120,0000,0000,
+        0140,0000,0040,0011,0060,0004,0001,0120,0003,0000,0140,0000,0040,
+        0200,0000,0100,0000,0140,0000,0000,0140,0000,0000,0140,0000,0240,
+        0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0140,0000,0240,
+        0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0060,0000,0240,
+        0021,0043,0041,0121,0040,0023,0042,0003,0142,0042,0061,0001,0022,
+        0120,0000,0140,0010,0140,0010,0000,0140,0002,0000,0120,0000,0120,
+        0000,0000,0000,0000,0360,0000,0000,0000,0000,0000,0160,0000,0000,
+        0100,0000,0040,0005,0120,0000,0000,0100,0000,0000,0060,0000,0140,
+        0140,0040,0100,0001,0240,0041,0000,0242,0000,0002,0140,0000,0100,
+        0240,0000,0120,0002,0200,0000,0000,0320,0007,0000,0240,0000,0340,
+        0101,0021,0041,0020,0040,0005,0042,0121,0002,0021,0201,0000,0020,
+        0160,0000,0100,0000,0140,0000,0000,0160,0006,0000,0220,0000,0140,
+        0140,0000,0020,0001,0020,0000,0000,0100,0001,0000,0300,0000,0000,
+        0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+        0106,0041,0040,0147,0040,0000,0063,0041,0001,0102,0160,0002,0002,
+        0300,0000,0040,0017,0140,0017,0000,0240,0000,0000,0140,0000,0120
+};
+
+Uchar        bxxh[26][13] = {
+        0005,0150,0153,0062,0062,0246,0152,0127,0146,0203,0310,0017,0206,
+        0100,0000,0120,0000,0140,0000,0000,0100,0000,0000,0120,0000,0060,
+        0100,0000,0040,0000,0060,0000,0000,0060,0000,0000,0220,0000,0040,
+        0100,0000,0120,0000,0200,0000,0000,0100,0000,0000,0140,0000,0060,
+        0043,0142,0046,0140,0062,0147,0210,0131,0046,0106,0246,0017,0111,
+        0060,0000,0020,0000,0060,0000,0000,0040,0000,0000,0100,0000,0000,
+        0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0100,0000,0040,
+        0100,0000,0100,0000,0100,0000,0000,0040,0000,0000,0100,0000,0140,
+        0066,0045,0145,0140,0000,0070,0377,0030,0130,0103,0003,0017,0006,
+        0040,0000,0040,0000,0020,0000,0000,0040,0000,0000,0100,0000,0000,
+        0200,0000,0020,0000,0140,0000,0000,0120,0000,0000,0120,0000,0040,
+        0120,0000,0040,0000,0060,0000,0000,0060,0000,0000,0160,0000,0040,
+        0120,0000,0040,0000,0120,0000,0000,0040,0000,0000,0160,0000,0040,
+        0120,0000,0020,0000,0140,0000,0000,0120,0000,0000,0140,0000,0040,
+        0051,0126,0150,0140,0060,0210,0146,0006,0006,0165,0003,0017,0244,
+        0120,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0000,0140,
+        0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+        0140,0000,0140,0000,0060,0000,0000,0100,0000,0000,0140,0000,0020,
+        0120,0000,0020,0000,0060,0000,0000,0060,0000,0000,0060,0000,0040,
+        0140,0000,0020,0000,0100,0000,0000,0140,0000,0000,0140,0000,0020,
+        0070,0125,0051,0162,0120,0105,0126,0104,0006,0044,0000,0017,0052,
+        0140,0000,0020,0000,0140,0000,0000,0060,0000,0000,0060,0000,0040,
+        0020,0000,0000,0000,0020,0000,0000,0000,0000,0000,0000,0000,0060,
+        0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0000,0240,
+        0065,0042,0060,0200,0000,0210,0222,0146,0006,0204,0220,0012,0003,
+        0240,0000,0020,0000,0120,0000,0000,0200,0000,0000,0200,0000,0240
+};
+
+Uchar        xhx[26][13] = {
+        0032,0146,0042,0107,0076,0102,0042,0146,0202,0050,0006,0000,0051,
+        0036,0377,0057,0013,0057,0366,0377,0057,0001,0377,0057,0000,0040,
+        0037,0377,0020,0000,0100,0022,0377,0057,0362,0116,0100,0000,0017,
+        0057,0377,0057,0031,0137,0363,0377,0037,0362,0270,0077,0000,0117,
+        0074,0142,0012,0236,0076,0125,0063,0165,0341,0046,0047,0000,0024,
+        0020,0017,0075,0377,0040,0001,0377,0017,0001,0204,0020,0000,0040,
+        0057,0017,0057,0340,0140,0362,0314,0117,0003,0302,0100,0000,0057,
+        0057,0357,0077,0017,0100,0366,0314,0057,0342,0346,0037,0000,0060,
+        0252,0145,0072,0157,0377,0165,0063,0066,0164,0050,0363,0000,0362,
+        0000,0000,0020,0000,0020,0000,0000,0017,0000,0000,0020,0000,0000,
+        0117,0017,0237,0377,0200,0354,0125,0110,0004,0257,0000,0000,0300,
+        0057,0367,0054,0357,0157,0216,0314,0114,0217,0353,0053,0000,0057,
+        0077,0213,0077,0077,0177,0317,0377,0114,0377,0352,0077,0000,0076,
+        0077,0213,0077,0077,0157,0177,0377,0054,0377,0352,0117,0000,0075,
+        0125,0230,0065,0216,0057,0066,0063,0047,0345,0126,0011,0000,0033,
+        0057,0377,0051,0360,0120,0361,0273,0056,0001,0256,0057,0000,0060,
+        0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+        0076,0310,0056,0310,0137,0174,0273,0055,0335,0266,0033,0000,0155,
+        0077,0157,0057,0360,0057,0063,0042,0024,0077,0206,0020,0000,0040,
+        0057,0037,0077,0360,0100,0365,0377,0037,0362,0176,0050,0000,0026,
+        0167,0146,0042,0112,0077,0110,0062,0254,0366,0052,0377,0000,0163,
+        0060,0000,0040,0000,0120,0000,0377,0060,0012,0000,0037,0000,0257,
+        0037,0232,0157,0361,0040,0003,0125,0010,0001,0256,0000,0000,0340,
+        0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0017,0277,
+        0253,0315,0257,0216,0377,0206,0146,0306,0371,0126,0232,0000,0004,
+        0057,0012,0100,0360,0160,0360,0000,0040,0000,0017,0157,0000,0176
+};
+
+Uchar        xxh[26][13] = {
+        0045,0150,0154,0162,0042,0246,0210,0147,0152,0103,0230,0017,0206,
+        0100,0000,0040,0000,0140,0000,0000,0100,0000,0021,0120,0017,0060,
+        0100,0000,0040,0002,0140,0320,0000,0060,0000,0001,0220,0017,0040,
+        0100,0001,0120,0001,0241,0000,0000,0100,0000,0020,0140,0017,0060,
+        0023,0162,0046,0142,0022,0207,0210,0131,0052,0106,0250,0017,0110,
+        0060,0000,0042,0000,0160,0000,0000,0040,0000,0212,0100,0017,0000,
+        0140,0000,0040,0002,0140,0000,0000,0120,0000,0040,0120,0017,0040,
+        0100,0000,0100,0000,0140,0001,0021,0140,0000,0046,0100,0017,0140,
+        0066,0045,0025,0201,0020,0130,0146,0030,0130,0103,0025,0017,0006,
+        0100,0000,0040,0000,0020,0000,0000,0040,0000,0000,0200,0017,0000,
+        0200,0000,0020,0001,0140,0000,0000,0140,0000,0000,0120,0017,0040,
+        0120,0026,0042,0020,0140,0161,0042,0143,0000,0022,0162,0017,0040,
+        0121,0042,0060,0020,0140,0200,0000,0123,0000,0021,0220,0017,0041,
+        0121,0042,0060,0120,0140,0200,0000,0123,0000,0021,0160,0017,0041,
+        0051,0126,0150,0141,0060,0210,0146,0066,0026,0165,0026,0017,0247,
+        0120,0000,0040,0003,0160,0000,0000,0140,0000,0021,0100,0017,0140,
+        0000,0000,0000,0000,0200,0000,0000,0000,0000,0000,0000,0017,0000,
+        0141,0023,0122,0040,0160,0143,0042,0142,0000,0047,0143,0017,0020,
+        0120,0000,0040,0006,0140,0060,0000,0141,0000,0026,0100,0017,0040,
+        0140,0000,0020,0007,0100,0000,0000,0140,0000,0001,0140,0017,0020,
+        0110,0125,0051,0162,0120,0125,0127,0104,0006,0104,0000,0017,0052,
+        0140,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0017,0000,
+        0040,0005,0020,0000,0040,0313,0231,0030,0000,0140,0000,0017,0056,
+        0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0017,0240,
+        0065,0042,0060,0040,0000,0206,0231,0146,0006,0224,0220,0017,0004,
+        0240,0000,0020,0000,0140,0000,0000,0220,0000,0000,0200,0017,0141
+};
diff --git a/troff/mbwc.c b/troff/mbwc.c
@@ -0,0 +1,165 @@
+#include 
+
+/*
+ * Use the FSS-UTF transformation proposed by posix.
+ *        We define 7 byte types:
+ *        T0        0xxxxxxx        7 free bits
+ *        Tx        10xxxxxx        6 free bits
+ *        T1        110xxxxx        5 free bits
+ *        T2        1110xxxx        4 free bits
+ *
+ *        Encoding is as follows.
+ *        From hex        Thru hex        Sequence                Bits
+ *        00000000        0000007F        T0                        7
+ *        00000080        000007FF        T1 Tx                        11
+ *        00000800        0000FFFF        T2 Tx Tx                16
+ */
+
+int
+mblen(const char *s, size_t n)
+{
+
+        return mbtowc(0, s, n);
+}
+
+int
+mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+        int c, c1, c2;
+        long l;
+
+        if(!s)
+                return 0;
+
+        if(n < 1)
+                goto bad;
+        c = s[0] & 0xff;
+        if((c & 0x80) == 0x00) {
+                if(pwc)
+                        *pwc = c;
+                if(c == 0)
+                        return 0;
+                return 1;
+        }
+
+        if(n < 2)
+                goto bad;
+        c1 = (s[1] ^ 0x80) & 0xff;
+        if((c1 & 0xC0) != 0x00)
+                goto bad;
+        if((c & 0xE0) == 0xC0) {
+                l = ((c << 6) | c1) & 0x7FF;
+                if(l < 0x080)
+                        goto bad;
+                if(pwc)
+                        *pwc = l;
+                return 2;
+        }
+
+        if(n < 3)
+                goto bad;
+        c2 = (s[2] ^ 0x80) & 0xff;
+        if((c2 & 0xC0) != 0x00)
+                goto bad;
+        if((c & 0xF0) == 0xE0) {
+                l = ((((c << 6) | c1) << 6) | c2) & 0xFFFF;
+                if(l < 0x0800)
+                        goto bad;
+                if(pwc)
+                        *pwc = l;
+                return 3;
+        }
+
+        /*
+         * bad decoding
+         */
+bad:
+        return -1;
+
+}
+
+int
+wctomb(char *s, wchar_t wchar)
+{
+        long c;
+
+        if(!s)
+                return 0;
+
+        c = wchar & 0xFFFF;
+        if(c < 0x80) {
+                s[0] = c;
+                return 1;
+        }
+
+        if(c < 0x800) {
+                s[0] = 0xC0 | (c >> 6);
+                s[1] = 0x80 | (c & 0x3F);
+                return 2;
+        }
+
+        s[0] = 0xE0 |  (c >> 12);
+        s[1] = 0x80 | ((c >> 6) & 0x3F);
+        s[2] = 0x80 |  (c & 0x3F);
+        return 3;
+}
+
+size_t
+mbstowcs(wchar_t *pwcs, const char *s, size_t n)
+{
+        int i, d, c;
+
+        for(i=0; i < n; i++) {
+                c = *s & 0xff;
+                if(c < 0x80) {
+                        *pwcs = c;
+                        if(c == 0)
+                                break;
+                        s++;
+                } else {
+                        d = mbtowc(pwcs, s, 3);
+                        if(d <= 0)
+                                return (size_t)((d<0) ? -1 : i);
+                        s += d;
+                }
+                pwcs++;
+        }
+        return i;
+}
+
+size_t
+wcstombs(char *s, const wchar_t *pwcs, size_t n)
+{
+        int d;
+        long c;
+        char *p, *pe;
+        char buf[3];
+
+        p = s;
+        pe = p+n-3;
+        while(p < pe) {
+                c = *pwcs++;
+                if(c < 0x80)
+                        *p++ = c;
+                else
+                        p += wctomb(p, c);
+                if(c == 0)
+                        return p-s;
+        }
+        while(p < pe+3) {
+                c = *pwcs++;
+                d = wctomb(buf, c);
+                if(p+d <= pe+3) {
+                        *p++ = buf[0];
+                        if(d > 1) {
+                                *p++ = buf[2];
+                                if(d > 2)
+                                        *p++ = buf[3];
+                        }
+                }
+                if(c == 0)
+                        break;
+        }
+        return p-s;
+}
+
diff --git a/troff/mkfile b/troff/mkfile
@@ -0,0 +1,57 @@
+<$PLAN9/src/mkhdr
+
+TARG=troff
+OFILES=n1.$O\
+        n2.$O\
+        n3.$O\
+        n4.$O\
+        n5.$O\
+        t6.$O\
+        n6.$O\
+        n7.$O\
+        n8.$O\
+        n9.$O\
+        t10.$O\
+        n10.$O\
+        t11.$O\
+        ni.$O\
+        hytab.$O\
+        suftab.$O\
+        dwbinit.$O\
+        mbwc.$O
+
+HFILES=tdef.h\
+        fns.h\
+        ext.h\
+        dwbinit.h\
+
+
+<$PLAN9/src/mkone
+CFLAGS=-DUNICODE
+
+TMACDIR='"tmac/tmac."'
+FONTDIR='"troff/font"'
+NTERMDIR='"troff/term/tab."'
+ALTHYPHENS='"lib/hyphen.tex"'
+TEXHYPHENS='"#9/lib/hyphen.tex"'
+DWBHOME='"#9/"'
+TDEVNAME='"utf"'
+NDEVNAME='"utf"'
+
+ni.$O:        ni.c $HFILES
+        $CC $CFLAGS -DTMACDIR=$TMACDIR ni.c
+
+t10.$O:        t10.c $HFILES
+        $CC $CFLAGS -DTDEVNAME=$TDEVNAME t10.c
+
+n1.$O:        n1.c $HFILES
+        $CC $CFLAGS -DFONTDIR=$FONTDIR -DNTERMDIR=$NTERMDIR -DTEXHYPHENS=$TEXHYPHENS -DALTHYPHENS=$ALTHYPHENS -DDWBHOME=$DWBHOME n1.c
+
+n10.$O:        n10.c $HFILES
+        $CC $CFLAGS -DTDEVNAME=$NDEVNAME n10.c
+
+n8.$O:        n8.c $HFILES
+        $CC $CFLAGS -DTEXHYPHENS=$TEXHYPHENS n8.c
+
+dwbinit.$O:        dwbinit.c
+        $CC $CFLAGS -DDWBHOME=$DWBHOME dwbinit.c
diff --git a/troff/n1.c b/troff/n1.c
@@ -0,0 +1,1134 @@
+/*
+ * n1.c
+ *
+ *        consume options, initialization, main loop,
+ *        input routines, escape function calling
+ */
+
+#include 
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+#include "dwbinit.h"
+
+#include 
+#include 
+
+char        *Version        = "March 11, 1994";
+
+#ifndef DWBVERSION
+#define DWBVERSION      "???"
+#endif
+
+char        *DWBfontdir = FONTDIR;
+char        *DWBntermdir = NTERMDIR;
+char        *DWBalthyphens = ALTHYPHENS;
+char        *DWBhomedir = "";
+
+dwbinit dwbpaths[] = {
+        &DWBfontdir, NULL, 0,
+        &DWBntermdir, NULL, 0,
+        &DWBalthyphens, NULL, 0,
+        &DWBhomedir, NULL, 0,
+        NULL, nextf, NS,
+        NULL, NULL, 0
+};
+
+int        TROFF        = 1;        /* assume we started in troff... */
+
+jmp_buf sjbuf;
+Offset        ipl[NSO];
+
+static        FILE        *ifile;
+static        FILE        *ifl[NSO];        /* open input file pointers */
+char        cfname[NSO+1][NS] = {  "stdin" };        /* file name stack */
+int        cfline[NSO];                /* input line count stack */
+char        *progname;                /* program name (troff or nroff) */
+
+int        trace = 0;        /* tracing mode: default off */
+int        trace1 = 0;
+
+int
+main(int argc, char *argv[])
+{
+        char *p;
+        int j;
+        Tchar i;
+        char buf[100];
+
+        ifile = stdin;                /* gcc */
+        ptid = stdout;
+
+        buf[0] = '\0';                /* make sure it's empty (silly 3b2) */
+        progname = argv[0];
+        if ((p = strrchr(progname, '/')) == NULL)
+                p = progname;
+        else
+                p++;
+        DWBinit(progname, dwbpaths);
+        if (strcmp(p, "nroff") == 0)
+                TROFF = 0;
+#ifdef UNICODE
+        alphabet = 128;        /* unicode for plan 9 */
+#endif        /*UNICODE*/
+        mnspace();
+        nnspace();
+        mrehash();
+        nrehash();
+        numtabp[NL].val = -1;
+
+        while (--argc > 0 && (++argv)[0][0] == '-')
+                switch (argv[0][1]) {
+
+                case 'N':        /* ought to be used first... */
+                        TROFF = 0;
+                        break;
+                case 'd':
+                        fprintf(stderr, "troff/nroff version %s\n", Version);
+                        break;
+                case 'F':        /* switch font tables from default */
+                        if (argv[0][2] != '\0') {
+                                strcpy(termtab, &argv[0][2]);
+                                strcpy(fontdir, &argv[0][2]);
+                        } else {
+                                argv++; argc--;
+                                strcpy(termtab, argv[0]);
+                                strcpy(fontdir, argv[0]);
+                        }
+                        break;
+                case 0:
+                        goto start;
+                case 'i':
+                        stdi++;
+                        break;
+                case 'n':
+                        npn = atoi(&argv[0][2]);
+                        break;
+                case 'u':        /* set emboldening amount */
+                        bdtab[3] = atoi(&argv[0][2]);
+                        if (bdtab[3] < 0 || bdtab[3] > 50)
+                                bdtab[3] = 0;
+                        break;
+                case 's':
+                        if (!(stop = atoi(&argv[0][2])))
+                                stop++;
+                        break;
+                case 'r':
+                        sprintf(buf + strlen(buf), ".nr %c %s\n",
+                                argv[0][2], &argv[0][3]);
+                        /* not yet cpushback(buf);*/
+                        /* dotnr(&argv[0][2], &argv[0][3]); */
+                        break;
+                case 'm':
+                        if (mflg++ >= NMF) {
+                                ERROR "Too many macro packages: %s", argv[0] WARN;
+                                break;
+                        }
+                        strcpy(mfiles[nmfi], nextf);
+                        strcat(mfiles[nmfi++], &argv[0][2]);
+                        break;
+                case 'o':
+                        getpn(&argv[0][2]);
+                        break;
+                case 'T':
+                        strcpy(devname, &argv[0][2]);
+                        dotT++;
+                        break;
+                case 'a':
+                        ascii = 1;
+                        break;
+                case 'h':
+                        hflg++;
+                        break;
+                case 'e':
+                        eqflg++;
+                        break;
+                case 'q':
+                        quiet++;
+                        save_tty();
+                        break;
+                case 'V':
+                        fprintf(stdout, "%croff: DWB %s\n", 
+                                        TROFF ? 't' : 'n', DWBVERSION);
+                        exit(0);
+                case 't':
+                        if (argv[0][2] != '\0')
+                                trace = trace1 = argv[0][2];
+                        break;                /* for the sake of compatibility */
+                default:
+                        ERROR "unknown option %s", argv[0] WARN;
+                        done(02);
+                }
+
+start:
+        /*
+         * cpushback maintains a LIFO, so push pack the -r arguments
+         * in reverse order to maintain a FIFO in case someone did -rC1 -rC3
+         */
+        if (buf[0]) {
+                char *p = buf;
+                while(*p++)
+                        ;
+                while(p > buf) {
+                        while(strncmp(p, ".nr", 3) != 0)
+                                p--;
+                        cpushback(p);
+                        *p-- = '\0';
+                }
+        }
+        argp = argv;
+        rargc = argc;
+        nmfi = 0;
+        init2();
+        setjmp(sjbuf);
+loop:
+        copyf = lgf = nb = nflush = nlflg = 0;
+        if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl && dip == d) {
+                nflush++;
+                trap = 0;
+                eject((Stack *)0);
+                goto loop;
+        }
+        i = getch();
+        if (pendt)
+                goto Lt;
+        if ((j = cbits(i)) == XPAR) {
+                copyf++;
+                tflg++;
+                while (cbits(i) != '\n')
+                        pchar(i = getch());
+                tflg = 0;
+                copyf--;
+                goto loop;
+        }
+        if (j == cc || j == c2) {
+                if (j == c2)
+                        nb++;
+                copyf++;
+                while ((j = cbits(i = getch())) == ' ' || j == '\t')
+                        ;
+                ch = i;
+                copyf--;
+                control(getrq(), 1);
+                flushi();
+                goto loop;
+        }
+Lt:
+        ch = i;
+        text();
+        if (nlflg)
+                numtabp[HP].val = 0;
+        goto loop;
+}
+
+
+
+void init2(void)
+{
+        int i;
+        char buf[100];
+
+        for (i = NTRTAB; --i; )
+                trtab[i] = i;
+        trtab[UNPAD] = ' ';
+        iflg = 0;
+        obufp = obuf;
+        if (TROFF)
+                t_ptinit();
+        else
+                n_ptinit();
+        mchbits();
+        cvtime();
+        numtabp[PID].val = getpid();
+        numtabp[HP].val = init = 0;
+        numtabp[NL].val = -1;
+        nfo = 0;
+        copyf = raw = 0;
+        sprintf(buf, ".ds .T %s\n", devname);
+        cpushback(buf);
+        sprintf(buf, ".ds .P %s\n", DWBhomedir);
+        cpushback(buf);
+        numtabp[CD].val = -1;        /* compensation */
+        nx = mflg;
+        frame = stk = (Stack *)setbrk(STACKSIZE);
+        dip = &d[0];
+        nxf = frame + 1;
+        for (i = 1; i < NEV; i++)        /* propagate the environment */
+                envcopy(&env[i], &env[0]);
+        for (i = 0; i < NEV; i++) {
+                if ((env[i]._word._bufp = (Tchar *)calloc(WDSIZE, sizeof(Tchar))) == NULL) {
+                        ERROR "not enough room for word buffers" WARN;
+                        done2(1);
+                }
+                env[i]._word._size = WDSIZE;
+                if ((env[i]._line._bufp = (Tchar *)calloc(LNSIZE, sizeof(Tchar))) == NULL) {
+                        ERROR "not enough room for line buffers" WARN;
+                        done2(1);
+                }
+                env[i]._line._size = LNSIZE;
+        }
+        if ((oline = (Tchar *)calloc(OLNSIZE, sizeof(Tchar))) == NULL) {
+                ERROR "not enough room for line buffers" WARN;
+                done2(1);
+        }
+        olinep = oline;
+        olnsize = OLNSIZE;
+        blockinit();
+}
+
+void cvtime(void)
+{
+        time_t tt;
+        struct tm *ltime;
+
+        time(&tt);
+        ltime = localtime(&tt);
+        numtabp[YR].val = ltime->tm_year % 100;
+        numtabp[YR].fmt = 2;
+        numtabp[MO].val = ltime->tm_mon + 1;        /* troff uses 1..12 */
+        numtabp[DY].val = ltime->tm_mday;
+        numtabp[DW].val = ltime->tm_wday + 1;        /* troff uses 1..7 */
+}
+
+
+
+char        errbuf[200];
+
+void errprint(void)        /* error message printer */
+{
+        int savecd = numtabp[CD].val;
+
+        if (!nlflg)
+                numtabp[CD].val++;
+
+        fprintf(stderr, "%s: ", progname);
+        fputs(errbuf, stderr);
+        if (cfname[ifi][0])
+                fprintf(stderr, "; %s:%d", cfname[ifi], numtabp[CD].val);
+        fputs("\n", stderr);
+        if (cfname[ifi][0])
+                stackdump();
+        numtabp[CD].val = savecd;
+}
+
+
+int control(int a, int b)
+{
+        int j, k;
+        extern Contab *contabp;
+
+        numerr.type = RQERR;
+        numerr.req = a;
+        if (a == 0 || (j = findmn(a)) == -1)
+                return(0);
+        if (contabp[j].f == 0) {
+                if (trace & TRMAC)
+                        fprintf(stderr, "invoke macro %s\n", unpair(a));
+                if (dip != d)
+                        for (k = dilev; k; k--)
+                                if (d[k].curd == a) {
+                                        ERROR "diversion %s invokes itself during diversion",
+                                                                unpair(a) WARN;
+                                        edone(0100);
+                                }
+                nxf->nargs = 0;
+                if (b)
+                        collect();
+                flushi();
+                return pushi(contabp[j].mx, a);        /* BUG??? all that matters is 0/!0 */
+        }
+        if (b) {
+                if (trace & TRREQ)
+                        fprintf(stderr, "invoke request %s\n", unpair(a));
+                 (*contabp[j].f)();
+        }
+        return(0);
+}
+
+void casept(void)
+{
+        int i;
+
+        noscale++;
+        if (skip())
+                i = trace1;
+        else {
+                i = max(inumb(&trace), 0);
+                if (nonumb)
+                        i = trace1;
+        }
+        trace1 = trace;
+        trace = i;
+        noscale = 0;
+}
+
+
+int getrq(void)
+{
+        int i, j;
+
+        if ((i = getach()) == 0 || (j = getach()) == 0)
+                goto rtn;
+        i = PAIR(i, j);
+rtn:
+        return(i);
+}
+
+/*
+ * table encodes some special characters, to speed up tests
+ * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
+ */
+
+char gchtab[NCHARS] = {
+        000,004,000,000,010,000,000,000, /* fc, ldr */
+        001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
+        000,000,000,000,000,000,000,000,
+        000,001,000,001,000,000,000,000, /* FLSS, ESC */
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,001,000, /* f */
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000,
+        000,000,000,000,000,000,000,000
+};
+
+int realcbits(Tchar c)        /* return character bits, or MOTCH if motion */
+{
+        if (ismot(c))
+                return MOTCH;
+        else
+                return c & 0xFFFF;
+}
+
+Tchar getch(void)
+{
+        int k;
+        Tchar i, j;
+
+g0:
+        if (ch) {
+                i = ch;
+                if (cbits(i) == '\n')
+                        nlflg++;
+                ch = 0;
+                return(i);
+        }
+
+        if (nlflg)
+                return('\n');
+        i = getch0();
+        if (ismot(i))
+                return(i);
+        k = cbits(i);
+        if (k >= sizeof(gchtab)/sizeof(gchtab[0]) || gchtab[k] == 0)        /* nothing special */
+                return(i);
+        if (k != ESC) {
+                if (k == '\n') {
+                        nlflg++;
+                        if (ip == 0)
+                                numtabp[CD].val++; /* line number */
+                        return(k);
+                }
+                if (k == FLSS) {
+                        copyf++; 
+                        raw++;
+                        i = getch0();
+                        if (!fi)
+                                flss = i;
+                        copyf--; 
+                        raw--;
+                        goto g0;
+                }
+                if (k == RPT) {
+                        setrpt();
+                        goto g0;
+                }
+                if (!copyf) {
+                        if (k == 'f' && lg && !lgf) {
+                                i = getlg(i);
+                                return(i);
+                        }
+                        if (k == fc || k == tabch || k == ldrch) {
+                                if ((i = setfield(k)) == 0)
+                                        goto g0; 
+                                else 
+                                        return(i);
+                        }
+                        if (k == '\b') {
+                                i = makem(-width(' ' | chbits));
+                                return(i);
+                        }
+                }
+                return(i);
+        }
+
+        k = cbits(j = getch0());
+        if (ismot(j))
+                return(j);
+
+        switch (k) {
+        case 'n':        /* number register */
+                setn();
+                goto g0;
+        case '$':        /* argument indicator */
+                seta();
+                goto g0;
+        case '*':        /* string indicator */
+                setstr();
+                goto g0;
+        case '{':        /* LEFT */
+                i = LEFT;
+                goto gx;
+        case '}':        /* RIGHT */
+                i = RIGHT;
+                goto gx;
+        case '"':        /* comment */
+                while (cbits(i = getch0()) != '\n')
+                        ;
+                if (ip == 0)
+                        numtabp[CD].val++; /* line number */
+                nlflg++;
+                return(i);
+
+/* experiment: put it here instead of copy mode */
+        case '(':        /* special char name \(xx */
+        case 'C':        /*                 \C'...' */
+                if ((i = setch(k)) == 0)
+                        goto g0;
+                goto gx;
+
+        case ESC:        /* double backslash */
+                i = eschar;
+                goto gx;
+        case 'e':        /* printable version of current eschar */
+                i = PRESC;
+                goto gx;
+        case '\n':        /* concealed newline */
+                numtabp[CD].val++;
+                goto g0;
+        case ' ':        /* unpaddable space */
+                i = UNPAD;
+                goto gx;
+        case '\'':        /* \(aa */
+                i = ACUTE;
+                goto gx;
+        case '`':        /* \(ga */
+                i = GRAVE;
+                goto gx;
+        case '_':        /* \(ul */
+                i = UNDERLINE;
+                goto gx;
+        case '-':        /* current font minus */
+                i = MINUS;
+                goto gx;
+        case '&':        /* filler */
+                i = FILLER;
+                goto gx;
+        case 'c':        /* to be continued */
+                i = CONT;
+                goto gx;
+        case '!':        /* transparent indicator */
+                i = XPAR;
+                goto gx;
+        case 't':        /* tab */
+                i = '\t';
+                return(i);
+        case 'a':        /* leader (SOH) */
+/* old:                *pbp++ = LEADER; goto g0; */
+                i = LEADER;
+                return i;
+        case '%':        /* ohc */
+                i = OHC;
+                return(i);
+        case 'g':        /* return format of a number register */
+                setaf();        /* should this really be in copy mode??? */
+                goto g0;
+        case '.':        /* . */
+                i = '.';
+gx:
+                setsfbits(i, sfbits(j));
+                return(i);
+        }
+        if (copyf) {
+                *pbp++ = j;
+                return(eschar);
+        }
+        switch (k) {
+
+        case 'f':        /* font indicator */
+                setfont(0);
+                goto g0;
+        case 's':        /* size indicator */
+                setps();
+                goto g0;
+        case 'v':        /* vert mot */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                if (i = vmot()) {
+                        return(i);
+                }
+                goto g0;
+        case 'h':         /* horiz mot */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                if (i = hmot())
+                        return(i);
+                goto g0;
+        case '|':        /* narrow space */
+                if (NROFF)
+                        goto g0;
+                return(makem((int)(EM)/6));
+        case '^':        /* half narrow space */
+                if (NROFF)
+                        goto g0;
+                return(makem((int)(EM)/12));
+        case 'w':        /* width function */
+                setwd();
+                goto g0;
+        case 'p':        /* spread */
+                spread++;
+                goto g0;
+        case 'N':        /* absolute character number */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                if ((i = setabs()) == 0)
+                        goto g0;
+                return i;
+        case 'H':        /* character height */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                return(setht());
+        case 'S':        /* slant */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                return(setslant());
+        case 'z':        /* zero with char */
+                return(setz());
+        case 'l':        /* hor line */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                setline();
+                goto g0;
+        case 'L':        /* vert line */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                setvline();
+                goto g0;
+        case 'D':        /* drawing function */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                setdraw();
+                goto g0;
+        case 'X':        /* \X'...' for copy through */
+                setxon();
+                goto g0;
+        case 'b':        /* bracket */
+                setbra();
+                goto g0;
+        case 'o':        /* overstrike */
+                setov();
+                goto g0;
+        case 'k':        /* mark hor place */
+                if ((k = findr(getsn())) != -1) {
+                        numtabp[k].val = numtabp[HP].val;
+                }
+                goto g0;
+        case '0':        /* number space */
+                return(makem(width('0' | chbits)));
+        case 'x':        /* extra line space */
+                numerr.type = numerr.escarg = 0; numerr.esc = k;
+                if (i = xlss())
+                        return(i);
+                goto g0;
+        case 'u':        /* half em up */
+        case 'r':        /* full em up */
+        case 'd':        /* half em down */
+                return(sethl(k));
+        default:
+                return(j);
+        }
+        /* NOTREACHED */
+}
+
+void setxon(void)        /* \X'...' for copy through */
+{
+        Tchar xbuf[NC];
+        Tchar *i;
+        Tchar c;
+        int delim, k;
+
+        if (ismot(c = getch()))
+                return;
+        delim = cbits(c);
+        i = xbuf;
+        *i++ = XON | chbits;
+        while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) {
+                if (k == ' ')
+                        setcbits(c, WORDSP);
+                *i++ = c | ZBIT;
+        }
+        *i++ = XOFF | chbits;
+        *i = 0;
+        pushback(xbuf);
+}
+
+
+char        ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 };
+
+Tchar getch0(void)
+{
+        Tchar i;
+
+again:
+        if (pbp > lastpbp)
+                i = *--pbp;
+        else if (ip) {
+                /* i = rbf(); */
+                i = rbf0(ip);
+                if (i == 0)
+                        i = rbf();
+                else {
+                        ++ip;
+                        if (pastend(ip)) {
+                                --ip;
+                                rbf();
+                        }
+                }
+        } else {
+                if (donef || ndone)
+                        done(0);
+                if (nx || 1) {        /* BUG: was ibufp >= eibuf, so EOF test is wrong */
+                        if (nfo < 0)
+                                ERROR "in getch0, nfo = %d", nfo WARN;
+                        if (nfo == 0) {
+g0:
+                                if (nextfile()) {
+                                        if (ip)
+                                                goto again;
+                                }
+                        }
+                        nx = 0;
+#ifdef UNICODE
+                        if (MB_CUR_MAX > 1)
+                                i = get1ch(ifile);
+                        else
+#endif        /*UNICODE*/
+                                i = getc(ifile);
+                        if (i == EOF)
+                                goto g0;
+                        if (ip)
+                                goto again;
+                }
+/*g2: */
+                if (i >= 040)                        /* zapped: && i < 0177 */
+                        goto g4;
+                i = ifilt[i];
+        }
+        if (cbits(i) == IMP && !raw)
+                goto again;
+        if (i == 0 && !init && !raw) {                /* zapped:  || i == 0177 */
+                goto again;
+        }
+g4:
+        if (ismot(i))
+                return i;
+        if (copyf == 0 && sfbits(i) == 0)
+                i |= chbits;
+        if (cbits(i) == eschar && !raw)
+                setcbits(i, ESC);
+        return(i);
+}
+
+
+#ifdef UNICODE
+Tchar get1ch(FILE *fp)        /* get one "character" from input, figure out what alphabet */
+{
+        wchar_t wc;
+        char buf[100], *p;
+        int i, n, c;
+
+        for (i = 0, p = buf; i < MB_CUR_MAX; i++) {
+                if ((c = getc(fp)) == EOF)
+                        return c;
+                *p++ = c;
+                if ((n = mbtowc(&wc, buf, p-buf)) >= 0)
+                        break;
+        }
+
+        if (n == 1)        /* real ascii, presumably */
+                return wc;
+        if (n == 0)
+                return p[-1];        /* illegal, but what else to do? */
+        if (c == EOF)
+                return EOF;
+        *p = 0;
+        return chadd(buf, MBchar, Install);        /* add name even if haven't seen it */
+}
+#endif        /*UNICODE*/
+
+void pushback(Tchar *b)
+{
+        Tchar *ob = b;
+
+        while (*b++)
+                ;
+        b--;
+        while (b > ob && pbp < &pbbuf[NC-3])
+                *pbp++ = *--b;
+        if (pbp >= &pbbuf[NC-3]) {
+                ERROR "pushback overflow" WARN;
+                done(2);
+        }
+}
+
+void cpushback(char *b)
+{
+        char *ob = b;
+
+        while (*b++)
+                ;
+        b--;
+        while (b > ob && pbp < &pbbuf[NC-3])
+                *pbp++ = *--b;
+        if (pbp >= &pbbuf[NC-3]) {
+                ERROR "cpushback overflow" WARN;
+                done(2);
+        }
+}
+
+int nextfile(void)
+{
+        char *p;
+
+n0:
+        if (ifile != stdin)
+                fclose(ifile);
+        if (ifi > 0 && !nx) {
+                if (popf())
+                        goto n0; /* popf error */
+                return(1);         /* popf ok */
+        }
+        if (nx || nmfi < mflg) {
+                p = mfiles[nmfi++];
+                if (*p != 0)
+                        goto n1;
+        }
+        if (rargc-- <= 0) {
+                if ((nfo -= mflg) && !stdi) {
+                        done(0);
+}
+                nfo++;
+                numtabp[CD].val = stdi = mflg = 0;
+                ifile = stdin;
+                strcpy(cfname[ifi], "stdin");
+                return(0);
+        }
+        p = (argp++)[0];
+        if (rargc >= 0)
+                cfname[ifi][0] = 0;
+n1:
+        numtabp[CD].val = 0;
+        if (p[0] == '-' && p[1] == 0) {
+                ifile = stdin;
+                strcpy(cfname[ifi], "stdin");
+        } else if ((ifile = fopen(unsharp(p), "r")) == NULL) {
+                ERROR "cannot open file %s", p WARN;
+                nfo -= mflg;
+                done(02);
+        } else
+                strcpy(cfname[ifi],p);
+        nfo++;
+        return(0);
+}
+
+int
+popf(void)
+{
+        --ifi;
+        if (ifi < 0) {
+                ERROR "popf went negative" WARN;
+                return 1;
+        }
+        numtabp[CD].val = cfline[ifi];        /* restore line counter */
+        ip = ipl[ifi];                        /* input pointer */
+        ifile = ifl[ifi];                /* input FILE * */
+        return(0);
+}
+
+
+void flushi(void)
+{
+        if (nflush)
+                return;
+        ch = 0;
+        copyf++;
+        while (!nlflg) {
+                if (donef && frame == stk)
+                        break;
+                getch();
+        }
+        copyf--;
+}
+
+/*
+ * return 16-bit, ascii/alphabetic character, ignore chars with more bits,
+ * (internal names), spaces and special cookies (below 040).
+ * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff.
+ */
+int
+getach(void)
+{
+        Tchar i;
+        int j;
+
+        lgf++;
+        j = cbits(i = getch());
+        if (ismot(i)
+            || j > SHORTMASK
+            || (j <= 040 && j != 002        /*STX*/
+                        && j != 003        /*ETX*/
+                        && j != 005        /*ENQ*/
+                        && j != 006        /*ACK*/
+                        && j != 007)) {        /*BELL*/
+                ch = i;
+                j = 0;
+        }
+        lgf--;
+        return j;
+}
+
+
+void casenx(void)
+{
+        lgf++;
+        skip();
+        getname();
+        nx++;
+        if (nmfi > 0)
+                nmfi--;
+        strcpy(mfiles[nmfi], nextf);
+        nextfile();
+        nlflg++;
+        ip = 0;
+        pendt = 0;
+        frame = stk;
+        nxf = frame + 1;
+}
+
+int
+getname(void)
+{
+        int j, k;
+
+        lgf++;
+        for (k = 0; k < NS - 1; k++) {
+                j = getach();
+                if (!j)
+                        break;
+                nextf[k] = j;
+        }
+        nextf[k] = 0;
+        lgf--;
+        return(nextf[0]);
+}
+
+
+void caseso(void)
+{
+        FILE *fp = 0;
+
+        lgf++;
+        nextf[0] = 0;
+        if (skip() || !getname() || (fp = fopen(unsharp(nextf), "r")) == NULL || ifi >= NSO) {
+                ERROR "can't open file %s", nextf WARN;
+                done(02);
+        }
+        strcpy(cfname[ifi+1], nextf);
+        cfline[ifi] = numtabp[CD].val;                /*hold line counter*/
+        numtabp[CD].val = 0;
+        flushi();
+        ifl[ifi] = ifile;
+        ifile = fp;
+        ipl[ifi] = ip;
+        ip = 0;
+        nx++;
+        nflush++;
+        ifi++;
+}
+
+void caself(void)        /* set line number and file */
+{
+        int n;
+
+        if (skip())
+                return;
+        n = atoi0();
+        if (!nonumb)
+                cfline[ifi] = numtabp[CD].val = n - 1;
+        if (!skip())
+                if (getname()) {        /* eats '\n' ? */
+                        strcpy(cfname[ifi], nextf);
+                        if (!nonumb)
+                                numtabp[CD].val--;
+                }
+}
+
+void cpout(FILE *fin, char *token)
+{
+        int n;
+        char buf[1024];
+
+        if (token) {        /* BUG: There should be no NULL bytes in input */
+                char *newl = buf;
+                while ((fgets(buf, sizeof buf, fin)) != NULL) {
+                        if (newl) {
+                                numtabp[CD].val++; /* line number */
+                                if (strcmp(token, buf) == 0)
+                                        return;
+                        }
+                        newl = strchr(buf, '\n');
+                        fputs(buf, ptid);
+                }
+        } else {
+                while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0)
+                        fwrite(buf, n, 1, ptid);
+                fclose(fin);
+        }
+}
+
+void casecf(void)
+{        /* copy file without change */
+        FILE *fd;
+        char *eof, *p;
+        extern int hpos, esc, po;
+
+        /* this may not make much sense in nroff... */
+
+        lgf++;
+        nextf[0] = 0;
+        if (!skip() && getname()) {
+                if (strncmp("<<", nextf, 2) != 0) {
+                        if ((fd = fopen(unsharp(nextf), "r")) == NULL) {
+                                ERROR "can't open file %s", nextf WARN;
+                                done(02);
+                        }
+                        eof = (char *) NULL;
+                } else {        /* current file */
+                        if (pbp > lastpbp || ip) {
+                                ERROR "casecf: not reading from file" WARN;
+                                done(02);
+                        }
+                        eof = &nextf[2];
+                        if (!*eof)  {
+                                ERROR "casecf: missing end of input token" WARN;
+                                done(02);
+                        }
+                        p = eof;
+                        while(*++p)
+                                ;
+                        *p++ = '\n';
+                        *p = 0;
+                        fd = ifile;
+                }
+        } else {
+                ERROR "casecf: no argument" WARN;
+                lgf--;
+                return;
+        }
+        lgf--;
+
+        /* make it into a clean state, be sure that everything is out */
+        tbreak();
+        hpos = po;
+        esc = 0;
+        ptesc();        /* to left margin */
+        esc = un;
+        ptesc();
+        ptlead();
+        ptps();
+        ptfont();
+        flusho();
+        cpout(fd, eof);
+        ptps();
+        ptfont();
+}
+
+void getline(char *s, int n)        /* get rest of input line into s */
+{
+        int i;
+
+        lgf++;
+        copyf++;
+        skip();
+        for (i = 0; i < n-1; i++)
+                if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT)
+                        break;
+        s[i] = 0;
+        copyf--;
+        lgf--;
+}
+
+void casesy(void)        /* call system */
+{
+        char sybuf[NTM];
+
+        getline(sybuf, NTM);
+        system(sybuf);
+}
+
+
+void getpn(char *a)
+{
+        int n, neg;
+
+        if (*a == 0)
+                return;
+        neg = 0;
+        for ( ; *a; a++)
+                switch (*a) {
+                case '+':
+                case ',':
+                        continue;
+                case '-':
+                        neg = 1;
+                        continue;
+                default:
+                        n = 0;
+                        if (isdigit((uchar)*a)) {
+                                do
+                                        n = 10 * n + *a++ - '0';
+                                while (isdigit((uchar)*a));
+                                a--;
+                        } else
+                                n = 9999;
+                        *pnp++ = neg ? -n : n;
+                        neg = 0;
+                        if (pnp >= &pnlist[NPN-2]) {
+                                ERROR "too many page numbers" WARN;
+                                done3(-3);
+                        }
+                }
+        if (neg)
+                *pnp++ = -9999;
+        *pnp = -INT_MAX;
+        print = 0;
+        pnp = pnlist;
+        if (*pnp != -INT_MAX)
+                chkpn();
+}
+
+
+void setrpt(void)
+{
+        Tchar i, j;
+
+        copyf++;
+        raw++;
+        i = getch0();
+        copyf--;
+        raw--;
+        if ((long) i < 0 || cbits(j = getch0()) == RPT)
+                return;
+        while (i > 0 && pbp < &pbbuf[NC-3]) {
+                i--;
+                *pbp++ = j;
+        }
+}
diff --git a/troff/n10.c b/troff/n10.c
@@ -0,0 +1,549 @@
+/*
+n10.c
+
+Device interfaces
+*/
+
+#include 
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+#include 
+
+Term        t;        /* terminal characteristics */
+
+int        dtab;
+int        plotmode;
+int        esct;
+
+enum        { Notype = 0, Type = 1 };
+
+static char *parse(char *s, int typeit)        /* convert \0, etc to nroff driving table format */
+{                /* typeit => add a type id to the front for later use */
+        static char buf[100], *t, *obuf;
+        int quote = 0;
+        wchar_t wc;
+
+        obuf = typeit == Type ? buf : buf+1;
+#ifdef UNICODE
+        if (mbtowc(&wc, s, strlen(s)) > 1) {        /* it's multibyte, */
+                buf[0] = MBchar;
+                strcpy(buf+1, s);
+                return obuf;
+        }                        /* so just hand it back */
+#endif        /*UNICODE*/
+        buf[0] = Troffchar;
+        t = buf + 1;
+        if (*s == '"') {
+                s++;
+                quote = 1;
+        }
+        for (;;) {
+                if (quote && *s == '"') {
+                        s++;
+                        break;
+                }
+                if (!quote && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\0'))
+                        break;
+                if (*s != '\\')
+                        *t++ = *s++;
+                else {
+                        s++;        /* skip \\ */
+                        if (isdigit((uchar)s[0]) && isdigit((uchar)s[1]) && isdigit((uchar)s[2])) {
+                                *t++ = (s[0]-'0')<<6 | (s[1]-'0')<<3 | s[2]-'0';
+                                s += 2;
+                        } else if (isdigit((uchar)s[0])) {
+                                *t++ = *s - '0';
+                        } else if (*s == 'b') {
+                                *t++ = '\b';
+                        } else if (*s == 'n') {
+                                *t++ = '\n';
+                        } else if (*s == 'r') {
+                                *t++ = '\r';
+                        } else if (*s == 't') {
+                                *t++ = '\t';
+                        } else {
+                                *t++ = *s;
+                        }
+                        s++;
+                }
+        }
+        *t = '\0';
+        return obuf;
+}
+
+
+static int getnrfont(FILE *fp)        /* read the nroff description file */
+{
+        Chwid chtemp[NCHARS];
+        static Chwid chinit;
+        int i, nw, n, wid, code, type;
+        char buf[100], ch[100], s1[100], s2[100];
+        wchar_t wc;
+
+        code = 0;
+        chinit.wid = 1;
+        chinit.str = "";
+        for (i = 0; i < ALPHABET; i++) {
+                chtemp[i] = chinit;        /* zero out to begin with */
+                chtemp[i].num = chtemp[i].code = i;        /* every alphabetic character is itself */
+                chtemp[i].wid = 1;        /* default ascii widths */
+        }
+        skipline(fp);
+        nw = ALPHABET;
+        while (fgets(buf, sizeof buf, fp) != NULL) {
+                sscanf(buf, "%s %s %[^\n]", ch, s1, s2);
+                if (!eq(s1, "\"")) {        /* genuine new character */
+                        sscanf(s1, "%d", &wid);
+                } /* else it's a synonym for prev character, */
+                        /* so leave previous values intact */
+
+                /* decide what kind of alphabet it might come from */
+
+                if (strlen(ch) == 1) {        /* it's ascii */
+                        n = ch[0];        /* origin includes non-graphics */
+                        chtemp[n].num = ch[0];
+                } else if (ch[0] == '\\' && ch[1] == '0') {
+                        n = strtol(ch+1, 0, 0);        /* \0octal or \0xhex */
+                        chtemp[n].num = n;
+#ifdef UNICODE
+                } else if (mbtowc(&wc, ch, strlen(ch)) > 1) {
+                        chtemp[nw].num = chadd(ch, MBchar, Install);
+                        n = nw;
+                        nw++;
+#endif        /*UNICODE*/
+                } else {
+                        if (strcmp(ch, "---") == 0) { /* no name */
+                                sprintf(ch, "%d", code);
+                                type = Number;
+                        } else
+                                type = Troffchar;
+/* BUG in here somewhere when same character occurs twice in table */
+                        chtemp[nw].num = chadd(ch, type, Install);
+                        n = nw;
+                        nw++;
+                }
+                chtemp[n].wid = wid;
+                chtemp[n].str = strdupl(parse(s2, Type));
+        }
+        t.tfont.nchars = nw;
+        t.tfont.wp = (Chwid *) malloc(nw * sizeof(Chwid));
+        if (t.tfont.wp == NULL)
+                return -1;
+        for (i = 0; i < nw; i++)
+                t.tfont.wp[i] = chtemp[i];
+        return 1;
+}
+
+
+void n_ptinit(void)
+{
+        int i;
+        char *p;
+        char opt[50], cmd[100];
+        FILE *fp;
+
+        hmot = n_hmot;
+        makem = n_makem;
+        setabs = n_setabs;
+        setch = n_setch;
+        sethl = n_sethl;
+        setht = n_setht;
+        setslant = n_setslant;
+        vmot = n_vmot;
+        xlss = n_xlss;
+        findft = n_findft;
+        width = n_width;
+        mchbits = n_mchbits;
+        ptlead = n_ptlead;
+        ptout = n_ptout;
+        ptpause = n_ptpause;
+        setfont = n_setfont;
+        setps = n_setps;
+        setwd = n_setwd;
+
+        if ((p = getenv("NROFFTERM")) != 0)
+                strcpy(devname, p);
+        if (termtab[0] == 0)
+                strcpy(termtab,DWBntermdir);
+        if (fontdir[0] == 0)
+                strcpy(fontdir, "");
+        if (devname[0] == 0)
+                strcpy(devname, NDEVNAME);
+        pl = 11*INCH;
+        po = PO;
+        hyf = 0;
+        ascii = 1;
+        lg = 0;
+        fontlab[1] = 'R';
+        fontlab[2] = 'I';
+        fontlab[3] = 'B';
+        fontlab[4] = PAIR('B','I');
+        fontlab[5] = 'D';
+        bdtab[3] = 3;
+        bdtab[4] = 3;
+
+        /* hyphalg = 0;        /* for testing */
+
+        strcat(termtab, devname);
+        if ((fp = fopen(unsharp(termtab), "r")) == NULL) {
+                ERROR "cannot open %s", termtab WARN;
+                exit(-1);
+        }
+
+
+/* this loop isn't robust about input format errors. */
+/* it assumes  name, name-value pairs..., charset */
+/* god help us if we get out of sync. */
+
+        fscanf(fp, "%s", cmd);        /* should be device name... */
+        if (!is(devname) && trace)
+                ERROR "wrong terminal name: saw %s, wanted %s", cmd, devname WARN;
+        for (;;) {
+                fscanf(fp, "%s", cmd);
+                if (is("charset"))
+                        break;
+                fscanf(fp, " %[^\n]", opt);
+                if (is("bset")) t.bset = atoi(opt);
+                else if (is("breset")) t.breset = atoi(opt);
+                else if (is("Hor")) t.Hor = atoi(opt);
+                else if (is("Vert")) t.Vert = atoi(opt);
+                else if (is("Newline")) t.Newline = atoi(opt);
+                else if (is("Char")) t.Char = atoi(opt);
+                else if (is("Em")) t.Em = atoi(opt);
+                else if (is("Halfline")) t.Halfline = atoi(opt);
+                else if (is("Adj")) t.Adj = atoi(opt);
+                else if (is("twinit")) t.twinit = strdupl(parse(opt, Notype));
+                else if (is("twrest")) t.twrest = strdupl(parse(opt, Notype));
+                else if (is("twnl")) t.twnl = strdupl(parse(opt, Notype));
+                else if (is("hlr")) t.hlr = strdupl(parse(opt, Notype));
+                else if (is("hlf")) t.hlf = strdupl(parse(opt, Notype));
+                else if (is("flr")) t.flr = strdupl(parse(opt, Notype));
+                else if (is("bdon")) t.bdon = strdupl(parse(opt, Notype));
+                else if (is("bdoff")) t.bdoff = strdupl(parse(opt, Notype));
+                else if (is("iton")) t.iton = strdupl(parse(opt, Notype));
+                else if (is("itoff")) t.itoff = strdupl(parse(opt, Notype));
+                else if (is("ploton")) t.ploton = strdupl(parse(opt, Notype));
+                else if (is("plotoff")) t.plotoff = strdupl(parse(opt, Notype));
+                else if (is("up")) t.up = strdupl(parse(opt, Notype));
+                else if (is("down")) t.down = strdupl(parse(opt, Notype));
+                else if (is("right")) t.right = strdupl(parse(opt, Notype));
+                else if (is("left")) t.left = strdupl(parse(opt, Notype));
+                else
+                        ERROR "bad tab.%s file, %s %s", devname, cmd, opt WARN;
+        }
+
+        getnrfont(fp);
+        fclose(fp);
+
+        sps = EM;
+        ics = EM * 2;
+        dtab = 8 * t.Em;
+        for (i = 0; i < 16; i++)
+                tabtab[i] = dtab * (i + 1);
+        pl = 11 * INCH;
+        po = PO;
+        spacesz = SS;
+        lss = lss1 = VS;
+        ll = ll1 = lt = lt1 = LL;
+        smnt = nfonts = 5;        /* R I B BI S */
+        n_specnames();        /* install names like "hyphen", etc. */
+        if (eqflg)
+                t.Adj = t.Hor;
+}
+
+
+void n_specnames(void)
+{
+
+        int        i;
+
+        for (i = 0; spnames[i].n; i++)
+                *spnames[i].n = chadd(spnames[i].v, Troffchar, Install);
+        if (c_isalnum == 0)
+                c_isalnum = NROFFCHARS;
+}
+
+void twdone(void)
+{
+        if (!TROFF && t.twrest) {
+                obufp = obuf;
+                oputs(t.twrest);
+                flusho();
+                if (pipeflg) {
+                        pclose(ptid);
+                }
+                restore_tty();
+        }
+}
+
+
+void n_ptout(Tchar i)
+{
+        *olinep++ = i;
+        if (olinep >= &oline[LNSIZE])
+                olinep--;
+        if (cbits(i) != '\n')
+                return;
+        olinep--;
+        lead += dip->blss + lss - t.Newline;
+        dip->blss = 0;
+        esct = esc = 0;
+        if (olinep > oline) {
+                move();
+                ptout1();
+                oputs(t.twnl);
+        } else {
+                lead += t.Newline;
+                move();
+        }
+        lead += dip->alss;
+        dip->alss = 0;
+        olinep = oline;
+}
+
+
+void ptout1(void)
+{
+        int k;
+        char *codep;
+        int w, j, phyw;
+        Tchar *q, i;
+        static int oxfont = FT;        /* start off in roman */
+
+        for (q = oline; q < olinep; q++) {
+                i = *q;
+                if (ismot(i)) {
+                        j = absmot(i);
+                        if (isnmot(i))
+                                j = -j;
+                        if (isvmot(i))
+                                lead += j;
+                        else 
+                                esc += j;
+                        continue;
+                }
+                if ((k = cbits(i)) <= ' ') {
+                        switch (k) {
+                        case ' ': /*space*/
+                                esc += t.Char;
+                                break;
+                        case '\033':
+                        case '\007':
+                        case '\016':
+                        case '\017':
+                                oput(k);
+                                break;
+                        }
+                        continue;
+                }
+                phyw = w = t.Char * t.tfont.wp[k].wid;
+                if (iszbit(i))
+                        w = 0;
+                if (esc || lead)
+                        move();
+                esct += w;
+                xfont = fbits(i);
+                if (xfont != oxfont) {
+                        switch (oxfont) {
+                        case ULFONT:        oputs(t.itoff); break;
+                        case BDFONT:        oputs(t.bdoff); break;
+                        case BIFONT:        oputs(t.itoff); oputs(t.bdoff); break;
+                        }
+                        switch (xfont) {
+                        case ULFONT:
+                                if (*t.iton & 0377) oputs(t.iton); break;
+                        case BDFONT:
+                                if (*t.bdon & 0377) oputs(t.bdon); break;
+                        case BIFONT:
+                                if (*t.bdon & 0377) oputs(t.bdon);
+                                if (*t.iton & 0377) oputs(t.iton);
+                                break;
+                        }
+                        oxfont = xfont;
+                }
+                if ((xfont == ulfont || xfont == BIFONT) && !(*t.iton & 0377)) {
+                        for (j = w / t.Char; j > 0; j--)
+                                oput('_');
+                        for (j = w / t.Char; j > 0; j--)
+                                oput('\b');
+                }
+                if (!(*t.bdon & 0377) && ((j = bdtab[xfont]) || xfont == BDFONT || xfont == BIFONT))
+                        j++;
+                else
+                        j = 1;        /* number of overstrikes for bold */
+                if (k < ALPHABET) {        /* ordinary ascii */
+                        oput(k);
+                        while (--j > 0) {
+                                oput('\b');
+                                oput(k);
+                        }
+                } else if (k >= t.tfont.nchars) {        /* BUG -- not really understood */
+/* fprintf(stderr, "big char %d, name %s\n", k, chname(k)); /* */
+                        oputs(chname(k)+1);        /* BUG: should separate Troffchar and MBchar... */
+                } else if (t.tfont.wp[k].str == 0) {
+/* fprintf(stderr, "nostr char %d, name %s\n", k, chname(k)); /* */
+                        oputs(chname(k)+1);        /* BUG: should separate Troffchar and MBchar... */
+                } else if (t.tfont.wp[k].str[0] == MBchar) {        /* parse() puts this on */
+/* fprintf(stderr, "MBstr char %d, name %s\n", k, chname(k)); /* */
+                        oputs(t.tfont.wp[k].str+1);
+                } else {
+                        int oj = j;
+/* fprintf(stderr, "str char %d, name %s\n", k, chname(k)); /* */
+                        codep = t.tfont.wp[k].str+1;        /* Troffchar by default */
+                        while (*codep != 0) {
+                                if (*codep & 0200) {
+                                        codep = plot(codep);
+                                        oput(' ');
+                                } else {
+                                        if (*codep == '%')        /* escape */
+                                                codep++;
+                                        oput(*codep);
+                                        if (*codep == '\033')
+                                                oput(*++codep);
+                                        else if (*codep != '\b')
+                                                for (j = oj; --j > 0; ) {
+                                                        oput('\b');
+                                                        oput(*codep);
+                                                }
+                                        codep++;
+                                }
+                        }
+                }
+                if (!w)
+                        for (j = phyw / t.Char; j > 0; j--)
+                                oput('\b');
+        }
+}
+
+
+char *plot(char *x)
+{
+        int        i;
+        char        *j, *k;
+
+        oputs(t.ploton);
+        k = x;
+        if ((*k & 0377) == 0200)
+                k++;
+        for (; *k; k++) {
+                if (*k == '%') {        /* quote char within plot mode */
+                        oput(*++k);
+                } else if (*k & 0200) {
+                        if (*k & 0100) {
+                                if (*k & 040)
+                                        j = t.up; 
+                                else 
+                                        j = t.down;
+                        } else {
+                                if (*k & 040)
+                                        j = t.left; 
+                                else 
+                                        j = t.right;
+                        }
+                        if ((i = *k & 037) == 0) {        /* 2nd 0200 turns it off */
+                                ++k;
+                                break;
+                        }
+                        while (i--)
+                                oputs(j);
+                } else 
+                        oput(*k);
+        }
+        oputs(t.plotoff);
+        return(k);
+}
+
+
+void move(void)
+{
+        int k;
+        char *i, *j;
+        char *p, *q;
+        int iesct, dt;
+
+        iesct = esct;
+        if (esct += esc)
+                i = "\0"; 
+        else 
+                i = "\n\0";
+        j = t.hlf;
+        p = t.right;
+        q = t.down;
+        if (lead) {
+                if (lead < 0) {
+                        lead = -lead;
+                        i = t.flr;
+                        /*        if(!esct)i = t.flr; else i = "\0";*/
+                        j = t.hlr;
+                        q = t.up;
+                }
+                if (*i & 0377) {
+                        k = lead / t.Newline;
+                        lead = lead % t.Newline;
+                        while (k--)
+                                oputs(i);
+                }
+                if (*j & 0377) {
+                        k = lead / t.Halfline;
+                        lead = lead % t.Halfline;
+                        while (k--)
+                                oputs(j);
+                } else { /* no half-line forward, not at line begining */
+                        k = lead / t.Newline;
+                        lead = lead % t.Newline;
+                        if (k > 0) 
+                                esc = esct;
+                        i = "\n";
+                        while (k--) 
+                                oputs(i);
+                }
+        }
+        if (esc) {
+                if (esc < 0) {
+                        esc = -esc;
+                        j = "\b";
+                        p = t.left;
+                } else {
+                        j = " ";
+                        if (hflg)
+                                while ((dt = dtab - (iesct % dtab)) <= esc) {
+                                        if (dt % t.Em)
+                                                break;
+                                        oput(TAB);
+                                        esc -= dt;
+                                        iesct += dt;
+                                }
+                }
+                k = esc / t.Em;
+                esc = esc % t.Em;
+                while (k--)
+                        oputs(j);
+        }
+        if ((*t.ploton & 0377) && (esc || lead)) {
+                oputs(t.ploton);
+                esc /= t.Hor;
+                lead /= t.Vert;
+                while (esc--)
+                        oputs(p);
+                while (lead--)
+                        oputs(q);
+                oputs(t.plotoff);
+        }
+        esc = lead = 0;
+}
+
+
+void n_ptlead(void)
+{
+        move();
+}
+
+
+void n_ptpause(void )
+{
+        char        junk;
+
+        flusho();
+        read(2, &junk, 1);
+}
diff --git a/troff/n2.c b/troff/n2.c
@@ -0,0 +1,325 @@
+/*
+ * n2.c
+ *
+ * output, cleanup
+ */
+
+#define _BSD_SOURCE 1        /* popen */
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+#include 
+
+#ifdef STRICT
+        /* not in ANSI or POSIX */
+FILE*        popen(char*, char*);
+#endif
+
+
+extern        jmp_buf        sjbuf;
+int        toolate;
+int        error;
+
+char        obuf[2*BUFSIZ];
+char        *obufp = obuf;
+
+        /* pipe command structure; allows redicously long commends for .pi */
+struct Pipe {
+        char        *buf;
+        int        tick;
+        int        cnt;
+} Pipe;
+
+
+int        xon        = 0;        /* records if in middle of \X */
+
+int pchar(Tchar i)
+{
+        int j;
+        static int hx = 0;        /* records if have seen HX */
+
+        if (hx) {
+                hx = 0;
+                j = absmot(i);
+                if (isnmot(i)) {
+                        if (j > dip->blss)
+                                dip->blss = j;
+                } else {
+                        if (j > dip->alss)
+                                dip->alss = j;
+                        ralss = dip->alss;
+                }
+                return 0;
+        }
+        if (ismot(i)) {
+                pchar1(i); 
+                return 0;
+        }
+        switch (j = cbits(i)) {
+        case 0:
+        case IMP:
+        case RIGHT:
+        case LEFT:
+                return 0;
+        case HX:
+                hx = 1;
+                return 0;
+        case XON:
+                xon++;
+                break;
+        case XOFF:
+                xon--;
+                break;
+        case PRESC:
+                if (!xon && !tflg && dip == &d[0])
+                        j = eschar;        /* fall through */
+        default:
+                setcbits(i, trtab[j]);
+        }
+        if (NROFF & xon)        /* rob fix for man2html */
+                return 0;
+        pchar1(i);
+        return 0;
+}
+
+
+void pchar1(Tchar i)
+{
+        int j;
+
+        j = cbits(i);
+        if (dip != &d[0]) {
+                wbf(i);
+                dip->op = offset;
+                return;
+        }
+        if (!tflg && !print) {
+                if (j == '\n')
+                        dip->alss = dip->blss = 0;
+                return;
+        }
+        if (j == FILLER && !xon)
+                return;
+        if (tflg) {        /* transparent mode, undiverted */
+                if (print)                        /* assumes that it's ok to print */
+                        /* OUT "%c", j PUT;        /* i.e., is ascii */
+                        outascii(i);
+                return;
+        }
+        if (TROFF && ascii)
+                outascii(i);
+        else
+                ptout(i);
+}
+
+
+void outweird(int k)        /* like ptchname() but ascii */
+{
+        char *chn = chname(k);
+
+        switch (chn[0]) {
+        case MBchar:
+                OUT "%s", chn+1 PUT;        /* \n not needed? */
+                break;
+        case Number:
+                OUT "\\N'%s'", chn+1 PUT;
+                break;
+        case Troffchar:
+                if (strlen(chn+1) == 2)
+                        OUT "\\(%s", chn+1 PUT;
+                else
+                        OUT "\\C'%s'", chn+1 PUT;
+                break;
+        default:
+                OUT " %s? ", chn PUT;
+                break;
+        }
+}
+
+void outascii(Tchar i)        /* print i in best-guess ascii */
+{
+        int j = cbits(i);
+
+/* is this ever called with NROFF set? probably doesn't work at all. */
+
+        if (ismot(i))
+                oput(' ');
+        else if (j < ALPHABET && j >= ' ' || j == '\n' || j == '\t')
+                oput(j);
+        else if (j == DRAWFCN)
+                oputs("\\D");
+        else if (j == HYPHEN)
+                oput('-');
+        else if (j == MINUS)        /* special pleading for strange encodings */
+                oputs("\\-");
+        else if (j == PRESC)
+                oputs("\\e");
+        else if (j == FILLER)
+                oputs("\\&");
+        else if (j == UNPAD)
+                oputs("\\ ");
+        else if (j == OHC)        /* this will never occur;  stripped out earlier */
+                oputs("\\%");
+        else if (j == XON)
+                oputs("\\X");
+        else if (j == XOFF)
+                oputs(" ");
+        else if (j == LIG_FI)
+                oputs("fi");
+        else if (j == LIG_FL)
+                oputs("fl");
+        else if (j == LIG_FF)
+                oputs("ff");
+        else if (j == LIG_FFI)
+                oputs("ffi");
+        else if (j == LIG_FFL)
+                oputs("ffl");
+        else if (j == WORDSP) {                /* nothing at all */
+                if (xon)                /* except in \X */
+                        oput(' ');
+
+        } else
+                outweird(j);
+}
+
+int flusho(void)
+{
+        if (NROFF && !toolate && t.twinit)
+                        fwrite(t.twinit, strlen(t.twinit), 1, ptid);
+
+        if (obufp > obuf) {
+                if (pipeflg && !toolate) {
+                        /* fprintf(stderr, "Pipe to <%s>\n", Pipe.buf); */
+                        if (!Pipe.buf[0] || (ptid = popen(Pipe.buf, "w")) == NULL)
+                                ERROR "pipe %s not created.", Pipe.buf WARN;
+                        if (Pipe.buf)
+                                free(Pipe.buf);
+                }
+                if (!toolate)
+                        toolate++;
+                *obufp = 0;
+                fputs(obuf, ptid);
+                fflush(ptid);
+                obufp = obuf;
+        }
+        return 1;
+}
+
+
+void caseex(void)
+{
+        done(0);
+}
+
+
+void done(int x) 
+{
+        int i;
+
+        error |= x;
+        app = ds = lgf = 0;
+        if (i = em) {
+                donef = -1;
+                eschar = '\\';
+                em = 0;
+                if (control(i, 0))
+                        longjmp(sjbuf, 1);
+        }
+        if (!nfo)
+                done3(0);
+        mflg = 0;
+        dip = &d[0];
+        if (woff)        /* BUG!!! This isn't set anywhere */
+                wbf((Tchar)0);
+        if (pendw)
+                getword(1);
+        pendnf = 0;
+        if (donef == 1)
+                done1(0);
+        donef = 1;
+        ip = 0;
+        frame = stk;
+        nxf = frame + 1;
+        if (!ejf)
+                tbreak();
+        nflush++;
+        eject((Stack *)0);
+        longjmp(sjbuf, 1);
+}
+
+
+void done1(int x) 
+{
+        error |= x;
+        if (numtabp[NL].val) {
+                trap = 0;
+                eject((Stack *)0);
+                longjmp(sjbuf, 1);
+        }
+        if (!ascii)
+                pttrailer();
+        done2(0);
+}
+
+
+void done2(int x) 
+{
+        ptlead();
+        if (TROFF && !ascii)
+                ptstop();
+        flusho();
+        done3(x);
+}
+
+void done3(int x) 
+{
+        error |= x;
+        flusho();
+        if (NROFF)
+                twdone();
+        if (pipeflg)
+                pclose(ptid);
+        exit(error);
+}
+
+
+void edone(int x) 
+{
+        frame = stk;
+        nxf = frame + 1;
+        ip = 0;
+        done(x);
+}
+
+
+void casepi(void)
+{
+        int j;
+        char buf[NTM];
+
+        if (Pipe.buf == NULL) {
+                if ((Pipe.buf = (char *)calloc(NTM, sizeof(char))) == NULL) {
+                        ERROR "No buf space for pipe cmd" WARN;
+                        return;
+                }
+                Pipe.tick = 1;
+        } else
+                Pipe.buf[Pipe.cnt++] = '|';
+
+        getline(buf, NTM);
+        j = strlen(buf);
+        if (toolate) {
+                ERROR "Cannot create pipe to %s", buf WARN;
+                return;
+        }
+        Pipe.cnt += j;
+        if (j >= NTM +1) {
+                Pipe.tick++;
+                if ((Pipe.buf = (char *)realloc(Pipe.buf, Pipe.tick * NTM * sizeof(char))) == NULL) {
+                        ERROR "No more buf space for pipe cmd" WARN;
+                        return;
+                }
+        }
+        strcat(Pipe.buf, buf);
+        pipeflg++;
+}
diff --git a/troff/n3.c b/troff/n3.c
@@ -0,0 +1,954 @@
+/*
+ * troff3.c
+ * 
+ * macro and string routines, storage allocation
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+Tchar        *argtop;
+int        pagech = '%';
+int        strflg;
+
+#define        MHASHSIZE        128        /* must be 2**n */
+#define        MHASH(x)        ((x>>6)^x) & (MHASHSIZE-1)
+Contab        *mhash[MHASHSIZE];
+
+
+Blockp        *blist;                /* allocated blocks for macros and strings */
+int        nblist;                /* how many there are */
+int        bfree = -1;        /* first (possible) free block in the list */
+
+Contab *contabp = NULL;
+#define MDELTA 500
+int        nm = 0;
+
+int savname;                /* name of macro/string being defined */
+int savslot;                /* place in Contab of savname */
+int freeslot = -1;        /* first (possible) free slot in contab */
+
+void prcontab(Contab *p)
+{
+        int i;
+        for (i = 0; i < nm; i++)
+                if (p)
+                        if (p[i].rq != 0)
+                                fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq));
+                        else
+                                fprintf(stderr, "slot %d empty\n", i);
+                else
+                        fprintf(stderr, "slot %d empty\n", i);
+}
+
+
+void blockinit(void)
+{
+        blist = (Blockp *) calloc(NBLIST, sizeof(Blockp));
+        if (blist == NULL) {
+                ERROR "not enough room for %d blocks", NBLIST WARN;
+                done2(1);
+        }
+        nblist = NBLIST;
+        blist[0].nextoff = blist[1].nextoff = -1;
+        blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+        blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+                /* -1 prevents blist[0] from being used; temporary fix */
+                /* for a design botch: offset==0 is overloaded. */
+                /* blist[1] reserved for .rd indicator -- also unused. */
+                /* but someone unwittingly looks at these, so allocate something */
+        bfree = 2;
+}
+
+
+char *grow(char *ptr, int num, int size)        /* make array bigger */
+{
+        char *p;
+
+        if (ptr == NULL)
+                p = (char *) calloc(num, size);
+        else
+                p = (char *) realloc(ptr, num * size);
+        return p;
+}
+
+void mnspace(void)
+{
+        nm = sizeof(contab)/sizeof(Contab) + MDELTA;
+        freeslot = sizeof(contab)/sizeof(Contab) + 1;
+        contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab));
+        if (contabp == NULL) {
+                ERROR "not enough memory for namespace of %d marcos", nm WARN;
+                exit(1);
+        }
+        contabp = (Contab *) memcpy((char *) contabp, (char *)contab,
+                                                        sizeof(contab));
+        if (contabp == NULL) {
+                ERROR "Cannot reinitialize macro/request name list" WARN;
+                exit(1);
+        }
+
+}
+
+void caseig(void)
+{
+        int i;
+        Offset oldoff = offset;
+
+        offset = 0;
+        i = copyb();
+        offset = oldoff;
+        if (i != '.')
+                control(i, 1);
+}
+
+
+void casern(void)
+{
+        int i, j, k;
+
+        lgf++;
+        skip();
+        if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
+                return;
+        skip();
+        clrmn(findmn(j = getrq()));
+        if (j) {
+                munhash(&contabp[oldmn]);
+                contabp[oldmn].rq = j;
+                maddhash(&contabp[oldmn]);
+                if (dip != d )
+                        for (k = dilev; k; k--)
+                                if (d[k].curd == i)
+                                        d[k].curd = j;
+        }
+}
+
+void maddhash(Contab *rp)
+{
+        Contab **hp;
+
+        if (rp->rq == 0)
+                return;
+        hp = &mhash[MHASH(rp->rq)];
+        rp->link = *hp;
+        *hp = rp;
+}
+
+void munhash(Contab *mp)
+{        
+        Contab *p;
+        Contab **lp;
+
+        if (mp->rq == 0)
+                return;
+        lp = &mhash[MHASH(mp->rq)];
+        p = *lp;
+        while (p) {
+                if (p == mp) {
+                        *lp = p->link;
+                        p->link = 0;
+                        return;
+                }
+                lp = &p->link;
+                p = p->link;
+        }
+}
+
+void mrehash(void)
+{
+        Contab *p;
+        int i;
+
+        for (i=0; i < MHASHSIZE; i++)
+                mhash[i] = 0;
+        for (p=contabp; p < &contabp[nm]; p++)
+                p->link = 0;
+        for (p=contabp; p < &contabp[nm]; p++) {
+                if (p->rq == 0)
+                        continue;
+                i = MHASH(p->rq);
+                p->link = mhash[i];
+                mhash[i] = p;
+        }
+}
+
+void caserm(void)
+{
+        int j;
+        int k = 0;
+
+        lgf++;
+g0:
+        while (!skip() && (j = getrq()) != 0) {
+                if (dip != d)
+                        for (k = dilev; k; k--)
+                                if (d[k].curd == j) {
+                                        ERROR "cannot remove diversion %s during definition",
+                                                                unpair(j) WARN;
+                                        goto g0;
+                                }
+                clrmn(findmn(j));
+        }
+        lgf--;
+}
+
+
+void caseas(void)
+{
+        app++;
+        caseds();
+}
+
+
+void caseds(void)
+{
+        ds++;
+        casede();
+}
+
+
+void caseam(void)
+{
+        app++;
+        casede();
+}
+
+
+void casede(void)
+{
+        int i, req;
+        Offset savoff;
+
+        req = '.';
+        lgf++;
+        skip();
+        if ((i = getrq()) == 0)
+                goto de1;
+        if ((offset = finds(i)) == 0)
+                goto de1;
+        if (newmn)
+                savslot = newmn;
+        else
+                savslot = findmn(i);
+        savname = i;
+        if (ds)
+                copys();
+        else
+                req = copyb();
+        clrmn(oldmn);
+        if (newmn) {
+                if (contabp[newmn].rq)
+                        munhash(&contabp[newmn]);
+                contabp[newmn].rq = i;
+                maddhash(&contabp[newmn]);
+
+        }
+        if (apptr) {
+                savoff = offset;
+                offset = apptr;
+                wbf((Tchar) IMP);
+                offset = savoff;
+        }
+        offset = dip->op;
+        if (req != '.')
+                control(req, 1);
+de1:
+        ds = app = 0;
+}
+
+
+int findmn(int i)
+{
+        Contab *p;
+
+        for (p = mhash[MHASH(i)]; p; p = p->link)
+                if (i == p->rq)
+                        return(p - contabp);
+        return(-1);
+}
+
+
+void clrmn(int i)
+{
+        if (i >= 0) {
+                if (contabp[i].mx)
+                        ffree(contabp[i].mx);
+                munhash(&contabp[i]);
+                contabp[i].rq = 0;
+                contabp[i].mx = 0;
+                contabp[i].emx = 0;
+                contabp[i].f = 0;
+                if (contabp[i].divsiz != NULL) {
+                        free(contabp[i].divsiz);
+                        contabp[i].divsiz = NULL;
+                }
+                if (freeslot > i)
+                        freeslot = i;
+        }
+}
+
+void growcontab(void)
+{
+        nm += MDELTA;
+        contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab));
+        if (contabp == NULL) {
+                ERROR "Too many (%d) string/macro names", nm WARN;
+                done2(02);
+        } else {
+                memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab),
+                                                0, MDELTA * sizeof(Contab));
+                mrehash();
+        }
+}
+
+
+Offset finds(int mn)
+{
+        int i;
+        Offset savip;
+
+        oldmn = findmn(mn);
+        newmn = 0;
+        apptr = 0;
+        if (app && oldmn >= 0 && contabp[oldmn].mx) {
+                savip = ip;
+                ip = contabp[oldmn].emx;
+                oldmn = -1;
+                apptr = ip;
+                if (!diflg)
+                        ip = incoff(ip);
+                nextb = ip;
+                ip = savip;
+        } else {
+                for (i = freeslot; i < nm; i++) {
+                        if (contabp[i].rq == 0)
+                                break;
+                }
+                if (i == nm) 
+                        growcontab();
+                freeslot = i + 1;
+                if ((nextb = alloc()) == -1) {
+                        app = 0;
+                        if (macerr++ > 1)
+                                done2(02);
+                        if (nextb == 0)
+                                ERROR "Not enough space for string/macro names" WARN;
+                        edone(04);
+                        return(offset = 0);
+                }
+                contabp[i].mx = nextb;
+                if (!diflg) {
+                        newmn = i;
+                        if (oldmn == -1)
+                                contabp[i].rq = -1;
+                } else {
+                        contabp[i].rq = mn;
+                        maddhash(&contabp[i]);
+                }
+        }
+        app = 0;
+        return(offset = nextb);
+}
+
+int skip(void)
+{
+        Tchar i;
+
+        while (cbits(i = getch()) == ' ' || ismot(i))
+                ;
+        ch = i;
+        return(nlflg);
+}
+
+
+int copyb(void)
+{
+        int i, j, state;
+        Tchar ii;
+        int req, k;
+        Offset savoff;
+        Uchar *p;
+
+        savoff = 0;
+        if (skip() || !(j = getrq()))
+                j = '.';
+        req = j;
+        p = unpair(j);
+        /* was: k = j >> BYTE; j &= BYTEMASK; */
+        j = p[0];
+        k = p[1];
+        copyf++;
+        flushi();
+        nlflg = 0;
+        state = 1;
+
+/* state 0        eat up
+ * state 1        look for .
+ * state 2        look for first char of end macro
+ * state 3        look for second char of end macro
+ */
+
+        while (1) {
+                i = cbits(ii = getch());
+                if (state == 3) {
+                        if (i == k)
+                                break;
+                        if (!k) {
+                                ch = ii;
+                                i = getach();
+                                ch = ii;
+                                if (!i)
+                                        break;
+                        }
+                        state = 0;
+                        goto c0;
+                }
+                if (i == '\n') {
+                        state = 1;
+                        nlflg = 0;
+                        goto c0;
+                }
+                if (state == 1 && i == '.') {
+                        state++;
+                        savoff = offset;
+                        goto c0;
+                }
+                if (state == 2 && i == j) {
+                        state++;
+                        goto c0;
+                }
+                state = 0;
+c0:
+                if (offset)
+                        wbf(ii);
+        }
+        if (offset) {
+                offset = savoff;
+                wbf((Tchar)0);
+        }
+        copyf--;
+        return(req);
+}
+
+
+void copys(void)
+{
+        Tchar i;
+
+        copyf++;
+        if (skip())
+                goto c0;
+        if (cbits(i = getch()) != '"')
+                wbf(i);
+        while (cbits(i = getch()) != '\n')
+                wbf(i);
+c0:
+        wbf((Tchar)0);
+        copyf--;
+}
+
+
+Offset alloc(void)        /* return free Offset in nextb */
+{
+        int i, j;
+
+        for (i = bfree; i < nblist; i++)
+                if (blist[i].nextoff == 0)
+                        break;
+        if (i == nblist) {
+                blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp));
+                if (blist == NULL) {
+                        ERROR "can't grow blist for string/macro defns" WARN;
+                        done2(2);
+                }
+                nblist *= 2;
+                for (j = i; j < nblist; j++) {
+                        blist[j].nextoff = 0;
+                        blist[j].bp = 0;
+                }
+        }
+        blist[i].nextoff = -1;        /* this block is the end */
+        bfree = i + 1;
+        if (blist[i].bp == 0)
+                blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+        if (blist[i].bp == NULL) {
+                ERROR "can't allocate memory for string/macro definitions" WARN;
+                done2(2);
+        }
+        nextb = (Offset) i * BLK;
+        return nextb;
+}
+
+
+void ffree(Offset i)        /* free list of blocks starting at blist(o) */
+{                        /* (doesn't actually free the blocks, just the pointers) */
+        int j;
+
+        for ( ; blist[j = bindex(i)].nextoff != -1; ) {
+                if (bfree > j)
+                        bfree = j;
+                i = blist[j].nextoff;
+                blist[j].nextoff = 0;
+        }
+        blist[j].nextoff = 0;
+}
+
+
+void wbf(Tchar i)        /* store i into offset, get ready for next one */
+{
+        int j, off;
+
+        if (!offset)
+                return;
+        j = bindex(offset);
+        if (i == 0)
+                contabp[savslot].emx = offset;
+        off = boffset(offset);
+        blist[j].bp[off++] = i;
+        offset++;
+        if (pastend(offset)) {        /* off the end of this block */
+                if (blist[j].nextoff == -1) {
+                        if ((nextb = alloc()) == -1) {
+                                ERROR "Out of temp file space" WARN;
+                                done2(01);
+                        }
+                        blist[j].nextoff = nextb;
+                }
+                offset = blist[j].nextoff;
+        }
+}
+
+
+Tchar rbf(void)        /* return next char from blist[] block */
+{
+        Tchar i, j;
+
+        if (ip == RD_OFFSET) {                /* for rdtty */
+                if (j = rdtty())
+                        return(j);
+                else
+                        return(popi());
+        }
+        
+        i = rbf0(ip);
+        if (i == 0) {
+                if (!app)
+                        i = popi();
+                return(i);
+        }
+        ip = incoff(ip);
+        return(i);
+}
+
+
+Offset xxxincoff(Offset p)                /* get next blist[] block */
+{
+        p++;
+        if (pastend(p)) {                /* off the end of this block */
+                if ((p = blist[bindex(p-1)].nextoff) == -1) {        /* and nothing was allocated after it */
+                        ERROR "Bad storage allocation" WARN;
+                        done2(-5);
+                }
+        }
+        return(p);
+}
+
+
+Tchar popi(void)
+{
+        Stack *p;
+
+        if (frame == stk)
+                return(0);
+        if (strflg)
+                strflg--;
+        p = nxf = frame;
+        p->nargs = 0;
+        frame = p->pframe;
+        ip = p->pip;
+        pendt = p->ppendt;
+        lastpbp = p->lastpbp;
+        return(p->pch);
+}
+
+/*
+ *        test that the end of the allocation is above a certain location
+ *        in memory
+ */
+#define SPACETEST(base, size) \
+        if ((char*)base + size >= (char*)stk+STACKSIZE) \
+                ERROR "Stacksize overflow in n3" WARN
+
+Offset pushi(Offset newip, int  mname)
+{
+        Stack *p;
+
+        SPACETEST(nxf, sizeof(Stack));
+        p = nxf;
+        p->pframe = frame;
+        p->pip = ip;
+        p->ppendt = pendt;
+        p->pch = ch;
+        p->lastpbp = lastpbp;
+        p->mname = mname;
+        lastpbp = pbp;
+        pendt = ch = 0;
+        frame = nxf;
+        if (nxf->nargs == 0) 
+                nxf += 1;
+        else 
+                nxf = (Stack *)argtop;
+        return(ip = newip);
+}
+
+
+void *setbrk(int x)
+{
+        char *i;
+
+        if ((i = (char *) calloc(x, 1)) == 0) {
+                ERROR "Core limit reached" WARN;
+                edone(0100);
+        }
+        return(i);
+}
+
+
+int getsn(void)
+{
+        int i;
+
+        if ((i = getach()) == 0)
+                return(0);
+        if (i == '(')
+                return(getrq());
+        else 
+                return(i);
+}
+
+
+Offset setstr(void)
+{
+        int i, j;
+
+        lgf++;
+        if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) {
+                lgf--;
+                return(0);
+        } else {
+                SPACETEST(nxf, sizeof(Stack));
+                nxf->nargs = 0;
+                strflg++;
+                lgf--;
+                return pushi(contabp[j].mx, i);
+        }
+}
+
+
+
+void collect(void)
+{
+        int j;
+        Tchar i, *strp, *lim, **argpp, **argppend;
+        int quote;
+        Stack *savnxf;
+
+        copyf++;
+        nxf->nargs = 0;
+        savnxf = nxf;
+        if (skip())
+                goto rtn;
+
+        {
+                char *memp;
+                memp = (char *)savnxf;
+                /*
+                 *        1 s structure for the macro descriptor
+                 *        APERMAC Tchar *'s for pointers into the strings
+                 *        space for the Tchar's themselves
+                 */
+                memp += sizeof(Stack);
+                /*
+                 *        CPERMAC = the total # of characters for ALL arguments
+                 */
+#define        CPERMAC        200
+#define        APERMAC        9
+                memp += APERMAC * sizeof(Tchar *);
+                memp += CPERMAC * sizeof(Tchar);
+                nxf = (Stack *)memp;
+        }
+        lim = (Tchar *)nxf;
+        argpp = (Tchar **)(savnxf + 1);
+        argppend = &argpp[APERMAC];
+        SPACETEST(argppend, sizeof(Tchar *));
+        strp = (Tchar *)argppend;
+        /*
+         *        Zero out all the string pointers before filling them in.
+         */
+        for (j = 0; j < APERMAC; j++)
+                argpp[j] = 0;
+        /* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x",
+         *         savnxf, nxf, argpp, strp, lim WARN;
+         */
+        strflg = 0;
+        while (argpp != argppend && !skip()) {
+                *argpp++ = strp;
+                quote = 0;
+                if (cbits(i = getch()) == '"')
+                        quote++;
+                else 
+                        ch = i;
+                while (1) {
+                        i = getch();
+/* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */
+                        if (nlflg || (!quote && argpp != argppend && cbits(i) == ' '))
+                                break;        /* collects rest into $9 */
+                        if (   quote
+                            && cbits(i) == '"'
+                            && cbits(i = getch()) != '"') {
+                                ch = i;
+                                break;
+                        }
+                        *strp++ = i;
+                        if (strflg && strp >= lim) {
+                                /* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */
+                                ERROR "Macro argument too long" WARN;
+                                copyf--;
+                                edone(004);
+                        }
+                        SPACETEST(strp, 3 * sizeof(Tchar));
+                }
+                *strp++ = 0;
+        }
+        nxf = savnxf;
+        nxf->nargs = argpp - (Tchar **)(savnxf + 1);
+        argtop = strp;
+rtn:
+        copyf--;
+}
+
+
+void seta(void)
+{
+        int i;
+
+        i = cbits(getch()) - '0';
+        if (i > 0 && i <= APERMAC && i <= frame->nargs)
+                pushback(*(((Tchar **)(frame + 1)) + i - 1));
+}
+
+
+void caseda(void)
+{
+        app++;
+        casedi();
+}
+
+void casegd(void)
+{
+        int i, j;
+
+        skip();
+        if ((i = getrq()) == 0)
+                return;
+        if ((j = findmn(i)) >= 0) {
+                if (contabp[j].divsiz != NULL) {
+                        numtabp[DN].val = contabp[j].divsiz->dix;
+                        numtabp[DL].val = contabp[j].divsiz->diy;
+                }
+        }
+}
+
+#define FINDDIV(o) if ((o =  findmn(dip->curd)) < 0) \
+                        ERROR "lost diversion %s", unpair(dip->curd) WARN
+
+void casedi(void)
+{
+        int i, j, *k;
+
+        lgf++;
+        if (skip() || (i = getrq()) == 0) {
+                if (dip != d) {
+                        FINDDIV(savslot);
+                        wbf((Tchar)0);
+                }
+                if (dilev > 0) {
+                        numtabp[DN].val = dip->dnl;
+                        numtabp[DL].val = dip->maxl;
+                        FINDDIV(j);
+                        if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) {
+                                ERROR "Cannot alloc diversion size" WARN;
+                                done2(1);
+                        } else {
+                                contabp[j].divsiz->dix = numtabp[DN].val;
+                                contabp[j].divsiz->diy = numtabp[DL].val;
+                        }
+                        dip = &d[--dilev];
+                        offset = dip->op;
+                }
+                goto rtn;
+        }
+        if (++dilev == NDI) {
+                --dilev;
+                ERROR "Diversions nested too deep" WARN;
+                edone(02);
+        }
+        if (dip != d) {
+                FINDDIV(j);
+                savslot = j;
+                wbf((Tchar)0);
+        }
+        diflg++;
+        dip = &d[dilev];
+        dip->op = finds(i);
+        dip->curd = i;
+        clrmn(oldmn);
+        k = (int *) & dip->dnl;
+        for (j = 0; j < 10; j++)
+                k[j] = 0;        /*not op and curd*/
+rtn:
+        app = 0;
+        diflg = 0;
+}
+
+
+void casedt(void)
+{
+        lgf++;
+        dip->dimac = dip->ditrap = dip->ditf = 0;
+        skip();
+        dip->ditrap = vnumb((int *)0);
+        if (nonumb)
+                return;
+        skip();
+        dip->dimac = getrq();
+}
+
+#define LNSIZE 4000
+void casetl(void)
+{
+        int j;
+        int w[3];
+        Tchar buf[LNSIZE];
+        Tchar *tp;
+        Tchar i, delim;
+
+         /*
+          * bug fix
+          *
+          * if .tl is the first thing in the file, the p1
+          * doesn't come out, also the pagenumber will be 0
+          *
+          * tends too confuse the device filter (and the user as well)
+          */
+         if (dip == d && numtabp[NL].val == -1)
+                 newline(1);
+        dip->nls = 0;
+        skip();
+        if (ismot(delim = getch())) {
+                ch = delim;
+                delim = '\'';
+        } else 
+                delim = cbits(delim);
+        tp = buf;
+        numtabp[HP].val = 0;
+        w[0] = w[1] = w[2] = 0;
+        j = 0;
+        while (cbits(i = getch()) != '\n') {
+                if (cbits(i) == cbits(delim)) {
+                        if (j < 3)
+                                w[j] = numtabp[HP].val;
+                        numtabp[HP].val = 0;
+                        if (w[j] != 0)
+                                *tp++ = WORDSP;
+                        j++;
+                        *tp++ = 0;
+                } else {
+                        if (cbits(i) == pagech) {
+                                setn1(numtabp[PN].val, numtabp[findr('%')].fmt,
+                                      i&SFMASK);
+                                continue;
+                        }
+                        numtabp[HP].val += width(i);
+                        if (tp < &buf[LNSIZE-10]) {
+                                if (cbits(i) == ' ' && *tp != WORDSP)
+                                        *tp++ = WORDSP;
+                                *tp++ = i;
+                        } else {
+                                ERROR "Overflow in casetl" WARN;
+                        }
+                }
+        }
+        if (j<3)
+                w[j] = numtabp[HP].val;
+        *tp++ = 0;
+        *tp++ = 0;
+        *tp = 0;
+        tp = buf;
+        if (NROFF)
+                horiz(po);
+        while (i = *tp++)
+                pchar(i);
+        if (w[1] || w[2])
+                horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
+        while (i = *tp++)
+                pchar(i);
+        if (w[2]) {
+                horiz(lt - w[0] - w[1] - w[2] - j);
+                while (i = *tp++)
+                        pchar(i);
+        }
+        newline(0);
+        if (dip != d) {
+                if (dip->dnl > dip->hnl)
+                        dip->hnl = dip->dnl;
+        } else {
+                if (numtabp[NL].val > dip->hnl)
+                        dip->hnl = numtabp[NL].val;
+        }
+}
+
+
+void casepc(void)
+{
+        pagech = chget(IMP);
+}
+
+
+void casepm(void)
+{
+        int i, k;
+        int xx, cnt, tcnt, kk, tot;
+        Offset j;
+
+        kk = cnt = tcnt = 0;
+        tot = !skip();
+        stackdump();
+        for (i = 0; i < nm; i++) {
+                if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0)
+                        continue;
+                tcnt++;
+                j = contabp[i].mx;
+                for (k = 1; (j = blist[bindex(j)].nextoff) != -1; )
+                        k++; 
+                cnt++;
+                kk += k;
+                if (!tot)
+                        fprintf(stderr, "%-2.2s %d\n", unpair(xx), k);
+        }
+        fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
+}
+
+void stackdump(void)        /* dumps stack of macros in process */
+{
+        Stack *p;
+
+        if (frame != stk) {
+                fprintf(stderr, "stack: ");
+                for (p = frame; p != stk; p = p->pframe)
+                        fprintf(stderr, "%s ", unpair(p->mname));
+                fprintf(stderr, "\n");
+        }
+}
diff --git a/troff/n4.c b/troff/n4.c
@@ -0,0 +1,828 @@
+/*
+ * troff4.c
+ *
+ * number registers, conversion, arithmetic
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+
+int        regcnt = NNAMES;
+int        falsef        = 0;        /* on if inside false branch of if */
+
+#define        NHASHSIZE        128        /* must be 2**n */
+#define        NHASH(i)        ((i>>6)^i) & (NHASHSIZE-1)
+Numtab        *nhash[NHASHSIZE];
+
+Numtab *numtabp = NULL;
+#define NDELTA 400
+int ncnt = 0;
+
+void setn(void)
+{
+        int i, j, f;
+        Tchar ii;
+        Uchar *p;
+        char buf[NTM];                /* for \n(.S */
+
+        f = nform = 0;
+        if ((i = cbits(ii = getach())) == '+')
+                f = 1;
+        else if (i == '-')
+                f = -1;
+        else if (ii)        /* don't put it back if it's already back (thanks to jaap) */
+                ch = ii;
+        if (falsef)
+                f = 0;
+        if ((i = getsn()) == 0)
+                return;
+        p = unpair(i);
+        if (p[0] == '.')
+                switch (p[1]) {
+                case 's':
+                        i = pts;
+                        break;
+                case 'v':
+                        i = lss;
+                        break;
+                case 'f':
+                        i = font;
+                        break;
+                case 'p':
+                        i = pl;
+                        break;
+                case 't':
+                        i = findt1();
+                        break;
+                case 'o':
+                        i = po;
+                        break;
+                case 'l':
+                        i = ll;
+                        break;
+                case 'i':
+                        i = in;
+                        break;
+                case '$':
+                        i = frame->nargs;
+                        break;
+                case 'A':
+                        i = ascii;
+                        break;
+                case 'c':
+                        i = numtabp[CD].val;
+                        break;
+                case 'n':
+                        i = lastl;
+                        break;
+                case 'a':
+                        i = ralss;
+                        break;
+                case 'h':
+                        i = dip->hnl;
+                        break;
+                case 'd':
+                        if (dip != d)
+                                i = dip->dnl;
+                        else
+                                i = numtabp[NL].val;
+                        break;
+                case 'u':
+                        i = fi;
+                        break;
+                case 'j':
+                        i = ad + 2 * admod;
+                        break;
+                case 'w':
+                        i = widthp;
+                        break;
+                case 'x':
+                        i = nel;
+                        break;
+                case 'y':
+                        i = un;
+                        break;
+                case 'T':
+                        i = dotT;
+                        break;         /* -Tterm used in nroff */
+                case 'V':
+                        i = VERT;
+                        break;
+                case 'H':
+                        i = HOR;
+                        break;
+                case 'k':
+                        i = ne;
+                        break;
+                case 'P':
+                        i = print;
+                        break;
+                case 'L':
+                        i = ls;
+                        break;
+                case 'R':        /* maximal # of regs that can be addressed */
+                        i = 255*256 - regcnt; 
+                        break;
+                case 'z':
+                        p = unpair(dip->curd);
+                        *pbp++ = p[1];        /* watch order */
+                        *pbp++ = p[0];
+                        return;
+                case 'b':
+                        i = bdtab[font];
+                        break;
+                case 'F':
+                        cpushback(cfname[ifi]);
+                        return;
+                 case 'S':
+                         buf[0] = j = 0;        
+                         for( i = 0; tabtab[i] != 0 && i < NTAB; i++) {
+                                 if (i > 0)
+                                         buf[j++] = ' ';
+                                 sprintf(&buf[j], "%ld", tabtab[i] & TABMASK);
+                                 j = strlen(buf);
+                                 if ( tabtab[i] & RTAB)
+                                         sprintf(&buf[j], "uR");
+                                 else if (tabtab[i] & CTAB)
+                                         sprintf(&buf[j], "uC");
+                                 else
+                                         sprintf(&buf[j], "uL");
+                                 j += 2;
+                         }
+                         cpushback(buf);
+                         return;
+                default:
+                        goto s0;
+                }
+        else {
+s0:
+                if ((j = findr(i)) == -1)
+                        i = 0;
+                else {
+                        i = numtabp[j].val = numtabp[j].val + numtabp[j].inc * f;
+                        nform = numtabp[j].fmt;
+                }
+        }
+        setn1(i, nform, (Tchar) 0);
+}
+
+Tchar        numbuf[25];
+Tchar        *numbufp;
+
+int wrc(Tchar i)
+{
+        if (numbufp >= &numbuf[24])
+                return(0);
+        *numbufp++ = i;
+        return(1);
+}
+
+
+
+/* insert into input number i, in format form, with size-font bits bits */
+void setn1(int i, int form, Tchar bits)
+{
+        numbufp = numbuf;
+        nrbits = bits;
+        nform = form;
+        fnumb(i, wrc);
+        *numbufp = 0;
+        pushback(numbuf);
+}
+
+void prnumtab(Numtab *p)
+{
+        int i;
+        for (i = 0; i < ncnt; i++)
+                if (p)
+                        if (p[i].r != 0)
+                                fprintf(stderr, "slot %d, %s, val %d\n", i, unpair(p[i].r), p[i].val);
+                        else
+                                fprintf(stderr, "slot %d empty\n", i);
+                else
+                        fprintf(stderr, "slot %d empty\n", i);
+}
+
+void nnspace(void)
+{
+        ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA;
+        numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab));
+        if (numtabp == NULL) {
+                ERROR "not enough memory for registers (%d)", ncnt WARN;
+                exit(1);
+        }
+        numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab,
+                                                        sizeof(numtab));
+        if (numtabp == NULL) {
+                ERROR "Cannot initialize registers" WARN;
+                exit(1);
+        }
+}
+
+void grownumtab(void)
+{
+        ncnt += NDELTA;
+        numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab));
+        if (numtabp == NULL) {
+                ERROR "Too many number registers (%d)", ncnt WARN;
+                done2(04);
+        } else {
+                memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numtab),
+                                                0, NDELTA * sizeof(Numtab));
+                nrehash();
+        }
+}
+
+void nrehash(void)
+{
+        Numtab *p;
+        int i;
+
+        for (i=0; ilink = 0;
+        for (p=numtabp; p < &numtabp[ncnt]; p++) {
+                if (p->r == 0)
+                        continue;
+                i = NHASH(p->r);
+                p->link = nhash[i];
+                nhash[i] = p;
+        }
+}
+
+void nunhash(Numtab *rp)
+{
+        Numtab *p;
+        Numtab **lp;
+
+        if (rp->r == 0)
+                return;
+        lp = &nhash[NHASH(rp->r)];
+        p = *lp;
+        while (p) {
+                if (p == rp) {
+                        *lp = p->link;
+                        p->link = 0;
+                        return;
+                }
+                lp = &p->link;
+                p = p->link;
+        }
+}
+
+int findr(int i)
+{
+        Numtab *p;
+        int h = NHASH(i);
+
+        if (i == 0)
+                return(-1);
+a0:
+        for (p = nhash[h]; p; p = p->link)
+                if (i == p->r)
+                        return(p - numtabp);
+        for (p = numtabp; p < &numtabp[ncnt]; p++) {
+                if (p->r == 0) {
+                        p->r = i;
+                        p->link = nhash[h];
+                        nhash[h] = p;
+                        regcnt++;
+                        return(p - numtabp);
+                }
+        }
+        grownumtab();
+        goto a0;
+}
+
+int usedr(int i)        /* returns -1 if nr i has never been used */
+{
+        Numtab *p;
+
+        if (i == 0)
+                return(-1);
+        for (p = nhash[NHASH(i)]; p; p = p->link)
+                if (i == p->r)
+                        return(p - numtabp);
+        return -1;
+}
+
+
+int fnumb(int i, int (*f)(Tchar))
+{
+        int j;
+
+        j = 0;
+        if (i < 0) {
+                j = (*f)('-' | nrbits);
+                i = -i;
+        }
+        switch (nform) {
+        default:
+        case '1':
+        case 0:
+                return decml(i, f) + j;
+        case 'i':
+        case 'I':
+                return roman(i, f) + j;
+        case 'a':
+        case 'A':
+                return abc(i, f) + j;
+        }
+}
+
+
+int decml(int i, int (*f)(Tchar))
+{
+        int j, k;
+
+        k = 0;
+        nform--;
+        if ((j = i / 10) || (nform > 0))
+                k = decml(j, f);
+        return(k + (*f)((i % 10 + '0') | nrbits));
+}
+
+
+int roman(int i, int (*f)(Tchar))
+{
+
+        if (!i)
+                return((*f)('0' | nrbits));
+        if (nform == 'i')
+                return(roman0(i, f, "ixcmz", "vldw"));
+        else
+                return(roman0(i, f, "IXCMZ", "VLDW"));
+}
+
+
+int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp)
+{
+        int q, rem, k;
+
+        if (!i)
+                return(0);
+        k = roman0(i / 10, f, onesp + 1, fivesp + 1);
+        q = (i = i % 10) / 5;
+        rem = i % 5;
+        if (rem == 4) {
+                k += (*f)(*onesp | nrbits);
+                if (q)
+                        i = *(onesp + 1);
+                else
+                        i = *fivesp;
+                return(k += (*f)(i | nrbits));
+        }
+        if (q)
+                k += (*f)(*fivesp | nrbits);
+        while (--rem >= 0)
+                k += (*f)(*onesp | nrbits);
+        return(k);
+}
+
+
+int abc(int i, int (*f)(Tchar))
+{
+        if (!i)
+                return((*f)('0' | nrbits));
+        else
+                return(abc0(i - 1, f));
+}
+
+
+int abc0(int i, int (*f)(Tchar))
+{
+        int j, k;
+
+        k = 0;
+        if (j = i / 26)
+                k = abc0(j - 1, f);
+        return(k + (*f)((i % 26 + nform) | nrbits));
+}
+
+long atoi0(void)
+{
+        int c, k, cnt;
+        Tchar ii;
+        long i, acc;
+
+        acc = 0;
+        nonumb = 0;
+        cnt = -1;
+a0:
+        cnt++;
+        ii = getch();
+        c = cbits(ii);
+        switch (c) {
+        default:
+                ch = ii;
+                if (cnt)
+                        break;
+        case '+':
+                i = ckph();
+                if (nonumb)
+                        break;
+                acc += i;
+                goto a0;
+        case '-':
+                i = ckph();
+                if (nonumb)
+                        break;
+                acc -= i;
+                goto a0;
+        case '*':
+                i = ckph();
+                if (nonumb)
+                        break;
+                acc *= i;
+                goto a0;
+        case '/':
+                i = ckph();
+                if (nonumb)
+                        break;
+                if (i == 0) {
+                        flusho();
+                        ERROR "divide by zero." WARN;
+                        acc = 0;
+                } else
+                        acc /= i;
+                goto a0;
+        case '%':
+                i = ckph();
+                if (nonumb)
+                        break;
+                acc %= i;
+                goto a0;
+        case '&':        /*and*/
+                i = ckph();
+                if (nonumb)
+                        break;
+                if ((acc > 0) && (i > 0))
+                        acc = 1;
+                else
+                        acc = 0;
+                goto a0;
+        case ':':        /*or*/
+                i = ckph();
+                if (nonumb)
+                        break;
+                if ((acc > 0) || (i > 0))
+                        acc = 1;
+                else
+                        acc = 0;
+                goto a0;
+        case '=':
+                if (cbits(ii = getch()) != '=')
+                        ch = ii;
+                i = ckph();
+                if (nonumb) {
+                        acc = 0;
+                        break;
+                }
+                if (i == acc)
+                        acc = 1;
+                else
+                        acc = 0;
+                goto a0;
+        case '>':
+                k = 0;
+                if (cbits(ii = getch()) == '=')
+                        k++;
+                else
+                        ch = ii;
+                i = ckph();
+                if (nonumb) {
+                        acc = 0;
+                        break;
+                }
+                if (acc > (i - k))
+                        acc = 1;
+                else
+                        acc = 0;
+                goto a0;
+        case '<':
+                k = 0;
+                if (cbits(ii = getch()) == '=')
+                        k++;
+                else
+                        ch = ii;
+                i = ckph();
+                if (nonumb) {
+                        acc = 0;
+                        break;
+                }
+                if (acc < (i + k))
+                        acc = 1;
+                else
+                        acc = 0;
+                goto a0;
+        case ')':
+                break;
+        case '(':
+                acc = atoi0();
+                goto a0;
+        }
+        return(acc);
+}
+
+
+long ckph(void)
+{
+        Tchar i;
+        long j;
+
+        if (cbits(i = getch()) == '(')
+                j = atoi0();
+        else {
+                j = atoi1(i);
+        }
+        return(j);
+}
+
+
+/*
+ * print error about illegal numeric argument;
+ */
+void prnumerr(void)
+{
+        char err_buf[40];
+        static char warn[] = "Numeric argument expected";
+        int savcd = numtabp[CD].val;
+
+        if (numerr.type == RQERR)
+                sprintf(err_buf, "%c%s: %s", nb ? cbits(c2) : cbits(cc),
+                                                unpair(numerr.req), warn);
+        else
+                sprintf(err_buf, "\\%c'%s': %s", numerr.esc, &numerr.escarg,
+                                                                        warn);
+        if (frame != stk)        /* uncertainty correction */
+                numtabp[CD].val--;
+        ERROR "%s", err_buf WARN;
+        numtabp[CD].val = savcd;
+}
+
+
+long atoi1(Tchar ii)
+{
+        int i, j, digits;
+        double acc;        /* this is the only double in troff! */
+        int neg, abs, field, decpnt;
+        extern int ifnum;
+
+
+        neg = abs = field = decpnt = digits = 0;
+        acc = 0;
+        for (;;) {
+                i = cbits(ii);
+                switch (i) {
+                default:
+                        break;
+                case '+':
+                        ii = getch();
+                        continue;
+                case '-':
+                        neg = 1;
+                        ii = getch();
+                        continue;
+                case '|':
+                        abs = 1 + neg;
+                        neg = 0;
+                        ii = getch();
+                        continue;
+                }
+                break;
+        }
+a1:
+        while (i >= '0' && i <= '9') {
+                field++;
+                digits++;
+                acc = 10 * acc + i - '0';
+                ii = getch();
+                i = cbits(ii);
+        }
+        if (i == '.' && !decpnt++) {
+                field++;
+                digits = 0;
+                ii = getch();
+                i = cbits(ii);
+                goto a1;
+        }
+        if (!field) {
+                ch = ii;
+                goto a2;
+        }
+        switch (i) {
+        case 'u':
+                i = j = 1;        /* should this be related to HOR?? */
+                break;
+        case 'v':        /*VSs - vert spacing*/
+                j = lss;
+                i = 1;
+                break;
+        case 'm':        /*Ems*/
+                j = EM;
+                i = 1;
+                break;
+        case 'n':        /*Ens*/
+                j = EM;
+                if (TROFF)
+                        i = 2;
+                else
+                        i = 1;        /*Same as Ems in NROFF*/
+                break;
+        case 'p':        /*Points*/
+                j = INCH;
+                i = 72;
+                break;
+        case 'i':        /*Inches*/
+                j = INCH;
+                i = 1;
+                break;
+        case 'c':        /*Centimeters*/
+                /* if INCH is too big, this will overflow */
+                j = INCH * 50;
+                i = 127;
+                break;
+        case 'P':        /*Picas*/
+                j = INCH;
+                i = 6;
+                break;
+        default:
+                j = dfact;
+                ch = ii;
+                i = dfactd;
+        }
+        if (neg)
+                acc = -acc;
+        if (!noscale) {
+                acc = (acc * j) / i;
+        }
+        if (field != digits && digits > 0)
+                while (digits--)
+                        acc /= 10;
+        if (abs) {
+                if (dip != d)
+                        j = dip->dnl;
+                else
+                        j = numtabp[NL].val;
+                if (!vflag) {
+                        j = numtabp[HP].val;
+                }
+                if (abs == 2)
+                        j = -j;
+                acc -= j;
+        }
+a2:
+        nonumb = (!field || field == decpnt);
+        if (nonumb && (trace & TRNARGS) && !ismot(ii) && !nlflg && !ifnum) {
+                if (cbits(ii) != RIGHT ) /* Too painful to do right */
+                        prnumerr();
+        }
+        return(acc);
+}
+
+
+void caserr(void)
+{
+        int i, j;
+        Numtab *p;
+
+        lgf++;
+        while (!skip() && (i = getrq()) ) {
+                j = usedr(i);
+                if (j < 0)
+                        continue;
+                p = &numtabp[j];
+                nunhash(p);
+                p->r = p->val = p->inc = p->fmt = 0;
+                regcnt--;
+        }
+}
+
+/*
+ * .nr request; if tracing, don't check optional
+ * 2nd argument because tbl generates .in 1.5n
+ */
+void casenr(void)
+{
+        int i, j;
+        int savtr = trace;
+
+        lgf++;
+        skip();
+        if ((i = findr(getrq())) == -1)
+                goto rtn;
+        skip();
+        j = inumb(&numtabp[i].val);
+        if (nonumb)
+                goto rtn;
+        numtabp[i].val = j;
+        skip();
+        trace = 0;
+        j = atoi0();                /* BUG??? */
+        trace = savtr;
+        if (nonumb)
+                goto rtn;
+        numtabp[i].inc = j;
+rtn:
+        return;
+}
+
+void caseaf(void)
+{
+        int i, k;
+        Tchar j;
+
+        lgf++;
+        if (skip() || !(i = getrq()) || skip())
+                return;
+        k = 0;
+        j = getch();
+        if (!isalpha(cbits(j))) {
+                ch = j;
+                while ((j = cbits(getch())) >= '0' &&  j <= '9')
+                        k++;
+        }
+        if (!k)
+                k = j;
+        numtabp[findr(i)].fmt = k;        /* was k & BYTEMASK */
+}
+
+void setaf(void)        /* return format of number register */
+{
+        int i, j;
+
+        i = usedr(getsn());
+        if (i == -1)
+                return;
+        if (numtabp[i].fmt > 20)        /* it was probably a, A, i or I */
+                *pbp++ = numtabp[i].fmt;
+        else
+                for (j = (numtabp[i].fmt ? numtabp[i].fmt : 1); j; j--)
+                        *pbp++ = '0';
+}
+
+
+int vnumb(int *i)
+{
+        vflag++;
+        dfact = lss;
+        res = VERT;
+        return(inumb(i));
+}
+
+
+int hnumb(int *i)
+{
+        dfact = EM;
+        res = HOR;
+        return(inumb(i));
+}
+
+
+int inumb(int *n)
+{
+        int i, j, f;
+        Tchar ii;
+
+        f = 0;
+        if (n) {
+                if ((j = cbits(ii = getch())) == '+')
+                        f = 1;
+                else if (j == '-')
+                        f = -1;
+                else
+                        ch = ii;
+        }
+        i = atoi0();
+        if (n && f)
+                i = *n + f * i;
+        i = quant(i, res);
+        vflag = 0;
+        res = dfactd = dfact = 1;
+        if (nonumb)
+                i = 0;
+        return(i);
+}
+
+
+int quant(int n, int m)
+{
+        int i, neg;
+
+        neg = 0;
+        if (n < 0) {
+                neg++;
+                n = -n;
+        }
+        /* better as i = ((n + m/2)/m)*m */
+        i = n / m;
+        if (n - m * i > m / 2)
+                i += 1;
+        i *= m;
+        if (neg)
+                i = -i;
+        return(i);
+}
diff --git a/troff/n5.c b/troff/n5.c
@@ -0,0 +1,1150 @@
+/*
+ * troff5.c
+ * 
+ * misc processing requests
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int        iflist[NIF];
+int        ifx;
+int        ifnum = 0;        /* trying numeric expression for .if or .ie condition */
+
+void casead(void)
+{
+        int i;
+
+        ad = 1;
+        /* leave admod alone */
+        if (skip())
+                return;
+        switch (i = cbits(getch())) {
+        case 'r':        /* right adj, left ragged */
+                admod = 2;
+                break;
+        case 'l':        /* left adj, right ragged */
+                admod = ad = 0;        /* same as casena */
+                break;
+        case 'c':        /*centered adj*/
+                admod = 1;
+                break;
+        case 'b': 
+        case 'n':
+                admod = 0;
+                break;
+        case '0': 
+        case '2': 
+        case '4':
+                ad = 0;
+        case '1': 
+        case '3': 
+        case '5':
+                admod = (i - '0') / 2;
+        }
+}
+
+
+void casena(void)
+{
+        ad = 0;
+}
+
+
+void casefi(void)
+{
+        tbreak();
+        fi = 1;
+        pendnf = 0;
+}
+
+
+void casenf(void)
+{
+        tbreak();
+        fi = 0;
+}
+
+
+void casers(void)
+{
+        dip->nls = 0;
+}
+
+
+void casens(void)
+{
+        dip->nls++;
+}
+
+int
+chget(int c)
+{
+        Tchar i;
+
+        i = 0;
+        if (skip() || ismot(i = getch()) || cbits(i) == ' ' || cbits(i) == '\n') {
+                ch = i;
+                return(c);
+        } else 
+                return cbits(i);        /* was (i & BYTEMASK) */
+}
+
+
+void casecc(void)
+{
+        cc = chget('.');
+}
+
+
+void casec2(void)
+{
+        c2 = chget('\'');
+}
+
+
+void casehc(void)
+{
+        ohc = chget(OHC);
+}
+
+
+void casetc(void)
+{
+        tabc = chget(0);
+}
+
+
+void caselc(void)
+{
+        dotc = chget(0);
+}
+
+
+void casehy(void)
+{
+        int i;
+
+        hyf = 1;
+        if (skip())
+                return;
+        noscale++;
+        i = atoi0();
+        noscale = 0;
+        if (nonumb)
+                return;
+        hyf = max(i, 0);
+}
+
+
+void casenh(void)
+{
+        hyf = 0;
+}
+
+int
+max(int aa, int bb)
+{
+        if (aa > bb)
+                return(aa);
+        else 
+                return(bb);
+}
+
+
+void casece(void)
+{
+        int i;
+
+        noscale++;
+        skip();
+        i = max(atoi0(), 0);
+        if (nonumb)
+                i = 1;
+        tbreak();
+        ce = i;
+        noscale = 0;
+}
+
+
+void casein(void)
+{
+        int i;
+
+        if (skip())
+                i = in1;
+        else {
+                i = max(hnumb(&in), 0);
+                if (nonumb)
+                        i = in1;
+        }
+        tbreak();
+        in1 = in;
+        in = i;
+        if (!nc) {
+                un = in;
+                setnel();
+        }
+}
+
+
+void casell(void)
+{
+        int i;
+
+        if (skip())
+                i = ll1;
+        else {
+                i = max(hnumb(&ll), INCH / 10);
+                if (nonumb)
+                        i = ll1;
+        }
+        ll1 = ll;
+        ll = i;
+        setnel();
+}
+
+
+void caselt(void)
+{
+        int i;
+
+        if (skip())
+                i = lt1;
+        else {
+                i = max(hnumb(<), 0);
+                if (nonumb)
+                        i = lt1;
+        }
+        lt1 = lt;
+        lt = i;
+}
+
+
+void caseti(void)
+{
+        int i;
+
+        if (skip())
+                return;
+        i = max(hnumb(&in), 0);
+        tbreak();
+        un1 = i;
+        setnel();
+}
+
+
+void casels(void)
+{
+        int i;
+
+        noscale++;
+        if (skip())
+                i = ls1;
+        else {
+                i = max(inumb(&ls), 1);
+                if (nonumb)
+                        i = ls1;
+        }
+        ls1 = ls;
+        ls = i;
+        noscale = 0;
+}
+
+
+void casepo(void)
+{
+        int i;
+
+        if (skip())
+                i = po1;
+        else {
+                i = max(hnumb(&po), 0);
+                if (nonumb)
+                        i = po1;
+        }
+        po1 = po;
+        po = i;
+        if (TROFF & !ascii)
+                esc += po - po1;
+}
+
+
+void casepl(void)
+{
+        int i;
+
+        skip();
+        if ((i = vnumb(&pl)) == 0)
+                pl = 11 * INCH; /*11in*/
+        else 
+                pl = i;
+        if (numtabp[NL].val > pl)
+                numtabp[NL].val = pl;
+}
+
+
+void casewh(void)
+{
+        int i, j, k;
+
+        lgf++;
+        skip();
+        i = vnumb((int *)0);
+        if (nonumb)
+                return;
+        skip();
+        j = getrq();
+        if ((k = findn(i)) != NTRAP) {
+                mlist[k] = j;
+                return;
+        }
+        for (k = 0; k < NTRAP; k++)
+                if (mlist[k] == 0)
+                        break;
+        if (k == NTRAP) {
+                flusho();
+                ERROR "cannot plant trap." WARN;
+                return;
+        }
+        mlist[k] = j;
+        nlist[k] = i;
+}
+
+
+void casech(void)
+{
+        int i, j, k;
+
+        lgf++;
+        skip();
+        if (!(j = getrq()))
+                return;
+        else 
+                for (k = 0; k < NTRAP; k++)
+                        if (mlist[k] == j)
+                                break;
+        if (k == NTRAP)
+                return;
+        skip();
+        i = vnumb((int *)0);
+        if (nonumb)
+                mlist[k] = 0;
+        nlist[k] = i;
+}
+
+int
+findn(int i)
+{
+        int k;
+
+        for (k = 0; k < NTRAP; k++)
+                if ((nlist[k] == i) && (mlist[k] != 0))
+                        break;
+        return(k);
+}
+
+
+void casepn(void)
+{
+        int i;
+
+        skip();
+        noscale++;
+        i = max(inumb(&numtabp[PN].val), 0);
+        noscale = 0;
+        if (!nonumb) {
+                npn = i;
+                npnflg++;
+        }
+}
+
+
+void casebp(void)
+{
+        int i;
+        Stack *savframe;
+
+        if (dip != d)
+                return;
+        savframe = frame;
+        skip();
+        if ((i = inumb(&numtabp[PN].val)) < 0)
+                i = 0;
+        tbreak();
+        if (!nonumb) {
+                npn = i;
+                npnflg++;
+        } else if (dip->nls)
+                return;
+        eject(savframe);
+}
+
+void casetm(void)
+{
+        casetm1(0, stderr);
+}
+
+
+void casefm(void)
+{
+        static struct fcache {
+                char *name;
+                FILE *fp;
+        } fcache[15];
+        int i;
+
+        if ( skip() || !getname()) {
+                ERROR "fm: missing filename" WARN;
+                return;
+        }
+                
+        for (i = 0; i < 15 && fcache[i].fp != NULL; i++) {
+                if (strcmp(nextf, fcache[i].name) == 0)
+                        break;
+        }
+        if (i >= 15) {
+                ERROR "fm: too many streams" WARN;
+                return;
+        }
+        if (fcache[i].fp == NULL) {
+                if( (fcache[i].fp = fopen(unsharp(nextf), "w")) == NULL) {
+                        ERROR "fm: cannot open %s", nextf WARN;
+                        return;
+                }
+                fcache[i].name = strdupl(nextf);
+        }
+        casetm1(0, fcache[i].fp);
+}
+
+void casetm1(int ab, FILE *out) 
+{
+        int i, j, c;
+        char *p;
+        char tmbuf[NTM];
+
+        lgf++;
+        copyf++;
+        if (ab) {
+                if (skip())
+                        ERROR "User Abort" WARN;
+                else {
+                        extern int error;
+                        int savtrac = trace;
+                        i = trace = 0;
+                        noscale++;
+                        i = inumb(&trace);
+                        noscale--;
+                        if (i) {
+                                error = i;
+                                if (nlflg || skip())
+                                        ERROR "User Abort, exit code %d", i WARN;
+                        }
+                        trace = savtrac;
+                }
+        } else
+                skip();        
+        for (i = 0; i < NTM - 2; ) {
+                if ((c = cbits(getch())) == '\n' || c == RIGHT)
+                        break;
+                else if (c == MINUS) {        /* special pleading for strange encodings */
+                        tmbuf[i++] = '\\';
+                        tmbuf[i++] = '-';
+                } else if (c == PRESC) {
+                        tmbuf[i++] = '\\';
+                        tmbuf[i++] = 'e';
+                } else if (c == FILLER) {
+                        tmbuf[i++] = '\\';
+                        tmbuf[i++] = '&';
+                } else if (c == UNPAD) {
+                        tmbuf[i++] = '\\';
+                        tmbuf[i++] = ' ';
+                } else if (c == OHC) {
+                        tmbuf[i++] = '\\';
+                        tmbuf[i++] = '%';
+                } else if (c >= ALPHABET) {
+                        p = chname(c);
+                        switch (*p) {
+                        case MBchar:
+                                strcpy(&tmbuf[i], p+1);
+                                break;
+                        case Number:
+                                sprintf(&tmbuf[i], "\\N'%s'", p+1);
+                                break;
+                        case Troffchar:
+                                if ((j = strlen(p+1)) == 2)
+                                        sprintf(&tmbuf[i], "\\(%s", p+1);
+                                else
+                                        sprintf(&tmbuf[i], "\\C'%s'", p+1);
+                                break;
+                        default:
+                                sprintf(&tmbuf[i]," %s? ", p);
+                                break;
+                        }
+                        j = strlen(&tmbuf[i]);
+                        i += j;
+                } else
+                        tmbuf[i++] = c;
+        }
+        tmbuf[i] = 0;
+        if (ab)        /* truncate output */
+                obufp = obuf;        /* should be a function in n2.c */
+        flusho();
+        if (i)
+                fprintf(out, "%s\n", tmbuf);
+        fflush(out);
+        copyf--;
+        lgf--;
+}
+
+
+void casesp(void)
+{
+        casesp1(0);
+}
+
+void casesp1(int a)
+{
+        int i, j, savlss;
+
+        tbreak();
+        if (dip->nls || trap)
+                return;
+        i = findt1();
+        if (!a) {
+                skip();
+                j = vnumb((int *)0);
+                if (nonumb)
+                        j = lss;
+        } else 
+                j = a;
+        if (j == 0)
+                return;
+        if (i < j)
+                j = i;
+        savlss = lss;
+        if (dip != d)
+                i = dip->dnl; 
+        else 
+                i = numtabp[NL].val;
+        if ((i + j) < 0)
+                j = -i;
+        lss = j;
+        newline(0);
+        lss = savlss;
+}
+
+
+void casert(void)
+{
+        int a, *p;
+
+        skip();
+        if (dip != d)
+                p = &dip->dnl; 
+        else 
+                p = &numtabp[NL].val;
+        a = vnumb(p);
+        if (nonumb)
+                a = dip->mkline;
+        if ((a < 0) || (a >= *p))
+                return;
+        nb++;
+        casesp1(a - *p);
+}
+
+
+void caseem(void)
+{
+        lgf++;
+        skip();
+        em = getrq();
+}
+
+
+void casefl(void)
+{
+        tbreak();
+        if (!ascii)
+                ptflush();
+        flusho();
+}
+
+
+void caseev(void)
+{
+        int nxev;
+
+        if (skip()) {
+e0:
+                if (evi == 0)
+                        return;
+                nxev =  evlist[--evi];
+                goto e1;
+        }
+        noscale++;
+        nxev = atoi0();
+        noscale = 0;
+        if (nonumb)
+                goto e0;
+        flushi();
+        if (nxev >= NEV || nxev < 0 || evi >= EVLSZ) {
+                flusho();
+                ERROR "cannot do .ev %d", nxev WARN;
+                if (error)
+                        done2(040);
+                else 
+                        edone(040);
+                return;
+        }
+        evlist[evi++] = ev;
+e1:
+        if (ev == nxev)
+                return;
+        ev = nxev;
+        envp = &env[ev];
+}
+
+void envcopy(Env *e1, Env *e2)        /* copy env e2 to e1 */
+{
+        *e1 = *e2;        /* rumor hath that this fails on some machines */
+}
+
+
+void caseel(void)
+{
+        if (--ifx < 0) {
+                ifx = 0;
+                iflist[0] = 0;
+        }
+        caseif1(2);
+}
+
+
+void caseie(void)
+{
+        if (ifx >= NIF) {
+                ERROR "if-else overflow." WARN;
+                ifx = 0;
+                edone(040);
+        }
+        caseif1(1);
+        ifx++;
+}
+
+
+void caseif(void)
+{
+        caseif1(0);
+}
+
+void caseif1(int x)
+{
+        extern int falsef;
+        int notflag, true;
+        Tchar i;
+
+        if (x == 2) {
+                notflag = 0;
+                true = iflist[ifx];
+                goto i1;
+        }
+        true = 0;
+        skip();
+        if ((cbits(i = getch())) == '!') {
+                notflag = 1;
+        } else {
+                notflag = 0;
+                ch = i;
+        }
+        ifnum++;
+        i = atoi0();
+        ifnum = 0;
+        if (!nonumb) {
+                if (i > 0)
+                        true++;
+                goto i1;
+        }
+        i = getch();
+        switch (cbits(i)) {
+        case 'e':
+                if (!(numtabp[PN].val & 01))
+                        true++;
+                break;
+        case 'o':
+                if (numtabp[PN].val & 01)
+                        true++;
+                break;
+        case 'n':
+                if (NROFF)
+                        true++;
+                break;
+        case 't':
+                if (TROFF)
+                        true++;
+                break;
+        case ' ':
+                break;
+        default:
+                true = cmpstr(i);
+        }
+i1:
+        true ^= notflag;
+        if (x == 1)
+                iflist[ifx] = !true;
+        if (true) {
+i2:
+                while ((cbits(i = getch())) == ' ')
+                        ;
+                if (cbits(i) == LEFT)
+                        goto i2;
+                ch = i;
+                nflush++;
+        } else {
+                if (!nlflg) {
+                        copyf++;
+                        falsef++;
+                        eatblk(0);
+                        copyf--;
+                        falsef--;
+                }
+        }
+}
+
+void eatblk(int inblk)
+{
+        int cnt, i;
+
+        cnt = 0;
+        do {
+                if (ch)        {
+                        i = cbits(ch);
+                        ch = 0;
+                } else
+                        i = cbits(getch0());
+                if (i == ESC)
+                        cnt++;
+                else {
+                        if (cnt == 1)
+                                switch (i) {
+                                case '{':  i = LEFT; break;
+                                case '}':  i = RIGHT; break;
+                                case '\n': i = 'x'; break;
+                                }
+                        cnt = 0;
+                }
+                if (i == LEFT) eatblk(1);
+        } while ((!inblk && (i != '\n')) || (inblk && (i != RIGHT)));
+        if (i == '\n') {
+                nlflg++;
+                if (ip == 0)
+                        numtabp[CD].val++;
+        }
+}
+
+int
+cmpstr(Tchar c)
+{
+        int j, delim;
+        Tchar i;
+        int val;
+        int savapts, savapts1, savfont, savfont1, savpts, savpts1;
+        Tchar string[1280];
+        Tchar *sp;
+
+        if (ismot(c))
+                return(0);
+        delim = cbits(c);
+        savapts = apts;
+        savapts1 = apts1;
+        savfont = font;
+        savfont1 = font1;
+        savpts = pts;
+        savpts1 = pts1;
+        sp = string;
+        while ((j = cbits(i = getch()))!=delim && j!='\n' && sp<&string[1280-1])
+                *sp++ = i;
+        if (sp >= string + 1280) {
+                ERROR "too-long string compare." WARN;
+                edone(0100);
+        }
+        if (nlflg) {
+                val = sp==string;
+                goto rtn;
+        }
+        *sp = 0;
+        apts = savapts;
+        apts1 = savapts1;
+        font = savfont;
+        font1 = savfont1;
+        pts = savpts;
+        pts1 = savpts1;
+        mchbits();
+        val = 1;
+        sp = string;
+        while ((j = cbits(i = getch())) != delim && j != '\n') {
+                if (*sp != i) {
+                        eat(delim);
+                        val = 0;
+                        goto rtn;
+                }
+                sp++;
+        }
+        if (*sp)
+                val = 0;
+rtn:
+        apts = savapts;
+        apts1 = savapts1;
+        font = savfont;
+        font1 = savfont1;
+        pts = savpts;
+        pts1 = savpts1;
+        mchbits();
+        return(val);
+}
+
+
+void caserd(void)
+{
+
+        lgf++;
+        skip();
+        getname();
+        if (!iflg) {
+                if (quiet) {
+                        if (NROFF) {
+                                echo_off();
+                                flusho();
+                        }
+                        fprintf(stderr, "\007"); /*bell*/
+                } else {
+                        if (nextf[0]) {
+                                fprintf(stderr, "%s:", nextf);
+                        } else {
+                                fprintf(stderr, "\007"); /*bell*/
+                        }
+                }
+        }
+        collect();
+        tty++;
+        pushi(RD_OFFSET, PAIR('r','d'));
+}
+
+int
+rdtty(void)
+{
+        char        onechar;
+
+        onechar = 0;
+        if (read(0, &onechar, 1) == 1) {
+                if (onechar == '\n')
+                        tty++;
+                else 
+                        tty = 1;
+                if (tty != 3)
+                        return(onechar);
+        }
+        tty = 0;
+        if (NROFF && quiet)
+                echo_on();
+        return(0);
+}
+
+
+void caseec(void)
+{
+        eschar = chget('\\');
+}
+
+
+void caseeo(void)
+{
+        eschar = 0;
+}
+
+
+void caseta(void)
+{
+        int i, j, k;
+
+        tabtab[0] = nonumb = 0;
+        for (i = 0; ((i < (NTAB - 1)) && !nonumb); i++) {
+                if (skip())
+                        break;
+                k = tabtab[max(i-1, 0)] & TABMASK;
+                if ((j = max(hnumb(&k), 0)) > TABMASK) {
+                        ERROR "Tab too far away" WARN;
+                        j = TABMASK;
+                }
+                tabtab[i] = j & TABMASK;
+                if (!nonumb) 
+                        switch (cbits(ch)) {
+                        case 'C':
+                                tabtab[i] |= CTAB;
+                                break;
+                        case 'R':
+                                tabtab[i] |= RTAB;
+                                break;
+                        default: /*includes L*/
+                                break;
+                        }
+                nonumb = ch = 0;
+        }
+        if (!skip())
+                ERROR "Too many tab stops" WARN;
+        tabtab[i] = 0;
+}
+
+
+void casene(void)
+{
+        int i, j;
+
+        skip();
+        i = vnumb((int *)0);
+        if (nonumb)
+                i = lss;
+        if (dip == d && numtabp[NL].val == -1) {
+                newline(1);
+                return;
+        }
+        if (i > (j = findt1())) {
+                i = lss;
+                lss = j;
+                dip->nls = 0;
+                newline(0);
+                lss = i;
+        }
+}
+
+
+void casetr(void)
+{
+        int i, j;
+        Tchar k;
+
+        lgf++;
+        skip();
+        while ((i = cbits(k=getch())) != '\n') {
+                if (ismot(k))
+                        return;
+                if (ismot(k = getch()))
+                        return;
+                if ((j = cbits(k)) == '\n')
+                        j = ' ';
+                trtab[i] = j;
+        }
+}
+
+
+void casecu(void)
+{
+        cu++;
+        caseul();
+}
+
+
+void caseul(void)
+{
+        int i;
+
+        noscale++;
+        skip();
+        i = max(atoi0(), 0);
+        if (nonumb)
+                i = 1;
+        if (ul && (i == 0)) {
+                font = sfont;
+                ul = cu = 0;
+        }
+        if (i) {
+                if (!ul) {
+                        sfont = font;
+                        font = ulfont;
+                }
+                ul = i;
+        }
+        noscale = 0;
+        mchbits();
+}
+
+
+void caseuf(void)
+{
+        int i, j;
+
+        if (skip() || !(i = getrq()) || i == 'S' ||  (j = findft(i))  == -1)
+                ulfont = ULFONT; /*default underline position*/
+        else 
+                ulfont = j;
+        if (NROFF && ulfont == FT)
+                ulfont = ULFONT;
+}
+
+
+void caseit(void)
+{
+        int i;
+
+        lgf++;
+        it = itmac = 0;
+        noscale++;
+        skip();
+        i = atoi0();
+        skip();
+        if (!nonumb && (itmac = getrq()))
+                it = i;
+        noscale = 0;
+}
+
+
+void casemc(void)
+{
+        int i;
+
+        if (icf > 1)
+                ic = 0;
+        icf = 0;
+        if (skip())
+                return;
+        ic = getch();
+        icf = 1;
+        skip();
+        i = max(hnumb((int *)0), 0);
+        if (!nonumb)
+                ics = i;
+}
+
+
+void casemk(void)
+{
+        int i, j;
+
+        if (dip != d)
+                j = dip->dnl; 
+        else 
+                j = numtabp[NL].val;
+        if (skip()) {
+                dip->mkline = j;
+                return;
+        }
+        if ((i = getrq()) == 0)
+                return;
+        numtabp[findr(i)].val = j;
+}
+
+
+void casesv(void)
+{
+        int i;
+
+        skip();
+        if ((i = vnumb((int *)0)) < 0)
+                return;
+        if (nonumb)
+                i = 1;
+        sv += i;
+        caseos();
+}
+
+
+void caseos(void)
+{
+        int savlss;
+
+        if (sv <= findt1()) {
+                savlss = lss;
+                lss = sv;
+                newline(0);
+                lss = savlss;
+                sv = 0;
+        }
+}
+
+
+void casenm(void)
+{
+        int i;
+
+        lnmod = nn = 0;
+        if (skip())
+                return;
+        lnmod++;
+        noscale++;
+        i = inumb(&numtabp[LN].val);
+        if (!nonumb)
+                numtabp[LN].val = max(i, 0);
+        getnm(&ndf, 1);
+        getnm(&nms, 0);
+        getnm(&ni, 0);
+        getnm(&nmwid, 3);        /* really kludgy! */
+        noscale = 0;
+        nmbits = chbits;
+}
+
+/*
+ * .nm relies on the fact that illegal args are skipped; don't warn
+ * for illegality of these
+ */
+void getnm(int *p, int min)
+{
+        int i;
+        int savtr = trace;
+
+        eat(' ');
+        if (skip())
+                return;
+        trace = 0;
+        i = atoi0();
+        if (nonumb)
+                return;
+        *p = max(i, min);
+        trace = savtr;
+}
+
+
+void casenn(void)
+{
+        noscale++;
+        skip();
+        nn = max(atoi0(), 1);
+        noscale = 0;
+}
+
+
+void caseab(void)
+{
+        casetm1(1, stderr);
+        done3(0);
+}
+
+
+/* nroff terminal handling has been pretty well excised */
+/* as part of the merge with troff.  these are ghostly remnants, */
+/* called, but doing nothing. restore them at your peril. */
+
+
+void save_tty(void)                        /*save any tty settings that may be changed*/
+{
+}
+
+
+void restore_tty(void)                        /*restore tty settings from beginning*/
+{
+}
+
+
+void set_tty(void)
+{
+}
+
+
+void echo_off(void)                        /*turn off ECHO for .rd in "-q" mode*/
+{
+}
+
+
+void echo_on(void)                        /*restore ECHO after .rd in "-q" mode*/
+{
+}
diff --git a/troff/n6.c b/troff/n6.c
@@ -0,0 +1,363 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+#include 
+
+/*
+ * n6.c -- width functions, sizes and fonts
+*/
+
+int
+n_width(Tchar j)
+{
+        int i, k;
+
+        if (iszbit(j))
+                return 0;
+        if (ismot(j)) {
+                if (isvmot(j))
+                        return(0);
+                k = absmot(j);
+                if (isnmot(j))
+                        k = -k;
+                return(k);
+        }
+        i = cbits(j);
+        if (i < ' ') {
+                if (i == '\b')
+                        return(-widthp);
+                if (i == PRESC)
+                        i = eschar;
+                else if (i == HX)
+                        return(0);
+        }
+        if (i == ohc)
+                return(0);
+        i = trtab[i];
+        if (i < ' ')
+                return(0);
+        if (i >= t.tfont.nchars)        /* not on the font */
+                k = t.Char;                /* really ought to check properly */
+        else
+                k = t.tfont.wp[i].wid * t.Char;
+        widthp = k;
+        return(k);
+}
+
+
+Tchar n_setch(int c)
+{
+        return t_setch(c);
+}
+
+Tchar n_setabs(void)        /* set absolute char from \N'...' */
+{                        /* for now, a no-op */
+        return t_setabs();
+}
+
+int n_findft(int i)
+{
+        int k;
+
+        if ((k = i - '0') >= 0 && k <= nfonts && k < smnt)
+                return(k);
+        for (k = 0; fontlab[k] != i; k++)
+                if (k > nfonts)
+                        return(-1);
+        return(k);
+}
+
+
+
+void n_mchbits(void)
+{
+        chbits = 0;
+        setfbits(chbits, font);
+        sps = width(' ' | chbits);
+}
+
+
+void n_setps(void )
+{
+        int i, j;
+
+        i = cbits(getch());
+        if (isdigit(i)) {                /* \sd or \sdd */
+                i -= '0';
+                if (i == 0)                /* \s0 */
+                        ;
+                else if (i <= 3 && (ch=getch()) && isdigit(cbits(ch))) {        /* \sdd */
+                        ch = 0;
+                }
+        } else if (i == '(') {                /* \s(dd */
+                getch();
+                getch();
+        } else if (i == '+' || i == '-') {        /* \s+, \s- */
+                j = cbits(getch());
+                if (isdigit(j)) {                /* \s+d, \s-d */
+                        ;
+                } else if (j == '(') {                /* \s+(dd, \s-(dd */
+                        getch();
+                        getch();
+                }
+        }
+}
+
+
+Tchar n_setht(void)                /* set character height from \H'...' */
+{
+
+        getch();
+        inumb(&apts);
+        getch();
+        return(0);
+}
+
+
+Tchar n_setslant(void)                /* set slant from \S'...' */
+{
+        int n;
+
+        getch();
+        n = 0;
+        n = inumb(&n);
+        getch();
+        return(0);
+}
+
+
+void n_caseft(void)
+{
+        skip();
+        setfont(1);
+}
+
+
+void n_setfont(int a)
+{
+        int i, j;
+
+        if (a)
+                i = getrq();
+        else 
+                i = getsn();
+        if (!i || i == 'P') {
+                j = font1;
+                goto s0;
+        }
+        if (i == 'S' || i == '0')
+                return;
+        if ((j = findft(i)) == -1)
+                return;
+s0:
+        font1 = font;
+        font = j;
+        mchbits();
+}
+
+
+void n_setwd(void)
+{
+        int base, wid;
+        Tchar i;
+        int        delim, emsz, k;
+        int        savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+
+        base = numtabp[ST].val = numtabp[ST].val = wid = numtabp[CT].val = 0;
+        if (ismot(i = getch()))
+                return;
+        delim = cbits(i);
+        savhp = numtabp[HP].val;
+        numtabp[HP].val = 0;
+        savapts = apts;
+        savapts1 = apts1;
+        savfont = font;
+        savfont1 = font1;
+        savpts = pts;
+        savpts1 = pts1;
+        setwdf++;
+        while (cbits(i = getch()) != delim && !nlflg) {
+                k = width(i);
+                wid += k;
+                numtabp[HP].val += k;
+                if (!ismot(i)) {
+                        emsz = (INCH * pts + 36) / 72;
+                } else if (isvmot(i)) {
+                        k = absmot(i);
+                        if (isnmot(i))
+                                k = -k;
+                        base -= k;
+                        emsz = 0;
+                } else 
+                        continue;
+                if (base < numtabp[SB].val)
+                        numtabp[SB].val = base;
+                if ((k = base + emsz) > numtabp[ST].val)
+                        numtabp[ST].val = k;
+        }
+        setn1(wid, 0, (Tchar) 0);
+        numtabp[HP].val = savhp;
+        apts = savapts;
+        apts1 = savapts1;
+        font = savfont;
+        font1 = savfont1;
+        pts = savpts;
+        pts1 = savpts1;
+        mchbits();
+        setwdf = 0;
+}
+
+
+Tchar n_vmot(void)
+{
+        dfact = lss;
+        vflag++;
+        return n_mot();
+}
+
+
+Tchar n_hmot(void)
+{
+        dfact = EM;
+        return n_mot();
+}
+
+
+Tchar n_mot(void)
+{
+        int j, n;
+        Tchar i;
+
+        j = HOR;
+        getch(); /*eat delim*/
+        if (n = atoi0()) {
+                if (vflag)
+                        j = VERT;
+                i = makem(quant(n, j));
+        } else
+                i = 0;
+        getch();
+        vflag = 0;
+        dfact = 1;
+        return(i);
+}
+
+
+Tchar n_sethl(int k)
+{
+        int j;
+        Tchar i;
+
+        j = t.Halfline;
+        if (k == 'u')
+                j = -j;
+        else if (k == 'r')
+                j = -2 * j;
+        vflag++;
+        i = makem(j);
+        vflag = 0;
+        return(i);
+}
+
+
+Tchar n_makem(int i)
+{
+        Tchar j;
+
+        if (i >= 0)
+                j = i;
+        else
+                j = -i;
+        j |= MOT;
+        if (i < 0)
+                j |= NMOT;
+        if (vflag)
+                j |= VMOT;
+        return(j);
+}
+
+
+void n_casefp(void)
+{
+        int i, j;
+
+        skip();
+        if ((i = cbits(getch()) - '0') < 0 || i > nfonts)
+                return;
+        if (skip() || !(j = getrq()))
+                return;
+        fontlab[i] = j;
+}
+
+
+
+void n_casebd(void)
+{
+        int i, j, k;
+
+        j = k = 0;
+bd0:
+        if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
+                if (k)
+                        goto bd1;
+                else 
+                        return;
+        }
+        if (j == smnt) {
+                k = smnt;
+                goto bd0;
+        }
+        if (k) {
+                sbold = j;
+                j = k;
+        }
+bd1:
+        skip();
+        noscale++;
+        bdtab[j] = atoi0();
+        noscale = 0;
+}
+
+
+void n_casevs(void)
+{
+        int i;
+
+        skip();
+        vflag++;
+        dfact = INCH; /*default scaling is points!*/
+        dfactd = 72;
+        res = VERT;
+        i = inumb(&lss);
+        if (nonumb)
+                i = lss1;
+        if (i < VERT)
+                i = VERT;        /* was VERT */
+        lss1 = lss;
+        lss = i;
+}
+
+
+
+
+Tchar n_xlss(void)
+{
+        /* stores \x'...' into
+        /* two successive Tchars.
+        /* the first contains HX, the second the value,
+        /* encoded as a vertical motion.
+        /* decoding is done in n2.c by pchar().
+        */
+        int        i;
+
+        getch();
+        dfact = lss;
+        i = quant(atoi0(), VERT);
+        dfact = 1;
+        getch();
+        if (i >= 0)
+                *pbp++ = MOT | VMOT | i;
+        else
+                *pbp++ = MOT | VMOT | NMOT | -i;
+        return(HX);
+}
diff --git a/troff/n7.c b/troff/n7.c
@@ -0,0 +1,837 @@
+#define _BSD_SOURCE 1        /* isascii */
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#ifdef STRICT
+        /* not in ANSI or POSIX */
+#define        isascii(a) ((a) >= 0 && (a) <= 127)
+#endif
+
+#define GETCH gettch
+Tchar        gettch(void);
+
+
+/*
+ * troff7.c
+ * 
+ * text
+ */
+
+int        brflg;
+
+void tbreak(void)
+{
+        int pad, k;
+        Tchar *i, j;
+        int resol;
+        int un0 = un;
+
+        trap = 0;
+        if (nb)
+                return;
+        if (dip == d && numtabp[NL].val == -1) {
+                newline(1);
+                return;
+        }
+        if (!nc) {
+                setnel();
+                if (!wch)
+                        return;
+                if (pendw)
+                        getword(1);
+                movword();
+        } else if (pendw && !brflg) {
+                getword(1);
+                movword();
+        }
+        *linep = dip->nls = 0;
+        if (NROFF && dip == d)
+                horiz(po);
+        if (lnmod)
+                donum();
+        lastl = ne;
+        if (brflg != 1) {
+                totout = 0;
+        } else if (ad) {
+                if ((lastl = ll - un) < ne)
+                        lastl = ne;
+        }
+        if (admod && ad && (brflg != 2)) {
+                lastl = ne;
+                adsp = adrem = 0;
+                if (admod == 1)
+                        un +=  quant(nel / 2, HOR);
+                else if (admod == 2)
+                        un += nel;
+        }
+        totout++;
+        brflg = 0;
+        if (lastl + un > dip->maxl)
+                dip->maxl = lastl + un;
+        horiz(un);
+        if (NROFF) {
+                if (adrem % t.Adj)
+                        resol = t.Hor; 
+                else 
+                        resol = t.Adj;
+        } else
+                resol = HOR;
+
+        lastl = ne + (nwd-1) * adsp + adrem;
+        for (i = line; nc > 0; ) {
+                if ((cbits(j = *i++)) == ' ') {
+                        pad = 0;
+                        do {
+                                pad += width(j);
+                                nc--;
+                        } while ((cbits(j = *i++)) == ' ');
+                        i--;
+                        pad += adsp;
+                        --nwd;
+                        if (adrem) {
+                                if (adrem < 0) {
+                                        pad -= resol;
+                                        adrem += resol;
+                                } else if ((totout & 01) || adrem / resol >= nwd) {
+                                        pad += resol;
+                                        adrem -= resol;
+                                }
+                        }
+                        pchar((Tchar) WORDSP);
+                        horiz(pad);
+                } else {
+                        pchar(j);
+                        nc--;
+                }
+        }
+        if (ic) {
+                if ((k = ll - un0 - lastl + ics) > 0)
+                        horiz(k);
+                pchar(ic);
+        }
+        if (icf)
+                icf++;
+        else 
+                ic = 0;
+        ne = nwd = 0;
+        un = in;
+        setnel();
+        newline(0);
+        if (dip != d) {
+                if (dip->dnl > dip->hnl)
+                        dip->hnl = dip->dnl;
+        } else {
+                if (numtabp[NL].val > dip->hnl)
+                        dip->hnl = numtabp[NL].val;
+        }
+        for (k = ls - 1; k > 0 && !trap; k--)
+                newline(0);
+        spread = 0;
+}
+
+void donum(void)
+{
+        int i, nw;
+        int lnv = numtabp[LN].val;
+
+        nrbits = nmbits;
+        nw = width('1' | nrbits);
+        if (nn) {
+                nn--;
+                goto d1;
+        }
+        if (lnv % ndf) {
+                numtabp[LN].val++;
+d1:
+                un += nw * (nmwid + nms + ni);
+                return;
+        }
+        i = 0;
+        do {                /* count digits in numtabp[LN].val */
+                i++;
+        } while ((lnv /= 10) > 0);
+        horiz(nw * (ni + max(nmwid-i, 0)));
+        nform = 0;
+        fnumb(numtabp[LN].val, pchar);
+        un += nw * nms;
+        numtabp[LN].val++;
+}
+
+
+void text(void)
+{
+        Tchar i;
+        static int spcnt;
+
+        nflush++;
+        numtabp[HP].val = 0;
+        if ((dip == d) && (numtabp[NL].val == -1)) {
+                newline(1); 
+                return;
+        }
+        setnel();
+        if (ce || !fi) {
+                nofill();
+                return;
+        }
+        if (pendw)
+                goto t4;
+        if (pendt)
+                if (spcnt)
+                        goto t2; 
+                else 
+                        goto t3;
+        pendt++;
+        if (spcnt)
+                goto t2;
+        while ((cbits(i = GETCH())) == ' ') {
+                spcnt++;
+                numtabp[HP].val += sps;
+                widthp = sps;
+        }
+        if (nlflg) {
+t1:
+                nflush = pendt = ch = spcnt = 0;
+                callsp();
+                return;
+        }
+        ch = i;
+        if (spcnt) {
+t2:
+                tbreak();
+                if (nc || wch)
+                        goto rtn;
+                un += spcnt * sps;
+                spcnt = 0;
+                setnel();
+                if (trap)
+                        goto rtn;
+                if (nlflg)
+                        goto t1;
+        }
+t3:
+        if (spread)
+                goto t5;
+        if (pendw || !wch)
+t4:
+                if (getword(0))
+                        goto t6;
+        if (!movword())
+                goto t3;
+t5:
+        if (nlflg)
+                pendt = 0;
+        adsp = adrem = 0;
+        if (ad) {
+                if (nwd == 1)
+                        adsp = nel; 
+                else 
+                        adsp = nel / (nwd - 1);
+                adsp = (adsp / HOR) * HOR;
+                adrem = nel - adsp*(nwd-1);
+        }
+        brflg = 1;
+        tbreak();
+        spread = 0;
+        if (!trap)
+                goto t3;
+        if (!nlflg)
+                goto rtn;
+t6:
+        pendt = 0;
+        ckul();
+rtn:
+        nflush = 0;
+}
+
+
+void nofill(void)
+{
+        int j;
+        Tchar i;
+
+        if (!pendnf) {
+                over = 0;
+                tbreak();
+                if (trap)
+                        goto rtn;
+                if (nlflg) {
+                        ch = nflush = 0;
+                        callsp();
+                        return;
+                }
+                adsp = adrem = 0;
+                nwd = 10000;
+        }
+        while ((j = (cbits(i = GETCH()))) != '\n') {
+                if (j == ohc)
+                        continue;
+                if (j == CONT) {
+                        pendnf++;
+                        nflush = 0;
+                        flushi();
+                        ckul();
+                        return;
+                }
+                j = width(i);
+                widthp = j;
+                numtabp[HP].val += j;
+                storeline(i, j);
+        }
+        if (ce) {
+                ce--;
+                if ((i = quant(nel / 2, HOR)) > 0)
+                        un += i;
+        }
+        if (!nc)
+                storeline((Tchar)FILLER, 0);
+        brflg = 2;
+        tbreak();
+        ckul();
+rtn:
+        pendnf = nflush = 0;
+}
+
+
+void callsp(void)
+{
+        int i;
+
+        if (flss)
+                i = flss; 
+        else 
+                i = lss;
+        flss = 0;
+        casesp1(i);
+}
+
+
+void ckul(void)
+{
+        if (ul && (--ul == 0)) {
+                cu = 0;
+                font = sfont;
+                mchbits();
+        }
+        if (it && --it == 0 && itmac)
+                control(itmac, 0);
+}
+
+
+void storeline(Tchar c, int w)
+{
+        int diff;
+
+        if (linep >= line + lnsize - 2) {
+                lnsize += LNSIZE;
+                diff = linep - line;
+                if (( line = (Tchar *)realloc((char *)line, lnsize * sizeof(Tchar))) != NULL) {
+                        if (linep && diff)
+                                linep = line + diff;
+                } else {
+                        if (over) {
+                                return;
+                        } else {
+                                flusho();
+                                ERROR "Line overflow." WARN;
+                                over++;
+                                *linep++ = LEFTHAND;
+                                w = width(LEFTHAND);
+                                nc++;
+                                c = '\n';
+                        }
+                }
+        }
+        *linep++ = c;
+        ne += w;
+        nel -= w;
+        nc++;
+}
+
+
+void newline(int a)
+{
+        int i, j, nlss;
+        int opn;
+
+        nlss = 0;
+        if (a)
+                goto nl1;
+        if (dip != d) {
+                j = lss;
+                pchar1((Tchar)FLSS);
+                if (flss)
+                        lss = flss;
+                i = lss + dip->blss;
+                dip->dnl += i;
+                pchar1((Tchar)i);
+                pchar1((Tchar)'\n');
+                lss = j;
+                dip->blss = flss = 0;
+                if (dip->alss) {
+                        pchar1((Tchar)FLSS);
+                        pchar1((Tchar)dip->alss);
+                        pchar1((Tchar)'\n');
+                        dip->dnl += dip->alss;
+                        dip->alss = 0;
+                }
+                if (dip->ditrap && !dip->ditf && dip->dnl >= dip->ditrap && dip->dimac)
+                        if (control(dip->dimac, 0)) {
+                                trap++; 
+                                dip->ditf++;
+                        }
+                return;
+        }
+        j = lss;
+        if (flss)
+                lss = flss;
+        nlss = dip->alss + dip->blss + lss;
+        numtabp[NL].val += nlss;
+        if (TROFF && ascii) {
+                dip->alss = dip->blss = 0;
+        }
+        pchar1((Tchar)'\n');
+        flss = 0;
+        lss = j;
+        if (numtabp[NL].val < pl)
+                goto nl2;
+nl1:
+        ejf = dip->hnl = numtabp[NL].val = 0;
+        ejl = frame;
+        if (donef) {
+                if ((!nc && !wch) || ndone)
+                        done1(0);
+                ndone++;
+                donef = 0;
+                if (frame == stk)
+                        nflush++;
+        }
+        opn = numtabp[PN].val;
+        numtabp[PN].val++;
+        if (npnflg) {
+                numtabp[PN].val = npn;
+                npn = npnflg = 0;
+        }
+nlpn:
+        if (numtabp[PN].val == pfrom) {
+                print++;
+                pfrom = -1;
+        } else if (opn == pto) {
+                print = 0;
+                opn = -1;
+                chkpn();
+                goto nlpn;
+        }
+        if (print)
+                ptpage(numtabp[PN].val);        /* supposedly in a clean state so can pause */
+        if (stop && print) {
+                dpn++;
+                if (dpn >= stop) {
+                        dpn = 0;
+                        ptpause();
+                }
+        }
+nl2:
+        trap = 0;
+        if (numtabp[NL].val == 0) {
+                if ((j = findn(0)) != NTRAP)
+                        trap = control(mlist[j], 0);
+        } else if ((i = findt(numtabp[NL].val - nlss)) <= nlss) {
+                if ((j = findn1(numtabp[NL].val - nlss + i)) == NTRAP) {
+                        flusho();
+                        ERROR "Trap botch." WARN;
+                        done2(-5);
+                }
+                trap = control(mlist[j], 0);
+        }
+}
+
+int
+findn1(int a)
+{
+        int i, j;
+
+        for (i = 0; i < NTRAP; i++) {
+                if (mlist[i]) {
+                        if ((j = nlist[i]) < 0)
+                                j += pl;
+                        if (j == a)
+                                break;
+                }
+        }
+        return(i);
+}
+
+
+void chkpn(void)
+{
+        pto = *(pnp++);
+        pfrom = pto>=0 ? pto : -pto;
+        if (pto == -INT_MAX) {
+                flusho();
+                done1(0);
+        }
+        if (pto < 0) {
+                pto = -pto;
+                print++;
+                pfrom = 0;
+        }
+}
+
+int
+findt(int a)
+{
+        int i, j, k;
+
+        k = INT_MAX;
+        if (dip != d) {
+                if (dip->dimac && (i = dip->ditrap - a) > 0)
+                        k = i;
+                return(k);
+        }
+        for (i = 0; i < NTRAP; i++) {
+                if (mlist[i]) {
+                        if ((j = nlist[i]) < 0)
+                                j += pl;
+                        if ((j -= a) <= 0)
+                                continue;
+                        if (j < k)
+                                k = j;
+                }
+        }
+        i = pl - a;
+        if (k > i)
+                k = i;
+        return(k);
+}
+
+int
+findt1(void)
+{
+        int i;
+
+        if (dip != d)
+                i = dip->dnl;
+        else 
+                i = numtabp[NL].val;
+        return(findt(i));
+}
+
+
+void eject(Stack *a)
+{
+        int savlss;
+
+        if (dip != d)
+                return;
+        ejf++;
+        if (a)
+                ejl = a;
+        else 
+                ejl = frame;
+        if (trap)
+                return;
+e1:
+        savlss = lss;
+        lss = findt(numtabp[NL].val);
+        newline(0);
+        lss = savlss;
+        if (numtabp[NL].val && !trap)
+                goto e1;
+}
+
+int
+movword(void)
+{
+        int w;
+        Tchar i, *wp;
+        int savwch, hys;
+
+        over = 0;
+        wp = wordp;
+        if (!nwd) {
+                while (cbits(*wp++) == ' ') {
+                        wch--;
+                        wne -= sps;
+                }
+                wp--;
+        }
+        if (wne > nel && !hyoff && hyf && (!nwd || nel > 3 * sps) &&
+           (!(hyf & 02) || (findt1() > lss)))
+                hyphen(wp);
+        savwch = wch;
+        hyp = hyptr;
+        nhyp = 0;
+        while (*hyp && *hyp <= wp)
+                hyp++;
+        while (wch) {
+                if (hyoff != 1 && *hyp == wp) {
+                        hyp++;
+                        if (!wdstart || (wp > wdstart + 1 && wp < wdend &&
+                           (!(hyf & 04) || wp < wdend - 1) &&                /* 04 => last 2 */
+                           (!(hyf & 010) || wp > wdstart + 2))) {        /* 010 => 1st 2 */
+                                nhyp++;
+                                storeline((Tchar)IMP, 0);
+                        }
+                }
+                i = *wp++;
+                w = width(i);
+                wne -= w;
+                wch--;
+                storeline(i, w);
+        }
+        if (nel >= 0) {
+                nwd++;
+                return(0);        /* line didn't fill up */
+        }
+        if (TROFF)
+                xbits((Tchar)HYPHEN, 1);
+        hys = width((Tchar)HYPHEN);
+m1:
+        if (!nhyp) {
+                if (!nwd)
+                        goto m3;
+                if (wch == savwch)
+                        goto m4;
+        }
+        if (*--linep != IMP)
+                goto m5;
+        if (!(--nhyp))
+                if (!nwd)
+                        goto m2;
+        if (nel < hys) {
+                nc--;
+                goto m1;
+        }
+m2:
+        if ((i = cbits(*(linep - 1))) != '-' && i != EMDASH) {
+                *linep = (*(linep - 1) & SFMASK) | HYPHEN;
+                w = width(*linep);
+                nel -= w;
+                ne += w;
+                linep++;
+        }
+m3:
+        nwd++;
+m4:
+        wordp = wp;
+        return(1);        /* line filled up */
+m5:
+        nc--;
+        w = width(*linep);
+        ne -= w;
+        nel += w;
+        wne += w;
+        wch++;
+        wp--;
+        goto m1;
+}
+
+
+void horiz(int i)
+{
+        vflag = 0;
+        if (i)
+                pchar(makem(i));
+}
+
+
+void setnel(void)
+{
+        if (!nc) {
+                linep = line;
+                if (un1 >= 0) {
+                        un = un1;
+                        un1 = -1;
+                }
+                nel = ll - un;
+                ne = adsp = adrem = 0;
+        }
+}
+
+int
+getword(int x)
+{
+        int j, k;
+        Tchar i, *wp;
+        int noword;
+        int obits;
+
+        j = 0;
+        noword = 0;
+        if (x)
+                if (pendw) {
+                        *pendw = 0;
+                        goto rtn;
+                }
+        if (wordp = pendw)
+                goto g1;
+        hyp = hyptr;
+        wordp = word;
+        over = wne = wch = 0;
+        hyoff = 0;
+        obits = chbits;
+        while (1) {        /* picks up 1st char of word */
+                j = cbits(i = GETCH());
+                if (j == '\n') {
+                        wne = wch = 0;
+                        noword = 1;
+                        goto rtn;
+                }
+                if (j == ohc) {
+                        hyoff = 1;        /* 1 => don't hyphenate */
+                        continue;
+                }
+                if (j == ' ') {
+                        numtabp[HP].val += sps;
+                        widthp = sps;
+                        storeword(i, sps);
+                        continue;
+                }
+                break;
+        }
+        storeword(' ' | obits, sps);
+        if (spflg) {
+                storeword(' ' | obits, sps);
+                spflg = 0;
+        }
+g0:
+        if (j == CONT) {
+                pendw = wordp;
+                nflush = 0;
+                flushi();
+                return(1);
+        }
+        if (hyoff != 1) {
+                if (j == ohc) {
+                        hyoff = 2;
+                        *hyp++ = wordp;
+                        if (hyp > hyptr + NHYP - 1)
+                                hyp = hyptr + NHYP - 1;
+                        goto g1;
+                }
+                if (((j == '-' || j == EMDASH)) && !(i & ZBIT))        /* zbit avoids \X */
+                        if (wordp > word + 1) {
+                                hyoff = 2;
+                                *hyp++ = wordp + 1;
+                                if (hyp > hyptr + NHYP - 1)
+                                        hyp = hyptr + NHYP - 1;
+                        }
+        }
+        j = width(i);
+        numtabp[HP].val += j;
+        storeword(i, j);
+g1:
+        j = cbits(i = GETCH());
+        if (j != ' ') {
+                static char *sentchar = ".?!";        /* sentence terminators */
+                if (j != '\n')
+                        goto g0;
+                wp = wordp-1;        /* handle extra space at end of sentence */
+                while (wp >= word) {
+                        j = cbits(*wp--);
+                        if (j=='"' || j=='\'' || j==')' || j==']' || j=='*' || j==DAGGER)
+                                continue;
+                        for (k = 0; sentchar[k]; k++)
+                                if (j == sentchar[k]) {
+                                        spflg++;
+                                        break;
+                                }
+                        break;
+                }
+        }
+        *wordp = 0;
+        numtabp[HP].val += sps;
+rtn:
+        for (wp = word; *wp; wp++) {
+                if (ismot(j))
+                        break;        /* drechsler */
+                j = cbits(*wp);
+                if (j == ' ')
+                        continue;
+                if (!(isascii(j) && isdigit(j)) && j != '-')
+                        break;
+        }
+        if (*wp == 0)        /* all numbers, so don't hyphenate */
+                hyoff = 1;
+        wdstart = 0;
+        wordp = word;
+        pendw = 0;
+        *hyp++ = 0;
+        setnel();
+        return(noword);
+}
+
+
+void storeword(Tchar c, int w)
+{
+        Tchar *savp;
+        int i;
+
+        if (wordp >= word + wdsize - 2) {
+                wdsize += WDSIZE;
+                savp = word;
+                if (( word = (Tchar *)realloc((char *)word, wdsize * sizeof(Tchar))) != NULL) {
+                        if (wordp)
+                                wordp = word + (wordp - savp);
+                        if (pendw)
+                                pendw = word + (pendw - savp);
+                        if (wdstart)
+                                wdstart = word + (wdstart - savp);
+                        if (wdend)
+                                wdend = word + (wdend - savp);
+                        for (i = 0; i < NHYP; i++)
+                                if (hyptr[i])
+                                        hyptr[i] = word + (hyptr[i] - savp);
+                } else {
+                        if (over) {
+                                return;
+                        } else {
+                                flusho();
+                                ERROR "Word overflow." WARN;
+                                over++;
+                                c = LEFTHAND;
+                                w = width(LEFTHAND);
+                        }
+                }
+        }
+        widthp = w;
+        wne += w;
+        *wordp++ = c;
+        wch++;
+}
+
+
+Tchar gettch(void)
+{
+        extern int c_isalnum;
+        Tchar i;
+        int j;
+
+        if (TROFF)
+                return getch();
+
+        i = getch();
+        j = cbits(i);
+        if (ismot(i) || fbits(i) != ulfont)
+                return(i);
+        if (cu) {
+                if (trtab[j] == ' ') {
+                        setcbits(i, '_');
+                        setfbits(i, FT);        /* default */
+                }
+                return(i);
+        }
+        /* should test here for characters that ought to be underlined */
+        /* in the old nroff, that was the 200 bit on the width! */
+        /* for now, just do letters, digits and certain special chars */
+        if (j <= 127) {
+                if (!isalnum(j))
+                        setfbits(i, FT);
+        } else {
+                if (j < c_isalnum)
+                        setfbits(i, FT);
+        }
+        return(i);
+}
diff --git a/troff/n8.c b/troff/n8.c
@@ -0,0 +1,545 @@
+#include 
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#define        HY_BIT        0200        /* stuff in here only works for 7-bit ascii */
+                        /* this value is used (as a literal) in suftab.c */
+                        /* to encode possible hyphenation points in suffixes. */
+                        /* it could be changed, by widening the tables */
+                        /* to be shorts instead of chars. */
+
+/*
+ * troff8.c
+ * 
+ * hyphenation
+ */
+
+int        hexsize = 0;                /* hyphenation exception list size */
+char        *hbufp = NULL;                /* base of list */
+char        *nexth = NULL;                /* first free slot in list */
+Tchar        *hyend;
+
+#define THRESH 160                 /* digram goodness threshold */
+int        thresh = THRESH;
+
+int        texhyphen(void);
+static        int        alpha(Tchar);
+
+void hyphen(Tchar *wp)
+{
+        int j;
+        Tchar *i;
+
+        i = wp;
+        while (punct((*i++)))
+                ;
+        if (!alpha(*--i))
+                return;
+        wdstart = i++;
+        while (alpha(*i++))
+                ;
+        hyend = wdend = --i - 1;
+        while (punct((*i++)))
+                ;
+        if (*--i)
+                return;
+        if (wdend - wdstart < 4)        /* 4 chars is too short to hyphenate */
+                return;
+        hyp = hyptr;
+        *hyp = 0;
+        hyoff = 2;
+
+        /* for now, try exceptions first, then tex (if hyphalg is non-zero),
+           then suffix and digram if tex didn't hyphenate it at all.
+        */
+
+        if (!exword() && !texhyphen() && !suffix())
+                digram();
+
+        /* this appears to sort hyphenation points into increasing order */
+        *hyp++ = 0;
+        if (*hyptr) 
+                for (j = 1; j; ) {
+                        j = 0;
+                        for (hyp = hyptr + 1; *hyp != 0; hyp++) {
+                                if (*(hyp - 1) > *hyp) {
+                                        j++;
+                                        i = *hyp;
+                                        *hyp = *(hyp - 1);
+                                        *(hyp - 1) = i;
+                                }
+                        }
+                }
+}
+
+static int alpha(Tchar i)        /* non-zero if really alphabetic */
+{
+        if (ismot(i))
+                return 0;
+        else if (cbits(i) >= ALPHABET)        /* this isn't very elegant, but there's */
+                return 0;                /* no good way to make sure i is in range for */
+        else                                /* the call of isalpha */
+                return isalpha(cbits(i));
+}
+
+int
+punct(Tchar i)
+{
+        if (!i || alpha(i))
+                return(0);
+        else
+                return(1);
+}
+
+
+void caseha(void)        /* set hyphenation algorithm */
+{
+        hyphalg = HYPHALG;
+        if (skip())
+                return;
+        noscale++;
+        hyphalg = atoi0();
+        noscale = 0;
+}
+
+
+void caseht(void)        /* set hyphenation threshold;  not in manual! */
+{
+        thresh = THRESH;
+        if (skip())
+                return;
+        noscale++;
+        thresh = atoi0();
+        noscale = 0;
+}
+
+
+char *growh(char *where)
+{
+        char *new;
+
+        hexsize += NHEX;
+        if ((new = grow(hbufp, hexsize, sizeof(char))) == NULL)
+                return NULL;
+        if (new == hbufp) {
+                return where;
+        } else {
+                int diff;
+                diff = where - hbufp;
+                hbufp = new;
+                return new + diff;
+        }
+}
+
+
+void casehw(void)
+{
+        int i, k;
+        char *j;
+        Tchar t;
+
+        if (nexth == NULL) {
+                if ((nexth = hbufp = grow(hbufp, NHEX, sizeof(char))) == NULL) {
+                        ERROR "No space for exception word list." WARN;
+                        return;
+                }
+                hexsize = NHEX;
+        }
+        k = 0;
+        while (!skip()) {
+                if ((j = nexth) >= hbufp + hexsize - 2)
+                        if ((j = nexth = growh(j)) == NULL)
+                                goto full;
+                for (;;) {
+                        if (ismot(t = getch()))
+                                continue;
+                        i = cbits(t);
+                        if (i == ' ' || i == '\n') {
+                                *j++ = 0;
+                                nexth = j;
+                                *j = 0;
+                                if (i == ' ')
+                                        break;
+                                else
+                                        return;
+                        }
+                        if (i == '-') {
+                                k = HY_BIT;
+                                continue;
+                        }
+                        *j++ = maplow(i) | k;
+                        k = 0;
+                        if (j >= hbufp + hexsize - 2)
+                                if ((j = growh(j)) == NULL)
+                                        goto full;
+                }
+        }
+        return;
+full:
+        ERROR "Cannot grow exception word list." WARN;
+        *nexth = 0;
+}
+
+
+int exword(void)
+{
+        Tchar *w;
+        char *e, *save;
+
+        e = hbufp;
+        while (1) {
+                save = e;
+                if (e == NULL || *e == 0)
+                        return(0);
+                w = wdstart;
+                while (*e && w <= hyend && (*e & 0177) == maplow(cbits(*w))) {
+                        e++; 
+                        w++;
+                }
+                if (!*e) {
+                        if (w-1 == hyend || (w == wdend && maplow(cbits(*w)) == 's')) {
+                                w = wdstart;
+                                for (e = save; *e; e++) {
+                                        if (*e & HY_BIT)
+                                                *hyp++ = w;
+                                        if (hyp > hyptr + NHYP - 1)
+                                                hyp = hyptr + NHYP - 1;
+                                        w++;
+                                }
+                                return(1);
+                        } else {
+                                e++; 
+                                continue;
+                        }
+                } else 
+                        while (*e++)
+                                ;
+        }
+}
+
+int
+suffix(void)
+{
+        Tchar *w;
+        char *s, *s0;
+        Tchar i;
+        extern char *suftab[];
+
+again:
+        i = cbits(*hyend);
+        if (!alpha(i))
+                return(0);
+        if (i < 'a')
+                i -= 'A' - 'a';
+        if ((s0 = suftab[i-'a']) == 0)
+                return(0);
+        for (;;) {
+                if ((i = *s0 & 017) == 0)
+                        return(0);
+                s = s0 + i - 1;
+                w = hyend - 1;
+                while (s > s0 && w >= wdstart && (*s & 0177) == maplow(cbits(*w))) {
+                        s--;
+                        w--;
+                }
+                if (s == s0)
+                        break;
+                s0 += i;
+        }
+        s = s0 + i - 1;
+        w = hyend;
+        if (*s0 & HY_BIT) 
+                goto mark;
+        while (s > s0) {
+                w--;
+                if (*s-- & HY_BIT) {
+mark:
+                        hyend = w - 1;
+                        if (*s0 & 0100)        /* 0100 used in suftab to encode something too */
+                                continue;
+                        if (!chkvow(w))
+                                return(0);
+                        *hyp++ = w;
+                }
+        }
+        if (*s0 & 040)
+                return(0);
+        if (exword())
+                return(1);
+        goto again;
+}
+
+int
+maplow(int i)
+{
+        if (isupper(i)) 
+                i = tolower(i);
+        return(i);
+}
+
+int
+vowel(int i)
+{
+        switch (i) {
+        case 'a': case 'A':
+        case 'e': case 'E':
+        case 'i': case 'I':
+        case 'o': case 'O':
+        case 'u': case 'U':
+        case 'y': case 'Y':
+                return(1);
+        default:
+                return(0);
+        }
+}
+
+
+Tchar *chkvow(Tchar *w)
+{
+        while (--w >= wdstart)
+                if (vowel(cbits(*w)))
+                        return(w);
+        return(0);
+}
+
+
+void digram(void)
+{
+        Tchar *w;
+        int val;
+        Tchar *nhyend, *maxw;
+        int maxval;
+        extern char bxh[26][13], bxxh[26][13], xxh[26][13], xhx[26][13], hxx[26][13];
+        maxw = 0;
+again:
+        if (!(w = chkvow(hyend + 1)))
+                return;
+        hyend = w;
+        if (!(w = chkvow(hyend)))
+                return;
+        nhyend = w;
+        maxval = 0;
+        w--;
+        while (++w < hyend && w < wdend - 1) {
+                val = 1;
+                if (w == wdstart)
+                        val *= dilook('a', cbits(*w), bxh);
+                else if (w == wdstart + 1)
+                        val *= dilook(cbits(*(w-1)), cbits(*w), bxxh);
+                else 
+                        val *= dilook(cbits(*(w-1)), cbits(*w), xxh);
+                val *= dilook(cbits(*w), cbits(*(w+1)), xhx);
+                val *= dilook(cbits(*(w+1)), cbits(*(w+2)), hxx);
+                if (val > maxval) {
+                        maxval = val;
+                        maxw = w + 1;
+                }
+        }
+        hyend = nhyend;
+        if (maxval > thresh)
+                *hyp++ = maxw;
+        goto again;
+}
+
+int
+dilook(int a, int b, char t[26][13])
+{
+        int i, j;
+
+        i = t[maplow(a)-'a'][(j = maplow(b)-'a')/2];
+        if (!(j & 01))
+                i >>= 4;
+        return(i & 017);
+}
+
+
+/* here beginneth the tex hyphenation code, as interpreted freely */
+/* the main difference is that there is no attempt to squeeze space */
+/* as tightly at tex does. */
+
+static int        texit(Tchar *, Tchar *);
+static int        readpats(void);
+static void        install(char *);
+static void        fixup(void);
+static int        trieindex(int, int);
+
+static char        pats[50000];        /* size ought to be computed dynamically */
+static char        *nextpat = pats;
+static char        *trie[27*27];        /* english-specific sizes */
+
+int texhyphen(void)
+{
+        static int loaded = 0;                /* -1: couldn't find tex file */
+
+        if (hyphalg == 0 || loaded == -1)        /* non-zero => tex for now */
+                return 0;
+        if (loaded == 0) {
+                if (readpats())
+                        loaded = 1;
+                else
+                        loaded = -1;
+        }
+        return texit(wdstart, wdend);
+}
+
+static int texit(Tchar *start, Tchar *end)        /* hyphenate as in tex, return # found */
+{
+        int nw, i, k, equal, cnt[500];
+        char w[500+1], *np, *pp, *wp, *xpp, *xwp;
+
+        w[0] = '.';
+        for (nw = 1; start <= end && nw < 500-1; nw++, start++)
+                w[nw] = maplow(tolower(cbits(*start)));
+        start -= (nw - 1);
+        w[nw++] = '.';
+        w[nw] = 0;
+/*
+ * printf("try %s\n", w);
+*/
+        for (i = 0; i <= nw; i++)
+                cnt[i] = '0';
+
+        for (wp = w; wp+1 < w+nw; wp++) {
+                for (pp = trie[trieindex(*wp, *(wp+1))]; pp < nextpat; ) {
+                        if (pp == 0                /* no trie entry */
+                         || *pp != *wp                /* no match on 1st letter */
+                         || *(pp+1) != *(wp+1))        /* no match on 2nd letter */
+                                break;                /*   so move to next letter of word */
+                        equal = 1;
+                        for (xpp = pp+2, xwp = wp+2; *xpp; )
+                                if (*xpp++ != *xwp++) {
+                                        equal = 0;
+                                        break;
+                                }
+                        if (equal) {
+                                np = xpp+1;        /* numpat */
+                                for (k = wp-w; *np; k++, np++)
+                                        if (*np > cnt[k])
+                                                cnt[k] = *np;
+/*
+ * printf("match: %s  %s\n", pp, xpp+1);
+*/
+                        }
+                        pp += *(pp-1);        /* skip over pattern and numbers to next */
+                }
+        }
+/*
+ * for (i = 0; i < nw; i++) printf("%c", w[i]);
+ * printf("  ");
+ * for (i = 0; i <= nw; i++) printf("%c", cnt[i]);
+ * printf("\n");
+*/
+/*
+ *         for (i = 1; i < nw - 1; i++) {
+ *                 if (i > 2 && i < nw - 3 && cnt[i] % 2)
+ *                         printf("-");
+ *                 if (cbits(start[i-1]) != '.')
+ *                         printf("%c", cbits(start[i-1]));
+ *         }
+ *         printf("\n");
+*/
+        for (i = 1; i < nw -1; i++)
+                if (i > 2 && i < nw - 3 && cnt[i] % 2)
+                        *hyp++ = start + i - 1;
+        return hyp - hyptr;        /* non-zero if a hyphen was found */
+}
+
+/*
+        This code assumes that hyphen.tex looks like
+                % some comments
+                \patterns{ % more comments
+                pat5ter4ns, 1 per line, SORTED, nothing else
+                }
+                more goo
+                \hyphenation{ % more comments
+                ex-cep-tions, one per line; i ignore this part for now
+                }
+
+        this code is NOT robust against variations.  unfortunately,
+        it looks like every local language version of this file has
+        a different format.  i have also made no provision for weird
+        characters.  sigh.
+*/
+
+static int readpats(void)
+{
+        FILE *fp;
+        char buf[200], buf1[200];
+
+        if ((fp = fopen(unsharp(TEXHYPHENS), "r")) == NULL
+         && (fp = fopen(unsharp(DWBalthyphens), "r")) == NULL) {
+                ERROR "warning: can't find hyphen.tex" WARN;
+                return 0;
+        }
+
+        while (fgets(buf, sizeof buf, fp) != NULL) {
+                sscanf(buf, "%s", buf1);
+                if (strcmp(buf1, "\\patterns{") == 0)
+                        break;
+        }
+        while (fgets(buf, sizeof buf, fp) != NULL) {
+                if (buf[0] == '}')
+                        break;
+                install(buf);
+        }
+        fclose(fp);
+        fixup();
+        return 1;
+}
+
+static void install(char *s)        /* map ab4c5de to: 12 abcde \0 00405 \0 */
+{
+        int npat, lastpat;
+        char num[500], *onextpat = nextpat;
+
+        num[0] = '0';
+        *nextpat++ = ' ';        /* fill in with count later */
+        for (npat = lastpat = 0; *s != '\n' && *s != '\0'; s++) {
+                if (isdigit((uchar)*s)) {
+                        num[npat] = *s;
+                        lastpat = npat;
+                } else {
+                        *nextpat++ = *s;
+                        npat++;
+                        num[npat] = '0';
+                }
+        }
+        *nextpat++ = 0;
+        if (nextpat > pats + sizeof(pats)-20) {
+                ERROR "tex hyphenation table overflow, tail end ignored" WARN;
+                nextpat = onextpat;
+        }
+        num[lastpat+1] = 0;
+        strcat(nextpat, num);
+        nextpat += strlen(nextpat) + 1;
+}
+
+static void fixup(void)        /* build indexes of where . a b c ... start */
+{
+        char *p, *lastc;
+        int n;
+
+        for (lastc = pats, p = pats+1; p < nextpat; p++)
+                if (*p == ' ') {
+                        *lastc = p - lastc;
+                        lastc = p;
+                }
+        *lastc = p - lastc;
+        for (p = pats+1; p < nextpat; ) {
+                n = trieindex(p[0], p[1]);
+                if (trie[n] == 0)
+                        trie[n] = p;
+                p += p[-1];
+        }
+        /* printf("pats = %d\n", nextpat - pats); */
+}
+
+static int trieindex(int d1, int d2)
+{
+        int z;
+
+        z = 27 * (d1 == '.' ? 0 : d1 - 'a' + 1) + (d2 == '.' ? 0 : d2 - 'a' + 1);
+        assert(z >= 0 && z < 27*27);
+        return z;
+}
diff --git a/troff/n9.c b/troff/n9.c
@@ -0,0 +1,489 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+
+/*
+ * troff9.c
+ * 
+ * misc functions
+ */
+
+Tchar setz(void)
+{
+        Tchar i;
+
+        if (!ismot(i = getch()))
+                i |= ZBIT;
+        return(i);
+}
+
+void setline(void)
+{
+        Tchar *i;
+        Tchar c;
+        int length;
+        int j, w, cnt, delim, rem, temp;
+        Tchar linebuf[NC];
+
+        if (ismot(c = getch()))
+                return;
+        delim = cbits(c);
+        vflag = 0;
+        dfact = EM;
+        length = quant(atoi0(), HOR);
+        dfact = 1;
+        if (!length) {
+                eat(delim);
+                return;
+        }
+s0:
+        if ((j = cbits(c = getch())) == delim || j == '\n') {
+                ch = c;
+                c = RULE | chbits;
+        } else if (cbits(c) == FILLER)
+                goto s0;
+        w = width(c);
+        if (w <= 0) {
+                ERROR "zero-width underline character ignored" WARN;
+                c = RULE | chbits;
+                w = width(c);
+        }
+        i = linebuf;
+        if (length < 0) {
+                *i++ = makem(length);
+                length = -length;
+        }
+        if (!(cnt = length / w)) {
+                *i++ = makem(-(temp = ((w - length) / 2)));
+                *i++ = c;
+                *i++ = makem(-(w - length - temp));
+                goto s1;
+        }
+        if (rem = length % w) {
+                if (cbits(c) == RULE || cbits(c) == UNDERLINE || cbits(c) == ROOTEN)
+                        *i++ = c | ZBIT;
+                *i++ = makem(rem);
+        }
+        if (cnt) {
+                *i++ = RPT;
+                *i++ = cnt;
+                *i++ = c;
+        }
+s1:
+        *i = 0;
+        eat(delim);
+        pushback(linebuf);
+}
+
+int
+eat(int c)
+{
+        int i;
+
+        while ((i = cbits(getch())) != c && i != '\n')
+                ;
+        return(i);
+}
+
+
+void setov(void)
+{
+        int j, k;
+        Tchar i, o[NOV+1];
+        int delim, w[NOV+1];
+
+        if (ismot(i = getch()))
+                return;
+        delim = cbits(i);
+        for (k = 0; k < NOV && (j = cbits(i = getch())) != delim && j != '\n'; k++) {
+                o[k] = i;
+                w[k] = width(i);
+        }
+        o[k] = w[k] = 0;
+        if (o[0])
+                for (j = 1; j; ) {
+                        j = 0;
+                        for (k = 1; o[k] ; k++) {
+                                if (w[k-1] < w[k]) {
+                                        j++;
+                                        i = w[k];
+                                        w[k] = w[k-1];
+                                        w[k-1] = i;
+                                        i = o[k];
+                                        o[k] = o[k-1];
+                                        o[k-1] = i;
+                                }
+                        }
+                }
+        else 
+                return;
+        *pbp++ = makem(w[0] / 2);
+        for (k = 0; o[k]; k++)
+                ;
+        while (k>0) {
+                k--;
+                *pbp++ = makem(-((w[k] + w[k+1]) / 2));
+                *pbp++ = o[k];
+        }
+}
+
+
+void setbra(void)
+{
+        int k;
+        Tchar i, *j, dwn;
+        int cnt, delim;
+        Tchar brabuf[NC];
+
+        if (ismot(i = getch()))
+                return;
+        delim = cbits(i);
+        j = brabuf + 1;
+        cnt = 0;
+        if (NROFF)
+                dwn = (2 * t.Halfline) | MOT | VMOT;
+        else
+                dwn = EM | MOT | VMOT;
+        while ((k = cbits(i = getch())) != delim && k != '\n' && j <= brabuf + NC - 4) {
+                *j++ = i | ZBIT;
+                *j++ = dwn;
+                cnt++;
+        }
+        if (--cnt < 0)
+                return;
+        else if (!cnt) {
+                ch = *(j - 2);
+                return;
+        }
+        *j = 0;
+        if (NROFF)
+                *--j = *brabuf = (cnt * t.Halfline) | MOT | NMOT | VMOT;
+        else
+                *--j = *brabuf = (cnt * EM) / 2 | MOT | NMOT | VMOT;
+        *--j &= ~ZBIT;
+        pushback(brabuf);
+}
+
+
+void setvline(void)
+{
+        int i;
+        Tchar c, rem, ver, neg;
+        int cnt, delim, v;
+        Tchar vlbuf[NC];
+        Tchar *vlp;
+
+        if (ismot(c = getch()))
+                return;
+        delim = cbits(c);
+        dfact = lss;
+        vflag++;
+        i = quant(atoi0(), VERT);
+        dfact = 1;
+        if (!i) {
+                eat(delim);
+                vflag = 0;
+                return;
+        }
+        if ((cbits(c = getch())) == delim) {
+                c = BOXRULE | chbits;        /*default box rule*/
+        } else 
+                getch();
+        c |= ZBIT;
+        neg = 0;
+        if (i < 0) {
+                i = -i;
+                neg = NMOT;
+        }
+        if (NROFF)
+                v = 2 * t.Halfline;
+        else {
+                v = EM;
+                if (v < VERT)                /* ATT EVK hack: Erik van Konijnenburg, */
+                        v = VERT;        /* hvlpb!evkonij, ATT NSI Hilversum, Holland */
+        }
+
+        cnt = i / v;
+        rem = makem(i % v) | neg;
+        ver = makem(v) | neg;
+        vlp = vlbuf;
+        if (!neg)
+                *vlp++ = ver;
+        if (absmot(rem) != 0) {
+                *vlp++ = c;
+                *vlp++ = rem;
+        }
+        while (vlp < vlbuf + NC - 3 && cnt--) {
+                *vlp++ = c;
+                *vlp++ = ver;
+        }
+        *(vlp - 2) &= ~ZBIT;
+        if (!neg)
+                vlp--;
+        *vlp = 0;
+        pushback(vlbuf);
+        vflag = 0;
+}
+
+#define        NPAIR        (NC/2-6)        /* max pairs in spline, etc. */
+
+void setdraw(void)        /* generate internal cookies for a drawing function */
+{
+        int i, j, k, dx[NPAIR], dy[NPAIR], delim, type;
+        Tchar c, drawbuf[NC];
+        int drawch = '.';        /* character to draw with */
+
+        /* input is \D'f dx dy dx dy ... c' (or at least it had better be) */
+        /* this does drawing function f with character c and the */
+        /* specified dx,dy pairs interpreted as appropriate */
+        /* pairs are deltas from last point, except for radii */
+
+        /* l dx dy:        line from here by dx,dy */
+        /* c x:                circle of diameter x, left side here */
+        /* e x y:        ellipse of diameters x,y, left side here */
+        /* a dx1 dy1 dx2 dy2:
+                        ccw arc: ctr at dx1,dy1, then end at dx2,dy2 from there */
+        /* ~ dx1 dy1 dx2 dy2...:
+                        spline to dx1,dy1 to dx2,dy2 ... */
+        /* b x c:
+                        built-up character of type c, ht x */
+        /* f dx dy ...:        f is any other char:  like spline */
+
+        if (ismot(c = getch()))
+                return;
+        delim = cbits(c);
+        numerr.escarg = type = cbits(getch());
+        if (type == '~')        /* head off the .tr ~ problem */
+                type = 's';
+        for (i = 0; i < NPAIR ; i++) {
+                skip();
+                vflag = 0;
+                dfact = EM;
+                dx[i] = quant(atoi0(), HOR);
+                if (dx[i] > MAXMOT)
+                        dx[i] = MAXMOT;
+                else if (dx[i] < -MAXMOT)
+                        dx[i] = -MAXMOT;
+                skip();
+                if (type == 'c') {
+                        dy[i] = 0;
+                        goto eat;
+                }
+                vflag = 1;
+                dfact = lss;
+                dy[i] = quant(atoi0(), VERT);
+                if (dy[i] > MAXMOT)
+                        dy[i] = MAXMOT;
+                else if (dy[i] < -MAXMOT)
+                        dy[i] = -MAXMOT;
+eat:
+                if (cbits(c = getch()) != ' ') {        /* must be the end */
+                        if (cbits(c) != delim) {
+                                drawch = cbits(c);
+                                getch();
+                        }
+                        i++;
+                        break;
+                }
+        }
+        dfact = 1;
+        vflag = 0;
+        if (TROFF) {
+                drawbuf[0] = DRAWFCN | chbits | ZBIT;
+                drawbuf[1] = type | chbits | ZBIT;
+                drawbuf[2] = drawch | chbits | ZBIT;
+                for (k = 0, j = 3; k < i; k++) {
+                        drawbuf[j++] = MOT | ((dx[k] >= 0) ? dx[k] : (NMOT | -dx[k]));
+                        drawbuf[j++] = MOT | VMOT | ((dy[k] >= 0) ? dy[k] : (NMOT | -dy[k]));
+                }
+                if (type == DRAWELLIPSE) {
+                        drawbuf[5] = drawbuf[4] | NMOT;        /* so the net vertical is zero */
+                        j = 6;
+                } else if (type == DRAWBUILD) {
+                        drawbuf[4] = drawbuf[3] | NMOT;        /* net horizontal motion is zero */
+                        drawbuf[2] &= ~ZBIT;                /* width taken from drawing char */
+                        j = 5;
+                }
+                drawbuf[j++] = DRAWFCN | chbits | ZBIT;        /* marks end for ptout */
+                drawbuf[j] = 0;
+                pushback(drawbuf);
+        }
+}
+
+
+void casefc(void)
+{
+        int i;
+        Tchar j;
+
+        gchtab[fc] &= ~FCBIT;
+        fc = IMP;
+        padc = ' ';
+        if (skip() || ismot(j = getch()) || (i = cbits(j)) == '\n')
+                return;
+        fc = i;
+        gchtab[fc] |= FCBIT;
+        if (skip() || ismot(ch) || (ch = cbits(ch)) == fc)
+                return;
+        padc = ch;
+}
+
+
+Tchar setfield(int x)
+{
+        Tchar ii, jj, *fp;
+        int i, j;
+        int length, ws, npad, temp, type;
+        Tchar **pp, *padptr[NPP];
+        Tchar fbuf[FBUFSZ];
+        int savfc, savtc, savlc;
+        Tchar rchar;
+        int savepos;
+        static Tchar wbuf[] = { WORDSP, 0};
+
+        rchar = 0;
+        if (x == tabch) 
+                rchar = tabc | chbits;
+        else if (x ==  ldrch) 
+                rchar = dotc | chbits;
+        temp = npad = ws = 0;
+        savfc = fc;
+        savtc = tabch;
+        savlc = ldrch;
+        tabch = ldrch = fc = IMP;
+        savepos = numtabp[HP].val;
+        gchtab[tabch] &= ~TABBIT;
+        gchtab[ldrch] &= ~LDRBIT;
+        gchtab[fc] &= ~FCBIT;
+        gchtab[IMP] |= TABBIT|LDRBIT|FCBIT;
+        for (j = 0; ; j++) {
+                if ((tabtab[j] & TABMASK) == 0) {
+                        if (x == savfc)
+                                ERROR "zero field width." WARN;
+                        jj = 0;
+                        goto rtn;
+                }
+                if ((length = ((tabtab[j] & TABMASK) - numtabp[HP].val)) > 0 )
+                        break;
+        }
+        type = tabtab[j] & ~TABMASK;
+        fp = fbuf;
+        pp = padptr;
+        if (x == savfc) {
+                while (1) {
+                        j = cbits(ii = getch());
+                        jj = width(ii);
+                        widthp = jj;
+                        numtabp[HP].val += jj;
+                        if (j == padc) {
+                                npad++;
+                                *pp++ = fp;
+                                if (pp > padptr + NPP - 1)
+                                        break;
+                                goto s1;
+                        } else if (j == savfc) 
+                                break;
+                        else if (j == '\n') {
+                                temp = j;
+                                if (nlflg && ip == 0) {
+                                        numtabp[CD].val--;
+                                        nlflg = 0;
+                                }
+                                break;
+                        }
+                        ws += jj;
+s1:
+                        *fp++ = ii;
+                        if (fp > fbuf + FBUFSZ - 3)
+                                break;
+                }
+                if (ws)
+                        *fp++ = WORDSP;
+                if (!npad) {
+                        npad++;
+                        *pp++ = fp;
+                        *fp++ = 0;
+                }
+                *fp++ = temp;
+                *fp = 0;
+                temp = i = (j = length - ws) / npad;
+                i = (i / HOR) * HOR;
+                if ((j -= i * npad) < 0)
+                        j = -j;
+                ii = makem(i);
+                if (temp < 0)
+                        ii |= NMOT;
+                for (; npad > 0; npad--) {
+                        *(*--pp) = ii;
+                        if (j) {
+                                j -= HOR;
+                                (*(*pp)) += HOR;
+                        }
+                }
+                pushback(fbuf);
+                jj = 0;
+        } else if (type == 0) {
+                /*plain tab or leader*/
+                if ((j = width(rchar)) > 0) {
+                        int nchar = length / j;
+                        while (nchar-->0 && pbp < &pbbuf[NC-3]) {
+                                numtabp[HP].val += j;
+                                widthp = j;
+                                *pbp++ = rchar;
+                        }
+                        length %= j;
+                }
+                if (length)
+                        jj = length | MOT;
+                else 
+                        jj = getch0();
+                if (savepos > 0)
+                        pushback(wbuf);
+        } else {
+                /*center tab*/
+                /*right tab*/
+                while ((j = cbits(ii = getch())) != savtc && j != '\n' && j != savlc) {
+                        jj = width(ii);
+                        ws += jj;
+                        numtabp[HP].val += jj;
+                        widthp = jj;
+                        *fp++ = ii;
+                        if (fp > fbuf + FBUFSZ - 3) 
+                                break;
+                }
+                *fp++ = ii;
+                *fp = 0;
+                if (type == RTAB)
+                        length -= ws;
+                else 
+                        length -= ws / 2; /*CTAB*/
+                pushback(fbuf);
+                if ((j = width(rchar)) != 0 && length > 0) {
+                        int nchar = length / j;
+                        while (nchar-- > 0 && pbp < &pbbuf[NC-3])
+                                *pbp++ = rchar;
+                        length %= j;
+                }
+                if (savepos > 0)
+                        pushback(wbuf);
+                length = (length / HOR) * HOR;
+                jj = makem(length);
+                if (nlflg) {
+                        if (ip == 0)
+                                numtabp[CD].val--;
+                        nlflg = 0;
+                }
+        }
+rtn:
+        gchtab[fc] &= ~FCBIT;
+        gchtab[tabch] &= ~TABBIT;
+        gchtab[ldrch] &= ~LDRBIT;
+        fc = savfc;
+        tabch = savtc;
+        ldrch = savlc;
+        gchtab[fc] |= FCBIT;
+        gchtab[tabch] = TABBIT;
+        gchtab[ldrch] |= LDRBIT;
+        numtabp[HP].val = savepos;
+        return(jj);
+}
diff --git a/troff/ni.c b/troff/ni.c
@@ -0,0 +1,390 @@
+#include 
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+char        termtab[NS];        /* term type added in ptinit() */
+char        fontdir[NS];        /* added in casefp; not used by nroff */
+char        devname[20];        /* default output device */
+
+Numtab numtab[NN] = {
+        { PAIR('%', 0) },
+        { PAIR('n', 'l') },
+        { PAIR('y', 'r') },
+        { PAIR('h', 'p') },
+        { PAIR('c', 't') },
+        { PAIR('d', 'n') },
+        { PAIR('m', 'o') },
+        { PAIR('d', 'y') },
+        { PAIR('d', 'w') },
+        { PAIR('l', 'n') },
+        { PAIR('d', 'l') },
+        { PAIR('s', 't') },
+        { PAIR('s', 'b') },
+        { PAIR('c', '.') },
+        { PAIR('$', '$') }
+};
+
+
+int        alphabet        = 256;        /* latin-1 */
+int        pto        = 10000;
+int        pfrom        = 1;
+int        print        = 1;
+char        nextf[NS]        = TMACDIR;
+char        mfiles[NMF][NS];
+int        nmfi        = 0;
+int        oldbits        = -1;
+int        init        = 1;
+int        fc        = IMP;        /* field character */
+int        eschar        = '\\';
+int        pl;
+int        po;
+FILE        *ptid;
+
+int        dfact        = 1;
+int        dfactd        = 1;
+int        res        = 1;
+int        smnt        = 0;        /* beginning of special fonts */
+int        ascii        = 0;        /* ascii normally off for troff, on for nroff;  -a turns on */
+int        lg;
+int        pnlist[NPN] = { -1 };
+
+
+int        *pnp        = pnlist;
+int        npn        = 1;
+int        npnflg        =  1;
+int        dpn        =  -1;
+int        totout        =  1;
+int        ulfont        =  ULFONT;
+int        tabch        =  TAB;
+int        ldrch        =  LEADER;
+
+
+Contab contab[NM] = {
+        C(PAIR('d', 's'), caseds),
+        C(PAIR('a', 's'), caseas),
+        C(PAIR('s', 'p'), casesp),
+        C(PAIR('f', 't'), caseft),
+        C(PAIR('p', 's'), caseps),
+        C(PAIR('v', 's'), casevs),
+        C(PAIR('n', 'r'), casenr),
+        C(PAIR('i', 'f'), caseif),
+        C(PAIR('i', 'e'), caseie),
+        C(PAIR('e', 'l'), caseel),
+        C(PAIR('p', 'o'), casepo),
+        C(PAIR('t', 'l'), casetl),
+        C(PAIR('t', 'm'), casetm),
+        C(PAIR('f', 'm'), casefm),
+        C(PAIR('b', 'p'), casebp),
+        C(PAIR('c', 'h'), casech),
+        C(PAIR('p', 'n'), casepn),
+        C(PAIR('b', 'r'), tbreak),
+        C(PAIR('t', 'i'), caseti),
+        C(PAIR('n', 'e'), casene),
+        C(PAIR('n', 'f'), casenf),
+        C(PAIR('c', 'e'), casece),
+        C(PAIR('f', 'i'), casefi),
+        C(PAIR('i', 'n'), casein),
+        C(PAIR('l', 'l'), casell),
+        C(PAIR('n', 's'), casens),
+        C(PAIR('m', 'k'), casemk),
+        C(PAIR('r', 't'), casert),
+        C(PAIR('a', 'm'), caseam),
+        C(PAIR('d', 'e'), casede),
+        C(PAIR('d', 'i'), casedi),
+        C(PAIR('d', 'a'), caseda),
+        C(PAIR('w', 'h'), casewh),
+        C(PAIR('d', 't'), casedt),
+        C(PAIR('i', 't'), caseit),
+        C(PAIR('r', 'm'), caserm),
+        C(PAIR('r', 'r'), caserr),
+        C(PAIR('r', 'n'), casern),
+        C(PAIR('a', 'd'), casead),
+        C(PAIR('r', 's'), casers),
+        C(PAIR('n', 'a'), casena),
+        C(PAIR('p', 'l'), casepl),
+        C(PAIR('t', 'a'), caseta),
+        C(PAIR('t', 'r'), casetr),
+        C(PAIR('u', 'l'), caseul),
+        C(PAIR('c', 'u'), casecu),
+        C(PAIR('l', 't'), caselt),
+        C(PAIR('n', 'x'), casenx),
+        C(PAIR('s', 'o'), caseso),
+        C(PAIR('i', 'g'), caseig),
+        C(PAIR('t', 'c'), casetc),
+        C(PAIR('f', 'c'), casefc),
+        C(PAIR('e', 'c'), caseec),
+        C(PAIR('e', 'o'), caseeo),
+        C(PAIR('l', 'c'), caselc),
+        C(PAIR('e', 'v'), caseev),
+        C(PAIR('r', 'd'), caserd),
+        C(PAIR('a', 'b'), caseab),
+        C(PAIR('f', 'l'), casefl),
+        C(PAIR('e', 'x'), caseex),
+        C(PAIR('s', 's'), casess),
+        C(PAIR('f', 'p'), casefp),
+        C(PAIR('c', 's'), casecs),
+        C(PAIR('b', 'd'), casebd),
+        C(PAIR('l', 'g'), caselg),
+        C(PAIR('h', 'c'), casehc),
+        C(PAIR('h', 'y'), casehy),
+        C(PAIR('n', 'h'), casenh),
+        C(PAIR('n', 'm'), casenm),
+        C(PAIR('n', 'n'), casenn),
+        C(PAIR('s', 'v'), casesv),
+        C(PAIR('o', 's'), caseos),
+        C(PAIR('l', 's'), casels),
+        C(PAIR('c', 'c'), casecc),
+        C(PAIR('c', '2'), casec2),
+        C(PAIR('e', 'm'), caseem),
+        C(PAIR('a', 'f'), caseaf),
+        C(PAIR('h', 'a'), caseha),
+        C(PAIR('h', 'w'), casehw),
+        C(PAIR('m', 'c'), casemc),
+        C(PAIR('p', 'm'), casepm),
+        C(PAIR('p', 'i'), casepi),
+        C(PAIR('u', 'f'), caseuf),
+        C(PAIR('p', 'c'), casepc),
+        C(PAIR('h', 't'), caseht),
+        C(PAIR('c', 'f'), casecf),
+        C(PAIR('s', 'y'), casesy),
+        C(PAIR('l', 'f'), caself),
+        C(PAIR('p', 't'), casept),
+        C(PAIR('g', 'd'), casegd)
+};
+
+
+Tbuf _oline;
+
+/*
+ * troff environment block
+ */
+
+Env env[NEV] = { {        /* this sets up env[0] */
+/* int        ics         */        0,        /* insertion character space, set by .mc */
+/* int        sps         */        0,
+/* int        spacesz         */        0,
+/* int        lss         */        0,
+/* int        lss1         */        0,
+/* int        ll         */        0,
+/* int        ll1         */        0,
+/* int        lt         */        0,
+/* int        lt1         */        0,
+/* Tchar ic         */        0,        /* insertion character (= margin character) */
+/* int        icf         */        0,        /* insertion character flag */
+/* Tchar chbits         */        0,        /* size+font bits for current character */
+/* Tchar spbits         */        0,
+/* Tchar nmbits         */        0,        /* size+font bits for number from .nm */
+/* int        apts         */        PS,        /* actual point size -- as requested by user */
+/* int        apts1         */        PS,        /* need not match an existent size */
+/* int        pts         */        PS,        /* hence, this is the size that really exists */
+/* int        pts1         */        PS,
+/* int        font         */        FT,
+/* int        font1         */        FT,
+/* int        ls         */        1,
+/* int        ls1         */        1,
+/* int        ad         */        1,
+/* int        nms         */        1,        /* .nm multiplier */
+/* int        ndf         */        1,        /* .nm separator */
+/* int        nmwid         */        3,        /* max width of .nm numbers */
+/* int        fi         */        1,
+/* int        cc         */        '.',
+/* int        c2         */        '\'',
+/* int        ohc         */        OHC,
+/* int        tdelim         */        IMP,
+/* int        hyf         */        1,
+/* int        hyoff         */        0,
+/* int        hyphalg  */        HYPHALG,
+/* int        un1         */        -1,
+/* int        tabc         */        0,
+/* int        dotc         */        '.',
+/* int        adsp         */        0,        /* add this much space to each padding point */
+/* int        adrem         */        0,        /* excess space to add until it runs out */
+/* int        lastl         */        0,        /* last text on current output line */
+/* int        nel         */        0,        /* how much space left on current output line */
+/* int        admod         */        0,        /* adjust mode */
+/* Tchar *wordp         */        0,
+/* int        spflg         */        0,        /* probably to indicate space after punctuation needed */
+/* Tchar *linep         */        0,
+/* Tchar *wdend         */        0,
+/* Tchar *wdstart */        0,
+/* int        wne         */        0,
+/* int        ne         */        0,        /* how much space taken on current output line */
+/* int        nc         */        0,        /* #characters (incl blank) on output line */
+/* int        nb         */        0,
+/* int        lnmod         */        0,        /* line number mode, set by .nm */
+/* int        nwd         */        0,        /* number of words on current output line */
+/* int        nn         */        0,        /* from .nn command */
+/* int        ni         */        0,        /* indent of .nm numbers, probably */
+/* int        ul         */        0,
+/* int        cu         */        0,
+/* int        ce         */        0,
+/* int        in         */        0,        /* indent and previous value */
+/* int        in1         */        0,
+/* int        un         */        0,        /* unindent of left margin in some way */
+/* int        wch         */        0,
+/* int        pendt         */        0,
+/* Tchar *pendw         */        (Tchar *)0,
+/* int        pendnf         */        0,
+/* int        spread         */        0,
+/* int        it         */        0,        /* input trap count */
+/* int        itmac         */        0
+} };
+
+Env        *envp        = env;        /* start off in env 0 */
+
+Numerr        numerr;
+
+Stack        *frame, *stk, *ejl;
+Stack        *nxf;
+
+int        pipeflg;
+int        hflg;        /* used in nroff only */
+int        eqflg;        /* used in nroff only */
+
+int        xpts;
+int        ppts;
+int        pfont;
+int        mpts;
+int        mfont;
+int        cs;
+int        ccs;
+int        bd;
+
+int        stdi;
+int        quiet;
+int        stop;
+char        ibuf[IBUFSZ];
+char        xbuf[IBUFSZ];
+char        *ibufp;
+char        *xbufp;
+char        *eibuf;
+char        *xeibuf;
+Tchar        pbbuf[NC];                /* pushback buffer for arguments, \n, etc. */
+Tchar        *pbp = pbbuf;                /* next free slot in pbbuf */
+Tchar        *lastpbp = pbbuf;        /* pbp in previous stack frame */
+int        nx;
+int        mflg;
+Tchar        ch = 0;
+int        ibf;
+int        ifi;
+int        iflg;
+int        rargc;
+char        **argp;
+Ushort        trtab[NTRTAB];
+int        lgf;
+int        copyf;
+Offset        ip;
+int        nlflg;
+int        donef;
+int        nflush;
+int        nfo;
+int        padc;
+int        raw;
+int        flss;
+int        nonumb;
+int        trap;
+int        tflg;
+int        ejf;
+int        dilev;
+Offset        offset;
+int        em;
+int        ds;
+Offset        woff;
+int        app;
+int        ndone;
+int        lead;
+int        ralss;
+Offset        nextb;
+Tchar        nrbits;
+int        nform;
+int        oldmn;
+int        newmn;
+int        macerr;
+Offset        apptr;
+int        diflg;
+int        evi;
+int        vflag;
+int        noscale;
+int        po1;
+int        nlist[NTRAP];
+int        mlist[NTRAP];
+int        evlist[EVLSZ];
+int        ev;
+int        tty;
+int        sfont        = FT;        /* appears to be "standard" font; used by .ul */
+int        sv;
+int        esc;
+int        widthp;
+int        xfont;
+int        setwdf;
+int        over;
+int        nhyp;
+Tchar        **hyp;
+Tchar        *olinep;
+int        dotT;
+char        *unlkp;
+Wcache        widcache[NWIDCACHE];
+Diver        d[NDI];
+Diver        *dip;
+
+int        c_hyphen;
+int        c_emdash;
+int        c_rule;
+int        c_minus;
+int        c_fi;
+int        c_fl;
+int        c_ff;
+int        c_ffi;
+int        c_ffl;
+int        c_acute;
+int        c_grave;
+int        c_under;
+int        c_rooten;
+int        c_boxrule;
+int        c_lefthand;
+int        c_dagger;
+int        c_isalnum;
+
+Spnames        spnames[] =
+{
+        &c_hyphen,        "hy",
+        &c_emdash,        "em",
+        &c_rule,        "ru",
+        &c_minus,        "\\-",
+        &c_fi,                "fi",
+        &c_fl,                "fl",
+        &c_ff,                "ff",
+        &c_ffi,                "Fi",
+        &c_ffl,                "Fl",
+        &c_acute,        "aa",
+        &c_grave,        "ga",
+        &c_under,        "ul",
+        &c_rooten,        "rn",
+        &c_boxrule,        "br",
+        &c_lefthand,        "lh",
+        &c_dagger,        "dg",        /* not in nroff?? */
+        &c_isalnum,        "__",
+        0, 0
+};
+
+
+Tchar        (*hmot)(void);
+Tchar        (*makem)(int i);
+Tchar        (*setabs)(void);
+Tchar        (*setch)(int c);
+Tchar        (*sethl)(int k);
+Tchar        (*setht)(void);
+Tchar        (*setslant)(void);
+Tchar        (*vmot)(void);
+Tchar        (*xlss)(void);
+int        (*findft)(int i);
+int        (*width)(Tchar j);
+void        (*mchbits)(void);
+void        (*ptlead)(void);
+void        (*ptout)(Tchar i);
+void        (*ptpause)(void);
+void        (*setfont)(int a);
+void        (*setps)(void);
+void        (*setwd)(void);
+
diff --git a/troff/suftab.c b/troff/suftab.c
@@ -0,0 +1,612 @@
+/*
+ * Suffix table
+ */
+
+typedef unsigned char Uchar;
+
+static        Uchar sufa[] = {
+        02,0200+'t',        /* -TA */
+        02,0200+'s',        /* -SA */
+        03,0200+'t','r',        /* -TRA */
+        03,0200+'d','r',        /* -DRA */
+        03,0200+'b','r',        /* -BRA */
+        02,0200+'p',        /* -PA */
+        02,0200+'n',        /* -NA */
+        02,0200+'m',        /* -MA */
+        03,0200+'p','l',        /* -PLA */
+        02,0200+'l',        /* -LA */
+        02,0200+'k',        /* -KA */
+        03,0200+'t','h',        /* -THA */
+        03,0200+'s','h',        /* -SHA */
+        02,0200+'g',        /* -GA */
+        02,0200+'d',        /* -DA */
+        02,0200+'c',        /* -CA */
+        02,0200+'b',        /* -BA */
+        00
+};
+
+static        Uchar sufc[] = {
+        04,'e','t',0200+'i',        /* ET-IC */
+        07,'a','l',0200+'i','s',0200+'t','i',        /* AL-IS-TIC */
+        04,'s',0200+'t','i',        /* S-TIC */
+        04,'p',0200+'t','i',        /* P-TIC */
+        05,0200+'l','y','t',0200+'i',        /* -LYT-IC */
+        04,'o','t',0200+'i',        /* OT-IC */
+        05,'a','n',0200+'t','i',        /* AN-TIC */
+        04,'n',0200+'t','i',        /* N-TIC */
+        04,'c',0200+'t','i',        /* C-TIC */
+        04,'a','t',0200+'i',        /* AT-IC */
+        04,'h',0200+'n','i',        /* H-NIC */
+        03,'n',0200+'i',        /* N-IC */
+        03,'m',0200+'i',        /* M-IC */
+        04,'l',0200+'l','i',        /* L-LIC */
+        04,'b',0200+'l','i',        /* B-LIC */
+        04,0200+'c','l','i',        /* -CLIC */
+        03,'l',0200+'i',        /* L-IC */
+        03,'h',0200+'i',        /* H-IC */
+        03,'f',0200+'i',        /* F-IC */
+        03,'d',0200+'i',        /* D-IC */
+        03,0200+'b','i',        /* -BIC */
+        03,'a',0200+'i',        /* A-IC */
+        03,0200+'m','a',        /* -MAC */
+        03,'i',0200+'a',        /* I-AC */
+        00
+};
+
+static        Uchar sufd[] = {
+        04,0200+'w','o','r',        /* -WORD */
+        04,0200+'l','o','r',        /* -LORD */
+        04,0200+'f','o','r',        /* -FORD */
+        04,0200+'y','a','r',        /* -YARD */
+        04,0200+'w','a','r',        /* -WARD */
+        05,0200+'g','u','a','r',        /* -GUARD */
+        04,0200+'t','a','r',        /* -TARD */
+        05,0200+'b','o','a','r',        /* -BOARD */
+        04,0200+'n','a','r',        /* -NARD */
+        05,0200+'l','i','a','r',        /* -LIARD */
+        04,0200+'i','a','r',        /* -IARD */
+        04,0200+'g','a','r',        /* -GARD */
+        04,0200+'b','a','r',        /* -BARD */
+        03,0200+'r','o',        /* -ROD */
+        04,0200+'w','o','o',        /* -WOOD */
+        04,0200+'h','o','o',        /* -HOOD */
+        04,0200+'m','o','n',        /* -MOND */
+        04,0200+'t','e','n',        /* -TEND */
+        05,0200+'s','t','a','n',        /* -STAND */
+        04,0200+'l','a','n',        /* -LAND */
+        04,0200+'h','a','n',        /* -HAND */
+        04,0200+'h','o','l',        /* -HOLD */
+        04,0200+'f','o','l',        /* -FOLD */
+        05,0200+'f','i','e','l',        /* -FIELD */
+        03,0200+'v','i',        /* -VID */
+        03,0200+'c','i',        /* -CID */
+        04,0200+'s','a','i',        /* -SAID */
+        04,0200+'m','a','i',        /* -MAID */
+        04,'t',0200+'t','e',        /* T-TED */
+        03,'t',0200+'e',        /* T-ED */
+        04,0200+'d','r','e',        /* -DRED */
+        04,0200+'c','r','e',        /* -CRED */
+        04,0200+'b','r','e',        /* -BRED */
+        05,'v',0200+'e','l','e',        /* V-ELED */
+        0100+04,'a','l',0200+'e',        /* AL/ED */
+        0140+03,0200+'e','e',        /* /EED */
+        040+05,'e','d',0200+'d','e',        /* ED-DED */
+        04,'d',0200+'d','e',        /* D-DED */
+        040+04,'e','d',0200+'e',        /* ED-ED */
+        03,'d',0200+'e',        /* D-ED */
+        05,0200+'d','u','c','e',        /* -DUCED */
+        0300+02,'e',        /* E/D */
+        05,0200+'s','t','e','a',        /* -STEAD */
+        05,0200+'a','h','e','a',        /* -AHEAD */
+        04,0200+'h','e','a',        /* -HEAD */
+        00
+};
+
+static        Uchar sufe[] = {
+        05,'a','r',0200+'i','z',        /* AR-IZE */
+        05,'a','n',0200+'i','z',        /* AN-IZE */
+        05,'a','l',0200+'i','z',        /* AL-IZE */
+        06,0200+'a','r','d',0200+'i','z',        /* -ARD-IZE */
+        05,0200+'s','e','l','v',        /* -SELVE */
+        05,0200+'k','n','i','v',        /* -KNIVE */
+        05,0200+'l','i','e','v',        /* -LIEVE */
+        0100+03,0200+'q','u',        /* /QUE */
+        07,'o','n',0200+'t','i','n',0200+'u',        /* ON-TIN-UE */
+        03,0200+'n','u',        /* -NUE */
+        03,0200+'d','u',        /* -DUE */
+        0300+02,'u',        /* U/E */
+        0300+05,'q','u','a','t',        /*  QUAT/E */
+        04,'u',0200+'a','t',        /* U-ATE */
+        05,0200+'s','t','a','t',        /* -STATE */
+        04,0200+'t','a','t',        /* -TATE */
+        06,0200+'t','o','r',0200+'a','t',        /* -TOR-ATE */
+        05,'e','n',0200+'a','t',        /* EN-ATE */
+        04,0200+'m','a','t',        /* -MATE */
+        05,0200+'h','o','u','s',        /* -HOUSE */
+        05,0200+'c','l','o','s',        /* -CLOSE */
+        04,'i',0200+'o','s',        /* I-OSE */
+        04,0200+'w','i','s',        /* -WISE */
+        05,'a','s',0200+'u','r',        /* AS-URE */
+        040+04,0200+'s','u','r',        /* -SURE */
+        06,0200+'f','i','g',0200+'u','r',        /* -FIG-URE */
+        040+03,0200+'t','r',        /* -TRE */
+        05,0200+'s','t','o','r',        /* -STORE */
+        04,0200+'f','o','r',        /* -FORE */
+        05,0200+'w','h','e','r',        /* -WHERE */
+        06,0200+'s','p','h','e','r',        /* -SPHERE */
+        03,0200+'d','r',        /* -DRE */
+        03,0200+'c','r',        /* -CRE */
+        03,0200+'b','r',        /* -BRE */
+        05,0200+'s','c','o','p',        /* -SCOPE */
+        04,'y',0200+'o','n',        /* Y-ONE */
+        05,0200+'s','t','o','n',        /* -STONE */
+        05,0200+'p','h','o','n',        /* -PHONE */
+        04,0200+'g','o','n',        /* -GONE */
+        04,'e',0200+'o','n',        /* E-ONE */
+        040+04,0200+'e','n','n',        /* -ENNE */
+        040+05,'a',0200+'r','i','n',        /* A-RINE */
+        05,0200+'c','l','i','n',        /* -CLINE */
+        04,0200+'l','i','n',        /* -LINE */
+        007,00200+'r','o','u',00200+'t','i','n',        /*-ROU-TINE */
+        04,0200+'s','o','m',        /* -SOME */
+        04,0200+'c','o','m',        /* -COME */
+        04,0200+'t','i','m',        /* -TIME */
+        03,0200+'z','l',        /* -ZLE */
+        03,0200+'t','l',        /* -TLE */
+        03,0200+'s','l',        /* -SLE */
+        03,0200+'p','l',        /* -PLE */
+        05,0200+'v','i','l','l',        /* -VILLE */
+        04,'c','k',0200+'l',        /* CK-LE */
+        03,0200+'k','l',        /* -KLE */
+        03,0200+'g','l',        /* -GLE */
+        03,0200+'f','l',        /* -FLE */
+        03,0200+'d','l',        /* -DLE */
+        03,0200+'c','l',        /* -CLE */
+        05,0200+'p','a',0200+'b','l',        /* -PA-BLE */
+        05,'f','a',0200+'b','l',        /* FA-BLE */
+        05,0200+'c','a',0200+'b','l',        /* -CA-BLE */
+        06,0200+'s','t','a','b','l',        /* -STABLE */
+        04,0200+'a','b','l',        /* -ABLE */
+        03,0200+'b','l',        /* -BLE */
+        04,0200+'d','a','l',        /* -DALE */
+        04,0200+'m','a','l',        /* -MALE */
+        04,0200+'s','a','l',        /* -SALE */
+        04,0200+'l','i','k',        /* -LIKE */
+        0340+05,'g',0200+'u','a','g',        /* -G/UAGE */
+        05,0200+'r','i','a','g',        /* -RIAGE */
+        05,'e','r',0200+'a','g',        /* ER-AGE */
+        04,'m',0200+'a','g',        /* M-AGE */
+        04,'k',0200+'a','g',        /* K-AGE */
+        04,'d',0200+'a','g',        /* D-AGE */
+        04,0200+'w','i','f',        /* -WIFE */
+        05,0200+'k','n','i','f',        /* -KNIFE */
+        03,0200+'s','e',        /* -SEE */
+        04,0200+'f','r','e',        /* -FREE */
+        0340+02,'e',        /* EE */
+        04,0200+'w','i','d',        /* -WIDE */
+        04,0200+'t','i','d',        /* -TIDE */
+        04,0200+'s','i','d',        /* -SIDE */
+        06,0200+'q','u','e','n','c',        /* -QUENCE */
+        07,0200+'f','l','u',0200+'e','n','c',        /* -FLU-ENCE */
+        040+06,'e','s',0200+'e','n','c',        /* ES-ENCE */
+        06,'e','r',0200+'e','n','c',        /* ER-ENCE */
+        05,'i',0200+'e','n','c',        /* I-ENCE */
+        040+05,0200+'s','a','n','c',        /* -SANCE */
+        06,'e','r',0200+'a','n','c',        /* ER-ANCE */
+        06,'a','r',0200+'a','n','c',        /* AR-ANCE */
+        05,0200+'n','a','n','c',        /* -NANCE */
+        07,0200+'b','a','l',0200+'a','n','c',        /* -BAL-ANCE */
+        05,'i',0200+'a','n','c',        /* I-ANCE */
+        07,0200+'j','u','s',0200+'t','i','c',        /* -JUS-TICE */
+        05,0200+'s','t','i','c',        /* -STICE */
+        06,0200+'n','o','v',0200+'i','c',        /* NOV-ICE */
+        04,0200+'v','i','c',        /* -VICE */
+        05,0200+'p','i','e','c',        /* -PIECE */
+        05,0200+'p','l','a','c',        /* -PLACE */
+        0340+01,        /* /E */
+        00
+};
+
+static        Uchar suff[] = {
+        03,0200+'o','f',        /* -OFF */
+        05,0200+'p','r','o','o',        /* -PROOF */
+        04,0200+'s','e','l',        /* -SELF */
+        03,0200+'r','i',        /* -RIF */
+        040+04,0200+'l','i','e',        /* -LIEF */
+        00
+};
+
+static        Uchar sufg[] = {
+        03,0200+'l','o',        /* -LOG */
+        04,0200+'l','o','n',        /* -LONG */
+        05,'t',0200+'t','i','n',        /* T-TING */
+        06,0200+'s','t','r','i','n',        /*  -STRING */
+        05,'r',0200+'r','i','n',        /* R-RING */
+        05,'p',0200+'p','i','n',        /* P-PING */
+        05,'n',0200+'n','i','n',        /* N-NING */
+        05,'m',0200+'m','i','n',        /* M-MING */
+        05,'l',0200+'l','i','n',        /*  L-LING */
+        05,0200+'z','l','i','n',        /* -ZLING */
+        05,0200+'t','l','i','n',        /* -TLING */
+        040+05,'s',0200+'l','i','n',        /* S-LING */
+        05,'r',0200+'l','i','n',        /* R-LING */
+        05,0200+'p','l','i','n',        /* -PLING */
+        06,'n',0200+'k','l','i','n',        /* N-KLING */
+        05,'k',0200+'l','i','n',        /* K-LING */
+        05,0200+'g','l','i','n',        /* -GLING */
+        05,0200+'f','l','i','n',        /* -FLING */
+        05,0200+'d','l','i','n',        /* -DLING */
+        05,0200+'c','l','i','n',        /* -CLING */
+        05,0200+'b','l','i','n',        /* -BLING */
+        06,'y',0200+'t','h','i','n',        /* Y-THING */
+        07,'e','e','t','h',0200+'i','n',        /* EETH-ING */
+        06,'e',0200+'t','h','i','n',        /* E-THING */
+        05,'g',0200+'g','i','n',        /* G-GING */
+        05,'d',0200+'d','i','n',        /* D-DING */
+        05,'b',0200+'b','i','n',        /* B-BING */
+        03,0200+'i','n',        /* -ING */
+        00
+};
+
+static        Uchar sufh[] = {
+        05,0200+'m','o','u','t',        /* -MOUTH */
+        05,0200+'w','o','r','t',        /* -WORTH */
+        04,0200+'w','i','t',        /* -WITH */
+        05,'t',0200+'t','i','s',        /* T-TISH */
+        05,'e',0200+'t','i','s',        /* E-TISH */
+        05,'p',0200+'p','i','s',        /* P-PISH */
+        05,'r',0200+'n','i','s',        /* R-NISH */
+        05,'n',0200+'n','i','s',        /* N-NISH */
+        05,0200+'p','l','i','s',        /* -PLISH */
+        05,0200+'g','u','i','s',        /*  -GUISH */
+        05,0200+'g','l','i','s',        /*  -GLISH */
+        05,'b',0200+'l','i','s',        /*  B-LISH */
+        05,'g',0200+'g','i','s',        /* G-GISH */
+        05,'d',0200+'d','i','s',        /* D-DISH */
+        03,0200+'i','s',        /* -ISH */
+        05,0200+'g','r','a','p',        /* -GRAPH */
+        07,0200+'b','o','r',0200+'o','u','g',        /* -BOR-OUGH */
+        05,0200+'b','u','r','g',        /* -BURGH */
+        04,0200+'v','i','c',        /* -VICH */
+        03,0200+'n','a',        /* -NAH */
+        03,0200+'l','a',        /* -LAH */
+        04,0200+'m','i',0200+'a',        /* -MI-AH */
+        00
+};
+
+static        Uchar sufi[] = {
+        03,0200+'t','r',        /* -TRI */
+        03,0200+'c','h',        /* -CHI */
+        0200+03,'i','f',        /* IF-I */
+        0200+03,'e','d',        /* ED-I */
+        05,0200+'a','s','c','i',        /* -ASCII */
+        04,0200+'s','e','m',        /* -SEMI */
+        00
+};
+
+static        Uchar sufk[] = {
+        04,0200+'w','o','r',        /* -WORK */
+        04,0200+'m','a','r',        /* -MARK */
+        04,0200+'b','o','o',        /* -BOOK */
+        04,0200+'w','a','l',        /* -WALK */
+        05,0200+'c','r','a','c',        /* -CRACK */
+        04,0200+'b','a','c',        /* -BACK */
+        00
+};
+
+static        Uchar sufl[] = {
+        03,0200+'f','u',        /* -FUL */
+        05,'s',0200+'w','e','l',        /* S-WELL */
+        04,0200+'t','e','l',        /* -TELL */
+        05,0200+'s','h','e','l',        /* -SHELL */
+        05,0200+'s','t','a','l',        /* -STALL */
+        04,'s',0200+'t','a',        /* S-TAL */
+        04,0200+'b','a','l',        /* -BALL */
+        04,0200+'c','a','l',        /* -CALL */
+        03,'v',0200+'e',        /* V-EL */
+        03,'u',0200+'e',        /* U-EL */
+        03,'k',0200+'e',        /* K-EL */
+        04,'t','h',0200+'e',        /* TH-EL */
+        05,'t','c','h',0200+'e',        /* TCH-EL */
+        03,'a',0200+'e',        /* A-EL */
+        0140+04,0200+'q','u','a',        /* /QUAL */
+        040+03,'u',0200+'a',        /* U-AL */
+        03,0200+'t','a',        /* -TAL */
+        04,'u','r',0200+'a',        /* UR-AL */
+        040+05,'g',0200+'o',0200+'n','a',        /* G-O-NAL */
+        04,'o','n',0200+'a',        /* ON-AL */
+        03,0200+'n','a',        /* -NAL */
+        04,0200+'t','i','a',        /* -TIAL */
+        04,0200+'s','i','a',        /* -SIAL */
+        040+05,0200+'t','r','i',0200+'a',        /* -TRI-AL */
+        04,'r','i',0200+'a',        /* RI-AL */
+        04,0200+'n','i',0200+'a',        /* -NI-AL */
+        04,0200+'d','i',0200+'a',        /* -DI-AL */
+        04,0200+'c','i','a',        /* -CIAL */
+        03,0200+'g','a',        /* -GAL */
+        04,0200+'m','e','a',        /* -MEAL */
+/*        040+04,0200+'r','e',0200+'a',        /* -RE-AL */
+        040+04,0200+'r','e','a',        /* -REAL */
+        06,'c',0200+'t','i',0200+'c','a',        /* C-TI-CAL */
+        05,0200+'s','i',0200+'c','a',        /* -SI-CAL */
+        04,0200+'i',0200+'c','a',        /* -I-CAL */
+        03,0200+'c','a',        /* -CAL */
+        03,0200+'b','a',        /* -BAL */
+        06,0200+'n','o',0200+'m','i',0200+'a',        /* -NO-MI-AL */
+        00
+};
+
+static        Uchar sufm[] = {
+        03,0200+'n','u',        /* -NUM */
+        05,'o',0200+'r','i',0200+'u',        /* O-RI-UM */
+        040+03,'i',0200+'u',        /* I-UM */
+        040+03,'e',0200+'u',        /* E-UM */
+        05,'i','v',0200+'i','s',        /* IV-ISM */
+        04,0200+'t','i','s',        /* -TISM */
+        05,'i',0200+'m','i','s',        /* I-MISM */
+        05,'a','l',0200+'i','s',        /* AL-ISM */
+        040+04,'e',0200+'i','s',        /* E-ISM */
+        040+04,'a',0200+'i','s',        /* A-ISM */
+        04,0200+'r','o','o',        /* -ROOM */
+        03,0200+'d','o',        /* -DOM */
+        03,0200+'h','a',        /* -HAM */
+        06,0200+'a',0200+'r','i','t','h',        /* -A-RITHM */
+        05,0200+'r','i','t','h',        /* -RITHM */
+        00
+};
+
+static        Uchar sufn[] = {
+        05,0200+'k','n','o','w', /* -KNOWN */
+        04,0200+'t','o','w',        /* -TOWN */
+        04,0200+'d','o','w',        /* -DOWN */
+        04,0200+'t','u','r',        /* -TURN */
+        05,0200+'s','p','o','o',        /* -SPOON */
+        04,0200+'n','o','o',        /* -NOON */
+        04,0200+'m','o','o',        /* -MOON */
+        011,'a','l',0200+'i',0200+'z','a',0200+'t','i','o',        /* AL-I-ZA-TION */
+        07,0200+'i',0200+'z','a',0200+'t','i','o',        /* -I-ZA-TION */
+        07,'l',0200+'i',0200+'a',0200+'t','i','o',        /* L-I-A-TION */
+        04,0200+'t','i','o',        /* -TION */
+        040+05,'s',0200+'s','i','o',        /* S-SION */
+        04,0200+'s','i','o',        /* -SION */
+        04,'n',0200+'i','o',        /* N-ION */
+        04,0200+'g','i','o',        /* -GION */
+        04,0200+'c','i','o',        /* -CION */
+        03,0200+'c','o',        /* -CON */
+        05,0200+'c','o','l','o',        /* -COLON */
+        03,0200+'t','o',        /* -TON */
+        04,'i','s',0200+'o',                /* IS-ON */
+        03,0200+'s','o',        /* -SON */
+        03,0200+'r','i',        /* -RIN */
+        03,0200+'p','i',        /* -PIN */
+        03,0200+'n','i',        /* -NIN */
+        03,0200+'m','i',        /* -MIN */
+        03,0200+'l','i',        /* -LIN */
+        03,0200+'k','i',        /* -KIN */
+        05,0200+'s','t','e','i',        /* -STEIN */
+        04,0200+'t','a','i',        /* -TAIN */
+        05,'g','h','t',0200+'e',        /* GHT-EN */
+        05,0200+'w','o','m',0200+'e',        /* -WOM-EN */
+        03,0200+'m','e',        /* -MEN */
+        04,'o',0200+'k','e',        /* O-KEN */
+        03,'k',0200+'e',        /* K-EN */
+        04,0200+'t','e','e',        /* -TEEN */
+        04,0200+'s','e','e',        /* -SEEN */
+        040+03,0200+'s','a',        /* -SAN */
+        05,0200+'w','o','m',0200+'a',        /* -WOM-AN */
+        03,0200+'m','a',        /* -MAN */
+        04,0200+'t','i','a',        /* -TIAN */
+        04,0200+'s','i','a',        /* -SIAN */
+        040+04,'e',0200+'i','a',        /* E-IAN */
+        04,0200+'c','i','a',        /* -CIAN */
+        0300+03,'i','a',        /* IA/N */
+        05,0200+'c','l','e','a',        /* -CLEAN */
+        04,0200+'m','e','a',        /* -MEAN */
+        040+03,'e',0200+'a',        /* E-AN */
+        00
+};
+
+static        Uchar sufo[] = {
+        05,0200+'m','a','c',0200+'r',        /* -MAC-RO */
+        00
+};
+
+static        Uchar sufp[] = {
+        05,0200+'g','r','o','u',        /* -GROUP */
+        02,0200+'u',        /* -UP */
+        04,0200+'s','h','i',        /* -SHIP */
+        04,0200+'k','e','e',        /* -KEEP */
+        00
+};
+
+static        Uchar sufr[] = {
+        04,0200+'z','a','r',        /* -ZARR */
+        0300+02,'r',        /* R/R */
+        03,0200+'t','o',        /* -TOR */
+        040+03,0200+'s','o',        /* -SOR */
+        040+04,0200+'r','i',0200+'o',        /* -RI-OR */
+        04,'i','z',0200+'e',        /* IZ-ER */
+        05,0200+'c','o','v',0200+'e',        /* -COV-ER */
+        04,0200+'o','v','e',        /* -OVER */
+        04,0200+'e','v',0200+'e',        /* -EV-ER */
+        8,0200+'c','o','m',0200+'p','u','t',0200+'e',        /* -COM-PUT-ER */
+        040+05,'u','s',0200+'t','e',        /* US-TER */
+        05,'o','s','t',0200+'e',        /* OST-ER */
+        040+05,0200+'a','c',0200+'t','e',        /* -AC-TER */
+        06,0200+'w','r','i','t',0200+'e',        /* -WRIT-ER */
+        040+05,'i','s',0200+'t','e',        /* IS-TER */
+        040+05,'e','s',0200+'t','e',        /* ES-TER */
+        040+05,'a','s',0200+'t','e',        /* AS-TER */
+        04,0200+'s','t','e',        /* -STER */
+        05,'a','r',0200+'t','e',        /* AR-TER */
+        04,'r','t',0200+'e',        /* RT-ER */
+        040+05,'m',0200+'e',0200+'t','e',        /* M-E-TER */
+        05,0200+'w','a',0200+'t','e',        /* -WA-TER */
+        03,'r',0200+'e',        /* R-ER */
+        04,'o','p',0200+'e',        /* OP-ER */
+        05,0200+'p','a',0200+'p','e',        /* -PA-PER */
+        04,'w','n',0200+'e',        /* WN-ER */
+        040+04,'s',0200+'n','e',        /* S-NER */
+        04,'o','n',0200+'e',        /* ON-ER */
+        04,'r','m',0200+'e',        /* RM-ER */
+        03,0200+'m','e',        /* -MER */
+        04,'l','l',0200+'e',        /* LL-ER */
+        05,'d',0200+'d','l','e',        /* D-DLER */
+        04,0200+'b','l','e',        /* -BLER */
+        03,'k',0200+'e',        /* K-ER */
+        05,'n',0200+'t','h','e',        /* N-THER */
+        06,0200+'f','a',0200+'t','h','e',        /* -FA-THER */
+        06,'e','i',0200+'t','h','e',        /* EI-THER */
+        04,'t','h',0200+'e',        /* TH-ER */
+        04,'s','h',0200+'e',        /* SH-ER */
+        04,0200+'p','h','e',        /* -PHER */
+        04,'c','h',0200+'e',        /* CH-ER */
+        04,'d','g',0200+'e',        /* DG-ER */
+        04,'r','d',0200+'e',        /* RD-ER */
+        06,'o','u','n','d',0200+'e',        /* OUND-ER */
+        04,'l','d',0200+'e',        /* LD-ER */
+        04,'i','d',0200+'e',        /* ID-ER */
+        05,0200+'d','u','c',0200+'e',        /* -DUC-ER */
+        04,'n','c',0200+'e',        /* NC-ER */
+        0100+02, 0200+'e',        /*  /ER */
+        03,0200+'s','a',        /* -SAR */
+        040+06,'a','c',0200+'u',0200+'l','a',        /* AC-U-LAR */
+        040+06,'e','c',0200+'u',0200+'l','a',        /* EC-U-LAR */
+        040+06,'i','c',0200+'u',0200+'l','a',        /* IC-U-LAR */
+        040+06,'e','g',0200+'u',0200+'l','a',        /* EG-U-LAR */
+        00
+};
+
+static        Uchar sufs[] = {
+        040+04,'u',0200+'o','u',        /* U-OUS */
+        05,0200+'t','i','o','u',        /* -TIOUS */
+        05,0200+'g','i','o','u',        /* -GIOUS */
+        05,0200+'c','i','o','u',        /* -CIOUS */
+        040+04,'i',0200+'o','u',        /* I-OUS */
+        05,0200+'g','e','o','u',        /* -GEOUS */
+        05,0200+'c','e','o','u',        /* -CEOUS */
+        04,'e',0200+'o','u',        /* E-OUS */
+        0140+02,0200+'u',        /* /US */
+        04,0200+'n','e','s',        /* -NESS */
+        04,0200+'l','e','s',        /* -LESS */
+        0140+02,0200+'s',        /* /SS */
+        040+05,'p',0200+'o',0200+'l','i',        /* P-O-LIS */
+        0140+02,0200+'i',        /* /IS */
+        0100+03,0200+'x','e',        /* X/ES */
+        0100+03,0200+'s','e',        /* S/ES */
+        0100+04,'s','h',0200+'e',        /* SH/ES */
+        0100+04,'c','h',0200+'e',        /* CH/ES */
+        0300+01,        /* /S */
+        00
+};
+
+static        Uchar suft[] = {
+        05,0200+'l','i','m',0200+'i',        /* -LIM-IT */
+        06,'i','o','n',0200+'i','s',        /* ION-IST */
+        05,'i','n',0200+'i','s',        /* IN-IST */
+        05,'a','l',0200+'i','s',        /* AL-IST */
+        06,'l',0200+'o',0200+'g','i','s',        /* L-O-GIST */
+        05,'h','t',0200+'e','s',        /* HT-EST */
+        04,'i',0200+'e','s',        /* I-EST */
+        05,'g',0200+'g','e','s',        /* G-GEST */
+        04,'g',0200+'e','s',        /* G-EST */
+        05,'d',0200+'d','e','s',        /* D-DEST */
+        04,'d',0200+'e','s',        /* D-EST */
+        04,0200+'c','a','s',        /* -CAST */
+        05,0200+'h','e','a','r',        /* -HEART */
+        04,0200+'f','o','o',        /* -FOOT */
+        03,'i',0200+'o',        /* I-OT */
+        05,0200+'f','r','o','n',        /* -FRONT */
+        05,0200+'p','r','i','n',        /* -PRINT */
+        04,0200+'m','e','n',        /* -MENT */
+        05,0200+'c','i','e','n',        /* -CIENT */
+        04,'i',0200+'a','n',        /* I-ANT */
+        06,0200+'w','r','i','g','h',        /* -WRIGHT */
+        06,0200+'b','r','i','g','h',        /* -BRIGHT */
+        06,0200+'f','l','i','g','h',        /* -FLIGHT */
+        06,0200+'w','e','i','g','h',        /* -WEIGHT */
+        05,0200+'s','h','i','f',        /* -SHIFT */
+        05,0200+'c','r','a','f',        /* -CRAFT */
+        040+04,'d','g',0200+'e',        /* DG-ET */
+        04,0200+'g','o','a',        /* -GOAT */
+        04,0200+'c','o','a',        /* -COAT */
+        04,0200+'b','o','a',        /* -BOAT */
+        04,0200+'w','h','a',        /* -WHAT */
+        04,0200+'c','u','i',        /* -CUIT */
+        00
+};
+
+static        Uchar sufy[] = {
+        040+04,'e','s',0200+'t',        /* ES-TY */
+        040+05,'q','u','i',0200+'t',        /* QUI-TY */
+        04,0200+'t','i',0200+'t',        /* -TI-TY */
+        040+05,'o','s',0200+'i',0200+'t',        /* OS-I-TY */
+        04,0200+'s','i',0200+'t',        /* -SI-TY */
+        05,'i','n',0200+'i',0200+'t',        /* IN-I-TY */
+        04,'n','i',0200+'t',        /* NI-TY */
+        040+010,'f','a',0200+'b','i','l',0200+'i',0200+'t',        /* FA-BIL-I-TY */
+        010,0200+'c','a',0200+'b','i','l',0200+'i',0200+'t',        /* -CA-BIL-I-TY */
+        010,0200+'p','a',0200+'b','i','l',0200+'i',0200+'t',        /* -PA-BIL-I-TY */
+        06,0200+'b','i','l',0200+'i',0200+'t',        /* -BIL-I-TY */
+        03,'i',0200+'t',        /* I-TY */
+        04,0200+'b','u','r',        /* -BUR-Y */
+        04,0200+'t','o',0200+'r',        /* -TO-RY */
+        05,0200+'q','u','a','r',        /* -QUAR-Y */
+        040+04,'u',0200+'a','r',        /* U-ARY */
+        07,0200+'m','e','n',0200+'t','a',0200+'r',        /* -MEN-TA-RY */
+        06,'i','o','n',0200+'a','r',        /* ION-ARY */
+        04,'i',0200+'a','r',        /* I-ARY */
+        04,'n',0200+'o',0200+'m',        /* N-O-MY */
+        03,0200+'p','l',        /* -PLY */
+        04,'g',0200+'g','l',        /* G-GLY */
+        05,0200+'p','a',0200+'b','l',        /* -PA-BLY */
+        05,'f','a',0200+'b','l',        /* FA-BLY */
+        05,0200+'c','a',0200+'b','l',        /* -CA-BLY */
+        04,0200+'a','b','l',        /* -ABLY */
+        03,0200+'b','l',        /* -BLY */
+        02,0200+'l',        /* -LY */
+        03,0200+'s','k',        /* -SKY */
+        040+06,'g',0200+'r','a',0200+'p','h',        /* G-RA-PHY */
+        04,'l',0200+'o',0200+'g',        /* L-O-GY */
+        02,0200+'f',        /* -FY */
+        03,0200+'n','e',        /* -NEY */
+        03,0200+'l','e',        /* -LEY */
+        04,'c','k',0200+'e',        /* CK-EY */
+        03,0200+'k','e',        /* -KEY */
+        04,0200+'b','o','d',        /* -BODY */
+        05,0200+'s','t','u','d',        /* -STUDY */
+        0340+04,'e','e','d',        /* EEDY */
+        02,0200+'b',        /* -BY */
+        03,0200+'w','a',        /* -WAY */
+        03,0200+'d','a',        /* -DAY */
+        00
+};
+
+Uchar        *suftab[] = {
+        sufa,
+        0,
+        sufc,
+        sufd,
+        sufe,
+        suff,
+        sufg,
+        sufh,
+        sufi,
+        0,
+        sufk,
+        sufl,
+        sufm,
+        sufn,
+        sufo,
+        sufp,
+        0,
+        sufr,
+        sufs,
+        suft,
+        0,
+        0,
+        0,
+        0,
+        sufy,
+        0
+};
diff --git a/troff/t10.c b/troff/t10.c
@@ -0,0 +1,513 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+/*
+ * troff10.c
+ * 
+ * typesetter interface
+ */
+
+int        vpos         = 0;        /* absolute vertical position on page */
+int        hpos         = 0;        /* ditto horizontal */
+
+extern Font fonts[MAXFONTS+1];
+
+int        Inch;
+int        Hor;
+int        Vert;
+int        Unitwidth;
+int        nfonts;
+
+
+
+void t_ptinit(void)
+{
+        int i;
+        char buf[100], *p;
+
+        hmot = t_hmot;
+        makem = t_makem;
+        setabs = t_setabs;
+        setch = t_setch;
+        sethl = t_sethl;
+        setht = t_setht;
+        setslant = t_setslant;
+        vmot = t_vmot;
+        xlss = t_xlss;
+        findft = t_findft;
+        width = t_width;
+        mchbits = t_mchbits;
+        ptlead = t_ptlead;
+        ptout = t_ptout;
+        ptpause = t_ptpause;
+        setfont = t_setfont;
+        setps = t_setps;
+        setwd = t_setwd;
+
+        /* open table for device, */
+        /* read in resolution, size info, font info, etc., set params */
+        if ((p = getenv("TYPESETTER")) != 0)
+                strcpy(devname, p);
+        if (termtab[0] == 0)
+                strcpy(termtab, DWBfontdir);
+        if (fontdir[0] == 0)
+                strcpy(fontdir, DWBfontdir);
+        if (devname[0] == 0)
+                strcpy(devname, TDEVNAME);
+        hyf = 1;
+        lg = 1;
+
+        sprintf(buf, "/dev%s/DESC", devname);
+        strcat(termtab, buf);
+        if (getdesc(termtab) < 0) {
+                ERROR "can't open DESC file %s", termtab WARN;
+                done3(1);
+        }
+        if (!ascii) {
+                OUT "x T %s\n", devname PUT;
+                OUT "x res %d %d %d\n", Inch, Hor, Vert PUT;
+                OUT "x init\n" PUT;
+        }
+        for (i = 1; i <= nfonts; i++)
+                setfp(i, fontlab[i], (char *) 0, 0);
+        sps = EM/3;        /* space size */
+        ics = EM;        /* insertion character space */
+        for (i = 0; i < (NTAB - 1) && DTAB * (i + 1) < TABMASK; i++)
+                tabtab[i] = DTAB * (i + 1);
+        tabtab[NTAB-1] = 0;
+        pl = 11 * INCH;                        /* paper length */
+        po = PO;                /* page offset */
+        spacesz = SS;
+        lss = lss1 = VS;
+        ll = ll1 = lt = lt1 = LL;
+        t_specnames();        /* install names like "hyphen", etc. */
+}
+
+void t_specnames(void)
+{
+        int        i;
+
+        for (i = 0; spnames[i].n; i++)
+                *spnames[i].n = chadd(spnames[i].v, Troffchar, Install);
+}
+
+void t_ptout(Tchar i)
+{
+        int dv;
+        Tchar *k;
+        int temp, a, b;
+        int diff;
+
+        if (cbits(i) != '\n') {
+                if (olinep >= oline + olnsize) {
+                        diff = olinep - oline;
+                        olnsize += OLNSIZE;
+                        if ((oline = (Tchar *)realloc((char *)oline, olnsize * sizeof(Tchar))) != NULL) {
+                                if (diff && olinep)
+                                        olinep = oline + diff;
+                        } else {
+                                ERROR "Output line overflow." WARN;
+                                done(2);
+                        }
+                }
+                *olinep++ = i;
+                return;
+        }
+        if (olinep == oline) {
+                lead += lss;
+                return;
+        }
+
+        hpos = po;        /* ??? */
+        esc = 0;        /* ??? */
+        ptesc();        /* the problem is to get back to the left end of the line */
+        dv = 0;
+        for (k = oline; k < olinep; k++) {
+                if (ismot(*k) && isvmot(*k)) {
+                        temp = absmot(*k);
+                        if (isnmot(*k))
+                                temp = -temp;
+                        dv += temp;
+                }
+        }
+        if (dv) {
+                vflag++;
+                *olinep++ = makem(-dv);
+                vflag = 0;
+        }
+
+        b = dip->blss + lss;
+        lead += dip->blss + lss;
+        dip->blss = 0;
+        for (k = oline; k < olinep; )
+                k += ptout0(k);        /* now passing a pointer! */
+        olinep = oline;
+        lead += dip->alss;
+        a = dip->alss;
+        dip->alss = 0;
+        /*
+        OUT "x xxx end of line: hpos=%d, vpos=%d\n", hpos, vpos PUT;
+*/
+        OUT "n%d %d\n", b, a PUT;        /* be nice to chuck */
+}
+
+int ptout0(Tchar *pi)
+{
+        int j, k, w;
+        int z, dx, dy, dx2, dy2, n;
+        Tchar i;
+        int outsize;        /* size of object being printed */
+
+        w = 0;
+        outsize = 1;        /* default */
+        i = *pi;
+        k = cbits(i);
+        if (ismot(i)) {
+                j = absmot(i);
+                if (isnmot(i))
+                        j = -j;
+                if (isvmot(i))
+                        lead += j;
+                else 
+                        esc += j;
+                return(outsize);
+        }
+        if (k == CHARHT) {
+                xpts = fbits(i);        /* sneaky, font bits as size bits */
+                if (xpts != mpts)
+                        ptps();
+                OUT "x H %ld\n", sbits(i) PUT;
+                return(outsize);
+        }
+        if (k == SLANT) {
+                OUT "x S %ld\n", sfbits(i)-180 PUT;
+                return(outsize);
+        }
+        if (k == WORDSP) {
+                oput('w');
+                return(outsize);
+        }
+        if (sfbits(i) == oldbits) {
+                xfont = pfont;
+                xpts = ppts;
+        } else 
+                xbits(i, 2);
+        if (k == XON) {
+                extern int xon;
+                ptflush();        /* guarantee that everything is out */
+                if (esc)
+                        ptesc();
+                if (xfont != mfont)
+                        ptfont();
+                if (xpts != mpts)
+                        ptps();
+                if (lead)
+                        ptlead();
+                OUT "x X " PUT;
+                xon++;
+                for (j = 1; cbits(pi[j]) != XOFF; j++)
+                        outascii(pi[j]);
+                oput('\n');
+                xon--;
+                return j+1;
+        }
+        if (k < 040 && k != DRAWFCN)
+                return(outsize);
+        j = z = 0;
+        if (k != DRAWFCN) {
+                if (widcache[k].fontpts == (xfont<<8) + xpts  && !setwdf) {
+                        w = widcache[k].width;
+                        bd = 0;
+                        cs = 0;
+                } else
+                        w = getcw(k);
+                if (cs) {
+                        if (bd)
+                                w += (bd - 1) * HOR;
+                        j = (cs - w) / 2;
+                        w = cs - j;
+                        if (bd)
+                                w -= (bd - 1) * HOR;
+                }
+                if (iszbit(i)) {
+                        if (cs)
+                                w = -j; 
+                        else 
+                                w = 0;
+                        z = 1;
+                }
+        }
+        esc += j;
+        if (xfont != mfont)
+                ptfont();
+        if (xpts != mpts)
+                ptps();
+        if (lead)
+                ptlead();
+        /* put out the real character here */
+        if (k == DRAWFCN) {
+                if (esc)
+                        ptesc();
+                w = 0;
+                dx = absmot(pi[3]);
+                if (isnmot(pi[3]))
+                        dx = -dx;
+                dy = absmot(pi[4]);
+                if (isnmot(pi[4]))
+                        dy = -dy;
+                switch (cbits(pi[1])) {
+                case DRAWCIRCLE:        /* circle */
+                        OUT "D%c %d\n", DRAWCIRCLE, dx PUT;        /* dx is diameter */
+                        hpos += dx;
+                        break;
+                case DRAWELLIPSE:
+                        OUT "D%c %d %d\n", DRAWELLIPSE, dx, dy PUT;
+                        hpos += dx;
+                        break;
+                case DRAWBUILD:
+                        k = cbits(pi[2]);
+                        OUT "D%c %d ", DRAWBUILD, dx PUT;
+                        if (k < ALPHABET)
+                                OUT "%c\n", k PUT;
+                        else
+                                ptchname(k);
+                        hpos += dx;
+                        break;
+                case DRAWLINE:        /* line */
+                        k = cbits(pi[2]);
+                        OUT "D%c %d %d ", DRAWLINE, dx, dy PUT;
+                        if (k < ALPHABET)
+                                OUT "%c\n", k PUT;
+                        else
+                                ptchname(k);
+                        hpos += dx;
+                        vpos += dy;
+                        break;
+                case DRAWARC:        /* arc */
+                        dx2 = absmot(pi[5]);
+                        if (isnmot(pi[5]))
+                                dx2 = -dx2;
+                        dy2 = absmot(pi[6]);
+                        if (isnmot(pi[6]))
+                                dy2 = -dy2;
+                        OUT "D%c %d %d %d %d\n", DRAWARC,
+                                dx, dy, dx2, dy2 PUT;
+                        hpos += dx + dx2;
+                        vpos += dy + dy2;
+                        break;
+
+                case 's':        /* using 's' internally to avoid .tr ~ */
+                        pi[1] = '~';
+                case DRAWSPLINE:        /* spline */
+                default:        /* something else; copy it like spline */
+                        OUT "D%c %d %d", (char)cbits(pi[1]), dx, dy PUT;
+                        hpos += dx;
+                        vpos += dy;
+                        if (cbits(pi[3]) == DRAWFCN || cbits(pi[4]) == DRAWFCN) {
+                                /* it was somehow defective */
+                                OUT "\n" PUT;
+                                break;
+                        }
+                        for (n = 5; cbits(pi[n]) != DRAWFCN; n += 2) {
+                                dx = absmot(pi[n]);
+                                if (isnmot(pi[n]))
+                                        dx = -dx;
+                                dy = absmot(pi[n+1]);
+                                if (isnmot(pi[n+1]))
+                                        dy = -dy;
+                                OUT " %d %d", dx, dy PUT;
+                                hpos += dx;
+                                vpos += dy;
+                        }
+                        OUT "\n" PUT;
+                        break;
+                }
+                for (n = 3; cbits(pi[n]) != DRAWFCN; n++)
+                        ;
+                outsize = n + 1;
+        } else if (k < ALPHABET) {
+                /* try to go faster and compress output */
+                /* by printing nnc for small positive motion followed by c */
+                /* kludgery; have to make sure set all the vars too */
+                if (esc > 0 && esc < 100) {
+                        oput(esc / 10 + '0');
+                        oput(esc % 10 + '0');
+                        oput(k);
+                        hpos += esc;
+                        esc = 0;
+                } else {
+                        if (esc)
+                                ptesc();
+                        oput('c');
+                        oput(k);
+                        oput('\n');
+                }
+        } else {
+                if (esc)
+                        ptesc();
+                ptchname(k);
+        }
+        if (bd) {
+                bd -= HOR;
+                if (esc += bd)
+                        ptesc();
+                if (k < ALPHABET)
+                        OUT "c%c\n", k PUT;
+                else
+                        ptchname(k);
+                if (z)
+                        esc -= bd;
+        }
+        esc += w;
+        return(outsize);
+}
+
+void ptchname(int k)
+{
+        char *chn = chname(k);
+
+        switch (chn[0]) {
+        case MBchar:
+                OUT "c%s\n", chn+1 PUT;        /* \n not needed? */
+                break;
+        case Number:
+                OUT "N%s\n", chn+1 PUT;
+                break;
+        case Troffchar:
+                OUT "C%s\n", chn+1 PUT;
+                break;
+        default:
+                ERROR "illegal char type %s", chn WARN;
+                break;
+        }
+}
+
+void ptflush(void)        /* get us to a clean output state */
+{
+        if (TROFF) {
+                /* ptesc(); but always H, no h */
+                hpos += esc;
+                OUT "\nH%d\n", hpos PUT;
+                esc = 0;
+                ptps();
+                ptfont();
+                ptlead();
+        }
+}
+
+void ptps(void)
+{
+        int i, j, k;
+
+        i = xpts;
+        for (j = 0; i > (k = pstab[j]); j++)
+                if (!k) {
+                        k = pstab[--j];
+                        break;
+                }
+        if (!ascii)
+                OUT "s%d\n", k PUT;        /* really should put out string rep of size */
+        mpts = i;
+}
+
+void ptfont(void)
+{
+        mfont = xfont;
+        if (ascii)
+                return;
+        if (xfont > nfonts) {
+                ptfpcmd(0, fonts[xfont].longname, 0);        /* Put the desired font in the
+                                         * fontcache of the filter */
+                OUT "f0\n" PUT;        /* make sure that it gets noticed */
+        } else
+                OUT "f%d\n", xfont PUT;
+}
+
+void ptfpcmd(int f, char *s, char *longname)
+{
+        if (f > nfonts)                /* a bit risky? */
+                f = 0;
+        if (longname) {
+                OUT "x font %d %s %s\n", f, s, longname PUT;
+        } else {
+                OUT "x font %d %s\n", f, s PUT;
+        }
+/*        OUT "f%d\n", xfont PUT;        /* need this for buggy version of adobe transcript */
+                                /* which apparently believes that x font means */
+                                /* to set the font, not just the position. */
+}
+
+void t_ptlead(void)
+{
+        vpos += lead;
+        if (!ascii)
+                OUT "V%d\n", vpos PUT;
+        lead = 0;
+}
+
+void ptesc(void)
+{
+        hpos += esc;
+        if (!ascii)
+                if (esc > 0) {
+                        oput('h');
+                        if (esc>=10 && esc<100) {
+                                oput(esc/10 + '0');
+                                oput(esc%10 + '0');
+                        } else
+                                OUT "%d", esc PUT;
+                } else
+                        OUT "H%d\n", hpos PUT;
+        esc = 0;
+}
+
+void ptpage(int n)        /* called at end of each output page, we hope */
+{
+        int i;
+
+        if (NROFF)
+                return;
+        ptlead();
+        vpos = 0;
+        if (ascii)
+                return;
+        OUT "p%d\n", n PUT;        /* new page */
+        for (i = 0; i <= nfonts; i++)
+                if (fontlab[i]) {
+                        if (fonts[i].truename)
+                                OUT "x font %d %s %s\n", i, fonts[i].longname, fonts[i].truename PUT;
+                        else
+                                OUT "x font %d %s\n", i, fonts[i].longname PUT;
+                }
+        ptps();
+        ptfont();
+}
+
+void pttrailer(void)
+{
+        if (TROFF)
+                OUT "x trailer\n" PUT;
+}
+
+void ptstop(void)
+{
+        if (TROFF)
+                OUT "x stop\n" PUT;
+}
+
+void t_ptpause(void)
+{
+        if (ascii)
+                return;
+        ptlead();
+        vpos = 0;
+        pttrailer();
+        ptlead();
+        OUT "x pause\n" PUT;
+        flusho();
+        mpts = mfont = 0;
+        ptesc();
+        esc = po;
+        hpos = vpos = 0;        /* probably in wrong place */
+}
diff --git a/troff/t11.c b/troff/t11.c
@@ -0,0 +1,260 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#define        MAXCH NCHARS                /* maximum number of global char names */
+char        *chnames[MAXCH];        /* chnames[n-ALPHABET] -> name of char n */
+int        nchnames;                /* number of Cxy names currently seen */
+
+#define        MAXPS        100                /* max number of point sizes */
+int        pstab[MAXPS];                /* point sizes */
+int        nsizes;                        /* number in DESC */
+
+Font        fonts[MAXFONTS+1];        /* font info + ptr to width info */
+
+
+#define        skipline(f)        while (getc(f) != '\n')
+
+#define        eq(s1, s2)        (strcmp(s1, s2) == 0)
+
+int
+getdesc(char *name)
+{
+        FILE *fin;
+        char cmd[100], s[100];
+        int i, v;
+
+        if ((fin = fopen(unsharp(name), "r")) == NULL)
+                return -1;
+        while (fscanf(fin, "%s", cmd) != EOF) {
+                if (strcmp(cmd, "res") == 0) {
+                        fscanf(fin, "%d", &Inch);
+                } else if (strcmp(cmd, "hor") == 0) {
+                        fscanf(fin, "%d", &Hor);
+                } else if (strcmp(cmd, "vert") == 0) {
+                        fscanf(fin, "%d", &Vert);
+                } else if (strcmp(cmd, "unitwidth") == 0) {
+                        fscanf(fin, "%d", &Unitwidth);
+                } else if (strcmp(cmd, "sizes") == 0) {
+                        nsizes = 0;
+                        while (fscanf(fin, "%d", &v) != EOF && v != 0 && nsizes < MAXPS)
+                                pstab[nsizes++] = v;
+                } else if (strcmp(cmd, "fonts") == 0) {
+                        fscanf(fin, "%d", &nfonts);
+                        for (i = 1; i <= nfonts; i++) {
+                                fscanf(fin, "%s", s);
+                                fontlab[i] = PAIR(s[0], s[1]);
+                        }
+                } else if (strcmp(cmd, "charset") == 0) {        /* add any names */
+                        while (fscanf(fin, "%s", s) != EOF)
+                                chadd(s, Troffchar, Install);
+                        break;
+                }
+                /* else 
+                        just skip anything else */
+                skipline(fin);
+        }
+        fclose(fin);
+        return 1;
+}
+
+static int checkfont(char *name)
+{                /* in case it's not really a font description file */
+                /* really paranoid, but consider \f. */
+        FILE *fp;
+        char buf[300], buf2[300];
+        int i, status = -1;
+
+        if ((fp = fopen(unsharp(name), "r")) == NULL)
+                return -1;
+        for (i = 1; i <= 10; i++) {
+                if (fgets(buf, sizeof buf, fp) == NULL)
+                        break;
+                sscanf(buf, "%s", buf2);
+                if (buf2[0] == '#') {
+                        i--;
+                        continue;
+                }
+                if (eq(buf2, "name") || eq(buf2, "fontname") ||
+                    eq(buf2, "special") || eq(buf2, "charset")) {
+                        status = 1;
+                        break;
+                }
+        }
+        fclose(fp);
+        return status;
+        
+}
+
+int
+getfont(char *name, int pos)        /* create width tab for font */
+{
+        FILE *fin;
+        Font *ftemp = &fonts[pos];
+        Chwid chtemp[MAXCH];
+        static Chwid chinit;
+        int i, nw, n, wid, kern, code, type;
+        char buf[100], ch[100], s1[100], s2[100], s3[100], cmd[300];
+
+        nw = code = 0;
+        /* fprintf(stderr, "read font %s onto %d\n", name, pos); */
+        if (checkfont(name) == -1)
+                return -1;
+        if ((fin = fopen(unsharp(name), "r")) == NULL)
+                return -1;
+        for (i = 0; i < ALPHABET; i++)
+                chtemp[i] = chinit;        /* zero out to begin with */
+        ftemp->specfont = ftemp->ligfont = 0;
+        ftemp->defaultwidth = ftemp->spacewidth = Inch * Unitwidth / 72 / 3; /* should be rounded */
+        while (fscanf(fin, "%s", cmd) != EOF) {
+                if (strcmp(cmd, "name") == 0)
+                        fscanf(fin, "%s", ftemp->longname);
+                else if (strcmp(cmd, "special") == 0)
+                        ftemp->specfont = 1;
+                else if (strcmp(cmd, "ligatures") == 0) {
+                        ftemp->ligfont = getlig(fin);
+                } else if (strcmp(cmd, "spacewidth") == 0) {
+                        fscanf(fin, "%d", &ftemp->spacewidth);
+                } else if (strcmp(cmd, "defaultwidth") == 0) {
+                        fscanf(fin, "%d", &ftemp->defaultwidth);
+                } else if (strcmp(cmd, "charset") == 0) {
+                        wchar_t wc;
+                        skipline(fin);
+                        nw = ALPHABET;
+                        while (fgets(buf, sizeof buf, fin) != NULL) {
+                                sscanf(buf, "%s %s %s %s", ch, s1, s2, s3);
+                                if (s1[0] != '"') {        /* genuine new character */
+                                        sscanf(s1, "%d", &wid);
+                                        sscanf(s2, "%d", &kern);
+                                        code = strtol(s3, 0, 0);        /* dec/oct/hex */
+                                }
+                                /* otherwise it's a synonym for prev character, */
+                                /* so leave previous values intact */
+
+
+                                /* decide what kind of alphabet it might come from here */
+
+
+                                if (strlen(ch) == 1) {        /* it's ascii */
+                                        n = ch[0];        /* origin includes non-graphics */
+                                        chtemp[n].num = ch[0];
+                                } else if (ch[0] == '\\' && ch[1] == '0') {
+                                        n = strtol(ch+1, 0, 0);        /* \0octal or \0xhex */
+                                        chtemp[n].num = n;
+#ifdef UNICODE
+                                } else if (mbtowc(&wc, ch, strlen(ch)) > 1) {
+                                        chtemp[nw].num = chadd(ch,  MBchar, Install);
+                                        n = nw;
+                                        nw++;
+#endif        /*UNICODE*/
+                                } else {
+                                        if (strcmp(ch, "---") == 0) { /* no name */
+                                                sprintf(ch, "%d", code);
+                                                type = Number;
+                                        } else
+                                                type = Troffchar;
+                                        chtemp[nw].num = chadd(ch, type, Install);
+                                        n = nw;
+                                        nw++;
+                                }
+                                chtemp[n].wid = wid;
+                                chtemp[n].kern = kern;
+                                chtemp[n].code = code;
+                                /*fprintf(stderr, "font %2.2s char %4.4s num %3d wid %2d code %3d\n",
+                                        ftemp->longname, ch, n, wid, code);
+                                */
+                        }
+                        break;
+                }
+                skipline(fin);
+        }
+        fclose(fin);
+        chtemp[' '].wid = ftemp->spacewidth;        /* width of space on this font */
+        ftemp->nchars = nw;
+        if (ftemp->wp)
+                free(ftemp->wp);        /* god help us if this wasn't allocated */
+        ftemp->wp = (Chwid *) malloc(nw * sizeof(Chwid));
+        if (ftemp->wp == NULL)
+                return -1;
+        for (i = 0; i < nw; i++)
+                ftemp->wp[i] = chtemp[i];
+/*
+ *        printf("%d chars: ", nw);
+ *        for (i = 0; i < nw; i++)
+ *                if (ftemp->wp[i].num > 0 && ftemp->wp[i].num < ALPHABET) {
+ *                        printf("%c %d ", ftemp->wp[i].num, ftemp->wp[i].wid);
+ *                else if (i >= ALPHABET)
+ *                        printf("%d (%s) %d ", ftemp->wp[i].num,
+ *                                chnames[ftemp->wp[i].num-ALPHABET], ftemp->wp[i].wid);
+ *        }
+ *        printf("\n");
+ */
+        return 1;
+}
+
+int
+chadd(char *s, int type, int install)        /* add s to global character name table; */
+{                                        /* or just look it up */
+
+        /* a temporary kludge:  store the "type" as the first character */
+        /* of the string, so we can remember from whence it came */
+
+        char *p;
+        int i;
+
+/* fprintf(stderr, "into chadd %s %c %c\n", s, type, install); /* */
+        for (i = 0; i < nchnames; i++)
+                if (type == chnames[i][0] && eq(s, chnames[i]+1)) /* +1 since type at front */
+                        break;
+/* fprintf(stderr, "i %d, nchnames %d\n", i, nchnames); /* */
+        if (i < nchnames)                /* found same type and bytes at position i */
+                return ALPHABET + i;
+        else if (install == Lookup)        /* not found, and we were just looking */
+                return -1;
+
+        chnames[nchnames] = p = (char *) malloc(strlen(s)+1+1);        /* type + \0 */
+        if (p == NULL) {
+                ERROR "out of space adding character %s", s WARN;
+                return LEFTHAND;
+        }
+        if (nchnames >= NCHARS - ALPHABET) {
+                ERROR "out of table space adding character %s", s WARN;
+                return LEFTHAND;
+        }
+        strcpy(chnames[nchnames]+1, s);
+        chnames[nchnames][0] = type;
+/* fprintf(stderr, "installed %c%s at %d\n", type, s, nchnames); /* */
+        return nchnames++ + ALPHABET;
+}
+
+char *chname(int n)        /* return string for char with index n */
+{                        /* includes type char at front, to be peeled off elsewhere */
+        if (n >= ALPHABET && n < nchnames + ALPHABET)
+                return chnames[n-ALPHABET];
+        else
+                return "";
+}
+
+int
+getlig(FILE *fin)        /* pick up ligature list */
+{
+        int lig;
+        char temp[200];
+
+        lig = 0;
+        while (fscanf(fin, "%s", temp) != EOF && strcmp(temp, "0") != 0) {
+                if (strcmp(temp, "fi") == 0)
+                        lig |= LFI;
+                else if (strcmp(temp, "fl") == 0)
+                        lig |= LFL;
+                else if (strcmp(temp, "ff") == 0)
+                        lig |= LFF;
+                else if (strcmp(temp, "ffi") == 0)
+                        lig |= LFFI;
+                else if (strcmp(temp, "ffl") == 0)
+                        lig |= LFFL;
+                else
+                        fprintf(stderr, "illegal ligature %s ignored\n", temp);
+        }
+        return lig;
+}
diff --git a/troff/t6.c b/troff/t6.c
@@ -0,0 +1,889 @@
+/*
+ * t6.c
+ * 
+ * width functions, sizes and fonts
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int        fontlab[MAXFONTS+1];
+int        cstab[MAXFONTS+1];
+int        ccstab[MAXFONTS+1];
+int        bdtab[MAXFONTS+1];
+int        sbold = 0;
+
+int
+t_width(Tchar j)
+{
+        int i, k;
+
+        if (iszbit(j))
+                return 0;
+        if (ismot(j)) {
+                if (isvmot(j))
+                        return(0);
+                k = absmot(j);
+                if (isnmot(j))
+                        k = -k;
+                return(k);
+        }
+        i = cbits(j);
+        if (i < ' ') {
+                if (i == '\b')
+                        return(-widthp);
+                if (i == PRESC)
+                        i = eschar;
+                else if (i == HX)
+                        return(0);
+        }
+        if (i == ohc)
+                return(0);
+        i = trtab[i];
+        if (i < ' ')
+                return(0);
+        if (sfbits(j) == oldbits) {
+                xfont = pfont;
+                xpts = ppts;
+        } else 
+                xbits(j, 0);
+        if (i < nchnames + ALPHABET && widcache[i].fontpts == (xfont<<8) + xpts && !setwdf)
+                k = widcache[i].width;
+        else {
+                k = getcw(i);
+                if (bd)
+                        k += (bd - 1) * HOR;
+                if (cs)
+                        k = cs;
+        }
+        widthp = k;
+        return(k);
+}
+
+/*
+ * clear width cache-- s means just space
+ */
+void zapwcache(int s)
+{
+        int i;
+
+        if (s) {
+                widcache[' '].fontpts = 0;
+                return;
+        }
+        for (i=0; iwp[n].num == n)        /* ascii at front */
+                        return n;
+                else
+                        return -1;
+        }
+        cp = &fp->wp[ALPHABET];
+        ep = &fp->wp[fp->nchars];
+        for ( ; cp < ep; cp++)        /* search others */
+                if (cp->num == n)
+                        return cp - &fp->wp[0];
+        /* maybe it was a \N... */
+        np = chname(n);
+        if (*np == Number) {
+                i = atoi(np+1);                /* sscanf(np+1, "%d", &i); */
+                cp = &fp->wp[0];
+                ep = &fp->wp[fp->nchars];
+                for ( ; cp < ep; cp++) {        /* search others */
+                        if (cp->code == i)
+                                return cp - &fp->wp[0];
+                }
+                return -2;        /* a \N that doesn't have an entry */
+        }
+        return -1;        /* vanilla not found */
+}
+
+int
+getcw(int i)
+{
+        int k, n, x;
+        Font *fp;
+        int nocache = 0;
+        if (i < ' ')
+                return 0;
+        bd = 0;
+        fp = &fonts[xfont];
+        if (i == ' ') {        /* a blank */
+                k = (fp->spacewidth * spacesz + 6) / 12;
+                /* this nonsense because .ss cmd uses 1/36 em as its units */
+                /*         and default is 12 */
+        } else if ((n = onfont(i, xfont)) >= 0) {        /* on this font at n */
+                k = fp->wp[n].wid;
+                if (setwdf)
+                        numtabp[CT].val |= fp->wp[n].kern;
+        } else if (n == -2) {                /* \N with default width */
+                
+                k = fp->defaultwidth;
+        } else {                        /* not on current font */
+                nocache = 1;
+                k = fp->defaultwidth;        /* default-size space */
+                if (smnt) {
+                        int ii, jj;
+                        for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts + 1) {
+                                if ((n = onfont(i, ii)) >= 0) {
+                                        k = fonts[ii].wp[n].wid;
+                                        if (xfont == sbold)
+                                                bd = bdtab[ii];
+                                        if (setwdf)
+                                                numtabp[CT].val |= fonts[ii].wp[n].kern;
+                                        break;
+                                }
+                        }
+                }
+        }
+        if (!bd)
+                bd = bdtab[xfont];
+        if (cs = cstab[xfont]) {
+                nocache = 1;
+                if (ccs = ccstab[xfont])
+                        x = ccs; 
+                else 
+                        x = xpts;
+                cs = (cs * EMPTS(x)) / 36;
+        }
+        /* was (k & BYTEMASK);  since .wid is unsigned, should never happen */
+        if (k < 0)
+                ERROR "can't happen: negative width %d in getcw %d\n", k, i WARN;
+        k = (k * xpts + (Unitwidth / 2)) / Unitwidth;
+        if (nocache|bd)
+                widcache[i].fontpts = 0;
+        else {
+                widcache[i].fontpts = (xfont<<8) + xpts;
+                widcache[i].width = k;
+        }
+        return(k);
+        /* Unitwidth is Units/Point, where
+        /* Units is the fundamental digitization
+        /* of the character set widths, and
+        /* Point is the number of goobies in a point
+        /* e.g., for cat, Units=36, Point=6, so Unitwidth=36/6=6
+        /* In effect, it's the size at which the widths
+        /* translate directly into units.
+        */
+}
+
+void xbits(Tchar i, int bitf)
+{
+        int k;
+
+        if(TROFF) {
+                xfont = fbits(i);
+                k = sbits(i);
+                if(k) {
+                        xpts = pstab[k-1];
+                        oldbits = sfbits(i);
+                        pfont = xfont;
+                        ppts = xpts;
+                        return;
+                }
+                switch(bitf) {
+                case 0:
+                        xfont = font;
+                        xpts = pts;
+                        break;
+                case 1:
+                        xfont = pfont;
+                        xpts = ppts;
+                        break;
+                case 2:
+                        xfont = mfont;
+                        xpts = mpts;
+                }
+        }
+}
+
+
+/* these next two functions ought to be the same in troff and nroff, */
+/* but the data structures they search are different. */
+/* silly historical problem. */
+
+
+Tchar t_setch(int c)
+{
+        int j;
+        char temp[50];
+        char *s;
+
+        j = 0;
+        s = temp;
+        if (c == '(') {        /* \(xx */
+                if ((*s++ = getach()) == 0 || (*s++ = getach()) == 0)
+                        return(0);
+        } else {        /* \C'...' */
+                c = getach();
+                while ((*s = getach()) != c && *s != 0 && s < temp + sizeof(temp) - 1)
+                        s++;
+        }
+        *s = '\0';
+#ifdef UNICODE
+        return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */
+#else
+        if (NROFF) {
+                j = chadd(temp, Troffchar, Lookup);
+                if ( j == -1)
+                        return 0;
+                else
+                        return j | chbits;
+        } else
+                return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */
+                
+#endif /*UNICODE*/
+}
+
+Tchar t_setabs(void)                /* set absolute char from \N'...' */
+{
+        int n;
+        char temp[10];
+
+        getch();        /* delim */
+        n = 0;
+        n = inumb(&n);
+        getch();        /* delim */
+        if (nonumb)
+                return 0;
+        sprintf(temp, "%d", n);        /* convert into "#n" */
+        n = chadd(temp, Number, Install);
+        return n | chbits;
+}
+
+
+/*
+ * fontlab[] is a cache that contains font information
+ * for each font.
+ * fontlab[] contains the 1- or 2-character name of the
+ * font current associated with that font.
+ * fonts 1..nfonts correspond to the mounted fonts;
+ * the last of these are the special fonts.
+ * If we don't use the (named) font in one of the
+ * standard positions, we install the name in the next
+ * free slot of fontlab[] and font[].
+ * Whenever we need info about the font, we
+ * read in the data into the next free slot with getfont.
+ * The ptfont() (t10.c) routine will tell
+ * the device filter to put the font always at position
+ * zero if xfont > nfonts, so no need to change these filters.
+ * Yes, this is a bit kludgy.
+ *
+ * This gives the new specs of findft:
+ *         find the font name i, where i also can be a number.
+ *         Installs the font(name) i when not present
+ *         returns -1 on error
+ */
+
+int
+t_findft(int i)
+{
+        int k;
+        Uchar *p;
+
+        p = unpair(i);
+
+        if (isdigit(p[0])) {                /* first look for numbers */
+                k = p[0] - '0';
+                if (p[1] > 0 && isdigit(p[1]))
+                        k = 10 * k + p[1] - '0';
+                if (k > 0 && k <= nfonts && k < smnt)
+                        return(k);        /* mounted font:  .ft 3 */
+                if (fontlab[k] && k <= MAXFONTS) {        /* translate */
+                        return(k);                        /*number to a name */
+                } else {
+                        fprintf(stderr, "troff: no font at position %d\n", k);
+                        return(-1);        /* wild number */
+                }
+        }
+
+        /*
+         * Now we look for font names
+         */
+        for (k = 1; fontlab[k] != i; k++) {
+                if (k > MAXFONTS)
+                        return(-1);        /* running out of fontlab space */
+                if (fontlab[k] == 0) {        /* passed all existing names */
+                        if (setfp(k, i, (char *) 0, 1) == -1)
+                                return(-1);
+                        else {
+                                fontlab[k] = i;        /* install the name */
+                                return(k);
+                        }
+                }
+        }
+        return(k);                        /* was one of the existing names */
+}
+
+
+void caseps(void)
+{
+        int i;
+
+        if (TROFF) {
+                if(skip())
+                        i = apts1;
+                else {
+                        noscale++;
+                        i = inumb(&apts);        /* this is a disaster for fractional point sizes */
+                        noscale = 0;
+                        if(nonumb)
+                                i = apts1;
+                }
+                casps1(i);
+        }
+}
+
+
+void casps1(int i)
+{
+
+/*
+ * in olden times, it used to ignore changes to 0 or negative.
+ * this is meant to allow the requested size to be anything,
+ * in particular so eqn can generate lots of \s-3's and still
+ * get back by matching \s+3's.
+
+        if (i <= 0)
+                return;
+*/
+        apts1 = apts;
+        apts = i;
+        pts1 = pts;
+        pts = findps(i);
+        mchbits();
+}
+
+int
+findps(int i)
+{
+        int j, k;
+
+        for (j=k=0 ; pstab[j] != 0 ; j++)
+                if (abs(pstab[j]-i) < abs(pstab[k]-i))
+                        k = j;
+
+        return(pstab[k]);
+}
+
+
+void t_mchbits(void)
+{
+        int i, j, k;
+
+        i = pts;
+        for (j = 0; i > (k = pstab[j]); j++)
+                if (!k) {
+                        j--;
+                        break;
+                }
+        chbits = 0;
+        setsbits(chbits, ++j);
+        setfbits(chbits, font);
+        sps = width(' ' | chbits);
+        zapwcache(1);
+}
+
+void t_setps(void)
+{
+        int i, j;
+
+        j = 0;
+        i = cbits(getch());
+        if (isdigit(i)) {                /* \sd or \sdd */
+                i -= '0';
+                if (i == 0)                /* \s0 */
+                        j = apts1;
+                else if (i <= 3 && (ch=getch()) && isdigit(j = cbits(ch))) {        /* \sdd */
+                        j = 10 * i + j - '0';
+                        ch = 0;
+                } else                /* \sd */
+                        j = i;
+        } else if (i == '(') {                /* \s(dd */
+                j = cbits(getch()) - '0';
+                j = 10 * j + cbits(getch()) - '0';
+                if (j == 0)                /* \s(00 */
+                        j = apts1;
+        } else if (i == '+' || i == '-') {        /* \s+, \s- */
+                j = cbits(getch());
+                if (isdigit(j)) {                /* \s+d, \s-d */
+                        j -= '0';
+                } else if (j == '(') {                /* \s+(dd, \s-(dd */
+                        j = cbits(getch()) - '0';
+                        j = 10 * j + cbits(getch()) - '0';
+                }
+                if (i == '-')
+                        j = -j;
+                j += apts;
+        }
+        casps1(j);
+}
+
+
+Tchar t_setht(void)                /* set character height from \H'...' */
+{
+        int n;
+        Tchar c;
+
+        getch();
+        n = inumb(&apts);
+        getch();
+        if (n == 0 || nonumb)
+                n = apts;        /* does this work? */
+        c = CHARHT;
+        c |= ZBIT;
+        setsbits(c, n);
+        setfbits(c, pts);        /* sneaky, CHARHT font bits are size bits */
+        return(c);
+}
+
+Tchar t_setslant(void)                /* set slant from \S'...' */
+{
+        int n;
+        Tchar c;
+
+        getch();
+        n = 0;
+        n = inumb(&n);
+        getch();
+        if (nonumb)
+                n = 0;
+        c = SLANT;
+        c |= ZBIT;
+        setsfbits(c, n+180);
+        return(c);
+}
+
+
+void caseft(void)
+{
+        if (!TROFF) {
+                n_caseft();
+                return;
+        }
+        skip();
+        setfont(1);
+}
+
+
+void t_setfont(int a)
+{
+        int i, j;
+
+        if (a)
+                i = getrq();
+        else 
+                i = getsn();
+        if (!i || i == 'P') {
+                j = font1;
+                goto s0;
+        }
+        if (/* i == 'S' || */ i == '0')        /* an experiment -- why can't we change to it? */
+                return;
+        if ((j = findft(i)) == -1)
+                if ((j = setfp(0, i, (char*) 0, 1)) == -1)        /* try to put it in position 0 */
+                        return;
+s0:
+        font1 = font;
+        font = j;
+        mchbits();
+}
+
+
+void t_setwd(void)
+{
+        int base, wid;
+        Tchar i;
+        int delim, emsz, k;
+        int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+
+        base = numtabp[ST].val = numtabp[SB].val = wid = numtabp[CT].val = 0;
+        if (ismot(i = getch()))
+                return;
+        delim = cbits(i);
+        savhp = numtabp[HP].val;
+        numtabp[HP].val = 0;
+        savapts = apts;
+        savapts1 = apts1;
+        savfont = font;
+        savfont1 = font1;
+        savpts = pts;
+        savpts1 = pts1;
+        setwdf++;
+        while (cbits(i = getch()) != delim && !nlflg) {
+                k = width(i);
+                wid += k;
+                numtabp[HP].val += k;
+                if (!ismot(i)) {
+                        emsz = (INCH/72) * xpts;
+                } else if (isvmot(i)) {
+                        k = absmot(i);
+                        if (isnmot(i))
+                                k = -k;
+                        base -= k;
+                        emsz = 0;
+                } else 
+                        continue;
+                if (base < numtabp[SB].val)
+                        numtabp[SB].val = base;
+                if ((k = base + emsz) > numtabp[ST].val)
+                        numtabp[ST].val = k;
+        }
+        setn1(wid, 0, (Tchar) 0);
+        numtabp[HP].val = savhp;
+        apts = savapts;
+        apts1 = savapts1;
+        font = savfont;
+        font1 = savfont1;
+        pts = savpts;
+        pts1 = savpts1;
+        mchbits();
+        setwdf = 0;
+}
+
+
+Tchar t_vmot(void)
+{
+        dfact = lss;
+        vflag++;
+        return t_mot();
+}
+
+
+Tchar t_hmot(void)
+{
+        dfact = EM;
+        return t_mot();
+}
+
+
+Tchar t_mot(void)
+{
+        int j, n;
+        Tchar i;
+
+        j = HOR;
+        getch(); /*eat delim*/
+        if (n = atoi0()) {
+                if (vflag)
+                        j = VERT;
+                i = makem(quant(n, j));
+        } else
+                i = 0;
+        getch();
+        vflag = 0;
+        dfact = 1;
+        return(i);
+}
+
+
+Tchar t_sethl(int k)
+{
+        int j;
+        Tchar i;
+
+        j = EM / 2;
+        if (k == 'u')
+                j = -j;
+        else if (k == 'r')
+                j = -2 * j;
+        vflag++;
+        i = makem(j);
+        vflag = 0;
+        return(i);
+}
+
+
+Tchar t_makem(int i)
+{
+        Tchar j;
+
+        if (i >= 0)
+                j = i;
+        else
+                j = -i;
+        if (Hor > 1 && !vflag)
+                j = (j + Hor/2)/Hor * Hor;
+        j |= MOT;
+        if (i < 0)
+                j |= NMOT;
+        if (vflag)
+                j |= VMOT;
+        return(j);
+}
+
+
+Tchar getlg(Tchar i)
+{
+        Tchar j, k;
+        int lf;
+
+        if (!TROFF)
+                return i;
+        if ((lf = fonts[fbits(i)].ligfont) == 0) /* font lacks ligatures */
+                return(i);
+        j = getch0();
+        if (cbits(j) == 'i' && (lf & LFI))
+                j = LIG_FI;
+        else if (cbits(j) == 'l' && (lf & LFL))
+                j = LIG_FL;
+        else if (cbits(j) == 'f' && (lf & LFF)) {
+                if ((lf & (LFFI|LFFL)) && lg != 2) {
+                        k = getch0();
+                        if (cbits(k)=='i' && (lf&LFFI))
+                                j = LIG_FFI;
+                        else if (cbits(k)=='l' && (lf&LFFL))
+                                j = LIG_FFL;
+                        else {
+                                *pbp++ = k;
+                                j = LIG_FF;
+                        }
+                } else 
+                        j = LIG_FF;
+        } else {
+                *pbp++ = j;
+                j = i;
+        }
+        return(i & SFMASK | j);
+}
+
+
+void caselg(void)
+{
+
+        if(TROFF) {
+                skip();
+                lg = atoi0();
+                if (nonumb)
+                        lg = 1;
+        }
+}
+
+void casefp(void)
+{
+        int i, j;
+
+        if (!TROFF) {
+                n_casefp();
+                return;
+        }
+        skip();
+        i = cbits(getch());
+        if (isdigit(i)) {
+                i -= '0';
+                j = cbits(getch());
+                if (isdigit(j))
+                        i = 10 * i + j - '0';
+        }
+        if (i <= 0 || i > nfonts)
+                ERROR "fp: bad font position %d", i WARN;
+        else if (skip() || !(j = getrq()))
+                ERROR "fp: no font name" WARN; 
+        else if (skip() || !getname())
+                setfp(i, j, (char*) 0, 1);
+        else                /* 3rd argument = filename */
+                setfp(i, j, nextf, 1);
+}
+
+char *strdupl(const char *s)        /* make a copy of s */
+{
+        char *t;
+
+        t = (char *) malloc(strlen(s) + 1);
+        if (t == NULL)
+                ERROR "out of space in strdupl(%s)", s FATAL;
+        strcpy(t, s);
+        return t;
+}
+
+int
+setfp(int pos, int f, char *truename, int print)        /* mount font f at position pos[0...nfonts] */
+{
+        char pathname[NS], shortname[NS], *sl;
+
+        sl = (char*)0;
+        zapwcache(0);
+        if (truename)
+                strcpy(shortname, truename);
+        else
+                strcpy(shortname, (char *) unpair(f));
+        if (truename && strrchr(truename, '/')) {        /* .fp 1 R dir/file: use verbatim */
+                sprintf(pathname, "%s", truename);
+                if (fonts[pos].truename)
+                        free(fonts[pos].truename);
+                fonts[pos].truename = strdupl(truename);
+        } else if (truename) {                        /* synonym: .fp 1 R Avant */
+                sprintf(pathname, "%s/dev%s/%s", fontdir, devname, truename);
+                truename = 0;        /* so doesn't get repeated by ptfpcmd */
+        } else                                        /* vanilla: .fp 5 XX */
+                sprintf(pathname, "%s/dev%s/%s", fontdir, devname, shortname);
+        if (truename == 0 && fonts[pos].truename != 0) {
+                free(fonts[pos].truename);
+                fonts[pos].truename = 0;
+        }
+        if (getfont(pathname, pos) < 0) {
+                ERROR "Can't open font file %s", pathname WARN;
+                return -1;
+        }
+        if (print && !ascii) {
+                ptfpcmd(pos, fonts[pos].longname, truename);
+                ptfont();
+        }
+        if (pos == smnt) {
+                smnt = 0; 
+                sbold = 0; 
+        }
+        fontlab[pos] = f;
+        if (smnt == 0 && fonts[pos].specfont)
+                smnt = pos;
+        bdtab[pos] = cstab[pos] = ccstab[pos] = 0;
+        return pos;
+}
+
+/*
+ * .cs request; don't check legality of optional arguments
+ */
+void casecs(void)
+{
+        int i, j;
+
+        if (TROFF) {
+                int savtr = trace;
+
+                trace = 0;
+                noscale++;
+                skip();
+                if (!(i = getrq()) || (i = findft(i)) < 0)
+                        goto rtn;
+                skip();
+                cstab[i] = atoi0();
+                skip();
+                j = atoi0();
+                if(nonumb)
+                        ccstab[i] = 0;
+                else
+                        ccstab[i] = findps(j);
+        rtn:
+                zapwcache(0);
+                noscale = 0;
+                trace = savtr;
+        }
+}
+
+
+void casebd(void)
+{
+        int i, j, k;
+
+        j=0;
+        if (!TROFF) {
+                n_casebd();
+                return;
+        }
+        zapwcache(0);
+        k = 0;
+bd0:
+        if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
+                if (k)
+                        goto bd1;
+                else 
+                        return;
+        }
+        if (j == smnt) {
+                k = smnt;
+                goto bd0;
+        }
+        if (k) {
+                sbold = j;
+                j = k;
+        }
+bd1:
+        skip();
+        noscale++;
+        bdtab[j] = atoi0();
+        noscale = 0;
+}
+
+
+void casevs(void)
+{
+        int i;
+
+        if (!TROFF) {
+                n_casevs();
+                return;
+        }
+        skip();
+        vflag++;
+        dfact = INCH; /* default scaling is points! */
+        dfactd = 72;
+        res = VERT;
+        i = inumb(&lss);
+        if (nonumb)
+                i = lss1;
+        if (i < VERT) 
+                i = VERT;
+        lss1 = lss;
+        lss = i;
+}
+
+
+void casess(void)
+{
+        int i;
+
+        if(TROFF) {
+                noscale++;
+                skip();
+                if(i = atoi0()) {
+                        spacesz = i & 0177;
+                        zapwcache(0);
+                        sps = width(' ' | chbits);
+                }
+                noscale = 0;
+        }
+}
+
+
+Tchar t_xlss(void)
+{
+        /* stores \x'...' into two successive Tchars.
+        /* the first contains HX, the second the value,
+        /* encoded as a vertical motion.
+        /* decoding is done in n2.c by pchar().
+        */
+        int i;
+
+        getch();
+        dfact = lss;
+        i = quant(atoi0(), VERT);
+        dfact = 1;
+        getch();
+        if (i >= 0)
+                *pbp++ = MOT | VMOT | i;
+        else
+                *pbp++ = MOT | VMOT | NMOT | -i;
+        return(HX);
+}
+
+Uchar *unpair(int i)
+{
+        static Uchar name[3];
+
+        name[0] = i & SHORTMASK;
+        name[1] = (i >> SHORT) & SHORTMASK;
+        name[2] = 0;
+        return name;
+}
diff --git a/troff/tdef.h b/troff/tdef.h
@@ -0,0 +1,673 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#undef MB_CUR_MAX
+#define MB_CUR_MAX 3
+
+#define        NROFF        (!TROFF)
+
+/* Site dependent definitions */
+
+#ifndef TMACDIR
+#define TMACDIR                "lib/tmac/tmac."
+#endif
+#ifndef FONTDIR
+#define FONTDIR                "lib/font"
+#endif
+#ifndef NTERMDIR
+#define NTERMDIR        "lib/term/tab."
+#endif
+#ifndef TDEVNAME
+#define TDEVNAME        "post"
+#endif
+#ifndef NDEVNAME
+#define NDEVNAME        "37"
+#endif
+#ifndef TEXHYPHENS
+#define        TEXHYPHENS        "/usr/lib/tex/macros/hyphen.tex"
+#endif
+#ifndef ALTHYPHENS
+#define        ALTHYPHENS        "lib/tmac/hyphen.tex"        /* another place to look */
+#endif
+
+typedef        unsigned char        Uchar;
+typedef unsigned short        Ushort;
+
+typedef        /*unsigned*/ long        Tchar;
+
+typedef        struct        Blockp        Blockp;
+typedef        struct        Diver        Diver;
+typedef        struct        Stack        Stack;
+typedef        struct        Divsiz        Divsiz;
+typedef        struct        Contab        Contab;
+typedef        struct        Numtab        Numtab;
+typedef        struct        Numerr        Numerr;
+typedef        struct        Env        Env;
+typedef        struct        Term        Term;
+typedef struct        Chwid        Chwid;
+typedef struct        Font        Font;
+typedef        struct        Spnames        Spnames;
+typedef        struct        Wcache        Wcache;
+typedef        struct        Tbuf        Tbuf;
+
+/* this simulates printf into a buffer that gets flushed sporadically */
+/* the BSD goo is because SunOS sprintf doesn't return anything useful */
+
+#ifdef BSD4_2
+#define        OUT                (obufp += strlen(sprintf(obufp,
+#define        PUT                ))) > obuf+BUFSIZ ? flusho() : 1
+#else
+#define        OUT                (obufp += sprintf(obufp,
+#define        PUT                )) > obuf+BUFSIZ ? flusho() : 1
+#endif
+
+#define        oputs(a)        OUT "%s", a PUT
+#define        oput(c)                ( *obufp++ = (c), obufp > obuf+BUFSIZ ? flusho() : 1 )
+
+extern        char        errbuf[];
+#define        ERROR        sprintf(errbuf,
+#define        WARN        ), errprint()
+#define        FATAL        ), errprint(), exit(1)
+
+/* starting values for typesetting parameters: */
+
+#define        PS        10        /* default point size */
+#define        FT        1        /* default font position */
+#define ULFONT        2        /* default underline font */
+#define        BDFONT        3        /* default emboldening font */
+#define        BIFONT        4        /* default bold italic font */
+#define        LL        (unsigned) 65*INCH/10        /* line length; 39picas=6.5in */
+#define        VS        ((12*INCH)/72)        /* initial vert space */
+
+
+#define        EMPTS(pts)        (((long)Inch*(pts) + 36) / 72)
+#define        EM        (TROFF? EMPTS(pts): t.Em)
+#define        INCH        (TROFF? Inch: 240)
+#define        HOR        (TROFF? Hor: t.Adj)
+#define        VERT        (TROFF? Vert: t.Vert)
+#define        PO        (TROFF? Inch: 0)
+#define        SPS        (TROFF? EMPTS(pts)/3: INCH/10)
+#define        SS        (TROFF? 12: INCH/10)
+#define        ICS        (TROFF? EMPTS(pts): 2*INCH/10)
+#define        DTAB        (TROFF? (INCH/2): 0)
+
+/* These "characters" are used to encode various internal functions
+/* Some make use of the fact that most ascii characters between
+/* 0 and 040 don't have any graphic or other function.
+/* The few that do have a purpose (e.g., \n, \b, \t, ...
+/* are avoided by the ad hoc choices here.
+/* See ifilt[] in n1.c for others -- 1, 2, 3, 5, 6, 7, 010, 011, 012 
+*/
+
+#define        LEADER        001
+#define        IMP        004        /* impossible char; glues things together */
+#define        TAB        011
+#define        RPT        014        /* next character is to be repeated many times */
+#define        CHARHT        015        /* size field sets character height */
+#define        SLANT        016        /* size field sets amount of slant */
+#define        DRAWFCN        017        /* next several chars describe arb drawing fcns */
+#        define        DRAWLINE        'l'        /* line: 'l' dx dy char */
+#        define        DRAWCIRCLE        'c'        /* circle: 'c' r */
+#        define        DRAWELLIPSE        'e'        /* ellipse: 'e' rx ry */
+#        define        DRAWARC                'a'        /* arc: 'a' dx dy dx dy */
+#        define        DRAWSPLINE        '~'        /* quadratic B spline: '~' dx dy dx dy ... */
+                                        /* other splines go thru too */
+/* NOTE:  the use of ~ is a botch since it's often used in .tr commands */
+/* better to use a letter like s, but change it in the postprocessors too */
+/* for now, this is taken care of in n9.c and t10.c */
+#        define        DRAWBUILD        'b'        /* built-up character (e.g., { */
+
+#define        LEFT        020        /* \{ */
+#define        RIGHT        021        /* \} */
+#define        FILLER        022        /* \& and similar purposes */
+#define        XON        023        /* \X'...' starts here */
+#define        OHC        024        /* optional hyphenation character \% */
+#define        CONT        025        /* \c character */
+#define        PRESC        026        /* printable escape */
+#define        UNPAD        027        /* unpaddable blank */
+#define        XPAR        030        /* transparent mode indicator */
+#define        FLSS        031        /* next Tchar contains vertical space */
+                        /* used when recalling diverted text */
+#define        WORDSP        032        /* paddable word space */
+#define        ESC        033        /* current escape character */
+#define        XOFF        034        /* \X'...' ends here */
+                        /* matches XON, but they will probably never nest */
+                        /* so could drop this when another control is needed */
+#define        HX        035        /* next character is value of \x'...' */
+#define MOTCH        036        /* this "character" is really motion; used by cbits() */
+
+#define        HYPHEN        c_hyphen
+#define        EMDASH        c_emdash        /* \(em */
+#define        RULE        c_rule                /* \(ru */
+#define        MINUS        c_minus                /* minus sign on current font */
+#define        LIG_FI        c_fi                /* \(ff */
+#define        LIG_FL        c_fl                /* \(fl */
+#define        LIG_FF        c_ff                /* \(ff */
+#define        LIG_FFI        c_ffi                /* \(Fi */
+#define        LIG_FFL        c_ffl                /* \(Fl */
+#define        ACUTE        c_acute                /* acute accent \(aa */
+#define        GRAVE        c_grave                /* grave accent \(ga */
+#define        UNDERLINE c_under        /* \(ul */
+#define        ROOTEN        c_rooten        /* root en \(rn */
+#define        BOXRULE        c_boxrule        /* box rule \(br */
+#define        LEFTHAND c_lefthand        /* left hand for word overflow */
+#define        DAGGER         c_dagger        /* dagger for end of sentence/footnote */
+
+#define        HYPHALG        1        /* hyphenation algorithm: 0=>good old troff, 1=>tex */
+
+
+/* array sizes, and similar limits: */
+
+#define MAXFONTS 99        /* Maximum number of fonts in fontab */
+#define        NM        91        /* requests + macros */
+#define        NN        NNAMES        /* number registers */
+#define        NNAMES        15        /* predefined reg names */
+#define        NIF        15        /* if-else nesting */
+#define        NS        128        /* name buffer */
+#define        NTM        1024        /* tm buffer */
+#define        NEV        3        /* environments */
+#define        EVLSZ        10        /* size of ev stack */
+
+#define        STACKSIZE (12*1024)        /* stack for macros and strings in progress */
+#define        NHYP        10        /* max hyphens per word */
+#define        NHEX        512        /* byte size of exception word list */
+#define        NTAB        100        /* tab stops */
+#define        NSO        5        /* "so" depth */
+#define        NMF        5        /* number of -m flags */
+#define        WDSIZE        500        /* word buffer click size */
+#define        LNSIZE        4000        /* line buffer click size */
+#define        OLNSIZE        5000        /* output line buffer click; bigger for 'w', etc. */
+#define        NDI        5        /* number of diversions */
+
+#define        ALPHABET alphabet        /* number of characters in basic alphabet. */
+                        /* 128 for parochial USA 7-bit ascii, */
+                        /* 256 for "European" mode with e.g., Latin-1 */
+
+        /* NCHARS must be greater than 
+                ALPHABET (ascii stuff) + total number of distinct char names
+                from all fonts that will be run in this job (including
+                unnamed ones and \N's)
+        */
+
+#define        NCHARS        (8*1024)        /* maximum size of troff character set*/
+
+
+        /* However for nroff you want only :
+        1. number of special codes in charset of DESC, which ends up being the
+                value of nchtab and which must be less than 512.
+        2. ALPHABET, which apparently is the size of the portion of the tables reserved
+                for special control symbols
+        Apparently the max N of \N is irrelevant; */
+        /* to allow \N of up to 254 with up to 338 special characters
+                you need NCHARS of 338 + ALPHABET = 466 */
+
+#define        NROFFCHARS        1024        /* maximum size of nroff character set */
+
+#define        NTRTAB                NCHARS        /* number of items in trtab[] */
+#define NWIDCACHE        NCHARS        /* number of items in widcache[] */
+
+#define        NTRAP        20        /* number of traps */
+#define        NPN        20        /* numbers in "-o" */
+#define        FBUFSZ        512        /* field buf size words */
+#define        IBUFSZ        4096        /* bytes */
+#define        NC        1024        /* cbuf size words */
+#define        NOV        10        /* number of overstrike chars */
+#define        NPP        10        /* pads per field */
+
+/*
+        Internal character representation:
+        Internally, every character is carried around as
+        a 32 bit cookie, called a "Tchar" (typedef long).
+        Bits are numbered 31..0 from left to right.
+        If bit 15 is 1, the character is motion, with
+                if bit 16 it's vertical motion
+                if bit 17 it's negative motion
+        If bit 15 is 0, the character is a real character.
+                if bit 31        zero motion
+                bits 30..24        size
+                bits 23..16        font
+*/
+
+/* in the following, "L" should really be a Tchar, but ... */
+/* numerology leaves room for 16 bit chars */
+
+#define        MOT        (01uL << 16)        /* motion character indicator */
+#define        VMOT        (01uL << 30)        /* vertical motion bit */
+#define        NMOT        (01uL << 29)        /* negative motion indicator */
+/* #define        MOTV        (MOT|VMOT|NMOT)        /* motion flags */
+/* #define        MAXMOT        (~MOTV)                /* maximum motion permitted */
+#define        MAXMOT        0xFFFF
+
+#define        ismot(n)        ((n) & MOT)
+#define        isvmot(n)        (((n) & (MOT|VMOT)) == (MOT|VMOT))        /* must have tested MOT previously */
+#define        isnmot(n)        (((n) & (MOT|NMOT)) == (MOT|NMOT))        /* ditto */
+#define        absmot(n)        ((n) & 0xFFFF)
+
+#define        ZBIT        (01uL << 31)        /* zero width char */
+#define        iszbit(n)        ((n) &  ZBIT)
+
+#define        FSHIFT        17
+#define        SSHIFT        (FSHIFT+7)
+#define        SMASK                (0177uL << SSHIFT)        /* 128 distinct sizes */
+#define        FMASK                (0177uL << FSHIFT)        /* 128 distinct fonts */
+#define        SFMASK                (SMASK|FMASK)        /* size and font in a Tchar */
+#define        sbits(n)        (((n) >> SSHIFT) & 0177)
+#define        fbits(n)        (((n) >> FSHIFT) & 0177)
+#define        sfbits(n)        (((n) & SFMASK) >> FSHIFT)
+#define        cbits(n)        ((n) & 0x1FFFF)                /* isolate character bits,  */
+                                                /* but don't include motions */
+extern        int        realcbits(Tchar);
+
+#define        setsbits(n,s)        n = (n & ~SMASK) | (Tchar)(s) << SSHIFT
+#define        setfbits(n,f)        n = (n & ~FMASK) | (Tchar)(f) << FSHIFT
+#define        setsfbits(n,sf)        n = (n & ~SFMASK) | (Tchar)(sf) << FSHIFT
+#define        setcbits(n,c)        n = (n & ~0xFFFFuL | (c))        /* set character bits */
+
+#define        BYTEMASK 0377
+#define        BYTE         8
+
+#define        SHORTMASK 0XFFFF
+#define        SHORT         16
+
+#define        TABMASK         ((unsigned) INT_MAX >> 1)
+#define        RTAB        ((TABMASK << 1) & ~TABMASK)
+#define        CTAB        (RTAB << 1)
+
+#define        TABBIT        02                /* bits in gchtab */
+#define        LDRBIT        04
+#define        FCBIT        010
+
+#define        PAIR(A,B)        (A|(B<_ics
+#define        sps        envp->_sps
+#define        spacesz        envp->_spacesz
+#define        lss        envp->_lss
+#define        lss1        envp->_lss1
+#define        ll        envp->_ll
+#define        ll1        envp->_ll1
+#define        lt        envp->_lt
+#define        lt1        envp->_lt1
+#define        ic        envp->_ic
+#define        icf        envp->_icf
+#define        chbits        envp->_chbits
+#define        spbits        envp->_spbits
+#define        nmbits        envp->_nmbits
+#define        apts        envp->_apts
+#define        apts1        envp->_apts1
+#define        pts        envp->_pts
+#define        pts1        envp->_pts1
+#define        font        envp->_font
+#define        font1        envp->_font1
+#define        ls        envp->_ls
+#define        ls1        envp->_ls1
+#define        ad        envp->_ad
+#define        nms        envp->_nms
+#define        ndf        envp->_ndf
+#define        nmwid        envp->_nmwid
+#define        fi        envp->_fi
+#define        cc        envp->_cc
+#define        c2        envp->_c2
+#define        ohc        envp->_ohc
+#define        tdelim        envp->_tdelim
+#define        hyf        envp->_hyf
+#define        hyoff        envp->_hyoff
+#define        hyphalg        envp->_hyphalg
+#define        un1        envp->_un1
+#define        tabc        envp->_tabc
+#define        dotc        envp->_dotc
+#define        adsp        envp->_adsp
+#define        adrem        envp->_adrem
+#define        lastl        envp->_lastl
+#define        nel        envp->_nel
+#define        admod        envp->_admod
+#define        wordp        envp->_wordp
+#define        spflg        envp->_spflg
+#define        linep        envp->_linep
+#define        wdend        envp->_wdend
+#define        wdstart        envp->_wdstart
+#define        wne        envp->_wne
+#define        ne        envp->_ne
+#define        nc        envp->_nc
+#define        nb        envp->_nb
+#define        lnmod        envp->_lnmod
+#define        nwd        envp->_nwd
+#define        nn        envp->_nn
+#define        ni        envp->_ni
+#define        ul        envp->_ul
+#define        cu        envp->_cu
+#define        ce        envp->_ce
+#define        in        envp->_in
+#define        in1        envp->_in1
+#define        un        envp->_un
+#define        wch        envp->_wch
+#define        pendt        envp->_pendt
+#define        pendw        envp->_pendw
+#define        pendnf        envp->_pendnf
+#define        spread        envp->_spread
+#define        it        envp->_it
+#define        itmac        envp->_itmac
+#define        hyptr        envp->_hyptr
+#define        tabtab        envp->_tabtab
+#define        line        envp->_line._bufp
+#define        lnsize        envp->_line._size
+#define        word        envp->_word._bufp
+#define wdsize        envp->_word._size
+
+#define oline        _oline._bufp
+#define olnsize        _oline._size
+
+/*
+ * Note:
+ * If this structure changes in ni.c, you must change
+ * this as well, and vice versa.
+ */
+
+struct Env {
+        int        _ics;
+        int        _sps;
+        int        _spacesz;
+        int        _lss;
+        int        _lss1;
+        int        _ll;
+        int        _ll1;
+        int        _lt;
+        int        _lt1;
+        Tchar        _ic;
+        int        _icf;
+        Tchar        _chbits;
+        Tchar        _spbits;
+        Tchar        _nmbits;
+        int        _apts;
+        int        _apts1;
+        int        _pts;
+        int        _pts1;
+        int        _font;
+        int        _font1;
+        int        _ls;
+        int        _ls1;
+        int        _ad;
+        int        _nms;
+        int        _ndf;
+        int        _nmwid;
+        int        _fi;
+        int        _cc;
+        int        _c2;
+        int        _ohc;
+        int        _tdelim;
+        int        _hyf;
+        int        _hyoff;
+        int        _hyphalg;
+        int        _un1;
+        int        _tabc;
+        int        _dotc;
+        int        _adsp;
+        int        _adrem;
+        int        _lastl;
+        int        _nel;
+        int        _admod;
+        Tchar        *_wordp;
+        int        _spflg;
+        Tchar        *_linep;
+        Tchar        *_wdend;
+        Tchar        *_wdstart;
+        int        _wne;
+        int        _ne;
+        int        _nc;
+        int        _nb;
+        int        _lnmod;
+        int        _nwd;
+        int        _nn;
+        int        _ni;
+        int        _ul;
+        int        _cu;
+        int        _ce;
+        int        _in;
+        int        _in1;
+        int        _un;
+        int        _wch;
+        int        _pendt;
+        Tchar        *_pendw;
+        int        _pendnf;
+        int        _spread;
+        int        _it;
+        int        _itmac;
+        Tchar        *_hyptr[NHYP];
+        long        _tabtab[NTAB];
+        Tbuf        _line;
+        Tbuf        _word;
+};
+
+extern        Env        env[];
+extern        Env        *envp;
+
+enum {        MBchar = 'U', Troffchar = 'C', Number = 'N', Install = 'i', Lookup = 'l' };
+        /* U => utf, for instance;  C => \(xx, N => \N'...' */
+
+
+
+struct Chwid {        /* data on one character */
+        Ushort        num;                /* character number:
+                                        0 -> not on this font
+                                        >= ALPHABET -> its number among all Cxy's */
+        Ushort        code;                /* char code for actual device.  used for \N */
+        char        *str;                /* code string for nroff */
+        Uchar        wid;                /* width */
+        Uchar        kern;                /* ascender/descender */
+};
+
+struct Font {        /* characteristics of a font */
+        int        name;                /* int name, e.g., BI (2 chars) */
+        char        longname[64];        /* long name of this font (e.g., "Bembo" */
+        char        *truename;        /* path name of table if not in standard place */
+        int        nchars;                /* number of width entries for this font */
+        char        specfont;        /* 1 == special font */
+        int        spacewidth;        /* width of space on this font */
+        int        defaultwidth;        /* default width of characters on this font */
+        Chwid        *wp;                /* widths, etc., of the real characters */
+        char        ligfont;        /* 1 == ligatures exist on this font */
+};
+
+/* ligatures, ORed into ligfont */
+
+#define        LFF        01
+#define        LFI        02
+#define        LFL        04
+#define        LFFI        010
+#define        LFFL        020
+
+/* tracing modes */
+#define TRNARGS        01                /* trace legality of numeric arguments */
+#define TRREQ        02                /* trace requests */
+#define TRMAC        04                /* trace macros */
+#define RQERR        01                /* processing request/macro */
+
+/* typewriter driving table structure */
+
+
+extern        Term        t;
+struct Term {
+        int        bset;                /* these bits have to be on */
+        int        breset;                /* these bits have to be off */
+        int        Hor;                /* #units in minimum horiz motion */
+        int        Vert;                /* #units in minimum vert motion */
+        int        Newline;        /* #units in single line space */
+        int        Char;                /* #units in character width */
+        int        Em;                /* ditto */
+        int        Halfline;        /* half line units */
+        int        Adj;                /* minimum units for horizontal adjustment */
+        char        *twinit;        /* initialize terminal */
+        char        *twrest;        /* reinitialize terminal */
+        char        *twnl;                /* terminal sequence for newline */
+        char        *hlr;                /* half-line reverse */
+        char        *hlf;                /* half-line forward */
+        char        *flr;                /* full-line reverse */
+        char        *bdon;                /* turn bold mode on */
+        char        *bdoff;                /* turn bold mode off */
+        char        *iton;                /* turn italic mode on */
+        char        *itoff;                /* turn italic mode off */
+        char        *ploton;        /* turn plot mode on */
+        char        *plotoff;        /* turn plot mode off */
+        char        *up;                /* sequence to move up in plot mode */
+        char        *down;                /* ditto */
+        char        *right;                /* ditto */
+        char        *left;                /* ditto */
+
+        Font        tfont;                /* widths and other info, as in a troff font */
+};
+
+extern        Term        t;
+
+/*
+ * for error reporting; keep track of escapes/requests with numeric arguments
+ */
+struct Numerr {
+        char        type;        /* request or escape? */
+        char        esc;        /* was escape sequence named esc */
+        char        escarg;        /* argument of esc's like \D'l' */
+        unsigned int        req;        /* was request or macro named req */
+};
diff --git a/troff/troff.1 b/troff/troff.1
@@ -0,0 +1,199 @@
+.TH TROFF 1
+.SH NAME
+troff, nroff \- text formatting and typesetting
+.SH SYNOPSIS
+.B troff
+[
+.I option ...
+]
+[
+.I file ...
+]
+.PP
+.B nroff
+[
+.I option ...
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Troff
+formats text in the named
+.I files
+for
+printing on a typesetter.
+.I Nroff
+does the same, but produces output suitable
+for typewriter-like devices.
+.PP
+If no
+.I file
+argument is present, the standard input is read.
+An argument consisting of a single minus
+.RB ( - )
+is taken to be
+a file name corresponding to the standard input.
+The options are:
+.nr xx \w'\fL-m\f2name\ \ '
+.TP \n(xxu
+.BI -o list
+Print pages in the comma-separated
+.I list
+of numbers and ranges.
+A range
+.IB N - M
+means 
+.I N
+through
+.IR M ;
+initial
+.BI - M
+means up to
+.IR M ;
+final
+.IB N -
+means from
+.I N
+to the end.
+.TP
+.BI -n N
+Number first generated page
+.IR N .
+.TP
+.BI -m name
+Process the macro file
+.BI /sys/lib/tmac/tmac. name
+before the input
+.IR files .
+.TP
+.BI -r aN
+Set register
+.I a
+(one character name) to
+.IR N .
+.TP
+.B -i
+Read standard input after the input files are exhausted.
+.TP
+.B -q
+Invoke the simultaneous input-output mode of the
+.B rd
+request.
+.TP
+.B -N
+Produce output suitable for typewriter-like devices.
+.SS Typesetter devices (not \fL-N\fP) only
+.TP \n(xxu
+.B -a
+Send a printable
+textual
+approximation
+of the results to the standard output.
+.TP
+.BI -T dest
+Prepare output for typesetter
+.IR dest :
+.br
+.ns
+.RS
+.TP \w'\fL-TLatin1\ 'u
+.B -Tutf
+(The default.) PostScript printers with
+preprocessing to handle Unicode
+characters encoded in
+.SM UTF
+.PD0
+.TP
+.B -Tpost
+Regular PostScript printers
+.PD0
+.TP
+.B -T202
+Mergenthaler Linotron 202 
+.RE
+.PD
+.TP "\w'\fL-m\f2name 'u"
+.BI -F dir
+Take font information from directory
+.IR dir .
+.SS Typewriter (\fL-N\fP) output only
+.TP \n(xxu
+.BI -s N
+Halt prior to every
+.I N
+pages (default
+.IR N =1)
+to allow paper loading or changing.
+.TP
+.BI -T name
+Prepare output for specified terminal.
+Known
+.I names
+include
+.B utf
+for the normal Plan 9
+.SM UTF
+encoding of the Unicode Standard character set (default),
+.B 37
+for the
+Teletype model 37,
+.B lp
+(`line-printer')
+for any terminal without half-line capability,
+.B 450
+for the \s-1DASI\s+1-450
+(Diablo Hyterm),
+and
+.B think
+(HP ThinkJet).
+.TP
+.B -e
+Produce equally-spaced words in adjusted
+lines, using full terminal resolution.
+.TP
+.B -h
+Use output tabs during horizontal spacing
+to speed output and reduce output character count.
+Tab settings are assumed to be every
+8 nominal character widths.
+.SH FILES
+.TF \*9/troff/term/*
+.TP
+.B /tmp/trtmp*
+temporary file
+.TP
+.B \*9/tmac/tmac.*
+standard macro files
+.TP
+.B \*9/troff/term/*
+terminal driving tables for
+.I nroff
+.TP
+.B \*9/troff/font/*
+font width tables for
+.I troff
+.SH SOURCE
+.B \*9/src/cmd/troff
+.SH "SEE ALSO"
+.IR lpr (1),
+.IR proof (1),
+.IR tr2post (1),
+.IR eqn (1), 
+.IR tbl (1), 
+.IR pic (1), 
+.IR grap (1),
+.IR doctype (1), 
+.IR ms (7),
+.IR image (7),
+.IR tex (1),
+.IR deroff (1)
+.br
+J. F. Ossanna and B. W. Kernighan,
+``Troff User's Manual''
+.br
+B. W. Kernighan,
+``A TROFF Tutorial'',
+.I
+Unix Research System Programmer's Manual,
+Tenth Edition, Volume 2.
diff --git a/troff/unansi b/troff/unansi
@@ -0,0 +1,49 @@
+# The awk program cvt will convert the relatively sterotyped ansi c
+# in this troff distribution into older-style c, by munging function
+# declarations.
+
+# You will also have to edit fns.h, by
+#        sed 's/(.*)/()/g' fns.h >foo; mv foo fns.h
+# check this before doing the move!
+
+# you will also have to make some editing changes in
+# tdef.h in the Contab structure: s/(void)/()/
+# you may have to fix up some function declarations
+# in n4.c, the ones with (*f)(Tchar).
+
+# you will surely also have header files to deal with.
+
+# the most obvious cases are dealt with by the following
+# commands.  make sure you do this stuff on a copy!
+
+# function prototypes in n8.c probably belong in fns.h. readpats(void) must
+# be readpats() before cvt runs.
+
+sed \
+        -e 's/(void)/()/' \
+        -e 's/(Tchar[^)]*);/();/' \
+        -e 's/(char[^)]*);/();/' \
+        -e 's/(int[^)]*);/();/' \
+n8.c >foo
+mv foo n8.c
+
+for i in *.c
+do
+        cvt $i >foo
+        mv foo $i
+done
+
+sed 's/(.*)/()/g' fns.h >foo
+mv foo fns.h
+
+sed -e 's/(void)/()/g' -e '/stdlib/d' tdef.h >foo
+mv foo tdef.h
+
+# Compliers may not approve of void *setbrk() in fns.h and n3.c.
+
+sed 's/^void\*[         ]setbrk/char*        setbrk/' fns.h >foo
+mv foo fns.h
+
+sed 's/^void \*setbrk/char *setbrk/' n3.c >foo
+mv foo n3.c
+