9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

commit a08a2a9000cf4cb01ac165f410dbe99a64191edd
parent c0a69251c8988bdbabf4f3d3e20f40e363990c6c
Author: Anselm R Garbe <anselm@garbe.us>
Date:   Mon, 24 Aug 2009 19:23:45 +0100

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

diff --git a/Makefile b/Makefile @@ -1,10 +1,10 @@ -# 9base - awk basename cal cat cleanname du echo grep rc sed seq sleep -# hoc sort tee test touch tr uniq from Plan 9 +# 9base - awk basename bc cal cat cleanname dc du echo grep mk rc sed seq sleep +# troff hoc sort tee test touch tr uniq from Plan 9 include config.mk -SUBDIRS = lib9 yacc awk basename bc dc du cal cat cleanname date echo grep ls \ - hoc rc read sed seq sleep sort tee test touch tr uniq +SUBDIRS = lib9 mk yacc awk basename bc dc du cal cat cleanname date echo grep ls \ + hoc rc read sed seq sleep sort tee test touch tr troff uniq all: @echo 9base build options: diff --git a/config.mk b/config.mk @@ -4,7 +4,7 @@ PREFIX = /usr/local/plan9 MANPREFIX = ${PREFIX}/share/man -VERSION = 3 +VERSION = 4 OBJTYPE = 386 #OBJTYPE = arm #OBJTYPE = x86_64 diff --git a/mk/Makefile b/mk/Makefile @@ -0,0 +1,11 @@ +# mk - mk unix port from plan9 +# Depends on ../lib9 + +TARG = mk + +OFILES = arc.o archive.o bufblock.o env.o file.o graph.o job.o lex.o \ + main.o match.o mk.o parse.o recipe.o rc.o rule.o run.o sh.o \ + shell.o shprint.o symtab.o var.o varsub.o word.o unix.o +MANFILES = mk.1 + +include ../std.mk diff --git a/mk/NOTICE b/mk/NOTICE @@ -0,0 +1,27 @@ +Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +Portions Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk). All rights reserved. +Portions Copyright © 1997-1999 Vita Nuova Limited. All rights reserved. +Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. + +Under a licence agreement with Lucent Technologies Inc. effective 1st March 2000, +Vita Nuova Holdings Limited has the right to determine (within a specified scope) +the form and content of sublicences for this software. + +Vita Nuova Holdings Limited now makes this software available as Free +Software under the terms of the `GNU General Public LIcense, Version 2' +(see the file LICENCE or http://www.fsf.org/copyleft/gpl.html for +the full terms and conditions). One of the conditions of that licence +is that you must keep intact all notices that refer to that licence and to the absence of +of any warranty: for this software, note that includes this NOTICE file in particular. + +This suite of programs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +`GNU General Public License' for more details. + +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices above. diff --git a/mk/README b/mk/README @@ -0,0 +1,7 @@ +This is a Unix port of mk, +originally done for the Inferno operating system. + +Russ Cox repackaged this to build as a standalone +Unix program. Send comments about packaging to +Russ Cox <rsc@post.harvard.edu> + diff --git a/mk/arc.c b/mk/arc.c @@ -0,0 +1,52 @@ +#include "mk.h" + +Arc * +newarc(Node *n, Rule *r, char *stem, Resub *match) +{ + Arc *a; + + a = (Arc *)Malloc(sizeof(Arc)); + a->n = n; + a->r = r; + a->stem = strdup(stem); + rcopy(a->match, match, NREGEXP); + a->next = 0; + a->flag = 0; + a->prog = r->prog; + return(a); +} + +void +dumpa(char *s, Arc *a) +{ + char buf[1024]; + + Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'", + s, a, a->n, a->r, a->flag, a->stem); + if(a->prog) + Bprint(&bout, " prog='%s'", a->prog); + Bprint(&bout, "\n"); + + if(a->n){ + snprint(buf, sizeof(buf), "%s ", (*s == ' ')? s:""); + dumpn(buf, a->n); + } +} + +void +nrep(void) +{ + Symtab *sym; + Word *w; + + sym = symlook("NREP", S_VAR, 0); + if(sym){ + w = sym->u.ptr; + if (w && w->s && *w->s) + nreps = atoi(w->s); + } + if(nreps < 1) + nreps = 1; + if(DEBUG(D_GRAPH)) + Bprint(&bout, "nreps = %d\n", nreps); +} diff --git a/mk/archive.c b/mk/archive.c @@ -0,0 +1,253 @@ +#include "mk.h" +#define ARMAG "!<arch>\n" +#define SARMAG 8 + +#define ARFMAG "`\n" +#define SARNAME 16 + +struct ar_hdr +{ + char name[SARNAME]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; +}; +#define SAR_HDR (SARNAME+44) + +static int dolong = 1; + +static void atimes(char *); +static char *split(char*, char**); + +long +readn(int f, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + while(t < n){ + m = read(f, a+t, n-t); + if(m <= 0){ + if(t == 0) + return m; + break; + } + t += m; + } + return t; +} +long +atimeof(int force, char *name) +{ + Symtab *sym; + long t; + char *archive, *member, buf[512]; + + archive = split(name, &member); + if(archive == 0) + Exit(); + + t = mtime(archive); + sym = symlook(archive, S_AGG, 0); + if(sym){ + if(force || (t > sym->u.value)){ + atimes(archive); + sym->u.value = t; + } + } + else{ + atimes(archive); + /* mark the aggegate as having been done */ + symlook(strdup(archive), S_AGG, "")->u.value = t; + } + /* truncate long member name to sizeof of name field in archive header */ + if(dolong) + snprint(buf, sizeof(buf), "%s(%s)", archive, member); + else + snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member); + sym = symlook(buf, S_TIME, 0); + if (sym) + return sym->u.value; + return 0; +} + +void +atouch(char *name) +{ + char *archive, *member; + int fd, i; + struct ar_hdr h; + long t; + + archive = split(name, &member); + if(archive == 0) + Exit(); + + fd = open(archive, ORDWR); + if(fd < 0){ + fd = create(archive, OWRITE, 0666); + if(fd < 0){ + fprint(2, "create %s: %r\n", archive); + Exit(); + } + write(fd, ARMAG, SARMAG); + } + if(symlook(name, S_TIME, 0)){ + /* hoon off and change it in situ */ + LSEEK(fd, SARMAG, 0); + while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){ + for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--) + ; + h.name[i+1]=0; + if(strcmp(member, h.name) == 0){ + t = SARNAME-sizeof(h); /* ughgghh */ + LSEEK(fd, t, 1); + fprint(fd, "%-12ld", time(0)); + break; + } + t = atol(h.size); + if(t&01) t++; + LSEEK(fd, t, 1); + } + } + close(fd); +} + +static void +atimes(char *ar) +{ + struct ar_hdr h; + long t; + int fd, i, namelen; + char buf[2048], *p, *strings; + char name[1024]; + Symtab *sym; + + strings = nil; + fd = open(ar, OREAD); + if(fd < 0) + return; + + if(read(fd, buf, SARMAG) != SARMAG){ + close(fd); + return; + } + while(readn(fd, (char *)&h, sizeof(h)) == sizeof(h)){ + t = atol(h.date); + if(t == 0) /* as it sometimes happens; thanks ken */ + t = 1; + namelen = 0; + if(memcmp(h.name, "#1/", 3) == 0){ /* BSD */ + namelen = atoi(h.name+3); + if(namelen >= sizeof name){ + namelen = 0; + goto skip; + } + if(readn(fd, name, namelen) != namelen) + break; + name[namelen] = 0; + }else if(memcmp(h.name, "// ", 2) == 0){ /* GNU */ + /* date, uid, gid, mode all ' ' */ + for(i=2; i<16+12+6+6+8; i++) + if(h.name[i] != ' ') + goto skip; + t = atol(h.size); + if(t&01) + t++; + free(strings); + strings = malloc(t+1); + if(strings){ + if(readn(fd, strings, t) != t){ + free(strings); + strings = nil; + break; + } + strings[t] = 0; + continue; + } + goto skip; + }else if(strings && h.name[0]=='/' && isdigit((uchar)h.name[1])){ + i = strtol(h.name+1, &p, 10); + if(*p != ' ' || i >= strlen(strings)) + goto skip; + p = strings+i; + for(; *p && *p != '/'; p++) + ; + namelen = p-(strings+i); + if(namelen >= sizeof name){ + namelen = 0; + goto skip; + } + memmove(name, strings+i, namelen); + name[namelen] = 0; + namelen = 0; + }else{ + strncpy(name, h.name, sizeof(h.name)); + for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--) + ; + if(name[i] == '/') /* system V bug */ + i--; + name[i+1]=0; + } + snprint(buf, sizeof buf, "%s(%s)", ar, name); + sym = symlook(strdup(buf), S_TIME, (void *)t); + sym->u.value = t; + skip: + t = atol(h.size); + if(t&01) t++; + t -= namelen; + LSEEK(fd, t, 1); + } + close(fd); + free(strings); +} + +static int +type(char *file) +{ + int fd; + char buf[SARMAG]; + + fd = open(file, OREAD); + if(fd < 0){ + if(symlook(file, S_BITCH, 0) == 0){ + if(strlen(file) < 2 || strcmp(file+strlen(file)-2, ".a") != 0) + Bprint(&bout, "%s doesn't exist: assuming it will be an archive\n", file); + symlook(file, S_BITCH, (void *)file); + } + return 1; + } + if(read(fd, buf, SARMAG) != SARMAG){ + close(fd); + return 0; + } + close(fd); + return !strncmp(ARMAG, buf, SARMAG); +} + +static char* +split(char *name, char **member) +{ + char *p, *q; + + p = strdup(name); + q = utfrune(p, '('); + if(q){ + *q++ = 0; + if(member) + *member = q; + q = utfrune(q, ')'); + if (q) + *q = 0; + if(type(p)) + return p; + free(p); + fprint(2, "mk: '%s' is not an archive\n", name); + } + return 0; +} diff --git a/mk/bufblock.c b/mk/bufblock.c @@ -0,0 +1,88 @@ +#include "mk.h" + +static Bufblock *freelist; +#define QUANTA 4096 + +Bufblock * +newbuf(void) +{ + Bufblock *p; + + if (freelist) { + p = freelist; + freelist = freelist->next; + } else { + p = (Bufblock *) Malloc(sizeof(Bufblock)); + p->start = Malloc(QUANTA*sizeof(*p->start)); + p->end = p->start+QUANTA; + } + p->current = p->start; + *p->start = 0; + p->next = 0; + return p; +} + +void +freebuf(Bufblock *p) +{ + p->next = freelist; + freelist = p; +} + +void +growbuf(Bufblock *p) +{ + int n; + Bufblock *f; + char *cp; + + n = p->end-p->start+QUANTA; + /* search the free list for a big buffer */ + for (f = freelist; f; f = f->next) { + if (f->end-f->start >= n) { + memcpy(f->start, p->start, p->end-p->start); + cp = f->start; + f->start = p->start; + p->start = cp; + cp = f->end; + f->end = p->end; + p->end = cp; + f->current = f->start; + break; + } + } + if (!f) { /* not found - grow it */ + p->start = Realloc(p->start, n); + p->end = p->start+n; + } + p->current = p->start+n-QUANTA; +} + +void +bufcpy(Bufblock *buf, char *cp, int n) +{ + + while (n--) + insert(buf, *cp++); +} + +void +insert(Bufblock *buf, int c) +{ + + if (buf->current >= buf->end) + growbuf(buf); + *buf->current++ = c; +} + +void +rinsert(Bufblock *buf, Rune r) +{ + int n; + + n = runelen(r); + if (buf->current+n > buf->end) + growbuf(buf); + runetochar(buf->current, &r); + buf->current += n; +} diff --git a/mk/env.c b/mk/env.c @@ -0,0 +1,149 @@ +#include "mk.h" + +enum { + ENVQUANTA=10 +}; + +Envy *envy; +static int nextv; + +static char *myenv[] = +{ + "target", + "stem", + "prereq", + "pid", + "nproc", + "newprereq", + "alltarget", + "newmember", + "stem0", /* must be in order from here */ + "stem1", + "stem2", + "stem3", + "stem4", + "stem5", + "stem6", + "stem7", + "stem8", + "stem9", + 0 +}; + +void +initenv(void) +{ + char **p; + + for(p = myenv; *p; p++) + symlook(*p, S_INTERNAL, (void *)""); + readenv(); /* o.s. dependent */ +} + +static void +envinsert(char *name, Word *value) +{ + static int envsize; + + if (nextv >= envsize) { + envsize += ENVQUANTA; + envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy)); + } + envy[nextv].name = name; + envy[nextv++].values = value; +} + +static void +envupd(char *name, Word *value) +{ + Envy *e; + + for(e = envy; e->name; e++) + if(strcmp(name, e->name) == 0){ + delword(e->values); + e->values = value; + return; + } + e->name = name; + e->values = value; + envinsert(0,0); +} + +static void +ecopy(Symtab *s) +{ + char **p; + + if(symlook(s->name, S_NOEXPORT, 0)) + return; + for(p = myenv; *p; p++) + if(strcmp(*p, s->name) == 0) + return; + envinsert(s->name, s->u.ptr); +} + +void +execinit(void) +{ + char **p; + + nextv = 0; + for(p = myenv; *p; p++) + envinsert(*p, stow("")); + + symtraverse(S_VAR, ecopy); + envinsert(0, 0); +} + +Envy* +buildenv(Job *j, int slot) +{ + char **p, *cp, *qp; + Word *w, *v, **l; + int i; + char buf[256]; + + envupd("target", wdup(j->t)); + if(j->r->attr&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 cache */ + return t; +} + +void +touch(char *name) +{ + Bprint(&bout, "touch(%s)\n", name); + if(nflag) + return; + + if(utfrune(name, '(')) + atouch(name); /* archive */ + else if(chgtime(name) < 0) { + fprint(2, "%s: %r\n", name); + Exit(); + } +} + +void +delete(char *name) +{ + if(utfrune(name, '(') == 0) { /* file */ + if(remove(name) < 0) + fprint(2, "remove %s: %r\n", name); + } else + fprint(2, "hoon off; mk can'tdelete archive members\n"); +} + +void +timeinit(char *s) +{ + long t; + char *cp; + Rune r; + int c, n; + + t = time(0); + while (*s) { + cp = s; + do{ + n = chartorune(&r, s); + if (r == ' ' || r == ',' || r == '\n') + break; + s += n; + } while(*s); + c = *s; + *s = 0; + symlook(strdup(cp), S_TIME, (void *)t)->u.value = t; + if (c) + *s++ = c; + while(*s){ + n = chartorune(&r, s); + if(r != ' ' && r != ',' && r != '\n') + break; + s += n; + } + } +} diff --git a/mk/fns.h b/mk/fns.h @@ -0,0 +1,88 @@ +#undef waitfor +#define waitfor mkwaitfor + +void addrule(char*, Word*, char*, Word*, int, int, char*); +void addrules(Word*, Word*, char*, int, int, char*); +void addw(Word*, char*); +void assert(char*, int); +int assline(Biobuf *, Bufblock *); +long atimeof(int,char*); +void atouch(char*); +void bufcpy(Bufblock *, char *, int); +Envy *buildenv(Job*, int); +void catchnotes(void); +int chgtime(char*); +void clrmade(Node*); +void delete(char*); +void delword(Word*); +int dorecipe(Node*); +void dumpa(char*, Arc*); +void dumpj(char*, Job*, int); +void dumpn(char*, Node*); +void dumpr(char*, Rule*); +void dumpv(char*); +void dumpw(char*, Word*); +void execinit(void); +int execsh(char*, char*, Bufblock*, Envy*, Shell*, Word*); +void Exit(void); +void expunge(int, char*); +void freebuf(Bufblock*); +void front(char*); +Node *graph(char*); +void growbuf(Bufblock *); +void initenv(void); +void initshell(void); +void insert(Bufblock *, int); +void ipop(void); +void ipush(void); +void killchildren(char*); +void *Malloc(int); +char *maketmp(int*); +int match(char*, char*, char*, Shell*); +char *membername(char*, int, char*); +void mk(char*); +unsigned long mkmtime(char*); +long mtime(char*); +Arc *newarc(Node*, Rule*, char*, Resub*); +Bufblock *newbuf(void); +Job *newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*); +Word *newword(char*); +int nextrune(Biobuf*, int); +int nextslot(void); +void nproc(void); +void nrep(void); +int outofdate(Node*, Arc*, int); +void parse(char*, int, int); +int pipecmd(char*, Envy*, int*, Shell*, Word*); +void popshell(void); +void prusage(void); +void pushshell(void); +void rcopy(char**, Resub*, int); +void readenv(void); +void *Realloc(void*, int); +void rinsert(Bufblock *, Rune); +char *rulecnt(void); +void run(Job*); +char *setshell(Word*); +void setvar(char*, void*); +int shargv(Word*, int, char***); +char *shname(char*); +void shprint(char*, Envy*, Bufblock*, Shell*); +Word *stow(char*); +void subst(char*, char*, char*); +void symdel(char*, int); +void syminit(void); +Symtab *symlook(char*, int, void*); +void symstat(void); +void symtraverse(int, void(*)(Symtab*)); +void timeinit(char*); +long timeof(char*, int); +void touch(char*); +void update(int, Node*); +void usage(void); +Word *varsub(char**); +int waitfor(char*); +int waitup(int, int*); +Word *wdup(Word*); +int work(Node*, Node*, Arc*); +char *wtos(Word*, int); diff --git a/mk/graph.c b/mk/graph.c @@ -0,0 +1,279 @@ +#include "mk.h" + +static Node *applyrules(char *, char *); +static void togo(Node *); +static int vacuous(Node *); +static Node *newnode(char *); +static void trace(char *, Arc *); +static void cyclechk(Node *); +static void ambiguous(Node *); +static void attribute(Node *); + +Node * +graph(char *target) +{ + Node *node; + char *cnt; + + cnt = rulecnt(); + node = applyrules(target, cnt); + free(cnt); + cyclechk(node); + node->flags |= PROBABLE; /* make sure it doesn't get deleted */ + vacuous(node); + ambiguous(node); + attribute(node); + return(node); +} + +static Node * +applyrules(char *target, char *cnt) +{ + Symtab *sym; + Node *node; + Rule *r; + Arc head, *a = &head; + Word *w; + char stem[NAMEBLOCK], buf[NAMEBLOCK]; + Resub rmatch[NREGEXP]; + +/* print("applyrules(%lux='%s')\n", target, target); */ + sym = symlook(target, S_NODE, 0); + if(sym) + return sym->u.ptr; + target = strdup(target); + node = newnode(target); + head.n = 0; + head.next = 0; + sym = symlook(target, S_TARGET, 0); + memset((char*)rmatch, 0, sizeof(rmatch)); + for(r = sym? sym->u.ptr:0; r; r = r->chain){ + if(r->attr&META) continue; + if(strcmp(target, r->target)) continue; + if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */ + if(cnt[r->rule] >= nreps) continue; + cnt[r->rule]++; + node->flags |= PROBABLE; + +/* if(r->attr&VIR) + * node->flags |= VIRTUAL; + * if(r->attr&NOREC) + * node->flags |= NORECIPE; + * if(r->attr&DEL) + * node->flags |= DELETE; + */ + if(!r->tail || !r->tail->s || !*r->tail->s) { + a->next = newarc((Node *)0, r, "", rmatch); + a = a->next; + } else + for(w = r->tail; w; w = w->next){ + a->next = newarc(applyrules(w->s, cnt), r, "", rmatch); + a = a->next; + } + cnt[r->rule]--; + head.n = node; + } + for(r = metarules; r; r = r->next){ + if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */ + if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR)) + continue; + if(r->attr&REGEXP){ + stem[0] = 0; + patrule = r; + memset((char*)rmatch, 0, sizeof(rmatch)); + if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0) + continue; + } else { + if(!match(node->name, r->target, stem, r->shellt)) continue; + } + if(cnt[r->rule] >= nreps) continue; + cnt[r->rule]++; + +/* if(r->attr&VIR) + * node->flags |= VIRTUAL; + * if(r->attr&NOREC) + * node->flags |= NORECIPE; + * if(r->attr&DEL) + * node->flags |= DELETE; + */ + + if(!r->tail || !r->tail->s || !*r->tail->s) { + a->next = newarc((Node *)0, r, stem, rmatch); + a = a->next; + } else + for(w = r->tail; w; w = w->next){ + if(r->attr&REGEXP) + regsub(w->s, buf, sizeof buf, rmatch, NREGEXP); + else + subst(stem, w->s, buf); + a->next = newarc(applyrules(buf, cnt), r, stem, rmatch); + a = a->next; + } + cnt[r->rule]--; + } + a->next = node->prereqs; + node->prereqs = head.next; + return(node); +} + +static void +togo(Node *node) +{ + Arc *la, *a; + + /* delete them now */ + la = 0; + for(a = node->prereqs; a; la = a, a = a->next) + if(a->flag&TOGO){ + if(a == node->prereqs) + node->prereqs = a->next; + else + la->next = a->next, a = la; + } +} + +static int +vacuous(Node *node) +{ + Arc *la, *a; + int vac = !(node->flags&PROBABLE); + + if(node->flags&READY) + return(node->flags&VACUOUS); + node->flags |= READY; + for(a = node->prereqs; a; a = a->next) + if(a->n && vacuous(a->n) && (a->r->attr&META)) + a->flag |= TOGO; + else + vac = 0; + /* if a rule generated arcs that DON'T go; no others from that rule go */ + for(a = node->prereqs; a; a = a->next) + if((a->flag&TOGO) == 0) + for(la = node->prereqs; la; la = la->next) + if((la->flag&TOGO) && (la->r == a->r)){ + la->flag &= ~TOGO; + } + togo(node); + if(vac) + node->flags |= VACUOUS; + return(vac); +} + +static Node * +newnode(char *name) +{ + register Node *node; + + node = (Node *)Malloc(sizeof(Node)); + symlook(name, S_NODE, (void *)node); + node->name = name; + node->time = timeof(name, 0); + node->prereqs = 0; + node->flags = node->time? PROBABLE : 0; + node->next = 0; + return(node); +} + +void +dumpn(char *s, Node *n) +{ + char buf[1024]; + Arc *a; + + snprint(buf, sizeof buf, "%s ", (*s == ' ')? s:""); + Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n", + s, n->name, n, n->time, n->flags, n->next); + for(a = n->prereqs; a; a = a->next) + dumpa(buf, a); +} + +static void +trace(char *s, Arc *a) +{ + fprint(2, "\t%s", s); + while(a){ + fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line, + a->n? a->n->name:""); + if(a->n){ + for(a = a->n->prereqs; a; a = a->next) + if(*a->r->recipe) break; + } else + a = 0; + } + fprint(2, "\n"); +} + +static void +cyclechk(Node *n) +{ + Arc *a; + + if((n->flags&CYCLE) && n->prereqs){ + fprint(2, "mk: cycle in graph detected at target %s\n", n->name); + Exit(); + } + n->flags |= CYCLE; + for(a = n->prereqs; a; a = a->next) + if(a->n) + cyclechk(a->n); + n->flags &= ~CYCLE; +} + +static void +ambiguous(Node *n) +{ + Arc *a; + Rule *r = 0; + Arc *la; + int bad = 0; + + la = 0; + for(a = n->prereqs; a; a = a->next){ + if(a->n) + ambiguous(a->n); + if(*a->r->recipe == 0) continue; + if(r == 0) + r = a->r, la = a; + else{ + if(r->recipe != a->r->recipe){ + if((r->attr&META) && !(a->r->attr&META)){ + la->flag |= TOGO; + r = a->r, la = a; + } else if(!(r->attr&META) && (a->r->attr&META)){ + a->flag |= TOGO; + continue; + } + } + if(r->recipe != a->r->recipe){ + if(bad == 0){ + fprint(2, "mk: ambiguous recipes for %s:\n", n->name); + bad = 1; + trace(n->name, la); + } + trace(n->name, a); + } + } + } + if(bad) + Exit(); + togo(n); +} + +static void +attribute(Node *n) +{ + register Arc *a; + + for(a = n->prereqs; a; a = a->next){ + if(a->r->attr&VIR) + n->flags |= VIRTUAL; + if(a->r->attr&NOREC) + n->flags |= NORECIPE; + if(a->r->attr&DEL) + n->flags |= DELETE; + if(a->n) + attribute(a->n); + } + if(n->flags&VIRTUAL) + n->time = 0; +} diff --git a/mk/job.c b/mk/job.c @@ -0,0 +1,33 @@ +#include "mk.h" + +Job * +newjob(Rule *r, Node *nlist, char *stem, char **match, Word *pre, Word *npre, Word *tar, Word *atar) +{ + register Job *j; + + j = (Job *)Malloc(sizeof(Job)); + j->r = r; + j->n = nlist; + j->stem = stem; + j->match = match; + j->p = pre; + j->np = npre; + j->t = tar; + j->at = atar; + j->nproc = -1; + j->next = 0; + return(j); +} + +void +dumpj(char *s, Job *j, int all) +{ + Bprint(&bout, "%s\n", s); + while(j){ + Bprint(&bout, "job@%ld: r=%ld n=%ld stem='%s' nproc=%d\n", + j, j->r, j->n, j->stem, j->nproc); + Bprint(&bout, "\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n", + wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wtos(j->np, ' ')); + j = all? j->next : 0; + } +} diff --git a/mk/lex.c b/mk/lex.c @@ -0,0 +1,146 @@ +#include "mk.h" + +static int bquote(Biobuf*, Bufblock*); + +/* + * Assemble a line skipping blank lines, comments, and eliding + * escaped newlines + */ +int +assline(Biobuf *bp, Bufblock *buf) +{ + int c; + int lastc; + + buf->current=buf->start; + while ((c = nextrune(bp, 1)) >= 0){ + switch(c) + { + case '\r': /* consumes CRs for Win95 */ + continue; + case '\n': + if (buf->current != buf->start) { + insert(buf, 0); + return 1; + } + break; /* skip empty lines */ + case '\\': + case '\'': + case '"': + rinsert(buf, c); + if (shellt->escapetoken(bp, buf, 1, c) == 0) + Exit(); + break; + case '`': + if (bquote(bp, buf) == 0) + Exit(); + break; + case '#': + lastc = '#'; + while ((c = Bgetc(bp)) != '\n') { + if (c < 0) + goto eof; + if(c != '\r') + lastc = c; + } + mkinline++; + if (lastc == '\\') + break; /* propagate escaped newlines??*/ + if (buf->current != buf->start) { + insert(buf, 0); + return 1; + } + break; + default: + rinsert(buf, c); + break; + } + } +eof: + insert(buf, 0); + return *buf->start != 0; +} + +/* + * assemble a back-quoted shell command into a buffer + */ +static int +bquote(Biobuf *bp, Bufblock *buf) +{ + int c, line, term; + int start; + + line = mkinline; + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + if(c == '{'){ + term = '}'; /* rc style */ + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + } else + term = '`'; /* sh style */ + + start = buf->current-buf->start; + for(;c > 0; c = nextrune(bp, 0)){ + if(c == term){ + insert(buf, '\n'); + insert(buf,0); + buf->current = buf->start+start; + execinit(); + execsh(0, buf->current, buf, envy, shellt, shellcmd); + return 1; + } + if(c == '\n') + break; + if(c == '\'' || c == '"' || c == '\\'){ + insert(buf, c); + if(!shellt->escapetoken(bp, buf, 1, c)) + return 0; + continue; + } + rinsert(buf, c); + } + SYNERR(line); + fprint(2, "missing closing %c after `\n", term); + return 0; +} + +/* + * get next character stripping escaped newlines + * the flag specifies whether escaped newlines are to be elided or + * replaced with a blank. + */ +int +nextrune(Biobuf *bp, int elide) +{ + int c, c2; + static int savec; + + if(savec){ + c = savec; + savec = 0; + return c; + } + + for (;;) { + c = Bgetrune(bp); + if (c == '\\') { + c2 = Bgetrune(bp); + if(c2 == '\r'){ + savec = c2; + c2 = Bgetrune(bp); + } + if (c2 == '\n') { + savec = 0; + mkinline++; + if (elide) + continue; + return ' '; + } + Bungetrune(bp); + } + if (c == '\n') + mkinline++; + return c; + } +} diff --git a/mk/main.c b/mk/main.c @@ -0,0 +1,287 @@ +#include "mk.h" + +#define MKFILE "mkfile" + +int debug; +Rule *rules, *metarules; +int nflag = 0; +int tflag = 0; +int iflag = 0; +int kflag = 0; +int aflag = 0; +int uflag = 0; +char *explain = 0; +Word *target1; +int nreps = 1; +Job *jobs; +Biobuf bout; +Rule *patrule; +void badusage(void); +#ifdef PROF +short buf[10000]; +#endif + +int +main(int argc, char **argv) +{ + Word *w; + char *s, *temp; + char *files[256], **f = files, **ff; + int sflag = 0; + int i; + int tfd = -1; + Biobuf tb; + Bufblock *buf; + Bufblock *whatif; + + /* + * start with a copy of the current environment variables + * instead of sharing them + */ + + Binit(&bout, 1, OWRITE); + buf = newbuf(); + whatif = 0; + USED(argc); + for(argv++; *argv && (**argv == '-'); argv++) + { + bufcpy(buf, argv[0], strlen(argv[0])); + insert(buf, ' '); + switch(argv[0][1]) + { + case 'a': + aflag = 1; + break; + case 'd': + if(*(s = &argv[0][2])) + while(*s) switch(*s++) + { + case 'p': debug |= D_PARSE; break; + case 'g': debug |= D_GRAPH; break; + case 'e': debug |= D_EXEC; break; + } + else + debug = 0xFFFF; + break; + case 'e': + explain = &argv[0][2]; + break; + case 'f': + if(*++argv == 0) + badusage(); + *f++ = *argv; + bufcpy(buf, argv[0], strlen(argv[0])); + insert(buf, ' '); + break; + case 'i': + iflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'w': + if(whatif == 0) + whatif = newbuf(); + else + insert(whatif, ' '); + if(argv[0][2]) + bufcpy(whatif, &argv[0][2], strlen(&argv[0][2])); + else { + if(*++argv == 0) + badusage(); + bufcpy(whatif, &argv[0][0], strlen(&argv[0][0])); + } + break; + default: + badusage(); + } + } +#ifdef PROF + { + extern etext(); + monitor(main, etext, buf, sizeof buf, 300); + } +#endif + + if(aflag) + iflag = 1; + usage(); + syminit(); + initshell(); + initenv(); + usage(); + + /* + assignment args become null strings + */ + temp = 0; + for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){ + bufcpy(buf, argv[i], strlen(argv[i])); + insert(buf, ' '); + if(tfd < 0){ + temp = maketmp(&tfd); + if(temp == 0) { + fprint(2, "temp file: %r\n"); + Exit(); + } + Binit(&tb, tfd, OWRITE); + } + Bprint(&tb, "%s\n", argv[i]); + *argv[i] = 0; + } + if(tfd >= 0){ + Bflush(&tb); + LSEEK(tfd, 0L, 0); + parse("command line args", tfd, 1); + remove(temp); + } + + if (buf->current != buf->start) { + buf->current--; + insert(buf, 0); + } + symlook("MKFLAGS", S_VAR, (void *) stow(buf->start)); + buf->current = buf->start; + for(i = 0; argv[i]; i++){ + if(*argv[i] == 0) continue; + if(i) + insert(buf, ' '); + bufcpy(buf, argv[i], strlen(argv[i])); + } + insert(buf, 0); + symlook("MKARGS", S_VAR, (void *) stow(buf->start)); + freebuf(buf); + + if(f == files){ + if(access(MKFILE, 4) == 0) + parse(MKFILE, open(MKFILE, 0), 0); + } else + for(ff = files; ff < f; ff++) + parse(*ff, open(*ff, 0), 0); + if(DEBUG(D_PARSE)){ + dumpw("default targets", target1); + dumpr("rules", rules); + dumpr("metarules", metarules); + dumpv("variables"); + } + if(whatif){ + insert(whatif, 0); + timeinit(whatif->start); + freebuf(whatif); + } + execinit(); + /* skip assignment args */ + while(*argv && (**argv == 0)) + argv++; + + catchnotes(); + if(*argv == 0){ + if(target1) + for(w = target1; w; w = w->next) + mk(w->s); + else { + fprint(2, "mk: nothing to mk\n"); + Exit(); + } + } else { + if(sflag){ + for(; *argv; argv++) + if(**argv) + mk(*argv); + } else { + Word *head, *tail, *t; + + /* fake a new rule with all the args as prereqs */ + tail = 0; + t = 0; + for(; *argv; argv++) + if(**argv){ + if(tail == 0) + tail = t = newword(*argv); + else { + t->next = newword(*argv); + t = t->next; + } + } + if(tail->next == 0) + mk(tail->s); + else { + head = newword("command line arguments"); + addrules(head, tail, strdup(""), VIR, mkinline, 0); + mk(head->s); + } + } + } + if(uflag) + prusage(); + exits(0); + return 0; +} + +void +badusage(void) +{ + + fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n"); + Exit(); +} + +void * +Malloc(int n) +{ + register void *s; + + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void * +Realloc(void *s, int n) +{ + if(s) + s = realloc(s, n); + else + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void +assert(char *s, int n) +{ + if(!n){ + fprint(2, "mk: Assertion ``%s'' failed.\n", s); + Exit(); + } +} + +void +regerror(char *s) +{ + if(patrule) + fprint(2, "mk: %s:%d: regular expression error; %s\n", + patrule->file, patrule->line, s); + else + fprint(2, "mk: %s:%d: regular expression error; %s\n", + infile, mkinline, s); + Exit(); +} diff --git a/mk/match.c b/mk/match.c @@ -0,0 +1,49 @@ +#include "mk.h" + +int +match(char *name, char *template, char *stem, Shell *sh) +{ + Rune r; + int n; + + while(*name && *template){ + n = chartorune(&r, template); + if (PERCENT(r)) + break; + while (n--) + if(*name++ != *template++) + return 0; + } + if(!PERCENT(*template)) + return 0; + n = strlen(name)-strlen(template+1); + if (n < 0) + return 0; + if (strcmp(template+1, name+n)) + return 0; + strncpy(stem, name, n); + stem[n] = 0; + if(*template == '&') + return !sh->charin(stem, "./"); + return 1; +} + +void +subst(char *stem, char *template, char *dest) +{ + Rune r; + char *s; + int n; + + while(*template){ + n = chartorune(&r, template); + if (PERCENT(r)) { + template += n; + for (s = stem; *s; s++) + *dest++ = *s; + } else + while (n--) + *dest++ = *template++; + } + *dest = 0; +} diff --git a/mk/mk.1 b/mk/mk.1 @@ -0,0 +1,691 @@ +.TH MK 1 +.SH NAME +mk \- maintain (make) related files +.SH SYNOPSIS +.B mk +[ +.B -f +.I mkfile +] ... +[ +.I option ... +] +[ +.I target ... +] +.SH DESCRIPTION +.I Mk +uses the dependency rules specified in +.I mkfile +to control the update (usually by compilation) of +.I targets +(usually files) +from the source files upon which they depend. +The +.I mkfile +(default +.LR mkfile ) +contains a +.I rule +for each target that identifies the files and other +targets upon which it depends and an +.IR sh (1) +script, a +.IR recipe , +to update the target. +The script is run if the target does not exist +or if it is older than any of the files it depends on. +.I Mkfile +may also contain +.I meta-rules +that define actions for updating implicit targets. +If no +.I target +is specified, the target of the first rule (not meta-rule) in +.I mkfile +is updated. +.PP +The environment variable +.B $NPROC +determines how many targets may be updated simultaneously; +Some operating systems, e.g., Plan 9, set +.B $NPROC +automatically to the number of CPUs on the current machine. +.PP +Options are: +.TP \w'\fL-d[egp]\ 'u +.B -a +Assume all targets to be out of date. +Thus, everything is updated. +.PD 0 +.TP +.BR -d [ egp ] +Produce debugging output +.RB ( p +is for parsing, +.B g +for graph building, +.B e +for execution). +.TP +.B -e +Explain why each target is made. +.TP +.B -i +Force any missing intermediate targets to be made. +.TP +.B -k +Do as much work as possible in the face of errors. +.TP +.B -n +Print, but do not execute, the commands +needed to update the targets. +.TP +.B -s +Make the command line arguments sequentially rather than in parallel. +.TP +.B -t +Touch (update the modified date of) file targets, without +executing any recipes. +.TP +.BI -w target1 , target2,... +Pretend the modify time for each +.I target +is the current time; useful in conjunction with +.B -n +to learn what updates would be triggered by +modifying the +.IR targets . +.PD +.SS The \fLmkfile\fP +A +.I mkfile +consists of +.I assignments +(described under `Environment') and +.IR rules . +A rule contains +.I targets +and a +.IR tail . +A target is a literal string +and is normally a file name. +The tail contains zero or more +.I prerequisites +and an optional +.IR recipe , +which is an +.B shell +script. +Each line of the recipe must begin with white space. +A rule takes the form +.IP +.EX +target: prereq1 prereq2 + \f2recipe using\fP prereq1, prereq2 \f2to build\fP target +.EE +.PP +When the recipe is executed, +the first character on every line is elided. +.PP +After the colon on the target line, a rule may specify +.IR attributes , +described below. +.PP +A +.I meta-rule +has a target of the form +.IB A % B +where +.I A +and +.I B +are (possibly empty) strings. +A meta-rule acts as a rule for any potential target whose +name matches +.IB A % B +with +.B % +replaced by an arbitrary string, called the +.IR stem . +In interpreting a meta-rule, +the stem is substituted for all occurrences of +.B % +in the prerequisite names. +In the recipe of a meta-rule, the environment variable +.B $stem +contains the string matched by the +.BR % . +For example, a meta-rule to compile a C program using +.IR 9c (1) +might be: +.IP +.EX +%: %.c + 9c -c $stem.c + 9l -o $stem $stem.o +.EE +.PP +Meta-rules may contain an ampersand +.B & +rather than a percent sign +.BR % . +A +.B % +matches a maximal length string of any characters; +an +.B & +matches a maximal length string of any characters except period +or slash. +.PP +The text of the +.I mkfile +is processed as follows. +Lines beginning with +.B < +followed by a file name are replaced by the contents of the named +file. +Lines beginning with +.B "<|" +followed by a file name are replaced by the output +of the execution of the named +file. +Blank lines and comments, which run from unquoted +.B # +characters to the following newline, are deleted. +The character sequence backslash-newline is deleted, +so long lines in +.I mkfile +may be folded. +Non-recipe lines are processed by substituting for +.BI `{ command } +the output of the +.I command +when run by +.IR sh . +References to variables are replaced by the variables' values. +Special characters may be quoted using single quotes +.BR \&'' +as in +.IR sh (1). +.PP +Assignments and rules are distinguished by +the first unquoted occurrence of +.B : +(rule) +or +.B = +(assignment). +.PP +A later rule may modify or override an existing rule under the +following conditions: +.TP +\- +If the targets of the rules exactly match and one rule +contains only a prerequisite clause and no recipe, the +clause is added to the prerequisites of the other rule. +If either or both targets are virtual, the recipe is +always executed. +.TP +\- +If the targets of the rules match exactly and the +prerequisites do not match and both rules +contain recipes, +.I mk +reports an ``ambiguous recipe'' error. +.TP +\- +If the target and prerequisites of both rules match exactly, +the second rule overrides the first. +.SS Environment +Rules may make use of +shell +environment variables. +A legal reference of the form +.B $OBJ +or +.B ${name} +is expanded as in +.IR sh (1). +A reference of the form +.BI ${name: A % B = C\fL%\fID\fL}\fR, +where +.I A, B, C, D +are (possibly empty) strings, +has the value formed by expanding +.B $name +and substituting +.I C +for +.I A +and +.I D +for +.I B +in each word in +.B $name +that matches pattern +.IB A % B\f1. +.PP +Variables can be set by +assignments of the form +.I + var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR +.br +Blanks in the +.I value +break it into words. +Such variables are exported +to the environment of +recipes as they are executed, unless +.BR U , +the only legal attribute +.IR attr , +is present. +The initial value of a variable is +taken from (in increasing order of precedence) +the default values below, +.I mk's +environment, the +.IR mkfiles , +and any command line assignment as an argument to +.IR mk . +A variable assignment argument overrides the first (but not any subsequent) +assignment to that variable. +.PP +The variable +.B MKFLAGS +contains all the option arguments (arguments starting with +.L - +or containing +.LR = ) +and +.B MKARGS +contains all the targets in the call to +.IR mk . +.PP +The variable +.B MKSHELL +contains the shell command line +.I mk +uses to run recipes. +If the first word of the command ends in +.B rc +or +.BR rcsh , +.I mk +uses +.IR rc (1)'s +quoting rules; otherwise it uses +.IR sh (1)'s. +The +.B MKSHELL +variable is consulted when the mkfile is read, not when it is executed, +so that different shells can be used within a single mkfile: +.IP +.EX +MKSHELL=$PLAN9/bin/rc +use-rc:V: + for(i in a b c) echo $i + +MKSHELL=sh +use-sh:V: + for i in a b c; do echo $i; done +.EE +.LP +Mkfiles included via +.B < +or +.B <| +.RI ( q.v. ) +see their own private copy of +.BR MKSHELL , +which always starts set to +.B sh . +.PP +Dynamic information may be included in the mkfile by using a line of the form +.IP +\fR<|\fIcommand\fR \fIargs\fR +.LP +This runs the command +.I command +with the given arguments +.I args +and pipes its standard output to +.I mk +to be included as part of the mkfile. For instance, the Inferno kernels +use this technique +to run a shell command with an awk script and a configuration +file as arguments in order for +the +.I awk +script to process the file and output a set of variables and their values. +.SS Execution +.PP +During execution, +.I mk +determines which targets must be updated, and in what order, +to build the +.I names +specified on the command line. +It then runs the associated recipes. +.PP +A target is considered up to date if it has no prerequisites or +if all its prerequisites are up to date and it is newer +than all its prerequisites. +Once the recipe for a target has executed, the target is +considered up to date. +.PP +The date stamp +used to determine if a target is up to date is computed +differently for different types of targets. +If a target is +.I virtual +(the target of a rule with the +.B V +attribute), +its date stamp is initially zero; when the target is +updated the date stamp is set to +the most recent date stamp of its prerequisites. +Otherwise, if a target does not exist as a file, +its date stamp is set to the most recent date stamp of its prerequisites, +or zero if it has no prerequisites. +Otherwise, the target is the name of a file and +the target's date stamp is always that file's modification date. +The date stamp is computed when the target is needed in +the execution of a rule; it is not a static value. +.PP +Nonexistent targets that have prerequisites +and are themselves prerequisites are treated specially. +Such a target +.I t +is given the date stamp of its most recent prerequisite +and if this causes all the targets which have +.I t +as a prerequisite to be up to date, +.I t +is considered up to date. +Otherwise, +.I t +is made in the normal fashion. +The +.B -i +flag overrides this special treatment. +.PP +Files may be made in any order that respects +the preceding restrictions. +.PP +A recipe is executed by supplying the recipe as standard input to +the command +.BR /bin/sh . +(Note that unlike +.IR make , +.I mk +feeds the entire recipe to the shell rather than running each line +of the recipe separately.) +The environment is augmented by the following variables: +.TP 14 +.B $alltarget +all the targets of this rule. +.TP +.B $newprereq +the prerequisites that caused this rule to execute. +.TP +.B $newmember +the prerequisites that are members of an aggregate +that caused this rule to execute. +When the prerequisites of a rule are members of an +aggregate, +.B $newprereq +contains the name of the aggregate and out of date +members, while +.B $newmember +contains only the name of the members. +.TP +.B $nproc +the process slot for this recipe. +It satisfies +.RB 0≤ $nproc < $NPROC . +.TP +.B $pid +the process id for the +.I mk +executing the recipe. +.TP +.B $prereq +all the prerequisites for this rule. +.TP +.B $stem +if this is a meta-rule, +.B $stem +is the string that matched +.B % +or +.BR & . +Otherwise, it is empty. +For regular expression meta-rules (see below), the variables +.LR stem0 ", ...," +.L stem9 +are set to the corresponding subexpressions. +.TP +.B $target +the targets for this rule that need to be remade. +.PP +These variables are available only during the execution of a recipe, +not while evaluating the +.IR mkfile . +.PP +Unless the rule has the +.B Q +attribute, +the recipe is printed prior to execution +with recognizable environment variables expanded. +Commands returning error status +cause +.I mk +to terminate. +.PP +Recipes and backquoted +.B rc +commands in places such as assignments +execute in a copy of +.I mk's +environment; changes they make to +environment variables are not visible from +.IR mk . +.PP +Variable substitution in a rule is done when +the rule is read; variable substitution in the recipe is done +when the recipe is executed. For example: +.IP +.EX +bar=a.c +foo: $bar + $CC -o foo $bar +bar=b.c +.EE +.PP +will compile +.B b.c +into +.BR foo , +if +.B a.c +is newer than +.BR foo . +.SS Aggregates +Names of the form +.IR a ( b ) +refer to member +.I b +of the aggregate +.IR a . +Currently, the only aggregates supported are +.I 9ar +(see +.IR 9c (1)) +archives. +.SS Attributes +The colon separating the target from the prerequisites +may be +immediately followed by +.I attributes +and another colon. +The attributes are: +.TP +.B D +If the recipe exits with a non-null status, the target is deleted. +.TP +.B E +Continue execution if the recipe draws errors. +.TP +.B N +If there is no recipe, the target has its time updated. +.TP +.B n +The rule is a meta-rule that cannot be a target of a virtual rule. +Only files match the pattern in the target. +.TP +.B P +The characters after the +.B P +until the terminating +.B : +are taken as a program name. +It will be invoked as +.B "sh -c prog 'arg1' 'arg2'" +and should return a zero exit status +if and only if arg1 is up to date with respect to arg2. +Date stamps are still propagated in the normal way. +.TP +.B Q +The recipe is not printed prior to execution. +.TP +.B R +The rule is a meta-rule using regular expressions. +In the rule, +.B % +has no special meaning. +The target is interpreted as a regular expression as defined in +.IR regexp (7). +The prerequisites may contain references +to subexpressions in form +.BI \e n\f1, +as in the substitute command of +.IR sed (1). +.TP +.B U +The targets are considered to have been updated +even if the recipe did not do so. +.TP +.B V +The targets of this rule are marked as virtual. +They are distinct from files of the same name. +.PD +.SH EXAMPLES +A simple mkfile to compile a program: +.IP +.EX +.ta 8n +8n +8n +8n +8n +8n +8n +</$objtype/mkfile + +prog: a.$O b.$O c.$O + $LD $LDFLAGS -o $target $prereq + +%.$O: %.c + $CC $CFLAGS $stem.c +.EE +.PP +Override flag settings in the mkfile: +.IP +.EX +% mk target 'CFLAGS=-S -w' +.EE +.PP +Maintain a library: +.IP +.EX +libc.a(%.$O):N: %.$O +libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ... + ar r libc.a $newmember +.EE +.PP +String expression variables to derive names from a master list: +.IP +.EX +NAMES=alloc arc bquote builtins expand main match mk var word +OBJ=${NAMES:%=%.$O} +.EE +.PP +Regular expression meta-rules: +.IP +.EX +([^/]*)/(.*)\e.$O:R: \e1/\e2.c + cd $stem1; $CC $CFLAGS $stem2.c +.EE +.PP +A correct way to deal with +.IR yacc (1) +grammars. +The file +.B lex.c +includes the file +.B x.tab.h +rather than +.B y.tab.h +in order to reflect changes in content, not just modification time. +.IP +.EX +lex.$O: x.tab.h +x.tab.h: y.tab.h + cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h +y.tab.c y.tab.h: gram.y + $YACC -d gram.y +.EE +.PP +The above example could also use the +.B P +attribute for the +.B x.tab.h +rule: +.IP +.EX +x.tab.h:Pcmp -s: y.tab.h + cp y.tab.h x.tab.h +.EE +.SH SOURCE +.B \*9/src/cmd/mk +.SH SEE ALSO +.IR sh (1), +.IR regexp (7) +.PP +A. Hume, +``Mk: a Successor to Make'' +(Tenth Edition Research Unix Manuals). +.PP +Andrew G. Hume and Bob Flandrena, +``Maintaining Files on Plan 9 with Mk''. +DOCPREFIX/doc/mk.pdf +.SH HISTORY +Andrew Hume wrote +.I mk +for Tenth Edition Research Unix. +It was later ported to Plan 9. +This software is a port of the Plan 9 version back to Unix. +.SH BUGS +Identical recipes for regular expression meta-rules only have one target. +.PP +Seemingly appropriate input like +.B CFLAGS=-DHZ=60 +is parsed as an erroneous attribute; correct it by inserting +a space after the first +.LR = . +.PP +The recipes printed by +.I mk +before being passed to +the shell +for execution are sometimes erroneously expanded +for printing. Don't trust what's printed; rely +on what the shell +does. diff --git a/mk/mk.c b/mk/mk.c @@ -0,0 +1,234 @@ +#include "mk.h" + +int runerrs; + +void +mk(char *target) +{ + Node *node; + int did = 0; + + nproc(); /* it can be updated dynamically */ + nrep(); /* it can be updated dynamically */ + runerrs = 0; + node = graph(target); + if(DEBUG(D_GRAPH)){ + dumpn("new target\n", node); + Bflush(&bout); + } + clrmade(node); + while(node->flags&NOTMADE){ + if(work(node, (Node *)0, (Arc *)0)) + did = 1; /* found something to do */ + else { + if(waitup(1, (int *)0) > 0){ + if(node->flags&(NOTMADE|BEINGMADE)){ + assert("must be run errors", runerrs); + break; /* nothing more waiting */ + } + } + } + } + if(node->flags&BEINGMADE) + waitup(-1, (int *)0); + while(jobs) + waitup(-2, (int *)0); + assert("target didn't get done", runerrs || (node->flags&MADE)); + if(did == 0) + Bprint(&bout, "mk: '%s' is up to date\n", node->name); +} + +void +clrmade(Node *n) +{ + Arc *a; + + n->flags &= ~(CANPRETEND|PRETENDING); + if(strchr(n->name, '(') ==0 || n->time) + n->flags |= CANPRETEND; + MADESET(n, NOTMADE); + for(a = n->prereqs; a; a = a->next) + if(a->n) + clrmade(a->n); +} + +static void +unpretend(Node *n) +{ + MADESET(n, NOTMADE); + n->flags &= ~(CANPRETEND|PRETENDING); + n->time = 0; +} + +static char* +dir(void) +{ + static char buf[1024]; + + return getcwd(buf, sizeof buf); +} + +int +work(Node *node, Node *p, Arc *parc) +{ + Arc *a, *ra; + int weoutofdate; + int ready; + int did = 0; + + /*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time); */ + if(node->flags&BEINGMADE) + return(did); + if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){ + if(explain) + fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n", + node->name, node->time, p->name, p->time); + unpretend(node); + } + /* + have a look if we are pretending in case + someone has been unpretended out from underneath us + */ + if(node->flags&MADE){ + if(node->flags&PRETENDING){ + node->time = 0; + }else + return(did); + } + /* consider no prerequsite case */ + if(node->prereqs == 0){ + if(node->time == 0){ + fprint(2, "mk: don't know how to make '%s' in %s\n", node->name, dir()); + if(kflag){ + node->flags |= BEINGMADE; + runerrs++; + } else + Exit(); + } else + MADESET(node, MADE); + return(did); + } + /* + now see if we are out of date or what + */ + ready = 1; + weoutofdate = aflag; + ra = 0; + for(a = node->prereqs; a; a = a->next) + if(a->n){ + did = work(a->n, node, a) || did; + if(a->n->flags&(NOTMADE|BEINGMADE)) + ready = 0; + if(outofdate(node, a, 0)){ + weoutofdate = 1; + if((ra == 0) || (ra->n == 0) + || (ra->n->time < a->n->time)) + ra = a; + } + } else { + if(node->time == 0){ + if(ra == 0) + ra = a; + weoutofdate = 1; + } + } + if(ready == 0) /* can't do anything now */ + return(did); + if(weoutofdate == 0){ + MADESET(node, MADE); + return(did); + } + /* + can we pretend to be made? + */ + if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND)) + && p && ra->n && !outofdate(p, ra, 0)){ + node->flags &= ~CANPRETEND; + MADESET(node, MADE); + if(explain && ((node->flags&PRETENDING) == 0)) + fprint(1, "pretending %s has time %ld\n", node->name, node->time); + node->flags |= PRETENDING; + return(did); + } + /* + node is out of date and we REALLY do have to do something. + quickly rescan for pretenders + */ + for(a = node->prereqs; a; a = a->next) + if(a->n && (a->n->flags&PRETENDING)){ + if(explain) + Bprint(&bout, "unpretending %s because of %s because of %s\n", + a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites"); + + unpretend(a->n); + did = work(a->n, node, a) || did; + ready = 0; + } + if(ready == 0) /* try later unless nothing has happened for -k's sake */ + return(did || work(node, p, parc)); + did = dorecipe(node) || did; + return(did); +} + +void +update(int fake, Node *node) +{ + Arc *a; + + MADESET(node, fake? BEINGMADE : MADE); + if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){ + node->time = timeof(node->name, 1); + node->flags &= ~(CANPRETEND|PRETENDING); + for(a = node->prereqs; a; a = a->next) + if(a->prog) + outofdate(node, a, 1); + } else { + node->time = 1; + for(a = node->prereqs; a; a = a->next) + if(a->n && outofdate(node, a, 1)) + node->time = a->n->time; + } +/* print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);*/ +} + +static int +pcmp(char *prog, char *p, char *q, Shell *sh, Word *shcmd) +{ + char buf[3*NAMEBLOCK]; + int pid; + + Bflush(&bout); + snprint(buf, sizeof buf, "%s '%s' '%s'\n", prog, p, q); + pid = pipecmd(buf, 0, 0, sh, shcmd); + while(waitup(-3, &pid) >= 0) + ; + return(pid? 2:1); +} + +int +outofdate(Node *node, Arc *arc, int eval) +{ + char buf[3*NAMEBLOCK], *str; + Symtab *sym; + int ret; + + str = 0; + if(arc->prog){ + snprint(buf, sizeof buf, "%s%c%s", node->name, 0377, arc->n->name); + sym = symlook(buf, S_OUTOFDATE, 0); + if(sym == 0 || eval){ + if(sym == 0) + str = strdup(buf); + ret = pcmp(arc->prog, node->name, arc->n->name, arc->r->shellt, arc->r->shellcmd); + if(sym) + sym->u.value = ret; + else + symlook(str, S_OUTOFDATE, (void *)(uintptr)ret); + } else + ret = sym->u.value; + return(ret-1); + } else if(strchr(arc->n->name, '(') && arc->n->time == 0) /* missing archive member */ + return 1; + else + return node->time <= arc->n->time; +} diff --git a/mk/mk.h b/mk/mk.h @@ -0,0 +1,185 @@ +#include "sys.h" + +#undef assert +#define assert mkassert +extern Biobuf bout; + +typedef struct Bufblock +{ + struct Bufblock *next; + char *start; + char *end; + char *current; +} Bufblock; + +typedef struct Word +{ + char *s; + struct Word *next; +} Word; + +typedef struct Envy +{ + char *name; + Word *values; +} Envy; + +extern Envy *envy; + +typedef struct Shell +{ + char *name; + char *termchars; /* used in parse.c to isolate assignment attribute */ + int iws; /* inter-word separator in environment */ + char *(*charin)(char*, char*); /* search for unescaped characters */ + char *(*expandquote)(char*, Rune, Bufblock*); /* extract escaped token */ + int (*escapetoken)(Biobuf*, Bufblock*, int, int); /* input escaped token */ + char *(*copyq)(char*, Rune, Bufblock*); /* check for quoted strings */ + int (*matchname)(char*); /* does name match */ +} Shell; + +typedef struct Rule +{ + char *target; /* one target */ + Word *tail; /* constituents of targets */ + char *recipe; /* do it ! */ + short attr; /* attributes */ + short line; /* source line */ + char *file; /* source file */ + Word *alltargets; /* all the targets */ + int rule; /* rule number */ + Reprog *pat; /* reg exp goo */ + char *prog; /* to use in out of date */ + struct Rule *chain; /* hashed per target */ + struct Rule *next; + Shell *shellt; /* shell to use with this rule */ + Word *shellcmd; +} Rule; + +extern Rule *rules, *metarules, *patrule; + +/* Rule.attr */ +#define META 0x0001 +#define UNUSED 0x0002 +#define UPD 0x0004 +#define QUIET 0x0008 +#define VIR 0x0010 +#define REGEXP 0x0020 +#define NOREC 0x0040 +#define DEL 0x0080 +#define NOVIRT 0x0100 + +#define NREGEXP 10 + +typedef struct Arc +{ + short flag; + struct Node *n; + Rule *r; + char *stem; + char *prog; + char *match[NREGEXP]; + struct Arc *next; +} Arc; + + /* Arc.flag */ +#define TOGO 1 + +typedef struct Node +{ + char *name; + long time; + unsigned short flags; + Arc *prereqs; + struct Node *next; /* list for a rule */ +} Node; + + /* Node.flags */ +#define VIRTUAL 0x0001 +#define CYCLE 0x0002 +#define READY 0x0004 +#define CANPRETEND 0x0008 +#define PRETENDING 0x0010 +#define NOTMADE 0x0020 +#define BEINGMADE 0x0040 +#define MADE 0x0080 +#define MADESET(n,m) n->flags = (n->flags&~(NOTMADE|BEINGMADE|MADE))|(m) +#define PROBABLE 0x0100 +#define VACUOUS 0x0200 +#define NORECIPE 0x0400 +#define DELETE 0x0800 +#define NOMINUSE 0x1000 + +typedef struct Job +{ + Rule *r; /* master rule for job */ + Node *n; /* list of node targets */ + char *stem; + char **match; + Word *p; /* prerequistes */ + Word *np; /* new prerequistes */ + Word *t; /* targets */ + Word *at; /* all targets */ + int nproc; /* slot number */ + struct Job *next; +} Job; +extern Job *jobs; + +typedef struct Symtab +{ + short space; + char *name; + union { + void *ptr; + uintptr value; + } u; + struct Symtab *next; +} Symtab; + +enum { + S_VAR, /* variable -> value */ + S_TARGET, /* target -> rule */ + S_TIME, /* file -> time */ + S_PID, /* pid -> products */ + S_NODE, /* target name -> node */ + S_AGG, /* aggregate -> time */ + S_BITCH, /* bitched about aggregate not there */ + S_NOEXPORT, /* var -> noexport */ + S_OVERRIDE, /* can't override */ + S_OUTOFDATE, /* n1\377n2 -> 2(outofdate) or 1(not outofdate) */ + S_MAKEFILE, /* target -> node */ + S_MAKEVAR, /* dumpable mk variable */ + S_EXPORTED, /* var -> current exported value */ + S_WESET, /* variable; we set in the mkfile */ + S_INTERNAL /* an internal mk variable (e.g., stem, target) */ +}; + +extern int debug; +extern int nflag, tflag, iflag, kflag, aflag, mflag; +extern int mkinline; +extern char *infile; +extern int nreps; +extern char *explain; +extern Shell *shellt; +extern Word *shellcmd; + +extern Shell shshell, rcshell; + +#define SYNERR(l) (fprint(2, "mk: %s:%d: syntax error; ", infile, ((l)>=0)?(l):mkinline)) +#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line)) +#define NAMEBLOCK 1000 +#define BIGBLOCK 20000 + +#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) +#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r))) + +#define DEBUG(x) (debug&(x)) +#define D_PARSE 0x01 +#define D_GRAPH 0x02 +#define D_EXEC 0x04 + +#define LSEEK(f,o,p) seek(f,o,p) + +#define PERCENT(ch) (((ch) == '%') || ((ch) == '&')) + +#include "fns.h" diff --git a/mk/mkfile b/mk/mkfile @@ -0,0 +1,37 @@ +<$PLAN9/src/mkhdr + +TARG=mk + +OFILES=\ + arc.$O\ + archive.$O\ + bufblock.$O\ + env.$O\ + file.$O\ + graph.$O\ + job.$O\ + lex.$O\ + main.$O\ + match.$O\ + mk.$O\ + parse.$O\ + recipe.$O\ + rc.$O\ + rule.$O\ + run.$O\ + sh.$O\ + shell.$O\ + shprint.$O\ + symtab.$O\ + var.$O\ + varsub.$O\ + word.$O\ + unix.$O\ + +HFILES=\ + mk.h\ + fns.h\ + + +<$PLAN9/src/mkone + diff --git a/mk/mkfile.test b/mk/mkfile.test @@ -0,0 +1,12 @@ +MKSHELL=$PLAN9/bin/rc +use-rc:V: + for(i in a b c) + echo $i + +MKSHELL=/bin/sh +use-sh:V: + for i in a b c + do + echo $i + done + diff --git a/mk/parse.c b/mk/parse.c @@ -0,0 +1,318 @@ +#include "mk.h" + +char *infile; +int mkinline; +static int rhead(char *, Word **, Word **, int *, char **); +static char *rbody(Biobuf*); +extern Word *target1; + +void +parse(char *f, int fd, int varoverride) +{ + int hline; + char *body; + Word *head, *tail; + int attr, set, pid; + char *prog, *p; + int newfd; + Biobuf in; + Bufblock *buf; + char *err; + + if(fd < 0){ + fprint(2, "open %s: %r\n", f); + Exit(); + } + pushshell(); + ipush(); + infile = strdup(f); + mkinline = 1; + Binit(&in, fd, OREAD); + buf = newbuf(); + while(assline(&in, buf)){ + hline = mkinline; + switch(rhead(buf->start, &head, &tail, &attr, &prog)) + { + case '<': + p = wtos(tail, ' '); + if(*p == 0){ + SYNERR(-1); + fprint(2, "missing include file name\n"); + Exit(); + } + newfd = open(p, OREAD); + if(newfd < 0){ + fprint(2, "warning: skipping missing include file %s: %r\n", p); + } else + parse(p, newfd, 0); + break; + case '|': + p = wtos(tail, ' '); + if(*p == 0){ + SYNERR(-1); + fprint(2, "missing include program name\n"); + Exit(); + } + execinit(); + pid=pipecmd(p, envy, &newfd, shellt, shellcmd); + if(newfd < 0){ + fprint(2, "warning: skipping missing program file %s: %r\n", p); + } else + parse(p, newfd, 0); + while(waitup(-3, &pid) >= 0) + ; + if(pid != 0){ + fprint(2, "bad include program status\n"); + Exit(); + } + break; + case ':': + body = rbody(&in); + addrules(head, tail, body, attr, hline, prog); + break; + case '=': + if(head->next){ + SYNERR(-1); + fprint(2, "multiple vars on left side of assignment\n"); + Exit(); + } + if(symlook(head->s, S_OVERRIDE, 0)){ + set = varoverride; + } else { + set = 1; + if(varoverride) + symlook(head->s, S_OVERRIDE, (void *)""); + } + if(set){ +/* +char *cp; +dumpw("tail", tail); +cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp); +*/ + setvar(head->s, (void *) tail); + symlook(head->s, S_WESET, (void *)""); + if(strcmp(head->s, "MKSHELL") == 0){ + if((err = setshell(tail)) != nil){ + SYNERR(hline); + fprint(2, "%s\n", err); + Exit(); + break; + } + } + } + if(attr) + symlook(head->s, S_NOEXPORT, (void *)""); + break; + default: + SYNERR(hline); + fprint(2, "expected one of :<=\n"); + Exit(); + break; + } + } + close(fd); + freebuf(buf); + ipop(); + popshell(); +} + +void +addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog) +{ + Word *w; + + assert("addrules args", head && body); + /* tuck away first non-meta rule as default target*/ + if(target1 == 0 && !(attr&REGEXP)){ + for(w = head; w; w = w->next) + if(shellt->charin(w->s, "%&")) + break; + if(w == 0) + target1 = wdup(head); + } + for(w = head; w; w = w->next) + addrule(w->s, tail, body, head, attr, hline, prog); +} + +static int +rhead(char *line, Word **h, Word **t, int *attr, char **prog) +{ + char *p; + char *pp; + int sep; + Rune r; + int n; + Word *w; + + p = shellt->charin(line,":=<"); + if(p == 0) + return('?'); + sep = *p; + *p++ = 0; + if(sep == '<' && *p == '|'){ + sep = '|'; + p++; + } + *attr = 0; + *prog = 0; + if(sep == '='){ + pp = shellt->charin(p, shellt->termchars); /* termchars is shell-dependent */ + if (pp && *pp == '=') { + while (p != pp) { + n = chartorune(&r, p); + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n",*p); + Exit(); + case 'U': + *attr = 1; + break; + } + p += n; + } + p++; /* skip trailing '=' */ + } + } + if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){ + while (*p) { + n = chartorune(&r, p); + if (r == ':') + break; + p += n; + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n", p[-1]); + Exit(); + case 'D': + *attr |= DEL; + break; + case 'E': + *attr |= NOMINUSE; + break; + case 'n': + *attr |= NOVIRT; + break; + case 'N': + *attr |= NOREC; + break; + case 'P': + pp = utfrune(p, ':'); + if (pp == 0 || *pp == 0) + goto eos; + *pp = 0; + *prog = strdup(p); + *pp = ':'; + p = pp; + break; + case 'Q': + *attr |= QUIET; + break; + case 'R': + *attr |= REGEXP; + break; + case 'U': + *attr |= UPD; + break; + case 'V': + *attr |= VIR; + break; + } + } + if (*p++ != ':') { + eos: + SYNERR(-1); + fprint(2, "missing trailing :\n"); + Exit(); + } + } + *h = w = stow(line); + if(*w->s == 0 && sep != '<' && sep != '|' && sep != 'S') { + SYNERR(mkinline-1); + fprint(2, "no var on left side of assignment/rule\n"); + Exit(); + } + *t = stow(p); + return(sep); +} + +static char * +rbody(Biobuf *in) +{ + Bufblock *buf; + int r, lastr; + char *p; + + lastr = '\n'; + buf = newbuf(); + for(;;){ + r = Bgetrune(in); + if (r < 0) + break; + if (lastr == '\n') { + if (r == '#') + rinsert(buf, r); + else if (r != ' ' && r != '\t') { + Bungetrune(in); + break; + } + } else + rinsert(buf, r); + lastr = r; + if (r == '\n') + mkinline++; + } + insert(buf, 0); + p = strdup(buf->start); + freebuf(buf); + return p; +} + +struct input +{ + char *file; + int line; + struct input *next; +}; +static struct input *inputs = 0; + +void +ipush(void) +{ + struct input *in, *me; + + me = (struct input *)Malloc(sizeof(*me)); + me->file = infile; + me->line = mkinline; + me->next = 0; + if(inputs == 0) + inputs = me; + else { + for(in = inputs; in->next; ) + in = in->next; + in->next = me; + } +} + +void +ipop(void) +{ + struct input *in, *me; + + assert("pop input list", inputs != 0); + if(inputs->next == 0){ + me = inputs; + inputs = 0; + } else { + for(in = inputs; in->next->next; ) + in = in->next; + me = in->next; + in->next = 0; + } + infile = me->file; + mkinline = me->line; + free((char *)me); +} diff --git a/mk/rc.c b/mk/rc.c @@ -0,0 +1,194 @@ +#include "mk.h" + +/* + * This file contains functions that depend on rc's syntax. Most + * of the routines extract strings observing rc's escape conventions + */ + + +/* + * skip a token in single quotes. + */ +static char * +squote(char *cp) +{ + Rune r; + int n; + + while(*cp){ + n = chartorune(&r, cp); + if(r == '\'') { + n += chartorune(&r, cp+n); + if(r != '\'') + return(cp); + } + cp += n; + } + SYNERR(-1); /* should never occur */ + fprint(2, "missing closing '\n"); + return 0; +} + +/* + * search a string for characters in a pattern set + * characters in quotes and variable generators are escaped + */ +char * +rccharin(char *cp, char *pat) +{ + Rune r; + int n, vargen; + + vargen = 0; + while(*cp){ + n = chartorune(&r, cp); + switch(r){ + case '\'': /* skip quoted string */ + cp = squote(cp+1); /* n must = 1 */ + if(!cp) + return 0; + break; + case '$': + if(*(cp+1) == '{') + vargen = 1; + break; + case '}': + if(vargen) + vargen = 0; + else if(utfrune(pat, r)) + return cp; + break; + default: + if(vargen == 0 && utfrune(pat, r)) + return cp; + break; + } + cp += n; + } + if(vargen){ + SYNERR(-1); + fprint(2, "missing closing } in pattern generator\n"); + } + return 0; +} + +/* + * extract an escaped token. Possible escape chars are single-quote, + * double-quote,and backslash. Only the first is valid for rc. the + * others are just inserted into the receiving buffer. + */ +char* +rcexpandquote(char *s, Rune r, Bufblock *b) +{ + if (r != '\'') { + rinsert(b, r); + return s; + } + + while(*s){ + s += chartorune(&r, s); + if(r == '\'') { + if(*s == '\'') + s++; + else + return s; + } + rinsert(b, r); + } + return 0; +} + +/* + * Input an escaped token. Possible escape chars are single-quote, + * double-quote and backslash. Only the first is a valid escape for + * rc; the others are just inserted into the receiving buffer. + */ +int +rcescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) +{ + int c, line; + + if(esc != '\'') + return 1; + + line = mkinline; + while((c = nextrune(bp, 0)) > 0){ + if(c == '\''){ + if(preserve) + rinsert(buf, c); + c = Bgetrune(bp); + if (c < 0) + break; + if(c != '\''){ + Bungetrune(bp); + return 1; + } + } + rinsert(buf, c); + } + SYNERR(line); fprint(2, "missing closing %c\n", esc); + return 0; +} + +/* + * copy a single-quoted string; s points to char after opening quote + */ +static char * +copysingle(char *s, Bufblock *buf) +{ + Rune r; + + while(*s){ + s += chartorune(&r, s); + rinsert(buf, r); + if(r == '\'') + break; + } + return s; +} +/* + * check for quoted strings. backquotes are handled here; single quotes above. + * s points to char after opening quote, q. + */ +char * +rccopyq(char *s, Rune q, Bufblock *buf) +{ + if(q == '\'') /* copy quoted string */ + return copysingle(s, buf); + + if(q != '`') /* not quoted */ + return s; + + while(*s){ /* copy backquoted string */ + s += chartorune(&q, s); + rinsert(buf, q); + if(q == '}') + break; + if(q == '\'') + s = copysingle(s, buf); /* copy quoted string */ + } + return s; +} + +static int +rcmatchname(char *name) +{ + char *p; + + if((p = strrchr(name, '/')) != nil) + name = p+1; + if(name[0] == 'r' && name[1] == 'c') + return 1; + return 0; +} + +Shell rcshell = { + "rc", + "'= \t", + '\1', + rccharin, + rcexpandquote, + rcescapetoken, + rccopyq, + rcmatchname +}; diff --git a/mk/recipe.c b/mk/recipe.c @@ -0,0 +1,117 @@ +#include "mk.h" + +int +dorecipe(Node *node) +{ + char buf[BIGBLOCK]; + register Node *n; + Rule *r = 0; + Arc *a, *aa; + Word head, ahead, lp, ln, *w, *ww, *aw; + Symtab *s; + int did = 0; + + aa = 0; + /* + pick up the rule + */ + for(a = node->prereqs; a; a = a->next) + if(*a->r->recipe) + r = (aa = a)->r; + /* + no recipe? go to buggery! + */ + if(r == 0){ + if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){ + fprint(2, "mk: no recipe to make '%s'\n", node->name); + Exit(); + } + if(strchr(node->name, '(') && node->time == 0) + MADESET(node, MADE); + else + update(0, node); + if(tflag){ + if(!(node->flags&VIRTUAL)) + touch(node->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", node->name); + } + return(did); + } + /* + build the node list + */ + node->next = 0; + head.next = 0; + ww = &head; + ahead.next = 0; + aw = &ahead; + if(r->attr&REGEXP){ + ww->next = newword(node->name); + aw->next = newword(node->name); + } else { + for(w = r->alltargets; w; w = w->next){ + if(r->attr&META) + subst(aa->stem, w->s, buf); + else + strcpy(buf, w->s); + aw->next = newword(buf); + aw = aw->next; + if((s = symlook(buf, S_NODE, 0)) == 0) + continue; /* not a node we are interested in */ + n = s->u.ptr; + if(aflag == 0 && n->time) { + for(a = n->prereqs; a; a = a->next) + if(a->n && outofdate(n, a, 0)) + break; + if(a == 0) + continue; + } + ww->next = newword(buf); + ww = ww->next; + if(n == node) continue; + n->next = node->next; + node->next = n; + } + } + for(n = node; n; n = n->next) + if((n->flags&READY) == 0) + return(did); + /* + gather the params for the job + */ + lp.next = ln.next = 0; + for(n = node; n; n = n->next){ + for(a = n->prereqs; a; a = a->next){ + if(a->n){ + addw(&lp, a->n->name); + if(outofdate(n, a, 0)){ + addw(&ln, a->n->name); + if(explain) + fprint(1, "%s(%ld) < %s(%ld)\n", + n->name, n->time, a->n->name, a->n->time); + } + } else { + if(explain) + fprint(1, "%s has no prerequisites\n", + n->name); + } + } + MADESET(n, BEINGMADE); + } + /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*/ + run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next)); + return(1); +} + +void +addw(Word *w, char *s) +{ + Word *lw; + + for(lw = w; w = w->next; lw = w){ + if(strcmp(s, w->s) == 0) + return; + } + lw->next = newword(s); +} diff --git a/mk/rule.c b/mk/rule.c @@ -0,0 +1,112 @@ +#include "mk.h" + +static Rule *lr, *lmr; +static int rcmp(Rule *r, char *target, Word *tail); +static int nrules = 0; + +void +addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, char *prog) +{ + Rule *r; + Rule *rr; + Symtab *sym; + int reuse; + + r = 0; + reuse = 0; + if(sym = symlook(head, S_TARGET, 0)){ + for(r = sym->u.ptr; r; r = r->chain) + if(rcmp(r, head, tail) == 0){ + reuse = 1; + break; + } + } + if(r == 0) + r = (Rule *)Malloc(sizeof(Rule)); + r->shellt = shellt; + r->shellcmd = shellcmd; + r->target = head; + r->tail = tail; + r->recipe = body; + r->line = hline; + r->file = infile; + r->attr = attr; + r->alltargets = ahead; + r->prog = prog; + r->rule = nrules++; + if(!reuse){ + rr = symlook(head, S_TARGET, (void *)r)->u.ptr; + if(rr != r){ + r->chain = rr->chain; + rr->chain = r; + } else + r->chain = 0; + } + if(!reuse) + r->next = 0; + if((attr&REGEXP) || shellt->charin(head, "%&")){ + r->attr |= META; + if(reuse) + return; + if(attr&REGEXP){ + patrule = r; + r->pat = regcomp(head); + } + if(metarules == 0) + metarules = lmr = r; + else { + lmr->next = r; + lmr = r; + } + } else { + if(reuse) + return; + r->pat = 0; + if(rules == 0) + rules = lr = r; + else { + lr->next = r; + lr = r; + } + } +} + +void +dumpr(char *s, Rule *r) +{ + if(r == nil) + return; + Bprint(&bout, "%s: start=%ld shelltype=%s shellcmd=%s\n", + s, r, r->shellt->name, wtos(r->shellcmd, ' ')); + for(; r; r = r->next){ + Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'", + r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' ')); + if(r->prog) + Bprint(&bout, " prog='%s'", r->prog); + Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' ')); + Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe); + } +} + +static int +rcmp(Rule *r, char *target, Word *tail) +{ + Word *w; + + if(strcmp(r->target, target)) + return 1; + for(w = r->tail; w && tail; w = w->next, tail = tail->next) + if(strcmp(w->s, tail->s)) + return 1; + return(w || tail); +} + +char * +rulecnt(void) +{ + char *s; + + s = Malloc(nrules); + memset(s, 0, nrules); + return(s); +} diff --git a/mk/run.c b/mk/run.c @@ -0,0 +1,296 @@ +#include "mk.h" + +typedef struct Event +{ + int pid; + Job *job; +} Event; +static Event *events; +static int nevents, nrunning, nproclimit; + +typedef struct Process +{ + int pid; + int status; + struct Process *b, *f; +} Process; +static Process *phead, *pfree; +static void sched(void); +static void pnew(int, int), pdelete(Process *); + +int pidslot(int); + +void +run(Job *j) +{ + Job *jj; + + if(jobs){ + for(jj = jobs; jj->next; jj = jj->next) + ; + jj->next = j; + } else + jobs = j; + j->next = 0; + /* this code also in waitup after parse redirect */ + if(nrunning < nproclimit) + sched(); +} + +static void +sched(void) +{ + char *flags; + Job *j; + Bufblock *buf; + int slot; + Node *n; + Envy *e; + + if(jobs == 0){ + usage(); + return; + } + j = jobs; + jobs = j->next; + if(DEBUG(D_EXEC)) + fprint(1, "firing up job for target %s\n", wtos(j->t, ' ')); + slot = nextslot(); + events[slot].job = j; + buf = newbuf(); + e = buildenv(j, slot); + shprint(j->r->recipe, e, buf, j->r->shellt); + if(!tflag && (nflag || !(j->r->attr&QUIET))) + Bwrite(&bout, buf->start, (long)strlen(buf->start)); + freebuf(buf); + if(nflag||tflag){ + for(n = j->n; n; n = n->next){ + if(tflag){ + if(!(n->flags&VIRTUAL)) + touch(n->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", n->name); + } + n->time = time((long *)0); + MADESET(n, MADE); + } + } else { + if(DEBUG(D_EXEC)) + fprint(1, "recipe='%s'", j->r->recipe);/**/ + Bflush(&bout); + if(j->r->attr&NOMINUSE) + flags = 0; + else + flags = "-e"; + events[slot].pid = execsh(flags, j->r->recipe, 0, e, j->r->shellt, j->r->shellcmd); + usage(); + nrunning++; + if(DEBUG(D_EXEC)) + fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid); + } +} + +int +waitup(int echildok, int *retstatus) +{ + Envy *e; + int pid; + int slot; + Symtab *s; + Word *w; + Job *j; + char buf[ERRMAX]; + Bufblock *bp; + int uarg = 0; + int done; + Node *n; + Process *p; + extern int runerrs; + + /* first check against the proces slist */ + if(retstatus) + for(p = phead; p; p = p->f) + if(p->pid == *retstatus){ + *retstatus = p->status; + pdelete(p); + return(-1); + } +again: /* rogue processes */ + pid = waitfor(buf); + if(pid == -1){ + if(echildok > 0) + return(1); + else { + fprint(2, "mk: (waitup %d): %r\n", echildok); + Exit(); + } + } + if(DEBUG(D_EXEC)) + fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf); + if(retstatus && pid == *retstatus){ + *retstatus = buf[0]? 1:0; + return(-1); + } + slot = pidslot(pid); + if(slot < 0){ + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + pnew(pid, buf[0]? 1:0); + goto again; + } + j = events[slot].job; + usage(); + nrunning--; + events[slot].pid = -1; + if(buf[0]){ + e = buildenv(j, slot); + bp = newbuf(); + shprint(j->r->recipe, e, bp, j->r->shellt); + front(bp->start); + fprint(2, "mk: %s: exit status=%s", bp->start, buf); + freebuf(bp); + for(n = j->n, done = 0; n; n = n->next) + if(n->flags&DELETE){ + if(done++ == 0) + fprint(2, ", deleting"); + fprint(2, " '%s'", n->name); + delete(n->name); + } + fprint(2, "\n"); + if(kflag){ + runerrs++; + uarg = 1; + } else { + jobs = 0; + Exit(); + } + } + for(w = j->t; w; w = w->next){ + if((s = symlook(w->s, S_NODE, 0)) == 0) + continue; /* not interested in this node */ + update(uarg, s->u.ptr); + } + if(nrunning < nproclimit) + sched(); + return(0); +} + +void +nproc(void) +{ + Symtab *sym; + Word *w; + + if(sym = symlook("NPROC", S_VAR, 0)) { + w = sym->u.ptr; + if (w && w->s && w->s[0]) + nproclimit = atoi(w->s); + } + if(nproclimit < 1) + nproclimit = 1; + if(DEBUG(D_EXEC)) + fprint(1, "nprocs = %d\n", nproclimit); + if(nproclimit > nevents){ + if(nevents) + events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event)); + else + events = (Event *)Malloc(nproclimit*sizeof(Event)); + while(nevents < nproclimit) + events[nevents++].pid = 0; + } +} + +int +nextslot(void) +{ + int i; + + for(i = 0; i < nproclimit; i++) + if(events[i].pid <= 0) return i; + assert("out of slots!!", 0); + return 0; /* cyntax */ +} + +int +pidslot(int pid) +{ + int i; + + for(i = 0; i < nevents; i++) + if(events[i].pid == pid) return(i); + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + return(-1); +} + + +static void +pnew(int pid, int status) +{ + Process *p; + + if(pfree){ + p = pfree; + pfree = p->f; + } else + p = (Process *)Malloc(sizeof(Process)); + p->pid = pid; + p->status = status; + p->f = phead; + phead = p; + if(p->f) + p->f->b = p; + p->b = 0; +} + +static void +pdelete(Process *p) +{ + if(p->f) + p->f->b = p->b; + if(p->b) + p->b->f = p->f; + else + phead = p->f; + p->f = pfree; + pfree = p; +} + +void +killchildren(char *msg) +{ + Process *p; + + kflag = 1; /* to make sure waitup doesn't exit */ + jobs = 0; /* make sure no more get scheduled */ + for(p = phead; p; p = p->f) + expunge(p->pid, msg); + while(waitup(1, (int *)0) == 0) + ; + Bprint(&bout, "mk: %s\n", msg); + Exit(); +} + +static long tslot[1000]; +static long tick; + +void +usage(void) +{ + long t; + + time(&t); + if(tick) + tslot[nrunning] += (t-tick); + tick = t; +} + +void +prusage(void) +{ + int i; + + usage(); + for(i = 0; i <= nevents; i++) + fprint(1, "%d: %ld\n", i, tslot[i]); +} diff --git a/mk/sh.c b/mk/sh.c @@ -0,0 +1,206 @@ +#include "mk.h" + +/* + * This file contains functions that depend on the shell's syntax. Most + * of the routines extract strings observing the shell's escape conventions. + */ + + +/* + * skip a token in quotes. + */ +static char * +squote(char *cp, int c) +{ + Rune r; + int n; + + while(*cp){ + n = chartorune(&r, cp); + if(r == c) + return cp; + if(r == '\\') + n += chartorune(&r, cp+n); + cp += n; + } + SYNERR(-1); /* should never occur */ + fprint(2, "missing closing '\n"); + return 0; +} +/* + * search a string for unescaped characters in a pattern set + */ +static char * +shcharin(char *cp, char *pat) +{ + Rune r; + int n, vargen; + + vargen = 0; + while(*cp){ + n = chartorune(&r, cp); + switch(r){ + case '\\': /* skip escaped char */ + cp += n; + n = chartorune(&r, cp); + break; + case '\'': /* skip quoted string */ + case '"': + cp = squote(cp+1, r); /* n must = 1 */ + if(!cp) + return 0; + break; + case '$': + if(*(cp+1) == '{') + vargen = 1; + break; + case '}': + if(vargen) + vargen = 0; + else if(utfrune(pat, r)) + return cp; + break; + default: + if(vargen == 0 && utfrune(pat, r)) + return cp; + break; + } + cp += n; + } + if(vargen){ + SYNERR(-1); + fprint(2, "missing closing } in pattern generator\n"); + } + return 0; +} + +/* + * extract an escaped token. Possible escape chars are single-quote, + * double-quote,and backslash. + */ +static char* +shexpandquote(char *s, Rune esc, Bufblock *b) +{ + Rune r; + + if (esc == '\\') { + s += chartorune(&r, s); + rinsert(b, r); + return s; + } + + while(*s){ + s += chartorune(&r, s); + if(r == esc) + return s; + if (r == '\\') { + rinsert(b, r); + s += chartorune(&r, s); + } + rinsert(b, r); + } + return 0; +} + +/* + * Input an escaped token. Possible escape chars are single-quote, + * double-quote and backslash. + */ +static int +shescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) +{ + int c, line; + + if(esc == '\\') { + c = Bgetrune(bp); + if(c == '\r') + c = Bgetrune(bp); + if (c == '\n') + mkinline++; + rinsert(buf, c); + return 1; + } + + line = mkinline; + while((c = nextrune(bp, 0)) >= 0){ + if(c == esc){ + if(preserve) + rinsert(buf, c); + return 1; + } + if(c == '\\') { + rinsert(buf, c); + c = Bgetrune(bp); + if(c == '\r') + c = Bgetrune(bp); + if (c < 0) + break; + if (c == '\n') + mkinline++; + } + rinsert(buf, c); + } + SYNERR(line); fprint(2, "missing closing %c\n", esc); + return 0; +} + +/* + * copy a quoted string; s points to char after opening quote + */ +static char * +copysingle(char *s, Rune q, Bufblock *buf) +{ + Rune r; + + while(*s){ + s += chartorune(&r, s); + rinsert(buf, r); + if(r == q) + break; + } + return s; +} +/* + * check for quoted strings. backquotes are handled here; single quotes above. + * s points to char after opening quote, q. + */ +static char * +shcopyq(char *s, Rune q, Bufblock *buf) +{ + if(q == '\'' || q == '"') /* copy quoted string */ + return copysingle(s, q, buf); + + if(q != '`') /* not quoted */ + return s; + + while(*s){ /* copy backquoted string */ + s += chartorune(&q, s); + rinsert(buf, q); + if(q == '`') + break; + if(q == '\'' || q == '"') + s = copysingle(s, q, buf); /* copy quoted string */ + } + return s; +} + +static int +shmatchname(char *name) +{ + USED(name); + + return 1; +} + + +Shell shshell = { + "sh", + "\"'= \t", /*used in parse.c to isolate assignment attribute*/ + ' ', /* inter-word separator in env */ + shcharin, + shexpandquote, + shescapetoken, + shcopyq, + shmatchname +}; + diff --git a/mk/shell.c b/mk/shell.c @@ -0,0 +1,80 @@ +#include "mk.h" + +static Shell *shells[] = { + &rcshell, + &shshell +}; + +Shell *shelldefault = &shshell; + +Shell *shellt; +Word *shellcmd; + +typedef struct Shellstack Shellstack; +struct Shellstack +{ + Shell *t; + Word *w; + Shellstack *next; +}; + +Shellstack *shellstack; + +char* +setshell(Word *w) +{ + int i; + + if(w->s == nil) + return "shell name not found on line"; + + for(i=0; i<nelem(shells); i++) + if(shells[i]->matchname(w->s)) + break; + if(i == nelem(shells)) + return "cannot determine shell type"; + shellt = shells[i]; + shellcmd = w; + return nil; +} + +void +initshell(void) +{ + shellcmd = stow(shelldefault->name); + shellt = shelldefault; + setvar("MKSHELL", shellcmd); +} + +void +pushshell(void) +{ + Shellstack *s; + + /* save */ + s = Malloc(sizeof *s); + s->t = shellt; + s->w = shellcmd; + s->next = shellstack; + shellstack = s; + + initshell(); /* reset to defaults */ +} + +void +popshell(void) +{ + Shellstack *s; + + if(shellstack == nil){ + fprint(2, "internal shellstack error\n"); + Exit(); + } + + s = shellstack; + shellstack = s->next; + shellt = s->t; + shellcmd = s->w; + setvar("MKSHELL", shellcmd); + free(s); +} diff --git a/mk/shprint.c b/mk/shprint.c @@ -0,0 +1,125 @@ +#include "mk.h" + +static char *vexpand(char*, Envy*, Bufblock*); + +#define getfields mkgetfields + +static int +getfields(char *str, char **args, int max, int mflag, char *set) +{ + Rune r; + int nr, intok, narg; + + if(max <= 0) + return 0; + + narg = 0; + args[narg] = str; + if(!mflag) + narg++; + intok = 0; + for(;; str += nr) { + nr = chartorune(&r, str); + if(r == 0) + break; + if(utfrune(set, r)) { + if(narg >= max) + break; + *str = 0; + intok = 0; + args[narg] = str + nr; + if(!mflag) + narg++; + } else { + if(!intok && mflag) + narg++; + intok = 1; + } + } + return narg; +} + +void +shprint(char *s, Envy *env, Bufblock *buf, Shell *sh) +{ + int n; + Rune r; + + while(*s) { + n = chartorune(&r, s); + if (r == '$') + s = vexpand(s, env, buf); + else { + rinsert(buf, r); + s += n; + s = sh->copyq(s, r, buf); /*handle quoted strings*/ + } + } + insert(buf, 0); +} + +static char * +mygetenv(char *name, Envy *env) +{ + if (!env) + return 0; + if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == 0) + return 0; + /* only resolve internal variables and variables we've set */ + for(; env->name; env++){ + if (strcmp(env->name, name) == 0) + return wtos(env->values, ' '); + } + return 0; +} + +static char * +vexpand(char *w, Envy *env, Bufblock *buf) +{ + char *s, carry, *p, *q; + + assert("vexpand no $", *w == '$'); + p = w+1; /* skip dollar sign */ + if(*p == '{') { + p++; + q = utfrune(p, '}'); + if (!q) + q = strchr(p, 0); + } else + q = shname(p); + carry = *q; + *q = 0; + s = mygetenv(p, env); + *q = carry; + if (carry == '}') + q++; + if (s) { + bufcpy(buf, s, strlen(s)); + free(s); + } else /* copy name intact*/ + bufcpy(buf, w, q-w); + return(q); +} + +void +front(char *s) +{ + char *t, *q; + int i, j; + char *flds[512]; + + q = strdup(s); + i = getfields(q, flds, 512, 0, " \t\n"); + if(i > 5){ + flds[4] = flds[i-1]; + flds[3] = "..."; + i = 5; + } + t = s; + for(j = 0; j < i; j++){ + for(s = flds[j]; *s; *t++ = *s++); + *t++ = ' '; + } + *t = 0; + free(q); +} diff --git a/mk/symtab.c b/mk/symtab.c @@ -0,0 +1,97 @@ +#include "mk.h" + +#define NHASH 4099 +#define HASHMUL 79L /* this is a good value */ +static Symtab *hash[NHASH]; + +void +syminit(void) +{ + Symtab **s, *ss, *next; + + for(s = hash; s < &hash[NHASH]; s++){ + for(ss = *s; ss; ss = next){ + next = ss->next; + free((char *)ss); + } + *s = 0; + } +} + +Symtab * +symlook(char *sym, int space, void *install) +{ + long h; + char *p; + Symtab *s; + + for(p = sym, h = space; *p; h += *p++) + h *= HASHMUL; + if(h < 0) + h = ~h; + h %= NHASH; + for(s = hash[h]; s; s = s->next) + if((s->space == space) && (strcmp(s->name, sym) == 0)) + return(s); + if(install == 0) + return(0); + s = (Symtab *)Malloc(sizeof(Symtab)); + s->space = space; + s->name = sym; + s->u.ptr = install; + s->next = hash[h]; + hash[h] = s; + return(s); +} + +void +symdel(char *sym, int space) +{ + long h; + char *p; + Symtab *s, *ls; + + /* multiple memory leaks */ + + for(p = sym, h = space; *p; h += *p++) + h *= HASHMUL; + if(h < 0) + h = ~h; + h %= NHASH; + for(s = hash[h], ls = 0; s; ls = s, s = s->next) + if((s->space == space) && (strcmp(s->name, sym) == 0)){ + if(ls) + ls->next = s->next; + else + hash[h] = s->next; + free((char *)s); + } +} + +void +symtraverse(int space, void (*fn)(Symtab*)) +{ + Symtab **s, *ss; + + for(s = hash; s < &hash[NHASH]; s++) + for(ss = *s; ss; ss = ss->next) + if(ss->space == space) + (*fn)(ss); +} + +void +symstat(void) +{ + Symtab **s, *ss; + int n; + int l[1000]; + + memset((char *)l, 0, sizeof(l)); + for(s = hash; s < &hash[NHASH]; s++){ + for(ss = *s, n = 0; ss; ss = ss->next) + n++; + l[n]++; + } + for(n = 0; n < 1000; n++) + if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n); +} diff --git a/mk/sys.h b/mk/sys.h @@ -0,0 +1,5 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> + diff --git a/mk/sys.std.h b/mk/sys.std.h @@ -0,0 +1,27 @@ +#include <utf.h> +#include <fmt.h> +#include <bio.h> +#include <regexp9.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <stdint.h> + +#define OREAD O_RDONLY +#define OWRITE O_WRONLY +#define ORDWR O_RDWR +#define nil 0 +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define seek lseek +#define remove unlink +#define exits(x) exit(x && *(char*)x ? 1 : 0) +#define USED(x) if(x){}else +#define create(name, mode, perm) open(name, mode|O_CREAT, perm) +#define ERRMAX 256 + +typedef uintptr_t uintptr; +#define uchar mk_uchar +typedef unsigned char uchar; diff --git a/mk/unix.c b/mk/unix.c @@ -0,0 +1,341 @@ +#define NOPLAN9DEFINES +#include "mk.h" +#include <sys/wait.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/time.h> + +char *shell = "/bin/sh"; +char *shellname = "sh"; + +extern char **environ; + +static void +mkperror(char *s) +{ + fprint(2, "%s: %r\n", s); +} + +void +readenv(void) +{ + char **p, *s; + Word *w; + + for(p = environ; *p; p++){ +/* rsc 5/5/2004 -- This misparses fn#cd={whatever} + s = shname(*p); + if(*s == '=') { + *s = 0; + w = newword(s+1); + } else + w = newword(""); +*/ + s = strchr(*p, '='); + if(s){ + *s = 0; + w = newword(s+1); + } else + w = newword(""); + if (symlook(*p, S_INTERNAL, 0)) + continue; + s = strdup(*p); + setvar(s, (void *)w); + symlook(s, S_EXPORTED, (void*)"")->u.ptr = ""; + } +} + +/* + * done on child side of fork, so parent's env is not affected + * and we don't care about freeing memory because we're going + * to exec immediately after this. + */ +void +exportenv(Envy *e, Shell *sh) +{ + int i; + char **p; + static char buf[16384]; + + p = 0; + for(i = 0; e->name; e++, i++) { + p = (char**) Realloc(p, (i+2)*sizeof(char*)); + if(e->values) + snprint(buf, sizeof buf, "%s=%s", e->name, wtos(e->values, sh->iws)); + else + snprint(buf, sizeof buf, "%s=", e->name); + p[i] = strdup(buf); + } + p[i] = 0; + environ = p; +} + +int +waitfor(char *msg) +{ + int status; + int pid; + + *msg = 0; + pid = wait(&status); + if(pid > 0) { + if(status&0x7f) { + if(status&0x80) + snprint(msg, ERRMAX, "signal %d, core dumped", status&0x7f); + else + snprint(msg, ERRMAX, "signal %d", status&0x7f); + } else if(status&0xff00) + snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff); + } + return pid; +} + +void +expunge(int pid, char *msg) +{ + if(strcmp(msg, "interrupt")) + kill(pid, SIGINT); + else + kill(pid, SIGHUP); +} + +int mypid; + +int +shargv(Word *cmd, int extra, char ***pargv) +{ + char **argv; + int i, n; + Word *w; + + n = 0; + for(w=cmd; w; w=w->next) + n++; + + argv = Malloc((n+extra+1)*sizeof(argv[0])); + i = 0; + for(w=cmd; w; w=w->next) + argv[i++] = w->s; + argv[n] = 0; + *pargv = argv; + return n; +} + +int +execsh(char *args, char *cmd, Bufblock *buf, Envy *e, Shell *sh, Word *shellcmd) +{ + char *p, **argv; + int tot, n, pid, in[2], out[2]; + + if(buf && pipe(out) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + mypid = getpid(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid == 0){ + if(buf) + close(out[0]); + if(pipe(in) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid != 0){ + dup2(in[0], 0); + if(buf){ + dup2(out[1], 1); + close(out[1]); + } + close(in[0]); + close(in[1]); + if (e) + exportenv(e, sh); + n = shargv(shellcmd, 1, &argv); + argv[n++] = args; + argv[n] = 0; + execvp(argv[0], argv); + mkperror(shell); + _exit(1); + } + close(out[1]); + close(in[0]); + if(DEBUG(D_EXEC)) + fprint(1, "starting: %s\n", cmd); + p = cmd+strlen(cmd); + while(cmd < p){ + n = write(in[1], cmd, p-cmd); + if(n < 0) + break; + cmd += n; + } + close(in[1]); + _exit(0); + } + if(buf){ + close(out[1]); + tot = 0; + for(;;){ + if (buf->current >= buf->end) + growbuf(buf); + n = read(out[0], buf->current, buf->end-buf->current); + if(n <= 0) + break; + buf->current += n; + tot += n; + } + if (tot && buf->current[-1] == '\n') + buf->current--; + close(out[0]); + } + return pid; +} + +int +pipecmd(char *cmd, Envy *e, int *fd, Shell *sh, Word *shellcmd) +{ + int pid, pfd[2]; + int n; + char **argv; + + if(DEBUG(D_EXEC)) + fprint(1, "pipecmd='%s'\n", cmd);/**/ + + if(fd && pipe(pfd) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid == 0){ + if(fd){ + close(pfd[0]); + dup2(pfd[1], 1); + close(pfd[1]); + } + if(e) + exportenv(e, sh); + n = shargv(shellcmd, 2, &argv); + argv[n++] = "-c"; + argv[n++] = cmd; + argv[n] = 0; + execvp(argv[0], argv); + mkperror(shell); + _exit(1); + } + if(fd){ + close(pfd[1]); + *fd = pfd[0]; + } + return pid; +} + +void +Exit(void) +{ + while(wait(0) >= 0) + ; + exits("error"); +} + +static struct +{ + int sig; + char *msg; +} sigmsgs[] = +{ + SIGALRM, "alarm", + SIGFPE, "sys: fp: fptrap", + SIGPIPE, "sys: write on closed pipe", + SIGILL, "sys: trap: illegal instruction", +/* SIGSEGV, "sys: segmentation violation", */ + 0, 0 +}; + +static void +notifyf(int sig) +{ + int i; + + for(i = 0; sigmsgs[i].msg; i++) + if(sigmsgs[i].sig == sig) + killchildren(sigmsgs[i].msg); + + /* should never happen */ + signal(sig, SIG_DFL); + kill(getpid(), sig); +} + +void +catchnotes(void) +{ + int i; + + for(i = 0; sigmsgs[i].msg; i++) + signal(sigmsgs[i].sig, notifyf); +} + +char* +maketmp(int *pfd) +{ + static char temp[] = "/tmp/mkargXXXXXX"; + static char buf[100]; + int fd; + + strcpy(buf, temp); + fd = mkstemp(buf); + if(fd < 0) + return 0; + *pfd = fd; + return buf; +} + +int +chgtime(char *name) +{ + if(access(name, 0) >= 0) + return utimes(name, 0); + return close(creat(name, 0666)); +} + +void +rcopy(char **to, Resub *match, int n) +{ + int c; + char *p; + + *to = match->s.sp; /* stem0 matches complete target */ + for(to++, match++; --n > 0; to++, match++){ + if(match->s.sp && match->e.ep){ + p = match->e.ep; + c = *p; + *p = 0; + *to = strdup(match->s.sp); + *p = c; + } + else + *to = 0; + } +} + +unsigned long +mkmtime(char *name) +{ + struct stat st; + + if(stat(name, &st) < 0) + return 0; + + return st.st_mtime; +} diff --git a/mk/var.c b/mk/var.c @@ -0,0 +1,41 @@ +#include "mk.h" + +void +setvar(char *name, void *ptr) +{ + symlook(name, S_VAR, ptr)->u.ptr = ptr; + symlook(name, S_MAKEVAR, (void*)""); +} + +static void +print1(Symtab *s) +{ + Word *w; + + Bprint(&bout, "\t%s=", s->name); + for (w = s->u.ptr; w; w = w->next) + Bprint(&bout, "'%s'", w->s); + Bprint(&bout, "\n"); +} + +void +dumpv(char *s) +{ + Bprint(&bout, "%s:\n", s); + symtraverse(S_VAR, print1); +} + +char * +shname(char *a) +{ + Rune r; + int n; + + while (*a) { + n = chartorune(&r, a); + if (!WORDCHR(r)) + break; + a += n; + } + return a; +} diff --git a/mk/varsub.c b/mk/varsub.c @@ -0,0 +1,252 @@ +#include "mk.h" + +static Word *subsub(Word*, char*, char*); +static Word *expandvar(char**); +static Bufblock *varname(char**); +static Word *extractpat(char*, char**, char*, char*); +static int submatch(char*, Word*, Word*, int*, char**); +static Word *varmatch(char *); + +Word * +varsub(char **s) +{ + Bufblock *b; + Word *w; + + if(**s == '{') /* either ${name} or ${name: A%B==C%D}*/ + return expandvar(s); + + b = varname(s); + if(b == 0) + return 0; + + w = varmatch(b->start); + freebuf(b); + return w; +} + +/* + * extract a variable name + */ +static Bufblock* +varname(char **s) +{ + Bufblock *b; + char *cp; + Rune r; + int n; + + b = newbuf(); + cp = *s; + for(;;){ + n = chartorune(&r, cp); + if (!WORDCHR(r)) + break; + rinsert(b, r); + cp += n; + } + if (b->current == b->start){ + SYNERR(-1); + fprint(2, "missing variable name <%s>\n", *s); + freebuf(b); + return 0; + } + *s = cp; + insert(b, 0); + return b; +} + +static Word* +varmatch(char *name) +{ + Word *w; + Symtab *sym; + + sym = symlook(name, S_VAR, 0); + if(sym){ + /* check for at least one non-NULL value */ + for (w = sym->u.ptr; w; w = w->next) + if(w->s && *w->s) + return wdup(w); + } + return 0; +} + +static Word* +expandvar(char **s) +{ + Word *w; + Bufblock *buf; + Symtab *sym; + char *cp, *begin, *end; + + begin = *s; + (*s)++; /* skip the '{' */ + buf = varname(s); + if (buf == 0) + return 0; + cp = *s; + if (*cp == '}') { /* ${name} variant*/ + (*s)++; /* skip the '}' */ + w = varmatch(buf->start); + freebuf(buf); + return w; + } + if (*cp != ':') { + SYNERR(-1); + fprint(2, "bad variable name <%s>\n", buf->start); + freebuf(buf); + return 0; + } + cp++; + end = shellt->charin(cp , "}"); + if(end == 0){ + SYNERR(-1); + fprint(2, "missing '}': %s\n", begin); + Exit(); + } + *end = 0; + *s = end+1; + + sym = symlook(buf->start, S_VAR, 0); + if(sym == 0 || sym->u.ptr == 0) + w = newword(buf->start); + else + w = subsub(sym->u.ptr, cp, end); + freebuf(buf); + return w; +} + +static Word* +extractpat(char *s, char **r, char *term, char *end) +{ + int save; + char *cp; + Word *w; + + cp = shellt->charin(s, term); + if(cp){ + *r = cp; + if(cp == s) + return 0; + save = *cp; + *cp = 0; + w = stow(s); + *cp = save; + } else { + *r = end; + w = stow(s); + } + return w; +} + +static Word* +subsub(Word *v, char *s, char *end) +{ + int nmid; + Word *head, *tail, *w, *h; + Word *a, *b, *c, *d; + Bufblock *buf; + char *cp, *enda; + + a = extractpat(s, &cp, "=%&", end); + b = c = d = 0; + if(PERCENT(*cp)) + b = extractpat(cp+1, &cp, "=", end); + if(*cp == '=') + c = extractpat(cp+1, &cp, "&%", end); + if(PERCENT(*cp)) + d = stow(cp+1); + else if(*cp) + d = stow(cp); + + head = tail = 0; + buf = newbuf(); + for(; v; v = v->next){ + h = w = 0; + if(submatch(v->s, a, b, &nmid, &enda)){ + /* enda points to end of A match in source; + * nmid = number of chars between end of A and start of B + */ + if(c){ + h = w = wdup(c); + while(w->next) + w = w->next; + } + if(PERCENT(*cp) && nmid > 0){ + if(w){ + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, enda, nmid); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + } else { + bufcpy(buf, enda, nmid); + insert(buf, 0); + h = w = newword(buf->start); + } + buf->current = buf->start; + } + if(d && *d->s){ + if(w){ + + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, d->s, strlen(d->s)); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + w->next = wdup(d->next); + while(w->next) + w = w->next; + buf->current = buf->start; + } else + h = w = wdup(d); + } + } + if(w == 0) + h = w = newword(v->s); + + if(head == 0) + head = h; + else + tail->next = h; + tail = w; + } + freebuf(buf); + delword(a); + delword(b); + delword(c); + delword(d); + return head; +} + +static int +submatch(char *s, Word *a, Word *b, int *nmid, char **enda) +{ + Word *w; + int n; + char *end; + + n = 0; + for(w = a; w; w = w->next){ + n = strlen(w->s); + if(strncmp(s, w->s, n) == 0) + break; + } + if(a && w == 0) /* a == NULL matches everything*/ + return 0; + + *enda = s+n; /* pointer to end a A part match */ + *nmid = strlen(s)-n; /* size of remainder of source */ + end = *enda+*nmid; + for(w = b; w; w = w->next){ + n = strlen(w->s); + if(strcmp(w->s, end-n) == 0){ + *nmid -= n; + break; + } + } + if(b && w == 0) /* b == NULL matches everything */ + return 0; + return 1; +} diff --git a/mk/word.c b/mk/word.c @@ -0,0 +1,189 @@ +#include "mk.h" + +static Word *nextword(char**); + +Word* +newword(char *s) +{ + Word *w; + + w = (Word *)Malloc(sizeof(Word)); + w->s = strdup(s); + w->next = 0; + return(w); +} + +Word * +stow(char *s) +{ + Word *head, *w, *new; + + w = head = 0; + while(*s){ + new = nextword(&s); + if(new == 0) + break; + if (w) + w->next = new; + else + head = w = new; + while(w->next) + w = w->next; + + } + if (!head) + head = newword(""); + return(head); +} + +char * +wtos(Word *w, int sep) +{ + Bufblock *buf; + char *cp; + + buf = newbuf(); + for(; w; w = w->next){ + for(cp = w->s; *cp; cp++) + insert(buf, *cp); + if(w->next) + insert(buf, sep); + } + insert(buf, 0); + cp = strdup(buf->start); + freebuf(buf); + return(cp); +} + +Word* +wdup(Word *w) +{ + Word *v, *new, *base; + + v = base = 0; + while(w){ + new = newword(w->s); + if(v) + v->next = new; + else + base = new; + v = new; + w = w->next; + } + return base; +} + +void +delword(Word *w) +{ + Word *v; + + while(v = w){ + w = w->next; + if(v->s) + free(v->s); + free(v); + } +} + +/* + * break out a word from a string handling quotes, executions, + * and variable expansions. + */ +static Word* +nextword(char **s) +{ + Bufblock *b; + Word *head, *tail, *w; + Rune r; + char *cp; + int empty; + + cp = *s; + b = newbuf(); +restart: + head = tail = 0; + while(*cp == ' ' || *cp == '\t') /* leading white space */ + cp++; + empty = 1; + while(*cp){ + cp += chartorune(&r, cp); + switch(r) + { + case ' ': + case '\t': + case '\n': + goto out; + case '\\': + case '\'': + case '"': + empty = 0; + cp = shellt->expandquote(cp, r, b); + if(cp == 0){ + fprint(2, "missing closing quote: %s\n", *s); + Exit(); + } + break; + case '$': + w = varsub(&cp); + if(w == 0){ + if(empty) + goto restart; + break; + } + empty = 0; + if(b->current != b->start){ + bufcpy(b, w->s, strlen(w->s)); + insert(b, 0); + free(w->s); + w->s = strdup(b->start); + b->current = b->start; + } + if(head){ + bufcpy(b, tail->s, strlen(tail->s)); + bufcpy(b, w->s, strlen(w->s)); + insert(b, 0); + free(tail->s); + tail->s = strdup(b->start); + tail->next = w->next; + free(w->s); + free(w); + b->current = b->start; + } else + tail = head = w; + while(tail->next) + tail = tail->next; + break; + default: + empty = 0; + rinsert(b, r); + break; + } + } +out: + *s = cp; + if(b->current != b->start){ + if(head){ + cp = b->current; + bufcpy(b, tail->s, strlen(tail->s)); + bufcpy(b, b->start, cp-b->start); + insert(b, 0); + free(tail->s); + tail->s = strdup(cp); + } else { + insert(b, 0); + head = newword(b->start); + } + } + freebuf(b); + return head; +} + +void +dumpw(char *s, Word *w) +{ + Bprint(&bout, "%s", s); + for(; w; w = w->next) + Bprint(&bout, " '%s'", w->s); + Bputc(&bout, '\n'); +} diff --git a/troff/FIXES b/troff/FIXES @@ -0,0 +1,821 @@ +March 11, 1994 + + If we are just plain old nroff (and not doing UNICODE) we should + only Lookup characters, not Install when we don't know them. + If we are troff, we Install them anyway + +March 8, 1994 + + Nroff had problems with parsing quoted white space as options or + character code in some terminals tables. Changed by having scanf + include white space when necessary as suggested by Rich. + +March 1, 1994 + + Made sanity check for terminal type depending on the trace level; + trace level set with -tn flag at start up + +22 Feb, 1994 + + More pointer shuffling fixes. + +18 Feb, 1994 + + More disabling of multibyte stuff. Fixed bug in n5.c: casetm didn' + know about the new format in the fontables. + +Feb 17, 1994 + + Removed extra include <setlocale> from n1.c + + Fixed dubious pointer shuffling in n7.c, t10.c & n8.c. Thanks Rich! + +Feb 10, 1994 + + Disabled the multybyte stuff; only plan 9 will get it. + +Jan 24, 1994 + + Fixed nasty bug discovered by td, which caused core dumps on + \D'l-0.002775i 0i' and apparently all numbers closer to 0 + than -.002775. Fixed in storeline() and storeword() (n7.c). + +Dec 16, 1993 + + nroff & troff -N were looking for the TYPESETTER variable, causing + + troff: cannot open /sys/lib/troff/term/tab.202; line 1, file stdin + + fixed my moving getenv("TYPESETTER") to t10.c in t_ptinit(void). + +Dec 3, 1993: + + The sequence \s+2\H'+10' came sometimes out in the wrong order + (x H before s), so there wasn't a difference bewteen \s+2\H'+10' + and \H'+10'\s+2. Now the fonts bits of the CHARHT are used to + register the current pontsize, so we can issue a s10 in t10.c + if needed. A bit sneaky. + + Try to prevent double slashes in path names. Especially under + plan9 things started to look ugly. + + Exception word list now grows dynamic. + +Nov 30, 1993: + + Allow multiple calls to .pi, requested by Rob. + .pi cat + .pi dogs + is now equivalent with + .pi cat | dogs + + + .ab now takes also optional error code: + .ab [n] [string] + If n and string, n is exit code, string is message + If n, n is exit code, ``User Abort, exit code n" is message + If !n and string, standard exit code, string is message + If !n and ! string, standard exit code, "User Abort" is message + +Nov 24, 1993: + + Reordered code to keep the UNASNI scripts happy. + + Nroff dumped core reading terminal tables: apparenty under plan 9, + scanf includes the '\n'; added test for '\0' in parse in n10.c. + + Relative tab settings (.ta +1C +2C) didn't work; anding the + previous value with TABMASK fixes this (caseta). + +Nov 23, 1993: + + Included code, originally done by bwk for plan 9, to handle + multi-byte characters. + +Nov 3, 1993: + + ``pair internal'' two char names by shifting 16 bits. Will allow + the use of 16 bit characters sets (Unicode in plan9 etc.) for + macro's etc. + +Oct 20, 1993: + + Word & line buffers are now dynamic: No more word or line overflow + unless when we run out of memory. + +Oct 11, 1993: + + lost diversion warning pops up regularly with man macro's. Due + to a possible macro coding problem. Triggered by something like + troff -man: + .TP + .TP + foo + .ex + Minimal code: + .di aa + throw away this diversion (aa) while being defined. + .rm aa + .br + .di + + Fixed by disallowing .rm to throw away current diversion. The + rn request will complain with: + + cannot remove diversion aa during definition; etc. + +Sep 29, 1993: + + Some long standing fixes which never went back in the source. + Thanks to Janet & Rich. + +Sep 28, 1993: + + Changed getach() (n1.c), so it does't consider truncated + special characters as (8-bit) ascii. STX ETX ENQ ACK and BELL + are still allowed for the ultimate backwards compatibility. + + Some code changes, so real ANSI compilers like the SGI version + (acc from Sun is a poor excuse for an ANSI compiler) don't + barf. Some compromises (static Tchar wbuf in n9.c) allowed so + the unansified stuff for non-ansi compilers (cc on Sun's) will + work as well. + +Sep 9, 1993: + + Be nice to Gerard. Now also word spaces in .tl and after + tabs/fleids etc. + +Aug 12, 1993: + + Tabs setting can now be humongous. We also allow 99 tabs to + accomodate tbl. As a side effect, NTM buffers are now 1K + +Aug 11, 1993: + + .R register, now contains maximum number of addessable + registers minus the number actually used. + + Small esthetic changes in error messages; removed a statement + which wasn't reached anyway. + +Aug 10, 1993: + + Some more speed hacks: be smarter doing the linear table + lookups in alloc() and finds(). + + The real name of the det diversion size macro is now gd. + +Aug 9, 1993: + + A much faster way to find the end of a string/macro, by + remembering that when defined. + +Aug 6, 1993: + + Slightly more eficient way of skipping to the end of a + string/macro + +Aug 5, 1993: + + Prevent character sign extension for 8-bit charnames diversions + etc. by unpair + +Aug 4, 1993: + + Growing the dynamical macro/strings name space and registers + space (See the experiment of 21 July) now with bigger + increments. Casts added to satisfy non-ANSI compilers. + +Aug 3, 1993: + + Should check return value in alloc (n3.c), to prevent core dump + when memory gets tight. + +July 28, 1993: + + New request: .sg <div> sets the dn and dl registers to the size + of the diversion named in the argument. Doesn't do anything + when the named diversion doesn't exist. The name sg is + temporary until we find a better one. + +July 21, 1993: + + Experiment: Macro space & registers name allocated + dynamically. Note that current reallocation occurs in + increments of 1, to force the code to be executed a lot; a kind + of stress testing. Also, eight bit characters allowed in + macro/string names. + +July 21, 1993: + + Turn on the escape mode if the end macro is called. + +July 20, 1993: + + Tracing mode now default off + + Don't print s stackdump either when a file specfied on the + command line argument cannot be opened + +July 15, 1993: + + Don't print useless line & current file informations when a + file specfied on the command line argument cannot be opened. + + Sun ansi compiler doesn't default adhere to standards. Undid + the kludge in tdef.h + +July 14, 1993: + + Coding error made the tab type R not function properly + +July 12, 1993: + + Fixed a typo in the version stuff, noticed by Rich + +July 9, 1993: + + Added the dwb home configuration stuff, thanks RIch. Also, + NCHARS is big enough. Added a fflush to casetm, so .fm <file> + will be up to date. + +June 25, 1993 (Rich): + + -t option + + reinstated for the sake of compatibility. Some old + shells scripts and man(1) from SunOs want this, sigh + + Compiler and system dependencies + + Some systems pull in sys/types.h via #include <time.h> and then + the compiler complains about two ushort typedefs. Therefore, + ushort is now Ushort (and uchar Uchar). + + The SVID specifies a strdup, POSIX doesn't, anyway, troff + provides its own version, slightly different then the standard + one. A To prevent name clashes with that definion, renamed to + strdupl. + +June 24, 1993 (Rich): + + -V option added for DWB3.4 (rich) + +May 18, 1993: + + Trivial fix (.cf) request for troff -a + + issuing + + .cf /dev/null + + with troff -a gives some spurious output: + + H720 + H720 + s10 + f1 + + fixed by checking for ascii mode it ptesc(), ptps() and + ptfont() in t10.c + + + Enhancement + + Added a .tm request to roff. Works just like .tm, but now + it will do it to file. The name is coined by Carmela. Great + for creating indeces & toc's (we hope). + +May 18 1993: + + Compatibilty change + + Somebody complained that his favorite macro didn't work: + it had a BELL (^G) in the name. This was a non-documented + feature of earlier versions of troff (although the + documentation actually doesn't say that you can. (They can + only be used for delimiters or with the tr request), so it + isn't that important). + + But the sake of eternal backward compatibilaty I allowed + some control characters like, STX, ACK, etc. also be part + of a macro/string name. + + While at it, I made it also possible to have eight bit + characters be part of the name. It might be that this screws + up the way users think about these things. For UNICODE + versions, they probably want to do that as well, and that + won't work as easy, (because these characters are 16-bits + wide), so it is dubious whether we actually want this. + + BTW. Now + + .de \(ts\ts + .tm terminal sigma macro + .. + .\(ts\(ts + + also works, as long the internal cookie for ts isn't more then + eight bits. + +May 12, 1993: + + Syntax change + + Some requests accept tabs as a separator, some don't and + this can be a nuisance. Now a tab is also recognized as + an argument separator for requests, this makes + + .so /dev/null + + works. + + To be more precise, any motion character is allowed, so + + .so\h'5i'/dev/null + + will work as well, if one really wants that. + + It will be a problem for users who really relied on this as in + + .ds x string + + and expect the tab to become part of the string a, but I haven't + seen any use of that (obscure trick). + +May 6, 1993: + + Eileen count fixed + + Troff sometimes went in a loop, and exited with: ``job + looping; check abuse of macros'' (also known as the Eileen's + loop). It can be forced with the next trivial programme: + + .de ff + .di xx + .. + .wh -1 ff + .bp + + Basically what happens is that a page transition now will + happen in a diversion, which doesn't make sense. Wat really + happens is that eject() (in n7.c) doesn't eject the frame + because we are in a diversion. This cause the loop in n1.c + (because now always stack->pname <= ejl). Adding check on + whether we are not in a diversion takes care of the problem. + +March 30, 1993: + + Need request, .ne + + When there is a begin of page trap set, and the first thing + in the file is a .ne request, the trap gets fired, but, + the x font R etc. cookies doen't come out, because the + troff thinks that the first page pseudo transition already + took place. Fixed by forcing the start of the first page + in the casene request with the same code as in casetl (which + caused a similar problem quite some time ago). + + Change to .cf request ``Here document'' + + If the argument of .cf starts with a <<, the rest of it is taken + as an EOF token. It will reat the rest of the input until it hits + the EOF token and copies it to the output. This is similar as + the shell's ``here document'' mechanisme and put in place to + improve the kludgy way picasso, picpack etc. now include + postscript. + + Using troff -TLatin1 (DWB version) and \N'...' caused core dump + + In t11, in chadd, it should test on NCHARS - ALPHABET to see + whether we run out of table space (and we probably should beaf + up NCHARS for the DWB version). + +March 16, 1993: + + Diversion rename bug fix + + It is possible to get troff in an infinite loop by renaming a + diversion in progress, and calling it later with the + new name (as in .di xx, .rn xx yy, .yy). The effect depends on + whether troff already put stuff in the diversion or not. + + Fix by having .rn also rename the current diversion (if + there is any and when appropriate). If the diversion calls + itself by the new name and given the fix made on 11 nov + 1992, this will now result in an error. (BTW, the fix from + 11 nov is improved: diversions nest, so we have to account + for that). + +December 18, 1992: + Some people have complete novels as comments, so we need + to skip comments while checking the legality of font files. + thaks Rixh + +December 16, 1992 + + Some people rely on the order that -r arguments are given, + so that troff -rC1 -rC3 ends up setting register C to 3. + Because cpushback() pushes things in a LIFO order back, we + have to do the same to get -r args in a FIFO order. + +Nov 17, 1992: + + Giving a -rL8 option cuased the string .nr L 8 to be printed + on the output, using the wonderful 3b2. Some garbage was + left in buf[100] in main(). Fixed by setting buf[0] explicitly + to 0 (because some C-compilers complain about ``no automatic + aggregate initialization''). + +Nov 11, 1992: + + Diversion bug fix + + If a diversion was being read and the input is faulty so + the diversion was reading in itself, it caused troff to + loop undefinitely. This was easily fixed by a test in + control(a,b) in n1.c. + + Something similar things might happen with macros causing + the ``eileenct problem'', but I didn't look for that. We + have to wait until it happens. + +Oct 26, 1992: + + Numeric arguments: + + Illegal argments are treated as missing arguments. This + changed the semantics of .ll, .ls, .in, .lg, .ul, .cu .lt + (which acted as if the argument was 0) and .ps which was + simply ignored with an illegal argument. + + Tidied up number parsing in atoi1(). This prevents arguments + like .x or 1.2.3.4 being interpret as a legal number (nonumb = 0) + + Numeric arguments error reporting: + + Controlled by .pt, illegal numbers are now reported (default + trace mode is 1). This is also true for the escapes: + \h'..', \v'..' \H'..', \S'..', \N'..', \D'..', \l'.., \L'.. + and \x'..'. + + \D'c' is the only drawing request which doesn't take a pair + of numbers as arguments, so a special case is put here in + setdraw() (This code actually could use an overhaul to get + better parsing. As long as the \D'..' cookies are machine + generated it is low on the priority list). + + Don't generate an error if the illegal argument to a request + is a \}. It is too painful to do right (although it can be + done, but it would clutter getch() and getcho() even more). + + Input line numbers (.c register) bug fixes: + + In not taken branches of .if or .ie, the input line # + (numtab[CD].val) should be raised when necessary (in eatblk()). + + For concealed newlines, we still should count the line for input. + + Setfield (n9.c) sometimes pushes the rest of the line back to + the input (including \n), without adjusting numtab[CD].val + + Because .c (and so numtab[CD].val) is the number of lines read + and the error might actually happen in the current line + (before seeing the '\n), we need to apply correction in + errprint when nlflg set. (This correction needs to be undone + when inside a macro because the nlflg is set by reading the + args to the macro). + + Line number setting (.lf) request bug fixes: + + I interpret that the .c register will contain the number of + read lines, not including the current one. + + Also, don't change the input line number when the first + argument of .lf is not a number. + + As a net effect, the next input + + .EQ + .EN + .ab + + will generate the same output whether eqn has been used or not. + + If request bug fix: + + A ``.if page .tm foo'' caused the next line being ignored; + This bcause when the 2nd delimiter of a string couldn't be + found in cmpstr, the next line was always eaten. Solution: + in caseif1, if the condition is false, we should check + nlflg before eating a block. (Note: We might have eaten + \{\ as well. We could disallow the \{\ in a string to be + compared to prevent that but that might break other things). + + Enhancement to .pt: + + The .pt now pops the previous values when no argument is + specified. Turned out to be handy when chasing for problems. + Just ``bracked'' the code with .pt 7 and .pt and you get + a trace of only that block. The meaning of the arguments + is now: + 01 trace numeric arguments (default on) + 02 trace requests + 04 trace macros + + Abort request (.ab) beautification: + + Don't print the extra carriage return when .ab is called + without an argument. + +Oct 12, 1992: + + (Comments & spelling errors from this day on by jaap) + + replaced 32767 by INT_MAX in several places to allow for very + long pages (on 32-but machines). + + The ``.fp 1 R \"COMMENT'' complains about ``./troff: Can't + open font file /usr/lib/font/devpost/h'' on some systems. It + sees the tab as part of the optional font file. Apparently it + is system dependent whether isgraph() includes the tab + character. Fixed by using getach() in getname() in n1.c + instead. + +Aug 28, 1992: + removed call to popi from rdtty(); it was eating up the + rest of the macro if it was used from within one. (thanks, jaap) + + +Jul 21, 1992: + added extra test in nextfile() to pop current input file + only if not in .nx command. thanks to jaap. + + added test in getword() to avoid hyphenating after \z character, + which prevents any hyphenation inside \X'...'. thanks to jaap. + + added, then removed, code in getword() to prevent hyphenating + anything shorter than 6 characters. looks like it changed a + lot more than i thought. + +Jul 12, 1992: + added .pt request to trace macros and requests (from jaap). + .pt N Print trace of macros (N=1), requests (N=2) or both (N=3) + +Jun 5, 1992: + added tests to t.twrest and t.twinit to avoid 0 deref in + n2 and n10, for nroff -t xxxxx. thanks to Rich Drechsler. + +May 22, 1992: + added extern decls to e.g., void Tchar (*hmot)(void) in tdef.h + and added definition to ni.c, so pointers are defined explicitly. + makes it work on turbo c++ and probably others. + + changed a couple of isdigit's and isgraph(getch()) to avoid + multiple evaluation (even though it shouldn't happen). + + Made /usr/bin/nroff a shell script. + +May 12, 1992: + n1.c: need p++ after strrchr to skip / in program name. + thanks to Rich Drechsler. + +Apr 17, 1992: + casefi(), n5.c: .u register should be 0 or 1, not incremented + with each .fi. + +Apr 5, 1992: + fiddled n7.c and added _nmwid to the environment, to add a + 5th argument to .nm: the maximum number of digits in any + line number. default is 3, which was previously hardwired in. + + added jaap's code for yet another register which actually delivers + a string, called .S (so it can easily go in the switch in setn() + in n4.c); it delivers the current tabstop and alignment modes in + a format suitable for a subsequent .ta \n(.S command: + .ds T \n(.S + ... + .ta \*T + +Mar 30, 1992: + added test in getword to avoid hyphenating things with motions + (and avoid a core dump sometimes too). + +Mar 13, 1992: + \n(sb initialized wrong in setwd(). + + TYPESETTER=foo troff -Tpost used foo instead of post. + +Mar 12, 1992: + rearranged tests in popf so that .so is closed properly before + moving on to the next macro package. + +Mar 1, 1992: + input mechanism rearranged to use getc() instead of stack of + explicit input buffers. 5-10% slowdown. + +Jan 28, 1992: + fixed .tm \(mi to print something sensible. thanks to jaap. + +Jan 2, 1992: + fiddle setfp so doesn't put out font stuff if -a turned on. + +Dec 17, 1991: + copy 3rd argument in .fp commands to x font ... lines when it contains + a /, for testing fonts locally. + +Dec 13, 1991: + parameterize the font directories, etc., so can be set in makefiles. + added -N argument to run as nroff. + +Nov 8, 1991: + add a maplow(towlower...) in n8.c to handle brain-damaged libraries. + +Nov 2, 1991: + merged nroff into troff, based on Ken's plan 9 version. + merged nii.c into ni.c, removed tw.h, etc. more work needed + to make this stuff cleaner. + +July 27, 1991: + added test in setn in n4 to fix bug that permitted things like + \n (ab to work "properly". thanks to jaap for finding and fixing. + + added paranoid testing in t11 to make sure font files look ok. + +May 13, 1991: + moved evaluation of \(xx from copy mode to non-copy mode, so that + weird character names wouldn't get reevaluated in argument parsing. + installed july 27. + +May 6, 1991: + increased size of hyphenation exception buffer to 512 from 128 + +Apr 14, 1991: + added an extra redundant call of ptfont in setfp, since it appears + that some versions of adobe transcript assume that an "x font" command + means to change the actual font as well. the fix preserves the current font. + thanks to david brailsford and friends for spotting the problem. + + fixed up tests in alpha() in n8 to defend isalpha() against too-big inputs. + punct() argument had wrong type too. thanks to rich drexler and peter nelson. + +Mar 19, 1991: + fixed bug that prevented .rd from working with new corebuf organization. + + fixed bug that caused .ig inside diversions to give bad storage + allocation. thanks to arthur david olson, whose fix was on netnews + 3 years earlier. + +Mar 5, 1991: + huge table sizes for kanji. + +Feb ??, 1991: + working on dealing with large alphabets, notably kanji. + added "defaultwidth" to font descriptions, for characters + not given an explicit width. + +Jan, 1991: + added tex hyphenation, using standard tex data files, but not the + elaborate compressed trie, which is a lot of trouble to save maybe + 40k bytes. this appears to run at exactly the same speed as before. + + so far this stuff reads into a fixed size array; that should change. + it should also be possible to deal with multiple languages. + + the command .ha sets the algorithm. .ha 1 => tex, with troff rules + if tex doesn't hyphenate; .ha 0 gives troff rules, and .ha resets + to the default, which is tex. the hyphenation algorithm is part of + the environment, a nod to a future in which i handle more than one + language. + + replaced the fixed size corebuf array for string/macro storage by + a dynamic structure that can grow. + + this appears to slow things down by maybe 3%. the code is about + the same complexity. + +Dec 27, 1990: + converted to ansi c, based on some work by ken thompson, but not + as thoroughly as he did. there is a shell script unansi and an awk + program cvt that will help you step back in time if you do not have + an ansi c compiler. + + moved the special-name characters up to 256 instead of 128, although + done in terms of ALPHABET, so one can pass 8 bit characters through. + removed lots of 0177's and similar numbers. input is now not filtered, + and if a character with the 8th bit on comes in, it will go out again. + + fixed t11.c to read character names in hex or octal as well as + single-character ascii. + + unknown characters are now carried through with width = spacewidth. + needs a way to set widths. + + removed all signal handling from troff. you signal, you die. + + added -d option to print version number. + +Dec 7, 1990: + .fp 3 V VERYLONGNAME used to truncate the name to 10 chars; fixed. + + increased the limit on FBUFSZ for tables with very long fields. + + changed atoi1() to use double to avoid intermediate overflow. + + moved filenames like /usr/lib/font into tdef.h for easy change. + removed some dreggish definitions. + + cleaned up non-portable error printing stuff; fixed up some messages. + +Dec 12, 1989: + Removed the .! command, an undocumented synonym for .sy. + +Dec 4, 1989: + Another wart to the \X code, to try to preserve blanks in all situations. + +Nov 17, 1989: + A number of small changes preparatory to getting rid of nroff. + The argument -Tnroff or -Tnroff-12 changes some internal values + so that the predicate .if n is true and certain arithmetic operations + are done as if nroff. This design is not yet final. + +Nov 7, 1989: + Fixed hyphenation for nov-ice, ad-vice, de-vice, ser-vice, *-vice. + +Oct 11, 1989: + It is now permitted to do an explicit change to font S. + It is not clear what will break (though nothing seems to have). + +Oct 10, 1989: + Modified flush code to always put out \nH instead of sometimes h. + This makes it easier to parse the output for positioning. + +Sep 9, 1989: + Fixed internal representation of \D'~...' so that it + is immune to .tr ~ and variations. No external change. + +Aug 9, 1989: + Changed .tm so it outputs \e, \%, \-, \&, \(blank). + This might break indexing code. + Only in the new version, as are all subsequent fixes. + +July, 1989: + A major internal change: font information is read in ascii + instead of the weird binary format of makedev (which is now dead). + character names need not all appear in DESC; new names that + appear when a font is used become part of the set of known names. + + There are some flaky bits here (it's conceivable that some \N + number will collide with a real name), and it's probably 10-15% + slower. Tant pis. + + As a by-product, nroff no longer compiles. I'll probably get + back to this, but an alternative is to bag it once and for all. + +May 25, 1989: + Another bug in \l, this time when width is 0. Not installed, + since it's in the new font version. + +Apr 23, 1989: + Fixed bug in n9 that caused core dump with unterminated + \l command, like \l'1.5i + + ptflush no longer called when -a is on. + +Apr 12, 1989: + fixed bug in n2 that failed to suppress printing of \! + output when a -o was in effect. + +Apr 5, 1989: + .fl and \X now cause output of size, font, hpos and vpos. + this is necesary for postprocessors that intend to insert + independent material, such as postscript. + +Feb 1, 1989: + wait for .pi pipe to empty before exiting + +Oct 2, 1988: + default is now -Tpost + +Sep 19, 1988: + added abortive code to handle built-up characters by + passing something through as \D'b...'. never used. + +Jul 4, 1988: + replaced the sbrk nonsense in n3.c by calls to malloc. + + \N now tests against proper font size. + + installed Jaap Akkerhuis's code (mutatis mutandis) for + permitting up to 99 fonts, swapping them into font pos 0 + as needed. fixes the long-standing problem of having + multiple font changes on a single output line. + +Jul 2, 1988: + \X now preserves spaces even when contents are diverted. + + \N code safer -- NTRTAB and NWIDCACHE enlarged. + +Jul 14, 1987: + Fixed obscure bug causing incorrect indentation of .mc output. diff --git a/troff/Makefile b/troff/Makefile @@ -0,0 +1,11 @@ +# mk - mk unix port from plan9 +# Depends on ../lib9 + +TARG = troff + +OFILES = n1.o n2.o n3.o n4.o n5.o t6.o n6.o n7.o n8.o n9.o t10.o\ + n10.o t11.o ni.o hytab.o suftab.o dwbinit.o mbwc.o +MANFILES = troff.1 +CFLAGS = -DUNICODE -DTMACDIR=\"tmac/tmac.\" -DTDEVNAME=\"utf\" -DFONTDIR=\"troff/font\" -DNTERMDIR=\"troff/term/tab.\" -DTEXHYPHENS=\"#9/lib/hyphen.tex\" -DALTHYPHENS=\"lib/hyphen.tex\" -DDWBHOME=\"#9/\" + +include ../std.mk diff --git a/troff/README b/troff/README @@ -0,0 +1,31 @@ +To make troff (actually a.out): + + make + +You will also need to write a driver for your favorite output device. +d202.c provides a model, although it is specialized to a machine no +one has. There are also a variety of postscript drivers that are the +best thing to use if you have a postscript device. + +You will also have to make a DESC file for your typesetter and some +font description files; see dev202 for examples. These describe the +named characters, widths, kerning information, and output codes. + +Nroff is the same program as troff, so you should + + cp a.out /usr/bin/troff + ln /usr/bin/troff /usr/bin/nroff + +or the equivalent. + +You will also need terminal description files for your terminals; see +tab.37, tab.450 and tab.lp for examples. + +Troff uses files that are normally stored in /usr/lib/font; +macro packages are in /usr/lib/tmac; and nroff tables are in +/usr/lib/term. You can edit tdef.h to change these assumptions. + +There have been a few features since the last version, and a number of +significant internal changes. Not all are improvements, of course. +Most of the more recent changes, including bug fixes, are in FIXES, +which you should read also. diff --git a/troff/cvt b/troff/cvt @@ -0,0 +1,45 @@ + +awk ' + +/^{/ { + if (prev != "") { + # comments can be trouble (e.g. ffree()) + if ( (c = match(prev, /\/\*.*\*\/$/)) != 0 ) { + comment = substr(prev, c) + sub(/\/\*.*\*\/$/, "", prev) + } else comment = "" + + x = prev + + # isolate argument list + sub(/^[^(]*\(/, "", x) + sub(/\)[^)]*$/, "", x) + + # find the names in it + n = split(x, args) + arglist = "" + for (i = 2; i <= n; i += 2) + arglist = arglist args[i] + gsub(/\(\*f\)\(Tchar\)/, "f", arglist) # special case for n4.c + gsub(/\[[0-9]+\]/, "", arglist) # for n8.c + gsub(/[*()\[\]]/, "", arglist) # discard noise characters *()[] + gsub(/,/, ", ", arglist) # space nicely + sub(/\(.*\)/, "(" arglist ")", prev) # reconstruct + print prev comment + + # argument declarations + gsub(/,/, ";", x) + gsub(/\(\*f\)\(Tchar\)/, "(*f)()", x) # special case for n4.c + if (x != "") + print "\t" x ";" + } + prev = $0 + next +} + +{ print prev + prev = $0 +} + +END { print prev } +' $* diff --git a/troff/dwbinit.c b/troff/dwbinit.c @@ -0,0 +1,317 @@ +/* + * + * Pathname management routines for DWB C programs. + * + * Applications should initialize a dwbinit array with the string + * pointers and arrays that need to be updated, and then hand that + * array to DWBinit before much else happens in their main program. + * DWBinit calls DWBhome to get the current home directory. DWBhome + * uses the last definition of DWBENV (usually "DWBHOME") in file + * DWBCONFIG (e.g., /usr/lib/dwb3.4) or the value assigned to that + * variable in the environment if the DWBCONFIG file doesn't exist, + * can't be read, or doesn't define DWBENV. + * + * DWBCONFIG must be a simple shell script - comments, a definition + * of DWBHOME, and perhaps an export or echo is about all that's + * allowed. The parsing in DWBhome is simple and makes no attempt + * to duplicate the shell. It only looks for DWBHOME= as the first + * non-white space string on a line, so + * + * # + * # A sample DWBCONFIG shell script + * # + * + * DWBHOME=/usr/add-on/dwb3.4 + * export DWBHOME + * + * means DWBhome would return "/usr/add-on/dwb3.4" for the DWB home + * directory. A DWBCONFIG file means there can only be one working + * copy of a DWB release on a system, which seems like a good idea. + * Using DWBCONFIG also means programs will always include correct + * versions of files (e.g., prologues or macro packages). + * + * Relying on an environment variable guarantees nothing. You could + * execute a version of dpost, but your environment might point at + * incorrect font tables or prologues. Despite the obvious problems + * we've also implemented an environment variable approach, but it's + * only used if there's no DWBCONFIG file. + * + * DWBinit calls DWBhome to get the DWB home directory prefix and + * then marches through its dwbinit argument, removing the default + * home directory and prepending the new home. DWBinit stops when + * it reaches an element that has NULL for its address and value + * fields. Pointers in a dwbinit array are reallocated and properly + * initialized; arrays are simply reinitialized if there's room. + * All pathnames that are to be adjusted should be relative. For + * example, + * + * char *fontdir = "lib/font"; + * char xyzzy[25] = "etc/xyzzy"; + * + * would be represented in a dwbinit array as, + * + * dwbinit allpaths[] = { + * &fontdir, NULL, 0, + * NULL, xyzzy, sizeof(xyzzy), + * NULL, NULL, 0 + * }; + * + * The last element must have NULL entries for the address and + * value fields. The main() routine would then do, + * + * #include "dwbinit.h" + * + * main() { + * + * DWBinit("program name", allpaths); + * ... + * } + * + * Debugging is enabled if DWBDEBUG is in the environment and has + * the value ON. Output is occasionally useful and probably should + * be documented. + * + */ + +#include <u.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +#include "dwbinit.h" + +#ifndef DWBCONFIG +#define DWBCONFIG "/dev/null" +#endif + +#ifndef DWBENV +#define DWBENV "DWBHOME" +#endif + +#ifndef DWBHOME +#define DWBHOME "" +#endif + +#ifndef DWBDEBUG +#define DWBDEBUG "DWBDEBUG" +#endif + +#ifndef DWBPREFIX +#define DWBPREFIX "\\*(.P" +#endif + +/*****************************************************************************/ + +void DWBdebug(dwbinit *ptr, int level) +{ + + char *path; + char *home; + static char *debug = NULL; + +/* + * + * Debugging output, but only if DWBDEBUG is defined to be ON in the + * environment. Dumps general info the first time through. + * + */ + + if ( debug == NULL && (debug = getenv(DWBDEBUG)) == NULL ) + debug = "OFF"; + + if ( strcmp(debug, "ON") == 0 ) { + if ( level == 0 ) { + fprintf(stderr, "Environment variable: %s\n", DWBENV); + fprintf(stderr, "Configuration file: %s\n", DWBCONFIG); + fprintf(stderr, "Default home: %s\n", DWBHOME); + if ( (home = DWBhome()) != NULL ) + fprintf(stderr, "Current home: %s\n", home); + } /* End if */ + + fprintf(stderr, "\n%s pathnames:\n", level == 0 ? "Original" : "Final"); + for ( ; ptr->value != NULL || ptr->address != NULL; ptr++ ) { + if ( (path = ptr->value) == NULL ) { + path = *ptr->address; + fprintf(stderr, " pointer: %s\n", path); + } else fprintf(stderr, " array[%d]: %s\n", ptr->length, path); + if ( level == 0 && *path == '/' ) + fprintf(stderr, " WARNING - absolute path\n"); + } /* End for */ + } /* End if */ + +} /* End of DWBdebug */ + +/*****************************************************************************/ + +extern char *unsharp(char*); + +char *DWBhome(void) +{ + + FILE *fp; + char *ptr; + char *path; + int len; + char buf[200]; + char *home = NULL; + +/* + * + * Return the DWB home directory. Uses the last definition of DWBENV + * (usually "DWBHOME") in file DWBCONFIG (perhaps /usr/lib/dwb3.4) or + * the value assigned to the variable named by the DWBENV string in + * the environment if DWBCONFIG doesn't exist or doesn't define DWBENV. + * Skips the file lookup if DWBCONFIG can't be read. Returns NULL if + * there's no home directory. + * + */ + + if ( (fp = fopen(DWBCONFIG, "r")) != NULL ) { + len = strlen(DWBENV); + while ( fgets(buf, sizeof(buf), fp) != NULL ) { + for ( ptr = buf; isspace((uchar)*ptr); ptr++ ) ; + if ( strncmp(ptr, DWBENV, len) == 0 && *(ptr+len) == '=' ) { + path = ptr + len + 1; + for ( ptr = path; !isspace((uchar)*ptr) && *ptr != ';'; ptr++ ) ; + *ptr = '\0'; + if ( home != NULL ) + free(home); + if ( (home = malloc(strlen(path)+1)) != NULL ) + strcpy(home, path); + } /* End if */ + } /* End while */ + fclose(fp); + } /* End if */ + + if ( home == NULL ) { + if ( (home = getenv(DWBENV)) == NULL ) { + if ( (home = DWBHOME) == NULL || *home == '\0' || *home == ' ' ) + home = NULL; + } /* End if */ + home = unsharp(home); + } /* End if */ + + while (home && *home == '/' && *(home +1) == '/') /* remove extra slashes */ + home++; + return(home); + +} /* End of DWBhome */ + +/*****************************************************************************/ + +void DWBinit(char *prog, dwbinit *paths) +{ + + char *prefix; + char *value; + char *path; + int plen; + int length; + dwbinit *opaths = paths; + +/* + * + * Adjust the pathnames listed in paths, using the home directory + * returned by DWBhome(). Stops when it reaches an element that has + * NULL address and value fields. Assumes pathnames are relative, + * but changes everything. DWBdebug issues a warning if an original + * path begins with a /. + * + * A non-NULL address refers to a pointer, which is reallocated and + * then reinitialized. A NULL address implies a non-NULL value field + * and describes a character array that we only reinitialize. The + * length field for an array is the size of that array. The length + * field of a pointer is an increment that's added to the length + * required to store the new pathname string - should help when we + * want to change character arrays to pointers in applications like + * troff. + * + */ + + if ( (prefix = DWBhome()) == NULL ) { + fprintf(stderr, "%s: no DWB home directory\n", prog); + exit(1); + } /* End if */ + + DWBdebug(opaths, 0); + plen = strlen(prefix); + + for ( ; paths->value != NULL || paths->address != NULL; paths++ ) { + if ( paths->address == NULL ) { + length = 0; + value = paths->value; + } else { + length = paths->length; + value = *paths->address; + } /* End else */ + + length += plen + 1 + strlen(value); /* +1 is for the '/' */ + + if ( (path = malloc(length+1)) == NULL ) { + fprintf(stderr, "%s: can't allocate pathname memory\n", prog); + exit(1); + } /* End if */ + + if ( *value != '\0' ) { + char *eop = prefix; + while(*eop++) + ; + eop -= 2; + if (*value != '/' && *eop != '/') { + sprintf(path, "%s/%s", prefix, value); + } else if (*value == '/' && *eop == '/') { + value++; + sprintf(path, "%s%s", prefix, value); + } else + sprintf(path, "%s%s", prefix, value); + } else + sprintf(path, "%s", prefix); + + if ( paths->address == NULL ) { + if ( strlen(path) >= paths->length ) { + fprintf(stderr, "%s: no room for %s\n", prog, path); + exit(1); + } /* End if */ + strcpy(paths->value, path); + free(path); + } else *paths->address = path; + } /* End for */ + + DWBdebug(opaths, 1); + +} /* End of DWBinit */ + +/*****************************************************************************/ + +void DWBprefix( char *prog, char *path, int length) +{ + + char *home; + char buf[512]; + int len = strlen(DWBPREFIX); + +/* + * + * Replace a leading DWBPREFIX string in path by the current DWBhome(). + * Used by programs that pretend to handle .so requests. Assumes path + * is an array with room for length characters. The implementation is + * not great, but should be good enough for now. Also probably should + * have DWBhome() only do the lookup once, and remember the value if + * called again. + * + */ + + if ( strncmp(path, DWBPREFIX, len) == 0 ) { + if ( (home = DWBhome()) != NULL ) { + if ( strlen(home) + strlen(path+len) < length ) { + sprintf(buf, "%s%s", home, path+len); + strcpy(path, buf); /* assuming there's room in path */ + } else fprintf(stderr, "%s: no room to grow path %s", prog, path); + } /* End if */ + } /* End if */ + +} /* End of DWBprefix */ + +/*****************************************************************************/ + diff --git a/troff/dwbinit.h b/troff/dwbinit.h @@ -0,0 +1,19 @@ +/* + * + * A structure used to adjust pathnames in DWB C code. Pointers + * set the address field, arrays use the value field and must + * also set length to the number elements in the array. Pointers + * are always reallocated and then reinitialized; arrays are only + * reinitialized, if there's room. + * + */ + +typedef struct { + char **address; + char *value; + int length; +} dwbinit; + +extern void DWBinit(char *, dwbinit *); +extern char* DWBhome(void); +extern void DWBprefix(char *, char *, int); diff --git a/troff/ext.h b/troff/ext.h @@ -0,0 +1,187 @@ +#define devname p9_devname + +extern int TROFF; + +extern int alphabet; +extern char **argp; +extern char *eibuf; +extern char *ibufp; +extern char *obufp; +extern char *unlkp; +extern char *xbufp; +extern char *xeibuf; +extern char cfname[NSO+1][NS]; +extern int trace; +extern char devname[]; +extern char ibuf[IBUFSZ]; +extern char mfiles[NMF][NS]; +extern char nextf[]; +extern char obuf[]; +extern char termtab[]; +extern char fontdir[]; +extern Font fonts[MAXFONTS+1]; +extern char xbuf[IBUFSZ]; +extern Offset apptr; +extern Offset ip; +extern Offset nextb; +extern Offset offset; +extern Offset woff; +extern Numerr numerr; +extern int *pnp; +extern int pstab[]; +extern int nsizes; +extern int app; +extern int ascii; +extern int bd; +extern int bdtab[]; +extern int ccs; +extern char *chnames[]; /* chnames[n-ALPHABET] -> name of char n */ +extern int copyf; +extern int cs; +extern int dfact; +extern int dfactd; +extern int diflg; +extern int dilev; +extern int donef; +extern int dotT; +extern int dpn; +extern int ds; +extern int ejf; +extern int em; +extern int eqflg; +extern int error; +extern int esc; +extern int eschar; +extern int ev; +extern int evi; +extern int evlist[EVLSZ]; +extern int fc; +extern int flss; +extern int fontlab[]; +extern int hflg; +extern int ibf; +extern int ifi; +extern int iflg; +extern int init; +extern int lead; +extern int lg; +extern int lgf; +extern int macerr; +extern int mflg; +extern int mfont; +extern int mlist[NTRAP]; +extern int mpts; +extern int nchnames; +extern int ndone; +extern int newmn; +extern int nflush; +extern int nfo; +extern int nfonts; +extern int nform; +extern int nhyp; +extern int nlflg; +extern int nlist[NTRAP]; +extern int nmfi; +extern int nonumb; +extern int noscale; +extern int npn; +extern int npnflg; +extern int nx; +extern int oldbits; +extern int oldmn; +extern int over; +extern int padc; +extern int pfont; +extern int pfrom; +extern int pipeflg; +extern int pl; +extern int pnlist[]; +extern int po1; +extern int po; +extern int ppts; +#define print troffprint +extern int print; +extern FILE *ptid; +extern int pto; +extern int quiet; +extern int ralss; +extern int rargc; +extern int raw; +extern int res; +extern int sbold; +extern int setwdf; +extern int sfont; +extern int smnt; +extern int stdi; +extern int stop; +extern int sv; +extern int tabch, ldrch; +extern int tflg; +extern int totout; +extern int trap; +extern Ushort trtab[]; +extern int tty; +extern int ulfont; +extern int vflag; +extern int whichroff; +extern int widthp; +extern int xfont; +extern int xpts; +extern Stack *ejl; +extern Stack *frame; +extern Stack *stk; +extern Stack *nxf; +extern Tchar **hyp; +extern Tchar *olinep; +extern Tchar pbbuf[NC]; +extern Tchar *pbp; +extern Tchar *lastpbp; +extern Tchar ch; +extern Tchar nrbits; +extern Tbuf _oline; +extern Wcache widcache[]; +extern char gchtab[]; +extern Diver d[NDI]; +extern Diver *dip; + + +extern char xchname[]; +extern short xchtab[]; +extern char *codestr; +extern char *chnamep; +extern short *chtab; +extern int nchtab; + +extern Numtab *numtabp; + +/* these characters are used as various signals or values +/* in miscellaneous places. +/* values are set in specnames in t10.c +*/ + +extern int c_hyphen; +extern int c_emdash; +extern int c_rule; +extern int c_minus; +extern int c_fi; +extern int c_fl; +extern int c_ff; +extern int c_ffi; +extern int c_ffl; +extern int c_acute; +extern int c_grave; +extern int c_under; +extern int c_rooten; +extern int c_boxrule; +extern int c_lefthand; +extern int c_dagger; +extern int c_isalnum; + +/* + * String pointers for DWB pathname management. + */ + +extern char *DWBfontdir; +extern char *DWBntermdir; +extern char *DWBalthyphens; + diff --git a/troff/find b/troff/find @@ -0,0 +1 @@ +grep $1 *.[ch] diff --git a/troff/fns.h b/troff/fns.h @@ -0,0 +1,389 @@ +#define getline p9getline + +/* + * other + */ +#ifdef NOTDEF +int pclose(FILE*); +long filesize(int fd); +int open(char *, int); +int read(int, char *, int); +int lseek(int, long, int); +int close(int); +int getpid(void); +#endif +char *unsharp(char*); + +/* + * c1.c + */ +void init0(void); +void init2(void); +void cvtime(void); +void errprint(void); +int control(int a, int b); +void casept(void); +int getrq(void); +Tchar getch(void); +void setxon(void); +Tchar getch0(void); +Tchar get1ch(FILE *); +void pushback(Tchar *b); +void cpushback(char *b); +int nextfile(void); +int popf(void); +void flushi(void); +int getach(void); +void casenx(void); +int getname(void); +void caseso(void); +void caself(void); +void casecf(void); +void getline(char *s, int n); +void casesy(void); +void getpn(char *a); +void setrpt(void); + +/* + * n2.c + */ +int pchar(Tchar i); +void pchar1(Tchar i); +int pchar2(Tchar i); +int flusho(void); +void casedone(void); +void caseex(void); +void done(int x); +void done1(int x); +void done2(int x); +void done3(int x); +void edone(int x); +void casepi(void); + +/* + * c3.c + */ +void blockinit(void); +char* grow(char *, int, int); +void mnspace(void); +void caseig(void); +void casern(void); +void maddhash(Contab *rp); +void munhash(Contab *mp); +void mrehash(void); +void caserm(void); +void caseas(void); +void caseds(void); +void caseam(void); +void casede(void); +int findmn(int i); +void clrmn(int i); +Offset finds(int mn); +int skip(void); +int copyb(void); +void copys(void); +Offset alloc(void); +void ffree(Offset i); +void wbf(Tchar i); +Tchar rbf(void); +Tchar popi(void); +Offset pushi(Offset newip, int mname); +void* setbrk(int x); +int getsn(void); +Offset setstr(void); +void collect(void); +void seta(void); +void caseda(void); +void casegd(void); +void casedi(void); +void casedt(void); +void casetl(void); +void casepc(void); +void casepm(void); +void stackdump(void); + +/* + * c4.c + */ +void setn(void); +int wrc(Tchar i); +void setn1(int i, int form, Tchar bits); +void nnspace(void); +void nrehash(void); +void nunhash(Numtab *rp); +int findr(int i); +int usedr(int i); +int fnumb(int i, int (*f)(Tchar)); +int decml(int i, int (*f)(Tchar)); +int roman(int i, int (*f)(Tchar)); +int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp); +int abc(int i, int (*f)(Tchar)); +int abc0(int i, int (*f)(Tchar)); +long atoi0(void); +long ckph(void); +long atoi1(Tchar ii); +void caserr(void); +void casenr(void); +void caseaf(void); +void setaf(void); +int vnumb(int *i); +int hnumb(int *i); +int inumb(int *n); +int quant(int n, int m); + +/* + * c5.c + */ +void casead(void); +void casena(void); +void casefi(void); +void casenf(void); +void casers(void); +void casens(void); +int chget(int c); +void casecc(void); +void casec2(void); +void casehc(void); +void casetc(void); +void caselc(void); +void casehy(void); +int max(int aa, int bb); +void casenh(void); +void casece(void); +void casein(void); +void casell(void); +void caselt(void); +void caseti(void); +void casels(void); +void casepo(void); +void casepl(void); +void casewh(void); +void casech(void); +int findn(int i); +void casepn(void); +void casebp(void); +void casextm(void); +void casetm(void); +void casefm(void); +void casetm1(int ab, FILE *out); +void casesp(void); +void casesp1(int a); +void casert(void); +void caseem(void); +void casefl(void); +void caseev(void); +void envcopy(Env *e1, Env *e2); +void caseel(void); +void caseie(void); +void casexif(void); +void caseif(void); +void caseif1(int); +void eatblk(int inblk); +int cmpstr(Tchar c); +void caserd(void); +int rdtty(void); +void caseec(void); +void caseeo(void); +void caseta(void); +void casene(void); +void casetr(void); +void casecu(void); +void caseul(void); +void caseuf(void); +void caseit(void); +void casemc(void); +void casemk(void); +void casesv(void); +void caseos(void); +void casenm(void); +void getnm(int *p, int min); +void casenn(void); +void caseab(void); +void save_tty(void); +void restore_tty(void); +void set_tty(void); +void echo_off(void); +void echo_on(void); + +/* + * t6.c + */ +int t_width(Tchar j); +void zapwcache(int s); +int onfont(int n, int f); +int getcw(int i); +void xbits(Tchar i, int bitf); +Tchar t_setch(int c); +Tchar t_setabs(void); +int t_findft(int i); +void caseps(void); +void casps1(int i); +int findps(int i); +void t_mchbits(void); +void t_setps(void); +Tchar t_setht(void); +Tchar t_setslant(void); +void caseft(void); +void t_setfont(int a); +void t_setwd(void); +Tchar t_vmot(void); +Tchar t_hmot(void); +Tchar t_mot(void); +Tchar t_sethl(int k); +Tchar t_makem(int i); +Tchar getlg(Tchar i); +void caselg(void); +void casefp(void); +char *strdupl(const char *); +int setfp(int pos, int f, char *truename, int print); +void casecs(void); +void casebd(void); +void casevs(void); +void casess(void); +Tchar t_xlss(void); +Uchar* unpair(int i); +void outascii(Tchar i); + +/* + * c7.c + */ +void tbreak(void); +void donum(void); +void text(void); +void nofill(void); +void callsp(void); +void ckul(void); +void storeline(Tchar c, int w); +void newline(int a); +int findn1(int a); +void chkpn(void); +int findt(int a); +int findt1(void); +void eject(Stack *a); +int movword(void); +void horiz(int i); +void setnel(void); +int getword(int x); +void storeword(Tchar c, int w); +Tchar gettch(void); + +/* + * c8.c + */ +void hyphen(Tchar *wp); +int punct(Tchar i); +int alph(int i); +void caseha(void); +void caseht(void); +void casehw(void); +int exword(void); +int suffix(void); +int maplow(int i); +int vowel(int i); +Tchar* chkvow(Tchar *w); +void digram(void); +int dilook(int a, int b, char t[26][13]); + +/* + * c9.c + */ +Tchar setz(void); +void setline(void); +int eat(int c); +void setov(void); +void setbra(void); +void setvline(void); +void setdraw(void); +void casefc(void); +Tchar setfield(int x); + +/* + * t10.c + */ +void t_ptinit(void); +void t_specnames(void); +void t_ptout(Tchar i); +int ptout0(Tchar *pi); +void ptchname(int); +void ptflush(void); +void ptps(void); +void ptfont(void); +void ptfpcmd(int f, char *s, char *fn); +void t_ptlead(void); +void ptesc(void); +void ptpage(int n); +void pttrailer(void); +void ptstop(void); +void t_ptpause(void); + +/* + * t11.c + */ +int getdesc(char *name); +int getfont(char *name, int pos); +int chadd(char *s, int, int); +char* chname(int n); +int getlig(FILE *fin); + +/* + * n6.c + */ +int n_width(Tchar j); +Tchar n_setch(int c); +Tchar n_setabs(void); +int n_findft(int i); +void n_mchbits(void); +void n_setps(void); +Tchar n_setht(void); +Tchar n_setslant(void); +void n_caseft(void); +void n_setfont(int a); +void n_setwd(void); +Tchar n_vmot(void); +Tchar n_hmot(void); +Tchar n_mot(void); +Tchar n_sethl(int k); +Tchar n_makem(int i); +void n_casefp(void); +void n_casebd(void); +void n_casevs(void); +Tchar n_xlss(void); + +/* + * n10.c + */ +void n_ptinit(void); +char* skipstr(char *s); +char* getstr(char *s, char *t); +char* getint(char *s, int *pn); +void twdone(void); +void n_specnames(void); +int findch(char *s); +void n_ptout(Tchar i); +void ptout1(void); +char* plot(char *x); +void move(void); +void n_ptlead(void); +void n_ptpause(void); + +/* + * indirect calls on TROFF/!TROFF. these are variables! + */ +extern Tchar (*hmot)(void); +extern Tchar (*makem)(int i); +extern Tchar (*setabs)(void); +extern Tchar (*setch)(int c); +extern Tchar (*sethl)(int k); +extern Tchar (*setht)(void); +extern Tchar (*setslant)(void); +extern Tchar (*vmot)(void); +extern Tchar (*xlss)(void); +extern int (*findft)(int i); +extern int (*width)(Tchar j); +extern void (*mchbits)(void); +extern void (*ptlead)(void); +extern void (*ptout)(Tchar i); +extern void (*ptpause)(void); +extern void (*setfont)(int a); +extern void (*setps)(void); +extern void (*setwd)(void); diff --git a/troff/hytab.c b/troff/hytab.c @@ -0,0 +1,126 @@ +/* + * Hyphenation digram tables + */ + +typedef unsigned char Uchar; + + +Uchar bxh[26][13] = { + 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0040,0000,0040 +}; + +Uchar hxx[26][13] = { + 0006,0042,0041,0123,0021,0024,0063,0042,0002,0043,0021,0001,0022, + 0140,0000,0200,0003,0260,0006,0000,0160,0007,0000,0140,0000,0320, + 0220,0000,0160,0005,0240,0010,0000,0100,0006,0000,0200,0000,0320, + 0240,0000,0120,0003,0140,0000,0000,0240,0010,0000,0220,0000,0160, + 0042,0023,0041,0040,0040,0022,0043,0041,0030,0064,0021,0000,0041, + 0100,0000,0140,0000,0220,0006,0000,0140,0003,0000,0200,0000,0000, + 0200,0000,0120,0002,0220,0010,0000,0160,0006,0000,0140,0000,0320, + 0020,0000,0020,0000,0020,0000,0000,0020,0000,0000,0020,0000,0000, + 0043,0163,0065,0044,0022,0043,0104,0042,0061,0146,0061,0000,0007, + 0100,0000,0140,0000,0040,0000,0000,0100,0000,0000,0120,0000,0000, + 0140,0000,0040,0011,0060,0004,0001,0120,0003,0000,0140,0000,0040, + 0200,0000,0100,0000,0140,0000,0000,0140,0000,0000,0140,0000,0240, + 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0140,0000,0240, + 0200,0000,0140,0000,0160,0000,0000,0220,0000,0000,0060,0000,0240, + 0021,0043,0041,0121,0040,0023,0042,0003,0142,0042,0061,0001,0022, + 0120,0000,0140,0010,0140,0010,0000,0140,0002,0000,0120,0000,0120, + 0000,0000,0000,0000,0360,0000,0000,0000,0000,0000,0160,0000,0000, + 0100,0000,0040,0005,0120,0000,0000,0100,0000,0000,0060,0000,0140, + 0140,0040,0100,0001,0240,0041,0000,0242,0000,0002,0140,0000,0100, + 0240,0000,0120,0002,0200,0000,0000,0320,0007,0000,0240,0000,0340, + 0101,0021,0041,0020,0040,0005,0042,0121,0002,0021,0201,0000,0020, + 0160,0000,0100,0000,0140,0000,0000,0160,0006,0000,0220,0000,0140, + 0140,0000,0020,0001,0020,0000,0000,0100,0001,0000,0300,0000,0000, + 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, + 0106,0041,0040,0147,0040,0000,0063,0041,0001,0102,0160,0002,0002, + 0300,0000,0040,0017,0140,0017,0000,0240,0000,0000,0140,0000,0120 +}; + +Uchar bxxh[26][13] = { + 0005,0150,0153,0062,0062,0246,0152,0127,0146,0203,0310,0017,0206, + 0100,0000,0120,0000,0140,0000,0000,0100,0000,0000,0120,0000,0060, + 0100,0000,0040,0000,0060,0000,0000,0060,0000,0000,0220,0000,0040, + 0100,0000,0120,0000,0200,0000,0000,0100,0000,0000,0140,0000,0060, + 0043,0142,0046,0140,0062,0147,0210,0131,0046,0106,0246,0017,0111, + 0060,0000,0020,0000,0060,0000,0000,0040,0000,0000,0100,0000,0000, + 0060,0000,0040,0000,0040,0000,0000,0040,0000,0000,0100,0000,0040, + 0100,0000,0100,0000,0100,0000,0000,0040,0000,0000,0100,0000,0140, + 0066,0045,0145,0140,0000,0070,0377,0030,0130,0103,0003,0017,0006, + 0040,0000,0040,0000,0020,0000,0000,0040,0000,0000,0100,0000,0000, + 0200,0000,0020,0000,0140,0000,0000,0120,0000,0000,0120,0000,0040, + 0120,0000,0040,0000,0060,0000,0000,0060,0000,0000,0160,0000,0040, + 0120,0000,0040,0000,0120,0000,0000,0040,0000,0000,0160,0000,0040, + 0120,0000,0020,0000,0140,0000,0000,0120,0000,0000,0140,0000,0040, + 0051,0126,0150,0140,0060,0210,0146,0006,0006,0165,0003,0017,0244, + 0120,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0000,0140, + 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, + 0140,0000,0140,0000,0060,0000,0000,0100,0000,0000,0140,0000,0020, + 0120,0000,0020,0000,0060,0000,0000,0060,0000,0000,0060,0000,0040, + 0140,0000,0020,0000,0100,0000,0000,0140,0000,0000,0140,0000,0020, + 0070,0125,0051,0162,0120,0105,0126,0104,0006,0044,0000,0017,0052, + 0140,0000,0020,0000,0140,0000,0000,0060,0000,0000,0060,0000,0040, + 0020,0000,0000,0000,0020,0000,0000,0000,0000,0000,0000,0000,0060, + 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0000,0240, + 0065,0042,0060,0200,0000,0210,0222,0146,0006,0204,0220,0012,0003, + 0240,0000,0020,0000,0120,0000,0000,0200,0000,0000,0200,0000,0240 +}; + +Uchar xhx[26][13] = { + 0032,0146,0042,0107,0076,0102,0042,0146,0202,0050,0006,0000,0051, + 0036,0377,0057,0013,0057,0366,0377,0057,0001,0377,0057,0000,0040, + 0037,0377,0020,0000,0100,0022,0377,0057,0362,0116,0100,0000,0017, + 0057,0377,0057,0031,0137,0363,0377,0037,0362,0270,0077,0000,0117, + 0074,0142,0012,0236,0076,0125,0063,0165,0341,0046,0047,0000,0024, + 0020,0017,0075,0377,0040,0001,0377,0017,0001,0204,0020,0000,0040, + 0057,0017,0057,0340,0140,0362,0314,0117,0003,0302,0100,0000,0057, + 0057,0357,0077,0017,0100,0366,0314,0057,0342,0346,0037,0000,0060, + 0252,0145,0072,0157,0377,0165,0063,0066,0164,0050,0363,0000,0362, + 0000,0000,0020,0000,0020,0000,0000,0017,0000,0000,0020,0000,0000, + 0117,0017,0237,0377,0200,0354,0125,0110,0004,0257,0000,0000,0300, + 0057,0367,0054,0357,0157,0216,0314,0114,0217,0353,0053,0000,0057, + 0077,0213,0077,0077,0177,0317,0377,0114,0377,0352,0077,0000,0076, + 0077,0213,0077,0077,0157,0177,0377,0054,0377,0352,0117,0000,0075, + 0125,0230,0065,0216,0057,0066,0063,0047,0345,0126,0011,0000,0033, + 0057,0377,0051,0360,0120,0361,0273,0056,0001,0256,0057,0000,0060, + 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, + 0076,0310,0056,0310,0137,0174,0273,0055,0335,0266,0033,0000,0155, + 0077,0157,0057,0360,0057,0063,0042,0024,0077,0206,0020,0000,0040, + 0057,0037,0077,0360,0100,0365,0377,0037,0362,0176,0050,0000,0026, + 0167,0146,0042,0112,0077,0110,0062,0254,0366,0052,0377,0000,0163, + 0060,0000,0040,0000,0120,0000,0377,0060,0012,0000,0037,0000,0257, + 0037,0232,0157,0361,0040,0003,0125,0010,0001,0256,0000,0000,0340, + 0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0377,0017,0277, + 0253,0315,0257,0216,0377,0206,0146,0306,0371,0126,0232,0000,0004, + 0057,0012,0100,0360,0160,0360,0000,0040,0000,0017,0157,0000,0176 +}; + +Uchar xxh[26][13] = { + 0045,0150,0154,0162,0042,0246,0210,0147,0152,0103,0230,0017,0206, + 0100,0000,0040,0000,0140,0000,0000,0100,0000,0021,0120,0017,0060, + 0100,0000,0040,0002,0140,0320,0000,0060,0000,0001,0220,0017,0040, + 0100,0001,0120,0001,0241,0000,0000,0100,0000,0020,0140,0017,0060, + 0023,0162,0046,0142,0022,0207,0210,0131,0052,0106,0250,0017,0110, + 0060,0000,0042,0000,0160,0000,0000,0040,0000,0212,0100,0017,0000, + 0140,0000,0040,0002,0140,0000,0000,0120,0000,0040,0120,0017,0040, + 0100,0000,0100,0000,0140,0001,0021,0140,0000,0046,0100,0017,0140, + 0066,0045,0025,0201,0020,0130,0146,0030,0130,0103,0025,0017,0006, + 0100,0000,0040,0000,0020,0000,0000,0040,0000,0000,0200,0017,0000, + 0200,0000,0020,0001,0140,0000,0000,0140,0000,0000,0120,0017,0040, + 0120,0026,0042,0020,0140,0161,0042,0143,0000,0022,0162,0017,0040, + 0121,0042,0060,0020,0140,0200,0000,0123,0000,0021,0220,0017,0041, + 0121,0042,0060,0120,0140,0200,0000,0123,0000,0021,0160,0017,0041, + 0051,0126,0150,0141,0060,0210,0146,0066,0026,0165,0026,0017,0247, + 0120,0000,0040,0003,0160,0000,0000,0140,0000,0021,0100,0017,0140, + 0000,0000,0000,0000,0200,0000,0000,0000,0000,0000,0000,0017,0000, + 0141,0023,0122,0040,0160,0143,0042,0142,0000,0047,0143,0017,0020, + 0120,0000,0040,0006,0140,0060,0000,0141,0000,0026,0100,0017,0040, + 0140,0000,0020,0007,0100,0000,0000,0140,0000,0001,0140,0017,0020, + 0110,0125,0051,0162,0120,0125,0127,0104,0006,0104,0000,0017,0052, + 0140,0000,0040,0000,0160,0000,0000,0140,0000,0000,0060,0017,0000, + 0040,0005,0020,0000,0040,0313,0231,0030,0000,0140,0000,0017,0056, + 0140,0000,0160,0000,0200,0000,0000,0140,0000,0000,0000,0017,0240, + 0065,0042,0060,0040,0000,0206,0231,0146,0006,0224,0220,0017,0004, + 0240,0000,0020,0000,0140,0000,0000,0220,0000,0000,0200,0017,0141 +}; diff --git a/troff/mbwc.c b/troff/mbwc.c @@ -0,0 +1,165 @@ +#include <stdlib.h> + +/* + * Use the FSS-UTF transformation proposed by posix. + * We define 7 byte types: + * T0 0xxxxxxx 7 free bits + * Tx 10xxxxxx 6 free bits + * T1 110xxxxx 5 free bits + * T2 1110xxxx 4 free bits + * + * Encoding is as follows. + * From hex Thru hex Sequence Bits + * 00000000 0000007F T0 7 + * 00000080 000007FF T1 Tx 11 + * 00000800 0000FFFF T2 Tx Tx 16 + */ + +int +mblen(const char *s, size_t n) +{ + + return mbtowc(0, s, n); +} + +int +mbtowc(wchar_t *pwc, const char *s, size_t n) +{ + int c, c1, c2; + long l; + + if(!s) + return 0; + + if(n < 1) + goto bad; + c = s[0] & 0xff; + if((c & 0x80) == 0x00) { + if(pwc) + *pwc = c; + if(c == 0) + return 0; + return 1; + } + + if(n < 2) + goto bad; + c1 = (s[1] ^ 0x80) & 0xff; + if((c1 & 0xC0) != 0x00) + goto bad; + if((c & 0xE0) == 0xC0) { + l = ((c << 6) | c1) & 0x7FF; + if(l < 0x080) + goto bad; + if(pwc) + *pwc = l; + return 2; + } + + if(n < 3) + goto bad; + c2 = (s[2] ^ 0x80) & 0xff; + if((c2 & 0xC0) != 0x00) + goto bad; + if((c & 0xF0) == 0xE0) { + l = ((((c << 6) | c1) << 6) | c2) & 0xFFFF; + if(l < 0x0800) + goto bad; + if(pwc) + *pwc = l; + return 3; + } + + /* + * bad decoding + */ +bad: + return -1; + +} + +int +wctomb(char *s, wchar_t wchar) +{ + long c; + + if(!s) + return 0; + + c = wchar & 0xFFFF; + if(c < 0x80) { + s[0] = c; + return 1; + } + + if(c < 0x800) { + s[0] = 0xC0 | (c >> 6); + s[1] = 0x80 | (c & 0x3F); + return 2; + } + + s[0] = 0xE0 | (c >> 12); + s[1] = 0x80 | ((c >> 6) & 0x3F); + s[2] = 0x80 | (c & 0x3F); + return 3; +} + +size_t +mbstowcs(wchar_t *pwcs, const char *s, size_t n) +{ + int i, d, c; + + for(i=0; i < n; i++) { + c = *s & 0xff; + if(c < 0x80) { + *pwcs = c; + if(c == 0) + break; + s++; + } else { + d = mbtowc(pwcs, s, 3); + if(d <= 0) + return (size_t)((d<0) ? -1 : i); + s += d; + } + pwcs++; + } + return i; +} + +size_t +wcstombs(char *s, const wchar_t *pwcs, size_t n) +{ + int d; + long c; + char *p, *pe; + char buf[3]; + + p = s; + pe = p+n-3; + while(p < pe) { + c = *pwcs++; + if(c < 0x80) + *p++ = c; + else + p += wctomb(p, c); + if(c == 0) + return p-s; + } + while(p < pe+3) { + c = *pwcs++; + d = wctomb(buf, c); + if(p+d <= pe+3) { + *p++ = buf[0]; + if(d > 1) { + *p++ = buf[2]; + if(d > 2) + *p++ = buf[3]; + } + } + if(c == 0) + break; + } + return p-s; +} + diff --git a/troff/mkfile b/troff/mkfile @@ -0,0 +1,57 @@ +<$PLAN9/src/mkhdr + +TARG=troff +OFILES=n1.$O\ + n2.$O\ + n3.$O\ + n4.$O\ + n5.$O\ + t6.$O\ + n6.$O\ + n7.$O\ + n8.$O\ + n9.$O\ + t10.$O\ + n10.$O\ + t11.$O\ + ni.$O\ + hytab.$O\ + suftab.$O\ + dwbinit.$O\ + mbwc.$O + +HFILES=tdef.h\ + fns.h\ + ext.h\ + dwbinit.h\ + + +<$PLAN9/src/mkone +CFLAGS=-DUNICODE + +TMACDIR='"tmac/tmac."' +FONTDIR='"troff/font"' +NTERMDIR='"troff/term/tab."' +ALTHYPHENS='"lib/hyphen.tex"' +TEXHYPHENS='"#9/lib/hyphen.tex"' +DWBHOME='"#9/"' +TDEVNAME='"utf"' +NDEVNAME='"utf"' + +ni.$O: ni.c $HFILES + $CC $CFLAGS -DTMACDIR=$TMACDIR ni.c + +t10.$O: t10.c $HFILES + $CC $CFLAGS -DTDEVNAME=$TDEVNAME t10.c + +n1.$O: n1.c $HFILES + $CC $CFLAGS -DFONTDIR=$FONTDIR -DNTERMDIR=$NTERMDIR -DTEXHYPHENS=$TEXHYPHENS -DALTHYPHENS=$ALTHYPHENS -DDWBHOME=$DWBHOME n1.c + +n10.$O: n10.c $HFILES + $CC $CFLAGS -DTDEVNAME=$NDEVNAME n10.c + +n8.$O: n8.c $HFILES + $CC $CFLAGS -DTEXHYPHENS=$TEXHYPHENS n8.c + +dwbinit.$O: dwbinit.c + $CC $CFLAGS -DDWBHOME=$DWBHOME dwbinit.c diff --git a/troff/n1.c b/troff/n1.c @@ -0,0 +1,1134 @@ +/* + * n1.c + * + * consume options, initialization, main loop, + * input routines, escape function calling + */ + +#include <u.h> +#include "tdef.h" +#include "fns.h" +#include "ext.h" +#include "dwbinit.h" + +#include <setjmp.h> +#include <time.h> + +char *Version = "March 11, 1994"; + +#ifndef DWBVERSION +#define DWBVERSION "???" +#endif + +char *DWBfontdir = FONTDIR; +char *DWBntermdir = NTERMDIR; +char *DWBalthyphens = ALTHYPHENS; +char *DWBhomedir = ""; + +dwbinit dwbpaths[] = { + &DWBfontdir, NULL, 0, + &DWBntermdir, NULL, 0, + &DWBalthyphens, NULL, 0, + &DWBhomedir, NULL, 0, + NULL, nextf, NS, + NULL, NULL, 0 +}; + +int TROFF = 1; /* assume we started in troff... */ + +jmp_buf sjbuf; +Offset ipl[NSO]; + +static FILE *ifile; +static FILE *ifl[NSO]; /* open input file pointers */ +char cfname[NSO+1][NS] = { "stdin" }; /* file name stack */ +int cfline[NSO]; /* input line count stack */ +char *progname; /* program name (troff or nroff) */ + +int trace = 0; /* tracing mode: default off */ +int trace1 = 0; + +int +main(int argc, char *argv[]) +{ + char *p; + int j; + Tchar i; + char buf[100]; + + ifile = stdin; /* gcc */ + ptid = stdout; + + buf[0] = '\0'; /* make sure it's empty (silly 3b2) */ + progname = argv[0]; + if ((p = strrchr(progname, '/')) == NULL) + p = progname; + else + p++; + DWBinit(progname, dwbpaths); + if (strcmp(p, "nroff") == 0) + TROFF = 0; +#ifdef UNICODE + alphabet = 128; /* unicode for plan 9 */ +#endif /*UNICODE*/ + mnspace(); + nnspace(); + mrehash(); + nrehash(); + numtabp[NL].val = -1; + + while (--argc > 0 && (++argv)[0][0] == '-') + switch (argv[0][1]) { + + case 'N': /* ought to be used first... */ + TROFF = 0; + break; + case 'd': + fprintf(stderr, "troff/nroff version %s\n", Version); + break; + case 'F': /* switch font tables from default */ + if (argv[0][2] != '\0') { + strcpy(termtab, &argv[0][2]); + strcpy(fontdir, &argv[0][2]); + } else { + argv++; argc--; + strcpy(termtab, argv[0]); + strcpy(fontdir, argv[0]); + } + break; + case 0: + goto start; + case 'i': + stdi++; + break; + case 'n': + npn = atoi(&argv[0][2]); + break; + case 'u': /* set emboldening amount */ + bdtab[3] = atoi(&argv[0][2]); + if (bdtab[3] < 0 || bdtab[3] > 50) + bdtab[3] = 0; + break; + case 's': + if (!(stop = atoi(&argv[0][2]))) + stop++; + break; + case 'r': + sprintf(buf + strlen(buf), ".nr %c %s\n", + argv[0][2], &argv[0][3]); + /* not yet cpushback(buf);*/ + /* dotnr(&argv[0][2], &argv[0][3]); */ + break; + case 'm': + if (mflg++ >= NMF) { + ERROR "Too many macro packages: %s", argv[0] WARN; + break; + } + strcpy(mfiles[nmfi], nextf); + strcat(mfiles[nmfi++], &argv[0][2]); + break; + case 'o': + getpn(&argv[0][2]); + break; + case 'T': + strcpy(devname, &argv[0][2]); + dotT++; + break; + case 'a': + ascii = 1; + break; + case 'h': + hflg++; + break; + case 'e': + eqflg++; + break; + case 'q': + quiet++; + save_tty(); + break; + case 'V': + fprintf(stdout, "%croff: DWB %s\n", + TROFF ? 't' : 'n', DWBVERSION); + exit(0); + case 't': + if (argv[0][2] != '\0') + trace = trace1 = argv[0][2]; + break; /* for the sake of compatibility */ + default: + ERROR "unknown option %s", argv[0] WARN; + done(02); + } + +start: + /* + * cpushback maintains a LIFO, so push pack the -r arguments + * in reverse order to maintain a FIFO in case someone did -rC1 -rC3 + */ + if (buf[0]) { + char *p = buf; + while(*p++) + ; + while(p > buf) { + while(strncmp(p, ".nr", 3) != 0) + p--; + cpushback(p); + *p-- = '\0'; + } + } + argp = argv; + rargc = argc; + nmfi = 0; + init2(); + setjmp(sjbuf); +loop: + copyf = lgf = nb = nflush = nlflg = 0; + if (ip && rbf0(ip) == 0 && ejf && frame->pframe <= ejl && dip == d) { + nflush++; + trap = 0; + eject((Stack *)0); + goto loop; + } + i = getch(); + if (pendt) + goto Lt; + if ((j = cbits(i)) == XPAR) { + copyf++; + tflg++; + while (cbits(i) != '\n') + pchar(i = getch()); + tflg = 0; + copyf--; + goto loop; + } + if (j == cc || j == c2) { + if (j == c2) + nb++; + copyf++; + while ((j = cbits(i = getch())) == ' ' || j == '\t') + ; + ch = i; + copyf--; + control(getrq(), 1); + flushi(); + goto loop; + } +Lt: + ch = i; + text(); + if (nlflg) + numtabp[HP].val = 0; + goto loop; +} + + + +void init2(void) +{ + int i; + char buf[100]; + + for (i = NTRTAB; --i; ) + trtab[i] = i; + trtab[UNPAD] = ' '; + iflg = 0; + obufp = obuf; + if (TROFF) + t_ptinit(); + else + n_ptinit(); + mchbits(); + cvtime(); + numtabp[PID].val = getpid(); + numtabp[HP].val = init = 0; + numtabp[NL].val = -1; + nfo = 0; + copyf = raw = 0; + sprintf(buf, ".ds .T %s\n", devname); + cpushback(buf); + sprintf(buf, ".ds .P %s\n", DWBhomedir); + cpushback(buf); + numtabp[CD].val = -1; /* compensation */ + nx = mflg; + frame = stk = (Stack *)setbrk(STACKSIZE); + dip = &d[0]; + nxf = frame + 1; + for (i = 1; i < NEV; i++) /* propagate the environment */ + envcopy(&env[i], &env[0]); + for (i = 0; i < NEV; i++) { + if ((env[i]._word._bufp = (Tchar *)calloc(WDSIZE, sizeof(Tchar))) == NULL) { + ERROR "not enough room for word buffers" WARN; + done2(1); + } + env[i]._word._size = WDSIZE; + if ((env[i]._line._bufp = (Tchar *)calloc(LNSIZE, sizeof(Tchar))) == NULL) { + ERROR "not enough room for line buffers" WARN; + done2(1); + } + env[i]._line._size = LNSIZE; + } + if ((oline = (Tchar *)calloc(OLNSIZE, sizeof(Tchar))) == NULL) { + ERROR "not enough room for line buffers" WARN; + done2(1); + } + olinep = oline; + olnsize = OLNSIZE; + blockinit(); +} + +void cvtime(void) +{ + time_t tt; + struct tm *ltime; + + time(&tt); + ltime = localtime(&tt); + numtabp[YR].val = ltime->tm_year % 100; + numtabp[YR].fmt = 2; + numtabp[MO].val = ltime->tm_mon + 1; /* troff uses 1..12 */ + numtabp[DY].val = ltime->tm_mday; + numtabp[DW].val = ltime->tm_wday + 1; /* troff uses 1..7 */ +} + + + +char errbuf[200]; + +void errprint(void) /* error message printer */ +{ + int savecd = numtabp[CD].val; + + if (!nlflg) + numtabp[CD].val++; + + fprintf(stderr, "%s: ", progname); + fputs(errbuf, stderr); + if (cfname[ifi][0]) + fprintf(stderr, "; %s:%d", cfname[ifi], numtabp[CD].val); + fputs("\n", stderr); + if (cfname[ifi][0]) + stackdump(); + numtabp[CD].val = savecd; +} + + +int control(int a, int b) +{ + int j, k; + extern Contab *contabp; + + numerr.type = RQERR; + numerr.req = a; + if (a == 0 || (j = findmn(a)) == -1) + return(0); + if (contabp[j].f == 0) { + if (trace & TRMAC) + fprintf(stderr, "invoke macro %s\n", unpair(a)); + if (dip != d) + for (k = dilev; k; k--) + if (d[k].curd == a) { + ERROR "diversion %s invokes itself during diversion", + unpair(a) WARN; + edone(0100); + } + nxf->nargs = 0; + if (b) + collect(); + flushi(); + return pushi(contabp[j].mx, a); /* BUG??? all that matters is 0/!0 */ + } + if (b) { + if (trace & TRREQ) + fprintf(stderr, "invoke request %s\n", unpair(a)); + (*contabp[j].f)(); + } + return(0); +} + +void casept(void) +{ + int i; + + noscale++; + if (skip()) + i = trace1; + else { + i = max(inumb(&trace), 0); + if (nonumb) + i = trace1; + } + trace1 = trace; + trace = i; + noscale = 0; +} + + +int getrq(void) +{ + int i, j; + + if ((i = getach()) == 0 || (j = getach()) == 0) + goto rtn; + i = PAIR(i, j); +rtn: + return(i); +} + +/* + * table encodes some special characters, to speed up tests + * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch + */ + +char gchtab[NCHARS] = { + 000,004,000,000,010,000,000,000, /* fc, ldr */ + 001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */ + 000,000,000,000,000,000,000,000, + 000,001,000,001,000,000,000,000, /* FLSS, ESC */ + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,001,000, /* f */ + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000 +}; + +int realcbits(Tchar c) /* return character bits, or MOTCH if motion */ +{ + if (ismot(c)) + return MOTCH; + else + return c & 0xFFFF; +} + +Tchar getch(void) +{ + int k; + Tchar i, j; + +g0: + if (ch) { + i = ch; + if (cbits(i) == '\n') + nlflg++; + ch = 0; + return(i); + } + + if (nlflg) + return('\n'); + i = getch0(); + if (ismot(i)) + return(i); + k = cbits(i); + if (k >= sizeof(gchtab)/sizeof(gchtab[0]) || gchtab[k] == 0) /* nothing special */ + return(i); + if (k != ESC) { + if (k == '\n') { + nlflg++; + if (ip == 0) + numtabp[CD].val++; /* line number */ + return(k); + } + if (k == FLSS) { + copyf++; + raw++; + i = getch0(); + if (!fi) + flss = i; + copyf--; + raw--; + goto g0; + } + if (k == RPT) { + setrpt(); + goto g0; + } + if (!copyf) { + if (k == 'f' && lg && !lgf) { + i = getlg(i); + return(i); + } + if (k == fc || k == tabch || k == ldrch) { + if ((i = setfield(k)) == 0) + goto g0; + else + return(i); + } + if (k == '\b') { + i = makem(-width(' ' | chbits)); + return(i); + } + } + return(i); + } + + k = cbits(j = getch0()); + if (ismot(j)) + return(j); + + switch (k) { + case 'n': /* number register */ + setn(); + goto g0; + case '$': /* argument indicator */ + seta(); + goto g0; + case '*': /* string indicator */ + setstr(); + goto g0; + case '{': /* LEFT */ + i = LEFT; + goto gx; + case '}': /* RIGHT */ + i = RIGHT; + goto gx; + case '"': /* comment */ + while (cbits(i = getch0()) != '\n') + ; + if (ip == 0) + numtabp[CD].val++; /* line number */ + nlflg++; + return(i); + +/* experiment: put it here instead of copy mode */ + case '(': /* special char name \(xx */ + case 'C': /* \C'...' */ + if ((i = setch(k)) == 0) + goto g0; + goto gx; + + case ESC: /* double backslash */ + i = eschar; + goto gx; + case 'e': /* printable version of current eschar */ + i = PRESC; + goto gx; + case '\n': /* concealed newline */ + numtabp[CD].val++; + goto g0; + case ' ': /* unpaddable space */ + i = UNPAD; + goto gx; + case '\'': /* \(aa */ + i = ACUTE; + goto gx; + case '`': /* \(ga */ + i = GRAVE; + goto gx; + case '_': /* \(ul */ + i = UNDERLINE; + goto gx; + case '-': /* current font minus */ + i = MINUS; + goto gx; + case '&': /* filler */ + i = FILLER; + goto gx; + case 'c': /* to be continued */ + i = CONT; + goto gx; + case '!': /* transparent indicator */ + i = XPAR; + goto gx; + case 't': /* tab */ + i = '\t'; + return(i); + case 'a': /* leader (SOH) */ +/* old: *pbp++ = LEADER; goto g0; */ + i = LEADER; + return i; + case '%': /* ohc */ + i = OHC; + return(i); + case 'g': /* return format of a number register */ + setaf(); /* should this really be in copy mode??? */ + goto g0; + case '.': /* . */ + i = '.'; +gx: + setsfbits(i, sfbits(j)); + return(i); + } + if (copyf) { + *pbp++ = j; + return(eschar); + } + switch (k) { + + case 'f': /* font indicator */ + setfont(0); + goto g0; + case 's': /* size indicator */ + setps(); + goto g0; + case 'v': /* vert mot */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + if (i = vmot()) { + return(i); + } + goto g0; + case 'h': /* horiz mot */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + if (i = hmot()) + return(i); + goto g0; + case '|': /* narrow space */ + if (NROFF) + goto g0; + return(makem((int)(EM)/6)); + case '^': /* half narrow space */ + if (NROFF) + goto g0; + return(makem((int)(EM)/12)); + case 'w': /* width function */ + setwd(); + goto g0; + case 'p': /* spread */ + spread++; + goto g0; + case 'N': /* absolute character number */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + if ((i = setabs()) == 0) + goto g0; + return i; + case 'H': /* character height */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + return(setht()); + case 'S': /* slant */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + return(setslant()); + case 'z': /* zero with char */ + return(setz()); + case 'l': /* hor line */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + setline(); + goto g0; + case 'L': /* vert line */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + setvline(); + goto g0; + case 'D': /* drawing function */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + setdraw(); + goto g0; + case 'X': /* \X'...' for copy through */ + setxon(); + goto g0; + case 'b': /* bracket */ + setbra(); + goto g0; + case 'o': /* overstrike */ + setov(); + goto g0; + case 'k': /* mark hor place */ + if ((k = findr(getsn())) != -1) { + numtabp[k].val = numtabp[HP].val; + } + goto g0; + case '0': /* number space */ + return(makem(width('0' | chbits))); + case 'x': /* extra line space */ + numerr.type = numerr.escarg = 0; numerr.esc = k; + if (i = xlss()) + return(i); + goto g0; + case 'u': /* half em up */ + case 'r': /* full em up */ + case 'd': /* half em down */ + return(sethl(k)); + default: + return(j); + } + /* NOTREACHED */ +} + +void setxon(void) /* \X'...' for copy through */ +{ + Tchar xbuf[NC]; + Tchar *i; + Tchar c; + int delim, k; + + if (ismot(c = getch())) + return; + delim = cbits(c); + i = xbuf; + *i++ = XON | chbits; + while ((k = cbits(c = getch())) != delim && k != '\n' && i < xbuf+NC-1) { + if (k == ' ') + setcbits(c, WORDSP); + *i++ = c | ZBIT; + } + *i++ = XOFF | chbits; + *i = 0; + pushback(xbuf); +} + + +char ifilt[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 }; + +Tchar getch0(void) +{ + Tchar i; + +again: + if (pbp > lastpbp) + i = *--pbp; + else if (ip) { + /* i = rbf(); */ + i = rbf0(ip); + if (i == 0) + i = rbf(); + else { + ++ip; + if (pastend(ip)) { + --ip; + rbf(); + } + } + } else { + if (donef || ndone) + done(0); + if (nx || 1) { /* BUG: was ibufp >= eibuf, so EOF test is wrong */ + if (nfo < 0) + ERROR "in getch0, nfo = %d", nfo WARN; + if (nfo == 0) { +g0: + if (nextfile()) { + if (ip) + goto again; + } + } + nx = 0; +#ifdef UNICODE + if (MB_CUR_MAX > 1) + i = get1ch(ifile); + else +#endif /*UNICODE*/ + i = getc(ifile); + if (i == EOF) + goto g0; + if (ip) + goto again; + } +/*g2: */ + if (i >= 040) /* zapped: && i < 0177 */ + goto g4; + i = ifilt[i]; + } + if (cbits(i) == IMP && !raw) + goto again; + if (i == 0 && !init && !raw) { /* zapped: || i == 0177 */ + goto again; + } +g4: + if (ismot(i)) + return i; + if (copyf == 0 && sfbits(i) == 0) + i |= chbits; + if (cbits(i) == eschar && !raw) + setcbits(i, ESC); + return(i); +} + + +#ifdef UNICODE +Tchar get1ch(FILE *fp) /* get one "character" from input, figure out what alphabet */ +{ + wchar_t wc; + char buf[100], *p; + int i, n, c; + + for (i = 0, p = buf; i < MB_CUR_MAX; i++) { + if ((c = getc(fp)) == EOF) + return c; + *p++ = c; + if ((n = mbtowc(&wc, buf, p-buf)) >= 0) + break; + } + + if (n == 1) /* real ascii, presumably */ + return wc; + if (n == 0) + return p[-1]; /* illegal, but what else to do? */ + if (c == EOF) + return EOF; + *p = 0; + return chadd(buf, MBchar, Install); /* add name even if haven't seen it */ +} +#endif /*UNICODE*/ + +void pushback(Tchar *b) +{ + Tchar *ob = b; + + while (*b++) + ; + b--; + while (b > ob && pbp < &pbbuf[NC-3]) + *pbp++ = *--b; + if (pbp >= &pbbuf[NC-3]) { + ERROR "pushback overflow" WARN; + done(2); + } +} + +void cpushback(char *b) +{ + char *ob = b; + + while (*b++) + ; + b--; + while (b > ob && pbp < &pbbuf[NC-3]) + *pbp++ = *--b; + if (pbp >= &pbbuf[NC-3]) { + ERROR "cpushback overflow" WARN; + done(2); + } +} + +int nextfile(void) +{ + char *p; + +n0: + if (ifile != stdin) + fclose(ifile); + if (ifi > 0 && !nx) { + if (popf()) + goto n0; /* popf error */ + return(1); /* popf ok */ + } + if (nx || nmfi < mflg) { + p = mfiles[nmfi++]; + if (*p != 0) + goto n1; + } + if (rargc-- <= 0) { + if ((nfo -= mflg) && !stdi) { + done(0); +} + nfo++; + numtabp[CD].val = stdi = mflg = 0; + ifile = stdin; + strcpy(cfname[ifi], "stdin"); + return(0); + } + p = (argp++)[0]; + if (rargc >= 0) + cfname[ifi][0] = 0; +n1: + numtabp[CD].val = 0; + if (p[0] == '-' && p[1] == 0) { + ifile = stdin; + strcpy(cfname[ifi], "stdin"); + } else if ((ifile = fopen(unsharp(p), "r")) == NULL) { + ERROR "cannot open file %s", p WARN; + nfo -= mflg; + done(02); + } else + strcpy(cfname[ifi],p); + nfo++; + return(0); +} + +int +popf(void) +{ + --ifi; + if (ifi < 0) { + ERROR "popf went negative" WARN; + return 1; + } + numtabp[CD].val = cfline[ifi]; /* restore line counter */ + ip = ipl[ifi]; /* input pointer */ + ifile = ifl[ifi]; /* input FILE * */ + return(0); +} + + +void flushi(void) +{ + if (nflush) + return; + ch = 0; + copyf++; + while (!nlflg) { + if (donef && frame == stk) + break; + getch(); + } + copyf--; +} + +/* + * return 16-bit, ascii/alphabetic character, ignore chars with more bits, + * (internal names), spaces and special cookies (below 040). + * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff. + */ +int +getach(void) +{ + Tchar i; + int j; + + lgf++; + j = cbits(i = getch()); + if (ismot(i) + || j > SHORTMASK + || (j <= 040 && j != 002 /*STX*/ + && j != 003 /*ETX*/ + && j != 005 /*ENQ*/ + && j != 006 /*ACK*/ + && j != 007)) { /*BELL*/ + ch = i; + j = 0; + } + lgf--; + return j; +} + + +void casenx(void) +{ + lgf++; + skip(); + getname(); + nx++; + if (nmfi > 0) + nmfi--; + strcpy(mfiles[nmfi], nextf); + nextfile(); + nlflg++; + ip = 0; + pendt = 0; + frame = stk; + nxf = frame + 1; +} + +int +getname(void) +{ + int j, k; + + lgf++; + for (k = 0; k < NS - 1; k++) { + j = getach(); + if (!j) + break; + nextf[k] = j; + } + nextf[k] = 0; + lgf--; + return(nextf[0]); +} + + +void caseso(void) +{ + FILE *fp = 0; + + lgf++; + nextf[0] = 0; + if (skip() || !getname() || (fp = fopen(unsharp(nextf), "r")) == NULL || ifi >= NSO) { + ERROR "can't open file %s", nextf WARN; + done(02); + } + strcpy(cfname[ifi+1], nextf); + cfline[ifi] = numtabp[CD].val; /*hold line counter*/ + numtabp[CD].val = 0; + flushi(); + ifl[ifi] = ifile; + ifile = fp; + ipl[ifi] = ip; + ip = 0; + nx++; + nflush++; + ifi++; +} + +void caself(void) /* set line number and file */ +{ + int n; + + if (skip()) + return; + n = atoi0(); + if (!nonumb) + cfline[ifi] = numtabp[CD].val = n - 1; + if (!skip()) + if (getname()) { /* eats '\n' ? */ + strcpy(cfname[ifi], nextf); + if (!nonumb) + numtabp[CD].val--; + } +} + +void cpout(FILE *fin, char *token) +{ + int n; + char buf[1024]; + + if (token) { /* BUG: There should be no NULL bytes in input */ + char *newl = buf; + while ((fgets(buf, sizeof buf, fin)) != NULL) { + if (newl) { + numtabp[CD].val++; /* line number */ + if (strcmp(token, buf) == 0) + return; + } + newl = strchr(buf, '\n'); + fputs(buf, ptid); + } + } else { + while ((n = fread(buf, sizeof *buf, sizeof buf, fin)) > 0) + fwrite(buf, n, 1, ptid); + fclose(fin); + } +} + +void casecf(void) +{ /* copy file without change */ + FILE *fd; + char *eof, *p; + extern int hpos, esc, po; + + /* this may not make much sense in nroff... */ + + lgf++; + nextf[0] = 0; + if (!skip() && getname()) { + if (strncmp("<<", nextf, 2) != 0) { + if ((fd = fopen(unsharp(nextf), "r")) == NULL) { + ERROR "can't open file %s", nextf WARN; + done(02); + } + eof = (char *) NULL; + } else { /* current file */ + if (pbp > lastpbp || ip) { + ERROR "casecf: not reading from file" WARN; + done(02); + } + eof = &nextf[2]; + if (!*eof) { + ERROR "casecf: missing end of input token" WARN; + done(02); + } + p = eof; + while(*++p) + ; + *p++ = '\n'; + *p = 0; + fd = ifile; + } + } else { + ERROR "casecf: no argument" WARN; + lgf--; + return; + } + lgf--; + + /* make it into a clean state, be sure that everything is out */ + tbreak(); + hpos = po; + esc = 0; + ptesc(); /* to left margin */ + esc = un; + ptesc(); + ptlead(); + ptps(); + ptfont(); + flusho(); + cpout(fd, eof); + ptps(); + ptfont(); +} + +void getline(char *s, int n) /* get rest of input line into s */ +{ + int i; + + lgf++; + copyf++; + skip(); + for (i = 0; i < n-1; i++) + if ((s[i] = cbits(getch())) == '\n' || s[i] == RIGHT) + break; + s[i] = 0; + copyf--; + lgf--; +} + +void casesy(void) /* call system */ +{ + char sybuf[NTM]; + + getline(sybuf, NTM); + system(sybuf); +} + + +void getpn(char *a) +{ + int n, neg; + + if (*a == 0) + return; + neg = 0; + for ( ; *a; a++) + switch (*a) { + case '+': + case ',': + continue; + case '-': + neg = 1; + continue; + default: + n = 0; + if (isdigit((uchar)*a)) { + do + n = 10 * n + *a++ - '0'; + while (isdigit((uchar)*a)); + a--; + } else + n = 9999; + *pnp++ = neg ? -n : n; + neg = 0; + if (pnp >= &pnlist[NPN-2]) { + ERROR "too many page numbers" WARN; + done3(-3); + } + } + if (neg) + *pnp++ = -9999; + *pnp = -INT_MAX; + print = 0; + pnp = pnlist; + if (*pnp != -INT_MAX) + chkpn(); +} + + +void setrpt(void) +{ + Tchar i, j; + + copyf++; + raw++; + i = getch0(); + copyf--; + raw--; + if ((long) i < 0 || cbits(j = getch0()) == RPT) + return; + while (i > 0 && pbp < &pbbuf[NC-3]) { + i--; + *pbp++ = j; + } +} diff --git a/troff/n10.c b/troff/n10.c @@ -0,0 +1,549 @@ +/* +n10.c + +Device interfaces +*/ + +#include <u.h> +#include "tdef.h" +#include "ext.h" +#include "fns.h" +#include <ctype.h> + +Term t; /* terminal characteristics */ + +int dtab; +int plotmode; +int esct; + +enum { Notype = 0, Type = 1 }; + +static char *parse(char *s, int typeit) /* convert \0, etc to nroff driving table format */ +{ /* typeit => add a type id to the front for later use */ + static char buf[100], *t, *obuf; + int quote = 0; + wchar_t wc; + + obuf = typeit == Type ? buf : buf+1; +#ifdef UNICODE + if (mbtowc(&wc, s, strlen(s)) > 1) { /* it's multibyte, */ + buf[0] = MBchar; + strcpy(buf+1, s); + return obuf; + } /* so just hand it back */ +#endif /*UNICODE*/ + buf[0] = Troffchar; + t = buf + 1; + if (*s == '"') { + s++; + quote = 1; + } + for (;;) { + if (quote && *s == '"') { + s++; + break; + } + if (!quote && (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\0')) + break; + if (*s != '\\') + *t++ = *s++; + else { + s++; /* skip \\ */ + if (isdigit((uchar)s[0]) && isdigit((uchar)s[1]) && isdigit((uchar)s[2])) { + *t++ = (s[0]-'0')<<6 | (s[1]-'0')<<3 | s[2]-'0'; + s += 2; + } else if (isdigit((uchar)s[0])) { + *t++ = *s - '0'; + } else if (*s == 'b') { + *t++ = '\b'; + } else if (*s == 'n') { + *t++ = '\n'; + } else if (*s == 'r') { + *t++ = '\r'; + } else if (*s == 't') { + *t++ = '\t'; + } else { + *t++ = *s; + } + s++; + } + } + *t = '\0'; + return obuf; +} + + +static int getnrfont(FILE *fp) /* read the nroff description file */ +{ + Chwid chtemp[NCHARS]; + static Chwid chinit; + int i, nw, n, wid, code, type; + char buf[100], ch[100], s1[100], s2[100]; + wchar_t wc; + + code = 0; + chinit.wid = 1; + chinit.str = ""; + for (i = 0; i < ALPHABET; i++) { + chtemp[i] = chinit; /* zero out to begin with */ + chtemp[i].num = chtemp[i].code = i; /* every alphabetic character is itself */ + chtemp[i].wid = 1; /* default ascii widths */ + } + skipline(fp); + nw = ALPHABET; + while (fgets(buf, sizeof buf, fp) != NULL) { + sscanf(buf, "%s %s %[^\n]", ch, s1, s2); + if (!eq(s1, "\"")) { /* genuine new character */ + sscanf(s1, "%d", &wid); + } /* else it's a synonym for prev character, */ + /* so leave previous values intact */ + + /* decide what kind of alphabet it might come from */ + + if (strlen(ch) == 1) { /* it's ascii */ + n = ch[0]; /* origin includes non-graphics */ + chtemp[n].num = ch[0]; + } else if (ch[0] == '\\' && ch[1] == '0') { + n = strtol(ch+1, 0, 0); /* \0octal or \0xhex */ + chtemp[n].num = n; +#ifdef UNICODE + } else if (mbtowc(&wc, ch, strlen(ch)) > 1) { + chtemp[nw].num = chadd(ch, MBchar, Install); + n = nw; + nw++; +#endif /*UNICODE*/ + } else { + if (strcmp(ch, "---") == 0) { /* no name */ + sprintf(ch, "%d", code); + type = Number; + } else + type = Troffchar; +/* BUG in here somewhere when same character occurs twice in table */ + chtemp[nw].num = chadd(ch, type, Install); + n = nw; + nw++; + } + chtemp[n].wid = wid; + chtemp[n].str = strdupl(parse(s2, Type)); + } + t.tfont.nchars = nw; + t.tfont.wp = (Chwid *) malloc(nw * sizeof(Chwid)); + if (t.tfont.wp == NULL) + return -1; + for (i = 0; i < nw; i++) + t.tfont.wp[i] = chtemp[i]; + return 1; +} + + +void n_ptinit(void) +{ + int i; + char *p; + char opt[50], cmd[100]; + FILE *fp; + + hmot = n_hmot; + makem = n_makem; + setabs = n_setabs; + setch = n_setch; + sethl = n_sethl; + setht = n_setht; + setslant = n_setslant; + vmot = n_vmot; + xlss = n_xlss; + findft = n_findft; + width = n_width; + mchbits = n_mchbits; + ptlead = n_ptlead; + ptout = n_ptout; + ptpause = n_ptpause; + setfont = n_setfont; + setps = n_setps; + setwd = n_setwd; + + if ((p = getenv("NROFFTERM")) != 0) + strcpy(devname, p); + if (termtab[0] == 0) + strcpy(termtab,DWBntermdir); + if (fontdir[0] == 0) + strcpy(fontdir, ""); + if (devname[0] == 0) + strcpy(devname, NDEVNAME); + pl = 11*INCH; + po = PO; + hyf = 0; + ascii = 1; + lg = 0; + fontlab[1] = 'R'; + fontlab[2] = 'I'; + fontlab[3] = 'B'; + fontlab[4] = PAIR('B','I'); + fontlab[5] = 'D'; + bdtab[3] = 3; + bdtab[4] = 3; + + /* hyphalg = 0; /* for testing */ + + strcat(termtab, devname); + if ((fp = fopen(unsharp(termtab), "r")) == NULL) { + ERROR "cannot open %s", termtab WARN; + exit(-1); + } + + +/* this loop isn't robust about input format errors. */ +/* it assumes name, name-value pairs..., charset */ +/* god help us if we get out of sync. */ + + fscanf(fp, "%s", cmd); /* should be device name... */ + if (!is(devname) && trace) + ERROR "wrong terminal name: saw %s, wanted %s", cmd, devname WARN; + for (;;) { + fscanf(fp, "%s", cmd); + if (is("charset")) + break; + fscanf(fp, " %[^\n]", opt); + if (is("bset")) t.bset = atoi(opt); + else if (is("breset")) t.breset = atoi(opt); + else if (is("Hor")) t.Hor = atoi(opt); + else if (is("Vert")) t.Vert = atoi(opt); + else if (is("Newline")) t.Newline = atoi(opt); + else if (is("Char")) t.Char = atoi(opt); + else if (is("Em")) t.Em = atoi(opt); + else if (is("Halfline")) t.Halfline = atoi(opt); + else if (is("Adj")) t.Adj = atoi(opt); + else if (is("twinit")) t.twinit = strdupl(parse(opt, Notype)); + else if (is("twrest")) t.twrest = strdupl(parse(opt, Notype)); + else if (is("twnl")) t.twnl = strdupl(parse(opt, Notype)); + else if (is("hlr")) t.hlr = strdupl(parse(opt, Notype)); + else if (is("hlf")) t.hlf = strdupl(parse(opt, Notype)); + else if (is("flr")) t.flr = strdupl(parse(opt, Notype)); + else if (is("bdon")) t.bdon = strdupl(parse(opt, Notype)); + else if (is("bdoff")) t.bdoff = strdupl(parse(opt, Notype)); + else if (is("iton")) t.iton = strdupl(parse(opt, Notype)); + else if (is("itoff")) t.itoff = strdupl(parse(opt, Notype)); + else if (is("ploton")) t.ploton = strdupl(parse(opt, Notype)); + else if (is("plotoff")) t.plotoff = strdupl(parse(opt, Notype)); + else if (is("up")) t.up = strdupl(parse(opt, Notype)); + else if (is("down")) t.down = strdupl(parse(opt, Notype)); + else if (is("right")) t.right = strdupl(parse(opt, Notype)); + else if (is("left")) t.left = strdupl(parse(opt, Notype)); + else + ERROR "bad tab.%s file, %s %s", devname, cmd, opt WARN; + } + + getnrfont(fp); + fclose(fp); + + sps = EM; + ics = EM * 2; + dtab = 8 * t.Em; + for (i = 0; i < 16; i++) + tabtab[i] = dtab * (i + 1); + pl = 11 * INCH; + po = PO; + spacesz = SS; + lss = lss1 = VS; + ll = ll1 = lt = lt1 = LL; + smnt = nfonts = 5; /* R I B BI S */ + n_specnames(); /* install names like "hyphen", etc. */ + if (eqflg) + t.Adj = t.Hor; +} + + +void n_specnames(void) +{ + + int i; + + for (i = 0; spnames[i].n; i++) + *spnames[i].n = chadd(spnames[i].v, Troffchar, Install); + if (c_isalnum == 0) + c_isalnum = NROFFCHARS; +} + +void twdone(void) +{ + if (!TROFF && t.twrest) { + obufp = obuf; + oputs(t.twrest); + flusho(); + if (pipeflg) { + pclose(ptid); + } + restore_tty(); + } +} + + +void n_ptout(Tchar i) +{ + *olinep++ = i; + if (olinep >= &oline[LNSIZE]) + olinep--; + if (cbits(i) != '\n') + return; + olinep--; + lead += dip->blss + lss - t.Newline; + dip->blss = 0; + esct = esc = 0; + if (olinep > oline) { + move(); + ptout1(); + oputs(t.twnl); + } else { + lead += t.Newline; + move(); + } + lead += dip->alss; + dip->alss = 0; + olinep = oline; +} + + +void ptout1(void) +{ + int k; + char *codep; + int w, j, phyw; + Tchar *q, i; + static int oxfont = FT; /* start off in roman */ + + for (q = oline; q < olinep; q++) { + i = *q; + if (ismot(i)) { + j = absmot(i); + if (isnmot(i)) + j = -j; + if (isvmot(i)) + lead += j; + else + esc += j; + continue; + } + if ((k = cbits(i)) <= ' ') { + switch (k) { + case ' ': /*space*/ + esc += t.Char; + break; + case '\033': + case '\007': + case '\016': + case '\017': + oput(k); + break; + } + continue; + } + phyw = w = t.Char * t.tfont.wp[k].wid; + if (iszbit(i)) + w = 0; + if (esc || lead) + move(); + esct += w; + xfont = fbits(i); + if (xfont != oxfont) { + switch (oxfont) { + case ULFONT: oputs(t.itoff); break; + case BDFONT: oputs(t.bdoff); break; + case BIFONT: oputs(t.itoff); oputs(t.bdoff); break; + } + switch (xfont) { + case ULFONT: + if (*t.iton & 0377) oputs(t.iton); break; + case BDFONT: + if (*t.bdon & 0377) oputs(t.bdon); break; + case BIFONT: + if (*t.bdon & 0377) oputs(t.bdon); + if (*t.iton & 0377) oputs(t.iton); + break; + } + oxfont = xfont; + } + if ((xfont == ulfont || xfont == BIFONT) && !(*t.iton & 0377)) { + for (j = w / t.Char; j > 0; j--) + oput('_'); + for (j = w / t.Char; j > 0; j--) + oput('\b'); + } + if (!(*t.bdon & 0377) && ((j = bdtab[xfont]) || xfont == BDFONT || xfont == BIFONT)) + j++; + else + j = 1; /* number of overstrikes for bold */ + if (k < ALPHABET) { /* ordinary ascii */ + oput(k); + while (--j > 0) { + oput('\b'); + oput(k); + } + } else if (k >= t.tfont.nchars) { /* BUG -- not really understood */ +/* fprintf(stderr, "big char %d, name %s\n", k, chname(k)); /* */ + oputs(chname(k)+1); /* BUG: should separate Troffchar and MBchar... */ + } else if (t.tfont.wp[k].str == 0) { +/* fprintf(stderr, "nostr char %d, name %s\n", k, chname(k)); /* */ + oputs(chname(k)+1); /* BUG: should separate Troffchar and MBchar... */ + } else if (t.tfont.wp[k].str[0] == MBchar) { /* parse() puts this on */ +/* fprintf(stderr, "MBstr char %d, name %s\n", k, chname(k)); /* */ + oputs(t.tfont.wp[k].str+1); + } else { + int oj = j; +/* fprintf(stderr, "str char %d, name %s\n", k, chname(k)); /* */ + codep = t.tfont.wp[k].str+1; /* Troffchar by default */ + while (*codep != 0) { + if (*codep & 0200) { + codep = plot(codep); + oput(' '); + } else { + if (*codep == '%') /* escape */ + codep++; + oput(*codep); + if (*codep == '\033') + oput(*++codep); + else if (*codep != '\b') + for (j = oj; --j > 0; ) { + oput('\b'); + oput(*codep); + } + codep++; + } + } + } + if (!w) + for (j = phyw / t.Char; j > 0; j--) + oput('\b'); + } +} + + +char *plot(char *x) +{ + int i; + char *j, *k; + + oputs(t.ploton); + k = x; + if ((*k & 0377) == 0200) + k++; + for (; *k; k++) { + if (*k == '%') { /* quote char within plot mode */ + oput(*++k); + } else if (*k & 0200) { + if (*k & 0100) { + if (*k & 040) + j = t.up; + else + j = t.down; + } else { + if (*k & 040) + j = t.left; + else + j = t.right; + } + if ((i = *k & 037) == 0) { /* 2nd 0200 turns it off */ + ++k; + break; + } + while (i--) + oputs(j); + } else + oput(*k); + } + oputs(t.plotoff); + return(k); +} + + +void move(void) +{ + int k; + char *i, *j; + char *p, *q; + int iesct, dt; + + iesct = esct; + if (esct += esc) + i = "\0"; + else + i = "\n\0"; + j = t.hlf; + p = t.right; + q = t.down; + if (lead) { + if (lead < 0) { + lead = -lead; + i = t.flr; + /* if(!esct)i = t.flr; else i = "\0";*/ + j = t.hlr; + q = t.up; + } + if (*i & 0377) { + k = lead / t.Newline; + lead = lead % t.Newline; + while (k--) + oputs(i); + } + if (*j & 0377) { + k = lead / t.Halfline; + lead = lead % t.Halfline; + while (k--) + oputs(j); + } else { /* no half-line forward, not at line begining */ + k = lead / t.Newline; + lead = lead % t.Newline; + if (k > 0) + esc = esct; + i = "\n"; + while (k--) + oputs(i); + } + } + if (esc) { + if (esc < 0) { + esc = -esc; + j = "\b"; + p = t.left; + } else { + j = " "; + if (hflg) + while ((dt = dtab - (iesct % dtab)) <= esc) { + if (dt % t.Em) + break; + oput(TAB); + esc -= dt; + iesct += dt; + } + } + k = esc / t.Em; + esc = esc % t.Em; + while (k--) + oputs(j); + } + if ((*t.ploton & 0377) && (esc || lead)) { + oputs(t.ploton); + esc /= t.Hor; + lead /= t.Vert; + while (esc--) + oputs(p); + while (lead--) + oputs(q); + oputs(t.plotoff); + } + esc = lead = 0; +} + + +void n_ptlead(void) +{ + move(); +} + + +void n_ptpause(void ) +{ + char junk; + + flusho(); + read(2, &junk, 1); +} diff --git a/troff/n2.c b/troff/n2.c @@ -0,0 +1,325 @@ +/* + * n2.c + * + * output, cleanup + */ + +#define _BSD_SOURCE 1 /* popen */ +#include "tdef.h" +#include "fns.h" +#include "ext.h" +#include <setjmp.h> + +#ifdef STRICT + /* not in ANSI or POSIX */ +FILE* popen(char*, char*); +#endif + + +extern jmp_buf sjbuf; +int toolate; +int error; + +char obuf[2*BUFSIZ]; +char *obufp = obuf; + + /* pipe command structure; allows redicously long commends for .pi */ +struct Pipe { + char *buf; + int tick; + int cnt; +} Pipe; + + +int xon = 0; /* records if in middle of \X */ + +int pchar(Tchar i) +{ + int j; + static int hx = 0; /* records if have seen HX */ + + if (hx) { + hx = 0; + j = absmot(i); + if (isnmot(i)) { + if (j > dip->blss) + dip->blss = j; + } else { + if (j > dip->alss) + dip->alss = j; + ralss = dip->alss; + } + return 0; + } + if (ismot(i)) { + pchar1(i); + return 0; + } + switch (j = cbits(i)) { + case 0: + case IMP: + case RIGHT: + case LEFT: + return 0; + case HX: + hx = 1; + return 0; + case XON: + xon++; + break; + case XOFF: + xon--; + break; + case PRESC: + if (!xon && !tflg && dip == &d[0]) + j = eschar; /* fall through */ + default: + setcbits(i, trtab[j]); + } + if (NROFF & xon) /* rob fix for man2html */ + return 0; + pchar1(i); + return 0; +} + + +void pchar1(Tchar i) +{ + int j; + + j = cbits(i); + if (dip != &d[0]) { + wbf(i); + dip->op = offset; + return; + } + if (!tflg && !print) { + if (j == '\n') + dip->alss = dip->blss = 0; + return; + } + if (j == FILLER && !xon) + return; + if (tflg) { /* transparent mode, undiverted */ + if (print) /* assumes that it's ok to print */ + /* OUT "%c", j PUT; /* i.e., is ascii */ + outascii(i); + return; + } + if (TROFF && ascii) + outascii(i); + else + ptout(i); +} + + +void outweird(int k) /* like ptchname() but ascii */ +{ + char *chn = chname(k); + + switch (chn[0]) { + case MBchar: + OUT "%s", chn+1 PUT; /* \n not needed? */ + break; + case Number: + OUT "\\N'%s'", chn+1 PUT; + break; + case Troffchar: + if (strlen(chn+1) == 2) + OUT "\\(%s", chn+1 PUT; + else + OUT "\\C'%s'", chn+1 PUT; + break; + default: + OUT " %s? ", chn PUT; + break; + } +} + +void outascii(Tchar i) /* print i in best-guess ascii */ +{ + int j = cbits(i); + +/* is this ever called with NROFF set? probably doesn't work at all. */ + + if (ismot(i)) + oput(' '); + else if (j < ALPHABET && j >= ' ' || j == '\n' || j == '\t') + oput(j); + else if (j == DRAWFCN) + oputs("\\D"); + else if (j == HYPHEN) + oput('-'); + else if (j == MINUS) /* special pleading for strange encodings */ + oputs("\\-"); + else if (j == PRESC) + oputs("\\e"); + else if (j == FILLER) + oputs("\\&"); + else if (j == UNPAD) + oputs("\\ "); + else if (j == OHC) /* this will never occur; stripped out earlier */ + oputs("\\%"); + else if (j == XON) + oputs("\\X"); + else if (j == XOFF) + oputs(" "); + else if (j == LIG_FI) + oputs("fi"); + else if (j == LIG_FL) + oputs("fl"); + else if (j == LIG_FF) + oputs("ff"); + else if (j == LIG_FFI) + oputs("ffi"); + else if (j == LIG_FFL) + oputs("ffl"); + else if (j == WORDSP) { /* nothing at all */ + if (xon) /* except in \X */ + oput(' '); + + } else + outweird(j); +} + +int flusho(void) +{ + if (NROFF && !toolate && t.twinit) + fwrite(t.twinit, strlen(t.twinit), 1, ptid); + + if (obufp > obuf) { + if (pipeflg && !toolate) { + /* fprintf(stderr, "Pipe to <%s>\n", Pipe.buf); */ + if (!Pipe.buf[0] || (ptid = popen(Pipe.buf, "w")) == NULL) + ERROR "pipe %s not created.", Pipe.buf WARN; + if (Pipe.buf) + free(Pipe.buf); + } + if (!toolate) + toolate++; + *obufp = 0; + fputs(obuf, ptid); + fflush(ptid); + obufp = obuf; + } + return 1; +} + + +void caseex(void) +{ + done(0); +} + + +void done(int x) +{ + int i; + + error |= x; + app = ds = lgf = 0; + if (i = em) { + donef = -1; + eschar = '\\'; + em = 0; + if (control(i, 0)) + longjmp(sjbuf, 1); + } + if (!nfo) + done3(0); + mflg = 0; + dip = &d[0]; + if (woff) /* BUG!!! This isn't set anywhere */ + wbf((Tchar)0); + if (pendw) + getword(1); + pendnf = 0; + if (donef == 1) + done1(0); + donef = 1; + ip = 0; + frame = stk; + nxf = frame + 1; + if (!ejf) + tbreak(); + nflush++; + eject((Stack *)0); + longjmp(sjbuf, 1); +} + + +void done1(int x) +{ + error |= x; + if (numtabp[NL].val) { + trap = 0; + eject((Stack *)0); + longjmp(sjbuf, 1); + } + if (!ascii) + pttrailer(); + done2(0); +} + + +void done2(int x) +{ + ptlead(); + if (TROFF && !ascii) + ptstop(); + flusho(); + done3(x); +} + +void done3(int x) +{ + error |= x; + flusho(); + if (NROFF) + twdone(); + if (pipeflg) + pclose(ptid); + exit(error); +} + + +void edone(int x) +{ + frame = stk; + nxf = frame + 1; + ip = 0; + done(x); +} + + +void casepi(void) +{ + int j; + char buf[NTM]; + + if (Pipe.buf == NULL) { + if ((Pipe.buf = (char *)calloc(NTM, sizeof(char))) == NULL) { + ERROR "No buf space for pipe cmd" WARN; + return; + } + Pipe.tick = 1; + } else + Pipe.buf[Pipe.cnt++] = '|'; + + getline(buf, NTM); + j = strlen(buf); + if (toolate) { + ERROR "Cannot create pipe to %s", buf WARN; + return; + } + Pipe.cnt += j; + if (j >= NTM +1) { + Pipe.tick++; + if ((Pipe.buf = (char *)realloc(Pipe.buf, Pipe.tick * NTM * sizeof(char))) == NULL) { + ERROR "No more buf space for pipe cmd" WARN; + return; + } + } + strcat(Pipe.buf, buf); + pipeflg++; +} diff --git a/troff/n3.c b/troff/n3.c @@ -0,0 +1,954 @@ +/* + * troff3.c + * + * macro and string routines, storage allocation + */ + +#include "tdef.h" +#include "fns.h" +#include "ext.h" + +Tchar *argtop; +int pagech = '%'; +int strflg; + +#define MHASHSIZE 128 /* must be 2**n */ +#define MHASH(x) ((x>>6)^x) & (MHASHSIZE-1) +Contab *mhash[MHASHSIZE]; + + +Blockp *blist; /* allocated blocks for macros and strings */ +int nblist; /* how many there are */ +int bfree = -1; /* first (possible) free block in the list */ + +Contab *contabp = NULL; +#define MDELTA 500 +int nm = 0; + +int savname; /* name of macro/string being defined */ +int savslot; /* place in Contab of savname */ +int freeslot = -1; /* first (possible) free slot in contab */ + +void prcontab(Contab *p) +{ + int i; + for (i = 0; i < nm; i++) + if (p) + if (p[i].rq != 0) + fprintf(stderr, "slot %d, %-2.2s\n", i, unpair(p[i].rq)); + else + fprintf(stderr, "slot %d empty\n", i); + else + fprintf(stderr, "slot %d empty\n", i); +} + + +void blockinit(void) +{ + blist = (Blockp *) calloc(NBLIST, sizeof(Blockp)); + if (blist == NULL) { + ERROR "not enough room for %d blocks", NBLIST WARN; + done2(1); + } + nblist = NBLIST; + blist[0].nextoff = blist[1].nextoff = -1; + blist[0].bp = (Tchar *) calloc(BLK, sizeof(Tchar)); + blist[1].bp = (Tchar *) calloc(BLK, sizeof(Tchar)); + /* -1 prevents blist[0] from being used; temporary fix */ + /* for a design botch: offset==0 is overloaded. */ + /* blist[1] reserved for .rd indicator -- also unused. */ + /* but someone unwittingly looks at these, so allocate something */ + bfree = 2; +} + + +char *grow(char *ptr, int num, int size) /* make array bigger */ +{ + char *p; + + if (ptr == NULL) + p = (char *) calloc(num, size); + else + p = (char *) realloc(ptr, num * size); + return p; +} + +void mnspace(void) +{ + nm = sizeof(contab)/sizeof(Contab) + MDELTA; + freeslot = sizeof(contab)/sizeof(Contab) + 1; + contabp = (Contab *) grow((char *) contabp, nm, sizeof(Contab)); + if (contabp == NULL) { + ERROR "not enough memory for namespace of %d marcos", nm WARN; + exit(1); + } + contabp = (Contab *) memcpy((char *) contabp, (char *)contab, + sizeof(contab)); + if (contabp == NULL) { + ERROR "Cannot reinitialize macro/request name list" WARN; + exit(1); + } + +} + +void caseig(void) +{ + int i; + Offset oldoff = offset; + + offset = 0; + i = copyb(); + offset = oldoff; + if (i != '.') + control(i, 1); +} + + +void casern(void) +{ + int i, j, k; + + lgf++; + skip(); + if ((i = getrq()) == 0 || (oldmn = findmn(i)) < 0) + return; + skip(); + clrmn(findmn(j = getrq())); + if (j) { + munhash(&contabp[oldmn]); + contabp[oldmn].rq = j; + maddhash(&contabp[oldmn]); + if (dip != d ) + for (k = dilev; k; k--) + if (d[k].curd == i) + d[k].curd = j; + } +} + +void maddhash(Contab *rp) +{ + Contab **hp; + + if (rp->rq == 0) + return; + hp = &mhash[MHASH(rp->rq)]; + rp->link = *hp; + *hp = rp; +} + +void munhash(Contab *mp) +{ + Contab *p; + Contab **lp; + + if (mp->rq == 0) + return; + lp = &mhash[MHASH(mp->rq)]; + p = *lp; + while (p) { + if (p == mp) { + *lp = p->link; + p->link = 0; + return; + } + lp = &p->link; + p = p->link; + } +} + +void mrehash(void) +{ + Contab *p; + int i; + + for (i=0; i < MHASHSIZE; i++) + mhash[i] = 0; + for (p=contabp; p < &contabp[nm]; p++) + p->link = 0; + for (p=contabp; p < &contabp[nm]; p++) { + if (p->rq == 0) + continue; + i = MHASH(p->rq); + p->link = mhash[i]; + mhash[i] = p; + } +} + +void caserm(void) +{ + int j; + int k = 0; + + lgf++; +g0: + while (!skip() && (j = getrq()) != 0) { + if (dip != d) + for (k = dilev; k; k--) + if (d[k].curd == j) { + ERROR "cannot remove diversion %s during definition", + unpair(j) WARN; + goto g0; + } + clrmn(findmn(j)); + } + lgf--; +} + + +void caseas(void) +{ + app++; + caseds(); +} + + +void caseds(void) +{ + ds++; + casede(); +} + + +void caseam(void) +{ + app++; + casede(); +} + + +void casede(void) +{ + int i, req; + Offset savoff; + + req = '.'; + lgf++; + skip(); + if ((i = getrq()) == 0) + goto de1; + if ((offset = finds(i)) == 0) + goto de1; + if (newmn) + savslot = newmn; + else + savslot = findmn(i); + savname = i; + if (ds) + copys(); + else + req = copyb(); + clrmn(oldmn); + if (newmn) { + if (contabp[newmn].rq) + munhash(&contabp[newmn]); + contabp[newmn].rq = i; + maddhash(&contabp[newmn]); + + } + if (apptr) { + savoff = offset; + offset = apptr; + wbf((Tchar) IMP); + offset = savoff; + } + offset = dip->op; + if (req != '.') + control(req, 1); +de1: + ds = app = 0; +} + + +int findmn(int i) +{ + Contab *p; + + for (p = mhash[MHASH(i)]; p; p = p->link) + if (i == p->rq) + return(p - contabp); + return(-1); +} + + +void clrmn(int i) +{ + if (i >= 0) { + if (contabp[i].mx) + ffree(contabp[i].mx); + munhash(&contabp[i]); + contabp[i].rq = 0; + contabp[i].mx = 0; + contabp[i].emx = 0; + contabp[i].f = 0; + if (contabp[i].divsiz != NULL) { + free(contabp[i].divsiz); + contabp[i].divsiz = NULL; + } + if (freeslot > i) + freeslot = i; + } +} + +void growcontab(void) +{ + nm += MDELTA; + contabp = (Contab *) grow((char *) contabp , nm, sizeof(Contab)); + if (contabp == NULL) { + ERROR "Too many (%d) string/macro names", nm WARN; + done2(02); + } else { + memset((char *)(contabp) + (nm - MDELTA) * sizeof(Contab), + 0, MDELTA * sizeof(Contab)); + mrehash(); + } +} + + +Offset finds(int mn) +{ + int i; + Offset savip; + + oldmn = findmn(mn); + newmn = 0; + apptr = 0; + if (app && oldmn >= 0 && contabp[oldmn].mx) { + savip = ip; + ip = contabp[oldmn].emx; + oldmn = -1; + apptr = ip; + if (!diflg) + ip = incoff(ip); + nextb = ip; + ip = savip; + } else { + for (i = freeslot; i < nm; i++) { + if (contabp[i].rq == 0) + break; + } + if (i == nm) + growcontab(); + freeslot = i + 1; + if ((nextb = alloc()) == -1) { + app = 0; + if (macerr++ > 1) + done2(02); + if (nextb == 0) + ERROR "Not enough space for string/macro names" WARN; + edone(04); + return(offset = 0); + } + contabp[i].mx = nextb; + if (!diflg) { + newmn = i; + if (oldmn == -1) + contabp[i].rq = -1; + } else { + contabp[i].rq = mn; + maddhash(&contabp[i]); + } + } + app = 0; + return(offset = nextb); +} + +int skip(void) +{ + Tchar i; + + while (cbits(i = getch()) == ' ' || ismot(i)) + ; + ch = i; + return(nlflg); +} + + +int copyb(void) +{ + int i, j, state; + Tchar ii; + int req, k; + Offset savoff; + Uchar *p; + + savoff = 0; + if (skip() || !(j = getrq())) + j = '.'; + req = j; + p = unpair(j); + /* was: k = j >> BYTE; j &= BYTEMASK; */ + j = p[0]; + k = p[1]; + copyf++; + flushi(); + nlflg = 0; + state = 1; + +/* state 0 eat up + * state 1 look for . + * state 2 look for first char of end macro + * state 3 look for second char of end macro + */ + + while (1) { + i = cbits(ii = getch()); + if (state == 3) { + if (i == k) + break; + if (!k) { + ch = ii; + i = getach(); + ch = ii; + if (!i) + break; + } + state = 0; + goto c0; + } + if (i == '\n') { + state = 1; + nlflg = 0; + goto c0; + } + if (state == 1 && i == '.') { + state++; + savoff = offset; + goto c0; + } + if (state == 2 && i == j) { + state++; + goto c0; + } + state = 0; +c0: + if (offset) + wbf(ii); + } + if (offset) { + offset = savoff; + wbf((Tchar)0); + } + copyf--; + return(req); +} + + +void copys(void) +{ + Tchar i; + + copyf++; + if (skip()) + goto c0; + if (cbits(i = getch()) != '"') + wbf(i); + while (cbits(i = getch()) != '\n') + wbf(i); +c0: + wbf((Tchar)0); + copyf--; +} + + +Offset alloc(void) /* return free Offset in nextb */ +{ + int i, j; + + for (i = bfree; i < nblist; i++) + if (blist[i].nextoff == 0) + break; + if (i == nblist) { + blist = (Blockp *) realloc((char *) blist, 2 * nblist * sizeof(Blockp)); + if (blist == NULL) { + ERROR "can't grow blist for string/macro defns" WARN; + done2(2); + } + nblist *= 2; + for (j = i; j < nblist; j++) { + blist[j].nextoff = 0; + blist[j].bp = 0; + } + } + blist[i].nextoff = -1; /* this block is the end */ + bfree = i + 1; + if (blist[i].bp == 0) + blist[i].bp = (Tchar *) calloc(BLK, sizeof(Tchar)); + if (blist[i].bp == NULL) { + ERROR "can't allocate memory for string/macro definitions" WARN; + done2(2); + } + nextb = (Offset) i * BLK; + return nextb; +} + + +void ffree(Offset i) /* free list of blocks starting at blist(o) */ +{ /* (doesn't actually free the blocks, just the pointers) */ + int j; + + for ( ; blist[j = bindex(i)].nextoff != -1; ) { + if (bfree > j) + bfree = j; + i = blist[j].nextoff; + blist[j].nextoff = 0; + } + blist[j].nextoff = 0; +} + + +void wbf(Tchar i) /* store i into offset, get ready for next one */ +{ + int j, off; + + if (!offset) + return; + j = bindex(offset); + if (i == 0) + contabp[savslot].emx = offset; + off = boffset(offset); + blist[j].bp[off++] = i; + offset++; + if (pastend(offset)) { /* off the end of this block */ + if (blist[j].nextoff == -1) { + if ((nextb = alloc()) == -1) { + ERROR "Out of temp file space" WARN; + done2(01); + } + blist[j].nextoff = nextb; + } + offset = blist[j].nextoff; + } +} + + +Tchar rbf(void) /* return next char from blist[] block */ +{ + Tchar i, j; + + if (ip == RD_OFFSET) { /* for rdtty */ + if (j = rdtty()) + return(j); + else + return(popi()); + } + + i = rbf0(ip); + if (i == 0) { + if (!app) + i = popi(); + return(i); + } + ip = incoff(ip); + return(i); +} + + +Offset xxxincoff(Offset p) /* get next blist[] block */ +{ + p++; + if (pastend(p)) { /* off the end of this block */ + if ((p = blist[bindex(p-1)].nextoff) == -1) { /* and nothing was allocated after it */ + ERROR "Bad storage allocation" WARN; + done2(-5); + } + } + return(p); +} + + +Tchar popi(void) +{ + Stack *p; + + if (frame == stk) + return(0); + if (strflg) + strflg--; + p = nxf = frame; + p->nargs = 0; + frame = p->pframe; + ip = p->pip; + pendt = p->ppendt; + lastpbp = p->lastpbp; + return(p->pch); +} + +/* + * test that the end of the allocation is above a certain location + * in memory + */ +#define SPACETEST(base, size) \ + if ((char*)base + size >= (char*)stk+STACKSIZE) \ + ERROR "Stacksize overflow in n3" WARN + +Offset pushi(Offset newip, int mname) +{ + Stack *p; + + SPACETEST(nxf, sizeof(Stack)); + p = nxf; + p->pframe = frame; + p->pip = ip; + p->ppendt = pendt; + p->pch = ch; + p->lastpbp = lastpbp; + p->mname = mname; + lastpbp = pbp; + pendt = ch = 0; + frame = nxf; + if (nxf->nargs == 0) + nxf += 1; + else + nxf = (Stack *)argtop; + return(ip = newip); +} + + +void *setbrk(int x) +{ + char *i; + + if ((i = (char *) calloc(x, 1)) == 0) { + ERROR "Core limit reached" WARN; + edone(0100); + } + return(i); +} + + +int getsn(void) +{ + int i; + + if ((i = getach()) == 0) + return(0); + if (i == '(') + return(getrq()); + else + return(i); +} + + +Offset setstr(void) +{ + int i, j; + + lgf++; + if ((i = getsn()) == 0 || (j = findmn(i)) == -1 || !contabp[j].mx) { + lgf--; + return(0); + } else { + SPACETEST(nxf, sizeof(Stack)); + nxf->nargs = 0; + strflg++; + lgf--; + return pushi(contabp[j].mx, i); + } +} + + + +void collect(void) +{ + int j; + Tchar i, *strp, *lim, **argpp, **argppend; + int quote; + Stack *savnxf; + + copyf++; + nxf->nargs = 0; + savnxf = nxf; + if (skip()) + goto rtn; + + { + char *memp; + memp = (char *)savnxf; + /* + * 1 s structure for the macro descriptor + * APERMAC Tchar *'s for pointers into the strings + * space for the Tchar's themselves + */ + memp += sizeof(Stack); + /* + * CPERMAC = the total # of characters for ALL arguments + */ +#define CPERMAC 200 +#define APERMAC 9 + memp += APERMAC * sizeof(Tchar *); + memp += CPERMAC * sizeof(Tchar); + nxf = (Stack *)memp; + } + lim = (Tchar *)nxf; + argpp = (Tchar **)(savnxf + 1); + argppend = &argpp[APERMAC]; + SPACETEST(argppend, sizeof(Tchar *)); + strp = (Tchar *)argppend; + /* + * Zero out all the string pointers before filling them in. + */ + for (j = 0; j < APERMAC; j++) + argpp[j] = 0; + /* ERROR "savnxf=0x%x,nxf=0x%x,argpp=0x%x,strp=argppend=0x%x, lim=0x%x", + * savnxf, nxf, argpp, strp, lim WARN; + */ + strflg = 0; + while (argpp != argppend && !skip()) { + *argpp++ = strp; + quote = 0; + if (cbits(i = getch()) == '"') + quote++; + else + ch = i; + while (1) { + i = getch(); +/* fprintf(stderr, "collect %c %d\n", cbits(i), cbits(i)); */ + if (nlflg || (!quote && argpp != argppend && cbits(i) == ' ')) + break; /* collects rest into $9 */ + if ( quote + && cbits(i) == '"' + && cbits(i = getch()) != '"') { + ch = i; + break; + } + *strp++ = i; + if (strflg && strp >= lim) { + /* ERROR "strp=0x%x, lim = 0x%x", strp, lim WARN; */ + ERROR "Macro argument too long" WARN; + copyf--; + edone(004); + } + SPACETEST(strp, 3 * sizeof(Tchar)); + } + *strp++ = 0; + } + nxf = savnxf; + nxf->nargs = argpp - (Tchar **)(savnxf + 1); + argtop = strp; +rtn: + copyf--; +} + + +void seta(void) +{ + int i; + + i = cbits(getch()) - '0'; + if (i > 0 && i <= APERMAC && i <= frame->nargs) + pushback(*(((Tchar **)(frame + 1)) + i - 1)); +} + + +void caseda(void) +{ + app++; + casedi(); +} + +void casegd(void) +{ + int i, j; + + skip(); + if ((i = getrq()) == 0) + return; + if ((j = findmn(i)) >= 0) { + if (contabp[j].divsiz != NULL) { + numtabp[DN].val = contabp[j].divsiz->dix; + numtabp[DL].val = contabp[j].divsiz->diy; + } + } +} + +#define FINDDIV(o) if ((o = findmn(dip->curd)) < 0) \ + ERROR "lost diversion %s", unpair(dip->curd) WARN + +void casedi(void) +{ + int i, j, *k; + + lgf++; + if (skip() || (i = getrq()) == 0) { + if (dip != d) { + FINDDIV(savslot); + wbf((Tchar)0); + } + if (dilev > 0) { + numtabp[DN].val = dip->dnl; + numtabp[DL].val = dip->maxl; + FINDDIV(j); + if ((contabp[j].divsiz = (Divsiz *) malloc(sizeof(Divsiz))) == NULL) { + ERROR "Cannot alloc diversion size" WARN; + done2(1); + } else { + contabp[j].divsiz->dix = numtabp[DN].val; + contabp[j].divsiz->diy = numtabp[DL].val; + } + dip = &d[--dilev]; + offset = dip->op; + } + goto rtn; + } + if (++dilev == NDI) { + --dilev; + ERROR "Diversions nested too deep" WARN; + edone(02); + } + if (dip != d) { + FINDDIV(j); + savslot = j; + wbf((Tchar)0); + } + diflg++; + dip = &d[dilev]; + dip->op = finds(i); + dip->curd = i; + clrmn(oldmn); + k = (int *) & dip->dnl; + for (j = 0; j < 10; j++) + k[j] = 0; /*not op and curd*/ +rtn: + app = 0; + diflg = 0; +} + + +void casedt(void) +{ + lgf++; + dip->dimac = dip->ditrap = dip->ditf = 0; + skip(); + dip->ditrap = vnumb((int *)0); + if (nonumb) + return; + skip(); + dip->dimac = getrq(); +} + +#define LNSIZE 4000 +void casetl(void) +{ + int j; + int w[3]; + Tchar buf[LNSIZE]; + Tchar *tp; + Tchar i, delim; + + /* + * bug fix + * + * if .tl is the first thing in the file, the p1 + * doesn't come out, also the pagenumber will be 0 + * + * tends too confuse the device filter (and the user as well) + */ + if (dip == d && numtabp[NL].val == -1) + newline(1); + dip->nls = 0; + skip(); + if (ismot(delim = getch())) { + ch = delim; + delim = '\''; + } else + delim = cbits(delim); + tp = buf; + numtabp[HP].val = 0; + w[0] = w[1] = w[2] = 0; + j = 0; + while (cbits(i = getch()) != '\n') { + if (cbits(i) == cbits(delim)) { + if (j < 3) + w[j] = numtabp[HP].val; + numtabp[HP].val = 0; + if (w[j] != 0) + *tp++ = WORDSP; + j++; + *tp++ = 0; + } else { + if (cbits(i) == pagech) { + setn1(numtabp[PN].val, numtabp[findr('%')].fmt, + i&SFMASK); + continue; + } + numtabp[HP].val += width(i); + if (tp < &buf[LNSIZE-10]) { + if (cbits(i) == ' ' && *tp != WORDSP) + *tp++ = WORDSP; + *tp++ = i; + } else { + ERROR "Overflow in casetl" WARN; + } + } + } + if (j<3) + w[j] = numtabp[HP].val; + *tp++ = 0; + *tp++ = 0; + *tp = 0; + tp = buf; + if (NROFF) + horiz(po); + while (i = *tp++) + pchar(i); + if (w[1] || w[2]) + horiz(j = quant((lt - w[1]) / 2 - w[0], HOR)); + while (i = *tp++) + pchar(i); + if (w[2]) { + horiz(lt - w[0] - w[1] - w[2] - j); + while (i = *tp++) + pchar(i); + } + newline(0); + if (dip != d) { + if (dip->dnl > dip->hnl) + dip->hnl = dip->dnl; + } else { + if (numtabp[NL].val > dip->hnl) + dip->hnl = numtabp[NL].val; + } +} + + +void casepc(void) +{ + pagech = chget(IMP); +} + + +void casepm(void) +{ + int i, k; + int xx, cnt, tcnt, kk, tot; + Offset j; + + kk = cnt = tcnt = 0; + tot = !skip(); + stackdump(); + for (i = 0; i < nm; i++) { + if ((xx = contabp[i].rq) == 0 || contabp[i].mx == 0) + continue; + tcnt++; + j = contabp[i].mx; + for (k = 1; (j = blist[bindex(j)].nextoff) != -1; ) + k++; + cnt++; + kk += k; + if (!tot) + fprintf(stderr, "%-2.2s %d\n", unpair(xx), k); + } + fprintf(stderr, "pm: total %d, macros %d, space %d\n", tcnt, cnt, kk); +} + +void stackdump(void) /* dumps stack of macros in process */ +{ + Stack *p; + + if (frame != stk) { + fprintf(stderr, "stack: "); + for (p = frame; p != stk; p = p->pframe) + fprintf(stderr, "%s ", unpair(p->mname)); + fprintf(stderr, "\n"); + } +} diff --git a/troff/n4.c b/troff/n4.c @@ -0,0 +1,828 @@ +/* + * troff4.c + * + * number registers, conversion, arithmetic + */ + +#include "tdef.h" +#include "fns.h" +#include "ext.h" + + +int regcnt = NNAMES; +int falsef = 0; /* on if inside false branch of if */ + +#define NHASHSIZE 128 /* must be 2**n */ +#define NHASH(i) ((i>>6)^i) & (NHASHSIZE-1) +Numtab *nhash[NHASHSIZE]; + +Numtab *numtabp = NULL; +#define NDELTA 400 +int ncnt = 0; + +void setn(void) +{ + int i, j, f; + Tchar ii; + Uchar *p; + char buf[NTM]; /* for \n(.S */ + + f = nform = 0; + if ((i = cbits(ii = getach())) == '+') + f = 1; + else if (i == '-') + f = -1; + else if (ii) /* don't put it back if it's already back (thanks to jaap) */ + ch = ii; + if (falsef) + f = 0; + if ((i = getsn()) == 0) + return; + p = unpair(i); + if (p[0] == '.') + switch (p[1]) { + case 's': + i = pts; + break; + case 'v': + i = lss; + break; + case 'f': + i = font; + break; + case 'p': + i = pl; + break; + case 't': + i = findt1(); + break; + case 'o': + i = po; + break; + case 'l': + i = ll; + break; + case 'i': + i = in; + break; + case '$': + i = frame->nargs; + break; + case 'A': + i = ascii; + break; + case 'c': + i = numtabp[CD].val; + break; + case 'n': + i = lastl; + break; + case 'a': + i = ralss; + break; + case 'h': + i = dip->hnl; + break; + case 'd': + if (dip != d) + i = dip->dnl; + else + i = numtabp[NL].val; + break; + case 'u': + i = fi; + break; + case 'j': + i = ad + 2 * admod; + break; + case 'w': + i = widthp; + break; + case 'x': + i = nel; + break; + case 'y': + i = un; + break; + case 'T': + i = dotT; + break; /* -Tterm used in nroff */ + case 'V': + i = VERT; + break; + case 'H': + i = HOR; + break; + case 'k': + i = ne; + break; + case 'P': + i = print; + break; + case 'L': + i = ls; + break; + case 'R': /* maximal # of regs that can be addressed */ + i = 255*256 - regcnt; + break; + case 'z': + p = unpair(dip->curd); + *pbp++ = p[1]; /* watch order */ + *pbp++ = p[0]; + return; + case 'b': + i = bdtab[font]; + break; + case 'F': + cpushback(cfname[ifi]); + return; + case 'S': + buf[0] = j = 0; + for( i = 0; tabtab[i] != 0 && i < NTAB; i++) { + if (i > 0) + buf[j++] = ' '; + sprintf(&buf[j], "%ld", tabtab[i] & TABMASK); + j = strlen(buf); + if ( tabtab[i] & RTAB) + sprintf(&buf[j], "uR"); + else if (tabtab[i] & CTAB) + sprintf(&buf[j], "uC"); + else + sprintf(&buf[j], "uL"); + j += 2; + } + cpushback(buf); + return; + default: + goto s0; + } + else { +s0: + if ((j = findr(i)) == -1) + i = 0; + else { + i = numtabp[j].val = numtabp[j].val + numtabp[j].inc * f; + nform = numtabp[j].fmt; + } + } + setn1(i, nform, (Tchar) 0); +} + +Tchar numbuf[25]; +Tchar *numbufp; + +int wrc(Tchar i) +{ + if (numbufp >= &numbuf[24]) + return(0); + *numbufp++ = i; + return(1); +} + + + +/* insert into input number i, in format form, with size-font bits bits */ +void setn1(int i, int form, Tchar bits) +{ + numbufp = numbuf; + nrbits = bits; + nform = form; + fnumb(i, wrc); + *numbufp = 0; + pushback(numbuf); +} + +void prnumtab(Numtab *p) +{ + int i; + for (i = 0; i < ncnt; i++) + if (p) + if (p[i].r != 0) + fprintf(stderr, "slot %d, %s, val %d\n", i, unpair(p[i].r), p[i].val); + else + fprintf(stderr, "slot %d empty\n", i); + else + fprintf(stderr, "slot %d empty\n", i); +} + +void nnspace(void) +{ + ncnt = sizeof(numtab)/sizeof(Numtab) + NDELTA; + numtabp = (Numtab *) grow((char *)numtabp, ncnt, sizeof(Numtab)); + if (numtabp == NULL) { + ERROR "not enough memory for registers (%d)", ncnt WARN; + exit(1); + } + numtabp = (Numtab *) memcpy((char *)numtabp, (char *)numtab, + sizeof(numtab)); + if (numtabp == NULL) { + ERROR "Cannot initialize registers" WARN; + exit(1); + } +} + +void grownumtab(void) +{ + ncnt += NDELTA; + numtabp = (Numtab *) grow((char *) numtabp, ncnt, sizeof(Numtab)); + if (numtabp == NULL) { + ERROR "Too many number registers (%d)", ncnt WARN; + done2(04); + } else { + memset((char *)(numtabp) + (ncnt - NDELTA) * sizeof(Numtab), + 0, NDELTA * sizeof(Numtab)); + nrehash(); + } +} + +void nrehash(void) +{ + Numtab *p; + int i; + + for (i=0; i<NHASHSIZE; i++) + nhash[i] = 0; + for (p=numtabp; p < &numtabp[ncnt]; p++) + p->link = 0; + for (p=numtabp; p < &numtabp[ncnt]; p++) { + if (p->r == 0) + continue; + i = NHASH(p->r); + p->link = nhash[i]; + nhash[i] = p; + } +} + +void nunhash(Numtab *rp) +{ + Numtab *p; + Numtab **lp; + + if (rp->r == 0) + return; + lp = &nhash[NHASH(rp->r)]; + p = *lp; + while (p) { + if (p == rp) { + *lp = p->link; + p->link = 0; + return; + } + lp = &p->link; + p = p->link; + } +} + +int findr(int i) +{ + Numtab *p; + int h = NHASH(i); + + if (i == 0) + return(-1); +a0: + for (p = nhash[h]; p; p = p->link) + if (i == p->r) + return(p - numtabp); + for (p = numtabp; p < &numtabp[ncnt]; p++) { + if (p->r == 0) { + p->r = i; + p->link = nhash[h]; + nhash[h] = p; + regcnt++; + return(p - numtabp); + } + } + grownumtab(); + goto a0; +} + +int usedr(int i) /* returns -1 if nr i has never been used */ +{ + Numtab *p; + + if (i == 0) + return(-1); + for (p = nhash[NHASH(i)]; p; p = p->link) + if (i == p->r) + return(p - numtabp); + return -1; +} + + +int fnumb(int i, int (*f)(Tchar)) +{ + int j; + + j = 0; + if (i < 0) { + j = (*f)('-' | nrbits); + i = -i; + } + switch (nform) { + default: + case '1': + case 0: + return decml(i, f) + j; + case 'i': + case 'I': + return roman(i, f) + j; + case 'a': + case 'A': + return abc(i, f) + j; + } +} + + +int decml(int i, int (*f)(Tchar)) +{ + int j, k; + + k = 0; + nform--; + if ((j = i / 10) || (nform > 0)) + k = decml(j, f); + return(k + (*f)((i % 10 + '0') | nrbits)); +} + + +int roman(int i, int (*f)(Tchar)) +{ + + if (!i) + return((*f)('0' | nrbits)); + if (nform == 'i') + return(roman0(i, f, "ixcmz", "vldw")); + else + return(roman0(i, f, "IXCMZ", "VLDW")); +} + + +int roman0(int i, int (*f)(Tchar), char *onesp, char *fivesp) +{ + int q, rem, k; + + if (!i) + return(0); + k = roman0(i / 10, f, onesp + 1, fivesp + 1); + q = (i = i % 10) / 5; + rem = i % 5; + if (rem == 4) { + k += (*f)(*onesp | nrbits); + if (q) + i = *(onesp + 1); + else + i = *fivesp; + return(k += (*f)(i | nrbits)); + } + if (q) + k += (*f)(*fivesp | nrbits); + while (--rem >= 0) + k += (*f)(*onesp | nrbits); + return(k); +} + + +int abc(int i, int (*f)(Tchar)) +{ + if (!i) + return((*f)('0' | nrbits)); + else + return(abc0(i - 1, f)); +} + + +int abc0(int i, int (*f)(Tchar)) +{ + int j, k; + + k = 0; + if (j = i / 26) + k = abc0(j - 1, f); + return(k + (*f)((i % 26 + nform) | nrbits)); +} + +long atoi0(void) +{ + int c, k, cnt; + Tchar ii; + long i, acc; + + acc = 0; + nonumb = 0; + cnt = -1; +a0: + cnt++; + ii = getch(); + c = cbits(ii); + switch (c) { + default: + ch = ii; + if (cnt) + break; + case '+': + i = ckph(); + if (nonumb) + break; + acc += i; + goto a0; + case '-': + i = ckph(); + if (nonumb) + break; + acc -= i; + goto a0; + case '*': + i = ckph(); + if (nonumb) + break; + acc *= i; + goto a0; + case '/': + i = ckph(); + if (nonumb) + break; + if (i == 0) { + flusho(); + ERROR "divide by zero." WARN; + acc = 0; + } else + acc /= i; + goto a0; + case '%': + i = ckph(); + if (nonumb) + break; + acc %= i; + goto a0; + case '&': /*and*/ + i = ckph(); + if (nonumb) + break; + if ((acc > 0) && (i > 0)) + acc = 1; + else + acc = 0; + goto a0; + case ':': /*or*/ + i = ckph(); + if (nonumb) + break; + if ((acc > 0) || (i > 0)) + acc = 1; + else + acc = 0; + goto a0; + case '=': + if (cbits(ii = getch()) != '=') + ch = ii; + i = ckph(); + if (nonumb) { + acc = 0; + break; + } + if (i == acc) + acc = 1; + else + acc = 0; + goto a0; + case '>': + k = 0; + if (cbits(ii = getch()) == '=') + k++; + else + ch = ii; + i = ckph(); + if (nonumb) { + acc = 0; + break; + } + if (acc > (i - k)) + acc = 1; + else + acc = 0; + goto a0; + case '<': + k = 0; + if (cbits(ii = getch()) == '=') + k++; + else + ch = ii; + i = ckph(); + if (nonumb) { + acc = 0; + break; + } + if (acc < (i + k)) + acc = 1; + else + acc = 0; + goto a0; + case ')': + break; + case '(': + acc = atoi0(); + goto a0; + } + return(acc); +} + + +long ckph(void) +{ + Tchar i; + long j; + + if (cbits(i = getch()) == '(') + j = atoi0(); + else { + j = atoi1(i); + } + return(j); +} + + +/* + * print error about illegal numeric argument; + */ +void prnumerr(void) +{ + char err_buf[40]; + static char warn[] = "Numeric argument expected"; + int savcd = numtabp[CD].val; + + if (numerr.type == RQERR) + sprintf(err_buf, "%c%s: %s", nb ? cbits(c2) : cbits(cc), + unpair(numerr.req), warn); + else + sprintf(err_buf, "\\%c'%s': %s", numerr.esc, &numerr.escarg, + warn); + if (frame != stk) /* uncertainty correction */ + numtabp[CD].val--; + ERROR "%s", err_buf WARN; + numtabp[CD].val = savcd; +} + + +long atoi1(Tchar ii) +{ + int i, j, digits; + double acc; /* this is the only double in troff! */ + int neg, abs, field, decpnt; + extern int ifnum; + + + neg = abs = field = decpnt = digits = 0; + acc = 0; + for (;;) { + i = cbits(ii); + switch (i) { + default: + break; + case '+': + ii = getch(); + continue; + case '-': + neg = 1; + ii = getch(); + continue; + case '|': + abs = 1 + neg; + neg = 0; + ii = getch(); + continue; + } + break; + } +a1: + while (i >= '0' && i <= '9') { + field++; + digits++; + acc = 10 * acc + i - '0'; + ii = getch(); + i = cbits(ii); + } + if (i == '.' && !decpnt++) { + field++; + digits = 0; + ii = getch(); + i = cbits(ii); + goto a1; + } + if (!field) { + ch = ii; + goto a2; + } + switch (i) { + case 'u': + i = j = 1; /* should this be related to HOR?? */ + break; + case 'v': /*VSs - vert spacing*/ + j = lss; + i = 1; + break; + case 'm': /*Ems*/ + j = EM; + i = 1; + break; + case 'n': /*Ens*/ + j = EM; + if (TROFF) + i = 2; + else + i = 1; /*Same as Ems in NROFF*/ + break; + case 'p': /*Points*/ + j = INCH; + i = 72; + break; + case 'i': /*Inches*/ + j = INCH; + i = 1; + break; + case 'c': /*Centimeters*/ + /* if INCH is too big, this will overflow */ + j = INCH * 50; + i = 127; + break; + case 'P': /*Picas*/ + j = INCH; + i = 6; + break; + default: + j = dfact; + ch = ii; + i = dfactd; + } + if (neg) + acc = -acc; + if (!noscale) { + acc = (acc * j) / i; + } + if (field != digits && digits > 0) + while (digits--) + acc /= 10; + if (abs) { + if (dip != d) + j = dip->dnl; + else + j = numtabp[NL].val; + if (!vflag) { + j = numtabp[HP].val; + } + if (abs == 2) + j = -j; + acc -= j; + } +a2: + nonumb = (!field || field == decpnt); + if (nonumb && (trace & TRNARGS) && !ismot(ii) && !nlflg && !ifnum) { + if (cbits(ii) != RIGHT ) /* Too painful to do right */ + prnumerr(); + } + return(acc); +} + + +void caserr(void) +{ + int i, j; + Numtab *p; + + lgf++; + while (!skip() && (i = getrq()) ) { + j = usedr(i); + if (j < 0) + continue; + p = &numtabp[j]; + nunhash(p); + p->r = p->val = p->inc = p->fmt = 0; + regcnt--; + } +} + +/* + * .nr request; if tracing, don't check optional + * 2nd argument because tbl generates .in 1.5n + */ +void casenr(void) +{ + int i, j; + int savtr = trace; + + lgf++; + skip(); + if ((i = findr(getrq())) == -1) + goto rtn; + skip(); + j = inumb(&numtabp[i].val); + if (nonumb) + goto rtn; + numtabp[i].val = j; + skip(); + trace = 0; + j = atoi0(); /* BUG??? */ + trace = savtr; + if (nonumb) + goto rtn; + numtabp[i].inc = j; +rtn: + return; +} + +void caseaf(void) +{ + int i, k; + Tchar j; + + lgf++; + if (skip() || !(i = getrq()) || skip()) + return; + k = 0; + j = getch(); + if (!isalpha(cbits(j))) { + ch = j; + while ((j = cbits(getch())) >= '0' && j <= '9') + k++; + } + if (!k) + k = j; + numtabp[findr(i)].fmt = k; /* was k & BYTEMASK */ +} + +void setaf(void) /* return format of number register */ +{ + int i, j; + + i = usedr(getsn()); + if (i == -1) + return; + if (numtabp[i].fmt > 20) /* it was probably a, A, i or I */ + *pbp++ = numtabp[i].fmt; + else + for (j = (numtabp[i].fmt ? numtabp[i].fmt : 1); j; j--) + *pbp++ = '0'; +} + + +int vnumb(int *i) +{ + vflag++; + dfact = lss; + res = VERT; + return(inumb(i)); +} + + +int hnumb(int *i) +{ + dfact = EM; + res = HOR; + return(inumb(i)); +} + + +int inumb(int *n) +{ + int i, j, f; + Tchar ii; + + f = 0; + if (n) { + if ((j = cbits(ii = getch())) == '+') + f = 1; + else if (j == '-') + f = -1; + else + ch = ii; + } + i = atoi0(); + if (n && f) + i = *n + f * i; + i = quant(i, res); + vflag = 0; + res = dfactd = dfact = 1; + if (nonumb) + i = 0; + return(i); +} + + +int quant(int n, int m) +{ + int i, neg; + + neg = 0; + if (n < 0) { + neg++; + n = -n; + } + /* better as i = ((n + m/2)/m)*m */ + i = n / m; + if (n - m * i > m / 2) + i += 1; + i *= m; + if (neg) + i = -i; + return(i); +} diff --git a/troff/n5.c b/troff/n5.c @@ -0,0 +1,1150 @@ +/* + * troff5.c + * + * misc processing requests + */ + +#include "tdef.h" +#include "fns.h" +#include "ext.h" + +int iflist[NIF]; +int ifx; +int ifnum = 0; /* trying numeric expression for .if or .ie condition */ + +void casead(void) +{ + int i; + + ad = 1; + /* leave admod alone */ + if (skip()) + return; + switch (i = cbits(getch())) { + case 'r': /* right adj, left ragged */ + admod = 2; + break; + case 'l': /* left adj, right ragged */ + admod = ad = 0; /* same as casena */ + break; + case 'c': /*centered adj*/ + admod = 1; + break; + case 'b': + case 'n': + admod = 0; + break; + case '0': + case '2': + case '4': + ad = 0; + case '1': + case '3': + case '5': + admod = (i - '0') / 2; + } +} + + +void casena(void) +{ + ad = 0; +} + + +void casefi(void) +{ + tbreak(); + fi = 1; + pendnf = 0; +} + + +void casenf(void) +{ + tbreak(); + fi = 0; +} + + +void casers(void) +{ + dip->nls = 0; +} + + +void casens(void) +{ + dip->nls++; +} + +int +chget(int c) +{ + Tchar i; + + i = 0; + if (skip() || ismot(i = getch()) || cbits(i) == ' ' || cbits(i) == '\n') { + ch = i; + return(c); + } else + return cbits(i); /* was (i & BYTEMASK) */ +} + + +void casecc(void) +{ + cc = chget('.'); +} + + +void casec2(void) +{ + c2 = chget('\''); +} + + +void casehc(void) +{ + ohc = chget(OHC); +} + + +void casetc(void) +{ + tabc = chget(0); +} + + +void caselc(void) +{ + dotc = chget(0); +} + + +void casehy(void) +{ + int i; + + hyf = 1; + if (skip()) + return; + noscale++; + i = atoi0(); + noscale = 0; + if (nonumb) + return; + hyf = max(i, 0); +} + + +void casenh(void) +{ + hyf = 0; +} + +int +max(int aa, int bb) +{ + if (aa > bb) + return(aa); + else + return(bb); +} + + +void casece(void) +{ + int i; + + noscale++; + skip(); + i = max(atoi0(), 0); + if (nonumb) + i = 1; + tbreak(); + ce = i; + noscale = 0; +} + + +void casein(void) +{ + int i; + + if (skip()) + i = in1; + else { + i = max(hnumb(&in), 0); + if (nonumb) + i = in1; + } + tbreak(); + in1 = in; + in = i; + if (!nc) { + un = in; + setnel(); + } +} + + +void casell(void) +{ + int i; + + if (skip()) + i = ll1; + else { + i = max(hnumb(&ll), INCH / 10); + if (nonumb) + i = ll1; + } + ll1 = ll; + ll = i; + setnel(); +} + + +void caselt(void) +{ + int i; + + if (skip()) + i = lt1; + else { + i = max(hnumb(&lt), 0); + if (nonumb) + i = lt1; + } + lt1 = lt; + lt = i; +} + + +void caseti(void) +{ + int i; + + if (skip()) + return; + i = max(hnumb(&in), 0); + tbreak(); + un1 = i; + setnel(); +} + + +void casels(void) +{ + int i; + + noscale++; + if (skip()) + i = ls1; + else { + i = max(inumb(&ls), 1); + if (nonumb) + i = ls1; + } + ls1 = ls; + ls = i; + noscale = 0; +} + + +void casepo(void) +{ + int i; + + if (skip()) + i = po1; + else { + i = max(hnumb(&po), 0); + if (nonumb) + i = po1; + } + po1 = po; + po = i; + if (TROFF & !ascii) + esc += po - po1; +} + + +void casepl(void) +{ + int i; + + skip(); + if ((i = vnumb(&pl)) == 0) + pl = 11 * INCH; /*11in*/ + else + pl = i; + if (numtabp[NL].val > pl) + numtabp[NL].val = pl; +} + + +void casewh(void) +{ + int i, j, k; + + lgf++; + skip(); + i = vnumb((int *)0); + if (nonumb) + return; + skip(); + j = getrq(); + if ((k = findn(i)) != NTRAP) { + mlist[k] = j; + return; + } + for (k = 0; k < NTRAP; k++) + if (mlist[k] == 0) + break; + if (k == NTRAP) { + flusho(); + ERROR "cannot plant trap." WARN; + return; + } + mlist[k] = j; + nlist[k] = i; +} + + +void casech(void) +{ + int i, j, k; + + lgf++; + skip(); + if (!(j = getrq())) + return; + else + for (k = 0; k < NTRAP; k++) + if (mlist[k] == j) + break; + if (k == NTRAP) + return; + skip(); + i = vnumb((int *)0); + if (nonumb) + mlist[k] = 0; + nlist[k] = i; +} + +int +findn(int i) +{ + int k; + + for (k = 0; k < NTRAP; k++) + if ((nlist[k] == i) && (mlist[k] != 0)) + break; + return(k); +} + + +void casepn(void) +{ + int i; + + skip(); + noscale++; + i = max(inumb(&numtabp[PN].val), 0); + noscale = 0; + if (!nonumb) { + npn = i; + npnflg++; + } +} + + +void casebp(void) +{ + int i; + Stack *savframe; + + if (dip != d) + return; + savframe = frame; + skip(); + if ((i = inumb(&numtabp[PN].val)) < 0) + i = 0; + tbreak(); + if (!nonumb) { + npn = i; + npnflg++; + } else if (dip->nls) + return; + eject(savframe); +} + +void casetm(void) +{ + casetm1(0, stderr); +} + + +void casefm(void) +{ + static struct fcache { + char *name; + FILE *fp; + } fcache[15]; + int i; + + if ( skip() || !getname()) { + ERROR "fm: missing filename" WARN; + return; + } + + for (i = 0; i < 15 && fcache[i].fp != NULL; i++) { + if (strcmp(nextf, fcache[i].name) == 0) + break; + } + if (i >= 15) { + ERROR "fm: too many streams" WARN; + return; + } + if (fcache[i].fp == NULL) { + if( (fcache[i].fp = fopen(unsharp(nextf), "w")) == NULL) { + ERROR "fm: cannot open %s", nextf WARN; + return; + } + fcache[i].name = strdupl(nextf); + } + casetm1(0, fcache[i].fp); +} + +void casetm1(int ab, FILE *out) +{ + int i, j, c; + char *p; + char tmbuf[NTM]; + + lgf++; + copyf++; + if (ab) { + if (skip()) + ERROR "User Abort" WARN; + else { + extern int error; + int savtrac = trace; + i = trace = 0; + noscale++; + i = inumb(&trace); + noscale--; + if (i) { + error = i; + if (nlflg || skip()) + ERROR "User Abort, exit code %d", i WARN; + } + trace = savtrac; + } + } else + skip(); + for (i = 0; i < NTM - 2; ) { + if ((c = cbits(getch())) == '\n' || c == RIGHT) + break; + else if (c == MINUS) { /* special pleading for strange encodings */ + tmbuf[i++] = '\\'; + tmbuf[i++] = '-'; + } else if (c == PRESC) { + tmbuf[i++] = '\\'; + tmbuf[i++] = 'e'; + } else if (c == FILLER) { + tmbuf[i++] = '\\'; + tmbuf[i++] = '&'; + } else if (c == UNPAD) { + tmbuf[i++] = '\\'; + tmbuf[i++] = ' '; + } else if (c == OHC) { + tmbuf[i++] = '\\'; + tmbuf[i++] = '%'; + } else if (c >= ALPHABET) { + p = chname(c); + switch (*p) { + case MBchar: + strcpy(&tmbuf[i], p+1); + break; + case Number: + sprintf(&tmbuf[i], "\\N'%s'", p+1); + break; + case Troffchar: + if ((j = strlen(p+1)) == 2) + sprintf(&tmbuf[i], "\\(%s", p+1); + else + sprintf(&tmbuf[i], "\\C'%s'", p+1); + break; + default: + sprintf(&tmbuf[i]," %s? ", p); + break; + } + j = strlen(&tmbuf[i]); + i += j; + } else + tmbuf[i++] = c; + } + tmbuf[i] = 0; + if (ab) /* truncate output */ + obufp = obuf; /* should be a function in n2.c */ + flusho(); + if (i) + fprintf(out, "%s\n", tmbuf); + fflush(out); + copyf--; + lgf--; +} + + +void casesp(void) +{ + casesp1(0); +} + +void casesp1(int a) +{ + int i, j, savlss; + + tbreak(); + if (dip->nls || trap) + return; + i = findt1(); + if (!a) { + skip(); + j = vnumb((int *)0); + if (nonumb) + j = lss; + } else + j = a; + if (j == 0) + return; + if (i < j) + j = i; + savlss = lss; + if (dip != d) + i = dip->dnl; + else + i = numtabp[NL].val; + if ((i + j) < 0) + j = -i; + lss = j; + newline(0); + lss = savlss; +} + + +void casert(void) +{ + int a, *p; + + skip(); + if (dip != d) + p = &dip->dnl; + else + p = &numtabp[NL].val; + a = vnumb(p); + if (nonumb) + a = dip->mkline; + if ((a < 0) || (a >= *p)) + return; + nb++; + casesp1(a - *p); +} + + +void caseem(void) +{ + lgf++; + skip(); + em = getrq(); +} + + +void casefl(void) +{ + tbreak(); + if (!ascii) + ptflush(); + flusho(); +} + + +void caseev(void) +{ + int nxev; + + if (skip()) { +e0: + if (evi == 0) + return; + nxev = evlist[--evi]; + goto e1; + } + noscale++; + nxev = atoi0(); + noscale = 0; + if (nonumb) + goto e0; + flushi(); + if (nxev >= NEV || nxev < 0 || evi >= EVLSZ) { + flusho(); + ERROR "cannot do .ev %d", nxev WARN; + if (error) + done2(040); + else + edone(040); + return; + } + evlist[evi++] = ev; +e1: + if (ev == nxev) + return; + ev = nxev; + envp = &env[ev]; +} + +void envcopy(Env *e1, Env *e2) /* copy env e2 to e1 */ +{ + *e1 = *e2; /* rumor hath that this fails on some machines */ +} + + +void caseel(void) +{ + if (--ifx < 0) { + ifx = 0; + iflist[0] = 0; + } + caseif1(2); +} + + +void caseie(void) +{ + if (ifx >= NIF) { + ERROR "if-else overflow." WARN; + ifx = 0; + edone(040); + } + caseif1(1); + ifx++; +} + + +void caseif(void) +{ + caseif1(0); +} + +void caseif1(int x) +{ + extern int falsef; + int notflag, true; + Tchar i; + + if (x == 2) { + notflag = 0; + true = iflist[ifx]; + goto i1; + } + true = 0; + skip(); + if ((cbits(i = getch())) == '!') { + notflag = 1; + } else { + notflag = 0; + ch = i; + } + ifnum++; + i = atoi0(); + ifnum = 0; + if (!nonumb) { + if (i > 0) + true++; + goto i1; + } + i = getch(); + switch (cbits(i)) { + case 'e': + if (!(numtabp[PN].val & 01)) + true++; + break; + case 'o': + if (numtabp[PN].val & 01) + true++; + break; + case 'n': + if (NROFF) + true++; + break; + case 't': + if (TROFF) + true++; + break; + case ' ': + break; + default: + true = cmpstr(i); + } +i1: + true ^= notflag; + if (x == 1) + iflist[ifx] = !true; + if (true) { +i2: + while ((cbits(i = getch())) == ' ') + ; + if (cbits(i) == LEFT) + goto i2; + ch = i; + nflush++; + } else { + if (!nlflg) { + copyf++; + falsef++; + eatblk(0); + copyf--; + falsef--; + } + } +} + +void eatblk(int inblk) +{ + int cnt, i; + + cnt = 0; + do { + if (ch) { + i = cbits(ch); + ch = 0; + } else + i = cbits(getch0()); + if (i == ESC) + cnt++; + else { + if (cnt == 1) + switch (i) { + case '{': i = LEFT; break; + case '}': i = RIGHT; break; + case '\n': i = 'x'; break; + } + cnt = 0; + } + if (i == LEFT) eatblk(1); + } while ((!inblk && (i != '\n')) || (inblk && (i != RIGHT))); + if (i == '\n') { + nlflg++; + if (ip == 0) + numtabp[CD].val++; + } +} + +int +cmpstr(Tchar c) +{ + int j, delim; + Tchar i; + int val; + int savapts, savapts1, savfont, savfont1, savpts, savpts1; + Tchar string[1280]; + Tchar *sp; + + if (ismot(c)) + return(0); + delim = cbits(c); + savapts = apts; + savapts1 = apts1; + savfont = font; + savfont1 = font1; + savpts = pts; + savpts1 = pts1; + sp = string; + while ((j = cbits(i = getch()))!=delim && j!='\n' && sp<&string[1280-1]) + *sp++ = i; + if (sp >= string + 1280) { + ERROR "too-long string compare." WARN; + edone(0100); + } + if (nlflg) { + val = sp==string; + goto rtn; + } + *sp = 0; + apts = savapts; + apts1 = savapts1; + font = savfont; + font1 = savfont1; + pts = savpts; + pts1 = savpts1; + mchbits(); + val = 1; + sp = string; + while ((j = cbits(i = getch())) != delim && j != '\n') { + if (*sp != i) { + eat(delim); + val = 0; + goto rtn; + } + sp++; + } + if (*sp) + val = 0; +rtn: + apts = savapts; + apts1 = savapts1; + font = savfont; + font1 = savfont1; + pts = savpts; + pts1 = savpts1; + mchbits(); + return(val); +} + + +void caserd(void) +{ + + lgf++; + skip(); + getname(); + if (!iflg) { + if (quiet) { + if (NROFF) { + echo_off(); + flusho(); + } + fprintf(stderr, "\007"); /*bell*/ + } else { + if (nextf[0]) { + fprintf(stderr, "%s:", nextf); + } else { + fprintf(stderr, "\007"); /*bell*/ + } + } + } + collect(); + tty++; + pushi(RD_OFFSET, PAIR('r','d')); +} + +int +rdtty(void) +{ + char onechar; + + onechar = 0; + if (read(0, &onechar, 1) == 1) { + if (onechar == '\n') + tty++; + else + tty = 1; + if (tty != 3) + return(onechar); + } + tty = 0; + if (NROFF && quiet) + echo_on(); + return(0); +} + + +void caseec(void) +{ + eschar = chget('\\'); +} + + +void caseeo(void) +{ + eschar = 0; +} + + +void caseta(void) +{ + int i, j, k; + + tabtab[0] = nonumb = 0; + for (i = 0; ((i < (NTAB - 1)) && !nonumb); i++) { + if (skip()) + break; + k = tabtab[max(i-1, 0)] & TABMASK; + if ((j = max(hnumb(&k), 0)) > TABMASK) { + ERROR "Tab too far away" WARN; + j = TABMASK; + } + tabtab[i] = j & TABMASK; + if (!nonumb) + switch (cbits(ch)) { + case 'C': + tabtab[i] |= CTAB; + break; + case 'R': + tabtab[i] |= RTAB; + break; + default: /*includes L*/ + break; + } + nonumb = ch = 0; + } + if (!skip()) + ERROR "Too many tab stops" WARN; + tabtab[i] = 0; +} + + +void casene(void) +{ + int i, j; + + skip(); + i = vnumb((int *)0); + if (nonumb) + i = lss; + if (dip == d && numtabp[NL].val == -1) { + newline(1); + return; + } + if (i > (j = findt1())) { + i = lss; + lss = j; + dip->nls = 0; + newline(0); + lss = i; + } +} + + +void casetr(void) +{ + int i, j; + Tchar k; + + lgf++; + skip(); + while ((i = cbits(k=getch())) != '\n') { + if (ismot(k)) + return; + if (ismot(k = getch())) + return; + if ((j = cbits(k)) == '\n') + j = ' '; + trtab[i] = j; + } +} + + +void casecu(void) +{ + cu++; + caseul(); +} + + +void caseul(void) +{ + int i; + + noscale++; + skip(); + i = max(atoi0(), 0); + if (nonumb) + i = 1; + if (ul && (i == 0)) { + font = sfont; + ul = cu = 0; + } + if (i) { + if (!ul) { + sfont = font; + font = ulfont; + } + ul = i; + } + noscale = 0; + mchbits(); +} + + +void caseuf(void) +{ + int i, j; + + if (skip() || !(i = getrq()) || i == 'S' || (j = findft(i)) == -1) + ulfont = ULFONT; /*default underline position*/ + else + ulfont = j; + if (NROFF && ulfont == FT) + ulfont = ULFONT; +} + + +void caseit(void) +{ + int i; + + lgf++; + it = itmac = 0; + noscale++; + skip(); + i = atoi0(); + skip(); + if (!nonumb && (itmac = getrq())) + it = i; + noscale = 0; +} + + +void casemc(void) +{ + int i; + + if (icf > 1) + ic = 0; + icf = 0; + if (skip()) + return; + ic = getch(); + icf = 1; + skip(); + i = max(hnumb((int *)0), 0); + if (!nonumb) + ics = i; +} + + +void casemk(void) +{ + int i, j; + + if (dip != d) + j = dip->dnl; + else + j = numtabp[NL].val; + if (skip()) { + dip->mkline = j; + return; + } + if ((i = getrq()) == 0) + return; + numtabp[findr(i)].val = j; +} + + +void casesv(void) +{ + int i; + + skip(); + if ((i = vnumb((int *)0)) < 0) + return; + if (nonumb) + i = 1; + sv += i; + caseos(); +} + + +void caseos(void) +{ + int savlss; + + if (sv <= findt1()) { + savlss = lss; + lss = sv; + newline(0); + lss = savlss; + sv = 0; + } +} + + +void casenm(void) +{ + int i; + + lnmod = nn = 0; + if (skip()) + return; + lnmod++; + noscale++; + i = inumb(&numtabp[LN].val); + if (!nonumb) + numtabp[LN].val = max(i, 0); + getnm(&ndf, 1); + getnm(&nms, 0); + getnm(&ni, 0); + getnm(&nmwid, 3); /* really kludgy! */ + noscale = 0; + nmbits = chbits; +} + +/* + * .nm relies on the fact that illegal args are skipped; don't warn + * for illegality of these + */ +void getnm(int *p, int min) +{ + int i; + int savtr = trace; + + eat(' '); + if (skip()) + return; + trace = 0; + i = atoi0(); + if (nonumb) + return; + *p = max(i, min); + trace = savtr; +} + + +void casenn(void) +{ + noscale++; + skip(); + nn = max(atoi0(), 1); + noscale = 0; +} + + +void caseab(void) +{ + casetm1(1, stderr); + done3(0); +} + + +/* nroff terminal handling has been pretty well excised */ +/* as part of the merge with troff. these are ghostly remnants, */ +/* called, but doing nothing. restore them at your peril. */ + + +void save_tty(void) /*save any tty settings that may be changed*/ +{ +} + + +void restore_tty(void) /*restore tty settings from beginning*/ +{ +} + + +void set_tty(void) +{ +} + + +void echo_off(void) /*turn off ECHO for .rd in "-q" mode*/ +{ +} + + +void echo_on(void) /*restore ECHO after .rd in "-q" mode*/ +{ +} diff --git a/troff/n6.c b/troff/n6.c @@ -0,0 +1,363 @@ +#include "tdef.h" +#include "ext.h" +#include "fns.h" +#include <ctype.h> + +/* + * n6.c -- width functions, sizes and fonts +*/ + +int +n_width(Tchar j) +{ + int i, k; + + if (iszbit(j)) + return 0; + if (ismot(j)) { + if (isvmot(j)) + return(0); + k = absmot(j); + if (isnmot(j)) + k = -k; + return(k); + } + i = cbits(j); + if (i < ' ') { + if (i == '\b') + return(-widthp); + if (i == PRESC) + i = eschar; + else if (i == HX) + return(0); + } + if (i == ohc) + return(0); + i = trtab[i]; + if (i < ' ') + return(0); + if (i >= t.tfont.nchars) /* not on the font */ + k = t.Char; /* really ought to check properly */ + else + k = t.tfont.wp[i].wid * t.Char; + widthp = k; + return(k); +} + + +Tchar n_setch(int c) +{ + return t_setch(c); +} + +Tchar n_setabs(void) /* set absolute char from \N'...' */ +{ /* for now, a no-op */ + return t_setabs(); +} + +int n_findft(int i) +{ + int k; + + if ((k = i - '0') >= 0 && k <= nfonts && k < smnt) + return(k); + for (k = 0; fontlab[k] != i; k++) + if (k > nfonts) + return(-1); + return(k); +} + + + +void n_mchbits(void) +{ + chbits = 0; + setfbits(chbits, font); + sps = width(' ' | chbits); +} + + +void n_setps(void ) +{ + int i, j; + + i = cbits(getch()); + if (isdigit(i)) { /* \sd or \sdd */ + i -= '0'; + if (i == 0) /* \s0 */ + ; + else if (i <= 3 && (ch=getch()) && isdigit(cbits(ch))) { /* \sdd */ + ch = 0; + } + } else if (i == '(') { /* \s(dd */ + getch(); + getch(); + } else if (i == '+' || i == '-') { /* \s+, \s- */ + j = cbits(getch()); + if (isdigit(j)) { /* \s+d, \s-d */ + ; + } else if (j == '(') { /* \s+(dd, \s-(dd */ + getch(); + getch(); + } + } +} + + +Tchar n_setht(void) /* set character height from \H'...' */ +{ + + getch(); + inumb(&apts); + getch(); + return(0); +} + + +Tchar n_setslant(void) /* set slant from \S'...' */ +{ + int n; + + getch(); + n = 0; + n = inumb(&n); + getch(); + return(0); +} + + +void n_caseft(void) +{ + skip(); + setfont(1); +} + + +void n_setfont(int a) +{ + int i, j; + + if (a) + i = getrq(); + else + i = getsn(); + if (!i || i == 'P') { + j = font1; + goto s0; + } + if (i == 'S' || i == '0') + return; + if ((j = findft(i)) == -1) + return; +s0: + font1 = font; + font = j; + mchbits(); +} + + +void n_setwd(void) +{ + int base, wid; + Tchar i; + int delim, emsz, k; + int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1; + + base = numtabp[ST].val = numtabp[ST].val = wid = numtabp[CT].val = 0; + if (ismot(i = getch())) + return; + delim = cbits(i); + savhp = numtabp[HP].val; + numtabp[HP].val = 0; + savapts = apts; + savapts1 = apts1; + savfont = font; + savfont1 = font1; + savpts = pts; + savpts1 = pts1; + setwdf++; + while (cbits(i = getch()) != delim && !nlflg) { + k = width(i); + wid += k; + numtabp[HP].val += k; + if (!ismot(i)) { + emsz = (INCH * pts + 36) / 72; + } else if (isvmot(i)) { + k = absmot(i); + if (isnmot(i)) + k = -k; + base -= k; + emsz = 0; + } else + continue; + if (base < numtabp[SB].val) + numtabp[SB].val = base; + if ((k = base + emsz) > numtabp[ST].val) + numtabp[ST].val = k; + } + setn1(wid, 0, (Tchar) 0); + numtabp[HP].val = savhp; + apts = savapts; + apts1 = savapts1; + font = savfont; + font1 = savfont1; + pts = savpts; + pts1 = savpts1; + mchbits(); + setwdf = 0; +} + + +Tchar n_vmot(void) +{ + dfact = lss; + vflag++; + return n_mot(); +} + + +Tchar n_hmot(void) +{ + dfact = EM; + return n_mot(); +} + + +Tchar n_mot(void) +{ + int j, n; + Tchar i; + + j = HOR; + getch(); /*eat delim*/ + if (n = atoi0()) { + if (vflag) + j = VERT; + i = makem(quant(n, j)); + } else + i = 0; + getch(); + vflag = 0; + dfact = 1; + return(i); +} + + +Tchar n_sethl(int k) +{ + int j; + Tchar i; + + j = t.Halfline; + if (k == 'u') + j = -j; + else if (k == 'r') + j = -2 * j; + vflag++; + i = makem(j); + vflag = 0; + return(i); +} + + +Tchar n_makem(int i) +{ + Tchar j; + + if (i >= 0) + j = i; + else + j = -i; + j |= MOT; + if (i < 0) + j |= NMOT; + if (vflag) + j |= VMOT; + return(j); +} + + +void n_casefp(void) +{ + int i, j; + + skip(); + if ((i = cbits(getch()) - '0') < 0 || i > nfonts) + return; + if (skip() || !(j = getrq())) + return; + fontlab[i] = j; +} + + + +void n_casebd(void) +{ + int i, j, k; + + j = k = 0; +bd0: + if (skip() || !(i = getrq()) || (j = findft(i)) == -1) { + if (k) + goto bd1; + else + return; + } + if (j == smnt) { + k = smnt; + goto bd0; + } + if (k) { + sbold = j; + j = k; + } +bd1: + skip(); + noscale++; + bdtab[j] = atoi0(); + noscale = 0; +} + + +void n_casevs(void) +{ + int i; + + skip(); + vflag++; + dfact = INCH; /*default scaling is points!*/ + dfactd = 72; + res = VERT; + i = inumb(&lss); + if (nonumb) + i = lss1; + if (i < VERT) + i = VERT; /* was VERT */ + lss1 = lss; + lss = i; +} + + + + +Tchar n_xlss(void) +{ + /* stores \x'...' into + /* two successive Tchars. + /* the first contains HX, the second the value, + /* encoded as a vertical motion. + /* decoding is done in n2.c by pchar(). + */ + int i; + + getch(); + dfact = lss; + i = quant(atoi0(), VERT); + dfact = 1; + getch(); + if (i >= 0) + *pbp++ = MOT | VMOT | i; + else + *pbp++ = MOT | VMOT | NMOT | -i; + return(HX); +} diff --git a/troff/n7.c b/troff/n7.c @@ -0,0 +1,837 @@ +#define _BSD_SOURCE 1 /* isascii */ +#include "tdef.h" +#include "fns.h" +#include "ext.h" + +#ifdef STRICT + /* not in ANSI or POSIX */ +#define isascii(a) ((a) >= 0 && (a) <= 127) +#endif + +#define GETCH gettch +Tchar gettch(void); + + +/* + * troff7.c + * + * text + */ + +int brflg; + +void tbreak(void) +{ + int pad, k; + Tchar *i, j; + int resol; + int un0 = un; + + trap = 0; + if (nb) + return; + if (dip == d && numtabp[NL].val == -1) { + newline(1); + return; + } + if (!nc) { + setnel(); + if (!wch) + return; + if (pendw) + getword(1); + movword(); + } else if (pendw && !brflg) { + getword(1); + movword(); + } + *linep = dip->nls = 0; + if (NROFF && dip == d) + horiz(po); + if (lnmod) + donum(); + lastl = ne; + if (brflg != 1) { + totout = 0; + } else if (ad) { + if ((lastl = ll - un) < ne) + lastl = ne; + } + if (admod && ad && (brflg != 2)) { + lastl = ne; + adsp = adrem = 0; + if (admod == 1) + un += quant(nel / 2, HOR); + else if (admod == 2) + un += nel; + } + totout++; + brflg = 0; + if (lastl + un > dip->maxl) + dip->maxl = lastl + un; + horiz(un); + if (NROFF) { + if (adrem % t.Adj) + resol = t.Hor; + else + resol = t.Adj; + } else + resol = HOR; + + lastl = ne + (nwd-1) * adsp + adrem; + for (i = line; nc > 0; ) { + if ((cbits(j = *i++)) == ' ') { + pad = 0; + do { + pad += width(j); + nc--; + } while ((cbits(j = *i++)) == ' '); + i--; + pad += adsp; + --nwd; + if (adrem) { + if (adrem < 0) { + pad -= resol; + adrem += resol; + } else if ((totout & 01) || adrem / resol >= nwd) { + pad += resol; + adrem -= resol; + } + } + pchar((Tchar) WORDSP); + horiz(pad); + } else { + pchar(j); + nc--; + } + } + if (ic) { + if ((k = ll - un0 - lastl + ics) > 0) + horiz(k); + pchar(ic); + } + if (icf) + icf++; + else + ic = 0; + ne = nwd = 0; + un = in; + setnel(); + newline(0); + if (dip != d) { + if (dip->dnl > dip->hnl) + dip->hnl = dip->dnl; + } else { + if (numtabp[NL].val > dip->hnl) + dip->hnl = numtabp[NL].val; + } + for (k = ls - 1; k > 0 && !trap; k--) + newline(0); + spread = 0; +} + +void donum(void) +{ + int i, nw; + int lnv = numtabp[LN].val; + + nrbits = nmbits; + nw = width('1' | nrbits); + if (nn) { + nn--; + goto d1; + } + if (lnv % ndf) { + numtabp[LN].val++; +d1: + un += nw * (nmwid + nms + ni); + return; + } + i = 0; + do { /* count digits in numtabp[LN].val */ + i++; + } while ((lnv /= 10) > 0); + horiz(nw * (ni + max(nmwid-i, 0))); + nform = 0; + fnumb(numtabp[LN].val, pchar); + un += nw * nms; + numtabp[LN].val++; +} + + +void text(void) +{ + Tchar i; + static int spcnt; + + nflush++; + numtabp[HP].val = 0; + if ((dip == d) && (numtabp[NL].val == -1)) { + newline(1); + return; + } + setnel(); + if (ce || !fi) { + nofill(); + return; + } + if (pendw) + goto t4; + if (pendt) + if (spcnt) + goto t2; + else + goto t3; + pendt++; + if (spcnt) + goto t2; + while ((cbits(i = GETCH())) == ' ') { + spcnt++; + numtabp[HP].val += sps; + widthp = sps; + } + if (nlflg) { +t1: + nflush = pendt = ch = spcnt = 0; + callsp(); + return; + } + ch = i; + if (spcnt) { +t2: + tbreak(); + if (nc || wch) + goto rtn; + un += spcnt * sps; + spcnt = 0; + setnel(); + if (trap) + goto rtn; + if (nlflg) + goto t1; + } +t3: + if (spread) + goto t5; + if (pendw || !wch) +t4: + if (getword(0)) + goto t6; + if (!movword()) + goto t3; +t5: + if (nlflg) + pendt = 0; + adsp = adrem = 0; + if (ad) { + if (nwd == 1) + adsp = nel; + else + adsp = nel / (nwd - 1); + adsp = (adsp / HOR) * HOR; + adrem = nel - adsp*(nwd-1); + } + brflg = 1; + tbreak(); + spread = 0; + if (!trap) + goto t3; + if (!nlflg) + goto rtn; +t6: + pendt = 0; + ckul(); +rtn: + nflush = 0; +} + + +void nofill(void) +{ + int j; + Tchar i; + + if (!pendnf) { + over = 0; + tbreak(); + if (trap) + goto rtn; + if (nlflg) { + ch = nflush = 0; + callsp(); + return; + } + adsp = adrem = 0; + nwd = 10000; + } + while ((j = (cbits(i = GETCH()))) != '\n') { + if (j == ohc) + continue; + if (j == CONT) { + pendnf++; + nflush = 0; + flushi(); + ckul(); + return; + } + j = width(i); + widthp = j; + numtabp[HP].val += j; + storeline(i, j); + } + if (ce) { + ce--; + if ((i = quant(nel / 2, HOR)) > 0) + un += i; + } + if (!nc) + storeline((Tchar)FILLER, 0); + brflg = 2; + tbreak(); + ckul(); +rtn: + pendnf = nflush = 0; +} + + +void callsp(void) +{ + int i; + + if (flss) + i = flss; + else + i = lss; + flss = 0; + casesp1(i); +} + + +void ckul(void) +{ + if (ul && (--ul == 0)) { + cu = 0; + font = sfont; + mchbits(); + } + if (it && --it == 0 && itmac) + control(itmac, 0); +} + + +void storeline(Tchar c, int w) +{ + int diff; + + if (linep >= line + lnsize - 2) { + lnsize += LNSIZE; + diff = linep - line; + if (( line = (Tchar *)realloc((char *)line, lnsize * sizeof(Tchar))) != NULL) { + if (linep && diff) + linep = line + diff; + } else { + if (over) { + return; + } else { + flusho(); + ERROR "Line overflow." WARN; + over++; + *linep++ = LEFTHAND; + w = width(LEFTHAND); + nc++; + c = '\n'; + } + } + } + *linep++ = c; + ne += w; + nel -= w; + nc++; +} + + +void newline(int a) +{ + int i, j, nlss; + int opn; + + nlss = 0; + if (a) + goto nl1; + if (dip != d) { + j = lss; + pchar1((Tchar)FLSS); + if (flss) + lss = flss; + i = lss + dip->blss; + dip->dnl += i; + pchar1((Tchar)i); + pchar1((Tchar)'\n'); + lss = j; + dip->blss = flss = 0; + if (dip->alss) { + pchar1((Tchar)FLSS); + pchar1((Tchar)dip->alss); + pchar1((Tchar)'\n'); + dip->dnl += dip->alss; + dip->alss = 0; + } + if (dip->ditrap && !dip->ditf && dip->dnl >= dip->ditrap && dip->dimac) + if (control(dip->dimac, 0)) { + trap++; + dip->ditf++; + } + return; + } + j = lss; + if (flss) + lss = flss; + nlss = dip->alss + dip->blss + lss; + numtabp[NL].val += nlss; + if (TROFF && ascii) { + dip->alss = dip->blss = 0; + } + pchar1((Tchar)'\n'); + flss = 0; + lss = j; + if (numtabp[NL].val < pl) + goto nl2; +nl1: + ejf = dip->hnl = numtabp[NL].val = 0; + ejl = frame; + if (donef) { + if ((!nc && !wch) || ndone) + done1(0); + ndone++; + donef = 0; + if (frame == stk) + nflush++; + } + opn = numtabp[PN].val; + numtabp[PN].val++; + if (npnflg) { + numtabp[PN].val = npn; + npn = npnflg = 0; + } +nlpn: + if (numtabp[PN].val == pfrom) { + print++; + pfrom = -1; + } else if (opn == pto) { + print = 0; + opn = -1; + chkpn(); + goto nlpn; + } + if (print) + ptpage(numtabp[PN].val); /* supposedly in a clean state so can pause */ + if (stop && print) { + dpn++; + if (dpn >= stop) { + dpn = 0; + ptpause(); + } + } +nl2: + trap = 0; + if (numtabp[NL].val == 0) { + if ((j = findn(0)) != NTRAP) + trap = control(mlist[j], 0); + } else if ((i = findt(numtabp[NL].val - nlss)) <= nlss) { + if ((j = findn1(numtabp[NL].val - nlss + i)) == NTRAP) { + flusho(); + ERROR "Trap botch." WARN; + done2(-5); + } + trap = control(mlist[j], 0); + } +} + +int +findn1(int a) +{ + int i, j; + + for (i = 0; i < NTRAP; i++) { + if (mlist[i]) { + if ((j = nlist[i]) < 0) + j += pl; + if (j == a) + break; + } + } + return(i); +} + + +void chkpn(void) +{ + pto = *(pnp++); + pfrom = pto>=0 ? pto : -pto; + if (pto == -INT_MAX) { + flusho(); + done1(0); + } + if (pto < 0) { + pto = -pto; + print++; + pfrom = 0; + } +} + +int +findt(int a) +{ + int i, j, k; + + k = INT_MAX; + if (dip != d) { + if (dip->dimac && (i = dip->ditrap - a) > 0) + k = i; + return(k); + } + for (i = 0; i < NTRAP; i++) { + if (mlist[i]) { + if ((j = nlist[i]) < 0) + j += pl; + if ((j -= a) <= 0) + continue; + if (j < k) + k = j; + } + } + i = pl - a; + if (k > i) + k = i; + return(k); +} + +int +findt1(void) +{ + int i; + + if (dip != d) + i = dip->dnl; + else + i = numtabp[NL].val; + return(findt(i)); +} + + +void eject(Stack *a) +{ + int savlss; + + if (dip != d) + return; + ejf++; + if (a) + ejl = a; + else + ejl = frame; + if (trap) + return; +e1: + savlss = lss; + lss = findt(numtabp[NL].val); + newline(0); + lss = savlss; + if (numtabp[NL].val && !trap) + goto e1; +} + +int +movword(void) +{ + int w; + Tchar i, *wp; + int savwch, hys; + + over = 0; + wp = wordp; + if (!nwd) { + while (cbits(*wp++) == ' ') { + wch--; + wne -= sps; + } + wp--; + } + if (wne > nel && !hyoff && hyf && (!nwd || nel > 3 * sps) && + (!(hyf & 02) || (findt1() > lss))) + hyphen(wp); + savwch = wch; + hyp = hyptr; + nhyp = 0; + while (*hyp && *hyp <= wp) + hyp++; + while (wch) { + if (hyoff != 1 && *hyp == wp) { + hyp++; + if (!wdstart || (wp > wdstart + 1 && wp < wdend && + (!(hyf & 04) || wp < wdend - 1) && /* 04 => last 2 */ + (!(hyf & 010) || wp > wdstart + 2))) { /* 010 => 1st 2 */ + nhyp++; + storeline((Tchar)IMP, 0); + } + } + i = *wp++; + w = width(i); + wne -= w; + wch--; + storeline(i, w); + } + if (nel >= 0) { + nwd++; + return(0); /* line didn't fill up */ + } + if (TROFF) + xbits((Tchar)HYPHEN, 1); + hys = width((Tchar)HYPHEN); +m1: + if (!nhyp) { + if (!nwd) + goto m3; + if (wch == savwch) + goto m4; + } + if (*--linep != IMP) + goto m5; + if (!(--nhyp)) + if (!nwd) + goto m2; + if (nel < hys) { + nc--; + goto m1; + } +m2: + if ((i = cbits(*(linep - 1))) != '-' && i != EMDASH) { + *linep = (*(linep - 1) & SFMASK) | HYPHEN; + w = width(*linep); + nel -= w; + ne += w; + linep++; + } +m3: + nwd++; +m4: + wordp = wp; + return(1); /* line filled up */ +m5: + nc--; + w = width(*linep); + ne -= w; + nel += w; + wne += w; + wch++; + wp--; + goto m1; +} + + +void horiz(int i) +{ + vflag = 0; + if (i) + pchar(makem(i)); +} + + +void setnel(void) +{ + if (!nc) { + linep = line; + if (un1 >= 0) { + un = un1; + un1 = -1; + } + nel = ll - un; + ne = adsp = adrem = 0; + } +} + +int +getword(int x) +{ + int j, k; + Tchar i, *wp; + int noword; + int obits; + + j = 0; + noword = 0; + if (x) + if (pendw) { + *pendw = 0; + goto rtn; + } + if (wordp = pendw) + goto g1; + hyp = hyptr; + wordp = word; + over = wne = wch = 0; + hyoff = 0; + obits = chbits; + while (1) { /* picks up 1st char of word */ + j = cbits(i = GETCH()); + if (j == '\n') { + wne = wch = 0; + noword = 1; + goto rtn; + } + if (j == ohc) { + hyoff = 1; /* 1 => don't hyphenate */ + continue; + } + if (j == ' ') { + numtabp[HP].val += sps; + widthp = sps; + storeword(i, sps); + continue; + } + break; + } + storeword(' ' | obits, sps); + if (spflg) { + storeword(' ' | obits, sps); + spflg = 0; + } +g0: + if (j == CONT) { + pendw = wordp; + nflush = 0; + flushi(); + return(1); + } + if (hyoff != 1) { + if (j == ohc) { + hyoff = 2; + *hyp++ = wordp; + if (hyp > hyptr + NHYP - 1) + hyp = hyptr + NHYP - 1; + goto g1; + } + if (((j == '-' || j == EMDASH)) && !(i & ZBIT)) /* zbit avoids \X */ + if (wordp > word + 1) { + hyoff = 2; + *hyp++ = wordp + 1; + if (hyp > hyptr + NHYP - 1) + hyp = hyptr + NHYP - 1; + } + } + j = width(i); + numtabp[HP].val += j; + storeword(i, j); +g1: + j = cbits(i = GETCH()); + if (j != ' ') { + static char *sentchar = ".?!"; /* sentence terminators */ + if (j != '\n') + goto g0; + wp = wordp-1; /* handle extra space at end of sentence */ + while (wp >= word) { + j = cbits(*wp--); + if (j=='"' || j=='\'' || j==')' || j==']' || j=='*' || j==DAGGER) + continue; + for (k = 0; sentchar[k]; k++) + if (j == sentchar[k]) { + spflg++; + break; + } + break; + } + } + *wordp = 0; + numtabp[HP].val += sps; +rtn: + for (wp = word; *wp; wp++) { + if (ismot(j)) + break; /* drechsler */ + j = cbits(*wp); + if (j == ' ') + continue; + if (!(isascii(j) && isdigit(j)) && j != '-') + break; + } + if (*wp == 0) /* all numbers, so don't hyphenate */ + hyoff = 1; + wdstart = 0; + wordp = word; + pendw = 0; + *hyp++ = 0; + setnel(); + return(noword); +} + + +void storeword(Tchar c, int w) +{ + Tchar *savp; + int i; + + if (wordp >= word + wdsize - 2) { + wdsize += WDSIZE; + savp = word; + if (( word = (Tchar *)realloc((char *)word, wdsize * sizeof(Tchar))) != NULL) { + if (wordp) + wordp = word + (wordp - savp); + if (pendw) + pendw = word + (pendw - savp); + if (wdstart) + wdstart = word + (wdstart - savp); + if (wdend) + wdend = word + (wdend - savp); + for (i = 0; i < NHYP; i++) + if (hyptr[i]) + hyptr[i] = word + (hyptr[i] - savp); + } else { + if (over) { + return; + } else { + flusho(); + ERROR "Word overflow." WARN; + over++; + c = LEFTHAND; + w = width(LEFTHAND); + } + } + } + widthp = w; + wne += w; + *wordp++ = c; + wch++; +} + + +Tchar gettch(void) +{ + extern int c_isalnum; + Tchar i; + int j; + + if (TROFF) + return getch(); + + i = getch(); + j = cbits(i); + if (ismot(i) || fbits(i) != ulfont) + return(i); + if (cu) { + if (trtab[j] == ' ') { + setcbits(i, '_'); + setfbits(i, FT); /* default */ + } + return(i); + } + /* should test here for characters that ought to be underlined */ + /* in the old nroff, that was the 200 bit on the width! */ + /* for now, just do letters, digits and certain special chars */ + if (j <= 127) { + if (!isalnum(j)) + setfbits(i, FT); + } else { + if (j < c_isalnum) + setfbits(i, FT); + } + return(i); +} diff --git a/troff/n8.c b/troff/n8.c @@ -0,0 +1,545 @@ +#include <u.h> +#include "tdef.h" +#include "fns.h" +#include "ext.h" + +#define HY_BIT 0200 /* stuff in here only works for 7-bit ascii */ + /* this value is used (as a literal) in suftab.c */ + /* to encode possible hyphenation points in suffixes. */ + /* it could be changed, by widening the tables */ + /* to be shorts instead of chars. */ + +/* + * troff8.c + * + * hyphenation + */ + +int hexsize = 0; /* hyphenation exception list size */ +char *hbufp = NULL; /* base of list */ +char *nexth = NULL; /* first free slot in list */ +Tchar *hyend; + +#define THRESH 160 /* digram goodness threshold */ +int thresh = THRESH; + +int texhyphen(void); +static int alpha(Tchar); + +void hyphen(Tchar *wp) +{ + int j; + Tchar *i; + + i = wp; + while (punct((*i++))) + ; + if (!alpha(*--i)) + return; + wdstart = i++; + while (alpha(*i++)) + ; + hyend = wdend = --i - 1; + while (punct((*i++))) + ; + if (*--i) + return; + if (wdend - wdstart < 4) /* 4 chars is too short to hyphenate */ + return; + hyp = hyptr; + *hyp = 0; + hyoff = 2; + + /* for now, try exceptions first, then tex (if hyphalg is non-zero), + then suffix and digram if tex didn't hyphenate it at all. + */ + + if (!exword() && !texhyphen() && !suffix()) + digram(); + + /* this appears to sort hyphenation points into increasing order */ + *hyp++ = 0; + if (*hyptr) + for (j = 1; j; ) { + j = 0; + for (hyp = hyptr + 1; *hyp != 0; hyp++) { + if (*(hyp - 1) > *hyp) { + j++; + i = *hyp; + *hyp = *(hyp - 1); + *(hyp - 1) = i; + } + } + } +} + +static int alpha(Tchar i) /* non-zero if really alphabetic */ +{ + if (ismot(i)) + return 0; + else if (cbits(i) >= ALPHABET) /* this isn't very elegant, but there's */ + return 0; /* no good way to make sure i is in range for */ + else /* the call of isalpha */ + return isalpha(cbits(i)); +} + +int +punct(Tchar i) +{ + if (!i || alpha(i)) + return(0); + else + return(1); +} + + +void caseha(void) /* set hyphenation algorithm */ +{ + hyphalg = HYPHALG; + if (skip()) + return; + noscale++; + hyphalg = atoi0(); + noscale = 0; +} + + +void caseht(void) /* set hyphenation threshold; not in manual! */ +{ + thresh = THRESH; + if (skip()) + return; + noscale++; + thresh = atoi0(); + noscale = 0; +} + + +char *growh(char *where) +{ + char *new; + + hexsize += NHEX; + if ((new = grow(hbufp, hexsize, sizeof(char))) == NULL) + return NULL; + if (new == hbufp) { + return where; + } else { + int diff; + diff = where - hbufp; + hbufp = new; + return new + diff; + } +} + + +void casehw(void) +{ + int i, k; + char *j; + Tchar t; + + if (nexth == NULL) { + if ((nexth = hbufp = grow(hbufp, NHEX, sizeof(char))) == NULL) { + ERROR "No space for exception word list." WARN; + return; + } + hexsize = NHEX; + } + k = 0; + while (!skip()) { + if ((j = nexth) >= hbufp + hexsize - 2) + if ((j = nexth = growh(j)) == NULL) + goto full; + for (;;) { + if (ismot(t = getch())) + continue; + i = cbits(t); + if (i == ' ' || i == '\n') { + *j++ = 0; + nexth = j; + *j = 0; + if (i == ' ') + break; + else + return; + } + if (i == '-') { + k = HY_BIT; + continue; + } + *j++ = maplow(i) | k; + k = 0; + if (j >= hbufp + hexsize - 2) + if ((j = growh(j)) == NULL) + goto full; + } + } + return; +full: + ERROR "Cannot grow exception word list." WARN; + *nexth = 0; +} + + +int exword(void) +{ + Tchar *w; + char *e, *save; + + e = hbufp; + while (1) { + save = e; + if (e == NULL || *e == 0) + return(0); + w = wdstart; + while (*e && w <= hyend && (*e & 0177) == maplow(cbits(*w))) { + e++; + w++; + } + if (!*e) { + if (w-1 == hyend || (w == wdend && maplow(cbits(*w)) == 's')) { + w = wdstart; + for (e = save; *e; e++) { + if (*e & HY_BIT) + *hyp++ = w; + if (hyp > hyptr + NHYP - 1) + hyp = hyptr + NHYP - 1; + w++; + } + return(1); + } else { + e++; + continue; + } + } else + while (*e++) + ; + } +} + +int +suffix(void) +{ + Tchar *w; + char *s, *s0; + Tchar i; + extern char *suftab[]; + +again: + i = cbits(*hyend); + if (!alpha(i)) + return(0); + if (i < 'a') + i -= 'A' - 'a'; + if ((s0 = suftab[i-'a']) == 0) + return(0); + for (;;) { + if ((i = *s0 & 017) == 0) + return(0); + s = s0 + i - 1; + w = hyend - 1; + while (s > s0 && w >= wdstart && (*s & 0177) == maplow(cbits(*w))) { + s--; + w--; + } + if (s == s0) + break; + s0 += i; + } + s = s0 + i - 1; + w = hyend; + if (*s0 & HY_BIT) + goto mark; + while (s > s0) { + w--; + if (*s-- & HY_BIT) { +mark: + hyend = w - 1; + if (*s0 & 0100) /* 0100 used in suftab to encode something too */ + continue; + if (!chkvow(w)) + return(0); + *hyp++ = w; + } + } + if (*s0 & 040) + return(0); + if (exword()) + return(1); + goto again; +} + +int +maplow(int i) +{ + if (isupper(i)) + i = tolower(i); + return(i); +} + +int +vowel(int i) +{ + switch (i) { + case 'a': case 'A': + case 'e': case 'E': + case 'i': case 'I': + case 'o': case 'O': + case 'u': case 'U': + case 'y': case 'Y': + return(1); + default: + return(0); + } +} + + +Tchar *chkvow(Tchar *w) +{ + while (--w >= wdstart) + if (vowel(cbits(*w))) + return(w); + return(0); +} + + +void digram(void) +{ + Tchar *w; + int val; + Tchar *nhyend, *maxw; + int maxval; + extern char bxh[26][13], bxxh[26][13], xxh[26][13], xhx[26][13], hxx[26][13]; + maxw = 0; +again: + if (!(w = chkvow(hyend + 1))) + return; + hyend = w; + if (!(w = chkvow(hyend))) + return; + nhyend = w; + maxval = 0; + w--; + while (++w < hyend && w < wdend - 1) { + val = 1; + if (w == wdstart) + val *= dilook('a', cbits(*w), bxh); + else if (w == wdstart + 1) + val *= dilook(cbits(*(w-1)), cbits(*w), bxxh); + else + val *= dilook(cbits(*(w-1)), cbits(*w), xxh); + val *= dilook(cbits(*w), cbits(*(w+1)), xhx); + val *= dilook(cbits(*(w+1)), cbits(*(w+2)), hxx); + if (val > maxval) { + maxval = val; + maxw = w + 1; + } + } + hyend = nhyend; + if (maxval > thresh) + *hyp++ = maxw; + goto again; +} + +int +dilook(int a, int b, char t[26][13]) +{ + int i, j; + + i = t[maplow(a)-'a'][(j = maplow(b)-'a')/2]; + if (!(j & 01)) + i >>= 4; + return(i & 017); +} + + +/* here beginneth the tex hyphenation code, as interpreted freely */ +/* the main difference is that there is no attempt to squeeze space */ +/* as tightly at tex does. */ + +static int texit(Tchar *, Tchar *); +static int readpats(void); +static void install(char *); +static void fixup(void); +static int trieindex(int, int); + +static char pats[50000]; /* size ought to be computed dynamically */ +static char *nextpat = pats; +static char *trie[27*27]; /* english-specific sizes */ + +int texhyphen(void) +{ + static int loaded = 0; /* -1: couldn't find tex file */ + + if (hyphalg == 0 || loaded == -1) /* non-zero => tex for now */ + return 0; + if (loaded == 0) { + if (readpats()) + loaded = 1; + else + loaded = -1; + } + return texit(wdstart, wdend); +} + +static int texit(Tchar *start, Tchar *end) /* hyphenate as in tex, return # found */ +{ + int nw, i, k, equal, cnt[500]; + char w[500+1], *np, *pp, *wp, *xpp, *xwp; + + w[0] = '.'; + for (nw = 1; start <= end && nw < 500-1; nw++, start++) + w[nw] = maplow(tolower(cbits(*start))); + start -= (nw - 1); + w[nw++] = '.'; + w[nw] = 0; +/* + * printf("try %s\n", w); +*/ + for (i = 0; i <= nw; i++) + cnt[i] = '0'; + + for (wp = w; wp+1 < w+nw; wp++) { + for (pp = trie[trieindex(*wp, *(wp+1))]; pp < nextpat; ) { + if (pp == 0 /* no trie entry */ + || *pp != *wp /* no match on 1st letter */ + || *(pp+1) != *(wp+1)) /* no match on 2nd letter */ + break; /* so move to next letter of word */ + equal = 1; + for (xpp = pp+2, xwp = wp+2; *xpp; ) + if (*xpp++ != *xwp++) { + equal = 0; + break; + } + if (equal) { + np = xpp+1; /* numpat */ + for (k = wp-w; *np; k++, np++) + if (*np > cnt[k]) + cnt[k] = *np; +/* + * printf("match: %s %s\n", pp, xpp+1); +*/ + } + pp += *(pp-1); /* skip over pattern and numbers to next */ + } + } +/* + * for (i = 0; i < nw; i++) printf("%c", w[i]); + * printf(" "); + * for (i = 0; i <= nw; i++) printf("%c", cnt[i]); + * printf("\n"); +*/ +/* + * for (i = 1; i < nw - 1; i++) { + * if (i > 2 && i < nw - 3 && cnt[i] % 2) + * printf("-"); + * if (cbits(start[i-1]) != '.') + * printf("%c", cbits(start[i-1])); + * } + * printf("\n"); +*/ + for (i = 1; i < nw -1; i++) + if (i > 2 && i < nw - 3 && cnt[i] % 2) + *hyp++ = start + i - 1; + return hyp - hyptr; /* non-zero if a hyphen was found */ +} + +/* + This code assumes that hyphen.tex looks like + % some comments + \patterns{ % more comments + pat5ter4ns, 1 per line, SORTED, nothing else + } + more goo + \hyphenation{ % more comments + ex-cep-tions, one per line; i ignore this part for now + } + + this code is NOT robust against variations. unfortunately, + it looks like every local language version of this file has + a different format. i have also made no provision for weird + characters. sigh. +*/ + +static int readpats(void) +{ + FILE *fp; + char buf[200], buf1[200]; + + if ((fp = fopen(unsharp(TEXHYPHENS), "r")) == NULL + && (fp = fopen(unsharp(DWBalthyphens), "r")) == NULL) { + ERROR "warning: can't find hyphen.tex" WARN; + return 0; + } + + while (fgets(buf, sizeof buf, fp) != NULL) { + sscanf(buf, "%s", buf1); + if (strcmp(buf1, "\\patterns{") == 0) + break; + } + while (fgets(buf, sizeof buf, fp) != NULL) { + if (buf[0] == '}') + break; + install(buf); + } + fclose(fp); + fixup(); + return 1; +} + +static void install(char *s) /* map ab4c5de to: 12 abcde \0 00405 \0 */ +{ + int npat, lastpat; + char num[500], *onextpat = nextpat; + + num[0] = '0'; + *nextpat++ = ' '; /* fill in with count later */ + for (npat = lastpat = 0; *s != '\n' && *s != '\0'; s++) { + if (isdigit((uchar)*s)) { + num[npat] = *s; + lastpat = npat; + } else { + *nextpat++ = *s; + npat++; + num[npat] = '0'; + } + } + *nextpat++ = 0; + if (nextpat > pats + sizeof(pats)-20) { + ERROR "tex hyphenation table overflow, tail end ignored" WARN; + nextpat = onextpat; + } + num[lastpat+1] = 0; + strcat(nextpat, num); + nextpat += strlen(nextpat) + 1; +} + +static void fixup(void) /* build indexes of where . a b c ... start */ +{ + char *p, *lastc; + int n; + + for (lastc = pats, p = pats+1; p < nextpat; p++) + if (*p == ' ') { + *lastc = p - lastc; + lastc = p; + } + *lastc = p - lastc; + for (p = pats+1; p < nextpat; ) { + n = trieindex(p[0], p[1]); + if (trie[n] == 0) + trie[n] = p; + p += p[-1]; + } + /* printf("pats = %d\n", nextpat - pats); */ +} + +static int trieindex(int d1, int d2) +{ + int z; + + z = 27 * (d1 == '.' ? 0 : d1 - 'a' + 1) + (d2 == '.' ? 0 : d2 - 'a' + 1); + assert(z >= 0 && z < 27*27); + return z; +} diff --git a/troff/n9.c b/troff/n9.c @@ -0,0 +1,489 @@ +#include "tdef.h" +#include "ext.h" +#include "fns.h" + +/* + * troff9.c + * + * misc functions + */ + +Tchar setz(void) +{ + Tchar i; + + if (!ismot(i = getch())) + i |= ZBIT; + return(i); +} + +void setline(void) +{ + Tchar *i; + Tchar c; + int length; + int j, w, cnt, delim, rem, temp; + Tchar linebuf[NC]; + + if (ismot(c = getch())) + return; + delim = cbits(c); + vflag = 0; + dfact = EM; + length = quant(atoi0(), HOR); + dfact = 1; + if (!length) { + eat(delim); + return; + } +s0: + if ((j = cbits(c = getch())) == delim || j == '\n') { + ch = c; + c = RULE | chbits; + } else if (cbits(c) == FILLER) + goto s0; + w = width(c); + if (w <= 0) { + ERROR "zero-width underline character ignored" WARN; + c = RULE | chbits; + w = width(c); + } + i = linebuf; + if (length < 0) { + *i++ = makem(length); + length = -length; + } + if (!(cnt = length / w)) { + *i++ = makem(-(temp = ((w - length) / 2))); + *i++ = c; + *i++ = makem(-(w - length - temp)); + goto s1; + } + if (rem = length % w) { + if (cbits(c) == RULE || cbits(c) == UNDERLINE || cbits(c) == ROOTEN) + *i++ = c | ZBIT; + *i++ = makem(rem); + } + if (cnt) { + *i++ = RPT; + *i++ = cnt; + *i++ = c; + } +s1: + *i = 0; + eat(delim); + pushback(linebuf); +} + +int +eat(int c) +{ + int i; + + while ((i = cbits(getch())) != c && i != '\n') + ; + return(i); +} + + +void setov(void) +{ + int j, k; + Tchar i, o[NOV+1]; + int delim, w[NOV+1]; + + if (ismot(i = getch())) + return; + delim = cbits(i); + for (k = 0; k < NOV && (j = cbits(i = getch())) != delim && j != '\n'; k++) { + o[k] = i; + w[k] = width(i); + } + o[k] = w[k] = 0; + if (o[0]) + for (j = 1; j; ) { + j = 0; + for (k = 1; o[k] ; k++) { + if (w[k-1] < w[k]) { + j++; + i = w[k]; + w[k] = w[k-1]; + w[k-1] = i; + i = o[k]; + o[k] = o[k-1]; + o[k-1] = i; + } + } + } + else + return; + *pbp++ = makem(w[0] / 2); + for (k = 0; o[k]; k++) + ; + while (k>0) { + k--; + *pbp++ = makem(-((w[k] + w[k+1]) / 2)); + *pbp++ = o[k]; + } +} + + +void setbra(void) +{ + int k; + Tchar i, *j, dwn; + int cnt, delim; + Tchar brabuf[NC]; + + if (ismot(i = getch())) + return; + delim = cbits(i); + j = brabuf + 1; + cnt = 0; + if (NROFF) + dwn = (2 * t.Halfline) | MOT | VMOT; + else + dwn = EM | MOT | VMOT; + while ((k = cbits(i = getch())) != delim && k != '\n' && j <= brabuf + NC - 4) { + *j++ = i | ZBIT; + *j++ = dwn; + cnt++; + } + if (--cnt < 0) + return; + else if (!cnt) { + ch = *(j - 2); + return; + } + *j = 0; + if (NROFF) + *--j = *brabuf = (cnt * t.Halfline) | MOT | NMOT | VMOT; + else + *--j = *brabuf = (cnt * EM) / 2 | MOT | NMOT | VMOT; + *--j &= ~ZBIT; + pushback(brabuf); +} + + +void setvline(void) +{ + int i; + Tchar c, rem, ver, neg; + int cnt, delim, v; + Tchar vlbuf[NC]; + Tchar *vlp; + + if (ismot(c = getch())) + return; + delim = cbits(c); + dfact = lss; + vflag++; + i = quant(atoi0(), VERT); + dfact = 1; + if (!i) { + eat(delim); + vflag = 0; + return; + } + if ((cbits(c = getch())) == delim) { + c = BOXRULE | chbits; /*default box rule*/ + } else + getch(); + c |= ZBIT; + neg = 0; + if (i < 0) { + i = -i; + neg = NMOT; + } + if (NROFF) + v = 2 * t.Halfline; + else { + v = EM; + if (v < VERT) /* ATT EVK hack: Erik van Konijnenburg, */ + v = VERT; /* hvlpb!evkonij, ATT NSI Hilversum, Holland */ + } + + cnt = i / v; + rem = makem(i % v) | neg; + ver = makem(v) | neg; + vlp = vlbuf; + if (!neg) + *vlp++ = ver; + if (absmot(rem) != 0) { + *vlp++ = c; + *vlp++ = rem; + } + while (vlp < vlbuf + NC - 3 && cnt--) { + *vlp++ = c; + *vlp++ = ver; + } + *(vlp - 2) &= ~ZBIT; + if (!neg) + vlp--; + *vlp = 0; + pushback(vlbuf); + vflag = 0; +} + +#define NPAIR (NC/2-6) /* max pairs in spline, etc. */ + +void setdraw(void) /* generate internal cookies for a drawing function */ +{ + int i, j, k, dx[NPAIR], dy[NPAIR], delim, type; + Tchar c, drawbuf[NC]; + int drawch = '.'; /* character to draw with */ + + /* input is \D'f dx dy dx dy ... c' (or at least it had better be) */ + /* this does drawing function f with character c and the */ + /* specified dx,dy pairs interpreted as appropriate */ + /* pairs are deltas from last point, except for radii */ + + /* l dx dy: line from here by dx,dy */ + /* c x: circle of diameter x, left side here */ + /* e x y: ellipse of diameters x,y, left side here */ + /* a dx1 dy1 dx2 dy2: + ccw arc: ctr at dx1,dy1, then end at dx2,dy2 from there */ + /* ~ dx1 dy1 dx2 dy2...: + spline to dx1,dy1 to dx2,dy2 ... */ + /* b x c: + built-up character of type c, ht x */ + /* f dx dy ...: f is any other char: like spline */ + + if (ismot(c = getch())) + return; + delim = cbits(c); + numerr.escarg = type = cbits(getch()); + if (type == '~') /* head off the .tr ~ problem */ + type = 's'; + for (i = 0; i < NPAIR ; i++) { + skip(); + vflag = 0; + dfact = EM; + dx[i] = quant(atoi0(), HOR); + if (dx[i] > MAXMOT) + dx[i] = MAXMOT; + else if (dx[i] < -MAXMOT) + dx[i] = -MAXMOT; + skip(); + if (type == 'c') { + dy[i] = 0; + goto eat; + } + vflag = 1; + dfact = lss; + dy[i] = quant(atoi0(), VERT); + if (dy[i] > MAXMOT) + dy[i] = MAXMOT; + else if (dy[i] < -MAXMOT) + dy[i] = -MAXMOT; +eat: + if (cbits(c = getch()) != ' ') { /* must be the end */ + if (cbits(c) != delim) { + drawch = cbits(c); + getch(); + } + i++; + break; + } + } + dfact = 1; + vflag = 0; + if (TROFF) { + drawbuf[0] = DRAWFCN | chbits | ZBIT; + drawbuf[1] = type | chbits | ZBIT; + drawbuf[2] = drawch | chbits | ZBIT; + for (k = 0, j = 3; k < i; k++) { + drawbuf[j++] = MOT | ((dx[k] >= 0) ? dx[k] : (NMOT | -dx[k])); + drawbuf[j++] = MOT | VMOT | ((dy[k] >= 0) ? dy[k] : (NMOT | -dy[k])); + } + if (type == DRAWELLIPSE) { + drawbuf[5] = drawbuf[4] | NMOT; /* so the net vertical is zero */ + j = 6; + } else if (type == DRAWBUILD) { + drawbuf[4] = drawbuf[3] | NMOT; /* net horizontal motion is zero */ + drawbuf[2] &= ~ZBIT; /* width taken from drawing char */ + j = 5; + } + drawbuf[j++] = DRAWFCN | chbits | ZBIT; /* marks end for ptout */ + drawbuf[j] = 0; + pushback(drawbuf); + } +} + + +void casefc(void) +{ + int i; + Tchar j; + + gchtab[fc] &= ~FCBIT; + fc = IMP; + padc = ' '; + if (skip() || ismot(j = getch()) || (i = cbits(j)) == '\n') + return; + fc = i; + gchtab[fc] |= FCBIT; + if (skip() || ismot(ch) || (ch = cbits(ch)) == fc) + return; + padc = ch; +} + + +Tchar setfield(int x) +{ + Tchar ii, jj, *fp; + int i, j; + int length, ws, npad, temp, type; + Tchar **pp, *padptr[NPP]; + Tchar fbuf[FBUFSZ]; + int savfc, savtc, savlc; + Tchar rchar; + int savepos; + static Tchar wbuf[] = { WORDSP, 0}; + + rchar = 0; + if (x == tabch) + rchar = tabc | chbits; + else if (x == ldrch) + rchar = dotc | chbits; + temp = npad = ws = 0; + savfc = fc; + savtc = tabch; + savlc = ldrch; + tabch = ldrch = fc = IMP; + savepos = numtabp[HP].val; + gchtab[tabch] &= ~TABBIT; + gchtab[ldrch] &= ~LDRBIT; + gchtab[fc] &= ~FCBIT; + gchtab[IMP] |= TABBIT|LDRBIT|FCBIT; + for (j = 0; ; j++) { + if ((tabtab[j] & TABMASK) == 0) { + if (x == savfc) + ERROR "zero field width." WARN; + jj = 0; + goto rtn; + } + if ((length = ((tabtab[j] & TABMASK) - numtabp[HP].val)) > 0 ) + break; + } + type = tabtab[j] & ~TABMASK; + fp = fbuf; + pp = padptr; + if (x == savfc) { + while (1) { + j = cbits(ii = getch()); + jj = width(ii); + widthp = jj; + numtabp[HP].val += jj; + if (j == padc) { + npad++; + *pp++ = fp; + if (pp > padptr + NPP - 1) + break; + goto s1; + } else if (j == savfc) + break; + else if (j == '\n') { + temp = j; + if (nlflg && ip == 0) { + numtabp[CD].val--; + nlflg = 0; + } + break; + } + ws += jj; +s1: + *fp++ = ii; + if (fp > fbuf + FBUFSZ - 3) + break; + } + if (ws) + *fp++ = WORDSP; + if (!npad) { + npad++; + *pp++ = fp; + *fp++ = 0; + } + *fp++ = temp; + *fp = 0; + temp = i = (j = length - ws) / npad; + i = (i / HOR) * HOR; + if ((j -= i * npad) < 0) + j = -j; + ii = makem(i); + if (temp < 0) + ii |= NMOT; + for (; npad > 0; npad--) { + *(*--pp) = ii; + if (j) { + j -= HOR; + (*(*pp)) += HOR; + } + } + pushback(fbuf); + jj = 0; + } else if (type == 0) { + /*plain tab or leader*/ + if ((j = width(rchar)) > 0) { + int nchar = length / j; + while (nchar-->0 && pbp < &pbbuf[NC-3]) { + numtabp[HP].val += j; + widthp = j; + *pbp++ = rchar; + } + length %= j; + } + if (length) + jj = length | MOT; + else + jj = getch0(); + if (savepos > 0) + pushback(wbuf); + } else { + /*center tab*/ + /*right tab*/ + while ((j = cbits(ii = getch())) != savtc && j != '\n' && j != savlc) { + jj = width(ii); + ws += jj; + numtabp[HP].val += jj; + widthp = jj; + *fp++ = ii; + if (fp > fbuf + FBUFSZ - 3) + break; + } + *fp++ = ii; + *fp = 0; + if (type == RTAB) + length -= ws; + else + length -= ws / 2; /*CTAB*/ + pushback(fbuf); + if ((j = width(rchar)) != 0 && length > 0) { + int nchar = length / j; + while (nchar-- > 0 && pbp < &pbbuf[NC-3]) + *pbp++ = rchar; + length %= j; + } + if (savepos > 0) + pushback(wbuf); + length = (length / HOR) * HOR; + jj = makem(length); + if (nlflg) { + if (ip == 0) + numtabp[CD].val--; + nlflg = 0; + } + } +rtn: + gchtab[fc] &= ~FCBIT; + gchtab[tabch] &= ~TABBIT; + gchtab[ldrch] &= ~LDRBIT; + fc = savfc; + tabch = savtc; + ldrch = savlc; + gchtab[fc] |= FCBIT; + gchtab[tabch] = TABBIT; + gchtab[ldrch] |= LDRBIT; + numtabp[HP].val = savepos; + return(jj); +} diff --git a/troff/ni.c b/troff/ni.c @@ -0,0 +1,390 @@ +#include <stdio.h> +#include "tdef.h" +#include "fns.h" +#include "ext.h" + +char termtab[NS]; /* term type added in ptinit() */ +char fontdir[NS]; /* added in casefp; not used by nroff */ +char devname[20]; /* default output device */ + +Numtab numtab[NN] = { + { PAIR('%', 0) }, + { PAIR('n', 'l') }, + { PAIR('y', 'r') }, + { PAIR('h', 'p') }, + { PAIR('c', 't') }, + { PAIR('d', 'n') }, + { PAIR('m', 'o') }, + { PAIR('d', 'y') }, + { PAIR('d', 'w') }, + { PAIR('l', 'n') }, + { PAIR('d', 'l') }, + { PAIR('s', 't') }, + { PAIR('s', 'b') }, + { PAIR('c', '.') }, + { PAIR('$', '$') } +}; + + +int alphabet = 256; /* latin-1 */ +int pto = 10000; +int pfrom = 1; +int print = 1; +char nextf[NS] = TMACDIR; +char mfiles[NMF][NS]; +int nmfi = 0; +int oldbits = -1; +int init = 1; +int fc = IMP; /* field character */ +int eschar = '\\'; +int pl; +int po; +FILE *ptid; + +int dfact = 1; +int dfactd = 1; +int res = 1; +int smnt = 0; /* beginning of special fonts */ +int ascii = 0; /* ascii normally off for troff, on for nroff; -a turns on */ +int lg; +int pnlist[NPN] = { -1 }; + + +int *pnp = pnlist; +int n