2002-05-15 Radek Doulik <rodo@ximian.com>
[mono.git] / mono / metadata / unicode.c
index ba57a96d9d7bdd628913db5896a1df2880182e3d..b04230c2d48ef212ce226deaec2fd7931661dada 100644 (file)
 #include <mono/metadata/object.h>
 #include <mono/metadata/unicode.h>
 
-#include <iconv.h>
+#if HAVE_ICONV_H
+#include <iconv.h> 
+#elif HAVE_GICONV_H
+#include <giconv.h> 
+#endif
 
 static MonoUnicodeCategory catmap[] = {
        /* G_UNICODE_CONTROL = */              Control,
@@ -94,7 +98,10 @@ ves_icall_System_Char_IsUpper (gunichar2 c)
 gboolean 
 ves_icall_System_Char_IsNumber (gunichar2 c)
 {
-       return g_unichar_isdigit (c);
+       GUnicodeType t = g_unichar_type (c);
+       return t == G_UNICODE_DECIMAL_NUMBER ||
+               t == G_UNICODE_LETTER_NUMBER ||
+               t == G_UNICODE_OTHER_NUMBER;
 }
 
 gboolean 
@@ -154,18 +161,24 @@ ves_icall_iconv_new_encoder (MonoString *name, MonoBoolean big_endian)
        iconv_t cd;
        char *n;
 
-       // fixme: add support big_endian
+       // fixme: don't enforce big endian, support old iconv
 
        g_assert (name);
 
        n = mono_string_to_utf8 (name);
 
+       /* force big endian before class libraries are fixed */
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+       big_endian = 1;
+#endif 
+
 #ifdef HAVE_NEW_ICONV
-       cd = iconv_open (n, "UTF-16le");
+       cd = iconv_open (n, big_endian ? "UTF-16be" : "UTF-16le");
 #else
        cd = iconv_open (n, "UTF-16");
 #endif
        g_assert (cd != (iconv_t)-1);
+       g_free (n);
 
        return (gpointer)cd;
 }
@@ -176,18 +189,24 @@ ves_icall_iconv_new_decoder (MonoString *name, MonoBoolean big_endian)
        iconv_t cd;
        char *n;
 
-       // fixme: add support big_endian
+       // fixme: don't enforce big endian, support old iconv
 
        g_assert (name);
 
        n = mono_string_to_utf8 (name);
 
+       /* force big endian before class libraries are fixed */
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+       big_endian = 1;
+#endif 
+
 #ifdef HAVE_NEW_ICONV
-       cd = iconv_open ("UTF-16le", n);
+       cd = iconv_open (big_endian ? "UTF-16be" : "UTF-16le", n);
 #else
        cd = iconv_open ("UTF-16", n);
 #endif
        g_assert (cd != (iconv_t)-1);
+       g_free (n);
 
        return (gpointer)cd;
 }
@@ -203,10 +222,10 @@ ves_icall_iconv_reset (gpointer converter)
 }
 
 static int
-iconv_get_length (iconv_t cd, guchar *src, int len)
+iconv_get_length (iconv_t cd, guchar *src, int len, gboolean encode)
 {
        guchar buf [512];
-       int i, res;
+       int res;
        guchar *outp;
        guchar *p;
        guint inbytes_remaining;
@@ -214,12 +233,14 @@ iconv_get_length (iconv_t cd, guchar *src, int len)
        guint outbuf_size;
        gboolean have_error = FALSE;
        size_t err;
-
+       
        g_assert (cd);
        g_assert (src);
 
 #ifndef HAVE_NEW_ICONV
-       if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
+       if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode) {
+               int i;
+               
                src = g_memdup (src, len);
                for (i = 0; i < len; i += 2) {
                        char t = src [i];
@@ -238,7 +259,7 @@ again:
        outbytes_remaining = outbuf_size;
        outp = buf;
 
-       err = iconv (cd, (const char **)&p, &inbytes_remaining, 
+       err = iconv (cd, (char **)&p, &inbytes_remaining, 
                     (char **)&outp, &outbytes_remaining);
 
        if(err == (size_t)-1) {
@@ -268,7 +289,7 @@ again:
        }
 
 #ifndef HAVE_NEW_ICONV
-       if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+       if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode)
                g_free (src);
 #endif
 
@@ -281,7 +302,7 @@ again:
 }
 
 int
-ves_icall_iconv_get_byte_count (gpointer converter, MonoArray *chars, gint32 index, gint32 count)
+ves_icall_iconv_get_byte_count (gpointer converter, MonoArray *chars, gint32 idx, gint32 count)
 {
        iconv_t cd = (iconv_t)converter;
        guchar *src;
@@ -289,21 +310,20 @@ ves_icall_iconv_get_byte_count (gpointer converter, MonoArray *chars, gint32 ind
 
        g_assert (cd);
        g_assert (chars);
-       g_assert (mono_array_length (chars) > index);
-       g_assert (mono_array_length (chars) >= (index + count));
+       g_assert (mono_array_length (chars) > idx);
+       g_assert (mono_array_length (chars) >= (idx + count));
 
-       if (!(len = (mono_array_length (chars) - index) * 2))
+       if (!(len = (mono_array_length (chars) - idx) * 2))
                return 0;
 
-       src =  mono_array_addr (chars, guint16, index);
+       src =  mono_array_addr (chars, guint16, idx);
 
-       return iconv_get_length (cd, src, len);
+       return iconv_get_length (cd, src, len, TRUE);
 }
 
 static int
-iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len)
+iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len, gboolean encode)
 {
-       int i;
        guchar *p, *outp;
        guint inbytes_remaining;
        guint outbytes_remaining;
@@ -316,7 +336,9 @@ iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len)
        g_assert (dest);
 
 #ifndef HAVE_NEW_ICONV
-       if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
+       if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode) {
+               int i;
+               
                src = g_memdup (src, len);
                for (i = 0; i < len; i += 2) {
                        char t = src [i];
@@ -333,7 +355,7 @@ iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len)
        outbytes_remaining = outbuf_size;
        outp = dest;
 
-       err = iconv (cd, (const char **)&p, &inbytes_remaining, (char **)&outp, &outbytes_remaining);
+       err = iconv (cd, (char **)&p, &inbytes_remaining, (char **)&outp, &outbytes_remaining);
 
        if(err == (size_t)-1) {
                if (errno == EINVAL) {
@@ -350,8 +372,19 @@ iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len)
        }
 
 #ifndef HAVE_NEW_ICONV
-       if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
-               g_free (src);
+       if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
+               if (encode) {
+                       g_free (src);
+               } else {
+                       int mb = max_len - outbytes_remaining;
+                       int i;
+                       for (i = 0; i < mb; i+=2) {
+                               char t = dest [i];
+                               dest [i] = dest [i + 1];
+                               dest [i + 1] = t;
+                       }
+               }
+}
 #endif
        if (have_error) {
                g_assert_not_reached ();
@@ -389,27 +422,24 @@ ves_icall_iconv_get_bytes (gpointer converter, MonoArray *chars, gint32 charInde
 
        max_len = mono_array_length (bytes) - byteIndex;
 
-       return iconv_convert (cd, src, len, dest, max_len);
+       return iconv_convert (cd, src, len, dest, max_len, TRUE);
 }
 
 int
-ves_icall_iconv_get_char_count (gpointer converter, MonoArray *bytes, gint32 index, gint32 count)
+ves_icall_iconv_get_char_count (gpointer converter, MonoArray *bytes, gint32 idx, gint32 count)
 {
        iconv_t cd = (iconv_t)converter;
        guchar *src;
-       int len;
 
        g_assert (cd);
        g_assert (bytes);
-       g_assert (mono_array_length (bytes) > index);
-       g_assert (mono_array_length (bytes) >= (index + count));
+       g_assert (mono_array_length (bytes) > idx);
+       g_assert (mono_array_length (bytes) >= (idx + count));
 
-       if (!(len = (mono_array_length (bytes) - index)))
-               return 0;
-
-       src =  mono_array_addr (bytes, char, index);
+       src =  mono_array_addr (bytes, char, idx);
 
-       return iconv_get_length (cd, src, len);
+       /* iconv_get_length () returns the number of bytes */
+       return iconv_get_length (cd, src, (int) count, FALSE) / 2;
 }
 
 int
@@ -418,7 +448,7 @@ ves_icall_iconv_get_chars (gpointer converter, MonoArray *bytes, gint32 byteInde
 {
        iconv_t cd = (iconv_t)converter;
        guchar *src, *dest;
-       int len, max_len;
+       int max_len;
 
        g_assert (cd);
        g_assert (chars);
@@ -427,14 +457,11 @@ ves_icall_iconv_get_chars (gpointer converter, MonoArray *bytes, gint32 byteInde
        g_assert (mono_array_length (chars) >= (byteIndex + byteCount));
        g_assert (mono_array_length (chars) > charIndex);
 
-       if (!(len = (mono_array_length (bytes) - byteIndex)))
-               return 0;
-
        src =  mono_array_addr (bytes, char, byteIndex);
        dest = mono_array_addr (chars, guint16, charIndex);
 
        max_len = (mono_array_length (chars) - charIndex) * 2;
 
        /* iconv_convert () returns the number of bytes */
-       return iconv_convert (cd, src, len, dest, max_len) / 2;
+       return iconv_convert (cd, src, (int) byteCount, dest, max_len, FALSE) / 2;
 }