Change default location for configuration file

– under Linux and macOS, use the “dynare” subdirectory of the configuration
  directory specified by the XDG specification
– under Windows, use the “dynare” subdirectory of the Application Data folder

The old location is kept for backward compatibility, with a warning.
master
Sébastien Villemot 2023-12-11 19:01:00 +01:00
parent 7c83b81623
commit 328e8eef78
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
3 changed files with 95 additions and 45 deletions

View File

@ -21,7 +21,12 @@
#include <iostream> #include <iostream>
#include <utility> #include <utility>
#ifdef _WIN32
# include <shlobj.h>
#endif
#include "Configuration.hh" #include "Configuration.hh"
#include "DataTree.hh" // For strsplit()
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
@ -109,64 +114,59 @@ Configuration::Configuration(bool parallel_arg, bool parallel_test_arg,
} }
void void
Configuration::getConfigFileInfo(const filesystem::path& conffile_option) Configuration::getConfigFileInfo(const filesystem::path& conffile_option,
WarningConsolidation& warnings)
{ {
using namespace boost; using namespace boost;
ifstream configFile;
if (conffile_option.empty()) filesystem::path config_file {conffile_option};
if (config_file.empty())
{ {
filesystem::path defaultConfigFile; config_file = findConfigFile("dynare.ini");
// Test OS and try to open default file
#if defined(_WIN32) || defined(__CYGWIN32__)
if (auto appdata = getenv("APPDATA"); appdata)
defaultConfigFile = filesystem::path {appdata} / "dynare.ini";
else
{
if (parallel || parallel_test)
cerr << "ERROR: ";
else
cerr << "WARNING: ";
cerr << "APPDATA environment variable not found." << endl;
if (parallel || parallel_test) if (config_file.empty()) // Try old default location (Dynare ⩽ 5) for backward compatibility
exit(EXIT_FAILURE); {
} filesystem::path old_default_config_file;
#ifdef _WIN32
array<wchar_t, MAX_PATH + 1> appdata;
if (SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY, nullptr,
SHGFP_TYPE_CURRENT, appdata.data())
== S_OK)
old_default_config_file = filesystem::path {appdata.data()} / "dynare.ini";
#else #else
if (auto home = getenv("HOME"); home) if (auto home = getenv("HOME"); home)
defaultConfigFile = filesystem::path {home} / ".dynare"; old_default_config_file = filesystem::path {home} / ".dynare";
else
{
if (parallel || parallel_test)
cerr << "ERROR: ";
else
cerr << "WARNING: ";
cerr << "HOME environment variable not found." << endl;
if (parallel || parallel_test)
exit(EXIT_FAILURE);
}
#endif #endif
configFile.open(defaultConfigFile, fstream::in); if (!old_default_config_file.empty() && exists(old_default_config_file))
if (!configFile.is_open())
{
if (parallel || parallel_test)
{ {
cerr << "ERROR: Could not open the default config file (" warnings << "WARNING: the location " << old_default_config_file.string()
<< defaultConfigFile.string() << ")" << endl; << " for the configuration file is obsolete; please see the reference"
exit(EXIT_FAILURE); << " manual for the new location." << endl;
config_file = old_default_config_file;
} }
else
return;
} }
} }
else
if (config_file.empty())
{ {
configFile.open(conffile_option, fstream::in); if (parallel || parallel_test)
if (!configFile.is_open())
{ {
cerr << "ERROR: Couldn't open file " << conffile_option.string() << endl; cerr << "ERROR: the parallel or parallel_test option was passed but no configuration "
<< "file was found" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
else
return;
}
ifstream configFile;
configFile.open(config_file, fstream::in);
if (!configFile.is_open())
{
cerr << "ERROR: Couldn't open configuration file " << config_file.string() << endl;
exit(EXIT_FAILURE);
} }
string name, computerName, port, userName, password, remoteDrive, remoteDirectory, programPath, string name, computerName, port, userName, password, remoteDrive, remoteDirectory, programPath,
@ -812,3 +812,46 @@ Configuration::writeEndParallel(ostream& output) const
<< " closeSlave(options_.parallel,options_.parallel_info.RemoteTmpFolder);" << endl << " closeSlave(options_.parallel,options_.parallel_info.RemoteTmpFolder);" << endl
<< "end" << endl; << "end" << endl;
} }
filesystem::path
Configuration::findConfigFile(const string& filename)
{
#ifdef _WIN32
array<wchar_t, MAX_PATH + 1> appdata;
if (SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_DONT_VERIFY, nullptr, SHGFP_TYPE_CURRENT,
appdata.data())
== S_OK)
{
filesystem::path candidate {filesystem::path {appdata.data()} / "dynare" / filename};
if (exists(candidate))
return candidate;
}
#else
filesystem::path xdg_config_home;
if (auto xdg_config_home_env = getenv("XDG_CONFIG_HOME"); xdg_config_home_env)
xdg_config_home = xdg_config_home_env;
if (auto home = getenv("HOME"); xdg_config_home.empty() && home)
xdg_config_home = filesystem::path {home} / ".config";
if (!xdg_config_home.empty())
{
filesystem::path candidate {xdg_config_home / "dynare" / filename};
if (exists(candidate))
return candidate;
}
string xdg_config_dirs;
if (auto xdg_config_dirs_env = getenv("XDG_CONFIG_DIRS"); xdg_config_dirs_env)
xdg_config_dirs = xdg_config_dirs_env;
if (xdg_config_dirs.empty())
xdg_config_dirs = "/etc/xdg";
for (const auto& dir : DataTree::strsplit(xdg_config_dirs, ':'))
{
filesystem::path candidate {filesystem::path {dir} / "dynare" / filename};
if (exists(candidate))
return candidate;
}
#endif
return {};
}

View File

@ -114,10 +114,17 @@ private:
const string& programPath, const string& programConfig, const string& programPath, const string& programConfig,
const string& matlabOctavePath, bool singleCompThread, const string& matlabOctavePath, bool singleCompThread,
int numberOfThreadsPerJob, const string& operatingSystem); int numberOfThreadsPerJob, const string& operatingSystem);
/* Given a filename (e.g. dynare.ini), looks for it in the configuration directory:
if under Linux or macOS, look into the dynare subdirectory of the XDG
configuration directories (following the default values and the precedence order specified in
the XDG specification)
if under Windows, look into %APPDATA%\dynare\
The returned path will be empty if the file is not found. */
[[nodiscard]] static filesystem::path findConfigFile(const string& filename);
public: public:
//! Parse config file //! Parse config file
void getConfigFileInfo(const filesystem::path& conffile_option); void getConfigFileInfo(const filesystem::path& conffile_option, WarningConsolidation& warnings);
//! Check Pass //! Check Pass
void checkPass(WarningConsolidation& warnings) const; void checkPass(WarningConsolidation& warnings) const;
//! Check Pass //! Check Pass

View File

@ -463,7 +463,7 @@ main(int argc, char** argv)
// Process config file // Process config file
Configuration config {parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec, Configuration config {parallel, parallel_test, parallel_follower_open_mode, parallel_use_psexec,
cluster_name}; cluster_name};
config.getConfigFileInfo(conffile); config.getConfigFileInfo(conffile, warnings);
config.checkPass(warnings); config.checkPass(warnings);
config.transformPass(); config.transformPass();