(root)/
fontconfig-2.14.2/
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[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__