Introduction
Introduction Statistics Contact Development Disclaimer Help
added mk and troff to 9base (unfinished yet, DO NOT USE) - 9base - revived mini…
git clone git://git.suckless.org/9base
Log
Files
Refs
README
LICENSE
---
commit a08a2a9000cf4cb01ac165f410dbe99a64191edd
parent c0a69251c8988bdbabf4f3d3e20f40e363990c6c
Author: Anselm R Garbe <[email protected]>
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…
+ 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 ([email protected]). All …
+Portions Copyright © 1997-1999 Vita Nuova Limited. All rights reserved.
+Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com…
+
+Under a licence agreement with Lucent Technologies Inc. effective 1st March 20…
+Vita Nuova Holdings Limited has the right to determine (within a specified sco…
+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…
+of any warranty: for this software, note that includes this NOTICE file in par…
+
+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 <[email protected]>
+
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 archiv…
+ 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…
+ Bprint(&bout, "%s doesn't exist: assuming it w…
+ 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&REGEXP)
+ envupd("stem",newword(""));
+ else
+ envupd("stem", newword(j->stem));
+ envupd("prereq", wdup(j->p));
+ sprint(buf, "%d", getpid());
+ envupd("pid", newword(buf));
+ sprint(buf, "%d", slot);
+ envupd("nproc", newword(buf));
+ envupd("newprereq", wdup(j->np));
+ envupd("alltarget", wdup(j->at));
+ l = &v;
+ v = w = wdup(j->np);
+ while(w){
+ cp = strchr(w->s, '(');
+ if(cp){
+ qp = strchr(cp+1, ')');
+ if(qp){
+ *qp = 0;
+ strcpy(w->s, cp+1);
+ l = &w->next;
+ w = w->next;
+ continue;
+ }
+ }
+ *l = w->next;
+ free(w->s);
+ free(w);
+ w = *l;
+ }
+ envupd("newmember", v);
+ /* update stem0 -> stem9 */
+ for(p = myenv; *p; p++)
+ if(strcmp(*p, "stem0") == 0)
+ break;
+ for(i = 0; *p; i++, p++){
+ if((j->r->attr&REGEXP) && j->match[i])
+ envupd(*p, newword(j->match[i]));
+ else
+ envupd(*p, newword(""));
+ }
+ return envy;
+}
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 cac…
+ 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 || …
+ 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, "",…
+ 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 || …
+ if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR))
+ continue;
+ if(r->attr&REGEXP){
+ stem[0] = 0;
+ patrule = r;
+ memset((char*)rmatch, 0, sizeof(rmatch));
+ if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
+ continue;
+ } else {
+ if(!match(node->name, r->target, stem, r->shellt)) con…
+ }
+ if(cnt[r->rule] >= nreps) continue;
+ cnt[r->rule]++;
+
+/* if(r->attr&VIR)
+ * node->flags |= VIRTUAL;
+ * if(r->attr&NOREC)
+ * node->flags |= NORECIPE;
+ * if(r->attr&DEL)
+ * node->flags |= DELETE;
+ */
+
+ if(!r->tail || !r->tail->s || !*r->tail->s) {
+ a->next = newarc((Node *)0, r, stem, rmatch);
+ a = a->next;
+ } else
+ for(w = r->tail; w; w = w->next){
+ if(r->attr&REGEXP)
+ regsub(w->s, buf, sizeof buf, rmatch, …
+ else
+ subst(stem, w->s, buf);
+ a->next = newarc(applyrules(buf, cnt), r, stem…
+ 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->nam…
+ 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 %…
+ 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, …
+{
+ 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' nprere…
+ wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wt…
+ 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 new…
+ 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…
+ 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]]…
+ 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, nod…
+ if(node->flags&BEINGMADE)
+ return(did);
+ if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p,…
+ if(explain)
+ fprint(1, "unpretending %s(%ld) because %s is out of d…
+ 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", n…
+ 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|CANPR…
+ && 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->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 b…
+ a->n->name, node->name, ra->n? ra->n->name : "…
+
+ unpretend(a->n);
+ did = work(a->n, node, a) || did;
+ ready = 0;
+ }
+ if(ready == 0) /* try later unless nothing has happened for -k'…
+ 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, n…
+}
+
+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->n…
+ 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…
+ 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 …
+ 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 assignmen…
+ int iws; /* inter-word separator in envi…
+ char *(*charin)(char*, char*); /* search for unescaped c…
+ char *(*expandquote)(char*, Rune, Bufblock*); /* extract…
+ int (*escapetoken)(Biobuf*, Bufblock*, int, int); /* inp…
+ char *(*copyq)(char*, Rune, Bufblock*); /* check for quo…
+ 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|BEIN…
+#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…
+#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r…
+#define NAMEBLOCK 1000
+#define BIGBLOCK 20000
+
+#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n'))
+#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^…
+
+#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 f…
+ } 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 f…
+ } 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 assig…
+ Exit();
+ }
+ if(symlook(head->s, S_OVERRIDE, 0)){
+ set = varoverride;
+ } else {
+ set = 1;
+ if(varoverride)
+ symlook(head->s, S_OVERRIDE, (void *)"…
+ }
+ if(set){
+/*
+char *cp;
+dumpw("tail", tail);
+cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
+*/
+ setvar(head->s, (void *) tail);
+ symlook(head->s, S_WESET, (void *)"");
+ if(strcmp(head->s, "MKSHELL") == 0){
+ if((err = setshell(tail)) != nil){
+ SYNERR(hline);
+ fprint(2, "%s\n", err);
+ Exit();
+ break;
+ }
+ }
+ }
+ if(attr)
+ symlook(head->s, S_NOEXPORT, (void *)"");
+ break;
+ default:
+ SYNERR(hline);
+ fprint(2, "expected one of :<=\n");
+ Exit();
+ break;
+ }
+ }
+ close(fd);
+ freebuf(buf);
+ ipop();
+ popshell();
+}
+
+void
+addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog)
+{
+ Word *w;
+
+ assert("addrules args", head && body);
+ /* tuck away first non-meta rule as default target*/
+ if(target1 == 0 && !(attr&REGEXP)){
+ for(w = head; w; w = w->next)
+ if(shellt->charin(w->s, "%&"))
+ break;
+ if(w == 0)
+ target1 = wdup(head);
+ }
+ for(w = head; w; w = w->next)
+ addrule(w->s, tail, body, head, attr, hline, prog);
+}
+
+static int
+rhead(char *line, Word **h, Word **t, int *attr, char **prog)
+{
+ char *p;
+ char *pp;
+ int sep;
+ Rune r;
+ int n;
+ Word *w;
+
+ p = shellt->charin(line,":=<");
+ if(p == 0)
+ return('?');
+ sep = *p;
+ *p++ = 0;
+ if(sep == '<' && *p == '|'){
+ sep = '|';
+ p++;
+ }
+ *attr = 0;
+ *prog = 0;
+ if(sep == '='){
+ pp = shellt->charin(p, shellt->termchars); /* termchars…
+ if (pp && *pp == '=') {
+ while (p != pp) {
+ n = chartorune(&r, p);
+ switch(r)
+ {
+ default:
+ SYNERR(-1);
+ fprint(2, "unknown attribute '%c'\n",*…
+ 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 quote…
+ * 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", no…
+ }
+ return(did);
+ }
+ /*
+ build the node list
+ */
+ node->next = 0;
+ head.next = 0;
+ ww = &head;
+ ahead.next = 0;
+ aw = &ahead;
+ if(r->attr&REGEXP){
+ ww->next = newword(node->name);
+ aw->next = newword(node->name);
+ } else {
+ for(w = r->alltargets; w; w = w->next){
+ if(r->attr&META)
+ subst(aa->stem, w->s, buf);
+ else
+ strcpy(buf, w->s);
+ aw->next = newword(buf);
+ aw = aw->next;
+ if((s = symlook(buf, S_NODE, 0)) == 0)
+ continue; /* not a node we are interest…
+ 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…
+ }
+ } 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, ' '),…
+ run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.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, …
+{
+ Rule *r;
+ Rule *rr;
+ Symtab *sym;
+ int reuse;
+
+ r = 0;
+ reuse = 0;
+ if(sym = symlook(head, S_TARGET, 0)){
+ for(r = sym->u.ptr; r; r = r->chain)
+ if(rcmp(r, head, tail) == 0){
+ reuse = 1;
+ break;
+ }
+ }
+ if(r == 0)
+ r = (Rule *)Malloc(sizeof(Rule));
+ r->shellt = shellt;
+ r->shellcmd = shellcmd;
+ r->target = head;
+ r->tail = tail;
+ r->recipe = body;
+ r->line = hline;
+ r->file = infile;
+ r->attr = attr;
+ r->alltargets = ahead;
+ r->prog = prog;
+ r->rule = nrules++;
+ if(!reuse){
+ rr = symlook(head, S_TARGET, (void *)r)->u.ptr;
+ if(rr != r){
+ r->chain = rr->chain;
+ rr->chain = r;
+ } else
+ r->chain = 0;
+ }
+ if(!reuse)
+ r->next = 0;
+ if((attr&REGEXP) || shellt->charin(head, "%&")){
+ r->attr |= META;
+ if(reuse)
+ return;
+ if(attr&REGEXP){
+ patrule = r;
+ r->pat = regcomp(head);
+ }
+ if(metarules == 0)
+ metarules = lmr = r;
+ else {
+ lmr->next = r;
+ lmr = r;
+ }
+ } else {
+ if(reuse)
+ return;
+ r->pat = 0;
+ if(rules == 0)
+ rules = lr = r;
+ else {
+ lr->next = r;
+ lr = r;
+ }
+ }
+}
+
+void
+dumpr(char *s, Rule *r)
+{
+ if(r == nil)
+ return;
+ Bprint(&bout, "%s: start=%ld shelltype=%s shellcmd=%s\n",
+ s, r, r->shellt->name, wtos(r->shellcmd, ' '));
+ for(; r; r = r->next){
+ Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld a…
+ r, r->file, r->line, r->attr, r->next, r->chain, wtos(…
+ 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->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->she…
+ usage();
+ nrunning++;
+ if(DEBUG(D_EXEC))
+ fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '),…
+ }
+}
+
+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",…
+ 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*s…
+ 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 convent…
+ */
+
+
+/*
+ * 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 quote…
+ * 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 strin…
+ }
+ 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 strin…
+ }
+ }
+ 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) == …
+ 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->va…
+ 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",…
+ 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 *shellcm…
+{
+ 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 o…
+ */
+ 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 curren…
+ thanks to david brailsford and friends for spotting the problem.
+
+ fixed up tests in alpha() in n8 to defend isalpha() against too-big in…
+ punct() argument had wrong type too. thanks to rich drexler and peter…
+
+Mar 19, 1991:
+ fixed bug that prevented .rd from working with new corebuf organizatio…
+
+ 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 situatio…
+
+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=\"…
+
+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 f…
+ gsub(/\[[0-9]+\]/, "", arglist) # for n8.c
+ gsub(/[*()\[\]]/, "", arglist) # discard noise …
+ gsub(/,/, ", ", arglist) # space nicely
+ sub(/\(.*\)/, "(" arglist ")", prev) # reconstruct
+ print prev comment
+
+ # argument declarations
+ gsub(/,/, ";", x)
+ gsub(/\(\*f\)\(Tchar\)/, "(*f)()", x) # special case fo…
+ 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 s…
+ 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 …
+ } 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…
+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=$TEXH…
+
+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] W…
+ 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…
+ 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…
+ 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 dur…
+ unpair(a) WARN;
+ edone(0100);
+ }
+ nxf->nargs = 0;
+ if (b)
+ collect();
+ flushi();
+ return pushi(contabp[j].mx, a); /* BUG??? all that matt…
+ }
+ 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) /*…
+ 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 …
+ 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 wh…
+{
+ 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…
+}
+#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 …
+ 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" WAR…
+ 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 dri…
+{ /* 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 == …
+ break;
+ if (*s != '\\')
+ *t++ = *s++;
+ else {
+ s++; /* skip \\ */
+ if (isdigit((uchar)s[0]) && isdigit((uchar)s[1]) && is…
+ *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…
+ 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 W…
+ 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); br…
+ }
+ 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 == BDFON…
+ 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 …
+/* fprintf(stderr, "big char %d, name %s\n", k, chname(k)); /* */
+ oputs(chname(k)+1); /* BUG: should separate Tro…
+ } 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 Tro…
+ } else if (t.tfont.wp[k].str[0] == MBchar) { /* parse()…
+/* 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 de…
+ 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…
+ ++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 p…
+ /* 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 earl…
+ 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")) == N…
+ 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 * si…
+ 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 string…
+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…
+ 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 somethi…
+ 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 duri…
+ 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…
+ 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…
+ 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" WAR…
+ 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 pointe…
+ 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 no…
+ 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 WA…
+ 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(Divs…
+ 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 …
+ 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 *…
+ 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, un…
+ 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 condi…
+
+void casead(void)
+{
+ int i;
+
+ ad = 1;
+ /* leave admod alone */
+ if (skip())
+ return;
+ switch (i = cbits(getch())) {
+ case 'r': /* right adj, left ragged */
+ admod = 2;
+ break;
+ case 'l': /* left adj, right ragged */
+ admod = ad = 0; /* same as casena */
+ break;
+ case 'c': /*centered adj*/
+ admod = 1;
+ break;
+ case 'b':
+ case 'n':
+ admod = 0;
+ break;
+ case '0':
+ case '2':
+ case '4':
+ ad = 0;
+ case '1':
+ case '3':
+ case '5':
+ admod = (i - '0') / 2;
+ }
+}
+
+
+void casena(void)
+{
+ ad = 0;
+}
+
+
+void casefi(void)
+{
+ tbreak();
+ fi = 1;
+ pendnf = 0;
+}
+
+
+void casenf(void)
+{
+ tbreak();
+ fi = 0;
+}
+
+
+void casers(void)
+{
+ dip->nls = 0;
+}
+
+
+void casens(void)
+{
+ dip->nls++;
+}
+
+int
+chget(int c)
+{
+ Tchar i;
+
+ i = 0;
+ if (skip() || ismot(i = getch()) || cbits(i) == ' ' || cbits(i) == '\n…
+ ch = i;
+ return(c);
+ } else
+ return cbits(i); /* was (i & BYTEMASK) */
+}
+
+
+void casecc(void)
+{
+ cc = chget('.');
+}
+
+
+void casec2(void)
+{
+ c2 = chget('\'');
+}
+
+
+void casehc(void)
+{
+ ohc = chget(OHC);
+}
+
+
+void casetc(void)
+{
+ tabc = chget(0);
+}
+
+
+void caselc(void)
+{
+ dotc = chget(0);
+}
+
+
+void casehy(void)
+{
+ int i;
+
+ hyf = 1;
+ if (skip())
+ return;
+ noscale++;
+ i = atoi0();
+ noscale = 0;
+ if (nonumb)
+ return;
+ hyf = max(i, 0);
+}
+
+
+void casenh(void)
+{
+ hyf = 0;
+}
+
+int
+max(int aa, int bb)
+{
+ if (aa > bb)
+ return(aa);
+ else
+ return(bb);
+}
+
+
+void casece(void)
+{
+ int i;
+
+ noscale++;
+ skip();
+ i = max(atoi0(), 0);
+ if (nonumb)
+ i = 1;
+ tbreak();
+ ce = i;
+ noscale = 0;
+}
+
+
+void casein(void)
+{
+ int i;
+
+ if (skip())
+ i = in1;
+ else {
+ i = max(hnumb(&in), 0);
+ if (nonumb)
+ i = in1;
+ }
+ tbreak();
+ in1 = in;
+ in = i;
+ if (!nc) {
+ un = in;
+ setnel();
+ }
+}
+
+
+void casell(void)
+{
+ int i;
+
+ if (skip())
+ i = ll1;
+ else {
+ i = max(hnumb(&ll), INCH / 10);
+ if (nonumb)
+ i = ll1;
+ }
+ ll1 = ll;
+ ll = i;
+ setnel();
+}
+
+
+void caselt(void)
+{
+ int i;
+
+ if (skip())
+ i = lt1;
+ else {
+ i = max(hnumb(&lt), 0);
+ if (nonumb)
+ i = lt1;
+ }
+ lt1 = lt;
+ lt = i;
+}
+
+
+void caseti(void)
+{
+ int i;
+
+ if (skip())
+ return;
+ i = max(hnumb(&in), 0);
+ tbreak();
+ un1 = i;
+ setnel();
+}
+
+
+void casels(void)
+{
+ int i;
+
+ noscale++;
+ if (skip())
+ i = ls1;
+ else {
+ i = max(inumb(&ls), 1);
+ if (nonumb)
+ i = ls1;
+ }
+ ls1 = ls;
+ ls = i;
+ noscale = 0;
+}
+
+
+void casepo(void)
+{
+ int i;
+
+ if (skip())
+ i = po1;
+ else {
+ i = max(hnumb(&po), 0);
+ if (nonumb)
+ i = po1;
+ }
+ po1 = po;
+ po = i;
+ if (TROFF & !ascii)
+ esc += po - po1;
+}
+
+
+void casepl(void)
+{
+ int i;
+
+ skip();
+ if ((i = vnumb(&pl)) == 0)
+ pl = 11 * INCH; /*11in*/
+ else
+ pl = i;
+ if (numtabp[NL].val > pl)
+ numtabp[NL].val = pl;
+}
+
+
+void casewh(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ return;
+ skip();
+ j = getrq();
+ if ((k = findn(i)) != NTRAP) {
+ mlist[k] = j;
+ return;
+ }
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == 0)
+ break;
+ if (k == NTRAP) {
+ flusho();
+ ERROR "cannot plant trap." WARN;
+ return;
+ }
+ mlist[k] = j;
+ nlist[k] = i;
+}
+
+
+void casech(void)
+{
+ int i, j, k;
+
+ lgf++;
+ skip();
+ if (!(j = getrq()))
+ return;
+ else
+ for (k = 0; k < NTRAP; k++)
+ if (mlist[k] == j)
+ break;
+ if (k == NTRAP)
+ return;
+ skip();
+ i = vnumb((int *)0);
+ if (nonumb)
+ mlist[k] = 0;
+ nlist[k] = i;
+}
+
+int
+findn(int i)
+{
+ int k;
+
+ for (k = 0; k < NTRAP; k++)
+ if ((nlist[k] == i) && (mlist[k] != 0))
+ break;
+ return(k);
+}
+
+
+void casepn(void)
+{
+ int i;
+
+ skip();
+ noscale++;
+ i = max(inumb(&numtabp[PN].val), 0);
+ noscale = 0;
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ }
+}
+
+
+void casebp(void)
+{
+ int i;
+ Stack *savframe;
+
+ if (dip != d)
+ return;
+ savframe = frame;
+ skip();
+ if ((i = inumb(&numtabp[PN].val)) < 0)
+ i = 0;
+ tbreak();
+ if (!nonumb) {
+ npn = i;
+ npnflg++;
+ } else if (dip->nls)
+ return;
+ eject(savframe);
+}
+
+void casetm(void)
+{
+ casetm1(0, stderr);
+}
+
+
+void casefm(void)
+{
+ static struct fcache {
+ char *name;
+ FILE *fp;
+ } fcache[15];
+ int i;
+
+ if ( skip() || !getname()) {
+ ERROR "fm: missing filename" WARN;
+ return;
+ }
+
+ for (i = 0; i < 15 && fcache[i].fp != NULL; i++) {
+ if (strcmp(nextf, fcache[i].name) == 0)
+ break;
+ }
+ if (i >= 15) {
+ ERROR "fm: too many streams" WARN;
+ return;
+ }
+ if (fcache[i].fp == NULL) {
+ if( (fcache[i].fp = fopen(unsharp(nextf), "w")) == NULL) {
+ ERROR "fm: cannot open %s", nextf WARN;
+ return;
+ }
+ fcache[i].name = strdupl(nextf);
+ }
+ casetm1(0, fcache[i].fp);
+}
+
+void casetm1(int ab, FILE *out)
+{
+ int i, j, c;
+ char *p;
+ char tmbuf[NTM];
+
+ lgf++;
+ copyf++;
+ if (ab) {
+ if (skip())
+ ERROR "User Abort" WARN;
+ else {
+ extern int error;
+ int savtrac = trace;
+ i = trace = 0;
+ noscale++;
+ i = inumb(&trace);
+ noscale--;
+ if (i) {
+ error = i;
+ if (nlflg || skip())
+ ERROR "User Abort, exit code %d", i WA…
+ }
+ 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 …
+ 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…
+{
+}
+
+
+void restore_tty(void) /*restore tty settings from begi…
+{
+}
+
+
+void set_tty(void)
+{
+}
+
+
+void echo_off(void) /*turn off ECHO for .rd in "-q" mod…
+{
+}
+
+
+void echo_on(void) /*restore ECHO after .rd in "-q" mod…
+{
+}
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))) { …
+ 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, savpts…
+
+ 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 >= n…
+ 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(Tc…
+ 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 && di…
+ 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…
+ 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) && /…
+ (!(hyf & 010) || wp > wdstart + 2))) { /* 01…
+ 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…
+ 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=='*' ||…
+ 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(Tc…
+ 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…
+ /* 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 …
+ return 0; /* no good way to make sure i is in r…
+ 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)) =…
+ 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(*…
+ 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 encod…
+ 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[2…
+ 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 dynamicall…
+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, retu…
+{
+ 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 lette…
+ || *(pp+1) != *(wp+1)) /* no match on 2nd lett…
+ break; /* so move to next let…
+ 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…
+ }
+ }
+/*
+ * 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' + …
+ 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) == R…
+ *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';…
+ 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 +…
+ *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 Konijne…
+ v = VERT; /* hvlpb!evkonij, ATT NSI Hilversum, …
+ }
+
+ 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 ther…
+ /* ~ 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 | -…
+ drawbuf[j++] = MOT | VMOT | ((dy[k] >= 0) ? dy[k] : (N…
+ }
+ if (type == DRAWELLIPSE) {
+ drawbuf[5] = drawbuf[4] | NMOT; /* so the net v…
+ j = 6;
+ } else if (type == DRAWBUILD) {
+ drawbuf[4] = drawbuf[3] | NMOT; /* net horizont…
+ drawbuf[2] &= ~ZBIT; /* width taken fro…
+ j = 5;
+ }
+ drawbuf[j++] = DRAWFCN | chbits | ZBIT; /* marks end fo…
+ 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 !=…
+ 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 nr…
+int lg;
+int pnlist[NPN] = { -1 };
+
+
+int *pnp = pnlist;
+int npn = 1;
+int npnflg = 1;
+int dpn = -1;
+int totout = 1;
+int ulfont = ULFONT;
+int tabch = TAB;
+int ldrch = LEADER;
+
+
+Contab contab[NM] = {
+ C(PAIR('d', 's'), caseds),
+ C(PAIR('a', 's'), caseas),
+ C(PAIR('s', 'p'), casesp),
+ C(PAIR('f', 't'), caseft),
+ C(PAIR('p', 's'), caseps),
+ C(PAIR('v', 's'), casevs),
+ C(PAIR('n', 'r'), casenr),
+ C(PAIR('i', 'f'), caseif),
+ C(PAIR('i', 'e'), caseie),
+ C(PAIR('e', 'l'), caseel),
+ C(PAIR('p', 'o'), casepo),
+ C(PAIR('t', 'l'), casetl),
+ C(PAIR('t', 'm'), casetm),
+ C(PAIR('f', 'm'), casefm),
+ C(PAIR('b', 'p'), casebp),
+ C(PAIR('c', 'h'), casech),
+ C(PAIR('p', 'n'), casepn),
+ C(PAIR('b', 'r'), tbreak),
+ C(PAIR('t', 'i'), caseti),
+ C(PAIR('n', 'e'), casene),
+ C(PAIR('n', 'f'), casenf),
+ C(PAIR('c', 'e'), casece),
+ C(PAIR('f', 'i'), casefi),
+ C(PAIR('i', 'n'), casein),
+ C(PAIR('l', 'l'), casell),
+ C(PAIR('n', 's'), casens),
+ C(PAIR('m', 'k'), casemk),
+ C(PAIR('r', 't'), casert),
+ C(PAIR('a', 'm'), caseam),
+ C(PAIR('d', 'e'), casede),
+ C(PAIR('d', 'i'), casedi),
+ C(PAIR('d', 'a'), caseda),
+ C(PAIR('w', 'h'), casewh),
+ C(PAIR('d', 't'), casedt),
+ C(PAIR('i', 't'), caseit),
+ C(PAIR('r', 'm'), caserm),
+ C(PAIR('r', 'r'), caserr),
+ C(PAIR('r', 'n'), casern),
+ C(PAIR('a', 'd'), casead),
+ C(PAIR('r', 's'), casers),
+ C(PAIR('n', 'a'), casena),
+ C(PAIR('p', 'l'), casepl),
+ C(PAIR('t', 'a'), caseta),
+ C(PAIR('t', 'r'), casetr),
+ C(PAIR('u', 'l'), caseul),
+ C(PAIR('c', 'u'), casecu),
+ C(PAIR('l', 't'), caselt),
+ C(PAIR('n', 'x'), casenx),
+ C(PAIR('s', 'o'), caseso),
+ C(PAIR('i', 'g'), caseig),
+ C(PAIR('t', 'c'), casetc),
+ C(PAIR('f', 'c'), casefc),
+ C(PAIR('e', 'c'), caseec),
+ C(PAIR('e', 'o'), caseeo),
+ C(PAIR('l', 'c'), caselc),
+ C(PAIR('e', 'v'), caseev),
+ C(PAIR('r', 'd'), caserd),
+ C(PAIR('a', 'b'), caseab),
+ C(PAIR('f', 'l'), casefl),
+ C(PAIR('e', 'x'), caseex),
+ C(PAIR('s', 's'), casess),
+ C(PAIR('f', 'p'), casefp),
+ C(PAIR('c', 's'), casecs),
+ C(PAIR('b', 'd'), casebd),
+ C(PAIR('l', 'g'), caselg),
+ C(PAIR('h', 'c'), casehc),
+ C(PAIR('h', 'y'), casehy),
+ C(PAIR('n', 'h'), casenh),
+ C(PAIR('n', 'm'), casenm),
+ C(PAIR('n', 'n'), casenn),
+ C(PAIR('s', 'v'), casesv),
+ C(PAIR('o', 's'), caseos),
+ C(PAIR('l', 's'), casels),
+ C(PAIR('c', 'c'), casecc),
+ C(PAIR('c', '2'), casec2),
+ C(PAIR('e', 'm'), caseem),
+ C(PAIR('a', 'f'), caseaf),
+ C(PAIR('h', 'a'), caseha),
+ C(PAIR('h', 'w'), casehw),
+ C(PAIR('m', 'c'), casemc),
+ C(PAIR('p', 'm'), casepm),
+ C(PAIR('p', 'i'), casepi),
+ C(PAIR('u', 'f'), caseuf),
+ C(PAIR('p', 'c'), casepc),
+ C(PAIR('h', 't'), caseht),
+ C(PAIR('c', 'f'), casecf),
+ C(PAIR('s', 'y'), casesy),
+ C(PAIR('l', 'f'), caself),
+ C(PAIR('p', 't'), casept),
+ C(PAIR('g', 'd'), casegd)
+};
+
+
+Tbuf _oline;
+
+/*
+ * troff environment block
+ */
+
+Env env[NEV] = { { /* this sets up env[0] */
+/* int ics */ 0, /* insertion character space, se…
+/* int sps */ 0,
+/* int spacesz */ 0,
+/* int lss */ 0,
+/* int lss1 */ 0,
+/* int ll */ 0,
+/* int ll1 */ 0,
+/* int lt */ 0,
+/* int lt1 */ 0,
+/* Tchar ic */ 0, /* insertion character (= margin chara…
+/* int icf */ 0, /* insertion character flag */
+/* Tchar chbits */ 0, /* size+font bits for current char…
+/* Tchar spbits */ 0,
+/* Tchar nmbits */ 0, /* size+font bits for number from …
+/* int apts */ PS, /* actual point size -- as req…
+/* int apts1 */ PS, /* need not match an existent…
+/* int pts */ PS, /* hence, this is the size that…
+/* int pts1 */ PS,
+/* int font */ FT,
+/* int font1 */ FT,
+/* int ls */ 1,
+/* int ls1 */ 1,
+/* int ad */ 1,
+/* int nms */ 1, /* .nm multiplier */
+/* int ndf */ 1, /* .nm separator */
+/* int nmwid */ 3, /* max width of .nm numbers */
+/* int fi */ 1,
+/* int cc */ '.',
+/* int c2 */ '\'',
+/* int ohc */ OHC,
+/* int tdelim */ IMP,
+/* int hyf */ 1,
+/* int hyoff */ 0,
+/* int hyphalg */ HYPHALG,
+/* int un1 */ -1,
+/* int tabc */ 0,
+/* int dotc */ '.',
+/* int adsp */ 0, /* add this much space to each …
+/* int adrem */ 0, /* excess space to add until i…
+/* int lastl */ 0, /* last text on current output…
+/* int nel */ 0, /* how much space left on curren…
+/* int admod */ 0, /* adjust mode */
+/* Tchar *wordp */ 0,
+/* int spflg */ 0, /* probably to indicate space …
+/* Tchar *linep */ 0,
+/* Tchar *wdend */ 0,
+/* Tchar *wdstart */ 0,
+/* int wne */ 0,
+/* int ne */ 0, /* how much space taken on curren…
+/* int nc */ 0, /* #characters (incl blank) on ou…
+/* int nb */ 0,
+/* int lnmod */ 0, /* line number mode, set by .n…
+/* int nwd */ 0, /* number of words on current ou…
+/* int nn */ 0, /* from .nn command */
+/* int ni */ 0, /* indent of .nm numbers, probabl…
+/* int ul */ 0,
+/* int cu */ 0,
+/* int ce */ 0,
+/* int in */ 0, /* indent and previous value */
+/* int in1 */ 0,
+/* int un */ 0, /* unindent of left margin in som…
+/* int wch */ 0,
+/* int pendt */ 0,
+/* Tchar *pendw */ (Tchar *)0,
+/* int pendnf */ 0,
+/* int spread */ 0,
+/* int it */ 0, /* input trap count */
+/* int itmac */ 0
+} };
+
+Env *envp = env; /* start off in env 0 */
+
+Numerr numerr;
+
+Stack *frame, *stk, *ejl;
+Stack *nxf;
+
+int pipeflg;
+int hflg; /* used in nroff only */
+int eqflg; /* used in nroff only */
+
+int xpts;
+int ppts;
+int pfont;
+int mpts;
+int mfont;
+int cs;
+int ccs;
+int bd;
+
+int stdi;
+int quiet;
+int stop;
+char ibuf[IBUFSZ];
+char xbuf[IBUFSZ];
+char *ibufp;
+char *xbufp;
+char *eibuf;
+char *xeibuf;
+Tchar pbbuf[NC]; /* pushback buffer for arguments, \n, e…
+Tchar *pbp = pbbuf; /* next free slot in pbbuf */
+Tchar *lastpbp = pbbuf; /* pbp in previous stack frame */
+int nx;
+int mflg;
+Tchar ch = 0;
+int ibf;
+int ifi;
+int iflg;
+int rargc;
+char **argp;
+Ushort trtab[NTRTAB];
+int lgf;
+int copyf;
+Offset ip;
+int nlflg;
+int donef;
+int nflush;
+int nfo;
+int padc;
+int raw;
+int flss;
+int nonumb;
+int trap;
+int tflg;
+int ejf;
+int dilev;
+Offset offset;
+int em;
+int ds;
+Offset woff;
+int app;
+int ndone;
+int lead;
+int ralss;
+Offset nextb;
+Tchar nrbits;
+int nform;
+int oldmn;
+int newmn;
+int macerr;
+Offset apptr;
+int diflg;
+int evi;
+int vflag;
+int noscale;
+int po1;
+int nlist[NTRAP];
+int mlist[NTRAP];
+int evlist[EVLSZ];
+int ev;
+int tty;
+int sfont = FT; /* appears to be "standard" font; used by…
+int sv;
+int esc;
+int widthp;
+int xfont;
+int setwdf;
+int over;
+int nhyp;
+Tchar **hyp;
+Tchar *olinep;
+int dotT;
+char *unlkp;
+Wcache widcache[NWIDCACHE];
+Diver d[NDI];
+Diver *dip;
+
+int c_hyphen;
+int c_emdash;
+int c_rule;
+int c_minus;
+int c_fi;
+int c_fl;
+int c_ff;
+int c_ffi;
+int c_ffl;
+int c_acute;
+int c_grave;
+int c_under;
+int c_rooten;
+int c_boxrule;
+int c_lefthand;
+int c_dagger;
+int c_isalnum;
+
+Spnames spnames[] =
+{
+ &c_hyphen, "hy",
+ &c_emdash, "em",
+ &c_rule, "ru",
+ &c_minus, "\\-",
+ &c_fi, "fi",
+ &c_fl, "fl",
+ &c_ff, "ff",
+ &c_ffi, "Fi",
+ &c_ffl, "Fl",
+ &c_acute, "aa",
+ &c_grave, "ga",
+ &c_under, "ul",
+ &c_rooten, "rn",
+ &c_boxrule, "br",
+ &c_lefthand, "lh",
+ &c_dagger, "dg", /* not in nroff?? */
+ &c_isalnum, "__",
+ 0, 0
+};
+
+
+Tchar (*hmot)(void);
+Tchar (*makem)(int i);
+Tchar (*setabs)(void);
+Tchar (*setch)(int c);
+Tchar (*sethl)(int k);
+Tchar (*setht)(void);
+Tchar (*setslant)(void);
+Tchar (*vmot)(void);
+Tchar (*xlss)(void);
+int (*findft)(int i);
+int (*width)(Tchar j);
+void (*mchbits)(void);
+void (*ptlead)(void);
+void (*ptout)(Tchar i);
+void (*ptpause)(void);
+void (*setfont)(int a);
+void (*setps)(void);
+void (*setwd)(void);
+
diff --git a/troff/suftab.c b/troff/suftab.c
@@ -0,0 +1,612 @@
+/*
+ * Suffix table
+ */
+
+typedef unsigned char Uchar;
+
+static Uchar sufa[] = {
+ 02,0200+'t', /* -TA */
+ 02,0200+'s', /* -SA */
+ 03,0200+'t','r', /* -TRA */
+ 03,0200+'d','r', /* -DRA */
+ 03,0200+'b','r', /* -BRA */
+ 02,0200+'p', /* -PA */
+ 02,0200+'n', /* -NA */
+ 02,0200+'m', /* -MA */
+ 03,0200+'p','l', /* -PLA */
+ 02,0200+'l', /* -LA */
+ 02,0200+'k', /* -KA */
+ 03,0200+'t','h', /* -THA */
+ 03,0200+'s','h', /* -SHA */
+ 02,0200+'g', /* -GA */
+ 02,0200+'d', /* -DA */
+ 02,0200+'c', /* -CA */
+ 02,0200+'b', /* -BA */
+ 00
+};
+
+static Uchar sufc[] = {
+ 04,'e','t',0200+'i', /* ET-IC */
+ 07,'a','l',0200+'i','s',0200+'t','i', /* AL-IS-TIC */
+ 04,'s',0200+'t','i', /* S-TIC */
+ 04,'p',0200+'t','i', /* P-TIC */
+ 05,0200+'l','y','t',0200+'i', /* -LYT-IC */
+ 04,'o','t',0200+'i', /* OT-IC */
+ 05,'a','n',0200+'t','i', /* AN-TIC */
+ 04,'n',0200+'t','i', /* N-TIC */
+ 04,'c',0200+'t','i', /* C-TIC */
+ 04,'a','t',0200+'i', /* AT-IC */
+ 04,'h',0200+'n','i', /* H-NIC */
+ 03,'n',0200+'i', /* N-IC */
+ 03,'m',0200+'i', /* M-IC */
+ 04,'l',0200+'l','i', /* L-LIC */
+ 04,'b',0200+'l','i', /* B-LIC */
+ 04,0200+'c','l','i', /* -CLIC */
+ 03,'l',0200+'i', /* L-IC */
+ 03,'h',0200+'i', /* H-IC */
+ 03,'f',0200+'i', /* F-IC */
+ 03,'d',0200+'i', /* D-IC */
+ 03,0200+'b','i', /* -BIC */
+ 03,'a',0200+'i', /* A-IC */
+ 03,0200+'m','a', /* -MAC */
+ 03,'i',0200+'a', /* I-AC */
+ 00
+};
+
+static Uchar sufd[] = {
+ 04,0200+'w','o','r', /* -WORD */
+ 04,0200+'l','o','r', /* -LORD */
+ 04,0200+'f','o','r', /* -FORD */
+ 04,0200+'y','a','r', /* -YARD */
+ 04,0200+'w','a','r', /* -WARD */
+ 05,0200+'g','u','a','r', /* -GUARD */
+ 04,0200+'t','a','r', /* -TARD */
+ 05,0200+'b','o','a','r', /* -BOARD */
+ 04,0200+'n','a','r', /* -NARD */
+ 05,0200+'l','i','a','r', /* -LIARD */
+ 04,0200+'i','a','r', /* -IARD */
+ 04,0200+'g','a','r', /* -GARD */
+ 04,0200+'b','a','r', /* -BARD */
+ 03,0200+'r','o', /* -ROD */
+ 04,0200+'w','o','o', /* -WOOD */
+ 04,0200+'h','o','o', /* -HOOD */
+ 04,0200+'m','o','n', /* -MOND */
+ 04,0200+'t','e','n', /* -TEND */
+ 05,0200+'s','t','a','n', /* -STAND */
+ 04,0200+'l','a','n', /* -LAND */
+ 04,0200+'h','a','n', /* -HAND */
+ 04,0200+'h','o','l', /* -HOLD */
+ 04,0200+'f','o','l', /* -FOLD */
+ 05,0200+'f','i','e','l', /* -FIELD */
+ 03,0200+'v','i', /* -VID */
+ 03,0200+'c','i', /* -CID */
+ 04,0200+'s','a','i', /* -SAID */
+ 04,0200+'m','a','i', /* -MAID */
+ 04,'t',0200+'t','e', /* T-TED */
+ 03,'t',0200+'e', /* T-ED */
+ 04,0200+'d','r','e', /* -DRED */
+ 04,0200+'c','r','e', /* -CRED */
+ 04,0200+'b','r','e', /* -BRED */
+ 05,'v',0200+'e','l','e', /* V-ELED */
+ 0100+04,'a','l',0200+'e', /* AL/ED */
+ 0140+03,0200+'e','e', /* /EED */
+ 040+05,'e','d',0200+'d','e', /* ED-DED */
+ 04,'d',0200+'d','e', /* D-DED */
+ 040+04,'e','d',0200+'e', /* ED-ED */
+ 03,'d',0200+'e', /* D-ED */
+ 05,0200+'d','u','c','e', /* -DUCED */
+ 0300+02,'e', /* E/D */
+ 05,0200+'s','t','e','a', /* -STEAD */
+ 05,0200+'a','h','e','a', /* -AHEAD */
+ 04,0200+'h','e','a', /* -HEAD */
+ 00
+};
+
+static Uchar sufe[] = {
+ 05,'a','r',0200+'i','z', /* AR-IZE */
+ 05,'a','n',0200+'i','z', /* AN-IZE */
+ 05,'a','l',0200+'i','z', /* AL-IZE */
+ 06,0200+'a','r','d',0200+'i','z', /* -ARD-IZE */
+ 05,0200+'s','e','l','v', /* -SELVE */
+ 05,0200+'k','n','i','v', /* -KNIVE */
+ 05,0200+'l','i','e','v', /* -LIEVE */
+ 0100+03,0200+'q','u', /* /QUE */
+ 07,'o','n',0200+'t','i','n',0200+'u', /* ON-TIN-UE */
+ 03,0200+'n','u', /* -NUE */
+ 03,0200+'d','u', /* -DUE */
+ 0300+02,'u', /* U/E */
+ 0300+05,'q','u','a','t', /* QUAT/E */
+ 04,'u',0200+'a','t', /* U-ATE */
+ 05,0200+'s','t','a','t', /* -STATE */
+ 04,0200+'t','a','t', /* -TATE */
+ 06,0200+'t','o','r',0200+'a','t', /* -TOR-ATE */
+ 05,'e','n',0200+'a','t', /* EN-ATE */
+ 04,0200+'m','a','t', /* -MATE */
+ 05,0200+'h','o','u','s', /* -HOUSE */
+ 05,0200+'c','l','o','s', /* -CLOSE */
+ 04,'i',0200+'o','s', /* I-OSE */
+ 04,0200+'w','i','s', /* -WISE */
+ 05,'a','s',0200+'u','r', /* AS-URE */
+ 040+04,0200+'s','u','r', /* -SURE */
+ 06,0200+'f','i','g',0200+'u','r', /* -FIG-URE */
+ 040+03,0200+'t','r', /* -TRE */
+ 05,0200+'s','t','o','r', /* -STORE */
+ 04,0200+'f','o','r', /* -FORE */
+ 05,0200+'w','h','e','r', /* -WHERE */
+ 06,0200+'s','p','h','e','r', /* -SPHERE */
+ 03,0200+'d','r', /* -DRE */
+ 03,0200+'c','r', /* -CRE */
+ 03,0200+'b','r', /* -BRE */
+ 05,0200+'s','c','o','p', /* -SCOPE */
+ 04,'y',0200+'o','n', /* Y-ONE */
+ 05,0200+'s','t','o','n', /* -STONE */
+ 05,0200+'p','h','o','n', /* -PHONE */
+ 04,0200+'g','o','n', /* -GONE */
+ 04,'e',0200+'o','n', /* E-ONE */
+ 040+04,0200+'e','n','n', /* -ENNE */
+ 040+05,'a',0200+'r','i','n', /* A-RINE */
+ 05,0200+'c','l','i','n', /* -CLINE */
+ 04,0200+'l','i','n', /* -LINE */
+ 007,00200+'r','o','u',00200+'t','i','n', /*-ROU-TINE */
+ 04,0200+'s','o','m', /* -SOME */
+ 04,0200+'c','o','m', /* -COME */
+ 04,0200+'t','i','m', /* -TIME */
+ 03,0200+'z','l', /* -ZLE */
+ 03,0200+'t','l', /* -TLE */
+ 03,0200+'s','l', /* -SLE */
+ 03,0200+'p','l', /* -PLE */
+ 05,0200+'v','i','l','l', /* -VILLE */
+ 04,'c','k',0200+'l', /* CK-LE */
+ 03,0200+'k','l', /* -KLE */
+ 03,0200+'g','l', /* -GLE */
+ 03,0200+'f','l', /* -FLE */
+ 03,0200+'d','l', /* -DLE */
+ 03,0200+'c','l', /* -CLE */
+ 05,0200+'p','a',0200+'b','l', /* -PA-BLE */
+ 05,'f','a',0200+'b','l', /* FA-BLE */
+ 05,0200+'c','a',0200+'b','l', /* -CA-BLE */
+ 06,0200+'s','t','a','b','l', /* -STABLE */
+ 04,0200+'a','b','l', /* -ABLE */
+ 03,0200+'b','l', /* -BLE */
+ 04,0200+'d','a','l', /* -DALE */
+ 04,0200+'m','a','l', /* -MALE */
+ 04,0200+'s','a','l', /* -SALE */
+ 04,0200+'l','i','k', /* -LIKE */
+ 0340+05,'g',0200+'u','a','g', /* -G/UAGE */
+ 05,0200+'r','i','a','g', /* -RIAGE */
+ 05,'e','r',0200+'a','g', /* ER-AGE */
+ 04,'m',0200+'a','g', /* M-AGE */
+ 04,'k',0200+'a','g', /* K-AGE */
+ 04,'d',0200+'a','g', /* D-AGE */
+ 04,0200+'w','i','f', /* -WIFE */
+ 05,0200+'k','n','i','f', /* -KNIFE */
+ 03,0200+'s','e', /* -SEE */
+ 04,0200+'f','r','e', /* -FREE */
+ 0340+02,'e', /* EE */
+ 04,0200+'w','i','d', /* -WIDE */
+ 04,0200+'t','i','d', /* -TIDE */
+ 04,0200+'s','i','d', /* -SIDE */
+ 06,0200+'q','u','e','n','c', /* -QUENCE */
+ 07,0200+'f','l','u',0200+'e','n','c', /* -FLU-ENCE */
+ 040+06,'e','s',0200+'e','n','c', /* ES-ENCE */
+ 06,'e','r',0200+'e','n','c', /* ER-ENCE */
+ 05,'i',0200+'e','n','c', /* I-ENCE */
+ 040+05,0200+'s','a','n','c', /* -SANCE */
+ 06,'e','r',0200+'a','n','c', /* ER-ANCE */
+ 06,'a','r',0200+'a','n','c', /* AR-ANCE */
+ 05,0200+'n','a','n','c', /* -NANCE */
+ 07,0200+'b','a','l',0200+'a','n','c', /* -BAL-ANCE */
+ 05,'i',0200+'a','n','c', /* I-ANCE */
+ 07,0200+'j','u','s',0200+'t','i','c', /* -JUS-TICE */
+ 05,0200+'s','t','i','c', /* -STICE */
+ 06,0200+'n','o','v',0200+'i','c', /* NOV-ICE */
+ 04,0200+'v','i','c', /* -VICE */
+ 05,0200+'p','i','e','c', /* -PIECE */
+ 05,0200+'p','l','a','c', /* -PLACE */
+ 0340+01, /* /E */
+ 00
+};
+
+static Uchar suff[] = {
+ 03,0200+'o','f', /* -OFF */
+ 05,0200+'p','r','o','o', /* -PROOF */
+ 04,0200+'s','e','l', /* -SELF */
+ 03,0200+'r','i', /* -RIF */
+ 040+04,0200+'l','i','e', /* -LIEF */
+ 00
+};
+
+static Uchar sufg[] = {
+ 03,0200+'l','o', /* -LOG */
+ 04,0200+'l','o','n', /* -LONG */
+ 05,'t',0200+'t','i','n', /* T-TING */
+ 06,0200+'s','t','r','i','n', /* -STRING */
+ 05,'r',0200+'r','i','n', /* R-RING */
+ 05,'p',0200+'p','i','n', /* P-PING */
+ 05,'n',0200+'n','i','n', /* N-NING */
+ 05,'m',0200+'m','i','n', /* M-MING */
+ 05,'l',0200+'l','i','n', /* L-LING */
+ 05,0200+'z','l','i','n', /* -ZLING */
+ 05,0200+'t','l','i','n', /* -TLING */
+ 040+05,'s',0200+'l','i','n', /* S-LING */
+ 05,'r',0200+'l','i','n', /* R-LING */
+ 05,0200+'p','l','i','n', /* -PLING */
+ 06,'n',0200+'k','l','i','n', /* N-KLING */
+ 05,'k',0200+'l','i','n', /* K-LING */
+ 05,0200+'g','l','i','n', /* -GLING */
+ 05,0200+'f','l','i','n', /* -FLING */
+ 05,0200+'d','l','i','n', /* -DLING */
+ 05,0200+'c','l','i','n', /* -CLING */
+ 05,0200+'b','l','i','n', /* -BLING */
+ 06,'y',0200+'t','h','i','n', /* Y-THING */
+ 07,'e','e','t','h',0200+'i','n', /* EETH-ING */
+ 06,'e',0200+'t','h','i','n', /* E-THING */
+ 05,'g',0200+'g','i','n', /* G-GING */
+ 05,'d',0200+'d','i','n', /* D-DING */
+ 05,'b',0200+'b','i','n', /* B-BING */
+ 03,0200+'i','n', /* -ING */
+ 00
+};
+
+static Uchar sufh[] = {
+ 05,0200+'m','o','u','t', /* -MOUTH */
+ 05,0200+'w','o','r','t', /* -WORTH */
+ 04,0200+'w','i','t', /* -WITH */
+ 05,'t',0200+'t','i','s', /* T-TISH */
+ 05,'e',0200+'t','i','s', /* E-TISH */
+ 05,'p',0200+'p','i','s', /* P-PISH */
+ 05,'r',0200+'n','i','s', /* R-NISH */
+ 05,'n',0200+'n','i','s', /* N-NISH */
+ 05,0200+'p','l','i','s', /* -PLISH */
+ 05,0200+'g','u','i','s', /* -GUISH */
+ 05,0200+'g','l','i','s', /* -GLISH */
+ 05,'b',0200+'l','i','s', /* B-LISH */
+ 05,'g',0200+'g','i','s', /* G-GISH */
+ 05,'d',0200+'d','i','s', /* D-DISH */
+ 03,0200+'i','s', /* -ISH */
+ 05,0200+'g','r','a','p', /* -GRAPH */
+ 07,0200+'b','o','r',0200+'o','u','g', /* -BOR-OUGH */
+ 05,0200+'b','u','r','g', /* -BURGH */
+ 04,0200+'v','i','c', /* -VICH */
+ 03,0200+'n','a', /* -NAH */
+ 03,0200+'l','a', /* -LAH */
+ 04,0200+'m','i',0200+'a', /* -MI-AH */
+ 00
+};
+
+static Uchar sufi[] = {
+ 03,0200+'t','r', /* -TRI */
+ 03,0200+'c','h', /* -CHI */
+ 0200+03,'i','f', /* IF-I */
+ 0200+03,'e','d', /* ED-I */
+ 05,0200+'a','s','c','i', /* -ASCII */
+ 04,0200+'s','e','m', /* -SEMI */
+ 00
+};
+
+static Uchar sufk[] = {
+ 04,0200+'w','o','r', /* -WORK */
+ 04,0200+'m','a','r', /* -MARK */
+ 04,0200+'b','o','o', /* -BOOK */
+ 04,0200+'w','a','l', /* -WALK */
+ 05,0200+'c','r','a','c', /* -CRACK */
+ 04,0200+'b','a','c', /* -BACK */
+ 00
+};
+
+static Uchar sufl[] = {
+ 03,0200+'f','u', /* -FUL */
+ 05,'s',0200+'w','e','l', /* S-WELL */
+ 04,0200+'t','e','l', /* -TELL */
+ 05,0200+'s','h','e','l', /* -SHELL */
+ 05,0200+'s','t','a','l', /* -STALL */
+ 04,'s',0200+'t','a', /* S-TAL */
+ 04,0200+'b','a','l', /* -BALL */
+ 04,0200+'c','a','l', /* -CALL */
+ 03,'v',0200+'e', /* V-EL */
+ 03,'u',0200+'e', /* U-EL */
+ 03,'k',0200+'e', /* K-EL */
+ 04,'t','h',0200+'e', /* TH-EL */
+ 05,'t','c','h',0200+'e', /* TCH-EL */
+ 03,'a',0200+'e', /* A-EL */
+ 0140+04,0200+'q','u','a', /* /QUAL */
+ 040+03,'u',0200+'a', /* U-AL */
+ 03,0200+'t','a', /* -TAL */
+ 04,'u','r',0200+'a', /* UR-AL */
+ 040+05,'g',0200+'o',0200+'n','a', /* G-O-NAL */
+ 04,'o','n',0200+'a', /* ON-AL */
+ 03,0200+'n','a', /* -NAL */
+ 04,0200+'t','i','a', /* -TIAL */
+ 04,0200+'s','i','a', /* -SIAL */
+ 040+05,0200+'t','r','i',0200+'a', /* -TRI-AL */
+ 04,'r','i',0200+'a', /* RI-AL */
+ 04,0200+'n','i',0200+'a', /* -NI-AL */
+ 04,0200+'d','i',0200+'a', /* -DI-AL */
+ 04,0200+'c','i','a', /* -CIAL */
+ 03,0200+'g','a', /* -GAL */
+ 04,0200+'m','e','a', /* -MEAL */
+/* 040+04,0200+'r','e',0200+'a', /* -RE-AL */
+ 040+04,0200+'r','e','a', /* -REAL */
+ 06,'c',0200+'t','i',0200+'c','a', /* C-TI-CAL */
+ 05,0200+'s','i',0200+'c','a', /* -SI-CAL */
+ 04,0200+'i',0200+'c','a', /* -I-CAL */
+ 03,0200+'c','a', /* -CAL */
+ 03,0200+'b','a', /* -BAL */
+ 06,0200+'n','o',0200+'m','i',0200+'a', /* -NO-MI-AL */
+ 00
+};
+
+static Uchar sufm[] = {
+ 03,0200+'n','u', /* -NUM */
+ 05,'o',0200+'r','i',0200+'u', /* O-RI-UM */
+ 040+03,'i',0200+'u', /* I-UM */
+ 040+03,'e',0200+'u', /* E-UM */
+ 05,'i','v',0200+'i','s', /* IV-ISM */
+ 04,0200+'t','i','s', /* -TISM */
+ 05,'i',0200+'m','i','s', /* I-MISM */
+ 05,'a','l',0200+'i','s', /* AL-ISM */
+ 040+04,'e',0200+'i','s', /* E-ISM */
+ 040+04,'a',0200+'i','s', /* A-ISM */
+ 04,0200+'r','o','o', /* -ROOM */
+ 03,0200+'d','o', /* -DOM */
+ 03,0200+'h','a', /* -HAM */
+ 06,0200+'a',0200+'r','i','t','h', /* -A-RITHM */
+ 05,0200+'r','i','t','h', /* -RITHM */
+ 00
+};
+
+static Uchar sufn[] = {
+ 05,0200+'k','n','o','w', /* -KNOWN */
+ 04,0200+'t','o','w', /* -TOWN */
+ 04,0200+'d','o','w', /* -DOWN */
+ 04,0200+'t','u','r', /* -TURN */
+ 05,0200+'s','p','o','o', /* -SPOON */
+ 04,0200+'n','o','o', /* -NOON */
+ 04,0200+'m','o','o', /* -MOON */
+ 011,'a','l',0200+'i',0200+'z','a',0200+'t','i','o', /* AL-I-ZA-…
+ 07,0200+'i',0200+'z','a',0200+'t','i','o', /* -I-ZA-TION */
+ 07,'l',0200+'i',0200+'a',0200+'t','i','o', /* L-I-A-TION */
+ 04,0200+'t','i','o', /* -TION */
+ 040+05,'s',0200+'s','i','o', /* S-SION */
+ 04,0200+'s','i','o', /* -SION */
+ 04,'n',0200+'i','o', /* N-ION */
+ 04,0200+'g','i','o', /* -GION */
+ 04,0200+'c','i','o', /* -CION */
+ 03,0200+'c','o', /* -CON */
+ 05,0200+'c','o','l','o', /* -COLON */
+ 03,0200+'t','o', /* -TON */
+ 04,'i','s',0200+'o', /* IS-ON */
+ 03,0200+'s','o', /* -SON */
+ 03,0200+'r','i', /* -RIN */
+ 03,0200+'p','i', /* -PIN */
+ 03,0200+'n','i', /* -NIN */
+ 03,0200+'m','i', /* -MIN */
+ 03,0200+'l','i', /* -LIN */
+ 03,0200+'k','i', /* -KIN */
+ 05,0200+'s','t','e','i', /* -STEIN */
+ 04,0200+'t','a','i', /* -TAIN */
+ 05,'g','h','t',0200+'e', /* GHT-EN */
+ 05,0200+'w','o','m',0200+'e', /* -WOM-EN */
+ 03,0200+'m','e', /* -MEN */
+ 04,'o',0200+'k','e', /* O-KEN */
+ 03,'k',0200+'e', /* K-EN */
+ 04,0200+'t','e','e', /* -TEEN */
+ 04,0200+'s','e','e', /* -SEEN */
+ 040+03,0200+'s','a', /* -SAN */
+ 05,0200+'w','o','m',0200+'a', /* -WOM-AN */
+ 03,0200+'m','a', /* -MAN */
+ 04,0200+'t','i','a', /* -TIAN */
+ 04,0200+'s','i','a', /* -SIAN */
+ 040+04,'e',0200+'i','a', /* E-IAN */
+ 04,0200+'c','i','a', /* -CIAN */
+ 0300+03,'i','a', /* IA/N */
+ 05,0200+'c','l','e','a', /* -CLEAN */
+ 04,0200+'m','e','a', /* -MEAN */
+ 040+03,'e',0200+'a', /* E-AN */
+ 00
+};
+
+static Uchar sufo[] = {
+ 05,0200+'m','a','c',0200+'r', /* -MAC-RO */
+ 00
+};
+
+static Uchar sufp[] = {
+ 05,0200+'g','r','o','u', /* -GROUP */
+ 02,0200+'u', /* -UP */
+ 04,0200+'s','h','i', /* -SHIP */
+ 04,0200+'k','e','e', /* -KEEP */
+ 00
+};
+
+static Uchar sufr[] = {
+ 04,0200+'z','a','r', /* -ZARR */
+ 0300+02,'r', /* R/R */
+ 03,0200+'t','o', /* -TOR */
+ 040+03,0200+'s','o', /* -SOR */
+ 040+04,0200+'r','i',0200+'o', /* -RI-OR */
+ 04,'i','z',0200+'e', /* IZ-ER */
+ 05,0200+'c','o','v',0200+'e', /* -COV-ER */
+ 04,0200+'o','v','e', /* -OVER */
+ 04,0200+'e','v',0200+'e', /* -EV-ER */
+ 8,0200+'c','o','m',0200+'p','u','t',0200+'e', /* -COM-PUT-ER */
+ 040+05,'u','s',0200+'t','e', /* US-TER */
+ 05,'o','s','t',0200+'e', /* OST-ER */
+ 040+05,0200+'a','c',0200+'t','e', /* -AC-TER */
+ 06,0200+'w','r','i','t',0200+'e', /* -WRIT-ER */
+ 040+05,'i','s',0200+'t','e', /* IS-TER */
+ 040+05,'e','s',0200+'t','e', /* ES-TER */
+ 040+05,'a','s',0200+'t','e', /* AS-TER */
+ 04,0200+'s','t','e', /* -STER */
+ 05,'a','r',0200+'t','e', /* AR-TER */
+ 04,'r','t',0200+'e', /* RT-ER */
+ 040+05,'m',0200+'e',0200+'t','e', /* M-E-TER */
+ 05,0200+'w','a',0200+'t','e', /* -WA-TER */
+ 03,'r',0200+'e', /* R-ER */
+ 04,'o','p',0200+'e', /* OP-ER */
+ 05,0200+'p','a',0200+'p','e', /* -PA-PER */
+ 04,'w','n',0200+'e', /* WN-ER */
+ 040+04,'s',0200+'n','e', /* S-NER */
+ 04,'o','n',0200+'e', /* ON-ER */
+ 04,'r','m',0200+'e', /* RM-ER */
+ 03,0200+'m','e', /* -MER */
+ 04,'l','l',0200+'e', /* LL-ER */
+ 05,'d',0200+'d','l','e', /* D-DLER */
+ 04,0200+'b','l','e', /* -BLER */
+ 03,'k',0200+'e', /* K-ER */
+ 05,'n',0200+'t','h','e', /* N-THER */
+ 06,0200+'f','a',0200+'t','h','e', /* -FA-THER */
+ 06,'e','i',0200+'t','h','e', /* EI-THER */
+ 04,'t','h',0200+'e', /* TH-ER */
+ 04,'s','h',0200+'e', /* SH-ER */
+ 04,0200+'p','h','e', /* -PHER */
+ 04,'c','h',0200+'e', /* CH-ER */
+ 04,'d','g',0200+'e', /* DG-ER */
+ 04,'r','d',0200+'e', /* RD-ER */
+ 06,'o','u','n','d',0200+'e', /* OUND-ER */
+ 04,'l','d',0200+'e', /* LD-ER */
+ 04,'i','d',0200+'e', /* ID-ER */
+ 05,0200+'d','u','c',0200+'e', /* -DUC-ER */
+ 04,'n','c',0200+'e', /* NC-ER */
+ 0100+02, 0200+'e', /* /ER */
+ 03,0200+'s','a', /* -SAR */
+ 040+06,'a','c',0200+'u',0200+'l','a', /* AC-U-LAR */
+ 040+06,'e','c',0200+'u',0200+'l','a', /* EC-U-LAR */
+ 040+06,'i','c',0200+'u',0200+'l','a', /* IC-U-LAR */
+ 040+06,'e','g',0200+'u',0200+'l','a', /* EG-U-LAR */
+ 00
+};
+
+static Uchar sufs[] = {
+ 040+04,'u',0200+'o','u', /* U-OUS */
+ 05,0200+'t','i','o','u', /* -TIOUS */
+ 05,0200+'g','i','o','u', /* -GIOUS */
+ 05,0200+'c','i','o','u', /* -CIOUS */
+ 040+04,'i',0200+'o','u', /* I-OUS */
+ 05,0200+'g','e','o','u', /* -GEOUS */
+ 05,0200+'c','e','o','u', /* -CEOUS */
+ 04,'e',0200+'o','u', /* E-OUS */
+ 0140+02,0200+'u', /* /US */
+ 04,0200+'n','e','s', /* -NESS */
+ 04,0200+'l','e','s', /* -LESS */
+ 0140+02,0200+'s', /* /SS */
+ 040+05,'p',0200+'o',0200+'l','i', /* P-O-LIS */
+ 0140+02,0200+'i', /* /IS */
+ 0100+03,0200+'x','e', /* X/ES */
+ 0100+03,0200+'s','e', /* S/ES */
+ 0100+04,'s','h',0200+'e', /* SH/ES */
+ 0100+04,'c','h',0200+'e', /* CH/ES */
+ 0300+01, /* /S */
+ 00
+};
+
+static Uchar suft[] = {
+ 05,0200+'l','i','m',0200+'i', /* -LIM-IT */
+ 06,'i','o','n',0200+'i','s', /* ION-IST */
+ 05,'i','n',0200+'i','s', /* IN-IST */
+ 05,'a','l',0200+'i','s', /* AL-IST */
+ 06,'l',0200+'o',0200+'g','i','s', /* L-O-GIST */
+ 05,'h','t',0200+'e','s', /* HT-EST */
+ 04,'i',0200+'e','s', /* I-EST */
+ 05,'g',0200+'g','e','s', /* G-GEST */
+ 04,'g',0200+'e','s', /* G-EST */
+ 05,'d',0200+'d','e','s', /* D-DEST */
+ 04,'d',0200+'e','s', /* D-EST */
+ 04,0200+'c','a','s', /* -CAST */
+ 05,0200+'h','e','a','r', /* -HEART */
+ 04,0200+'f','o','o', /* -FOOT */
+ 03,'i',0200+'o', /* I-OT */
+ 05,0200+'f','r','o','n', /* -FRONT */
+ 05,0200+'p','r','i','n', /* -PRINT */
+ 04,0200+'m','e','n', /* -MENT */
+ 05,0200+'c','i','e','n', /* -CIENT */
+ 04,'i',0200+'a','n', /* I-ANT */
+ 06,0200+'w','r','i','g','h', /* -WRIGHT */
+ 06,0200+'b','r','i','g','h', /* -BRIGHT */
+ 06,0200+'f','l','i','g','h', /* -FLIGHT */
+ 06,0200+'w','e','i','g','h', /* -WEIGHT */
+ 05,0200+'s','h','i','f', /* -SHIFT */
+ 05,0200+'c','r','a','f', /* -CRAFT */
+ 040+04,'d','g',0200+'e', /* DG-ET */
+ 04,0200+'g','o','a', /* -GOAT */
+ 04,0200+'c','o','a', /* -COAT */
+ 04,0200+'b','o','a', /* -BOAT */
+ 04,0200+'w','h','a', /* -WHAT */
+ 04,0200+'c','u','i', /* -CUIT */
+ 00
+};
+
+static Uchar sufy[] = {
+ 040+04,'e','s',0200+'t', /* ES-TY */
+ 040+05,'q','u','i',0200+'t', /* QUI-TY */
+ 04,0200+'t','i',0200+'t', /* -TI-TY */
+ 040+05,'o','s',0200+'i',0200+'t', /* OS-I-TY */
+ 04,0200+'s','i',0200+'t', /* -SI-TY */
+ 05,'i','n',0200+'i',0200+'t', /* IN-I-TY */
+ 04,'n','i',0200+'t', /* NI-TY */
+ 040+010,'f','a',0200+'b','i','l',0200+'i',0200+'t', /* FA-BIL-I…
+ 010,0200+'c','a',0200+'b','i','l',0200+'i',0200+'t', /* -CA-BIL…
+ 010,0200+'p','a',0200+'b','i','l',0200+'i',0200+'t', /* -PA-BIL…
+ 06,0200+'b','i','l',0200+'i',0200+'t', /* -BIL-I-TY */
+ 03,'i',0200+'t', /* I-TY */
+ 04,0200+'b','u','r', /* -BUR-Y */
+ 04,0200+'t','o',0200+'r', /* -TO-RY */
+ 05,0200+'q','u','a','r', /* -QUAR-Y */
+ 040+04,'u',0200+'a','r', /* U-ARY */
+ 07,0200+'m','e','n',0200+'t','a',0200+'r', /* -MEN-TA-RY */
+ 06,'i','o','n',0200+'a','r', /* ION-ARY */
+ 04,'i',0200+'a','r', /* I-ARY */
+ 04,'n',0200+'o',0200+'m', /* N-O-MY */
+ 03,0200+'p','l', /* -PLY */
+ 04,'g',0200+'g','l', /* G-GLY */
+ 05,0200+'p','a',0200+'b','l', /* -PA-BLY */
+ 05,'f','a',0200+'b','l', /* FA-BLY */
+ 05,0200+'c','a',0200+'b','l', /* -CA-BLY */
+ 04,0200+'a','b','l', /* -ABLY */
+ 03,0200+'b','l', /* -BLY */
+ 02,0200+'l', /* -LY */
+ 03,0200+'s','k', /* -SKY */
+ 040+06,'g',0200+'r','a',0200+'p','h', /* G-RA-PHY */
+ 04,'l',0200+'o',0200+'g', /* L-O-GY */
+ 02,0200+'f', /* -FY */
+ 03,0200+'n','e', /* -NEY */
+ 03,0200+'l','e', /* -LEY */
+ 04,'c','k',0200+'e', /* CK-EY */
+ 03,0200+'k','e', /* -KEY */
+ 04,0200+'b','o','d', /* -BODY */
+ 05,0200+'s','t','u','d', /* -STUDY */
+ 0340+04,'e','e','d', /* EEDY */
+ 02,0200+'b', /* -BY */
+ 03,0200+'w','a', /* -WAY */
+ 03,0200+'d','a', /* -DAY */
+ 00
+};
+
+Uchar *suftab[] = {
+ sufa,
+ 0,
+ sufc,
+ sufd,
+ sufe,
+ suff,
+ sufg,
+ sufh,
+ sufi,
+ 0,
+ sufk,
+ sufl,
+ sufm,
+ sufn,
+ sufo,
+ sufp,
+ 0,
+ sufr,
+ sufs,
+ suft,
+ 0,
+ 0,
+ 0,
+ 0,
+ sufy,
+ 0
+};
diff --git a/troff/t10.c b/troff/t10.c
@@ -0,0 +1,513 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+/*
+ * troff10.c
+ *
+ * typesetter interface
+ */
+
+int vpos = 0; /* absolute vertical position on page */
+int hpos = 0; /* ditto horizontal */
+
+extern Font fonts[MAXFONTS+1];
+
+int Inch;
+int Hor;
+int Vert;
+int Unitwidth;
+int nfonts;
+
+
+
+void t_ptinit(void)
+{
+ int i;
+ char buf[100], *p;
+
+ hmot = t_hmot;
+ makem = t_makem;
+ setabs = t_setabs;
+ setch = t_setch;
+ sethl = t_sethl;
+ setht = t_setht;
+ setslant = t_setslant;
+ vmot = t_vmot;
+ xlss = t_xlss;
+ findft = t_findft;
+ width = t_width;
+ mchbits = t_mchbits;
+ ptlead = t_ptlead;
+ ptout = t_ptout;
+ ptpause = t_ptpause;
+ setfont = t_setfont;
+ setps = t_setps;
+ setwd = t_setwd;
+
+ /* open table for device, */
+ /* read in resolution, size info, font info, etc., set params */
+ if ((p = getenv("TYPESETTER")) != 0)
+ strcpy(devname, p);
+ if (termtab[0] == 0)
+ strcpy(termtab, DWBfontdir);
+ if (fontdir[0] == 0)
+ strcpy(fontdir, DWBfontdir);
+ if (devname[0] == 0)
+ strcpy(devname, TDEVNAME);
+ hyf = 1;
+ lg = 1;
+
+ sprintf(buf, "/dev%s/DESC", devname);
+ strcat(termtab, buf);
+ if (getdesc(termtab) < 0) {
+ ERROR "can't open DESC file %s", termtab WARN;
+ done3(1);
+ }
+ if (!ascii) {
+ OUT "x T %s\n", devname PUT;
+ OUT "x res %d %d %d\n", Inch, Hor, Vert PUT;
+ OUT "x init\n" PUT;
+ }
+ for (i = 1; i <= nfonts; i++)
+ setfp(i, fontlab[i], (char *) 0, 0);
+ sps = EM/3; /* space size */
+ ics = EM; /* insertion character space */
+ for (i = 0; i < (NTAB - 1) && DTAB * (i + 1) < TABMASK; i++)
+ tabtab[i] = DTAB * (i + 1);
+ tabtab[NTAB-1] = 0;
+ pl = 11 * INCH; /* paper length */
+ po = PO; /* page offset */
+ spacesz = SS;
+ lss = lss1 = VS;
+ ll = ll1 = lt = lt1 = LL;
+ t_specnames(); /* install names like "hyphen", etc. */
+}
+
+void t_specnames(void)
+{
+ int i;
+
+ for (i = 0; spnames[i].n; i++)
+ *spnames[i].n = chadd(spnames[i].v, Troffchar, Install);
+}
+
+void t_ptout(Tchar i)
+{
+ int dv;
+ Tchar *k;
+ int temp, a, b;
+ int diff;
+
+ if (cbits(i) != '\n') {
+ if (olinep >= oline + olnsize) {
+ diff = olinep - oline;
+ olnsize += OLNSIZE;
+ if ((oline = (Tchar *)realloc((char *)oline, olnsize *…
+ if (diff && olinep)
+ olinep = oline + diff;
+ } else {
+ ERROR "Output line overflow." WARN;
+ done(2);
+ }
+ }
+ *olinep++ = i;
+ return;
+ }
+ if (olinep == oline) {
+ lead += lss;
+ return;
+ }
+
+ hpos = po; /* ??? */
+ esc = 0; /* ??? */
+ ptesc(); /* the problem is to get back to the left end of the l…
+ dv = 0;
+ for (k = oline; k < olinep; k++) {
+ if (ismot(*k) && isvmot(*k)) {
+ temp = absmot(*k);
+ if (isnmot(*k))
+ temp = -temp;
+ dv += temp;
+ }
+ }
+ if (dv) {
+ vflag++;
+ *olinep++ = makem(-dv);
+ vflag = 0;
+ }
+
+ b = dip->blss + lss;
+ lead += dip->blss + lss;
+ dip->blss = 0;
+ for (k = oline; k < olinep; )
+ k += ptout0(k); /* now passing a pointer! */
+ olinep = oline;
+ lead += dip->alss;
+ a = dip->alss;
+ dip->alss = 0;
+ /*
+ OUT "x xxx end of line: hpos=%d, vpos=%d\n", hpos, vpos PUT;
+*/
+ OUT "n%d %d\n", b, a PUT; /* be nice to chuck */
+}
+
+int ptout0(Tchar *pi)
+{
+ int j, k, w;
+ int z, dx, dy, dx2, dy2, n;
+ Tchar i;
+ int outsize; /* size of object being printed */
+
+ w = 0;
+ outsize = 1; /* default */
+ i = *pi;
+ k = cbits(i);
+ if (ismot(i)) {
+ j = absmot(i);
+ if (isnmot(i))
+ j = -j;
+ if (isvmot(i))
+ lead += j;
+ else
+ esc += j;
+ return(outsize);
+ }
+ if (k == CHARHT) {
+ xpts = fbits(i); /* sneaky, font bits as size bits */
+ if (xpts != mpts)
+ ptps();
+ OUT "x H %ld\n", sbits(i) PUT;
+ return(outsize);
+ }
+ if (k == SLANT) {
+ OUT "x S %ld\n", sfbits(i)-180 PUT;
+ return(outsize);
+ }
+ if (k == WORDSP) {
+ oput('w');
+ return(outsize);
+ }
+ if (sfbits(i) == oldbits) {
+ xfont = pfont;
+ xpts = ppts;
+ } else
+ xbits(i, 2);
+ if (k == XON) {
+ extern int xon;
+ ptflush(); /* guarantee that everything is out */
+ if (esc)
+ ptesc();
+ if (xfont != mfont)
+ ptfont();
+ if (xpts != mpts)
+ ptps();
+ if (lead)
+ ptlead();
+ OUT "x X " PUT;
+ xon++;
+ for (j = 1; cbits(pi[j]) != XOFF; j++)
+ outascii(pi[j]);
+ oput('\n');
+ xon--;
+ return j+1;
+ }
+ if (k < 040 && k != DRAWFCN)
+ return(outsize);
+ j = z = 0;
+ if (k != DRAWFCN) {
+ if (widcache[k].fontpts == (xfont<<8) + xpts && !setwdf) {
+ w = widcache[k].width;
+ bd = 0;
+ cs = 0;
+ } else
+ w = getcw(k);
+ if (cs) {
+ if (bd)
+ w += (bd - 1) * HOR;
+ j = (cs - w) / 2;
+ w = cs - j;
+ if (bd)
+ w -= (bd - 1) * HOR;
+ }
+ if (iszbit(i)) {
+ if (cs)
+ w = -j;
+ else
+ w = 0;
+ z = 1;
+ }
+ }
+ esc += j;
+ if (xfont != mfont)
+ ptfont();
+ if (xpts != mpts)
+ ptps();
+ if (lead)
+ ptlead();
+ /* put out the real character here */
+ if (k == DRAWFCN) {
+ if (esc)
+ ptesc();
+ w = 0;
+ dx = absmot(pi[3]);
+ if (isnmot(pi[3]))
+ dx = -dx;
+ dy = absmot(pi[4]);
+ if (isnmot(pi[4]))
+ dy = -dy;
+ switch (cbits(pi[1])) {
+ case DRAWCIRCLE: /* circle */
+ OUT "D%c %d\n", DRAWCIRCLE, dx PUT; /* dx is di…
+ hpos += dx;
+ break;
+ case DRAWELLIPSE:
+ OUT "D%c %d %d\n", DRAWELLIPSE, dx, dy PUT;
+ hpos += dx;
+ break;
+ case DRAWBUILD:
+ k = cbits(pi[2]);
+ OUT "D%c %d ", DRAWBUILD, dx PUT;
+ if (k < ALPHABET)
+ OUT "%c\n", k PUT;
+ else
+ ptchname(k);
+ hpos += dx;
+ break;
+ case DRAWLINE: /* line */
+ k = cbits(pi[2]);
+ OUT "D%c %d %d ", DRAWLINE, dx, dy PUT;
+ if (k < ALPHABET)
+ OUT "%c\n", k PUT;
+ else
+ ptchname(k);
+ hpos += dx;
+ vpos += dy;
+ break;
+ case DRAWARC: /* arc */
+ dx2 = absmot(pi[5]);
+ if (isnmot(pi[5]))
+ dx2 = -dx2;
+ dy2 = absmot(pi[6]);
+ if (isnmot(pi[6]))
+ dy2 = -dy2;
+ OUT "D%c %d %d %d %d\n", DRAWARC,
+ dx, dy, dx2, dy2 PUT;
+ hpos += dx + dx2;
+ vpos += dy + dy2;
+ break;
+
+ case 's': /* using 's' internally to avoid .tr ~ */
+ pi[1] = '~';
+ case DRAWSPLINE: /* spline */
+ default: /* something else; copy it like spline */
+ OUT "D%c %d %d", (char)cbits(pi[1]), dx, dy PUT;
+ hpos += dx;
+ vpos += dy;
+ if (cbits(pi[3]) == DRAWFCN || cbits(pi[4]) == DRAWFCN…
+ /* it was somehow defective */
+ OUT "\n" PUT;
+ break;
+ }
+ for (n = 5; cbits(pi[n]) != DRAWFCN; n += 2) {
+ dx = absmot(pi[n]);
+ if (isnmot(pi[n]))
+ dx = -dx;
+ dy = absmot(pi[n+1]);
+ if (isnmot(pi[n+1]))
+ dy = -dy;
+ OUT " %d %d", dx, dy PUT;
+ hpos += dx;
+ vpos += dy;
+ }
+ OUT "\n" PUT;
+ break;
+ }
+ for (n = 3; cbits(pi[n]) != DRAWFCN; n++)
+ ;
+ outsize = n + 1;
+ } else if (k < ALPHABET) {
+ /* try to go faster and compress output */
+ /* by printing nnc for small positive motion followed by c */
+ /* kludgery; have to make sure set all the vars too */
+ if (esc > 0 && esc < 100) {
+ oput(esc / 10 + '0');
+ oput(esc % 10 + '0');
+ oput(k);
+ hpos += esc;
+ esc = 0;
+ } else {
+ if (esc)
+ ptesc();
+ oput('c');
+ oput(k);
+ oput('\n');
+ }
+ } else {
+ if (esc)
+ ptesc();
+ ptchname(k);
+ }
+ if (bd) {
+ bd -= HOR;
+ if (esc += bd)
+ ptesc();
+ if (k < ALPHABET)
+ OUT "c%c\n", k PUT;
+ else
+ ptchname(k);
+ if (z)
+ esc -= bd;
+ }
+ esc += w;
+ return(outsize);
+}
+
+void ptchname(int k)
+{
+ char *chn = chname(k);
+
+ switch (chn[0]) {
+ case MBchar:
+ OUT "c%s\n", chn+1 PUT; /* \n not needed? */
+ break;
+ case Number:
+ OUT "N%s\n", chn+1 PUT;
+ break;
+ case Troffchar:
+ OUT "C%s\n", chn+1 PUT;
+ break;
+ default:
+ ERROR "illegal char type %s", chn WARN;
+ break;
+ }
+}
+
+void ptflush(void) /* get us to a clean output state */
+{
+ if (TROFF) {
+ /* ptesc(); but always H, no h */
+ hpos += esc;
+ OUT "\nH%d\n", hpos PUT;
+ esc = 0;
+ ptps();
+ ptfont();
+ ptlead();
+ }
+}
+
+void ptps(void)
+{
+ int i, j, k;
+
+ i = xpts;
+ for (j = 0; i > (k = pstab[j]); j++)
+ if (!k) {
+ k = pstab[--j];
+ break;
+ }
+ if (!ascii)
+ OUT "s%d\n", k PUT; /* really should put out string rep…
+ mpts = i;
+}
+
+void ptfont(void)
+{
+ mfont = xfont;
+ if (ascii)
+ return;
+ if (xfont > nfonts) {
+ ptfpcmd(0, fonts[xfont].longname, 0); /* Put the desire…
+ * fontcache of the filter */
+ OUT "f0\n" PUT; /* make sure that it gets noticed */
+ } else
+ OUT "f%d\n", xfont PUT;
+}
+
+void ptfpcmd(int f, char *s, char *longname)
+{
+ if (f > nfonts) /* a bit risky? */
+ f = 0;
+ if (longname) {
+ OUT "x font %d %s %s\n", f, s, longname PUT;
+ } else {
+ OUT "x font %d %s\n", f, s PUT;
+ }
+/* OUT "f%d\n", xfont PUT; /* need this for buggy version of ado…
+ /* which apparently believes that x font means…
+ /* to set the font, not just the position. */
+}
+
+void t_ptlead(void)
+{
+ vpos += lead;
+ if (!ascii)
+ OUT "V%d\n", vpos PUT;
+ lead = 0;
+}
+
+void ptesc(void)
+{
+ hpos += esc;
+ if (!ascii)
+ if (esc > 0) {
+ oput('h');
+ if (esc>=10 && esc<100) {
+ oput(esc/10 + '0');
+ oput(esc%10 + '0');
+ } else
+ OUT "%d", esc PUT;
+ } else
+ OUT "H%d\n", hpos PUT;
+ esc = 0;
+}
+
+void ptpage(int n) /* called at end of each output page, we hope */
+{
+ int i;
+
+ if (NROFF)
+ return;
+ ptlead();
+ vpos = 0;
+ if (ascii)
+ return;
+ OUT "p%d\n", n PUT; /* new page */
+ for (i = 0; i <= nfonts; i++)
+ if (fontlab[i]) {
+ if (fonts[i].truename)
+ OUT "x font %d %s %s\n", i, fonts[i].longname,…
+ else
+ OUT "x font %d %s\n", i, fonts[i].longname PUT;
+ }
+ ptps();
+ ptfont();
+}
+
+void pttrailer(void)
+{
+ if (TROFF)
+ OUT "x trailer\n" PUT;
+}
+
+void ptstop(void)
+{
+ if (TROFF)
+ OUT "x stop\n" PUT;
+}
+
+void t_ptpause(void)
+{
+ if (ascii)
+ return;
+ ptlead();
+ vpos = 0;
+ pttrailer();
+ ptlead();
+ OUT "x pause\n" PUT;
+ flusho();
+ mpts = mfont = 0;
+ ptesc();
+ esc = po;
+ hpos = vpos = 0; /* probably in wrong place */
+}
diff --git a/troff/t11.c b/troff/t11.c
@@ -0,0 +1,260 @@
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+#define MAXCH NCHARS /* maximum number of global char na…
+char *chnames[MAXCH]; /* chnames[n-ALPHABET] -> name of char n */
+int nchnames; /* number of Cxy names currently seen */
+
+#define MAXPS 100 /* max number of point sizes */
+int pstab[MAXPS]; /* point sizes */
+int nsizes; /* number in DESC */
+
+Font fonts[MAXFONTS+1]; /* font info + ptr to width info */
+
+
+#define skipline(f) while (getc(f) != '\n')
+
+#define eq(s1, s2) (strcmp(s1, s2) == 0)
+
+int
+getdesc(char *name)
+{
+ FILE *fin;
+ char cmd[100], s[100];
+ int i, v;
+
+ if ((fin = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ while (fscanf(fin, "%s", cmd) != EOF) {
+ if (strcmp(cmd, "res") == 0) {
+ fscanf(fin, "%d", &Inch);
+ } else if (strcmp(cmd, "hor") == 0) {
+ fscanf(fin, "%d", &Hor);
+ } else if (strcmp(cmd, "vert") == 0) {
+ fscanf(fin, "%d", &Vert);
+ } else if (strcmp(cmd, "unitwidth") == 0) {
+ fscanf(fin, "%d", &Unitwidth);
+ } else if (strcmp(cmd, "sizes") == 0) {
+ nsizes = 0;
+ while (fscanf(fin, "%d", &v) != EOF && v != 0 && nsize…
+ pstab[nsizes++] = v;
+ } else if (strcmp(cmd, "fonts") == 0) {
+ fscanf(fin, "%d", &nfonts);
+ for (i = 1; i <= nfonts; i++) {
+ fscanf(fin, "%s", s);
+ fontlab[i] = PAIR(s[0], s[1]);
+ }
+ } else if (strcmp(cmd, "charset") == 0) { /* add any na…
+ while (fscanf(fin, "%s", s) != EOF)
+ chadd(s, Troffchar, Install);
+ break;
+ }
+ /* else
+ just skip anything else */
+ skipline(fin);
+ }
+ fclose(fin);
+ return 1;
+}
+
+static int checkfont(char *name)
+{ /* in case it's not really a font description file */
+ /* really paranoid, but consider \f. */
+ FILE *fp;
+ char buf[300], buf2[300];
+ int i, status = -1;
+
+ if ((fp = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ for (i = 1; i <= 10; i++) {
+ if (fgets(buf, sizeof buf, fp) == NULL)
+ break;
+ sscanf(buf, "%s", buf2);
+ if (buf2[0] == '#') {
+ i--;
+ continue;
+ }
+ if (eq(buf2, "name") || eq(buf2, "fontname") ||
+ eq(buf2, "special") || eq(buf2, "charset")) {
+ status = 1;
+ break;
+ }
+ }
+ fclose(fp);
+ return status;
+
+}
+
+int
+getfont(char *name, int pos) /* create width tab for font */
+{
+ FILE *fin;
+ Font *ftemp = &fonts[pos];
+ Chwid chtemp[MAXCH];
+ static Chwid chinit;
+ int i, nw, n, wid, kern, code, type;
+ char buf[100], ch[100], s1[100], s2[100], s3[100], cmd[300];
+
+ nw = code = 0;
+ /* fprintf(stderr, "read font %s onto %d\n", name, pos); */
+ if (checkfont(name) == -1)
+ return -1;
+ if ((fin = fopen(unsharp(name), "r")) == NULL)
+ return -1;
+ for (i = 0; i < ALPHABET; i++)
+ chtemp[i] = chinit; /* zero out to begin with */
+ ftemp->specfont = ftemp->ligfont = 0;
+ ftemp->defaultwidth = ftemp->spacewidth = Inch * Unitwidth / 72 / 3; /…
+ while (fscanf(fin, "%s", cmd) != EOF) {
+ if (strcmp(cmd, "name") == 0)
+ fscanf(fin, "%s", ftemp->longname);
+ else if (strcmp(cmd, "special") == 0)
+ ftemp->specfont = 1;
+ else if (strcmp(cmd, "ligatures") == 0) {
+ ftemp->ligfont = getlig(fin);
+ } else if (strcmp(cmd, "spacewidth") == 0) {
+ fscanf(fin, "%d", &ftemp->spacewidth);
+ } else if (strcmp(cmd, "defaultwidth") == 0) {
+ fscanf(fin, "%d", &ftemp->defaultwidth);
+ } else if (strcmp(cmd, "charset") == 0) {
+ wchar_t wc;
+ skipline(fin);
+ nw = ALPHABET;
+ while (fgets(buf, sizeof buf, fin) != NULL) {
+ sscanf(buf, "%s %s %s %s", ch, s1, s2, s3);
+ if (s1[0] != '"') { /* genuine new char…
+ sscanf(s1, "%d", &wid);
+ sscanf(s2, "%d", &kern);
+ code = strtol(s3, 0, 0); /* dec…
+ }
+ /* otherwise it's a synonym for prev character…
+ /* so leave previous values intact */
+
+
+ /* decide what kind of alphabet it might come …
+
+
+ if (strlen(ch) == 1) { /* it's ascii */
+ n = ch[0]; /* origin includes n…
+ chtemp[n].num = ch[0];
+ } else if (ch[0] == '\\' && ch[1] == '0') {
+ n = strtol(ch+1, 0, 0); /* \0oc…
+ chtemp[n].num = n;
+#ifdef UNICODE
+ } else if (mbtowc(&wc, ch, strlen(ch)) > 1) {
+ chtemp[nw].num = chadd(ch, MBchar, In…
+ n = nw;
+ nw++;
+#endif /*UNICODE*/
+ } else {
+ if (strcmp(ch, "---") == 0) { /* no na…
+ sprintf(ch, "%d", code);
+ type = Number;
+ } else
+ type = Troffchar;
+ chtemp[nw].num = chadd(ch, type, Insta…
+ n = nw;
+ nw++;
+ }
+ chtemp[n].wid = wid;
+ chtemp[n].kern = kern;
+ chtemp[n].code = code;
+ /*fprintf(stderr, "font %2.2s char %4.4s num %…
+ ftemp->longname, ch, n, wid, code);
+ */
+ }
+ break;
+ }
+ skipline(fin);
+ }
+ fclose(fin);
+ chtemp[' '].wid = ftemp->spacewidth; /* width of space on this …
+ ftemp->nchars = nw;
+ if (ftemp->wp)
+ free(ftemp->wp); /* god help us if this wasn't allocate…
+ ftemp->wp = (Chwid *) malloc(nw * sizeof(Chwid));
+ if (ftemp->wp == NULL)
+ return -1;
+ for (i = 0; i < nw; i++)
+ ftemp->wp[i] = chtemp[i];
+/*
+ * printf("%d chars: ", nw);
+ * for (i = 0; i < nw; i++)
+ * if (ftemp->wp[i].num > 0 && ftemp->wp[i].num < ALPHABET) {
+ * printf("%c %d ", ftemp->wp[i].num, ftemp->wp[i].wid);
+ * else if (i >= ALPHABET)
+ * printf("%d (%s) %d ", ftemp->wp[i].num,
+ * chnames[ftemp->wp[i].num-ALPHABET], ftemp->w…
+ * }
+ * printf("\n");
+ */
+ return 1;
+}
+
+int
+chadd(char *s, int type, int install) /* add s to global character name…
+{ /* or just look it up */
+
+ /* a temporary kludge: store the "type" as the first character */
+ /* of the string, so we can remember from whence it came */
+
+ char *p;
+ int i;
+
+/* fprintf(stderr, "into chadd %s %c %c\n", s, type, install); /* */
+ for (i = 0; i < nchnames; i++)
+ if (type == chnames[i][0] && eq(s, chnames[i]+1)) /* +1 since …
+ break;
+/* fprintf(stderr, "i %d, nchnames %d\n", i, nchnames); /* */
+ if (i < nchnames) /* found same type and bytes at posit…
+ return ALPHABET + i;
+ else if (install == Lookup) /* not found, and we were just look…
+ return -1;
+
+ chnames[nchnames] = p = (char *) malloc(strlen(s)+1+1); /* type…
+ if (p == NULL) {
+ ERROR "out of space adding character %s", s WARN;
+ return LEFTHAND;
+ }
+ if (nchnames >= NCHARS - ALPHABET) {
+ ERROR "out of table space adding character %s", s WARN;
+ return LEFTHAND;
+ }
+ strcpy(chnames[nchnames]+1, s);
+ chnames[nchnames][0] = type;
+/* fprintf(stderr, "installed %c%s at %d\n", type, s, nchnames); /* */
+ return nchnames++ + ALPHABET;
+}
+
+char *chname(int n) /* return string for char with index n */
+{ /* includes type char at front, to be peeled off else…
+ if (n >= ALPHABET && n < nchnames + ALPHABET)
+ return chnames[n-ALPHABET];
+ else
+ return "";
+}
+
+int
+getlig(FILE *fin) /* pick up ligature list */
+{
+ int lig;
+ char temp[200];
+
+ lig = 0;
+ while (fscanf(fin, "%s", temp) != EOF && strcmp(temp, "0") != 0) {
+ if (strcmp(temp, "fi") == 0)
+ lig |= LFI;
+ else if (strcmp(temp, "fl") == 0)
+ lig |= LFL;
+ else if (strcmp(temp, "ff") == 0)
+ lig |= LFF;
+ else if (strcmp(temp, "ffi") == 0)
+ lig |= LFFI;
+ else if (strcmp(temp, "ffl") == 0)
+ lig |= LFFL;
+ else
+ fprintf(stderr, "illegal ligature %s ignored\n", temp);
+ }
+ return lig;
+}
diff --git a/troff/t6.c b/troff/t6.c
@@ -0,0 +1,889 @@
+/*
+ * t6.c
+ *
+ * width functions, sizes and fonts
+ */
+
+#include "tdef.h"
+#include "fns.h"
+#include "ext.h"
+
+int fontlab[MAXFONTS+1];
+int cstab[MAXFONTS+1];
+int ccstab[MAXFONTS+1];
+int bdtab[MAXFONTS+1];
+int sbold = 0;
+
+int
+t_width(Tchar j)
+{
+ int i, k;
+
+ if (iszbit(j))
+ return 0;
+ if (ismot(j)) {
+ if (isvmot(j))
+ return(0);
+ k = absmot(j);
+ if (isnmot(j))
+ k = -k;
+ return(k);
+ }
+ i = cbits(j);
+ if (i < ' ') {
+ if (i == '\b')
+ return(-widthp);
+ if (i == PRESC)
+ i = eschar;
+ else if (i == HX)
+ return(0);
+ }
+ if (i == ohc)
+ return(0);
+ i = trtab[i];
+ if (i < ' ')
+ return(0);
+ if (sfbits(j) == oldbits) {
+ xfont = pfont;
+ xpts = ppts;
+ } else
+ xbits(j, 0);
+ if (i < nchnames + ALPHABET && widcache[i].fontpts == (xfont<<8) + xpt…
+ k = widcache[i].width;
+ else {
+ k = getcw(i);
+ if (bd)
+ k += (bd - 1) * HOR;
+ if (cs)
+ k = cs;
+ }
+ widthp = k;
+ return(k);
+}
+
+/*
+ * clear width cache-- s means just space
+ */
+void zapwcache(int s)
+{
+ int i;
+
+ if (s) {
+ widcache[' '].fontpts = 0;
+ return;
+ }
+ for (i=0; i<NWIDCACHE; i++)
+ widcache[i].fontpts = 0;
+}
+
+int
+onfont(int n, int f) /* is char n on font f? */
+{
+ int i;
+ Font *fp = &fonts[f];
+ Chwid *cp, *ep;
+ char *np;
+
+ if (n < ALPHABET) {
+ if (fp->wp[n].num == n) /* ascii at front */
+ return n;
+ else
+ return -1;
+ }
+ cp = &fp->wp[ALPHABET];
+ ep = &fp->wp[fp->nchars];
+ for ( ; cp < ep; cp++) /* search others */
+ if (cp->num == n)
+ return cp - &fp->wp[0];
+ /* maybe it was a \N... */
+ np = chname(n);
+ if (*np == Number) {
+ i = atoi(np+1); /* sscanf(np+1, "%d", &i); */
+ cp = &fp->wp[0];
+ ep = &fp->wp[fp->nchars];
+ for ( ; cp < ep; cp++) { /* search others */
+ if (cp->code == i)
+ return cp - &fp->wp[0];
+ }
+ return -2; /* a \N that doesn't have an entry */
+ }
+ return -1; /* vanilla not found */
+}
+
+int
+getcw(int i)
+{
+ int k, n, x;
+ Font *fp;
+ int nocache = 0;
+ if (i < ' ')
+ return 0;
+ bd = 0;
+ fp = &fonts[xfont];
+ if (i == ' ') { /* a blank */
+ k = (fp->spacewidth * spacesz + 6) / 12;
+ /* this nonsense because .ss cmd uses 1/36 em as its units */
+ /* and default is 12 */
+ } else if ((n = onfont(i, xfont)) >= 0) { /* on this font at n …
+ k = fp->wp[n].wid;
+ if (setwdf)
+ numtabp[CT].val |= fp->wp[n].kern;
+ } else if (n == -2) { /* \N with default width */
+
+ k = fp->defaultwidth;
+ } else { /* not on current font */
+ nocache = 1;
+ k = fp->defaultwidth; /* default-size space */
+ if (smnt) {
+ int ii, jj;
+ for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts …
+ if ((n = onfont(i, ii)) >= 0) {
+ k = fonts[ii].wp[n].wid;
+ if (xfont == sbold)
+ bd = bdtab[ii];
+ if (setwdf)
+ numtabp[CT].val |= fonts[ii].w…
+ break;
+ }
+ }
+ }
+ }
+ if (!bd)
+ bd = bdtab[xfont];
+ if (cs = cstab[xfont]) {
+ nocache = 1;
+ if (ccs = ccstab[xfont])
+ x = ccs;
+ else
+ x = xpts;
+ cs = (cs * EMPTS(x)) / 36;
+ }
+ /* was (k & BYTEMASK); since .wid is unsigned, should never happen */
+ if (k < 0)
+ ERROR "can't happen: negative width %d in getcw %d\n", k, i WA…
+ k = (k * xpts + (Unitwidth / 2)) / Unitwidth;
+ if (nocache|bd)
+ widcache[i].fontpts = 0;
+ else {
+ widcache[i].fontpts = (xfont<<8) + xpts;
+ widcache[i].width = k;
+ }
+ return(k);
+ /* Unitwidth is Units/Point, where
+ /* Units is the fundamental digitization
+ /* of the character set widths, and
+ /* Point is the number of goobies in a point
+ /* e.g., for cat, Units=36, Point=6, so Unitwidth=36/6=6
+ /* In effect, it's the size at which the widths
+ /* translate directly into units.
+ */
+}
+
+void xbits(Tchar i, int bitf)
+{
+ int k;
+
+ if(TROFF) {
+ xfont = fbits(i);
+ k = sbits(i);
+ if(k) {
+ xpts = pstab[k-1];
+ oldbits = sfbits(i);
+ pfont = xfont;
+ ppts = xpts;
+ return;
+ }
+ switch(bitf) {
+ case 0:
+ xfont = font;
+ xpts = pts;
+ break;
+ case 1:
+ xfont = pfont;
+ xpts = ppts;
+ break;
+ case 2:
+ xfont = mfont;
+ xpts = mpts;
+ }
+ }
+}
+
+
+/* these next two functions ought to be the same in troff and nroff, */
+/* but the data structures they search are different. */
+/* silly historical problem. */
+
+
+Tchar t_setch(int c)
+{
+ int j;
+ char temp[50];
+ char *s;
+
+ j = 0;
+ s = temp;
+ if (c == '(') { /* \(xx */
+ if ((*s++ = getach()) == 0 || (*s++ = getach()) == 0)
+ return(0);
+ } else { /* \C'...' */
+ c = getach();
+ while ((*s = getach()) != c && *s != 0 && s < temp + sizeof(te…
+ s++;
+ }
+ *s = '\0';
+#ifdef UNICODE
+ return chadd(temp, Troffchar, Install) | chbits; /* add name even if h…
+#else
+ if (NROFF) {
+ j = chadd(temp, Troffchar, Lookup);
+ if ( j == -1)
+ return 0;
+ else
+ return j | chbits;
+ } else
+ return chadd(temp, Troffchar, Install) | chbits; /* add name e…
+
+#endif /*UNICODE*/
+}
+
+Tchar t_setabs(void) /* set absolute char from \N'...' */
+{
+ int n;
+ char temp[10];
+
+ getch(); /* delim */
+ n = 0;
+ n = inumb(&n);
+ getch(); /* delim */
+ if (nonumb)
+ return 0;
+ sprintf(temp, "%d", n); /* convert into "#n" */
+ n = chadd(temp, Number, Install);
+ return n | chbits;
+}
+
+
+/*
+ * fontlab[] is a cache that contains font information
+ * for each font.
+ * fontlab[] contains the 1- or 2-character name of the
+ * font current associated with that font.
+ * fonts 1..nfonts correspond to the mounted fonts;
+ * the last of these are the special fonts.
+ * If we don't use the (named) font in one of the
+ * standard positions, we install the name in the next
+ * free slot of fontlab[] and font[].
+ * Whenever we need info about the font, we
+ * read in the data into the next free slot with getfont.
+ * The ptfont() (t10.c) routine will tell
+ * the device filter to put the font always at position
+ * zero if xfont > nfonts, so no need to change these filters.
+ * Yes, this is a bit kludgy.
+ *
+ * This gives the new specs of findft:
+ * find the font name i, where i also can be a number.
+ * Installs the font(name) i when not present
+ * returns -1 on error
+ */
+
+int
+t_findft(int i)
+{
+ int k;
+ Uchar *p;
+
+ p = unpair(i);
+
+ if (isdigit(p[0])) { /* first look for numbers */
+ k = p[0] - '0';
+ if (p[1] > 0 && isdigit(p[1]))
+ k = 10 * k + p[1] - '0';
+ if (k > 0 && k <= nfonts && k < smnt)
+ return(k); /* mounted font: .ft 3 */
+ if (fontlab[k] && k <= MAXFONTS) { /* translate */
+ return(k); /*number to a name */
+ } else {
+ fprintf(stderr, "troff: no font at position %d\n", k);
+ return(-1); /* wild number */
+ }
+ }
+
+ /*
+ * Now we look for font names
+ */
+ for (k = 1; fontlab[k] != i; k++) {
+ if (k > MAXFONTS)
+ return(-1); /* running out of fontlab space */
+ if (fontlab[k] == 0) { /* passed all existing names */
+ if (setfp(k, i, (char *) 0, 1) == -1)
+ return(-1);
+ else {
+ fontlab[k] = i; /* install the name */
+ return(k);
+ }
+ }
+ }
+ return(k); /* was one of the existing names */
+}
+
+
+void caseps(void)
+{
+ int i;
+
+ if (TROFF) {
+ if(skip())
+ i = apts1;
+ else {
+ noscale++;
+ i = inumb(&apts); /* this is a disaster for fra…
+ noscale = 0;
+ if(nonumb)
+ i = apts1;
+ }
+ casps1(i);
+ }
+}
+
+
+void casps1(int i)
+{
+
+/*
+ * in olden times, it used to ignore changes to 0 or negative.
+ * this is meant to allow the requested size to be anything,
+ * in particular so eqn can generate lots of \s-3's and still
+ * get back by matching \s+3's.
+
+ if (i <= 0)
+ return;
+*/
+ apts1 = apts;
+ apts = i;
+ pts1 = pts;
+ pts = findps(i);
+ mchbits();
+}
+
+int
+findps(int i)
+{
+ int j, k;
+
+ for (j=k=0 ; pstab[j] != 0 ; j++)
+ if (abs(pstab[j]-i) < abs(pstab[k]-i))
+ k = j;
+
+ return(pstab[k]);
+}
+
+
+void t_mchbits(void)
+{
+ int i, j, k;
+
+ i = pts;
+ for (j = 0; i > (k = pstab[j]); j++)
+ if (!k) {
+ j--;
+ break;
+ }
+ chbits = 0;
+ setsbits(chbits, ++j);
+ setfbits(chbits, font);
+ sps = width(' ' | chbits);
+ zapwcache(1);
+}
+
+void t_setps(void)
+{
+ int i, j;
+
+ j = 0;
+ i = cbits(getch());
+ if (isdigit(i)) { /* \sd or \sdd */
+ i -= '0';
+ if (i == 0) /* \s0 */
+ j = apts1;
+ else if (i <= 3 && (ch=getch()) && isdigit(j = cbits(ch))) { …
+ j = 10 * i + j - '0';
+ ch = 0;
+ } else /* \sd */
+ j = i;
+ } else if (i == '(') { /* \s(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ if (j == 0) /* \s(00 */
+ j = apts1;
+ } else if (i == '+' || i == '-') { /* \s+, \s- */
+ j = cbits(getch());
+ if (isdigit(j)) { /* \s+d, \s-d */
+ j -= '0';
+ } else if (j == '(') { /* \s+(dd, \s-(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ }
+ if (i == '-')
+ j = -j;
+ j += apts;
+ }
+ casps1(j);
+}
+
+
+Tchar t_setht(void) /* set character height from \H'...' */
+{
+ int n;
+ Tchar c;
+
+ getch();
+ n = inumb(&apts);
+ getch();
+ if (n == 0 || nonumb)
+ n = apts; /* does this work? */
+ c = CHARHT;
+ c |= ZBIT;
+ setsbits(c, n);
+ setfbits(c, pts); /* sneaky, CHARHT font bits are size bits */
+ return(c);
+}
+
+Tchar t_setslant(void) /* set slant from \S'...' */
+{
+ int n;
+ Tchar c;
+
+ getch();
+ n = 0;
+ n = inumb(&n);
+ getch();
+ if (nonumb)
+ n = 0;
+ c = SLANT;
+ c |= ZBIT;
+ setsfbits(c, n+180);
+ return(c);
+}
+
+
+void caseft(void)
+{
+ if (!TROFF) {
+ n_caseft();
+ return;
+ }
+ skip();
+ setfont(1);
+}
+
+
+void t_setfont(int a)
+{
+ int i, j;
+
+ if (a)
+ i = getrq();
+ else
+ i = getsn();
+ if (!i || i == 'P') {
+ j = font1;
+ goto s0;
+ }
+ if (/* i == 'S' || */ i == '0') /* an experiment -- why can't w…
+ return;
+ if ((j = findft(i)) == -1)
+ if ((j = setfp(0, i, (char*) 0, 1)) == -1) /* try to pu…
+ return;
+s0:
+ font1 = font;
+ font = j;
+ mchbits();
+}
+
+
+void t_setwd(void)
+{
+ int base, wid;
+ Tchar i;
+ int delim, emsz, k;
+ int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+
+ base = numtabp[ST].val = numtabp[SB].val = wid = numtabp[CT].val = 0;
+ if (ismot(i = getch()))
+ return;
+ delim = cbits(i);
+ savhp = numtabp[HP].val;
+ numtabp[HP].val = 0;
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ setwdf++;
+ while (cbits(i = getch()) != delim && !nlflg) {
+ k = width(i);
+ wid += k;
+ numtabp[HP].val += k;
+ if (!ismot(i)) {
+ emsz = (INCH/72) * xpts;
+ } else if (isvmot(i)) {
+ k = absmot(i);
+ if (isnmot(i))
+ k = -k;
+ base -= k;
+ emsz = 0;
+ } else
+ continue;
+ if (base < numtabp[SB].val)
+ numtabp[SB].val = base;
+ if ((k = base + emsz) > numtabp[ST].val)
+ numtabp[ST].val = k;
+ }
+ setn1(wid, 0, (Tchar) 0);
+ numtabp[HP].val = savhp;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ mchbits();
+ setwdf = 0;
+}
+
+
+Tchar t_vmot(void)
+{
+ dfact = lss;
+ vflag++;
+ return t_mot();
+}
+
+
+Tchar t_hmot(void)
+{
+ dfact = EM;
+ return t_mot();
+}
+
+
+Tchar t_mot(void)
+{
+ int j, n;
+ Tchar i;
+
+ j = HOR;
+ getch(); /*eat delim*/
+ if (n = atoi0()) {
+ if (vflag)
+ j = VERT;
+ i = makem(quant(n, j));
+ } else
+ i = 0;
+ getch();
+ vflag = 0;
+ dfact = 1;
+ return(i);
+}
+
+
+Tchar t_sethl(int k)
+{
+ int j;
+ Tchar i;
+
+ j = EM / 2;
+ if (k == 'u')
+ j = -j;
+ else if (k == 'r')
+ j = -2 * j;
+ vflag++;
+ i = makem(j);
+ vflag = 0;
+ return(i);
+}
+
+
+Tchar t_makem(int i)
+{
+ Tchar j;
+
+ if (i >= 0)
+ j = i;
+ else
+ j = -i;
+ if (Hor > 1 && !vflag)
+ j = (j + Hor/2)/Hor * Hor;
+ j |= MOT;
+ if (i < 0)
+ j |= NMOT;
+ if (vflag)
+ j |= VMOT;
+ return(j);
+}
+
+
+Tchar getlg(Tchar i)
+{
+ Tchar j, k;
+ int lf;
+
+ if (!TROFF)
+ return i;
+ if ((lf = fonts[fbits(i)].ligfont) == 0) /* font lacks ligatures */
+ return(i);
+ j = getch0();
+ if (cbits(j) == 'i' && (lf & LFI))
+ j = LIG_FI;
+ else if (cbits(j) == 'l' && (lf & LFL))
+ j = LIG_FL;
+ else if (cbits(j) == 'f' && (lf & LFF)) {
+ if ((lf & (LFFI|LFFL)) && lg != 2) {
+ k = getch0();
+ if (cbits(k)=='i' && (lf&LFFI))
+ j = LIG_FFI;
+ else if (cbits(k)=='l' && (lf&LFFL))
+ j = LIG_FFL;
+ else {
+ *pbp++ = k;
+ j = LIG_FF;
+ }
+ } else
+ j = LIG_FF;
+ } else {
+ *pbp++ = j;
+ j = i;
+ }
+ return(i & SFMASK | j);
+}
+
+
+void caselg(void)
+{
+
+ if(TROFF) {
+ skip();
+ lg = atoi0();
+ if (nonumb)
+ lg = 1;
+ }
+}
+
+void casefp(void)
+{
+ int i, j;
+
+ if (!TROFF) {
+ n_casefp();
+ return;
+ }
+ skip();
+ i = cbits(getch());
+ if (isdigit(i)) {
+ i -= '0';
+ j = cbits(getch());
+ if (isdigit(j))
+ i = 10 * i + j - '0';
+ }
+ if (i <= 0 || i > nfonts)
+ ERROR "fp: bad font position %d", i WARN;
+ else if (skip() || !(j = getrq()))
+ ERROR "fp: no font name" WARN;
+ else if (skip() || !getname())
+ setfp(i, j, (char*) 0, 1);
+ else /* 3rd argument = filename */
+ setfp(i, j, nextf, 1);
+}
+
+char *strdupl(const char *s) /* make a copy of s */
+{
+ char *t;
+
+ t = (char *) malloc(strlen(s) + 1);
+ if (t == NULL)
+ ERROR "out of space in strdupl(%s)", s FATAL;
+ strcpy(t, s);
+ return t;
+}
+
+int
+setfp(int pos, int f, char *truename, int print) /* mount font f at pos…
+{
+ char pathname[NS], shortname[NS], *sl;
+
+ sl = (char*)0;
+ zapwcache(0);
+ if (truename)
+ strcpy(shortname, truename);
+ else
+ strcpy(shortname, (char *) unpair(f));
+ if (truename && strrchr(truename, '/')) { /* .fp 1 R dir/file: …
+ sprintf(pathname, "%s", truename);
+ if (fonts[pos].truename)
+ free(fonts[pos].truename);
+ fonts[pos].truename = strdupl(truename);
+ } else if (truename) { /* synonym: .fp 1 R Avan…
+ sprintf(pathname, "%s/dev%s/%s", fontdir, devname, truename);
+ truename = 0; /* so doesn't get repeated by ptfpcmd */
+ } else /* vanilla: .fp 5 XX */
+ sprintf(pathname, "%s/dev%s/%s", fontdir, devname, shortname);
+ if (truename == 0 && fonts[pos].truename != 0) {
+ free(fonts[pos].truename);
+ fonts[pos].truename = 0;
+ }
+ if (getfont(pathname, pos) < 0) {
+ ERROR "Can't open font file %s", pathname WARN;
+ return -1;
+ }
+ if (print && !ascii) {
+ ptfpcmd(pos, fonts[pos].longname, truename);
+ ptfont();
+ }
+ if (pos == smnt) {
+ smnt = 0;
+ sbold = 0;
+ }
+ fontlab[pos] = f;
+ if (smnt == 0 && fonts[pos].specfont)
+ smnt = pos;
+ bdtab[pos] = cstab[pos] = ccstab[pos] = 0;
+ return pos;
+}
+
+/*
+ * .cs request; don't check legality of optional arguments
+ */
+void casecs(void)
+{
+ int i, j;
+
+ if (TROFF) {
+ int savtr = trace;
+
+ trace = 0;
+ noscale++;
+ skip();
+ if (!(i = getrq()) || (i = findft(i)) < 0)
+ goto rtn;
+ skip();
+ cstab[i] = atoi0();
+ skip();
+ j = atoi0();
+ if(nonumb)
+ ccstab[i] = 0;
+ else
+ ccstab[i] = findps(j);
+ rtn:
+ zapwcache(0);
+ noscale = 0;
+ trace = savtr;
+ }
+}
+
+
+void casebd(void)
+{
+ int i, j, k;
+
+ j=0;
+ if (!TROFF) {
+ n_casebd();
+ return;
+ }
+ zapwcache(0);
+ k = 0;
+bd0:
+ if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
+ if (k)
+ goto bd1;
+ else
+ return;
+ }
+ if (j == smnt) {
+ k = smnt;
+ goto bd0;
+ }
+ if (k) {
+ sbold = j;
+ j = k;
+ }
+bd1:
+ skip();
+ noscale++;
+ bdtab[j] = atoi0();
+ noscale = 0;
+}
+
+
+void casevs(void)
+{
+ int i;
+
+ if (!TROFF) {
+ n_casevs();
+ return;
+ }
+ skip();
+ vflag++;
+ dfact = INCH; /* default scaling is points! */
+ dfactd = 72;
+ res = VERT;
+ i = inumb(&lss);
+ if (nonumb)
+ i = lss1;
+ if (i < VERT)
+ i = VERT;
+ lss1 = lss;
+ lss = i;
+}
+
+
+void casess(void)
+{
+ int i;
+
+ if(TROFF) {
+ noscale++;
+ skip();
+ if(i = atoi0()) {
+ spacesz = i & 0177;
+ zapwcache(0);
+ sps = width(' ' | chbits);
+ }
+ noscale = 0;
+ }
+}
+
+
+Tchar t_xlss(void)
+{
+ /* stores \x'...' into two successive Tchars.
+ /* the first contains HX, the second the value,
+ /* encoded as a vertical motion.
+ /* decoding is done in n2.c by pchar().
+ */
+ int i;
+
+ getch();
+ dfact = lss;
+ i = quant(atoi0(), VERT);
+ dfact = 1;
+ getch();
+ if (i >= 0)
+ *pbp++ = MOT | VMOT | i;
+ else
+ *pbp++ = MOT | VMOT | NMOT | -i;
+ return(HX);
+}
+
+Uchar *unpair(int i)
+{
+ static Uchar name[3];
+
+ name[0] = i & SHORTMASK;
+ name[1] = (i >> SHORT) & SHORTMASK;
+ name[2] = 0;
+ return name;
+}
diff --git a/troff/tdef.h b/troff/tdef.h
@@ -0,0 +1,673 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+#undef MB_CUR_MAX
+#define MB_CUR_MAX 3
+
+#define NROFF (!TROFF)
+
+/* Site dependent definitions */
+
+#ifndef TMACDIR
+#define TMACDIR "lib/tmac/tmac."
+#endif
+#ifndef FONTDIR
+#define FONTDIR "lib/font"
+#endif
+#ifndef NTERMDIR
+#define NTERMDIR "lib/term/tab."
+#endif
+#ifndef TDEVNAME
+#define TDEVNAME "post"
+#endif
+#ifndef NDEVNAME
+#define NDEVNAME "37"
+#endif
+#ifndef TEXHYPHENS
+#define TEXHYPHENS "/usr/lib/tex/macros/hyphen.tex"
+#endif
+#ifndef ALTHYPHENS
+#define ALTHYPHENS "lib/tmac/hyphen.tex" /* another place…
+#endif
+
+typedef unsigned char Uchar;
+typedef unsigned short Ushort;
+
+typedef /*unsigned*/ long Tchar;
+
+typedef struct Blockp Blockp;
+typedef struct Diver Diver;
+typedef struct Stack Stack;
+typedef struct Divsiz Divsiz;
+typedef struct Contab Contab;
+typedef struct Numtab Numtab;
+typedef struct Numerr Numerr;
+typedef struct Env Env;
+typedef struct Term Term;
+typedef struct Chwid Chwid;
+typedef struct Font Font;
+typedef struct Spnames Spnames;
+typedef struct Wcache Wcache;
+typedef struct Tbuf Tbuf;
+
+/* this simulates printf into a buffer that gets flushed sporadically */
+/* the BSD goo is because SunOS sprintf doesn't return anything useful */
+
+#ifdef BSD4_2
+#define OUT (obufp += strlen(sprintf(obufp,
+#define PUT ))) > obuf+BUFSIZ ? flusho() : 1
+#else
+#define OUT (obufp += sprintf(obufp,
+#define PUT )) > obuf+BUFSIZ ? flusho() : 1
+#endif
+
+#define oputs(a) OUT "%s", a PUT
+#define oput(c) ( *obufp++ = (c), obufp > obuf+BUFSIZ ? …
+
+extern char errbuf[];
+#define ERROR sprintf(errbuf,
+#define WARN ), errprint()
+#define FATAL ), errprint(), exit(1)
+
+/* starting values for typesetting parameters: */
+
+#define PS 10 /* default point size */
+#define FT 1 /* default font position */
+#define ULFONT 2 /* default underline font */
+#define BDFONT 3 /* default emboldening font */
+#define BIFONT 4 /* default bold italic font */
+#define LL (unsigned) 65*INCH/10 /* line length; 39picas=…
+#define VS ((12*INCH)/72) /* initial vert space */
+
+
+#define EMPTS(pts) (((long)Inch*(pts) + 36) / 72)
+#define EM (TROFF? EMPTS(pts): t.Em)
+#define INCH (TROFF? Inch: 240)
+#define HOR (TROFF? Hor: t.Adj)
+#define VERT (TROFF? Vert: t.Vert)
+#define PO (TROFF? Inch: 0)
+#define SPS (TROFF? EMPTS(pts)/3: INCH/10)
+#define SS (TROFF? 12: INCH/10)
+#define ICS (TROFF? EMPTS(pts): 2*INCH/10)
+#define DTAB (TROFF? (INCH/2): 0)
+
+/* These "characters" are used to encode various internal functions
+/* Some make use of the fact that most ascii characters between
+/* 0 and 040 don't have any graphic or other function.
+/* The few that do have a purpose (e.g., \n, \b, \t, ...
+/* are avoided by the ad hoc choices here.
+/* See ifilt[] in n1.c for others -- 1, 2, 3, 5, 6, 7, 010, 011, 012
+*/
+
+#define LEADER 001
+#define IMP 004 /* impossible char; glues things together…
+#define TAB 011
+#define RPT 014 /* next character is to be repeated many …
+#define CHARHT 015 /* size field sets character height */
+#define SLANT 016 /* size field sets amount of slant */
+#define DRAWFCN 017 /* next several chars describe arb dr…
+# define DRAWLINE 'l' /* line: 'l' dx dy char */
+# define DRAWCIRCLE 'c' /* circle: 'c' r */
+# define DRAWELLIPSE 'e' /* ellipse: 'e' rx ry */
+# define DRAWARC 'a' /* arc: 'a' dx dy dx …
+# define DRAWSPLINE '~' /* quadratic B spline: '~'…
+ /* other splines go thru too */
+/* NOTE: the use of ~ is a botch since it's often used in .tr commands */
+/* better to use a letter like s, but change it in the postprocessors too */
+/* for now, this is taken care of in n9.c and t10.c */
+# define DRAWBUILD 'b' /* built-up character (e.g.…
+
+#define LEFT 020 /* \{ */
+#define RIGHT 021 /* \} */
+#define FILLER 022 /* \& and similar purposes */
+#define XON 023 /* \X'...' starts here */
+#define OHC 024 /* optional hyphenation character \% */
+#define CONT 025 /* \c character */
+#define PRESC 026 /* printable escape */
+#define UNPAD 027 /* unpaddable blank */
+#define XPAR 030 /* transparent mode indicator */
+#define FLSS 031 /* next Tchar contains vertical space */
+ /* used when recalling diverted text */
+#define WORDSP 032 /* paddable word space */
+#define ESC 033 /* current escape character */
+#define XOFF 034 /* \X'...' ends here */
+ /* matches XON, but they will probably never nest */
+ /* so could drop this when another control is needed */
+#define HX 035 /* next character is value of \x'...' */
+#define MOTCH 036 /* this "character" is really motion; used by …
+
+#define HYPHEN c_hyphen
+#define EMDASH c_emdash /* \(em */
+#define RULE c_rule /* \(ru */
+#define MINUS c_minus /* minus sign on current fo…
+#define LIG_FI c_fi /* \(ff */
+#define LIG_FL c_fl /* \(fl */
+#define LIG_FF c_ff /* \(ff */
+#define LIG_FFI c_ffi /* \(Fi */
+#define LIG_FFL c_ffl /* \(Fl */
+#define ACUTE c_acute /* acute accent \(aa */
+#define GRAVE c_grave /* grave accent \(ga */
+#define UNDERLINE c_under /* \(ul */
+#define ROOTEN c_rooten /* root en \(rn */
+#define BOXRULE c_boxrule /* box rule \(br */
+#define LEFTHAND c_lefthand /* left hand for word overflow */
+#define DAGGER c_dagger /* dagger for end of sentence/fo…
+
+#define HYPHALG 1 /* hyphenation algorithm: 0=>good old t…
+
+
+/* array sizes, and similar limits: */
+
+#define MAXFONTS 99 /* Maximum number of fonts in fontab */
+#define NM 91 /* requests + macros */
+#define NN NNAMES /* number registers */
+#define NNAMES 15 /* predefined reg names */
+#define NIF 15 /* if-else nesting */
+#define NS 128 /* name buffer */
+#define NTM 1024 /* tm buffer */
+#define NEV 3 /* environments */
+#define EVLSZ 10 /* size of ev stack */
+
+#define STACKSIZE (12*1024) /* stack for macros and strings in p…
+#define NHYP 10 /* max hyphens per word */
+#define NHEX 512 /* byte size of exception word list */
+#define NTAB 100 /* tab stops */
+#define NSO 5 /* "so" depth */
+#define NMF 5 /* number of -m flags */
+#define WDSIZE 500 /* word buffer click size */
+#define LNSIZE 4000 /* line buffer click size */
+#define OLNSIZE 5000 /* output line buffer click; bigger …
+#define NDI 5 /* number of diversions */
+
+#define ALPHABET alphabet /* number of characters in basic alpha…
+ /* 128 for parochial USA 7-bit ascii, */
+ /* 256 for "European" mode with e.g., Latin-1 */
+
+ /* NCHARS must be greater than
+ ALPHABET (ascii stuff) + total number of distinct char names
+ from all fonts that will be run in this job (including
+ unnamed ones and \N's)
+ */
+
+#define NCHARS (8*1024) /* maximum size of troff characte…
+
+
+ /* However for nroff you want only :
+ 1. number of special codes in charset of DESC, which ends up being the
+ value of nchtab and which must be less than 512.
+ 2. ALPHABET, which apparently is the size of the portion of the tables…
+ for special control symbols
+ Apparently the max N of \N is irrelevant; */
+ /* to allow \N of up to 254 with up to 338 special characters
+ you need NCHARS of 338 + ALPHABET = 466 */
+
+#define NROFFCHARS 1024 /* maximum size of nroff characte…
+
+#define NTRTAB NCHARS /* number of items in trtab…
+#define NWIDCACHE NCHARS /* number of items in widcache[] */
+
+#define NTRAP 20 /* number of traps */
+#define NPN 20 /* numbers in "-o" */
+#define FBUFSZ 512 /* field buf size words */
+#define IBUFSZ 4096 /* bytes */
+#define NC 1024 /* cbuf size words */
+#define NOV 10 /* number of overstrike chars */
+#define NPP 10 /* pads per field */
+
+/*
+ Internal character representation:
+ Internally, every character is carried around as
+ a 32 bit cookie, called a "Tchar" (typedef long).
+ Bits are numbered 31..0 from left to right.
+ If bit 15 is 1, the character is motion, with
+ if bit 16 it's vertical motion
+ if bit 17 it's negative motion
+ If bit 15 is 0, the character is a real character.
+ if bit 31 zero motion
+ bits 30..24 size
+ bits 23..16 font
+*/
+
+/* in the following, "L" should really be a Tchar, but ... */
+/* numerology leaves room for 16 bit chars */
+
+#define MOT (01uL << 16) /* motion character indicator */
+#define VMOT (01uL << 30) /* vertical motion bit */
+#define NMOT (01uL << 29) /* negative motion indicator */
+/* #define MOTV (MOT|VMOT|NMOT) /* motion flags */
+/* #define MAXMOT (~MOTV) /* maximum motion permi…
+#define MAXMOT 0xFFFF
+
+#define ismot(n) ((n) & MOT)
+#define isvmot(n) (((n) & (MOT|VMOT)) == (MOT|VMOT)) /* m…
+#define isnmot(n) (((n) & (MOT|NMOT)) == (MOT|NMOT)) /* d…
+#define absmot(n) ((n) & 0xFFFF)
+
+#define ZBIT (01uL << 31) /* zero width char */
+#define iszbit(n) ((n) & ZBIT)
+
+#define FSHIFT 17
+#define SSHIFT (FSHIFT+7)
+#define SMASK (0177uL << SSHIFT) /* 128 distinct …
+#define FMASK (0177uL << FSHIFT) /* 128 distinct …
+#define SFMASK (SMASK|FMASK) /* size and font in …
+#define sbits(n) (((n) >> SSHIFT) & 0177)
+#define fbits(n) (((n) >> FSHIFT) & 0177)
+#define sfbits(n) (((n) & SFMASK) >> FSHIFT)
+#define cbits(n) ((n) & 0x1FFFF) /* isolate chara…
+ /* but don't include motions */
+extern int realcbits(Tchar);
+
+#define setsbits(n,s) n = (n & ~SMASK) | (Tchar)(s) << SSHIFT
+#define setfbits(n,f) n = (n & ~FMASK) | (Tchar)(f) << FSHIFT
+#define setsfbits(n,sf) n = (n & ~SFMASK) | (Tchar)(sf) << FSHIFT
+#define setcbits(n,c) n = (n & ~0xFFFFuL | (c)) /* set ch…
+
+#define BYTEMASK 0377
+#define BYTE 8
+
+#define SHORTMASK 0XFFFF
+#define SHORT 16
+
+#define TABMASK ((unsigned) INT_MAX >> 1)
+#define RTAB ((TABMASK << 1) & ~TABMASK)
+#define CTAB (RTAB << 1)
+
+#define TABBIT 02 /* bits in gchtab */
+#define LDRBIT 04
+#define FCBIT 010
+
+#define PAIR(A,B) (A|(B<<SHORT))
+
+
+extern int Inch, Hor, Vert, Unitwidth;
+
+struct Spnames
+{
+ int *n;
+ char *v;
+};
+
+extern Spnames spnames[];
+
+/*
+ String and macro definitions are stored conceptually in a giant array
+ indexed by type Offset. In olden times, this array was real, and thus
+ both huge and limited in size, leading to the "Out of temp file space"
+ error. In this version, the array is represented by a list of blocks,
+ pointed to by blist[].bp. Each block is of size BLK Tchars, and BLK
+ MUST be a power of 2 for the macros below to work.
+
+ The blocks associated with a particular string or macro are chained
+ together in the array blist[]. Each blist[i].nextoff contains the
+ Offset associated with the next block in the giant array, or -1 if
+ this is the last block in the chain. If .nextoff is 0, the block is
+ free.
+
+ To find the right index in blist for an Offset, divide by BLK.
+*/
+
+#define NBLIST 2048 /* starting number of blocks in all d…
+
+#define BLK 128 /* number of Tchars in a block; must be 2…
+
+#define rbf0(o) (blist[bindex(o)].bp[boffset(o)])
+#define bindex(o) ((o) / BLK)
+#define boffset(o) ((o) & (BLK-1))
+#define pastend(o) (((o) & (BLK-1)) == 0)
+/* #define incoff(o) ( (++o & (BLK-1)) ? o : blist[bindex(o-1)].…
+#define incoff(o) ( (((o)+1) & (BLK-1)) ? o+1 : blist[bindex(o)]…
+
+#define skipline(f) while (getc(f) != '\n')
+#define is(s) (strcmp(cmd, s) == 0)
+#define eq(s1, s2) (strcmp(s1, s2) == 0)
+
+
+typedef unsigned long Offset; /* an offset in mac…
+
+struct Blockp { /* info about a block: */
+ Tchar *bp; /* the data */
+ Offset nextoff; /* offset of next block in a chain */
+};
+
+extern Blockp *blist;
+
+#define RD_OFFSET (1 * BLK) /* .rd command uses block 1 */
+
+struct Diver { /* diversion */
+ Offset op;
+ int dnl;
+ int dimac;
+ int ditrap;
+ int ditf;
+ int alss;
+ int blss;
+ int nls;
+ int mkline;
+ int maxl;
+ int hnl;
+ int curd;
+};
+
+struct Stack { /* stack frame */
+ int nargs;
+ Stack *pframe;
+ Offset pip;
+ int pnchar;
+ Tchar prchar;
+ int ppendt;
+ Tchar pch;
+ Tchar *lastpbp;
+ int mname;
+};
+
+extern Stack s;
+
+struct Divsiz {
+ int dix;
+ int diy;
+};
+
+struct Contab { /* command or macro */
+ unsigned int rq;
+ Contab *link;
+ void (*f)(void);
+ Offset mx;
+ Offset emx;
+ Divsiz *divsiz;
+};
+
+#define C(a,b) {a, 0, b, 0, 0} /* how to initiali…
+
+extern Contab contab[NM];
+
+struct Numtab { /* number registers */
+ unsigned int r; /* name */
+ int val;
+ short fmt;
+ short inc;
+ Numtab *link;
+};
+
+extern Numtab numtab[NN];
+
+#define PN 0
+#define NL 1
+#define YR 2
+#define HP 3
+#define CT 4
+#define DN 5
+#define MO 6
+#define DY 7
+#define DW 8
+#define LN 9
+#define DL 10
+#define ST 11
+#define SB 12
+#define CD 13
+#define PID 14
+
+struct Wcache { /* width cache, indexed by character */
+ short fontpts;
+ short width;
+};
+
+struct Tbuf { /* growable Tchar buffer */
+ Tchar *_bufp;
+ unsigned int _size;
+};
+
+/* the infamous environment block */
+
+#define ics envp->_ics
+#define sps envp->_sps
+#define spacesz envp->_spacesz
+#define lss envp->_lss
+#define lss1 envp->_lss1
+#define ll envp->_ll
+#define ll1 envp->_ll1
+#define lt envp->_lt
+#define lt1 envp->_lt1
+#define ic envp->_ic
+#define icf envp->_icf
+#define chbits envp->_chbits
+#define spbits envp->_spbits
+#define nmbits envp->_nmbits
+#define apts envp->_apts
+#define apts1 envp->_apts1
+#define pts envp->_pts
+#define pts1 envp->_pts1
+#define font envp->_font
+#define font1 envp->_font1
+#define ls envp->_ls
+#define ls1 envp->_ls1
+#define ad envp->_ad
+#define nms envp->_nms
+#define ndf envp->_ndf
+#define nmwid envp->_nmwid
+#define fi envp->_fi
+#define cc envp->_cc
+#define c2 envp->_c2
+#define ohc envp->_ohc
+#define tdelim envp->_tdelim
+#define hyf envp->_hyf
+#define hyoff envp->_hyoff
+#define hyphalg envp->_hyphalg
+#define un1 envp->_un1
+#define tabc envp->_tabc
+#define dotc envp->_dotc
+#define adsp envp->_adsp
+#define adrem envp->_adrem
+#define lastl envp->_lastl
+#define nel envp->_nel
+#define admod envp->_admod
+#define wordp envp->_wordp
+#define spflg envp->_spflg
+#define linep envp->_linep
+#define wdend envp->_wdend
+#define wdstart envp->_wdstart
+#define wne envp->_wne
+#define ne envp->_ne
+#define nc envp->_nc
+#define nb envp->_nb
+#define lnmod envp->_lnmod
+#define nwd envp->_nwd
+#define nn envp->_nn
+#define ni envp->_ni
+#define ul envp->_ul
+#define cu envp->_cu
+#define ce envp->_ce
+#define in envp->_in
+#define in1 envp->_in1
+#define un envp->_un
+#define wch envp->_wch
+#define pendt envp->_pendt
+#define pendw envp->_pendw
+#define pendnf envp->_pendnf
+#define spread envp->_spread
+#define it envp->_it
+#define itmac envp->_itmac
+#define hyptr envp->_hyptr
+#define tabtab envp->_tabtab
+#define line envp->_line._bufp
+#define lnsize envp->_line._size
+#define word envp->_word._bufp
+#define wdsize envp->_word._size
+
+#define oline _oline._bufp
+#define olnsize _oline._size
+
+/*
+ * Note:
+ * If this structure changes in ni.c, you must change
+ * this as well, and vice versa.
+ */
+
+struct Env {
+ int _ics;
+ int _sps;
+ int _spacesz;
+ int _lss;
+ int _lss1;
+ int _ll;
+ int _ll1;
+ int _lt;
+ int _lt1;
+ Tchar _ic;
+ int _icf;
+ Tchar _chbits;
+ Tchar _spbits;
+ Tchar _nmbits;
+ int _apts;
+ int _apts1;
+ int _pts;
+ int _pts1;
+ int _font;
+ int _font1;
+ int _ls;
+ int _ls1;
+ int _ad;
+ int _nms;
+ int _ndf;
+ int _nmwid;
+ int _fi;
+ int _cc;
+ int _c2;
+ int _ohc;
+ int _tdelim;
+ int _hyf;
+ int _hyoff;
+ int _hyphalg;
+ int _un1;
+ int _tabc;
+ int _dotc;
+ int _adsp;
+ int _adrem;
+ int _lastl;
+ int _nel;
+ int _admod;
+ Tchar *_wordp;
+ int _spflg;
+ Tchar *_linep;
+ Tchar *_wdend;
+ Tchar *_wdstart;
+ int _wne;
+ int _ne;
+ int _nc;
+ int _nb;
+ int _lnmod;
+ int _nwd;
+ int _nn;
+ int _ni;
+ int _ul;
+ int _cu;
+ int _ce;
+ int _in;
+ int _in1;
+ int _un;
+ int _wch;
+ int _pendt;
+ Tchar *_pendw;
+ int _pendnf;
+ int _spread;
+ int _it;
+ int _itmac;
+ Tchar *_hyptr[NHYP];
+ long _tabtab[NTAB];
+ Tbuf _line;
+ Tbuf _word;
+};
+
+extern Env env[];
+extern Env *envp;
+
+enum { MBchar = 'U', Troffchar = 'C', Number = 'N', Install = 'i', Look…
+ /* U => utf, for instance; C => \(xx, N => \N'...' */
+
+
+
+struct Chwid { /* data on one character */
+ Ushort num; /* character number:
+ 0 -> not on this font
+ >= ALPHABET -> its number among all Cx…
+ Ushort code; /* char code for actual device. us…
+ char *str; /* code string for nroff */
+ Uchar wid; /* width */
+ Uchar kern; /* ascender/descender */
+};
+
+struct Font { /* characteristics of a font */
+ int name; /* int name, e.g., BI (2 chars) */
+ char longname[64]; /* long name of this font (e.g., "Bem…
+ char *truename; /* path name of table if not in standard…
+ int nchars; /* number of width entries for this …
+ char specfont; /* 1 == special font */
+ int spacewidth; /* width of space on this font */
+ int defaultwidth; /* default width of characters on this…
+ Chwid *wp; /* widths, etc., of the real characte…
+ char ligfont; /* 1 == ligatures exist on this font */
+};
+
+/* ligatures, ORed into ligfont */
+
+#define LFF 01
+#define LFI 02
+#define LFL 04
+#define LFFI 010
+#define LFFL 020
+
+/* tracing modes */
+#define TRNARGS 01 /* trace legality of numeric argument…
+#define TRREQ 02 /* trace requests */
+#define TRMAC 04 /* trace macros */
+#define RQERR 01 /* processing request/macro */
+
+/* typewriter driving table structure */
+
+
+extern Term t;
+struct Term {
+ int bset; /* these bits have to be on */
+ int breset; /* these bits have to be off */
+ int Hor; /* #units in minimum horiz motion */
+ int Vert; /* #units in minimum vert motion */
+ int Newline; /* #units in single line space */
+ int Char; /* #units in character width */
+ int Em; /* ditto */
+ int Halfline; /* half line units */
+ int Adj; /* minimum units for horizontal adjustm…
+ char *twinit; /* initialize terminal */
+ char *twrest; /* reinitialize terminal */
+ char *twnl; /* terminal sequence for newline */
+ char *hlr; /* half-line reverse */
+ char *hlf; /* half-line forward */
+ char *flr; /* full-line reverse */
+ char *bdon; /* turn bold mode on */
+ char *bdoff; /* turn bold mode off */
+ char *iton; /* turn italic mode on */
+ char *itoff; /* turn italic mode off */
+ char *ploton; /* turn plot mode on */
+ char *plotoff; /* turn plot mode off */
+ char *up; /* sequence to move up in plot mode */
+ char *down; /* ditto */
+ char *right; /* ditto */
+ char *left; /* ditto */
+
+ Font tfont; /* widths and other info, as in a tr…
+};
+
+extern Term t;
+
+/*
+ * for error reporting; keep track of escapes/requests with numeric arguments
+ */
+struct Numerr {
+ char type; /* request or escape? */
+ char esc; /* was escape sequence named esc */
+ char escarg; /* argument of esc's like \D'l' */
+ unsigned int req; /* was request or macro named req */
+};
diff --git a/troff/troff.1 b/troff/troff.1
@@ -0,0 +1,199 @@
+.TH TROFF 1
+.SH NAME
+troff, nroff \- text formatting and typesetting
+.SH SYNOPSIS
+.B troff
+[
+.I option ...
+]
+[
+.I file ...
+]
+.PP
+.B nroff
+[
+.I option ...
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Troff
+formats text in the named
+.I files
+for
+printing on a typesetter.
+.I Nroff
+does the same, but produces output suitable
+for typewriter-like devices.
+.PP
+If no
+.I file
+argument is present, the standard input is read.
+An argument consisting of a single minus
+.RB ( - )
+is taken to be
+a file name corresponding to the standard input.
+The options are:
+.nr xx \w'\fL-m\f2name\ \ '
+.TP \n(xxu
+.BI -o list
+Print pages in the comma-separated
+.I list
+of numbers and ranges.
+A range
+.IB N - M
+means
+.I N
+through
+.IR M ;
+initial
+.BI - M
+means up to
+.IR M ;
+final
+.IB N -
+means from
+.I N
+to the end.
+.TP
+.BI -n N
+Number first generated page
+.IR N .
+.TP
+.BI -m name
+Process the macro file
+.BI /sys/lib/tmac/tmac. name
+before the input
+.IR files .
+.TP
+.BI -r aN
+Set register
+.I a
+(one character name) to
+.IR N .
+.TP
+.B -i
+Read standard input after the input files are exhausted.
+.TP
+.B -q
+Invoke the simultaneous input-output mode of the
+.B rd
+request.
+.TP
+.B -N
+Produce output suitable for typewriter-like devices.
+.SS Typesetter devices (not \fL-N\fP) only
+.TP \n(xxu
+.B -a
+Send a printable
+textual
+approximation
+of the results to the standard output.
+.TP
+.BI -T dest
+Prepare output for typesetter
+.IR dest :
+.br
+.ns
+.RS
+.TP \w'\fL-TLatin1\ 'u
+.B -Tutf
+(The default.) PostScript printers with
+preprocessing to handle Unicode
+characters encoded in
+.SM UTF
+.PD0
+.TP
+.B -Tpost
+Regular PostScript printers
+.PD0
+.TP
+.B -T202
+Mergenthaler Linotron 202
+.RE
+.PD
+.TP "\w'\fL-m\f2name 'u"
+.BI -F dir
+Take font information from directory
+.IR dir .
+.SS Typewriter (\fL-N\fP) output only
+.TP \n(xxu
+.BI -s N
+Halt prior to every
+.I N
+pages (default
+.IR N =1)
+to allow paper loading or changing.
+.TP
+.BI -T name
+Prepare output for specified terminal.
+Known
+.I names
+include
+.B utf
+for the normal Plan 9
+.SM UTF
+encoding of the Unicode Standard character set (default),
+.B 37
+for the
+Teletype model 37,
+.B lp
+(`line-printer')
+for any terminal without half-line capability,
+.B 450
+for the \s-1DASI\s+1-450
+(Diablo Hyterm),
+and
+.B think
+(HP ThinkJet).
+.TP
+.B -e
+Produce equally-spaced words in adjusted
+lines, using full terminal resolution.
+.TP
+.B -h
+Use output tabs during horizontal spacing
+to speed output and reduce output character count.
+Tab settings are assumed to be every
+8 nominal character widths.
+.SH FILES
+.TF \*9/troff/term/*
+.TP
+.B /tmp/trtmp*
+temporary file
+.TP
+.B \*9/tmac/tmac.*
+standard macro files
+.TP
+.B \*9/troff/term/*
+terminal driving tables for
+.I nroff
+.TP
+.B \*9/troff/font/*
+font width tables for
+.I troff
+.SH SOURCE
+.B \*9/src/cmd/troff
+.SH "SEE ALSO"
+.IR lpr (1),
+.IR proof (1),
+.IR tr2post (1),
+.IR eqn (1),
+.IR tbl (1),
+.IR pic (1),
+.IR grap (1),
+.IR doctype (1),
+.IR ms (7),
+.IR image (7),
+.IR tex (1),
+.IR deroff (1)
+.br
+J. F. Ossanna and B. W. Kernighan,
+``Troff User's Manual''
+.br
+B. W. Kernighan,
+``A TROFF Tutorial'',
+.I
+Unix Research System Programmer's Manual,
+Tenth Edition, Volume 2.
diff --git a/troff/unansi b/troff/unansi
@@ -0,0 +1,49 @@
+# The awk program cvt will convert the relatively sterotyped ansi c
+# in this troff distribution into older-style c, by munging function
+# declarations.
+
+# You will also have to edit fns.h, by
+# sed 's/(.*)/()/g' fns.h >foo; mv foo fns.h
+# check this before doing the move!
+
+# you will also have to make some editing changes in
+# tdef.h in the Contab structure: s/(void)/()/
+# you may have to fix up some function declarations
+# in n4.c, the ones with (*f)(Tchar).
+
+# you will surely also have header files to deal with.
+
+# the most obvious cases are dealt with by the following
+# commands. make sure you do this stuff on a copy!
+
+# function prototypes in n8.c probably belong in fns.h. readpats(void) must
+# be readpats() before cvt runs.
+
+sed \
+ -e 's/(void)/()/' \
+ -e 's/(Tchar[^)]*);/();/' \
+ -e 's/(char[^)]*);/();/' \
+ -e 's/(int[^)]*);/();/' \
+n8.c >foo
+mv foo n8.c
+
+for i in *.c
+do
+ cvt $i >foo
+ mv foo $i
+done
+
+sed 's/(.*)/()/g' fns.h >foo
+mv foo fns.h
+
+sed -e 's/(void)/()/g' -e '/stdlib/d' tdef.h >foo
+mv foo tdef.h
+
+# Compliers may not approve of void *setbrk() in fns.h and n3.c.
+
+sed 's/^void\*[ ]setbrk/char* setbrk/' fns.h >foo
+mv foo fns.h
+
+sed 's/^void \*setbrk/char *setbrk/' n3.c >foo
+mv foo n3.c
+
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.