From ab1220b6634a6dd559cf2490a5acc884ea252d9a Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 8 Nov 2021 16:17:08 +0100 Subject: [PATCH] App: code refactoring in Application::ExtractUserPath() --- src/App/Application.cpp | 315 ++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 161 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index d47e10666e..1db585f346 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2807,82 +2807,86 @@ ostream& operator<<(ostream& os, const vector& v) return os; } -void Application::ExtractUserPath() +namespace { + +boost::filesystem::path stringToPath(std::string str) { - // std paths - mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; - mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; +#if defined(FC_OS_WIN32) + std::wstring_convert> converter; + boost::filesystem::path path(converter.from_bytes(str)); +#else + boost::filesystem::path path(str); +#endif + return path; +} - // Set application tmp. directory - mConfig["AppTempPath"] = Base::FileInfo::getTempPath(); +std::string pathToString(const boost::filesystem::path& p) +{ +#if defined(FC_OS_WIN32) + std::wstring_convert> converter; + return converter.to_bytes(p.wstring()); +#else + return p.string(); +#endif +} - // this is to support a portable version of FreeCAD - QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); - QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME")); - QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA")); - QString userTemp = env.value(QString::fromLatin1("FREECAD_USER_TEMP")); - - // verify env. variables - if (!userHome.isEmpty()) { - QDir dir(userHome); - if (dir.exists()) - userHome = QDir::toNativeSeparators(dir.canonicalPath()); - else - userHome.clear(); - } - - if (!userData.isEmpty()) { - QDir dir(userData); - if (dir.exists()) - userData = QDir::toNativeSeparators(dir.canonicalPath()); - else - userData.clear(); - } - else if (!userHome.isEmpty()) { - // if FREECAD_USER_HOME is set but not FREECAD_USER_DATA - userData = userHome; - } - - // override temp directory if set by env. variable - if (!userTemp.isEmpty()) { - QDir dir(userTemp); - if (dir.exists()) { - userTemp = dir.canonicalPath(); - userTemp += QDir::separator(); - userTemp = QDir::toNativeSeparators(userTemp); - mConfig["AppTempPath"] = userTemp.toUtf8().data(); - } - } - else if (!userHome.isEmpty()) { - // if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP - QDir dir(userHome); - dir.mkdir(QString::fromLatin1("temp")); - QFileInfo fi(dir, QString::fromLatin1("temp")); - QString tmp(fi.absoluteFilePath()); - tmp += QDir::separator(); - tmp = QDir::toNativeSeparators(tmp); - mConfig["AppTempPath"] = tmp.toUtf8().data(); - } - -#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) +QString getDefaultHome() +{ + QString path; +#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) || defined(FC_OS_MACOSX) // Default paths for the user specific stuff struct passwd *pwd = getpwuid(getuid()); - if (pwd == NULL) + if (!pwd) + throw Base::RuntimeError("Getting HOME path from system failed!"); + path = QString::fromUtf8(pwd->pw_dir); + +#elif defined(FC_OS_WIN32) + WCHAR szPath[MAX_PATH]; + std::wstring_convert> converter; + // Get the default path where we can save our documents. It seems that + // 'CSIDL_MYDOCUMENTS' doesn't work on all machines, so we use 'CSIDL_PERSONAL' + // which does the same. + if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) { + path = QString::fromStdString(converter.to_bytes(szPath)); + } + else { throw Base::RuntimeError("Getting HOME path from system failed!"); - mConfig["UserHomePath"] = pwd->pw_dir; - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); } - boost::filesystem::path appData(pwd->pw_dir); - if (!userData.isEmpty()) - appData = userData.toUtf8().data(); +#else +# error "Implement getDefaultHome() for your platform." +#endif - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); + return path; +} + +QString getDefaultUserData(const QString& home) +{ +#if defined(FC_OS_WIN32) + WCHAR szPath[MAX_PATH]; + std::wstring_convert> converter; + if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) { + return QString::fromStdString(converter.to_bytes(szPath)); } +#endif + return home; +} + +void getUserData(boost::filesystem::path& appData) +{ +#if defined(FC_OS_MACOSX) + appData = appData / "Library" / "Preferences"; +#else + Q_UNUSED(appData) +#endif +} + +void getApplicationData(std::map& mConfig, boost::filesystem::path& appData) +{ + // Actually the name of the directory where the parameters are stored should be the name of + // the application due to branding reasons. +#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of // the path. if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { @@ -2892,52 +2896,83 @@ void Application::ExtractUserPath() appData /= "." + mConfig["ExeName"]; } - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - - // In order to write to our data path, we must create some directories, first. - if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(appData); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); - } - } - - mConfig["UserAppData"] = appData.string() + PATHSEP; - -#elif defined(FC_OS_MACOSX) - // Default paths for the user specific stuff on the platform - struct passwd *pwd = getpwuid(getuid()); - if (pwd == NULL) - throw Base::RuntimeError("Getting HOME path from system failed!"); - mConfig["UserHomePath"] = pwd->pw_dir; - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); - } - - boost::filesystem::path appData(pwd->pw_dir); - if (!userData.isEmpty()) - appData = userData.toUtf8().data(); - - appData = appData / "Library" / "Preferences"; - - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); - } - +#elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32) // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of // the path. if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { appData /= mConfig["ExeVendor"]; } appData /= mConfig["ExeName"]; +#endif +} +} + +void Application::ExtractUserPath() +{ + // std paths + mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP; + mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP; + + // this is to support a portable version of FreeCAD + QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); + QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME")); + QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA")); + QString userTemp = env.value(QString::fromLatin1("FREECAD_USER_TEMP")); + + auto toNativePath = [](QString& path) { + if (!path.isEmpty()) { + QDir dir(path); + if (dir.exists()) + path = QDir::toNativeSeparators(dir.canonicalPath()); + else + path.clear(); + } + }; + + // verify env. variables + toNativePath(userHome); + toNativePath(userData); + toNativePath(userTemp); + + // if FREECAD_USER_HOME is set but not FREECAD_USER_DATA + if (!userHome.isEmpty() && userData.isEmpty()) { + userData = userHome; + } + + // if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP + if (!userHome.isEmpty() && userTemp.isEmpty()) { + QDir dir(userHome); + dir.mkdir(QString::fromLatin1("temp")); + QFileInfo fi(dir, QString::fromLatin1("temp")); + userTemp = fi.absoluteFilePath(); + } - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - + // User home path + // + if (userHome.isEmpty()) { + userHome = getDefaultHome(); + } + mConfig["UserHomePath"] = userHome.toUtf8().data(); + + + // User data path + // + if (userData.isEmpty()) { + userData = getDefaultUserData(userHome); + } + boost::filesystem::path appData(stringToPath(userData.toStdString())); + getUserData(appData); + if (!boost::filesystem::exists(appData)) { + // This should never ever happen + throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); + } + + // In the second step we want the directory where user settings of the application can be + // kept. There we create a directory with name of the vendor and a sub-directory with name + // of the application. + getApplicationData(mConfig, appData); + // In order to write to our data path, we must create some directories, first. if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { try { @@ -2947,72 +2982,30 @@ void Application::ExtractUserPath() } } - mConfig["UserAppData"] = appData.string() + PATHSEP; + mConfig["UserAppData"] = pathToString(appData) + PATHSEP; -#elif defined(FC_OS_WIN32) - WCHAR szPath[MAX_PATH]; - std::wstring_convert> converter; - // Get the default path where we can save our documents. It seems that - // 'CSIDL_MYDOCUMENTS' doesn't work on all machines, so we use 'CSIDL_PERSONAL' - // which does the same. - if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) { - mConfig["UserHomePath"] = converter.to_bytes(szPath); + + // Set application tmp. directory + // + if (!userTemp.isEmpty()) { + userTemp += QDir::separator(); + mConfig["AppTempPath"] = userTemp.toUtf8().data(); } else { - mConfig["UserHomePath"] = mConfig["AppHomePath"]; + mConfig["AppTempPath"] = Base::FileInfo::getTempPath(); } - if (!userHome.isEmpty()) { - mConfig["UserHomePath"] = userHome.toUtf8().data(); - } - // In the second step we want the directory where user settings of the application can be - // kept. There we create a directory with name of the vendor and a sub-directory with name - // of the application. - if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) { - boost::filesystem::path appData(szPath); - if (!userData.isEmpty()) - appData = userData.toStdWString(); - - if (!boost::filesystem::exists(appData)) { - // This should never ever happen - throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!"); - } - - // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of - // the path. - if (mConfig.find("AppDataSkipVendor") == mConfig.end()) { - appData /= mConfig["ExeVendor"]; - } - appData /= mConfig["ExeName"]; - - // Actually the name of the directory where the parameters are stored should be the name of - // the application due to branding reasons. - - // In order to write to our data path, we must create some directories, first. - if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(appData); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message()); - } - } - - mConfig["UserAppData"] = converter.to_bytes(appData.wstring()) + PATHSEP; - - // Create the default macro directory - boost::filesystem::path macroDir = converter.from_bytes(getUserMacroDir()); - if (!boost::filesystem::exists(macroDir) && !Py_IsInitialized()) { - try { - boost::filesystem::create_directories(macroDir); - } catch (const boost::filesystem::filesystem_error& e) { - throw Base::FileSystemError("Could not create macro directory. Failed with: " + e.code().message()); - } + // Create the default macro directory + // + boost::filesystem::path macroDir = stringToPath(getUserMacroDir()); + if (!boost::filesystem::exists(macroDir) && !Py_IsInitialized()) { + try { + boost::filesystem::create_directories(macroDir); + } catch (const boost::filesystem::filesystem_error& e) { + throw Base::FileSystemError("Could not create macro directory. Failed with: " + e.code().message()); } } -#else -# error "Implement ExtractUserPath() for your platform." -#endif } #if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)