57183fd7cb2917b3ec93f9d35a43873b0dc8078e
[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 <string.h>
26 #include "fcint.h"
27 #include "fcftint.h"
28
29 typedef struct {
30     const FcChar8       lang[8];
31     const FcCharSet     charset;
32 } FcLangCharSet;
33
34 typedef struct {
35     int begin;
36     int end;
37 } FcLangCharSetRange;
38
39 #include "../fc-lang/fclang.h"
40
41 struct _FcLangSet {
42     FcStrSet    *extra;
43     FcChar32    map_size;
44     FcChar32    map[NUM_LANG_SET_MAP];
45 };
46
47 static int FcLangSetIndex (const FcChar8 *lang);
48
49
50 static void
51 FcLangSetBitSet (FcLangSet    *ls,
52                  unsigned int  id)
53 {
54   unsigned int bucket;
55
56   id = fcLangCharSetIndices[id];
57   bucket = id >> 5;
58   if (bucket >= ls->map_size)
59     return; /* shouldn't happen really */
60
61   ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
62 }
63
64 static FcBool
65 FcLangSetBitGet (const FcLangSet *ls,
66                  unsigned int     id)
67 {
68   unsigned int bucket;
69
70   id = fcLangCharSetIndices[id];
71   bucket = id >> 5;
72   if (bucket >= ls->map_size)
73     return FcFalse;
74
75   return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
76 }
77
78 static void
79 FcLangSetBitReset (FcLangSet    *ls,
80                    unsigned int  id)
81 {
82   unsigned int bucket;
83
84   id = fcLangCharSetIndices[id];
85   bucket = id >> 5;
86   if (bucket >= ls->map_size)
87     return; /* shouldn't happen really */
88
89   ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
90 }
91
92 FcLangSet *
93 FcFreeTypeLangSet (const FcCharSet  *charset,
94                    const FcChar8    *exclusiveLang)
95 {
96     int             i, j;
97     FcChar32        missing;
98     const FcCharSet *exclusiveCharset = 0;
99     FcLangSet       *ls;
100
101     if (exclusiveLang)
102         exclusiveCharset = FcLangGetCharSet (exclusiveLang);
103     ls = FcLangSetCreate ();
104     if (!ls)
105         return 0;
106     if (FcDebug() & FC_DBG_LANGSET)
107     {
108         printf ("font charset");
109         FcCharSetPrint (charset);
110         printf ("\n");
111     }
112     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
113     {
114         if (FcDebug() & FC_DBG_LANGSET)
115         {
116             printf ("%s charset", fcLangCharSets[i].lang);
117             FcCharSetPrint (&fcLangCharSets[i].charset);
118             printf ("\n");
119         }
120         
121         /*
122          * Check for Han charsets to make fonts
123          * which advertise support for a single language
124          * not support other Han languages
125          */
126         if (exclusiveCharset &&
127             FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
128         {
129             if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
130                 continue;
131
132             for (j = 0; j < fcLangCharSets[i].charset.num; j++)
133                 if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
134                     FcCharSetLeaf(exclusiveCharset, j))
135                     continue;
136         }
137         missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
138         if (FcDebug() & FC_DBG_SCANV)
139         {
140             if (missing && missing < 10)
141             {
142                 FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
143                                                          charset);
144                 FcChar32    ucs4;
145                 FcChar32    map[FC_CHARSET_MAP_SIZE];
146                 FcChar32    next;
147
148                 printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
149                 printf ("{");
150                 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
151                      ucs4 != FC_CHARSET_DONE;
152                      ucs4 = FcCharSetNextPage (missed, map, &next))
153                 {
154                     int     i, j;
155                     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
156                         if (map[i])
157                         {
158                             for (j = 0; j < 32; j++)
159                                 if (map[i] & (1 << j))
160                                     printf (" %04x", ucs4 + i * 32 + j);
161                         }
162                 }
163                 printf (" }\n\t");
164                 FcCharSetDestroy (missed);
165             }
166             else
167                 printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
168         }
169         if (!missing)
170             FcLangSetBitSet (ls, i);
171     }
172
173     if (FcDebug() & FC_DBG_SCANV)
174         printf ("\n");
175
176
177     return ls;
178 }
179
180 FcChar8 *
181 FcLangNormalize (const FcChar8 *lang)
182 {
183     FcChar8 *result = NULL, *s, *orig;
184     char *territory, *encoding, *modifier;
185     size_t llen, tlen = 0, mlen = 0, ssize;
186
187     if (!lang || !*lang)
188         return NULL;
189
190     if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
191         FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
192     {
193         result = FcStrCopy ((const FcChar8 *)"en");
194         goto bail;
195     }
196
197     s = FcStrCopy (lang);
198     if (!s)
199         goto bail;
200     /* store the original length of 's' here to let FcMemFree know
201      * the correct size since we breaks 's' from now on.
202      */
203     ssize = strlen ((const char *)s) + 1;
204
205     /* from the comments in glibc:
206      *
207      * LOCALE can consist of up to four recognized parts for the XPG syntax:
208      *
209      *            language[_territory[.codeset]][@modifier]
210      *
211      * Beside the first all of them are allowed to be missing.  If the
212      * full specified locale is not found, the less specific one are
213      * looked for.  The various part will be stripped off according to
214      * the following order:
215      *            (1) codeset
216      *            (2) normalized codeset
217      *            (3) territory
218      *            (4) modifier
219      *
220      * So since we don't take care of the codeset part here, what patterns
221      * we need to deal with is:
222      *
223      *   1. language_territory@modifier
224      *   2. language@modifier
225      *   3. language
226      *
227      * then. and maybe no need to try language_territory here.
228      */
229     modifier = strchr ((const char *) s, '@');
230     if (modifier)
231     {
232         *modifier = 0;
233         modifier++;
234         mlen = strlen (modifier);
235     }
236     encoding = strchr ((const char *) s, '.');
237     if (encoding)
238     {
239         *encoding = 0;
240         encoding++;
241         if (modifier)
242         {
243             memmove (encoding, modifier, mlen + 1);
244             modifier = encoding;
245         }
246     }
247     territory = strchr ((const char *) s, '_');
248     if (!territory)
249         territory = strchr ((const char *) s, '-');
250     if (territory)
251     {
252         *territory = 0;
253         territory++;
254         tlen = strlen (territory);
255     }
256     llen = strlen ((const char *) s);
257     if (llen < 2 || llen > 3)
258     {
259         fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
260                  lang);
261         goto bail0;
262     }
263     if (territory && (tlen < 2 || tlen > 3))
264     {
265         fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
266                  lang);
267         goto bail0;
268     }
269     if (territory)
270         territory[-1] = '-';
271     if (modifier)
272         modifier[-1] = '@';
273     orig = FcStrDowncase (s);
274     if (!orig)
275         goto bail0;
276     if (territory)
277     {
278         if (FcDebug () & FC_DBG_LANGSET)
279             printf("Checking the existence of %s.orth\n", s);
280         if (FcLangSetIndex (s) < 0)
281         {
282             memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
283             if (modifier)
284                 modifier = territory;
285         }
286         else
287         {
288             result = s;
289             /* we'll miss the opportunity to reduce the correct size
290              * of the allocated memory for the string after that.
291              */
292             FcMemFree (FC_MEM_STRING, ssize);
293             FcMemAlloc (FC_MEM_STRING, strlen((const char *)s) + 1);
294             s = NULL;
295             goto bail1;
296         }
297     }
298     if (modifier)
299     {
300         if (FcDebug () & FC_DBG_LANGSET)
301             printf("Checking the existence of %s.orth\n", s);
302         if (FcLangSetIndex (s) < 0)
303             modifier[-1] = 0;
304         else
305         {
306             result = s;
307             /* we'll miss the opportunity to reduce the correct size
308              * of the allocated memory for the string after that.
309              */
310             FcMemFree (FC_MEM_STRING, ssize);
311             FcMemAlloc (FC_MEM_STRING, strlen((const char *)s) + 1);
312             s = NULL;
313             goto bail1;
314         }
315     }
316     if (FcDebug () & FC_DBG_LANGSET)
317         printf("Checking the existence of %s.orth\n", s);
318     if (FcLangSetIndex (s) < 0)
319     {
320         /* there seems no languages matched in orth.
321          * add the language as is for fallback.
322          */
323         result = orig;
324         orig = NULL;
325     }
326     else
327     {
328         result = s;
329         /* we'll miss the opportunity to reduce the correct size
330          * of the allocated memory for the string after that.
331          */
332         FcMemFree (FC_MEM_STRING, ssize);
333         FcMemAlloc (FC_MEM_STRING, strlen((const char *)s) + 1);
334         s = NULL;
335     }
336   bail1:
337     if (orig)
338         FcStrFree (orig);
339   bail0:
340     if (s)
341     {
342         free (s);
343         FcMemFree (FC_MEM_STRING, ssize);
344     }
345   bail:
346     if (FcDebug () & FC_DBG_LANGSET)
347     {
348         if (result)
349             printf ("normalized: %s -> %s\n", lang, result);
350         else
351             printf ("Unable to normalize %s\n", lang);
352     }
353
354     return result;
355 }
356
357 #define FcLangEnd(c)    ((c) == '-' || (c) == '\0')
358
359 FcLangResult
360 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
361 {
362     FcChar8         c1, c2;
363     FcLangResult    result = FcLangDifferentLang;
364
365     for (;;)
366     {
367         c1 = *s1++;
368         c2 = *s2++;
369         
370         c1 = FcToLower (c1);
371         c2 = FcToLower (c2);
372         if (c1 != c2)
373         {
374             if (FcLangEnd (c1) && FcLangEnd (c2))
375                 result = FcLangDifferentTerritory;
376             return result;
377         }
378         else if (!c1)
379             return FcLangEqual;
380         else if (c1 == '-')
381             result = FcLangDifferentTerritory;
382     }
383 }
384
385 /*
386  * Return FcTrue when super contains sub.
387  *
388  * super contains sub if super and sub have the same
389  * language and either the same country or one
390  * is missing the country
391  */
392
393 static FcBool
394 FcLangContains (const FcChar8 *super, const FcChar8 *sub)
395 {
396     FcChar8         c1, c2;
397
398     for (;;)
399     {
400         c1 = *super++;
401         c2 = *sub++;
402         
403         c1 = FcToLower (c1);
404         c2 = FcToLower (c2);
405         if (c1 != c2)
406         {
407             /* see if super has a country while sub is mising one */
408             if (c1 == '-' && c2 == '\0')
409                 return FcTrue;
410             /* see if sub has a country while super is mising one */
411             if (c1 == '\0' && c2 == '-')
412                 return FcTrue;
413             return FcFalse;
414         }
415         else if (!c1)
416             return FcTrue;
417     }
418 }
419
420 const FcCharSet *
421 FcLangGetCharSet (const FcChar8 *lang)
422 {
423     int         i;
424     int         country = -1;
425
426     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
427     {
428         switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
429         case FcLangEqual:
430             return &fcLangCharSets[i].charset;
431         case FcLangDifferentTerritory:
432             if (country == -1)
433                 country = i;
434         case FcLangDifferentLang:
435         default:
436             break;
437         }
438     }
439     if (country == -1)
440         return 0;
441     return &fcLangCharSets[country].charset;
442 }
443
444 FcStrSet *
445 FcGetLangs (void)
446 {
447     FcStrSet *langs;
448     int i;
449
450     langs = FcStrSetCreate();
451     if (!langs)
452         return 0;
453
454     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
455         FcStrSetAdd (langs, fcLangCharSets[i].lang);
456
457     return langs;
458 }
459
460 FcLangSet *
461 FcLangSetCreate (void)
462 {
463     FcLangSet   *ls;
464
465     ls = malloc (sizeof (FcLangSet));
466     if (!ls)
467         return 0;
468     FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
469     memset (ls->map, '\0', sizeof (ls->map));
470     ls->map_size = NUM_LANG_SET_MAP;
471     ls->extra = 0;
472     return ls;
473 }
474
475 void
476 FcLangSetDestroy (FcLangSet *ls)
477 {
478     if (ls->extra)
479         FcStrSetDestroy (ls->extra);
480     FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
481     free (ls);
482 }
483
484 FcLangSet *
485 FcLangSetCopy (const FcLangSet *ls)
486 {
487     FcLangSet   *new;
488
489     new = FcLangSetCreate ();
490     if (!new)
491         goto bail0;
492     memset (new->map, '\0', sizeof (new->map));
493     memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
494     if (ls->extra)
495     {
496         FcStrList       *list;
497         FcChar8         *extra;
498         
499         new->extra = FcStrSetCreate ();
500         if (!new->extra)
501             goto bail1;
502
503         list = FcStrListCreate (ls->extra);     
504         if (!list)
505             goto bail1;
506         
507         while ((extra = FcStrListNext (list)))
508             if (!FcStrSetAdd (new->extra, extra))
509             {
510                 FcStrListDone (list);
511                 goto bail1;
512             }
513         FcStrListDone (list);
514     }
515     return new;
516 bail1:
517     FcLangSetDestroy (new);
518 bail0:
519     return 0;
520 }
521
522 static int
523 FcLangSetIndex (const FcChar8 *lang)
524 {
525     int     low, high, mid = 0;
526     int     cmp = 0;
527     FcChar8 firstChar = FcToLower(lang[0]);
528     FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
529
530     if (firstChar < 'a')
531     {
532         low = 0;
533         high = fcLangCharSetRanges[0].begin;
534     }
535     else if(firstChar > 'z')
536     {
537         low = fcLangCharSetRanges[25].begin;
538         high = NUM_LANG_CHAR_SET - 1;
539     }
540     else
541     {
542         low = fcLangCharSetRanges[firstChar - 'a'].begin;
543         high = fcLangCharSetRanges[firstChar - 'a'].end;
544         /* no matches */
545         if (low > high)
546             return -low; /* next entry after where it would be */
547     }
548
549     while (low <= high)
550     {
551         mid = (high + low) >> 1;
552         if(fcLangCharSets[mid].lang[0] != firstChar)
553             cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
554         else
555         {   /* fast path for resolving 2-letter languages (by far the most common) after
556              * finding the first char (probably already true because of the hash table) */
557             cmp = fcLangCharSets[mid].lang[1] - secondChar;
558             if (cmp == 0 &&
559                 (fcLangCharSets[mid].lang[2] != '\0' ||
560                  lang[2] != '\0'))
561             {
562                 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
563                                          lang+2);
564             }
565         }
566         if (cmp == 0)
567             return mid;
568         if (cmp < 0)
569             low = mid + 1;
570         else
571             high = mid - 1;
572     }
573     if (cmp < 0)
574         mid++;
575     return -(mid + 1);
576 }
577
578 FcBool
579 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
580 {
581     int     id;
582
583     id = FcLangSetIndex (lang);
584     if (id >= 0)
585     {
586         FcLangSetBitSet (ls, id);
587         return FcTrue;
588     }
589     if (!ls->extra)
590     {
591         ls->extra = FcStrSetCreate ();
592         if (!ls->extra)
593             return FcFalse;
594     }
595     return FcStrSetAdd (ls->extra, lang);
596 }
597
598 FcBool
599 FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
600 {
601     int id;
602
603     id = FcLangSetIndex (lang);
604     if (id >= 0)
605     {
606         FcLangSetBitReset (ls, id);
607     }
608     else if (ls->extra)
609     {
610         FcStrSetDel (ls->extra, lang);
611     }
612     return FcTrue;
613 }
614
615 FcLangResult
616 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
617 {
618     int             id;
619     FcLangResult    best, r;
620     int             i;
621
622     id = FcLangSetIndex (lang);
623     if (id < 0)
624         id = -id - 1;
625     else if (FcLangSetBitGet (ls, id))
626         return FcLangEqual;
627     best = FcLangDifferentLang;
628     for (i = id - 1; i >= 0; i--)
629     {
630         r = FcLangCompare (lang, fcLangCharSets[i].lang);
631         if (r == FcLangDifferentLang)
632             break;
633         if (FcLangSetBitGet (ls, i) && r < best)
634             best = r;
635     }
636     for (i = id; i < NUM_LANG_CHAR_SET; i++)
637     {
638         r = FcLangCompare (lang, fcLangCharSets[i].lang);
639         if (r == FcLangDifferentLang)
640             break;
641         if (FcLangSetBitGet (ls, i) && r < best)
642             best = r;
643     }
644     if (ls->extra)
645     {
646         FcStrList       *list = FcStrListCreate (ls->extra);
647         FcChar8         *extra;
648         
649         if (list)
650         {
651             while (best > FcLangEqual && (extra = FcStrListNext (list)))
652             {
653                 r = FcLangCompare (lang, extra);
654                 if (r < best)
655                     best = r;
656             }
657             FcStrListDone (list);
658         }
659     }
660     return best;
661 }
662
663 static FcLangResult
664 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
665 {
666     FcStrList       *list = FcStrListCreate (set);
667     FcLangResult    r, best = FcLangDifferentLang;
668     FcChar8         *extra;
669
670     if (list)
671     {
672         while (best > FcLangEqual && (extra = FcStrListNext (list)))
673         {
674             r = FcLangSetHasLang (ls, extra);
675             if (r < best)
676                 best = r;
677         }
678         FcStrListDone (list);
679     }
680     return best;
681 }
682
683 FcLangResult
684 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
685 {
686     int             i, j, count;
687     FcLangResult    best, r;
688
689     count = FC_MIN (lsa->map_size, lsb->map_size);
690     count = FC_MIN (NUM_LANG_SET_MAP, count);
691     for (i = 0; i < count; i++)
692         if (lsa->map[i] & lsb->map[i])
693             return FcLangEqual;
694     best = FcLangDifferentLang;
695     for (j = 0; j < NUM_COUNTRY_SET; j++)
696         for (i = 0; i < count; i++)
697             if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
698                 (lsb->map[i] & fcLangCountrySets[j][i]))
699             {
700                 best = FcLangDifferentTerritory;
701                 break;
702             }
703     if (lsa->extra)
704     {
705         r = FcLangSetCompareStrSet (lsb, lsa->extra);
706         if (r < best)
707             best = r;
708     }
709     if (best > FcLangEqual && lsb->extra)
710     {
711         r = FcLangSetCompareStrSet (lsa, lsb->extra);
712         if (r < best)
713             best = r;
714     }
715     return best;
716 }
717
718 /*
719  * Used in computing values -- mustn't allocate any storage
720  * XXX Not thread-safe
721  */
722 FcLangSet *
723 FcLangSetPromote (const FcChar8 *lang)
724 {
725     static FcLangSet    ls;
726     static FcStrSet     strs;
727     static FcChar8      *str;
728     int                 id;
729
730     memset (ls.map, '\0', sizeof (ls.map));
731     ls.map_size = NUM_LANG_SET_MAP;
732     ls.extra = 0;
733     id = FcLangSetIndex (lang);
734     if (id > 0)
735     {
736         FcLangSetBitSet (&ls, id);
737     }
738     else
739     {
740         ls.extra = &strs;
741         strs.num = 1;
742         strs.size = 1;
743         strs.strs = &str;
744         strs.ref = 1;
745         str = (FcChar8 *) lang;
746     }
747     return &ls;
748 }
749
750 FcChar32
751 FcLangSetHash (const FcLangSet *ls)
752 {
753     FcChar32    h = 0;
754     int         i, count;
755
756     count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
757     for (i = 0; i < count; i++)
758         h ^= ls->map[i];
759     if (ls->extra)
760         h ^= ls->extra->num;
761     return h;
762 }
763
764 FcLangSet *
765 FcNameParseLangSet (const FcChar8 *string)
766 {
767     FcChar8         lang[32], c = 0;
768     int i;
769     FcLangSet       *ls;
770
771     ls = FcLangSetCreate ();
772     if (!ls)
773         goto bail0;
774
775     for(;;)
776     {
777         for(i = 0; i < 31;i++)
778         {
779             c = *string++;
780             if(c == '\0' || c == '|')
781                 break; /* end of this code */
782             lang[i] = c;
783         }
784         lang[i] = '\0';
785         if (!FcLangSetAdd (ls, lang))
786             goto bail1;
787         if(c == '\0')
788             break;
789     }
790     return ls;
791 bail1:
792     FcLangSetDestroy (ls);
793 bail0:
794     return 0;
795 }
796
797 FcBool
798 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
799 {
800     int         i, bit, count;
801     FcChar32    bits;
802     FcBool      first = FcTrue;
803
804     count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
805     for (i = 0; i < count; i++)
806     {
807         if ((bits = ls->map[i]))
808         {
809             for (bit = 0; bit <= 31; bit++)
810                 if (bits & (1 << bit))
811                 {
812                     int id = (i << 5) | bit;
813                     if (!first)
814                         if (!FcStrBufChar (buf, '|'))
815                             return FcFalse;
816                     if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
817                         return FcFalse;
818                     first = FcFalse;
819                 }
820         }
821     }
822     if (ls->extra)
823     {
824         FcStrList   *list = FcStrListCreate (ls->extra);
825         FcChar8     *extra;
826
827         if (!list)
828             return FcFalse;
829         while ((extra = FcStrListNext (list)))
830         {
831             if (!first)
832                 if (!FcStrBufChar (buf, '|'))
833                 {
834                     FcStrListDone (list);
835                     return FcFalse;
836                 }
837             if (!FcStrBufString (buf, extra))
838                 {
839                     FcStrListDone (list);
840                     return FcFalse;
841                 }
842             first = FcFalse;
843         }
844         FcStrListDone (list);
845     }
846     return FcTrue;
847 }
848
849 FcBool
850 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
851 {
852     int     i, count;
853
854     count = FC_MIN (lsa->map_size, lsb->map_size);
855     count = FC_MIN (NUM_LANG_SET_MAP, count);
856     for (i = 0; i < count; i++)
857     {
858         if (lsa->map[i] != lsb->map[i])
859             return FcFalse;
860     }
861     if (!lsa->extra && !lsb->extra)
862         return FcTrue;
863     if (lsa->extra && lsb->extra)
864         return FcStrSetEqual (lsa->extra, lsb->extra);
865     return FcFalse;
866 }
867
868 static FcBool
869 FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
870 {
871     int             id;
872     int             i;
873
874     id = FcLangSetIndex (lang);
875     if (id < 0)
876         id = -id - 1;
877     else if (FcLangSetBitGet (ls, id))
878         return FcTrue;
879     /*
880      * search up and down among equal languages for a match
881      */
882     for (i = id - 1; i >= 0; i--)
883     {
884         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
885             break;
886         if (FcLangSetBitGet (ls, i) &&
887             FcLangContains (fcLangCharSets[i].lang, lang))
888             return FcTrue;
889     }
890     for (i = id; i < NUM_LANG_CHAR_SET; i++)
891     {
892         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
893             break;
894         if (FcLangSetBitGet (ls, i) &&
895             FcLangContains (fcLangCharSets[i].lang, lang))
896             return FcTrue;
897     }
898     if (ls->extra)
899     {
900         FcStrList       *list = FcStrListCreate (ls->extra);
901         FcChar8         *extra;
902         
903         if (list)
904         {
905             while ((extra = FcStrListNext (list)))
906             {
907                 if (FcLangContains (extra, lang))
908                     break;
909             }
910             FcStrListDone (list);
911             if (extra)
912                 return FcTrue;
913         }
914     }
915     return FcFalse;
916 }
917
918 /*
919  * return FcTrue if lsa contains every language in lsb
920  */
921 FcBool
922 FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
923 {
924     int             i, j, count;
925     FcChar32        missing;
926
927     if (FcDebug() & FC_DBG_MATCHV)
928     {
929         printf ("FcLangSet "); FcLangSetPrint (lsa);
930         printf (" contains "); FcLangSetPrint (lsb);
931         printf ("\n");
932     }
933     /*
934      * check bitmaps for missing language support
935      */
936     count = FC_MIN (lsa->map_size, lsb->map_size);
937     count = FC_MIN (NUM_LANG_SET_MAP, count);
938     for (i = 0; i < count; i++)
939     {
940         missing = lsb->map[i] & ~lsa->map[i];
941         if (missing)
942         {
943             for (j = 0; j < 32; j++)
944                 if (missing & (1 << j))
945                 {
946                     if (!FcLangSetContainsLang (lsa,
947                                                 fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
948                     {
949                         if (FcDebug() & FC_DBG_MATCHV)
950                             printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
951                         return FcFalse;
952                     }
953                 }
954         }
955     }
956     if (lsb->extra)
957     {
958         FcStrList   *list = FcStrListCreate (lsb->extra);
959         FcChar8     *extra;
960
961         if (list)
962         {
963             while ((extra = FcStrListNext (list)))
964             {
965                 if (!FcLangSetContainsLang (lsa, extra))
966                 {
967                     if (FcDebug() & FC_DBG_MATCHV)
968                         printf ("\tMissing string %s\n", extra);
969                     break;
970                 }
971             }
972             FcStrListDone (list);
973             if (extra)
974                 return FcFalse;
975         }
976     }
977     return FcTrue;
978 }
979
980 FcBool
981 FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
982 {
983     if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
984         return FcFalse;
985     return FcTrue;
986 }
987
988 FcLangSet *
989 FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
990 {
991     FcLangSet   *l_serialize = FcSerializePtr (serialize, l);
992
993     if (!l_serialize)
994         return NULL;
995     memset (l_serialize->map, '\0', sizeof (l_serialize->map));
996     memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
997     l_serialize->map_size = NUM_LANG_SET_MAP;
998     l_serialize->extra = NULL; /* We don't serialize ls->extra */
999     return l_serialize;
1000 }
1001
1002 FcStrSet *
1003 FcLangSetGetLangs (const FcLangSet *ls)
1004 {
1005     FcStrSet *langs;
1006     int       i;
1007
1008     langs = FcStrSetCreate();
1009     if (!langs)
1010         return 0;
1011
1012     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
1013         if (FcLangSetBitGet (ls, i))
1014             FcStrSetAdd (langs, fcLangCharSets[i].lang);
1015
1016     if (ls->extra)
1017     {
1018         FcStrList       *list = FcStrListCreate (ls->extra);
1019         FcChar8         *extra;
1020
1021         if (list)
1022         {
1023             while ((extra = FcStrListNext (list)))
1024                 FcStrSetAdd (langs, extra);
1025
1026             FcStrListDone (list);
1027         }
1028     }
1029
1030     return langs;
1031 }
1032
1033 static FcLangSet *
1034 FcLangSetOperate(const FcLangSet        *a,
1035                  const FcLangSet        *b,
1036                  FcBool                 (*func) (FcLangSet      *ls,
1037                                                  const FcChar8  *s))
1038 {
1039     FcLangSet   *langset = FcLangSetCopy (a);
1040     FcStrList   *sl = FcStrListCreate (FcLangSetGetLangs (b));
1041     FcChar8     *str;
1042
1043     while ((str = FcStrListNext (sl)))
1044     {
1045         func (langset, str);
1046     }
1047     FcStrListDone (sl);
1048
1049     return langset;
1050 }
1051
1052 FcLangSet *
1053 FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
1054 {
1055     return FcLangSetOperate(a, b, FcLangSetAdd);
1056 }
1057
1058 FcLangSet *
1059 FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
1060 {
1061     return FcLangSetOperate(a, b, FcLangSetDel);
1062 }
1063
1064 #define __fclang__
1065 #include "fcaliastail.h"
1066 #include "fcftaliastail.h"
1067 #undef __fclang__