2 * gunicode.c: Some Unicode routines
5 * Miguel de Icaza (miguel@novell.com)
7 * (C) 2006 Novell, Inc.
9 * utf8 validation code came from:
10 * libxml2-2.6.26 licensed under the MIT X11 license
12 * Authors credit in libxml's string.c:
13 * William Brack <wbrack@mmm.com.hk>
16 * Permission is hereby granted, free of charge, to any person obtaining
17 * a copy of this software and associated documentation files (the
18 * "Software"), to deal in the Software without restriction, including
19 * without limitation the rights to use, copy, modify, merge, publish,
20 * distribute, sublicense, and/or sell copies of the Software, and to
21 * permit persons to whom the Software is furnished to do so, subject to
22 * the following conditions:
24 * The above copyright notice and this permission notice shall be
25 * included in all copies or substantial portions of the Software.
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 #include <unicode-data.h>
42 #if defined(_MSC_VER) || defined(G_OS_WIN32)
47 # ifdef HAVE_LANGINFO_H
48 # include <langinfo.h>
50 # ifdef HAVE_LOCALCHARSET_H
51 # include <localcharset.h>
55 static char *my_charset;
56 static gboolean is_utf8;
59 * Character set conversion
62 * Index into the table below with the first byte of a UTF-8 sequence to
63 * get the number of trailing bytes that are supposed to follow it.
64 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
65 * left as-is for anyone who may want to do such conversion, which was
66 * allowed in earlier algorithms.
68 const gchar g_trailingBytesForUTF8 [256] = {
69 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
70 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
71 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
72 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
73 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
74 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
75 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
76 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,0,0
80 * Magic values subtracted from a buffer value during UTF8 conversion.
81 * This table contains as many values as there might be trailing bytes
82 * in a UTF-8 sequence.
84 static const gulong offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
85 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
88 g_unichar_type (gunichar c)
92 guint16 cp = (guint16) c;
93 for (i = 0; i < unicode_category_ranges_count; i++) {
94 if (cp < unicode_category_ranges [i].start)
96 if (unicode_category_ranges [i].end <= cp)
98 return unicode_category [i] [cp - unicode_category_ranges [i].start];
102 // 3400-4DB5: OtherLetter
103 // 4E00-9FC3: OtherLetter
104 // AC00-D7A3: OtherLetter
105 // D800-DFFF: OtherSurrogate
106 // E000-F8FF: OtherPrivateUse
107 // 20000-2A6D6 OtherLetter
108 // F0000-FFFFD OtherPrivateUse
109 // 100000-10FFFD OtherPrivateUse
111 if (0x3400 <= cp && cp < 0x4DB5)
112 return G_UNICODE_OTHER_LETTER;
113 if (0x4E00 <= cp && cp < 0x9FC3)
114 return G_UNICODE_OTHER_LETTER;
115 if (0xAC00<= cp && cp < 0xD7A3)
116 return G_UNICODE_OTHER_LETTER;
117 if (0xD800 <= cp && cp < 0xDFFF)
118 return G_UNICODE_SURROGATE;
119 if (0xE000 <= cp && cp < 0xF8FF)
120 return G_UNICODE_PRIVATE_USE;
121 /* since the argument is UTF-16, we cannot check beyond FFFF */
123 /* It should match any of above */
128 g_unichar_break_type (gunichar c)
131 return G_UNICODE_BREAK_UNKNOWN;
135 g_unichar_case (gunichar c, gboolean upper)
138 guint32 cp = (guint32) c, v;
140 for (i = 0; i < simple_case_map_ranges_count; i++) {
141 if (cp < simple_case_map_ranges [i].start)
143 if (simple_case_map_ranges [i].end <= cp)
146 const guint16 *tab = upper ? simple_upper_case_mapping_lowarea [i] : simple_lower_case_mapping_lowarea [i];
147 v = tab [cp - simple_case_map_ranges [i].start];
150 i2 = (gint8)(i - (upper ? simple_upper_case_mapping_lowarea_table_count : simple_lower_case_mapping_lowarea_table_count));
151 tab = upper ? simple_upper_case_mapping_higharea [i2] : simple_lower_case_mapping_higharea [i2];
152 v = tab [cp - simple_case_map_ranges [i].start];
154 return v != 0 ? (gunichar) v : c;
160 g_unichar_toupper (gunichar c)
162 return g_unichar_case (c, TRUE);
166 g_unichar_tolower (gunichar c)
168 return g_unichar_case (c, FALSE);
172 g_unichar_totitle (gunichar c)
178 for (i = 0; i < simple_titlecase_mapping_count; i++) {
179 if (simple_titlecase_mapping [i].codepoint == cp)
180 return simple_titlecase_mapping [i].title;
181 if (simple_titlecase_mapping [i].codepoint > cp)
182 /* it is ordered, hence no more match */
185 return g_unichar_toupper (c);
189 g_unichar_isxdigit (gunichar c)
191 return (g_unichar_xdigit_value (c) != -1);
196 g_unichar_xdigit_value (gunichar c)
198 if (c >= 0x30 && c <= 0x39) /*0-9*/
200 if (c >= 0x41 && c <= 0x46) /*A-F*/
202 if (c >= 0x61 && c <= 0x66) /*a-f*/
208 g_unichar_isspace (gunichar c)
210 GUnicodeType type = g_unichar_type (c);
211 if (type == G_UNICODE_LINE_SEPARATOR ||
212 type == G_UNICODE_PARAGRAPH_SEPARATOR ||
213 type == G_UNICODE_SPACE_SEPARATOR)
220 g_convert (const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset,
221 gsize *bytes_read, gsize *bytes_written, GError **err)
223 size_t outsize, outused, outleft, inleft, grow, rc;
224 char *result, *outbuf, *inbuf;
225 gboolean flush = FALSE;
226 gboolean done = FALSE;
229 g_return_val_if_fail (str != NULL, NULL);
230 g_return_val_if_fail (to_charset != NULL, NULL);
231 g_return_val_if_fail (from_charset != NULL, NULL);
233 if ((cd = g_iconv_open (to_charset, from_charset)) == (GIConv) -1) {
234 g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION, g_strerror (ENOTSUP));
245 inleft = len < 0 ? strlen (str) : len;
246 inbuf = (char *) str;
248 outleft = outsize = MAX (inleft, 8);
249 outbuf = result = g_malloc (outsize + 4);
253 rc = g_iconv (cd, &inbuf, &inleft, &outbuf, &outleft);
255 rc = g_iconv (cd, NULL, NULL, &outbuf, &outleft);
257 if (rc == (size_t) -1) {
260 /* grow our result buffer */
261 grow = MAX (inleft, 8) << 1;
262 outused = outbuf - result;
265 result = g_realloc (result, outsize + 4);
266 outbuf = result + outused;
269 /* incomplete input, stop converting and terminate here */
276 /* illegal sequence in the input */
277 g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "%s", g_strerror (errno));
280 /* save offset of the illegal input sequence */
281 *bytes_read = (inbuf - str);
292 g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED, "%s", g_strerror (errno));
305 /* input has been converted and output has been flushed */
308 /* input has been converted, need to flush the output */
315 /* Note: not all charsets can be null-terminated with a single
316 null byte. UCS2, for example, needs 2 null bytes and UCS4
317 needs 4. I hope that 4 null bytes is enough to terminate all
318 multibyte charsets? */
320 /* null-terminate the result */
321 memset (outbuf, 0, 4);
324 *bytes_written = outbuf - result;
327 *bytes_read = inbuf - str;
333 * This is broken, and assumes an UTF8 system, but will do for eglib's first user
336 g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
341 len = strlen (utf8string);
343 res = g_malloc (len + 1);
344 g_strlcpy (res, utf8string, len + 1);
349 g_get_charset (G_CONST_RETURN char **charset)
351 if (my_charset == NULL) {
353 static char buf [14];
354 sprintf (buf, "CP%u", GetACP ());
358 /* These shouldn't be heap allocated */
359 #if HAVE_LOCALCHARSET_H
360 my_charset = locale_charset ();
361 #elif defined(HAVE_LANGINFO_H)
362 my_charset = nl_langinfo (CODESET);
364 my_charset = "UTF-8";
366 is_utf8 = strcmp (my_charset, "UTF-8") == 0;
371 *charset = my_charset;
377 g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
379 g_get_charset (NULL);
381 return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error);
385 g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
387 g_get_charset (NULL);
389 return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error);
393 * @utf: Pointer to putative UTF-8 encoded string.
395 * Checks @utf for being valid UTF-8. @utf is assumed to be
396 * null-terminated. This function is not super-strict, as it will
397 * allow longer UTF-8 sequences than necessary. Note that Java is
398 * capable of producing these sequences if provoked. Also note, this
399 * routine checks for the 4-byte maximum size, but does not check for
400 * 0x10ffff maximum value.
402 * Return value: true if @utf is valid.
405 g_utf8_validate (const gchar *str, gssize max_len, const gchar **end)
407 gssize byteCount = 0;
408 gboolean retVal = TRUE;
409 gboolean lastRet = TRUE;
410 guchar* ptr = (guchar*) str;
416 else if (max_len < 0)
418 while (*ptr != 0 && byteCount <= max_len) {
419 length = g_trailingBytesForUTF8 [*ptr] + 1;
420 srcPtr = (guchar*) ptr + length;
422 default: retVal = FALSE;
423 /* Everything else falls through when "TRUE"... */
424 case 4: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
425 if ((a == (guchar) 0xBF || a == (guchar) 0xBE) && *(srcPtr-1) == (guchar) 0xBF) {
426 if (*(srcPtr-2) == (guchar) 0x8F || *(srcPtr-2) == (guchar) 0x9F ||
427 *(srcPtr-2) == (guchar) 0xAF || *(srcPtr-2) == (guchar) 0xBF)
430 case 3: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
431 case 2: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
434 /* no fall-through in this inner switch */
435 case 0xE0: if (a < (guchar) 0xA0) retVal = FALSE; break;
436 case 0xED: if (a > (guchar) 0x9F) retVal = FALSE; break;
437 case 0xEF: if (a == (guchar)0xB7 && (*(srcPtr+1) > (guchar) 0x8F && *(srcPtr+1) < 0xB0)) retVal = FALSE;
438 if (a == (guchar)0xBF && (*(srcPtr+1) == (guchar) 0xBE || *(srcPtr+1) == 0xBF)) retVal = FALSE; break;
439 case 0xF0: if (a < (guchar) 0x90) retVal = FALSE; break;
440 case 0xF4: if (a > (guchar) 0x8F) retVal = FALSE; break;
441 default: if (a < (guchar) 0x80) retVal = FALSE;
444 case 1: if (*ptr >= (guchar ) 0x80 && *ptr < (guchar) 0xC2) retVal = FALSE;
446 if (*ptr > (guchar) 0xF4)
448 //If the string is invalid, set the end to the invalid byte.
449 if (!retVal && lastRet) {
458 if (retVal && end != NULL)
464 * @src: Pointer to UTF-8 encoded character.
466 * Return value: UTF-16 value of @src
469 g_utf8_get_char (const gchar *src)
472 guchar* ptr = (guchar*) src;
473 gushort extraBytesToRead = g_trailingBytesForUTF8 [*ptr];
475 switch (extraBytesToRead) {
476 case 5: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
477 case 4: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
478 case 3: ch += *ptr++; ch <<= 6;
479 case 2: ch += *ptr++; ch <<= 6;
480 case 1: ch += *ptr++; ch <<= 6;
483 ch -= offsetsFromUTF8 [extraBytesToRead];
487 g_utf8_strlen (const gchar *str, gssize max)
489 gssize byteCount = 0;
490 guchar* ptr = (guchar*) str;
496 while (*ptr != 0 && byteCount <= max) {
497 gssize cLen = g_trailingBytesForUTF8 [*ptr] + 1;
498 if (max > 0 && (byteCount + cLen) > max)