mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-23 15:25:35 +08:00
605 lines
22 KiB
C++
605 lines
22 KiB
C++
/* Copyright (C) 2015
|
|
* swift project Community / Contributors
|
|
*
|
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
|
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
|
* or distributed except according to the terms contained in the LICENSE file.
|
|
*/
|
|
|
|
#include "blackmisc/network/networkutils.h"
|
|
#include "blackmisc/math/mathutils.h"
|
|
#include "blackmisc/worker.h"
|
|
#include "blackmisc/fileutils.h"
|
|
#include "blackmisc/directoryutils.h"
|
|
#include "blackconfig/buildconfig.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDateTime>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QFlags>
|
|
#include <QIODevice>
|
|
#include <QList>
|
|
#include <QLockFile>
|
|
#include <QRegularExpression>
|
|
#include <QTextStream>
|
|
#include <QtGlobal>
|
|
#include <QMap>
|
|
#include <algorithm>
|
|
#include <QStringBuilder>
|
|
|
|
using namespace BlackConfig;
|
|
using namespace BlackMisc::Math;
|
|
using namespace BlackMisc::Network;
|
|
|
|
namespace BlackMisc
|
|
{
|
|
const QString &CFileUtils::jsonAppendix()
|
|
{
|
|
static const QString j(".json");
|
|
return j;
|
|
}
|
|
|
|
const QString &CFileUtils::jsonWildcardAppendix()
|
|
{
|
|
static const QString jw("*" + jsonAppendix());
|
|
return jw;
|
|
}
|
|
|
|
bool CFileUtils::writeStringToFile(const QString &content, const QString &fileNameAndPath)
|
|
{
|
|
if (fileNameAndPath.isEmpty()) { return false; }
|
|
QFile file(fileNameAndPath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return false; }
|
|
QTextStream stream(&file);
|
|
stream << content;
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool CFileUtils::writeByteArrayToFile(const QByteArray &data, const QString &fileNameAndPath)
|
|
{
|
|
if (fileNameAndPath.isEmpty()) { return false; }
|
|
QFile file(fileNameAndPath);
|
|
if (!file.open(QIODevice::WriteOnly)) { return false; }
|
|
const qint64 c = file.write(data);
|
|
file.close();
|
|
return c == data.count();
|
|
}
|
|
|
|
bool CFileUtils::writeStringToLockedFile(const QString &content, const QString &fileNameAndPath)
|
|
{
|
|
QLockFile lock(fileNameAndPath + ".lock");
|
|
lock.lock();
|
|
return writeStringToFile(content, fileNameAndPath);
|
|
}
|
|
|
|
QString CFileUtils::readFileToString(const QString &fileNameAndPath)
|
|
{
|
|
QFile file(fileNameAndPath);
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return {}; }
|
|
QTextStream stream(&file);
|
|
const QString content(stream.readAll());
|
|
file.close();
|
|
return content;
|
|
}
|
|
|
|
QString CFileUtils::readLockedFileToString(const QString &fileNameAndPath)
|
|
{
|
|
QLockFile lock(fileNameAndPath + ".lock");
|
|
lock.lock();
|
|
return readFileToString(fileNameAndPath);
|
|
}
|
|
|
|
QString CFileUtils::readFileToString(const QString &filePath, const QString &fileName)
|
|
{
|
|
return readFileToString(appendFilePaths(filePath, fileName));
|
|
}
|
|
|
|
QString CFileUtils::readLockedFileToString(const QString &filePath, const QString &fileName)
|
|
{
|
|
return readLockedFileToString(appendFilePaths(filePath, fileName));
|
|
}
|
|
|
|
bool CFileUtils::writeStringToFileInBackground(const QString &content, const QString &fileNameAndPath)
|
|
{
|
|
if (fileNameAndPath.isEmpty()) { return false; }
|
|
CWorker *worker = CWorker::fromTask(QCoreApplication::instance(), "writeStringToFileInBackground", [content, fileNameAndPath]()
|
|
{
|
|
const bool s = CFileUtils::writeStringToFile(content, fileNameAndPath);
|
|
Q_UNUSED(s);
|
|
});
|
|
return worker ? true : false;
|
|
}
|
|
|
|
QString CFileUtils::appendFilePaths(const QString &path1, const QString &path2)
|
|
{
|
|
if (path1.isEmpty()) { return QDir::cleanPath(path2); }
|
|
if (path2.isEmpty()) { return QDir::cleanPath(path1); }
|
|
if (path1.endsWith('/'))
|
|
{
|
|
// avoid double /
|
|
if (path2.startsWith('/')) { return QDir::cleanPath(path1 % path2.mid(1)); }
|
|
return QDir::cleanPath(path1 % path2);
|
|
}
|
|
return QDir::cleanPath(path1 % QChar('/') % path2);
|
|
}
|
|
|
|
QString CFileUtils::appendFilePathsAndFixUnc(const QString &path1, const QString &path2)
|
|
{
|
|
static const bool win = CBuildConfig::isRunningOnWindowsNtPlatform();
|
|
return win ? CFileUtils::fixWindowsUncPath(appendFilePaths(path1, path2)) : appendFilePaths(path1, path2);
|
|
}
|
|
|
|
QString CFileUtils::stripFileFromPath(const QString &path)
|
|
{
|
|
if (path.endsWith('/')) { return path; }
|
|
if (!path.contains('/')) { return path; }
|
|
return path.left(path.lastIndexOf('/'));
|
|
}
|
|
|
|
QString CFileUtils::stripFirstSlashPart(const QString &path)
|
|
{
|
|
QString p = normalizeFilePathToQtStandard(path);
|
|
int i = p.indexOf('/');
|
|
if (i < 0) { return p; }
|
|
if ((i + 1) >= path.length()) { return {}; }
|
|
return path.mid(i + 1);
|
|
}
|
|
|
|
QStringList CFileUtils::stripFirstSlashParts(const QStringList &paths)
|
|
{
|
|
QStringList stripped;
|
|
for (const QString &path : paths)
|
|
{
|
|
stripped.push_back(stripFileFromPath(path));
|
|
}
|
|
return stripped;
|
|
}
|
|
|
|
QString CFileUtils::stripLeadingSlashOrDriveLetter(const QString &path)
|
|
{
|
|
thread_local const QRegularExpression re("^\\/+|^[a-zA-Z]:\\/*");
|
|
QString p(path);
|
|
return p.replace(re, "");
|
|
}
|
|
|
|
QStringList CFileUtils::stripLeadingSlashOrDriveLetters(const QStringList &paths)
|
|
{
|
|
QStringList stripped;
|
|
for (const QString &path : paths)
|
|
{
|
|
stripped.push_back(stripLeadingSlashOrDriveLetter(path));
|
|
}
|
|
return stripped;
|
|
}
|
|
|
|
QString CFileUtils::lastPathSegment(const QString &path)
|
|
{
|
|
if (path.isEmpty()) { return {}; }
|
|
if (path.endsWith('/')) { return CFileUtils::lastPathSegment(path.left(path.length() - 1)); }
|
|
if (!path.contains('/')) { return path; }
|
|
return path.mid(path.lastIndexOf('/') + 1);
|
|
}
|
|
|
|
QString CFileUtils::appendFilePaths(const QString &path1, const QString &path2, const QString &path3)
|
|
{
|
|
return CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(path1, path2), path3);
|
|
}
|
|
|
|
QString CFileUtils::appendFilePathsAndFixUnc(const QString &path1, const QString &path2, const QString &path3)
|
|
{
|
|
static const bool win = CBuildConfig::isRunningOnWindowsNtPlatform();
|
|
return win ?
|
|
CFileUtils::fixWindowsUncPath(CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(path1, path2), path3)) :
|
|
CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(path1, path2), path3);
|
|
}
|
|
|
|
QString CFileUtils::pathUp(const QString &path)
|
|
{
|
|
const int i = path.lastIndexOf('/');
|
|
if (i < 0) { return path; }
|
|
return path.left(i);
|
|
}
|
|
|
|
QString CFileUtils::normalizeFilePathToQtStandard(const QString &filePath)
|
|
{
|
|
if (filePath.isEmpty()) { return {}; }
|
|
QString n = QDir::cleanPath(filePath);
|
|
n = n.replace('\\', '/').replace("//", "/"); // should be done alreay by cleanPath, paranoia
|
|
return n;
|
|
}
|
|
|
|
QStringList CFileUtils::makeDirectoriesRelative(const QStringList &directories, const QString &rootDirectory, Qt::CaseSensitivity cs)
|
|
{
|
|
// not using QDir::relativePath because I do not want "../xyz" paths
|
|
if (rootDirectory.isEmpty() || rootDirectory == "/") { return directories; }
|
|
const QString rd(rootDirectory.endsWith('/') ? rootDirectory.left(rootDirectory.length() - 1) : rootDirectory);
|
|
const int p = rd.length();
|
|
QStringList relativeDirectories;
|
|
for (const QString &dir : directories)
|
|
{
|
|
if (dir.startsWith(rd, cs) && dir.length() > p + 1)
|
|
{
|
|
relativeDirectories.append(dir.mid(p + 1));
|
|
}
|
|
else
|
|
{
|
|
relativeDirectories.append(dir); // absolute
|
|
}
|
|
}
|
|
return relativeDirectories;
|
|
}
|
|
|
|
bool CFileUtils::sameDirectories(const QStringList &dirs1, const QStringList &dirs2, Qt::CaseSensitivity cs)
|
|
{
|
|
// clean up
|
|
QStringList dirs1Cleaned(dirs1);
|
|
QStringList dirs2Cleaned(dirs2);
|
|
dirs1Cleaned.removeAll("");
|
|
dirs1Cleaned.removeDuplicates();
|
|
dirs2Cleaned.removeAll("");
|
|
dirs2Cleaned.removeDuplicates();
|
|
if (dirs1Cleaned.size() != dirs2Cleaned.size()) { return false; }
|
|
|
|
int d2 = 0;
|
|
dirs1Cleaned.sort(cs);
|
|
dirs2Cleaned.sort(cs);
|
|
for (const QString &d1 : dirs1)
|
|
{
|
|
if (!stringCompare(d1, dirs2.at(d2), cs)) { return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Qt::CaseSensitivity CFileUtils::osFileNameCaseSensitivity()
|
|
{
|
|
return CBuildConfig::isRunningOnWindowsNtPlatform() ? Qt::CaseInsensitive : Qt::CaseSensitive;
|
|
}
|
|
|
|
bool CFileUtils::isFileNameCaseSensitive()
|
|
{
|
|
return CFileUtils::osFileNameCaseSensitivity() == Qt::CaseSensitive;
|
|
}
|
|
|
|
bool CFileUtils::matchesExcludeDirectory(const QString &directoryPath, const QString &excludePattern, Qt::CaseSensitivity cs)
|
|
{
|
|
if (directoryPath.isEmpty() || excludePattern.isEmpty()) { return false; }
|
|
const QString normalizedExcludePattern(normalizeFilePathToQtStandard(excludePattern));
|
|
return directoryPath.contains(normalizedExcludePattern, cs);
|
|
}
|
|
|
|
bool CFileUtils::isExcludedDirectory(const QDir &directory, const QStringList &excludeDirectories, Qt::CaseSensitivity cs)
|
|
{
|
|
if (excludeDirectories.isEmpty()) { return false; }
|
|
const QString d = directory.absolutePath();
|
|
return isExcludedDirectory(d, excludeDirectories, cs);
|
|
}
|
|
|
|
bool CFileUtils::isExcludedDirectory(const QFileInfo &fileInfo, const QStringList &excludeDirectories, Qt::CaseSensitivity cs)
|
|
{
|
|
if (excludeDirectories.isEmpty()) { return false; }
|
|
return isExcludedDirectory(fileInfo.absoluteDir(), excludeDirectories, cs);
|
|
}
|
|
|
|
bool CFileUtils::isExcludedDirectory(const QString &directoryPath, const QStringList &excludeDirectories, Qt::CaseSensitivity cs)
|
|
{
|
|
if (excludeDirectories.isEmpty()) { return false; }
|
|
for (const QString &ex : excludeDirectories)
|
|
{
|
|
if (matchesExcludeDirectory(directoryPath, ex, cs)) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QStringList CFileUtils::removeSubDirectories(const QStringList &directories, Qt::CaseSensitivity cs)
|
|
{
|
|
if (directories.size() < 2) { return directories; }
|
|
QStringList dirs(directories);
|
|
dirs.removeDuplicates();
|
|
dirs.sort(cs);
|
|
if (dirs.size() < 2) { return dirs; }
|
|
|
|
QString last;
|
|
QStringList result;
|
|
for (const QString &path : as_const(dirs))
|
|
{
|
|
if (path.isEmpty()) { continue; }
|
|
if (last.isEmpty() || !path.startsWith(last, cs))
|
|
{
|
|
result.append(path);
|
|
}
|
|
last = path;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QString CFileUtils::findFirstExisting(const QStringList &filesOrDirectory)
|
|
{
|
|
if (filesOrDirectory.isEmpty()) { return {}; }
|
|
for (const QString &f : filesOrDirectory)
|
|
{
|
|
if (f.isEmpty()) { continue; }
|
|
const QString fn(normalizeFilePathToQtStandard(f));
|
|
const QFileInfo fi(fn);
|
|
if (fi.exists()) { return fi.absoluteFilePath(); }
|
|
}
|
|
return {};
|
|
}
|
|
|
|
QString CFileUtils::findFirstFile(const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories, std::function<bool(const QFileInfo &)> predicate)
|
|
{
|
|
if (isExcludedDirectory(dir, excludeDirectories)) { return QString(); }
|
|
const QFileInfoList result = dir.entryInfoList(nameFilters, QDir::Files);
|
|
if (predicate)
|
|
{
|
|
auto it = std::find_if(result.cbegin(), result.cend(), predicate);
|
|
if (it != result.cend()) { return it->filePath(); }
|
|
}
|
|
else
|
|
{
|
|
if (! result.isEmpty()) { return result.first().filePath(); }
|
|
}
|
|
if (recursive)
|
|
{
|
|
for (const auto &subdir : dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
|
{
|
|
if (isExcludedDirectory(subdir, excludeDirectories)) { continue; }
|
|
const QString first = findFirstFile(subdir.filePath(), true, nameFilters, excludeDirectories, predicate);
|
|
if (! first.isEmpty()) { return first; }
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool CFileUtils::containsFile(const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories, std::function<bool(const QFileInfo &)> predicate)
|
|
{
|
|
return ! findFirstFile(dir, recursive, nameFilters, excludeDirectories, predicate).isEmpty();
|
|
}
|
|
|
|
QString CFileUtils::findFirstNewerThan(const QDateTime &time, const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories)
|
|
{
|
|
return findFirstFile(dir, recursive, nameFilters, excludeDirectories, [time](const QFileInfo & fi) { return fi.lastModified() > time; });
|
|
}
|
|
|
|
bool CFileUtils::containsFileNewerThan(const QDateTime &time, const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories)
|
|
{
|
|
return ! findFirstNewerThan(time, dir, recursive, nameFilters, excludeDirectories).isEmpty();
|
|
}
|
|
|
|
QFileInfoList CFileUtils::enumerateFiles(const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories, std::function<bool(const QFileInfo &)> predicate)
|
|
{
|
|
if (isExcludedDirectory(dir, excludeDirectories)) { return QFileInfoList(); }
|
|
QFileInfoList result = dir.entryInfoList(nameFilters, QDir::Files);
|
|
if (predicate)
|
|
{
|
|
result.erase(std::remove_if(result.begin(), result.end(), std::not1(predicate)), result.end());
|
|
}
|
|
if (recursive)
|
|
{
|
|
for (const auto &subdir : dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
|
{
|
|
if (isExcludedDirectory(subdir, excludeDirectories)) { continue; }
|
|
result += enumerateFiles(subdir.filePath(), true, nameFilters, excludeDirectories, predicate);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QFileInfo CFileUtils::findLastModified(const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories)
|
|
{
|
|
if (isExcludedDirectory(dir, excludeDirectories)) { return QString(); }
|
|
const QFileInfoList files = enumerateFiles(dir, recursive, nameFilters, excludeDirectories);
|
|
if (files.isEmpty()) { return {}; }
|
|
|
|
auto it = std::max_element(files.cbegin(), files.cend(), [](const QFileInfo & a, const QFileInfo & b)
|
|
{
|
|
return a.lastModified() < b.lastModified();
|
|
});
|
|
return *it;
|
|
}
|
|
|
|
QFileInfo CFileUtils::findLastCreated(const QDir &dir, bool recursive, const QStringList &nameFilters, const QStringList &excludeDirectories)
|
|
{
|
|
if (isExcludedDirectory(dir, excludeDirectories)) { return QString(); }
|
|
const QFileInfoList files = enumerateFiles(dir, recursive, nameFilters, excludeDirectories);
|
|
if (files.isEmpty()) { return {}; }
|
|
|
|
auto it = std::max_element(files.cbegin(), files.cend(), [](const QFileInfo & a, const QFileInfo & b)
|
|
{
|
|
return a.created() < b.created();
|
|
});
|
|
return *it;
|
|
}
|
|
|
|
const QStringList &CFileUtils::getSwiftExecutables()
|
|
{
|
|
static const QStringList executables(
|
|
QFileInfo(QCoreApplication::applicationFilePath())
|
|
.dir()
|
|
.entryList(QDir::Executable | QDir::Files)
|
|
);
|
|
return executables;
|
|
}
|
|
|
|
QStringList CFileUtils::getBaseNamesOnly(const QStringList &fileNames)
|
|
{
|
|
QStringList baseNames;
|
|
for (const QString &fn : fileNames)
|
|
{
|
|
const QFileInfo fi(fn);
|
|
baseNames.push_back(fi.baseName());
|
|
}
|
|
return baseNames;
|
|
}
|
|
|
|
QStringList CFileUtils::getFileNamesOnly(const QStringList &fileNames)
|
|
{
|
|
QStringList fns;
|
|
for (const QString &fn : fileNames)
|
|
{
|
|
const QFileInfo fi(fn);
|
|
fns.push_back(fi.fileName());
|
|
}
|
|
return fns;
|
|
}
|
|
|
|
QString CFileUtils::lockFileError(const QLockFile &lockFile)
|
|
{
|
|
switch (lockFile.error())
|
|
{
|
|
case QLockFile::NoError: return QStringLiteral("No error");
|
|
case QLockFile::PermissionError: return QStringLiteral("Insufficient permission");
|
|
case QLockFile::UnknownError: return QStringLiteral("Unknown error");
|
|
case QLockFile::LockFailedError:
|
|
{
|
|
QString hostname, appname;
|
|
qint64 pid = 0;
|
|
lockFile.getLockInfo(&pid, &hostname, &appname);
|
|
return QStringLiteral("Lock open in another process (%1 %2 on %3)").arg(hostname, QString::number(pid), appname);
|
|
}
|
|
default: return QStringLiteral("Bad error number");
|
|
}
|
|
}
|
|
|
|
QString CFileUtils::fixWindowsUncPath(const QString &filePath)
|
|
{
|
|
static const bool win = CBuildConfig::isRunningOnWindowsNtPlatform();
|
|
if (!win) { return filePath; }
|
|
if (!filePath.startsWith('/')) { return filePath; }
|
|
if (filePath.startsWith("//")) { return filePath; }
|
|
return QStringLiteral("/%1").arg(filePath);
|
|
}
|
|
|
|
QStringList CFileUtils::fixWindowsUncPaths(const QStringList &filePaths)
|
|
{
|
|
static const bool win = CBuildConfig::isRunningOnWindowsNtPlatform();
|
|
if (!win) { return filePaths; }
|
|
|
|
QStringList fixedPaths;
|
|
for (const QString &path : filePaths)
|
|
{
|
|
fixedPaths << fixWindowsUncPath(path);
|
|
}
|
|
return fixedPaths;
|
|
}
|
|
|
|
bool CFileUtils::isWindowsUncPath(const QString &filePath)
|
|
{
|
|
if (filePath.startsWith("//") || filePath.startsWith("\\\\")) { return true; }
|
|
if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return false; } // "/tmp" is valid on Unix/Mac
|
|
|
|
// Windows here
|
|
const QString fp = fixWindowsUncPath(filePath);
|
|
return (fp.startsWith("//") || fp.startsWith("\\\\"));
|
|
}
|
|
|
|
QString CFileUtils::windowsUncMachine(const QString &filePath)
|
|
{
|
|
if (!CFileUtils::isWindowsUncPath(filePath)) { return {}; }
|
|
QString f = filePath;
|
|
f.replace("\\", "/");
|
|
f.replace("//", "");
|
|
if (f.startsWith("/")) { f = f.mid(1); }
|
|
const int i = f.indexOf('/');
|
|
if (i < 0) { return f; }
|
|
return f.left(i);
|
|
}
|
|
|
|
bool CFileUtils::canPingUncMachine(const QString &machine)
|
|
{
|
|
static QMap<QString, qint64> good;
|
|
static QMap<QString, qint64> bad;
|
|
|
|
if (machine.isEmpty()) { return false; }
|
|
const QString m = machine.toLower();
|
|
if (good.contains(m)) { return true; } // good ones we only test once
|
|
if (bad.contains(m))
|
|
{
|
|
const qint64 ts = bad.value(m);
|
|
const qint64 dt = QDateTime::currentSecsSinceEpoch() - ts;
|
|
if (dt < 1000 * 60) { return false; } // still bad
|
|
|
|
// outdated, test again
|
|
}
|
|
|
|
const bool p = CNetworkUtils::canPing(m);
|
|
if (p)
|
|
{
|
|
good.insert(m, QDateTime::currentSecsSinceEpoch());
|
|
bad.remove(m);
|
|
}
|
|
else
|
|
{
|
|
bad.insert(m, QDateTime::currentSecsSinceEpoch());
|
|
good.remove(m);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
QString CFileUtils::toWindowsLocalPath(const QString &path)
|
|
{
|
|
QString p = CFileUtils::fixWindowsUncPath(path);
|
|
return p.replace('/', '\\');
|
|
}
|
|
|
|
QString CFileUtils::humanReadableFileSize(qint64 size)
|
|
{
|
|
// from https://stackoverflow.com/a/30958189/356726
|
|
// fell free to replace it by something better
|
|
static const QStringList units({ "KB", "MB", "GB", "TB" });
|
|
if (size <= 1024) { return QString::number(size); }
|
|
|
|
QStringListIterator i(units);
|
|
double currentSize = size;
|
|
QString unit;
|
|
while (currentSize >= 1024.0 && i.hasNext())
|
|
{
|
|
unit = i.next();
|
|
currentSize /= 1024.0;
|
|
}
|
|
return QStringLiteral("%1 %2").arg(CMathUtils::roundAsString(currentSize, 2), unit);
|
|
}
|
|
|
|
const QStringList &CFileUtils::executableSuffixes()
|
|
{
|
|
// incomplete list of file name appendixes
|
|
// dmg is not a executable. It is a MacOS container. If you open it, a new virtual drive will be mapped which includes a executable.
|
|
static const QStringList appendixes({".exe", ".dmg", ".run"});
|
|
return appendixes;
|
|
}
|
|
|
|
bool CFileUtils::isExecutableFile(const QString &fileName)
|
|
{
|
|
for (const QString &app : CFileUtils::executableSuffixes())
|
|
{
|
|
if (fileName.endsWith(app, Qt::CaseInsensitive)) { return true; }
|
|
}
|
|
return CFileUtils::isSwiftInstaller(fileName);
|
|
}
|
|
|
|
bool CFileUtils::isSwiftInstaller(const QString &fileName)
|
|
{
|
|
if (fileName.isEmpty()) { return false; }
|
|
return fileName.contains("swift", Qt::CaseInsensitive) && fileName.contains("installer");
|
|
}
|
|
|
|
QString CFileUtils::soundFilePathAndFileName(const QString &name)
|
|
{
|
|
if (name.isEmpty()) { return {}; }
|
|
return CFileUtils::appendFilePaths(CDirectoryUtils::soundFilesDirectory(), name);
|
|
}
|
|
|
|
QUrl CFileUtils::soundFileQUrl(const QString &directory, const QString &name)
|
|
{
|
|
if (name.isEmpty()) { return {}; }
|
|
if (!directory.isEmpty())
|
|
{
|
|
const QString f = CFileUtils::appendFilePathsAndFixUnc(directory, name);
|
|
const QFileInfo fi(f);
|
|
if (fi.exists()) { return QUrl::fromLocalFile(f); }
|
|
}
|
|
return QUrl::fromLocalFile(CFileUtils::soundFilePathAndFileName(name));
|
|
}
|
|
} // ns
|