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
33 FcChar8 *
FcStrCopy(const FcChar8 * s)34 FcStrCopy (const FcChar8 *s)
35 {
36 return FcStrdup (s);
37 }
38
39 static FcChar8 *
FcStrMakeTriple(const FcChar8 * s1,const FcChar8 * s2,const FcChar8 * s3)40 FcStrMakeTriple (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
65 FcChar8 *
FcStrPlus(const FcChar8 * s1,const FcChar8 * s2)66 FcStrPlus (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
80 void
FcStrFree(FcChar8 * s)81 FcStrFree (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
92 typedef struct _FcCaseWalker {
93 const FcChar8 *read;
94 const FcChar8 *src;
95 FcChar8 utf8[FC_MAX_CASE_FOLD_CHARS + 1];
96 } FcCaseWalker;
97
98 static void
FcStrCaseWalkerInit(const FcChar8 * src,FcCaseWalker * w)99 FcStrCaseWalkerInit (const FcChar8 *src, FcCaseWalker *w)
100 {
101 w->src = src;
102 w->read = 0;
103 }
104
105 static FcChar8
FcStrCaseWalkerLong(FcCaseWalker * w,FcChar8 r)106 FcStrCaseWalkerLong (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
162 static FcChar8
FcStrCaseWalkerNextNonDelim(FcCaseWalker * w,const char * delims)163 FcStrCaseWalkerNextNonDelim (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
185 static FcChar8
FcStrCaseWalkerNextNonBlank(FcCaseWalker * w)186 FcStrCaseWalkerNextNonBlank (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
208 static FcChar8
FcStrCaseWalkerNext(FcCaseWalker * w)209 FcStrCaseWalkerNext (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
229 FcChar8 *
FcStrDowncase(const FcChar8 * s)230 FcStrDowncase (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
247 int
FcStrCmpIgnoreCase(const FcChar8 * s1,const FcChar8 * s2)248 FcStrCmpIgnoreCase (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
268 int
FcStrCmpIgnoreBlanksAndCase(const FcChar8 * s1,const FcChar8 * s2)269 FcStrCmpIgnoreBlanksAndCase (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
289 int
FcStrCmp(const FcChar8 * s1,const FcChar8 * s2)290 FcStrCmp (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
310 FcChar32
FcStrHashIgnoreCase(const FcChar8 * s)311 FcStrHashIgnoreCase (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
323 FcChar32
FcStrHashIgnoreBlanksAndCase(const FcChar8 * s)324 FcStrHashIgnoreBlanksAndCase (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
340 static FcBool
FcStrIsAtIgnoreBlanksAndCase(const FcChar8 * s1,const FcChar8 * s2)341 FcStrIsAtIgnoreBlanksAndCase (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
363 const FcChar8 *
FcStrContainsIgnoreBlanksAndCase(const FcChar8 * s1,const FcChar8 * s2)364 FcStrContainsIgnoreBlanksAndCase (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
375 static FcBool
FcCharIsPunct(const FcChar8 c)376 FcCharIsPunct (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
399 static FcBool
FcStrIsAtIgnoreCase(const FcChar8 * s1,const FcChar8 * s2)400 FcStrIsAtIgnoreCase (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
422 const FcChar8 *
FcStrContainsIgnoreCase(const FcChar8 * s1,const FcChar8 * s2)423 FcStrContainsIgnoreCase (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
438 const FcChar8 *
FcStrContainsWord(const FcChar8 * s1,const FcChar8 * s2)439 FcStrContainsWord (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
466 int
FcStrMatchIgnoreCaseAndDelims(const FcChar8 * s1,const FcChar8 * s2,const FcChar8 * delims)467 FcStrMatchIgnoreCaseAndDelims (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
487 FcBool
FcStrGlobMatch(const FcChar8 * glob,const FcChar8 * string)488 FcStrGlobMatch (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
531 const FcChar8 *
FcStrStrIgnoreCase(const FcChar8 * s1,const FcChar8 * s2)532 FcStrStrIgnoreCase (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
576 const FcChar8 *
FcStrStr(const FcChar8 * s1,const FcChar8 * s2)577 FcStrStr (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
589 again:
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
626 int
FcUtf8ToUcs4(const FcChar8 * src_orig,FcChar32 * dst,int len)627 FcUtf8ToUcs4 (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
697 FcBool
FcUtf8Len(const FcChar8 * string,int len,int * nchar,int * wchar)698 FcUtf8Len (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
731 int
FcUcs4ToUtf8(FcChar32 ucs4,FcChar8 dest[FC_UTF8_MAX_LEN])732 FcUcs4ToUtf8 (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
756 int
FcUtf16ToUcs4(const FcChar8 * src_orig,FcEndian endian,FcChar32 * dst,int len)757 FcUtf16ToUcs4 (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
793 FcBool
FcUtf16Len(const FcChar8 * string,FcEndian endian,int len,int * nchar,int * wchar)794 FcUtf16Len (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
828 void
FcStrBufInit(FcStrBuf * buf,FcChar8 * init,int size)829 FcStrBufInit (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
845 void
FcStrBufDestroy(FcStrBuf * buf)846 FcStrBufDestroy (FcStrBuf *buf)
847 {
848 if (buf->allocated)
849 {
850 free (buf->buf);
851 FcStrBufInit (buf, 0, 0);
852 }
853 }
854
855 FcChar8 *
FcStrBufDone(FcStrBuf * buf)856 FcStrBufDone (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
873 FcChar8 *
FcStrBufDoneStatic(FcStrBuf * buf)874 FcStrBufDoneStatic (FcStrBuf *buf)
875 {
876 FcStrBufChar (buf, '\0');
877
878 if (buf->failed)
879 return NULL;
880
881 return buf->buf;
882 }
883
884 FcBool
FcStrBufChar(FcStrBuf * buf,FcChar8 c)885 FcStrBufChar (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
922 FcBool
FcStrBufString(FcStrBuf * buf,const FcChar8 * s)923 FcStrBufString (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
932 FcBool
FcStrBufData(FcStrBuf * buf,const FcChar8 * s,int len)933 FcStrBufData (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
941 FcBool
FcStrUsesHome(const FcChar8 * s)942 FcStrUsesHome (const FcChar8 *s)
943 {
944 return *s == '~';
945 }
946
947 FcBool
FcStrIsAbsoluteFilename(const FcChar8 * s)948 FcStrIsAbsoluteFilename (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
958 FcChar8 *
FcStrBuildFilename(const FcChar8 * path,...)959 FcStrBuildFilename (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
1010 bail2:
1011 FcStrListDone (list);
1012 bail1:
1013 va_end (ap);
1014 bail0:
1015 FcStrSetDestroy (sset);
1016
1017 return ret;
1018 }
1019
1020 FcChar8 *
FcStrCopyFilename(const FcChar8 * s)1021 FcStrCopyFilename (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
1047 FcChar8 *
FcStrLastSlash(const FcChar8 * path)1048 FcStrLastSlash (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
1066 FcChar8 *
FcStrDirname(const FcChar8 * file)1067 FcStrDirname (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
1083 FcChar8 *
FcStrBasename(const FcChar8 * file)1084 FcStrBasename (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
1094 static FcChar8 *
FcStrCanonAbsoluteFilename(const FcChar8 * s)1095 FcStrCanonAbsoluteFilename (const FcChar8 *s)
1096 {
1097 FcChar8 *file;
1098 FcChar8 *f;
1099 const FcChar8 *slash;
1100 int size;
1101
1102 size = strlen ((char *) s) + 1;
1103 file = malloc (size);
1104 if (!file)
1105 return NULL;
1106 slash = NULL;
1107 f = file;
1108 #ifdef _WIN32
1109 if (*s == '/' && *(s+1) == '/') /* Network path, do not squash // */
1110 *f++ = *s++;
1111 #endif
1112 for (;;) {
1113 if (*s == '/' || *s == '\0')
1114 {
1115 if (slash)
1116 {
1117 switch (s - slash) {
1118 case 1:
1119 f -= 1; /* squash // and trim final / from file */
1120 break;
1121 case 2:
1122 if (!strncmp ((char *) slash, "/.", 2))
1123 {
1124 f -= 2; /* trim /. from file */
1125 }
1126 break;
1127 case 3:
1128 if (!strncmp ((char *) slash, "/..", 3))
1129 {
1130 f -= 3; /* trim /.. from file */
1131 while (f > file) {
1132 if (*--f == '/')
1133 break;
1134 }
1135 }
1136 break;
1137 }
1138 }
1139 slash = s;
1140 }
1141 if (!(*f++ = *s++))
1142 break;
1143 }
1144 return file;
1145 }
1146
1147 #ifdef _WIN32
1148 /*
1149 * Convert '\\' to '/' , remove double '/'
1150 */
1151 static void
FcConvertDosPath(char * str)1152 FcConvertDosPath (char *str)
1153 {
1154 size_t len = strlen (str);
1155 char *p = str;
1156 char *dest = str;
1157 char *end = str + len;
1158 char last = 0;
1159
1160 if (*p == '\\')
1161 {
1162 *p = '/';
1163 p++;
1164 dest++;
1165 }
1166 while (p < end)
1167 {
1168 if (*p == '\\')
1169 *p = '/';
1170
1171 if (*p != '/'
1172 || last != '/')
1173 {
1174 *dest++ = *p;
1175 }
1176
1177 last = *p;
1178 p++;
1179 }
1180
1181 *dest = 0;
1182 }
1183 #endif
1184
1185 FcChar8 *
FcStrCanonFilename(const FcChar8 * s)1186 FcStrCanonFilename (const FcChar8 *s)
1187 {
1188 #ifdef _WIN32
1189 FcChar8 full[FC_MAX_FILE_LEN + 2];
1190 int size = GetFullPathName ((LPCSTR) s, sizeof (full) -1,
1191 (LPSTR) full, NULL);
1192
1193 if (size == 0)
1194 perror ("GetFullPathName");
1195
1196 FcConvertDosPath ((char *) full);
1197 return FcStrCanonAbsoluteFilename (full);
1198 #else
1199 if (s[0] == '/')
1200 return FcStrCanonAbsoluteFilename (s);
1201 else
1202 {
1203 FcChar8 *full;
1204 FcChar8 *file;
1205
1206 FcChar8 cwd[FC_MAX_FILE_LEN + 2];
1207 if (getcwd ((char *) cwd, FC_MAX_FILE_LEN) == NULL)
1208 return NULL;
1209 full = FcStrBuildFilename (cwd, s, NULL);
1210 file = FcStrCanonAbsoluteFilename (full);
1211 FcStrFree (full);
1212 return file;
1213 }
1214 #endif
1215 }
1216
1217
1218 FcStrSet *
FcStrSetCreate(void)1219 FcStrSetCreate (void)
1220 {
1221 return FcStrSetCreateEx (FCSS_DEFAULT);
1222 }
1223
1224 FcStrSet *
FcStrSetCreateEx(unsigned int control)1225 FcStrSetCreateEx (unsigned int control)
1226 {
1227 FcStrSet *set = malloc (sizeof (FcStrSet));
1228 if (!set)
1229 return 0;
1230 FcRefInit (&set->ref, 1);
1231 set->num = 0;
1232 set->size = 0;
1233 set->strs = 0;
1234 set->control = control;
1235 return set;
1236 }
1237
1238 static FcBool
_FcStrSetGrow(FcStrSet * set,int growElements)1239 _FcStrSetGrow (FcStrSet *set, int growElements)
1240 {
1241 /* accommodate an additional NULL entry at the end of the array */
1242 FcChar8 **strs = malloc ((set->size + growElements + 1) * sizeof (FcChar8 *));
1243 if (!strs)
1244 return FcFalse;
1245 if (set->num)
1246 memcpy (strs, set->strs, set->num * sizeof (FcChar8 *));
1247 if (set->strs)
1248 free (set->strs);
1249 set->size = set->size + growElements;
1250 set->strs = strs;
1251 return FcTrue;
1252 }
1253
1254 static FcBool
_FcStrSetAppend(FcStrSet * set,FcChar8 * s)1255 _FcStrSetAppend (FcStrSet *set, FcChar8 *s)
1256 {
1257 if (!FcStrSetHasControlBit (set, FCSS_ALLOW_DUPLICATES))
1258 {
1259 if (FcStrSetMember (set, s))
1260 {
1261 FcStrFree (s);
1262 return FcTrue;
1263 }
1264 }
1265 if (set->num == set->size)
1266 {
1267 int growElements = FcStrSetHasControlBit (set, FCSS_GROW_BY_64) ? 64 : 1;
1268 if (!_FcStrSetGrow(set, growElements))
1269 return FcFalse;
1270 }
1271 set->strs[set->num++] = s;
1272 set->strs[set->num] = 0;
1273 return FcTrue;
1274 }
1275
1276 FcBool
FcStrSetMember(FcStrSet * set,const FcChar8 * s)1277 FcStrSetMember (FcStrSet *set, const FcChar8 *s)
1278 {
1279 int i;
1280
1281 for (i = 0; i < set->num; i++)
1282 if (!FcStrCmp (set->strs[i], s))
1283 return FcTrue;
1284 return FcFalse;
1285 }
1286
1287 static int
fc_strcmp_r(const FcChar8 * s1,const FcChar8 * s2,const FcChar8 ** ret)1288 fc_strcmp_r (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 **ret)
1289 {
1290 FcChar8 c1, c2;
1291
1292 if (s1 == s2)
1293 {
1294 if (ret)
1295 *ret = NULL;
1296 return 0;
1297 }
1298 for (;;)
1299 {
1300 if (s1)
1301 c1 = *s1++;
1302 else
1303 c1 = 0;
1304 if (s2)
1305 c2 = *s2++;
1306 else
1307 c2 = 0;
1308 if (!c1 || c1 != c2)
1309 break;
1310 }
1311 if (ret)
1312 *ret = s1;
1313 return (int) c1 - (int) c2;
1314 }
1315
1316 FcBool
FcStrSetMemberAB(FcStrSet * set,const FcChar8 * a,FcChar8 * b,FcChar8 ** ret)1317 FcStrSetMemberAB (FcStrSet *set, const FcChar8 *a, FcChar8 *b, FcChar8 **ret)
1318 {
1319 int i;
1320 const FcChar8 *s = NULL;
1321
1322 for (i = 0; i < set->num; i++)
1323 {
1324 if (!fc_strcmp_r (set->strs[i], a, &s) && s)
1325 {
1326 if (!fc_strcmp_r (s, b, NULL))
1327 {
1328 if (ret)
1329 *ret = set->strs[i];
1330 return FcTrue;
1331 }
1332 }
1333 }
1334 if (ret)
1335 *ret = NULL;
1336 return FcFalse;
1337 }
1338
1339 FcBool
FcStrSetEqual(FcStrSet * sa,FcStrSet * sb)1340 FcStrSetEqual (FcStrSet *sa, FcStrSet *sb)
1341 {
1342 int i;
1343 if (sa->num != sb->num)
1344 return FcFalse;
1345 for (i = 0; i < sa->num; i++)
1346 if (!FcStrSetMember (sb, sa->strs[i]))
1347 return FcFalse;
1348 return FcTrue;
1349 }
1350
1351 FcBool
FcStrSetAdd(FcStrSet * set,const FcChar8 * s)1352 FcStrSetAdd (FcStrSet *set, const FcChar8 *s)
1353 {
1354 FcChar8 *new = FcStrCopy (s);
1355 if (!new)
1356 return FcFalse;
1357 if (!_FcStrSetAppend (set, new))
1358 {
1359 FcStrFree (new);
1360 return FcFalse;
1361 }
1362 return FcTrue;
1363 }
1364
1365 FcBool
FcStrSetAddTriple(FcStrSet * set,const FcChar8 * a,const FcChar8 * b,const FcChar8 * c)1366 FcStrSetAddTriple (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *c)
1367 {
1368 FcChar8 *new = FcStrMakeTriple (a, b, c);
1369 if (!new)
1370 return FcFalse;
1371 if (!_FcStrSetAppend (set, new))
1372 {
1373 FcStrFree (new);
1374 return FcFalse;
1375 }
1376 return FcTrue;
1377 }
1378
1379 const FcChar8 *
FcStrTripleSecond(FcChar8 * str)1380 FcStrTripleSecond (FcChar8 *str)
1381 {
1382 FcChar8 *second = str + strlen((char *) str) + 1;
1383
1384 if (*second == '\0')
1385 return 0;
1386 return second;
1387 }
1388
1389 const FcChar8 *
FcStrTripleThird(FcChar8 * str)1390 FcStrTripleThird (FcChar8 *str)
1391 {
1392 FcChar8 *second = str + strlen ((char *) str) + 1;
1393 FcChar8 *third = second + strlen ((char *) second) + 1;
1394
1395 if (*third == '\0')
1396 return 0;
1397 return third;
1398 }
1399
1400 FcBool
FcStrSetAddFilename(FcStrSet * set,const FcChar8 * s)1401 FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s)
1402 {
1403 FcChar8 *new = FcStrCopyFilename (s);
1404 if (!new)
1405 return FcFalse;
1406 if (!_FcStrSetAppend (set, new))
1407 {
1408 FcStrFree (new);
1409 return FcFalse;
1410 }
1411 return FcTrue;
1412 }
1413
1414 FcBool
FcStrSetAddFilenamePairWithSalt(FcStrSet * set,const FcChar8 * a,const FcChar8 * b,const FcChar8 * salt)1415 FcStrSetAddFilenamePairWithSalt (FcStrSet *set, const FcChar8 *a, const FcChar8 *b, const FcChar8 *salt)
1416 {
1417 FcChar8 *new_a = NULL;
1418 FcChar8 *new_b = NULL;
1419 FcChar8 *rs = NULL;
1420 FcBool ret;
1421
1422 if (a)
1423 {
1424 new_a = FcStrCopyFilename (a);
1425 if (!new_a)
1426 return FcFalse;
1427 }
1428 if (b)
1429 {
1430 new_b = FcStrCopyFilename(b);
1431 if (!new_b)
1432 {
1433 if (new_a)
1434 FcStrFree(new_a);
1435 return FcFalse;
1436 }
1437 }
1438 /* Override maps with new one if exists */
1439 if (FcStrSetMemberAB (set, new_a, new_b, &rs))
1440 {
1441 FcStrSetDel (set, rs);
1442 }
1443 ret = FcStrSetAddTriple (set, new_a, new_b, salt);
1444 if (new_a)
1445 FcStrFree (new_a);
1446 if (new_b)
1447 FcStrFree (new_b);
1448 return ret;
1449 }
1450
1451 FcBool
FcStrSetAddLangs(FcStrSet * strs,const char * languages)1452 FcStrSetAddLangs (FcStrSet *strs, const char *languages)
1453 {
1454 const char *p = languages, *next;
1455 FcChar8 lang[128] = {0}, *normalized_lang;
1456 size_t len;
1457 FcBool ret = FcFalse;
1458
1459 if (!languages)
1460 return FcFalse;
1461
1462 while ((next = strchr (p, ':')))
1463 {
1464 len = next - p;
1465 len = FC_MIN (len, 127);
1466 strncpy ((char *) lang, p, len);
1467 lang[len] = 0;
1468 /* ignore an empty item */
1469 if (*lang)
1470 {
1471 normalized_lang = FcLangNormalize ((const FcChar8 *) lang);
1472 if (normalized_lang)
1473 {
1474 FcStrSetAdd (strs, normalized_lang);
1475 FcStrFree (normalized_lang);
1476 ret = FcTrue;
1477 }
1478 }
1479 p = next + 1;
1480 }
1481 if (*p)
1482 {
1483 normalized_lang = FcLangNormalize ((const FcChar8 *) p);
1484 if (normalized_lang)
1485 {
1486 FcStrSetAdd (strs, normalized_lang);
1487 FcStrFree (normalized_lang);
1488 ret = FcTrue;
1489 }
1490 }
1491
1492 return ret;
1493 }
1494
1495 FcBool
FcStrSetDel(FcStrSet * set,const FcChar8 * s)1496 FcStrSetDel (FcStrSet *set, const FcChar8 *s)
1497 {
1498 int i;
1499
1500 for (i = 0; i < set->num; i++)
1501 if (!FcStrCmp (set->strs[i], s))
1502 {
1503 FcStrFree (set->strs[i]);
1504 /*
1505 * copy remaining string pointers and trailing
1506 * NULL
1507 */
1508 memmove (&set->strs[i], &set->strs[i+1],
1509 (set->num - i) * sizeof (FcChar8 *));
1510 set->num--;
1511 return FcTrue;
1512 }
1513 return FcFalse;
1514 }
1515
1516 FcBool
FcStrSetDeleteAll(FcStrSet * set)1517 FcStrSetDeleteAll (FcStrSet *set)
1518 {
1519 int i;
1520
1521 if (FcRefIsConst (&set->ref))
1522 return FcFalse;
1523
1524 for (i = set->num; i > 0; i--)
1525 {
1526 FcStrFree (set->strs[i - 1]);
1527 set->num--;
1528 }
1529 return FcTrue;
1530 }
1531
1532 /* TODO Make public */
1533 static FcStrSet *
FcStrSetReference(FcStrSet * set)1534 FcStrSetReference (FcStrSet *set)
1535 {
1536 if (FcRefIsConst (&set->ref))
1537 return set;
1538
1539 FcRefInc (&set->ref);
1540 return set;
1541 }
1542
1543 void
FcStrSetDestroy(FcStrSet * set)1544 FcStrSetDestroy (FcStrSet *set)
1545 {
1546 int i;
1547
1548 /* We rely on this in FcGetDefaultLangs for caching. */
1549 if (FcRefIsConst (&set->ref))
1550 return;
1551
1552 if (FcRefDec (&set->ref) != 1)
1553 return;
1554
1555 for (i = 0; i < set->num; i++)
1556 FcStrFree (set->strs[i]);
1557 if (set->strs)
1558 free (set->strs);
1559 free (set);
1560 }
1561
1562 FcStrList *
FcStrListCreate(FcStrSet * set)1563 FcStrListCreate (FcStrSet *set)
1564 {
1565 FcStrList *list;
1566
1567 list = malloc (sizeof (FcStrList));
1568 if (!list)
1569 return 0;
1570 list->set = set;
1571 FcStrSetReference (set);
1572 list->n = 0;
1573 return list;
1574 }
1575
1576 void
FcStrListFirst(FcStrList * list)1577 FcStrListFirst (FcStrList *list)
1578 {
1579 list->n = 0;
1580 }
1581
1582 FcChar8 *
FcStrListNext(FcStrList * list)1583 FcStrListNext (FcStrList *list)
1584 {
1585 if (list->n >= list->set->num)
1586 return 0;
1587 return list->set->strs[list->n++];
1588 }
1589
1590 void
FcStrListDone(FcStrList * list)1591 FcStrListDone (FcStrList *list)
1592 {
1593 FcStrSetDestroy (list->set);
1594 free (list);
1595 }
1596
1597 #define __fcstr__
1598 #include "fcaliastail.h"
1599 #undef __fcstr__
1600