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