//// PathUtils.cpp// libraries/shared/src//// Created by Brad Hefta-Gaub on 12/15/14.// Copyright 2014 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "PathUtils.h" #include <mutex> // std::once #include <QtCore/QCoreApplication>#include <QtCore/QDateTime>#include <QtCore/QDir>#include <QtCore/QDirIterator>#include <QtCore/QFileInfo>#include <QtCore/QProcessEnvironment>#include <QtCore/QRegularExpression>#include <QtCore/QStandardPaths>#include <QtCore/QString>#include <QtCore/QUrl>#include <QtCore/QVector> #if defined(Q_OS_OSX)#include <mach-o/dyld.h>#endif #include "shared/GlobalAppProperties.h"#include "SharedUtil.h" // Format: AppName-PID-Timestamp// Example: ...QString TEMP_DIR_FORMAT { "%1-%2-%3" }; #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)static bool USE_SOURCE_TREE_RESOURCES() { static bool result = false; static std::once_flag once; std::call_once(once, [&] { const QString USE_SOURCE_TREE_RESOURCES_FLAG("HIFI_USE_SOURCE_TREE_RESOURCES"); result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG); }); return result;}#endif const QString& PathUtils::getRccPath() { static QString rccLocation; static std::once_flag once; std::call_once(once, [&] { static const QString rccName{ "/resources.rcc" };#if defined(Q_OS_OSX) char buffer[8192]; uint32_t bufferSize = sizeof(buffer); _NSGetExecutablePath(buffer, &bufferSize); rccLocation = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + rccName;#elif defined(Q_OS_ANDROID) rccLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + rccName;#else rccLocation = QCoreApplication::applicationDirPath() + rccName;#endif }); return rccLocation;} #ifdef DEV_BUILDconst QString& PathUtils::projectRootPath() { static QString sourceFolder; static std::once_flag once; std::call_once(once, [&] { QDir thisDir = QFileInfo(__FILE__).absoluteDir(); sourceFolder = QDir::cleanPath(thisDir.absoluteFilePath("../../../")); }); return sourceFolder;}#endif const QString& PathUtils::resourcesPath() { static QString staticResourcePath{ ":/" }; static std::once_flag once; std::call_once(once, [&]{#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree staticResourcePath = projectRootPath() + "/interface/resources/"; }#endif }); return staticResourcePath;} const QString& PathUtils::resourcesUrl() { static QString staticResourcePath{ "qrc:///" }; static std::once_flag once; std::call_once(once, [&]{#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/").toString(); }#endif }); return staticResourcePath;} QUrl PathUtils::resourcesUrl(const QString& relativeUrl) { return QUrl(resourcesUrl() + relativeUrl);} QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) { QString path = fileUrl.path(); if (path.startsWith("/~/")) { // this results in a qrc:// url... // return resourcesUrl(path.mid(3)); #ifdef Q_OS_MAC static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";#elif defined (ANDROID) static const QString staticResourcePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/";#else static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";#endif path.replace(0, 3, staticResourcePath); QUrl expandedURL = QUrl::fromLocalFile(path); return expandedURL; } return fileUrl;} const QString& PathUtils::qmlBaseUrl() { static const QString staticResourcePath = resourcesUrl() + "qml/"; return staticResourcePath;} QUrl PathUtils::qmlUrl(const QString& relativeUrl) { return QUrl(qmlBaseUrl() + relativeUrl);} QString PathUtils::getAppDataPath() { return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/";} QString PathUtils::getAppLocalDataPath() { QString overriddenPath = qApp->property(hifi::properties::APP_LOCAL_DATA_PATH).toString(); // return overridden path if set if (!overriddenPath.isEmpty()) { return overriddenPath; } // otherwise return standard path#ifdef Q_OS_ANDROID return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/";#else return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/";#endif} QString PathUtils::getAppDataFilePath(const QString& filename) { return QDir(getAppDataPath()).absoluteFilePath(filename);} QString PathUtils::getAppLocalDataFilePath(const QString& filename) { return QDir(getAppLocalDataPath()).absoluteFilePath(filename);} QString PathUtils::generateTemporaryDir() { QDir rootTempDir = QDir::tempPath(); QString appName = qApp->applicationName(); for (auto i = 0; i < 64; ++i) { auto now = std::chrono::system_clock::now().time_since_epoch().count(); auto dirName = TEMP_DIR_FORMAT.arg(appName).arg(qApp->applicationPid()).arg(now); QDir tempDir = rootTempDir.filePath(dirName); if (tempDir.mkpath(".")) { return tempDir.absolutePath(); } } return "";} bool PathUtils::deleteMyTemporaryDir(QString dirName) { QDir rootTempDir = QDir::tempPath(); QString appName = qApp->applicationName(); QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?<pid>\\d+)\\-(?<timestamp>\\d+)$" }; auto match = re.match(dirName); auto pid = match.capturedRef("pid").toLongLong(); if (match.hasMatch() && rootTempDir.exists(dirName) && pid == qApp->applicationPid()) { auto absoluteDirPath = QDir(rootTempDir.absoluteFilePath(dirName)); bool success = absoluteDirPath.removeRecursively(); if (success) { qDebug() << " Removing temporary directory: " << absoluteDirPath.absolutePath(); } else { qDebug() << " Failed to remove temporary directory: " << absoluteDirPath.absolutePath(); } return success; } return false;} // Delete all temporary directories for an applicationint PathUtils::removeTemporaryApplicationDirs(QString appName) { if (appName.isNull()) { appName = qApp->applicationName(); } auto dirName = TEMP_DIR_FORMAT.arg(appName).arg("*").arg("*"); QDir rootTempDir = QDir::tempPath(); auto dirs = rootTempDir.entryInfoList({ dirName }, QDir::Dirs); int removed = 0; for (auto& dir : dirs) { auto dirName = dir.fileName(); auto absoluteDirPath = QDir(dir.absoluteFilePath()); QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?<pid>\\d+)\\-(?<timestamp>\\d+)$" }; auto match = re.match(dirName); if (match.hasMatch()) { auto pid = match.capturedRef("pid").toLongLong(); auto timestamp = match.capturedRef("timestamp"); if (!processIsRunning(pid)) { qDebug() << " Removing old temporary directory: " << dir.absoluteFilePath(); absoluteDirPath.removeRecursively(); removed++; } else { qDebug() << " Not removing (process is running): " << dir.absoluteFilePath(); } } } return removed;} QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) { QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { if (fileNameLowered.endsWith(possibleExtension.toLower())) { return fileName.left(fileName.count() - possibleExtension.count() - 1); } } return fileName;} QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions) { QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions); QString newestFileName = originalFileName; QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0); foreach (QString possibleExtension, possibleExtensions) { QString fileName = sansExt + "." + possibleExtension; QFileInfo fileInfo(fileName); if (fileInfo.exists() && fileInfo.lastModified() > newestTime) { newestFileName = fileName; newestTime = fileInfo.lastModified(); } } return newestFileName;} QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) { static QString overriddenDefaultScriptsLocation = ""; QString path; // set overriddenDefaultScriptLocation if it was passed in if (!newDefaultPath.isEmpty()) { overriddenDefaultScriptsLocation = newDefaultPath; } // use the overridden location if it is set if (!overriddenDefaultScriptsLocation.isEmpty()) { path = overriddenDefaultScriptsLocation; } else {#if defined(Q_OS_OSX) path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";#elif defined(Q_OS_ANDROID) path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/scripts";#else path = QCoreApplication::applicationDirPath() + "/scripts";#endif } // turn the string into a legit QUrl return QUrl::fromLocalFile(QFileInfo(path).canonicalFilePath());} QString PathUtils::stripFilename(const QUrl& url) { // Guard against meaningless query and fragment parts. // Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation()) return url.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment);} Qt::CaseSensitivity PathUtils::getFSCaseSensitivity() { static Qt::CaseSensitivity sensitivity { Qt::CaseSensitive }; static std::once_flag once; std::call_once(once, [&] { QString path = defaultScriptsLocation().toLocalFile(); QFileInfo upperFI(path.toUpper()); QFileInfo lowerFI(path.toLower()); sensitivity = (upperFI == lowerFI) ? Qt::CaseInsensitive : Qt::CaseSensitive; }); return sensitivity;} bool PathUtils::isDescendantOf(const QUrl& childURL, const QUrl& parentURL) { QString child = stripFilename(childURL); QString parent = stripFilename(parentURL); return child.startsWith(parent, PathUtils::getFSCaseSensitivity());}
//
// PathUtils.cpp
// libraries/shared/src
//
// Created by Brad Hefta-Gaub on 12/15/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PathUtils.h"
#include <mutex> // std::once
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
#include <QtCore/QFileInfo>
#include <QtCore/QProcessEnvironment>
#include <QtCore/QRegularExpression>
#include <QtCore/QStandardPaths>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtCore/QVector>
#if defined(Q_OS_OSX)
#include <mach-o/dyld.h>
#endif
#include "shared/GlobalAppProperties.h"
#include "SharedUtil.h"
// Format: AppName-PID-Timestamp
// Example: ...
QString TEMP_DIR_FORMAT { "%1-%2-%3" };
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
static bool USE_SOURCE_TREE_RESOURCES() {
static bool result = false;
static std::once_flag once;
std::call_once(once, [&] {
const QString USE_SOURCE_TREE_RESOURCES_FLAG("HIFI_USE_SOURCE_TREE_RESOURCES");
result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG);
});
return result;
}
#endif
const QString& PathUtils::getRccPath() {
static QString rccLocation;
static std::once_flag once;
std::call_once(once, [&] {
static const QString rccName{ "/resources.rcc" };
#if defined(Q_OS_OSX)
char buffer[8192];
uint32_t bufferSize = sizeof(buffer);
_NSGetExecutablePath(buffer, &bufferSize);
rccLocation = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + rccName;
#elif defined(Q_OS_ANDROID)
rccLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + rccName;
#else
rccLocation = QCoreApplication::applicationDirPath() + rccName;
#endif
});
return rccLocation;
}
#ifdef DEV_BUILD
const QString& PathUtils::projectRootPath() {
static QString sourceFolder;
static std::once_flag once;
std::call_once(once, [&] {
QDir thisDir = QFileInfo(__FILE__).absoluteDir();
sourceFolder = QDir::cleanPath(thisDir.absoluteFilePath("../../../"));
});
return sourceFolder;
}
#endif
const QString& PathUtils::resourcesPath() {
static QString staticResourcePath{ ":/" };
static std::once_flag once;
std::call_once(once, [&]{
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree
staticResourcePath = projectRootPath() + "/interface/resources/";
}
#endif
});
return staticResourcePath;
}
const QString& PathUtils::resourcesUrl() {
static QString staticResourcePath{ "qrc:///" };
static std::once_flag once;
std::call_once(once, [&]{
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree
staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/").toString();
}
#endif
});
return staticResourcePath;
}
QUrl PathUtils::resourcesUrl(const QString& relativeUrl) {
return QUrl(resourcesUrl() + relativeUrl);
}
QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) {
QString path = fileUrl.path();
if (path.startsWith("/~/")) {
// this results in a qrc:// url...
// return resourcesUrl(path.mid(3));
#ifdef Q_OS_MAC
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
#elif defined (ANDROID)
static const QString staticResourcePath =
QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/";
#else
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";
#endif
path.replace(0, 3, staticResourcePath);
QUrl expandedURL = QUrl::fromLocalFile(path);
return expandedURL;
}
return fileUrl;
}
const QString& PathUtils::qmlBaseUrl() {
static const QString staticResourcePath = resourcesUrl() + "qml/";
return staticResourcePath;
}
QUrl PathUtils::qmlUrl(const QString& relativeUrl) {
return QUrl(qmlBaseUrl() + relativeUrl);
}
QString PathUtils::getAppDataPath() {
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/";
}
QString PathUtils::getAppLocalDataPath() {
QString overriddenPath = qApp->property(hifi::properties::APP_LOCAL_DATA_PATH).toString();
// return overridden path if set
if (!overriddenPath.isEmpty()) {
return overriddenPath;
}
// otherwise return standard path
#ifdef Q_OS_ANDROID
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/";
#else
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/";
#endif
}
QString PathUtils::getAppDataFilePath(const QString& filename) {
return QDir(getAppDataPath()).absoluteFilePath(filename);
}
QString PathUtils::getAppLocalDataFilePath(const QString& filename) {
return QDir(getAppLocalDataPath()).absoluteFilePath(filename);
}
QString PathUtils::generateTemporaryDir() {
QDir rootTempDir = QDir::tempPath();
QString appName = qApp->applicationName();
for (auto i = 0; i < 64; ++i) {
auto now = std::chrono::system_clock::now().time_since_epoch().count();
auto dirName = TEMP_DIR_FORMAT.arg(appName).arg(qApp->applicationPid()).arg(now);
QDir tempDir = rootTempDir.filePath(dirName);
if (tempDir.mkpath(".")) {
return tempDir.absolutePath();
}
}
return "";
}
bool PathUtils::deleteMyTemporaryDir(QString dirName) {
QDir rootTempDir = QDir::tempPath();
QString appName = qApp->applicationName();
QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?<pid>\\d+)\\-(?<timestamp>\\d+)$" };
auto match = re.match(dirName);
auto pid = match.capturedRef("pid").toLongLong();
if (match.hasMatch() && rootTempDir.exists(dirName) && pid == qApp->applicationPid()) {
auto absoluteDirPath = QDir(rootTempDir.absoluteFilePath(dirName));
bool success = absoluteDirPath.removeRecursively();
if (success) {
qDebug() << " Removing temporary directory: " << absoluteDirPath.absolutePath();
} else {
qDebug() << " Failed to remove temporary directory: " << absoluteDirPath.absolutePath();
}
return success;
}
return false;
}
// Delete all temporary directories for an application
int PathUtils::removeTemporaryApplicationDirs(QString appName) {
if (appName.isNull()) {
appName = qApp->applicationName();
}
auto dirName = TEMP_DIR_FORMAT.arg(appName).arg("*").arg("*");
QDir rootTempDir = QDir::tempPath();
auto dirs = rootTempDir.entryInfoList({ dirName }, QDir::Dirs);
int removed = 0;
for (auto& dir : dirs) {
auto dirName = dir.fileName();
auto absoluteDirPath = QDir(dir.absoluteFilePath());
QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?<pid>\\d+)\\-(?<timestamp>\\d+)$" };
auto match = re.match(dirName);
if (match.hasMatch()) {
auto pid = match.capturedRef("pid").toLongLong();
auto timestamp = match.capturedRef("timestamp");
if (!processIsRunning(pid)) {
qDebug() << " Removing old temporary directory: " << dir.absoluteFilePath();
absoluteDirPath.removeRecursively();
removed++;
} else {
qDebug() << " Not removing (process is running): " << dir.absoluteFilePath();
}
}
}
return removed;
}
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) {
QString fileNameLowered = fileName.toLower();
foreach (const QString possibleExtension, possibleExtensions) {
if (fileNameLowered.endsWith(possibleExtension.toLower())) {
return fileName.left(fileName.count() - possibleExtension.count() - 1);
}
}
return fileName;
}
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions) {
QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions);
QString newestFileName = originalFileName;
QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0);
foreach (QString possibleExtension, possibleExtensions) {
QString fileName = sansExt + "." + possibleExtension;
QFileInfo fileInfo(fileName);
if (fileInfo.exists() && fileInfo.lastModified() > newestTime) {
newestFileName = fileName;
newestTime = fileInfo.lastModified();
}
}
return newestFileName;
}
QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) {
static QString overriddenDefaultScriptsLocation = "";
QString path;
// set overriddenDefaultScriptLocation if it was passed in
if (!newDefaultPath.isEmpty()) {
overriddenDefaultScriptsLocation = newDefaultPath;
}
// use the overridden location if it is set
if (!overriddenDefaultScriptsLocation.isEmpty()) {
path = overriddenDefaultScriptsLocation;
} else {
#if defined(Q_OS_OSX)
path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";
#elif defined(Q_OS_ANDROID)
path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/scripts";
#else
path = QCoreApplication::applicationDirPath() + "/scripts";
#endif
}
// turn the string into a legit QUrl
return QUrl::fromLocalFile(QFileInfo(path).canonicalFilePath());
}
QString PathUtils::stripFilename(const QUrl& url) {
// Guard against meaningless query and fragment parts.
// Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
return url.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment);
}
Qt::CaseSensitivity PathUtils::getFSCaseSensitivity() {
static Qt::CaseSensitivity sensitivity { Qt::CaseSensitive };
static std::once_flag once;
std::call_once(once, [&] {
QString path = defaultScriptsLocation().toLocalFile();
QFileInfo upperFI(path.toUpper());
QFileInfo lowerFI(path.toLower());
sensitivity = (upperFI == lowerFI) ? Qt::CaseInsensitive : Qt::CaseSensitive;
});
return sensitivity;
}
bool PathUtils::isDescendantOf(const QUrl& childURL, const QUrl& parentURL) {
QString child = stripFilename(childURL);
QString parent = stripFilename(parentURL);
return child.startsWith(parent, PathUtils::getFSCaseSensitivity());
}