applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.git] / glib / gstrfuncs.c
index 1769854..707f357 100644 (file)
@@ -2,27 +2,51 @@
  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+/*
+ * MT safe
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
 #include <ctype.h>             /* For tolower() */
+#if !defined (HAVE_STRSIGNAL) || !defined(NO_SYS_SIGLIST_DECL)
+#include <signal.h>
+#endif
 #include "glib.h"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
 /* do not include <unistd.h> in this place since it
  * inteferes with g_strsignal() on some OSes
  */
@@ -31,7 +55,7 @@ gchar*
 g_strdup (const gchar *str)
 {
   gchar *new_str;
-  
+
   if (str)
     {
       new_str = g_new (char, strlen (str) + 1);
@@ -39,10 +63,27 @@ g_strdup (const gchar *str)
     }
   else
     new_str = NULL;
-  
+
   return new_str;
 }
 
+gpointer
+g_memdup (gconstpointer mem,
+         guint         byte_size)
+{
+  gpointer new_mem;
+
+  if (mem)
+    {
+      new_mem = g_malloc (byte_size);
+      memcpy (new_mem, mem, byte_size);
+    }
+  else
+    new_mem = NULL;
+
+  return new_mem;
+}
+
 gchar*
 g_strndup (const gchar *str,
           guint        n)
@@ -115,9 +156,9 @@ g_strconcat (const gchar *string1, ...)
   va_list args;
   gchar          *s;
   gchar          *concat;
-  
+
   g_return_val_if_fail (string1 != NULL, NULL);
-  
+
   l = 1 + strlen (string1);
   va_start (args, string1);
   s = va_arg (args, gchar*);
@@ -127,10 +168,10 @@ g_strconcat (const gchar *string1, ...)
       s = va_arg (args, gchar*);
     }
   va_end (args);
-  
+
   concat = g_new (gchar, l);
   concat[0] = 0;
-  
+
   strcat (concat, string1);
   va_start (args, string1);
   s = va_arg (args, gchar*);
@@ -140,7 +181,7 @@ g_strconcat (const gchar *string1, ...)
       s = va_arg (args, gchar*);
     }
   va_end (args);
-  
+
   return concat;
 }
 
@@ -152,23 +193,25 @@ g_strtod (const gchar *nptr,
   gchar *fail_pos_2;
   gdouble val_1;
   gdouble val_2 = 0;
-  
+
   g_return_val_if_fail (nptr != NULL, 0);
-  
+
   fail_pos_1 = NULL;
   fail_pos_2 = NULL;
-  
+
   val_1 = strtod (nptr, &fail_pos_1);
-  
+
   if (fail_pos_1 && fail_pos_1[0] != 0)
     {
       gchar *old_locale;
-      
-      old_locale = setlocale (LC_NUMERIC, "C");
+
+      old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+      setlocale (LC_NUMERIC, "C");
       val_2 = strtod (nptr, &fail_pos_2);
       setlocale (LC_NUMERIC, old_locale);
+      g_free (old_locale);
     }
-  
+
   if (!fail_pos_1 || fail_pos_1[0] == 0 || fail_pos_1 >= fail_pos_2)
     {
       if (endptr)
@@ -186,8 +229,9 @@ g_strtod (const gchar *nptr,
 gchar*
 g_strerror (gint errnum)
 {
-  static char msg[64];
-  
+  static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
+  char *msg;
+
 #ifdef HAVE_STRERROR
   return strerror (errnum);
 #elif NO_SYS_ERRLIST
@@ -608,22 +652,36 @@ g_strerror (gint errnum)
 #else /* NO_SYS_ERRLIST */
   extern int sys_nerr;
   extern char *sys_errlist[];
-  
+
   if ((errnum > 0) && (errnum <= sys_nerr))
     return sys_errlist [errnum];
 #endif /* NO_SYS_ERRLIST */
-  
+
+  msg = g_static_private_get (&msg_private);
+  if (!msg)
+    {
+      msg = g_new (gchar, 64);
+      g_static_private_set (&msg_private, msg, g_free);
+    }
+
   sprintf (msg, "unknown error (%d)", errnum);
+
   return msg;
 }
 
 gchar*
 g_strsignal (gint signum)
 {
-  static char msg[64];
-  
+  static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
+  char *msg;
+
 #ifdef HAVE_STRSIGNAL
+#ifdef G_OS_BEOS
+extern const char * strsignal(int);
+#else /* !G_OS_BEOS */
+  /* this is declared differently (const) in string.h on BeOS */
   extern char *strsignal (int sig);
+#endif /* !G_OS_BEOS */
   return strsignal (signum);
 #elif NO_SYS_SIGLIST
   switch (signum)
@@ -723,157 +781,157 @@ g_strsignal (gint signum)
 #endif
     }
 #else /* NO_SYS_SIGLIST */
-  extern char *sys_siglist[];
-  return sys_siglist [signum];
+
+#ifdef NO_SYS_SIGLIST_DECL
+  extern char *sys_siglist[];  /*(see Tue Jan 19 00:44:24 1999 in changelog)*/
+#endif
+
+  return (char*) /* this function should return const --josh */ sys_siglist [signum];
 #endif /* NO_SYS_SIGLIST */
-  
+
+  msg = g_static_private_get (&msg_private);
+  if (!msg)
+    {
+      msg = g_new (gchar, 64);
+      g_static_private_set (&msg_private, msg, g_free);
+    }
+
   sprintf (msg, "unknown signal (%d)", signum);
+  
   return msg;
 }
 
-guint
-g_printf_string_upper_bound (const gchar* format,
-                            va_list      args)
+/* Functions g_strlcpy and g_strlcat were originally developed by
+ * Todd C. Miller <Todd.Miller@courtesan.com> to simplify writing secure code.
+ * See ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/strlcpy.3
+ * for more information.
+ */
+
+#ifdef HAVE_STRLCPY
+/* Use the native ones, if available; they might be implemented in assembly */
+gsize
+g_strlcpy (gchar       *dest,
+          const gchar *src,
+          gsize        dest_size)
+{
+  g_return_val_if_fail (dest != NULL, NULL);
+  g_return_val_if_fail (src  != NULL, NULL);
+  
+  return strlcpy (dest, src, dest_size);
+}
+
+gsize
+g_strlcat (gchar       *dest,
+          const gchar *src,
+          gsize        dest_size)
 {
-  guint len = 1;
+  g_return_val_if_fail (dest != NULL, NULL);
+  g_return_val_if_fail (src  != NULL, NULL);
   
-  while (*format)
+  return strlcat (dest, src, dest_size);
+}
+
+#else /* ! HAVE_STRLCPY */
+/* g_strlcpy
+ *
+ * Copy string src to buffer dest (of buffer size dest_size).  At most
+ * dest_size-1 characters will be copied.  Always NUL terminates
+ * (unless dest_size == 0).  This function does NOT allocate memory.
+ * Unlike strncpy, this function doesn't pad dest (so it's often faster).
+ * Returns size of attempted result, strlen(src),
+ * so if retval >= dest_size, truncation occurred.
+ */
+gsize
+g_strlcpy (gchar       *dest,
+           const gchar *src,
+           gsize        dest_size)
+{
+  register gchar *d = dest;
+  register const gchar *s = src;
+  register gsize n = dest_size;
+  
+  g_return_val_if_fail (dest != NULL, NULL);
+  g_return_val_if_fail (src  != NULL, NULL);
+  
+  /* Copy as many bytes as will fit */
+  if (n != 0 && --n != 0)
+    do
+      {
+       register gchar c = *s++;
+       
+       *d++ = c;
+       if (c == 0)
+         break;
+      }
+    while (--n != 0);
+  
+  /* If not enough room in dest, add NUL and traverse rest of src */
+  if (n == 0)
     {
-      gboolean long_int = FALSE;
-      gboolean extra_long = FALSE;
-      gchar c;
-      
-      c = *format++;
-      
-      if (c == '%')
+      if (dest_size != 0)
+       *d = 0;
+      while (*s++)
+       ;
+    }
+  
+  return s - src - 1;  /* count does not include NUL */
+}
+
+/* g_strlcat
+ *
+ * Appends string src to buffer dest (of buffer size dest_size).
+ * At most dest_size-1 characters will be copied.
+ * Unlike strncat, dest_size is the full size of dest, not the space left over.
+ * This function does NOT allocate memory.
+ * This always NUL terminates (unless siz == 0 or there were no NUL characters
+ * in the dest_size characters of dest to start with).
+ * Returns size of attempted result, which is
+ * MIN (dest_size, strlen (original dest)) + strlen (src),
+ * so if retval >= dest_size, truncation occurred.
+ */
+gsize
+g_strlcat (gchar       *dest,
+           const gchar *src,
+           gsize        dest_size)
+{
+  register gchar *d = dest;
+  register const gchar *s = src;
+  register gsize bytes_left = dest_size;
+  gsize dlength;  /* Logically, MIN (strlen (d), dest_size) */
+  
+  g_return_val_if_fail (dest != NULL, NULL);
+  g_return_val_if_fail (src  != NULL, NULL);
+  
+  /* Find the end of dst and adjust bytes left but don't go past end */
+  while (*d != 0 && bytes_left-- != 0)
+    d++;
+  dlength = d - dest;
+  bytes_left = dest_size - dlength;
+  
+  if (bytes_left == 0)
+    return dlength + strlen (s);
+  
+  while (*s != 0)
+    {
+      if (bytes_left != 1)
        {
-         gboolean done = FALSE;
-         
-         while (*format && !done)
-           {
-             switch (*format++)
-               {
-                 gchar *string_arg;
-                 
-               case '*':
-                 len += va_arg (args, int);
-                 break;
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                 /* add specified format length, since it might exceed the
-                  * size we assume it to have.
-                  */
-                 format -= 1;
-                 len += strtol (format, (char**) &format, 10);
-                 break;
-               case 'h':
-                 /* ignore short int flag, since all args have at least the
-                  * same size as an int
-                  */
-                 break;
-               case 'l':
-                 if (long_int)
-                   extra_long = TRUE; /* linux specific */
-                 else
-                   long_int = TRUE;
-                 break;
-               case 'q':
-               case 'L':
-                 long_int = TRUE;
-                 extra_long = TRUE;
-                 break;
-               case 's':
-                 string_arg = va_arg (args, char *);
-                 if (string_arg)
-                   len += strlen (string_arg);
-                 else
-                   {
-                     /* add enough padding to hold "(null)" identifier */
-                     len += 16;
-                   }
-                 done = TRUE;
-                 break;
-               case 'd':
-               case 'i':
-               case 'o':
-               case 'u':
-               case 'x':
-               case 'X':
-#ifdef HAVE_GINT64
-                 if (extra_long)
-                   (void) va_arg (args, gint64);
-                 else
-#endif /* HAVE_GINT64 */
-                   {
-                     if (long_int)
-                       (void) va_arg (args, long);
-                     else
-                       (void) va_arg (args, int);
-                   }
-                 len += extra_long ? 64 : 32;
-                 done = TRUE;
-                 break;
-               case 'D':
-               case 'O':
-               case 'U':
-                 (void) va_arg (args, long);
-                 len += 32;
-                 done = TRUE;
-                 break;
-               case 'e':
-               case 'E':
-               case 'f':
-               case 'g':
-#ifdef HAVE_LONG_DOUBLE
-                 if (extra_long)
-                   (void) va_arg (args, long double);
-                 else
-#endif /* HAVE_LONG_DOUBLE */
-                   (void) va_arg (args, double);
-                 len += extra_long ? 64 : 32;
-                 done = TRUE;
-                 break;
-               case 'c':
-                 (void) va_arg (args, int);
-                 len += 1;
-                 done = TRUE;
-                 break;
-               case 'p':
-               case 'n':
-                 (void) va_arg (args, void*);
-                 len += 32;
-                 done = TRUE;
-                 break;
-               case '%':
-                 len += 1;
-                 done = TRUE;
-                 break;
-               default:
-                 /* ignore unknow/invalid flags */
-                 break;
-               }
-           }
+         *d++ = *s;
+         bytes_left--;
        }
-      else
-       len += 1;
+      s++;
     }
+  *d = 0;
   
-  return len;
+  return dlength + (s - src);  /* count does not include NUL */
 }
+#endif /* ! HAVE_STRLCPY */
 
-void
-g_strdown (gchar  *string)
+gchar*
+g_strdown (gchar *string)
 {
-  register gchar *s;
+  register guchar *s;
   
-  g_return_if_fail (string != NULL);
+  g_return_val_if_fail (string != NULL, NULL);
   
   s = string;
   
@@ -882,40 +940,44 @@ g_strdown (gchar  *string)
       *s = tolower (*s);
       s++;
     }
+  
+  return string;
 }
 
-void
-g_strup (gchar *string)
+gchar*
+g_strup (gchar *string)
 {
-  register gchar *s;
-  
-  g_return_if_fail (string != NULL);
-  
+  register guchar *s;
+
+  g_return_val_if_fail (string != NULL, NULL);
+
   s = string;
-  
+
   while (*s)
     {
       *s = toupper (*s);
       s++;
     }
+
+  return string;
 }
 
-void
-g_strreverse (gchar      *string)
+gchar*
+g_strreverse (gchar *string)
 {
-  g_return_if_fail (string != NULL);
-  
+  g_return_val_if_fail (string != NULL, NULL);
+
   if (*string)
     {
       register gchar *h, *t;
-      
+
       h = string;
       t = string + strlen (string) - 1;
-      
+
       while (h < t)
        {
          register gchar c;
-         
+
          c = *h;
          *h = *t;
          h++;
@@ -923,6 +985,8 @@ g_strreverse (gchar   *string)
          t--;
        }
     }
+
+  return string;
 }
 
 gint
@@ -930,10 +994,16 @@ g_strcasecmp (const gchar *s1,
              const gchar *s2)
 {
 #ifdef HAVE_STRCASECMP
+  g_return_val_if_fail (s1 != NULL, 0);
+  g_return_val_if_fail (s2 != NULL, 0);
+
   return strcasecmp (s1, s2);
 #else
   gint c1, c2;
-  
+
+  g_return_val_if_fail (s1 != NULL, 0);
+  g_return_val_if_fail (s2 != NULL, 0);
+
   while (*s1 && *s2)
     {
       /* According to A. Cox, some platforms have islower's that
@@ -945,26 +1015,621 @@ g_strcasecmp (const gchar *s1,
        return (c1 - c2);
       s1++; s2++;
     }
-  
+
   return (((gint)(guchar) *s1) - ((gint)(guchar) *s2));
 #endif
 }
 
-void
+gint
+g_strncasecmp (const gchar *s1,
+              const gchar *s2,
+              guint n)
+{
+#ifdef HAVE_STRNCASECMP
+  return strncasecmp (s1, s2, n);
+#else
+  gint c1, c2;
+
+  g_return_val_if_fail (s1 != NULL, 0);
+  g_return_val_if_fail (s2 != NULL, 0);
+
+  while (n && *s1 && *s2)
+    {
+      n -= 1;
+      /* According to A. Cox, some platforms have islower's that
+       * don't work right on non-uppercase
+       */
+      c1 = isupper ((guchar)*s1) ? tolower ((guchar)*s1) : *s1;
+      c2 = isupper ((guchar)*s2) ? tolower ((guchar)*s2) : *s2;
+      if (c1 != c2)
+       return (c1 - c2);
+      s1++; s2++;
+    }
+
+  if (n)
+    return (((gint) (guchar) *s1) - ((gint) (guchar) *s2));
+  else
+    return 0;
+#endif
+}
+
+gchar*
 g_strdelimit (gchar      *string,
              const gchar *delimiters,
              gchar        new_delim)
 {
   register gchar *c;
-  
-  g_return_if_fail (string != NULL);
-  
+
+  g_return_val_if_fail (string != NULL, NULL);
+
   if (!delimiters)
     delimiters = G_STR_DELIMITERS;
-  
+
   for (c = string; *c; c++)
     {
       if (strchr (delimiters, *c))
        *c = new_delim;
     }
+
+  return string;
+}
+
+gchar*
+g_strcanon (gchar       *string,
+           const gchar *valid_chars,
+           gchar        subsitutor)
+{
+  register gchar *c;
+
+  g_return_val_if_fail (string != NULL, NULL);
+  g_return_val_if_fail (valid_chars != NULL, NULL);
+
+  for (c = string; *c; c++)
+    {
+      if (!strchr (valid_chars, *c))
+       *c = subsitutor;
+    }
+
+  return string;
+}
+
+gchar*
+g_strcompress (const gchar *source)
+{
+  const gchar *p = source, *octal;
+  gchar *dest = g_malloc (strlen (source) + 1);
+  gchar *q = dest;
+  
+  while (*p)
+    {
+      if (*p == '\\')
+       {
+         p++;
+         switch (*p)
+           {
+           case '0':  case '1':  case '2':  case '3':  case '4':
+           case '5':  case '6':  case '7':
+             *q = 0;
+             octal = p;
+             while ((p < octal + 3) && (*p >= '0') && (*p <= '7'))
+               {
+                 *q = (*q * 8) + (*p - '0');
+                 p++;
+               }
+             q++;
+             p--;
+             break;
+           case 'b':
+             *q++ = '\b';
+             break;
+           case 'f':
+             *q++ = '\f';
+             break;
+           case 'n':
+             *q++ = '\n';
+             break;
+           case 'r':
+             *q++ = '\r';
+             break;
+           case 't':
+             *q++ = '\t';
+             break;
+           default:            /* Also handles \" and \\ */
+             *q++ = *p;
+             break;
+           }
+       }
+      else
+       *q++ = *p;
+      p++;
+    }
+  *q = 0;
+  
+  return dest;
+}
+
+gchar *
+g_strescape (const gchar *source,
+            const gchar *exceptions)
+{
+  const guchar *p = (guchar *) source;
+  /* Each source byte needs maximally four destination chars (\777) */
+  gchar *dest = g_malloc (strlen (source) * 4 + 1);
+  gchar *q = dest;
+  guchar excmap[256];
+
+  memset (excmap, 0, 256);
+  if (exceptions)
+    {
+      guchar *e = (guchar *) exceptions;
+
+      while (*e)
+       {
+         excmap[*e] = 1;
+         e++;
+       }
+    }
+
+  while (*p)
+    {
+      if (excmap[*p])
+       *q++ = *p;
+      else
+       {
+         switch (*p)
+           {
+           case '\b':
+             *q++ = '\\';
+             *q++ = 'b';
+             break;
+           case '\f':
+             *q++ = '\\';
+             *q++ = 'f';
+             break;
+           case '\n':
+             *q++ = '\\';
+             *q++ = 'n';
+             break;
+           case '\r':
+             *q++ = '\\';
+             *q++ = 'r';
+             break;
+           case '\t':
+             *q++ = '\\';
+             *q++ = 't';
+             break;
+           case '\\':
+             *q++ = '\\';
+             *q++ = '\\';
+             break;
+           case '"':
+             *q++ = '\\';
+             *q++ = '"';
+             break;
+           default:
+             if ((*p < ' ') || (*p >= 0177))
+               {
+                 *q++ = '\\';
+                 *q++ = '0' + (((*p) >> 6) & 07);
+                 *q++ = '0' + (((*p) >> 3) & 07);
+                 *q++ = '0' + ((*p) & 07);
+               }
+             else
+               *q++ = *p;
+             break;
+           }
+       }
+      p++;
+    }
+  *q = 0;
+  return dest;
+}
+
+/*
+ * g_filename_to_utf8
+ *
+ * Converts a string which is in the encoding used for file names by
+ * the C runtime (usually the same as that used by the operating
+ * system) in the current locale into a UTF-8 string.
+ */
+
+gchar *
+g_filename_to_utf8 (const gchar *opsysstring)
+{
+#ifdef G_OS_WIN32
+
+  gint i, clen, wclen, first;
+  const gint len = strlen (opsysstring);
+  wchar_t *wcs, wc;
+  gchar *result, *bp;
+  const wchar_t *wcp;
+
+  wcs = g_new (wchar_t, len);
+  wclen = MultiByteToWideChar (CP_ACP, 0, opsysstring, len, wcs, len);
+
+  wcp = wcs;
+  clen = 0;
+  for (i = 0; i < wclen; i++)
+    {
+      wc = *wcp++;
+
+      if (wc < 0x80)
+       clen += 1;
+      else if (wc < 0x800)
+       clen += 2;
+      else if (wc < 0x10000)
+       clen += 3;
+      else if (wc < 0x200000)
+       clen += 4;
+      else if (wc < 0x4000000)
+       clen += 5;
+      else
+       clen += 6;
+    }
+
+  result = g_malloc (clen + 1);
+  
+  wcp = wcs;
+  bp = result;
+  for (i = 0; i < wclen; i++)
+    {
+      wc = *wcp++;
+
+      if (wc < 0x80)
+       {
+         first = 0;
+         clen = 1;
+       }
+      else if (wc < 0x800)
+       {
+         first = 0xc0;
+         clen = 2;
+       }
+      else if (wc < 0x10000)
+       {
+         first = 0xe0;
+         clen = 3;
+       }
+      else if (wc < 0x200000)
+       {
+         first = 0xf0;
+         clen = 4;
+       }
+      else if (wc < 0x4000000)
+       {
+         first = 0xf8;
+         clen = 5;
+       }
+      else
+       {
+         first = 0xfc;
+         clen = 6;
+       }
+      
+      /* Woo-hoo! */
+      switch (clen)
+       {
+       case 6: bp[5] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
+       case 5: bp[4] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
+       case 4: bp[3] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
+       case 3: bp[2] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
+       case 2: bp[1] = (wc & 0x3f) | 0x80; wc >>= 6; /* Fall through */
+       case 1: bp[0] = wc | first;
+       }
+
+      bp += clen;
+    }
+  *bp = 0;
+
+  g_free (wcs);
+
+  return result;
+
+#else
+
+  return g_strdup (opsysstring);
+
+#endif
+}
+
+/*
+ * g_filename_from_utf8
+ *
+ * The reverse of g_filename_to_utf8.
+ */
+
+gchar *
+g_filename_from_utf8 (const gchar *utf8string)
+{
+#ifdef G_OS_WIN32
+
+  gint i, mask, clen, wclen, mblen;
+  const gint len = strlen (utf8string);
+  wchar_t *wcs, *wcp;
+  gchar *result;
+  guchar *cp, *end, c;
+  gint n;
+  
+  /* First convert to wide chars */
+  cp = (guchar *) utf8string;
+  end = cp + len;
+  n = 0;
+  wcs = g_new (wchar_t, len + 1);
+  wcp = wcs;
+  while (cp != end)
+    {
+      mask = 0;
+      c = *cp;
+
+      if (c < 0x80)
+       {
+         clen = 1;
+         mask = 0x7f;
+       }
+      else if ((c & 0xe0) == 0xc0)
+       {
+         clen = 2;
+         mask = 0x1f;
+       }
+      else if ((c & 0xf0) == 0xe0)
+       {
+         clen = 3;
+         mask = 0x0f;
+       }
+      else if ((c & 0xf8) == 0xf0)
+       {
+         clen = 4;
+         mask = 0x07;
+       }
+      else if ((c & 0xfc) == 0xf8)
+       {
+         clen = 5;
+         mask = 0x03;
+       }
+      else if ((c & 0xfc) == 0xfc)
+       {
+         clen = 6;
+         mask = 0x01;
+       }
+      else
+       {
+         g_free (wcs);
+         return NULL;
+       }
+
+      if (cp + clen > end)
+       {
+         g_free (wcs);
+         return NULL;
+       }
+
+      *wcp = (cp[0] & mask);
+      for (i = 1; i < clen; i++)
+       {
+         if ((cp[i] & 0xc0) != 0x80)
+           {
+             g_free (wcs);
+             return NULL;
+           }
+         *wcp <<= 6;
+         *wcp |= (cp[i] & 0x3f);
+       }
+
+      cp += clen;
+      wcp++;
+      n++;
+    }
+  if (cp != end)
+    {
+      g_free (wcs);
+      return NULL;
+    }
+
+  /* n is the number of wide chars constructed */
+
+  /* Convert to a string in the current ANSI codepage */
+
+  result = g_new (gchar, 3 * n + 1);
+  mblen = WideCharToMultiByte (CP_ACP, 0, wcs, n, result, 3*n, NULL, NULL);
+  result[mblen] = 0;
+  g_free (wcs);
+
+  return result;
+
+#else
+
+  return g_strdup (utf8string);
+
+#endif
+}
+
+gchar*
+g_strchug (gchar *string)
+{
+  guchar *start;
+
+  g_return_val_if_fail (string != NULL, NULL);
+
+  for (start = string; *start && isspace (*start); start++)
+    ;
+
+  g_memmove (string, start, strlen( start) + 1);
+
+  return string;
+}
+
+gchar*
+g_strchomp (gchar *string)
+{
+  gchar *s;
+
+  g_return_val_if_fail (string != NULL, NULL);
+
+  if (!*string)
+    return string;
+
+  for (s = string + strlen (string) - 1; s >= string && isspace ((guchar)*s); 
+       s--)
+    *s = '\0';
+
+  return string;
+}
+
+gchar**
+g_strsplit (const gchar *string,
+           const gchar *delimiter,
+           gint         max_tokens)
+{
+  GSList *string_list = NULL, *slist;
+  gchar **str_array, *s;
+  guint i, n = 1;
+
+  g_return_val_if_fail (string != NULL, NULL);
+  g_return_val_if_fail (delimiter != NULL, NULL);
+
+  if (max_tokens < 1)
+    max_tokens = G_MAXINT;
+
+  s = strstr (string, delimiter);
+  if (s)
+    {
+      guint delimiter_len = strlen (delimiter);
+
+      do
+       {
+         guint len;
+         gchar *new_string;
+
+         len = s - string;
+         new_string = g_new (gchar, len + 1);
+         strncpy (new_string, string, len);
+         new_string[len] = 0;
+         string_list = g_slist_prepend (string_list, new_string);
+         n++;
+         string = s + delimiter_len;
+         s = strstr (string, delimiter);
+       }
+      while (--max_tokens && s);
+    }
+  if (*string)
+    {
+      n++;
+      string_list = g_slist_prepend (string_list, g_strdup (string));
+    }
+
+  str_array = g_new (gchar*, n);
+
+  i = n - 1;
+
+  str_array[i--] = NULL;
+  for (slist = string_list; slist; slist = slist->next)
+    str_array[i--] = slist->data;
+
+  g_slist_free (string_list);
+
+  return str_array;
+}
+
+void
+g_strfreev (gchar **str_array)
+{
+  if (str_array)
+    {
+      int i;
+
+      for(i = 0; str_array[i] != NULL; i++)
+       g_free(str_array[i]);
+
+      g_free (str_array);
+    }
+}
+
+gchar*
+g_strjoinv (const gchar  *separator,
+           gchar       **str_array)
+{
+  gchar *string;
+
+  g_return_val_if_fail (str_array != NULL, NULL);
+
+  if (separator == NULL)
+    separator = "";
+
+  if (*str_array)
+    {
+      guint i, len;
+      guint separator_len;
+
+      separator_len = strlen (separator);
+      len = 1 + strlen (str_array[0]);
+      for(i = 1; str_array[i] != NULL; i++)
+       len += separator_len + strlen(str_array[i]);
+
+      string = g_new (gchar, len);
+      *string = 0;
+      strcat (string, *str_array);
+      for (i = 1; str_array[i] != NULL; i++)
+       {
+         strcat (string, separator);
+         strcat (string, str_array[i]);
+       }
+      }
+  else
+    string = g_strdup ("");
+
+  return string;
+}
+
+gchar*
+g_strjoin (const gchar  *separator,
+          ...)
+{
+  gchar *string, *s;
+  va_list args;
+  guint len;
+  guint separator_len;
+
+  if (separator == NULL)
+    separator = "";
+
+  separator_len = strlen (separator);
+
+  va_start (args, separator);
+
+  s = va_arg (args, gchar*);
+
+  if (s)
+    {
+      len = strlen (s);
+
+      s = va_arg (args, gchar*);
+      while (s)
+       {
+         len += separator_len + strlen (s);
+         s = va_arg (args, gchar*);
+       }
+      va_end (args);
+
+      string = g_new (gchar, len + 1);
+      *string = 0;
+
+      va_start (args, separator);
+
+      s = va_arg (args, gchar*);
+      strcat (string, s);
+
+      s = va_arg (args, gchar*);
+      while (s)
+       {
+         strcat (string, separator);
+         strcat (string, s);
+         s = va_arg (args, gchar*);
+       }
+    }
+  else
+    string = g_strdup ("");
+
+  va_end (args);
+
+  return string;
 }