| /* |
| * fontconfig/src/fcstr.c |
| * |
| * Copyright © 2000 Keith Packard |
| * |
| * Permission to use, copy, modify, distribute, and sell this software and its |
| * documentation for any purpose is hereby granted without fee, provided that |
| * the above copyright notice appear in all copies and that both that |
| * copyright notice and this permission notice appear in supporting |
| * documentation, and that the name of the author(s) not be used in |
| * advertising or publicity pertaining to distribution of the software without |
| * specific, written prior permission. The authors make no |
| * representations about the suitability of this software for any purpose. It |
| * is provided "as is" without express or implied warranty. |
| * |
| * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "fcint.h" |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <string.h> |
| |
| |
| /* Objects MT-safe for readonly access. */ |
| |
| FcChar8 * |
| FcStrCopy (const FcChar8 *s) |
| { |
| return FcStrdup (s); |
| } |
| |
| static FcChar8 * |
| FcStrMakeTriple (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *s3) |
| { |
| int s1l = s1 ? strlen ((char *) s1) : 0; |
| int s2l = s2 ? strlen ((char *) s2) : 0; |
| int s3l = s3 ? strlen ((char *) s3) : 0; |
| int l = s1l + 1 + s2l + 1 + s3l + 1; |
| FcChar8 *s = malloc (l); |
| |
| if (!s) |
| return 0; |
| if (s1) |
| memcpy (s, s1, s1l + 1); |
| else |
| s[0] = '\0'; |
| if (s2) |
| memcpy (s + s1l + 1, s2, s2l + 1); |
| else |
| s[s1l + 1] = '\0'; |
| if (s3) |
| memcpy (s + s1l + 1 + s2l + 1, s3, s3l + 1); |
| else |
| s[s1l + 1 + s2l + 1] = '\0'; |
| return s; |
| } |
| |
| FcChar8 * |
| FcStrPlus (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| int s1l = strlen ((char *) s1); |
| int s2l = strlen ((char *) s2); |
| int l = s1l + s2l + 1; |
| FcChar8 *s = malloc (l); |
| |
| if (!s) |
| return 0; |
| memcpy (s, s1, s1l); |
| memcpy (s + s1l, s2, s2l + 1); |
| return s; |
| } |
| |
| void |
| FcStrFree (FcChar8 *s) |
| { |
| free (s); |
| } |
| |
| |
| #include "../fc-case/fccase.h" |
| |
| #define FcCaseFoldUpperCount(cf) \ |
| ((cf)->method == FC_CASE_FOLD_FULL ? 1 : (cf)->count) |
| |
| typedef struct _FcCaseWalker { |
| const FcChar8 *read; |
| const FcChar8 *src; |
| FcChar8 utf8[FC_MAX_CASE_FOLD_CHARS + 1]; |
| } FcCaseWalker; |
| |
| static void |
| FcStrCaseWalkerInit (const FcChar8 *src, FcCaseWalker *w) |
| { |
| w->src = src; |
| w->read = 0; |
| } |
| |
| static FcChar8 |
| FcStrCaseWalkerLong (FcCaseWalker *w, FcChar8 r) |
| { |
| FcChar32 ucs4; |
| int slen; |
| int len = strlen((char*)w->src); |
| |
| slen = FcUtf8ToUcs4 (w->src - 1, &ucs4, len + 1); |
| if (slen <= 0) |
| return r; |
| if (FC_MIN_FOLD_CHAR <= ucs4 && ucs4 <= FC_MAX_FOLD_CHAR) |
| { |
| int min = 0; |
| int max = FC_NUM_CASE_FOLD; |
| |
| while (min <= max) |
| { |
| int mid = (min + max) >> 1; |
| FcChar32 low = fcCaseFold[mid].upper; |
| FcChar32 high = low + FcCaseFoldUpperCount (&fcCaseFold[mid]); |
| |
| if (high <= ucs4) |
| min = mid + 1; |
| else if (ucs4 < low) |
| max = mid - 1; |
| else |
| { |
| const FcCaseFold *fold = &fcCaseFold[mid]; |
| int dlen; |
| |
| switch (fold->method) { |
| case FC_CASE_FOLD_EVEN_ODD: |
| if ((ucs4 & 1) != (fold->upper & 1)) |
| return r; |
| /* fall through ... */ |
| default: |
| dlen = FcUcs4ToUtf8 (ucs4 + fold->offset, w->utf8); |
| break; |
| case FC_CASE_FOLD_FULL: |
| dlen = fold->count; |
| memcpy (w->utf8, fcCaseFoldChars + fold->offset, dlen); |
| break; |
| } |
| |
| /* consume rest of src utf-8 bytes */ |
| w->src += slen - 1; |
| |
| /* read from temp buffer */ |
| w->utf8[dlen] = '\0'; |
| w->read = w->utf8; |
| return *w->read++; |
| } |
| } |
| } |
| return r; |
| } |
| |
| static FcChar8 |
| FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims) |
| { |
| FcChar8 r; |
| |
| if (FC_UNLIKELY (w->read != NULL)) |
| { |
| if ((r = *w->read++)) |
| return r; |
| w->read = 0; |
| } |
| do |
| { |
| r = *w->src++; |
| } while (r != 0 && delims && strchr (delims, r)); |
| |
| if (FC_UNLIKELY ((r & 0xc0) == 0xc0)) |
| return FcStrCaseWalkerLong (w, r); |
| if ('A' <= r && r <= 'Z') |
| r = r - 'A' + 'a'; |
| return r; |
| } |
| |
| static FcChar8 |
| FcStrCaseWalkerNextNonBlank (FcCaseWalker *w) |
| { |
| FcChar8 r; |
| |
| if (FC_UNLIKELY (w->read != NULL)) |
| { |
| if ((r = *w->read++)) |
| return r; |
| w->read = 0; |
| } |
| do |
| { |
| r = *w->src++; |
| } while (r == ' '); |
| |
| if (FC_UNLIKELY ((r & 0xc0) == 0xc0)) |
| return FcStrCaseWalkerLong (w, r); |
| if ('A' <= r && r <= 'Z') |
| r = r - 'A' + 'a'; |
| return r; |
| } |
| |
| static FcChar8 |
| FcStrCaseWalkerNext (FcCaseWalker *w) |
| { |
| FcChar8 r; |
| |
| if (FC_UNLIKELY (w->read != NULL)) |
| { |
| if ((r = *w->read++)) |
| return r; |
| w->read = 0; |
| } |
| |
| r = *w->src++; |
| |
| if (FC_UNLIKELY ((r & 0xc0) == 0xc0)) |
| return FcStrCaseWalkerLong (w, r); |
| if ('A' <= r && r <= 'Z') |
| r = r - 'A' + 'a'; |
| return r; |
| } |
| |
| FcChar8 * |
| FcStrDowncase (const FcChar8 *s) |
| { |
| FcCaseWalker w; |
| int len = 0; |
| FcChar8 *dst, *d; |
| |
| FcStrCaseWalkerInit (s, &w); |
| while (FcStrCaseWalkerNext (&w)) |
| len++; |
| d = dst = malloc (len + 1); |
| if (!d) |
| return 0; |
| FcStrCaseWalkerInit (s, &w); |
| while ((*d++ = FcStrCaseWalkerNext (&w))); |
| return dst; |
| } |
| |
| int |
| FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| |
| if (s1 == s2) return 0; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| for (;;) |
| { |
| c1 = FcStrCaseWalkerNext (&w1); |
| c2 = FcStrCaseWalkerNext (&w2); |
| if (!c1 || (c1 != c2)) |
| break; |
| } |
| return (int) c1 - (int) c2; |
| } |
| |
| int |
| FcStrCmpIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| |
| if (s1 == s2) return 0; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| for (;;) |
| { |
| c1 = FcStrCaseWalkerNextNonBlank (&w1); |
| c2 = FcStrCaseWalkerNextNonBlank (&w2); |
| if (!c1 || (c1 != c2)) |
| break; |
| } |
| return (int) c1 - (int) c2; |
| } |
| |
| int |
| FcStrCmp (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcChar8 c1, c2; |
| |
| if (s1 == s2) |
| return 0; |
| for (;;) |
| { |
| c1 = *s1++; |
| c2 = *s2++; |
| if (!c1 || c1 != c2) |
| break; |
| } |
| return (int) c1 - (int) c2; |
| } |
| |
| /* |
| * Return a hash value for a string |
| */ |
| |
| FcChar32 |
| FcStrHashIgnoreCase (const FcChar8 *s) |
| { |
| FcChar32 h = 0; |
| FcCaseWalker w; |
| FcChar8 c; |
| |
| FcStrCaseWalkerInit (s, &w); |
| while ((c = FcStrCaseWalkerNext (&w))) |
| h = ((h << 3) ^ (h >> 3)) ^ c; |
| return h; |
| } |
| |
| FcChar32 |
| FcStrHashIgnoreBlanksAndCase (const FcChar8 *s) |
| { |
| FcChar32 h = 0; |
| FcCaseWalker w; |
| FcChar8 c; |
| |
| FcStrCaseWalkerInit (s, &w); |
| while ((c = FcStrCaseWalkerNextNonBlank (&w))) |
| h = ((h << 3) ^ (h >> 3)) ^ c; |
| return h; |
| } |
| |
| /* |
| * Is the head of s1 equal to s2? |
| */ |
| |
| static FcBool |
| FcStrIsAtIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| for (;;) |
| { |
| c1 = FcStrCaseWalkerNextNonBlank (&w1); |
| c2 = FcStrCaseWalkerNextNonBlank (&w2); |
| if (!c1 || (c1 != c2)) |
| break; |
| } |
| return c1 == c2 || !c2; |
| } |
| |
| /* |
| * Does s1 contain an instance of s2 (ignoring blanks and case)? |
| */ |
| |
| const FcChar8 * |
| FcStrContainsIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| while (*s1) |
| { |
| if (FcStrIsAtIgnoreBlanksAndCase (s1, s2)) |
| return s1; |
| s1++; |
| } |
| return 0; |
| } |
| |
| static FcBool |
| FcCharIsPunct (const FcChar8 c) |
| { |
| if (c < '0') |
| return FcTrue; |
| if (c <= '9') |
| return FcFalse; |
| if (c < 'A') |
| return FcTrue; |
| if (c <= 'Z') |
| return FcFalse; |
| if (c < 'a') |
| return FcTrue; |
| if (c <= 'z') |
| return FcFalse; |
| if (c <= '~') |
| return FcTrue; |
| return FcFalse; |
| } |
| |
| /* |
| * Is the head of s1 equal to s2? |
| */ |
| |
| static FcBool |
| FcStrIsAtIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| for (;;) |
| { |
| c1 = FcStrCaseWalkerNext (&w1); |
| c2 = FcStrCaseWalkerNext (&w2); |
| if (!c1 || (c1 != c2)) |
| break; |
| } |
| return c1 == c2 || !c2; |
| } |
| |
| /* |
| * Does s1 contain an instance of s2 (ignoring blanks and case)? |
| */ |
| |
| const FcChar8 * |
| FcStrContainsIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| while (*s1) |
| { |
| if (FcStrIsAtIgnoreCase (s1, s2)) |
| return s1; |
| s1++; |
| } |
| return 0; |
| } |
| |
| /* |
| * Does s1 contain an instance of s2 on a word boundary (ignoring case)? |
| */ |
| |
| const FcChar8 * |
| FcStrContainsWord (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcBool wordStart = FcTrue; |
| int s1len = strlen ((char *) s1); |
| int s2len = strlen ((char *) s2); |
| |
| while (s1len >= s2len) |
| { |
| if (wordStart && |
| FcStrIsAtIgnoreCase (s1, s2) && |
| (s1len == s2len || FcCharIsPunct (s1[s2len]))) |
| { |
| return s1; |
| } |
| wordStart = FcFalse; |
| if (FcCharIsPunct (*s1)) |
| wordStart = FcTrue; |
| s1++; |
| s1len--; |
| } |
| return 0; |
| } |
| |
| /* |
| * returns the number of strings (ignoring delimiters and case) being matched |
| */ |
| |
| int |
| FcStrMatchIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *delims) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| |
| if (s1 == s2) return 0; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| for (;;) |
| { |
| c1 = FcStrCaseWalkerNextNonDelim (&w1, (const char *)delims); |
| c2 = FcStrCaseWalkerNextNonDelim (&w2, (const char *)delims); |
| if (!c1 || (c1 != c2)) |
| break; |
| } |
| return w1.src - s1 - 1; |
| } |
| |
| FcBool |
| FcStrGlobMatch (const FcChar8 *glob, |
| const FcChar8 *string) |
| { |
| FcChar8 c; |
| |
| while ((c = *glob++)) |
| { |
| switch (c) { |
| case '*': |
| /* short circuit common case */ |
| if (!*glob) |
| return FcTrue; |
| /* short circuit another common case */ |
| if (strchr ((char *) glob, '*') == 0) |
| { |
| size_t l1, l2; |
| |
| l1 = strlen ((char *) string); |
| l2 = strlen ((char *) glob); |
| if (l1 < l2) |
| return FcFalse; |
| string += (l1 - l2); |
| } |
| while (*string) |
| { |
| if (FcStrGlobMatch (glob, string)) |
| return FcTrue; |
| string++; |
| } |
| return FcFalse; |
| case '?': |
| if (*string++ == '\0') |
| return FcFalse; |
| break; |
| default: |
| if (*string++ != c) |
| return FcFalse; |
| break; |
| } |
| } |
| return *string == '\0'; |
| } |
| |
| const FcChar8 * |
| FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcCaseWalker w1, w2; |
| FcChar8 c1, c2; |
| const FcChar8 *cur; |
| |
| if (!s1 || !s2) |
| return 0; |
| |
| if (s1 == s2) |
| return s1; |
| |
| FcStrCaseWalkerInit (s1, &w1); |
| FcStrCaseWalkerInit (s2, &w2); |
| |
| c2 = FcStrCaseWalkerNext (&w2); |
| |
| for (;;) |
| { |
| cur = w1.src; |
| c1 = FcStrCaseWalkerNext (&w1); |
| if (!c1) |
| break; |
| if (c1 == c2) |
| { |
| FcCaseWalker w1t = w1; |
| FcCaseWalker w2t = w2; |
| FcChar8 c1t, c2t; |
| |
| for (;;) |
| { |
| c1t = FcStrCaseWalkerNext (&w1t); |
| c2t = FcStrCaseWalkerNext (&w2t); |
| |
| if (!c2t) |
| return cur; |
| if (c2t != c1t) |
| break; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| const FcChar8 * |
| FcStrStr (const FcChar8 *s1, const FcChar8 *s2) |
| { |
| FcChar8 c1, c2; |
| const FcChar8 * p = s1; |
| const FcChar8 * b = s2; |
| |
| if (!s1 || !s2) |
| return 0; |
| |
| if (s1 == s2) |
| return s1; |
| |
| again: |
| c2 = *s2++; |
| |
| if (!c2) |
| return 0; |
| |
| for (;;) |
| { |
| p = s1; |
| c1 = *s1++; |
| if (!c1 || c1 == c2) |
| break; |
| } |
| |
| if (c1 != c2) |
| return 0; |
| |
| for (;;) |
| { |
| c1 = *s1; |
| c2 = *s2; |
| if (c1 && c2 && c1 != c2) |
| { |
| s1 = p + 1; |
| s2 = b; |
| goto again; |
| } |
| if (!c2) |
| return p; |
| if (!c1) |
| return 0; |
| ++ s1; |
| ++ s2; |
| } |
| /* never reached. */ |
| } |
| |
| int |
| FcUtf8ToUcs4 (const FcChar8 *src_orig, |
| FcChar32 *dst, |
| int len) |
| { |
| const FcChar8 *src = src_orig; |
| FcChar8 s; |
| int extra; |
| FcChar32 result; |
| |
| if (len == 0) |
| return 0; |
| |
| s = *src++; |
| len--; |
| |
| if (!(s & 0x80)) |
| { |
| result = s; |
| extra = 0; |
| } |
| else if (!(s & 0x40)) |
| { |
| return -1; |
| } |
| else if (!(s & 0x20)) |
| { |
| result = s & 0x1f; |
| extra = 1; |
| } |
| else if (!(s & 0x10)) |
| { |
| result = s & 0xf; |
| extra = 2; |
| } |
| else if (!(s & 0x08)) |
| { |
| result = s & 0x07; |
| extra = 3; |
| } |
| else if (!(s & 0x04)) |
| { |
| result = s & 0x03; |
| extra = 4; |
| } |
| else if ( ! (s & 0x02)) |
| { |
| result = s & 0x01; |
| extra = 5; |
| } |
| else |
| { |
| return -1; |
| } |
| if (extra > len) |
| return -1; |
| |
| while (extra--) |
| { |
| result <<= 6; |
| s = *src++; |
| |
| if ((s & 0xc0) != 0x80) |
| return -1; |
| |
| result |= s & 0x3f; |
| } |
| *dst = result; |
| return src - src_orig; |
| } |
| |
| FcBool |
| FcUtf8Len (const FcChar8 *string, |
| int len, |
| int *nchar, |
| int *wchar) |
| { |
| int n; |
| int clen; |
| FcChar32 c; |
| FcChar32 max; |
| |
| n = 0; |
| max = 0; |
| while (len) |
| { |
| clen = FcUtf8ToUcs4 (string, &c, len); |
| if (clen <= 0) /* malformed UTF8 string */ |
| return FcFalse; |
| if (c > max) |
| max = c; |
| string += clen; |
| len -= clen; |
| n++; |
| } |
| *nchar = n; |
| if (max >= 0x10000) |
| *wchar = 4; |
| else if (max > 0x100) |
| *wchar = 2; |
| else |
| *wchar = 1; |
| return FcTrue; |
| } |
| |
| int |
| FcUcs4ToUtf8 (FcChar32 ucs4, |
| FcChar8 dest[FC_UTF8_MAX_LEN]) |
| { |
| int bits; |
| FcChar8 *d = dest; |
| |
| if (ucs4 < 0x80) { *d++= ucs4; bits= -6; } |
| else if (ucs4 < 0x800) { *d++= ((ucs4 >> 6) & 0x1F) | 0xC0; bits= 0; } |
| else if (ucs4 < 0x10000) { *d++= ((ucs4 >> 12) & 0x0F) | 0xE0; bits= 6; } |
| else if (ucs4 < 0x200000) { *d++= ((ucs4 >> 18) & 0x07) | 0xF0; bits= 12; } |
| else if (ucs4 < 0x4000000) { *d++= ((ucs4 >> 24) & 0x03) | 0xF8; bits= 18; } |
| else if (ucs4 < 0x80000000) { *d++= ((ucs4 >> 30) & 0x01) | 0xFC; bits= 24; } |
| else return 0; |
| |
| for ( ; bits >= 0; bits-= 6) { |
| *d++= ((ucs4 >> bits) & 0x3F) | 0x80; |
| } |
| return d - dest; |
| } |
| |
| #define GetUtf16(src,endian) \ |
| ((FcChar16) ((src)[endian == FcEndianBig ? 0 : 1] << 8) | \ |
| (FcChar16) ((src)[endian == FcEndianBig ? 1 : 0])) |
| |
| int |
| FcUtf16ToUcs4 (const FcChar8 *src_orig, |
| FcEndian endian, |
| FcChar32 *dst, |
| int len) /* in bytes */ |
| { |
| const FcChar8 *src = src_orig; |
| FcChar16 a, b; |
| FcChar32 result; |
| |
| if (len < 2) |
| return 0; |
| |
| a = GetUtf16 (src, endian); src += 2; len -= 2; |
| |
| /* |
| * Check for surrogate |
| */ |
| if ((a & 0xfc00) == 0xd800) |
| { |
| if (len < 2) |
| return 0; |
| b = GetUtf16 (src, endian); src += 2; len -= 2; |
| /* |
| * Check for invalid surrogate sequence |
| */ |
| if ((b & 0xfc00) != 0xdc00) |
| return 0; |
| result = ((((FcChar32) a & 0x3ff) << 10) | |
| ((FcChar32) b & 0x3ff)) + 0x10000; |
| } |
| else |
| result = a; |
| *dst = result; |
| return src - src_orig; |
| } |
| |
| FcBool |
| FcUtf16Len (const FcChar8 *string, |
| FcEndian endian, |
| int len, /* in bytes */ |
| int *nchar, |
| int *wchar) |
| { |
| int n; |
| int clen; |
| FcChar32 c; |
| FcChar32 max; |
| |
| n = 0; |
| max = 0; |
| while (len) |
| { |
| clen = FcUtf16ToUcs4 (string, endian, &c, len); |
| if (clen <= 0) /* malformed UTF8 string */ |
| return FcFalse; |
| if (c > max) |
| max = c; |
| string += clen; |
| len -= clen; |
| n++; |
| } |
| *nchar = n; |
| if (max >= 0x10000) |
| *wchar = 4; |
| else if (max > 0x100) |
| *wchar = 2; |
| else |
| *wchar = 1; |
| return FcTrue; |
| } |
| |
| void |
| FcStrBufInit (FcStrBuf *buf, FcChar8 *init, int size) |
| { |
| if (init) |
| { |
| buf->buf = init; |
| buf->size = size; |
| } else |
| { |
| buf->buf = buf->buf_static; |
| buf->size = sizeof (buf->buf_static); |
| } |
| buf->allocated = FcFalse; |
| buf->failed = FcFalse; |
| buf->len = 0; |
| } |
| |
| void |
| FcStrBufDestroy (FcStrBuf *buf) |
| { |
| if (buf->allocated) |
| { |
| free (buf->buf); |
| FcStrBufInit (buf, 0, 0); |
| } |
| } |
| |
| FcChar8 * |
| FcStrBufDone (FcStrBuf *buf) |
| { |
| FcChar8 *ret; |
| |
| if (buf->failed) |
| ret = NULL; |
| else |
| ret = malloc (buf->len + 1); |
| if (ret) |
| { |
| memcpy (ret, buf->buf, buf->len); |
| ret[buf->len] = '\0'; |
| } |
| FcStrBufDestroy (buf); |
| return ret; |
| } |
| |
| FcChar8 * |
| FcStrBufDoneStatic (FcStrBuf *buf) |
| { |
| FcStrBufChar (buf, '\0'); |
| |
| if (buf->failed) |
| return NULL; |
| |
| return buf->buf; |
| } |
| |
| FcBool |
| FcStrBufChar (FcStrBuf *buf, FcChar8 c) |
| { |
| if (buf->len == buf->size) |
| { |
| FcChar8 *new; |
| int size; |
| |
| if (buf->failed) |
| return FcFalse; |
| |
| if (buf->allocated) |
| { |
| size = buf->size * 2; |
| new = realloc (buf->buf, size); |
| } |
| else |
| { |
| size = buf->size + 64; |
| new = malloc (size); |
| if (new) |
| { |
| buf->allocated = FcTrue; |
| memcpy (new, buf->buf, buf->len); |
| } |
| } |
| if (!new) |
| { |
| buf->failed = FcTrue; |
| return FcFalse; |
| } |
| buf->size = size; |
| buf->buf = new; |
| } |
| buf->buf[buf->len++] = c; |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrBufString (FcStrBuf *buf, const FcChar8 *s) |
| { |
| FcChar8 c; |
| while ((c = *s++)) |
| if (!FcStrBufChar (buf, c)) |
| return FcFalse; |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrBufData (FcStrBuf *buf, const FcChar8 *s, int len) |
| { |
| while (len-- > 0) |
| if (!FcStrBufChar (buf, *s++)) |
| return FcFalse; |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrUsesHome (const FcChar8 *s) |
| { |
| return *s == '~'; |
| } |
| |
| FcBool |
| FcStrIsAbsoluteFilename (const FcChar8 *s) |
| { |
| #ifdef _WIN32 |
| if (*s == '\\' || |
| (isalpha (*s) && s[1] == ':' && (s[2] == '/' || s[2] == '\\'))) |
| return FcTrue; |
| #endif |
| return *s == '/'; |
| } |
| |
| FcChar8 * |
| FcStrBuildFilename (const FcChar8 *path, |
| ...) |
| { |
| va_list ap; |
| FcStrSet *sset; |
| FcStrList *list; |
| FcChar8 *s, *ret = NULL, *p; |
| size_t len = 0; |
| |
| if (!path) |
| return NULL; |
| |
| sset = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64); |
| if (!sset) |
| return NULL; |
| |
| if (!FcStrSetAdd (sset, path)) |
| goto bail0; |
| |
| va_start (ap, path); |
| while (1) |
| { |
| s = (FcChar8 *)va_arg (ap, FcChar8 *); |
| if (!s) |
| break; |
| if (!FcStrSetAdd (sset, s)) |
| goto bail1; |
| } |
| list = FcStrListCreate (sset); |
| while ((s = FcStrListNext (list))) |
| { |
| len += strlen ((const char *)s) + 1; |
| } |
| list->n = 0; |
| ret = malloc (sizeof (FcChar8) * (len + 1)); |
| if (!ret) |
| goto bail2; |
| p = ret; |
| while ((s = FcStrListNext (list))) |
| { |
| if (p != ret) |
| { |
| p[0] = FC_DIR_SEPARATOR; |
| p++; |
| } |
| len = strlen ((const char *)s); |
| memcpy (p, s, len); |
| p += len; |
| } |
| *p = 0; |
| |
| bail2: |
| FcStrListDone (list); |
| bail1: |
| va_end (ap); |
| bail0: |
| FcStrSetDestroy (sset); |
| |
| return ret; |
| } |
| |
| FcChar8 * |
| FcStrCopyFilename (const FcChar8 *s) |
| { |
| FcChar8 *new; |
| |
| if (*s == '~') |
| { |
| FcChar8 *home = FcConfigHome (); |
| FcChar8 *full; |
| int size; |
| if (!home) |
| return NULL; |
| size = strlen ((char *) home) + strlen ((char *) s); |
| full = (FcChar8 *) malloc (size + 1); |
| if (!full) |
| return NULL; |
| strcpy ((char *) full, (char *) home); |
| strcat ((char *) full, (char *) s + 1); |
| new = FcStrCanonFilename (full); |
| free (full); |
| } |
| else |
| new = FcStrCanonFilename (s); |
| |
| return new; |
| } |
| |
| FcChar8 * |
| FcStrLastSlash (const FcChar8 *path) |
| { |
| FcChar8 *slash; |
| |
| slash = (FcChar8 *) strrchr ((const char *) path, '/'); |
| #ifdef _WIN32 |
| { |
| FcChar8 *backslash; |
| |
| backslash = (FcChar8 *) strrchr ((const char *) path, '\\'); |
| if (!slash || (backslash && backslash > slash)) |
| slash = backslash; |
| } |
| #endif |
| |
| return slash; |
| } |
| |
| FcChar8 * |
| FcStrDirname (const FcChar8 *file) |
| { |
| FcChar8 *slash; |
| FcChar8 *dir; |
| |
| slash = FcStrLastSlash (file); |
| if (!slash) |
| return FcStrCopy ((FcChar8 *) "."); |
| dir = malloc ((slash - file) + 1); |
| if (!dir) |
| return 0; |
| strncpy ((char *) dir, (const char *) file, slash - file); |
| dir[slash - file] = '\0'; |
| return dir; |
| } |
| |
| FcChar8 * |
| FcStrBasename (const FcChar8 *file) |
| { |
| FcChar8 *slash; |
| |
| slash = FcStrLastSlash (file); |
| if (!slash) |
| return FcStrCopy (file); |
| return FcStrCopy (slash + 1); |
| } |
| |
| static FcChar8 * |
| FcStrCanonAbsoluteFilename (const FcChar8 *s) |
| { |
| FcChar8 *file; |
| FcChar8 *f; |
| const FcChar8 *slash; |
| int size; |
| |
| size = strlen ((char *) s) + 1; |
| file = malloc (size); |
| if (!file) |
| return NULL; |
| slash = NULL; |
| f = file; |
| #ifdef _WIN32 |
| if (*s == '/' && *(s+1) == '/') /* Network path, do not squash // */ |
| *f++ = *s++; |
| #endif |
| for (;;) { |
| if (*s == '/' || *s == '\0') |
| { |
| if (slash) |
| { |
| switch (s - slash) { |
| case 1: |
| f -= 1; /* squash // and trim final / from file */ |
| break; |
| case 2: |
| if (!strncmp ((char *) slash, "/.", 2)) |
| { |
| f -= 2; /* trim /. from file */ |
| } |
| break; |
| case 3: |
| if (!strncmp ((char *) slash, "/..", 3)) |
| { |
| f -= 3; /* trim /.. from file */ |
| while (f > file) { |
| if (*--f == '/') |
| break; |
| } |
| } |
| break; |
| } |
| } |
| slash = s; |
| } |
| if (!(*f++ = *s++)) |
| break; |
| } |
| return file; |
| } |
| |
| #ifdef _WIN32 |
| /* |
| * Convert '\\' to '/' , remove double '/' |
| */ |
| static void |
| FcConvertDosPath (char *str) |
| { |
| size_t len = strlen (str); |
| char *p = str; |
| char *dest = str; |
| char *end = str + len; |
| char last = 0; |
| |
| if (*p == '\\') |
| { |
| *p = '/'; |
| p++; |
| dest++; |
| } |
| while (p < end) |
| { |
| if (*p == '\\') |
| *p = '/'; |
| |
| if (*p != '/' |
| || last != '/') |
| { |
| *dest++ = *p; |
| } |
| |
| last = *p; |
| p++; |
| } |
| |
| *dest = 0; |
| } |
| #endif |
| |
| FcChar8 * |
| FcStrCanonFilename (const FcChar8 *s) |
| { |
| #ifdef _WIN32 |
| FcChar8 full[FC_MAX_FILE_LEN + 2]; |
| int size = GetFullPathName ((LPCSTR) s, sizeof (full) -1, |
| (LPSTR) full, NULL); |
| |
| if (size == 0) |
| perror ("GetFullPathName"); |
| |
| FcConvertDosPath ((char *) full); |
| return FcStrCanonAbsoluteFilename (full); |
| #else |
| if (s[0] == '/') |
| return FcStrCanonAbsoluteFilename (s); |
| else |
| { |
| FcChar8 *full; |
| FcChar8 *file; |
| |
| FcChar8 cwd[FC_MAX_FILE_LEN + 2]; |
| if (getcwd ((char *) cwd, FC_MAX_FILE_LEN) == NULL) |
| return NULL; |
| full = FcStrBuildFilename (cwd, s, NULL); |
| file = FcStrCanonAbsoluteFilename (full); |
| FcStrFree (full); |
| return file; |
| } |
| #endif |
| } |
| |
| |
| FcStrSet * |
| FcStrSetCreate (void) |
| { |
| return FcStrSetCreateEx (FCSS_DEFAULT); |
| } |
| |
| FcStrSet * |
| FcStrSetCreateEx (unsigned int control) |
| { |
| FcStrSet *set = malloc (sizeof (FcStrSet)); |
| if (!set) |
| return 0; |
| FcRefInit (&set->ref, 1); |
| set->num = 0; |
| set->size = 0; |
| set->strs = 0; |
| set->control = control; |
| return set; |
| } |
| |
| static FcBool |
| _FcStrSetGrow (FcStrSet *set, int growElements) |
| { |
| /* accommodate an additional NULL entry at the end of the array */ |
| FcChar8 **strs = malloc ((set->size + growElements + 1) * sizeof (FcChar8 *)); |
| if (!strs) |
| return FcFalse; |
| if (set->num) |
| memcpy (strs, set->strs, set->num * sizeof (FcChar8 *)); |
| if (set->strs) |
| free (set->strs); |
| set->size = set->size + growElements; |
| set->strs = strs; |
| return FcTrue; |
| } |
| |
| static FcBool |
| _FcStrSetAppend (FcStrSet *set, FcChar8 *s) |
| { |
| if (!FcStrSetHasControlBit (set, FCSS_ALLOW_DUPLICATES)) |
| { |
| if (FcStrSetMember (set, s)) |
| { |
| FcStrFree (s); |
| return FcTrue; |
| } |
| } |
| if (set->num == set->size) |
| { |
| int growElements = FcStrSetHasControlBit (set, FCSS_GROW_BY_64) ? 64 : 1; |
| if (!_FcStrSetGrow(set, growElements)) |
| return FcFalse; |
| } |
| set->strs[set->num++] = s; |
| set->strs[set->num] = 0; |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrSetMember (FcStrSet *set, const FcChar8 *s) |
| { |
| int i; |
| |
| for (i = 0; i < set->num; i++) |
| if (!FcStrCmp (set->strs[i], s)) |
| return FcTrue; |
| return FcFalse; |
| } |
| |
| static int |
| fc_strcmp_r (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 **ret) |
| { |
| FcChar8 c1, c2; |
| |
| if (s1 == s2) |
| { |
| if (ret) |
| *ret = NULL; |
| return 0; |
| } |
| for (;;) |
| { |
| if (s1) |
| c1 = *s1++; |
| else |
| c1 = 0; |
| if (s2) |
| c2 = *s2++; |
| else |
| c2 = 0; |
| if (!c1 || c1 != c2) |
| break; |
| } |
| if (ret) |
| *ret = s1; |
| return (int) c1 - (int) c2; |
| } |
| |
| FcBool |
| FcStrSetMemberAB (FcStrSet *set, const FcChar8 *a, FcChar8 *b, FcChar8 **ret) |
| { |
| int i; |
| const FcChar8 *s = NULL; |
| |
| for (i = 0; i < set->num; i++) |
| { |
| if (!fc_strcmp_r (set->strs[i], a, &s) && s) |
| { |
| if (!fc_strcmp_r (s, b, NULL)) |
| { |
| if (ret) |
| *ret = set->strs[i]; |
| return FcTrue; |
| } |
| } |
| } |
| if (ret) |
| *ret = NULL; |
| return FcFalse; |
| } |
| |
| FcBool |
| FcStrSetEqual (FcStrSet *sa, FcStrSet *sb) |
| { |
| int i; |
| if (sa->num != sb->num) |
| return FcFalse; |
| for (i = 0; i < sa->num; i++) |
| if (!FcStrSetMember (sb, sa->strs[i])) |
| return FcFalse; |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrSetAdd (FcStrSet *set, const FcChar8 *s) |
| { |
| FcChar8 *new = FcStrCopy (s); |
| if (!new) |
| return FcFalse; |
| if (!_FcStrSetAppend (set, new)) |
| { |
| FcStrFree (new); |
| return FcFalse; |
| } |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrSetAddTriple (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *c) |
| { |
| FcChar8 *new = FcStrMakeTriple (a, b, c); |
| if (!new) |
| return FcFalse; |
| if (!_FcStrSetAppend (set, new)) |
| { |
| FcStrFree (new); |
| return FcFalse; |
| } |
| return FcTrue; |
| } |
| |
| const FcChar8 * |
| FcStrTripleSecond (FcChar8 *str) |
| { |
| FcChar8 *second = str + strlen((char *) str) + 1; |
| |
| if (*second == '\0') |
| return 0; |
| return second; |
| } |
| |
| const FcChar8 * |
| FcStrTripleThird (FcChar8 *str) |
| { |
| FcChar8 *second = str + strlen ((char *) str) + 1; |
| FcChar8 *third = second + strlen ((char *) second) + 1; |
| |
| if (*third == '\0') |
| return 0; |
| return third; |
| } |
| |
| FcBool |
| FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s) |
| { |
| FcChar8 *new = FcStrCopyFilename (s); |
| if (!new) |
| return FcFalse; |
| if (!_FcStrSetAppend (set, new)) |
| { |
| FcStrFree (new); |
| return FcFalse; |
| } |
| return FcTrue; |
| } |
| |
| FcBool |
| FcStrSetAddFilenamePairWithSalt (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *salt) |
| { |
| FcChar8 *new_a = NULL; |
| FcChar8 *new_b = NULL; |
| FcChar8 *rs = NULL; |
| FcBool ret; |
| |
| if (a) |
| { |
| new_a = FcStrCopyFilename (a); |
| if (!new_a) |
| return FcFalse; |
| } |
| if (b) |
| { |
| new_b = FcStrCopyFilename(b); |
| if (!new_b) |
| { |
| if (new_a) |
| FcStrFree(new_a); |
| return FcFalse; |
| } |
| } |
| /* Override maps with new one if exists */ |
| if (FcStrSetMemberAB (set, new_a, new_b, &rs)) |
| { |
| FcStrSetDel (set, rs); |
| } |
| ret = FcStrSetAddTriple (set, new_a, new_b, salt); |
| if (new_a) |
| FcStrFree (new_a); |
| if (new_b) |
| FcStrFree (new_b); |
| return ret; |
| } |
| |
| FcBool |
| FcStrSetAddLangs (FcStrSet *strs, const char *languages) |
| { |
| const char *p = languages, *next; |
| FcChar8 lang[128] = {0}, *normalized_lang; |
| size_t len; |
| FcBool ret = FcFalse; |
| |
| if (!languages) |
| return FcFalse; |
| |
| while ((next = strchr (p, ':'))) |
| { |
| len = next - p; |
| len = FC_MIN (len, 127); |
| strncpy ((char *) lang, p, len); |
| lang[len] = 0; |
| /* ignore an empty item */ |
| if (*lang) |
| { |
| normalized_lang = FcLangNormalize ((const FcChar8 *) lang); |
| if (normalized_lang) |
| { |
| FcStrSetAdd (strs, normalized_lang); |
| FcStrFree (normalized_lang); |
| ret = FcTrue; |
| } |
| } |
| p = next + 1; |
| } |
| if (*p) |
| { |
| normalized_lang = FcLangNormalize ((const FcChar8 *) p); |
| if (normalized_lang) |
| { |
| FcStrSetAdd (strs, normalized_lang); |
| FcStrFree (normalized_lang); |
| ret = FcTrue; |
| } |
| } |
| |
| return ret; |
| } |
| |
| FcBool |
| FcStrSetDel (FcStrSet *set, const FcChar8 *s) |
| { |
| int i; |
| |
| for (i = 0; i < set->num; i++) |
| if (!FcStrCmp (set->strs[i], s)) |
| { |
| FcStrFree (set->strs[i]); |
| /* |
| * copy remaining string pointers and trailing |
| * NULL |
| */ |
| memmove (&set->strs[i], &set->strs[i+1], |
| (set->num - i) * sizeof (FcChar8 *)); |
| set->num--; |
| return FcTrue; |
| } |
| return FcFalse; |
| } |
| |
| FcBool |
| FcStrSetDeleteAll (FcStrSet *set) |
| { |
| int i; |
| |
| if (FcRefIsConst (&set->ref)) |
| return FcFalse; |
| |
| for (i = set->num; i > 0; i--) |
| { |
| FcStrFree (set->strs[i - 1]); |
| set->num--; |
| } |
| return FcTrue; |
| } |
| |
| /* TODO Make public */ |
| static FcStrSet * |
| FcStrSetReference (FcStrSet *set) |
| { |
| if (FcRefIsConst (&set->ref)) |
| return set; |
| |
| FcRefInc (&set->ref); |
| return set; |
| } |
| |
| void |
| FcStrSetDestroy (FcStrSet *set) |
| { |
| int i; |
| |
| /* We rely on this in FcGetDefaultLangs for caching. */ |
| if (FcRefIsConst (&set->ref)) |
| return; |
| |
| if (FcRefDec (&set->ref) != 1) |
| return; |
| |
| for (i = 0; i < set->num; i++) |
| FcStrFree (set->strs[i]); |
| if (set->strs) |
| free (set->strs); |
| free (set); |
| } |
| |
| FcStrList * |
| FcStrListCreate (FcStrSet *set) |
| { |
| FcStrList *list; |
| |
| list = malloc (sizeof (FcStrList)); |
| if (!list) |
| return 0; |
| list->set = set; |
| FcStrSetReference (set); |
| list->n = 0; |
| return list; |
| } |
| |
| void |
| FcStrListFirst (FcStrList *list) |
| { |
| list->n = 0; |
| } |
| |
| FcChar8 * |
| FcStrListNext (FcStrList *list) |
| { |
| if (list->n >= list->set->num) |
| return 0; |
| return list->set->strs[list->n++]; |
| } |
| |
| void |
| FcStrListDone (FcStrList *list) |
| { |
| FcStrSetDestroy (list->set); |
| free (list); |
| } |
| |
| #define __fcstr__ |
| #include "fcaliastail.h" |
| #undef __fcstr__ |