fcstr.c revision c9710b42
1/* 2 * fontconfig/src/fcstr.c 3 * 4 * Copyright © 2000 Keith Packard 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of the author(s) not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. The authors make no 13 * representations about the suitability of this software for any purpose. It 14 * is provided "as is" without express or implied warranty. 15 * 16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 * PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25#include "fcint.h" 26#include <stdlib.h> 27#include <ctype.h> 28#include <string.h> 29#ifdef HAVE_REGEX_H 30#include <regex.h> 31#endif 32 33 34/* Objects MT-safe for readonly access. */ 35 36FcChar8 * 37FcStrCopy (const FcChar8 *s) 38{ 39 return FcStrdup (s); 40} 41 42FcChar8 * 43FcStrPlus (const FcChar8 *s1, const FcChar8 *s2) 44{ 45 int s1l = strlen ((char *) s1); 46 int s2l = strlen ((char *) s2); 47 int l = s1l + s2l + 1; 48 FcChar8 *s = malloc (l); 49 50 if (!s) 51 return 0; 52 memcpy (s, s1, s1l); 53 memcpy (s + s1l, s2, s2l + 1); 54 return s; 55} 56 57void 58FcStrFree (FcChar8 *s) 59{ 60 free (s); 61} 62 63 64#include "../fc-case/fccase.h" 65 66#define FcCaseFoldUpperCount(cf) \ 67 ((cf)->method == FC_CASE_FOLD_FULL ? 1 : (cf)->count) 68 69typedef struct _FcCaseWalker { 70 const FcChar8 *read; 71 const FcChar8 *src; 72 FcChar8 utf8[FC_MAX_CASE_FOLD_CHARS + 1]; 73} FcCaseWalker; 74 75static void 76FcStrCaseWalkerInit (const FcChar8 *src, FcCaseWalker *w) 77{ 78 w->src = src; 79 w->read = 0; 80} 81 82static FcChar8 83FcStrCaseWalkerLong (FcCaseWalker *w, FcChar8 r) 84{ 85 FcChar32 ucs4; 86 int slen; 87 int len = strlen((char*)w->src); 88 89 slen = FcUtf8ToUcs4 (w->src - 1, &ucs4, len + 1); 90 if (slen <= 0) 91 return r; 92 if (FC_MIN_FOLD_CHAR <= ucs4 && ucs4 <= FC_MAX_FOLD_CHAR) 93 { 94 int min = 0; 95 int max = FC_NUM_CASE_FOLD; 96 97 while (min <= max) 98 { 99 int mid = (min + max) >> 1; 100 FcChar32 low = fcCaseFold[mid].upper; 101 FcChar32 high = low + FcCaseFoldUpperCount (&fcCaseFold[mid]); 102 103 if (high <= ucs4) 104 min = mid + 1; 105 else if (ucs4 < low) 106 max = mid - 1; 107 else 108 { 109 const FcCaseFold *fold = &fcCaseFold[mid]; 110 int dlen; 111 112 switch (fold->method) { 113 case FC_CASE_FOLD_EVEN_ODD: 114 if ((ucs4 & 1) != (fold->upper & 1)) 115 return r; 116 /* fall through ... */ 117 default: 118 dlen = FcUcs4ToUtf8 (ucs4 + fold->offset, w->utf8); 119 break; 120 case FC_CASE_FOLD_FULL: 121 dlen = fold->count; 122 memcpy (w->utf8, fcCaseFoldChars + fold->offset, dlen); 123 break; 124 } 125 126 /* consume rest of src utf-8 bytes */ 127 w->src += slen - 1; 128 129 /* read from temp buffer */ 130 w->utf8[dlen] = '\0'; 131 w->read = w->utf8; 132 return *w->read++; 133 } 134 } 135 } 136 return r; 137} 138 139static FcChar8 140FcStrCaseWalkerNext (FcCaseWalker *w, const char *delims) 141{ 142 FcChar8 r; 143 144 if (w->read) 145 { 146 if ((r = *w->read++)) 147 return r; 148 w->read = 0; 149 } 150 do 151 { 152 r = *w->src++; 153 } while (r != 0 && delims && strchr (delims, r)); 154 155 if ((r & 0xc0) == 0xc0) 156 return FcStrCaseWalkerLong (w, r); 157 if ('A' <= r && r <= 'Z') 158 r = r - 'A' + 'a'; 159 return r; 160} 161 162FcChar8 * 163FcStrDowncase (const FcChar8 *s) 164{ 165 FcCaseWalker w; 166 int len = 0; 167 FcChar8 *dst, *d; 168 169 FcStrCaseWalkerInit (s, &w); 170 while (FcStrCaseWalkerNext (&w, NULL)) 171 len++; 172 d = dst = malloc (len + 1); 173 if (!d) 174 return 0; 175 FcStrCaseWalkerInit (s, &w); 176 while ((*d++ = FcStrCaseWalkerNext (&w, NULL))); 177 return dst; 178} 179 180int 181FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) 182{ 183 FcCaseWalker w1, w2; 184 FcChar8 c1, c2; 185 186 if (s1 == s2) return 0; 187 188 FcStrCaseWalkerInit (s1, &w1); 189 FcStrCaseWalkerInit (s2, &w2); 190 191 for (;;) 192 { 193 c1 = FcStrCaseWalkerNext (&w1, NULL); 194 c2 = FcStrCaseWalkerNext (&w2, NULL); 195 if (!c1 || (c1 != c2)) 196 break; 197 } 198 return (int) c1 - (int) c2; 199} 200 201int 202FcStrCmpIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) 203{ 204 return FcStrCmpIgnoreCaseAndDelims (s1, s2, (const FcChar8 *)" "); 205} 206 207int 208FcStrCmpIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *delims) 209{ 210 FcCaseWalker w1, w2; 211 FcChar8 c1, c2; 212 213 if (s1 == s2) return 0; 214 215 FcStrCaseWalkerInit (s1, &w1); 216 FcStrCaseWalkerInit (s2, &w2); 217 218 for (;;) 219 { 220 c1 = FcStrCaseWalkerNext (&w1, (const char *)delims); 221 c2 = FcStrCaseWalkerNext (&w2, (const char *)delims); 222 if (!c1 || (c1 != c2)) 223 break; 224 } 225 return (int) c1 - (int) c2; 226} 227 228int 229FcStrCmp (const FcChar8 *s1, const FcChar8 *s2) 230{ 231 FcChar8 c1, c2; 232 233 if (s1 == s2) 234 return 0; 235 for (;;) 236 { 237 c1 = *s1++; 238 c2 = *s2++; 239 if (!c1 || c1 != c2) 240 break; 241 } 242 return (int) c1 - (int) c2; 243} 244 245#ifdef USE_REGEX 246static FcBool 247_FcStrRegexCmp (const FcChar8 *s, const FcChar8 *regex, int cflags, int eflags) 248{ 249 int ret = -1; 250 regex_t reg; 251 252 if ((ret = regcomp (®, (const char *)regex, cflags)) != 0) 253 { 254 if (FcDebug () & FC_DBG_MATCHV) 255 { 256 char buf[512]; 257 258 regerror (ret, ®, buf, 512); 259 printf("Regexp compile error: %s\n", buf); 260 } 261 return FcFalse; 262 } 263 ret = regexec (®, (const char *)s, 0, NULL, eflags); 264 if (ret != 0) 265 { 266 if (FcDebug () & FC_DBG_MATCHV) 267 { 268 char buf[512]; 269 270 regerror (ret, ®, buf, 512); 271 printf("Regexp exec error: %s\n", buf); 272 } 273 } 274 regfree (®); 275 276 return ret == 0 ? FcTrue : FcFalse; 277} 278#else 279# define _FcStrRegexCmp(_s_, _regex_, _cflags_, _eflags_) (FcFalse) 280#endif 281 282FcBool 283FcStrRegexCmp (const FcChar8 *s, const FcChar8 *regex) 284{ 285 return _FcStrRegexCmp (s, regex, REG_EXTENDED | REG_NOSUB, 0); 286} 287 288FcBool 289FcStrRegexCmpIgnoreCase (const FcChar8 *s, const FcChar8 *regex) 290{ 291 return _FcStrRegexCmp (s, regex, REG_EXTENDED | REG_NOSUB | REG_ICASE, 0); 292} 293 294/* 295 * Return a hash value for a string 296 */ 297 298FcChar32 299FcStrHashIgnoreCase (const FcChar8 *s) 300{ 301 FcChar32 h = 0; 302 FcCaseWalker w; 303 FcChar8 c; 304 305 FcStrCaseWalkerInit (s, &w); 306 while ((c = FcStrCaseWalkerNext (&w, NULL))) 307 h = ((h << 3) ^ (h >> 3)) ^ c; 308 return h; 309} 310 311/* 312 * Is the head of s1 equal to s2? 313 */ 314 315static FcBool 316FcStrIsAtIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) 317{ 318 FcCaseWalker w1, w2; 319 FcChar8 c1, c2; 320 321 FcStrCaseWalkerInit (s1, &w1); 322 FcStrCaseWalkerInit (s2, &w2); 323 324 for (;;) 325 { 326 c1 = FcStrCaseWalkerNext (&w1, " "); 327 c2 = FcStrCaseWalkerNext (&w2, " "); 328 if (!c1 || (c1 != c2)) 329 break; 330 } 331 return c1 == c2 || !c2; 332} 333 334/* 335 * Does s1 contain an instance of s2 (ignoring blanks and case)? 336 */ 337 338const FcChar8 * 339FcStrContainsIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) 340{ 341 while (*s1) 342 { 343 if (FcStrIsAtIgnoreBlanksAndCase (s1, s2)) 344 return s1; 345 s1++; 346 } 347 return 0; 348} 349 350static FcBool 351FcCharIsPunct (const FcChar8 c) 352{ 353 if (c < '0') 354 return FcTrue; 355 if (c <= '9') 356 return FcFalse; 357 if (c < 'A') 358 return FcTrue; 359 if (c <= 'Z') 360 return FcFalse; 361 if (c < 'a') 362 return FcTrue; 363 if (c <= 'z') 364 return FcFalse; 365 if (c <= '~') 366 return FcTrue; 367 return FcFalse; 368} 369 370/* 371 * Is the head of s1 equal to s2? 372 */ 373 374static FcBool 375FcStrIsAtIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) 376{ 377 FcCaseWalker w1, w2; 378 FcChar8 c1, c2; 379 380 FcStrCaseWalkerInit (s1, &w1); 381 FcStrCaseWalkerInit (s2, &w2); 382 383 for (;;) 384 { 385 c1 = FcStrCaseWalkerNext (&w1, NULL); 386 c2 = FcStrCaseWalkerNext (&w2, NULL); 387 if (!c1 || (c1 != c2)) 388 break; 389 } 390 return c1 == c2 || !c2; 391} 392 393/* 394 * Does s1 contain an instance of s2 (ignoring blanks and case)? 395 */ 396 397const FcChar8 * 398FcStrContainsIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) 399{ 400 while (*s1) 401 { 402 if (FcStrIsAtIgnoreCase (s1, s2)) 403 return s1; 404 s1++; 405 } 406 return 0; 407} 408 409/* 410 * Does s1 contain an instance of s2 on a word boundary (ignoring case)? 411 */ 412 413const FcChar8 * 414FcStrContainsWord (const FcChar8 *s1, const FcChar8 *s2) 415{ 416 FcBool wordStart = FcTrue; 417 int s1len = strlen ((char *) s1); 418 int s2len = strlen ((char *) s2); 419 420 while (s1len >= s2len) 421 { 422 if (wordStart && 423 FcStrIsAtIgnoreCase (s1, s2) && 424 (s1len == s2len || FcCharIsPunct (s1[s2len]))) 425 { 426 return s1; 427 } 428 wordStart = FcFalse; 429 if (FcCharIsPunct (*s1)) 430 wordStart = FcTrue; 431 s1++; 432 s1len--; 433 } 434 return 0; 435} 436 437/* 438 * returns the number of strings (ignoring delimitors and case) being matched 439 */ 440 441int 442FcStrMatchIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *delims) 443{ 444 FcCaseWalker w1, w2; 445 FcChar8 c1, c2; 446 447 if (s1 == s2) return 0; 448 449 FcStrCaseWalkerInit (s1, &w1); 450 FcStrCaseWalkerInit (s2, &w2); 451 452 for (;;) 453 { 454 c1 = FcStrCaseWalkerNext (&w1, (const char *)delims); 455 c2 = FcStrCaseWalkerNext (&w2, (const char *)delims); 456 if (!c1 || (c1 != c2)) 457 break; 458 } 459 return w1.src - s1 - 1; 460} 461 462FcBool 463FcStrGlobMatch (const FcChar8 *glob, 464 const FcChar8 *string) 465{ 466 FcChar8 c; 467 468 while ((c = *glob++)) 469 { 470 switch (c) { 471 case '*': 472 /* short circuit common case */ 473 if (!*glob) 474 return FcTrue; 475 /* short circuit another common case */ 476 if (strchr ((char *) glob, '*') == 0) 477 { 478 size_t l1, l2; 479 480 l1 = strlen ((char *) string); 481 l2 = strlen ((char *) glob); 482 if (l1 < l2) 483 return FcFalse; 484 string += (l1 - l2); 485 } 486 while (*string) 487 { 488 if (FcStrGlobMatch (glob, string)) 489 return FcTrue; 490 string++; 491 } 492 return FcFalse; 493 case '?': 494 if (*string++ == '\0') 495 return FcFalse; 496 break; 497 default: 498 if (*string++ != c) 499 return FcFalse; 500 break; 501 } 502 } 503 return *string == '\0'; 504} 505 506const FcChar8 * 507FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) 508{ 509 FcCaseWalker w1, w2; 510 FcChar8 c1, c2; 511 const FcChar8 *cur; 512 513 if (!s1 || !s2) 514 return 0; 515 516 if (s1 == s2) 517 return s1; 518 519 FcStrCaseWalkerInit (s1, &w1); 520 FcStrCaseWalkerInit (s2, &w2); 521 522 c2 = FcStrCaseWalkerNext (&w2, NULL); 523 524 for (;;) 525 { 526 cur = w1.src; 527 c1 = FcStrCaseWalkerNext (&w1, NULL); 528 if (!c1) 529 break; 530 if (c1 == c2) 531 { 532 FcCaseWalker w1t = w1; 533 FcCaseWalker w2t = w2; 534 FcChar8 c1t, c2t; 535 536 for (;;) 537 { 538 c1t = FcStrCaseWalkerNext (&w1t, NULL); 539 c2t = FcStrCaseWalkerNext (&w2t, NULL); 540 541 if (!c2t) 542 return cur; 543 if (c2t != c1t) 544 break; 545 } 546 } 547 } 548 return 0; 549} 550 551const FcChar8 * 552FcStrStr (const FcChar8 *s1, const FcChar8 *s2) 553{ 554 FcChar8 c1, c2; 555 const FcChar8 * p = s1; 556 const FcChar8 * b = s2; 557 558 if (!s1 || !s2) 559 return 0; 560 561 if (s1 == s2) 562 return s1; 563 564again: 565 c2 = *s2++; 566 567 if (!c2) 568 return 0; 569 570 for (;;) 571 { 572 p = s1; 573 c1 = *s1++; 574 if (!c1 || c1 == c2) 575 break; 576 } 577 578 if (c1 != c2) 579 return 0; 580 581 for (;;) 582 { 583 c1 = *s1; 584 c2 = *s2; 585 if (c1 && c2 && c1 != c2) 586 { 587 s1 = p + 1; 588 s2 = b; 589 goto again; 590 } 591 if (!c2) 592 return p; 593 if (!c1) 594 return 0; 595 ++ s1; 596 ++ s2; 597 } 598 /* never reached. */ 599} 600 601int 602FcUtf8ToUcs4 (const FcChar8 *src_orig, 603 FcChar32 *dst, 604 int len) 605{ 606 const FcChar8 *src = src_orig; 607 FcChar8 s; 608 int extra; 609 FcChar32 result; 610 611 if (len == 0) 612 return 0; 613 614 s = *src++; 615 len--; 616 617 if (!(s & 0x80)) 618 { 619 result = s; 620 extra = 0; 621 } 622 else if (!(s & 0x40)) 623 { 624 return -1; 625 } 626 else if (!(s & 0x20)) 627 { 628 result = s & 0x1f; 629 extra = 1; 630 } 631 else if (!(s & 0x10)) 632 { 633 result = s & 0xf; 634 extra = 2; 635 } 636 else if (!(s & 0x08)) 637 { 638 result = s & 0x07; 639 extra = 3; 640 } 641 else if (!(s & 0x04)) 642 { 643 result = s & 0x03; 644 extra = 4; 645 } 646 else if ( ! (s & 0x02)) 647 { 648 result = s & 0x01; 649 extra = 5; 650 } 651 else 652 { 653 return -1; 654 } 655 if (extra > len) 656 return -1; 657 658 while (extra--) 659 { 660 result <<= 6; 661 s = *src++; 662 663 if ((s & 0xc0) != 0x80) 664 return -1; 665 666 result |= s & 0x3f; 667 } 668 *dst = result; 669 return src - src_orig; 670} 671 672FcBool 673FcUtf8Len (const FcChar8 *string, 674 int len, 675 int *nchar, 676 int *wchar) 677{ 678 int n; 679 int clen; 680 FcChar32 c; 681 FcChar32 max; 682 683 n = 0; 684 max = 0; 685 while (len) 686 { 687 clen = FcUtf8ToUcs4 (string, &c, len); 688 if (clen <= 0) /* malformed UTF8 string */ 689 return FcFalse; 690 if (c > max) 691 max = c; 692 string += clen; 693 len -= clen; 694 n++; 695 } 696 *nchar = n; 697 if (max >= 0x10000) 698 *wchar = 4; 699 else if (max > 0x100) 700 *wchar = 2; 701 else 702 *wchar = 1; 703 return FcTrue; 704} 705 706int 707FcUcs4ToUtf8 (FcChar32 ucs4, 708 FcChar8 dest[FC_UTF8_MAX_LEN]) 709{ 710 int bits; 711 FcChar8 *d = dest; 712 713 if (ucs4 < 0x80) { *d++= ucs4; bits= -6; } 714 else if (ucs4 < 0x800) { *d++= ((ucs4 >> 6) & 0x1F) | 0xC0; bits= 0; } 715 else if (ucs4 < 0x10000) { *d++= ((ucs4 >> 12) & 0x0F) | 0xE0; bits= 6; } 716 else if (ucs4 < 0x200000) { *d++= ((ucs4 >> 18) & 0x07) | 0xF0; bits= 12; } 717 else if (ucs4 < 0x4000000) { *d++= ((ucs4 >> 24) & 0x03) | 0xF8; bits= 18; } 718 else if (ucs4 < 0x80000000) { *d++= ((ucs4 >> 30) & 0x01) | 0xFC; bits= 24; } 719 else return 0; 720 721 for ( ; bits >= 0; bits-= 6) { 722 *d++= ((ucs4 >> bits) & 0x3F) | 0x80; 723 } 724 return d - dest; 725} 726 727#define GetUtf16(src,endian) \ 728 ((FcChar16) ((src)[endian == FcEndianBig ? 0 : 1] << 8) | \ 729 (FcChar16) ((src)[endian == FcEndianBig ? 1 : 0])) 730 731int 732FcUtf16ToUcs4 (const FcChar8 *src_orig, 733 FcEndian endian, 734 FcChar32 *dst, 735 int len) /* in bytes */ 736{ 737 const FcChar8 *src = src_orig; 738 FcChar16 a, b; 739 FcChar32 result; 740 741 if (len < 2) 742 return 0; 743 744 a = GetUtf16 (src, endian); src += 2; len -= 2; 745 746 /* 747 * Check for surrogate 748 */ 749 if ((a & 0xfc00) == 0xd800) 750 { 751 if (len < 2) 752 return 0; 753 b = GetUtf16 (src, endian); src += 2; len -= 2; 754 /* 755 * Check for invalid surrogate sequence 756 */ 757 if ((b & 0xfc00) != 0xdc00) 758 return 0; 759 result = ((((FcChar32) a & 0x3ff) << 10) | 760 ((FcChar32) b & 0x3ff)) + 0x10000; 761 } 762 else 763 result = a; 764 *dst = result; 765 return src - src_orig; 766} 767 768FcBool 769FcUtf16Len (const FcChar8 *string, 770 FcEndian endian, 771 int len, /* in bytes */ 772 int *nchar, 773 int *wchar) 774{ 775 int n; 776 int clen; 777 FcChar32 c; 778 FcChar32 max; 779 780 n = 0; 781 max = 0; 782 while (len) 783 { 784 clen = FcUtf16ToUcs4 (string, endian, &c, len); 785 if (clen <= 0) /* malformed UTF8 string */ 786 return FcFalse; 787 if (c > max) 788 max = c; 789 string += clen; 790 len -= clen; 791 n++; 792 } 793 *nchar = n; 794 if (max >= 0x10000) 795 *wchar = 4; 796 else if (max > 0x100) 797 *wchar = 2; 798 else 799 *wchar = 1; 800 return FcTrue; 801} 802 803void 804FcStrBufInit (FcStrBuf *buf, FcChar8 *init, int size) 805{ 806 if (init) 807 { 808 buf->buf = init; 809 buf->size = size; 810 } else 811 { 812 buf->buf = buf->buf_static; 813 buf->size = sizeof (buf->buf_static); 814 } 815 buf->allocated = FcFalse; 816 buf->failed = FcFalse; 817 buf->len = 0; 818} 819 820void 821FcStrBufDestroy (FcStrBuf *buf) 822{ 823 if (buf->allocated) 824 { 825 free (buf->buf); 826 FcStrBufInit (buf, 0, 0); 827 } 828} 829 830FcChar8 * 831FcStrBufDone (FcStrBuf *buf) 832{ 833 FcChar8 *ret; 834 835 if (buf->failed) 836 ret = NULL; 837 else 838 ret = malloc (buf->len + 1); 839 if (ret) 840 { 841 memcpy (ret, buf->buf, buf->len); 842 ret[buf->len] = '\0'; 843 } 844 FcStrBufDestroy (buf); 845 return ret; 846} 847 848FcChar8 * 849FcStrBufDoneStatic (FcStrBuf *buf) 850{ 851 FcStrBufChar (buf, '\0'); 852 853 if (buf->failed) 854 return NULL; 855 856 return buf->buf; 857} 858 859FcBool 860FcStrBufChar (FcStrBuf *buf, FcChar8 c) 861{ 862 if (buf->len == buf->size) 863 { 864 FcChar8 *new; 865 int size; 866 867 if (buf->failed) 868 return FcFalse; 869 870 if (buf->allocated) 871 { 872 size = buf->size * 2; 873 new = realloc (buf->buf, size); 874 } 875 else 876 { 877 size = buf->size + 64; 878 new = malloc (size); 879 if (new) 880 { 881 buf->allocated = FcTrue; 882 memcpy (new, buf->buf, buf->len); 883 } 884 } 885 if (!new) 886 { 887 buf->failed = FcTrue; 888 return FcFalse; 889 } 890 buf->size = size; 891 buf->buf = new; 892 } 893 buf->buf[buf->len++] = c; 894 return FcTrue; 895} 896 897FcBool 898FcStrBufString (FcStrBuf *buf, const FcChar8 *s) 899{ 900 FcChar8 c; 901 while ((c = *s++)) 902 if (!FcStrBufChar (buf, c)) 903 return FcFalse; 904 return FcTrue; 905} 906 907FcBool 908FcStrBufData (FcStrBuf *buf, const FcChar8 *s, int len) 909{ 910 while (len-- > 0) 911 if (!FcStrBufChar (buf, *s++)) 912 return FcFalse; 913 return FcTrue; 914} 915 916FcBool 917FcStrUsesHome (const FcChar8 *s) 918{ 919 return *s == '~'; 920} 921 922FcChar8 * 923FcStrBuildFilename (const FcChar8 *path, 924 ...) 925{ 926 va_list ap; 927 FcStrSet *sset = FcStrSetCreate (); 928 FcStrList *list; 929 FcChar8 *s, *ret = NULL, *p; 930 size_t len = 0; 931 932 if (!sset || !path) 933 return NULL; 934 935 if (!FcStrSetAdd (sset, path)) 936 goto bail0; 937 938 va_start (ap, path); 939 while (1) 940 { 941 s = (FcChar8 *)va_arg (ap, FcChar8 *); 942 if (!s) 943 break; 944 if (!FcStrSetAdd (sset, s)) 945 goto bail1; 946 } 947 list = FcStrListCreate (sset); 948 while ((s = FcStrListNext (list))) 949 { 950 len += strlen ((const char *)s) + 1; 951 } 952 list->n = 0; 953 ret = malloc (sizeof (FcChar8) * (len + 1)); 954 if (!ret) 955 goto bail2; 956 p = ret; 957 while ((s = FcStrListNext (list))) 958 { 959 if (p != ret) 960 { 961 p[0] = FC_DIR_SEPARATOR; 962 p++; 963 } 964 len = strlen ((const char *)s); 965 memcpy (p, s, len); 966 p += len; 967 } 968 *p = 0; 969 970bail2: 971 FcStrListDone (list); 972bail1: 973 va_end (ap); 974bail0: 975 FcStrSetDestroy (sset); 976 977 return ret; 978} 979 980FcChar8 * 981FcStrCopyFilename (const FcChar8 *s) 982{ 983 FcChar8 *new; 984 985 if (*s == '~') 986 { 987 FcChar8 *home = FcConfigHome (); 988 FcChar8 *full; 989 int size; 990 if (!home) 991 return NULL; 992 size = strlen ((char *) home) + strlen ((char *) s); 993 full = (FcChar8 *) malloc (size); 994 if (!full) 995 return NULL; 996 strcpy ((char *) full, (char *) home); 997 strcat ((char *) full, (char *) s + 1); 998 new = FcStrCanonFilename (full); 999 free (full); 1000 } 1001 else 1002 new = FcStrCanonFilename (s); 1003 1004 return new; 1005} 1006 1007FcChar8 * 1008FcStrLastSlash (const FcChar8 *path) 1009{ 1010 FcChar8 *slash; 1011 1012 slash = (FcChar8 *) strrchr ((const char *) path, '/'); 1013#ifdef _WIN32 1014 { 1015 FcChar8 *backslash; 1016 1017 backslash = (FcChar8 *) strrchr ((const char *) path, '\\'); 1018 if (!slash || (backslash && backslash > slash)) 1019 slash = backslash; 1020 } 1021#endif 1022 1023 return slash; 1024} 1025 1026FcChar8 * 1027FcStrDirname (const FcChar8 *file) 1028{ 1029 FcChar8 *slash; 1030 FcChar8 *dir; 1031 1032 slash = FcStrLastSlash (file); 1033 if (!slash) 1034 return FcStrCopy ((FcChar8 *) "."); 1035 dir = malloc ((slash - file) + 1); 1036 if (!dir) 1037 return 0; 1038 strncpy ((char *) dir, (const char *) file, slash - file); 1039 dir[slash - file] = '\0'; 1040 return dir; 1041} 1042 1043FcChar8 * 1044FcStrBasename (const FcChar8 *file) 1045{ 1046 FcChar8 *slash; 1047 1048 slash = FcStrLastSlash (file); 1049 if (!slash) 1050 return FcStrCopy (file); 1051 return FcStrCopy (slash + 1); 1052} 1053 1054static FcChar8 * 1055FcStrCanonAbsoluteFilename (const FcChar8 *s) 1056{ 1057 FcChar8 *file; 1058 FcChar8 *f; 1059 const FcChar8 *slash; 1060 int size; 1061 1062 size = strlen ((char *) s) + 1; 1063 file = malloc (size); 1064 if (!file) 1065 return NULL; 1066 slash = NULL; 1067 f = file; 1068#ifdef _WIN32 1069 if (*s == '/' && *(s+1) == '/') /* Network path, do not squash // */ 1070 *f++ = *s++; 1071#endif 1072 for (;;) { 1073 if (*s == '/' || *s == '\0') 1074 { 1075 if (slash) 1076 { 1077 switch (s - slash) { 1078 case 1: 1079 f -= 1; /* squash // and trim final / from file */ 1080 break; 1081 case 2: 1082 if (!strncmp ((char *) slash, "/.", 2)) 1083 { 1084 f -= 2; /* trim /. from file */ 1085 } 1086 break; 1087 case 3: 1088 if (!strncmp ((char *) slash, "/..", 3)) 1089 { 1090 f -= 3; /* trim /.. from file */ 1091 while (f > file) { 1092 if (*--f == '/') 1093 break; 1094 } 1095 } 1096 break; 1097 } 1098 } 1099 slash = s; 1100 } 1101 if (!(*f++ = *s++)) 1102 break; 1103 } 1104 return file; 1105} 1106 1107#ifdef _WIN32 1108/* 1109 * Convert '\\' to '/' , remove double '/' 1110 */ 1111static void 1112FcConvertDosPath (char *str) 1113{ 1114 size_t len = strlen (str); 1115 char *p = str; 1116 char *dest = str; 1117 char *end = str + len; 1118 char last = 0; 1119 1120 if (*p == '\\') 1121 { 1122 *p = '/'; 1123 p++; 1124 dest++; 1125 } 1126 while (p < end) 1127 { 1128 if (*p == '\\') 1129 *p = '/'; 1130 1131 if (*p != '/' 1132 || last != '/') 1133 { 1134 *dest++ = *p; 1135 } 1136 1137 last = *p; 1138 p++; 1139 } 1140 1141 *dest = 0; 1142} 1143#endif 1144 1145FcChar8 * 1146FcStrCanonFilename (const FcChar8 *s) 1147{ 1148#ifdef _WIN32 1149 FcChar8 full[FC_MAX_FILE_LEN + 2]; 1150 int size = GetFullPathName ((LPCSTR) s, sizeof (full) -1, 1151 (LPSTR) full, NULL); 1152 1153 if (size == 0) 1154 perror ("GetFullPathName"); 1155 1156 FcConvertDosPath ((char *) full); 1157 return FcStrCanonAbsoluteFilename (full); 1158#else 1159 if (s[0] == '/') 1160 return FcStrCanonAbsoluteFilename (s); 1161 else 1162 { 1163 FcChar8 *full; 1164 FcChar8 *file; 1165 1166 FcChar8 cwd[FC_MAX_FILE_LEN + 2]; 1167 if (getcwd ((char *) cwd, FC_MAX_FILE_LEN) == NULL) 1168 return NULL; 1169 full = FcStrBuildFilename (cwd, s, NULL); 1170 file = FcStrCanonAbsoluteFilename (full); 1171 FcStrFree (full); 1172 return file; 1173 } 1174#endif 1175} 1176 1177 1178FcStrSet * 1179FcStrSetCreate (void) 1180{ 1181 FcStrSet *set = malloc (sizeof (FcStrSet)); 1182 if (!set) 1183 return 0; 1184 FcRefInit (&set->ref, 1); 1185 set->num = 0; 1186 set->size = 0; 1187 set->strs = 0; 1188 return set; 1189} 1190 1191static FcBool 1192_FcStrSetAppend (FcStrSet *set, FcChar8 *s) 1193{ 1194 if (FcStrSetMember (set, s)) 1195 { 1196 FcStrFree (s); 1197 return FcTrue; 1198 } 1199 if (set->num == set->size) 1200 { 1201 FcChar8 **strs = malloc ((set->size + 2) * sizeof (FcChar8 *)); 1202 1203 if (!strs) 1204 return FcFalse; 1205 if (set->num) 1206 memcpy (strs, set->strs, set->num * sizeof (FcChar8 *)); 1207 if (set->strs) 1208 free (set->strs); 1209 set->size = set->size + 1; 1210 set->strs = strs; 1211 } 1212 set->strs[set->num++] = s; 1213 set->strs[set->num] = 0; 1214 return FcTrue; 1215} 1216 1217FcBool 1218FcStrSetMember (FcStrSet *set, const FcChar8 *s) 1219{ 1220 int i; 1221 1222 for (i = 0; i < set->num; i++) 1223 if (!FcStrCmp (set->strs[i], s)) 1224 return FcTrue; 1225 return FcFalse; 1226} 1227 1228FcBool 1229FcStrSetEqual (FcStrSet *sa, FcStrSet *sb) 1230{ 1231 int i; 1232 if (sa->num != sb->num) 1233 return FcFalse; 1234 for (i = 0; i < sa->num; i++) 1235 if (!FcStrSetMember (sb, sa->strs[i])) 1236 return FcFalse; 1237 return FcTrue; 1238} 1239 1240FcBool 1241FcStrSetAdd (FcStrSet *set, const FcChar8 *s) 1242{ 1243 FcChar8 *new = FcStrCopy (s); 1244 if (!new) 1245 return FcFalse; 1246 if (!_FcStrSetAppend (set, new)) 1247 { 1248 FcStrFree (new); 1249 return FcFalse; 1250 } 1251 return FcTrue; 1252} 1253 1254FcBool 1255FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s) 1256{ 1257 FcChar8 *new = FcStrCopyFilename (s); 1258 if (!new) 1259 return FcFalse; 1260 if (!_FcStrSetAppend (set, new)) 1261 { 1262 FcStrFree (new); 1263 return FcFalse; 1264 } 1265 return FcTrue; 1266} 1267 1268FcBool 1269FcStrSetAddLangs (FcStrSet *strs, const char *languages) 1270{ 1271 const char *p = languages, *next; 1272 FcChar8 lang[128] = {0}, *normalized_lang; 1273 size_t len; 1274 FcBool ret = FcFalse; 1275 1276 if (!languages) 1277 return FcFalse; 1278 1279 while ((next = strchr (p, ':'))) 1280 { 1281 len = next - p; 1282 len = FC_MIN (len, 127); 1283 strncpy ((char *) lang, p, len); 1284 lang[len] = 0; 1285 /* ignore an empty item */ 1286 if (*lang) 1287 { 1288 normalized_lang = FcLangNormalize ((const FcChar8 *) lang); 1289 if (normalized_lang) 1290 { 1291 FcStrSetAdd (strs, normalized_lang); 1292 FcStrFree (normalized_lang); 1293 ret = FcTrue; 1294 } 1295 } 1296 p = next + 1; 1297 } 1298 if (*p) 1299 { 1300 normalized_lang = FcLangNormalize ((const FcChar8 *) p); 1301 if (normalized_lang) 1302 { 1303 FcStrSetAdd (strs, normalized_lang); 1304 FcStrFree (normalized_lang); 1305 ret = FcTrue; 1306 } 1307 } 1308 1309 return ret; 1310} 1311 1312FcBool 1313FcStrSetDel (FcStrSet *set, const FcChar8 *s) 1314{ 1315 int i; 1316 1317 for (i = 0; i < set->num; i++) 1318 if (!FcStrCmp (set->strs[i], s)) 1319 { 1320 FcStrFree (set->strs[i]); 1321 /* 1322 * copy remaining string pointers and trailing 1323 * NULL 1324 */ 1325 memmove (&set->strs[i], &set->strs[i+1], 1326 (set->num - i) * sizeof (FcChar8 *)); 1327 set->num--; 1328 return FcTrue; 1329 } 1330 return FcFalse; 1331} 1332 1333/* TODO Make public */ 1334static FcStrSet * 1335FcStrSetReference (FcStrSet *set) 1336{ 1337 if (FcRefIsConst (&set->ref)) 1338 return set; 1339 1340 FcRefInc (&set->ref); 1341 return set; 1342} 1343 1344void 1345FcStrSetDestroy (FcStrSet *set) 1346{ 1347 int i; 1348 1349 /* We rely on this in FcGetDefaultLangs for caching. */ 1350 if (FcRefIsConst (&set->ref)) 1351 return; 1352 1353 if (FcRefDec (&set->ref) != 1) 1354 return; 1355 1356 for (i = 0; i < set->num; i++) 1357 FcStrFree (set->strs[i]); 1358 if (set->strs) 1359 free (set->strs); 1360 free (set); 1361} 1362 1363FcStrList * 1364FcStrListCreate (FcStrSet *set) 1365{ 1366 FcStrList *list; 1367 1368 list = malloc (sizeof (FcStrList)); 1369 if (!list) 1370 return 0; 1371 list->set = set; 1372 FcStrSetReference (set); 1373 list->n = 0; 1374 return list; 1375} 1376 1377FcChar8 * 1378FcStrListNext (FcStrList *list) 1379{ 1380 if (list->n >= list->set->num) 1381 return 0; 1382 return list->set->strs[list->n++]; 1383} 1384 1385void 1386FcStrListDone (FcStrList *list) 1387{ 1388 FcStrSetDestroy (list->set); 1389 free (list); 1390} 1391 1392#define __fcstr__ 1393#include "fcaliastail.h" 1394#undef __fcstr__ 1395