Merge branch 'master' of github.com:mono/mono
[mono.git] / eglib / src / gunicode.c
1 /*
2  * gunicode.c: Some Unicode routines 
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@novell.com)
6  *
7  * (C) 2006 Novell, Inc.
8  *
9  * utf8 validation code came from:
10  *      libxml2-2.6.26 licensed under the MIT X11 license
11  *
12  * Authors credit in libxml's string.c:
13  *   William Brack <wbrack@mmm.com.hk>
14  *   daniel@veillard.com
15  *
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:
23  *
24  * The above copyright notice and this permission notice shall be
25  * included in all copies or substantial portions of the Software.
26  *
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.
34  *
35  */
36 #include <config.h>
37 #include <stdio.h>
38 #include <glib.h>
39 #include <unicode-data.h>
40 #include <errno.h>
41
42 #if defined(_MSC_VER) || defined(G_OS_WIN32)
43 /* FIXME */
44 #  define CODESET 1
45 #  include <windows.h>
46 #  ifdef _MSC_VER
47        typedef int iconv_t;
48 #  endif
49 #else
50 #    ifdef HAVE_LANGINFO_H
51 #       include <langinfo.h>
52 #    endif
53 #    ifdef HAVE_ICONV_H
54 #       include <iconv.h>
55 #    endif
56 #endif
57
58 static char *my_charset;
59 static gboolean is_utf8;
60
61 /*
62  * Character set conversion
63  */
64 /*
65 * Index into the table below with the first byte of a UTF-8 sequence to
66 * get the number of trailing bytes that are supposed to follow it.
67 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
68 * left as-is for anyone who may want to do such conversion, which was
69 * allowed in earlier algorithms.
70 */
71 const gchar g_trailingBytesForUTF8 [256] = {
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         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,
76         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,
77         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,
78         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,
79         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 };
81
82 /*
83 * Magic values subtracted from a buffer value during UTF8 conversion.
84 * This table contains as many values as there might be trailing bytes
85 * in a UTF-8 sequence.
86 */
87 static const gulong offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
88 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
89
90 GUnicodeType 
91 g_unichar_type (gunichar c)
92 {
93 int i;
94
95         guint16 cp = (guint16) c;
96         for (i = 0; i < unicode_category_ranges_count; i++) {
97                 if (cp < unicode_category_ranges [i].start)
98                         continue;
99                 if (unicode_category_ranges [i].end <= cp)
100                         continue;
101                 return unicode_category [i] [cp - unicode_category_ranges [i].start];
102         }
103
104         /*
105         // 3400-4DB5: OtherLetter
106         // 4E00-9FC3: OtherLetter
107         // AC00-D7A3: OtherLetter
108         // D800-DFFF: OtherSurrogate
109         // E000-F8FF: OtherPrivateUse
110         // 20000-2A6D6 OtherLetter
111         // F0000-FFFFD OtherPrivateUse
112         // 100000-10FFFD OtherPrivateUse
113         */
114         if (0x3400 <= cp && cp < 0x4DB5)
115                 return G_UNICODE_OTHER_LETTER;
116         if (0x4E00 <= cp && cp < 0x9FC3)
117                 return G_UNICODE_OTHER_LETTER;
118         if (0xAC00<= cp && cp < 0xD7A3)
119                 return G_UNICODE_OTHER_LETTER;
120         if (0xD800 <= cp && cp < 0xDFFF)
121                 return G_UNICODE_SURROGATE;
122         if (0xE000 <= cp && cp < 0xF8FF)
123                 return G_UNICODE_PRIVATE_USE;
124         /* since the argument is UTF-16, we cannot check beyond FFFF */
125
126         /* It should match any of above */
127         return 0;
128 }
129
130 gunichar
131 g_unichar_case (gunichar c, gboolean upper)
132 {
133         gint8 i, i2;
134         guint32 cp = (guint32) c, v;
135
136         for (i = 0; i < simple_case_map_ranges_count; i++) {
137                 if (cp < simple_case_map_ranges [i].start)
138                         return c;
139                 if (simple_case_map_ranges [i].end <= cp)
140                         continue;
141                 if (c < 0x10000) {
142                         const guint16 *tab = upper ? simple_upper_case_mapping_lowarea [i] : simple_lower_case_mapping_lowarea [i];
143                         v = tab [cp - simple_case_map_ranges [i].start];
144                 } else {
145                         const guint32 *tab;
146                         i2 = (gint8)(i - (upper ? simple_upper_case_mapping_lowarea_table_count : simple_lower_case_mapping_lowarea_table_count));
147                         tab = upper ? simple_upper_case_mapping_higharea [i2] : simple_lower_case_mapping_higharea [i2];
148                         v = tab [cp - simple_case_map_ranges [i].start];
149                 }
150                 return v != 0 ? (gunichar) v : c;
151         }
152         return c;
153 }
154
155 gunichar
156 g_unichar_toupper (gunichar c)
157 {
158         return g_unichar_case (c, TRUE);
159 }
160
161 gunichar
162 g_unichar_tolower (gunichar c)
163 {
164         return g_unichar_case (c, FALSE);
165 }
166
167 gunichar
168 g_unichar_totitle (gunichar c)
169 {
170         guint8 i;
171         guint32 cp;
172
173         cp = (guint32) c;
174         for (i = 0; i < simple_titlecase_mapping_count; i++) {
175                 if (simple_titlecase_mapping [i].codepoint == cp)
176                         return simple_titlecase_mapping [i].title;
177                 if (simple_titlecase_mapping [i].codepoint > cp)
178                         /* it is ordered, hence no more match */
179                         break;
180         }
181         return g_unichar_toupper (c);
182 }
183
184 gboolean
185 g_unichar_isxdigit (gunichar c)
186 {
187         return (g_unichar_xdigit_value (c) != -1);
188
189 }
190
191 gint
192 g_unichar_xdigit_value (gunichar c)
193 {
194         if (c >= 0x30 && c <= 0x39) /*0-9*/
195                 return (c - 0x30);
196         if (c >= 0x41 && c <= 0x46) /*A-F*/
197                 return (c - 0x37);
198         if (c >= 0x61 && c <= 0x66) /*a-f*/
199                 return (c - 0x57);
200         return -1;
201 }
202
203 gchar *
204 g_convert (const gchar *str, gssize len,
205            const gchar *to_codeset, const gchar *from_codeset,
206            gsize *bytes_read, gsize *bytes_written, GError **error)
207 {
208         char *result = NULL;
209 #ifdef G_OS_WIN32
210 #elif HAVE_ICONV_H
211         iconv_t convertor;
212         char *buffer, *output;
213         const char *strptr = (const char *) str;
214         size_t str_len = len == -1 ? strlen (str) : len;
215         size_t buffer_size;
216         size_t left, out_left;
217         
218         convertor = iconv_open (to_codeset, from_codeset);
219         if (convertor == (iconv_t) -1){
220                 *bytes_written = 0;
221                 *bytes_read = 0;
222                 return NULL;
223         }
224
225         buffer_size = str_len + 1 + 8;
226         buffer = g_malloc (buffer_size);
227         out_left = str_len;
228         output = buffer;
229         left = str_len;
230         while (left > 0){
231                 int res = iconv (convertor, (char **) &strptr, &left, &output, &out_left);
232                 if (res == (size_t) -1){
233                         if (errno == E2BIG){
234                                 char *n;
235                                 size_t extra_space = 8 + left;
236                                 size_t output_used = output - buffer;
237                                 
238                                 buffer_size += extra_space;
239                                 
240                                 n = g_realloc (buffer, buffer_size);
241                                 
242                                 if (n == NULL){
243                                         if (error != NULL)
244                                                 *error = g_error_new (NULL, G_CONVERT_ERROR_FAILED, "No memory left");
245                                         g_free (buffer);
246                                         result = NULL;
247                                         goto leave;
248                                 }
249                                 buffer = n;
250                                 out_left += extra_space;
251                                 output = buffer + output_used;
252                         } else if (errno == EILSEQ){
253                                 if (error != NULL)
254                                         *error = g_error_new (NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "Invalid multi-byte sequence on input");
255                                 result = NULL;
256                                 g_free (buffer);
257                                 goto leave;
258                         } else if (errno == EINVAL){
259                                 if (error != NULL)
260                                         *error = g_error_new (NULL, G_CONVERT_ERROR_PARTIAL_INPUT, "Partial character sequence");
261                                 result = NULL;
262                                 g_free (buffer);
263                                 goto leave;
264                         }
265                 } 
266         }
267         if (bytes_read != NULL)
268                 *bytes_read = strptr - str;
269         if (bytes_written != NULL)
270                 *bytes_written = output - buffer;
271         *output = 0;
272         result = buffer;
273  leave:
274         iconv_close (convertor);
275 #endif
276         return result;
277 }
278
279 /*
280  * This is broken, and assumes an UTF8 system, but will do for eglib's first user
281  */
282 gchar *
283 g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
284 {
285         char *res;
286         
287         if (len == -1)
288                 len = strlen (utf8string);
289
290         res = g_malloc (len + 1);
291         g_strlcpy (res, utf8string, len + 1);
292         return res;
293 }
294
295 gboolean
296 g_get_charset (G_CONST_RETURN char **charset)
297 {
298 #ifdef G_OS_WIN32
299         static char buf[14];
300         sprintf (buf, "CP%u", GetACP ());
301         *charset = buf;
302         is_utf8 = FALSE;
303 #else
304         if (my_charset == NULL){
305                 /* These shouldn't be heap allocated */
306 #if HAVE_LANGINFO_H
307                 my_charset = nl_langinfo (CODESET);
308 #else
309                 my_charset = "UTF-8";
310 #endif
311                 is_utf8 = strcmp (my_charset, "UTF-8") == 0;
312         }
313         
314         if (charset != NULL)
315                 *charset = my_charset;
316
317 #endif
318         return is_utf8;
319 }
320
321 gchar *
322 g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
323 {
324         g_get_charset (NULL);
325
326         return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error);
327 }
328
329 gchar *
330 g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
331 {
332         g_get_charset (NULL);
333
334         return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error);
335 }
336 /**
337  * g_utf8_validate
338  * @utf: Pointer to putative UTF-8 encoded string.
339  *
340  * Checks @utf for being valid UTF-8. @utf is assumed to be
341  * null-terminated. This function is not super-strict, as it will
342  * allow longer UTF-8 sequences than necessary. Note that Java is
343  * capable of producing these sequences if provoked. Also note, this
344  * routine checks for the 4-byte maximum size, but does not check for
345  * 0x10ffff maximum value.
346  *
347  * Return value: true if @utf is valid.
348  **/
349 gboolean
350 g_utf8_validate (const gchar *str, gssize max_len, const gchar **end)
351 {
352         gssize byteCount = 0;
353         gboolean retVal = TRUE;
354         gboolean lastRet = TRUE;
355         guchar* ptr = (guchar*) str;
356         guint length;
357         guchar a;
358         guchar* srcPtr;
359         if (max_len == 0)
360                 return 0;
361         else if (max_len < 0)
362                 byteCount = max_len;
363         while (*ptr != 0 && byteCount <= max_len) {
364                 length = g_trailingBytesForUTF8 [*ptr] + 1;
365                 srcPtr = (guchar*) ptr + length;
366                 switch (length) {
367                 default: retVal = FALSE;
368                 /* Everything else falls through when "TRUE"... */
369                 case 4: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
370                                 if ((a == (guchar) 0xBF || a == (guchar) 0xBE) && *(srcPtr-1) == (guchar) 0xBF) {
371                                 if (*(srcPtr-2) == (guchar) 0x8F || *(srcPtr-2) == (guchar) 0x9F ||
372                                         *(srcPtr-2) == (guchar) 0xAF || *(srcPtr-2) == (guchar) 0xBF)
373                                         retVal = FALSE;
374                                 }
375                 case 3: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
376                 case 2: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
377
378                 switch (*ptr) {
379                 /* no fall-through in this inner switch */
380                 case 0xE0: if (a < (guchar) 0xA0) retVal = FALSE; break;
381                 case 0xED: if (a > (guchar) 0x9F) retVal = FALSE; break;
382                 case 0xEF: if (a == (guchar)0xB7 && (*(srcPtr+1) > (guchar) 0x8F && *(srcPtr+1) < 0xB0)) retVal = FALSE;
383                                    if (a == (guchar)0xBF && (*(srcPtr+1) == (guchar) 0xBE || *(srcPtr+1) == 0xBF)) retVal = FALSE; break;
384                 case 0xF0: if (a < (guchar) 0x90) retVal = FALSE; break;
385                 case 0xF4: if (a > (guchar) 0x8F) retVal = FALSE; break;
386                 default:   if (a < (guchar) 0x80) retVal = FALSE;
387                 }
388
389                 case 1: if (*ptr >= (guchar ) 0x80 && *ptr < (guchar) 0xC2) retVal = FALSE;
390                 }
391                 if (*ptr > (guchar) 0xF4)
392                         retVal = FALSE;
393                 //If the string is invalid, set the end to the invalid byte.
394                 if (!retVal && lastRet) {
395                         if (end != NULL)
396                                 *end = (gchar*) ptr;
397                         lastRet = FALSE;
398                 }
399                 ptr += length;
400                 if(max_len > 0)
401                         byteCount += length;
402         }
403         if (retVal && end != NULL)
404                 *end = (gchar*) ptr;
405         return retVal;
406 }
407 /**
408  * g_utf8_get_char
409  * @src: Pointer to UTF-8 encoded character.
410  *
411  * Return value: UTF-16 value of @src
412  **/
413 gunichar
414 g_utf8_get_char (const gchar *src)
415 {
416         gunichar ch = 0;
417         guchar* ptr = (guchar*) src;
418         gushort extraBytesToRead = g_trailingBytesForUTF8 [*ptr];
419
420         switch (extraBytesToRead) {
421         case 5: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
422         case 4: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
423         case 3: ch += *ptr++; ch <<= 6;
424         case 2: ch += *ptr++; ch <<= 6;
425         case 1: ch += *ptr++; ch <<= 6;
426         case 0: ch += *ptr;
427         }
428         ch -= offsetsFromUTF8 [extraBytesToRead];
429         return ch;
430 }
431 glong
432 g_utf8_strlen (const gchar *str, gssize max)
433 {
434         gssize byteCount = 0;
435         guchar* ptr = (guchar*) str;
436         glong length = 0;
437         if (max == 0)
438                 return 0;
439         else if (max < 0)
440                 byteCount = max;
441         while (*ptr != 0 && byteCount <= max) {
442                 gssize cLen = g_trailingBytesForUTF8 [*ptr] + 1;
443                 if (max > 0 && (byteCount + cLen) > max)
444                         return length;
445                 ptr += cLen;
446                 length++;
447                 if (max > 0)
448                         byteCount += cLen;
449         }
450         return length;
451 }