From 68eab1d99981a8cc6865239500fe4a84878d880c Mon Sep 17 00:00:00 2001 From: Sophie Herold Date: Fri, 24 Dec 2021 20:11:39 +0000 Subject: [PATCH] utils: Add XDG_STATE_HOME support --- docs/reference/glib/glib-sections.txt | 1 + glib/gtestutils.c | 5 +- glib/gtestutils.h | 1 + glib/gutils.c | 67 +++++++++++++++++++++++++++ glib/gutils.h | 2 + glib/tests/utils.c | 9 ++++ tests/testglib.c | 4 +- 7 files changed, 87 insertions(+), 2 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index c563a0bcd..c8681fa2f 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2209,6 +2209,7 @@ g_get_real_name g_get_user_cache_dir g_get_user_data_dir g_get_user_config_dir +g_get_user_state_dir g_get_user_runtime_dir GUserDirectory g_get_user_special_dir diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 6273ab7e0..a061da799 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -1434,7 +1434,7 @@ test_do_isolate_dirs (GError **error) { gchar *subdir = NULL; gchar *home_dir = NULL, *cache_dir = NULL, *config_dir = NULL; - gchar *data_dir = NULL, *runtime_dir = NULL; + gchar *state_dir = NULL, *data_dir = NULL, *runtime_dir = NULL; gchar *config_dirs[3]; gchar *data_dirs[3]; @@ -1471,6 +1471,7 @@ test_do_isolate_dirs (GError **error) cache_dir = g_build_filename (subdir, "cache", NULL); config_dir = g_build_filename (subdir, "config", NULL); data_dir = g_build_filename (subdir, "data", NULL); + state_dir = g_build_filename (subdir, "state", NULL); config_dirs[0] = g_build_filename (subdir, "system-config1", NULL); config_dirs[1] = g_build_filename (subdir, "system-config2", NULL); @@ -1488,10 +1489,12 @@ test_do_isolate_dirs (GError **error) "XDG_CONFIG_HOME", config_dir, "XDG_DATA_DIRS", data_dirs, "XDG_DATA_HOME", data_dir, + "XDG_STATE_HOME", state_dir, "XDG_RUNTIME_DIR", runtime_dir, NULL); g_free (runtime_dir); + g_free (state_dir); g_free (data_dir); g_free (config_dir); g_free (cache_dir); diff --git a/glib/gtestutils.h b/glib/gtestutils.h index f5202ac44..7dee4822d 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -275,6 +275,7 @@ void g_test_init (int *argc, * - g_get_user_config_dir() * - g_get_system_data_dirs() * - g_get_user_data_dir() + * - g_get_user_state_dir() * - g_get_user_runtime_dir() * * The subdirectories may not be created by the test harness; as with normal diff --git a/glib/gutils.c b/glib/gutils.c index 87f10f69f..6652d0ba0 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -549,6 +549,7 @@ static gchar *g_user_data_dir = NULL; static gchar **g_system_data_dirs = NULL; static gchar *g_user_cache_dir = NULL; static gchar *g_user_config_dir = NULL; +static gchar *g_user_state_dir = NULL; static gchar *g_user_runtime_dir = NULL; static gchar **g_system_config_dirs = NULL; static gchar **g_user_special_dirs = NULL; @@ -1770,6 +1771,8 @@ g_set_user_dirs (const gchar *first_dir_type, set_strv_if_different (&g_system_data_dirs, dir_type, dir_value); else if (g_str_equal (dir_type, "XDG_DATA_HOME")) set_str_if_different (&g_user_data_dir, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_STATE_HOME")) + set_str_if_different (&g_user_state_dir, dir_type, dir_value); else if (g_str_equal (dir_type, "XDG_RUNTIME_DIR")) set_str_if_different (&g_user_runtime_dir, dir_type, dir_value); else @@ -1970,6 +1973,70 @@ g_get_user_cache_dir (void) return user_cache_dir; } +static gchar * +g_build_user_state_dir (void) +{ + gchar *state_dir = NULL; + const gchar *state_dir_env = g_getenv ("XDG_STATE_HOME"); + + if (state_dir_env && state_dir_env[0]) + state_dir = g_strdup (state_dir_env); +#ifdef G_OS_WIN32 + else + state_dir = get_special_folder (&FOLDERID_LocalAppData); +#endif + if (!state_dir || !state_dir[0]) + { + gchar *home_dir = g_build_home_dir (); + state_dir = g_build_filename (home_dir, ".local/state", NULL); + g_free (home_dir); + } + + return g_steal_pointer (&state_dir); +} + +/** + * g_get_user_state_dir: + * + * Returns a base directory in which to store state files specific to + * particular user. + * + * 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_STATE_HOME`. + * + * On Windows it follows XDG Base Directory Specification if `XDG_STATE_HOME` is defined. + * If `XDG_STATE_HOME` is undefined, the folder to use for local (as opposed + * to roaming) application data is used instead. See the + * [documentation for `FOLDERID_LocalAppData`](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid). + * Note that in this case on Windows it will be the same + * as what g_get_user_data_dir() returns. + * + * The return value is cached and modifying it at runtime is not supported, as + * it’s not thread-safe to modify environment variables at runtime. + * + * Returns: (type filename) (transfer none): a string owned by GLib that + * must not be modified or freed. + * + * Since: 2.72 + **/ +const gchar * +g_get_user_state_dir (void) +{ + const gchar *user_state_dir; + + G_LOCK (g_utils_global); + + if (g_user_state_dir == NULL) + g_user_state_dir = g_build_user_state_dir (); + user_state_dir = g_user_state_dir; + + G_UNLOCK (g_utils_global); + + return user_state_dir; +} + static gchar * g_build_user_runtime_dir (void) { diff --git a/glib/gutils.h b/glib/gutils.h index f8a6049ff..08c187040 100644 --- a/glib/gutils.h +++ b/glib/gutils.h @@ -197,6 +197,8 @@ GLIB_AVAILABLE_IN_ALL const gchar * g_get_user_config_dir (void); GLIB_AVAILABLE_IN_ALL const gchar * g_get_user_cache_dir (void); +GLIB_AVAILABLE_IN_2_72 +const gchar * g_get_user_state_dir (void); GLIB_AVAILABLE_IN_ALL const gchar * const * g_get_system_data_dirs (void); diff --git a/glib/tests/utils.c b/glib/tests/utils.c index 6068cd9be..643978149 100644 --- a/glib/tests/utils.c +++ b/glib/tests/utils.c @@ -688,6 +688,15 @@ test_xdg_dirs (void) g_assert_cmpstr (dir, ==, xdg); g_free (xdg); + xdg = g_strdup (g_getenv ("XDG_STATE_HOME")); + if (!xdg) + xdg = g_build_filename (g_get_home_dir (), ".local/state", NULL); + + dir = g_get_user_state_dir (); + + g_assert_cmpstr (dir, ==, xdg); + g_free (xdg); + xdg = g_strdup (g_getenv ("XDG_RUNTIME_DIR")); if (!xdg) xdg = g_strdup (g_get_user_cache_dir ()); diff --git a/tests/testglib.c b/tests/testglib.c index 48cd74a2a..b692c4c31 100644 --- a/tests/testglib.c +++ b/tests/testglib.c @@ -877,7 +877,7 @@ static void test_info (void) { const gchar *un, *rn, *hn; - const gchar *tmpdir, *homedir, *userdatadir, *uconfdir, *ucachedir; + const gchar *tmpdir, *homedir, *userdatadir, *uconfdir, *ucachedir, *ustatedir; const gchar *uddesktop, *udddocs, *uddpubshare, *uruntimedir; gchar **sv, *cwd, *sdatadirs, *sconfdirs, *langnames; const gchar *charset; @@ -917,6 +917,8 @@ test_info (void) g_assert (uconfdir != NULL); ucachedir = g_get_user_cache_dir (); g_assert (ucachedir != NULL); + ustatedir = g_get_user_state_dir (); + g_assert (ustatedir != NULL); uddesktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); g_assert (uddesktop != NULL);