Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/cmp-i-unicode-casemap.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
/* Comparator 'i;unicode-casemap':
4
 *
5
 */
6
7
#include "lib.h"
8
#include "unichar.h"
9
10
#include "sieve-common.h"
11
#include "sieve-comparators.h"
12
13
/*
14
 * Comparator implementation
15
 */
16
17
static int
18
cmp_i_unicode_casemap_compare(const struct sieve_comparator *cmp ATTR_UNUSED,
19
            const char *val1, size_t val1_size, const char *val2,
20
            size_t val2_size)
21
0
{
22
0
  string_t *value_a = t_str_new(val1_size);
23
0
  string_t *value_b = t_str_new(val2_size);
24
25
0
  uni_utf8_to_decomposed_titlecase(val1, val1_size, value_a);
26
0
  uni_utf8_to_decomposed_titlecase(val2, val2_size, value_b);
27
28
0
  val1 = str_c(value_a);
29
0
  val2 = str_c(value_b);
30
31
0
  return strcmp(val1, val2);
32
0
}
33
34
static bool
35
cmp_i_unicode_casemap_char_match(const struct sieve_comparator *cmp ATTR_UNUSED,
36
         const char **val, const char *val_end,
37
         const char **key, const char *key_end)
38
0
{
39
0
  const char *val_begin = *val;
40
0
  const char *key_begin = *key;
41
42
0
  while (*val < val_end && *key < key_end) {
43
0
    unsigned int val_len = uni_utf8_char_bytes((unsigned char)**val);
44
0
    unsigned int key_len = uni_utf8_char_bytes((unsigned char)**key);
45
46
0
    unichar_t val_chr, key_chr;
47
0
    uni_utf8_get_char(*val, &val_chr);
48
0
    uni_utf8_get_char(*key, &key_chr);
49
50
    /* normalize */
51
0
    val_chr = uni_ucs4_to_titlecase(val_chr);
52
0
    key_chr = uni_ucs4_to_titlecase(key_chr);
53
54
0
    if (val_chr != key_chr)
55
0
      break;
56
0
    (*val) += val_len;
57
0
    (*key) += key_len;
58
0
  }
59
60
0
  i_assert(*val <= val_end);
61
0
  i_assert(*key <= key_end);
62
63
0
  if (*key < key_end) {
64
    /* Reset */
65
0
    *val = val_begin;
66
0
    *key = key_begin;
67
68
0
    return FALSE;
69
0
  }
70
71
0
  return TRUE;
72
0
}
73
74
static bool
75
cmp_i_unicode_casemap_char_skip(const struct sieve_comparator *cmp ATTR_UNUSED,
76
        const char **val, const char *val_end)
77
0
{
78
0
  if (*val >= val_end)
79
0
    return FALSE;
80
0
  unsigned int len = uni_utf8_char_bytes(**val);
81
0
  (*val) += len;
82
0
  return TRUE;
83
0
}
84
85
/*
86
 * Comparator object
87
 */
88
89
const struct sieve_comparator_def i_unicode_casemap_comparator = {
90
  SIEVE_OBJECT("i;unicode-casemap",
91
    &comparator_operand, SIEVE_COMPARATOR_I_UNICODE_CASEMAP),
92
  .flags =
93
    SIEVE_COMPARATOR_FLAG_EQUALITY |
94
    SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH |
95
    SIEVE_COMPARATOR_FLAG_PREFIX_MATCH,
96
  .compare = cmp_i_unicode_casemap_compare,
97
  .char_match = cmp_i_unicode_casemap_char_match,
98
  .char_skip = cmp_i_unicode_casemap_char_skip,
99
};