xdgmime: Add xdg_mime_set_dirs() method to override XDG envvars

In order to make xdgmime properly relocatable so that unit tests can use
it without it reading and modifying the user’s actual xdgmime files, and
without the need to call setenv() (and get tied up with thread safety
problems), add a xdg_mime_set_dirs() method to allow the dirs to be
overridden. They will still default to the values of $XDG_DATA_HOME and
$XDG_DATA_DIRS.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2018-12-13 16:24:07 +00:00 committed by Emmanuel Fleury
parent b765c7950c
commit fa6f45536d
2 changed files with 132 additions and 66 deletions

View File

@ -60,6 +60,8 @@ static XdgCallbackList *callback_list = NULL;
static XdgIconList *icon_list = NULL; static XdgIconList *icon_list = NULL;
static XdgIconList *generic_icon_list = NULL; static XdgIconList *generic_icon_list = NULL;
static char **xdg_dirs = NULL; /* NULL terminated */
XdgMimeCache **_caches = NULL; XdgMimeCache **_caches = NULL;
static int n_caches = 0; static int n_caches = 0;
@ -143,8 +145,8 @@ xdg_mime_init_from_directory (const char *directory)
assert (directory != NULL); assert (directory != NULL);
file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); strcpy (file_name, directory); strcat (file_name, "/mime.cache");
if (stat (file_name, &st) == 0) if (stat (file_name, &st) == 0)
{ {
XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name); XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
@ -163,8 +165,8 @@ xdg_mime_init_from_directory (const char *directory)
} }
free (file_name); free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1); file_name = malloc (strlen (directory) + strlen ("/globs2") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/globs2"); strcpy (file_name, directory); strcat (file_name, "/globs2");
if (stat (file_name, &st) == 0) if (stat (file_name, &st) == 0)
{ {
_xdg_mime_glob_read_from_file (global_hash, file_name, TRUE); _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE);
@ -173,8 +175,8 @@ xdg_mime_init_from_directory (const char *directory)
else else
{ {
free (file_name); free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); file_name = malloc (strlen (directory) + strlen ("/globs") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/globs"); strcpy (file_name, directory); strcat (file_name, "/globs");
if (stat (file_name, &st) == 0) if (stat (file_name, &st) == 0)
{ {
_xdg_mime_glob_read_from_file (global_hash, file_name, FALSE); _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE);
@ -186,8 +188,8 @@ xdg_mime_init_from_directory (const char *directory)
} }
} }
file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); file_name = malloc (strlen (directory) + strlen ("/magic") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/magic"); strcpy (file_name, directory); strcat (file_name, "/magic");
if (stat (file_name, &st) == 0) if (stat (file_name, &st) == 0)
{ {
_xdg_mime_magic_read_from_file (global_magic, file_name); _xdg_mime_magic_read_from_file (global_magic, file_name);
@ -198,69 +200,81 @@ xdg_mime_init_from_directory (const char *directory)
free (file_name); free (file_name);
} }
file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1); file_name = malloc (strlen (directory) + strlen ("/aliases") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); strcpy (file_name, directory); strcat (file_name, "/aliases");
_xdg_mime_alias_read_from_file (alias_list, file_name); _xdg_mime_alias_read_from_file (alias_list, file_name);
free (file_name); free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); file_name = malloc (strlen (directory) + strlen ("/subclasses") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); strcpy (file_name, directory); strcat (file_name, "/subclasses");
_xdg_mime_parent_read_from_file (parent_list, file_name); _xdg_mime_parent_read_from_file (parent_list, file_name);
free (file_name); free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1); file_name = malloc (strlen (directory) + strlen ("/icons") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/icons"); strcpy (file_name, directory); strcat (file_name, "/icons");
_xdg_mime_icon_read_from_file (icon_list, file_name); _xdg_mime_icon_read_from_file (icon_list, file_name);
free (file_name); free (file_name);
file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1); file_name = malloc (strlen (directory) + strlen ("/generic-icons") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons"); strcpy (file_name, directory); strcat (file_name, "/generic-icons");
_xdg_mime_icon_read_from_file (generic_icon_list, file_name); _xdg_mime_icon_read_from_file (generic_icon_list, file_name);
free (file_name); free (file_name);
return FALSE; /* Keep processing */ return FALSE; /* Keep processing */
} }
/* Runs a command on all the directories in the search path */ /* Set @xdg_dirs from the environment. It must not have been set already. */
static void static void
xdg_run_command_on_dirs (XdgDirectoryFunc func, xdg_init_dirs (void)
void *user_data)
{ {
const char *xdg_data_home; const char *xdg_data_home, *home, *xdg_data_dirs;
const char *xdg_data_dirs;
const char *ptr; const char *ptr;
size_t n_dirs = 0;
size_t i, current_dir;
assert (xdg_dirs == NULL);
xdg_data_home = getenv ("XDG_DATA_HOME"); xdg_data_home = getenv ("XDG_DATA_HOME");
if (xdg_data_home)
{
if ((func) (xdg_data_home, user_data))
return;
}
else
{
const char *home;
home = getenv ("HOME"); home = getenv ("HOME");
if (home != NULL)
{
char *guessed_xdg_home;
int stop_processing;
guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
strcpy (guessed_xdg_home, home);
strcat (guessed_xdg_home, "/.local/share/");
stop_processing = (func) (guessed_xdg_home, user_data);
free (guessed_xdg_home);
if (stop_processing)
return;
}
}
xdg_data_dirs = getenv ("XDG_DATA_DIRS"); xdg_data_dirs = getenv ("XDG_DATA_DIRS");
if (xdg_data_dirs == NULL) if (xdg_data_dirs == NULL)
xdg_data_dirs = "/usr/local/share/:/usr/share/"; xdg_data_dirs = "/usr/local/share/:/usr/share/";
/* Work out how many dirs were dealing with. */
if (xdg_data_home != NULL || home != NULL)
n_dirs++;
n_dirs++; /* initial entry in @xdg_data_dirs */
for (i = 0; xdg_data_dirs[i] != '\0'; i++)
if (xdg_data_dirs[i] == ':')
n_dirs++;
xdg_dirs = calloc (n_dirs + 1 /* NULL terminator */, sizeof (char *));
current_dir = 0;
/* $XDG_DATA_HOME */
if (xdg_data_home != NULL)
{
char *mime_subdir;
mime_subdir = malloc (strlen (xdg_data_home) + strlen ("/mime/") + 1);
strcpy (mime_subdir, xdg_data_home);
strcat (mime_subdir, "/mime/");
xdg_dirs[current_dir++] = mime_subdir;
}
else if (home != NULL)
{
char *guessed_xdg_home;
guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/mime/") + 1);
strcpy (guessed_xdg_home, home);
strcat (guessed_xdg_home, "/.local/share/mime/");
xdg_dirs[current_dir++] = guessed_xdg_home;
}
/* $XDG_DATA_DIRS */
ptr = xdg_data_dirs; ptr = xdg_data_dirs;
while (*ptr != '\000') while (*ptr != '\000')
@ -268,7 +282,6 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func,
const char *end_ptr; const char *end_ptr;
char *dir; char *dir;
int len; int len;
int stop_processing;
end_ptr = ptr; end_ptr = ptr;
while (*end_ptr != ':' && *end_ptr != '\000') while (*end_ptr != ':' && *end_ptr != '\000')
@ -284,17 +297,68 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func,
len = end_ptr - ptr; len = end_ptr - ptr;
else else
len = end_ptr - ptr + 1; len = end_ptr - ptr + 1;
dir = malloc (len + 1); dir = malloc (len + strlen ("/mime/") + 1);
strncpy (dir, ptr, len); strncpy (dir, ptr, len);
dir[len] = '\0'; dir[len] = '\0';
stop_processing = (func) (dir, user_data); strcat (dir, "/mime/");
free (dir);
if (stop_processing) xdg_dirs[current_dir++] = dir;
return;
ptr = end_ptr; ptr = end_ptr;
} }
/* NULL terminator */
xdg_dirs[current_dir] = NULL;
need_reread = TRUE;
}
/* Runs a command on all the directories in the search path (@xdg_dirs). */
static void
xdg_run_command_on_dirs (XdgDirectoryFunc func,
void *user_data)
{
size_t i;
if (xdg_dirs == NULL)
xdg_init_dirs ();
for (i = 0; xdg_dirs[i] != NULL; i++)
{
if ((func) (xdg_dirs[i], user_data))
return;
}
}
/* Allows the calling code to override the directories used by xdgmime, without
* having to change environment variables in a running process (which is not
* thread safe). This is intended to be used by tests. The changes will be
* picked up by xdg_mime_init() next time public API is called.
*
* This will set @xdg_dirs. Directories in @dirs must be complete, including
* the conventional `/mime` subdirectory. This is to allow tests to override
* them without the need to create a subdirectory. */
void
xdg_mime_set_dirs (const char * const *dirs)
{
size_t i;
for (i = 0; xdg_dirs != NULL && xdg_dirs[i] != NULL; i++)
free (xdg_dirs[i]);
if (xdg_dirs != NULL)
free (xdg_dirs[i]);
xdg_dirs = NULL;
if (dirs != NULL)
{
for (i = 0; dirs[i] != NULL; i++);
xdg_dirs = calloc (i + 1 /* NULL terminator */, sizeof (char*));
for (i = 0; dirs[i] != NULL; i++)
xdg_dirs[i] = strdup (dirs[i]);
xdg_dirs[i] = NULL;
}
need_reread = TRUE;
} }
/* Checks file_path to make sure it has the same mtime as last time it was /* Checks file_path to make sure it has the same mtime as last time it was
@ -348,8 +412,8 @@ xdg_check_dir (const char *directory,
assert (directory != NULL); assert (directory != NULL);
/* Check the mime.cache file */ /* Check the mime.cache file */
file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); strcpy (file_name, directory); strcat (file_name, "/mime.cache");
invalid = xdg_check_file (file_name, &exists); invalid = xdg_check_file (file_name, &exists);
free (file_name); free (file_name);
if (invalid) if (invalid)
@ -363,8 +427,8 @@ xdg_check_dir (const char *directory,
} }
/* Check the globs file */ /* Check the globs file */
file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); file_name = malloc (strlen (directory) + strlen ("/globs") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/globs"); strcpy (file_name, directory); strcat (file_name, "/globs");
invalid = xdg_check_file (file_name, NULL); invalid = xdg_check_file (file_name, NULL);
free (file_name); free (file_name);
if (invalid) if (invalid)
@ -374,8 +438,8 @@ xdg_check_dir (const char *directory,
} }
/* Check the magic file */ /* Check the magic file */
file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); file_name = malloc (strlen (directory) + strlen ("/magic") + 1);
strcpy (file_name, directory); strcat (file_name, "/mime/magic"); strcpy (file_name, directory); strcat (file_name, "/magic");
invalid = xdg_check_file (file_name, NULL); invalid = xdg_check_file (file_name, NULL);
free (file_name); free (file_name);
if (invalid) if (invalid)

View File

@ -129,6 +129,8 @@ int xdg_mime_register_reload_callback (XdgMimeCallback callback,
void xdg_mime_remove_callback (int callback_id); void xdg_mime_remove_callback (int callback_id);
#endif #endif
void xdg_mime_set_dirs (const char * const *dirs);
/* Private versions of functions that don't call xdg_mime_init () */ /* Private versions of functions that don't call xdg_mime_init () */
int _xdg_mime_mime_type_equal (const char *mime_a, int _xdg_mime_mime_type_equal (const char *mime_a,
const char *mime_b); const char *mime_b);