Coverage Report

Created: 2026-04-12 07:00

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