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
This commit is contained in:
Руслан Ижбулатов 2016-05-12 08:49:51 +00:00 committed by Philip Withnall
parent f9fe9ea417
commit 788705633e

View File

@ -63,6 +63,7 @@
#include "gstrfuncs.h" #include "gstrfuncs.h"
#include "garray.h" #include "garray.h"
#include "glibintl.h" #include "glibintl.h"
#include "gstdio.h"
#ifdef G_PLATFORM_WIN32 #ifdef G_PLATFORM_WIN32
#include "gconvert.h" #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). * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
* In this case the directory retrieved will be `XDG_DATA_HOME`. * In this case the directory retrieved will be `XDG_DATA_HOME`.
* *
* On Windows this is the folder to use for local (as opposed to * On Windows it follows XDG Base Directory Specification if `XDG_DATA_HOME`
* roaming) application data. See documentation for * is defined. If `XDG_DATA_HOME` is undefined, the folder to use for local (as
* CSIDL_LOCAL_APPDATA. Note that on Windows it thus is the same as * opposed to roaming) application data is used instead. See the
* what g_get_user_config_dir() returns. * [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 * Returns: (type filename): a string owned by GLib that must not be modified
* or freed. * or freed.
@ -1163,19 +1166,19 @@ g_set_application_name (const gchar *application_name)
const gchar * const gchar *
g_get_user_data_dir (void) g_get_user_data_dir (void)
{ {
gchar *data_dir; gchar *data_dir = NULL;
G_LOCK (g_utils_global); G_LOCK (g_utils_global);
if (!g_user_data_dir) if (!g_user_data_dir)
{ {
#ifdef G_OS_WIN32 const gchar *data_dir_env = g_getenv ("XDG_DATA_HOME");
data_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
#else
data_dir = (gchar *) g_getenv ("XDG_DATA_HOME");
if (data_dir && data_dir[0]) if (data_dir_env && data_dir_env[0])
data_dir = g_strdup (data_dir); data_dir = g_strdup (data_dir_env);
#ifdef G_OS_WIN32
else
data_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
#endif #endif
if (!data_dir || !data_dir[0]) if (!data_dir || !data_dir[0])
{ {
@ -1200,17 +1203,17 @@ g_get_user_data_dir (void)
static void static void
g_init_user_config_dir (void) g_init_user_config_dir (void)
{ {
gchar *config_dir; gchar *config_dir = NULL;
if (!g_user_config_dir) if (!g_user_config_dir)
{ {
#ifdef G_OS_WIN32 const gchar *config_dir_env = g_getenv ("XDG_CONFIG_HOME");
config_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
#else
config_dir = (gchar *) g_getenv ("XDG_CONFIG_HOME");
if (config_dir && config_dir[0]) if (config_dir_env && config_dir_env[0])
config_dir = g_strdup (config_dir); config_dir = g_strdup (config_dir_env);
#ifdef G_OS_WIN32
else
config_dir = get_special_folder (CSIDL_LOCAL_APPDATA);
#endif #endif
if (!config_dir || !config_dir[0]) 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). * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
* In this case the directory retrieved will be `XDG_CONFIG_HOME`. * In this case the directory retrieved will be `XDG_CONFIG_HOME`.
* *
* On Windows this is the folder to use for local (as opposed to * On Windows it follows XDG Base Directory Specification if `XDG_CONFIG_HOME` is defined.
* roaming) application data. See documentation for * If `XDG_CONFIG_HOME` is undefined, the folder to use for local (as opposed
* CSIDL_LOCAL_APPDATA. Note that on Windows it thus is the same as * to roaming) application data is used instead. See the
* what g_get_user_data_dir() returns. * [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 * Returns: (type filename): a string owned by GLib that must not be modified
* or freed. * or freed.
@ -1267,12 +1272,13 @@ g_get_user_config_dir (void)
* On UNIX platforms this is determined using the mechanisms described * On UNIX platforms this is determined using the mechanisms described
* in the * in the
* [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec). * [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 * On Windows it follows XDG Base Directory Specification if `XDG_CACHE_HOME` is defined.
* temporary Internet files. A typical path is * If `XDG_CACHE_HOME` is undefined, the directory that serves as a common
* C:\Documents and Settings\username\Local Settings\Temporary Internet Files. * repository for temporary Internet files is used instead. A typical path is
* See documentation for CSIDL_INTERNET_CACHE. * `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 * Returns: (type filename): a string owned by GLib that must not be modified
* or freed. * or freed.
@ -1281,19 +1287,19 @@ g_get_user_config_dir (void)
const gchar * const gchar *
g_get_user_cache_dir (void) g_get_user_cache_dir (void)
{ {
gchar *cache_dir; gchar *cache_dir = NULL;
G_LOCK (g_utils_global); G_LOCK (g_utils_global);
if (!g_user_cache_dir) if (!g_user_cache_dir)
{ {
#ifdef G_OS_WIN32 const gchar *cache_dir_env = g_getenv ("XDG_CACHE_HOME");
cache_dir = get_special_folder (CSIDL_INTERNET_CACHE); /* XXX correct? */
#else
cache_dir = (gchar *) g_getenv ("XDG_CACHE_HOME");
if (cache_dir && cache_dir[0]) if (cache_dir_env && cache_dir_env[0])
cache_dir = g_strdup (cache_dir); cache_dir = g_strdup (cache_dir_env);
#ifdef G_OS_WIN32
else
cache_dir = get_special_folder (CSIDL_INTERNET_CACHE);
#endif #endif
if (!cache_dir || !cache_dir[0]) 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 * Returns a directory that is unique to the current user on the local
* system. * system.
* *
* On UNIX platforms this is determined using the mechanisms described * This is determined using the mechanisms described
* in the * in the
* [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec). * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
* This is the directory * 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 * In the case that this variable is not set, we return the value of
* g_get_user_cache_dir(), after verifying that it exists. * 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 * Returns: (type filename): a string owned by GLib that must not be
* modified or freed. * modified or freed.
* *
@ -1341,7 +1342,6 @@ g_get_user_cache_dir (void)
const gchar * const gchar *
g_get_user_runtime_dir (void) g_get_user_runtime_dir (void)
{ {
#ifndef G_OS_WIN32
static const gchar *runtime_dir; static const gchar *runtime_dir;
if (g_once_init_enter (&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 * exists this will work. If the user changed $XDG_CACHE_HOME
* then they can make sure that it exists... * then they can make sure that it exists...
*/ */
(void) mkdir (dir, 0700); (void) g_mkdir (dir, 0700);
} }
g_assert (dir != NULL); g_assert (dir != NULL);
@ -1374,9 +1374,6 @@ g_get_user_runtime_dir (void)
} }
return runtime_dir; return runtime_dir;
#else /* Windows */
return g_get_user_cache_dir ();
#endif
} }
#ifdef HAVE_CARBON #ifdef HAVE_CARBON
@ -1816,8 +1813,8 @@ get_module_share_dir (gconstpointer address)
return retval; return retval;
} }
const gchar * const * static const gchar * const *
g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void)) g_win32_get_system_data_dirs_for_module_real (void (*address_of_function)(void))
{ {
GArray *data_dirs; GArray *data_dirs;
HMODULE hmodule; HMODULE hmodule;
@ -1913,6 +1910,54 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void))
return (const gchar * const *) retval; 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 #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 * On UNIX platforms this is determined using the mechanisms described
* in the * in the
* [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec) * [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 * 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 * on Windows 2000 or later and are not present in the list on other
* Windows versions.) See documentation for CSIDL_COMMON_APPDATA and * Windows versions.) See documentation for CSIDL_COMMON_APPDATA and
@ -1959,19 +2006,25 @@ g_get_system_data_dirs (void)
{ {
gchar **data_dir_vector; 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); G_LOCK (g_utils_global);
if (!g_system_data_dirs) 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"); gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS");
#ifndef G_OS_WIN32
if (!data_dirs || !data_dirs[0]) if (!data_dirs || !data_dirs[0])
data_dirs = "/usr/local/share/:/usr/share/"; data_dirs = "/usr/local/share/:/usr/share/";
data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); 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 #endif
g_system_data_dirs = data_dir_vector; 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). * [XDG Base Directory Specification](http://www.freedesktop.org/Standards/basedir-spec).
* In this case the list of directories retrieved will be `XDG_CONFIG_DIRS`. * 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. * On Windows it follows XDG Base Directory Specification if `XDG_CONFIG_DIRS` is defined.
* A typical path is C:\Documents and Settings\All Users\Application Data. * If `XDG_CONFIG_DIRS` is undefined, the directory that contains application
* This folder is used for application data that is not user specific. * data for all users is used instead. A typical path is
* For example, an application can store a spell-check dictionary, a database * `C:\Documents and Settings\All Users\Application Data`.
* of clip art, or a log file in the CSIDL_COMMON_APPDATA folder. * This folder is used for application data
* This information will not roam and is available to anyone using the computer. * 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): * Returns: (array zero-terminated=1) (element-type filename) (transfer none):
* a %NULL-terminated array of strings owned by GLib that must not be * 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 * const gchar * const *
g_get_system_config_dirs (void) g_get_system_config_dirs (void)
{ {
gchar *conf_dirs, **conf_dir_vector; gchar **conf_dir_vector;
G_LOCK (g_utils_global); G_LOCK (g_utils_global);
if (!g_system_config_dirs) if (!g_system_config_dirs)
{ {
const gchar *conf_dirs = g_getenv ("XDG_CONFIG_DIRS");
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA);
if (conf_dirs) if (conf_dirs)
{ {
conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
g_free (conf_dirs);
} }
else else
{ {
/* Return empty list */ gchar *special_conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA);
conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0);
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 #else
conf_dirs = (gchar *) g_getenv ("XDG_CONFIG_DIRS");
if (!conf_dirs || !conf_dirs[0]) if (!conf_dirs || !conf_dirs[0])
conf_dirs = "/etc/xdg"; conf_dirs = "/etc/xdg";