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