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