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