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
ioutils.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "ioutils.h"
5
6#include <qdir.h>
7#include <qfile.h>
8#include <qregularexpression.h>
9
10#ifdef Q_OS_WIN
11# include <qt_windows.h>
12#else
13# include <sys/types.h>
14# include <sys/stat.h>
15# include <unistd.h>
16# include <utime.h>
17# include <fcntl.h>
18# include <errno.h>
19#endif
20
21#define fL1S(s) QString::fromLatin1(s)
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::Literals::StringLiterals;
26using namespace QMakeInternal;
27
28QString IoUtils::binaryAbsLocation(const QString &argv0)
29{
30 QString ret;
31 if (!argv0.isEmpty() && isAbsolutePath(argv0)) {
32 ret = argv0;
33 } else if (argv0.contains(u'/')
34#ifdef Q_OS_WIN
35 || argv0.contains(u'\\')
36#endif
37 ) { // relative PWD
38 ret = QDir::current().absoluteFilePath(argv0);
39 } else { // in the PATH
40 QByteArray pEnv = qgetenv("PATH");
41 QDir currentDir = QDir::current();
42 QStringList paths = QString::fromLocal8Bit(pEnv).split(QDir::listSeparator());
43#ifdef Q_OS_WIN
44 paths.prepend("."_L1);
45#endif
46 for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
47 if ((*p).isEmpty())
48 continue;
49 QString candidate = currentDir.absoluteFilePath(*p + u'/' + argv0);
50 if (QFile::exists(candidate)) {
51 ret = candidate;
52 break;
53 }
54 }
55 }
56
57 return QDir::cleanPath(ret);
58}
59
60IoUtils::FileType IoUtils::fileType(const QString &fileName)
61{
62 Q_ASSERT(fileName.isEmpty() || isAbsolutePath(fileName));
63#ifdef Q_OS_WIN
64 DWORD attr = GetFileAttributesW((WCHAR*)fileName.utf16());
65 if (attr == INVALID_FILE_ATTRIBUTES)
66 return FileNotFound;
67 return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FileIsDir : FileIsRegular;
68#else
69 struct ::stat st;
70 if (::stat(fileName.toLocal8Bit().constData(), &st))
71 return FileNotFound;
72 return S_ISDIR(st.st_mode) ? FileIsDir : S_ISREG(st.st_mode) ? FileIsRegular : FileNotFound;
73#endif
74}
75
76bool IoUtils::isRelativePath(const QString &path)
77{
78#ifdef QMAKE_BUILTIN_PRFS
79 if (path.startsWith(":/"_L1))
80 return false;
81#endif
82#ifdef Q_OS_WIN
83 // Unlike QFileInfo, this considers only paths with both a drive prefix and
84 // a subsequent (back-)slash absolute:
85 if (path.length() >= 3 && path.at(1) == u':' && path.at(0).isLetter()
86 && (path.at(2) == u'/' || path.at(2) == u'\\')) {
87 return false;
88 }
89 // ... unless, of course, they're UNC:
90 if (path.length() >= 2
91 && (path.at(0).unicode() == '\\' || path.at(0).unicode() == '/')
92 && path.at(1) == path.at(0)) {
93 return false;
94 }
95#else
96 if (path.startsWith(u'/'))
97 return false;
98#endif // Q_OS_WIN
99 return true;
100}
101
102QStringView IoUtils::pathName(const QString &fileName)
103{
104 return QStringView{ fileName }.left(fileName.lastIndexOf(u'/') + 1);
105}
106
107QStringView IoUtils::fileName(const QString &fileName)
108{
109 return QStringView(fileName).mid(fileName.lastIndexOf(u'/') + 1);
110}
111
112QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
113{
114 if (fileName.isEmpty())
115 return QString();
116 if (isAbsolutePath(fileName))
117 return QDir::cleanPath(fileName);
118#ifdef Q_OS_WIN // Add drive to otherwise-absolute path:
119 if (fileName.at(0).unicode() == '/' || fileName.at(0).unicode() == '\\') {
120 Q_ASSERT_X(isAbsolutePath(baseDir), "IoUtils::resolvePath", qUtf8Printable(baseDir));
121 return QDir::cleanPath(baseDir.left(2) + fileName);
122 }
123#endif // Q_OS_WIN
124 return QDir::cleanPath(baseDir + u'/' + fileName);
125}
126
127inline static
128bool isSpecialChar(ushort c, const uchar (&iqm)[16])
129{
130 if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
131 return true;
132 return false;
133}
134
135inline static
136bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
137{
138 for (int x = arg.size() - 1; x >= 0; --x) {
139 if (isSpecialChar(arg.unicode()[x].unicode(), iqm))
140 return true;
141 }
142 return false;
143}
144
145QString IoUtils::shellQuoteUnix(const QString &arg)
146{
147 // Chars that should be quoted (TM). This includes:
148 static const uchar iqm[] = {
149 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
150 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
151 }; // 0-32 \'"$`<>|;&(){}*?#!~[]
152
153 if (!arg.size())
154 return QString::fromLatin1("''");
155
156 QString ret(arg);
157 if (hasSpecialChars(ret, iqm)) {
158 ret.replace(QLatin1Char('\''), "'\\''"_L1);
159 ret.prepend(QLatin1Char('\''));
160 ret.append(QLatin1Char('\''));
161 }
162 return ret;
163}
164
165QString IoUtils::shellQuoteWin(const QString &arg)
166{
167 // Chars that should be quoted (TM). This includes:
168 // - control chars & space
169 // - the shell meta chars "&()<>^|
170 // - the potential separators ,;=
171 static const uchar iqm[] = {
172 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
173 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
174 };
175 // Shell meta chars that need escaping.
176 static const uchar ism[] = {
177 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50,
178 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
179 }; // &()<>^|
180
181 if (!arg.size())
182 return QString::fromLatin1("\"\"");
183
184 QString ret(arg);
185 if (hasSpecialChars(ret, iqm)) {
186 // The process-level standard quoting allows escaping quotes with backslashes (note
187 // that backslashes don't escape themselves, unless they are followed by a quote).
188 // Consequently, quotes are escaped and their preceding backslashes are doubled.
189 ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
190 // Trailing backslashes must be doubled as well, as they are followed by a quote.
191 ret.replace(QRegularExpression("(\\\\+)$"_L1), "\\1\\1"_L1);
192 // However, the shell also interprets the command, and no backslash-escaping exists
193 // there - a quote always toggles the quoting state, but is nonetheless passed down
194 // to the called process verbatim. In the unquoted state, the circumflex escapes
195 // meta chars (including itself and quotes), and is removed from the command.
196 bool quoted = true;
197 for (int i = 0; i < ret.size(); i++) {
198 QChar c = ret.unicode()[i];
199 if (c.unicode() == '"')
200 quoted = !quoted;
201 else if (!quoted && isSpecialChar(c.unicode(), ism))
202 ret.insert(i++, u'^');
203 }
204 if (!quoted)
205 ret.append(u'^');
206 ret.append(u'"');
207 ret.prepend(u'"');
208 }
209 return ret;
210}
211
212#if defined(PROEVALUATOR_FULL)
213
214# if defined(Q_OS_WIN)
215static QString windowsErrorCode()
216{
217 wchar_t *string = nullptr;
218 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
219 NULL,
220 GetLastError(),
221 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
222 (LPWSTR)&string,
223 0,
224 NULL);
225 QString ret = QString::fromWCharArray(string);
226 LocalFree((HLOCAL)string);
227 return ret.trimmed();
228}
229# endif
230
231bool IoUtils::touchFile(const QString &targetFileName, const QString &referenceFileName, QString *errorString)
232{
233# ifdef Q_OS_UNIX
234 struct stat st;
235 if (stat(referenceFileName.toLocal8Bit().constData(), &st)) {
236 *errorString = fL1S("Cannot stat() reference file %1: %2.").arg(referenceFileName, fL1S(strerror(errno)));
237 return false;
238 }
239# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
240 const struct timespec times[2] = { { 0, UTIME_NOW }, st.st_mtim };
241 const bool utimeError = utimensat(AT_FDCWD, targetFileName.toLocal8Bit().constData(), times, 0) < 0;
242# else
243 struct utimbuf utb;
244 utb.actime = time(0);
245 utb.modtime = st.st_mtime;
246 const bool utimeError= utime(targetFileName.toLocal8Bit().constData(), &utb) < 0;
247# endif
248 if (utimeError) {
249 *errorString = fL1S("Cannot touch %1: %2.").arg(targetFileName, fL1S(strerror(errno)));
250 return false;
251 }
252# else
253 HANDLE rHand = CreateFile((wchar_t*)referenceFileName.utf16(),
254 GENERIC_READ, FILE_SHARE_READ,
255 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
256 if (rHand == INVALID_HANDLE_VALUE) {
257 *errorString = fL1S("Cannot open reference file %1: %2").arg(referenceFileName, windowsErrorCode());
258 return false;
259 }
260 FILETIME ft;
261 GetFileTime(rHand, NULL, NULL, &ft);
262 CloseHandle(rHand);
263 HANDLE wHand = CreateFile((wchar_t*)targetFileName.utf16(),
264 GENERIC_WRITE, FILE_SHARE_READ,
265 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
266 if (wHand == INVALID_HANDLE_VALUE) {
267 *errorString = fL1S("Cannot open %1: %2").arg(targetFileName, windowsErrorCode());
268 return false;
269 }
270 SetFileTime(wHand, NULL, NULL, &ft);
271 CloseHandle(wHand);
272# endif
273 return true;
274}
275
276#if defined(QT_BUILD_QMAKE) && defined(Q_OS_UNIX)
277bool IoUtils::readLinkTarget(const QString &symlinkPath, QString *target)
278{
279 const QByteArray localSymlinkPath = QFile::encodeName(symlinkPath);
280# if defined(__GLIBC__) && !defined(PATH_MAX)
281# define PATH_CHUNK_SIZE 256
282 char *s = 0;
283 int len = -1;
284 int size = PATH_CHUNK_SIZE;
285
286 forever {
287 s = (char *)::realloc(s, size);
288 len = ::readlink(localSymlinkPath.constData(), s, size);
289 if (len < 0) {
290 ::free(s);
291 break;
292 }
293 if (len < size)
294 break;
295 size *= 2;
296 }
297# else
298 char s[PATH_MAX+1];
299 int len = readlink(localSymlinkPath.constData(), s, PATH_MAX);
300# endif
301 if (len <= 0)
302 return false;
303 *target = QFile::decodeName(QByteArray(s, len));
304# if defined(__GLIBC__) && !defined(PATH_MAX)
305 ::free(s);
306# endif
307 return true;
308}
309#endif
310
311#endif // PROEVALUATOR_FULL
312
313QT_END_NAMESPACE
\inmodule QtCore
Definition qdir.h:22
This class provides replacement functionality for QFileInfo, QFile & QDir, as these are abysmally slo...
Definition ioutils.h:19
static FileType fileType(const QString &fileName)
Definition ioutils.cpp:60
static bool isRelativePath(const QString &fileName)
Definition ioutils.cpp:76
static QString binaryAbsLocation(const QString &argv0)
Definition ioutils.cpp:28
static bool isAbsolutePath(const QString &fileName)
Definition ioutils.h:31
static QString resolvePath(const QString &baseDir, const QString &fileName)
Definition ioutils.cpp:112
static QString shellQuoteWin(const QString &arg)
Definition ioutils.cpp:165
static QString shellQuoteUnix(const QString &arg)
Definition ioutils.cpp:145
static bool isSpecialChar(ushort c, const uchar(&iqm)[16])
Definition ioutils.cpp:128
static bool hasSpecialChars(const QString &arg, const uchar(&iqm)[16])
Definition ioutils.cpp:136