commit a08a2a9000cf4cb01ac165f410dbe99a64191edd
parent c0a69251c8988bdbabf4f3d3e20f40e363990c6c
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(-)
diff --git a/Makefile b/Makefile
@@ -1,10 +1,10 @@
-# 9base - awk basename cal cat cleanname du echo grep rc sed seq sleep
-# hoc sort tee test touch tr uniq from Plan 9
+# 9base - awk basename bc cal cat cleanname dc du echo grep mk rc sed seq sleep
+# troff hoc sort tee test touch tr uniq from Plan 9
include config.mk
-SUBDIRS = lib9 yacc awk basename bc dc du cal cat cleanname date echo grep ls \
- hoc rc read sed seq sleep sort tee test touch tr uniq
+SUBDIRS = lib9 mk yacc awk basename bc dc du cal cat cleanname date echo grep ls \
+ hoc rc read sed seq sleep sort tee test touch tr troff uniq
all:
@echo 9base build options:
diff --git a/config.mk b/config.mk
@@ -4,7 +4,7 @@
PREFIX = /usr/local/plan9
MANPREFIX = ${PREFIX}/share/man
-VERSION = 3
+VERSION = 4
OBJTYPE = 386
#OBJTYPE = arm
#OBJTYPE = x86_64
diff --git a/mk/Makefile b/mk/Makefile
@@ -0,0 +1,11 @@
+# mk - mk unix port from plan9
+# Depends on ../lib9
+
+TARG = mk
+
+OFILES = arc.o archive.o bufblock.o env.o file.o graph.o job.o lex.o \
+ main.o match.o mk.o parse.o recipe.o rc.o rule.o run.o sh.o \
+ shell.o shprint.o symtab.o var.o varsub.o word.o unix.o
+MANFILES = mk.1
+
+include ../std.mk
diff --git a/mk/NOTICE b/mk/NOTICE
@@ -0,0 +1,27 @@
+Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+Portions Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk). All rights reserved.
+Portions Copyright © 1997-1999 Vita Nuova Limited. All rights reserved.
+Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
+
+Under a licence agreement with Lucent Technologies Inc. effective 1st March 2000,
+Vita Nuova Holdings Limited has the right to determine (within a specified scope)
+the form and content of sublicences for this software.
+
+Vita Nuova Holdings Limited now makes this software available as Free
+Software under the terms of the `GNU General Public LIcense, Version 2'
+(see the file LICENCE or http://www.fsf.org/copyleft/gpl.html for
+the full terms and conditions). One of the conditions of that licence
+is that you must keep intact all notices that refer to that licence and to the absence of
+of any warranty: for this software, note that includes this NOTICE file in particular.
+
+This suite of programs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+`GNU General Public License' for more details.
+
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices above.
diff --git a/mk/README b/mk/README
@@ -0,0 +1,7 @@
+This is a Unix port of mk,
+originally done for the Inferno operating system.
+
+Russ Cox repackaged this to build as a standalone
+Unix program. Send comments about packaging to
+Russ Cox <rsc@post.harvard.edu>
+
diff --git a/mk/arc.c b/mk/arc.c
@@ -0,0 +1,52 @@
+#include "mk.h"
+
+Arc *
+newarc(Node *n, Rule *r, char *stem, Resub *match)
+{
+ Arc *a;
+
+ a = (Arc *)Malloc(sizeof(Arc));
+ a->n = n;
+ a->r = r;
+ a->stem = strdup(stem);
+ rcopy(a->match, match, NREGEXP);
+ a->next = 0;
+ a->flag = 0;
+ a->prog = r->prog;
+ return(a);
+}
+
+void
+dumpa(char *s, Arc *a)
+{
+ char buf[1024];
+
+ Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'",
+ s, a, a->n, a->r, a->flag, a->stem);
+ if(a->prog)
+ Bprint(&bout, " prog='%s'", a->prog);
+ Bprint(&bout, "\n");
+
+ if(a->n){
+ snprint(buf, sizeof(buf), "%s ", (*s == ' ')? s:"");
+ dumpn(buf, a->n);
+ }
+}
+
+void
+nrep(void)
+{
+ Symtab *sym;
+ Word *w;
+
+ sym = symlook("NREP", S_VAR, 0);
+ if(sym){
+ w = sym->u.ptr;
+ if (w && w->s && *w->s)
+ nreps = atoi(w->s);
+ }
+ if(nreps < 1)
+ nreps = 1;
+ if(DEBUG(D_GRAPH))
+ Bprint(&bout, "nreps = %d\n", nreps);
+}
diff --git a/mk/archive.c b/mk/archive.c
@@ -0,0 +1,253 @@
+#include "mk.h"
+#define ARMAG "!<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;
+}
diff --git a/mk/bufblock.c b/mk/bufblock.c
@@ -0,0 +1,88 @@
+#include "mk.h"
+
+static Bufblock *freelist;
+#define QUANTA 4096
+
+Bufblock *
+newbuf(void)
+{
+ Bufblock *p;
+
+ if (freelist) {
+ p = freelist;
+ freelist = freelist->next;
+ } else {
+ p = (Bufblock *) Malloc(sizeof(Bufblock));
+ p->start = Malloc(QUANTA*sizeof(*p->start));
+ p->end = p->start+QUANTA;
+ }
+ p->current = p->start;
+ *p->start = 0;
+ p->next = 0;
+ return p;
+}
+
+void
+freebuf(Bufblock *p)
+{
+ p->next = freelist;
+ freelist = p;
+}
+
+void
+growbuf(Bufblock *p)
+{
+ int n;
+ Bufblock *f;
+ char *cp;
+
+ n = p->end-p->start+QUANTA;
+ /* search the free list for a big buffer */
+ for (f = freelist; f; f = f->next) {
+ if (f->end-f->start >= n) {
+ memcpy(f->start, p->start, p->end-p->start);
+ cp = f->start;
+ f->start = p->start;
+ p->start = cp;
+ cp = f->end;
+ f->end = p->end;
+ p->end = cp;
+ f->current = f->start;
+ break;
+ }
+ }
+ if (!f) { /* not found - grow it */
+ p->start = Realloc(p->start, n);
+ p->end = p->start+n;
+ }
+ p->current = p->start+n-QUANTA;
+}
+
+void
+bufcpy(Bufblock *buf, char *cp, int n)
+{
+
+ while (n--)
+ insert(buf, *cp++);
+}
+
+void
+insert(Bufblock *buf, int c)
+{
+
+ if (buf->current >= buf->end)
+ growbuf(buf);
+ *buf->current++ = c;
+}
+
+void
+rinsert(Bufblock *buf, Rune r)
+{
+ int n;
+
+ n = runelen(r);
+ if (buf->current+n > buf->end)
+ growbuf(buf);
+ runetochar(buf->current, &r);
+ buf->current += n;
+}
diff --git a/mk/env.c b/mk/env.c
@@ -0,0 +1,149 @@
+#include "mk.h"
+
+enum {
+ ENVQUANTA=10
+};
+
+Envy *envy;
+static int nextv;
+
+static char *myenv[] =
+{
+ "target",
+ "stem",
+ "prereq",
+ "pid",
+ "nproc",
+ "newprereq",
+ "alltarget",
+ "newmember",
+ "stem0", /* must be in order from here */
+ "stem1",
+ "stem2",
+ "stem3",
+ "stem4",
+ "stem5",
+ "stem6",
+ "stem7",
+ "stem8",
+ "stem9",
+ 0
+};
+
+void
+initenv(void)
+{
+ char **p;
+
+ for(p = myenv; *p; p++)
+ symlook(*p, S_INTERNAL, (void *)"");
+ readenv(); /* o.s. dependent */
+}
+
+static void
+envinsert(char *name, Word *value)
+{
+ static int envsize;
+
+ if (nextv >= envsize) {
+ envsize += ENVQUANTA;
+ envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy));
+ }
+ envy[nextv].name = name;
+ envy[nextv++].values = value;
+}
+
+static void
+envupd(char *name, Word *value)
+{
+ Envy *e;
+
+ for(e = envy; e->name; e++)
+ if(strcmp(name, e->name) == 0){
+ delword(e->values);
+ e->values = value;
+ return;
+ }
+ e->name = name;
+ e->values = value;
+ envinsert(0,0);
+}
+
+static void
+ecopy(Symtab *s)
+{
+ char **p;
+
+ if(symlook(s->name, S_NOEXPORT, 0))
+ return;
+ for(p = myenv; *p; p++)
+ if(strcmp(*p, s->name) == 0)
+ return;
+ envinsert(s->name, s->u.ptr);
+}
+
+void
+execinit(void)
+{
+ char **p;
+
+ nextv = 0;
+ for(p = myenv; *p; p++)
+ envinsert(*p, stow(""));
+
+ symtraverse(S_VAR, ecopy);
+ envinsert(0, 0);
+}
+
+Envy*
+buildenv(Job *j, int slot)
+{
+ char **p, *cp, *qp;
+ Word *w, *v, **l;
+ int i;
+ char buf[256];
+
+ envupd("target", wdup(j->t));
+ if(j->r->attr®EXP)
+ envupd("stem",newword(""));
+ else
+ envupd("stem", newword(j->stem));
+ envupd("prereq", wdup(j->p));
+ sprint(buf, "%d", getpid());
+ envupd("pid", newword(buf));
+ sprint(buf, "%d", slot);
+ envupd("nproc", newword(buf));
+ envupd("newprereq", wdup(j->np));
+ envupd("alltarget", wdup(j->at));
+ l = &v;
+ v = w = wdup(j->np);
+ while(w){
+ cp = strchr(w->s, '(');
+ if(cp){
+ qp = strchr(cp+1, ')');
+ if(qp){
+ *qp = 0;
+ strcpy(w->s, cp+1);
+ l = &w->next;
+ w = w->next;
+ continue;
+ }
+ }
+ *l = w->next;
+ free(w->s);
+ free(w);
+ w = *l;
+ }
+ envupd("newmember", v);
+ /* update stem0 -> stem9 */
+ for(p = myenv; *p; p++)
+ if(strcmp(*p, "stem0") == 0)
+ break;
+ for(i = 0; *p; i++, p++){
+ if((j->r->attr®EXP) && j->match[i])
+ envupd(*p, newword(j->match[i]));
+ else
+ envupd(*p, newword(""));
+ }
+ return envy;
+}
diff --git a/mk/file.c b/mk/file.c
@@ -0,0 +1,90 @@
+#include "mk.h"
+
+/* table-driven version in bootes dump of 12/31/96 */
+
+long
+mtime(char *name)
+{
+ return mkmtime(name);
+}
+
+long
+timeof(char *name, int force)
+{
+ Symtab *sym;
+ long t;
+
+ if(utfrune(name, '('))
+ return atimeof(force, name); /* archive */
+
+ if(force)
+ return mtime(name);
+
+
+ sym = symlook(name, S_TIME, 0);
+ if (sym)
+ return sym->u.value;
+
+ t = mtime(name);
+ if(t == 0)
+ return 0;
+
+ symlook(name, S_TIME, (void*)t); /* install time in cache */
+ return t;
+}
+
+void
+touch(char *name)
+{
+ Bprint(&bout, "touch(%s)\n", name);
+ if(nflag)
+ return;
+
+ if(utfrune(name, '('))
+ atouch(name); /* archive */
+ else if(chgtime(name) < 0) {
+ fprint(2, "%s: %r\n", name);
+ Exit();
+ }
+}
+
+void
+delete(char *name)
+{
+ if(utfrune(name, '(') == 0) { /* file */
+ if(remove(name) < 0)
+ fprint(2, "remove %s: %r\n", name);
+ } else
+ fprint(2, "hoon off; mk can'tdelete archive members\n");
+}
+
+void
+timeinit(char *s)
+{
+ long t;
+ char *cp;
+ Rune r;
+ int c, n;
+
+ t = time(0);
+ while (*s) {
+ cp = s;
+ do{
+ n = chartorune(&r, s);
+ if (r == ' ' || r == ',' || r == '\n')
+ break;
+ s += n;
+ } while(*s);
+ c = *s;
+ *s = 0;
+ symlook(strdup(cp), S_TIME, (void *)t)->u.value = t;
+ if (c)
+ *s++ = c;
+ while(*s){
+ n = chartorune(&r, s);
+ if(r != ' ' && r != ',' && r != '\n')
+ break;
+ s += n;
+ }
+ }
+}
diff --git a/mk/fns.h b/mk/fns.h
@@ -0,0 +1,88 @@
+#undef waitfor
+#define waitfor mkwaitfor
+
+void addrule(char*, Word*, char*, Word*, int, int, char*);
+void addrules(Word*, Word*, char*, int, int, char*);
+void addw(Word*, char*);
+void assert(char*, int);
+int assline(Biobuf *, Bufblock *);
+long atimeof(int,char*);
+void atouch(char*);
+void bufcpy(Bufblock *, char *, int);
+Envy *buildenv(Job*, int);
+void catchnotes(void);
+int chgtime(char*);
+void clrmade(Node*);
+void delete(char*);
+void delword(Word*);
+int dorecipe(Node*);
+void dumpa(char*, Arc*);
+void dumpj(char*, Job*, int);
+void dumpn(char*, Node*);
+void dumpr(char*, Rule*);
+void dumpv(char*);
+void dumpw(char*, Word*);
+void execinit(void);
+int execsh(char*, char*, Bufblock*, Envy*, Shell*, Word*);
+void Exit(void);
+void expunge(int, char*);
+void freebuf(Bufblock*);
+void front(char*);
+Node *graph(char*);
+void growbuf(Bufblock *);
+void initenv(void);
+void initshell(void);
+void insert(Bufblock *, int);
+void ipop(void);
+void ipush(void);
+void killchildren(char*);
+void *Malloc(int);
+char *maketmp(int*);
+int match(char*, char*, char*, Shell*);
+char *membername(char*, int, char*);
+void mk(char*);
+unsigned long mkmtime(char*);
+long mtime(char*);
+Arc *newarc(Node*, Rule*, char*, Resub*);
+Bufblock *newbuf(void);
+Job *newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*);
+Word *newword(char*);
+int nextrune(Biobuf*, int);
+int nextslot(void);
+void nproc(void);
+void nrep(void);
+int outofdate(Node*, Arc*, int);
+void parse(char*, int, int);
+int pipecmd(char*, Envy*, int*, Shell*, Word*);
+void popshell(void);
+void prusage(void);
+void pushshell(void);
+void rcopy(char**, Resub*, int);
+void readenv(void);
+void *Realloc(void*, int);
+void rinsert(Bufblock *, Rune);
+char *rulecnt(void);
+void run(Job*);
+char *setshell(Word*);
+void setvar(char*, void*);
+int shargv(Word*, int, char***);
+char *shname(char*);
+void shprint(char*, Envy*, Bufblock*, Shell*);
+Word *stow(char*);
+void subst(char*, char*, char*);
+void symdel(char*, int);
+void syminit(void);
+Symtab *symlook(char*, int, void*);
+void symstat(void);
+void symtraverse(int, void(*)(Symtab*));
+void timeinit(char*);
+long timeof(char*, int);
+void touch(char*);
+void update(int, Node*);
+void usage(void);
+Word *varsub(char**);
+int waitfor(char*);
+int waitup(int, int*);
+Word *wdup(Word*);
+int work(Node*, Node*, Arc*);
+char *wtos(Word*, int);
diff --git a/mk/graph.c b/mk/graph.c
@@ -0,0 +1,279 @@
+#include "mk.h"
+
+static Node *applyrules(char *, char *);
+static void togo(Node *);
+static int vacuous(Node *);
+static Node *newnode(char *);
+static void trace(char *, Arc *);
+static void cyclechk(Node *);
+static void ambiguous(Node *);
+static void attribute(Node *);
+
+Node *
+graph(char *target)
+{
+ Node *node;
+ char *cnt;
+
+ cnt = rulecnt();
+ node = applyrules(target, cnt);
+ free(cnt);
+ cyclechk(node);
+ node->flags |= PROBABLE; /* make sure it doesn't get deleted */
+ vacuous(node);
+ ambiguous(node);
+ attribute(node);
+ return(node);
+}
+
+static Node *
+applyrules(char *target, char *cnt)
+{
+ Symtab *sym;
+ Node *node;
+ Rule *r;
+ Arc head, *a = &head;
+ Word *w;
+ char stem[NAMEBLOCK], buf[NAMEBLOCK];
+ Resub rmatch[NREGEXP];
+
+/* print("applyrules(%lux='%s')\n", target, target); */
+ sym = symlook(target, S_NODE, 0);
+ if(sym)
+ return sym->u.ptr;
+ target = strdup(target);
+ node = newnode(target);
+ head.n = 0;
+ head.next = 0;
+ sym = symlook(target, S_TARGET, 0);
+ memset((char*)rmatch, 0, sizeof(rmatch));
+ for(r = sym? sym->u.ptr:0; r; r = r->chain){
+ if(r->attr&META) continue;
+ if(strcmp(target, r->target)) continue;
+ if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */
+ if(cnt[r->rule] >= nreps) continue;
+ cnt[r->rule]++;
+ node->flags |= PROBABLE;
+
+/* if(r->attr&VIR)
+ * node->flags |= VIRTUAL;
+ * if(r->attr&NOREC)
+ * node->flags |= NORECIPE;
+ * if(r->attr&DEL)
+ * node->flags |= DELETE;
+ */
+ if(!r->tail || !r->tail->s || !*r->tail->s) {
+ a->next = newarc((Node *)0, r, "", rmatch);
+ a = a->next;
+ } else
+ for(w = r->tail; w; w = w->next){
+ a->next = newarc(applyrules(w->s, cnt), r, "", rmatch);
+ a = a->next;
+ }
+ cnt[r->rule]--;
+ head.n = node;
+ }
+ for(r = metarules; r; r = r->next){
+ if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */
+ if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR))
+ continue;
+ if(r->attr®EXP){
+ stem[0] = 0;
+ patrule = r;
+ memset((char*)rmatch, 0, sizeof(rmatch));
+ if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
+ continue;
+ } else {
+ if(!match(node->name, r->target, stem, r->shellt)) continue;
+ }
+ if(cnt[r->rule] >= nreps) continue;
+ cnt[r->rule]++;
+
+/* if(r->attr&VIR)
+ * node->flags |= VIRTUAL;
+ * if(r->attr&NOREC)
+ * node->flags |= NORECIPE;
+ * if(r->attr&DEL)
+ * node->flags |= DELETE;
+ */
+
+ if(!r->tail || !r->tail->s || !*r->tail->s) {
+ a->next = newarc((Node *)0, r, stem, rmatch);
+ a = a->next;
+ } else
+ for(w = r->tail; w; w = w->next){
+ if(r->attr®EXP)
+ regsub(w->s, buf, sizeof buf, rmatch, NREGEXP);
+ else
+ subst(stem, w->s, buf);
+ a->next = newarc(applyrules(buf, cnt), r, stem, rmatch);
+ a = a->next;
+ }
+ cnt[r->rule]--;
+ }
+ a->next = node->prereqs;
+ node->prereqs = head.next;
+ return(node);
+}
+
+static void
+togo(Node *node)
+{
+ Arc *la, *a;
+
+ /* delete them now */
+ la = 0;
+ for(a = node->prereqs; a; la = a, a = a->next)
+ if(a->flag&TOGO){
+ if(a == node->prereqs)
+ node->prereqs = a->next;
+ else
+ la->next = a->next, a = la;
+ }
+}
+
+static int
+vacuous(Node *node)
+{
+ Arc *la, *a;
+ int vac = !(node->flags&PROBABLE);
+
+ if(node->flags&READY)
+ return(node->flags&VACUOUS);
+ node->flags |= READY;
+ for(a = node->prereqs; a; a = a->next)
+ if(a->n && vacuous(a->n) && (a->r->attr&META))
+ a->flag |= TOGO;
+ else
+ vac = 0;
+ /* if a rule generated arcs that DON'T go; no others from that rule go */
+ for(a = node->prereqs; a; a = a->next)
+ if((a->flag&TOGO) == 0)
+ for(la = node->prereqs; la; la = la->next)
+ if((la->flag&TOGO) && (la->r == a->r)){
+ la->flag &= ~TOGO;
+ }
+ togo(node);
+ if(vac)
+ node->flags |= VACUOUS;
+ return(vac);
+}
+
+static Node *
+newnode(char *name)
+{
+ register Node *node;
+
+ node = (Node *)Malloc(sizeof(Node));
+ symlook(name, S_NODE, (void *)node);
+ node->name = name;
+ node->time = timeof(name, 0);
+ node->prereqs = 0;
+ node->flags = node->time? PROBABLE : 0;
+ node->next = 0;
+ return(node);
+}
+
+void
+dumpn(char *s, Node *n)
+{
+ char buf[1024];
+ Arc *a;
+
+ snprint(buf, sizeof buf, "%s ", (*s == ' ')? s:"");
+ Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n",
+ s, n->name, n, n->time, n->flags, n->next);
+ for(a = n->prereqs; a; a = a->next)
+ dumpa(buf, a);
+}
+
+static void
+trace(char *s, Arc *a)
+{
+ fprint(2, "\t%s", s);
+ while(a){
+ fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line,
+ a->n? a->n->name:"");
+ if(a->n){
+ for(a = a->n->prereqs; a; a = a->next)
+ if(*a->r->recipe) break;
+ } else
+ a = 0;
+ }
+ fprint(2, "\n");
+}
+
+static void
+cyclechk(Node *n)
+{
+ Arc *a;
+
+ if((n->flags&CYCLE) && n->prereqs){
+ fprint(2, "mk: cycle in graph detected at target %s\n", n->name);
+ Exit();
+ }
+ n->flags |= CYCLE;
+ for(a = n->prereqs; a; a = a->next)
+ if(a->n)
+ cyclechk(a->n);
+ n->flags &= ~CYCLE;
+}
+
+static void
+ambiguous(Node *n)
+{
+ Arc *a;
+ Rule *r = 0;
+ Arc *la;
+ int bad = 0;
+
+ la = 0;
+ for(a = n->prereqs; a; a = a->next){
+ if(a->n)
+ ambiguous(a->n);
+ if(*a->r->recipe == 0) continue;
+ if(r == 0)
+ r = a->r, la = a;
+ else{
+ if(r->recipe != a->r->recipe){
+ if((r->attr&META) && !(a->r->attr&META)){
+ la->flag |= TOGO;
+ r = a->r, la = a;
+ } else if(!(r->attr&META) && (a->r->attr&META)){
+ a->flag |= TOGO;
+ continue;
+ }
+ }
+ if(r->recipe != a->r->recipe){
+ if(bad == 0){
+ fprint(2, "mk: ambiguous recipes for %s:\n", n->name);
+ bad = 1;
+ trace(n->name, la);
+ }
+ trace(n->name, a);
+ }
+ }
+ }
+ if(bad)
+ Exit();
+ togo(n);
+}
+
+static void
+attribute(Node *n)
+{
+ register Arc *a;
+
+ for(a = n->prereqs; a; a = a->next){
+ if(a->r->attr&VIR)
+ n->flags |= VIRTUAL;
+ if(a->r->attr&NOREC)
+ n->flags |= NORECIPE;
+ if(a->r->attr&DEL)
+ n->flags |= DELETE;
+ if(a->n)
+ attribute(a->n);
+ }
+ if(n->flags&VIRTUAL)
+ n->time = 0;
+}
diff --git a/mk/job.c b/mk/job.c
@@ -0,0 +1,33 @@
+#include "mk.h"
+
+Job *
+newjob(Rule *r, Node *nlist, char *stem, char **match, Word *pre, Word *npre, Word *tar, Word *atar)
+{
+ register Job *j;
+
+ j = (Job *)Malloc(sizeof(Job));
+ j->r = r;
+ j->n = nlist;
+ j->stem = stem;
+ j->match = match;
+ j->p = pre;
+ j->np = npre;
+ j->t = tar;
+ j->at = atar;
+ j->nproc = -1;
+ j->next = 0;
+ return(j);
+}
+
+void
+dumpj(char *s, Job *j, int all)
+{
+ Bprint(&bout, "%s\n", s);
+ while(j){
+ Bprint(&bout, "job@%ld: r=%ld n=%ld stem='%s' nproc=%d\n",
+ j, j->r, j->n, j->stem, j->nproc);
+ Bprint(&bout, "\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n",
+ wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wtos(j->np, ' '));
+ j = all? j->next : 0;
+ }
+}
diff --git a/mk/lex.c b/mk/lex.c
@@ -0,0 +1,146 @@
+#include "mk.h"
+
+static int bquote(Biobuf*, Bufblock*);
+
+/*
+ * Assemble a line skipping blank lines, comments, and eliding
+ * escaped newlines
+ */
+int
+assline(Biobuf *bp, Bufblock *buf)
+{
+ int c;
+ int lastc;
+
+ buf->current=buf->start;
+ while ((c = nextrune(bp, 1)) >= 0){
+ switch(c)
+ {
+ case '\r': /* consumes CRs for Win95 */
+ continue;
+ case '\n':
+ if (buf->current != buf->start) {
+ insert(buf, 0);
+ return 1;
+ }
+ break; /* skip empty lines */
+ case '\\':
+ case '\'':
+ case '"':
+ rinsert(buf, c);
+ if (shellt->escapetoken(bp, buf, 1, c) == 0)
+ Exit();
+ break;
+ case '`':
+ if (bquote(bp, buf) == 0)
+ Exit();
+ break;
+ case '#':
+ lastc = '#';
+ while ((c = Bgetc(bp)) != '\n') {
+ if (c < 0)
+ goto eof;
+ if(c != '\r')
+ lastc = c;
+ }
+ mkinline++;
+ if (lastc == '\\')
+ break; /* propagate escaped newlines??*/
+ if (buf->current != buf->start) {
+ insert(buf, 0);
+ return 1;
+ }
+ break;
+ default:
+ rinsert(buf, c);
+ break;
+ }
+ }
+eof:
+ insert(buf, 0);
+ return *buf->start != 0;
+}
+
+/*
+ * assemble a back-quoted shell command into a buffer
+ */
+static int
+bquote(Biobuf *bp, Bufblock *buf)
+{
+ int c, line, term;
+ int start;
+
+ line = mkinline;
+ while((c = Bgetrune(bp)) == ' ' || c == '\t')
+ ;
+ if(c == '{'){
+ term = '}'; /* rc style */
+ while((c = Bgetrune(bp)) == ' ' || c == '\t')
+ ;
+ } else
+ term = '`'; /* sh style */
+
+ start = buf->current-buf->start;
+ for(;c > 0; c = nextrune(bp, 0)){
+ if(c == term){
+ insert(buf, '\n');
+ insert(buf,0);
+ buf->current = buf->start+start;
+ execinit();
+ execsh(0, buf->current, buf, envy, shellt, shellcmd);
+ return 1;
+ }
+ if(c == '\n')
+ break;
+ if(c == '\'' || c == '"' || c == '\\'){
+ insert(buf, c);
+ if(!shellt->escapetoken(bp, buf, 1, c))
+ return 0;
+ continue;
+ }
+ rinsert(buf, c);
+ }
+ SYNERR(line);
+ fprint(2, "missing closing %c after `\n", term);
+ return 0;
+}
+
+/*
+ * get next character stripping escaped newlines
+ * the flag specifies whether escaped newlines are to be elided or
+ * replaced with a blank.
+ */
+int
+nextrune(Biobuf *bp, int elide)
+{
+ int c, c2;
+ static int savec;
+
+ if(savec){
+ c = savec;
+ savec = 0;
+ return c;
+ }
+
+ for (;;) {
+ c = Bgetrune(bp);
+ if (c == '\\') {
+ c2 = Bgetrune(bp);
+ if(c2 == '\r'){
+ savec = c2;
+ c2 = Bgetrune(bp);
+ }
+ if (c2 == '\n') {
+ savec = 0;
+ mkinline++;
+ if (elide)
+ continue;
+ return ' ';
+ }
+ Bungetrune(bp);
+ }
+ if (c == '\n')
+ mkinline++;
+ return c;
+ }
+}
diff --git a/mk/main.c b/mk/main.c
@@ -0,0 +1,287 @@
+#include "mk.h"
+
+#define MKFILE "mkfile"
+
+int debug;
+Rule *rules, *metarules;
+int nflag = 0;
+int tflag = 0;
+int iflag = 0;
+int kflag = 0;
+int aflag = 0;
+int uflag = 0;
+char *explain = 0;
+Word *target1;
+int nreps = 1;
+Job *jobs;
+Biobuf bout;
+Rule *patrule;
+void badusage(void);
+#ifdef PROF
+short buf[10000];
+#endif
+
+int
+main(int argc, char **argv)
+{
+ Word *w;
+ char *s, *temp;
+ char *files[256], **f = files, **ff;
+ int sflag = 0;
+ int i;
+ int tfd = -1;
+ Biobuf tb;
+ Bufblock *buf;
+ Bufblock *whatif;
+
+ /*
+ * start with a copy of the current environment variables
+ * instead of sharing them
+ */
+
+ Binit(&bout, 1, OWRITE);
+ buf = newbuf();
+ whatif = 0;
+ USED(argc);
+ for(argv++; *argv && (**argv == '-'); argv++)
+ {
+ bufcpy(buf, argv[0], strlen(argv[0]));
+ insert(buf, ' ');
+ switch(argv[0][1])
+ {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'd':
+ if(*(s = &argv[0][2]))
+ while(*s) switch(*s++)
+ {
+ case 'p': debug |= D_PARSE; break;
+ case 'g': debug |= D_GRAPH; break;
+ case 'e': debug |= D_EXEC; break;
+ }
+ else
+ debug = 0xFFFF;
+ break;
+ case 'e':
+ explain = &argv[0][2];
+ break;
+ case 'f':
+ if(*++argv == 0)
+ badusage();
+ *f++ = *argv;
+ bufcpy(buf, argv[0], strlen(argv[0]));
+ insert(buf, ' ');
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'w':
+ if(whatif == 0)
+ whatif = newbuf();
+ else
+ insert(whatif, ' ');
+ if(argv[0][2])
+ bufcpy(whatif, &argv[0][2], strlen(&argv[0][2]));
+ else {
+ if(*++argv == 0)
+ badusage();
+ bufcpy(whatif, &argv[0][0], strlen(&argv[0][0]));
+ }
+ break;
+ default:
+ badusage();
+ }
+ }
+#ifdef PROF
+ {
+ extern etext();
+ monitor(main, etext, buf, sizeof buf, 300);
+ }
+#endif
+
+ if(aflag)
+ iflag = 1;
+ usage();
+ syminit();
+ initshell();
+ initenv();
+ usage();
+
+ /*
+ assignment args become null strings
+ */
+ temp = 0;
+ for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){
+ bufcpy(buf, argv[i], strlen(argv[i]));
+ insert(buf, ' ');
+ if(tfd < 0){
+ temp = maketmp(&tfd);
+ if(temp == 0) {
+ fprint(2, "temp file: %r\n");
+ Exit();
+ }
+ Binit(&tb, tfd, OWRITE);
+ }
+ Bprint(&tb, "%s\n", argv[i]);
+ *argv[i] = 0;
+ }
+ if(tfd >= 0){
+ Bflush(&tb);
+ LSEEK(tfd, 0L, 0);
+ parse("command line args", tfd, 1);
+ remove(temp);
+ }
+
+ if (buf->current != buf->start) {
+ buf->current--;
+ insert(buf, 0);
+ }
+ symlook("MKFLAGS", S_VAR, (void *) stow(buf->start));
+ buf->current = buf->start;
+ for(i = 0; argv[i]; i++){
+ if(*argv[i] == 0) continue;
+ if(i)
+ insert(buf, ' ');
+ bufcpy(buf, argv[i], strlen(argv[i]));
+ }
+ insert(buf, 0);
+ symlook("MKARGS", S_VAR, (void *) stow(buf->start));
+ freebuf(buf);
+
+ if(f == files){
+ if(access(MKFILE, 4) == 0)
+ parse(MKFILE, open(MKFILE, 0), 0);
+ } else
+ for(ff = files; ff < f; ff++)
+ parse(*ff, open(*ff, 0), 0);
+ if(DEBUG(D_PARSE)){
+ dumpw("default targets", target1);
+ dumpr("rules", rules);
+ dumpr("metarules", metarules);
+ dumpv("variables");
+ }
+ if(whatif){
+ insert(whatif, 0);
+ timeinit(whatif->start);
+ freebuf(whatif);
+ }
+ execinit();
+ /* skip assignment args */
+ while(*argv && (**argv == 0))
+ argv++;
+
+ catchnotes();
+ if(*argv == 0){
+ if(target1)
+ for(w = target1; w; w = w->next)
+ mk(w->s);
+ else {
+ fprint(2, "mk: nothing to mk\n");
+ Exit();
+ }
+ } else {
+ if(sflag){
+ for(; *argv; argv++)
+ if(**argv)
+ mk(*argv);
+ } else {
+ Word *head, *tail, *t;
+
+ /* fake a new rule with all the args as prereqs */
+ tail = 0;
+ t = 0;
+ for(; *argv; argv++)
+ if(**argv){
+ if(tail == 0)
+ tail = t = newword(*argv);
+ else {
+ t->next = newword(*argv);
+ t = t->next;
+ }
+ }
+ if(tail->next == 0)
+ mk(tail->s);
+ else {
+ head = newword("command line arguments");
+ addrules(head, tail, strdup(""), VIR, mkinline, 0);
+ mk(head->s);
+ }
+ }
+ }
+ if(uflag)
+ prusage();
+ exits(0);
+ return 0;
+}
+
+void
+badusage(void)
+{
+
+ fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n");
+ Exit();
+}
+
+void *
+Malloc(int n)
+{
+ register void *s;
+
+ s = malloc(n);
+ if(!s) {
+ fprint(2, "mk: cannot alloc %d bytes\n", n);
+ Exit();
+ }
+ return(s);
+}
+
+void *
+Realloc(void *s, int n)
+{
+ if(s)
+ s = realloc(s, n);
+ else
+ s = malloc(n);
+ if(!s) {
+ fprint(2, "mk: cannot alloc %d bytes\n", n);
+ Exit();
+ }
+ return(s);
+}
+
+void
+assert(char *s, int n)
+{
+ if(!n){
+ fprint(2, "mk: Assertion ``%s'' failed.\n", s);
+ Exit();
+ }
+}
+
+void
+regerror(char *s)
+{
+ if(patrule)
+ fprint(2, "mk: %s:%d: regular expression error; %s\n",
+ patrule->file, patrule->line, s);
+ else
+ fprint(2, "mk: %s:%d: regular expression error; %s\n",
+ infile, mkinline, s);
+ Exit();
+}
diff --git a/mk/match.c b/mk/match.c
@@ -0,0 +1,49 @@
+#include "mk.h"
+
+int
+match(char *name, char *template, char *stem, Shell *sh)
+{
+ Rune r;
+ int n;
+
+ while(*name && *template){
+ n = chartorune(&r, template);
+ if (PERCENT(r))
+ break;
+ while (n--)
+ if(*name++ != *template++)
+ return 0;
+ }
+ if(!PERCENT(*template))
+ return 0;
+ n = strlen(name)-strlen(template+1);
+ if (n < 0)
+ return 0;
+ if (strcmp(template+1, name+n))
+ return 0;
+ strncpy(stem, name, n);
+ stem[n] = 0;
+ if(*template == '&')
+ return !sh->charin(stem, "./");
+ return 1;
+}
+
+void
+subst(char *stem, char *template, char *dest)
+{
+ Rune r;
+ char *s;
+ int n;
+
+ while(*template){
+ n = chartorune(&r, template);
+ if (PERCENT(r)) {
+ template += n;
+ for (s = stem; *s; s++)
+ *dest++ = *s;
+ } else
+ while (n--)
+ *dest++ = *template++;
+ }
+ *dest = 0;
+}
diff --git a/mk/mk.1 b/mk/mk.1
@@ -0,0 +1,691 @@
+.TH MK 1
+.SH NAME
+mk \- maintain (make) related files
+.SH SYNOPSIS
+.B mk
+[
+.B -f
+.I mkfile
+] ...
+[
+.I option ...
+]
+[
+.I target ...
+]
+.SH DESCRIPTION
+.I Mk
+uses the dependency rules specified in
+.I mkfile
+to control the update (usually by compilation) of
+.I targets
+(usually files)
+from the source files upon which they depend.
+The
+.I mkfile
+(default
+.LR mkfile )
+contains a
+.I rule
+for each target that identifies the files and other
+targets upon which it depends and an
+.IR sh (1)
+script, a
+.IR recipe ,
+to update the target.
+The script is run if the target does not exist
+or if it is older than any of the files it depends on.
+.I Mkfile
+may also contain
+.I meta-rules
+that define actions for updating implicit targets.
+If no
+.I target
+is specified, the target of the first rule (not meta-rule) in
+.I mkfile
+is updated.
+.PP
+The environment variable
+.B $NPROC
+determines how many targets may be updated simultaneously;
+Some operating systems, e.g., Plan 9, set
+.B $NPROC
+automatically to the number of CPUs on the current machine.
+.PP
+Options are:
+.TP \w'\fL-d[egp]\ 'u
+.B -a
+Assume all targets to be out of date.
+Thus, everything is updated.
+.PD 0
+.TP
+.BR -d [ egp ]
+Produce debugging output
+.RB ( p
+is for parsing,
+.B g
+for graph building,
+.B e
+for execution).
+.TP
+.B -e
+Explain why each target is made.
+.TP
+.B -i
+Force any missing intermediate targets to be made.
+.TP
+.B -k
+Do as much work as possible in the face of errors.
+.TP
+.B -n
+Print, but do not execute, the commands
+needed to update the targets.
+.TP
+.B -s
+Make the command line arguments sequentially rather than in parallel.
+.TP
+.B -t
+Touch (update the modified date of) file targets, without
+executing any recipes.
+.TP
+.BI -w target1 , target2,...
+Pretend the modify time for each
+.I target
+is the current time; useful in conjunction with
+.B -n
+to learn what updates would be triggered by
+modifying the
+.IR targets .
+.PD
+.SS The \fLmkfile\fP
+A
+.I mkfile
+consists of
+.I assignments
+(described under `Environment') and
+.IR rules .
+A rule contains
+.I targets
+and a
+.IR tail .
+A target is a literal string
+and is normally a file name.
+The tail contains zero or more
+.I prerequisites
+and an optional
+.IR recipe ,
+which is an
+.B shell
+script.
+Each line of the recipe must begin with white space.
+A rule takes the form
+.IP
+.EX
+target: prereq1 prereq2
+ \f2recipe using\fP prereq1, prereq2 \f2to build\fP target
+.EE
+.PP
+When the recipe is executed,
+the first character on every line is elided.
+.PP
+After the colon on the target line, a rule may specify
+.IR attributes ,
+described below.
+.PP
+A
+.I meta-rule
+has a target of the form
+.IB A % B
+where
+.I A
+and
+.I B
+are (possibly empty) strings.
+A meta-rule acts as a rule for any potential target whose
+name matches
+.IB A % B
+with
+.B %
+replaced by an arbitrary string, called the
+.IR stem .
+In interpreting a meta-rule,
+the stem is substituted for all occurrences of
+.B %
+in the prerequisite names.
+In the recipe of a meta-rule, the environment variable
+.B $stem
+contains the string matched by the
+.BR % .
+For example, a meta-rule to compile a C program using
+.IR 9c (1)
+might be:
+.IP
+.EX
+%: %.c
+ 9c -c $stem.c
+ 9l -o $stem $stem.o
+.EE
+.PP
+Meta-rules may contain an ampersand
+.B &
+rather than a percent sign
+.BR % .
+A
+.B %
+matches a maximal length string of any characters;
+an
+.B &
+matches a maximal length string of any characters except period
+or slash.
+.PP
+The text of the
+.I mkfile
+is processed as follows.
+Lines beginning with
+.B <
+followed by a file name are replaced by the contents of the named
+file.
+Lines beginning with
+.B "<|"
+followed by a file name are replaced by the output
+of the execution of the named
+file.
+Blank lines and comments, which run from unquoted
+.B #
+characters to the following newline, are deleted.
+The character sequence backslash-newline is deleted,
+so long lines in
+.I mkfile
+may be folded.
+Non-recipe lines are processed by substituting for
+.BI `{ command }
+the output of the
+.I command
+when run by
+.IR sh .
+References to variables are replaced by the variables' values.
+Special characters may be quoted using single quotes
+.BR \&''
+as in
+.IR sh (1).
+.PP
+Assignments and rules are distinguished by
+the first unquoted occurrence of
+.B :
+(rule)
+or
+.B =
+(assignment).
+.PP
+A later rule may modify or override an existing rule under the
+following conditions:
+.TP
+\-
+If the targets of the rules exactly match and one rule
+contains only a prerequisite clause and no recipe, the
+clause is added to the prerequisites of the other rule.
+If either or both targets are virtual, the recipe is
+always executed.
+.TP
+\-
+If the targets of the rules match exactly and the
+prerequisites do not match and both rules
+contain recipes,
+.I mk
+reports an ``ambiguous recipe'' error.
+.TP
+\-
+If the target and prerequisites of both rules match exactly,
+the second rule overrides the first.
+.SS Environment
+Rules may make use of
+shell
+environment variables.
+A legal reference of the form
+.B $OBJ
+or
+.B ${name}
+is expanded as in
+.IR sh (1).
+A reference of the form
+.BI ${name: A % B = C\fL%\fID\fL}\fR,
+where
+.I A, B, C, D
+are (possibly empty) strings,
+has the value formed by expanding
+.B $name
+and substituting
+.I C
+for
+.I A
+and
+.I D
+for
+.I B
+in each word in
+.B $name
+that matches pattern
+.IB A % B\f1.
+.PP
+Variables can be set by
+assignments of the form
+.I
+ var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR
+.br
+Blanks in the
+.I value
+break it into words.
+Such variables are exported
+to the environment of
+recipes as they are executed, unless
+.BR U ,
+the only legal attribute
+.IR attr ,
+is present.
+The initial value of a variable is
+taken from (in increasing order of precedence)
+the default values below,
+.I mk's
+environment, the
+.IR mkfiles ,
+and any command line assignment as an argument to
+.IR mk .
+A variable assignment argument overrides the first (but not any subsequent)
+assignment to that variable.
+.PP
+The variable
+.B MKFLAGS
+contains all the option arguments (arguments starting with
+.L -
+or containing
+.LR = )
+and
+.B MKARGS
+contains all the targets in the call to
+.IR mk .
+.PP
+The variable
+.B MKSHELL
+contains the shell command line
+.I mk
+uses to run recipes.
+If the first word of the command ends in
+.B rc
+or
+.BR rcsh ,
+.I mk
+uses
+.IR rc (1)'s
+quoting rules; otherwise it uses
+.IR sh (1)'s.
+The
+.B MKSHELL
+variable is consulted when the mkfile is read, not when it is executed,
+so that different shells can be used within a single mkfile:
+.IP
+.EX
+MKSHELL=$PLAN9/bin/rc
+use-rc:V:
+ for(i in a b c) echo $i
+
+MKSHELL=sh
+use-sh:V:
+ for i in a b c; do echo $i; done
+.EE
+.LP
+Mkfiles included via
+.B <
+or
+.B <|
+.RI ( q.v. )
+see their own private copy of
+.BR MKSHELL ,
+which always starts set to
+.B sh .
+.PP
+Dynamic information may be included in the mkfile by using a line of the form
+.IP
+\fR<|\fIcommand\fR \fIargs\fR
+.LP
+This runs the command
+.I command
+with the given arguments
+.I args
+and pipes its standard output to
+.I mk
+to be included as part of the mkfile. For instance, the Inferno kernels
+use this technique
+to run a shell command with an awk script and a configuration
+file as arguments in order for
+the
+.I awk
+script to process the file and output a set of variables and their values.
+.SS Execution
+.PP
+During execution,
+.I mk
+determines which targets must be updated, and in what order,
+to build the
+.I names
+specified on the command line.
+It then runs the associated recipes.
+.PP
+A target is considered up to date if it has no prerequisites or
+if all its prerequisites are up to date and it is newer
+than all its prerequisites.
+Once the recipe for a target has executed, the target is
+considered up to date.
+.PP
+The date stamp
+used to determine if a target is up to date is computed
+differently for different types of targets.
+If a target is
+.I virtual
+(the target of a rule with the
+.B V
+attribute),
+its date stamp is initially zero; when the target is
+updated the date stamp is set to
+the most recent date stamp of its prerequisites.
+Otherwise, if a target does not exist as a file,
+its date stamp is set to the most recent date stamp of its prerequisites,
+or zero if it has no prerequisites.
+Otherwise, the target is the name of a file and
+the target's date stamp is always that file's modification date.
+The date stamp is computed when the target is needed in
+the execution of a rule; it is not a static value.
+.PP
+Nonexistent targets that have prerequisites
+and are themselves prerequisites are treated specially.
+Such a target
+.I t
+is given the date stamp of its most recent prerequisite
+and if this causes all the targets which have
+.I t
+as a prerequisite to be up to date,
+.I t
+is considered up to date.
+Otherwise,
+.I t
+is made in the normal fashion.
+The
+.B -i
+flag overrides this special treatment.
+.PP
+Files may be made in any order that respects
+the preceding restrictions.
+.PP
+A recipe is executed by supplying the recipe as standard input to
+the command
+.BR /bin/sh .
+(Note that unlike
+.IR make ,
+.I mk
+feeds the entire recipe to the shell rather than running each line
+of the recipe separately.)
+The environment is augmented by the following variables:
+.TP 14
+.B $alltarget
+all the targets of this rule.
+.TP
+.B $newprereq
+the prerequisites that caused this rule to execute.
+.TP
+.B $newmember
+the prerequisites that are members of an aggregate
+that caused this rule to execute.
+When the prerequisites of a rule are members of an
+aggregate,
+.B $newprereq
+contains the name of the aggregate and out of date
+members, while
+.B $newmember
+contains only the name of the members.
+.TP
+.B $nproc
+the process slot for this recipe.
+It satisfies
+.RB 0≤ $nproc < $NPROC .
+.TP
+.B $pid
+the process id for the
+.I mk
+executing the recipe.
+.TP
+.B $prereq
+all the prerequisites for this rule.
+.TP
+.B $stem
+if this is a meta-rule,
+.B $stem
+is the string that matched
+.B %
+or
+.BR & .
+Otherwise, it is empty.
+For regular expression meta-rules (see below), the variables
+.LR stem0 ", ...,"
+.L stem9
+are set to the corresponding subexpressions.
+.TP
+.B $target
+the targets for this rule that need to be remade.
+.PP
+These variables are available only during the execution of a recipe,
+not while evaluating the
+.IR mkfile .
+.PP
+Unless the rule has the
+.B Q
+attribute,
+the recipe is printed prior to execution
+with recognizable environment variables expanded.
+Commands returning error status
+cause
+.I mk
+to terminate.
+.PP
+Recipes and backquoted
+.B rc
+commands in places such as assignments
+execute in a copy of
+.I mk's
+environment; changes they make to
+environment variables are not visible from
+.IR mk .
+.PP
+Variable substitution in a rule is done when
+the rule is read; variable substitution in the recipe is done
+when the recipe is executed. For example:
+.IP
+.EX
+bar=a.c
+foo: $bar
+ $CC -o foo $bar
+bar=b.c
+.EE
+.PP
+will compile
+.B b.c
+into
+.BR foo ,
+if
+.B a.c
+is newer than
+.BR foo .
+.SS Aggregates
+Names of the form
+.IR a ( b )
+refer to member
+.I b
+of the aggregate
+.IR a .
+Currently, the only aggregates supported are
+.I 9ar
+(see
+.IR 9c (1))
+archives.
+.SS Attributes
+The colon separating the target from the prerequisites
+may be
+immediately followed by
+.I attributes
+and another colon.
+The attributes are:
+.TP
+.B D
+If the recipe exits with a non-null status, the target is deleted.
+.TP
+.B E
+Continue execution if the recipe draws errors.
+.TP
+.B N
+If there is no recipe, the target has its time updated.
+.TP
+.B n
+The rule is a meta-rule that cannot be a target of a virtual rule.
+Only files match the pattern in the target.
+.TP
+.B P
+The characters after the
+.B P
+until the terminating
+.B :
+are taken as a program name.
+It will be invoked as
+.B "sh -c prog 'arg1' 'arg2'"
+and should return a zero exit status
+if and only if arg1 is up to date with respect to arg2.
+Date stamps are still propagated in the normal way.
+.TP
+.B Q
+The recipe is not printed prior to execution.
+.TP
+.B R
+The rule is a meta-rule using regular expressions.
+In the rule,
+.B %
+has no special meaning.
+The target is interpreted as a regular expression as defined in
+.IR regexp (7).
+The prerequisites may contain references
+to subexpressions in form
+.BI \e n\f1,
+as in the substitute command of
+.IR sed (1).
+.TP
+.B U
+The targets are considered to have been updated
+even if the recipe did not do so.
+.TP
+.B V
+The targets of this rule are marked as virtual.
+They are distinct from files of the same name.
+.PD
+.SH EXAMPLES
+A simple mkfile to compile a program:
+.IP
+.EX
+.ta 8n +8n +8n +8n +8n +8n +8n
+</$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.
diff --git a/mk/mk.c b/mk/mk.c
@@ -0,0 +1,234 @@
+#include "mk.h"
+
+int runerrs;
+
+void
+mk(char *target)
+{
+ Node *node;
+ int did = 0;
+
+ nproc(); /* it can be updated dynamically */
+ nrep(); /* it can be updated dynamically */
+ runerrs = 0;
+ node = graph(target);
+ if(DEBUG(D_GRAPH)){
+ dumpn("new target\n", node);
+ Bflush(&bout);
+ }
+ clrmade(node);
+ while(node->flags&NOTMADE){
+ if(work(node, (Node *)0, (Arc *)0))
+ did = 1; /* found something to do */
+ else {
+ if(waitup(1, (int *)0) > 0){
+ if(node->flags&(NOTMADE|BEINGMADE)){
+ assert("must be run errors", runerrs);
+ break; /* nothing more waiting */
+ }
+ }
+ }
+ }
+ if(node->flags&BEINGMADE)
+ waitup(-1, (int *)0);
+ while(jobs)
+ waitup(-2, (int *)0);
+ assert("target didn't get done", runerrs || (node->flags&MADE));
+ if(did == 0)
+ Bprint(&bout, "mk: '%s' is up to date\n", node->name);
+}
+
+void
+clrmade(Node *n)
+{
+ Arc *a;
+
+ n->flags &= ~(CANPRETEND|PRETENDING);
+ if(strchr(n->name, '(') ==0 || n->time)
+ n->flags |= CANPRETEND;
+ MADESET(n, NOTMADE);
+ for(a = n->prereqs; a; a = a->next)
+ if(a->n)
+ clrmade(a->n);
+}
+
+static void
+unpretend(Node *n)
+{
+ MADESET(n, NOTMADE);
+ n->flags &= ~(CANPRETEND|PRETENDING);
+ n->time = 0;
+}
+
+static char*
+dir(void)
+{
+ static char buf[1024];
+
+ return getcwd(buf, sizeof buf);
+}
+
+int
+work(Node *node, Node *p, Arc *parc)
+{
+ Arc *a, *ra;
+ int weoutofdate;
+ int ready;
+ int did = 0;
+
+ /*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time); */
+ if(node->flags&BEINGMADE)
+ return(did);
+ if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){
+ if(explain)
+ fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n",
+ node->name, node->time, p->name, p->time);
+ unpretend(node);
+ }
+ /*
+ have a look if we are pretending in case
+ someone has been unpretended out from underneath us
+ */
+ if(node->flags&MADE){
+ if(node->flags&PRETENDING){
+ node->time = 0;
+ }else
+ return(did);
+ }
+ /* consider no prerequsite case */
+ if(node->prereqs == 0){
+ if(node->time == 0){
+ fprint(2, "mk: don't know how to make '%s' in %s\n", node->name, dir());
+ if(kflag){
+ node->flags |= BEINGMADE;
+ runerrs++;
+ } else
+ Exit();
+ } else
+ MADESET(node, MADE);
+ return(did);
+ }
+ /*
+ now see if we are out of date or what
+ */
+ ready = 1;
+ weoutofdate = aflag;
+ ra = 0;
+ for(a = node->prereqs; a; a = a->next)
+ if(a->n){
+ did = work(a->n, node, a) || did;
+ if(a->n->flags&(NOTMADE|BEINGMADE))
+ ready = 0;
+ if(outofdate(node, a, 0)){
+ weoutofdate = 1;
+ if((ra == 0) || (ra->n == 0)
+ || (ra->n->time < a->n->time))
+ ra = a;
+ }
+ } else {
+ if(node->time == 0){
+ if(ra == 0)
+ ra = a;
+ weoutofdate = 1;
+ }
+ }
+ if(ready == 0) /* can't do anything now */
+ return(did);
+ if(weoutofdate == 0){
+ MADESET(node, MADE);
+ return(did);
+ }
+ /*
+ can we pretend to be made?
+ */
+ if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND))
+ && p && ra->n && !outofdate(p, ra, 0)){
+ node->flags &= ~CANPRETEND;
+ MADESET(node, MADE);
+ if(explain && ((node->flags&PRETENDING) == 0))
+ fprint(1, "pretending %s has time %ld\n", node->name, node->time);
+ node->flags |= PRETENDING;
+ return(did);
+ }
+ /*
+ node is out of date and we REALLY do have to do something.
+ quickly rescan for pretenders
+ */
+ for(a = node->prereqs; a; a = a->next)
+ if(a->n && (a->n->flags&PRETENDING)){
+ if(explain)
+ Bprint(&bout, "unpretending %s because of %s because of %s\n",
+ a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites");
+
+ unpretend(a->n);
+ did = work(a->n, node, a) || did;
+ ready = 0;
+ }
+ if(ready == 0) /* try later unless nothing has happened for -k's sake */
+ return(did || work(node, p, parc));
+ did = dorecipe(node) || did;
+ return(did);
+}
+
+void
+update(int fake, Node *node)
+{
+ Arc *a;
+
+ MADESET(node, fake? BEINGMADE : MADE);
+ if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){
+ node->time = timeof(node->name, 1);
+ node->flags &= ~(CANPRETEND|PRETENDING);
+ for(a = node->prereqs; a; a = a->next)
+ if(a->prog)
+ outofdate(node, a, 1);
+ } else {
+ node->time = 1;
+ for(a = node->prereqs; a; a = a->next)
+ if(a->n && outofdate(node, a, 1))
+ node->time = a->n->time;
+ }
+/* print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);*/
+}
+
+static int
+pcmp(char *prog, char *p, char *q, Shell *sh, Word *shcmd)
+{
+ char buf[3*NAMEBLOCK];
+ int pid;
+
+ Bflush(&bout);
+ snprint(buf, sizeof buf, "%s '%s' '%s'\n", prog, p, q);
+ pid = pipecmd(buf, 0, 0, sh, shcmd);
+ while(waitup(-3, &pid) >= 0)
+ ;
+ return(pid? 2:1);
+}
+
+int
+outofdate(Node *node, Arc *arc, int eval)
+{
+ char buf[3*NAMEBLOCK], *str;
+ Symtab *sym;
+ int ret;
+
+ str = 0;
+ if(arc->prog){
+ snprint(buf, sizeof buf, "%s%c%s", node->name, 0377, arc->n->name);
+ sym = symlook(buf, S_OUTOFDATE, 0);
+ if(sym == 0 || eval){
+ if(sym == 0)
+ str = strdup(buf);
+ ret = pcmp(arc->prog, node->name, arc->n->name, arc->r->shellt, arc->r->shellcmd);
+ if(sym)
+ sym->u.value = ret;
+ else
+ symlook(str, S_OUTOFDATE, (void *)(uintptr)ret);
+ } else
+ ret = sym->u.value;
+ return(ret-1);
+ } else if(strchr(arc->n->name, '(') && arc->n->time == 0) /* missing archive member */
+ return 1;
+ else
+ return node->time <= arc->n->time;
+}
diff --git a/mk/mk.h b/mk/mk.h
@@ -0,0 +1,185 @@
+#include "sys.h"
+
+#undef assert
+#define assert mkassert
+extern Biobuf bout;
+
+typedef struct Bufblock
+{
+ struct Bufblock *next;
+ char *start;
+ char *end;
+ char *current;
+} Bufblock;
+
+typedef struct Word
+{
+ char *s;
+ struct Word *next;
+} Word;
+
+typedef struct Envy
+{
+ char *name;
+ Word *values;
+} Envy;
+
+extern Envy *envy;
+
+typedef struct Shell
+{
+ char *name;
+ char *termchars; /* used in parse.c to isolate assignment attribute */
+ int iws; /* inter-word separator in environment */
+ char *(*charin)(char*, char*); /* search for unescaped characters */
+ char *(*expandquote)(char*, Rune, Bufblock*); /* extract escaped token */
+ int (*escapetoken)(Biobuf*, Bufblock*, int, int); /* input escaped token */
+ char *(*copyq)(char*, Rune, Bufblock*); /* check for quoted strings */
+ int (*matchname)(char*); /* does name match */
+} Shell;
+
+typedef struct Rule
+{
+ char *target; /* one target */
+ Word *tail; /* constituents of targets */
+ char *recipe; /* do it ! */
+ short attr; /* attributes */
+ short line; /* source line */
+ char *file; /* source file */
+ Word *alltargets; /* all the targets */
+ int rule; /* rule number */
+ Reprog *pat; /* reg exp goo */
+ char *prog; /* to use in out of date */
+ struct Rule *chain; /* hashed per target */
+ struct Rule *next;
+ Shell *shellt; /* shell to use with this rule */
+ Word *shellcmd;
+} Rule;
+
+extern Rule *rules, *metarules, *patrule;
+
+/* Rule.attr */
+#define META 0x0001
+#define UNUSED 0x0002
+#define UPD 0x0004
+#define QUIET 0x0008
+#define VIR 0x0010
+#define REGEXP 0x0020
+#define NOREC 0x0040
+#define DEL 0x0080
+#define NOVIRT 0x0100
+
+#define NREGEXP 10
+
+typedef struct Arc
+{
+ short flag;
+ struct Node *n;
+ Rule *r;
+ char *stem;
+ char *prog;
+ char *match[NREGEXP];
+ struct Arc *next;
+} Arc;
+
+ /* Arc.flag */
+#define TOGO 1
+
+typedef struct Node
+{
+ char *name;
+ long time;
+ unsigned short flags;
+ Arc *prereqs;
+ struct Node *next; /* list for a rule */
+} Node;
+
+ /* Node.flags */
+#define VIRTUAL 0x0001
+#define CYCLE 0x0002
+#define READY 0x0004
+#define CANPRETEND 0x0008
+#define PRETENDING 0x0010
+#define NOTMADE 0x0020
+#define BEINGMADE 0x0040
+#define MADE 0x0080
+#define MADESET(n,m) n->flags = (n->flags&~(NOTMADE|BEINGMADE|MADE))|(m)
+#define PROBABLE 0x0100
+#define VACUOUS 0x0200
+#define NORECIPE 0x0400
+#define DELETE 0x0800
+#define NOMINUSE 0x1000
+
+typedef struct Job
+{
+ Rule *r; /* master rule for job */
+ Node *n; /* list of node targets */
+ char *stem;
+ char **match;
+ Word *p; /* prerequistes */
+ Word *np; /* new prerequistes */
+ Word *t; /* targets */
+ Word *at; /* all targets */
+ int nproc; /* slot number */
+ struct Job *next;
+} Job;
+extern Job *jobs;
+
+typedef struct Symtab
+{
+ short space;
+ char *name;
+ union {
+ void *ptr;
+ uintptr value;
+ } u;
+ struct Symtab *next;
+} Symtab;
+
+enum {
+ S_VAR, /* variable -> value */
+ S_TARGET, /* target -> rule */
+ S_TIME, /* file -> time */
+ S_PID, /* pid -> products */
+ S_NODE, /* target name -> node */
+ S_AGG, /* aggregate -> time */
+ S_BITCH, /* bitched about aggregate not there */
+ S_NOEXPORT, /* var -> noexport */
+ S_OVERRIDE, /* can't override */
+ S_OUTOFDATE, /* n1\377n2 -> 2(outofdate) or 1(not outofdate) */
+ S_MAKEFILE, /* target -> node */
+ S_MAKEVAR, /* dumpable mk variable */
+ S_EXPORTED, /* var -> current exported value */
+ S_WESET, /* variable; we set in the mkfile */
+ S_INTERNAL /* an internal mk variable (e.g., stem, target) */
+};
+
+extern int debug;
+extern int nflag, tflag, iflag, kflag, aflag, mflag;
+extern int mkinline;
+extern char *infile;
+extern int nreps;
+extern char *explain;
+extern Shell *shellt;
+extern Word *shellcmd;
+
+extern Shell shshell, rcshell;
+
+#define SYNERR(l) (fprint(2, "mk: %s:%d: syntax error; ", infile, ((l)>=0)?(l):mkinline))
+#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line))
+#define NAMEBLOCK 1000
+#define BIGBLOCK 20000
+
+#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n'))
+#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r)))
+
+#define DEBUG(x) (debug&(x))
+#define D_PARSE 0x01
+#define D_GRAPH 0x02
+#define D_EXEC 0x04
+
+#define LSEEK(f,o,p) seek(f,o,p)
+
+#define PERCENT(ch) (((ch) == '%') || ((ch) == '&'))
+
+#include "fns.h"
diff --git a/mk/mkfile b/mk/mkfile
@@ -0,0 +1,37 @@
+<$PLAN9/src/mkhdr
+
+TARG=mk
+
+OFILES=\
+ arc.$O\
+ archive.$O\
+ bufblock.$O\
+ env.$O\
+ file.$O\
+ graph.$O\
+ job.$O\
+ lex.$O\
+ main.$O\
+ match.$O\
+ mk.$O\
+ parse.$O\
+ recipe.$O\
+ rc.$O\
+ rule.$O\
+ run.$O\
+ sh.$O\
+ shell.$O\
+ shprint.$O\
+ symtab.$O\
+ var.$O\
+ varsub.$O\
+ word.$O\
+ unix.$O\
+
+HFILES=\
+ mk.h\
+ fns.h\
+
+
+<$PLAN9/src/mkone
+
diff --git a/mk/mkfile.test b/mk/mkfile.test
@@ -0,0 +1,12 @@
+MKSHELL=$PLAN9/bin/rc
+use-rc:V:
+ for(i in a b c)
+ echo $i
+
+MKSHELL=/bin/sh
+use-sh:V:
+ for i in a b c
+ do
+ echo $i
+ done
+
diff --git a/mk/parse.c b/mk/parse.c
@@ -0,0 +1,318 @@
+#include "mk.h"
+
+char *infile;
+int mkinline;
+static int rhead(char *, Word **, Word **, int *, char **);
+static char *rbody(Biobuf*);
+extern Word *target1;
+
+void
+parse(char *f, int fd, int varoverride)
+{
+ int hline;
+ char *body;
+ Word *head, *tail;
+ int attr, set, pid;
+ char *prog, *p;
+ int newfd;
+ Biobuf in;
+ Bufblock *buf;
+ char *err;
+
+ if(fd < 0){
+ fprint(2, "open %s: %r\n", f);
+ Exit();
+ }
+ pushshell();
+ ipush();
+ infile = strdup(f);
+ mkinline = 1;
+ Binit(&in, fd, OREAD);
+ buf = newbuf();
+ while(assline(&in, buf)){
+ hline = mkinline;
+ switch(rhead(buf->start, &head, &tail, &attr, &prog))
+ {
+ case '<':
+ p = wtos(tail, ' ');
+ if(*p == 0){
+ SYNERR(-1);
+ fprint(2, "missing include file name\n");
+ Exit();
+ }
+ newfd = open(p, OREAD);
+ if(newfd < 0){
+ fprint(2, "warning: skipping missing include file %s: %r\n", p);
+ } else
+ parse(p, newfd, 0);
+ break;
+ case '|':
+ p = wtos(tail, ' ');
+ if(*p == 0){
+ SYNERR(-1);
+ fprint(2, "missing include program name\n");
+ Exit();
+ }
+ execinit();
+ pid=pipecmd(p, envy, &newfd, shellt, shellcmd);
+ if(newfd < 0){
+ fprint(2, "warning: skipping missing program file %s: %r\n", p);
+ } else
+ parse(p, newfd, 0);
+ while(waitup(-3, &pid) >= 0)
+ ;
+ if(pid != 0){
+ fprint(2, "bad include program status\n");
+ Exit();
+ }
+ break;
+ case ':':
+ body = rbody(&in);
+ addrules(head, tail, body, attr, hline, prog);
+ break;
+ case '=':
+ if(head->next){
+ SYNERR(-1);
+ fprint(2, "multiple vars on left side of assignment\n");
+ Exit();
+ }
+ if(symlook(head->s, S_OVERRIDE, 0)){
+ set = varoverride;
+ } else {
+ set = 1;
+ if(varoverride)
+ symlook(head->s, S_OVERRIDE, (void *)"");
+ }
+ if(set){
+/*
+char *cp;
+dumpw("tail", tail);
+cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
+*/
+ setvar(head->s, (void *) tail);
+ symlook(head->s, S_WESET, (void *)"");
+ if(strcmp(head->s, "MKSHELL") == 0){
+ if((err = setshell(tail)) != nil){
+ SYNERR(hline);
+ fprint(2, "%s\n", err);
+ Exit();
+ break;
+ }
+ }
+ }
+ if(attr)
+ symlook(head->s, S_NOEXPORT, (void *)"");
+ break;
+ default:
+ SYNERR(hline);
+ fprint(2, "expected one of :<=\n");
+ Exit();
+ break;
+ }
+ }
+ close(fd);
+ freebuf(buf);
+ ipop();
+ popshell();
+}
+
+void
+addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog)
+{
+ Word *w;
+
+ assert("addrules args", head && body);
+ /* tuck away first non-meta rule as default target*/
+ if(target1 == 0 && !(attr®EXP)){
+ for(w = head; w; w = w->next)
+ if(shellt->charin(w->s, "%&"))
+ break;
+ if(w == 0)
+ target1 = wdup(head);
+ }
+ for(w = head; w; w = w->next)
+ addrule(w->s, tail, body, head, attr, hline, prog);
+}
+
+static int
+rhead(char *line, Word **h, Word **t, int *attr, char **prog)
+{
+ char *p;
+ char *pp;
+ int sep;
+ Rune r;
+ int n;
+ Word *w;
+
+ p = shellt->charin(line,":=<");
+ if(p == 0)
+ return('?');
+ sep = *p;
+ *p++ = 0;
+ if(sep == '<' && *p == '|'){
+ sep = '|';
+ p++;
+ }
+ *attr = 0;
+ *prog = 0;
+ if(sep == '='){
+ pp = shellt->charin(p, shellt->termchars); /* termchars is shell-dependent */
+ if (pp && *pp == '=') {
+ while (p != pp) {
+ n = chartorune(&r, p);
+ switch(r)
+ {
+ default:
+ SYNERR(-1);
+ fprint(2, "unknown attribute '%c'\n",*p);
+ Exit();
+ case 'U':
+ *attr = 1;
+ break;
+ }
+ p += n;
+ }
+ p++; /* skip trailing '=' */
+ }
+ }
+ if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){
+ while (*p) {
+ n = chartorune(&r, p);
+ if (r == ':')
+ break;
+ p += n;
+ switch(r)
+ {
+ default:
+ SYNERR(-1);
+ fprint(2, "unknown attribute '%c'\n", p[-1]);
+ Exit();
+ case 'D':
+ *attr |= DEL;
+ break;
+ case 'E':
+ *attr |= NOMINUSE;
+ break;
+ case 'n':
+ *attr |= NOVIRT;
+ break;
+ case 'N':
+ *attr |= NOREC;
+ break;
+ case 'P':
+ pp = utfrune(p, ':');
+ if (pp == 0 || *pp == 0)
+ goto eos;
+ *pp = 0;
+ *prog = strdup(p);
+ *pp = ':';
+ p = pp;
+ break;
+ case 'Q':
+ *attr |= QUIET;
+ break;
+ case 'R':
+ *attr |= REGEXP;
+ break;
+ case 'U':
+ *attr |= UPD;
+ break;
+ case 'V':
+ *attr |= VIR;
+ break;
+ }
+ }
+ if (*p++ != ':') {
+ eos:
+ SYNERR(-1);
+ fprint(2, "missing trailing :\n");
+ Exit();
+ }
+ }
+ *h = w = stow(line);
+ if(*w->s == 0 && sep != '<' && sep != '|' && sep != 'S') {
+ SYNERR(mkinline-1);
+ fprint(2, "no var on left side of assignment/rule\n");
+ Exit();
+ }
+ *t = stow(p);
+ return(sep);
+}
+
+static char *
+rbody(Biobuf *in)
+{
+ Bufblock *buf;
+ int r, lastr;
+ char *p;
+
+ lastr = '\n';
+ buf = newbuf();
+ for(;;){
+ r = Bgetrune(in);
+ if (r < 0)
+ break;
+ if (lastr == '\n') {
+ if (r == '#')
+ rinsert(buf, r);
+ else if (r != ' ' && r != '\t') {
+ Bungetrune(in);
+ break;
+ }
+ } else
+ rinsert(buf, r);
+ lastr = r;
+ if (r == '\n')
+ mkinline++;
+ }
+ insert(buf, 0);
+ p = strdup(buf->start);
+ freebuf(buf);
+ return p;
+}
+
+struct input
+{
+ char *file;
+ int line;
+ struct input *next;
+};
+static struct input *inputs = 0;
+
+void
+ipush(void)
+{
+ struct input *in, *me;
+
+ me = (struct input *)Malloc(sizeof(*me));
+ me->file = infile;
+ me->line = mkinline;
+ me->next = 0;
+ if(inputs == 0)
+ inputs = me;
+ else {
+ for(in = inputs; in->next; )
+ in = in->next;
+ in->next = me;
+ }
+}
+
+void
+ipop(void)
+{
+ struct input *in, *me;
+
+ assert("pop input list", inputs != 0);
+ if(inputs->next == 0){
+ me = inputs;
+ inputs = 0;
+ } else {
+ for(in = inputs; in->next->next; )
+ in = in->next;
+ me = in->next;
+ in->next = 0;
+ }
+ infile = me->file;
+ mkinline = me->line;
+ free((char *)me);
+}
diff --git a/mk/rc.c b/mk/rc.c
@@ -0,0 +1,194 @@
+#include "mk.h"
+
+/*
+ * This file contains functions that depend on rc's syntax. Most
+ * of the routines extract strings observing rc's escape conventions
+ */
+
+
+/*
+ * skip a token in single quotes.
+ */
+static char *
+squote(char *cp)
+{
+ Rune r;
+ int n;
+
+ while(*cp){
+ n = chartorune(&r, cp);
+ if(r == '\'') {
+ n += chartorune(&r, cp+n);
+ if(r != '\'')
+ return(cp);
+ }
+ cp += n;
+ }
+ SYNERR(-1); /* should never occur */
+ fprint(2, "missing closing '\n");
+ return 0;
+}
+
+/*
+ * search a string for characters in a pattern set
+ * characters in quotes and variable generators are escaped
+ */
+char *
+rccharin(char *cp, char *pat)
+{
+ Rune r;
+ int n, vargen;
+
+ vargen = 0;
+ while(*cp){
+ n = chartorune(&r, cp);
+ switch(r){
+ case '\'': /* skip quoted string */
+ cp = squote(cp+1); /* n must = 1 */
+ if(!cp)
+ return 0;
+ break;
+ case '$':
+ if(*(cp+1) == '{')
+ vargen = 1;
+ break;
+ case '}':
+ if(vargen)
+ vargen = 0;
+ else if(utfrune(pat, r))
+ return cp;
+ break;
+ default:
+ if(vargen == 0 && utfrune(pat, r))
+ return cp;
+ break;
+ }
+ cp += n;
+ }
+ if(vargen){
+ SYNERR(-1);
+ fprint(2, "missing closing } in pattern generator\n");
+ }
+ return 0;
+}
+
+/*
+ * extract an escaped token. Possible escape chars are single-quote,
+ * double-quote,and backslash. Only the first is valid for rc. the
+ * others are just inserted into the receiving buffer.
+ */
+char*
+rcexpandquote(char *s, Rune r, Bufblock *b)
+{
+ if (r != '\'') {
+ rinsert(b, r);
+ return s;
+ }
+
+ while(*s){
+ s += chartorune(&r, s);
+ if(r == '\'') {
+ if(*s == '\'')
+ s++;
+ else
+ return s;
+ }
+ rinsert(b, r);
+ }
+ return 0;
+}
+
+/*
+ * Input an escaped token. Possible escape chars are single-quote,
+ * double-quote and backslash. Only the first is a valid escape for
+ * rc; the others are just inserted into the receiving buffer.
+ */
+int
+rcescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+ int c, line;
+
+ if(esc != '\'')
+ return 1;
+
+ line = mkinline;
+ while((c = nextrune(bp, 0)) > 0){
+ if(c == '\''){
+ if(preserve)
+ rinsert(buf, c);
+ c = Bgetrune(bp);
+ if (c < 0)
+ break;
+ if(c != '\''){
+ Bungetrune(bp);
+ return 1;
+ }
+ }
+ rinsert(buf, c);
+ }
+ SYNERR(line); fprint(2, "missing closing %c\n", esc);
+ return 0;
+}
+
+/*
+ * copy a single-quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Bufblock *buf)
+{
+ Rune r;
+
+ while(*s){
+ s += chartorune(&r, s);
+ rinsert(buf, r);
+ if(r == '\'')
+ break;
+ }
+ return s;
+}
+/*
+ * check for quoted strings. backquotes are handled here; single quotes above.
+ * s points to char after opening quote, q.
+ */
+char *
+rccopyq(char *s, Rune q, Bufblock *buf)
+{
+ if(q == '\'') /* copy quoted string */
+ return copysingle(s, buf);
+
+ if(q != '`') /* not quoted */
+ return s;
+
+ while(*s){ /* copy backquoted string */
+ s += chartorune(&q, s);
+ rinsert(buf, q);
+ if(q == '}')
+ break;
+ if(q == '\'')
+ s = copysingle(s, buf); /* copy quoted string */
+ }
+ return s;
+}
+
+static int
+rcmatchname(char *name)
+{
+ char *p;
+
+ if((p = strrchr(name, '/')) != nil)
+ name = p+1;
+ if(name[0] == 'r' && name[1] == 'c')
+ return 1;
+ return 0;
+}
+
+Shell rcshell = {
+ "rc",
+ "'= \t",
+ '\1',
+ rccharin,
+ rcexpandquote,
+ rcescapetoken,
+ rccopyq,
+ rcmatchname
+};
diff --git a/mk/recipe.c b/mk/recipe.c
@@ -0,0 +1,117 @@
+#include "mk.h"
+
+int
+dorecipe(Node *node)
+{
+ char buf[BIGBLOCK];
+ register Node *n;
+ Rule *r = 0;
+ Arc *a, *aa;
+ Word head, ahead, lp, ln, *w, *ww, *aw;
+ Symtab *s;
+ int did = 0;
+
+ aa = 0;
+ /*
+ pick up the rule
+ */
+ for(a = node->prereqs; a; a = a->next)
+ if(*a->r->recipe)
+ r = (aa = a)->r;
+ /*
+ no recipe? go to buggery!
+ */
+ if(r == 0){
+ if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){
+ fprint(2, "mk: no recipe to make '%s'\n", node->name);
+ Exit();
+ }
+ if(strchr(node->name, '(') && node->time == 0)
+ MADESET(node, MADE);
+ else
+ update(0, node);
+ if(tflag){
+ if(!(node->flags&VIRTUAL))
+ touch(node->name);
+ else if(explain)
+ Bprint(&bout, "no touch of virtual '%s'\n", node->name);
+ }
+ return(did);
+ }
+ /*
+ build the node list
+ */
+ node->next = 0;
+ head.next = 0;
+ ww = &head;
+ ahead.next = 0;
+ aw = &ahead;
+ if(r->attr®EXP){
+ ww->next = newword(node->name);
+ aw->next = newword(node->name);
+ } else {
+ for(w = r->alltargets; w; w = w->next){
+ if(r->attr&META)
+ subst(aa->stem, w->s, buf);
+ else
+ strcpy(buf, w->s);
+ aw->next = newword(buf);
+ aw = aw->next;
+ if((s = symlook(buf, S_NODE, 0)) == 0)
+ continue; /* not a node we are interested in */
+ n = s->u.ptr;
+ if(aflag == 0 && n->time) {
+ for(a = n->prereqs; a; a = a->next)
+ if(a->n && outofdate(n, a, 0))
+ break;
+ if(a == 0)
+ continue;
+ }
+ ww->next = newword(buf);
+ ww = ww->next;
+ if(n == node) continue;
+ n->next = node->next;
+ node->next = n;
+ }
+ }
+ for(n = node; n; n = n->next)
+ if((n->flags&READY) == 0)
+ return(did);
+ /*
+ gather the params for the job
+ */
+ lp.next = ln.next = 0;
+ for(n = node; n; n = n->next){
+ for(a = n->prereqs; a; a = a->next){
+ if(a->n){
+ addw(&lp, a->n->name);
+ if(outofdate(n, a, 0)){
+ addw(&ln, a->n->name);
+ if(explain)
+ fprint(1, "%s(%ld) < %s(%ld)\n",
+ n->name, n->time, a->n->name, a->n->time);
+ }
+ } else {
+ if(explain)
+ fprint(1, "%s has no prerequisites\n",
+ n->name);
+ }
+ }
+ MADESET(n, BEINGMADE);
+ }
+ /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*/
+ run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next));
+ return(1);
+}
+
+void
+addw(Word *w, char *s)
+{
+ Word *lw;
+
+ for(lw = w; w = w->next; lw = w){
+ if(strcmp(s, w->s) == 0)
+ return;
+ }
+ lw->next = newword(s);
+}
diff --git a/mk/rule.c b/mk/rule.c
@@ -0,0 +1,112 @@
+#include "mk.h"
+
+static Rule *lr, *lmr;
+static int rcmp(Rule *r, char *target, Word *tail);
+static int nrules = 0;
+
+void
+addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, char *prog)
+{
+ Rule *r;
+ Rule *rr;
+ Symtab *sym;
+ int reuse;
+
+ r = 0;
+ reuse = 0;
+ if(sym = symlook(head, S_TARGET, 0)){
+ for(r = sym->u.ptr; r; r = r->chain)
+ if(rcmp(r, head, tail) == 0){
+ reuse = 1;
+ break;
+ }
+ }
+ if(r == 0)
+ r = (Rule *)Malloc(sizeof(Rule));
+ r->shellt = shellt;
+ r->shellcmd = shellcmd;
+ r->target = head;
+ r->tail = tail;
+ r->recipe = body;
+ r->line = hline;
+ r->file = infile;
+ r->attr = attr;
+ r->alltargets = ahead;
+ r->prog = prog;
+ r->rule = nrules++;
+ if(!reuse){
+ rr = symlook(head, S_TARGET, (void *)r)->u.ptr;
+ if(rr != r){
+ r->chain = rr->chain;
+ rr->chain = r;
+ } else
+ r->chain = 0;
+ }
+ if(!reuse)
+ r->next = 0;
+ if((attr®EXP) || shellt->charin(head, "%&")){
+ r->attr |= META;
+ if(reuse)
+ return;
+ if(attr®EXP){
+ patrule = r;
+ r->pat = regcomp(head);
+ }
+ if(metarules == 0)
+ metarules = lmr = r;
+ else {
+ lmr->next = r;
+ lmr = r;
+ }
+ } else {
+ if(reuse)
+ return;
+ r->pat = 0;
+ if(rules == 0)
+ rules = lr = r;
+ else {
+ lr->next = r;
+ lr = r;
+ }
+ }
+}
+
+void
+dumpr(char *s, Rule *r)
+{
+ if(r == nil)
+ return;
+ Bprint(&bout, "%s: start=%ld shelltype=%s shellcmd=%s\n",
+ s, r, r->shellt->name, wtos(r->shellcmd, ' '));
+ for(; r; r = r->next){
+ Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'",
+ r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' '));
+ if(r->prog)
+ Bprint(&bout, " prog='%s'", r->prog);
+ Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' '));
+ Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe);
+ }
+}
+
+static int
+rcmp(Rule *r, char *target, Word *tail)
+{
+ Word *w;
+
+ if(strcmp(r->target, target))
+ return 1;
+ for(w = r->tail; w && tail; w = w->next, tail = tail->next)
+ if(strcmp(w->s, tail->s))
+ return 1;
+ return(w || tail);
+}
+
+char *
+rulecnt(void)
+{
+ char *s;
+
+ s = Malloc(nrules);
+ memset(s, 0, nrules);
+ return(s);
+}
diff --git a/mk/run.c b/mk/run.c
@@ -0,0 +1,296 @@
+#include "mk.h"
+
+typedef struct Event
+{
+ int pid;
+ Job *job;
+} Event;
+static Event *events;
+static int nevents, nrunning, nproclimit;
+
+typedef struct Process
+{
+ int pid;
+ int status;
+ struct Process *b, *f;
+} Process;
+static Process *phead, *pfree;
+static void sched(void);
+static void pnew(int, int), pdelete(Process *);
+
+int pidslot(int);
+
+void
+run(Job *j)
+{
+ Job *jj;
+
+ if(jobs){
+ for(jj = jobs; jj->next; jj = jj->next)
+ ;
+ jj->next = j;
+ } else
+ jobs = j;
+ j->next = 0;
+ /* this code also in waitup after parse redirect */
+ if(nrunning < nproclimit)
+ sched();
+}
+
+static void
+sched(void)
+{
+ char *flags;
+ Job *j;
+ Bufblock *buf;
+ int slot;
+ Node *n;
+ Envy *e;
+
+ if(jobs == 0){
+ usage();
+ return;
+ }
+ j = jobs;
+ jobs = j->next;
+ if(DEBUG(D_EXEC))
+ fprint(1, "firing up job for target %s\n", wtos(j->t, ' '));
+ slot = nextslot();
+ events[slot].job = j;
+ buf = newbuf();
+ e = buildenv(j, slot);
+ shprint(j->r->recipe, e, buf, j->r->shellt);
+ if(!tflag && (nflag || !(j->r->attr&QUIET)))
+ Bwrite(&bout, buf->start, (long)strlen(buf->start));
+ freebuf(buf);
+ if(nflag||tflag){
+ for(n = j->n; n; n = n->next){
+ if(tflag){
+ if(!(n->flags&VIRTUAL))
+ touch(n->name);
+ else if(explain)
+ Bprint(&bout, "no touch of virtual '%s'\n", n->name);
+ }
+ n->time = time((long *)0);
+ MADESET(n, MADE);
+ }
+ } else {
+ if(DEBUG(D_EXEC))
+ fprint(1, "recipe='%s'", j->r->recipe);/**/
+ Bflush(&bout);
+ if(j->r->attr&NOMINUSE)
+ flags = 0;
+ else
+ flags = "-e";
+ events[slot].pid = execsh(flags, j->r->recipe, 0, e, j->r->shellt, j->r->shellcmd);
+ usage();
+ nrunning++;
+ if(DEBUG(D_EXEC))
+ fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid);
+ }
+}
+
+int
+waitup(int echildok, int *retstatus)
+{
+ Envy *e;
+ int pid;
+ int slot;
+ Symtab *s;
+ Word *w;
+ Job *j;
+ char buf[ERRMAX];
+ Bufblock *bp;
+ int uarg = 0;
+ int done;
+ Node *n;
+ Process *p;
+ extern int runerrs;
+
+ /* first check against the proces slist */
+ if(retstatus)
+ for(p = phead; p; p = p->f)
+ if(p->pid == *retstatus){
+ *retstatus = p->status;
+ pdelete(p);
+ return(-1);
+ }
+again: /* rogue processes */
+ pid = waitfor(buf);
+ if(pid == -1){
+ if(echildok > 0)
+ return(1);
+ else {
+ fprint(2, "mk: (waitup %d): %r\n", echildok);
+ Exit();
+ }
+ }
+ if(DEBUG(D_EXEC))
+ fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf);
+ if(retstatus && pid == *retstatus){
+ *retstatus = buf[0]? 1:0;
+ return(-1);
+ }
+ slot = pidslot(pid);
+ if(slot < 0){
+ if(DEBUG(D_EXEC))
+ fprint(2, "mk: wait returned unexpected process %d\n", pid);
+ pnew(pid, buf[0]? 1:0);
+ goto again;
+ }
+ j = events[slot].job;
+ usage();
+ nrunning--;
+ events[slot].pid = -1;
+ if(buf[0]){
+ e = buildenv(j, slot);
+ bp = newbuf();
+ shprint(j->r->recipe, e, bp, j->r->shellt);
+ front(bp->start);
+ fprint(2, "mk: %s: exit status=%s", bp->start, buf);
+ freebuf(bp);
+ for(n = j->n, done = 0; n; n = n->next)
+ if(n->flags&DELETE){
+ if(done++ == 0)
+ fprint(2, ", deleting");
+ fprint(2, " '%s'", n->name);
+ delete(n->name);
+ }
+ fprint(2, "\n");
+ if(kflag){
+ runerrs++;
+ uarg = 1;
+ } else {
+ jobs = 0;
+ Exit();
+ }
+ }
+ for(w = j->t; w; w = w->next){
+ if((s = symlook(w->s, S_NODE, 0)) == 0)
+ continue; /* not interested in this node */
+ update(uarg, s->u.ptr);
+ }
+ if(nrunning < nproclimit)
+ sched();
+ return(0);
+}
+
+void
+nproc(void)
+{
+ Symtab *sym;
+ Word *w;
+
+ if(sym = symlook("NPROC", S_VAR, 0)) {
+ w = sym->u.ptr;
+ if (w && w->s && w->s[0])
+ nproclimit = atoi(w->s);
+ }
+ if(nproclimit < 1)
+ nproclimit = 1;
+ if(DEBUG(D_EXEC))
+ fprint(1, "nprocs = %d\n", nproclimit);
+ if(nproclimit > nevents){
+ if(nevents)
+ events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event));
+ else
+ events = (Event *)Malloc(nproclimit*sizeof(Event));
+ while(nevents < nproclimit)
+ events[nevents++].pid = 0;
+ }
+}
+
+int
+nextslot(void)
+{
+ int i;
+
+ for(i = 0; i < nproclimit; i++)
+ if(events[i].pid <= 0) return i;
+ assert("out of slots!!", 0);
+ return 0; /* cyntax */
+}
+
+int
+pidslot(int pid)
+{
+ int i;
+
+ for(i = 0; i < nevents; i++)
+ if(events[i].pid == pid) return(i);
+ if(DEBUG(D_EXEC))
+ fprint(2, "mk: wait returned unexpected process %d\n", pid);
+ return(-1);
+}
+
+
+static void
+pnew(int pid, int status)
+{
+ Process *p;
+
+ if(pfree){
+ p = pfree;
+ pfree = p->f;
+ } else
+ p = (Process *)Malloc(sizeof(Process));
+ p->pid = pid;
+ p->status = status;
+ p->f = phead;
+ phead = p;
+ if(p->f)
+ p->f->b = p;
+ p->b = 0;
+}
+
+static void
+pdelete(Process *p)
+{
+ if(p->f)
+ p->f->b = p->b;
+ if(p->b)
+ p->b->f = p->f;
+ else
+ phead = p->f;
+ p->f = pfree;
+ pfree = p;
+}
+
+void
+killchildren(char *msg)
+{
+ Process *p;
+
+ kflag = 1; /* to make sure waitup doesn't exit */
+ jobs = 0; /* make sure no more get scheduled */
+ for(p = phead; p; p = p->f)
+ expunge(p->pid, msg);
+ while(waitup(1, (int *)0) == 0)
+ ;
+ Bprint(&bout, "mk: %s\n", msg);
+ Exit();
+}
+
+static long tslot[1000];
+static long tick;
+
+void
+usage(void)
+{
+ long t;
+
+ time(&t);
+ if(tick)
+ tslot[nrunning] += (t-tick);
+ tick = t;
+}
+
+void
+prusage(void)
+{
+ int i;
+
+ usage();
+ for(i = 0; i <= nevents; i++)
+ fprint(1, "%d: %ld\n", i, tslot[i]);
+}
diff --git a/mk/sh.c b/mk/sh.c
@@ -0,0 +1,206 @@
+#include "mk.h"
+
+/*
+ * This file contains functions that depend on the shell's syntax. Most
+ * of the routines extract strings observing the shell's escape conventions.
+ */
+
+
+/*
+ * skip a token in quotes.
+ */
+static char *
+squote(char *cp, int c)
+{
+ Rune r;
+ int n;
+
+ while(*cp){
+ n = chartorune(&r, cp);
+ if(r == c)
+ return cp;
+ if(r == '\\')
+ n += chartorune(&r, cp+n);
+ cp += n;
+ }
+ SYNERR(-1); /* should never occur */
+ fprint(2, "missing closing '\n");
+ return 0;
+}
+/*
+ * search a string for unescaped characters in a pattern set
+ */
+static char *
+shcharin(char *cp, char *pat)
+{
+ Rune r;
+ int n, vargen;
+
+ vargen = 0;
+ while(*cp){
+ n = chartorune(&r, cp);
+ switch(r){
+ case '\\': /* skip escaped char */
+ cp += n;
+ n = chartorune(&r, cp);
+ break;
+ case '\'': /* skip quoted string */
+ case '"':
+ cp = squote(cp+1, r); /* n must = 1 */
+ if(!cp)
+ return 0;
+ break;
+ case '$':
+ if(*(cp+1) == '{')
+ vargen = 1;
+ break;
+ case '}':
+ if(vargen)
+ vargen = 0;
+ else if(utfrune(pat, r))
+ return cp;
+ break;
+ default:
+ if(vargen == 0 && utfrune(pat, r))
+ return cp;
+ break;
+ }
+ cp += n;
+ }
+ if(vargen){
+ SYNERR(-1);
+ fprint(2, "missing closing } in pattern generator\n");
+ }
+ return 0;
+}
+
+/*
+ * extract an escaped token. Possible escape chars are single-quote,
+ * double-quote,and backslash.
+ */
+static char*
+shexpandquote(char *s, Rune esc, Bufblock *b)
+{
+ Rune r;
+
+ if (esc == '\\') {
+ s += chartorune(&r, s);
+ rinsert(b, r);
+ return s;
+ }
+
+ while(*s){
+ s += chartorune(&r, s);
+ if(r == esc)
+ return s;
+ if (r == '\\') {
+ rinsert(b, r);
+ s += chartorune(&r, s);
+ }
+ rinsert(b, r);
+ }
+ return 0;
+}
+
+/*
+ * Input an escaped token. Possible escape chars are single-quote,
+ * double-quote and backslash.
+ */
+static int
+shescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc)
+{
+ int c, line;
+
+ if(esc == '\\') {
+ c = Bgetrune(bp);
+ if(c == '\r')
+ c = Bgetrune(bp);
+ if (c == '\n')
+ mkinline++;
+ rinsert(buf, c);
+ return 1;
+ }
+
+ line = mkinline;
+ while((c = nextrune(bp, 0)) >= 0){
+ if(c == esc){
+ if(preserve)
+ rinsert(buf, c);
+ return 1;
+ }
+ if(c == '\\') {
+ rinsert(buf, c);
+ c = Bgetrune(bp);
+ if(c == '\r')
+ c = Bgetrune(bp);
+ if (c < 0)
+ break;
+ if (c == '\n')
+ mkinline++;
+ }
+ rinsert(buf, c);
+ }
+ SYNERR(line); fprint(2, "missing closing %c\n", esc);
+ return 0;
+}
+
+/*
+ * copy a quoted string; s points to char after opening quote
+ */
+static char *
+copysingle(char *s, Rune q, Bufblock *buf)
+{
+ Rune r;
+
+ while(*s){
+ s += chartorune(&r, s);
+ rinsert(buf, r);
+ if(r == q)
+ break;
+ }
+ return s;
+}
+/*
+ * check for quoted strings. backquotes are handled here; single quotes above.
+ * s points to char after opening quote, q.
+ */
+static char *
+shcopyq(char *s, Rune q, Bufblock *buf)
+{
+ if(q == '\'' || q == '"') /* copy quoted string */
+ return copysingle(s, q, buf);
+
+ if(q != '`') /* not quoted */
+ return s;
+
+ while(*s){ /* copy backquoted string */
+ s += chartorune(&q, s);
+ rinsert(buf, q);
+ if(q == '`')
+ break;
+ if(q == '\'' || q == '"')
+ s = copysingle(s, q, buf); /* copy quoted string */
+ }
+ return s;
+}
+
+static int
+shmatchname(char *name)
+{
+ USED(name);
+
+ return 1;
+}
+
+
+Shell shshell = {
+ "sh",
+ "\"'= \t", /*used in parse.c to isolate assignment attribute*/
+ ' ', /* inter-word separator in env */
+ shcharin,
+ shexpandquote,
+ shescapetoken,
+ shcopyq,
+ shmatchname
+};
+
diff --git a/mk/shell.c b/mk/shell.c
@@ -0,0 +1,80 @@
+#include "mk.h"
+
+static Shell *shells[] = {
+ &rcshell,
+ &shshell
+};
+
+Shell *shelldefault = &shshell;
+
+Shell *shellt;
+Word *shellcmd;
+
+typedef struct Shellstack Shellstack;
+struct Shellstack
+{
+ Shell *t;
+ Word *w;
+ Shellstack *next;
+};
+
+Shellstack *shellstack;
+
+char*
+setshell(Word *w)
+{
+ int i;
+
+ if(w->s == nil)
+ return "shell name not found on line";
+
+ for(i=0; 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);
+}
diff --git a/mk/shprint.c b/mk/shprint.c
@@ -0,0 +1,125 @@
+#include "mk.h"
+
+static char *vexpand(char*, Envy*, Bufblock*);
+
+#define getfields mkgetfields
+
+static int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+ Rune r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;; str += nr) {
+ nr = chartorune(&r, str);
+ if(r == 0)
+ break;
+ if(utfrune(set, r)) {
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
+
+void
+shprint(char *s, Envy *env, Bufblock *buf, Shell *sh)
+{
+ int n;
+ Rune r;
+
+ while(*s) {
+ n = chartorune(&r, s);
+ if (r == '$')
+ s = vexpand(s, env, buf);
+ else {
+ rinsert(buf, r);
+ s += n;
+ s = sh->copyq(s, r, buf); /*handle quoted strings*/
+ }
+ }
+ insert(buf, 0);
+}
+
+static char *
+mygetenv(char *name, Envy *env)
+{
+ if (!env)
+ return 0;
+ if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == 0)
+ return 0;
+ /* only resolve internal variables and variables we've set */
+ for(; env->name; env++){
+ if (strcmp(env->name, name) == 0)
+ return wtos(env->values, ' ');
+ }
+ return 0;
+}
+
+static char *
+vexpand(char *w, Envy *env, Bufblock *buf)
+{
+ char *s, carry, *p, *q;
+
+ assert("vexpand no $", *w == '$');
+ p = w+1; /* skip dollar sign */
+ if(*p == '{') {
+ p++;
+ q = utfrune(p, '}');
+ if (!q)
+ q = strchr(p, 0);
+ } else
+ q = shname(p);
+ carry = *q;
+ *q = 0;
+ s = mygetenv(p, env);
+ *q = carry;
+ if (carry == '}')
+ q++;
+ if (s) {
+ bufcpy(buf, s, strlen(s));
+ free(s);
+ } else /* copy name intact*/
+ bufcpy(buf, w, q-w);
+ return(q);
+}
+
+void
+front(char *s)
+{
+ char *t, *q;
+ int i, j;
+ char *flds[512];
+
+ q = strdup(s);
+ i = getfields(q, flds, 512, 0, " \t\n");
+ if(i > 5){
+ flds[4] = flds[i-1];
+ flds[3] = "...";
+ i = 5;
+ }
+ t = s;
+ for(j = 0; j < i; j++){
+ for(s = flds[j]; *s; *t++ = *s++);
+ *t++ = ' ';
+ }
+ *t = 0;
+ free(q);
+}
diff --git a/mk/symtab.c b/mk/symtab.c
@@ -0,0 +1,97 @@
+#include "mk.h"
+
+#define NHASH 4099
+#define HASHMUL 79L /* this is a good value */
+static Symtab *hash[NHASH];
+
+void
+syminit(void)
+{
+ Symtab **s, *ss, *next;
+
+ for(s = hash; s < &hash[NHASH]; s++){
+ for(ss = *s; ss; ss = next){
+ next = ss->next;
+ free((char *)ss);
+ }
+ *s = 0;
+ }
+}
+
+Symtab *
+symlook(char *sym, int space, void *install)
+{
+ long h;
+ char *p;
+ Symtab *s;
+
+ for(p = sym, h = space; *p; h += *p++)
+ h *= HASHMUL;
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for(s = hash[h]; s; s = s->next)
+ if((s->space == space) && (strcmp(s->name, sym) == 0))
+ return(s);
+ if(install == 0)
+ return(0);
+ s = (Symtab *)Malloc(sizeof(Symtab));
+ s->space = space;
+ s->name = sym;
+ s->u.ptr = install;
+ s->next = hash[h];
+ hash[h] = s;
+ return(s);
+}
+
+void
+symdel(char *sym, int space)
+{
+ long h;
+ char *p;
+ Symtab *s, *ls;
+
+ /* multiple memory leaks */
+
+ for(p = sym, h = space; *p; h += *p++)
+ h *= HASHMUL;
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for(s = hash[h], ls = 0; s; ls = s, s = s->next)
+ if((s->space == space) && (strcmp(s->name, sym) == 0)){
+ if(ls)
+ ls->next = s->next;
+ else
+ hash[h] = s->next;
+ free((char *)s);
+ }
+}
+
+void
+symtraverse(int space, void (*fn)(Symtab*))
+{
+ Symtab **s, *ss;
+
+ for(s = hash; s < &hash[NHASH]; s++)
+ for(ss = *s; ss; ss = ss->next)
+ if(ss->space == space)
+ (*fn)(ss);
+}
+
+void
+symstat(void)
+{
+ Symtab **s, *ss;
+ int n;
+ int l[1000];
+
+ memset((char *)l, 0, sizeof(l));
+ for(s = hash; s < &hash[NHASH]; s++){
+ for(ss = *s, n = 0; ss; ss = ss->next)
+ n++;
+ l[n]++;
+ }
+ for(n = 0; n < 1000; n++)
+ if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n);
+}
diff --git a/mk/sys.h b/mk/sys.h
@@ -0,0 +1,5 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+
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;
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;
+}
diff --git a/mk/var.c b/mk/var.c
@@ -0,0 +1,41 @@
+#include "mk.h"
+
+void
+setvar(char *name, void *ptr)
+{
+ symlook(name, S_VAR, ptr)->u.ptr = ptr;
+ symlook(name, S_MAKEVAR, (void*)"");
+}
+
+static void
+print1(Symtab *s)
+{
+ Word *w;
+
+ Bprint(&bout, "\t%s=", s->name);
+ for (w = s->u.ptr; w; w = w->next)
+ Bprint(&bout, "'%s'", w->s);
+ Bprint(&bout, "\n");
+}
+
+void
+dumpv(char *s)
+{
+ Bprint(&bout, "%s:\n", s);
+ symtraverse(S_VAR, print1);
+}
+
+char *
+shname(char *a)
+{
+ Rune r;
+ int n;
+
+ while (*a) {
+ n = chartorune(&r, a);
+ if (!WORDCHR(r))
+ break;
+ a += n;
+ }
+ return a;
+}
diff --git a/mk/varsub.c b/mk/varsub.c
@@ -0,0 +1,252 @@
+#include "mk.h"
+
+static Word *subsub(Word*, char*, char*);
+static Word *expandvar(char**);
+static Bufblock *varname(char**);
+static Word *extractpat(char*, char**, char*, char*);
+static int submatch(char*, Word*, Word*, int*, char**);
+static Word *varmatch(char *);
+
+Word *
+varsub(char **s)
+{
+ Bufblock *b;
+ Word *w;
+
+ if(**s == '{') /* either ${name} or ${name: A%B==C%D}*/
+ return expandvar(s);
+
+ b = varname(s);
+ if(b == 0)
+ return 0;
+
+ w = varmatch(b->start);
+ freebuf(b);
+ return w;
+}
+
+/*
+ * extract a variable name
+ */
+static Bufblock*
+varname(char **s)
+{
+ Bufblock *b;
+ char *cp;
+ Rune r;
+ int n;
+
+ b = newbuf();
+ cp = *s;
+ for(;;){
+ n = chartorune(&r, cp);
+ if (!WORDCHR(r))
+ break;
+ rinsert(b, r);
+ cp += n;
+ }
+ if (b->current == b->start){
+ SYNERR(-1);
+ fprint(2, "missing variable name <%s>\n", *s);
+ freebuf(b);
+ return 0;
+ }
+ *s = cp;
+ insert(b, 0);
+ return b;
+}
+
+static Word*
+varmatch(char *name)
+{
+ Word *w;
+ Symtab *sym;
+
+ sym = symlook(name, S_VAR, 0);
+ if(sym){
+ /* check for at least one non-NULL value */
+ for (w = sym->u.ptr; w; w = w->next)
+ if(w->s && *w->s)
+ return wdup(w);
+ }
+ return 0;
+}
+
+static Word*
+expandvar(char **s)
+{
+ Word *w;
+ Bufblock *buf;
+ Symtab *sym;
+ char *cp, *begin, *end;
+
+ begin = *s;
+ (*s)++; /* skip the '{' */
+ buf = varname(s);
+ if (buf == 0)
+ return 0;
+ cp = *s;
+ if (*cp == '}') { /* ${name} variant*/
+ (*s)++; /* skip the '}' */
+ w = varmatch(buf->start);
+ freebuf(buf);
+ return w;
+ }
+ if (*cp != ':') {
+ SYNERR(-1);
+ fprint(2, "bad variable name <%s>\n", buf->start);
+ freebuf(buf);
+ return 0;
+ }
+ cp++;
+ end = shellt->charin(cp , "}");
+ if(end == 0){
+ SYNERR(-1);
+ fprint(2, "missing '}': %s\n", begin);
+ Exit();
+ }
+ *end = 0;
+ *s = end+1;
+
+ sym = symlook(buf->start, S_VAR, 0);
+ if(sym == 0 || sym->u.ptr == 0)
+ w = newword(buf->start);
+ else
+ w = subsub(sym->u.ptr, cp, end);
+ freebuf(buf);
+ return w;
+}
+
+static Word*
+extractpat(char *s, char **r, char *term, char *end)
+{
+ int save;
+ char *cp;
+ Word *w;
+
+ cp = shellt->charin(s, term);
+ if(cp){
+ *r = cp;
+ if(cp == s)
+ return 0;
+ save = *cp;
+ *cp = 0;
+ w = stow(s);
+ *cp = save;
+ } else {
+ *r = end;
+ w = stow(s);
+ }
+ return w;
+}
+
+static Word*
+subsub(Word *v, char *s, char *end)
+{
+ int nmid;
+ Word *head, *tail, *w, *h;
+ Word *a, *b, *c, *d;
+ Bufblock *buf;
+ char *cp, *enda;
+
+ a = extractpat(s, &cp, "=%&", end);
+ b = c = d = 0;
+ if(PERCENT(*cp))
+ b = extractpat(cp+1, &cp, "=", end);
+ if(*cp == '=')
+ c = extractpat(cp+1, &cp, "&%", end);
+ if(PERCENT(*cp))
+ d = stow(cp+1);
+ else if(*cp)
+ d = stow(cp);
+
+ head = tail = 0;
+ buf = newbuf();
+ for(; v; v = v->next){
+ h = w = 0;
+ if(submatch(v->s, a, b, &nmid, &enda)){
+ /* enda points to end of A match in source;
+ * nmid = number of chars between end of A and start of B
+ */
+ if(c){
+ h = w = wdup(c);
+ while(w->next)
+ w = w->next;
+ }
+ if(PERCENT(*cp) && nmid > 0){
+ if(w){
+ bufcpy(buf, w->s, strlen(w->s));
+ bufcpy(buf, enda, nmid);
+ insert(buf, 0);
+ free(w->s);
+ w->s = strdup(buf->start);
+ } else {
+ bufcpy(buf, enda, nmid);
+ insert(buf, 0);
+ h = w = newword(buf->start);
+ }
+ buf->current = buf->start;
+ }
+ if(d && *d->s){
+ if(w){
+
+ bufcpy(buf, w->s, strlen(w->s));
+ bufcpy(buf, d->s, strlen(d->s));
+ insert(buf, 0);
+ free(w->s);
+ w->s = strdup(buf->start);
+ w->next = wdup(d->next);
+ while(w->next)
+ w = w->next;
+ buf->current = buf->start;
+ } else
+ h = w = wdup(d);
+ }
+ }
+ if(w == 0)
+ h = w = newword(v->s);
+
+ if(head == 0)
+ head = h;
+ else
+ tail->next = h;
+ tail = w;
+ }
+ freebuf(buf);
+ delword(a);
+ delword(b);
+ delword(c);
+ delword(d);
+ return head;
+}
+
+static int
+submatch(char *s, Word *a, Word *b, int *nmid, char **enda)
+{
+ Word *w;
+ int n;
+ char *end;
+
+ n = 0;
+ for(w = a; w; w = w->next){
+ n = strlen(w->s);
+ if(strncmp(s, w->s, n) == 0)
+ break;
+ }
+ if(a && w == 0) /* a == NULL matches everything*/
+ return 0;
+
+ *enda = s+n; /* pointer to end a A part match */
+ *nmid = strlen(s)-n; /* size of remainder of source */
+ end = *enda+*nmid;
+ for(w = b; w; w = w->next){
+ n = strlen(w->s);
+ if(strcmp(w->s, end-n) == 0){
+ *nmid -= n;
+ break;
+ }
+ }
+ if(b && w == 0) /* b == NULL matches everything */
+ return 0;
+ return 1;
+}
diff --git a/mk/word.c b/mk/word.c
@@ -0,0 +1,189 @@
+#include "mk.h"
+
+static Word *nextword(char**);
+
+Word*
+newword(char *s)
+{
+ Word *w;
+
+ w = (Word *)Malloc(sizeof(Word));
+ w->s = strdup(s);
+ w->next = 0;
+ return(w);
+}
+
+Word *
+stow(char *s)
+{
+ Word *head, *w, *new;
+
+ w = head = 0;
+ while(*s){
+ new = nextword(&s);
+ if(new == 0)
+ break;
+ if (w)
+ w->next = new;
+ else
+ head = w = new;
+ while(w->next)
+ w = w->next;
+
+ }
+ if (!head)
+ head = newword("");
+ return(head);
+}
+
+char *
+wtos(Word *w, int sep)
+{
+ Bufblock *buf;
+ char *cp;
+
+ buf = newbuf();
+ for(; w; w = w->next){
+ for(cp = w->s; *cp; cp++)
+ insert(buf, *cp);
+ if(w->next)
+ insert(buf, sep);
+ }
+ insert(buf, 0);
+ cp = strdup(buf->start);
+ freebuf(buf);
+ return(cp);
+}
+
+Word*
+wdup(Word *w)
+{
+ Word *v, *new, *base;
+
+ v = base = 0;
+ while(w){
+ new = newword(w->s);
+ if(v)
+ v->next = new;
+ else
+ base = new;
+ v = new;
+ w = w->next;
+ }
+ return base;
+}
+
+void
+delword(Word *w)
+{
+ Word *v;
+
+ while(v = w){
+ w = w->next;
+ if(v->s)
+ free(v->s);
+ free(v);
+ }
+}
+
+/*
+ * break out a word from a string handling quotes, executions,
+ * and variable expansions.
+ */
+static Word*
+nextword(char **s)
+{
+ Bufblock *b;
+ Word *head, *tail, *w;
+ Rune r;
+ char *cp;
+ int empty;
+
+ cp = *s;
+ b = newbuf();
+restart:
+ head = tail = 0;
+ while(*cp == ' ' || *cp == '\t') /* leading white space */
+ cp++;
+ empty = 1;
+ while(*cp){
+ cp += chartorune(&r, cp);
+ switch(r)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ goto out;
+ case '\\':
+ case '\'':
+ case '"':
+ empty = 0;
+ cp = shellt->expandquote(cp, r, b);
+ if(cp == 0){
+ fprint(2, "missing closing quote: %s\n", *s);
+ Exit();
+ }
+ break;
+ case '$':
+ w = varsub(&cp);
+ if(w == 0){
+ if(empty)
+ goto restart;
+ break;
+ }
+ empty = 0;
+ if(b->current != b->start){
+ bufcpy(b, w->s, strlen(w->s));
+ insert(b, 0);
+ free(w->s);
+ w->s = strdup(b->start);
+ b->current = b->start;
+ }
+ if(head){
+ bufcpy(b, tail->s, strlen(tail->s));
+ bufcpy(b, w->s, strlen(w->s));
+ insert(b, 0);
+ free(tail->s);
+ tail->s = strdup(b->start);
+ tail->next = w->next;
+ free(w->s);
+ free(w);
+ b->current = b->start;
+ } else
+ tail = head = w;
+ while(tail->next)
+ tail = tail->next;
+ break;
+ default:
+ empty = 0;
+ rinsert(b, r);
+ break;
+ }
+ }
+out:
+ *s = cp;
+ if(b->current != b->start){
+ if(head){
+ cp = b->current;
+ bufcpy(b, tail->s, strlen(tail->s));
+ bufcpy(b, b->start, cp-b->start);
+ insert(b, 0);
+ free(tail->s);
+ tail->s = strdup(cp);
+ } else {
+ insert(b, 0);
+ head = newword(b->start);
+ }
+ }
+ freebuf(b);
+ return head;
+}
+
+void
+dumpw(char *s, Word *w)
+{
+ Bprint(&bout, "%s", s);
+ for(; w; w = w->next)
+ Bprint(&bout, " '%s'", w->s);
+ Bputc(&bout, '\n');
+}
diff --git a/troff/FIXES b/troff/FIXES
@@ -0,0 +1,821 @@
+March 11, 1994
+
+ If we are just plain old nroff (and not doing UNICODE) we should
+ only Lookup characters, not Install when we don't know them.
+ If we are troff, we Install them anyway
+
+March 8, 1994
+
+ Nroff had problems with parsing quoted white space as options or
+ character code in some terminals tables. Changed by having scanf
+ include white space when necessary as suggested by Rich.
+
+March 1, 1994
+
+ Made sanity check for terminal type depending on the trace level;
+ trace level set with -tn flag at start up
+
+22 Feb, 1994
+
+ More pointer shuffling fixes.
+
+18 Feb, 1994
+
+ More disabling of multibyte stuff. Fixed bug in n5.c: casetm didn'
+ know about the new format in the fontables.
+
+Feb 17, 1994
+
+ Removed extra include <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.
diff --git a/troff/Makefile b/troff/Makefile
@@ -0,0 +1,11 @@
+# mk - mk unix port from plan9
+# Depends on ../lib9
+
+TARG = troff
+
+OFILES = n1.o n2.o n3.o n4.o n5.o t6.o n6.o n7.o n8.o n9.o t10.o\
+ n10.o t11.o ni.o hytab.o suftab.o dwbinit.o mbwc.o
+MANFILES = troff.1
+CFLAGS = -DUNICODE -DTMACDIR=\"tmac/tmac.\" -DTDEVNAME=\"utf\" -DFONTDIR=\"troff/font\" -DNTERMDIR=\"troff/term/tab.\" -DTEXHYPHENS=\"#9/lib/hyphen.tex\" -DALTHYPHENS=\"lib/hyphen.tex\" -DDWBHOME=\"#9/\"
+
+include ../std.mk
diff --git a/troff/README b/troff/README
@@ -0,0 +1,31 @@
+To make troff (actually a.out):
+
+ make
+
+You will also need to write a driver for your favorite output device.
+d202.c provides a model, although it is specialized to a machine no
+one has. There are also a variety of postscript drivers that are the
+best thing to use if you have a postscript device.
+
+You will also have to make a DESC file for your typesetter and some
+font description files; see dev202 for examples. These describe the
+named characters, widths, kerning information, and output codes.
+
+Nroff is the same program as troff, so you should
+
+ cp a.out /usr/bin/troff
+ ln /usr/bin/troff /usr/bin/nroff
+
+or the equivalent.
+
+You will also need terminal description files for your terminals; see
+tab.37, tab.450 and tab.lp for examples.
+
+Troff uses files that are normally stored in /usr/lib/font;
+macro packages are in /usr/lib/tmac; and nroff tables are in
+/usr/lib/term. You can edit tdef.h to change these assumptions.
+
+There have been a few features since the last version, and a number of
+significant internal changes. Not all are improvements, of course.
+Most of the more recent changes, including bug fixes, are in FIXES,
+which you should read also.
diff --git a/troff/cvt b/troff/cvt
@@ -0,0 +1,45 @@
+
+awk '
+
+/^{/ {
+ if (prev != "") {
+ # comments can be trouble (e.g. ffree())
+ if ( (c = match(prev, /\/\*.*\*\/$/)) != 0 ) {
+ comment = substr(prev, c)
+ sub(/\/\*.*\*\/$/, "", prev)
+ } else comment = ""
+
+ x = prev
+
+ # isolate argument list
+ sub(/^[^(]*\(/, "", x)
+ sub(/\)[^)]*$/, "", x)
+
+ # find the names in it
+ n = split(x, args)
+ arglist = ""
+ for (i = 2; i <= n; i += 2)
+ arglist = arglist args[i]
+ gsub(/\(\*f\)\(Tchar\)/, "f", arglist) # special case for n4.c
+ gsub(/\[[0-9]+\]/, "", arglist) # for n8.c
+ gsub(/[*()\[\]]/, "", arglist) # discard noise characters *()[]
+ gsub(/,/, ", ", arglist) # space nicely
+ sub(/\(.*\)/, "(" arglist ")", prev) # reconstruct
+ print prev comment
+
+ # argument declarations
+ gsub(/,/, ";", x)
+ gsub(/\(\*f\)\(Tchar\)/, "(*f)()", x) # special case for n4.c
+ if (x != "")
+ print "\t" x ";"
+ }
+ prev = $0
+ next
+}
+
+{ print prev
+ prev = $0
+}
+
+END { print prev }
+' $*
diff --git a/troff/dwbinit.c b/troff/dwbinit.c
@@ -0,0 +1,317 @@
+/*
+ *
+ * Pathname management routines for DWB C programs.
+ *
+ * Applications should initialize a dwbinit array with the string
+ * pointers and arrays that need to be updated, and then hand that
+ * array to DWBinit before much else happens in their main program.
+ * DWBinit calls DWBhome to get the current home directory. DWBhome
+ * uses the last definition of DWBENV (usually "DWBHOME") in file
+ * DWBCONFIG (e.g., /usr/lib/dwb3.4) or the value assigned to that
+ * variable in the environment if the DWBCONFIG file doesn't exist,
+ * can't be read, or doesn't define DWBENV.
+ *
+ * DWBCONFIG must be a simple shell script - comments, a definition
+ * of DWBHOME, and perhaps an export or echo is about all that's
+ * allowed. The parsing in DWBhome is simple and makes no attempt
+ * to duplicate the shell. It only looks for DWBHOME= as the first
+ * non-white space string on a line, so
+ *
+ * #
+ * # A sample DWBCONFIG shell script
+ * #
+ *
+ * DWBHOME=/usr/add-on/dwb3.4
+ * export DWBHOME
+ *
+ * means DWBhome would return "/usr/add-on/dwb3.4" for the DWB home
+ * directory. A DWBCONFIG file means there can only be one working
+ * copy of a DWB release on a system, which seems like a good idea.
+ * Using DWBCONFIG also means programs will always include correct
+ * versions of files (e.g., prologues or macro packages).
+ *
+ * Relying on an environment variable guarantees nothing. You could
+ * execute a version of dpost, but your environment might point at
+ * incorrect font tables or prologues. Despite the obvious problems
+ * we've also implemented an environment variable approach, but it's
+ * only used if there's no DWBCONFIG file.
+ *
+ * DWBinit calls DWBhome to get the DWB home directory prefix and
+ * then marches through its dwbinit argument, removing the default
+ * home directory and prepending the new home. DWBinit stops when
+ * it reaches an element that has NULL for its address and value
+ * fields. Pointers in a dwbinit array are reallocated and properly
+ * initialized; arrays are simply reinitialized if there's room.
+ * All pathnames that are to be adjusted should be relative. For
+ * example,
+ *
+ * char *fontdir = "lib/font";
+ * char xyzzy[25] = "etc/xyzzy";
+ *
+ * would be represented in a dwbinit array as,
+ *
+ * dwbinit allpaths[] = {
+ * &fontdir, NULL, 0,
+ * NULL, xyzzy, sizeof(xyzzy),
+ * NULL, NULL, 0
+ * };
+ *
+ * The last element must have NULL entries for the address and
+ * value fields. The main() routine would then do,
+ *
+ * #include "dwbinit.h"
+ *
+ * main() {
+ *
+ * DWBinit("program name", allpaths);
+ * ...
+ * }
+ *
+ * Debugging is enabled if DWBDEBUG is in the environment and has
+ * the value ON. Output is occasionally useful and probably should
+ * be documented.
+ *
+ */
+
+#include <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 */
+
+/*****************************************************************************/
+
diff --git a/troff/dwbinit.h b/troff/dwbinit.h
@@ -0,0 +1,19 @@
+/*
+ *
+ * A structure used to adjust pathnames in DWB C code. Pointers
+ * set the address field, arrays use the value field and must
+ * also set length to the number elements in the array. Pointers
+ * are always reallocated and then reinitialized; arrays are only
+ * reinitialized, if there's room.
+ *
+ */
+
+typedef struct {
+ char **address;
+ char *value;
+ int length;
+} dwbinit;
+
+extern void DWBinit(char *, dwbinit *);
+extern char* DWBhome(void);
+extern void DWBprefix(char *, char *, int);
diff --git a/troff/ext.h b/troff/ext.h
@@ -0,0 +1,187 @@
+#define devname p9_devname
+
+extern int TROFF;
+
+extern int alphabet;
+extern char **argp;
+extern char *eibuf;
+extern char *ibufp;
+extern char *obufp;
+extern char *unlkp;
+extern char *xbufp;
+extern char *xeibuf;
+extern char cfname[NSO+1][NS];
+extern int trace;
+extern char devname[];
+extern char ibuf[IBUFSZ];
+extern char mfiles[NMF][NS];
+extern char nextf[];
+extern char obuf[];
+extern char termtab[];
+extern char fontdir[];
+extern Font fonts[MAXFONTS+1];
+extern char xbuf[IBUFSZ];
+extern Offset apptr;
+extern Offset ip;
+extern Offset nextb;
+extern Offset offset;
+extern Offset woff;
+extern Numerr numerr;
+extern int *pnp;
+extern int pstab[];
+extern int nsizes;
+extern int app;
+extern int ascii;
+extern int bd;
+extern int bdtab[];
+extern int ccs;
+extern char *chnames[]; /* chnames[n-ALPHABET] -> name of char n */
+extern int copyf;
+extern int cs;
+extern int dfact;
+extern int dfactd;
+extern int diflg;
+extern int dilev;
+extern int donef;
+extern int dotT;
+extern int dpn;
+extern int ds;
+extern int ejf;
+extern int em;
+extern int eqflg;
+extern int error;
+extern int esc;
+extern int eschar;
+extern int ev;
+extern int evi;
+extern int evlist[EVLSZ];
+extern int fc;
+extern int flss;
+extern int fontlab[];
+extern int hflg;
+extern int ibf;
+extern int ifi;
+extern int iflg;
+extern int init;
+extern int lead;
+extern int lg;
+extern int lgf;
+extern int macerr;
+extern int mflg;
+extern int mfont;
+extern int mlist[NTRAP];
+extern int mpts;
+extern int nchnames;
+extern int ndone;
+extern int newmn;
+extern int nflush;
+extern int nfo;
+extern int nfonts;
+extern int nform;
+extern int nhyp;
+extern int nlflg;
+extern int nlist[NTRAP];
+extern int nmfi;
+extern int nonumb;
+extern int noscale;
+extern int npn;
+extern int npnflg;
+extern int nx;
+extern int oldbits;
+extern int oldmn;
+extern int over;
+extern int padc;
+extern int pfont;
+extern int pfrom;
+extern int pipeflg;
+extern int pl;
+extern int pnlist[];
+extern int po1;
+extern int po;
+extern int ppts;
+#define print troffprint
+extern int print;
+extern FILE *ptid;
+extern int pto;
+extern int quiet;
+extern int ralss;
+extern int rargc;
+extern int raw;
+extern int res;
+extern int sbold;
+extern int setwdf;
+extern int sfont;
+extern int smnt;
+extern int stdi;
+extern int stop;
+extern int sv;
+extern int tabch, ldrch;
+extern int tflg;
+extern int totout;
+extern int trap;
+extern Ushort trtab[];
+extern int tty;
+extern int ulfont;
+extern int vflag;
+extern int whichroff;
+extern int widthp;
+extern int xfont;
+extern int xpts;
+extern Stack *ejl;
+extern Stack *frame;
+extern Stack *stk;
+extern Stack *nxf;
+extern Tchar **hyp;
+extern Tchar *olinep;
+extern Tchar pbbuf[NC];
+extern Tchar *pbp;
+extern Tchar *lastpbp;
+extern Tchar ch;
+extern Tchar nrbits;
+extern Tbuf _oline;
+extern Wcache widcache[];
+extern char gchtab[];
+extern Diver d[NDI];
+extern Diver *dip;
+
+
+extern char xchname[];
+extern short xchtab[];
+extern char *codestr;
+extern char *chnamep;
+extern short *chtab;
+extern int nchtab;
+
+extern Numtab *numtabp;
+
+/* these characters are used as various signals or values
+/* in miscellaneous places.
+/* values are set in specnames in t10.c
+*/
+
+extern int c_hyphen;
+extern int c_emdash;
+extern int c_rule;
+extern int c_minus;
+extern int c_fi;
+extern int c_fl;
+extern int c_ff;
+extern int c_ffi;
+extern int c_ffl;
+extern int c_acute;
+extern int c_grave;
+extern int c_under;
+extern int c_rooten;
+extern int c_boxrule;
+extern int c_lefthand;
+extern int c_dagger;
+extern int c_isalnum;
+
+/*
+ * String pointers for DWB pathname management.
+ */
+
+extern char *DWBfontdir;
+extern char *DWBntermdir;
+extern char *DWBalthyphens;
+
diff --git a/troff/find b/troff/find
@@ -0,0 +1 @@
+grep $1 *.[ch]
diff --git a/troff/fns.h b/troff/fns.h
@@ -0,0 +1,389 @@
+#define getline p9getline
+
+/*
+ * other
+ */
+#ifdef NOTDEF
+int pclose(FILE*);
+long filesize(int fd);
+int open(char *, int);
+int read(int, char *, int);
+int lseek(int, long, int);
+int close(int);
+int getpid(void);
+#endif
+char *unsharp(char*);
+
+/*
+ * c1.c
+ */
+void init0(void);
+void init2(void);
+void cvtime(void);
+void errprint(void);
+int control(int a, int b);
+void casept(void);
+int getrq(void);
+Tchar getch(void);
+void setxon(void);
+Tchar getch0(void);
+Tchar get1ch(FILE *);
+void pushback(Tchar *b);
+void cpushback(char *b);
+int nextfile(void);
+int popf(void);
+void flushi(void);
+int getach(void);
+void casenx(void);
+int getname(void);
+void caseso(void);
+void caself(void);
+void casecf(void);
+void getline(char *s, int n);
+void casesy(void);
+void getpn(char *a);
+void setrpt(void);
+
+/*
+ * n2.c
+ */
+int pchar(Tchar i);
+void pchar1(Tchar i);
+int pchar2(Tchar i);
+int flusho(void);
+void casedone(void);
+void caseex(void);
+void done(int x);
+void done1(int x);
+void done2(int x);
+void done3(int x);
+void edone(int x);
+void casepi(void);
+
+/*
+ * c3.c
+ */
+void blockinit(void);
+char* grow(char *, int, int);
+void mnspace(void);
+void caseig(void);
+void casern(void);
+void maddhash(Contab *rp);
+void munhash(Contab *mp);
+void mrehash(void);
+void caserm(void);
+void caseas(void);
+void caseds(void);
+void caseam(void);
+void casede(void);
+int findmn(int i);
+void clrmn(int i);
+Offset finds(int mn);
+int skip(void);
+int copyb(void);
+void copys(void);
+Offset alloc(void);
+void ffree(Offset i);
+void wbf(Tchar i);
+Tchar rbf(void);
+Tchar popi(void);
+Offset pushi(Offset newip, int mname);
+void* setbrk(int x);
+int getsn(void);
+Offset setstr(void);
+void collect(void);
+void seta(void);
+void caseda(void);
+void casegd(void);
+void casedi(void);
+void casedt(void);
+void casetl(void);
+void casepc(void);
+void casepm(void);
+void stackdump(void);
+
+/*
+ * c4.c
+ */
+void setn(void);
+int wrc(Tchar i);
+void setn1(int i, int form, Tchar bits);
+void nnspace(void);
+void nrehash(void);
+void nunhash(Numtab *rp);
+int findr(int i);
+int usedr(int i);
+int fnumb(int i, int (*f)(Tchar));
+int decml(int i, int (*f)(Tchar));
+int roman(int i, int (*f)(Tchar));
+int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp);
+int abc(int i, int (*f)(Tchar));
+int abc0(int i, int (*f)(Tchar));
+long atoi0(void);
+long ckph(void);
+long atoi1(Tchar ii);
+void caserr(void);
+void casenr(void);
+void caseaf(void);
+void setaf(void);
+int vnumb(int *i);
+int hnumb(int *i);
+int inumb(int *n);
+int quant(int n, int m);
+
+/*
+ * c5.c
+ */
+void casead(void);
+void casena(void);
+void casefi(void);
+void casenf(void);
+void casers(void);
+void casens(void);
+int chget(int c);
+void casecc(void);
+void casec2(void);
+void casehc(void);
+void casetc(void);
+void caselc(void);
+void casehy(void);
+int max(int aa, int bb);
+void casenh(void);
+void casece(void);
+void casein(void);
+void casell(void);
+void caselt(void);
+void caseti(void);
+void casels(void);
+void casepo(void);
+void casepl(void);
+void casewh(void);
+void casech(void);
+int findn(int i);
+void casepn(void);
+void casebp(void);
+void casextm(void);
+void casetm(void);
+void casefm(void);
+void casetm1(int ab, FILE *out);
+void casesp(void);
+void casesp1(int a);
+void casert(void);
+void caseem(void);
+void casefl(void);
+void caseev(void);
+void envcopy(Env *e1, Env *e2);
+void caseel(void);
+void caseie(void);
+void casexif(void);
+void caseif(void);
+void caseif1(int);
+void eatblk(int inblk);
+int cmpstr(Tchar c);
+void caserd(void);
+int rdtty(void);
+void caseec(void);
+void caseeo(void);
+void caseta(void);
+void casene(void);
+void casetr(void);
+void casecu(void);
+void caseul(void);
+void caseuf(void);
+void caseit(void);
+void casemc(void);
+void casemk(void);
+void casesv(void);
+void caseos(void);
+void casenm(void);
+void getnm(int *p, int min);
+void casenn(void);
+void caseab(void);
+void save_tty(void);
+void restore_tty(void);
+void set_tty(void);
+void echo_off(void);
+void echo_on(void);
+
+/*
+ * t6.c
+ */
+int t_width(Tchar j);
+void zapwcache(int s);
+int onfont(int n, int f);
+int getcw(int i);
+void xbits(Tchar i, int bitf);
+Tchar t_setch(int c);
+Tchar t_setabs(void);
+int t_findft(int i);
+void caseps(void);
+void casps1(int i);
+int findps(int i);
+void t_mchbits(void);
+void t_setps(void);
+Tchar t_setht(void);
+Tchar t_setslant(void);
+void caseft(void);
+void t_setfont(int a);
+void t_setwd(void);
+Tchar t_vmot(void);
+Tchar t_hmot(void);
+Tchar t_mot(void);
+Tchar t_sethl(int k);
+Tchar t_makem(int i);
+Tchar getlg(Tchar i);
+void caselg(void);
+void casefp(void);
+char *strdupl(const char *);
+int setfp(int pos, int f, char *truename, int print);
+void casecs(void);
+void casebd(void);
+void casevs(void);
+void casess(void);
+Tchar t_xlss(void);
+Uchar* unpair(int i);
+void outascii(Tchar i);
+
+/*
+ * c7.c
+ */
+void tbreak(void);
+void donum(void);
+void text(void);
+void nofill(void);
+void callsp(void);
+void ckul(void);
+void storeline(Tchar c, int w);
+void newline(int a);
+int findn1(int a);
+void chkpn(void);
+int findt(int a);
+int findt1(void);
+void eject(Stack *a);
+int movword(void);
+void horiz(int i);
+void setnel(void);
+int getword(int x);
+void storeword(Tchar c, int w);
+Tchar gettch(void);
+
+/*
+ * c8.c
+ */
+void hyphen(Tchar *wp);
+int punct(Tchar i);
+int alph(int i);
+void caseha(void);
+void caseht(void);
+void casehw(void);
+int exword(void);
+int suffix(void);
+int maplow(int i);
+int vowel(int i);
+Tchar* chkvow(Tchar *w);
+void digram(void);
+int dilook(int a, int b, char t[26][13]);
+
+/*
+ * c9.c
+ */
+Tchar setz(void);
+void setline(void);
+int eat(int c);
+void setov(void);
+void setbra(void);
+void setvline(void);
+void setdraw(void);
+void casefc(void);
+Tchar setfield(int x);
+
+/*
+ * t10.c
+ */
+void t_ptinit(void);
+void t_specnames(void);
+void t_ptout(Tchar i);
+int ptout0(Tchar *pi);
+void ptchname(int);
+void ptflush(void);
+void ptps(void);
+void ptfont(void);
+void ptfpcmd(int f, char *s, char *fn);
+void t_ptlead(void);
+void ptesc(void);
+void ptpage(int n);
+void pttrailer(void);
+void ptstop(void);
+void t_ptpause(void);
+
+/*
+ * t11.c
+ */
+int getdesc(char *name);
+int getfont(char *name, int pos);
+int chadd(char *s, int, int);
+char* chname(int n);
+int getlig(FILE *fin);
+
+/*
+ * n6.c
+ */
+int n_width(Tchar j);
+Tchar n_setch(int c);
+Tchar n_setabs(void);
+int n_findft(int i);
+void n_mchbits(void);
+void n_setps(void);
+Tchar n_setht(void);
+Tchar n_setslant(void);
+void n_caseft(void);
+void n_setfont(int a);
+void n_setwd(void);
+Tchar n_vmot(void);
+Tchar n_hmot(void);
+Tchar n_mot(void);
+Tchar n_sethl(int k);
+Tchar n_makem(int i);
+void n_casefp(void);
+void n_casebd(void);
+void n_casevs(void);
+Tchar n_xlss(void);
+
+/*
+ * n10.c
+ */
+void n_ptinit(void);
+char* skipstr(char *s);
+char* getstr(char *s, char *t);
+char* getint(char *s, int *pn);
+void twdone(void);
+void n_specnames(void);
+int findch(char *s);
+void n_ptout(Tchar i);
+void ptout1(void);
+char* plot(char *x);
+void move(void);
+void n_ptlead(void);
+void n_ptpause(void);
+
+/*
+ * indirect calls on TROFF/!TROFF. these are variables!
+ */
+extern Tchar (*hmot)(void);
+extern Tchar (*makem)(int i);
+extern Tchar (*setabs)(void);
+extern Tchar (*setch)(int c);
+extern Tchar (*sethl)(int k);
+extern Tchar (*setht)(void);
+extern Tchar (*setslant)(void);
+extern Tchar (*vmot)(void);
+extern Tchar (*xlss)(void);
+extern int (*findft)(int i);
+extern int (*width)(Tchar j);
+extern void (*mchbits)(void);
+extern void (*ptlead)(void);
+extern void (*ptout)(Tchar i);
+extern void (*ptpause)(void);
+extern void (*setfont)(int a);
+extern void (*setps)(void);
+extern void (*setwd)(void);
diff --git a/troff/hytab.c b/troff/hytab.c
@@ -0,0 +1,126 @@
+/*
+ * Hyphenation digram tables
+ */
+
+typedef unsigned char Uchar;
+
+
+Uchar bxh[26][13] = {
+ 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0040,0000,0040
+};
+
+Uchar hxx[26][13] = {
+ 0006,0042,0041,0123,0021,0024,0063,0042,0002,0043,0021,0001,0022,
+ 0140,0000,0200,0003,0260,0006,0000,0160,0007,0000,0140,0000,0320,
+ 0220,0000,0160,0005,0240,0010,0000,0100,0006,0000,0200,0000,0320,
+ 0240,0000,0120,0003,0140,0000,0000,0240,0010,0000,0220,0000,0160,
+ 0042,0023,0041,0040,0040,0022,0043,0041,0030,0064,0021,0000,0041,
+ 0100,0000,0140,0000,0220,0006,0000,0140,0003,0000,0200,0000,0000,
+ 0200,0000,0120,0002,0220,0010,0000,0160,0006,0000,0140,0000,0320,
+ 0020,0000,0020,0000,0020,0000,0000,0020,0000,0000,0020,0000,0000,
+ 0043,0163,0065,0044,0022,0043,0104,0042,0061,0146,0061,0000,0007,
+ 0100,0000,0140,0000,0040,0000,0000,0100,0000,0000,0120,0000,0000,
+ 0140,0000,0040,0011,0060,0004,0001,0120,0003,0000,0140,0000,0040,
+ 0200,0000,0100,0000,0140,0000,0000,0140,0000,0000,0140,0000,0240,
+ 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0140,0000,0240,
+ 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0060,0000,0240,
+ 0021,0043,0041,0121,0040,0023,0042,0003,0142,0042,0061,0001,0022,
+ 0120,0000,0140,0010,0140,0010,0000,0140,0002,0000,0120,0000,0120,
+ 0000,0000,0000,0000,0360,0000,0000,0000,0000,0000,0160,0000,0000,
+ 0100,0000,0040,0005,0120,0000,0000,0100,0000,0000,0060,0000,0140,
+ 0140,0040,0100,0001,0240,0041,0000,0242,0000,0002,0140,0000,0100,
+ 0240,0000,0120,0002,0200,0000,0000,0320,0007,0000,0240,0000,0340,
+ 0101,0021,0041,0020,0040,0005,0042,0121,0002,0021,0201,0000,0020,
+ 0160,0000,0100,0000,0140,0000,0000,0160,0006,0000,0220,0000,0140,
+ 0140,0000,0020,0001,0020,0000,0000,0100,0001,0000,0300,0000,0000,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0106,0041,0040,0147,0040,0000,0063,0041,0001,0102,0160,0002,0002,
+ 0300,0000,0040,0017,0140,0017,0000,0240,0000,0000,0140,0000,0120
+};
+
+Uchar bxxh[26][13] = {
+ 0005,0150,0153,0062,0062,0246,0152,0127,0146,0203,0310,0017,0206,
+ 0100,0000,0120,0000,0140,0000,0000,0100,0000,0000,0120,0000,0060,
+ 0100,0000,0040,0000,0060,0000,0000,0060,0000,0000,0220,0000,0040,
+ 0100,0000,0120,0000,0200,0000,0000,0100,0000,0000,0140,0000,0060,
+ 0043,0142,0046,0140,0062,0147,0210,0131,0046,0106,0246,0017,0111,
+ 0060,0000,0020,0000,0060,0000,0000,0040,0000,0000,0100,0000,0000,
+ 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0100,0000,0040,
+ 0100,0000,0100,0000,0100,0000,0000,0040,0000,0000,0100,0000,0140,
+ 0066,0045,0145,0140,0000,0070,0377,0030,0130,0103,0003,0017,0006,
+ 0040,0000,0040,0000,0020,0000,0000,0040,0000,0000,0100,0000,0000,
+ 0200,0000,0020,0000,0140,0000,0000,0120,0000,0000,0120,0000,0040,
+ 0120,0000,0040,0000,0060,0000,0000,0060,0000,0000,0160,0000,0040,
+ 0120,0000,0040,0000,0120,0000,0000,0040,0000,0000,0160,0000,0040,
+ 0120,0000,0020,0000,0140,0000,0000,0120,0000,0000,0140,0000,0040,
+ 0051,0126,0150,0140,0060,0210,0146,0006,0006,0165,0003,0017,0244,
+ 0120,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0000,0140,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0140,0000,0140,0000,0060,0000,0000,0100,0000,0000,0140,0000,0020,
+ 0120,0000,0020,0000,0060,0000,0000,0060,0000,0000,0060,0000,0040,
+ 0140,0000,0020,0000,0100,0000,0000,0140,0000,0000,0140,0000,0020,
+ 0070,0125,0051,0162,0120,0105,0126,0104,0006,0044,0000,0017,0052,
+ 0140,0000,0020,0000,0140,0000,0000,0060,0000,0000,0060,0000,0040,
+ 0020,0000,0000,0000,0020,0000,0000,0000,0000,0000,0000,0000,0060,
+ 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0000,0240,
+ 0065,0042,0060,0200,0000,0210,0222,0146,0006,0204,0220,0012,0003,
+ 0240,0000,0020,0000,0120,0000,0000,0200,0000,0000,0200,0000,0240
+};
+
+Uchar xhx[26][13] = {
+ 0032,0146,0042,0107,0076,0102,0042,0146,0202,0050,0006,0000,0051,
+ 0036,0377,0057,0013,0057,0366,0377,0057,0001,0377,0057,0000,0040,
+ 0037,0377,0020,0000,0100,0022,0377,0057,0362,0116,0100,0000,0017,
+ 0057,0377,0057,0031,0137,0363,0377,0037,0362,0270,0077,0000,0117,
+ 0074,0142,0012,0236,0076,0125,0063,0165,0341,0046,0047,0000,0024,
+ 0020,0017,0075,0377,0040,0001,0377,0017,0001,0204,0020,0000,0040,
+ 0057,0017,0057,0340,0140,0362,0314,0117,0003,0302,0100,0000,0057,
+ 0057,0357,0077,0017,0100,0366,0314,0057,0342,0346,0037,0000,0060,
+ 0252,0145,0072,0157,0377,0165,0063,0066,0164,0050,0363,0000,0362,
+ 0000,0000,0020,0000,0020,0000,0000,0017,0000,0000,0020,0000,0000,
+ 0117,0017,0237,0377,0200,0354,0125,0110,0004,0257,0000,0000,0300,
+ 0057,0367,0054,0357,0157,0216,0314,0114,0217,0353,0053,0000,0057,
+ 0077,0213,0077,0077,0177,0317,0377,0114,0377,0352,0077,0000,0076,
+ 0077,0213,0077,0077,0157,0177,0377,0054,0377,0352,0117,0000,0075,
+ 0125,0230,0065,0216,0057,0066,0063,0047,0345,0126,0011,0000,0033,
+ 0057,0377,0051,0360,0120,0361,0273,0056,0001,0256,0057,0000,0060,
+ 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,
+ 0076,0310,0056,0310,0137,0174,0273,0055,0335,0266,0033,0000,0155,
+ 0077,0157,0057,0360,0057,0063,0042,0024,0077,0206,0020,0000,0040,
+ 0057,0037,0077,0360,0100,0365,0377,0037,0362,0176,0050,0000,0026,
+ 0167,0146,0042,0112,0077,0110,0062,0254,0366,0052,0377,0000,0163,
+ 0060,0000,0040,0000,0120,0000,0377,0060,0012,0000,0037,0000,0257,
+ 0037,0232,0157,0361,0040,0003,0125,0010,0001,0256,0000,0000,0340,
+ 0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0017,0277,
+ 0253,0315,0257,0216,0377,0206,0146,0306,0371,0126,0232,0000,0004,
+ 0057,0012,0100,0360,0160,0360,0000,0040,0000,0017,0157,0000,0176
+};
+
+Uchar xxh[26][13] = {
+ 0045,0150,0154,0162,0042,0246,0210,0147,0152,0103,0230,0017,0206,
+ 0100,0000,0040,0000,0140,0000,0000,0100,0000,0021,0120,0017,0060,
+ 0100,0000,0040,0002,0140,0320,0000,0060,0000,0001,0220,0017,0040,
+ 0100,0001,0120,0001,0241,0000,0000,0100,0000,0020,0140,0017,0060,
+ 0023,0162,0046,0142,0022,0207,0210,0131,0052,0106,0250,0017,0110,
+ 0060,0000,0042,0000,0160,0000,0000,0040,0000,0212,0100,0017,0000,
+ 0140,0000,0040,0002,0140,0000,0000,0120,0000,0040,0120,0017,0040,
+ 0100,0000,0100,0000,0140,0001,0021,0140,0000,0046,0100,0017,0140,
+ 0066,0045,0025,0201,0020,0130,0146,0030,0130,0103,0025,0017,0006,
+ 0100,0000,0040,0000,0020,0000,0000,0040,0000,0000,0200,0017,0000,
+ 0200,0000,0020,0001,0140,0000,0000,0140,0000,0000,0120,0017,0040,
+ 0120,0026,0042,0020,0140,0161,0042,0143,0000,0022,0162,0017,0040,
+ 0121,0042,0060,0020,0140,0200,0000,0123,0000,0021,0220,0017,0041,
+ 0121,0042,0060,0120,0140,0200,0000,0123,0000,0021,0160,0017,0041,
+ 0051,0126,0150,0141,0060,0210,0146,0066,0026,0165,0026,0017,0247,
+ 0120,0000,0040,0003,0160,0000,0000,0140,0000,0021,0100,0017,0140,
+ 0000,0000,0000,0000,0200,0000,0000,0000,0000,0000,0000,0017,0000,
+ 0141,0023,0122,0040,0160,0143,0042,0142,0000,0047,0143,0017,0020,
+ 0120,0000,0040,0006,0140,0060,0000,0141,0000,0026,0100,0017,0040,
+ 0140,0000,0020,0007,0100,0000,0000,0140,0000,0001,0140,0017,0020,
+ 0110,0125,0051,0162,0120,0125,0127,0104,0006,0104,0000,0017,0052,
+ 0140,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0017,0000,
+ 0040,0005,0020,0000,0040,0313,0231,0030,0000,0140,0000,0017,0056,
+ 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0017,0240,
+ 0065,0042,0060,0040,0000,0206,0231,0146,0006,0224,0220,0017,0004,
+ 0240,0000,0020,0000,0140,0000,0000,0220,0000,0000,0200,0017,0141
+};
diff --git a/troff/mbwc.c b/troff/mbwc.c
@@ -0,0 +1,165 @@
+#include <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;
+}
+
diff --git a/troff/mkfile b/troff/mkfile
@@ -0,0 +1,57 @@
+<$PLAN9/src/mkhdr
+
+TARG=troff
+OFILES=n1.$O\
+ n2.$O\
+ n3.$O\
+ n4.$O\
+ n5.$O\
+ t6.$O\
+ n6.$O\
+ n7.$O\
+ n8.$O\
+ n9.$O\
+ t10.$O\
+ n10.$O\
+ t11.$O\
+ ni.$O\
+ hytab.$O\
+ suftab.$O\
+ dwbinit.$O\
+ mbwc.$O
+
+HFILES=tdef.h\
+ fns.h\
+ ext.h\
+ dwbinit.h\
+
+
+<$PLAN9/src/mkone
+CFLAGS=-DUNICODE
+
+TMACDIR='"tmac/tmac."'
+FONTDIR='"troff/font"'
+NTERMDIR='"troff/term/tab."'
+ALTHYPHENS='"lib/hyphen.tex"'
+TEXHYPHENS='"#9/lib/hyphen.tex"'
+DWBHOME='"#9/"'
+TDEVNAME='"utf"'
+NDEVNAME='"utf"'
+
+ni.$O: ni.c $HFILES
+ $CC $CFLAGS -DTMACDIR=$TMACDIR ni.c
+
+t10.$O: t10.c $HFILES
+ $CC $CFLAGS -DTDEVNAME=$TDEVNAME t10.c
+
+n1.$O: n1.c $HFILES
+ $CC $CFLAGS -DFONTDIR=$FONTDIR -DNTERMDIR=$NTERMDIR -DTEXHYPHENS=$TEXHYPHENS -DALTHYPHENS=$ALTHYPHENS -DDWBHOME=$DWBHOME n1.c
+
+n10.$O: n10.c $HFILES
+ $CC $CFLAGS -DTDEVNAME=$NDEVNAME n10.c
+
+n8.$O: n8.c $HFILES
+ $CC $CFLAGS -DTEXHYPHENS=$TEXHYPHENS n8.c
+
+dwbinit.$O: dwbinit.c
+ $CC $CFLAGS -DDWBHOME=$DWBHOME dwbinit.c
diff --git a/troff/n1.c b/troff/n1.c
@@ -0,0 +1,1134 @@
+/*
+ * n1.c
+ *
+ * consume options, initialization, main loop,
+ * input routines, escape function calling
+ */
+
+#include <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;
+ }
+}
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);
+}
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++;
+}
diff --git a/troff/n3.c b/troff/n3.c
@@ -0,0 +1,954 @@
+/*
+ * troff3.c
+ *
+ * macro and string routines, storage allocation
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+Tchar *argtop;
+int pagech = '%';
+int strflg;
+
+#define MHASHSIZE 128 /* must be 2**n */
+#define MHASH(x) ((x>>6)^x) & (MHASHSIZE-1)
+Contab *mhash[MHASHSIZE];
+
+
+Blockp *blist; /* allocated blocks for macros and strings */
+int nblist; /* how many there are */
+int bfree = -1; /* first (possible) free block in the list */
+
+Contab *contabp = NULL;
+#define MDELTA 500
+int nm = 0;
+
+int savname; /* name of macro/string being defined */
+int savslot; /* place in Contab of savname */
+int freeslot = -1; /* first (possible) free slot in contab */
+
+void prcontab(Contab *p)
+{
+ int i;
+ for (i = 0; i < nm; i++)
+ if (p)
+ if (p[i].rq != 0)
+ fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq));
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+}
+
+
+void blockinit(void)
+{
+ blist = (Blockp *) calloc(NBLIST, sizeof(Blockp));
+ if (blist == NULL) {
+ ERROR "not enough room for %d blocks", NBLIST WARN;
+ done2(1);
+ }
+ nblist = NBLIST;
+ blist[0].nextoff = blist[1].nextoff = -1;
+ blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ /* -1 prevents blist[0] from being used; temporary fix */
+ /* for a design botch: offset==0 is overloaded. */
+ /* blist[1] reserved for .rd indicator -- also unused. */
+ /* but someone unwittingly looks at these, so allocate something */
+ bfree = 2;
+}
+
+
+char *grow(char *ptr, int num, int size) /* make array bigger */
+{
+ char *p;
+
+ if (ptr == NULL)
+ p = (char *) calloc(num, size);
+ else
+ p = (char *) realloc(ptr, num * size);
+ return p;
+}
+
+void mnspace(void)
+{
+ nm = sizeof(contab)/sizeof(Contab) + MDELTA;
+ freeslot = sizeof(contab)/sizeof(Contab) + 1;
+ contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab));
+ if (contabp == NULL) {
+ ERROR "not enough memory for namespace of %d marcos", nm WARN;
+ exit(1);
+ }
+ contabp = (Contab *) memcpy((char *) contabp, (char *)contab,
+ sizeof(contab));
+ if (contabp == NULL) {
+ ERROR "Cannot reinitialize macro/request name list" WARN;
+ exit(1);
+ }
+
+}
+
+void caseig(void)
+{
+ int i;
+ Offset oldoff = offset;
+
+ offset = 0;
+ i = copyb();
+ offset = oldoff;
+ if (i != '.')
+ control(i, 1);
+}
+
+
+void casern(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0)
+ return;
+ skip();
+ clrmn(findmn(j = getrq()));
+ if (j) {
+ munhash(&contabp[oldmn]);
+ contabp[oldmn].rq = j;
+ maddhash(&contabp[oldmn]);
+ if (dip != d )
+ for (k = dilev; k; k--)
+ if (d[k].curd == i)
+ d[k].curd = j;
+ }
+}
+
+void maddhash(Contab *rp)
+{
+ Contab **hp;
+
+ if (rp->rq == 0)
+ return;
+ hp = &mhash[MHASH(rp->rq)];
+ rp->link = *hp;
+ *hp = rp;
+}
+
+void munhash(Contab *mp)
+{
+ Contab *p;
+ Contab **lp;
+
+ if (mp->rq == 0)
+ return;
+ lp = &mhash[MHASH(mp->rq)];
+ p = *lp;
+ while (p) {
+ if (p == mp) {
+ *lp = p->link;
+ p->link = 0;
+ return;
+ }
+ lp = &p->link;
+ p = p->link;
+ }
+}
+
+void mrehash(void)
+{
+ Contab *p;
+ int i;
+
+ for (i=0; i < MHASHSIZE; i++)
+ mhash[i] = 0;
+ for (p=contabp; p < &contabp[nm]; p++)
+ p->link = 0;
+ for (p=contabp; p < &contabp[nm]; p++) {
+ if (p->rq == 0)
+ continue;
+ i = MHASH(p->rq);
+ p->link = mhash[i];
+ mhash[i] = p;
+ }
+}
+
+void caserm(void)
+{
+ int j;
+ int k = 0;
+
+ lgf++;
+g0:
+ while (!skip() && (j = getrq()) != 0) {
+ if (dip != d)
+ for (k = dilev; k; k--)
+ if (d[k].curd == j) {
+ ERROR "cannot remove diversion %s during definition",
+ unpair(j) WARN;
+ goto g0;
+ }
+ clrmn(findmn(j));
+ }
+ lgf--;
+}
+
+
+void caseas(void)
+{
+ app++;
+ caseds();
+}
+
+
+void caseds(void)
+{
+ ds++;
+ casede();
+}
+
+
+void caseam(void)
+{
+ app++;
+ casede();
+}
+
+
+void casede(void)
+{
+ int i, req;
+ Offset savoff;
+
+ req = '.';
+ lgf++;
+ skip();
+ if ((i = getrq()) == 0)
+ goto de1;
+ if ((offset = finds(i)) == 0)
+ goto de1;
+ if (newmn)
+ savslot = newmn;
+ else
+ savslot = findmn(i);
+ savname = i;
+ if (ds)
+ copys();
+ else
+ req = copyb();
+ clrmn(oldmn);
+ if (newmn) {
+ if (contabp[newmn].rq)
+ munhash(&contabp[newmn]);
+ contabp[newmn].rq = i;
+ maddhash(&contabp[newmn]);
+
+ }
+ if (apptr) {
+ savoff = offset;
+ offset = apptr;
+ wbf((Tchar) IMP);
+ offset = savoff;
+ }
+ offset = dip->op;
+ if (req != '.')
+ control(req, 1);
+de1:
+ ds = app = 0;
+}
+
+
+int findmn(int i)
+{
+ Contab *p;
+
+ for (p = mhash[MHASH(i)]; p; p = p->link)
+ if (i == p->rq)
+ return(p - contabp);
+ return(-1);
+}
+
+
+void clrmn(int i)
+{
+ if (i >= 0) {
+ if (contabp[i].mx)
+ ffree(contabp[i].mx);
+ munhash(&contabp[i]);
+ contabp[i].rq = 0;
+ contabp[i].mx = 0;
+ contabp[i].emx = 0;
+ contabp[i].f = 0;
+ if (contabp[i].divsiz != NULL) {
+ free(contabp[i].divsiz);
+ contabp[i].divsiz = NULL;
+ }
+ if (freeslot > i)
+ freeslot = i;
+ }
+}
+
+void growcontab(void)
+{
+ nm += MDELTA;
+ contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab));
+ if (contabp == NULL) {
+ ERROR "Too many (%d) string/macro names", nm WARN;
+ done2(02);
+ } else {
+ memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab),
+ 0, MDELTA * sizeof(Contab));
+ mrehash();
+ }
+}
+
+
+Offset finds(int mn)
+{
+ int i;
+ Offset savip;
+
+ oldmn = findmn(mn);
+ newmn = 0;
+ apptr = 0;
+ if (app && oldmn >= 0 && contabp[oldmn].mx) {
+ savip = ip;
+ ip = contabp[oldmn].emx;
+ oldmn = -1;
+ apptr = ip;
+ if (!diflg)
+ ip = incoff(ip);
+ nextb = ip;
+ ip = savip;
+ } else {
+ for (i = freeslot; i < nm; i++) {
+ if (contabp[i].rq == 0)
+ break;
+ }
+ if (i == nm)
+ growcontab();
+ freeslot = i + 1;
+ if ((nextb = alloc()) == -1) {
+ app = 0;
+ if (macerr++ > 1)
+ done2(02);
+ if (nextb == 0)
+ ERROR "Not enough space for string/macro names" WARN;
+ edone(04);
+ return(offset = 0);
+ }
+ contabp[i].mx = nextb;
+ if (!diflg) {
+ newmn = i;
+ if (oldmn == -1)
+ contabp[i].rq = -1;
+ } else {
+ contabp[i].rq = mn;
+ maddhash(&contabp[i]);
+ }
+ }
+ app = 0;
+ return(offset = nextb);
+}
+
+int skip(void)
+{
+ Tchar i;
+
+ while (cbits(i = getch()) == ' ' || ismot(i))
+ ;
+ ch = i;
+ return(nlflg);
+}
+
+
+int copyb(void)
+{
+ int i, j, state;
+ Tchar ii;
+ int req, k;
+ Offset savoff;
+ Uchar *p;
+
+ savoff = 0;
+ if (skip() || !(j = getrq()))
+ j = '.';
+ req = j;
+ p = unpair(j);
+ /* was: k = j >> BYTE; j &= BYTEMASK; */
+ j = p[0];
+ k = p[1];
+ copyf++;
+ flushi();
+ nlflg = 0;
+ state = 1;
+
+/* state 0 eat up
+ * state 1 look for .
+ * state 2 look for first char of end macro
+ * state 3 look for second char of end macro
+ */
+
+ while (1) {
+ i = cbits(ii = getch());
+ if (state == 3) {
+ if (i == k)
+ break;
+ if (!k) {
+ ch = ii;
+ i = getach();
+ ch = ii;
+ if (!i)
+ break;
+ }
+ state = 0;
+ goto c0;
+ }
+ if (i == '\n') {
+ state = 1;
+ nlflg = 0;
+ goto c0;
+ }
+ if (state == 1 && i == '.') {
+ state++;
+ savoff = offset;
+ goto c0;
+ }
+ if (state == 2 && i == j) {
+ state++;
+ goto c0;
+ }
+ state = 0;
+c0:
+ if (offset)
+ wbf(ii);
+ }
+ if (offset) {
+ offset = savoff;
+ wbf((Tchar)0);
+ }
+ copyf--;
+ return(req);
+}
+
+
+void copys(void)
+{
+ Tchar i;
+
+ copyf++;
+ if (skip())
+ goto c0;
+ if (cbits(i = getch()) != '"')
+ wbf(i);
+ while (cbits(i = getch()) != '\n')
+ wbf(i);
+c0:
+ wbf((Tchar)0);
+ copyf--;
+}
+
+
+Offset alloc(void) /* return free Offset in nextb */
+{
+ int i, j;
+
+ for (i = bfree; i < nblist; i++)
+ if (blist[i].nextoff == 0)
+ break;
+ if (i == nblist) {
+ blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp));
+ if (blist == NULL) {
+ ERROR "can't grow blist for string/macro defns" WARN;
+ done2(2);
+ }
+ nblist *= 2;
+ for (j = i; j < nblist; j++) {
+ blist[j].nextoff = 0;
+ blist[j].bp = 0;
+ }
+ }
+ blist[i].nextoff = -1; /* this block is the end */
+ bfree = i + 1;
+ if (blist[i].bp == 0)
+ blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar));
+ if (blist[i].bp == NULL) {
+ ERROR "can't allocate memory for string/macro definitions" WARN;
+ done2(2);
+ }
+ nextb = (Offset) i * BLK;
+ return nextb;
+}
+
+
+void ffree(Offset i) /* free list of blocks starting at blist(o) */
+{ /* (doesn't actually free the blocks, just the pointers) */
+ int j;
+
+ for ( ; blist[j = bindex(i)].nextoff != -1; ) {
+ if (bfree > j)
+ bfree = j;
+ i = blist[j].nextoff;
+ blist[j].nextoff = 0;
+ }
+ blist[j].nextoff = 0;
+}
+
+
+void wbf(Tchar i) /* store i into offset, get ready for next one */
+{
+ int j, off;
+
+ if (!offset)
+ return;
+ j = bindex(offset);
+ if (i == 0)
+ contabp[savslot].emx = offset;
+ off = boffset(offset);
+ blist[j].bp[off++] = i;
+ offset++;
+ if (pastend(offset)) { /* off the end of this block */
+ if (blist[j].nextoff == -1) {
+ if ((nextb = alloc()) == -1) {
+ ERROR "Out of temp file space" WARN;
+ done2(01);
+ }
+ blist[j].nextoff = nextb;
+ }
+ offset = blist[j].nextoff;
+ }
+}
+
+
+Tchar rbf(void) /* return next char from blist[] block */
+{
+ Tchar i, j;
+
+ if (ip == RD_OFFSET) { /* for rdtty */
+ if (j = rdtty())
+ return(j);
+ else
+ return(popi());
+ }
+
+ i = rbf0(ip);
+ if (i == 0) {
+ if (!app)
+ i = popi();
+ return(i);
+ }
+ ip = incoff(ip);
+ return(i);
+}
+
+
+Offset xxxincoff(Offset p) /* get next blist[] block */
+{
+ p++;
+ if (pastend(p)) { /* off the end of this block */
+ if ((p = blist[bindex(p-1)].nextoff) == -1) { /* and nothing was allocated after it */
+ ERROR "Bad storage allocation" WARN;
+ done2(-5);
+ }
+ }
+ return(p);
+}
+
+
+Tchar popi(void)
+{
+ Stack *p;
+
+ if (frame == stk)
+ return(0);
+ if (strflg)
+ strflg--;
+ p = nxf = frame;
+ p->nargs = 0;
+ frame = p->pframe;
+ ip = p->pip;
+ pendt = p->ppendt;
+ lastpbp = p->lastpbp;
+ return(p->pch);
+}
+
+/*
+ * test that the end of the allocation is above a certain location
+ * in memory
+ */
+#define SPACETEST(base, size) \
+ if ((char*)base + size >= (char*)stk+STACKSIZE) \
+ ERROR "Stacksize overflow in n3" WARN
+
+Offset pushi(Offset newip, int mname)
+{
+ Stack *p;
+
+ SPACETEST(nxf, sizeof(Stack));
+ p = nxf;
+ p->pframe = frame;
+ p->pip = ip;
+ p->ppendt = pendt;
+ p->pch = ch;
+ p->lastpbp = lastpbp;
+ p->mname = mname;
+ lastpbp = pbp;
+ pendt = ch = 0;
+ frame = nxf;
+ if (nxf->nargs == 0)
+ nxf += 1;
+ else
+ nxf = (Stack *)argtop;
+ return(ip = newip);
+}
+
+
+void *setbrk(int x)
+{
+ char *i;
+
+ if ((i = (char *) calloc(x, 1)) == 0) {
+ ERROR "Core limit reached" WARN;
+ edone(0100);
+ }
+ return(i);
+}
+
+
+int getsn(void)
+{
+ int i;
+
+ if ((i = getach()) == 0)
+ return(0);
+ if (i == '(')
+ return(getrq());
+ else
+ return(i);
+}
+
+
+Offset setstr(void)
+{
+ int i, j;
+
+ lgf++;
+ if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) {
+ lgf--;
+ return(0);
+ } else {
+ SPACETEST(nxf, sizeof(Stack));
+ nxf->nargs = 0;
+ strflg++;
+ lgf--;
+ return pushi(contabp[j].mx, i);
+ }
+}
+
+
+
+void collect(void)
+{
+ int j;
+ Tchar i, *strp, *lim, **argpp, **argppend;
+ int quote;
+ Stack *savnxf;
+
+ copyf++;
+ nxf->nargs = 0;
+ savnxf = nxf;
+ if (skip())
+ goto rtn;
+
+ {
+ char *memp;
+ memp = (char *)savnxf;
+ /*
+ * 1 s structure for the macro descriptor
+ * APERMAC Tchar *'s for pointers into the strings
+ * space for the Tchar's themselves
+ */
+ memp += sizeof(Stack);
+ /*
+ * CPERMAC = the total # of characters for ALL arguments
+ */
+#define CPERMAC 200
+#define APERMAC 9
+ memp += APERMAC * sizeof(Tchar *);
+ memp += CPERMAC * sizeof(Tchar);
+ nxf = (Stack *)memp;
+ }
+ lim = (Tchar *)nxf;
+ argpp = (Tchar **)(savnxf + 1);
+ argppend = &argpp[APERMAC];
+ SPACETEST(argppend, sizeof(Tchar *));
+ strp = (Tchar *)argppend;
+ /*
+ * Zero out all the string pointers before filling them in.
+ */
+ for (j = 0; j < APERMAC; j++)
+ argpp[j] = 0;
+ /* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x",
+ * savnxf, nxf, argpp, strp, lim WARN;
+ */
+ strflg = 0;
+ while (argpp != argppend && !skip()) {
+ *argpp++ = strp;
+ quote = 0;
+ if (cbits(i = getch()) == '"')
+ quote++;
+ else
+ ch = i;
+ while (1) {
+ i = getch();
+/* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */
+ if (nlflg || (!quote && argpp != argppend && cbits(i) == ' '))
+ break; /* collects rest into $9 */
+ if ( quote
+ && cbits(i) == '"'
+ && cbits(i = getch()) != '"') {
+ ch = i;
+ break;
+ }
+ *strp++ = i;
+ if (strflg && strp >= lim) {
+ /* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */
+ ERROR "Macro argument too long" WARN;
+ copyf--;
+ edone(004);
+ }
+ SPACETEST(strp, 3 * sizeof(Tchar));
+ }
+ *strp++ = 0;
+ }
+ nxf = savnxf;
+ nxf->nargs = argpp - (Tchar **)(savnxf + 1);
+ argtop = strp;
+rtn:
+ copyf--;
+}
+
+
+void seta(void)
+{
+ int i;
+
+ i = cbits(getch()) - '0';
+ if (i > 0 && i <= APERMAC && i <= frame->nargs)
+ pushback(*(((Tchar **)(frame + 1)) + i - 1));
+}
+
+
+void caseda(void)
+{
+ app++;
+ casedi();
+}
+
+void casegd(void)
+{
+ int i, j;
+
+ skip();
+ if ((i = getrq()) == 0)
+ return;
+ if ((j = findmn(i)) >= 0) {
+ if (contabp[j].divsiz != NULL) {
+ numtabp[DN].val = contabp[j].divsiz->dix;
+ numtabp[DL].val = contabp[j].divsiz->diy;
+ }
+ }
+}
+
+#define FINDDIV(o) if ((o = findmn(dip->curd)) < 0) \
+ ERROR "lost diversion %s", unpair(dip->curd) WARN
+
+void casedi(void)
+{
+ int i, j, *k;
+
+ lgf++;
+ if (skip() || (i = getrq()) == 0) {
+ if (dip != d) {
+ FINDDIV(savslot);
+ wbf((Tchar)0);
+ }
+ if (dilev > 0) {
+ numtabp[DN].val = dip->dnl;
+ numtabp[DL].val = dip->maxl;
+ FINDDIV(j);
+ if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) {
+ ERROR "Cannot alloc diversion size" WARN;
+ done2(1);
+ } else {
+ contabp[j].divsiz->dix = numtabp[DN].val;
+ contabp[j].divsiz->diy = numtabp[DL].val;
+ }
+ dip = &d[--dilev];
+ offset = dip->op;
+ }
+ goto rtn;
+ }
+ if (++dilev == NDI) {
+ --dilev;
+ ERROR "Diversions nested too deep" WARN;
+ edone(02);
+ }
+ if (dip != d) {
+ FINDDIV(j);
+ savslot = j;
+ wbf((Tchar)0);
+ }
+ diflg++;
+ dip = &d[dilev];
+ dip->op = finds(i);
+ dip->curd = i;
+ clrmn(oldmn);
+ k = (int *) & dip->dnl;
+ for (j = 0; j < 10; j++)
+ k[j] = 0; /*not op and curd*/
+rtn:
+ app = 0;
+ diflg = 0;
+}
+
+
+void casedt(void)
+{
+ lgf++;
+ dip->dimac = dip->ditrap = dip->ditf = 0;
+ skip();
+ dip->ditrap = vnumb((int *)0);
+ if (nonumb)
+ return;
+ skip();
+ dip->dimac = getrq();
+}
+
+#define LNSIZE 4000
+void casetl(void)
+{
+ int j;
+ int w[3];
+ Tchar buf[LNSIZE];
+ Tchar *tp;
+ Tchar i, delim;
+
+ /*
+ * bug fix
+ *
+ * if .tl is the first thing in the file, the p1
+ * doesn't come out, also the pagenumber will be 0
+ *
+ * tends too confuse the device filter (and the user as well)
+ */
+ if (dip == d && numtabp[NL].val == -1)
+ newline(1);
+ dip->nls = 0;
+ skip();
+ if (ismot(delim = getch())) {
+ ch = delim;
+ delim = '\'';
+ } else
+ delim = cbits(delim);
+ tp = buf;
+ numtabp[HP].val = 0;
+ w[0] = w[1] = w[2] = 0;
+ j = 0;
+ while (cbits(i = getch()) != '\n') {
+ if (cbits(i) == cbits(delim)) {
+ if (j < 3)
+ w[j] = numtabp[HP].val;
+ numtabp[HP].val = 0;
+ if (w[j] != 0)
+ *tp++ = WORDSP;
+ j++;
+ *tp++ = 0;
+ } else {
+ if (cbits(i) == pagech) {
+ setn1(numtabp[PN].val, numtabp[findr('%')].fmt,
+ i&SFMASK);
+ continue;
+ }
+ numtabp[HP].val += width(i);
+ if (tp < &buf[LNSIZE-10]) {
+ if (cbits(i) == ' ' && *tp != WORDSP)
+ *tp++ = WORDSP;
+ *tp++ = i;
+ } else {
+ ERROR "Overflow in casetl" WARN;
+ }
+ }
+ }
+ if (j<3)
+ w[j] = numtabp[HP].val;
+ *tp++ = 0;
+ *tp++ = 0;
+ *tp = 0;
+ tp = buf;
+ if (NROFF)
+ horiz(po);
+ while (i = *tp++)
+ pchar(i);
+ if (w[1] || w[2])
+ horiz(j = quant((lt - w[1]) / 2 - w[0], HOR));
+ while (i = *tp++)
+ pchar(i);
+ if (w[2]) {
+ horiz(lt - w[0] - w[1] - w[2] - j);
+ while (i = *tp++)
+ pchar(i);
+ }
+ newline(0);
+ if (dip != d) {
+ if (dip->dnl > dip->hnl)
+ dip->hnl = dip->dnl;
+ } else {
+ if (numtabp[NL].val > dip->hnl)
+ dip->hnl = numtabp[NL].val;
+ }
+}
+
+
+void casepc(void)
+{
+ pagech = chget(IMP);
+}
+
+
+void casepm(void)
+{
+ int i, k;
+ int xx, cnt, tcnt, kk, tot;
+ Offset j;
+
+ kk = cnt = tcnt = 0;
+ tot = !skip();
+ stackdump();
+ for (i = 0; i < nm; i++) {
+ if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0)
+ continue;
+ tcnt++;
+ j = contabp[i].mx;
+ for (k = 1; (j = blist[bindex(j)].nextoff) != -1; )
+ k++;
+ cnt++;
+ kk += k;
+ if (!tot)
+ fprintf(stderr, "%-2.2s %d\n", unpair(xx), k);
+ }
+ fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk);
+}
+
+void stackdump(void) /* dumps stack of macros in process */
+{
+ Stack *p;
+
+ if (frame != stk) {
+ fprintf(stderr, "stack: ");
+ for (p = frame; p != stk; p = p->pframe)
+ fprintf(stderr, "%s ", unpair(p->mname));
+ fprintf(stderr, "\n");
+ }
+}
diff --git a/troff/n4.c b/troff/n4.c
@@ -0,0 +1,828 @@
+/*
+ * troff4.c
+ *
+ * number registers, conversion, arithmetic
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+
+int regcnt = NNAMES;
+int falsef = 0; /* on if inside false branch of if */
+
+#define NHASHSIZE 128 /* must be 2**n */
+#define NHASH(i) ((i>>6)^i) & (NHASHSIZE-1)
+Numtab *nhash[NHASHSIZE];
+
+Numtab *numtabp = NULL;
+#define NDELTA 400
+int ncnt = 0;
+
+void setn(void)
+{
+ int i, j, f;
+ Tchar ii;
+ Uchar *p;
+ char buf[NTM]; /* for \n(.S */
+
+ f = nform = 0;
+ if ((i = cbits(ii = getach())) == '+')
+ f = 1;
+ else if (i == '-')
+ f = -1;
+ else if (ii) /* don't put it back if it's already back (thanks to jaap) */
+ ch = ii;
+ if (falsef)
+ f = 0;
+ if ((i = getsn()) == 0)
+ return;
+ p = unpair(i);
+ if (p[0] == '.')
+ switch (p[1]) {
+ case 's':
+ i = pts;
+ break;
+ case 'v':
+ i = lss;
+ break;
+ case 'f':
+ i = font;
+ break;
+ case 'p':
+ i = pl;
+ break;
+ case 't':
+ i = findt1();
+ break;
+ case 'o':
+ i = po;
+ break;
+ case 'l':
+ i = ll;
+ break;
+ case 'i':
+ i = in;
+ break;
+ case '$':
+ i = frame->nargs;
+ break;
+ case 'A':
+ i = ascii;
+ break;
+ case 'c':
+ i = numtabp[CD].val;
+ break;
+ case 'n':
+ i = lastl;
+ break;
+ case 'a':
+ i = ralss;
+ break;
+ case 'h':
+ i = dip->hnl;
+ break;
+ case 'd':
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ break;
+ case 'u':
+ i = fi;
+ break;
+ case 'j':
+ i = ad + 2 * admod;
+ break;
+ case 'w':
+ i = widthp;
+ break;
+ case 'x':
+ i = nel;
+ break;
+ case 'y':
+ i = un;
+ break;
+ case 'T':
+ i = dotT;
+ break; /* -Tterm used in nroff */
+ case 'V':
+ i = VERT;
+ break;
+ case 'H':
+ i = HOR;
+ break;
+ case 'k':
+ i = ne;
+ break;
+ case 'P':
+ i = print;
+ break;
+ case 'L':
+ i = ls;
+ break;
+ case 'R': /* maximal # of regs that can be addressed */
+ i = 255*256 - regcnt;
+ break;
+ case 'z':
+ p = unpair(dip->curd);
+ *pbp++ = p[1]; /* watch order */
+ *pbp++ = p[0];
+ return;
+ case 'b':
+ i = bdtab[font];
+ break;
+ case 'F':
+ cpushback(cfname[ifi]);
+ return;
+ case 'S':
+ buf[0] = j = 0;
+ for( i = 0; tabtab[i] != 0 && i < NTAB; i++) {
+ if (i > 0)
+ buf[j++] = ' ';
+ sprintf(&buf[j], "%ld", tabtab[i] & TABMASK);
+ j = strlen(buf);
+ if ( tabtab[i] & RTAB)
+ sprintf(&buf[j], "uR");
+ else if (tabtab[i] & CTAB)
+ sprintf(&buf[j], "uC");
+ else
+ sprintf(&buf[j], "uL");
+ j += 2;
+ }
+ cpushback(buf);
+ return;
+ default:
+ goto s0;
+ }
+ else {
+s0:
+ if ((j = findr(i)) == -1)
+ i = 0;
+ else {
+ i = numtabp[j].val = numtabp[j].val + numtabp[j].inc * f;
+ nform = numtabp[j].fmt;
+ }
+ }
+ setn1(i, nform, (Tchar) 0);
+}
+
+Tchar numbuf[25];
+Tchar *numbufp;
+
+int wrc(Tchar i)
+{
+ if (numbufp >= &numbuf[24])
+ return(0);
+ *numbufp++ = i;
+ return(1);
+}
+
+
+
+/* insert into input number i, in format form, with size-font bits bits */
+void setn1(int i, int form, Tchar bits)
+{
+ numbufp = numbuf;
+ nrbits = bits;
+ nform = form;
+ fnumb(i, wrc);
+ *numbufp = 0;
+ pushback(numbuf);
+}
+
+void prnumtab(Numtab *p)
+{
+ int i;
+ for (i = 0; i < ncnt; i++)
+ if (p)
+ if (p[i].r != 0)
+ fprintf(stderr, "slot %d, %s, val %d\n", i, unpair(p[i].r), p[i].val);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+ else
+ fprintf(stderr, "slot %d empty\n", i);
+}
+
+void nnspace(void)
+{
+ ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA;
+ numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "not enough memory for registers (%d)", ncnt WARN;
+ exit(1);
+ }
+ numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab,
+ sizeof(numtab));
+ if (numtabp == NULL) {
+ ERROR "Cannot initialize registers" WARN;
+ exit(1);
+ }
+}
+
+void grownumtab(void)
+{
+ ncnt += NDELTA;
+ numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab));
+ if (numtabp == NULL) {
+ ERROR "Too many number registers (%d)", ncnt WARN;
+ done2(04);
+ } else {
+ memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numtab),
+ 0, NDELTA * sizeof(Numtab));
+ nrehash();
+ }
+}
+
+void nrehash(void)
+{
+ Numtab *p;
+ int i;
+
+ for (i=0; 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);
+}
diff --git a/troff/n5.c b/troff/n5.c
@@ -0,0 +1,1150 @@
+/*
+ * troff5.c
+ *
+ * misc processing requests
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int iflist[NIF];
+int ifx;
+int ifnum = 0; /* trying numeric expression for .if or .ie condition */
+
+void casead(void)
+{
+ int i;
+
+ ad = 1;
+ /* leave admod alone */
+ if (skip())
+ return;
+ switch (i = cbits(getch())) {
+ case 'r': /* right adj, left ragged */
+ admod = 2;
+ break;
+ case 'l': /* left adj, right ragged */
+ admod = ad = 0; /* same as casena */
+ break;
+ case 'c': /*centered adj*/
+ admod = 1;
+ break;
+ case 'b':
+ case 'n':
+ admod = 0;
+ break;
+ case '0':
+ case '2':
+ case '4':
+ ad = 0;
+ case '1':
+ case '3':
+ case '5':
+ admod = (i - '0') / 2;
+ }
+}
+
+
+void casena(void)
+{
+ ad = 0;
+}
+
+
+void casefi(void)
+{
+ tbreak();
+ fi = 1;
+ pendnf = 0;
+}
+
+
+void casenf(void)
+{
+ tbreak();
+ fi = 0;
+}
+
+
+void casers(void)
+{
+ dip->nls = 0;
+}
+
+
+void casens(void)
+{
+ dip->nls++;
+}
+
+int
+chget(int c)
+{
+ Tchar i;
+
+ i = 0;
+ if (skip() || ismot(i = getch()) || cbits(i) == ' ' || cbits(i) == '\n') {
+ ch = i;
+ return(c);
+ } else
+ return cbits(i); /* was (i & BYTEMASK) */
+}
+
+
+void casecc(void)
+{
+ cc = chget('.');
+}
+
+
+void casec2(void)
+{
+ c2 = chget('\'');
+}
+
+
+void casehc(void)
+{
+ ohc = chget(OHC);
+}
+
+
+void casetc(void)
+{
+ tabc = chget(0);
+}
+
+
+void caselc(void)
+{
+ dotc = chget(0);
+}
+
+
+void casehy(void)
+{
+ int i;
+
+ hyf = 1;
+ if (skip())
+ return;
+ noscale++;
+ i = atoi0();
+ noscale = 0;
+ if (nonumb)
+ return;
+ hyf = max(i, 0);
+}
+
+
+void casenh(void)
+{
+ hyf = 0;
+}
+
+int
+max(int aa, int bb)
+{
+ if (aa > bb)
+ return(aa);
+ else
+ return(bb);
+}
+
+
+void casece(void)
+{
+ int i;
+
+ noscale++;
+ skip();
+ i = max(atoi0(), 0);
+ if (nonumb)
+ i = 1;
+ tbreak();
+ ce = i;
+ noscale = 0;
+}
+
+
+void casein(void)
+{
+ int i;
+
+ if (skip())
+ i = in1;
+ else {
+ i = max(hnumb(&in), 0);
+ if (nonumb)
+ i = in1;
+ }
+ tbreak();
+ in1 = in;
+ in = i;
+ if (!nc) {
+ un = in;
+ setnel();
+ }
+}
+
+
+void casell(void)
+{
+ int i;
+
+ if (skip())
+ i = ll1;
+ else {
+ i = max(hnumb(&ll), INCH / 10);
+ if (nonumb)
+ i = ll1;
+ }
+ ll1 = ll;
+ ll = i;
+ setnel();
+}
+
+
+void caselt(void)
+{
+ int i;
+
+ if (skip())
+ i = lt1;
+ else {
+ i = max(hnumb(<), 0);
+ if (nonumb)
+ i = lt1;
+ }
+ lt1 = lt;
+ lt = i;
+}
+
+
+void caseti(void)
+{
+ int i;
+
+ if (skip())
+ return;
+ i = max(hnumb(&in), 0);
+ tbreak();
+ un1 = i;
+ setnel();
+}
+
+
+void casels(void)
+{
+ int i;
+
+ noscale++;
+ if (skip())
+ i = ls1;
+ else {
+ i = max(inumb(&ls), 1);
+ if (nonumb)
+ i = ls1;
+ }
+ ls1 = ls;
+ ls = i;
+ noscale = 0;
+}
+
+
+void casepo(void)
+{
+ int i;
+
+ if (skip())
+ i = po1;
+ else {
+ i = max(hnumb(&po), 0);
+ if (nonumb)
+ i = po1;
+ }
+ po1 = po;
+ po = i;
+ if (TROFF & !ascii)
+ esc += po - po1;
+}
+
+
+void casepl(void)
+{
+ int i;
+
+ skip();
+ if ((i = vnumb(&pl)) == 0)
+ pl = 11 * INCH; /*11in*/
+ else
+ pl = i;
+ if (numtabp[NL].val > pl)
+ numtabp[NL].val = pl;
+}
+
+
+void casewh(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ return;
+ skip();
+ j = getrq();
+ if ((k = findn(i)) != NTRAP) {
+ mlist[k] = j;
+ return;
+ }
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == 0)
+ break;
+ if (k == NTRAP) {
+ flusho();
+ ERROR "cannot plant trap." WARN;
+ return;
+ }
+ mlist[k] = j;
+ nlist[k] = i;
+}
+
+
+void casech(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ if (!(j = getrq()))
+ return;
+ else
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == j)
+ break;
+ if (k == NTRAP)
+ return;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ mlist[k] = 0;
+ nlist[k] = i;
+}
+
+int
+findn(int i)
+{
+ int k;
+
+ for (k = 0; k < NTRAP; k++)
+ if ((nlist[k] == i) && (mlist[k] != 0))
+ break;
+ return(k);
+}
+
+
+void casepn(void)
+{
+ int i;
+
+ skip();
+ noscale++;
+ i = max(inumb(&numtabp[PN].val), 0);
+ noscale = 0;
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ }
+}
+
+
+void casebp(void)
+{
+ int i;
+ Stack *savframe;
+
+ if (dip != d)
+ return;
+ savframe = frame;
+ skip();
+ if ((i = inumb(&numtabp[PN].val)) < 0)
+ i = 0;
+ tbreak();
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ } else if (dip->nls)
+ return;
+ eject(savframe);
+}
+
+void casetm(void)
+{
+ casetm1(0, stderr);
+}
+
+
+void casefm(void)
+{
+ static struct fcache {
+ char *name;
+ FILE *fp;
+ } fcache[15];
+ int i;
+
+ if ( skip() || !getname()) {
+ ERROR "fm: missing filename" WARN;
+ return;
+ }
+
+ for (i = 0; i < 15 && fcache[i].fp != NULL; i++) {
+ if (strcmp(nextf, fcache[i].name) == 0)
+ break;
+ }
+ if (i >= 15) {
+ ERROR "fm: too many streams" WARN;
+ return;
+ }
+ if (fcache[i].fp == NULL) {
+ if( (fcache[i].fp = fopen(unsharp(nextf), "w")) == NULL) {
+ ERROR "fm: cannot open %s", nextf WARN;
+ return;
+ }
+ fcache[i].name = strdupl(nextf);
+ }
+ casetm1(0, fcache[i].fp);
+}
+
+void casetm1(int ab, FILE *out)
+{
+ int i, j, c;
+ char *p;
+ char tmbuf[NTM];
+
+ lgf++;
+ copyf++;
+ if (ab) {
+ if (skip())
+ ERROR "User Abort" WARN;
+ else {
+ extern int error;
+ int savtrac = trace;
+ i = trace = 0;
+ noscale++;
+ i = inumb(&trace);
+ noscale--;
+ if (i) {
+ error = i;
+ if (nlflg || skip())
+ ERROR "User Abort, exit code %d", i WARN;
+ }
+ trace = savtrac;
+ }
+ } else
+ skip();
+ for (i = 0; i < NTM - 2; ) {
+ if ((c = cbits(getch())) == '\n' || c == RIGHT)
+ break;
+ else if (c == MINUS) { /* special pleading for strange encodings */
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '-';
+ } else if (c == PRESC) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = 'e';
+ } else if (c == FILLER) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '&';
+ } else if (c == UNPAD) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = ' ';
+ } else if (c == OHC) {
+ tmbuf[i++] = '\\';
+ tmbuf[i++] = '%';
+ } else if (c >= ALPHABET) {
+ p = chname(c);
+ switch (*p) {
+ case MBchar:
+ strcpy(&tmbuf[i], p+1);
+ break;
+ case Number:
+ sprintf(&tmbuf[i], "\\N'%s'", p+1);
+ break;
+ case Troffchar:
+ if ((j = strlen(p+1)) == 2)
+ sprintf(&tmbuf[i], "\\(%s", p+1);
+ else
+ sprintf(&tmbuf[i], "\\C'%s'", p+1);
+ break;
+ default:
+ sprintf(&tmbuf[i]," %s? ", p);
+ break;
+ }
+ j = strlen(&tmbuf[i]);
+ i += j;
+ } else
+ tmbuf[i++] = c;
+ }
+ tmbuf[i] = 0;
+ if (ab) /* truncate output */
+ obufp = obuf; /* should be a function in n2.c */
+ flusho();
+ if (i)
+ fprintf(out, "%s\n", tmbuf);
+ fflush(out);
+ copyf--;
+ lgf--;
+}
+
+
+void casesp(void)
+{
+ casesp1(0);
+}
+
+void casesp1(int a)
+{
+ int i, j, savlss;
+
+ tbreak();
+ if (dip->nls || trap)
+ return;
+ i = findt1();
+ if (!a) {
+ skip();
+ j = vnumb((int *)0);
+ if (nonumb)
+ j = lss;
+ } else
+ j = a;
+ if (j == 0)
+ return;
+ if (i < j)
+ j = i;
+ savlss = lss;
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ if ((i + j) < 0)
+ j = -i;
+ lss = j;
+ newline(0);
+ lss = savlss;
+}
+
+
+void casert(void)
+{
+ int a, *p;
+
+ skip();
+ if (dip != d)
+ p = &dip->dnl;
+ else
+ p = &numtabp[NL].val;
+ a = vnumb(p);
+ if (nonumb)
+ a = dip->mkline;
+ if ((a < 0) || (a >= *p))
+ return;
+ nb++;
+ casesp1(a - *p);
+}
+
+
+void caseem(void)
+{
+ lgf++;
+ skip();
+ em = getrq();
+}
+
+
+void casefl(void)
+{
+ tbreak();
+ if (!ascii)
+ ptflush();
+ flusho();
+}
+
+
+void caseev(void)
+{
+ int nxev;
+
+ if (skip()) {
+e0:
+ if (evi == 0)
+ return;
+ nxev = evlist[--evi];
+ goto e1;
+ }
+ noscale++;
+ nxev = atoi0();
+ noscale = 0;
+ if (nonumb)
+ goto e0;
+ flushi();
+ if (nxev >= NEV || nxev < 0 || evi >= EVLSZ) {
+ flusho();
+ ERROR "cannot do .ev %d", nxev WARN;
+ if (error)
+ done2(040);
+ else
+ edone(040);
+ return;
+ }
+ evlist[evi++] = ev;
+e1:
+ if (ev == nxev)
+ return;
+ ev = nxev;
+ envp = &env[ev];
+}
+
+void envcopy(Env *e1, Env *e2) /* copy env e2 to e1 */
+{
+ *e1 = *e2; /* rumor hath that this fails on some machines */
+}
+
+
+void caseel(void)
+{
+ if (--ifx < 0) {
+ ifx = 0;
+ iflist[0] = 0;
+ }
+ caseif1(2);
+}
+
+
+void caseie(void)
+{
+ if (ifx >= NIF) {
+ ERROR "if-else overflow." WARN;
+ ifx = 0;
+ edone(040);
+ }
+ caseif1(1);
+ ifx++;
+}
+
+
+void caseif(void)
+{
+ caseif1(0);
+}
+
+void caseif1(int x)
+{
+ extern int falsef;
+ int notflag, true;
+ Tchar i;
+
+ if (x == 2) {
+ notflag = 0;
+ true = iflist[ifx];
+ goto i1;
+ }
+ true = 0;
+ skip();
+ if ((cbits(i = getch())) == '!') {
+ notflag = 1;
+ } else {
+ notflag = 0;
+ ch = i;
+ }
+ ifnum++;
+ i = atoi0();
+ ifnum = 0;
+ if (!nonumb) {
+ if (i > 0)
+ true++;
+ goto i1;
+ }
+ i = getch();
+ switch (cbits(i)) {
+ case 'e':
+ if (!(numtabp[PN].val & 01))
+ true++;
+ break;
+ case 'o':
+ if (numtabp[PN].val & 01)
+ true++;
+ break;
+ case 'n':
+ if (NROFF)
+ true++;
+ break;
+ case 't':
+ if (TROFF)
+ true++;
+ break;
+ case ' ':
+ break;
+ default:
+ true = cmpstr(i);
+ }
+i1:
+ true ^= notflag;
+ if (x == 1)
+ iflist[ifx] = !true;
+ if (true) {
+i2:
+ while ((cbits(i = getch())) == ' ')
+ ;
+ if (cbits(i) == LEFT)
+ goto i2;
+ ch = i;
+ nflush++;
+ } else {
+ if (!nlflg) {
+ copyf++;
+ falsef++;
+ eatblk(0);
+ copyf--;
+ falsef--;
+ }
+ }
+}
+
+void eatblk(int inblk)
+{
+ int cnt, i;
+
+ cnt = 0;
+ do {
+ if (ch) {
+ i = cbits(ch);
+ ch = 0;
+ } else
+ i = cbits(getch0());
+ if (i == ESC)
+ cnt++;
+ else {
+ if (cnt == 1)
+ switch (i) {
+ case '{': i = LEFT; break;
+ case '}': i = RIGHT; break;
+ case '\n': i = 'x'; break;
+ }
+ cnt = 0;
+ }
+ if (i == LEFT) eatblk(1);
+ } while ((!inblk && (i != '\n')) || (inblk && (i != RIGHT)));
+ if (i == '\n') {
+ nlflg++;
+ if (ip == 0)
+ numtabp[CD].val++;
+ }
+}
+
+int
+cmpstr(Tchar c)
+{
+ int j, delim;
+ Tchar i;
+ int val;
+ int savapts, savapts1, savfont, savfont1, savpts, savpts1;
+ Tchar string[1280];
+ Tchar *sp;
+
+ if (ismot(c))
+ return(0);
+ delim = cbits(c);
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ sp = string;
+ while ((j = cbits(i = getch()))!=delim && j!='\n' && sp<&string[1280-1])
+ *sp++ = i;
+ if (sp >= string + 1280) {
+ ERROR "too-long string compare." WARN;
+ edone(0100);
+ }
+ if (nlflg) {
+ val = sp==string;
+ goto rtn;
+ }
+ *sp = 0;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ val = 1;
+ sp = string;
+ while ((j = cbits(i = getch())) != delim && j != '\n') {
+ if (*sp != i) {
+ eat(delim);
+ val = 0;
+ goto rtn;
+ }
+ sp++;
+ }
+ if (*sp)
+ val = 0;
+rtn:
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ return(val);
+}
+
+
+void caserd(void)
+{
+
+ lgf++;
+ skip();
+ getname();
+ if (!iflg) {
+ if (quiet) {
+ if (NROFF) {
+ echo_off();
+ flusho();
+ }
+ fprintf(stderr, "\007"); /*bell*/
+ } else {
+ if (nextf[0]) {
+ fprintf(stderr, "%s:", nextf);
+ } else {
+ fprintf(stderr, "\007"); /*bell*/
+ }
+ }
+ }
+ collect();
+ tty++;
+ pushi(RD_OFFSET, PAIR('r','d'));
+}
+
+int
+rdtty(void)
+{
+ char onechar;
+
+ onechar = 0;
+ if (read(0, &onechar, 1) == 1) {
+ if (onechar == '\n')
+ tty++;
+ else
+ tty = 1;
+ if (tty != 3)
+ return(onechar);
+ }
+ tty = 0;
+ if (NROFF && quiet)
+ echo_on();
+ return(0);
+}
+
+
+void caseec(void)
+{
+ eschar = chget('\\');
+}
+
+
+void caseeo(void)
+{
+ eschar = 0;
+}
+
+
+void caseta(void)
+{
+ int i, j, k;
+
+ tabtab[0] = nonumb = 0;
+ for (i = 0; ((i < (NTAB - 1)) && !nonumb); i++) {
+ if (skip())
+ break;
+ k = tabtab[max(i-1, 0)] & TABMASK;
+ if ((j = max(hnumb(&k), 0)) > TABMASK) {
+ ERROR "Tab too far away" WARN;
+ j = TABMASK;
+ }
+ tabtab[i] = j & TABMASK;
+ if (!nonumb)
+ switch (cbits(ch)) {
+ case 'C':
+ tabtab[i] |= CTAB;
+ break;
+ case 'R':
+ tabtab[i] |= RTAB;
+ break;
+ default: /*includes L*/
+ break;
+ }
+ nonumb = ch = 0;
+ }
+ if (!skip())
+ ERROR "Too many tab stops" WARN;
+ tabtab[i] = 0;
+}
+
+
+void casene(void)
+{
+ int i, j;
+
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ i = lss;
+ if (dip == d && numtabp[NL].val == -1) {
+ newline(1);
+ return;
+ }
+ if (i > (j = findt1())) {
+ i = lss;
+ lss = j;
+ dip->nls = 0;
+ newline(0);
+ lss = i;
+ }
+}
+
+
+void casetr(void)
+{
+ int i, j;
+ Tchar k;
+
+ lgf++;
+ skip();
+ while ((i = cbits(k=getch())) != '\n') {
+ if (ismot(k))
+ return;
+ if (ismot(k = getch()))
+ return;
+ if ((j = cbits(k)) == '\n')
+ j = ' ';
+ trtab[i] = j;
+ }
+}
+
+
+void casecu(void)
+{
+ cu++;
+ caseul();
+}
+
+
+void caseul(void)
+{
+ int i;
+
+ noscale++;
+ skip();
+ i = max(atoi0(), 0);
+ if (nonumb)
+ i = 1;
+ if (ul && (i == 0)) {
+ font = sfont;
+ ul = cu = 0;
+ }
+ if (i) {
+ if (!ul) {
+ sfont = font;
+ font = ulfont;
+ }
+ ul = i;
+ }
+ noscale = 0;
+ mchbits();
+}
+
+
+void caseuf(void)
+{
+ int i, j;
+
+ if (skip() || !(i = getrq()) || i == 'S' || (j = findft(i)) == -1)
+ ulfont = ULFONT; /*default underline position*/
+ else
+ ulfont = j;
+ if (NROFF && ulfont == FT)
+ ulfont = ULFONT;
+}
+
+
+void caseit(void)
+{
+ int i;
+
+ lgf++;
+ it = itmac = 0;
+ noscale++;
+ skip();
+ i = atoi0();
+ skip();
+ if (!nonumb && (itmac = getrq()))
+ it = i;
+ noscale = 0;
+}
+
+
+void casemc(void)
+{
+ int i;
+
+ if (icf > 1)
+ ic = 0;
+ icf = 0;
+ if (skip())
+ return;
+ ic = getch();
+ icf = 1;
+ skip();
+ i = max(hnumb((int *)0), 0);
+ if (!nonumb)
+ ics = i;
+}
+
+
+void casemk(void)
+{
+ int i, j;
+
+ if (dip != d)
+ j = dip->dnl;
+ else
+ j = numtabp[NL].val;
+ if (skip()) {
+ dip->mkline = j;
+ return;
+ }
+ if ((i = getrq()) == 0)
+ return;
+ numtabp[findr(i)].val = j;
+}
+
+
+void casesv(void)
+{
+ int i;
+
+ skip();
+ if ((i = vnumb((int *)0)) < 0)
+ return;
+ if (nonumb)
+ i = 1;
+ sv += i;
+ caseos();
+}
+
+
+void caseos(void)
+{
+ int savlss;
+
+ if (sv <= findt1()) {
+ savlss = lss;
+ lss = sv;
+ newline(0);
+ lss = savlss;
+ sv = 0;
+ }
+}
+
+
+void casenm(void)
+{
+ int i;
+
+ lnmod = nn = 0;
+ if (skip())
+ return;
+ lnmod++;
+ noscale++;
+ i = inumb(&numtabp[LN].val);
+ if (!nonumb)
+ numtabp[LN].val = max(i, 0);
+ getnm(&ndf, 1);
+ getnm(&nms, 0);
+ getnm(&ni, 0);
+ getnm(&nmwid, 3); /* really kludgy! */
+ noscale = 0;
+ nmbits = chbits;
+}
+
+/*
+ * .nm relies on the fact that illegal args are skipped; don't warn
+ * for illegality of these
+ */
+void getnm(int *p, int min)
+{
+ int i;
+ int savtr = trace;
+
+ eat(' ');
+ if (skip())
+ return;
+ trace = 0;
+ i = atoi0();
+ if (nonumb)
+ return;
+ *p = max(i, min);
+ trace = savtr;
+}
+
+
+void casenn(void)
+{
+ noscale++;
+ skip();
+ nn = max(atoi0(), 1);
+ noscale = 0;
+}
+
+
+void caseab(void)
+{
+ casetm1(1, stderr);
+ done3(0);
+}
+
+
+/* nroff terminal handling has been pretty well excised */
+/* as part of the merge with troff. these are ghostly remnants, */
+/* called, but doing nothing. restore them at your peril. */
+
+
+void save_tty(void) /*save any tty settings that may be changed*/
+{
+}
+
+
+void restore_tty(void) /*restore tty settings from beginning*/
+{
+}
+
+
+void set_tty(void)
+{
+}
+
+
+void echo_off(void) /*turn off ECHO for .rd in "-q" mode*/
+{
+}
+
+
+void echo_on(void) /*restore ECHO after .rd in "-q" mode*/
+{
+}
diff --git a/troff/n6.c b/troff/n6.c
@@ -0,0 +1,363 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+#include <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);
+}
diff --git a/troff/n7.c b/troff/n7.c
@@ -0,0 +1,837 @@
+#define _BSD_SOURCE 1 /* isascii */
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#ifdef STRICT
+ /* not in ANSI or POSIX */
+#define isascii(a) ((a) >= 0 && (a) <= 127)
+#endif
+
+#define GETCH gettch
+Tchar gettch(void);
+
+
+/*
+ * troff7.c
+ *
+ * text
+ */
+
+int brflg;
+
+void tbreak(void)
+{
+ int pad, k;
+ Tchar *i, j;
+ int resol;
+ int un0 = un;
+
+ trap = 0;
+ if (nb)
+ return;
+ if (dip == d && numtabp[NL].val == -1) {
+ newline(1);
+ return;
+ }
+ if (!nc) {
+ setnel();
+ if (!wch)
+ return;
+ if (pendw)
+ getword(1);
+ movword();
+ } else if (pendw && !brflg) {
+ getword(1);
+ movword();
+ }
+ *linep = dip->nls = 0;
+ if (NROFF && dip == d)
+ horiz(po);
+ if (lnmod)
+ donum();
+ lastl = ne;
+ if (brflg != 1) {
+ totout = 0;
+ } else if (ad) {
+ if ((lastl = ll - un) < ne)
+ lastl = ne;
+ }
+ if (admod && ad && (brflg != 2)) {
+ lastl = ne;
+ adsp = adrem = 0;
+ if (admod == 1)
+ un += quant(nel / 2, HOR);
+ else if (admod == 2)
+ un += nel;
+ }
+ totout++;
+ brflg = 0;
+ if (lastl + un > dip->maxl)
+ dip->maxl = lastl + un;
+ horiz(un);
+ if (NROFF) {
+ if (adrem % t.Adj)
+ resol = t.Hor;
+ else
+ resol = t.Adj;
+ } else
+ resol = HOR;
+
+ lastl = ne + (nwd-1) * adsp + adrem;
+ for (i = line; nc > 0; ) {
+ if ((cbits(j = *i++)) == ' ') {
+ pad = 0;
+ do {
+ pad += width(j);
+ nc--;
+ } while ((cbits(j = *i++)) == ' ');
+ i--;
+ pad += adsp;
+ --nwd;
+ if (adrem) {
+ if (adrem < 0) {
+ pad -= resol;
+ adrem += resol;
+ } else if ((totout & 01) || adrem / resol >= nwd) {
+ pad += resol;
+ adrem -= resol;
+ }
+ }
+ pchar((Tchar) WORDSP);
+ horiz(pad);
+ } else {
+ pchar(j);
+ nc--;
+ }
+ }
+ if (ic) {
+ if ((k = ll - un0 - lastl + ics) > 0)
+ horiz(k);
+ pchar(ic);
+ }
+ if (icf)
+ icf++;
+ else
+ ic = 0;
+ ne = nwd = 0;
+ un = in;
+ setnel();
+ newline(0);
+ if (dip != d) {
+ if (dip->dnl > dip->hnl)
+ dip->hnl = dip->dnl;
+ } else {
+ if (numtabp[NL].val > dip->hnl)
+ dip->hnl = numtabp[NL].val;
+ }
+ for (k = ls - 1; k > 0 && !trap; k--)
+ newline(0);
+ spread = 0;
+}
+
+void donum(void)
+{
+ int i, nw;
+ int lnv = numtabp[LN].val;
+
+ nrbits = nmbits;
+ nw = width('1' | nrbits);
+ if (nn) {
+ nn--;
+ goto d1;
+ }
+ if (lnv % ndf) {
+ numtabp[LN].val++;
+d1:
+ un += nw * (nmwid + nms + ni);
+ return;
+ }
+ i = 0;
+ do { /* count digits in numtabp[LN].val */
+ i++;
+ } while ((lnv /= 10) > 0);
+ horiz(nw * (ni + max(nmwid-i, 0)));
+ nform = 0;
+ fnumb(numtabp[LN].val, pchar);
+ un += nw * nms;
+ numtabp[LN].val++;
+}
+
+
+void text(void)
+{
+ Tchar i;
+ static int spcnt;
+
+ nflush++;
+ numtabp[HP].val = 0;
+ if ((dip == d) && (numtabp[NL].val == -1)) {
+ newline(1);
+ return;
+ }
+ setnel();
+ if (ce || !fi) {
+ nofill();
+ return;
+ }
+ if (pendw)
+ goto t4;
+ if (pendt)
+ if (spcnt)
+ goto t2;
+ else
+ goto t3;
+ pendt++;
+ if (spcnt)
+ goto t2;
+ while ((cbits(i = GETCH())) == ' ') {
+ spcnt++;
+ numtabp[HP].val += sps;
+ widthp = sps;
+ }
+ if (nlflg) {
+t1:
+ nflush = pendt = ch = spcnt = 0;
+ callsp();
+ return;
+ }
+ ch = i;
+ if (spcnt) {
+t2:
+ tbreak();
+ if (nc || wch)
+ goto rtn;
+ un += spcnt * sps;
+ spcnt = 0;
+ setnel();
+ if (trap)
+ goto rtn;
+ if (nlflg)
+ goto t1;
+ }
+t3:
+ if (spread)
+ goto t5;
+ if (pendw || !wch)
+t4:
+ if (getword(0))
+ goto t6;
+ if (!movword())
+ goto t3;
+t5:
+ if (nlflg)
+ pendt = 0;
+ adsp = adrem = 0;
+ if (ad) {
+ if (nwd == 1)
+ adsp = nel;
+ else
+ adsp = nel / (nwd - 1);
+ adsp = (adsp / HOR) * HOR;
+ adrem = nel - adsp*(nwd-1);
+ }
+ brflg = 1;
+ tbreak();
+ spread = 0;
+ if (!trap)
+ goto t3;
+ if (!nlflg)
+ goto rtn;
+t6:
+ pendt = 0;
+ ckul();
+rtn:
+ nflush = 0;
+}
+
+
+void nofill(void)
+{
+ int j;
+ Tchar i;
+
+ if (!pendnf) {
+ over = 0;
+ tbreak();
+ if (trap)
+ goto rtn;
+ if (nlflg) {
+ ch = nflush = 0;
+ callsp();
+ return;
+ }
+ adsp = adrem = 0;
+ nwd = 10000;
+ }
+ while ((j = (cbits(i = GETCH()))) != '\n') {
+ if (j == ohc)
+ continue;
+ if (j == CONT) {
+ pendnf++;
+ nflush = 0;
+ flushi();
+ ckul();
+ return;
+ }
+ j = width(i);
+ widthp = j;
+ numtabp[HP].val += j;
+ storeline(i, j);
+ }
+ if (ce) {
+ ce--;
+ if ((i = quant(nel / 2, HOR)) > 0)
+ un += i;
+ }
+ if (!nc)
+ storeline((Tchar)FILLER, 0);
+ brflg = 2;
+ tbreak();
+ ckul();
+rtn:
+ pendnf = nflush = 0;
+}
+
+
+void callsp(void)
+{
+ int i;
+
+ if (flss)
+ i = flss;
+ else
+ i = lss;
+ flss = 0;
+ casesp1(i);
+}
+
+
+void ckul(void)
+{
+ if (ul && (--ul == 0)) {
+ cu = 0;
+ font = sfont;
+ mchbits();
+ }
+ if (it && --it == 0 && itmac)
+ control(itmac, 0);
+}
+
+
+void storeline(Tchar c, int w)
+{
+ int diff;
+
+ if (linep >= line + lnsize - 2) {
+ lnsize += LNSIZE;
+ diff = linep - line;
+ if (( line = (Tchar *)realloc((char *)line, lnsize * sizeof(Tchar))) != NULL) {
+ if (linep && diff)
+ linep = line + diff;
+ } else {
+ if (over) {
+ return;
+ } else {
+ flusho();
+ ERROR "Line overflow." WARN;
+ over++;
+ *linep++ = LEFTHAND;
+ w = width(LEFTHAND);
+ nc++;
+ c = '\n';
+ }
+ }
+ }
+ *linep++ = c;
+ ne += w;
+ nel -= w;
+ nc++;
+}
+
+
+void newline(int a)
+{
+ int i, j, nlss;
+ int opn;
+
+ nlss = 0;
+ if (a)
+ goto nl1;
+ if (dip != d) {
+ j = lss;
+ pchar1((Tchar)FLSS);
+ if (flss)
+ lss = flss;
+ i = lss + dip->blss;
+ dip->dnl += i;
+ pchar1((Tchar)i);
+ pchar1((Tchar)'\n');
+ lss = j;
+ dip->blss = flss = 0;
+ if (dip->alss) {
+ pchar1((Tchar)FLSS);
+ pchar1((Tchar)dip->alss);
+ pchar1((Tchar)'\n');
+ dip->dnl += dip->alss;
+ dip->alss = 0;
+ }
+ if (dip->ditrap && !dip->ditf && dip->dnl >= dip->ditrap && dip->dimac)
+ if (control(dip->dimac, 0)) {
+ trap++;
+ dip->ditf++;
+ }
+ return;
+ }
+ j = lss;
+ if (flss)
+ lss = flss;
+ nlss = dip->alss + dip->blss + lss;
+ numtabp[NL].val += nlss;
+ if (TROFF && ascii) {
+ dip->alss = dip->blss = 0;
+ }
+ pchar1((Tchar)'\n');
+ flss = 0;
+ lss = j;
+ if (numtabp[NL].val < pl)
+ goto nl2;
+nl1:
+ ejf = dip->hnl = numtabp[NL].val = 0;
+ ejl = frame;
+ if (donef) {
+ if ((!nc && !wch) || ndone)
+ done1(0);
+ ndone++;
+ donef = 0;
+ if (frame == stk)
+ nflush++;
+ }
+ opn = numtabp[PN].val;
+ numtabp[PN].val++;
+ if (npnflg) {
+ numtabp[PN].val = npn;
+ npn = npnflg = 0;
+ }
+nlpn:
+ if (numtabp[PN].val == pfrom) {
+ print++;
+ pfrom = -1;
+ } else if (opn == pto) {
+ print = 0;
+ opn = -1;
+ chkpn();
+ goto nlpn;
+ }
+ if (print)
+ ptpage(numtabp[PN].val); /* supposedly in a clean state so can pause */
+ if (stop && print) {
+ dpn++;
+ if (dpn >= stop) {
+ dpn = 0;
+ ptpause();
+ }
+ }
+nl2:
+ trap = 0;
+ if (numtabp[NL].val == 0) {
+ if ((j = findn(0)) != NTRAP)
+ trap = control(mlist[j], 0);
+ } else if ((i = findt(numtabp[NL].val - nlss)) <= nlss) {
+ if ((j = findn1(numtabp[NL].val - nlss + i)) == NTRAP) {
+ flusho();
+ ERROR "Trap botch." WARN;
+ done2(-5);
+ }
+ trap = control(mlist[j], 0);
+ }
+}
+
+int
+findn1(int a)
+{
+ int i, j;
+
+ for (i = 0; i < NTRAP; i++) {
+ if (mlist[i]) {
+ if ((j = nlist[i]) < 0)
+ j += pl;
+ if (j == a)
+ break;
+ }
+ }
+ return(i);
+}
+
+
+void chkpn(void)
+{
+ pto = *(pnp++);
+ pfrom = pto>=0 ? pto : -pto;
+ if (pto == -INT_MAX) {
+ flusho();
+ done1(0);
+ }
+ if (pto < 0) {
+ pto = -pto;
+ print++;
+ pfrom = 0;
+ }
+}
+
+int
+findt(int a)
+{
+ int i, j, k;
+
+ k = INT_MAX;
+ if (dip != d) {
+ if (dip->dimac && (i = dip->ditrap - a) > 0)
+ k = i;
+ return(k);
+ }
+ for (i = 0; i < NTRAP; i++) {
+ if (mlist[i]) {
+ if ((j = nlist[i]) < 0)
+ j += pl;
+ if ((j -= a) <= 0)
+ continue;
+ if (j < k)
+ k = j;
+ }
+ }
+ i = pl - a;
+ if (k > i)
+ k = i;
+ return(k);
+}
+
+int
+findt1(void)
+{
+ int i;
+
+ if (dip != d)
+ i = dip->dnl;
+ else
+ i = numtabp[NL].val;
+ return(findt(i));
+}
+
+
+void eject(Stack *a)
+{
+ int savlss;
+
+ if (dip != d)
+ return;
+ ejf++;
+ if (a)
+ ejl = a;
+ else
+ ejl = frame;
+ if (trap)
+ return;
+e1:
+ savlss = lss;
+ lss = findt(numtabp[NL].val);
+ newline(0);
+ lss = savlss;
+ if (numtabp[NL].val && !trap)
+ goto e1;
+}
+
+int
+movword(void)
+{
+ int w;
+ Tchar i, *wp;
+ int savwch, hys;
+
+ over = 0;
+ wp = wordp;
+ if (!nwd) {
+ while (cbits(*wp++) == ' ') {
+ wch--;
+ wne -= sps;
+ }
+ wp--;
+ }
+ if (wne > nel && !hyoff && hyf && (!nwd || nel > 3 * sps) &&
+ (!(hyf & 02) || (findt1() > lss)))
+ hyphen(wp);
+ savwch = wch;
+ hyp = hyptr;
+ nhyp = 0;
+ while (*hyp && *hyp <= wp)
+ hyp++;
+ while (wch) {
+ if (hyoff != 1 && *hyp == wp) {
+ hyp++;
+ if (!wdstart || (wp > wdstart + 1 && wp < wdend &&
+ (!(hyf & 04) || wp < wdend - 1) && /* 04 => last 2 */
+ (!(hyf & 010) || wp > wdstart + 2))) { /* 010 => 1st 2 */
+ nhyp++;
+ storeline((Tchar)IMP, 0);
+ }
+ }
+ i = *wp++;
+ w = width(i);
+ wne -= w;
+ wch--;
+ storeline(i, w);
+ }
+ if (nel >= 0) {
+ nwd++;
+ return(0); /* line didn't fill up */
+ }
+ if (TROFF)
+ xbits((Tchar)HYPHEN, 1);
+ hys = width((Tchar)HYPHEN);
+m1:
+ if (!nhyp) {
+ if (!nwd)
+ goto m3;
+ if (wch == savwch)
+ goto m4;
+ }
+ if (*--linep != IMP)
+ goto m5;
+ if (!(--nhyp))
+ if (!nwd)
+ goto m2;
+ if (nel < hys) {
+ nc--;
+ goto m1;
+ }
+m2:
+ if ((i = cbits(*(linep - 1))) != '-' && i != EMDASH) {
+ *linep = (*(linep - 1) & SFMASK) | HYPHEN;
+ w = width(*linep);
+ nel -= w;
+ ne += w;
+ linep++;
+ }
+m3:
+ nwd++;
+m4:
+ wordp = wp;
+ return(1); /* line filled up */
+m5:
+ nc--;
+ w = width(*linep);
+ ne -= w;
+ nel += w;
+ wne += w;
+ wch++;
+ wp--;
+ goto m1;
+}
+
+
+void horiz(int i)
+{
+ vflag = 0;
+ if (i)
+ pchar(makem(i));
+}
+
+
+void setnel(void)
+{
+ if (!nc) {
+ linep = line;
+ if (un1 >= 0) {
+ un = un1;
+ un1 = -1;
+ }
+ nel = ll - un;
+ ne = adsp = adrem = 0;
+ }
+}
+
+int
+getword(int x)
+{
+ int j, k;
+ Tchar i, *wp;
+ int noword;
+ int obits;
+
+ j = 0;
+ noword = 0;
+ if (x)
+ if (pendw) {
+ *pendw = 0;
+ goto rtn;
+ }
+ if (wordp = pendw)
+ goto g1;
+ hyp = hyptr;
+ wordp = word;
+ over = wne = wch = 0;
+ hyoff = 0;
+ obits = chbits;
+ while (1) { /* picks up 1st char of word */
+ j = cbits(i = GETCH());
+ if (j == '\n') {
+ wne = wch = 0;
+ noword = 1;
+ goto rtn;
+ }
+ if (j == ohc) {
+ hyoff = 1; /* 1 => don't hyphenate */
+ continue;
+ }
+ if (j == ' ') {
+ numtabp[HP].val += sps;
+ widthp = sps;
+ storeword(i, sps);
+ continue;
+ }
+ break;
+ }
+ storeword(' ' | obits, sps);
+ if (spflg) {
+ storeword(' ' | obits, sps);
+ spflg = 0;
+ }
+g0:
+ if (j == CONT) {
+ pendw = wordp;
+ nflush = 0;
+ flushi();
+ return(1);
+ }
+ if (hyoff != 1) {
+ if (j == ohc) {
+ hyoff = 2;
+ *hyp++ = wordp;
+ if (hyp > hyptr + NHYP - 1)
+ hyp = hyptr + NHYP - 1;
+ goto g1;
+ }
+ if (((j == '-' || j == EMDASH)) && !(i & ZBIT)) /* zbit avoids \X */
+ if (wordp > word + 1) {
+ hyoff = 2;
+ *hyp++ = wordp + 1;
+ if (hyp > hyptr + NHYP - 1)
+ hyp = hyptr + NHYP - 1;
+ }
+ }
+ j = width(i);
+ numtabp[HP].val += j;
+ storeword(i, j);
+g1:
+ j = cbits(i = GETCH());
+ if (j != ' ') {
+ static char *sentchar = ".?!"; /* sentence terminators */
+ if (j != '\n')
+ goto g0;
+ wp = wordp-1; /* handle extra space at end of sentence */
+ while (wp >= word) {
+ j = cbits(*wp--);
+ if (j=='"' || j=='\'' || j==')' || j==']' || j=='*' || j==DAGGER)
+ continue;
+ for (k = 0; sentchar[k]; k++)
+ if (j == sentchar[k]) {
+ spflg++;
+ break;
+ }
+ break;
+ }
+ }
+ *wordp = 0;
+ numtabp[HP].val += sps;
+rtn:
+ for (wp = word; *wp; wp++) {
+ if (ismot(j))
+ break; /* drechsler */
+ j = cbits(*wp);
+ if (j == ' ')
+ continue;
+ if (!(isascii(j) && isdigit(j)) && j != '-')
+ break;
+ }
+ if (*wp == 0) /* all numbers, so don't hyphenate */
+ hyoff = 1;
+ wdstart = 0;
+ wordp = word;
+ pendw = 0;
+ *hyp++ = 0;
+ setnel();
+ return(noword);
+}
+
+
+void storeword(Tchar c, int w)
+{
+ Tchar *savp;
+ int i;
+
+ if (wordp >= word + wdsize - 2) {
+ wdsize += WDSIZE;
+ savp = word;
+ if (( word = (Tchar *)realloc((char *)word, wdsize * sizeof(Tchar))) != NULL) {
+ if (wordp)
+ wordp = word + (wordp - savp);
+ if (pendw)
+ pendw = word + (pendw - savp);
+ if (wdstart)
+ wdstart = word + (wdstart - savp);
+ if (wdend)
+ wdend = word + (wdend - savp);
+ for (i = 0; i < NHYP; i++)
+ if (hyptr[i])
+ hyptr[i] = word + (hyptr[i] - savp);
+ } else {
+ if (over) {
+ return;
+ } else {
+ flusho();
+ ERROR "Word overflow." WARN;
+ over++;
+ c = LEFTHAND;
+ w = width(LEFTHAND);
+ }
+ }
+ }
+ widthp = w;
+ wne += w;
+ *wordp++ = c;
+ wch++;
+}
+
+
+Tchar gettch(void)
+{
+ extern int c_isalnum;
+ Tchar i;
+ int j;
+
+ if (TROFF)
+ return getch();
+
+ i = getch();
+ j = cbits(i);
+ if (ismot(i) || fbits(i) != ulfont)
+ return(i);
+ if (cu) {
+ if (trtab[j] == ' ') {
+ setcbits(i, '_');
+ setfbits(i, FT); /* default */
+ }
+ return(i);
+ }
+ /* should test here for characters that ought to be underlined */
+ /* in the old nroff, that was the 200 bit on the width! */
+ /* for now, just do letters, digits and certain special chars */
+ if (j <= 127) {
+ if (!isalnum(j))
+ setfbits(i, FT);
+ } else {
+ if (j < c_isalnum)
+ setfbits(i, FT);
+ }
+ return(i);
+}
diff --git a/troff/n8.c b/troff/n8.c
@@ -0,0 +1,545 @@
+#include <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;
+}
diff --git a/troff/n9.c b/troff/n9.c
@@ -0,0 +1,489 @@
+#include "tdef.h"
+#include "ext.h"
+#include "fns.h"
+
+/*
+ * troff9.c
+ *
+ * misc functions
+ */
+
+Tchar setz(void)
+{
+ Tchar i;
+
+ if (!ismot(i = getch()))
+ i |= ZBIT;
+ return(i);
+}
+
+void setline(void)
+{
+ Tchar *i;
+ Tchar c;
+ int length;
+ int j, w, cnt, delim, rem, temp;
+ Tchar linebuf[NC];
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ vflag = 0;
+ dfact = EM;
+ length = quant(atoi0(), HOR);
+ dfact = 1;
+ if (!length) {
+ eat(delim);
+ return;
+ }
+s0:
+ if ((j = cbits(c = getch())) == delim || j == '\n') {
+ ch = c;
+ c = RULE | chbits;
+ } else if (cbits(c) == FILLER)
+ goto s0;
+ w = width(c);
+ if (w <= 0) {
+ ERROR "zero-width underline character ignored" WARN;
+ c = RULE | chbits;
+ w = width(c);
+ }
+ i = linebuf;
+ if (length < 0) {
+ *i++ = makem(length);
+ length = -length;
+ }
+ if (!(cnt = length / w)) {
+ *i++ = makem(-(temp = ((w - length) / 2)));
+ *i++ = c;
+ *i++ = makem(-(w - length - temp));
+ goto s1;
+ }
+ if (rem = length % w) {
+ if (cbits(c) == RULE || cbits(c) == UNDERLINE || cbits(c) == ROOTEN)
+ *i++ = c | ZBIT;
+ *i++ = makem(rem);
+ }
+ if (cnt) {
+ *i++ = RPT;
+ *i++ = cnt;
+ *i++ = c;
+ }
+s1:
+ *i = 0;
+ eat(delim);
+ pushback(linebuf);
+}
+
+int
+eat(int c)
+{
+ int i;
+
+ while ((i = cbits(getch())) != c && i != '\n')
+ ;
+ return(i);
+}
+
+
+void setov(void)
+{
+ int j, k;
+ Tchar i, o[NOV+1];
+ int delim, w[NOV+1];
+
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ for (k = 0; k < NOV && (j = cbits(i = getch())) != delim && j != '\n'; k++) {
+ o[k] = i;
+ w[k] = width(i);
+ }
+ o[k] = w[k] = 0;
+ if (o[0])
+ for (j = 1; j; ) {
+ j = 0;
+ for (k = 1; o[k] ; k++) {
+ if (w[k-1] < w[k]) {
+ j++;
+ i = w[k];
+ w[k] = w[k-1];
+ w[k-1] = i;
+ i = o[k];
+ o[k] = o[k-1];
+ o[k-1] = i;
+ }
+ }
+ }
+ else
+ return;
+ *pbp++ = makem(w[0] / 2);
+ for (k = 0; o[k]; k++)
+ ;
+ while (k>0) {
+ k--;
+ *pbp++ = makem(-((w[k] + w[k+1]) / 2));
+ *pbp++ = o[k];
+ }
+}
+
+
+void setbra(void)
+{
+ int k;
+ Tchar i, *j, dwn;
+ int cnt, delim;
+ Tchar brabuf[NC];
+
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ j = brabuf + 1;
+ cnt = 0;
+ if (NROFF)
+ dwn = (2 * t.Halfline) | MOT | VMOT;
+ else
+ dwn = EM | MOT | VMOT;
+ while ((k = cbits(i = getch())) != delim && k != '\n' && j <= brabuf + NC - 4) {
+ *j++ = i | ZBIT;
+ *j++ = dwn;
+ cnt++;
+ }
+ if (--cnt < 0)
+ return;
+ else if (!cnt) {
+ ch = *(j - 2);
+ return;
+ }
+ *j = 0;
+ if (NROFF)
+ *--j = *brabuf = (cnt * t.Halfline) | MOT | NMOT | VMOT;
+ else
+ *--j = *brabuf = (cnt * EM) / 2 | MOT | NMOT | VMOT;
+ *--j &= ~ZBIT;
+ pushback(brabuf);
+}
+
+
+void setvline(void)
+{
+ int i;
+ Tchar c, rem, ver, neg;
+ int cnt, delim, v;
+ Tchar vlbuf[NC];
+ Tchar *vlp;
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ dfact = lss;
+ vflag++;
+ i = quant(atoi0(), VERT);
+ dfact = 1;
+ if (!i) {
+ eat(delim);
+ vflag = 0;
+ return;
+ }
+ if ((cbits(c = getch())) == delim) {
+ c = BOXRULE | chbits; /*default box rule*/
+ } else
+ getch();
+ c |= ZBIT;
+ neg = 0;
+ if (i < 0) {
+ i = -i;
+ neg = NMOT;
+ }
+ if (NROFF)
+ v = 2 * t.Halfline;
+ else {
+ v = EM;
+ if (v < VERT) /* ATT EVK hack: Erik van Konijnenburg, */
+ v = VERT; /* hvlpb!evkonij, ATT NSI Hilversum, Holland */
+ }
+
+ cnt = i / v;
+ rem = makem(i % v) | neg;
+ ver = makem(v) | neg;
+ vlp = vlbuf;
+ if (!neg)
+ *vlp++ = ver;
+ if (absmot(rem) != 0) {
+ *vlp++ = c;
+ *vlp++ = rem;
+ }
+ while (vlp < vlbuf + NC - 3 && cnt--) {
+ *vlp++ = c;
+ *vlp++ = ver;
+ }
+ *(vlp - 2) &= ~ZBIT;
+ if (!neg)
+ vlp--;
+ *vlp = 0;
+ pushback(vlbuf);
+ vflag = 0;
+}
+
+#define NPAIR (NC/2-6) /* max pairs in spline, etc. */
+
+void setdraw(void) /* generate internal cookies for a drawing function */
+{
+ int i, j, k, dx[NPAIR], dy[NPAIR], delim, type;
+ Tchar c, drawbuf[NC];
+ int drawch = '.'; /* character to draw with */
+
+ /* input is \D'f dx dy dx dy ... c' (or at least it had better be) */
+ /* this does drawing function f with character c and the */
+ /* specified dx,dy pairs interpreted as appropriate */
+ /* pairs are deltas from last point, except for radii */
+
+ /* l dx dy: line from here by dx,dy */
+ /* c x: circle of diameter x, left side here */
+ /* e x y: ellipse of diameters x,y, left side here */
+ /* a dx1 dy1 dx2 dy2:
+ ccw arc: ctr at dx1,dy1, then end at dx2,dy2 from there */
+ /* ~ dx1 dy1 dx2 dy2...:
+ spline to dx1,dy1 to dx2,dy2 ... */
+ /* b x c:
+ built-up character of type c, ht x */
+ /* f dx dy ...: f is any other char: like spline */
+
+ if (ismot(c = getch()))
+ return;
+ delim = cbits(c);
+ numerr.escarg = type = cbits(getch());
+ if (type == '~') /* head off the .tr ~ problem */
+ type = 's';
+ for (i = 0; i < NPAIR ; i++) {
+ skip();
+ vflag = 0;
+ dfact = EM;
+ dx[i] = quant(atoi0(), HOR);
+ if (dx[i] > MAXMOT)
+ dx[i] = MAXMOT;
+ else if (dx[i] < -MAXMOT)
+ dx[i] = -MAXMOT;
+ skip();
+ if (type == 'c') {
+ dy[i] = 0;
+ goto eat;
+ }
+ vflag = 1;
+ dfact = lss;
+ dy[i] = quant(atoi0(), VERT);
+ if (dy[i] > MAXMOT)
+ dy[i] = MAXMOT;
+ else if (dy[i] < -MAXMOT)
+ dy[i] = -MAXMOT;
+eat:
+ if (cbits(c = getch()) != ' ') { /* must be the end */
+ if (cbits(c) != delim) {
+ drawch = cbits(c);
+ getch();
+ }
+ i++;
+ break;
+ }
+ }
+ dfact = 1;
+ vflag = 0;
+ if (TROFF) {
+ drawbuf[0] = DRAWFCN | chbits | ZBIT;
+ drawbuf[1] = type | chbits | ZBIT;
+ drawbuf[2] = drawch | chbits | ZBIT;
+ for (k = 0, j = 3; k < i; k++) {
+ drawbuf[j++] = MOT | ((dx[k] >= 0) ? dx[k] : (NMOT | -dx[k]));
+ drawbuf[j++] = MOT | VMOT | ((dy[k] >= 0) ? dy[k] : (NMOT | -dy[k]));
+ }
+ if (type == DRAWELLIPSE) {
+ drawbuf[5] = drawbuf[4] | NMOT; /* so the net vertical is zero */
+ j = 6;
+ } else if (type == DRAWBUILD) {
+ drawbuf[4] = drawbuf[3] | NMOT; /* net horizontal motion is zero */
+ drawbuf[2] &= ~ZBIT; /* width taken from drawing char */
+ j = 5;
+ }
+ drawbuf[j++] = DRAWFCN | chbits | ZBIT; /* marks end for ptout */
+ drawbuf[j] = 0;
+ pushback(drawbuf);
+ }
+}
+
+
+void casefc(void)
+{
+ int i;
+ Tchar j;
+
+ gchtab[fc] &= ~FCBIT;
+ fc = IMP;
+ padc = ' ';
+ if (skip() || ismot(j = getch()) || (i = cbits(j)) == '\n')
+ return;
+ fc = i;
+ gchtab[fc] |= FCBIT;
+ if (skip() || ismot(ch) || (ch = cbits(ch)) == fc)
+ return;
+ padc = ch;
+}
+
+
+Tchar setfield(int x)
+{
+ Tchar ii, jj, *fp;
+ int i, j;
+ int length, ws, npad, temp, type;
+ Tchar **pp, *padptr[NPP];
+ Tchar fbuf[FBUFSZ];
+ int savfc, savtc, savlc;
+ Tchar rchar;
+ int savepos;
+ static Tchar wbuf[] = { WORDSP, 0};
+
+ rchar = 0;
+ if (x == tabch)
+ rchar = tabc | chbits;
+ else if (x == ldrch)
+ rchar = dotc | chbits;
+ temp = npad = ws = 0;
+ savfc = fc;
+ savtc = tabch;
+ savlc = ldrch;
+ tabch = ldrch = fc = IMP;
+ savepos = numtabp[HP].val;
+ gchtab[tabch] &= ~TABBIT;
+ gchtab[ldrch] &= ~LDRBIT;
+ gchtab[fc] &= ~FCBIT;
+ gchtab[IMP] |= TABBIT|LDRBIT|FCBIT;
+ for (j = 0; ; j++) {
+ if ((tabtab[j] & TABMASK) == 0) {
+ if (x == savfc)
+ ERROR "zero field width." WARN;
+ jj = 0;
+ goto rtn;
+ }
+ if ((length = ((tabtab[j] & TABMASK) - numtabp[HP].val)) > 0 )
+ break;
+ }
+ type = tabtab[j] & ~TABMASK;
+ fp = fbuf;
+ pp = padptr;
+ if (x == savfc) {
+ while (1) {
+ j = cbits(ii = getch());
+ jj = width(ii);
+ widthp = jj;
+ numtabp[HP].val += jj;
+ if (j == padc) {
+ npad++;
+ *pp++ = fp;
+ if (pp > padptr + NPP - 1)
+ break;
+ goto s1;
+ } else if (j == savfc)
+ break;
+ else if (j == '\n') {
+ temp = j;
+ if (nlflg && ip == 0) {
+ numtabp[CD].val--;
+ nlflg = 0;
+ }
+ break;
+ }
+ ws += jj;
+s1:
+ *fp++ = ii;
+ if (fp > fbuf + FBUFSZ - 3)
+ break;
+ }
+ if (ws)
+ *fp++ = WORDSP;
+ if (!npad) {
+ npad++;
+ *pp++ = fp;
+ *fp++ = 0;
+ }
+ *fp++ = temp;
+ *fp = 0;
+ temp = i = (j = length - ws) / npad;
+ i = (i / HOR) * HOR;
+ if ((j -= i * npad) < 0)
+ j = -j;
+ ii = makem(i);
+ if (temp < 0)
+ ii |= NMOT;
+ for (; npad > 0; npad--) {
+ *(*--pp) = ii;
+ if (j) {
+ j -= HOR;
+ (*(*pp)) += HOR;
+ }
+ }
+ pushback(fbuf);
+ jj = 0;
+ } else if (type == 0) {
+ /*plain tab or leader*/
+ if ((j = width(rchar)) > 0) {
+ int nchar = length / j;
+ while (nchar-->0 && pbp < &pbbuf[NC-3]) {
+ numtabp[HP].val += j;
+ widthp = j;
+ *pbp++ = rchar;
+ }
+ length %= j;
+ }
+ if (length)
+ jj = length | MOT;
+ else
+ jj = getch0();
+ if (savepos > 0)
+ pushback(wbuf);
+ } else {
+ /*center tab*/
+ /*right tab*/
+ while ((j = cbits(ii = getch())) != savtc && j != '\n' && j != savlc) {
+ jj = width(ii);
+ ws += jj;
+ numtabp[HP].val += jj;
+ widthp = jj;
+ *fp++ = ii;
+ if (fp > fbuf + FBUFSZ - 3)
+ break;
+ }
+ *fp++ = ii;
+ *fp = 0;
+ if (type == RTAB)
+ length -= ws;
+ else
+ length -= ws / 2; /*CTAB*/
+ pushback(fbuf);
+ if ((j = width(rchar)) != 0 && length > 0) {
+ int nchar = length / j;
+ while (nchar-- > 0 && pbp < &pbbuf[NC-3])
+ *pbp++ = rchar;
+ length %= j;
+ }
+ if (savepos > 0)
+ pushback(wbuf);
+ length = (length / HOR) * HOR;
+ jj = makem(length);
+ if (nlflg) {
+ if (ip == 0)
+ numtabp[CD].val--;
+ nlflg = 0;
+ }
+ }
+rtn:
+ gchtab[fc] &= ~FCBIT;
+ gchtab[tabch] &= ~TABBIT;
+ gchtab[ldrch] &= ~LDRBIT;
+ fc = savfc;
+ tabch = savtc;
+ ldrch = savlc;
+ gchtab[fc] |= FCBIT;
+ gchtab[tabch] = TABBIT;
+ gchtab[ldrch] |= LDRBIT;
+ numtabp[HP].val = savepos;
+ return(jj);
+}
diff --git a/troff/ni.c b/troff/ni.c
@@ -0,0 +1,390 @@
+#include <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 n