]> git.wh0rd.org Git - fontconfig.git/blob - src/fcstr.c
Adopt some RedHat suggestions for standard font configuration.
[fontconfig.git] / src / fcstr.c
1 /*
2  * $RCSId: xc/lib/fontconfig/src/fcstr.c,v 1.10 2002/08/31 22:17:32 keithp Exp $
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 Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes 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  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD 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 <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include "fcint.h"
29
30 FcChar8 *
31 FcStrCopy (const FcChar8 *s)
32 {
33     FcChar8     *r;
34
35     if (!s)
36         return 0;
37     r = (FcChar8 *) malloc (strlen ((char *) s) + 1);
38     if (!r)
39         return 0;
40     FcMemAlloc (FC_MEM_STRING, strlen ((char *) s) + 1);
41     strcpy ((char *) r, (char *) s);
42     return r;
43 }
44
45 FcChar8 *
46 FcStrPlus (const FcChar8 *s1, const FcChar8 *s2)
47 {
48     int     l = strlen ((char *)s1) + strlen ((char *) s2) + 1;
49     FcChar8 *s = malloc (l);
50
51     if (!s)
52         return 0;
53     FcMemAlloc (FC_MEM_STRING, l);
54     strcpy ((char *) s, (char *) s1);
55     strcat ((char *) s, (char *) s2);
56     return s;
57 }
58
59 void
60 FcStrFree (FcChar8 *s)
61 {
62     FcMemFree (FC_MEM_STRING, strlen ((char *) s) + 1);
63     free (s);
64 }
65
66
67 #include "../fc-case/fccase.h"
68
69 #define FcCaseFoldUpperCount(cf) \
70     ((cf)->method == FC_CASE_FOLD_FULL ? 1 : (cf)->count)
71
72 #define FC_STR_CANON_BUF_LEN    1024
73
74 typedef struct _FcCaseWalker {
75     const FcChar8   *read;
76     const FcChar8   *src;
77     int             len;
78     FcChar8         utf8[FC_MAX_CASE_FOLD_CHARS + 1];
79 } FcCaseWalker;
80
81 static void
82 FcStrCaseWalkerInit (const FcChar8 *src, FcCaseWalker *w)
83 {
84     w->src = src;
85     w->read = 0;
86     w->len = strlen (src);
87 }
88
89 static FcChar8
90 FcStrCaseWalkerLong (FcCaseWalker *w, FcChar8 r)
91 {
92     FcChar32    ucs4;
93     int         slen;
94
95     slen = FcUtf8ToUcs4 (w->src - 1, &ucs4, w->len);
96     if (slen <= 0)
97         return r;
98     if (FC_MIN_FOLD_CHAR <= ucs4 && ucs4 <= FC_MAX_FOLD_CHAR)
99     {
100         int min = 0;
101         int max = FC_NUM_CASE_FOLD;
102
103         while (min <= max)
104         {
105             int         mid = (min + max) >> 1;
106             FcChar32    low = fcCaseFold[mid].upper;
107             FcChar32    high = low + FcCaseFoldUpperCount (&fcCaseFold[mid]);
108             
109             if (high <= ucs4)
110                 min = mid + 1;
111             else if (ucs4 < low)
112                 max = mid - 1;
113             else
114             {
115                 const FcCaseFold    *fold = &fcCaseFold[mid];
116                 int                 dlen;
117                 
118                 switch (fold->method) {
119                 case  FC_CASE_FOLD_EVEN_ODD:
120                     if ((ucs4 & 1) != (fold->upper & 1))
121                         return r;
122                     /* fall through ... */
123                 default:
124                     dlen = FcUcs4ToUtf8 (ucs4 + fold->offset, w->utf8);
125                     break;
126                 case FC_CASE_FOLD_FULL:
127                     dlen = fold->count;
128                     memcpy (w->utf8, fcCaseFoldChars + fold->offset, dlen);
129                     break;
130                 }
131                 
132                 /* consume rest of src utf-8 bytes */
133                 w->src += slen - 1;
134                 w->len -= slen - 1;
135                 
136                 /* read from temp buffer */
137                 w->utf8[dlen] = '\0';
138                 w->read = w->utf8;
139                 return *w->read++;
140             }
141         }
142     }
143     return r;
144 }
145
146 static FcChar8
147 FcStrCaseWalkerNext (FcCaseWalker *w)
148 {
149     FcChar8     r;
150
151     if (w->read)
152     {
153         if ((r = *w->read++))
154             return r;
155         w->read = 0;
156     }
157     r = *w->src++;
158     --w->len;
159     
160     if ((r & 0xc0) == 0xc0)
161         return FcStrCaseWalkerLong (w, r);
162     if ('A' <= r && r <= 'Z')
163         r = r - 'A' + 'a';
164     return r;
165 }
166
167 static FcChar8
168 FcStrCaseWalkerNextIgnoreBlanks (FcCaseWalker *w)
169 {
170     FcChar8     r;
171
172     if (w->read)
173     {
174         if ((r = *w->read++))
175             return r;
176         w->read = 0;
177     }
178     do
179     {
180         r = *w->src++;
181         --w->len;
182     } while (r == ' ');
183     
184     if ((r & 0xc0) == 0xc0)
185         return FcStrCaseWalkerLong (w, r);
186     if ('A' <= r && r <= 'Z')
187         r = r - 'A' + 'a';
188     return r;
189 }
190
191 int
192 FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
193 {
194     FcCaseWalker    w1, w2;
195     FcChar8         c1, c2;
196
197     if (s1 == s2) return 0;
198     
199     FcStrCaseWalkerInit (s1, &w1);
200     FcStrCaseWalkerInit (s2, &w2);
201     
202     for (;;) 
203     {
204         c1 = FcStrCaseWalkerNext (&w1);
205         c2 = FcStrCaseWalkerNext (&w2);
206         if (!c1 || (c1 != c2))
207             break;
208     }
209     return (int) c1 - (int) c2;
210 }
211
212 int
213 FcStrCmpIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
214 {
215     FcCaseWalker    w1, w2;
216     FcChar8         c1, c2;
217
218     if (s1 == s2) return 0;
219     
220     FcStrCaseWalkerInit (s1, &w1);
221     FcStrCaseWalkerInit (s2, &w2);
222     
223     for (;;) 
224     {
225         c1 = FcStrCaseWalkerNextIgnoreBlanks (&w1);
226         c2 = FcStrCaseWalkerNextIgnoreBlanks (&w2);
227         if (!c1 || (c1 != c2))
228             break;
229     }
230     return (int) c1 - (int) c2;
231 }
232
233 int
234 FcStrCmp (const FcChar8 *s1, const FcChar8 *s2)
235 {
236     FcChar8 c1, c2;
237     
238     if (s1 == s2)
239         return 0;
240     for (;;) 
241     {
242         c1 = *s1++;
243         c2 = *s2++;
244         if (!c1 || c1 != c2)
245             break;
246     }
247     return (int) c1 - (int) c2;
248 }
249
250 /*
251  * Return a hash value for a string
252  */
253
254 FcChar32
255 FcStrHashIgnoreCase (const FcChar8 *s)
256 {
257     FcChar32        h = 0;
258     FcCaseWalker    w;
259     FcChar8         c;
260
261     FcStrCaseWalkerInit (s, &w);
262     while ((c = FcStrCaseWalkerNext (&w)))
263         h = ((h << 3) ^ (h >> 3)) ^ c;
264     return h;
265 }
266
267 /*
268  * Is the head of s1 equal to s2?
269  */
270
271 static FcBool
272 FcStrIsAtIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
273 {
274     FcCaseWalker    w1, w2;
275     FcChar8         c1, c2;
276
277     FcStrCaseWalkerInit (s1, &w1);
278     FcStrCaseWalkerInit (s2, &w2);
279     
280     for (;;) 
281     {
282         c1 = FcStrCaseWalkerNextIgnoreBlanks (&w1);
283         c2 = FcStrCaseWalkerNextIgnoreBlanks (&w2);
284         if (!c1 || (c1 != c2))
285             break;
286     }
287     return c1 == c2 || !c2;
288 }
289
290 /*
291  * Does s1 contain an instance of s2 (ignoring blanks and case)?
292  */
293
294 const FcChar8 *
295 FcStrContainsIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
296 {
297     while (*s1)
298     {
299         if (FcStrIsAtIgnoreBlanksAndCase (s1, s2))
300             return s1;
301         s1++;
302     }
303     return 0;
304 }
305
306 /*
307  * Is the head of s1 equal to s2?
308  */
309
310 static FcBool
311 FcStrIsAtIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
312 {
313     FcCaseWalker    w1, w2;
314     FcChar8         c1, c2;
315
316     FcStrCaseWalkerInit (s1, &w1);
317     FcStrCaseWalkerInit (s2, &w2);
318     
319     for (;;) 
320     {
321         c1 = FcStrCaseWalkerNext (&w1);
322         c2 = FcStrCaseWalkerNext (&w2);
323         if (!c1 || (c1 != c2))
324             break;
325     }
326     return c1 == c2 || !c2;
327 }
328
329 /*
330  * Does s1 contain an instance of s2 (ignoring blanks and case)?
331  */
332
333 const FcChar8 *
334 FcStrContainsIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
335 {
336     while (*s1)
337     {
338         if (FcStrIsAtIgnoreCase (s1, s2))
339             return s1;
340         s1++;
341     }
342     return 0;
343 }
344
345 const FcChar8 *
346 FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
347 {
348     FcCaseWalker    w1, w2;
349     FcChar8         c1, c2;
350     const FcChar8   *cur;
351
352     if (!s1 || !s2)
353         return 0;
354
355     if (s1 == s2)
356         return s1;
357     
358     FcStrCaseWalkerInit (s1, &w1);
359     FcStrCaseWalkerInit (s2, &w2);
360     
361     c2 = FcStrCaseWalkerNext (&w2);
362     
363     for (;;)
364     {
365         cur = w1.src;
366         c1 = FcStrCaseWalkerNext (&w1);
367         if (!c1)
368             break;
369         if (c1 == c2)
370         {
371             FcCaseWalker    w1t = w1;
372             FcCaseWalker    w2t = w2;
373             FcChar8         c1t, c2t;
374
375             for (;;)
376             {
377                 c1t = FcStrCaseWalkerNext (&w1t);
378                 c2t = FcStrCaseWalkerNext (&w2t);
379
380                 if (!c2t)
381                     return cur;
382                 if (c2t != c1t)
383                     break;
384             }
385         }
386     }
387     return 0;
388 }
389
390 const FcChar8 *
391 FcStrStr (const FcChar8 *s1, const FcChar8 *s2)
392 {
393     FcChar8 c1, c2;
394     const FcChar8 * p = s1;
395     const FcChar8 * b = s2;
396
397     if (!s1 || !s2)
398         return 0;
399
400     if (s1 == s2)
401         return s1;
402
403 again:
404     c2 = *s2++;
405
406     if (!c2)
407         return 0;
408
409     for (;;) 
410     {
411         p = s1;
412         c1 = *s1++;
413         if (!c1 || c1 == c2)
414             break;
415     }
416
417     if (c1 != c2)
418         return 0;
419
420     for (;;)
421     {
422         c1 = *s1;
423         c2 = *s2;
424         if (c1 && c2 && c1 != c2)
425         {
426             s1 = p + 1;
427             s2 = b;
428             goto again;
429         }
430         if (!c2)
431             return p;
432         if (!c1)
433             return 0;
434         ++ s1;
435         ++ s2;
436     }
437
438     return 0;
439 }
440
441 int
442 FcUtf8ToUcs4 (const FcChar8 *src_orig,
443               FcChar32      *dst,
444               int           len)
445 {
446     const FcChar8   *src = src_orig;
447     FcChar8         s;
448     int             extra;
449     FcChar32        result;
450
451     if (len == 0)
452         return 0;
453     
454     s = *src++;
455     len--;
456     
457     if (!(s & 0x80))
458     {
459         result = s;
460         extra = 0;
461     } 
462     else if (!(s & 0x40))
463     {
464         return -1;
465     }
466     else if (!(s & 0x20))
467     {
468         result = s & 0x1f;
469         extra = 1;
470     }
471     else if (!(s & 0x10))
472     {
473         result = s & 0xf;
474         extra = 2;
475     }
476     else if (!(s & 0x08))
477     {
478         result = s & 0x07;
479         extra = 3;
480     }
481     else if (!(s & 0x04))
482     {
483         result = s & 0x03;
484         extra = 4;
485     }
486     else if ( ! (s & 0x02))
487     {
488         result = s & 0x01;
489         extra = 5;
490     }
491     else
492     {
493         return -1;
494     }
495     if (extra > len)
496         return -1;
497     
498     while (extra--)
499     {
500         result <<= 6;
501         s = *src++;
502         
503         if ((s & 0xc0) != 0x80)
504             return -1;
505         
506         result |= s & 0x3f;
507     }
508     *dst = result;
509     return src - src_orig;
510 }
511
512 FcBool
513 FcUtf8Len (const FcChar8    *string,
514            int              len,
515            int              *nchar,
516            int              *wchar)
517 {
518     int         n;
519     int         clen;
520     FcChar32    c;
521     FcChar32    max;
522     
523     n = 0;
524     max = 0;
525     while (len)
526     {
527         clen = FcUtf8ToUcs4 (string, &c, len);
528         if (clen <= 0)  /* malformed UTF8 string */
529             return FcFalse;
530         if (c > max)
531             max = c;
532         string += clen;
533         len -= clen;
534         n++;
535     }
536     *nchar = n;
537     if (max >= 0x10000)
538         *wchar = 4;
539     else if (max > 0x100)
540         *wchar = 2;
541     else
542         *wchar = 1;
543     return FcTrue;
544 }
545
546 int
547 FcUcs4ToUtf8 (FcChar32  ucs4,
548               FcChar8   dest[FC_UTF8_MAX_LEN])
549 {
550     int bits;
551     FcChar8 *d = dest;
552     
553     if      (ucs4 <       0x80) {  *d++=  ucs4;                         bits= -6; }
554     else if (ucs4 <      0x800) {  *d++= ((ucs4 >>  6) & 0x1F) | 0xC0;  bits=  0; }
555     else if (ucs4 <    0x10000) {  *d++= ((ucs4 >> 12) & 0x0F) | 0xE0;  bits=  6; }
556     else if (ucs4 <   0x200000) {  *d++= ((ucs4 >> 18) & 0x07) | 0xF0;  bits= 12; }
557     else if (ucs4 <  0x4000000) {  *d++= ((ucs4 >> 24) & 0x03) | 0xF8;  bits= 18; }
558     else if (ucs4 < 0x80000000) {  *d++= ((ucs4 >> 30) & 0x01) | 0xFC;  bits= 24; }
559     else return 0;
560
561     for ( ; bits >= 0; bits-= 6) {
562         *d++= ((ucs4 >> bits) & 0x3F) | 0x80;
563     }
564     return d - dest;
565 }
566
567 #define GetUtf16(src,endian) \
568     ((FcChar16) ((src)[endian == FcEndianBig ? 0 : 1] << 8) | \
569      (FcChar16) ((src)[endian == FcEndianBig ? 1 : 0]))
570
571 int
572 FcUtf16ToUcs4 (const FcChar8    *src_orig,
573                FcEndian         endian,
574                FcChar32         *dst,
575                int              len)    /* in bytes */
576 {
577     const FcChar8   *src = src_orig;
578     FcChar16        a, b;
579     FcChar32        result;
580
581     if (len < 2)
582         return 0;
583     
584     a = GetUtf16 (src, endian); src += 2; len -= 2;
585     
586     /* 
587      * Check for surrogate 
588      */
589     if ((a & 0xfc00) == 0xd800)
590     {
591         if (len < 2)
592             return 0;
593         b = GetUtf16 (src, endian); src += 2; len -= 2;
594         /*
595          * Check for invalid surrogate sequence
596          */
597         if ((b & 0xfc00) != 0xdc00)
598             return 0;
599         result = ((((FcChar32) a & 0x3ff) << 10) |
600                   ((FcChar32) b & 0x3ff)) + 0x10000;
601     }
602     else
603         result = a;
604     *dst = result;
605     return src - src_orig;
606 }
607
608 FcBool
609 FcUtf16Len (const FcChar8   *string,
610             FcEndian        endian,
611             int             len,        /* in bytes */
612             int             *nchar,
613             int             *wchar)
614 {
615     int         n;
616     int         clen;
617     FcChar32    c;
618     FcChar32    max;
619     
620     n = 0;
621     max = 0;
622     while (len)
623     {
624         clen = FcUtf16ToUcs4 (string, endian, &c, len);
625         if (clen <= 0)  /* malformed UTF8 string */
626             return FcFalse;
627         if (c > max)
628             max = c;
629         string += clen;
630         len -= clen;
631         n++;
632     }
633     *nchar = n;
634     if (max >= 0x10000)
635         *wchar = 4;
636     else if (max > 0x100)
637         *wchar = 2;
638     else
639         *wchar = 1;
640     return FcTrue;
641 }
642
643 void
644 FcStrBufInit (FcStrBuf *buf, FcChar8 *init, int size)
645 {
646     buf->buf = init;
647     buf->allocated = FcFalse;
648     buf->failed = FcFalse;
649     buf->len = 0;
650     buf->size = size;
651 }
652
653 void
654 FcStrBufDestroy (FcStrBuf *buf)
655 {
656     if (buf->allocated)
657     {
658         FcMemFree (FC_MEM_STRBUF, buf->size);
659         free (buf->buf);
660         FcStrBufInit (buf, 0, 0);
661     }
662 }
663
664 FcChar8 *
665 FcStrBufDone (FcStrBuf *buf)
666 {
667     FcChar8 *ret;
668
669     ret = malloc (buf->len + 1);
670     if (ret)
671     {
672         FcMemAlloc (FC_MEM_STRING, buf->len + 1);
673         memcpy (ret, buf->buf, buf->len);
674         ret[buf->len] = '\0';
675     }
676     FcStrBufDestroy (buf);
677     return ret;
678 }
679
680 FcBool
681 FcStrBufChar (FcStrBuf *buf, FcChar8 c)
682 {
683     if (buf->len == buf->size)
684     {
685         FcChar8     *new;
686         int         size;
687
688         if (buf->allocated)
689         {
690             size = buf->size * 2;
691             new = realloc (buf->buf, size);
692         }
693         else
694         {
695             size = buf->size + 1024;
696             new = malloc (size);
697             if (new)
698             {
699                 buf->allocated = FcTrue;
700                 memcpy (new, buf->buf, buf->len);
701             }
702         }
703         if (!new)
704         {
705             buf->failed = FcTrue;
706             return FcFalse;
707         }
708         if (buf->size)
709             FcMemFree (FC_MEM_STRBUF, buf->size);
710         FcMemAlloc (FC_MEM_STRBUF, size);
711         buf->size = size;
712         buf->buf = new;
713     }
714     buf->buf[buf->len++] = c;
715     return FcTrue;
716 }
717
718 FcBool
719 FcStrBufString (FcStrBuf *buf, const FcChar8 *s)
720 {
721     FcChar8 c;
722     while ((c = *s++))
723         if (!FcStrBufChar (buf, c))
724             return FcFalse;
725     return FcTrue;
726 }
727
728 FcBool
729 FcStrBufData (FcStrBuf *buf, const FcChar8 *s, int len)
730 {
731     while (len-- > 0)
732         if (!FcStrBufChar (buf, *s++))
733             return FcFalse;
734     return FcTrue;
735 }
736
737 FcBool
738 FcStrUsesHome (const FcChar8 *s)
739 {
740     return *s == '~';
741 }
742
743 FcChar8 *
744 FcStrCopyFilename (const FcChar8 *s)
745 {
746     FcChar8 *new;
747     
748     if (*s == '~')
749     {
750         FcChar8 *home = FcConfigHome ();
751         int     size;
752         if (!home)
753             return 0;
754         size = strlen ((char *) home) + strlen ((char *) s);
755         new = (FcChar8 *) malloc (size);
756         if (!new)
757             return 0;
758         FcMemAlloc (FC_MEM_STRING, size);
759         strcpy ((char *) new, (char *) home);
760         strcat ((char *) new, (char *) s + 1);
761     }
762     else
763     {
764         int     size = strlen ((char *) s) + 1;
765         new = (FcChar8 *) malloc (size);
766         if (!new)
767             return 0;
768         FcMemAlloc (FC_MEM_STRING, size);
769         strcpy ((char *) new, (const char *) s);
770     }
771     return new;
772 }
773
774 FcChar8 *
775 FcStrLastSlash (const FcChar8  *path)
776 {
777     FcChar8         *slash;
778
779     slash = (FcChar8 *) strrchr ((const char *) path, '/');
780 #ifdef _WIN32
781     {
782         FcChar8     *backslash;
783
784         backslash = (FcChar8 *) strrchr ((const char *) path, '\\');
785         if (!slash || (backslash && backslash > slash))
786             slash = backslash;
787     }
788 #endif
789
790     return slash;
791 }
792   
793 FcChar8 *
794 FcStrDirname (const FcChar8 *file)
795 {
796     FcChar8 *slash;
797     FcChar8 *dir;
798
799     slash = FcStrLastSlash (file);
800     if (!slash)
801         return FcStrCopy ((FcChar8 *) ".");
802     dir = malloc ((slash - file) + 1);
803     if (!dir)
804         return 0;
805     FcMemAlloc (FC_MEM_STRING, (slash - file) + 1);
806     strncpy ((char *) dir, (const char *) file, slash - file);
807     dir[slash - file] = '\0';
808     return dir;
809 }
810
811 FcChar8 *
812 FcStrBasename (const FcChar8 *file)
813 {
814     FcChar8 *slash;
815
816     slash = FcStrLastSlash (file);
817     if (!slash)
818         return FcStrCopy (file);
819     return FcStrCopy (slash + 1);
820 }
821
822 FcStrSet *
823 FcStrSetCreate (void)
824 {
825     FcStrSet    *set = malloc (sizeof (FcStrSet));
826     if (!set)
827         return 0;
828     FcMemAlloc (FC_MEM_STRSET, sizeof (FcStrSet));
829     set->ref = 1;
830     set->num = 0;
831     set->size = 0;
832     set->strs = 0;
833     return set;
834 }
835
836 static FcBool
837 _FcStrSetAppend (FcStrSet *set, FcChar8 *s)
838 {
839     if (FcStrSetMember (set, s))
840     {
841         FcStrFree (s);
842         return FcTrue;
843     }
844     if (set->num == set->size)
845     {
846         FcChar8 **strs = malloc ((set->size + 2) * sizeof (FcChar8 *));
847
848         if (!strs)
849             return FcFalse;
850         FcMemAlloc (FC_MEM_STRSET, (set->size + 2) * sizeof (FcChar8 *));
851         set->size = set->size + 1;
852         if (set->num)
853             memcpy (strs, set->strs, set->num * sizeof (FcChar8 *));
854         if (set->strs)
855             free (set->strs);
856         set->strs = strs;
857     }
858     set->strs[set->num++] = s;
859     set->strs[set->num] = 0;
860     return FcTrue;
861 }
862
863 FcBool
864 FcStrSetMember (FcStrSet *set, const FcChar8 *s)
865 {
866     int i;
867
868     for (i = 0; i < set->num; i++)
869         if (!FcStrCmp (set->strs[i], s))
870             return FcTrue;
871     return FcFalse;
872 }
873
874 FcBool
875 FcStrSetEqual (FcStrSet *sa, FcStrSet *sb)
876 {
877     int i;
878     if (sa->num != sb->num)
879         return FcFalse;
880     for (i = 0; i < sa->num; i++)
881         if (!FcStrSetMember (sb, sa->strs[i]))
882             return FcFalse;
883     return FcTrue;
884 }
885
886 FcBool
887 FcStrSetAdd (FcStrSet *set, const FcChar8 *s)
888 {
889     FcChar8 *new = FcStrCopy (s);
890     if (!new)
891         return FcFalse;
892     if (!_FcStrSetAppend (set, new))
893     {
894         FcStrFree (new);
895         return FcFalse;
896     }
897     return FcTrue;
898 }
899
900 FcBool
901 FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s)
902 {
903     FcChar8 *new = FcStrCopyFilename (s);
904     if (!new)
905         return FcFalse;
906     if (!_FcStrSetAppend (set, new))
907     {
908         FcStrFree (new);
909         return FcFalse;
910     }
911     return FcTrue;
912 }
913
914 FcBool
915 FcStrSetDel (FcStrSet *set, const FcChar8 *s)
916 {
917     int i;
918
919     for (i = 0; i < set->num; i++)
920         if (!FcStrCmp (set->strs[i], s))
921         {
922             FcStrFree (set->strs[i]);
923             /*
924              * copy remaining string pointers and trailing
925              * NULL
926              */
927             memmove (&set->strs[i], &set->strs[i+1], 
928                      (set->num - i) * sizeof (FcChar8 *));
929             set->num--;
930             return FcTrue;
931         }
932     return FcFalse;
933 }
934
935 void
936 FcStrSetDestroy (FcStrSet *set)
937 {
938     if (--set->ref == 0)
939     {
940         int     i;
941     
942         for (i = 0; i < set->num; i++)
943             FcStrFree (set->strs[i]);
944         FcMemFree (FC_MEM_STRSET, (set->size) * sizeof (FcChar8 *));
945         if (set->strs)
946             free (set->strs);
947         FcMemFree (FC_MEM_STRSET, sizeof (FcStrSet));
948         free (set);
949     }
950 }
951
952 FcStrList *
953 FcStrListCreate (FcStrSet *set)
954 {
955     FcStrList   *list;
956
957     list = malloc (sizeof (FcStrList));
958     if (!list)
959         return 0;
960     FcMemAlloc (FC_MEM_STRLIST, sizeof (FcStrList));
961     list->set = set;
962     set->ref++;
963     list->n = 0;
964     return list;
965 }
966
967 FcChar8 *
968 FcStrListNext (FcStrList *list)
969 {
970     if (list->n >= list->set->num)
971         return 0;
972     return list->set->strs[list->n++];
973 }
974
975 void
976 FcStrListDone (FcStrList *list)
977 {
978     FcStrSetDestroy (list->set);
979     FcMemFree (FC_MEM_STRLIST, sizeof (FcStrList));
980     free (list);
981 }