1/* 2Copyright (c) 1998-2001 by Juliusz Chroboczek 3 4Permission is hereby granted, free of charge, to any person obtaining a copy 5of this software and associated documentation files (the "Software"), to deal 6in the Software without restriction, including without limitation the rights 7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8copies of the Software, and to permit persons to whom the Software is 9furnished to do so, subject to the following conditions: 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20THE SOFTWARE. 21*/ 22 23/* Parser for encoding files */ 24 25/* This code assumes that we are using ASCII. We don't use the ctype 26 functions, as they depend on the current locale. On the other 27 hand, we do use strcasecmp, but only on strings that we've checked 28 to be pure ASCII. Bloody ``Code Set Independence''. */ 29 30#ifdef HAVE_CONFIG_H 31#include <config.h> 32#endif 33 34#include <string.h> 35#include <strings.h> 36#include <stdio.h> 37 38#include <stdlib.h> 39 40#include <fcntl.h> 41#ifdef O_CLOEXEC 42#define FOPEN_CLOEXEC "e" 43#else 44#define FOPEN_CLOEXEC "" 45#endif 46 47 48#include "zlib.h" 49typedef gzFile FontFilePtr; 50 51#define FontFileGetc(f) gzgetc(f) 52#define FontFileOpen(filename) gzopen(filename, "rb") 53#define FontFileClose(f) gzclose(f) 54 55#define MAXFONTFILENAMELEN 1024 56#define MAXFONTNAMELEN 1024 57 58#include <X11/fonts/fontenc.h> 59#include "fontencI.h" 60#include "reallocarray.h" 61 62#define MAXALIASES 20 63 64#define EOF_TOKEN -1 65#define ERROR_TOKEN -2 66#define EOL_TOKEN 0 67#define NUMBER_TOKEN 1 68#define KEYWORD_TOKEN 2 69 70#define EOF_LINE -1 71#define ERROR_LINE -2 72#define STARTENCODING_LINE 1 73#define STARTMAPPING_LINE 2 74#define ENDMAPPING_LINE 3 75#define CODE_LINE 4 76#define CODE_RANGE_LINE 5 77#define CODE_UNDEFINE_LINE 6 78#define NAME_LINE 7 79#define SIZE_LINE 8 80#define ALIAS_LINE 9 81#define FIRSTINDEX_LINE 10 82 83/* Return from lexer */ 84#define MAXKEYWORDLEN 100 85 86static long number_value; 87static char keyword_value[MAXKEYWORDLEN + 1]; 88 89static long value1, value2, value3; 90 91/* Lexer code */ 92 93/* Skip to the beginning of new line */ 94static void 95skipEndOfLine(FontFilePtr f, int c) 96{ 97 if (c == 0) 98 c = FontFileGetc(f); 99 100 for (;;) 101 if (c <= 0 || c == '\n') 102 return; 103 else 104 c = FontFileGetc(f); 105} 106 107/* Get a number; we're at the first digit. */ 108static unsigned 109getnum(FontFilePtr f, int c, int *cp) 110{ 111 unsigned n = 0; 112 int base = 10; 113 114 /* look for `0' or `0x' prefix */ 115 if (c == '0') { 116 c = FontFileGetc(f); 117 base = 8; 118 if (c == 'x' || c == 'X') { 119 base = 16; 120 c = FontFileGetc(f); 121 } 122 } 123 124 /* accumulate digits */ 125 for (;;) { 126 if ('0' <= c && c <= '9') { 127 n *= base; 128 n += c - '0'; 129 } 130 else if ('a' <= c && c <= 'f') { 131 n *= base; 132 n += c - 'a' + 10; 133 } 134 else if ('A' <= c && c <= 'F') { 135 n *= base; 136 n += c - 'A' + 10; 137 } 138 else 139 break; 140 c = FontFileGetc(f); 141 } 142 143 *cp = c; 144 return n; 145} 146 147/* Skip to beginning of new line; return 1 if only whitespace was found. */ 148static int 149endOfLine(FontFilePtr f, int c) 150{ 151 if (c == 0) 152 c = FontFileGetc(f); 153 154 for (;;) { 155 if (c <= 0 || c == '\n') 156 return 1; 157 else if (c == '#') { 158 skipEndOfLine(f, c); 159 return 1; 160 } 161 else if (c == ' ' || c == '\t') { 162 skipEndOfLine(f, c); 163 return 0; 164 } 165 c = FontFileGetc(f); 166 } 167} 168 169/* Get a token; we're at first char */ 170static int 171gettoken(FontFilePtr f, int c, int *cp) 172{ 173 char *p; 174 175 if (c <= 0) 176 c = FontFileGetc(f); 177 178 if (c <= 0) { 179 return EOF_TOKEN; 180 } 181 182 while (c == ' ' || c == '\t') 183 c = FontFileGetc(f); 184 185 if (c == '\n') { 186 return EOL_TOKEN; 187 } 188 else if (c == '#') { 189 skipEndOfLine(f, c); 190 return EOL_TOKEN; 191 } 192 else if (c >= '0' && c <= '9') { 193 number_value = getnum(f, c, cp); 194 return NUMBER_TOKEN; 195 } 196 else if ((c >= 'A' && c <= 'Z') || 197 (c >= 'a' && c <= 'z') || 198 c == '/' || c == '_' || c == '-' || c == '.') { 199 p = keyword_value; 200 *p++ = c; 201 while (p - keyword_value < MAXKEYWORDLEN) { 202 c = FontFileGetc(f); 203 if (c <= ' ' || c > '~' || c == '#') 204 break; 205 *p++ = c; 206 } 207 *cp = c; 208 *p = '\0'; 209 return KEYWORD_TOKEN; 210 } 211 else { 212 *cp = c; 213 return ERROR_TOKEN; 214 } 215} 216 217/* Parse a line. 218 * Always skips to the beginning of a new line, even if an error occurs */ 219static int 220getnextline(FontFilePtr f) 221{ 222 int c, token; 223 224 c = FontFileGetc(f); 225 if (c <= 0) 226 return EOF_LINE; 227 228 again: 229 token = gettoken(f, c, &c); 230 231 switch (token) { 232 case EOF_TOKEN: 233 return EOF_LINE; 234 case EOL_TOKEN: 235 /* empty line */ 236 c = FontFileGetc(f); 237 goto again; 238 case NUMBER_TOKEN: 239 value1 = number_value; 240 token = gettoken(f, c, &c); 241 switch (token) { 242 case NUMBER_TOKEN: 243 value2 = number_value; 244 token = gettoken(f, c, &c); 245 switch (token) { 246 case NUMBER_TOKEN: 247 value3 = number_value; 248 return CODE_RANGE_LINE; 249 case EOL_TOKEN: 250 return CODE_LINE; 251 default: 252 skipEndOfLine(f, c); 253 return ERROR_LINE; 254 } 255 case KEYWORD_TOKEN: 256 if (!endOfLine(f, c)) 257 return ERROR_LINE; 258 else 259 return NAME_LINE; 260 default: 261 skipEndOfLine(f, c); 262 return ERROR_LINE; 263 } 264 case KEYWORD_TOKEN: 265 if (!strcasecmp(keyword_value, "STARTENCODING")) { 266 token = gettoken(f, c, &c); 267 if (token == KEYWORD_TOKEN) { 268 if (endOfLine(f, c)) 269 return STARTENCODING_LINE; 270 else 271 return ERROR_LINE; 272 } 273 else { 274 skipEndOfLine(f, c); 275 return ERROR_LINE; 276 } 277 } 278 else if (!strcasecmp(keyword_value, "ALIAS")) { 279 token = gettoken(f, c, &c); 280 if (token == KEYWORD_TOKEN) { 281 if (endOfLine(f, c)) 282 return ALIAS_LINE; 283 else 284 return ERROR_LINE; 285 } 286 else { 287 skipEndOfLine(f, c); 288 return ERROR_LINE; 289 } 290 } 291 else if (!strcasecmp(keyword_value, "SIZE")) { 292 token = gettoken(f, c, &c); 293 if (token == NUMBER_TOKEN) { 294 value1 = number_value; 295 token = gettoken(f, c, &c); 296 switch (token) { 297 case NUMBER_TOKEN: 298 value2 = number_value; 299 return SIZE_LINE; 300 case EOL_TOKEN: 301 value2 = 0; 302 return SIZE_LINE; 303 default: 304 skipEndOfLine(f, c); 305 return ERROR_LINE; 306 } 307 } 308 else { 309 skipEndOfLine(f, c); 310 return ERROR_LINE; 311 } 312 } 313 else if (!strcasecmp(keyword_value, "FIRSTINDEX")) { 314 token = gettoken(f, c, &c); 315 if (token == NUMBER_TOKEN) { 316 value1 = number_value; 317 token = gettoken(f, c, &c); 318 switch (token) { 319 case NUMBER_TOKEN: 320 value2 = number_value; 321 return FIRSTINDEX_LINE; 322 case EOL_TOKEN: 323 value2 = 0; 324 return FIRSTINDEX_LINE; 325 default: 326 skipEndOfLine(f, c); 327 return ERROR_LINE; 328 } 329 } 330 else { 331 skipEndOfLine(f, c); 332 return ERROR_LINE; 333 } 334 } 335 else if (!strcasecmp(keyword_value, "STARTMAPPING")) { 336 keyword_value[0] = 0; 337 value1 = 0; 338 value2 = 0; 339 /* first a keyword */ 340 token = gettoken(f, c, &c); 341 if (token != KEYWORD_TOKEN) { 342 skipEndOfLine(f, c); 343 return ERROR_LINE; 344 } 345 346 /* optional first integer */ 347 token = gettoken(f, c, &c); 348 if (token == NUMBER_TOKEN) { 349 value1 = number_value; 350 } 351 else if (token == EOL_TOKEN) { 352 return STARTMAPPING_LINE; 353 } 354 else { 355 skipEndOfLine(f, c); 356 return ERROR_LINE; 357 } 358 359 /* optional second integer */ 360 token = gettoken(f, c, &c); 361 if (token == NUMBER_TOKEN) { 362 value2 = number_value; 363 } 364 else if (token == EOL_TOKEN) { 365 return STARTMAPPING_LINE; 366 } 367 else { 368 skipEndOfLine(f, c); 369 return ERROR_LINE; 370 } 371 372 if (!endOfLine(f, c)) 373 return ERROR_LINE; 374 else { 375 return STARTMAPPING_LINE; 376 } 377 } 378 else if (!strcasecmp(keyword_value, "UNDEFINE")) { 379 /* first integer */ 380 token = gettoken(f, c, &c); 381 if (token != NUMBER_TOKEN) { 382 skipEndOfLine(f, c); 383 return ERROR_LINE; 384 } 385 value1 = number_value; 386 /* optional second integer */ 387 token = gettoken(f, c, &c); 388 if (token == EOL_TOKEN) { 389 value2 = value1; 390 return CODE_UNDEFINE_LINE; 391 } 392 else if (token == NUMBER_TOKEN) { 393 value2 = number_value; 394 if (endOfLine(f, c)) { 395 return CODE_UNDEFINE_LINE; 396 } 397 else 398 return ERROR_LINE; 399 } 400 else { 401 skipEndOfLine(f, c); 402 return ERROR_LINE; 403 } 404 } 405 else if (!strcasecmp(keyword_value, "ENDENCODING")) { 406 if (endOfLine(f, c)) 407 return EOF_LINE; 408 else 409 return ERROR_LINE; 410 } 411 else if (!strcasecmp(keyword_value, "ENDMAPPING")) { 412 if (endOfLine(f, c)) 413 return ENDMAPPING_LINE; 414 else 415 return ERROR_LINE; 416 } 417 else { 418 skipEndOfLine(f, c); 419 return ERROR_LINE; 420 } 421 default: 422 return ERROR_LINE; 423 } 424} 425 426static void 427install_mapping(FontEncPtr encoding, FontMapPtr mapping) 428{ 429 FontMapPtr m; 430 431 if (encoding->mappings == NULL) 432 encoding->mappings = mapping; 433 else { 434 m = encoding->mappings; 435 while (m->next != NULL) 436 m = m->next; 437 m->next = mapping; 438 } 439 mapping->next = NULL; 440 mapping->encoding = encoding; 441} 442 443static int 444setCode(unsigned from, unsigned to, unsigned row_size, 445 unsigned *first, unsigned *last, 446 unsigned *encsize, unsigned short **enc) 447{ 448 unsigned index, i; 449 450 unsigned short *newenc; 451 452 if (from > 0xFFFF) 453 return 0; /* success */ 454 455 if (row_size == 0) 456 index = from; 457 else { 458 if ((value1 & 0xFF) >= row_size) 459 return 0; /* ignore out of range mappings */ 460 index = (from >> 8) * row_size + (from & 0xFF); 461 } 462 463 /* Optimize away useless identity mappings. This is only expected 464 to be useful with linear encodings. */ 465 if (index == to && (index < *first || index > *last)) 466 return 0; 467 if (*encsize == 0) { 468 *encsize = (index < 256) ? 256 : 0x10000; 469 *enc = Xmallocarray(*encsize, sizeof(unsigned short)); 470 if (*enc == NULL) { 471 *encsize = 0; 472 return 1; 473 } 474 } 475 else if (*encsize <= index) { 476 *encsize = 0x10000; 477 newenc = Xreallocarray(*enc, *encsize, sizeof(unsigned short)); 478 if (newenc == NULL) 479 return 1; 480 *enc = newenc; 481 } 482 if (*first > *last) { 483 *first = *last = index; 484 } 485 if (index < *first) { 486 for (i = index; i < *first; i++) 487 (*enc)[i] = i; 488 *first = index; 489 } 490 if (index > *last) { 491 for (i = *last + 1; i <= index; i++) 492 (*enc)[i] = i; 493 *last = index; 494 } 495 (*enc)[index] = to; 496 return 0; 497} 498 499/* Parser. If headerOnly is true, we're only interested in the 500 data contained in the encoding file's header. */ 501 502/* As font encodings are currently never freed, the allocations done 503 by this function are mostly its private business. Note, however, 504 that FontEncIdentify needs to free the header fields -- so if you 505 change this function, you may need to change FontEncIdentify. */ 506 507/* I want a garbage collector. */ 508 509static FontEncPtr 510parseEncodingFile(FontFilePtr f, int headerOnly) 511{ 512 int line; 513 514 unsigned short *enc = NULL; 515 char **nam = NULL, **newnam; 516 unsigned i, first = 0xFFFF, last = 0, encsize = 0, namsize = 0; 517 FontEncPtr encoding = NULL; 518 FontMapPtr mapping = NULL; 519 FontEncSimpleMapPtr sm; 520 FontEncSimpleNamePtr sn; 521 char *aliases[MAXALIASES] = { NULL }; 522 int numaliases = 0; 523 524#if 0 525 /* GCC complains about unused labels. Please fix GCC rather than 526 obfuscating my code. */ 527 no_encoding: 528#endif 529 line = getnextline(f); 530 switch (line) { 531 case EOF_LINE: 532 goto error; 533 case STARTENCODING_LINE: 534 encoding = malloc(sizeof(FontEncRec)); 535 if (encoding == NULL) 536 goto error; 537 encoding->name = strdup(keyword_value); 538 if (encoding->name == NULL) 539 goto error; 540 encoding->size = 256; 541 encoding->row_size = 0; 542 encoding->mappings = NULL; 543 encoding->next = NULL; 544 encoding->first = encoding->first_col = 0; 545 goto no_mapping; 546 default: 547 goto error; 548 } 549 550 no_mapping: 551 line = getnextline(f); 552 switch (line) { 553 case EOF_LINE: 554 goto done; 555 case ALIAS_LINE: 556 if (numaliases < MAXALIASES) { 557 aliases[numaliases] = strdup(keyword_value); 558 if (aliases[numaliases] == NULL) 559 goto error; 560 numaliases++; 561 } 562 goto no_mapping; 563 case SIZE_LINE: 564 encoding->size = value1; 565 encoding->row_size = value2; 566 goto no_mapping; 567 case FIRSTINDEX_LINE: 568 encoding->first = value1; 569 encoding->first_col = value2; 570 goto no_mapping; 571 case STARTMAPPING_LINE: 572 if (headerOnly) 573 goto done; 574 if (!strcasecmp(keyword_value, "unicode")) { 575 mapping = malloc(sizeof(FontMapRec)); 576 if (mapping == NULL) 577 goto error; 578 mapping->type = FONT_ENCODING_UNICODE; 579 mapping->pid = 0; 580 mapping->eid = 0; 581 mapping->recode = NULL; 582 mapping->name = NULL; 583 mapping->client_data = NULL; 584 mapping->next = NULL; 585 goto mapping; 586 } 587 else if (!strcasecmp(keyword_value, "cmap")) { 588 mapping = malloc(sizeof(FontMapRec)); 589 if (mapping == NULL) 590 goto error; 591 mapping->type = FONT_ENCODING_TRUETYPE; 592 mapping->pid = value1; 593 mapping->eid = value2; 594 mapping->recode = NULL; 595 mapping->name = NULL; 596 mapping->client_data = NULL; 597 mapping->next = NULL; 598 goto mapping; 599 } 600 else if (!strcasecmp(keyword_value, "postscript")) { 601 mapping = malloc(sizeof(FontMapRec)); 602 if (mapping == NULL) 603 goto error; 604 mapping->type = FONT_ENCODING_POSTSCRIPT; 605 mapping->pid = 0; 606 mapping->eid = 0; 607 mapping->recode = NULL; 608 mapping->name = NULL; 609 mapping->client_data = NULL; 610 mapping->next = NULL; 611 goto string_mapping; 612 } 613 else { /* unknown mapping type -- ignore */ 614 goto skipmapping; 615 } 616 /* NOTREACHED */ 617 goto error; 618 default: 619 goto no_mapping; /* ignore unknown lines */ 620 } 621 622 skipmapping: 623 line = getnextline(f); 624 switch (line) { 625 case ENDMAPPING_LINE: 626 goto no_mapping; 627 case EOF_LINE: 628 goto error; 629 default: 630 goto skipmapping; 631 } 632 633 mapping: 634 line = getnextline(f); 635 switch (line) { 636 case EOF_LINE: 637 goto error; 638 case ENDMAPPING_LINE: 639 mapping->recode = FontEncSimpleRecode; 640 mapping->name = FontEncUndefinedName; 641 mapping->client_data = sm = malloc(sizeof(FontEncSimpleMapRec)); 642 if (sm == NULL) 643 goto error; 644 sm->row_size = encoding->row_size; 645 if (first <= last) { 646 unsigned short *newmap; 647 648 sm->first = first; 649 sm->len = last - first + 1; 650 newmap = Xmallocarray(sm->len, sizeof(unsigned short)); 651 if (newmap == NULL) { 652 free(sm); 653 mapping->client_data = sm = NULL; 654 goto error; 655 } 656 for (i = 0; i < sm->len; i++) 657 newmap[i] = enc[first + i]; 658 sm->map = newmap; 659 } 660 else { 661 sm->first = 0; 662 sm->len = 0; 663 sm->map = NULL; 664 } 665 install_mapping(encoding, mapping); 666 mapping = NULL; 667 first = 0xFFFF; 668 last = 0; 669 goto no_mapping; 670 671 case CODE_LINE: 672 if (setCode(value1, value2, encoding->row_size, 673 &first, &last, &encsize, &enc)) 674 goto error; 675 goto mapping; 676 677 case CODE_RANGE_LINE: 678 if (value1 > 0x10000) 679 value1 = 0x10000; 680 if (value2 > 0x10000) 681 value2 = 0x10000; 682 if (value2 < value1) 683 goto mapping; 684 /* Do the last value first to avoid having to realloc() */ 685 if (setCode(value2, value3 + (value2 - value1), encoding->row_size, 686 &first, &last, &encsize, &enc)) 687 goto error; 688 for (i = value1; i < value2; i++) { 689 if (setCode(i, value3 + (i - value1), encoding->row_size, 690 &first, &last, &encsize, &enc)) 691 goto error; 692 } 693 goto mapping; 694 695 case CODE_UNDEFINE_LINE: 696 if (value1 > 0x10000) 697 value1 = 0x10000; 698 if (value2 > 0x10000) 699 value2 = 0x10000; 700 if (value2 < value1) 701 goto mapping; 702 /* Do the last value first to avoid having to realloc() */ 703 if (setCode(value2, 0, encoding->row_size, 704 &first, &last, &encsize, &enc)) 705 goto error; 706 for (i = value1; i < value2; i++) { 707 if (setCode(i, 0, encoding->row_size, 708 &first, &last, &encsize, &enc)) 709 goto error; 710 } 711 goto mapping; 712 713 default: 714 goto mapping; /* ignore unknown lines */ 715 } 716 717 string_mapping: 718 line = getnextline(f); 719 switch (line) { 720 case EOF_LINE: 721 goto error; 722 case ENDMAPPING_LINE: 723 mapping->recode = FontEncUndefinedRecode; 724 mapping->name = FontEncSimpleName; 725 mapping->client_data = sn = malloc(sizeof(FontEncSimpleNameRec)); 726 if (sn == NULL) 727 goto error; 728 if (first > last) { 729 free(sn); 730 mapping->client_data = sn = NULL; 731 goto error; 732 } 733 sn->first = first; 734 sn->len = last - first + 1; 735 sn->map = Xmallocarray(sn->len, sizeof(char *)); 736 if (sn->map == NULL) { 737 free(sn); 738 mapping->client_data = sn = NULL; 739 goto error; 740 } 741 for (i = 0; i < sn->len; i++) 742 sn->map[i] = nam[first + i]; 743 install_mapping(encoding, mapping); 744 mapping = NULL; 745 first = 0xFFFF; 746 last = 0; 747 goto no_mapping; 748 case NAME_LINE: 749 if (value1 >= 0x10000) 750 goto string_mapping; 751 if (namsize == 0) { 752 namsize = (value1) < 256 ? 256 : 0x10000; 753 nam = Xmallocarray(namsize, sizeof(char *)); 754 if (nam == NULL) { 755 namsize = 0; 756 goto error; 757 } 758 } 759 else if (namsize <= value1) { 760 namsize = 0x10000; 761 if ((newnam = (char **) realloc(nam, namsize)) == NULL) 762 goto error; 763 nam = newnam; 764 } 765 if (first > last) { 766 first = last = value1; 767 } 768 if (value1 < first) { 769 for (i = value1; i < first; i++) 770 nam[i] = NULL; 771 first = value1; 772 } 773 if (value1 > last) { 774 for (i = last + 1; i <= value1; i++) 775 nam[i] = NULL; 776 last = value1; 777 } 778 nam[value1] = strdup(keyword_value); 779 if (nam[value1] == NULL) { 780 goto error; 781 } 782 goto string_mapping; 783 784 default: 785 goto string_mapping; /* ignore unknown lines */ 786 } 787 788 done: 789 if (encsize) { 790 free(enc); 791 encsize = 0; 792 enc = NULL; 793 } 794 if (namsize) { 795 free(nam); /* don't free entries! */ 796 namsize = 0; 797 nam = NULL; 798 } 799 800 encoding->aliases = NULL; 801 if (numaliases) { 802 encoding->aliases = Xmallocarray(numaliases + 1, sizeof(char *)); 803 if (encoding->aliases == NULL) 804 goto error; 805 for (i = 0; i < numaliases; i++) 806 encoding->aliases[i] = aliases[i]; 807 encoding->aliases[numaliases] = NULL; 808 } 809 810 return encoding; 811 812 error: 813 if (encsize) { 814 free(enc); 815 encsize = 0; 816 } 817 if (namsize) { 818 for (i = first; i <= last; i++) 819 free(nam[i]); 820 free(nam); 821 } 822 if (mapping) { 823 free(mapping->client_data); 824 free(mapping); 825 } 826 if (encoding) { 827 FontMapPtr nextmap; 828 829 free(encoding->name); 830 for (mapping = encoding->mappings; mapping; mapping = nextmap) { 831 free(mapping->client_data); 832 nextmap = mapping->next; 833 free(mapping); 834 } 835 free(encoding); 836 } 837 for (i = 0; i < numaliases; i++) 838 free(aliases[i]); 839 /* We don't need to free sn and sm as they handled locally in the body. */ 840 return NULL; 841} 842 843const char * 844FontEncDirectory(void) 845{ 846 static const char *dir = NULL; 847 848 if (dir == NULL) { 849 const char *c = getenv("FONT_ENCODINGS_DIRECTORY"); 850 851 if (c) { 852 dir = strdup(c); 853 if (!dir) 854 return NULL; 855 } 856 else { 857 dir = FONT_ENCODINGS_DIRECTORY; 858 } 859 } 860 return dir; 861} 862 863static void 864parseFontFileName(const char *fontFileName, char *buf, char *dir) 865{ 866 const char *p; 867 char *q, *lastslash; 868 869 for (p = fontFileName, q = dir, lastslash = NULL; *p; p++, q++) { 870 *q = *p; 871 if (*p == '/') 872 lastslash = q + 1; 873 } 874 875 if (!lastslash) 876 lastslash = dir; 877 878 *lastslash = '\0'; 879 880 if (buf && strlen(dir) + 14 < MAXFONTFILENAMELEN) { 881 snprintf(buf, MAXFONTFILENAMELEN, "%s%s", dir, "encodings.dir"); 882 } 883} 884 885static FontEncPtr 886FontEncReallyReallyLoad(const char *charset, 887 const char *dirname, const char *dir) 888{ 889 FontFilePtr f; 890 FILE *file; 891 FontEncPtr encoding; 892 char file_name[MAXFONTFILENAMELEN], encoding_name[MAXFONTNAMELEN], 893 buf[MAXFONTFILENAMELEN]; 894 int count, n; 895 static char format[24] = ""; 896 897 /* As we don't really expect to open encodings that often, we don't 898 take the trouble of caching encodings directories. */ 899 900 if ((file = fopen(dirname, "r" FOPEN_CLOEXEC)) == NULL) { 901 return NULL; 902 } 903 904 count = fscanf(file, "%d\n", &n); 905 if (count == EOF || count != 1) { 906 fclose(file); 907 return NULL; 908 } 909 910 encoding = NULL; 911 if (!format[0]) { 912 snprintf(format, sizeof(format), "%%%ds %%%d[^\n]\n", 913 (int) sizeof(encoding_name) - 1, (int) sizeof(file_name) - 1); 914 } 915 for (;;) { 916 count = fscanf(file, format, encoding_name, file_name); 917 if (count == EOF) 918 break; 919 if (count != 2) 920 break; 921 922 if (!strcasecmp(encoding_name, charset)) { 923 /* Found it */ 924 if (file_name[0] != '/') { 925 if (strlen(dir) + strlen(file_name) >= MAXFONTFILENAMELEN) { 926 fclose(file); 927 return NULL; 928 } 929 snprintf(buf, MAXFONTFILENAMELEN, "%s%s", dir, file_name); 930 } 931 else { 932 snprintf(buf, MAXFONTFILENAMELEN, "%s", file_name); 933 } 934 935 f = FontFileOpen(buf); 936 if (f == NULL) { 937 fclose(file); 938 return NULL; 939 } 940 encoding = parseEncodingFile(f, 0); 941 FontFileClose(f); 942 break; 943 } 944 } 945 946 fclose(file); 947 948 return encoding; 949} 950 951/* Parser ntrypoint -- used by FontEncLoad */ 952FontEncPtr 953FontEncReallyLoad(const char *charset, const char *fontFileName) 954{ 955 FontEncPtr encoding; 956 char dir[MAXFONTFILENAMELEN], dirname[MAXFONTFILENAMELEN]; 957 const char *d; 958 959 if (fontFileName) { 960 parseFontFileName(fontFileName, dirname, dir); 961 encoding = FontEncReallyReallyLoad(charset, dirname, dir); 962 if (encoding) 963 return (encoding); 964 } 965 966 d = FontEncDirectory(); 967 if (d) { 968 parseFontFileName(d, NULL, dir); 969 encoding = FontEncReallyReallyLoad(charset, d, dir); 970 return encoding; 971 } 972 973 return NULL; 974} 975 976/* Return a NULL-terminated array of encoding names. Note that this 977 * function has incestuous knowledge of the allocations done by 978 * parseEncodingFile. */ 979 980char ** 981FontEncIdentify(const char *fileName) 982{ 983 FontFilePtr f; 984 FontEncPtr encoding; 985 char **names, **name, **alias; 986 int numaliases; 987 988 if ((f = FontFileOpen(fileName)) == NULL) { 989 return NULL; 990 } 991 encoding = parseEncodingFile(f, 1); 992 FontFileClose(f); 993 994 if (!encoding) 995 return NULL; 996 997 numaliases = 0; 998 if (encoding->aliases) 999 for (alias = encoding->aliases; *alias; alias++) 1000 numaliases++; 1001 1002 names = Xmallocarray(numaliases + 2, sizeof(char *)); 1003 if (names == NULL) { 1004 free(encoding->aliases); 1005 free(encoding); 1006 return NULL; 1007 } 1008 1009 name = names; 1010 *(name++) = encoding->name; 1011 if (numaliases > 0) 1012 for (alias = encoding->aliases; *alias; alias++, name++) 1013 *name = *alias; 1014 1015 *name = NULL; 1016 free(encoding->aliases); 1017 free(encoding); 1018 1019 return names; 1020} 1021