fcstr.c revision b09479dc
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; 928 FcStrList *list; 929 FcChar8 *s, *ret = NULL, *p; 930 size_t len = 0; 931 932 if (!path) 933 return NULL; 934 935 sset = FcStrSetCreate (); 936 if (!sset) 937 return NULL; 938 939 if (!FcStrSetAdd (sset, path)) 940 goto bail0; 941 942 va_start (ap, path); 943 while (1) 944 { 945 s = (FcChar8 *)va_arg (ap, FcChar8 *); 946 if (!s) 947 break; 948 if (!FcStrSetAdd (sset, s)) 949 goto bail1; 950 } 951 list = FcStrListCreate (sset); 952 while ((s = FcStrListNext (list))) 953 { 954 len += strlen ((const char *)s) + 1; 955 } 956 list->n = 0; 957 ret = malloc (sizeof (FcChar8) * (len + 1)); 958 if (!ret) 959 goto bail2; 960 p = ret; 961 while ((s = FcStrListNext (list))) 962 { 963 if (p != ret) 964 { 965 p[0] = FC_DIR_SEPARATOR; 966 p++; 967 } 968 len = strlen ((const char *)s); 969 memcpy (p, s, len); 970 p += len; 971 } 972 *p = 0; 973 974bail2: 975 FcStrListDone (list); 976bail1: 977 va_end (ap); 978bail0: 979 FcStrSetDestroy (sset); 980 981 return ret; 982} 983 984FcChar8 * 985FcStrCopyFilename (const FcChar8 *s) 986{ 987 FcChar8 *new; 988 989 if (*s == '~') 990 { 991 FcChar8 *home = FcConfigHome (); 992 FcChar8 *full; 993 int size; 994 if (!home) 995 return NULL; 996 size = strlen ((char *) home) + strlen ((char *) s); 997 full = (FcChar8 *) malloc (size); 998 if (!full) 999 return NULL; 1000 strcpy ((char *) full, (char *) home); 1001 strcat ((char *) full, (char *) s + 1); 1002 new = FcStrCanonFilename (full); 1003 free (full); 1004 } 1005 else 1006 new = FcStrCanonFilename (s); 1007 1008 return new; 1009} 1010 1011FcChar8 * 1012FcStrLastSlash (const FcChar8 *path) 1013{ 1014 FcChar8 *slash; 1015 1016 slash = (FcChar8 *) strrchr ((const char *) path, '/'); 1017#ifdef _WIN32 1018 { 1019 FcChar8 *backslash; 1020 1021 backslash = (FcChar8 *) strrchr ((const char *) path, '\\'); 1022 if (!slash || (backslash && backslash > slash)) 1023 slash = backslash; 1024 } 1025#endif 1026 1027 return slash; 1028} 1029 1030FcChar8 * 1031FcStrDirname (const FcChar8 *file) 1032{ 1033 FcChar8 *slash; 1034 FcChar8 *dir; 1035 1036 slash = FcStrLastSlash (file); 1037 if (!slash) 1038 return FcStrCopy ((FcChar8 *) "."); 1039 dir = malloc ((slash - file) + 1); 1040 if (!dir) 1041 return 0; 1042 strncpy ((char *) dir, (const char *) file, slash - file); 1043 dir[slash - file] = '\0'; 1044 return dir; 1045} 1046 1047FcChar8 * 1048FcStrBasename (const FcChar8 *file) 1049{ 1050 FcChar8 *slash; 1051 1052 slash = FcStrLastSlash (file); 1053 if (!slash) 1054 return FcStrCopy (file); 1055 return FcStrCopy (slash + 1); 1056} 1057 1058static FcChar8 * 1059FcStrCanonAbsoluteFilename (const FcChar8 *s) 1060{ 1061 FcChar8 *file; 1062 FcChar8 *f; 1063 const FcChar8 *slash; 1064 int size; 1065 1066 size = strlen ((char *) s) + 1; 1067 file = malloc (size); 1068 if (!file) 1069 return NULL; 1070 slash = NULL; 1071 f = file; 1072#ifdef _WIN32 1073 if (*s == '/' && *(s+1) == '/') /* Network path, do not squash // */ 1074 *f++ = *s++; 1075#endif 1076 for (;;) { 1077 if (*s == '/' || *s == '\0') 1078 { 1079 if (slash) 1080 { 1081 switch (s - slash) { 1082 case 1: 1083 f -= 1; /* squash // and trim final / from file */ 1084 break; 1085 case 2: 1086 if (!strncmp ((char *) slash, "/.", 2)) 1087 { 1088 f -= 2; /* trim /. from file */ 1089 } 1090 break; 1091 case 3: 1092 if (!strncmp ((char *) slash, "/..", 3)) 1093 { 1094 f -= 3; /* trim /.. from file */ 1095 while (f > file) { 1096 if (*--f == '/') 1097 break; 1098 } 1099 } 1100 break; 1101 } 1102 } 1103 slash = s; 1104 } 1105 if (!(*f++ = *s++)) 1106 break; 1107 } 1108 return file; 1109} 1110 1111#ifdef _WIN32 1112/* 1113 * Convert '\\' to '/' , remove double '/' 1114 */ 1115static void 1116FcConvertDosPath (char *str) 1117{ 1118 size_t len = strlen (str); 1119 char *p = str; 1120 char *dest = str; 1121 char *end = str + len; 1122 char last = 0; 1123 1124 if (*p == '\\') 1125 { 1126 *p = '/'; 1127 p++; 1128 dest++; 1129 } 1130 while (p < end) 1131 { 1132 if (*p == '\\') 1133 *p = '/'; 1134 1135 if (*p != '/' 1136 || last != '/') 1137 { 1138 *dest++ = *p; 1139 } 1140 1141 last = *p; 1142 p++; 1143 } 1144 1145 *dest = 0; 1146} 1147#endif 1148 1149FcChar8 * 1150FcStrCanonFilename (const FcChar8 *s) 1151{ 1152#ifdef _WIN32 1153 FcChar8 full[FC_MAX_FILE_LEN + 2]; 1154 int size = GetFullPathName ((LPCSTR) s, sizeof (full) -1, 1155 (LPSTR) full, NULL); 1156 1157 if (size == 0) 1158 perror ("GetFullPathName"); 1159 1160 FcConvertDosPath ((char *) full); 1161 return FcStrCanonAbsoluteFilename (full); 1162#else 1163 if (s[0] == '/') 1164 return FcStrCanonAbsoluteFilename (s); 1165 else 1166 { 1167 FcChar8 *full; 1168 FcChar8 *file; 1169 1170 FcChar8 cwd[FC_MAX_FILE_LEN + 2]; 1171 if (getcwd ((char *) cwd, FC_MAX_FILE_LEN) == NULL) 1172 return NULL; 1173 full = FcStrBuildFilename (cwd, s, NULL); 1174 file = FcStrCanonAbsoluteFilename (full); 1175 FcStrFree (full); 1176 return file; 1177 } 1178#endif 1179} 1180 1181 1182FcStrSet * 1183FcStrSetCreate (void) 1184{ 1185 FcStrSet *set = malloc (sizeof (FcStrSet)); 1186 if (!set) 1187 return 0; 1188 FcRefInit (&set->ref, 1); 1189 set->num = 0; 1190 set->size = 0; 1191 set->strs = 0; 1192 return set; 1193} 1194 1195static FcBool 1196_FcStrSetAppend (FcStrSet *set, FcChar8 *s) 1197{ 1198 if (FcStrSetMember (set, s)) 1199 { 1200 FcStrFree (s); 1201 return FcTrue; 1202 } 1203 if (set->num == set->size) 1204 { 1205 FcChar8 **strs = malloc ((set->size + 2) * sizeof (FcChar8 *)); 1206 1207 if (!strs) 1208 return FcFalse; 1209 if (set->num) 1210 memcpy (strs, set->strs, set->num * sizeof (FcChar8 *)); 1211 if (set->strs) 1212 free (set->strs); 1213 set->size = set->size + 1; 1214 set->strs = strs; 1215 } 1216 set->strs[set->num++] = s; 1217 set->strs[set->num] = 0; 1218 return FcTrue; 1219} 1220 1221FcBool 1222FcStrSetMember (FcStrSet *set, const FcChar8 *s) 1223{ 1224 int i; 1225 1226 for (i = 0; i < set->num; i++) 1227 if (!FcStrCmp (set->strs[i], s)) 1228 return FcTrue; 1229 return FcFalse; 1230} 1231 1232FcBool 1233FcStrSetEqual (FcStrSet *sa, FcStrSet *sb) 1234{ 1235 int i; 1236 if (sa->num != sb->num) 1237 return FcFalse; 1238 for (i = 0; i < sa->num; i++) 1239 if (!FcStrSetMember (sb, sa->strs[i])) 1240 return FcFalse; 1241 return FcTrue; 1242} 1243 1244FcBool 1245FcStrSetAdd (FcStrSet *set, const FcChar8 *s) 1246{ 1247 FcChar8 *new = FcStrCopy (s); 1248 if (!new) 1249 return FcFalse; 1250 if (!_FcStrSetAppend (set, new)) 1251 { 1252 FcStrFree (new); 1253 return FcFalse; 1254 } 1255 return FcTrue; 1256} 1257 1258FcBool 1259FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s) 1260{ 1261 FcChar8 *new = FcStrCopyFilename (s); 1262 if (!new) 1263 return FcFalse; 1264 if (!_FcStrSetAppend (set, new)) 1265 { 1266 FcStrFree (new); 1267 return FcFalse; 1268 } 1269 return FcTrue; 1270} 1271 1272FcBool 1273FcStrSetAddLangs (FcStrSet *strs, const char *languages) 1274{ 1275 const char *p = languages, *next; 1276 FcChar8 lang[128] = {0}, *normalized_lang; 1277 size_t len; 1278 FcBool ret = FcFalse; 1279 1280 if (!languages) 1281 return FcFalse; 1282 1283 while ((next = strchr (p, ':'))) 1284 { 1285 len = next - p; 1286 len = FC_MIN (len, 127); 1287 strncpy ((char *) lang, p, len); 1288 lang[len] = 0; 1289 /* ignore an empty item */ 1290 if (*lang) 1291 { 1292 normalized_lang = FcLangNormalize ((const FcChar8 *) lang); 1293 if (normalized_lang) 1294 { 1295 FcStrSetAdd (strs, normalized_lang); 1296 FcStrFree (normalized_lang); 1297 ret = FcTrue; 1298 } 1299 } 1300 p = next + 1; 1301 } 1302 if (*p) 1303 { 1304 normalized_lang = FcLangNormalize ((const FcChar8 *) p); 1305 if (normalized_lang) 1306 { 1307 FcStrSetAdd (strs, normalized_lang); 1308 FcStrFree (normalized_lang); 1309 ret = FcTrue; 1310 } 1311 } 1312 1313 return ret; 1314} 1315 1316FcBool 1317FcStrSetDel (FcStrSet *set, const FcChar8 *s) 1318{ 1319 int i; 1320 1321 for (i = 0; i < set->num; i++) 1322 if (!FcStrCmp (set->strs[i], s)) 1323 { 1324 FcStrFree (set->strs[i]); 1325 /* 1326 * copy remaining string pointers and trailing 1327 * NULL 1328 */ 1329 memmove (&set->strs[i], &set->strs[i+1], 1330 (set->num - i) * sizeof (FcChar8 *)); 1331 set->num--; 1332 return FcTrue; 1333 } 1334 return FcFalse; 1335} 1336 1337/* TODO Make public */ 1338static FcStrSet * 1339FcStrSetReference (FcStrSet *set) 1340{ 1341 if (FcRefIsConst (&set->ref)) 1342 return set; 1343 1344 FcRefInc (&set->ref); 1345 return set; 1346} 1347 1348void 1349FcStrSetDestroy (FcStrSet *set) 1350{ 1351 int i; 1352 1353 /* We rely on this in FcGetDefaultLangs for caching. */ 1354 if (FcRefIsConst (&set->ref)) 1355 return; 1356 1357 if (FcRefDec (&set->ref) != 1) 1358 return; 1359 1360 for (i = 0; i < set->num; i++) 1361 FcStrFree (set->strs[i]); 1362 if (set->strs) 1363 free (set->strs); 1364 free (set); 1365} 1366 1367FcStrList * 1368FcStrListCreate (FcStrSet *set) 1369{ 1370 FcStrList *list; 1371 1372 list = malloc (sizeof (FcStrList)); 1373 if (!list) 1374 return 0; 1375 list->set = set; 1376 FcStrSetReference (set); 1377 list->n = 0; 1378 return list; 1379} 1380 1381void 1382FcStrListFirst (FcStrList *list) 1383{ 1384 list->n = 0; 1385} 1386 1387FcChar8 * 1388FcStrListNext (FcStrList *list) 1389{ 1390 if (list->n >= list->set->num) 1391 return 0; 1392 return list->set->strs[list->n++]; 1393} 1394 1395void 1396FcStrListDone (FcStrList *list) 1397{ 1398 FcStrSetDestroy (list->set); 1399 free (list); 1400} 1401 1402#define __fcstr__ 1403#include "fcaliastail.h" 1404#undef __fcstr__ 1405