1 /*
2 This file is part of darktable,
3 Copyright (C) 2012-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* getpwnam_r availability check */
20 #if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE \
21 || defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__ \
22 || defined __NetBSD__ || defined __OpenBSD__
23 #include "config.h"
24
25 #include <pwd.h>
26 #include <sys/types.h>
27 #define HAVE_GETPWNAM_R 1
28 #endif
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #ifdef __APPLE__
35 #include "osx/osx.h"
36 #endif
37
38 #include "common/grealpath.h"
39 #include "darktable.h"
40 #include "file_location.h"
41 #include "whereami.h"
42
dt_loc_init(const char * datadir,const char * moduledir,const char * localedir,const char * configdir,const char * cachedir,const char * tmpdir)43 void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir)
44 {
45 // Assemble pathes
46 char* application_directory = NULL;
47 int dirname_length;
48 // calling wai_getExecutablePath twice as recommended in the docs:
49 // the first call retrieves the length of the path
50 int length = wai_getExecutablePath(NULL, 0, &dirname_length);
51 if (length > 0)
52 {
53 application_directory = (char*)malloc(length + 1);
54 // the second call retrieves the path including the executable
55 wai_getExecutablePath(application_directory, length, &dirname_length);
56 // strip of the executable name from the path to retrieve the path alone
57 application_directory[dirname_length] = '\0';
58 }
59 dt_print(DT_DEBUG_DEV, "application_directory: %s\n", application_directory);
60
61 // set up absolute pathes based on their relative value
62 dt_loc_init_datadir(application_directory, datadir);
63 dt_loc_init_plugindir(application_directory, moduledir);
64 dt_loc_init_localedir(application_directory, localedir);
65 dt_loc_init_user_config_dir(configdir);
66 dt_loc_init_user_cache_dir(cachedir);
67 dt_loc_init_sharedir(application_directory);
68 dt_loc_init_tmp_dir(tmpdir);
69 free(application_directory);
70 }
71
dt_loc_get_home_dir(const gchar * user)72 gchar *dt_loc_get_home_dir(const gchar *user)
73 {
74 if(user == NULL || g_strcmp0(user, g_get_user_name()) == 0)
75 {
76 const char *home_dir = g_getenv("HOME");
77 return g_strdup((home_dir != NULL) ? home_dir : g_get_home_dir());
78 }
79
80 #if defined HAVE_GETPWNAM_R
81 /* if the given username is not the same as the current one, we try
82 * to retrieve the pw dir from the password file entry */
83 struct passwd pwd;
84 struct passwd *result;
85 #ifdef _SC_GETPW_R_SIZE_MAX
86 int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
87 if(bufsize < 0)
88 {
89 bufsize = 4096;
90 }
91 #else
92 int bufsize = 4096;
93 #endif
94
95 gchar *buffer = g_malloc0_n(bufsize, sizeof(gchar));
96 if(buffer == NULL)
97 {
98 return NULL;
99 }
100
101 getpwnam_r(user, &pwd, buffer, bufsize, &result);
102 if(result == NULL)
103 {
104 g_free(buffer);
105 return NULL;
106 }
107
108 gchar *dir = g_strdup(pwd.pw_dir);
109 g_free(buffer);
110
111 return dir;
112 #else
113 return NULL;
114 #endif
115 }
116
dt_loc_init_generic(const char * absolute_value,const char * application_directory,const char * default_value)117 gchar *dt_loc_init_generic(const char *absolute_value, const char *application_directory, const char *default_value)
118 {
119 gchar *result = NULL;
120 gchar *path = NULL;
121
122 if(absolute_value)
123 {
124 // the only adjustment the absolute path needs is transforming the possible tilde '~' to an absolute path
125 path = dt_util_fix_path(absolute_value);
126 }
127 else
128 {
129 // the default_value could be absolute or relative. we decide upon presence of the application_directory.
130 if(application_directory)
131 {
132 // default_value is relative.
133 // combine basename (application_directory) and relative path (default_value).
134 gchar complete_path[PATH_MAX] = { 0 };
135 #if defined(__APPLE__)
136 char *bundle_path = dt_osx_get_bundle_res_path();
137 if(bundle_path)
138 {
139 /// bundle detected ...
140
141 // on a mac inside the bundle the executables are in <bundleroot>/Contents/MacOS/
142 // all other directories are a subdirectory of <bundleroot>/Contents/Resources:
143 // <bundleroot>/Contents/Resources/etc
144 // <bundleroot>/Contents/Resources/lib
145 // <bundleroot>/Contents/Resources/share
146 // so the relative path from the binary directory to the other directories differs to the non-bundle version by
147 // ../etc -> ../Resources/etc,
148 // ../lib -> ../Resources/lib,
149 // ../share -> ../Resources/share,
150 // So we have to modify the relative default value
151
152 // +2: removes the two dots '..'
153 g_snprintf(complete_path, sizeof(complete_path), "%s/../Resources%s", application_directory, default_value + 2);
154 }
155 else
156 {
157 /// outside a bundle. Apply standard linux path rules
158 g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
159 }
160 #else
161 g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
162 #endif
163 path = g_strdup(complete_path);
164 }
165 else
166 {
167 // default_value is absolute
168 path = g_strdup(default_value);
169 }
170 }
171
172 // create file if it does not exist
173 if(g_file_test(path, G_FILE_TEST_EXISTS) == FALSE) g_mkdir_with_parents(path, 0700);
174
175 // removes '.', '..', and extra '/' characters.
176 result = g_realpath(path);
177
178 g_free(path);
179 return result;
180 }
181
dt_loc_init_user_config_dir(const char * configdir)182 void dt_loc_init_user_config_dir(const char *configdir)
183 {
184 char *default_config_dir = g_build_filename(g_get_user_config_dir(), "darktable", NULL);
185 darktable.configdir = dt_loc_init_generic(configdir, NULL, default_config_dir);
186 dt_check_opendir("darktable.configdir", darktable.configdir);
187 g_free(default_config_dir);
188 }
189
dt_loc_init_tmp_dir(const char * tmpdir)190 void dt_loc_init_tmp_dir(const char *tmpdir)
191 {
192 darktable.tmpdir = dt_loc_init_generic(tmpdir, NULL, g_get_tmp_dir());
193 dt_check_opendir("darktable.tmpdir", darktable.tmpdir);
194 }
195
dt_loc_init_user_cache_dir(const char * cachedir)196 void dt_loc_init_user_cache_dir(const char *cachedir)
197 {
198 char *default_cache_dir = g_build_filename(g_get_user_cache_dir(), "darktable", NULL);
199 darktable.cachedir = dt_loc_init_generic(cachedir, NULL, default_cache_dir);
200 dt_check_opendir("darktable.cachedir", darktable.cachedir);
201 g_free(default_cache_dir);
202 }
203
dt_loc_init_plugindir(const char * application_directory,const char * plugindir)204 void dt_loc_init_plugindir(const char* application_directory, const char *plugindir)
205 {
206 darktable.plugindir = dt_loc_init_generic(plugindir, application_directory, DARKTABLE_LIBDIR);
207 dt_check_opendir("darktable.plugindir", darktable.plugindir);
208 }
209
dt_check_opendir(const char * context,const char * directory)210 void dt_check_opendir(const char* context, const char* directory)
211 {
212 if (!directory)
213 {
214 fprintf(stderr, "directory for %s has not been set.\n", context);
215 exit(EXIT_FAILURE);
216 }
217
218 #if _WIN32
219 wchar_t *wdirectory = g_utf8_to_utf16 (directory, -1, NULL, NULL, NULL);
220 DWORD attribs = GetFileAttributesW(wdirectory);
221 g_free(wdirectory);
222 if (attribs != INVALID_FILE_ATTRIBUTES &&
223 (attribs & FILE_ATTRIBUTE_DIRECTORY))
224 {
225 dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
226 }
227 else
228 {
229 fprintf(stderr, "%s: directory '%s' fails to open.'\n", context, directory);
230 exit(EXIT_FAILURE);
231 }
232 #else
233 DIR* dir = opendir(directory);
234 if (dir)
235 {
236 dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
237 closedir(dir);
238 }
239 else
240 {
241 fprintf(stderr, "opendir '%s' fails with: '%s'\n", directory, strerror(errno));
242 exit(EXIT_FAILURE);
243 }
244 #endif
245 }
246
dt_loc_init_localedir(const char * application_directory,const char * localedir)247 void dt_loc_init_localedir(const char* application_directory, const char *localedir)
248 {
249 darktable.localedir = dt_loc_init_generic(localedir, application_directory, DARKTABLE_LOCALEDIR);
250 dt_check_opendir("darktable.localedir", darktable.localedir);
251 }
252
dt_loc_init_datadir(const char * application_directory,const char * datadir)253 void dt_loc_init_datadir(const char* application_directory, const char *datadir)
254 {
255 darktable.datadir = dt_loc_init_generic(datadir, application_directory, DARKTABLE_DATADIR);
256 dt_check_opendir("darktable.datadir", darktable.datadir);
257 }
258
dt_loc_init_sharedir(const char * application_directory)259 void dt_loc_init_sharedir(const char* application_directory)
260 {
261 darktable.sharedir = dt_loc_init_generic(NULL, application_directory, DARKTABLE_SHAREDIR);
262 dt_check_opendir("darktable.sharedir", darktable.sharedir);
263 }
264
dt_loc_get_kerneldir(char * kerneldir,size_t bufsize)265 void dt_loc_get_kerneldir(char *kerneldir, size_t bufsize)
266 {
267 char datadir[PATH_MAX] = { 0 };
268 dt_loc_get_datadir(datadir, sizeof(datadir));
269 snprintf(kerneldir, bufsize, "%s" G_DIR_SEPARATOR_S "kernels", datadir);
270 }
271
dt_loc_get_plugindir(char * plugindir,size_t bufsize)272 void dt_loc_get_plugindir(char *plugindir, size_t bufsize)
273 {
274 g_strlcpy(plugindir, darktable.plugindir, bufsize);
275 }
276
dt_loc_get_localedir(char * localedir,size_t bufsize)277 void dt_loc_get_localedir(char *localedir, size_t bufsize)
278 {
279 g_strlcpy(localedir, darktable.localedir, bufsize);
280 }
281
dt_loc_get_user_config_dir(char * configdir,size_t bufsize)282 void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
283 {
284 g_strlcpy(configdir, darktable.configdir, bufsize);
285 }
dt_loc_get_user_cache_dir(char * cachedir,size_t bufsize)286 void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
287 {
288 g_strlcpy(cachedir, darktable.cachedir, bufsize);
289 }
dt_loc_get_tmp_dir(char * tmpdir,size_t bufsize)290 void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
291 {
292 g_strlcpy(tmpdir, darktable.tmpdir, bufsize);
293 }
dt_loc_get_datadir(char * datadir,size_t bufsize)294 void dt_loc_get_datadir(char *datadir, size_t bufsize)
295 {
296 g_strlcpy(datadir, darktable.datadir, bufsize);
297 }
dt_loc_get_sharedir(char * sharedir,size_t bufsize)298 void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
299 {
300 g_strlcpy(sharedir, darktable.sharedir, bufsize);
301 }
302 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
303 // vim: shiftwidth=2 expandtab tabstop=2 cindent
304 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
305