/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 | | }; |