Coverage Report

Created: 2026-10-17 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/unicode-break.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "unicode-data.h"
5
#include "unicode-break.h"
6
7
/* This file implements the Unicode Text Segmemtation algorithms as specified in
8
   Unicode Standard Annex #29.
9
 */
10
11
/*
12
 * Grapheme Cluster Boundaries (Unicode Standard Annex #29, Section 3)
13
 */
14
15
void unicode_gc_break_init(struct unicode_gc_break *gcbrk)
16
0
{
17
0
  i_zero(gcbrk);
18
0
}
19
20
bool unicode_gc_break_cp(struct unicode_gc_break *gcbrk, uint32_t cp,
21
       const struct unicode_code_point_data **_cp_data)
22
0
{
23
0
  if (*_cp_data == NULL)
24
0
    *_cp_data = unicode_code_point_get_data(cp);
25
26
0
  const struct unicode_code_point_data *cp_data = *_cp_data;
27
0
  int bstatus = -1;
28
29
  /* GB1: Break at the start and end of text.
30
     sot + Any
31
     Any + eot
32
   */
33
0
  if (!gcbrk->gb1) {
34
0
    gcbrk->gb1 = TRUE;
35
0
    bstatus = 1;
36
0
  }
37
38
  /* GB3: Do not break between a CR and LF.
39
     CR x LF
40
   */
41
0
  if (gcbrk->gb3) {
42
0
    if (cp_data->pb_b_lf) {
43
0
      if (bstatus < 0)
44
0
        bstatus = 0;
45
0
    }
46
0
    if (!cp_data->pb_b_cr)
47
0
      gcbrk->gb3 = FALSE;
48
0
  } else if (cp_data->pb_b_cr) {
49
0
    gcbrk->gb3 = TRUE;
50
0
  }
51
52
  /* GB4, GB5: Break before and after controls.
53
     (Control | CR | LF) +
54
     + (Control | CR | LF)
55
   */
56
0
  if (gcbrk->gb4) {
57
    /* GB4: (Control | CR | LF) / */
58
0
    if (bstatus < 0)
59
0
      bstatus = 1;
60
0
    if (!cp_data->pb_b_cr && !cp_data->pb_b_lf &&
61
0
        !cp_data->pb_gcb_control)
62
0
      gcbrk->gb4 = FALSE;
63
0
  } else if (cp_data->pb_b_cr || cp_data->pb_b_lf ||
64
0
       cp_data->pb_gcb_control) {
65
0
    gcbrk->gb4 = TRUE;
66
    /* GB5: / (Control | CR | LF) */
67
0
    if (bstatus < 0)
68
0
      bstatus = 1;
69
0
  }
70
71
  /* GB6: Do not break Hangul syllable or other conjoining sequences.
72
     L x (L | V | LV | LVT)
73
   */
74
0
  if (gcbrk->gb6) {
75
0
    if (cp_data->pb_gcb_v || cp_data->pb_gcb_lv ||
76
0
        cp_data->pb_gcb_lvt) {
77
0
      if (bstatus < 0)
78
0
        bstatus = 0;
79
0
      gcbrk->gb6 = FALSE;
80
0
    } else if (cp_data->pb_gcb_l) {
81
0
      if (bstatus < 0)
82
0
        bstatus = 0;
83
0
    } else {
84
0
      gcbrk->gb6 = FALSE;
85
0
    }
86
0
  } else if (cp_data->pb_gcb_l) {
87
0
    gcbrk->gb6 = TRUE;
88
0
  }
89
90
  /* GB7: Do not break Hangul syllable or other conjoining sequences.
91
     (LV | V) x (V | T)
92
   */
93
0
  if (gcbrk->gb7) {
94
0
    if (cp_data->pb_gcb_t) {
95
0
      if (bstatus < 0)
96
0
        bstatus = 0;
97
0
      gcbrk->gb7 = FALSE;
98
0
    } else if (cp_data->pb_gcb_v) {
99
0
      if (bstatus < 0)
100
0
        bstatus = 0;
101
0
    } else {
102
0
      gcbrk->gb7 = FALSE;
103
0
    }
104
0
  } else if (cp_data->pb_gcb_lv || cp_data->pb_gcb_v) {
105
0
    gcbrk->gb7 = TRUE;
106
0
  }
107
108
  /* GB8: Do not break Hangul syllable or other conjoining sequences.
109
     (LVT | T) x T
110
   */
111
0
  if (gcbrk->gb8) {
112
0
    if (!cp_data->pb_gcb_t)
113
0
      gcbrk->gb8 = FALSE;
114
0
    else {
115
0
      if (bstatus < 0)
116
0
        bstatus = 0;
117
0
    }
118
0
  } else if (cp_data->pb_gcb_lvt || cp_data->pb_gcb_t) {
119
0
    gcbrk->gb8 = TRUE;
120
0
  }
121
122
  /* GB9: Do not break before extending characters or ZWJ.
123
     x (Extend | ZWJ)
124
   */
125
0
  if (cp_data->pb_gcb_extend || cp_data->pb_b_zwj) {
126
0
    if (bstatus < 0)
127
0
      bstatus = 0;
128
0
  }
129
130
  /* GB9a: Do not break before SpacingMarks.
131
     x SpacingMark
132
   */
133
0
  if (cp_data->pb_gcb_spacingmark) {
134
0
    if (bstatus < 0)
135
0
      bstatus = 0;
136
0
  }
137
138
  /* GB9b: Do not break after Prepend characters.
139
     Prepend x
140
   */
141
0
  if (gcbrk->gb9b) {
142
0
    if (bstatus < 0)
143
0
      bstatus = 0;
144
0
    if (!cp_data->pb_gcb_prepend)
145
0
      gcbrk->gb9b = FALSE;
146
0
  } else if (cp_data->pb_gcb_prepend) {
147
0
    gcbrk->gb9b = TRUE;
148
0
  }
149
150
  /* GB9c: Do not break within Indic conjuncts.
151
     \p{InCB=Linker} \p{InCB=Extend}* x \p{InCB=Consonant}
152
   */
153
0
  enum {
154
0
    GB9C_STATE_NONE = 0,
155
0
    GB9C_STATE_LINKER,
156
0
  };
157
0
  switch (cp_data->indic_conjunct_break) {
158
0
  case UNICODE_INDIC_CONJUNCT_BREAK_LINKER:
159
0
    gcbrk->gb9c = GB9C_STATE_LINKER;
160
0
    break;
161
0
  case UNICODE_INDIC_CONJUNCT_BREAK_EXTEND:
162
0
    break;
163
0
  case UNICODE_INDIC_CONJUNCT_BREAK_CONSONANT:
164
0
    if (gcbrk->gb9c == GB9C_STATE_LINKER && bstatus < 0)
165
0
      bstatus = 0;
166
0
    gcbrk->gb9c = GB9C_STATE_NONE;
167
0
    break;
168
0
  default:
169
0
    gcbrk->gb9c = GB9C_STATE_NONE;
170
0
    break;
171
0
  }
172
173
  /* GB11: Do not break within emoji ZWJ sequences.
174
     \p{Extended_Pictographic} Extend* ZWJ x \p{Extended_Pictographic}
175
   */
176
0
  enum {
177
0
    GB11_STATE_NONE = 0,
178
0
    GB11_STATE_EP,
179
0
    GB11_STATE_ZWJ,
180
0
  };
181
0
  switch (gcbrk->gb11) {
182
0
  case GB11_STATE_NONE:
183
0
    if (cp_data->pb_e_extended_pictographic)
184
0
      gcbrk->gb11 = GB11_STATE_EP;
185
0
    break;
186
0
  case GB11_STATE_EP:
187
0
    if (cp_data->pb_e_extended_pictographic)
188
0
      break;
189
0
    if (cp_data->pb_gcb_extend)
190
0
      break;
191
0
    if (cp_data->pb_b_zwj) {
192
0
      gcbrk->gb11 = GB11_STATE_ZWJ;
193
0
      break;
194
0
    }
195
0
    gcbrk->gb11 = GB11_STATE_NONE;
196
0
    break;
197
0
  case GB11_STATE_ZWJ:
198
0
    if (cp_data->pb_e_extended_pictographic) {
199
0
      if (bstatus < 0)
200
0
        bstatus = 0;
201
0
      gcbrk->gb11 = GB11_STATE_EP;
202
0
      break;
203
0
    }
204
0
    gcbrk->gb11 = GB11_STATE_NONE;
205
0
    break;
206
0
  default:
207
0
    i_unreached();
208
0
  }
209
210
  /* GB12, GB13: Do not break within emoji flag sequences. That is, do not
211
           break between regional indicator (RI) symbols if there is
212
           an odd number of RI characters before the break point.
213
     sot   (RI RI)* RI x RI
214
     [^RI] (RI RI)* RI x RI
215
   */
216
0
  if (gcbrk->gb12) {
217
0
    if (cp_data->pb_b_regional_indicator) {
218
0
      if (bstatus < 0)
219
0
        bstatus = 0;
220
0
    }
221
0
    gcbrk->gb12 = FALSE;
222
0
  } else if (cp_data->pb_b_regional_indicator) {
223
0
    gcbrk->gb12 = TRUE;
224
0
  }
225
226
  /* GB999: Otherwise, break everywhere.
227
     (Any + Any)
228
   */
229
0
  if (bstatus == 0)
230
0
    return FALSE;
231
0
  return TRUE;
232
0
}