1 /*
2   font-open.c: find font files.  The routine font_open() itself bears
3   no relation (except for the interface) to the original font_open().
4 
5   Copyright (c) 1999-2013  The texk project
6 
7   Permission is hereby granted, free of charge, to any person obtaining a copy
8   of this software and associated documentation files (the "Software"), to
9   deal in the Software without restriction, including without limitation the
10   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11   sell copies of the Software, and to permit persons to whom the Software is
12   furnished to do so, subject to the following conditions:
13 
14   The above copyright notice and this permission notice shall be included in
15   all copies or substantial portions of the Software.
16 
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20   IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
21   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 */
26 
27 #include "xdvi-config.h"
28 #include "xdvi.h"
29 #include "dvi-draw.h"
30 #ifdef PTEX
31 #include "ptexmap.h"
32 #endif
33 #include "util.h"
34 #include "events.h"
35 #include "dvi-init.h"
36 #include "my-snprintf.h"
37 #include "print-log.h"
38 
39 #include "statusline.h"
40 #include "message-window.h"
41 #include "font-open.h"
42 
43 #include "kpathsea/c-fopen.h"
44 #include "kpathsea/tex-glyph.h"
45 
46 #include <stdlib.h>
47 #include <ctype.h>
48 
49 #if HAVE_SYS_WAIT_H
50 # include <sys/wait.h>
51 #endif
52 #ifndef WIFEXITED
53 # define WIFEXITED(status)	(((status) & 255) == 0)
54 #endif
55 #ifndef WEXITSTATUS
56 # define WEXITSTATUS(status)	((unsigned)(status) >> 8)
57 #endif
58 #ifndef WIFSIGNALED
59 # ifndef WIFSTOPPED
60 #  define WIFSTOPPED(status)	(((status) & 0xff) == 0x7f)
61 # endif
62 # define WIFSIGNALED(status)	(!WIFSTOPPED(status) && !WIFEXITED(status))
63 #endif
64 #ifndef WTERMSIG
65 # define WTERMSIG(status)	((status) & 0x7f)
66 #endif
67 
68 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
69 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
70 # define O_NONBLOCK O_NDELAY
71 #endif
72 
73 #ifdef EWOULDBLOCK
74 # ifdef EAGAIN
75 #  define AGAIN_CONDITION	(errno == EWOULDBLOCK || errno == EAGAIN)
76 # else
77 #  define AGAIN_CONDITION	(errno == EWOULDBLOCK)
78 # endif
79 #else /* EWOULDBLOCK */
80 # ifdef EAGAIN
81 #  define AGAIN_CONDITION	(errno == EAGAIN)
82 # endif
83 #endif /* EWOULDBLOCK */
84 
85 #if HAVE_POLL
86 # include <poll.h>
87 # define XIO_IN POLLIN
88 # define XIO_OUT POLLOUT
89 #else
90 # define XIO_IN 1
91 # define XIO_OUT 2
92 #endif /* HAVE_POLL */
93 
94 
95 	/* see just above the second init_t1_lookup for why this was chosen */
96 #define DVIPS_SETUP 1
97 
98 #if FREETYPE || PS
99 
100 /*
101  *	The following code handles lookup of Type 1 fonts for use as FreeType
102  *	fonts, and for use within MetaPost output.
103  *	The system psfonts.map file is read into an AVL tree as needed (lazy
104  *	evaluation), and searched for Type 1 fonts.
105  */
106 
107 struct p_list {					/* list of map file names */
108 	struct p_list	*next;
109 	const char	*value;
110 };
111 
112 	/* Initialize this list to "psfonts.map".  */
113 
114 static	struct p_list	psfonts_map		= {NULL, "psfonts.map"};
115 
116 static	struct p_list	*p_head			= &psfonts_map;
117 static	struct p_list	**p_tail		= &psfonts_map.next;
118 
119 static	FILE		*mapfile		= NULL;
120 
121 static	char	*ffline	= NULL;		/* an array used to store */
122 					/* the file name being formed.  */
123 					/* It expands as needed. */
124 					/* Also used elsewhere.  */
125 static	size_t	ffline_len = 0;		/* current length of ffline[] */
126 
127 /*
128  *	Expand ffline[] to at least the given size.
129  */
130 
131 static void
expandline(int n)132 expandline(int n)
133 {
134 	int	newlen	= n + 128;
135 
136 	ffline = (ffline == NULL) ? xmalloc(newlen) : xrealloc(ffline, newlen);
137 	ffline_len = newlen;
138 }
139 
140 
141 /*
142  *	Like fgets(), but read an arbitrarily long line into ffline[].
143  */
144 
145 static Boolean
fgets_long(FILE * f)146 fgets_long(FILE *f)
147 {
148 	int	len;
149 
150 	if (fgets(ffline, ffline_len, f) == NULL)
151 	    return False;
152 
153 	len = 0;
154 	for (;;) {
155 	    len += strlen(ffline + len);
156 	    if (len > 0 && ffline[len - 1] == '\n') {
157 		ffline[--len] = '\0';
158 		break;
159 	    }
160 	    if (len < ffline_len - 1)
161 		break;
162 	    expandline(len);
163 	    fgets(ffline + len, ffline_len - len, f);
164 	}
165 
166 	return True;
167 }
168 
169 # if DVIPS_SETUP
170 
171 /*
172  *	Get list of map files from dvips config file(s).
173  */
174 
175 static void
getdefaults(FILE * f)176 getdefaults(FILE *f)
177 {
178 	char		*p, *q;
179 	int		len;
180 	struct p_list	*p_node;
181 
182 	while (fgets_long(f)) {
183 	    p = ffline;
184 	    while (*p == ' ' || *p == '\t') ++p;
185 	    if (*p == 'p') {
186 		do ++p;
187 		while (*p == ' ' || *p == '\t');
188 
189 		if (*p == '+')
190 		    do ++p;
191 		    while (*p == ' ' || *p == '\t');
192 		else {	/* discard old list */
193 		    struct p_list *pl, *pl2;
194 
195 		    *p_tail = NULL;
196 		    pl = p_head;
197 		    if (pl == &psfonts_map) pl = pl->next;
198 		    while (pl != NULL) {
199 			free((char *) pl->value);
200 			pl2 = pl->next;
201 			free(pl);
202 			pl = pl2;
203 		    }
204 		    p_tail = &p_head;
205 		}
206 
207 		/* get white-delimited argument */
208 		len = strlen(p);
209 		q = memchr(p, ' ', len);
210 		if (q != NULL) len = q - p;
211 		q = memchr(p, '\t', len);
212 		if (q != NULL) len = q - p;
213 		p[len] = '\0';
214 
215 		p_node = xmalloc(sizeof *p_node);
216 		p_node->value = xmemdup(p, len + 1);
217 		*p_tail = p_node;
218 		p_tail = &p_node->next;
219 	    } else if (*p == 'x') {
220 		char *name;
221 
222 		name = NULL;
223 		do ++p;
224 		while (*p == ' ' || *p == '\t');
225 
226 		if (*p == '+')
227 		    do ++p;
228 		    while (*p == ' ' || *p == '\t');
229 
230 		name = strtok(p, " \t");
231 		if (name == NULL) {
232 		    XDVI_WARNING((stderr, "Syntax error in entry \"%s\" "
233 			"(ignored)", ffline));
234 		    continue;
235 		}
236 		read_ptexmap_file(name);
237 	    } else if (*p == 'r') {
238 		char *name, *ptr;
239 
240 		name = ptr = NULL;
241 		do ++p;
242 		while (*p == ' ' || *p == '\t');
243 		name = strtok(p, " \t");
244 		if ((name == NULL) ||
245 		    (ptr  = strtok(NULL, " \t")) == NULL) {
246 		    XDVI_WARNING((stderr, "Syntax error in entry \"%s\" "
247 			"(ignored)", ffline));
248 		    continue;
249 		}
250 		add_replace_dictionary(name, ptr);
251 	    }
252 	}
253 
254 	fclose(f);
255 }
256 
257 # endif /* DVIPS_SETUP */
258 
259 /*
260  *	Open the next (available) map file.
261  */
262 
263 static Boolean
open_next_mapfile(void)264 open_next_mapfile(void)
265 {
266 	char	*filename;
267 
268 	/*
269 	 * Look for the first (openable) map file.  Others will be read later
270 	 * as needed.
271 	 */
272 
273 	while (p_head != NULL) {
274 	    filename = kpse_find_file(p_head->value, kpse_program_text_format,
275 	      True);
276 	    if (filename == NULL)
277 		filename = kpse_find_file(p_head->value, kpse_fontmap_format,
278 		  True);
279 	    if (filename == NULL) {
280 		XDVI_WARNING((stderr,
281 		  "could not find dvips map file %s; skipping\n",
282 		  p_head->value));
283 	    }
284 	    else {
285 		mapfile = XFOPEN(filename, OPEN_MODE);
286 		if (mapfile == NULL) {
287 		    XDVI_WARNING((stderr,
288 		      "could not open dvips map file %s: %s\n",
289 		      filename, strerror(errno)));
290 		    free(filename);
291 		}
292 		else {
293 		    TRACE_FT((stderr, "Map file: %s\n", filename));
294 		    free(filename);
295 		    return True;
296 		}
297 	    }
298 	    p_head = p_head->next;
299 	}
300 
301 	return False;
302 }
303 
304 
305 /*
306  *	Initialize these lookup routines.
307  */
308 
309 # if DVIPS_SETUP
310 
311 Boolean
init_t1_lookup(void)312 init_t1_lookup(void)
313 {
314 	char		*filename;
315 	struct stat	statbuf;
316 	FILE		*f;
317 	char		*dvipsrc;
318 
319 	if (ffline == NULL) expandline(80);
320 
321 	filename = find_file("config.ps", &statbuf, kpse_dvips_config_format);
322 	if (filename == NULL) {
323 	    TRACE_FT((stderr, "could not find file config.ps; skipping"));
324 	}
325 	else {
326 	    f = XFOPEN(filename, OPEN_MODE);
327 	    if (f == NULL) {
328 		TRACE_FT((stderr, "could not open file %s: %s",
329 		  filename, strerror(errno)));
330 	    }
331 	    else
332 		getdefaults(f);
333 	    free(filename);
334 	}
335 
336 	dvipsrc = getenv("DVIPSRC");
337 	if (dvipsrc == NULL) {
338 	    dvipsrc = getenv("HOME");
339 	    if (dvipsrc != NULL) {
340 		size_t n;
341 
342 		n = strlen(dvipsrc);
343 		if (n + 10 > ffline_len) expandline(n + 10);
344 		memcpy(ffline, dvipsrc, n);
345 		memcpy(ffline + n, "/.dvipsrc", 10);
346 		dvipsrc = ffline;
347 	    }
348 	}
349 	if (dvipsrc != NULL) {
350 	    f = XFOPEN(dvipsrc, OPEN_MODE);
351 	    if (f == NULL) {
352 		TRACE_FT((stderr,
353 		  "could not open dvipsrc file %s: %s; skipping",
354 		  dvipsrc, strerror(errno)));
355 	    }
356 	    else
357 		getdefaults(f);
358 	}
359 
360 	filename = find_file("config.xdvi", &statbuf, kpse_dvips_config_format);
361 	if (filename == NULL) {
362 	    TRACE_FT((stderr, "could not find file config.xdvi; skipping"));
363 	}
364 	else {
365 	    f = XFOPEN(filename, OPEN_MODE);
366 	    if (f == NULL) {
367 		TRACE_FT((stderr, "could not open file %s: %s",
368 		  filename, strerror(errno)));
369 	    }
370 	    else
371 		getdefaults(f);
372 	    free(filename);
373 	}
374 
375 	*p_tail = NULL;
376 
377 	return open_next_mapfile();
378 }
379 
380 # else /* !DVIPS_SETUP */
381 
382 /*
383  *	Under the earlier regime, T1 lookup was controlled by xdvi.cfg.
384  *
385  *	This has been disabled, for the following reasons.
386  *
387  *	It only supported "enc" and "dvipsmap" directives.
388  *
389  *	The "enc" directives selected implicit encoding files for fonts ending
390  *	in certain character strings (8r, 8c, 8y), unless the map file
391  *	explicitly referenced a .enc file.  However, among the dvips map files
392  *	supplied with TeX Live 2012 (at least) there were no such fonts ending
393  *	in 8r and 8y, and the encoding file for the 8c fonts was cork.enc,
394  *	which is no longer included in TeX Live.
395  *
396  *	As for the dvipsmap directives, it seemed better to use the dvips
397  *	configuration method.
398  */
399 
400 Boolean
init_t1_lookup(void)401 init_t1_lookup(void)
402 {
403 	char *filename;
404 	FILE *fp;
405 	char *keyword;
406 	char *ptr;
407 	struct p_list *p_node;
408 	char *enc;
409 	char *name;
410 	static const char delim[] = "\t \n\r";
411 
412 	if (ffline == NULL) expandline(80);
413 
414 	filename = kpse_find_file("xdvi.cfg", kpse_program_text_format, 1);
415 	if (filename == NULL) {
416 	    statusline_error(STATUS_MEDIUM,
417 	      "Warning: Unable to find \"xdvi.cfg\"!");
418 	    return open_next_mapfile();
419 	}
420 
421 	if ((fp = XFOPEN(filename, "r")) == NULL) {
422 	    XDVI_ERROR((stderr, "Cannot open config file `%s' for reading: %s",
423 	      filename, strerror(errno)));
424 	    free(filename);
425 	    return open_next_mapfile();
426 	}
427 
428 	TRACE_FT((stderr, "Reading cfg file %s", filename));
429 
430 	while (fgets_long(fp)) {
431 	    keyword = ffline;
432 
433 	    /* Skip leading whitespace */
434 	    while (*keyword == ' ' || *keyword == '\t')
435 		keyword++;
436 
437 	    /* % in first column is a correct comment */
438 	    if (*keyword == '%' || *keyword == '\0' || *keyword == '\n')
439 		continue;
440 
441 	    keyword = strtok(keyword, delim);
442 
443 	    if (strcmp(keyword, "dvipsmap") == 0) {
444 		if ((ptr = strtok(NULL, delim)) == NULL) {
445 		    XDVI_WARNING((stderr, "Syntax error in entry \"%s\"",
446 		      ffline));
447 		    continue;
448 		}
449 		TRACE_FT((stderr, "DVIPSMAP: '%s'", ptr));
450 
451 		/* Add it to the list for later reading */
452 		p_node = xmalloc(sizeof *p_node);
453 		p_node->value = xmemdup(ptr, strlen(ptr) + 1);
454 		/* If we're still pointing to the default list, remove it */
455 		if (p_tail == &psfonts_map.next)
456 		    p_tail = &p_head;
457 		*p_tail = p_node;
458 		p_tail = &p_node->next;
459 	    }
460 	    else if (strcmp(keyword, "encmap") == 0) {
461 		popup_message(globals.widgets.top_level,
462 			  MSG_ERR,
463 			  "Your xdvi.cfg file is for a previous version "
464 			  "of xdvik. Please replace it with the xdvi.cfg file "
465 			  "in the current xdvik distribution.",
466 			  "The keyword \"encmap\" in xdvi.cfg is no longer "
467 			  "supported.  Please update the config file %s.",
468 			  filename);
469 	    }
470 	    else if (strcmp(keyword, "enc") == 0) {
471 		enc = strtok(NULL, delim);
472 		name = strtok(NULL, delim);
473 		if ((ptr = strtok(NULL, delim)) == NULL) {
474 		    XDVI_WARNING((stderr,
475 			    "Syntax error in entry \"%s\" (skipping line)",
476 			    ffline));
477 		    continue;
478 		}
479 #  if 0
480 		i = new_encoding(enc, ptr);
481 		TRACE_FT((stderr, "Encoding[%d]: '%s' = '%s' -> '%s'",
482 		  i, enc, name, ptr));
483 #  endif
484 #ifdef PTEX
485 	    }
486 	    else if (strcmp(keyword, "ptexmap") == 0) {
487 		if ((ptr = strtok(NULL, delim)) == NULL) {
488 		    XDVI_WARNING((stderr,
489 			    "Syntax error in entry \"%s\" (skipping line)",
490 			    ffline));
491 		    continue;
492 		}
493 		read_ptexmap_file(ptr);
494 	    }
495 	    else if (strcmp(keyword, "replace") == 0) {
496 		if ((name = strtok(NULL, delim)) == NULL ||
497 		    (ptr  = strtok(NULL, delim)) == NULL) {
498 		    XDVI_WARNING((stderr,
499 			    "Syntax error in entry \"%s\" (skipping line)",
500 			    ffline));
501 		    continue;
502 		}
503 		add_replace_dictionary(name, ptr);
504 #endif
505 	    } else {
506 		/* again, nag them with a popup so that they'll do something
507 		   about this ... */
508 		popup_message(globals.widgets.top_level,
509 			  MSG_ERR,
510 			  "Please check the syntax of your config file.  "
511 #ifdef PTEX
512 			  "Valid keywords are: \"enc\", \"dvipsmap\", \"ptexmap\" and \"replace\".",
513 #else
514 			  "Valid keywords are: \"enc\" and \"dvipsmap\".",
515 #endif
516 			  "Skipping unknown keyword \"%s\" in config file %s.",
517 			  keyword, filename);
518 	    }
519 	}
520 	*p_tail = NULL;	/* terminate linked list of map files */
521 
522 	fclose(fp);
523 	free(filename);
524 
525 	return open_next_mapfile();
526 }
527 
528 # endif /* not DVIPS_SETUP */
529 
530 
531 /*
532  *	Information about Type 1 fonts is stored in an AVL tree.
533  */
534 
535 static	struct avl_t1	*t1_head	= NULL;
536 
537 
538 /*
539  *	Parse line from psfonts.map file.
540  */
541 
542 static struct avl_t1 *
dvips_parse(const char * line)543 dvips_parse(const char *line)
544 {
545 	const char	*w1p, *encp, *pfp, *qp;
546 	size_t		w1l, encl, pfl, ql;
547 	const char	*w2p;
548 	size_t		w2l;
549 	const char	*p, *p0;
550 	const char	*err;
551 	struct avl_t1	*t1p;
552 	char		*q;
553 
554 	w2l = w1l = encl = pfl = ql = 0;
555 	err = NULL;
556 	p = line;
557 	for (;;) {	/* loop over words */
558 	    while (*p == ' ' || *p == '\t') ++p;
559 	    if (*p == '\0')
560 		break;
561 
562 	    if (*p == '"') {	/* quoted string */
563 		const char *p_end;
564 
565 		p0 = p + 1;
566 		p_end = p0 + strlen(p0);
567 		p = memchr(p0, '"', p_end - p0);
568 		if (p == NULL) p = p_end;
569 		qp = (ql == 0 ? p0 : NULL);
570 		ql += p - p0 + 1;
571 		if (*p == '"') ++p;
572 		continue;
573 	    }
574 
575 	    if (*p == '<') {	/* encoding or pfa/b file */
576 		int	wtype	= 0;
577 
578 		++p;
579 		if (*p == '<') {
580 		    wtype = 1;	/* font file */
581 		    ++p;
582 		}
583 		else if (*p == '[') {
584 		    wtype = -1;	/* encoding file */
585 		    ++p;
586 		}
587 
588 		/* find word */
589 		while (*p == ' ' || *p == '\t') ++p;
590 		p0 = p;
591 		while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
592 
593 		if (wtype == 0 && p > p0 + 4 && p[-4] == '.') {
594 		    if (memcmp(p - 3, "enc", 3) == 0
595 		      || memcmp(p - 3, "ENC", 3) == 0)
596 			wtype = -1;
597 		    else if (memcmp(p - 3, "pfa", 3) == 0
598 		      || memcmp(p - 3, "pfb", 3) == 0
599 		      || memcmp(p - 3, "PFA", 3) == 0
600 		      || memcmp(p - 3, "PFB", 3) == 0)
601 			wtype = 1;
602 		}
603 
604 		if (wtype > 0) {
605 		    if (pfl != 0)
606 			err = "more than one font file given";
607 		    else {
608 			pfp = p0;
609 			pfl = p - p0 + 1;
610 		    }
611 		}
612 		else if (wtype < 0) {
613 		    if (encl != 0)
614 			err = "more than one encoding file given";
615 		    else {
616 			encp = p0;
617 			encl = p - p0 + 1;
618 		    }
619 		}
620 		else
621 		    err = "cannot identify file type";
622 	    }
623 	    else {	/* if ordinary word */
624 		p0 = p;
625 		while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
626 		if (w1l == 0) {
627 		    w1p = p0;
628 		    w1l = p - p0;
629 		}
630 		else if (w2l == 0) {
631 		    w2p = p0;
632 		    w2l = p - p0;
633 		}
634 		else
635 		    err = "more than two non-download words given";
636 	    }
637 	}	/* end loop over words */
638 
639 	if (w1l == 0) {
640 	    TRACE_FT((stderr,
641 	      "map file %s: line \"%s\" does not give a font name.",
642 	      p_head->value, line));
643 	    return NULL;
644 	}
645 
646 	if (err != NULL) {
647 	    TRACE_FT((stderr, "map file %s, font %.*s: %s", p_head->value,
648 	      (int) w1l, w1p, err));
649 	    return NULL;
650 	}
651 
652 	t1p = (struct avl_t1 *) avladd(w1p, w1l, (struct avl **) &t1_head,
653 	  sizeof(struct avl_t1));
654 
655 	if (t1p->key != w1p) {	/* if existing record */
656 	    TRACE_FT((stderr,
657 	      "map file %s, font %.*s: duplicate record; using first one",
658 	      p_head->value, (int) w1l, w1p));
659 	    return NULL;
660 	}
661 
662 	t1p->key = q = xmalloc(w1l + w2l + 1 + pfl + encl + ql);
663 
664 	memcpy(q, w1p, w1l);
665 	q += w1l;
666 	t1p->psname = t1p->key;
667 	if (w2l != 0) {
668 	    t1p->psname = q;
669 	    memcpy(q, w2p, w2l);
670 	    q += w2l;
671 	}
672 	*q++ = '\0';
673 
674 	t1p->fontfile = NULL;
675 	if (pfl != 0) {
676 	    t1p->fontfile = q;
677 	    memcpy(q, pfp, pfl - 1);
678 	    q += pfl;
679 	    q[-1] = '\0';
680 	}
681 
682 	t1p->encname = t1p->addinfo = NULL;
683 # if FREETYPE
684 	t1p->bad = False;
685 	t1p->ft = NULL;
686 # endif
687 
688 	if (encl != 0) {
689 	    t1p->encname = q;
690 	    memcpy(q, encp, encl - 1);
691 	    q += encl;
692 	    q[-1] = '\0';
693 	}
694 
695 	if (ql != 0) {
696 	    t1p->addinfo = q;
697 	    if (qp != 0) {
698 		memcpy(q, qp, ql - 1);
699 		q += ql;
700 	    }
701 	    else {	/* multiple quoted strings; rescan to get them */
702 		const char	*p_end;
703 
704 		p = line;
705 		p_end = p + strlen(p);
706 		for (;;) {
707 		    while (*p == ' ' || *p == '\t') ++p;
708 		    if (*p == '\0')
709 			break;
710 
711 		    /* found a word */
712 		    if (*p == '"') {
713 			++p;
714 			p0 = p;
715 			p = memchr(p0, '"', p_end - p0);
716 			if (p == NULL) p = p_end;
717 			memcpy(q, p0, p - p0);
718 			q += p - p0;
719 			*q++ = ' ';
720 			if (*p == '\0') break;
721 			++p;
722 		    }
723 		    else	/* skip unquoted word */
724 			while (*p != '\0' && *p != ' ' && *p != '\t') ++p;
725 		}
726 	    }
727 	    q[-1] = '\0';
728 	}
729 
730 	return t1p;
731 }
732 
733 /*
734  *	Information on the Ghostscript font aliasing mechanism is kept in
735  *	another AVL tree.  Each identifier points to a list of strings,
736  *	up to one from each Fontmap file.  Within a Fontmap file, later entries
737  *	for a given string override earlier ones.
738  */
739 
740 struct avl_gs {		/* structure for gs font information */
741 	AVL_COMMON;
742 	short		fontmap_number;	/* sequence of most recent entry */
743 	Boolean		in_use;
744 	struct gs_list	*list;
745 };
746 
747 struct gs_list {	/* list of Fontmap entries for this font name */
748 	struct gs_list	*next;
749 	const char	value[0];
750 };
751 
752 static	struct avl_gs	*gs_head	= NULL;
753 
754 typedef	void	(*gs_path_proc)(FILE *);
755 
756 static	const char	*env_gs_lib;
757 
758 static FILE *
gs_try_fopen(const char * str,unsigned int len,const char * name)759 gs_try_fopen(const char *str, unsigned int len, const char *name)
760 {
761 	unsigned int	namelen	= strlen(name) + 1;
762 	FILE		*f;
763 
764 	if (len + namelen + 1 > ffline_len) expandline(len + namelen + 1);
765 	memcpy(ffline, str, len);
766 	ffline[len] = '/';
767 	memcpy(ffline + len + 1, name, namelen);
768 
769 	f = XFOPEN(ffline, OPEN_MODE);
770 
771 	TRACE_FT((stderr, "gs_try_fopen: %s: %s", ffline,
772 	      f != NULL ? "file opened" : strerror(errno)));
773 
774 	return f;
775 }
776 
777 static FILE *
gs_path_fopen(const char * name,gs_path_proc proc)778 gs_path_fopen(const char *name, gs_path_proc proc)
779 {
780 	const	char	*str1	= env_gs_lib;
781 	const	char	*str2	= DEFAULT_GS_LIB_PATH;
782 	const	char	*str1_end, *str2_end;
783 	const char	*p1, *p2;
784 	FILE		*f;
785 	unsigned int	namelen;
786 
787 	if (str1 == NULL) {
788 	    str1 = str2;
789 	    str2 = NULL;
790 	}
791 
792 	str1_end = str1 + strlen(str1);
793 
794 	for (;;) {
795 	    p1 = memchr(str1, ':', str1_end - str1);
796 	    if (p1 == NULL) p1 = str1_end;
797 	    if (p1 == str1) {
798 		if (str2 != NULL) {
799 		    str2_end = str2 + strlen(str2);
800 		    for (;;) {
801 			p2 = memchr(str2, ':', str2_end - str2);
802 			if (p2 == NULL) p2 = str2_end;
803 			if (p2 > str2) {
804 			    f = gs_try_fopen(str2, p2 - str2, name);
805 			    if (f != NULL) {
806 				if (proc != NULL)
807 				    proc(f);
808 				else
809 				    return f;
810 			    }
811 			}
812 			if (*p2 == '\0')
813 			    break;
814 			str2 = p2 + 1;
815 		    }
816 		    str2 = NULL;
817 		}
818 	    }
819 	    else {
820 		f = gs_try_fopen(str1, p1 - str1, name);
821 		if (f != NULL) {
822 		    if (proc != NULL)
823 			proc(f);
824 		    else
825 			return f;
826 		}
827 	    }
828 
829 	    if (*p1 == '\0')
830 		break;
831 	    str1 = p1 + 1;
832 	}
833 
834 	/* leave the file name in ffline[] for error message */
835 	namelen = strlen(name) + 1;
836 	if (namelen > ffline_len) expandline(namelen);
837 	memcpy(ffline, name, namelen);
838 
839 	return NULL;
840 }
841 
842 
843 static FILE *
lookup_gs_font(const char * font,const char ** path_ret)844 lookup_gs_font(const char *font, const char **path_ret)
845 {
846 	struct avl_gs	*gsfp;
847 	int		font_len;
848 	int		i;
849 
850 	font_len = strlen(font);
851 	gsfp = gs_head;
852 	for (;;) {
853 	    if (gsfp == NULL)		/* if not found */
854 		return NULL;
855 
856 	    i = font_len - gsfp->key_len;
857 	    if (i == 0)
858 		i = memcmp(font, gsfp->key, font_len);
859 	    if (i == 0) {		/* if found */
860 		struct gs_list	*gl, *gl2, *gl3;
861 		FILE		*f;
862 
863 		if (gsfp->in_use) {
864 		    TRACE_FT((stderr, "Alias loop for %s detected; ignoring.",
865 		      font));
866 		    return NULL;
867 		}
868 
869 		/* If we haven't done it yet, reverse the linked list */
870 		if (gsfp->fontmap_number != 0) {
871 		    gsfp->fontmap_number = 0;
872 		    gl = gsfp->list;
873 		    gl2 = NULL;
874 		    while (gl != NULL) {
875 			gl3 = gl->next;
876 			gl->next = gl2;
877 			gl2 = gl;
878 			gl = gl3;
879 		    }
880 		    gsfp->list = gl2;
881 		}
882 		gsfp->in_use = True;
883 		f = NULL;
884 		for (gl = gsfp->list; gl != NULL; gl = gl->next) {
885 		    if (gl->value[0] == '\0') {	/* if alias */
886 			TRACE_FT((stderr, "Found alias %s --> %s",
887 			  font, gl->value + 1));
888 			f = lookup_gs_font(gl->value + 1, path_ret);
889 			if (f == NULL)
890 			    TRACE_FT((stderr, "Alias %s not found.",
891 			      gl->value + 1));
892 			if (f != NULL) break;
893 		    }
894 		    else {
895 			TRACE_FT((stderr, "Checking file %s", gl->value));
896 			if (gl->value[0] == '/' || (gl->value[0] == '.'
897 			  && (gl->value[1] == '/' || (gl->value[1] == '.'
898 			  && gl->value[2] == '/')))) {
899 			    f = XFOPEN(gl->value, OPEN_MODE);
900 			    if (f != NULL) {
901 				*path_ret = xstrdup(gl->value);
902 				break;
903 			    }
904 			}
905 			else {
906 			    f = gs_path_fopen(gl->value, NULL);
907 			    if (f != NULL) {
908 				*path_ret = xstrdup(ffline);
909 				break;
910 			    }
911 			}
912 		    }
913 		}
914 		gsfp->in_use = False;
915 		return f;
916 	    }
917 	    gsfp = (struct avl_gs *) (i < 0 ? gsfp->left : gsfp->right);
918 	}
919 }
920 
921 
922 #define	GS_BUF_SIZE	4096
923 
924 struct gsfile {
925 	FILE			*f;
926 	unsigned char		*buffer;
927 	const unsigned char	*bufpos;
928 	const unsigned char	*buf_end;
929 };
930 
931 static Boolean
gs_fillbuf(struct gsfile * gsf)932 gs_fillbuf(struct gsfile *gsf)
933 {
934 	unsigned char	*p;
935 	unsigned int	len;
936 
937 	if (gsf->buf_end < gsf->buffer + GS_BUF_SIZE)
938 	    return False;
939 
940 	gsf->bufpos = p = gsf->buffer;
941 	for (;;) {
942 	    len = gsf->buf_end - p;
943 	    if (len <= 0) break;
944 	    len = fread(p, 1, len, gsf->f);
945 	    if (len <= 0) break;
946 	    p += len;
947 	}
948 	gsf->buf_end = p;
949 	return (p > gsf->buffer);
950 }
951 
952 static	unsigned char	gs_ctype[256];
953 
954 #define	GS_EOF	'\0'
955 #define	GS_ERR	'%'
956 #define	LPAREN	'('
957 #define	RPAREN	')'
958 
959 static void
init_gs_ctype(void)960 init_gs_ctype(void)
961 {
962 	const char	*p;
963 
964 	for (p = " \t\f\n\r";; ++p) {
965 	    gs_ctype[(unsigned char) *p] = 1;	/* white space */
966 	    if (*p == '\0') break;
967 	}
968 	gs_ctype['/'] = 2;		/* literal token */
969 	gs_ctype['('] = 3;		/* string */
970 	for (p = ")<>[]{}%"; *p != '\0'; ++p)
971 	    gs_ctype[(unsigned char) *p] = 4;	/* delimiter */
972 }
973 
974 static unsigned char
get_gs_character(struct gsfile * gsfp)975 get_gs_character(struct gsfile *gsfp)
976 {
977 	unsigned char		c;
978 
979 	for (;;) {
980 	    if (gsfp->bufpos >= gsfp->buf_end && !gs_fillbuf(gsfp))
981 		return '%';
982 	    c = *gsfp->bufpos++;
983 
984 	    /* Check for comments */
985 	    if (c == '%') {
986 		for (;;) {
987 		    const unsigned char *p1, *p2;
988 
989 		    p1 = memchr(gsfp->bufpos, '\n',
990 		      gsfp->buf_end - gsfp->bufpos);
991 		    if (p1 == NULL) p1 = gsfp->buf_end;
992 		    p2 = memchr(gsfp->bufpos, '\r', p1 - gsfp->bufpos);
993 		    if (p2 != NULL) p1 = p2;
994 		    p2 = memchr(gsfp->bufpos, '\f', p1 - gsfp->bufpos);
995 		    if (p2 != NULL) p1 = p2;
996 		    if (p1 < gsfp->buf_end) {
997 			gsfp->bufpos = p1 + 1;
998 			break;
999 		    }
1000 		    if (!gs_fillbuf(gsfp))
1001 			return '%';
1002 		}
1003 		continue;
1004 	    }
1005 
1006 	    if (gs_ctype[c] != 1)
1007 		break;
1008 	}
1009 
1010 	return c;
1011 }
1012 
1013 static unsigned char
get_gs_token(struct gsfile * gsfp,unsigned int pos,unsigned int * pos_ret,const char * file_type)1014 get_gs_token(struct gsfile *gsfp,
1015 	unsigned int pos,
1016 	unsigned int *pos_ret,
1017 	const char *file_type)
1018 {
1019 	unsigned char		gs_t_type;
1020 	unsigned char		c;
1021 	const unsigned char	*p0;
1022 	unsigned int		pos0;
1023 	unsigned int		depth;
1024 
1025 	gs_t_type = c = get_gs_character(gsfp);
1026 	if (c == '%')
1027 	    return GS_EOF;
1028 
1029 	p0 = gsfp->bufpos;
1030 	switch (gs_ctype[c]) {
1031 	    case 0:	/* most characters */
1032 	    case 2:	/* '/' */
1033 		--p0;	/* retain initial character */
1034 		pos0 = pos;
1035 		for (;;) {
1036 		    if (gsfp->bufpos >= gsfp->buf_end) {
1037 			unsigned int	len	= gsfp->bufpos - p0;
1038 
1039 			if (pos + len >= ffline_len) expandline(pos + len);
1040 			bcopy(p0, ffline + pos, len);
1041 			pos += len;
1042 			if (!gs_fillbuf(gsfp))
1043 			    break;
1044 			p0 = gsfp->buffer;
1045 		    }
1046 		    if (gs_ctype[*gsfp->bufpos] != 0) {
1047 			unsigned int	len	= gsfp->bufpos - p0;
1048 
1049 			if (pos + len >= ffline_len) expandline(pos + len);
1050 			bcopy(p0, ffline + pos, len);
1051 			pos += len;
1052 			break;
1053 		    }
1054 		    ++gsfp->bufpos;
1055 		}
1056 		/* Filter out DOS ^Z */
1057 		if (pos == pos0 + 1 && ffline[pos0] == '\032')
1058 		    return GS_EOF;
1059 		break;
1060 
1061 	    case 3:	/* left parenthesis */
1062 		depth = 1;
1063 		for (;;) {
1064 		    const unsigned char *p1, *p2, *p3;
1065 
1066 		    if (gsfp->bufpos >= gsfp->buf_end) {
1067 			unsigned int	len	= gsfp->bufpos - p0;
1068 
1069 			if (pos + len >= ffline_len) expandline(pos + len);
1070 			bcopy(p0, ffline + pos, len);
1071 			pos += len;
1072 			if (!gs_fillbuf(gsfp)) {
1073 			    TRACE_FT((stderr,
1074 			      "unterminated string in %s file; giving up.",
1075 			      file_type));
1076 			    return GS_ERR;
1077 			}
1078 			p0 = gsfp->buffer;
1079 		    }
1080 		    p1 = memchr(gsfp->bufpos, RPAREN,
1081 		      gsfp->buf_end - gsfp->bufpos);
1082 		    if (p1 == NULL) p1 = gsfp->buf_end;
1083 		    for (;;) {
1084 			p2 = memchr(gsfp->bufpos, LPAREN, p1 - gsfp->bufpos);
1085 			if (p2 == NULL) p2 = p1;
1086 			p3 = p2;
1087 			for (;;) {
1088 			    if (p3 <= gsfp->bufpos) {
1089 				if (c == '\\') --p3;
1090 				break;
1091 			    }
1092 			    if (p3[-1] != '\\') break;
1093 			    --p3;
1094 			}
1095 			c = '\\' - 1 + ((p2 - p3) & 1);
1096 			if (p2 >= p1)
1097 			    break;
1098 			if (c != '\\')
1099 			    ++depth;
1100 			gsfp->bufpos = p2 + 1;
1101 			c = '\0';
1102 		    }
1103 		    if (p1 < gsfp->buf_end) {	/* if left parenthesis at p1 */
1104 			if (c != '\\') {
1105 			    if (--depth == 0) {
1106 				unsigned int	len	= p1 - p0;
1107 
1108 				if (pos + len >= ffline_len)
1109 				    expandline(pos + len);
1110 				bcopy(p0, ffline + pos, len);
1111 				pos += len;
1112 				gsfp->bufpos = p1 + 1;
1113 				break;
1114 			    }
1115 			}
1116 			++p1;
1117 		    }
1118 		    gsfp->bufpos = p1;
1119 		}
1120 		/* We could do backslash escaping here, but it's probably
1121 		   unnecessary.  */
1122 		break;
1123 
1124 	    default:
1125 		TRACE_FT((stderr,
1126 		  "invalid character `%c' encountered in %s file; giving up.",
1127 		  c, file_type));
1128 		return GS_ERR;
1129 	}
1130 
1131 	*pos_ret = pos;
1132 	return gs_t_type;
1133 }
1134 
1135 
1136 static	short		gs_fontmap_number	= 0;
1137 
1138 static void
process_gs_fontmap(FILE * f)1139 process_gs_fontmap(FILE *f)
1140 {
1141 	struct gsfile	gsf;
1142 	unsigned char	buffer[GS_BUF_SIZE];
1143 	unsigned char	ttype;
1144 	unsigned int	pos1, pos2, pos3;
1145 
1146 	++gs_fontmap_number;
1147 
1148 	gsf.f = f;
1149 	gsf.buffer = buffer;
1150 	gsf.bufpos = gsf.buf_end = buffer + GS_BUF_SIZE;
1151 
1152 	/*
1153 	 * Allow entries of the following types:
1154 	 *
1155 	 *	(string) .runlibfile
1156 	 *	(string) .runlibfileifexists
1157 	 *	/identifier (string) ;
1158 	 *	/identifier /alias ;
1159 	 */
1160 
1161 	for (;;) {
1162 	    ttype = get_gs_token(&gsf, 0, &pos1, "Fontmap");
1163 	    if (ttype == GS_EOF || ttype == GS_ERR)
1164 		break;
1165 	    if (ttype == LPAREN) {
1166 	        Boolean quiet = False;
1167 		FILE	*f1;
1168 
1169 		ttype = get_gs_token(&gsf, pos1, &pos2, "Fontmap");
1170 		if (ttype == GS_ERR)
1171 		    break;
1172 		if (ttype == GS_EOF) {
1173 		    TRACE_FT((stderr,
1174 		      "unexpected end of Fontmap file; giving up."));
1175 		    break;
1176 		}
1177 		if (ttype == '.' && pos2 - pos1 == 19
1178 		  && memcmp(ffline + pos1, ".runlibfileifexists", 19) == 0)
1179 		    quiet = True;
1180 		else if (ttype != '.' || pos2 - pos1 != 11
1181 		  || memcmp(ffline + pos1, ".runlibfile", 11) != 0) {
1182 		    TRACE_FT((stderr, "invalid token following \"(%.*s)\" in Fontmap file; giving up.",
1183 		      (int) pos1, ffline));
1184 		    break;
1185 		}
1186 
1187 		ffline[pos1] = '\0';
1188 		if (ffline[0] == '/' || (ffline[0] == '.' && (ffline[1] == '/'
1189 		  || (ffline[1] == '.' && ffline[2] == '/'))))
1190 		    f1 = XFOPEN(ffline, OPEN_MODE);
1191 		else {
1192 		    char *q;
1193 
1194 		    q = xmemdup(ffline, pos1 + 1);
1195 		    f1 = gs_path_fopen(q, NULL);
1196 		    free(q);
1197 		}
1198 
1199 		if (f1 == NULL) {
1200 		    if (!quiet)
1201 			XDVI_WARNING((stderr, "Fontmap .runlibfile: %s: %s",
1202 			  ffline, strerror(errno)));
1203 		    else
1204 			TRACE_FT((stderr,
1205 			  "Fontmap .runlibfileifexists: %s: %s\n",
1206 			  ffline, strerror(errno)));
1207 		}
1208 		else {
1209 		    --gs_fontmap_number;
1210 		    process_gs_fontmap(f1);
1211 		}
1212 	    }
1213 	    else if (ttype == '/') {
1214 		struct avl_gs	*gsfp;
1215 		struct gs_list	*gslp;
1216 
1217 		ttype = get_gs_token(&gsf, pos1, &pos2, "Fontmap");
1218 		if (ttype == GS_ERR)
1219 		    break;
1220 		if (ttype == GS_EOF) {
1221 		    TRACE_FT((stderr,
1222 		      "unexpected end of Fontmap file; giving up."));
1223 		    break;
1224 		}
1225 		if ((ttype != '/' && ttype != LPAREN)
1226 		  || pos2 == pos1	/* empty string would mess things up */
1227 		  || get_gs_token(&gsf, pos2, &pos3, "Fontmap") != ';'
1228 		  || pos3 != pos2 + 1) {
1229 		    TRACE_FT((stderr,
1230 		      "invalid token following \"%.*s\" in Fontmap file; giving up.",
1231 		      (int) pos1, ffline));
1232 		    break;
1233 		}
1234 		if (ttype == '/')
1235 		    ffline[pos1] = '\0';	/* mark aliases by initial \0 */
1236 		ffline[pos2++] = '\0';		/* terminate string */
1237 
1238 		/* Add to database */
1239 		gsfp = (struct avl_gs *) avladd(ffline + 1, pos1 - 1,
1240 		  (struct avl **) &gs_head, sizeof *gsfp);
1241 
1242 		if (gsfp->key == ffline + 1) {	/* if new record */
1243 		    gsfp->key = xmemdup(ffline + 1, pos1 - 1);
1244 		    gsfp->in_use = False;
1245 		    gsfp->list = NULL;
1246 		}
1247 		else {
1248 		    if (strlen(gsfp->list->value + 1) + 2 == pos2 - pos1
1249 		      && memcmp(gsfp->list->value, ffline + pos1, pos2 - pos1)
1250 		      == 0)
1251 			continue;	/* ignore duplicate entry */
1252 		    if (gsfp->fontmap_number == gs_fontmap_number) {
1253 			/* Later entries in a Fontmap file override earlier
1254 			   ones */
1255 			gslp = gsfp->list;
1256 			gsfp->list = gslp->next;
1257 			free(gslp);
1258 		    }
1259 		}
1260 		gslp = xmalloc(sizeof *gslp + pos2 - pos1);
1261 		gslp->next = gsfp->list;
1262 		gsfp->list = gslp;
1263 		memcpy((char *) gslp->value, ffline + pos1, pos2 - pos1);
1264 
1265 		gsfp->fontmap_number = gs_fontmap_number;
1266 	    }
1267 	    else {
1268 		TRACE_FT((stderr,
1269 		  "invalid token \"%s\" in Fontmap file; giving up.",
1270 		  ffline));
1271 	    }
1272 	}
1273 
1274 	fclose(f);
1275 }
1276 
1277 /*
1278  *	Read Ghostscript Fontmap files.  These are used if the line in
1279  *	psfonts.map does not contain a filename for the font.
1280  *
1281  *	For example:
1282  *		n019003l NimbusSanL-Regu
1283  */
1284 
1285 static void
read_gs_fontmaps(void)1286 read_gs_fontmaps(void)
1287 {
1288 	env_gs_lib = getenv("XDVI_GS_LIB");
1289 	if (env_gs_lib == NULL)
1290 	    env_gs_lib = getenv("GS_LIB");
1291 
1292 	if (gs_ctype[0] == 0)
1293 	    init_gs_ctype();
1294 
1295 	(void) gs_path_fopen("Fontmap", process_gs_fontmap);
1296 }
1297 
1298 
1299 /*
1300  *	pre_lookup_t1_font - Find a Type 1 font (or return NULL).
1301  */
1302 
1303 static struct avl_t1 *
pre_lookup_t1_font(const char * fontname)1304 pre_lookup_t1_font(const char *fontname)
1305 {
1306 	struct avl_t1	*t1p;
1307 	size_t		len;
1308 	int		i;
1309 
1310 	/* first, search for the font */
1311 
1312 	len = strlen(fontname);
1313 	t1p = t1_head;
1314 	while (t1p != NULL) {
1315 	    i = len - t1p->key_len;
1316 	    if (i == 0)
1317 		i = memcmp(fontname, t1p->key, len);
1318 	    if (i == 0)
1319 		return t1p;	/* found it */
1320 	    t1p = (struct avl_t1 *) (i < 0 ? t1p->left : t1p->right);
1321 	}
1322 
1323 	/* next, read in more records in hopes of finding the font */
1324 
1325 	if (p_head != NULL)
1326 	    for (;;) {
1327 		if (!fgets_long(mapfile)) {	/* if end of file */
1328 		    fclose(mapfile);
1329 		    p_head = p_head->next;
1330 		    if (!open_next_mapfile())
1331 			return NULL;
1332 		    continue;
1333 		}
1334 
1335 		if (*ffline < ' ' || *ffline == '*' || *ffline == '#'
1336 		  || *ffline == ';' || *ffline == '%')
1337 		    continue;
1338 
1339 		t1p = dvips_parse(ffline);
1340 
1341 		if (t1p != NULL && t1p->key_len == len
1342 		  && memcmp(t1p->key, fontname, len) == 0)
1343 		    return t1p;	/* found it */
1344 	    }
1345 
1346 	return NULL;
1347 }
1348 
1349 /*
1350  *	lookup_t1_font - Find a Type 1 font (or return NULL).
1351  */
1352 
1353 Boolean
lookup_t1_font(struct font * fontp,const char * fontname)1354 lookup_t1_font(struct font *fontp,
1355 	const char *fontname)
1356 {
1357 	struct avl_t1	*t1p;
1358 	struct ftfont	*ftp;
1359 
1360 	t1p = pre_lookup_t1_font(fontname);
1361 
1362 	if (t1p == NULL)
1363 	    return False;
1364 
1365 	if (t1p->bad) {
1366 	    TRACE_FT((stderr,
1367 	      "Font %s is marked as bad:  skipping scalable version",
1368 	      fontname));
1369 	    return False;
1370 	}
1371 
1372 	ftp = t1p->ft;
1373 	if (ftp != NULL) {	/* if it's is already in use at another size */
1374 	    struct font *first_size;
1375 
1376 	    /* The first node in the linked list of sizes contains the file */
1377 	    /* reference, so we link in the new node after the first node */
1378 	    first_size = ftp->first_size;
1379 	    fontp->next_size = first_size->next_size;
1380 	    first_size->next_size = fontp;
1381 	}
1382 	else {	/* first use at this size */
1383 	    t1p->ft = ftp = xmalloc(sizeof *ftp);
1384 	    ftp->face = NULL;
1385 	    ftp->t1 = t1p;
1386 	    ftp->first_size = fontp;
1387 	    fontp->next_size = NULL;
1388 	}
1389 	fontp->ft = ftp;
1390 	fontp->size = NULL;
1391 
1392 	return True;
1393 }
1394 
1395 FILE *
open_t1_font(struct avl_t1 * t1p,const char ** path_ret)1396 open_t1_font(struct avl_t1 *t1p,
1397 	const char **path_ret)
1398 {
1399 	FILE		*f;
1400 
1401 	if (t1p->fontfile == NULL) {	/* look up in GS Fontmap */
1402 	    static Boolean gs_fontmap_initialized = False;
1403 
1404 	    if (!gs_fontmap_initialized) {
1405 		read_gs_fontmaps();
1406 		gs_fontmap_initialized = True;
1407 	    }
1408 
1409 	    TRACE_FT((stderr,
1410 	      "Looking for font %.*s using gs method (PS name %s) --",
1411 	      t1p->key_len, t1p->key, t1p->psname));
1412 	    f = lookup_gs_font(t1p->psname, path_ret);
1413 
1414 	    if (f == NULL) {
1415 		TRACE_FT((stderr, "cannot find Type 1 font %s",
1416 		  t1p->psname));
1417 		return NULL;
1418 	    }
1419 
1420 	    TRACE_FT((stderr, "Found file %s", *path_ret));
1421 	}
1422 	else {
1423 	    char	*filename;
1424 
1425 	    filename = kpse_find_file(t1p->fontfile, kpse_type1_format, 0);
1426 	    if (filename == NULL) {
1427 		TRACE_FT((stderr, "cannot find Type 1 font file %s "
1428 		  "(will try PK version instead).",
1429 		  t1p->fontfile));
1430 		return NULL;
1431 	    }
1432 
1433 	    f = XFOPEN(filename, OPEN_MODE);
1434 	    if (f == NULL) {
1435 		TRACE_FT((stderr, "cannot open Type 1 font file %s: %s",
1436 		  filename, strerror(errno)));
1437 		free(filename);
1438 		return NULL;
1439 	    }
1440 	    *path_ret = filename;
1441 	}
1442 
1443 	return f;
1444 }
1445 
1446 
1447 /*
1448  *	Read the encoding vector file.  This assumes the same format as afm2tfm.
1449  */
1450 
1451 extern	struct findrec	search_header;	/* from special.c */
1452 
1453 void
read_encoding(struct avl_enc * encp)1454 read_encoding(struct avl_enc *encp)
1455 {
1456 	char		*filename;
1457 	FILE		*f;
1458 	struct gsfile	gsf;
1459 	unsigned char	buffer[GS_BUF_SIZE];
1460 	jmp_buf		err_env;
1461 	unsigned char	ttype;
1462 	unsigned int	pos1, pos2;
1463 	unsigned int	identindex[256];
1464 	const char	*str;
1465 	unsigned int	i;
1466 
1467 	TRACE_FT((stderr, "Reading encoding file %s", encp->key));
1468 
1469 	encp->valid = False;
1470 
1471 	/*
1472 	 * With TDS 1.0/kpathsea 3.5.2(?), encoding files are in texmf/fonts/enc
1473 	 * and accessed via kpse_enc_format; see e.g.:
1474 	 * http://tug.org/mailman/htdig/tex-live/2004-January/004734.html
1475 	 *
1476 	 * The lookups under kpse_program_text_format and
1477 	 * kpse_tex_ps_header_format are kept for backwards compatibility.
1478 	 */
1479 
1480 	filename = kpse_find_file(encp->key, kpse_enc_format, 0);
1481 	if (filename == NULL) {
1482 	    filename = kpse_find_file(filename, kpse_program_text_format, 0);
1483 	    if (filename == NULL)
1484 		filename = kpse_find_file(filename, kpse_tex_ps_header_format,
1485 		  True);
1486 	}
1487 	if (filename == NULL) {
1488 	    TRACE_FT((stderr,
1489 	      "cannot find encoding file %s; ignoring encoding", encp->key));
1490 	    return;
1491 	}
1492 
1493 	f = XFOPEN(filename, OPEN_MODE);
1494 	if (f == NULL) {
1495 	    TRACE_FT((stderr, "cannot open encoding file %s: %s",
1496 	      filename, strerror(errno)));
1497 	    free(filename);
1498 	    return;
1499 	}
1500 	free(filename);
1501 
1502 	if (gs_ctype[0] == 0)
1503 	    init_gs_ctype();
1504 
1505 	gsf.f = f;
1506 	gsf.buffer = buffer;
1507 	gsf.bufpos = gsf.buf_end = buffer + GS_BUF_SIZE;
1508 
1509 	if (!setjmp(err_env)) {
1510 	    if (get_gs_token(&gsf, 0, &pos1, "encoding") != '/'
1511 	      || get_gs_character(&gsf) != '[')
1512 		longjmp(err_env, 1);
1513 
1514 	    pos1 = 0;
1515 	    for (i = 0; i < 256; ++i) {
1516 		if (get_gs_token(&gsf, pos1, &pos2, "encoding") != '/')
1517 		    longjmp(err_env, 1);
1518 		if (pos2 == pos1 + 8
1519 		  && memcmp(ffline + pos1, "/.notdef", 8) == 0)
1520 		    identindex[i] = 0;
1521 		else {
1522 		    ffline[pos1] = '\0';
1523 		    identindex[i] = pos1 + 1;
1524 		    pos1 = pos2;
1525 		}
1526 	    }
1527 
1528 	    if (get_gs_character(&gsf) != ']')
1529 		longjmp(err_env, 1);
1530 
1531 	    ttype = get_gs_token(&gsf, pos1, &pos2, "encoding");
1532 	    if (!(ttype == GS_EOF
1533 	      || (ttype == 'd' && pos2 == pos1 + 3
1534 	      && memcmp(ffline + pos1, "def", 3) == 0
1535 	      && get_gs_token(&gsf, pos2, &pos2, "encoding") == GS_EOF)))
1536 		longjmp(err_env, 1);
1537 
1538 	    if (pos1 >= ffline_len) expandline(pos1 + 1);
1539 	    ffline[pos1] = '\0';
1540 	    str = xmemdup(ffline + 1, pos1);
1541 	    for (i = 0; i < 256; ++i)
1542 		encp->vec[i] =
1543 		  (identindex[i] != 0 ? str + identindex[i] - 1 : NULL);
1544 
1545 	    encp->valid = True;
1546 	}
1547 	else	/* if error */
1548 	    TRACE_FT((stderr,
1549 	      "invalid format in encoding file %s; giving up.", encp->key));
1550 
1551 	fclose(f);
1552 }
1553 
1554 #endif /* FREETYPE || PS */
1555 
1556 
1557 #if 0
1558 static int mktexpk_io[2];
1559 static struct xchild mktexpk_child = { NULL, 0, True, "font creation", NULL, NULL, mktexpk_ended };
1560 
1561 static char *read_from_mktexpk(int ignored);
1562 static void write_to_mktexpk(int ignored);
1563 
1564 static struct xio mktexpk_xio = { NULL, 0, XIO_IN,
1565 #if HAVE_POLL
1566 				  NULL,
1567 #endif
1568 				  read_from_mktexpk,
1569 				  NULL, NULL};
1570 
1571 
1572 static void
1573 mktexpk_ended(int status, struct xchild *this)
1574 {
1575     char str[1024] = "";
1576     char *err_msg = NULL;
1577 
1578     fprintf(stderr, "------- MKTEXPK_ENDED!\n");
1579     if (this->io != NULL && WIFEXITED(status)) {
1580 	err_msg = (this->io->read_proc)(this->io->fd, NULL);
1581 	SNPRINTF(str, 1024, "\nProcess `%s' returned exit code %d.\n",
1582 		 this->name, WEXITSTATUS(status));
1583 	str[1024 - 1] = '\0';
1584 	printlog_append_str(str);
1585 	if (err_msg != NULL) {
1586 	    fprintf(stderr, "FROM MKTEXPK: |%s|\n", err_msg);
1587 	    printlog_append_str(err_msg);
1588 	}
1589     }
1590     printlog_enable_closebutton();
1591     /*     free(this->name); */
1592     /*     free(this->io); */
1593     /*     free(this); */
1594 
1595     read_from_mktexpk(0);
1596     clear_io(this->io);
1597     (void)close(mktexpk_xio.fd);
1598 
1599     if (WIFEXITED(status)) {
1600 	if (WEXITSTATUS(status) == 0) {
1601 	    printlog_append("Done.\n", strlen("Done.\n"));
1602 	}
1603 	else
1604 	    sprintf(str, "\nPrint process returned exit code %d.\n",
1605 		    WEXITSTATUS(status));
1606     }
1607     else if (WIFSIGNALED(status))
1608 	sprintf(str, "\nPrint process terminated by signal %d.\n",
1609 		WTERMSIG(status));
1610     else
1611 	sprintf(str, "\nPrint process returned unknown status 0x%x.\n",
1612 		status);
1613 
1614 
1615 }
1616 
1617 static char *
1618 read_from_mktexpk(int fd)
1619 {
1620     int bytes;
1621     char line[80];
1622     char *buf;
1623 
1624     fprintf(stderr, "------- READ_FROM_MKTEXPK!\n");
1625     for (;;) {
1626 #ifndef MOTIF
1627 	bytes = read(fd, line, sizeof line);
1628 #else
1629 	bytes = read(fd, line, sizeof line - 1);
1630 #endif
1631 	if (bytes < 0) {
1632 	    if (AGAIN_CONDITION)
1633 		break;
1634 	    perror("xdvi: read_from_mktexpk");
1635 	    break;
1636 	}
1637 
1638 	if (bytes == 0)
1639 	    break;
1640 	else {
1641 #ifdef MOTIF
1642 	    line[bytes] = '\0';
1643 #endif
1644 	    fprintf(stderr, "------- READ_FROM_MKTEXPK:|%s|\n", line);
1645 	    printlog_append(line, bytes);
1646 	}
1647     }
1648     buf = xmalloc(bytes + 1);
1649     memcpy(buf, line, bytes);
1650     buf[bytes] = '\0';
1651     return buf;
1652 }
1653 
1654 static void
1655 write_to_mktexpk(int ignored)
1656 {
1657     UNUSED(ignored);
1658 
1659     return;
1660 }
1661 #endif /* 0 */
1662 
1663 
1664 #if DELAYED_MKTEXPK
1665 
1666 /* hash table for names of missing fonts, and their indexes */
1667 static hashTableT missing_font_hash;
1668 
1669 /* counters for missing fonts */
1670 static int missing_font_ctr = 0;
1671 static int missing_font_curr = 0;
1672 
1673 static const char *const dummy_font_value = ""; /* used as value in hash table of missing fonts ... */
1674 
1675 /* static char **all_fonts = NULL; */
1676 /* static size_t all_fonts_size = 0; */
1677 
1678 void
reset_missing_font_count(void)1679 reset_missing_font_count(void)
1680 {
1681     missing_font_ctr = missing_font_curr = 0;
1682 }
1683 
1684 /* Register font `fname' at size `dpi' as a font for which we'll need
1685  * to create a PK file
1686  */
1687 static void
add_missing_font(const char * fname,int dpi)1688 add_missing_font(const char *fname, int dpi)
1689 {
1690     char *buf = NULL;
1691 
1692     if (missing_font_hash.size == 0) {
1693 	missing_font_hash = hash_create(197);
1694     }
1695 
1696     /* font name (hash key) needs to be dynamically allocated here */
1697     buf = xmalloc(strlen(fname) + strlen(" at ") + LENGTH_OF_INT + 1);
1698     sprintf(buf, "%s at %d", fname, dpi);
1699     if (hash_lookup(missing_font_hash, buf) == NULL) {
1700 	missing_font_ctr++;
1701 	hash_insert(&missing_font_hash, buf, dummy_font_value);
1702     }
1703 }
1704 
1705 /* Check if font `fname' at size `dpi' is in the hash of fonts for which
1706  * we need to create a PK file. If it is, return its index (>= 0) and delete
1707  * it from the hash table; else, return -1.
1708  */
1709 static Boolean
get_and_remove_missing_font(const char * fname,int dpi)1710 get_and_remove_missing_font(const char *fname, int dpi)
1711 {
1712     char buf[1024];
1713 
1714     if (missing_font_hash.size == 0)
1715 	return False;
1716 
1717     SNPRINTF(buf, 1024, "%s at %d", fname, dpi);
1718     buf[1023] = '\0';
1719     if (hash_lookup(missing_font_hash, buf) == NULL) {
1720 	return False;
1721     }
1722 
1723     hash_remove(&missing_font_hash, buf, dummy_font_value);
1724     return True;
1725 }
1726 
1727 
1728 static Boolean
message_font_creation(const char * fname,int dpi)1729 message_font_creation(const char *fname, int dpi)
1730 {
1731     if (get_and_remove_missing_font(fname, dpi)) {
1732 	missing_font_curr++;
1733 	statusline_info(STATUS_MEDIUM,
1734 			 "Creating PK font: %s at %d dpi (%d of %d) ...",
1735 			 fname,
1736 			 dpi,
1737 			 missing_font_curr,
1738 			 missing_font_ctr);
1739 	force_statusline_update();
1740 	return True;
1741     }
1742     return False;
1743 }
1744 #endif /* DELAYED_MKTEXPK */
1745 
1746 FILE *
font_open(Boolean load_font_now,struct font * fontp,const char ** font_ret,int * dpi_ret)1747 font_open(
1748 #if DELAYED_MKTEXPK
1749 	  Boolean load_font_now,
1750 #endif
1751 	  struct font *fontp,
1752 	  const char **font_ret,
1753 	  int *dpi_ret)
1754 {
1755     char *name = NULL;
1756     kpse_glyph_file_type file_ret;
1757 #if DELAYED_MKTEXPK
1758     Boolean message_done = False;
1759     Boolean need_statusline_update = False;
1760 #endif
1761     /* defaults in case of success; filename_ret will be
1762        non-NULL iff the fallback font is used.
1763     */
1764     *font_ret = NULL;
1765     /* filename_ret is NULL iff a T1 version of a font has been used */
1766     fontp->filename = NULL;
1767     *dpi_ret = fontp->fsize;
1768 
1769 #ifdef PTEX
1770      /* for kanji, first try jfm's, then 8-bit vf's (not 16bit ovf's). */
1771     if (iskanjifont(fontp->fontname))
1772 	name = kpse_find_tfm(fontp->fontname);
1773     else
1774 #endif /* PTEX */
1775     if (resource.omega) { /* for omega, first try 16-bit ovf's, then 8-bit vf's. */
1776 	name = kpse_find_ovf(fontp->fontname);
1777 	if (name == NULL)
1778 	    name = kpse_find_vf(fontp->fontname);
1779     }
1780     else {
1781 	name = kpse_find_vf(fontp->fontname);
1782     }
1783 
1784     if (name) { /* found a vf font */
1785 	/* pretend it has the expected dpi value, else caller will complain */
1786  	*dpi_ret = fontp->fsize;
1787 	fontp->filename = name;
1788 	return XFOPEN(name, FOPEN_R_MODE);
1789     }
1790 
1791 #if FREETYPE
1792     if (resource.freetype
1793 # if DELAYED_MKTEXPK
1794 	&& load_font_now
1795 # endif
1796 	) {
1797 	/* First attempt: freetype font of correct size
1798 	 * (for delayed_mtkexpk, only when scanning postamble for the first time)
1799 	 */
1800 	if (lookup_t1_font(fontp, fontp->fontname)) {
1801 	    TRACE_FT((stderr, "found freetype font %s", fontp->fontname));
1802 	    return NULL;
1803 	}
1804 	TRACE_FT((stderr,
1805 		  "Freetype version of font %s not found, trying pixel version next, then fallback",
1806 		  fontp->fontname));
1807     }
1808 #endif /* FREETYPE */
1809 
1810 
1811     /*
1812       TODO:
1813 
1814       Probably a better approach would be as follows:
1815 
1816       1. Read the postamble to get all font definitions. Then, set:
1817 
1818       kpse_set_program_enabled(kpse_any_glyph_format, False, kpse_src_compile);
1819 
1820       and run load_font() on all of the fonts, with an array in which to save
1821       the names that don't exist (that returned NULL).
1822 
1823       2. Run load_font() again on the fonts that didn't exist in step
1824       (1) and display the output in a window. This somehow needs to
1825       be fork()ed so that the window itself remains responsive.
1826       (Maybe it's easier to call mktexpk directly on the command-line?)
1827 
1828       _________________________________________________________
1829       |                                                       |
1830       |   Xdvi is creating fonts, please be patient ...       |
1831       |                                                       |
1832       |   Font xyz (n of m)                                   |
1833       |                                                       |
1834       |   Errors: 0          [ Show Details ... ]             |
1835       |                                                       |
1836       |   [ ... some progress meter or busy indicator ... ]   |
1837       |                                                       |
1838       |                                                       |
1839       |   [ Exit xdvi ]                            [ Help ]   |
1840       |                                                       |
1841       ---------------------------------------------------------
1842 
1843       This window can be shown before the main window is opened.
1844 
1845     */
1846 
1847     /* Second try: PK/GF/... font within allowable size range */
1848     /*
1849       NOTE SU: The problem with this is that it will already use the PK version
1850       of the fallback font (e.g. cmr10.600pk) if the PK version exists, so the
1851       Type1 version of the fallback won't get used at all. But maybe this isn't
1852       that severe, given that the font is grossly wrong anyway.
1853     */
1854 #if DELAYED_MKTEXPK
1855     if (load_font_now) {
1856 	fprintf(stderr, "loading font now\n");
1857 	if (message_font_creation(fontp->fontname, (int)(fontp->fsize + 0.5))) {
1858 	    message_done = True;
1859 	    name = kpse_find_glyph(fontp->fontname, (unsigned)(fontp->fsize + .5),
1860 				   kpse_any_glyph_format, &file_ret);
1861 	}
1862 	else {
1863 	    kpse_set_program_enabled(kpse_any_glyph_format, False, kpse_src_compile);
1864 	    name = kpse_find_glyph(fontp->fontname, (unsigned)(fontp->fsize + .5),
1865 				   kpse_any_glyph_format, &file_ret);
1866 	    /* no success if either name is NULL or the filename returned in file_ret is
1867 	       a different font */
1868 #if 1
1869 	    /* ??? Bug with tex/test2.tex if cmbr exists but cmr doesn't ??? */
1870 	    fprintf(stderr, "creating %s\n", fontp->fontname);
1871 	    if (!name || strcmp(file_ret.name, fontp->fontname) != 0) {
1872 		statusline_info(STATUS_MEDIUM,
1873 				 "Creating PK font: %s at %d dpi ...",
1874 				 fontp->fontname,
1875 				 (int)(fontp->fsize + 0.5));
1876 		need_statusline_update = True;
1877 		force_statusline_update();
1878 		kpse_set_program_enabled(kpse_any_glyph_format, resource.makepk, kpse_src_compile);
1879 		name = kpse_find_glyph(fontp->fontname, (unsigned)(fontp->fsize + .5),
1880 				       kpse_any_glyph_format, &file_ret);
1881 	    }
1882 #endif
1883 	}
1884     }
1885     else {
1886 	name = kpse_find_glyph(fontp->fontname, (unsigned)(fontp->fsize + .5),
1887 			       kpse_any_glyph_format, &file_ret);
1888     }
1889 #else /* DELAYED_MKTEXPK */
1890     name = kpse_find_glyph(fontp->fontname, (unsigned)(fontp->fsize + .5),
1891 			   kpse_any_glyph_format, &file_ret);
1892 #endif /* DELAYED_MKTEXPK */
1893 
1894     if (name) { /* success */
1895 #if DELAYED_MKTEXPK
1896 	if (need_statusline_update) {
1897 	    statusline_info(STATUS_SHORT, "Creating PK font: %s at %d dpi ... done",
1898 			     fontp->fontname,
1899 			     (int)(fontp->fsize + 0.5));
1900 	    force_statusline_update();
1901 	}
1902 #endif
1903 	*dpi_ret = file_ret.dpi;
1904 	fontp->filename = name;
1905 	*font_ret = file_ret.name;
1906 	TRACE_FT((stderr, "Found pixel version: %s at %d dpi", file_ret.name, *dpi_ret));
1907 #if DELAYED_MKTEXPK
1908 	if (message_done) {
1909 	    statusline_append(STATUS_VERYSHORT, "DUMMY", /* append text, don't overwrite */
1910 			      "done.");
1911 	    force_statusline_update();
1912 	}
1913 #endif
1914 	return XFOPEN(name, FOPEN_R_MODE);
1915     }
1916 #if DELAYED_MKTEXPK
1917     else if (!load_font_now) {
1918 	add_missing_font(fontp->fontname, (int)(fontp->fsize + 0.5));
1919 	return NULL;
1920     }
1921 #endif
1922     else if (resource.alt_font != NULL) {
1923 	/* The strange thing about kpse_find_glyph() is that it
1924 	   won't create a PK version of alt_font if it doesn't
1925 	   already exist. So we invoke it explicitly a second time
1926 	   for that one.
1927 	*/
1928 	TRACE_FT((stderr, "Trying fallback"));
1929 #if FREETYPE
1930 	if (resource.freetype
1931 #if DELAYED_MKTEXPK
1932 	    && load_font_now
1933 #endif
1934 	    ) {
1935 	    /* Third attempt: freetype version of fallback font */
1936 	    if (lookup_t1_font(fontp, resource.alt_font)) {
1937 		TRACE_FT((stderr, "found fallback font for %s: %s",
1938 			fontp->fontname, resource.alt_font));
1939 		*font_ret = xstrdup(resource.alt_font);
1940 		return NULL;
1941 	    }
1942 	    TRACE_FT((stderr,
1943 		      "Freetype version of fallback font %s not found, trying pixel version",
1944 		      resource.alt_font));
1945 	}
1946 #endif /* FREETYPE */
1947 	/* Fourth attempt: PK version of fallback font */
1948 	name = kpse_find_glyph(resource.alt_font, (unsigned)(fontp->fsize + .5),
1949 			       kpse_any_glyph_format, &file_ret);
1950 	if (name) { /* success */
1951 	    TRACE_FT((stderr, "Success for PK version of fallback"));
1952 	    *dpi_ret = file_ret.dpi;
1953 	    fontp->filename = name;
1954 	    *font_ret = xstrdup(resource.alt_font);
1955 	    return XFOPEN(name, FOPEN_R_MODE);
1956 	}
1957 	else {
1958 	    TRACE_FT((stderr, "Failure for PK version of fallback"));
1959 	}
1960     }
1961 
1962     /* all other cases are failure */
1963     TRACE_FT((stderr, "Failure"));
1964     return NULL;
1965 }
1966