2007-10-19 Nagappan A <anagappan@novell.com>
[mono.git] / eglib / src / gunicode.c
index 10bda117087af4a91d3b73d5dc2586cbc1ac29f0..d9e680994ec45ea82ae5f9ca2a0e5e07d85add99 100644 (file)
@@ -6,6 +6,13 @@
  *
  * (C) 2006 Novell, Inc.
  *
+ * utf8 validation code came from:
+ *     libxml2-2.6.26 licensed under the MIT X11 license
+ *
+ * Authors credit in libxml's string.c:
+ *   William Brack <wbrack@mmm.com.hk>
+ *   daniel@veillard.com
+ *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
  * "Software"), to deal in the Software without restriction, including
  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
  */
+#include <stdio.h>
+#include <glib.h>
+#include <errno.h>
+#ifdef _MSC_VER
+/* FIXME */
+#define CODESET 1
+typedef int iconv_t;
+#else
+#include <langinfo.h>
+#include <iconv.h>
+#endif
+
+static char *my_charset;
+static gboolean is_utf8;
 
 GUnicodeType 
 g_unichar_type (gunichar c)
 {
-       g_abort ("g_unichar_type is not implemented");
+       g_error ("%s", "g_unichar_type is not implemented");
+       return 0;
 }
 
 gunichar
 g_unichar_tolower (gunichar c)
 {
-       g_abort ("g_unichar_type is not implemented");
+       g_error ("%s", "g_unichar_type is not implemented");
+       return 0;
+}
+
+gboolean
+g_unichar_isxdigit (gunichar c)
+{
+       return (g_unichar_xdigit_value (c) != -1);
+
+}
+
+gint
+g_unichar_xdigit_value (gunichar c)
+{
+       if (c >= 0x30 && c <= 0x39) /*0-9*/
+               return (c - 0x30);
+       if (c >= 0x41 && c <= 0x46) /*A-F*/
+               return (c - 0x37);
+       if (c >= 0x61 && c <= 0x66) /*a-f*/
+               return (c - 0x57);
+       return -1;
+}
+
+gchar *
+g_convert (const gchar *str, gssize len,
+          const gchar *to_codeset, const gchar *from_codeset,
+          gsize *bytes_read, gsize *bytes_written, GError **error)
+{
+       char *result = NULL;
+#ifdef G_OS_WIN32
+#else
+       iconv_t convertor;
+       char *buffer, *output;
+       const char *strptr = (const char *) str;
+       size_t str_len = len == -1 ? strlen (str) : len;
+       size_t buffer_size;
+       size_t left, out_left;
+       
+       convertor = iconv_open (to_codeset, from_codeset);
+       if (convertor == (iconv_t) -1){
+               *bytes_written = 0;
+               *bytes_read = 0;
+               return NULL;
+       }
+
+       buffer_size = str_len + 1 + 8;
+       buffer = g_malloc (buffer_size);
+       out_left = str_len;
+       output = buffer;
+       left = str_len;
+       while (left > 0){
+               int res = iconv (convertor, (char **) &strptr, &left, &output, &out_left);
+               if (res == (size_t) -1){
+                       if (errno == E2BIG){
+                               char *n;
+                               size_t extra_space = 8 + left;
+                               size_t output_used = output - buffer;
+                               
+                               buffer_size += extra_space;
+                               
+                               n = g_realloc (buffer, buffer_size);
+                               
+                               if (n == NULL){
+                                       if (error != NULL)
+                                               *error = g_error_new (NULL, G_CONVERT_ERROR_FAILED, "No memory left");
+                                       g_free (buffer);
+                                       result = NULL;
+                                       goto leave;
+                               }
+                               buffer = n;
+                               out_left += extra_space;
+                               output = buffer + output_used;
+                       } else if (errno == EILSEQ){
+                               if (error != NULL)
+                                       *error = g_error_new (NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE, "Invalid multi-byte sequence on input");
+                               result = NULL;
+                               g_free (buffer);
+                               goto leave;
+                       } else if (errno == EINVAL){
+                               if (error != NULL)
+                                       *error = g_error_new (NULL, G_CONVERT_ERROR_PARTIAL_INPUT, "Partial character sequence");
+                               result = NULL;
+                               g_free (buffer);
+                               goto leave;
+                       }
+               } 
+       }
+       if (bytes_read != NULL)
+               *bytes_read = strptr - str;
+       if (bytes_written != NULL)
+               *bytes_written = output - buffer;
+       *output = 0;
+       result = buffer;
+ leave:
+       iconv_close (convertor);
+#endif
+       return result;
+}
+
+/*
+ * This is broken, and assumes an UTF8 system, but will do for eglib's first user
+ */
+gchar *
+g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
+{
+       char *res;
+       
+       if (len == -1)
+               len = strlen (utf8string);
+
+       res = g_malloc (len + 1);
+       g_strlcpy (res, utf8string, len + 1);
+       return res;
+}
+
+gboolean
+g_get_charset (G_CONST_RETURN char **charset)
+{
+#ifdef G_OS_WIN32
+#else
+       if (my_charset == NULL){
+               my_charset = g_strdup (nl_langinfo (CODESET));
+               is_utf8 = strcmp (my_charset, "UTF-8") == 0;
+       }
+       
+       if (charset != NULL)
+               *charset = my_charset;
+
+#endif
+       return is_utf8;
+}
+
+gchar *
+g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
+{
+       g_get_charset (NULL);
+
+       return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error);
+}
+
+gchar *
+g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
+{
+       g_get_charset (NULL);
+
+       return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error);
+}
+/**
+ * g_utf8_validate
+ * @utf: Pointer to putative UTF-8 encoded string.
+ *
+ * Checks @utf for being valid UTF-8. @utf is assumed to be
+ * null-terminated. This function is not super-strict, as it will
+ * allow longer UTF-8 sequences than necessary. Note that Java is
+ * capable of producing these sequences if provoked. Also note, this
+ * routine checks for the 4-byte maximum size, but does not check for
+ * 0x10ffff maximum value.
+ *
+ * Return value: true if @utf is valid.
+ **/
+gboolean
+g_utf8_validate (const gchar *utf, gssize max_len, const gchar **end)
+{
+       int ix;
+       
+       g_return_val_if_fail (utf != NULL, FALSE);
+
+       if (max_len == -1)
+               max_len = strlen (utf);
+       
+       /*
+        * utf is a string of 1, 2, 3 or 4 bytes.  The valid strings
+        * are as follows (in "bit format"):
+        *    0xxxxxxx                                      valid 1-byte
+        *    110xxxxx 10xxxxxx                             valid 2-byte
+        *    1110xxxx 10xxxxxx 10xxxxxx                    valid 3-byte
+        *    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx           valid 4-byte
+        */
+       for (ix = 0; ix < max_len;) {      /* string is 0-terminated */
+               unsigned char c;
+               
+               c = utf[ix];
+               if ((c & 0x80) == 0x00) {       /* 1-byte code, starts with 10 */
+                       ix++;
+               } else if ((c & 0xe0) == 0xc0) {/* 2-byte code, starts with 110 */
+                       if (((ix+1) >= max_len) || (utf[ix+1] & 0xc0 ) != 0x80){
+                               if (end != NULL)
+                                       *end = &utf [ix];
+                               return FALSE;
+                       }
+                       ix += 2;
+               } else if ((c & 0xf0) == 0xe0) {/* 3-byte code, starts with 1110 */
+                       if (((ix + 2) >= max_len) || 
+                           ((utf[ix+1] & 0xc0) != 0x80) ||
+                           ((utf[ix+2] & 0xc0) != 0x80)){
+                               if (end != NULL)
+                                       *end = &utf [ix];
+                               return FALSE;
+                       }
+                       ix += 3;
+               } else if ((c & 0xf8) == 0xf0) {/* 4-byte code, starts with 11110 */
+                       if (((ix + 3) >= max_len) ||
+                           ((utf[ix+1] & 0xc0) != 0x80) ||
+                           ((utf[ix+2] & 0xc0) != 0x80) ||
+                           ((utf[ix+3] & 0xc0) != 0x80)){
+                               if (end != NULL)
+                                       *end = &utf [ix];
+                               return FALSE;
+                       }
+                       ix += 4;
+               } else {/* unknown encoding */
+                       if (end != NULL)
+                               *end = &utf [ix];
+                       return FALSE;
+               }
+       }
+       
+       return TRUE;
 }