Improve g_get_system_data_dirs() on Windows. A library that calls

2005-04-08  Tor Lillqvist  <tml@novell.com>

	Improve g_get_system_data_dirs() on Windows. A library that calls
	g_get_system_data_dirs() might be installed in a different
	top-level prefix than GLib or the application being run.

	* glib/gutils.h (g_win32_get_system_data_dirs): New static
	function defined in this header. Calls
	g_win32_get_system_data_dirs_for_module() passing the address of
	itself as parameter. g_get_system_data_dirs() is #defined as this
	function.

	* glib/gutils.c (g_win32_get_system_data_dirs_for_module): New
	function. If the address parameter is non-NULL, the corresponding
	module's installation location is used for one of the returned
	path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS,
	glib top-level and application top-level folders.
	(g_get_system_data_dirs): Now just for backward compatibility on
	Win32. Just call g_win32_get_system_data_dirs_for_module(NULL).

	* glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module.
This commit is contained in:
Tor Lillqvist 2005-04-08 12:03:16 +00:00 committed by Tor Lillqvist
parent 30942cba28
commit 5a88294fc3
7 changed files with 291 additions and 65 deletions

View File

@ -1,5 +1,25 @@
2005-04-08 Tor Lillqvist <tml@novell.com>
Improve g_get_system_data_dirs() on Windows. A library that calls
g_get_system_data_dirs() might be installed in a different
top-level prefix than GLib or the application being run.
* glib/gutils.h (g_win32_get_system_data_dirs): New static
function defined in this header. Calls
g_win32_get_system_data_dirs_for_module() passing the address of
itself as parameter. g_get_system_data_dirs() is #defined as this
function.
* glib/gutils.c (g_win32_get_system_data_dirs_for_module): New
function. If the address parameter is non-NULL, the corresponding
module's installation location is used for one of the returned
path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS,
glib top-level and application top-level folders.
(g_get_system_data_dirs): Now just for backward compatibility on
Win32. Just call g_win32_get_system_data_dirs_for_module(NULL).
* glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module.
* glib/gutils.c (read_aliases): Not used on Windows.
(unalias_lang): Don't do anything on Windows, there is no
/usr/share/locale/locale.alias file..

View File

@ -1,5 +1,25 @@
2005-04-08 Tor Lillqvist <tml@novell.com>
Improve g_get_system_data_dirs() on Windows. A library that calls
g_get_system_data_dirs() might be installed in a different
top-level prefix than GLib or the application being run.
* glib/gutils.h (g_win32_get_system_data_dirs): New static
function defined in this header. Calls
g_win32_get_system_data_dirs_for_module() passing the address of
itself as parameter. g_get_system_data_dirs() is #defined as this
function.
* glib/gutils.c (g_win32_get_system_data_dirs_for_module): New
function. If the address parameter is non-NULL, the corresponding
module's installation location is used for one of the returned
path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS,
glib top-level and application top-level folders.
(g_get_system_data_dirs): Now just for backward compatibility on
Win32. Just call g_win32_get_system_data_dirs_for_module(NULL).
* glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module.
* glib/gutils.c (read_aliases): Not used on Windows.
(unalias_lang): Don't do anything on Windows, there is no
/usr/share/locale/locale.alias file..

View File

@ -1,5 +1,25 @@
2005-04-08 Tor Lillqvist <tml@novell.com>
Improve g_get_system_data_dirs() on Windows. A library that calls
g_get_system_data_dirs() might be installed in a different
top-level prefix than GLib or the application being run.
* glib/gutils.h (g_win32_get_system_data_dirs): New static
function defined in this header. Calls
g_win32_get_system_data_dirs_for_module() passing the address of
itself as parameter. g_get_system_data_dirs() is #defined as this
function.
* glib/gutils.c (g_win32_get_system_data_dirs_for_module): New
function. If the address parameter is non-NULL, the corresponding
module's installation location is used for one of the returned
path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS,
glib top-level and application top-level folders.
(g_get_system_data_dirs): Now just for backward compatibility on
Win32. Just call g_win32_get_system_data_dirs_for_module(NULL).
* glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module.
* glib/gutils.c (read_aliases): Not used on Windows.
(unalias_lang): Don't do anything on Windows, there is no
/usr/share/locale/locale.alias file..

View File

@ -1,5 +1,25 @@
2005-04-08 Tor Lillqvist <tml@novell.com>
Improve g_get_system_data_dirs() on Windows. A library that calls
g_get_system_data_dirs() might be installed in a different
top-level prefix than GLib or the application being run.
* glib/gutils.h (g_win32_get_system_data_dirs): New static
function defined in this header. Calls
g_win32_get_system_data_dirs_for_module() passing the address of
itself as parameter. g_get_system_data_dirs() is #defined as this
function.
* glib/gutils.c (g_win32_get_system_data_dirs_for_module): New
function. If the address parameter is non-NULL, the corresponding
module's installation location is used for one of the returned
path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS,
glib top-level and application top-level folders.
(g_get_system_data_dirs): Now just for backward compatibility on
Win32. Just call g_win32_get_system_data_dirs_for_module(NULL).
* glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module.
* glib/gutils.c (read_aliases): Not used on Windows.
(unalias_lang): Don't do anything on Windows, there is no
/usr/share/locale/locale.alias file..

View File

@ -1174,6 +1174,9 @@ g_get_real_name_utf8
#endif
g_get_system_config_dirs
g_get_system_data_dirs
#ifdef G_OS_WIN32
g_win32_get_system_data_dirs_for_module
#endif
g_get_tmp_dir PRIVATE
#ifdef G_OS_WIN32
g_get_tmp_dir_utf8

View File

@ -70,6 +70,10 @@
# define STRICT /* Strict typing, please */
# include <windows.h>
# undef STRICT
# ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
# define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
# define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
# endif
# include <lmcons.h> /* For UNLEN */
#endif /* G_PLATFORM_WIN32 */
@ -2043,6 +2047,174 @@ g_get_user_cache_dir (void)
return cache_dir;
}
#ifdef G_OS_WIN32
#undef g_get_system_data_dirs
static HMODULE
get_module_for_address (gconstpointer address)
{
/* Holds the g_utils_global lock */
static gboolean beenhere = FALSE;
typedef BOOL (WINAPI *t_GetModuleHandleExA) (DWORD, LPCTSTR, HMODULE *);
static t_GetModuleHandleExA p_GetModuleHandleExA = NULL;
HMODULE hmodule;
if (!address)
return NULL;
if (!beenhere)
{
p_GetModuleHandleExA =
(t_GetModuleHandleExA) GetProcAddress (LoadLibrary ("kernel32.dll"),
"GetModuleHandleExA");
beenhere = TRUE;
}
if (p_GetModuleHandleExA == NULL ||
!(*p_GetModuleHandleExA) (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT |
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
address, &hmodule))
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery (address, &mbi, sizeof (mbi));
hmodule = (HMODULE) mbi.AllocationBase;
}
return hmodule;
}
static gchar *
get_module_share_dir (gconstpointer address)
{
HMODULE hmodule;
gchar *filename = NULL;
gchar *p, *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';
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)
{
GArray *data_dirs;
HMODULE hmodule;
static GHashTable *per_module_data_dirs = NULL;
gchar **retval;
gchar *p;
if (address)
{
G_LOCK (g_utils_global);
hmodule = get_module_for_address (address);
if (hmodule != NULL)
{
if (per_module_data_dirs == NULL)
per_module_data_dirs = g_hash_table_new (NULL, NULL);
else
{
retval = g_hash_table_lookup (per_module_data_dirs, hmodule);
if (retval != NULL)
{
G_UNLOCK (g_utils_global);
return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval;
}
}
}
}
data_dirs = g_array_new (TRUE, TRUE, sizeof (char *));
/* Documents and Settings\All Users\Application Data */
p = get_special_folder (CSIDL_COMMON_APPDATA);
if (p)
g_array_append_val (data_dirs, p);
/* Documents and Settings\All Users\Documents */
p = get_special_folder (CSIDL_COMMON_DOCUMENTS);
if (p)
g_array_append_val (data_dirs, p);
/* Using the above subfolders of Documents and Settings perhaps
* makes sense from a Windows perspective.
*
* But looking at the actual use cases of this function in GTK+
* and GNOME software, what we really want is the "share"
* 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.
*
* Additionally, also use the "share" subfolder of the installation
* locations of GLib and the .exe file being run.
*
* To guard against none of the above being what is really wanted,
* callers of this function should have Win32-specific code to look
* up their installation folder themselves, and handle a subfolder
* "share" of it in the same way as the folders returned from this
* function.
*/
p = get_module_share_dir (address);
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);
p = g_win32_get_package_installation_subdirectory (NULL, NULL, "share");
if (p)
g_array_append_val (data_dirs, p);
retval = (gchar **) g_array_free (data_dirs, FALSE);
if (address)
{
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;
}
#endif
/**
* g_get_system_data_dirs:
*
@ -2053,6 +2225,28 @@ g_get_user_cache_dir (void)
* the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
* XDG Base Directory Specification</ulink>
*
* 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
* Windows versions.) See documentation for CSIDL_COMMON_APPDATA and
* CSIDL_COMMON_DOCUMENTS.
*
* Then follows the "share" subfolder in the installation folder for
* the package containing the DLL that calls this function, if it can
* be determined.
*
* Finally the list contains the "share" subfolder in the installation
* folder for GLib, and in the installation folder for the package the
* application's .exe file belongs to.
*
* The installation folders above are determined by looking up the
* folder where the module (DLL or EXE) in question is located. If the
* folder's name is "bin", its parent is used, otherwise the folder
* itself.
*
* Note that on Windows the returned list can vary depending on where
* this function is called.
*
* Return value: a %NULL-terminated array of strings owned by GLib that must
* not be modified or freed.
* Since: 2.6
@ -2060,78 +2254,16 @@ g_get_user_cache_dir (void)
G_CONST_RETURN gchar * G_CONST_RETURN *
g_get_system_data_dirs (void)
{
gchar *data_dirs, **data_dir_vector;
gchar **data_dir_vector;
G_LOCK (g_utils_global);
if (!g_system_data_dirs)
{
#ifdef G_OS_WIN32
gchar *glib_top_share_dir, *exe_top_share_dir;
/* Documents and Settings\All Users\Application Data */
char *appdata = get_special_folder (CSIDL_COMMON_APPDATA);
/* Documents and Settings\All Users\Documents */
char *docs = get_special_folder (CSIDL_COMMON_DOCUMENTS);
if (appdata && docs)
{
data_dirs = g_strconcat (appdata,
G_SEARCHPATH_SEPARATOR_S,
docs,
NULL);
g_free (appdata);
g_free (docs);
}
else if (appdata)
data_dirs = appdata;
else if (docs)
data_dirs = docs;
else
data_dirs = g_strdup ("");
/* Using the above subfolders of Documents and Settings perhaps
* makes sense from a Windows perspective.
*
* But looking at the actual use cases of this function in GTK+
* and GNOME software, what we really want is the "share"
* subdirectory of the installation directory for the package
* our caller is a part of.
*
* As we don't know who calls us, punt, and use the installation
* location of GLib, and of the .exe file being run. To guard
* against neither of those being what we really want, callers
* of this function should have Win32-specific code to look up
* their installation folder themselves, and handle a subfolder
* "share" of it in the same way as the folders returned from
* this function.
*/
glib_top_share_dir = g_win32_get_package_installation_subdirectory (NULL, dll_name, "share");
if (glib_top_share_dir)
{
gchar *tem = data_dirs;
data_dirs = g_strconcat (data_dirs, G_SEARCHPATH_SEPARATOR_S,
glib_top_share_dir, NULL);
g_free (tem);
g_free (glib_top_share_dir);
}
exe_top_share_dir = g_win32_get_package_installation_subdirectory (NULL, NULL, "share");
if (exe_top_share_dir)
{
gchar *tem = data_dirs;
data_dirs = g_strconcat (data_dirs, G_SEARCHPATH_SEPARATOR_S,
exe_top_share_dir, NULL);
g_free (tem);
g_free (exe_top_share_dir);
}
data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
g_free (data_dirs);
data_dir_vector = (gchar **) g_win32_get_system_data_dirs_for_module (NULL);
#else
data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS");
gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS");
if (!data_dirs || !data_dirs[0])
data_dirs = "/usr/local/share/:/usr/share/";

View File

@ -125,6 +125,17 @@ G_CONST_RETURN gchar* g_get_user_data_dir (void);
G_CONST_RETURN gchar* g_get_user_config_dir (void);
G_CONST_RETURN gchar* g_get_user_cache_dir (void);
G_CONST_RETURN gchar* G_CONST_RETURN * g_get_system_data_dirs (void);
#ifdef G_OS_WIN32
G_CONST_RETURN gchar* G_CONST_RETURN * g_win32_get_system_data_dirs_for_module (gconstpointer address);
static G_CONST_RETURN gchar * G_CONST_RETURN *
g_win32_get_system_data_dirs (void)
{
return g_win32_get_system_data_dirs_for_module (&g_win32_get_system_data_dirs);
}
#define g_get_system_data_dirs g_win32_get_system_data_dirs
#endif
G_CONST_RETURN gchar* G_CONST_RETURN * g_get_system_config_dirs (void);
G_CONST_RETURN gchar* G_CONST_RETURN * g_get_language_names (void);