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