fcstr.c revision a4e54154
12c393a42Smrg/*
2a6844aabSmrg * fontconfig/src/fcstr.c
32c393a42Smrg *
42c393a42Smrg * Copyright © 2000 Keith Packard
52c393a42Smrg *
62c393a42Smrg * Permission to use, copy, modify, distribute, and sell this software and its
72c393a42Smrg * documentation for any purpose is hereby granted without fee, provided that
82c393a42Smrg * the above copyright notice appear in all copies and that both that
92c393a42Smrg * copyright notice and this permission notice appear in supporting
10ca08ab68Smrg * documentation, and that the name of the author(s) not be used in
112c393a42Smrg * advertising or publicity pertaining to distribution of the software without
12ca08ab68Smrg * specific, written prior permission.  The authors make no
132c393a42Smrg * representations about the suitability of this software for any purpose.  It
142c393a42Smrg * is provided "as is" without express or implied warranty.
152c393a42Smrg *
16a6844aabSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18a6844aabSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
192c393a42Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
202c393a42Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
212c393a42Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
222c393a42Smrg * PERFORMANCE OF THIS SOFTWARE.
232c393a42Smrg */
242c393a42Smrg
252c393a42Smrg#include "fcint.h"
262c393a42Smrg#include <stdlib.h>
272c393a42Smrg#include <ctype.h>
282c393a42Smrg#include <string.h>
29c9710b42Smrg
30c9710b42Smrg
31c9710b42Smrg/* Objects MT-safe for readonly access. */
322c393a42Smrg
332c393a42SmrgFcChar8 *
342c393a42SmrgFcStrCopy (const FcChar8 *s)
352c393a42Smrg{
36c9710b42Smrg    return FcStrdup (s);
372c393a42Smrg}
382c393a42Smrg
39a4e54154Smrgstatic FcChar8 *
40a4e54154SmrgFcStrMakeTriple (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *s3)
41a4e54154Smrg{
42a4e54154Smrg    int	    s1l = s1 ? strlen ((char *) s1) : 0;
43a4e54154Smrg    int	    s2l = s2 ? strlen ((char *) s2) : 0;
44a4e54154Smrg    int     s3l = s3 ? strlen ((char *) s3) : 0;
45a4e54154Smrg    int	    l = s1l + 1 + s2l + 1 + s3l + 1;
46a4e54154Smrg    FcChar8 *s = malloc (l);
47a4e54154Smrg
48a4e54154Smrg    if (!s)
49a4e54154Smrg	return 0;
50a4e54154Smrg    if (s1)
51a4e54154Smrg	memcpy (s, s1, s1l + 1);
52a4e54154Smrg    else
53a4e54154Smrg	s[0] = '\0';
54a4e54154Smrg    if (s2)
55a4e54154Smrg	memcpy (s + s1l + 1, s2, s2l + 1);
56a4e54154Smrg    else
57a4e54154Smrg	s[s1l + 1] = '\0';
58a4e54154Smrg    if (s3)
59a4e54154Smrg	memcpy (s + s1l + 1 + s2l + 1, s3, s3l + 1);
60a4e54154Smrg    else
61a4e54154Smrg	s[s1l + 1 + s2l + 1] = '\0';
62a4e54154Smrg    return s;
63a4e54154Smrg}
64a4e54154Smrg
652c393a42SmrgFcChar8 *
662c393a42SmrgFcStrPlus (const FcChar8 *s1, const FcChar8 *s2)
672c393a42Smrg{
68ca08ab68Smrg    int	    s1l = strlen ((char *) s1);
69ca08ab68Smrg    int	    s2l = strlen ((char *) s2);
70ca08ab68Smrg    int	    l = s1l + s2l + 1;
712c393a42Smrg    FcChar8 *s = malloc (l);
722c393a42Smrg
732c393a42Smrg    if (!s)
742c393a42Smrg	return 0;
75ca08ab68Smrg    memcpy (s, s1, s1l);
76ca08ab68Smrg    memcpy (s + s1l, s2, s2l + 1);
772c393a42Smrg    return s;
782c393a42Smrg}
792c393a42Smrg
802c393a42Smrgvoid
812c393a42SmrgFcStrFree (FcChar8 *s)
822c393a42Smrg{
832c393a42Smrg    free (s);
842c393a42Smrg}
852c393a42Smrg
862c393a42Smrg
872c393a42Smrg#include "../fc-case/fccase.h"
882c393a42Smrg
892c393a42Smrg#define FcCaseFoldUpperCount(cf) \
902c393a42Smrg    ((cf)->method == FC_CASE_FOLD_FULL ? 1 : (cf)->count)
912c393a42Smrg
922c393a42Smrgtypedef struct _FcCaseWalker {
932c393a42Smrg    const FcChar8   *read;
942c393a42Smrg    const FcChar8   *src;
952c393a42Smrg    FcChar8	    utf8[FC_MAX_CASE_FOLD_CHARS + 1];
962c393a42Smrg} FcCaseWalker;
972c393a42Smrg
982c393a42Smrgstatic void
992c393a42SmrgFcStrCaseWalkerInit (const FcChar8 *src, FcCaseWalker *w)
1002c393a42Smrg{
1012c393a42Smrg    w->src = src;
1022c393a42Smrg    w->read = 0;
1032c393a42Smrg}
1042c393a42Smrg
1052c393a42Smrgstatic FcChar8
1062c393a42SmrgFcStrCaseWalkerLong (FcCaseWalker *w, FcChar8 r)
1072c393a42Smrg{
1082c393a42Smrg    FcChar32	ucs4;
1092c393a42Smrg    int		slen;
1102c393a42Smrg    int		len = strlen((char*)w->src);
1112c393a42Smrg
1122c393a42Smrg    slen = FcUtf8ToUcs4 (w->src - 1, &ucs4, len + 1);
1132c393a42Smrg    if (slen <= 0)
1142c393a42Smrg	return r;
1152c393a42Smrg    if (FC_MIN_FOLD_CHAR <= ucs4 && ucs4 <= FC_MAX_FOLD_CHAR)
1162c393a42Smrg    {
1172c393a42Smrg	int min = 0;
1182c393a42Smrg	int max = FC_NUM_CASE_FOLD;
1192c393a42Smrg
1202c393a42Smrg	while (min <= max)
1212c393a42Smrg	{
1222c393a42Smrg	    int		mid = (min + max) >> 1;
1232c393a42Smrg	    FcChar32    low = fcCaseFold[mid].upper;
1242c393a42Smrg	    FcChar32    high = low + FcCaseFoldUpperCount (&fcCaseFold[mid]);
125ca08ab68Smrg
1262c393a42Smrg	    if (high <= ucs4)
1272c393a42Smrg		min = mid + 1;
1282c393a42Smrg	    else if (ucs4 < low)
1292c393a42Smrg		max = mid - 1;
1302c393a42Smrg	    else
1312c393a42Smrg	    {
1322c393a42Smrg		const FcCaseFold    *fold = &fcCaseFold[mid];
1332c393a42Smrg		int		    dlen;
1342c393a42Smrg
1352c393a42Smrg		switch (fold->method) {
1362c393a42Smrg		case  FC_CASE_FOLD_EVEN_ODD:
1372c393a42Smrg		    if ((ucs4 & 1) != (fold->upper & 1))
1382c393a42Smrg			return r;
1392c393a42Smrg		    /* fall through ... */
1402c393a42Smrg		default:
1412c393a42Smrg		    dlen = FcUcs4ToUtf8 (ucs4 + fold->offset, w->utf8);
1422c393a42Smrg		    break;
1432c393a42Smrg		case FC_CASE_FOLD_FULL:
1442c393a42Smrg		    dlen = fold->count;
1452c393a42Smrg		    memcpy (w->utf8, fcCaseFoldChars + fold->offset, dlen);
1462c393a42Smrg		    break;
1472c393a42Smrg		}
1482c393a42Smrg
1492c393a42Smrg		/* consume rest of src utf-8 bytes */
1502c393a42Smrg		w->src += slen - 1;
1512c393a42Smrg
1522c393a42Smrg		/* read from temp buffer */
1532c393a42Smrg		w->utf8[dlen] = '\0';
1542c393a42Smrg		w->read = w->utf8;
1552c393a42Smrg		return *w->read++;
1562c393a42Smrg	    }
1572c393a42Smrg	}
1582c393a42Smrg    }
1592c393a42Smrg    return r;
1602c393a42Smrg}
1612c393a42Smrg
1622c393a42Smrgstatic FcChar8
163a4e54154SmrgFcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
1642c393a42Smrg{
1652c393a42Smrg    FcChar8	r;
1662c393a42Smrg
167a4e54154Smrg    if (FC_UNLIKELY (w->read != NULL))
1682c393a42Smrg    {
1692c393a42Smrg	if ((r = *w->read++))
1702c393a42Smrg	    return r;
1712c393a42Smrg	w->read = 0;
1722c393a42Smrg    }
1732c393a42Smrg    do
1742c393a42Smrg    {
1752c393a42Smrg	r = *w->src++;
176c9710b42Smrg    } while (r != 0 && delims && strchr (delims, r));
177ca08ab68Smrg
178a4e54154Smrg    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
179a4e54154Smrg	return FcStrCaseWalkerLong (w, r);
180a4e54154Smrg    if ('A' <= r && r <= 'Z')
181a4e54154Smrg        r = r - 'A' + 'a';
182a4e54154Smrg    return r;
183a4e54154Smrg}
184a4e54154Smrg
185a4e54154Smrgstatic FcChar8
186a4e54154SmrgFcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
187a4e54154Smrg{
188a4e54154Smrg    FcChar8	r;
189a4e54154Smrg
190a4e54154Smrg    if (FC_UNLIKELY (w->read != NULL))
191a4e54154Smrg    {
192a4e54154Smrg	if ((r = *w->read++))
193a4e54154Smrg	    return r;
194a4e54154Smrg	w->read = 0;
195a4e54154Smrg    }
196a4e54154Smrg    do
197a4e54154Smrg    {
198a4e54154Smrg	r = *w->src++;
199a4e54154Smrg    } while (r == ' ');
200a4e54154Smrg
201a4e54154Smrg    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
202a4e54154Smrg	return FcStrCaseWalkerLong (w, r);
203a4e54154Smrg    if ('A' <= r && r <= 'Z')
204a4e54154Smrg        r = r - 'A' + 'a';
205a4e54154Smrg    return r;
206a4e54154Smrg}
207a4e54154Smrg
208a4e54154Smrgstatic FcChar8
209a4e54154SmrgFcStrCaseWalkerNext (FcCaseWalker *w)
210a4e54154Smrg{
211a4e54154Smrg    FcChar8	r;
212a4e54154Smrg
213a4e54154Smrg    if (FC_UNLIKELY (w->read != NULL))
214a4e54154Smrg    {
215a4e54154Smrg	if ((r = *w->read++))
216a4e54154Smrg	    return r;
217a4e54154Smrg	w->read = 0;
218a4e54154Smrg    }
219a4e54154Smrg
220a4e54154Smrg    r = *w->src++;
221a4e54154Smrg
222a4e54154Smrg    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
2232c393a42Smrg	return FcStrCaseWalkerLong (w, r);
2242c393a42Smrg    if ('A' <= r && r <= 'Z')
2252c393a42Smrg        r = r - 'A' + 'a';
2262c393a42Smrg    return r;
2272c393a42Smrg}
2282c393a42Smrg
2292c393a42SmrgFcChar8 *
2302c393a42SmrgFcStrDowncase (const FcChar8 *s)
2312c393a42Smrg{
2322c393a42Smrg    FcCaseWalker    w;
2332c393a42Smrg    int		    len = 0;
2342c393a42Smrg    FcChar8	    *dst, *d;
2352c393a42Smrg
2362c393a42Smrg    FcStrCaseWalkerInit (s, &w);
237a4e54154Smrg    while (FcStrCaseWalkerNext (&w))
2382c393a42Smrg	len++;
2392c393a42Smrg    d = dst = malloc (len + 1);
2402c393a42Smrg    if (!d)
2412c393a42Smrg	return 0;
2422c393a42Smrg    FcStrCaseWalkerInit (s, &w);
243a4e54154Smrg    while ((*d++ = FcStrCaseWalkerNext (&w)));
2442c393a42Smrg    return dst;
2452c393a42Smrg}
2462c393a42Smrg
2472c393a42Smrgint
2482c393a42SmrgFcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
2492c393a42Smrg{
2502c393a42Smrg    FcCaseWalker    w1, w2;
2512c393a42Smrg    FcChar8	    c1, c2;
2522c393a42Smrg
2532c393a42Smrg    if (s1 == s2) return 0;
254ca08ab68Smrg
2552c393a42Smrg    FcStrCaseWalkerInit (s1, &w1);
2562c393a42Smrg    FcStrCaseWalkerInit (s2, &w2);
257ca08ab68Smrg
258ca08ab68Smrg    for (;;)
2592c393a42Smrg    {
260a4e54154Smrg	c1 = FcStrCaseWalkerNext (&w1);
261a4e54154Smrg	c2 = FcStrCaseWalkerNext (&w2);
2622c393a42Smrg	if (!c1 || (c1 != c2))
2632c393a42Smrg	    break;
2642c393a42Smrg    }
2652c393a42Smrg    return (int) c1 - (int) c2;
2662c393a42Smrg}
2672c393a42Smrg
2682c393a42Smrgint
2692c393a42SmrgFcStrCmpIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
2702c393a42Smrg{
2712c393a42Smrg    FcCaseWalker    w1, w2;
2722c393a42Smrg    FcChar8	    c1, c2;
2732c393a42Smrg
2742c393a42Smrg    if (s1 == s2) return 0;
275ca08ab68Smrg
2762c393a42Smrg    FcStrCaseWalkerInit (s1, &w1);
2772c393a42Smrg    FcStrCaseWalkerInit (s2, &w2);
278ca08ab68Smrg
279ca08ab68Smrg    for (;;)
2802c393a42Smrg    {
281a4e54154Smrg	c1 = FcStrCaseWalkerNextNonBlank (&w1);
282a4e54154Smrg	c2 = FcStrCaseWalkerNextNonBlank (&w2);
2832c393a42Smrg	if (!c1 || (c1 != c2))
2842c393a42Smrg	    break;
2852c393a42Smrg    }
2862c393a42Smrg    return (int) c1 - (int) c2;
2872c393a42Smrg}
2882c393a42Smrg
2892c393a42Smrgint
2902c393a42SmrgFcStrCmp (const FcChar8 *s1, const FcChar8 *s2)
2912c393a42Smrg{
2922c393a42Smrg    FcChar8 c1, c2;
293ca08ab68Smrg
2942c393a42Smrg    if (s1 == s2)
2952c393a42Smrg	return 0;
296ca08ab68Smrg    for (;;)
2972c393a42Smrg    {
2982c393a42Smrg	c1 = *s1++;
2992c393a42Smrg	c2 = *s2++;
3002c393a42Smrg	if (!c1 || c1 != c2)
3012c393a42Smrg	    break;
3022c393a42Smrg    }
3032c393a42Smrg    return (int) c1 - (int) c2;
3042c393a42Smrg}
3052c393a42Smrg
3062c393a42Smrg/*
3072c393a42Smrg * Return a hash value for a string
3082c393a42Smrg */
3092c393a42Smrg
3102c393a42SmrgFcChar32
3112c393a42SmrgFcStrHashIgnoreCase (const FcChar8 *s)
3122c393a42Smrg{
3132c393a42Smrg    FcChar32	    h = 0;
3142c393a42Smrg    FcCaseWalker    w;
3152c393a42Smrg    FcChar8	    c;
3162c393a42Smrg
3172c393a42Smrg    FcStrCaseWalkerInit (s, &w);
318a4e54154Smrg    while ((c = FcStrCaseWalkerNext (&w)))
319a4e54154Smrg	h = ((h << 3) ^ (h >> 3)) ^ c;
320a4e54154Smrg    return h;
321a4e54154Smrg}
322a4e54154Smrg
323a4e54154SmrgFcChar32
324a4e54154SmrgFcStrHashIgnoreBlanksAndCase (const FcChar8 *s)
325a4e54154Smrg{
326a4e54154Smrg    FcChar32	    h = 0;
327a4e54154Smrg    FcCaseWalker    w;
328a4e54154Smrg    FcChar8	    c;
329a4e54154Smrg
330a4e54154Smrg    FcStrCaseWalkerInit (s, &w);
331a4e54154Smrg    while ((c = FcStrCaseWalkerNextNonBlank (&w)))
3322c393a42Smrg	h = ((h << 3) ^ (h >> 3)) ^ c;
3332c393a42Smrg    return h;
3342c393a42Smrg}
3352c393a42Smrg
3362c393a42Smrg/*
3372c393a42Smrg * Is the head of s1 equal to s2?
3382c393a42Smrg */
3392c393a42Smrg
3402c393a42Smrgstatic FcBool
3412c393a42SmrgFcStrIsAtIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
3422c393a42Smrg{
3432c393a42Smrg    FcCaseWalker    w1, w2;
3442c393a42Smrg    FcChar8	    c1, c2;
3452c393a42Smrg
3462c393a42Smrg    FcStrCaseWalkerInit (s1, &w1);
3472c393a42Smrg    FcStrCaseWalkerInit (s2, &w2);
348ca08ab68Smrg
349ca08ab68Smrg    for (;;)
3502c393a42Smrg    {
351a4e54154Smrg	c1 = FcStrCaseWalkerNextNonBlank (&w1);
352a4e54154Smrg	c2 = FcStrCaseWalkerNextNonBlank (&w2);
3532c393a42Smrg	if (!c1 || (c1 != c2))
3542c393a42Smrg	    break;
3552c393a42Smrg    }
3562c393a42Smrg    return c1 == c2 || !c2;
3572c393a42Smrg}
3582c393a42Smrg
3592c393a42Smrg/*
3602c393a42Smrg * Does s1 contain an instance of s2 (ignoring blanks and case)?
3612c393a42Smrg */
3622c393a42Smrg
3632c393a42Smrgconst FcChar8 *
3642c393a42SmrgFcStrContainsIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
3652c393a42Smrg{
3662c393a42Smrg    while (*s1)
3672c393a42Smrg    {
3682c393a42Smrg	if (FcStrIsAtIgnoreBlanksAndCase (s1, s2))
3692c393a42Smrg	    return s1;
3702c393a42Smrg	s1++;
3712c393a42Smrg    }
3722c393a42Smrg    return 0;
3732c393a42Smrg}
3742c393a42Smrg
3752c393a42Smrgstatic FcBool
3762c393a42SmrgFcCharIsPunct (const FcChar8 c)
3772c393a42Smrg{
3782c393a42Smrg    if (c < '0')
3792c393a42Smrg	return FcTrue;
3802c393a42Smrg    if (c <= '9')
3812c393a42Smrg	return FcFalse;
3822c393a42Smrg    if (c < 'A')
3832c393a42Smrg	return FcTrue;
3842c393a42Smrg    if (c <= 'Z')
3852c393a42Smrg	return FcFalse;
3862c393a42Smrg    if (c < 'a')
3872c393a42Smrg	return FcTrue;
3882c393a42Smrg    if (c <= 'z')
3892c393a42Smrg	return FcFalse;
3902c393a42Smrg    if (c <= '~')
3912c393a42Smrg	return FcTrue;
3922c393a42Smrg    return FcFalse;
3932c393a42Smrg}
3942c393a42Smrg
3952c393a42Smrg/*
3962c393a42Smrg * Is the head of s1 equal to s2?
3972c393a42Smrg */
3982c393a42Smrg
3992c393a42Smrgstatic FcBool
4002c393a42SmrgFcStrIsAtIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
4012c393a42Smrg{
4022c393a42Smrg    FcCaseWalker    w1, w2;
4032c393a42Smrg    FcChar8	    c1, c2;
4042c393a42Smrg
4052c393a42Smrg    FcStrCaseWalkerInit (s1, &w1);
4062c393a42Smrg    FcStrCaseWalkerInit (s2, &w2);
407ca08ab68Smrg
408ca08ab68Smrg    for (;;)
4092c393a42Smrg    {
410a4e54154Smrg	c1 = FcStrCaseWalkerNext (&w1);
411a4e54154Smrg	c2 = FcStrCaseWalkerNext (&w2);
4122c393a42Smrg	if (!c1 || (c1 != c2))
4132c393a42Smrg	    break;
4142c393a42Smrg    }
4152c393a42Smrg    return c1 == c2 || !c2;
4162c393a42Smrg}
4172c393a42Smrg
4182c393a42Smrg/*
4192c393a42Smrg * Does s1 contain an instance of s2 (ignoring blanks and case)?
4202c393a42Smrg */
4212c393a42Smrg
4222c393a42Smrgconst FcChar8 *
4232c393a42SmrgFcStrContainsIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
4242c393a42Smrg{
4252c393a42Smrg    while (*s1)
4262c393a42Smrg    {
4272c393a42Smrg	if (FcStrIsAtIgnoreCase (s1, s2))
4282c393a42Smrg	    return s1;
4292c393a42Smrg	s1++;
4302c393a42Smrg    }
4312c393a42Smrg    return 0;
4322c393a42Smrg}
4332c393a42Smrg
4342c393a42Smrg/*
4352c393a42Smrg * Does s1 contain an instance of s2 on a word boundary (ignoring case)?
4362c393a42Smrg */
4372c393a42Smrg
4382c393a42Smrgconst FcChar8 *
4392c393a42SmrgFcStrContainsWord (const FcChar8 *s1, const FcChar8 *s2)
4402c393a42Smrg{
4412c393a42Smrg    FcBool  wordStart = FcTrue;
4422c393a42Smrg    int	    s1len = strlen ((char *) s1);
4432c393a42Smrg    int	    s2len = strlen ((char *) s2);
4442c393a42Smrg
4452c393a42Smrg    while (s1len >= s2len)
4462c393a42Smrg    {
447ca08ab68Smrg	if (wordStart &&
4482c393a42Smrg	    FcStrIsAtIgnoreCase (s1, s2) &&
4492c393a42Smrg	    (s1len == s2len || FcCharIsPunct (s1[s2len])))
4502c393a42Smrg	{
4512c393a42Smrg	    return s1;
4522c393a42Smrg	}
4532c393a42Smrg	wordStart = FcFalse;
4542c393a42Smrg	if (FcCharIsPunct (*s1))
4552c393a42Smrg	    wordStart = FcTrue;
4562c393a42Smrg	s1++;
4572c393a42Smrg	s1len--;
4582c393a42Smrg    }
4592c393a42Smrg    return 0;
4602c393a42Smrg}
4612c393a42Smrg
462c9710b42Smrg/*
463a4e54154Smrg * returns the number of strings (ignoring delimiters and case) being matched
464c9710b42Smrg */
465c9710b42Smrg
466c9710b42Smrgint
467c9710b42SmrgFcStrMatchIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *delims)
468c9710b42Smrg{
469c9710b42Smrg    FcCaseWalker    w1, w2;
470c9710b42Smrg    FcChar8	    c1, c2;
471c9710b42Smrg
472c9710b42Smrg    if (s1 == s2) return 0;
473c9710b42Smrg
474c9710b42Smrg    FcStrCaseWalkerInit (s1, &w1);
475c9710b42Smrg    FcStrCaseWalkerInit (s2, &w2);
476c9710b42Smrg
477c9710b42Smrg    for (;;)
478c9710b42Smrg    {
479a4e54154Smrg	c1 = FcStrCaseWalkerNextNonDelim (&w1, (const char *)delims);
480a4e54154Smrg	c2 = FcStrCaseWalkerNextNonDelim (&w2, (const char *)delims);
481c9710b42Smrg	if (!c1 || (c1 != c2))
482c9710b42Smrg	    break;
483c9710b42Smrg    }
484c9710b42Smrg    return w1.src - s1 - 1;
485c9710b42Smrg}
486c9710b42Smrg
487c9710b42SmrgFcBool
488c9710b42SmrgFcStrGlobMatch (const FcChar8 *glob,
489c9710b42Smrg		const FcChar8 *string)
490c9710b42Smrg{
491c9710b42Smrg    FcChar8	c;
492c9710b42Smrg
493c9710b42Smrg    while ((c = *glob++))
494c9710b42Smrg    {
495c9710b42Smrg	switch (c) {
496c9710b42Smrg	case '*':
497c9710b42Smrg	    /* short circuit common case */
498c9710b42Smrg	    if (!*glob)
499c9710b42Smrg		return FcTrue;
500c9710b42Smrg	    /* short circuit another common case */
501c9710b42Smrg	    if (strchr ((char *) glob, '*') == 0)
502c9710b42Smrg	    {
503c9710b42Smrg		size_t l1, l2;
504c9710b42Smrg
505c9710b42Smrg		l1 = strlen ((char *) string);
506c9710b42Smrg		l2 = strlen ((char *) glob);
507c9710b42Smrg		if (l1 < l2)
508c9710b42Smrg		    return FcFalse;
509c9710b42Smrg		string += (l1 - l2);
510c9710b42Smrg	    }
511c9710b42Smrg	    while (*string)
512c9710b42Smrg	    {
513c9710b42Smrg		if (FcStrGlobMatch (glob, string))
514c9710b42Smrg		    return FcTrue;
515c9710b42Smrg		string++;
516c9710b42Smrg	    }
517c9710b42Smrg	    return FcFalse;
518c9710b42Smrg	case '?':
519c9710b42Smrg	    if (*string++ == '\0')
520c9710b42Smrg		return FcFalse;
521c9710b42Smrg	    break;
522c9710b42Smrg	default:
523c9710b42Smrg	    if (*string++ != c)
524c9710b42Smrg		return FcFalse;
525c9710b42Smrg	    break;
526c9710b42Smrg	}
527c9710b42Smrg    }
528c9710b42Smrg    return *string == '\0';
529c9710b42Smrg}
530c9710b42Smrg
5312c393a42Smrgconst FcChar8 *
5322c393a42SmrgFcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
5332c393a42Smrg{
5342c393a42Smrg    FcCaseWalker    w1, w2;
5352c393a42Smrg    FcChar8	    c1, c2;
5362c393a42Smrg    const FcChar8   *cur;
5372c393a42Smrg
5382c393a42Smrg    if (!s1 || !s2)
5392c393a42Smrg	return 0;
5402c393a42Smrg
5412c393a42Smrg    if (s1 == s2)
5422c393a42Smrg	return s1;
543ca08ab68Smrg
5442c393a42Smrg    FcStrCaseWalkerInit (s1, &w1);
5452c393a42Smrg    FcStrCaseWalkerInit (s2, &w2);
546ca08ab68Smrg
547a4e54154Smrg    c2 = FcStrCaseWalkerNext (&w2);
548ca08ab68Smrg
5492c393a42Smrg    for (;;)
5502c393a42Smrg    {
5512c393a42Smrg	cur = w1.src;
552a4e54154Smrg	c1 = FcStrCaseWalkerNext (&w1);
5532c393a42Smrg	if (!c1)
5542c393a42Smrg	    break;
5552c393a42Smrg	if (c1 == c2)
5562c393a42Smrg	{
5572c393a42Smrg	    FcCaseWalker    w1t = w1;
5582c393a42Smrg	    FcCaseWalker    w2t = w2;
5592c393a42Smrg	    FcChar8	    c1t, c2t;
5602c393a42Smrg
5612c393a42Smrg	    for (;;)
5622c393a42Smrg	    {
563a4e54154Smrg		c1t = FcStrCaseWalkerNext (&w1t);
564a4e54154Smrg		c2t = FcStrCaseWalkerNext (&w2t);
5652c393a42Smrg
5662c393a42Smrg		if (!c2t)
5672c393a42Smrg		    return cur;
5682c393a42Smrg		if (c2t != c1t)
5692c393a42Smrg		    break;
5702c393a42Smrg	    }
5712c393a42Smrg	}
5722c393a42Smrg    }
5732c393a42Smrg    return 0;
5742c393a42Smrg}
5752c393a42Smrg
5762c393a42Smrgconst FcChar8 *
5772c393a42SmrgFcStrStr (const FcChar8 *s1, const FcChar8 *s2)
5782c393a42Smrg{
5792c393a42Smrg    FcChar8 c1, c2;
5802c393a42Smrg    const FcChar8 * p = s1;
5812c393a42Smrg    const FcChar8 * b = s2;
5822c393a42Smrg
5832c393a42Smrg    if (!s1 || !s2)
5842c393a42Smrg	return 0;
5852c393a42Smrg
5862c393a42Smrg    if (s1 == s2)
5872c393a42Smrg	return s1;
5882c393a42Smrg
5892c393a42Smrgagain:
5902c393a42Smrg    c2 = *s2++;
5912c393a42Smrg
5922c393a42Smrg    if (!c2)
5932c393a42Smrg	return 0;
5942c393a42Smrg
595ca08ab68Smrg    for (;;)
5962c393a42Smrg    {
5972c393a42Smrg	p = s1;
5982c393a42Smrg	c1 = *s1++;
5992c393a42Smrg	if (!c1 || c1 == c2)
6002c393a42Smrg	    break;
6012c393a42Smrg    }
6022c393a42Smrg
6032c393a42Smrg    if (c1 != c2)
6042c393a42Smrg	return 0;
6052c393a42Smrg
6062c393a42Smrg    for (;;)
6072c393a42Smrg    {
6082c393a42Smrg	c1 = *s1;
6092c393a42Smrg	c2 = *s2;
6102c393a42Smrg	if (c1 && c2 && c1 != c2)
6112c393a42Smrg	{
6122c393a42Smrg	    s1 = p + 1;
6132c393a42Smrg	    s2 = b;
6142c393a42Smrg	    goto again;
6152c393a42Smrg	}
6162c393a42Smrg	if (!c2)
6172c393a42Smrg	    return p;
6182c393a42Smrg	if (!c1)
6192c393a42Smrg	    return 0;
6202c393a42Smrg	++ s1;
6212c393a42Smrg	++ s2;
6222c393a42Smrg    }
6232c393a42Smrg    /* never reached. */
6242c393a42Smrg}
6252c393a42Smrg
6262c393a42Smrgint
6272c393a42SmrgFcUtf8ToUcs4 (const FcChar8 *src_orig,
6282c393a42Smrg	      FcChar32	    *dst,
6292c393a42Smrg	      int	    len)
6302c393a42Smrg{
6312c393a42Smrg    const FcChar8   *src = src_orig;
6322c393a42Smrg    FcChar8	    s;
6332c393a42Smrg    int		    extra;
6342c393a42Smrg    FcChar32	    result;
6352c393a42Smrg
6362c393a42Smrg    if (len == 0)
6372c393a42Smrg	return 0;
638ca08ab68Smrg
6392c393a42Smrg    s = *src++;
6402c393a42Smrg    len--;
641ca08ab68Smrg
6422c393a42Smrg    if (!(s & 0x80))
6432c393a42Smrg    {
6442c393a42Smrg	result = s;
6452c393a42Smrg	extra = 0;
646ca08ab68Smrg    }
6472c393a42Smrg    else if (!(s & 0x40))
6482c393a42Smrg    {
6492c393a42Smrg	return -1;
6502c393a42Smrg    }
6512c393a42Smrg    else if (!(s & 0x20))
6522c393a42Smrg    {
6532c393a42Smrg	result = s & 0x1f;
6542c393a42Smrg	extra = 1;
6552c393a42Smrg    }
6562c393a42Smrg    else if (!(s & 0x10))
6572c393a42Smrg    {
6582c393a42Smrg	result = s & 0xf;
6592c393a42Smrg	extra = 2;
6602c393a42Smrg    }
6612c393a42Smrg    else if (!(s & 0x08))
6622c393a42Smrg    {
6632c393a42Smrg	result = s & 0x07;
6642c393a42Smrg	extra = 3;
6652c393a42Smrg    }
6662c393a42Smrg    else if (!(s & 0x04))
6672c393a42Smrg    {
6682c393a42Smrg	result = s & 0x03;
6692c393a42Smrg	extra = 4;
6702c393a42Smrg    }
6712c393a42Smrg    else if ( ! (s & 0x02))
6722c393a42Smrg    {
6732c393a42Smrg	result = s & 0x01;
6742c393a42Smrg	extra = 5;
6752c393a42Smrg    }
6762c393a42Smrg    else
6772c393a42Smrg    {
6782c393a42Smrg	return -1;
6792c393a42Smrg    }
6802c393a42Smrg    if (extra > len)
6812c393a42Smrg	return -1;
682ca08ab68Smrg
6832c393a42Smrg    while (extra--)
6842c393a42Smrg    {
6852c393a42Smrg	result <<= 6;
6862c393a42Smrg	s = *src++;
6872c393a42Smrg
6882c393a42Smrg	if ((s & 0xc0) != 0x80)
6892c393a42Smrg	    return -1;
6902c393a42Smrg
6912c393a42Smrg	result |= s & 0x3f;
6922c393a42Smrg    }
6932c393a42Smrg    *dst = result;
6942c393a42Smrg    return src - src_orig;
6952c393a42Smrg}
6962c393a42Smrg
6972c393a42SmrgFcBool
6982c393a42SmrgFcUtf8Len (const FcChar8    *string,
6992c393a42Smrg	   int		    len,
7002c393a42Smrg	   int		    *nchar,
7012c393a42Smrg	   int		    *wchar)
7022c393a42Smrg{
7032c393a42Smrg    int		n;
7042c393a42Smrg    int		clen;
7052c393a42Smrg    FcChar32	c;
7062c393a42Smrg    FcChar32	max;
707ca08ab68Smrg
7082c393a42Smrg    n = 0;
7092c393a42Smrg    max = 0;
7102c393a42Smrg    while (len)
7112c393a42Smrg    {
7122c393a42Smrg	clen = FcUtf8ToUcs4 (string, &c, len);
7132c393a42Smrg	if (clen <= 0)	/* malformed UTF8 string */
7142c393a42Smrg	    return FcFalse;
7152c393a42Smrg	if (c > max)
7162c393a42Smrg	    max = c;
7172c393a42Smrg	string += clen;
7182c393a42Smrg	len -= clen;
7192c393a42Smrg	n++;
7202c393a42Smrg    }
7212c393a42Smrg    *nchar = n;
7222c393a42Smrg    if (max >= 0x10000)
7232c393a42Smrg	*wchar = 4;
7242c393a42Smrg    else if (max > 0x100)
7252c393a42Smrg	*wchar = 2;
7262c393a42Smrg    else
7272c393a42Smrg	*wchar = 1;
7282c393a42Smrg    return FcTrue;
7292c393a42Smrg}
7302c393a42Smrg
7312c393a42Smrgint
7322c393a42SmrgFcUcs4ToUtf8 (FcChar32	ucs4,
7332c393a42Smrg	      FcChar8	dest[FC_UTF8_MAX_LEN])
7342c393a42Smrg{
7352c393a42Smrg    int	bits;
7362c393a42Smrg    FcChar8 *d = dest;
737ca08ab68Smrg
7382c393a42Smrg    if      (ucs4 <       0x80) {  *d++=  ucs4;                         bits= -6; }
7392c393a42Smrg    else if (ucs4 <      0x800) {  *d++= ((ucs4 >>  6) & 0x1F) | 0xC0;  bits=  0; }
7402c393a42Smrg    else if (ucs4 <    0x10000) {  *d++= ((ucs4 >> 12) & 0x0F) | 0xE0;  bits=  6; }
7412c393a42Smrg    else if (ucs4 <   0x200000) {  *d++= ((ucs4 >> 18) & 0x07) | 0xF0;  bits= 12; }
7422c393a42Smrg    else if (ucs4 <  0x4000000) {  *d++= ((ucs4 >> 24) & 0x03) | 0xF8;  bits= 18; }
7432c393a42Smrg    else if (ucs4 < 0x80000000) {  *d++= ((ucs4 >> 30) & 0x01) | 0xFC;  bits= 24; }
7442c393a42Smrg    else return 0;
7452c393a42Smrg
7462c393a42Smrg    for ( ; bits >= 0; bits-= 6) {
7472c393a42Smrg	*d++= ((ucs4 >> bits) & 0x3F) | 0x80;
7482c393a42Smrg    }
7492c393a42Smrg    return d - dest;
7502c393a42Smrg}
7512c393a42Smrg
7522c393a42Smrg#define GetUtf16(src,endian) \
7532c393a42Smrg    ((FcChar16) ((src)[endian == FcEndianBig ? 0 : 1] << 8) | \
7542c393a42Smrg     (FcChar16) ((src)[endian == FcEndianBig ? 1 : 0]))
7552c393a42Smrg
7562c393a42Smrgint
7572c393a42SmrgFcUtf16ToUcs4 (const FcChar8	*src_orig,
7582c393a42Smrg	       FcEndian		endian,
7592c393a42Smrg	       FcChar32		*dst,
7602c393a42Smrg	       int		len)	/* in bytes */
7612c393a42Smrg{
7622c393a42Smrg    const FcChar8   *src = src_orig;
7632c393a42Smrg    FcChar16	    a, b;
7642c393a42Smrg    FcChar32	    result;
7652c393a42Smrg
7662c393a42Smrg    if (len < 2)
7672c393a42Smrg	return 0;
768ca08ab68Smrg
7692c393a42Smrg    a = GetUtf16 (src, endian); src += 2; len -= 2;
770ca08ab68Smrg
771ca08ab68Smrg    /*
772ca08ab68Smrg     * Check for surrogate
7732c393a42Smrg     */
7742c393a42Smrg    if ((a & 0xfc00) == 0xd800)
7752c393a42Smrg    {
7762c393a42Smrg	if (len < 2)
7772c393a42Smrg	    return 0;
7782c393a42Smrg	b = GetUtf16 (src, endian); src += 2; len -= 2;
7792c393a42Smrg	/*
7802c393a42Smrg	 * Check for invalid surrogate sequence
7812c393a42Smrg	 */
7822c393a42Smrg	if ((b & 0xfc00) != 0xdc00)
7832c393a42Smrg	    return 0;
7842c393a42Smrg	result = ((((FcChar32) a & 0x3ff) << 10) |
7852c393a42Smrg		  ((FcChar32) b & 0x3ff)) + 0x10000;
7862c393a42Smrg    }
7872c393a42Smrg    else
7882c393a42Smrg	result = a;
7892c393a42Smrg    *dst = result;
7902c393a42Smrg    return src - src_orig;
7912c393a42Smrg}
7922c393a42Smrg
7932c393a42SmrgFcBool
7942c393a42SmrgFcUtf16Len (const FcChar8   *string,
7952c393a42Smrg	    FcEndian	    endian,
7962c393a42Smrg	    int		    len,	/* in bytes */
7972c393a42Smrg	    int		    *nchar,
7982c393a42Smrg	    int		    *wchar)
7992c393a42Smrg{
8002c393a42Smrg    int		n;
8012c393a42Smrg    int		clen;
8022c393a42Smrg    FcChar32	c;
8032c393a42Smrg    FcChar32	max;
804ca08ab68Smrg
8052c393a42Smrg    n = 0;
8062c393a42Smrg    max = 0;
8072c393a42Smrg    while (len)
8082c393a42Smrg    {
8092c393a42Smrg	clen = FcUtf16ToUcs4 (string, endian, &c, len);
8102c393a42Smrg	if (clen <= 0)	/* malformed UTF8 string */
8112c393a42Smrg	    return FcFalse;
8122c393a42Smrg	if (c > max)
8132c393a42Smrg	    max = c;
8142c393a42Smrg	string += clen;
8152c393a42Smrg	len -= clen;
8162c393a42Smrg	n++;
8172c393a42Smrg    }
8182c393a42Smrg    *nchar = n;
8192c393a42Smrg    if (max >= 0x10000)
8202c393a42Smrg	*wchar = 4;
8212c393a42Smrg    else if (max > 0x100)
8222c393a42Smrg	*wchar = 2;
8232c393a42Smrg    else
8242c393a42Smrg	*wchar = 1;
8252c393a42Smrg    return FcTrue;
8262c393a42Smrg}
8272c393a42Smrg
8282c393a42Smrgvoid
8292c393a42SmrgFcStrBufInit (FcStrBuf *buf, FcChar8 *init, int size)
8302c393a42Smrg{
831a6844aabSmrg    if (init)
832a6844aabSmrg    {
833a6844aabSmrg	buf->buf = init;
834a6844aabSmrg	buf->size = size;
835a6844aabSmrg    } else
836a6844aabSmrg    {
837a6844aabSmrg	buf->buf = buf->buf_static;
838a6844aabSmrg	buf->size = sizeof (buf->buf_static);
839a6844aabSmrg    }
8402c393a42Smrg    buf->allocated = FcFalse;
8412c393a42Smrg    buf->failed = FcFalse;
8422c393a42Smrg    buf->len = 0;
8432c393a42Smrg}
8442c393a42Smrg
8452c393a42Smrgvoid
8462c393a42SmrgFcStrBufDestroy (FcStrBuf *buf)
8472c393a42Smrg{
8482c393a42Smrg    if (buf->allocated)
8492c393a42Smrg    {
8502c393a42Smrg	free (buf->buf);
8512c393a42Smrg	FcStrBufInit (buf, 0, 0);
8522c393a42Smrg    }
8532c393a42Smrg}
8542c393a42Smrg
8552c393a42SmrgFcChar8 *
8562c393a42SmrgFcStrBufDone (FcStrBuf *buf)
8572c393a42Smrg{
8582c393a42Smrg    FcChar8 *ret;
8592c393a42Smrg
860a6844aabSmrg    if (buf->failed)
861a6844aabSmrg	ret = NULL;
862a6844aabSmrg    else
863a6844aabSmrg	ret = malloc (buf->len + 1);
8642c393a42Smrg    if (ret)
8652c393a42Smrg    {
8662c393a42Smrg	memcpy (ret, buf->buf, buf->len);
8672c393a42Smrg	ret[buf->len] = '\0';
8682c393a42Smrg    }
8692c393a42Smrg    FcStrBufDestroy (buf);
8702c393a42Smrg    return ret;
8712c393a42Smrg}
8722c393a42Smrg
873a6844aabSmrgFcChar8 *
874a6844aabSmrgFcStrBufDoneStatic (FcStrBuf *buf)
875a6844aabSmrg{
876a6844aabSmrg    FcStrBufChar (buf, '\0');
877a6844aabSmrg
878a6844aabSmrg    if (buf->failed)
879a6844aabSmrg	return NULL;
880a6844aabSmrg
881a6844aabSmrg    return buf->buf;
882a6844aabSmrg}
883a6844aabSmrg
8842c393a42SmrgFcBool
8852c393a42SmrgFcStrBufChar (FcStrBuf *buf, FcChar8 c)
8862c393a42Smrg{
8872c393a42Smrg    if (buf->len == buf->size)
8882c393a42Smrg    {
8892c393a42Smrg	FcChar8	    *new;
8902c393a42Smrg	int	    size;
8912c393a42Smrg
892a6844aabSmrg	if (buf->failed)
893a6844aabSmrg	    return FcFalse;
894a6844aabSmrg
8952c393a42Smrg	if (buf->allocated)
8962c393a42Smrg	{
8972c393a42Smrg	    size = buf->size * 2;
8982c393a42Smrg	    new = realloc (buf->buf, size);
8992c393a42Smrg	}
9002c393a42Smrg	else
9012c393a42Smrg	{
9022c393a42Smrg	    size = buf->size + 64;
9032c393a42Smrg	    new = malloc (size);
9042c393a42Smrg	    if (new)
9052c393a42Smrg	    {
9062c393a42Smrg		buf->allocated = FcTrue;
9072c393a42Smrg		memcpy (new, buf->buf, buf->len);
9082c393a42Smrg	    }
9092c393a42Smrg	}
9102c393a42Smrg	if (!new)
9112c393a42Smrg	{
9122c393a42Smrg	    buf->failed = FcTrue;
9132c393a42Smrg	    return FcFalse;
9142c393a42Smrg	}
9152c393a42Smrg	buf->size = size;
9162c393a42Smrg	buf->buf = new;
9172c393a42Smrg    }
9182c393a42Smrg    buf->buf[buf->len++] = c;
9192c393a42Smrg    return FcTrue;
9202c393a42Smrg}
9212c393a42Smrg
9222c393a42SmrgFcBool
9232c393a42SmrgFcStrBufString (FcStrBuf *buf, const FcChar8 *s)
9242c393a42Smrg{
9252c393a42Smrg    FcChar8 c;
9262c393a42Smrg    while ((c = *s++))
9272c393a42Smrg	if (!FcStrBufChar (buf, c))
9282c393a42Smrg	    return FcFalse;
9292c393a42Smrg    return FcTrue;
9302c393a42Smrg}
9312c393a42Smrg
9322c393a42SmrgFcBool
9332c393a42SmrgFcStrBufData (FcStrBuf *buf, const FcChar8 *s, int len)
9342c393a42Smrg{
9352c393a42Smrg    while (len-- > 0)
9362c393a42Smrg	if (!FcStrBufChar (buf, *s++))
9372c393a42Smrg	    return FcFalse;
9382c393a42Smrg    return FcTrue;
9392c393a42Smrg}
9402c393a42Smrg
9412c393a42SmrgFcBool
9422c393a42SmrgFcStrUsesHome (const FcChar8 *s)
9432c393a42Smrg{
9442c393a42Smrg    return *s == '~';
9452c393a42Smrg}
9462c393a42Smrg
947a32e9e42SmrgFcBool
948a32e9e42SmrgFcStrIsAbsoluteFilename (const FcChar8 *s)
949a32e9e42Smrg{
950a32e9e42Smrg#ifdef _WIN32
951a32e9e42Smrg    if (*s == '\\' ||
952a32e9e42Smrg	(isalpha (*s) && s[1] == ':' && (s[2] == '/' || s[2] == '\\')))
953a32e9e42Smrg	return FcTrue;
954a32e9e42Smrg#endif
955a32e9e42Smrg    return *s == '/';
956a32e9e42Smrg}
957a32e9e42Smrg
958c9710b42SmrgFcChar8 *
959c9710b42SmrgFcStrBuildFilename (const FcChar8 *path,
960c9710b42Smrg		    ...)
961c9710b42Smrg{
962c9710b42Smrg    va_list ap;
963b09479dcSmrg    FcStrSet *sset;
964c9710b42Smrg    FcStrList *list;
965c9710b42Smrg    FcChar8 *s, *ret = NULL, *p;
966c9710b42Smrg    size_t len = 0;
967c9710b42Smrg
968b09479dcSmrg    if (!path)
969b09479dcSmrg	return NULL;
970b09479dcSmrg
971953daebaSmrg    sset = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
972b09479dcSmrg    if (!sset)
973c9710b42Smrg	return NULL;
974c9710b42Smrg
975c9710b42Smrg    if (!FcStrSetAdd (sset, path))
976c9710b42Smrg	goto bail0;
977c9710b42Smrg
978c9710b42Smrg    va_start (ap, path);
979c9710b42Smrg    while (1)
980c9710b42Smrg    {
981c9710b42Smrg	s = (FcChar8 *)va_arg (ap, FcChar8 *);
982c9710b42Smrg	if (!s)
983c9710b42Smrg	    break;
984c9710b42Smrg	if (!FcStrSetAdd (sset, s))
985c9710b42Smrg	    goto bail1;
986c9710b42Smrg    }
987c9710b42Smrg    list = FcStrListCreate (sset);
988c9710b42Smrg    while ((s = FcStrListNext (list)))
989c9710b42Smrg    {
990c9710b42Smrg	len += strlen ((const char *)s) + 1;
991c9710b42Smrg    }
992c9710b42Smrg    list->n = 0;
993c9710b42Smrg    ret = malloc (sizeof (FcChar8) * (len + 1));
994c9710b42Smrg    if (!ret)
995c9710b42Smrg	goto bail2;
996c9710b42Smrg    p = ret;
997c9710b42Smrg    while ((s = FcStrListNext (list)))
998c9710b42Smrg    {
999c9710b42Smrg	if (p != ret)
1000c9710b42Smrg	{
1001c9710b42Smrg	    p[0] = FC_DIR_SEPARATOR;
1002c9710b42Smrg	    p++;
1003c9710b42Smrg	}
1004c9710b42Smrg	len = strlen ((const char *)s);
1005c9710b42Smrg	memcpy (p, s, len);
1006c9710b42Smrg	p += len;
1007c9710b42Smrg    }
1008c9710b42Smrg    *p = 0;
1009c9710b42Smrg
1010c9710b42Smrgbail2:
1011c9710b42Smrg    FcStrListDone (list);
1012c9710b42Smrgbail1:
1013c9710b42Smrg    va_end (ap);
1014c9710b42Smrgbail0:
1015c9710b42Smrg    FcStrSetDestroy (sset);
1016c9710b42Smrg
1017c9710b42Smrg    return ret;
1018c9710b42Smrg}
1019c9710b42Smrg
10202c393a42SmrgFcChar8 *
10212c393a42SmrgFcStrCopyFilename (const FcChar8 *s)
10222c393a42Smrg{
10232c393a42Smrg    FcChar8 *new;
1024ca08ab68Smrg
10252c393a42Smrg    if (*s == '~')
10262c393a42Smrg    {
10272c393a42Smrg	FcChar8	*home = FcConfigHome ();
10282c393a42Smrg	FcChar8	*full;
10292c393a42Smrg	int	size;
10302c393a42Smrg	if (!home)
1031ca08ab68Smrg	    return NULL;
10322c393a42Smrg	size = strlen ((char *) home) + strlen ((char *) s);
1033a32e9e42Smrg	full = (FcChar8 *) malloc (size + 1);
10342c393a42Smrg	if (!full)
1035ca08ab68Smrg	    return NULL;
10362c393a42Smrg	strcpy ((char *) full, (char *) home);
10372c393a42Smrg	strcat ((char *) full, (char *) s + 1);
10382c393a42Smrg	new = FcStrCanonFilename (full);
10392c393a42Smrg	free (full);
10402c393a42Smrg    }
10412c393a42Smrg    else
10422c393a42Smrg	new = FcStrCanonFilename (s);
1043ca08ab68Smrg
10442c393a42Smrg    return new;
10452c393a42Smrg}
10462c393a42Smrg
10472c393a42SmrgFcChar8 *
10482c393a42SmrgFcStrLastSlash (const FcChar8  *path)
10492c393a42Smrg{
10502c393a42Smrg    FcChar8	    *slash;
10512c393a42Smrg
10522c393a42Smrg    slash = (FcChar8 *) strrchr ((const char *) path, '/');
10532c393a42Smrg#ifdef _WIN32
10542c393a42Smrg    {
10552c393a42Smrg        FcChar8     *backslash;
10562c393a42Smrg
10572c393a42Smrg	backslash = (FcChar8 *) strrchr ((const char *) path, '\\');
10582c393a42Smrg	if (!slash || (backslash && backslash > slash))
10592c393a42Smrg	    slash = backslash;
10602c393a42Smrg    }
10612c393a42Smrg#endif
10622c393a42Smrg
10632c393a42Smrg    return slash;
10642c393a42Smrg}
1065ca08ab68Smrg
10662c393a42SmrgFcChar8 *
10672c393a42SmrgFcStrDirname (const FcChar8 *file)
10682c393a42Smrg{
10692c393a42Smrg    FcChar8 *slash;
10702c393a42Smrg    FcChar8 *dir;
10712c393a42Smrg
10722c393a42Smrg    slash = FcStrLastSlash (file);
10732c393a42Smrg    if (!slash)
10742c393a42Smrg	return FcStrCopy ((FcChar8 *) ".");
10752c393a42Smrg    dir = malloc ((slash - file) + 1);
10762c393a42Smrg    if (!dir)
10772c393a42Smrg	return 0;
10782c393a42Smrg    strncpy ((char *) dir, (const char *) file, slash - file);
10792c393a42Smrg    dir[slash - file] = '\0';
10802c393a42Smrg    return dir;
10812c393a42Smrg}
10822c393a42Smrg
10832c393a42SmrgFcChar8 *
10842c393a42SmrgFcStrBasename (const FcChar8 *file)
10852c393a42Smrg{
10862c393a42Smrg    FcChar8 *slash;
10872c393a42Smrg
10882c393a42Smrg    slash = FcStrLastSlash (file);
10892c393a42Smrg    if (!slash)
10902c393a42Smrg	return FcStrCopy (file);
10912c393a42Smrg    return FcStrCopy (slash + 1);
10922c393a42Smrg}
10932c393a42Smrg
1094a4e54154SmrgFcChar8 *
1095a4e54154SmrgFcStrRealPath (const FcChar8 *path)
1096a4e54154Smrg{
1097a4e54154Smrg    char	resolved_name[FC_PATH_MAX+1];
1098a4e54154Smrg    char	*resolved_ret;
1099a4e54154Smrg
1100a4e54154Smrg    if (!path)
1101a4e54154Smrg	return NULL;
1102a4e54154Smrg
1103a4e54154Smrg#ifndef _WIN32
1104a4e54154Smrg    resolved_ret = realpath((const char *) path, resolved_name);
1105a4e54154Smrg#else
1106a4e54154Smrg    if (GetFullPathNameA ((LPCSTR) path, FC_PATH_MAX, resolved_name, NULL) == 0)
1107a4e54154Smrg    {
1108a4e54154Smrg        fprintf (stderr, "Fontconfig warning: GetFullPathNameA failed.\n");
1109a4e54154Smrg        return NULL;
1110a4e54154Smrg    }
1111a4e54154Smrg    resolved_ret = resolved_name;
1112a4e54154Smrg#endif
1113a4e54154Smrg    if (resolved_ret)
1114a4e54154Smrg	path = (FcChar8 *) resolved_ret;
1115a4e54154Smrg    return FcStrCopyFilename(path);
1116a4e54154Smrg}
1117a4e54154Smrg
11182c393a42Smrgstatic FcChar8 *
11192c393a42SmrgFcStrCanonAbsoluteFilename (const FcChar8 *s)
11202c393a42Smrg{
11212c393a42Smrg    FcChar8 *file;
11222c393a42Smrg    FcChar8 *f;
11232c393a42Smrg    const FcChar8 *slash;
11242c393a42Smrg    int size;
11252c393a42Smrg
11262c393a42Smrg    size = strlen ((char *) s) + 1;
11272c393a42Smrg    file = malloc (size);
11282c393a42Smrg    if (!file)
11292c393a42Smrg	return NULL;
11302c393a42Smrg    slash = NULL;
11312c393a42Smrg    f = file;
1132ca08ab68Smrg#ifdef _WIN32
1133ca08ab68Smrg    if (*s == '/' && *(s+1) == '/') /* Network path, do not squash // */
1134ca08ab68Smrg	*f++ = *s++;
1135ca08ab68Smrg#endif
11362c393a42Smrg    for (;;) {
11372c393a42Smrg	if (*s == '/' || *s == '\0')
11382c393a42Smrg	{
11392c393a42Smrg	    if (slash)
11402c393a42Smrg	    {
11412c393a42Smrg		switch (s - slash) {
1142a6844aabSmrg		case 1:
1143a6844aabSmrg		    f -= 1;	/* squash // and trim final / from file */
1144a6844aabSmrg		    break;
11452c393a42Smrg		case 2:
11462c393a42Smrg		    if (!strncmp ((char *) slash, "/.", 2))
11472c393a42Smrg		    {
11482c393a42Smrg			f -= 2;	/* trim /. from file */
11492c393a42Smrg		    }
11502c393a42Smrg		    break;
11512c393a42Smrg		case 3:
11522c393a42Smrg		    if (!strncmp ((char *) slash, "/..", 3))
11532c393a42Smrg		    {
11542c393a42Smrg			f -= 3;	/* trim /.. from file */
11552c393a42Smrg			while (f > file) {
11562c393a42Smrg			    if (*--f == '/')
11572c393a42Smrg				break;
11582c393a42Smrg			}
11592c393a42Smrg		    }
11602c393a42Smrg		    break;
11612c393a42Smrg		}
11622c393a42Smrg	    }
11632c393a42Smrg	    slash = s;
11642c393a42Smrg	}
11652c393a42Smrg	if (!(*f++ = *s++))
11662c393a42Smrg	    break;
11672c393a42Smrg    }
11682c393a42Smrg    return file;
11692c393a42Smrg}
1170ca08ab68Smrg
11712c393a42Smrg#ifdef _WIN32
11722c393a42Smrg/*
1173ca08ab68Smrg * Convert '\\' to '/' , remove double '/'
11742c393a42Smrg */
11752c393a42Smrgstatic void
11762c393a42SmrgFcConvertDosPath (char *str)
11772c393a42Smrg{
11782c393a42Smrg  size_t len = strlen (str);
11792c393a42Smrg  char *p = str;
11802c393a42Smrg  char *dest = str;
11812c393a42Smrg  char *end = str + len;
11822c393a42Smrg  char last = 0;
1183a6844aabSmrg
1184a6844aabSmrg  if (*p == '\\')
1185a6844aabSmrg    {
1186a6844aabSmrg      *p = '/';
1187a6844aabSmrg      p++;
1188a6844aabSmrg      dest++;
1189a6844aabSmrg    }
11902c393a42Smrg  while (p < end)
11912c393a42Smrg    {
11922c393a42Smrg      if (*p == '\\')
11932c393a42Smrg	*p = '/';
11942c393a42Smrg
11952c393a42Smrg      if (*p != '/'
11962c393a42Smrg	  || last != '/')
11972c393a42Smrg	{
11982c393a42Smrg	  *dest++ = *p;
11992c393a42Smrg	}
12002c393a42Smrg
12012c393a42Smrg      last = *p;
12022c393a42Smrg      p++;
12032c393a42Smrg    }
12042c393a42Smrg
12052c393a42Smrg  *dest = 0;
12062c393a42Smrg}
12072c393a42Smrg#endif
12082c393a42Smrg
12092c393a42SmrgFcChar8 *
12102c393a42SmrgFcStrCanonFilename (const FcChar8 *s)
12112c393a42Smrg{
12122c393a42Smrg#ifdef _WIN32
12132c393a42Smrg    FcChar8 full[FC_MAX_FILE_LEN + 2];
1214ca08ab68Smrg    int size = GetFullPathName ((LPCSTR) s, sizeof (full) -1,
1215ca08ab68Smrg				(LPSTR) full, NULL);
12162c393a42Smrg
12172c393a42Smrg    if (size == 0)
12182c393a42Smrg	perror ("GetFullPathName");
12192c393a42Smrg
1220ca08ab68Smrg    FcConvertDosPath ((char *) full);
12212c393a42Smrg    return FcStrCanonAbsoluteFilename (full);
12222c393a42Smrg#else
12232c393a42Smrg    if (s[0] == '/')
12242c393a42Smrg	return FcStrCanonAbsoluteFilename (s);
12252c393a42Smrg    else
12262c393a42Smrg    {
12272c393a42Smrg	FcChar8	*full;
12282c393a42Smrg	FcChar8 *file;
12292c393a42Smrg
12302c393a42Smrg	FcChar8	cwd[FC_MAX_FILE_LEN + 2];
12312c393a42Smrg	if (getcwd ((char *) cwd, FC_MAX_FILE_LEN) == NULL)
12322c393a42Smrg	    return NULL;
1233c9710b42Smrg	full = FcStrBuildFilename (cwd, s, NULL);
12342c393a42Smrg	file = FcStrCanonAbsoluteFilename (full);
12352c393a42Smrg	FcStrFree (full);
12362c393a42Smrg	return file;
12372c393a42Smrg    }
12382c393a42Smrg#endif
12392c393a42Smrg}
12402c393a42Smrg
12412c393a42Smrg
12422c393a42SmrgFcStrSet *
12432c393a42SmrgFcStrSetCreate (void)
1244953daebaSmrg{
1245953daebaSmrg    return FcStrSetCreateEx (FCSS_DEFAULT);
1246953daebaSmrg}
1247953daebaSmrg
1248953daebaSmrgFcStrSet *
1249953daebaSmrgFcStrSetCreateEx (unsigned int control)
12502c393a42Smrg{
12512c393a42Smrg    FcStrSet	*set = malloc (sizeof (FcStrSet));
12522c393a42Smrg    if (!set)
12532c393a42Smrg	return 0;
1254c9710b42Smrg    FcRefInit (&set->ref, 1);
12552c393a42Smrg    set->num = 0;
12562c393a42Smrg    set->size = 0;
12572c393a42Smrg    set->strs = 0;
1258953daebaSmrg    set->control = control;
12592c393a42Smrg    return set;
12602c393a42Smrg}
12612c393a42Smrg
1262953daebaSmrgstatic FcBool
1263953daebaSmrg_FcStrSetGrow (FcStrSet *set, int growElements)
1264953daebaSmrg{
1265953daebaSmrg    /* accommodate an additional NULL entry at the end of the array */
1266953daebaSmrg    FcChar8 **strs = malloc ((set->size + growElements + 1) * sizeof (FcChar8 *));
1267953daebaSmrg    if (!strs)
1268953daebaSmrg        return FcFalse;
1269953daebaSmrg    if (set->num)
1270953daebaSmrg        memcpy (strs, set->strs, set->num * sizeof (FcChar8 *));
1271953daebaSmrg    if (set->strs)
1272953daebaSmrg        free (set->strs);
1273953daebaSmrg    set->size = set->size + growElements;
1274953daebaSmrg    set->strs = strs;
1275953daebaSmrg    return FcTrue;
1276953daebaSmrg}
1277953daebaSmrg
12782c393a42Smrgstatic FcBool
1279a4e54154Smrg_FcStrSetInsert (FcStrSet *set, FcChar8 *s, int pos)
12802c393a42Smrg{
1281953daebaSmrg    if (!FcStrSetHasControlBit (set, FCSS_ALLOW_DUPLICATES))
12822c393a42Smrg    {
1283953daebaSmrg        if (FcStrSetMember (set, s))
1284953daebaSmrg        {
1285953daebaSmrg            FcStrFree (s);
1286953daebaSmrg            return FcTrue;
1287953daebaSmrg        }
12882c393a42Smrg    }
12892c393a42Smrg    if (set->num == set->size)
12902c393a42Smrg    {
1291953daebaSmrg        int growElements = FcStrSetHasControlBit (set, FCSS_GROW_BY_64) ? 64 : 1;
1292953daebaSmrg        if (!_FcStrSetGrow(set, growElements))
1293953daebaSmrg            return FcFalse;
12942c393a42Smrg    }
1295a4e54154Smrg    if (pos >= set->num)
1296a4e54154Smrg    {
1297a4e54154Smrg	set->strs[set->num++] = s;
1298a4e54154Smrg	set->strs[set->num] = 0;
1299a4e54154Smrg    }
1300a4e54154Smrg    else
1301a4e54154Smrg    {
1302a4e54154Smrg	int i;
1303a4e54154Smrg
1304a4e54154Smrg	set->num++;
1305a4e54154Smrg	set->strs[set->num] = 0;
1306a4e54154Smrg	for (i = set->num - 1; i > pos; i--)
1307a4e54154Smrg	    set->strs[i] = set->strs[i - 1];
1308a4e54154Smrg	set->strs[pos] = s;
1309a4e54154Smrg    }
13102c393a42Smrg    return FcTrue;
13112c393a42Smrg}
13122c393a42Smrg
13132c393a42SmrgFcBool
13142c393a42SmrgFcStrSetMember (FcStrSet *set, const FcChar8 *s)
13152c393a42Smrg{
13162c393a42Smrg    int	i;
13172c393a42Smrg
13182c393a42Smrg    for (i = 0; i < set->num; i++)
13192c393a42Smrg	if (!FcStrCmp (set->strs[i], s))
13202c393a42Smrg	    return FcTrue;
13212c393a42Smrg    return FcFalse;
13222c393a42Smrg}
13232c393a42Smrg
1324a4e54154Smrgstatic int
1325a4e54154Smrgfc_strcmp_r (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 **ret)
1326a4e54154Smrg{
1327a4e54154Smrg    FcChar8 c1, c2;
1328a4e54154Smrg
1329a4e54154Smrg    if (s1 == s2)
1330a4e54154Smrg    {
1331a4e54154Smrg	if (ret)
1332a4e54154Smrg	    *ret = NULL;
1333a4e54154Smrg	return 0;
1334a4e54154Smrg    }
1335a4e54154Smrg    for (;;)
1336a4e54154Smrg    {
1337a4e54154Smrg	if (s1)
1338a4e54154Smrg	    c1 = *s1++;
1339a4e54154Smrg	else
1340a4e54154Smrg	    c1 = 0;
1341a4e54154Smrg	if (s2)
1342a4e54154Smrg	    c2 = *s2++;
1343a4e54154Smrg	else
1344a4e54154Smrg	    c2 = 0;
1345a4e54154Smrg	if (!c1 || c1 != c2)
1346a4e54154Smrg	    break;
1347a4e54154Smrg    }
1348a4e54154Smrg    if (ret)
1349a4e54154Smrg	*ret = s1;
1350a4e54154Smrg    return (int) c1  - (int) c2;
1351a4e54154Smrg}
1352a4e54154Smrg
1353a4e54154SmrgFcBool
1354a4e54154SmrgFcStrSetMemberAB (FcStrSet *set, const FcChar8 *a, FcChar8 *b, FcChar8 **ret)
1355a4e54154Smrg{
1356a4e54154Smrg    int i;
1357a4e54154Smrg    const FcChar8 *s = NULL;
1358a4e54154Smrg
1359a4e54154Smrg    for (i = 0; i < set->num; i++)
1360a4e54154Smrg    {
1361a4e54154Smrg	if (!fc_strcmp_r (set->strs[i], a, &s) && s)
1362a4e54154Smrg	{
1363a4e54154Smrg	    if (!fc_strcmp_r (s, b, NULL))
1364a4e54154Smrg	    {
1365a4e54154Smrg		if (ret)
1366a4e54154Smrg		    *ret = set->strs[i];
1367a4e54154Smrg		return FcTrue;
1368a4e54154Smrg	    }
1369a4e54154Smrg	}
1370a4e54154Smrg    }
1371a4e54154Smrg    if (ret)
1372a4e54154Smrg	*ret = NULL;
1373a4e54154Smrg    return FcFalse;
1374a4e54154Smrg}
1375a4e54154Smrg
13762c393a42SmrgFcBool
13772c393a42SmrgFcStrSetEqual (FcStrSet *sa, FcStrSet *sb)
13782c393a42Smrg{
13792c393a42Smrg    int	i;
13802c393a42Smrg    if (sa->num != sb->num)
13812c393a42Smrg	return FcFalse;
13822c393a42Smrg    for (i = 0; i < sa->num; i++)
13832c393a42Smrg	if (!FcStrSetMember (sb, sa->strs[i]))
13842c393a42Smrg	    return FcFalse;
13852c393a42Smrg    return FcTrue;
13862c393a42Smrg}
13872c393a42Smrg
13882c393a42SmrgFcBool
13892c393a42SmrgFcStrSetAdd (FcStrSet *set, const FcChar8 *s)
13902c393a42Smrg{
13912c393a42Smrg    FcChar8 *new = FcStrCopy (s);
13922c393a42Smrg    if (!new)
13932c393a42Smrg	return FcFalse;
1394a4e54154Smrg    if (!_FcStrSetInsert (set, new, set->num))
1395a4e54154Smrg    {
1396a4e54154Smrg	FcStrFree (new);
1397a4e54154Smrg	return FcFalse;
1398a4e54154Smrg    }
1399a4e54154Smrg    return FcTrue;
1400a4e54154Smrg}
1401a4e54154Smrg
1402a4e54154SmrgFcBool
1403a4e54154SmrgFcStrSetInsert (FcStrSet *set, const FcChar8 *s, int pos)
1404a4e54154Smrg{
1405a4e54154Smrg    FcChar8 *new = FcStrCopy (s);
1406a4e54154Smrg    if (!new)
1407a4e54154Smrg	return FcFalse;
1408a4e54154Smrg    if (!_FcStrSetInsert (set, new, pos))
14092c393a42Smrg    {
14102c393a42Smrg	FcStrFree (new);
14112c393a42Smrg	return FcFalse;
14122c393a42Smrg    }
14132c393a42Smrg    return FcTrue;
14142c393a42Smrg}
14152c393a42Smrg
1416a4e54154SmrgFcBool
1417a4e54154SmrgFcStrSetAddTriple (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *c)
1418a4e54154Smrg{
1419a4e54154Smrg    FcChar8 *new = FcStrMakeTriple (a, b, c);
1420a4e54154Smrg    if (!new)
1421a4e54154Smrg	return FcFalse;
1422a4e54154Smrg    if (!_FcStrSetInsert (set, new, set->num))
1423a4e54154Smrg    {
1424a4e54154Smrg	FcStrFree (new);
1425a4e54154Smrg	return FcFalse;
1426a4e54154Smrg    }
1427a4e54154Smrg    return FcTrue;
1428a4e54154Smrg}
1429a4e54154Smrg
1430a4e54154Smrgconst FcChar8 *
1431a4e54154SmrgFcStrTripleSecond (FcChar8 *str)
1432a4e54154Smrg{
1433a4e54154Smrg    FcChar8 *second = str + strlen((char *) str) + 1;
1434a4e54154Smrg
1435a4e54154Smrg    if (*second == '\0')
1436a4e54154Smrg	return 0;
1437a4e54154Smrg    return second;
1438a4e54154Smrg}
1439a4e54154Smrg
1440a4e54154Smrgconst FcChar8 *
1441a4e54154SmrgFcStrTripleThird (FcChar8 *str)
1442a4e54154Smrg{
1443a4e54154Smrg    FcChar8 *second = str + strlen ((char *) str) + 1;
1444a4e54154Smrg    FcChar8 *third = second + strlen ((char *) second) + 1;
1445a4e54154Smrg
1446a4e54154Smrg    if (*third == '\0')
1447a4e54154Smrg	return 0;
1448a4e54154Smrg    return third;
1449a4e54154Smrg}
1450a4e54154Smrg
14512c393a42SmrgFcBool
14522c393a42SmrgFcStrSetAddFilename (FcStrSet *set, const FcChar8 *s)
14532c393a42Smrg{
14542c393a42Smrg    FcChar8 *new = FcStrCopyFilename (s);
14552c393a42Smrg    if (!new)
14562c393a42Smrg	return FcFalse;
1457a4e54154Smrg    if (!_FcStrSetInsert (set, new, set->num))
14582c393a42Smrg    {
14592c393a42Smrg	FcStrFree (new);
14602c393a42Smrg	return FcFalse;
14612c393a42Smrg    }
14622c393a42Smrg    return FcTrue;
14632c393a42Smrg}
14642c393a42Smrg
1465a4e54154SmrgFcBool
1466a4e54154SmrgFcStrSetAddFilenamePairWithSalt (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *salt)
1467a4e54154Smrg{
1468a4e54154Smrg    FcChar8 *new_a = NULL;
1469a4e54154Smrg    FcChar8 *new_b = NULL;
1470a4e54154Smrg    FcChar8 *rs = NULL;
1471a4e54154Smrg    FcBool  ret;
1472a4e54154Smrg
1473a4e54154Smrg    if (a)
1474a4e54154Smrg    {
1475a4e54154Smrg	new_a = FcStrCopyFilename (a);
1476a4e54154Smrg	if (!new_a)
1477a4e54154Smrg	    return FcFalse;
1478a4e54154Smrg    }
1479a4e54154Smrg    if (b)
1480a4e54154Smrg    {
1481a4e54154Smrg	new_b = FcStrCopyFilename(b);
1482a4e54154Smrg	if (!new_b)
1483a4e54154Smrg	{
1484a4e54154Smrg	    if (new_a)
1485a4e54154Smrg		FcStrFree(new_a);
1486a4e54154Smrg	    return FcFalse;
1487a4e54154Smrg	}
1488a4e54154Smrg    }
1489a4e54154Smrg    /* Override maps with new one if exists */
1490a4e54154Smrg    if (FcStrSetMemberAB (set, new_a, new_b, &rs))
1491a4e54154Smrg    {
1492a4e54154Smrg	FcStrSetDel (set, rs);
1493a4e54154Smrg    }
1494a4e54154Smrg    ret = FcStrSetAddTriple (set, new_a, new_b, salt);
1495a4e54154Smrg    if (new_a)
1496a4e54154Smrg	FcStrFree (new_a);
1497a4e54154Smrg    if (new_b)
1498a4e54154Smrg	FcStrFree (new_b);
1499a4e54154Smrg    return ret;
1500a4e54154Smrg}
1501a4e54154Smrg
1502ca08ab68SmrgFcBool
1503ca08ab68SmrgFcStrSetAddLangs (FcStrSet *strs, const char *languages)
1504ca08ab68Smrg{
1505ca08ab68Smrg    const char *p = languages, *next;
1506ca08ab68Smrg    FcChar8 lang[128] = {0}, *normalized_lang;
1507ca08ab68Smrg    size_t len;
1508ca08ab68Smrg    FcBool ret = FcFalse;
1509ca08ab68Smrg
1510ca08ab68Smrg    if (!languages)
1511ca08ab68Smrg	return FcFalse;
1512ca08ab68Smrg
1513ca08ab68Smrg    while ((next = strchr (p, ':')))
1514ca08ab68Smrg    {
1515ca08ab68Smrg	len = next - p;
1516c9710b42Smrg	len = FC_MIN (len, 127);
1517ca08ab68Smrg	strncpy ((char *) lang, p, len);
1518ca08ab68Smrg	lang[len] = 0;
1519ca08ab68Smrg	/* ignore an empty item */
1520ca08ab68Smrg	if (*lang)
1521ca08ab68Smrg	{
1522ca08ab68Smrg	    normalized_lang = FcLangNormalize ((const FcChar8 *) lang);
1523ca08ab68Smrg	    if (normalized_lang)
1524ca08ab68Smrg	    {
1525ca08ab68Smrg		FcStrSetAdd (strs, normalized_lang);
1526c9710b42Smrg		FcStrFree (normalized_lang);
1527ca08ab68Smrg		ret = FcTrue;
1528ca08ab68Smrg	    }
1529ca08ab68Smrg	}
1530ca08ab68Smrg	p = next + 1;
1531ca08ab68Smrg    }
1532ca08ab68Smrg    if (*p)
1533ca08ab68Smrg    {
1534ca08ab68Smrg	normalized_lang = FcLangNormalize ((const FcChar8 *) p);
1535ca08ab68Smrg	if (normalized_lang)
1536ca08ab68Smrg	{
1537ca08ab68Smrg	    FcStrSetAdd (strs, normalized_lang);
1538c9710b42Smrg	    FcStrFree (normalized_lang);
1539ca08ab68Smrg	    ret = FcTrue;
1540ca08ab68Smrg	}
1541ca08ab68Smrg    }
1542ca08ab68Smrg
1543ca08ab68Smrg    return ret;
1544ca08ab68Smrg}
1545ca08ab68Smrg
15462c393a42SmrgFcBool
15472c393a42SmrgFcStrSetDel (FcStrSet *set, const FcChar8 *s)
15482c393a42Smrg{
15492c393a42Smrg    int	i;
15502c393a42Smrg
15512c393a42Smrg    for (i = 0; i < set->num; i++)
15522c393a42Smrg	if (!FcStrCmp (set->strs[i], s))
15532c393a42Smrg	{
15542c393a42Smrg	    FcStrFree (set->strs[i]);
15552c393a42Smrg	    /*
15562c393a42Smrg	     * copy remaining string pointers and trailing
15572c393a42Smrg	     * NULL
15582c393a42Smrg	     */
1559ca08ab68Smrg	    memmove (&set->strs[i], &set->strs[i+1],
15602c393a42Smrg		     (set->num - i) * sizeof (FcChar8 *));
15612c393a42Smrg	    set->num--;
15622c393a42Smrg	    return FcTrue;
15632c393a42Smrg	}
15642c393a42Smrg    return FcFalse;
15652c393a42Smrg}
15662c393a42Smrg
1567a4e54154SmrgFcBool
1568a4e54154SmrgFcStrSetDeleteAll (FcStrSet *set)
1569a4e54154Smrg{
1570a4e54154Smrg    int i;
1571a4e54154Smrg
1572a4e54154Smrg    if (FcRefIsConst (&set->ref))
1573a4e54154Smrg	return FcFalse;
1574a4e54154Smrg
1575a4e54154Smrg    for (i = set->num; i > 0; i--)
1576a4e54154Smrg    {
1577a4e54154Smrg	FcStrFree (set->strs[i - 1]);
1578a4e54154Smrg	set->num--;
1579a4e54154Smrg    }
1580a4e54154Smrg    return FcTrue;
1581a4e54154Smrg}
1582a4e54154Smrg
1583c9710b42Smrg/* TODO Make public */
1584c9710b42Smrgstatic FcStrSet *
1585c9710b42SmrgFcStrSetReference (FcStrSet *set)
1586c9710b42Smrg{
1587c9710b42Smrg    if (FcRefIsConst (&set->ref))
1588c9710b42Smrg	return set;
1589c9710b42Smrg
1590c9710b42Smrg    FcRefInc (&set->ref);
1591c9710b42Smrg    return set;
1592c9710b42Smrg}
1593c9710b42Smrg
15942c393a42Smrgvoid
15952c393a42SmrgFcStrSetDestroy (FcStrSet *set)
15962c393a42Smrg{
1597c9710b42Smrg    int	i;
1598ca08ab68Smrg
1599c9710b42Smrg    /* We rely on this in FcGetDefaultLangs for caching. */
1600c9710b42Smrg    if (FcRefIsConst (&set->ref))
1601c9710b42Smrg	return;
1602c9710b42Smrg
1603c9710b42Smrg    if (FcRefDec (&set->ref) != 1)
1604c9710b42Smrg	return;
1605c9710b42Smrg
1606c9710b42Smrg    for (i = 0; i < set->num; i++)
1607c9710b42Smrg	FcStrFree (set->strs[i]);
1608c9710b42Smrg    if (set->strs)
1609c9710b42Smrg	free (set->strs);
1610c9710b42Smrg    free (set);
16112c393a42Smrg}
16122c393a42Smrg
16132c393a42SmrgFcStrList *
16142c393a42SmrgFcStrListCreate (FcStrSet *set)
16152c393a42Smrg{
16162c393a42Smrg    FcStrList	*list;
16172c393a42Smrg
16182c393a42Smrg    list = malloc (sizeof (FcStrList));
16192c393a42Smrg    if (!list)
16202c393a42Smrg	return 0;
16212c393a42Smrg    list->set = set;
1622c9710b42Smrg    FcStrSetReference (set);
16232c393a42Smrg    list->n = 0;
16242c393a42Smrg    return list;
16252c393a42Smrg}
16262c393a42Smrg
16276fc018e4Smrgvoid
16286fc018e4SmrgFcStrListFirst (FcStrList *list)
16296fc018e4Smrg{
16306fc018e4Smrg    list->n = 0;
16316fc018e4Smrg}
16326fc018e4Smrg
16332c393a42SmrgFcChar8 *
16342c393a42SmrgFcStrListNext (FcStrList *list)
16352c393a42Smrg{
16362c393a42Smrg    if (list->n >= list->set->num)
16372c393a42Smrg	return 0;
16382c393a42Smrg    return list->set->strs[list->n++];
16392c393a42Smrg}
16402c393a42Smrg
16412c393a42Smrgvoid
16422c393a42SmrgFcStrListDone (FcStrList *list)
16432c393a42Smrg{
16442c393a42Smrg    FcStrSetDestroy (list->set);
16452c393a42Smrg    free (list);
16462c393a42Smrg}
16472c393a42Smrg
16482c393a42Smrg#define __fcstr__
16492c393a42Smrg#include "fcaliastail.h"
16502c393a42Smrg#undef __fcstr__
1651