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