applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.git] / glib / gstrfuncs.c
index c8bf80f..707f357 100644 (file)
@@ -2,23 +2,23 @@
  * 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-1999.  See the AUTHORS
+ * 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/. 
 #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
  */
@@ -200,9 +205,11 @@ g_strtod (const gchar *nptr,
     {
       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)
@@ -790,169 +797,159 @@ extern const char * strsignal(int);
     }
 
   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)
 {
-  guint len = 1;
+  g_return_val_if_fail (dest != NULL, NULL);
+  g_return_val_if_fail (src  != NULL, NULL);
+  
+  return strlcpy (dest, src, dest_size);
+}
 
-  while (*format)
-    {
-      gboolean long_int = FALSE;
-      gboolean extra_long = FALSE;
-      gchar c;
+gsize
+g_strlcat (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 strlcat (dest, src, dest_size);
+}
 
-      c = *format++;
+#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)
+    {
+      if (dest_size != 0)
+       *d = 0;
+      while (*s++)
+       ;
+    }
+  
+  return s - src - 1;  /* count does not include NUL */
+}
 
-      if (c == '%')
+/* 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 G_HAVE_GINT64
-                 if (extra_long)
-                   (void) va_arg (args, gint64);
-                 else
-#endif /* G_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++;
     }
-
-  return len;
+  *d = 0;
+  
+  return dlength + (s - src);  /* count does not include NUL */
 }
+#endif /* ! HAVE_STRLCPY */
 
-void
+gchar*
 g_strdown (gchar *string)
 {
   register guchar *s;
-
-  g_return_if_fail (string != NULL);
-
+  
+  g_return_val_if_fail (string != NULL, NULL);
+  
   s = string;
-
+  
   while (*s)
     {
       *s = tolower (*s);
       s++;
     }
+  
+  return string;
 }
 
-void
+gchar*
 g_strup (gchar *string)
 {
   register guchar *s;
 
-  g_return_if_fail (string != NULL);
+  g_return_val_if_fail (string != NULL, NULL);
 
   s = string;
 
@@ -961,12 +958,14 @@ g_strup (gchar *string)
       *s = toupper (*s);
       s++;
     }
+
+  return string;
 }
 
-void
+gchar*
 g_strreverse (gchar *string)
 {
-  g_return_if_fail (string != NULL);
+  g_return_val_if_fail (string != NULL, NULL);
 
   if (*string)
     {
@@ -986,6 +985,8 @@ g_strreverse (gchar *string)
          t--;
        }
     }
+
+  return string;
 }
 
 gint
@@ -1032,8 +1033,9 @@ g_strncasecmp (const gchar *s1,
   g_return_val_if_fail (s1 != NULL, 0);
   g_return_val_if_fail (s2 != NULL, 0);
 
-  while (n-- && *s1 && *s2)
+  while (n && *s1 && *s2)
     {
+      n -= 1;
       /* According to A. Cox, some platforms have islower's that
        * don't work right on non-uppercase
        */
@@ -1045,7 +1047,7 @@ g_strncasecmp (const gchar *s1,
     }
 
   if (n)
-    return (((gint)(guchar) *s1) - ((gint)(guchar) *s2));
+    return (((gint) (guchar) *s1) - ((gint) (guchar) *s2));
   else
     return 0;
 #endif
@@ -1073,12 +1075,31 @@ g_strdelimit (gchar       *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 == '\\')
@@ -1123,6 +1144,7 @@ g_strcompress (const gchar *source)
       p++;
     }
   *q = 0;
+  
   return dest;
 }
 
@@ -1203,7 +1225,224 @@ g_strescape (const gchar *source,
   return dest;
 }
 
-/* blame Elliot for these next five routines */
+/*
+ * 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)
 {
@@ -1214,7 +1453,7 @@ g_strchug (gchar *string)
   for (start = string; *start && isspace (*start); start++)
     ;
 
-  g_memmove(string, start, strlen(start) + 1);
+  g_memmove (string, start, strlen( start) + 1);
 
   return string;
 }