+
+#include "../fc-case/fccase.h"
+
+#define FcCaseFoldUpperCount(cf) \
+ ((cf)->method == FC_CASE_FOLD_FULL ? 1 : (cf)->count)
+
+#define FC_STR_CANON_BUF_LEN 1024
+
+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
+FcStrCaseWalkerNext (FcCaseWalker *w)
+{
+ FcChar8 r;
+
+ if (w->read)
+ {
+ if ((r = *w->read++))
+ return r;
+ w->read = 0;
+ }
+ r = *w->src++;
+
+ if ((r & 0xc0) == 0xc0)
+ return FcStrCaseWalkerLong (w, r);
+ if ('A' <= r && r <= 'Z')
+ r = r - 'A' + 'a';
+ return r;
+}
+
+static FcChar8
+FcStrCaseWalkerNextIgnoreBlanks (FcCaseWalker *w)
+{
+ FcChar8 r;
+
+ if (w->read)
+ {
+ if ((r = *w->read++))
+ return r;
+ w->read = 0;
+ }
+ do
+ {
+ r = *w->src++;
+ } while (r == ' ');
+
+ if ((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;
+ FcMemAlloc (FC_MEM_STRING, len + 1);
+ FcStrCaseWalkerInit (s, &w);
+ while ((*d++ = FcStrCaseWalkerNext (&w)));
+ return dst;
+}
+