Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This file is part of mpv. |
3 | | * |
4 | | * Get path to config dir/file. |
5 | | * |
6 | | * mpv is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * mpv is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include <assert.h> |
21 | | #include <stdio.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <stdbool.h> |
25 | | #include <sys/types.h> |
26 | | #include <sys/stat.h> |
27 | | #include <errno.h> |
28 | | |
29 | | #include "config.h" |
30 | | |
31 | | #include "misc/path_utils.h" |
32 | | #include "common/common.h" |
33 | | #include "common/global.h" |
34 | | #include "common/msg.h" |
35 | | #include "options/options.h" |
36 | | #include "options/path.h" |
37 | | #include "mpv_talloc.h" |
38 | | #include "osdep/io.h" |
39 | | #include "osdep/path.h" |
40 | | #include "misc/ctype.h" |
41 | | |
42 | | // In order of decreasing priority: the first has highest priority. |
43 | | static const mp_get_platform_path_cb path_resolvers[] = { |
44 | | #if HAVE_COCOA |
45 | | mp_get_platform_path_mac, |
46 | | #endif |
47 | | #if HAVE_DARWIN |
48 | | mp_get_platform_path_darwin, |
49 | | #elif !defined(_WIN32) || defined(__CYGWIN__) |
50 | | mp_get_platform_path_unix, |
51 | | #endif |
52 | | #if HAVE_UWP |
53 | | mp_get_platform_path_uwp, |
54 | | #elif defined(_WIN32) |
55 | | mp_get_platform_path_win, |
56 | | #endif |
57 | | }; |
58 | | |
59 | | // from highest (most preferred) to lowest priority |
60 | | static const char *const config_dirs[] = { |
61 | | "home", |
62 | | "old_home", |
63 | | "osxbundle", |
64 | | "exe_dir", |
65 | | "global", |
66 | | }; |
67 | | |
68 | | // Return a platform specific path using a path type as defined in osdep/path.h. |
69 | | // Keep in mind that the only way to free the return value is freeing talloc_ctx |
70 | | // (or its children), as this function can return a statically allocated string. |
71 | | static const char *mp_get_platform_path(void *talloc_ctx, |
72 | | struct mpv_global *global, |
73 | | const char *type) |
74 | 3.89M | { |
75 | 3.89M | mp_assert(talloc_ctx); |
76 | | |
77 | 3.89M | if (global->configdir) { |
78 | | // Return NULL for all platform paths if --no-config is passed |
79 | 3.85M | if (!global->configdir[0]) |
80 | 3.84M | return NULL; |
81 | | |
82 | | // force all others to NULL, only first returns the path |
83 | 27.4k | for (int n = 0; n < MP_ARRAY_SIZE(config_dirs); n++) { |
84 | 26.7k | if (strcmp(config_dirs[n], type) == 0) |
85 | 8.40k | return (n == 0) ? global->configdir : NULL; |
86 | 26.7k | } |
87 | 9.05k | } |
88 | | |
89 | | // Return the native config path if the platform doesn't support the |
90 | | // type we are trying to fetch. |
91 | 37.1k | const char *fallback_type = NULL; |
92 | 37.1k | if (!strcmp(type, "cache") || !strcmp(type, "state")) |
93 | 7.98k | fallback_type = "home"; |
94 | | |
95 | 47.7k | for (int n = 0; n < MP_ARRAY_SIZE(path_resolvers); n++) { |
96 | 37.1k | const char *path = path_resolvers[n](talloc_ctx, type); |
97 | 37.1k | if (path && path[0]) |
98 | 26.5k | return path; |
99 | 37.1k | } |
100 | | |
101 | 10.6k | if (fallback_type) { |
102 | 0 | mp_assert(strcmp(fallback_type, type) != 0); |
103 | 0 | return mp_get_platform_path(talloc_ctx, global, fallback_type); |
104 | 0 | } |
105 | 10.6k | return NULL; |
106 | 10.6k | } |
107 | | |
108 | | void mp_init_paths(struct mpv_global *global, struct MPOpts *opts) |
109 | 129k | { |
110 | 129k | TA_FREEP(&global->configdir); |
111 | | |
112 | 129k | const char *force_configdir = getenv("MPV_HOME"); |
113 | 129k | if (opts->force_configdir && opts->force_configdir[0]) |
114 | 91 | force_configdir = opts->force_configdir; |
115 | 129k | if (!opts->load_config) |
116 | 128k | force_configdir = ""; |
117 | | |
118 | 129k | global->configdir = mp_get_user_path(global, global, force_configdir); |
119 | 129k | } |
120 | | |
121 | | char *mp_find_user_file(void *talloc_ctx, struct mpv_global *global, |
122 | | const char *type, const char *filename) |
123 | 686k | { |
124 | 686k | void *tmp = talloc_new(NULL); |
125 | 686k | char *res = (char *)mp_get_platform_path(tmp, global, type); |
126 | 686k | if (res) |
127 | 8.23k | res = mp_path_join(talloc_ctx, res, filename); |
128 | 686k | talloc_free(tmp); |
129 | 686k | MP_DBG(global, "%s path: '%s' -> '%s'\n", type, filename, res ? res : "-"); |
130 | 686k | return res; |
131 | 686k | } |
132 | | |
133 | | static char **mp_find_all_config_files_limited(void *talloc_ctx, |
134 | | struct mpv_global *global, |
135 | | int max_files, |
136 | | const char *filename) |
137 | 588k | { |
138 | 588k | char **ret = talloc_array(talloc_ctx, char*, 2); // 2 preallocated |
139 | 588k | int num_ret = 0; |
140 | | |
141 | 3.53M | for (int i = 0; i < MP_ARRAY_SIZE(config_dirs); i++) { |
142 | 2.94M | const char *dir = mp_get_platform_path(ret, global, config_dirs[i]); |
143 | 2.94M | bstr s = bstr0(filename); |
144 | 2.95M | while (dir && num_ret < max_files && s.len) { |
145 | 15.4k | bstr fn; |
146 | 15.4k | bstr_split_tok(s, "|", &fn, &s); |
147 | | |
148 | 15.4k | char *file = mp_path_join_bstr(ret, bstr0(dir), fn); |
149 | 15.4k | if (mp_path_exists(file)) { |
150 | 1.65k | MP_DBG(global, "config path: '%.*s' -> '%s'\n", |
151 | 1.65k | BSTR_P(fn), file); |
152 | 1.65k | MP_TARRAY_APPEND(NULL, ret, num_ret, file); |
153 | 13.7k | } else { |
154 | 13.7k | MP_DBG(global, "config path: '%.*s' -/-> '%s'\n", |
155 | 13.7k | BSTR_P(fn), file); |
156 | 13.7k | } |
157 | 15.4k | } |
158 | 2.94M | } |
159 | | |
160 | 588k | MP_TARRAY_GROW(NULL, ret, num_ret); |
161 | 588k | ret[num_ret] = NULL; |
162 | | |
163 | 588k | for (int n = 0; n < num_ret / 2; n++) |
164 | 0 | MPSWAP(char*, ret[n], ret[num_ret - n - 1]); |
165 | 588k | return ret; |
166 | 588k | } |
167 | | |
168 | | char **mp_find_all_config_files(void *talloc_ctx, struct mpv_global *global, |
169 | | const char *filename) |
170 | 505k | { |
171 | 505k | return mp_find_all_config_files_limited(talloc_ctx, global, 64, filename); |
172 | 505k | } |
173 | | |
174 | | char *mp_find_config_file(void *talloc_ctx, struct mpv_global *global, |
175 | | const char *filename) |
176 | 83.1k | { |
177 | 83.1k | char **l = mp_find_all_config_files_limited(talloc_ctx, global, 1, filename); |
178 | 83.1k | char *r = l && l[0] ? talloc_steal(talloc_ctx, l[0]) : NULL; |
179 | 83.1k | talloc_free(l); |
180 | 83.1k | return r; |
181 | 83.1k | } |
182 | | |
183 | | char *mp_get_user_path(void *talloc_ctx, struct mpv_global *global, |
184 | | const char *path) |
185 | 1.17M | { |
186 | 1.17M | if (!path) |
187 | 632k | return NULL; |
188 | 539k | char *res = NULL; |
189 | 539k | bstr bpath = bstr0(path); |
190 | 539k | if (bstr_eatstart0(&bpath, "~")) { |
191 | | // parse to "~" <prefix> "/" <rest> |
192 | 267k | bstr prefix, rest; |
193 | 267k | if (bstr_split_tok(bpath, "/", &prefix, &rest)) { |
194 | 267k | const char *rest0 = rest.start; // ok in this case |
195 | 267k | if (bstr_equals0(prefix, "~")) { |
196 | 6.62k | res = mp_find_config_file(talloc_ctx, global, rest0); |
197 | 6.62k | if (!res) { |
198 | 4.97k | void *tmp = talloc_new(NULL); |
199 | 4.97k | const char *p = mp_get_platform_path(tmp, global, "home"); |
200 | 4.97k | res = mp_path_join_bstr(talloc_ctx, bstr0(p), rest); |
201 | 4.97k | talloc_free(tmp); |
202 | 4.97k | } |
203 | 260k | } else if (bstr_equals0(prefix, "")) { |
204 | 720 | char *home = getenv("HOME"); |
205 | 720 | if (!home) |
206 | 0 | home = getenv("USERPROFILE"); |
207 | 720 | res = mp_path_join_bstr(talloc_ctx, bstr0(home), rest); |
208 | 259k | } else if (bstr_eatstart0(&prefix, "~")) { |
209 | 259k | void *tmp = talloc_new(NULL); |
210 | 259k | char type[80]; |
211 | 259k | snprintf(type, sizeof(type), "%.*s", BSTR_P(prefix)); |
212 | 259k | const char *p = mp_get_platform_path(tmp, global, type); |
213 | 259k | res = mp_path_join_bstr(talloc_ctx, bstr0(p), rest); |
214 | 259k | talloc_free(tmp); |
215 | 259k | } |
216 | 267k | } |
217 | 267k | } |
218 | 539k | if (!res) { |
219 | 273k | res = talloc_strdup(talloc_ctx, path); |
220 | 273k | } else { |
221 | 266k | MP_DBG(global, "user path: '%s' -> '%s'\n", path, res); |
222 | 266k | } |
223 | 539k | return res; |
224 | 1.17M | } |
225 | | |
226 | | char *mp_normalize_user_path(void *talloc_ctx, struct mpv_global *global, |
227 | | const char *path) |
228 | 11.0k | { |
229 | 11.0k | char *expanded = mp_get_user_path(NULL, global, path); |
230 | 11.0k | char *normalized = mp_normalize_path(talloc_ctx, expanded); |
231 | 11.0k | talloc_free(expanded); |
232 | 11.0k | return normalized; |
233 | 11.0k | } |
234 | | |
235 | | void mp_mk_user_dir(struct mpv_global *global, const char *type, char *subdir) |
236 | 129k | { |
237 | 129k | char *dir = mp_find_user_file(NULL, global, type, subdir); |
238 | 129k | if (dir) |
239 | 576 | mp_mkdirp(dir); |
240 | 129k | talloc_free(dir); |
241 | 129k | } |