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