Remove all uses of G_CONST_RETURN
[platform/upstream/glib.git] / glib / gutils.c
index eb0d1d0..c870152 100644 (file)
 #include <stdio.h>
 #include <locale.h>
 #include <string.h>
+#include <ctype.h>             /* For tolower() */
 #include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
+#ifdef HAVE_CRT_EXTERNS_H 
+#include <crt_externs.h> /* for _NSGetEnviron */
+#endif
 
 /* implement gutils's inline functions
  */
 #define        G_IMPLEMENT_INLINES 1
 #define        __G_UTILS_C__
-#include "glib.h"
+#include "gutils.h"
+
+#include "gfileutils.h"
+#include "ghash.h"
+#include "gslist.h"
 #include "gprintfint.h"
-#include "gthreadinit.h"
-#include "galias.h"
+#include "gthread.h"
+#include "gthreadprivate.h"
+#include "gtestutils.h"
+#include "gunicode.h"
+#include "gstrfuncs.h"
+#include "garray.h"
+#include "glibintl.h"
+
+#ifdef G_PLATFORM_WIN32
+#include "garray.h"
+#include "gconvert.h"
+#include "gwin32.h"
+#endif
 
 #ifdef MAXPATHLEN
 #define        G_PATH_LENGTH   MAXPATHLEN
 #  include <direct.h>
 #  include <shlobj.h>
    /* older SDK (e.g. msvc 5.0) does not have these*/
+#  ifndef CSIDL_MYMUSIC
+#    define CSIDL_MYMUSIC 13
+#  endif
+#  ifndef CSIDL_MYVIDEO
+#    define CSIDL_MYVIDEO 14
+#  endif
 #  ifndef CSIDL_INTERNET_CACHE
 #    define CSIDL_INTERNET_CACHE 32
 #  endif
 #  ifndef CSIDL_COMMON_APPDATA
 #    define CSIDL_COMMON_APPDATA 35
 #  endif
+#  ifndef CSIDL_MYPICTURES
+#    define CSIDL_MYPICTURES 0x27
+#  endif
 #  ifndef CSIDL_COMMON_DOCUMENTS
 #    define CSIDL_COMMON_DOCUMENTS 46
 #  endif
 #  include <process.h>
 #endif
 
-#ifdef HAVE_CODESET
-#include <langinfo.h>
+#ifdef HAVE_CARBON
+#include <CoreServices/CoreServices.h>
 #endif
 
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
-#include <libintl.h>
+#ifdef HAVE_CODESET
+#include <langinfo.h>
 #endif
 
 const guint glib_major_version = GLIB_MAJOR_VERSION;
@@ -112,15 +142,67 @@ const guint glib_binary_age = GLIB_BINARY_AGE;
 
 #ifdef G_PLATFORM_WIN32
 
-G_WIN32_DLLMAIN_FOR_DLL_NAME (static, dll_name)
+static HMODULE glib_dll = NULL;
+
+#ifdef DLL_EXPORT
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+        DWORD     fdwReason,
+        LPVOID    lpvReserved)
+{
+  if (fdwReason == DLL_PROCESS_ATTACH)
+      glib_dll = hinstDLL;
+
+  return TRUE;
+}
+
+#endif
+
+gchar *
+_glib_get_dll_directory (void)
+{
+  gchar *retval;
+  gchar *p;
+  wchar_t wc_fn[MAX_PATH];
+
+#ifdef DLL_EXPORT
+  if (glib_dll == NULL)
+    return NULL;
+#endif
+
+  /* This code is different from that in
+   * g_win32_get_package_installation_directory_of_module() in that
+   * here we return the actual folder where the GLib DLL is. We don't
+   * do the check for it being in a "bin" or "lib" subfolder and then
+   * returning the parent of that.
+   *
+   * In a statically built GLib, glib_dll will be NULL and we will
+   * thus look up the application's .exe file's location.
+   */
+  if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH))
+    return NULL;
+
+  retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
+
+  p = strrchr (retval, G_DIR_SEPARATOR);
+  if (p == NULL)
+    {
+      /* Wtf? */
+      return NULL;
+    }
+  *p = '\0';
+
+  return retval;
+}
 
 #endif
 
 /**
  * glib_check_version:
  * @required_major: the required major version.
- * @required_minor: the required major version.
- * @required_micro: the required major version.
+ * @required_minor: the required minor version.
+ * @required_micro: the required micro version.
  *
  * Checks that the GLib library in use is compatible with the
  * given version. Generally you would pass in the constants
@@ -205,11 +287,44 @@ g_memmove (gpointer      dest,
 }
 #endif /* !HAVE_MEMMOVE && !HAVE_WORKING_BCOPY */
 
+#ifdef G_OS_WIN32
+#undef g_atexit
+#endif
+
 /**
  * g_atexit:
  * @func: the function to call on normal program termination.
  * 
  * Specifies a function to be called at normal program termination.
+ *
+ * Since GLib 2.8.2, on Windows g_atexit() actually is a preprocessor
+ * macro that maps to a call to the atexit() function in the C
+ * library. This means that in case the code that calls g_atexit(),
+ * i.e. atexit(), is in a DLL, the function will be called when the
+ * DLL is detached from the program. This typically makes more sense
+ * than that the function is called when the GLib DLL is detached,
+ * which happened earlier when g_atexit() was a function in the GLib
+ * DLL.
+ *
+ * The behaviour of atexit() in the context of dynamically loaded
+ * modules is not formally specified and varies wildly.
+ *
+ * On POSIX systems, calling g_atexit() (or atexit()) in a dynamically
+ * loaded module which is unloaded before the program terminates might
+ * well cause a crash at program exit.
+ *
+ * Some POSIX systems implement atexit() like Windows, and have each
+ * dynamically loaded module maintain an own atexit chain that is
+ * called when the module is unloaded.
+ *
+ * On other POSIX systems, before a dynamically loaded module is
+ * unloaded, the registered atexit functions (if any) residing in that
+ * module are called, regardless where the code that registered them
+ * resided. This is presumably the most robust approach.
+ *
+ * As can be seen from the above, for portability it's best to avoid
+ * calling g_atexit() (or atexit()) except in the main executable of a
+ * program.
  */
 void
 g_atexit (GVoidFunc func)
@@ -350,9 +465,12 @@ g_find_program_in_path (const gchar *program)
   const gchar *path_copy;
   gchar *filename = NULL, *appdir = NULL;
   gchar *sysdir = NULL, *windir = NULL;
+  int n;
+  wchar_t wfilename[MAXPATHLEN], wsysdir[MAXPATHLEN],
+    wwindir[MAXPATHLEN];
 #endif
-  size_t len;
-  size_t pathlen;
+  gsize len;
+  gsize pathlen;
 
   g_return_val_if_fail (program != NULL, NULL);
 
@@ -390,42 +508,17 @@ g_find_program_in_path (const gchar *program)
       path = "/bin:/usr/bin:.";
     }
 #else
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      int n;
-      wchar_t wfilename[MAXPATHLEN], wsysdir[MAXPATHLEN],
-       wwindir[MAXPATHLEN];
-      
-      n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
-      
-      n = GetSystemDirectoryW (wsysdir, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       sysdir = g_utf16_to_utf8 (wsysdir, -1, NULL, NULL, NULL);
-      
-      n = GetWindowsDirectoryW (wwindir, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       windir = g_utf16_to_utf8 (wwindir, -1, NULL, NULL, NULL);
-    }
-  else
-    {
-      int n;
-      gchar cpfilename[MAXPATHLEN], cpsysdir[MAXPATHLEN],
-       cpwindir[MAXPATHLEN];
-      
-      n = GetModuleFileNameA (NULL, cpfilename, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       filename = g_locale_to_utf8 (cpfilename, -1, NULL, NULL, NULL);
-      
-      n = GetSystemDirectoryA (cpsysdir, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       sysdir = g_locale_to_utf8 (cpsysdir, -1, NULL, NULL, NULL);
-      
-      n = GetWindowsDirectoryA (cpwindir, MAXPATHLEN);
-      if (n > 0 && n < MAXPATHLEN)
-       windir = g_locale_to_utf8 (cpwindir, -1, NULL, NULL, NULL);
-    }
+  n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
+  if (n > 0 && n < MAXPATHLEN)
+    filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
+  
+  n = GetSystemDirectoryW (wsysdir, MAXPATHLEN);
+  if (n > 0 && n < MAXPATHLEN)
+    sysdir = g_utf16_to_utf8 (wsysdir, -1, NULL, NULL, NULL);
+  
+  n = GetWindowsDirectoryW (wwindir, MAXPATHLEN);
+  if (n > 0 && n < MAXPATHLEN)
+    windir = g_utf16_to_utf8 (wwindir, -1, NULL, NULL, NULL);
   
   if (filename)
     {
@@ -516,19 +609,40 @@ g_find_program_in_path (const gchar *program)
   return NULL;
 }
 
+static gboolean
+debug_key_matches (const gchar *key,
+                  const gchar *token,
+                  guint        length)
+{
+  for (; length; length--, key++, token++)
+    {
+      char k = (*key   == '_') ? '-' : tolower (*key  );
+      char t = (*token == '_') ? '-' : tolower (*token);
+
+      if (k != t)
+        return FALSE;
+    }
+
+  return *key == '\0';
+}
+
 /**
  * g_parse_debug_string:
- * @string: a list of debug options separated by ':' or "all" 
- *     to set all flags.
+ * @string: a list of debug options separated by colons, spaces, or
+ * commas, or %NULL.
  * @keys: pointer to an array of #GDebugKey which associate 
  *     strings with bit flags.
  * @nkeys: the number of #GDebugKey<!-- -->s in the array.
  *
- * Parses a string containing debugging options separated 
- * by ':' into a %guint containing bit flags. This is used 
+ * Parses a string containing debugging options
+ * into a %guint containing bit flags. This is used 
  * within GDK and GTK+ to parse the debug options passed on the
  * command line or through environment variables.
  *
+ * If @string is equal to "all", all flags are set.  If @string
+ * is equal to "help", all the available keys in @keys are printed
+ * out to standard error.
+ *
  * Returns: the combined set of bit flags.
  */
 guint       
@@ -539,34 +653,45 @@ g_parse_debug_string  (const gchar     *string,
   guint i;
   guint result = 0;
   
-  g_return_val_if_fail (string != NULL, 0);
+  if (string == NULL)
+    return 0;
+
+  /* this function is used by gmem.c/gslice.c initialization code,
+   * so introducing malloc dependencies here would require adaptions
+   * of those code portions.
+   */
   
   if (!g_ascii_strcasecmp (string, "all"))
     {
       for (i=0; i<nkeys; i++)
        result |= keys[i].value;
     }
+  else if (!g_ascii_strcasecmp (string, "help"))
+    {
+      /* using stdio directly for the reason stated above */
+      fprintf (stderr, "Supported debug values: ");
+      for (i=0; i<nkeys; i++)
+       fprintf (stderr, " %s", keys[i].key);
+      fprintf (stderr, "\n");
+    }
   else
     {
       const gchar *p = string;
       const gchar *q;
-      gboolean done = FALSE;
       
-      while (*p && !done)
+      while (*p)
        {
-         q = strchr (p, ':');
+         q = strpbrk (p, ":;, \t");
          if (!q)
-           {
-             q = p + strlen(p);
-             done = TRUE;
-           }
+           q = p + strlen(p);
          
-         for (i=0; i<nkeys; i++)
-           if (g_ascii_strncasecmp (keys[i].key, p, q - p) == 0 &&
-               keys[i].key[q - p] == '\0')
+         for (i = 0; i < nkeys; i++)
+           if (debug_key_matches (keys[i].key, p, q - p))
              result |= keys[i].value;
          
-         p = q + 1;
+         p = q;
+         if (*p)
+           p++;
        }
     }
   
@@ -582,11 +707,11 @@ g_parse_debug_string  (const gchar     *string,
  * 
  * Return value: the name of the file without any leading directory components.
  *
- * Deprecated: Use g_path_get_basename() instead, but notice that
+ * Deprecated:2.2: Use g_path_get_basename() instead, but notice that
  * g_path_get_basename() allocates new memory for the returned string, unlike
  * this function which returns a pointer into the argument.
  **/
-G_CONST_RETURN gchar*
+const gchar *
 g_basename (const gchar           *file_name)
 {
   register gchar *base;
@@ -677,11 +802,32 @@ g_path_get_basename (const gchar   *file_name)
  * g_path_is_absolute:
  * @file_name: a file name.
  *
- * Returns %TRUE if the given @file_name is an absolute file name,
- * i.e. it contains a full path from the root directory such as "/usr/local"
- * on UNIX or "C:\windows" on Windows systems.
+ * Returns %TRUE if the given @file_name is an absolute file name.
+ * Note that this is a somewhat vague concept on Windows.
+ *
+ * On POSIX systems, an absolute file name is well-defined. It always
+ * starts from the single root directory. For example "/usr/local".
  *
- * Returns: %TRUE if @file_name is an absolute path. 
+ * On Windows, the concepts of current drive and drive-specific
+ * current directory introduce vagueness. This function interprets as
+ * an absolute file name one that either begins with a directory
+ * separator such as "\Users\tml" or begins with the root on a drive,
+ * for example "C:\Windows". The first case also includes UNC paths
+ * such as "\\myserver\docs\foo". In all cases, either slashes or
+ * backslashes are accepted.
+ *
+ * Note that a file name relative to the current drive root does not
+ * truly specify a file uniquely over time and across processes, as
+ * the current drive is a per-process value and can be changed.
+ *
+ * File names relative the current directory on some specific drive,
+ * such as "D:foo/bar", are not interpreted as absolute by this
+ * function, but they obviously are not relative to the normal current
+ * directory as returned by getcwd() or g_get_current_dir()
+ * either. Such paths should be avoided, or need to be handled using
+ * Windows-specific code.
+ *
+ * Returns: %TRUE if @file_name is absolute. 
  */
 gboolean
 g_path_is_absolute (const gchar *file_name)
@@ -711,7 +857,7 @@ g_path_is_absolute (const gchar *file_name)
  *
  * Returns: a pointer into @file_name after the root component.
  */
-G_CONST_RETURN gchar*
+const gchar *
 g_path_skip_root (const gchar *file_name)
 {
   g_return_val_if_fail (file_name != NULL, NULL);
@@ -883,33 +1029,16 @@ g_get_current_dir (void)
 #ifdef G_OS_WIN32
 
   gchar *dir = NULL;
+  wchar_t dummy[2], *wdir;
+  int len;
 
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t dummy[2], *wdir;
-      int len;
-
-      len = GetCurrentDirectoryW (2, dummy);
-      wdir = g_new (wchar_t, len);
-
-      if (GetCurrentDirectoryW (len, wdir) == len - 1)
-       dir = g_utf16_to_utf8 (wdir, -1, NULL, NULL, NULL);
-
-      g_free (wdir);
-    }
-  else
-    {
-      gchar dummy[2], *cpdir;
-      int len;
-
-      len = GetCurrentDirectoryA (2, dummy);
-      cpdir = g_new (gchar, len);
-
-      if (GetCurrentDirectoryA (len, cpdir) == len - 1)
-       dir = g_locale_to_utf8 (cpdir, -1, NULL, NULL, NULL);
+  len = GetCurrentDirectoryW (2, dummy);
+  wdir = g_new (wchar_t, len);
 
-      g_free (cpdir);
-    }
+  if (GetCurrentDirectoryW (len, wdir) == len - 1)
+    dir = g_utf16_to_utf8 (wdir, -1, NULL, NULL, NULL);
+  
+  g_free (wdir);
 
   if (dir == NULL)
     dir = g_strdup ("\\");
@@ -935,6 +1064,7 @@ g_get_current_dir (void)
 #else  /* !sun || !HAVE_GETCWD */
   while (max_len < G_MAXULONG / 2)
     {
+      g_free (buffer);
       buffer = g_new (gchar, max_len + 1);
       *buffer = 0;
       dir = getcwd (buffer, max_len);
@@ -942,7 +1072,6 @@ g_get_current_dir (void)
       if (dir || errno != ERANGE)
        break;
 
-      g_free (buffer);
       max_len *= 2;
     }
 #endif /* !sun || !HAVE_GETCWD */
@@ -979,7 +1108,7 @@ g_get_current_dir (void)
  * overwritten by the next call to g_getenv(), g_setenv() or
  * g_unsetenv().
  **/
-G_CONST_RETURN gchar*
+const gchar *
 g_getenv (const gchar *variable)
 {
 #ifndef G_OS_WIN32
@@ -992,6 +1121,8 @@ g_getenv (const gchar *variable)
 
   GQuark quark;
   gchar *value;
+  wchar_t dummy[2], *wname, *wvalue;
+  int len;
 
   g_return_val_if_fail (variable != NULL, NULL);
   g_return_val_if_fail (g_utf8_validate (variable, -1, NULL), NULL);
@@ -1006,110 +1137,51 @@ g_getenv (const gchar *variable)
    * contain references to other environment variables.)
    */
 
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t dummy[2], *wname, *wvalue;
-      int len;
-      
-      wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
-
-      len = GetEnvironmentVariableW (wname, dummy, 2);
-
-      if (len == 0)
-       {
-         g_free (wname);
-         return NULL;
-       }
-      else if (len == 1)
-       len = 2;
-
-      wvalue = g_new (wchar_t, len);
-
-      if (GetEnvironmentVariableW (wname, wvalue, len) != len - 1)
-       {
-         g_free (wname);
-         g_free (wvalue);
-         return NULL;
-       }
-
-      if (wcschr (wvalue, L'%') != NULL)
-       {
-         wchar_t *tem = wvalue;
+  wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
 
-         len = ExpandEnvironmentStringsW (wvalue, dummy, 2);
+  len = GetEnvironmentVariableW (wname, dummy, 2);
 
-         if (len > 0)
-           {
-             wvalue = g_new (wchar_t, len);
-
-             if (ExpandEnvironmentStringsW (tem, wvalue, len) != len)
-               {
-                 g_free (wvalue);
-                 wvalue = tem;
-               }
-             else
-               g_free (tem);
-           }
-       }
+  if (len == 0)
+    {
+      g_free (wname);
+      return NULL;
+    }
+  else if (len == 1)
+    len = 2;
 
-      value = g_utf16_to_utf8 (wvalue, -1, NULL, NULL, NULL);
+  wvalue = g_new (wchar_t, len);
 
+  if (GetEnvironmentVariableW (wname, wvalue, len) != len - 1)
+    {
       g_free (wname);
       g_free (wvalue);
+      return NULL;
     }
-  else
-    {
-      gchar dummy[3], *cpname, *cpvalue;
-      int len;
-      
-      cpname = g_locale_from_utf8 (variable, -1, NULL, NULL, NULL);
-
-      g_return_val_if_fail (cpname != NULL, NULL);
-
-      len = GetEnvironmentVariableA (cpname, dummy, 2);
-
-      if (len == 0)
-       {
-         g_free (cpname);
-         return NULL;
-       }
-      else if (len == 1)
-       len = 2;
 
-      cpvalue = g_new (gchar, len);
+  if (wcschr (wvalue, L'%') != NULL)
+    {
+      wchar_t *tem = wvalue;
 
-      if (GetEnvironmentVariableA (cpname, cpvalue, len) != len - 1)
-       {
-         g_free (cpname);
-         g_free (cpvalue);
-         return NULL;
-       }
+      len = ExpandEnvironmentStringsW (wvalue, dummy, 2);
 
-      if (strchr (cpvalue, '%') != NULL)
+      if (len > 0)
        {
-         gchar *tem = cpvalue;
-
-         len = ExpandEnvironmentStringsA (cpvalue, dummy, 3);
+         wvalue = g_new (wchar_t, len);
 
-         if (len > 0)
+         if (ExpandEnvironmentStringsW (tem, wvalue, len) != len)
            {
-             cpvalue = g_new (gchar, len);
-
-             if (ExpandEnvironmentStringsA (tem, cpvalue, len) != len)
-               {
-                 g_free (cpvalue);
-                 cpvalue = tem;
-               }
-             else
-               g_free (tem);
+             g_free (wvalue);
+             wvalue = tem;
            }
+         else
+           g_free (tem);
        }
+    }
 
-      value = g_locale_to_utf8 (cpvalue, -1, NULL, NULL, NULL);
+  value = g_utf16_to_utf8 (wvalue, -1, NULL, NULL, NULL);
 
-      g_free (cpname);
-      g_free (cpvalue);
-    }
+  g_free (wname);
+  g_free (wvalue);
 
   quark = g_quark_from_string (value);
   g_free (value);
@@ -1119,6 +1191,30 @@ g_getenv (const gchar *variable)
 #endif /* G_OS_WIN32 */
 }
 
+/* _g_getenv_nomalloc
+ * this function does a getenv() without doing any kind of allocation
+ * through glib. it's suitable for chars <= 127 only (both, for the
+ * variable name and the contents) and for contents < 1024 chars in
+ * length. also, it aliases "" to a NULL return value.
+ **/
+const gchar*
+_g_getenv_nomalloc (const gchar *variable,
+                    gchar        buffer[1024])
+{
+  const gchar *retval = getenv (variable);
+  if (retval && retval[0])
+    {
+      gint l = strlen (retval);
+      if (l < 1024)
+        {
+          strncpy (buffer, retval, l);
+          buffer[l] = 0;
+          return buffer;
+        }
+    }
+  return NULL;
+}
+
 /**
  * g_setenv:
  * @variable: the environment variable to set, must not contain '='.
@@ -1170,6 +1266,8 @@ g_setenv (const gchar *variable,
 #else /* G_OS_WIN32 */
 
   gboolean retval;
+  wchar_t *wname, *wvalue, *wassignment;
+  gchar *tem;
 
   g_return_val_if_fail (variable != NULL, FALSE);
   g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
@@ -1193,51 +1291,35 @@ g_setenv (const gchar *variable,
    * the putenv() first, then call SetEnvironmentValueW ourselves.
    */
 
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t *wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
-      wchar_t *wvalue = g_utf8_to_utf16 (value, -1, NULL, NULL, NULL);
-      gchar *tem = g_strconcat (variable, "=", value, NULL);
-      wchar_t *wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
-      
-      g_free (tem);
-      _wputenv (wassignment);
-      g_free (wassignment);
-
-      retval = (SetEnvironmentVariableW (wname, wvalue) != 0);
-
-      g_free (wname);
-      g_free (wvalue);
-    }
-  else
-    {
-      /* In the non-Unicode case (Win9x), just putenv() is good
-       * enough.
-       */
-      gchar *tem = g_strconcat (variable, "=", value, NULL);
-      gchar *cpassignment = g_locale_from_utf8 (tem, -1, NULL, NULL, NULL);
+  wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
+  wvalue = g_utf8_to_utf16 (value, -1, NULL, NULL, NULL);
+  tem = g_strconcat (variable, "=", value, NULL);
+  wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
+    
+  g_free (tem);
+  _wputenv (wassignment);
+  g_free (wassignment);
 
-      g_free (tem);
-      
-      retval = (putenv (cpassignment) == 0);
+  retval = (SetEnvironmentVariableW (wname, wvalue) != 0);
 
-      g_free (cpassignment);
-    }
+  g_free (wname);
+  g_free (wvalue);
 
   return retval;
 
 #endif /* G_OS_WIN32 */
 }
 
-#ifndef G_OS_WIN32
+#ifdef HAVE__NSGETENVIRON
+#define environ (*_NSGetEnviron())
+#elif !defined(G_OS_WIN32)
 
 /* According to the Single Unix Specification, environ is not in 
  * any system header, although unistd.h often declares it.
  */
 extern char **environ;
-
 #endif
-           
+
 /**
  * g_unsetenv:
  * @variable: the environment variable to remove, must not contain '='.
@@ -1291,38 +1373,24 @@ g_unsetenv (const gchar *variable)
 
 #else  /* G_OS_WIN32 */
 
+  wchar_t *wname, *wassignment;
+  gchar *tem;
+
   g_return_if_fail (variable != NULL);
   g_return_if_fail (strchr (variable, '=') == NULL);
   g_return_if_fail (g_utf8_validate (variable, -1, NULL));
 
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t *wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
-      gchar *tem = g_strconcat (variable, "=", NULL);
-      wchar_t *wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
-      
-      g_free (tem);
-      _wputenv (wassignment);
-      g_free (wassignment);
-
-      SetEnvironmentVariableW (wname, NULL);
-
-      g_free (wname);
-    }
-  else
-    {
-      /* In the non-Unicode case (Win9x), just putenv() is good
-       * enough.
-       */
-      gchar *tem = g_strconcat (variable, "=", NULL);
-      gchar *cpassignment = g_locale_from_utf8 (tem, -1, NULL, NULL, NULL);
+  wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
+  tem = g_strconcat (variable, "=", NULL);
+  wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
+    
+  g_free (tem);
+  _wputenv (wassignment);
+  g_free (wassignment);
 
-      g_free (tem);
-      
-      putenv (cpassignment);
+  SetEnvironmentVariableW (wname, NULL);
 
-      g_free (cpassignment);
-    }
+  g_free (wname);
 
 #endif /* G_OS_WIN32 */
 }
@@ -1335,11 +1403,19 @@ g_unsetenv (const gchar *variable)
  * Returns: a %NULL-terminated list of strings which must be freed
  * with g_strfreev().
  *
+ * Programs that want to be portable to Windows should typically use
+ * this function and g_getenv() instead of using the environ array
+ * from the C library directly. On Windows, the strings in the environ
+ * array are in system codepage encoding, while in most of the typical
+ * use cases for environment variables in GLib-using programs you want
+ * the UTF-8 encoding that this function and g_getenv() provide.
+ *
  * Since: 2.8
  */
 gchar **
 g_listenv (void)
 {
+#ifndef G_OS_WIN32
   gchar **result, *eq;
   gint len, i, j;
 
@@ -1357,6 +1433,85 @@ g_listenv (void)
   result[j] = NULL;
 
   return result;
+#else
+  gchar **result, *eq;
+  gint len = 0, j;
+  wchar_t *p, *q;
+
+  p = (wchar_t *) GetEnvironmentStringsW ();
+  if (p != NULL)
+    {
+      q = p;
+      while (*q)
+       {
+         q += wcslen (q) + 1;
+         len++;
+       }
+    }
+  result = g_new0 (gchar *, len + 1);
+
+  j = 0;
+  q = p;
+  while (*q)
+    {
+      result[j] = g_utf16_to_utf8 (q, -1, NULL, NULL, NULL);
+      if (result[j] != NULL)
+       {
+         eq = strchr (result[j], '=');
+         if (eq && eq > result[j])
+           {
+             *eq = '\0';
+             j++;
+           }
+         else
+           g_free (result[j]);
+       }
+      q += wcslen (q) + 1;
+    }
+  result[j] = NULL;
+  FreeEnvironmentStringsW (p);
+
+  return result;
+#endif
+}
+
+/**
+ * g_get_environ:
+ * 
+ * Gets the list of environment variables for the current process.  The
+ * list is %NULL terminated and each item in the list is of the form
+ * 'NAME=VALUE'.
+ *
+ * This is equivalent to direct access to the 'environ' global variable,
+ * except portable.
+ *
+ * The return value is freshly allocated and it should be freed with
+ * g_strfreev() when it is no longer needed.
+ *
+ * Returns: the list of environment variables
+ *
+ * Since: 2.28
+ */
+gchar **
+g_get_environ (void)
+{
+#ifndef G_OS_WIN32
+  return g_strdupv (environ);
+#else
+  gunichar2 *strings;
+  gchar **result;
+  gint i, n;
+
+  strings = GetEnvironmentStringsW ();
+  for (n = 0; strings[n]; n += wcslen (strings + n) + 1);
+  result = g_new (char *, n + 1);
+  for (i = 0; strings[i]; i += wcslen (strings + i) + 1)
+    result[i] = g_utf16_to_utf8 (strings + i, -1, NULL, NULL, NULL);
+  FreeEnvironmentStringsW (strings);
+  result[i] = NULL;
+
+  return result;
+#endif
 }
 
 G_LOCK_DEFINE_STATIC (g_utils_global);
@@ -1383,15 +1538,17 @@ static  gchar   *g_user_cache_dir = NULL;
 static  gchar   *g_user_config_dir = NULL;
 static  gchar  **g_system_config_dirs = NULL;
 
+static  gchar  **g_user_special_dirs = NULL;
+
+/* fifteen minutes of fame for everybody */
+#define G_USER_DIRS_EXPIRE      15 * 60
+
 #ifdef G_OS_WIN32
 
 static gchar *
 get_special_folder (int csidl)
 {
-  union {
-    char c[MAX_PATH+1];
-    wchar_t wc[MAX_PATH+1];
-  } path;
+  wchar_t path[MAX_PATH+1];
   HRESULT hr;
   LPITEMIDLIST pidl = NULL;
   BOOL b;
@@ -1400,18 +1557,9 @@ get_special_folder (int csidl)
   hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
   if (hr == S_OK)
     {
-      if (G_WIN32_HAVE_WIDECHAR_API ())
-       {
-         b = SHGetPathFromIDListW (pidl, path.wc);
-         if (b)
-           retval = g_utf16_to_utf8 (path.wc, -1, NULL, NULL, NULL);
-       }
-      else
-       {
-         b = SHGetPathFromIDListA (pidl, path.c);
-         if (b)
-           retval = g_locale_to_utf8 (path.c, -1, NULL, NULL, NULL);
-       }
+      b = SHGetPathFromIDListW (pidl, path);
+      if (b)
+       retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
       CoTaskMemFree (pidl);
     }
   return retval;
@@ -1420,18 +1568,24 @@ get_special_folder (int csidl)
 static char *
 get_windows_directory_root (void)
 {
-  char windowsdir[MAX_PATH];
+  wchar_t wwindowsdir[MAX_PATH];
 
-  if (GetWindowsDirectory (windowsdir, sizeof (windowsdir)))
+  if (GetWindowsDirectoryW (wwindowsdir, G_N_ELEMENTS (wwindowsdir)))
     {
       /* Usually X:\Windows, but in terminal server environments
        * might be an UNC path, AFAIK.
        */
-      char *p = (char *) g_path_skip_root (windowsdir);
+      char *windowsdir = g_utf16_to_utf8 (wwindowsdir, -1, NULL, NULL, NULL);
+      char *p;
+
+      if (windowsdir == NULL)
+       return g_strdup ("C:\\");
+
+      p = (char *) g_path_skip_root (windowsdir);
       if (G_IS_DIR_SEPARATOR (p[-1]) && p[-2] != ':')
        p--;
       *p = '\0';
-      return g_strdup (windowsdir);
+      return windowsdir;
     }
   else
     return g_strdup ("C:\\");
@@ -1446,17 +1600,17 @@ g_get_any_init_do (void)
   gchar hostname[100];
 
   g_tmp_dir = g_strdup (g_getenv ("TMPDIR"));
-  if (!g_tmp_dir)
+  if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
     g_tmp_dir = g_strdup (g_getenv ("TMP"));
-  if (!g_tmp_dir)
+  if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
     g_tmp_dir = g_strdup (g_getenv ("TEMP"));
 
 #ifdef G_OS_WIN32
-  if (!g_tmp_dir)
+  if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
     g_tmp_dir = get_windows_directory_root ();
 #else  
 #ifdef P_tmpdir
-  if (!g_tmp_dir)
+  if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
     {
       gsize k;    
       g_tmp_dir = g_strdup (P_tmpdir);
@@ -1466,7 +1620,7 @@ g_get_any_init_do (void)
     }
 #endif
   
-  if (!g_tmp_dir)
+  if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
     {
       g_tmp_dir = g_strdup ("/tmp");
     }
@@ -1518,7 +1672,8 @@ g_get_any_init_do (void)
     struct passwd *pw = NULL;
     gpointer buffer = NULL;
     gint error;
-    
+    gchar *logname;
+
 #  if defined (HAVE_POSIX_GETPWUID_R) || defined (HAVE_NONPOSIX_GETPWUID_R)
     struct passwd pwd;
 #    ifdef _SC_GETPW_R_SIZE_MAX  
@@ -1530,7 +1685,9 @@ g_get_any_init_do (void)
 #    else /* _SC_GETPW_R_SIZE_MAX */
     glong bufsize = 64;
 #    endif /* _SC_GETPW_R_SIZE_MAX */
-    
+
+    logname = (gchar *) g_getenv ("LOGNAME");
+        
     do
       {
        g_free (buffer);
@@ -1541,7 +1698,15 @@ g_get_any_init_do (void)
        errno = 0;
        
 #    ifdef HAVE_POSIX_GETPWUID_R
-       error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+       if (logname) {
+         error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
+         if (!pw || (pw->pw_uid != getuid ())) {
+           /* LOGNAME is lying, fall back to looking up the uid */
+           error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+         }
+       } else {
+         error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+       }
        error = error < 0 ? errno : error;
 #    else /* HAVE_NONPOSIX_GETPWUID_R */
    /* HPUX 11 falls into the HAVE_POSIX_GETPWUID_R case */
@@ -1549,7 +1714,15 @@ g_get_any_init_do (void)
        error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
        pw = error == 0 ? &pwd : NULL;
 #      else /* !_AIX */
-       pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+       if (logname) {
+         pw = getpwnam_r (logname, &pwd, buffer, bufsize);
+         if (!pw || (pw->pw_uid != getuid ())) {
+           /* LOGNAME is lying, fall back to looking up the uid */
+           pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+         }
+       } else {
+         pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+       }
        error = pw ? 0 : errno;
 #      endif /* !_AIX */            
 #    endif /* HAVE_NONPOSIX_GETPWUID_R */
@@ -1613,28 +1786,16 @@ g_get_any_init_do (void)
 #else /* !HAVE_PWD_H */
   
 #ifdef G_OS_WIN32
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      guint len = UNLEN+1;
-      wchar_t buffer[UNLEN+1];
-      
-      if (GetUserNameW (buffer, (LPDWORD) &len))
-       {
-         g_user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
-         g_real_name = g_strdup (g_user_name);
-       }
-    }
-  else
-    {
-      guint len = UNLEN+1;
-      char buffer[UNLEN+1];
-      
-      if (GetUserNameA (buffer, (LPDWORD) &len))
-       {
-         g_user_name = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
-         g_real_name = g_strdup (g_user_name);
-       }
-    }
+  {
+    guint len = UNLEN+1;
+    wchar_t buffer[UNLEN+1];
+    
+    if (GetUserNameW (buffer, (LPDWORD) &len))
+      {
+       g_user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+       g_real_name = g_strdup (g_user_name);
+      }
+  }
 #endif /* G_OS_WIN32 */
 
 #endif /* !HAVE_PWD_H */
@@ -1711,7 +1872,7 @@ g_get_any_init_locked (void)
  *
  * Returns: the user name of the current user.
  */
-G_CONST_RETURN gchar*
+const gchar *
 g_get_user_name (void)
 {
   g_get_any_init_locked ();
@@ -1729,7 +1890,7 @@ g_get_user_name (void)
  *
  * Returns: the user's real name.
  */
-G_CONST_RETURN gchar*
+const gchar *
 g_get_real_name (void)
 {
   g_get_any_init_locked ();
@@ -1739,15 +1900,35 @@ g_get_real_name (void)
 /**
  * g_get_home_dir:
  *
- * Gets the current user's home directory. 
+ * Gets the current user's home directory as defined in the 
+ * password database.
  *
  * Note that in contrast to traditional UNIX tools, this function 
  * prefers <filename>passwd</filename> entries over the <envar>HOME</envar> 
- * environment variable.
- *
- * Returns: the current user's home directory.
+ * environment variable. 
+ *
+ * One of the reasons for this decision is that applications in many 
+ * cases need special handling to deal with the case where 
+ * <envar>HOME</envar> is
+ * <simplelist>
+ *   <member>Not owned by the user</member>
+ *   <member>Not writeable</member>
+ *   <member>Not even readable</member>
+ * </simplelist>
+ * Since applications are in general <emphasis>not</emphasis> written 
+ * to deal with these situations it was considered better to make 
+ * g_get_home_dir() not pay attention to <envar>HOME</envar> and to 
+ * return the real home directory for the user. If applications
+ * want to pay attention to <envar>HOME</envar>, they can do:
+ * |[
+ *  const char *homedir = g_getenv ("HOME");
+ *   if (!homedir)
+ *      homedir = g_get_home_dir (<!-- -->);
+ * ]|
+ *
+ * Returns: the current user's home directory
  */
-G_CONST_RETURN gchar*
+const gchar *
 g_get_home_dir (void)
 {
   g_get_any_init_locked ();
@@ -1762,11 +1943,11 @@ g_get_home_dir (void)
  * <envar>TMP</envar>, and <envar>TEMP</envar> in that order. If none 
  * of those are defined "/tmp" is returned on UNIX and "C:\" on Windows. 
  * The encoding of the returned string is system-defined. On Windows, 
- * it is always UTF-8. The return value is never %NULL.
+ * it is always UTF-8. The return value is never %NULL or the empty string.
  *
  * Returns: the directory to use for temporary files.
  */
-G_CONST_RETURN gchar*
+const gchar *
 g_get_tmp_dir (void)
 {
   g_get_any_init_locked ();
@@ -1829,22 +2010,13 @@ g_get_prgname (void)
       if (!beenhere)
        {
          gchar *utf8_buf = NULL;
+         wchar_t buf[MAX_PATH+1];
 
          beenhere = TRUE;
-         if (G_WIN32_HAVE_WIDECHAR_API ())
-           {
-             wchar_t buf[MAX_PATH+1];
-             if (GetModuleFileNameW (GetModuleHandle (NULL),
-                                     buf, G_N_ELEMENTS (buf)) > 0)
-               utf8_buf = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
-           }
-         else
-           {
-             gchar buf[MAX_PATH+1];
-             if (GetModuleFileNameA (GetModuleHandle (NULL),
-                                     buf, G_N_ELEMENTS (buf)) > 0)
-               utf8_buf = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
-           }
+         if (GetModuleFileNameW (GetModuleHandle (NULL),
+                                 buf, G_N_ELEMENTS (buf)) > 0)
+           utf8_buf = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
+
          if (utf8_buf)
            {
              g_prgname = g_path_get_basename (utf8_buf);
@@ -1894,7 +2066,7 @@ static gchar *g_application_name = NULL;
  *
  * Since: 2.2
  **/
-G_CONST_RETURN gchar*
+const gchar *
 g_get_application_name (void)
 {
   gchar* retval;
@@ -1925,6 +2097,7 @@ g_get_application_name (void)
  * The application name will be used in contexts such as error messages,
  * or when displaying an application's name in the task list.
  * 
+ * Since: 2.2
  **/
 void
 g_set_application_name (const gchar *application_name)
@@ -1939,7 +2112,7 @@ g_set_application_name (const gchar *application_name)
   G_UNLOCK (g_application_name);
 
   if (already_set)
-    g_warning ("g_set_application() name called multiple times");
+    g_warning ("g_set_application_name() called multiple times");
 }
 
 /**
@@ -1950,13 +2123,19 @@ g_set_application_name (const gchar *application_name)
  *
  * On UNIX platforms this is determined using the mechanisms described in
  * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>
- * 
+ * XDG Base Directory Specification</ulink>.
+ * In this case the directory retrieved will be XDG_DATA_HOME.
+ *
+ * On Windows this is the folder to use for local (as opposed to
+ * roaming) application data. See documentation for
+ * CSIDL_LOCAL_APPDATA. Note that on Windows it thus is the same as
+ * what g_get_user_config_dir() returns.
+ *
  * Return value: a string owned by GLib that must not be modified 
  *               or freed.
  * Since: 2.6
  **/
-G_CONST_RETURN gchar*
+const gchar *
 g_get_user_data_dir (void)
 {
   gchar *data_dir;  
@@ -1966,7 +2145,7 @@ g_get_user_data_dir (void)
   if (!g_user_data_dir)
     {
 #ifdef G_OS_WIN32
-      data_dir = get_special_folder (CSIDL_PERSONAL);
+      data_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
 #else
       data_dir = (gchar *) g_getenv ("XDG_DATA_HOME");
 
@@ -1995,31 +2174,15 @@ g_get_user_data_dir (void)
   return data_dir;
 }
 
-/**
- * g_get_user_config_dir:
- * 
- * Returns a base directory in which to store user-specific application 
- * configuration information such as user preferences and settings. 
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>
- * 
- * Return value: a string owned by GLib that must not be modified 
- *               or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar*
-g_get_user_config_dir (void)
+static void
+g_init_user_config_dir (void)
 {
-  gchar *config_dir;  
-
-  G_LOCK (g_utils_global);
+  gchar *config_dir;
 
   if (!g_user_config_dir)
     {
 #ifdef G_OS_WIN32
-      config_dir = get_special_folder (CSIDL_APPDATA);
+      config_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
 #else
       config_dir = (gchar *) g_getenv ("XDG_CONFIG_HOME");
 
@@ -2035,14 +2198,41 @@ g_get_user_config_dir (void)
          else
            config_dir = g_build_filename (g_tmp_dir, g_user_name, ".config", NULL);
        }
+
       g_user_config_dir = config_dir;
     }
-  else
-    config_dir = g_user_config_dir;
+}
+
+/**
+ * g_get_user_config_dir:
+ * 
+ * Returns a base directory in which to store user-specific application 
+ * configuration information such as user preferences and settings. 
+ *
+ * On UNIX platforms this is determined using the mechanisms described in
+ * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
+ * XDG Base Directory Specification</ulink>.
+ * In this case the directory retrieved will be XDG_CONFIG_HOME.
+ *
+ * On Windows this is the folder to use for local (as opposed to
+ * roaming) application data. See documentation for
+ * CSIDL_LOCAL_APPDATA. Note that on Windows it thus is the same as
+ * what g_get_user_data_dir() returns.
+ *
+ * Return value: a string owned by GLib that must not be modified 
+ *               or freed.
+ * Since: 2.6
+ **/
+const gchar *
+g_get_user_config_dir (void)
+{
+  G_LOCK (g_utils_global);
+
+  g_init_user_config_dir ();
 
   G_UNLOCK (g_utils_global);
 
-  return config_dir;
+  return g_user_config_dir;
 }
 
 /**
@@ -2053,13 +2243,19 @@ g_get_user_config_dir (void)
  *
  * On UNIX platforms this is determined using the mechanisms described in
  * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>
- * 
+ * XDG Base Directory Specification</ulink>.
+ * In this case the directory retrieved will be XDG_CACHE_HOME.
+ *
+ * On Windows is the directory that serves as a common repository for
+ * temporary Internet files. A typical path is
+ * C:\Documents and Settings\username\Local Settings\Temporary Internet Files.
+ * See documentation for CSIDL_INTERNET_CACHE.
+ *
  * Return value: a string owned by GLib that must not be modified 
  *               or freed.
  * Since: 2.6
  **/
-G_CONST_RETURN gchar*
+const gchar *
 g_get_user_cache_dir (void)
 {
   gchar *cache_dir;  
@@ -2095,6 +2291,444 @@ g_get_user_cache_dir (void)
   return cache_dir;
 }
 
+/**
+ * g_get_user_runtime_dir:
+ *
+ * Returns a directory that is unique to the current user on the local
+ * system.
+ *
+ * On UNIX platforms this is determined using the mechanisms described in
+ * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
+ * XDG Base Directory Specification</ulink>.  This is the directory
+ * specified in the <envar>XDG_RUNTIME_DIR</envar> environment variable.
+ * In the case that this variable is not set, GLib will issue a warning
+ * message to stderr and return the value of g_get_user_cache_dir().
+ *
+ * On Windows this is the folder to use for local (as opposed to
+ * roaming) application data. See documentation for
+ * CSIDL_LOCAL_APPDATA.  Note that on Windows it thus is the same as
+ * what g_get_user_config_dir() returns.
+ *
+ * Returns: a string owned by GLib that must not be modified or freed.
+ *
+ * Since: 2.28
+ **/
+const gchar *
+g_get_user_runtime_dir (void)
+{
+#ifndef G_OS_WIN32
+  static const gchar *runtime_dir;
+  static gsize initialised;
+
+  if (g_once_init_enter (&initialised))
+    {
+      runtime_dir = g_strdup (getenv ("XDG_RUNTIME_DIR"));
+      
+      if (runtime_dir == NULL)
+        g_warning ("XDG_RUNTIME_DIR variable not set.  "
+                   "Falling back to XDG cache dir.");
+
+      g_once_init_leave (&initialised, 1);
+    }
+
+  if (runtime_dir)
+    return runtime_dir;
+
+  /* Both fallback for UNIX and the default
+   * in Windows: use the user cache directory.
+   */
+#endif
+
+  return g_get_user_cache_dir ();
+}
+
+#ifdef HAVE_CARBON
+
+static gchar *
+find_folder (OSType type)
+{
+  gchar *filename = NULL;
+  FSRef  found;
+
+  if (FSFindFolder (kUserDomain, type, kDontCreateFolder, &found) == noErr)
+    {
+      CFURLRef url = CFURLCreateFromFSRef (kCFAllocatorSystemDefault, &found);
+
+      if (url)
+       {
+         CFStringRef path = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
+
+         if (path)
+           {
+             filename = g_strdup (CFStringGetCStringPtr (path, kCFStringEncodingUTF8));
+
+             if (! filename)
+               {
+                 filename = g_new0 (gchar, CFStringGetLength (path) * 3 + 1);
+
+                 CFStringGetCString (path, filename,
+                                     CFStringGetLength (path) * 3 + 1,
+                                     kCFStringEncodingUTF8);
+               }
+
+             CFRelease (path);
+           }
+
+         CFRelease (url);
+       }
+    }
+
+  return filename;
+}
+
+static void
+load_user_special_dirs (void)
+{
+  g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = find_folder (kDesktopFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = find_folder (kDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = find_folder (kDesktopFolderType); /* XXX correct ? */
+  g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = find_folder (kMusicDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = find_folder (kPictureDocumentsFolderType);
+  g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = NULL;
+  g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = NULL;
+  g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = find_folder (kMovieDocumentsFolderType);
+}
+
+#endif /* HAVE_CARBON */
+
+#if defined(G_OS_WIN32)
+static void
+load_user_special_dirs (void)
+{
+  typedef HRESULT (WINAPI *t_SHGetKnownFolderPath) (const GUID *rfid,
+                                                   DWORD dwFlags,
+                                                   HANDLE hToken,
+                                                   PWSTR *ppszPath);
+  t_SHGetKnownFolderPath p_SHGetKnownFolderPath;
+
+  static const GUID FOLDERID_Downloads =
+    { 0x374de290, 0x123f, 0x4565, { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b } };
+  static const GUID FOLDERID_Public =
+    { 0xDFDF76A2, 0xC82A, 0x4D63, { 0x90, 0x6A, 0x56, 0x44, 0xAC, 0x45, 0x73, 0x85 } };
+
+  wchar_t *wcp;
+
+  p_SHGetKnownFolderPath = (t_SHGetKnownFolderPath) GetProcAddress (GetModuleHandle ("shell32.dll"),
+                                                                   "SHGetKnownFolderPath");
+
+  g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
+  g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = get_special_folder (CSIDL_PERSONAL);
+
+  if (p_SHGetKnownFolderPath == NULL)
+    {
+      g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
+    }
+  else
+    {
+      wcp = NULL;
+      (*p_SHGetKnownFolderPath) (&FOLDERID_Downloads, 0, NULL, &wcp);
+      if (wcp)
+        {
+          g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = g_utf16_to_utf8 (wcp, -1, NULL, NULL, NULL);
+          if (g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] == NULL)
+              g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
+          CoTaskMemFree (wcp);
+        }
+      else
+          g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
+    }
+
+  g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = get_special_folder (CSIDL_MYMUSIC);
+  g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = get_special_folder (CSIDL_MYPICTURES);
+
+  if (p_SHGetKnownFolderPath == NULL)
+    {
+      /* XXX */
+      g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);
+    }
+  else
+    {
+      wcp = NULL;
+      (*p_SHGetKnownFolderPath) (&FOLDERID_Public, 0, NULL, &wcp);
+      if (wcp)
+        {
+          g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = g_utf16_to_utf8 (wcp, -1, NULL, NULL, NULL);
+          if (g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] == NULL)
+              g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);
+          CoTaskMemFree (wcp);
+        }
+      else
+          g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);
+    }
+  
+  g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = get_special_folder (CSIDL_TEMPLATES);
+  g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = get_special_folder (CSIDL_MYVIDEO);
+}
+#endif /* G_OS_WIN32 */
+
+static void g_init_user_config_dir (void);
+
+#if defined(G_OS_UNIX) && !defined(HAVE_CARBON)
+
+/* adapted from xdg-user-dir-lookup.c
+ *
+ * Copyright (C) 2007 Red Hat Inc.
+ *
+ * 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 without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions: 
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software. 
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE 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.
+ */
+static void
+load_user_special_dirs (void)
+{
+  gchar *config_file;
+  gchar *data;
+  gchar **lines;
+  gint n_lines, i;
+  
+  g_init_user_config_dir ();
+  config_file = g_build_filename (g_user_config_dir,
+                                  "user-dirs.dirs",
+                                  NULL);
+  
+  if (!g_file_get_contents (config_file, &data, NULL, NULL))
+    {
+      g_free (config_file);
+      return;
+    }
+
+  lines = g_strsplit (data, "\n", -1);
+  n_lines = g_strv_length (lines);
+  g_free (data);
+  
+  for (i = 0; i < n_lines; i++)
+    {
+      gchar *buffer = lines[i];
+      gchar *d, *p;
+      gint len;
+      gboolean is_relative = FALSE;
+      GUserDirectory directory;
+
+      /* Remove newline at end */
+      len = strlen (buffer);
+      if (len > 0 && buffer[len - 1] == '\n')
+       buffer[len - 1] = 0;
+      
+      p = buffer;
+      while (*p == ' ' || *p == '\t')
+       p++;
+      
+      if (strncmp (p, "XDG_DESKTOP_DIR", strlen ("XDG_DESKTOP_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DESKTOP;
+          p += strlen ("XDG_DESKTOP_DIR");
+        }
+      else if (strncmp (p, "XDG_DOCUMENTS_DIR", strlen ("XDG_DOCUMENTS_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DOCUMENTS;
+          p += strlen ("XDG_DOCUMENTS_DIR");
+        }
+      else if (strncmp (p, "XDG_DOWNLOAD_DIR", strlen ("XDG_DOWNLOAD_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_DOWNLOAD;
+          p += strlen ("XDG_DOWNLOAD_DIR");
+        }
+      else if (strncmp (p, "XDG_MUSIC_DIR", strlen ("XDG_MUSIC_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_MUSIC;
+          p += strlen ("XDG_MUSIC_DIR");
+        }
+      else if (strncmp (p, "XDG_PICTURES_DIR", strlen ("XDG_PICTURES_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_PICTURES;
+          p += strlen ("XDG_PICTURES_DIR");
+        }
+      else if (strncmp (p, "XDG_PUBLICSHARE_DIR", strlen ("XDG_PUBLICSHARE_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_PUBLIC_SHARE;
+          p += strlen ("XDG_PUBLICSHARE_DIR");
+        }
+      else if (strncmp (p, "XDG_TEMPLATES_DIR", strlen ("XDG_TEMPLATES_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_TEMPLATES;
+          p += strlen ("XDG_TEMPLATES_DIR");
+        }
+      else if (strncmp (p, "XDG_VIDEOS_DIR", strlen ("XDG_VIDEOS_DIR")) == 0)
+        {
+          directory = G_USER_DIRECTORY_VIDEOS;
+          p += strlen ("XDG_VIDEOS_DIR");
+        }
+      else
+       continue;
+
+      while (*p == ' ' || *p == '\t')
+       p++;
+
+      if (*p != '=')
+       continue;
+      p++;
+
+      while (*p == ' ' || *p == '\t')
+       p++;
+
+      if (*p != '"')
+       continue;
+      p++;
+
+      if (strncmp (p, "$HOME", 5) == 0)
+       {
+         p += 5;
+         is_relative = TRUE;
+       }
+      else if (*p != '/')
+       continue;
+
+      d = strrchr (p, '"');
+      if (!d)
+        continue;
+      *d = 0;
+
+      d = p;
+      
+      /* remove trailing slashes */
+      len = strlen (d);
+      if (d[len - 1] == '/')
+        d[len - 1] = 0;
+      
+      if (is_relative)
+        {
+          g_get_any_init ();
+          g_user_special_dirs[directory] = g_build_filename (g_home_dir, d, NULL);
+        }
+      else
+       g_user_special_dirs[directory] = g_strdup (d);
+    }
+
+  g_strfreev (lines);
+  g_free (config_file);
+}
+
+#endif /* G_OS_UNIX && !HAVE_CARBON */
+
+
+/**
+ * g_reload_user_special_dirs_cache:
+ *
+ * Resets the cache used for g_get_user_special_dir(), so
+ * that the latest on-disk version is used. Call this only
+ * if you just changed the data on disk yourself.
+ *
+ * Due to threadsafety issues this may cause leaking of strings
+ * that were previously returned from g_get_user_special_dir()
+ * that can't be freed. We ensure to only leak the data for
+ * the directories that actually changed value though.
+ *
+ * Since: 2.22
+ */
+void
+g_reload_user_special_dirs_cache (void)
+{
+  int i;
+
+  G_LOCK (g_utils_global);
+
+  if (g_user_special_dirs != NULL)
+    {
+      /* save a copy of the pointer, to check if some memory can be preserved */
+      char **old_g_user_special_dirs = g_user_special_dirs;
+      char *old_val;
+
+      /* recreate and reload our cache */
+      g_user_special_dirs = g_new0 (gchar *, G_USER_N_DIRECTORIES);
+      load_user_special_dirs ();
+
+      /* only leak changed directories */
+      for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+        {
+         old_val = old_g_user_special_dirs[i];
+         if (g_strcmp0 (old_val, g_user_special_dirs[i]) == 0)
+            {
+             /* don't leak */
+             g_free (g_user_special_dirs[i]);
+             g_user_special_dirs[i] = old_val;
+            }
+         else
+            g_free (old_val);
+        }
+
+      /* free the old array */
+      g_free (old_g_user_special_dirs);
+    }
+
+  G_UNLOCK (g_utils_global);
+}
+
+/**
+ * g_get_user_special_dir:
+ * @directory: the logical id of special directory
+ *
+ * Returns the full path of a special directory using its logical id.
+ *
+ * On Unix this is done using the XDG special user directories.
+ * For compatibility with existing practise, %G_USER_DIRECTORY_DESKTOP
+ * falls back to <filename>$HOME/Desktop</filename> when XDG special
+ * user directories have not been set up. 
+ *
+ * Depending on the platform, the user might be able to change the path
+ * of the special directory without requiring the session to restart; GLib
+ * will not reflect any change once the special directories are loaded.
+ *
+ * Return value: the path to the specified special directory, or %NULL
+ *   if the logical id was not found. The returned string is owned by
+ *   GLib and should not be modified or freed.
+ *
+ * Since: 2.14
+ */
+const gchar *
+g_get_user_special_dir (GUserDirectory directory)
+{
+  g_return_val_if_fail (directory >= G_USER_DIRECTORY_DESKTOP &&
+                        directory < G_USER_N_DIRECTORIES, NULL);
+
+  G_LOCK (g_utils_global);
+
+  if (G_UNLIKELY (g_user_special_dirs == NULL))
+    {
+      g_user_special_dirs = g_new0 (gchar *, G_USER_N_DIRECTORIES);
+
+      load_user_special_dirs ();
+
+      /* Special-case desktop for historical compatibility */
+      if (g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] == NULL)
+        {
+          g_get_any_init ();
+
+          g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] =
+            g_build_filename (g_home_dir, "Desktop", NULL);
+        }
+    }
+
+  G_UNLOCK (g_utils_global);
+
+  return g_user_special_dirs[directory];
+}
+
 #ifdef G_OS_WIN32
 
 #undef g_get_system_data_dirs
@@ -2107,7 +2741,7 @@ get_module_for_address (gconstpointer address)
   static gboolean beenhere = FALSE;
   typedef BOOL (WINAPI *t_GetModuleHandleExA) (DWORD, LPCTSTR, HMODULE *);
   static t_GetModuleHandleExA p_GetModuleHandleExA = NULL;
-  HMODULE hmodule;
+  HMODULE hmodule = NULL;
 
   if (!address)
     return NULL;
@@ -2115,7 +2749,7 @@ get_module_for_address (gconstpointer address)
   if (!beenhere)
     {
       p_GetModuleHandleExA =
-       (t_GetModuleHandleExA) GetProcAddress (LoadLibrary ("kernel32.dll"),
+       (t_GetModuleHandleExA) GetProcAddress (GetModuleHandle ("kernel32.dll"),
                                               "GetModuleHandleExA");
       beenhere = TRUE;
     }
@@ -2137,55 +2771,34 @@ static gchar *
 get_module_share_dir (gconstpointer address)
 {
   HMODULE hmodule;
-  gchar *filename = NULL;
-  gchar *p, *retval;
+  gchar *filename;
+  gchar *retval;
 
   hmodule = get_module_for_address (address);
   if (hmodule == NULL)
     return NULL;
 
-  if (G_WIN32_IS_NT_BASED ())
-    {
-      wchar_t wfilename[MAX_PATH];
-      if (GetModuleFileNameW (hmodule, wfilename, G_N_ELEMENTS (wfilename)))
-       filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
-    }
-  else
-    {
-      char cpfilename[MAX_PATH];
-      if (GetModuleFileNameA (hmodule, cpfilename, G_N_ELEMENTS (cpfilename)))
-       filename = g_locale_to_utf8 (cpfilename, -1, NULL, NULL, NULL);
-    }
-
-  if (filename == NULL)
-    return NULL;
-
-  if ((p = strrchr (filename, G_DIR_SEPARATOR)) != NULL)
-    *p = '\0';
-
-  p = strrchr (filename, G_DIR_SEPARATOR);
-  if (p && (g_ascii_strcasecmp (p + 1, "bin") == 0))
-    *p = '\0';
-
+  filename = g_win32_get_package_installation_directory_of_module (hmodule);
   retval = g_build_filename (filename, "share", NULL);
   g_free (filename);
 
   return retval;
 }
 
-G_CONST_RETURN gchar * G_CONST_RETURN *
-g_win32_get_system_data_dirs_for_module (gconstpointer address)
+const gchar * const *
+g_win32_get_system_data_dirs_for_module (void (*address_of_function)())
 {
   GArray *data_dirs;
   HMODULE hmodule;
   static GHashTable *per_module_data_dirs = NULL;
   gchar **retval;
   gchar *p;
+  gchar *exe_root;
       
-  if (address)
+  if (address_of_function)
     {
       G_LOCK (g_utils_global);
-      hmodule = get_module_for_address (address);
+      hmodule = get_module_for_address (address_of_function);
       if (hmodule != NULL)
        {
          if (per_module_data_dirs == NULL)
@@ -2197,7 +2810,7 @@ g_win32_get_system_data_dirs_for_module (gconstpointer address)
              if (retval != NULL)
                {
                  G_UNLOCK (g_utils_global);
-                 return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval;
+                 return (const gchar * const *) retval;
                }
            }
        }
@@ -2223,9 +2836,9 @@ g_win32_get_system_data_dirs_for_module (gconstpointer address)
    * subdirectory of the installation directory for the package
    * our caller is a part of.
    *
-   * The address parameter, if non-NULL, points to a function in the
-   * calling module. Use that to determine that module's installation
-   * folder, and use its "share" subfolder.
+   * The address_of_function parameter, if non-NULL, points to a
+   * function in the calling module. Use that to determine that
+   * module's installation folder, and use its "share" subfolder.
    *
    * Additionally, also use the "share" subfolder of the installation
    * locations of GLib and the .exe file being run.
@@ -2237,28 +2850,35 @@ g_win32_get_system_data_dirs_for_module (gconstpointer address)
    * function.
    */
 
-  p = get_module_share_dir (address);
+  p = get_module_share_dir (address_of_function);
   if (p)
     g_array_append_val (data_dirs, p);
     
-  p = g_win32_get_package_installation_subdirectory (NULL, dll_name, "share");
-  if (p)
-    g_array_append_val (data_dirs, p);
+  if (glib_dll != NULL)
+    {
+      gchar *glib_root = g_win32_get_package_installation_directory_of_module (glib_dll);
+      p = g_build_filename (glib_root, "share", NULL);
+      if (p)
+       g_array_append_val (data_dirs, p);
+      g_free (glib_root);
+    }
   
-  p = g_win32_get_package_installation_subdirectory (NULL, NULL, "share");
+  exe_root = g_win32_get_package_installation_directory_of_module (NULL);
+  p = g_build_filename (exe_root, "share", NULL);
   if (p)
     g_array_append_val (data_dirs, p);
+  g_free (exe_root);
 
   retval = (gchar **) g_array_free (data_dirs, FALSE);
 
-  if (address)
+  if (address_of_function)
     {
       if (hmodule != NULL)
        g_hash_table_insert (per_module_data_dirs, hmodule, retval);
       G_UNLOCK (g_utils_global);
     }
 
-  return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval;
+  return (const gchar * const *) retval;
 }
 
 #endif
@@ -2272,7 +2892,8 @@ g_win32_get_system_data_dirs_for_module (gconstpointer address)
  * On UNIX platforms this is determined using the mechanisms described in
  * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
  * XDG Base Directory Specification</ulink>
- * 
+ * In this case the list of directories retrieved will be XDG_DATA_DIRS.
+ *
  * On Windows the first elements in the list are the Application Data
  * and Documents folders for All Users. (These can be determined only
  * on Windows 2000 or later and are not present in the list on other
@@ -2299,7 +2920,7 @@ g_win32_get_system_data_dirs_for_module (gconstpointer address)
  *               not be modified or freed.
  * Since: 2.6
  **/
-G_CONST_RETURN gchar * G_CONST_RETURN * 
+const gchar * const * 
 g_get_system_data_dirs (void)
 {
   gchar **data_dir_vector;
@@ -2326,7 +2947,7 @@ g_get_system_data_dirs (void)
 
   G_UNLOCK (g_utils_global);
 
-  return (G_CONST_RETURN gchar * G_CONST_RETURN *) data_dir_vector;
+  return (const gchar * const *) data_dir_vector;
 }
 
 /**
@@ -2337,13 +2958,21 @@ g_get_system_data_dirs (void)
  *
  * On UNIX platforms this is determined using the mechanisms described in
  * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>
- * 
+ * XDG Base Directory Specification</ulink>.
+ * In this case the list of directories retrieved will be XDG_CONFIG_DIRS.
+ *
+ * On Windows is the directory that contains application data for all users.
+ * A typical path is C:\Documents and Settings\All Users\Application Data.
+ * This folder is used for application data that is not user specific.
+ * For example, an application can store a spell-check dictionary, a database
+ * of clip art, or a log file in the CSIDL_COMMON_APPDATA folder.
+ * This information will not roam and is available to anyone using the computer.
+ *
  * Return value: a %NULL-terminated array of strings owned by GLib that must 
  *               not be modified or freed.
  * Since: 2.6
  **/
-G_CONST_RETURN gchar * G_CONST_RETURN *
+const gchar * const *
 g_get_system_config_dirs (void)
 {
   gchar *conf_dirs, **conf_dir_vector;
@@ -2379,7 +3008,7 @@ g_get_system_config_dirs (void)
     conf_dir_vector = g_system_config_dirs;
   G_UNLOCK (g_utils_global);
 
-  return (G_CONST_RETURN gchar * G_CONST_RETURN *) conf_dir_vector;
+  return (const gchar * const *) conf_dir_vector;
 }
 
 #ifndef G_OS_WIN32
@@ -2539,36 +3168,39 @@ explode_locale (const gchar *locale,
  *       but it is big, ugly, and complicated, so I'm reluctant
  *       to do so when this should handle 99% of the time...
  */
-GSList *
-_g_compute_locale_variants (const gchar *locale)
+static void
+append_locale_variants (GPtrArray *array,
+                        const gchar *locale)
 {
-  GSList *retval = NULL;
-
   gchar *language = NULL;
   gchar *territory = NULL;
   gchar *codeset = NULL;
   gchar *modifier = NULL;
 
   guint mask;
-  guint i;
+  guint i, j;
 
-  g_return_val_if_fail (locale != NULL, NULL);
+  g_return_if_fail (locale != NULL);
 
   mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
 
   /* Iterate through all possible combinations, from least attractive
    * to most attractive.
    */
-  for (i = 0; i <= mask; i++)
-    if ((i & ~mask) == 0)
-      {
-       gchar *val = g_strconcat (language,
-                                 (i & COMPONENT_TERRITORY) ? territory : "",
-                                 (i & COMPONENT_CODESET) ? codeset : "",
-                                 (i & COMPONENT_MODIFIER) ? modifier : "",
-                                 NULL);
-       retval = g_slist_prepend (retval, val);
-      }
+  for (j = 0; j <= mask; ++j)
+    {
+      i = mask - j;
+
+      if ((i & ~mask) == 0)
+        {
+          gchar *val = g_strconcat (language,
+                                    (i & COMPONENT_TERRITORY) ? territory : "",
+                                    (i & COMPONENT_CODESET) ? codeset : "",
+                                    (i & COMPONENT_MODIFIER) ? modifier : "",
+                                    NULL);
+          g_ptr_array_add (array, val);
+        }
+    }
 
   g_free (language);
   if (mask & COMPONENT_CODESET)
@@ -2577,8 +3209,41 @@ _g_compute_locale_variants (const gchar *locale)
     g_free (territory);
   if (mask & COMPONENT_MODIFIER)
     g_free (modifier);
+}
 
-  return retval;
+/**
+ * g_get_locale_variants:
+ * @locale: a locale identifier
+ *
+ * Returns a list of derived variants of @locale, which can be used to
+ * e.g. construct locale-dependent filenames or search paths. The returned
+ * list is sorted from most desirable to least desirable.
+ * This function handles territory, charset and extra locale modifiers.
+ * 
+ * For example, if @locale is "fr_BE", then the returned list
+ * is "fr_BE", "fr".
+ *
+ * If you need the list of variants for the <emphasis>current locale</emphasis>,
+ * use g_get_language_names().
+ *
+ * Returns: (transfer full) (array zero-terminated="1") (element-type utf8): a newly
+ *   allocated array of newly allocated strings with the locale variants. Free with
+ *   g_strfreev().
+ *
+ * Since: 2.28
+ */
+gchar **
+g_get_locale_variants (const gchar *locale)
+{
+  GPtrArray *array;
+
+  g_return_val_if_fail (locale != NULL, NULL);
+
+  array = g_ptr_array_sized_new (8);
+  append_locale_variants (array, locale);
+  g_ptr_array_add (array, NULL);
+
+  return (gchar **) g_ptr_array_free (array, FALSE);
 }
 
 /* The following is (partly) taken from the gettext package.
@@ -2621,9 +3286,12 @@ guess_category_value (const gchar *category_name)
    * by Windows and the Microsoft C runtime (in the "English_United
    * States" format) translated into the Unixish format.
    */
-  retval = g_win32_getlocale ();
-  if ((retval != NULL) && (retval[0] != '\0'))
+  {
+    char *locale = g_win32_getlocale ();
+    retval = g_intern_string (locale);
+    g_free (locale);
     return retval;
+  }
 #endif  
 
   return NULL;
@@ -2665,7 +3333,7 @@ language_names_cache_free (gpointer data)
  *
  * Since: 2.6
  **/
-G_CONST_RETURN gchar * G_CONST_RETURN * 
+const gchar * const * 
 g_get_language_names (void)
 {
   static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
@@ -2684,34 +3352,26 @@ g_get_language_names (void)
 
   if (!(cache->languages && strcmp (cache->languages, value) == 0))
     {
-      gchar **languages;
+      GPtrArray *array;
       gchar **alist, **a;
-      GSList *list, *l;
-      gint i;
 
       g_free (cache->languages);
       g_strfreev (cache->language_names);
       cache->languages = g_strdup (value);
 
+      array = g_ptr_array_sized_new (8);
+
       alist = g_strsplit (value, ":", 0);
-      list = NULL;
       for (a = alist; *a; a++)
-       {
-         gchar *b = unalias_lang (*a);
-         list = g_slist_concat (list, _g_compute_locale_variants (b));
-       }
+        append_locale_variants (array, unalias_lang (*a));
       g_strfreev (alist);
-      list = g_slist_append (list, g_strdup ("C"));
+      g_ptr_array_add (array, g_strdup ("C"));
+      g_ptr_array_add (array, NULL);
 
-      cache->language_names = languages = g_new (gchar *, g_slist_length (list) + 1);
-      for (l = list, i = 0; l; l = l->next, i++)
-       languages[i] = l->data;
-      languages[i] = NULL;
-
-      g_slist_free (list);
+      cache->language_names = (gchar **) g_ptr_array_free (array, FALSE);
     }
 
-  return (G_CONST_RETURN gchar * G_CONST_RETURN *) cache->language_names;
+  return (const gchar * const *) cache->language_names;
 }
 
 /**
@@ -2784,6 +3444,84 @@ g_int_hash (gconstpointer v)
 }
 
 /**
+ * g_int64_equal:
+ * @v1: a pointer to a #gint64 key.
+ * @v2: a pointer to a #gint64 key to compare with @v1.
+ *
+ * Compares the two #gint64 values being pointed to and returns 
+ * %TRUE if they are equal.
+ * It can be passed to g_hash_table_new() as the @key_equal_func
+ * parameter, when using pointers to 64-bit integers as keys in a #GHashTable.
+ * 
+ * Returns: %TRUE if the two keys match.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_int64_equal (gconstpointer v1,
+               gconstpointer v2)
+{
+  return *((const gint64*) v1) == *((const gint64*) v2);
+}
+
+/**
+ * g_int64_hash:
+ * @v: a pointer to a #gint64 key
+ *
+ * Converts a pointer to a #gint64 to a hash value.
+ * It can be passed to g_hash_table_new() as the @hash_func parameter, 
+ * when using pointers to 64-bit integers values as keys in a #GHashTable.
+ *
+ * Returns: a hash value corresponding to the key.
+ *
+ * Since: 2.22
+ */
+guint
+g_int64_hash (gconstpointer v)
+{
+  return (guint) *(const gint64*) v;
+}
+
+/**
+ * g_double_equal:
+ * @v1: a pointer to a #gdouble key.
+ * @v2: a pointer to a #gdouble key to compare with @v1.
+ *
+ * Compares the two #gdouble values being pointed to and returns 
+ * %TRUE if they are equal.
+ * It can be passed to g_hash_table_new() as the @key_equal_func
+ * parameter, when using pointers to doubles as keys in a #GHashTable.
+ * 
+ * Returns: %TRUE if the two keys match.
+ *
+ * Since: 2.22
+ */
+gboolean
+g_double_equal (gconstpointer v1,
+                gconstpointer v2)
+{
+  return *((const gdouble*) v1) == *((const gdouble*) v2);
+}
+
+/**
+ * g_double_hash:
+ * @v: a pointer to a #gdouble key
+ *
+ * Converts a pointer to a #gdouble to a hash value.
+ * It can be passed to g_hash_table_new() as the @hash_func parameter, 
+ * when using pointers to doubles as keys in a #GHashTable.
+ *
+ * Returns: a hash value corresponding to the key.
+ *
+ * Since: 2.22
+ */
+guint
+g_double_hash (gconstpointer v)
+{
+  return (guint) *(const gdouble*) v;
+}
+
+/**
  * g_nullify_pointer:
  * @nullify_location: the memory address of the pointer.
  * 
@@ -2824,38 +3562,43 @@ _g_utils_thread_init (void)
   g_get_language_names ();
 }
 
-#ifdef ENABLE_NLS
-
-#include <libintl.h>
-
 #ifdef G_OS_WIN32
 
 /**
  * _glib_get_locale_dir:
  *
- * Return the path to the lib\locale subfolder of the GLib
- * installation folder. The path is in the system codepage. We have to
- * use system codepage as bindtextdomain() doesn't have a UTF-8
- * interface.
+ * Return the path to the share\locale or lib\locale subfolder of the
+ * GLib installation folder. The path is in the system codepage. We
+ * have to use system codepage as bindtextdomain() doesn't have a
+ * UTF-8 interface.
  */
-static const gchar *
+gchar *
 _glib_get_locale_dir (void)
 {
-  gchar *dir, *cp_dir;
+  gchar *install_dir = NULL, *locale_dir;
   gchar *retval = NULL;
 
-  dir = g_win32_get_package_installation_directory (GETTEXT_PACKAGE, dll_name);
-  cp_dir = g_win32_locale_filename_from_utf8 (dir);
-  g_free (dir);
+  if (glib_dll != NULL)
+    install_dir = g_win32_get_package_installation_directory_of_module (glib_dll);
 
-  if (cp_dir)
+  if (install_dir)
     {
-      /* Don't use g_build_filename() on pathnames in the system
-       * codepage. In CJK locales cp_dir might end with a double-byte
-       * character whose trailing byte is a backslash.
+      /*
+       * Append "/share/locale" or "/lib/locale" depending on whether
+       * autoconfigury detected GNU gettext or not.
        */
-      retval = g_strconcat (cp_dir, "\\lib\\locale", NULL);
-      g_free (cp_dir);
+      const char *p = GLIB_LOCALE_DIR + strlen (GLIB_LOCALE_DIR);
+      while (*--p != '/')
+       ;
+      while (*--p != '/')
+       ;
+
+      locale_dir = g_build_filename (install_dir, p, NULL);
+
+      retval = g_win32_locale_filename_from_utf8 (locale_dir);
+
+      g_free (install_dir);
+      g_free (locale_dir);
     }
 
   if (retval)
@@ -2865,30 +3608,73 @@ _glib_get_locale_dir (void)
 }
 
 #undef GLIB_LOCALE_DIR
-#define GLIB_LOCALE_DIR _glib_get_locale_dir ()
 
 #endif /* G_OS_WIN32 */
 
-G_CONST_RETURN gchar *
-_glib_gettext (const gchar *str)
+static void
+ensure_gettext_initialized(void)
 {
   static gboolean _glib_gettext_initialized = FALSE;
 
   if (!_glib_gettext_initialized)
     {
-      bindtextdomain(GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#ifdef G_OS_WIN32
+      gchar *tmp = _glib_get_locale_dir ();
+      bindtextdomain (GETTEXT_PACKAGE, tmp);
+      g_free (tmp);
+#else
+      bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#endif
 #    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 #    endif
       _glib_gettext_initialized = TRUE;
     }
-  
-  return dgettext (GETTEXT_PACKAGE, str);
 }
 
-#endif /* ENABLE_NLS */
+/**
+ * glib_gettext:
+ * @str: The string to be translated
+ *
+ * Returns the translated string from the glib translations.
+ * This is an internal function and should only be used by
+ * the internals of glib (such as libgio).
+ *
+ * Returns: the transation of @str to the current locale
+ */
+const gchar *
+glib_gettext (const gchar *str)
+{
+  ensure_gettext_initialized();
+
+  return g_dgettext (GETTEXT_PACKAGE, str);
+}
+
+/**
+ * glib_pgettext:
+ * @msgctxtid: a combined message context and message id, separated
+ *   by a \004 character
+ * @msgidoffset: the offset of the message id in @msgctxid
+ *
+ * This function is a variant of glib_gettext() which supports
+ * a disambiguating message context. See g_dpgettext() for full
+ * details.
+ *
+ * This is an internal function and should only be used by
+ * the internals of glib (such as libgio).
+ *
+ * Returns: the transation of @str to the current locale
+ */
+const gchar *
+glib_pgettext(const gchar *msgctxtid,
+              gsize        msgidoffset)
+{
+  ensure_gettext_initialized();
+
+  return g_dpgettext (GETTEXT_PACKAGE, msgctxtid, msgidoffset);
+}
 
-#ifdef G_OS_WIN32
+#if defined (G_OS_WIN32) && !defined (_WIN64)
 
 /* Binary compatibility versions. Not for newly compiled code. */
 
@@ -2923,7 +3709,7 @@ g_get_current_dir (void)
 
 #undef g_getenv
 
-G_CONST_RETURN gchar*
+const gchar *
 g_getenv (const gchar *variable)
 {
   gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
@@ -2972,7 +3758,7 @@ g_unsetenv (const gchar *variable)
 
 #undef g_get_user_name
 
-G_CONST_RETURN gchar*
+const gchar *
 g_get_user_name (void)
 {
   g_get_any_init_locked ();
@@ -2981,7 +3767,7 @@ g_get_user_name (void)
 
 #undef g_get_real_name
 
-G_CONST_RETURN gchar*
+const gchar *
 g_get_real_name (void)
 {
   g_get_any_init_locked ();
@@ -2990,7 +3776,7 @@ g_get_real_name (void)
 
 #undef g_get_home_dir
 
-G_CONST_RETURN gchar*
+const gchar *
 g_get_home_dir (void)
 {
   g_get_any_init_locked ();
@@ -2999,7 +3785,7 @@ g_get_home_dir (void)
 
 #undef g_get_tmp_dir
 
-G_CONST_RETURN gchar*
+const gchar *
 g_get_tmp_dir (void)
 {
   g_get_any_init_locked ();
@@ -3007,6 +3793,3 @@ g_get_tmp_dir (void)
 }
 
 #endif
-
-#define __G_UTILS_C__
-#include "galiasdef.c"