/*
 * 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 (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)afm.c	1.65 (gritter) 1/14/10
 */

#include <stdlib.h>
#include <string.h>
#include "dev.h"
#include "afm.h"

#ifndef __unused
#define	__unused __attribute__((unused))
#endif

extern	char		*chname;
extern	short		*chtab;
extern	int		nchtab;

extern	void	errprint(const char *, ...);

#if !defined (DPOST) && !defined (DUMP)
static	void	addkernpair(struct afmtab *, char *_line);
#endif

/*
 * This table maps troff special characters to PostScript names.
 */
static const struct names {
	const char	*trname;
	const char	*psname;
} names[] = {
	{ "hy",	"hyphen" },
	{ "ct",	"cent" },
	{ "fi",	"fi" },
	{ "fi",	"f_i" },
	{ "fl",	"fl" },
	{ "fl",	"f_l" },
	{ "ff", "ff" },
	{ "ff", "f_f" },
	{ "Fi", "ffi" },
	{ "Fi", "f_f_i" },
	{ "Fl", "ffl" },
	{ "Fl", "f_f_l" },
	{ "dg",	"dagger" },
	{ "dd",	"daggerdbl" },
	{ "bu",	"bullet" },
	{ "de",	"ring" },
	{ "em",	"emdash" },
	{ "en",	"endash" },
	{ "sc",	"section" },
	{ "``",	"quotedblleft" },
	{ "''",	"quotedblright" },
	{ "12",	"onehalf" },
	{ "14",	"onequarter" },
	{ "34",	"threequarters" },
	{ "aq", "quotesingle" },
	{ "oq", "quoteleft" },
	{ "cq", "quoteright" },
	{ 0,	0 }
};

/*
 * Names for Symbol fonts only.
 */
static const struct names greeknames[] = {
	{ "*A",	"Alpha" },
	{ "*B",	"Beta" },
	{ "*C",	"Xi" },
	{ "*D",	"Delta" },
	{ "*E",	"Epsilon" },
	{ "*F",	"Phi" },
	{ "*G",	"Gamma" },
	{ "*H",	"Theta" },
	{ "*I",	"Iota" },
	{ "*K",	"Kappa" },
	{ "*L",	"Lambda" },
	{ "*M",	"Mu" },
	{ "*N",	"Nu" },
	{ "*O",	"Omicron" },
	{ "*P",	"Pi" },
	{ "*Q",	"Psi" },
	{ "*R",	"Rho" },
	{ "*S",	"Sigma" },
	{ "*T",	"Tau" },
	{ "*U",	"Upsilon" },
	{ "*W",	"Omega" },
	{ "*X",	"Chi" },
	{ "*Y",	"Eta" },
	{ "*Z",	"Zeta" },
	{ "*a",	"alpha" },
	{ "*b",	"beta" },
	{ "*c",	"xi" },
	{ "*d",	"delta" },
	{ "*e",	"epsilon" },
	{ "*f",	"phi" },
	{ "*g",	"gamma" },
	{ "*h",	"theta" },
	{ "*i",	"iota" },
	{ "*k",	"kappa" },
	{ "*l",	"lambda" },
	{ "*m",	"mu" },
	{ "*n",	"nu" },
	{ "*o",	"omicron" },
	{ "*p",	"pi" },
	{ "*q",	"psi" },
	{ "*r",	"rho" },
	{ "*s",	"sigma" },
	{ "*t",	"tau" },
	{ "*u",	"upsilon" },
	{ "*w",	"omega" },
	{ "*x",	"chi" },
	{ "*y",	"eta" },
	{ "*z",	"zeta" },
	{ 0,	0 }
};

static const struct names mathnames[] = {
	{ "!=",	"notequal" },
	{ "**",	"asteriskmath" },
	{ "+-",	"plusminus" },
	{ "->",	"arrowright" },
	{ "<",	"less" },
	{ "<-",	"arrowleft" },
	{ "<=",	"lessequal" },
	{ "==",	"equivalence" },
	{ ">",	"greater" },
	{ ">=",	"greaterequal" },
	{ "O+",	"circleplus" },
	{ "Ox",	"circlemultiply" },
//	{ "^",	"logicaland" },
	{ "al",	"aleph" },
	{ "ap",	"similar" },
	{ "bu",	"bullet" },
	{ "ca",	"intersection" },
	{ "co",	"copyrightserif" },
	{ "cu",	"union" },
	{ "da",	"arrowdown" },
	{ "de",	"degree" },
	{ "di",	"divide" },
	{ "eq",	"equal" },
	{ "es",	"emptyset" },
	{ "fa",	"universal" },
	{ "fm",	"minute" },
	{ "gr",	"gradient" },
	{ "ib",	"reflexsubset" },
	{ "if",	"infinity" },
	{ "ip",	"reflexsuperset" },
	{ "is",	"integral" },
	{ "mi",	"minus" },
	{ "mo",	"element" },
	{ "mu",	"multiply" },
	{ "no",	"logicalnot" },
	{ "or",	"bar" },
//	{ "or",	"logicalor" },
	{ "pd",	"partialdiff" },
	{ "pl",	"plus" },
	{ "pt",	"proportional" },
	{ "rg",	"registerserif" },
	{ "sb",	"propersubset" },
	{ "sl",	"fraction" },
	{ "sp",	"propersuperset" },
	{ "sr",	"radical" },
	{ "te",	"existential" },
	{ "tm",	"trademarkserif" },
	{ "ts",	"sigma1" },
	{ "ua",	"arrowup" },
	{ "~~",	"approxequal" },
	{ 0,	0 },
};

static const struct names largenames[] = {
	{ "bv",	"braceex" },
	{ "lb",	"braceleftbt" },
	{ "lc",	"bracketlefttp" },
	{ "lf",	"bracketleftbt" },
	{ "lk",	"braceleftmid" },
	{ "lt",	"bracelefttp" },
	{ "rb",	"bracerightbt" },
	{ "rc",	"bracketrighttp" },
	{ "rf",	"bracketrightbt" },
	{ "rk",	"bracerightmid" },
	{ "rn",	"radicalex" },
	{ "rt",	"bracerighttp" },
	{ 0,	0 }
};

static const struct names punctnames[] = {
	{ "or",	"bar" },
	{ "\\-","endash" },
	{ "aa","acute" },
	{ "ga","grave" },
	{ "rs","backslash" },
	{ "dq","quotedbl" },
	{ 0,	0 }
};

/*
 * These names are only used with the S font.
 */
static const struct names Snames[] = {
	{ "br",	"parenleftex" },
	{ "ul",	"underscore" },
	{ "vr",	"bracketleftex" },
	{ 0,	0 }
};

/*
 * These names are only used with the S1 font.
 */
static const struct names S1names[] = {
	{ "ru",	"underscore" },
	{ 0,	0 }
};

/*
 * Figures from charlib.
 */
#define	NCHARLIB	16
static const struct charlib {
	const char	*name;
	int		width;
	int		kern;
	int		code;
	int		symbol;
	enum {
		NEEDS_F = 01,
		NEEDS_I = 02,
		NEEDS_L = 04
	}		needs;
} charlib[] = {
	{ "bx",	 500, 2, 1, 0, 0 },
	{ "ci",	 750, 0, 1, 0, 0 },
	{ "sq",	 500, 2, 1, 0, 0 },
	{ "ff",	 600, 2, 1, 0, 1 },	/* widths of the ligatures */
	{ "Fi",	 840, 2, 1, 0, 3 },	/* are likely wrong, but */
	{ "Fl",	 840, 2, 1, 0, 5 },	/* they are normally not used */
	{ "~=",	 550, 0, 1, 1, 0 },
	{ "L1", 1100, 1, 2, 1, 0 },
	{ "LA",	1100, 1, 2, 1, 0 },
	{ "LV",	1100, 3, 2, 1, 0 },
	{ "LH",	2100, 1, 2, 1, 0 },
	{ "Lb",	2100, 1, 2, 1, 0 },
	{ "lh",	1000, 0, 2, 1, 0 },
	{ "rh",	1000, 0, 2, 1, 0 },
	{ "Sl",	 500, 2, 1, 1, 0 },
	{ "ob",	 380, 0, 1, 1, 0 },
	{    0,	   0, 0, 0, 0, 0 }
};

/*
 * The values in this table determine if a character that is found on an
 * ASCII position in a PostScript font actually is that ASCII character.
 * If not, the position in fitab remains empty, and the fallback sequence
 * is used to find it in another font.
 */
static const struct asciimap {
	int		code;
	const char	*psc;
} asciimap[] = {
	{ 0x0020,	"space" },
	{ 0x0021,	"exclam" },
	{ 0x0024,	"dollar" },
	{ 0x0024,	"dollaralt" },		/* FournierMT-RegularAlt */
	{ 0x0025,	"percent" },
	{ 0x0026,	"ampersand" },
	{ 0x0026,	"ampersandalt" },	/* AGaramondAlt-Italic */
	{ 0x0027,	"quoteright" },
	{ 0x0028,	"parenleft" },
	{ 0x0029,	"parenright" },
	{ 0x002A,	"asterisk" },
	{ 0x002B,	"plus" },
	{ 0x002C,	"comma" },
	{ 0x002D,	"hyphen" },
	{ 0x002E,	"period" },
	{ 0x002F,	"slash" },
	{ 0x0030,	"zero" },
	{ 0x0030,	"zerooldstyle" },
	{ 0x0030,	"zeroalt" },		/* BulmerMT-RegularAlt */
	{ 0x0031,	"one" },
	{ 0x0031,	"oneoldstyle" },
	{ 0x0031,	"onefitted" },
	{ 0x0031,	"onealtfitted" },	/* BulmerMT-ItalicAlt */
	{ 0x0032,	"two" },
	{ 0x0032,	"twooldstyle" },
	{ 0x0032,	"twoalt" },		/* BulmerMT-RegularAlt */
	{ 0x0033,	"three" },
	{ 0x0033,	"threeoldstyle" },
	{ 0x0033,	"threealt" },		/* BulmerMT-RegularAlt */
	{ 0x0034,	"four" },
	{ 0x0034,	"fouroldstyle" },
	{ 0x0034,	"fouralt" },		/* BulmerMT-RegularAlt */
	{ 0x0035,	"five" },
	{ 0x0035,	"fiveoldstyle" },
	{ 0x0035,	"fivealt" },		/* BulmerMT-RegularAlt */
	{ 0x0036,	"six" },
	{ 0x0036,	"sixoldstyle" },
	{ 0x0036,	"sixalt" },		/* BulmerMT-RegularAlt */
	{ 0x0037,	"seven" },
	{ 0x0037,	"sevenoldstyle" },
	{ 0x0037,	"sevenalt" },		/* BulmerMT-RegularAlt */
	{ 0x0038,	"eight" },
	{ 0x0038,	"eightoldstyle" },
	{ 0x0038,	"eightalt" },		/* BulmerMT-RegularAlt */
	{ 0x0039,	"nine" },
	{ 0x0039,	"nineoldstyle" },
	{ 0x0039,	"ninealt" },		/* BulmerMT-RegularAlt */
	{ 0x003A,	"colon" },
	{ 0x003B,	"semicolon" },
	{ 0x003D,	"equal" },
	{ 0x003F,	"question" },
	{ 0x0041,	"A" },
	{ 0x0041,	"Aswash" },		/* AGaramondAlt-Italic */
	{ 0x0042,	"B" },
	{ 0x0042,	"Bswash" },		/* AGaramondAlt-Italic */
	{ 0x0043,	"C" },
	{ 0x0043,	"Cswash" },		/* AGaramondAlt-Italic */
	{ 0x0044,	"D" },
	{ 0x0044,	"Dswash" },		/* AGaramondAlt-Italic */
	{ 0x0045,	"E" },
	{ 0x0045,	"Eswash" },		/* AGaramondAlt-Italic */
	{ 0x0046,	"F" },
	{ 0x0046,	"Fswash" },		/* AGaramondAlt-Italic */
	{ 0x0047,	"G" },
	{ 0x0047,	"Gswash" },		/* AGaramondAlt-Italic */
	{ 0x0048,	"H" },
	{ 0x0048,	"Hswash" },		/* AGaramondAlt-Italic */
	{ 0x0049,	"I" },
	{ 0x0049,	"Iswash" },		/* AGaramondAlt-Italic */
	{ 0x004A,	"J" },
	{ 0x004A,	"Jalt" },		/* FournierMT-RegularAlt */
	{ 0x004A,	"Jalttwo" },		/* BulmerMT-ItalicAlt */
	{ 0x004A,	"Jswash" },		/* AGaramondAlt-Italic */
	{ 0x004A,	"JTallCapalt" },	/* FournierMT-RegularAlt */
	{ 0x004B,	"K" },
	{ 0x004B,	"Kalt" },		/* BulmerMT-ItalicAlt */
	{ 0x004B,	"Kswash" },		/* AGaramondAlt-Italic */
	{ 0x004C,	"L" },
	{ 0x004C,	"Lswash" },		/* AGaramondAlt-Italic */
	{ 0x004D,	"M" },
	{ 0x004D,	"Mswash" },		/* AGaramondAlt-Italic */
	{ 0x004E,	"N" },
	{ 0x004E,	"Nalt" },		/* BulmerMT-ItalicAlt */
	{ 0x004E,	"Nswash" },		/* AGaramondAlt-Italic */
	{ 0x004F,	"O" },
	{ 0x004F,	"Oalt" },		/* BulmerMT-ItalicAlt */
	{ 0x004F,	"Oswash" },		/* AGaramondAlt-Italic */
	{ 0x0050,	"P" },
	{ 0x0050,	"Pswash" },		/* AGaramondAlt-Italic */
	{ 0x0051,	"Q" },
	{ 0x0051,	"Qalt" },		/* FournierMT-RegularAlt */
	{ 0x0051,	"Qalttitling" },	/* AGaramondAlt-Regular */
	{ 0x0051,	"Qswash" },		/* AGaramondAlt-Italic */
	{ 0x0051,	"QTallCapalt" },	/* FournierMT-RegularAlt */
	{ 0x0052,	"R" },
	{ 0x0052,	"Ralternate" },		/* Bembo-Alt */
	{ 0x0052,	"Rswash" },		/* AGaramondAlt-Italic */
	{ 0x0053,	"S" },
	{ 0x0053,	"Sswash" },		/* AGaramondAlt-Italic */
	{ 0x0054,	"T" },
	{ 0x0054,	"Talt" },		/* BulmerMT-ItalicAlt */
	{ 0x0054,	"Tswash" },		/* AGaramondAlt-Italic */
	{ 0x0055,	"U" },
	{ 0x0055,	"Uswash" },		/* AGaramondAlt-Italic */
	{ 0x0056,	"V" },
	{ 0x0056,	"Vswash" },		/* AGaramondAlt-Italic */
	{ 0x0057,	"W" },
	{ 0x0057,	"Wswash" },		/* AGaramondAlt-Italic */
	{ 0x0058,	"X" },
	{ 0x0058,	"Xswash" },		/* AGaramondAlt-Italic */
	{ 0x0059,	"Y" },
	{ 0x0059,	"Yalt" },		/* BulmerMT-ItalicAlt */
	{ 0x0059,	"Yswash" },		/* AGaramondAlt-Italic */
	{ 0x005A,	"Z" },
	{ 0x005A,	"Zswash" },		/* AGaramondAlt-Italic */
	{ 0x005B,	"bracketleft" },
	{ 0x005D,	"bracketright" },
	{ 0x005F,	"underscore" },
	{ 0x0060,	"quoteleft" },
	{ 0x0060,	"quotealtleft" },	/* BulmerMT-RegularAlt */
	{ 0x0061,	"a" },
	{ 0x0061,	"Asmall" },
	{ 0x0061,	"aswash" },		/* AGaramondAlt-Regular */
	{ 0x0062,	"b" },
	{ 0x0062,	"Bsmall" },
	{ 0x0063,	"c" },
	{ 0x0063,	"Csmall" },
	{ 0x0064,	"d" },
	{ 0x0064,	"Dsmall" },
	{ 0x0065,	"e" },
	{ 0x0065,	"Esmall" },
	{ 0x0065,	"eswash" },		/* AGaramondAlt-Regular */
	{ 0x0066,	"f" },
	{ 0x0066,	"Fsmall" },
	{ 0x0067,	"g" },
	{ 0x0067,	"Gsmall" },
	{ 0x0068,	"h" },
	{ 0x0068,	"Hsmall" },
	{ 0x0069,	"i" },
	{ 0x0069,	"Ismall" },
	{ 0x006A,	"j" },
	{ 0x006A,	"Jsmall" },
	{ 0x006A,	"Jsmallalt" },		/* FournierMT-RegularAlt */
	{ 0x006B,	"k" },
	{ 0x006B,	"Ksmall" },
	{ 0x006C,	"l" },
	{ 0x006C,	"Lsmall" },
	{ 0x006D,	"m" },
	{ 0x006D,	"Msmall" },
	{ 0x006E,	"n" },
	{ 0x006E,	"Nsmall" },
	{ 0x006E,	"nswash" },		/* AGaramondAlt-Regular */
	{ 0x006F,	"o" },
	{ 0x006F,	"Osmall" },
	{ 0x0070,	"p" },
	{ 0x0070,	"Psmall" },
	{ 0x0071,	"q" },
	{ 0x0071,	"Qsmall" },
	{ 0x0072,	"r" },
	{ 0x0072,	"Rsmall" },
	{ 0x0072,	"rswash" },		/* AGaramondAlt-Regular */
	{ 0x0073,	"s" },
	{ 0x0073,	"Ssmall" },
	{ 0x0074,	"t" },
	{ 0x0074,	"Tsmall" },
	{ 0x0074,	"tswash" },		/* AGaramondAlt-Regular */
	{ 0x0074,	"tswashalt" },		/* AGaramondAlt-Regular */
	{ 0x0075,	"u" },
	{ 0x0075,	"Usmall" },
	{ 0x0076,	"v" },
	{ 0x0076,	"Vsmall" },
	{ 0x0076,	"vswash" },		/* AGaramondAlt-Italic */
	{ 0x0077,	"w" },
	{ 0x0077,	"Wsmall" },
	{ 0x0077,	"walt" },		/* FournierMT-RegularAlt */
	{ 0x0078,	"x" },
	{ 0x0078,	"Xsmall" },
	{ 0x0079,	"y" },
	{ 0x0079,	"Ysmall" },
	{ 0x007A,	"z" },
	{ 0x007A,	"Zsmall" },
	{ 0x007A,	"zalt" },		/* FournierMT-ItalicAlt */
	{ 0x007A,	"zswash" },		/* AGaramondAlt-Regular */
	{ 0x007B,	"braceleft" },
 	{ 0x007C,	"bar" },
	{ 0x007D,	"braceright" },
	{ 0,		0 }
};

/*
 * ASCII characters that are always taken from the S (math) font.
 */
static const struct asciimap	mathascii[] = {
	{ 0x002D,	"minus" },
	{ 0x007E,	"similar" },
	{ 0,		0 }
};

/*
 * ASCII characters that are always taken from the S1 (punct) font.
 */
static const struct asciimap	punctascii[] = {
 	{ 0x0022,	"quotedbl" },
 	{ 0x0023,	"numbersign" },
 	{ 0x003C,	"less" },
 	{ 0x003E,	"greater" },
 	{ 0x0040,	"at" },
 	{ 0x005C,	"backslash" },
 	{ 0x005E,	"asciicircum" },
 	{ 0x007E,	"asciitilde" },
	{ 0,		NULL }
};

int
nextprime(int n)
{
	const int	primes[] = {
		509, 1021, 2039, 4093, 8191, 16381, 32749, 65521,
		131071, 262139, 524287, 1048573, 2097143, 4194301,
		8388593, 16777213, 33554393, 67108859, 134217689,
		268435399, 536870909, 1073741789, 2147483647
	};
	int	mprime = 7;
	size_t	i;

	for (i = 0; i < sizeof primes / sizeof *primes; i++)
		if ((mprime = primes[i]) >= (n < 65536 ? n*4 :
					n < 262144 ? n*2 : n))
			break;
	if (i == sizeof primes / sizeof *primes)
		mprime = n;     /* not so prime, but better than failure */
	return mprime;
}

unsigned
pjw(const char *cp)
{
	unsigned	h = 0, g;

	cp--;
	while (*++cp) {
		h = (h << 4 & 0xffffffff) + (*cp&0377);
		if ((g = h & 0xf0000000) != 0) {
			h = h ^ g >> 24;
			h = h ^ g;
		}
	}
	return h;
}

struct namecache *
afmnamelook(struct afmtab *a, const char *name)
{
	struct namecache	*np;
	int	h, c, n = 0;

	h = pjw(name) % a->nameprime;
	np = &a->namecache[c = h];
	while (np->afpos != 0) {
		if (a->nametab[np->afpos] == 0 ||
				strcmp(a->nametab[np->afpos], name) == 0)
			break;
		h = (n + 1) / 2;
		h *= h;
		if (n & 1)
			c -= h;
		else
			c += h;
		n++;
		while (c >= a->nameprime)
			c -= a->nameprime;
		np = &a->namecache[c];
	}
	return np;
}

static int
mapname1(const char *psname, const struct names *np)
{
	int	i, j;

	for (i = 0; np[i].trname; i++)
		if (strcmp(np[i].psname, psname) == 0)
			break;
	if (np[i].trname)
		for (j = 0; j < nchtab; j++)
			if (strcmp(np[i].trname, &chname[chtab[j]]) == 0)
				return j + 128;
	return 0;
}

int
afmmapname(const char *psname, enum spec s)
{
	int	k;

	if (s & SPEC_S && (k = mapname1(psname, Snames)) > 0)
		return k;
	if (s & SPEC_S1 && (k = mapname1(psname, S1names)) > 0)
		return k;
	if (s & (SPEC_MATH|SPEC_S) && (k = mapname1(psname, mathnames)) > 0)
		return k;
	if (s & (SPEC_GREEK|SPEC_S) && (k = mapname1(psname, greeknames)) > 0)
		return k;
	if (s & (SPEC_LARGE|SPEC_S) && (k = mapname1(psname, largenames)) > 0)
		return k;
	if (s & (SPEC_PUNCT|SPEC_S1) && (k = mapname1(psname, punctnames)) > 0)
		return k;
	return mapname1(psname, names);
}

/*
 * After all characters have been read, construct a font-specific
 * encoding for the rest. Also move the name table to permanent space.
 */
void
afmremap(struct afmtab *a)
{
	int	i, j = 128 - 32 + nchtab;
	char	*space, *tp;
	struct namecache	*np;

	for (i = 1; i < a->nchars; i++) {
		if (a->codetab[i] == NOCODE && a->nametab[i] != NULL) {
#if defined (DPOST) || defined (DUMP)
			while (a->fitab[j] != 0)
				j++;
#else	/* TROFF */
			extern int	ps2cc(const char *);
			j = ps2cc(a->nametab[i]) + 128 - 32 + nchtab;
#endif	/* TROFF */
			a->fitab[j] = i;
			np = afmnamelook(a, a->nametab[i]);
			np->afpos = i;
			np->fival[0] = j;
			if (np->gid != 0 && a->gid2tr)
				a->gid2tr[np->gid].ch1 = j + 32 + nchtab + 128;
			if (strcmp(a->nametab[i], "space") == 0) {
				np->fival[1] = 0;
				if (np->gid != 0 && a->gid2tr)
					a->gid2tr[np->gid].ch2 = 32;
			}
		}
	}
	space = malloc(a->nspace);
	for (i = 0; i < a->nchars; i++) {
		if (a->nametab[i]) {
			tp = a->nametab[i];
			a->nametab[i] = space;
			while ((*space++ = *tp++));
		}
	}
}

#ifndef	DUMP
static int
asciiequiv(int code __unused, const char *psc, enum spec s)
{
	int	i;

	if (psc != NULL) {
		for (i = 0; asciimap[i].psc; i++)
			if (strcmp(asciimap[i].psc, psc) == 0)
				return asciimap[i].code;
		if (s & (SPEC_MATH|SPEC_S)) {
			for (i = 0; mathascii[i].psc; i++)
				if (strcmp(mathascii[i].psc, psc) == 0)
					return mathascii[i].code;
		}
		if (s & (SPEC_PUNCT|SPEC_S1)) {
			for (i = 0; punctascii[i].psc; i++)
				if (strcmp(punctascii[i].psc, psc) == 0)
					return punctascii[i].code;
		}
	}
	return 0;
}

static char *
thisword(char *text, const char *wrd)
{
	while (*text == *wrd)
		text++, wrd++;
	if (*wrd != 0)
		return 0;
	if (*text == 0 || *text == ' ' || *text == '\t' || *text == '\n' ||
			*text == '\r') {
		while (*text != 0 && (*text == ' ' || *text == '\t'))
			text++;
		return text;
	}
	return NULL;
}
#endif	/* !DUMP */

int
unitconv(int i)
{
	if (unitsPerEm * 72 != dev.unitwidth * dev.res)
		i = i * dev.unitwidth * dev.res / 72 / unitsPerEm;
	return i;
}

#ifndef	DUMP
void
afmaddchar(struct afmtab *a, int C, int tp, int cl, int WX, int B[4], char *N,
		enum spec s, int gid)
{
	struct namecache	*np = NULL;

	if (N != NULL) {
		np = afmnamelook(a, N);
		np->afpos = a->nchars;
		np->gid = gid;
		if (a->isFixedPitch && strcmp(N, "space") == 0)
			a->fontab[0] = _unitconv(WX);
	}
	a->fontab[a->nchars] = _unitconv(WX);
	if (B) {
		a->bbtab[a->nchars] = malloc(4 * sizeof **a->bbtab);
		a->bbtab[a->nchars][0] = _unitconv(B[0]);
		a->bbtab[a->nchars][1] = _unitconv(B[1]);
		a->bbtab[a->nchars][2] = _unitconv(B[2]);
		a->bbtab[a->nchars][3] = _unitconv(B[3]);
	/*
	 * Crude heuristics mainly based on observations with the existing
	 * fonts for -Tpost and on tests with eqn.
	 */
		if (B[1] <= -10)
			a->kerntab[a->nchars] |= 1;
		if (B[3] > (a->xheight + a->capheight) / 2)
			a->kerntab[a->nchars] |= 2;
	}
	/*
	 * Only map a character directly if it maps to an ASCII
	 * equivalent or to a troff special character.
	 */
	C = asciiequiv(C, N, s);
	if (cl)
		a->codetab[a->nchars] = cl;
	else if (tp)
		a->codetab[a->nchars] = tp;
	else if (C > 32 && C < 127 && a->fitab[C - 32] == 0)
		a->codetab[a->nchars] = C;
	else
		a->codetab[a->nchars] = NOCODE;
	if (C > 32 && C < 127 && a->fitab[C - 32] == 0) {
		a->fitab[C - 32] = a->nchars;
		if (gid && a->gid2tr)
			a->gid2tr[gid].ch1 = C;
		if (np)
			np->fival[0] = C - 32;
	} else if (C == 32 && np)
		np->fival[0] = 0;
	if (tp) {
		a->fitab[tp - 32] = a->nchars;
		if (gid && a->gid2tr)
			a->gid2tr[gid].ch2 = tp;
		if (np)
			np->fival[1] = tp - 32;
	}
	a->nametab[a->nchars] = N;
	a->nchars++;
}

/*
 * Add charlib figues.
 */
static void
addcharlib(struct afmtab *a, int symbol)
{
	int	i, j;
	int	B[4] = { 0, 0, 0, 0 };

	for (j = 0; j < nchtab; j++)
		for (i = 0; charlib[i].name; i++) {
			if (charlib[i].symbol && !symbol)
				continue;
			if (charlib[i].needs & NEEDS_F && a->fitab['f'-32] == 0)
				continue;
			if (charlib[i].needs & NEEDS_I && a->fitab['i'-32] == 0)
				continue;
			if (charlib[i].needs & NEEDS_L && a->fitab['l'-32] == 0)
				continue;
			if (strcmp(charlib[i].name, &chname[chtab[j]]) == 0) {
				B[1] = charlib[i].kern & 1 ? -11 : 0;
				B[3] = charlib[i].kern & 2 ?
					a->capheight + 1 : 0;
				afmaddchar(a, NOCODE, j+128, charlib[i].code,
					charlib[i].width * unitsPerEm / 1024,
					B, NULL, SPEC_NONE, 0);
			}
		}
}

static void
addmetrics(struct afmtab *a, char *_line, enum spec s)
{
	char	*lp = _line, c, *xp;
	int	C = NOCODE, WX = 0, tp;
	char	*N = NULL;
	int	B[4] = { -1, -1, -1, -1 };

	while (*lp && *lp != '\n' && *lp != '\r') {
		switch (*lp) {
		case 'C':
			C = strtol(&lp[1], NULL, 10);
			break;
		case 'W':
			if (lp[1] == 'X')
				WX = strtol(&lp[2], NULL, 10);
			break;
		case 'N':
			for (N = &lp[1]; *N == ' ' || *N == '\t'; N++);
			for (lp = N; *lp && *lp != '\n' && *lp != '\r' &&
					*lp != ' ' && *lp != '\t' &&
					*lp != ';'; lp++);
			c = *lp;
			*lp++ = 0;
			a->nspace += lp - N;
			if (c == ';')
				continue;
			break;
		case 'B':
			xp = &lp[1];
			B[0] = strtol(xp, &xp, 10);
			B[1] = strtol(xp, &xp, 10);
			B[2] = strtol(xp, &xp, 10);
			B[3] = strtol(xp, &xp, 10);
			break;
		case 'L':
			if (C == 'f') {
				xp = &lp[1];
				while (*xp == ' ' || *xp == '\t')
					xp++;
				switch (*xp) {
				case 'i':
					a->Font.ligfont |= LFI;
					break;
				case 'l':
					a->Font.ligfont |= LFL;
					break;
				}
			}
			break;
		default:
			lp++;
		}
		while (*lp && *lp != '\n' && *lp != '\r' && *lp != ';')
			lp++;
		if (*lp == ';') {
			while (*lp && *lp != '\n' && *lp != '\r' &&
					(*lp == ' ' || *lp == '\t' ||
					 *lp == ';'))
				lp++;
		}
	}
	if (N == NULL)
		return;
	tp = afmmapname(N, s);
	afmaddchar(a, C, tp, 0, WX, B, N, s, 0);
}

void
afmalloc(struct afmtab *a, int n)
{
	int	i;

#if defined (DPOST) || defined (DUMP)
	a->fichars = n+NCHARLIB+1 + 128 - 32 + nchtab;
#else	/* TROFF */
	extern int	psmaxcode;
	a->fichars = n+NCHARLIB+1 + 128 - 32 + nchtab + psmaxcode+1;
#endif	/* TROFF */
	a->fitab = calloc(a->fichars, sizeof *a->fitab);
	a->fontab = malloc((n+NCHARLIB+1)*sizeof *a->fontab);
	a->fontab[0] = dev.res * dev.unitwidth / 72 / 3;
	a->bbtab = calloc(n+NCHARLIB+1, sizeof *a->bbtab);
	a->kerntab = calloc(n+NCHARLIB+1, sizeof *a->kerntab);
	a->codetab = malloc((n+NCHARLIB+1)*sizeof *a->codetab);
	a->codetab[0] = 0;
	for (i = 1; i < n+NCHARLIB+1; i++)
		a->codetab[i] = NOCODE;
	a->nametab = malloc((n+NCHARLIB+1)*sizeof *a->nametab);
	a->nametab[0] = 0;
	a->nchars = 1;
	addcharlib(a, (a->base[0]=='S' && a->base[1]==0) || a->spec&SPEC_S);
	a->nameprime = nextprime(n+NCHARLIB+1);
	a->namecache = calloc(a->nameprime, sizeof *a->namecache);
	for (i = 0; i < a->nameprime; i++) {
		a->namecache[i].fival[0] = NOCODE;
		a->namecache[i].fival[1] = NOCODE;
	}
}

int
afmget(struct afmtab *a, char *contents, size_t size)
{
	enum {
		NONE,
		FONTMETRICS,
		CHARMETRICS,
		KERNDATA,
		KERNPAIRS
	} state = NONE;
	char	*cp, *th, *tp;
	int	n = 0;
	enum spec	s;

	if ((cp = strrchr(a->file, '/')) == NULL)
		cp = a->file;
	else
		cp++;
	a->base = strdup(cp);
	if ((cp = strrchr(a->base, '.')) != NULL)
		*cp = '\0';
	if (dev.allpunct)
		a->spec |= SPEC_PUNCT;
	if (a->base[0]=='S' && a->base[1]==0)
		a->spec |= SPEC_S;
	if (a->base[0]=='S' && a->base[1]=='1' && a->base[2]==0)
		a->spec |= SPEC_S1;
	s = a->spec;
	a->xheight = 500;
	a->capheight = 700;
	unitsPerEm = 1000;
	if (memcmp(contents, "OTTO", 4) == 0 ||
			memcmp(contents, "\0\1\0\0", 4) == 0 ||
			memcmp(contents, "true", 4) == 0)
		return otfget(a, contents, size);
	a->lineno = 1;
	for (cp = contents; cp < &contents[size]; a->lineno++, cp++) {
		while (*cp == ' ' || *cp == '\t' || *cp == '\r')
			cp++;
		if (*cp == '\n')
			continue;
		if (thisword(cp, "Comment"))
			/*EMPTY*/;
		else if (state == NONE && thisword(cp, "StartFontMetrics"))
			state = FONTMETRICS;
		else if (state == FONTMETRICS && thisword(cp, "EndFontMetrics"))
			state = NONE;
		else if (state == FONTMETRICS &&
				(th = thisword(cp, "FontName")) != NULL) {
			for (tp = th; *tp && *tp != ' ' && *tp != '\t' &&
					*tp != '\n' && *tp != '\r'; tp++);
			a->fontname = malloc(tp - th + 1);
			memcpy(a->fontname, th, tp - th);
			a->fontname[tp - th] = 0;
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "IsFixedPitch")) != NULL) {
			a->isFixedPitch = strncmp(th, "true", 4) == 0;
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "XHeight")) != NULL) {
			a->xheight = strtol(th, NULL, 10);
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "CapHeight")) != NULL) {
			a->capheight = strtol(th, NULL, 10);
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "Ascender")) != NULL) {
			a->ascender = strtol(th, NULL, 10);
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "Descender")) != NULL) {
			a->descender = strtol(th, NULL, 10);
		} else if (state == FONTMETRICS &&
				(th = thisword(cp, "StartCharMetrics")) != 0) {
			n = strtol(th, NULL, 10);
			state = CHARMETRICS;
			afmalloc(a, n);
		} else if (state == CHARMETRICS &&
				thisword(cp, "EndCharMetrics")) {
			state = FONTMETRICS;
			afmremap(a);
		} else if (state == CHARMETRICS && n-- > 0) {
			addmetrics(a, cp, s);
#ifndef	DPOST
		} else if (state == FONTMETRICS &&
				thisword(cp, "StartKernData") != 0) {
			state = KERNDATA;
		} else if (state == KERNDATA &&
				(th = thisword(cp, "StartKernPairs")) != 0) {
			n = strtol(th, NULL, 10);
			state = KERNPAIRS;
		} else if (state == KERNPAIRS &&
				thisword(cp, "EndKernPairs")) {
			state = KERNDATA;
		} else if (state == KERNPAIRS && n-- > 0) {
			addkernpair(a, cp);
		} else if (state == KERNDATA &&
				thisword(cp, "EndKernData")) {
			state = FONTMETRICS;
#endif	/* !DPOST */
		}
		while (cp < &contents[size] && *cp != '\n')
			cp++;
	}
	if (a->fontname == NULL) {
		errprint("Missing \"FontName\" in %s", a->path);
		return -1;
	}
	a->Font.nwfont = a->nchars > 255 ? 255 : a->nchars;
	return 0;
}

/*
 * This is for legacy font support. It exists at this place because both
 * troff and dpost need it in combination with AFM support.
 */
void
makefont(int nf, const char *devfontab, const char *devkerntab,
    const char *devcodetab, const char *devfitab, int nw)
{
	int	i;

	free(fontab[nf]);
	free(kerntab[nf]);
	free(codetab[nf]);
	free(fitab[nf]);
	fontab[nf] = calloc(nw, sizeof *fontab);
	kerntab[nf] = calloc(nw, sizeof *kerntab);
	codetab[nf] = calloc(nw, sizeof *codetab);
	fitab[nf] = calloc(NCHARS ? NCHARS : 128 - 32 + nchtab, sizeof *fitab);
	if (devfontab) for (i = 0; i < nw; i++)
		fontab[nf][i] = devfontab[i]&0377;
	if (devkerntab) for (i = 0; i < nw; i++)
		kerntab[nf][i] = devkerntab[i]&0377;
	if (devcodetab) for (i = 0; i < nw; i++)
		codetab[nf][i] = devcodetab[i]&0377;
	if (devfitab) for (i = 0; i < 128 - 32 + nchtab; i++)
		fitab[nf][i] = devfitab[i]&0377;
	fontbase[nf]->spacewidth = fontab[nf][0];
	fontbase[nf]->cspacewidth = -1;
}

#ifndef	DPOST
/*
 * For short to medium-sized documents, the run time is dominated by
 * the time required to read kerning pairs for fonts with many pairs.
 * Kerning pairs are thus simply dumped in the order they occur at
 * this point. Later when a kerning pair is actually looked up, the
 * structures accessed are sorted (eliding duplicates), leading to
 * better performance with large documents.
 */
void
afmaddkernpair(struct afmtab *a, int ch1, int ch2, int k)
{
	struct kernpairs	*kp;

	if (k == 0)
		return;
	if (a->kernpairs == NULL)
		a->kernpairs = calloc(a->fichars, sizeof *a->kernpairs);
	kp = &a->kernpairs[ch1];
	while (kp->cnt == NKERNPAIRS) {
		if (kp->next == NULL)
			kp->next = calloc(1, sizeof *kp->next);
		kp = kp->next;
	}
	kp->ch2[kp->cnt] = ch2;
	kp->k[kp->cnt] = k;
	kp->cnt++;
}

static void
addkernpair(struct afmtab *a, char *_line)
{
	struct namecache	*np1, *np2;
	char	*lp = _line, c, *cp;
	int	n, i, j;

	if (lp[0] == 'K' && lp[1] == 'P') {
		lp += 2;
		if (*lp == 'X')
			lp++;
		while ((*lp && *lp == ' ') || *lp == '\t')
			lp++;
		cp = lp;
		while (*lp && *lp != '\n' && *lp != '\r' &&
					*lp != ' ' && *lp != '\t')
			lp++;
		if ((c = *lp) == 0)
			return;
		*lp = 0;
		np1 = afmnamelook(a, cp);
		*lp = c;
		while ((*lp && *lp == ' ') || *lp == '\t')
			lp++;
		cp = lp;
		while (*lp && *lp != '\n' && *lp != '\r' &&
				*lp != ' ' && *lp != '\t')
			lp++;
		if ((c = *lp) == 0)
			return;
		*lp = 0;
		np2 = afmnamelook(a, cp);
		*lp = c;
		n = _unitconv(strtol(&lp[1], NULL, 10));
		for (i = 0; i < 2; i++)
			if (np1->fival[i] != NOCODE)
				for (j = 0; j < 2; j++)
					if (np2->fival[j] != NOCODE)
						afmaddkernpair(a,
								np1->fival[i],
								np2->fival[j],
								n);
	}
}

static void
sortkernpairs(struct kernpairs *kp)
{
	int	i, j, s, t;
	do {
		s = 0;
		for (i = 0; i < kp->cnt-1; i++)
			if (kp->ch2[i] > kp->ch2[i+1]) {
				t = kp->ch2[i];
				kp->ch2[i] = kp->ch2[i+1];
				kp->ch2[i+1] = t;
				t = kp->k[i];
				kp->k[i] = kp->k[i+1];
				kp->k[i+1] = t;
				s = 1;
			}
	} while (s);
	for (j = 0; j < kp->cnt-1; j++)
		if (kp->ch2[j] == kp->ch2[j+1]) {
			for (i = j+1; i < kp->cnt-1; i++) {
				kp->ch2[i] = kp->ch2[i+1];
				kp->k[i] = kp->k[i+1];
			}
			kp->cnt--;
			j--;
		}
	kp->sorted = 1;
}

int
afmgetkern(struct afmtab *a, int ch1, int ch2)
{
	struct kernpairs	*kp;
	int	l, m, r;

	if (a->kernpairs) {
		kp = &a->kernpairs[ch1];
		do {
			if (kp->sorted == 0)
				sortkernpairs(kp);
			r = kp->cnt - 1;
			if (ch2 >= kp->ch2[0] && r >= 0 && ch2 <= kp->ch2[r]) {
				l = 0;
				do {
					m = (l+r) / 2;
					if (ch2 < kp->ch2[m])
						r = m-1;
					else
						l = m+1;
				} while (ch2 != kp->ch2[m] && l <= r);
				if (kp->ch2[m] == ch2)
					return kp->k[m];
			}
		} while ((kp = kp->next) != NULL);
	}
	return 0;
}
#endif	/* !DPOST */

char *
afmencodepath(const char *cp)
{
	const char	hex[] = "0123456789ABCDEF";
	char	*enc, *ep;

	ep = enc = malloc(3 * strlen(cp) + 1);
	while (*cp) {
		if (*cp&0200 || *cp <= 040 || *cp == 0177) {
			*ep++ = '%';
			*ep++ = hex[(*cp&0360) >> 4];
			*ep++ = hex[*cp++&017];
		} else
			*ep++ = *cp++;
	}
	*ep = 0;
	return enc;
}

static int
unhex(int c)
{
	if (c >= 'A' && c <= 'F')
		return c - 'A';
	if (c >= 'a' && c <= 'f')
		return c - 'a';
	return c - '0';
}

static int
xdigit(int c)
{
	return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') ||
		(c >= '0' && c <= '9');
}

char *
afmdecodepath(const char *cp)
{
	char	*dec, *dp;

	dec = dp = malloc(strlen(cp) + 1);
	while (*cp) {
		if (cp[0] == '%' && xdigit(cp[1]&0377) && xdigit(cp[2]&0377)) {
			*dp++ = unhex(cp[1]) << 4 | unhex(cp[2]);
			cp += 3;
		} else
			*dp++ = *cp++;
	}
	*dp = 0;
	return dec;
}
#endif	/* !DUMP */
