1 /* $XTermId: fontenc.c,v 1.95 2021/02/18 21:41:52 tom Exp $ */
2 
3 /*
4 Copyright 2013-2020,2021 by Thomas E. Dickey
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, 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 IN
22 THE SOFTWARE.
23 */
24 
25 #include <other.h>
26 #include <sys.h>
27 
28 #ifdef USE_ZLIB
29 #include <zlib.h>
30 #endif
31 
32 #define flatUCode(row,col) (((row) << 8) + (col))
33 
34 typedef enum {
35     ftUnknown
36     ,ftComment
37     ,ftDefine
38     ,ftStartEncoding
39     ,ftStartMapping
40     ,ftEndMapping
41     ,ftEndEncoding
42     ,ftFirstIndex
43     ,ftSize
44     ,ftUndefine
45     ,ftAlias
46 } FONTENC_TYPES;
47 
48 /*
49  * The fontenc library is single-use (luit-only) and undocumented (read the
50  * source code).  Rather than incorporate it directly here, this module adapts
51  * as needed, and replaces where fontenc is unsuitable for reuse.
52  */
53 #ifdef USE_FONTENC
54 extern FontEncPtr FontEncReallyLoad(const char *charset, const char *fontFileName);
55 #endif
56 
57 /*
58  * fontenc uses FontEncDirectory to find the "encodings.dir" file.  It uses a
59  * compiled-in default value which can be overridden by an environment
60  * variable.
61  */
62 #ifndef USE_FONTENC
63 extern char *FontEncDirectory(void);
64 
65 char *
FontEncDirectory(void)66 FontEncDirectory(void)
67 {
68     static char default_dir[] = ENCODINGS_DIR_FILE;
69     static char *dir = NULL;
70 
71     if (dir == NULL) {
72 	char *c = getenv("FONT_ENCODINGS_DIRECTORY");
73 	if (c) {
74 	    dir = strmalloc(c);
75 	    if (!dir)
76 		return NULL;
77 	} else if (default_dir[0] != '\0') {
78 	    dir = default_dir;
79 	}
80     }
81     return dir;
82 }
83 #endif /* !USE_FONTENC */
84 
85 /*
86  * fontenc uses an "encodings.dir" file which can appear in the X fonts tree in
87  * more than one place (because the file is created as a side-effect of
88  * mkfontdir).
89  *
90  * The "encodings.dir" file has a count of the number of records at the start.
91  * Subsequent lines contain an alias and a pathname (for a ".enc" file).  The
92  * pathname can be either absolute or relative to the directory containing
93  * "encodings.dir".
94  *
95  * The ".enc" files typically are gzip'd (fontenc is hardcoded to expect this,
96  * and has been known to dump core when a stray uncompressed file is put into
97  * the tree).  The alias in "encodings.dir" is obtained from the first line
98  * of the ".enc" file, and may be unrelated to the actual filename.
99  */
100 typedef struct {
101     int used;
102     char *alias;
103     char *path;
104     FontEncPtr data;
105 } ENCODINGS_DIR;
106 
107 static ENCODINGS_DIR *encodings_dir;
108 
109 /*
110  * Read a buffer, trimming trailing whitespace (unlike getline, which in turn
111  * is not portable enough in any case).
112  */
113 static char *
getFileBuffer(char ** bufferp,size_t * lengthp,FILE * fp)114 getFileBuffer(char **bufferp, size_t *lengthp, FILE *fp)
115 {
116     size_t used = 0;
117     char extra[BUFSIZ];
118     int finished = 1;
119 
120     if (*bufferp != 0)
121 	**bufferp = '\0';
122 
123     while (fgets(extra, (int) sizeof(extra) - 1, fp)) {
124 	size_t have = strlen(extra);
125 	int found = 0;
126 	finished = 0;
127 	while (have != 0) {
128 	    --have;
129 	    if (extra[have] == '\n') {
130 		found = 1;
131 	    }
132 	    if (isspace(UChar(extra[have]))) {
133 		extra[have] = '\0';
134 	    } else {
135 		break;
136 	    }
137 	}
138 	if (have) {
139 	    size_t need = have + used + 2;
140 	    if (*lengthp < need) {
141 		*bufferp = realloc(*bufferp, need);
142 		if (*bufferp == 0)
143 		    return 0;
144 		*lengthp = need;
145 	    } else if (*bufferp == 0) {
146 		return 0;
147 	    }
148 	    strcpy(*bufferp + used, extra);
149 	    used += have;
150 	}
151 	if (found) {
152 	    break;
153 	}
154     }
155 
156     return !finished ? *bufferp : 0;
157 }
158 
159 #ifndef USE_FONTENC
160 #ifdef USE_ZLIB
161 /*
162  * Read a buffer, trimming trailing whitespace (unlike getline, which in turn
163  * is not portable enough in any case).
164  */
165 static char *
getGzipBuffer(char ** bufferp,size_t * lengthp,gzFile fp)166 getGzipBuffer(char **bufferp, size_t *lengthp, gzFile fp)
167 {
168     size_t used = 0;
169     char extra[BUFSIZ];
170     int finished = 1;
171 
172     if (*bufferp != 0)
173 	**bufferp = '\0';
174 
175     while (gzgets(fp, extra, (int) sizeof(extra) - 1)) {
176 	size_t have = strlen(extra);
177 	int found = 0;
178 	finished = 0;
179 	while (have != 0) {
180 	    --have;
181 	    if (extra[have] == '\n') {
182 		found = 1;
183 	    }
184 	    if (isspace(UChar(extra[have]))) {
185 		extra[have] = '\0';
186 	    } else {
187 		break;
188 	    }
189 	}
190 	if (have) {
191 	    size_t need = have + used + 2;
192 	    if (*lengthp < need) {
193 		*bufferp = realloc(*bufferp, need);
194 		*lengthp = need;
195 	    }
196 	    strcpy(*bufferp + used, extra);
197 	    used += have;
198 	}
199 	if (found) {
200 	    break;
201 	}
202     }
203 
204     return !finished ? *bufferp : 0;
205 }
206 #endif /* USE_ZLIB */
207 #endif /* !USE_FONTENC */
208 
209 static char *
absolutePath(char * given,const char * refPath)210 absolutePath(char *given, const char *refPath)
211 {
212     char *result;
213     char *leaf;
214 
215     if (*given != '/') {
216 	if (!strncmp(given, "./", (size_t) 2)) {
217 	    given += 2;
218 	}
219 	result = malloc(strlen(refPath) + strlen(given) + 2);
220 	if (result != 0) {
221 	    strcpy(result, refPath);
222 	    if ((leaf = strrchr(result, '/')) == 0) {
223 		leaf = result + strlen(result);
224 		*leaf++ = '/';
225 	    }
226 	    strcpy(leaf + 1, given);
227 	} else {
228 	    FatalError("cannot allocate absolute path for %s\n", given);
229 	}
230     } else {
231 	result = strmalloc(given);
232     }
233     return result;
234 }
235 
236 static int
compare_aliases(const void * a,const void * b)237 compare_aliases(const void *a, const void *b)
238 {
239     const ENCODINGS_DIR *p = a;
240     const ENCODINGS_DIR *q = b;
241     return strcmp(p->alias, q->alias);
242 }
243 
244 static char *
skipToWhite(char * value)245 skipToWhite(char *value)
246 {
247     char *result = 0;
248     while (*value != '\0') {
249 	if (isspace(UChar(*value))) {
250 	    result = value;
251 	    break;
252 	}
253 	++value;
254     }
255     return result;
256 }
257 
258 static void
loadEncodingsDir(void)259 loadEncodingsDir(void)
260 {
261     if (encodings_dir == 0) {
262 	FILE *fp;
263 	const char *path = FontEncDirectory();
264 	char *buffer = 0;
265 	size_t entry = 0;
266 	size_t length = 0;
267 	size_t entries = 0;
268 	char *value;
269 	int used = 0;
270 	int n, found;
271 	int row = 0;
272 
273 	if (path == 0) {
274 	    TRACE(("cannot find encodings.dir\n"));
275 	} else if ((fp = fopen(path, "r")) == 0) {
276 	    FatalError("cannot open %s\n", path);
277 	} else {
278 	    while (getFileBuffer(&buffer, &length, fp) != 0) {
279 		++row;
280 		if (*buffer == '\0')
281 		    continue;
282 		if (encodings_dir == 0) {
283 		    char *next = 0;
284 		    long count = strtol(buffer, &next, 10);
285 		    if (count <= 0) {
286 			FatalError("found no count in %s\n", path);
287 		    }
288 		    entries = (size_t) count;
289 		    encodings_dir = calloc(entries + 1, sizeof(*encodings_dir));
290 		    if (encodings_dir == 0) {
291 			FatalError("cannot allocate %ld encodings\n", count);
292 		    }
293 		} else if ((value = skipToWhite(buffer)) != 0) {
294 		    *value++ = '\0';
295 		    /* get rid of duplicates - they do occur */
296 		    for (n = found = 0; n < used; ++n) {
297 			if (!strcmp(buffer, encodings_dir[n].alias)) {
298 			    found = 1;
299 			    break;
300 			}
301 		    }
302 		    if (!found) {
303 			encodings_dir[used].alias = strmalloc(buffer);
304 			encodings_dir[used].path = absolutePath(value, path);
305 			++used;
306 		    }
307 
308 		    if (++entry >= entries)
309 			break;
310 		} else {
311 		    FatalError("incorrect format of line %d:%s\n", row, buffer);
312 		}
313 	    }
314 	    fclose(fp);
315 	    free(buffer);
316 	    if (used > 1) {
317 		qsort(encodings_dir,
318 		      (size_t) used,
319 		      sizeof(encodings_dir[0]),
320 		      compare_aliases);
321 	    }
322 	}
323     }
324 }
325 
326 #ifndef USE_FONTENC
327 /*
328  * The fontenc library attempts to fill in the one-one mapping in the setCode
329  * function, but it is inconsistently applied and the loaded map contains gaps.
330  * This function sets one-one for any codes which map to NUL.
331  */
332 static void
fillCharMap(FontEncSimpleMapPtr mp,int size)333 fillCharMap(FontEncSimpleMapPtr mp, int size)
334 {
335     int n;
336 
337     if (mp != 0 && mp->map != 0) {
338 	for (n = 0; n < size; ++n) {
339 	    if (mp->map[n] == 0) {
340 		mp->map[n] = (UCode) (n + mp->first);
341 	    }
342 	}
343     }
344 }
345 
346 /*
347  * This function does the opposite of fillCharMap(), replacing one-one mappings
348  * with a NUL.
349  */
350 static void
trimCharMap(FontEncSimpleMapPtr mp,int size)351 trimCharMap(FontEncSimpleMapPtr mp, int size)
352 {
353     int n;
354 
355     if (mp != 0 && mp->map != 0) {
356 	for (n = 0; n < size; ++n) {
357 	    if (mp->map[n] == (UCode) (n + mp->first)) {
358 		mp->map[n] = 0;
359 	    }
360 	}
361     }
362 }
363 
364 static void
trim_or_fill(FontEncSimpleMapPtr mp,int size)365 trim_or_fill(FontEncSimpleMapPtr mp, int size)
366 {
367     if (fill_fontenc) {
368 	fillCharMap(mp, size);
369     } else {
370 	trimCharMap(mp, size);
371     }
372 }
373 #else /* USE_FONTENC */
374 #define fillCharMap(mp, size)	/* nothing */
375 #define trimCharMap(mp, size)	/* nothing */
376 #define trim_or_fill(mp, size)	/* nothing */
377 #endif /* !USE_FONTENC */
378 
379 static size_t
fontencSize(FontEncPtr enc)380 fontencSize(FontEncPtr enc)
381 {
382     size_t result = (size_t) (enc->size ? enc->size : 256);
383     if (enc->row_size)
384 	result = (result) * 256;
385     return result;
386 }
387 
388 #ifndef USE_FONTENC
389 #ifdef USE_ZLIB
390 static char *
skipBlanks(char * value)391 skipBlanks(char *value)
392 {
393     while (isspace(UChar(*value)))
394 	++value;
395     return value;
396 }
397 
398 static char *
skipNonblanks(char * value)399 skipNonblanks(char *value)
400 {
401     while (*value != '\0' && !isspace(UChar(*value)))
402 	++value;
403     return value;
404 }
405 
406 static char *
allocateToken(char * value)407 allocateToken(char *value)
408 {
409     char *result;
410     char *next;
411     char save;
412 
413     value = skipBlanks(value);
414     next = skipNonblanks(value);
415     save = *next;
416     *next = '\0';
417     result = strmalloc(value);
418     *next = save;
419 
420     return result;
421 }
422 
423 static int
compareKeyword(char * value,const char * expected)424 compareKeyword(char *value, const char *expected)
425 {
426     int result;
427     char save;
428     char *next;
429 
430     next = skipNonblanks(value);
431     save = *next;
432     *next = '\0';
433 
434     result = StrCaseCmp(value, expected);
435 
436     *next = save;
437     return result;
438 }
439 
440 static FONTENC_TYPES
getLineType(char * line,char ** nextp)441 getLineType(char *line, char **nextp)
442 {
443     FONTENC_TYPES result = ftUnknown;
444     char save;
445 
446     line = skipBlanks(line);
447     *nextp = skipNonblanks(line);
448     save = **nextp;
449     **nextp = '\0';
450 
451     if (*line == '\0' || *line == '#') {
452 	result = ftComment;
453     } else if (isdigit(UChar(*line))) {
454 	result = ftDefine;
455     } else if (!StrCaseCmp(line, "ALIAS")) {
456 	result = ftAlias;
457     } else if (!StrCaseCmp(line, "STARTENCODING")) {
458 	result = ftStartEncoding;
459     } else if (!StrCaseCmp(line, "STARTMAPPING")) {
460 	result = ftStartMapping;
461     } else if (!StrCaseCmp(line, "ENDENCODING")) {
462 	result = ftEndEncoding;
463     } else if (!StrCaseCmp(line, "ENDMAPPING")) {
464 	result = ftEndMapping;
465     } else if (!StrCaseCmp(line, "SIZE")) {
466 	result = ftSize;
467     } else if (!StrCaseCmp(line, "FIRSTINDEX")) {
468 	result = ftFirstIndex;
469     } else if (!StrCaseCmp(line, "UNDEFINE")) {
470 	result = ftUndefine;
471     }
472     **nextp = save;
473     return result;
474 }
475 
476 #define MAX_NUMBERS 4
477 
478 static int
getNumbers(char * line,int * results)479 getNumbers(char *line, int *results)
480 {
481     int count = 0;
482     char *next;
483     long value;
484 
485     while (count + 1 < MAX_NUMBERS) {
486 	line = skipBlanks(line);
487 	value = strtol(line, &next, 0);
488 	if (next == line)
489 	    break;
490 	results[count++] = (int) value;
491 	line = next;
492     }
493 
494     return count;
495 }
496 
497 static int
fontencFirst(FontEncPtr enc)498 fontencFirst(FontEncPtr enc)
499 {
500     int result;
501 
502     if (enc->row_size) {
503 	result = (enc->row_size * enc->first) + enc->first_col;
504     } else {
505 	result = enc->first;
506     }
507     return result;
508 }
509 
510 static int
fontencIndex(FontEncPtr enc,unsigned from)511 fontencIndex(FontEncPtr enc, unsigned from)
512 {
513     int result;
514 
515     if ((int) from < MIN_UCODE) {
516 	result = -1;
517     } else if (from <= MAX_UCODE) {
518 	if (enc->row_size == 0) {
519 	    result = (int) from;
520 	} else {
521 	    int row = (int) rowOf(from);
522 	    int col = (int) colOf(from);
523 
524 	    if (col >= enc->row_size) {
525 		result = -1;
526 	    } else {
527 		result = (row * enc->row_size) + col;
528 	    }
529 	}
530     } else {
531 	result = -1;
532     }
533     return result;
534 }
535 
536 static void
defineCode(FontEncPtr enc,int from,int to)537 defineCode(FontEncPtr enc, int from, int to)
538 {
539     int inx = fontencIndex(enc, (unsigned) from);
540 
541     if (inx >= 0 && enc->mappings != NULL) {
542 	int first = fontencFirst(enc);
543 	FontEncSimpleMapPtr data = enc->mappings->client_data;
544 	int limit = (int) fontencSize(enc);
545 
546 	if (inx >= first && (unsigned) inx < data->len && inx < limit) {
547 	    data->map[inx - first] = (UCode) to;
548 	}
549     }
550 }
551 #endif /* USE_ZLIB */
552 #endif /* !USE_FONTENC */
553 
554 /*
555  * fontenc uses this indexing scheme to reduce tablesize.
556  * The function returns a valid index into the forward map to Unicode;
557  */
558 static int
fontencUnmap(FontEncSimpleMapPtr map,int from)559 fontencUnmap(FontEncSimpleMapPtr map, int from)
560 {
561     int result;
562 
563     if (map != 0) {
564 	if (map->row_size) {
565 	    from += map->first;
566 	    if (map->row_size == 0) {
567 		result = from;
568 	    } else {
569 		int row = (from / (int) map->row_size);
570 		int col = (from % (int) map->row_size);
571 		result = (row * 256) + col;
572 	    }
573 	} else {
574 	    result = from + map->first;
575 	}
576 
577 	if (result < MIN_UCODE) {
578 	    result = -1;
579 	} else if (result >= MAX_UCODE) {
580 	    result = -1;
581 	}
582     } else {
583 	result = -1;
584     }
585     return result;
586 }
587 
588 unsigned
luitRecode(unsigned code,void * client_data)589 luitRecode(unsigned code, void *client_data)
590 {
591     unsigned result;
592     FontEncSimpleMapPtr map;
593     unsigned inx;
594 
595     map = client_data;
596 
597     if (code >= MAX_UCODE) {
598 	result = 0;
599     } else {
600 	unsigned col = (unsigned) (map->row_size ? colOf(code) : 0);
601 
602 	if (map->row_size && col >= map->row_size) {
603 	    result = 0;
604 	} else {
605 	    if (map->row_size) {
606 		inx = col + (unsigned) rowOf(code) * map->row_size;
607 	    } else {
608 		inx = code;
609 	    }
610 
611 	    if (map->map
612 		&& inx >= map->first
613 		&& inx < map->first + map->len) {
614 		result = map->map[inx - map->first];
615 	    } else {
616 		result = code;
617 	    }
618 	}
619     }
620     return result;
621 }
622 
623 #define UpdateLoChar(value) \
624 	    if (lo_char < 0) { \
625 		lo_char = value; \
626 	    } else if (lo_char > value) { \
627 		lo_char = value; \
628 	    }
629 #define UpdateHiChar(value) \
630 	    if (hi_char < 0) { \
631 		hi_char = value; \
632 	    } else if (hi_char < value) { \
633 		hi_char = value; \
634 	    }
635 
636 /*
637  * Read an encoding file, report summary statistics.
638  */
639 static FontEncPtr
loadFontEncRec(const char * charset,const char * path)640 loadFontEncRec(const char *charset, const char *path)
641 {
642     FontEncPtr result;
643 #if defined(USE_FONTENC)
644     result = FontEncReallyLoad(charset, path);
645 #elif defined(USE_ZLIB)
646     gzFile fp;
647     char *buffer = 0;
648     size_t length = 0;
649     size_t numAliases = 0;
650     size_t result_size = 0;
651     int numbers[MAX_NUMBERS];
652     FontMapPtr mapping = 0;
653     int done = 0;
654 
655     (void) charset;
656 
657     if ((result = calloc((size_t) 1, sizeof(*result))) == 0)
658 	return 0;
659 
660     fp = gzopen(path, "r");
661     if (fp != 0) {
662 	int count = 0;
663 	int ignore = 0;
664 	while (!done && getGzipBuffer(&buffer, &length, fp)) {
665 	    char *later;
666 	    ++count;
667 	    switch (getLineType(buffer, &later)) {
668 	    case ftComment:
669 		break;
670 	    case ftStartEncoding:
671 		/*
672 		 * Parameter should agree with "charset", but sometimes
673 		 * does not.
674 		 */
675 		result->name = allocateToken(later);
676 		result->size = 256;
677 		break;
678 	    case ftAlias:
679 		result->aliases = realloc(result->aliases,
680 					  (numAliases + 2) * sizeof(char *));
681 		result->aliases[numAliases++] = allocateToken(later);
682 		result->aliases[numAliases] = 0;
683 		break;
684 	    case ftSize:
685 		/*
686 		 * A single parameter gives the highest value directly.
687 		 * Two parameters give number of rows and row size.
688 		 */
689 		switch (getNumbers(later, numbers)) {
690 		default:
691 		    result->row_size = numbers[1];
692 		    /* FALLTHRU */
693 		case 1:
694 		    result->size = numbers[0];
695 		    break;
696 		case 0:
697 		    break;
698 		}
699 		break;
700 	    case ftFirstIndex:
701 		/*
702 		 * Referring to SIZE, this gives the first defined code in
703 		 * the range.  Use one or two parameters together with the
704 		 * row size from the SIZE statement.
705 		 */
706 		switch (getNumbers(later, numbers)) {
707 		default:
708 		    result->first_col = numbers[1];
709 		    /* FALLTHRU */
710 		case 1:
711 		    result->first = numbers[0];
712 		    break;
713 		case 0:
714 		    break;
715 		}
716 		break;
717 	    case ftUndefine:
718 		/*
719 		 * Mark one or more codes within the overall range as
720 		 * undefined.  There is no mapping to unicode for those codes.
721 		 *
722 		 * One or two parameters are expected.  Two parameters specify
723 		 * a range of codes to undefine.
724 		 */
725 		if (!ignore) {
726 		    int code;
727 		    switch (getNumbers(later, numbers)) {
728 		    default:
729 			/* ignore */
730 			break;
731 		    case 2:
732 			for (code = numbers[0]; code <= numbers[1]; ++code) {
733 			    defineCode(result, code, 0);
734 			}
735 			break;
736 		    case 1:
737 			defineCode(result, numbers[0], 0);
738 			break;
739 		    }
740 		}
741 		break;
742 	    case ftDefine:
743 		if (!ignore) {
744 		    int from;
745 		    int to;
746 
747 		    switch (getNumbers(buffer, numbers)) {
748 		    case 1:
749 			/* ignore */
750 			break;
751 		    case 2:
752 			defineCode(result, numbers[0], numbers[1]);
753 			break;
754 		    case 3:
755 			to = numbers[2];
756 			for (from = numbers[0]; from <= numbers[1]; ++from) {
757 			    defineCode(result, from, to++);
758 			}
759 			break;
760 		    }
761 		}
762 		break;
763 	    case ftStartMapping:
764 		/*
765 		 * The encoding can have more than one mapping, but we care
766 		 * only about the mapping to unicode.
767 		 */
768 		later = skipBlanks(later);
769 		if (!compareKeyword(later, "unicode")) {
770 		    FontEncSimpleMapPtr mq;
771 
772 		    ignore = 0;
773 		    result_size = fontencSize(result);
774 		    mapping = TypeCalloc(FontMapRec);
775 		    if (mapping == 0)
776 			FatalError("cannot allocate map record\n");
777 		    mapping->type = FONT_ENCODING_UNICODE;
778 		    mapping->recode = luitRecode;
779 
780 		    if ((mq = TypeCalloc(FontEncSimpleMapRec)) == 0
781 			|| (mq->map = TypeCallocN(UCode, result_size)) == 0) {
782 			FatalError("cannot allocate map for %ld codes\n",
783 				   (long) result_size);
784 		    }
785 
786 		    if (result->row_size) {
787 			mq->first = (UCode) flatUCode(result->first, result->first_col);
788 		    } else {
789 			mq->first = (UCode) result->first;
790 		    }
791 
792 		    mq->len = (unsigned) fontencSize(result);
793 		    mq->first = (UCode) fontencFirst(result);
794 		    mq->row_size = (UCode) result->row_size;
795 
796 		    mapping->client_data = mq;
797 		    result->mappings = mapping;
798 		} else {
799 		    ignore = 1;
800 		}
801 		break;
802 	    case ftEndMapping:
803 		ignore = 0;
804 		break;
805 	    case ftEndEncoding:
806 		done = 1;
807 		/* should be end of file */
808 		break;
809 	    default:
810 		if (!ignore)
811 		    printf("\t->%s\n", buffer);
812 		break;
813 	    }
814 	}
815 	gzclose(fp);
816 	free(buffer);
817     }
818 
819     if (result->name == 0)
820 	result->name = strmalloc(charset);
821 #else /* !USE_FONTENC && !USE_ZLIB */
822     (void) charset;
823     (void) path;
824     result = 0;
825 #endif /* USE_FONTENC/USE_ZLIB */
826     return result;
827 }
828 
829 /*
830  * Find an encoding, given its name.
831  */
832 FontEncPtr
lookupOneFontenc(const char * name)833 lookupOneFontenc(const char *name)
834 {
835     int n;
836     FontEncPtr result = 0;
837 
838 #ifdef USE_FONTENC
839     result = FontEncFind(name, NULL);
840     if (result == 0)
841 #endif
842     {
843 	loadEncodingsDir();
844 	if (encodings_dir != 0) {
845 	    for (n = 0; encodings_dir[n].alias != 0; ++n) {
846 		if (!StrCaseCmp(name, encodings_dir[n].alias)) {
847 		    if ((result = encodings_dir[n].data) == 0
848 			&& encodings_dir[n].used == 0) {
849 			result = loadFontEncRec(encodings_dir[n].alias,
850 						encodings_dir[n].path);
851 			if (result == 0) {
852 			    Warning("cannot load data for %s\n",
853 				    encodings_dir[n].path);
854 			} else {
855 			    VERBOSE(1, ("load alias \"%s\" from \"%s\"\n",
856 					encodings_dir[n].alias,
857 					encodings_dir[n].path));
858 			}
859 			encodings_dir[n].used = 1;
860 			encodings_dir[n].data = result;
861 		    }
862 		    break;
863 		}
864 	    }
865 	}
866     }
867     return result;
868 }
869 
870 static const char *
fontmapTypename(int type)871 fontmapTypename(int type)
872 {
873     const char *result = "?";
874     switch (type) {
875 #ifdef FONT_ENCODING_TRUETYPE
876     case FONT_ENCODING_TRUETYPE:
877 	result = "cmap";
878 	break;
879 #endif
880     case FONT_ENCODING_UNICODE:
881 	result = "unicode";
882 	break;
883 #ifdef FONT_ENCODING_POSTSCRIPT
884     case FONT_ENCODING_POSTSCRIPT:
885 	result = "postscript";
886 	break;
887 #endif
888     }
889     return result;
890 }
891 
892 static FontEncSimpleMapPtr
findUnicodeMapping(FontEncPtr data)893 findUnicodeMapping(FontEncPtr data)
894 {
895     FontEncSimpleMapPtr mq = 0;
896     FontMapPtr mp;
897 
898     for (mp = data->mappings; mp != NULL; mp = mp->next) {
899 	if (mp->type == FONT_ENCODING_UNICODE) {
900 	    mq = mp->client_data;
901 	    if (mq->map == NULL)
902 		mq = NULL;
903 	    break;
904 	}
905     }
906     return mq;
907 }
908 
909 /*
910  * Read an encoding file, report summary statistics.
911  */
912 static FontEncPtr
reportOneFontenc(const char * alias,const char * path)913 reportOneFontenc(const char *alias, const char *path)
914 {
915     FontEncPtr data = loadFontEncRec(alias, path);
916     if (data != 0) {
917 	int n;
918 	int lo_char = -1;
919 	int hi_char = -1;
920 	int num_def = 0;
921 	int inx;
922 	FontEncSimpleMapPtr mq;
923 
924 	printf("\tName: %s\n", data->name);
925 	if (data->aliases) {
926 	    for (n = 0; data->aliases[n]; ++n) {
927 		printf("\t      %s\n", data->aliases[n]);
928 	    }
929 	}
930 	if (data->row_size) {
931 	    printf("\tSize: %d %d (%d)\n",
932 		   data->size,
933 		   data->row_size,
934 		   data->size * data->row_size);
935 	} else {
936 	    printf("\tSize: %d\n", data->size);
937 	}
938 	if (data->first_col) {
939 	    printf("\tBase: %04X\n", flatUCode(data->first, data->first_col));
940 	} else {
941 	    printf("\tBase: %04X\n", data->first);
942 	}
943 	mq = findUnicodeMapping(data);
944 	if (mq != 0) {
945 	    trim_or_fill(mq, (int) fontencSize(data));
946 	    for (n = 0; n < (int) mq->len; ++n) {
947 		inx = fontencUnmap(mq, n);
948 		if (inx >= 0 && mq->map[n]) {
949 		    UpdateLoChar(inx);
950 		    UpdateHiChar(inx);
951 		    if (mq->map[n] != inx)
952 			num_def++;
953 		}
954 	    }
955 	    if (lo_char < 0) {
956 		lo_char = mq->first;
957 		hi_char = data->size - 1;
958 	    }
959 	    printf("\tData: [%04x..%04x] defined %d\n", lo_char, hi_char, num_def);
960 	}
961     }
962     return data;
963 }
964 
965 /*
966  * Make a report of the encoding files which could be loaded using either
967  * the fontenc library (using its hardcoded tables) or dynamically via luit.
968  */
969 int
reportFontencCharsets(void)970 reportFontencCharsets(void)
971 {
972     int rc = EXIT_FAILURE;
973     int n;
974 
975     printf("Available encodings listed in:\n\t%s\n", FontEncDirectory());
976     loadEncodingsDir();
977     if (encodings_dir != 0) {
978 	for (n = 0; encodings_dir[n].alias != 0; ++n) {
979 	    printf("%s\n\t%s\n",
980 		   encodings_dir[n].alias,
981 		   encodings_dir[n].path);
982 	    encodings_dir[n].data = reportOneFontenc(encodings_dir[n].alias,
983 						     encodings_dir[n].path);
984 	    if (encodings_dir[n].data != 0) {
985 		rc = EXIT_SUCCESS;
986 	    }
987 	}
988     }
989 
990     if (rc != EXIT_SUCCESS) {
991 	Warning("no encodings found\n");
992     }
993     return rc;
994 }
995 
996 /*
997  * Regurgitate an encoding from the in-memory copy.
998  */
999 static int
showOneCharset(const char * name,FontEncPtr data)1000 showOneCharset(const char *name, FontEncPtr data)
1001 {
1002     int rc = EXIT_FAILURE;
1003 
1004     if (data != 0) {
1005 	FontMapPtr mp;
1006 	int n;
1007 
1008 	printf("# %s\n", name);
1009 	printf("STARTENCODING %s\n", data->name ? data->name : "unknown");
1010 
1011 	if (data->aliases) {
1012 	    for (n = 0; data->aliases[n]; ++n) {
1013 		printf("ALIAS %s\n", data->aliases[n]);
1014 	    }
1015 	}
1016 
1017 	if (data->row_size)
1018 	    printf("SIZE %d %d\n", data->size, data->row_size);
1019 	else if (data->size)
1020 	    printf("SIZE %d\n", data->size);
1021 
1022 	if (data->first_col)
1023 	    printf("FIRSTINDEX %d %d\n", data->first, data->first_col);
1024 	else if (data->first)
1025 	    printf("FIRSTINDEX %d\n", data->first);
1026 
1027 	for (mp = data->mappings; mp != 0; mp = mp->next) {
1028 	    printf("STARTMAPPING %s\n", fontmapTypename(mp->type));
1029 	    if (mp->type == FONT_ENCODING_UNICODE) {
1030 		int limit = (int) fontencSize(data);
1031 		unsigned ch;
1032 
1033 		if (mp->client_data == 0)
1034 		    printf("# no client_data-array\n");
1035 		if (mp->recode == 0) {
1036 		    printf("# no recode-function\n");
1037 		} else {
1038 		    trim_or_fill(mp->client_data, (int) fontencSize(data));
1039 		    for (n = 0; n < limit; ++n) {
1040 			ch = mp->recode((unsigned) n, mp->client_data);
1041 			if (ch || !n) {
1042 			    if (!fill_fontenc && ((unsigned) n == ch))
1043 				continue;
1044 			    printf("0x%04X 0x%04X\n", n, ch);
1045 			}
1046 		    }
1047 		}
1048 	    }
1049 	    printf("ENDMAPPING\n");
1050 	}
1051 	printf("# vile:tblmode\n");
1052 	printf("ENDENCODING\n");
1053 	rc = EXIT_SUCCESS;
1054     } else {
1055 	Warning("no encoding data found for %s\n", name);
1056     }
1057     return rc;
1058 }
1059 
1060 int
showFontencCharset(const char * name)1061 showFontencCharset(const char *name)
1062 {
1063     return showOneCharset(name, lookupOneFontenc(name));
1064 }
1065 
1066 /*
1067  * Returns 94, 96 or 128 for an 8-bit character-set, based on the mapping.
1068  */
1069 int
typeOfFontenc(FontEncPtr enc)1070 typeOfFontenc(FontEncPtr enc)
1071 {
1072     FontEncSimpleMapPtr mp = findUnicodeMapping(enc);
1073     int result = 0;
1074 
1075     if (mp != NULL) {
1076 	int limit = (int) fontencSize(enc);
1077 	int n;
1078 	int hi = 0;
1079 	int lo = limit;
1080 
1081 	for (n = 0; n < limit; ++n) {
1082 	    UCode target = mp->map[n - mp->first];
1083 	    if (target != 0 && target != n) {
1084 		if (n > hi)
1085 		    hi = n;
1086 		if (n < lo)
1087 		    lo = n;
1088 	    }
1089 	}
1090 	if (lo >= 128 && lo < 160) {
1091 	    result = 128;
1092 	} else if (lo >= 160 && hi < 256) {
1093 	    result = 96;
1094 	} else {
1095 	    result = hi - lo + 1;
1096 	}
1097 	VERBOSE(2, ("character-set size %d\n", result));
1098     }
1099     return result;
1100 }
1101 
1102 /*
1103  * Returns 0x00 or 0x80 for an 8-bit character-set, according to where the
1104  * lowest mapping is done.
1105  */
1106 unsigned
shiftOfFontenc(FontEncPtr enc)1107 shiftOfFontenc(FontEncPtr enc)
1108 {
1109     FontEncSimpleMapPtr mp = findUnicodeMapping(enc);
1110     unsigned result = 0x80;
1111 
1112     if (mp != NULL) {
1113 	int limit = (int) fontencSize(enc);
1114 	int n;
1115 
1116 	for (n = 0; n < limit; ++n) {
1117 	    UCode target = mp->map[n - mp->first];
1118 	    if (n >= 128) {
1119 		break;
1120 	    } else if (target != 0 && target != n) {
1121 		result = 0;
1122 		break;
1123 	    }
1124 	}
1125 	VERBOSE(2, ("character-set offset %u\n", result));
1126     }
1127     return result;
1128 }
1129 
1130 #ifdef USE_ICONV
1131 /*
1132  * Display built-in encoding as ".enc" format.
1133  */
1134 int
showBuiltinCharset(const char * name)1135 showBuiltinCharset(const char *name)
1136 {
1137     FontEncPtr data = luitGetFontEnc(name, umBUILTIN);
1138     int rc = showOneCharset(name, data);
1139     luitFreeFontEnc(data);
1140     return rc;
1141 }
1142 
1143 /*
1144  * Display iconv encoding as ".enc" format.
1145  */
1146 int
showIconvCharset(const char * name)1147 showIconvCharset(const char *name)
1148 {
1149     FontEncPtr data = luitGetFontEnc(name, umICONV);
1150     int rc = showOneCharset(name, data);
1151     luitFreeFontEnc(data);
1152     return rc;
1153 }
1154 #endif /* USE_ICONV */
1155 
1156 #ifdef NO_LEAKS
1157 static void
freeFontMapRec(FontMapPtr data)1158 freeFontMapRec(FontMapPtr data)
1159 {
1160     switch (data->type) {
1161 #ifdef FONT_ENCODING_TRUETYPE
1162     case FONT_ENCODING_TRUETYPE:
1163 	/* FALLTHRU */
1164 #endif
1165     case FONT_ENCODING_UNICODE:
1166 	{
1167 	    FontEncSimpleMapPtr mapHead = data->client_data;
1168 	    free((void *) mapHead->map);
1169 	    free(mapHead);
1170 	}
1171 	break;
1172 #ifdef FONT_ENCODING_POSTSCRIPT
1173     case FONT_ENCODING_POSTSCRIPT:
1174 	{
1175 	    FontEncSimpleNamePtr mapHead = data->client_data;
1176 	    if (mapHead->map) {
1177 		int n;
1178 		for (n = 0; mapHead->map[n]; ++n) {
1179 		    free(mapHead->map[n]);
1180 		}
1181 		free(mapHead->map);
1182 	    }
1183 	    free(mapHead);
1184 	}
1185 	break;
1186 #endif /* FONT_ENCODING_POSTSCRIPT */
1187     default:
1188 	free(data->client_data);
1189 	break;
1190     }
1191     free(data);
1192 }
1193 
1194 static void
freeFontEncRec(FontEncPtr data)1195 freeFontEncRec(FontEncPtr data)
1196 {
1197     if (data != 0) {
1198 	while (data->mappings != 0) {
1199 	    FontMapPtr next = data->mappings->next;
1200 	    freeFontMapRec(data->mappings);
1201 	    data->mappings = next;
1202 	}
1203 	if (data->aliases) {
1204 	    int n;
1205 	    for (n = 0; data->aliases[n]; ++n) {
1206 		free(data->aliases[n]);
1207 	    }
1208 	    free(data->aliases);
1209 	}
1210 	free(data->name);
1211 	free(data);
1212     }
1213 }
1214 
1215 void
fontenc_leaks(void)1216 fontenc_leaks(void)
1217 {
1218     if (encodings_dir != 0) {
1219 	int enc;
1220 	for (enc = 0; encodings_dir[enc].alias != 0; ++enc) {
1221 	    freeFontEncRec(encodings_dir[enc].data);
1222 	    free(encodings_dir[enc].alias);
1223 	    free(encodings_dir[enc].path);
1224 	}
1225 	free(encodings_dir);
1226     }
1227 }
1228 #endif /* NOLEAKS */
1229