/src/vlc/src/posix/dirs.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * dirs.c: XDG directories configuration |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2007 VLC authors and VideoLAN |
5 | | * Copyright © 2007-2009 Rémi Denis-Courmont |
6 | | * |
7 | | * Authors: Gildas Bazin <gbazin@videolan.org> |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation; either version 2.1 of the License, or |
12 | | * (at your option) any later version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program; if not, write to the Free Software Foundation, |
21 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
22 | | *****************************************************************************/ |
23 | | |
24 | | #ifdef HAVE_CONFIG_H |
25 | | # include "config.h" |
26 | | #endif |
27 | | |
28 | | #include <vlc_common.h> |
29 | | |
30 | | #include "../libvlc.h" |
31 | | #include "../config/configuration.h" |
32 | | |
33 | | #include <unistd.h> |
34 | | #include <pwd.h> |
35 | | #include <assert.h> |
36 | | #include <limits.h> |
37 | | |
38 | | /** |
39 | | * Determines the architecture-dependent data directory |
40 | | * |
41 | | * @return a string (always succeeds). |
42 | | */ |
43 | | VLC_WEAK char *config_GetLibDir(void) |
44 | 0 | { |
45 | 0 | return strdup(LIBDIR); |
46 | 0 | } |
47 | | |
48 | | char *config_GetSysPath(vlc_sysdir_t type, const char *filename) |
49 | 0 | { |
50 | 0 | static const char env_vars[][17] = { |
51 | 0 | [VLC_PKG_DATA_DIR] = "VLC_DATA_PATH", |
52 | 0 | [VLC_PKG_LIB_DIR] = "VLC_LIB_PATH", |
53 | 0 | [VLC_PKG_LIBEXEC_DIR] = "VLC_LIBEXEC_PATH", |
54 | 0 | }; |
55 | |
|
56 | 0 | if (type < ARRAY_SIZE(env_vars)) { |
57 | 0 | const char *name = env_vars[type]; |
58 | 0 | if (*name != '\0') { |
59 | 0 | const char *value = getenv(name); |
60 | 0 | if (value != NULL) { |
61 | 0 | const char *fmt = (filename != NULL) ? "%s/%s" : "%s"; |
62 | 0 | char *filepath; |
63 | |
|
64 | 0 | if (unlikely(asprintf(&filepath, fmt, value, filename) == -1)) |
65 | 0 | filepath = NULL; |
66 | 0 | return filepath; |
67 | 0 | } |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | 0 | char *libdir = config_GetLibDir(); |
72 | 0 | if (libdir == NULL) |
73 | 0 | return NULL; /* OOM */ |
74 | | |
75 | 0 | static const char *const dirs[] = { |
76 | 0 | [VLC_PKG_DATA_DIR] = PKGDATADIR, |
77 | 0 | [VLC_PKG_LIB_DIR] = PKGLIBDIR, |
78 | 0 | [VLC_PKG_LIBEXEC_DIR] = PKGLIBEXECDIR, |
79 | 0 | [VLC_SYSDATA_DIR] = SYSDATADIR, |
80 | 0 | [VLC_LIB_DIR] = LIBDIR, |
81 | 0 | [VLC_LIBEXEC_DIR] = LIBEXECDIR, |
82 | 0 | [VLC_LOCALE_DIR] = LOCALEDIR, |
83 | 0 | }; |
84 | 0 | assert(type < ARRAY_SIZE(dirs)); |
85 | |
|
86 | 0 | const char *dir_static = dirs[type]; |
87 | 0 | assert(*dir_static != '\0'); |
88 | | /* Look for common static prefix. */ |
89 | 0 | size_t prefix_len = 0; |
90 | 0 | while (prefix_len < strlen(LIBDIR) |
91 | 0 | && LIBDIR[prefix_len] == dir_static[prefix_len]) |
92 | 0 | prefix_len++; |
93 | | |
94 | | /* Check that suffix matches between static and dynamic libdir paths. */ |
95 | 0 | char *filepath = NULL; |
96 | 0 | size_t suffix_len = strlen(LIBDIR) - prefix_len; |
97 | 0 | size_t libdir_len = strlen(libdir); |
98 | |
|
99 | 0 | if (suffix_len > libdir_len |
100 | 0 | || memcmp(&LIBDIR[prefix_len], libdir + (libdir_len - suffix_len), |
101 | 0 | suffix_len)) { |
102 | 0 | suffix_len = libdir_len; |
103 | 0 | prefix_len = 0; |
104 | 0 | } |
105 | | |
106 | | /* Graft non-common static suffix to dynamically computed common prefix. */ |
107 | 0 | const char *fmt = (filename != NULL) ? "%.*s%s/%s" : "%.*s%s"; |
108 | |
|
109 | 0 | if (unlikely(asprintf(&filepath, fmt, (int)(libdir_len - suffix_len), |
110 | 0 | libdir, dir_static + prefix_len, filename) == -1)) |
111 | 0 | filepath = NULL; |
112 | |
|
113 | 0 | free(libdir); |
114 | 0 | return filepath; |
115 | 0 | } |
116 | | |
117 | | static char *config_GetHomeDir (void) |
118 | 0 | { |
119 | | /* 1/ Try $HOME */ |
120 | 0 | const char *home = getenv ("HOME"); |
121 | 0 | if (home != NULL) |
122 | 0 | return strdup (home); |
123 | 0 | #if defined(HAVE_GETPWUID_R) |
124 | | /* 2/ Try /etc/passwd */ |
125 | 0 | long max = sysconf (_SC_GETPW_R_SIZE_MAX); |
126 | 0 | if (max != -1) |
127 | 0 | { |
128 | 0 | char buf[max]; |
129 | 0 | struct passwd pwbuf, *pw; |
130 | |
|
131 | 0 | if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0 |
132 | 0 | && pw != NULL) |
133 | 0 | return strdup (pw->pw_dir); |
134 | 0 | } |
135 | 0 | #endif |
136 | 0 | return NULL; |
137 | 0 | } |
138 | | |
139 | | static char *config_GetAppDir (const char *xdg_name, const char *xdg_default) |
140 | 0 | { |
141 | 0 | char *psz_dir; |
142 | 0 | char var[sizeof ("XDG__HOME") + strlen (xdg_name)]; |
143 | | |
144 | | /* XDG Base Directory Specification - Version 0.6 */ |
145 | 0 | snprintf (var, sizeof (var), "XDG_%s_HOME", xdg_name); |
146 | |
|
147 | 0 | const char *home = getenv (var); |
148 | 0 | if (home != NULL) |
149 | 0 | { |
150 | 0 | if (asprintf (&psz_dir, "%s/vlc", home) == -1) |
151 | 0 | psz_dir = NULL; |
152 | 0 | return psz_dir; |
153 | 0 | } |
154 | | |
155 | 0 | char *psz_home = config_GetHomeDir (); |
156 | 0 | if( psz_home == NULL |
157 | 0 | || asprintf( &psz_dir, "%s/%s/vlc", psz_home, xdg_default ) == -1 ) |
158 | 0 | psz_dir = NULL; |
159 | 0 | free (psz_home); |
160 | 0 | return psz_dir; |
161 | 0 | } |
162 | | |
163 | | static char *config_GetTypeDir (const char *xdg_name) |
164 | 0 | { |
165 | 0 | const size_t namelen = strlen (xdg_name); |
166 | 0 | const char *home = getenv ("HOME"); |
167 | 0 | const char *dir = getenv ("XDG_CONFIG_HOME"); |
168 | 0 | const char *file = "user-dirs.dirs"; |
169 | |
|
170 | 0 | if (home == NULL) |
171 | 0 | return NULL; |
172 | 0 | if (dir == NULL) |
173 | 0 | { |
174 | 0 | dir = home; |
175 | 0 | file = ".config/user-dirs.dirs"; |
176 | 0 | } |
177 | |
|
178 | 0 | char *path; |
179 | 0 | if (asprintf (&path, "%s/%s", dir, file) == -1) |
180 | 0 | return NULL; |
181 | | |
182 | 0 | FILE *stream = fopen (path, "rte"); |
183 | 0 | free (path); |
184 | 0 | path = NULL; |
185 | 0 | if (stream != NULL) |
186 | 0 | { |
187 | 0 | char *linebuf = NULL; |
188 | 0 | size_t linelen = 0; |
189 | |
|
190 | 0 | while (getline (&linebuf, &linelen, stream) != -1) |
191 | 0 | { |
192 | 0 | char *ptr = linebuf; |
193 | 0 | ptr += strspn (ptr, " \t"); /* Skip whites */ |
194 | 0 | if (strncmp (ptr, "XDG_", 4)) |
195 | 0 | continue; |
196 | 0 | ptr += 4; /* Skip XDG_ */ |
197 | 0 | if (strncmp (ptr, xdg_name, namelen)) |
198 | 0 | continue; |
199 | 0 | ptr += namelen; /* Skip XDG type name */ |
200 | 0 | if (strncmp (ptr, "_DIR", 4)) |
201 | 0 | continue; |
202 | 0 | ptr += 4; /* Skip _DIR */ |
203 | 0 | ptr += strspn (ptr, " \t"); /* Skip whites */ |
204 | 0 | if (*ptr != '=') |
205 | 0 | continue; |
206 | 0 | ptr++; /* Skip equality sign */ |
207 | 0 | ptr += strspn (ptr, " \t"); /* Skip whites */ |
208 | 0 | if (*ptr != '"') |
209 | 0 | continue; |
210 | 0 | ptr++; /* Skip quote */ |
211 | 0 | linelen -= ptr - linebuf; |
212 | |
|
213 | 0 | char *out; |
214 | 0 | if (strncmp (ptr, "$HOME", 5)) |
215 | 0 | { |
216 | 0 | path = malloc (linelen); |
217 | 0 | if (path == NULL) |
218 | 0 | continue; |
219 | 0 | out = path; |
220 | 0 | } |
221 | 0 | else |
222 | 0 | { /* Prefix with $HOME */ |
223 | 0 | const size_t homelen = strlen (home); |
224 | 0 | ptr += 5; |
225 | 0 | path = malloc (homelen + linelen - 5); |
226 | 0 | if (path == NULL) |
227 | 0 | continue; |
228 | 0 | memcpy (path, home, homelen); |
229 | 0 | out = path + homelen; |
230 | 0 | } |
231 | | |
232 | 0 | while (*ptr != '"') |
233 | 0 | { |
234 | 0 | if (*ptr == '\\') |
235 | 0 | ptr++; |
236 | 0 | if (*ptr == '\0') |
237 | 0 | { |
238 | 0 | free (path); |
239 | 0 | path = NULL; |
240 | 0 | break; |
241 | 0 | } |
242 | 0 | *(out++) = *(ptr++); |
243 | 0 | } |
244 | 0 | if (path != NULL) |
245 | 0 | *out = '\0'; |
246 | 0 | break; |
247 | 0 | } |
248 | 0 | free (linebuf); |
249 | 0 | fclose (stream); |
250 | 0 | } |
251 | | |
252 | | /* Default! */ |
253 | 0 | if (path == NULL) |
254 | 0 | { |
255 | 0 | if (strcmp (xdg_name, "DESKTOP") == 0) |
256 | 0 | { |
257 | 0 | if (asprintf (&path, "%s/Desktop", home) == -1) |
258 | 0 | return NULL; |
259 | 0 | } |
260 | 0 | else |
261 | 0 | path = strdup (home); |
262 | 0 | } |
263 | | |
264 | 0 | return path; |
265 | 0 | } |
266 | | |
267 | | |
268 | | char *platform_GetUserDir (vlc_userdir_t type) |
269 | 0 | { |
270 | 0 | switch (type) |
271 | 0 | { |
272 | 0 | case VLC_HOME_DIR: |
273 | 0 | break; |
274 | 0 | case VLC_CONFIG_DIR: |
275 | 0 | return config_GetAppDir ("CONFIG", ".config"); |
276 | 0 | case VLC_USERDATA_DIR: |
277 | 0 | return config_GetAppDir ("DATA", ".local/share"); |
278 | 0 | case VLC_CACHE_DIR: |
279 | 0 | return config_GetAppDir ("CACHE", ".cache"); |
280 | | |
281 | 0 | case VLC_DESKTOP_DIR: |
282 | 0 | return config_GetTypeDir ("DESKTOP"); |
283 | 0 | case VLC_DOWNLOAD_DIR: |
284 | 0 | return config_GetTypeDir ("DOWNLOAD"); |
285 | 0 | case VLC_TEMPLATES_DIR: |
286 | 0 | return config_GetTypeDir ("TEMPLATES"); |
287 | 0 | case VLC_PUBLICSHARE_DIR: |
288 | 0 | return config_GetTypeDir ("PUBLICSHARE"); |
289 | 0 | case VLC_DOCUMENTS_DIR: |
290 | 0 | return config_GetTypeDir ("DOCUMENTS"); |
291 | 0 | case VLC_MUSIC_DIR: |
292 | 0 | return config_GetTypeDir ("MUSIC"); |
293 | 0 | case VLC_SNAPSHOTS_DIR: |
294 | 0 | case VLC_PICTURES_DIR: |
295 | 0 | return config_GetTypeDir ("PICTURES"); |
296 | 0 | case VLC_VIDEOS_DIR: |
297 | 0 | return config_GetTypeDir ("VIDEOS"); |
298 | 0 | } |
299 | 0 | return config_GetHomeDir (); |
300 | 0 | } |