Coverage Report

Created: 2025-07-23 08:13

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