/*
This file is part of darktable,
Copyright (C) 2012-2020 darktable developers.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see .
*/
/* getpwnam_r availability check */
#if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE \
|| defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__ \
|| defined __NetBSD__ || defined __OpenBSD__
#include "config.h"
#include
#include
#define HAVE_GETPWNAM_R 1
#endif
#ifdef HAVE_CONFIG_H
#include
#endif
#ifdef __APPLE__
#include "osx/osx.h"
#endif
#include "common/grealpath.h"
#include "darktable.h"
#include "file_location.h"
#include "whereami.h"
void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir)
{
// Assemble pathes
char* application_directory = NULL;
int dirname_length;
// calling wai_getExecutablePath twice as recommended in the docs:
// the first call retrieves the length of the path
int length = wai_getExecutablePath(NULL, 0, &dirname_length);
if (length > 0)
{
application_directory = (char*)malloc(length + 1);
// the second call retrieves the path including the executable
wai_getExecutablePath(application_directory, length, &dirname_length);
// strip of the executable name from the path to retrieve the path alone
application_directory[dirname_length] = '\0';
}
dt_print(DT_DEBUG_DEV, "application_directory: %s\n", application_directory);
// set up absolute pathes based on their relative value
dt_loc_init_datadir(application_directory, datadir);
dt_loc_init_plugindir(application_directory, moduledir);
dt_loc_init_localedir(application_directory, localedir);
dt_loc_init_user_config_dir(configdir);
dt_loc_init_user_cache_dir(cachedir);
dt_loc_init_sharedir(application_directory);
dt_loc_init_tmp_dir(tmpdir);
free(application_directory);
}
gchar *dt_loc_get_home_dir(const gchar *user)
{
if(user == NULL || g_strcmp0(user, g_get_user_name()) == 0)
{
const char *home_dir = g_getenv("HOME");
return g_strdup((home_dir != NULL) ? home_dir : g_get_home_dir());
}
#if defined HAVE_GETPWNAM_R
/* if the given username is not the same as the current one, we try
* to retrieve the pw dir from the password file entry */
struct passwd pwd;
struct passwd *result;
#ifdef _SC_GETPW_R_SIZE_MAX
int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if(bufsize < 0)
{
bufsize = 4096;
}
#else
int bufsize = 4096;
#endif
gchar *buffer = g_malloc0_n(bufsize, sizeof(gchar));
if(buffer == NULL)
{
return NULL;
}
getpwnam_r(user, &pwd, buffer, bufsize, &result);
if(result == NULL)
{
g_free(buffer);
return NULL;
}
gchar *dir = g_strdup(pwd.pw_dir);
g_free(buffer);
return dir;
#else
return NULL;
#endif
}
gchar *dt_loc_init_generic(const char *absolute_value, const char *application_directory, const char *default_value)
{
gchar *result = NULL;
gchar *path = NULL;
if(absolute_value)
{
// the only adjustment the absolute path needs is transforming the possible tilde '~' to an absolute path
path = dt_util_fix_path(absolute_value);
}
else
{
// the default_value could be absolute or relative. we decide upon presence of the application_directory.
if(application_directory)
{
// default_value is relative.
// combine basename (application_directory) and relative path (default_value).
gchar complete_path[PATH_MAX] = { 0 };
#if defined(__APPLE__)
char *bundle_path = dt_osx_get_bundle_res_path();
if(bundle_path)
{
/// bundle detected ...
// on a mac inside the bundle the executables are in /Contents/MacOS/
// all other directories are a subdirectory of /Contents/Resources:
// /Contents/Resources/etc
// /Contents/Resources/lib
// /Contents/Resources/share
// so the relative path from the binary directory to the other directories differs to the non-bundle version by
// ../etc -> ../Resources/etc,
// ../lib -> ../Resources/lib,
// ../share -> ../Resources/share,
// So we have to modify the relative default value
// +2: removes the two dots '..'
g_snprintf(complete_path, sizeof(complete_path), "%s/../Resources%s", application_directory, default_value + 2);
}
else
{
/// outside a bundle. Apply standard linux path rules
g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
}
#else
g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
#endif
path = g_strdup(complete_path);
}
else
{
// default_value is absolute
path = g_strdup(default_value);
}
}
// create file if it does not exist
if(g_file_test(path, G_FILE_TEST_EXISTS) == FALSE) g_mkdir_with_parents(path, 0700);
// removes '.', '..', and extra '/' characters.
result = g_realpath(path);
g_free(path);
return result;
}
void dt_loc_init_user_config_dir(const char *configdir)
{
char *default_config_dir = g_build_filename(g_get_user_config_dir(), "darktable", NULL);
darktable.configdir = dt_loc_init_generic(configdir, NULL, default_config_dir);
dt_check_opendir("darktable.configdir", darktable.configdir);
g_free(default_config_dir);
}
void dt_loc_init_tmp_dir(const char *tmpdir)
{
darktable.tmpdir = dt_loc_init_generic(tmpdir, NULL, g_get_tmp_dir());
dt_check_opendir("darktable.tmpdir", darktable.tmpdir);
}
void dt_loc_init_user_cache_dir(const char *cachedir)
{
char *default_cache_dir = g_build_filename(g_get_user_cache_dir(), "darktable", NULL);
darktable.cachedir = dt_loc_init_generic(cachedir, NULL, default_cache_dir);
dt_check_opendir("darktable.cachedir", darktable.cachedir);
g_free(default_cache_dir);
}
void dt_loc_init_plugindir(const char* application_directory, const char *plugindir)
{
darktable.plugindir = dt_loc_init_generic(plugindir, application_directory, DARKTABLE_LIBDIR);
dt_check_opendir("darktable.plugindir", darktable.plugindir);
}
void dt_check_opendir(const char* context, const char* directory)
{
if (!directory)
{
fprintf(stderr, "directory for %s has not been set.\n", context);
exit(EXIT_FAILURE);
}
#if _WIN32
wchar_t *wdirectory = g_utf8_to_utf16 (directory, -1, NULL, NULL, NULL);
DWORD attribs = GetFileAttributesW(wdirectory);
g_free(wdirectory);
if (attribs != INVALID_FILE_ATTRIBUTES &&
(attribs & FILE_ATTRIBUTE_DIRECTORY))
{
dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
}
else
{
fprintf(stderr, "%s: directory '%s' fails to open.'\n", context, directory);
exit(EXIT_FAILURE);
}
#else
DIR* dir = opendir(directory);
if (dir)
{
dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
closedir(dir);
}
else
{
fprintf(stderr, "opendir '%s' fails with: '%s'\n", directory, strerror(errno));
exit(EXIT_FAILURE);
}
#endif
}
void dt_loc_init_localedir(const char* application_directory, const char *localedir)
{
darktable.localedir = dt_loc_init_generic(localedir, application_directory, DARKTABLE_LOCALEDIR);
dt_check_opendir("darktable.localedir", darktable.localedir);
}
void dt_loc_init_datadir(const char* application_directory, const char *datadir)
{
darktable.datadir = dt_loc_init_generic(datadir, application_directory, DARKTABLE_DATADIR);
dt_check_opendir("darktable.datadir", darktable.datadir);
}
void dt_loc_init_sharedir(const char* application_directory)
{
darktable.sharedir = dt_loc_init_generic(NULL, application_directory, DARKTABLE_SHAREDIR);
dt_check_opendir("darktable.sharedir", darktable.sharedir);
}
void dt_loc_get_kerneldir(char *kerneldir, size_t bufsize)
{
char datadir[PATH_MAX] = { 0 };
dt_loc_get_datadir(datadir, sizeof(datadir));
snprintf(kerneldir, bufsize, "%s" G_DIR_SEPARATOR_S "kernels", datadir);
}
void dt_loc_get_plugindir(char *plugindir, size_t bufsize)
{
g_strlcpy(plugindir, darktable.plugindir, bufsize);
}
void dt_loc_get_localedir(char *localedir, size_t bufsize)
{
g_strlcpy(localedir, darktable.localedir, bufsize);
}
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
{
g_strlcpy(configdir, darktable.configdir, bufsize);
}
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
{
g_strlcpy(cachedir, darktable.cachedir, bufsize);
}
void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
{
g_strlcpy(tmpdir, darktable.tmpdir, bufsize);
}
void dt_loc_get_datadir(char *datadir, size_t bufsize)
{
g_strlcpy(datadir, darktable.datadir, bufsize);
}
void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
{
g_strlcpy(sharedir, darktable.sharedir, bufsize);
}
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;