summaryrefslogtreecommitdiff
path: root/troff/troff.d/t6.c
diff options
context:
space:
mode:
Diffstat (limited to 'troff/troff.d/t6.c')
-rw-r--r--troff/troff.d/t6.c3032
1 files changed, 3032 insertions, 0 deletions
diff --git a/troff/troff.d/t6.c b/troff/troff.d/t6.c
new file mode 100644
index 0000000000000..bf7909cfd7b37
--- /dev/null
+++ b/troff/troff.d/t6.c
@@ -0,0 +1,3032 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/* from OpenSolaris "t6.c 1.9 05/06/08 SMI" */
+
+/*
+ * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
+ *
+ * Sccsid @(#)t6.c 1.194 (gritter) 2/7/10
+ */
+
+/*
+ * Changes Copyright (c) 2014 Carsten Kunze <carsten.kunze at arcor.de>
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/*
+ * t6.c
+ *
+ * width functions, sizes and fonts
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "tdef.h"
+#include "dev.h"
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "ext.h"
+#include "afm.h"
+#include "pt.h"
+#include "troff.h"
+#include "fontmap.h"
+
+/* fitab[f][c] is 0 if c is not on font f */
+ /* if it's non-zero, c is in fontab[f] at position
+ * fitab[f][c].
+ */
+int *fontlab;
+int *pstab;
+int *cstab;
+int *ccstab;
+int **fallbacktab;
+float *zoomtab;
+int *bdtab;
+int **lhangtab;
+int **rhangtab;
+int **kernafter;
+int **kernbefore;
+int **ftrtab;
+struct lgtab **lgtab;
+int ***lgrevtab;
+struct tracktab *tracktab;
+int sbold = 0;
+int kern = 0;
+struct box mediasize, bleedat, trimat, cropat;
+int psmaxcode;
+struct ref *anchors, *links, *ulinks;
+static int _minflg;
+int lastrst;
+int lastrsb;
+int spacewidth;
+
+static void kernsingle(int **);
+static int _ps2cc(const char *name, int create);
+
+int
+width(register tchar j)
+{
+ register int i, k;
+
+ _minflg = minflg;
+ minflg = minspc = 0;
+ lasttrack = 0;
+ rawwidth = 0;
+ lastrst = lastrsb = 0;
+ if (isadjspc(j))
+ return(0);
+ if (j & (ZBIT|MOT)) {
+ if (iszbit(j))
+ return(0);
+ if (isvmot(j))
+ return(0);
+ k = absmot(j);
+ if (isnmot(j))
+ k = -k;
+ return(k);
+ }
+ i = cbits(j);
+ if (html && i >= NCHARS)
+ i = ' ';
+ if (i < ' ') {
+ if (i == '\b')
+ return(-widthp);
+ if (i == PRESC)
+ i = eschar;
+ else if (iscontrol(i))
+ return(0);
+ else if (isxfunc(j, CHAR)) {
+ k = charout[sbits(j)].width + lettrack;
+ lastrst = charout[sbits(j)].height;
+ lastrsb = -charout[sbits(j)].depth;
+ goto set;
+ }
+ } else if (i == ' ' && issentsp(j))
+ {
+ _minflg = 0;
+ return(ses);
+ }
+ if (i==ohc)
+ return(0);
+ if (!xflag || !isdi(j)) {
+ if (i == STRETCH)
+ setcbits(j, ' ');
+ i = trtab[i];
+ i = ftrans(fbits(j), i);
+ }
+ if (i < 32)
+ return(0);
+ lasttrack = 0;
+ if (sfbits(j) == oldbits) {
+ xfont = pfont;
+ xpts = ppts;
+ } else
+ xbits(j, 0);
+ if (widcache[i-32].fontpts == xfont + (xpts<<8) &&
+ (i > 32 || widcache[i-32].evid == evname) &&
+ !setwdf && !_minflg && !horscale) {
+ rawwidth = widcache[i-32].width - widcache[i-32].track;
+ k = widcache[i-32].width + lettrack;
+ lastrst = widcache[i-32].rst;
+ lastrsb = widcache[i-32].rsb;
+ lasttrack = widcache[i-32].track;
+ } else {
+ if (_minflg && i == 32 && cbits(j) != 32)
+ _minflg = 0;
+ k = getcw(i-32);
+ if (i == 32 && _minflg && !cs) {
+ _minflg = 0;
+ minspc = getcw(0) - k;
+ }
+ _minflg = 0;
+ set: if (bd && !fmtchar)
+ k += (bd - 1) * HOR;
+ if (cs && !fmtchar)
+ k = cs;
+ }
+ widthp = k;
+ return(k);
+}
+
+/*
+ * clear width cache-- s means just space
+ */
+void
+zapwcache(int s)
+{
+ register int i;
+
+ if (s) {
+ widcache[0].fontpts = 0;
+ return;
+ }
+ for (i=0; i<NWIDCACHE; i++)
+ widcache[i].fontpts = 0;
+}
+
+int
+getcw(register int i)
+{
+ register int k;
+ register int *p;
+ register int x, j;
+ int nocache = 0;
+ int ofont = xfont;
+ int s, t;
+ double z = 1, zv;
+ struct afmtab *a;
+ int cd = 0;
+
+ bd = 0;
+ if (i >= nchtab + 128-32) {
+ if (afmtab && fontbase[xfont]->afmpos - 1 >= 0) {
+ cd = nchtab + 128;
+ i -= cd;
+ } else {
+ j = abscw(i + 32 - (nchtab+128));
+ goto g0;
+ }
+ }
+ if (i == 0) { /* a blank */
+ if (_minflg) {
+ j = minspsz;
+ nocache = 1;
+ } else
+ j = spacesz;
+ if (fontbase[xfont]->cspacewidth >= 0)
+ k = fontbase[xfont]->cspacewidth;
+ else if (spacewidth || gflag)
+ k = fontbase[xfont]->spacewidth;
+ else
+ k = fontab[xfont][0];
+ k = (k * j + 6) / 12;
+ lastrst = lastrsb = 0;
+ /* this nonsense because .ss cmd uses 1/36 em as its units */
+ /* and default is 12 */
+ goto g1;
+ }
+ if ((j = fitab[xfont][i]) == 0) { /* it's not on current font */
+ int ii, jj, t;
+ /* search through search list of xfont
+ * to see what font it ought to be on.
+ * first searches explicit fallbacks, then
+ * searches S, then remaining fonts in wraparound order.
+ */
+ nocache = 1;
+ if (fallbacktab[xfont]) {
+ for (jj = 0; fallbacktab[xfont][jj] != 0; jj++) {
+ if ((ii = findft(fallbacktab[xfont][jj],0)) < 0)
+ continue;
+ t = ftrans(ii, i + 32) - 32;
+ j = fitab[ii][t];
+ if (j != 0) {
+ xfont = ii;
+ goto found;
+ }
+ }
+ }
+ if (smnt) {
+ for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts + 1) {
+ if (fontbase[ii] == NULL)
+ continue;
+ t = ftrans(ii, i + 32) - 32;
+ j = fitab[ii][t];
+ if (j != 0) {
+ /*
+ * troff traditionally relies on the
+ * device postprocessor to find the
+ * appropriate character since it
+ * searches the fonts in the same
+ * order. This does not work with the
+ * new requests anymore, so change
+ * the font explicitly.
+ */
+ if (xflag)
+ xfont = ii;
+ found: p = fontab[ii];
+ k = *(p + j);
+ if (afmtab &&
+ (t=fontbase[ii]->afmpos-1)>=0) {
+ a = afmtab[t];
+ if (a->bbtab[j]) {
+ lastrst = a->bbtab[j][3];
+ lastrsb = a->bbtab[j][1];
+ } else {
+ lastrst = a->ascender;
+ lastrsb = a->descender;
+ }
+ }
+ if (xfont == sbold)
+ bd = bdtab[ii];
+ if (setwdf)
+ numtab[CT].val |= kerntab[ii][j];
+ goto g1;
+ }
+ }
+ }
+ k = fontab[xfont][0]; /* leave a space-size space */
+ lastrst = lastrsb = 0;
+ goto g1;
+ }
+ g0:
+ p = fontab[xfont];
+ if (setwdf)
+ numtab[CT].val |= kerntab[xfont][j];
+ if (afmtab && (t = fontbase[xfont]->afmpos-1) >= 0) {
+ a = afmtab[t];
+ if (a->bbtab[j]) {
+ lastrst = a->bbtab[j][3];
+ lastrsb = a->bbtab[j][1];
+ } else {
+ /*
+ * Avoid zero values by all means. In many use
+ * cases, endless loops will result unless values
+ * are non-zero.
+ */
+ lastrst = a->ascender;
+ lastrsb = a->descender;
+ }
+ }
+ k = *(p + j);
+ if (dev.anysize == 0 || xflag == 0 || (z = zoomtab[xfont]) == 0)
+ z = 1;
+ g1:
+ zv = z;
+ if (horscale) {
+ z *= horscale;
+ nocache = 1;
+ }
+ if (!bd)
+ bd = bdtab[ofont];
+ if ((cs = cstab[ofont])) {
+ nocache = 1;
+ if ((ccs = ccstab[ofont]))
+ x = pts2u(ccs);
+ else
+ x = xpts;
+ cs = (cs * EMPTS(x)) / 36;
+ }
+ k = (k * z * u2pts(xpts) + (Unitwidth / 2)) / Unitwidth;
+ lastrst = (lastrst * zv * u2pts(xpts) + (Unitwidth / 2)) / Unitwidth;
+ lastrsb = (lastrsb * zv * u2pts(xpts) + (Unitwidth / 2)) / Unitwidth;
+ rawwidth = k;
+ s = xpts;
+ lasttrack = 0;
+ if (s <= tracktab[ofont].s1 && tracktab[ofont].n1)
+ lasttrack = tracktab[ofont].n1;
+ else if (s >= tracktab[ofont].s2 && tracktab[ofont].n2)
+ lasttrack = tracktab[ofont].n2;
+ else if (s > tracktab[ofont].s1 && s < tracktab[ofont].s2) {
+ int r;
+ r = (s * tracktab[ofont].n2 - s * tracktab[ofont].n1
+ + tracktab[ofont].s2 * tracktab[ofont].n1
+ - tracktab[ofont].s1 * tracktab[ofont].n2)
+ / (tracktab[ofont].s2 - tracktab[ofont].s1);
+ if (r != 0)
+ lasttrack = r;
+ }
+ k += lasttrack + lettrack;
+ i += cd;
+ if (nocache|bd)
+ widcache[i].fontpts = 0;
+ else {
+ widcache[i].fontpts = xfont + (xpts<<8);
+ widcache[i].width = k - lettrack;
+ widcache[i].rst = lastrst;
+ widcache[i].rsb = lastrsb;
+ widcache[i].track = lasttrack;
+ widcache[i].evid = evname;
+ }
+ return(k);
+ /* Unitwidth is Units/Point, where
+ * Units is the fundamental digitization
+ * of the character set widths, and
+ * Point is the number of goobies in a point
+ * e.g., for cat, Units=36, Point=6, so Unitwidth=36/6=6
+ * In effect, it's the size at which the widths
+ * translate directly into units.
+ */
+}
+
+int
+abscw(int n) /* return index of abs char n in fontab[], etc. */
+{ register int i, ncf;
+
+ ncf = fontbase[xfont]->nwfont & BYTEMASK;
+ for (i = 0; i < ncf; i++)
+ if (codetab[xfont][i] == n)
+ return i;
+ return 0;
+}
+
+int
+onfont(tchar c)
+{
+ int k = cbits(c);
+ int f = fbits(c);
+
+ if (k <= ' ')
+ return 1;
+ k -= 32;
+ if (k >= nchtab + 128-32) {
+ if (afmtab && fontbase[f]->afmpos - 1 >= 0)
+ k -= nchtab + 128;
+ else
+ return abscw(k + 32 - (nchtab+128)) != 0;
+ }
+ return fitab[f][k] != 0;
+}
+
+static int
+fvert2pts(int f, int s, int k)
+{
+ double z;
+
+ if (k != 0) {
+ k = (k * u2pts(s) + (Unitwidth / 2)) / Unitwidth;
+ if (dev.anysize && xflag && (z = zoomtab[f]) != 0)
+ k *= z;
+ }
+ return k;
+}
+
+int
+getascender(void)
+{
+ struct afmtab *a;
+ int n;
+
+ if ((n = fontbase[font]->afmpos - 1) >= 0) {
+ a = afmtab[n];
+ return fvert2pts(font, pts, a->ascender);
+ } else
+ return 0;
+}
+
+int
+getdescender(void)
+{
+ struct afmtab *a;
+ int n;
+
+ if ((n = fontbase[font]->afmpos - 1) >= 0) {
+ a = afmtab[n];
+ return fvert2pts(font, pts, a->descender);
+ } else
+ return 0;
+}
+
+int
+kernadjust(tchar c, tchar d)
+{
+ lastkern = 0;
+ if (!kern || ismot(c) || ismot(d) || html)
+ return 0;
+ if (!isdi(c)) {
+ c = trtab[cbits(c)] | (c & SFMASK);
+ c = ftrans(fbits(c), cbits(c)) | (c & SFMASK);
+ }
+ if (!isdi(d)) {
+ d = trtab[cbits(d)] | (d & SFMASK);
+ d = ftrans(fbits(d), cbits(d)) | (d & SFMASK);
+ }
+ return getkw(c, d);
+}
+
+#define kprime 1021
+#define khash(c, d) (((2654435769U * (c) * (d) >> 16)&0x7fffffff) % kprime)
+
+static struct knode {
+ struct knode *next;
+ tchar c;
+ tchar d;
+ int n;
+} **ktable;
+
+static void
+kadd(tchar c, tchar d, int n)
+{
+ struct knode *kn;
+ int h;
+
+ if (ktable == NULL)
+ ktable = calloc(kprime, sizeof *ktable);
+ h = khash(c, d);
+ kn = calloc(1, sizeof *kn);
+ kn->c = c;
+ kn->d = d;
+ kn->n = n;
+ kn->next = ktable[h];
+ ktable[h] = kn;
+}
+
+static void
+kzap(int f)
+{
+ struct knode *kp;
+ int i;
+
+ if (ktable == NULL)
+ return;
+ for (i = 0; i < kprime; i++)
+ for (kp = ktable[i]; kp; kp = kp->next)
+ if (fbits(kp->c) == f || fbits(kp->d) == f)
+ kp->n = INT_MAX;
+}
+
+static tchar
+findchar(tchar c)
+{
+ int f, i;
+
+ f = fbits(c);
+ c = cbits(c);
+ i = c - 32;
+ if (c != ' ' && i > 0 && i < nchtab + 128 - 32 && fitab[f][i] == 0) {
+ int ii, jj;
+ if (fallbacktab[f]) {
+ for (jj = 0; fallbacktab[f][jj] != 0; jj++) {
+ if ((ii = findft(fallbacktab[f][jj], 0)) < 0)
+ continue;
+ if (fitab[ii][i] != 0) {
+ f = ii;
+ goto found;
+ }
+ }
+ }
+ if (smnt) {
+ for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts + 1) {
+ if (fontbase[ii] == NULL)
+ continue;
+ if (fitab[ii][i] != 0) {
+ f = ii;
+ goto found;
+ }
+ }
+ }
+ return 0;
+ }
+found:
+ setfbits(c, f);
+ return c;
+}
+
+static struct knode *
+klook(tchar c, tchar d)
+{
+ struct knode *kp;
+ int h;
+
+ c = findchar(c);
+ d = findchar(d);
+ h = khash(c, d);
+ for (kp = ktable[h]; kp; kp = kp->next)
+ if (kp->c == c && kp->d == d)
+ break;
+ return kp && kp->n != INT_MAX ? kp : NULL;
+}
+
+int
+getkw(tchar c, tchar d)
+{
+ struct knode *kp;
+ struct afmtab *a;
+ int f, g, i, j, k, n, s, I, J;
+ double z;
+
+ if (isxfunc(c, CHAR))
+ c = charout[sbits(c)].ch;
+ if (isxfunc(d, CHAR))
+ d = charout[sbits(d)].ch;
+ lastkern = 0;
+ if (!kern || iszbit(c) || iszbit(d) || ismot(c) || ismot(d))
+ return 0;
+ if (sbits(c) != sbits(d))
+ return 0;
+ f = fbits(c);
+ g = fbits(d);
+ if ((s = sbits(c)) == 0) {
+ s = xpts;
+ if (f == 0)
+ f = xfont;
+ }
+ i = cbits(c);
+ j = cbits(d);
+ if (i == SLANT || j == SLANT || i == XFUNC || j == XFUNC || cstab[f])
+ return 0;
+ k = 0;
+ if (i >= 32 && j >= 32) {
+ if (ktable != NULL && (kp = klook(c, d)) != NULL)
+ k = kp->n;
+ else if ((n = (fontbase[f]->afmpos)-1) >= 0 &&
+ n == (fontbase[g]->afmpos)-1 &&
+ fontbase[f]->kernfont >= 0) {
+ a = afmtab[n];
+ I = i - 32;
+ J = j - 32;
+ if (I >= nchtab + 128)
+ I -= nchtab + 128;
+ if (J >= nchtab + 128)
+ J -= nchtab + 128;
+ k = afmgetkern(a, I, J);
+ if (abs(k) < fontbase[f]->kernfont)
+ k = 0;
+ }
+ if (j>32 && kernafter != NULL && kernafter[fbits(c)] != NULL)
+ k += kernafter[fbits(c)][i];
+ if (i>32 && kernbefore != NULL && kernbefore[fbits(d)] != NULL)
+ k += kernbefore[fbits(d)][j];
+ }
+ if (k != 0) {
+ k = (k * u2pts(s) + (Unitwidth / 2)) / Unitwidth;
+ if (dev.anysize && xflag && (z = zoomtab[f]) != 0)
+ k *= z;
+ }
+ lastkern = k;
+ return k;
+}
+
+void
+xbits(register tchar i, int bitf)
+{
+ register int k;
+
+ xfont = fbits(i);
+ k = sbits(i);
+ if (k) {
+ xpts = dev.anysize && xflag ? k : pstab[--k];
+ oldbits = sfbits(i);
+ pfont = xfont;
+ ppts = xpts;
+ return;
+ }
+ switch (bitf) {
+ case 0:
+ xfont = font;
+ xpts = pts;
+ break;
+ case 1:
+ xfont = pfont;
+ xpts = ppts;
+ break;
+ case 2:
+ xfont = mfont;
+ xpts = mpts;
+ }
+}
+
+static tchar
+postchar1(const char *temp, int f)
+{
+ struct namecache *np;
+ struct afmtab *a;
+ int i;
+
+ if (afmtab && (i = (fontbase[f]->afmpos) - 1) >= 0) {
+ a = afmtab[i];
+ np = afmnamelook(a, temp);
+ if (np->afpos != 0) {
+ if (np->fival[0] != NOCODE &&
+ fitab[f][np->fival[0]])
+ return np->fival[0] + 32 + nchtab + 128;
+ else if (np->fival[1] != NOCODE &&
+ fitab[f][np->fival[1]])
+ return np->fival[1] + 32 + nchtab + 128;
+ else
+ return 0;
+ }
+ }
+ return(0);
+}
+
+static tchar
+postchar(const char *temp, int *fp)
+{
+ int i, j;
+ tchar c;
+
+ *fp = font;
+ if ((c = postchar1(temp, *fp)) != 0)
+ return c;
+ if (fallbacktab[font]) {
+ for (j = 0; fallbacktab[font][j] != 0; j++) {
+ if ((i = findft(fallbacktab[font][j], 0)) < 0)
+ continue;
+ if ((c = postchar1(temp, i)) != 0 && fchartab[c] == 0) {
+ *fp = i;
+ return c;
+ }
+ }
+ }
+ if (smnt) {
+ for (i=smnt, j=0; j < nfonts; j++, i=i % nfonts + 1) {
+ if (fontbase[i] == NULL)
+ continue;
+ if ((c = postchar1(temp, i)) != 0 && fchartab[c] == 0) {
+ *fp = i;
+ return c;
+ }
+ }
+ }
+ return 0;
+}
+
+const struct amap {
+ char *alias;
+ char *trname;
+} amap[] = {
+ { "lq", "``" },
+ { "rq", "''" },
+ { NULL, NULL }
+};
+
+tchar
+setch(int delim) {
+ register int j;
+ char temp[NC];
+ tchar c, d[2] = {0, 0};
+ int f, n;
+ const struct amap *ap;
+
+ n = 0;
+ if (delim == 'C')
+ d[0] = getach();
+ do {
+ c = getach();
+ if (c == 0 && n < 2)
+ return(0);
+ if (n >= sizeof temp) {
+ temp[n-1] = 0;
+ break;
+ }
+ if ((delim == '[' && c == ']') || (delim == 'C' && c == d[0])) {
+ temp[n] = 0;
+ break;
+ }
+ temp[n++] = c;
+ if (delim == '(' && n == 2) {
+ temp[n] = 0;
+ break;
+ }
+ } while (c);
+ for (ap = amap; ap->alias; ap++)
+ if (!strcmp(ap->alias, temp)) {
+ size_t l;
+ char *s = ap->trname;
+ if ((l = strlen(s) + 1) > NC) {
+ fprintf(stderr, "%s %i: strlen(%s)+1 > %d\n",
+ __FILE__, __LINE__, s, NC);
+ break;
+ }
+ memcpy(temp, s, l);
+ break;
+ }
+ if (delim == '[' && c != ']') {
+ nodelim(']');
+ return ' ';
+ }
+ if (delim == 'C' && c != d[0]) {
+ nodelim(d[0]);
+ return ' ';
+ }
+ c = 0;
+ if (delim == '[' || delim == 'C') {
+ size_t l = strlen(temp);
+ if (gemu) {
+ if (l == 5 && *temp == 'u'
+ && isxdigit((unsigned)temp[1])
+ && isxdigit((unsigned)temp[2])
+ && isxdigit((unsigned)temp[3])
+ && isxdigit((unsigned)temp[4])) {
+ int n;
+ n = strtol(temp + 1, NULL, 16);
+ if (n)
+ c = setuc0(n);
+ } else if ((l == 6 || (l == 7
+ && isdigit((unsigned)temp[6])))
+ && isdigit((unsigned)temp[5])
+ && isdigit((unsigned)temp[4])
+ && !strncmp(temp, "char", 4)) {
+ int i = atoi(temp + 4);
+ if (i <= 127)
+ c = i + nchtab + 128;
+ }
+ }
+ if (!c && (c = postchar(temp, &f))) {
+ c |= chbits & ~FMASK;
+ setfbits(c, f);
+ }
+ }
+ if (c == 0)
+ for (j = 0; j < nchtab; j++)
+ if (strcmp(&chname[chtab[j]], temp) == 0) {
+ c = (j + 128) | chbits;
+ break;
+ }
+ if (c == 0 && delim == '(')
+ if ((c = postchar(temp, &f)) != 0) {
+ c |= chbits & ~FMASK;
+ setfbits(c, f);
+ }
+ if (c == 0 && (c = _ps2cc(temp, 0)) != 0) {
+ c += nchtab + 128 + 32 + 128 - 32 + nchtab;
+ if (chartab[c] == NULL)
+ c = 0;
+ }
+ if (c == 0 && warn & WARN_CHAR)
+ errprint("missing glyph \\%c%s%s%s%s", delim, d, temp, d,
+ delim == '[' ? "]" : "");
+ if (c == 0 && !tryglf)
+ c = ' ';
+ return c;
+}
+
+tchar setabs(void) /* set absolute char from \C'...' */
+{
+ int n;
+
+ getch();
+ n = 0;
+ n = inumb(&n);
+ getch();
+ if (nonumb || n + nchtab + 128 >= NCHARS)
+ return 0;
+ return n + nchtab + 128;
+}
+
+
+
+int
+findft(register int i, int required)
+{
+ register int k;
+ int nk;
+ char *mn, *mp;
+
+ if ((k = i - '0') >= 0 && k <= nfonts && k < smnt && fontbase[k])
+ return(k);
+ for (k = 0; k > nfonts || fontlab[k] != i; k++)
+ if (k > nfonts) {
+ mn = macname(i);
+ nk = k;
+ if ((k = strtol(mn, &mp, 10)) >= 0 && *mp == 0 &&
+ mp > mn && k <= nfonts && fontbase[k])
+ break;
+ if (setfp(nk, i, NULL) == -1)
+ return -1;
+ else {
+ fontlab[nk] = i;
+ return nk;
+ }
+ if (required && warn & WARN_FONT)
+ errprint("%s: no such font", mn);
+ return(-1);
+ }
+ return(k);
+}
+
+void
+caseps(void)
+{
+ register int i;
+
+ if (skip(0))
+ i = apts1;
+ else {
+ if (xflag == 0) {
+ noscale++;
+ apts = u2pts(apts);
+ } else {
+ dfact = INCH;
+ dfactd = 72;
+ res = VERT;
+ }
+ i = inumb(&apts);
+ if (xflag == 0) {
+ noscale--;
+ i = pts2u(i);
+ apts = pts2u(apts);
+ }
+ if (nonumb)
+ return;
+ }
+ casps1(i);
+}
+
+void
+casps1(register int i)
+{
+
+/*
+ * in olden times, it used to ignore changes to 0 or negative.
+ * this is meant to allow the requested size to be anything,
+ * in particular so eqn can generate lots of \s-3's and still
+ * get back by matching \s+3's.
+
+ if (i <= 0)
+ return;
+*/
+ apts1 = apts;
+ apts = i;
+ pts1 = pts;
+ pts = findps(i);
+ mchbits();
+}
+
+int
+findps(register int i)
+{
+ register int j, k;
+
+ if (dev.anysize && xflag) {
+ if (i <= 0)
+ i = pstab[0];
+ return i;
+ }
+ for (j=k=0 ; pstab[j] != 0 ; j++)
+ if (abs(pstab[j]-i) < abs(pstab[k]-i))
+ k = j;
+
+ return(pstab[k]);
+}
+
+void
+mchbits(void)
+{
+ register int i, j, k;
+
+ i = pts;
+ if (dev.anysize && xflag)
+ j = i - 1;
+ else for (j = 0; i > (k = pstab[j]); j++)
+ if (!k) {
+ k = pstab[--j];
+ break;
+ }
+ chbits = 0;
+ setsbits(chbits, ++j);
+ setfbits(chbits, font);
+ zapwcache(1);
+ if (minspsz) {
+ k = spacesz;
+ spacesz = minspsz;
+ minsps = width(' ' | chbits);
+ spacesz = k;
+ zapwcache(1);
+ }
+ if (letspsz) {
+ k = spacesz;
+ spacesz = letspsz;
+ letsps = width(' ' | chbits);
+ spacesz = k;
+ zapwcache(1);
+ }
+ k = spacesz;
+ spacesz = sesspsz;
+ ses = width(' ' | chbits);
+ spacesz = k;
+ zapwcache(1);
+ sps = width(' ' | chbits);
+ zapwcache(1);
+}
+
+void
+setps(void)
+{
+ tchar c;
+ register int i, j = 0;
+ int k;
+
+ i = cbits(c = getch());
+ if (ismot(c) && xflag)
+ return;
+ if (ischar(i) && isdigit(i)) { /* \sd or \sdd */
+ i -= '0';
+ if (i == 0) /* \s0 */
+ j = apts1;
+ else if (i <= 3 && ischar(j = cbits(ch = getch())) &&
+ isdigit(j)) { /* \sdd */
+ j = 10 * i + j - '0';
+ ch = 0;
+ j = pts2u(j);
+ } else /* \sd */
+ j = pts2u(i);
+ } else if (i == '(') { /* \s(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ if (j == 0) /* \s(00 */
+ j = apts1;
+ else
+ j = pts2u(j);
+ } else if (i == '+' || i == '-') { /* \s+, \s- */
+ j = cbits(c = getch());
+ if (ischar(j) && isdigit(j)) { /* \s+d, \s-d */
+ j -= '0';
+ j = pts2u(j);
+ } else if (j == '(') { /* \s+(dd, \s-(dd */
+ j = cbits(getch()) - '0';
+ j = 10 * j + cbits(getch()) - '0';
+ j = pts2u(j);
+ } else if (xflag) { /* \s+[dd], */
+ k = j == '[' ? ']' : j; /* \s-'dd' */
+ setcbits(c, k);
+ dfact = INCH;
+ dfactd = 72;
+ res = HOR;
+ j = hatoi();
+ res = dfactd = dfact = 1;
+ if (nonumb)
+ return;
+ if (!issame(getch(), c))
+ nodelim(k);
+ }
+ if (i == '-')
+ j = -j;
+ j += apts;
+ } else if (xflag) { /* \s'+dd', \s[dd] */
+ if (i == '[') {
+ i = ']';
+ setcbits(c, i);
+ }
+ dfact = INCH;
+ dfactd = 72;
+ res = HOR;
+ j = inumb2(&apts, &k);
+ if (nonumb)
+ return;
+ if (j == 0 && k == 0)
+ j = apts1;
+ if (!issame(getch(), c))
+ nodelim(i);
+ }
+ casps1(j);
+}
+
+
+tchar setht(void) /* set character height from \H'...' */
+{
+ int n;
+ tchar c;
+
+ getch();
+ dfact = INCH;
+ dfactd = 72;
+ res = VERT;
+ n = inumb(&apts);
+ getch();
+ if (n == 0 || nonumb)
+ n = apts; /* does this work? */
+ c = CHARHT;
+ c |= ZBIT;
+ setfbits(c, font);
+ setsbits(c, n);
+ return(c);
+}
+
+tchar setslant(void) /* set slant from \S'...' */
+{
+ int n;
+ tchar c;
+
+ getch();
+ n = 0;
+ n = inumb(&n);
+ getch();
+ if (nonumb)
+ n = 0;
+ c = SLANT;
+ c |= ZBIT;
+ setfbits(c, font);
+ setsbits(c, n+180);
+ return(c);
+}
+
+void
+caseft(void)
+{
+ skip(0);
+ setfont(1);
+}
+
+void
+setfont(int a)
+{
+ register int i, j;
+
+ if (a)
+ i = getrq(3);
+ else
+ i = getsn(1);
+ if (!i || i == 'P') {
+ j = font1;
+ goto s0;
+ }
+ if (/* i == 'S' || */ i == '0')
+ return;
+ if ((j = findft(i, 0)) == -1)
+ if ((j = setfp(0, i, 0)) == -1) { /* try to put it in position 0 */
+ if (xflag) {
+ font1 = font;
+ }
+ return;
+ }
+s0:
+ font1 = font;
+ font = j;
+ mchbits();
+}
+
+void
+setwd(void)
+{
+ register int base, wid;
+ register tchar i;
+ tchar delim, lasti = 0;
+ int emsz, k;
+ int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;
+ int savlgf;
+ int rst = 0, rsb = 0;
+ int n;
+
+ base = numtab[SB].val = numtab[ST].val = wid = numtab[CT].val = 0;
+ if (ismot(i = getch()))
+ return;
+ delim = i;
+ argdelim = delim;
+ n = noschr;
+ noschr = 0;
+ savhp = numtab[HP].val;
+ numtab[HP].val = 0;
+ savapts = apts;
+ savapts1 = apts1;
+ savfont = font;
+ savfont1 = font1;
+ savpts = pts;
+ savpts1 = pts1;
+ savlgf = lgf;
+ lgf = 0;
+ setwdf++;
+ while (i = getch(), !issame(i, delim) && !nlflg) {
+ k = width(i);
+ k += kernadjust(lasti, i);
+ lasti = i;
+ wid += k;
+ numtab[HP].val += k;
+ if (!ismot(i)) {
+ emsz = POINT * u2pts(xpts);
+ } else if (isvmot(i)) {
+ k = absmot(i);
+ if (isnmot(i))
+ k = -k;
+ base -= k;
+ emsz = 0;
+ } else
+ continue;
+ if (base < numtab[SB].val)
+ numtab[SB].val = base;
+ if ((k = base + emsz) > numtab[ST].val)
+ numtab[ST].val = k;
+ if (lastrst > rst)
+ rst = lastrst;
+ if (lastrsb < rsb)
+ rsb = lastrsb;
+ }
+ if (!issame(i, delim))
+ nodelim(delim);
+ argdelim = 0;
+ noschr = n;
+ setn1(wid, 0, (tchar) 0);
+ prwatchn(&numtab[CT]);
+ prwatchn(&numtab[SB]);
+ prwatchn(&numtab[ST]);
+ setnr("rst", rst, 0);
+ setnr("rsb", rsb, 0);
+ numtab[HP].val = savhp;
+ apts = savapts;
+ apts1 = savapts1;
+ font = savfont;
+ font1 = savfont1;
+ pts = savpts;
+ pts1 = savpts1;
+ lgf = savlgf;
+ mchbits();
+ setwdf = 0;
+}
+
+
+tchar vmot(void)
+{
+ dfact = lss;
+ vflag++;
+ return(mot());
+}
+
+
+tchar hmot(void)
+{
+ dfact = EM;
+ return(mot());
+}
+
+
+tchar mot(void)
+{
+ register int j, n;
+ register tchar i;
+ tchar c, delim;
+
+ j = HOR;
+ delim = getch(); /*eat delim*/
+ if ((n = hatoi())) {
+ if (vflag)
+ j = VERT;
+ i = makem(quant(n, j));
+ } else
+ i = 0;
+ c = getch();
+ if (!issame(c, delim))
+ nodelim(delim);
+ vflag = 0;
+ dfact = 1;
+ return(i);
+}
+
+
+tchar sethl(int k)
+{
+ register int j;
+ tchar i;
+
+ j = EM / 2;
+ if (k == 'u')
+ j = -j;
+ else if (k == 'r')
+ j = -2 * j;
+ vflag++;
+ i = makem(j);
+ vflag = 0;
+ return(i);
+}
+
+
+tchar makem(register int i)
+{
+ register tchar j;
+
+ if ((j = i) < 0)
+ j = -j;
+ j = sabsmot(j) | MOT;
+ if (i < 0)
+ j |= NMOT;
+ if (vflag)
+ j |= VMOT;
+ return(j);
+}
+
+
+tchar getlg(tchar i)
+{
+ tchar j, k, pb[NC];
+ struct lgtab *lp;
+ int c, f, n, lgn;
+
+ f = fbits(i);
+ if (lgtab[f] == NULL) /* font lacks ligatures */
+ return(i);
+ c = cbits(i);
+ lp = &lgtab[f][c];
+ if (lp->from != c || (lp = lp->link) == NULL)
+ return(i);
+ k = i;
+ n = 1;
+ lgn = lg == 2 ? 2 : 1000;
+ for (;;) {
+ j = getch0();
+ if (n < sizeof pb)
+ pb[n-1] = j;
+ c = cbits(j);
+ while (lp != NULL && lp->from != c)
+ lp = lp->next;
+ if (lp == NULL || lp->to == 0) {
+ pbbuf[pbp++] = j;
+ return(k);
+ }
+ if (lp->to == -1) { /* fdeferlig request */
+ pb[n < sizeof pb ? n : sizeof pb - 1] = 0;
+ pushback(pb);
+ return(i);
+ }
+ k = (i & SFMASK) | lp->to | AUTOLIG;
+ if (lp->link == NULL || ++n > lgn)
+ return(k);
+ lp = lp->link;
+ }
+}
+
+int
+strlg(int f, int *tp, int n)
+{
+ struct lgtab *lp;
+ int i;
+
+ if (n == 1)
+ return tp[0];
+ if (lgtab[f] == NULL)
+ return 0;
+ lp = &lgtab[f][tp[0]];
+ if (lp->from != tp[0])
+ return 0;
+ for (i = 1; i < n; i++) {
+ if ((lp = lp->link) == NULL)
+ return 0;
+ while (lp != NULL && lp->from != tp[i])
+ lp = lp->next;
+ if (lp == NULL || lp->to == 0)
+ return 0;
+ }
+ return lp->to > 0 ? lp->to : 0;
+}
+
+void
+caselg(void)
+{
+
+ lg = 1;
+ if (skip(0))
+ return;
+ lg = hatoi();
+}
+
+static void
+addlig(int f, tchar *from, int to)
+{
+ int i, j;
+ struct lgtab *lp;
+
+ if (from[0] == 0 || from[1] == 0) {
+ if (warn & WARN_FONT)
+ errprint("short ligature has no effect");
+ return;
+ }
+ if (lgtab[f] == NULL)
+ lgtab[f] = calloc(NCHARS, sizeof **lgtab);
+ i = cbits(from[0]);
+ gchtab[i] |= LGBIT;
+ lp = &lgtab[f][i];
+ lp->from = i;
+ j = 1;
+ for (;;) {
+ i = cbits(from[j]);
+ if (lp->link == NULL) {
+ if (from[j+1] != 0) {
+ if (warn & WARN_FONT)
+ errprint("ligature step missing");
+ return;
+ }
+ lp->link = calloc(1, sizeof *lp->link);
+ lp = lp->link;
+ lp->from = i;
+ lp->to = to;
+ break;
+ }
+ lp = lp->link;
+ if (++j >= 4) {
+ if (warn & WARN_FONT)
+ errprint("ignoring ligature of length >4");
+ return;
+ }
+ while (lp->from != i && lp->next)
+ lp = lp->next;
+ if (lp->from != i) {
+ if (from[j] != 0) {
+ if (warn & WARN_FONT)
+ errprint("ligature step missing");
+ return;
+ }
+ lp->next = calloc(1, sizeof *lp->next);
+ lp = lp->next;
+ lp->from = i;
+ }
+ if (from[j] == 0) {
+ lp->to = to;
+ break;
+ }
+ }
+ if (to >= 0) {
+ if (lgrevtab[f] == NULL)
+ lgrevtab[f] = calloc(NCHARS, sizeof **lgrevtab);
+ lgrevtab[f][to] = malloc((j+2) * sizeof ***lgrevtab);
+ j = 0;
+ while ((lgrevtab[f][to][j] = cbits(from[j])))
+ j++;
+ }
+ /*
+ * If the font still contains the charlib substitutes for ff,
+ * Fi, and Fl, hide them. The ".flig" request is intended for
+ * use in combination with expert fonts only.
+ */
+ if ((to == LIG_FF || (cbits(from[0]) == 'f' && cbits(from[1]) == 'f' &&
+ cbits(from[2]) == 0)) &&
+ fitab[f][LIG_FF-32] != NOCODE)
+ if (codetab[f][fitab[f][LIG_FF-32]] < 32)
+ fitab[f][LIG_FF-32] = 0;
+ if ((to == LIG_FFI || (cbits(from[0]) == 'f' && cbits(from[1]) == 'f' &&
+ cbits(from[2]) == 'i' && cbits(from[3]) == 0)) &&
+ fitab[f][LIG_FFI-32] != NOCODE)
+ if (codetab[f][fitab[f][LIG_FFI-32]] < 32)
+ fitab[f][LIG_FFI-32] = 0;
+ if ((to == LIG_FFL || (cbits(from[0]) == 'f' && cbits(from[1]) == 'f' &&
+ cbits(from[2]) == 'l' && cbits(from[3]) == 0)) &&
+ fitab[f][LIG_FFL-32] != NOCODE)
+ if (codetab[f][fitab[f][LIG_FFL-32]] < 32)
+ fitab[f][LIG_FFL-32] = 0;
+}
+
+static void
+dellig(int f, tchar *from)
+{
+ struct lgtab *lp, *lq;
+ int i, j;
+
+ if (from[0] == 0 || from[1] == 0)
+ return;
+ if (lgtab[f] == NULL)
+ return;
+ i = cbits(from[0]);
+ lp = lq = &lgtab[f][i];
+ j = 1;
+ for (;;) {
+ i = cbits(from[j]);
+ if (lp->link == NULL)
+ break;
+ lq = lp;
+ lp = lp->link;
+ while (lp->from != i && lp->next) {
+ lq = lp;
+ lp = lp->next;
+ }
+ if (lp->from != i)
+ break;
+ if (from[++j] == 0) {
+ if (lq->link == lp)
+ lq->link = lp->next;
+ else if (lq->next == lp)
+ lq->next = lp->next;
+ if (lp->link)
+ if (warn & WARN_FONT)
+ errprint("deleted ligature cuts chain");
+ free(lgrevtab[f][lp->to]);
+ lgrevtab[f][lp->to] = NULL;
+ free(lp);
+ break;
+ }
+ }
+}
+
+void
+setlig(int f, int j)
+{
+ tchar from[4], to;
+
+ free(lgrevtab[f]);
+ lgrevtab[f] = NULL;
+ free(lgtab[f]);
+ lgtab[f] = NULL;
+ from[0] = 'f';
+ from[2] = from[3] = 0;
+ from[1] = 'f';
+ to = LIG_FF;
+ if (j & LFF)
+ addlig(f, from, to);
+ from[1] = 'i';
+ to = LIG_FI;
+ if (j & LFI)
+ addlig(f, from, to);
+ from[1] = 'l';
+ to = LIG_FL;
+ if (j & LFL)
+ addlig(f, from, to);
+ from[1] = 'f';
+ from[2] = 'i';
+ to = LIG_FFI;
+ if (j & LFFI)
+ addlig(f, from, to);
+ from[2] = 'l';
+ to = LIG_FFL;
+ if (j & LFFL)
+ addlig(f, from, to);
+}
+
+static int
+getflig(int f, int mode)
+{
+ int delete, allnum;
+ tchar from[NC], to;
+ int c, i, j;
+ char number[NC];
+
+ if (skip(0))
+ return 0;
+ switch (cbits(c = getch())) {
+ case '-':
+ c = getch();
+ delete = 1;
+ break;
+ case '+':
+ c = getch();
+ /*FALLTHRU*/
+ default:
+ delete = 0;
+ break;
+ case 0:
+ return 0;
+ }
+ allnum = 1;
+ for (i = 0; i < sizeof from - 2; i++) {
+ from[i] = c;
+ j = cbits(c);
+ if (c == 0 || ismot(c) || j == ' ' || j == '\n') {
+ from[i] = 0;
+ ch = j;
+ break;
+ }
+ if (j < '0' || j > '9')
+ allnum = 0;
+ c = getch();
+ }
+ if (mode == 0 && allnum == 1) { /* backwards compatibility */
+ if (skip(0) == 0)
+ goto new;
+ for (j = 0; j <= i+1; j++)
+ number[j] = cbits(from[j]);
+ j = strtol(number, NULL, 10);
+ setlig(f, j);
+ return 0;
+ }
+ if (delete == 0) {
+ if (mode >= 0) {
+ if (skip(1))
+ return 0;
+ new: to = cbits(getch());
+ } else
+ to = -1;
+ addlig(f, from, to);
+ } else
+ dellig(f, from);
+ return 1;
+}
+
+void
+caseflig(int defer)
+{
+ int i, j;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((j = findft(i, 1)) < 0)
+ return;
+ i = 0;
+ while (getflig(j, defer ? -1 : i++) != 0);
+}
+
+void
+casefdeferlig(void)
+{
+ caseflig(1);
+}
+
+void
+casefp(int spec)
+{
+ register int i, j;
+ char *file, *supply;
+
+ lgf++;
+ skip(0);
+ if ((i = xflag ? hatoi() : cbits(getch()) - '0') < 0 || i > 255)
+ bad: errprint("fp: bad font position %d", i);
+ else if (skip(0) || !(j = getrq(3)))
+ errprint("fp: no font name");
+ else {
+ if (skip(0) || !getname()) {
+ if (i == 0)
+ goto bad;
+ setfp(i, j, 0);
+ } else { /* 3rd argument = filename */
+ size_t l;
+ l = strlen(nextf) + 1;
+ file = malloc(l);
+ n_strcpy(file, nextf, l);
+ if (!skip(0) && getname()) {
+ l = strlen(nextf) + 1;
+ supply = malloc(l);
+ n_strcpy(supply, nextf, l);
+ } else
+ supply = NULL;
+ if (loadafm(i?i:-1, j, file, supply, 0, spec) == 0) {
+ if (i == 0) {
+ if (warn & WARN_FONT)
+ errprint("fp: cannot mount %s",
+ file);
+ } else
+ setfp(i, j, file);
+ }
+ free(file);
+ free(supply);
+ }
+ }
+}
+
+void
+casefps(void)
+{
+ const struct {
+ enum spec spec;
+ const char *name;
+ } tab[] = {
+ { SPEC_MATH, "math" },
+ { SPEC_GREEK, "greek" },
+ { SPEC_PUNCT, "punct" },
+ { SPEC_LARGE, "large" },
+ { SPEC_S1, "S1" },
+ { SPEC_S, "S" },
+ { SPEC_NONE, NULL }
+ };
+ char name[NC];
+ int c = 0, i;
+ enum spec s = SPEC_NONE;
+
+ if (skip(1))
+ return;
+ do {
+ for (i = 0; i < sizeof name - 2; i++) {
+ if ((c = getach()) == 0 || c == ':' || c == ',')
+ break;
+ name[i] = c;
+ }
+ name[i] = 0;
+ for (i = 0; tab[i].name; i++)
+ if (strcmp(tab[i].name, name) == 0) {
+ s |= tab[i].spec;
+ break;
+ }
+ if (tab[i].name == NULL)
+ errprint("fps: unknown special set %s", name);
+ } while (c);
+ casefp(s);
+}
+
+int
+setfp(int pos, int f, char *truename) /* mount font f at position pos[0...nfonts] */
+{
+ char longname[4096], *shortname, *ap;
+ char *fpout;
+ int i, nw;
+
+ zapwcache(0);
+ if (truename)
+ shortname = truename;
+ else
+ shortname = macname(f);
+ shortname = mapft(shortname);
+ snprintf(longname, sizeof longname, "%s/dev%s/%s",
+ fontfile, devname, shortname);
+ if ((fpout = readfont(longname, &dev, warn & WARN_FONT)) == NULL)
+ return(-1);
+ if (pos >= Nfont)
+ growfonts(pos+1);
+ if (pos > nfonts)
+ nfonts = pos;
+ fontbase[pos] = (struct Font *)fpout;
+ if ((ap = strstr(fontbase[pos]->namefont, ".afm")) != NULL) {
+ *ap = 0;
+ if (ap == &fontbase[pos]->namefont[1])
+ f &= BYTEMASK;
+ loadafm(pos, f, fontbase[pos]->namefont, NULL, 1, SPEC_NONE);
+ free(fpout);
+ } else {
+ nw = fontbase[pos]->nwfont & BYTEMASK;
+ makefont(pos, &((char *)fontbase[pos])[sizeof(struct Font)],
+ &((char *)fontbase[pos])[sizeof(struct Font) + nw],
+ &((char *)fontbase[pos])[sizeof(struct Font) + 2*nw],
+ &((char *)fontbase[pos])[sizeof(struct Font) + 3*nw],
+ nw);
+ setlig(pos, fontbase[pos]->ligfont);
+ }
+ if (pos == smnt) {
+ smnt = 0;
+ sbold = 0;
+ }
+ if ((fontlab[pos] = f) == 'S')
+ smnt = pos;
+ bdtab[pos] = cstab[pos] = ccstab[pos] = 0;
+ zoomtab[pos] = 0;
+ fallbacktab[pos] = NULL;
+ free(lhangtab[pos]);
+ lhangtab[pos] = NULL;
+ free(rhangtab[pos]);
+ rhangtab[pos] = NULL;
+ memset(&tracktab[pos], 0, sizeof tracktab[pos]);
+ for (i = 0; i < NCHARS; i++)
+ ftrtab[pos][i] = i;
+ kzap(pos);
+ /* if there is a directory, no place to store its name. */
+ /* if position isn't zero, no place to store its value. */
+ /* only time a FONTPOS is pushed back is if it's a */
+ /* standard font on position 0 (i.e., mounted implicitly. */
+ /* there's a bug here: if there are several input lines */
+ /* that look like .ft XX in short successtion, the output */
+ /* will all be in the last one because the "x font ..." */
+ /* comes out too soon. pushing back FONTPOS doesn't work */
+ /* with .ft commands because input is flushed after .xx cmds */
+ if (realpage && ap == NULL)
+ ptfpcmd(pos, shortname, NULL, 0);
+ if (pos == 0)
+ ch = (tchar) FONTPOS | (tchar) f << 22;
+ return(pos);
+}
+
+int
+nextfp(void)
+{
+ int i;
+
+ for (i = 1; i <= nfonts; i++)
+ if (fontbase[i] == NULL)
+ return i;
+ if (i <= 255)
+ return i;
+ return 0;
+}
+
+void
+casecs(void)
+{
+ register int i, j;
+
+ noscale++;
+ if (skip(1))
+ goto rtn;
+ if (!(i = getrq(2)))
+ goto rtn;
+ if ((i = findft(i, 1)) < 0)
+ goto rtn;
+ skip(0);
+ cstab[i] = hatoi();
+ skip(0);
+ j = hatoi();
+ if (nonumb)
+ ccstab[i] = 0;
+ else
+ ccstab[i] = findps(j);
+rtn:
+ zapwcache(0);
+ noscale = 0;
+}
+
+void
+casebd(void)
+{
+ register int i, j = 0, k;
+
+ zapwcache(0);
+ k = 0;
+bd0:
+ if (skip(1) || !(i = getrq(2)) || (j = findft(i, 1)) == -1) {
+ if (k)
+ goto bd1;
+ else
+ return;
+ }
+ if (j == smnt) {
+ k = smnt;
+ goto bd0;
+ }
+ if (k) {
+ sbold = j;
+ j = k;
+ }
+bd1:
+ skip(0);
+ noscale++;
+ bdtab[j] = hatoi();
+ noscale = 0;
+}
+
+void
+casevs(void)
+{
+ register int i;
+
+ skip(0);
+ vflag++;
+ dfact = INCH; /* default scaling is points! */
+ dfactd = 72;
+ res = VERT;
+ i = inumb(&lss);
+ if (nonumb)
+ i = lss1;
+ if (xflag && i < 0) {
+ if (warn & WARN_RANGE)
+ errprint("negative vertical spacing ignored");
+ i = lss1;
+ }
+ if (i < VERT)
+ i = VERT;
+ lss1 = lss;
+ lss = i;
+}
+
+void
+casess(int flg)
+{
+ register int i, j;
+
+ noscale++;
+ if (skip(flg == 0))
+ minsps = minspsz = 0;
+ else if ((i = hatoi()) != 0 && !nonumb) {
+ if (xflag && flg == 0 && !skip(0)) {
+ j = hatoi();
+ if (!nonumb) {
+ sesspsz = j & 0177;
+ spacesz = sesspsz;
+ zapwcache(1);
+ ses = width(' ' | chbits);
+ }
+ }
+ if (flg) {
+ j = spacesz;
+ minspsz = i & 0177;
+ spacesz = minspsz;
+ zapwcache(1);
+ minsps = width(' ' | chbits);
+ spacesz = j;
+ zapwcache(0);
+ sps = width(' ' | chbits);
+ } else {
+ spacesz = i & 0177;
+ zapwcache(0);
+ sps = width(' ' | chbits);
+ if (minspsz > spacesz)
+ minsps = minspsz = 0;
+ }
+ }
+ noscale = 0;
+}
+
+void
+caseminss(void)
+{
+ casess(1);
+}
+
+void
+caseletadj(void)
+{
+ int s, n, x, l, h;
+
+ dfact = LAFACT / 100;
+ if (skip(0) || (n = hatoi()) == 0) {
+ letspsz = 0;
+ letsps = 0;
+ lspmin = 0;
+ lspmax = 0;
+ lshmin = 0;
+ lshmax = 0;
+ goto ret;
+ }
+ if (skip(1))
+ goto ret;
+ dfact = LAFACT / 100;
+ l = hatoi();
+ if (skip(1))
+ goto ret;
+ noscale++;
+ s = hatoi();
+ noscale--;
+ if (skip(1))
+ goto ret;
+ dfact = LAFACT / 100;
+ x = hatoi();
+ if (skip(1))
+ goto ret;
+ dfact = LAFACT / 100;
+ h = hatoi();
+ letspsz = s;
+ lspmin = LAFACT - n;
+ lspmax = x - LAFACT;
+ lshmin = LAFACT - l;
+ lshmax = h - LAFACT;
+ s = spacesz;
+ spacesz = letspsz;
+ zapwcache(1);
+ letsps = width(' ' | chbits);
+ spacesz = s;
+ zapwcache(1);
+ sps = width(' ' | chbits);
+ zapwcache(1);
+ret:
+ dfact = 1;
+}
+
+void
+casefspacewidth(void)
+{
+ int f, n, i;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ if (skip(0)) {
+ fontbase[f]->cspacewidth = -1;
+ fontab[f][0] = fontbase[f]->spacewidth;
+ } else {
+ noscale++;
+ n = hatoi();
+ noscale--;
+ unitsPerEm = 1000;
+ if (n >= 0)
+ fontbase[f]->cspacewidth = fontab[f][0] = _unitconv(n);
+ else if (warn & WARN_RANGE)
+ errprint("ignoring negative space width %d", n);
+ }
+ zapwcache(1);
+}
+
+void
+casespacewidth(void)
+{
+ noscale++;
+ spacewidth = skip(0) || hatoi();
+ noscale--;
+}
+
+tchar 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(hatoi(), VERT);
+ dfact = 1;
+ getch();
+ if (i >= 0)
+ pbbuf[pbp++] = MOT | VMOT | sabsmot(i);
+ else
+ pbbuf[pbp++] = MOT | VMOT | NMOT | sabsmot(-i);
+ return(HX);
+}
+
+struct afmtab **afmtab;
+int nafm;
+
+char *
+onefont(char *prefix, char *file, char *type)
+{
+ char *path, *fp, *tp;
+ size_t l;
+
+ l = strlen(prefix) + strlen(file) + 2;
+ path = malloc(l);
+ n_strcpy(path, prefix, l);
+ n_strcat(path, "/", l);
+ n_strcat(path, file, l);
+ if (type) {
+ for (fp = file; *fp; fp++);
+ for (tp = type; *tp; tp++);
+ while (tp >= type && fp >= file && *fp-- == *tp--);
+ if (tp >= type) {
+ l = strlen(path) + strlen(type) + 2;
+ tp = malloc(l);
+ n_strcpy(tp, path, l);
+ n_strcat(tp, ".", l);
+ n_strcat(tp, type, l);
+ free(path);
+ path = tp;
+ }
+ }
+ return path;
+}
+
+static char *
+getfontpath(char *file, char *type)
+{
+ char *path, *troffonts, *tp, *tq, c;
+ size_t l;
+
+ if ((troffonts = getenv("TROFFONTS")) != NULL) {
+ l = strlen(troffonts) + 1;
+ tp = malloc(l);
+ n_strcpy(tp, troffonts, l);
+ troffonts = tp;
+ do {
+ for (tq = tp; *tq && *tq != ':'; tq++);
+ c = *tq;
+ *tq = 0;
+ path = onefont(tp, file, type);
+ if (access(path, 0) == 0) {
+ free(troffonts);
+ return path;
+ }
+ free(path);
+ tp = &tq[1];
+ } while (c);
+ free(troffonts);
+ }
+ l = strlen(fontfile) + strlen(devname) + 10;
+ tp = malloc(l);
+ snprintf(tp, l, "%s/dev%s", fontfile, devname);
+ path = onefont(tp, file, type);
+ free(tp);
+ return path;
+}
+
+static void
+checkenminus(int f)
+{
+ /*
+ * A fix for a very special case: If the font supplies punctuation
+ * characters but is not S1, only one of \- and \(en is present
+ * since the PostScript character "endash" is mapped to both of
+ * them.
+ */
+ enum spec spec;
+ int i;
+
+ if (afmtab == NULL || (i = fontbase[f]->afmpos - 1) < 0)
+ return;
+ if (c_endash == 0 || c_minus == 0)
+ specnames();
+ spec = afmtab[i]->spec;
+ if ((spec&(SPEC_PUNCT|SPEC_S1)) == SPEC_PUNCT) {
+ if (fitab[f][c_endash-32] == 0 && ftrtab[f][c_minus-32])
+ ftrtab[f][c_endash] = c_minus;
+ else if (fitab[f][c_endash-32] && ftrtab[f][c_minus-32] != 0)
+ ftrtab[f][c_minus] = c_endash;
+ }
+}
+
+int
+loadafm(int nf, int rq, char *file, char *supply, int required, enum spec spec)
+{
+ struct stat st;
+ int fd;
+ char *path, *contents;
+ struct afmtab *a;
+ int i, have = 0;
+ struct namecache *np;
+ size_t l;
+
+ zapwcache(0);
+ if (nf < 0)
+ nf = nextfp();
+ path = getfontpath(file, "afm");
+ if (access(path, 0) < 0) {
+ path = getfontpath(file, "otf");
+ if (access(path, 0) < 0)
+ path = getfontpath(file, "ttf");
+ }
+ if (dev.allpunct)
+ spec |= SPEC_PUNCT;
+ a = calloc(1, sizeof *a);
+ for (i = 0; i < nafm; i++)
+ if (strcmp(afmtab[i]->path, path) == 0 &&
+ afmtab[i]->spec == spec) {
+ *a = *afmtab[i];
+ have = 1;
+ break;
+ }
+ a->path = path;
+ l = strlen(file) + 1;
+ a->file = malloc(l);
+ n_strcpy(a->file, file, l);
+ a->spec = spec;
+ a->rq = rq;
+ a->Font.namefont[0] = rq&0377;
+ a->Font.namefont[1] = (rq>>8)&0377;
+ snprintf(a->Font.intname, sizeof(a->Font.intname), "%d", nf);
+ if (have)
+ goto done;
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ if (required)
+ errprint("Can't open %s", path);
+ free(a->file);
+ free(a);
+ free(path);
+ return 0;
+ }
+ if (fstat(fd, &st) < 0) {
+ errprint("Can't stat %s", path);
+ free(a->file);
+ free(a);
+ free(path);
+ return -1;
+ }
+ contents = malloc(st.st_size + 1);
+ if (read(fd, contents, st.st_size) != st.st_size) {
+ errprint("Can't read %s", path);
+ free(a->file);
+ free(a);
+ free(path);
+ free(contents);
+ return -1;
+ }
+ contents[st.st_size] = 0;
+ close(fd);
+ if (afmget(a, contents, st.st_size) < 0) {
+ free(path);
+ free(contents);
+ return -1;
+ }
+ free(contents);
+ morechars(a->nchars+32+1+128-32+nchtab+32+nchtab+128+psmaxcode+1);
+done: afmtab = realloc(afmtab, (nafm+1) * sizeof *afmtab);
+ afmtab[nafm] = a;
+ if (nf >= Nfont)
+ growfonts(nf+1);
+ a->Font.afmpos = nafm+1;
+ if ((np = afmnamelook(a, "space")) != NULL)
+ a->Font.spacewidth = a->fontab[np->afpos];
+ else
+ a->Font.spacewidth = a->fontab[0];
+ a->Font.cspacewidth = -1;
+ fontbase[nf] = &afmtab[nafm]->Font;
+ fontlab[nf] = rq;
+ free(fontab[nf]);
+ free(kerntab[nf]);
+ free(codetab[nf]);
+ free(fitab[nf]);
+ fontab[nf] = malloc(a->nchars * sizeof *fontab[nf]);
+ kerntab[nf] = malloc(a->nchars * sizeof *kerntab[nf]);
+ codetab[nf] = malloc(a->nchars * sizeof *codetab[nf]);
+ fitab[nf] = calloc(NCHARS, sizeof *fitab[nf]);
+ memcpy(fontab[nf], a->fontab, a->nchars * sizeof *fontab[nf]);
+ memcpy(kerntab[nf], a->kerntab, a->nchars * sizeof *kerntab[nf]);
+ memcpy(codetab[nf], a->codetab, a->nchars * sizeof *codetab[nf]);
+ memcpy(fitab[nf], a->fitab, a->fichars * sizeof *fitab[nf]);
+ bdtab[nf] = cstab[nf] = ccstab[nf] = 0;
+ zoomtab[nf] = 0;
+ fallbacktab[nf] = NULL;
+ free(lhangtab[nf]);
+ lhangtab[nf] = NULL;
+ free(rhangtab[nf]);
+ rhangtab[nf] = NULL;
+ memset(&tracktab[nf], 0, sizeof tracktab[nf]);
+ setlig(nf, a->Font.ligfont);
+ for (i = 0; i < NCHARS; i++)
+ ftrtab[nf][i] = i;
+ kzap(nf);
+ nafm++;
+ if (nf > nfonts)
+ nfonts = nf;
+ if (supply) {
+ char *data;
+ if (strcmp(supply, "pfb") == 0 || strcmp(supply, "pfa") == 0 ||
+ strcmp(supply, "t42") == 0 ||
+ strcmp(supply, "otf") == 0 ||
+ strcmp(supply, "ttf") == 0)
+ data = getfontpath(file, supply);
+ else
+ data = getfontpath(supply, NULL);
+ a->supply = afmencodepath(data);
+ free(data);
+ if (realpage)
+ ptsupplyfont(a->fontname, a->supply);
+ }
+ checkenminus(nf);
+ if (realpage)
+ ptfpcmd(nf, macname(fontlab[nf]), a->path, (int)a->spec);
+ return 1;
+}
+
+int
+tracknum(void)
+{
+ skip(1);
+ dfact = INCH;
+ dfactd = 72;
+ res = VERT;
+ return inumb(NULL);
+}
+
+void
+casetrack(void)
+{
+ int i, j, s1, n1, s2, n2;
+
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((j = findft(i, 1)) < 0)
+ return;
+ s1 = tracknum();
+ if (!nonumb) {
+ n1 = tracknum();
+ if (!nonumb) {
+ s2 = tracknum();
+ if (!nonumb) {
+ n2 = tracknum();
+ if (!nonumb) {
+ tracktab[j].s1 = s1;
+ tracktab[j].n1 = n1;
+ tracktab[j].s2 = s2;
+ tracktab[j].n2 = n2;
+ zapwcache(0);
+ }
+ }
+ }
+ }
+}
+
+void
+casefallback(void)
+{
+ int *fb = NULL;
+ int i, j, n = 0;
+
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((j = findft(i, 1)) < 0)
+ return;
+ do {
+ skip(0);
+ i = getrq(2);
+ fb = realloc(fb, (n+2) * sizeof *fb);
+ fb[n++] = i;
+ } while (i);
+ fallbacktab[j] = fb;
+}
+
+void
+casehidechar(void)
+{
+ int savfont = font, savfont1 = font1;
+ int i, j;
+ tchar k;
+
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((j = findft(i, 1)) < 0)
+ return;
+ font = font1 = j;
+ mchbits();
+ while ((i = cbits(k = getch())) != '\n') {
+ if (fbits(k) != j || ismot(k) || i == ' ')
+ continue;
+ if (i >= nchtab + 128-32 && afmtab &&
+ fontbase[j]->afmpos - 1 >= 0)
+ i -= nchtab + 128;
+ fitab[j][i - 32] = 0;
+ }
+ font = savfont;
+ font1 = savfont1;
+ mchbits();
+ zapwcache(0);
+}
+
+void
+casefzoom(void)
+{
+ int i, j;
+ float f;
+
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((j = findft(i, 1)) < 0)
+ return;
+ skip(1);
+ f = atof();
+ if (!nonumb && f >= 0) {
+ zoomtab[j] = f;
+ zapwcache(0);
+ if (realpage && j == xfont && !ascii)
+ ptps();
+ }
+}
+
+double
+getfzoom(void)
+{
+ return zoomtab[font];
+}
+
+void
+casekern(void)
+{
+ kern = skip(0) || hatoi() ? 1 : 0;
+}
+
+void
+casefkern(void)
+{
+ int f, i, j;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ if (skip(0))
+ fontbase[f]->kernfont = 0;
+ else {
+ j = hatoi();
+ if (!nonumb)
+ fontbase[f]->kernfont = j ? j : -1;
+ }
+}
+
+static void
+setpapersize(int setmedia)
+{
+ const struct {
+ char *name;
+ int width;
+ int heigth;
+ } papersizes[] = {
+ { "executive", 518, 756 },
+ { "letter", 612, 792 },
+ { "legal", 612, 992 },
+ { "ledger", 1224, 792 },
+ { "tabloid", 792, 1224 },
+ { "a0", 2384, 3370 },
+ { "a1", 1684, 2384 },
+ { "a2", 1191, 1684 },
+ { "a3", 842, 1191 },
+ { "a4", 595, 842 },
+ { "a5", 420, 595 },
+ { "a6", 298, 420 },
+ { "a7", 210, 298 },
+ { "a8", 147, 210 },
+ { "a9", 105, 147 },
+ { "a10", 74, 105 },
+ { "b0", 2835, 4008 },
+ { "b1", 2004, 2835 },
+ { "b2", 1417, 2004 },
+ { "b3", 1000, 1417 },
+ { "b4", 709, 1000 },
+ { "b5", 499, 709 },
+ { "b6", 354, 499 },
+ { "b7", 249, 354 },
+ { "b8", 176, 249 },
+ { "b9", 125, 176 },
+ { "b10", 87, 125 },
+ { "c0", 2599, 3677 },
+ { "c1", 1837, 2599 },
+ { "c2", 1298, 1837 },
+ { "c3", 918, 1298 },
+ { "c4", 649, 918 },
+ { "c5", 459, 649 },
+ { "c6", 323, 459 },
+ { "c7", 230, 323 },
+ { "c8", 162, 230 },
+ { NULL, 0, 0 }
+ };
+ int c;
+ int x = 0, y = 0, n;
+ char buf[NC];
+
+ lgf++;
+ if (skip(1))
+ return;
+ c = cbits(ch);
+ if (isdigit(c) || c == '(') {
+ x = hatoi();
+ if (!nonumb) {
+ skip(1);
+ y = hatoi();
+ }
+ if (nonumb || x == 0 || y == 0)
+ return;
+ } else {
+ n = 0;
+ do {
+ c = getach();
+ if (n+1 < sizeof buf)
+ buf[n++] = c;
+ } while (c);
+ buf[n] = 0;
+ for (n = 0; papersizes[n].name != NULL; n++)
+ if (strcmp(buf, papersizes[n].name) == 0) {
+ x = papersizes[n].width * INCH / 72;
+ y = papersizes[n].heigth * INCH / 72;
+ break;
+ }
+ if (x == 0 || y == 0) {
+ errprint("Unknown paper size %s", buf);
+ return;
+ }
+ }
+ pl = defaultpl = y;
+ if (numtab[NL].val > pl) {
+ numtab[NL].val = pl;
+ prwatchn(&numtab[NL]);
+ }
+ po = x > 6 * PO ? PO : x / 8;
+ ll = ll1 = lt = lt1 = x - 2 * po;
+ setnel();
+ mediasize.val[2] = x;
+ mediasize.val[3] = y;
+ mediasize.flag |= 1;
+ if (setmedia)
+ mediasize.flag |= 2;
+ if (realpage)
+ ptpapersize();
+}
+
+void
+casepapersize(void)
+{
+ setpapersize(0);
+}
+
+void
+casemediasize(void)
+{
+ setpapersize(1);
+}
+
+static void
+cutat(struct box *bp)
+{
+ int c[4], i;
+
+ for (i = 0; i < 4; i++) {
+ if (skip(1))
+ return;
+ dfact = INCH;
+ dfactd = 72;
+ c[i] = inumb(NULL);
+ if (nonumb)
+ return;
+ }
+ for (i = 0; i < 4; i++)
+ bp->val[i] = c[i];
+ bp->flag |= 1;
+ if (realpage)
+ ptcut();
+}
+
+void
+casetrimat(void)
+{
+ cutat(&trimat);
+}
+
+void
+casebleedat(void)
+{
+ cutat(&bleedat);
+}
+
+void
+casecropat(void)
+{
+ cutat(&cropat);
+}
+
+void
+caselhang(void)
+{
+ kernsingle(lhangtab);
+}
+
+void
+caserhang(void)
+{
+ kernsingle(rhangtab);
+}
+
+void
+casekernpair(void)
+{
+ int savfont = font, savfont1 = font1;
+ int f, g, i, j, n;
+ tchar c, d, *cp = NULL, *dp = NULL;
+ int a = 0, b = 0;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ font = font1 = f;
+ mchbits();
+ if (skip(1))
+ goto done;
+ while ((j = cbits(c = getch())) > ' ' || j == UNPAD) {
+ if (fbits(c) != f) {
+ if (warn & WARN_CHAR)
+ errprint("glyph %C not in font %s",
+ c, macname(i));
+ continue;
+ }
+ cp = realloc(cp, ++a * sizeof *cp);
+ cp[a-1] = c;
+ }
+ if (a == 0 || skip(1))
+ goto done;
+ i = getrq(2);
+ if ((g = findft(i, 1)) < 0)
+ goto done;
+ font = font1 = g;
+ mchbits();
+ if (skip(1))
+ goto done;
+ while ((j = cbits(c = getch())) > ' ' || j == UNPAD) {
+ if (fbits(c) != g) {
+ if (warn & WARN_CHAR)
+ errprint("glyph %C not in font %s",
+ c, macname(i));
+ continue;
+ }
+ dp = realloc(dp, ++b * sizeof *dp);
+ dp[b-1] = c;
+ }
+ if (b == 0 || skip(1))
+ goto done;
+ noscale++;
+ n = hatoi();
+ noscale--;
+ unitsPerEm = 1000;
+ n = _unitconv(n);
+ for (i = 0; i < a; i++)
+ for (j = 0; j < b; j++) {
+ if ((c = cbits(cp[i])) == 0)
+ continue;
+ if (c == UNPAD)
+ c = ' ';
+ setfbits(c, f);
+ if ((d = cbits(dp[j])) == 0)
+ continue;
+ if (d == UNPAD)
+ d = ' ';
+ setfbits(d, g);
+ kadd(c, d, n);
+ }
+done:
+ free(cp);
+ free(dp);
+ font = savfont;
+ font1 = savfont1;
+ mchbits();
+}
+
+static void
+kernsingle(int **tp)
+{
+ int savfont = font, savfont1 = font1;
+ int f, i, j, n;
+ int twice = 0;
+ tchar c, *cp = NULL;
+ int a;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ font = font1 = f;
+ mchbits();
+ while (!skip(twice++ == 0)) {
+ a = 0;
+ while ((j = cbits(c = getch())) > ' ') {
+ if (fbits(c) != f) {
+ if (warn & WARN_CHAR)
+ errprint("glyph %C not in font %s",
+ c, macname(i));
+ continue;
+ }
+ cp = realloc(cp, ++a * sizeof *cp);
+ cp[a-1] = c;
+ }
+ if (skip(1))
+ break;
+ noscale++;
+ n = hatoi();
+ noscale--;
+ if (tp[f] == NULL)
+ tp[f] = calloc(NCHARS, sizeof *tp);
+ unitsPerEm = 1000;
+ n = _unitconv(n);
+ for (j = 0; j < a; j++)
+ tp[f][cbits(cp[j])] = n;
+ }
+ free(cp);
+ font = savfont;
+ font1 = savfont1;
+ mchbits();
+}
+
+void
+casekernafter(void)
+{
+ kernsingle(kernafter);
+}
+
+void
+casekernbefore(void)
+{
+ kernsingle(kernbefore);
+}
+
+void
+caseftr(void)
+{
+ int savfont = font, savfont1 = font1;
+ int f, i, j;
+ tchar k;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ font = font1 = f;
+ mchbits();
+ if (skip(1))
+ goto done;
+ while ((i = cbits(k=getch())) != '\n') {
+ if (ismot(k))
+ goto done;
+ if (ismot(k = getch()))
+ goto done;
+ if ((j = cbits(k)) == '\n')
+ j = ' ';
+ ftrtab[f][i] = j;
+ }
+done:
+ checkenminus(f);
+ font = savfont;
+ font1 = savfont1;
+ mchbits();
+}
+
+static int
+getfeature(struct afmtab *a, int f)
+{
+ char name[NC];
+ int ch1, ch2, c, i, j, minus;
+ struct feature *fp;
+
+ if (skip(0))
+ return 0;
+ switch (c = getach()) {
+ case '-':
+ c = getach();
+ minus = 1;
+ break;
+ case '+':
+ c = getach();
+ /*FALLTHRU*/
+ default:
+ minus = 0;
+ break;
+ case 0:
+ return 0;
+ }
+ for (i = 0; i < sizeof name - 2; i++) {
+ name[i] = c;
+ if ((c = getach()) == 0)
+ break;
+ }
+ name[i+1] = 0;
+ for (i = 0; (fp = a->features[i]); i++)
+ if (strcmp(fp->name, name) == 0) {
+ for (j = 0; j < fp->npairs; j++) {
+ ch1 = fp->pairs[j].ch1;
+ ch2 = fp->pairs[j].ch2;
+ if (minus) {
+ if (ftrtab[f][ch1] == ch2)
+ ftrtab[f][ch1] = ch1;
+ } else {
+ ftrtab[f][ch1] = ch2;
+ }
+ }
+ break;
+ }
+ if (fp == NULL)
+ errprint("no feature named %s in font %s", name, a->fontname);
+ return 1;
+}
+
+void
+casefeature(void)
+{
+ struct afmtab *a;
+ int f, i, j;
+
+ lgf++;
+ if (skip(1))
+ return;
+ i = getrq(2);
+ if ((f = findft(i, 1)) < 0)
+ return;
+ if ((j = (fontbase[f]->afmpos) - 1) < 0 ||
+ ((a = afmtab[j])->type != TYPE_OTF &&
+ a->type != TYPE_TTF)) {
+ errprint("font %s is not an OpenType font", macname(i));
+ return;
+ }
+ if (a->features == NULL) {
+ errprint("font %s has no OpenType features", a->fontname);
+ return;
+ }
+ while (getfeature(a, f) != 0);
+}
+
+#include "unimap.h"
+
+static int
+ufmap(int c, int f, int *fp)
+{
+ struct unimap *up, ***um;
+ struct afmtab *a;
+ int i;
+
+ if ((c&~0xffff) == 0 &&
+ (i = (fontbase[f]->afmpos) - 1) >= 0 &&
+ (um = (a = afmtab[i])->unimap) != NULL &&
+ um[c>>8] != NULL &&
+ (up = um[c>>8][c&0377]) != NULL) {
+ *fp = f;
+ return up->u.code;
+ }
+ return 0;
+}
+
+int
+un2tr(int c, int *fp)
+{
+ extern char ifilt[];
+ struct unimap *um, *up;
+ int i, j;
+
+ switch (c) {
+ case 0x00A0:
+ *fp = font;
+ return UNPAD;
+ case 0x00AD:
+ *fp = font;
+ return ohc;
+ case 0x2002:
+ return makem((int)(EM)/2);
+ case 0x2003:
+ return makem((int)EM);
+ case 0x2004:
+ return makem((int)EM/3);
+ case 0x2005:
+ return makem((int)EM/4);
+ case 0x2006:
+ return makem((int)EM/6);
+ case 0x2007:
+ return makem(width('0' | chbits));
+ case 0x2008:
+ return makem(width('.' | chbits));
+ case 0x2009:
+ return makem((int)EM/6);
+ case 0x200A:
+ return makem((int)EM/12);
+ case 0x2010:
+ *fp = font;
+ return '-';
+ case 0x2027:
+ *fp = font;
+ return OHC | BLBIT;
+ case 0x2060:
+ *fp = font;
+ return FILLER;
+ default:
+ if ((i = ufmap(c, font, fp)) != 0)
+ return i;
+ if ((c&~0xffff) == 0 && unimap[c>>8] != NULL &&
+ (um = unimap[c>>8][c&0377]) != NULL) {
+ up = um;
+ do
+ if ((j = postchar1(up->u.psc, font)) != 0) {
+ *fp = font;
+ return j;
+ }
+ while ((up = up->next) != NULL);
+ up = um;
+ do
+ if ((j = postchar(up->u.psc, fp)) != 0)
+ return j;
+ while ((up = up->next) != NULL);
+ up = um;
+ do
+ if ((j = _ps2cc(up->u.psc, 0)) != 0) {
+ j += nchtab + 128 + 32 +
+ 128 - 32 + nchtab;
+ if (chartab[j] != NULL)
+ return j;
+ }
+ while ((up = up->next) != NULL);
+ }
+ if (fallbacktab[font])
+ for (j = 0; fallbacktab[font][j] != 0; j++) {
+ if ((i = findft(fallbacktab[font][j], 0)) < 0)
+ continue;
+ if ((i = ufmap(c, i, fp)) != 0)
+ return i;
+ }
+ if (smnt)
+ for (i = smnt, j=0; j < nfonts; j++, i = i % nfonts + 1) {
+ if (fontbase[i] == NULL)
+ continue;
+ if ((i = ufmap(c, i, fp)) != 0)
+ return i;
+ }
+ *fp = font;
+ if ((c < 040 && c == ifilt[c]) || (c >= 040 && c < 0177))
+ return c;
+ else if ((c & ~0177) == 0) {
+ illseq(c, NULL, 0);
+ return 0;
+ } else if (defcf && (c & ~0xffff) == 0) {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "[uni%04X]", c);
+ cpushback(buf);
+ unadd(c, NULL);
+ return WORDSP;
+ } else if (html) {
+ return c;
+ } else {
+ if (warn & WARN_CHAR)
+ errprint("no glyph available for %U", c);
+ return tryglf ? 0 : ' ';
+ }
+ }
+}
+
+int
+tr2un(tchar i, int f)
+{
+ struct afmtab *a;
+ int c, n;
+
+ if (i < 32)
+ return -1;
+ else if (i < 128)
+ return i;
+ if ((n = (fontbase[f]->afmpos) - 1) >= 0) {
+ a = afmtab[n];
+ if (a->unitab && i < a->nunitab && a->unitab[i])
+ return a->unitab[i];
+ if (i - 32 >= nchtab + 128)
+ i -= nchtab + 128;
+ if ((n = a->fitab[i - 32]) < a->nchars &&
+ a->nametab[n] != NULL)
+ for (c = 0; rawunimap[c].psc; c++)
+ if (strcmp(rawunimap[c].psc, a->nametab[n])==0)
+ return rawunimap[c].code;
+ }
+ return -1;
+}
+
+tchar
+setuc0(int n)
+{
+ int f;
+ tchar c;
+
+ if ((c = un2tr(n, &f)) != 0 && !ismot(c)) {
+ c |= chbits & ~FMASK;
+ setfbits(c, f);
+ }
+ return c;
+}
+
+static char *
+getref(void)
+{
+ int a = 0, i, c, delim;
+ char *np = NULL;
+
+ if ((delim = getach()) != 0) {
+ for (i = 0; ; i++) {
+ if (i + 1 >= a)
+ np = realloc(np, a += 32);
+ if ((c = getach()) == 0) {
+ if (cbits(ch) == ' ') {
+ ch = 0;
+ c = ' ';
+ } else {
+ nodelim(delim);
+ break;
+ }
+ }
+ if (c == delim)
+ break;
+ np[i] = c;
+ }
+ np[i] = 0;
+ }
+ return np;
+}
+
+tchar
+setanchor(void)
+{
+ static int cnt;
+ struct ref *rp;
+ char *np;
+
+ if ((np = getref()) != NULL) {
+ rp = calloc(1, sizeof *rp);
+ rp->cnt = ++cnt;
+ rp->name = np;
+ rp->next = anchors;
+ anchors = rp;
+ return mkxfunc(ANCHOR, cnt);
+ } else
+ return mkxfunc(ANCHOR, 0);
+}
+
+static tchar
+_setlink(struct ref **rstart, int oncode, int offcode, int *cnt)
+{
+ struct ref *rp;
+ char *np;
+ int sv;
+
+ sv = linkin;
+ if ((linkin = !linkin)) {
+ if ((np = getref()) != NULL) {
+ rp = calloc(1, sizeof *rp);
+ rp->cnt = ++*cnt;
+ rp->name = np;
+ rp->next = *rstart;
+ *rstart = rp;
+ linkin = *cnt;
+ return mkxfunc(oncode, *cnt);
+ } else {
+ linkin = -1;
+ return mkxfunc(oncode, 0);
+ }
+ } else
+ return mkxfunc(offcode, sv > 0 ? sv : 0);
+}
+
+tchar
+setlink(void)
+{
+ static int cnt;
+
+ return _setlink(&links, LINKON, LINKOFF, &cnt);
+}
+
+tchar
+setulink(void)
+{
+ static int cnt;
+
+ return _setlink(&ulinks, ULINKON, ULINKOFF, &cnt);
+}
+
+int
+pts2u(int p)
+{
+ return p * INCH / 72;
+}
+
+double
+u2pts(int u)
+{
+ return u * 72.0 / INCH;
+}
+
+#define psnprime 1021
+
+static struct psnnode {
+ struct psnnode *next;
+ const char *name;
+ int code;
+} **psntable;
+
+static int
+_ps2cc(const char *name, int create)
+{
+ struct psnnode *pp;
+ unsigned h;
+
+ if (psntable == NULL)
+ psntable = calloc(psnprime, sizeof *psntable);
+ h = pjw(name) % psnprime;
+ for (pp = psntable[h]; pp; pp = pp->next)
+ if (strcmp(name, pp->name) == 0)
+ return pp->code;
+ if (create == 0)
+ return 0;
+ pp = calloc(1, sizeof *pp);
+ pp->name = strdup(name);
+ pp->next = psntable[h];
+ psntable[h] = pp;
+ return pp->code = ++psmaxcode;
+}
+
+int
+ps2cc(const char *name)
+{
+ return _ps2cc(name, 1);
+}