URI: 
       added mk and troff to 9base (unfinished yet, DO NOT USE) - 9base - revived minimalist port of Plan 9 userland to Unix
  HTML git clone git://git.suckless.org/9base
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit a08a2a9000cf4cb01ac165f410dbe99a64191edd
   DIR parent c0a69251c8988bdbabf4f3d3e20f40e363990c6c
  HTML Author: Anselm R Garbe <anselm@garbe.us>
       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(-)
       ---
   DIR 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:
   DIR 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
   DIR 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
   DIR 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.
   DIR 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 <rsc@post.harvard.edu>
       +
   DIR 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);
       +}
   DIR diff --git a/mk/archive.c b/mk/archive.c
       @@ -0,0 +1,253 @@
       +#include        "mk.h"
       +#define        ARMAG        "!<arch>\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;
       +}
   DIR 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;
       +}
   DIR 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&REGEXP)
       +                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&REGEXP) && j->match[i])
       +                        envupd(*p, newword(j->match[i]));
       +                else 
       +                        envupd(*p, newword(""));
       +        }
       +        return envy;
       +}
   DIR 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;
       +                }
       +        }
       +}
   DIR 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);
   DIR 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&REGEXP){
       +                        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&REGEXP)
       +                                        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;
       +}
   DIR 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;
       +        }
       +}
   DIR 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;
       +        }
       +}
   DIR 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();
       +}
   DIR 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;
       +}
   DIR 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
       +</$objtype/mkfile
       +
       +prog:        a.$O b.$O c.$O
       +        $LD $LDFLAGS -o $target $prereq
       +
       +%.$O:        %.c
       +        $CC $CFLAGS $stem.c
       +.EE
       +.PP
       +Override flag settings in the mkfile:
       +.IP
       +.EX
       +% mk target 'CFLAGS=-S -w'
       +.EE
       +.PP
       +Maintain a library:
       +.IP
       +.EX
       +libc.a(%.$O):N:        %.$O
       +libc.a:        libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ...
       +        ar r libc.a $newmember
       +.EE
       +.PP
       +String expression variables to derive names from a master list:
       +.IP
       +.EX
       +NAMES=alloc arc bquote builtins expand main match mk var word
       +OBJ=${NAMES:%=%.$O}
       +.EE
       +.PP
       +Regular expression meta-rules:
       +.IP
       +.EX
       +([^/]*)/(.*)\e.$O:R:  \e1/\e2.c
       +        cd $stem1; $CC $CFLAGS $stem2.c
       +.EE
       +.PP
       +A correct way to deal with
       +.IR yacc (1)
       +grammars.
       +The file
       +.B lex.c
       +includes the file
       +.B x.tab.h
       +rather than
       +.B y.tab.h
       +in order to reflect changes in content, not just modification time.
       +.IP
       +.EX
       +lex.$O:        x.tab.h
       +x.tab.h:        y.tab.h
       +        cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
       +y.tab.c y.tab.h:        gram.y
       +        $YACC -d gram.y
       +.EE
       +.PP
       +The above example could also use the
       +.B P
       +attribute for the
       +.B x.tab.h
       +rule:
       +.IP
       +.EX
       +x.tab.h:Pcmp -s:        y.tab.h
       +        cp y.tab.h x.tab.h
       +.EE
       +.SH SOURCE
       +.B \*9/src/cmd/mk
       +.SH SEE ALSO
       +.IR sh (1),
       +.IR regexp (7)
       +.PP
       +A. Hume,
       +``Mk: a Successor to Make''
       +(Tenth Edition Research Unix Manuals).
       +.PP
       +Andrew G. Hume and Bob Flandrena,
       +``Maintaining Files on Plan 9 with Mk''.
       +DOCPREFIX/doc/mk.pdf
       +.SH HISTORY
       +Andrew Hume wrote
       +.I mk
       +for Tenth Edition Research Unix.
       +It was later ported to Plan 9.
       +This software is a port of the Plan 9 version back to Unix.
       +.SH BUGS
       +Identical recipes for regular expression meta-rules only have one target.
       +.PP
       +Seemingly appropriate input like
       +.B CFLAGS=-DHZ=60
       +is parsed as an erroneous attribute; correct it by inserting
       +a space after the first 
       +.LR = .
       +.PP
       +The recipes printed by
       +.I mk
       +before being passed to
       +the shell
       +for execution are sometimes erroneously expanded
       +for printing.  Don't trust what's printed; rely
       +on what the shell
       +does.
   DIR 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;
       +}
   DIR 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"
   DIR 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
       +
   DIR 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
       +
   DIR 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&REGEXP)){
       +                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);
       +}
   DIR 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
       +};
   DIR 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&REGEXP){
       +                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);
       +}
   DIR 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&REGEXP) || shellt->charin(head, "%&")){
       +                r->attr |= META;
       +                if(reuse)
       +                        return;
       +                if(attr&REGEXP){
       +                        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);
       +}
   DIR 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]);
       +}
   DIR 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
       +};
       +
   DIR 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; i<nelem(shells); i++)
       +                if(shells[i]->matchname(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);
       +}
   DIR 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);
       +}
   DIR 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);
       +}
   DIR diff --git a/mk/sys.h b/mk/sys.h
       @@ -0,0 +1,5 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <regexp.h>
       +
   DIR diff --git a/mk/sys.std.h b/mk/sys.std.h
       @@ -0,0 +1,27 @@
       +#include <utf.h>
       +#include <fmt.h>
       +#include <bio.h>
       +#include <regexp9.h>
       +#include <stdlib.h>
       +#include <unistd.h>
       +#include <fcntl.h>
       +#include <string.h>
       +#include <ctype.h>
       +#include <time.h>
       +#include <stdint.h>
       +
       +#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;
   DIR diff --git a/mk/unix.c b/mk/unix.c
       @@ -0,0 +1,341 @@
       +#define NOPLAN9DEFINES
       +#include        "mk.h"
       +#include        <sys/wait.h>
       +#include        <signal.h>
       +#include        <sys/stat.h>
       +#include        <sys/time.h>
       +
       +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;
       +}
   DIR 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;
       +}
   DIR 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;
       +}
   DIR 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');
       +}
   DIR 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 <setlocale> 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 <div> 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 <file>
       +        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 <time.h> 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.
   DIR 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
   DIR 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.
   DIR 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 }
       +' $*
   DIR 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 <u.h>
       +#include <stdio.h>
       +#include <ctype.h>
       +#include <string.h>
       +#include <stdlib.h>
       +
       +#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 */
       +
       +/*****************************************************************************/
       +
   DIR 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);
   DIR 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;
       +
   DIR diff --git a/troff/find b/troff/find
       @@ -0,0 +1 @@
       +grep $1 *.[ch]
   DIR 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);
   DIR 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
       +};
   DIR diff --git a/troff/mbwc.c b/troff/mbwc.c
       @@ -0,0 +1,165 @@
       +#include <stdlib.h>
       +
       +/*
       + * 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;
       +}
       +
   DIR 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
   DIR 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 <u.h>
       +#include "tdef.h"
       +#include "fns.h"
       +#include "ext.h"
       +#include "dwbinit.h"
       +
       +#include <setjmp.h>
       +#include <time.h>
       +
       +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;
       +        }
       +}
   DIR diff --git a/troff/n10.c b/troff/n10.c
       @@ -0,0 +1,549 @@
       +/*
       +n10.c
       +
       +Device interfaces
       +*/
       +
       +#include <u.h>
       +#include "tdef.h"
       +#include "ext.h"
       +#include "fns.h"
       +#include <ctype.h>
       +
       +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);
       +}
   DIR 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 <setjmp.h>
       +
       +#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++;
       +}
   DIR 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");
       +        }
       +}
   DIR 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; i<NHASHSIZE; i++)
       +                nhash[i] = 0;
       +        for (p=numtabp; p < &numtabp[ncnt]; p++)
       +                p->link = 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);
       +}
   DIR 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(&lt), 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*/
       +{
       +}
   DIR diff --git a/troff/n6.c b/troff/n6.c
       @@ -0,0 +1,363 @@
       +#include "tdef.h"
       +#include "ext.h"
       +#include "fns.h"
       +#include <ctype.h>
       +
       +/*
       + * 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);
       +}
   DIR 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);
       +}
   DIR diff --git a/troff/n8.c b/troff/n8.c
       @@ -0,0 +1,545 @@
       +#include <u.h>
       +#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;
       +}
   DIR 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);
       +}
   DIR diff --git a/troff/ni.c b/troff/ni.c
       @@ -0,0 +1,390 @@
       +#include <stdio.h>
       +#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);
       +
   DIR 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
       +};
   DIR 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 */
       +}
   DIR 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;
       +}
   DIR 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; i<NWIDCACHE; i++)
       +                widcache[i].fontpts = 0;
       +}
       +
       +int
       +onfont(int n, int f)        /* is char n on font f? */
       +{
       +        int i;
       +        Font *fp = &fonts[f];
       +        Chwid *cp, *ep;
       +        char *np;
       +
       +        if (n < ALPHABET) {
       +                if (fp->wp[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;
       +}
   DIR diff --git a/troff/tdef.h b/troff/tdef.h
       @@ -0,0 +1,673 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <limits.h>
       +#include <ctype.h>
       +#include <string.h>
       +#include <unistd.h>
       +
       +#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<<SHORT))
       +
       +
       +extern        int        Inch, Hor, Vert, Unitwidth;
       +
       +struct        Spnames
       +{
       +        int        *n;
       +        char        *v;
       +};
       +
       +extern        Spnames        spnames[];
       +
       +/*
       +        String and macro definitions are stored conceptually in a giant array
       +        indexed by type Offset.  In olden times, this array was real, and thus
       +        both huge and limited in size, leading to the "Out of temp file space"
       +        error.  In this version, the array is represented by a list of blocks,
       +        pointed to by blist[].bp.  Each block is of size BLK Tchars, and BLK
       +        MUST be a power of 2 for the macros below to work.
       +        
       +        The blocks associated with a particular string or macro are chained
       +        together in the array blist[].  Each blist[i].nextoff contains the
       +        Offset associated with the next block in the giant array, or -1 if
       +        this is the last block in the chain.  If .nextoff is 0, the block is
       +        free.
       +        
       +        To find the right index in blist for an Offset, divide by BLK.
       +*/
       +
       +#define        NBLIST        2048        /* starting number of blocks in all definitions */
       +
       +#define        BLK        128        /* number of Tchars in a block; must be 2^N with defns below */
       +
       +#define        rbf0(o)                (blist[bindex(o)].bp[boffset(o)])
       +#define        bindex(o)        ((o) / BLK)
       +#define        boffset(o)        ((o) & (BLK-1))
       +#define        pastend(o)        (((o) & (BLK-1)) == 0)
       +/* #define        incoff(o)        ( (++o & (BLK-1)) ? o : blist[bindex(o-1)].nextoff ) */
       +#define        incoff(o)        ( (((o)+1) & (BLK-1)) ? o+1 : blist[bindex(o)].nextoff )
       +
       +#define        skipline(f)        while (getc(f) != '\n')
       +#define is(s)                (strcmp(cmd, s) == 0)
       +#define        eq(s1, s2)        (strcmp(s1, s2) == 0)
       +
       +
       +typedef        unsigned long        Offset;                /* an offset in macro/string storage */
       +
       +struct Blockp {                /* info about a block: */
       +        Tchar        *bp;                /* the data */
       +        Offset        nextoff;        /* offset of next block in a chain */
       +};
       +
       +extern        Blockp        *blist;
       +
       +#define        RD_OFFSET        (1 * BLK)        /* .rd command uses block 1 */
       +
       +struct Diver {                /* diversion */
       +        Offset        op;
       +        int        dnl;
       +        int        dimac;
       +        int        ditrap;
       +        int        ditf;
       +        int        alss;
       +        int        blss;
       +        int        nls;
       +        int        mkline;
       +        int        maxl;
       +        int        hnl;
       +        int        curd;
       +};
       +
       +struct Stack {                /* stack frame */
       +        int        nargs;
       +        Stack        *pframe;
       +        Offset        pip;
       +        int        pnchar;
       +        Tchar        prchar;
       +        int        ppendt;
       +        Tchar        pch;
       +        Tchar        *lastpbp;
       +        int        mname;
       +};
       +
       +extern        Stack        s;
       +
       +struct Divsiz {
       +        int dix;
       +        int diy;
       +};
       +
       +struct Contab {                /* command or macro */
       +        unsigned int        rq;
       +        Contab        *link;
       +        void        (*f)(void);
       +        Offset        mx;
       +        Offset        emx;
       +        Divsiz        *divsiz;
       +};
       +
       +#define        C(a,b)        {a, 0, b, 0, 0}                /* how to initialize a contab entry */
       +
       +extern        Contab        contab[NM];
       +
       +struct Numtab {        /* number registers */
       +        unsigned int        r;                /* name */
       +        int        val;
       +        short        fmt;
       +        short        inc;
       +        Numtab        *link;
       +};
       +
       +extern        Numtab        numtab[NN];
       +
       +#define        PN        0
       +#define        NL        1
       +#define        YR        2
       +#define        HP        3
       +#define        CT        4
       +#define        DN        5
       +#define        MO        6
       +#define        DY        7
       +#define        DW        8
       +#define        LN        9
       +#define        DL        10
       +#define        ST        11
       +#define        SB        12
       +#define        CD        13
       +#define        PID        14
       +
       +struct        Wcache {        /* width cache, indexed by character */
       +        short        fontpts;
       +        short        width;
       +};
       +
       +struct        Tbuf {                /* growable Tchar buffer */
       +        Tchar *_bufp;
       +        unsigned int _size;
       +};
       +
       +/* the infamous environment block */
       +
       +#define        ics        envp->_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 */
       +};
   DIR 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.
   DIR 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
       +