[glib] W32: Support XDG_* environment variables



commit 788705633ede3a82f7ca0e1014dc8e6f7a02b349
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Thu May 12 08:49:51 2016 +0000

    W32: Support XDG_* environment variables
    
    Try to get XDG_* environment variables and, if they are available, use their
    contents to initialize various directories the same way this happens on *nix.
    When these variables are not available, fall back to the W32-specific APIs for
    getting directories.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=766358

 glib/gutils.c |  190 +++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 125 insertions(+), 65 deletions(-)
---
diff --git a/glib/gutils.c b/glib/gutils.c
index 099bba5..eb44f17 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -63,6 +63,7 @@
 #include "gstrfuncs.h"
 #include "garray.h"
 #include "glibintl.h"
+#include "gstdio.h"
 
 #ifdef G_PLATFORM_WIN32
 #include "gconvert.h"
@@ -1151,10 +1152,12 @@ g_set_application_name (const gchar *application_name)
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
  * 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.
+ * On Windows it follows XDG Base Directory Specification if `XDG_DATA_HOME`
+ * is defined. If `XDG_DATA_HOME` is undefined, the folder to use for local (as
+ * opposed to roaming) application data is used instead. See the
+ * [documentation for 
`CSIDL_LOCAL_APPDATA`](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494%28v=vs.85%29.aspx#csidl_local_appdata).
+ * Note that in this case on Windows it will be the same
+ * as what g_get_user_config_dir() returns.
  *
  * Returns: (type filename): a string owned by GLib that must not be modified
  *               or freed.
@@ -1163,19 +1166,19 @@ g_set_application_name (const gchar *application_name)
 const gchar *
 g_get_user_data_dir (void)
 {
-  gchar *data_dir;  
+  gchar *data_dir = NULL;
 
   G_LOCK (g_utils_global);
 
   if (!g_user_data_dir)
     {
-#ifdef G_OS_WIN32
-      data_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
-#else
-      data_dir = (gchar *) g_getenv ("XDG_DATA_HOME");
+      const gchar *data_dir_env = g_getenv ("XDG_DATA_HOME");
 
-      if (data_dir && data_dir[0])
-        data_dir = g_strdup (data_dir);
+      if (data_dir_env && data_dir_env[0])
+        data_dir = g_strdup (data_dir_env);
+#ifdef G_OS_WIN32
+      else
+        data_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
 #endif
       if (!data_dir || !data_dir[0])
        {
@@ -1200,17 +1203,17 @@ g_get_user_data_dir (void)
 static void
 g_init_user_config_dir (void)
 {
-  gchar *config_dir;
+  gchar *config_dir = NULL;
 
   if (!g_user_config_dir)
     {
-#ifdef G_OS_WIN32
-      config_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
-#else
-      config_dir = (gchar *) g_getenv ("XDG_CONFIG_HOME");
+      const gchar *config_dir_env = g_getenv ("XDG_CONFIG_HOME");
 
-      if (config_dir && config_dir[0])
-       config_dir = g_strdup (config_dir);
+      if (config_dir_env && config_dir_env[0])
+       config_dir = g_strdup (config_dir_env);
+#ifdef G_OS_WIN32
+      else
+        config_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
 #endif
       if (!config_dir || !config_dir[0])
        {
@@ -1237,10 +1240,12 @@ g_init_user_config_dir (void)
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
  * 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.
+ * On Windows it follows XDG Base Directory Specification if `XDG_CONFIG_HOME` is defined.
+ * If `XDG_CONFIG_HOME` is undefined, the folder to use for local (as opposed
+ * to roaming) application data is used instead. See the
+ * [documentation for 
`CSIDL_LOCAL_APPDATA`](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494%28v=vs.85%29.aspx#csidl_local_appdata).
+ * Note that in this case on Windows it will be  the same
+ * as what g_get_user_data_dir() returns.
  *
  * Returns: (type filename): a string owned by GLib that must not be modified
  *               or freed.
@@ -1267,12 +1272,13 @@ g_get_user_config_dir (void)
  * On UNIX platforms this is determined using the mechanisms described
  * in the
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
- * In this case the directory retrieved will be XDG_CACHE_HOME.
+ * 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.
+ * On Windows it follows XDG Base Directory Specification if `XDG_CACHE_HOME` is defined.
+ * If `XDG_CACHE_HOME` is undefined, the directory that serves as a common
+ * repository for temporary Internet files is used instead. A typical path is
+ * `C:\Documents and Settings\username\Local Settings\Temporary Internet Files`.
+ * See the [documentation for 
`CSIDL_INTERNET_CACHE`](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494%28v=vs.85%29.aspx#csidl_internet_cache).
  *
  * Returns: (type filename): a string owned by GLib that must not be modified
  *               or freed.
@@ -1281,19 +1287,19 @@ g_get_user_config_dir (void)
 const gchar *
 g_get_user_cache_dir (void)
 {
-  gchar *cache_dir;  
+  gchar *cache_dir = NULL;
 
   G_LOCK (g_utils_global);
 
   if (!g_user_cache_dir)
     {
-#ifdef G_OS_WIN32
-      cache_dir = get_special_folder (CSIDL_INTERNET_CACHE); /* XXX correct? */
-#else
-      cache_dir = (gchar *) g_getenv ("XDG_CACHE_HOME");
+      const gchar *cache_dir_env = g_getenv ("XDG_CACHE_HOME");
 
-      if (cache_dir && cache_dir[0])
-          cache_dir = g_strdup (cache_dir);
+      if (cache_dir_env && cache_dir_env[0])
+        cache_dir = g_strdup (cache_dir_env);
+#ifdef G_OS_WIN32
+      else
+        cache_dir = get_special_folder (CSIDL_INTERNET_CACHE);
 #endif
       if (!cache_dir || !cache_dir[0])
        {
@@ -1320,7 +1326,7 @@ g_get_user_cache_dir (void)
  * Returns a directory that is unique to the current user on the local
  * system.
  *
- * On UNIX platforms this is determined using the mechanisms described
+ * This is determined using the mechanisms described
  * in the 
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
  * This is the directory
@@ -1328,11 +1334,6 @@ g_get_user_cache_dir (void)
  * In the case that this variable is not set, we return the value of
  * g_get_user_cache_dir(), after verifying that it exists.
  *
- * 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: (type filename): a string owned by GLib that must not be
  *     modified or freed.
  *
@@ -1341,7 +1342,6 @@ g_get_user_cache_dir (void)
 const gchar *
 g_get_user_runtime_dir (void)
 {
-#ifndef G_OS_WIN32
   static const gchar *runtime_dir;
 
   if (g_once_init_enter (&runtime_dir))
@@ -1365,7 +1365,7 @@ g_get_user_runtime_dir (void)
            * exists this will work.  If the user changed $XDG_CACHE_HOME
            * then they can make sure that it exists...
            */
-          (void) mkdir (dir, 0700);
+          (void) g_mkdir (dir, 0700);
         }
 
       g_assert (dir != NULL);
@@ -1374,9 +1374,6 @@ g_get_user_runtime_dir (void)
     }
 
   return runtime_dir;
-#else /* Windows */
-  return g_get_user_cache_dir ();
-#endif
 }
 
 #ifdef HAVE_CARBON
@@ -1816,8 +1813,8 @@ get_module_share_dir (gconstpointer address)
   return retval;
 }
 
-const gchar * const *
-g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void))
+static const gchar * const *
+g_win32_get_system_data_dirs_for_module_real (void (*address_of_function)(void))
 {
   GArray *data_dirs;
   HMODULE hmodule;
@@ -1913,6 +1910,54 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void))
   return (const gchar * const *) retval;
 }
 
+const gchar * const *
+g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void))
+{
+  gboolean should_call_g_get_system_data_dirs;
+
+  should_call_g_get_system_data_dirs = TRUE;
+  /* These checks are the same as the ones that g_get_system_data_dirs() does.
+   * Please keep them in sync.
+   */
+  G_LOCK (g_utils_global);
+
+  if (!g_system_data_dirs)
+    {
+      const gchar *data_dirs = g_getenv ("XDG_DATA_DIRS");
+
+      if (!data_dirs || !data_dirs[0])
+        should_call_g_get_system_data_dirs = FALSE;
+    }
+
+  G_UNLOCK (g_utils_global);
+
+  /* There is a subtle difference between g_win32_get_system_data_dirs_for_module (NULL),
+   * which is what GLib code can normally call,
+   * and g_win32_get_system_data_dirs_for_module (&_g_win32_get_system_data_dirs),
+   * which is what the inline function used by non-GLib code calls.
+   * The former gets prefix relative to currently-running executable,
+   * the latter - relative to the module that calls _g_win32_get_system_data_dirs()
+   * (disguised as g_get_system_data_dirs()), which could be an executable or
+   * a DLL that is located somewhere else.
+   * This is why that inline function in gutils.h exists, and why we can't just
+   * call g_get_system_data_dirs() from there - because we need to get the address
+   * local to the non-GLib caller-module.
+   */
+
+  /*
+   * g_get_system_data_dirs() will fall back to calling
+   * g_win32_get_system_data_dirs_for_module_real(NULL) if XDG_DATA_DIRS is NULL
+   * or an empty string. The checks above ensure that we do not call it in such
+   * cases and use the address_of_function that we've been given by the inline function.
+   * The reason we're calling g_get_system_data_dirs /at all/ is to give
+   * XDG_DATA_DIRS precedence (if it is set).
+   */
+  if (should_call_g_get_system_data_dirs)
+    return g_get_system_data_dirs ();
+
+  return g_win32_get_system_data_dirs_for_module_real (address_of_function);
+}
+
 #endif
 
 /**
@@ -1924,9 +1969,11 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void))
  * On UNIX platforms this is determined using the mechanisms described
  * in the
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec)
- * In this case the list of directories retrieved will be XDG_DATA_DIRS.
+ * 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
+ * On Windows it follows XDG Base Directory Specification if `XDG_DATA_DIRS` is defined.
+ * If `XDG_DATA_DIRS` is undefined,
+ * 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
  * Windows versions.) See documentation for CSIDL_COMMON_APPDATA and
@@ -1959,19 +2006,25 @@ g_get_system_data_dirs (void)
 {
   gchar **data_dir_vector;
 
+  /* These checks are the same as the ones that g_win32_get_system_data_dirs_for_module()
+   * does. Please keep them in sync.
+   */
   G_LOCK (g_utils_global);
 
   if (!g_system_data_dirs)
     {
-#ifdef G_OS_WIN32
-      data_dir_vector = (gchar **) g_win32_get_system_data_dirs_for_module (NULL);
-#else
       gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS");
 
+#ifndef G_OS_WIN32
       if (!data_dirs || !data_dirs[0])
           data_dirs = "/usr/local/share/:/usr/share/";
 
       data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
+#else
+      if (!data_dirs || !data_dirs[0])
+        data_dir_vector = g_strdupv ((gchar **) g_win32_get_system_data_dirs_for_module_real (NULL));
+      else
+        data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
 #endif
 
       g_system_data_dirs = data_dir_vector;
@@ -1995,12 +2048,15 @@ g_get_system_data_dirs (void)
  * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
  * 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.
+ * On Windows it follows XDG Base Directory Specification if `XDG_CONFIG_DIRS` is defined.
+ * If `XDG_CONFIG_DIRS` is undefined, the directory that contains application
+ * data for all users is used instead. 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.
  *
  * Returns: (array zero-terminated=1) (element-type filename) (transfer none):
  *     a %NULL-terminated array of strings owned by GLib that must not be
@@ -2011,27 +2067,31 @@ g_get_system_data_dirs (void)
 const gchar * const *
 g_get_system_config_dirs (void)
 {
-  gchar *conf_dirs, **conf_dir_vector;
+  gchar **conf_dir_vector;
 
   G_LOCK (g_utils_global);
 
   if (!g_system_config_dirs)
     {
+      const gchar *conf_dirs = g_getenv ("XDG_CONFIG_DIRS");
 #ifdef G_OS_WIN32
-      conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA);
       if (conf_dirs)
        {
          conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
-         g_free (conf_dirs);
        }
       else
        {
-         /* Return empty list */
-         conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0);
+         gchar *special_conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA);
+
+         if (special_conf_dirs)
+           conf_dir_vector = g_strsplit (special_conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
+         else
+           /* Return empty list */
+           conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0);
+
+         g_free (special_conf_dirs);
        }
 #else
-      conf_dirs = (gchar *) g_getenv ("XDG_CONFIG_DIRS");
-
       if (!conf_dirs || !conf_dirs[0])
           conf_dirs = "/etc/xdg";
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]