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