applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.git] / glib / gstrfuncs.c
index 37df963..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)
@@ -669,12 +676,12 @@ g_strsignal (gint signum)
   char *msg;
 
 #ifdef HAVE_STRSIGNAL
-#ifdef GLIB_NATIVE_BEOS
+#ifdef G_OS_BEOS
 extern const char * strsignal(int);
-#else /* !GLIB_NATIVE_BEOS */
+#else /* !G_OS_BEOS */
   /* this is declared differently (const) in string.h on BeOS */
   extern char *strsignal (int sig);
-#endif /* !GLIB_NATIVE_BEOS */
+#endif /* !G_OS_BEOS */
   return strsignal (signum);
 #elif NO_SYS_SIGLIST
   switch (signum)
@@ -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,43 +1075,374 @@ g_strdelimit (gchar      *string,
 }
 
 gchar*
-g_strescape (gchar *string)
+g_strcanon (gchar       *string,
+           const gchar *valid_chars,
+           gchar        subsitutor)
 {
-  gchar *q;
-  gchar *escaped;
-  guint escapes_needed = 0;
-  gchar *p = string;
-
-  g_message ("g_strescape() is deprecated");
+  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 != '\000')
+  while (*p)
     {
-      escapes_needed += (*p == '\\' || *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;
+}
 
-  if (!escapes_needed)
-    return g_strdup (string);
+/*
+ * 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.
+ */
 
-  escaped = g_new (gchar, strlen (string) + escapes_needed + 1);
+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;
 
-  p = string;
-  q = escaped;
+  wcs = g_new (wchar_t, len);
+  wclen = MultiByteToWideChar (CP_ACP, 0, opsysstring, len, wcs, len);
 
-  while (*p != '\000')
+  wcp = wcs;
+  clen = 0;
+  for (i = 0; i < wclen; i++)
     {
-      if (*p == '\\' || *p == '"')
-       *q++ = '\\';
-      *q++ = *p++;
+      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;
     }
-  *q = '\000';
 
-  return escaped;
+  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
 }
 
-/* blame Elliot for these next five routines */
 gchar*
 g_strchug (gchar *string)
 {
@@ -1120,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;
 }