/src/dovecot/src/lib/unicode-transform.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "unichar.h" |
5 | | #include "unicode-data.h" |
6 | | #include "unicode-transform.h" |
7 | | |
8 | 0 | #define HANGUL_FIRST 0xac00 |
9 | 0 | #define HANGUL_LAST 0xd7a3 |
10 | | |
11 | | /* |
12 | | * Transform |
13 | | */ |
14 | | |
15 | | ssize_t uniform_transform_forward( |
16 | | struct unicode_transform *trans, const uint32_t *out, |
17 | | const struct unicode_code_point_data *const *out_data, size_t out_len, |
18 | | const char **error_r) |
19 | 0 | { |
20 | 0 | struct unicode_transform_buffer buf_next; |
21 | 0 | ssize_t sret; |
22 | |
|
23 | 0 | i_zero(&buf_next); |
24 | 0 | buf_next.cp = out; |
25 | 0 | buf_next.cp_data = out_data; |
26 | 0 | buf_next.cp_count = out_len; |
27 | |
|
28 | 0 | i_assert(trans->next != NULL); |
29 | 0 | i_assert(trans->next->def != NULL); |
30 | 0 | i_assert(trans->next->def->input != NULL); |
31 | 0 | sret = trans->next->def->input(trans->next, &buf_next, error_r); |
32 | |
|
33 | 0 | i_assert(sret >= 0 || *error_r != NULL); |
34 | 0 | i_assert(sret <= (ssize_t)out_len); |
35 | 0 | return sret; |
36 | 0 | } |
37 | | |
38 | | ssize_t unicode_transform_input_buf(struct unicode_transform *trans, |
39 | | const struct unicode_transform_buffer *buf, |
40 | | const char **error_r) |
41 | 0 | { |
42 | 0 | struct unicode_transform_buffer in_buf; |
43 | 0 | size_t input_total = 0; |
44 | 0 | ssize_t sret; |
45 | 0 | bool flushed = FALSE; |
46 | 0 | int ret; |
47 | |
|
48 | 0 | *error_r = NULL; |
49 | |
|
50 | 0 | in_buf = *buf; |
51 | |
|
52 | 0 | while (in_buf.cp_count > 0) { |
53 | 0 | if (in_buf.cp_count > 0) { |
54 | 0 | i_assert(trans->def->input != NULL); |
55 | 0 | sret = trans->def->input(trans, &in_buf, error_r); |
56 | 0 | if (sret < 0) { |
57 | 0 | i_assert(*error_r != NULL); |
58 | 0 | return -1; |
59 | 0 | } |
60 | 0 | if (sret > 0) { |
61 | 0 | i_assert((size_t)sret <= in_buf.cp_count); |
62 | 0 | in_buf.cp += sret; |
63 | 0 | in_buf.cp_count -= sret; |
64 | 0 | input_total += sret; |
65 | 0 | flushed = FALSE; |
66 | 0 | continue; |
67 | 0 | } |
68 | 0 | if (sret == 0 && flushed) |
69 | 0 | break; |
70 | 0 | } |
71 | | |
72 | 0 | struct unicode_transform *tp = trans; |
73 | |
|
74 | 0 | while (tp->next != NULL) { |
75 | 0 | if (tp->def->flush != NULL) { |
76 | 0 | ret = tp->def->flush(tp, FALSE, error_r); |
77 | 0 | if (ret < 0) { |
78 | 0 | i_assert(*error_r != NULL); |
79 | 0 | return -1; |
80 | 0 | } |
81 | 0 | } |
82 | 0 | tp = tp->next; |
83 | 0 | } |
84 | | |
85 | 0 | flushed = TRUE; |
86 | 0 | } |
87 | | |
88 | 0 | return input_total; |
89 | 0 | } |
90 | | |
91 | | int unicode_transform_flush(struct unicode_transform *trans, |
92 | | const char **error_r) |
93 | 0 | { |
94 | 0 | int ret; |
95 | |
|
96 | 0 | *error_r = NULL; |
97 | |
|
98 | 0 | while (trans != NULL) { |
99 | 0 | struct unicode_transform *tp = trans; |
100 | 0 | bool progress = FALSE; |
101 | |
|
102 | 0 | while (tp != NULL) { |
103 | 0 | if (tp->def->flush == NULL) { |
104 | 0 | progress = TRUE; |
105 | 0 | if (tp == trans) |
106 | 0 | trans = trans->next; |
107 | 0 | } else { |
108 | 0 | ret = tp->def->flush(tp, (tp == trans), error_r); |
109 | 0 | if (ret < 0) { |
110 | 0 | i_assert(*error_r != NULL); |
111 | 0 | return -1; |
112 | 0 | } |
113 | 0 | if (ret > 0) { |
114 | 0 | progress = TRUE; |
115 | 0 | if (tp == trans) |
116 | 0 | trans = trans->next; |
117 | 0 | } |
118 | 0 | } |
119 | 0 | tp = tp->next; |
120 | 0 | } |
121 | 0 | if (!progress) |
122 | 0 | return 0; |
123 | 0 | } |
124 | 0 | return 1; |
125 | 0 | } |
126 | | |
127 | | /* Buffer Sink */ |
128 | | |
129 | | static ssize_t |
130 | | unicode_buffer_sink_input(struct unicode_transform *trans, |
131 | | const struct unicode_transform_buffer *buf, |
132 | | const char **error_r); |
133 | | |
134 | | static const struct unicode_transform_def unicode_buffer_sink_def = { |
135 | | .input = unicode_buffer_sink_input, |
136 | | }; |
137 | | |
138 | | void unicode_buffer_sink_init(struct unicode_buffer_sink *sink, |
139 | | buffer_t *buffer) |
140 | 0 | { |
141 | 0 | i_zero(sink); |
142 | 0 | unicode_transform_init(&sink->transform, &unicode_buffer_sink_def); |
143 | 0 | sink->buffer = buffer; |
144 | 0 | } |
145 | | |
146 | | static ssize_t |
147 | | unicode_buffer_sink_input(struct unicode_transform *trans, |
148 | | const struct unicode_transform_buffer *buf, |
149 | | const char **error_r ATTR_UNUSED) |
150 | 0 | { |
151 | 0 | struct unicode_buffer_sink *sink = |
152 | 0 | container_of(trans, struct unicode_buffer_sink, transform); |
153 | |
|
154 | 0 | uni_ucs4_to_utf8(buf->cp, buf->cp_count, sink->buffer); |
155 | 0 | return buf->cp_count; |
156 | 0 | } |
157 | | |
158 | | /* Static Array Sink */ |
159 | | |
160 | | static ssize_t |
161 | | unicode_static_array_sink_input(struct unicode_transform *trans, |
162 | | const struct unicode_transform_buffer *buf, |
163 | | const char **error_r); |
164 | | |
165 | | static const struct unicode_transform_def unicode_static_array_sink_def = { |
166 | | .input = unicode_static_array_sink_input, |
167 | | }; |
168 | | |
169 | | void unicode_static_array_sink_init(struct unicode_static_array_sink *sink, |
170 | | uint32_t *array, size_t array_size, |
171 | | size_t *array_pos) |
172 | 0 | { |
173 | 0 | i_zero(sink); |
174 | 0 | unicode_transform_init(&sink->transform, |
175 | 0 | &unicode_static_array_sink_def); |
176 | 0 | sink->array = array; |
177 | 0 | sink->array_size = array_size; |
178 | 0 | sink->array_pos = array_pos; |
179 | 0 | } |
180 | | |
181 | | static ssize_t |
182 | | unicode_static_array_sink_input(struct unicode_transform *trans, |
183 | | const struct unicode_transform_buffer *buf, |
184 | | const char **error_r) |
185 | 0 | { |
186 | 0 | struct unicode_static_array_sink *sink = |
187 | 0 | container_of(trans, struct unicode_static_array_sink, |
188 | 0 | transform); |
189 | |
|
190 | 0 | if (*sink->array_pos + buf->cp_count > sink->array_size) { |
191 | 0 | *error_r = "Output overflow"; |
192 | 0 | return -1; |
193 | 0 | } |
194 | 0 | memcpy(sink->array + *sink->array_pos, buf->cp, |
195 | 0 | buf->cp_count * sizeof(*buf->cp)); |
196 | 0 | *sink->array_pos += buf->cp_count; |
197 | 0 | return buf->cp_count; |
198 | 0 | } |
199 | | |
200 | | /* |
201 | | * Hangul syllable (de)composition |
202 | | */ |
203 | | |
204 | 0 | #define UNI_HANGUL_S_BASE 0xac00 |
205 | 0 | #define UNI_HANGUL_L_BASE 0x1100 |
206 | 0 | #define UNI_HANGUL_V_BASE 0x1161 |
207 | 0 | #define UNI_HANGUL_T_BASE 0x11a7 |
208 | 0 | #define UNI_HANGUL_L_COUNT 19 |
209 | 0 | #define UNI_HANGUL_V_COUNT 21 |
210 | 0 | #define UNI_HANGUL_T_COUNT 28 |
211 | 0 | #define UNI_HANGUL_N_COUNT (UNI_HANGUL_V_COUNT * UNI_HANGUL_T_COUNT) |
212 | 0 | #define UNI_HANGUL_L_END (UNI_HANGUL_L_BASE + UNI_HANGUL_L_COUNT) |
213 | 0 | #define UNI_HANGUL_V_END (UNI_HANGUL_V_BASE + UNI_HANGUL_V_COUNT) |
214 | 0 | #define UNI_HANGUL_T_END (UNI_HANGUL_T_BASE + UNI_HANGUL_T_COUNT) |
215 | 0 | #define UNI_HANGUL_S_END 0xD7A4 |
216 | | |
217 | | static size_t unicode_hangul_decompose(uint32_t cp, uint32_t buf[3]) |
218 | 0 | { |
219 | | /* The Unicode Standard, Section 3.12.2: |
220 | | Hangul Syllable Decomposition |
221 | | */ |
222 | |
|
223 | 0 | size_t s_index = cp - UNI_HANGUL_S_BASE; |
224 | 0 | size_t l_index = s_index / UNI_HANGUL_N_COUNT; |
225 | 0 | size_t v_index = ((s_index % UNI_HANGUL_N_COUNT) / UNI_HANGUL_T_COUNT); |
226 | 0 | size_t t_index = s_index % UNI_HANGUL_T_COUNT; |
227 | 0 | uint32_t l_part = UNI_HANGUL_L_BASE + l_index; |
228 | 0 | uint32_t v_part = UNI_HANGUL_V_BASE + v_index; |
229 | |
|
230 | 0 | if (t_index == 0) { |
231 | 0 | buf[0] = l_part; |
232 | 0 | buf[1] = v_part; |
233 | 0 | return 2; |
234 | 0 | } |
235 | | |
236 | 0 | uint32_t t_part = UNI_HANGUL_T_BASE + t_index; |
237 | |
|
238 | 0 | buf[0] = l_part; |
239 | 0 | buf[1] = v_part; |
240 | 0 | buf[2] = t_part; |
241 | 0 | return 3; |
242 | 0 | } |
243 | | |
244 | | static uint32_t unicode_hangul_compose_pair(uint32_t l, uint32_t r) |
245 | 0 | { |
246 | | /* The Unicode Standard, Section 3.12.3: |
247 | | Hangul Syllable Composition |
248 | | */ |
249 | | |
250 | | /* <LPart, VPart> */ |
251 | 0 | if (l >= UNI_HANGUL_L_BASE && l < UNI_HANGUL_L_END && |
252 | 0 | r >= UNI_HANGUL_V_BASE && r < UNI_HANGUL_V_END) { |
253 | 0 | uint32_t l_part = l, v_part = r; |
254 | |
|
255 | 0 | size_t l_index = l_part - UNI_HANGUL_L_BASE; |
256 | 0 | size_t v_index = v_part - UNI_HANGUL_V_BASE; |
257 | 0 | size_t lv_index = l_index * UNI_HANGUL_N_COUNT + |
258 | 0 | v_index * UNI_HANGUL_T_COUNT; |
259 | 0 | return UNI_HANGUL_S_BASE + lv_index; |
260 | 0 | } |
261 | | /* A sequence <LVPart, TPart> */ |
262 | 0 | if (l >= UNI_HANGUL_S_BASE && l < UNI_HANGUL_S_END && |
263 | 0 | r >= (UNI_HANGUL_T_BASE + 1u) && r < UNI_HANGUL_T_END && |
264 | 0 | ((l - UNI_HANGUL_S_BASE) % UNI_HANGUL_T_COUNT) == 0) { |
265 | 0 | uint32_t lv_part = l, t_part = r; |
266 | |
|
267 | 0 | size_t t_index = t_part - UNI_HANGUL_T_BASE; |
268 | 0 | return lv_part + t_index; |
269 | 0 | } |
270 | 0 | return 0x0000; |
271 | 0 | } |
272 | | |
273 | | /* |
274 | | * Normalization transform: NFD, NFKD, NFC, NFKC |
275 | | */ |
276 | | |
277 | | /* The transform buffers the decomposed code points until they can't change |
278 | | anymore: |
279 | | |
280 | | - The Canonical Ordering Algorithm only reorders non-starters, and never |
281 | | across a starter. So everything up to and including the last starter in |
282 | | the buffer is in its final order, while the non-starters after it may |
283 | | still be reordered by the following input. |
284 | | - For NFC and NFKC, the Canonical Composition Algorithm is run |
285 | | incrementally on the code points that are in their final order. A later |
286 | | code point can only compose with the last starter, because any starter |
287 | | in between blocks it. So everything before the last starter is final. |
288 | | |
289 | | The Stream-Safe Text Process (UAX15-D4) limits the number of non-starters |
290 | | after the last starter to UNICODE_NF_STREAM_SAFE_NON_STARTER_LEN. So after |
291 | | the final code points are forwarded, the buffer contains at most a starter |
292 | | and the non-starters after it. UNICODE_NF_BUFFER_SIZE leaves space for a |
293 | | CGJ and the longest decomposition after them, so the next code point can |
294 | | always be accepted after the final code points are forwarded. */ |
295 | | |
296 | 0 | #define UNICODE_COMBINING_GRAPHEME_JOINER 0x034f |
297 | | |
298 | | static ssize_t |
299 | | unicode_nf_input(struct unicode_transform *trans, |
300 | | const struct unicode_transform_buffer *buf, |
301 | | const char **error_r); |
302 | | static int |
303 | | unicode_nf_flush(struct unicode_transform *trans, bool finished, |
304 | | const char **error_r); |
305 | | |
306 | | static const struct unicode_transform_def unicode_nf_def = { |
307 | | .input = unicode_nf_input, |
308 | | .flush = unicode_nf_flush, |
309 | | }; |
310 | | |
311 | | void unicode_nf_init(struct unicode_nf_context *ctx_r, |
312 | | enum unicode_nf_type type) |
313 | 0 | { |
314 | | /* The buffers are written before they're read */ |
315 | 0 | memset(ctx_r, 0, offsetof(struct unicode_nf_context, cp_buffer)); |
316 | 0 | unicode_transform_init(&ctx_r->transform, &unicode_nf_def); |
317 | |
|
318 | 0 | switch (type) { |
319 | 0 | case UNICODE_NFD: |
320 | 0 | ctx_r->canonical = TRUE; |
321 | 0 | break; |
322 | 0 | case UNICODE_NFKD: |
323 | 0 | break; |
324 | 0 | case UNICODE_NFC: |
325 | 0 | ctx_r->compose = TRUE; |
326 | 0 | ctx_r->canonical = TRUE; |
327 | 0 | break; |
328 | 0 | case UNICODE_NFKC: |
329 | 0 | ctx_r->compose = TRUE; |
330 | 0 | break; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | void unicode_nf_reset(struct unicode_nf_context *ctx) |
335 | 0 | { |
336 | 0 | enum unicode_nf_type type = |
337 | 0 | (ctx->compose ? (ctx->canonical ? UNICODE_NFC : UNICODE_NFKC) : |
338 | 0 | (ctx->canonical ? UNICODE_NFD : UNICODE_NFKD)); |
339 | 0 | struct unicode_transform *next = ctx->transform.next; |
340 | |
|
341 | 0 | unicode_nf_init(ctx, type); |
342 | 0 | unicode_transform_chain(&ctx->transform, next); |
343 | 0 | } |
344 | | |
345 | | static void |
346 | | unicode_nf_buffer_delete(struct unicode_nf_context *ctx, size_t offset, |
347 | | size_t count) |
348 | 0 | { |
349 | 0 | if (count == 0) |
350 | 0 | return; |
351 | | |
352 | 0 | i_assert(offset < ctx->buffer_len); |
353 | 0 | i_assert(count <= ctx->buffer_len); |
354 | 0 | i_assert(offset <= (ctx->buffer_len - count)); |
355 | | |
356 | 0 | if (count == ctx->buffer_len) { |
357 | 0 | ctx->buffer_len = 0; |
358 | 0 | return; |
359 | 0 | } |
360 | | |
361 | 0 | size_t trailer = ctx->buffer_len - (offset + count); |
362 | 0 | if (trailer > 0) { |
363 | 0 | memmove(&ctx->cp_buffer[offset], |
364 | 0 | &ctx->cp_buffer[offset + count], |
365 | 0 | trailer * sizeof(ctx->cp_buffer[0])); |
366 | 0 | memmove(&ctx->cpd_buffer[offset], |
367 | 0 | &ctx->cpd_buffer[offset + count], |
368 | 0 | trailer * sizeof(ctx->cpd_buffer[0])); |
369 | 0 | } |
370 | 0 | ctx->buffer_len -= count; |
371 | 0 | } |
372 | | |
373 | | static void |
374 | | unicode_nf_buffer_delete_head(struct unicode_nf_context *ctx, size_t count) |
375 | 0 | { |
376 | 0 | if (count == 0) |
377 | 0 | return; |
378 | | |
379 | 0 | i_assert(count <= ctx->buffer_output_max); |
380 | 0 | unicode_nf_buffer_delete(ctx, 0, count); |
381 | |
|
382 | 0 | ctx->buffer_output_max -= count; |
383 | 0 | ctx->run_pos -= count; |
384 | 0 | if (ctx->compose) |
385 | 0 | ctx->composed_pos -= count; |
386 | 0 | if (ctx->have_starter) { |
387 | | /* The starter is forwarded only when flushing at the end */ |
388 | 0 | if (ctx->starter_pos >= count) |
389 | 0 | ctx->starter_pos -= count; |
390 | 0 | else |
391 | 0 | ctx->have_starter = FALSE; |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | | static void |
396 | | unicode_nf_buffer_append(struct unicode_nf_context *ctx, uint32_t cp, |
397 | | const struct unicode_code_point_data *cpd) |
398 | 0 | { |
399 | 0 | uint8_t ccc = cpd->canonical_combining_class; |
400 | 0 | size_t pos = ctx->buffer_len; |
401 | |
|
402 | 0 | i_assert(ctx->buffer_len < UNICODE_NF_BUFFER_SIZE); |
403 | 0 | if (ccc != 0) { |
404 | | /* Apply the Canonical Ordering Algorithm (COA): Insert the |
405 | | non-starter after the preceding code points that have the |
406 | | same or a lower combining class. Starters have combining |
407 | | class 0, so a non-starter is never moved before a |
408 | | starter. */ |
409 | 0 | while (pos > 0 && |
410 | 0 | ctx->cpd_buffer[pos - 1]->canonical_combining_class > ccc) { |
411 | 0 | ctx->cp_buffer[pos] = ctx->cp_buffer[pos - 1]; |
412 | 0 | ctx->cpd_buffer[pos] = ctx->cpd_buffer[pos - 1]; |
413 | 0 | pos--; |
414 | 0 | } |
415 | 0 | } |
416 | 0 | ctx->cp_buffer[pos] = cp; |
417 | 0 | ctx->cpd_buffer[pos] = cpd; |
418 | 0 | ctx->buffer_len++; |
419 | 0 | if (ccc == 0) |
420 | 0 | ctx->run_pos = ctx->buffer_len; |
421 | 0 | } |
422 | | |
423 | | static uint32_t |
424 | | unicode_nf_compose_pair(uint32_t l, uint32_t r, |
425 | | const struct unicode_code_point_data *l_data) |
426 | 0 | { |
427 | 0 | uint32_t comp = unicode_hangul_compose_pair(l, r); |
428 | |
|
429 | 0 | if (comp > 0x0000) |
430 | 0 | return comp; |
431 | 0 | return unicode_code_point_data_find_composition(l_data, r); |
432 | 0 | } |
433 | | |
434 | | static void unicode_nf_compose(struct unicode_nf_context *ctx) |
435 | 0 | { |
436 | 0 | size_t in_pos, out_pos = ctx->composed_pos; |
437 | | |
438 | | /* Apply the Canonical Composition Algorithm (UAX15-D117) to the code |
439 | | points whose order is final. */ |
440 | 0 | for (in_pos = ctx->composed_pos; in_pos < ctx->run_pos; in_pos++) { |
441 | 0 | uint32_t cp = ctx->cp_buffer[in_pos]; |
442 | 0 | const struct unicode_code_point_data *cpd = |
443 | 0 | ctx->cpd_buffer[in_pos]; |
444 | 0 | uint8_t ccc = cpd->canonical_combining_class; |
445 | | |
446 | | /* The code point isn't blocked from the last starter, if |
447 | | all the code points between them have a lower combining |
448 | | class. A starter is blocked by anything in between. */ |
449 | 0 | if (ctx->have_starter && ctx->last_ccc < (int)ccc) { |
450 | 0 | size_t starter_pos = ctx->starter_pos; |
451 | 0 | uint32_t comp = unicode_nf_compose_pair( |
452 | 0 | ctx->cp_buffer[starter_pos], cp, |
453 | 0 | ctx->cpd_buffer[starter_pos]); |
454 | |
|
455 | 0 | if (comp > 0x0000) { |
456 | 0 | ctx->cp_buffer[starter_pos] = comp; |
457 | 0 | ctx->cpd_buffer[starter_pos] = |
458 | 0 | unicode_code_point_get_data(comp); |
459 | 0 | continue; |
460 | 0 | } |
461 | 0 | } |
462 | 0 | if (ccc == 0) { |
463 | 0 | ctx->have_starter = TRUE; |
464 | 0 | ctx->starter_pos = out_pos; |
465 | 0 | ctx->last_ccc = -1; |
466 | 0 | } else { |
467 | 0 | ctx->last_ccc = ccc; |
468 | 0 | } |
469 | 0 | ctx->cp_buffer[out_pos] = cp; |
470 | 0 | ctx->cpd_buffer[out_pos] = cpd; |
471 | 0 | out_pos++; |
472 | 0 | } |
473 | |
|
474 | 0 | if (in_pos > out_pos) { |
475 | | /* Remove the gap left by the composed code points */ |
476 | 0 | unicode_nf_buffer_delete(ctx, out_pos, (in_pos - out_pos)); |
477 | 0 | ctx->run_pos = out_pos; |
478 | 0 | } |
479 | 0 | ctx->composed_pos = out_pos; |
480 | 0 | } |
481 | | |
482 | | static void unicode_nf_update_output_max(struct unicode_nf_context *ctx) |
483 | 0 | { |
484 | 0 | if (!ctx->compose) |
485 | 0 | ctx->buffer_output_max = ctx->run_pos; |
486 | 0 | else { |
487 | 0 | unicode_nf_compose(ctx); |
488 | 0 | ctx->buffer_output_max = (ctx->have_starter ? |
489 | 0 | ctx->starter_pos : ctx->composed_pos); |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | static void |
494 | | unicode_nf_get_ccc_counts(const uint32_t *decomp, size_t len, |
495 | | const struct unicode_code_point_data *const *decomp_cpd, |
496 | | size_t *lead_r, size_t *trail_r, bool *seen_starter_r) |
497 | 0 | { |
498 | 0 | const struct unicode_code_point_data *cpd; |
499 | 0 | size_t i; |
500 | |
|
501 | 0 | *lead_r = *trail_r = 0; |
502 | 0 | *seen_starter_r = FALSE; |
503 | 0 | for (i = 0; i < len; i++) { |
504 | 0 | cpd = (decomp_cpd != NULL ? decomp_cpd[i] : |
505 | 0 | unicode_code_point_get_data(decomp[i])); |
506 | 0 | if (cpd->canonical_combining_class == 0) { |
507 | 0 | *seen_starter_r = TRUE; |
508 | 0 | *trail_r = 0; |
509 | 0 | } else if (!*seen_starter_r) |
510 | 0 | (*lead_r)++; |
511 | 0 | else |
512 | 0 | (*trail_r)++; |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | | static bool |
517 | | unicode_nf_input_cp(struct unicode_nf_context *ctx, uint32_t cp, |
518 | | const struct unicode_code_point_data *cpd) |
519 | 0 | { |
520 | 0 | const struct unicode_code_point_data * |
521 | 0 | decomp_cpd[UNICODE_DECOMPOSITION_MAX_LENGTH]; |
522 | 0 | const uint32_t *decomp, *decomp_k; |
523 | 0 | uint32_t decomp_hangul[3]; |
524 | 0 | size_t len, len_k, i; |
525 | | |
526 | | /* |
527 | | * Decompose the code point |
528 | | */ |
529 | |
|
530 | 0 | if (cp >= HANGUL_FIRST && cp <= HANGUL_LAST) { |
531 | 0 | len = len_k = unicode_hangul_decompose(cp, decomp_hangul); |
532 | 0 | decomp = decomp_k = decomp_hangul; |
533 | 0 | for (i = 0; i < len; i++) |
534 | 0 | decomp_cpd[i] = unicode_code_point_get_data(decomp[i]); |
535 | 0 | } else { |
536 | 0 | if (cpd == NULL) |
537 | 0 | cpd = unicode_code_point_get_data(cp); |
538 | 0 | len = unicode_code_point_data_get_full_decomposition( |
539 | 0 | cpd, ctx->canonical, &decomp); |
540 | 0 | if (len == 0) { |
541 | 0 | decomp = &cp; |
542 | 0 | len = 1; |
543 | 0 | decomp_cpd[0] = cpd; |
544 | 0 | } else { |
545 | 0 | for (i = 0; i < len; i++) { |
546 | 0 | decomp_cpd[i] = |
547 | 0 | unicode_code_point_get_data(decomp[i]); |
548 | 0 | } |
549 | 0 | } |
550 | 0 | len_k = len; |
551 | 0 | decomp_k = decomp; |
552 | 0 | if (ctx->canonical) { |
553 | 0 | len_k = unicode_code_point_data_get_full_decomposition( |
554 | 0 | cpd, FALSE, &decomp_k); |
555 | 0 | if (len_k == 0) { |
556 | 0 | decomp_k = decomp; |
557 | 0 | len_k = len; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | } |
561 | |
|
562 | 0 | i_assert(len <= UNICODE_DECOMPOSITION_MAX_LENGTH); |
563 | 0 | i_assert(len_k <= UNICODE_DECOMPOSITION_MAX_LENGTH); |
564 | | /* decomp_cpd[] is reused below when decomp_k == decomp */ |
565 | 0 | i_assert(decomp_k != decomp || len_k == len); |
566 | | |
567 | | /* UAX15-D4: Stream-Safe Text Process is the process of producing a |
568 | | Unicode string in Stream-Safe Text Format by processing that string |
569 | | from start to finish, inserting U+034F COMBINING GRAPHEME JOINER |
570 | | (CGJ) within long sequences of non-starters. The exact position of |
571 | | the inserted CGJs are determined according to the following |
572 | | algorithm, which describes the generation of an output string from an |
573 | | input string: |
574 | | |
575 | | 1. If the input string is empty, return an empty output string. |
576 | | 2. Set nonStarterCount to zero. |
577 | | 3. For each code point C in the input string: |
578 | | a. Produce the NFKD decomposition S. |
579 | | b. If nonStarterCount plus the number of initial non-starters in |
580 | | S is greater than 30, append a CGJ to the output string and |
581 | | set the nonStarterCount to zero. |
582 | | c. Append C to the output string. |
583 | | d. If there are no starters in S, increment nonStarterCount by |
584 | | the number of code points in S; otherwise, set |
585 | | nonStarterCount to the number of trailing non-starters in S |
586 | | (which may be zero). |
587 | | 4. Return the output string. |
588 | | */ |
589 | 0 | size_t ns_lead, ns_trail; |
590 | 0 | bool seen_starter, cgj; |
591 | |
|
592 | 0 | unicode_nf_get_ccc_counts(decomp_k, len_k, |
593 | 0 | (decomp_k == decomp ? decomp_cpd : NULL), |
594 | 0 | &ns_lead, &ns_trail, &seen_starter); |
595 | 0 | cgj = (ctx->nonstarter_count + ns_lead > |
596 | 0 | UNICODE_NF_STREAM_SAFE_NON_STARTER_LEN); |
597 | |
|
598 | 0 | if (ctx->buffer_len + len + (cgj ? 1 : 0) > UNICODE_NF_BUFFER_SIZE) { |
599 | | /* Buffer is full. The final code points need to be |
600 | | forwarded first. */ |
601 | 0 | return FALSE; |
602 | 0 | } |
603 | | |
604 | 0 | if (cgj) { |
605 | 0 | unicode_nf_buffer_append(ctx, UNICODE_COMBINING_GRAPHEME_JOINER, |
606 | 0 | unicode_code_point_get_data( |
607 | 0 | UNICODE_COMBINING_GRAPHEME_JOINER)); |
608 | 0 | ctx->nonstarter_count = 0; |
609 | 0 | } |
610 | 0 | if (seen_starter) |
611 | 0 | ctx->nonstarter_count = ns_trail; |
612 | 0 | else |
613 | 0 | ctx->nonstarter_count += len_k; |
614 | |
|
615 | 0 | for (i = 0; i < len; i++) |
616 | 0 | unicode_nf_buffer_append(ctx, decomp[i], decomp_cpd[i]); |
617 | |
|
618 | 0 | unicode_nf_update_output_max(ctx); |
619 | 0 | return TRUE; |
620 | 0 | } |
621 | | |
622 | | static ssize_t |
623 | | unicode_nf_forward(struct unicode_nf_context *ctx, const char **error_r) |
624 | 0 | { |
625 | 0 | ssize_t sret; |
626 | |
|
627 | 0 | if (ctx->buffer_output_max == 0) |
628 | 0 | return 0; |
629 | | |
630 | 0 | sret = uniform_transform_forward(&ctx->transform, ctx->cp_buffer, |
631 | 0 | ctx->cpd_buffer, |
632 | 0 | ctx->buffer_output_max, error_r); |
633 | 0 | if (sret < 0) |
634 | 0 | return -1; |
635 | 0 | unicode_nf_buffer_delete_head(ctx, sret); |
636 | 0 | return sret; |
637 | 0 | } |
638 | | |
639 | | static ssize_t |
640 | | unicode_nf_input(struct unicode_transform *trans, |
641 | | const struct unicode_transform_buffer *buf, |
642 | | const char **error_r) |
643 | 0 | { |
644 | 0 | struct unicode_nf_context *ctx = |
645 | 0 | container_of(trans, struct unicode_nf_context, transform); |
646 | 0 | ssize_t sret; |
647 | 0 | size_t n = 0; |
648 | |
|
649 | 0 | while (n < buf->cp_count) { |
650 | 0 | if (unicode_nf_input_cp(ctx, buf->cp[n], |
651 | 0 | (buf->cp_data == NULL ? |
652 | 0 | NULL : buf->cp_data[n]))) { |
653 | 0 | n++; |
654 | 0 | continue; |
655 | 0 | } |
656 | | |
657 | | /* Only the final code points can fill the buffer */ |
658 | 0 | i_assert(ctx->buffer_output_max > 0); |
659 | 0 | sret = unicode_nf_forward(ctx, error_r); |
660 | 0 | if (sret < 0) |
661 | 0 | return -1; |
662 | 0 | if (sret == 0) { |
663 | | /* The next transform is full */ |
664 | 0 | break; |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | 0 | if (unicode_nf_forward(ctx, error_r) < 0) |
669 | 0 | return -1; |
670 | 0 | return n; |
671 | 0 | } |
672 | | |
673 | | static int |
674 | | unicode_nf_flush(struct unicode_transform *trans, bool finished, |
675 | | const char **error_r) |
676 | 0 | { |
677 | 0 | struct unicode_nf_context *ctx = |
678 | 0 | container_of(trans, struct unicode_nf_context, transform); |
679 | |
|
680 | 0 | if (finished) { |
681 | | /* No more input, so everything is final */ |
682 | 0 | ctx->run_pos = ctx->buffer_len; |
683 | 0 | if (ctx->compose) |
684 | 0 | unicode_nf_compose(ctx); |
685 | 0 | ctx->buffer_output_max = ctx->buffer_len; |
686 | 0 | } |
687 | |
|
688 | 0 | if (unicode_nf_forward(ctx, error_r) < 0) |
689 | 0 | return -1; |
690 | 0 | return (ctx->buffer_output_max == 0 ? 1 : 0); |
691 | 0 | } |
692 | | |
693 | | /* |
694 | | * Normalization check |
695 | | */ |
696 | | |
697 | | static ssize_t |
698 | | unicode_nf_check_sink_input(struct unicode_transform *trans, |
699 | | const struct unicode_transform_buffer *buf, |
700 | | const char **error_r); |
701 | | |
702 | | static const struct unicode_transform_def unicode_nf_check_sink_def = { |
703 | | .input = unicode_nf_check_sink_input, |
704 | | }; |
705 | | |
706 | | void unicode_nf_checker_init(struct unicode_nf_checker *unc_r, |
707 | | enum unicode_nf_type type) |
708 | 0 | { |
709 | 0 | i_zero(unc_r); |
710 | |
|
711 | 0 | switch (type) { |
712 | 0 | case UNICODE_NFD: |
713 | 0 | unc_r->canonical = TRUE; |
714 | 0 | unc_r->nf_qc_mask = UNICODE_NFD_QUICK_CHECK_MASK; |
715 | 0 | unc_r->nf_qc_yes = UNICODE_NFD_QUICK_CHECK_YES; |
716 | 0 | unc_r->nf_qc_no = UNICODE_NFD_QUICK_CHECK_NO; |
717 | 0 | break; |
718 | 0 | case UNICODE_NFKD: |
719 | 0 | unc_r->nf_qc_mask = UNICODE_NFKD_QUICK_CHECK_MASK; |
720 | 0 | unc_r->nf_qc_yes = UNICODE_NFKD_QUICK_CHECK_YES; |
721 | 0 | unc_r->nf_qc_no = UNICODE_NFKD_QUICK_CHECK_NO; |
722 | 0 | break; |
723 | 0 | case UNICODE_NFC: |
724 | 0 | unc_r->compose = TRUE; |
725 | 0 | unc_r->canonical = TRUE; |
726 | 0 | unc_r->nf_qc_mask = UNICODE_NFC_QUICK_CHECK_MASK; |
727 | 0 | unc_r->nf_qc_yes = UNICODE_NFC_QUICK_CHECK_YES; |
728 | 0 | unc_r->nf_qc_no = UNICODE_NFC_QUICK_CHECK_NO; |
729 | 0 | break; |
730 | 0 | case UNICODE_NFKC: |
731 | 0 | unc_r->compose = TRUE; |
732 | 0 | unc_r->nf_qc_mask = UNICODE_NFKC_QUICK_CHECK_MASK; |
733 | 0 | unc_r->nf_qc_yes = UNICODE_NFKC_QUICK_CHECK_YES; |
734 | 0 | unc_r->nf_qc_no = UNICODE_NFKC_QUICK_CHECK_NO; |
735 | 0 | break; |
736 | 0 | } |
737 | | |
738 | 0 | unicode_nf_init(&unc_r->nf, type); |
739 | 0 | unicode_transform_init(&unc_r->sink, &unicode_nf_check_sink_def); |
740 | 0 | unicode_transform_chain(&unc_r->nf.transform, &unc_r->sink); |
741 | 0 | } |
742 | | |
743 | | void unicode_nf_checker_reset(struct unicode_nf_checker *unc) |
744 | 0 | { |
745 | 0 | enum unicode_nf_type type = |
746 | 0 | (unc->compose ? (unc->canonical ? UNICODE_NFC : UNICODE_NFKC) : |
747 | 0 | (unc->canonical ? UNICODE_NFD : UNICODE_NFKD)); |
748 | |
|
749 | 0 | unicode_nf_checker_init(unc, type); |
750 | 0 | } |
751 | | |
752 | | static ssize_t |
753 | | unicode_nf_check_sink_input(struct unicode_transform *trans, |
754 | | const struct unicode_transform_buffer *buf, |
755 | | const char **error_r) |
756 | 0 | { |
757 | 0 | struct unicode_nf_checker *unc = |
758 | 0 | container_of(trans, struct unicode_nf_checker, sink); |
759 | 0 | size_t n; |
760 | |
|
761 | 0 | if (buf->cp_count > unc->buffer_len) { |
762 | 0 | *error_r = "Not normalized"; |
763 | 0 | return -1; |
764 | 0 | } |
765 | 0 | for (n = 0; n < buf->cp_count; n++) { |
766 | 0 | if (buf->cp[n] != unc->cp_buffer[n]) { |
767 | 0 | *error_r = "Not normalized"; |
768 | 0 | return -1; |
769 | 0 | } |
770 | 0 | } |
771 | 0 | unc->buffer_len -= buf->cp_count; |
772 | 0 | memmove(&unc->cp_buffer[0], &unc->cp_buffer[buf->cp_count], |
773 | 0 | unc->buffer_len * sizeof(unc->cp_buffer[0])); |
774 | 0 | return buf->cp_count; |
775 | 0 | } |
776 | | |
777 | | static int |
778 | | unicode_nf_checker_nf_input(struct unicode_nf_checker *unc, uint32_t cp, |
779 | | const struct unicode_code_point_data *cp_data) |
780 | 0 | { |
781 | 0 | struct unicode_transform_buffer buf; |
782 | 0 | const char *error; |
783 | 0 | ssize_t sret; |
784 | | |
785 | | /* Add the code point to the buffer first, since the normalization |
786 | | transform may output it immediately. */ |
787 | 0 | i_assert(unc->buffer_len < N_ELEMENTS(unc->cp_buffer)); |
788 | 0 | unc->cp_buffer[unc->buffer_len++] = cp; |
789 | 0 | unc->nf_used = TRUE; |
790 | |
|
791 | 0 | i_zero(&buf); |
792 | 0 | buf.cp = &cp; |
793 | 0 | buf.cp_data = &cp_data; |
794 | 0 | buf.cp_count = 1; |
795 | 0 | sret = unicode_transform_input_buf(&unc->nf.transform, &buf, &error); |
796 | 0 | if (sret < 0) |
797 | 0 | return -1; |
798 | 0 | i_assert(sret == 1); |
799 | 0 | return 0; |
800 | 0 | } |
801 | | |
802 | | static int unicode_nf_checker_nf_finish(struct unicode_nf_checker *unc) |
803 | 0 | { |
804 | 0 | const char *error; |
805 | 0 | int ret; |
806 | |
|
807 | 0 | if (!unc->nf_used) |
808 | 0 | return 1; |
809 | | |
810 | 0 | ret = unicode_transform_flush(&unc->nf.transform, &error); |
811 | 0 | i_assert(ret != 0); |
812 | 0 | if (ret < 0 || unc->buffer_len > 0) |
813 | 0 | return 0; |
814 | 0 | unicode_nf_reset(&unc->nf); |
815 | 0 | unc->nf_used = FALSE; |
816 | 0 | return 1; |
817 | 0 | } |
818 | | |
819 | | int unicode_nf_checker_input(struct unicode_nf_checker *unc, uint32_t cp, |
820 | | const struct unicode_code_point_data **_cp_data) |
821 | 0 | { |
822 | 0 | const struct unicode_code_point_data *cpd_last = unc->cpd_last; |
823 | |
|
824 | 0 | if (*_cp_data == NULL) |
825 | 0 | *_cp_data = unicode_code_point_get_data(cp); |
826 | |
|
827 | 0 | const struct unicode_code_point_data *cp_data = *_cp_data; |
828 | 0 | bool last_starter = unc->last_starter; |
829 | |
|
830 | 0 | unc->cpd_last = cp_data; |
831 | 0 | unc->last_starter = FALSE; |
832 | |
|
833 | 0 | if (cp_data->general_category == UNICODE_GENERAL_CATEGORY_INVALID) |
834 | 0 | return -1; |
835 | 0 | if ((cp_data->nf_quick_check & unc->nf_qc_mask) == unc->nf_qc_no) |
836 | 0 | return 0; |
837 | 0 | if (cpd_last != NULL && cp_data->canonical_combining_class != 0 && |
838 | 0 | cpd_last->canonical_combining_class > |
839 | 0 | cp_data->canonical_combining_class) |
840 | 0 | return 0; |
841 | 0 | if ((cp_data->nf_quick_check & unc->nf_qc_mask) == unc->nf_qc_yes && |
842 | 0 | cp_data->canonical_combining_class == 0) { |
843 | | /* Nothing after this starter interacts with anything before |
844 | | it. Finish checking the previous code points, and remember |
845 | | this one in case the next code point needs to be checked |
846 | | with the normalization transform. */ |
847 | 0 | if (unicode_nf_checker_nf_finish(unc) == 0) |
848 | 0 | return 0; |
849 | 0 | unc->last_cp = cp; |
850 | 0 | unc->last_starter = TRUE; |
851 | 0 | return 1; |
852 | 0 | } |
853 | | |
854 | 0 | if (last_starter && |
855 | 0 | unicode_nf_checker_nf_input(unc, unc->last_cp, cpd_last) < 0) |
856 | 0 | return 0; |
857 | 0 | if (unicode_nf_checker_nf_input(unc, cp, cp_data) < 0) |
858 | 0 | return 0; |
859 | 0 | return 1; |
860 | 0 | } |
861 | | |
862 | | int unicode_nf_checker_finish(struct unicode_nf_checker *unc) |
863 | 0 | { |
864 | 0 | return unicode_nf_checker_nf_finish(unc); |
865 | 0 | } |
866 | | |
867 | | /* |
868 | | * Casemap Transform |
869 | | */ |
870 | | |
871 | 0 | #define UNICODE_GREEK_CAPITAL_LETTER_SIGMA 0x03a3 |
872 | 0 | #define UNICODE_GREEK_SMALL_LETTER_FINAL_SIGMA 0x03c2 |
873 | | #define UNICODE_GREEK_SMALL_LETTER_SIGMA 0x03c3 |
874 | | |
875 | | static size_t |
876 | | unicode_casemap_uppercase_cp(const struct unicode_code_point_data *cp_data, |
877 | | const uint32_t **map_r); |
878 | | static size_t |
879 | | unicode_casemap_lowercase_cp(const struct unicode_code_point_data *cp_data, |
880 | | const uint32_t **map_r); |
881 | | static size_t |
882 | | unicode_casemap_casefold_cp(const struct unicode_code_point_data *cp_data, |
883 | | const uint32_t **map_r); |
884 | | |
885 | | static ssize_t |
886 | | unicode_casemap_input(struct unicode_transform *trans, |
887 | | const struct unicode_transform_buffer *buf, |
888 | | const char **error_r); |
889 | | static int |
890 | | unicode_casemap_flush(struct unicode_transform *trans, bool finished, |
891 | | const char **error_r); |
892 | | |
893 | | static const struct unicode_transform_def unicode_casemap_def = { |
894 | | .input = unicode_casemap_input, |
895 | | .flush = unicode_casemap_flush, |
896 | | }; |
897 | | |
898 | | void unicode_casemap_init_uppercase(struct unicode_casemap *map_r) |
899 | 0 | { |
900 | | /* The buffers are written before they're read */ |
901 | 0 | memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer)); |
902 | 0 | unicode_transform_init(&map_r->transform, &unicode_casemap_def); |
903 | 0 | map_r->map = unicode_casemap_uppercase_cp; |
904 | 0 | } |
905 | | |
906 | | void unicode_casemap_init_lowercase(struct unicode_casemap *map_r) |
907 | 0 | { |
908 | | /* The buffers are written before they're read */ |
909 | 0 | memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer)); |
910 | 0 | unicode_transform_init(&map_r->transform, &unicode_casemap_def); |
911 | 0 | map_r->map = unicode_casemap_lowercase_cp; |
912 | 0 | } |
913 | | |
914 | | void unicode_casemap_init_lowercase_final_sigma(struct unicode_casemap *map_r) |
915 | 0 | { |
916 | 0 | unicode_casemap_init_lowercase(map_r); |
917 | 0 | map_r->final_sigma = TRUE; |
918 | 0 | } |
919 | | |
920 | | void unicode_casemap_init_casefold(struct unicode_casemap *map_r) |
921 | 0 | { |
922 | | /* The buffers are written before they're read */ |
923 | 0 | memset(map_r, 0, offsetof(struct unicode_casemap, cp_buffer)); |
924 | 0 | unicode_transform_init(&map_r->transform, &unicode_casemap_def); |
925 | 0 | map_r->map = unicode_casemap_casefold_cp; |
926 | 0 | } |
927 | | |
928 | | static size_t |
929 | | unicode_casemap_uppercase_cp(const struct unicode_code_point_data *cp_data, |
930 | | const uint32_t **map_r) |
931 | 0 | { |
932 | 0 | return unicode_code_point_data_get_uppercase_mapping(cp_data, map_r); |
933 | 0 | } |
934 | | |
935 | | static size_t |
936 | | unicode_casemap_lowercase_cp(const struct unicode_code_point_data *cp_data, |
937 | | const uint32_t **map_r) |
938 | 0 | { |
939 | 0 | return unicode_code_point_data_get_lowercase_mapping(cp_data, map_r); |
940 | 0 | } |
941 | | |
942 | | static size_t |
943 | | unicode_casemap_casefold_cp(const struct unicode_code_point_data *cp_data, |
944 | | const uint32_t **map_r) |
945 | 0 | { |
946 | 0 | return unicode_code_point_data_get_casefold_mapping(cp_data, map_r); |
947 | 0 | } |
948 | | |
949 | | static void |
950 | | unicode_casemap_sigma_decide(struct unicode_casemap *map, |
951 | | bool followed_by_cased) |
952 | 0 | { |
953 | 0 | i_assert(map->sigma_pending); |
954 | 0 | i_assert(map->cp_buffer[map->sigma_pos] == |
955 | 0 | UNICODE_GREEK_SMALL_LETTER_SIGMA); |
956 | | |
957 | 0 | if (!followed_by_cased) { |
958 | 0 | map->cp_buffer[map->sigma_pos] = |
959 | 0 | UNICODE_GREEK_SMALL_LETTER_FINAL_SIGMA; |
960 | 0 | } |
961 | 0 | map->sigma_pending = FALSE; |
962 | 0 | } |
963 | | |
964 | | static bool |
965 | | unicode_casemap_input_cp(struct unicode_casemap *map, uint32_t cp, |
966 | | const struct unicode_code_point_data *cp_data) |
967 | 0 | { |
968 | 0 | unsigned int pos = map->buffer_len; |
969 | 0 | const uint32_t *map_cps; |
970 | 0 | size_t i, map_cps_len; |
971 | |
|
972 | 0 | if (cp_data == NULL) |
973 | 0 | cp_data = unicode_code_point_get_data(cp); |
974 | |
|
975 | 0 | if (map->sigma_pending && !cp_data->pb_c_case_ignorable) { |
976 | | /* This code point shows whether a cased letter follows the |
977 | | sigma */ |
978 | 0 | unicode_casemap_sigma_decide(map, cp_data->pb_c_cased); |
979 | 0 | } |
980 | |
|
981 | 0 | map_cps_len = map->map(cp_data, &map_cps); |
982 | 0 | if (map->buffer_len + I_MAX(map_cps_len, 1) > |
983 | 0 | UNICODE_CASEMAP_BUFFER_SIZE) { |
984 | 0 | if (map->sigma_pending && map->sigma_pos == 0) { |
985 | | /* Too many case-ignorable code points follow the |
986 | | sigma */ |
987 | 0 | unicode_casemap_sigma_decide(map, FALSE); |
988 | 0 | } |
989 | 0 | return FALSE; |
990 | 0 | } |
991 | | |
992 | 0 | if (map_cps_len == 0) { |
993 | 0 | map->cp_buffer[map->buffer_len] = cp; |
994 | 0 | map->cpd_buffer[map->buffer_len++] = cp_data; |
995 | 0 | } else { |
996 | 0 | for (i = 0; i < map_cps_len; i++) { |
997 | 0 | map->cp_buffer[map->buffer_len] = map_cps[i]; |
998 | 0 | map->cpd_buffer[map->buffer_len++] = NULL; |
999 | 0 | } |
1000 | 0 | } |
1001 | |
|
1002 | 0 | if (map->final_sigma && !cp_data->pb_c_case_ignorable) { |
1003 | 0 | if (cp == UNICODE_GREEK_CAPITAL_LETTER_SIGMA && |
1004 | 0 | map->preceded_by_cased) { |
1005 | | /* Keep the sigma until it's known whether a cased |
1006 | | letter follows it */ |
1007 | 0 | map->sigma_pending = TRUE; |
1008 | 0 | map->sigma_pos = pos; |
1009 | 0 | } |
1010 | 0 | map->preceded_by_cased = cp_data->pb_c_cased; |
1011 | 0 | } |
1012 | 0 | return TRUE; |
1013 | 0 | } |
1014 | | |
1015 | | static ssize_t |
1016 | | unicode_casemap_forward(struct unicode_casemap *map, const char **error_r) |
1017 | 0 | { |
1018 | 0 | unsigned int count = (map->sigma_pending ? |
1019 | 0 | map->sigma_pos : map->buffer_len); |
1020 | 0 | ssize_t sret; |
1021 | |
|
1022 | 0 | if (count == 0) |
1023 | 0 | return 0; |
1024 | | |
1025 | 0 | sret = uniform_transform_forward(&map->transform, map->cp_buffer, |
1026 | 0 | map->cpd_buffer, count, error_r); |
1027 | 0 | if (sret < 0) |
1028 | 0 | return -1; |
1029 | | |
1030 | 0 | map->buffer_len -= sret; |
1031 | 0 | memmove(map->cp_buffer, &map->cp_buffer[sret], |
1032 | 0 | map->buffer_len * sizeof(map->cp_buffer[0])); |
1033 | 0 | memmove(map->cpd_buffer, &map->cpd_buffer[sret], |
1034 | 0 | map->buffer_len * sizeof(map->cpd_buffer[0])); |
1035 | 0 | if (map->sigma_pending) |
1036 | 0 | map->sigma_pos -= sret; |
1037 | 0 | return sret; |
1038 | 0 | } |
1039 | | |
1040 | | static ssize_t |
1041 | | unicode_casemap_input(struct unicode_transform *trans, |
1042 | | const struct unicode_transform_buffer *buf, |
1043 | | const char **error_r) |
1044 | 0 | { |
1045 | 0 | struct unicode_casemap *map = |
1046 | 0 | container_of(trans, struct unicode_casemap, transform); |
1047 | 0 | ssize_t sret; |
1048 | 0 | size_t n = 0; |
1049 | |
|
1050 | 0 | while (n < buf->cp_count) { |
1051 | 0 | if (unicode_casemap_input_cp(map, buf->cp[n], |
1052 | 0 | (buf->cp_data == NULL ? |
1053 | 0 | NULL : buf->cp_data[n]))) { |
1054 | 0 | n++; |
1055 | 0 | continue; |
1056 | 0 | } |
1057 | | |
1058 | | /* The buffer is full */ |
1059 | 0 | sret = unicode_casemap_forward(map, error_r); |
1060 | 0 | if (sret < 0) |
1061 | 0 | return -1; |
1062 | 0 | if (sret == 0) { |
1063 | | /* The next transform is full */ |
1064 | 0 | break; |
1065 | 0 | } |
1066 | 0 | } |
1067 | | |
1068 | 0 | if (unicode_casemap_forward(map, error_r) < 0) |
1069 | 0 | return -1; |
1070 | 0 | return n; |
1071 | 0 | } |
1072 | | |
1073 | | static int |
1074 | | unicode_casemap_flush(struct unicode_transform *trans, bool finished, |
1075 | | const char **error_r) |
1076 | 0 | { |
1077 | 0 | struct unicode_casemap *map = |
1078 | 0 | container_of(trans, struct unicode_casemap, transform); |
1079 | |
|
1080 | 0 | if (finished && map->sigma_pending) { |
1081 | | /* No more input, so no cased letter follows the sigma */ |
1082 | 0 | unicode_casemap_sigma_decide(map, FALSE); |
1083 | 0 | } |
1084 | |
|
1085 | 0 | if (unicode_casemap_forward(map, error_r) < 0) |
1086 | 0 | return -1; |
1087 | 0 | if (map->buffer_len > 0) |
1088 | 0 | return 0; |
1089 | 0 | if (finished) { |
1090 | | /* The string ended */ |
1091 | 0 | map->preceded_by_cased = FALSE; |
1092 | 0 | } |
1093 | 0 | return 1; |
1094 | 0 | } |
1095 | | |
1096 | | /* |
1097 | | * RFC 5051 - Simple Unicode Collation Algorithm |
1098 | | */ |
1099 | | |
1100 | | void unicode_rfc5051_init(struct unicode_rfc5051_context *ctx) |
1101 | 0 | { |
1102 | 0 | i_zero(ctx); |
1103 | 0 | } |
1104 | | |
1105 | | size_t unicode_rfc5051_normalize(struct unicode_rfc5051_context *ctx, |
1106 | | uint32_t cp, const uint32_t **norm_r) |
1107 | 0 | { |
1108 | 0 | const struct unicode_code_point_data *cpd; |
1109 | 0 | size_t len; |
1110 | |
|
1111 | 0 | cpd = unicode_code_point_get_data(cp); |
1112 | 0 | if (cpd->simple_titlecase_mapping != 0x0000) |
1113 | 0 | cp = cpd->simple_titlecase_mapping; |
1114 | |
|
1115 | 0 | if (cp >= HANGUL_FIRST && cp <= HANGUL_LAST) { |
1116 | 0 | *norm_r = ctx->buffer; |
1117 | 0 | return unicode_hangul_decompose(cp, ctx->buffer); |
1118 | 0 | } |
1119 | | |
1120 | 0 | len = unicode_code_point_get_full_decomposition(cp, FALSE, norm_r); |
1121 | 0 | if (len == 0) { |
1122 | 0 | ctx->buffer[0] = cp; |
1123 | 0 | *norm_r = ctx->buffer; |
1124 | 0 | return 1; |
1125 | 0 | } |
1126 | 0 | return len; |
1127 | 0 | } |