2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2011-03-30 12:06:05 +02:00
|
|
|
**
|
2021-07-16 11:16:45 +02:00
|
|
|
** Copyright (C) 2021 The Qt Company Ltd.
|
2016-01-15 14:58:39 +01:00
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-03-30 12:06:05 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2011-03-30 12:06:05 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:58:39 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2011-03-30 12:06:05 +02:00
|
|
|
**
|
2016-01-15 14:58:39 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2011-03-30 12:06:05 +02:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2011-03-30 12:06:05 +02:00
|
|
|
|
|
|
|
|
#include "fileutils.h"
|
|
|
|
|
#include "savefile.h"
|
|
|
|
|
|
2014-09-10 14:00:12 +02:00
|
|
|
#include "algorithm.h"
|
2021-05-11 14:34:56 +02:00
|
|
|
#include "commandline.h"
|
2021-06-07 15:47:06 +02:00
|
|
|
#include "environment.h"
|
2021-06-29 16:57:16 +02:00
|
|
|
#include "hostosinfo.h"
|
2011-03-30 12:06:05 +02:00
|
|
|
#include "qtcassert.h"
|
|
|
|
|
|
2017-08-18 14:32:39 +02:00
|
|
|
#include <QDataStream>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDateTime>
|
2020-06-11 15:41:26 +02:00
|
|
|
#include <QDebug>
|
2019-04-04 23:03:03 +03:00
|
|
|
#include <QOperatingSystemVersion>
|
2014-07-07 16:16:49 +02:00
|
|
|
#include <QTimer>
|
2014-07-09 18:21:39 +02:00
|
|
|
#include <QUrl>
|
2019-04-04 23:03:03 +03:00
|
|
|
#include <qplatformdefs.h>
|
2011-03-30 12:06:05 +02:00
|
|
|
|
2017-08-18 14:32:39 +02:00
|
|
|
#ifdef QT_GUI_LIB
|
|
|
|
|
#include <QMessageBox>
|
2022-01-21 13:11:39 +01:00
|
|
|
#include <QRegularExpression>
|
2017-08-18 14:32:39 +02:00
|
|
|
#endif
|
|
|
|
|
|
2013-10-23 22:07:46 +03:00
|
|
|
#ifdef Q_OS_WIN
|
2021-05-05 10:28:07 +02:00
|
|
|
#ifdef QTCREATOR_PCH_H
|
2021-05-01 21:04:48 +02:00
|
|
|
#define CALLBACK WINAPI
|
2021-05-05 10:28:07 +02:00
|
|
|
#endif
|
2013-10-23 22:07:46 +03:00
|
|
|
#include <qt_windows.h>
|
2018-07-19 00:07:58 +03:00
|
|
|
#include <shlobj.h>
|
2013-10-23 22:07:46 +03:00
|
|
|
#endif
|
|
|
|
|
|
2015-09-01 15:59:27 +02:00
|
|
|
#ifdef Q_OS_OSX
|
|
|
|
|
#include "fileutils_mac.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-03-30 12:06:05 +02:00
|
|
|
namespace Utils {
|
|
|
|
|
|
2021-07-16 11:16:45 +02:00
|
|
|
// FileReader
|
2021-06-11 14:33:58 +02:00
|
|
|
|
2011-03-30 12:06:05 +02:00
|
|
|
QByteArray FileReader::fetchQrc(const QString &fileName)
|
|
|
|
|
{
|
2021-05-18 09:47:07 +02:00
|
|
|
QTC_ASSERT(fileName.startsWith(':'), return QByteArray());
|
2011-03-30 12:06:05 +02:00
|
|
|
QFile file(fileName);
|
|
|
|
|
bool ok = file.open(QIODevice::ReadOnly);
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(ok, qWarning() << fileName << "not there!"; return QByteArray());
|
2011-03-30 12:06:05 +02:00
|
|
|
return file.readAll();
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 09:47:07 +02:00
|
|
|
bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode)
|
2011-03-30 12:06:05 +02:00
|
|
|
{
|
2012-04-17 08:01:25 +02:00
|
|
|
QTC_ASSERT(!(mode & ~(QIODevice::ReadOnly | QIODevice::Text)), return false);
|
2011-03-30 12:06:05 +02:00
|
|
|
|
2021-05-18 09:47:07 +02:00
|
|
|
if (filePath.needsDevice()) {
|
|
|
|
|
// TODO: add error handling to FilePath::fileContents
|
|
|
|
|
m_data = filePath.fileContents();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QFile file(filePath.toString());
|
2011-03-30 12:06:05 +02:00
|
|
|
if (!file.open(QIODevice::ReadOnly | mode)) {
|
|
|
|
|
m_errorString = tr("Cannot open %1 for reading: %2").arg(
|
2021-05-18 09:47:07 +02:00
|
|
|
filePath.toUserOutput(), file.errorString());
|
2011-03-30 12:06:05 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
m_data = file.readAll();
|
|
|
|
|
if (file.error() != QFile::NoError) {
|
|
|
|
|
m_errorString = tr("Cannot read %1: %2").arg(
|
2021-05-18 09:47:07 +02:00
|
|
|
filePath.toUserOutput(), file.errorString());
|
2011-03-30 12:06:05 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 09:47:07 +02:00
|
|
|
bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode, QString *errorString)
|
2011-03-30 12:06:05 +02:00
|
|
|
{
|
2021-05-18 09:47:07 +02:00
|
|
|
if (fetch(filePath, mode))
|
2011-03-30 12:06:05 +02:00
|
|
|
return true;
|
|
|
|
|
if (errorString)
|
|
|
|
|
*errorString = m_errorString;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 14:32:39 +02:00
|
|
|
#ifdef QT_GUI_LIB
|
2021-05-18 09:47:07 +02:00
|
|
|
bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode, QWidget *parent)
|
2011-03-30 12:06:05 +02:00
|
|
|
{
|
2021-05-18 09:47:07 +02:00
|
|
|
if (fetch(filePath, mode))
|
2011-03-30 12:06:05 +02:00
|
|
|
return true;
|
|
|
|
|
if (parent)
|
|
|
|
|
QMessageBox::critical(parent, tr("File Error"), m_errorString);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-08-18 14:32:39 +02:00
|
|
|
#endif // QT_GUI_LIB
|
2011-03-30 12:06:05 +02:00
|
|
|
|
2021-07-16 11:16:45 +02:00
|
|
|
// FileSaver
|
|
|
|
|
|
2018-07-23 10:45:40 +02:00
|
|
|
FileSaverBase::FileSaverBase() = default;
|
2011-03-30 12:06:05 +02:00
|
|
|
|
2017-07-03 12:02:26 +02:00
|
|
|
FileSaverBase::~FileSaverBase() = default;
|
|
|
|
|
|
2011-03-30 12:06:05 +02:00
|
|
|
bool FileSaverBase::finalize()
|
|
|
|
|
{
|
|
|
|
|
m_file->close();
|
|
|
|
|
setResult(m_file->error() == QFile::NoError);
|
2017-06-29 15:04:01 +02:00
|
|
|
m_file.reset();
|
2011-03-30 12:06:05 +02:00
|
|
|
return !m_hasError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::finalize(QString *errStr)
|
|
|
|
|
{
|
|
|
|
|
if (finalize())
|
|
|
|
|
return true;
|
|
|
|
|
if (errStr)
|
|
|
|
|
*errStr = errorString();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 14:32:39 +02:00
|
|
|
#ifdef QT_GUI_LIB
|
2011-03-30 12:06:05 +02:00
|
|
|
bool FileSaverBase::finalize(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
if (finalize())
|
|
|
|
|
return true;
|
|
|
|
|
QMessageBox::critical(parent, tr("File Error"), errorString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-08-18 14:32:39 +02:00
|
|
|
#endif // QT_GUI_LIB
|
2011-03-30 12:06:05 +02:00
|
|
|
|
|
|
|
|
bool FileSaverBase::write(const char *data, int len)
|
|
|
|
|
{
|
|
|
|
|
if (m_hasError)
|
|
|
|
|
return false;
|
|
|
|
|
return setResult(m_file->write(data, len) == len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::write(const QByteArray &bytes)
|
|
|
|
|
{
|
|
|
|
|
if (m_hasError)
|
|
|
|
|
return false;
|
|
|
|
|
return setResult(m_file->write(bytes) == bytes.count());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::setResult(bool ok)
|
|
|
|
|
{
|
|
|
|
|
if (!ok && !m_hasError) {
|
2017-06-29 15:16:26 +02:00
|
|
|
if (!m_file->errorString().isEmpty()) {
|
2021-05-18 09:47:07 +02:00
|
|
|
m_errorString = tr("Cannot write file %1: %2")
|
|
|
|
|
.arg(m_filePath.toUserOutput(), m_file->errorString());
|
2017-06-29 15:16:26 +02:00
|
|
|
} else {
|
2021-05-18 09:47:07 +02:00
|
|
|
m_errorString = tr("Cannot write file %1. Disk full?").arg(m_filePath.toUserOutput());
|
2017-06-29 15:16:26 +02:00
|
|
|
}
|
2011-03-30 12:06:05 +02:00
|
|
|
m_hasError = true;
|
|
|
|
|
}
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::setResult(QTextStream *stream)
|
|
|
|
|
{
|
|
|
|
|
stream->flush();
|
|
|
|
|
return setResult(stream->status() == QTextStream::Ok);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::setResult(QDataStream *stream)
|
|
|
|
|
{
|
2011-04-18 15:40:10 +02:00
|
|
|
return setResult(stream->status() == QDataStream::Ok);
|
2011-03-30 12:06:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaverBase::setResult(QXmlStreamWriter *stream)
|
|
|
|
|
{
|
|
|
|
|
return setResult(!stream->hasError());
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-16 11:16:45 +02:00
|
|
|
// FileSaver
|
2011-03-30 12:06:05 +02:00
|
|
|
|
2021-05-18 09:47:07 +02:00
|
|
|
FileSaver::FileSaver(const FilePath &filePath, QIODevice::OpenMode mode)
|
2011-03-30 12:06:05 +02:00
|
|
|
{
|
2021-05-18 09:47:07 +02:00
|
|
|
m_filePath = filePath;
|
2016-10-07 10:34:40 +02:00
|
|
|
// Workaround an assert in Qt -- and provide a useful error message, too:
|
2021-07-23 16:30:40 +02:00
|
|
|
if (m_filePath.osType() == OsType::OsTypeWindows) {
|
2016-10-07 10:34:40 +02:00
|
|
|
// Taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
|
|
|
|
static const QStringList reservedNames
|
2017-02-22 15:09:35 +01:00
|
|
|
= {"CON", "PRN", "AUX", "NUL",
|
|
|
|
|
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
|
|
|
|
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
|
2021-06-03 12:53:40 +02:00
|
|
|
const QString fn = filePath.baseName().toUpper();
|
2021-05-06 16:42:02 +02:00
|
|
|
if (reservedNames.contains(fn)) {
|
2021-05-18 09:47:07 +02:00
|
|
|
m_errorString = tr("%1: Is a reserved filename on Windows. Cannot save.")
|
2021-07-23 16:30:40 +02:00
|
|
|
.arg(filePath.toUserOutput());
|
2021-05-06 16:42:02 +02:00
|
|
|
m_hasError = true;
|
|
|
|
|
return;
|
2016-10-07 10:34:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-07-23 16:30:40 +02:00
|
|
|
if (filePath.needsDevice()) {
|
|
|
|
|
// Write to a local temporary file first. Actual saving to the selected location
|
|
|
|
|
// is done via m_filePath.writeFileContents() in finalize()
|
|
|
|
|
m_isSafe = false;
|
|
|
|
|
auto tf = new QTemporaryFile(QDir::tempPath() + "/remotefilesaver-XXXXXX");
|
|
|
|
|
tf->setAutoRemove(false);
|
|
|
|
|
m_file.reset(tf);
|
|
|
|
|
} else if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
|
|
|
|
|
m_file.reset(new QFile{filePath.path()});
|
2011-03-30 12:06:05 +02:00
|
|
|
m_isSafe = false;
|
|
|
|
|
} else {
|
2021-07-23 16:30:40 +02:00
|
|
|
m_file.reset(new SaveFile{filePath.path()});
|
2011-03-30 12:06:05 +02:00
|
|
|
m_isSafe = true;
|
|
|
|
|
}
|
|
|
|
|
if (!m_file->open(QIODevice::WriteOnly | mode)) {
|
2021-05-18 09:47:07 +02:00
|
|
|
QString err = filePath.exists() ?
|
2011-03-30 12:06:05 +02:00
|
|
|
tr("Cannot overwrite file %1: %2") : tr("Cannot create file %1: %2");
|
2021-05-18 09:47:07 +02:00
|
|
|
m_errorString = err.arg(filePath.toUserOutput(), m_file->errorString());
|
2011-03-30 12:06:05 +02:00
|
|
|
m_hasError = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileSaver::finalize()
|
|
|
|
|
{
|
2021-07-23 16:30:40 +02:00
|
|
|
if (m_filePath.needsDevice()) {
|
|
|
|
|
m_file->close();
|
|
|
|
|
m_file->open(QIODevice::ReadOnly);
|
|
|
|
|
const QByteArray data = m_file->readAll();
|
|
|
|
|
const bool res = m_filePath.writeFileContents(data);
|
|
|
|
|
m_file->remove();
|
|
|
|
|
m_file.reset();
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 12:06:05 +02:00
|
|
|
if (!m_isSafe)
|
|
|
|
|
return FileSaverBase::finalize();
|
|
|
|
|
|
2018-07-19 16:39:41 +02:00
|
|
|
auto sf = static_cast<SaveFile *>(m_file.get());
|
2013-06-04 18:00:42 +02:00
|
|
|
if (m_hasError) {
|
|
|
|
|
if (sf->isOpen())
|
|
|
|
|
sf->rollback();
|
|
|
|
|
} else {
|
2011-03-30 12:06:05 +02:00
|
|
|
setResult(sf->commit());
|
2013-06-04 18:00:42 +02:00
|
|
|
}
|
2017-06-29 15:04:01 +02:00
|
|
|
m_file.reset();
|
2011-03-30 12:06:05 +02:00
|
|
|
return !m_hasError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TempFileSaver::TempFileSaver(const QString &templ)
|
|
|
|
|
{
|
2017-06-29 15:04:01 +02:00
|
|
|
m_file.reset(new QTemporaryFile{});
|
2018-07-19 16:39:41 +02:00
|
|
|
auto tempFile = static_cast<QTemporaryFile *>(m_file.get());
|
2011-03-30 12:06:05 +02:00
|
|
|
if (!templ.isEmpty())
|
|
|
|
|
tempFile->setFileTemplate(templ);
|
|
|
|
|
tempFile->setAutoRemove(false);
|
|
|
|
|
if (!tempFile->open()) {
|
|
|
|
|
m_errorString = tr("Cannot create temporary file in %1: %2").arg(
|
|
|
|
|
QDir::toNativeSeparators(QFileInfo(tempFile->fileTemplate()).absolutePath()),
|
|
|
|
|
tempFile->errorString());
|
|
|
|
|
m_hasError = true;
|
|
|
|
|
}
|
2021-05-18 09:47:07 +02:00
|
|
|
m_filePath = FilePath::fromString(tempFile->fileName());
|
2011-03-30 12:06:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TempFileSaver::~TempFileSaver()
|
|
|
|
|
{
|
2017-06-29 15:04:01 +02:00
|
|
|
m_file.reset();
|
2011-03-30 12:06:05 +02:00
|
|
|
if (m_autoRemove)
|
2021-05-18 09:47:07 +02:00
|
|
|
QFile::remove(m_filePath.toString());
|
2011-03-30 12:06:05 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-16 11:16:45 +02:00
|
|
|
#ifdef QT_GUI_LIB
|
2021-08-09 08:53:40 +02:00
|
|
|
FileUtils::CopyAskingForOverwrite::CopyAskingForOverwrite(QWidget *dialogParent, const std::function<void (FilePath)> &postOperation)
|
2021-07-16 11:16:45 +02:00
|
|
|
: m_parent(dialogParent)
|
|
|
|
|
, m_postOperation(postOperation)
|
|
|
|
|
{}
|
2021-06-04 07:59:00 +02:00
|
|
|
|
2021-08-09 08:53:40 +02:00
|
|
|
bool FileUtils::CopyAskingForOverwrite::operator()(const FilePath &src,
|
|
|
|
|
const FilePath &dest,
|
2021-07-16 11:16:45 +02:00
|
|
|
QString *error)
|
2020-06-11 15:41:26 +02:00
|
|
|
{
|
|
|
|
|
bool copyFile = true;
|
|
|
|
|
if (dest.exists()) {
|
|
|
|
|
if (m_skipAll)
|
|
|
|
|
copyFile = false;
|
|
|
|
|
else if (!m_overwriteAll) {
|
|
|
|
|
const int res = QMessageBox::question(
|
|
|
|
|
m_parent,
|
|
|
|
|
QCoreApplication::translate("Utils::FileUtils", "Overwrite File?"),
|
|
|
|
|
QCoreApplication::translate("Utils::FileUtils", "Overwrite existing file \"%1\"?")
|
2021-08-09 08:53:40 +02:00
|
|
|
.arg(dest.toUserOutput()),
|
2020-06-11 15:41:26 +02:00
|
|
|
QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll
|
|
|
|
|
| QMessageBox::Cancel);
|
|
|
|
|
if (res == QMessageBox::Cancel) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (res == QMessageBox::No) {
|
|
|
|
|
copyFile = false;
|
|
|
|
|
} else if (res == QMessageBox::NoToAll) {
|
|
|
|
|
m_skipAll = true;
|
|
|
|
|
copyFile = false;
|
|
|
|
|
} else if (res == QMessageBox::YesToAll) {
|
|
|
|
|
m_overwriteAll = true;
|
|
|
|
|
}
|
|
|
|
|
if (copyFile)
|
2021-08-09 08:53:40 +02:00
|
|
|
dest.removeFile();
|
2020-06-11 15:41:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (copyFile) {
|
2021-08-09 08:53:40 +02:00
|
|
|
dest.parentDir().ensureWritableDir();
|
|
|
|
|
if (!src.copyFile(dest)) {
|
2020-06-11 15:41:26 +02:00
|
|
|
if (error) {
|
|
|
|
|
*error = QCoreApplication::translate("Utils::FileUtils",
|
|
|
|
|
"Could not copy file \"%1\" to \"%2\".")
|
2021-08-09 08:53:40 +02:00
|
|
|
.arg(src.toUserOutput(), dest.toUserOutput());
|
2020-06-11 15:41:26 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-02-12 16:06:24 +01:00
|
|
|
if (m_postOperation)
|
|
|
|
|
m_postOperation(dest);
|
2020-06-11 15:41:26 +02:00
|
|
|
}
|
|
|
|
|
m_files.append(dest.absoluteFilePath());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 16:49:42 +02:00
|
|
|
FilePaths FileUtils::CopyAskingForOverwrite::files() const
|
2020-06-11 15:41:26 +02:00
|
|
|
{
|
2021-08-09 08:53:40 +02:00
|
|
|
return m_files;
|
2020-06-11 15:41:26 +02:00
|
|
|
}
|
|
|
|
|
#endif // QT_GUI_LIB
|
|
|
|
|
|
2021-07-16 11:16:45 +02:00
|
|
|
// Copied from qfilesystemengine_win.cpp
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
|
|
|
|
|
// File ID for Windows up to version 7.
|
|
|
|
|
static inline QByteArray fileIdWin7(HANDLE handle)
|
|
|
|
|
{
|
|
|
|
|
BY_HANDLE_FILE_INFORMATION info;
|
|
|
|
|
if (GetFileInformationByHandle(handle, &info)) {
|
|
|
|
|
char buffer[sizeof "01234567:0123456701234567\0"];
|
|
|
|
|
qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
|
|
|
|
|
info.dwVolumeSerialNumber,
|
|
|
|
|
info.nFileIndexHigh,
|
|
|
|
|
info.nFileIndexLow);
|
|
|
|
|
return QByteArray(buffer);
|
|
|
|
|
}
|
|
|
|
|
return QByteArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// File ID for Windows starting from version 8.
|
|
|
|
|
static QByteArray fileIdWin8(HANDLE handle)
|
|
|
|
|
{
|
|
|
|
|
QByteArray result;
|
|
|
|
|
FILE_ID_INFO infoEx;
|
|
|
|
|
if (GetFileInformationByHandleEx(handle,
|
|
|
|
|
static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
|
|
|
|
|
&infoEx, sizeof(FILE_ID_INFO))) {
|
|
|
|
|
result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
|
|
|
|
|
result += ':';
|
|
|
|
|
// Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
|
|
|
|
|
result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QByteArray fileIdWin(HANDLE fHandle)
|
|
|
|
|
{
|
|
|
|
|
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ?
|
|
|
|
|
fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
QByteArray FileUtils::fileId(const FilePath &fileName)
|
|
|
|
|
{
|
|
|
|
|
QByteArray result;
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
const HANDLE handle =
|
|
|
|
|
CreateFile((wchar_t*)fileName.toUserOutput().utf16(), 0,
|
|
|
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
|
|
|
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
|
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
|
|
|
result = fileIdWin(handle);
|
|
|
|
|
CloseHandle(handle);
|
|
|
|
|
}
|
|
|
|
|
#else // Copied from qfilesystemengine_unix.cpp
|
|
|
|
|
if (Q_UNLIKELY(fileName.isEmpty()))
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
QT_STATBUF statResult;
|
|
|
|
|
if (QT_STAT(fileName.toString().toLocal8Bit().constData(), &statResult))
|
|
|
|
|
return result;
|
|
|
|
|
result = QByteArray::number(quint64(statResult.st_dev), 16);
|
|
|
|
|
result += ':';
|
|
|
|
|
result += QByteArray::number(quint64(statResult.st_ino));
|
|
|
|
|
#endif
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-19 15:37:39 +01:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
template <>
|
2018-06-05 14:22:26 +02:00
|
|
|
void withNtfsPermissions(const std::function<void()> &task)
|
2018-02-19 15:37:39 +01:00
|
|
|
{
|
|
|
|
|
qt_ntfs_permission_lookup++;
|
|
|
|
|
task();
|
|
|
|
|
qt_ntfs_permission_lookup--;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-04-27 12:05:13 +02:00
|
|
|
|
2019-12-17 14:18:14 +01:00
|
|
|
|
2021-07-16 15:24:26 +02:00
|
|
|
#ifdef QT_WIDGETS_LIB
|
|
|
|
|
|
|
|
|
|
static std::function<QWidget *()> s_dialogParentGetter;
|
|
|
|
|
|
|
|
|
|
void FileUtils::setDialogParentGetter(const std::function<QWidget *()> &getter)
|
|
|
|
|
{
|
|
|
|
|
s_dialogParentGetter = getter;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 09:01:53 +02:00
|
|
|
static QWidget *dialogParent(QWidget *parent)
|
2021-07-16 15:24:26 +02:00
|
|
|
{
|
2021-08-11 09:01:53 +02:00
|
|
|
return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
|
2021-07-16 15:24:26 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-11 09:01:53 +02:00
|
|
|
FilePath FileUtils::getOpenFilePath(QWidget *parent,
|
|
|
|
|
const QString &caption,
|
2021-07-16 15:24:26 +02:00
|
|
|
const FilePath &dir,
|
|
|
|
|
const QString &filter,
|
|
|
|
|
QString *selectedFilter,
|
|
|
|
|
QFileDialog::Options options)
|
|
|
|
|
{
|
2021-08-11 09:01:53 +02:00
|
|
|
const QString result = QFileDialog::getOpenFileName(dialogParent(parent),
|
2021-07-16 15:24:26 +02:00
|
|
|
caption,
|
|
|
|
|
dir.toString(),
|
|
|
|
|
filter,
|
|
|
|
|
selectedFilter,
|
|
|
|
|
options);
|
|
|
|
|
return FilePath::fromString(result);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 09:01:53 +02:00
|
|
|
FilePath FileUtils::getSaveFilePath(QWidget *parent,
|
|
|
|
|
const QString &caption,
|
|
|
|
|
const FilePath &dir,
|
|
|
|
|
const QString &filter,
|
|
|
|
|
QString *selectedFilter,
|
|
|
|
|
QFileDialog::Options options)
|
2021-07-16 15:24:26 +02:00
|
|
|
{
|
2021-08-11 09:01:53 +02:00
|
|
|
const QString result = QFileDialog::getSaveFileName(dialogParent(parent),
|
2021-07-16 15:24:26 +02:00
|
|
|
caption,
|
|
|
|
|
dir.toString(),
|
|
|
|
|
filter,
|
|
|
|
|
selectedFilter,
|
|
|
|
|
options);
|
|
|
|
|
return FilePath::fromString(result);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 09:01:53 +02:00
|
|
|
FilePath FileUtils::getExistingDirectory(QWidget *parent,
|
|
|
|
|
const QString &caption,
|
|
|
|
|
const FilePath &dir,
|
|
|
|
|
QFileDialog::Options options)
|
2021-07-16 15:24:26 +02:00
|
|
|
{
|
2021-08-11 09:01:53 +02:00
|
|
|
const QString result = QFileDialog::getExistingDirectory(dialogParent(parent),
|
2021-07-16 15:24:26 +02:00
|
|
|
caption,
|
|
|
|
|
dir.toString(),
|
|
|
|
|
options);
|
|
|
|
|
return FilePath::fromString(result);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 09:01:53 +02:00
|
|
|
FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
|
|
|
|
|
const QString &caption,
|
2021-07-16 15:24:26 +02:00
|
|
|
const FilePath &dir,
|
|
|
|
|
const QString &filter,
|
|
|
|
|
QString *selectedFilter,
|
|
|
|
|
QFileDialog::Options options)
|
|
|
|
|
{
|
2021-08-11 09:01:53 +02:00
|
|
|
const QStringList result = QFileDialog::getOpenFileNames(dialogParent(parent),
|
2021-07-16 15:24:26 +02:00
|
|
|
caption,
|
|
|
|
|
dir.toString(),
|
|
|
|
|
filter,
|
|
|
|
|
selectedFilter,
|
|
|
|
|
options);
|
|
|
|
|
return transform(result, &FilePath::fromString);
|
|
|
|
|
}
|
2022-01-21 13:11:39 +01:00
|
|
|
|
|
|
|
|
// Used on 'ls' output on unix-like systems.
|
|
|
|
|
void FileUtils::iterateLsOutput(const FilePath &base,
|
|
|
|
|
const QStringList &entries,
|
|
|
|
|
const FileFilter &filter,
|
|
|
|
|
const std::function<bool (const FilePath &)> &callBack)
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(filter.iteratorFlags != QDirIterator::NoIteratorFlags); // FIXME: Not supported yet below.
|
|
|
|
|
|
|
|
|
|
const QList<QRegularExpression> nameRegexps =
|
|
|
|
|
transform(filter.nameFilters, [](const QString &filter) {
|
|
|
|
|
QRegularExpression re;
|
|
|
|
|
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
|
|
|
|
|
QTC_CHECK(re.isValid());
|
|
|
|
|
return re;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const auto nameMatches = [&nameRegexps](const QString &fileName) {
|
|
|
|
|
for (const QRegularExpression &re : nameRegexps) {
|
|
|
|
|
const QRegularExpressionMatch match = re.match(fileName);
|
|
|
|
|
if (match.hasMatch())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return nameRegexps.isEmpty();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// FIXME: Handle filters. For now bark on unsupported options.
|
|
|
|
|
QTC_CHECK(filter.fileFilters == QDir::NoFilter);
|
|
|
|
|
|
|
|
|
|
for (const QString &entry : entries) {
|
|
|
|
|
if (!nameMatches(entry))
|
|
|
|
|
continue;
|
|
|
|
|
if (!callBack(base.pathAppended(entry)))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-16 15:24:26 +02:00
|
|
|
#endif // QT_WIDGETS_LIB
|
|
|
|
|
|
|
|
|
|
} // namespace Utils
|