Fix typo
[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                 if (bytes_written)
221                         *bytes_written = 0;
222                 if (bytes_read)
223                         *bytes_read = 0;
224                 return NULL;
225         }
226
227         buffer_size = str_len + 1 + 8;
228         buffer = g_malloc (buffer_size);
229         out_left = str_len;
230         output = buffer;
231         left = str_len;
232         while (left > 0){
233                 int res = iconv (convertor, (char **) &strptr, &left, &output, &out_left);
234                 if (res == (size_t) -1){
235                         if (errno == E2BIG){
236                                 char *n;
237                                 size_t extra_space = 8 + left;
238                                 size_t output_used = output - buffer;
239                                 
240                                 buffer_size += extra_space;
241                                 
242                                 n = g_realloc (buffer, buffer_size);
243                                 
244                                 if (n == NULL){
245                                         if (error != NULL)
246                                                 *error = g_error_new (NULL, G_CONVERT_ERROR_FAILED, "No memory left");
247                                         g_free (buffer);
248                                         result = NULL;
249                                         goto leave;
250                                 }
251                                 buffer = n;
252                                 out_left += extra_space;
253                                 output = buffer + output_used;
254                         } else if (errno == EILSEQ){
255                                 if (error != NULL)
256                                         *error = g_error_new (NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "Invalid multi-byte sequence on input");
257                                 result = NULL;
258                                 g_free (buffer);
259                                 goto leave;
260                         } else if (errno == EINVAL){
261                                 if (error != NULL)
262                                         *error = g_error_new (NULL, G_CONVERT_ERROR_PARTIAL_INPUT, "Partial character sequence");
263                                 result = NULL;
264                                 g_free (buffer);
265                                 goto leave;
266                         }
267                 } 
268         }
269         if (bytes_read != NULL)
270                 *bytes_read = strptr - str;
271         if (bytes_written != NULL)
272                 *bytes_written = output - buffer;
273         *output = 0;
274         result = buffer;
275  leave:
276         iconv_close (convertor);
277 #endif
278         return result;
279 }
280
281 /*
282  * This is broken, and assumes an UTF8 system, but will do for eglib's first user
283  */
284 gchar *
285 g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
286 {
287         char *res;
288         
289         if (len == -1)
290                 len = strlen (utf8string);
291
292         res = g_malloc (len + 1);
293         g_strlcpy (res, utf8string, len + 1);
294         return res;
295 }
296
297 gboolean
298 g_get_charset (G_CONST_RETURN char **charset)
299 {
300 #ifdef G_OS_WIN32
301         static char buf[14];
302         sprintf (buf, "CP%u", GetACP ());
303         *charset = buf;
304         is_utf8 = FALSE;
305 #else
306         if (my_charset == NULL){
307                 /* These shouldn't be heap allocated */
308 #if HAVE_LANGINFO_H
309                 my_charset = nl_langinfo (CODESET);
310 #else
311                 my_charset = "UTF-8";
312 #endif
313                 is_utf8 = strcmp (my_charset, "UTF-8") == 0;
314         }
315         
316         if (charset != NULL)
317                 *charset = my_charset;
318
319 #endif
320         return is_utf8;
321 }
322
323 gchar *
324 g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
325 {
326         g_get_charset (NULL);
327
328         return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error);
329 }
330
331 gchar *
332 g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
333 {
334         g_get_charset (NULL);
335
336         return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error);
337 }
338 /**
339  * g_utf8_validate
340  * @utf: Pointer to putative UTF-8 encoded string.
341  *
342  * Checks @utf for being valid UTF-8. @utf is assumed to be
343  * null-terminated. This function is not super-strict, as it will
344  * allow longer UTF-8 sequences than necessary. Note that Java is
345  * capable of producing these sequences if provoked. Also note, this
346  * routine checks for the 4-byte maximum size, but does not check for
347  * 0x10ffff maximum value.
348  *
349  * Return value: true if @utf is valid.
350  **/
351 gboolean
352 g_utf8_validate (const gchar *str, gssize max_len, const gchar **end)
353 {
354         gssize byteCount = 0;
355         gboolean retVal = TRUE;
356         gboolean lastRet = TRUE;
357         guchar* ptr = (guchar*) str;
358         guint length;
359         guchar a;
360         guchar* srcPtr;
361         if (max_len == 0)
362                 return 0;
363         else if (max_len < 0)
364                 byteCount = max_len;
365         while (*ptr != 0 && byteCount <= max_len) {
366                 length = g_trailingBytesForUTF8 [*ptr] + 1;
367                 srcPtr = (guchar*) ptr + length;
368                 switch (length) {
369                 default: retVal = FALSE;
370                 /* Everything else falls through when "TRUE"... */
371                 case 4: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
372                                 if ((a == (guchar) 0xBF || a == (guchar) 0xBE) && *(srcPtr-1) == (guchar) 0xBF) {
373                                 if (*(srcPtr-2) == (guchar) 0x8F || *(srcPtr-2) == (guchar) 0x9F ||
374                                         *(srcPtr-2) == (guchar) 0xAF || *(srcPtr-2) == (guchar) 0xBF)
375                                         retVal = FALSE;
376                                 }
377                 case 3: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
378                 case 2: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
379
380                 switch (*ptr) {
381                 /* no fall-through in this inner switch */
382                 case 0xE0: if (a < (guchar) 0xA0) retVal = FALSE; break;
383                 case 0xED: if (a > (guchar) 0x9F) retVal = FALSE; break;
384                 case 0xEF: if (a == (guchar)0xB7 && (*(srcPtr+1) > (guchar) 0x8F && *(srcPtr+1) < 0xB0)) retVal = FALSE;
385                                    if (a == (guchar)0xBF && (*(srcPtr+1) == (guchar) 0xBE || *(srcPtr+1) == 0xBF)) retVal = FALSE; break;
386                 case 0xF0: if (a < (guchar) 0x90) retVal = FALSE; break;
387                 case 0xF4: if (a > (guchar) 0x8F) retVal = FALSE; break;
388                 default:   if (a < (guchar) 0x80) retVal = FALSE;
389                 }
390
391                 case 1: if (*ptr >= (guchar ) 0x80 && *ptr < (guchar) 0xC2) retVal = FALSE;
392                 }
393                 if (*ptr > (guchar) 0xF4)
394                         retVal = FALSE;
395                 //If the string is invalid, set the end to the invalid byte.
396                 if (!retVal && lastRet) {
397                         if (end != NULL)
398                                 *end = (gchar*) ptr;
399                         lastRet = FALSE;
400                 }
401                 ptr += length;
402                 if(max_len > 0)
403                         byteCount += length;
404         }
405         if (retVal && end != NULL)
406                 *end = (gchar*) ptr;
407         return retVal;
408 }
409 /**
410  * g_utf8_get_char
411  * @src: Pointer to UTF-8 encoded character.
412  *
413  * Return value: UTF-16 value of @src
414  **/
415 gunichar
416 g_utf8_get_char (const gchar *src)
417 {
418         gunichar ch = 0;
419         guchar* ptr = (guchar*) src;
420         gushort extraBytesToRead = g_trailingBytesForUTF8 [*ptr];
421
422         switch (extraBytesToRead) {
423         case 5: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
424         case 4: ch += *ptr++; ch <<= 6; // remember, illegal UTF-8
425         case 3: ch += *ptr++; ch <<= 6;
426         case 2: ch += *ptr++; ch <<= 6;
427         case 1: ch += *ptr++; ch <<= 6;
428         case 0: ch += *ptr;
429         }
430         ch -= offsetsFromUTF8 [extraBytesToRead];
431         return ch;
432 }
433 glong
434 g_utf8_strlen (const gchar *str, gssize max)
435 {
436         gssize byteCount = 0;
437         guchar* ptr = (guchar*) str;
438         glong length = 0;
439         if (max == 0)
440                 return 0;
441         else if (max < 0)
442                 byteCount = max;
443         while (*ptr != 0 && byteCount <= max) {
444                 gssize cLen = g_trailingBytesForUTF8 [*ptr] + 1;
445                 if (max > 0 && (byteCount + cLen) > max)
446                         return length;
447                 ptr += cLen;
448                 length++;
449                 if (max > 0)
450                         byteCount += cLen;
451         }
452         return length;
453 }