summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/codecs/qtextcodec.cpp49
-rw-r--r--src/corelib/codecs/qtextcodec.h13
-rw-r--r--src/corelib/configure.json99
-rw-r--r--src/corelib/corelib.pro4
-rw-r--r--src/corelib/debug_script.py98
-rw-r--r--src/corelib/doc/snippets/code/doc_src_qpair.cpp7
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qlocale.cpp17
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qstringiterator.cpp5
-rw-r--r--src/corelib/doc/snippets/qsignalmapper/buttonwidget.cpp16
-rw-r--r--src/corelib/doc/snippets/qstring/main.cpp17
-rw-r--r--src/corelib/doc/snippets/qversionnumber/main.cpp8
-rw-r--r--src/corelib/doc/snippets/signalmapper/filereader.cpp117
-rw-r--r--src/corelib/doc/src/objectmodel/signalsandslots.qdoc22
-rw-r--r--src/corelib/global/global.pri21
-rw-r--r--src/corelib/global/minimum-linux.S81
-rw-r--r--src/corelib/global/minimum-linux_p.h98
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h7
-rw-r--r--src/corelib/global/qendian.h235
-rw-r--r--src/corelib/global/qendian.qdoc369
-rw-r--r--src/corelib/global/qendian_p.h142
-rw-r--r--src/corelib/global/qfloat16.cpp24
-rw-r--r--src/corelib/global/qfloat16.h5
-rw-r--r--src/corelib/global/qglobal.cpp307
-rw-r--r--src/corelib/global/qglobal.h48
-rw-r--r--src/corelib/global/qglobal_p.h73
-rw-r--r--src/corelib/global/qlogging.cpp28
-rw-r--r--src/corelib/global/qnamespace.h10
-rw-r--r--src/corelib/global/qnamespace.qdoc29
-rw-r--r--src/corelib/global/qnumeric.h18
-rw-r--r--src/corelib/global/qnumeric_p.h54
-rw-r--r--src/corelib/global/qoperatingsystemversion.cpp23
-rw-r--r--src/corelib/global/qoperatingsystemversion.h24
-rw-r--r--src/corelib/global/qoperatingsystemversion_p.h19
-rw-r--r--src/corelib/global/qrandom.cpp1394
-rw-r--r--src/corelib/global/qrandom.h263
-rw-r--r--src/corelib/global/qrandom_p.h (renamed from src/corelib/io/qwinoverlappedionotifier_p.h)62
-rw-r--r--src/corelib/global/qt_windows.h4
-rw-r--r--src/corelib/global/qtypeinfo.h10
-rw-r--r--src/corelib/io/io.pri8
-rw-r--r--src/corelib/io/qabstractfileengine.cpp47
-rw-r--r--src/corelib/io/qabstractfileengine_p.h9
-rw-r--r--src/corelib/io/qdatastream.cpp32
-rw-r--r--src/corelib/io/qdatastream.h5
-rw-r--r--src/corelib/io/qdebug.cpp15
-rw-r--r--src/corelib/io/qdebug.h3
-rw-r--r--src/corelib/io/qfile.cpp122
-rw-r--r--src/corelib/io/qfile.h1
-rw-r--r--src/corelib/io/qfiledevice.cpp70
-rw-r--r--src/corelib/io/qfiledevice.h11
-rw-r--r--src/corelib/io/qfileinfo.cpp341
-rw-r--r--src/corelib/io/qfileinfo.h11
-rw-r--r--src/corelib/io/qfileinfo_p.h48
-rw-r--r--src/corelib/io/qfilesystemengine.cpp228
-rw-r--r--src/corelib/io/qfilesystemengine_p.h10
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp896
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp317
-rw-r--r--src/corelib/io/qfilesystementry.cpp5
-rw-r--r--src/corelib/io/qfilesystementry_p.h5
-rw-r--r--src/corelib/io/qfilesystemmetadata_p.h60
-rw-r--r--src/corelib/io/qfsfileengine.cpp35
-rw-r--r--src/corelib/io/qfsfileengine_p.h2
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp148
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp58
-rw-r--r--src/corelib/io/qiodevice.cpp124
-rw-r--r--src/corelib/io/qiodevice.h1
-rw-r--r--src/corelib/io/qiodevice_p.h3
-rw-r--r--src/corelib/io/qlockfile.cpp71
-rw-r--r--src/corelib/io/qlockfile_p.h8
-rw-r--r--src/corelib/io/qlockfile_unix.cpp167
-rw-r--r--src/corelib/io/qlockfile_win.cpp60
-rw-r--r--src/corelib/io/qprocess.cpp99
-rw-r--r--src/corelib/io/qprocess.h1
-rw-r--r--src/corelib/io/qprocess_p.h6
-rw-r--r--src/corelib/io/qprocess_unix.cpp129
-rw-r--r--src/corelib/io/qprocess_win.cpp134
-rw-r--r--src/corelib/io/qresource.cpp50
-rw-r--r--src/corelib/io/qsavefile.cpp39
-rw-r--r--src/corelib/io/qsettings.cpp80
-rw-r--r--src/corelib/io/qsettings.h2
-rw-r--r--src/corelib/io/qsettings_p.h3
-rw-r--r--src/corelib/io/qstandardpaths.cpp6
-rw-r--r--src/corelib/io/qstandardpaths_unix.cpp8
-rw-r--r--src/corelib/io/qstandardpaths_win.cpp10
-rw-r--r--src/corelib/io/qstorageinfo.cpp35
-rw-r--r--src/corelib/io/qstorageinfo.h7
-rw-r--r--src/corelib/io/qtemporarydir.cpp83
-rw-r--r--src/corelib/io/qtemporaryfile.cpp502
-rw-r--r--src/corelib/io/qtemporaryfile.h4
-rw-r--r--src/corelib/io/qtemporaryfile_p.h52
-rw-r--r--src/corelib/io/qtextstream.cpp3
-rw-r--r--src/corelib/io/qtldurl.cpp42
-rw-r--r--src/corelib/io/qwindowspipereader.cpp13
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp8
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h1
-rw-r--r--src/corelib/io/qwinoverlappedionotifier.cpp428
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp385
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.h4
-rw-r--r--src/corelib/itemmodels/qstringlistmodel.cpp2
-rw-r--r--src/corelib/json/qjson.cpp13
-rw-r--r--src/corelib/json/qjson_p.h129
-rw-r--r--src/corelib/json/qjsonarray.cpp21
-rw-r--r--src/corelib/json/qjsonarray.h22
-rw-r--r--src/corelib/json/qjsondocument.cpp74
-rw-r--r--src/corelib/json/qjsondocument.h23
-rw-r--r--src/corelib/json/qjsonobject.cpp22
-rw-r--r--src/corelib/json/qjsonobject.h21
-rw-r--r--src/corelib/json/qjsonvalue.cpp78
-rw-r--r--src/corelib/json/qjsonvalue.h29
-rw-r--r--src/corelib/kernel/kernel.pri3
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp3
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.h2
-rw-r--r--src/corelib/kernel/qcore_mac.cpp10
-rw-r--r--src/corelib/kernel/qcore_mac_objc.mm145
-rw-r--r--src/corelib/kernel/qcore_mac_p.h60
-rw-r--r--src/corelib/kernel/qcore_unix.cpp33
-rw-r--r--src/corelib/kernel/qcore_unix_p.h24
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp123
-rw-r--r--src/corelib/kernel/qcoreapplication.h3
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h6
-rw-r--r--src/corelib/kernel/qcoreapplication_win.cpp60
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h1
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp68
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h2
-rw-r--r--src/corelib/kernel/qfunctions_fake_env_p.h2
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp40
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h16
-rw-r--r--src/corelib/kernel/qmath.h24
-rw-r--r--src/corelib/kernel/qmath.qdoc19
-rw-r--r--src/corelib/kernel/qmetaobject.cpp99
-rw-r--r--src/corelib/kernel/qmetaobject_p.h3
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp36
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h3
-rw-r--r--src/corelib/kernel/qmetatype.cpp1
-rw-r--r--src/corelib/kernel/qmetatype.h47
-rw-r--r--src/corelib/kernel/qobject.cpp2
-rw-r--r--src/corelib/kernel/qobject.h4
-rw-r--r--src/corelib/kernel/qobject_impl.h78
-rw-r--r--src/corelib/kernel/qobject_p.h3
-rw-r--r--src/corelib/kernel/qobjectdefs.h89
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h94
-rw-r--r--src/corelib/kernel/qsignalmapper.cpp8
-rw-r--r--src/corelib/kernel/qsignalmapper.h6
-rw-r--r--src/corelib/kernel/qtimer.h14
-rw-r--r--src/corelib/kernel/qvariant.cpp162
-rw-r--r--src/corelib/kernel/qvariant_p.h10
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp67
-rw-r--r--src/corelib/kernel/qwineventnotifier_p.h (renamed from src/corelib/tools/qpodlist_p.h)70
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp8
-rw-r--r--src/corelib/mimetypes/qmimeprovider_p.h2
-rw-r--r--src/corelib/mimetypes/qmimetype.cpp90
-rw-r--r--src/corelib/mimetypes/qmimetype.h18
-rw-r--r--src/corelib/mimetypes/qmimetypeparser.cpp1
-rw-r--r--src/corelib/plugin/qfactoryloader.cpp8
-rw-r--r--src/corelib/plugin/quuid.cpp313
-rw-r--r--src/corelib/plugin/quuid.h3
-rw-r--r--src/corelib/thread/qmutex.cpp2
-rw-r--r--src/corelib/thread/qmutex.h8
-rw-r--r--src/corelib/thread/qresultstore.h2
-rw-r--r--src/corelib/thread/qsemaphore.cpp153
-rw-r--r--src/corelib/thread/qsemaphore.h41
-rw-r--r--src/corelib/thread/qthread.cpp78
-rw-r--r--src/corelib/thread/qthread.h108
-rw-r--r--src/corelib/thread/qthread_p.h4
-rw-r--r--src/corelib/thread/qthread_unix.cpp151
-rw-r--r--src/corelib/thread/qthread_win.cpp4
-rw-r--r--src/corelib/thread/qthreadpool.cpp35
-rw-r--r--src/corelib/thread/qthreadpool.h4
-rw-r--r--src/corelib/thread/qthreadpool_p.h12
-rw-r--r--src/corelib/tools/qbytearray.cpp100
-rw-r--r--src/corelib/tools/qbytearray.h101
-rw-r--r--src/corelib/tools/qchar.cpp23
-rw-r--r--src/corelib/tools/qchar.h7
-rw-r--r--src/corelib/tools/qdatetime.cpp214
-rw-r--r--src/corelib/tools/qdatetime.h29
-rw-r--r--src/corelib/tools/qdatetimeparser.cpp1004
-rw-r--r--src/corelib/tools/qdatetimeparser_p.h40
-rw-r--r--src/corelib/tools/qhash.cpp178
-rw-r--r--src/corelib/tools/qhash.h11
-rw-r--r--src/corelib/tools/qhashfunctions.h8
-rw-r--r--src/corelib/tools/qiterator.h31
-rw-r--r--src/corelib/tools/qiterator.qdoc120
-rw-r--r--src/corelib/tools/qlinkedlist.h4
-rw-r--r--src/corelib/tools/qlist.h6
-rw-r--r--src/corelib/tools/qlocale.cpp441
-rw-r--r--src/corelib/tools/qlocale.h36
-rw-r--r--src/corelib/tools/qlocale.qdoc2
-rw-r--r--src/corelib/tools/qlocale_data_p.h9174
-rw-r--r--src/corelib/tools/qlocale_mac.mm4
-rw-r--r--src/corelib/tools/qlocale_p.h62
-rw-r--r--src/corelib/tools/qlocale_win.cpp6
-rw-r--r--src/corelib/tools/qmap.cpp67
-rw-r--r--src/corelib/tools/qmap.h10
-rw-r--r--src/corelib/tools/qrect.cpp4
-rw-r--r--src/corelib/tools/qrect.h2
-rw-r--r--src/corelib/tools/qregularexpression.cpp172
-rw-r--r--src/corelib/tools/qregularexpression.h15
-rw-r--r--src/corelib/tools/qringbuffer_p.h2
-rw-r--r--src/corelib/tools/qscopedpointer.cpp6
-rw-r--r--src/corelib/tools/qscopedpointer.h13
-rw-r--r--src/corelib/tools/qsimd.cpp26
-rw-r--r--src/corelib/tools/qsimd_p.h10
-rw-r--r--src/corelib/tools/qstring.cpp1452
-rw-r--r--src/corelib/tools/qstring.h263
-rw-r--r--src/corelib/tools/qstringalgorithms.h88
-rw-r--r--src/corelib/tools/qstringalgorithms_p.h8
-rw-r--r--src/corelib/tools/qstringbuilder.h7
-rw-r--r--src/corelib/tools/qstringiterator.qdoc8
-rw-r--r--src/corelib/tools/qstringiterator_p.h10
-rw-r--r--src/corelib/tools/qstringlist.cpp36
-rw-r--r--src/corelib/tools/qstringlist.h7
-rw-r--r--src/corelib/tools/qstringliteral.h128
-rw-r--r--src/corelib/tools/qstringview.cpp777
-rw-r--r--src/corelib/tools/qstringview.h307
-rw-r--r--src/corelib/tools/qtimezone.cpp7
-rw-r--r--src/corelib/tools/qtimezone.h8
-rw-r--r--src/corelib/tools/qtimezoneprivate.cpp2
-rw-r--r--src/corelib/tools/qtimezoneprivate_data_p.h5
-rw-r--r--src/corelib/tools/qtimezoneprivate_icu.cpp5
-rw-r--r--src/corelib/tools/qtimezoneprivate_mac.mm88
-rw-r--r--src/corelib/tools/qtimezoneprivate_tz.cpp63
-rw-r--r--src/corelib/tools/qtimezoneprivate_win.cpp2
-rw-r--r--src/corelib/tools/qvarlengtharray.h5
-rw-r--r--src/corelib/tools/qvarlengtharray.qdoc6
-rw-r--r--src/corelib/tools/qvector.h9
-rw-r--r--src/corelib/tools/qvector.qdoc7
-rw-r--r--src/corelib/tools/qversionnumber.cpp53
-rw-r--r--src/corelib/tools/qversionnumber.h4
-rw-r--r--src/corelib/tools/tools.pri5
-rw-r--r--src/corelib/xml/qxmlstream.cpp70
-rw-r--r--src/corelib/xml/qxmlstream.g105
-rw-r--r--src/corelib/xml/qxmlstream_p.h73
-rw-r--r--src/corelib/xml/qxmlutils.cpp37
-rw-r--r--src/corelib/xml/qxmlutils_p.h7
233 files changed, 18116 insertions, 9258 deletions
diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp
index aed3532024..4f0cd914ca 100644
--- a/src/corelib/codecs/qtextcodec.cpp
+++ b/src/corelib/codecs/qtextcodec.cpp
@@ -43,6 +43,7 @@
#ifndef QT_NO_TEXTCODEC
+#include "qbytearraymatcher.h"
#include "qlist.h"
#include "qfile.h"
#include "qstringlist.h"
@@ -803,6 +804,7 @@ QTextEncoder* QTextCodec::makeEncoder(QTextCodec::ConversionFlags flags) const
The \a state of the convertor used is updated.
*/
+#if QT_STRINGVIEW_LEVEL < 2
/*!
Converts \a str from Unicode to the encoding of this codec, and
returns the result in a QByteArray.
@@ -811,6 +813,19 @@ QByteArray QTextCodec::fromUnicode(const QString& str) const
{
return convertFromUnicode(str.constData(), str.length(), 0);
}
+#endif
+
+/*!
+ \overload
+ \since 5.10
+
+ Converts \a str from Unicode to the encoding of this codec, and
+ returns the result in a QByteArray.
+*/
+QByteArray QTextCodec::fromUnicode(QStringView str) const
+{
+ return convertFromUnicode(str.data(), str.length(), nullptr);
+}
/*!
\fn QString QTextCodec::toUnicode(const char *input, int size,
@@ -844,6 +859,7 @@ bool QTextCodec::canEncode(QChar ch) const
return (state.invalidChars == 0);
}
+#if QT_STRINGVIEW_LEVEL < 2
/*!
\overload
@@ -856,7 +872,22 @@ bool QTextCodec::canEncode(const QString& s) const
convertFromUnicode(s.constData(), s.length(), &state);
return (state.invalidChars == 0);
}
+#endif
+
+/*!
+ \overload
+ \since 5.10
+ Returns \c true if the Unicode string \a s can be fully encoded
+ with this codec; otherwise returns \c false.
+*/
+bool QTextCodec::canEncode(QStringView s) const
+{
+ ConverterState state;
+ state.flags = ConvertInvalidToNull;
+ convertFromUnicode(s.data(), s.length(), &state);
+ return !state.invalidChars;
+}
/*!
\overload
@@ -921,6 +952,7 @@ bool QTextEncoder::hasFailure() const
return state.invalidChars != 0;
}
+#if QT_STRINGVIEW_LEVEL < 2
/*!
Converts the Unicode string \a str into an encoded QByteArray.
*/
@@ -929,6 +961,17 @@ QByteArray QTextEncoder::fromUnicode(const QString& str)
QByteArray result = c->fromUnicode(str.constData(), str.length(), &state);
return result;
}
+#endif
+
+/*!
+ \overload
+ \since 5.10
+ Converts the Unicode string \a str into an encoded QByteArray.
+*/
+QByteArray QTextEncoder::fromUnicode(QStringView str)
+{
+ return c->fromUnicode(str.data(), str.length(), &state);
+}
/*!
\overload
@@ -1050,10 +1093,12 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCo
// determine charset
QTextCodec *c = QTextCodec::codecForUtfText(ba, 0);
if (!c) {
+ static Q_RELAXED_CONSTEXPR auto matcher = qMakeStaticByteArrayMatcher("meta ");
QByteArray header = ba.left(1024).toLower();
- int pos = header.indexOf("meta ");
+ int pos = matcher.indexIn(header);
if (pos != -1) {
- pos = header.indexOf("charset=", pos);
+ static Q_RELAXED_CONSTEXPR auto matcher = qMakeStaticByteArrayMatcher("charset=");
+ pos = matcher.indexIn(header, pos);
if (pos != -1) {
pos += qstrlen("charset=");
diff --git a/src/corelib/codecs/qtextcodec.h b/src/corelib/codecs/qtextcodec.h
index 47b2fe6eed..5163d37238 100644
--- a/src/corelib/codecs/qtextcodec.h
+++ b/src/corelib/codecs/qtextcodec.h
@@ -79,11 +79,17 @@ public:
static QTextCodec *codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec);
bool canEncode(QChar) const;
+#if QT_STRINGVIEW_LEVEL < 2
bool canEncode(const QString&) const;
+#endif
+ bool canEncode(QStringView) const;
QString toUnicode(const QByteArray&) const;
QString toUnicode(const char* chars) const;
+#if QT_STRINGVIEW_LEVEL < 2
QByteArray fromUnicode(const QString& uc) const;
+#endif
+ QByteArray fromUnicode(QStringView uc) const;
enum ConversionFlag {
DefaultConversion,
ConvertInvalidToNull = 0x80000000,
@@ -133,9 +139,12 @@ class Q_CORE_EXPORT QTextEncoder {
Q_DISABLE_COPY(QTextEncoder)
public:
explicit QTextEncoder(const QTextCodec *codec) : c(codec), state() {}
- QTextEncoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags);
+ explicit QTextEncoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags);
~QTextEncoder();
+#if QT_STRINGVIEW_LEVEL < 2
QByteArray fromUnicode(const QString& str);
+#endif
+ QByteArray fromUnicode(QStringView str);
QByteArray fromUnicode(const QChar *uc, int len);
bool hasFailure() const;
private:
@@ -147,7 +156,7 @@ class Q_CORE_EXPORT QTextDecoder {
Q_DISABLE_COPY(QTextDecoder)
public:
explicit QTextDecoder(const QTextCodec *codec) : c(codec), state() {}
- QTextDecoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags);
+ explicit QTextDecoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags);
~QTextDecoder();
QString toUnicode(const char* chars, int len);
QString toUnicode(const QByteArray &ba);
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index a5b69a2df6..8067ca70f1 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -251,6 +251,23 @@
]
}
},
+ "cxx11_future": {
+ "label": "C++11 <future>",
+ "type": "compile",
+ "test": {
+ "include": "future",
+ "main": [
+ "std::future<int> f = std::async([]() { return 42; });",
+ "(void)f.get();"
+ ],
+ "qmake": "unix:LIBS += -lpthread"
+ }
+ },
+ "cxx11_random": {
+ "label": "C++11 <random>",
+ "type": "compile",
+ "test": "unix/cxx11_random"
+ },
"eventfd": {
"label": "eventfd",
"type": "compile",
@@ -264,6 +281,26 @@
]
}
},
+ "futimens": {
+ "label": "futimens()",
+ "type": "compile",
+ "test": "unix/futimens"
+ },
+ "futimes": {
+ "label": "futimes()",
+ "type": "compile",
+ "test": "unix/futimes"
+ },
+ "getauxval": {
+ "label": "getauxval()",
+ "type": "compile",
+ "test": "unix/getauxval"
+ },
+ "getentropy": {
+ "label": "getentropy()",
+ "type": "compile",
+ "test": "unix/getentropy"
+ },
"posix-iconv": {
"label": "POSIX iconv",
"type": "compile",
@@ -349,6 +386,28 @@
]
}
},
+ "renameat2": {
+ "label": "renameat2()",
+ "type": "compile",
+ "test": {
+ "head": "#define _ATFILE_SOURCE 1",
+ "include": [ "fcntl.h", "stdio.h" ],
+ "main": "renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_NOREPLACE | RENAME_WHITEOUT);"
+ }
+ },
+ "statx": {
+ "label": "statx() in libc",
+ "type": "compile",
+ "test": {
+ "head": "#define _ATFILE_SOURCE 1",
+ "include": [ "sys/types.h", "sys/stat.h", "unistd.h", "fcntl.h" ],
+ "main": [
+ "struct statx statxbuf;",
+ "unsigned int mask = STATX_BASIC_STATS;",
+ "return statx(AT_FDCWD, \"\", AT_STATX_SYNC_AS_STAT, mask, &statxbuf);"
+ ]
+ }
+ },
"syslog": {
"label": "syslog",
"type": "compile",
@@ -395,11 +454,36 @@
"condition": "features.doubleconversion && libs.doubleconversion",
"output": [ "privateFeature" ]
},
+ "cxx11_future": {
+ "label": "C++11 <future>",
+ "condition": "tests.cxx11_future",
+ "output": [ "publicFeature" ]
+ },
"eventfd": {
"label": "eventfd",
"condition": "tests.eventfd",
"output": [ "feature" ]
},
+ "futimens": {
+ "label": "futimens()",
+ "condition": "!config.win32 && tests.futimens",
+ "output": [ "privateFeature" ]
+ },
+ "futimes": {
+ "label": "futimes()",
+ "condition": "!config.win32 && !features.futimens && tests.futimes",
+ "output": [ "privateFeature" ]
+ },
+ "getauxval": {
+ "label": "getauxval()",
+ "condition": "config.linux && tests.getauxval",
+ "output": [ "privateFeature" ]
+ },
+ "getentropy": {
+ "label": "getentropy()",
+ "condition": "config.unix && tests.getentropy",
+ "output": [ "privateFeature" ]
+ },
"glib": {
"label": "GLib",
"autoDetect": "!config.win32",
@@ -516,11 +600,21 @@
"condition": "features.statemachine",
"output": [ "publicFeature" ]
},
+ "renameat2": {
+ "label": "renameat2()",
+ "condition": "config.linux && tests.renameat2",
+ "output": [ "privateFeature" ]
+ },
"slog2": {
"label": "slog2",
"condition": "libs.slog2",
"output": [ "privateFeature" ]
},
+ "statx": {
+ "label": "statx() in libc",
+ "condition": "config.linux && tests.statx",
+ "output": [ "privateFeature" ]
+ },
"syslog": {
"label": "syslog",
"autoDetect": false,
@@ -771,6 +865,11 @@ or are able to read the logged output from journald, syslog or slog2."
},
{
"type": "error",
+ "condition": "!tests.cxx11_random",
+ "message": "C++11 <random> is required and is missing or failed to compile."
+ },
+ {
+ "type": "error",
"condition": "input.doubleconversion == 'no' && !tests.xlocalescanprint",
"message": "Your C library does not provide sscanf_l or snprintf_l.
You need to use libdouble-conversion for double/string conversion."
diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro
index 32f9992b6b..6dc11e1a4d 100644
--- a/src/corelib/corelib.pro
+++ b/src/corelib/corelib.pro
@@ -50,8 +50,6 @@ win32 {
mingw {
# otherwise mingw headers do not declare common functions like putenv
CONFIG -= strict_c++
- # Override MinGW's definition in _mingw.h
- DEFINES += WINVER=0x600 _WIN32_WINNT=0x0600
}
LIBS_PRIVATE += -lws2_32
!winrt {
@@ -156,3 +154,5 @@ ctest_qt5_module_files.files += $$ctest_macros_file.output $$cmake_extras_mkspec
ctest_qt5_module_files.path = $$[QT_INSTALL_LIBS]/cmake/Qt5Core
INSTALLS += ctest_qt5_module_files cmake_qt5_umbrella_module_files
+
+QMAKE_DSYM_DEBUG_SCRIPT = $$PWD/debug_script.py
diff --git a/src/corelib/debug_script.py b/src/corelib/debug_script.py
new file mode 100644
index 0000000000..9111213ef7
--- /dev/null
+++ b/src/corelib/debug_script.py
@@ -0,0 +1,98 @@
+#############################################################################
+##
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the QtCore module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:GPL-EXCEPT$
+## 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
+## 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.
+##
+## 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.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import os
+import sys
+import imp
+
+from distutils.version import LooseVersion
+
+MODULE_NAME = 'qt'
+
+def import_bridge(path, debugger, session_dict, reload_module = False):
+ if not reload_module and MODULE_NAME in sys.modules:
+ del sys.modules[MODULE_NAME]
+
+ bridge = imp.load_source(MODULE_NAME, path)
+
+ if not hasattr(bridge, '__lldb_init_module'):
+ return None
+
+ # Make available for the current LLDB session, so that LLDB
+ # can find the functions when initializing the module.
+ session_dict[MODULE_NAME] = bridge
+
+ # Initialize the module now that it's available globally
+ bridge.__lldb_init_module(debugger, session_dict)
+
+ if not debugger.GetCategory('Qt'):
+ # Summary provider failed for some reason
+ del session_dict[MODULE_NAME]
+ return None
+
+ return bridge
+
+def report_success(bridge):
+ print "Using Qt summary providers from Creator %s in '%s'" \
+ % (bridge.CREATOR_VERSION, bridge.CREATOR_PATH)
+
+def __lldb_init_module(debugger, session_dict):
+ # Check if the module has already been imported globally. This ensures
+ # that the Qt Creator application search is only performed once per
+ # LLDB process invocation, while still reloading for each session.
+ if MODULE_NAME in sys.modules:
+ module = sys.modules[MODULE_NAME]
+ # Reload module for this sessions
+ bridge = import_bridge(module.__file__, debugger, session_dict,
+ reload_module = True)
+ if bridge:
+ report_success(bridge)
+ return
+
+ versions = {}
+ for install in os.popen(
+ 'mdfind kMDItemCFBundleIdentifier=org.qt-project.qtcreator'
+ '| while read p;'
+ 'do echo $p=$(mdls "$p" -name kMDItemVersion -raw);'
+ 'done'):
+ install = install.strip()
+ (p, v) = install.split('=')
+ versions[v] = p
+
+ for version in sorted(versions, key=LooseVersion, reverse=True):
+ path = versions[version]
+
+ bridge_path = path + '/Contents/Resources/debugger/lldbbridge.py'
+ bridge = import_bridge(bridge_path, debugger, session_dict)
+ if bridge:
+ bridge.CREATOR_VERSION = version
+ bridge.CREATOR_PATH = path
+ report_success(bridge)
+ return
+
+ print "Could not find Qt Creator installation, no Qt summary providers installed"
diff --git a/src/corelib/doc/snippets/code/doc_src_qpair.cpp b/src/corelib/doc/snippets/code/doc_src_qpair.cpp
index f94e3b0d37..e62f91830c 100644
--- a/src/corelib/doc/snippets/code/doc_src_qpair.cpp
+++ b/src/corelib/doc/snippets/code/doc_src_qpair.cpp
@@ -47,6 +47,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+#include <qmath.h>
//! [0]
QPair<QString, double> pair;
@@ -55,7 +56,7 @@ QPair<QString, double> pair;
//! [1]
pair.first = "pi";
-pair.second = 3.14159265358979323846;
+pair.second = M_PI;
//! [1]
//! [struct]
@@ -65,10 +66,10 @@ struct Variable {
};
Variable v;
v.name = "pi";
-v.value = 3.14159265358979323846;
+v.value = M_PI;
//! [struct]
//! [2]
QList<QPair<int, double> > list;
-list.append(qMakePair(66, 3.14159));
+list.append(qMakePair(66, M_PI));
//! [2]
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qlocale.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qlocale.cpp
index 3794049f97..aed14c379f 100644
--- a/src/corelib/doc/snippets/code/src_corelib_tools_qlocale.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_tools_qlocale.cpp
@@ -103,3 +103,20 @@ d = german.toDouble( "1234.56", &ok ); // ok == false
d = german.toDouble( "1.234", &ok ); // ok == true, d == 1234.0
//! [3]
+
+//! [3-qstringview]
+bool ok;
+double d;
+
+QLocale c(QLocale::C);
+d = c.toDouble(u"1234.56", &ok); // ok == true, d == 1234.56
+d = c.toDouble(u"1,234.56", &ok); // ok == true, d == 1234.56
+d = c.toDouble(u"1234,56", &ok); // ok == false
+
+QLocale german(QLocale::German);
+d = german.toDouble(u"1234,56", &ok); // ok == true, d == 1234.56
+d = german.toDouble(u"1.234,56", &ok); // ok == true, d == 1234.56
+d = german.toDouble(u"1234.56", &ok); // ok == false
+
+d = german.toDouble(u"1.234", &ok); // ok == true, d == 1234.0
+//! [3-qstringview]
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qstringiterator.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qstringiterator.cpp
index 1fcb7b9945..7a2b4812ef 100644
--- a/src/corelib/doc/snippets/code/src_corelib_tools_qstringiterator.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_tools_qstringiterator.cpp
@@ -58,7 +58,7 @@ int main()
{
//! [0]
QString string(QStringLiteral("a string"));
-QStringIterator i(string);
+QStringIterator i(string); // implicitly converted to QStringView
//! [0]
//! [1]
@@ -71,8 +71,7 @@ while (i.hasNext())
{
//! [2]
-QString string(QStringLiteral("𝄞 is the G clef"));
-QStringIterator i(string);
+QStringIterator i(u"𝄞 is the G clef");
qDebug() << hex << i.next(); // will print 1d11e (U+1D11E, MUSICAL SYMBOL G CLEF)
qDebug() << hex << i.next(); // will print 20 (U+0020, SPACE)
qDebug() << hex << i.next(); // will print 69 (U+0069, LATIN SMALL LETTER I)
diff --git a/src/corelib/doc/snippets/qsignalmapper/buttonwidget.cpp b/src/corelib/doc/snippets/qsignalmapper/buttonwidget.cpp
index 39b03f5ff3..e91c41b305 100644
--- a/src/corelib/doc/snippets/qsignalmapper/buttonwidget.cpp
+++ b/src/corelib/doc/snippets/qsignalmapper/buttonwidget.cpp
@@ -49,6 +49,7 @@
****************************************************************************/
#include <QtGui>
+#include <QtWidgets>
#include "buttonwidget.h"
@@ -74,3 +75,18 @@ ButtonWidget::ButtonWidget(const QStringList &texts, QWidget *parent)
setLayout(gridLayout);
}
//! [2]
+
+//! [3]
+ButtonWidget::ButtonWidget(const QStringList &texts, QWidget *parent)
+ : QWidget(parent)
+{
+ QGridLayout *gridLayout = new QGridLayout;
+ for (int i = 0; i < texts.size(); ++i) {
+ QString text = texts[i];
+ QPushButton *button = new QPushButton(text);
+ connect(button, &QPushButton::clicked, [=] { clicked(text); });
+ gridLayout->addWidget(button, i / 3, i % 3);
+ }
+ setLayout(gridLayout);
+}
+//! [3]
diff --git a/src/corelib/doc/snippets/qstring/main.cpp b/src/corelib/doc/snippets/qstring/main.cpp
index 41ee5a9cef..3d09e3618e 100644
--- a/src/corelib/doc/snippets/qstring/main.cpp
+++ b/src/corelib/doc/snippets/qstring/main.cpp
@@ -262,6 +262,17 @@ void Widget::argFunction()
.arg(i).arg(total).arg(fileName);
//! [11]
+ {
+ //! [11-qstringview]
+ int i; // current file's number
+ int total; // number of files to process
+ QStringView fileName; // current file's name
+
+ QString status = QString("Processing file %1 of %2: %3")
+ .arg(i).arg(total).arg(fileName);
+ //! [11-qstringview]
+ }
+
//! [12] //! [13]
QString str;
//! [12]
@@ -321,6 +332,12 @@ void Widget::compareSensitiveFunction()
int y = QString::compare("auto", "Car", Qt::CaseSensitive); // y > 0
int z = QString::compare("auto", "Car", Qt::CaseInsensitive); // z < 0
//! [16]
+
+ //! [QtPrivate::compareStrings-QSV-QSV]
+ int x = QtPrivate::compareStrings(u"aUtO", u"AuTo", Qt::CaseInsensitive); // x == 0
+ int y = QtPrivate::compareStrings(u"auto", u"Car", Qt::CaseSensitive); // y > 0
+ int z = QtPrivate::compareStrings(u"auto", u"Car", Qt::CaseInsensitive); // z < 0
+ //! [QtPrivate::compareStrings-QSV-QSV]
}
void Widget::containsFunction()
diff --git a/src/corelib/doc/snippets/qversionnumber/main.cpp b/src/corelib/doc/snippets/qversionnumber/main.cpp
index 0e9997b6dc..27b7b1a7ef 100644
--- a/src/corelib/doc/snippets/qversionnumber/main.cpp
+++ b/src/corelib/doc/snippets/qversionnumber/main.cpp
@@ -95,6 +95,14 @@ void QObject::parse()
// version is 5.4.0
// suffixIndex is 5
//! [3]
+
+ //! [3-latin1-1]
+ QLatin1String string("5.4.0-alpha");
+ int suffixIndex;
+ auto version = QVersionNumber::fromString(string, &suffixIndex);
+ // version is 5.4.0
+ // suffixIndex is 5
+ //! [3-latin1-1]
}
void Object::equivalent()
diff --git a/src/corelib/doc/snippets/signalmapper/filereader.cpp b/src/corelib/doc/snippets/signalmapper/filereader.cpp
deleted file mode 100644
index 674f73d671..0000000000
--- a/src/corelib/doc/snippets/signalmapper/filereader.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the config.tests of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** 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
-** 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.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtGui>
-#include "filereader.h"
-
-
-FileReader::FileReader(QWidget *parent)
- : QWidget(parent)
-{
- textEdit = new QTextEdit;
-
- taxFileButton = new QPushButton("Tax File");
- accountFileButton = new QPushButton("Accounts File");
- reportFileButton = new QPushButton("Report File");
-
-//! [0]
- signalMapper = new QSignalMapper(this);
- signalMapper->setMapping(taxFileButton, QString("taxfile.txt"));
- signalMapper->setMapping(accountFileButton, QString("accountsfile.txt"));
- signalMapper->setMapping(reportFileButton, QString("reportfile.txt"));
-
- connect(taxFileButton, &QPushButton::clicked,
- signalMapper, &QSignalMapper::map);
- connect(accountFileButton, &QPushButton::clicked,
- signalMapper, &QSignalMapper::map);
- connect(reportFileButton, &QPushButton::clicked,
- signalMapper, &QSignalMapper::map);
-//! [0]
-
-//! [1]
- connect(signalMapper, SIGNAL(mapped(QString)),
- this, SLOT(readFile(QString)));
-//! [1]
-
-/*
-//! [2]
- //slower due to signature normalization at runtime
-
- connect(signalMapper, SIGNAL(mapped(QString)),
- this, SLOT(readFile(QString)));
-//! [2]
-*/
- QHBoxLayout *buttonLayout = new QHBoxLayout;
- buttonLayout->addWidget(taxFileButton);
- buttonLayout->addWidget(accountFileButton);
- buttonLayout->addWidget(reportFileButton);
-
- QVBoxLayout *mainLayout = new QVBoxLayout;
- mainLayout->addWidget(textEdit);
- mainLayout->addLayout(buttonLayout);
-
- setLayout(mainLayout);
-}
-
-void FileReader::readFile(const QString &filename)
-{
- QFile file(filename);
-
- if (!file.open(QIODevice::ReadOnly)) {
- QMessageBox::information(this, tr("Unable to open file"),
- file.errorString());
- return;
- }
-
-
- QTextStream in(&file);
- textEdit->setPlainText(in.readAll());
-}
-
diff --git a/src/corelib/doc/src/objectmodel/signalsandslots.qdoc b/src/corelib/doc/src/objectmodel/signalsandslots.qdoc
index ed61511c62..6d3064d217 100644
--- a/src/corelib/doc/src/objectmodel/signalsandslots.qdoc
+++ b/src/corelib/doc/src/objectmodel/signalsandslots.qdoc
@@ -395,24 +395,12 @@
signal, Qt provides the QObject::sender() function, which returns
a pointer to the object that sent the signal.
- The QSignalMapper class is provided for situations where many
- signals are connected to the same slot and the slot needs to
- handle each signal differently.
+ Lambda expressions are a convenient way to pass custom arguments to a slot:
- Suppose you have three push buttons that determine which file you
- will open: "Tax File", "Accounts File", or "Report File".
-
- In order to open the correct file, you use QSignalMapper::setMapping() to
- map all the QPushButton::clicked() signals to a QSignalMapper object. Then you connect
- the file's QPushButton::clicked() signal to the QSignalMapper::map() slot.
-
- \snippet signalmapper/filereader.cpp 0
-
- Then, you connect the \l{QSignalMapper::}{mapped()} signal to
- \c{readFile()} where a different file will be opened, depending on
- which push button is pressed.
-
- \snippet signalmapper/filereader.cpp 1
+ \code
+ connect(action, &QAction::triggered, engine,
+ [=]() { engine->processAction(action->text()); });
+ \endcode
\sa {Meta-Object System}, {Qt's Property System}
diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri
index a3c1c4d65e..e1ca8828f9 100644
--- a/src/corelib/global/global.pri
+++ b/src/corelib/global/global.pri
@@ -9,6 +9,7 @@ HEADERS += \
global/qprocessordetection.h \
global/qnamespace.h \
global/qendian.h \
+ global/qendian_p.h \
global/qnumeric_p.h \
global/qnumeric.h \
global/qfloat16_p.h \
@@ -21,6 +22,8 @@ HEADERS += \
global/qisenum.h \
global/qtypetraits.h \
global/qflags.h \
+ global/qrandom.h \
+ global/qrandom_p.h \
global/qhooks_p.h \
global/qversiontagging.h
@@ -33,6 +36,7 @@ SOURCES += \
global/qfloat16.cpp \
global/qoperatingsystemversion.cpp \
global/qlogging.cpp \
+ global/qrandom.cpp \
global/qhooks.cpp
VERSIONTAGGING_SOURCES = global/qversiontagging.cpp
@@ -55,6 +59,23 @@ if(linux*|hurd*):!cross_compile:!static:!*-armcc* {
DEFINES += ELF_INTERPRETER=\\\"$$system(LC_ALL=C readelf -l /bin/ls | perl -n -e \'$$prog\')\\\"
}
+linux:!static {
+ precompile_header {
+ # we'll get an error if we just use SOURCES +=
+ no_pch_assembler.commands = $$QMAKE_CC -c $(CFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
+ no_pch_assembler.dependency_type = TYPE_C
+ no_pch_assembler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+ no_pch_assembler.input = NO_PCH_ASM
+ no_pch_assembler.name = compiling[no_pch] ${QMAKE_FILE_IN}
+ silent: no_pch_assembler.commands = @echo compiling[no_pch] ${QMAKE_FILE_IN} && $$no_pch_assembler.commands
+ QMAKE_EXTRA_COMPILERS += no_pch_assembler
+ NO_PCH_ASM += global/minimum-linux.S
+ } else {
+ SOURCES += global/minimum-linux.S
+ }
+ HEADERS += global/minimum-linux_p.h
+}
+
qtConfig(slog2): \
LIBS_PRIVATE += -lslog2
diff --git a/src/corelib/global/minimum-linux.S b/src/corelib/global/minimum-linux.S
new file mode 100644
index 0000000000..dfc3cec1be
--- /dev/null
+++ b/src/corelib/global/minimum-linux.S
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "minimum-linux_p.h"
+
+/* Copied from #include <elf.h>:
+ */
+#define ELF_NOTE_GNU "GNU"
+#define NT_GNU_ABI_TAG 1
+#define ELF_NOTE_OS_LINUX 0
+
+#ifdef __arm__
+# define progbits %progbits
+# define note %note
+#else
+# define progbits @progbits
+# define note @note
+#endif
+
+/* Add information for the ELF dynamic linker what the minimum Linux version
+ * required for Qt is.
+ *
+ * The .note.ABI-tag note section is defined at
+ * https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/noteabitag.html
+ */
+
+ .section ".note.GNU-stack", "", progbits
+ .section ".note.ABI-tag", "a", note
+ .align 4 /* we have 32-bit data */
+
+/* * For the format of the note section's contents, see Elf32_Nhdr / Elf64_Nhdr */
+ .long .Lnameend-.Lname /* n_namesz */
+ .long 16 /* n_descsz(16 bytes, normative) */
+ .long NT_GNU_ABI_TAG /* n_type */
+
+.Lname:
+ .asciz ELF_NOTE_GNU
+.Lnameend:
+
+/* Operating systems: */
+ .long ELF_NOTE_OS_LINUX
+
+ .long MINLINUX_MAJOR
+ .long MINLINUX_MINOR
+ .long MINLINUX_PATCH
diff --git a/src/corelib/global/minimum-linux_p.h b/src/corelib/global/minimum-linux_p.h
new file mode 100644
index 0000000000..324744b856
--- /dev/null
+++ b/src/corelib/global/minimum-linux_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MINIMUMLINUX_P_H
+#define MINIMUMLINUX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// EXTRA WARNING
+// -------------
+//
+// This file must also be valid assembler source.
+//
+
+#include "private/qglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/* Minimum Linux kernel version:
+ * We require the following features in Qt (unconditional, no fallback):
+ * Feature Added in version Macro
+ * - inotify_init1 before 2.6.12-rc12
+ * - futex(2) before 2.6.12-rc12
+ * - FUTEX_WAKE_OP 2.6.14 FUTEX_OP
+ * - linkat(2) 2.6.17 O_TMPFILE
+ * - FUTEX_PRIVATE_FLAG 2.6.22
+ * - O_CLOEXEC 2.6.23
+ * - eventfd 2.6.23
+ * - pipe2 & dup3 2.6.27
+ * - accept4 2.6.28
+ * - renameat2 3.16 QT_CONFIG(renameat2)
+ * - getrandom 3.17 QT_CONFIG(getentropy)
+ */
+
+#if QT_CONFIG(getentropy)
+# define MINLINUX_MAJOR 3
+# define MINLINUX_MINOR 17
+# define MINLINUX_PATCH 0
+#elif QT_CONFIG(renameat2)
+# define MINLINUX_MAJOR 3
+# define MINLINUX_MINOR 16
+# define MINLINUX_PATCH 0
+#else
+# define MINLINUX_MAJOR 2
+# define MINLINUX_MINOR 6
+# define MINLINUX_PATCH 28
+#endif
+
+#define MINIMUM_LINUX_VERSION QT_VERSION_CHECK(MINLINUX_MAJOR, MINLINUX_MINOR, MINLINUX_PATCH)
+
+QT_END_NAMESPACE
+
+#endif // MINIMUMLINUX_P_H
diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h
index 05a492fb96..2164d7f21f 100644
--- a/src/corelib/global/qconfig-bootstrapped.h
+++ b/src/corelib/global/qconfig-bootstrapped.h
@@ -75,17 +75,24 @@
# define QT_FEATURE_alloca_malloc_h -1
#endif
#define QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+#define QT_FEATURE_cxx11_random (QT_HAS_INCLUDE(<random>) ? 1 : -1)
#define QT_NO_DATASTREAM
#define QT_FEATURE_datetimeparser -1
+#define QT_FEATURE_getauxval (QT_HAS_INCLUDE(<sys/auxv.h>) ? 1 : -1)
+#define QT_FEATURE_getentropy -1
#define QT_NO_GEOM_VARIANT
#define QT_FEATURE_iconv -1
#define QT_FEATURE_icu -1
#define QT_FEATURE_journald -1
+#define QT_FEATURE_futimens -1
+#define QT_FEATURE_futimes -1
#define QT_FEATURE_library -1
#define QT_NO_QOBJECT
#define QT_FEATURE_process -1
+#define QT_FEATURE_renameat2 -1
#define QT_FEATURE_sharedmemory -1
#define QT_FEATURE_slog2 -1
+#define QT_FEATURE_statx -1
#define QT_FEATURE_syslog -1
#define QT_NO_SYSTEMLOCALE
#define QT_FEATURE_systemsemaphore -1
diff --git a/src/corelib/global/qendian.h b/src/corelib/global/qendian.h
index 4e9cd2e6e3..3337829de0 100644
--- a/src/corelib/global/qendian.h
+++ b/src/corelib/global/qendian.h
@@ -52,22 +52,6 @@ QT_BEGIN_NAMESPACE
/*
* ENDIAN FUNCTIONS
*/
-inline void qbswap_helper(const void *src, void *dest, int size)
-{
- for (int i = 0; i < size ; ++i)
- static_cast<uchar *>(dest)[i] = static_cast<const uchar *>(src)[size - 1 - i];
-}
-
-/*
- * qbswap(const T src, const void *dest);
- * Changes the byte order of \a src from big endian to little endian or vice versa
- * and stores the result in \a dest.
- * There is no alignment requirements for \a dest.
-*/
-template <typename T> inline void qbswap(const T src, void *dest)
-{
- qbswap_helper(&src, dest, sizeof(T));
-}
// Used to implement a type-safe and alignment-safe copy operation
// If you want to avoid the memcpy, you must write specializations for these functions
@@ -103,30 +87,11 @@ template <typename T> Q_ALWAYS_INLINE T qFromUnaligned(const void *src)
* This function can be used if you are not concerned about alignment issues,
* and it is therefore a bit more convenient and in most cases more efficient.
*/
-template <typename T> T qbswap(T source);
-
-// GCC 4.3 implemented all the intrinsics, but the 16-bit one only got implemented in 4.8;
-// Clang 2.6 implemented the 32- and 64-bit but waited until 3.2 to implement the 16-bit one
-#if (defined(Q_CC_GNU) && Q_CC_GNU >= 403) || QT_HAS_BUILTIN(__builtin_bswap32)
-template <> inline quint64 qbswap<quint64>(quint64 source)
-{
- return __builtin_bswap64(source);
-}
-template <> inline quint32 qbswap<quint32>(quint32 source)
-{
- return __builtin_bswap32(source);
-}
+template <typename T> Q_DECL_CONSTEXPR T qbswap(T source);
-template <> inline void qbswap<quint64>(quint64 source, void *dest)
-{
- qToUnaligned<quint64>(__builtin_bswap64(source), dest);
-}
-template <> inline void qbswap<quint32>(quint32 source, void *dest)
-{
- qToUnaligned<quint32>(__builtin_bswap32(source), dest);
-}
-#else
-template <> inline quint64 qbswap<quint64>(quint64 source)
+// These definitions are written so that they are recognized by most compilers
+// as bswap and replaced with single instruction builtins if available.
+template <> inline Q_DECL_CONSTEXPR quint64 qbswap<quint64>(quint64 source)
{
return 0
| ((source & Q_UINT64_C(0x00000000000000ff)) << 56)
@@ -139,7 +104,7 @@ template <> inline quint64 qbswap<quint64>(quint64 source)
| ((source & Q_UINT64_C(0xff00000000000000)) >> 56);
}
-template <> inline quint32 qbswap<quint32>(quint32 source)
+template <> inline Q_DECL_CONSTEXPR quint32 qbswap<quint32>(quint32 source)
{
return 0
| ((source & 0x000000ff) << 24)
@@ -147,65 +112,61 @@ template <> inline quint32 qbswap<quint32>(quint32 source)
| ((source & 0x00ff0000) >> 8)
| ((source & 0xff000000) >> 24);
}
-#endif // GCC & Clang intrinsics
-#if (defined(Q_CC_GNU) && Q_CC_GNU >= 408) || QT_HAS_BUILTIN(__builtin_bswap16)
-template <> inline quint16 qbswap<quint16>(quint16 source)
-{
- return __builtin_bswap16(source);
-}
-template <> inline void qbswap<quint16>(quint16 source, void *dest)
-{
- qToUnaligned<quint16>(__builtin_bswap16(source), dest);
-}
-#else
-template <> inline quint16 qbswap<quint16>(quint16 source)
+
+template <> inline Q_DECL_CONSTEXPR quint16 qbswap<quint16>(quint16 source)
{
return quint16( 0
| ((source & 0x00ff) << 8)
| ((source & 0xff00) >> 8) );
}
-#endif // GCC & Clang intrinsics
+
+template <> inline Q_DECL_CONSTEXPR quint8 qbswap<quint8>(quint8 source)
+{
+ return source;
+}
// signed specializations
-template <> inline qint64 qbswap<qint64>(qint64 source)
+template <> inline Q_DECL_CONSTEXPR qint64 qbswap<qint64>(qint64 source)
{
return qbswap<quint64>(quint64(source));
}
-template <> inline qint32 qbswap<qint32>(qint32 source)
+template <> inline Q_DECL_CONSTEXPR qint32 qbswap<qint32>(qint32 source)
{
return qbswap<quint32>(quint32(source));
}
-template <> inline qint16 qbswap<qint16>(qint16 source)
+template <> inline Q_DECL_CONSTEXPR qint16 qbswap<qint16>(qint16 source)
{
return qbswap<quint16>(quint16(source));
}
-template <> inline void qbswap<qint64>(qint64 source, void *dest)
+template <> inline Q_DECL_CONSTEXPR qint8 qbswap<qint8>(qint8 source)
{
- qbswap<quint64>(quint64(source), dest);
+ return source;
}
-template <> inline void qbswap<qint32>(qint32 source, void *dest)
+/*
+ * qbswap(const T src, const void *dest);
+ * Changes the byte order of \a src from big endian to little endian or vice versa
+ * and stores the result in \a dest.
+ * There is no alignment requirements for \a dest.
+*/
+template <typename T> inline void qbswap(const T src, void *dest)
{
- qbswap<quint32>(quint32(source), dest);
+ qToUnaligned<T>(qbswap<T>(src), dest);
}
-template <> inline void qbswap<qint16>(qint16 source, void *dest)
-{
- qbswap<quint16>(quint16(source), dest);
-}
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
-template <typename T> inline T qToBigEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qToBigEndian(T source)
{ return source; }
-template <typename T> inline T qFromBigEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qFromBigEndian(T source)
{ return source; }
-template <typename T> inline T qToLittleEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qToLittleEndian(T source)
{ return qbswap<T>(source); }
-template <typename T> inline T qFromLittleEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qFromLittleEndian(T source)
{ return qbswap<T>(source); }
template <typename T> inline void qToBigEndian(T src, void *dest)
{ qToUnaligned<T>(src, dest); }
@@ -213,13 +174,13 @@ template <typename T> inline void qToLittleEndian(T src, void *dest)
{ qbswap<T>(src, dest); }
#else // Q_LITTLE_ENDIAN
-template <typename T> inline T qToBigEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qToBigEndian(T source)
{ return qbswap<T>(source); }
-template <typename T> inline T qFromBigEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qFromBigEndian(T source)
{ return qbswap<T>(source); }
-template <typename T> inline T qToLittleEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qToLittleEndian(T source)
{ return source; }
-template <typename T> inline T qFromLittleEndian(T source)
+template <typename T> inline Q_DECL_CONSTEXPR T qFromLittleEndian(T source)
{ return source; }
template <typename T> inline void qToBigEndian(T src, void *dest)
{ qbswap<T>(src, dest); }
@@ -228,15 +189,6 @@ template <typename T> inline void qToLittleEndian(T src, void *dest)
#endif // Q_BYTE_ORDER == Q_BIG_ENDIAN
-template <> inline quint8 qbswap<quint8>(quint8 source)
-{
- return source;
-}
-
-template <> inline qint8 qbswap<qint8>(qint8 source)
-{
- return source;
-}
/* T qFromLittleEndian(const void *src)
* This function will read a little-endian encoded value from \a src
@@ -267,6 +219,127 @@ template <> inline quint8 qFromBigEndian<quint8>(const void *src)
template <> inline qint8 qFromBigEndian<qint8>(const void *src)
{ return static_cast<const qint8 *>(src)[0]; }
+template<class S>
+class QSpecialInteger
+{
+ typedef typename S::StorageType T;
+ T val;
+public:
+ QSpecialInteger() = default;
+ explicit Q_DECL_CONSTEXPR QSpecialInteger(T i) : val(S::toSpecial(i)) {}
+
+ QSpecialInteger &operator =(T i) { val = S::toSpecial(i); return *this; }
+ operator T() const { return S::fromSpecial(val); }
+
+ bool operator ==(QSpecialInteger<S> i) const { return val == i.val; }
+ bool operator !=(QSpecialInteger<S> i) const { return val != i.val; }
+
+ QSpecialInteger &operator +=(T i)
+ { return (*this = S::fromSpecial(val) + i); }
+ QSpecialInteger &operator -=(T i)
+ { return (*this = S::fromSpecial(val) - i); }
+ QSpecialInteger &operator *=(T i)
+ { return (*this = S::fromSpecial(val) * i); }
+ QSpecialInteger &operator >>=(T i)
+ { return (*this = S::fromSpecial(val) >> i); }
+ QSpecialInteger &operator <<=(T i)
+ { return (*this = S::fromSpecial(val) << i); }
+ QSpecialInteger &operator /=(T i)
+ { return (*this = S::fromSpecial(val) / i); }
+ QSpecialInteger &operator %=(T i)
+ { return (*this = S::fromSpecial(val) % i); }
+ QSpecialInteger &operator |=(T i)
+ { return (*this = S::fromSpecial(val) | i); }
+ QSpecialInteger &operator &=(T i)
+ { return (*this = S::fromSpecial(val) & i); }
+ QSpecialInteger &operator ^=(T i)
+ { return (*this = S::fromSpecial(val) ^ i); }
+};
+
+template<typename T>
+class QLittleEndianStorageType {
+public:
+ typedef T StorageType;
+ static Q_DECL_CONSTEXPR T toSpecial(T source) { return qToLittleEndian(source); }
+ static Q_DECL_CONSTEXPR T fromSpecial(T source) { return qFromLittleEndian(source); }
+};
+
+template<typename T>
+class QBigEndianStorageType {
+public:
+ typedef T StorageType;
+ static Q_DECL_CONSTEXPR T toSpecial(T source) { return qToBigEndian(source); }
+ static Q_DECL_CONSTEXPR T fromSpecial(T source) { return qFromBigEndian(source); }
+};
+
+#ifdef Q_QDOC
+class QLEInteger {
+public:
+ explicit Q_DECL_CONSTEXPR QLEInteger(T i);
+ QLEInteger &operator =(T i);
+ operator T() const;
+ bool operator ==(QLEInteger i) const;
+ bool operator !=(QLEInteger i) const;
+ QLEInteger &operator +=(T i);
+ QLEInteger &operator -=(T i);
+ QLEInteger &operator *=(T i);
+ QLEInteger &operator >>=(T i);
+ QLEInteger &operator <<=(T i);
+ QLEInteger &operator /=(T i);
+ QLEInteger &operator %=(T i);
+ QLEInteger &operator |=(T i);
+ QLEInteger &operator &=(T i);
+ QLEInteger &operator ^=(T i);
+};
+
+class QBEInteger {
+public:
+ explicit Q_DECL_CONSTEXPR QBEInteger(T i);
+ QBEInteger &operator =(T i);
+ operator T() const;
+ bool operator ==(QBEInteger i) const;
+ bool operator !=(QBEInteger i) const;
+ QBEInteger &operator +=(T i);
+ QBEInteger &operator -=(T i);
+ QBEInteger &operator *=(T i);
+ QBEInteger &operator >>=(T i);
+ QBEInteger &operator <<=(T i);
+ QBEInteger &operator /=(T i);
+ QBEInteger &operator %=(T i);
+ QBEInteger &operator |=(T i);
+ QBEInteger &operator &=(T i);
+ QBEInteger &operator ^=(T i);
+};
+#else
+
+template<typename T>
+using QLEInteger = QSpecialInteger<QLittleEndianStorageType<T>>;
+
+template<typename T>
+using QBEInteger = QSpecialInteger<QBigEndianStorageType<T>>;
+#endif
+template <typename T>
+class QTypeInfo<QLEInteger<T> >
+ : public QTypeInfoMerger<QLEInteger<T>, T> {};
+
+template <typename T>
+class QTypeInfo<QBEInteger<T> >
+ : public QTypeInfoMerger<QBEInteger<T>, T> {};
+
+typedef QLEInteger<qint16> qint16_le;
+typedef QLEInteger<qint32> qint32_le;
+typedef QLEInteger<qint64> qint64_le;
+typedef QLEInteger<quint16> quint16_le;
+typedef QLEInteger<quint32> quint32_le;
+typedef QLEInteger<quint64> quint64_le;
+
+typedef QBEInteger<qint16> qint16_be;
+typedef QBEInteger<qint32> qint32_be;
+typedef QBEInteger<qint64> qint64_be;
+typedef QBEInteger<quint16> quint16_be;
+typedef QBEInteger<quint32> quint32_be;
+typedef QBEInteger<quint64> quint64_be;
+
QT_END_NAMESPACE
#endif // QENDIAN_H
diff --git a/src/corelib/global/qendian.qdoc b/src/corelib/global/qendian.qdoc
index 9ce9dbdb0e..2ccdea5979 100644
--- a/src/corelib/global/qendian.qdoc
+++ b/src/corelib/global/qendian.qdoc
@@ -183,3 +183,372 @@
unmodified.
*/
+/*!
+ \class QLEInteger
+ \inmodule QtCore
+ \brief The QLEInteger class provides platform-independent little-endian integers.
+ \since 5.10
+
+ The template parameter \c T must be a C++ integer type:
+ \list
+ \li 8-bit: char, signed char, unsigned char, qint8, quint8
+ \li 16-bit: short, unsigned short, qint16, quint16, char16_t
+ \li 32-bit: int, unsigned int, qint32, quint32, char32_t
+ \li 64-bit: long long, unsigned long long, qint64, quint64
+ \li platform-specific size: long, unsigned long
+ \li pointer size: qintptr, quintptr, qptrdiff
+ \endlist
+
+ \note Using this class may be slower than using native integers, so only use it when
+ an exact endian is needed.
+*/
+
+/*! \fn QLEInteger::QLEInteger(T value)
+
+ Constructs a QLEInteger with the given \a value.
+*/
+
+/*! \fn QLEInteger &QLEInteger::operator=(T value)
+
+ Assigns \a value to this QLEInteger and returns a reference to
+ this QLEInteger.
+*/
+
+/*!
+ \fn QLEInteger::operator T() const
+
+ Returns the value of this QLEInteger as a native integer.
+*/
+
+/*!
+ \fn bool QLEInteger::operator==(QLEInteger other) const
+
+ Returns \c true if the value of this QLEInteger is equal to the value of \a other.
+*/
+
+/*!
+ \fn bool QLEInteger::operator!=(QLEInteger other) const
+
+ Returns \c true if the value of this QLEInteger is not equal to the value of \a other.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator+=(T i)
+
+ Adds \a i to this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator-=(T i)
+
+ Subtracts \a i from this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator*=(T i)
+
+ Multiplies \a i with this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator/=(T i)
+
+ Divides this QLEInteger with \a i and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator%=(T i)
+
+ Sets this QLEInteger to the remainder of a division by \a i and
+ returns a reference to this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator>>=(T i)
+
+ Performs a left-shift by \a i on this QLEInteger and returns a
+ reference to this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator<<=(T i)
+
+ Performs a right-shift by \a i on this QLEInteger and returns a
+ reference to this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator|=(T i)
+
+ Performs a bitwise OR with \a i onto this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator&=(T i)
+
+ Performs a bitwise AND with \a i onto this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QLEInteger &QLEInteger::operator^=(T i)
+
+ Performs a bitwise XOR with \a i onto this QLEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \class QBEInteger
+ \inmodule QtCore
+ \brief The QBEInteger class provides platform-independent big-endian integers.
+ \since 5.10
+
+ The template parameter \c T must be a C++ integer type:
+ \list
+ \li 8-bit: char, signed char, unsigned char, qint8, quint8
+ \li 16-bit: short, unsigned short, qint16, quint16, char16_t (C++11)
+ \li 32-bit: int, unsigned int, qint32, quint32, char32_t (C++11)
+ \li 64-bit: long long, unsigned long long, qint64, quint64
+ \li platform-specific size: long, unsigned long
+ \li pointer size: qintptr, quintptr, qptrdiff
+ \endlist
+
+ \note Using this class may be slower than using native integers, so only use it when
+ an exact endian is needed.
+*/
+
+/*! \fn QBEInteger::QBEInteger(T value)
+
+ Constructs a QBEInteger with the given \a value.
+*/
+
+/*! \fn QBEInteger &QBEInteger::operator=(T value)
+
+ Assigns \a value to this QBEInteger and returns a reference to
+ this QBEInteger.
+*/
+
+/*!
+ \fn QBEInteger::operator T() const
+
+ Returns the value of this QBEInteger as a native integer.
+*/
+
+/*!
+ \fn bool QBEInteger::operator==(QBEInteger other) const
+
+ Returns \c true if the value of this QBEInteger is equal to the value of \a other.
+*/
+
+/*!
+ \fn bool QBEInteger::operator!=(QBEInteger other) const
+
+ Returns \c true if the value of this QBEInteger is not equal to the value of \a other.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator+=(T i)
+
+ Adds \a i to this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator-=(T i)
+
+ Subtracts \a i from this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator*=(T i)
+
+ Multiplies \a i with this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator/=(T i)
+
+ Divides this QBEInteger with \a i and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator%=(T i)
+
+ Sets this QBEInteger to the remainder of a division by \a i and
+ returns a reference to this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator>>=(T i)
+
+ Performs a left-shift by \a i on this QBEInteger and returns a
+ reference to this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator<<=(T i)
+
+ Performs a right-shift by \a i on this QBEInteger and returns a
+ reference to this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator|=(T i)
+
+ Performs a bitwise OR with \a i onto this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator&=(T i)
+
+ Performs a bitwise AND with \a i onto this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \fn QBEInteger &QBEInteger::operator^=(T i)
+
+ Performs a bitwise XOR with \a i onto this QBEInteger and returns a reference to
+ this object.
+*/
+
+/*!
+ \typedef quint16_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<quint16>. This type is guaranteed to be stored in memory as
+ a 16-bit little-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint16
+*/
+
+/*!
+ \typedef quint32_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<quint32>. This type is guaranteed to be stored in memory as
+ a 32-bit little-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint32
+*/
+
+/*!
+ \typedef quint64_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<quint64>. This type is guaranteed to be stored in memory as
+ a 64-bit little-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint64
+*/
+
+/*!
+ \typedef quint16_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<quint16>. This type is guaranteed to be stored in memory as
+ a 16-bit big-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint16
+*/
+
+/*!
+ \typedef quint32_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<quint32>. This type is guaranteed to be stored in memory as
+ a 32-bit big-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint32
+*/
+
+/*!
+ \typedef quint64_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<quint64>. This type is guaranteed to be stored in memory as
+ a 64-bit big-endian unsigned integer on all platforms supported by Qt.
+
+ \sa quint64
+*/
+
+/*!
+ \typedef qint16_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<qint16>. This type is guaranteed to be stored in memory as
+ a 16-bit little-endian signed integer on all platforms supported by Qt.
+
+ \sa qint16
+*/
+
+/*!
+ \typedef qint32_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<qint32>. This type is guaranteed to be stored in memory as
+ a 32-bit little-endian signed integer on all platforms supported by Qt.
+
+ \sa qint32
+*/
+
+/*!
+ \typedef qint64_le
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QLEInteger<qint64>. This type is guaranteed to be stored in memory as
+ a 64-bit little-endian signed integer on all platforms supported by Qt.
+
+ \sa qint64
+*/
+
+/*!
+ \typedef qint16_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<qint16>. This type is guaranteed to be stored in memory as
+ a 16-bit big-endian signed integer on all platforms supported by Qt.
+
+ \sa qint16
+*/
+
+/*!
+ \typedef qint32_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<qint32>. This type is guaranteed to be stored in memory as
+ a 32-bit big-endian signed integer on all platforms supported by Qt.
+
+ \sa qint32
+*/
+
+/*!
+ \typedef qint64_be
+ \relates <QtEndian>
+ \since 5.10
+
+ Typedef for QBEInteger<qint64>. This type is guaranteed to be stored in memory as
+ a 64-bit big-endian signed integer on all platforms supported by Qt.
+
+ \sa qint64
+*/
diff --git a/src/corelib/global/qendian_p.h b/src/corelib/global/qendian_p.h
new file mode 100644
index 0000000000..c830e65b54
--- /dev/null
+++ b/src/corelib/global/qendian_p.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QENDIAN_P_H
+#define QENDIAN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qendian.h>
+
+QT_BEGIN_NAMESPACE
+
+// Note if using multiple of these bitfields in a union; the underlying storage type must
+// match. Since we always use an unsigned storage type, unsigned and signed versions may
+// be used together, but different bit-widths may not.
+template<class S, int pos, int width>
+class QSpecialIntegerBitfield
+{
+protected:
+ typedef typename S::StorageType T;
+ typedef typename std::make_unsigned<T>::type UT;
+
+ static Q_DECL_CONSTEXPR UT mask()
+ {
+ return ((UT(1) << width) - 1) << pos;
+ }
+public:
+ // FIXME: val is public until qtdeclarative is fixed to not access it directly.
+ UT val;
+
+ QSpecialIntegerBitfield &operator =(T t)
+ {
+ UT i = S::fromSpecial(val);
+ i &= ~mask();
+ i |= (UT(t) << pos) & mask();
+ val = S::toSpecial(i);
+ return *this;
+ }
+ operator T() const
+ {
+ if (std::is_signed<T>::value) {
+ UT i = S::fromSpecial(val);
+ i <<= (sizeof(T) * 8) - width - pos;
+ T t = T(i);
+ t >>= (sizeof(T) * 8) - width;
+ return t;
+ }
+ return (S::fromSpecial(val) & mask()) >> pos;
+ }
+
+ bool operator !() const { return !(val & S::toSpecial(mask())); }
+ bool operator ==(QSpecialIntegerBitfield<S, pos, width> i) const
+ { return ((val ^ i.val) & S::toSpecial(mask())) == 0; }
+ bool operator !=(QSpecialIntegerBitfield<S, pos, width> i) const
+ { return ((val ^ i.val) & S::toSpecial(mask())) != 0; }
+
+ QSpecialIntegerBitfield &operator +=(T i)
+ { return (*this = (T(*this) + i)); }
+ QSpecialIntegerBitfield &operator -=(T i)
+ { return (*this = (T(*this) - i)); }
+ QSpecialIntegerBitfield &operator *=(T i)
+ { return (*this = (T(*this) * i)); }
+ QSpecialIntegerBitfield &operator /=(T i)
+ { return (*this = (T(*this) / i)); }
+ QSpecialIntegerBitfield &operator %=(T i)
+ { return (*this = (T(*this) % i)); }
+ QSpecialIntegerBitfield &operator |=(T i)
+ { return (*this = (T(*this) | i)); }
+ QSpecialIntegerBitfield &operator &=(T i)
+ { return (*this = (T(*this) & i)); }
+ QSpecialIntegerBitfield &operator ^=(T i)
+ { return (*this = (T(*this) ^ i)); }
+ QSpecialIntegerBitfield &operator >>=(T i)
+ { return (*this = (T(*this) >> i)); }
+ QSpecialIntegerBitfield &operator <<=(T i)
+ { return (*this = (T(*this) << i)); }
+};
+
+template<typename T, int pos, int width>
+using QLEIntegerBitfield = QSpecialIntegerBitfield<QLittleEndianStorageType<T>, pos, width>;
+
+template<typename T, int pos, int width>
+using QBEIntegerBitfield = QSpecialIntegerBitfield<QBigEndianStorageType<T>, pos, width>;
+
+template<int pos, int width>
+using qint32_le_bitfield = QLEIntegerBitfield<int, pos, width>;
+template<int pos, int width>
+using quint32_le_bitfield = QLEIntegerBitfield<uint, pos, width>;
+template<int pos, int width>
+using qint32_be_bitfield = QBEIntegerBitfield<int, pos, width>;
+template<int pos, int width>
+using quint32_be_bitfield = QBEIntegerBitfield<uint, pos, width>;
+
+
+QT_END_NAMESPACE
+
+#endif // QENDIAN_P_H
diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp
index 1de1ae65fb..89edfc8787 100644
--- a/src/corelib/global/qfloat16.cpp
+++ b/src/corelib/global/qfloat16.cpp
@@ -41,7 +41,11 @@
QT_BEGIN_NAMESPACE
-/*! \headerfile <QFloat16>
+/*!
+ \headerfile <QFloat16>
+ \title 16-bit Floating Point Support
+ \ingroup funclists
+ \brief The <QFloat16> header file provides 16-bit floating point support.
This header file provides support for half-precision (16-bit) floating
point data with the class \c qfloat16. It is fully compliant with IEEE
@@ -59,24 +63,6 @@ QT_BEGIN_NAMESPACE
\since 5.9
*/
-Q_STATIC_ASSERT_X(sizeof(float) == sizeof(quint32),
- "qfloat16 assumes that floats are 32 bits wide");
-
-// There are a few corner cases regarding denormals where GHS compiler is relying
-// hardware behavior that is not IEC 559 compliant. Therefore the compiler
-// reports std::numeric_limits<float>::is_iec559 as false. This is all right
-// according to our needs.
-
-#if !defined(Q_CC_GHS)
-Q_STATIC_ASSERT_X(std::numeric_limits<float>::is_iec559,
- "Only works with IEEE 754 floating point");
-#endif
-
-Q_STATIC_ASSERT_X(std::numeric_limits<float>::has_infinity &&
- std::numeric_limits<float>::has_quiet_NaN &&
- std::numeric_limits<float>::has_signaling_NaN,
- "Only works with IEEE 754 floating point");
-
/*!
Returns true if the \c qfloat16 \a {f} is equivalent to infinity.
\relates <QFloat16>
diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h
index a0aa9496b4..72e28edf63 100644
--- a/src/corelib/global/qfloat16.h
+++ b/src/corelib/global/qfloat16.h
@@ -130,7 +130,7 @@ inline qfloat16::qfloat16(float f) Q_DECL_NOTHROW
__m128i packhalf = _mm_cvtps_ph(packsingle, 0);
b16 = _mm_extract_epi16(packhalf, 0);
#elif defined (__ARM_FP16_FORMAT_IEEE)
- __fp16 f16 = f;
+ __fp16 f16 = __fp16(f);
memcpy(&b16, &f16, sizeof(quint16));
#else
quint32 u;
@@ -176,7 +176,8 @@ inline qfloat16 operator/(qfloat16 a, qfloat16 b) Q_DECL_NOTHROW { return qfloat
inline FP operator OP(qfloat16 lhs, FP rhs) Q_DECL_NOTHROW { return static_cast<FP>(lhs) OP rhs; } \
inline FP operator OP(FP lhs, qfloat16 rhs) Q_DECL_NOTHROW { return lhs OP static_cast<FP>(rhs); }
#define QF16_MAKE_ARITH_OP_EQ_FP(FP, OP_EQ, OP) \
- inline qfloat16& operator OP_EQ(qfloat16& lhs, FP rhs) Q_DECL_NOTHROW { lhs = qfloat16(static_cast<FP>(lhs) OP rhs); return lhs; }
+ inline qfloat16& operator OP_EQ(qfloat16& lhs, FP rhs) Q_DECL_NOTHROW \
+ { lhs = qfloat16(float(static_cast<FP>(lhs) OP rhs)); return lhs; }
#define QF16_MAKE_ARITH_OP(FP) \
QF16_MAKE_ARITH_OP_FP(FP, +) \
QF16_MAKE_ARITH_OP_FP(FP, -) \
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 8d614b4f91..36b7560398 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -42,7 +42,6 @@
#include "qstring.h"
#include "qvector.h"
#include "qlist.h"
-#include "qthreadstorage.h"
#include "qdir.h"
#include "qdatetime.h"
#include "qoperatingsystemversion.h"
@@ -131,6 +130,38 @@ Q_CORE_EXPORT void *qMemSet(void *dest, int c, size_t n);
Q_STATIC_ASSERT_X(sizeof(int) == 4, "Qt assumes that int is 32 bits");
Q_STATIC_ASSERT_X(UCHAR_MAX == 255, "Qt assumes that char is 8 bits");
Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined incorrectly");
+Q_STATIC_ASSERT_X(sizeof(float) == 4, "Qt assumes that float is 32 bits");
+
+// While we'd like to check for __STDC_IEC_559__, as per ISO/IEC 9899:2011
+// Annex F (C11, normative for C++11), there are a few corner cases regarding
+// denormals where GHS compiler is relying hardware behavior that is not IEC
+// 559 compliant. So split the check in several subchecks.
+
+// On GHC the compiler reports std::numeric_limits<float>::is_iec559 as false.
+// This is all right according to our needs.
+#if !defined(Q_CC_GHS)
+Q_STATIC_ASSERT_X(std::numeric_limits<float>::is_iec559,
+ "Qt assumes IEEE 754 floating point");
+#endif
+
+// Technically, presence of NaN and infinities are implied from the above check,
+// but double checking our environment doesn't hurt...
+Q_STATIC_ASSERT_X(std::numeric_limits<float>::has_infinity &&
+ std::numeric_limits<float>::has_quiet_NaN &&
+ std::numeric_limits<float>::has_signaling_NaN,
+ "Qt assumes IEEE 754 floating point");
+
+// is_iec559 checks for ISO/IEC/IEEE 60559:2011 (aka IEEE 754-2008) compliance,
+// but that allows for a non-binary radix. We need to recheck that.
+// Note how __STDC_IEC_559__ would instead check for IEC 60559:1989, aka
+// ANSI/IEEE 754−1985, which specifically implies binary floating point numbers.
+Q_STATIC_ASSERT_X(std::numeric_limits<float>::radix == 2,
+ "Qt assumes binary IEEE 754 floating point");
+
+// not required by the definition of size_t, but we depend on this
+Q_STATIC_ASSERT_X(sizeof(size_t) == sizeof(void *), "size_t and a pointer don't have the same size");
+Q_STATIC_ASSERT(sizeof(size_t) == sizeof(qssize_t)); // implied by the definition
+Q_STATIC_ASSERT((std::is_same<qssize_t, qptrdiff>::value));
/*!
\class QFlag
@@ -793,6 +824,21 @@ Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined in
*/
/*!
+ \typedef qssize_t
+ \relates <QtGlobal>
+ \since 5.10
+
+ Integral type providing Posix' \c ssize_t for all platforms.
+
+ This type is guaranteed to be the same size as a \c size_t on all
+ platforms supported by Qt.
+
+ Note that qssize_t is signed. Use \c size_t for unsigned values.
+
+ \sa qptrdiff
+*/
+
+/*!
\enum QtMsgType
\relates <QtGlobal>
@@ -3091,7 +3137,7 @@ void qt_assert_x(const char *where, const char *what, const char *file, int line
Deliberately not exported as part of the Qt API, but used in both
qsimplerichtext.cpp and qgfxraster_qws.cpp
*/
-Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n)
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION unsigned int qt_int_sqrt(unsigned int n)
{
// n must be in the range 0...UINT_MAX/2-1
if (n >= (UINT_MAX>>2)) {
@@ -3121,24 +3167,27 @@ void *qMemSet(void *dest, int c, size_t n) { return memset(dest, c, n); }
// add thread-safety for the Qt wrappers.
static QBasicMutex environmentMutex;
-// getenv is declared as deprecated in VS2005. This function
-// makes use of the new secure getenv function.
/*!
\relates <QtGlobal>
+ \threadsafe
- Returns the value of the environment variable with name \a
- varName. To get the variable string, use QByteArray::constData().
- To convert the data to a QString use QString::fromLocal8Bit().
+ Returns the value of the environment variable with name \a varName as a
+ QByteArray. If no variable by that name is found in the environment, this
+ function returns a default-constructed QByteArray.
- \note qgetenv() was introduced because getenv() from the standard
- C library was deprecated in VC2005 (and later versions). qgetenv()
- uses the new replacement function in VC, and calls the standard C
- library's implementation on all other platforms.
+ The Qt environment manipulation functions are thread-safe, but this
+ requires that the C library equivalent functions like getenv and putenv are
+ not directly called.
- \warning Don't use qgetenv on Windows if the content may contain
- non-US-ASCII characters, like file paths.
+ To convert the data to a QString use QString::fromLocal8Bit().
+
+ \note on desktop Windows, qgetenv() may produce data loss if the
+ original string contains Unicode characters not representable in the
+ ANSI encoding. Use qEnvironmentVariable() instead.
+ On Unix systems, this function is lossless.
- \sa qputenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty()
+ \sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(),
+ qEnvironmentVariableIsEmpty()
*/
QByteArray qgetenv(const char *varName)
{
@@ -3160,6 +3209,87 @@ QByteArray qgetenv(const char *varName)
#endif
}
+
+/*!
+ \relates <QtGlobal>
+ \since 5.10
+
+ Returns the value of the environment variable with name \a varName as a
+ QString. If no variable by that name is found in the environment, this
+ function returns \a defaultValue.
+
+ The Qt environment manipulation functions are thread-safe, but this
+ requires that the C library equivalent functions like getenv and putenv are
+ not directly called.
+
+ The following table describes how to choose between qgetenv() and
+ qEnvironmentVariable():
+ \table
+ \header \li Condition \li Recommendation
+ \row
+ \li Variable contains file paths or user text
+ \li qEnvironmentVariable()
+ \row
+ \li Windows-specific code
+ \li qEnvironmentVariable()
+ \row
+ \li Unix-specific code, destination variable is not QString and/or is
+ used to interface with non-Qt APIs
+ \li qgetenv()
+ \row
+ \li Destination variable is a QString
+ \li qEnvironmentVariable()
+ \row
+ \li Destination variable is a QByteArray or std::string
+ \li qgetenv()
+ \endtable
+
+ \note on Unix systems, this function may produce data loss if the original
+ string contains arbitrary binary data that cannot be decoded by the locale
+ codec. Use qgetenv() instead for that case. On Windows, this function is
+ lossless.
+
+ \note the variable name \a varName must contain only US-ASCII characters.
+
+ \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty()
+*/
+QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
+{
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+ QMutexLocker locker(&environmentMutex);
+ QVarLengthArray<wchar_t, 32> wname(int(strlen(varName)) + 1);
+ for (int i = 0; i < wname.size(); ++i) // wname.size() is correct: will copy terminating null
+ wname[i] = uchar(varName[i]);
+ size_t requiredSize = 0;
+ QString buffer;
+ _wgetenv_s(&requiredSize, 0, 0, wname.data());
+ if (requiredSize == 0)
+ return defaultValue;
+ buffer.resize(int(requiredSize));
+ _wgetenv_s(&requiredSize, reinterpret_cast<wchar_t *>(buffer.data()), requiredSize,
+ wname.data());
+ // requiredSize includes the terminating null, which we don't want.
+ Q_ASSERT(buffer.endsWith(QLatin1Char('\0')));
+ buffer.chop(1);
+ return buffer;
+#else
+ QByteArray value = qgetenv(varName);
+ if (value.isNull())
+ return defaultValue;
+// duplicated in qfile.h (QFile::decodeName)
+#if defined(Q_OS_DARWIN)
+ return QString::fromUtf8(value).normalized(QString::NormalizationForm_C);
+#else // other Unix
+ return QString::fromLocal8Bit(value);
+#endif
+#endif
+}
+
+QString qEnvironmentVariable(const char *varName)
+{
+ return qEnvironmentVariable(varName, QString());
+}
+
/*!
\relates <QtGlobal>
\since 5.1
@@ -3172,7 +3302,7 @@ QByteArray qgetenv(const char *varName)
\endcode
except that it's potentially much faster, and can't throw exceptions.
- \sa qgetenv(), qEnvironmentVariableIsSet()
+ \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
*/
bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT
{
@@ -3209,7 +3339,7 @@ bool qEnvironmentVariableIsEmpty(const char *varName) Q_DECL_NOEXCEPT
are too long will either be truncated or this function will set \a ok to \c
false.
- \sa qgetenv(), qEnvironmentVariableIsSet()
+ \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
*/
int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT
{
@@ -3260,7 +3390,7 @@ int qEnvironmentVariableIntValue(const char *varName, bool *ok) Q_DECL_NOEXCEPT
\endcode
except that it's potentially much faster, and can't throw exceptions.
- \sa qgetenv(), qEnvironmentVariableIsEmpty()
+ \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty()
*/
bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT
{
@@ -3290,7 +3420,7 @@ bool qEnvironmentVariableIsSet(const char *varName) Q_DECL_NOEXCEPT
uses the replacement function in VC, and calls the standard C
library's implementation on all other platforms.
- \sa qgetenv()
+ \sa qgetenv(), qEnvironmentVariable()
*/
bool qputenv(const char *varName, const QByteArray& value)
{
@@ -3321,7 +3451,7 @@ bool qputenv(const char *varName, const QByteArray& value)
\since 5.1
- \sa qputenv(), qgetenv()
+ \sa qputenv(), qgetenv(), qEnvironmentVariable()
*/
bool qunsetenv(const char *varName)
{
@@ -3346,138 +3476,6 @@ bool qunsetenv(const char *varName)
#endif
}
-#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
-typedef QThreadStorage<QJNIObjectPrivate> AndroidRandomStorage;
-Q_GLOBAL_STATIC(AndroidRandomStorage, randomTLS)
-
-#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
-
-# if defined(Q_OS_INTEGRITY) && defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 500)
-// older versions of INTEGRITY used a long instead of a uint for the seed.
-typedef long SeedStorageType;
-# else
-typedef uint SeedStorageType;
-# endif
-
-typedef QThreadStorage<SeedStorageType *> SeedStorage;
-Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value
-#endif
-
-/*!
- \relates <QtGlobal>
- \since 4.2
-
- Thread-safe version of the standard C++ \c srand() function.
-
- Sets the argument \a seed to be used to generate a new random number sequence of
- pseudo random integers to be returned by qrand().
-
- The sequence of random numbers generated is deterministic per thread. For example,
- if two threads call qsrand(1) and subsequently call qrand(), the threads will get
- the same random number sequence.
-
- \sa qrand()
-*/
-void qsrand(uint seed)
-{
-#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
- if (randomTLS->hasLocalData()) {
- randomTLS->localData().callMethod<void>("setSeed", "(J)V", jlong(seed));
- return;
- }
-
- QJNIObjectPrivate random("java/util/Random",
- "(J)V",
- jlong(seed));
- if (!random.isValid()) {
- srand(seed);
- return;
- }
-
- randomTLS->setLocalData(random);
-#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
- SeedStorage *seedStorage = randTLS();
- if (seedStorage) {
- SeedStorageType *pseed = seedStorage->localData();
- if (!pseed)
- seedStorage->setLocalData(pseed = new SeedStorageType);
- *pseed = seed;
- } else {
- //global static seed storage should always exist,
- //except after being deleted by QGlobalStaticDeleter.
- //But since it still can be called from destructor of another
- //global static object, fallback to srand(seed)
- srand(seed);
- }
-#else
- // On Windows srand() and rand() already use Thread-Local-Storage
- // to store the seed between calls
- // this is also valid for QT_NO_THREAD
- srand(seed);
-#endif
-}
-
-/*!
- \relates <QtGlobal>
- \since 4.2
-
- Thread-safe version of the standard C++ \c rand() function.
-
- Returns a value between 0 and \c RAND_MAX (defined in \c <cstdlib> and
- \c <stdlib.h>), the next number in the current sequence of pseudo-random
- integers.
-
- Use \c qsrand() to initialize the pseudo-random number generator with
- a seed value.
-
- \sa qsrand()
-*/
-int qrand()
-{
-#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
- AndroidRandomStorage *randomStorage = randomTLS();
- if (!randomStorage)
- return rand();
-
- if (randomStorage->hasLocalData()) {
- return randomStorage->localData().callMethod<jint>("nextInt",
- "(I)I",
- RAND_MAX);
- }
-
- QJNIObjectPrivate random("java/util/Random",
- "(J)V",
- jlong(1));
-
- if (!random.isValid())
- return rand();
-
- randomStorage->setLocalData(random);
- return random.callMethod<jint>("nextInt", "(I)I", RAND_MAX);
-#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
- SeedStorage *seedStorage = randTLS();
- if (seedStorage) {
- SeedStorageType *pseed = seedStorage->localData();
- if (!pseed) {
- seedStorage->setLocalData(pseed = new SeedStorageType);
- *pseed = 1;
- }
- return rand_r(pseed);
- } else {
- //global static seed storage should always exist,
- //except after being deleted by QGlobalStaticDeleter.
- //But since it still can be called from destructor of another
- //global static object, fallback to rand()
- return rand();
- }
-#else
- // On Windows srand() and rand() already use Thread-Local-Storage
- // to store the seed between calls
- // this is also valid for QT_NO_THREAD
- return rand();
-#endif
-}
-
/*!
\macro forever
\relates <QtGlobal>
@@ -4380,8 +4378,11 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
stderr. Under Windows, the message is sent to the debugger.
On QNX the message is sent to slogger2. This
function does nothing if \c QT_NO_WARNING_OUTPUT was defined
- during compilation; it exits if the environment variable \c
- QT_FATAL_WARNINGS is not empty.
+ during compilation; it exits if at the nth warning corresponding to the
+ counter in environment variable \c QT_FATAL_WARNINGS. That is, if the
+ environment variable contains the value 1, it will exit on the 1st message;
+ if it contains the value 10, it will exit on the 10th message. Any
+ non-numeric value is equivalent to 1.
This function takes a format string and a list of arguments,
similar to the C printf() function. The format should be a Latin-1
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 35c35ac64d..7b691ca59e 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -46,8 +46,9 @@
# include <cstddef>
# include <utility>
#endif
-
-#include <stddef.h>
+#ifndef __ASSEMBLER__
+# include <stddef.h>
+#endif
/*
QT_VERSION is (major << 16) + (minor << 8) + patch.
@@ -188,6 +189,7 @@ namespace QT_NAMESPACE {}
# define QT_LARGEFILE_SUPPORT 64
#endif
+#ifndef __ASSEMBLER__
QT_BEGIN_NAMESPACE
/*
@@ -355,7 +357,7 @@ typedef double qreal;
#if !defined(QT_NAMESPACE) && defined(__cplusplus) && !defined(Q_QDOC)
extern "C"
#endif
-Q_CORE_EXPORT const char *qVersion(void) Q_DECL_NOTHROW;
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION const char *qVersion(void) Q_DECL_NOTHROW;
#if defined(__cplusplus)
@@ -436,6 +438,9 @@ namespace QtPrivate {
sizeof(void *) == sizeof(quintptr)
&& sizeof(void *) == sizeof(qptrdiff)
+
+ size_t and qssize_t are not guaranteed to be the same size as a pointer, but
+ they usually are.
*/
template <int> struct QIntegerForSize;
template <> struct QIntegerForSize<1> { typedef quint8 Unsigned; typedef qint8 Signed; };
@@ -451,6 +456,7 @@ typedef QIntegerForSize<Q_PROCESSOR_WORDSIZE>::Unsigned qregisteruint;
typedef QIntegerForSizeof<void*>::Unsigned quintptr;
typedef QIntegerForSizeof<void*>::Signed qptrdiff;
typedef qptrdiff qintptr;
+using qssize_t = QIntegerForSizeof<std::size_t>::Signed;
/* moc compats (signals/slots) */
#ifndef QT_MOC_COMPAT
@@ -612,6 +618,18 @@ private:
void *pool;
};
+#else
+
+#define QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(macos, ios, tvos, watchos) (0)
+#define QT_MACOS_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(macos, ios) (0)
+#define QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(macos) (0)
+#define QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(ios) (0)
+#define QT_TVOS_PLATFORM_SDK_EQUAL_OR_ABOVE(tvos) (0)
+#define QT_WATCHOS_PLATFORM_SDK_EQUAL_OR_ABOVE(watchos) (0)
+
+#define QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(osx, ios) (0)
+#define QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(osx) (0)
+
#endif // Q_OS_DARWIN
/*
@@ -660,7 +678,7 @@ Q_NORETURN Q_CORE_EXPORT void qTerminate() Q_DECL_NOTHROW;
# endif
#endif
-Q_CORE_EXPORT bool qSharedBuild() Q_DECL_NOTHROW;
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qSharedBuild() Q_DECL_NOTHROW;
#ifndef Q_OUTOFLINE_TEMPLATE
# define Q_OUTOFLINE_TEMPLATE
@@ -709,9 +727,9 @@ Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line)
#if !defined(Q_ASSERT)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
-# define Q_ASSERT(cond) do { } while ((false) && (cond))
+# define Q_ASSERT(cond) static_cast<void>(false && (cond))
# else
-# define Q_ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop())
+# define Q_ASSERT(cond) ((cond) ? static_cast<void>(0) : qt_assert(#cond, __FILE__, __LINE__))
# endif
#endif
@@ -726,9 +744,9 @@ Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char *
#if !defined(Q_ASSERT_X)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
-# define Q_ASSERT_X(cond, where, what) do { } while ((false) && (cond))
+# define Q_ASSERT_X(cond, where, what) static_cast<void>(false && (cond))
# else
-# define Q_ASSERT_X(cond, where, what) ((!(cond)) ? qt_assert_x(where, what,__FILE__,__LINE__) : qt_noop())
+# define Q_ASSERT_X(cond, where, what) ((cond) ? static_cast<void>(0) : qt_assert_x(where, what, __FILE__, __LINE__))
# endif
#endif
@@ -915,13 +933,9 @@ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantic
# endif
#endif
-namespace QtPrivate {
-template <typename T> struct QAddConst { typedef const T Type; };
-}
-
// this adds const to non-const objects (like std::as_const)
template <typename T>
-Q_DECL_CONSTEXPR typename QtPrivate::QAddConst<T>::Type &qAsConst(T &t) Q_DECL_NOTHROW { return t; }
+Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; }
// prevent rvalue arguments:
template <typename T>
void qAsConst(const T &&) Q_DECL_EQ_DELETE;
@@ -1112,6 +1126,13 @@ template <typename... Args> Q_CONSTEXPR Q_DECL_UNUSED QNonConstOverload<Args...>
class QByteArray;
Q_CORE_EXPORT QByteArray qgetenv(const char *varName);
+#ifdef Q_QDOC
+Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName,
+ const QString &defaultValue = QString());
+#else // need it as two functions because QString is only forward-declared here
+Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName);
+Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName, const QString &defaultValue);
+#endif
Q_CORE_EXPORT bool qputenv(const char *varName, const QByteArray& value);
Q_CORE_EXPORT bool qunsetenv(const char *varName);
@@ -1161,5 +1182,6 @@ QT_END_NAMESPACE
#include <QtCore/qversiontagging.h>
#endif /* __cplusplus */
+#endif /* !__ASSEMBLER__ */
#endif /* QGLOBAL_H */
diff --git a/src/corelib/global/qglobal_p.h b/src/corelib/global/qglobal_p.h
index b1d2836783..0f092e9006 100644
--- a/src/corelib/global/qglobal_p.h
+++ b/src/corelib/global/qglobal_p.h
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2015 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -59,5 +60,77 @@
#include <QtCore/private/qtcore-config_p.h>
#endif
+#if defined(__cplusplus)
+#if !QT_HAS_BUILTIN(__builtin_available)
+#include <initializer_list>
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qversionnumber.h>
+
+QT_BEGIN_NAMESPACE
+
+struct qt_clang_builtin_available_os_version_data {
+ QOperatingSystemVersion::OSType type;
+ const char *version;
+};
+
+static inline bool qt_clang_builtin_available(
+ const std::initializer_list<qt_clang_builtin_available_os_version_data> &versions)
+{
+ for (auto it = versions.begin(); it != versions.end(); ++it) {
+ if (QOperatingSystemVersion::currentType() == it->type) {
+ const auto current = QOperatingSystemVersion::current();
+ return QVersionNumber(
+ current.majorVersion(),
+ current.minorVersion(),
+ current.microVersion()) >= QVersionNumber::fromString(
+ QString::fromLatin1(it->version));
+ }
+ }
+
+ // Result is true if the platform is not any of the checked ones; this matches behavior of
+ // LLVM __builtin_available and @available constructs
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#define QT_AVAILABLE_OS_VER(os, ver) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available_os_version_data){\
+ QT_PREPEND_NAMESPACE(QOperatingSystemVersion)::os, #ver}
+#define QT_AVAILABLE_CAT(L, R) QT_AVAILABLE_CAT_(L, R)
+#define QT_AVAILABLE_CAT_(L, R) L ## R
+#define QT_AVAILABLE_EXPAND(...) QT_AVAILABLE_OS_VER(__VA_ARGS__)
+#define QT_AVAILABLE_SPLIT(os_ver) QT_AVAILABLE_EXPAND(QT_AVAILABLE_CAT(QT_AVAILABLE_SPLIT_, os_ver))
+#define QT_AVAILABLE_SPLIT_macOS MacOS,
+#define QT_AVAILABLE_SPLIT_iOS IOS,
+#define QT_AVAILABLE_SPLIT_tvOS TvOS,
+#define QT_AVAILABLE_SPLIT_watchOS WatchOS,
+#define QT_BUILTIN_AVAILABLE0(e) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available)({})
+#define QT_BUILTIN_AVAILABLE1(a, e) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available)({QT_AVAILABLE_SPLIT(a)})
+#define QT_BUILTIN_AVAILABLE2(a, b, e) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available)({QT_AVAILABLE_SPLIT(a), \
+ QT_AVAILABLE_SPLIT(b)})
+#define QT_BUILTIN_AVAILABLE3(a, b, c, e) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available)({QT_AVAILABLE_SPLIT(a), \
+ QT_AVAILABLE_SPLIT(b), \
+ QT_AVAILABLE_SPLIT(c)})
+#define QT_BUILTIN_AVAILABLE4(a, b, c, d, e) \
+ QT_PREPEND_NAMESPACE(qt_clang_builtin_available)({QT_AVAILABLE_SPLIT(a), \
+ QT_AVAILABLE_SPLIT(b), \
+ QT_AVAILABLE_SPLIT(c), \
+ QT_AVAILABLE_SPLIT(d)})
+#define QT_BUILTIN_AVAILABLE_ARG(arg0, arg1, arg2, arg3, arg4, arg5, ...) arg5
+#define QT_BUILTIN_AVAILABLE_CHOOSER(...) QT_BUILTIN_AVAILABLE_ARG(__VA_ARGS__, \
+ QT_BUILTIN_AVAILABLE4, \
+ QT_BUILTIN_AVAILABLE3, \
+ QT_BUILTIN_AVAILABLE2, \
+ QT_BUILTIN_AVAILABLE1, \
+ QT_BUILTIN_AVAILABLE0, )
+#define __builtin_available(...) QT_BUILTIN_AVAILABLE_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
+#endif // !QT_HAS_BUILTIN(__builtin_available)
+#endif // defined(__cplusplus)
+
#endif // QGLOBAL_P_H
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index b5ba935194..6602d53b08 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -153,19 +153,39 @@ Q_NORETURN
static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message);
static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message);
+static int checked_var_value(const char *varname)
+{
+ // qEnvironmentVariableIntValue returns 0 on both parsing failure and on
+ // empty, but we need to distinguish between the two for backwards
+ // compatibility reasons.
+ QByteArray str = qgetenv(varname);
+ if (str.isEmpty())
+ return 0;
+
+ bool ok;
+ int value = str.toInt(&ok, 0);
+ return ok ? value : 1;
+}
+
static bool isFatal(QtMsgType msgType)
{
if (msgType == QtFatalMsg)
return true;
if (msgType == QtCriticalMsg) {
- static bool fatalCriticals = !qEnvironmentVariableIsEmpty("QT_FATAL_CRITICALS");
- return fatalCriticals;
+ static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS");
+
+ // it's fatal if the current value is exactly 1,
+ // otherwise decrement if it's non-zero
+ return fatalCriticals.load() && fatalCriticals.fetchAndAddRelaxed(-1) == 1;
}
if (msgType == QtWarningMsg || msgType == QtCriticalMsg) {
- static bool fatalWarnings = !qEnvironmentVariableIsEmpty("QT_FATAL_WARNINGS");
- return fatalWarnings;
+ static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS");
+
+ // it's fatal if the current value is exactly 1,
+ // otherwise decrement if it's non-zero
+ return fatalWarnings.load() && fatalWarnings.fetchAndAddRelaxed(-1) == 1;
}
return false;
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index da44c01594..ab83730caa 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -241,8 +241,11 @@ public:
TextForceRightToLeft = 0x40000,
// Ensures that the longest variant is always used when computing the
// size of a multi-variant string.
- TextLongestVariant = 0x80000,
- TextBypassShaping = 0x100000
+ TextLongestVariant = 0x80000
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ , TextBypassShaping = 0x100000
+#endif
};
enum TextElideMode {
@@ -508,6 +511,9 @@ public:
AA_CompressHighFrequencyEvents = 25,
AA_DontCheckOpenGLContextThreadAffinity = 26,
AA_DisableShaderDiskCache = 27,
+ AA_DontShowShortcutsInContextMenus = 28,
+ AA_CompressTabletEvents = 29,
+ AA_DisableWindowContextHelpButton = 30, // ### Qt 6: remove me
// Add new attributes before this line
AA_AttributeCount
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index efa8e26938..e64fb221d3 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -114,6 +114,11 @@
\macos menubar \e{may not} pick up a change in this attribute. Changes
in the QAction::iconVisibleInMenu property will always be picked up.
+ \value AA_DontShowShortcutsInContextMenus Actions with the Shortcut property
+ won't be shown in any shortcut menus unless specifically set by the
+ QAction::shortcutVisibleInContextMenu property. This value has
+ been added in Qt 5.10.
+
\value AA_NativeWindows Ensures that widgets have native windows.
\value AA_DontCreateNativeWidgetSiblings Ensures that siblings of native
@@ -244,7 +249,14 @@
(In the future, the compression feature may be implemented across platforms.)
You can test the attribute to see whether compression is enabled.
If your application needs to handle all events with no compression,
- you can unset this attribute. This value has been added in Qt 5.7.
+ you can unset this attribute. Notice that input events from tablet devices
+ will not be compressed. See AA_CompressTabletEvents if you want these to be
+ compressed as well. This value has been added in Qt 5.7.
+
+ \value AA_CompressTabletEvents Enables compression of input events from tablet devices.
+ Notice that AA_CompressHighFrequencyEvents must be true for events compression
+ to be enabled, and that this flag extends the former to tablet events. Its default
+ value is false. This value has been added in Qt 5.10.
\value AA_DontCheckOpenGLContextThreadAffinity When making a context
current using QOpenGLContext, do not check that the
@@ -262,6 +274,12 @@
\e glProgramBinary(). In the unlikely event of this being problematic,
set this attribute to disable all disk-based caching of shaders.
+ \value AA_DisableWindowContextHelpButton Disables the WindowContextHelpButtonHint
+ by default on Qt::Sheet and Qt::Dialog widgets. This hides the \gui ? button
+ on Windows, which only makes sense if you use \l QWhatsThis functionality.
+ This value has been added in Qt 5.10. For Qt 6, WindowContextHelpButtonHint
+ will not be set by default.
+
The following values are deprecated or obsolete:
\value AA_ImmediateWidgetCreation This attribute is no longer fully
@@ -664,8 +682,9 @@
be short, localized names. This is basically equivalent to using the date format
string, "ddd MMM d yyyy". See QDate::toString() for more information.
- \value ISODate \l{ISO 8601} extended format: either \c{YYYY-MM-DD} for dates or
- \c{YYYY-MM-DDTHH:mm:ss}, \c{YYYY-MM-DDTHH:mm:ssTZD} (e.g., 1997-07-16T19:20:30+01:00)
+ \value ISODate \l{ISO 8601} extended format: either \c{yyyy-MM-dd} for dates or
+ \c{yyyy-MM-ddTHH:mm:ss} (e.g. 2017-07-24T15:46:29), or with a time-zone
+ suffix (Z for UTC otherwise an offset as [+|-]HH:mm) where appropriate
for combined dates and times.
\value ISODateWithMs \l{ISO 8601} extended format, including milliseconds if applicable.
@@ -2940,10 +2959,10 @@
This enum specifies the coordinate system.
- \value DeviceCoordinates Coordinates are relative to the upper-left corner
+ \value DeviceCoordinates Coordinates are relative to the top-left corner
of the object's paint device.
- \value LogicalCoordinates Coordinates are relative to the upper-left corner
+ \value LogicalCoordinates Coordinates are relative to the top-left corner
of the object.
*/
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 80be984bef..535a96aaec 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -45,15 +45,15 @@
QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT bool qIsInf(double d);
-Q_CORE_EXPORT bool qIsNaN(double d);
-Q_CORE_EXPORT bool qIsFinite(double d);
-Q_CORE_EXPORT bool qIsInf(float f);
-Q_CORE_EXPORT bool qIsNaN(float f);
-Q_CORE_EXPORT bool qIsFinite(float f);
-Q_CORE_EXPORT double qSNaN();
-Q_CORE_EXPORT double qQNaN();
-Q_CORE_EXPORT double qInf();
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(double d);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(double d);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(double d);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(float f);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(float f);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(float f);
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qSNaN();
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN();
+Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf();
Q_CORE_EXPORT quint32 qFloatDistance(float a, float b);
Q_CORE_EXPORT quint64 qFloatDistance(double a, double b);
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index 2291675501..37eddfa9b5 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -73,12 +73,12 @@
QT_BEGIN_NAMESPACE
namespace qnumeric_std_wrapper {
// the 'using namespace std' below is cases where the stdlib already put the math.h functions in the std namespace and undefined the macros.
-static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); }
-static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); }
-static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); }
-static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); }
-static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); }
-static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); }
+Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); }
}
QT_END_NAMESPACE
// These macros from math.h conflict with the real functions in the std namespace.
@@ -94,23 +94,23 @@ QT_BEGIN_NAMESPACE
namespace qnumeric_std_wrapper {
#if defined(QT_MATH_H_DEFINES_MACROS)
# undef QT_MATH_H_DEFINES_MACROS
-static inline bool isnan(double d) { return math_h_isnan(d); }
-static inline bool isinf(double d) { return math_h_isinf(d); }
-static inline bool isfinite(double d) { return math_h_isfinite(d); }
-static inline bool isnan(float f) { return math_h_isnan(f); }
-static inline bool isinf(float f) { return math_h_isinf(f); }
-static inline bool isfinite(float f) { return math_h_isfinite(f); }
+Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return math_h_isnan(d); }
+Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return math_h_isinf(d); }
+Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return math_h_isfinite(d); }
+Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return math_h_isnan(f); }
+Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return math_h_isinf(f); }
+Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return math_h_isfinite(f); }
#else
-static inline bool isnan(double d) { return std::isnan(d); }
-static inline bool isinf(double d) { return std::isinf(d); }
-static inline bool isfinite(double d) { return std::isfinite(d); }
-static inline bool isnan(float f) { return std::isnan(f); }
-static inline bool isinf(float f) { return std::isinf(f); }
-static inline bool isfinite(float f) { return std::isfinite(f); }
+Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return std::isnan(d); }
+Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return std::isinf(d); }
+Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return std::isfinite(d); }
+Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return std::isnan(f); }
+Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return std::isinf(f); }
+Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return std::isfinite(f); }
#endif
}
-Q_DECL_CONSTEXPR static inline double qt_inf() Q_DECL_NOEXCEPT
+Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_inf() Q_DECL_NOEXCEPT
{
Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_infinity,
"platform has no definition for infinity for type double");
@@ -118,7 +118,7 @@ Q_DECL_CONSTEXPR static inline double qt_inf() Q_DECL_NOEXCEPT
}
// Signaling NaN
-Q_DECL_CONSTEXPR static inline double qt_snan() Q_DECL_NOEXCEPT
+Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_snan() Q_DECL_NOEXCEPT
{
Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_signaling_NaN,
"platform has no definition for signaling NaN for type double");
@@ -126,39 +126,39 @@ Q_DECL_CONSTEXPR static inline double qt_snan() Q_DECL_NOEXCEPT
}
// Quiet NaN
-Q_DECL_CONSTEXPR static inline double qt_qnan() Q_DECL_NOEXCEPT
+Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_qnan() Q_DECL_NOEXCEPT
{
Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_quiet_NaN,
"platform has no definition for quiet NaN for type double");
return std::numeric_limits<double>::quiet_NaN();
}
-static inline bool qt_is_inf(double d)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(double d)
{
return qnumeric_std_wrapper::isinf(d);
}
-static inline bool qt_is_nan(double d)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(double d)
{
return qnumeric_std_wrapper::isnan(d);
}
-static inline bool qt_is_finite(double d)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(double d)
{
return qnumeric_std_wrapper::isfinite(d);
}
-static inline bool qt_is_inf(float f)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(float f)
{
return qnumeric_std_wrapper::isinf(f);
}
-static inline bool qt_is_nan(float f)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(float f)
{
return qnumeric_std_wrapper::isnan(f);
}
-static inline bool qt_is_finite(float f)
+Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f)
{
return qnumeric_std_wrapper::isfinite(f);
}
diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp
index 594dc6bc17..682c9bab61 100644
--- a/src/corelib/global/qoperatingsystemversion.cpp
+++ b/src/corelib/global/qoperatingsystemversion.cpp
@@ -43,6 +43,7 @@
#endif
#include <qversionnumber.h>
+#include <qdebug.h>
#if defined(Q_OS_ANDROID)
#include <private/qjni_p.h>
@@ -154,6 +155,8 @@ QT_BEGIN_NAMESPACE
\fn QOperatingSystemVersion QOperatingSystemVersion::current()
Returns a QOperatingSystemVersion indicating the current OS and its version number.
+
+ \sa currentType()
*/
#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN)
QOperatingSystemVersion QOperatingSystemVersion::current()
@@ -300,6 +303,14 @@ int QOperatingSystemVersion::compare(const QOperatingSystemVersion &v1,
*/
/*!
+ \fn QOperatingSystemVersion::OSType QOperatingSystemVersion::currentType()
+
+ Returns the current OS type without constructing a QOperatingSystemVersion instance.
+
+ \sa current()
+*/
+
+/*!
\fn QString QOperatingSystemVersion::name() const
Returns a string representation of the OS type identified by the QOperatingSystemVersion.
@@ -510,4 +521,16 @@ const QOperatingSystemVersion QOperatingSystemVersion::AndroidNougat_MR1 =
const QOperatingSystemVersion QOperatingSystemVersion::AndroidOreo =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 8, 0);
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QOperatingSystemVersion &ov)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "QOperatingSystemVersion(" << ov.name()
+ << ", " << ov.majorVersion() << '.' << ov.minorVersion()
+ << '.' << ov.microVersion() << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h
index 1f3ff8e1ab..5f27deab9e 100644
--- a/src/corelib/global/qoperatingsystemversion.h
+++ b/src/corelib/global/qoperatingsystemversion.h
@@ -92,6 +92,25 @@ public:
static QOperatingSystemVersion current();
+ static Q_DECL_CONSTEXPR OSType currentType()
+ {
+#if defined(Q_OS_WIN)
+ return Windows;
+#elif defined(Q_OS_MACOS)
+ return MacOS;
+#elif defined(Q_OS_IOS)
+ return IOS;
+#elif defined(Q_OS_TVOS)
+ return TvOS;
+#elif defined(Q_OS_WATCHOS)
+ return WatchOS;
+#elif defined(Q_OS_ANDROID)
+ return Android;
+#else
+ return Unknown;
+#endif
+ }
+
Q_DECL_CONSTEXPR int majorVersion() const { return m_major; }
Q_DECL_CONSTEXPR int minorVersion() const { return m_minor; }
Q_DECL_CONSTEXPR int microVersion() const { return m_micro; }
@@ -128,6 +147,11 @@ private:
};
Q_DECLARE_TYPEINFO(QOperatingSystemVersion, QT_VERSION < QT_VERSION_CHECK(6, 0, 0) ? Q_RELOCATABLE_TYPE : Q_PRIMITIVE_TYPE);
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QOperatingSystemVersion &ov);
+#endif
+
QT_END_NAMESPACE
#endif // QOPERATINGSYSTEMVERSION_H
diff --git a/src/corelib/global/qoperatingsystemversion_p.h b/src/corelib/global/qoperatingsystemversion_p.h
index 78d0daf0c6..6922f4ad54 100644
--- a/src/corelib/global/qoperatingsystemversion_p.h
+++ b/src/corelib/global/qoperatingsystemversion_p.h
@@ -63,25 +63,6 @@ QT_BEGIN_NAMESPACE
OSVERSIONINFOEX qWindowsVersionInfo();
#endif
-static inline QOperatingSystemVersion::OSType currentType()
-{
-#if defined(Q_OS_WIN)
- return QOperatingSystemVersion::Windows;
-#elif defined(Q_OS_MACOS)
- return QOperatingSystemVersion::MacOS;
-#elif defined(Q_OS_IOS)
- return QOperatingSystemVersion::IOS;
-#elif defined(Q_OS_TVOS)
- return QOperatingSystemVersion::TvOS;
-#elif defined(Q_OS_WATCHOS)
- return QOperatingSystemVersion::WatchOS;
-#elif defined(Q_OS_ANDROID)
- return QOperatingSystemVersion::Android;
-#else
- return QOperatingSystemVersion::Unknown;
-#endif
-}
-
QT_END_NAMESPACE
#endif // QOPERATINGSYSTEMVERSION_P_H
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
new file mode 100644
index 0000000000..72ac8d332b
--- /dev/null
+++ b/src/corelib/global/qrandom.cpp
@@ -0,0 +1,1394 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// for rand_s
+#define _CRT_RAND_S
+
+#include "qrandom.h"
+#include "qrandom_p.h"
+#include <qobjectdefs.h>
+#include <qmutex.h>
+#include <qthreadstorage.h>
+
+#include <errno.h>
+
+#if QT_CONFIG(getentropy)
+# include <sys/random.h>
+#elif !defined(Q_OS_BSD4) && !defined(Q_OS_WIN)
+# include "qdeadlinetimer.h"
+# include "qhashfunctions.h"
+
+# if QT_CONFIG(getauxval)
+# include <sys/auxv.h>
+# endif
+#endif // !QT_CONFIG(getentropy)
+
+#ifdef Q_OS_UNIX
+# include <fcntl.h>
+# include <private/qcore_unix_p.h>
+#else
+# include <qt_windows.h>
+
+// RtlGenRandom is not exported by its name in advapi32.dll, but as SystemFunction036
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
+// Implementation inspired on https://hg.mozilla.org/mozilla-central/file/722fdbff1efc/security/nss/lib/freebl/win_rand.c#l146
+// Argument why this is safe to use: https://bugzilla.mozilla.org/show_bug.cgi?id=504270
+extern "C" {
+DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
+}
+#endif
+
+#if defined(Q_OS_ANDROID)
+# include <private/qjni_p.h>
+#endif
+
+// This file is too low-level for regular Q_ASSERT (the logging framework may
+// recurse back), so use regular assert()
+#undef NDEBUG
+#undef Q_ASSERT_X
+#undef Q_ASSERT
+#define Q_ASSERT(cond) assert(cond)
+#define Q_ASSERT_X(cond, x, msg) assert(cond && msg)
+#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
+# define NDEBUG 1
+#endif
+#include <assert.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+static qssize_t qt_random_cpu(void *buffer, qssize_t count) Q_DECL_NOTHROW;
+
+# ifdef Q_PROCESSOR_X86_64
+# define _rdrandXX_step _rdrand64_step
+# else
+# define _rdrandXX_step _rdrand32_step
+# endif
+
+static QT_FUNCTION_TARGET(RDRND) qssize_t qt_random_cpu(void *buffer, qssize_t count) Q_DECL_NOTHROW
+{
+ unsigned *ptr = reinterpret_cast<unsigned *>(buffer);
+ unsigned *end = ptr + count;
+
+ while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) {
+ if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr)) == 0)
+ goto out;
+ ptr += sizeof(qregisteruint)/sizeof(*ptr);
+ }
+
+ if (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) {
+ if (_rdrand32_step(ptr))
+ goto out;
+ ++ptr;
+ }
+
+out:
+ return ptr - reinterpret_cast<unsigned *>(buffer);
+}
+#else
+static qssize_t qt_random_cpu(void *, qssize_t)
+{
+ return 0;
+}
+#endif
+
+enum {
+ // may be "overridden" by a member enum
+ FillBufferNoexcept = true
+};
+
+struct QRandomGenerator::SystemGenerator
+{
+#if QT_CONFIG(getentropy)
+ static qssize_t fillBuffer(void *buffer, qssize_t count) Q_DECL_NOTHROW
+ {
+ // getentropy can read at most 256 bytes, so break the reading
+ qssize_t read = 0;
+ while (count - read > 256) {
+ // getentropy can't fail under normal circumstances
+ int ret = getentropy(reinterpret_cast<uchar *>(buffer) + read, 256);
+ Q_ASSERT(ret == 0);
+ Q_UNUSED(ret);
+ read += 256;
+ }
+
+ int ret = getentropy(reinterpret_cast<uchar *>(buffer) + read, count - read);
+ Q_ASSERT(ret == 0);
+ Q_UNUSED(ret);
+ return count;
+ }
+
+#elif defined(Q_OS_UNIX)
+ enum { FillBufferNoexcept = false };
+
+ QBasicAtomicInt fdp1; // "file descriptor plus 1"
+ int openDevice()
+ {
+ int fd = fdp1.loadAcquire() - 1;
+ if (fd != -1)
+ return fd;
+
+ fd = qt_safe_open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = qt_safe_open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd == -1) {
+ // failed on both, set to -2 so we won't try again
+ fd = -2;
+ }
+
+ int opened_fdp1;
+ if (fdp1.testAndSetOrdered(0, fd + 1, opened_fdp1))
+ return fd;
+
+ // failed, another thread has opened the file descriptor
+ if (fd >= 0)
+ qt_safe_close(fd);
+ return opened_fdp1 - 1;
+ }
+
+#ifdef Q_CC_GNU
+ // If it's not GCC or GCC-like, then we'll leak the file descriptor
+ __attribute__((destructor))
+#endif
+ static void closeDevice()
+ {
+ int fd = self().fdp1.load() - 1;
+ if (fd >= 0)
+ qt_safe_close(fd);
+ }
+
+ Q_DECL_CONSTEXPR SystemGenerator() : fdp1 Q_BASIC_ATOMIC_INITIALIZER(0) {}
+
+ qssize_t fillBuffer(void *buffer, qssize_t count)
+ {
+ int fd = openDevice();
+ if (Q_UNLIKELY(fd < 0))
+ return 0;
+
+ qint64 n = qt_safe_read(fd, buffer, count);
+ return qMax<qssize_t>(n, 0); // ignore any errors
+ }
+
+#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+ qssize_t fillBuffer(void *buffer, qssize_t count) Q_DECL_NOTHROW
+ {
+ auto RtlGenRandom = SystemFunction036;
+ return RtlGenRandom(buffer, ULONG(count)) ? count: 0;
+ }
+#elif defined(Q_OS_WINRT)
+ qssize_t fillBuffer(void *, qssize_t) Q_DECL_NOTHROW
+ {
+ // always use the fallback
+ return 0;
+ }
+#endif // Q_OS_WINRT
+
+ static SystemGenerator &self();
+ void generate(quint32 *begin, quint32 *end) Q_DECL_NOEXCEPT_EXPR(FillBufferNoexcept);
+
+ // For std::mersenne_twister_engine implementations that use something
+ // other than quint32 (unsigned int) to fill their buffers.
+ template <typename T> void generate(T *begin, T *end)
+ {
+ Q_STATIC_ASSERT(sizeof(T) >= sizeof(quint32));
+ if (sizeof(T) == sizeof(quint32)) {
+ // Microsoft Visual Studio uses unsigned long, but that's still 32-bit
+ generate(reinterpret_cast<quint32 *>(begin), reinterpret_cast<quint32 *>(end));
+ } else {
+ // Slow path. Fix your C++ library.
+ std::generate(begin, end, [this]() {
+ quint32 datum;
+ generate(&datum, &datum + 1);
+ return datum;
+ });
+ }
+ }
+};
+
+#if defined(Q_OS_WIN)
+static void fallback_update_seed(unsigned) {}
+static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
+{
+ // on Windows, rand_s is a high-quality random number generator
+ // and it requires no seeding
+ std::generate(ptr, ptr + left, []() {
+ unsigned value;
+ rand_s(&value);
+ return value;
+ });
+}
+#elif QT_CONFIG(getentropy)
+static void fallback_update_seed(unsigned) {}
+static void fallback_fill(quint32 *, qssize_t) Q_DECL_NOTHROW
+{
+ // no fallback necessary, getentropy cannot fail under normal circumstances
+ Q_UNREACHABLE();
+}
+#elif defined(Q_OS_BSD4)
+static void fallback_update_seed(unsigned) {}
+static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
+{
+ // BSDs have arc4random(4) and these work even in chroot(2)
+ arc4random_buf(ptr, left * sizeof(*ptr));
+}
+#else
+static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
+static void fallback_update_seed(unsigned value)
+{
+ // Update the seed to be used for the fallback mechansim, if we need to.
+ // We can't use QtPrivate::QHashCombine here because that is not an atomic
+ // operation. A simple XOR will have to do then.
+ seed.fetchAndXorRelaxed(value);
+}
+
+Q_NEVER_INLINE
+#ifdef Q_CC_GNU
+__attribute__((cold)) // this function is pretty big, so optimize for size
+#endif
+static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
+{
+ quint32 scratch[12]; // see element count below
+ quint32 *end = scratch;
+
+ auto foldPointer = [](quintptr v) {
+ if (sizeof(quintptr) == sizeof(quint32)) {
+ // For 32-bit systems, we simply return the pointer.
+ return quint32(v);
+ } else {
+ // For 64-bit systems, we try to return the variable part of the
+ // pointer. On current x86-64 and AArch64, the top 17 bits are
+ // architecturally required to be the same, but in reality the top
+ // 24 bits on Linux are likely to be the same for all processes.
+ return quint32(v >> (32 - 24));
+ }
+ };
+
+ Q_ASSERT(left);
+
+ *end++ = foldPointer(quintptr(&seed)); // 1: variable in this library/executable's .data
+ *end++ = foldPointer(quintptr(&scratch)); // 2: variable in the stack
+ *end++ = foldPointer(quintptr(&errno)); // 3: veriable either in libc or thread-specific
+ *end++ = foldPointer(quintptr(reinterpret_cast<void*>(strerror))); // 4: function in libc (and unlikely to be a macro)
+
+#ifndef QT_BOOTSTRAPPED
+ quint64 nsecs = QDeadlineTimer::current(Qt::PreciseTimer).deadline();
+ *end++ = quint32(nsecs); // 5
+#endif
+
+ if (quint32 v = seed.load())
+ *end++ = v; // 6
+
+#if QT_CONFIG(getauxval)
+ // works on Linux -- all modern libc have getauxval
+# ifdef AT_RANDOM
+ // ELF's auxv AT_RANDOM has 16 random bytes
+ // (other ELF-based systems don't seem to have AT_RANDOM)
+ ulong auxvSeed = getauxval(AT_RANDOM);
+ if (auxvSeed) {
+ memcpy(end, reinterpret_cast<void *>(auxvSeed), 16);
+ end += 4; // 7 to 10
+ }
+# endif
+
+ // Both AT_BASE and AT_SYSINFO_EHDR have some randomness in them due to the
+ // system's ASLR, even if many bits are the same. They also have randomness
+ // between them.
+# ifdef AT_BASE
+ // present at least on the BSDs too, indicates the address of the loader
+ ulong base = getauxval(AT_BASE);
+ if (base)
+ *end++ = foldPointer(base); // 11
+# endif
+# ifdef AT_SYSINFO_EHDR
+ // seems to be Linux-only, indicates the global page of the sysinfo
+ ulong sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
+ if (sysinfo_ehdr)
+ *end++ = foldPointer(sysinfo_ehdr); // 12
+# endif
+#endif
+
+ Q_ASSERT(end <= std::end(scratch));
+
+ // this is highly inefficient, we should save the generator across calls...
+ std::seed_seq sseq(scratch, end);
+ std::mt19937 generator(sseq);
+ std::generate(ptr, ptr + left, generator);
+
+ fallback_update_seed(*ptr);
+}
+#endif
+
+Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin, quint32 *end)
+ Q_DECL_NOEXCEPT_EXPR(FillBufferNoexcept)
+{
+ quint32 *buffer = begin;
+ qssize_t count = end - begin;
+
+ if (Q_UNLIKELY(uint(qt_randomdevice_control) & SetRandomData)) {
+ uint value = uint(qt_randomdevice_control) & RandomDataMask;
+ std::fill_n(buffer, count, value);
+ return;
+ }
+
+ qssize_t filled = 0;
+ if (qt_has_hwrng() && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
+ filled += qt_random_cpu(buffer, count);
+
+ if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) {
+ qssize_t bytesFilled =
+ fillBuffer(buffer + filled, (count - filled) * qssize_t(sizeof(*buffer)));
+ filled += bytesFilled / qssize_t(sizeof(*buffer));
+ }
+ if (filled)
+ fallback_update_seed(*buffer);
+
+ if (Q_UNLIKELY(filled != count)) {
+ // failed to fill the entire buffer, try the faillback mechanism
+ fallback_fill(buffer + filled, count - filled);
+ }
+}
+
+struct QRandomGenerator::SystemAndGlobalGenerators
+{
+ // Construction notes:
+ // 1) The global PRNG state is in a different cacheline compared to the
+ // mutex that protects it. This avoids any false cacheline sharing of
+ // the state in case another thread tries to lock the mutex. It's not
+ // a common scenario, but since sizeof(QRandomGenerator) >= 2560, the
+ // overhead is actually acceptable.
+ // 2) We use both Q_DECL_ALIGN and std::aligned_storage<..., 64> because
+ // some implementations of std::aligned_storage can't align to more
+ // than a primitive type's alignment.
+ // 3) We don't store the entire system QRandomGenerator, only the space
+ // used by the QRandomGenerator::type member. This is fine because we
+ // (ab)use the common initial sequence exclusion to aliasing rules.
+ QBasicMutex globalPRNGMutex;
+ struct ShortenedSystem { uint type; } system_;
+ SystemGenerator sys;
+ Q_DECL_ALIGN(64) std::aligned_storage<sizeof(QRandomGenerator64), 64>::type global_;
+
+#ifdef Q_COMPILER_CONSTEXPR
+ constexpr SystemAndGlobalGenerators()
+ : globalPRNGMutex{}, system_{0}, sys{}, global_{}
+ {}
+#endif
+
+ void confirmLiteral()
+ {
+#if defined(Q_COMPILER_CONSTEXPR) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
+ // Currently fails to compile with MSVC 2017, saying QBasicMutex is not
+ // a literal type. Disassembly with MSVC 2013 and 2015 shows it is
+ // actually a literal; MSVC 2017 has a bug relating to this, so we're
+ // withhold judgement for now. Integrity's compiler is unable to
+ // guarantee g's alignment for some reason.
+
+ constexpr SystemAndGlobalGenerators g = {};
+ Q_UNUSED(g);
+ Q_STATIC_ASSERT(std::is_literal_type<SystemAndGlobalGenerators>::value);
+#endif
+ }
+
+ static SystemAndGlobalGenerators *self()
+ {
+ static SystemAndGlobalGenerators g;
+ Q_STATIC_ASSERT(sizeof(g) > sizeof(QRandomGenerator64));
+ return &g;
+ }
+
+ static QRandomGenerator64 *system()
+ {
+ // Though we never call the constructor, the system QRandomGenerator is
+ // properly initialized by the zero initialization performed in self().
+ // Though QRandomGenerator is has non-vacuous initialization, we
+ // consider it initialized because of the common initial sequence.
+ return reinterpret_cast<QRandomGenerator64 *>(&self()->system_);
+ }
+
+ static QRandomGenerator64 *globalNoInit()
+ {
+ // This function returns the pointer to the global QRandomGenerator,
+ // but does not initialize it. Only call it directly if you meant to do
+ // a pointer comparison.
+ return reinterpret_cast<QRandomGenerator64 *>(&self()->global_);
+ }
+
+ static void securelySeed(QRandomGenerator *rng)
+ {
+ // force reconstruction, just to be pedantic
+ new (rng) QRandomGenerator{System{}};
+
+ rng->type = MersenneTwister;
+ new (&rng->storage.engine()) RandomEngine(self()->sys);
+ }
+
+ struct PRNGLocker {
+ const bool locked;
+ PRNGLocker(const QRandomGenerator *that)
+ : locked(that == globalNoInit())
+ {
+ if (locked)
+ self()->globalPRNGMutex.lock();
+ }
+ ~PRNGLocker()
+ {
+ if (locked)
+ self()->globalPRNGMutex.unlock();
+ }
+ };
+};
+
+inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::self()
+{
+ return SystemAndGlobalGenerators::self()->sys;
+}
+
+/*!
+ \class QRandomGenerator
+ \inmodule QtCore
+ \reentrant
+ \since 5.10
+
+ \brief The QRandomGenerator class allows one to obtain random values from a
+ high-quality Random Number Generator.
+
+ QRandomGenerator may be used to generate random values from a high-quality
+ random number generator. Like the C++ random engines, QRandomGenerator can
+ be seeded with user-provided values through the constructor.
+ When seeded, the sequence of numbers generated by this
+ class is deterministic. That is to say, given the same seed data,
+ QRandomGenerator will generate the same sequence of numbers. But given
+ different seeds, the results should be considerably different.
+
+ QRandomGenerator::securelySeeded() can be used to create a QRandomGenerator
+ that is securely seeded with QRandomGenerator::system(), meaning that the
+ sequence of numbers it generates cannot be easily predicted. Additionally,
+ QRandomGenerator::global() returns a global instance of QRandomGenerator
+ that Qt will ensure to be securely seeded. This object is thread-safe, may
+ be shared for most uses, and is always seeded from
+ QRandomGenerator::system()
+
+ QRandomGenerator::system() may be used to access the system's
+ cryptographically-safe random generator. On Unix systems, it's equivalent
+ to reading from \c {/dev/urandom} or the \c {getrandom()} or \c
+ {getentropy()} system calls.
+
+ The class can generate 32-bit or 64-bit quantities, or fill an array of
+ those. The most common way of generating new values is to call the generate(),
+ generate64() or fillRange() functions. One would use it as:
+
+ \code
+ quint32 value = QRandomGenerator::global()->generate();
+ \endcode
+
+ Additionally, it provides a floating-point function generateDouble() that
+ returns a number in the range [0, 1) (that is, inclusive of zero and
+ exclusive of 1). There's also a set of convenience functions that
+ facilitate obtaining a random number in a bounded, integral range.
+
+ \section1 Seeding and determinism
+
+ QRandomGenerator may be seeded with specific seed data. When that is done,
+ the numbers generated by the object will always be the same, as in the
+ following example:
+
+ \code
+ QRandomGenerator prng1(1234), prng2(1234);
+ Q_ASSERT(prng1.generate32() == prng2.generate32());
+ Q_ASSERT(prng1.generate64() == prng2.generate64());
+ \endcode
+
+ The seed data takes the form of one or more 32-bit words. The ideal seed
+ size is approximately equal to the size of the QRandomGenerator class
+ itself. Due to mixing of the seed data, QRandomGenerator cannot guarantee
+ that distinct seeds will produce different sequences.
+
+ QRandomGenerator::global(), like all generators created by
+ QRandomGenerator::securelySeeded(), is always seeded from
+ QRandomGenerator::system(), so it's not possible to make it produce
+ identical sequences.
+
+ \section1 Bulk data
+
+ When operating in deterministic mode, QRandomGenerator may be used for bulk
+ data generation. In fact, applications that do not need
+ cryptographically-secure or true random data are advised to use a regular
+ QRandomGenerator instead of QRandomGenerator::system() for their random
+ data needs.
+
+ For ease of use, QRandomGenerator provides a global object that can
+ be easily used, as in the following example:
+
+ \code
+ int x = QRandomGenerator::global()->generate32();
+ int y = QRandomGenerator::global()->generate32();
+ int w = QRandomGenerator::global()->bounded(16384);
+ int h = QRandomGenerator::global()->bounded(16384);
+ \endcode
+
+ \section1 System-wide random number generator
+
+ QRandomGenerator::system() may be used to access the system-wide random
+ number generator, which is cryptographically-safe on all systems that Qt
+ runs on. This function will use hardware facilities to generate random
+ numbers where available. On such systems, those facilities are true Random
+ Number Generators. However, if they are true RNGs, those facilities have
+ finite entropy sources and thus may fail to produce any results if their
+ entropy pool is exhausted.
+
+ If that happens, first the operating system then QRandomGenerator will fall
+ back to Pseudo Random Number Generators of decreasing qualities (Qt's
+ fallback generator being the simplest). Whether those generators are still
+ of cryptographic quality is implementation-defined. Therefore,
+ QRandomGenerator::system() should not be used for high-frequency random
+ number generation, lest the entropy pool become empty. As a rule of thumb,
+ this class should not be called upon to generate more than a kilobyte per
+ second of random data (note: this may vary from system to system).
+
+ If an application needs true RNG data in bulk, it should use the operating
+ system facilities (such as \c{/dev/random} on Linux) directly and wait for
+ entropy to become available. If the application requires PRNG engines of
+ cryptographic quality but not of true randomness,
+ QRandomGenerator::system() may still be used (see section below).
+
+ If neither a true RNG nor a cryptographically secure PRNG are required,
+ applications should instead use PRNG engines like QRandomGenerator's
+ deterministic mode and those from the C++ Standard Library.
+ QRandomGenerator::system() can be used to seed those.
+
+ \section2 Fallback quality
+
+ QRandomGenerator::system() uses the operating system facilities to obtain
+ random numbers, which attempt to collect real entropy from the surrounding
+ environment to produce true random numbers. However, it's possible that the
+ entropy pool becomes exhausted, in which case the operating system will
+ fall back to a pseudo-random engine for a time. Under no circumstances will
+ QRandomGenerator::system() block, waiting for more entropy to be collected.
+
+ The following operating systems guarantee that the results from their
+ random-generation API will be of at least cryptographically-safe quality,
+ even if the entropy pool is exhausted: Apple OSes (Darwin), BSDs, Linux,
+ Windows. Barring a system installation problem (such as \c{/dev/urandom}
+ not being readable by the current process), QRandomGenerator::system() will
+ therefore have the same guarantees.
+
+ On other operating systems, QRandomGenerator will fall back to a PRNG of
+ good numeric distribution, but it cannot guarantee proper seeding in all
+ cases. Please consult the OS documentation for more information.
+
+ Applications that require QRandomGenerator not to fall back to
+ non-cryptographic quality generators are advised to check their operating
+ system documentation or restrict their deployment to one of the above.
+
+ \section1 Reentrancy and thread-safety
+
+ QRandomGenerator is reentrant, meaning that multiple threads can operate on
+ this class at the same time, so long as they operate on different objects.
+ If multiple threads need to share one PRNG sequence, external locking by a
+ mutex is required.
+
+ The exceptions are the objects returned by QRandomGenerator::global() and
+ QRandomGenerator::system(): those objects are thread-safe and may be used
+ by any thread without external locking. Note that thread-safety does not
+ extend to copying those objects: they should always be used by reference.
+
+ \section1 Standard C++ Library compatibility
+
+ QRandomGenerator is modeled after the requirements for random number
+ engines in the C++ Standard Library and may be used in almost all contexts
+ that the Standard Library engines can. Exceptions to the requirements are
+ the following:
+
+ \list
+ \li QRandomGenerator does not support seeding from another seed
+ sequence-like class besides std::seed_seq itself;
+ \li QRandomGenerator is not comparable (but is copyable) or
+ streamable to \c{std::ostream} or from \c{std::istream}.
+ \endlist
+
+ QRandomGenerator is also compatible with the uniform distribution classes
+ \c{std::uniform_int_distribution} and \c{std:uniform_real_distribution}, as
+ well as the free function \c{std::generate_canonical}. For example, the
+ following code may be used to generate a floating-point number in the range
+ [1, 2.5):
+
+ \code
+ std::uniform_real_distribution dist(1, 2.5);
+ return dist(*QRandomGenerator::global());
+ \endcode
+
+ \sa QRandomGenerator64, qrand()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(quint32 seedValue)
+
+ Initializes this QRandomGenerator object with the value \a seedValue as
+ the seed. Two objects constructed or reseeded with the same seed value will
+ produce the same number sequence.
+
+ \sa seed(), securelySeeded()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(const quint32 (&seedBuffer)[N])
+ \overload
+
+ Initializes this QRandomGenerator object with the values found in the
+ array \a seedBuffer as the seed. Two objects constructed or reseeded with
+ the same seed value will produce the same number sequence.
+
+ \sa seed(), securelySeeded()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(const quint32 *seedBuffer, qssize_t len)
+ \overload
+
+ Initializes this QRandomGenerator object with \a len values found in
+ the array \a seedBuffer as the seed. Two objects constructed or reseeded
+ with the same seed value will produce the same number sequence.
+
+ This constructor is equivalent to:
+ \code
+ std::seed_seq sseq(seedBuffer, seedBuffer + len);
+ QRandomGenerator generator(sseq);
+ \endcode
+
+ \sa seed(), securelySeeded()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(const quint32 *begin, const quin32 *end)
+ \overload
+
+ Initializes this QRandomGenerator object with the values found in the range
+ from \a begin to \a end as the seed. Two objects constructed or reseeded
+ with the same seed value will produce the same number sequence.
+
+ This constructor is equivalent to:
+ \code
+ std::seed_seq sseq(begin, end);
+ QRandomGenerator generator(sseq);
+ \endcode
+
+ \sa seed(), securelySeeded()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(std::seed_seq &sseq)
+ \overload
+
+ Initializes this QRandomGenerator object with the seed sequence \a
+ sseq as the seed. Two objects constructed or reseeded with the same seed
+ value will produce the same number sequence.
+
+ \sa seed(), securelySeeded()
+ */
+
+/*!
+ \fn QRandomGenerator::QRandomGenerator(const QRandomGenerator &other)
+
+ Creates a copy of the generator state in the \a other object. If \a other is
+ QRandomGenerator::system() or a copy of that, this object will also read
+ from the operating system random-generating facilities. In that case, the
+ sequences generated by the two objects will be different.
+
+ In all other cases, the new QRandomGenerator object will start at the same
+ position in the deterministic sequence as the \a other object was. Both
+ objects will generate the same sequence from this point on.
+
+ For that reason, it is not adviseable to create a copy of
+ QRandomGenerator::global(). If one needs an exclusive deterministic
+ generator, consider instead using securelySeeded() to obtain a new object
+ that shares no relationship with the QRandomGenerator::global().
+ */
+
+/*!
+ \fn bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
+ \relates QRandomGenerator
+
+ Returns true if the two the two engines \a rng1 and \a rng2 are at the same
+ state or if they are both reading from the operating system facilities,
+ false otherwise.
+*/
+
+/*!
+ \fn bool operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
+ \relates QRandomGenerator
+
+ Returns true if the two the two engines \a rng1 and \a rng2 are at
+ different states or if one of them is reading from the operating system
+ facilities and the other is not, false otherwise.
+*/
+
+/*!
+ \typedef QRandomGenerator::result_type
+
+ A typedef to the type that operator()() returns. That is, quint32.
+
+ \sa operator()()
+ */
+
+/*!
+ \fn result_type QRandomGenerator::operator()()
+
+ Generates a 32-bit random quantity and returns it.
+
+ \sa generate(), generate64()
+ */
+
+/*!
+ \fn result_type QRandomGenerator::min()
+
+ Returns the minimum value that QRandomGenerator may ever generate. That is, 0.
+
+ \sa max(), QRandomGenerator64::min()
+ */
+
+/*!
+ \fn result_type QRandomGenerator::max()
+
+ Returns the maximum value that QRandomGenerator may ever generate. That is,
+ \c {std::numeric_limits<result_type>::max()}.
+
+ \sa min(), QRandomGenerator64::max()
+ */
+
+/*!
+ \fn void QRandomGenerator::seed(quint32 seed)
+
+ Reseeds this object using the value \a seed as the seed.
+ */
+
+/*!
+ \fn void QRandomGenerator::seed(std::seed_seq &seed)
+ \overload
+
+ Reseeds this object using the seed sequence \a sseq as the seed.
+ */
+
+/*!
+ \fn void QRandomGenerator::discard(unsigned long long z)
+
+ Discards the next \a z entries from the sequence. This method is equivalent
+ to calling generate() \a z times and discarding the result, as in:
+
+ \code
+ while (z--)
+ generator.generate();
+ \endcode
+*/
+
+/*!
+ \fn void QRandomGenerator::generate(ForwardIterator begin, ForwardIterator end)
+
+ Generates 32-bit quantities and stores them in the range between \a begin
+ and \a end. This function is equivalent to (and is implemented as):
+
+ \code
+ std::generate(begin, end, [this]() { return generate(); });
+ \endcode
+
+ This function complies with the requirements for the function
+ \c{\l{http://en.cppreference.com/w/cpp/numeric/random/seed_seq/generate}{std::seed_seq::generate}},
+ which requires unsigned 32-bit integer values.
+
+ Note that if the [begin, end) range refers to an area that can store more
+ than 32 bits per element, the elements will still be initialized with only
+ 32 bits of data. Any other bits will be zero. To fill the range with 64 bit
+ quantities, one can write:
+
+ \code
+ std::generate(begin, end, []() { return QRandomGenerator::global()->generate64(); });
+ \endcode
+
+ If the range refers to contiguous memory (such as an array or the data from
+ a QVector), the fillRange() function may be used too.
+
+ \sa fillRange()
+ */
+
+/*!
+ \fn void QRandomGenerator::generate(quint32 *begin, quint32 *end)
+ \overload
+ \internal
+
+ Same as the other overload, but more efficiently fills \a begin to \a end.
+ */
+
+/*!
+ \fn void QRandomGenerator::fillRange(UInt *buffer, qssize_t count)
+
+ Generates \a count 32- or 64-bit quantities (depending on the type \c UInt)
+ and stores them in the buffer pointed by \a buffer. This is the most
+ efficient way to obtain more than one quantity at a time, as it reduces the
+ number of calls into the Random Number Generator source.
+
+ For example, to fill a vector of 16 entries with random values, one may
+ write:
+
+ \code
+ QVector<quint32> vector;
+ vector.resize(16);
+ QRandomGenerator::fillRange(vector.data(), vector.size());
+ \endcode
+
+ \sa generate()
+ */
+
+/*!
+ \fn void QRandomGenerator::fillRange(UInt (&buffer)[N})
+
+ Generates \c N 32- or 64-bit quantities (depending on the type \c UInt) and
+ stores them in the \a buffer array. This is the most efficient way to
+ obtain more than one quantity at a time, as it reduces the number of calls
+ into the Random Number Generator source.
+
+ For example, to fill generate two 32-bit quantities, one may write:
+
+ \code
+ quint32 array[2];
+ QRandomGenerator::fillRange(array);
+ \endcode
+
+ It would have also been possible to make one call to generate64() and then split
+ the two halves of the 64-bit value.
+
+ \sa generate()
+ */
+
+/*!
+ \fn qreal QRandomGenerator::generateDouble()
+
+ Generates one random qreal in the canonical range [0, 1) (that is,
+ inclusive of zero and exclusive of 1).
+
+ This function is equivalent to:
+ \code
+ QRandomGenerator64 rd;
+ return std::generate_canonical<qreal, std::numeric_limits<qreal>::digits>(rd);
+ \endcode
+
+ The same may also be obtained by using
+ \c{\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution}{std::uniform_real_distribution}}
+ with parameters 0 and 1.
+
+ \sa generate(), generate64(), bounded()
+ */
+
+/*!
+ \fn qreal QRandomGenerator::bounded(qreal highest)
+
+ Generates one random qreal in the range between 0 (inclusive) and \a
+ highest (exclusive). This function is equivalent to and is implemented as:
+
+ \code
+ return generateDouble() * highest;
+ \endcode
+
+ \sa generateDouble(), bounded()
+ */
+
+/*!
+ \fn quint32 QRandomGenerator::bounded(quint32 highest)
+ \overload
+
+ Generates one random 32-bit quantity in the range between 0 (inclusive) and
+ \a highest (exclusive). The same result may also be obtained by using
+ \c{\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{std::uniform_int_distribution}}
+ with parameters 0 and \c{highest - 1}. That class can also be used to obtain
+ quantities larger than 32 bits.
+
+ For example, to obtain a value between 0 and 255 (inclusive), one would write:
+
+ \code
+ quint32 v = QRandomGenerator::bounded(256);
+ \endcode
+
+ Naturally, the same could also be obtained by masking the result of generate()
+ to only the lower 8 bits. Either solution is as efficient.
+
+ Note that this function cannot be used to obtain values in the full 32-bit
+ range of quint32. Instead, use generate().
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn quint32 QRandomGenerator::bounded(int highest)
+ \overload
+
+ Generates one random 32-bit quantity in the range between 0 (inclusive) and
+ \a highest (exclusive). \a highest must not be negative.
+
+ Note that this function cannot be used to obtain values in the full 32-bit
+ range of int. Instead, use generate() and cast to int.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn quint32 QRandomGenerator::bounded(quint32 lowest, quint32 highest)
+ \overload
+
+ Generates one random 32-bit quantity in the range between \a lowest (inclusive)
+ and \a highest (exclusive). The same result may also be obtained by using
+ \c{\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{std::uniform_int_distribution}}
+ with parameters \a lowest and \c{\a highest - 1}. That class can also be used to
+ obtain quantities larger than 32 bits.
+
+ For example, to obtain a value between 1000 (incl.) and 2000 (excl.), one
+ would write:
+
+ \code
+ quint32 v = QRandomGenerator::bounded(1000, 2000);
+ \endcode
+
+
+ Note that this function cannot be used to obtain values in the full 32-bit
+ range of quint32. Instead, use generate().
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn quint32 QRandomGenerator::bounded(int lowest, int highest)
+ \overload
+
+ Generates one random 32-bit quantity in the range between \a lowest
+ (inclusive) and \a highest (exclusive), both of which may be negative.
+
+ Note that this function cannot be used to obtain values in the full 32-bit
+ range of int. Instead, use generate() and cast to int.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn QRandomGenerator *QRandomGenerator::system()
+ \threadsafe
+
+ Returns a pointer to a shared QRandomGenerator that always uses the
+ facilities provided by the operating system to generate random numbers. The
+ system facilities are considered to be cryptographically safe on at least
+ the following operating systems: Apple OSes (Darwin), BSDs, Linux, Windows.
+ That may also be the case on other operating systems.
+
+ They are also possibly backed by a true hardware random number generator.
+ For that reason, the QRandomGenerator returned by this function should not
+ be used for bulk data generation. Instead, use it to seed QRandomGenerator
+ or a random engine from the <random> header.
+
+ The object returned by this function is thread-safe and may be used in any
+ thread without locks. It may also be copied and the resulting
+ QRandomGenerator will also access the operating system facilities, but they
+ will not generate the same sequence.
+
+ \sa securelySeeded(), global()
+*/
+
+/*!
+ \fn QRandomGenerator *QRandomGenerator::global()
+ \threadsafe
+
+ Returns a pointer to a shared QRandomGenerator that was seeded using
+ securelySeeded(). This function should be used to create random data
+ without the expensive creation of a securely-seeded QRandomGenerator
+ for a specific use or storing the rather large QRandomGenerator object.
+
+ For example, the following creates a random RGB color:
+
+ \code
+ return QColor::fromRgb(QRandomGenerator::global()->generate());
+ \endcode
+
+ Accesses to this object are thread-safe and it may therefore be used in any
+ thread without locks. The object may also be copied and the sequence
+ produced by the copy will be the same as the shared object will produce.
+ Note, however, that if there are other threads accessing the global object,
+ those threads may obtain samples at unpredictable intervals.
+
+ \sa securelySeeded(), system()
+*/
+
+/*!
+ \fn QRandomGenerator QRandomGenerator::securelySeeded()
+
+ Returns a new QRandomGenerator object that was securely seeded with
+ QRandomGenerator::system(). This function will obtain the ideal seed size
+ for the algorithm that QRandomGenerator uses and is therefore the
+ recommended way for creating a new QRandomGenerator object that will be
+ kept for some time.
+
+ Given the amount of data required to securely seed the deterministic
+ engine, this function is somewhat expensive and should not be used for
+ short-term uses of QRandomGenerator (using it to generate fewer than 2600
+ bytes of random data is effectively a waste of resources). If the use
+ doesn't require that much data, consider using QRandomGenerator::global()
+ and not storing a QRandomGenerator object instead.
+
+ \sa global(), system()
+ */
+
+/*!
+ \class QRandomGenerator64
+ \inmodule QtCore
+ \since 5.10
+
+ \brief The QRandomGenerator64 class allows one to obtain 64-bit random values
+ from a high-quality, seed-less Random Number Generator.
+
+ QRandomGenerator64 is a simple adaptor class around QRandomGenerator, making the
+ QRandomGenerator::generate64() function the default for operator()(), instead of the
+ function that returns 32-bit quantities. This class is intended to be used
+ in conjunction with Standard Library algorithms that need 64-bit quantities
+ instead of 32-bit ones.
+
+ In all other aspects, the class is the same. Please refer to
+ QRandomGenerator's documentation for more information.
+
+ \sa QRandomGenerator
+*/
+
+/*!
+ \fn QRandomGenerator64::QRandomGenerator64(const QRandomGenerator &other)
+ \internal
+
+ Creates a copy.
+*/
+
+/*!
+ \typedef QRandomGenerator64::result_type
+
+ A typedef to the type that operator()() returns. That is, quint64.
+
+ \sa operator()()
+ */
+
+/*!
+ \fn quint64 QRandomGenerator64::generate()
+
+ Generates one 64-bit random value and returns it.
+
+ Note about casting to a signed integer: all bits returned by this function
+ are random, so there's a 50% chance that the most significant bit will be
+ set. If you wish to cast the returned value to qint64 and keep it positive,
+ you should mask the sign bit off:
+
+ \code
+ qint64 value = QRandomGenerator64::generate() & std::numeric_limits<qint64>::max();
+ \endcode
+
+ \sa QRandomGenerator, QRandomGenerator::generate64()
+ */
+
+/*!
+ \fn result_type QRandomGenerator64::operator()()
+
+ Generates a 64-bit random quantity and returns it.
+
+ \sa QRandomGenerator::generate(), QRandomGenerator::generate64()
+ */
+
+Q_DECL_CONSTEXPR QRandomGenerator::Storage::Storage()
+ : dummy(0)
+{
+ // nothing
+}
+
+inline QRandomGenerator64::QRandomGenerator64(System s)
+ : QRandomGenerator(s)
+{
+}
+
+QRandomGenerator64 *QRandomGenerator64::system()
+{
+ auto self = SystemAndGlobalGenerators::system();
+ Q_ASSERT(self->type == SystemRNG);
+ return self;
+}
+
+QRandomGenerator64 *QRandomGenerator64::global()
+{
+ auto self = SystemAndGlobalGenerators::globalNoInit();
+
+ // Yes, this is a double-checked lock.
+ // We can return even if the type is not completely initialized yet:
+ // any thread trying to actually use the contents of the random engine
+ // will necessarily wait on the lock.
+ if (Q_LIKELY(self->type != SystemRNG))
+ return self;
+
+ SystemAndGlobalGenerators::PRNGLocker locker(self);
+ if (self->type == SystemRNG)
+ SystemAndGlobalGenerators::securelySeed(self);
+
+ return self;
+}
+
+QRandomGenerator64 QRandomGenerator64::securelySeeded()
+{
+ QRandomGenerator64 result(System{});
+ SystemAndGlobalGenerators::securelySeed(&result);
+ return result;
+}
+
+/// \internal
+inline QRandomGenerator::QRandomGenerator(System)
+ : type(SystemRNG)
+{
+ // don't touch storage
+}
+
+QRandomGenerator::QRandomGenerator(const QRandomGenerator &other)
+ : type(other.type)
+{
+ Q_ASSERT(this != system());
+ Q_ASSERT(this != SystemAndGlobalGenerators::globalNoInit());
+
+ if (type != SystemRNG) {
+ SystemAndGlobalGenerators::PRNGLocker lock(&other);
+ storage.engine() = other.storage.engine();
+ }
+}
+
+QRandomGenerator &QRandomGenerator::operator=(const QRandomGenerator &other)
+{
+ if (Q_UNLIKELY(this == system()) || Q_UNLIKELY(this == SystemAndGlobalGenerators::globalNoInit()))
+ qFatal("Attempted to overwrite a QRandomGenerator to system() or global().");
+
+ if ((type = other.type) != SystemRNG) {
+ SystemAndGlobalGenerators::PRNGLocker lock(&other);
+ storage.engine() = other.storage.engine();
+ }
+ return *this;
+}
+
+QRandomGenerator::QRandomGenerator(std::seed_seq &sseq) Q_DECL_NOTHROW
+ : type(MersenneTwister)
+{
+ Q_ASSERT(this != system());
+ Q_ASSERT(this != SystemAndGlobalGenerators::globalNoInit());
+
+ new (&storage.engine()) RandomEngine(sseq);
+}
+
+QRandomGenerator::QRandomGenerator(const quint32 *begin, const quint32 *end)
+ : type(MersenneTwister)
+{
+ Q_ASSERT(this != system());
+ Q_ASSERT(this != SystemAndGlobalGenerators::globalNoInit());
+
+ std::seed_seq s(begin, end);
+ new (&storage.engine()) RandomEngine(s);
+}
+
+void QRandomGenerator::discard(unsigned long long z)
+{
+ if (Q_UNLIKELY(type == SystemRNG))
+ return;
+
+ SystemAndGlobalGenerators::PRNGLocker lock(this);
+ storage.engine().discard(z);
+}
+
+bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
+{
+ if (rng1.type != rng2.type)
+ return false;
+ if (rng1.type == SystemRNG)
+ return true;
+
+ // Lock global() if either is it (otherwise this locking is a no-op)
+ using PRNGLocker = QRandomGenerator::SystemAndGlobalGenerators::PRNGLocker;
+ PRNGLocker locker(&rng1 == QRandomGenerator::global() ? &rng1 : &rng2);
+ return rng1.storage.engine() == rng2.storage.engine();
+}
+
+/*!
+ \internal
+
+ Fills the range pointed by \a buffer and \a bufferEnd with 32-bit random
+ values. The buffer must be correctly aligned.
+ */
+void QRandomGenerator::_fillRange(void *buffer, void *bufferEnd)
+{
+ // Verify that the pointers are properly aligned for 32-bit
+ Q_ASSERT(quintptr(buffer) % sizeof(quint32) == 0);
+ Q_ASSERT(quintptr(bufferEnd) % sizeof(quint32) == 0);
+ quint32 *begin = static_cast<quint32 *>(buffer);
+ quint32 *end = static_cast<quint32 *>(bufferEnd);
+
+ if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control) & (UseSystemRNG|SetRandomData)))
+ return SystemGenerator::self().generate(begin, end);
+
+ SystemAndGlobalGenerators::PRNGLocker lock(this);
+ std::generate(begin, end, [this]() { return storage.engine()(); });
+}
+
+#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
+typedef QThreadStorage<QJNIObjectPrivate> AndroidRandomStorage;
+Q_GLOBAL_STATIC(AndroidRandomStorage, randomTLS)
+
+#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
+using SeedStorageType = QtPrivate::FunctionPointer<decltype(&srand)>::Arguments::Car;
+
+typedef QThreadStorage<SeedStorageType *> SeedStorage;
+Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value
+
+#endif
+
+/*!
+ \relates <QtGlobal>
+ \since 4.2
+
+ Thread-safe version of the standard C++ \c srand() function.
+
+ Sets the argument \a seed to be used to generate a new random number sequence of
+ pseudo random integers to be returned by qrand().
+
+ The sequence of random numbers generated is deterministic per thread. For example,
+ if two threads call qsrand(1) and subsequently call qrand(), the threads will get
+ the same random number sequence.
+
+ \sa qrand(), QRandomGenerator
+*/
+void qsrand(uint seed)
+{
+#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
+ if (randomTLS->hasLocalData()) {
+ randomTLS->localData().callMethod<void>("setSeed", "(J)V", jlong(seed));
+ return;
+ }
+
+ QJNIObjectPrivate random("java/util/Random",
+ "(J)V",
+ jlong(seed));
+ if (!random.isValid()) {
+ srand(seed);
+ return;
+ }
+
+ randomTLS->setLocalData(random);
+#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
+ SeedStorage *seedStorage = randTLS();
+ if (seedStorage) {
+ SeedStorageType *pseed = seedStorage->localData();
+ if (!pseed)
+ seedStorage->setLocalData(pseed = new SeedStorageType);
+ *pseed = seed;
+ } else {
+ //global static seed storage should always exist,
+ //except after being deleted by QGlobalStaticDeleter.
+ //But since it still can be called from destructor of another
+ //global static object, fallback to srand(seed)
+ srand(seed);
+ }
+#else
+ // On Windows srand() and rand() already use Thread-Local-Storage
+ // to store the seed between calls
+ // this is also valid for QT_NO_THREAD
+ srand(seed);
+#endif
+}
+
+/*!
+ \relates <QtGlobal>
+ \since 4.2
+
+ Thread-safe version of the standard C++ \c rand() function.
+
+ Returns a value between 0 and \c RAND_MAX (defined in \c <cstdlib> and
+ \c <stdlib.h>), the next number in the current sequence of pseudo-random
+ integers.
+
+ Use \c qsrand() to initialize the pseudo-random number generator with a
+ seed value. Seeding must be performed at least once on each thread. If that
+ step is skipped, then the sequence will be pre-seeded with a constant
+ value.
+
+ \sa qsrand(), QRandomGenerator
+*/
+int qrand()
+{
+#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
+ AndroidRandomStorage *randomStorage = randomTLS();
+ if (!randomStorage)
+ return rand();
+
+ if (randomStorage->hasLocalData()) {
+ return randomStorage->localData().callMethod<jint>("nextInt",
+ "(I)I",
+ RAND_MAX);
+ }
+
+ QJNIObjectPrivate random("java/util/Random",
+ "(J)V",
+ jlong(1));
+
+ if (!random.isValid())
+ return rand();
+
+ randomStorage->setLocalData(random);
+ return random.callMethod<jint>("nextInt", "(I)I", RAND_MAX);
+#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
+ SeedStorage *seedStorage = randTLS();
+ if (seedStorage) {
+ SeedStorageType *pseed = seedStorage->localData();
+ if (!pseed) {
+ seedStorage->setLocalData(pseed = new SeedStorageType);
+ *pseed = 1;
+ }
+ return rand_r(pseed);
+ } else {
+ //global static seed storage should always exist,
+ //except after being deleted by QGlobalStaticDeleter.
+ //But since it still can be called from destructor of another
+ //global static object, fallback to rand()
+ return rand();
+ }
+#else
+ // On Windows srand() and rand() already use Thread-Local-Storage
+ // to store the seed between calls
+ // this is also valid for QT_NO_THREAD
+ return rand();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/global/qrandom.h b/src/corelib/global/qrandom.h
new file mode 100644
index 0000000000..bde64646a4
--- /dev/null
+++ b/src/corelib/global/qrandom.h
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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
+** 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.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRANDOM_H
+#define QRANDOM_H
+
+#include <QtCore/qglobal.h>
+#include <algorithm> // for std::generate
+#include <random> // for std::mt19937
+
+QT_BEGIN_NAMESPACE
+
+class QRandomGenerator
+{
+ // restrict the template parameters to unsigned integers 32 bits wide or larger
+ template <typename UInt> using IfValidUInt =
+ typename std::enable_if<std::is_unsigned<UInt>::value && sizeof(UInt) >= sizeof(uint), bool>::type;
+public:
+ QRandomGenerator(quint32 seedValue = 1)
+ : QRandomGenerator(&seedValue, 1)
+ {}
+ template <qssize_t N> QRandomGenerator(const quint32 (&seedBuffer)[N])
+ : QRandomGenerator(seedBuffer, seedBuffer + N)
+ {}
+ QRandomGenerator(const quint32 *seedBuffer, qssize_t len)
+ : QRandomGenerator(seedBuffer, seedBuffer + len)
+ {}
+ Q_CORE_EXPORT QRandomGenerator(std::seed_seq &sseq) Q_DECL_NOTHROW;
+ Q_CORE_EXPORT QRandomGenerator(const quint32 *begin, const quint32 *end);
+
+ // copy constructor & assignment operator (move unnecessary)
+ Q_CORE_EXPORT QRandomGenerator(const QRandomGenerator &other);
+ Q_CORE_EXPORT QRandomGenerator &operator=(const QRandomGenerator &other);
+
+ friend Q_CORE_EXPORT bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2);
+ friend bool operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
+ {
+ return !(rng1 == rng2);
+ }
+
+ quint32 generate()
+ {
+ quint32 ret;
+ fillRange(&ret, 1);
+ return ret;
+ }
+
+ quint64 generate64()
+ {
+ quint32 buf[2];
+ fillRange(buf);
+ return buf[0] | (quint64(buf[1]) << 32);
+ }
+
+ double generateDouble()
+ {
+ // IEEE 754 double precision has:
+ // 1 bit sign
+ // 10 bits exponent
+ // 53 bits mantissa
+ // In order for our result to be normalized in the range [0, 1), we
+ // need exactly 53 bits of random data. Use generate64() to get enough.
+ quint64 x = generate64();
+ quint64 limit = Q_UINT64_C(1) << std::numeric_limits<double>::digits;
+ x >>= std::numeric_limits<quint64>::digits - std::numeric_limits<double>::digits;
+ return double(x) / double(limit);
+ }
+
+ double bounded(double highest)
+ {
+ return generateDouble() * highest;
+ }
+
+ quint32 bounded(quint32 highest)
+ {
+ quint64 value = generate();
+ value *= highest;
+ value /= (max)() + quint64(1);
+ return quint32(value);
+ }
+
+ int bounded(int highest)
+ {
+ return int(bounded(quint32(highest)));
+ }
+
+ quint32 bounded(quint32 lowest, quint32 highest)
+ {
+ return bounded(highest - lowest) + lowest;
+ }
+
+ int bounded(int lowest, int highest)
+ {
+ return bounded(highest - lowest) + lowest;
+ }
+
+ template <typename UInt, IfValidUInt<UInt> = true>
+ void fillRange(UInt *buffer, qssize_t count)
+ {
+ _fillRange(buffer, buffer + count);
+ }
+
+ template <typename UInt, size_t N, IfValidUInt<UInt> = true>
+ void fillRange(UInt (&buffer)[N])
+ {
+ _fillRange(buffer, buffer + N);
+ }
+
+ // API like std::seed_seq
+ template <typename ForwardIterator>
+ void generate(ForwardIterator begin, ForwardIterator end)
+ {
+ std::generate(begin, end, [this]() { return generate(); });
+ }
+
+ void generate(quint32 *begin, quint32 *end)
+ {
+ _fillRange(begin, end);
+ }
+
+ // API like std:: random engines
+ typedef quint32 result_type;
+ result_type operator()() { return generate(); }
+ void seed(quint32 s = 1) { *this = { s }; }
+ void seed(std::seed_seq &sseq) Q_DECL_NOTHROW { *this = { sseq }; }
+ Q_CORE_EXPORT void discard(unsigned long long z);
+ static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
+ static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
+
+ static inline Q_DECL_CONST_FUNCTION QRandomGenerator *system();
+ static inline Q_DECL_CONST_FUNCTION QRandomGenerator *global();
+ static inline QRandomGenerator securelySeeded();
+
+protected:
+ enum System {};
+ QRandomGenerator(System);
+
+private:
+ Q_CORE_EXPORT void _fillRange(void *buffer, void *bufferEnd);
+
+ friend class QRandomGenerator64;
+ struct SystemGenerator;
+ struct SystemAndGlobalGenerators;
+ typedef std::mt19937 RandomEngine;
+
+ union Storage {
+ uint dummy;
+#ifdef Q_COMPILER_UNRESTRICTED_UNIONS
+ RandomEngine twister;
+ RandomEngine &engine() { return twister; }
+ const RandomEngine &engine() const { return twister; }
+#else
+ std::aligned_storage<sizeof(RandomEngine), Q_ALIGNOF(RandomEngine)>::type buffer;
+ RandomEngine &engine() { return reinterpret_cast<RandomEngine &>(buffer); }
+ const RandomEngine &engine() const { return reinterpret_cast<const RandomEngine &>(buffer); }
+#endif
+
+ Q_STATIC_ASSERT_X(std::is_trivially_destructible<RandomEngine>::value,
+ "std::mersenne_twister not trivially destructible as expected");
+ Q_DECL_CONSTEXPR Storage();
+ };
+ uint type;
+ Storage storage;
+};
+
+class QRandomGenerator64 : public QRandomGenerator
+{
+ QRandomGenerator64(System);
+public:
+ // unshadow generate() overloads, since we'll override.
+ using QRandomGenerator::generate;
+ quint64 generate() { return generate64(); }
+
+ typedef quint64 result_type;
+ result_type operator()() { return generate64(); }
+
+#ifndef Q_QDOC
+ QRandomGenerator64(quint32 seedValue = 1)
+ : QRandomGenerator(seedValue)
+ {}
+ template <qssize_t N> QRandomGenerator64(const quint32 (&seedBuffer)[N])
+ : QRandomGenerator(seedBuffer)
+ {}
+ QRandomGenerator64(const quint32 *seedBuffer, qssize_t len)
+ : QRandomGenerator(seedBuffer, len)
+ {}
+ QRandomGenerator64(std::seed_seq &sseq) Q_DECL_NOTHROW
+ : QRandomGenerator(sseq)
+ {}
+ QRandomGenerator64(const quint32 *begin, const quint32 *end)
+ : QRandomGenerator(begin, end)
+ {}
+ QRandomGenerator64(const QRandomGenerator &other) : QRandomGenerator(other) {}
+
+ void discard(unsigned long long z)
+ {
+ Q_ASSERT_X(z * 2 > z, "QRandomGenerator64::discard",
+ "Overflow. Are you sure you want to skip over 9 quintillion samples?");
+ QRandomGenerator::discard(z * 2);
+ }
+
+ static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
+ static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
+ static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 *system();
+ static Q_DECL_CONST_FUNCTION Q_CORE_EXPORT QRandomGenerator64 *global();
+ static Q_CORE_EXPORT QRandomGenerator64 securelySeeded();
+#endif // Q_QDOC
+};
+
+inline QRandomGenerator *QRandomGenerator::system()
+{
+ return QRandomGenerator64::system();
+}
+
+inline QRandomGenerator *QRandomGenerator::global()
+{
+ return QRandomGenerator64::global();
+}
+
+QRandomGenerator QRandomGenerator::securelySeeded()
+{
+ return QRandomGenerator64::securelySeeded();
+}
+
+QT_END_NAMESPACE
+
+#endif // QRANDOM_H
diff --git a/src/corelib/io/qwinoverlappedionotifier_p.h b/src/corelib/global/qrandom_p.h
index 276a1d861e..917a91098e 100644
--- a/src/corelib/io/qwinoverlappedionotifier_p.h
+++ b/src/corelib/global/qrandom_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QWINOVERLAPPEDIONOTIFIER_P_H
-#define QWINOVERLAPPEDIONOTIFIER_P_H
+#ifndef QRANDOM_P_H
+#define QRANDOM_P_H
//
// W A R N I N G
@@ -51,40 +51,44 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
-#include <qobject.h>
-
-typedef struct _OVERLAPPED OVERLAPPED;
+#include "qglobal_p.h"
+#include <private/qsimd_p.h>
QT_BEGIN_NAMESPACE
-class QWinOverlappedIoNotifierPrivate;
+enum QRandomGeneratorControl {
+ UseSystemRNG = 1,
+ SkipSystemRNG = 2,
+ SkipHWRNG = 4,
+ SetRandomData = 8,
-class Q_CORE_EXPORT QWinOverlappedIoNotifier : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(QWinOverlappedIoNotifier)
- Q_DECLARE_PRIVATE(QWinOverlappedIoNotifier)
- Q_PRIVATE_SLOT(d_func(), void _q_notified())
- friend class QWinIoCompletionPort;
-public:
- QWinOverlappedIoNotifier(QObject *parent = 0);
- ~QWinOverlappedIoNotifier();
+ // 28 bits
+ RandomDataMask = 0xfffffff0
+};
- void setHandle(Qt::HANDLE h);
- Qt::HANDLE handle() const;
+enum RNGType {
+ SystemRNG = 0,
+ MersenneTwister = 1
+};
- void setEnabled(bool enabled);
- OVERLAPPED *waitForAnyNotified(int msecs);
- bool waitForNotified(int msecs, OVERLAPPED *overlapped);
+#if defined(QT_BUILD_INTERNAL) && defined(QT_BUILD_CORE_LIB)
+Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
+#elif defined(QT_BUILD_INTERNAL)
+extern Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control;
+#else
+enum { qt_randomdevice_control = 0 };
+#endif
-Q_SIGNALS:
- void notified(quint32 numberOfBytes, quint32 errorCode, OVERLAPPED *overlapped);
-#if !defined(Q_QDOC)
- void _q_notify();
+inline bool qt_has_hwrng()
+{
+#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
+ return qCpuHasFeature(RDRND);
+#else
+ return false;
#endif
-};
+}
+
QT_END_NAMESPACE
-#endif // QWINOVERLAPPEDIONOTIFIER_P_H
+#endif // QRANDOM_P_H
diff --git a/src/corelib/global/qt_windows.h b/src/corelib/global/qt_windows.h
index bc48104edc..67ba27f072 100644
--- a/src/corelib/global/qt_windows.h
+++ b/src/corelib/global/qt_windows.h
@@ -48,10 +48,10 @@
#if defined(Q_CC_MINGW)
// mingw's windows.h does not set _WIN32_WINNT, resulting breaking compilation
# ifndef WINVER
-# define WINVER 0x600
+# define WINVER 0x601
# endif
# ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x600
+# define _WIN32_WINNT 0x601
# endif
# ifndef NTDDI_VERSION
# define NTDDI_VERSION 0x06000000
diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h
index 7031021e16..4f79c48c51 100644
--- a/src/corelib/global/qtypeinfo.h
+++ b/src/corelib/global/qtypeinfo.h
@@ -311,14 +311,11 @@ Q_DECLARE_TYPEINFO(qint64, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(quint64, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(float, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(double, Q_PRIMITIVE_TYPE);
-#ifndef Q_OS_DARWIN
-Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE);
-#endif
-
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
// ### Qt 6: remove the other branch
// This was required so that QList<T> for these types allocates out of the array storage
+Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE);
# ifdef Q_COMPILER_UNICODE_STRINGS
Q_DECLARE_TYPEINFO(char16_t, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(char32_t, Q_PRIMITIVE_TYPE);
@@ -327,6 +324,11 @@ Q_DECLARE_TYPEINFO(char32_t, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(wchar_t, Q_PRIMITIVE_TYPE);
# endif
#else
+# ifndef Q_OS_DARWIN
+Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE);
+# else
+Q_DECLARE_TYPEINFO(long double, Q_RELOCATABLE_TYPE);
+# endif
# ifdef Q_COMPILER_UNICODE_STRINGS
Q_DECLARE_TYPEINFO(char16_t, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(char32_t, Q_RELOCATABLE_TYPE);
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
index d24c290508..4614fe2a6b 100644
--- a/src/corelib/io/io.pri
+++ b/src/corelib/io/io.pri
@@ -121,18 +121,16 @@ win32 {
!winrt {
HEADERS += \
io/qwindowspipereader_p.h \
- io/qwindowspipewriter_p.h \
- io/qwinoverlappedionotifier_p.h
+ io/qwindowspipewriter_p.h
SOURCES += \
io/qsettings_win.cpp \
io/qstandardpaths_win.cpp \
io/qstorageinfo_win.cpp \
io/qwindowspipereader.cpp \
- io/qwindowspipewriter.cpp \
- io/qwinoverlappedionotifier.cpp
+ io/qwindowspipewriter.cpp
- LIBS += -lmpr
+ LIBS += -lmpr -lnetapi32 -luserenv
} else {
SOURCES += \
io/qstandardpaths_winrt.cpp \
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
index 89727673d3..f2a895bbb8 100644
--- a/src/corelib/io/qabstractfileengine.cpp
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -328,7 +328,8 @@ QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName)
These are used by the fileTime() function.
- \value CreationTime When the file was created.
+ \value BirthTime When the file was born (created).
+ \value MetadataChangeTime When the file's metadata was last changed.
\value ModificationTime When the file was most recently modified.
\value AccessTime When the file was most recently accessed (e.g.
read or written to).
@@ -737,13 +738,31 @@ QString QAbstractFileEngine::owner(FileOwner owner) const
return QString();
}
+
+/*!
+ \since 5.10
+
+ Sets the file \a time to \a newDate, returning true if successful;
+ otherwise returns false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa fileTime()
+*/
+bool QAbstractFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
+{
+ Q_UNUSED(newDate);
+ Q_UNUSED(time);
+ return false;
+}
+
/*!
- If \a time is \c CreationTime, return when the file was created.
- If \a time is \c ModificationTime, return when the file was most
- recently modified. If \a time is \c AccessTime, return when the
- file was most recently accessed (e.g. read or written).
- If the time cannot be determined return QDateTime() (an invalid
- date time).
+ If \a time is \c BirthTime, return when the file was born (created). If \a
+ time is \c MetadataChangeTime, return when the file's metadata was last
+ changed. If \a time is \c ModificationTime, return when the file was most
+ recently modified. If \a time is \c AccessTime, return when the file was
+ most recently accessed (e.g. read or written). If the time cannot be
+ determined return QDateTime() (an invalid date time).
This virtual function must be reimplemented by all subclasses.
@@ -843,6 +862,20 @@ bool QAbstractFileEngine::unmap(uchar *address)
}
/*!
+ \since 5.10
+
+ Duplicates the contents of this file (starting from the current position)
+ to the file specified by the engine \a target.
+
+ Returns \c true on success; otherwise, \c false is returned.
+ */
+bool QAbstractFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ Q_UNUSED(target);
+ return false;
+}
+
+/*!
\since 4.3
\class QAbstractFileEngineIterator
\inmodule QtCore
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h
index 58fa776e49..00c415b521 100644
--- a/src/corelib/io/qabstractfileengine_p.h
+++ b/src/corelib/io/qabstractfileengine_p.h
@@ -113,9 +113,10 @@ public:
OwnerGroup
};
enum FileTime {
- CreationTime,
- ModificationTime,
- AccessTime
+ AccessTime,
+ BirthTime,
+ MetadataChangeTime,
+ ModificationTime
};
virtual ~QAbstractFileEngine();
@@ -145,9 +146,11 @@ public:
virtual QString fileName(FileName file=DefaultName) const;
virtual uint ownerId(FileOwner) const;
virtual QString owner(FileOwner) const;
+ virtual bool setFileTime(const QDateTime &newDate, FileTime time);
virtual QDateTime fileTime(FileTime time) const;
virtual void setFileName(const QString &file);
virtual int handle() const;
+ virtual bool cloneTo(QAbstractFileEngine *target);
bool atEnd() const;
uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
bool unmap(uchar *ptr);
diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp
index 9a42fb4a37..1486d3630c 100644
--- a/src/corelib/io/qdatastream.cpp
+++ b/src/corelib/io/qdatastream.cpp
@@ -562,6 +562,7 @@ void QDataStream::setByteOrder(ByteOrder bo)
\value Qt_5_7 Same as Qt_5_6
\value Qt_5_8 Same as Qt_5_6
\value Qt_5_9 Same as Qt_5_6
+ \value Qt_5_10 Same as Qt_5_6
\omitvalue Qt_DefaultCompiledVersion
\sa setVersion(), version()
@@ -1384,32 +1385,13 @@ int QDataStream::writeRawData(const char *s, int len)
int QDataStream::skipRawData(int len)
{
CHECK_STREAM_PRECOND(-1)
+ if (q_status != Ok && dev->isTransactionStarted())
+ return -1;
- if (dev->isSequential()) {
- char buf[4096];
- int sumRead = 0;
-
- while (len > 0) {
- int blockSize = qMin(len, (int)sizeof(buf));
- int n = readBlock(buf, blockSize);
- if (n == -1)
- return -1;
- if (n == 0)
- return sumRead;
-
- sumRead += n;
- len -= blockSize;
- }
- return sumRead;
- } else {
- qint64 pos = dev->pos();
- qint64 size = dev->size();
- if (pos + len > size)
- len = size - pos;
- if (!dev->seek(pos + len))
- return -1;
- return len;
- }
+ const int skipResult = dev->skip(len);
+ if (skipResult != len)
+ setStatus(ReadPastEnd);
+ return skipResult;
}
QT_END_NAMESPACE
diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h
index 575607e147..0a429d091a 100644
--- a/src/corelib/io/qdatastream.h
+++ b/src/corelib/io/qdatastream.h
@@ -96,10 +96,11 @@ public:
Qt_5_7 = Qt_5_6,
Qt_5_8 = Qt_5_7,
Qt_5_9 = Qt_5_8,
-#if QT_VERSION >= 0x050a00
+ Qt_5_10 = Qt_5_9,
+#if QT_VERSION >= 0x050b00
#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion
#endif
- Qt_DefaultCompiledVersion = Qt_5_9
+ Qt_DefaultCompiledVersion = Qt_5_10
};
enum ByteOrder {
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp
index be33ec2d23..341400fd93 100644
--- a/src/corelib/io/qdebug.cpp
+++ b/src/corelib/io/qdebug.cpp
@@ -648,6 +648,21 @@ QDebug &QDebug::resetFormat()
*/
/*!
+ \since 5.10
+ \fn QDebug &QDebug::operator<<(QStringView s)
+
+ Writes the string view, \a s, to the stream and returns a reference to the
+ stream. Normally, QDebug prints the string inside quotes and transforms
+ non-printable characters to their Unicode values (\\u1234).
+
+ To print non-printable characters without transformation, enable the
+ noquote() functionality. Note that some QDebug backends might not be 8-bit
+ clean.
+
+ See the QString overload for examples.
+*/
+
+/*!
\fn QDebug &QDebug::operator<<(QLatin1String s)
Writes the string, \a s, to the stream and returns a reference to the
diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h
index 186722b69b..ee8ef679a9 100644
--- a/src/corelib/io/qdebug.h
+++ b/src/corelib/io/qdebug.h
@@ -151,8 +151,11 @@ public:
inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); }
inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); }
inline QDebug &operator<<(const char* t) { stream->ts << QString::fromUtf8(t); return maybeSpace(); }
+#if QT_STRINGVIEW_LEVEL < 2
inline QDebug &operator<<(const QString & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
inline QDebug &operator<<(const QStringRef & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
+#endif
+ inline QDebug &operator<<(QStringView s) { putString(s.data(), size_t(s.size())); return maybeSpace(); }
inline QDebug &operator<<(QLatin1String t) { putByteArray(t.latin1(), t.size(), ContainsLatin1); return maybeSpace(); }
inline QDebug &operator<<(const QByteArray & t) { putByteArray(t.constData(), t.size(), ContainsBinary); return maybeSpace(); }
inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); }
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
index 64d34aec59..bac995ff25 100644
--- a/src/corelib/io/qfile.cpp
+++ b/src/corelib/io/qfile.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,12 +43,14 @@
#include "qfile.h"
#include "qfsfileengine_p.h"
#include "qtemporaryfile.h"
+#include "qtemporaryfile_p.h"
#include "qlist.h"
#include "qfileinfo.h"
#include "private/qiodevice_p.h"
#include "private/qfile_p.h"
#include "private/qfilesystemengine_p.h"
#include "private/qsystemerror_p.h"
+#include "private/qtemporaryfile_p.h"
#if defined(QT_BUILD_CORE_LIB)
# include "qcoreapplication.h"
#endif
@@ -420,7 +423,7 @@ QFile::exists() const
Q_D(const QFile);
// 0x1000000 = QAbstractFileEngine::Refresh, forcing an update
return (d->engine()->fileFlags(QAbstractFileEngine::FlagsMask
- | QAbstractFileEngine::FileFlag(0x1000000)) & QAbstractFileEngine::ExistsFlag);
+ | QAbstractFileEngine::Refresh) & QAbstractFileEngine::ExistsFlag);
}
/*!
@@ -553,7 +556,9 @@ bool
QFile::rename(const QString &newName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+
+ // if this is a QTemporaryFile, the virtual fileName() call here may do something
+ if (fileName().isEmpty()) {
qWarning("QFile::rename: Empty or null file name");
return false;
}
@@ -565,57 +570,64 @@ QFile::rename(const QString &newName)
d->setError(QFile::RenameError, tr("Source file does not exist."));
return false;
}
+
// If the file exists and it is a case-changing rename ("foo" -> "Foo"),
// compare Ids to make sure it really is a different file.
// Note: this does not take file engines into account.
+ bool changingCase = false;
QByteArray targetId = QFileSystemEngine::id(QFileSystemEntry(newName));
if (!targetId.isNull()) {
QByteArray fileId = d->fileEngine ?
d->fileEngine->id() :
QFileSystemEngine::id(QFileSystemEntry(d->fileName));
- if (fileId != targetId || d->fileName.compare(newName, Qt::CaseInsensitive)) {
- // ### Race condition. If a file is moved in after this, it /will/ be
- // overwritten. On Unix, the proper solution is to use hardlinks:
- // return ::link(old, new) && ::remove(old);
+ changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0);
+ if (!changingCase) {
d->setError(QFile::RenameError, tr("Destination file exists"));
return false;
}
-#ifndef QT_NO_TEMPORARYFILE
- // This #ifndef disables the workaround it encloses. Therefore, this configuration is not recommended.
+
#ifdef Q_OS_LINUX
// rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive
// FS, such as FAT32. Move the file away and rename in 2 steps to work around.
- QTemporaryFile tempFile(d->fileName + QLatin1String(".XXXXXX"));
- tempFile.setAutoRemove(false);
- if (!tempFile.open(QIODevice::ReadWrite)) {
- d->setError(QFile::RenameError, tempFile.errorString());
- return false;
- }
- tempFile.close();
- if (!d->engine()->rename(tempFile.fileName())) {
- d->setError(QFile::RenameError, tr("Error while renaming."));
- return false;
- }
- if (tempFile.rename(newName)) {
- d->fileEngine->setFileName(newName);
- d->fileName = newName;
- return true;
- }
- d->setError(QFile::RenameError, tempFile.errorString());
- // We need to restore the original file.
- if (!tempFile.rename(d->fileName)) {
- d->setError(QFile::RenameError, errorString() + QLatin1Char('\n')
+ QTemporaryFileName tfn(d->fileName);
+ QFileSystemEntry src(d->fileName);
+ QSystemError error;
+ for (int attempt = 0; attempt < 16; ++attempt) {
+ QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath());
+
+ // rename to temporary name
+ if (!QFileSystemEngine::renameFile(src, tmp, error))
+ continue;
+
+ // rename to final name
+ if (QFileSystemEngine::renameFile(tmp, QFileSystemEntry(newName), error)) {
+ d->fileEngine->setFileName(newName);
+ d->fileName = newName;
+ return true;
+ }
+
+ // We need to restore the original file.
+ QSystemError error2;
+ if (QFileSystemEngine::renameFile(tmp, src, error2))
+ break; // report the original error, below
+
+ // report both errors
+ d->setError(QFile::RenameError,
+ tr("Error while renaming: %1").arg(error.toString())
+ + QLatin1Char('\n')
+ tr("Unable to restore from %1: %2").
- arg(QDir::toNativeSeparators(tempFile.fileName()), tempFile.errorString()));
+ arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString()));
+ return false;
}
+ d->setError(QFile::RenameError,
+ tr("Error while renaming: %1").arg(error.toString()));
return false;
#endif // Q_OS_LINUX
-#endif // QT_NO_TEMPORARYFILE
}
unsetError();
close();
if(error() == QFile::NoError) {
- if (d->engine()->rename(newName)) {
+ if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) {
unsetError();
// engine was able to handle the new name so we just reset it
d->fileEngine->setFileName(newName);
@@ -707,7 +719,7 @@ bool
QFile::link(const QString &linkName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+ if (fileName().isEmpty()) {
qWarning("QFile::link: Empty or null file name");
return false;
}
@@ -753,7 +765,7 @@ bool
QFile::copy(const QString &newName)
{
Q_D(QFile);
- if (d->fileName.isEmpty()) {
+ if (fileName().isEmpty()) {
qWarning("QFile::copy: Empty or null file name");
return false;
}
@@ -794,25 +806,27 @@ QFile::copy(const QString &newName)
close();
d->setError(QFile::CopyError, tr("Cannot open for output"));
} else {
- char block[4096];
- qint64 totalRead = 0;
- while(!atEnd()) {
- qint64 in = read(block, sizeof(block));
- if (in <= 0)
- break;
- totalRead += in;
- if(in != out.write(block, in)) {
- close();
- d->setError(QFile::CopyError, tr("Failure to write block"));
- error = true;
- break;
+ if (!d->engine()->cloneTo(out.d_func()->engine())) {
+ char block[4096];
+ qint64 totalRead = 0;
+ while (!atEnd()) {
+ qint64 in = read(block, sizeof(block));
+ if (in <= 0)
+ break;
+ totalRead += in;
+ if (in != out.write(block, in)) {
+ close();
+ d->setError(QFile::CopyError, tr("Failure to write block"));
+ error = true;
+ break;
+ }
}
- }
- if (totalRead != size()) {
- // Unable to read from the source. The error string is
- // already set from read().
- error = true;
+ if (totalRead != size()) {
+ // Unable to read from the source. The error string is
+ // already set from read().
+ error = true;
+ }
}
if (!error && !out.rename(newName)) {
error = true;
@@ -957,7 +971,9 @@ bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
qWarning("QFile::open: File access not specified");
return false;
}
- if (d->openExternalFile(mode, fh, handleFlags)) {
+
+ // QIODevice provides the buffering, so request unbuffered file engines
+ if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) {
QIODevice::open(mode);
if (!(mode & Append) && !isSequential()) {
qint64 pos = (qint64)QT_FTELL(fh);
@@ -1013,7 +1029,9 @@ bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
qWarning("QFile::open: File access not specified");
return false;
}
- if (d->openExternalFile(mode, fd, handleFlags)) {
+
+ // QIODevice provides the buffering, so request unbuffered file engines
+ if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) {
QIODevice::open(mode);
if (!(mode & Append) && !isSequential()) {
qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR);
diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h
index a2a1b25248..c5819e8076 100644
--- a/src/corelib/io/qfile.h
+++ b/src/corelib/io/qfile.h
@@ -81,6 +81,7 @@ public:
}
static QString decodeName(const QByteArray &localFileName)
{
+ // note: duplicated in qglobal.cpp (qEnvironmentVariable)
return QString::fromUtf8(localFileName).normalized(QString::NormalizationForm_C);
}
#else
diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp
index f50949e6c1..2f99775c65 100644
--- a/src/corelib/io/qfiledevice.cpp
+++ b/src/corelib/io/qfiledevice.cpp
@@ -740,6 +740,76 @@ bool QFileDevice::unmap(uchar *address)
return false;
}
+/*!
+ \enum QFileDevice::FileTime
+ \since 5.10
+
+ This enum is used by the fileTime() and setFileTime() functions.
+
+ \value FileAccessTime When the file was most recently accessed
+ (e.g. read or written to).
+ \value FileBirthTime When the file was created (may not be not
+ supported on UNIX).
+ \value FileMetadataChangeTime When the file's metadata was last changed.
+ \value FileModificationTime When the file was most recently modified.
+
+ \sa setFileTime(), fileTime(), QFileInfo::fileTime()
+*/
+
+static inline QAbstractFileEngine::FileTime FileDeviceTimeToAbstractFileEngineTime(QFileDevice::FileTime time)
+{
+ Q_STATIC_ASSERT(int(QFileDevice::FileAccessTime) == int(QAbstractFileEngine::AccessTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileBirthTime) == int(QAbstractFileEngine::BirthTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileMetadataChangeTime) == int(QAbstractFileEngine::MetadataChangeTime));
+ Q_STATIC_ASSERT(int(QFileDevice::FileModificationTime) == int(QAbstractFileEngine::ModificationTime));
+ return QAbstractFileEngine::FileTime(time);
+}
+
+/*!
+ \since 5.10
+ Returns the file time specified by \a time.
+ If the time cannot be determined return QDateTime() (an invalid
+ date time).
+
+ \sa setFileTime(), FileTime, QDateTime::isValid()
+*/
+QDateTime QFileDevice::fileTime(QFileDevice::FileTime time) const
+{
+ Q_D(const QFileDevice);
+
+ if (d->engine())
+ return d->engine()->fileTime(FileDeviceTimeToAbstractFileEngineTime(time));
+
+ return QDateTime();
+}
+
+/*!
+ \since 5.10
+ Sets the file time specified by \a fileTime to \a newDate, returning true
+ if successful; otherwise returns false.
+
+ \note The file must be open to use this function.
+
+ \sa fileTime(), FileTime
+*/
+bool QFileDevice::setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime)
+{
+ Q_D(QFileDevice);
+
+ if (!d->engine()) {
+ d->setError(QFileDevice::UnspecifiedError, tr("No file engine available"));
+ return false;
+ }
+
+ if (!d->fileEngine->setFileTime(newDate, FileDeviceTimeToAbstractFileEngineTime(fileTime))) {
+ d->setError(d->fileEngine->error(), d->fileEngine->errorString());
+ return false;
+ }
+
+ unsetError();
+ return true;
+}
+
QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
diff --git a/src/corelib/io/qfiledevice.h b/src/corelib/io/qfiledevice.h
index 4cfda1b044..e941781c25 100644
--- a/src/corelib/io/qfiledevice.h
+++ b/src/corelib/io/qfiledevice.h
@@ -45,6 +45,7 @@
QT_BEGIN_NAMESPACE
+class QDateTime;
class QFileDevicePrivate;
class Q_CORE_EXPORT QFileDevice : public QIODevice
@@ -73,6 +74,13 @@ public:
CopyError = 14
};
+ enum FileTime {
+ FileAccessTime,
+ FileBirthTime,
+ FileMetadataChangeTime,
+ FileModificationTime
+ };
+
enum Permission {
ReadOwner = 0x4000, WriteOwner = 0x2000, ExeOwner = 0x1000,
ReadUser = 0x0400, WriteUser = 0x0200, ExeUser = 0x0100,
@@ -119,6 +127,9 @@ public:
uchar *map(qint64 offset, qint64 size, MemoryMapFlags flags = NoOptions);
bool unmap(uchar *address);
+ QDateTime fileTime(QFileDevice::FileTime time) const;
+ bool setFileTime(const QDateTime &newDate, QFileDevice::FileTime fileTime);
+
protected:
QFileDevice();
#ifdef QT_NO_QOBJECT
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 907574b6dd..26078a6c71 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -42,6 +42,7 @@
#include "qglobal.h"
#include "qdir.h"
#include "qfileinfo_p.h"
+#include "qdebug.h"
QT_BEGIN_NAMESPACE
@@ -185,17 +186,25 @@ uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) cons
QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) const
{
Q_ASSERT(fileEngine); // should never be called when using the native FS
- if (fileTimes.size() != 3)
- fileTimes.resize(3);
if (!cache_enabled)
clearFlags();
- uint cf;
- if (request == QAbstractFileEngine::CreationTime)
- cf = CachedCTime;
- else if (request == QAbstractFileEngine::ModificationTime)
- cf = CachedMTime;
- else
+
+ uint cf = 0;
+ switch (request) {
+ case QAbstractFileEngine::AccessTime:
cf = CachedATime;
+ break;
+ case QAbstractFileEngine::BirthTime:
+ cf = CachedBTime;
+ break;
+ case QAbstractFileEngine::MetadataChangeTime:
+ cf = CachedMCTime;
+ break;
+ case QAbstractFileEngine::ModificationTime:
+ cf = CachedMTime;
+ break;
+ }
+
if (!getCachedFlag(cf)) {
fileTimes[request] = fileEngine->fileTime(request);
setCachedFlag(cf);
@@ -262,8 +271,8 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
info objects, just append one to the file name given to the constructors
or setFile().
- The file's dates are returned by created(), lastModified() and
- lastRead(). Information about the file's access permissions is
+ The file's dates are returned by created(), lastModified(), lastRead() and
+ fileTime(). Information about the file's access permissions is
obtained with isReadable(), isWritable() and isExecutable(). The
file's ownership is available from owner(), ownerId(), group() and
groupId(). You can examine a file's permissions and ownership in a
@@ -582,9 +591,6 @@ QString QFileInfo::absolutePath() const
if (d->isDefaultConstructed) {
return QLatin1String("");
- } else if (d->fileEntry.isEmpty()) {
- qWarning("QFileInfo::absolutePath: Constructed with empty filename");
- return QLatin1String("");
}
return d->getFileName(QAbstractFileEngine::AbsolutePathName);
}
@@ -904,14 +910,10 @@ QDir QFileInfo::absoluteDir() const
bool QFileInfo::isReadable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserReadPermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserReadPermission);
- return (d->metaData.permissions() & QFile::ReadUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::ReadUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserReadPermission,
+ [d]() { return (d->metaData.permissions() & QFile::ReadUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::ReadUserPerm); });
}
/*!
@@ -925,14 +927,10 @@ bool QFileInfo::isReadable() const
bool QFileInfo::isWritable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserWritePermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserWritePermission);
- return (d->metaData.permissions() & QFile::WriteUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::WriteUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserWritePermission,
+ [d]() { return (d->metaData.permissions() & QFile::WriteUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::WriteUserPerm); });
}
/*!
@@ -943,14 +941,10 @@ bool QFileInfo::isWritable() const
bool QFileInfo::isExecutable() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserExecutePermission))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserExecutePermission);
- return (d->metaData.permissions() & QFile::ExeUser) != 0;
- }
- return d->getFileFlags(QAbstractFileEngine::ExeUserPerm);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::UserExecutePermission,
+ [d]() { return (d->metaData.permissions() & QFile::ExeUser) != 0; },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::ExeUserPerm); });
}
/*!
@@ -962,14 +956,10 @@ bool QFileInfo::isExecutable() const
bool QFileInfo::isHidden() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::HiddenAttribute))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::HiddenAttribute);
- return d->metaData.isHidden();
- }
- return d->getFileFlags(QAbstractFileEngine::HiddenFlag);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::HiddenAttribute,
+ [d]() { return d->metaData.isHidden(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::HiddenFlag); });
}
/*!
@@ -1005,14 +995,10 @@ bool QFileInfo::isNativePath() const
bool QFileInfo::isFile() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::FileType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::FileType);
- return d->metaData.isFile();
- }
- return d->getFileFlags(QAbstractFileEngine::FileType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::FileType,
+ [d]() { return d->metaData.isFile(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::FileType); });
}
/*!
@@ -1024,14 +1010,10 @@ bool QFileInfo::isFile() const
bool QFileInfo::isDir() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::DirectoryType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::DirectoryType);
- return d->metaData.isDirectory();
- }
- return d->getFileFlags(QAbstractFileEngine::DirectoryType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::DirectoryType,
+ [d]() { return d->metaData.isDirectory(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::DirectoryType); });
}
@@ -1045,14 +1027,10 @@ bool QFileInfo::isDir() const
bool QFileInfo::isBundle() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::BundleType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::BundleType);
- return d->metaData.isBundle();
- }
- return d->getFileFlags(QAbstractFileEngine::BundleType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::BundleType,
+ [d]() { return d->metaData.isBundle(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::BundleType); });
}
/*!
@@ -1079,14 +1057,10 @@ bool QFileInfo::isBundle() const
bool QFileInfo::isSymLink() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::LegacyLinkType))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::LegacyLinkType);
- return d->metaData.isLegacyLink();
- }
- return d->getFileFlags(QAbstractFileEngine::LinkType);
+ return d->checkAttribute<bool>(
+ QFileSystemMetaData::LegacyLinkType,
+ [d]() { return d->metaData.isLegacyLink(); },
+ [d]() { return d->getFileFlags(QAbstractFileEngine::LinkType); });
}
/*!
@@ -1098,7 +1072,7 @@ bool QFileInfo::isRoot() const
{
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
- return true;
+ return false;
if (d->fileEngine == 0) {
if (d->fileEntry.isRoot()) {
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
@@ -1174,14 +1148,10 @@ QString QFileInfo::owner() const
uint QFileInfo::ownerId() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserId))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserId);
- return d->metaData.userId();
- }
- return d->fileEngine->ownerId(QAbstractFileEngine::OwnerUser);
+ return d->checkAttribute(uint(-2),
+ QFileSystemMetaData::UserId,
+ [d]() { return d->metaData.userId(); },
+ [d]() { return d->fileEngine->ownerId(QAbstractFileEngine::OwnerUser); });
}
/*!
@@ -1213,14 +1183,10 @@ QString QFileInfo::group() const
uint QFileInfo::groupId() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::GroupId))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::GroupId);
- return d->metaData.groupId();
- }
- return d->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup);
+ return d->checkAttribute(uint(-2),
+ QFileSystemMetaData::GroupId,
+ [d]() { return d->metaData.groupId(); },
+ [d]() { return d->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup); });
}
/*!
@@ -1242,16 +1208,15 @@ uint QFileInfo::groupId() const
bool QFileInfo::permission(QFile::Permissions permissions) const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return false;
- if (d->fileEngine == 0) {
- // the QFileSystemMetaData::MetaDataFlag and QFile::Permissions overlap, so just static cast.
- QFileSystemMetaData::MetaDataFlag permissionFlags = static_cast<QFileSystemMetaData::MetaDataFlag>((int)permissions);
- if (!d->cache_enabled || !d->metaData.hasFlags(permissionFlags))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, permissionFlags);
- return (d->metaData.permissions() & permissions) == permissions;
- }
- return d->getFileFlags(QAbstractFileEngine::FileFlags((int)permissions)) == (uint)permissions;
+ // the QFileSystemMetaData::MetaDataFlag and QFile::Permissions overlap, so just cast.
+ auto fseFlags = QFileSystemMetaData::MetaDataFlag(int(permissions));
+ auto feFlags = QAbstractFileEngine::FileFlags(int(permissions));
+ return d->checkAttribute<bool>(
+ fseFlags,
+ [=]() { return (d->metaData.permissions() & permissions) == permissions; },
+ [=]() {
+ return d->getFileFlags(feFlags) == uint(permissions);
+ });
}
/*!
@@ -1264,14 +1229,12 @@ bool QFileInfo::permission(QFile::Permissions permissions) const
QFile::Permissions QFileInfo::permissions() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::Permissions))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::Permissions);
- return d->metaData.permissions();
- }
- return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask);
+ return d->checkAttribute<QFile::Permissions>(
+ QFileSystemMetaData::Permissions,
+ [d]() { return d->metaData.permissions(); },
+ [d]() {
+ return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask);
+ });
}
@@ -1284,64 +1247,77 @@ QFile::Permissions QFileInfo::permissions() const
qint64 QFileInfo::size() const
{
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return 0;
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::SizeAttribute))
- QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::SizeAttribute);
- return d->metaData.size();
- }
- if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
- d->setCachedFlag(QFileInfoPrivate::CachedSize);
- d->fileSize = d->fileEngine->size();
- }
- return d->fileSize;
+ return d->checkAttribute<qint64>(
+ QFileSystemMetaData::SizeAttribute,
+ [d]() { return d->metaData.size(); },
+ [d]() {
+ if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
+ d->setCachedFlag(QFileInfoPrivate::CachedSize);
+ d->fileSize = d->fileEngine->size();
+ }
+ return d->fileSize;
+ });
}
+#if QT_DEPRECATED_SINCE(5, 10)
/*!
- Returns the date and local time when the file was created.
+ \deprecated
- On most Unix systems, this function returns the time of the last
- status change. A status change occurs when the file is created,
- but it also occurs whenever the user writes or sets inode
- information (for example, changing the file permissions).
+ Returns the date and time when the file was created, the time its metadata
+ was last changed or the time of last modification, whichever one of the
+ three is available (in that order).
- If neither creation time nor "last status change" time are not
- available, returns the same as lastModified().
+ This function is deprecated. Instead, use the birthTime() function to get
+ the time the file was created, metadataChangeTime() to get the time its
+ metadata was last changed, or lastModified() to get the time it was last modified.
- \sa lastModified(), lastRead()
+ \sa birthTime(), metadataChangeTime(), lastModified(), lastRead()
*/
QDateTime QFileInfo::created() const
{
- Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::CreationTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::CreationTime))
- return QDateTime();
- return d->metaData.creationTime().toLocalTime();
- }
- return d->getFileTime(QAbstractFileEngine::CreationTime).toLocalTime();
+ QDateTime d = fileTime(QFile::FileBirthTime);
+ if (d.isValid())
+ return d;
+ return fileTime(QFile::FileMetadataChangeTime);
+}
+#endif
+
+/*!
+ \since 5.10
+ Returns the date and time when the file was created / born.
+
+ If the file birth time is not available, this function returns an invalid
+ QDateTime.
+
+ \sa lastModified(), lastRead(), metadataChangeTime()
+*/
+QDateTime QFileInfo::birthTime() const
+{
+ return fileTime(QFile::FileBirthTime);
+}
+
+/*!
+ \since 5.10
+ Returns the date and time when the file metadata was changed. A metadata
+ change occurs when the file is created, but it also occurs whenever the
+ user writes or sets inode information (for example, changing the file
+ permissions).
+
+ \sa lastModified(), lastRead()
+*/
+QDateTime QFileInfo::metadataChangeTime() const
+{
+ return fileTime(QFile::FileMetadataChangeTime);
}
/*!
Returns the date and local time when the file was last modified.
- \sa created(), lastRead()
+ \sa birthTime(), lastRead(), metadataChangeTime(), fileTime()
*/
QDateTime QFileInfo::lastModified() const
{
- Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ModificationTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ModificationTime))
- return QDateTime();
- return d->metaData.modificationTime().toLocalTime();
- }
- return d->getFileTime(QAbstractFileEngine::ModificationTime).toLocalTime();
+ return fileTime(QFile::FileModificationTime);
}
/*!
@@ -1350,20 +1326,50 @@ QDateTime QFileInfo::lastModified() const
On platforms where this information is not available, returns the
same as lastModified().
- \sa created(), lastModified()
+ \sa birthTime(), lastModified(), metadataChangeTime(), fileTime()
*/
QDateTime QFileInfo::lastRead() const
{
+ return fileTime(QFile::FileAccessTime);
+}
+
+/*!
+ \since 5.10
+
+ Returns the file time specified by \a time. If the time cannot be
+ determined, an invalid date time is returned.
+
+ \sa QFile::FileTime, QDateTime::isValid()
+*/
+QDateTime QFileInfo::fileTime(QFile::FileTime time) const
+{
+ Q_STATIC_ASSERT(int(QFile::FileAccessTime) == int(QAbstractFileEngine::AccessTime));
+ Q_STATIC_ASSERT(int(QFile::FileBirthTime) == int(QAbstractFileEngine::BirthTime));
+ Q_STATIC_ASSERT(int(QFile::FileMetadataChangeTime) == int(QAbstractFileEngine::MetadataChangeTime));
+ Q_STATIC_ASSERT(int(QFile::FileModificationTime) == int(QAbstractFileEngine::ModificationTime));
+
Q_D(const QFileInfo);
- if (d->isDefaultConstructed)
- return QDateTime();
- if (d->fileEngine == 0) {
- if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::AccessTime))
- if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::AccessTime))
- return QDateTime();
- return d->metaData.accessTime().toLocalTime();
+ auto fetime = QAbstractFileEngine::FileTime(time);
+ QFileSystemMetaData::MetaDataFlags flag;
+ switch (time) {
+ case QFile::FileAccessTime:
+ flag = QFileSystemMetaData::AccessTime;
+ break;
+ case QFile::FileBirthTime:
+ flag = QFileSystemMetaData::BirthTime;
+ break;
+ case QFile::FileMetadataChangeTime:
+ flag = QFileSystemMetaData::MetadataChangeTime;
+ break;
+ case QFile::FileModificationTime:
+ flag = QFileSystemMetaData::ModificationTime;
+ break;
}
- return d->getFileTime(QAbstractFileEngine::AccessTime).toLocalTime();
+
+ return d->checkAttribute<QDateTime>(
+ flag,
+ [=]() { return d->metaData.fileTime(fetime).toLocalTime(); },
+ [=]() { return d->getFileTime(fetime).toLocalTime(); });
}
/*!
@@ -1410,4 +1416,15 @@ void QFileInfo::setCaching(bool enable)
Synonym for QList<QFileInfo>.
*/
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QFileInfo &fi)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg.noquote();
+ dbg << "QFileInfo(" << QDir::toNativeSeparators(fi.filePath()) << ')';
+ return dbg;
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h
index a160e8463e..f295a86015 100644
--- a/src/corelib/io/qfileinfo.h
+++ b/src/corelib/io/qfileinfo.h
@@ -129,9 +129,16 @@ public:
qint64 size() const;
+ // ### Qt6: inline these functions
+#if QT_DEPRECATED_SINCE(5, 10)
+ QT_DEPRECATED_X("Use either birthTime() or metadataChangeTime()")
QDateTime created() const;
+#endif
+ QDateTime birthTime() const;
+ QDateTime metadataChangeTime() const;
QDateTime lastModified() const;
QDateTime lastRead() const;
+ QDateTime fileTime(QFile::FileTime time) const;
bool caching() const;
void setCaching(bool on);
@@ -151,6 +158,10 @@ Q_DECLARE_SHARED(QFileInfo)
typedef QList<QFileInfo> QFileInfoList;
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug, const QFileInfo &);
+#endif
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QFileInfo)
diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h
index 806df179e8..e4b28f4519 100644
--- a/src/corelib/io/qfileinfo_p.h
+++ b/src/corelib/io/qfileinfo_p.h
@@ -56,7 +56,6 @@
#include "qatomic.h"
#include "qshareddata.h"
#include "qfilesystemengine_p.h"
-#include "qvector.h"
#include <QtCore/private/qabstractfileengine_p.h>
#include <QtCore/private/qfilesystementry_p.h>
@@ -67,9 +66,18 @@ QT_BEGIN_NAMESPACE
class QFileInfoPrivate : public QSharedData
{
public:
- enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04,
- CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40,
- CachedSize =0x08, CachedPerms=0x80 };
+ enum {
+ // note: cachedFlags is only 30-bits wide
+ CachedFileFlags = 0x01,
+ CachedLinkTypeFlag = 0x02,
+ CachedBundleTypeFlag = 0x04,
+ CachedSize = 0x08,
+ CachedATime = 0x10,
+ CachedBTime = 0x20,
+ CachedMCTime = 0x40,
+ CachedMTime = 0x80,
+ CachedPerms = 0x100
+ };
inline QFileInfoPrivate()
: QSharedData(), fileEngine(0),
@@ -95,7 +103,7 @@ public:
fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)),
cachedFlags(0),
#ifndef QT_NO_FSFILEENGINE
- isDefaultConstructed(false),
+ isDefaultConstructed(file.isEmpty()),
#else
isDefaultConstructed(!fileEngine),
#endif
@@ -158,24 +166,40 @@ public:
QScopedPointer<QAbstractFileEngine> const fileEngine;
mutable QString fileNames[QAbstractFileEngine::NFileNames];
- mutable QString fileOwners[2];
+ mutable QString fileOwners[2]; // QAbstractFileEngine::FileOwner: OwnerUser and OwnerGroup
+ mutable QDateTime fileTimes[4]; // QAbstractFileEngine::FileTime: BirthTime, MetadataChangeTime, ModificationTime, AccessTime
mutable uint cachedFlags : 30;
bool const isDefaultConstructed : 1; // QFileInfo is a default constructed instance
bool cache_enabled : 1;
mutable uint fileFlags;
mutable qint64 fileSize;
- // ### Qt6: FIXME: This vector is essentially a plain array
- // mutable QDateTime fileTimes[3], but the array is slower
- // to initialize than the QVector as QDateTime has a pimpl.
- // In Qt 6, QDateTime should inline its data members,
- // and this here can be an array again.
- mutable QVector<QDateTime> fileTimes;
inline bool getCachedFlag(uint c) const
{ return cache_enabled ? (cachedFlags & c) : 0; }
inline void setCachedFlag(uint c) const
{ if (cache_enabled) cachedFlags |= c; }
+ template <typename Ret, typename FSLambda, typename EngineLambda>
+ Ret checkAttribute(Ret defaultValue, QFileSystemMetaData::MetaDataFlags fsFlags, const FSLambda &fsLambda,
+ const EngineLambda &engineLambda) const
+ {
+ if (isDefaultConstructed)
+ return defaultValue;
+ if (fileEngine)
+ return engineLambda();
+ if (!cache_enabled || !metaData.hasFlags(fsFlags)) {
+ QFileSystemEngine::fillMetaData(fileEntry, metaData, fsFlags);
+ // ignore errors, fillMetaData will have cleared the flags
+ }
+ return fsLambda();
+ }
+
+ template <typename Ret, typename FSLambda, typename EngineLambda>
+ Ret checkAttribute(QFileSystemMetaData::MetaDataFlags fsFlags, const FSLambda &fsLambda,
+ const EngineLambda &engineLambda) const
+ {
+ return checkAttribute(Ret(), fsFlags, fsLambda, engineLambda);
+ }
};
QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp
index ccbcdb1037..7abdf90bf5 100644
--- a/src/corelib/io/qfilesystemengine.cpp
+++ b/src/corelib/io/qfilesystemengine.cpp
@@ -86,8 +86,6 @@ QString QFileSystemEngine::slowCanonicalized(const QString &path)
fi.setFile(prefix);
if (fi.isSymLink()) {
QString target = fi.symLinkTarget();
- if(QFileInfo(target).isRelative())
- target = fi.absolutePath() + slash + target;
if (separatorPos != -1) {
if (fi.isDir() && !target.endsWith(slash))
target.append(slash);
@@ -204,228 +202,6 @@ QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine(
return engine;
}
-//these unix functions are in this file, because they are shared by symbian port
-//for open C file handles.
-#ifdef Q_OS_UNIX
-//static
-bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
-{
- data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
- data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
-
- QT_STATBUF statBuffer;
- if (QT_FSTAT(fd, &statBuffer) == 0) {
- data.fillFromStatBuf(statBuffer);
- return true;
- }
-
- return false;
-}
-
-#if defined(_DEXTRA_FIRST)
-static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
-{
- statBuf64->st_mode = statBuf32.st_mode;
- statBuf64->st_size = statBuf32.st_size;
-#if _POSIX_VERSION >= 200809L
- statBuf64->st_ctim = statBuf32.st_ctim;
- statBuf64->st_mtim = statBuf32.st_mtim;
- statBuf64->st_atim = statBuf32.st_atim;
-#else
- statBuf64->st_ctime = statBuf32.st_ctime;
- statBuf64->st_mtime = statBuf32.st_mtime;
- statBuf64->st_atime = statBuf32.st_atime;
-#endif
- statBuf64->st_uid = statBuf32.st_uid;
- statBuf64->st_gid = statBuf32.st_gid;
-}
-#endif
-
-#if _POSIX_VERSION >= 200809L
-static qint64 timespecToMSecs(const timespec &spec)
-{
- return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
-}
-#endif
-
-void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
-{
- // Permissions
- if (statBuffer.st_mode & S_IRUSR)
- entryFlags |= QFileSystemMetaData::OwnerReadPermission;
- if (statBuffer.st_mode & S_IWUSR)
- entryFlags |= QFileSystemMetaData::OwnerWritePermission;
- if (statBuffer.st_mode & S_IXUSR)
- entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
-
- if (statBuffer.st_mode & S_IRGRP)
- entryFlags |= QFileSystemMetaData::GroupReadPermission;
- if (statBuffer.st_mode & S_IWGRP)
- entryFlags |= QFileSystemMetaData::GroupWritePermission;
- if (statBuffer.st_mode & S_IXGRP)
- entryFlags |= QFileSystemMetaData::GroupExecutePermission;
-
- if (statBuffer.st_mode & S_IROTH)
- entryFlags |= QFileSystemMetaData::OtherReadPermission;
- if (statBuffer.st_mode & S_IWOTH)
- entryFlags |= QFileSystemMetaData::OtherWritePermission;
- if (statBuffer.st_mode & S_IXOTH)
- entryFlags |= QFileSystemMetaData::OtherExecutePermission;
-
- // Type
- if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
- entryFlags |= QFileSystemMetaData::FileType;
- else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
- entryFlags |= QFileSystemMetaData::DirectoryType;
- else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
- entryFlags |= QFileSystemMetaData::SequentialType;
-
- // Attributes
- entryFlags |= QFileSystemMetaData::ExistsAttribute;
- size_ = statBuffer.st_size;
-#if defined(Q_OS_DARWIN)
- if (statBuffer.st_flags & UF_HIDDEN) {
- entryFlags |= QFileSystemMetaData::HiddenAttribute;
- knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
- }
-#endif
-
- // Times
-#if _POSIX_VERSION >= 200809L
- modificationTime_ = timespecToMSecs(statBuffer.st_mtim);
- creationTime_ = timespecToMSecs(statBuffer.st_ctim);
- if (!creationTime_)
- creationTime_ = modificationTime_;
- accessTime_ = timespecToMSecs(statBuffer.st_atim);
-#else
- creationTime_ = qint64(statBuffer.st_ctime ? statBuffer.st_ctime : statBuffer.st_mtime) * 1000;
- modificationTime_ = qint64(statBuffer.st_mtime) * 1000;
- accessTime_ = qint64(statBuffer.st_atime) * 1000;
-#endif
- userId_ = statBuffer.st_uid;
- groupId_ = statBuffer.st_gid;
-}
-
-void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
-{
-#if defined(_DEXTRA_FIRST)
- knownFlagsMask = 0;
- entryFlags = 0;
- for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
- extra = _DEXTRA_NEXT(extra)) {
- if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
-
- const struct dirent_extra_stat * const extra_stat =
- reinterpret_cast<struct dirent_extra_stat *>(extra);
-
- // Remember whether this was a link or not, this saves an lstat() call later.
- if (extra->d_type == _DTYPE_LSTAT) {
- knownFlagsMask |= QFileSystemMetaData::LinkType;
- if (S_ISLNK(extra_stat->d_stat.st_mode))
- entryFlags |= QFileSystemMetaData::LinkType;
- }
-
- // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
- // as we need the stat() information there, not the lstat() information.
- // In this case, don't use the extra information.
- // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
- // symlinks, we always incur the cost of an extra stat() call later.
- if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
- continue;
-
-#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
- // Even with large file support, d_stat is always of type struct stat, not struct stat64,
- // so it needs to be converted
- struct stat64 statBuf;
- fillStat64fromStat32(&statBuf, extra_stat->d_stat);
- fillFromStatBuf(statBuf);
-#else
- fillFromStatBuf(extra_stat->d_stat);
-#endif
- knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
- if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
- knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
- entryFlags |= QFileSystemMetaData::ExistsAttribute;
- }
- }
- }
-#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
- // BSD4 includes OS X and iOS
-
- // ### This will clear all entry flags and knownFlagsMask
- switch (entry.d_type)
- {
- case DT_DIR:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_BLK:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::AliasType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_CHR:
- case DT_FIFO:
- case DT_SOCK:
- // ### System attribute
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::AliasType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_LNK:
- knownFlagsMask = QFileSystemMetaData::LinkType;
- entryFlags = QFileSystemMetaData::LinkType;
- break;
-
- case DT_REG:
- knownFlagsMask = QFileSystemMetaData::LinkType
- | QFileSystemMetaData::FileType
- | QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType
- | QFileSystemMetaData::SequentialType
- | QFileSystemMetaData::ExistsAttribute;
-
- entryFlags = QFileSystemMetaData::FileType
- | QFileSystemMetaData::ExistsAttribute;
-
- break;
-
- case DT_UNKNOWN:
- default:
- clear();
- }
-#else
- Q_UNUSED(entry)
-#endif
-}
-
-#endif
-
//static
QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData)
{
@@ -435,6 +211,8 @@ QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileS
#else //(Q_OS_UNIX)
if (!metaData.hasFlags(QFileSystemMetaData::UserId))
QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId);
+ if (!metaData.exists())
+ return QString();
return resolveUserName(metaData.userId());
#endif
}
@@ -448,6 +226,8 @@ QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFile
#else //(Q_OS_UNIX)
if (!metaData.hasFlags(QFileSystemMetaData::GroupId))
QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId);
+ if (!metaData.exists())
+ return QString();
return resolveGroupName(metaData.groupId());
#endif
}
diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h
index e3e52f6eaa..09ec2d6a10 100644
--- a/src/corelib/io/qfilesystemengine_p.h
+++ b/src/corelib/io/qfilesystemengine_p.h
@@ -91,8 +91,11 @@ public:
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what);
#if defined(Q_OS_UNIX)
+ static bool cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData);
static bool fillMetaData(int fd, QFileSystemMetaData &data); // what = PosixStatFlags
static QByteArray id(int fd);
+ static bool setFileTime(int fd, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
static bool setPermissions(int fd, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data = nullptr);
#endif
@@ -106,6 +109,8 @@ public:
static bool fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what);
static QByteArray id(HANDLE fHandle);
+ static bool setFileTime(HANDLE fHandle, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
static QString owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own);
static QString nativeAbsoluteFilePath(const QString &path);
#endif
@@ -121,11 +126,16 @@ public:
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
+ static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool removeFile(const QFileSystemEntry &entry, QSystemError &error);
static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data = 0);
+ // unused, therefore not implemented
+ static bool setFileTime(const QFileSystemEntry &entry, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime whatTime, QSystemError &error);
+
static bool setCurrentPath(const QFileSystemEntry &entry);
static QFileSystemEntry currentPath();
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 7fed54f733..d77cdc123c 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -43,6 +43,8 @@
#include "qfilesystemengine_p.h"
#include "qfile.h"
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/qvarlengtharray.h>
#include <pwd.h>
@@ -53,7 +55,6 @@
#include <stdio.h>
#include <errno.h>
-
#if defined(Q_OS_MAC)
# include <QtCore/private/qcore_mac_p.h>
# include <CoreFoundation/CFBundle.h>
@@ -68,14 +69,48 @@
#endif
#if defined(Q_OS_DARWIN)
+# if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+# include <sys/clonefile.h>
+# endif
+# include <copyfile.h>
// We cannot include <Foundation/Foundation.h> (it's an Objective-C header), but
// we need these declarations:
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
extern "C" NSString *NSTemporaryDirectory();
#endif
+#if defined(Q_OS_LINUX)
+# include <sys/ioctl.h>
+# include <sys/syscall.h>
+# include <sys/sendfile.h>
+# include <linux/fs.h>
+
+// in case linux/fs.h is too old and doesn't define it:
+#ifndef FICLONE
+# define FICLONE _IOW(0x94, 9, int)
+#endif
+
+# if !QT_CONFIG(renameat2) && defined(SYS_renameat2)
+static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags)
+{ return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); }
+# endif
+
+# if !QT_CONFIG(statx) && defined(SYS_statx) && QT_HAS_INCLUDE(<linux/stat.h>)
+# include <linux/stat.h>
+static int statx(int dirfd, const char *pathname, int flag, unsigned mask, struct statx *statxbuf)
+{ return syscall(SYS_statx, dirfd, pathname, flag, mask, statxbuf); }
+# endif
+#endif
+
QT_BEGIN_NAMESPACE
+#define emptyFileEntryWarning() emptyFileEntryWarning_(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC)
+static void emptyFileEntryWarning_(const char *file, int line, const char *function)
+{
+ QMessageLogger(file, line, function).warning("Empty filename passed to function");
+ errno = EINVAL;
+}
+
#if defined(Q_OS_DARWIN)
static inline bool hasResourcePropertyFlag(const QFileSystemMetaData &data,
const QFileSystemEntry &entry,
@@ -144,33 +179,453 @@ static bool isPackage(const QFileSystemMetaData &data, const QFileSystemEntry &e
}
#endif
+namespace {
+namespace GetFileTimes {
+#if !QT_CONFIG(futimens) && (QT_CONFIG(futimes))
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atim, &T::st_mtim, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atim.tv_sec;
+ access->tv_usec = p->st_atim.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtim.tv_sec;
+ modification->tv_usec = p->st_mtim.tv_nsec / 1000;
+}
+
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimespec, &T::st_mtimespec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atimespec.tv_sec;
+ access->tv_usec = p->st_atimespec.tv_nsec / 1000;
+
+ modification->tv_sec = p->st_mtimespec.tv_sec;
+ modification->tv_usec = p->st_mtimespec.tv_nsec / 1000;
+}
+
+template <typename T>
+static inline typename QtPrivate::QEnableIf<(&T::st_atimensec, &T::st_mtimensec, true)>::Type get(const T *p, struct timeval *access, struct timeval *modification)
+{
+ access->tv_sec = p->st_atime;
+ access->tv_usec = p->st_atimensec / 1000;
+
+ modification->tv_sec = p->st_mtime;
+ modification->tv_usec = p->st_mtimensec / 1000;
+}
+#endif
+
+qint64 timespecToMSecs(const timespec &spec)
+{
+ return (qint64(spec.tv_sec) * 1000) + (spec.tv_nsec / 1000000);
+}
+
+// fallback set
+Q_DECL_UNUSED qint64 atime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_atime) * 1000; }
+Q_DECL_UNUSED qint64 birthtime(const QT_STATBUF &, ulong) { return Q_INT64_C(0); }
+Q_DECL_UNUSED qint64 ctime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_ctime) * 1000; }
+Q_DECL_UNUSED qint64 mtime(const QT_STATBUF &statBuffer, ulong) { return qint64(statBuffer.st_mtime) * 1000; }
+
+// Xtim, POSIX.1-2008
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atim, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtim, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctim, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctim); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtim, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtim); }
+
+#ifndef st_mtimespec
+// Xtimespec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimespec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_atimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimespec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_birthtimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimespec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_ctimespec); }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimespec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return timespecToMSecs(statBuffer.st_mtimespec); }
+#endif
+
+// Xtimensec
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_atimensec, true), qint64>::type
+atime(const T &statBuffer, int)
+{ return statBuffer.st_atime * Q_INT64_C(1000) + statBuffer.st_atimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_birthtimensec, true), qint64>::type
+birthtime(const T &statBuffer, int)
+{ return statBuffer.st_birthtime * Q_INT64_C(1000) + statBuffer.st_birthtimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_ctimensec, true), qint64>::type
+ctime(const T &statBuffer, int)
+{ return statBuffer.st_ctime * Q_INT64_C(1000) + statBuffer.st_ctimensec / 1000000; }
+
+template <typename T>
+Q_DECL_UNUSED static typename std::enable_if<(&T::st_mtimensec, true), qint64>::type
+mtime(const T &statBuffer, int)
+{ return statBuffer.st_mtime * Q_INT64_C(1000) + statBuffer.st_mtimensec / 1000000; }
+}
+}
+
+#ifdef STATX_BASIC_STATS
+static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
+{
+#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
+ static QBasicAtomicInteger<qint8> statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#else
+ static QBasicAtomicInt statxTested = Q_BASIC_ATOMIC_INITIALIZER(0);
+#endif
+
+ if (statxTested.load() == -1)
+ return -ENOSYS;
+
+ unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
+ int ret = statx(fd, pathname, flags, mask, statxBuffer);
+ if (ret == -1 && errno == ENOSYS) {
+ statxTested.store(-1);
+ return -ENOSYS;
+ }
+ statxTested.store(1);
+ return ret == -1 ? -errno : 0;
+}
+
+static int qt_statx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, 0, statxBuffer);
+}
+
+static int qt_lstatx(const char *pathname, struct statx *statxBuffer)
+{
+ return qt_real_statx(AT_FDCWD, pathname, AT_SYMLINK_NOFOLLOW, statxBuffer);
+}
+
+static int qt_fstatx(int fd, struct statx *statxBuffer)
+{
+ return qt_real_statx(fd, "", AT_EMPTY_PATH, statxBuffer);
+}
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &statxBuffer)
+{
+ // Permissions
+ if (statxBuffer.stx_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statxBuffer.stx_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statxBuffer.stx_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statxBuffer.stx_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statxBuffer.stx_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statxBuffer.stx_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statxBuffer.stx_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statxBuffer.stx_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if (S_ISLNK(statxBuffer.stx_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ if ((statxBuffer.stx_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statxBuffer.stx_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statxBuffer.stx_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statxBuffer.stx_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = qint64(statxBuffer.stx_size);
+
+ // Times
+ auto toMSecs = [](struct statx_timestamp ts)
+ {
+ return qint64(ts.tv_sec) * 1000 + (ts.tv_nsec / 1000000);
+ };
+ accessTime_ = toMSecs(statxBuffer.stx_atime);
+ metadataChangeTime_ = toMSecs(statxBuffer.stx_ctime);
+ modificationTime_ = toMSecs(statxBuffer.stx_mtime);
+ if (statxBuffer.stx_mask & STATX_BTIME)
+ birthTime_ = toMSecs(statxBuffer.stx_btime);
+ else
+ birthTime_ = 0;
+
+ userId_ = statxBuffer.stx_uid;
+ groupId_ = statxBuffer.stx_gid;
+}
+#else
+struct statx { mode_t stx_mode; };
+static int qt_statx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_lstatx(const char *, struct statx *)
+{ return -ENOSYS; }
+
+static int qt_fstatx(int, struct statx *)
+{ return -ENOSYS; }
+
+inline void QFileSystemMetaData::fillFromStatxBuf(const struct statx &)
+{ }
+#endif
+
//static
-QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
{
-#if defined(__GLIBC__) && !defined(PATH_MAX)
-#define PATH_CHUNK_SIZE 256
- char *s = 0;
- int len = -1;
- int size = PATH_CHUNK_SIZE;
-
- while (1) {
- s = (char *) ::realloc(s, size);
- Q_CHECK_PTR(s);
- len = ::readlink(link.nativeFilePath().constData(), s, size);
- if (len < 0) {
- ::free(s);
- break;
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+
+ union {
+ struct statx statxBuffer;
+ QT_STATBUF statBuffer;
+ };
+
+ int ret = qt_fstatx(fd, &statxBuffer);
+ if (ret != -ENOSYS) {
+ if (ret == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ return true;
}
- if (len < size) {
- break;
+ return false;
+ }
+
+ if (QT_FSTAT(fd, &statBuffer) == 0) {
+ data.fillFromStatBuf(statBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+#if defined(_DEXTRA_FIRST)
+static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
+{
+ statBuf64->st_mode = statBuf32.st_mode;
+ statBuf64->st_size = statBuf32.st_size;
+#if _POSIX_VERSION >= 200809L
+ statBuf64->st_ctim = statBuf32.st_ctim;
+ statBuf64->st_mtim = statBuf32.st_mtim;
+ statBuf64->st_atim = statBuf32.st_atim;
+#else
+ statBuf64->st_ctime = statBuf32.st_ctime;
+ statBuf64->st_mtime = statBuf32.st_mtime;
+ statBuf64->st_atime = statBuf32.st_atime;
+#endif
+ statBuf64->st_uid = statBuf32.st_uid;
+ statBuf64->st_gid = statBuf32.st_gid;
+}
+#endif
+
+void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
+{
+ // Permissions
+ if (statBuffer.st_mode & S_IRUSR)
+ entryFlags |= QFileSystemMetaData::OwnerReadPermission;
+ if (statBuffer.st_mode & S_IWUSR)
+ entryFlags |= QFileSystemMetaData::OwnerWritePermission;
+ if (statBuffer.st_mode & S_IXUSR)
+ entryFlags |= QFileSystemMetaData::OwnerExecutePermission;
+
+ if (statBuffer.st_mode & S_IRGRP)
+ entryFlags |= QFileSystemMetaData::GroupReadPermission;
+ if (statBuffer.st_mode & S_IWGRP)
+ entryFlags |= QFileSystemMetaData::GroupWritePermission;
+ if (statBuffer.st_mode & S_IXGRP)
+ entryFlags |= QFileSystemMetaData::GroupExecutePermission;
+
+ if (statBuffer.st_mode & S_IROTH)
+ entryFlags |= QFileSystemMetaData::OtherReadPermission;
+ if (statBuffer.st_mode & S_IWOTH)
+ entryFlags |= QFileSystemMetaData::OtherWritePermission;
+ if (statBuffer.st_mode & S_IXOTH)
+ entryFlags |= QFileSystemMetaData::OtherExecutePermission;
+
+ // Type
+ if ((statBuffer.st_mode & S_IFMT) == S_IFREG)
+ entryFlags |= QFileSystemMetaData::FileType;
+ else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR)
+ entryFlags |= QFileSystemMetaData::DirectoryType;
+ else if ((statBuffer.st_mode & S_IFMT) != S_IFBLK)
+ entryFlags |= QFileSystemMetaData::SequentialType;
+
+ // Attributes
+ entryFlags |= QFileSystemMetaData::ExistsAttribute; // inode exists
+ if (statBuffer.st_nlink == 0)
+ entryFlags |= QFileSystemMetaData::WasDeletedAttribute;
+ size_ = statBuffer.st_size;
+#ifdef UF_HIDDEN
+ if (statBuffer.st_flags & UF_HIDDEN) {
+ entryFlags |= QFileSystemMetaData::HiddenAttribute;
+ knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
+ }
+#endif
+
+ // Times
+ accessTime_ = GetFileTimes::atime(statBuffer, 0);
+ birthTime_ = GetFileTimes::birthtime(statBuffer, 0);
+ metadataChangeTime_ = GetFileTimes::ctime(statBuffer, 0);
+ modificationTime_ = GetFileTimes::mtime(statBuffer, 0);
+
+ userId_ = statBuffer.st_uid;
+ groupId_ = statBuffer.st_gid;
+}
+
+void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
+{
+#if defined(_DEXTRA_FIRST)
+ knownFlagsMask = 0;
+ entryFlags = 0;
+ for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
+ extra = _DEXTRA_NEXT(extra)) {
+ if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
+
+ const struct dirent_extra_stat * const extra_stat =
+ reinterpret_cast<struct dirent_extra_stat *>(extra);
+
+ // Remember whether this was a link or not, this saves an lstat() call later.
+ if (extra->d_type == _DTYPE_LSTAT) {
+ knownFlagsMask |= QFileSystemMetaData::LinkType;
+ if (S_ISLNK(extra_stat->d_stat.st_mode))
+ entryFlags |= QFileSystemMetaData::LinkType;
+ }
+
+ // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
+ // as we need the stat() information there, not the lstat() information.
+ // In this case, don't use the extra information.
+ // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
+ // symlinks, we always incur the cost of an extra stat() call later.
+ if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
+ continue;
+
+#if defined(QT_USE_XOPEN_LFS_EXTENSIONS) && defined(QT_LARGEFILE_SUPPORT)
+ // Even with large file support, d_stat is always of type struct stat, not struct stat64,
+ // so it needs to be converted
+ struct stat64 statBuf;
+ fillStat64fromStat32(&statBuf, extra_stat->d_stat);
+ fillFromStatBuf(statBuf);
+#else
+ fillFromStatBuf(extra_stat->d_stat);
+#endif
+ knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+ if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
+ knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
+ entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
}
- size *= 2;
+ }
+#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
+ // BSD4 includes OS X and iOS
+
+ // ### This will clear all entry flags and knownFlagsMask
+ switch (entry.d_type)
+ {
+ case DT_DIR:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_BLK:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_CHR:
+ case DT_FIFO:
+ case DT_SOCK:
+ // ### System attribute
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::AliasType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_LNK:
+ knownFlagsMask = QFileSystemMetaData::LinkType;
+ entryFlags = QFileSystemMetaData::LinkType;
+ break;
+
+ case DT_REG:
+ knownFlagsMask = QFileSystemMetaData::LinkType
+ | QFileSystemMetaData::FileType
+ | QFileSystemMetaData::DirectoryType
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::SequentialType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ entryFlags = QFileSystemMetaData::FileType
+ | QFileSystemMetaData::ExistsAttribute;
+
+ break;
+
+ case DT_UNKNOWN:
+ default:
+ clear();
}
#else
- char s[PATH_MAX+1];
- int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX);
+ Q_UNUSED(entry)
#endif
- if (len > 0) {
+}
+
+//static
+QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data)
+{
+ if (Q_UNLIKELY(link.isEmpty()))
+ return emptyFileEntryWarning(), link;
+
+ QByteArray s = qt_readlink(link.nativeFilePath().constData());
+ if (s.length() > 0) {
QString ret;
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
fillMetaData(link, data, QFileSystemMetaData::DirectoryType);
@@ -181,19 +636,10 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
ret += QLatin1Char('/');
}
- s[len] = '\0';
- ret += QFile::decodeName(QByteArray(s));
-#if defined(__GLIBC__) && !defined(PATH_MAX)
- ::free(s);
-#endif
+ ret += QFile::decodeName(s);
- if (!ret.startsWith(QLatin1Char('/'))) {
- const QString linkPath = link.path();
- if (linkPath.startsWith(QLatin1Char('/')))
- ret.prepend(linkPath + QLatin1Char('/'));
- else
- ret.prepend(QDir::currentPath() + QLatin1Char('/') + linkPath + QLatin1Char('/'));
- }
+ if (!ret.startsWith(QLatin1Char('/')))
+ ret.prepend(absoluteName(link).path() + QLatin1Char('/'));
ret = QDir::cleanPath(ret);
if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
ret.chop(1);
@@ -235,7 +681,9 @@ QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link,
//static
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
- if (entry.isEmpty() || entry.isRoot())
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
+ if (entry.isRoot())
return entry;
#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
@@ -302,6 +750,8 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
//static
QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), entry;
if (entry.isAbsolute() && entry.isClean())
return entry;
@@ -335,6 +785,9 @@ QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry)
//static
QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), QByteArray();
+
QT_STATBUF statResult;
if (QT_STAT(entry.nativeFilePath().constData(), &statResult)) {
if (errno != ENOENT)
@@ -442,56 +895,105 @@ QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry)
bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data,
QFileSystemMetaData::MetaDataFlags what)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
#if defined(Q_OS_DARWIN)
if (what & QFileSystemMetaData::BundleType) {
if (!data.hasFlags(QFileSystemMetaData::DirectoryType))
what |= QFileSystemMetaData::DirectoryType;
}
+#endif
+#ifdef UF_HIDDEN
if (what & QFileSystemMetaData::HiddenAttribute) {
// OS X >= 10.5: st_flags & UF_HIDDEN
what |= QFileSystemMetaData::PosixStatFlags;
}
#endif // defined(Q_OS_DARWIN)
+ // if we're asking for any of the stat(2) flags, then we're getting them all
if (what & QFileSystemMetaData::PosixStatFlags)
what |= QFileSystemMetaData::PosixStatFlags;
- if (what & QFileSystemMetaData::ExistsAttribute) {
- // FIXME: Would other queries being performed provide this bit?
- what |= QFileSystemMetaData::PosixStatFlags;
- }
-
data.entryFlags &= ~what;
const QByteArray nativeFilePath = entry.nativeFilePath();
bool entryExists = true; // innocent until proven otherwise
- QT_STATBUF statBuffer;
- bool statBufferValid = false;
+ // first, we may try lstat(2). Possible outcomes:
+ // - success and is a symlink: filesystem entry exists, but we need stat(2)
+ // -> statResult = -1;
+ // - success and is not a symlink: filesystem entry exists and we're done
+ // -> statResult = 0
+ // - failure: really non-existent filesystem entry
+ // -> entryExists = false; statResult = 0;
+ // both stat(2) and lstat(2) may generate a number of different errno
+ // conditions, but of those, the only ones that could happen and the
+ // entry still exist are EACCES, EFAULT, ENOMEM and EOVERFLOW. If we get
+ // EACCES or ENOMEM, then we have no choice on how to proceed, so we may
+ // as well conclude it doesn't exist; EFAULT can't happen and EOVERFLOW
+ // shouldn't happen because we build in _LARGEFIE64.
+ union {
+ QT_STATBUF statBuffer;
+ struct statx statxBuffer;
+ };
+ int statResult = -1;
if (what & QFileSystemMetaData::LinkType) {
- if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) {
- if (S_ISLNK(statBuffer.st_mode)) {
+ mode_t mode = 0;
+ statResult = qt_lstatx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use lstst(2)
+ statResult = QT_LSTAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ mode = statBuffer.st_mode;
+ } else if (statResult == 0) {
+ statResult = 1; // record it was statx(2) that succeeded
+ mode = statxBuffer.stx_mode;
+ }
+
+ if (statResult >= 0) {
+ if (S_ISLNK(mode)) {
+ // it's a symlink, we don't know if the file "exists"
data.entryFlags |= QFileSystemMetaData::LinkType;
+ statResult = -1; // force stat(2) below
} else {
- statBufferValid = true;
- data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ // it's a reagular file and it exists
+ if (statResult)
+ data.fillFromStatxBuf(statxBuffer);
+ else
+ data.fillFromStatBuf(statBuffer);
+ data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags
+ | QFileSystemMetaData::ExistsAttribute;
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
}
} else {
+ // it doesn't exist
entryExists = false;
+ data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
}
data.knownFlagsMask |= QFileSystemMetaData::LinkType;
}
- if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) {
- if (entryExists && !statBufferValid)
- statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0);
+ // second, we try a regular stat(2)
+ if (statResult != 0 && (what & QFileSystemMetaData::PosixStatFlags)) {
+ if (entryExists && statResult == -1) {
+ data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags;
+ statResult = qt_statx(nativeFilePath, &statxBuffer);
+ if (statResult == -ENOSYS) {
+ // use stat(2)
+ statResult = QT_STAT(nativeFilePath, &statBuffer);
+ if (statResult == 0)
+ data.fillFromStatBuf(statBuffer);
+ } else if (statResult == 0) {
+ data.fillFromStatxBuf(statxBuffer);
+ }
+ }
- if (statBufferValid)
- data.fillFromStatBuf(statBuffer);
- else {
+ if (statResult != 0) {
entryExists = false;
- data.creationTime_ = 0;
+ data.birthTime_ = 0;
+ data.metadataChangeTime_ = 0;
data.modificationTime_ = 0;
data.accessTime_ = 0;
data.size_ = 0;
@@ -504,34 +1006,49 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
| QFileSystemMetaData::ExistsAttribute;
}
+ // third, we try access(2)
+ if (what & (QFileSystemMetaData::UserPermissions | QFileSystemMetaData::ExistsAttribute)) {
+ // calculate user permissions
+ auto checkAccess = [&](QFileSystemMetaData::MetaDataFlag flag, int mode) {
+ if (!entryExists || (what & flag) == 0)
+ return;
+ if (QT_ACCESS(nativeFilePath, mode) == 0) {
+ // access ok (and file exists)
+ data.entryFlags |= flag | QFileSystemMetaData::ExistsAttribute;
+ } else if (errno != EACCES && errno != EROFS) {
+ entryExists = false;
+ }
+ };
+
+ checkAccess(QFileSystemMetaData::UserReadPermission, R_OK);
+ checkAccess(QFileSystemMetaData::UserWritePermission, W_OK);
+ checkAccess(QFileSystemMetaData::UserExecutePermission, X_OK);
+
+ // if we still haven't found out if the file exists, try F_OK
+ if (entryExists && (data.entryFlags & QFileSystemMetaData::ExistsAttribute) == 0) {
+ entryExists = QT_ACCESS(nativeFilePath, F_OK) == 0;
+ if (entryExists)
+ data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
+ }
+
+ data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions) |
+ QFileSystemMetaData::ExistsAttribute;
+ }
+
#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::AliasType)
- {
+ if (what & QFileSystemMetaData::AliasType) {
if (entryExists && hasResourcePropertyFlag(data, entry, kCFURLIsAliasFileKey))
data.entryFlags |= QFileSystemMetaData::AliasType;
data.knownFlagsMask |= QFileSystemMetaData::AliasType;
}
-#endif
- if (what & QFileSystemMetaData::UserPermissions) {
- // calculate user permissions
+ if (what & QFileSystemMetaData::BundleType) {
+ if (entryExists && isPackage(data, entry))
+ data.entryFlags |= QFileSystemMetaData::BundleType;
- if (entryExists) {
- if (what & QFileSystemMetaData::UserReadPermission) {
- if (QT_ACCESS(nativeFilePath, R_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserReadPermission;
- }
- if (what & QFileSystemMetaData::UserWritePermission) {
- if (QT_ACCESS(nativeFilePath, W_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserWritePermission;
- }
- if (what & QFileSystemMetaData::UserExecutePermission) {
- if (QT_ACCESS(nativeFilePath, X_OK) == 0)
- data.entryFlags |= QFileSystemMetaData::UserExecutePermission;
- }
- }
- data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions);
+ data.knownFlagsMask |= QFileSystemMetaData::BundleType;
}
+#endif
if (what & QFileSystemMetaData::HiddenAttribute
&& !data.isHidden()) {
@@ -545,21 +1062,80 @@ bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemM
data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute;
}
-#if defined(Q_OS_DARWIN)
- if (what & QFileSystemMetaData::BundleType) {
- if (entryExists && isPackage(data, entry))
- data.entryFlags |= QFileSystemMetaData::BundleType;
-
- data.knownFlagsMask |= QFileSystemMetaData::BundleType;
- }
-#endif
if (!entryExists) {
+ what &= ~QFileSystemMetaData::LinkType; // don't clear link: could be broken symlink
data.clearFlags(what);
return false;
}
return data.hasFlags(what);
}
+// static
+bool QFileSystemEngine::cloneFile(int srcfd, int dstfd, const QFileSystemMetaData &knownData)
+{
+ QT_STATBUF statBuffer;
+ if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isFile()) {
+ statBuffer.st_size = knownData.size();
+ statBuffer.st_mode = S_IFREG;
+ } else if (knownData.hasFlags(QFileSystemMetaData::PosixStatFlags) &&
+ knownData.isDirectory()) {
+ return false; // fcopyfile(3) returns success on directories
+ } else if (QT_FSTAT(srcfd, &statBuffer) == -1) {
+ return false;
+ } else if (!S_ISREG((statBuffer.st_mode))) {
+ // not a regular file, let QFile do the copy
+ return false;
+ }
+
+#if defined(Q_OS_LINUX)
+ if (statBuffer.st_size == 0) {
+ // empty file? we're done.
+ return true;
+ }
+
+ // first, try FICLONE (only works on regular files and only on certain fs)
+ if (::ioctl(dstfd, FICLONE, srcfd) == 0)
+ return true;
+
+ // Second, try sendfile (it can send to some special types too).
+ // sendfile(2) is limited in the kernel to 2G - 4k
+ auto sendfileSize = [](QT_OFF_T size) { return size_t(qMin<qint64>(0x7ffff000, size)); };
+
+ ssize_t n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == -1) {
+ // if we got an error here, give up and try at an upper layer
+ return false;
+ }
+
+ statBuffer.st_size -= n;
+ while (statBuffer.st_size) {
+ n = ::sendfile(dstfd, srcfd, NULL, sendfileSize(statBuffer.st_size));
+ if (n == 0) {
+ // uh oh, this is probably a real error (like ENOSPC), but we have
+ // no way to notify QFile of partial success, so just erase any work
+ // done (hopefully we won't get any errors, because there's nothing
+ // we can do about them)
+ n = ftruncate(dstfd, 0);
+ n = lseek(srcfd, 0, SEEK_SET);
+ n = lseek(dstfd, 0, SEEK_SET);
+ return false;
+ }
+ if (n == 0)
+ return true;
+ statBuffer.st_size -= n;
+ }
+
+ return true;
+#elif defined(Q_OS_DARWIN)
+ // try fcopyfile
+ return fcopyfile(srcfd, dstfd, nullptr, COPYFILE_DATA | COPYFILE_STAT) == 0;
+#else
+ Q_UNUSED(dstfd);
+ return false;
+#endif
+}
+
// Note: if \a shouldMkdirFirst is false, we assume the caller did try to mkdir
// before calling this function.
static bool createDirectoryWithParents(const QByteArray &nativeName, bool shouldMkdirFirst = true)
@@ -598,6 +1174,8 @@ static bool createDirectoryWithParents(const QByteArray &nativeName, bool should
bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents)
{
QString dirName = entry.filePath();
+ if (Q_UNLIKELY(dirName.isEmpty()))
+ return emptyFileEntryWarning(), false;
// Darwin doesn't support trailing /'s, so remove for everyone
while (dirName.size() > 1 && dirName.endsWith(QLatin1Char('/')))
@@ -616,6 +1194,9 @@ bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool crea
//static
bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
if (removeEmptyParents) {
QString dirName = QDir::cleanPath(entry.filePath());
for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
@@ -639,6 +1220,8 @@ bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool remo
//static
bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -648,8 +1231,18 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(101200, 100000, 100000, 30000)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (::clonefile(source.nativeFilePath().constData(),
+ target.nativeFilePath().constData(), 0) == 0)
+ return true;
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#else
Q_UNUSED(source);
Q_UNUSED(target);
+#endif
error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented
return false;
}
@@ -657,6 +1250,78 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst
//static
bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
{
+ QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
+ QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
+ if (Q_UNLIKELY(srcPath.isEmpty() || tgtPath.isEmpty()))
+ return emptyFileEntryWarning(), false;
+
+#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2))
+ if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
+ return true;
+
+ // If we're using syscall(), check for ENOSYS;
+ // if renameat2 came from libc, we don't accept ENOSYS.
+ // We can also get EINVAL for some non-local filesystems.
+ if ((QT_CONFIG(renameat2) || errno != ENOSYS) && errno != EINVAL) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#endif
+#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) {
+ if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
+ return true;
+ if (errno != ENOTSUP) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+ }
+#endif
+
+ if (::link(srcPath, tgtPath) == 0) {
+ if (::unlink(srcPath) == 0)
+ return true;
+
+ // if we managed to link but can't unlink the source, it's likely
+ // it's in a directory we don't have write access to; fail the
+ // renaming instead
+ int savedErrno = errno;
+
+ // this could fail too, but there's nothing we can do about it now
+ ::unlink(tgtPath);
+
+ error = QSystemError(savedErrno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ switch (errno) {
+ case EACCES:
+ case EEXIST:
+ case ENAMETOOLONG:
+ case ENOENT:
+ case ENOTDIR:
+ case EROFS:
+ case EXDEV:
+ // accept the error from link(2) (especially EEXIST) and don't retry
+ break;
+
+ default:
+ // fall back to rename()
+ // ### Race condition. If a file is moved in after this, it /will/ be
+ // overwritten.
+ if (::rename(srcPath, tgtPath) == 0)
+ return true;
+ }
+
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+}
+
+//static
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+{
+ if (Q_UNLIKELY(source.isEmpty() || target.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -666,6 +1331,8 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
if (unlink(entry.nativeFilePath().constData()) == 0)
return true;
error = QSystemError(errno, QSystemError::StandardLibraryError);
@@ -700,8 +1367,10 @@ static mode_t toMode_t(QFile::Permissions permissions)
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data)
{
- mode_t mode = toMode_t(permissions);
+ if (Q_UNLIKELY(entry.isEmpty()))
+ return emptyFileEntryWarning(), false;
+ mode_t mode = toMode_t(permissions);
bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0;
if (success && data) {
data->entryFlags &= ~QFileSystemMetaData::Permissions;
@@ -729,6 +1398,71 @@ bool QFileSystemEngine::setPermissions(int fd, QFile::Permissions permissions, Q
return success;
}
+//static
+bool QFileSystemEngine::setFileTime(int fd, const QDateTime &newDate, QAbstractFileEngine::FileTime time, QSystemError &error)
+{
+ if (!newDate.isValid() || time == QAbstractFileEngine::BirthTime ||
+ time == QAbstractFileEngine::MetadataChangeTime) {
+ error = QSystemError(EINVAL, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+#if QT_CONFIG(futimens)
+ struct timespec ts[2];
+
+ ts[0].tv_sec = ts[1].tv_sec = 0;
+ ts[0].tv_nsec = ts[1].tv_nsec = UTIME_OMIT;
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ ts[0].tv_sec = msecs / 1000;
+ ts[0].tv_nsec = (msecs % 1000) * 1000000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ ts[1].tv_sec = msecs / 1000;
+ ts[1].tv_nsec = (msecs % 1000) * 1000000;
+ }
+
+ if (futimens(fd, ts) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#elif QT_CONFIG(futimes)
+ struct timeval tv[2];
+ QT_STATBUF st;
+
+ if (QT_FSTAT(fd, &st) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ GetFileTimes::get(&st, &tv[0], &tv[1]);
+
+ const qint64 msecs = newDate.toMSecsSinceEpoch();
+
+ if (time == QAbstractFileEngine::AccessTime) {
+ tv[0].tv_sec = msecs / 1000;
+ tv[0].tv_usec = (msecs % 1000) * 1000;
+ } else if (time == QAbstractFileEngine::ModificationTime) {
+ tv[1].tv_sec = msecs / 1000;
+ tv[1].tv_usec = (msecs % 1000) * 1000;
+ }
+
+ if (futimes(fd, tv) == -1) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+
+ return true;
+#else
+ Q_UNUSED(fd);
+ error = QSystemError(ENOSYS, QSystemError::StandardLibraryError);
+ return false;
+#endif
+}
+
QString QFileSystemEngine::homePath()
{
QString home = QFile::decodeName(qgetenv("HOME"));
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index b01aa55756..944ca232ee 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -48,7 +48,6 @@
#include "qfile.h"
#include "qdir.h"
-#include "private/qmutexpool_p.h"
#include "qvarlengtharray.h"
#include "qdatetime.h"
#include "qt_windows.h"
@@ -60,6 +59,7 @@
#include <objbase.h>
#ifndef Q_OS_WINRT
# include <shlobj.h>
+# include <lm.h>
# include <accctrl.h>
#endif
#include <initguid.h>
@@ -147,48 +147,30 @@ typedef struct _REPARSE_DATA_BUFFER {
# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
-QT_BEGIN_NAMESPACE
-
-Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
-
-#if defined(Q_OS_WINRT)
-// As none of the functions we try to resolve do exist on WinRT we
-// avoid library loading on WinRT in general to shorten everything
-// up a little bit.
+#if defined(Q_OS_WINRT) || defined(QT_BOOTSTRAPPED)
# define QT_FEATURE_fslibs -1
#else
-# define QT_FEATURE_fslibs QT_FEATURE_library
+# define QT_FEATURE_fslibs 1
#endif // Q_OS_WINRT
#if QT_CONFIG(fslibs)
-QT_BEGIN_INCLUDE_NAMESPACE
-typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
-static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
-typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
-static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
-typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
-static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
-typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
-static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
-typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
-static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
-QT_END_INCLUDE_NAMESPACE
-
+#include <aclapi.h>
+#include <userenv.h>
static TRUSTEE_W currentUserTrusteeW;
static TRUSTEE_W worldTrusteeW;
static PSID currentUserSID = 0;
static PSID worldSID = 0;
-/*
- Deletes the allocated SIDs during global static cleanup
-*/
-class SidCleanup
+QT_BEGIN_NAMESPACE
+
+namespace {
+struct GlobalSid
{
-public:
- ~SidCleanup();
+ GlobalSid();
+ ~GlobalSid();
};
-SidCleanup::~SidCleanup()
+GlobalSid::~GlobalSid()
{
free(currentUserSID);
currentUserSID = 0;
@@ -200,39 +182,13 @@ SidCleanup::~SidCleanup()
}
}
-Q_GLOBAL_STATIC(SidCleanup, initSidCleanup)
-
-static void resolveLibs()
+GlobalSid::GlobalSid()
{
- static bool triedResolve = false;
- if (!triedResolve) {
- // need to resolve the security info functions
-
- // protect initialization
-#ifndef QT_NO_THREAD
- QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
- // check triedResolve again, since another thread may have already
- // done the initialization
- if (triedResolve) {
- // another thread did initialize the security function pointers,
- // so we shouldn't do it again.
- return;
- }
-#endif
-
- triedResolve = true;
- HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32");
- if (advapiHnd) {
- ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
- ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
- ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
- ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
- }
- if (ptrBuildTrusteeWithSidW) {
+ {
+ {
// Create TRUSTEE for current user
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
- initSidCleanup();
if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) {
DWORD retsize = 0;
// GetTokenInformation requires a buffer big enough for the TOKEN_USER struct and
@@ -247,61 +203,69 @@ static void resolveLibs()
DWORD sidLen = ::GetLengthSid(tokenSid);
currentUserSID = reinterpret_cast<PSID>(malloc(sidLen));
if (::CopySid(sidLen, currentUserSID, tokenSid))
- ptrBuildTrusteeWithSidW(&currentUserTrusteeW, currentUserSID);
+ BuildTrusteeWithSid(&currentUserTrusteeW, currentUserSID);
}
free(tokenBuffer);
}
::CloseHandle(token);
}
- typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
- PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
- if (ptrAllocateAndInitializeSid) {
+ {
// Create TRUSTEE for Everyone (World)
SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
- if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
- ptrBuildTrusteeWithSidW(&worldTrusteeW, worldSID);
+ if (AllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldSID))
+ BuildTrusteeWithSid(&worldTrusteeW, worldSID);
}
}
- HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv");
- if (userenvHnd)
- ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
}
}
+
+Q_GLOBAL_STATIC(GlobalSid, initGlobalSid)
+
+QT_END_NAMESPACE
+
+} // anonymous namespace
#endif // QT_CONFIG(fslibs)
-typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
-static PtrNetShareEnum ptrNetShareEnum = 0;
-typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID);
-static PtrNetApiBufferFree ptrNetApiBufferFree = 0;
-typedef struct _SHARE_INFO_1 {
- LPWSTR shi1_netname;
- DWORD shi1_type;
- LPWSTR shi1_remark;
-} SHARE_INFO_1;
+QT_BEGIN_NAMESPACE
+Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
-static bool resolveUNCLibs()
+static inline bool toFileTime(const QDateTime &date, FILETIME *fileTime)
{
- static bool triedResolve = false;
- if (!triedResolve) {
-#ifndef QT_NO_THREAD
- QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
- if (triedResolve) {
- return ptrNetShareEnum && ptrNetApiBufferFree;
- }
-#endif
- triedResolve = true;
-#if !defined(Q_OS_WINRT)
- HINSTANCE hLib = QSystemLibrary::load(L"Netapi32");
- if (hLib) {
- ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum");
- if (ptrNetShareEnum)
- ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree");
- }
-#endif // !Q_OS_WINRT
+ SYSTEMTIME sTime;
+ if (date.timeSpec() == Qt::LocalTime) {
+ SYSTEMTIME lTime;
+ const QDate d = date.date();
+ const QTime t = date.time();
+
+ lTime.wYear = d.year();
+ lTime.wMonth = d.month();
+ lTime.wDay = d.day();
+ lTime.wHour = t.hour();
+ lTime.wMinute = t.minute();
+ lTime.wSecond = t.second();
+ lTime.wMilliseconds = t.msec();
+ lTime.wDayOfWeek = d.dayOfWeek() % 7;
+
+ if (!::TzSpecificLocalTimeToSystemTime(0, &lTime, &sTime))
+ return false;
+ } else {
+ QDateTime utcDate = date.toUTC();
+ const QDate d = utcDate.date();
+ const QTime t = utcDate.time();
+
+ sTime.wYear = d.year();
+ sTime.wMonth = d.month();
+ sTime.wDay = d.day();
+ sTime.wHour = t.hour();
+ sTime.wMinute = t.minute();
+ sTime.wSecond = t.second();
+ sTime.wMilliseconds = t.msec();
+ sTime.wDayOfWeek = d.dayOfWeek() % 7;
}
- return ptrNetShareEnum && ptrNetApiBufferFree;
+
+ return ::SystemTimeToFileTime(&sTime, fileTime);
}
static QString readSymLink(const QFileSystemEntry &link)
@@ -339,7 +303,7 @@ static QString readSymLink(const QFileSystemEntry &link)
CloseHandle(handle);
#if QT_CONFIG(fslibs)
- resolveLibs();
+ initGlobalSid();
QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive);
if (matchVolName.indexIn(result) == 0) {
DWORD len;
@@ -436,32 +400,35 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
{
- if (resolveUNCLibs()) {
- SHARE_INFO_1 *BufPtr, *p;
- DWORD res;
- DWORD er = 0, tr = 0, resume = 0, i;
- do {
- res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
- if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
- p = BufPtr;
- for (i = 1; i <= er; ++i) {
- if (list && p->shi1_type == 0)
- list->append(QString::fromWCharArray(p->shi1_netname));
- p++;
- }
+ DWORD res = ERROR_NOT_SUPPORTED;
+#ifndef Q_OS_WINRT
+ SHARE_INFO_1 *BufPtr, *p;
+ DWORD er = 0, tr = 0, resume = 0, i;
+ do {
+ res = NetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
+ if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
+ p = BufPtr;
+ for (i = 1; i <= er; ++i) {
+ if (list && p->shi1_type == 0)
+ list->append(QString::fromWCharArray(p->shi1_netname));
+ p++;
}
- ptrNetApiBufferFree(BufPtr);
- } while (res == ERROR_MORE_DATA);
- return res == ERROR_SUCCESS;
- }
- return false;
+ }
+ NetApiBufferFree(BufPtr);
+ } while (res == ERROR_MORE_DATA);
+#else
+ Q_UNUSED(server);
+ Q_UNUSED(list);
+#endif
+ return res == ERROR_SUCCESS;
}
void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data)
{
data.size_ = 0;
data.fileAttribute_ = 0;
- data.creationTime_ = FILETIME();
+ data.birthTime_ = FILETIME();
+ data.changeTime_ = FILETIME();
data.lastAccessTime_ = FILETIME();
data.lastWriteTime_ = FILETIME();
}
@@ -650,36 +617,72 @@ QByteArray QFileSystemEngine::id(HANDLE fHandle)
}
//static
+bool QFileSystemEngine::setFileTime(HANDLE fHandle, const QDateTime &newDate,
+ QAbstractFileEngine::FileTime time, QSystemError &error)
+{
+ FILETIME fTime;
+ FILETIME *pLastWrite = NULL;
+ FILETIME *pLastAccess = NULL;
+ FILETIME *pCreationTime = NULL;
+
+ switch (time) {
+ case QAbstractFileEngine::ModificationTime:
+ pLastWrite = &fTime;
+ break;
+
+ case QAbstractFileEngine::AccessTime:
+ pLastAccess = &fTime;
+ break;
+
+ case QAbstractFileEngine::BirthTime:
+ pCreationTime = &fTime;
+ break;
+
+ default:
+ error = QSystemError(ERROR_INVALID_PARAMETER, QSystemError::NativeError);
+ return false;
+ }
+
+ if (!toFileTime(newDate, &fTime))
+ return false;
+
+ if (!::SetFileTime(fHandle, pCreationTime, pLastAccess, pLastWrite)) {
+ error = QSystemError(::GetLastError(), QSystemError::NativeError);
+ return false;
+ }
+ return true;
+}
+
QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own)
{
QString name;
#if QT_CONFIG(fslibs)
extern int qt_ntfs_permission_lookup;
- if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
- resolveLibs();
- if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
+ if (qt_ntfs_permission_lookup > 0) {
+ initGlobalSid();
+ {
PSID pOwner = 0;
PSECURITY_DESCRIPTOR pSD;
- if (ptrGetNamedSecurityInfoW((wchar_t*)entry.nativeFilePath().utf16(), SE_FILE_OBJECT,
- own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
- own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0,
- 0, 0, &pSD) == ERROR_SUCCESS) {
+ if (GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(entry.nativeFilePath().utf16()), SE_FILE_OBJECT,
+ own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
+ own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0,
+ 0, 0, &pSD) == ERROR_SUCCESS) {
DWORD lowner = 64;
DWORD ldomain = 64;
QVarLengthArray<wchar_t, 64> owner(lowner);
QVarLengthArray<wchar_t, 64> domain(ldomain);
SID_NAME_USE use = SidTypeUnknown;
// First call, to determine size of the strings (with '\0').
- if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
- (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
+ if (!LookupAccountSid(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
+ domain.data(), &ldomain, &use)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (lowner > (DWORD)owner.size())
owner.resize(lowner);
if (ldomain > (DWORD)domain.size())
domain.resize(ldomain);
// Second call, try on resized buf-s
- if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner,
- (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) {
+ if (!LookupAccountSid(NULL, pOwner, owner.data(), &lowner,
+ domain.data(), &ldomain, &use)) {
lowner = 0;
}
} else {
@@ -704,9 +707,9 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
QFileSystemMetaData::MetaDataFlags what)
{
#if QT_CONFIG(fslibs)
- if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
- resolveLibs();
- if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) {
+ if (qt_ntfs_permission_lookup > 0) {
+ initGlobalSid();
+ {
enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
QString fname = entry.nativeFilePath();
@@ -714,15 +717,15 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
PSID pGroup = 0;
PACL pDacl;
PSECURITY_DESCRIPTOR pSD;
- DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
- OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
- &pOwner, &pGroup, &pDacl, 0, &pSD);
+ DWORD res = GetNamedSecurityInfo(reinterpret_cast<const wchar_t*>(fname.utf16()), SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &pOwner, &pGroup, &pDacl, 0, &pSD);
if(res == ERROR_SUCCESS) {
ACCESS_MASK access_mask;
TRUSTEE_W trustee;
if (what & QFileSystemMetaData::UserPermissions) { // user
data.knownFlagsMask |= QFileSystemMetaData::UserPermissions;
- if(ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
+ if (GetEffectiveRightsFromAcl(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1;
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::UserReadPermission;
@@ -733,8 +736,8 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::OwnerPermissions) { // owner
data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions;
- ptrBuildTrusteeWithSidW(&trustee, pOwner);
- if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ BuildTrusteeWithSid(&trustee, pOwner);
+ if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1;
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::OwnerReadPermission;
@@ -745,8 +748,8 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::GroupPermissions) { // group
data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions;
- ptrBuildTrusteeWithSidW(&trustee, pGroup);
- if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ BuildTrusteeWithSid(&trustee, pGroup);
+ if (GetEffectiveRightsFromAcl(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1;
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::GroupReadPermission;
@@ -757,7 +760,7 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst
}
if (what & QFileSystemMetaData::OtherPermissions) { // other (world)
data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions;
- if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
+ if (GetEffectiveRightsFromAcl(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS)
access_mask = (ACCESS_MASK)-1; // ###
if(access_mask & ReadMask)
data.entryFlags |= QFileSystemMetaData::OtherReadPermission;
@@ -910,8 +913,10 @@ bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data,
FILE_BASIC_INFO fileBasicInfo;
if (GetFileInformationByHandleEx(fHandle, FileBasicInfo, &fileBasicInfo, sizeof(fileBasicInfo))) {
data.fillFromFileAttribute(fileBasicInfo.FileAttributes);
- data.creationTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
- data.creationTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
+ data.birthTime_.dwHighDateTime = fileBasicInfo.CreationTime.HighPart;
+ data.birthTime_.dwLowDateTime = fileBasicInfo.CreationTime.LowPart;
+ data.changeTime_.dwHighDateTime = fileBasicInfo.ChangeTime.HighPart;
+ data.changeTime_.dwLowDateTime = fileBasicInfo.ChangeTime.LowPart;
data.lastAccessTime_.dwHighDateTime = fileBasicInfo.LastAccessTime.HighPart;
data.lastAccessTime_.dwLowDateTime = fileBasicInfo.LastAccessTime.LowPart;
data.lastWriteTime_.dwHighDateTime = fileBasicInfo.LastWriteTime.HighPart;
@@ -1168,19 +1173,19 @@ QString QFileSystemEngine::homePath()
{
QString ret;
#if QT_CONFIG(fslibs)
- resolveLibs();
- if (ptrGetUserProfileDirectoryW) {
+ initGlobalSid();
+ {
HANDLE hnd = ::GetCurrentProcess();
HANDLE token = 0;
BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token);
if (ok) {
DWORD dwBufferSize = 0;
// First call, to determine size of the strings (with '\0').
- ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize);
+ ok = GetUserProfileDirectory(token, NULL, &dwBufferSize);
if (!ok && dwBufferSize != 0) { // We got the required buffer size
wchar_t *userDirectory = new wchar_t[dwBufferSize];
// Second call, now we can fill the allocated buffer.
- ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
+ ok = GetUserProfileDirectory(token, userDirectory, &dwBufferSize);
if (ok)
ret = QString::fromWCharArray(userDirectory);
delete [] userDirectory;
@@ -1326,6 +1331,17 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
}
//static
+bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error)
+{
+ bool ret = ::MoveFileEx(reinterpret_cast<const wchar_t *>(source.nativeFilePath().utf16()),
+ reinterpret_cast<const wchar_t *>(target.nativeFilePath().utf16()),
+ MOVEFILE_REPLACE_EXISTING) != 0;
+ if (!ret)
+ error = QSystemError(::GetLastError(), QSystemError::NativeError);
+ return ret;
+}
+
+//static
bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error)
{
bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0;
@@ -1357,6 +1373,9 @@ bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Per
static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
{
+ if (time->dwHighDateTime == 0 && time->dwLowDateTime == 0)
+ return QDateTime();
+
SYSTEMTIME sTime;
FileTimeToSystemTime(time, &sTime);
return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
@@ -1364,9 +1383,13 @@ static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
Qt::UTC);
}
-QDateTime QFileSystemMetaData::creationTime() const
+QDateTime QFileSystemMetaData::birthTime() const
+{
+ return fileTimeToQDateTime(&birthTime_);
+}
+QDateTime QFileSystemMetaData::metadataChangeTime() const
{
- return fileTimeToQDateTime(&creationTime_);
+ return fileTimeToQDateTime(&changeTime_);
}
QDateTime QFileSystemMetaData::modificationTime() const
{
diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp
index cbff17d0f1..3ff90bd0a3 100644
--- a/src/corelib/io/qfilesystementry.cpp
+++ b/src/corelib/io/qfilesystementry.cpp
@@ -331,11 +331,6 @@ bool QFileSystemEntry::isRoot() const
return isRootPath(m_filePath);
}
-bool QFileSystemEntry::isEmpty() const
-{
- return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
-}
-
// private methods
void QFileSystemEntry::findLastSeparator() const
diff --git a/src/corelib/io/qfilesystementry_p.h b/src/corelib/io/qfilesystementry_p.h
index 700696d09e..251eca553a 100644
--- a/src/corelib/io/qfilesystementry_p.h
+++ b/src/corelib/io/qfilesystementry_p.h
@@ -98,7 +98,10 @@ public:
#endif
bool isRoot() const;
- bool isEmpty() const;
+ bool isEmpty() const
+ {
+ return m_filePath.isEmpty() && m_nativeFilePath.isEmpty();
+ }
void clear()
{
*this = QFileSystemEntry();
diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h
index b09223d656..55e44d52c7 100644
--- a/src/corelib/io/qfilesystemmetadata_p.h
+++ b/src/corelib/io/qfilesystemmetadata_p.h
@@ -120,16 +120,22 @@ public:
// Attributes
HiddenAttribute = 0x00100000,
SizeAttribute = 0x00200000, // Note: overlaps with QAbstractFileEngine::LocalDiskFlag
- ExistsAttribute = 0x00400000,
+ ExistsAttribute = 0x00400000, // For historical reasons, indicates existence of data, not the file
+#if defined(Q_OS_WIN)
+ WasDeletedAttribute = 0x0,
+#else
+ WasDeletedAttribute = 0x40000000, // Indicates the file was deleted
+#endif
- Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute,
+ Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute | WasDeletedAttribute,
- // Times
- CreationTime = 0x01000000, // Note: overlaps with QAbstractFileEngine::Refresh
+ // Times - if we know one of them, we know them all
+ AccessTime = 0x02000000,
+ BirthTime = 0x02000000,
+ MetadataChangeTime = 0x02000000,
ModificationTime = 0x02000000,
- AccessTime = 0x04000000,
- Times = CreationTime | ModificationTime | AccessTime,
+ Times = AccessTime | BirthTime | MetadataChangeTime | ModificationTime,
// Owner IDs
UserId = 0x10000000,
@@ -144,6 +150,7 @@ public:
| QFileSystemMetaData::DirectoryType
| QFileSystemMetaData::SequentialType
| QFileSystemMetaData::SizeAttribute
+ | QFileSystemMetaData::WasDeletedAttribute
| QFileSystemMetaData::Times
| QFileSystemMetaData::OwnerIds,
@@ -191,6 +198,7 @@ public:
bool isLegacyLink() const { return (entryFlags & LegacyLinkType); }
bool isSequential() const { return (entryFlags & SequentialType); }
bool isHidden() const { return (entryFlags & HiddenAttribute); }
+ bool wasDeleted() const { return (entryFlags & WasDeletedAttribute); }
#if defined(Q_OS_WIN)
bool isLnkFile() const { return (entryFlags & WinLnkType); }
#else
@@ -201,9 +209,10 @@ public:
QFile::Permissions permissions() const { return QFile::Permissions(Permissions & entryFlags); }
- QDateTime creationTime() const;
- QDateTime modificationTime() const;
QDateTime accessTime() const;
+ QDateTime birthTime() const;
+ QDateTime metadataChangeTime() const;
+ QDateTime modificationTime() const;
QDateTime fileTime(QAbstractFileEngine::FileTime time) const;
uint userId() const;
@@ -211,6 +220,7 @@ public:
uint ownerId(QAbstractFileEngine::FileOwner owner) const;
#ifdef Q_OS_UNIX
+ void fillFromStatxBuf(const struct statx &statBuffer);
void fillFromStatBuf(const QT_STATBUF &statBuffer);
void fillFromDirEnt(const QT_DIRENT &statBuffer);
#endif
@@ -233,14 +243,16 @@ private:
// Platform-specific data goes here:
#if defined(Q_OS_WIN)
DWORD fileAttribute_;
- FILETIME creationTime_;
+ FILETIME birthTime_;
+ FILETIME changeTime_;
FILETIME lastAccessTime_;
FILETIME lastWriteTime_;
#else
// msec precision
- qint64 creationTime_;
- qint64 modificationTime_;
qint64 accessTime_;
+ qint64 birthTime_;
+ qint64 metadataChangeTime_;
+ qint64 modificationTime_;
uint userId_;
uint groupId_;
@@ -268,8 +280,11 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
case QAbstractFileEngine::AccessTime:
return accessTime();
- case QAbstractFileEngine::CreationTime:
- return creationTime();
+ case QAbstractFileEngine::BirthTime:
+ return birthTime();
+
+ case QAbstractFileEngine::MetadataChangeTime:
+ return metadataChangeTime();
}
return QDateTime();
@@ -277,9 +292,14 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
#endif
#if defined(Q_OS_UNIX)
-inline QDateTime QFileSystemMetaData::creationTime() const { return QDateTime::fromMSecsSinceEpoch(creationTime_); }
-inline QDateTime QFileSystemMetaData::modificationTime() const { return QDateTime::fromMSecsSinceEpoch(modificationTime_); }
-inline QDateTime QFileSystemMetaData::accessTime() const { return QDateTime::fromMSecsSinceEpoch(accessTime_); }
+inline QDateTime QFileSystemMetaData::birthTime() const
+{ return birthTime_ ? QDateTime::fromMSecsSinceEpoch(birthTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::metadataChangeTime() const
+{ return metadataChangeTime_ ? QDateTime::fromMSecsSinceEpoch(metadataChangeTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::modificationTime() const
+{ return modificationTime_ ? QDateTime::fromMSecsSinceEpoch(modificationTime_) : QDateTime(); }
+inline QDateTime QFileSystemMetaData::accessTime() const
+{ return accessTime_ ? QDateTime::fromMSecsSinceEpoch(accessTime_) : QDateTime(); }
inline uint QFileSystemMetaData::userId() const { return userId_; }
inline uint QFileSystemMetaData::groupId() const { return groupId_; }
@@ -318,9 +338,9 @@ inline void QFileSystemMetaData::fillFromFileAttribute(DWORD fileAttribute,bool
inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType, bool isDriveRoot)
{
fillFromFileAttribute(findData.dwFileAttributes, isDriveRoot);
- creationTime_ = findData.ftCreationTime;
+ birthTime_ = findData.ftCreationTime;
lastAccessTime_ = findData.ftLastAccessTime;
- lastWriteTime_ = findData.ftLastWriteTime;
+ changeTime_ = lastWriteTime_ = findData.ftLastWriteTime;
if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) {
size_ = 0;
} else {
@@ -343,9 +363,9 @@ inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, boo
inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo)
{
fillFromFileAttribute(fileInfo.dwFileAttributes);
- creationTime_ = fileInfo.ftCreationTime;
+ birthTime_ = fileInfo.ftCreationTime;
lastAccessTime_ = fileInfo.ftLastAccessTime;
- lastWriteTime_ = fileInfo.ftLastWriteTime;
+ changeTime_ = lastWriteTime_ = fileInfo.ftLastWriteTime;
if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) {
size_ = 0;
} else {
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index 88e8baedca..b7a5440224 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -195,6 +195,9 @@ void QFSFileEngine::setFileName(const QString &file)
*/
bool QFSFileEngine::open(QIODevice::OpenMode openMode)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_D(QFSFileEngine);
if (d->fileEntry.isEmpty()) {
qWarning("QFSFileEngine::open: No file name specified");
@@ -230,6 +233,9 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_D(QFSFileEngine);
// Append implies WriteOnly.
@@ -255,6 +261,9 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHand
*/
bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
{
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
+
Q_Q(QFSFileEngine);
this->fh = fh;
fd = -1;
@@ -516,6 +525,25 @@ bool QFSFileEngine::seek(qint64 pos)
}
/*!
+ \reimp
+*/
+QDateTime QFSFileEngine::fileTime(FileTime time) const
+{
+ Q_D(const QFSFileEngine);
+
+ if (time == AccessTime) {
+ // always refresh for the access time
+ d->metaData.clearFlags(QFileSystemMetaData::AccessTime);
+ }
+
+ if (d->doStat(QFileSystemMetaData::Times))
+ return d->metaData.fileTime(time);
+
+ return QDateTime();
+}
+
+
+/*!
\internal
*/
bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
@@ -699,6 +727,7 @@ qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
qint64 QFSFileEngine::write(const char *data, qint64 len)
{
Q_D(QFSFileEngine);
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
// On Windows' stdlib implementation, the results of calling fread and
// fwrite are undefined if not called either in sequence, or if preceded
@@ -860,9 +889,9 @@ bool QFSFileEngine::supportsExtension(Extension extension) const
/*! \fn bool QFSFileEngine::copy(const QString &copyName)
- For windows, copy the file to file \a copyName.
+ For Windows or Apple platforms, copy the file to file \a copyName.
- Not implemented for Unix.
+ Not implemented for other Unix platforms.
*/
/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
@@ -890,7 +919,7 @@ bool QFSFileEngine::supportsExtension(Extension extension) const
\reimp
*/
-/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const
+/*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
\reimp
*/
diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h
index ab9ad3aa6b..1f34dfc2be 100644
--- a/src/corelib/io/qfsfileengine_p.h
+++ b/src/corelib/io/qfsfileengine_p.h
@@ -97,6 +97,7 @@ public:
QString fileName(FileName file) const Q_DECL_OVERRIDE;
uint ownerId(FileOwner) const Q_DECL_OVERRIDE;
QString owner(FileOwner) const Q_DECL_OVERRIDE;
+ bool setFileTime(const QDateTime &newDate, FileTime time) Q_DECL_OVERRIDE;
QDateTime fileTime(FileTime time) const Q_DECL_OVERRIDE;
void setFileName(const QString &file) Q_DECL_OVERRIDE;
int handle() const Q_DECL_OVERRIDE;
@@ -109,6 +110,7 @@ public:
qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 readLine(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE;
+ bool cloneTo(QAbstractFileEngine *target) override;
bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0) Q_DECL_OVERRIDE;
bool supportsExtension(Extension extension) const Q_DECL_OVERRIDE;
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
index c613368a91..c040d67862 100644
--- a/src/corelib/io/qfsfileengine_unix.cpp
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -65,46 +65,6 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- Returns the stdlib open string corresponding to a QIODevice::OpenMode.
-*/
-static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry,
- QFileSystemMetaData &metaData)
-{
- QByteArray mode;
- if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
- mode = "rb";
- if (flags & QIODevice::WriteOnly) {
- metaData.clearFlags(QFileSystemMetaData::FileType);
- if (!fileEntry.isEmpty()
- && QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType)
- && metaData.isFile()) {
- mode += '+';
- } else {
- mode = "wb+";
- }
- }
- } else if (flags & QIODevice::WriteOnly) {
- mode = "wb";
- if (flags & QIODevice::ReadOnly)
- mode += '+';
- }
- if (flags & QIODevice::Append) {
- mode = "ab";
- if (flags & QIODevice::ReadOnly)
- mode += '+';
- }
-
-#if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0207
- // must be glibc >= 2.7
- mode += 'e';
-#endif
-
- return mode;
-}
-
-/*!
- \internal
-
Returns the stdio open flags corresponding to a QIODevice::OpenMode.
*/
static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
@@ -130,17 +90,6 @@ static inline int openModeToOpenFlags(QIODevice::OpenMode mode)
return oflags;
}
-/*!
- \internal
-
- Sets the file descriptor to close on exec. That is, the file
- descriptor is not inherited by child processes.
-*/
-static inline bool setCloseOnExec(int fd)
-{
- return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
-}
-
static inline QString msgOpenDirectory()
{
const char message[] = QT_TRANSLATE_NOOP("QIODevice", "file to open is a directory");
@@ -158,6 +107,8 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
{
Q_Q(QFSFileEngine);
+ Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
+ "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
if (openMode & QIODevice::Unbuffered) {
int flags = openModeToOpenFlags(openMode);
@@ -199,49 +150,6 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
}
fh = 0;
- } else {
- QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData);
-
- // Try to open the file in buffered mode.
- do {
- fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData());
- } while (!fh && errno == EINTR);
-
- // On failure, return and report the error.
- if (!fh) {
- q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
- qt_error_string(int(errno)));
- return false;
- }
-
- if (!(openMode & QIODevice::WriteOnly)) {
- // we don't need this check if we tried to open for writing because then
- // we had received EISDIR anyway.
- if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData)
- && metaData.isDirectory()) {
- q->setError(QFile::OpenError, msgOpenDirectory());
- fclose(fh);
- return false;
- }
- }
-
- setCloseOnExec(fileno(fh)); // ignore failure
-
- // Seek to the end when in Append mode.
- if (openMode & QIODevice::Append) {
- int ret;
- do {
- ret = QT_FSEEK(fh, 0, SEEK_END);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
- qt_error_string(int(errno)));
- return false;
- }
- }
-
- fd = -1;
}
closeFileHandle = true;
@@ -416,8 +324,14 @@ bool QFSFileEngine::copy(const QString &newName)
bool QFSFileEngine::renameOverwrite(const QString &newName)
{
- // On Unix, rename() overwrites.
- return rename(newName);
+ Q_D(QFSFileEngine);
+ QSystemError error;
+ bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error);
+
+ if (!ret)
+ setError(QFile::RenameError, error.toString());
+
+ return ret;
}
bool QFSFileEngine::rename(const QString &newName)
@@ -549,11 +463,14 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
| QFileSystemMetaData::LinkType
| QFileSystemMetaData::FileType
| QFileSystemMetaData::DirectoryType
- | QFileSystemMetaData::BundleType;
+ | QFileSystemMetaData::BundleType
+ | QFileSystemMetaData::WasDeletedAttribute;
if (type & FlagsMask)
queryFlags |= QFileSystemMetaData::HiddenAttribute
| QFileSystemMetaData::ExistsAttribute;
+ else if (type & ExistsFlag)
+ queryFlags |= QFileSystemMetaData::WasDeletedAttribute;
queryFlags |= QFileSystemMetaData::LinkType;
@@ -585,7 +502,8 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
}
if (type & FlagsMask) {
- if (exists)
+ // the inode existing does not mean the file exists
+ if (!d->metaData.wasDeleted())
ret |= ExistsFlag;
if (d->fileEntry.isRoot())
ret |= RootFlag;
@@ -689,14 +607,23 @@ bool QFSFileEngine::setSize(qint64 size)
return ret;
}
-QDateTime QFSFileEngine::fileTime(FileTime time) const
+bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
{
- Q_D(const QFSFileEngine);
+ Q_D(QFSFileEngine);
+
+ if (d->openMode == QIODevice::NotOpen) {
+ setError(QFile::PermissionsError, qt_error_string(EACCES));
+ return false;
+ }
- if (d->doStat(QFileSystemMetaData::Times))
- return d->metaData.fileTime(time);
+ QSystemError error;
+ if (!QFileSystemEngine::setFileTime(d->nativeHandle(), newDate, time, error)) {
+ setError(QFile::PermissionsError, error.toString());
+ return false;
+ }
- return QDateTime();
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
+ return true;
}
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
@@ -715,7 +642,6 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
#endif
Q_Q(QFSFileEngine);
- Q_UNUSED(flags);
if (openMode == QIODevice::NotOpen) {
q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
return 0;
@@ -806,6 +732,20 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
#endif
}
+/*!
+ \reimp
+*/
+bool QFSFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ Q_D(QFSFileEngine);
+ if ((target->fileFlags(LocalDiskFlag) & LocalDiskFlag) == 0)
+ return false;
+
+ int srcfd = d->nativeHandle();
+ int dstfd = target->handle();
+ return QFileSystemEngine::cloneFile(srcfd, dstfd, d->metaData);
+}
+
QT_END_NAMESPACE
#endif // QT_NO_FSFILEENGINE
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
index c99a25f30d..759effe632 100644
--- a/src/corelib/io/qfsfileengine_win.cpp
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -474,11 +474,10 @@ bool QFSFileEngine::rename(const QString &newName)
bool QFSFileEngine::renameOverwrite(const QString &newName)
{
Q_D(QFSFileEngine);
- bool ret = ::MoveFileEx((wchar_t*)d->fileEntry.nativeFilePath().utf16(),
- (wchar_t*)QFileSystemEntry(newName).nativeFilePath().utf16(),
- MOVEFILE_REPLACE_EXISTING) != 0;
+ QSystemError error;
+ bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error);
if (!ret)
- setError(QFile::RenameError, QSystemError(::GetLastError(), QSystemError::NativeError).toString());
+ setError(QFile::RenameError, error.toString());
return ret;
}
@@ -694,6 +693,8 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::Fil
}
if (type & FlagsMask) {
if (d->metaData.exists()) {
+ // if we succeeded in querying, then the file exists: a file on
+ // Windows cannot be deleted if we have an open handle to it
ret |= ExistsFlag;
if (d->fileEntry.isRoot())
ret |= RootFlag;
@@ -854,15 +855,41 @@ bool QFSFileEngine::setSize(qint64 size)
return false;
}
-
-QDateTime QFSFileEngine::fileTime(FileTime time) const
+bool QFSFileEngine::setFileTime(const QDateTime &newDate, FileTime time)
{
- Q_D(const QFSFileEngine);
+ Q_D(QFSFileEngine);
+
+ if (d->openMode == QFile::NotOpen) {
+ setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
+ return false;
+ }
+
+ if (!newDate.isValid() || time == QAbstractFileEngine::MetadataChangeTime) {
+ setError(QFile::UnspecifiedError, qt_error_string(ERROR_INVALID_PARAMETER));
+ return false;
+ }
+
+ HANDLE handle = d->fileHandle;
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (d->fh)
+ handle = reinterpret_cast<HANDLE>(::_get_osfhandle(QT_FILENO(d->fh)));
+ else if (d->fd != -1)
+ handle = reinterpret_cast<HANDLE>(::_get_osfhandle(d->fd));
+ }
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ setError(QFile::PermissionsError, qt_error_string(ERROR_ACCESS_DENIED));
+ return false;
+ }
- if (d->doStat(QFileSystemMetaData::Times))
- return d->metaData.fileTime(time);
+ QSystemError error;
+ if (!QFileSystemEngine::setFileTime(handle, newDate, time, error)) {
+ setError(QFile::PermissionsError, error.toString());
+ return false;
+ }
- return QDateTime();
+ d->metaData.clearFlags(QFileSystemMetaData::Times);
+ return true;
}
uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
@@ -1002,4 +1029,15 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr)
return true;
}
+/*!
+ \reimp
+*/
+bool QFSFileEngine::cloneTo(QAbstractFileEngine *target)
+{
+ // There's some Windows Server 2016 API, but we won't
+ // bother with it.
+ Q_UNUSED(target);
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 80122eac5e..0a3e83206b 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -775,6 +775,7 @@ bool QIODevice::open(OpenMode mode)
d->writeBuffers.clear();
d->setReadChannelCount(isReadable() ? 1 : 0);
d->setWriteChannelCount(isWritable() ? 1 : 0);
+ d->errorString.clear();
#if defined QIODEVICE_DEBUG
printf("%p QIODevice::open(0x%x)\n", this, quint32(mode));
#endif
@@ -801,7 +802,6 @@ void QIODevice::close()
emit aboutToClose();
#endif
d->openMode = NotOpen;
- d->errorString.clear();
d->pos = 0;
d->transactionStarted = false;
d->transactionPos = 0;
@@ -1879,6 +1879,128 @@ QByteArray QIODevice::peek(qint64 maxSize)
}
/*!
+ \since 5.11
+
+ Skips up to \a maxSize bytes from the device. Returns the number of bytes
+ actually skipped, or -1 on error.
+
+ This function does not wait and only discards the data that is already
+ available for reading.
+
+ If the device is opened in text mode, end-of-line terminators are
+ translated to '\n' symbols and count as a single byte identically to the
+ read() and peek() behavior.
+
+ This function works for all devices, including sequential ones that cannot
+ seek(). It is optimized to skip unwanted data after a peek() call.
+
+ For random-access devices, skip() can be used to seek forward from the
+ current position. Negative \a maxSize values are not allowed.
+
+ \sa peek(), seek(), read()
+*/
+qint64 QIODevice::skip(qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_MAXLEN(skip, qint64(-1));
+ CHECK_READABLE(skip, qint64(-1));
+
+ const bool sequential = d->isSequential();
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::skip(%lld), d->pos = %lld, d->buffer.size() = %lld\n",
+ this, maxSize, d->pos, d->buffer.size());
+#endif
+
+ if ((sequential && d->transactionStarted) || (d->openMode & QIODevice::Text) != 0)
+ return d->skipByReading(maxSize);
+
+ // First, skip over any data in the internal buffer.
+ qint64 skippedSoFar = 0;
+ if (!d->buffer.isEmpty()) {
+ skippedSoFar = d->buffer.skip(maxSize);
+#if defined QIODEVICE_DEBUG
+ printf("%p \tskipping %lld bytes in buffer\n", this, skippedSoFar);
+#endif
+ if (!sequential)
+ d->pos += skippedSoFar;
+ if (d->buffer.isEmpty())
+ readData(nullptr, 0);
+ if (skippedSoFar == maxSize)
+ return skippedSoFar;
+
+ maxSize -= skippedSoFar;
+ }
+
+ // Try to seek on random-access device. At this point,
+ // the internal read buffer is empty.
+ if (!sequential) {
+ const qint64 bytesToSkip = qMin(size() - d->pos, maxSize);
+
+ // If the size is unknown or file position is at the end,
+ // fall back to reading below.
+ if (bytesToSkip > 0) {
+ if (!seek(d->pos + bytesToSkip))
+ return skippedSoFar ? skippedSoFar : Q_INT64_C(-1);
+ if (bytesToSkip == maxSize)
+ return skippedSoFar + bytesToSkip;
+
+ skippedSoFar += bytesToSkip;
+ maxSize -= bytesToSkip;
+ }
+ }
+
+ const qint64 skipResult = d->skip(maxSize);
+ if (skippedSoFar == 0)
+ return skipResult;
+
+ if (skipResult == -1)
+ return skippedSoFar;
+
+ return skippedSoFar + skipResult;
+}
+
+/*!
+ \internal
+*/
+qint64 QIODevicePrivate::skipByReading(qint64 maxSize)
+{
+ qint64 readSoFar = 0;
+ do {
+ char dummy[4096];
+ const qint64 readBytes = qMin<qint64>(maxSize, sizeof(dummy));
+ const qint64 readResult = read(dummy, readBytes);
+
+ // Do not try again, if we got less data.
+ if (readResult != readBytes) {
+ if (readSoFar == 0)
+ return readResult;
+
+ if (readResult == -1)
+ return readSoFar;
+
+ return readSoFar + readResult;
+ }
+
+ readSoFar += readResult;
+ maxSize -= readResult;
+ } while (maxSize > 0);
+
+ return readSoFar;
+}
+
+/*!
+ \internal
+*/
+qint64 QIODevicePrivate::skip(qint64 maxSize)
+{
+ // Base implementation discards the data by reading into the dummy buffer.
+ // It's slow, but this works for all types of devices. Subclasses can
+ // reimplement this function to improve on that.
+ return skipByReading(maxSize);
+}
+
+/*!
Blocks until new data is available for reading and the readyRead()
signal has been emitted, or until \a msecs milliseconds have
passed. If msecs is -1, this function will not time out.
diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h
index 162480d22f..e64a4d0bb1 100644
--- a/src/corelib/io/qiodevice.h
+++ b/src/corelib/io/qiodevice.h
@@ -136,6 +136,7 @@ public:
qint64 peek(char *data, qint64 maxlen);
QByteArray peek(qint64 maxlen);
+ qint64 skip(qint64 maxSize);
virtual bool waitForReadyRead(int msecs);
virtual bool waitForBytesWritten(int msecs);
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
index 71a326dd53..de2aa1597e 100644
--- a/src/corelib/io/qiodevice_p.h
+++ b/src/corelib/io/qiodevice_p.h
@@ -174,6 +174,9 @@ public:
qint64 read(char *data, qint64 maxSize, bool peeking = false);
virtual qint64 peek(char *data, qint64 maxSize);
virtual QByteArray peek(qint64 maxSize);
+ qint64 skipByReading(qint64 maxSize);
+ // ### Qt6: consider replacing with a protected virtual QIODevice::skipData().
+ virtual qint64 skip(qint64 maxSize);
#ifdef QT_NO_QOBJECT
QIODevice *q_ptr;
diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp
index 48317d07e0..129cf01b63 100644
--- a/src/corelib/io/qlockfile.cpp
+++ b/src/corelib/io/qlockfile.cpp
@@ -2,6 +2,7 @@
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,12 +43,34 @@
#include "qlockfile_p.h"
#include <QtCore/qthread.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qfileinfo.h>
QT_BEGIN_NAMESPACE
+namespace {
+struct LockFileInfo
+{
+ qint64 pid;
+ QString appname;
+ QString hostname;
+};
+}
+
+static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info);
+
+static QString machineName()
+{
+#ifdef Q_OS_WIN
+ // we don't use QSysInfo because it tries to do name resolution
+ return qEnvironmentVariable("COMPUTERNAME");
+#else
+ return QSysInfo::machineHostName();
+#endif
+}
+
/*!
\class QLockFile
\inmodule QtCore
@@ -291,10 +314,27 @@ bool QLockFile::tryLock(int timeout)
bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
{
Q_D(const QLockFile);
- return d->getLockInfo(pid, hostname, appname);
+ LockFileInfo info;
+ if (!getLockInfo_helper(d->fileName, &info))
+ return false;
+ if (pid)
+ *pid = info.pid;
+ if (hostname)
+ *hostname = info.hostname;
+ if (appname)
+ *appname = info.appname;
+ return true;
}
-bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appname) const
+QByteArray QLockFilePrivate::lockFileContents() const
+{
+ // Use operator% from the fast builder to avoid multiple memory allocations.
+ return QByteArray::number(QCoreApplication::applicationPid()) % '\n'
+ % processNameByPid(QCoreApplication::applicationPid()).toUtf8() % '\n'
+ % machineName().toUtf8() % '\n';
+}
+
+static bool getLockInfo_helper(const QString &fileName, LockFileInfo *info)
{
QFile reader(fileName);
if (!reader.open(QIODevice::ReadOnly))
@@ -309,14 +349,25 @@ bool QLockFilePrivate::getLockInfo(qint64 *pid, QString *hostname, QString *appn
QByteArray hostNameLine = reader.readLine();
hostNameLine.chop(1);
- qint64 thePid = pidLine.toLongLong();
- if (pid)
- *pid = thePid;
- if (appname)
- *appname = QString::fromUtf8(appNameLine);
- if (hostname)
- *hostname = QString::fromUtf8(hostNameLine);
- return thePid > 0;
+ bool ok;
+ info->appname = QString::fromUtf8(appNameLine);
+ info->hostname = QString::fromUtf8(hostNameLine);
+ info->pid = pidLine.toLongLong(&ok);
+ return ok && info->pid > 0;
+}
+
+bool QLockFilePrivate::isApparentlyStale() const
+{
+ LockFileInfo info;
+ if (getLockInfo_helper(fileName, &info)) {
+ if (info.hostname.isEmpty() || info.hostname == machineName()) {
+ if (!isProcessRunning(info.pid, info.appname))
+ return true;
+ }
+ }
+
+ const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTimeUtc());
+ return staleLockTime > 0 && qAbs(age) > staleLockTime;
}
/*!
diff --git a/src/corelib/io/qlockfile_p.h b/src/corelib/io/qlockfile_p.h
index 86a606ec00..b41dfb38ad 100644
--- a/src/corelib/io/qlockfile_p.h
+++ b/src/corelib/io/qlockfile_p.h
@@ -78,16 +78,14 @@ public:
}
QLockFile::LockError tryLock_sys();
bool removeStaleLock();
- bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
+ QByteArray lockFileContents() const;
// Returns \c true if the lock belongs to dead PID, or is old.
// The attempt to delete it will tell us if it was really stale or not, though.
bool isApparentlyStale() const;
+
// used in dbusmenu
Q_CORE_EXPORT static QString processNameByPid(qint64 pid);
-
-#ifdef Q_OS_UNIX
- static int checkFcntlWorksAfterFlock(const QString &fn);
-#endif
+ static bool isProcessRunning(qint64 pid, const QString &appname);
QString fileName;
#ifdef Q_OS_WIN
diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp
index ccc607afd5..fc01f83e80 100644
--- a/src/corelib/io/qlockfile_unix.cpp
+++ b/src/corelib/io/qlockfile_unix.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -42,7 +42,6 @@
#include "private/qlockfile_p.h"
#include "QtCore/qtemporaryfile.h"
-#include "QtCore/qcoreapplication.h"
#include "QtCore/qfileinfo.h"
#include "QtCore/qdebug.h"
#include "QtCore/qdatetime.h"
@@ -94,91 +93,59 @@ static qint64 qt_write_loop(int fd, const char *data, qint64 len)
return pos;
}
-int QLockFilePrivate::checkFcntlWorksAfterFlock(const QString &fn)
-{
-#ifndef QT_NO_TEMPORARYFILE
- QTemporaryFile file(fn);
- if (!file.open())
- return 0;
- const int fd = file.d_func()->engine()->handle();
-#if defined(LOCK_EX) && defined(LOCK_NB)
- if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
- return 0;
-#endif
- struct flock flockData;
- flockData.l_type = F_WRLCK;
- flockData.l_whence = SEEK_SET;
- flockData.l_start = 0;
- flockData.l_len = 0; // 0 = entire file
- flockData.l_pid = getpid();
- if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems
- return 0;
- return 1;
-#else
- Q_UNUSED(fn);
- return 0;
-#endif
-}
-
-// Cache the result of checkFcntlWorksAfterFlock for each directory a lock
-// file is created in because in some filesystems, like NFS, both locks
-// are the same. This does not take into account a filesystem changing.
-// QCache is set to hold a maximum of 10 entries, this is to avoid unbounded
-// growth, this is caching directories of files and it is assumed a low number
-// will be sufficient.
-typedef QCache<QString, bool> CacheType;
-Q_GLOBAL_STATIC_WITH_ARGS(CacheType, fcntlOK, (10));
-static QBasicMutex fcntlLock;
+/*
+ * Details about file locking on Unix.
+ *
+ * There are three types of advisory locks on Unix systems:
+ * 1) POSIX process-wide locks using fcntl(F_SETLK)
+ * 2) BSD flock(2) system call
+ * 3) Linux-specific file descriptor locks using fcntl(F_OFD_SETLK)
+ * There's also a mandatory locking feature by POSIX, which is deprecated on
+ * Linux and users are advised not to use it.
+ *
+ * The first problem is that the POSIX API is braindead. POSIX.1-2008 says:
+ *
+ * All locks associated with a file for a given process shall be removed when
+ * a file descriptor for that file is closed by that process or the process
+ * holding that file descriptor terminates.
+ *
+ * The Linux manpage is clearer:
+ *
+ * * If a process closes _any_ file descriptor referring to a file, then all
+ * of the process's locks on that file are released, regardless of the file
+ * descriptor(s) on which the locks were obtained. This is bad: [...]
+ *
+ * * The threads in a process share locks. In other words, a multithreaded
+ * program can't use record locking to ensure that threads don't
+ * simultaneously access the same region of a file.
+ *
+ * So in order to use POSIX locks, we'd need a global mutex that stays locked
+ * while the QLockFile is locked. For that reason, Qt does not use POSIX
+ * advisory locks anymore.
+ *
+ * The next problem is that POSIX leaves undefined the relationship between
+ * locks with fcntl(), flock() and lockf(). In some systems (like the BSDs),
+ * all three use the same record set, while on others (like Linux) the locks
+ * are independent, except if locking over NFS mounts, in which case they're
+ * actually the same. Therefore, it's a very bad idea to mix them in the same
+ * process.
+ *
+ * We therefore use only flock(2).
+ */
-/*!
- \internal
- Checks that the OS isn't using POSIX locks to emulate flock().
- \macos is one of those.
-*/
-static bool fcntlWorksAfterFlock(const QString &fn)
-{
- QMutexLocker lock(&fcntlLock);
- if (fcntlOK.isDestroyed())
- return QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
- bool *worksPtr = fcntlOK->object(fn);
- if (worksPtr)
- return *worksPtr;
-
- const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
- worksPtr = new bool(val);
- fcntlOK->insert(fn, worksPtr);
-
- return val;
-}
-
-static bool setNativeLocks(const QString &fileName, int fd)
+static bool setNativeLocks(int fd)
{
#if defined(LOCK_EX) && defined(LOCK_NB)
if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
return false;
+#else
+ Q_UNUSED(fd);
#endif
- struct flock flockData;
- flockData.l_type = F_WRLCK;
- flockData.l_whence = SEEK_SET;
- flockData.l_start = 0;
- flockData.l_len = 0; // 0 = entire file
- flockData.l_pid = getpid();
- if (fcntlWorksAfterFlock(QDir::cleanPath(QFileInfo(fileName).absolutePath()) + QString('/'))
- && fcntl(fd, F_SETLK, &flockData) == -1) { // for networked filesystems
- return false;
- }
return true;
}
QLockFile::LockError QLockFilePrivate::tryLock_sys()
{
- // Assemble data, to write in a single call to write
- // (otherwise we'd have to check every write call)
- // Use operator% from the fast builder to avoid multiple memory allocations.
- QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) % '\n'
- % QCoreApplication::applicationName().toUtf8() % '\n'
- % QSysInfo::machineHostName().toUtf8() % '\n';
-
const QByteArray lockFileName = QFile::encodeName(fileName);
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
@@ -193,13 +160,14 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
}
}
// Ensure nobody else can delete the file while we have it
- if (!setNativeLocks(fileName, fd)) {
+ if (!setNativeLocks(fd)) {
const int errnoSaved = errno;
qWarning() << "setNativeLocks failed:" << qt_error_string(errnoSaved);
}
+ QByteArray fileData = lockFileContents();
if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) {
- close(fd);
+ qt_safe_close(fd);
if (!QFile::remove(fileName))
qWarning("QLockFile: Could not remove our own lock file %s.", qPrintable(fileName));
return QLockFile::UnknownError; // partition full
@@ -224,31 +192,26 @@ bool QLockFilePrivate::removeStaleLock()
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0666);
if (fd < 0) // gone already?
return false;
- bool success = setNativeLocks(fileName, fd) && (::unlink(lockFileName) == 0);
+ bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0);
close(fd);
return success;
}
-bool QLockFilePrivate::isApparentlyStale() const
+bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname)
{
- qint64 pid;
- QString hostname, appname;
- if (getLockInfo(&pid, &hostname, &appname)) {
- if (hostname.isEmpty() || hostname == QSysInfo::machineHostName()) {
- if (::kill(pid, 0) == -1 && errno == ESRCH)
- return true; // PID doesn't exist anymore
- const QString processName = processNameByPid(pid);
- if (!processName.isEmpty()) {
- QFileInfo fi(appname);
- if (fi.isSymLink())
- fi.setFile(fi.symLinkTarget());
- if (processName != fi.fileName())
- return true; // PID got reused by a different application.
- }
- }
+ if (::kill(pid, 0) == -1 && errno == ESRCH)
+ return false; // PID doesn't exist anymore
+
+ const QString processName = processNameByPid(pid);
+ if (!processName.isEmpty()) {
+ QFileInfo fi(appname);
+ if (fi.isSymLink())
+ fi.setFile(fi.symLinkTarget());
+ if (processName != fi.fileName())
+ return false; // PID got reused by a different application.
}
- const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
- return staleLockTime > 0 && qAbs(age) > staleLockTime;
+
+ return true;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
@@ -258,17 +221,17 @@ QString QLockFilePrivate::processNameByPid(qint64 pid)
proc_name(pid, name, sizeof(name) / sizeof(char));
return QFile::decodeName(name);
#elif defined(Q_OS_LINUX)
- if (!QFile::exists(QStringLiteral("/proc/version")))
+ if (!qt_haveLinuxProcfs())
return QString();
+
char exePath[64];
- char buf[PATH_MAX + 1];
sprintf(exePath, "/proc/%lld/exe", pid);
- size_t len = (size_t)readlink(exePath, buf, sizeof(buf));
- if (len >= sizeof(buf)) {
+
+ QByteArray buf = qt_readlink(exePath);
+ if (buf.isEmpty()) {
// The pid is gone. Return some invalid process name to fail the test.
return QStringLiteral("/ERROR/");
}
- buf[len] = 0;
return QFileInfo(QFile::decodeName(buf)).fileName();
#elif defined(Q_OS_HAIKU)
thread_info info;
diff --git a/src/corelib/io/qlockfile_win.cpp b/src/corelib/io/qlockfile_win.cpp
index 4b43181686..de64ec0432 100644
--- a/src/corelib/io/qlockfile_win.cpp
+++ b/src/corelib/io/qlockfile_win.cpp
@@ -2,6 +2,7 @@
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -42,7 +43,6 @@
#include "private/qfilesystementry_p.h"
#include <qt_windows.h>
-#include "QtCore/qcoreapplication.h"
#include "QtCore/qfileinfo.h"
#include "QtCore/qdatetime.h"
#include "QtCore/qdebug.h"
@@ -50,11 +50,6 @@
QT_BEGIN_NAMESPACE
-static inline QByteArray localHostName()
-{
- return qgetenv("COMPUTERNAME");
-}
-
static inline bool fileExists(const wchar_t *fileName)
{
WIN32_FILE_ATTRIBUTE_DATA data;
@@ -107,15 +102,7 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
// We hold the lock, continue.
fileHandle = fh;
- // Assemble data, to write in a single call to write
- // (otherwise we'd have to check every write call)
- QByteArray fileData;
- fileData += QByteArray::number(QCoreApplication::applicationPid());
- fileData += '\n';
- fileData += QCoreApplication::applicationName().toUtf8();
- fileData += '\n';
- fileData += localHostName();
- fileData += '\n';
+ QByteArray fileData = lockFileContents();
DWORD bytesWritten = 0;
QLockFile::LockError error = QLockFile::NoError;
if (!WriteFile(fh, fileData.constData(), fileData.size(), &bytesWritten, NULL) || !FlushFileBuffers(fh))
@@ -129,38 +116,33 @@ bool QLockFilePrivate::removeStaleLock()
return QFile::remove(fileName);
}
-bool QLockFilePrivate::isApparentlyStale() const
+bool QLockFilePrivate::isProcessRunning(qint64 pid, const QString &appname)
{
- qint64 pid;
- QString hostname, appname;
-
// On WinRT there seems to be no way of obtaining information about other
// processes due to sandboxing
#ifndef Q_OS_WINRT
- if (getLockInfo(&pid, &hostname, &appname)) {
- if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {
- HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
- if (!procHandle)
- return true;
- // We got a handle but check if process is still alive
- DWORD exitCode = 0;
- if (!::GetExitCodeProcess(procHandle, &exitCode))
- exitCode = 0;
- ::CloseHandle(procHandle);
- if (exitCode != STILL_ACTIVE)
- return true;
- const QString processName = processNameByPid(pid);
- if (!processName.isEmpty() && processName != appname)
- return true; // PID got reused by a different application.
- }
- }
+ HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (!procHandle)
+ return false;
+
+ // We got a handle but check if process is still alive
+ DWORD exitCode = 0;
+ if (!::GetExitCodeProcess(procHandle, &exitCode))
+ exitCode = 0;
+ ::CloseHandle(procHandle);
+ if (exitCode != STILL_ACTIVE)
+ return false;
+
+ const QString processName = processNameByPid(pid);
+ if (!processName.isEmpty() && processName != appname)
+ return false; // PID got reused by a different application.
+
#else // !Q_OS_WINRT
Q_UNUSED(pid);
- Q_UNUSED(hostname);
Q_UNUSED(appname);
#endif // Q_OS_WINRT
- const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
- return staleLockTime > 0 && qAbs(age) > staleLockTime;
+
+ return true;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 88a54f275f..7ed319f5f1 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -450,11 +450,6 @@ void QProcessPrivate::Channel::clear()
process = 0;
}
-/*! \fn bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
-
-\internal
- */
-
/*!
\class QProcess
\inmodule QtCore
@@ -1058,10 +1053,7 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
readBuffer.chop(available - readBytes);
bool didRead = false;
- if (readBytes == 0) {
- if (channel->notifier)
- channel->notifier->setEnabled(false);
- } else if (currentReadChannel == channelIdx) {
+ if (currentReadChannel == channelIdx) {
didRead = true;
if (!emittedReadyRead) {
emittedReadyRead = true;
@@ -2114,6 +2106,64 @@ void QProcess::start(OpenMode mode)
}
/*!
+ \since 5.10
+
+ Starts the program set by setProgram() with arguments set by setArguments()
+ in a new process, and detaches from it. Returns \c true on success;
+ otherwise returns \c false. If the calling process exits, the
+ detached process will continue to run unaffected.
+
+ \b{Unix:} The started process will run in its own session and act
+ like a daemon.
+
+ The process will be started in the directory set by setWorkingDirectory().
+ If workingDirectory() is empty, the working directory is inherited
+ from the calling process.
+
+ \note On QNX, this may cause all application threads to
+ temporarily freeze.
+
+ If the function is successful then *\a pid is set to the process identifier
+ of the started process. Note that the child process may exit and the PID
+ may become invalid without notice. Furthermore, after the child process
+ exits, the same PID may be recycled and used by a completely different
+ process. User code should be careful when using this variable, especially
+ if one intends to forcibly terminate the process by operating system means.
+
+ Only the following property setters are supported by startDetached():
+ \list
+ \li setArguments()
+ \li setCreateProcessArgumentsModifier()
+ \li setNativeArguments()
+ \li setProcessEnvironment()
+ \li setProgram()
+ \li setStandardErrorFile()
+ \li setStandardInputFile()
+ \li setStandardOutputFile()
+ \li setWorkingDirectory()
+ \endlist
+ All other properties of the QProcess object are ignored.
+
+ \sa start()
+ \sa startDetached(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid)
+ \sa startDetached(const QString &command)
+*/
+bool QProcess::startDetached(qint64 *pid)
+{
+ Q_D(QProcess);
+ if (d->processState != NotRunning) {
+ qWarning("QProcess::startDetached: Process is already running");
+ return false;
+ }
+ if (d->program.isEmpty()) {
+ d->setErrorAndEmit(QProcess::FailedToStart, tr("No program defined"));
+ return false;
+ }
+ return d->startDetached(pid);
+}
+
+/*!
Starts the program set by setProgram() with arguments set by setArguments().
The OpenMode is set to \a mode.
@@ -2447,6 +2497,8 @@ int QProcess::execute(const QString &command)
}
/*!
+ \overload startDetached()
+
Starts the program \a program with the arguments \a arguments in a
new process, and detaches from it. Returns \c true on success;
otherwise returns \c false. If the calling process exits, the
@@ -2454,16 +2506,10 @@ int QProcess::execute(const QString &command)
Argument handling is identical to the respective start() overload.
- \b{Unix:} The started process will run in its own session and act
- like a daemon.
-
The process will be started in the directory \a workingDirectory.
If \a workingDirectory is empty, the working directory is inherited
from the calling process.
- \note On QNX, this may cause all application threads to
- temporarily freeze.
-
If the function is successful then *\a pid is set to the process
identifier of the started process.
@@ -2474,10 +2520,11 @@ bool QProcess::startDetached(const QString &program,
const QString &workingDirectory,
qint64 *pid)
{
- return QProcessPrivate::startDetached(program,
- arguments,
- workingDirectory,
- pid);
+ QProcess process;
+ process.setProgram(program);
+ process.setArguments(arguments);
+ process.setWorkingDirectory(workingDirectory);
+ return process.startDetached(pid);
}
/*!
@@ -2486,11 +2533,14 @@ bool QProcess::startDetached(const QString &program,
bool QProcess::startDetached(const QString &program,
const QStringList &arguments)
{
- return QProcessPrivate::startDetached(program, arguments);
+ QProcess process;
+ process.setProgram(program);
+ process.setArguments(arguments);
+ return process.startDetached();
}
/*!
- \overload
+ \overload startDetached()
Starts the command \a command in a new process, and detaches from it.
Returns \c true on success; otherwise returns \c false.
@@ -2508,9 +2558,10 @@ bool QProcess::startDetached(const QString &command)
if (args.isEmpty())
return false;
- const QString prog = args.takeFirst();
-
- return QProcessPrivate::startDetached(prog, args);
+ QProcess process;
+ process.setProgram(args.takeFirst());
+ process.setArguments(args);
+ return process.startDetached();
}
QT_BEGIN_INCLUDE_NAMESPACE
diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h
index 19157bdd02..c8aef2f0b1 100644
--- a/src/corelib/io/qprocess.h
+++ b/src/corelib/io/qprocess.h
@@ -163,6 +163,7 @@ public:
void start(const QString &command, OpenMode mode = ReadWrite);
#endif
void start(OpenMode mode = ReadWrite);
+ bool startDetached(qint64 *pid = nullptr);
bool open(OpenMode mode = ReadWrite) Q_DECL_OVERRIDE;
QString program() const;
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index c5abf7b762..deb29dca0a 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -350,7 +350,7 @@ public:
void start(QIODevice::OpenMode mode);
void startProcess();
#if defined(Q_OS_UNIX)
- void execChild(const char *workingDirectory, char **path, char **argv, char **envp);
+ void execChild(const char *workingDirectory, char **argv, char **envp);
#endif
bool processStarted(QString *errorMessage = Q_NULLPTR);
void terminateProcess();
@@ -360,13 +360,13 @@ public:
bool waitForDeadChild();
#endif
#ifdef Q_OS_WIN
+ bool callCreateProcess(QProcess::CreateProcessArguments *cpargs);
bool drainOutputPipes();
void flushPipeWriter();
qint64 pipeWriterBytesToWrite() const;
#endif
- static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(),
- qint64 *pid = 0);
+ bool startDetached(qint64 *pPid);
int exitCode;
QProcess::ExitStatus exitStatus;
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 318c633017..15752f84b2 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -87,6 +87,7 @@ QT_END_NAMESPACE
#include "qprocess.h"
#include "qprocess_p.h"
+#include "qstandardpaths.h"
#include "private/qcore_unix_p.h"
#ifdef Q_OS_MAC
@@ -420,8 +421,16 @@ void QProcessPrivate::startProcess()
#endif
// Add the program name to the argument list.
- char *dupProgramName = ::strdup(encodedProgramName.constData());
- argv[0] = dupProgramName;
+ argv[0] = nullptr;
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString &exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty()) {
+ const QByteArray &tmp = QFile::encodeName(exeFilePath);
+ argv[0] = ::strdup(tmp.constData());
+ }
+ }
+ if (!argv[0])
+ argv[0] = ::strdup(encodedProgramName.constData());
// Add every argument to the list
for (int i = 0; i < arguments.count(); ++i)
@@ -443,29 +452,6 @@ void QProcessPrivate::startProcess()
workingDirPtr = encodedWorkingDirectory.constData();
}
- // If the program does not specify a path, generate a list of possible
- // locations for the binary using the PATH environment variable.
- char **path = 0;
- int pathc = 0;
- if (!program.contains(QLatin1Char('/'))) {
- const QString pathEnv = QString::fromLocal8Bit(qgetenv("PATH"));
- if (!pathEnv.isEmpty()) {
- QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
- if (!pathEntries.isEmpty()) {
- pathc = pathEntries.size();
- path = new char *[pathc + 1];
- path[pathc] = 0;
-
- for (int k = 0; k < pathEntries.size(); ++k) {
- QByteArray tmp = QFile::encodeName(pathEntries.at(k));
- if (!tmp.endsWith('/')) tmp += '/';
- tmp += encodedProgramName;
- path[k] = ::strdup(tmp.constData());
- }
- }
- }
- }
-
// Start the process manager, and fork off the child process.
pid_t childPid;
forkfd = ::forkfd(FFD_CLOEXEC, &childPid);
@@ -473,16 +459,12 @@ void QProcessPrivate::startProcess()
if (forkfd != FFD_CHILD_PROCESS) {
// Parent process.
// Clean up duplicated memory.
- free(dupProgramName);
- for (int i = 1; i <= arguments.count(); ++i)
+ for (int i = 0; i <= arguments.count(); ++i)
free(argv[i]);
for (int i = 0; i < envc; ++i)
free(envp[i]);
- for (int i = 0; i < pathc; ++i)
- free(path[i]);
delete [] argv;
delete [] envp;
- delete [] path;
}
// On QNX, if spawnChild failed, childPid will be -1 but forkfd is still 0.
@@ -503,7 +485,7 @@ void QProcessPrivate::startProcess()
// Start the child.
if (forkfd == FFD_CHILD_PROCESS) {
- execChild(workingDirPtr, path, argv, envp);
+ execChild(workingDirPtr, argv, envp);
::_exit(-1);
}
@@ -550,7 +532,7 @@ struct ChildError
char function[8];
};
-void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp)
+void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp)
{
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
@@ -574,7 +556,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
}
}
- // make sure this fd is closed if execvp() succeeds
+ // make sure this fd is closed if execv() succeeds
qt_safe_close(childStartedPipe[0]);
// enter the working directory
@@ -589,25 +571,13 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
// execute the process
if (!envp) {
- qt_safe_execvp(argv[0], argv);
+ qt_safe_execv(argv[0], argv);
strcpy(error.function, "execvp");
} else {
- if (path) {
- char **arg = path;
- while (*arg) {
- argv[0] = *arg;
#if defined (QPROCESS_DEBUG)
- fprintf(stderr, "QProcessPrivate::execChild() searching / starting %s\n", argv[0]);
+ fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
#endif
- qt_safe_execve(argv[0], argv, envp);
- ++arg;
- }
- } else {
-#if defined (QPROCESS_DEBUG)
- fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
-#endif
- qt_safe_execve(argv[0], argv, envp);
- }
+ qt_safe_execve(argv[0], argv, envp);
strcpy(error.function, "execve");
}
@@ -933,7 +903,7 @@ bool QProcessPrivate::waitForDeadChild()
return true;
}
-bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+bool QProcessPrivate::startDetached(qint64 *pid)
{
QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
@@ -949,6 +919,17 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
return false;
}
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ qt_safe_close(startedPipe[0]);
+ qt_safe_close(startedPipe[1]);
+ return false;
+ }
+
pid_t childPid = fork();
if (childPid == 0) {
struct sigaction noaction;
@@ -965,6 +946,18 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
if (doubleForkPid == 0) {
qt_safe_close(pidPipe[1]);
+ // copy the stdin socket if asked to (without closing on exec)
+ if (inputChannelMode != QProcess::ForwardedInputChannel)
+ qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
+
+ // copy the stdout and stderr if asked to
+ if (processChannelMode != QProcess::ForwardedChannels) {
+ if (processChannelMode != QProcess::ForwardedOutputChannel)
+ qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
+ if (processChannelMode != QProcess::ForwardedErrorChannel)
+ qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
+ }
+
if (!encodedWorkingDirectory.isEmpty()) {
if (QT_CHDIR(encodedWorkingDirectory.constData()) == -1)
qWarning("QProcessPrivate::startDetached: failed to chdir to %s", encodedWorkingDirectory.constData());
@@ -975,23 +968,28 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
argv[arguments.size() + 1] = 0;
+ // Duplicate the environment.
+ int envc = 0;
+ char **envp = nullptr;
+ if (environment.d.constData()) {
+ QProcessEnvironmentPrivate::MutexLocker locker(environment.d);
+ envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
+ }
+
+ QByteArray tmp;
if (!program.contains(QLatin1Char('/'))) {
- const QString path = QString::fromLocal8Bit(qgetenv("PATH"));
- if (!path.isEmpty()) {
- QStringList pathEntries = path.split(QLatin1Char(':'));
- for (int k = 0; k < pathEntries.size(); ++k) {
- QByteArray tmp = QFile::encodeName(pathEntries.at(k));
- if (!tmp.endsWith('/')) tmp += '/';
- tmp += QFile::encodeName(program);
- argv[0] = tmp.data();
- qt_safe_execv(argv[0], argv);
- }
- }
- } else {
- QByteArray tmp = QFile::encodeName(program);
- argv[0] = tmp.data();
- qt_safe_execv(argv[0], argv);
+ const QString &exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty())
+ tmp = QFile::encodeName(exeFilePath);
}
+ if (tmp.isEmpty())
+ tmp = QFile::encodeName(program);
+ argv[0] = tmp.data();
+
+ if (envp)
+ qt_safe_execve(argv[0], argv, envp);
+ else
+ qt_safe_execv(argv[0], argv);
struct sigaction noaction;
memset(&noaction, 0, sizeof(noaction));
@@ -1021,6 +1019,9 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
::_exit(1);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
qt_safe_close(startedPipe[1]);
qt_safe_close(pidPipe[1]);
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index 05c9d6594c..8da6d6b16e 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -48,6 +48,7 @@
#include <qdir.h>
#include <qelapsedtimer.h>
#include <qfileinfo.h>
+#include <qrandom.h>
#include <qregexp.h>
#include <qwineventnotifier.h>
#include <private/qsystemlibrary_p.h>
@@ -99,10 +100,8 @@ static void qt_create_pipe(Q_PIPE *pipe, bool isInputPipe)
wchar_t pipeName[256];
unsigned int attempts = 1000;
forever {
- // ### The user must make sure to call qsrand() to make the pipe names less predictable.
- // ### Replace the call to qrand() with a secure version, once we have it in Qt.
_snwprintf(pipeName, sizeof(pipeName) / sizeof(pipeName[0]),
- L"\\\\.\\pipe\\qt-%X", qrand());
+ L"\\\\.\\pipe\\qt-%X", QRandomGenerator::global()->generate());
DWORD dwOpenMode = FILE_FLAG_OVERLAPPED;
DWORD dwOutputBufferSize = 0;
@@ -358,7 +357,8 @@ void QProcessPrivate::closeChannel(Channel *channel)
destroyPipe(channel->pipe);
}
-static QString qt_create_commandline(const QString &program, const QStringList &arguments)
+static QString qt_create_commandline(const QString &program, const QStringList &arguments,
+ const QString &nativeArguments)
{
QString args;
if (!program.isEmpty()) {
@@ -387,6 +387,13 @@ static QString qt_create_commandline(const QString &program, const QStringList &
}
args += QLatin1Char(' ') + tmp;
}
+
+ if (!nativeArguments.isEmpty()) {
+ if (!args.isEmpty())
+ args += QLatin1Char(' ');
+ args += nativeArguments;
+ }
+
return args;
}
@@ -450,6 +457,30 @@ static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &e
return envlist;
}
+bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
+{
+ if (modifyCreateProcessArgs)
+ modifyCreateProcessArgs(cpargs);
+ bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
+ cpargs->processAttributes, cpargs->threadAttributes,
+ cpargs->inheritHandles, cpargs->flags, cpargs->environment,
+ cpargs->currentDirectory, cpargs->startupInfo,
+ cpargs->processInformation);
+ if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = INVALID_Q_PIPE;
+ }
+ if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ return success;
+}
+
void QProcessPrivate::startProcess()
{
Q_Q(QProcess);
@@ -472,15 +503,10 @@ void QProcessPrivate::startProcess()
!openChannel(stderrChannel))
return;
- QString args = qt_create_commandline(program, arguments);
+ const QString args = qt_create_commandline(program, arguments, nativeArguments);
QByteArray envlist;
if (environment.d.constData())
envlist = qt_create_environment(environment.d.constData()->vars);
- if (!nativeArguments.isEmpty()) {
- if (!args.isEmpty())
- args += QLatin1Char(' ');
- args += nativeArguments;
- }
#if defined QPROCESS_DEBUG
qDebug("Creating process");
@@ -507,18 +533,14 @@ void QProcessPrivate::startProcess()
const QString nativeWorkingDirectory = QDir::toNativeSeparators(workingDirectory);
QProcess::CreateProcessArguments cpargs = {
- 0, (wchar_t*)args.utf16(),
- 0, 0, TRUE, dwCreationFlags,
- environment.isEmpty() ? 0 : envlist.data(),
- nativeWorkingDirectory.isEmpty() ? Q_NULLPTR : (wchar_t*)nativeWorkingDirectory.utf16(),
+ nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
+ nullptr, nullptr, true, dwCreationFlags,
+ environment.isEmpty() ? nullptr : envlist.data(),
+ nativeWorkingDirectory.isEmpty()
+ ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()),
&startupInfo, pid
};
- if (modifyCreateProcessArgs)
- modifyCreateProcessArgs(&cpargs);
- success = CreateProcess(cpargs.applicationName, cpargs.arguments, cpargs.processAttributes,
- cpargs.threadAttributes, cpargs.inheritHandles, cpargs.flags,
- cpargs.environment, cpargs.currentDirectory, cpargs.startupInfo,
- cpargs.processInformation);
+ success = callCreateProcess(&cpargs);
QString errorString;
if (!success) {
@@ -526,19 +548,6 @@ void QProcessPrivate::startProcess()
errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
}
- if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
- CloseHandle(stdinChannel.pipe[0]);
- stdinChannel.pipe[0] = INVALID_Q_PIPE;
- }
- if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stdoutChannel.pipe[1]);
- stdoutChannel.pipe[1] = INVALID_Q_PIPE;
- }
- if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stderrChannel.pipe[1]);
- stderrChannel.pipe[1] = INVALID_Q_PIPE;
- }
-
if (!success) {
cleanup();
setErrorAndEmit(QProcess::FailedToStart, errorString);
@@ -826,6 +835,7 @@ bool QProcessPrivate::writeToStdin()
// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
// with ERROR_ELEVATION_REQUIRED.
static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
+ const QString &nativeArguments,
const QString &workingDir, qint64 *pid)
{
typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *);
@@ -836,7 +846,8 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList &
if (!shellExecuteEx)
return false;
- const QString args = qt_create_commandline(QString(), arguments); // needs arguments only
+ const QString args = qt_create_commandline(QString(), // needs arguments only
+ arguments, nativeArguments);
SHELLEXECUTEINFOW shellExecuteExInfo;
memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
@@ -859,25 +870,52 @@ static bool startDetachedUacPrompt(const QString &programIn, const QStringList &
return true;
}
-bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
+bool QProcessPrivate::startDetached(qint64 *pid)
{
static const DWORD errorElevationRequired = 740;
- QString args = qt_create_commandline(program, arguments);
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ return false;
+ }
+
+ QString args = qt_create_commandline(program, arguments, nativeArguments);
bool success = false;
PROCESS_INFORMATION pinfo;
+ void *envPtr = nullptr;
+ QByteArray envlist;
+ if (environment.d.constData()) {
+ envlist = qt_create_environment(environment.d.constData()->vars);
+ envPtr = envlist.data();
+ }
+
DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
};
- success = CreateProcess(0, (wchar_t*)args.utf16(),
- 0, 0, FALSE, dwCreationFlags, 0,
- workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
- &startupInfo, &pinfo);
+
+ const bool inheritHandles = stdinChannel.type == Channel::Redirect
+ || stdoutChannel.type == Channel::Redirect
+ || stderrChannel.type == Channel::Redirect;
+ QProcess::CreateProcessArguments cpargs = {
+ nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
+ nullptr, nullptr, inheritHandles, dwCreationFlags, envPtr,
+ workingDirectory.isEmpty()
+ ? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
+ &startupInfo, &pinfo
+ };
+ success = callCreateProcess(&cpargs);
if (success) {
CloseHandle(pinfo.hThread);
@@ -885,9 +923,19 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a
if (pid)
*pid = pinfo.dwProcessId;
} else if (GetLastError() == errorElevationRequired) {
- success = startDetachedUacPrompt(program, arguments, workingDir, pid);
+ if (envPtr)
+ qWarning("QProcess: custom environment will be ignored for detached elevated process.");
+ if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
+ || !stderrChannel.file.isEmpty()) {
+ qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
+ }
+ success = startDetachedUacPrompt(program, arguments, nativeArguments,
+ workingDirectory, pid);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
return success;
}
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
index 4e7120a3b8..c1187e5145 100644
--- a/src/corelib/io/qresource.cpp
+++ b/src/corelib/io/qresource.cpp
@@ -67,10 +67,9 @@ QT_BEGIN_NAMESPACE
class QStringSplitter
{
public:
- QStringSplitter(const QString &s)
- : m_string(s), m_data(m_string.constData()), m_len(s.length()), m_pos(0)
+ explicit QStringSplitter(QStringView sv)
+ : m_data(sv.data()), m_len(sv.size())
{
- m_splitChar = QLatin1Char('/');
}
inline bool hasNext() {
@@ -79,18 +78,17 @@ public:
return m_pos < m_len;
}
- inline QStringRef next() {
+ inline QStringView next() {
int start = m_pos;
while (m_pos < m_len && m_data[m_pos] != m_splitChar)
++m_pos;
- return QStringRef(&m_string, start, m_pos - start);
+ return QStringView(m_data + start, m_pos - start);
}
- QString m_string;
const QChar *m_data;
- QChar m_splitChar;
- int m_len;
- int m_pos;
+ qssize_t m_len;
+ qssize_t m_pos = 0;
+ QChar m_splitChar = QLatin1Char('/');
};
@@ -679,7 +677,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
QStringSplitter splitter(path);
while (child_count && splitter.hasNext()) {
- QStringRef segment = splitter.next();
+ QStringView segment = splitter.next();
#ifdef DEBUG_RESOURCE_MATCH
qDebug() << " CHILDREN" << segment;
@@ -836,24 +834,24 @@ QStringList QResourceRoot::children(int node) const
bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
{
const QString root = mappingRoot();
- if(!root.isEmpty()) {
- const QVector<QStringRef> root_segments = root.splitRef(QLatin1Char('/'), QString::SkipEmptyParts),
- path_segments = path.splitRef(QLatin1Char('/'), QString::SkipEmptyParts);
- if(path_segments.size() <= root_segments.size()) {
- int matched = 0;
- for(int i = 0; i < path_segments.size(); ++i) {
- if(root_segments[i] != path_segments[i])
- break;
- ++matched;
- }
- if(matched == path_segments.size()) {
- if(match && root_segments.size() > matched)
- *match = root_segments.at(matched).toString();
- return true;
- }
+ if (root.isEmpty())
+ return false;
+
+ QStringSplitter rootIt(root);
+ QStringSplitter pathIt(path);
+ while (rootIt.hasNext()) {
+ if (pathIt.hasNext()) {
+ if (rootIt.next() != pathIt.next()) // mismatch
+ return false;
+ } else {
+ // end of path, but not of root:
+ if (match)
+ *match = rootIt.next().toString();
+ return true;
}
}
- return false;
+ // end of root
+ return !pathIt.hasNext();
}
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp
index 3f45ca5f91..63f2284ef5 100644
--- a/src/corelib/io/qsavefile.cpp
+++ b/src/corelib/io/qsavefile.cpp
@@ -231,7 +231,38 @@ bool QSaveFile::open(OpenMode mode)
d->finalFileName = existingFile.filePath();
}
- d->fileEngine = new QTemporaryFileEngine;
+ auto openDirectly = [&]() {
+ d->fileEngine = QAbstractFileEngine::create(d->finalFileName);
+ if (d->fileEngine->open(mode | QIODevice::Unbuffered)) {
+ d->useTemporaryFile = false;
+ QFileDevice::open(mode);
+ return true;
+ }
+ return false;
+ };
+
+#ifdef Q_OS_WIN
+ // check if it is an Alternate Data Stream
+ if (d->finalFileName == d->fileName && d->fileName.indexOf(QLatin1Char(':'), 2) > 1) {
+ // yes, we can't rename onto it...
+ if (d->directWriteFallback) {
+ if (openDirectly())
+ return true;
+ d->setError(d->fileEngine->error(), d->fileEngine->errorString());
+ delete d->fileEngine;
+ d->fileEngine = 0;
+ } else {
+ QString msg =
+ QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback "
+ "enabled: path contains an Alternate Data Stream specifier")
+ .arg(QDir::toNativeSeparators(d->fileName));
+ d->setError(QFileDevice::OpenError, msg);
+ }
+ return false;
+ }
+#endif
+
+ d->fileEngine = new QTemporaryFileEngine(&d->finalFileName);
// if the target file exists, we'll copy its permissions below,
// but until then, let's ensure the temporary file is not accessible
// to a third party
@@ -243,12 +274,8 @@ bool QSaveFile::open(OpenMode mode)
#ifdef Q_OS_UNIX
if (d->directWriteFallback && err == QFileDevice::OpenError && errno == EACCES) {
delete d->fileEngine;
- d->fileEngine = QAbstractFileEngine::create(d->finalFileName);
- if (d->fileEngine->open(mode | QIODevice::Unbuffered)) {
- d->useTemporaryFile = false;
- QFileDevice::open(mode);
+ if (openDirectly())
return true;
- }
err = d->fileEngine->error();
}
#endif
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index d5460238ec..d0cbc82c56 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -1405,6 +1405,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
return;
}
+ if (!readOnly && !confFile->isWritable()) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
#ifndef QT_BOOTSTRAPPED
/*
Use a lockfile in order to protect us against other QSettings instances
@@ -1414,17 +1419,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
Concurrent read and write are not a problem because the writing operation is atomic.
*/
QLockFile lockFile(confFile->name + QLatin1String(".lock"));
-#endif
- if (!readOnly) {
- if (!confFile->isWritable()
-#ifndef QT_BOOTSTRAPPED
- || !lockFile.lock()
-#endif
- ) {
- setStatus(QSettings::AccessError);
- return;
- }
+ if (!readOnly && !lockFile.lock() && atomicSyncOnly) {
+ setStatus(QSettings::AccessError);
+ return;
}
+#endif
/*
We hold the lock. Let's reread the file if it has changed
@@ -1496,6 +1495,7 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile)
#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(temporaryfile)
QSaveFile sf(confFile->name);
+ sf.setDirectWriteFallback(!atomicSyncOnly);
#else
QFile sf(confFile->name);
#endif
@@ -1802,6 +1802,8 @@ struct QSettingsIniSection
inline QSettingsIniSection() : position(-1) {}
};
+Q_DECLARE_TYPEINFO(QSettingsIniSection, Q_MOVABLE_TYPE);
+
typedef QMap<QString, QSettingsIniSection> IniMap;
/*
@@ -2199,10 +2201,16 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
QSettings can safely be used from different processes (which can
be different instances of your application running at the same
time or different applications altogether) to read and write to
- the same system locations. It uses advisory file locking and a
- smart merging algorithm to ensure data integrity. Note that sync()
- imports changes made by other processes (in addition to writing
- the changes from this QSettings).
+ the same system locations, provided certain conditions are met. For
+ QSettings::IniFormat, it uses advisory file locking and a smart merging
+ algorithm to ensure data integrity. The condition for that to work is that
+ the writeable configuration file must be a regular file and must reside in
+ a directory that the current user can create new, temporary files in. If
+ that is not the case, then one must use setAtomicSyncRequired() to turn the
+ safety off.
+
+ Note that sync() imports changes made by other processes (in addition to
+ writing the changes from this QSettings).
\section1 Platform-Specific Notes
@@ -2883,6 +2891,50 @@ QSettings::Status QSettings::status() const
}
/*!
+ \since 5.10
+
+ Returns \c true if QSettings is only allowed to perform atomic saving and
+ reloading (synchronization) of the settings. Returns \c false if it is
+ allowed to save the settings contents directly to the configuration file.
+
+ The default is \c true.
+
+ \sa setAtomicSyncRequired(), QSaveFile
+*/
+bool QSettings::isAtomicSyncRequired() const
+{
+ Q_D(const QSettings);
+ return d->atomicSyncOnly;
+}
+
+/*!
+ \since 5.10
+
+ Configures whether QSettings is required to perform atomic saving and
+ reloading (synchronization) of the settings. If the \a enable argument is
+ \c true (the default), sync() will only perform synchronization operations
+ that are atomic. If this is not possible, sync() will fail and status()
+ will be an error condition.
+
+ Setting this property to \c false will allow QSettings to write directly to
+ the configuration file and ignore any errors trying to lock it against
+ other processes trying to write at the same time. Because of the potential
+ for corruption, this option should be used with care, but is required in
+ certain conditions, like a QSettings::IniFormat configuration file that
+ exists in an otherwise non-writeable directory or NTFS Alternate Data
+ Streams.
+
+ See \l QSaveFile for more information on the feature.
+
+ \sa isAtomicSyncRequired(), QSaveFile
+*/
+void QSettings::setAtomicSyncRequired(bool enable)
+{
+ Q_D(QSettings);
+ d->atomicSyncOnly = enable;
+}
+
+/*!
Appends \a prefix to the current group.
The current group is automatically prepended to all keys
diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h
index edd59026ed..da5502e5ca 100644
--- a/src/corelib/io/qsettings.h
+++ b/src/corelib/io/qsettings.h
@@ -146,6 +146,8 @@ public:
void clear();
void sync();
Status status() const;
+ bool isAtomicSyncRequired() const;
+ void setAtomicSyncRequired(bool enable);
void beginGroup(const QString &prefix);
void endGroup();
diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h
index 639605d8c4..7923c24770 100644
--- a/src/corelib/io/qsettings_p.h
+++ b/src/corelib/io/qsettings_p.h
@@ -109,6 +109,8 @@ private:
};
#endif
+Q_DECLARE_TYPEINFO(QSettingsKey, Q_MOVABLE_TYPE);
+
typedef QMap<QSettingsKey, QByteArray> UnparsedSettingsMap;
typedef QMap<QSettingsKey, QVariant> ParsedSettingsMap;
@@ -247,6 +249,7 @@ protected:
QString groupPrefix;
bool fallbacks;
bool pendingChanges;
+ bool atomicSyncOnly = true;
mutable QSettings::Status status;
};
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp
index f2368c3b23..c3d45caf0e 100644
--- a/src/corelib/io/qstandardpaths.cpp
+++ b/src/corelib/io/qstandardpaths.cpp
@@ -183,7 +183,7 @@ QT_BEGIN_NAMESPACE
\li "C:/Users/<USER>"
\row \li DataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li CacheLocation
\li "~/Library/Caches/<APPNAME>", "/Library/Caches/<APPNAME>"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>/cache"
@@ -207,10 +207,10 @@ QT_BEGIN_NAMESPACE
\li "C:/Users/<USER>/AppData/Local/cache"
\row \li AppDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Roaming/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li AppLocalDataLocation
\li "~/Library/Application Support/<APPNAME>", "/Library/Application Support/<APPNAME>". "<APPDIR>/../Resources"
- \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data"
+ \li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>", "<APPDIR>", "<APPDIR>/data", "<APPDIR>/data/<APPNAME>"
\row \li AppConfigLocation
\li "~/Library/Preferences/<APPNAME>"
\li "C:/Users/<USER>/AppData/Local/<APPNAME>", "C:/ProgramData/<APPNAME>"
diff --git a/src/corelib/io/qstandardpaths_unix.cpp b/src/corelib/io/qstandardpaths_unix.cpp
index f0ff46fe7f..d06778e2a1 100644
--- a/src/corelib/io/qstandardpaths_unix.cpp
+++ b/src/corelib/io/qstandardpaths_unix.cpp
@@ -42,6 +42,7 @@
#include <qfile.h>
#include <qhash.h>
#include <qtextstream.h>
+#include <qregularexpression.h>
#include <private/qfilesystemengine_p.h>
#include <errno.h>
#include <stdlib.h>
@@ -179,11 +180,12 @@ QString QStandardPaths::writableLocation(StandardLocation type)
QHash<QString, QString> lines;
QTextStream stream(&file);
// Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
- QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
+ QRegularExpression exp(QLatin1String("^XDG_(.*)_DIR=(.*)$"));
while (!stream.atEnd()) {
const QString &line = stream.readLine();
- if (exp.indexIn(line) != -1) {
- const QStringList lst = exp.capturedTexts();
+ QRegularExpressionMatch match = exp.match(line);
+ if (match.hasMatch()) {
+ const QStringList lst = match.capturedTexts();
const QString key = lst.at(1);
QString value = lst.at(2);
if (value.length() > 2
diff --git a/src/corelib/io/qstandardpaths_win.cpp b/src/corelib/io/qstandardpaths_win.cpp
index a06b204da7..eeb02419c3 100644
--- a/src/corelib/io/qstandardpaths_win.cpp
+++ b/src/corelib/io/qstandardpaths_win.cpp
@@ -227,7 +227,15 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
QString applicationDirPath = qApp ? QCoreApplication::applicationDirPath()
: QFileInfo(qAppFileName()).path();
dirs.append(applicationDirPath);
- dirs.append(applicationDirPath + QLatin1String("/data"));
+ const QString dataDir = applicationDirPath + QLatin1String("/data");
+ dirs.append(dataDir);
+
+ if (!isGenericConfigLocation(type)) {
+ QString appDataDir = dataDir;
+ appendOrganizationAndApp(appDataDir);
+ if (appDataDir != dataDir)
+ dirs.append(appDataDir);
+ }
#endif // !QT_BOOTSTRAPPED
} // isConfigLocation()
diff --git a/src/corelib/io/qstorageinfo.cpp b/src/corelib/io/qstorageinfo.cpp
index 27f0552a31..e2c1f0232f 100644
--- a/src/corelib/io/qstorageinfo.cpp
+++ b/src/corelib/io/qstorageinfo.cpp
@@ -40,6 +40,8 @@
#include "qstorageinfo.h"
#include "qstorageinfo_p.h"
+#include "qdebug.h"
+
QT_BEGIN_NAMESPACE
/*!
@@ -431,4 +433,37 @@ QStorageInfo QStorageInfo::root()
volume than the \a second; otherwise returns false.
*/
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QStorageInfo &s)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug.noquote();
+ debug << "QStorageInfo(";
+ if (s.isValid()) {
+ const QStorageInfoPrivate *d = s.d.constData();
+ debug << '"' << d->rootPath << '"';
+ if (!d->fileSystemType.isEmpty())
+ debug << ", type=" << d->fileSystemType;
+ if (!d->name.isEmpty())
+ debug << ", name=\"" << d->name << '"';
+ if (!d->device.isEmpty())
+ debug << ", device=\"" << d->device << '"';
+ if (!d->subvolume.isEmpty())
+ debug << ", subvolume=\"" << d->subvolume << '"';
+ if (d->readOnly)
+ debug << " [read only]";
+ debug << (d->ready ? " [ready]" : " [not ready]");
+ if (d->bytesTotal > 0) {
+ debug << ", bytesTotal=" << d->bytesTotal << ", bytesFree=" << d->bytesFree
+ << ", bytesAvailable=" << d->bytesAvailable;
+ }
+ } else {
+ debug << "invalid";
+ }
+ debug<< ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
diff --git a/src/corelib/io/qstorageinfo.h b/src/corelib/io/qstorageinfo.h
index e2d9747ceb..4ab7a353ef 100644
--- a/src/corelib/io/qstorageinfo.h
+++ b/src/corelib/io/qstorageinfo.h
@@ -49,6 +49,8 @@
QT_BEGIN_NAMESPACE
+class QDebug;
+
class QStorageInfoPrivate;
class Q_CORE_EXPORT QStorageInfo
{
@@ -94,6 +96,7 @@ public:
private:
friend class QStorageInfoPrivate;
friend bool operator==(const QStorageInfo &first, const QStorageInfo &second);
+ friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QStorageInfo &);
QExplicitlySharedDataPointer<QStorageInfoPrivate> d;
};
@@ -114,6 +117,10 @@ inline bool QStorageInfo::isRoot() const
Q_DECLARE_SHARED(QStorageInfo)
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QStorageInfo &);
+#endif
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QStorageInfo)
diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp
index ffaee9c683..2779246c5b 100644
--- a/src/corelib/io/qtemporarydir.cpp
+++ b/src/corelib/io/qtemporarydir.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -44,22 +44,15 @@
#include "qdiriterator.h"
#include "qplatformdefs.h"
+#include "private/qtemporaryfile_p.h"
#include <QDebug>
#include <QPair>
+#include <QRandomGenerator>
#if defined(QT_BUILD_CORE_LIB)
#include "qcoreapplication.h"
#endif
-#if !defined(Q_OS_QNX) && !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY)
-# define USE_SYSTEM_MKDTEMP
-#endif
-
-#include <stdlib.h> // mkdtemp
-#ifndef USE_SYSTEM_MKDTEMP
-#include <private/qfilesystemengine_p.h>
-#endif
-
#if !defined(Q_OS_WIN)
#include <errno.h>
#endif
@@ -102,38 +95,12 @@ static QString defaultTemplateName()
return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX");
}
-#ifndef USE_SYSTEM_MKDTEMP
-static int nextRand(int &v)
-{
- int r = v % 62;
- v /= 62;
- if (v < 62)
- v = qrand();
- return r;
-}
-
-QPair<QString, bool> q_mkdtemp(QString templateName)
+void QTemporaryDirPrivate::create(const QString &templateName)
{
- Q_ASSERT(templateName.endsWith(QLatin1String("XXXXXX")));
-
- static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
- const int length = templateName.size();
-
- QChar *XXXXXX = templateName.data() + length - 6;
-
+ QTemporaryFileName tfn(templateName);
for (int i = 0; i < 256; ++i) {
- int v = qrand();
-
- /* Fill in the random bits. */
- XXXXXX[0] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[1] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[2] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[3] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[4] = QLatin1Char(letters[nextRand(v)]);
- XXXXXX[5] = QLatin1Char(letters[v % 62]);
-
- QFileSystemEntry fileSystemEntry(templateName);
+ tfn.generateNext();
+ QFileSystemEntry fileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
if (QFileSystemEngine::createDirectory(fileSystemEntry, false)) {
QSystemError error;
QFileSystemEngine::setPermissions(fileSystemEntry,
@@ -145,7 +112,9 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
qWarning() << "Unable to remove unused directory" << templateName;
continue;
}
- return qMakePair(templateName, true);
+ success = true;
+ pathOrError = fileSystemEntry.filePath();
+ return;
}
# ifdef Q_OS_WIN
const int exists = ERROR_ALREADY_EXISTS;
@@ -155,36 +124,10 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
int code = errno;
# endif
if (code != exists)
- return qMakePair(qt_error_string(code), false);
+ break;
}
- return qMakePair(qt_error_string(), false);
-}
-
-#else // !USE_SYSTEM_MKDTEMP
-
-QPair<QString, bool> q_mkdtemp(char *templateName)
-{
- bool ok = (mkdtemp(templateName) != 0);
- return qMakePair(ok ? QFile::decodeName(templateName) : qt_error_string(), ok);
-}
-
-#endif // USE_SYSTEM_MKDTEMP
-
-void QTemporaryDirPrivate::create(const QString &templateName)
-{
-#ifndef USE_SYSTEM_MKDTEMP
- QString buffer = templateName;
- if (!buffer.endsWith(QLatin1String("XXXXXX")))
- buffer += QLatin1String("XXXXXX");
- const QPair<QString, bool> result = q_mkdtemp(buffer);
-#else // !USE_SYSTEM_MKDTEMP
- QByteArray buffer = QFile::encodeName(templateName);
- if (!buffer.endsWith("XXXXXX"))
- buffer += "XXXXXX";
- QPair<QString, bool> result = q_mkdtemp(buffer.data()); // modifies buffer
-#endif // USE_SYSTEM_MKDTEMP
- pathOrError = result.first;
- success = result.second;
+ pathOrError = qt_error_string();
+ success = false;
}
//************* QTemporaryDir
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
index 8a99873fee..b8d3e859cf 100644
--- a/src/corelib/io/qtemporaryfile.cpp
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -39,9 +40,8 @@
#include "qtemporaryfile.h"
-#ifndef QT_NO_TEMPORARYFILE
-
#include "qplatformdefs.h"
+#include "qrandom.h"
#include "private/qtemporaryfile_p.h"
#include "private/qfile_p.h"
#include "private/qsystemerror_p.h"
@@ -53,6 +53,8 @@
#if defined(QT_BUILD_CORE_LIB)
#include "qcoreapplication.h"
+#else
+#define tr(X) QString::fromLatin1(X)
#endif
QT_BEGIN_NAMESPACE
@@ -73,82 +75,145 @@ typedef char Latin1Char;
typedef int NativeFileHandle;
#endif
-/*
- * Copyright (c) 1987, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
+QTemporaryFileName::QTemporaryFileName(const QString &templateName)
+{
+ // Ensure there is a placeholder mask
+ QString qfilename = templateName;
+ uint phPos = qfilename.length();
+ uint phLength = 0;
+
+ while (phPos != 0) {
+ --phPos;
+
+ if (qfilename[phPos] == QLatin1Char('X')) {
+ ++phLength;
+ continue;
+ }
+
+ if (phLength >= 6
+ || qfilename[phPos] == QLatin1Char('/')) {
+ ++phPos;
+ break;
+ }
+
+ // start over
+ phLength = 0;
+ }
+
+ if (phLength < 6)
+ qfilename.append(QLatin1String(".XXXXXX"));
+
+ // "Nativify" :-)
+ QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
+ QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
+ .nativeFilePath();
+
+ // Find mask in native path
+ phPos = filename.length();
+ phLength = 0;
+ while (phPos != 0) {
+ --phPos;
+
+ if (filename[phPos] == Latin1Char('X')) {
+ ++phLength;
+ continue;
+ }
+
+ if (phLength >= 6) {
+ ++phPos;
+ break;
+ }
+
+ // start over
+ phLength = 0;
+ }
+
+ Q_ASSERT(phLength >= 6);
+ path = filename;
+ pos = phPos;
+ length = phLength;
+}
/*!
\internal
- Generates a unique file path and returns a native handle to the open file.
- \a path is used as a template when generating unique paths, \a pos
- identifies the position of the first character that will be replaced in the
- template and \a length the number of characters that may be substituted.
- \a mode specifies the file mode bits (not used on Windows).
-
- Returns an open handle to the newly created file if successful, an invalid
- handle otherwise. In both cases, the string in \a path will be changed and
- contain the generated path name.
+ Generates a unique file path from the template \a templ and returns it.
+ The path in \c templ.path is modified.
*/
-static bool createFileFromTemplate(NativeFileHandle &file,
- QFileSystemEntry::NativePath &path, size_t pos, size_t length, quint32 mode,
- QSystemError &error)
+QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
{
Q_ASSERT(length != 0);
- Q_ASSERT(pos < size_t(path.length()));
- Q_ASSERT(length <= size_t(path.length()) - pos);
+ Q_ASSERT(pos < path.length());
+ Q_ASSERT(length <= path.length() - pos);
Char *const placeholderStart = (Char *)path.data() + pos;
Char *const placeholderEnd = placeholderStart + length;
- // Initialize placeholder with random chars + PID.
+ // Replace placeholder with random chars.
{
+ // Since our dictionary is 26+26 characters, it would seem we only need
+ // a random number from 0 to 63 to select a character. However, due to
+ // the limited range, that would mean 12 (64-52) characters have double
+ // the probability of the others: 1 in 32 instead of 1 in 64.
+ //
+ // To overcome this limitation, we use more bits per character. With 10
+ // bits, there are 16 characters with probability 19/1024 and the rest
+ // at 20/1024 (i.e, less than .1% difference). This allows us to do 3
+ // characters per 32-bit random number, which is also half the typical
+ // placeholder length.
+ enum { BitsPerCharacter = 10 };
+
Char *rIter = placeholderEnd;
+ while (rIter != placeholderStart) {
+ quint32 rnd = QRandomGenerator::global()->generate();
+ auto applyOne = [&]() {
+ quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
+ rnd >>= BitsPerCharacter;
+ char ch = char((26 + 26) * v / (1 << BitsPerCharacter));
+ if (ch < 26)
+ *--rIter = Latin1Char(ch + 'A');
+ else
+ *--rIter = Latin1Char(ch - 26 + 'a');
+ };
+
+ applyOne();
+ if (rIter == placeholderStart)
+ break;
-#if defined(QT_BUILD_CORE_LIB)
- quint64 pid = quint64(QCoreApplication::applicationPid());
- do {
- *--rIter = Latin1Char((pid % 10) + '0');
- pid /= 10;
- } while (rIter != placeholderStart && pid != 0);
-#endif
+ applyOne();
+ if (rIter == placeholderStart)
+ break;
- while (rIter != placeholderStart) {
- char ch = char((qrand() & 0xffff) % (26 + 26));
- if (ch < 26)
- *--rIter = Latin1Char(ch + 'A');
- else
- *--rIter = Latin1Char(ch - 26 + 'a');
+ applyOne();
}
}
- for (;;) {
+ return path;
+}
+
+#ifndef QT_NO_TEMPORARYFILE
+
+/*!
+ \internal
+
+ Generates a unique file path from the template \a templ and creates a new
+ file based based on those parameters: the \c templ.length characters in \c
+ templ.path starting at \c templ.pos will be replacd by a random sequence of
+ characters. \a mode specifies the file mode bits (not used on Windows).
+
+ Returns true on success and sets the file handle on \a file. On error,
+ returns false, sets an invalid handle on \a handle and sets the error
+ condition in \a error. In both cases, the string in \a templ will be
+ changed and contain the generated path name.
+*/
+static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
+ quint32 mode, QSystemError &error)
+{
+ const int maxAttempts = 16;
+ for (int attempt = 0; attempt < maxAttempts; ++attempt) {
// Atomically create file and obtain handle
+ const QFileSystemEntry::NativePath &path = templ.generateNext();
+
#if defined(Q_OS_WIN)
Q_UNUSED(mode);
@@ -195,45 +260,58 @@ static bool createFileFromTemplate(NativeFileHandle &file,
return false;
}
#endif
-
- /* tricky little algorwwithm for backward compatibility */
- for (Char *iter = placeholderStart;;) {
- // Character progression: [0-9] => 'a' ... 'z' => 'A' .. 'Z'
- // String progression: "ZZaiC" => "aabiC"
- switch (char(*iter)) {
- case 'Z':
- // Rollover, advance next character
- *iter = Latin1Char('a');
- if (++iter == placeholderEnd) {
- // Out of alternatives. Return file exists error, previously set.
- error = QSystemError(err, QSystemError::NativeError);
- return false;
- }
-
- continue;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- *iter = Latin1Char('a');
- break;
-
- case 'z':
- // increment 'z' to 'A'
- *iter = Latin1Char('A');
- break;
-
- default:
- ++*iter;
- break;
- }
- break;
- }
}
- Q_ASSERT(false);
return false;
}
+enum class CreateUnnamedFileStatus {
+ Success = 0,
+ NotSupported,
+ OtherError
+};
+
+static CreateUnnamedFileStatus
+createUnnamedFile(NativeFileHandle &file, QTemporaryFileName &tfn, quint32 mode, QSystemError *error)
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ // first, check if we have /proc, otherwise can't make the file exist later
+ // (no error message set, as caller will try regular temporary file)
+ if (!qt_haveLinuxProcfs())
+ return CreateUnnamedFileStatus::NotSupported;
+
+ const char *p = ".";
+ int lastSlash = tfn.path.lastIndexOf('/');
+ if (lastSlash != -1) {
+ tfn.path[lastSlash] = '\0';
+ p = tfn.path.data();
+ }
+
+ file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
+ static_cast<mode_t>(mode));
+ if (file != -1)
+ return CreateUnnamedFileStatus::Success;
+
+ if (errno == EOPNOTSUPP || errno == EISDIR) {
+ // fs or kernel doesn't support O_TMPFILE, so
+ // put the slash back so we may try a regular file
+ if (lastSlash != -1)
+ tfn.path[lastSlash] = '/';
+ return CreateUnnamedFileStatus::NotSupported;
+ }
+
+ // real error
+ *error = QSystemError(errno, QSystemError::NativeError);
+ return CreateUnnamedFileStatus::OtherError;
+#else
+ Q_UNUSED(file);
+ Q_UNUSED(tfn);
+ Q_UNUSED(mode);
+ Q_UNUSED(error);
+ return CreateUnnamedFileStatus::NotSupported;
+#endif
+}
+
//************* QTemporaryFileEngine
QTemporaryFileEngine::~QTemporaryFileEngine()
{
@@ -264,13 +342,6 @@ void QTemporaryFileEngine::setFileName(const QString &file)
QFSFileEngine::setFileName(file);
}
-void QTemporaryFileEngine::setFileTemplate(const QString &fileTemplate)
-{
- Q_D(QFSFileEngine);
- if (filePathIsTemplate)
- d->fileEntry = QFileSystemEntry(fileTemplate);
-}
-
bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
{
Q_D(QFSFileEngine);
@@ -281,59 +352,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
if (!filePathIsTemplate)
return QFSFileEngine::open(openMode);
- QString qfilename = d->fileEntry.filePath();
-
- // Ensure there is a placeholder mask
- uint phPos = qfilename.length();
- uint phLength = 0;
-
- while (phPos != 0) {
- --phPos;
-
- if (qfilename[phPos] == QLatin1Char('X')) {
- ++phLength;
- continue;
- }
-
- if (phLength >= 6
- || qfilename[phPos] == QLatin1Char('/')) {
- ++phPos;
- break;
- }
-
- // start over
- phLength = 0;
- }
-
- if (phLength < 6)
- qfilename.append(QLatin1String(".XXXXXX"));
-
- // "Nativify" :-)
- QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
- QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
- .nativeFilePath();
-
- // Find mask in native path
- phPos = filename.length();
- phLength = 0;
- while (phPos != 0) {
- --phPos;
-
- if (filename[phPos] == Latin1Char('X')) {
- ++phLength;
- continue;
- }
-
- if (phLength >= 6) {
- ++phPos;
- break;
- }
-
- // start over
- phLength = 0;
- }
-
- Q_ASSERT(phLength >= 6);
+ QTemporaryFileName tfn(templateName);
QSystemError error;
#if defined(Q_OS_WIN)
@@ -342,19 +361,24 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
NativeFileHandle &file = d->fd;
#endif
- if (!createFileFromTemplate(file, filename, phPos, phLength, fileMode, error)) {
+ CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
+ if (st == CreateUnnamedFileStatus::Success) {
+ unnamedFile = true;
+ d->fileEntry.clear();
+ } else if (st == CreateUnnamedFileStatus::NotSupported &&
+ createFileFromTemplate(file, tfn, fileMode, error)) {
+ filePathIsTemplate = false;
+ unnamedFile = false;
+ d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
+ } else {
setError(QFile::OpenError, error.toString());
return false;
}
- d->fileEntry = QFileSystemEntry(filename, QFileSystemEntry::FromNativePath());
-
#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
d->closeFileHandle = true;
#endif
- filePathIsTemplate = false;
-
d->openMode = openMode;
d->lastFlushFailed = false;
d->tried_stat = 0;
@@ -369,7 +393,7 @@ bool QTemporaryFileEngine::remove()
// we must explicitly call QFSFileEngine::close() before we remove it.
d->unmapAll();
QFSFileEngine::close();
- if (QFSFileEngine::remove()) {
+ if (isUnnamedFile() || QFSFileEngine::remove()) {
d->fileEntry.clear();
// If a QTemporaryFile is constructed using a template file path, the path
// is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
@@ -384,12 +408,22 @@ bool QTemporaryFileEngine::remove()
bool QTemporaryFileEngine::rename(const QString &newName)
{
+ if (isUnnamedFile()) {
+ bool ok = materializeUnnamedFile(newName, DontOverwrite);
+ QFSFileEngine::close();
+ return ok;
+ }
QFSFileEngine::close();
return QFSFileEngine::rename(newName);
}
bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
{
+ if (isUnnamedFile()) {
+ bool ok = materializeUnnamedFile(newName, Overwrite);
+ QFSFileEngine::close();
+ return ok;
+ }
QFSFileEngine::close();
return QFSFileEngine::renameOverwrite(newName);
}
@@ -402,6 +436,88 @@ bool QTemporaryFileEngine::close()
return true;
}
+QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
+{
+ if (isUnnamedFile()) {
+ if (file == LinkName) {
+ // we know our file isn't (won't be) a symlink
+ return QString();
+ }
+
+ // for all other cases, materialize the file
+ const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
+ }
+ return QFSFileEngine::fileName(file);
+}
+
+bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
+{
+ Q_ASSERT(isUnnamedFile());
+
+#ifdef LINUX_UNNAMED_TMPFILE
+ Q_D(QFSFileEngine);
+ const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
+ auto materializeAt = [=](const QFileSystemEntry &dst) {
+ return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
+ };
+#else
+ auto materializeAt = [](const QFileSystemEntry &) { return false; };
+#endif
+
+ auto success = [this](const QFileSystemEntry &entry) {
+ filePathIsTemplate = false;
+ unnamedFile = false;
+ d_func()->fileEntry = entry;
+ return true;
+ };
+
+ auto materializeAsTemplate = [=](const QString &newName) {
+ QTemporaryFileName tfn(newName);
+ static const int maxAttempts = 16;
+ for (int attempt = 0; attempt < maxAttempts; ++attempt) {
+ tfn.generateNext();
+ QFileSystemEntry entry(tfn.path, QFileSystemEntry::FromNativePath());
+ if (materializeAt(entry))
+ return success(entry);
+ }
+ return false;
+ };
+
+ if (mode == NameIsTemplate) {
+ if (materializeAsTemplate(newName))
+ return true;
+ } else {
+ // Use linkat to materialize the file
+ QFileSystemEntry dst(newName);
+ if (materializeAt(dst))
+ return success(dst);
+
+ if (errno == EEXIST && mode == Overwrite) {
+ // retry by first creating a temporary file in the right dir
+ if (!materializeAsTemplate(templateName))
+ return false;
+
+ // then rename the materialized file to target (same as renameOverwrite)
+ QFSFileEngine::close();
+ return QFSFileEngine::renameOverwrite(newName);
+ }
+ }
+
+ // failed
+ setError(QFile::RenameError, QSystemError(errno, QSystemError::NativeError).toString());
+ return false;
+}
+
+bool QTemporaryFileEngine::isUnnamedFile() const
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ Q_ASSERT(unnamedFile == d_func()->fileEntry.isEmpty());
+ return unnamedFile;
+#else
+ return false;
+#endif
+}
+
//************* QTemporaryFilePrivate
QTemporaryFilePrivate::QTemporaryFilePrivate()
@@ -420,7 +536,7 @@ QTemporaryFilePrivate::~QTemporaryFilePrivate()
QAbstractFileEngine *QTemporaryFilePrivate::engine() const
{
if (!fileEngine) {
- fileEngine = new QTemporaryFileEngine;
+ fileEngine = new QTemporaryFileEngine(&templateName);
resetFileEngine();
}
return fileEngine;
@@ -438,6 +554,17 @@ void QTemporaryFilePrivate::resetFileEngine() const
tef->initialize(fileName, 0600, false);
}
+void QTemporaryFilePrivate::materializeUnnamedFile()
+{
+#ifdef LINUX_UNNAMED_TMPFILE
+ if (!fileName.isEmpty() || !fileEngine)
+ return;
+
+ auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine);
+ fileName = tef->fileName(QAbstractFileEngine::DefaultName);
+#endif
+}
+
QString QTemporaryFilePrivate::defaultTemplateName()
{
QString baseName;
@@ -646,6 +773,10 @@ void QTemporaryFile::setAutoRemove(bool b)
QString QTemporaryFile::fileName() const
{
Q_D(const QTemporaryFile);
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef && tef->isReallyOpen())
+ const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
+
if(d->fileName.isEmpty())
return QString();
return d->engine()->fileName(QAbstractFileEngine::DefaultName);
@@ -679,8 +810,36 @@ void QTemporaryFile::setFileTemplate(const QString &name)
{
Q_D(QTemporaryFile);
d->templateName = name;
- if (d->fileEngine)
- static_cast<QTemporaryFileEngine*>(d->fileEngine)->setFileTemplate(name);
+}
+
+/*!
+ \internal
+
+ This is just a simplified version of QFile::rename() because we know a few
+ extra details about what kind of file we have. The documentation is hidden
+ from the user because QFile::rename() should be enough.
+*/
+bool QTemporaryFile::rename(const QString &newName)
+{
+ Q_D(QTemporaryFile);
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
+ return QFile::rename(newName);
+
+ unsetError();
+ close();
+ if (error() == QFile::NoError) {
+ if (tef->rename(newName)) {
+ unsetError();
+ // engine was able to handle the new name so we just reset it
+ tef->setFileName(newName);
+ d->fileName = newName;
+ return true;
+ }
+
+ d->setError(QFile::RenameError, tef->errorString());
+ }
+ return false;
}
/*!
@@ -771,11 +930,10 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
bool QTemporaryFile::open(OpenMode flags)
{
Q_D(QTemporaryFile);
- if (!d->fileName.isEmpty()) {
- if (static_cast<QTemporaryFileEngine*>(d->engine())->isReallyOpen()) {
- setOpenMode(flags);
- return true;
- }
+ auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef && tef->isReallyOpen()) {
+ setOpenMode(flags);
+ return true;
}
// reset the engine state so it creates a new, unique file name from the template;
@@ -786,16 +944,20 @@ bool QTemporaryFile::open(OpenMode flags)
d->resetFileEngine();
if (QFile::open(flags)) {
- d->fileName = d->fileEngine->fileName(QAbstractFileEngine::DefaultName);
+ tef = static_cast<QTemporaryFileEngine *>(d->fileEngine);
+ if (tef->isUnnamedFile())
+ d->fileName.clear();
+ else
+ d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
return true;
}
return false;
}
-QT_END_NAMESPACE
-
#endif // QT_NO_TEMPORARYFILE
+QT_END_NAMESPACE
+
#ifndef QT_NO_QOBJECT
#include "moc_qtemporaryfile.cpp"
#endif
diff --git a/src/corelib/io/qtemporaryfile.h b/src/corelib/io/qtemporaryfile.h
index 3dc2e75f50..7fc5a299fc 100644
--- a/src/corelib/io/qtemporaryfile.h
+++ b/src/corelib/io/qtemporaryfile.h
@@ -80,6 +80,10 @@ public:
QString fileName() const Q_DECL_OVERRIDE;
QString fileTemplate() const;
void setFileTemplate(const QString &name);
+
+ // Hides QFile::rename
+ bool rename(const QString &newName);
+
#if QT_DEPRECATED_SINCE(5,1)
QT_DEPRECATED inline static QTemporaryFile *createLocalFile(const QString &fileName)
{ return createNativeFile(fileName); }
diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h
index 7f365f0e8a..46a0d7aba3 100644
--- a/src/corelib/io/qtemporaryfile_p.h
+++ b/src/corelib/io/qtemporaryfile_p.h
@@ -53,25 +53,48 @@
#include <QtCore/qglobal.h>
-#ifndef QT_NO_TEMPORARYFILE
-
#include "private/qfsfileengine_p.h"
#include "private/qfilesystemengine_p.h"
#include "private/qfile_p.h"
+#include "qtemporaryfile.h"
+
+#ifdef Q_OS_LINUX
+# include <fcntl.h>
+# ifdef O_TMPFILE
+// some early libc support had the wrong values for O_TMPFILE
+// (see https://bugzilla.gnome.org/show_bug.cgi?id=769453#c18)
+# if (O_TMPFILE & O_DIRECTORY) == O_DIRECTORY
+# define LINUX_UNNAMED_TMPFILE
+# endif
+# endif
+#endif
QT_BEGIN_NAMESPACE
+struct QTemporaryFileName
+{
+ QFileSystemEntry::NativePath path;
+ qssize_t pos;
+ qssize_t length;
+
+ QTemporaryFileName(const QString &templateName);
+ QFileSystemEntry::NativePath generateNext();
+};
+
+#ifndef QT_NO_TEMPORARYFILE
+
class QTemporaryFilePrivate : public QFilePrivate
{
Q_DECLARE_PUBLIC(QTemporaryFile)
-protected:
+public:
QTemporaryFilePrivate();
explicit QTemporaryFilePrivate(const QString &templateNameIn);
~QTemporaryFilePrivate();
QAbstractFileEngine *engine() const override;
void resetFileEngine() const;
+ void materializeUnnamedFile();
bool autoRemove = true;
QString templateName = defaultTemplateName();
@@ -85,37 +108,50 @@ class QTemporaryFileEngine : public QFSFileEngine
{
Q_DECLARE_PRIVATE(QFSFileEngine)
public:
+ QTemporaryFileEngine(const QString *templateName)
+ : templateName(*templateName)
+ {}
+
void initialize(const QString &file, quint32 mode, bool nameIsTemplate = true)
{
Q_D(QFSFileEngine);
Q_ASSERT(!isReallyOpen());
fileMode = mode;
filePathIsTemplate = filePathWasTemplate = nameIsTemplate;
- d->fileEntry = QFileSystemEntry(file);
- if (!filePathIsTemplate)
+ if (filePathIsTemplate) {
+ d->fileEntry.clear();
+ } else {
+ d->fileEntry = QFileSystemEntry(file);
QFSFileEngine::setFileName(file);
+ }
}
~QTemporaryFileEngine();
bool isReallyOpen() const;
void setFileName(const QString &file) override;
- void setFileTemplate(const QString &fileTemplate);
bool open(QIODevice::OpenMode flags) override;
bool remove() override;
bool rename(const QString &newName) override;
bool renameOverwrite(const QString &newName) override;
bool close() override;
+ QString fileName(FileName file) const override;
+ enum MaterializationMode { Overwrite, DontOverwrite, NameIsTemplate };
+ bool materializeUnnamedFile(const QString &newName, MaterializationMode mode);
+ bool isUnnamedFile() const;
+
+ const QString &templateName;
quint32 fileMode;
bool filePathIsTemplate;
bool filePathWasTemplate;
+ bool unnamedFile = false;
};
-QT_END_NAMESPACE
-
#endif // QT_NO_TEMPORARYFILE
+QT_END_NAMESPACE
+
#endif /* QTEMPORARYFILE_P_H */
diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp
index 5144ac0ec9..ee3cb4efcb 100644
--- a/src/corelib/io/qtextstream.cpp
+++ b/src/corelib/io/qtextstream.cpp
@@ -954,8 +954,7 @@ QTextStreamPrivate::PaddingResult QTextStreamPrivate::padding(int len) const
right = padSize - padSize/2;
break;
}
- const PaddingResult result = { left, right };
- return result;
+ return { left, right };
}
/*!
diff --git a/src/corelib/io/qtldurl.cpp b/src/corelib/io/qtldurl.cpp
index 96543bbbfd..a934d19fa2 100644
--- a/src/corelib/io/qtldurl.cpp
+++ b/src/corelib/io/qtldurl.cpp
@@ -50,9 +50,21 @@
QT_BEGIN_NAMESPACE
-static bool containsTLDEntry(const QStringRef &entry)
+enum TLDMatchType {
+ ExactMatch,
+ SuffixMatch,
+ ExceptionMatch,
+};
+
+static bool containsTLDEntry(QStringView entry, TLDMatchType match)
{
- int index = qt_hash(entry) % tldCount;
+ const QStringView matchSymbols[] = {
+ QStringViewLiteral(""),
+ QStringViewLiteral("*"),
+ QStringViewLiteral("!"),
+ };
+ const auto symbol = matchSymbols[match];
+ int index = qt_hash(entry, qt_hash(symbol)) % tldCount;
// select the right chunk from the big table
short chunk = 0;
@@ -65,19 +77,14 @@ static bool containsTLDEntry(const QStringRef &entry)
// check all the entries from the given index
while (chunkIndex < tldIndices[index+1] - offset) {
- QString currentEntry = QString::fromUtf8(tldData[chunk] + chunkIndex);
- if (currentEntry == entry)
+ const auto utf8 = tldData[chunk] + chunkIndex;
+ if ((symbol.isEmpty() || QLatin1Char(*utf8) == symbol) && entry == QString::fromUtf8(utf8 + symbol.size()))
return true;
- chunkIndex += qstrlen(tldData[chunk] + chunkIndex) + 1; // +1 for the ending \0
+ chunkIndex += qstrlen(utf8) + 1; // +1 for the ending \0
}
return false;
}
-static inline bool containsTLDEntry(const QString &entry)
-{
- return containsTLDEntry(QStringRef(&entry));
-}
-
/*!
\internal
@@ -111,19 +118,16 @@ Q_CORE_EXPORT bool qIsEffectiveTLD(const QStringRef &domain)
{
// for domain 'foo.bar.com':
// 1. return if TLD table contains 'foo.bar.com'
- if (containsTLDEntry(domain))
+ // 2. else if table contains '*.bar.com',
+ // 3. test that table does not contain '!foo.bar.com'
+
+ if (containsTLDEntry(domain, ExactMatch)) // 1
return true;
const int dot = domain.indexOf(QLatin1Char('.'));
if (dot >= 0) {
- int count = domain.size() - dot;
- QString wildCardDomain = QLatin1Char('*') + domain.right(count);
- // 2. if table contains '*.bar.com',
- // test if table contains '!foo.bar.com'
- if (containsTLDEntry(wildCardDomain)) {
- QString exceptionDomain = QLatin1Char('!') + domain;
- return (! containsTLDEntry(exceptionDomain));
- }
+ if (containsTLDEntry(domain.mid(dot), SuffixMatch)) // 2
+ return !containsTLDEntry(domain, ExceptionMatch); // 3
}
return false;
}
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index b93fed5ba8..ef5ff2d827 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -132,17 +132,8 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
actualReadBufferSize--;
readSoFar = 1;
} else {
- qint64 bytesToRead = qMin(actualReadBufferSize, maxlen);
- readSoFar = 0;
- while (readSoFar < bytesToRead) {
- const char *ptr = readBuffer.readPointer();
- qint64 bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
- readBuffer.nextDataBlockSize());
- memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
- readSoFar += bytesToReadFromThisBlock;
- readBuffer.free(bytesToReadFromThisBlock);
- actualReadBufferSize -= bytesToReadFromThisBlock;
- }
+ readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
+ actualReadBufferSize -= readSoFar;
}
if (!pipeBroken) {
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index 846891102f..75cb8a7ede 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -57,7 +57,6 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent),
handle(pipeWriteEnd),
overlapped(nullptr),
- numberOfBytesToWrite(0),
pendingBytesWrittenValue(0),
stopped(true),
writeSequenceStarted(false),
@@ -98,7 +97,7 @@ bool QWindowsPipeWriter::waitForWrite(int msecs)
qint64 QWindowsPipeWriter::bytesToWrite() const
{
- return numberOfBytesToWrite + pendingBytesWrittenValue;
+ return buffer.size() + pendingBytesWrittenValue;
}
void QWindowsPipeWriter::emitPendingBytesWrittenValue()
@@ -137,7 +136,6 @@ void QWindowsPipeWriter::notified(DWORD errorCode, DWORD numberOfBytesWritten)
{
notifiedCalled = true;
writeSequenceStarted = false;
- numberOfBytesToWrite = 0;
Q_ASSERT(errorCode != ERROR_SUCCESS || numberOfBytesWritten == DWORD(buffer.size()));
buffer.clear();
@@ -193,13 +191,11 @@ bool QWindowsPipeWriter::write(const QByteArray &ba)
overlapped = new Overlapped(this);
overlapped->clear();
buffer = ba;
- numberOfBytesToWrite = buffer.size();
stopped = false;
writeSequenceStarted = true;
- if (!WriteFileEx(handle, buffer.constData(), numberOfBytesToWrite,
+ if (!WriteFileEx(handle, buffer.constData(), buffer.size(),
overlapped, &writeFileCompleted)) {
writeSequenceStarted = false;
- numberOfBytesToWrite = 0;
buffer.clear();
const DWORD errorCode = GetLastError();
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
index c8252e2a4b..d6671c3f27 100644
--- a/src/corelib/io/qwindowspipewriter_p.h
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -145,7 +145,6 @@ private:
HANDLE handle;
Overlapped *overlapped;
QByteArray buffer;
- qint64 numberOfBytesToWrite;
qint64 pendingBytesWrittenValue;
bool stopped;
bool writeSequenceStarted;
diff --git a/src/corelib/io/qwinoverlappedionotifier.cpp b/src/corelib/io/qwinoverlappedionotifier.cpp
deleted file mode 100644
index d7745ae1b6..0000000000
--- a/src/corelib/io/qwinoverlappedionotifier.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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
-** 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.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qwinoverlappedionotifier_p.h"
-#include <qdebug.h>
-#include <qatomic.h>
-#include <qelapsedtimer.h>
-#include <qmutex.h>
-#include <qpointer.h>
-#include <qqueue.h>
-#include <qset.h>
-#include <qthread.h>
-#include <qt_windows.h>
-#include <private/qobject_p.h>
-#include <private/qiodevice_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QWinOverlappedIoNotifier
- \inmodule QtCore
- \brief The QWinOverlappedIoNotifier class provides support for overlapped I/O notifications on Windows.
- \since 5.0
- \internal
-
- The QWinOverlappedIoNotifier class makes it possible to use efficient
- overlapped (asynchronous) I/O notifications on Windows by using an
- I/O completion port.
-
- Once you have obtained a file handle, you can use setHandle() to get
- notifications for I/O operations. Whenever an I/O operation completes,
- the notified() signal is emitted which will pass the number of transferred
- bytes, the operation's error code and a pointer to the operation's
- OVERLAPPED object to the receiver.
-
- Every handle that supports overlapped I/O can be used by
- QWinOverlappedIoNotifier. That includes file handles, TCP sockets
- and named pipes.
-
- Note that you must not use ReadFileEx() and WriteFileEx() together
- with QWinOverlappedIoNotifier. They are not supported as they use a
- different I/O notification mechanism.
-
- The hEvent member in the OVERLAPPED structure passed to ReadFile()
- or WriteFile() is ignored and can be used for other purposes.
-
- \warning This class is only available on Windows.
-
- Due to peculiarities of the Windows I/O completion port API, users of
- QWinOverlappedIoNotifier must pay attention to the following restrictions:
- \list
- \li File handles with a QWinOverlappedIoNotifer are assigned to an I/O
- completion port until the handle is closed. It is impossible to
- disassociate the file handle from the I/O completion port.
- \li There can be only one QWinOverlappedIoNotifer per file handle. Creating
- another QWinOverlappedIoNotifier for that file, even with a duplicated
- handle, will fail.
- \li Certain Windows API functions are unavailable for file handles that are
- assigned to an I/O completion port. This includes the functions
- \c{ReadFileEx} and \c{WriteFileEx}.
- \endlist
- See also the remarks in the MSDN documentation for the
- \c{CreateIoCompletionPort} function.
-*/
-
-struct IOResult
-{
- IOResult(DWORD n = 0, DWORD e = 0, OVERLAPPED *p = 0)
- : numberOfBytes(n), errorCode(e), overlapped(p)
- {}
-
- DWORD numberOfBytes;
- DWORD errorCode;
- OVERLAPPED *overlapped;
-};
-
-
-class QWinIoCompletionPort;
-
-class QWinOverlappedIoNotifierPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QWinOverlappedIoNotifier)
-public:
- QWinOverlappedIoNotifierPrivate()
- : hHandle(INVALID_HANDLE_VALUE)
- {
- }
-
- OVERLAPPED *waitForAnyNotified(int msecs);
- void notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped);
- void _q_notified();
- OVERLAPPED *dispatchNextIoResult();
-
- static QWinIoCompletionPort *iocp;
- static HANDLE iocpInstanceLock;
- static unsigned int iocpInstanceRefCount;
- HANDLE hHandle;
- HANDLE hSemaphore;
- HANDLE hResultsMutex;
- QAtomicInt waiting;
- QQueue<IOResult> results;
-};
-
-QWinIoCompletionPort *QWinOverlappedIoNotifierPrivate::iocp = 0;
-HANDLE QWinOverlappedIoNotifierPrivate::iocpInstanceLock = CreateMutex(NULL, FALSE, NULL);
-unsigned int QWinOverlappedIoNotifierPrivate::iocpInstanceRefCount = 0;
-
-
-class QWinIoCompletionPort : protected QThread
-{
-public:
- QWinIoCompletionPort()
- : finishThreadKey(reinterpret_cast<ULONG_PTR>(this)),
- drainQueueKey(reinterpret_cast<ULONG_PTR>(this + 1)),
- hPort(INVALID_HANDLE_VALUE)
- {
- setObjectName(QLatin1String("I/O completion port thread"));
- HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (!hIOCP) {
- qErrnoWarning("CreateIoCompletionPort failed.");
- return;
- }
- hPort = hIOCP;
- hQueueDrainedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!hQueueDrainedEvent) {
- qErrnoWarning("CreateEvent failed.");
- return;
- }
- }
-
- ~QWinIoCompletionPort()
- {
- PostQueuedCompletionStatus(hPort, 0, finishThreadKey, NULL);
- QThread::wait();
- CloseHandle(hPort);
- CloseHandle(hQueueDrainedEvent);
- }
-
- void registerNotifier(QWinOverlappedIoNotifierPrivate *notifier)
- {
- const HANDLE hHandle = notifier->hHandle;
- HANDLE hIOCP = CreateIoCompletionPort(hHandle, hPort,
- reinterpret_cast<ULONG_PTR>(notifier), 0);
- if (!hIOCP) {
- qErrnoWarning("Can't associate file handle %x with I/O completion port.", hHandle);
- return;
- }
- mutex.lock();
- notifiers += notifier;
- mutex.unlock();
- if (!QThread::isRunning())
- QThread::start();
- }
-
- void unregisterNotifier(QWinOverlappedIoNotifierPrivate *notifier)
- {
- mutex.lock();
- notifiers.remove(notifier);
- mutex.unlock();
- }
-
- void drainQueue()
- {
- QMutexLocker locker(&drainQueueMutex);
- ResetEvent(hQueueDrainedEvent);
- PostQueuedCompletionStatus(hPort, 0, drainQueueKey, NULL);
- WaitForSingleObject(hQueueDrainedEvent, INFINITE);
- }
-
- using QThread::isRunning;
-
-protected:
- void run()
- {
- DWORD dwBytesRead;
- ULONG_PTR pulCompletionKey;
- OVERLAPPED *overlapped;
- DWORD msecs = INFINITE;
-
- forever {
- BOOL success = GetQueuedCompletionStatus(hPort,
- &dwBytesRead,
- &pulCompletionKey,
- &overlapped,
- msecs);
-
- DWORD errorCode = success ? ERROR_SUCCESS : GetLastError();
- if (!success && !overlapped) {
- if (!msecs) {
- // Time out in drain mode. The completion status queue is empty.
- msecs = INFINITE;
- SetEvent(hQueueDrainedEvent);
- continue;
- }
- qErrnoWarning(errorCode, "GetQueuedCompletionStatus failed.");
- return;
- }
-
- if (pulCompletionKey == finishThreadKey)
- return;
- if (pulCompletionKey == drainQueueKey) {
- // Enter drain mode.
- Q_ASSERT(msecs == INFINITE);
- msecs = 0;
- continue;
- }
-
- QWinOverlappedIoNotifierPrivate *notifier
- = reinterpret_cast<QWinOverlappedIoNotifierPrivate *>(pulCompletionKey);
- mutex.lock();
- if (notifiers.contains(notifier))
- notifier->notify(dwBytesRead, errorCode, overlapped);
- mutex.unlock();
- }
- }
-
-private:
- const ULONG_PTR finishThreadKey;
- const ULONG_PTR drainQueueKey;
- HANDLE hPort;
- QSet<QWinOverlappedIoNotifierPrivate *> notifiers;
- QMutex mutex;
- QMutex drainQueueMutex;
- HANDLE hQueueDrainedEvent;
-};
-
-
-QWinOverlappedIoNotifier::QWinOverlappedIoNotifier(QObject *parent)
- : QObject(*new QWinOverlappedIoNotifierPrivate, parent)
-{
- Q_D(QWinOverlappedIoNotifier);
- WaitForSingleObject(d->iocpInstanceLock, INFINITE);
- if (!d->iocp)
- d->iocp = new QWinIoCompletionPort;
- d->iocpInstanceRefCount++;
- ReleaseMutex(d->iocpInstanceLock);
-
- d->hSemaphore = CreateSemaphore(NULL, 0, 255, NULL);
- d->hResultsMutex = CreateMutex(NULL, FALSE, NULL);
- connect(this, SIGNAL(_q_notify()), this, SLOT(_q_notified()), Qt::QueuedConnection);
-}
-
-QWinOverlappedIoNotifier::~QWinOverlappedIoNotifier()
-{
- Q_D(QWinOverlappedIoNotifier);
- setEnabled(false);
- CloseHandle(d->hResultsMutex);
- CloseHandle(d->hSemaphore);
-
- WaitForSingleObject(d->iocpInstanceLock, INFINITE);
- if (!--d->iocpInstanceRefCount) {
- delete d->iocp;
- d->iocp = 0;
- }
- ReleaseMutex(d->iocpInstanceLock);
-}
-
-void QWinOverlappedIoNotifier::setHandle(Qt::HANDLE h)
-{
- Q_D(QWinOverlappedIoNotifier);
- d->hHandle = h;
-}
-
-Qt::HANDLE QWinOverlappedIoNotifier::handle() const
-{
- Q_D(const QWinOverlappedIoNotifier);
- return d->hHandle;
-}
-
-void QWinOverlappedIoNotifier::setEnabled(bool enabled)
-{
- Q_D(QWinOverlappedIoNotifier);
- if (enabled)
- d->iocp->registerNotifier(d);
- else
- d->iocp->unregisterNotifier(d);
-}
-
-OVERLAPPED *QWinOverlappedIoNotifierPrivate::waitForAnyNotified(int msecs)
-{
- if (!iocp->isRunning()) {
- qWarning("Called QWinOverlappedIoNotifier::waitForAnyNotified on inactive notifier.");
- return 0;
- }
-
- if (msecs == 0)
- iocp->drainQueue();
-
- const DWORD wfso = WaitForSingleObject(hSemaphore, msecs == -1 ? INFINITE : DWORD(msecs));
- switch (wfso) {
- case WAIT_OBJECT_0:
- return dispatchNextIoResult();
- case WAIT_TIMEOUT:
- return 0;
- default:
- qErrnoWarning("QWinOverlappedIoNotifier::waitForAnyNotified: WaitForSingleObject failed.");
- return 0;
- }
-}
-
-class QScopedAtomicIntIncrementor
-{
-public:
- QScopedAtomicIntIncrementor(QAtomicInt &i)
- : m_int(i)
- {
- ++m_int;
- }
-
- ~QScopedAtomicIntIncrementor()
- {
- --m_int;
- }
-
-private:
- QAtomicInt &m_int;
-};
-
-/*!
- * Wait synchronously for any notified signal.
- *
- * The function returns a pointer to the OVERLAPPED object corresponding to the completed I/O
- * operation. In case no I/O operation was completed during the \a msec timeout, this function
- * returns a null pointer.
- */
-OVERLAPPED *QWinOverlappedIoNotifier::waitForAnyNotified(int msecs)
-{
- Q_D(QWinOverlappedIoNotifier);
- QScopedAtomicIntIncrementor saii(d->waiting);
- OVERLAPPED *result = d->waitForAnyNotified(msecs);
- return result;
-}
-
-/*!
- * Wait synchronously for the notified signal.
- *
- * The function returns true if the notified signal was emitted for
- * the I/O operation that corresponds to the OVERLAPPED object.
- */
-bool QWinOverlappedIoNotifier::waitForNotified(int msecs, OVERLAPPED *overlapped)
-{
- Q_D(QWinOverlappedIoNotifier);
- QScopedAtomicIntIncrementor saii(d->waiting);
- int t = msecs;
- QElapsedTimer stopWatch;
- stopWatch.start();
- forever {
- OVERLAPPED *triggeredOverlapped = waitForAnyNotified(t);
- if (!triggeredOverlapped)
- return false;
- if (triggeredOverlapped == overlapped)
- return true;
- t = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
- if (t == 0)
- return false;
- }
-}
-
-/*!
- * Note: This function runs in the I/O completion port thread.
- */
-void QWinOverlappedIoNotifierPrivate::notify(DWORD numberOfBytes, DWORD errorCode,
- OVERLAPPED *overlapped)
-{
- Q_Q(QWinOverlappedIoNotifier);
- WaitForSingleObject(hResultsMutex, INFINITE);
- results.enqueue(IOResult(numberOfBytes, errorCode, overlapped));
- ReleaseMutex(hResultsMutex);
- ReleaseSemaphore(hSemaphore, 1, NULL);
- if (!waiting)
- emit q->_q_notify();
-}
-
-void QWinOverlappedIoNotifierPrivate::_q_notified()
-{
- if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0)
- dispatchNextIoResult();
-}
-
-OVERLAPPED *QWinOverlappedIoNotifierPrivate::dispatchNextIoResult()
-{
- Q_Q(QWinOverlappedIoNotifier);
- WaitForSingleObject(hResultsMutex, INFINITE);
- IOResult ioresult = results.dequeue();
- ReleaseMutex(hResultsMutex);
- emit q->notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped);
- return ioresult.overlapped;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qwinoverlappedionotifier_p.cpp"
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
index 226a2401e1..ef3281df30 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
@@ -56,6 +56,15 @@ QT_BEGIN_NAMESPACE
typedef QVector<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList;
+struct QSortFilterProxyModelDataChanged
+{
+ QSortFilterProxyModelDataChanged(const QModelIndex &tl, const QModelIndex &br)
+ : topLeft(tl), bottomRight(br) { }
+
+ QModelIndex topLeft;
+ QModelIndex bottomRight;
+};
+
static inline QSet<int> qVectorToSet(const QVector<int> &vector)
{
QSet<int> set;
@@ -164,9 +173,12 @@ public:
bool sort_localeaware;
int filter_column;
- QRegExp filter_regexp;
int filter_role;
+ QRegExp filter_regexp;
+ QModelIndex last_top_source;
+ bool filter_recursive;
+ bool complete_insert;
bool dynamic_sortfilter;
QRowsRemoval itemsBeingRemoved;
@@ -289,6 +301,11 @@ public:
Qt::Orientation orient, int start, int end, int delta_item_count, bool remove);
virtual void _q_sourceModelDestroyed() Q_DECL_OVERRIDE;
+
+ bool needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const;
+
+ bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const;
+ bool filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const;
};
typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
@@ -300,6 +317,32 @@ void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
source_index_mapping.clear();
}
+bool QSortFilterProxyModelPrivate::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const
+{
+ Q_Q(const QSortFilterProxyModel);
+ return filter_recursive
+ ? filterRecursiveAcceptsRow(source_row, source_parent)
+ : q->filterAcceptsRow(source_row, source_parent);
+}
+
+bool QSortFilterProxyModelPrivate::filterRecursiveAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ Q_Q(const QSortFilterProxyModel);
+
+ if (q->filterAcceptsRow(source_row, source_parent))
+ return true;
+
+ const QModelIndex index = model->index(source_row, 0, source_parent);
+ const int count = model->rowCount(index);
+
+ for (int i = 0; i < count; ++i) {
+ if (filterRecursiveAcceptsRow(i, index))
+ return true;
+ }
+
+ return false;
+}
+
void QSortFilterProxyModelPrivate::remove_from_mapping(const QModelIndex &source_parent)
{
if (Mapping *m = source_index_mapping.take(source_parent)) {
@@ -340,7 +383,7 @@ IndexMap::const_iterator QSortFilterProxyModelPrivate::create_mapping(
int source_rows = model->rowCount(source_parent);
m->source_rows.reserve(source_rows);
for (int i = 0; i < source_rows; ++i) {
- if (q->filterAcceptsRow(i, source_parent))
+ if (filterAcceptsRowInternal(i, source_parent))
m->source_rows.append(i);
}
int source_cols = model->columnCount(source_parent);
@@ -794,7 +837,7 @@ void QSortFilterProxyModelPrivate::source_items_inserted(
QVector<int> source_items;
for (int i = start; i <= end; ++i) {
if ((orient == Qt::Vertical)
- ? q->filterAcceptsRow(i, source_parent)
+ ? filterAcceptsRowInternal(i, source_parent)
: q->filterAcceptsColumn(i, source_parent)) {
source_items.append(i);
}
@@ -814,7 +857,7 @@ void QSortFilterProxyModelPrivate::source_items_inserted(
orthogonal_source_to_proxy.resize(ortho_end);
for (int ortho_item = 0; ortho_item < ortho_end; ++ortho_item) {
- if ((orient == Qt::Horizontal) ? q->filterAcceptsRow(ortho_item, source_parent)
+ if ((orient == Qt::Horizontal) ? filterAcceptsRowInternal(ortho_item, source_parent)
: q->filterAcceptsColumn(ortho_item, source_parent)) {
orthogonal_proxy_to_source.append(ortho_item);
}
@@ -969,12 +1012,7 @@ void QSortFilterProxyModelPrivate::updateChildrenMapping(const QModelIndex &sour
// reinsert moved, mapped indexes
QVector<QPair<QModelIndex, Mapping*> >::iterator it = moved_source_index_mappings.begin();
for (; it != moved_source_index_mappings.end(); ++it) {
-#ifdef QT_STRICT_ITERATORS
- source_index_mapping.insert((*it).first, (*it).second);
- (*it).second->map_iter = source_index_mapping.constFind((*it).first);
-#else
- (*it).second->map_iter = source_index_mapping.insert((*it).first, (*it).second);
-#endif
+ (*it).second->map_iter = QHash<QModelIndex, Mapping *>::const_iterator(source_index_mapping.insert((*it).first, (*it).second));
}
}
@@ -1125,7 +1163,7 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
for (int i = 0; i < proxy_to_source.count(); ++i) {
const int source_item = proxy_to_source.at(i);
if ((orient == Qt::Vertical)
- ? !q->filterAcceptsRow(source_item, source_parent)
+ ? !filterAcceptsRowInternal(source_item, source_parent)
: !q->filterAcceptsColumn(source_item, source_parent)) {
// This source item does not satisfy the filter, so it must be removed
source_items_remove.append(source_item);
@@ -1137,7 +1175,7 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
for (int source_item = 0; source_item < source_count; ++source_item) {
if (source_to_proxy.at(source_item) == -1) {
if ((orient == Qt::Vertical)
- ? q->filterAcceptsRow(source_item, source_parent)
+ ? filterAcceptsRowInternal(source_item, source_parent)
: q->filterAcceptsColumn(source_item, source_parent)) {
// This source item satisfies the filter, so it must be added
source_items_insert.append(source_item);
@@ -1156,6 +1194,33 @@ QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed(
return qVectorToSet(source_items_remove);
}
+bool QSortFilterProxyModelPrivate::needsReorder(const QVector<int> &source_rows, const QModelIndex &source_parent) const
+{
+ Q_Q(const QSortFilterProxyModel);
+ Q_ASSERT(source_sort_column != -1);
+ const int proxyRowCount = q->rowCount(source_to_proxy(source_parent));
+ // If any modified proxy row no longer passes lessThan(previous, current) or lessThan(current, next) then we need to reorder.
+ return std::any_of(source_rows.begin(), source_rows.end(),
+ [this, q, proxyRowCount, source_parent](int sourceRow) -> bool {
+ const QModelIndex sourceIndex = model->index(sourceRow, source_sort_column, source_parent);
+ const QModelIndex proxyIndex = source_to_proxy(sourceIndex);
+ Q_ASSERT(proxyIndex.isValid()); // caller ensured source_rows were not filtered out
+ if (proxyIndex.row() > 0) {
+ const QModelIndex prevProxyIndex = q->sibling(proxyIndex.row() - 1, proxy_sort_column, proxyIndex);
+ const QModelIndex prevSourceIndex = proxy_to_source(prevProxyIndex);
+ if (sort_order == Qt::AscendingOrder ? q->lessThan(sourceIndex, prevSourceIndex) : q->lessThan(prevSourceIndex, sourceIndex))
+ return true;
+ }
+ if (proxyIndex.row() < proxyRowCount - 1) {
+ const QModelIndex nextProxyIndex = q->sibling(proxyIndex.row() + 1, proxy_sort_column, proxyIndex);
+ const QModelIndex nextSourceIndex = proxy_to_source(nextProxyIndex);
+ if (sort_order == Qt::AscendingOrder ? q->lessThan(nextSourceIndex, sourceIndex) : q->lessThan(sourceIndex, nextSourceIndex))
+ return true;
+ }
+ return false;
+ });
+}
+
void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left,
const QModelIndex &source_bottom_right,
const QVector<int> &roles)
@@ -1163,106 +1228,127 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc
Q_Q(QSortFilterProxyModel);
if (!source_top_left.isValid() || !source_bottom_right.isValid())
return;
- QModelIndex source_parent = source_top_left.parent();
- IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
- if (it == source_index_mapping.constEnd()) {
- // Don't care, since we don't have mapping for this index
- return;
+
+ std::vector<QSortFilterProxyModelDataChanged> data_changed_list;
+ data_changed_list.emplace_back(source_top_left, source_bottom_right);
+
+ // Do check parents if the filter role have changed and we are recursive
+ if (filter_recursive && (roles.isEmpty() || roles.contains(filter_role))) {
+ QModelIndex source_parent = source_top_left.parent();
+
+ while (source_parent.isValid()) {
+ data_changed_list.emplace_back(source_parent, source_parent);
+ source_parent = source_parent.parent();
+ }
}
- Mapping *m = it.value();
- // Figure out how the source changes affect us
- QVector<int> source_rows_remove;
- QVector<int> source_rows_insert;
- QVector<int> source_rows_change;
- QVector<int> source_rows_resort;
- int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
- for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
- if (dynamic_sortfilter) {
- if (m->proxy_rows.at(source_row) != -1) {
- if (!q->filterAcceptsRow(source_row, source_parent)) {
- // This source row no longer satisfies the filter, so it must be removed
- source_rows_remove.append(source_row);
- } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
- // This source row has changed in a way that may affect sorted order
- source_rows_resort.append(source_row);
+ for (const QSortFilterProxyModelDataChanged &data_changed : data_changed_list) {
+ const QModelIndex &source_top_left = data_changed.topLeft;
+ const QModelIndex &source_bottom_right = data_changed.bottomRight;
+ const QModelIndex source_parent = source_top_left.parent();
+
+ IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
+ if (it == source_index_mapping.constEnd()) {
+ // Don't care, since we don't have mapping for this index
+ continue;
+ }
+ Mapping *m = it.value();
+
+ // Figure out how the source changes affect us
+ QVector<int> source_rows_remove;
+ QVector<int> source_rows_insert;
+ QVector<int> source_rows_change;
+ QVector<int> source_rows_resort;
+ int end = qMin(source_bottom_right.row(), m->proxy_rows.count() - 1);
+ for (int source_row = source_top_left.row(); source_row <= end; ++source_row) {
+ if (dynamic_sortfilter) {
+ if (m->proxy_rows.at(source_row) != -1) {
+ if (!filterAcceptsRowInternal(source_row, source_parent)) {
+ // This source row no longer satisfies the filter, so it must be removed
+ source_rows_remove.append(source_row);
+ } else if (source_sort_column >= source_top_left.column() && source_sort_column <= source_bottom_right.column()) {
+ // This source row has changed in a way that may affect sorted order
+ source_rows_resort.append(source_row);
+ } else {
+ // This row has simply changed, without affecting filtering nor sorting
+ source_rows_change.append(source_row);
+ }
} else {
- // This row has simply changed, without affecting filtering nor sorting
- source_rows_change.append(source_row);
+ if (!itemsBeingRemoved.contains(source_parent, source_row) && filterAcceptsRowInternal(source_row, source_parent)) {
+ // This source row now satisfies the filter, so it must be added
+ source_rows_insert.append(source_row);
+ }
}
} else {
- if (!itemsBeingRemoved.contains(source_parent, source_row) && q->filterAcceptsRow(source_row, source_parent)) {
- // This source row now satisfies the filter, so it must be added
- source_rows_insert.append(source_row);
+ if (m->proxy_rows.at(source_row) != -1)
+ source_rows_change.append(source_row);
+ }
+ }
+
+ if (!source_rows_remove.isEmpty()) {
+ remove_source_items(m->proxy_rows, m->source_rows,
+ source_rows_remove, source_parent, Qt::Vertical);
+ QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove);
+ QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
+ while (childIt != m->mapped_children.begin()) {
+ --childIt;
+ const QModelIndex source_child_index = *childIt;
+ if (source_rows_remove_set.contains(source_child_index.row())) {
+ childIt = m->mapped_children.erase(childIt);
+ remove_from_mapping(source_child_index);
}
}
- } else {
- if (m->proxy_rows.at(source_row) != -1)
- source_rows_change.append(source_row);
}
- }
- if (!source_rows_remove.isEmpty()) {
- remove_source_items(m->proxy_rows, m->source_rows,
- source_rows_remove, source_parent, Qt::Vertical);
- QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove);
- QVector<QModelIndex>::iterator childIt = m->mapped_children.end();
- while (childIt != m->mapped_children.begin()) {
- --childIt;
- const QModelIndex source_child_index = *childIt;
- if (source_rows_remove_set.contains(source_child_index.row())) {
- childIt = m->mapped_children.erase(childIt);
- remove_from_mapping(source_child_index);
+ if (!source_rows_resort.isEmpty()) {
+ if (needsReorder(source_rows_resort, source_parent)) {
+ // Re-sort the rows of this level
+ QList<QPersistentModelIndex> parents;
+ parents << q->mapFromSource(source_parent);
+ emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
+ QModelIndexPairList source_indexes = store_persistent_indexes();
+ remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
+ source_parent, Qt::Vertical, false);
+ sort_source_rows(source_rows_resort, source_parent);
+ insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
+ source_parent, Qt::Vertical, false);
+ update_persistent_indexes(source_indexes);
+ emit q->layoutChanged(parents, QAbstractItemModel::VerticalSortHint);
}
+ // Make sure we also emit dataChanged for the rows
+ source_rows_change += source_rows_resort;
}
- }
- if (!source_rows_resort.isEmpty()) {
- // Re-sort the rows of this level
- QList<QPersistentModelIndex> parents;
- parents << q->mapFromSource(source_parent);
- emit q->layoutAboutToBeChanged(parents, QAbstractItemModel::VerticalSortHint);
- QModelIndexPairList source_indexes = store_persistent_indexes();
- remove_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
- source_parent, Qt::Vertical, false);
- sort_source_rows(source_rows_resort, source_parent);
- insert_source_items(m->proxy_rows, m->source_rows, source_rows_resort,
- source_parent, Qt::Vertical, false);
- update_persistent_indexes(source_indexes);
- emit q->layoutChanged(parents, QAbstractItemModel::VerticalSortHint);
- // Make sure we also emit dataChanged for the rows
- source_rows_change += source_rows_resort;
- }
-
- if (!source_rows_change.isEmpty()) {
- // Find the proxy row range
- int proxy_start_row;
- int proxy_end_row;
- proxy_item_range(m->proxy_rows, source_rows_change,
- proxy_start_row, proxy_end_row);
- // ### Find the proxy column range also
- if (proxy_end_row >= 0) {
- // the row was accepted, but some columns might still be filtered out
- int source_left_column = source_top_left.column();
- while (source_left_column < source_bottom_right.column()
- && m->proxy_columns.at(source_left_column) == -1)
- ++source_left_column;
- const QModelIndex proxy_top_left = create_index(
- proxy_start_row, m->proxy_columns.at(source_left_column), it);
- int source_right_column = source_bottom_right.column();
- while (source_right_column > source_top_left.column()
- && m->proxy_columns.at(source_right_column) == -1)
- --source_right_column;
- const QModelIndex proxy_bottom_right = create_index(
- proxy_end_row, m->proxy_columns.at(source_right_column), it);
- emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles);
+ if (!source_rows_change.isEmpty()) {
+ // Find the proxy row range
+ int proxy_start_row;
+ int proxy_end_row;
+ proxy_item_range(m->proxy_rows, source_rows_change,
+ proxy_start_row, proxy_end_row);
+ // ### Find the proxy column range also
+ if (proxy_end_row >= 0) {
+ // the row was accepted, but some columns might still be filtered out
+ int source_left_column = source_top_left.column();
+ while (source_left_column < source_bottom_right.column()
+ && m->proxy_columns.at(source_left_column) == -1)
+ ++source_left_column;
+ const QModelIndex proxy_top_left = create_index(
+ proxy_start_row, m->proxy_columns.at(source_left_column), it);
+ int source_right_column = source_bottom_right.column();
+ while (source_right_column > source_top_left.column()
+ && m->proxy_columns.at(source_right_column) == -1)
+ --source_right_column;
+ const QModelIndex proxy_bottom_right = create_index(
+ proxy_end_row, m->proxy_columns.at(source_right_column), it);
+ emit q->dataChanged(proxy_top_left, proxy_bottom_right, roles);
+ }
}
- }
- if (!source_rows_insert.isEmpty()) {
- sort_source_rows(source_rows_insert, source_parent);
- insert_source_items(m->proxy_rows, m->source_rows,
- source_rows_insert, source_parent, Qt::Vertical);
+ if (!source_rows_insert.isEmpty()) {
+ sort_source_rows(source_rows_insert, source_parent);
+ insert_source_items(m->proxy_rows, m->source_rows,
+ source_rows_insert, source_parent, Qt::Vertical);
+ }
}
}
@@ -1322,7 +1408,7 @@ void QSortFilterProxyModelPrivate::_q_sourceReset()
// All internal structures are deleted in clear()
q->endResetModel();
update_source_sort_column();
- if (dynamic_sortfilter)
+ if (dynamic_sortfilter && update_source_sort_column())
sort();
}
@@ -1386,18 +1472,60 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeInserted(
{
Q_UNUSED(start);
Q_UNUSED(end);
+
+ const bool toplevel = !source_parent.isValid();
+ const bool recursive_accepted = filter_recursive && !toplevel && filterAcceptsRowInternal(source_parent.row(), source_parent.parent());
//Force the creation of a mapping now, even if its empty.
//We need it because the proxy can be acessed at the moment it emits rowsAboutToBeInserted in insert_source_items
- if (can_create_mapping(source_parent))
- create_mapping(source_parent);
+ if (!filter_recursive || toplevel || recursive_accepted) {
+ if (can_create_mapping(source_parent))
+ create_mapping(source_parent);
+ if (filter_recursive)
+ complete_insert = true;
+ } else {
+ // The row could have been rejected or the parent might be not yet known... let's try to discover it
+ QModelIndex top_source_parent = source_parent;
+ QModelIndex parent = source_parent.parent();
+ QModelIndex grandParent = parent.parent();
+
+ while (parent.isValid() && !filterAcceptsRowInternal(parent.row(), grandParent)) {
+ top_source_parent = parent;
+ parent = grandParent;
+ grandParent = parent.parent();
+ }
+
+ last_top_source = top_source_parent;
+ }
}
void QSortFilterProxyModelPrivate::_q_sourceRowsInserted(
const QModelIndex &source_parent, int start, int end)
{
- source_items_inserted(source_parent, start, end, Qt::Vertical);
- if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
- sort(); // now it should succeed so we need to make sure to sort again
+ if (!filter_recursive || complete_insert) {
+ if (filter_recursive)
+ complete_insert = false;
+ source_items_inserted(source_parent, start, end, Qt::Vertical);
+ if (update_source_sort_column() && dynamic_sortfilter) //previous call to update_source_sort_column may fail if the model has no column.
+ sort(); // now it should succeed so we need to make sure to sort again
+ return;
+ }
+
+ if (filter_recursive) {
+ bool accept = false;
+
+ for (int row = start; row <= end; ++row) {
+ if (filterAcceptsRowInternal(row, source_parent)) {
+ accept = true;
+ break;
+ }
+ }
+
+ if (!accept) // the new rows have no descendants that match the filter, filter them out.
+ return;
+
+ // last_top_source should now become visible
+ _q_sourceDataChanged(last_top_source, last_top_source, QVector<int>());
+ }
}
void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(
@@ -1413,6 +1541,27 @@ void QSortFilterProxyModelPrivate::_q_sourceRowsRemoved(
{
itemsBeingRemoved = QRowsRemoval();
source_items_removed(source_parent, start, end, Qt::Vertical);
+
+ if (filter_recursive) {
+ // Find out if removing this visible row means that some ascendant
+ // row can now be hidden.
+ // We go up until we find a row that should still be visible
+ // and then make QSFPM re-evaluate the last one we saw before that, to hide it.
+
+ QModelIndex to_hide;
+ QModelIndex source_ascendant = source_parent;
+
+ while (source_ascendant.isValid()) {
+ if (filterAcceptsRowInternal(source_ascendant.row(), source_ascendant.parent()))
+ break;
+
+ to_hide = source_ascendant;
+ source_ascendant = source_ascendant.parent();
+ }
+
+ if (to_hide.isValid())
+ _q_sourceDataChanged(to_hide, to_hide, QVector<int>());
+ }
}
void QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeMoved(
@@ -1685,7 +1834,9 @@ QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent)
d->sort_localeaware = false;
d->filter_column = 0;
d->filter_role = Qt::DisplayRole;
+ d->filter_recursive = false;
d->dynamic_sortfilter = true;
+ d->complete_insert = false;
connect(this, SIGNAL(modelReset()), this, SLOT(_q_clearMapping()));
}
@@ -2506,6 +2657,32 @@ void QSortFilterProxyModel::setFilterRole(int role)
}
/*!
+ \since 5.9
+ \property QSortFilterProxyModel::recursiveFiltering
+ \brief whether the filter to be applied recursively on children, and for
+ any matching child, its parents will be visible as well.
+
+ The default value is false.
+
+ \sa filterAcceptsRow()
+*/
+bool QSortFilterProxyModel::recursiveFiltering() const
+{
+ Q_D(const QSortFilterProxyModel);
+ return d->filter_recursive;
+}
+
+void QSortFilterProxyModel::setRecursiveFiltering(bool recursive)
+{
+ Q_D(QSortFilterProxyModel);
+ if (d->filter_recursive == recursive)
+ return;
+ d->filter_about_to_be_changed();
+ d->filter_recursive = recursive;
+ d->filter_changed();
+}
+
+/*!
\obsolete
This function is obsolete. Use invalidate() instead.
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.h b/src/corelib/itemmodels/qsortfilterproxymodel.h
index 06ff79ef5f..6f2e9806ed 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.h
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.h
@@ -67,6 +67,7 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel
Q_PROPERTY(bool isSortLocaleAware READ isSortLocaleAware WRITE setSortLocaleAware)
Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole)
Q_PROPERTY(int filterRole READ filterRole WRITE setFilterRole)
+ Q_PROPERTY(bool recursiveFiltering READ recursiveFiltering WRITE setRecursiveFiltering)
public:
explicit QSortFilterProxyModel(QObject *parent = Q_NULLPTR);
@@ -107,6 +108,9 @@ public:
int filterRole() const;
void setFilterRole(int role);
+ bool recursiveFiltering() const;
+ void setRecursiveFiltering(bool recursive);
+
public Q_SLOTS:
void setFilterRegExp(const QString &pattern);
void setFilterWildcard(const QString &pattern);
diff --git a/src/corelib/itemmodels/qstringlistmodel.cpp b/src/corelib/itemmodels/qstringlistmodel.cpp
index 2b47d43469..bcfd88fb19 100644
--- a/src/corelib/itemmodels/qstringlistmodel.cpp
+++ b/src/corelib/itemmodels/qstringlistmodel.cpp
@@ -131,7 +131,7 @@ int QStringListModel::rowCount(const QModelIndex &parent) const
*/
QModelIndex QStringListModel::sibling(int row, int column, const QModelIndex &idx) const
{
- if (!idx.isValid() || column != 0 || row >= lst.count())
+ if (!idx.isValid() || column != 0 || row >= lst.count() || row < 0)
return QModelIndex();
return createIndex(row, 0);
diff --git a/src/corelib/json/qjson.cpp b/src/corelib/json/qjson.cpp
index d509349a51..e4bca3bcd0 100644
--- a/src/corelib/json/qjson.cpp
+++ b/src/corelib/json/qjson.cpp
@@ -45,15 +45,8 @@ QT_BEGIN_NAMESPACE
namespace QJsonPrivate
{
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
-#define Q_TO_LITTLE_ENDIAN(x) (x)
-#else
-#define Q_TO_LITTLE_ENDIAN(x) ( ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24) )
-#endif
-
-static const Base emptyArray = { { Q_TO_LITTLE_ENDIAN(sizeof(Base)) }, { 0 }, { 0 } };
-static const Base emptyObject = { { Q_TO_LITTLE_ENDIAN(sizeof(Base)) }, { 0 }, { 0 } };
-
+static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } };
+static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } };
void Data::compact()
{
@@ -394,7 +387,7 @@ int Value::requiredStorage(QJsonValue &v, bool *compressed)
v.d->compact();
v.base = static_cast<QJsonPrivate::Base *>(v.d->header->root());
}
- return v.base ? v.base->size : sizeof(QJsonPrivate::Base);
+ return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base);
case QJsonValue::Undefined:
case QJsonValue::Null:
case QJsonValue::Bool:
diff --git a/src/corelib/json/qjson_p.h b/src/corelib/json/qjson_p.h
index c012ec2662..131528376b 100644
--- a/src/corelib/json/qjson_p.h
+++ b/src/corelib/json/qjson_p.h
@@ -61,6 +61,7 @@
#include <qendian.h>
#include <qnumeric.h>
+#include "private/qendian_p.h"
#include "private/qsimd_p.h"
#include <limits.h>
@@ -131,42 +132,7 @@ class Value;
class Entry;
template<typename T>
-class q_littleendian
-{
-public:
- T val;
-
- q_littleendian &operator =(T i) { val = qToLittleEndian(i); return *this; }
- operator T() const { return qFromLittleEndian(val); }
-
- bool operator ==(T i) { return qFromLittleEndian(val) == i; }
- bool operator !=(T i) { return qFromLittleEndian(val) != i; }
- bool operator ==(q_littleendian<T> i) { return val == i.val; }
- bool operator !=(q_littleendian<T> i) { return val != i.val; }
- bool operator <(T i) { return qFromLittleEndian(val) < i; }
- bool operator >(T i) { return qFromLittleEndian(val) > i; }
- bool operator <=(T i) { return qFromLittleEndian(val) <= i; }
- bool operator >=(T i) { return qFromLittleEndian(val) >= i; }
- q_littleendian &operator +=(T i) {
- val = qToLittleEndian(qFromLittleEndian(val) + i);
- return *this;
- }
- q_littleendian &operator |=(T i) {
- val = qToLittleEndian(qFromLittleEndian(val) | i);
- return *this;
- }
- q_littleendian &operator &=(T i) {
- val = qToLittleEndian(qFromLittleEndian(val) & i);
- return *this;
- }
-};
-} // namespace QJsonPrivate
-
-template <typename T>
-class QTypeInfo<QJsonPrivate::q_littleendian<T> >
- : public QTypeInfoMerger<QJsonPrivate::q_littleendian<T>, T> {};
-
-namespace QJsonPrivate {
+using q_littleendian = QLEInteger<T>;
typedef q_littleendian<short> qle_short;
typedef q_littleendian<unsigned short> qle_ushort;
@@ -174,97 +140,10 @@ typedef q_littleendian<int> qle_int;
typedef q_littleendian<unsigned int> qle_uint;
template<int pos, int width>
-class qle_bitfield
-{
-public:
- uint val;
-
- enum {
- mask = ((1u << width) - 1) << pos
- };
-
- void operator =(uint t) {
- uint i = qFromLittleEndian(val);
- i &= ~mask;
- i |= t << pos;
- val = qToLittleEndian(i);
- }
- operator uint() const {
- uint t = qFromLittleEndian(val);
- t &= mask;
- t >>= pos;
- return t;
- }
- bool operator !() const {
- return !operator uint();
- }
-
- bool operator ==(uint t) { return uint(*this) == t; }
- bool operator !=(uint t) { return uint(*this) != t; }
- bool operator <(uint t) { return uint(*this) < t; }
- bool operator >(uint t) { return uint(*this) > t; }
- bool operator <=(uint t) { return uint(*this) <= t; }
- bool operator >=(uint t) { return uint(*this) >= t; }
- qle_bitfield &operator +=(uint i) {
- *this = (uint(*this) + i);
- return *this;
- }
- qle_bitfield &operator -=(uint i) {
- *this = (uint(*this) - i);
- return *this;
- }
- qle_bitfield &operator |=(uint i) {
- *this = (uint(*this) | i);
- return *this;
- }
- qle_bitfield &operator &=(uint i) {
- *this = (uint(*this) & i);
- return *this;
- }
-};
+using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
template<int pos, int width>
-class qle_signedbitfield
-{
-public:
- uint val;
-
- enum {
- mask = ((1u << width) - 1) << pos
- };
-
- void operator =(int t) {
- uint i = qFromLittleEndian(val);
- i &= ~mask;
- i |= t << pos;
- val = qToLittleEndian(i);
- }
- operator int() const {
- uint i = qFromLittleEndian(val);
- i <<= 32 - width - pos;
- int t = (int) i;
- t >>= 32 - width;
- return t;
- }
- bool operator !() const {
- return !operator int();
- }
-
- bool operator ==(int t) { return int(*this) == t; }
- bool operator !=(int t) { return int(*this) != t; }
- bool operator <(int t) { return int(*this) < t; }
- bool operator >(int t) { return int(*this) > t; }
- bool operator <=(int t) { return int(*this) <= t; }
- bool operator >=(int t) { return int(*this) >= t; }
- qle_signedbitfield &operator +=(int i) {
- *this = (int(*this) + i);
- return *this;
- }
- qle_signedbitfield &operator -=(int i) {
- *this = (int(*this) - i);
- return *this;
- }
-};
+using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
typedef qle_uint offset;
diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp
index d4cc4b81df..c5a5aaf39d 100644
--- a/src/corelib/json/qjsonarray.cpp
+++ b/src/corelib/json/qjsonarray.cpp
@@ -212,6 +212,27 @@ QJsonArray &QJsonArray::operator =(const QJsonArray &other)
return *this;
}
+/*!
+ \fn QJsonArray::QJsonArray(QJsonArray &&other)
+ \since 5.10
+
+ Move-constructs a QJsonArray from \a other.
+*/
+
+/*!
+ \fn QJsonArray &QJsonArray::operator =(QJsonArray &&other)
+ \since 5.10
+
+ Move-assigns \a other to this array.
+*/
+
+/*!
+ \fn void QJsonArray::swap(QJsonArray &other)
+ \since 5.10
+
+ Swaps the array \a other with this. This operation is very fast and never fails.
+*/
+
/*! \fn QJsonArray &QJsonArray::operator+=(const QJsonValue &value)
Appends \a value to the array, and returns a reference to the array itself.
diff --git a/src/corelib/json/qjsonarray.h b/src/corelib/json/qjsonarray.h
index 3b6fa37cfa..ddba2ca78e 100644
--- a/src/corelib/json/qjsonarray.h
+++ b/src/corelib/json/qjsonarray.h
@@ -72,6 +72,20 @@ public:
QJsonArray(const QJsonArray &other);
QJsonArray &operator =(const QJsonArray &other);
+ QJsonArray(QJsonArray &&other) Q_DECL_NOTHROW
+ : d(other.d),
+ a(other.a)
+ {
+ other.d = nullptr;
+ other.a = nullptr;
+ }
+
+ QJsonArray &operator =(QJsonArray &&other) Q_DECL_NOTHROW
+ {
+ swap(other);
+ return *this;
+ }
+
static QJsonArray fromStringList(const QStringList &list);
static QJsonArray fromVariantList(const QVariantList &list);
QVariantList toVariantList() const;
@@ -101,6 +115,12 @@ public:
bool operator==(const QJsonArray &other) const;
bool operator!=(const QJsonArray &other) const;
+ void swap(QJsonArray &other) Q_DECL_NOTHROW
+ {
+ qSwap(d, other.d);
+ qSwap(a, other.a);
+ }
+
class const_iterator;
class iterator {
@@ -243,6 +263,8 @@ private:
QJsonPrivate::Array *a;
};
+Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray)
+
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &);
#endif
diff --git a/src/corelib/json/qjsondocument.cpp b/src/corelib/json/qjsondocument.cpp
index ed454d5442..6469412054 100644
--- a/src/corelib/json/qjsondocument.cpp
+++ b/src/corelib/json/qjsondocument.cpp
@@ -153,6 +153,28 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)
return *this;
}
+/*!
+ \fn QJsonDocument::QJsonDocument(QJsonDocument &&other)
+ \since 5.10
+
+ Move-constructs a QJsonDocument from \a other.
+*/
+
+/*!
+ \fn QJsonDocument &QJsonDocument::operator =(QJsonDocument &&other)
+ \since 5.10
+
+ Move-assigns \a other to this document.
+*/
+
+/*!
+ \fn void QJsonDocument::swap(QJsonDocument &other)
+ \since 5.10
+
+ Swaps the document \a other with this. This operation is very fast and never fails.
+*/
+
+
/*! \enum QJsonDocument::DataValidation
This value is used to tell QJsonDocument whether to validate the binary data
@@ -529,6 +551,58 @@ void QJsonDocument::setArray(const QJsonArray &array)
}
/*!
+ Returns a QJsonValue representing the value for the key \a key.
+
+ Equivalent to calling object().value(key).
+
+ The returned QJsonValue is QJsonValue::Undefined if the key does not exist,
+ or if isObject() is false.
+
+ \since 5.10
+
+ \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject
+ */
+const QJsonValue QJsonDocument::operator[](const QString &key) const
+{
+ if (!isObject())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return object().value(key);
+}
+
+/*!
+ \overload
+ \since 5.10
+*/
+const QJsonValue QJsonDocument::operator[](QLatin1String key) const
+{
+ if (!isObject())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return object().value(key);
+}
+
+/*!
+ Returns a QJsonValue representing the value for index \a i.
+
+ Equivalent to calling array().at(i).
+
+ The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds,
+ or if isArray() is false.
+
+ \since 5.10
+
+ \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray
+ */
+const QJsonValue QJsonDocument::operator[](int i) const
+{
+ if (!isArray())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return array().at(i);
+}
+
+/*!
Returns \c true if the \a other document is equal to this document.
*/
bool QJsonDocument::operator==(const QJsonDocument &other) const
diff --git a/src/corelib/json/qjsondocument.h b/src/corelib/json/qjsondocument.h
index 19885a8d56..4e76af21e2 100644
--- a/src/corelib/json/qjsondocument.h
+++ b/src/corelib/json/qjsondocument.h
@@ -93,6 +93,23 @@ public:
QJsonDocument(const QJsonDocument &other);
QJsonDocument &operator =(const QJsonDocument &other);
+ QJsonDocument(QJsonDocument &&other) Q_DECL_NOTHROW
+ : d(other.d)
+ {
+ other.d = nullptr;
+ }
+
+ QJsonDocument &operator =(QJsonDocument &&other) Q_DECL_NOTHROW
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(QJsonDocument &other) Q_DECL_NOTHROW
+ {
+ qSwap(d, other.d);
+ }
+
enum DataValidation {
Validate,
BypassValidation
@@ -131,6 +148,10 @@ public:
void setObject(const QJsonObject &object);
void setArray(const QJsonArray &array);
+ const QJsonValue operator[](const QString &key) const;
+ const QJsonValue operator[](QLatin1String key) const;
+ const QJsonValue operator[](int i) const;
+
bool operator==(const QJsonDocument &other) const;
bool operator!=(const QJsonDocument &other) const { return !(*this == other); }
@@ -147,6 +168,8 @@ private:
QJsonPrivate::Data *d;
};
+Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument)
+
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &);
#endif
diff --git a/src/corelib/json/qjsonobject.cpp b/src/corelib/json/qjsonobject.cpp
index b5b6f36bc6..4a316c8a6f 100644
--- a/src/corelib/json/qjsonobject.cpp
+++ b/src/corelib/json/qjsonobject.cpp
@@ -194,6 +194,28 @@ QJsonObject &QJsonObject::operator =(const QJsonObject &other)
}
/*!
+ \fn QJsonObject::QJsonObject(QJsonObject &&other)
+ \since 5.10
+
+ Move-constructs a QJsonObject from \a other.
+*/
+
+/*!
+ \fn QJsonObject &QJsonObject::operator =(QJsonObject &&other)
+ \since 5.10
+
+ Move-assigns \a other to this object.
+*/
+
+/*!
+ \fn void QJsonObject::swap(QJsonObject &other)
+ \since 5.10
+
+ Swaps the object \a other with this. This operation is very fast and never fails.
+*/
+
+
+/*!
Converts the variant map \a map to a QJsonObject.
The keys in \a map will be used as the keys in the JSON object,
diff --git a/src/corelib/json/qjsonobject.h b/src/corelib/json/qjsonobject.h
index e238c84d98..c77e2164a8 100644
--- a/src/corelib/json/qjsonobject.h
+++ b/src/corelib/json/qjsonobject.h
@@ -74,6 +74,25 @@ public:
QJsonObject(const QJsonObject &other);
QJsonObject &operator =(const QJsonObject &other);
+ QJsonObject(QJsonObject &&other) Q_DECL_NOTHROW
+ : d(other.d), o(other.o)
+ {
+ other.d = nullptr;
+ other.o = nullptr;
+ }
+
+ QJsonObject &operator =(QJsonObject &&other) Q_DECL_NOTHROW
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(QJsonObject &other) Q_DECL_NOTHROW
+ {
+ qSwap(d, other.d);
+ qSwap(o, other.o);
+ }
+
static QJsonObject fromVariantMap(const QVariantMap &map);
QVariantMap toVariantMap() const;
static QJsonObject fromVariantHash(const QVariantHash &map);
@@ -241,6 +260,8 @@ private:
QJsonPrivate::Object *o;
};
+Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject)
+
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &);
#endif
diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp
index 4b52014db1..33707b6ec3 100644
--- a/src/corelib/json/qjsonvalue.cpp
+++ b/src/corelib/json/qjsonvalue.cpp
@@ -276,14 +276,32 @@ QJsonValue::QJsonValue(const QJsonValue &other)
QJsonValue &QJsonValue::operator =(const QJsonValue &other)
{
QJsonValue copy(other);
- // swap(copy);
- qSwap(dbl, copy.dbl);
- qSwap(d, copy.d);
- qSwap(t, copy.t);
+ swap(copy);
return *this;
}
/*!
+ \fn QJsonValue::QJsonValue(QJsonValue &&other)
+ \since 5.10
+
+ Move-constructs a QJsonValue from \a other.
+*/
+
+/*!
+ \fn QJsonValue &QJsonValue::operator =(QJsonValue &&other)
+ \since 5.10
+
+ Move-assigns \a other to this value.
+*/
+
+/*!
+ \fn void QJsonValue::swap(QJsonValue &other)
+ \since 5.10
+
+ Swaps the value \a other with this. This operation is very fast and never fails.
+*/
+
+/*!
\fn bool QJsonValue::isNull() const
Returns \c true if the value is null.
@@ -625,6 +643,58 @@ QJsonObject QJsonValue::toObject() const
}
/*!
+ Returns a QJsonValue representing the value for the key \a key.
+
+ Equivalent to calling toObject().value(key).
+
+ The returned QJsonValue is QJsonValue::Undefined if the key does not exist,
+ or if isObject() is false.
+
+ \since 5.10
+
+ \sa QJsonValue, QJsonValue::isUndefined(), QJsonObject
+ */
+const QJsonValue QJsonValue::operator[](const QString &key) const
+{
+ if (!isObject())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return toObject().value(key);
+}
+
+/*!
+ \overload
+ \since 5.10
+*/
+const QJsonValue QJsonValue::operator[](QLatin1String key) const
+{
+ if (!isObject())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return toObject().value(key);
+}
+
+/*!
+ Returns a QJsonValue representing the value for index \a i.
+
+ Equivalent to calling toArray().at(i).
+
+ The returned QJsonValue is QJsonValue::Undefined, if \a i is out of bounds,
+ or if isArray() is false.
+
+ \since 5.10
+
+ \sa QJsonValue, QJsonValue::isUndefined(), QJsonArray
+ */
+const QJsonValue QJsonValue::operator[](int i) const
+{
+ if (!isArray())
+ return QJsonValue(QJsonValue::Undefined);
+
+ return toArray().at(i);
+}
+
+/*!
Returns \c true if the value is equal to \a other.
*/
bool QJsonValue::operator==(const QJsonValue &other) const
diff --git a/src/corelib/json/qjsonvalue.h b/src/corelib/json/qjsonvalue.h
index a853acaddd..5d5ec72605 100644
--- a/src/corelib/json/qjsonvalue.h
+++ b/src/corelib/json/qjsonvalue.h
@@ -92,6 +92,29 @@ public:
QJsonValue(const QJsonValue &other);
QJsonValue &operator =(const QJsonValue &other);
+ QJsonValue(QJsonValue &&other) Q_DECL_NOTHROW
+ : ui(other.ui),
+ d(other.d),
+ t(other.t)
+ {
+ other.ui = 0;
+ other.d = nullptr;
+ other.t = Null;
+ }
+
+ QJsonValue &operator =(QJsonValue &&other) Q_DECL_NOTHROW
+ {
+ swap(other);
+ return *this;
+ }
+
+ void swap(QJsonValue &other) Q_DECL_NOTHROW
+ {
+ qSwap(ui, other.ui);
+ qSwap(d, other.d);
+ qSwap(t, other.t);
+ }
+
static QJsonValue fromVariant(const QVariant &variant);
QVariant toVariant() const;
@@ -114,6 +137,10 @@ public:
QJsonObject toObject() const;
QJsonObject toObject(const QJsonObject &defaultValue) const;
+ const QJsonValue operator[](const QString &key) const;
+ const QJsonValue operator[](QLatin1String key) const;
+ const QJsonValue operator[](int i) const;
+
bool operator==(const QJsonValue &other) const;
bool operator!=(const QJsonValue &other) const;
@@ -217,6 +244,8 @@ public:
};
#endif
+Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonValue)
+
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &);
#endif
diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri
index 29bd5bbc6c..8abe9b2b44 100644
--- a/src/corelib/kernel/kernel.pri
+++ b/src/corelib/kernel/kernel.pri
@@ -79,7 +79,8 @@ win32 {
kernel/qsharedmemory_win.cpp \
kernel/qsystemsemaphore_win.cpp
HEADERS += \
- kernel/qwineventnotifier.h
+ kernel/qwineventnotifier.h \
+ kernel/qwineventnotifier_p.h
winrt {
SOURCES += kernel/qeventdispatcher_winrt.cpp
diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp
index d234949d14..d1e436c371 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.cpp
+++ b/src/corelib/kernel/qabstracteventdispatcher.cpp
@@ -375,8 +375,7 @@ void QAbstractEventDispatcher::closingDown()
*/
/*!
- Installs an event filter \a filterObj for all native event filters
- received by the application.
+ Installs an event filter \a filterObj for all native events received by the application.
The event filter \a filterObj receives events via its \l {QAbstractNativeEventFilter::}{nativeEventFilter()}
function, which is called for all events received by all threads.
diff --git a/src/corelib/kernel/qabstracteventdispatcher.h b/src/corelib/kernel/qabstracteventdispatcher.h
index 0d3e53e4a7..3a530cf1de 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.h
+++ b/src/corelib/kernel/qabstracteventdispatcher.h
@@ -124,6 +124,8 @@ protected:
QObject *parent);
};
+Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfo, (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) ? Q_PRIMITIVE_TYPE : Q_RELOCATABLE_TYPE));
+
QT_END_NAMESPACE
#endif // QABSTRACTEVENTDISPATCHER_H
diff --git a/src/corelib/kernel/qcore_mac.cpp b/src/corelib/kernel/qcore_mac.cpp
index c689f47d8f..bfb3b2ff07 100644
--- a/src/corelib/kernel/qcore_mac.cpp
+++ b/src/corelib/kernel/qcore_mac.cpp
@@ -45,16 +45,16 @@ QT_BEGIN_NAMESPACE
QCFString::operator QString() const
{
- if (string.isEmpty() && type)
- const_cast<QCFString*>(this)->string = QString::fromCFString(type);
+ if (string.isEmpty() && value)
+ const_cast<QCFString*>(this)->string = QString::fromCFString(value);
return string;
}
QCFString::operator CFStringRef() const
{
- if (!type)
- const_cast<QCFString*>(this)->type = string.toCFString();
- return type;
+ if (!value)
+ const_cast<QCFString*>(this)->value = string.toCFString();
+ return value;
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm
index 231afb991c..24d73fa8be 100644
--- a/src/corelib/kernel/qcore_mac_objc.mm
+++ b/src/corelib/kernel/qcore_mac_objc.mm
@@ -42,7 +42,6 @@
#ifdef Q_OS_OSX
#include <AppKit/NSText.h>
-#include <Carbon/Carbon.h>
#endif
#include <qdebug.h>
@@ -53,7 +52,11 @@ QT_BEGIN_NAMESPACE
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
{
- return dbg << (nsObject ? nsObject.description.UTF8String : "NSObject(0x0)");
+ return dbg << (nsObject ?
+ dbg.verbosity() > 2 ?
+ nsObject.debugDescription.UTF8String :
+ nsObject.description.UTF8String
+ : "NSObject(0x0)");
}
QDebug operator<<(QDebug dbg, CFStringRef stringRef)
@@ -81,19 +84,122 @@ QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TY
// -------------------------------------------------------------------------
+QT_END_NAMESPACE
+QT_USE_NAMESPACE
+@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
+{
+ NSAutoreleasePool **m_pool;
+}
+-(id)initWithPool:(NSAutoreleasePool**)pool;
+@end
+@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
+-(id)initWithPool:(NSAutoreleasePool**)pool
+{
+ if (self = [super init])
+ m_pool = pool;
+ return self;
+}
+-(void)dealloc
+{
+ if (*m_pool) {
+ // The pool is still valid, which means we're not being drained from
+ // the corresponding QMacAutoReleasePool (see below).
+
+ // QMacAutoReleasePool has only a single member, the NSAutoreleasePool*
+ // so the address of that member is also the QMacAutoReleasePool itself.
+ QMacAutoReleasePool *pool = reinterpret_cast<QMacAutoReleasePool *>(m_pool);
+ qWarning() << "Premature drain of" << pool << "This can happen if you've allocated"
+ << "the pool on the heap, or as a member of a heap-allocated object. This is not a"
+ << "supported use of QMacAutoReleasePool, and might result in crashes when objects"
+ << "in the pool are deallocated and then used later on under the assumption they"
+ << "will be valid until" << pool << "has been drained.";
+
+ // Reset the pool so that it's not drained again later on
+ *m_pool = nullptr;
+ }
+
+ [super dealloc];
+}
+@end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
+QT_BEGIN_NAMESPACE
+
QMacAutoReleasePool::QMacAutoReleasePool()
: pool([[NSAutoreleasePool alloc] init])
{
+ [[[QMacAutoReleasePoolTracker alloc] initWithPool:
+ reinterpret_cast<NSAutoreleasePool **>(&pool)] autorelease];
}
QMacAutoReleasePool::~QMacAutoReleasePool()
{
+ if (!pool) {
+ qWarning() << "Prematurely drained pool" << this << "finally drained. Any objects belonging"
+ << "to this pool have already been released, and have potentially been invalid since the"
+ << "premature drain earlier on.";
+ return;
+ }
+
+ // Save and reset pool before draining, so that the pool tracker can know
+ // that it's being drained by its owning pool.
+ NSAutoreleasePool *savedPool = static_cast<NSAutoreleasePool*>(pool);
+ pool = nullptr;
+
// Drain behaves the same as release, with the advantage that
// if we're ever used in a garbage-collected environment, the
// drain acts as a hint to the garbage collector to collect.
- [static_cast<NSAutoreleasePool*>(pool) drain];
+ [savedPool drain];
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "QMacAutoReleasePool(" << (const void *)pool << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+#ifdef Q_OS_MACOS
+/*
+ Ensure that Objective-C objects auto-released in main(), directly or indirectly,
+ after QCoreApplication construction, are released when the app goes out of scope.
+ The memory will be reclaimed by the system either way when the process exits,
+ but by having a root level pool we ensure that the objects get their dealloc
+ methods called, which is useful for debugging object ownership graphs, etc.
+*/
+
+QT_END_NAMESPACE
+#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
+@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
+@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
+QT_BEGIN_NAMESPACE
+
+const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] = "QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
+
+QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
+{
+ if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
+ return;
+
+ pool.reset(new QMacAutoReleasePool);
+
+ [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
+
+ if (qstrcmp(qgetenv("OBJC_DEBUG_MISSING_POOLS"), "YES") == 0) {
+ qDebug("QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n" \
+ "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
+ __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
+ }
}
+QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
+{
+}
+#endif
+
// -------------------------------------------------------------------------
#ifdef Q_OS_OSX
@@ -140,6 +246,7 @@ struct qtKey2CocoaKeySortLessThan
}
};
+static const int NSEscapeCharacter = 27; // not defined by Cocoa headers
static const int NumEntries = 59;
static const KeyPair entries[NumEntries] = {
{ NSEnterCharacter, Qt::Key_Enter },
@@ -148,7 +255,7 @@ static const KeyPair entries[NumEntries] = {
{ NSNewlineCharacter, Qt::Key_Return },
{ NSCarriageReturnCharacter, Qt::Key_Return },
{ NSBackTabCharacter, Qt::Key_Backtab },
- { kEscapeCharCode, Qt::Key_Escape },
+ { NSEscapeCharacter, Qt::Key_Escape },
// Cocoa sends us delete when pressing backspace!
// (NB when we reverse this list in qtKey2CocoaKey, there
// will be two indices of Qt::Key_Backspace. But is seems to work
@@ -237,6 +344,36 @@ Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode)
#endif // Q_OS_OSX
+void qt_apple_check_os_version()
+{
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+ const char *os = "iOS";
+ const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
+#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
+ const char *os = "tvOS";
+ const int version = __TV_OS_VERSION_MIN_REQUIRED;
+#elif defined(__WATCH_OS_VERSION_MIN_REQUIRED)
+ const char *os = "watchOS";
+ const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ const char *os = "macOS";
+ const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
+#endif
+ const NSOperatingSystemVersion required = (NSOperatingSystemVersion){
+ version / 10000, version / 100 % 100, version % 100};
+ const NSOperatingSystemVersion current = NSProcessInfo.processInfo.operatingSystemVersion;
+ if (![NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:required]) {
+ fprintf(stderr, "You can't use this version of %s with this version of %s. "
+ "You have %s %ld.%ld.%ld. Qt requires %s %ld.%ld.%ld or later.\n",
+ (reinterpret_cast<const NSString *>(
+ NSBundle.mainBundle.infoDictionary[@"CFBundleName"]).UTF8String),
+ os,
+ os, long(current.majorVersion), long(current.minorVersion), long(current.patchVersion),
+ os, long(required.majorVersion), long(required.minorVersion), long(required.patchVersion));
+ abort();
+ }
+}
+
// -------------------------------------------------------------------------
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index b87babc07a..13143a08bb 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -68,6 +68,7 @@
#endif
#include "qstring.h"
+#include "qscopedpointer.h"
#if defined( __OBJC__) && defined(QT_NAMESPACE)
#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__)
@@ -76,6 +77,37 @@
#endif
QT_BEGIN_NAMESPACE
+template <typename T, typename U, U (*RetainFunction)(U), void (*ReleaseFunction)(U)>
+class QAppleRefCounted
+{
+public:
+ QAppleRefCounted(const T &t = T()) : value(t) {}
+ QAppleRefCounted(QAppleRefCounted &&other) : value(other.value) { other.value = T(); }
+ QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); }
+ ~QAppleRefCounted() { if (value) ReleaseFunction(value); }
+ operator T() { return value; }
+ void swap(QAppleRefCounted &other) Q_DECL_NOEXCEPT_EXPR(noexcept(qSwap(value, other.value)))
+ { qSwap(value, other.value); }
+ QAppleRefCounted &operator=(const QAppleRefCounted &other)
+ { QAppleRefCounted copy(other); swap(copy); return *this; }
+ QAppleRefCounted &operator=(QAppleRefCounted &&other)
+ { QAppleRefCounted moved(std::move(other)); swap(moved); return *this; }
+ T *operator&() { return &value; }
+protected:
+ T value;
+};
+
+
+#ifdef Q_OS_MACOS
+class QMacRootLevelAutoReleasePool
+{
+public:
+ QMacRootLevelAutoReleasePool();
+ ~QMacRootLevelAutoReleasePool();
+private:
+ QScopedPointer<QMacAutoReleasePool> pool;
+};
+#endif
/*
Helper class that automates refernce counting for CFtypes.
@@ -90,33 +122,17 @@ QT_BEGIN_NAMESPACE
HIThemeGet*Shape functions, which in reality are "Copy" functions.
*/
template <typename T>
-class Q_CORE_EXPORT QCFType
+class QCFType : public QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>
{
public:
- inline QCFType(const T &t = 0) : type(t) {}
- inline QCFType(const QCFType &helper) : type(helper.type) { if (type) CFRetain(type); }
- inline ~QCFType() { if (type) CFRelease(type); }
- inline operator T() { return type; }
- inline QCFType operator =(const QCFType &helper)
- {
- if (helper.type)
- CFRetain(helper.type);
- CFTypeRef type2 = type;
- type = helper.type;
- if (type2)
- CFRelease(type2);
- return *this;
- }
- inline T *operator&() { return &type; }
- template <typename X> X as() const { return reinterpret_cast<X>(type); }
+ using QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>::QAppleRefCounted;
+ template <typename X> X as() const { return reinterpret_cast<X>(this->value); }
static QCFType constructFromGet(const T &t)
{
if (t)
CFRetain(t);
return QCFType<T>(t);
}
-protected:
- T type;
};
class Q_CORE_EXPORT QCFString : public QCFType<CFStringRef>
@@ -137,6 +153,12 @@ Q_CORE_EXPORT QChar qt_mac_qtKey2CocoaKey(Qt::Key key);
Q_CORE_EXPORT Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode);
#endif
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool);
+#endif
+
+Q_CORE_EXPORT void qt_apple_check_os_version();
+
QT_END_NAMESPACE
#endif // QCORE_MAC_P_H
diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp
index 686143f8c7..3b0da136ca 100644
--- a/src/corelib/kernel/qcore_unix.cpp
+++ b/src/corelib/kernel/qcore_unix.cpp
@@ -50,6 +50,37 @@
QT_BEGIN_NAMESPACE
+QByteArray qt_readlink(const char *path)
+{
+#ifndef PATH_MAX
+ // suitably large value that won't consume too much memory
+# define PATH_MAX 1024*1024
+#endif
+
+ QByteArray buf(256, Qt::Uninitialized);
+
+ ssize_t len = ::readlink(path, buf.data(), buf.size());
+ while (len == buf.size()) {
+ // readlink(2) will fill our buffer and not necessarily terminate with NUL;
+ if (buf.size() >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return QByteArray();
+ }
+
+ // double the size and try again
+ buf.resize(buf.size() * 2);
+ len = ::readlink(path, buf.data(), buf.size());
+ }
+
+ if (len == -1)
+ return QByteArray();
+
+ buf.resize(len);
+ return buf;
+}
+
+#ifndef QT_BOOTSTRAPPED
+
#if QT_CONFIG(poll_pollts)
# define ppoll pollts
#endif
@@ -121,4 +152,6 @@ int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout
}
}
+#endif // QT_BOOTSTRAPPED
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h
index 86bace3260..f78d2b9f24 100644
--- a/src/corelib/kernel/qcore_unix_p.h
+++ b/src/corelib/kernel/qcore_unix_p.h
@@ -55,6 +55,7 @@
#include <QtCore/private/qglobal_p.h>
#include "qplatformdefs.h"
#include "qatomic.h"
+#include "qbytearray.h"
#ifndef Q_OS_UNIX
# error "qcore_unix_p.h included on a non-Unix system"
@@ -102,6 +103,8 @@ struct sockaddr;
QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(pollfd, Q_PRIMITIVE_TYPE);
+
// Internal operator functions for timespecs
inline timespec &normalizedTimespec(timespec &t)
{
@@ -183,10 +186,11 @@ static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 07
int fd;
EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode));
- // unknown flags are ignored, so we have no way of verifying if
- // O_CLOEXEC was accepted
+#ifndef O_CLOEXEC
if (fd != -1)
::fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+
return fd;
}
#undef QT_OPEN
@@ -337,6 +341,22 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
// in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp
timespec qt_gettime() Q_DECL_NOTHROW;
void qt_nanosleep(timespec amount);
+QByteArray qt_readlink(const char *path);
+
+/* non-static */
+inline bool qt_haveLinuxProcfs()
+{
+#ifdef Q_OS_LINUX
+# ifdef QT_LINUX_ALWAYS_HAVE_PROCFS
+ return true;
+# else
+ static const bool present = (access("/proc/version", F_OK) == 0);
+ return present;
+# endif
+#else
+ return false;
+#endif
+}
Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index cba279c184..609e52d9d2 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -263,7 +263,7 @@ Q_GLOBAL_STATIC(QStartUpFuncList, preRList)
typedef QList<QtCleanUpFunction> QVFuncList;
Q_GLOBAL_STATIC(QVFuncList, postRList)
#ifndef QT_NO_QOBJECT
-static QBasicMutex globalPreRoutinesMutex;
+static QBasicMutex globalRoutinesMutex;
#endif
/*!
@@ -277,13 +277,15 @@ void qAddPreRoutine(QtStartUpFunction p)
QStartUpFuncList *list = preRList();
if (!list)
return;
+
+ if (QCoreApplication::instance())
+ p();
+
// Due to C++11 parallel dynamic initialization, this can be called
// from multiple threads.
#ifndef QT_NO_THREAD
- QMutexLocker locker(&globalPreRoutinesMutex);
+ QMutexLocker locker(&globalRoutinesMutex);
#endif
- if (QCoreApplication::instance())
- p();
list->prepend(p); // in case QCoreApplication is re-created, see qt_call_pre_routines
}
@@ -292,6 +294,9 @@ void qAddPostRoutine(QtCleanUpFunction p)
QVFuncList *list = postRList();
if (!list)
return;
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(&globalRoutinesMutex);
+#endif
list->prepend(p);
}
@@ -300,6 +305,9 @@ void qRemovePostRoutine(QtCleanUpFunction p)
QVFuncList *list = postRList();
if (!list)
return;
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(&globalRoutinesMutex);
+#endif
list->removeAll(p);
}
@@ -308,15 +316,18 @@ static void qt_call_pre_routines()
if (!preRList.exists())
return;
+ QVFuncList list;
+ {
#ifndef QT_NO_THREAD
- QMutexLocker locker(&globalPreRoutinesMutex);
+ QMutexLocker locker(&globalRoutinesMutex);
#endif
- QVFuncList *list = &(*preRList);
- // Unlike qt_call_post_routines, we don't empty the list, because
- // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects
- // the function to be executed every time QCoreApplication is created.
- for (int i = 0; i < list->count(); ++i)
- list->at(i)();
+ // Unlike qt_call_post_routines, we don't empty the list, because
+ // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects
+ // the function to be executed every time QCoreApplication is created.
+ list = *preRList;
+ }
+ for (int i = 0; i < list.count(); ++i)
+ list.at(i)();
}
void Q_CORE_EXPORT qt_call_post_routines()
@@ -324,9 +335,21 @@ void Q_CORE_EXPORT qt_call_post_routines()
if (!postRList.exists())
return;
- QVFuncList *list = &(*postRList);
- while (!list->isEmpty())
- (list->takeFirst())();
+ forever {
+ QVFuncList list;
+ {
+ // extract the current list and make the stored list empty
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(&globalRoutinesMutex);
+#endif
+ qSwap(*postRList, list);
+ }
+
+ if (list.isEmpty())
+ break;
+ for (QtCleanUpFunction f : qAsConst(list))
+ f();
+ }
}
@@ -437,6 +460,9 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint
, q_ptr(0)
#endif
{
+#if defined(Q_OS_DARWIN)
+ qt_apple_check_os_version();
+#endif
app_compile_version = flags & 0xffffff;
static const char *const empty = "";
if (argc == 0 || argv == 0) {
@@ -1019,6 +1045,21 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
}
/*!
+ \internal
+ \since 5.10
+
+ Forwards the \a event to the \a receiver, using the spontaneous
+ state of the \a originatingEvent if specified.
+*/
+bool QCoreApplication::forwardEvent(QObject *receiver, QEvent *event, QEvent *originatingEvent)
+{
+ if (event && originatingEvent)
+ event->spont = originatingEvent->spont;
+
+ return notifyInternal2(receiver, event);
+}
+
+/*!
Sends \a event to \a receiver: \a {receiver}->event(\a event).
Returns the value that is returned from the receiver's event
handler. Note that this function is called for all events sent to
@@ -1916,7 +1957,10 @@ bool QCoreApplication::installTranslator(QTranslator *translationFile)
if (!QCoreApplicationPrivate::checkInstance("installTranslator"))
return false;
QCoreApplicationPrivate *d = self->d_func();
- d->translators.prepend(translationFile);
+ {
+ QWriteLocker locker(&d->translateMutex);
+ d->translators.prepend(translationFile);
+ }
#ifndef QT_NO_TRANSLATION_BUILDER
if (translationFile->isEmpty())
@@ -1948,8 +1992,10 @@ bool QCoreApplication::removeTranslator(QTranslator *translationFile)
if (!QCoreApplicationPrivate::checkInstance("removeTranslator"))
return false;
QCoreApplicationPrivate *d = self->d_func();
+ QWriteLocker locker(&d->translateMutex);
if (d->translators.removeAll(translationFile)) {
#ifndef QT_NO_QOBJECT
+ locker.unlock();
if (!self->closingDown()) {
QEvent ev(QEvent::LanguageChange);
QCoreApplication::sendEvent(self, &ev);
@@ -1985,7 +2031,7 @@ static void replacePercentN(QString *result, int n)
}
/*!
- \reentrant
+ \threadsafe
Returns the translation text for \a sourceText, by querying the
installed translation files. The translation files are searched
@@ -2014,13 +2060,7 @@ static void replacePercentN(QString *result, int n)
This function is not virtual. You can use alternative translation
techniques by subclassing \l QTranslator.
- \warning This method is reentrant only if all translators are
- installed \e before calling this method. Installing or removing
- translators while performing translations is not supported. Doing
- so will most likely result in crashes or other undesirable
- behavior.
-
- \sa QObject::tr(), installTranslator()
+ \sa QObject::tr(), installTranslator(), removeTranslator(), translate()
*/
QString QCoreApplication::translate(const char *context, const char *sourceText,
const char *disambiguation, int n)
@@ -2030,14 +2070,18 @@ QString QCoreApplication::translate(const char *context, const char *sourceText,
if (!sourceText)
return result;
- if (self && !self->d_func()->translators.isEmpty()) {
- QList<QTranslator*>::ConstIterator it;
- QTranslator *translationFile;
- for (it = self->d_func()->translators.constBegin(); it != self->d_func()->translators.constEnd(); ++it) {
- translationFile = *it;
- result = translationFile->translate(context, sourceText, disambiguation, n);
- if (!result.isNull())
- break;
+ if (self) {
+ QCoreApplicationPrivate *d = self->d_func();
+ QReadLocker locker(&d->translateMutex);
+ if (!d->translators.isEmpty()) {
+ QList<QTranslator*>::ConstIterator it;
+ QTranslator *translationFile;
+ for (it = d->translators.constBegin(); it != d->translators.constEnd(); ++it) {
+ translationFile = *it;
+ result = translationFile->translate(context, sourceText, disambiguation, n);
+ if (!result.isNull())
+ break;
+ }
}
}
@@ -2061,8 +2105,11 @@ QString qtTrId(const char *id, int n)
bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator)
{
- return QCoreApplication::self
- && QCoreApplication::self->d_func()->translators.contains(translator);
+ if (!QCoreApplication::self)
+ return false;
+ QCoreApplicationPrivate *d = QCoreApplication::self->d_func();
+ QReadLocker locker(&d->translateMutex);
+ return d->translators.contains(translator);
}
#else
@@ -2840,6 +2887,7 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc
/*!
\fn void qAddPostRoutine(QtCleanUpFunction ptr)
+ \threadsafe
\relates QCoreApplication
Adds a global routine that will be called from the QCoreApplication
@@ -2853,10 +2901,10 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc
\snippet code/src_corelib_kernel_qcoreapplication.cpp 4
- Note that for an application- or module-wide cleanup, qaddPostRoutine()
+ Note that for an application- or module-wide cleanup, qAddPostRoutine()
is often not suitable. For example, if the program is split into dynamically
loaded modules, the relevant module may be unloaded long before the
- QCoreApplication destructor is called. In such cases, if using qaddPostRoutine()
+ QCoreApplication destructor is called. In such cases, if using qAddPostRoutine()
is still desirable, qRemovePostRoutine() can be used to prevent a routine
from being called by the QCoreApplication destructor. For example, if that
routine was called before the module was unloaded.
@@ -2872,11 +2920,14 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc
By selecting the right parent object, this can often be made to
clean up the module's data at the right moment.
+ \note This function has been thread-safe since Qt 5.10.
+
\sa qRemovePostRoutine()
*/
/*!
\fn void qRemovePostRoutine(QtCleanUpFunction ptr)
+ \threadsafe
\relates QCoreApplication
\since 5.3
@@ -2885,6 +2936,8 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc
must have been previously added to the list by a call to
qAddPostRoutine(), otherwise this function has no effect.
+ \note This function has been thread-safe since Qt 5.10.
+
\sa qAddPostRoutine()
*/
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 0fee7b3de8..6555733c4e 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -139,7 +139,7 @@ public:
static QString applicationDirPath();
static QString applicationFilePath();
- static qint64 applicationPid();
+ static qint64 applicationPid() Q_DECL_CONST_FUNCTION;
#if QT_CONFIG(library)
static void setLibraryPaths(const QStringList &);
@@ -206,6 +206,7 @@ private:
QT_DEPRECATED bool notifyInternal(QObject *receiver, QEvent *event); // ### Qt6 BIC: remove me
# endif
static bool notifyInternal2(QObject *receiver, QEvent *);
+ static bool forwardEvent(QObject *receiver, QEvent *event, QEvent *originatingEvent = nullptr);
#endif
static QCoreApplication *self;
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index c646786296..cd995c17f1 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -58,6 +58,10 @@
#include "private/qobject_p.h"
#endif
+#ifdef Q_OS_MACOS
+#include "private/qcore_mac_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
typedef QList<QTranslator*> QTranslatorList;
@@ -134,7 +138,7 @@ public:
#ifndef QT_NO_TRANSLATION
QTranslatorList translators;
-
+ QReadWriteLock translateMutex;
static bool isTranslatorInstalled(QTranslator *translator);
#endif
diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp
index 50888dd0aa..00548365d2 100644
--- a/src/corelib/kernel/qcoreapplication_win.cpp
+++ b/src/corelib/kernel/qcoreapplication_win.cpp
@@ -67,43 +67,33 @@ int appCmdShow = 0;
Q_CORE_EXPORT QString qAppFileName() // get application file name
{
- // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths
- // up to, and including MAX_PATH size perfectly fine with string termination, as well
- // as easily detect if the file path is indeed larger than MAX_PATH, in which case we
- // need to use the heap instead. This is a work-around, since contrary to what the
- // MSDN documentation states, GetModuleFileName sometimes doesn't set the
- // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if
- // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH.
- // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal
- // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use
- // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok).
- wchar_t buffer[MAX_PATH + 2];
- DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1);
- buffer[MAX_PATH + 1] = 0;
-
- if (v == 0)
- return QString();
- else if (v <= MAX_PATH)
- return QString::fromWCharArray(buffer);
-
- // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap
- wchar_t *b = 0;
- int i = 1;
- size_t size;
+ /*
+ GetModuleFileName() returns the length of the module name, when it has
+ space to store it and 0-terminate; this return is necessarily smaller than
+ the buffer size, as it doesn't include the terminator. When it lacks
+ space, the function returns the full size of the buffer and fills the
+ buffer, truncating the full path to make it fit. We have reports that
+ GetModuleFileName sometimes doesn't set the error number to
+ ERROR_INSUFFICIENT_BUFFER, as claimed by the MSDN documentation; so we
+ only trust the answer when the return is actually less than the buffer
+ size we pass in. (When truncating, except on XP, it does so by enough to
+ still have space to 0-terminate; in either case, it fills the claimed
+ space and returns the size of the space. While XP might thus give us the
+ full name, without a 0 terminator, and return its actual length, we can
+ never be sure that's what's happened until a later call with bigger buffer
+ confirms it by returning less than its buffer size.)
+ */
+ // Full path may be longer than MAX_PATH - expand until we have enough space:
+ QVarLengthArray<wchar_t, MAX_PATH + 1> space;
+ DWORD v;
+ size_t size = 1;
do {
- ++i;
- size = MAX_PATH * i;
- b = reinterpret_cast<wchar_t *>(realloc(b, (size + 1) * sizeof(wchar_t)));
- if (b)
- v = GetModuleFileName(NULL, b, DWORD(size));
- } while (b && v == size);
-
- if (b)
- *(b + size) = 0;
- QString res = QString::fromWCharArray(b);
- free(b);
+ size += MAX_PATH;
+ space.resize(int(size));
+ v = GetModuleFileName(NULL, space.data(), DWORD(space.size()));
+ } while (Q_UNLIKELY(v >= size));
- return res;
+ return QString::fromWCharArray(space.data(), v);
}
QString QCoreApplicationPrivate::appName() const
diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h
index d728040343..bcf5b10baa 100644
--- a/src/corelib/kernel/qeventdispatcher_unix_p.h
+++ b/src/corelib/kernel/qeventdispatcher_unix_p.h
@@ -55,7 +55,6 @@
#include "QtCore/qlist.h"
#include "private/qabstracteventdispatcher_p.h"
#include "private/qcore_unix_p.h"
-#include "private/qpodlist_p.h"
#include "QtCore/qvarlengtharray.h"
#include "private/qtimerinfo_unix_p.h"
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 7a6149b405..bbd442d570 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -53,6 +53,7 @@
#include "qcoreapplication_p.h"
#include <private/qthread_p.h>
#include <private/qmutexpool_p.h>
+#include <private/qwineventnotifier_p.h>
QT_BEGIN_NAMESPACE
@@ -96,13 +97,14 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: threadId(GetCurrentThreadId()), interrupt(false), closingDown(false), internalHwnd(0),
getMessageHook(0), serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0),
- wakeUps(0)
- , activateNotifiersPosted(false)
+ wakeUps(0), activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL)
{
}
QEventDispatcherWin32Private::~QEventDispatcherWin32Private()
{
+ if (winEventNotifierActivatedEvent)
+ CloseHandle(winEventNotifierActivatedEvent);
if (internalHwnd)
DestroyWindow(internalHwnd);
}
@@ -538,12 +540,14 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = 0;
- HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
+ DWORD nCount = 0;
+ HANDLE *pHandles = nullptr;
+ if (d->winEventNotifierActivatedEvent) {
+ nCount = 1;
+ pHandles = &d->winEventNotifierActivatedEvent;
+ }
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
- DWORD nCount = d->winEventNotifierList.count();
- Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
-
MSG msg;
bool haveMessage;
@@ -585,8 +589,6 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
}
if (!haveMessage) {
// no message - check for signalled objects
- for (int i=0; i<(int)nCount; i++)
- pHandles[i] = d->winEventNotifierList.at(i)->handle();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
// a new message has arrived, process it
@@ -627,7 +629,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
DispatchMessage(&msg);
}
} else if (waitRet - WAIT_OBJECT_0 < nCount) {
- d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
+ activateEventNotifiers();
} else {
// nothing todo so break
break;
@@ -640,16 +642,11 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
- DWORD nCount = d->winEventNotifierList.count();
- Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
- for (int i=0; i<(int)nCount; i++)
- pHandles[i] = d->winEventNotifierList.at(i)->handle();
-
emit aboutToBlock();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
if (waitRet - WAIT_OBJECT_0 < nCount) {
- d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
+ activateEventNotifiers();
retVal = true;
}
}
@@ -907,12 +904,12 @@ bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier)
if (d->winEventNotifierList.contains(notifier))
return true;
- if (d->winEventNotifierList.count() >= MAXIMUM_WAIT_OBJECTS - 2) {
- qWarning("QWinEventNotifier: Cannot have more than %d enabled at one time", MAXIMUM_WAIT_OBJECTS - 2);
- return false;
- }
d->winEventNotifierList.append(notifier);
- return true;
+
+ if (!d->winEventNotifierActivatedEvent)
+ d->winEventNotifierActivatedEvent = CreateEvent(0, TRUE, FALSE, nullptr);
+
+ return QWinEventNotifierPrivate::get(notifier)->registerWaitObject();
}
void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier)
@@ -928,17 +925,36 @@ void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier)
Q_D(QEventDispatcherWin32);
int i = d->winEventNotifierList.indexOf(notifier);
- if (i != -1)
- d->winEventNotifierList.takeAt(i);
+ if (i == -1)
+ return;
+ d->winEventNotifierList.takeAt(i);
+ QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier);
+ if (nd->waitHandle)
+ nd->unregisterWaitObject();
}
void QEventDispatcherWin32::activateEventNotifiers()
{
Q_D(QEventDispatcherWin32);
- //### this could break if events are removed/added in the activation
- for (int i=0; i<d->winEventNotifierList.count(); i++) {
- if (WaitForSingleObjectEx(d->winEventNotifierList.at(i)->handle(), 0, TRUE) == WAIT_OBJECT_0)
- d->activateEventNotifier(d->winEventNotifierList.at(i));
+ ResetEvent(d->winEventNotifierActivatedEvent);
+
+ // Iterate backwards, because the notifier might remove itself on activate().
+ for (int i = d->winEventNotifierList.count(); --i >= 0;) {
+ QWinEventNotifier *notifier = d->winEventNotifierList.at(i);
+ QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier);
+ if (nd->signaledCount.load() != 0) {
+ --nd->signaledCount;
+ nd->unregisterWaitObject();
+ d->activateEventNotifier(notifier);
+ }
+ }
+
+ // Re-register the remaining activated notifiers.
+ for (int i = 0; i < d->winEventNotifierList.count(); ++i) {
+ QWinEventNotifier *notifier = d->winEventNotifierList.at(i);
+ QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier);
+ if (!nd->waitHandle)
+ nd->registerWaitObject();
}
}
diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h
index f6d1bffdf5..683c7f8f36 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -161,6 +161,7 @@ class Q_CORE_EXPORT QEventDispatcherWin32Private : public QAbstractEventDispatch
public:
QEventDispatcherWin32Private();
~QEventDispatcherWin32Private();
+ static QEventDispatcherWin32Private *get(QEventDispatcherWin32 *q) { return q->d_func(); }
DWORD threadId;
@@ -192,6 +193,7 @@ public:
void postActivateSocketNotifiers();
void doWsaAsyncSelect(int socket, long event);
+ HANDLE winEventNotifierActivatedEvent;
QList<QWinEventNotifier *> winEventNotifierList;
void activateEventNotifier(QWinEventNotifier * wen);
diff --git a/src/corelib/kernel/qfunctions_fake_env_p.h b/src/corelib/kernel/qfunctions_fake_env_p.h
index 16d18c4d88..7010d2cf5d 100644
--- a/src/corelib/kernel/qfunctions_fake_env_p.h
+++ b/src/corelib/kernel/qfunctions_fake_env_p.h
@@ -77,7 +77,9 @@ struct NameEquals {
{ return qstrcmp(other.name, name) == 0; }
};
+#ifndef Q_CLANG_QDOC
Q_GLOBAL_STATIC(QVector<Variable>, qt_app_environment)
+#endif
errno_t qt_fake_getenv_s(size_t *sizeNeeded, char *buffer, size_t bufferSize, const char *varName)
{
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index cb4b93905e..02c58858ff 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -75,6 +75,11 @@ static jmethodID g_hideSplashScreenMethodID = Q_NULLPTR;
Q_GLOBAL_STATIC(std::deque<QtAndroidPrivate::Runnable>, g_pendingRunnables);
static QBasicMutex g_pendingRunnablesMutex;
+Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr));
+Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
+Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
+Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
+
class PermissionsResultClass : public QObject
{
Q_OBJECT
@@ -406,7 +411,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
"runPendingCppRunnablesOnAndroidThread",
"()V");
- g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "()V");
+ g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "(I)V");
g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative));
env->DeleteLocalRef(jQtNative);
@@ -511,7 +516,7 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis
}, env);
}
-QHash<QString, QtAndroidPrivate::PermissionsResult> QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs)
+QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs)
{
QSharedPointer<QHash<QString, QtAndroidPrivate::PermissionsResult>> res(new QHash<QString, QtAndroidPrivate::PermissionsResult>());
QSharedPointer<QSemaphore> sem(new QSemaphore);
@@ -567,9 +572,36 @@ void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventList
g_keyEventListeners()->listeners.removeOne(listener);
}
-void QtAndroidPrivate::hideSplashScreen(JNIEnv *env)
+void QtAndroidPrivate::hideSplashScreen(JNIEnv *env, int duration)
+{
+ env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID, duration);
+}
+
+void QtAndroidPrivate::waitForServiceSetup()
+{
+ g_waitForServiceSetupSemaphore->acquire();
+}
+
+int QtAndroidPrivate::acuqireServiceSetup(int flags)
+{
+ g_serviceSetupLockers->ref();
+ return flags;
+}
+
+void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
+{
+ QMutexLocker lock(g_onBindListenerMutex);
+ *g_onBindListener = listener;
+ if (!(*g_serviceSetupLockers)--)
+ g_waitForServiceSetupSemaphore->release();
+}
+
+jobject QtAndroidPrivate::callOnBindListener(jobject intent)
{
- env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID);
+ QMutexLocker lock(g_onBindListenerMutex);
+ if (g_onBindListener)
+ return (*g_onBindListener)->onBind(intent);
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index d88e4fc19e..ea5103c173 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -100,6 +100,13 @@ namespace QtAndroidPrivate
virtual bool handleKeyEvent(jobject event) = 0;
};
+ class Q_CORE_EXPORT OnBindListener
+ {
+ public:
+ virtual ~OnBindListener() {}
+ virtual jobject onBind(jobject intent) = 0;
+ };
+
enum class PermissionsResult {
Granted,
Denied
@@ -142,7 +149,14 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener);
Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener);
- Q_CORE_EXPORT void hideSplashScreen(JNIEnv *env);
+ Q_CORE_EXPORT void hideSplashScreen(JNIEnv *env, int duration = 0);
+
+
+ Q_CORE_EXPORT void waitForServiceSetup();
+ Q_CORE_EXPORT int acuqireServiceSetup(int flags);
+ Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener);
+ Q_CORE_EXPORT jobject callOnBindListener(jobject intent);
+
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h
index 773884047a..305e9065e9 100644
--- a/src/corelib/kernel/qmath.h
+++ b/src/corelib/kernel/qmath.h
@@ -149,6 +149,8 @@ inline qreal qPow(qreal x, qreal y)
return pow(x, y);
}
+// TODO: use template variables (e.g. Qt::pi<type>) for these once we have C++14 support:
+
#ifndef M_E
#define M_E (2.7182818284590452354)
#endif
@@ -242,16 +244,13 @@ Q_DECL_CONSTEXPR inline double qRadiansToDegrees(double radians)
}
-#if defined(QT_HAS_BUILTIN_CLZ)
-inline quint32 qNextPowerOfTwo(quint32 v)
+Q_DECL_RELAXED_CONSTEXPR inline quint32 qNextPowerOfTwo(quint32 v)
{
+#if defined(QT_HAS_BUILTIN_CLZ)
if (v == 0)
return 1;
return 2U << (31 ^ QAlgorithmsPrivate::qt_builtin_clz(v));
-}
#else
-inline quint32 qNextPowerOfTwo(quint32 v)
-{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
@@ -259,19 +258,16 @@ inline quint32 qNextPowerOfTwo(quint32 v)
v |= v >> 16;
++v;
return v;
-}
#endif
+}
-#if defined(QT_HAS_BUILTIN_CLZLL)
-inline quint64 qNextPowerOfTwo(quint64 v)
+Q_DECL_RELAXED_CONSTEXPR inline quint64 qNextPowerOfTwo(quint64 v)
{
+#if defined(QT_HAS_BUILTIN_CLZLL)
if (v == 0)
return 1;
return Q_UINT64_C(2) << (63 ^ QAlgorithmsPrivate::qt_builtin_clzll(v));
-}
#else
-inline quint64 qNextPowerOfTwo(quint64 v)
-{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
@@ -280,15 +276,15 @@ inline quint64 qNextPowerOfTwo(quint64 v)
v |= v >> 32;
++v;
return v;
-}
#endif
+}
-inline quint32 qNextPowerOfTwo(qint32 v)
+Q_DECL_RELAXED_CONSTEXPR inline quint32 qNextPowerOfTwo(qint32 v)
{
return qNextPowerOfTwo(quint32(v));
}
-inline quint64 qNextPowerOfTwo(qint64 v)
+Q_DECL_RELAXED_CONSTEXPR inline quint64 qNextPowerOfTwo(qint64 v)
{
return qNextPowerOfTwo(quint64(v));
}
diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc
index 3a692d5935..a2e24e925b 100644
--- a/src/corelib/kernel/qmath.qdoc
+++ b/src/corelib/kernel/qmath.qdoc
@@ -35,7 +35,24 @@
These functions are partly convenience definitions for basic math operations
not available in the C or Standard Template Libraries.
- \pagekeywords math trigonometry qmath floor ceiling absolute sine cosine tangent inverse tan exponent power natural logarithm
+ The header also ensures some constants specified in POSIX, but not present
+ in C++ standards (so absent from <math.h> on some platforms), are defined:
+
+ \value M_E The base of the natural logarithms, e = exp(1)
+ \value M_LOG2E The base-two logarithm of e
+ \value M_LOG10E The base-ten logarithm of e
+ \value M_LN2 The natural logarithm of two
+ \value M_LN10 The natural logarithm of ten
+ \value M_PI The ratio of a circle's circumference to diameter, \unicode{0x3C0}
+ \value M_PI_2 Half M_PI, \unicode{0x3C0} / 2
+ \value M_PI_4 Quarter M_PI, \unicode{0x3C0} / 4
+ \value M_1_PI The inverse of M_PI, 1 / \unicode{0x3C0}
+ \value M_2_PI Twice the inverse of M_PI, 2 / \unicode{0x3C0}
+ \value M_2_SQRTPI Two divided by the square root of pi, 2 / \unicode{0x221A}\unicode{0x3C0}
+ \value M_SQRT2 The square root of two, \unicode{0x221A}2
+ \value M_SQRT1_2 The square roof of half, 1 / \unicode{0x221A}2
+
+ \pagekeywords math trigonometry qmath floor ceiling absolute sine cosine tangent inverse tan exponent power natural logarithm pi
*/
/*!
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index a8003f7e46..f07b463482 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -1489,6 +1489,51 @@ bool QMetaObject::invokeMethod(QObject *obj,
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
+bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret)
+{
+ if (! object)
+ return false;
+
+ QThread *currentThread = QThread::currentThread();
+ QThread *objectThread = object->thread();
+ if (type == Qt::AutoConnection)
+ type = (currentThread == objectThread) ? Qt::DirectConnection : Qt::QueuedConnection;
+
+ void *argv[] = { ret };
+
+ if (type == Qt::DirectConnection) {
+ slot->call(object, argv);
+ } else if (type == Qt::QueuedConnection) {
+ if (argv[0]) {
+ qWarning("QMetaObject::invokeMethod: Unable to invoke methods with return values in "
+ "queued connections");
+ return false;
+ }
+
+ // args and typesCopy will be deallocated by ~QMetaCallEvent() using free()
+ void **args = static_cast<void **>(calloc(1, sizeof(void *)));
+ Q_CHECK_PTR(args);
+
+ int *types = static_cast<int *>(calloc(1, sizeof(int)));
+ Q_CHECK_PTR(types);
+
+ QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 1, types, args));
+ } else if (type == Qt::BlockingQueuedConnection) {
+#ifndef QT_NO_THREAD
+ if (currentThread == objectThread)
+ qWarning("QMetaObject::invokeMethod: Dead lock detected");
+
+ QSemaphore semaphore;
+ QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 0, 0, argv, &semaphore));
+ semaphore.acquire();
+#endif // QT_NO_THREAD
+ } else {
+ qWarning("QMetaObject::invokeMethod: Unknown connection type");
+ return false;
+ }
+ return true;
+}
+
/*! \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(0),
@@ -1544,6 +1589,44 @@ bool QMetaObject::invokeMethod(QObject *obj,
*/
/*!
+ \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = Q_NULLPTR)
+
+ \since 5.10
+
+ \overload
+*/
+
+/*!
+ \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret)
+
+ \since 5.10
+
+ \overload
+
+ This overload invokes the member function using the connection type Qt::AutoConnection.
+*/
+
+/*!
+ \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = Q_NULLPTR)
+
+ \since 5.10
+
+ \overload
+
+ Call the functor in the event loop of \a context.
+*/
+
+/*!
+ \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret = Q_NULLPTR)
+
+ \since 5.10
+
+ \overload
+
+ Call the functor in the event loop of \a context using the connection type Qt::AutoConnection.
+*/
+
+/*!
\fn QMetaObject::Connection::Connection(const Connection &other)
Constructs a copy of \a other.
@@ -3264,7 +3347,21 @@ int QMetaProperty::notifySignalIndex() const
if (hasNotifySignal()) {
int offset = priv(mobj->d.data)->propertyData +
priv(mobj->d.data)->propertyCount * 3 + idx;
- return mobj->d.data[offset] + mobj->methodOffset();
+ int methodIndex = mobj->d.data[offset];
+ if (methodIndex & IsUnresolvedSignal) {
+ methodIndex &= ~IsUnresolvedSignal;
+ const QByteArray signalName = stringData(mobj, methodIndex);
+ const QMetaObject *m = mobj;
+ const int idx = indexOfMethodRelative<MethodSignal>(&m, signalName, 0, nullptr);
+ if (idx >= 0) {
+ return idx + m->methodOffset();
+ } else {
+ qWarning("QMetaProperty::notifySignal: cannot find the NOTIFY signal %s in class %s for property '%s'",
+ signalName.constData(), objectClassName(mobj), name());
+ return -1;
+ }
+ }
+ return methodIndex + mobj->methodOffset();
} else {
return -1;
}
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index e247c48703..434ef84808 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -111,7 +111,8 @@ enum MetaObjectFlags {
enum MetaDataFlags {
IsUnresolvedType = 0x80000000,
- TypeNameIndexMask = 0x7FFFFFFF
+ TypeNameIndexMask = 0x7FFFFFFF,
+ IsUnresolvedSignal = 0x70000000
};
enum EnumFlags {
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index 56f187a59d..e3b70638c6 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -190,12 +190,13 @@ class QMetaEnumBuilderPrivate
{
public:
QMetaEnumBuilderPrivate(const QByteArray& _name)
- : name(_name), isFlag(false)
+ : name(_name), isFlag(false), isScoped(false)
{
}
QByteArray name;
bool isFlag;
+ bool isScoped;
QList<QByteArray> keys;
QVector<int> values;
};
@@ -637,6 +638,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum& prototype)
{
QMetaEnumBuilder en = addEnumerator(prototype.name());
en.setIsFlag(prototype.isFlag());
+ en.setIsScoped(prototype.isScoped());
int count = prototype.keyCount();
for (int index = 0; index < count; ++index)
en.addKey(prototype.key(index), prototype.value(index));
@@ -1408,12 +1410,13 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
Q_ASSERT(!buf || dataIndex == pmeta->enumeratorData);
for (const auto &enumerator : d->enumerators) {
int name = strings.enter(enumerator.name);
- int isFlag = (int)(enumerator.isFlag);
+ int isFlag = enumerator.isFlag ? EnumIsFlag : 0;
+ int isScoped = enumerator.isScoped ? EnumIsScoped : 0;
int count = enumerator.keys.size();
int enumOffset = enumIndex;
if (buf) {
data[dataIndex] = name;
- data[dataIndex + 1] = isFlag;
+ data[dataIndex + 1] = isFlag | isScoped;
data[dataIndex + 2] = count;
data[dataIndex + 3] = enumOffset;
}
@@ -1641,6 +1644,7 @@ void QMetaObjectBuilder::serialize(QDataStream& stream) const
for (const auto &enumerator : d->enumerators) {
stream << enumerator.name;
stream << enumerator.isFlag;
+ stream << enumerator.isScoped;
stream << enumerator.keys;
stream << enumerator.values;
}
@@ -1807,6 +1811,7 @@ void QMetaObjectBuilder::deserialize
addEnumerator(name);
QMetaEnumBuilderPrivate &enumerator = d->enumerators[index];
stream >> enumerator.isFlag;
+ stream >> enumerator.isScoped;
stream >> enumerator.keys;
stream >> enumerator.values;
if (enumerator.keys.size() != enumerator.values.size()) {
@@ -2633,6 +2638,31 @@ void QMetaEnumBuilder::setIsFlag(bool value)
}
/*!
+ Return \c true if this enumerator should be considered scoped (C++11 enum class).
+
+ \sa setIsScoped()
+*/
+bool QMetaEnumBuilder::isScoped() const
+{
+ QMetaEnumBuilderPrivate *d = d_func();
+ if (d)
+ return d->isScoped;
+ return false;
+}
+
+/*!
+ Sets this enumerator to be a scoped enum if \value is true
+
+ \sa isScoped()
+*/
+void QMetaEnumBuilder::setIsScoped(bool value)
+{
+ QMetaEnumBuilderPrivate *d = d_func();
+ if (d)
+ d->isScoped = value;
+}
+
+/*!
Returns the number of keys.
\sa key(), addKey()
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 144595330d..03b2afaebc 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -300,6 +300,9 @@ public:
bool isFlag() const;
void setIsFlag(bool value);
+ bool isScoped() const;
+ void setIsScoped(bool value);
+
int keyCount() const;
QByteArray key(int index) const;
int value(int index) const;
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index b75f2ad9dc..e48807ea49 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -308,6 +308,7 @@ struct DefinedTypesFilter {
\omitvalue TrackingPointerToQObject
\omitvalue WasDeclaredAsMetaType
\omitvalue IsGadget This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5.
+ \omitvalue PointerToGadget
*/
/*!
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index d88f469e0f..f704c5b21a 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -462,7 +462,8 @@ public:
WeakPointerToQObject = 0x40,
TrackingPointerToQObject = 0x80,
WasDeclaredAsMetaType = 0x100,
- IsGadget = 0x200
+ IsGadget = 0x200,
+ PointerToGadget = 0x400
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
@@ -1388,6 +1389,19 @@ namespace QtPrivate
enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) };
};
+ template<typename T, typename Enable = void>
+ struct IsPointerToGadgetHelper { enum { Value = false }; };
+
+ template<typename T>
+ struct IsPointerToGadgetHelper<T*, typename T::QtGadgetHelper>
+ {
+ using BaseType = T;
+ template <typename X>
+ static char checkType(void (X::*)());
+ static void *checkType(void (T::*)());
+ enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) };
+ };
+
template<typename T> char qt_getEnumMetaObject(const T&);
@@ -1423,6 +1437,11 @@ namespace QtPrivate
static inline const QMetaObject *value() { return &T::staticMetaObject; }
};
template<typename T>
+ struct MetaObjectForType<T, typename QEnableIf<IsPointerToGadgetHelper<T>::Value>::Type>
+ {
+ static inline const QMetaObject *value() { return &IsPointerToGadgetHelper<T>::BaseType::staticMetaObject; }
+ };
+ template<typename T>
struct MetaObjectForType<T, typename std::enable_if<IsQEnumHelper<T>::Value>::type >
{
static inline const QMetaObject *value() { return qt_getEnumMetaObject(T()); }
@@ -1578,6 +1597,7 @@ namespace QtPrivate
template <typename T, int =
QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject :
QtPrivate::IsGadgetHelper<T>::Value ? QMetaType::IsGadget :
+ QtPrivate::IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget :
QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0>
struct QMetaTypeIdQObject
{
@@ -1631,6 +1651,7 @@ namespace QtPrivate {
| (IsTrackingPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::TrackingPointerToQObject : 0)
| (std::is_enum<T>::value ? QMetaType::IsEnumeration : 0)
| (IsGadgetHelper<T>::Value ? QMetaType::IsGadget : 0)
+ | (IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget : 0)
};
};
@@ -1798,6 +1819,30 @@ struct QMetaTypeIdQObject<T, QMetaType::IsGadget>
};
template <typename T>
+struct QMetaTypeIdQObject<T*, QMetaType::PointerToGadget>
+{
+ enum {
+ Defined = 1
+ };
+
+ static int qt_metatype_id()
+ {
+ static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (const int id = metatype_id.loadAcquire())
+ return id;
+ const char * const cName = T::staticMetaObject.className();
+ QByteArray typeName;
+ typeName.reserve(int(strlen(cName)) + 1);
+ typeName.append(cName).append('*');
+ const int newId = qRegisterNormalizedMetaType<T*>(
+ typeName,
+ reinterpret_cast<T**>(quintptr(-1)));
+ metatype_id.storeRelease(newId);
+ return newId;
+ }
+};
+
+template <typename T>
struct QMetaTypeIdQObject<T, QMetaType::IsEnumeration>
{
enum {
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 3f50716cd7..3b0f7ead09 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -2339,7 +2339,7 @@ static void err_info_about_objects(const char * func,
a thread different from this object's thread. Do not use this
function in this type of scenario.
- \sa senderSignalIndex(), QSignalMapper
+ \sa senderSignalIndex()
*/
QObject *QObject::sender() const
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 6941c55896..2e66daa914 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -304,7 +304,7 @@ public:
static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
{
- return connect(sender, signal, sender, slot, Qt::DirectConnection);
+ return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection);
}
//connect to a functor, with a "context" object defining in which event loop is going to be executed
@@ -334,7 +334,7 @@ public:
return connectImpl(sender, reinterpret_cast<void **>(&signal), context, Q_NULLPTR,
new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount,
typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value,
- typename SignalType::ReturnType>(slot),
+ typename SignalType::ReturnType>(std::move(slot)),
type, types, &SignalType::Object::staticMetaObject);
}
#endif //Q_QDOC
diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h
index d7ae63a98c..c775d807b1 100644
--- a/src/corelib/kernel/qobject_impl.h
+++ b/src/corelib/kernel/qobject_impl.h
@@ -74,60 +74,6 @@ namespace QtPrivate {
template <typename... Args> struct ConnectionTypes<List<Args...>, true>
{ static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper<Args>::qt_metatype_id())..., 0 }; return t; } };
- // internal base class (interface) containing functions required to call a slot managed by a pointer to function.
- class QSlotObjectBase {
- QAtomicInt m_ref;
- // don't use virtual functions here; we don't want the
- // compiler to create tons of per-polymorphic-class stuff that
- // we'll never need. We just use one function pointer.
- typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);
- const ImplFn m_impl;
- protected:
- enum Operation {
- Destroy,
- Call,
- Compare,
-
- NumOperations
- };
- public:
- explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}
-
- inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); }
- inline void destroyIfLastRef() Q_DECL_NOTHROW
- { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); }
-
- inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; }
- inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, Q_NULLPTR); }
- protected:
- ~QSlotObjectBase() {}
- private:
- Q_DISABLE_COPY(QSlotObjectBase)
- };
- // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject
- // Args and R are the List of arguments and the returntype of the signal to which the slot is connected.
- template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
- {
- typedef QtPrivate::FunctionPointer<Func> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
- {
- switch (which) {
- case Destroy:
- delete static_cast<QSlotObject*>(this_);
- break;
- case Call:
- FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
- break;
- case Compare:
- *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
- break;
- case NumOperations: ;
- }
- }
- public:
- explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
- };
// implementation of QSlotObjectBase for which the slot is a static function
// Args and R are the List of arguments and the returntype of the signal to which the slot is connected.
template<typename Func, typename Args, typename R> class QStaticSlotObject : public QSlotObjectBase
@@ -151,30 +97,6 @@ namespace QtPrivate {
public:
explicit QStaticSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
};
- // implementation of QSlotObjectBase for which the slot is a functor (or lambda)
- // N is the number of arguments
- // Args and R are the List of arguments and the returntype of the signal to which the slot is connected.
- template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
- {
- typedef QtPrivate::Functor<Func, N> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
- {
- switch (which) {
- case Destroy:
- delete static_cast<QFunctorSlotObject*>(this_);
- break;
- case Call:
- FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
- break;
- case Compare: // not implemented
- case NumOperations:
- Q_UNUSED(ret);
- }
- }
- public:
- explicit QFunctorSlotObject(const Func &f) : QSlotObjectBase(&impl), function(f) {}
- };
}
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index 7b9253ac64..ad88bcf274 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -235,7 +235,7 @@ public:
mutable quint32 connectedSignals[2];
union {
- QObject *currentChildBeingDeleted;
+ QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set
QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
};
@@ -244,6 +244,7 @@ public:
QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};
+Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_MOVABLE_TYPE);
/*! \internal
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index cec822ad14..5fc8937f23 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -57,7 +57,6 @@ struct QArrayData;
typedef QArrayData QByteArrayData;
class QString;
-
#ifndef Q_MOC_OUTPUT_REVISION
#define Q_MOC_OUTPUT_REVISION 67
#endif
@@ -467,6 +466,91 @@ struct Q_CORE_EXPORT QMetaObject
val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction, typename MemberFunctionReturnType>
+ static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = nullptr);
+ template<typename PointerToMemberFunction, typename MemberFunctionReturnType>
+ static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret);
+ template<typename Functor, typename FunctorReturnType>
+ static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
+ template<typename Functor, typename FunctorReturnType>
+ static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret);
+#else
+
+ // invokeMethod() for member function pointer
+ template <typename Func>
+ static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && !std::is_convertible<Func, const char*>::value
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
+ invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
+ Func function,
+ Qt::ConnectionType type = Qt::AutoConnection,
+ typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
+ {
+ return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret);
+ }
+
+ template <typename Func>
+ static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && !std::is_convertible<Func, const char*>::value
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
+ invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
+ Func function,
+ typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
+ {
+ return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret);
+ }
+
+ // invokeMethod() for function pointer (not member)
+ template <typename Func>
+ static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && !std::is_convertible<Func, const char*>::value
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
+ invokeMethod(QObject *context, Func function,
+ Qt::ConnectionType type = Qt::AutoConnection,
+ typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
+ {
+ return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret);
+ }
+
+ template <typename Func>
+ static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && !std::is_convertible<Func, const char*>::value
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
+ invokeMethod(QObject *context, Func function,
+ typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
+ {
+ return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret);
+ }
+
+ // invokeMethod() for Functor
+ template <typename Func>
+ static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
+ && !std::is_convertible<Func, const char*>::value, bool>::type
+ invokeMethod(QObject *context, Func function,
+ Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr)
+ {
+ return invokeMethodImpl(context,
+ new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function),
+ type,
+ ret);
+ }
+
+ template <typename Func>
+ static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
+ && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
+ && !std::is_convertible<Func, const char*>::value, bool>::type
+ invokeMethod(QObject *context, Func function, typename std::result_of<Func()>::type *ret)
+ {
+ return invokeMethodImpl(context,
+ new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function),
+ Qt::AutoConnection,
+ ret);
+ }
+
+#endif
+
QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
@@ -506,6 +590,9 @@ struct Q_CORE_EXPORT QMetaObject
const QMetaObject * const *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
+
+private:
+ static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
};
class Q_CORE_EXPORT QMetaObject::Connection {
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h
index 29ab77b269..b9f2e47e32 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -51,7 +51,7 @@
#endif
QT_BEGIN_NAMESPACE
-
+class QObject;
namespace QtPrivate {
template <typename T> struct RemoveRef { typedef T Type; };
@@ -350,6 +350,98 @@ namespace QtP