Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
file_location.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2014, 2016-2017 Tobias Ellinghaus.
4 Copyright (C) 2011 Henrik Andersson.
5 Copyright (C) 2011 johannes hanika.
6 Copyright (C) 2011 Kanstantsin Shautsou.
7 Copyright (C) 2011 Moritz Lipp.
8 Copyright (C) 2012 Christian Tellefsen.
9 Copyright (C) 2012 Jean-Sébastien Pédron.
10 Copyright (C) 2012 Jérémy Rosen.
11 Copyright (C) 2012 Richard Wonka.
12 Copyright (C) 2013 Simon Spannagel.
13 Copyright (C) 2014, 2016 Roman Lebedev.
14 Copyright (C) 2016 Pedro Côrte-Real.
15 Copyright (C) 2016-2017 Peter Budai.
16 Copyright (C) 2018 parafin.
17 Copyright (C) 2019 Philippe Weyland.
18 Copyright (C) 2020-2021 David-Tillmann Schaefer.
19 Copyright (C) 2020-2021 Pascal Obry.
20 Copyright (C) 2022, 2025-2026 Aurélien PIERRE.
21 Copyright (C) 2022 Martin Bařinka.
22 Copyright (C) 2023 Alynx Zhou.
23 Copyright (C) 2026 Guillaume Stutin.
24
25 darktable is free software: you can redistribute it and/or modify
26 it under the terms of the GNU General Public License as published by
27 the Free Software Foundation, either version 3 of the License, or
28 (at your option) any later version.
29
30 darktable is distributed in the hope that it will be useful,
31 but WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 GNU General Public License for more details.
34
35 You should have received a copy of the GNU General Public License
36 along with darktable. If not, see <http://www.gnu.org/licenses/>.
37*/
38
39/* getpwnam_r availability check */
40#if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE \
41 || defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__ \
42 || defined __NetBSD__ || defined __OpenBSD__
43#include "config.h"
44
45#include <pwd.h>
46#define HAVE_GETPWNAM_R 1
47#endif
48
49#ifdef HAVE_CONFIG_H
50#include <config.h>
51#endif
52
53#ifdef __APPLE__
54#include "osx/osx.h"
55#endif
56
57#include "common/grealpath.h"
58#include "common/paths.h" // DT_PATH_MAX
59#include "darktable.h"
60#include "file_location.h"
61#include "whereami.h"
62#include "system/macros.h"
63#include "system/mem_alloc.h"
64#include "common/logging.h"
65#include "common/utility.h"
66
67// The directory of the running executable, to free, or NULL when it cannot be found.
68static char *_application_directory(void)
69{
70 char *application_directory = NULL;
71 int dirname_length;
72 // calling wai_getExecutablePath twice as recommended in the docs:
73 // the first call retrieves the length of the path
74 int length = wai_getExecutablePath(NULL, 0, &dirname_length);
75 if (length > 0)
76 {
77 application_directory = (char*)malloc(length + 1);
78 if(IS_NULL_PTR(application_directory)) return NULL;
79 // the second call retrieves the path including the executable
80 wai_getExecutablePath(application_directory, length, &dirname_length);
81 // strip of the executable name from the path to retrieve the path alone
82 application_directory[dirname_length] = '\0';
83 }
84 return application_directory;
85}
86
87void dt_loc_resolve_localedir(char *localedir, size_t bufsize)
88{
89 char *application_directory = _application_directory();
90 gchar *path = dt_loc_init_generic(NULL, application_directory, DARKTABLE_LOCALEDIR);
91 g_strlcpy(localedir, path ? path : "", bufsize);
92 dt_free(path);
93 dt_free(application_directory);
94}
95
96void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir, const char *kerneldir)
97{
98 // Assemble pathes
99 char *application_directory = _application_directory();
100 dt_print(DT_DEBUG_DEV, "application_directory: %s\n", application_directory);
101
102 // set up absolute pathes based on their relative value
103 dt_loc_init_datadir(application_directory, datadir);
104 dt_loc_init_moduledir(application_directory, moduledir);
105 dt_loc_init_localedir(application_directory, localedir);
106 dt_loc_init_kerneldir(application_directory, kerneldir);
109 dt_loc_init_sharedir(application_directory);
110 dt_loc_init_tmp_dir(tmpdir);
111 dt_free(application_directory);
112}
113
114gchar *dt_loc_get_home_dir(const gchar *user)
115{
116 if(IS_NULL_PTR(user) || g_strcmp0(user, g_get_user_name()) == 0)
117 {
118 const char *home_dir = g_getenv("HOME");
119 return g_strdup((!IS_NULL_PTR(home_dir)) ? home_dir : g_get_home_dir());
120 }
121
122#if defined HAVE_GETPWNAM_R
123 /* if the given username is not the same as the current one, we try
124 * to retrieve the pw dir from the password file entry */
125 struct passwd pwd;
126 struct passwd *result;
127#ifdef _SC_GETPW_R_SIZE_MAX
128 int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
129 if(bufsize < 0)
130 {
131 bufsize = 4096;
132 }
133#else
134 int bufsize = 4096;
135#endif
136
137 gchar *buffer = g_malloc0_n(bufsize, sizeof(gchar));
138 if(IS_NULL_PTR(buffer))
139 {
140 return NULL;
141 }
142
143 getpwnam_r(user, &pwd, buffer, bufsize, &result);
144 if(IS_NULL_PTR(result))
145 {
146 dt_free(buffer);
147 return NULL;
148 }
149
150 gchar *dir = g_strdup(pwd.pw_dir);
151 dt_free(buffer);
152
153 return dir;
154#else
155 return NULL;
156#endif
157}
158
159gchar *dt_loc_init_generic(const char *absolute_value, const char *application_directory, const char *default_value)
160{
161 gchar *result = NULL;
162 gchar *path = NULL;
163
164 if(absolute_value)
165 {
166 // the only adjustment the absolute path needs is transforming the possible tilde '~' to an absolute path
167 path = dt_util_fix_path(absolute_value);
168 }
169 else
170 {
171 // the default_value could be absolute or relative. we decide upon presence of the application_directory.
172 if(application_directory)
173 {
174 // default_value is relative.
175 // combine basename (application_directory) and relative path (default_value).
176 gchar complete_path[DT_PATH_MAX] = { 0 };
177#if defined(__APPLE__)
178 char *bundle_path = dt_osx_get_bundle_res_path();
179 if(bundle_path)
180 {
182
183 // on a mac inside the bundle the executables are in <bundleroot>/Contents/MacOS/
184 // all other directories are a subdirectory of <bundleroot>/Contents/Resources:
185 // <bundleroot>/Contents/Resources/etc
186 // <bundleroot>/Contents/Resources/lib
187 // <bundleroot>/Contents/Resources/share
188 // so the relative path from the binary directory to the other directories differs to the non-bundle version by
189 // ../etc -> ../Resources/etc,
190 // ../lib -> ../Resources/lib,
191 // ../share -> ../Resources/share,
192 // So we have to modify the relative default value
193
194 // +2: removes the two dots '..'
195 g_snprintf(complete_path, sizeof(complete_path), "%s/../Resources%s", application_directory, default_value + 2);
196 }
197 else
198 {
200 g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
201 }
202#else
203 g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
204#endif
205 path = g_strdup(complete_path);
206 }
207 else
208 {
209 // default_value is absolute
210 path = g_strdup(default_value);
211 }
212 }
213
214 // create file if it does not exist
215 if(g_file_test(path, G_FILE_TEST_EXISTS) == FALSE) g_mkdir_with_parents(path, 0700);
216
217 // removes '.', '..', and extra '/' characters.
218 result = g_realpath(path);
219
220 dt_free(path);
221 return result;
222}
223
224void dt_loc_init_user_config_dir(const char *configdir)
225{
226 char *default_config_dir = g_build_filename(g_get_user_config_dir(), "ansel", NULL);
227 darktable.configdir = dt_loc_init_generic(configdir, NULL, default_config_dir);
228 dt_check_opendir("ansel.configdir", darktable.configdir);
229 dt_free(default_config_dir);
230}
231
232void dt_loc_init_tmp_dir(const char *tmpdir)
233{
234 darktable.tmpdir = dt_loc_init_generic(tmpdir, NULL, g_get_tmp_dir());
235 dt_check_opendir("ansel.tmpdir", darktable.tmpdir);
236}
237
239{
240 /* XDG first, on every platform: it is what a caller setting it expects, and GLib honours it on
241 * Windows too. Absolute only -- the specification says a relative path in one of these variables
242 * is invalid and must be ignored, and taking one literally would put the cache somewhere that
243 * depends on the working directory. */
244 const gchar *xdg = g_getenv("XDG_CACHE_HOME");
245 if(!IS_NULL_PTR(xdg) && xdg[0] && g_path_is_absolute(xdg)) return g_build_filename(xdg, "ansel", NULL);
246
247#ifdef _WIN32
248 /* NOT g_get_user_cache_dir(): on Windows that is FOLDERID_InternetCache -- the shell folder
249 * still labelled "Temporary Internet Files" -- a shell-managed container for the browser cache,
250 * not a general-purpose per-user cache. Storage Sense empties it whenever it runs, of every file
251 * not WRITTEN in the last week or so however recently it was read, so a thumbnail cache -- written
252 * once, read for months -- is deleted behind the user's back. It is also Hidden and System, so
253 * Explorer does not show it even with hidden files shown, and a log file written there is
254 * unfindable for a user who goes looking. Measured: doc/image-mipmap-cache.md.
255 *
256 * %LOCALAPPDATA% is read directly rather than through g_get_user_data_dir(), which resolves to
257 * the same folder but takes XDG_DATA_HOME first: that variable says where DATA goes and must not
258 * steer the cache. g_get_user_data_dir() is the fallback for the case where the variable is
259 * missing, which is a broken environment rather than a supported one.
260 *
261 * cache\ansel, not ansel\cache: %LOCALAPPDATA%\ansel is the CONFIG folder (g_get_user_config_dir()
262 * is %LOCALAPPDATA% on Windows), and a cache nested in it would go with every backup or deletion of
263 * anselrc and library.db. Beside it instead, as ~/.cache/ansel sits beside ~/.config/ansel on Linux;
264 * %LOCALAPPDATA%\cache is also Qt's QStandardPaths::GenericCacheLocation on Windows. */
265 const gchar *local_app_data = g_getenv("LOCALAPPDATA");
266 if(IS_NULL_PTR(local_app_data) || !local_app_data[0]) local_app_data = g_get_user_data_dir();
267 return g_build_filename(local_app_data, "cache", "ansel", NULL);
268#else
269 /* NOT g_get_user_cache_dir() either: GLib takes XDG_CACHE_HOME literally whenever it is non-empty,
270 * relative or not, so the value rejected above would come straight back through it -- and GLib
271 * memoises its first answer, so whether it did would depend on who asked first. ~/.cache is what
272 * it answers for an unset or empty variable, the only other cases that reach this line. */
273 return g_build_filename(g_get_home_dir(), ".cache", "ansel", NULL);
274#endif
275}
276
277void dt_loc_init_user_cache_dir(const char *cachedir)
278{
279 char *default_cache_dir = dt_loc_default_user_cache_dir();
280 darktable.cachedir = dt_loc_init_generic(cachedir, NULL, default_cache_dir);
281 dt_check_opendir("ansel.cachedir", darktable.cachedir);
282 dt_free(default_cache_dir);
283}
284
285void dt_loc_init_moduledir(const char* application_directory, const char *moduledir)
286{
287 darktable.moduledir = dt_loc_init_generic(moduledir, application_directory, DARKTABLE_MODULEDIR);
288 dt_check_opendir("ansel.moduledir", darktable.moduledir);
289}
290
291void dt_check_opendir(const char* context, const char* directory)
292{
294 {
295 fprintf(stderr, "directory for %s has not been set.\n", context);
296 exit(EXIT_FAILURE);
297 }
298
299#if _WIN32
300 wchar_t *wdirectory = g_utf8_to_utf16 (directory, -1, NULL, NULL, NULL);
301 DWORD attribs = GetFileAttributesW(wdirectory);
302 dt_free(wdirectory);
303 if (attribs != INVALID_FILE_ATTRIBUTES &&
304 (attribs & FILE_ATTRIBUTE_DIRECTORY))
305 {
306 dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
307 }
308 else
309 {
310 fprintf(stderr, "%s: directory '%s' fails to open.'\n", context, directory);
311 exit(EXIT_FAILURE);
312 }
313#else
314 DIR* dir = opendir(directory);
315 if (dir)
316 {
317 dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
318 closedir(dir);
319 }
320 else
321 {
322 fprintf(stderr, "opendir '%s' fails with: '%s'\n", directory, strerror(errno));
323 exit(EXIT_FAILURE);
324 }
325#endif
326}
327
328void dt_loc_init_localedir(const char* application_directory, const char *localedir)
329{
330 darktable.localedir = dt_loc_init_generic(localedir, application_directory, DARKTABLE_LOCALEDIR);
331 dt_check_opendir("ansel.localedir", darktable.localedir);
332}
333
334void dt_loc_init_datadir(const char* application_directory, const char *datadir)
335{
336 darktable.datadir = dt_loc_init_generic(datadir, application_directory, DARKTABLE_DATADIR);
337 dt_check_opendir("ansel.datadir", darktable.datadir);
338}
339
340void dt_loc_init_sharedir(const char* application_directory)
341{
342 darktable.sharedir = dt_loc_init_generic(NULL, application_directory, DARKTABLE_SHAREDIR);
343 dt_check_opendir("ansel.sharedir", darktable.sharedir);
344}
345
346void dt_loc_init_kerneldir(const char* application_directory, const char *kerneldir)
347{
348#ifdef HAVE_OPENCL
349 darktable.kerneldir = dt_loc_init_generic(kerneldir, application_directory, DARKTABLE_KERNELSDIR);
350 dt_check_opendir("ansel.kerneldir", darktable.kerneldir);
351#else
352 darktable.kerneldir = NULL;
353#endif
354}
355
356const char *dt_loc_datadir(void)
357{
358 return darktable.datadir;
359}
360
361const char *dt_loc_sharedir(void)
362{
363 return darktable.sharedir;
364}
365
366const char *dt_loc_moduledir(void)
367{
368 return darktable.moduledir;
369}
370
371const char *dt_loc_localedir(void)
372{
373 return darktable.localedir;
374}
375
376const char *dt_loc_tmpdir(void)
377{
378 return darktable.tmpdir;
379}
380
381const char *dt_loc_configdir(void)
382{
383 return darktable.configdir;
384}
385
386const char *dt_loc_cachedir(void)
387{
388 return darktable.cachedir;
389}
390
391const char *dt_loc_kerneldir(void)
392{
393 return darktable.kerneldir;
394}
395
396void dt_loc_get_kerneldir(char *kerneldir, size_t bufsize)
397{
398 g_strlcpy(kerneldir, dt_loc_kerneldir(), bufsize);
399}
400
401void dt_loc_get_moduledir(char *moduledir, size_t bufsize)
402{
403 g_strlcpy(moduledir, dt_loc_moduledir(), bufsize);
404}
405
406void dt_loc_get_localedir(char *localedir, size_t bufsize)
407{
408 g_strlcpy(localedir, dt_loc_localedir(), bufsize);
409}
410
411void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
412{
413 g_strlcpy(configdir, dt_loc_configdir(), bufsize);
414}
415void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
416{
417 g_strlcpy(cachedir, dt_loc_cachedir(), bufsize);
418}
419void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
420{
421 g_strlcpy(tmpdir, dt_loc_tmpdir(), bufsize);
422}
423void dt_loc_get_datadir(char *datadir, size_t bufsize)
424{
425 g_strlcpy(datadir, dt_loc_datadir(), bufsize);
426}
427void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
428{
429 g_strlcpy(sharedir, dt_loc_sharedir(), bufsize);
430}
431// clang-format off
432// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
433// vim: shiftwidth=2 expandtab tabstop=2 cindent
434// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
435// clang-format on
#define FALSE
Definition ashift_lsd.c:158
#define DARKTABLE_LOCALEDIR
#define DARKTABLE_KERNELSDIR
#define DARKTABLE_DATADIR
#define DARKTABLE_SHAREDIR
#define DARKTABLE_MODULEDIR
darktable_t darktable
Definition darktable.c:213
gchar * directory
documentation root given after –doc, or NULL
gchar * dt_loc_default_user_cache_dir(void)
Resolve the default user cache directory the way dt_loc_init_user_cache_dir() does,...
void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
void dt_loc_init_tmp_dir(const char *tmpdir)
const char * dt_loc_moduledir(void)
const char * dt_loc_sharedir(void)
void dt_loc_init_moduledir(const char *application_directory, const char *moduledir)
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
const char * dt_loc_datadir(void)
const char * dt_loc_cachedir(void)
void dt_loc_get_kerneldir(char *kerneldir, size_t bufsize)
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_localedir(char *localedir, size_t bufsize)
gchar * dt_loc_init_generic(const char *absolute_value, const char *application_directory, const char *default_value)
void dt_loc_init_datadir(const char *application_directory, const char *datadir)
void dt_loc_init_user_cache_dir(const char *cachedir)
void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir, const char *kerneldir)
const char * dt_loc_kerneldir(void)
void dt_loc_get_moduledir(char *moduledir, size_t bufsize)
void dt_loc_init_kerneldir(const char *application_directory, const char *kerneldir)
void dt_loc_init_localedir(const char *application_directory, const char *localedir)
const char * dt_loc_tmpdir(void)
void dt_loc_init_sharedir(const char *application_directory)
static char * _application_directory(void)
const char * dt_loc_configdir(void)
void dt_loc_init_user_config_dir(const char *configdir)
void dt_loc_resolve_localedir(char *localedir, size_t bufsize)
Resolve the locale directory the way dt_loc_init() does, creating it if missing, but set no global an...
gchar * dt_loc_get_home_dir(const gchar *user)
void dt_check_opendir(const char *context, const char *directory)
const char * dt_loc_localedir(void)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
static gchar * g_realpath(const char *path)
Definition grealpath.h:49
@ DT_DEBUG_DEV
Definition logging.h:53
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * dt_osx_get_bundle_res_path()
Definition osx.mm:139
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
char * tmpdir
Definition darktable.h:219
char * cachedir
Definition darktable.h:221
char * sharedir
Definition darktable.h:216
char * moduledir
Definition darktable.h:217
char * configdir
Definition darktable.h:220
char * datadir
Definition darktable.h:215
char * localedir
Definition darktable.h:218
char * kerneldir
Definition darktable.h:222
gchar * dt_util_fix_path(const gchar *path)
Definition utility.c:249