Fix msvc build.
[mono.git] / eglib / src / gutf8.c
index 77f96bedbb6141a35917c80573c078acec8e2e5c..ab6c12ea69c1fc5ad614df6e2e98b73310cf008b 100644 (file)
@@ -21,6 +21,40 @@ g_convert_error_quark ()
        return error_quark;
 }
 
+static gunichar*
+utf8_case_conv (const gchar *str, gssize len, gboolean upper)
+{
+       glong i, u16len, u32len;
+       gunichar2 *u16str;
+       gunichar *u32str;
+       gchar *u8str;
+       GError **err = NULL;
+
+       u16str = g_utf8_to_utf16 (str, (glong)len, NULL, &u16len, err);
+       u32str = g_utf16_to_ucs4 (u16str, u16len, NULL, &u32len, err);
+       for (i = 0; i < u32len; i++) {
+               u32str [i] = upper ? g_unichar_toupper (u32str [i]) : g_unichar_tolower (u32str [i]);
+       }
+       g_free (u16str);
+       u16str = g_ucs4_to_utf16 (u32str, u32len, NULL, &u16len, err);
+       u8str = g_utf16_to_utf8 (u16str, u16len, NULL, NULL, err);
+       g_free (u32str);
+       g_free (u16str);
+       return (gunichar*)u8str;
+}
+
+gchar*
+g_utf8_strup (const gchar *str, gssize len)
+{
+       return (gchar*)utf8_case_conv (str, len, TRUE);
+}
+
+gchar*
+g_utf8_strdown (const gchar *str, gssize len)
+{
+       return (gchar*)utf8_case_conv (str, len, FALSE);
+}
+
 gunichar2*
 g_utf8_to_utf16 (const gchar *str, glong len, glong *items_read, glong *items_written, GError **error)
 {
@@ -86,12 +120,12 @@ g_utf8_to_utf16 (const gchar *str, glong len, glong *items_read, glong *items_wr
                                if (--mb_remain == 0) {
                                        /* multi byte character is fully consumed now. */
                                        if (codepoint < 0x10000) {
-                                               ret [out_pos++] = codepoint % 0x10000;
+                                               ret [out_pos++] = (gunichar2)(codepoint % 0x10000);
                                        } else if (codepoint < 0x110000) {
                                                /* surrogate pair */
                                                codepoint -= 0x10000;
-                                               ret [out_pos++] = (codepoint >> 10) + 0xD800;
-                                               ret [out_pos++] = (codepoint & 0x3FF) + 0xDC00;
+                                               ret [out_pos++] = (gunichar2)((codepoint >> 10) + 0xD800);
+                                               ret [out_pos++] = (gunichar2)((codepoint & 0x3FF) + 0xDC00);
                                        } else {
                                                /* invalid utf-8 sequence (excess) */
                                                codepoint = 0;
@@ -247,7 +281,7 @@ g_utf16_to_utf8 (const gunichar2 *str, glong len, glong *items_read, glong *item
        gchar *ret;
        glong in_pos, out_pos;
        gunichar2 ch;
-       guint32 codepoint;
+       guint32 codepoint = 0;
        gboolean surrogate;
 
        in_pos = 0;
@@ -268,18 +302,20 @@ g_utf16_to_utf8 (const gunichar2 *str, glong len, glong *items_read, glong *item
        while (len < 0 ? str [in_pos] : in_pos < len) {
                ch = str [in_pos];
                if (surrogate) {
-                       surrogate = 0;
-                       if (ch >= 0xDC00 && ch <= 0xDFFF)
+                       if (ch >= 0xDC00 && ch <= 0xDFFF) {
                                codepoint = 0x10000 + (ch - 0xDC00) + ((surrogate - 0xD800) << 10);
-                       else
+                               surrogate = 0;
+                       } else {
+                               surrogate = 0;
                                /* invalid surrogate pair */
                                continue;
+                       }
                } else {
                        /* fast path optimization */
                        if (ch < 0x80) {
                                for (; len < 0 ? str [in_pos] : in_pos < len; in_pos++) {
                                        if (str [in_pos] < 0x80)
-                                               ret [out_pos++] = str [in_pos];
+                                               ret [out_pos++] = (gchar)(str [in_pos]);
                                        else
                                                break;
                                }
@@ -296,6 +332,8 @@ g_utf16_to_utf8 (const gunichar2 *str, glong len, glong *items_read, glong *item
                }
                in_pos++;
 
+               if (surrogate != 0)
+                       continue;
                if (codepoint < 0x80)
                        ret [out_pos++] = (gchar) codepoint;
                else if (codepoint < 0x0800) {
@@ -379,3 +417,171 @@ utf16_to_utf8_len (const gunichar2 *str, glong len, glong *items_read, GError **
                *items_read = in_pos;
        return ret;
 }
+
+static glong
+g_ucs4_to_utf16_len (const gunichar *str, glong len, glong *items_read, GError **error)
+{
+       glong retlen = 0;
+       glong errindex = 0;
+       const gunichar *lstr = str;
+
+       if (!str)
+               return 0;
+
+       while (*lstr != '\0' && len--) {
+               gunichar ch;
+               ch = *lstr++;
+               if (ch <= 0x0000FFFF) { 
+                       if (ch >= 0xD800 && ch <= 0xDFFF) {
+                               errindex = (glong)(lstr - str)-1;
+                               if (error)
+                                       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                                       "Invalid sequence in conversion input");
+                               if (items_read)
+                                       *items_read = errindex;
+                               return 0;
+                       } else {
+                               retlen++;
+                       }
+               } else if (ch > 0x10FFFF) {
+                       errindex = (glong)(lstr - str)-1;
+                       if (error)
+                               g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                               "Character out of range for UTF-16");
+                       if (items_read)
+                               *items_read = errindex;
+                       return 0;
+
+               } else {
+                       retlen+=2;
+               }
+       }
+
+       if (items_read)
+               *items_read = (glong)(lstr - str);
+       return retlen;
+}
+
+gunichar2*
+g_ucs4_to_utf16 (const gunichar *str, glong len, glong *items_read, glong *items_written, GError **error)
+{
+       glong allocsz;
+       gunichar2 *retstr = 0;
+       gunichar2 *retch = 0;
+       glong nwritten = 0;
+       GError *lerror =0 ;
+
+       allocsz = g_ucs4_to_utf16_len (str, len, items_read, &lerror);
+
+       if (!lerror) {
+               retch = retstr = g_malloc ((allocsz+1) * sizeof (gunichar2));
+               retstr[allocsz] = '\0';
+
+               while (*str != '\0' && len--) {
+                       gunichar ch;
+                       ch = *str++;
+                       if (ch <= 0x0000FFFF && (ch < 0xD800 || ch > 0xDFFF)) {
+                               *retch++ = (gunichar2)ch;
+                               nwritten ++;
+                       } else {
+                               ch -= 0x0010000UL;
+                               *retch++ = (gunichar2)((ch >> 10) + 0xD800);
+                               *retch++ = (gunichar2)((ch & 0x3FFUL) + 0xDC00);
+                               nwritten +=2;
+                       }
+               }
+       }
+
+       if (items_written)
+               *items_written = nwritten;
+       if (error)
+               *error = lerror;
+
+       return retstr;
+}
+
+static glong
+g_utf16_to_ucs4_len (const gunichar2 *str, glong len, glong *items_read, GError **error)
+{
+       glong retlen = 0;
+       glong errindex = 0;
+       const gunichar2 *lstr = str;
+       gunichar2 ch,ch2;
+
+       if (!str)
+               return 0;
+
+       while (*lstr != '\0' && len--) {
+               ch = *lstr++;
+               if (ch >= 0xD800 && ch <= 0xDBFF) {
+                       if (!len--) {
+                               lstr--;
+                               break;
+                       }
+                       ch2 = *lstr;
+                       if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
+                               lstr++;
+                       } else {
+                               errindex = (glong)(lstr - str);
+                               if (error)
+                                       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                                       "Invalid sequence in conversion input");
+                               if (items_read)
+                                       *items_read = errindex;
+                               return 0;
+                       }
+               } else {
+                       if (ch >= 0xDC00 && ch <= 0xDFFF) {
+                               errindex = (glong)(lstr - str)-1;
+                               if (error)
+                                       g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                                       "Invalid sequence in conversion input");
+                               if (items_read)
+                                       *items_read = errindex;
+                               return 0;
+                       }
+               }
+               retlen++;
+       }
+
+       if (items_read)
+               *items_read = (glong)(lstr - str);
+
+       return retlen;
+}
+
+gunichar*
+g_utf16_to_ucs4 (const gunichar2 *str, glong len, glong *items_read, glong *items_written, GError **error)
+{
+       glong allocsz;
+       gunichar *retstr = 0;
+       gunichar *retch = 0;
+       glong nwritten = 0;
+       GError *lerror =0 ;
+       gunichar ch,ch2;
+
+       allocsz = g_utf16_to_ucs4_len (str, len, items_read, &lerror);
+
+       if (!lerror) {
+               retch = retstr = g_malloc ((allocsz+1) * sizeof (gunichar));
+               retstr[allocsz] = '\0';
+               nwritten = allocsz;
+
+               while (*str != '\0' && allocsz--) {
+                       ch = *str++;
+                       if (ch >= 0xD800 && ch <= 0xDBFF) {
+                               ch2 = *str++;
+                               ch = ((ch - (gunichar)0xD800) << 10)
+                                     + (ch2 - (gunichar)0xDC00) + (gunichar)0x0010000UL;
+                       }
+                       *retch++ = ch;
+               }
+       }
+
+       if (items_written)
+               *items_written = nwritten;
+       if (error)
+               *error = lerror;
+
+       return retstr;
+}