diff options
Diffstat (limited to 'src/corelib')
68 files changed, 2877 insertions, 1242 deletions
diff --git a/src/corelib/configure.json b/src/corelib/configure.json index c170b38977..f4162956d3 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -11,7 +11,7 @@ "icu": "boolean", "inotify": "boolean", "journald": "boolean", - "pcre": { "type": "enum", "values": [ "qt", "system" ] }, + "pcre2": { "type": "enum", "values": [ "qt", "system" ] }, "posix-ipc": { "type": "boolean", "name": "ipc_posix" }, "pps": { "type": "boolean", "name": "qqnx_pps" }, "slog2": "boolean", @@ -81,11 +81,11 @@ "-ldl" ] }, - "pcre": { - "label": "PCRE", - "test": "unix/pcre", + "pcre2": { + "label": "PCRE2", + "test": "unix/pcre2", "sources": [ - "-lpcre16" + "-lpcre2-16" ] }, "pps": { @@ -307,14 +307,14 @@ "section": "Utilities", "output": [ "publicFeature", "feature" ] }, - "system-pcre": { - "label": "Using system PCRE", - "disable": "input.pcre == 'qt'", - "enable": "input.pcre == 'system'", - "condition": "libs.pcre", + "system-pcre2": { + "label": "Using system PCRE2", + "disable": "input.pcre2 == 'qt'", + "enable": "input.pcre2 == 'system'", + "condition": "libs.pcre2", "output": [ "privateFeature", - { "type": "privateConfig", "negative": true, "name": "pcre" } + { "type": "privateConfig", "negative": true, "name": "pcre2" } ] }, "poll_ppoll": { @@ -629,7 +629,7 @@ Please apply the patch corresponding to your Standard Library vendor, found in "args": "qqnx_pps", "condition": "config.qnx" }, - "system-pcre" + "system-pcre2" ] } ] diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp index 9fd52517c2..f852988e9a 100644 --- a/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qobject.cpp @@ -48,14 +48,6 @@ ** ****************************************************************************/ -//! [0] -QLineEdit *lineEdit = static_cast<QLineEdit *>( - qt_find_obj_child(myWidget, "QLineEdit", "my line edit")); -if (lineEdit) - lineEdit->setText("Default"); -//! [0] - - //! [1] QObject *obj = new QPushButton; obj->metaObject()->className(); // returns "QPushButton" diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index 36655ca1dd..d23706e631 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -2,6 +2,8 @@ HEADERS += \ global/qglobal.h \ + global/qoperatingsystemversion.h \ + global/qoperatingsystemversion_p.h \ global/qsystemdetection.h \ global/qcompilerdetection.h \ global/qprocessordetection.h \ @@ -27,11 +29,15 @@ SOURCES += \ global/qlibraryinfo.cpp \ global/qmalloc.cpp \ global/qnumeric.cpp \ + global/qoperatingsystemversion.cpp \ global/qlogging.cpp \ global/qhooks.cpp VERSIONTAGGING_SOURCES = global/qversiontagging.cpp +darwin: SOURCES += global/qoperatingsystemversion_darwin.mm +win32: SOURCES += global/qoperatingsystemversion_win.cpp + # qlibraryinfo.cpp includes qconfig.cpp INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 6efdc4c22c..47aca712b7 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -45,6 +45,8 @@ #include "qthreadstorage.h" #include "qdir.h" #include "qdatetime.h" +#include "qoperatingsystemversion.h" +#include "qoperatingsystemversion_p.h" #include <private/qlocale_tools_p.h> #include <qmutex.h> @@ -1064,10 +1066,6 @@ bool qSharedBuild() Q_DECL_NOTHROW on which the application is compiled. \li \l ByteOrder specifies whether the platform is big-endian or little-endian. - \li \l WindowsVersion specifies the version of the Windows operating - system on which the application is run. - \li \l MacintoshVersion specifies the version of the Macintosh - operating system on which the application is run. \endlist Some constants are defined only on certain platforms. You can use @@ -1088,12 +1086,14 @@ bool qSharedBuild() Q_DECL_NOTHROW */ /*! + \deprecated \variable QSysInfo::WindowsVersion \brief the version of the Windows operating system on which the application is run. */ /*! + \deprecated \fn QSysInfo::WindowsVersion QSysInfo::windowsVersion() \since 4.4 @@ -1103,12 +1103,14 @@ bool qSharedBuild() Q_DECL_NOTHROW */ /*! + \deprecated \variable QSysInfo::MacintoshVersion \brief the version of the Macintosh operating system on which the application is run. */ /*! + \deprecated \fn QSysInfo::MacVersion QSysInfo::macVersion() Returns the version of Darwin (\macos or iOS) on which the @@ -1126,6 +1128,7 @@ bool qSharedBuild() Q_DECL_NOTHROW */ /*! + \deprecated \enum QSysInfo::WinVersion This enum provides symbolic names for the various versions of the @@ -1182,6 +1185,7 @@ bool qSharedBuild() Q_DECL_NOTHROW */ /*! + \deprecated \enum QSysInfo::MacVersion This enum provides symbolic names for the various versions of the @@ -1949,28 +1953,31 @@ QT_BEGIN_INCLUDE_NAMESPACE #include "qnamespace.h" QT_END_INCLUDE_NAMESPACE +#if QT_DEPRECATED_SINCE(5, 9) QSysInfo::MacVersion QSysInfo::macVersion() { - const QAppleOperatingSystemVersion version = qt_apple_os_version(); // qtcore_mac_objc.mm + const auto version = QOperatingSystemVersion::current(); #if defined(Q_OS_OSX) - return QSysInfo::MacVersion(Q_MV_OSX(version.major, version.minor)); + return QSysInfo::MacVersion(Q_MV_OSX(version.majorVersion(), version.minorVersion())); #elif defined(Q_OS_IOS) - return QSysInfo::MacVersion(Q_MV_IOS(version.major, version.minor)); + return QSysInfo::MacVersion(Q_MV_IOS(version.majorVersion(), version.minorVersion())); #elif defined(Q_OS_TVOS) - return QSysInfo::MacVersion(Q_MV_TVOS(version.major, version.minor)); + return QSysInfo::MacVersion(Q_MV_TVOS(version.majorVersion(), version.minorVersion())); #elif defined(Q_OS_WATCHOS) - return QSysInfo::MacVersion(Q_MV_WATCHOS(version.major, version.minor)); + return QSysInfo::MacVersion(Q_MV_WATCHOS(version.majorVersion(), version.minorVersion())); #else return QSysInfo::MV_Unknown; #endif } const QSysInfo::MacVersion QSysInfo::MacintoshVersion = QSysInfo::macVersion(); +#endif -#ifdef Q_OS_OSX -static const char *osxVer_helper(QAppleOperatingSystemVersion version = qt_apple_os_version()) +#ifdef Q_OS_DARWIN +static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) { - if (version.major == 10) { - switch (version.minor) { +#ifdef Q_OS_MACOS + if (version.majorVersion() == 10) { + switch (version.minorVersion()) { case 9: return "Mavericks"; case 10: @@ -1982,6 +1989,9 @@ static const char *osxVer_helper(QAppleOperatingSystemVersion version = qt_apple } } // unknown, future version +#else + Q_UNUSED(version); +#endif return 0; } #endif @@ -2022,140 +2032,30 @@ QWindowsSockInit::~QWindowsSockInit() Q_GLOBAL_STATIC(QWindowsSockInit, winsockInit) # endif // QT_BOOTSTRAPPED -#ifdef Q_OS_WINRT -static inline HMODULE moduleHandleForFunction(LPCVOID address) -{ - // This is a widely used, decades-old technique for retrieving the handle - // of a module and is effectively equivalent to GetModuleHandleEx - // (which is unavailable on WinRT) - MEMORY_BASIC_INFORMATION mbi = { 0, 0, 0, 0, 0, 0, 0 }; - if (VirtualQuery(address, &mbi, sizeof(mbi)) == 0) - return 0; - return reinterpret_cast<HMODULE>(mbi.AllocationBase); -} -#endif - -static inline OSVERSIONINFOEX determineWinOsVersion() -{ - OSVERSIONINFOEX result = { sizeof(OSVERSIONINFOEX), 0, 0, 0, 0, {'\0'}, 0, 0, 0, 0, 0}; - -#define GetProcAddressA GetProcAddress - - // GetModuleHandle is not supported in WinRT and linking to it at load time - // will not pass the Windows App Certification Kit... but it exists and is functional, - // so use some unusual but widely used techniques to get a pointer to it -#ifdef Q_OS_WINRT - // 1. Get HMODULE of kernel32.dll, using the address of some function exported by that DLL - HMODULE kernelModule = moduleHandleForFunction(reinterpret_cast<LPCVOID>(VirtualQuery)); - if (Q_UNLIKELY(!kernelModule)) - return result; - - // 2. Get pointer to GetModuleHandle so we can then load other arbitrary modules (DLLs) - typedef HMODULE(WINAPI *GetModuleHandleFunction)(LPCWSTR); - GetModuleHandleFunction pGetModuleHandle = reinterpret_cast<GetModuleHandleFunction>( - GetProcAddressA(kernelModule, "GetModuleHandleW")); - if (Q_UNLIKELY(!pGetModuleHandle)) - return result; -#else -#define pGetModuleHandle GetModuleHandleW -#endif - -#ifndef Q_OS_WINCE - HMODULE ntdll = pGetModuleHandle(L"ntdll.dll"); - if (Q_UNLIKELY(!ntdll)) - return result; - - // NTSTATUS is not defined on WinRT - typedef LONG NTSTATUS; - typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO); - - // RtlGetVersion is documented public API but we must load it dynamically - // because linking to it at load time will not pass the Windows App Certification Kit - // https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910.aspx - RtlGetVersionFunction pRtlGetVersion = reinterpret_cast<RtlGetVersionFunction>( - GetProcAddressA(ntdll, "RtlGetVersion")); - if (Q_UNLIKELY(!pRtlGetVersion)) - return result; - - // GetVersionEx() has been deprecated in Windows 8.1 and will return - // only Windows 8 from that version on, so use the kernel API function. - pRtlGetVersion((LPOSVERSIONINFO) &result); // always returns STATUS_SUCCESS -#else // !Q_OS_WINCE - GetVersionEx(&result); -#endif - return result; -} - -static OSVERSIONINFOEX winOsVersion() -{ - OSVERSIONINFOEX realResult = determineWinOsVersion(); -#ifdef QT_DEBUG - { - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QT_WINVER_OVERRIDE"))) { - OSVERSIONINFOEX result = realResult; - result.dwMajorVersion = 0; - result.dwMinorVersion = 0; - - // Erase any build number and service pack information - result.dwBuildNumber = 0; - result.szCSDVersion[0] = L'\0'; - result.wServicePackMajor = 0; - result.wServicePackMinor = 0; - - const QByteArray winVerOverride = qgetenv("QT_WINVER_OVERRIDE"); - if (winVerOverride == "WINDOWS7" || winVerOverride == "2008_R2") { - result.dwMajorVersion = 6; - result.dwMinorVersion = 1; - } else if (winVerOverride == "WINDOWS8" || winVerOverride == "2012") { - result.dwMajorVersion = 6; - result.dwMinorVersion = 2; - } else if (winVerOverride == "WINDOWS8_1" || winVerOverride == "2012_R2") { - result.dwMajorVersion = 6; - result.dwMinorVersion = 3; - } else if (winVerOverride == "WINDOWS10" || winVerOverride == "2016") { - result.dwMajorVersion = 10; - } else { - return realResult; - } - - if (winVerOverride == "2008_R2" - || winVerOverride == "2012" - || winVerOverride == "2012_R2" - || winVerOverride == "2016") { - // If the current host OS is a domain controller and the override OS - // is also a server type OS, preserve that information - if (result.wProductType == VER_NT_WORKSTATION) - result.wProductType = VER_NT_SERVER; - } else { - // Any other OS must be a workstation OS type - result.wProductType = VER_NT_WORKSTATION; - } - } - } -#endif - return realResult; -} - +#if QT_DEPRECATED_SINCE(5, 9) QSysInfo::WinVersion QSysInfo::windowsVersion() { - const OSVERSIONINFOEX osver = winOsVersion(); - if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 1) + const auto version = QOperatingSystemVersion::current(); + if (version.majorVersion() == 6 && version.minorVersion() == 1) return QSysInfo::WV_WINDOWS7; - if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 2) + if (version.majorVersion() == 6 && version.minorVersion() == 2) return QSysInfo::WV_WINDOWS8; - if (osver.dwMajorVersion == 6 && osver.dwMinorVersion == 3) + if (version.majorVersion() == 6 && version.minorVersion() == 3) return QSysInfo::WV_WINDOWS8_1; - if (osver.dwMajorVersion == 10 && osver.dwMinorVersion == 0) + if (version.majorVersion() == 10 && version.minorVersion() == 0) return QSysInfo::WV_WINDOWS10; return QSysInfo::WV_NT_based; } +const QSysInfo::WinVersion QSysInfo::WindowsVersion = QSysInfo::windowsVersion(); +#endif static QString winSp_helper() { - const qint16 major = winOsVersion().wServicePackMajor; + const auto osv = qWindowsVersionInfo(); + const qint16 major = osv.wServicePackMajor; if (major) { QString sp = QStringLiteral(" SP ") + QString::number(major); - const qint16 minor = winOsVersion().wServicePackMinor; + const qint16 minor = osv.wServicePackMinor; if (minor) sp += QLatin1Char('.') + QString::number(minor); @@ -2164,9 +2064,10 @@ static QString winSp_helper() return QString(); } -static const char *winVer_helper() +static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) { - const OSVERSIONINFOEX osver = winOsVersion(); + Q_UNUSED(version); + const OSVERSIONINFOEX osver = qWindowsVersionInfo(); const bool workstation = osver.wProductType == VER_NT_WORKSTATION; #define Q_WINVER(major, minor) (major << 8 | minor) @@ -2185,8 +2086,6 @@ static const char *winVer_helper() return 0; } -const QSysInfo::WinVersion QSysInfo::WindowsVersion = QSysInfo::windowsVersion(); - #endif #if defined(Q_OS_UNIX) # if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD) @@ -2367,6 +2266,68 @@ static bool findUnixOsVersion(QUnixOSVersion &v) # endif // USE_ETC_OS_RELEASE #endif // Q_OS_UNIX +#ifdef Q_OS_ANDROID +static const char *osVer_helper(QOperatingSystemVersion) +{ +/* Data: + + + +Cupcake +Donut +Eclair +Eclair +Eclair +Froyo +Gingerbread +Gingerbread +Honeycomb +Honeycomb +Honeycomb +Ice Cream Sandwich +Ice Cream Sandwich +Jelly Bean +Jelly Bean +Jelly Bean +KitKat +KitKat +Lollipop +Lollipop +Marshmallow +Nougat +Nougat + */ + static const char versions_string[] = + "\0" + "Cupcake\0" + "Donut\0" + "Eclair\0" + "Froyo\0" + "Gingerbread\0" + "Honeycomb\0" + "Ice Cream Sandwich\0" + "Jelly Bean\0" + "KitKat\0" + "Lollipop\0" + "Marshmallow\0" + "Nougat\0" + "\0"; + + static const int versions_indices[] = { + 0, 0, 0, 1, 9, 15, 15, 15, + 22, 28, 28, 40, 40, 40, 50, 50, + 69, 69, 69, 80, 80, 87, 87, 96, + 108, 108, -1 + }; + + static const int versions_count = (sizeof versions_indices) / (sizeof versions_indices[0]); + + // https://source.android.com/source/build-numbers.html + // https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels + const int sdk_int = QJNIObjectPrivate::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT"); + return &versions_string[versions_indices[qBound(0, sdk_int, versions_count - 1)]]; +} +#endif /*! \since 5.4 @@ -2615,9 +2576,9 @@ QString QSysInfo::kernelType() QString QSysInfo::kernelVersion() { #ifdef Q_OS_WIN - const OSVERSIONINFOEX osver = winOsVersion(); - return QString::number(int(osver.dwMajorVersion)) + QLatin1Char('.') + QString::number(int(osver.dwMinorVersion)) - + QLatin1Char('.') + QString::number(int(osver.dwBuildNumber)); + const auto osver = QOperatingSystemVersion::current(); + return QString::number(osver.majorVersion()) + QLatin1Char('.') + QString::number(osver.minorVersion()) + + QLatin1Char('.') + QString::number(osver.microVersion()); #else struct utsname u; if (uname(&u) == 0) @@ -2685,8 +2646,8 @@ QString QSysInfo::productType() #elif defined(Q_OS_WATCHOS) return QStringLiteral("watchos"); #elif defined(Q_OS_MACOS) - const QAppleOperatingSystemVersion version = qt_apple_os_version(); - if (version.major == 10 && version.minor < 12) + const auto version = QOperatingSystemVersion::current(); + if (version.majorVersion() == 10 && version.minorVersion() < 12) return QStringLiteral("osx"); return QStringLiteral("macos"); #elif defined(Q_OS_DARWIN) @@ -2708,8 +2669,23 @@ QString QSysInfo::productType() version could not be determined, this function returns "unknown". It will return the Android, iOS, \macos, Windows full-product - versions on those systems. In particular, on OS X, iOS and Windows, the - returned string is similar to the macVersion() or windowsVersion() enums. + versions on those systems. + + Typical returned values are (note: list not exhaustive): + \list + \li "2016.09" (Amazon Linux AMI 2016.09) + \li "7.1" (Android Nougat) + \li "25" (Fedora 25) + \li "10.1" (iOS 10.1) + \li "10.12" (macOS Sierra) + \li "10.0" (tvOS 10) + \li "16.10" (Ubuntu 16.10) + \li "3.1" (watchOS 3.1) + \li "7 SP 1" (Windows 7 Service Pack 1) + \li "8.1" (Windows 8.1) + \li "10" (Windows 10) + \li "Server 2016" (Windows Server 2016) + \endlist On Linux systems, it will try to determine the distribution version and will return that. This is also done on Debian/kFreeBSD, so this function will @@ -2726,20 +2702,17 @@ QString QSysInfo::productType() */ QString QSysInfo::productVersion() { -#if defined(Q_OS_MAC) - const QAppleOperatingSystemVersion version = qt_apple_os_version(); - return QString::number(version.major) + QLatin1Char('.') + QString::number(version.minor); +#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) + const auto version = QOperatingSystemVersion::current(); + return QString::number(version.majorVersion()) + QLatin1Char('.') + QString::number(version.minorVersion()); #elif defined(Q_OS_WIN) - const char *version = winVer_helper(); + const char *version = osVer_helper(); if (version) { const QLatin1Char spaceChar(' '); return QString::fromLatin1(version).remove(spaceChar).toLower() + winSp_helper().remove(spaceChar).toLower(); } // fall through -// Android should not fall through to the Unix code -#elif defined(Q_OS_ANDROID) - return QJNIObjectPrivate::getStaticObjectField("android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").toString(); #elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX QUnixOSVersion unixOsVersion; findUnixOsVersion(unixOsVersion); @@ -2767,44 +2740,23 @@ QString QSysInfo::productVersion() */ QString QSysInfo::prettyProductName() { -#if defined(Q_OS_IOS) - return QLatin1String("iOS ") + productVersion(); -#elif defined(Q_OS_TVOS) - return QLatin1String("tvOS ") + productVersion(); -#elif defined(Q_OS_WATCHOS) - return QLatin1String("watchOS ") + productVersion(); -#elif defined(Q_OS_MACOS) - const QAppleOperatingSystemVersion version = qt_apple_os_version(); - const char *name = osxVer_helper(version); - if (name) { - return (version.major == 10 && version.minor < 12 - ? QLatin1String("OS X ") - : QLatin1String("macOS ")) - + QLatin1String(name) - + QLatin1String(" (") + QString::number(version.major) - + QLatin1Char('.') + QString::number(version.minor) - + QLatin1Char(')'); - } else { - return QLatin1String("macOS ") - + QString::number(version.major) + QLatin1Char('.') - + QString::number(version.minor); - } -#elif defined(Q_OS_WINPHONE) - return QLatin1String("Windows Phone ") + QLatin1String(winVer_helper()); -#elif defined(Q_OS_WIN) - const char *name = winVer_helper(); - const OSVERSIONINFOEX osver = winOsVersion(); +#if defined(Q_OS_WINPHONE) + return QLatin1String("Windows Phone ") + QLatin1String(osVer_helper()); +#elif defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN) + const auto version = QOperatingSystemVersion::current(); + const char *name = osVer_helper(version); if (name) - return QLatin1String("Windows ") + QLatin1String(name) + winSp_helper() - + QLatin1String(" (") + QString::number(osver.dwMajorVersion) - + QLatin1Char('.') + QString::number(osver.dwMinorVersion) + return version.name() + QLatin1Char(' ') + QLatin1String(name) +# if defined(Q_OS_WIN) + + winSp_helper() +# endif + + QLatin1String(" (") + QString::number(version.majorVersion()) + + QLatin1Char('.') + QString::number(version.minorVersion()) + QLatin1Char(')'); - else - return QLatin1String("Windows ") - + QString::number(osver.dwMajorVersion) + QLatin1Char('.') - + QString::number(osver.dwMinorVersion); -#elif defined(Q_OS_ANDROID) - return QLatin1String("Android ") + productVersion(); + else + return version.name() + QLatin1Char(' ') + + QString::number(version.majorVersion()) + QLatin1Char('.') + + QString::number(version.minorVersion()); #elif defined(Q_OS_HAIKU) return QLatin1String("Haiku ") + productVersion(); #elif defined(Q_OS_UNIX) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 1737f58c87..4354c85bfe 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -44,6 +44,8 @@ #ifdef __cplusplus # include <type_traits> # include <cstddef> +# include <type_traits> +# include <utility> #endif #include <stddef.h> @@ -917,17 +919,34 @@ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantic #ifndef QT_NO_FOREACH +namespace QtPrivate { + template <typename T> class QForeachContainer { QForeachContainer &operator=(const QForeachContainer &) Q_DECL_EQ_DELETE; public: QForeachContainer(const T &t) : c(t) {} QForeachContainer(T &&t) : c(std::move(t)) {} + QForeachContainer(const QForeachContainer &other) + : c(other.c), + i(c.begin()), + e(c.end()), + control(other.control) + { + } + const T c; typename T::const_iterator i = c.begin(), e = c.end(); int control = 1; }; +template<typename T> +QForeachContainer<typename std::decay<T>::type> qMakeForeachContainer(T &&t) +{ + return QForeachContainer<typename std::decay<T>::type>(std::forward<T>(t)); +} + +} // Explanation of the control word: // - it's initialized to 1 // - that means both the inner and outer loops start @@ -938,7 +957,7 @@ public: // - if there was a break inside the inner loop, it will exit with control still // set to 1; in that case, the outer loop will invert it to 0 and will exit too #define Q_FOREACH(variable, container) \ -for (QForeachContainer<typename std::remove_reference<decltype(container)>::type> _container_((container)); \ +for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \ _container_.control && _container_.i != _container_.e; \ ++_container_.i, _container_.control ^= 1) \ for (variable = *_container_.i; _container_.control; _container_.control = 0) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index a30344995e..873a36f4a6 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -505,6 +505,7 @@ public: AA_SynthesizeMouseForUnhandledTabletEvents = 24, AA_CompressHighFrequencyEvents = 25, AA_DontCheckOpenGLContextThreadAffinity = 26, + AA_DisableShaderDiskCache = 27, // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 250195a512..64f0b5e1da 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -257,6 +257,15 @@ \l{QOpenGLContext::makeCurrent}{makeCurrent()}. This value has been added in Qt 5.8. + \value AA_DisableShaderDiskCache Disables caching of shader program binaries + on disk. By default Qt Quick, QPainter's OpenGL backend, and any + application using QOpenGLShaderProgram with one of its + \e addCacheableShaderFromSource overloads will employ a disk-based + \l{Caching Program Binaries}{program binary cache} in either the shared + or per-process cache storage location, on systems that support + \e glProgramBinary(). In the unlikely event of this being problematic, + set this attribute to disable all disk-based caching of shaders. + The following values are obsolete: \value AA_ImmediateWidgetCreation This attribute is no longer fully diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp new file mode 100644 index 0000000000..fb1ada905c --- /dev/null +++ b/src/corelib/global/qoperatingsystemversion.cpp @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** 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 "qoperatingsystemversion.h" +#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN) +#include "qoperatingsystemversion_p.h" +#endif + +#include <qversionnumber.h> + +#if defined(Q_OS_ANDROID) +#include <private/qjni_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QOperatingSystemVersion + \inmodule QtCore + \since 5.9 + \brief The QOperatingSystemVersion class provides information about the operating system version. + + Unlike other version functions in QSysInfo, QOperatingSystemVersion provides access to the full + version number that \a developers typically use to vary behavior or determine whether to enable + APIs or features based on the operating system version (as opposed to the kernel version number + or marketing version). + + This class is also a complete replacement for QSysInfo::macVersion and QSysInfo::windowsVersion, + additionally providing access to the third (micro) version number component. + + Presently, Android, Apple Platforms (iOS, macOS, tvOS, and watchOS), and Windows are supported. + + The \a majorVersion(), \a minorVersion(), and \a microVersion() functions return the parts of + the operating system version number based on: + + \table + \header \li Platforms \li Value + \row \li Android \li result of parsing + \l{https://developer.android.com/reference/android/os/Build.VERSION.html#RELEASE}{android.os.Build.VERSION.RELEASE} + using QVersionNumber, with a fallback to + \l{https://developer.android.com/reference/android/os/Build.VERSION.html#SDK_INT}{android.os.Build.VERSION.SDK_INT} + to determine the major and minor version component if the former fails + \row \li Apple Platforms \li majorVersion, minorVersion, and patchVersion from + \l{https://developer.apple.com/reference/foundation/nsprocessinfo/1410906-operatingsystemversion?language=objc}{NSProcessInfo.operatingSystemVersion} + \row \li Windows \li dwMajorVersion, dwMinorVersion, and dwBuildNumber from + \l{https://msdn.microsoft.com/en-us/library/mt723418.aspx}{RtlGetVersion} - + note that this function ALWAYS return the version number of the underlying operating system, + as opposed to the shim underneath GetVersionEx that hides the real version number + if the application is not manifested for that version of the OS + \endtable +*/ + +/*! + \enum QOperatingSystemVersion::OSType + + This enum provides symbolic names for the various operating + system families supported by QOperatingSystemVersion. + + \value Android The Google Android operating system. + \value IOS The Apple iOS operating system. + \value MacOS The Apple macOS operating system. + \value TvOS The Apple tvOS operating system. + \value WatchOS The Apple watchOS operating system. + \value Windows The Microsoft Windows operating system (in both Win32 and WinRT environments). + + \value Unknown An unknown or unsupported operating system. +*/ + +/*! + \fn QOperatingSystemVersion::QOperatingSystemVersion(int maj, int min, int mic) + + Constructs a QOperatingSystemVersion consisting of the OS type \a os, and + major, minor, and micro version numbers \a maj, \a min and \a mic, respectively. +*/ + +/*! + \fn QOperatingSystemVersion QOperatingSystemVersion::current() + + Returns a QOperatingSystemVersion indicating the current OS and its version number. +*/ +#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN) +QOperatingSystemVersion QOperatingSystemVersion::current() +{ + QOperatingSystemVersion version; + version.m_os = currentType(); +#if defined(Q_OS_ANDROID) +#ifndef QT_BOOTSTRAPPED + const QVersionNumber v = QVersionNumber::fromString(QJNIObjectPrivate::getStaticObjectField( + "android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").toString()); + if (!v.isNull()) { + version.m_major = v.majorVersion(); + version.m_minor = v.minorVersion(); + version.m_micro = v.microVersion(); + return version; + } +#endif + + version.m_major = -1; + version.m_minor = -1; + + static const int versions[][2] = { + { 1, 0 }, // API level 1 + { 1, 1 }, // API level 2 + { 1, 5 }, // API level 3 + { 1, 6 }, // API level 4 + { 2, 0 }, // API level 5 + { 2, 0 }, // API level 6 + { 2, 1 }, // API level 7 + { 2, 2 }, // API level 8 + { 2, 3 }, // API level 9 + { 2, 3 }, // API level 10 + { 3, 0 }, // API level 11 + { 3, 1 }, // API level 12 + { 3, 2 }, // API level 13 + { 4, 0 }, // API level 14 + { 4, 0 }, // API level 15 + { 4, 1 }, // API level 16 + { 4, 2 }, // API level 17 + { 4, 3 }, // API level 18 + { 4, 4 }, // API level 19 + { 4, 4 }, // API level 20 + { 5, 0 }, // API level 21 + { 5, 1 }, // API level 22 + { 6, 0 }, // API level 23 + { 7, 0 }, // API level 24 + { 7, 1 }, // API level 25 + }; + + // This will give us at least the first 2 version components + const size_t versionIdx = size_t(QJNIObjectPrivate::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT")) - 1; + if (versionIdx < sizeof(versions) / sizeof(versions[0])) { + version.m_major = versions[versionIdx][0]; + version.m_minor = versions[versionIdx][1]; + } + + // API level 6 was exactly version 2.0.1 + version.m_micro = versionIdx == 5 ? 1 : -1; +#else + version.m_major = -1; + version.m_minor = -1; + version.m_micro = -1; +#endif + return version; +} +#endif + +static inline int compareVersionComponents(int lhs, int rhs) +{ + return lhs >= 0 && rhs >= 0 ? lhs - rhs : 0; +} + +/*! + \fn int QOperatingSystemVersion::compare(const QOperatingSystemVersion &v1, + const QOperatingSystemVersion &v2) + + Compares \a v1 with \a v2 and returns an integer less than, equal to, or + greater than zero, depending on whether \a v1 is less than, equal to, or + greater than \a v2, respectively. + + Comparisons are performed by comparing the version number components of + \a v1 and \a v2. + + \note This function cannot take the OS type into account; you should use + the overloaded comparison operators to compare QOperatingSystemVersions + in a safe manner. +*/ +int QOperatingSystemVersion::compare(const QOperatingSystemVersion &v1, const QOperatingSystemVersion &v2) +{ + if (v1.m_major == v2.m_major) { + if (v1.m_minor == v2.m_minor) { + return compareVersionComponents(v1.m_micro, v2.m_micro); + } + return compareVersionComponents(v1.m_minor, v2.m_minor); + } + return compareVersionComponents(v1.m_major, v2.m_major); +} + +#ifndef QT_BOOTSTRAPPED +/*! + \fn QOperatingSystemVersion QOperatingSystemVersion::fromVersionNumber(const QVersionNumber &version, + QOperatingSystemVersion::OSType os) + + Returns a QOperatingSystemVersion consisting of the OS type \a os and version number \a version. +*/ +QOperatingSystemVersion QOperatingSystemVersion::fromVersionNumber(const QVersionNumber &version, + QOperatingSystemVersion::OSType os) +{ + return QOperatingSystemVersion(os, version.majorVersion(), version.minorVersion(), version.microVersion()); +} + +/*! + \fn QOperatingSystemVersion QOperatingSystemVersion::toVersionNumber() const + + Returns the QOperatingSystemVersion's version number as a QVersionNumber. +*/ +QVersionNumber QOperatingSystemVersion::toVersionNumber() const +{ + return QVersionNumber(m_major, m_minor, m_micro); +} +#endif + +/*! + \fn int QOperatingSystemVersion::majorVersion() const + + Returns the major version number, that is, the first segment of the operating system's version number. + + See the main class documentation for what the major version number is on a given operating system. + + -1 indicates an unknown or absent version number component. + + \sa minorVersion(), microVersion() +*/ + +/*! + \fn int QOperatingSystemVersion::minorVersion() const + + Returns the minor version number, that is, the second segment of the operating system's version number. + + See the main class documentation for what the minor version number is on a given operating system. + + -1 indicates an unknown or absent version number component. + + \sa majorVersion(), macro() +*/ + +/*! + \fn int QOperatingSystemVersion::microVersion() const + + Returns the micro version number, that is, the third segment of the operating system's version number. + + See the main class documentation for what the micro version number is on a given operating system. + + -1 indicates an unknown or absent version number component. + + \sa majorVersion(), minorVersion() +*/ + +/*! + \fn QOperatingSystemVersion::OSType QOperatingSystemVersion::type() const + + Returns the OS type identified by the QOperatingSystemVersion. + + \sa typeName() +*/ + +/*! + \fn QString QOperatingSystemVersion::name() const + + Returns a string representation of the OS type identified by the QOperatingSystemVersion. + + \sa type() +*/ +QString QOperatingSystemVersion::name() const +{ + switch (type()) { + case QOperatingSystemVersion::Windows: + return QStringLiteral("Windows"); + case QOperatingSystemVersion::MacOS: { + if (majorVersion() < 10) + return QStringLiteral("Mac OS"); + if (majorVersion() == 10 && minorVersion() < 8) + return QStringLiteral("Mac OS X"); + if (majorVersion() == 10 && minorVersion() < 12) + return QStringLiteral("OS X"); + return QStringLiteral("macOS"); + } + case QOperatingSystemVersion::IOS: { + if (majorVersion() < 4) + return QStringLiteral("iPhone OS"); + return QStringLiteral("iOS"); + } + case QOperatingSystemVersion::TvOS: + return QStringLiteral("tvOS"); + case QOperatingSystemVersion::WatchOS: + return QStringLiteral("watchOS"); + case QOperatingSystemVersion::Android: + return QStringLiteral("Android"); + case QOperatingSystemVersion::Unknown: + default: + return QString(); + } +} + +/*! + \variable QOperatingSystemVersion::Windows7 + \brief a version corresponding to Windows 7 (version 6.1). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::Windows7 = QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 1); + +/*! + \variable QOperatingSystemVersion::Windows8 + \brief a version corresponding to Windows 8 (version 6.2). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::Windows8 = QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 2); + +/*! + \variable QOperatingSystemVersion::Windows8_1 + \brief a version corresponding to Windows 8.1 (version 6.3). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::Windows8_1 = QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 3); + +/*! + \variable QOperatingSystemVersion::Windows10 + \brief a version corresponding to Windows 10 (version 10.0). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::Windows10 = QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10); + +/*! + \variable QOperatingSystemVersion::OSXMavericks + \brief a version corresponding to OS X Mavericks (version 10.9). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::OSXMavericks = QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 9); + +/*! + \variable QOperatingSystemVersion::OSXYosemite + \brief a version corresponding to OS X Yosemite (version 10.10). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::OSXYosemite = QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 10); + +/*! + \variable QOperatingSystemVersion::OSXElCapitan + \brief a version corresponding to OS X El Capitan (version 10.11). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::OSXElCapitan = QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 11); + +/*! + \variable QOperatingSystemVersion::MacOSSierra + \brief a version corresponding to macOS Sierra (version 10.12). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::MacOSSierra = QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 12); + +/*! + \variable QOperatingSystemVersion::AndroidJellyBean + \brief a version corresponding to Android Jelly Bean (version 4.1, API level 16). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean = QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 1); + +/*! + \variable QOperatingSystemVersion::AndroidJellyBean_MR1 + \brief a version corresponding to Android Jelly Bean, maintenance release 1 (version 4.2, API level 17). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean_MR1 = QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 2); + +/*! + \variable QOperatingSystemVersion::AndroidJellyBean_MR2 + \brief a version corresponding to Android Jelly Bean, maintenance release 2 (version 4.3, API level 18). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean_MR2 = QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 3); + +/*! + \variable QOperatingSystemVersion::AndroidKitKat + \brief a version corresponding to Android KitKat (versions 4.4 & 4.4W, API levels 19 & 20). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidKitKat = QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 4); + +/*! + \variable QOperatingSystemVersion::AndroidLollipop + \brief a version corresponding to Android Lollipop (version 5.0, API level 21). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidLollipop = QOperatingSystemVersion(QOperatingSystemVersion::Android, 5, 0); + +/*! + \variable QOperatingSystemVersion::AndroidLollipop_MR1 + \brief a version corresponding to Android Lollipop, maintenance release 1 (version 5.1, API level 22). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidLollipop_MR1 = QOperatingSystemVersion(QOperatingSystemVersion::Android, 5, 1); + +/*! + \variable QOperatingSystemVersion::AndroidMarshmallow + \brief a version corresponding to Android Marshmallow (version 6.0, API level 23). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidMarshmallow = QOperatingSystemVersion(QOperatingSystemVersion::Android, 6, 0); + +/*! + \variable QOperatingSystemVersion::AndroidNougat + \brief a version corresponding to Android Nougat (version 7.0, API level 24). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidNougat = QOperatingSystemVersion(QOperatingSystemVersion::Android, 7, 0); + +/*! + \variable QOperatingSystemVersion::AndroidNougat_MR1 + \brief a version corresponding to Android Nougat, maintenance release 1 (version 7.0, API level 25). + \since 5.9 + */ +const QOperatingSystemVersion QOperatingSystemVersion::AndroidNougat_MR1 = QOperatingSystemVersion(QOperatingSystemVersion::Android, 7, 1); + +QT_END_NAMESPACE diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h new file mode 100644 index 0000000000..ef999c6ae4 --- /dev/null +++ b/src/corelib/global/qoperatingsystemversion.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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 <QtCore/qglobal.h> + +#ifndef QOPERATINGSYSTEMVERSION_H +#define QOPERATINGSYSTEMVERSION_H + +QT_BEGIN_NAMESPACE + +class QString; +class QVersionNumber; + +class Q_CORE_EXPORT QOperatingSystemVersion +{ +public: + enum OSType { + Unknown = 0, + Windows, + MacOS, + IOS, + TvOS, + WatchOS, + Android + }; + + static const QOperatingSystemVersion Windows7; + static const QOperatingSystemVersion Windows8; + static const QOperatingSystemVersion Windows8_1; + static const QOperatingSystemVersion Windows10; + + static const QOperatingSystemVersion OSXMavericks; + static const QOperatingSystemVersion OSXYosemite; + static const QOperatingSystemVersion OSXElCapitan; + static const QOperatingSystemVersion MacOSSierra; + + static const QOperatingSystemVersion AndroidJellyBean; + static const QOperatingSystemVersion AndroidJellyBean_MR1; + static const QOperatingSystemVersion AndroidJellyBean_MR2; + static const QOperatingSystemVersion AndroidKitKat; + static const QOperatingSystemVersion AndroidLollipop; + static const QOperatingSystemVersion AndroidLollipop_MR1; + static const QOperatingSystemVersion AndroidMarshmallow; + static const QOperatingSystemVersion AndroidNougat; + static const QOperatingSystemVersion AndroidNougat_MR1; + + QOperatingSystemVersion(const QOperatingSystemVersion &other) = default; + Q_DECL_CONSTEXPR QOperatingSystemVersion(OSType osType, int vmajor, int vminor = -1, int vmicro = -1) + : m_os(osType), m_major(vmajor), m_minor(vminor), m_micro(vmicro) { } + + static QOperatingSystemVersion current(); + + static int compare(const QOperatingSystemVersion &v1, const QOperatingSystemVersion &v2); + + QOperatingSystemVersion fromVersionNumber(const QVersionNumber &version, OSType os); + QVersionNumber toVersionNumber() const; + + 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; } + + Q_DECL_CONSTEXPR OSType type() const { return m_os; } + QString name() const; + +private: + QOperatingSystemVersion() = default; + OSType m_os; + int m_major; + int m_minor; + int m_micro; +}; + +inline bool operator>(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) > 0; } + +inline bool operator>=(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) >= 0; } + +inline bool operator<(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) < 0; } + +inline bool operator<=(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) <= 0; } + +inline bool operator==(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) == 0; } + +inline bool operator!=(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs) +{ return !(lhs == rhs); } + +QT_END_NAMESPACE + +#endif // QOPERATINGSYSTEMVERSION_H diff --git a/src/corelib/global/qoperatingsystemversion_darwin.mm b/src/corelib/global/qoperatingsystemversion_darwin.mm new file mode 100644 index 0000000000..3dd007cbb3 --- /dev/null +++ b/src/corelib/global/qoperatingsystemversion_darwin.mm @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 "qoperatingsystemversion_p.h" +#import <Foundation/Foundation.h> + +#ifdef Q_OS_IOS +#import <UIKit/UIKit.h> +#endif + +QT_BEGIN_NAMESPACE + +typedef qint16 (*GestaltFunction)(quint32 selector, qint32 *response); + +QOperatingSystemVersion QOperatingSystemVersion::current() +{ + QOperatingSystemVersion v; + v.m_os = currentType(); + v.m_major = -1; + v.m_minor = -1; + v.m_micro = -1; +#if QT_MACOS_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_10, __IPHONE_8_0) || defined(Q_OS_TVOS) || defined(Q_OS_WATCHOS) + if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { + NSOperatingSystemVersion osv = NSProcessInfo.processInfo.operatingSystemVersion; + v.m_major = osv.majorVersion; + v.m_minor = osv.minorVersion; + v.m_micro = osv.patchVersion; + return v; + } +#endif + // Use temporary variables so we can return 0.0.0 (unknown version) + // in case of an error partway through determining the OS version + qint32 major = 0, minor = 0, patch = 0; +#if QT_MACOS_IOS_DEPLOYMENT_TARGET_BELOW(__MAC_10_10, __IPHONE_8_0) +#if defined(Q_OS_IOS) + @autoreleasepool { + NSArray *parts = [UIDevice.currentDevice.systemVersion componentsSeparatedByString:@"."]; + major = parts.count > 0 ? [[parts objectAtIndex:0] intValue] : 0; + minor = parts.count > 1 ? [[parts objectAtIndex:1] intValue] : 0; + patch = parts.count > 2 ? [[parts objectAtIndex:2] intValue] : 0; + } +#elif defined(Q_OS_MACOS) + static GestaltFunction pGestalt = 0; + if (!pGestalt) { + CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreServices")); + pGestalt = reinterpret_cast<GestaltFunction>(CFBundleGetFunctionPointerForName(b, + CFSTR("Gestalt"))); + } + if (!pGestalt) + return v; + if (pGestalt('sys1', &major) != 0) + return v; + if (pGestalt('sys2', &minor) != 0) + return v; + if (pGestalt('sys3', &patch) != 0) + return v; +#endif +#endif + v.m_major = major; + v.m_minor = minor; + v.m_micro = patch; + return v; +} + +QT_END_NAMESPACE diff --git a/src/corelib/global/qoperatingsystemversion_p.h b/src/corelib/global/qoperatingsystemversion_p.h new file mode 100644 index 0000000000..78d0daf0c6 --- /dev/null +++ b/src/corelib/global/qoperatingsystemversion_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QOPERATINGSYSTEMVERSION_P_H +#define QOPERATINGSYSTEMVERSION_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 "qoperatingsystemversion.h" + +#ifdef Q_OS_WIN +#include <qt_windows.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +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/qoperatingsystemversion_win.cpp b/src/corelib/global/qoperatingsystemversion_win.cpp new file mode 100644 index 0000000000..060ca2f7da --- /dev/null +++ b/src/corelib/global/qoperatingsystemversion_win.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** 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 "qoperatingsystemversion_p.h" +#include <qt_windows.h> +#include <qbytearray.h> + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WINRT +static inline HMODULE moduleHandleForFunction(LPCVOID address) +{ + // This is a widely used, decades-old technique for retrieving the handle + // of a module and is effectively equivalent to GetModuleHandleEx + // (which is unavailable on WinRT) + MEMORY_BASIC_INFORMATION mbi = { 0, 0, 0, 0, 0, 0, 0 }; + if (VirtualQuery(address, &mbi, sizeof(mbi)) == 0) + return 0; + return reinterpret_cast<HMODULE>(mbi.AllocationBase); +} +#endif + +static inline OSVERSIONINFOEX determineWinOsVersion() +{ + OSVERSIONINFOEX result = { sizeof(OSVERSIONINFOEX), 0, 0, 0, 0, {'\0'}, 0, 0, 0, 0, 0}; + +#define GetProcAddressA GetProcAddress + + // GetModuleHandle is not supported in WinRT and linking to it at load time + // will not pass the Windows App Certification Kit... but it exists and is functional, + // so use some unusual but widely used techniques to get a pointer to it +#ifdef Q_OS_WINRT + // 1. Get HMODULE of kernel32.dll, using the address of some function exported by that DLL + HMODULE kernelModule = moduleHandleForFunction(reinterpret_cast<LPCVOID>(VirtualQuery)); + if (Q_UNLIKELY(!kernelModule)) + return result; + + // 2. Get pointer to GetModuleHandle so we can then load other arbitrary modules (DLLs) + typedef HMODULE(WINAPI *GetModuleHandleFunction)(LPCWSTR); + GetModuleHandleFunction pGetModuleHandle = reinterpret_cast<GetModuleHandleFunction>( + GetProcAddressA(kernelModule, "GetModuleHandleW")); + if (Q_UNLIKELY(!pGetModuleHandle)) + return result; +#else +#define pGetModuleHandle GetModuleHandleW +#endif + +#ifndef Q_OS_WINCE + HMODULE ntdll = pGetModuleHandle(L"ntdll.dll"); + if (Q_UNLIKELY(!ntdll)) + return result; + + // NTSTATUS is not defined on WinRT + typedef LONG NTSTATUS; + typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO); + + // RtlGetVersion is documented public API but we must load it dynamically + // because linking to it at load time will not pass the Windows App Certification Kit + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910.aspx + RtlGetVersionFunction pRtlGetVersion = reinterpret_cast<RtlGetVersionFunction>( + GetProcAddressA(ntdll, "RtlGetVersion")); + if (Q_UNLIKELY(!pRtlGetVersion)) + return result; + + // GetVersionEx() has been deprecated in Windows 8.1 and will return + // only Windows 8 from that version on, so use the kernel API function. + pRtlGetVersion((LPOSVERSIONINFO) &result); // always returns STATUS_SUCCESS +#else // !Q_OS_WINCE + GetVersionEx(&result); +#endif + return result; +} + +OSVERSIONINFOEX qWindowsVersionInfo() +{ + OSVERSIONINFOEX realResult = determineWinOsVersion(); +#ifdef QT_DEBUG + { + if (Q_UNLIKELY(qEnvironmentVariableIsSet("QT_WINVER_OVERRIDE"))) { + OSVERSIONINFOEX result = realResult; + result.dwMajorVersion = 0; + result.dwMinorVersion = 0; + + // Erase any build number and service pack information + result.dwBuildNumber = 0; + result.szCSDVersion[0] = L'\0'; + result.wServicePackMajor = 0; + result.wServicePackMinor = 0; + + const QByteArray winVerOverride = qgetenv("QT_WINVER_OVERRIDE"); + if (winVerOverride == "WINDOWS7" || winVerOverride == "2008_R2") { + result.dwMajorVersion = 6; + result.dwMinorVersion = 1; + } else if (winVerOverride == "WINDOWS8" || winVerOverride == "2012") { + result.dwMajorVersion = 6; + result.dwMinorVersion = 2; + } else if (winVerOverride == "WINDOWS8_1" || winVerOverride == "2012_R2") { + result.dwMajorVersion = 6; + result.dwMinorVersion = 3; + } else if (winVerOverride == "WINDOWS10" || winVerOverride == "2016") { + result.dwMajorVersion = 10; + } else { + return realResult; + } + + if (winVerOverride == "2008_R2" + || winVerOverride == "2012" + || winVerOverride == "2012_R2" + || winVerOverride == "2016") { + // If the current host OS is a domain controller and the override OS + // is also a server type OS, preserve that information + if (result.wProductType == VER_NT_WORKSTATION) + result.wProductType = VER_NT_SERVER; + } else { + // Any other OS must be a workstation OS type + result.wProductType = VER_NT_WORKSTATION; + } + } + } +#endif + return realResult; +} + +QOperatingSystemVersion QOperatingSystemVersion::current() +{ + QOperatingSystemVersion v; + v.m_os = currentType(); + const OSVERSIONINFOEX osv = qWindowsVersionInfo(); + v.m_major = osv.dwMajorVersion; + v.m_minor = osv.dwMinorVersion; + v.m_micro = osv.dwBuildNumber; + return v; +} + +QT_END_NAMESPACE diff --git a/src/corelib/global/qsysinfo.h b/src/corelib/global/qsysinfo.h index 23f412aa6a..3cbcfd3fc9 100644 --- a/src/corelib/global/qsysinfo.h +++ b/src/corelib/global/qsysinfo.h @@ -49,6 +49,23 @@ QT_BEGIN_NAMESPACE System information */ +/* + * GCC (5-7) has a regression that causes it to emit wrong deprecated warnings: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77849 + * + * Try to work around it by defining our own macro. + */ + +#ifdef QT_SYSINFO_DEPRECATED_X +#error "QT_SYSINFO_DEPRECATED_X already defined" +#endif + +#ifdef Q_CC_GNU +#define QT_SYSINFO_DEPRECATED_X(x) +#else +#define QT_SYSINFO_DEPRECATED_X(x) QT_DEPRECATED_X(x) +#endif + class QString; class Q_CORE_EXPORT QSysInfo { public: @@ -79,7 +96,8 @@ public: # endif }; #endif - enum WinVersion { +#if QT_DEPRECATED_SINCE(5, 9) + enum QT_DEPRECATED_X("Use QOperatingSystemVersion") WinVersion { WV_None = 0x0000, WV_32s = 0x0001, @@ -117,19 +135,25 @@ public: WV_CE_6 = 0x0400, WV_CE_based = 0x0f00 }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") +QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") +QT_WARNING_DISABLE_INTEL(1478) +QT_WARNING_DISABLE_MSVC(4996) #if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) - static const WinVersion WindowsVersion; - static WinVersion windowsVersion(); + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static const WinVersion WindowsVersion; + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static WinVersion windowsVersion(); #else - static const WinVersion WindowsVersion = WV_None; - static WinVersion windowsVersion() { return WV_None; } + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static const WinVersion WindowsVersion = WV_None; + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static WinVersion windowsVersion() { return WV_None; } #endif +QT_WARNING_POP #define Q_MV_OSX(major, minor) (major == 10 ? minor + 2 : (major == 9 ? 1 : 0)) #define Q_MV_IOS(major, minor) (QSysInfo::MV_IOS | major << 4 | minor) #define Q_MV_TVOS(major, minor) (QSysInfo::MV_TVOS | major << 4 | minor) #define Q_MV_WATCHOS(major, minor) (QSysInfo::MV_WATCHOS | major << 4 | minor) - enum MacVersion { + enum QT_DEPRECATED_X("Use QOperatingSystemVersion") MacVersion { MV_None = 0xffff, MV_Unknown = 0x0000, @@ -198,13 +222,20 @@ public: MV_WATCHOS_2_2 = Q_MV_WATCHOS(2, 2), MV_WATCHOS_3_0 = Q_MV_WATCHOS(3, 0) }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") +QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") +QT_WARNING_DISABLE_INTEL(1478) +QT_WARNING_DISABLE_MSVC(4996) #if defined(Q_OS_MAC) - static const MacVersion MacintoshVersion; - static MacVersion macVersion(); + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static const MacVersion MacintoshVersion; + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static MacVersion macVersion(); #else - static const MacVersion MacintoshVersion = MV_None; - static MacVersion macVersion() { return MV_None; } + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static const MacVersion MacintoshVersion = MV_None; + QT_SYSINFO_DEPRECATED_X("Use QOperatingSystemVersion::current()") static MacVersion macVersion() { return MV_None; } #endif +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(5, 9) static QString buildCpuArchitecture(); static QString currentCpuArchitecture(); @@ -219,5 +250,7 @@ public: static QString machineHostName(); }; +#undef QT_SYSINFO_DEPRECATED_X + QT_END_NAMESPACE #endif // QSYSINFO_H diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index ac58677b77..994ed88791 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -95,10 +95,11 @@ public: Qt_5_6 = 17, Qt_5_7 = Qt_5_6, Qt_5_8 = Qt_5_7, -#if QT_VERSION >= 0x050900 + Qt_5_9 = Qt_5_8, +#if QT_VERSION >= 0x050a00 #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif - Qt_DefaultCompiledVersion = Qt_5_8 + Qt_DefaultCompiledVersion = Qt_5_9 }; enum ByteOrder { @@ -222,6 +223,98 @@ private: QDataStream::Status oldStatus; }; +template <typename Container> +QDataStream &readArrayBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + c.reserve(n); + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.append(t); + } + + return s; +} + +template <typename Container> +QDataStream &readListBasedContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::value_type t; + s >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c << t; + } + + return s; +} + +template <typename Container> +QDataStream &readAssociativeContainer(QDataStream &s, Container &c) +{ + StreamStateSaver stateSaver(&s); + + c.clear(); + quint32 n; + s >> n; + for (quint32 i = 0; i < n; ++i) { + typename Container::key_type k; + typename Container::mapped_type t; + s >> k >> t; + if (s.status() != QDataStream::Ok) { + c.clear(); + break; + } + c.insertMulti(k, t); + } + + return s; +} + +template <typename Container> +QDataStream &writeSequentialContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + for (const typename Container::value_type &t : c) + s << t; + + return s; +} + +template <typename Container> +QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c) +{ + s << quint32(c.size()); + // Deserialization should occur in the reverse order. + // Otherwise, value() will return the least recently inserted + // value instead of the most recently inserted one. + auto it = c.constEnd(); + auto begin = c.constBegin(); + while (it != begin) { + --it; + s << it.key() << it.value(); + } + + return s; +} + } // QtPrivate namespace /***************************************************************************** @@ -265,209 +358,75 @@ inline QDataStream &QDataStream::operator<<(quint64 i) { return *this << qint64(i); } template <typename T> -QDataStream& operator>>(QDataStream& s, QList<T>& l) +inline QDataStream &operator>>(QDataStream &s, QList<T> &l) { - QtPrivate::StreamStateSaver stateSaver(&s); - - l.clear(); - quint32 c; - s >> c; - l.reserve(c); - for(quint32 i = 0; i < c; ++i) - { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - l.clear(); - break; - } - l.append(t); - } - - return s; + return QtPrivate::readArrayBasedContainer(s, l); } template <typename T> -QDataStream& operator<<(QDataStream& s, const QList<T>& l) +inline QDataStream &operator<<(QDataStream &s, const QList<T> &l) { - s << quint32(l.size()); - for (int i = 0; i < l.size(); ++i) - s << l.at(i); - return s; + return QtPrivate::writeSequentialContainer(s, l); } template <typename T> -QDataStream& operator>>(QDataStream& s, QLinkedList<T>& l) +inline QDataStream &operator>>(QDataStream &s, QLinkedList<T> &l) { - QtPrivate::StreamStateSaver stateSaver(&s); - - l.clear(); - quint32 c; - s >> c; - for(quint32 i = 0; i < c; ++i) - { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - l.clear(); - break; - } - l.append(t); - } - - return s; + return QtPrivate::readListBasedContainer(s, l); } template <typename T> -QDataStream& operator<<(QDataStream& s, const QLinkedList<T>& l) +inline QDataStream &operator<<(QDataStream &s, const QLinkedList<T> &l) { - s << quint32(l.size()); - typename QLinkedList<T>::ConstIterator it = l.constBegin(); - for(; it != l.constEnd(); ++it) - s << *it; - return s; + return QtPrivate::writeSequentialContainer(s, l); } template<typename T> -QDataStream& operator>>(QDataStream& s, QVector<T>& v) +inline QDataStream &operator>>(QDataStream &s, QVector<T> &v) { - QtPrivate::StreamStateSaver stateSaver(&s); - - v.clear(); - quint32 c; - s >> c; - v.resize(c); - for(quint32 i = 0; i < c; ++i) { - T t; - s >> t; - if (s.status() != QDataStream::Ok) { - v.clear(); - break; - } - v[i] = t; - } - - return s; + return QtPrivate::readArrayBasedContainer(s, v); } template<typename T> -QDataStream& operator<<(QDataStream& s, const QVector<T>& v) +inline QDataStream &operator<<(QDataStream &s, const QVector<T> &v) { - s << quint32(v.size()); - for (typename QVector<T>::const_iterator it = v.begin(); it != v.end(); ++it) - s << *it; - return s; + return QtPrivate::writeSequentialContainer(s, v); } template <typename T> -QDataStream &operator>>(QDataStream &in, QSet<T> &set) +inline QDataStream &operator>>(QDataStream &s, QSet<T> &set) { - QtPrivate::StreamStateSaver stateSaver(&in); - - set.clear(); - quint32 c; - in >> c; - for (quint32 i = 0; i < c; ++i) { - T t; - in >> t; - if (in.status() != QDataStream::Ok) { - set.clear(); - break; - } - set << t; - } - - return in; + return QtPrivate::readListBasedContainer(s, set); } template <typename T> -QDataStream& operator<<(QDataStream &out, const QSet<T> &set) +inline QDataStream &operator<<(QDataStream &s, const QSet<T> &set) { - out << quint32(set.size()); - typename QSet<T>::const_iterator i = set.constBegin(); - while (i != set.constEnd()) { - out << *i; - ++i; - } - return out; + return QtPrivate::writeSequentialContainer(s, set); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QHash<Key, T> &hash) +inline QDataStream &operator>>(QDataStream &s, QHash<Key, T> &hash) { - QtPrivate::StreamStateSaver stateSaver(&in); - - hash.clear(); - quint32 n; - in >> n; - - for (quint32 i = 0; i < n; ++i) { - if (in.status() != QDataStream::Ok) - break; - - Key k; - T t; - in >> k >> t; - hash.insertMulti(k, t); - } - - if (in.status() != QDataStream::Ok) - hash.clear(); - return in; + return QtPrivate::readAssociativeContainer(s, hash); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QHash<Key, T>& hash) +inline QDataStream &operator<<(QDataStream &s, const QHash<Key, T> &hash) { - out << quint32(hash.size()); - typename QHash<Key, T>::ConstIterator it = hash.end(); - typename QHash<Key, T>::ConstIterator begin = hash.begin(); - while (it != begin) { - --it; - out << it.key() << it.value(); - } - return out; + return QtPrivate::writeAssociativeContainer(s, hash); } -#ifdef Q_QDOC + template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<Key, T> &map) -#else -template <class aKey, class aT> -Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &map) -#endif +inline QDataStream &operator>>(QDataStream &s, QMap<Key, T> &map) { - QtPrivate::StreamStateSaver stateSaver(&in); - - map.clear(); - quint32 n; - in >> n; - - map.detach(); - for (quint32 i = 0; i < n; ++i) { - if (in.status() != QDataStream::Ok) - break; - - aKey key; - aT value; - in >> key >> value; - map.insertMulti(key, value); - } - if (in.status() != QDataStream::Ok) - map.clear(); - return in; + return QtPrivate::readAssociativeContainer(s, map); } template <class Key, class T> -Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QMap<Key, T> &map) +inline QDataStream &operator<<(QDataStream &s, const QMap<Key, T> &map) { - out << quint32(map.size()); - typename QMap<Key, T>::ConstIterator it = map.end(); - typename QMap<Key, T>::ConstIterator begin = map.begin(); - while (it != begin) { - --it; - out << it.key() << it.value(); - } - return out; + return QtPrivate::writeAssociativeContainer(s, map); } #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp index cdb64d08e1..663d9cd61d 100644 --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qfilesystemengine_p.h" - +#include "qoperatingsystemversion.h" #include "qplatformdefs.h" #include "qsysinfo.h" #include "private/qabstractfileengine_p.h" @@ -637,7 +637,7 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry) FILE_SHARE_READ, OPEN_EXISTING, NULL); #endif // Q_OS_WINRT if (handle) { - result = QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8 ? + result = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? fileIdWin8(handle) : fileId(handle); CloseHandle(handle); } diff --git a/src/corelib/io/qfilesystemiterator_win.cpp b/src/corelib/io/qfilesystemiterator_win.cpp index 2ce7bd7a4b..9140118872 100644 --- a/src/corelib/io/qfilesystemiterator_win.cpp +++ b/src/corelib/io/qfilesystemiterator_win.cpp @@ -39,6 +39,7 @@ #include "qfilesystemiterator_p.h" #include "qfilesystemengine_p.h" +#include "qoperatingsystemversion.h" #include "qplatformdefs.h" #include "qvector.h" @@ -93,7 +94,7 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa haveData = true; int infoLevel = 0 ; // FindExInfoStandard; DWORD dwAdditionalFlags = 0; - if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows7) { dwAdditionalFlags = 2; // FIND_FIRST_EX_LARGE_FETCH infoLevel = 1 ; // FindExInfoBasic; } diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index a1d90c76f4..612b3fa57c 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -64,6 +64,9 @@ # include "qfilesystemwatcher_fsevents_p.h" #endif +#include <algorithm> +#include <iterator> + QT_BEGIN_NAMESPACE QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine(QObject *parent) @@ -102,6 +105,17 @@ void QFileSystemWatcherPrivate::init() SIGNAL(directoryChanged(QString,bool)), q, SLOT(_q_directoryChanged(QString,bool))); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveLockForRemoval, + q, [this] (const QString &p) { _q_winDriveLockForRemoval(p); }); + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveLockForRemovalFailed, + q, [this] (const QString &p) { _q_winDriveLockForRemovalFailed(p); }); + QObject::connect(static_cast<QWindowsFileSystemWatcherEngine *>(native), + &QWindowsFileSystemWatcherEngine::driveRemoved, + q, [this] (const QString &p) { _q_winDriveRemoved(p); }); +#endif // !Q_OS_WINRT } } @@ -146,7 +160,46 @@ void QFileSystemWatcherPrivate::_q_directoryChanged(const QString &path, bool re emit q->directoryChanged(path, QFileSystemWatcher::QPrivateSignal()); } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + +void QFileSystemWatcherPrivate::_q_winDriveLockForRemoval(const QString &path) +{ + // Windows: Request to lock a (removable/USB) drive for removal, release + // its paths under watch, temporarily storing them should the lock fail. + Q_Q(QFileSystemWatcher); + QStringList pathsToBeRemoved; + auto pred = [&path] (const QString &f) { return !f.startsWith(path, Qt::CaseInsensitive); }; + std::remove_copy_if(files.cbegin(), files.cend(), + std::back_inserter(pathsToBeRemoved), pred); + std::remove_copy_if(directories.cbegin(), directories.cend(), + std::back_inserter(pathsToBeRemoved), pred); + if (!pathsToBeRemoved.isEmpty()) { + q->removePaths(pathsToBeRemoved); + temporarilyRemovedPaths.insert(path.at(0), pathsToBeRemoved); + } +} + +void QFileSystemWatcherPrivate::_q_winDriveLockForRemovalFailed(const QString &path) +{ + // Windows: Request to lock a (removable/USB) drive failed (blocked by other + // application), restore the watched paths. + Q_Q(QFileSystemWatcher); + if (!path.isEmpty()) { + const auto it = temporarilyRemovedPaths.find(path.at(0)); + if (it != temporarilyRemovedPaths.end()) { + q->addPaths(it.value()); + temporarilyRemovedPaths.erase(it); + } + } +} +void QFileSystemWatcherPrivate::_q_winDriveRemoved(const QString &path) +{ + // Windows: Drive finally removed, clear out paths stored in lock request. + if (!path.isEmpty()) + temporarilyRemovedPaths.remove(path.at(0)); +} +#endif // Q_OS_WIN && !Q_OS_WINRT /*! \class QFileSystemWatcher diff --git a/src/corelib/io/qfilesystemwatcher_p.h b/src/corelib/io/qfilesystemwatcher_p.h index 6c64411f92..4220c1db28 100644 --- a/src/corelib/io/qfilesystemwatcher_p.h +++ b/src/corelib/io/qfilesystemwatcher_p.h @@ -58,6 +58,7 @@ #include <private/qobject_p.h> #include <QtCore/qstringlist.h> +#include <QtCore/qhash.h> QT_BEGIN_NAMESPACE @@ -106,6 +107,15 @@ public: // private slots void _q_fileChanged(const QString &path, bool removed); void _q_directoryChanged(const QString &path, bool removed); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + void _q_winDriveLockForRemoval(const QString &); + void _q_winDriveLockForRemovalFailed(const QString &); + void _q_winDriveRemoved(const QString &); + +private: + QHash<QChar, QStringList> temporarilyRemovedPaths; +#endif // Q_OS_WIN && !Q_OS_WINRT }; diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp index be56d8dd1d..c385a82fc5 100644 --- a/src/corelib/io/qfilesystemwatcher_win.cpp +++ b/src/corelib/io/qfilesystemwatcher_win.cpp @@ -52,6 +52,16 @@ #include <qt_windows.h> +#ifndef Q_OS_WINRT +# include <qabstractnativeeventfilter.h> +# include <qcoreapplication.h> +# include <qdir.h> +# include <private/qeventdispatcher_win_p.h> +# include <dbt.h> +# include <algorithm> +# include <vector> +#endif // !Q_OS_WINRT + QT_BEGIN_NAMESPACE // #define WINQFSW_DEBUG @@ -61,11 +71,275 @@ QT_BEGIN_NAMESPACE # define DEBUG if (false) qDebug #endif +#ifndef Q_OS_WINRT +/////////// +// QWindowsRemovableDriveListener +// Listen for the various WM_DEVICECHANGE message indicating drive addition/removal +// requests and removals. +/////////// +class QWindowsRemovableDriveListener : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + // Device UUids as declared in ioevent.h (GUID_IO_VOLUME_LOCK, ...) + enum VolumeUuid { UnknownUuid, UuidIoVolumeLock, UuidIoVolumeLockFailed, + UuidIoVolumeUnlock, UuidIoMediaRemoval }; + + struct RemovableDriveEntry { + HDEVNOTIFY devNotify; + wchar_t drive; + }; + + explicit QWindowsRemovableDriveListener(QObject *parent = nullptr); + ~QWindowsRemovableDriveListener(); + + // Call from QFileSystemWatcher::addPaths() to set up notifications on drives + void addPath(const QString &path); + + bool nativeEventFilter(const QByteArray &, void *messageIn, long *) override; + +signals: + void driveAdded(); + void driveRemoved(const QString &); + void driveLockForRemoval(const QString &); + void driveLockForRemovalFailed(const QString &); + +private: + static VolumeUuid volumeUuid(const UUID &needle); + void handleDbtCustomEvent(const MSG *msg); + void handleDbtDriveArrivalRemoval(const MSG *msg); + + std::vector<RemovableDriveEntry> m_removableDrives; + quintptr m_lastMessageHash; +}; + +static inline QEventDispatcherWin32 *winEventDispatcher() +{ + return static_cast<QEventDispatcherWin32 *>(QCoreApplication::instance()->eventDispatcher()); +} + +QWindowsRemovableDriveListener::QWindowsRemovableDriveListener(QObject *parent) + : QObject(parent) + , m_lastMessageHash(0) +{ + winEventDispatcher()->installNativeEventFilter(this); +} + +static void stopDeviceNotification(QWindowsRemovableDriveListener::RemovableDriveEntry &e) +{ + UnregisterDeviceNotification(e.devNotify); + e.devNotify = 0; +} + +template <class Iterator> // Search sequence of RemovableDriveEntry for HDEVNOTIFY. +static inline Iterator findByHDevNotify(Iterator i1, Iterator i2, HDEVNOTIFY hdevnotify) +{ + return std::find_if(i1, i2, + [hdevnotify] (const QWindowsRemovableDriveListener::RemovableDriveEntry &e) { return e.devNotify == hdevnotify; }); +} + +QWindowsRemovableDriveListener::~QWindowsRemovableDriveListener() +{ + std::for_each(m_removableDrives.begin(), m_removableDrives.end(), stopDeviceNotification); +} + +static QString pathFromEntry(const QWindowsRemovableDriveListener::RemovableDriveEntry &re) +{ + QString path = QStringLiteral("A:/"); + path[0] = QChar::fromLatin1(re.drive); + return path; +} + +// Handle WM_DEVICECHANGE+DBT_CUSTOMEVENT, which is sent based on the registration +// on the volume handle with QEventDispatcherWin32's message window in the class. +// Capture the GUID_IO_VOLUME_LOCK indicating the drive is to be removed. +QWindowsRemovableDriveListener::VolumeUuid QWindowsRemovableDriveListener::volumeUuid(const UUID &needle) +{ + static const struct VolumeUuidMapping // UUIDs from IoEvent.h (missing in MinGW) + { + VolumeUuid v; + UUID uuid; + } mapping[] = { + { UuidIoVolumeLock, // GUID_IO_VOLUME_LOCK + {0x50708874, 0xc9af, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoVolumeLockFailed, // GUID_IO_VOLUME_LOCK_FAILED + {0xae2eed10, 0x0ba8, 0x11d2, {0x8f, 0xfb, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoVolumeUnlock, // GUID_IO_VOLUME_UNLOCK + {0x9a8c3d68, 0xd0cb, 0x11d1, {0x8f, 0xef, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x32}} }, + { UuidIoMediaRemoval, // GUID_IO_MEDIA_REMOVAL + {0xd07433c1, 0xa98e, 0x11d2, {0x91, 0x7a, 0x0, 0xa0, 0xc9, 0x06, 0x8f, 0xf3}} } + }; + + static const VolumeUuidMapping *end = mapping + sizeof(mapping) / sizeof(mapping[0]); + const VolumeUuidMapping *m = + std::find_if(mapping, end, [&needle] (const VolumeUuidMapping &m) { return IsEqualGUID(m.uuid, needle); }); + return m != end ? m->v : UnknownUuid; +} + +inline void QWindowsRemovableDriveListener::handleDbtCustomEvent(const MSG *msg) +{ + const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam); + if (broadcastHeader->dbch_devicetype != DBT_DEVTYP_HANDLE) + return; + const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader); + const auto it = findByHDevNotify(m_removableDrives.cbegin(), m_removableDrives.cend(), + broadcastHandle->dbch_hdevnotify); + if (it == m_removableDrives.cend()) + return; + switch (volumeUuid(broadcastHandle->dbch_eventguid)) { + case UuidIoVolumeLock: // Received for removable USB media + emit driveLockForRemoval(pathFromEntry(*it)); + break; + case UuidIoVolumeLockFailed: + emit driveLockForRemovalFailed(pathFromEntry(*it)); + break; + case UuidIoVolumeUnlock: + break; + case UuidIoMediaRemoval: // Received for optical drives + break; + default: + break; + } +} + +// Handle WM_DEVICECHANGE+DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE which are +// sent to all top level windows and cannot be registered for (that is, their +// triggering depends on top level windows being present) +inline void QWindowsRemovableDriveListener::handleDbtDriveArrivalRemoval(const MSG *msg) +{ + const DEV_BROADCAST_HDR *broadcastHeader = reinterpret_cast<const DEV_BROADCAST_HDR *>(msg->lParam); + switch (broadcastHeader->dbch_devicetype) { + case DBT_DEVTYP_HANDLE: // WM_DEVICECHANGE/DBT_DEVTYP_HANDLE is sent for our registered drives. + if (msg->wParam == DBT_DEVICEREMOVECOMPLETE) { + const DEV_BROADCAST_HANDLE *broadcastHandle = reinterpret_cast<const DEV_BROADCAST_HANDLE *>(broadcastHeader); + const auto it = findByHDevNotify(m_removableDrives.begin(), m_removableDrives.end(), + broadcastHandle->dbch_hdevnotify); + // Emit for removable USB drives we were registered for. + if (it != m_removableDrives.end()) { + emit driveRemoved(pathFromEntry(*it)); + stopDeviceNotification(*it); + m_removableDrives.erase(it); + } + } + break; + case DBT_DEVTYP_VOLUME: { + const DEV_BROADCAST_VOLUME *broadcastVolume = reinterpret_cast<const DEV_BROADCAST_VOLUME *>(broadcastHeader); + // WM_DEVICECHANGE/DBT_DEVTYP_VOLUME messages are sent to all toplevel windows. Compare a hash value to ensure + // it is handled only once. + const quintptr newHash = reinterpret_cast<quintptr>(broadcastVolume) + msg->wParam + + quintptr(broadcastVolume->dbcv_flags) + quintptr(broadcastVolume->dbcv_unitmask); + if (newHash == m_lastMessageHash) + return; + m_lastMessageHash = newHash; + // Check for DBTF_MEDIA (inserted/Removed Optical drives). Ignore for now. + if (broadcastVolume->dbcv_flags & DBTF_MEDIA) + return; + // Continue with plugged in USB media where dbcv_flags=0. + switch (msg->wParam) { + case DBT_DEVICEARRIVAL: + emit driveAdded(); + break; + case DBT_DEVICEREMOVECOMPLETE: // handled by DBT_DEVTYP_HANDLE above + break; + } + } + break; + } +} + +bool QWindowsRemovableDriveListener::nativeEventFilter(const QByteArray &, void *messageIn, long *) +{ + const MSG *msg = reinterpret_cast<const MSG *>(messageIn); + if (msg->message == WM_DEVICECHANGE) { + switch (msg->wParam) { + case DBT_CUSTOMEVENT: + handleDbtCustomEvent(msg); + break; + case DBT_DEVICEARRIVAL: + case DBT_DEVICEREMOVECOMPLETE: + handleDbtDriveArrivalRemoval(msg); + break; + } + } + return false; +} + +// Set up listening for WM_DEVICECHANGE+DBT_CUSTOMEVENT for a removable drive path, +void QWindowsRemovableDriveListener::addPath(const QString &p) +{ + const wchar_t drive = p.size() >= 2 && p.at(0).isLetter() && p.at(1) == QLatin1Char(':') + ? wchar_t(p.at(0).toUpper().unicode()) : L'\0'; + if (!drive) + return; + // Already listening? + if (std::any_of(m_removableDrives.cbegin(), m_removableDrives.cend(), + [drive](const RemovableDriveEntry &e) { return e.drive == drive; })) { + return; + } + + wchar_t devicePath[8] = L"\\\\.\\A:\\"; + devicePath[4] = drive; + RemovableDriveEntry re; + re.drive = drive; + if (GetDriveTypeW(devicePath + 4) != DRIVE_REMOVABLE) + return; + const HANDLE volumeHandle = + CreateFile(devicePath, FILE_READ_ATTRIBUTES, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, // Volume requires BACKUP_SEMANTICS + 0); + if (volumeHandle == INVALID_HANDLE_VALUE) { + qErrnoWarning("CreateFile %s failed.", + qPrintable(QString::fromWCharArray(devicePath))); + return; + } + + DEV_BROADCAST_HANDLE notify; + ZeroMemory(¬ify, sizeof(notify)); + notify.dbch_size = sizeof(notify); + notify.dbch_devicetype = DBT_DEVTYP_HANDLE; + notify.dbch_handle = volumeHandle; + re.devNotify = RegisterDeviceNotification(winEventDispatcher()->internalHwnd(), + ¬ify, DEVICE_NOTIFY_WINDOW_HANDLE); + // Empirically found: The notifications also work when the handle is immediately + // closed. Do it here to avoid having to close/reopen in lock message handling. + CloseHandle(volumeHandle); + if (!re.devNotify) { + qErrnoWarning("RegisterDeviceNotification %s failed.", + qPrintable(QString::fromWCharArray(devicePath))); + return; + } + + m_removableDrives.push_back(re); +} +#endif // !Q_OS_WINRT + +/////////// +// QWindowsFileSystemWatcherEngine +/////////// QWindowsFileSystemWatcherEngine::Handle::Handle() : handle(INVALID_HANDLE_VALUE), flags(0u) { } +QWindowsFileSystemWatcherEngine::QWindowsFileSystemWatcherEngine(QObject *parent) + : QFileSystemWatcherEngine(parent) +#ifndef Q_OS_WINRT + , m_driveListener(new QWindowsRemovableDriveListener(this)) +#endif +{ +#ifndef Q_OS_WINRT + parent->setProperty("_q_driveListener", + QVariant::fromValue(static_cast<QObject *>(m_driveListener))); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemoval, + this, &QWindowsFileSystemWatcherEngine::driveLockForRemoval); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveLockForRemovalFailed, + this, &QWindowsFileSystemWatcherEngine::driveLockForRemovalFailed); + QObject::connect(m_driveListener, &QWindowsRemovableDriveListener::driveRemoved, + this, &QWindowsFileSystemWatcherEngine::driveRemoved); +#endif // !Q_OS_WINRT +} + QWindowsFileSystemWatcherEngine::~QWindowsFileSystemWatcherEngine() { for (auto *thread : qAsConst(threads)) @@ -210,6 +484,13 @@ QStringList QWindowsFileSystemWatcherEngine::addPaths(const QStringList &paths, } } } + +#ifndef Q_OS_WINRT + for (const QString &path : paths) { + if (!p.contains(path)) + m_driveListener->addPath(path); + } +#endif // !Q_OS_WINRT return p; } @@ -439,4 +720,9 @@ void QWindowsFileSystemWatcherEngineThread::wakeup() } QT_END_NAMESPACE + +#ifndef Q_OS_WINRT +# include "qfilesystemwatcher_win.moc" +#endif + #endif // QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_win_p.h b/src/corelib/io/qfilesystemwatcher_win_p.h index e8f5c49dec..51c7082483 100644 --- a/src/corelib/io/qfilesystemwatcher_win_p.h +++ b/src/corelib/io/qfilesystemwatcher_win_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class QWindowsFileSystemWatcherEngineThread; +class QWindowsRemovableDriveListener; // Even though QWindowsFileSystemWatcherEngine is derived of QThread // via QFileSystemWatcher, it does not start a thread. @@ -75,9 +76,7 @@ class QWindowsFileSystemWatcherEngine : public QFileSystemWatcherEngine { Q_OBJECT public: - inline QWindowsFileSystemWatcherEngine(QObject *parent) - : QFileSystemWatcherEngine(parent) - { } + explicit QWindowsFileSystemWatcherEngine(QObject *parent); ~QWindowsFileSystemWatcherEngine(); QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories); @@ -121,9 +120,17 @@ public: || lastModified != fileInfo.lastModified()); } }; + +signals: + void driveLockForRemoval(const QString &); + void driveLockForRemovalFailed(const QString &); + void driveRemoved(const QString &); + private: QList<QWindowsFileSystemWatcherEngineThread *> threads; - +#ifndef Q_OS_WINRT + QWindowsRemovableDriveListener *m_driveListener; +#endif }; class QFileSystemWatcherPathKey : public QString diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index bf2f47d399..b2e3df79b8 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -144,7 +144,7 @@ static inline bool setCloseOnExec(int fd) static inline QString msgOpenDirectory() { const char message[] = QT_TRANSLATE_NOOP("QIODevice", "file to open is a directory"); -#ifndef QT_BOOTSTRAPPED +#if QT_CONFIG(translation) return QIODevice::tr(message); #else return QLatin1String(message); diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 23a3cc1a16..45f0a0e8c0 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -1526,7 +1526,7 @@ void QProcess::setStandardOutputProcess(QProcess *destination) dto->stdinChannel.pipeFrom(dfrom); } -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) /*! \since 4.7 diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 4ce0503761..75a0bac2ed 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_PROCESS -#if !defined(Q_OS_WIN) || defined(Q_QDOC) +#if !defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) typedef qint64 Q_PID; #else QT_END_NAMESPACE @@ -187,7 +187,7 @@ public: void setStandardErrorFile(const QString &fileName, OpenMode mode = Truncate); void setStandardOutputProcess(QProcess *destination); -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) QString nativeArguments() const; void setNativeArguments(const QString &arguments); struct CreateProcessArguments @@ -206,7 +206,7 @@ public: typedef std::function<void(CreateProcessArguments *)> CreateProcessArgumentModifier; CreateProcessArgumentModifier createProcessArgumentsModifier() const; void setCreateProcessArgumentsModifier(CreateProcessArgumentModifier modifier); -#endif // Q_OS_WIN +#endif // Q_OS_WIN || Q_CLANG_QDOC QString workingDirectory() const; void setWorkingDirectory(const QString &dir); diff --git a/src/corelib/io/qsavefile.h b/src/corelib/io/qsavefile.h index 10857c27d1..0a6af91261 100644 --- a/src/corelib/io/qsavefile.h +++ b/src/corelib/io/qsavefile.h @@ -84,6 +84,9 @@ protected: private: void close() Q_DECL_OVERRIDE; +#if !QT_CONFIG(translation) + static QString tr(const char *string) { return QString::fromLatin1(string); } +#endif private: Q_DISABLE_COPY(QSaveFile) diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 8c67d97afa..1a69891d3b 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -128,7 +128,18 @@ Q_DECLARE_TYPEINFO(QConfFileCustomFormat, Q_MOVABLE_TYPE); typedef QHash<QString, QConfFile *> ConfFileHash; typedef QCache<QString, QConfFile> ConfFileCache; -typedef QHash<int, QString> PathHash; +namespace { + struct Path + { + // Note: Defining constructors explicitly because of buggy C++11 + // implementation in MSVC (uniform initialization). + Path() {} + Path(const QString & p, bool ud) : path(p), userDefined(ud) {} + QString path; + bool userDefined; //!< true - user defined, overridden by setPath + }; +} +typedef QHash<int, Path> PathHash; typedef QVector<QConfFileCustomFormat> CustomFormatVector; Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc) @@ -220,7 +231,7 @@ void QConfFile::clearCache() // QSettingsPrivate QSettingsPrivate::QSettingsPrivate(QSettings::Format format) - : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), spec(0), fallbacks(true), + : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) { } @@ -228,7 +239,7 @@ QSettingsPrivate::QSettingsPrivate(QSettings::Format format) QSettingsPrivate::QSettingsPrivate(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application) : format(format), scope(scope), organizationName(organization), applicationName(application), - iniCodec(0), spec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) + iniCodec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError) { } @@ -940,7 +951,7 @@ void QConfFileSettingsPrivate::initFormat() void QConfFileSettingsPrivate::initAccess() { - if (confFiles[spec]) { + if (!confFiles.isEmpty()) { if (format > QSettings::IniFormat) { if (!readFunc) setStatus(QSettings::AccessError); @@ -1070,22 +1081,22 @@ static void initDefaultPaths(QMutexLocker *locker) const QString programDataFolder = windowsConfigPath(FOLDERID_ProgramData); # endif pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), - roamingAppDataFolder + QDir::separator()); + Path(roamingAppDataFolder + QDir::separator(), false)); pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), - programDataFolder + QDir::separator()); + Path(programDataFolder + QDir::separator(), false)); #else const QString userPath = make_user_path(); - pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), userPath); - pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), systemPath); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), Path(userPath, false)); + pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), Path(systemPath, false)); #ifndef Q_OS_MAC - pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), userPath); - pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), systemPath); + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), Path(userPath, false)); + pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), Path(systemPath, false)); #endif #endif // Q_OS_WIN } } -static QString getPath(QSettings::Format format, QSettings::Scope scope) +static Path getPath(QSettings::Format format, QSettings::Scope scope) { Q_ASSERT((int)QSettings::NativeFormat == 0); Q_ASSERT((int)QSettings::IniFormat == 1); @@ -1095,14 +1106,23 @@ static QString getPath(QSettings::Format format, QSettings::Scope scope) if (pathHash->isEmpty()) initDefaultPaths(&locker); - QString result = pathHash->value(pathHashKey(format, scope)); - if (!result.isEmpty()) + Path result = pathHash->value(pathHashKey(format, scope)); + if (!result.path.isEmpty()) return result; // fall back on INI path return pathHash->value(pathHashKey(QSettings::IniFormat, scope)); } +#if defined(QT_BUILD_INTERNAL) && defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) +// Note: Suitable only for autotests. +void Q_AUTOTEST_EXPORT clearDefaultPaths() +{ + QMutexLocker locker(&settingsGlobalMutex); + pathHashFunc()->clear(); +} +#endif // QT_BUILD_INTERNAL && Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS + QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, QSettings::Scope scope, const QString &organization, @@ -1110,7 +1130,6 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, : QSettingsPrivate(format, scope, organization, application), nextPosition(0x40000000) // big positive number { - int i; initFormat(); QString org = organization; @@ -1123,22 +1142,43 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, QString orgFile = org + extension; if (scope == QSettings::UserScope) { - QString userPath = getPath(format, QSettings::UserScope); + Path userPath = getPath(format, QSettings::UserScope); if (!application.isEmpty()) - confFiles[F_User | F_Application].reset(QConfFile::fromName(userPath + appFile, true)); - confFiles[F_User | F_Organization].reset(QConfFile::fromName(userPath + orgFile, true)); + confFiles.append(QConfFile::fromName(userPath.path + appFile, true)); + confFiles.append(QConfFile::fromName(userPath.path + orgFile, true)); } - QString systemPath = getPath(format, QSettings::SystemScope); - if (!application.isEmpty()) - confFiles[F_System | F_Application].reset(QConfFile::fromName(systemPath + appFile, false)); - confFiles[F_System | F_Organization].reset(QConfFile::fromName(systemPath + orgFile, false)); - - for (i = 0; i < NumConfFiles; ++i) { - if (confFiles[i]) { - spec = i; - break; + Path systemPath = getPath(format, QSettings::SystemScope); +#if defined(Q_XDG_PLATFORM) && !defined(QT_NO_STANDARDPATHS) + // check if the systemPath wasn't overridden by QSettings::setPath() + if (!systemPath.userDefined) { + // Note: We can't use QStandardPaths::locateAll() as we need all the + // possible files (not just the existing ones) and there is no way + // to exclude user specific (XDG_CONFIG_HOME) directory from the search. + QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation); + // remove the QStandardLocation::writableLocation() (XDG_CONFIG_HOME) + if (!dirs.isEmpty()) + dirs.takeFirst(); + QStringList paths; + if (!application.isEmpty()) { + paths.reserve(dirs.size() * 2); + for (const auto &dir : qAsConst(dirs)) + paths.append(dir + QLatin1Char('/') + appFile); + } else { + paths.reserve(dirs.size()); } + for (const auto &dir : qAsConst(dirs)) + paths.append(dir + QLatin1Char('/') + orgFile); + + // Note: No check for existence of files is done intentionaly. + for (const auto &path : qAsConst(paths)) + confFiles.append(QConfFile::fromName(path, false)); + } else +#endif // Q_XDG_PLATFORM && !QT_NO_STANDARDPATHS + { + if (!application.isEmpty()) + confFiles.append(QConfFile::fromName(systemPath.path + appFile, false)); + confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false)); } initAccess(); @@ -1151,7 +1191,7 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, { initFormat(); - confFiles[0].reset(QConfFile::fromName(fileName, true)); + confFiles.append(QConfFile::fromName(fileName, true)); initAccess(); } @@ -1162,40 +1202,39 @@ QConfFileSettingsPrivate::~QConfFileSettingsPrivate() ConfFileHash *usedHash = usedHashFunc(); ConfFileCache *unusedCache = unusedCacheFunc(); - for (int i = 0; i < NumConfFiles; ++i) { - if (confFiles[i] && !confFiles[i]->ref.deref()) { - if (confFiles[i]->size == 0) { - delete confFiles[i].take(); + for (auto conf_file : qAsConst(confFiles)) { + if (!conf_file->ref.deref()) { + if (conf_file->size == 0) { + delete conf_file; } else { if (usedHash) - usedHash->remove(confFiles[i]->name); + usedHash->remove(conf_file->name); if (unusedCache) { QT_TRY { // compute a better size? - unusedCache->insert(confFiles[i]->name, confFiles[i].data(), - 10 + (confFiles[i]->originalKeys.size() / 4)); - confFiles[i].take(); + unusedCache->insert(conf_file->name, conf_file, + 10 + (conf_file->originalKeys.size() / 4)); } QT_CATCH(...) { // out of memory. Do not cache the file. - delete confFiles[i].take(); + delete conf_file; } } else { // unusedCache is gone - delete the entry to prevent a memory leak - delete confFiles[i].take(); + delete conf_file; } } } - // prevent the ScopedPointer to deref it again. - confFiles[i].take(); } } void QConfFileSettingsPrivate::remove(const QString &key) { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QSettingsKey theKey(key, caseSensitivity); QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity); QMutexLocker locker(&confFile->mutex); @@ -1219,10 +1258,12 @@ void QConfFileSettingsPrivate::remove(const QString &key) void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value) { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QSettingsKey theKey(key, caseSensitivity, nextPosition++); QMutexLocker locker(&confFile->mutex); confFile->removedKeys.remove(theKey); @@ -1235,29 +1276,27 @@ bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const ParsedSettingsMap::const_iterator j; bool found = false; - for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i].data()) { - QMutexLocker locker(&confFile->mutex); + for (auto confFile : qAsConst(confFiles)) { + QMutexLocker locker(&confFile->mutex); - if (!confFile->addedKeys.isEmpty()) { - j = confFile->addedKeys.constFind(theKey); - found = (j != confFile->addedKeys.constEnd()); - } - if (!found) { - ensureSectionParsed(confFile, theKey); - j = confFile->originalKeys.constFind(theKey); - found = (j != confFile->originalKeys.constEnd() - && !confFile->removedKeys.contains(theKey)); - } + if (!confFile->addedKeys.isEmpty()) { + j = confFile->addedKeys.constFind(theKey); + found = (j != confFile->addedKeys.constEnd()); + } + if (!found) { + ensureSectionParsed(confFile, theKey); + j = confFile->originalKeys.constFind(theKey); + found = (j != confFile->originalKeys.constEnd() + && !confFile->removedKeys.contains(theKey)); + } - if (found && value) - *value = *j; + if (found && value) + *value = *j; - if (found) - return true; - if (!fallbacks) - break; - } + if (found) + return true; + if (!fallbacks) + break; } return false; } @@ -1270,34 +1309,31 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec QSettingsKey thePrefix(prefix, caseSensitivity); int startPos = prefix.size(); - for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i].data()) { - QMutexLocker locker(&confFile->mutex); + for (auto confFile : qAsConst(confFiles)) { + QMutexLocker locker(&confFile->mutex); - if (thePrefix.isEmpty()) { - ensureAllSectionsParsed(confFile); - } else { - ensureSectionParsed(confFile, thePrefix); - } - - j = const_cast<const ParsedSettingsMap *>( - &confFile->originalKeys)->lowerBound( thePrefix); - while (j != confFile->originalKeys.constEnd() && j.key().startsWith(thePrefix)) { - if (!confFile->removedKeys.contains(j.key())) - processChild(j.key().originalCaseKey().midRef(startPos), spec, result); - ++j; - } + if (thePrefix.isEmpty()) + ensureAllSectionsParsed(confFile); + else + ensureSectionParsed(confFile, thePrefix); - j = const_cast<const ParsedSettingsMap *>( - &confFile->addedKeys)->lowerBound(thePrefix); - while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) { + j = const_cast<const ParsedSettingsMap *>( + &confFile->originalKeys)->lowerBound( thePrefix); + while (j != confFile->originalKeys.constEnd() && j.key().startsWith(thePrefix)) { + if (!confFile->removedKeys.contains(j.key())) processChild(j.key().originalCaseKey().midRef(startPos), spec, result); - ++j; - } + ++j; + } - if (!fallbacks) - break; + j = const_cast<const ParsedSettingsMap *>( + &confFile->addedKeys)->lowerBound(thePrefix); + while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) { + processChild(j.key().originalCaseKey().midRef(startPos), spec, result); + ++j; } + + if (!fallbacks) + break; } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), @@ -1307,10 +1343,12 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec void QConfFileSettingsPrivate::clear() { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return; + // Note: First config file is always the most specific. + QConfFile *confFile = confFiles.at(0); + QMutexLocker locker(&confFile->mutex); ensureAllSectionsParsed(confFile); confFile->addedKeys.clear(); @@ -1322,12 +1360,9 @@ void QConfFileSettingsPrivate::sync() // people probably won't be checking the status a whole lot, so in case of // error we just try to go on and make the best of it - for (int i = 0; i < NumConfFiles; ++i) { - QConfFile *confFile = confFiles[i].data(); - if (confFile) { - QMutexLocker locker(&confFile->mutex); - syncConfFile(i); - } + for (auto confFile : qAsConst(confFiles)) { + QMutexLocker locker(&confFile->mutex); + syncConfFile(confFile); } } @@ -1338,10 +1373,11 @@ void QConfFileSettingsPrivate::flush() QString QConfFileSettingsPrivate::fileName() const { - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return QString(); - return confFile->name; + + // Note: First config file is always the most specific. + return confFiles.at(0)->name; } bool QConfFileSettingsPrivate::isWritable() const @@ -1349,16 +1385,14 @@ bool QConfFileSettingsPrivate::isWritable() const if (format > QSettings::IniFormat && !writeFunc) return false; - QConfFile *confFile = confFiles[spec].data(); - if (!confFile) + if (confFiles.isEmpty()) return false; - return confFile->isWritable(); + return confFiles.at(0)->isWritable(); } -void QConfFileSettingsPrivate::syncConfFile(int confFileNo) +void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) { - QConfFile *confFile = confFiles[confFileNo].data(); bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty(); /* @@ -2188,9 +2222,10 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, \list 1 \li \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf}) \li \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf}) - \li \c{/etc/xdg/MySoft/Star Runner.conf} - \li \c{/etc/xdg/MySoft.conf} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.conf} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.conf} \endlist + \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. On \macos versions 10.2 and 10.3, these files are used by default: @@ -2225,9 +2260,10 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile, \list 1 \li \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini}) \li \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini}) - \li \c{/etc/xdg/MySoft/Star Runner.ini} - \li \c{/etc/xdg/MySoft.ini} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft/Star Runner.ini} + \li for each directory <dir> in $XDG_CONFIG_DIRS: \c{<dir>/MySoft.ini} \endlist + \note If XDG_CONFIG_DIRS is unset, the default value of \c{/etc/xdg} is used. On Windows, the following files are used: @@ -3380,7 +3416,7 @@ void QSettings::setPath(Format format, Scope scope, const QString &path) PathHash *pathHash = pathHashFunc(); if (pathHash->isEmpty()) initDefaultPaths(&locker); - pathHash->insert(pathHashKey(format, scope), path + QDir::separator()); + pathHash->insert(pathHashKey(format, scope), Path(path + QDir::separator(), true)); } /*! diff --git a/src/corelib/io/qsettings_mac.cpp b/src/corelib/io/qsettings_mac.cpp index b79abfb874..dcaefd1613 100644 --- a/src/corelib/io/qsettings_mac.cpp +++ b/src/corelib/io/qsettings_mac.cpp @@ -422,20 +422,15 @@ QMacSettingsPrivate::QMacSettingsPrivate(QSettings::Scope scope, const QString & javaPackageName.prepend(QLatin1String("com.")); suiteId = javaPackageName; - if (scope == QSettings::SystemScope) - spec |= F_System; - - if (application.isEmpty()) { - spec |= F_Organization; - } else { + if (!application.isEmpty()) { javaPackageName += QLatin1Char('.'); javaPackageName += application; applicationId = javaPackageName; } numDomains = 0; - for (int i = (spec & F_System) ? 1 : 0; i < 2; ++i) { - for (int j = (spec & F_Organization) ? 1 : 0; j < 3; ++j) { + for (int i = (scope == QSettings::SystemScope) ? 1 : 0; i < 2; ++i) { + for (int j = (application.isEmpty()) ? 1 : 0; j < 3; ++j) { SearchDomain &domain = domains[numDomains++]; domain.userName = (i == 0) ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser; if (j == 0) @@ -576,7 +571,7 @@ bool QMacSettingsPrivate::isWritable() const QString QMacSettingsPrivate::fileName() const { QString result; - if ((spec & F_System) == 0) + if (scope == QSettings::UserScope) result = QDir::homePath(); result += QLatin1String("/Library/Preferences/"); result += QString::fromCFString(domains[0].applicationOrSuiteId); diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index e6d3413bab..639605d8c4 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -236,19 +236,6 @@ public: QTextCodec *codec); static QStringList splitArgs(const QString &s, int idx); - /* - The numeric values of these enums define their search order. For example, - F_User | F_Organization is searched before F_System | F_Application, - because their values are respectively 1 and 2. - */ - enum { - F_Application = 0x0, - F_Organization = 0x1, - F_User = 0x0, - F_System = 0x2, - NumConfFiles = 4 - }; - QSettings::Format format; QSettings::Scope scope; QString organizationName; @@ -258,7 +245,6 @@ public: protected: QStack<QSettingsGroup> groupStack; QString groupPrefix; - int spec; bool fallbacks; bool pendingChanges; mutable QSettings::Status status; @@ -293,7 +279,7 @@ public: private: void initFormat(); void initAccess(); - void syncConfFile(int confFileNo); + void syncConfFile(QConfFile *confFile); bool writeIniFile(QIODevice &device, const ParsedSettingsMap &map); #ifdef Q_OS_MAC bool readPlistFile(const QByteArray &data, ParsedSettingsMap *map) const; @@ -302,7 +288,7 @@ private: void ensureAllSectionsParsed(QConfFile *confFile) const; void ensureSectionParsed(QConfFile *confFile, const QSettingsKey &key) const; - QScopedSharedPointer<QConfFile> confFiles[NumConfFiles]; + QVector<QConfFile *> confFiles; QSettings::ReadFunc readFunc; QSettings::WriteFunc writeFunc; QString extension; diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index 1c10548cbc..edcae16776 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -138,21 +138,6 @@ static void mergeKeySets(NameSet *dest, const QStringList &src) ** Wrappers for the insane windows registry API */ -static QString errorCodeToString(DWORD errorCode) -{ - wchar_t *data = 0; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, data, 0, 0); - QString result = QString::fromWCharArray(data); - - if (data != 0) - LocalFree(data); - - if (result.endsWith(QLatin1Char('\n'))) - result.truncate(result.length() - 1); - - return result; -} - // Open a key with the specified "perms". // "access" is to explicitly use the 32- or 64-bit branch. static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0) @@ -184,7 +169,7 @@ static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSub return resultHandle; //qWarning("QSettings: Failed to create subkey \"%s\": %s", - // rSubKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + // qPrintable(rSubKey), qPrintable(qt_error_string(int(res)))); return 0; } @@ -224,7 +209,7 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS &numKeys, &maxKeySize, 0, 0, 0); if (res != ERROR_SUCCESS) { - qWarning("QSettings: RegQueryInfoKey() failed: %s", errorCodeToString(res).toLatin1().data()); + qWarning("QSettings: RegQueryInfoKey() failed: %s", qPrintable(qt_error_string(int(res)))); return result; } @@ -258,7 +243,7 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS item = QString::fromWCharArray((const wchar_t *)buff.constData(), l); if (res != ERROR_SUCCESS) { - qWarning("QSettings: RegEnumValue failed: %s", errorCodeToString(res).toLatin1().data()); + qWarning("QSettings: RegEnumValue failed: %s", qPrintable(qt_error_string(int(res)))); continue; } if (item.isEmpty()) @@ -313,7 +298,7 @@ static void deleteChildGroups(HKEY parentHandle, REGSAM access = 0) LONG res = RegDeleteKey(parentHandle, reinterpret_cast<const wchar_t *>(group.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteKey failed on subkey \"%s\": %s", - group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(group), qPrintable(qt_error_string(int(res)))); return; } } @@ -613,7 +598,7 @@ QWinSettingsPrivate::~QWinSettingsPrivate() DWORD res = RegDeleteKey(writeHandle(), reinterpret_cast<const wchar_t *>(emptyKey.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: Failed to delete key \"%s\": %s", - regList.at(0).key().toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(regList.at(0).key()), qPrintable(qt_error_string(int(res)))); } } @@ -652,7 +637,7 @@ void QWinSettingsPrivate::remove(const QString &uKey) LONG res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(group.utf16())); if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteValue failed on subkey \"%s\": %s", - group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(group), qPrintable(qt_error_string(int(res)))); } } } else { @@ -660,7 +645,7 @@ void QWinSettingsPrivate::remove(const QString &uKey) if (res != ERROR_SUCCESS) { qWarning("QSettings: RegDeleteKey failed on key \"%s\": %s", - rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(rKey), qPrintable(qt_error_string(int(res)))); } } RegCloseKey(handle); @@ -758,7 +743,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value) deleteWriteHandleOnExit = false; } else { qWarning("QSettings: failed to set subkey \"%s\": %s", - rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + qPrintable(rKey), qPrintable(qt_error_string(int(res)))); setStatus(QSettings::AccessError); } diff --git a/src/corelib/io/qstorageinfo.cpp b/src/corelib/io/qstorageinfo.cpp index 3773b72606..9885da1af9 100644 --- a/src/corelib/io/qstorageinfo.cpp +++ b/src/corelib/io/qstorageinfo.cpp @@ -260,7 +260,7 @@ QByteArray QStorageInfo::fileSystemType() const devpath like \c /dev/sda0 for local storages. On Windows, it returns the UNC path starting with \c \\\\?\\ for local storages (in other words, the volume GUID). - \sa rootPath() + \sa rootPath(), subvolume() */ QByteArray QStorageInfo::device() const { @@ -268,6 +268,26 @@ QByteArray QStorageInfo::device() const } /*! + \since 5.8 + Returns the subvolume name for this volume. + + Some filesystem types allow multiple subvolumes inside one device, which + may be mounted in different paths. If the subvolume could be detected, it + is returned here. The format of the subvolume name is specific to each + filesystem type. + + If this volume was not mounted from a subvolume of a larger filesystem or + if the subvolume could not be detected, this function returns an empty byte + array. + + \sa device() +*/ +QByteArray QStorageInfo::subvolume() const +{ + return d->subvolume; +} + +/*! Returns the human-readable name of a filesystem, usually called \c label. Not all filesystems support this feature. In this case, the value returned by diff --git a/src/corelib/io/qstorageinfo.h b/src/corelib/io/qstorageinfo.h index 8941c11a16..e2d9747ceb 100644 --- a/src/corelib/io/qstorageinfo.h +++ b/src/corelib/io/qstorageinfo.h @@ -71,6 +71,7 @@ public: QString rootPath() const; QByteArray device() const; + QByteArray subvolume() const; QByteArray fileSystemType() const; QString name() const; QString displayName() const; @@ -100,7 +101,7 @@ inline bool operator==(const QStorageInfo &first, const QStorageInfo &second) { if (first.d == second.d) return true; - return first.device() == second.device(); + return first.device() == second.device() && first.rootPath() == second.rootPath(); } inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second) diff --git a/src/corelib/io/qstorageinfo_p.h b/src/corelib/io/qstorageinfo_p.h index a14fa8480a..ec5bb785e3 100644 --- a/src/corelib/io/qstorageinfo_p.h +++ b/src/corelib/io/qstorageinfo_p.h @@ -85,6 +85,7 @@ protected: public: QString rootPath; QByteArray device; + QByteArray subvolume; QByteArray fileSystemType; QString name; diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index ae5c42ffd1..fcc7b8ca50 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -120,6 +120,7 @@ public: inline QString rootPath() const; inline QByteArray fileSystemType() const; inline QByteArray device() const; + inline QByteArray options() const; private: #if defined(Q_OS_BSD4) QT_STATFSBUF *stat_buf; @@ -133,6 +134,7 @@ private: QByteArray m_rootPath; QByteArray m_fileSystemType; QByteArray m_device; + QByteArray m_options; #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) FILE *fp; mntent mnt; @@ -228,6 +230,11 @@ inline QByteArray QStorageIterator::device() const return QByteArray(stat_buf[currentIndex].f_mntfromname); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #elif defined(Q_OS_SOLARIS) static const char pathMounted[] = "/etc/mnttab"; @@ -301,6 +308,7 @@ inline bool QStorageIterator::next() m_device = data.at(0); m_rootPath = data.at(1); m_fileSystemType = data.at(2); + m_options = data.at(3); return true; } @@ -320,6 +328,11 @@ inline QByteArray QStorageIterator::device() const return m_device; } +inline QByteArray QStorageIterator::options() const +{ + return m_options; +} + #elif defined(Q_OS_LINUX) || defined(Q_OS_HURD) static const char pathMounted[] = "/etc/mtab"; @@ -363,6 +376,11 @@ inline QByteArray QStorageIterator::device() const return QByteArray(mnt.mnt_fsname); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(mnt.mnt_opts); +} + #elif defined(Q_OS_HAIKU) inline QStorageIterator::QStorageIterator() { @@ -420,6 +438,11 @@ inline QByteArray QStorageIterator::device() const return m_device; } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #else inline QStorageIterator::QStorageIterator() @@ -455,8 +478,35 @@ inline QByteArray QStorageIterator::device() const return QByteArray(); } +inline QByteArray QStorageIterator::options() const +{ + return QByteArray(); +} + #endif +static QByteArray extractSubvolume(const QStorageIterator &it) +{ +#ifdef Q_OS_LINUX + if (it.fileSystemType() == "btrfs") { + const QByteArrayList opts = it.options().split(','); + QByteArray id; + for (const QByteArray &opt : opts) { + static const char subvol[] = "subvol="; + static const char subvolid[] = "subvolid="; + if (opt.startsWith(subvol)) + return std::move(opt).mid(strlen(subvol)); + if (opt.startsWith(subvolid)) + id = std::move(opt).mid(strlen(subvolid)); + } + + // if we didn't find the subvolume name, return the subvolume ID + return id; + } +#endif + return QByteArray(); +} + void QStorageInfoPrivate::initRootPath() { rootPath = QFileInfo(rootPath).canonicalFilePath(); @@ -483,6 +533,7 @@ void QStorageInfoPrivate::initRootPath() rootPath = mountDir; device = it.device(); fileSystemType = fsName; + subvolume = extractSubvolume(it); } } } diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp index 6e50a8513e..b2bf9fce97 100644 --- a/src/corelib/io/qtemporarydir.cpp +++ b/src/corelib/io/qtemporarydir.cpp @@ -305,6 +305,33 @@ QString QTemporaryDir::path() const } /*! + \since 5.9 + + Returns the path name of a file in the temporary directory. + Does \e not check if the file actually exists in the directory. + Redundant multiple separators or "." and ".." directories in + \a fileName are not removed (see QDir::cleanPath()). Absolute + paths are not allowed. +*/ +QString QTemporaryDir::filePath(const QString &fileName) const +{ + if (QDir::isAbsolutePath(fileName)) { + qWarning("QTemporaryDir::filePath: Absolute paths are not allowed: %s", qUtf8Printable(fileName)); + return QString(); + } + + if (!d_ptr->success) + return QString(); + + QString ret = d_ptr->pathOrError; + if (!fileName.isEmpty()) { + ret += QLatin1Char('/'); + ret += fileName; + } + return ret; +} + +/*! Returns \c true if the QTemporaryDir is in auto remove mode. Auto-remove mode will automatically delete the directory from disk upon destruction. This makes it very easy to create your diff --git a/src/corelib/io/qtemporarydir.h b/src/corelib/io/qtemporarydir.h index 2e70d34ae4..3f6b70a2eb 100644 --- a/src/corelib/io/qtemporarydir.h +++ b/src/corelib/io/qtemporarydir.h @@ -65,6 +65,7 @@ public: bool remove(); QString path() const; + QString filePath(const QString &fileName) const; private: QScopedPointer<QTemporaryDirPrivate> d_ptr; diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 4fdb1505ff..9b565bff9d 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -2547,6 +2547,7 @@ QTextStream &QTextStream::operator<<(double f) } uint flags = 0; + const QLocale::NumberOptions numberOptions = locale().numberOptions(); if (numberFlags() & ShowBase) flags |= QLocaleData::ShowBase; if (numberFlags() & ForceSign) @@ -2555,12 +2556,18 @@ QTextStream &QTextStream::operator<<(double f) flags |= QLocaleData::UppercaseBase; if (numberFlags() & UppercaseDigits) flags |= QLocaleData::CapitalEorX; - if (numberFlags() & ForcePoint) - flags |= QLocaleData::Alternate; - if (locale() != QLocale::c() && !(locale().numberOptions() & QLocale::OmitGroupSeparator)) + if (numberFlags() & ForcePoint) { + flags |= QLocaleData::ForcePoint; + + // Only for backwards compatibility + flags |= QLocaleData::AddTrailingZeroes | QLocaleData::ShowBase; + } + if (locale() != QLocale::c() && !(numberOptions & QLocale::OmitGroupSeparator)) flags |= QLocaleData::ThousandsGroup; - if (!(locale().numberOptions() & QLocale::OmitLeadingZeroInExponent)) + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; const QLocaleData *dd = d->locale.d->m_data; QString num = dd->doubleToString(f, d->params.realNumberPrecision, form, -1, flags); diff --git a/src/corelib/itemmodels/qitemselectionmodel.cpp b/src/corelib/itemmodels/qitemselectionmodel.cpp index 6390d5f389..6c7101d41f 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.cpp +++ b/src/corelib/itemmodels/qitemselectionmodel.cpp @@ -274,8 +274,6 @@ QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange & */ /*! - \fn bool QItemSelectionRange::operator<(const QItemSelectionRange &other) const - Returns \c true if the selection range is less than the \a other range given; otherwise returns \c false. @@ -284,6 +282,33 @@ QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange & class can be used with QMap. */ +bool QItemSelectionRange::operator<(const QItemSelectionRange &other) const +{ + // ### Qt 6: This is inconsistent with op== and needs to be fixed, nay, + // ### removed, but cannot, because it was inline up to and including 5.9 + + // Comparing parents will compare the models, but if two equivalent ranges + // in two different models have invalid parents, they would appear the same + if (other.tl.model() == tl.model()) { + // parent has to be calculated, so we only do so once. + const QModelIndex topLeftParent = tl.parent(); + const QModelIndex otherTopLeftParent = other.tl.parent(); + if (topLeftParent == otherTopLeftParent) { + if (other.tl.row() == tl.row()) { + if (other.tl.column() == tl.column()) { + if (other.br.row() == br.row()) { + return br.column() < other.br.column(); + } + return br.row() < other.br.row(); + } + return tl.column() < other.tl.column(); + } + return tl.row() < other.tl.row(); + } + return topLeftParent < otherTopLeftParent; + } + return tl.model() < other.tl.model(); +} /*! \fn bool QItemSelectionRange::isValid() const diff --git a/src/corelib/itemmodels/qitemselectionmodel.h b/src/corelib/itemmodels/qitemselectionmodel.h index 3d3cb00750..92c459a243 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.h +++ b/src/corelib/itemmodels/qitemselectionmodel.h @@ -116,30 +116,7 @@ public: { return (tl == other.tl && br == other.br); } inline bool operator!=(const QItemSelectionRange &other) const { return !operator==(other); } - inline bool operator<(const QItemSelectionRange &other) const - { - // Comparing parents will compare the models, but if two equivalent ranges - // in two different models have invalid parents, they would appear the same - if (other.tl.model() == tl.model()) { - // parent has to be calculated, so we only do so once. - const QModelIndex topLeftParent = tl.parent(); - const QModelIndex otherTopLeftParent = other.tl.parent(); - if (topLeftParent == otherTopLeftParent) { - if (other.tl.row() == tl.row()) { - if (other.tl.column() == tl.column()) { - if (other.br.row() == br.row()) { - return br.column() < other.br.column(); - } - return br.row() < other.br.row(); - } - return tl.column() < other.tl.column(); - } - return tl.row() < other.tl.row(); - } - return topLeftParent < otherTopLeftParent; - } - return tl.model() < other.tl.model(); - } + bool operator<(const QItemSelectionRange &other) const; inline bool isValid() const { diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm index d7e8d4847a..231afb991c 100644 --- a/src/corelib/kernel/qcore_mac_objc.mm +++ b/src/corelib/kernel/qcore_mac_objc.mm @@ -47,14 +47,8 @@ #include <qdebug.h> -#if defined(Q_OS_IOS) -#import <UIKit/UIKit.h> -#endif - QT_BEGIN_NAMESPACE -typedef qint16 (*GestaltFunction)(quint32 selector, qint32 *response); - // ------------------------------------------------------------------------- QDebug operator<<(QDebug dbg, const NSObject *nsObject) @@ -87,54 +81,6 @@ QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TY // ------------------------------------------------------------------------- -QAppleOperatingSystemVersion qt_apple_os_version() -{ - QAppleOperatingSystemVersion v = {0, 0, 0}; -#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_10, __IPHONE_8_0) || defined(Q_OS_TVOS) || defined(Q_OS_WATCHOS) - if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { - NSOperatingSystemVersion osv = NSProcessInfo.processInfo.operatingSystemVersion; - v.major = osv.majorVersion; - v.minor = osv.minorVersion; - v.patch = osv.patchVersion; - return v; - } -#endif - // Use temporary variables so we can return 0.0.0 (unknown version) - // in case of an error partway through determining the OS version - qint32 major = 0, minor = 0, patch = 0; -#if QT_MAC_DEPLOYMENT_TARGET_BELOW(__MAC_10_10, __IPHONE_8_0) -#if defined(Q_OS_IOS) - @autoreleasepool { - NSArray *parts = [UIDevice.currentDevice.systemVersion componentsSeparatedByString:@"."]; - major = parts.count > 0 ? [[parts objectAtIndex:0] intValue] : 0; - minor = parts.count > 1 ? [[parts objectAtIndex:1] intValue] : 0; - patch = parts.count > 2 ? [[parts objectAtIndex:2] intValue] : 0; - } -#elif defined(Q_OS_OSX) - static GestaltFunction pGestalt = 0; - if (!pGestalt) { - CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreServices")); - pGestalt = reinterpret_cast<GestaltFunction>(CFBundleGetFunctionPointerForName(b, - CFSTR("Gestalt"))); - } - if (!pGestalt) - return v; - if (pGestalt('sys1', &major) != 0) - return v; - if (pGestalt('sys2', &minor) != 0) - return v; - if (pGestalt('sys3', &patch) != 0) - return v; -#endif -#endif - v.major = major; - v.minor = minor; - v.patch = patch; - return v; -} - -// ------------------------------------------------------------------------- - QMacAutoReleasePool::QMacAutoReleasePool() : pool([[NSAutoreleasePool alloc] init]) { diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index cb709f9d4b..d0edef33a2 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -131,12 +131,6 @@ private: QString string; }; -typedef struct { - int major, minor, patch; -} QAppleOperatingSystemVersion; - -QAppleOperatingSystemVersion qt_apple_os_version(); - #ifdef Q_OS_OSX Q_CORE_EXPORT QChar qt_mac_qtKey2CocoaKey(Qt::Key key); Q_CORE_EXPORT Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode); diff --git a/src/corelib/kernel/qdeadlinetimer.cpp b/src/corelib/kernel/qdeadlinetimer.cpp index d670637d53..99d223ca88 100644 --- a/src/corelib/kernel/qdeadlinetimer.cpp +++ b/src/corelib/kernel/qdeadlinetimer.cpp @@ -173,9 +173,9 @@ Q_DECL_CONST_FUNCTION static inline QPair<qint64, qint64> toSecsAndNSecs(qint64 */ /*! - \fn QDeadlineTimer::QDeadlineTimer(ForeverConstant foreverConstant, Qt::TimerType timerType) + \fn QDeadlineTimer::QDeadlineTimer(ForeverConstant forever, Qt::TimerType timerType) - QDeadlineTimer objects created with parameter \a foreverConstant never expire. + QDeadlineTimer objects created with parameter \a forever never expire. For such objects, remainingTime() will return -1, deadline() will return the maximum value, and isForever() will return true. @@ -341,54 +341,6 @@ void QDeadlineTimer::setPreciseRemainingTime(qint64 secs, qint64 nsecs, Qt::Time */ /*! - \fn std::chrono::nanoseconds remainingTimeAsDuration() const - - Returns a \c{std::chrono::duration} object of type \c{Duration} containing - the remaining time in this QDeadlineTimer, if it still has time left. If - the deadline has passed, this returns \c{Duration::zero()}, whereas if the - object is set to never expire, it returns \c{Duration::max()} (instead of - -1). - - It is not possible to obtain the overdue time for expired timers with this - function. To do that, see deadline(). - - \note The overload of this function without template parameter always - returns milliseconds. - - \sa setRemainingTime(), deadline<Clock, Duration>() -*/ - -/*! - \overload - \fn std::chrono::time_point<Clock, Duration> QDeadlineTimer::deadline() const - - Returns the absolute time point for the deadline stored in QDeadlineTimer - object as a \c{std::chrono::time_point} object. The template parameter - \c{Clock} is mandatory and indicates which of the C++ timekeeping clocks to - use as a reference. The value will be in the past if this QDeadlineTimer - has expired. - - If this QDeadlineTimer never expires, this function returns - \c{std::chrono::time_point<Clock, Duration>::max()}. - - This function can be used to calculate the amount of time a timer is - overdue, by subtracting the current time point of the reference clock, as - in the following example: - - \code - auto realTimeLeft = std::chrono::nanoseconds::max(); - auto tp = deadline.deadline<std::chrono::steady_clock>(); - if (tp != std::chrono::steady_clock::max()) - realTimeLeft = tp - std::chrono::steady_clock::now(); - \endcode - - \note Timers that were created as expired have an indetermine time point in - the past as their deadline, so the above calculation may not work. - - \sa remainingTime(), deadlineNSecs(), setDeadline() -*/ - -/*! \fn bool QDeadlineTimer::isForever() const Returns true if this QDeadlineTimer object never expires, false otherwise. @@ -789,6 +741,30 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) Q_DECL_ To subtract times of precision greater than 1 millisecond, use addNSecs(). */ +/*! + \fn void QDeadlineTimer::swap(QDeadlineTimer &other) + + Swaps this deadline timer with the \a other deadline timer. + */ + +/*! + \fn QDeadlineTimer & QDeadlineTimer::operator=(std::chrono::time_point<Clock, Duration> deadline_) + + Assigns \a deadline_ to this deadline timer. + */ + +/*! + \fn QDeadlineTimer & QDeadlineTimer::operator=(std::chrono::duration<Rep, Period> remaining) + + Sets this deadline timer to the \a remaining time. + */ + +/*! + \fn std::chrono::nanoseconds QDeadlineTimer::remainingTimeAsDuration() const + + Returns the time remaining before the deadline. + */ + // the rest of the functions are in qelapsedtimer_xxx.cpp QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 1a0efae2dc..75ac104155 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -42,6 +42,7 @@ #include "qcoreapplication.h" #include <private/qsystemlibrary_p.h> +#include "qoperatingsystemversion.h" #include "qpair.h" #include "qset.h" #include "qsocketnotifier.h" @@ -236,7 +237,7 @@ static inline UINT inputTimerMask() // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8. #if WINVER > 0x0601 - if (QSysInfo::WindowsVersion < QSysInfo::WV_WINDOWS8) + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) result &= ~(QS_TOUCH | QS_POINTER); #endif // WINVER > 0x0601 return result; @@ -1058,4 +1059,11 @@ void QEventDispatcherWin32::sendPostedEvents() QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); } +HWND QEventDispatcherWin32::internalHwnd() +{ + Q_D(QEventDispatcherWin32); + createInternalHwnd(); + return d->internalHwnd; +} + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index 773315c04f..227fcf89ff 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -106,6 +106,8 @@ public: bool event(QEvent *e); + HWND internalHwnd(); + protected: QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent = 0); virtual void sendPostedEvents(); diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 63b02d2c0c..fd1c767b4c 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -147,12 +147,13 @@ static inline QMutex *signalSlotLock(const QObject *o) uint(quintptr(o)) % sizeof(_q_ObjectMutexPool)/sizeof(QBasicMutex)]); } -// ### Qt >= 5.6, remove qt_add/removeObject +#if QT_VERSION < 0x60000 extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) {} extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) {} +#endif struct QConnectionSenderSwitcher { QObject *receiver; @@ -754,30 +755,6 @@ void QMetaCallEvent::placeMetaCall(QObject *object) \sa {Object Trees & Ownership} */ -/*! - \relates QObject - - Returns a pointer to the object named \a name that inherits \a - type and with a given \a parent. - - Returns 0 if there is no such child. - - \snippet code/src_corelib_kernel_qobject.cpp 0 -*/ - -void *qt_find_obj_child(QObject *parent, const char *type, const QString &name) -{ - QObjectList list = parent->children(); - if (list.size() == 0) return 0; - for (int i = 0; i < list.size(); ++i) { - QObject *obj = list.at(i); - if (name == obj->objectName() && obj->inherits(type)) - return obj; - } - return 0; -} - - /***************************************************************************** QObject member functions *****************************************************************************/ @@ -835,7 +812,9 @@ QObject::QObject(QObject *parent) QT_RETHROW; } } +#if QT_VERSION < 0x60000 qt_addObject(this); +#endif if (Q_UNLIKELY(qtHookData[QHooks::AddQObject])) reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this); } @@ -868,7 +847,9 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent) QT_RETHROW; } } +#if QT_VERSION < 0x60000 qt_addObject(this); +#endif if (Q_UNLIKELY(qtHookData[QHooks::AddQObject])) reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this); } @@ -1040,7 +1021,9 @@ QObject::~QObject() if (!d->children.isEmpty()) d->deleteChildren(); +#if QT_VERSION < 0x60000 qt_removeObject(this); +#endif if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject])) reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this); @@ -3941,9 +3924,8 @@ QList<QByteArray> QObject::dynamicPropertyNames() const QObject debugging output routines. *****************************************************************************/ -static void dumpRecursive(int level, QObject *object) +static void dumpRecursive(int level, const QObject *object) { -#if defined(QT_DEBUG) if (object) { QByteArray buf; buf.fill(' ', level / 2 * 8); @@ -3972,45 +3954,65 @@ static void dumpRecursive(int level, QObject *object) dumpRecursive(level+1, children.at(i)); } } -#else - Q_UNUSED(level) - Q_UNUSED(object) -#endif } /*! - Dumps a tree of children to the debug output. + \overload + \obsolete - This function is useful for debugging, but does nothing if the - library has been compiled in release mode (i.e. without debugging - information). + Dumps a tree of children to the debug output. \sa dumpObjectInfo() */ void QObject::dumpObjectTree() { + const_cast<const QObject *>(this)->dumpObjectTree(); +} + +/*! + Dumps a tree of children to the debug output. + + \note before Qt 5.9, this function was not const. + + \sa dumpObjectInfo() +*/ + +void QObject::dumpObjectTree() const +{ dumpRecursive(0, this); } /*! + \overload + \obsolete + Dumps information about signal connections, etc. for this object to the debug output. - This function is useful for debugging, but does nothing if the - library has been compiled in release mode (i.e. without debugging - information). - \sa dumpObjectTree() */ void QObject::dumpObjectInfo() { -#if defined(QT_DEBUG) + const_cast<const QObject *>(this)->dumpObjectInfo(); +} + +/*! + Dumps information about signal connections, etc. for this object + to the debug output. + + \note before Qt 5.9, this function was not const. + + \sa dumpObjectTree() +*/ + +void QObject::dumpObjectInfo() const +{ qDebug("OBJECT %s::%s", metaObject()->className(), objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); - Q_D(QObject); + Q_D(const QObject); QMutexLocker locker(signalSlotLock(this)); // first, look for connections where this object is the sender @@ -4066,7 +4068,6 @@ void QObject::dumpObjectInfo() } else { qDebug(" <None>"); } -#endif } #ifndef QT_NO_USERDATA diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 69b70ad6ec..25acdefeaf 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -375,8 +375,12 @@ public: #endif //Q_QDOC - void dumpObjectTree(); - void dumpObjectInfo(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void dumpObjectTree(); // ### Qt 6: remove + void dumpObjectInfo(); // ### Qt 6: remove +#endif + void dumpObjectTree() const; + void dumpObjectInfo() const; #ifndef QT_NO_PROPERTIES bool setProperty(const char *name, const QVariant &value); diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp index 1967899a58..83da09da44 100644 --- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp +++ b/src/corelib/kernel/qsystemsemaphore_systemv.cpp @@ -72,7 +72,13 @@ QT_BEGIN_NAMESPACE key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) { if (key.isEmpty()){ - errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + errorString = +#if QT_CONFIG(translation) + QCoreApplication::tr("%1: key is empty", "QSystemSemaphore") +#else + QString::fromLatin1("%1: key is empty") +#endif + .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; return -1; } @@ -84,16 +90,30 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) // Create the file needed for ftok int built = QSharedMemoryPrivate::createUnixKeyFile(fileName); if (-1 == built) { - errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + errorString = +#if QT_CONFIG(translation) + QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore") +#else + QString::fromLatin1("%1: unable to make key") +#endif + .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; return -1; } createdFile = (1 == built); +#if !defined(QT_NO_SHAREDMEMORY) && !defined(QT_POSIX_IPC) && !defined(Q_OS_ANDROID) // Get the unix key for the created file unix_key = qt_safe_ftok(QFile::encodeName(fileName), 'Q'); +#endif if (-1 == unix_key) { - errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + errorString = +#if QT_CONFIG(translation) + QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore") +#else + QString::fromLatin1("%1: ftok failed") +#endif + .arg(QLatin1String("QSystemSemaphore::handle:")); error = QSystemSemaphore::KeyError; return -1; } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 0ffc22d810..4a4d5b9294 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2914,7 +2914,7 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = static const size_t qCanConvertMatrixMaximumTargetType = 8 * sizeof(*qCanConvertMatrix); #ifndef QT_BOOTSTRAPPED -/*! +/* Returns \c true if from inherits to. */ static bool canConvertMetaObject(const QMetaObject *from, const QMetaObject *to) diff --git a/src/corelib/kernel/qwineventnotifier.h b/src/corelib/kernel/qwineventnotifier.h index f17fa059a1..f29f325d13 100644 --- a/src/corelib/kernel/qwineventnotifier.h +++ b/src/corelib/kernel/qwineventnotifier.h @@ -42,7 +42,7 @@ #include "QtCore/qobject.h" -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) QT_BEGIN_NAMESPACE diff --git a/src/corelib/mimetypes/qmimetypeparser.cpp b/src/corelib/mimetypes/qmimetypeparser.cpp index 7693055cc1..20b4461b03 100644 --- a/src/corelib/mimetypes/qmimetypeparser.cpp +++ b/src/corelib/mimetypes/qmimetypeparser.cpp @@ -165,7 +165,8 @@ bool QMimeTypeParserBase::parseNumber(const QStringRef &n, int *target, QString bool ok; *target = n.toInt(&ok); if (Q_UNLIKELY(!ok)) { - *errorMessage = QLatin1String("Not a number '") + n + QLatin1String("'."); + if (errorMessage) + *errorMessage = QLatin1String("Not a number '") + n + QLatin1String("'."); return false; } return true; diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 55af7256be..a04536b18b 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -63,6 +63,29 @@ QT_WARNING_POP static const QArrayData &qt_array_empty = qt_array[0]; static const QArrayData &qt_array_unsharable_empty = qt_array[1]; +static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, size_t headerSize, + uint options) +{ + // Calculate the byte size + // allocSize = objectSize * capacity + headerSize, but checked for overflow + // plus padded to grow in size + if (options & QArrayData::Grow) { + auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize); + capacity = r.elementCount; + return r.size; + } else { + return qCalculateBlockSize(capacity, objectSize, headerSize); + } +} + +static QArrayData *reallocateData(QArrayData *header, size_t allocSize, uint options) +{ + header = static_cast<QArrayData *>(::realloc(header, allocSize)); + if (header) + header->capacityReserved = bool(options & QArrayData::CapacityReserved); + return header; +} + QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, size_t capacity, AllocationOptions options) Q_DECL_NOTHROW { @@ -91,18 +114,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, if (headerSize > size_t(MaxAllocSize)) return 0; - // Calculate the byte size - // allocSize = objectSize * capacity + headerSize, but checked for overflow - // plus padded to grow in size - size_t allocSize; - if (options & Grow) { - auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize); - capacity = r.elementCount; - allocSize = r.size; - } else { - allocSize = qCalculateBlockSize(capacity, objectSize, headerSize); - } - + size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); if (header) { quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) @@ -122,6 +134,21 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, return header; } +QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, size_t capacity, + AllocationOptions options) Q_DECL_NOTHROW +{ + Q_ASSERT(data); + Q_ASSERT(data->isMutable()); + Q_ASSERT(!data->ref.isShared()); + + size_t headerSize = sizeof(QArrayData); + size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); + QArrayData *header = static_cast<QArrayData *>(reallocateData(data, allocSize, options)); + if (header) + header->alloc = capacity; + return header; +} + void QArrayData::deallocate(QArrayData *data, size_t objectSize, size_t alignment) Q_DECL_NOTHROW { diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 5a369baf08..bc20932cca 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -115,6 +115,9 @@ struct Q_CORE_EXPORT QArrayData static QArrayData *allocate(size_t objectSize, size_t alignment, size_t capacity, AllocationOptions options = Default) Q_DECL_NOTHROW Q_REQUIRED_RESULT; + static QArrayData *reallocateUnaligned(QArrayData *data, size_t objectSize, + size_t newCapacity, AllocationOptions newOptions = Default) + Q_DECL_NOTHROW Q_REQUIRED_RESULT; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment) Q_DECL_NOTHROW; @@ -222,6 +225,14 @@ struct QTypedArrayData Q_ALIGNOF(AlignmentDummy), capacity, options)); } + static QTypedArrayData *reallocateUnaligned(QTypedArrayData *data, size_t capacity, + AllocationOptions options = Default) + { + Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData)); + return static_cast<QTypedArrayData *>(QArrayData::reallocateUnaligned(data, sizeof(T), + capacity, options)); + } + static void deallocate(QArrayData *data) { Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData)); diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 9298472305..7d9c5dc325 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -663,6 +663,20 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel) */ #ifndef QT_NO_COMPRESS +namespace { +struct QByteArrayDataDeleter +{ + static inline void cleanup(QTypedArrayData<char> *d) + { if (d) QTypedArrayData<char>::deallocate(d); } +}; +} + +static QByteArray invalidCompressedData() +{ + qWarning("qUncompress: Input data is corrupted"); + return QByteArray(); +} + QByteArray qUncompress(const uchar* data, int nbytes) { if (!data) { @@ -677,53 +691,29 @@ QByteArray qUncompress(const uchar* data, int nbytes) ulong expectedSize = uint((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] )); ulong len = qMax(expectedSize, 1ul); - QScopedPointer<QByteArray::Data, QScopedPointerPodDeleter> d; + const ulong maxPossibleSize = MaxAllocSize - sizeof(QByteArray::Data); + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } + QScopedPointer<QByteArray::Data, QByteArrayDataDeleter> d(QByteArray::Data::allocate(expectedSize + 1)); + if (Q_UNLIKELY(d.data() == nullptr)) + return invalidCompressedData(); + + d->size = expectedSize; forever { ulong alloc = len; - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + alloc + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - d->offset = sizeof(QByteArrayData); - d->size = 0; // Shut up valgrind "uninitialized variable" warning int res = ::uncompress((uchar*)d->data(), &len, data+4, nbytes-4); switch (res) { case Z_OK: - if (len != alloc) { - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + len + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - } - d->ref.initializeOwned(); + Q_ASSERT(len <= alloc); + Q_UNUSED(alloc); d->size = len; - d->alloc = uint(len) + 1u; - d->capacityReserved = false; - d->offset = sizeof(QByteArrayData); d->data()[len] = 0; - { QByteArrayDataPtr dataPtr = { d.take() }; return QByteArray(dataPtr); @@ -735,6 +725,17 @@ QByteArray qUncompress(const uchar* data, int nbytes) case Z_BUF_ERROR: len *= 2; + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } else { + // grow the block + QByteArray::Data *p = QByteArray::Data::reallocateUnaligned(d.data(), len + 1); + if (Q_UNLIKELY(p == nullptr)) + return invalidCompressedData(); + d.take(); // don't free + d.reset(p); + } continue; case Z_DATA_ERROR: @@ -1707,19 +1708,8 @@ void QByteArray::reallocData(uint alloc, Data::AllocationOptions options) Data::deallocate(d); d = x; } else { - size_t blockSize; - if (options & Data::Grow) { - auto r = qCalculateGrowingBlockSize(alloc, sizeof(QChar), sizeof(Data)); - blockSize = r.size; - alloc = uint(r.elementCount); - } else { - blockSize = qCalculateBlockSize(alloc, sizeof(QChar), sizeof(Data)); - } - - Data *x = static_cast<Data *>(::realloc(d, blockSize)); + Data *x = Data::reallocateUnaligned(d, alloc, options); Q_CHECK_PTR(x); - x->alloc = alloc; - x->capacityReserved = (options & Data::CapacityReserved) ? 1 : 0; d = x; } } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 477402d6de..6875cbd764 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -252,7 +252,7 @@ public: void truncate(int pos); void chop(int n); -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) # if defined(Q_CC_GNU) // required due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61941 # pragma push_macro("Q_REQUIRED_RESULT") diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index 287861eee8..c820892307 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -424,7 +424,7 @@ QT_BEGIN_NAMESPACE \enum QChar::Direction This enum type defines the Unicode direction attributes. See the - \l{http://www.unicode.org/}{Unicode Standard} for a description + \l{http://www.unicode.org/reports/tr9/tr9-35.html#Table_Bidirectional_Character_Types}{Unicode Standard} for a description of the values. In order to conform to C/C++ naming conventions "Dir" is prepended diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index a642358770..d52f03acae 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -929,7 +929,7 @@ QString QDate::toString(Qt::DateFormat format) const */ QString QDate::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING @@ -1333,6 +1333,7 @@ QDate QDate::fromString(const QString &string, const QString &format) QDate date; #if QT_CONFIG(timezone) QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, &date, 0); #else @@ -1676,7 +1677,7 @@ QString QTime::toString(Qt::DateFormat format) const */ QString QTime::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING /*! @@ -2030,6 +2031,7 @@ QTime QTime::fromString(const QString &string, const QString &format) QTime time; #if QT_CONFIG(timezone) QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, 0, &time); #else @@ -2592,7 +2594,7 @@ static void refreshDateTime(QDateTimeData &d) if (!d->m_timeZone.isValid()) status &= ~QDateTimePrivate::ValidDateTime; else - epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, &testDate, &testTime); + epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime); } #endif // timezone @@ -2913,11 +2915,13 @@ inline QDateTime::Data QDateTimePrivate::create(const QDate &toDate, const QTime } // Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs +// DST transitions are disambiguated by hint. inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone, + DaylightStatus hint, QDate *localDate, QTime *localTime) { // Get the effective data from QTimeZone - QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs); + QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. if (data.atMSecsSinceEpoch >= 0) { @@ -3530,7 +3534,8 @@ qint64 QDateTime::toMSecsSinceEpoch() const #if !QT_CONFIG(timezone) return 0; #else - return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone); + return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, + extractDaylightStatus(getStatus(d))); #endif } Q_UNREACHABLE(); @@ -3629,10 +3634,16 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. d.detach(); - if (msecs >= 0) + if (msecs >= 0) { + status = mergeDaylightStatus(status, + d->m_timeZone.d->isDaylightTime(msecs) + ? QDateTimePrivate::DaylightTime + : QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs); - else + } else { + status = mergeDaylightStatus(status, QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs); + } msecs = msecs + (d->m_offsetFromUtc * 1000); status = status | QDateTimePrivate::ValidDate @@ -3896,7 +3907,7 @@ QString QDateTime::toString(Qt::DateFormat format) const */ QString QDateTime::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING @@ -3920,7 +3931,10 @@ static inline void massageAdjustedDateTime(const QDateTimeData &d, QDate *date, localMSecsToEpochMSecs(timeToMSecs(*date, *time), &status, date, time); #if QT_CONFIG(timezone) } else if (spec == Qt::TimeZone) { - QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), d->m_timeZone, date, time); + QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), + d->m_timeZone, + QDateTimePrivate::UnknownDaylightTime, + date, time); #endif // timezone } } @@ -4950,6 +4964,7 @@ QDateTime QDateTime::fromString(const QString &string, const QString &format) QDate date; QDateTimeParser dt(QVariant::DateTime, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) return QDateTime(date, time); #else diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h index eb33dddbb7..a217990c8d 100644 --- a/src/corelib/tools/qdatetime_p.h +++ b/src/corelib/tools/qdatetime_p.h @@ -134,6 +134,7 @@ public: #if QT_CONFIG(timezone) static qint64 zoneMSecsToEpochMSecs(qint64 msecs, const QTimeZone &zone, + DaylightStatus hint = UnknownDaylightTime, QDate *localDate = 0, QTime *localTime = 0); #endif // timezone diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index f499681ca9..77f7ba963b 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -2018,6 +2018,8 @@ QString QLocale::toString(double i, char f, int prec) const flags |= QLocaleData::ThousandsGroup; if (!(d->m_numberOptions & OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (d->m_numberOptions & IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; return d->m_data->doubleToString(i, prec, form, -1, flags); } @@ -2779,7 +2781,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q reinterpret_cast<ushort *>(digits.data())[i] += z; } - bool always_show_decpt = (flags & Alternate || flags & ForcePoint); + bool always_show_decpt = (flags & ForcePoint); switch (form) { case DFExponent: { num_str = exponentForm(_zero, decimal, exponential, group, plus, minus, @@ -2794,7 +2796,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q break; } case DFSignificantDigits: { - PrecisionMode mode = (flags & Alternate) ? + PrecisionMode mode = (flags & AddTrailingZeroes) ? PMSignificantDigits : PMChopTrailingZeros; int cutoff = precision < 0 ? 6 : precision; @@ -2899,7 +2901,7 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, for (int i = num_str.length()/* - cnt_thousand_sep*/; i < precision; ++i) num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0')); - if ((flags & Alternate || flags & ShowBase) + if ((flags & ShowBase) && base == 8 && (num_str.isEmpty() || num_str[0].unicode() != QLatin1Char('0'))) num_str.prepend(QLatin1Char('0')); @@ -2920,10 +2922,10 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, --num_pad_chars; // leave space for optional '0x' in hex form - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && (flags & ShowBase)) num_pad_chars -= 2; // leave space for optional '0b' in binary form - else if (base == 2 && (flags & Alternate || flags & ShowBase)) + else if (base == 2 && (flags & ShowBase)) num_pad_chars -= 2; for (int i = 0; i < num_pad_chars; ++i) @@ -2933,9 +2935,9 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, if (flags & CapitalEorX) num_str = num_str.toUpper(); - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && (flags & ShowBase)) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x")); - if (base == 2 && (flags & Alternate || flags & ShowBase)) + if (base == 2 && (flags & ShowBase)) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b")); // add sign @@ -2984,7 +2986,7 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, if (zeroPadding > 0) num_str.prepend(QString(zeroPadding, resultZero)); - if ((flags & Alternate || flags & ShowBase) + if ((flags & ShowBase) && base == 8 && (num_str.isEmpty() || num_str.at(0).unicode() != QLatin1Char('0'))) num_str.prepend(QLatin1Char('0')); @@ -2999,10 +3001,10 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, int num_pad_chars = width - num_str.length(); // leave space for optional '0x' in hex form - if (base == 16 && flags & Alternate) + if (base == 16 && flags & ShowBase) num_pad_chars -= 2; // leave space for optional '0b' in binary form - else if (base == 2 && flags & Alternate) + else if (base == 2 && flags & ShowBase) num_pad_chars -= 2; if (num_pad_chars > 0) @@ -3012,9 +3014,9 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, if (flags & CapitalEorX) num_str = num_str.toUpper(); - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && flags & ShowBase) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x")); - else if (base == 2 && (flags & Alternate || flags & ShowBase)) + else if (base == 2 && flags & ShowBase) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b")); // add sign @@ -3075,25 +3077,37 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti out = in.toLatin1(); else break; + } else if (out == '.') { + // Fail if more than one decimal point or point after e + if (decpt_idx != -1 || exponent_idx != -1) + return false; + decpt_idx = idx; + } else if (out == 'e' || out == 'E') { + exponent_idx = idx; } if (number_options & QLocale::RejectLeadingZeroInExponent) { - if (out == 'e' || out == 'E') { - exponent_idx = idx; - } else if (exponent_idx != -1) { - if (out >= '1' && out <= '9') - exponent_idx = -1; // leading digit is not 0, forget exponent_idx - else if (out == '0' && idx < l - 1) + if (exponent_idx != -1 && out == '0' && idx < l - 1) { + // After the exponent there can only be '+', '-' or digits. + // If we find a '0' directly after some non-digit, then that is a leading zero. + if (result->last() < '0' || result->last() > '9') return false; } } + if (number_options & QLocale::RejectTrailingZeroesAfterDot) { + // If we've seen a decimal point and the last character after the exponent is 0, then + // that is a trailing zero. + if (decpt_idx >= 0 && idx == exponent_idx && result->last() == '0') + return false; + } + if (!(number_options & QLocale::RejectGroupSeparator)) { if (start_of_digits_idx == -1 && out >= '0' && out <= '9') { start_of_digits_idx = idx; } else if (out == ',') { - // Don't allow group chars after the decimal point - if (decpt_idx != -1) + // Don't allow group chars after the decimal point or exponent + if (decpt_idx != -1 || exponent_idx != -1) return false; // check distance from the last separator or from the beginning of the digits @@ -3110,12 +3124,6 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti ++idx; continue; } else if (out == '.' || out == 'e' || out == 'E') { - // Fail if more than one decimal point - if (out == '.' && decpt_idx != -1) - return false; - if (decpt_idx == -1) - decpt_idx = idx; - // check distance from the last separator // ### FIXME: Some locales allow other groupings! See https://en.wikipedia.org/wiki/Thousands_separator if (last_separator_idx != -1 && idx - last_separator_idx != 4) @@ -3141,6 +3149,12 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti return false; } + if (number_options & QLocale::RejectTrailingZeroesAfterDot) { + // In decimal form, the last character can be a trailing zero if we've seen a decpt. + if (decpt_idx != -1 && exponent_idx == -1 && result->last() == '0') + return false; + } + result->append('\0'); return idx == l; } diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 657fce9fa1..bd89e48234 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -897,7 +897,9 @@ public: OmitGroupSeparator = 0x01, RejectGroupSeparator = 0x02, OmitLeadingZeroInExponent = 0x04, - RejectLeadingZeroInExponent = 0x08 + RejectLeadingZeroInExponent = 0x08, + IncludeTrailingZeroesAfterDot = 0x10, + RejectTrailingZeroesAfterDot = 0x20 }; Q_DECLARE_FLAGS(NumberOptions, NumberOption) diff --git a/src/corelib/tools/qlocale.qdoc b/src/corelib/tools/qlocale.qdoc index 8c5711fb5e..d419172356 100644 --- a/src/corelib/tools/qlocale.qdoc +++ b/src/corelib/tools/qlocale.qdoc @@ -949,7 +949,8 @@ setNumberOptions(). \value DefaultNumberOptions This option represents the default behavior, with - group separators and with one leading zero in single digit exponents. + group separators, with one leading zero in single digit exponents, and + without trailing zeroes after the decimal dot. \value OmitGroupSeparator If this option is set, the number-to-string functions will not insert group separators in their return values. The default is to insert group separators. @@ -964,6 +965,14 @@ functions will fail if they encounter an exponent padded with zeroes when parsing a floating point number in scientific notation. The default is to accept such padding. + \value IncludeTrailingZeroesAfterDot If this option is set, the number-to-string + functions will pad numbers with zeroes to the requested precision in "g" + or "most concise" mode, even if the number of significant digits is lower + than the requested precision. The default is to omit trailing zeroes. + \value RejectTrailingZeroesAfterDot If this option is set, the string-to-number + functions will fail if they encounter trailing zeroes after the decimal + dot when parsing a number in scientific or decimal representation. The + default is to accept trailing zeroes. \sa setNumberOptions(), numberOptions() */ diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index 20eff8fd64..7749f66b8e 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -193,7 +193,7 @@ public: enum Flags { NoFlags = 0, - Alternate = 0x01, + AddTrailingZeroes = 0x01, ZeroPadded = 0x02, LeftAdjusted = 0x04, BlankBeforePositive = 0x08, @@ -204,7 +204,7 @@ public: ShowBase = 0x80, UppercaseBase = 0x100, ZeroPadExponent = 0x200, - ForcePoint = Alternate + ForcePoint = 0x400 }; enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode }; diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 4a30daa72c..791a4970f0 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. -** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Copyright (C) 2016 Giuseppe D'Angelo <dangelog@gmail.com>. +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -45,7 +45,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qhashfunctions.h> -#include <QtCore/qmutex.h> +#include <QtCore/qreadwritelock.h> #include <QtCore/qvector.h> #include <QtCore/qstringlist.h> #include <QtCore/qdebug.h> @@ -54,7 +54,7 @@ #include <QtCore/qatomic.h> #include <QtCore/qdatastream.h> -#include <pcre.h> +#include <pcre2.h> QT_BEGIN_NAMESPACE @@ -548,7 +548,7 @@ QT_BEGIN_NAMESPACE \inmodule QtCore \reentrant - \brief The QRegularExpressionMatch class provides the results of matching + \brief The QRegularExpressionMatch class provides the results of a matching a QRegularExpression against a string. \since 5.0 @@ -789,19 +789,19 @@ static int convertToPcreOptions(QRegularExpression::PatternOptions patternOption int options = 0; if (patternOptions & QRegularExpression::CaseInsensitiveOption) - options |= PCRE_CASELESS; + options |= PCRE2_CASELESS; if (patternOptions & QRegularExpression::DotMatchesEverythingOption) - options |= PCRE_DOTALL; + options |= PCRE2_DOTALL; if (patternOptions & QRegularExpression::MultilineOption) - options |= PCRE_MULTILINE; + options |= PCRE2_MULTILINE; if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) - options |= PCRE_EXTENDED; + options |= PCRE2_EXTENDED; if (patternOptions & QRegularExpression::InvertedGreedinessOption) - options |= PCRE_UNGREEDY; + options |= PCRE2_UNGREEDY; if (patternOptions & QRegularExpression::DontCaptureOption) - options |= PCRE_NO_AUTO_CAPTURE; + options |= PCRE2_NO_AUTO_CAPTURE; if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) - options |= PCRE_UCP; + options |= PCRE2_UCP; return options; } @@ -814,7 +814,9 @@ static int convertToPcreOptions(QRegularExpression::MatchOptions matchOptions) int options = 0; if (matchOptions & QRegularExpression::AnchoredMatchOption) - options |= PCRE_ANCHORED; + options |= PCRE2_ANCHORED; + if (matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) + options |= PCRE2_NO_UTF_CHECK; return options; } @@ -856,20 +858,16 @@ struct QRegularExpressionPrivate : QSharedData QRegularExpression::PatternOptions patternOptions; QString pattern; - // *All* of the following members are set managed while holding this mutex, + // *All* of the following members are managed while holding this mutex, // except for isDirty which is set to true by QRegularExpression setters // (right after a detach happened). - // On the other hand, after the compilation and studying, - // it's safe to *use* (i.e. read) them from multiple threads at the same time. - // Therefore, doMatch doesn't need to lock this mutex. - QMutex mutex; + mutable QReadWriteLock mutex; - // The PCRE pointers are reference-counted by the QRegularExpressionPrivate + // The PCRE code pointer is reference-counted by the QRegularExpressionPrivate // objects themselves; when the private is copied (i.e. a detach happened) // they are set to 0 - pcre16 *compiledPattern; - QAtomicPointer<pcre16_extra> studyData; - const char *errorString; + pcre2_code_16 *compiledPattern; + int errorCode; int errorOffset; int capturingCount; unsigned int usedCount; @@ -884,8 +882,7 @@ struct QRegularExpressionMatchPrivate : QSharedData int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount = 0); + QRegularExpression::MatchOptions matchOptions); QRegularExpressionMatch nextMatch() const; @@ -934,10 +931,13 @@ QRegularExpression::QRegularExpression(QRegularExpressionPrivate &dd) \internal */ QRegularExpressionPrivate::QRegularExpressionPrivate() - : patternOptions(0), pattern(), + : QSharedData(), + patternOptions(0), + pattern(), mutex(), - compiledPattern(0), studyData(0), - errorString(0), errorOffset(-1), + compiledPattern(0), + errorCode(0), + errorOffset(-1), capturingCount(0), usedCount(0), usingCrLfNewlines(false), @@ -964,13 +964,16 @@ QRegularExpressionPrivate::~QRegularExpressionPrivate() */ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPrivate &other) : QSharedData(other), - patternOptions(other.patternOptions), pattern(other.pattern), + patternOptions(other.patternOptions), + pattern(other.pattern), mutex(), - compiledPattern(0), studyData(0), - errorString(0), - errorOffset(-1), capturingCount(0), + compiledPattern(0), + errorCode(0), + errorOffset(-1), + capturingCount(0), usedCount(0), - usingCrLfNewlines(false), isDirty(true) + usingCrLfNewlines(false), + isDirty(true) { } @@ -979,14 +982,13 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri */ void QRegularExpressionPrivate::cleanCompiledPattern() { - pcre16_free(compiledPattern); - pcre16_free_study(studyData.load()); - usedCount = 0; + pcre2_code_free_16(compiledPattern); compiledPattern = 0; - studyData.store(0); - usingCrLfNewlines = false; + errorCode = 0; errorOffset = -1; capturingCount = 0; + usedCount = 0; + usingCrLfNewlines = false; } /*! @@ -994,7 +996,7 @@ void QRegularExpressionPrivate::cleanCompiledPattern() */ void QRegularExpressionPrivate::compilePattern() { - QMutexLocker lock(&mutex); + const QWriteLocker lock(&mutex); if (!isDirty) return; @@ -1003,18 +1005,23 @@ void QRegularExpressionPrivate::compilePattern() cleanCompiledPattern(); int options = convertToPcreOptions(patternOptions); - options |= PCRE_UTF16; + options |= PCRE2_UTF; - int errorCode; - compiledPattern = pcre16_compile2(pattern.utf16(), options, - &errorCode, &errorString, &errorOffset, 0); + PCRE2_SIZE patternErrorOffset; + compiledPattern = pcre2_compile_16(pattern.utf16(), + pattern.length(), + options, + &errorCode, + &patternErrorOffset, + NULL); - if (!compiledPattern) + if (!compiledPattern) { + errorOffset = static_cast<int>(patternErrorOffset); return; - - Q_ASSERT(errorCode == 0); - Q_ASSERT(studyData.load() == 0); // studying (=>optimizing) is always done later - errorOffset = -1; + } else { + // ignore whatever PCRE2 wrote into errorCode -- leave it to 0 to mean "no error" + errorCode = 0; + } getPatternInfo(); } @@ -1025,53 +1032,31 @@ void QRegularExpressionPrivate::compilePattern() void QRegularExpressionPrivate::getPatternInfo() { Q_ASSERT(compiledPattern); - Q_ASSERT(studyData.load() == 0); - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount); + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_CAPTURECOUNT, &capturingCount); // detect the settings for the newline - unsigned long int patternNewlineSetting; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_OPTIONS, &patternNewlineSetting); - patternNewlineSetting &= PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF - | PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; - if (patternNewlineSetting == 0) { + unsigned int patternNewlineSetting; + if (pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_NEWLINE, &patternNewlineSetting) != 0) { // no option was specified in the regexp, grab PCRE build defaults - int pcreNewlineSetting; - pcre16_config(PCRE_CONFIG_NEWLINE, &pcreNewlineSetting); - switch (pcreNewlineSetting) { - case 13: - patternNewlineSetting = PCRE_NEWLINE_CR; break; - case 10: - patternNewlineSetting = PCRE_NEWLINE_LF; break; - case 3338: // (13<<8 | 10) - patternNewlineSetting = PCRE_NEWLINE_CRLF; break; - case -2: - patternNewlineSetting = PCRE_NEWLINE_ANYCRLF; break; - case -1: - patternNewlineSetting = PCRE_NEWLINE_ANY; break; - default: - qWarning("QRegularExpressionPrivate::compilePattern(): " - "PCRE_CONFIG_NEWLINE returned an unknown newline"); - break; - } + pcre2_config_16(PCRE2_CONFIG_NEWLINE, &patternNewlineSetting); } - usingCrLfNewlines = (patternNewlineSetting == PCRE_NEWLINE_CRLF) || - (patternNewlineSetting == PCRE_NEWLINE_ANY) || - (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); + usingCrLfNewlines = (patternNewlineSetting == PCRE2_NEWLINE_CRLF) || + (patternNewlineSetting == PCRE2_NEWLINE_ANY) || + (patternNewlineSetting == PCRE2_NEWLINE_ANYCRLF); - int hasJOptionChanged; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_JCHANGED, &hasJOptionChanged); - if (hasJOptionChanged) { - qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n" - " is using the (?J) option; duplicate capturing group names are not supported by Qt", + unsigned int hasJOptionChanged; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_JCHANGED, &hasJOptionChanged); + if (Q_UNLIKELY(hasJOptionChanged)) { + qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n is using the (?J) option; duplicate capturing group names are not supported by Qt", qPrintable(pattern)); } } /* - Simple "smartpointer" wrapper around a pcre_jit_stack, to be used with + Simple "smartpointer" wrapper around a pcre2_jit_stack_16, to be used with QThreadStorage. */ class QPcreJitStackPointer @@ -1086,7 +1071,7 @@ public: { // The default JIT stack size in PCRE is 32K, // we allocate from 32K up to 512K. - stack = pcre16_jit_stack_alloc(32*1024, 512*1024); + stack = pcre2_jit_stack_create_16(32 * 1024, 512 * 1024, NULL); } /*! \internal @@ -1094,10 +1079,10 @@ public: ~QPcreJitStackPointer() { if (stack) - pcre16_jit_stack_free(stack); + pcre2_jit_stack_free_16(stack); } - pcre16_jit_stack *stack; + pcre2_jit_stack_16 *stack; }; Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) @@ -1105,7 +1090,7 @@ Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) /*! \internal */ -static pcre16_jit_stack *qtPcreCallback(void *) +static pcre2_jit_stack_16 *qtPcreCallback(void *) { if (jitStacks()->hasLocalData()) return jitStacks()->localData()->stack; @@ -1135,53 +1120,32 @@ static bool isJitEnabled() /*! \internal - The purpose of the function is to call pcre16_study (which allows some - optimizations to be performed, including JIT-compiling the pattern), and - setting the studyData member variable to the result of the study. It gets - called by doMatch() every time a match is performed. As of now, the - optimizations on the pattern are performed after a certain number of usages - (i.e. the qt_qregularexpression_optimize_after_use_count constant) unless - the DontAutomaticallyOptimizeOption option is set on the QRegularExpression - object, or anyhow by calling optimize() (which will pass - ImmediateOptimizeOption). + The purpose of the function is to call pcre2_jit_compile_16, which + JIT-compiles the pattern. + + It gets called by doMatch() every time a match is performed. - Notice that although the method is protected by a mutex, one thread may - invoke this function and return immediately (i.e. not study the pattern, - leaving studyData to NULL); but before calling pcre16_exec to perform the - match, another thread performs the studying and sets studyData to something - else. Although the assignment to studyData is itself atomic, the release of - the memory pointed by studyData isn't. Therefore, we work on a local copy - (localStudyData) before using storeRelease on studyData. In doMatch there's - the corresponding loadAcquire. + As of now, the optimizations on the pattern are performed after a certain + number of usages (i.e. the qt_qregularexpression_optimize_after_use_count + constant) unless the DontAutomaticallyOptimizeOption option is set on the + QRegularExpression object, or anyhow by calling optimize() (which will pass + ImmediateOptimizeOption). */ void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) { Q_ASSERT(compiledPattern); - QMutexLocker lock(&mutex); + static const bool enableJit = isJitEnabled(); - if (studyData.load()) // already optimized + if (!enableJit) return; + const QWriteLocker lock(&mutex); + if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count)) return; - static const bool enableJit = isJitEnabled(); - - int studyOptions = 0; - if (enableJit) - studyOptions |= (PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE | PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE); - - const char *err; - pcre16_extra * const localStudyData = pcre16_study(compiledPattern, studyOptions, &err); - - if (localStudyData && localStudyData->flags & PCRE_EXTRA_EXECUTABLE_JIT) - pcre16_assign_jit_stack(localStudyData, qtPcreCallback, 0); - - if (!localStudyData && err) - qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); - - studyData.storeRelease(localStudyData); + pcre2_jit_compile_16(compiledPattern, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD); } /*! @@ -1197,7 +1161,7 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const if (!compiledPattern) return -1; - int index = pcre16_get_stringnumber(compiledPattern, name.utf16()); + int index = pcre2_substring_number_from_name_16(compiledPattern, name.utf16()); if (index >= 0) return index; @@ -1207,24 +1171,25 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const /*! \internal - This is a simple wrapper for pcre16_exec for handling the case in which the + This is a simple wrapper for pcre2_match_16 for handling the case in which the JIT runs out of memory. In that case, we allocate a thread-local JIT stack - and re-run pcre16_exec. + and re-run pcre2_match_16. */ -static int pcre16SafeExec(const pcre16 *code, const pcre16_extra *extra, - const unsigned short *subject, int length, - int startOffset, int options, - int *ovector, int ovecsize) +static int safe_pcre2_match_16(const pcre2_code_16 *code, + const unsigned short *subject, int length, + int startOffset, int options, + pcre2_match_data_16 *matchData, + pcre2_match_context_16 *matchContext) { - int result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + int result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); - if (result == PCRE_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { + if (result == PCRE2_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { QPcreJitStackPointer *p = new QPcreJitStackPointer; jitStacks()->setLocalData(p); - result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); } return result; @@ -1273,29 +1238,24 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString QRegularExpression re(*const_cast<QRegularExpressionPrivate *>(this)); + QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, + subjectStart, subjectLength, + matchType, matchOptions); + if (offset < 0 || offset > subjectLength) - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; - if (!compiledPattern) { + if (Q_UNLIKELY(!compiledPattern)) { qWarning("QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; } // skip optimizing and doing the actual matching if NoMatch type was requested if (matchType == QRegularExpression::NoMatch) { - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions); priv->isValid = true; return priv; } - // capturingCount doesn't include the implicit "0" capturing group - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions, - capturingCount + 1); - if (!(patternOptions & QRegularExpression::DontAutomaticallyOptimizeOption)) { const OptimizePatternOption optimizePatternOption = (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) @@ -1306,22 +1266,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption); } - // work with a local copy of the study data, as we are running pcre_exec - // potentially more than once, and we don't want to run call it - // with different study data - const pcre16_extra * const currentStudyData = studyData.loadAcquire(); - int pcreOptions = convertToPcreOptions(matchOptions); if (matchType == QRegularExpression::PartialPreferCompleteMatch) - pcreOptions |= PCRE_PARTIAL_SOFT; + pcreOptions |= PCRE2_PARTIAL_SOFT; else if (matchType == QRegularExpression::PartialPreferFirstMatch) - pcreOptions |= PCRE_PARTIAL_HARD; + pcreOptions |= PCRE2_PARTIAL_HARD; - if (checkSubjectStringOption == DontCheckSubjectString - || matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) { - pcreOptions |= PCRE_NO_UTF16_CHECK; - } + if (checkSubjectStringOption == DontCheckSubjectString) + pcreOptions |= PCRE2_NO_UTF_CHECK; bool previousMatchWasEmpty = false; if (previous && previous->hasMatch && @@ -1329,25 +1282,28 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString previousMatchWasEmpty = true; } - int * const captureOffsets = priv->capturedOffsets.data(); - const int captureOffsetsCount = priv->capturedOffsets.size(); + pcre2_match_context_16 *matchContext = pcre2_match_context_create_16(NULL); + pcre2_jit_stack_assign_16(matchContext, &qtPcreCallback, NULL); + pcre2_match_data_16 *matchData = pcre2_match_data_create_from_pattern_16(compiledPattern, NULL); const unsigned short * const subjectUtf16 = subject.utf16() + subjectStart; int result; + QReadLocker lock(&mutex); + if (!previousMatchWasEmpty) { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } else { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions | PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED, + matchData, matchContext); - if (result == PCRE_ERROR_NOMATCH) { + if (result == PCRE2_ERROR_NOMATCH) { ++offset; if (usingCrLfNewlines @@ -1360,13 +1316,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString ++offset; } - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } } + lock.unlock(); + #ifdef QREGULAREXPRESSION_DEBUG qDebug() << "Matching" << pattern << "against" << subject << "starting at" << subjectStart << "len" << subjectLength @@ -1386,10 +1344,10 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString priv->capturedOffsets.resize(result * 2); } else { // no match, partial match or error - priv->hasPartialMatch = (result == PCRE_ERROR_PARTIAL); - priv->isValid = (result == PCRE_ERROR_NOMATCH || result == PCRE_ERROR_PARTIAL); + priv->hasPartialMatch = (result == PCRE2_ERROR_PARTIAL); + priv->isValid = (result == PCRE2_ERROR_NOMATCH || result == PCRE2_ERROR_PARTIAL); - if (result == PCRE_ERROR_PARTIAL) { + if (result == PCRE2_ERROR_PARTIAL) { // partial match: // leave the start and end capture offsets (i.e. cap(0)) priv->capturedCount = 1; @@ -1401,6 +1359,35 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString } } + // copy the captured substrings offsets, if any + if (priv->capturedCount) { + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(matchData); + int * const capturedOffsets = priv->capturedOffsets.data(); + + for (int i = 0; i < priv->capturedCount * 2; ++i) + capturedOffsets[i] = static_cast<int>(ovector[i]); + + // For partial matches, PCRE2 and PCRE1 differ in behavior when lookbehinds + // are involved. PCRE2 reports the real begin of the match and the maximum + // used lookbehind as distinct information; PCRE1 instead automatically + // adjusted ovector[0] to include the maximum lookbehind. + // + // For instance, given the pattern "\bstring\b", and the subject "a str": + // * PCRE1 reports partial, capturing " str" + // * PCRE2 reports partial, capturing "str" with a lookbehind of 1 + // + // To keep behavior, emulate PCRE1 here. + // (Eventually, we could expose the lookbehind info in a future patch.) + if (result == PCRE2_ERROR_PARTIAL) { + unsigned int maximumLookBehind; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_MAXLOOKBEHIND, &maximumLookBehind); + capturedOffsets[0] -= maximumLookBehind; + } + } + + pcre2_match_data_free_16(matchData); + pcre2_match_context_free_16(matchContext); + return priv; } @@ -1412,19 +1399,13 @@ QRegularExpressionMatchPrivate::QRegularExpressionMatchPrivate(const QRegularExp int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount) + QRegularExpression::MatchOptions matchOptions) : regularExpression(re), subject(subject), subjectStart(subjectStart), subjectLength(subjectLength), matchType(matchType), matchOptions(matchOptions), capturedCount(0), hasMatch(false), hasPartialMatch(false), isValid(false) { - Q_ASSERT(capturingCount >= 0); - if (capturingCount > 0) { - const int captureOffsetsCount = capturingCount * 3; - capturedOffsets.resize(captureOffsetsCount); - } } @@ -1632,13 +1613,13 @@ QStringList QRegularExpression::namedCaptureGroups() const // contains one ushort followed by the name, NUL terminated. // The ushort is the numerical index of the name in the pattern. // The length of each entry is namedCapturingTableEntrySize. - ushort *namedCapturingTable; - int namedCapturingTableEntryCount; - int namedCapturingTableEntrySize; + PCRE2_SPTR16 *namedCapturingTable; + unsigned int namedCapturingTableEntryCount; + unsigned int namedCapturingTableEntrySize; - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMETABLE, &namedCapturingTable); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMECOUNT, &namedCapturingTableEntryCount); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMETABLE, &namedCapturingTable); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMECOUNT, &namedCapturingTableEntryCount); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); QStringList result; @@ -1647,9 +1628,9 @@ QStringList QRegularExpression::namedCaptureGroups() const for (int i = 0; i < d->capturingCount + 1; ++i) result.append(QString()); - for (int i = 0; i < namedCapturingTableEntryCount; ++i) { - const ushort * const currentNamedCapturingTableRow = namedCapturingTable + - namedCapturingTableEntrySize * i; + for (unsigned int i = 0; i < namedCapturingTableEntryCount; ++i) { + const ushort * const currentNamedCapturingTableRow = + reinterpret_cast<const ushort *>(namedCapturingTable) + namedCapturingTableEntrySize * i; const int index = *currentNamedCapturingTableRow; result[index] = QString::fromUtf16(currentNamedCapturingTableRow + 1); @@ -1680,8 +1661,19 @@ bool QRegularExpression::isValid() const QString QRegularExpression::errorString() const { d.data()->compilePattern(); - if (d->errorString) - return QCoreApplication::translate("QRegularExpression", d->errorString); + if (d->errorCode) { + QString errorString; + int errorStringLength; + do { + errorString.resize(errorString.length() + 64); + errorStringLength = pcre2_get_error_message_16(d->errorCode, + reinterpret_cast<ushort *>(errorString.data()), + errorString.length()); + } while (errorStringLength < 0); + errorString.resize(errorStringLength); + + return QCoreApplication::translate("QRegularExpression", errorString.toLatin1().constData()); + } return QCoreApplication::translate("QRegularExpression", "no error"); } @@ -2583,7 +2575,8 @@ QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match) and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2012 University of Cambridge + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2015 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -2625,80 +2618,149 @@ static const char *pcreCompileErrorCodes[] = QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating ] for character class"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in character class"), QT_TRANSLATE_NOOP("QRegularExpression", "range out of order in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "nothing to repeat"), + QT_TRANSLATE_NOOP("QRegularExpression", "quantifier does not follow a repeatable item"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unexpected repeat"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (? or (?-"), QT_TRANSLATE_NOOP("QRegularExpression", "POSIX named classes are supported only within a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing )"), + QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "reference to non-existent subpattern"), - QT_TRANSLATE_NOOP("QRegularExpression", "erroffset passed as NULL"), - QT_TRANSLATE_NOOP("QRegularExpression", "unknown option bit(s) set"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern passed as NULL"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognised compile-time option bit(s)"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after (?# comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "failed to get memory"), - QT_TRANSLATE_NOOP("QRegularExpression", "unmatched parentheses"), + QT_TRANSLATE_NOOP("QRegularExpression", "failed to allocate heap memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unmatched closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: code overflow"), - QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?<"), + QT_TRANSLATE_NOOP("QRegularExpression", "letter or underscore expected after (?< or (?'"), QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is not fixed length"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed number or name after (?("), QT_TRANSLATE_NOOP("QRegularExpression", "conditional group contains more than two branches"), - QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?("), + QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?( or (?(?C)"), QT_TRANSLATE_NOOP("QRegularExpression", "(?R or (?[+-]digits must be followed by )"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown POSIX class name"), - QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UTF8 support"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\x{...} sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error in pcre2_study(): should not occur"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have Unicode support"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\x{} or \\o{} is too large"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid condition (?(0)"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\C not allowed in lookbehind assertion"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\C is not allowed in a lookbehind assertion"), QT_TRANSLATE_NOOP("QRegularExpression", "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u"), - QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is > 255"), - QT_TRANSLATE_NOOP("QRegularExpression", "closing ) for (?C expected"), - QT_TRANSLATE_NOOP("QRegularExpression", "recursive call could loop indefinitely"), + QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is greater than 255"), + QT_TRANSLATE_NOOP("QRegularExpression", "closing parenthesis for (?C expected"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in (*VERB) name"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?P"), QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in subpattern name (missing terminator)"), - QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-8 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "support for \\P, \\p, and \\X has not been compiled"), + QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name (PCRE2_DUPNAMES not set)"), + QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have support for \\P, \\p, or \\X"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed \\P or \\p sequence"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown property name after \\P or \\p"), - QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum 32 characters)"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum 10000)"), - QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 (not in UTF-8 mode)"), + QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum " "10000" " characters)"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum " "256" ")"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 in 8-bit non-UTF-8 mode"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: overran compiling workspace"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: previously-checked referenced subpattern not found"), QT_TRANSLATE_NOOP("QRegularExpression", "DEFINE group contains more than one branch"), - QT_TRANSLATE_NOOP("QRegularExpression", "repeating a DEFINE group is not allowed"), - QT_TRANSLATE_NOOP("QRegularExpression", "inconsistent NEWLINE options"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown newline setting"), QT_TRANSLATE_NOOP("QRegularExpression", "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number"), QT_TRANSLATE_NOOP("QRegularExpression", "a numbered reference must not be zero"), QT_TRANSLATE_NOOP("QRegularExpression", "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)"), - QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized"), + QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized or malformed"), QT_TRANSLATE_NOOP("QRegularExpression", "number is too big"), QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name expected"), QT_TRANSLATE_NOOP("QRegularExpression", "digit expected after (?+"), - QT_TRANSLATE_NOOP("QRegularExpression", "] is an invalid data character in JavaScript compatibility mode"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), QT_TRANSLATE_NOOP("QRegularExpression", "different names for subpatterns of the same number are not allowed"), QT_TRANSLATE_NOOP("QRegularExpression", "(*MARK) must have an argument"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UCP support"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by an ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a printable ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a letter or one of [\\]^_?"), QT_TRANSLATE_NOOP("QRegularExpression", "\\k is not followed by a braced, angle-bracketed, or quoted name"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in find_fixedlength()"), QT_TRANSLATE_NOOP("QRegularExpression", "\\N is not supported in a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many forward references"), + QT_TRANSLATE_NOOP("QRegularExpression", "SPARE ERROR"), QT_TRANSLATE_NOOP("QRegularExpression", "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-16 string"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UTF is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UCP is disabled by the application"), QT_TRANSLATE_NOOP("QRegularExpression", "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\u.... sequence is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-32 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "setting UTF is disabled by the application"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), - QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}") + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\u.... sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}"), + QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in (?(VERSION condition"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in auto_possessify()"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating delimiter for callout with string argument"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized string delimiter follows (?C"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "(?| and/or (?J: or (?x: parentheses are too deeply nested"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled in this PCRE2 library"), + QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too complicated"), + QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is too long"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern string is longer than the limit set by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "no error"), + QT_TRANSLATE_NOOP("QRegularExpression", "no match"), + QT_TRANSLATE_NOOP("QRegularExpression", "partial match"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 1 byte missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 2 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 3 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 4 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 2 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 3 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 4 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 5 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 6 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 6-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 2-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 3-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 4-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 5-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 6-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: isolated byte with 0x80 bit set"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: illegal byte (0xfe or 0xff)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: missing low surrogate at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: invalid low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: isolated low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad data value"), + QT_TRANSLATE_NOOP("QRegularExpression", "patterns do not all use the same character tables"), + QT_TRANSLATE_NOOP("QRegularExpression", "magic number missing"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern compiled in wrong mode: 8/16/32-bit error"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset value"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad option value"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset into UTF string"), + QT_TRANSLATE_NOOP("QRegularExpression", "callout error code"), /* Never returned by PCRE2 itself */ + QT_TRANSLATE_NOOP("QRegularExpression", "invalid data in workspace for DFA restart"), + QT_TRANSLATE_NOOP("QRegularExpression", "too much recursion for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "backreference condition or recursion test is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "function is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern contains an item that is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "workspace size exceeded in DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error - pattern overwritten?"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad JIT option"), + QT_TRANSLATE_NOOP("QRegularExpression", "JIT stack limit reached"), + QT_TRANSLATE_NOOP("QRegularExpression", "match limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "no more memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown substring"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-unique substring name"), + QT_TRANSLATE_NOOP("QRegularExpression", "NULL argument passed"), + QT_TRANSLATE_NOOP("QRegularExpression", "nested recursion at the same subject position"), + QT_TRANSLATE_NOOP("QRegularExpression", "recursion limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not available"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not set"), + QT_TRANSLATE_NOOP("QRegularExpression", "offset limit set without PCRE2_USE_OFFSET_LIMIT"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad escape sequence in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "expected closing curly bracket in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad substitution in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "match with end before start is not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many replacements (more than INT_MAX)") }; #endif // #if 0 diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp index cb11e72435..8fa378e935 100644 --- a/src/corelib/tools/qringbuffer.cpp +++ b/src/corelib/tools/qringbuffer.cpp @@ -132,7 +132,6 @@ char *QRingBuffer::reserve(qint64 bytes) char *writePtr = buffers.last().data() + tail; bufferSize += bytes; - Q_ASSERT(bytes < MaxByteArraySize); tail += int(bytes); return writePtr; } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 8888eced87..8cf058f035 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -491,6 +491,30 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) return UnrollTailLoop<7>::exec(l, 0, lambda, lambda); # endif #endif +#if defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64 + if (l >= 8) { + const QChar *end = a + l; + const uint16x8_t mask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; + while (a + 8 < end) { + uint16x8_t da = vld1q_u16(reinterpret_cast<const uint16_t *>(a)); + uint16x8_t db = vld1q_u16(reinterpret_cast<const uint16_t *>(b)); + + uint8_t r = ~(uint8_t)vaddvq_u16(vandq_u16(vceqq_u16(da, db), mask)); + if (r) { + // found a different QChar + uint idx = qCountTrailingZeroBits(r); + return (int)a[idx].unicode() - (int)b[idx].unicode(); + } + a += 8; + b += 8; + } + l &= 7; + } + const auto &lambda = [=](int i) -> int { + return a[i].unicode() - b[i].unicode(); + }; + return UnrollTailLoop<7>::exec(l, 0, lambda, lambda); +#endif // __ARM_NEON__ if (!l) return 0; @@ -1617,7 +1641,7 @@ QString::QString(QChar ch) \internal */ -/*! \fn QString &QString::operator=(const Null &) +/*! \fn QString &QString::operator=(const QString::Null &) \internal */ @@ -1767,17 +1791,11 @@ void QString::resize(int size, QChar fillChar) void QString::reallocData(uint alloc, bool grow) { - size_t blockSize; - if (grow) { - auto r = qCalculateGrowingBlockSize(alloc, sizeof(QChar), sizeof(Data)); - blockSize = r.size; - alloc = uint(r.elementCount); - } else { - blockSize = qCalculateBlockSize(alloc, sizeof(QChar), sizeof(Data)); - } + auto allocOptions = d->detachFlags(); + if (grow) + allocOptions |= QArrayData::Grow; if (d->ref.isShared() || IS_RAW_DATA(d)) { - Data::AllocationOptions allocOptions(d->capacityReserved ? Data::CapacityReserved : 0); Data *x = Data::allocate(alloc, allocOptions); Q_CHECK_PTR(x); x->size = qMin(int(alloc) - 1, d->size); @@ -1787,11 +1805,9 @@ void QString::reallocData(uint alloc, bool grow) Data::deallocate(d); d = x; } else { - Data *p = static_cast<Data *>(::realloc(d, blockSize)); + Data *p = Data::reallocateUnaligned(d, alloc, allocOptions); Q_CHECK_PTR(p); d = p; - d->alloc = alloc; - d->offset = sizeof(QStringData); } } @@ -4466,11 +4482,11 @@ bool QString::startsWith(const QStringRef &s, Qt::CaseSensitivity cs) const \sa startsWith() */ -bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const +bool QString::endsWith(const QString &s, Qt::CaseSensitivity cs) const { return qt_ends_with(isNull() ? 0 : unicode(), size(), s.isNull() ? 0 : s.unicode(), s.size(), cs); - } +} /*! \since 4.8 @@ -4887,6 +4903,7 @@ QString QString::fromUcs4(const uint *unicode, int size) return QUtf32::convertToUnicode((const char *)unicode, size*4, 0); } + /*! Resizes the string to \a size characters and copies \a unicode into the string. @@ -5981,7 +5998,10 @@ static uint parse_flag_characters(const char * &c) Q_DECL_NOTHROW uint flags = QLocaleData::ZeroPadExponent; while (true) { switch (*c) { - case '#': flags |= QLocaleData::Alternate; break; + case '#': + flags |= QLocaleData::ShowBase | QLocaleData::AddTrailingZeroes + | QLocaleData::ForcePoint; + break; case '0': flags |= QLocaleData::ZeroPadded; break; case '-': flags |= QLocaleData::LeftAdjusted; break; case ' ': flags |= QLocaleData::BlankBeforePositive; break; @@ -6239,7 +6259,7 @@ QString QString::vasprintf(const char *cformat, va_list ap) case 'p': { void *arg = va_arg(ap, void*); const quint64 i = reinterpret_cast<quintptr>(arg); - flags |= QLocaleData::Alternate; + flags |= QLocaleData::ShowBase; subst = QLocaleData::c()->unsLongLongToString(i, precision, 16, width, flags); ++c; break; @@ -7814,10 +7834,13 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha if (d.locale_occurrences > 0) { QLocale locale; - if (!(locale.numberOptions() & QLocale::OmitGroupSeparator)) + const QLocale::NumberOptions numberOptions = locale.numberOptions(); + if (!(numberOptions & QLocale::OmitGroupSeparator)) flags |= QLocaleData::ThousandsGroup; - if (!(locale.numberOptions() & QLocale::OmitLeadingZeroInExponent)) + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; locale_arg = locale.d->m_data->doubleToString(a, prec, form, fieldWidth, flags); } @@ -8025,33 +8048,12 @@ bool QString::isSimpleText() const /*! \fn bool QString::isRightToLeft() const Returns \c true if the string is read right to left. + + \sa QStringRef::isRightToLeft() */ bool QString::isRightToLeft() const { - const ushort *p = d->data(); - const ushort * const end = p + d->size; - while (p < end) { - uint ucs4 = *p; - if (QChar::isHighSurrogate(ucs4) && p < end - 1) { - ushort low = p[1]; - if (QChar::isLowSurrogate(low)) { - ucs4 = QChar::surrogateToUcs4(ucs4, low); - ++p; - } - } - switch (QChar::direction(ucs4)) - { - case QChar::DirL: - return false; - case QChar::DirR: - case QChar::DirAL: - return true; - default: - break; - } - ++p; - } - return false; + return QStringRef(this).isRightToLeft(); } /*! \fn QChar *QString::data() @@ -9007,7 +9009,7 @@ ownership of it, no memory is freed when instances are destroyed. Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in the string. - \sa cbegin(), end(), rbegin(), rend() + \sa cbegin(), constBegin(), end(), constEnd(), rbegin(), rend() */ /*! @@ -9016,7 +9018,16 @@ ownership of it, no memory is freed when instances are destroyed. Same as begin(). - \sa begin(), cend(), rbegin(), rend() + \sa begin(), constBegin(), cend(), constEnd(), rbegin(), rend() +*/ + +/*! + \fn QStringRef::const_iterator QStringRef::constBegin() const + \since 5.9 + + Same as begin(). + + \sa begin(), cend(), constEnd(), rbegin(), rend() */ /*! @@ -9026,7 +9037,7 @@ ownership of it, no memory is freed when instances are destroyed. Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary character after the last character in the list. - \sa cbegin(), end(), rbegin(), rend() + \sa cbegin(), constBegin(), end(), constEnd(), rbegin(), rend() */ /*! \fn QStringRef::const_iterator QStringRef::cend() const @@ -9034,7 +9045,15 @@ ownership of it, no memory is freed when instances are destroyed. Same as end(). - \sa end(), cbegin(), rbegin(), rend() + \sa end(), constEnd(), cbegin(), constBegin(), rbegin(), rend() +*/ + +/*! \fn QStringRef::const_iterator QStringRef::constEnd() const + \since 5.9 + + Same as end(). + + \sa end(), cend(), cbegin(), constBegin(), rbegin(), rend() */ /*! @@ -9944,6 +9963,41 @@ int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const } /*! + \since 5.9 + + Returns \c true if the string is read right to left. + + \sa QString::isRightToLeft() +*/ +bool QStringRef::isRightToLeft() const +{ + const ushort *p = reinterpret_cast<const ushort*>(unicode()); + const ushort * const end = p + size(); + while (p < end) { + uint ucs4 = *p; + if (QChar::isHighSurrogate(ucs4) && p < end - 1) { + ushort low = p[1]; + if (QChar::isLowSurrogate(low)) { + ucs4 = QChar::surrogateToUcs4(ucs4, low); + ++p; + } + } + switch (QChar::direction(ucs4)) + { + case QChar::DirL: + return false; + case QChar::DirR: + case QChar::DirAL: + return true; + default: + break; + } + ++p; + } + return false; +} + +/*! \since 4.8 Returns \c true if the string reference starts with \a str; otherwise diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index b50b2ee4e5..08242bff11 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -397,7 +397,7 @@ public: QString leftJustified(int width, QChar fill = QLatin1Char(' '), bool trunc = false) const Q_REQUIRED_RESULT; QString rightJustified(int width, QChar fill = QLatin1Char(' '), bool trunc = false) const Q_REQUIRED_RESULT; -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) # if defined(Q_CC_GNU) // required due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61941 # pragma push_macro("Q_REQUIRED_RESULT") @@ -522,7 +522,7 @@ public: const ushort *utf16() const; -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) QByteArray toLatin1() const & Q_REQUIRED_RESULT { return toLatin1_helper(*this); } QByteArray toLatin1() && Q_REQUIRED_RESULT @@ -1400,7 +1400,8 @@ public: QStringRef(QStringRef &&other) Q_DECL_NOTHROW : m_string(other.m_string), m_position(other.m_position), m_size(other.m_size) {} QStringRef &operator=(QStringRef &&other) Q_DECL_NOTHROW { return *this = other; } #endif - QStringRef &operator=(const QStringRef &other) Q_DECL_NOTHROW { + QStringRef &operator=(const QStringRef &other) Q_DECL_NOTHROW + { m_string = other.m_string; m_position = other.m_position; m_size = other.m_size; return *this; } @@ -1449,6 +1450,8 @@ public: m_size -= n; } + bool isRightToLeft() const; + bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; @@ -1461,7 +1464,8 @@ public: inline QStringRef &operator=(const QString *string); - inline const QChar *unicode() const { + inline const QChar *unicode() const + { if (!m_string) return reinterpret_cast<const QChar *>(QString::Data::sharedNull()->data()); return m_string->unicode() + m_position; @@ -1471,8 +1475,10 @@ public: inline const_iterator begin() const { return unicode(); } inline const_iterator cbegin() const { return unicode(); } + inline const_iterator constBegin() const { return unicode(); } inline const_iterator end() const { return unicode() + size(); } inline const_iterator cend() const { return unicode() + size(); } + inline const_iterator constEnd() const { return unicode() + size(); } inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } inline const_reverse_iterator crbegin() const { return rbegin(); } inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp index 2ff03eddec..ea8f6d1438 100644 --- a/src/corelib/tools/qtimezoneprivate.cpp +++ b/src/corelib/tools/qtimezoneprivate.cpp @@ -49,10 +49,6 @@ QT_BEGIN_NAMESPACE -enum { - MSECS_TRAN_WINDOW = 21600000 // 6 hour window for possible recent transitions -}; - /* Static utilities for looking up Windows ID tables */ @@ -248,67 +244,206 @@ QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const } // Private only method for use by QDateTime to convert local msecs to epoch msecs -// TODO Could be platform optimised if needed -QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) const +QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs, int hint) const { - if (!hasDaylightTime() ||!hasTransitions()) { - // No DST means same offset for all local msecs - // Having DST but no transitions means we can't calculate, so use nearest - return data(forLocalMSecs - (standardTimeOffset(forLocalMSecs) * 1000)); - } + if (!hasDaylightTime()) // No DST means same offset for all local msecs + return data(forLocalMSecs - standardTimeOffset(forLocalMSecs) * 1000); - // Get the transition for the local msecs which most of the time should be the right one - // Only around the transition times might it not be the right one - Data tran = previousTransition(forLocalMSecs); - Data nextTran; - - // If the local msecs is less than the real local time of the transition - // then get the previous transition to use instead - if (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { - while (tran.atMSecsSinceEpoch != invalidMSecs() - && forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { - nextTran = tran; - tran = previousTransition(tran.atMSecsSinceEpoch); - } - } else { - // The zone msecs is after the transition, so check it is before the next tran - // If not try use the next transition instead - nextTran = nextTransition(tran.atMSecsSinceEpoch); + /* + We need a UTC time at which to ask for the offset, in order to be able to + add that offset to forLocalMSecs, to get the UTC time we + need. Fortunately, no time-zone offset is more than 14 hours; and DST + transitions happen (much) more than thirty-two hours apart. So sampling + offset sixteen hours each side gives us information we can be sure + brackets the correct time and at most one DST transition. + */ + const qint64 sixteenHoursInMSecs(16 * 3600 * 1000); + /* + Offsets are Local - UTC, positive to the east of Greenwich, negative to + the west; DST offset always exceeds standard offset, when DST applies. + When we have offsets on either side of a transition, the lower one is + standard, the higher is DST. + + Non-DST transitions (jurisdictions changing time-zone and time-zones + changing their standard offset, typically) are described below as if they + were DST transitions (since these are more usual and familiar); the code + mostly concerns itself with offsets from UTC, described in terms of the + common case for changes in that. If there is no actual change in offset + (e.g. a DST transition cancelled by a standard offset change), this code + should handle it gracefully; without transitions, it'll see early == late + and take the easy path; with transitions, tran and nextTran get the + correct UTC time as atMSecsSinceEpoch so comparing to nextStart selects + the right one. In all other cases, the transition changes offset and the + reasoning that applies to DST applies just the same. Aside from hinting, + the only thing that looks at DST-ness at all, other than inferred from + offset changes, is the case without transition data handling an invalid + time in the gap that a transition passed over. + + The handling of hint (see below) is apt to go wrong in non-DST + transitions. There isn't really a great deal we can hope to do about that + without adding yet more unreliable complexity to the heuristics in use for + already obscure corner-cases. + */ + + /* + The hint (really a QDateTimePrivate::DaylightStatus) is > 0 if caller + thinks we're in DST, 0 if in standard. A value of -2 means never-DST, so + should have been handled above; if it slips through, it's wrong but we + should probably treat it as standard anyway (never-DST means + always-standard, after all). If the hint turns out to be wrong, fall back + on trying the other possibility: which makes it harmless to treat -1 + (meaning unknown) as standard (i.e. try standard first, then try DST). In + practice, away from a transition, the only difference hint makes is to + which candidate we try first: if the hint is wrong (or unknown and + standard fails), we'll try the other candidate and it'll work. + + For the obscure (and invalid) case where forLocalMSecs falls in a + spring-forward's missing hour, a common case is that we started with a + date/time for which the hint was valid and adjusted it naively; for that + case, we should correct the adjustment by shunting across the transition + into where hint is wrong. So half-way through the gap, arrived at from + the DST side, should be read as an hour earlier, in standard time; but, if + arrived at from the standard side, should be read as an hour later, in + DST. (This shall be wrong in some cases; for example, when a country + changes its transition dates and changing a date/time by more than six + months lands it on a transition. However, these cases are even more + obscure than those where the heuristic is good.) + */ + + if (hasTransitions()) { + /* + We have transitions. + + Each transition gives the offsets to use until the next; so we need the + most recent transition before the time forLocalMSecs describes. If it + describes a time *in* a transition, we'll need both that transition and + the one before it. So find one transition that's probably after (and not + much before, otherwise) and another that's definitely before, then work + out which one to use. When both or neither work on forLocalMSecs, use + hint to disambiguate. + */ + + // Get a transition definitely before the local MSecs; usually all we need. + // Only around the transition times might we need another. + Data tran = previousTransition(forLocalMSecs - sixteenHoursInMSecs); + Q_ASSERT(forLocalMSecs < 0 || // Pre-epoch TZ info may be unavailable + forLocalMSecs >= tran.atMSecsSinceEpoch + tran.offsetFromUtc * 1000); + Data nextTran = nextTransition(tran.atMSecsSinceEpoch); + /* + Now walk those forward until they bracket forLocalMSecs with transitions. + + One of the transitions should then be telling us the right offset to use. + In a transition, we need the transition before it (to describe the run-up + to the transition) and the transition itself; so we need to stop when + nextTran is that transition. + */ while (nextTran.atMSecsSinceEpoch != invalidMSecs() - && forLocalMSecs >= nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)) { + && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) { + Data newTran = nextTransition(nextTran.atMSecsSinceEpoch); + if (newTran.atMSecsSinceEpoch == invalidMSecs() + || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 + > forLocalMSecs + sixteenHoursInMSecs) { + // Definitely not a relevant tansition: too far in the future. + break; + } tran = nextTran; - nextTran = nextTransition(tran.atMSecsSinceEpoch); + nextTran = newTran; } + + // Check we do *really* have transitions for this zone: + if (tran.atMSecsSinceEpoch != invalidMSecs()) { + + /* + So now tran is definitely before and nextTran is either after or only + slightly before. The one with the larger offset is in DST; the other in + standard time. Our hint tells us which of those to use (defaulting to + standard if no hint): try it first; if that fails, try the other; if both + fail life's tricky. + */ + Q_ASSERT(forLocalMSecs < 0 + || forLocalMSecs > tran.atMSecsSinceEpoch + tran.offsetFromUtc * 1000); + const qint64 nextStart = nextTran.atMSecsSinceEpoch; + // Work out the UTC values it might make sense to return: + nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000; + tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000; + + const bool nextIsDst = tran.offsetFromUtc < nextTran.offsetFromUtc; + // If that agrees with hint > 0, our first guess is to use nextTran; else tran. + const bool nextFirst = nextIsDst == (hint > 0) && nextStart != invalidMSecs(); + for (int i = 0; i < 2; i++) { + /* + On the first pass, the case we consider is what hint told us to expect + (except when hint was -1 and didn't actually tell us what to expect), + so it's likely right. We only get a second pass if the first failed, + by which time the second case, that we're trying, is likely right. If + an overwhelming majority of calls have hint == -1, the Q_LIKELY here + shall be wrong half the time; otherwise, its errors shall be rarer + than that. + */ + if (nextFirst ? i == 0 : i) { + Q_ASSERT(nextStart != invalidMSecs()); + if (Q_LIKELY(nextStart <= nextTran.atMSecsSinceEpoch)) + return nextTran; + } else { + // If next is invalid, nextFirst is false, to route us here first: + if (nextStart == invalidMSecs() || Q_LIKELY(nextStart > tran.atMSecsSinceEpoch)) + return tran; + } + } + + /* + Neither is valid (e.g. in a spring-forward's gap) and + nextTran.atMSecsSinceEpoch < nextStart <= tran.atMSecsSinceEpoch, so + 0 < tran.atMSecsSinceEpoch - nextTran.atMSecsSinceEpoch + = (nextTran.offsetFromUtc - tran.offsetFromUtc) * 1000 + */ + int dstStep = nextTran.offsetFromUtc - tran.offsetFromUtc; + Q_ASSERT(dstStep > 0); // How else could we get here ? + if (nextFirst) { // hint thought we needed nextTran, so use tran + tran.atMSecsSinceEpoch -= dstStep; + return tran; + } + nextTran.atMSecsSinceEpoch += dstStep; + return nextTran; + } + // System has transitions but not for this zone. + // Try falling back to offsetFromUtc } - if (tran.daylightTimeOffset == 0) { - // If tran is in StandardTime, then need to check if falls close to either DST transition. - // If it does, then it may need adjusting for missing hour or for second occurrence - qint64 diffPrevTran = forLocalMSecs - - (tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)); - qint64 diffNextTran = nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000) - - forLocalMSecs; - if (diffPrevTran >= 0 && diffPrevTran < MSECS_TRAN_WINDOW) { - // If tran picked is for standard time check if changed from DST in last 6 hours, - // as the local msecs may be ambiguous and represent two valid utc msecs. - // If in last 6 hours then get prev tran and if diff falls within the DST offset - // then use the prev tran as we default to the FirstOccurrence - // TODO Check if faster to just always get prev tran, or if faster using 6 hour check. - Data dstTran = previousTransition(tran.atMSecsSinceEpoch); - if (dstTran.atMSecsSinceEpoch != invalidMSecs() - && dstTran.daylightTimeOffset > 0 && diffPrevTran < (dstTran.daylightTimeOffset * 1000)) - tran = dstTran; - } else if (diffNextTran >= 0 && diffNextTran <= (nextTran.daylightTimeOffset * 1000)) { - // If time falls within last hour of standard time then is actually the missing hour - // So return the next tran instead and adjust the local time to be valid - tran = nextTran; - forLocalMSecs = forLocalMSecs + (nextTran.daylightTimeOffset * 1000); + /* Bracket and refine to discover offset. */ + qint64 utcEpochMSecs; + + int early = offsetFromUtc(forLocalMSecs - sixteenHoursInMSecs); + int late = offsetFromUtc(forLocalMSecs + sixteenHoursInMSecs); + if (Q_LIKELY(early == late)) { // > 99% of the time + utcEpochMSecs = forLocalMSecs - early * 1000; + } else { + // Close to a DST transition: early > late is near a fall-back, + // early < late is near a spring-forward. + const int offsetInDst = qMax(early, late); + const int offsetInStd = qMin(early, late); + // Candidate values for utcEpochMSecs (if forLocalMSecs is valid): + const qint64 forDst = forLocalMSecs - offsetInDst * 1000; + const qint64 forStd = forLocalMSecs - offsetInStd * 1000; + // Best guess at the answer: + const qint64 hinted = hint > 0 ? forDst : forStd; + if (Q_LIKELY(offsetFromUtc(hinted) == (hint > 0 ? offsetInDst : offsetInStd))) { + utcEpochMSecs = hinted; + } else if (hint <= 0 && offsetFromUtc(forDst) == offsetInDst) { + utcEpochMSecs = forDst; + } else if (hint > 0 && offsetFromUtc(forStd) == offsetInStd) { + utcEpochMSecs = forStd; + } else { + // Invalid forLocalMSecs: in spring-forward gap. + const int dstStep = daylightTimeOffset(early < late ? + forLocalMSecs + sixteenHoursInMSecs : + forLocalMSecs - sixteenHoursInMSecs); + Q_ASSERT(dstStep); // There can't be a transition without it ! + utcEpochMSecs = (hint > 0) ? forStd - dstStep : forDst + dstStep; } } - // tran should now hold the right transition offset to use - tran.atMSecsSinceEpoch = forLocalMSecs - (tran.offsetFromUtc * 1000); - return tran; + return data(utcEpochMSecs); } bool QTimeZonePrivate::hasTransitions() const diff --git a/src/corelib/tools/qtimezoneprivate_android.cpp b/src/corelib/tools/qtimezoneprivate_android.cpp index 53bf90f01d..e079fa0d63 100644 --- a/src/corelib/tools/qtimezoneprivate_android.cpp +++ b/src/corelib/tools/qtimezoneprivate_android.cpp @@ -207,58 +207,6 @@ QTimeZonePrivate::Data QAndroidTimeZonePrivate::previousTransition(qint64 before return invalidData(); } -// Since Android does not provide an API to access transitions, -// dataForLocalTime needs to be reimplemented without direct use of transitions -QTimeZonePrivate::Data QAndroidTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) const -{ - if (!androidTimeZone.isValid()) { - return invalidData(); - } else { - qint64 UTCepochMSecs; - - // compare the UTC time with standard offset against normal DST offset of one hour - qint64 standardUTCMSecs(forLocalMSecs - (standardTimeOffset(forLocalMSecs) * 1000)); - qint64 daylightUTCMsecs; - - // Check if daylight-saving time applies, - // checking also for DST boundaries - if (isDaylightTime(standardUTCMSecs)) { - // If DST does apply, then standardUTCMSecs will be an hour or so ahead of the real epoch time - // so check that time - daylightUTCMsecs = standardUTCMSecs - daylightTimeOffset(standardUTCMSecs)*1000; - if (isDaylightTime(daylightUTCMsecs)) { - // DST confirmed - UTCepochMSecs = daylightUTCMsecs; - } else { - // DST has just finished - UTCepochMSecs = standardUTCMSecs; - } - } else { - // Standard time indicated, but check for a false negative. - // Would a standard one-hour DST offset indicate DST? - daylightUTCMsecs = standardUTCMSecs - 3600000; // 3600000 MSECS_PER_HOUR - if (isDaylightTime(daylightUTCMsecs)) { - // DST may have just started, - // but double check against timezone's own DST offset - // (don't necessarily assume a one-hour offset) - daylightUTCMsecs = standardUTCMSecs - daylightTimeOffset(daylightUTCMsecs)*1000; - if (isDaylightTime(daylightUTCMsecs)) { - // DST confirmed - UTCepochMSecs = daylightUTCMsecs; - } else { - // false positive, apply standard time after all - UTCepochMSecs = standardUTCMSecs; - } - } else { - // confirmed standard time - UTCepochMSecs = standardUTCMSecs; - } - } - - return data(UTCepochMSecs); - } -} - QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const { QJNIObjectPrivate androidSystemTimeZone = QJNIObjectPrivate::callStaticObjectMethod("java.util.TimeZone", "getDefault", "()Ljava/util/TimeZone;"); diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index d06784b0f9..c397c18aac 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -123,7 +123,7 @@ public: virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const; virtual Data data(qint64 forMSecsSinceEpoch) const; - virtual Data dataForLocalTime(qint64 forLocalMSecs) const; + Data dataForLocalTime(qint64 forLocalMSecs, int hint) const; virtual bool hasTransitions() const; virtual Data nextTransition(qint64 afterMSecsSinceEpoch) const; @@ -475,8 +475,6 @@ public: Data nextTransition(qint64 afterMSecsSinceEpoch) const Q_DECL_OVERRIDE; Data previousTransition(qint64 beforeMSecsSinceEpoch) const Q_DECL_OVERRIDE; - Data dataForLocalTime(qint64 forLocalMSecs) const Q_DECL_OVERRIDE; - QByteArray systemTimeZoneId() const Q_DECL_OVERRIDE; QList<QByteArray> availableTimeZoneIds() const Q_DECL_OVERRIDE; diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 10b61c3a40..38dff88919 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -199,7 +199,7 @@ static QVector<QTzTransition> parseTzTransitions(QDataStream &ds, int tzh_timecn } } else { // Parse tzh_timecnt x 4-byte transition times - int val; + qint32 val; for (int i = 0; i < tzh_timecnt && ds.status() == QDataStream::Ok; ++i) { ds >> val; transitions[i].tz_time = val; @@ -725,19 +725,61 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) } } - // Now for each transition time calculate our rule and save them - m_tranTimes.reserve(tranList.count()); - for (const QTzTransition &tz_tran : qAsConst(tranList)) { + // Now for each transition time calculate and store our rule: + const int tranCount = tranList.count();; + m_tranTimes.reserve(tranCount); + // The DST offset when in effect: usually stable, usually an hour: + int lastDstOff = 3600; + for (int i = 0; i < tranCount; i++) { + const QTzTransition &tz_tran = tranList.at(i); QTzTransitionTime tran; QTzTransitionRule rule; const QTzType tz_type = typeList.at(tz_tran.tz_typeind); // Calculate the associated Rule - if (!tz_type.tz_isdst) + if (!tz_type.tz_isdst) { utcOffset = tz_type.tz_gmtoff; + } else if (Q_UNLIKELY(tz_type.tz_gmtoff != utcOffset + lastDstOff)) { + /* + This might be a genuine change in DST offset, but could also be + DST starting at the same time as the standard offset changed. See + if DST's end gives a more plausible utcOffset (i.e. one closer to + the last we saw, or a simple whole hour): + */ + // Standard offset inferred from net offset and expected DST offset: + const int inferStd = tz_type.tz_gmtoff - lastDstOff; // != utcOffset + for (int j = i + 1; j < tranCount; j++) { + const QTzType new_type = typeList.at(tranList.at(j).tz_typeind); + if (!new_type.tz_isdst) { + const int newUtc = new_type.tz_gmtoff; + if (newUtc == utcOffset) { + // DST-end can't help us, avoid lots of messy checks. + // else: See if the end matches the familiar DST offset: + } else if (newUtc == inferStd) { + utcOffset = newUtc; + // else: let either end shift us to one hour as DST offset: + } else if (tz_type.tz_gmtoff - 3600 == utcOffset) { + // Start does it + } else if (tz_type.tz_gmtoff - 3600 == newUtc) { + utcOffset = newUtc; // End does it + // else: prefer whichever end gives DST offset closer to + // last, but consider any offset > 0 "closer" than any <= 0: + } else if (newUtc < tz_type.tz_gmtoff + ? (utcOffset >= tz_type.tz_gmtoff + || qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd)) + : (utcOffset >= tz_type.tz_gmtoff + && qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd))) { + utcOffset = newUtc; + } + break; + } + } + lastDstOff = tz_type.tz_gmtoff - utcOffset; + } rule.stdOffset = utcOffset; rule.dstOffset = tz_type.tz_gmtoff - utcOffset; rule.abbreviationIndex = tz_type.tz_abbrind; + // If the rule already exist then use that, otherwise add it int ruleIndex = m_tranRules.indexOf(rule); if (ruleIndex == -1) { diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index fa8e07abbc..ab5997635a 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -173,7 +173,7 @@ qtConfig(timezone) { } qtConfig(regularexpression) { - QMAKE_USE_PRIVATE += pcre + QMAKE_USE_PRIVATE += pcre2 HEADERS += tools/qregularexpression.h SOURCES += tools/qregularexpression.cpp |