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