QFileInfo: add isOther()
"Special" is interpreted similar to the logic used in QDirListing::IteratorFlag::ExcludeOther. This also matches how std::filesystem::is_other() is supposed to work except that in Qt .lnk files on Windows are treated as "other". [ChangeLog][QtCore][QFileInfo] Added isOther() method. Fixes: QTBUG-133331 Change-Id: I2550fd37cd495b4593cacf704ce63da7d1c2addd Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
c553f39e3d
commit
9a83c2b88c
@ -1178,6 +1178,57 @@ bool QFileInfo::isSymbolicLink() const
|
||||
[d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.10
|
||||
|
||||
Returns \c true if this QFileInfo refers to a file system entry that is
|
||||
\e not a directory, regular file or symbolic link. Otherwise returns
|
||||
\c false.
|
||||
|
||||
If this QFileInfo refers to a nonexistent entry, this method returns
|
||||
\c false.
|
||||
|
||||
If the entry is a dangling symbolic link (the target doesn't exist), this
|
||||
method returns \c false. For a non-dangling symbolic link, this function
|
||||
returns information about the target, not the symbolic link.
|
||||
|
||||
On Unix a special (other) file system entry is a FIFO, socket, character
|
||||
device, or block device. For more details, see the
|
||||
\l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknod.html}{\c mknod}
|
||||
manual page.
|
||||
|
||||
On Windows (for historical reasons, see \l{Symbolic Links and Shortcuts})
|
||||
this method returns \c true for \c .lnk files.
|
||||
|
||||
\sa isDir(), isFile(), isSymLink(), QDirListing::IteratorFlag::ExcludeOther
|
||||
*/
|
||||
bool QFileInfo::isOther() const
|
||||
{
|
||||
Q_D(const QFileInfo);
|
||||
using M = QFileSystemMetaData::MetaDataFlag;
|
||||
// No M::LinkType to make QFileSystemEngine always call stat().
|
||||
// M::WinLnkType is only relevant on Windows for '.lnk' files
|
||||
constexpr auto mdFlags = M::ExistsAttribute | M::DirectoryType | M::FileType | M::WinLnkType;
|
||||
|
||||
auto fsLambda = [d]() {
|
||||
// Check isLnkFile() first because currently exists() returns false for
|
||||
// a broken '.lnk' where the target doesn't exist.
|
||||
if (d->metaData.isLnkFile()) // Always false on non-Windows OSes
|
||||
return true;
|
||||
return d->metaData.exists() && !d->metaData.isDirectory() && !d->metaData.isFile();
|
||||
};
|
||||
|
||||
auto engineLambda = [d]() {
|
||||
using F = QAbstractFileEngine::FileFlag;
|
||||
return d->getFileFlags(F::ExistsFlag)
|
||||
&& !d->getFileFlags(F::LinkType) // QAFE doesn't have a separate type for ".lnk" file
|
||||
&& !d->getFileFlags(F::DirectoryType)
|
||||
&& !d->getFileFlags(F::FileType);
|
||||
};
|
||||
|
||||
return d->checkAttribute<bool>(mdFlags, std::move(fsLambda), std::move(engineLambda));
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if this object points to a shortcut;
|
||||
otherwise returns \c false.
|
||||
|
@ -125,6 +125,7 @@ public:
|
||||
bool isDir() const;
|
||||
bool isSymLink() const;
|
||||
bool isSymbolicLink() const;
|
||||
bool isOther() const;
|
||||
bool isShortcut() const;
|
||||
bool isAlias() const;
|
||||
bool isJunction() const;
|
||||
|
@ -133,6 +133,9 @@ private slots:
|
||||
void isDir_data();
|
||||
void isDir();
|
||||
|
||||
void isOther_data();
|
||||
void isOther();
|
||||
|
||||
void isRoot_data();
|
||||
void isRoot();
|
||||
|
||||
@ -258,6 +261,7 @@ private:
|
||||
QString m_resourcesDir;
|
||||
QTemporaryDir m_dir;
|
||||
QSharedPointer<QTemporaryDir> m_dataDir;
|
||||
QTemporaryDir m_tempSubDir;
|
||||
};
|
||||
|
||||
void tst_QFileInfo::initTestCase()
|
||||
@ -412,6 +416,107 @@ void tst_QFileInfo::isDir()
|
||||
QVERIFY(!isDir);
|
||||
}
|
||||
|
||||
void tst_QFileInfo::isOther_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<bool>("expected");
|
||||
|
||||
m_tempSubDir.remove();
|
||||
m_tempSubDir = QTemporaryDir(m_dir.path() + "/isother_test_dir.XXXXXX"_L1);
|
||||
|
||||
const QString prefix = m_tempSubDir.path() + u'/';
|
||||
|
||||
const QString filePath = prefix + "file"_L1;
|
||||
QFile file(filePath);
|
||||
if (file.open(QFile::WriteOnly)) {
|
||||
file.write("JAJAJAA");
|
||||
file.close();
|
||||
}
|
||||
const QString linkToFile = prefix + "link-to-file"_L1;
|
||||
file.link(linkToFile);
|
||||
|
||||
const QString dirPath = m_dir.path();
|
||||
const QString linkToDir = prefix + "link-to-dir"_L1;
|
||||
QFile::link(dirPath, linkToDir);
|
||||
|
||||
const QString brokenLink = prefix + "broken-symlink"_L1;
|
||||
QFile dummy(prefix + "dummyfile"_L1);
|
||||
(void)dummy.open(QIODevice::WriteOnly);
|
||||
dummy.link(brokenLink);
|
||||
dummy.remove();
|
||||
|
||||
QTest::newRow("regular-file") << filePath << false;
|
||||
QTest::newRow("symlink-to-regular-file") << linkToFile << false;
|
||||
QTest::newRow("dir") << dirPath << false;
|
||||
QTest::newRow("symlink-to-dir") << linkToDir << false;
|
||||
QTest::newRow("broken-symlink") << brokenLink << false;
|
||||
|
||||
QTest::newRow("qresources-file") << ":/tst_qfileinfo/resources/file1" << false;
|
||||
QTest::newRow("qresources-broken-dir") << ":/I/do_not_expect_this_path_to_exist/" << false;
|
||||
QTest::newRow("qresources-broken-file") << ":/tst_qfileinfo/resources/ghost-file" << false;
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
auto addSpecialRow = [&prefix](const QString &s, uint type) {
|
||||
const QString name = prefix + s;
|
||||
if (::mknod(name.toLatin1().constData(), 0777 | type, dev_t{}) == 0)
|
||||
QTest::addRow("%s", qPrintable(name)) << name << true;
|
||||
else
|
||||
qDebug("mknod call failed: %s", strerror(errno));
|
||||
};
|
||||
addSpecialRow("fifo-test", S_IFIFO);
|
||||
addSpecialRow("socket-test", S_IFSOCK);
|
||||
|
||||
const QString linkToSpecial = prefix + "link-to-dev-null"_L1;
|
||||
QFile::link(u"/dev/null"_s, linkToSpecial);
|
||||
|
||||
QTest::newRow("character-device") << u"/dev/null"_s << true;
|
||||
QTest::newRow("symlink-to-special") << linkToSpecial << true;
|
||||
#elif defined(Q_OS_WIN)
|
||||
const QString name = prefix + "win-CreateSymbolicLink-file"_L1;
|
||||
const auto res = FileSystem::createSymbolicLink(name.toLatin1().constData(),
|
||||
file.fileName().toLatin1().constData());
|
||||
if (res.dwErr == ERROR_SUCCESS)
|
||||
QTest::newRow("win-CreateSymbolicLink-file") << name << false;
|
||||
else if (res.dwErr == ERROR_PRIVILEGE_NOT_HELD)
|
||||
qDebug() << msgInsufficientPrivileges(res.errorMessage);
|
||||
else
|
||||
qDebug() << res.errorMessage;
|
||||
|
||||
const QString winLnk = prefix + "winLnk.lnk"_L1;
|
||||
QFile lnkTarget(prefix + "lnkTarget"_L1);
|
||||
(void)lnkTarget.open(QIODevice::WriteOnly);
|
||||
lnkTarget.link(winLnk);
|
||||
QTest::newRow("winLnk") << winLnk << true;
|
||||
|
||||
const QString brokenWinLnk = prefix + "broken_winLnk.lnk"_L1;
|
||||
const QString dummyPath = prefix + "lnkDummy"_L1;
|
||||
lnkTarget.copy(dummyPath);
|
||||
QFile lnkDummy(dummyPath);
|
||||
lnkDummy.link(brokenWinLnk);
|
||||
lnkDummy.remove();
|
||||
QTest::newRow("broken-winLnk") << brokenWinLnk << true;
|
||||
#endif // Q_OS_UNIX
|
||||
}
|
||||
|
||||
void tst_QFileInfo::isOther()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFETCH(bool, expected);
|
||||
|
||||
QFileInfo info(path);
|
||||
QCOMPARE(info.isOther(), expected);
|
||||
if (!QByteArrayView{QTest::currentDataTag()}.contains("broken"))
|
||||
QVERIFY(info.exists());
|
||||
|
||||
#if QT_CONFIG(cxx17_filesystem) // This code doesn't work in QNX on the CI
|
||||
if (QByteArrayView{QTest::currentDataTag()}.contains("winLnk"))
|
||||
QEXPECT_FAIL("", "QFileInfo::isOther() returns true for '.lnk' files on Windows, "
|
||||
"std::filesystem::is_other() returns false", Continue);
|
||||
|
||||
QCOMPARE_EQ(info.isOther(), std::filesystem::is_other(path.toStdString()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QFileInfo::isRoot_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
|
Loading…
x
Reference in New Issue
Block a user