diff --git a/src/blackmisc/fileutils.cpp b/src/blackmisc/fileutils.cpp index 7f6bdf725..e9a7ad18c 100644 --- a/src/blackmisc/fileutils.cpp +++ b/src/blackmisc/fileutils.cpp @@ -506,6 +506,31 @@ namespace BlackMisc return f.left(i); } + QSet CFileUtils::windowsUncMachines(const QSet &paths) + { + if (paths.isEmpty()) { return {}; } + + const Qt::CaseSensitivity cs = osFileNameCaseSensitivity(); + const bool isCs = isFileNameCaseSensitive(); + + QSet machines; + QString lastMachine; + + for (const QString &p : paths) + { + if (!lastMachine.isEmpty() && p.contains(lastMachine, cs)) + { + // shortcut + continue; + } + const QString m = isCs ? windowsUncMachine(p) : windowsUncMachine(p).toLower(); + if (m.isEmpty()) { continue; } + lastMachine = m; + machines.insert(m); + } + return machines; + } + bool CFileUtils::canPingUncMachine(const QString &machine) { static QMap good; diff --git a/src/blackmisc/fileutils.h b/src/blackmisc/fileutils.h index e03deff83..a289837c6 100644 --- a/src/blackmisc/fileutils.h +++ b/src/blackmisc/fileutils.h @@ -181,6 +181,9 @@ namespace BlackMisc //! Machine in Windows UNC path static QString windowsUncMachine(const QString &filePath); + //! All UNC machines from the paths + static QSet windowsUncMachines(const QSet &paths); + //! Can connect the UNC machine static bool canPingUncMachine(const QString &machine); diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index 4b50ef6d8..8db3fbbc7 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -8,16 +8,18 @@ #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/matchingutils.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/aviation/callsign.h" #include "blackmisc/aviation/logutils.h" #include "blackmisc/math/mathutils.h" #include "blackmisc/compare.h" #include "blackmisc/iterator.h" #include "blackmisc/range.h" -#include "fileutils.h" -#include "directoryutils.h" +#include "blackmisc/fileutils.h" +#include "blackmisc/directoryutils.h" #include "blackmisc/statusmessage.h" #include "blackmisc/stringutils.h" +#include "blackconfig/buildconfig.h" #include #include @@ -27,6 +29,7 @@ #include #include +using namespace BlackConfig; using namespace BlackMisc::Network; using namespace BlackMisc::Math; using namespace BlackMisc::Aviation; @@ -1352,6 +1355,22 @@ namespace BlackMisc return files; } + QSet CAircraftModelList::getAllUNCFileNames() const + { + const bool cs = CFileUtils::isFileNameCaseSensitive(); + QSet files; + for (const CAircraftModel &model : as_const(*this)) + { + if (!model.hasFileName()) { continue; } + const QString fn = (cs ? model.getFileName() : model.getFileNameLowerCase()); + if (CFileUtils::isWindowsUncPath(fn)) + { + files.insert(fn); + } + } + return files; + } + QString CAircraftModelList::getCombinedTypesAsString(const QString &separator) const { if (this->isEmpty()) { return {}; } @@ -1540,6 +1559,13 @@ namespace BlackMisc CAircraftModelList sorted(*this); if (!alreadySortedByFn) { sorted.sortByFileName(); } + // avoid hanging if UNC paths are not available + if (CBuildConfig::isRunningOnWindowsNtPlatform()) + { + const CStatusMessageList uncMsgs = this->validateUncFiles(sorted.getAllUNCFileNames()); + if (uncMsgs.hasErrorMessages()) { return uncMsgs; } + } + const bool caseSensitive = CFileUtils::isFileNameCaseSensitive(); const QString simRootDir = CFileUtils::normalizeFilePathToQtStandard( CFileUtils::stripLeadingSlashOrDriveLetter( @@ -1615,6 +1641,35 @@ namespace BlackMisc return msgs; } + CStatusMessageList CAircraftModelList::validateUncFiles(const QSet uncFiles) const + { + // check if UNC paths can be reached + CStatusMessageList msgs; + if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return msgs; } + if (uncFiles.isEmpty()) { return msgs; } + + const QSet uncMachines = CFileUtils::windowsUncMachines(uncFiles); + if (uncMachines.isEmpty()) + { + msgs.push_back(CStatusMessage(this).validationInfo(u"Found NO UNC machines for %1 files, odd...?") << uncFiles.size()); + } + else + { + const QString machines = joinStringSet(uncMachines, ", "); + msgs.push_back(CStatusMessage(this).validationInfo(u"Found %1 UNC files on machines: %2") << uncFiles.size() << machines); + } + + for (const QString &m : uncMachines) + { + const bool ping = CNetworkUtils::canPing(m); + if (!ping) + { + msgs.push_back(CStatusMessage(this).validationError(u"Cannot ping UNC machine(s): %1. UNC files: %2") << m << uncFiles.size()); + } + } + return msgs; + } + QJsonObject CAircraftModelList::toMemoizedJson() const { CAircraftModel::MemoHelper::CMemoizer helper; diff --git a/src/blackmisc/simulation/aircraftmodellist.h b/src/blackmisc/simulation/aircraftmodellist.h index 1a0fa15de..162b9e72c 100644 --- a/src/blackmisc/simulation/aircraftmodellist.h +++ b/src/blackmisc/simulation/aircraftmodellist.h @@ -473,6 +473,10 @@ namespace BlackMisc //! All file names QSet getAllFileNames() const; + //! All UNC file names + //! \remark Windows on a "shared drive" paths + QSet getAllUNCFileNames() const; + //! All combined types as string QString getCombinedTypesAsString(const QString &separator = ", ") const; @@ -555,6 +559,10 @@ namespace BlackMisc const Aviation::CAircraftCategoryList &aircraftCategories = {}, const Aviation::CLiveryList &liveries = {}, const CDistributorList &distributors = {}); + + private: + //! Validate UNC paths (Windows) + CStatusMessageList validateUncFiles(const QSet uncFiles) const; }; //! Model per callsign