Rewrite several file path routines to use boost::filesystem

This is a basis for moving .gnucash to a more modern location for
application specific user data (following the xdg spec).
This commit is contained in:
Geert Janssens 2017-08-22 16:33:34 +02:00
parent 2006155985
commit ae75bc963f
26 changed files with 437 additions and 227 deletions

View File

@ -438,7 +438,7 @@ SET (Boost_FIND_QUIETLY ON)
IF (NOT DEFINED ${BOOST_ROOT}) IF (NOT DEFINED ${BOOST_ROOT})
SET(BOOST_ROOT $ENV{BOOST_ROOT}) SET(BOOST_ROOT $ENV{BOOST_ROOT})
ENDIF() ENDIF()
FIND_PACKAGE (Boost 1.54.0 REQUIRED COMPONENTS date_time regex locale) FIND_PACKAGE (Boost 1.54.0 REQUIRED COMPONENTS date_time regex locale filesystem)
IF (Boost_FOUND) IF (Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})

View File

@ -430,7 +430,7 @@ show_session_error (QofBackendError io_error,
fmt = _("You attempted to save in\n%s\nor a subdirectory thereof. " fmt = _("You attempted to save in\n%s\nor a subdirectory thereof. "
"This is not allowed as %s reserves that directory for internal use.\n\n" "This is not allowed as %s reserves that directory for internal use.\n\n"
"Please try again in a different directory."); "Please try again in a different directory.");
gnc_error_dialog (parent, fmt, gnc_dotgnucash_dir(), PACKAGE_NAME); gnc_error_dialog (parent, fmt, gnc_userdata_dir(), PACKAGE_NAME);
break; break;
case ERR_SQL_DB_TOO_OLD: case ERR_SQL_DB_TOO_OLD:
@ -1139,7 +1139,7 @@ check_file_path (const char *path)
{ {
/* Remember the directory as the default. */ /* Remember the directory as the default. */
gchar *dir = g_path_get_dirname(path); gchar *dir = g_path_get_dirname(path);
const gchar *dotgnucash = gnc_dotgnucash_dir(); const gchar *dotgnucash = gnc_userdata_dir();
char *dirpath = dir; char *dirpath = dir;
/* Prevent user from storing file in GnuCash' private configuration /* Prevent user from storing file in GnuCash' private configuration

View File

@ -719,16 +719,16 @@ gnc_gui_init(void)
gnc_window_set_progressbar_window (GNC_WINDOW(main_window)); gnc_window_set_progressbar_window (GNC_WINDOW(main_window));
#ifdef MAC_INTEGRATION #ifdef MAC_INTEGRATION
map = gnc_build_dotgnucash_path(ACCEL_MAP_NAME); map = gnc_build_userdata_path(ACCEL_MAP_NAME);
if (!g_file_test (map, G_FILE_TEST_EXISTS)) if (!g_file_test (map, G_FILE_TEST_EXISTS))
{ {
g_free (map); g_free (map);
data_dir = gnc_path_get_pkgdatadir(); data_dir = gnc_path_get_pkgdatadir();
map = g_build_filename(data_dir, "ui", "osx_accel_map", NULL); map = g_build_filename(data_dir, "ui", "osx_accel_map", NULL);
g_free(data_dir); g_free(data_dir);
} }
#else #else
map = gnc_build_dotgnucash_path(ACCEL_MAP_NAME); map = gnc_build_userdata_path(ACCEL_MAP_NAME);
#endif /* MAC_INTEGRATION */ #endif /* MAC_INTEGRATION */
gtk_accel_map_load(map); gtk_accel_map_load(map);
g_free(map); g_free(map);
@ -765,7 +765,7 @@ gnc_gui_shutdown (void)
if (gnome_is_running && !gnome_is_terminating) if (gnome_is_running && !gnome_is_terminating)
{ {
gnome_is_terminating = TRUE; gnome_is_terminating = TRUE;
map = gnc_build_dotgnucash_path(ACCEL_MAP_NAME); map = gnc_build_userdata_path(ACCEL_MAP_NAME);
gtk_accel_map_save(map); gtk_accel_map_save(map);
g_free(map); g_free(map);
gtk_main_quit(); gtk_main_quit();

View File

@ -799,7 +799,7 @@ pcd_save_custom_data(PrintCheckDialog *pcd, const gchar *title)
pcd->splits_account_x, pcd->splits_account_y); pcd->splits_account_x, pcd->splits_account_y);
filename = g_strconcat(title, CHECK_NAME_EXTENSION, NULL); filename = g_strconcat(title, CHECK_NAME_EXTENSION, NULL);
pathname = g_build_filename(gnc_dotgnucash_dir(), CHECK_FMT_DIR, pathname = g_build_filename(gnc_userdata_dir(), CHECK_FMT_DIR,
filename, NULL); filename, NULL);
if (gnc_key_file_save_to_file(pathname, key_file, &error)) if (gnc_key_file_save_to_file(pathname, key_file, &error))
@ -1558,7 +1558,7 @@ read_formats(PrintCheckDialog *pcd, GtkListStore *store)
g_free(dirname); g_free(dirname);
g_free(pkgdatadir); g_free(pkgdatadir);
dirname = gnc_build_dotgnucash_path(CHECK_FMT_DIR); dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
/* Translators: This is a directory name. It may be presented to /* Translators: This is a directory name. It may be presented to
* the user to indicate that some data file was defined by a * the user to indicate that some data file was defined by a
* user herself. */ * user herself. */
@ -1901,7 +1901,7 @@ read_image (const gchar *filename)
if (!g_file_test(tmp_name, G_FILE_TEST_EXISTS)) if (!g_file_test(tmp_name, G_FILE_TEST_EXISTS))
{ {
g_free(tmp_name); g_free(tmp_name);
dirname = gnc_build_dotgnucash_path(CHECK_FMT_DIR); dirname = gnc_build_userdata_path(CHECK_FMT_DIR);
tmp_name = g_build_filename(dirname, filename, (char *)NULL); tmp_name = g_build_filename(dirname, filename, (char *)NULL);
g_free(dirname); g_free(dirname);
} }

View File

@ -344,7 +344,7 @@ try_load_config_array(const gchar *fns[])
for (i = 0; fns[i]; i++) for (i = 0; fns[i]; i++)
{ {
filename = gnc_build_dotgnucash_path(fns[i]); filename = gnc_build_userdata_path(fns[i]);
if (gfec_try_load(filename)) if (gfec_try_load(filename))
{ {
g_free(filename); g_free(filename);
@ -703,7 +703,7 @@ gnc_log_init()
{ {
gchar *log_config_filename; gchar *log_config_filename;
log_config_filename = gnc_build_dotgnucash_path("log.conf"); log_config_filename = gnc_build_userdata_path("log.conf");
if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS)) if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS))
qof_log_parse_log_config(log_config_filename); qof_log_parse_log_config(log_config_filename);
g_free(log_config_filename); g_free(log_config_filename);

View File

@ -91,7 +91,7 @@
(false-if-exception (false-if-exception
(read))) (read)))
(let* ((pref-filename (gnc-build-dotgnucash-path "qif-accounts-map")) (let* ((pref-filename (gnc-build-userdata-path "qif-accounts-map"))
(results '())) (results '()))
;; Get the user's saved mappings. ;; Get the user's saved mappings.
@ -293,7 +293,7 @@
;; This procedure does all the work. We'll define it, then call it safely. ;; This procedure does all the work. We'll define it, then call it safely.
(define (private-save) (define (private-save)
(with-output-to-file (gnc-build-dotgnucash-path "qif-accounts-map") (with-output-to-file (gnc-build-userdata-path "qif-accounts-map")
(lambda () (lambda ()
(display ";;; qif-accounts-map") (display ";;; qif-accounts-map")
(newline) (newline)

View File

@ -1116,7 +1116,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
GncPluginPage *parent; GncPluginPage *parent;
gboolean use_new; gboolean use_new;
gchar *name; gchar *name;
gchar *saved_reports_path = gnc_build_dotgnucash_path (SAVED_REPORTS_FILE); gchar *saved_reports_path = gnc_build_userdata_path (SAVED_REPORTS_FILE);
gchar *report_save_str = g_strdup_printf ( gchar *report_save_str = g_strdup_printf (
_("Update the current report's saved configuration. " _("Update the current report's saved configuration. "
"The report will be saved in the file %s. "), saved_reports_path); "The report will be saved in the file %s. "), saved_reports_path);

View File

@ -82,7 +82,7 @@
;; Then look in Gnucash's standard report directory. ;; Then look in Gnucash's standard report directory.
;; If no file is found, returns just 'fname' for use in error messages. ;; If no file is found, returns just 'fname' for use in error messages.
;; Note: this has been tested on Linux and Windows Vista so far... ;; Note: this has been tested on Linux and Windows Vista so far...
(let* ((userpath (gnc-build-dotgnucash-path fname)) (let* ((userpath (gnc-build-userdata-path fname))
(syspath (gnc-build-report-path fname))) (syspath (gnc-build-report-path fname)))
; make sure there's a trailing delimiter ; make sure there's a trailing delimiter
(if (access? userpath R_OK) (if (access? userpath R_OK)

View File

@ -279,8 +279,8 @@ gnc_saved_reports_write_internal (const gchar *file, const gchar *contents, gboo
gboolean gnc_saved_reports_backup (void) gboolean gnc_saved_reports_backup (void)
{ {
gboolean success = FALSE; gboolean success = FALSE;
gchar *saved_rpts_path = gnc_build_dotgnucash_path (SAVED_REPORTS_FILE); gchar *saved_rpts_path = gnc_build_userdata_path (SAVED_REPORTS_FILE);
gchar *saved_rpts_bkp_path = g_strconcat (saved_rpts_path, "-backup", NULL); gchar *saved_rpts_bkp_path = gnc_build_userdata_path (SAVED_REPORTS_FILE "-backup");
gchar *contents = NULL; gchar *contents = NULL;
GError *save_error = NULL; GError *save_error = NULL;
@ -310,7 +310,7 @@ gboolean
gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite) gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite)
{ {
gboolean success = FALSE; gboolean success = FALSE;
gchar *saved_rpts_path = gnc_build_dotgnucash_path (SAVED_REPORTS_FILE); gchar *saved_rpts_path = gnc_build_userdata_path (SAVED_REPORTS_FILE);
if (report_def) if (report_def)
{ {

View File

@ -127,7 +127,7 @@
(record-accessor <html-style-sheet> 'style)) (record-accessor <html-style-sheet> 'style))
(define gnc:current-saved-stylesheets (define gnc:current-saved-stylesheets
(gnc-build-dotgnucash-path "stylesheets-2.0")) (gnc-build-userdata-path "stylesheets-2.0"))
(define (gnc:save-style-sheet-options) (define (gnc:save-style-sheet-options)
(let ((port (false-if-exception (let ((port (false-if-exception

View File

@ -30,7 +30,6 @@
(use-modules (gnucash report report-system)) (use-modules (gnucash report report-system))
(use-modules (gnucash app-utils)) (use-modules (gnucash app-utils))
(use-modules (gnucash engine)) (use-modules (gnucash engine))
(use-modules (sw_engine))
(use-modules (gnucash report report-system collectors)) (use-modules (gnucash report report-system collectors))
(use-modules (gnucash report report-system list-extras)) (use-modules (gnucash report report-system list-extras))

View File

@ -228,7 +228,7 @@ error_handler(const char *msg)
} }
gboolean gboolean
gfec_try_load(gchar *fn) gfec_try_load(const gchar *fn)
{ {
g_debug("looking for %s", fn); g_debug("looking for %s", fn);
if (g_file_test(fn, G_FILE_TEST_EXISTS)) if (g_file_test(fn, G_FILE_TEST_EXISTS))

View File

@ -18,6 +18,6 @@ typedef void (*gfec_error_handler)(const char *error_message);
SCM gfec_eval_file(const char *file, gfec_error_handler error_handler); SCM gfec_eval_file(const char *file, gfec_error_handler error_handler);
SCM gfec_eval_string(const char *str, gfec_error_handler error_handler); SCM gfec_eval_string(const char *str, gfec_error_handler error_handler);
SCM gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler); SCM gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler);
gboolean gfec_try_load(gchar *fn); gboolean gfec_try_load(const gchar *fn);
#endif #endif

View File

@ -61,7 +61,7 @@ static gboolean parser_inited = FALSE;
static gchar * static gchar *
gnc_exp_parser_filname (void) gnc_exp_parser_filname (void)
{ {
return gnc_build_dotgnucash_path("expressions-2.0"); return gnc_build_userdata_path("expressions-2.0");
} }
void void

View File

@ -16,7 +16,7 @@ SET (core_utils_SOURCES
binreloc.c binreloc.c
gnc-prefs.c gnc-prefs.c
gnc-environment.c gnc-environment.c
gnc-filepath-utils.c gnc-filepath-utils.cpp
gnc-gkeyfile-utils.c gnc-gkeyfile-utils.c
gnc-glib-utils.c gnc-glib-utils.c
gnc-guile-utils.c gnc-guile-utils.c
@ -117,7 +117,7 @@ SET(core_utils_noinst_HEADERS
) )
SET(core_utils_ALL_SOURCES ${core_utils_SOURCES} ${core_utils_noinst_HEADERS}) SET(core_utils_ALL_SOURCES ${core_utils_SOURCES} ${core_utils_noinst_HEADERS})
SET(core_utils_ALL_LIBRARIES ${GUILE_LDFLAGS} ${GLIB2_LDFLAGS} ${GOBJECT_LDFLAGS} ${GTK_MAC_LDFLAGS}) SET(core_utils_ALL_LIBRARIES ${Boost_LIBRARIES} -lboost_filesystem ${GUILE_LDFLAGS} ${GLIB2_LDFLAGS} ${GOBJECT_LDFLAGS} ${GTK_MAC_LDFLAGS})
SET(core_utils_ALL_INCLUDES SET(core_utils_ALL_INCLUDES
${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/common
${CMAKE_BINARY_DIR}/common ${CMAKE_BINARY_DIR}/common

View File

@ -8,7 +8,7 @@ libgnc_core_utils_la_SOURCES = \
binreloc.c \ binreloc.c \
gnc-prefs.c \ gnc-prefs.c \
gnc-environment.c \ gnc-environment.c \
gnc-filepath-utils.c \ gnc-filepath-utils.cpp \
gnc-gkeyfile-utils.c \ gnc-gkeyfile-utils.c \
gnc-glib-utils.c \ gnc-glib-utils.c \
gnc-guile-utils.c \ gnc-guile-utils.c \
@ -21,7 +21,8 @@ libgnc_core_utils_la_LIBADD = \
${GUILE_LIBS} \ ${GUILE_LIBS} \
${GLIB_LIBS} \ ${GLIB_LIBS} \
${BINRELOC_LIBS} \ ${BINRELOC_LIBS} \
${GTK_MAC_LIBS} ${GTK_MAC_LIBS} \
${BOOST_LDFLAGS} -lboost_filesystem
noinst_HEADERS = \ noinst_HEADERS = \
@ -58,7 +59,8 @@ AM_CPPFLAGS = \
${GLIB_CFLAGS} \ ${GLIB_CFLAGS} \
${GTK_MAC_CFLAGS} \ ${GTK_MAC_CFLAGS} \
-I${top_builddir}/common \ -I${top_builddir}/common \
-I${top_srcdir}/common -I${top_srcdir}/common \
$(BOOST_CPPFLAGS)
gncscmmoddir = ${GNC_SCM_INSTALL_DIR}/gnucash gncscmmoddir = ${GNC_SCM_INSTALL_DIR}/gnucash
gncscmmod_DATA = core-utils.scm gncscmmod_DATA = core-utils.scm

View File

@ -65,8 +65,8 @@ gchar * gnc_path_get_stdreportsdir(void);
%newobject gnc_path_find_localized_html_file; %newobject gnc_path_find_localized_html_file;
gchar * gnc_path_find_localized_html_file(const gchar *); gchar * gnc_path_find_localized_html_file(const gchar *);
%newobject gnc_build_dotgnucash_path; %newobject gnc_build_userdata_path;
gchar * gnc_build_dotgnucash_path(const gchar *); gchar * gnc_build_userdata_path(const gchar *);
gchar * gnc_build_report_path(const gchar *); gchar * gnc_build_report_path(const gchar *);
gchar * gnc_build_stdreports_path(const gchar *); gchar * gnc_build_stdreports_path(const gchar *);

View File

@ -41,7 +41,7 @@
(re-export gnc-path-get-bindir) (re-export gnc-path-get-bindir)
(re-export gnc-path-get-stdreportsdir) (re-export gnc-path-get-stdreportsdir)
(re-export gnc-path-find-localized-html-file) (re-export gnc-path-find-localized-html-file)
(re-export gnc-build-dotgnucash-path) (re-export gnc-build-userdata-path)
(re-export gnc-build-report-path) (re-export gnc-build-report-path)
(re-export gnc-build-stdreports-path) (re-export gnc-build-stdreports-path)
(re-export gnc-utf8?) (re-export gnc-utf8?)

View File

@ -26,6 +26,7 @@
* @author Copyright (c) 2000 Dave Peticolas * @author Copyright (c) 2000 Dave Peticolas
*/ */
extern "C" {
#include "config.h" #include "config.h"
#include <platform.h> #include <platform.h>
@ -55,6 +56,12 @@
#include <glib/gwin32.h> #include <glib/gwin32.h>
#define PATH_MAX MAXPATHLEN #define PATH_MAX MAXPATHLEN
#endif #endif
}
#include <boost/filesystem.hpp>
namespace bfs = boost::filesystem;
namespace bst = boost::system;
/** /**
* Scrubs a filename by changing "strange" chars (e.g. those that are not * Scrubs a filename by changing "strange" chars (e.g. those that are not
@ -104,7 +111,7 @@ check_path_return_if_valid(gchar *path)
* *
* Otherwise, assume that filefrag is a well-formed relative path and * Otherwise, assume that filefrag is a well-formed relative path and
* try to find a file with its path relative to * try to find a file with its path relative to
* \li the current working directory, * \li the current working directory,
* \li the installed system-wide data directory (e.g., /usr/local/share/gnucash), * \li the installed system-wide data directory (e.g., /usr/local/share/gnucash),
* \li the installed system configuration directory (e.g., /usr/local/etc/gnucash), * \li the installed system configuration directory (e.g., /usr/local/etc/gnucash),
* \li or in the user's configuration directory (e.g., $HOME/.gnucash/data) * \li or in the user's configuration directory (e.g., $HOME/.gnucash/data)
@ -164,7 +171,7 @@ gnc_resolve_file_path (const gchar * filefrag)
return fullpath; return fullpath;
/* Look in the users config dir (e.g. $HOME/.gnucash/data) */ /* Look in the users config dir (e.g. $HOME/.gnucash/data) */
fullpath = gnc_build_data_path(filefrag); fullpath = g_strdup(gnc_build_data_path(filefrag));
if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR)) if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR))
return fullpath; return fullpath;
@ -196,7 +203,7 @@ gnc_path_find_localized_html_file_internal (const gchar * file_name)
const gchar *env_doc_path = g_getenv("GNC_DOC_PATH"); const gchar *env_doc_path = g_getenv("GNC_DOC_PATH");
const gchar *default_dirs[] = const gchar *default_dirs[] =
{ {
gnc_build_dotgnucash_path ("html"), gnc_build_userdata_path ("html"),
gnc_path_get_pkgdocdir (), gnc_path_get_pkgdocdir (),
gnc_path_get_pkgdatadir (), gnc_path_get_pkgdatadir (),
NULL NULL
@ -292,190 +299,156 @@ gnc_path_find_localized_html_file (const gchar *file_name)
} }
/* ====================================================================== */ /* ====================================================================== */
/** @brief Check that the supplied directory path exists, is a directory, and /** @brief Check that the supplied directory path exists, is a directory, and
* that the user has adequate permissions to use it. * that the user has adequate permissions to use it.
* *
* @param dirname The path to check * @param dirname The path to check
*/ */
static gboolean static bool
gnc_validate_directory (const gchar *dirname, gboolean create, gchar **msg) gnc_validate_directory (const bfs::path &dirname, bool create)
{ {
GStatBuf statbuf; if (dirname.empty())
gint rc; return false;
*msg = NULL; if (!bfs::exists(dirname) && (!create))
rc = g_stat (dirname, &statbuf); return false;
if (rc)
/* Optionally create directories if they don't exist yet
* Note this will do nothing if the directory and its
* parents already exist, but will fail if the path
* points to a file or a softlink. So it serves as a test
* for that as well.
*/
bfs::create_directories(dirname);
auto d = bfs::directory_entry (dirname);
auto perms = d.status().permissions();
/* On Windows only write permission will be checked.
* So strictly speaking we'd need two error messages here depending
* on the platform. For simplicity this detail is glossed over though. */
if ((perms & bfs::owner_all) != bfs::owner_all)
throw (bfs::filesystem_error(
std::string(_("Insufficient permissions, at least write and access permissions required: "))
+ dirname.c_str(), dirname,
bst::error_code(bst::errc::permission_denied, bst::generic_category())));
return true;
}
static auto usr_conf_dir = bfs::path();
static void
gnc_filepath_init()
{
auto try_home_dir = true;
auto env_var = g_getenv("GNC_DOT_DIR");
if (env_var)
usr_conf_dir = env_var;
if (!usr_conf_dir.empty())
{ {
switch (errno) try
{ {
case ENOENT: gnc_validate_directory(usr_conf_dir, true);
if (create) try_home_dir = false;
{ }
rc = g_mkdir (dirname, catch (const bfs::filesystem_error& ex)
#ifdef G_OS_WIN32 {
0 /* The mode argument is ignored on windows */ g_warning("%s is not a suitable base directory for the user configuration."
#else "Trying home directory instead.\nThe failure is\n%s",
S_IRWXU /* perms = S_IRWXU = 0700 */ usr_conf_dir.c_str(), ex.what());
#endif
);
if (rc)
{
*msg = g_strdup_printf(
_("An error occurred while creating the directory:\n"
" %s\n"
"Please correct the problem and restart GnuCash.\n"
"The reported error was '%s' (errno %d).\n"),
dirname, g_strerror(errno) ? g_strerror(errno) : "", errno);
return FALSE;
}
}
else
{
*msg = g_strdup_printf(
_("Note: the directory\n"
" %s\n"
"doesn't exist. This is however not fatal.\n"),
dirname);
return FALSE;
}
g_stat (dirname, &statbuf);
break;
case EACCES:
*msg = g_strdup_printf(
_("The directory\n"
" %s\n"
"exists but cannot be accessed. This program \n"
"must have full access (read/write/execute) to \n"
"the directory in order to function properly.\n"),
dirname);
return FALSE;
case ENOTDIR:
*msg = g_strdup_printf(
_("The path\n"
" %s\n"
"exists but it is not a directory. Please delete\n"
"the file and start GnuCash again.\n"),
dirname);
return FALSE;
default:
*msg = g_strdup_printf(
_("An unknown error occurred when validating that the\n"
" %s\n"
"directory exists and is usable. Please correct the\n"
"problem and restart GnuCash. The reported error \n"
"was '%s' (errno %d)."),
dirname, g_strerror(errno) ? g_strerror(errno) : "", errno);
return FALSE;
} }
} }
if ((statbuf.st_mode & S_IFDIR) != S_IFDIR) if (try_home_dir)
{ {
*msg = g_strdup_printf( usr_conf_dir = g_get_home_dir();
_("The path\n" try
" %s\n" {
"exists but it is not a directory. Please delete\n" if (!gnc_validate_directory(usr_conf_dir, false))
"the file and start GnuCash again.\n"), usr_conf_dir = g_get_tmp_dir();
dirname); }
return FALSE; catch (const bfs::filesystem_error& ex)
{
g_warning("Cannot find suitable home directory. Using tmp directory instead.\n"
"The failure is\n%s", ex.what());
usr_conf_dir = g_get_tmp_dir();
}
} }
#ifndef G_OS_WIN32 g_assert(!usr_conf_dir.empty());
/* The mode argument is ignored on windows anyway */
if ((statbuf.st_mode & S_IRWXU) != S_IRWXU)
{
*msg = g_strdup_printf(
_("The permissions are wrong on the directory\n"
" %s\n"
"They must be at least 'rwx' for the user.\n"),
dirname);
return FALSE;
}
#endif
return TRUE; usr_conf_dir /= ".gnucash";
if (!gnc_validate_directory(usr_conf_dir, true))
{
g_warning("Cannot find suitable .gnucash directory in home directory. Using tmp directory instead.");
usr_conf_dir = g_get_tmp_dir();
g_assert(!usr_conf_dir.empty());
usr_conf_dir /= ".gnucash";
/* This may throw and end the program! */
gnc_validate_directory(usr_conf_dir, true);
}
/* Since we're in code that is only executed once....
* Note these calls may throw and end the program! */
gnc_validate_directory(usr_conf_dir / "books", true);
gnc_validate_directory(usr_conf_dir / "checks", true);
gnc_validate_directory(usr_conf_dir / "translog", true);
} }
/** @fn const gchar * gnc_dotgnucash_dir () /** @fn const gchar * gnc_userdata_dir ()
* @brief Ensure that the user's configuration directory exists and is minimally populated. * @brief Ensure that the user's configuration directory exists and is minimally populated.
* *
* Note that the default path is $HOME/.gnucash; This can be changed * Note that the default path is $HOME/.gnucash; This can be changed
* by the environment variable $GNC_DOT_DIR. * by the environment variable $GNC_DOT_DIR.
* *
* @return An absolute path to the configuration directory * @return An absolute path to the configuration directory. This string is
* by the gnc_filepath_utils code and should not be freed by the user.
*/ */
const gchar * const gchar *
gnc_dotgnucash_dir (void) gnc_userdata_dir (void)
{ {
static gchar *dotgnucash = NULL; if (usr_conf_dir.empty())
gchar *tmp_dir; gnc_filepath_init();
gchar *errmsg = NULL;
if (dotgnucash) return usr_conf_dir.c_str();
return dotgnucash;
dotgnucash = g_strdup(g_getenv("GNC_DOT_DIR"));
if (!dotgnucash)
{
const gchar *home = g_get_home_dir();
if (!home || !gnc_validate_directory(home, FALSE, &errmsg))
{
g_free(errmsg);
g_warning("Cannot find suitable home directory. Using tmp directory instead.");
home = g_get_tmp_dir();
}
g_assert(home);
dotgnucash = g_build_filename(home, ".gnucash", (gchar *)NULL);
}
if (!gnc_validate_directory(dotgnucash, TRUE, &errmsg))
{
const gchar *tmp = g_get_tmp_dir();
g_free(errmsg);
g_free(dotgnucash);
g_warning("Cannot find suitable .gnucash directory in home directory. Using tmp directory instead.");
g_assert(tmp);
dotgnucash = g_build_filename(tmp, ".gnucash", (gchar *)NULL);
if (!gnc_validate_directory(dotgnucash, TRUE, &errmsg))
exit(1);
}
/* Since we're in code that is only executed once.... */
tmp_dir = g_build_filename(dotgnucash, "books", (gchar *)NULL);
if (!gnc_validate_directory(tmp_dir, TRUE, &errmsg))
exit(1);
g_free(tmp_dir);
tmp_dir = g_build_filename(dotgnucash, "checks", (gchar *)NULL);
if (!gnc_validate_directory(tmp_dir, TRUE, &errmsg))
exit(1);
g_free(tmp_dir);
tmp_dir = g_build_filename(dotgnucash, "translog", (gchar *)NULL);
if (!gnc_validate_directory(tmp_dir, TRUE, &errmsg))
exit(1);
g_free(tmp_dir);
return dotgnucash;
} }
/** @fn gchar * gnc_build_dotgnucash_path (const gchar *filename) static const bfs::path&
gnc_userdata_dir_as_path (void)
{
if (usr_conf_dir.empty())
gnc_filepath_init();
return usr_conf_dir;
}
/** @fn gchar * gnc_build_userdata_path (const gchar *filename)
* @brief Make a path to filename in the user's configuration directory. * @brief Make a path to filename in the user's configuration directory.
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *
gnc_build_dotgnucash_path (const gchar *filename) gnc_build_userdata_path (const gchar *filename)
{ {
return g_build_filename(gnc_dotgnucash_dir(), filename, (gchar *)NULL); return g_strdup((gnc_userdata_dir_as_path() / filename).c_str());
}
static bfs::path
gnc_build_userdata_subdir_path (const gchar *subdir, const gchar *filename)
{
gchar* filename_dup = g_strdup(filename);
scrub_filename(filename_dup);
auto result = (gnc_userdata_dir_as_path() / subdir) / filename_dup;
g_free(filename_dup);
return result;
} }
/** @fn gchar * gnc_build_book_path (const gchar *filename) /** @fn gchar * gnc_build_book_path (const gchar *filename)
@ -483,20 +456,14 @@ gnc_build_dotgnucash_path (const gchar *filename)
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *
gnc_build_book_path (const gchar *filename) gnc_build_book_path (const gchar *filename)
{ {
gchar* filename_dup = g_strdup(filename); return g_strdup(gnc_build_userdata_subdir_path("books", filename).c_str());
gchar* result = NULL;
scrub_filename(filename_dup);
result = g_build_filename(gnc_dotgnucash_dir(), "books",
filename_dup, (gchar *)NULL);
g_free(filename_dup);
return result;
} }
/** @fn gchar * gnc_build_translog_path (const gchar *filename) /** @fn gchar * gnc_build_translog_path (const gchar *filename)
@ -504,20 +471,14 @@ gnc_build_book_path (const gchar *filename)
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *
gnc_build_translog_path (const gchar *filename) gnc_build_translog_path (const gchar *filename)
{ {
gchar* filename_dup = g_strdup(filename); return g_strdup(gnc_build_userdata_subdir_path("translog", filename).c_str());
gchar* result = NULL;
scrub_filename(filename_dup);
result = g_build_filename(gnc_dotgnucash_dir(), "translog",
filename_dup, (gchar *)NULL);
g_free(filename_dup);
return result;
} }
/** @fn gchar * gnc_build_data_path (const gchar *filename) /** @fn gchar * gnc_build_data_path (const gchar *filename)
@ -525,19 +486,14 @@ gnc_build_translog_path (const gchar *filename)
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *
gnc_build_data_path (const gchar *filename) gnc_build_data_path (const gchar *filename)
{ {
gchar* filename_dup = g_strdup(filename); return g_strdup(gnc_build_userdata_subdir_path("data", filename).c_str());
gchar* result;
scrub_filename(filename_dup);
result = g_build_filename(gnc_dotgnucash_dir(), "data", filename_dup, (gchar *)NULL);
g_free(filename_dup);
return result;
} }
/** @fn gchar * gnc_build_report_path (const gchar *filename) /** @fn gchar * gnc_build_report_path (const gchar *filename)
@ -545,7 +501,8 @@ gnc_build_data_path (const gchar *filename)
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *
@ -560,7 +517,8 @@ gnc_build_report_path (const gchar *filename)
* *
* @param filename The name of the file * @param filename The name of the file
* *
* @return An absolute path. * @return An absolute path. The returned string should be freed by the user
* using g_free().
*/ */
gchar * gchar *

View File

@ -75,8 +75,8 @@ gchar *gnc_resolve_file_path (const gchar *filefrag);
*/ */
gchar *gnc_path_find_localized_html_file (const gchar *file_name); gchar *gnc_path_find_localized_html_file (const gchar *file_name);
const gchar *gnc_dotgnucash_dir (void); const gchar *gnc_userdata_dir (void);
gchar *gnc_build_dotgnucash_path (const gchar *filename); gchar *gnc_build_userdata_path (const gchar *filename);
gchar *gnc_build_book_path (const gchar *filename); gchar *gnc_build_book_path (const gchar *filename);
gchar *gnc_build_translog_path (const gchar *filename); gchar *gnc_build_translog_path (const gchar *filename);
gchar *gnc_build_data_path (const gchar *filename); gchar *gnc_build_data_path (const gchar *filename);

View File

@ -14,5 +14,8 @@ ENDMACRO()
ADD_CORE_UTILS_TEST(test-gnc-glib-utils test-gnc-glib-utils.c) ADD_CORE_UTILS_TEST(test-gnc-glib-utils test-gnc-glib-utils.c)
ADD_CORE_UTILS_TEST(test-resolve-file-path test-resolve-file-path.c) ADD_CORE_UTILS_TEST(test-resolve-file-path test-resolve-file-path.c)
ADD_CORE_UTILS_TEST(test-usr-conf-dir test-usr-conf-dir.c)
ADD_CORE_UTILS_TEST(test-usr-conf-dir-invalid-home test-usr-conf-dir-invalid-home.c)
SET_DIST_LIST(test_core_utils_DIST CMakeLists.txt Makefile.am test-gnc-glib-utils.c test-resolve-file-path.c) SET_DIST_LIST(test_core_utils_DIST CMakeLists.txt Makefile.am test-gnc-glib-utils.c
test-resolve-file-path.c test-usr-conf-dir.c test-usr-conf-dir-invalid-home.c)

View File

@ -10,20 +10,24 @@ AM_CPPFLAGS = \
-I${top_srcdir}/libgnucash/core-utils \ -I${top_srcdir}/libgnucash/core-utils \
-I${top_srcdir}/libgnucash/engine \ -I${top_srcdir}/libgnucash/engine \
${GUILE_CFLAGS} \ ${GUILE_CFLAGS} \
${GLIB_CFLAGS} ${GLIB_CFLAGS} \
$(BOOST_CPPFLAGS)
LDADD = \ LDADD = \
../libgnc-core-utils.la \ ../libgnc-core-utils.la \
${top_builddir}/common/test-core/libtest-core.la \ ${top_builddir}/common/test-core/libtest-core.la \
${GLIB_LIBS} ${GLIB_LIBS} \
${BOOST_LDFLAGS}
# these tests are ordered kind more or less in the order # these tests are ordered kind more or less in the order
# that they should be executed, with more basic tests coming first. # that they should be executed, with more basic tests coming first.
# #
TESTS = \ TESTS = \
test-gnc-glib-utils \
test-resolve-file-path \ test-resolve-file-path \
test-gnc-glib-utils test-usr-conf-dir \
test-usr-conf-dir-invalid-home
GNC_TEST_DEPS = \ GNC_TEST_DEPS = \
--library-dir ${top_builddir}/libgnucash/core-utils --library-dir ${top_builddir}/libgnucash/core-utils

View File

@ -29,16 +29,16 @@
#include "test-stuff.h" #include "test-stuff.h"
#include "gnc-filepath-utils.h" #include "gnc-filepath-utils.h"
struct test_strings_struct struct relpath_strings_struct
{ {
char *input; char *input;
char *output; char *output;
int prefix_home; int prefix_home;
}; };
typedef struct test_strings_struct test_strings; typedef struct relpath_strings_struct relpath_strings;
test_strings strs[] = relpath_strings strs[] =
{ {
{ {
G_DIR_SEPARATOR_S ".gnucash" G_DIR_SEPARATOR_S "test-account-name", G_DIR_SEPARATOR_S ".gnucash" G_DIR_SEPARATOR_S "test-account-name",
@ -57,6 +57,18 @@ int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int i; int i;
char *home_dir = NULL;
if (argc > 1)
/* One can pass a homedir on the command line. This
* will most likely cause the test to fail, but it can be
* used to pass invalid home directories manually. The
* test error messages should then show the system's temporary
* directory to be used instead */
home_dir = argv[1];
else
/* Set up a fake home directory to play with */
home_dir = g_dir_make_tmp("gnucashXXXXXX", NULL);
for (i = 0; strs[i].input != NULL; i++) for (i = 0; strs[i].input != NULL; i++)
{ {
@ -66,15 +78,15 @@ main(int argc, char **argv)
if (strs[i].prefix_home == 1) if (strs[i].prefix_home == 1)
{ {
dain = g_build_filename(g_get_home_dir(), strs[i].input, dain = g_build_filename(home_dir, strs[i].input,
(gchar *)NULL); (gchar *)NULL);
wantout = g_build_filename(g_get_home_dir(), strs[i].output, wantout = g_build_filename(home_dir, strs[i].output,
(gchar *)NULL); (gchar *)NULL);
} }
else if (strs[i].prefix_home == 2) else if (strs[i].prefix_home == 2)
{ {
dain = g_strdup(strs[i].input); dain = g_strdup(strs[i].input);
wantout = g_build_filename(g_get_home_dir(), strs[i].output, wantout = g_build_filename(home_dir, strs[i].output,
(gchar *)NULL); (gchar *)NULL);
} }
else else
@ -92,6 +104,7 @@ main(int argc, char **argv)
g_free(wantout); g_free(wantout);
g_free(daout); g_free(daout);
} }
print_test_results(); print_test_results();
return get_rv(); return get_rv();
} }

View File

@ -0,0 +1,110 @@
/***************************************************************************
* test-resolve-file-path.c
*
* Thu Sep 29 22:48:57 2005
* Copyright 2005 GnuCash team
****************************************************************************/
/*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "test-stuff.h"
#include "gnc-filepath-utils.h"
struct usr_confpath_strings_struct
{
int func_num;
char *funcname;
char *output;
};
typedef struct usr_confpath_strings_struct usr_confpath_strings;
usr_confpath_strings strs2[] =
{
{
0, "gnc_build_userdata_path",
".gnucash"
},
{
1, "gnc_build_book_path",
".gnucash" G_DIR_SEPARATOR_S "books"
},
{
2, "gnc_build_translog_path",
".gnucash" G_DIR_SEPARATOR_S "translog"
},
{
3, "gnc_build_data_path",
".gnucash" G_DIR_SEPARATOR_S "data"
},
{ 0, NULL, NULL },
};
int
main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
{
int i;
const char *tmp_dir = g_get_tmp_dir();
/* Run usr conf dir tests with a valid and writable homedir */
g_setenv("HOME", "/notexist", TRUE);
for (i = 0; strs2[i].funcname != NULL; i++)
{
char *daout;
char *wantout;
if (strs2[i].func_num == 0)
{
wantout = g_build_filename(tmp_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_userdata_path("foo");
}
else if (strs2[i].func_num == 1)
{
wantout = g_build_filename(tmp_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_book_path("foo");
}
else if (strs2[i].func_num == 2)
{
wantout = g_build_filename(tmp_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_translog_path("foo");
}
else // if (strs2[i].prefix_home == 3)
{
wantout = g_build_filename(tmp_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_data_path("foo");
}
do_test_args(g_strcmp0(daout, wantout) == 0,
"gnc_build_x_path",
__FILE__, __LINE__,
"%s (%s) vs %s", daout, strs2[i].funcname, wantout);
g_free(wantout);
g_free(daout);
}
print_test_results();
return get_rv();
}

View File

@ -0,0 +1,121 @@
/***************************************************************************
* test-resolve-file-path.c
*
* Thu Sep 29 22:48:57 2005
* Copyright 2005 GnuCash team
****************************************************************************/
/*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "test-stuff.h"
#include "gnc-filepath-utils.h"
struct usr_confpath_strings_struct
{
int func_num;
char *funcname;
char *output;
};
typedef struct usr_confpath_strings_struct usr_confpath_strings;
usr_confpath_strings strs2[] =
{
{
0, "gnc_build_userdata_path",
".gnucash"
},
{
1, "gnc_build_book_path",
".gnucash" G_DIR_SEPARATOR_S "books"
},
{
2, "gnc_build_translog_path",
".gnucash" G_DIR_SEPARATOR_S "translog"
},
{
3, "gnc_build_data_path",
".gnucash" G_DIR_SEPARATOR_S "data"
},
{ 0, NULL, NULL },
};
int
main(int argc, char **argv)
{
int i;
char *home_dir = NULL;
if (argc > 1)
/* One can pass a homedir on the command line. This
* will most likely cause the test to fail, but it can be
* used to pass invalid home directories manually. The
* test error messages should then show the system's temporary
* directory to be used instead */
home_dir = argv[1];
else
/* Set up a fake home directory to play with */
home_dir = g_dir_make_tmp("gnucashXXXXXX", NULL);
/* Run usr conf dir tests with a valid and writable homedir */
g_setenv("HOME", home_dir, TRUE);
for (i = 0; strs2[i].funcname != NULL; i++)
{
char *daout;
char *wantout;
if (strs2[i].func_num == 0)
{
wantout = g_build_filename(home_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_userdata_path("foo");
}
else if (strs2[i].func_num == 1)
{
wantout = g_build_filename(home_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_book_path("foo");
}
else if (strs2[i].func_num == 2)
{
wantout = g_build_filename(home_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_translog_path("foo");
}
else // if (strs2[i].prefix_home == 3)
{
wantout = g_build_filename(home_dir, strs2[i].output, "foo",
(gchar *)NULL);
daout = gnc_build_data_path("foo");
}
do_test_args(g_strcmp0(daout, wantout) == 0,
"gnc_build_x_path",
__FILE__, __LINE__,
"%s (%s) vs %s", daout, strs2[i].funcname, wantout);
g_free(wantout);
g_free(daout);
}
print_test_results();
return get_rv();
}

View File

@ -598,7 +598,7 @@ libgnucash/backend/xml/sixtp-utils.cpp
libgnucash/core-utils/binreloc.c libgnucash/core-utils/binreloc.c
libgnucash/core-utils/core-utils.scm libgnucash/core-utils/core-utils.scm
libgnucash/core-utils/gnc-environment.c libgnucash/core-utils/gnc-environment.c
libgnucash/core-utils/gnc-filepath-utils.c libgnucash/core-utils/gnc-filepath-utils.cpp
libgnucash/core-utils/gnc-gkeyfile-utils.c libgnucash/core-utils/gnc-gkeyfile-utils.c
libgnucash/core-utils/gnc-glib-utils.c libgnucash/core-utils/gnc-glib-utils.c
libgnucash/core-utils/gnc-guile-utils.c libgnucash/core-utils/gnc-guile-utils.c