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