From cb250d490c14872f03bb0320179e48d05fe2539d Mon Sep 17 00:00:00 2001 From: Kenneth Zhou Date: Wed, 18 Jun 2014 19:55:40 -0400 Subject: Basic xdg directory implementation 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 --- options/path.c | 202 +++++++++++++++++++++++++++++++++------------------ options/path.h | 12 ++- osdep/path-macosx.m | 5 +- osdep/path-win.c | 59 +++++++-------- osdep/path.h | 15 +++- player/configfiles.c | 33 +++++---- player/lua.c | 2 +- player/scripting.c | 8 +- stream/stream_dvb.c | 19 ++--- sub/ass_mp.c | 4 +- sub/find_subfiles.c | 2 +- 11 files changed, 209 insertions(+), 152 deletions(-) diff --git a/options/path.c b/options/path.c index 53ce3a5282..decaba4c78 100644 --- a/options/path.c +++ b/options/path.c @@ -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, @@ -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); } @@ -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); } diff --git a/options/path.h b/options/path.h index 0d23dcbe9b..30ca34d205 100644 --- a/options/path.h +++ b/options/path.h @@ -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 @@ -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 */ diff --git a/osdep/path-macosx.m b/osdep/path-macosx.m index c5240f570e..83fb113e8d 100644 --- a/osdep/path-macosx.m +++ b/osdep/path-macosx.m @@ -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; } diff --git a/osdep/path-win.c b/osdep/path-win.c index 2b1e8b5144..e35bab37a2 100644 --- a/osdep/path-win.c +++ b/osdep/path-win.c @@ -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++; } diff --git a/osdep/path.h b/osdep/path.h index 91afbce604..c2fe2ba303 100644 --- a/osdep/path.h +++ b/osdep/path.h @@ -1,12 +1,19 @@ #ifndef OSDEP_PATH_H #define OSDEP_PATH_H +#define MAX_CONFIG_PATHS 32 + struct mpv_global; -char *mp_get_win_config_path(const char *filename); +// Windows config directories +char *mp_get_win_exe_dir(void *talloc_ctx); +char *mp_get_win_exe_subdir(void *talloc_ctx); +char *mp_get_win_app_dir(void *talloc_ctx); + +void mp_add_win_config_dirs(void *talloc_ctx, struct mpv_global *global, + char **dirs, int i); -// Returns absolute path of a resource file in a Mac OS X application bundle. -char *mp_get_macosx_bundled_path(void *talloc_ctx, struct mpv_global *global, - const char *filename); +// Returns Mac OS X application bundle directory. +char *mp_get_macosx_bundle_dir(void *talloc_ctx); #endif diff --git a/player/configfiles.c b/player/configfiles.c index 2794ddaebf..a295d7d9f2 100644 --- a/player/configfiles.c +++ b/player/configfiles.c @@ -71,21 +71,22 @@ bool mp_parse_cfgfiles(struct MPContext *mpctx) // The #if is a stupid hack to avoid errors if libavfilter is not available. #if HAVE_LIBAVFILTER && HAVE_ENCODING conffile = mp_find_config_file(tmp, mpctx->global, "encoding-profiles.conf"); - if (conffile && mp_path_exists(conffile)) + if (conffile) m_config_parse_config_file(mpctx->mconfig, conffile, SECT_ENCODE, 0); #endif - conffile = mp_find_global_config_file(tmp, mpctx->global, "mpv.conf"); - if (conffile && m_config_parse_config_file(conf, conffile, section, 0) < 0) { - r = false; - goto done; + // Maintain compatibility with /config + for (char** cf = mp_find_all_config_files(tmp, mpctx->global, "config"); *cf; cf++) { + if (m_config_parse_config_file(conf, *cf, section, 0) < 0) { + r = false; + goto done; + } } - mp_mk_config_dir(mpctx->global, NULL); - if (!(conffile = mp_find_user_config_file(tmp, mpctx->global, "config"))) - MP_ERR(mpctx, "mp_find_user_config_file(\"config\") problem\n"); - else if (m_config_parse_config_file(conf, conffile, section, 0) < 0) { - r = false; - goto done; + for (char** cf = mp_find_all_config_files(tmp, mpctx->global, "mpv.conf"); *cf; cf++) { + if (m_config_parse_config_file(conf, *cf, section, 0) < 0) { + r = false; + goto done; + } } if (encoding) @@ -134,7 +135,7 @@ static void mp_load_per_file_config(struct MPContext *mpctx) return; } - if ((confpath = mp_find_user_config_file(NULL, mpctx->global, name))) { + if ((confpath = mp_find_config_file(NULL, mpctx->global, name))) { try_load_config(mpctx, confpath, FILE_LOCAL_FLAGS); talloc_free(confpath); @@ -200,9 +201,13 @@ static char *mp_get_playback_resume_config_filename(struct mpv_global *global, for (int i = 0; i < 16; i++) conf = talloc_asprintf_append(conf, "%02X", md5[i]); - conf = talloc_asprintf(tmp, "%s/%s", MP_WATCH_LATER_CONF, conf); + res = talloc_asprintf(tmp, MP_WATCH_LATER_CONF "/%s", conf); + res = mp_find_config_file(NULL, global, res); - res = mp_find_user_config_file(NULL, global, conf); + if (!res) { + res = mp_find_config_file(tmp, global, MP_WATCH_LATER_CONF); + res = talloc_asprintf(NULL, "%s/%s", res, conf); + } exit: talloc_free(tmp); diff --git a/player/lua.c b/player/lua.c index ab00c58c68..25f29c3f09 100644 --- a/player/lua.c +++ b/player/lua.c @@ -343,7 +343,7 @@ static int script_find_config_file(lua_State *L) { struct MPContext *mpctx = get_mpctx(L); const char *s = luaL_checkstring(L, 1); - char *path = mp_find_user_config_file(NULL, mpctx->global, s); + char *path = mp_find_config_file(NULL, mpctx->global, s); if (path) { lua_pushstring(L, path); } else { diff --git a/player/scripting.c b/player/scripting.c index 7b3d408030..2272f3c2ee 100644 --- a/player/scripting.c +++ b/player/scripting.c @@ -167,11 +167,11 @@ void mp_load_scripts(struct MPContext *mpctx) } if (!mpctx->opts->auto_load_scripts) return; - // Load ~/.mpv/lua/* + + // Load all lua scripts void *tmp = talloc_new(NULL); - char *script_path = mp_find_user_config_file(tmp, mpctx->global, "lua"); - if (script_path) { - files = list_script_files(tmp, script_path); + for (char **luadir = mp_find_all_config_files(tmp, mpctx->global, "lua"); *luadir; luadir++) { + files = list_script_files(tmp, *luadir); for (int n = 0; files && files[n]; n++) mp_load_script(mpctx, files[n]); } diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c index 1462d568e7..d6ebddb944 100644 --- a/stream/stream_dvb.c +++ b/stream/stream_dvb.c @@ -750,29 +750,24 @@ dvb_config_t *dvb_get_config(stream_t *stream) } void *talloc_ctx = talloc_new(NULL); - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf"); + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf"); switch(type) { case TUNER_TER: - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf.ter"); + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.ter"); break; case TUNER_CBL: - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf.cbl"); + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.cbl"); break; case TUNER_SAT: - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf.sat"); + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.sat"); break; case TUNER_ATSC: - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf.atsc"); + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.atsc"); break; } - if(conf_file && (access(conf_file, F_OK | R_OK) != 0)) { - conf_file = mp_find_user_config_file(talloc_ctx, global, "channels.conf"); - - if(conf_file && (access(conf_file, F_OK | R_OK) != 0)) { - conf_file = mp_find_global_config_file(talloc_ctx, global, "channels.conf"); - } - } + if(conf_file && (access(conf_file, F_OK | R_OK) != 0)) + conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf"); list = dvb_get_channels(log, conf_file, type); talloc_free(talloc_ctx); diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 02172605d5..0609351f7b 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -177,7 +177,7 @@ void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts, struct mpv_global *global, struct mp_log *log) { void *tmp = talloc_new(NULL); - char *default_font = mp_find_user_config_file(tmp, global, "subfont.ttf"); + char *default_font = mp_find_config_file(tmp, global, "subfont.ttf"); char *config = mp_find_config_file(tmp, global, "fonts.conf"); if (default_font && !mp_path_exists(default_font)) @@ -249,7 +249,7 @@ static void message_callback(int level, const char *format, va_list va, void *ct ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log) { - char *path = mp_find_user_config_file(NULL, global, "fonts"); + char *path = mp_find_config_file(NULL, global, "fonts"); ASS_Library *priv = ass_library_init(); if (!priv) abort(); diff --git a/sub/find_subfiles.c b/sub/find_subfiles.c index a8d6348fe9..9313bf379d 100644 --- a/sub/find_subfiles.c +++ b/sub/find_subfiles.c @@ -242,7 +242,7 @@ struct subfn *find_text_subtitles(struct mpv_global *global, const char *fname) } // Load subtitles in ~/.mpv/sub limiting sub fuzziness - char *mp_subdir = mp_find_user_config_file(NULL, global, "sub/"); + char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); if (mp_subdir) append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); talloc_free(mp_subdir); -- cgit v1.2.3