wdvi

network DVI viewer
Log | Files | Refs

font-open.c (38928B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 1990-2013  Paul Vojta
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining a copy
      6 of this software and associated documentation files (the "Software"), to
      7 deal in the Software without restriction, including without limitation the
      8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      9 sell copies of the Software, and to permit persons to whom the Software is
     10 furnished to do so, subject to the following conditions:
     11 
     12 The above copyright notice and this permission notice shall be included in
     13 all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18 IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF OR CONTRIBUTOR TO
     19 THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     22 IN THE SOFTWARE.
     23 
     24 NOTE:
     25 	xdvi is based on prior work, as noted in the modification history
     26 	in xdvi.c.
     27 
     28 \*========================================================================*/
     29 
     30 #include <err.h>
     31 #include <errno.h>
     32 #include <stdlib.h>	/* free(), atoi() */
     33 
     34 #include "events.h"	/* ev_flags */
     35 #include "font.h"	/* struct avl_t1 */
     36 #include "filf-app.h"	/* application-related defs, etc. */
     37 #include "filefind.h"
     38 #include "font-open.h"
     39 #include "util.h"	/* xmalloc(), xmemdup(), avladd(), ... */
     40 
     41 
     42 /*
     43  *	If you think you have to change DEFAULT_TAIL, then you haven't read the
     44  *	documentation closely enough.
     45  */
     46 
     47 #define	PATH_SEP	':'
     48 #define	DEFAULT_TAIL	"/%f.%d%p"
     49 #define	DEFAULT_VF_TAIL	"/%f.vf"
     50 
     51 #define DEFAULT_FONT_PATH	".:%S"	/* font searching */
     52 #define DEFAULT_VF_PATH		".:%S"	/* virtual-font searching */
     53 #define DEFAULT_DVIPS_CF_PATH	".:%S"	/* dvips config file searching */
     54 #define DEFAULT_FONTMAP_PATH	".:%S"	/* dvips-style fontmap searching */
     55 #define DEFAULT_ENC_PATH	".:%S"	/* encoding file searching */
     56 #define DEFAULT_TYPE1_PATH	".:%S"	/* Type 1 font file searching */
     57 
     58 /*
     59  * gs -h | sed \
     60  *     -e '1,/Search path:/d' \
     61  *     -e '/For more information/,$d' \
     62  *     -e '/Initialization files are compiled/d' \
     63  *     -e 's/$/\/\/\//' \
     64  *     -e 's/^   //' \
     65  *   | tr '\n' '/'
     66  */
     67 #define DEFAULT_GS_LIB_PATH "/usr/local/share/ghostscript/9.07/Resource/Init:/usr/local/share/ghostscript/9.07/lib:/usr/local/share/ghostscript/9.07/Resource/Font:/usr/local/share/fonts/ghostscript"
     68 
     69 #ifndef DEFAULT_FONT_SIZES
     70 	/* default sizes for current dpi */
     71 #define	DEFAULT_FONT_SIZES	"m0:m0.5:m1:m2:m3:m4:m5"
     72 #endif
     73 
     74 /*
     75  *	Information on how to search for pk and gf files.
     76  */
     77 
     78 static	const	char	no_f_str_pkgf[]	= DEFAULT_TAIL;
     79 
     80 static	struct findrec			search_pkgf	= {
     81 	/* path1	*/	NULL,
     82 	/* path2	*/	DEFAULT_FONT_PATH,
     83 	/* type		*/	"font",
     84 	/* fF_etc	*/	"fFdbpm",
     85 	/* x_var_char	*/	'd',
     86 	/* n_var_opts	*/	3,
     87 	/* no_f_str	*/	no_f_str_pkgf,
     88 	/* no_f_str_end	*/	no_f_str_pkgf + sizeof(no_f_str_pkgf) - 1,
     89 	/* abs_str	*/	"%f.%d%p",
     90 	/* pct_s_str	*/	"%qfonts/%p/%m//:%qfonts/%p/modeless//",
     91 	{
     92 	  /* v.stephead		*/	NULL,
     93 	  /* v.pct_s_head	*/	NULL,
     94 	  /* v.pct_s_count	*/	0,
     95 	  /* v.pct_s_atom	*/	NULL,
     96 	  /* v.rootp		*/	NULL,
     97 	}
     98 };
     99 
    100 /*
    101  *	Information on how to search for vf files.
    102  */
    103 
    104 static	const	char	no_f_str_vf[]	= DEFAULT_VF_TAIL;
    105 
    106 static	struct findrec			search_vf	= {
    107 	/* path1	*/	NULL,
    108 	/* path2	*/	DEFAULT_VF_PATH,
    109 	/* type		*/	"vf",
    110 	/* fF_etc	*/	"fF",
    111 	/* x_var_char	*/	'f',		/* i.e., none */
    112 	/* n_var_opts	*/	2,
    113 	/* no_f_str	*/	no_f_str_vf,
    114 	/* no_f_str_end	*/	no_f_str_vf + sizeof(no_f_str_vf) - 1,
    115 	/* abs_str	*/	"%f.vf",
    116 	/* pct_s_str	*/	"%qfonts/vf//",
    117 	{
    118 	  /* v.stephead		*/	NULL,
    119 	  /* v.pct_s_head	*/	NULL,
    120 	  /* v.pct_s_count	*/	0,
    121 	  /* v.pct_s_atom	*/	NULL,
    122 	  /* v.rootp		*/	NULL,
    123 	}
    124 };
    125 
    126 static	int	*sizes, *sizend;
    127 static	char	default_size_list[]	= DEFAULT_FONT_SIZES;
    128 
    129 static	char		bdpi_string[10];
    130 static	char		dpi_string[10];
    131 
    132 static	double		magsteps[10]	= {1.0, 1.2, 1.44, 1.728, 2.0736,
    133 					   2.48832, 2.985984, 3.5831808,
    134 					   4.29981696, 5.159780352};
    135 
    136 
    137 /*
    138  *	Information on how to search for dvips configuration files.
    139  */
    140 
    141 static	const	char	no_f_str_dvips_cf[]	= "/%f";
    142 
    143 static	struct findrec			search_dvips_cf	= {
    144 	/* path1	*/	NULL,
    145 	/* path2	*/	DEFAULT_DVIPS_CF_PATH,
    146 	/* type		*/	"dvips config",
    147 	/* fF_etc	*/	"fF",
    148 	/* x_var_char	*/	'f',		/* i.e., none */
    149 	/* n_var_opts	*/	2,
    150 	/* no_f_str	*/	no_f_str_dvips_cf,
    151 	/* no_f_str_end	*/	no_f_str_dvips_cf + sizeof no_f_str_dvips_cf -1,
    152 	/* abs_str	*/	"%f",
    153 	/* pct_s_str	*/	"%qdvips//",
    154 	{
    155 	  /* v.stephead		*/	NULL,
    156 	  /* v.pct_s_head	*/	NULL,
    157 	  /* v.pct_s_count	*/	0,
    158 	  /* v.pct_s_atom	*/	NULL,
    159 	  /* v.rootp		*/	NULL,
    160 	}
    161 };
    162 
    163 /*
    164  *	Information on how to search for (dvips-style) map files.
    165  */
    166 
    167 static	const	char	no_f_str_fontmap[]	= "/%f";
    168 
    169 static	struct findrec			search_fontmap	= {
    170 	/* path1	*/	NULL,
    171 	/* path2	*/	DEFAULT_FONTMAP_PATH,
    172 	/* type		*/	"font map",
    173 	/* fF_etc	*/	"fF",
    174 	/* x_var_char	*/	'f',		/* i.e., none */
    175 	/* n_var_opts	*/	2,
    176 	/* no_f_str	*/	no_f_str_fontmap,
    177 	/* no_f_str_end	*/	no_f_str_fontmap + sizeof no_f_str_fontmap -1,
    178 	/* abs_str	*/	"%f",
    179 	/* pct_s_str	*/    "%qfonts/map/{xdvi,dvips,pdftex}//:%qfonts/map//",
    180 	{
    181 	  /* v.stephead		*/	NULL,
    182 	  /* v.pct_s_head	*/	NULL,
    183 	  /* v.pct_s_count	*/	0,
    184 	  /* v.pct_s_atom	*/	NULL,
    185 	  /* v.rootp		*/	NULL,
    186 	}
    187 };
    188 
    189 /*
    190  *	Information on how to search for font encoding files.
    191  */
    192 
    193 static	const	char	no_f_str_enc[]	= "/%f";
    194 
    195 static	struct findrec			search_enc	= {
    196 	/* path1	*/	no_f_str_enc,	/* flag value:  uninitialized */
    197 	/* path2	*/	DEFAULT_ENC_PATH,
    198 	/* type		*/	"font encoding",
    199 	/* fF_etc	*/	"fF",
    200 	/* x_var_char	*/	'f',		/* i.e., none */
    201 	/* n_var_opts	*/	2,
    202 	/* no_f_str	*/	no_f_str_enc,
    203 	/* no_f_str_end	*/	no_f_str_enc + sizeof no_f_str_enc -1,
    204 	/* abs_str	*/	"%f",
    205 	/* pct_s_str	*/	"%qfonts/enc//",
    206 	{
    207 	  /* v.stephead		*/	NULL,
    208 	  /* v.pct_s_head	*/	NULL,
    209 	  /* v.pct_s_count	*/	0,
    210 	  /* v.pct_s_atom	*/	NULL,
    211 	  /* v.rootp		*/	NULL,
    212 	}
    213 };
    214 
    215 /*
    216  *	Information on how to search for Type 1 fonts.
    217  */
    218 
    219 static	const	char	no_f_str_type1[]	= "/%f";
    220 
    221 static	struct findrec			search_type1	= {
    222 	/* path1	*/	no_f_str_type1,	/* flag value:  uninitialized */
    223 	/* path2	*/	DEFAULT_TYPE1_PATH,
    224 	/* type		*/	"Type 1 font",
    225 	/* fF_etc	*/	"fF",
    226 	/* x_var_char	*/	'f',
    227 	/* n_var_opts	*/	2,
    228 	/* no_f_str	*/	no_f_str_type1,
    229 	/* no_f_str_end	*/	no_f_str_type1 + sizeof(no_f_str_type1) - 1,
    230 	/* abs_str	*/	"%f",
    231 	/* pct_s_str	*/	"%qfonts/type1//",
    232 	{
    233 	  /* v.stephead		*/	NULL,
    234 	  /* v.pct_s_head	*/	NULL,
    235 	  /* v.pct_s_count	*/	0,
    236 	  /* v.pct_s_atom	*/	NULL,
    237 	  /* v.rootp		*/	NULL,
    238 	}
    239 };
    240 
    241 
    242 
    243 static	int
    244 atosize(p)
    245 	const	char	*p;
    246 {
    247 	const	char	*q;
    248 	Boolean		minus;
    249 	double		factor;
    250 
    251 	if (*p != 'm')
    252 	    return atoi(p);
    253 
    254 	for (q = "agstep";; ++q) {
    255 	    ++p;
    256 	    if (*q == '\0' || *p != *q) break;
    257 	}
    258 
    259 	minus = False;
    260 	if (*p == '-') {
    261 	    minus = True;
    262 	    ++p;
    263 	}
    264 
    265 	if (*p < '0' || *p > '9')
    266 	    return 0;
    267 	factor = magsteps[*p - '0'];
    268 	++p;
    269 
    270 	if (*p == '.' && p[1] == '5')
    271 	    factor *= 1.0954451150103321;	/* sqrt(1.2) */
    272 
    273 	return (int)
    274 	  ((minus ? pixels_per_inch / factor : pixels_per_inch * factor) + 0.5);
    275 }
    276 
    277 static	void
    278 get_sizes(size_list, spp)
    279 	char	*size_list;
    280 	int	**spp;
    281 {
    282 	if (*size_list == PATH_SEP) ++size_list;
    283 	for (;;) {
    284 	    *(*spp)++ = atosize(size_list);
    285 	    size_list = index(size_list, PATH_SEP);
    286 	    if (size_list == NULL) return;
    287 	    ++size_list;
    288 	}
    289 }
    290 
    291 
    292 
    293 /*
    294  *	The following code handles lookup of Type 1 fonts for use as FreeType
    295  *	fonts, and for use within MetaPost output.
    296  *	The system psfonts.map file is read into an AVL tree as needed (lazy
    297  *	evaluation), and searched for Type 1 fonts.
    298  */
    299 
    300 struct p_list {					/* list of map file names */
    301 	struct p_list	*next;
    302 	const char	*value;
    303 };
    304 
    305 	/* Initialize this list to "psfonts.map".  */
    306 
    307 static	struct p_list	psfonts_map		= {NULL, "psfonts.map"};
    308 
    309 static	struct p_list	*p_head			= &psfonts_map;
    310 static	struct p_list	**p_tail		= &psfonts_map.next;
    311 
    312 static	FILE		*mapfile		= NULL;
    313 
    314 static	Boolean
    315 fgets_long(f)
    316 	FILE	*f;
    317 {
    318 	int	len;
    319 
    320 	if (fgets(ffline, ffline_len, f) == NULL)
    321 	    return False;
    322 
    323 	len = 0;
    324 	for (;;) {
    325 	    len += strlen(ffline + len);
    326 	    if (len > 0 && ffline[len - 1] == '\n') {
    327 		ffline[--len] = '\0';
    328 		break;
    329 	    }
    330 	    if (len < ffline_len - 1)
    331 		break;
    332 	    expandline(len);
    333 	    fgets(ffline + len, ffline_len - len, f);
    334 	}
    335 
    336 	return True;
    337 }
    338 
    339 /*
    340  *	Get list of map files from dvips config file(s).
    341  */
    342 
    343 static	void
    344 getdefaults(f)
    345 	FILE	*f;
    346 {
    347 	char		*p, *q;
    348 	int		len;
    349 	struct p_list	*p_node;
    350 
    351 	while (fgets_long(f)) {
    352 	    p = ffline;
    353 	    while (*p == ' ' || *p == '\t') ++p;
    354 	    if (*p == 'p') {
    355 		do ++p;
    356 		while (*p == ' ' || *p == '\t');
    357 
    358 		if (*p == '+')
    359 		    do ++p;
    360 		    while (*p == ' ' || *p == '\t');
    361 		else {	/* discard old list */
    362 		    struct p_list *pl, *pl2;
    363 
    364 		    *p_tail = NULL;
    365 		    pl = p_head;
    366 		    if (pl == &psfonts_map) pl = pl->next;
    367 		    while (pl != NULL) {
    368 			free((char *) pl->value);
    369 			pl2 = pl->next;
    370 			free(pl);
    371 			pl = pl2;
    372 		    }
    373 		    p_tail = &p_head;
    374 		}
    375 
    376 		/* get white-delimited argument */
    377 		len = strlen(p);
    378 		q = memchr(p, ' ', len);
    379 		if (q != NULL) len = q - p;
    380 		q = memchr(p, '\t', len);
    381 		if (q != NULL) len = q - p;
    382 		p[len] = '\0';
    383 
    384 		p_node = xmalloc(sizeof *p_node);
    385 		p_node->value = xmemdup(p, len + 1);
    386 		*p_tail = p_node;
    387 		p_tail = &p_node->next;
    388 	    }
    389 	}
    390 
    391 	fclose(f);
    392 }
    393 
    394 
    395 /*
    396  *	Information about Type 1 fonts is stored in an AVL tree.
    397  */
    398 
    399 static	struct avl_t1	*t1_head	= NULL;
    400 
    401 
    402 /*
    403  *	Parse line from psfonts.map file.
    404  */
    405 
    406 static struct avl_t1 *
    407 dvips_parse(line)
    408 	const char	*line;
    409 {
    410 	const char	*w1p, *encp, *pfp, *qp;
    411 	size_t		w1l, encl, pfl, ql;
    412 	const char	*w2p;
    413 	size_t		w2l;
    414 	const char	*p, *p0;
    415 	const char	*err;
    416 	struct avl_t1	*t1p;
    417 	char		*q;
    418 
    419 	w2l = w1l = encl = pfl = ql = 0;
    420 	err = NULL;
    421 	p = line;
    422 	for (;;) {	/* loop over words */
    423 	    while (*p == ' ' || *p == '\t') ++p;
    424 	    if (*p == '\0')
    425 		break;
    426 
    427 	    if (*p == '"') {	/* quoted string */
    428 		const char *p_end;
    429 
    430 		p0 = p + 1;
    431 		p_end = p0 + strlen(p0);
    432 		p = memchr(p0, '"', p_end - p0);
    433 		if (p == NULL) p = p_end;
    434 		qp = (ql == 0 ? p0 : NULL);
    435 		ql += p - p0 + 1;
    436 		if (*p == '"') ++p;
    437 		continue;
    438 	    }
    439 
    440 	    if (*p == '<') {	/* encoding or pfa/b file */
    441 		int	wtype	= 0;
    442 
    443 		++p;
    444 		if (*p == '<') {
    445 		    wtype = 1;	/* font file */
    446 		    ++p;
    447 		}
    448 		else if (*p == '[') {
    449 		    wtype = -1;	/* encoding file */
    450 		    ++p;
    451 		}
    452 
    453 		/* find word */
    454 		while (*p == ' ' || *p == '\t') ++p;
    455 		p0 = p;
    456 		while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
    457 
    458 		if (wtype == 0 && p > p0 + 4 && p[-4] == '.') {
    459 		    if (memcmp(p - 3, "enc", 3) == 0
    460 		      || memcmp(p - 3, "ENC", 3) == 0)
    461 			wtype = -1;
    462 		    else if (memcmp(p - 3, "pfa", 3) == 0
    463 		      || memcmp(p - 3, "pfb", 3) == 0
    464 		      || memcmp(p - 3, "PFA", 3) == 0
    465 		      || memcmp(p - 3, "PFB", 3) == 0)
    466 			wtype = 1;
    467 		}
    468 
    469 		if (wtype > 0) {
    470 		    if (pfl != 0)
    471 			err = "more than one font file given";
    472 		    else {
    473 			pfp = p0;
    474 			pfl = p - p0 + 1;
    475 		    }
    476 		}
    477 		else if (wtype < 0) {
    478 		    if (encl != 0)
    479 			err = "more than one encoding file given";
    480 		    else {
    481 			encp = p0;
    482 			encl = p - p0 + 1;
    483 		    }
    484 		}
    485 		else
    486 		    err = "cannot identify file type";
    487 	    }
    488 	    else {	/* if ordinary word */
    489 		p0 = p;
    490 		while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
    491 		if (w1l == 0) {
    492 		    w1p = p0;
    493 		    w1l = p - p0;
    494 		}
    495 		else if (w2l == 0) {
    496 		    w2p = p0;
    497 		    w2l = p - p0;
    498 		}
    499 		else
    500 		    err = "more than two non-download words given";
    501 	    }
    502 	}	/* end loop over words */
    503 
    504 	if (w1l == 0) {
    505 	    if (debug & DBG_OPEN)
    506 		warnx("map file '%s': line '%s' does not give a font name.",
    507 		  p_head->value, line);
    508 	    return NULL;
    509 	}
    510 
    511 	if (err != NULL) {
    512 	    if (debug & DBG_OPEN)
    513 		warnx("map file '%s', font '%.*s': '%s'", p_head->value,
    514 		    (int) w1l, w1p, err);
    515 	    return NULL;
    516 	}
    517 
    518 	t1p = (struct avl_t1 *) avladd(w1p, w1l, (struct avl **) &t1_head,
    519 	  sizeof(struct avl_t1));
    520 
    521 	if (t1p->key != w1p) {	/* if existing record */
    522 	    if (debug & DBG_OPEN)
    523 		warnx("map file '%s', font '%.*s': duplicate record; using first one",
    524 		  p_head->value, (int) w1l, w1p);
    525 	    return NULL;
    526 	}
    527 
    528 	t1p->key = q = xmalloc(w1l + w2l + 1 + pfl + encl + ql);
    529 
    530 	memcpy(q, w1p, w1l);
    531 	q += w1l;
    532 	t1p->psname = t1p->key;
    533 	if (w2l != 0) {
    534 	    t1p->psname = q;
    535 	    memcpy(q, w2p, w2l);
    536 	    q += w2l;
    537 	}
    538 	*q++ = '\0';
    539 
    540 	t1p->fontfile = NULL;
    541 	if (pfl != 0) {
    542 	    t1p->fontfile = q;
    543 	    memcpy(q, pfp, pfl - 1);
    544 	    q += pfl;
    545 	    q[-1] = '\0';
    546 	}
    547 
    548 	t1p->encname = t1p->addinfo = NULL;
    549 	t1p->bad = False;
    550 	t1p->ft = NULL;
    551 
    552 	if (encl != 0) {
    553 	    t1p->encname = q;
    554 	    memcpy(q, encp, encl - 1);
    555 	    q += encl;
    556 	    q[-1] = '\0';
    557 	}
    558 
    559 	if (ql != 0) {
    560 	    t1p->addinfo = q;
    561 	    if (qp != 0) {
    562 		memcpy(q, qp, ql - 1);
    563 		q += ql;
    564 	    }
    565 	    else {	/* multiple quoted strings; rescan to get them */
    566 		const char	*p_end;
    567 
    568 		p = line;
    569 		p_end = p + strlen(p);
    570 		for (;;) {
    571 		    while (*p == ' ' || *p == '\t') ++p;
    572 		    if (*p == '\0')
    573 			break;
    574 
    575 		    /* found a word */
    576 		    if (*p == '"') {
    577 			++p;
    578 			p0 = p;
    579 			p = memchr(p0, '"', p_end - p0);
    580 			if (p == NULL) p = p_end;
    581 			memcpy(q, p0, p - p0);
    582 			q += p - p0;
    583 			*q++ = ' ';
    584 			if (*p == '\0') break;
    585 			++p;
    586 		    }
    587 		    else	/* skip unquoted word */
    588 			while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
    589 		}
    590 	    }
    591 	    q[-1] = '\0';
    592 	}
    593 
    594 	return t1p;
    595 }
    596 
    597 /*
    598  *	Information on the Ghostscript font aliasing mechanism is kept in
    599  *	another AVL tree.  Each identifier points to a list of strings,
    600  *	up to one from each Fontmap file.  Within a Fontmap file, later entries
    601  *	for a given string override earlier ones.
    602  */
    603 
    604 struct avl_gs {		/* structure for gs font information */
    605 	AVL_COMMON;
    606 	short		fontmap_number;	/* sequence of most recent entry */
    607 	Boolean		in_use;
    608 	struct gs_list	*list;
    609 };
    610 
    611 struct gs_list {	/* list of Fontmap entries for this font name */
    612 	struct gs_list	*next;
    613 	const char	value[0];
    614 };
    615 
    616 static	struct avl_gs	*gs_head	= NULL;
    617 
    618 typedef	void	(*gs_path_proc)(FILE *);
    619 
    620 static	const char	*env_gs_lib;
    621 
    622 static FILE *
    623 gs_try_fopen(str, len, name)
    624 	const char	*str;
    625 	unsigned int	len;
    626 	const char	*name;
    627 {
    628 	unsigned int	namelen	= strlen(name) + 1;
    629 	FILE		*f;
    630 
    631 	if (len + namelen + 1 > ffline_len) expandline(len + namelen + 1);
    632 	memcpy(ffline, str, len);
    633 	ffline[len] = '/';
    634 	memcpy(ffline + len + 1, name, namelen);
    635 
    636 	f = xfopen(ffline, "r");
    637 
    638 	if (debug & DBG_OPEN)
    639 	    printf("gs_try_fopen: %s: %s\n", ffline,
    640 	      f != NULL ? "file opened" : strerror(errno));
    641 
    642 	return f;
    643 }
    644 
    645 static FILE *
    646 gs_path_fopen(name, proc)
    647 	const char	*name;
    648 	gs_path_proc	proc;
    649 {
    650 	const	char	*str1	= env_gs_lib;
    651 	const	char	*str2	= DEFAULT_GS_LIB_PATH;
    652 	const	char	*str1_end, *str2_end;
    653 	const char	*p1, *p2;
    654 	FILE		*f;
    655 
    656 	if (str1 == NULL) {
    657 	    str1 = str2;
    658 	    str2 = NULL;
    659 	}
    660 
    661 	str1_end = str1 + strlen(str1);
    662 
    663 	for (;;) {
    664 	    p1 = memchr(str1, ':', str1_end - str1);
    665 	    if (p1 == NULL) p1 = str1_end;
    666 	    if (p1 == str1) {
    667 		if (str2 != NULL) {
    668 		    str2_end = str2 + strlen(str2);
    669 		    for (;;) {
    670 			p2 = memchr(str2, ':', str2_end - str2);
    671 			if (p2 == NULL) p2 = str2_end;
    672 			if (p2 > str2) {
    673 			    f = gs_try_fopen(str2, p2 - str2, name);
    674 			    if (f != NULL) {
    675 				if (proc != NULL)
    676 				    proc(f);
    677 				else
    678 				    return f;
    679 			    }
    680 			}
    681 			if (*p2 == '\0')
    682 			    break;
    683 			str2 = p2 + 1;
    684 		    }
    685 		    str2 = NULL;
    686 		}
    687 	    }
    688 	    else {
    689 		f = gs_try_fopen(str1, p1 - str1, name);
    690 		if (f != NULL) {
    691 		    if (proc != NULL)
    692 			proc(f);
    693 		    else
    694 			return f;
    695 		}
    696 	    }
    697 
    698 	    if (*p1 == '\0')
    699 		break;
    700 	    str1 = p1 + 1;
    701 	}
    702 
    703 	return NULL;
    704 }
    705 
    706 
    707 static FILE *
    708 lookup_gs_font(font, path_ret)
    709 	const char	*font;
    710 	const char	**path_ret;
    711 {
    712 	struct avl_gs	*gsfp;
    713 	int		font_len;
    714 	int		i;
    715 
    716 	font_len = strlen(font);
    717 	gsfp = gs_head;
    718 	for (;;) {
    719 	    if (gsfp == NULL)		/* if not found */
    720 		return NULL;
    721 
    722 	    i = font_len - gsfp->key_len;
    723 	    if (i == 0)
    724 		i = memcmp(font, gsfp->key, font_len);
    725 	    if (i == 0) {		/* if found */
    726 		struct gs_list	*gl, *gl2, *gl3;
    727 		FILE		*f;
    728 
    729 		if (gsfp->in_use) {
    730 		    if (debug & DBG_OPEN)
    731 			printf("Alias loop for %s detected; ignoring.\n", font);
    732 		    return NULL;
    733 		}
    734 
    735 		/* If we haven't done it yet, reverse the linked list */
    736 		if (gsfp->fontmap_number != 0) {
    737 		    gsfp->fontmap_number = 0;
    738 		    gl = gsfp->list;
    739 		    gl2 = NULL;
    740 		    while (gl != NULL) {
    741 			gl3 = gl->next;
    742 			gl->next = gl2;
    743 			gl2 = gl;
    744 			gl = gl3;
    745 		    }
    746 		    gsfp->list = gl2;
    747 		}
    748 		gsfp->in_use = True;
    749 		f = NULL;
    750 		for (gl = gsfp->list; gl != NULL; gl = gl->next) {
    751 		    if (gl->value[0] == '\0') {	/* if alias */
    752 			if (debug & DBG_OPEN)
    753 			    printf("Found alias %s --> %s\n", font,
    754 			      gl->value + 1);
    755 			f = lookup_gs_font(gl->value + 1, path_ret);
    756 			if (debug & DBG_OPEN)
    757 			    if (f == NULL)
    758 				printf("Alias %s not found.\n", gl->value + 1);
    759 			if (f != NULL) break;
    760 		    }
    761 		    else {
    762 			if (debug & DBG_OPEN)
    763 			    printf("Checking file %s\n", gl->value);
    764 			if (gl->value[0] == '/' || (gl->value[0] == '.'
    765 			  && (gl->value[1] == '/' || (gl->value[1] == '.'
    766 			  && gl->value[2] == '/')))) {
    767 			    f = xfopen(gl->value, "r");
    768 			    if (f != NULL) {
    769 				*path_ret = xstrdup(gl->value);
    770 				break;
    771 			    }
    772 			}
    773 			else {
    774 			    f = gs_path_fopen(gl->value, NULL);
    775 			    if (f != NULL) {
    776 				*path_ret = xstrdup(ffline);
    777 				break;
    778 			    }
    779 			}
    780 		    }
    781 		}
    782 		gsfp->in_use = False;
    783 		return f;
    784 	    }
    785 	    gsfp = (struct avl_gs *) (i < 0 ? gsfp->left : gsfp->right);
    786 	}
    787 }
    788 
    789 
    790 #define	GS_BUF_SIZE	4096
    791 
    792 struct gsfile {
    793 	FILE			*f;
    794 	unsigned char		*buffer;
    795 	const unsigned char	*bufpos;
    796 	const unsigned char	*buf_end;
    797 };
    798 
    799 static Boolean
    800 gs_fillbuf(gsf)
    801 	struct gsfile	*gsf;
    802 {
    803 	unsigned char	*p;
    804 	unsigned int	len;
    805 
    806 	if (gsf->buf_end < gsf->buffer + GS_BUF_SIZE)
    807 	    return False;
    808 
    809 	gsf->bufpos = p = gsf->buffer;
    810 	for (;;) {
    811 	    len = gsf->buf_end - p;
    812 	    if (len <= 0) break;
    813 	    len = fread(p, 1, len, gsf->f);
    814 	    if (len <= 0) break;
    815 	    p += len;
    816 	}
    817 	gsf->buf_end = p;
    818 	return (p > gsf->buffer);
    819 }
    820 
    821 static	unsigned char	gs_ctype[256];
    822 
    823 #define	GS_EOF	'\0'
    824 #define	GS_ERR	'%'
    825 #define	LPAREN	'('
    826 #define	RPAREN	')'
    827 
    828 static void
    829 init_gs_ctype()
    830 {
    831 	const char	*p;
    832 
    833 	for (p = " \t\f\n\r";; ++p) {
    834 	    gs_ctype[(unsigned char) *p] = 1;	/* white space */
    835 	    if (*p == '\0') break;
    836 	}
    837 	gs_ctype['/'] = 2;		/* literal token */
    838 	gs_ctype['('] = 3;		/* string */
    839 	for (p = ")<>[]{}%"; *p != '\0'; ++p)
    840 	    gs_ctype[(unsigned char) *p] = 4;	/* delimiter */
    841 }
    842 
    843 static unsigned char
    844 get_gs_character(gsfp)
    845 	struct gsfile	*gsfp;
    846 {
    847 	unsigned char		c;
    848 
    849 	for (;;) {
    850 	    if (gsfp->bufpos >= gsfp->buf_end && !gs_fillbuf(gsfp))
    851 		return '%';
    852 	    c = *gsfp->bufpos++;
    853 
    854 	    /* Check for comments */
    855 	    if (c == '%') {
    856 		for (;;) {
    857 		    const unsigned char *p1, *p2;
    858 
    859 		    p1 = memchr(gsfp->bufpos, '\n',
    860 		      gsfp->buf_end - gsfp->bufpos);
    861 		    if (p1 == NULL) p1 = gsfp->buf_end;
    862 		    p2 = memchr(gsfp->bufpos, '\r', p1 - gsfp->bufpos);
    863 		    if (p2 != NULL) p1 = p2;
    864 		    p2 = memchr(gsfp->bufpos, '\f', p1 - gsfp->bufpos);
    865 		    if (p2 != NULL) p1 = p2;
    866 		    if (p1 < gsfp->buf_end) {
    867 			gsfp->bufpos = p1 + 1;
    868 			break;
    869 		    }
    870 		    if (!gs_fillbuf(gsfp))
    871 			return '%';
    872 		}
    873 		continue;
    874 	    }
    875 
    876 	    if (gs_ctype[c] != 1)
    877 		break;
    878 	}
    879 
    880 	return c;
    881 }
    882 
    883 static unsigned char
    884 get_gs_token(gsfp, pos, pos_ret, file_type)
    885 	struct gsfile	*gsfp;
    886 	unsigned int	pos;
    887 	unsigned int	*pos_ret;
    888 	const char	*file_type;
    889 {
    890 	unsigned char		gs_t_type;
    891 	unsigned char		c;
    892 	const unsigned char	*p0;
    893 	unsigned int		pos0;
    894 	unsigned int		depth;
    895 
    896 	gs_t_type = c = get_gs_character(gsfp);
    897 	if (c == '%')
    898 	    return GS_EOF;
    899 
    900 	p0 = gsfp->bufpos;
    901 	switch (gs_ctype[c]) {
    902 	    case 0:	/* most characters */
    903 	    case 2:	/* '/' */
    904 		--p0;	/* retain initial character */
    905 		pos0 = pos;
    906 		for (;;) {
    907 		    if (gsfp->bufpos >= gsfp->buf_end) {
    908 			unsigned int	len	= gsfp->bufpos - p0;
    909 
    910 			if (pos + len >= ffline_len) expandline(pos + len);
    911 			bcopy(p0, ffline + pos, len);
    912 			pos += len;
    913 			if (!gs_fillbuf(gsfp))
    914 			    break;
    915 			p0 = gsfp->buffer;
    916 		    }
    917 		    if (gs_ctype[*gsfp->bufpos] != 0) {
    918 			unsigned int	len	= gsfp->bufpos - p0;
    919 
    920 			if (pos + len >= ffline_len) expandline(pos + len);
    921 			bcopy(p0, ffline + pos, len);
    922 			pos += len;
    923 			break;
    924 		    }
    925 		    ++gsfp->bufpos;
    926 		}
    927 		/* Filter out DOS ^Z */
    928 		if (pos == pos0 + 1 && ffline[pos0] == '\032')
    929 		    return GS_EOF;
    930 		break;
    931 
    932 	    case 3:	/* left parenthesis */
    933 		depth = 1;
    934 		for (;;) {
    935 		    const unsigned char *p1, *p2, *p3;
    936 
    937 		    if (gsfp->bufpos >= gsfp->buf_end) {
    938 			unsigned int	len	= gsfp->bufpos - p0;
    939 
    940 			if (pos + len >= ffline_len) expandline(pos + len);
    941 			bcopy(p0, ffline + pos, len);
    942 			pos += len;
    943 			if (!gs_fillbuf(gsfp)) {
    944 			    warnx("unterminated string in '%s' file; giving up." ,
    945 			      file_type);
    946 			    return GS_ERR;
    947 			}
    948 			p0 = gsfp->buffer;
    949 		    }
    950 		    p1 = memchr(gsfp->bufpos, RPAREN,
    951 		      gsfp->buf_end - gsfp->bufpos);
    952 		    if (p1 == NULL) p1 = gsfp->buf_end;
    953 		    for (;;) {
    954 			p2 = memchr(gsfp->bufpos, LPAREN, p1 - gsfp->bufpos);
    955 			if (p2 == NULL) p2 = p1;
    956 			p3 = p2;
    957 			for (;;) {
    958 			    if (p3 <= gsfp->bufpos) {
    959 				if (c == '\\') --p3;
    960 				break;
    961 			    }
    962 			    if (p3[-1] != '\\') break;
    963 			    --p3;
    964 			}
    965 			c = '\\' - 1 + ((p2 - p3) & 1);
    966 			if (p2 >= p1)
    967 			    break;
    968 			if (c != '\\')
    969 			    ++depth;
    970 			gsfp->bufpos = p2 + 1;
    971 			c = '\0';
    972 		    }
    973 		    if (p1 < gsfp->buf_end) {	/* if left parenthesis at p1 */
    974 			if (c != '\\') {
    975 			    if (--depth == 0) {
    976 				unsigned int	len	= p1 - p0;
    977 
    978 				if (pos + len >= ffline_len)
    979 				    expandline(pos + len);
    980 				bcopy(p0, ffline + pos, len);
    981 				pos += len;
    982 				gsfp->bufpos = p1 + 1;
    983 				break;
    984 			    }
    985 			}
    986 			++p1;
    987 		    }
    988 		    gsfp->bufpos = p1;
    989 		}
    990 		/* We could do backslash escaping here, but it's probably
    991 		   unnecessary.  */
    992 		break;
    993 
    994 	    default:
    995 		warnx("invalid character `%c' encountered in '%s' file; giving up.",
    996 		  c, file_type);
    997 		return GS_ERR;
    998 	}
    999 
   1000 	*pos_ret = pos;
   1001 	return gs_t_type;
   1002 }
   1003 
   1004 
   1005 static	short		gs_fontmap_number	= 0;
   1006 
   1007 static void
   1008 process_gs_fontmap(FILE *f)
   1009 {
   1010 	struct gsfile	gsf;
   1011 	unsigned char	buffer[GS_BUF_SIZE];
   1012 	unsigned char	ttype;
   1013 	unsigned int	pos1, pos2, pos3;
   1014 
   1015 	++gs_fontmap_number;
   1016 
   1017 	gsf.f = f;
   1018 	gsf.buffer = buffer;
   1019 	gsf.bufpos = gsf.buf_end = buffer + GS_BUF_SIZE;
   1020 
   1021 	/*
   1022 	 * Allow entries of the following types:
   1023 	 *
   1024 	 *	(string) .runlibfile
   1025 	 *	/identifier (string) ;
   1026 	 *	/identifier /alias ;
   1027 	 */
   1028 
   1029 	for (;;) {
   1030 	    ttype = get_gs_token(&gsf, 0, &pos1, "Fontmap");
   1031 	    if (ttype == GS_EOF || ttype == GS_ERR)
   1032 		break;
   1033 	    if (ttype == LPAREN) {
   1034 		FILE	*f1;
   1035 
   1036 		ttype = get_gs_token(&gsf, pos1, &pos2, "Fontmap");
   1037 		if (ttype == GS_ERR)
   1038 		    break;
   1039 		if (ttype == GS_EOF) {
   1040 		    warnx("unexpected end of Fontmap file; giving up.");
   1041 		    break;
   1042 		}
   1043 		if (ttype != '.' || pos2 - pos1 != 11
   1044 		  || memcmp(ffline + pos1, ".runlibfile", 11) != 0) {
   1045 		    warnx("invalid token following '(%.*s)' in Fontmap file; giving up.",
   1046 		      (int) pos1, ffline);
   1047 		    break;
   1048 		}
   1049 
   1050 		ffline[pos1] = '\0';
   1051 		if (ffline[0] == '/' || (ffline[0] == '.' && (ffline[1] == '/'
   1052 		  || (ffline[1] == '.' && ffline[2] == '/'))))
   1053 		    f1 = xfopen(ffline, "r");
   1054 		else {
   1055 		    char *q;
   1056 
   1057 		    q = xmemdup(ffline, pos1 + 1);
   1058 		    f1 = gs_path_fopen(q, NULL);
   1059 		    free(q);
   1060 		}
   1061 
   1062 		if (f1 == NULL)
   1063 		    warn("Fontmap .runlibfile: '%s'", ffline);
   1064 		else {
   1065 		    --gs_fontmap_number;
   1066 		    process_gs_fontmap(f1);
   1067 		}
   1068 	    }
   1069 	    else if (ttype == '/') {
   1070 		struct avl_gs	*gsfp;
   1071 		struct gs_list	*gslp;
   1072 
   1073 		ttype = get_gs_token(&gsf, pos1, &pos2, "Fontmap");
   1074 		if (ttype == GS_ERR)
   1075 		    break;
   1076 		if (ttype == GS_EOF) {
   1077 		    warnx("unexpected end of Fontmap file; giving up.");
   1078 		    break;
   1079 		}
   1080 		if ((ttype != '/' && ttype != LPAREN)
   1081 		  || pos2 == pos1	/* empty string would mess things up */
   1082 		  || get_gs_token(&gsf, pos2, &pos3, "Fontmap") != ';'
   1083 		  || pos3 != pos2 + 1) {
   1084 		    warnx("invalid token following '%.*s' in Fontmap file; giving up.",
   1085 		      (int) pos1, ffline);
   1086 		    break;
   1087 		}
   1088 		if (ttype == '/')
   1089 		    ffline[pos1] = '\0';	/* mark aliases by initial \0 */
   1090 		ffline[pos2++] = '\0';		/* terminate string */
   1091 
   1092 		/* Add to database */
   1093 		gsfp = (struct avl_gs *) avladd(ffline + 1, pos1 - 1,
   1094 		  (struct avl **) &gs_head, sizeof *gsfp);
   1095 
   1096 		if (gsfp->key == ffline + 1) {	/* if new record */
   1097 		    gsfp->key = xmemdup(ffline + 1, pos1 - 1);
   1098 		    gsfp->in_use = False;
   1099 		    gsfp->list = NULL;
   1100 		}
   1101 		else {
   1102 		    if (strlen(gsfp->list->value + 1) + 2 == pos2 - pos1
   1103 		      && memcmp(gsfp->list->value, ffline + pos1, pos2 - pos1)
   1104 		      == 0)
   1105 			continue;	/* ignore duplicate entry */
   1106 		    if (gsfp->fontmap_number == gs_fontmap_number) {
   1107 			/* Later entries in a Fontmap file override earlier
   1108 			   ones */
   1109 			gslp = gsfp->list;
   1110 			gsfp->list = gslp->next;
   1111 			free(gslp);
   1112 		    }
   1113 		}
   1114 		gslp = xmalloc(sizeof *gslp + pos2 - pos1);
   1115 		gslp->next = gsfp->list;
   1116 		gsfp->list = gslp;
   1117 		memcpy((char *) gslp->value, ffline + pos1, pos2 - pos1);
   1118 
   1119 		gsfp->fontmap_number = gs_fontmap_number;
   1120 	    }
   1121 	    else {
   1122 		warnx("invalid token '%s' in Fontmap file; giving up.",
   1123 		  ffline);
   1124 	    }
   1125 	}
   1126 
   1127 	fclose(f);
   1128 }
   1129 
   1130 /*
   1131  *	Read Ghostscript Fontmap files.  These are used if the line in
   1132  *	psfonts.map does not contain a filename for the font.
   1133  *
   1134  *	For example:
   1135  *		n019003l NimbusSanL-Regu
   1136  */
   1137 
   1138 static void
   1139 read_gs_fontmaps()
   1140 {
   1141 	env_gs_lib = getenv("XDVI_GS_LIB");
   1142 	if (env_gs_lib == NULL)
   1143 	    env_gs_lib = getenv("GS_LIB");
   1144 
   1145 	if (gs_ctype[0] == 0)
   1146 	    init_gs_ctype();
   1147 
   1148 	(void) gs_path_fopen("Fontmap", process_gs_fontmap);
   1149 }
   1150 
   1151 
   1152 /*
   1153  *	pre_lookup_t1_font - Find a Type 1 font (or return NULL).
   1154  */
   1155 
   1156 static struct avl_t1 *
   1157 pre_lookup_t1_font(fontname)
   1158 	const char	*fontname;
   1159 {
   1160 	struct avl_t1	*t1p;
   1161 	size_t		len;
   1162 	int		i;
   1163 
   1164 	/* first, search for the font */
   1165 
   1166 	len = strlen(fontname);
   1167 	t1p = t1_head;
   1168 	while (t1p != NULL) {
   1169 	    i = len - t1p->key_len;
   1170 	    if (i == 0)
   1171 		i = memcmp(fontname, t1p->key, len);
   1172 	    if (i == 0)
   1173 		return t1p;	/* found it */
   1174 	    t1p = (struct avl_t1 *) (i < 0 ? t1p->left : t1p->right);
   1175 	}
   1176 
   1177 	/* next, read in more records in hopes of finding the font */
   1178 
   1179 	if (p_head != NULL)
   1180 	    for (;;) {
   1181 		if (!fgets_long(mapfile)) {	/* if end of file */
   1182 		    fclose(mapfile);
   1183 		    for (;;) {
   1184 			p_head = p_head->next;
   1185 			if (p_head == NULL)
   1186 			    return NULL;
   1187 			mapfile = filefind(p_head->value, &search_fontmap,
   1188 			  NULL);
   1189 			if (mapfile != NULL) {
   1190 			    if (debug & DBG_OPEN)
   1191 				printf("Map file: %s\n", p_head->value);
   1192 			    break;
   1193 			}
   1194 			warnx("cannot open map file '%s'", p_head->value);
   1195 		    }
   1196 		    continue;
   1197 		}
   1198 
   1199 		if (*ffline < ' ' || *ffline == '*' || *ffline == '#'
   1200 		  || *ffline == ';' || *ffline == '%')
   1201 		    continue;
   1202 
   1203 		t1p = dvips_parse(ffline);
   1204 
   1205 		if (t1p != NULL && t1p->key_len == len
   1206 		  && memcmp(t1p->key, fontname, len) == 0)
   1207 		    return t1p;	/* found it */
   1208 	    }
   1209 
   1210 	return NULL;
   1211 }
   1212 
   1213 /*
   1214  *	lookup_t1_font - Find a Type 1 font (or return NULL).
   1215  */
   1216 
   1217 
   1218 static struct avl_t1 *
   1219 lookup_t1_font(fontp)
   1220 	struct font	*fontp;
   1221 {
   1222 	struct avl_t1	*t1p;
   1223 	struct ftfont	*ftp;
   1224 
   1225 	t1p = pre_lookup_t1_font(fontp->fontname);
   1226 
   1227 	if (t1p == NULL)
   1228 	    return NULL;
   1229 
   1230 	if (t1p->bad) {
   1231 	    if (debug & DBG_OPEN)
   1232 		printf("Font %s is marked as bad:  skipping scalable version\n",
   1233 		  fontp->fontname);
   1234 	    return NULL;
   1235 	}
   1236 
   1237 	ftp = t1p->ft;
   1238 	if (ftp != NULL) {	/* if it's is already in use at another size */
   1239 	    struct font *first_size;
   1240 
   1241 	    /* The first node in the linked list of sizes contains the file */
   1242 	    /* reference, so we link in the new node after the first node */
   1243 	    first_size = ftp->first_size;
   1244 	    fontp->next_size = first_size->next_size;
   1245 	    first_size->next_size = fontp;
   1246 	}
   1247 	else {	/* first use at this size */
   1248 	    t1p->ft = ftp = xmalloc(sizeof *ftp);
   1249 	    ftp->face = NULL;
   1250 	    ftp->t1 = t1p;
   1251 	    ftp->first_size = fontp;
   1252 	    fontp->next_size = NULL;
   1253 	}
   1254 	fontp->ft = ftp;
   1255 	fontp->size = NULL;
   1256 
   1257 	return t1p;
   1258 }
   1259 
   1260 
   1261 FILE *
   1262 open_t1_font(t1p, path_ret)
   1263 	struct avl_t1	*t1p;
   1264 	const char	**path_ret;
   1265 {
   1266 	FILE		*f;
   1267 
   1268 	/*
   1269 	 * Set up the search paths, if necessary.
   1270 	 */
   1271 
   1272 	if (search_type1.path1 == no_f_str_type1) {	/* if uninitialized */
   1273 	    if ((search_type1.path1 = getenv("XDVIT1FONTS")) == NULL
   1274 	      && (search_type1.path1 = getenv("T1FONTS")) == NULL
   1275 	      && (search_type1.path1 = getenv("T1INPUTS")) == NULL
   1276 	      && (search_type1.path1 = getenv("TEXFONTS")) == NULL
   1277 	      && (search_type1.path1 = getenv("XDVIHEADERS")) == NULL
   1278 	      && (search_type1.path1 = getenv("TEXPSHEADERS")) == NULL
   1279 	      && (search_type1.path1 = getenv("PSHEADERS")) == NULL) {
   1280 		search_type1.path1 = search_type1.path2;
   1281 		search_type1.path2 = NULL;
   1282 	    }
   1283 	}
   1284 
   1285 	if (t1p->fontfile == NULL) {	/* look up in GS Fontmap */
   1286 	    static Boolean gs_fontmap_initialized = False;
   1287 
   1288 	    if (!gs_fontmap_initialized) {
   1289 		read_gs_fontmaps();
   1290 		gs_fontmap_initialized = True;
   1291 	    }
   1292 
   1293 	    if (debug & DBG_OPEN)
   1294 		printf(
   1295 		  "Looking for font %.*s using gs method (PS name %s) --\n",
   1296 		  t1p->key_len, t1p->key, t1p->psname);
   1297 	    f = lookup_gs_font(t1p->psname, path_ret);
   1298 
   1299 	    if (f == NULL) {
   1300 		warnx("cannot find Type 1 font '%s'", t1p->psname);
   1301 		return NULL;
   1302 	    }
   1303 
   1304 	    if (debug & DBG_OPEN)
   1305 		printf("Found file %s\n", *path_ret);
   1306 	}
   1307 	else {
   1308 	    f = filefind(t1p->fontfile, &search_type1, path_ret);
   1309 
   1310 	    if (f == NULL) {
   1311 		warnx("cannot find Type 1 font file '%s' (will try PK version instead)",
   1312 		  t1p->fontfile);
   1313 		return NULL;
   1314 	    }
   1315 	}
   1316 
   1317 	return f;
   1318 }
   1319 
   1320 
   1321 /*
   1322  *	Read the encoding vector file.  This assumes the same format as afm2tfm.
   1323  */
   1324 
   1325 void
   1326 read_encoding(encp)
   1327 	struct avl_enc	*encp;
   1328 {
   1329 	FILE		*f;
   1330 	struct gsfile	gsf;
   1331 	unsigned char	buffer[GS_BUF_SIZE];
   1332 	jmp_buf		err_env;
   1333 	unsigned char	ttype;
   1334 	unsigned int	pos1, pos2;
   1335 	unsigned int	identindex[256];
   1336 	const char	*str;
   1337 	unsigned int	i;
   1338 
   1339 	if (debug & DBG_OPEN)
   1340 	    printf("Reading encoding file %s\n", encp->key);
   1341 
   1342 	encp->valid = False;
   1343 
   1344 	/*
   1345 	 * Set up the search paths, if necessary.
   1346 	 */
   1347 
   1348 	if (search_enc.path1 == no_f_str_enc) {		/* if uninitialized */
   1349 	    if ((search_enc.path1 = getenv("XDVIENCS")) == NULL
   1350 	      && (search_enc.path1 = getenv("ENCFONTS")) == NULL
   1351 	      && (search_enc.path1 = getenv("TEXFONTS")) == NULL) {
   1352 		search_enc.path1 = search_enc.path2;
   1353 		search_enc.path2 = NULL;
   1354 	    }
   1355 	}
   1356 
   1357 	f = filefind(encp->key, &search_enc, NULL);
   1358 	if (f == NULL) {
   1359 	    warnx("cannot find encoding file '%s'; ignoring encoding",
   1360 		encp->key);
   1361 	    return;
   1362 	}
   1363 
   1364 	if (gs_ctype[0] == 0)
   1365 	    init_gs_ctype();
   1366 
   1367 	gsf.f = f;
   1368 	gsf.buffer = buffer;
   1369 	gsf.bufpos = gsf.buf_end = buffer + GS_BUF_SIZE;
   1370 
   1371 	if (!setjmp(err_env)) {
   1372 	    if (get_gs_token(&gsf, 0, &pos1, "encoding") != '/'
   1373 	      || get_gs_character(&gsf) != '[')
   1374 		longjmp(err_env, 1);
   1375 
   1376 	    pos1 = 0;
   1377 	    for (i = 0; i < 256; ++i) {
   1378 		if (get_gs_token(&gsf, pos1, &pos2, "encoding") != '/')
   1379 		    longjmp(err_env, 1);
   1380 		if (pos2 == pos1 + 8
   1381 		  && memcmp(ffline + pos1, "/.notdef", 8) == 0)
   1382 		    identindex[i] = 0;
   1383 		else {
   1384 		    ffline[pos1] = '\0';
   1385 		    identindex[i] = pos1 + 1;
   1386 		    pos1 = pos2;
   1387 		}
   1388 	    }
   1389 
   1390 	    if (get_gs_character(&gsf) != ']')
   1391 		longjmp(err_env, 1);
   1392 
   1393 	    ttype = get_gs_token(&gsf, pos1, &pos2, "encoding");
   1394 	    if (!(ttype == GS_EOF
   1395 	      || (ttype == 'd' && pos2 == pos1 + 3
   1396 	      && memcmp(ffline + pos1, "def", 3) == 0
   1397 	      && get_gs_token(&gsf, pos2, &pos2, "encoding") == GS_EOF)))
   1398 		longjmp(err_env, 1);
   1399 
   1400 	    if (pos1 >= ffline_len) expandline(pos1 + 1);
   1401 	    ffline[pos1] = '\0';
   1402 	    str = xmemdup(ffline + 1, pos1);
   1403 	    for (i = 0; i < 256; ++i)
   1404 		encp->vec[i] =
   1405 		  (identindex[i] != 0 ? str + identindex[i] - 1 : NULL);
   1406 
   1407 	    encp->valid = True;
   1408 	}
   1409 	else	/* if error */
   1410 	    warnx("invalid format in encoding file '%s'; giving up.",
   1411 		encp->key);
   1412 
   1413 	fclose(f);
   1414 }
   1415 
   1416 
   1417 
   1418 void
   1419 init_font_open()
   1420 {
   1421 	char		*size_list;
   1422 	int		*sp, *sp1;
   1423 	unsigned int	n;
   1424 	char		*p;
   1425 	FILE		*f;
   1426 	const char	*dvipsrc;
   1427 
   1428 	sprintf(bdpi_string, "%d", pixels_per_inch);
   1429 
   1430 	if ((search_pkgf.path1 = getenv("XDVIFONTS")) == NULL
   1431 #ifndef	XDVIFONTS_ONLY
   1432 		&& (search_pkgf.path1 = getenv("PKFONTS")) == NULL
   1433 		&& (search_pkgf.path1 = getenv("TEXPKS")) == NULL
   1434 		&& (search_pkgf.path1 = getenv("TEXFONTS")) == NULL
   1435 #endif
   1436 		) {
   1437 	    search_pkgf.path1 = search_pkgf.path2;
   1438 	    search_pkgf.path2 = NULL;
   1439 	}
   1440 
   1441 	/*
   1442 	 * pk/gf searching is the only kind that uses more than three
   1443 	 * characters in fF_etc, so these can be initialized once and then
   1444 	 * forgotten.
   1445 	 */
   1446 
   1447 	fF_values[2] = dpi_string;
   1448 	fF_values[3] = bdpi_string;
   1449 	fF_values[4] = "pk";
   1450 	fF_values[5] = resource.mfmode;
   1451 
   1452 	if ((search_vf.path1 = getenv("XDVIVFS")) == NULL
   1453 #ifndef	XDVIFONTS_ONLY
   1454 		&& (search_vf.path1 = getenv("VFFONTS")) == NULL
   1455 #endif
   1456 		) {
   1457 	    search_vf.path1 = search_vf.path2;
   1458 	    search_vf.path2 = NULL;
   1459 	}
   1460 
   1461 	size_list = getenv("XDVISIZES");
   1462 	n = 1;	/* count number of sizes */
   1463 	if (size_list == NULL || *size_list == '\0' || *size_list == PATH_SEP)
   1464 	    for (p = default_size_list; (p = index(p, PATH_SEP)) != NULL; ++p)
   1465 		++n;
   1466 	if (size_list != NULL)
   1467 	    for (p = size_list; (p = index(p, PATH_SEP)) != NULL; ++p) ++n;
   1468 	sizes = xmalloc(n * sizeof(int));
   1469 	sizend = sizes;	/* get the actual sizes */
   1470 	if (size_list == NULL || *size_list == '\0' || *size_list == PATH_SEP)
   1471 	    get_sizes(default_size_list, &sizend);
   1472 	if (size_list != NULL && *size_list != '\0')
   1473 	    get_sizes(size_list, &sizend);
   1474 
   1475 	/* sort the sizes (insertion sort) */
   1476 	for (sp = sizes + 1; sp < sizend; ++sp)
   1477 	    if (*sp < sp[-1]) {
   1478 		int	i	= *sp;
   1479 
   1480 		sp1 = sp;
   1481 		do
   1482 		    *sp1 = sp1[-1];
   1483 		while (--sp1 > sizes && i < sp1[-1]);
   1484 		*sp1 = i;
   1485 	    }
   1486 
   1487 	/* eliminate duplicates and erroneous values */
   1488 	n = 0;	/* previous value */
   1489 	for (sp = sp1 = sizes; sp < sizend; ++sp)
   1490 	    if (*sp != n)
   1491 		n = *sp1++ = *sp;
   1492 	sizend = sp1;
   1493 
   1494 	/*
   1495 	 * Initialize reading of Type 1 fonts and map files
   1496 	 * (PS uses T1 fonts for MetaPost figures).
   1497 	 */
   1498 
   1499 	if ((search_dvips_cf.path1 = getenv("XDVITYPE1CONFIG")) == NULL
   1500 	  && (search_dvips_cf.path1 = getenv("TEXCONFIG")) == NULL) {
   1501 	    search_dvips_cf.path1 = search_dvips_cf.path2;
   1502 	    search_dvips_cf.path2 = NULL;
   1503 	}
   1504 
   1505 	if ((search_fontmap.path1 = getenv("XDVIFONTMAPS")) == NULL
   1506 	  && (search_fontmap.path1 = getenv("TEXFONTMAPS")) == NULL
   1507 	  && (search_fontmap.path1 = getenv("TEXFONTS")) == NULL) {
   1508 	    search_fontmap.path1 = search_fontmap.path2;
   1509 	    search_fontmap.path2 = NULL;
   1510 	}
   1511 
   1512 	if (ffline == NULL) expandline(80);
   1513 
   1514 	f = filefind("config.ps", &search_dvips_cf, NULL);
   1515 	if (f != NULL)
   1516 	    getdefaults(f);
   1517 
   1518 	dvipsrc = getenv("DVIPSRC");
   1519 	if (dvipsrc == NULL) {
   1520 	    dvipsrc = getenv("HOME");
   1521 	    if (dvipsrc != NULL) {
   1522 		n = strlen(dvipsrc);
   1523 		if (n + 10 > ffline_len) expandline(n + 10);
   1524 		memcpy(ffline, dvipsrc, n);
   1525 		memcpy(ffline + n, "/.dvipsrc", 10);
   1526 		dvipsrc = ffline;
   1527 	    }
   1528 	}
   1529 	if (dvipsrc != NULL) {
   1530 	    f = xfopen(dvipsrc, "r");
   1531 	    if (f != NULL)
   1532 		getdefaults(f);
   1533 	}
   1534 
   1535 	f = filefind("config.xdvi", &search_dvips_cf, NULL);
   1536 	if (f != NULL)
   1537 	    getdefaults(f);
   1538 
   1539 	*p_tail = NULL;
   1540 
   1541 	/*
   1542 	 * Look for the first (openable) map file.  Others will be read later
   1543 	 * as needed.
   1544 	 */
   1545 
   1546 	while (p_head != NULL) {
   1547 	    mapfile = filefind(p_head->value, &search_fontmap, NULL);
   1548 	    if (mapfile != NULL) {
   1549 		if (debug & DBG_OPEN)
   1550 		    printf("Map file: %s\n", p_head->value);
   1551 		break;
   1552 	    }
   1553 	    warnx("Cannot open map file '%s'", p_head->value);
   1554 	    p_head = p_head->next;
   1555 	}
   1556 }
   1557 
   1558 /*
   1559  *	Try a given size.
   1560  */
   1561 
   1562 static	FILE *
   1563 try_size(font, dpi, ret_path)
   1564 	const char	*font;
   1565 	int		dpi;
   1566 	const char	**ret_path;
   1567 {
   1568 	sprintf(dpi_string, "%d", dpi);
   1569 
   1570 	return filefind(font, &search_pkgf, ret_path);
   1571 }
   1572 
   1573 
   1574 /*
   1575  *	Try a given font name
   1576  */
   1577 
   1578 static	Boolean
   1579 pre_font_open(struct font *fontp, const char *fontname, double fdpi,
   1580 	const char **name_ret, int *dpi_ret)
   1581 {
   1582 	FILE	*f;
   1583 	int	*p1, *p2;
   1584 	int	dpi;
   1585 	int	tempdpi;
   1586 
   1587 	*dpi_ret = dpi = fdpi + 0.5;
   1588 
   1589 	/*
   1590 	 * First look for a scalable font.
   1591 	 */
   1592 
   1593 	struct avl_t1 *t1p = lookup_t1_font(fontp);
   1594 
   1595 	if (t1p != NULL) {	/* if font found */
   1596 		if (debug & DBG_OPEN)
   1597 			printf("lookup_t1_font(%s) --> found %s\n",
   1598 				fontp->fontname, t1p->fontfile != NULL ? 
   1599 				t1p->fontfile : t1p->psname);
   1600 
   1601 		return True;
   1602 	}
   1603 
   1604 	if (debug & DBG_OPEN)
   1605 		printf("lookup_t1_font(%s) --> not found\n", fontp->fontname);
   1606 
   1607 	/*
   1608 	 * Loop over sizes.  Try actual size first, then closest sizes.
   1609 	 * If the pathname is absolutely or explicitly relative, don't
   1610 	 * use the usual paths to search for it; just look for it in the
   1611 	 * directory specified.
   1612 	 */
   1613 
   1614 	f = try_size(fontname, dpi, name_ret);
   1615 	if (f != NULL) {
   1616 	    fontp->file = f;
   1617 	    return True;
   1618 	}
   1619 
   1620 	/* Try at one away from the size we just tried, to account
   1621 	   for rounding error.  */
   1622 	tempdpi = dpi + (dpi < fdpi ? 1 : -1);
   1623 	f = try_size(fontname, tempdpi, name_ret);
   1624 	if (f != NULL) {
   1625 	    fontp->file = f;
   1626 	    *dpi_ret = tempdpi;
   1627 	    return True;
   1628 	}
   1629 
   1630 	/* Try a virtual font. */
   1631 	f = filefind(fontname, &search_vf, name_ret);
   1632 	if (f != NULL) {
   1633 	    fontp->file = f;
   1634 	    return True;
   1635 	}
   1636 
   1637 	/* Now try at all the sizes. */
   1638 	for (p2 = sizes; p2 < sizend; ++p2) if (*p2 >= dpi) break;
   1639 	p1 = p2;
   1640 	for (;;) {
   1641 		/* find another resolution */
   1642 	    if (p1 <= sizes)
   1643 		if (p2 >= sizend) return False;
   1644 		else tempdpi = *p2++;
   1645 	    else if (p2 >= sizend || (long) dpi * dpi <= (long) p1[-1] * *p2)
   1646 		    tempdpi = *--p1;
   1647 		else tempdpi = *p2++;
   1648 	    f = try_size(fontname, *dpi_ret = tempdpi, name_ret);
   1649 	    if (f != NULL) {
   1650 		fontp->file = f;
   1651 		return True;
   1652 	    }
   1653 	}
   1654 }
   1655 
   1656 /*
   1657  *	font_open()
   1658  *
   1659  *	This is the publicly accessible routine.  It uses the following fields
   1660  *	in the font structure:
   1661  *		fontname
   1662  *		magstepval
   1663  *		fsize
   1664  *		filename
   1665  *		ft
   1666  *		next_size
   1667  *		size
   1668  */
   1669 
   1670 Boolean
   1671 font_open(fontp, font_ret, dpi_ret)
   1672 	struct font	*fontp;
   1673 	char		**font_ret;
   1674 	int		*dpi_ret;
   1675 {
   1676 	Boolean	val;
   1677 	int	actual_pt, low_pt, high_pt, trial_pt;
   1678 	char	*fn, *fnend;
   1679 
   1680 	val = pre_font_open(fontp, fontp->fontname, fontp->fsize,
   1681 	  &fontp->filename, dpi_ret);
   1682 	if (val || ev_flags & EV_GE_NEWDOC) {
   1683 	    *font_ret = NULL;
   1684 	    return val;
   1685 	}
   1686 
   1687 	fn = xmalloc(strlen(fontp->fontname) + 2);
   1688 	strcpy(fn, fontp->fontname);
   1689 	fnend = fn + strlen(fn);
   1690 	while (fnend > fn && fnend[-1] >= '0' && fnend[-1] <= '9') --fnend;
   1691 	actual_pt = low_pt = high_pt = atoi(fnend);
   1692 	if (actual_pt) {
   1693 	    low_pt = actual_pt - 1;
   1694 	    high_pt = actual_pt + 1;
   1695 	    for (;;) {
   1696 		if (2 * low_pt >= actual_pt &&
   1697 		    (low_pt * high_pt > actual_pt * actual_pt ||
   1698 		    high_pt > actual_pt + 5))
   1699 			trial_pt = low_pt--;
   1700 		else if (high_pt > actual_pt + 5) break;
   1701 		else trial_pt = high_pt++;
   1702 		sprintf(fnend, "%d", trial_pt);
   1703 		if (pre_font_open(fontp, fn,
   1704 		  fontp->fsize * actual_pt / trial_pt,
   1705 		  &fontp->filename, dpi_ret)) {
   1706 		    *font_ret = fn;
   1707 		    return True;
   1708 		}
   1709 	    }
   1710 	}
   1711 	free(fn);
   1712 
   1713 	if (resource._alt_font != NULL) {
   1714 	    if (pre_font_open(fontp, resource._alt_font, fontp->fsize,
   1715 	      &fontp->filename, dpi_ret)) {
   1716 		*font_ret = xstrdup(resource._alt_font);
   1717 		return True;
   1718 	    }
   1719 	}
   1720 	return False;
   1721 }