Cleanup copyright notices to replace "Keith Packard" with "the author(s)"
[platform/upstream/fontconfig.git] / src / fclang.c
1 /*
2  * fontconfig/src/fclang.c
3  *
4  * Copyright © 2002 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 "fcftint.h"
27
28 typedef struct {
29     const FcChar8       lang[8];
30     const FcCharSet     charset;
31 } FcLangCharSet;
32
33 typedef struct {
34     int begin;
35     int end;
36 } FcLangCharSetRange;
37
38 #include "../fc-lang/fclang.h"
39
40 struct _FcLangSet {
41     FcStrSet    *extra;
42     FcChar32    map_size;
43     FcChar32    map[NUM_LANG_SET_MAP];
44 };
45
46 static void
47 FcLangSetBitSet (FcLangSet    *ls,
48                  unsigned int  id)
49 {
50   int bucket;
51
52   id = fcLangCharSetIndices[id];
53   bucket = id >> 5;
54   if (bucket >= ls->map_size)
55     return; /* shouldn't happen really */
56
57   ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
58 }
59
60 static FcBool
61 FcLangSetBitGet (const FcLangSet *ls,
62                  unsigned int     id)
63 {
64   int bucket;
65
66   id = fcLangCharSetIndices[id];
67   bucket = id >> 5;
68   if (bucket >= ls->map_size)
69     return FcFalse;
70
71   return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
72 }
73
74 FcLangSet *
75 FcFreeTypeLangSet (const FcCharSet  *charset,
76                    const FcChar8    *exclusiveLang)
77 {
78     int             i, j;
79     FcChar32        missing;
80     const FcCharSet *exclusiveCharset = 0;
81     FcLangSet       *ls;
82
83     if (exclusiveLang)
84         exclusiveCharset = FcLangGetCharSet (exclusiveLang);
85     ls = FcLangSetCreate ();
86     if (!ls)
87         return 0;
88     if (FcDebug() & FC_DBG_LANGSET)
89     {
90         printf ("font charset");
91         FcCharSetPrint (charset);
92         printf ("\n");
93     }
94     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
95     {
96         if (FcDebug() & FC_DBG_LANGSET)
97         {
98             printf ("%s charset", fcLangCharSets[i].lang);
99             FcCharSetPrint (&fcLangCharSets[i].charset);
100             printf ("\n");
101         }
102         
103         /*
104          * Check for Han charsets to make fonts
105          * which advertise support for a single language
106          * not support other Han languages
107          */
108         if (exclusiveCharset &&
109             FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
110         {
111             if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
112                 continue;
113
114             for (j = 0; j < fcLangCharSets[i].charset.num; j++)
115                 if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
116                     FcCharSetLeaf(exclusiveCharset, j))
117                     continue;
118         }
119         missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
120         if (FcDebug() & FC_DBG_SCANV)
121         {
122             if (missing && missing < 10)
123             {
124                 FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
125                                                          charset);
126                 FcChar32    ucs4;
127                 FcChar32    map[FC_CHARSET_MAP_SIZE];
128                 FcChar32    next;
129
130                 printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
131                 printf ("{");
132                 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
133                      ucs4 != FC_CHARSET_DONE;
134                      ucs4 = FcCharSetNextPage (missed, map, &next))
135                 {
136                     int     i, j;
137                     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
138                         if (map[i])
139                         {
140                             for (j = 0; j < 32; j++)
141                                 if (map[i] & (1 << j))
142                                     printf (" %04x", ucs4 + i * 32 + j);
143                         }
144                 }
145                 printf (" }\n\t");
146                 FcCharSetDestroy (missed);
147             }
148             else
149                 printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
150         }
151         if (!missing)
152             FcLangSetBitSet (ls, i);
153     }
154
155     if (FcDebug() & FC_DBG_SCANV)
156         printf ("\n");
157
158
159     return ls;
160 }
161
162 #define FcLangEnd(c)    ((c) == '-' || (c) == '\0')
163
164 FcLangResult
165 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
166 {
167     FcChar8         c1, c2;
168     FcLangResult    result = FcLangDifferentLang;
169
170     for (;;)
171     {
172         c1 = *s1++;
173         c2 = *s2++;
174         
175         c1 = FcToLower (c1);
176         c2 = FcToLower (c2);
177         if (c1 != c2)
178         {
179             if (FcLangEnd (c1) && FcLangEnd (c2))
180                 result = FcLangDifferentTerritory;
181             return result;
182         }
183         else if (!c1)
184             return FcLangEqual;
185         else if (c1 == '-')
186             result = FcLangDifferentTerritory;
187     }
188 }
189
190 /*
191  * Return FcTrue when super contains sub.
192  *
193  * super contains sub if super and sub have the same
194  * language and either the same country or one
195  * is missing the country
196  */
197
198 static FcBool
199 FcLangContains (const FcChar8 *super, const FcChar8 *sub)
200 {
201     FcChar8         c1, c2;
202
203     for (;;)
204     {
205         c1 = *super++;
206         c2 = *sub++;
207         
208         c1 = FcToLower (c1);
209         c2 = FcToLower (c2);
210         if (c1 != c2)
211         {
212             /* see if super has a country while sub is mising one */
213             if (c1 == '-' && c2 == '\0')
214                 return FcTrue;
215             /* see if sub has a country while super is mising one */
216             if (c1 == '\0' && c2 == '-')
217                 return FcTrue;
218             return FcFalse;
219         }
220         else if (!c1)
221             return FcTrue;
222     }
223 }
224
225 const FcCharSet *
226 FcLangGetCharSet (const FcChar8 *lang)
227 {
228     int         i;
229     int         country = -1;
230
231     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
232     {
233         switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
234         case FcLangEqual:
235             return &fcLangCharSets[i].charset;
236         case FcLangDifferentTerritory:
237             if (country == -1)
238                 country = i;
239         case FcLangDifferentLang:
240         default:
241             break;
242         }
243     }
244     if (country == -1)
245         return 0;
246     return &fcLangCharSets[country].charset;
247 }
248
249 FcStrSet *
250 FcGetLangs (void)
251 {
252     FcStrSet *langs;
253     int i;
254
255     langs = FcStrSetCreate();
256     if (!langs)
257         return 0;
258
259     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
260         FcStrSetAdd (langs, fcLangCharSets[i].lang);
261
262     return langs;
263 }
264
265 FcLangSet *
266 FcLangSetCreate (void)
267 {
268     FcLangSet   *ls;
269
270     ls = malloc (sizeof (FcLangSet));
271     if (!ls)
272         return 0;
273     FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
274     memset (ls->map, '\0', sizeof (ls->map));
275     ls->map_size = NUM_LANG_SET_MAP;
276     ls->extra = 0;
277     return ls;
278 }
279
280 void
281 FcLangSetDestroy (FcLangSet *ls)
282 {
283     if (ls->extra)
284         FcStrSetDestroy (ls->extra);
285     FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
286     free (ls);
287 }
288
289 FcLangSet *
290 FcLangSetCopy (const FcLangSet *ls)
291 {
292     FcLangSet   *new;
293
294     new = FcLangSetCreate ();
295     if (!new)
296         goto bail0;
297     memset (new->map, '\0', sizeof (new->map));
298     memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
299     if (ls->extra)
300     {
301         FcStrList       *list;
302         FcChar8         *extra;
303         
304         new->extra = FcStrSetCreate ();
305         if (!new->extra)
306             goto bail1;
307
308         list = FcStrListCreate (ls->extra);     
309         if (!list)
310             goto bail1;
311         
312         while ((extra = FcStrListNext (list)))
313             if (!FcStrSetAdd (new->extra, extra))
314             {
315                 FcStrListDone (list);
316                 goto bail1;
317             }
318         FcStrListDone (list);
319     }
320     return new;
321 bail1:
322     FcLangSetDestroy (new);
323 bail0:
324     return 0;
325 }
326
327 static int
328 FcLangSetIndex (const FcChar8 *lang)
329 {
330     int     low, high, mid = 0;
331     int     cmp = 0;
332     FcChar8 firstChar = FcToLower(lang[0]);
333     FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
334
335     if (firstChar < 'a')
336     {
337         low = 0;
338         high = fcLangCharSetRanges[0].begin;
339     }
340     else if(firstChar > 'z')
341     {
342         low = fcLangCharSetRanges[25].begin;
343         high = NUM_LANG_CHAR_SET - 1;
344     }
345     else
346     {
347         low = fcLangCharSetRanges[firstChar - 'a'].begin;
348         high = fcLangCharSetRanges[firstChar - 'a'].end;
349         /* no matches */
350         if (low > high)
351             return -low; /* next entry after where it would be */
352     }
353
354     while (low <= high)
355     {
356         mid = (high + low) >> 1;
357         if(fcLangCharSets[mid].lang[0] != firstChar)
358             cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
359         else
360         {   /* fast path for resolving 2-letter languages (by far the most common) after
361              * finding the first char (probably already true because of the hash table) */
362             cmp = fcLangCharSets[mid].lang[1] - secondChar;
363             if (cmp == 0 &&
364                 (fcLangCharSets[mid].lang[2] != '\0' ||
365                  lang[2] != '\0'))
366             {
367                 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
368                                          lang+2);
369             }
370         }
371         if (cmp == 0)
372             return mid;
373         if (cmp < 0)
374             low = mid + 1;
375         else
376             high = mid - 1;
377     }
378     if (cmp < 0)
379         mid++;
380     return -(mid + 1);
381 }
382
383 FcBool
384 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
385 {
386     int     id;
387
388     id = FcLangSetIndex (lang);
389     if (id >= 0)
390     {
391         FcLangSetBitSet (ls, id);
392         return FcTrue;
393     }
394     if (!ls->extra)
395     {
396         ls->extra = FcStrSetCreate ();
397         if (!ls->extra)
398             return FcFalse;
399     }
400     return FcStrSetAdd (ls->extra, lang);
401 }
402
403 FcLangResult
404 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
405 {
406     int             id;
407     FcLangResult    best, r;
408     int             i;
409
410     id = FcLangSetIndex (lang);
411     if (id < 0)
412         id = -id - 1;
413     else if (FcLangSetBitGet (ls, id))
414         return FcLangEqual;
415     best = FcLangDifferentLang;
416     for (i = id - 1; i >= 0; i--)
417     {
418         r = FcLangCompare (lang, fcLangCharSets[i].lang);
419         if (r == FcLangDifferentLang)
420             break;
421         if (FcLangSetBitGet (ls, i) && r < best)
422             best = r;
423     }
424     for (i = id; i < NUM_LANG_CHAR_SET; i++)
425     {
426         r = FcLangCompare (lang, fcLangCharSets[i].lang);
427         if (r == FcLangDifferentLang)
428             break;
429         if (FcLangSetBitGet (ls, i) && r < best)
430             best = r;
431     }
432     if (ls->extra)
433     {
434         FcStrList       *list = FcStrListCreate (ls->extra);
435         FcChar8         *extra;
436         
437         if (list)
438         {
439             while (best > FcLangEqual && (extra = FcStrListNext (list)))
440             {
441                 r = FcLangCompare (lang, extra);
442                 if (r < best)
443                     best = r;
444             }
445             FcStrListDone (list);
446         }
447     }
448     return best;
449 }
450
451 static FcLangResult
452 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
453 {
454     FcStrList       *list = FcStrListCreate (set);
455     FcLangResult    r, best = FcLangDifferentLang;
456     FcChar8         *extra;
457
458     if (list)
459     {
460         while (best > FcLangEqual && (extra = FcStrListNext (list)))
461         {
462             r = FcLangSetHasLang (ls, extra);
463             if (r < best)
464                 best = r;
465         }
466         FcStrListDone (list);
467     }
468     return best;
469 }
470
471 FcLangResult
472 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
473 {
474     int             i, j, count;
475     FcLangResult    best, r;
476
477     count = FC_MIN (lsa->map_size, lsb->map_size);
478     count = FC_MIN (NUM_LANG_SET_MAP, count);
479     for (i = 0; i < count; i++)
480         if (lsa->map[i] & lsb->map[i])
481             return FcLangEqual;
482     best = FcLangDifferentLang;
483     for (j = 0; j < NUM_COUNTRY_SET; j++)
484         for (i = 0; i < count; i++)
485             if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
486                 (lsb->map[i] & fcLangCountrySets[j][i]))
487             {
488                 best = FcLangDifferentTerritory;
489                 break;
490             }
491     if (lsa->extra)
492     {
493         r = FcLangSetCompareStrSet (lsb, lsa->extra);
494         if (r < best)
495             best = r;
496     }
497     if (best > FcLangEqual && lsb->extra)
498     {
499         r = FcLangSetCompareStrSet (lsa, lsb->extra);
500         if (r < best)
501             best = r;
502     }
503     return best;
504 }
505
506 /*
507  * Used in computing values -- mustn't allocate any storage
508  */
509 FcLangSet *
510 FcLangSetPromote (const FcChar8 *lang)
511 {
512     static FcLangSet    ls;
513     static FcStrSet     strs;
514     static FcChar8      *str;
515     int                 id;
516
517     memset (ls.map, '\0', sizeof (ls.map));
518     ls.extra = 0;
519     id = FcLangSetIndex (lang);
520     if (id > 0)
521     {
522         FcLangSetBitSet (&ls, id);
523     }
524     else
525     {
526         ls.extra = &strs;
527         strs.num = 1;
528         strs.size = 1;
529         strs.strs = &str;
530         strs.ref = 1;
531         str = (FcChar8 *) lang;
532     }
533     return &ls;
534 }
535
536 FcChar32
537 FcLangSetHash (const FcLangSet *ls)
538 {
539     FcChar32    h = 0;
540     int         i, count;
541
542     count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
543     for (i = 0; i < count; i++)
544         h ^= ls->map[i];
545     if (ls->extra)
546         h ^= ls->extra->num;
547     return h;
548 }
549
550 FcLangSet *
551 FcNameParseLangSet (const FcChar8 *string)
552 {
553     FcChar8         lang[32], c = 0;
554     int i;
555     FcLangSet       *ls;
556
557     ls = FcLangSetCreate ();
558     if (!ls)
559         goto bail0;
560
561     for(;;)
562     {
563         for(i = 0; i < 31;i++)
564         {
565             c = *string++;
566             if(c == '\0' || c == '|')
567                 break; /* end of this code */
568             lang[i] = c;
569         }
570         lang[i] = '\0';
571         if (!FcLangSetAdd (ls, lang))
572             goto bail1;
573         if(c == '\0')
574             break;
575     }
576     return ls;
577 bail1:
578     FcLangSetDestroy (ls);
579 bail0:
580     return 0;
581 }
582
583 FcBool
584 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
585 {
586     int         i, bit, count;
587     FcChar32    bits;
588     FcBool      first = FcTrue;
589
590     count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
591     for (i = 0; i < count; i++)
592     {
593         if ((bits = ls->map[i]))
594         {
595             for (bit = 0; bit <= 31; bit++)
596                 if (bits & (1 << bit))
597                 {
598                     int id = (i << 5) | bit;
599                     if (!first)
600                         if (!FcStrBufChar (buf, '|'))
601                             return FcFalse;
602                     if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
603                         return FcFalse;
604                     first = FcFalse;
605                 }
606         }
607     }
608     if (ls->extra)
609     {
610         FcStrList   *list = FcStrListCreate (ls->extra);
611         FcChar8     *extra;
612
613         if (!list)
614             return FcFalse;
615         while ((extra = FcStrListNext (list)))
616         {
617             if (!first)
618                 if (!FcStrBufChar (buf, '|'))
619                 {
620                     FcStrListDone (list);
621                     return FcFalse;
622                 }
623             if (!FcStrBufString (buf, extra))
624                 {
625                     FcStrListDone (list);
626                     return FcFalse;
627                 }
628             first = FcFalse;
629         }
630         FcStrListDone (list);
631     }
632     return FcTrue;
633 }
634
635 FcBool
636 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
637 {
638     int     i, count;
639
640     count = FC_MIN (lsa->map_size, lsb->map_size);
641     count = FC_MIN (NUM_LANG_SET_MAP, count);
642     for (i = 0; i < count; i++)
643     {
644         if (lsa->map[i] != lsb->map[i])
645             return FcFalse;
646     }
647     if (!lsa->extra && !lsb->extra)
648         return FcTrue;
649     if (lsa->extra && lsb->extra)
650         return FcStrSetEqual (lsa->extra, lsb->extra);
651     return FcFalse;
652 }
653
654 static FcBool
655 FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
656 {
657     int             id;
658     int             i;
659
660     id = FcLangSetIndex (lang);
661     if (id < 0)
662         id = -id - 1;
663     else if (FcLangSetBitGet (ls, id))
664         return FcTrue;
665     /*
666      * search up and down among equal languages for a match
667      */
668     for (i = id - 1; i >= 0; i--)
669     {
670         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
671             break;
672         if (FcLangSetBitGet (ls, i) &&
673             FcLangContains (fcLangCharSets[i].lang, lang))
674             return FcTrue;
675     }
676     for (i = id; i < NUM_LANG_CHAR_SET; i++)
677     {
678         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
679             break;
680         if (FcLangSetBitGet (ls, i) &&
681             FcLangContains (fcLangCharSets[i].lang, lang))
682             return FcTrue;
683     }
684     if (ls->extra)
685     {
686         FcStrList       *list = FcStrListCreate (ls->extra);
687         FcChar8         *extra;
688         
689         if (list)
690         {
691             while ((extra = FcStrListNext (list)))
692             {
693                 if (FcLangContains (extra, lang))
694                     break;
695             }
696             FcStrListDone (list);
697             if (extra)
698                 return FcTrue;
699         }
700     }
701     return FcFalse;
702 }
703
704 /*
705  * return FcTrue if lsa contains every language in lsb
706  */
707 FcBool
708 FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
709 {
710     int             i, j, count;
711     FcChar32        missing;
712
713     if (FcDebug() & FC_DBG_MATCHV)
714     {
715         printf ("FcLangSet "); FcLangSetPrint (lsa);
716         printf (" contains "); FcLangSetPrint (lsb);
717         printf ("\n");
718     }
719     /*
720      * check bitmaps for missing language support
721      */
722     count = FC_MIN (lsa->map_size, lsb->map_size);
723     count = FC_MIN (NUM_LANG_SET_MAP, count);
724     for (i = 0; i < count; i++)
725     {
726         missing = lsb->map[i] & ~lsa->map[i];
727         if (missing)
728         {
729             for (j = 0; j < 32; j++)
730                 if (missing & (1 << j))
731                 {
732                     if (!FcLangSetContainsLang (lsa,
733                                                 fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
734                     {
735                         if (FcDebug() & FC_DBG_MATCHV)
736                             printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
737                         return FcFalse;
738                     }
739                 }
740         }
741     }
742     if (lsb->extra)
743     {
744         FcStrList   *list = FcStrListCreate (lsb->extra);
745         FcChar8     *extra;
746
747         if (list)
748         {
749             while ((extra = FcStrListNext (list)))
750             {
751                 if (!FcLangSetContainsLang (lsa, extra))
752                 {
753                     if (FcDebug() & FC_DBG_MATCHV)
754                         printf ("\tMissing string %s\n", extra);
755                     break;
756                 }
757             }
758             FcStrListDone (list);
759             if (extra)
760                 return FcFalse;
761         }
762     }
763     return FcTrue;
764 }
765
766 FcBool
767 FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
768 {
769     if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
770         return FcFalse;
771     return FcTrue;
772 }
773
774 FcLangSet *
775 FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
776 {
777     FcLangSet   *l_serialize = FcSerializePtr (serialize, l);
778
779     if (!l_serialize)
780         return NULL;
781     memset (l_serialize->map, '\0', sizeof (l_serialize->map));
782     memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
783     l_serialize->map_size = NUM_LANG_SET_MAP;
784     l_serialize->extra = NULL; /* We don't serialize ls->extra */
785     return l_serialize;
786 }
787
788 FcStrSet *
789 FcLangSetGetLangs (const FcLangSet *ls)
790 {
791     FcStrSet *langs;
792     int       i;
793
794     langs = FcStrSetCreate();
795     if (!langs)
796         return 0;
797
798     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
799         if (FcLangSetBitGet (ls, i))
800             FcStrSetAdd (langs, fcLangCharSets[i].lang);
801
802     if (ls->extra)
803     {
804         FcStrList       *list = FcStrListCreate (ls->extra);
805         FcChar8         *extra;
806
807         if (list)
808         {
809             while ((extra = FcStrListNext (list)))
810                 FcStrSetAdd (langs, extra);
811
812             FcStrListDone (list);
813         }
814     }
815
816     return langs;
817 }
818
819 #define __fclang__
820 #include "fcaliastail.h"
821 #include "fcftaliastail.h"
822 #undef __fclang__