Skip to content

Commit

Permalink
Basic xdg directory implementation
Browse files Browse the repository at this point in the history
Search $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS for config files.
This also negates the need to have separate user and global variants of
mp_find_config_file()

Closes #864, #109.

Signed-off-by: wm4 <wm4@nowhere>
  • Loading branch information
myaaaaaaaaa authored and wm4 committed Jun 26, 2014
1 parent 8bb7d42 commit cb250d4
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 152 deletions.
202 changes: 131 additions & 71 deletions options/path.c
Expand Up @@ -43,100 +43,139 @@
#include "osdep/io.h"
#include "osdep/path.h"

typedef char *(*lookup_fun)(void *tctx, struct mpv_global *global, const char *);
static const lookup_fun config_lookup_functions[] = {
mp_find_user_config_file,
#define STRNULL(s) ((s) ? (s) : "(NULL)")



static void mp_add_xdg_config_dirs(void *talloc_ctx, struct mpv_global *global,
char **dirs, int i)
{
const char *home = getenv("HOME");
const char *tmp = NULL;

tmp = getenv("XDG_CONFIG_HOME");
if (tmp && *tmp)
dirs[i++] = talloc_asprintf(talloc_ctx, "%s/mpv", tmp);
else if (home && *home)
dirs[i++] = talloc_asprintf(talloc_ctx, "%s/.config/mpv", home);

// Maintain compatibility with old ~/.mpv
if (home && *home)
dirs[i++] = talloc_asprintf(talloc_ctx, "%s/.mpv", home);

#if HAVE_COCOA
mp_get_macosx_bundled_path,
dirs[i++] = mp_get_macosx_bundle_dir(talloc_ctx);
#endif
mp_find_global_config_file,
NULL
};

#define STRNULL(s) ((s) ? (s) : "(NULL)")
tmp = getenv("XDG_CONFIG_DIRS");
if (tmp && *tmp) {
char *xdgdirs = talloc_strdup(talloc_ctx, tmp);
while (xdgdirs) {
char *dir = xdgdirs;

char *mp_find_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename)
{
struct MPOpts *opts = global->opts;
xdgdirs = strchr(xdgdirs, ':');
if (xdgdirs)
*xdgdirs++ = 0;

char *res = NULL;
if (opts->load_config) {
if (opts->force_configdir && opts->force_configdir[0]) {
// Always force the local config dir.
res = mp_find_user_config_file(talloc_ctx, global, filename);
} else {
for (int i = 0; config_lookup_functions[i] != NULL; i++) {
res = config_lookup_functions[i](talloc_ctx, global, filename);
if (!res)
continue;

if (mp_path_exists(res))
break;

talloc_free(res);
res = NULL;
if (!dir[0])
continue;

dirs[i++] = talloc_asprintf(talloc_ctx, "%s%s", dir, "/mpv");

if (i + 1 >= MAX_CONFIG_PATHS) {
MP_WARN(global, "Too many config files, not reading any more\n");
break;
}
}
}
MP_VERBOSE(global, "any config path: '%s' -> '%s'\n", STRNULL(filename),
STRNULL(res));
return res;
else {
dirs[i++] = MPLAYER_CONFDIR;
}
}

char *mp_find_user_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename)
// Return NULL-terminated array of config directories, from highest to lowest
// priority
static char **mp_config_dirs(void *talloc_ctx, struct mpv_global *global)
{
char **ret = talloc_zero_array(talloc_ctx, char*, MAX_CONFIG_PATHS);

if (global->opts->force_configdir && global->opts->force_configdir[0]) {
ret[0] = talloc_strdup(talloc_ctx, global->opts->force_configdir);
return ret;
}

const char *tmp = NULL;
int i = 0;

tmp = getenv("MPV_HOME");
if (tmp && *tmp)
ret[i++] = talloc_strdup(talloc_ctx, tmp);

#if defined(_WIN32) && !defined(__CYGWIN__)
mp_add_win_config_dirs(talloc_ctx, global, ret, i);
#else
mp_add_xdg_config_dirs(talloc_ctx, global, ret, i);
#endif

MP_VERBOSE(global, "search dirs:");
for (char **c = ret; *c; c++)
MP_VERBOSE(global, " %s", *c);
MP_VERBOSE(global, "\n");

return ret;
}



char *mp_find_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename)
{
struct MPOpts *opts = global->opts;

void *tmp = talloc_new(NULL);
char *res = NULL;
if (opts->load_config) {
if (opts->force_configdir && opts->force_configdir[0]) {
res = mp_path_join(talloc_ctx, bstr0(opts->force_configdir),
bstr0(filename));
} else {
char *homedir = getenv("MPV_HOME");
char *configdir = NULL;

if (!homedir) {
#ifdef _WIN32
res = talloc_steal(talloc_ctx, mp_get_win_config_path(filename));
#endif
homedir = getenv("HOME");
configdir = ".mpv";
}
for (char **dir = mp_config_dirs(tmp, global); *dir; dir++) {
char *config_file = talloc_asprintf(tmp, "%s/%s", *dir, filename);

if (!res && homedir) {
char *temp = mp_path_join(NULL, bstr0(homedir), bstr0(configdir));
res = mp_path_join(talloc_ctx, bstr0(temp), bstr0(filename));
talloc_free(temp);
if (mp_path_exists(config_file)) {
res = talloc_strdup(talloc_ctx, config_file);
break;
}
}
}

MP_VERBOSE(global, "user config path: '%s' -> '%s'\n", STRNULL(filename),
talloc_free(tmp);

MP_VERBOSE(global, "config path: '%s' -> '%s'\n", STRNULL(filename),
STRNULL(res));
return res;
}

char *mp_find_global_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename)
char **mp_find_all_config_files(void *talloc_ctx, struct mpv_global *global,
const char *filename)
{
struct MPOpts *opts = global->opts;
char *res = NULL;

if (opts->load_config && !(opts->force_configdir && opts->force_configdir[0]))
{
if (filename) {
res = mp_path_join(talloc_ctx, bstr0(MPLAYER_CONFDIR), bstr0(filename));
} else {
res = talloc_strdup(talloc_ctx, MPLAYER_CONFDIR);
char **front = talloc_zero_array(talloc_ctx, char*, MAX_CONFIG_PATHS);
char **ret = front + (MAX_CONFIG_PATHS - 1);

if (opts->load_config) {
for (char **dir = mp_config_dirs(talloc_ctx, global); *dir; dir++) {
char *config_file = talloc_asprintf(talloc_ctx, "%s/%s", *dir, filename);

if (!mp_path_exists(config_file))
continue;

*(--ret) = config_file;
}
}

MP_VERBOSE(global, "global config path: '%s' -> '%s'\n", STRNULL(filename),
STRNULL(res));
return res;
MP_VERBOSE(global, "config file: '%s'\n", STRNULL(filename));

for (char** c = ret; *c; c++)
MP_VERBOSE(global, " -> '%s'\n", *c);

return ret;
}

char *mp_get_user_path(void *talloc_ctx, struct mpv_global *global,
Expand All @@ -152,7 +191,7 @@ char *mp_get_user_path(void *talloc_ctx, struct mpv_global *global,
if (bstr_split_tok(bpath, "/", &prefix, &rest)) {
const char *rest0 = rest.start; // ok in this case
if (bstr_equals0(prefix, "~")) {
res = mp_find_user_config_file(talloc_ctx, global, rest0);
res = mp_find_config_file(talloc_ctx, global, rest0);
} else if (bstr_equals0(prefix, "")) {
res = mp_path_join(talloc_ctx, bstr0(getenv("HOME")), rest);
}
Expand Down Expand Up @@ -281,14 +320,35 @@ bstr mp_split_proto(bstr path, bstr *out_url)
return r;
}

void mp_mkdirp(const char *dir)
{
void *tmp = talloc_new(NULL);
char *path = talloc_strdup(tmp, dir);
char *cdir = path + 1;

while (cdir) {
cdir = strchr(cdir, '/');
if (cdir)
*cdir = 0;

mkdir(path, 0700);

if (cdir)
*cdir++ = '/';
}

talloc_free(tmp);
}

void mp_mk_config_dir(struct mpv_global *global, char *subdir)
{
void *tmp = talloc_new(NULL);
char *confdir = mp_find_user_config_file(tmp, global, "");
if (confdir) {
if (subdir)
confdir = mp_path_join(tmp, bstr0(confdir), bstr0(subdir));
mkdir(confdir, 0777);
char *dir = mp_config_dirs(tmp, global)[0];

if (dir) {
dir = talloc_asprintf(tmp, "%s/%s", dir, subdir);
mp_mkdirp(dir);
}

talloc_free(tmp);
}
12 changes: 5 additions & 7 deletions options/path.h
Expand Up @@ -32,13 +32,10 @@ struct mpv_global;
char *mp_find_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename);

// Search for the input filename in the global configuration location.
char *mp_find_global_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename);

// Search for the input filename in the user configuration location.
char *mp_find_user_config_file(void *talloc_ctx, struct mpv_global *global,
const char *filename);
// Find all instances of the given config file. Paths are returned in order
// from lowest to highest priority.
char **mp_find_all_config_files(void *talloc_ctx, struct mpv_global *global,
const char *filename);

// Normally returns a talloc_strdup'ed copy of the path, except for special
// paths starting with '~'. Used to allow the user explicitly reference a
Expand Down Expand Up @@ -77,6 +74,7 @@ bool mp_is_url(bstr path);

bstr mp_split_proto(bstr path, bstr *out_url);

void mp_mkdirp(const char *dir);
void mp_mk_config_dir(struct mpv_global *global, char *subdir);

#endif /* MPLAYER_PATH_H */
5 changes: 2 additions & 3 deletions osdep/path-macosx.m
Expand Up @@ -20,12 +20,11 @@
#include "options/path.h"
#include "osdep/path.h"

char *mp_get_macosx_bundled_path(void *talloc_ctx, struct mpv_global *global,
const char *file)
char *mp_get_macosx_bundle_dir(void *talloc_ctx)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *path = [[NSBundle mainBundle] resourcePath];
char *rv = mp_path_join(talloc_ctx, bstr0([path UTF8String]), bstr0(file));
char *rv = talloc_strdup(talloc_ctx, [path UTF8String]);
[pool release];
return rv;
}
59 changes: 26 additions & 33 deletions osdep/path-win.c
Expand Up @@ -24,55 +24,48 @@

// Warning: do not use PATH_MAX. Cygwin messed it up.

static void get_exe_dir(wchar_t path[MAX_PATH + 1])
char *mp_get_win_exe_dir(void *talloc_ctx)
{
int len = (int)GetModuleFileNameW(NULL, path, MAX_PATH);
wchar_t w_exedir[MAX_PATH + 1] = {0};

int len = (int)GetModuleFileNameW(NULL, w_exedir, MAX_PATH);
int imax = 0;
for (int i = 0; i < len; i++) {
if (path[i] == '\\') {
path[i] = '/';
if (w_exedir[i] == '\\') {
w_exedir[i] = '/';
imax = i;
}
}

path[imax] = '\0';
w_exedir[imax] = '\0';

return mp_to_utf8(talloc_ctx, w_exedir);
}
char *mp_get_win_exe_subdir(void *talloc_ctx)
{
return talloc_asprintf(talloc_ctx, "%s/mpv", mp_get_win_exe_dir(talloc_ctx));
}

char *mp_get_win_config_path(const char *filename)
char *mp_get_win_app_dir(void *talloc_ctx)
{
wchar_t w_appdir[MAX_PATH + 1] = {0};
wchar_t w_exedir[MAX_PATH + 1] = {0};
char *res = NULL;
void *tmp = talloc_new(NULL);

#ifndef __CYGWIN__
if (SHGetFolderPathW(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL,
SHGFP_TYPE_CURRENT, w_appdir) != S_OK)
w_appdir[0] = '\0';
#endif
return NULL;

get_exe_dir(w_exedir);
return talloc_asprintf(talloc_ctx, "%s/mpv", mp_to_utf8(talloc_ctx, w_appdir));
}

if (filename && filename[0] && w_exedir[0]) {
char *dir = mp_to_utf8(tmp, w_exedir);
char *temp = mp_path_join(tmp, bstr0(dir), bstr0("mpv"));
res = mp_path_join(NULL, bstr0(temp), bstr0(filename));
if (!mp_path_exists(res) || mp_path_isdir(res)) {
talloc_free(res);
res = mp_path_join(NULL, bstr0(dir), bstr0(filename));
if (!mp_path_exists(res) || mp_path_isdir(res)) {
talloc_free(res);
res = NULL;
}
}
}

if (!res && w_appdir[0]) {
char *dir = mp_to_utf8(tmp, w_appdir);
char *temp = mp_path_join(tmp, bstr0(dir), bstr0("mpv"));
res = mp_path_join(NULL, bstr0(temp), bstr0(filename));
}

talloc_free(tmp);
return res;
void mp_add_win_config_dirs(void *talloc_ctx, struct mpv_global *global,
char **dirs, int i)
{
if ((dirs[i] = mp_get_win_exe_subdir(talloc_ctx)))
i++;
if ((dirs[i] = mp_get_win_exe_dir(talloc_ctx)))
i++;
if ((dirs[i] = mp_get_win_app_dir(talloc_ctx)))
i++;
}

0 comments on commit cb250d4

Please sign in to comment.