Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfilesystementry.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
6
7#include <QtCore/qdir.h>
8#include <QtCore/private/qdir_p.h>
9#include <QtCore/qfile.h>
10#include <QtCore/private/qfsfileengine_p.h>
11#ifdef Q_OS_WIN
12#include <QtCore/qstringbuilder.h>
13#endif
14
16
17using namespace Qt::StringLiterals;
18
19// Assigned to m_lastSeparator and m_firstDotInFileName to indicate resolveFilePath()
20// hasn't been called yet
21constexpr int Uninitialized = -2;
22
23#ifdef Q_OS_WIN
24static bool isUncRoot(const QString &server)
25{
26 QString localPath = QDir::toNativeSeparators(server);
27 if (!localPath.startsWith("\\\\"_L1))
28 return false;
29
30 int idx = localPath.indexOf(u'\\', 2);
31 if (idx == -1 || idx + 1 == localPath.length())
32 return true;
33
34 return QStringView{localPath}.right(localPath.length() - idx - 1).trimmed().isEmpty();
35}
36
37static inline QString fixIfRelativeUncPath(const QString &path)
38{
39 QString currentPath = QDir::currentPath();
40 if (currentPath.startsWith("//"_L1))
41 return currentPath % QChar(u'/') % path;
42 return path;
43}
44#endif
45
46QFileSystemEntry::QFileSystemEntry()
47 : m_lastSeparator(-1),
48 m_firstDotInFileName(-1),
49 m_lastDotInFileName(-1)
50{
51}
52
53/*!
54 \internal
55 Use this constructor when the path is supplied by user code, as it may contain a mix
56 of '/' and the native separator.
57 */
58QFileSystemEntry::QFileSystemEntry(const QString &filePath)
59 : m_filePath(QDir::fromNativeSeparators(filePath)),
60 m_lastSeparator(Uninitialized),
61 m_firstDotInFileName(Uninitialized),
62 m_lastDotInFileName(0)
63{
64}
65
66/*!
67 \internal
68 Use this constructor when the path is guaranteed to be in internal format, i.e. all
69 directory separators are '/' and not the native separator.
70 */
71QFileSystemEntry::QFileSystemEntry(const QString &filePath, FromInternalPath /* dummy */)
72 : m_filePath(filePath),
73 m_lastSeparator(Uninitialized),
74 m_firstDotInFileName(Uninitialized),
75 m_lastDotInFileName(0)
76{
77}
78
79/*!
80 \internal
81 Use this constructor when the path comes from a native API
82 */
83QFileSystemEntry::QFileSystemEntry(const NativePath &nativeFilePath, FromNativePath /* dummy */)
84 : m_nativeFilePath(nativeFilePath),
85 m_lastSeparator(Uninitialized),
86 m_firstDotInFileName(Uninitialized),
87 m_lastDotInFileName(0)
88{
89}
90
91QFileSystemEntry::QFileSystemEntry(const QString &filePath, const NativePath &nativeFilePath)
92 : m_filePath(QDir::fromNativeSeparators(filePath)),
93 m_nativeFilePath(nativeFilePath),
94 m_lastSeparator(Uninitialized),
95 m_firstDotInFileName(Uninitialized),
96 m_lastDotInFileName(0)
97{
98}
99
100QString QFileSystemEntry::filePath() const
101{
102 resolveFilePath();
103 return m_filePath;
104}
105
106QFileSystemEntry::NativePath QFileSystemEntry::nativeFilePath() const
107{
108 resolveNativeFilePath();
109 return m_nativeFilePath;
110}
111
112void QFileSystemEntry::resolveFilePath() const
113{
114 if (m_filePath.isEmpty() && !m_nativeFilePath.isEmpty()) {
115#ifdef Q_OS_WIN
116 m_filePath = QDir::fromNativeSeparators(m_nativeFilePath);
117#else
118 m_filePath = QDir::fromNativeSeparators(QFile::decodeName(m_nativeFilePath));
119#endif
120 }
121}
122
123void QFileSystemEntry::resolveNativeFilePath() const
124{
125 if (!m_filePath.isEmpty() && m_nativeFilePath.isEmpty()) {
126#ifdef Q_OS_WIN
127 QString filePath = m_filePath;
128 if (isRelative())
129 filePath = fixIfRelativeUncPath(m_filePath);
130 m_nativeFilePath = QFSFileEnginePrivate::longFileName(QDir::toNativeSeparators(filePath));
131#else
132 m_nativeFilePath = QFile::encodeName(QDir::toNativeSeparators(m_filePath));
133#endif
134 }
135}
136
137QString QFileSystemEntry::fileName() const
138{
139 findLastSeparator();
140#if defined(Q_OS_WIN)
141 if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == u':')
142 return m_filePath.mid(2);
143#endif
144 return m_filePath.mid(m_lastSeparator + 1);
145}
146
147QString QFileSystemEntry::path() const
148{
149 findLastSeparator();
150 if (m_lastSeparator == -1) {
151#if defined(Q_OS_WIN)
152 if (m_filePath.length() >= 2 && m_filePath.at(1) == u':')
153 return m_filePath.left(2);
154#endif
155 return QString(u'.');
156 }
157 if (m_lastSeparator == 0)
158 return QString(u'/');
159#if defined(Q_OS_WIN)
160 if (m_lastSeparator == 2 && m_filePath.at(1) == u':')
161 return m_filePath.left(m_lastSeparator + 1);
162#endif
163 return m_filePath.left(m_lastSeparator);
164}
165
166QString QFileSystemEntry::baseName() const
167{
168 findFileNameSeparators();
169 int length = -1;
170 if (m_firstDotInFileName >= 0) {
171 length = m_firstDotInFileName;
172 if (m_lastSeparator != -1) // avoid off by one
173 length--;
174 }
175#if defined(Q_OS_WIN)
176 if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == u':')
177 return m_filePath.mid(2, length - 2);
178#endif
179 return m_filePath.mid(m_lastSeparator + 1, length);
180}
181
182QString QFileSystemEntry::completeBaseName() const
183{
184 findFileNameSeparators();
185 int length = -1;
186 if (m_firstDotInFileName >= 0) {
187 length = m_firstDotInFileName + m_lastDotInFileName;
188 if (m_lastSeparator != -1) // avoid off by one
189 length--;
190 }
191#if defined(Q_OS_WIN)
192 if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == u':')
193 return m_filePath.mid(2, length - 2);
194#endif
195 return m_filePath.mid(m_lastSeparator + 1, length);
196}
197
198QString QFileSystemEntry::suffix() const
199{
200 findFileNameSeparators();
201
202 if (m_lastDotInFileName == -1)
203 return QString();
204
205 return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + m_lastDotInFileName + 1);
206}
207
208QString QFileSystemEntry::completeSuffix() const
209{
210 findFileNameSeparators();
211 if (m_firstDotInFileName == -1)
212 return QString();
213
214 return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + 1);
215}
216
217#if defined(Q_OS_WIN)
218bool QFileSystemEntry::isRelative() const
219{
220 resolveFilePath();
221 return (m_filePath.isEmpty()
222 || (m_filePath.at(0).unicode() != '/'
223 && !(m_filePath.length() >= 2 && m_filePath.at(1).unicode() == ':')));
224}
225
226bool QFileSystemEntry::isAbsolute() const
227{
228 resolveFilePath();
229 return ((m_filePath.length() >= 3
230 && m_filePath.at(0).isLetter()
231 && m_filePath.at(1).unicode() == ':'
232 && m_filePath.at(2).unicode() == '/')
233 || (m_filePath.length() >= 2
234 && m_filePath.at(0) == u'/'
235 && m_filePath.at(1) == u'/'));
236}
237#else
238bool QFileSystemEntry::isRelative() const
239{
240 return !isAbsolute();
241}
242
243bool QFileSystemEntry::isAbsolute() const
244{
245 resolveFilePath();
246 return (!m_filePath.isEmpty() && (m_filePath.at(0).unicode() == '/'));
247}
248#endif
249
250#if defined(Q_OS_WIN)
251bool QFileSystemEntry::isDriveRoot() const
252{
253 resolveFilePath();
254 return QFileSystemEntry::isDriveRootPath(m_filePath);
255}
256
257bool QFileSystemEntry::isDriveRootPath(const QString &path)
258{
259 return (path.length() == 3
260 && path.at(0).isLetter() && path.at(1) == u':'
261 && path.at(2) == u'/');
262}
263
264QString QFileSystemEntry::removeUncOrLongPathPrefix(QString path)
265{
266 constexpr qsizetype minPrefixSize = 4;
267 if (path.size() < minPrefixSize)
268 return path;
269
270 auto data = path.data();
271 const auto slash = path[0];
272 if (slash != u'\\' && slash != u'/')
273 return path;
274
275 // check for "//?/" or "/??/"
276 if (data[2] == u'?' && data[3] == slash && (data[1] == slash || data[1] == u'?')) {
277 path = path.sliced(minPrefixSize);
278
279 // check for a possible "UNC/" prefix left-over
280 if (path.size() >= 4) {
281 data = path.data();
282 if (data[0] == u'U' && data[1] == u'N' && data[2] == u'C' && data[3] == slash) {
283 data[2] = slash;
284 return path.sliced(2);
285 }
286 }
287 }
288
289 return path;
290}
291#endif // Q_OS_WIN
292
293bool QFileSystemEntry::isRootPath(const QString &path)
294{
295 if (path == "/"_L1
296#if defined(Q_OS_WIN)
297 || isDriveRootPath(path)
298 || isUncRoot(path)
299#endif
300 )
301 return true;
302
303 return false;
304}
305
306bool QFileSystemEntry::isRoot() const
307{
308 resolveFilePath();
309 return isRootPath(m_filePath);
310}
311
312bool QFileSystemEntry::isEmpty() const
313{
314 return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
315}
316
317// private methods
318
319void QFileSystemEntry::findLastSeparator() const
320{
321 if (m_lastSeparator == Uninitialized) {
322 resolveFilePath();
323 m_lastSeparator = m_filePath.lastIndexOf(u'/');
324 }
325}
326
327void QFileSystemEntry::findFileNameSeparators() const
328{
329 if (m_firstDotInFileName == Uninitialized) {
330 resolveFilePath();
331 int firstDotInFileName = -1;
332 int lastDotInFileName = -1;
333 int lastSeparator = m_lastSeparator;
334
335 int stop;
336 if (lastSeparator < 0) {
337 lastSeparator = -1;
338 stop = 0;
339 } else {
340 stop = lastSeparator;
341 }
342
343 Q_ASSERT(m_filePath.size() == int(m_filePath.size()));
344 int i = int(m_filePath.size() - 1);
345 for (; i >= stop; --i) {
346 if (m_filePath.at(i).unicode() == '.') {
347 firstDotInFileName = lastDotInFileName = i;
348 break;
349 } else if (m_filePath.at(i).unicode() == '/') {
350 lastSeparator = i;
351 break;
352 }
353 }
354
355 if (lastSeparator != i) {
356 for (--i; i >= stop; --i) {
357 if (m_filePath.at(i).unicode() == '.')
358 firstDotInFileName = i;
359 else if (m_filePath.at(i).unicode() == '/') {
360 lastSeparator = i;
361 break;
362 }
363 }
364 }
365 m_lastSeparator = lastSeparator;
366 m_firstDotInFileName = firstDotInFileName == -1 ? -1 : firstDotInFileName - qMax(0, lastSeparator);
367 if (lastDotInFileName == -1)
368 m_lastDotInFileName = -1;
369 else if (firstDotInFileName == lastDotInFileName)
370 m_lastDotInFileName = 0;
371 else
372 m_lastDotInFileName = lastDotInFileName - firstDotInFileName;
373 }
374}
375
376bool QFileSystemEntry::isClean() const
377{
378 resolveFilePath();
379 return qt_isPathNormalized(m_filePath, QDirPrivate::DefaultNormalization);
380}
381
382QT_END_NAMESPACE
Combined button and popup list for selecting options.
constexpr int Uninitialized