1 /* Case mapping for UTF-8/UTF-16/UTF-32 strings (locale dependent).
2 Copyright (C) 2009 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2009.
5 This program is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Quoting the Unicode standard:
19 Definition: A character is defined to be "cased" if it has the Lowercase or
20 Uppercase property or has a General_Category value of Titlecase_Letter. */
24 return (uc_is_property_lowercase (uc)
25 || uc_is_property_uppercase (uc)
26 || uc_is_general_category (uc, UC_TITLECASE_LETTER));
29 /* Quoting the Unicode standard:
30 Definition: A character is defined to be "case-ignorable" if it has the
31 value MidLetter {or the value MidNumLet} for the Word_Break property or
32 its General_Category is one of Nonspacing_Mark (Mn), Enclosing_Mark (Me),
33 Format (Cf), Modifier_Letter (Lm), or Modifier_Symbol (Sk).
34 The text marked in braces was added in Unicode 5.1.0, see
35 <http://www.unicode.org/versions/Unicode5.1.0/> section "Update of
36 Definition of case-ignorable". */
38 is_case_ignorable (ucs4_t uc)
40 int wbp = uc_wordbreak_property (uc);
42 return (wbp == WBP_MIDLETTER || wbp == WBP_MIDNUMLET
43 || uc_is_general_category_withtable (uc, UC_CATEGORY_MASK_Mn
47 | UC_CATEGORY_MASK_Sk));
51 FUNC (const UNIT *s, size_t n, const char *iso639_language,
52 ucs4_t (*single_character_map) (ucs4_t),
53 size_t offset_in_rule, /* offset in 'struct special_casing_rule' */
55 UNIT *resultbuf, size_t *lengthp)
57 /* The result being accumulated. */
62 /* Initialize the accumulator. */
63 if (nf != NULL || resultbuf == NULL)
76 const UNIT *s_end = s + n;
78 /* Helper for evaluating the FINAL_SIGMA condition:
79 Last character that was not case-ignorable. */
80 ucs4_t last_char_except_ignorable = 0xFFFD;
82 /* Helper for evaluating the AFTER_SOFT_DOTTED and AFTER_I conditions:
83 Last character that was of combining class 230 ("Above") or 0. */
84 ucs4_t last_char_normal_or_above = 0xFFFD;
89 int count = U_MBTOUC_UNSAFE (&uc, s, s_end - s);
92 unsigned int mapped_count;
96 /* Look first in the special-casing table. */
99 code[0] = (uc >> 8) & 0xff;
102 for (code[2] = 0; ; code[2]++)
104 const struct special_casing_rule *rule =
105 gl_unicase_special_lookup (code, 3);
110 /* Test if the condition applies. */
111 /* Does the language apply? */
112 if (rule->language[0] == '\0'
113 || (iso639_language != NULL
114 && iso639_language[0] == rule->language[0]
115 && iso639_language[1] == rule->language[1]))
117 /* Does the context apply? */
118 int context = rule->context;
129 case SCC_FINAL_SIGMA:
130 /* "Before" condition: preceded by a sequence
131 consisting of a cased letter and a case-ignorable
133 "After" condition: not followed by a sequence
134 consisting of a case-ignorable sequence and then a
136 /* Test the "before" condition. */
137 applies = is_cased (last_char_except_ignorable);
138 /* Test the "after" condition. */
141 const UNIT *s2 = s + count;
145 int count2 = U_MBTOUC_UNSAFE (&uc2, s2, s_end - s2);
151 if (!is_case_ignorable (uc2))
158 case SCC_AFTER_SOFT_DOTTED:
159 /* "Before" condition: There is a Soft_Dotted character
160 before it, with no intervening character of
161 combining class 0 or 230 (Above). */
162 /* Test the "before" condition. */
163 applies = uc_is_property_soft_dotted (last_char_normal_or_above);
167 /* "After" condition: followed by a character of
168 combining class 230 (Above) with no intervening
169 character of combining class 0 or 230 (Above). */
170 /* Test the "after" condition. */
172 const UNIT *s2 = s + count;
177 int count2 = U_MBTOUC_UNSAFE (&uc2, s2, s_end - s2);
178 int ccc = uc_combining_class (uc2);
184 if (ccc == UC_CCC_NR)
192 /* "After" condition: followed by COMBINING DOT ABOVE
193 (U+0307). Any sequence of characters with a
194 combining class that is neither 0 nor 230 may
195 intervene between the current character and the
196 combining dot above. */
197 /* Test the "after" condition. */
199 const UNIT *s2 = s + count;
204 int count2 = U_MBTOUC_UNSAFE (&uc2, s2, s_end - s2);
205 if (uc2 == 0x0307) /* COMBINING DOT ABOVE */
211 int ccc = uc_combining_class (uc2);
212 if (ccc == UC_CCC_A || ccc == UC_CCC_NR)
221 /* "Before" condition: There is an uppercase I before
222 it, and there is no intervening character of
223 combining class 0 or 230 (Above). */
224 /* Test the "before" condition. */
225 applies = (last_char_normal_or_above == 'I');
231 if (rule->context < 0)
237 Look up the mapping (0 to 3 characters). */
238 const unsigned short *mapped_in_rule =
239 (const unsigned short *)((const char *)rule + offset_in_rule);
241 if (mapped_in_rule[0] == 0)
245 mapped_uc[0] = mapped_in_rule[0];
246 if (mapped_in_rule[1] == 0)
250 mapped_uc[1] = mapped_in_rule[1];
251 if (mapped_in_rule[2] == 0)
255 mapped_uc[2] = mapped_in_rule[2];
264 /* Optimization: Save a hash table lookup in the next round. */
270 /* No special-cased mapping. So use the locale and context independent
272 mapped_uc[0] = single_character_map (uc);
276 /* Found the mapping: uc maps to mapped_uc[0..mapped_count-1]. */
280 for (i = 0; i < mapped_count; i++)
282 ucs4_t muc = mapped_uc[i];
284 /* Append muc to the result accumulator. */
285 if (length < allocated)
287 int ret = U_UCTOMB (result + length, muc, allocated - length);
300 size_t old_allocated = allocated;
301 size_t new_allocated = 2 * old_allocated;
302 if (new_allocated < 64)
304 if (new_allocated < old_allocated) /* integer overflow? */
310 larger_result = (UNIT *) malloc (new_allocated * sizeof (UNIT));
311 if (larger_result == NULL)
317 else if (result == resultbuf)
319 larger_result = (UNIT *) malloc (new_allocated * sizeof (UNIT));
320 if (larger_result == NULL)
325 U_CPY (larger_result, resultbuf, length);
330 (UNIT *) realloc (result, new_allocated * sizeof (UNIT));
331 if (larger_result == NULL)
337 result = larger_result;
338 allocated = new_allocated;
340 int ret = U_UCTOMB (result + length, muc, allocated - length);
357 if (!is_case_ignorable (uc))
358 last_char_except_ignorable = uc;
361 int ccc = uc_combining_class (uc);
362 if (ccc == UC_CCC_A || ccc == UC_CCC_NR)
363 last_char_normal_or_above = uc;
372 /* Finally, normalize the result. */
373 UNIT *normalized_result;
375 normalized_result = U_NORMALIZE (nf, result, length, resultbuf, lengthp);
376 if (normalized_result == NULL)
380 return normalized_result;
387 /* Return a non-NULL value. NULL means error. */
388 result = (UNIT *) malloc (1);
396 else if (result != resultbuf && length < allocated)
398 /* Shrink the allocated memory if possible. */
401 memory = (UNIT *) realloc (result, length * sizeof (UNIT));
410 if (result != resultbuf)
412 int saved_errno = errno;