summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qfileinfo.cpp123
-rw-r--r--src/corelib/io/qfileinfo.h17
-rw-r--r--tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp166
3 files changed, 287 insertions, 19 deletions
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index a5a3bc8b3e..b720966d8f 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -256,10 +256,9 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
\snippet code/src_corelib_io_qfileinfo.cpp 0
- On Windows, symlinks (shortcuts) are \c .lnk files. The reported
- size() is that of the symlink (not the link's target), and
- opening a symlink using QFile opens the \c .lnk file. For
- example:
+ On Windows, shortcuts are \c .lnk files. The reported size() is that of
+ the shortcut (not the link's target), and opening a shortcut using QFile
+ opens the \c .lnk file. For example:
\snippet code/src_corelib_io_qfileinfo.cpp 1
@@ -312,6 +311,19 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
*/
/*!
+ \enum QFileInfo::FileType
+
+ This enum is returned by type() to describe the type of the file system
+ entity described by the QFileInfo object.
+
+ \value Unknown The object refers to an unknown item.
+ \value Regular The object refers to a regular file.
+ \value Directory The object refers to a directory.
+ \value SymbolicLink The object refers to a symbolic link.
+ \value Shortcut The object refers to a shortcut.
+*/
+
+/*!
\fn QFileInfo &QFileInfo::operator=(QFileInfo &&other)
Move-assigns \a other to this QFileInfo instance.
@@ -996,11 +1008,7 @@ bool QFileInfo::isNativePath() const
*/
bool QFileInfo::isFile() const
{
- Q_D(const QFileInfo);
- return d->checkAttribute<bool>(
- QFileSystemMetaData::FileType,
- [d]() { return d->metaData.isFile(); },
- [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); });
+ return (type() & FileTypeMask) == Regular;
}
/*!
@@ -1011,11 +1019,7 @@ bool QFileInfo::isFile() const
*/
bool QFileInfo::isDir() const
{
- Q_D(const QFileInfo);
- return d->checkAttribute<bool>(
- QFileSystemMetaData::DirectoryType,
- [d]() { return d->metaData.isDirectory(); },
- [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); });
+ return (type() & FileTypeMask) == Directory;
}
@@ -1036,7 +1040,7 @@ bool QFileInfo::isBundle() const
}
/*!
- Returns \c true if this object points to a symbolic link;
+ Returns \c true if this object points to a symbolic link or shortcut;
otherwise returns \c false.
Symbolic links exist on Unix (including \macos and iOS) and Windows
@@ -1066,6 +1070,48 @@ bool QFileInfo::isSymLink() const
}
/*!
+ \fn bool QFileInfo::isSymbolicLink() const
+
+ Returns \c true if this object points to a symbolic link;
+ otherwise returns \c false.
+
+ Symbolic links exist on Unix (including \macos and iOS) and Windows
+ (NTFS-symlink) and are typically created by the \c{ln -s} or \c{mklink}
+ commands, respectively.
+
+ Unix handles symlinks transparently. Opening a symbolic link effectively
+ opens the \l{symLinkTarget()}{link's target}.
+
+ In contrast to isSymLink(), false will be returned for shortcuts
+ (\c *.lnk files) on Windows. Use QFileInfo::isShortcut() instead.
+
+ \note If the symlink points to a non existing file, exists() returns
+ false.
+
+ \sa isFile(), isDir(), isShortcut(), symLinkTarget()
+*/
+
+/*!
+ \fn bool QFileInfo::isShortcut() const
+
+ Returns \c true if this object points to a shortcut;
+ otherwise returns \c false.
+
+ Shortcuts only exist on Windows and are typically \c .lnk files.
+ For instance, true will be returned for shortcuts (\c *.lnk files) on
+ Windows, but false will be returned on Unix (including \macos and iOS).
+
+ The shortcut (.lnk) files are treated as regular files. Opening those will
+ open the \c .lnk file itself. In order to open the file a shortcut
+ references to, it must uses symLinkTarget() on a shortcut.
+
+ \note Even if a shortcut (broken shortcut) points to a non existing file,
+ isShortcut() returns true.
+
+ \sa isFile(), isDir(), isSymbolicLink(), symLinkTarget()
+*/
+
+/*!
Returns \c true if the object points to a directory or to a symbolic
link to a directory, and that directory is the root directory; otherwise
returns \c false.
@@ -1268,6 +1314,53 @@ qint64 QFileInfo::size() const
});
}
+/*!
+ Returns the QFileInfo::FileTypes.
+
+ QFileInfo::FileTypes combines with an indirection flag (link type) and a
+ base type it refers to.
+
+ For example, \c SymbolicLink combines with \c Regular meaning a symlink to
+ a regular file.
+
+ In addition, FileTypeMask and LinkTypeMask are used to extract the base
+ type and link type respectively.
+
+ \sa isFile(), isDir(), isShortcut(), isSymbolicLink()
+*/
+QFileInfo::FileTypes QFileInfo::type() const
+{
+ Q_D(const QFileInfo);
+
+ QFileInfo::FileTypes type = QFileInfo::Unknown;
+ if (d->checkAttribute<bool>(
+ QFileSystemMetaData::LegacyLinkType,
+ [d]() { return d->metaData.isLnkFile(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); })) {
+ type = QFileInfo::Shortcut;
+ } else if (d->checkAttribute<bool>(
+ QFileSystemMetaData::LegacyLinkType,
+ [d]() { return d->metaData.isLink(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); })) {
+ type = QFileInfo::SymbolicLink;
+ }
+
+ if (d->checkAttribute<bool>(
+ QFileSystemMetaData::DirectoryType,
+ [d]() { return d->metaData.isDirectory(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); })) {
+ return type | QFileInfo::Directory;
+ }
+
+ if (d->checkAttribute<bool>(
+ QFileSystemMetaData::FileType,
+ [d]() { return d->metaData.isFile(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); })) {
+ return type | QFileInfo::Regular;
+ }
+ return type;
+}
+
#if QT_DEPRECATED_SINCE(5, 10)
/*!
\deprecated
diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h
index 111517325d..1cbeafdd4a 100644
--- a/src/corelib/io/qfileinfo.h
+++ b/src/corelib/io/qfileinfo.h
@@ -66,6 +66,20 @@ public:
QFileInfo(const QFileInfo &fileinfo);
~QFileInfo();
+ enum FileType {
+ Unknown,
+ // base type
+ Regular,
+ Directory,
+ // indirection flag
+ SymbolicLink = 0x10,
+ Shortcut = 0x20,
+ // mask
+ FileTypeMask = 0x0f,
+ LinkTypeMask = 0xf0
+ };
+ Q_DECLARE_FLAGS(FileTypes, FileType)
+
QFileInfo &operator=(const QFileInfo &fileinfo);
QFileInfo &operator=(QFileInfo &&other) noexcept { swap(other); return *this; }
@@ -111,6 +125,8 @@ public:
bool isFile() const;
bool isDir() const;
bool isSymLink() const;
+ inline bool isSymbolicLink() const { return type() & SymbolicLink; }
+ inline bool isShortcut() const { return type() & Shortcut; }
bool isRoot() const;
bool isBundle() const;
@@ -129,6 +145,7 @@ public:
QFile::Permissions permissions() const;
qint64 size() const;
+ FileTypes type() const;
// ### Qt6: inline these functions
#if QT_DEPRECATED_SINCE(5, 10)
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
index 60e320c44d..6fcfe87c83 100644
--- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
@@ -236,6 +236,8 @@ private slots:
void isSymLink_data();
void isSymLink();
+ void link_data();
+ void link();
void isHidden_data();
void isHidden();
@@ -277,6 +279,9 @@ private slots:
void invalidState();
void nonExistingFile();
+ void type_data();
+ void type();
+
private:
const QString m_currentDir;
QString m_sourceFile;
@@ -1337,6 +1342,88 @@ void tst_QFileInfo::isSymLink()
#endif
}
+Q_DECLARE_METATYPE(QFileInfo::FileType)
+
+void tst_QFileInfo::link_data()
+{
+ QFile::remove("link");
+ QFile::remove("link.lnk");
+ QFile::remove("brokenlink");
+ QFile::remove("brokenlink.lnk");
+ QFile::remove("dummyfile");
+ QFile::remove("relative/link");
+
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QFileInfo::FileType>("linkType");
+ QTest::addColumn<QString>("linkTarget");
+
+ QFile file1(m_sourceFile);
+ QFile file2("dummyfile");
+ file2.open(QIODevice::WriteOnly);
+
+ QTest::newRow("existent file") << m_sourceFile << QFileInfo::Unknown << "";
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+ // windows shortcuts
+ QVERIFY(file1.link("link.lnk"));
+ QTest::newRow("link.lnk")
+ << "link.lnk" << QFileInfo::Shortcut << QFileInfo(m_sourceFile).absoluteFilePath();
+
+ QVERIFY(file2.link("brokenlink.lnk"));
+ QTest::newRow("broken link.lnk")
+ << "brokenlink.lnk" << QFileInfo::Shortcut << QFileInfo("dummyfile").absoluteFilePath();
+#endif
+
+#ifndef Q_NO_SYMLINKS
+#if defined(Q_OS_WIN)
+#if !defined(Q_OS_WINRT)
+ QString errorMessage;
+ DWORD creationResult = createSymbolicLink("link", m_sourceFile, &errorMessage);
+ if (creationResult == ERROR_PRIVILEGE_NOT_HELD) {
+ QWARN(msgInsufficientPrivileges(errorMessage));
+ } else {
+ QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage));
+ QTest::newRow("link")
+ << "link" << QFileInfo::SymbolicLink << QFileInfo(m_sourceFile).absoluteFilePath();
+ }
+
+ creationResult = createSymbolicLink("brokenlink", "dummyfile", &errorMessage);
+ if (creationResult == ERROR_PRIVILEGE_NOT_HELD) {
+ QWARN(msgInsufficientPrivileges(errorMessage));
+ } else {
+ QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage));
+ QTest::newRow("broken link")
+ << "brokenlink" << QFileInfo::SymbolicLink << QFileInfo("dummyfile").absoluteFilePath();
+ }
+#endif // !Q_OS_WINRT
+#else // Unix:
+ QVERIFY(file1.link("link"));
+ QTest::newRow("link")
+ << "link" << QFileInfo::SymbolicLink << QFileInfo(m_sourceFile).absoluteFilePath();
+
+ QVERIFY(file2.link("brokenlink"));
+ QTest::newRow("broken link")
+ << "brokenlink" << QFileInfo::SymbolicLink << QFileInfo("dummyfile").absoluteFilePath();
+
+ QDir::current().mkdir("relative");
+ QFile::link("../dummyfile", "relative/link");
+ QTest::newRow("relative link")
+ << "relative/link" << QFileInfo::SymbolicLink << QFileInfo("dummyfile").absoluteFilePath();
+#endif
+#endif // !Q_NO_SYMLINKS
+ file2.remove();
+}
+
+void tst_QFileInfo::link()
+{
+ QFETCH(QString, path);
+ QFETCH(QFileInfo::FileType, linkType);
+ QFETCH(QString, linkTarget);
+
+ QFileInfo fi(path);
+ QCOMPARE(fi.type() & QFileInfo::LinkTypeMask, linkType);
+ QCOMPARE(fi.symLinkTarget(), linkTarget);
+}
+
void tst_QFileInfo::isHidden_data()
{
QTest::addColumn<QString>("path");
@@ -1638,7 +1725,7 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks()
QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage));
QFileInfo fi(path);
- const bool actualIsSymLink = fi.isSymLink();
+ const bool actualIsSymLink = fi.isSymbolicLink();
const QString actualSymLinkTarget = isSymLink ? fi.symLinkTarget() : QString();
const QString actualCanonicalFilePath = isSymLink ? fi.canonicalFilePath() : QString();
// Ensure that junctions, mountpoints are removed. If this fails, do not remove
@@ -1667,14 +1754,16 @@ void tst_QFileInfo::brokenShortcut()
file.close();
QFileInfo info(linkName);
- QVERIFY(info.isSymLink());
+ QVERIFY(!info.isSymbolicLink());
+ QVERIFY(info.isShortcut());
QVERIFY(!info.exists());
QFile::remove(linkName);
QDir current; // QTBUG-21863
QVERIFY(current.mkdir(linkName));
QFileInfo dirInfo(linkName);
- QVERIFY(!dirInfo.isSymLink());
+ QVERIFY(!dirInfo.isSymbolicLink());
+ QVERIFY(!dirInfo.isShortcut());
QVERIFY(dirInfo.isDir());
current.rmdir(linkName);
}
@@ -2032,7 +2121,8 @@ static void stateCheck(const QFileInfo &info, const QString &dirname, const QStr
QVERIFY(!info.isHidden());
QVERIFY(!info.isFile());
QVERIFY(!info.isDir());
- QVERIFY(!info.isSymLink());
+ QVERIFY(!info.isSymbolicLink());
+ QVERIFY(!info.isShortcut());
QVERIFY(!info.isBundle());
QVERIFY(!info.isRoot());
QCOMPARE(info.isNativePath(), !filename.isEmpty());
@@ -2089,5 +2179,73 @@ void tst_QFileInfo::nonExistingFile()
stateCheck(info, dirname, filename);
}
+Q_DECLARE_METATYPE(QFileInfo::FileTypes)
+
+void tst_QFileInfo::type_data()
+{
+ QFile::remove("link.lnk");
+ QFile::remove("symlink.lnk");
+ QFile::remove("link");
+ QFile::remove("symlink");
+ QFile::remove("directory.lnk");
+ QFile::remove("directory");
+
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QFileInfo::FileTypes>("type");
+
+ QFile regularFile(m_sourceFile);
+ QTest::newRow("regular")
+ << regularFile.fileName() << QFileInfo::FileTypes(QFileInfo::Regular);
+ QTest::newRow("directory")
+ << QDir::currentPath() << QFileInfo::FileTypes(QFileInfo::Directory);
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+ // windows shortcuts
+ QVERIFY(regularFile.link("link.lnk"));
+ QTest::newRow("shortcut")
+ << "link.lnk" << QFileInfo::FileTypes(QFileInfo::Shortcut | QFileInfo::Regular);
+ QVERIFY(regularFile.link("link"));
+ QTest::newRow("invalid-shortcut")
+ << "link" << QFileInfo::FileTypes(QFileInfo::Regular);
+ QVERIFY(QFile::link(QDir::currentPath(), "directory.lnk"));
+ QTest::newRow("directory-shortcut")
+ << "directory.lnk" << QFileInfo::FileTypes(QFileInfo::Shortcut | QFileInfo::Directory);
+#endif
+
+#ifndef Q_NO_SYMLINKS
+#if defined(Q_OS_WIN)
+#if !defined(Q_OS_WINRT)
+ QString errorMessage;
+ const DWORD creationResult = createSymbolicLink("symlink", m_sourceFile, &errorMessage);
+ if (creationResult == ERROR_PRIVILEGE_NOT_HELD) {
+ QWARN(msgInsufficientPrivileges(errorMessage));
+ } else {
+ QVERIFY2(creationResult == ERROR_SUCCESS, qPrintable(errorMessage));
+ QTest::newRow("NTFS-symlink")
+ << "symlink" << QFileInfo::FileTypes(QFileInfo::SymbolicLink | QFileInfo::Regular);
+ }
+#endif // !Q_OS_WINRT
+#else // Unix:
+ QVERIFY(regularFile.link("symlink.lnk"));
+ QTest::newRow("symlink.lnk")
+ << "symlink.lnk" << QFileInfo::FileTypes(QFileInfo::SymbolicLink | QFileInfo::Regular);
+ QVERIFY(regularFile.link("symlink"));
+ QTest::newRow("symlink")
+ << "symlink" << QFileInfo::FileTypes(QFileInfo::SymbolicLink | QFileInfo::Regular);
+ QVERIFY(QFile::link(QDir::currentPath(), "directory"));
+ QTest::newRow("directory-symlink")
+ << "directory" << QFileInfo::FileTypes(QFileInfo::SymbolicLink | QFileInfo::Directory);
+#endif
+#endif // !Q_NO_SYMLINKS
+}
+
+void tst_QFileInfo::type()
+{
+ QFETCH(QString, path);
+ QFETCH(QFileInfo::FileTypes, type);
+
+ QFileInfo info(path);
+ QCOMPARE(info.type(), type);
+}
+
QTEST_MAIN(tst_QFileInfo)
#include "tst_qfileinfo.moc"