From 788705633ede3a82f7ca0e1014dc8e6f7a02b349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 12 May 2016 08:49:51 +0000 Subject: [PATCH] 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 file changed, 125 insertions(+), 65 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 099bba5a5..eb44f179f 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";