diff options
Diffstat (limited to 'src/corelib')
217 files changed, 12950 insertions, 4788 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index a9bb129..f20bbf4 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -166,11 +166,12 @@ Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) #endif QUnifiedTimer::QUnifiedTimer() : - QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), - insideTick(false), currentAnimationIdx(0), consistentTiming(false), slowMode(false), + QObject(), defaultDriver(this), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), + currentAnimationIdx(0), consistentTiming(false), slowMode(false), slowdownFactor(5.0f), isPauseTimerActive(false), runningLeafAnimations(0) { time.invalidate(); + driver = &defaultDriver; } @@ -253,14 +254,27 @@ void QUnifiedTimer::restartAnimationTimer() qDebug() << runningPauseAnimations; qDebug() << closestPauseAnimationTimeToFinish(); } + driver->stop(); animationTimer.start(closestTimeToFinish, this); isPauseTimerActive = true; - } else if (!animationTimer.isActive() || isPauseTimerActive) { - animationTimer.start(timingInterval, this); + } else if (!driver->isRunning() || isPauseTimerActive) { + driver->start(); isPauseTimerActive = false; } } +void QUnifiedTimer::setTimingInterval(int interval) +{ + timingInterval = interval; + + if (driver->isRunning() && !isPauseTimerActive) { + //we changed the timing interval + driver->stop(); + driver->start(); + } +} + + void QUnifiedTimer::timerEvent(QTimerEvent *event) { //in the case of consistent timing we make sure the orders in which events come is always the same @@ -375,6 +389,140 @@ int QUnifiedTimer::closestPauseAnimationTimeToFinish() return closestTimeToFinish; } +void QUnifiedTimer::installAnimationDriver(QAnimationDriver *d) +{ + if (driver->isRunning()) { + qWarning("QUnifiedTimer: Cannot change animation driver while animations are running"); + return; + } + + if (driver && driver != &defaultDriver) + delete driver; + + driver = d; +} + +/*! + \class QAnimationDriver + + \brief The QAnimationDriver class is used to exchange the mechanism that drives animations. + + The default animation system is driven by a timer that fires at regular intervals. + In some scenarios, it is better to drive the animation based on other synchronization + mechanisms, such as the vertical refresh rate of the screen. + + \internal + */ + +QAnimationDriver::QAnimationDriver(QObject *parent) + : QObject(*(new QAnimationDriverPrivate), parent) +{ +} + +QAnimationDriver::QAnimationDriver(QAnimationDriverPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + + +/*! + Advances the animation based on the current time. This function should + be continuously called by the driver while the animation is running. + + \internal + */ +void QAnimationDriver::advance() +{ + QUnifiedTimer *instance = QUnifiedTimer::instance(); + + // update current time on all top level animations + instance->updateAnimationsTime(); + instance->restartAnimationTimer(); +} + + +/*! + Installs this animation driver. The animation driver is thread local and + will only apply for the thread its installed in. + + \internal + */ +void QAnimationDriver::install() +{ + QUnifiedTimer *timer = QUnifiedTimer::instance(true); + timer->installAnimationDriver(this); +} + +bool QAnimationDriver::isRunning() const +{ + return d_func()->running; +} + + +void QAnimationDriver::start() +{ + Q_D(QAnimationDriver); + if (!d->running) { + started(); + d->running = true; + } +} + + +void QAnimationDriver::stop() +{ + Q_D(QAnimationDriver); + if (d->running) { + stopped(); + d->running = false; + } +} + +/*! + \fn QAnimationDriver::started() + + This function is called by the animation framework to notify the driver + that it should start running. + + \internal + */ + +/*! + \fn QAnimationDriver::stopped() + + This function is called by the animation framework to notify the driver + that it should stop running. + + \internal + */ + +/*! + The default animation driver just spins the timer... + */ +QDefaultAnimationDriver::QDefaultAnimationDriver(QUnifiedTimer *timer) + : QAnimationDriver(0), m_unified_timer(timer) +{ +} + +void QDefaultAnimationDriver::timerEvent(QTimerEvent *e) +{ + Q_ASSERT(e->timerId() == m_timer.timerId()); + Q_UNUSED(e); // if the assertions are disabled + advance(); +} + +void QDefaultAnimationDriver::started() +{ + m_timer.start(m_unified_timer->timingInterval, this); +} + +void QDefaultAnimationDriver::stopped() +{ + m_timer.stop(); +} + + + void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) { Q_Q(QAbstractAnimation); @@ -800,6 +948,9 @@ void QAbstractAnimation::stop() { Q_D(QAbstractAnimation); + if (d->state == Stopped) + return; + d->setState(Stopped); } diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h index 447b89d..0900870 100644 --- a/src/corelib/animation/qabstractanimation.h +++ b/src/corelib/animation/qabstractanimation.h @@ -54,6 +54,7 @@ QT_MODULE(Core) class QAnimationGroup; class QSequentialAnimationGroup; +class QAnimationDriver; class QAbstractAnimationPrivate; class Q_CORE_EXPORT QAbstractAnimation : public QObject @@ -132,6 +133,36 @@ private: Q_DECLARE_PRIVATE(QAbstractAnimation) }; +class QAnimationDriverPrivate; +class Q_CORE_EXPORT QAnimationDriver : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QAnimationDriver) + +public: + QAnimationDriver(QObject *parent = 0); + + void advance(); + void install(); + + bool isRunning() const; + +protected: + virtual void started() {}; + virtual void stopped() {}; + + QAnimationDriver(QAnimationDriverPrivate &dd, QObject *parent = 0); + +private: + friend class QUnifiedTimer; + + void start(); + void stop(); +}; + + + + #endif //QT_NO_ANIMATION QT_END_NAMESPACE diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index aeee1f2..ba92960 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -58,6 +58,7 @@ #include <QtCore/qtimer.h> #include <QtCore/qelapsedtimer.h> #include <private/qobject_p.h> +#include <qabstractanimation.h> #ifdef Q_OS_WIN #include <qt_windows.h> @@ -114,30 +115,47 @@ private: Q_DECLARE_PUBLIC(QAbstractAnimation) }; + +class QUnifiedTimer; +class QDefaultAnimationDriver : public QAnimationDriver +{ + Q_OBJECT +public: + QDefaultAnimationDriver(QUnifiedTimer *timer); + void timerEvent(QTimerEvent *e); + + void started(); + void stopped(); + +private: + QBasicTimer m_timer; + QUnifiedTimer *m_unified_timer; +}; + +class Q_CORE_EXPORT QAnimationDriverPrivate : public QObjectPrivate +{ +public: + QAnimationDriverPrivate() : running(false) {} + bool running; +}; + typedef QElapsedTimer ElapsedTimer; -class QUnifiedTimer : public QObject +class Q_CORE_EXPORT QUnifiedTimer : public QObject { private: QUnifiedTimer(); public: //XXX this is needed by dui - static Q_CORE_EXPORT QUnifiedTimer *instance(); + static QUnifiedTimer *instance(); static QUnifiedTimer *instance(bool create); static void registerAnimation(QAbstractAnimation *animation, bool isTopLevel); static void unregisterAnimation(QAbstractAnimation *animation); //defines the timing interval. Default is DEFAULT_TIMER_INTERVAL - void setTimingInterval(int interval) - { - timingInterval = interval; - if (animationTimer.isActive() && !isPauseTimerActive) { - //we changed the timing interval - animationTimer.start(timingInterval, this); - } - } + void setTimingInterval(int interval); /* this allows to have a consistent timer interval at each tick from the timer @@ -161,11 +179,20 @@ public: */ static void updateAnimationTimer(); + void installAnimationDriver(QAnimationDriver *driver); + + void restartAnimationTimer(); + void updateAnimationsTime(); + protected: void timerEvent(QTimerEvent *); private: - // timer used for all active (running) animations + friend class QDefaultAnimationDriver; + + QAnimationDriver *driver; + QDefaultAnimationDriver defaultDriver; + QBasicTimer animationTimer; // timer used to delay the check if we should start/stop the animation timer QBasicTimer startStopAnimationTimer; @@ -196,9 +223,6 @@ private: void registerRunningAnimation(QAbstractAnimation *animation); void unregisterRunningAnimation(QAbstractAnimation *animation); - void restartAnimationTimer(); - - void updateAnimationsTime(); int closestPauseAnimationTimeToFinish(); }; diff --git a/src/corelib/arch/arch.pri b/src/corelib/arch/arch.pri index 57bc80a..cd23e5e 100644 --- a/src/corelib/arch/arch.pri +++ b/src/corelib/arch/arch.pri @@ -12,6 +12,8 @@ symbian:HEADERS += arch/qatomic_symbian.h \ vxworks:HEADERS += arch/qatomic_vxworks.h +integrity:HEADERS += arch/qatomic_integrity.h + !wince*:!win32:!mac:!symbian:HEADERS += arch/qatomic_alpha.h \ arch/qatomic_avr32.h \ arch/qatomic_ia64.h \ @@ -21,7 +23,9 @@ vxworks:HEADERS += arch/qatomic_vxworks.h arch/qatomic_generic.h \ arch/qatomic_powerpc.h \ arch/qatomic_arm.h \ + arch/qatomic_armv5.h \ arch/qatomic_armv6.h \ + arch/qatomic_armv7.h \ arch/qatomic_i386.h \ arch/qatomic_mips.h \ arch/qatomic_s390.h \ diff --git a/src/corelib/arch/armv6/arch.pri b/src/corelib/arch/armv6/arch.pri deleted file mode 100644 index fd0cce1..0000000 --- a/src/corelib/arch/armv6/arch.pri +++ /dev/null @@ -1,3 +0,0 @@ -# -# ARMv6 -# diff --git a/src/corelib/arch/integrity/arch.pri b/src/corelib/arch/integrity/arch.pri new file mode 100644 index 0000000..2c4196e --- /dev/null +++ b/src/corelib/arch/integrity/arch.pri @@ -0,0 +1,3 @@ +# +# INTEGRITY RTOS architecture +# diff --git a/src/corelib/arch/qatomic_arch.h b/src/corelib/arch/qatomic_arch.h index baf899f..3da833a 100644 --- a/src/corelib/arch/qatomic_arch.h +++ b/src/corelib/arch/qatomic_arch.h @@ -46,7 +46,9 @@ QT_BEGIN_HEADER #include "QtCore/qglobal.h" -#if defined(QT_ARCH_VXWORKS) +#if defined(QT_ARCH_INTEGRITY) +# include "QtCore/qatomic_integrity.h" +#elif defined(QT_ARCH_VXWORKS) # include "QtCore/qatomic_vxworks.h" #elif defined(QT_ARCH_ALPHA) # include "QtCore/qatomic_alpha.h" @@ -88,6 +90,8 @@ QT_BEGIN_HEADER # include "QtCore/qatomic_sh.h" #elif defined(QT_ARCH_SH4A) # include "QtCore/qatomic_sh4a.h" +#elif defined(QT_ARCH_NACL) +# include "QtCore/qatomic_generic.h" #else # error "Qt has not been ported to this architecture" #endif diff --git a/src/corelib/arch/qatomic_arm.h b/src/corelib/arch/qatomic_arm.h index 7b15e0a..07d2143 100644 --- a/src/corelib/arch/qatomic_arm.h +++ b/src/corelib/arch/qatomic_arm.h @@ -44,387 +44,32 @@ QT_BEGIN_HEADER -QT_BEGIN_NAMESPACE - -#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE - -inline bool QBasicAtomicInt::isReferenceCountingNative() -{ return false; } -inline bool QBasicAtomicInt::isReferenceCountingWaitFree() -{ return false; } - -#define Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE - -inline bool QBasicAtomicInt::isTestAndSetNative() -{ return false; } -inline bool QBasicAtomicInt::isTestAndSetWaitFree() -{ return false; } - -#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE -#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE - -inline bool QBasicAtomicInt::isFetchAndStoreNative() -{ return true; } -inline bool QBasicAtomicInt::isFetchAndStoreWaitFree() -{ return true; } - -#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE - -inline bool QBasicAtomicInt::isFetchAndAddNative() -{ return false; } -inline bool QBasicAtomicInt::isFetchAndAddWaitFree() -{ return false; } - -#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetNative() -{ return false; } -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetWaitFree() -{ return false; } - -#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE -#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreNative() -{ return true; } -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreWaitFree() -{ return true; } - -#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddNative() -{ return false; } -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree() -{ return false; } - -#ifndef QT_NO_ARM_EABI - -// kernel places a restartable cmpxchg implementation at a fixed address -extern "C" typedef int (qt_atomic_eabi_cmpxchg_int_t)(int oldval, int newval, volatile int *ptr); -extern "C" typedef int (qt_atomic_eabi_cmpxchg_ptr_t)(const void *oldval, const void *newval, volatile void *ptr); -#define qt_atomic_eabi_cmpxchg_int (*reinterpret_cast<qt_atomic_eabi_cmpxchg_int_t *>(0xffff0fc0)) -#define qt_atomic_eabi_cmpxchg_ptr (*reinterpret_cast<qt_atomic_eabi_cmpxchg_ptr_t *>(0xffff0fc0)) - -#else - -extern Q_CORE_EXPORT char q_atomic_lock; -Q_CORE_EXPORT void qt_atomic_yield(int *); - -#ifdef Q_CC_RVCT - -Q_CORE_EXPORT __asm char q_atomic_swp(volatile char *ptr, char newval); - -#else - -inline char q_atomic_swp(volatile char *ptr, char newval) -{ - register char ret; - asm volatile("swpb %0,%2,[%3]" - : "=&r"(ret), "=m" (*ptr) - : "r"(newval), "r"(ptr) - : "cc", "memory"); - return ret; -} - -#endif // Q_CC_RVCT - -#endif // QT_NO_ARM_EABI - -// Reference counting - -inline bool QBasicAtomicInt::ref() -{ -#ifndef QT_NO_ARM_EABI - register int originalValue; - register int newValue; - do { - originalValue = _q_value; - newValue = originalValue + 1; - } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); - return newValue != 0; -#else - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - int originalValue = _q_value++; - q_atomic_swp(&q_atomic_lock, 0); - return originalValue != -1; -#endif -} - -inline bool QBasicAtomicInt::deref() -{ -#ifndef QT_NO_ARM_EABI - register int originalValue; - register int newValue; - do { - originalValue = _q_value; - newValue = originalValue - 1; - } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); - return newValue != 0; -#else - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - int originalValue = _q_value--; - q_atomic_swp(&q_atomic_lock, 0); - return originalValue != 1; -#endif -} - -// Test and set for integers - -inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) -{ -#ifndef QT_NO_ARM_EABI - register int originalValue; - do { - originalValue = _q_value; - if (originalValue != expectedValue) - return false; - } while (qt_atomic_eabi_cmpxchg_int(expectedValue, newValue, &_q_value) != 0); - return true; -#else - bool returnValue = false; - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - if (_q_value == expectedValue) { - _q_value = newValue; - returnValue = true; - } - q_atomic_swp(&q_atomic_lock, 0); - return returnValue; -#endif -} - -inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -inline bool QBasicAtomicInt::testAndSetAcquire(int expectedValue, int newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -// Fetch and store for integers - -#ifndef Q_CC_RVCT - -inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) -{ - int originalValue; - asm volatile("swp %0,%2,[%3]" - : "=&r"(originalValue), "=m" (_q_value) - : "r"(newValue), "r"(&_q_value) - : "cc", "memory"); - return originalValue; -} - -#endif - -inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -inline int QBasicAtomicInt::fetchAndStoreAcquire(int newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -inline int QBasicAtomicInt::fetchAndStoreRelease(int newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -// Fetch and add for integers - -inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) -{ -#ifndef QT_NO_ARM_EABI - register int originalValue; - register int newValue; - do { - originalValue = _q_value; - newValue = originalValue + valueToAdd; - } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); - return originalValue; -#else - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - int originalValue = _q_value; - _q_value += valueToAdd; - q_atomic_swp(&q_atomic_lock, 0); - return originalValue; -#endif -} - -inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -inline int QBasicAtomicInt::fetchAndAddAcquire(int valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -inline int QBasicAtomicInt::fetchAndAddRelease(int valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -// Test and set for pointers - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) -{ -#ifndef QT_NO_ARM_EABI - register T *originalValue; - do { - originalValue = _q_value; - if (originalValue != expectedValue) - return false; - } while (qt_atomic_eabi_cmpxchg_ptr(expectedValue, newValue, &_q_value) != 0); - return true; +#if defined(__ARM_ARCH_7__) \ + || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) +# define QT_ARCH_ARMV7 +QT_BEGIN_INCLUDE_HEADER +# include "QtCore/qatomic_armv7.h" +QT_END_INCLUDE_HEADER +#elif defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6T2__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) \ + || (defined(__TARGET_ARCH_ARM) && (__TARGET_ARCH_ARM-0 >= 6)) +# define QT_ARCH_ARMV6 +QT_BEGIN_INCLUDE_HEADER +# include "QtCore/qatomic_armv6.h" +QT_END_INCLUDE_HEADER #else - bool returnValue = false; - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - if (_q_value == expectedValue) { - _q_value = newValue; - returnValue = true; - } - q_atomic_swp(&q_atomic_lock, 0); - return returnValue; +# define QT_ARCH_ARMV5 +QT_BEGIN_INCLUDE_HEADER +# include "QtCore/qatomic_armv5.h" +QT_END_INCLUDE_HEADER #endif -} - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetAcquire(T *expectedValue, T *newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValue, T *newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} - -// Fetch and store for pointers - -#ifdef Q_CC_RVCT - -template <typename T> -__asm T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) -{ - add r2, pc, #0 - bx r2 - arm - swp r2,r1,[r0] - mov r0, r2 - bx lr - thumb -} - -#else - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) -{ - T *originalValue; - asm volatile("swp %0,%2,[%3]" - : "=&r"(originalValue), "=m" (_q_value) - : "r"(newValue), "r"(&_q_value) - : "cc", "memory"); - return originalValue; -} - -#endif // Q_CC_RVCT - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreAcquire(T *newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelease(T *newValue) -{ - return fetchAndStoreOrdered(newValue); -} - -// Fetch and add for pointers - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) -{ -#ifndef QT_NO_ARM_EABI - register T *originalValue; - register T *newValue; - do { - originalValue = _q_value; - newValue = originalValue + valueToAdd; - } while (qt_atomic_eabi_cmpxchg_ptr(originalValue, newValue, &_q_value) != 0); - return originalValue; -#else - int count = 0; - while (q_atomic_swp(&q_atomic_lock, ~0) != 0) - qt_atomic_yield(&count); - T *originalValue = (_q_value); - _q_value += valueToAdd; - q_atomic_swp(&q_atomic_lock, 0); - return originalValue; -#endif -} - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddAcquire(qptrdiff valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelease(qptrdiff valueToAdd) -{ - return fetchAndAddOrdered(valueToAdd); -} - -QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/arch/qatomic_armv5.h b/src/corelib/arch/qatomic_armv5.h new file mode 100644 index 0000000..ac8fe96 --- /dev/null +++ b/src/corelib/arch/qatomic_armv5.h @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QATOMIC_ARMV5_H +#define QATOMIC_ARMV5_H + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isReferenceCountingNative() +{ return false; } +inline bool QBasicAtomicInt::isReferenceCountingWaitFree() +{ return false; } + +#define Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isTestAndSetNative() +{ return false; } +inline bool QBasicAtomicInt::isTestAndSetWaitFree() +{ return false; } + +#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE +#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE + +inline bool QBasicAtomicInt::isFetchAndStoreNative() +{ return true; } +inline bool QBasicAtomicInt::isFetchAndStoreWaitFree() +{ return true; } + +#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isFetchAndAddNative() +{ return false; } +inline bool QBasicAtomicInt::isFetchAndAddWaitFree() +{ return false; } + +#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetNative() +{ return false; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetWaitFree() +{ return false; } + +#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE +#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreNative() +{ return true; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreWaitFree() +{ return true; } + +#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddNative() +{ return false; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree() +{ return false; } + +#ifndef QT_NO_ARM_EABI + +// kernel places a restartable cmpxchg implementation at a fixed address +extern "C" typedef int (qt_atomic_eabi_cmpxchg_int_t)(int oldval, int newval, volatile int *ptr); +extern "C" typedef int (qt_atomic_eabi_cmpxchg_ptr_t)(const void *oldval, const void *newval, volatile void *ptr); +#define qt_atomic_eabi_cmpxchg_int (*reinterpret_cast<qt_atomic_eabi_cmpxchg_int_t *>(0xffff0fc0)) +#define qt_atomic_eabi_cmpxchg_ptr (*reinterpret_cast<qt_atomic_eabi_cmpxchg_ptr_t *>(0xffff0fc0)) + +#else + +extern Q_CORE_EXPORT char q_atomic_lock; +Q_CORE_EXPORT void qt_atomic_yield(int *); + +#ifdef Q_CC_RVCT + +Q_CORE_EXPORT __asm char q_atomic_swp(volatile char *ptr, char newval); + +#else + +inline char q_atomic_swp(volatile char *ptr, char newval) +{ + register char ret; + asm volatile("swpb %0,%2,[%3]" + : "=&r"(ret), "=m" (*ptr) + : "r"(newval), "r"(ptr) + : "cc", "memory"); + return ret; +} + +#endif // Q_CC_RVCT + +#endif // QT_NO_ARM_EABI + +// Reference counting + +inline bool QBasicAtomicInt::ref() +{ +#ifndef QT_NO_ARM_EABI + register int originalValue; + register int newValue; + do { + originalValue = _q_value; + newValue = originalValue + 1; + } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); + return newValue != 0; +#else + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + int originalValue = _q_value++; + q_atomic_swp(&q_atomic_lock, 0); + return originalValue != -1; +#endif +} + +inline bool QBasicAtomicInt::deref() +{ +#ifndef QT_NO_ARM_EABI + register int originalValue; + register int newValue; + do { + originalValue = _q_value; + newValue = originalValue - 1; + } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); + return newValue != 0; +#else + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + int originalValue = _q_value--; + q_atomic_swp(&q_atomic_lock, 0); + return originalValue != 1; +#endif +} + +// Test and set for integers + +inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) +{ +#ifndef QT_NO_ARM_EABI + register int originalValue; + do { + originalValue = _q_value; + if (originalValue != expectedValue) + return false; + } while (qt_atomic_eabi_cmpxchg_int(expectedValue, newValue, &_q_value) != 0); + return true; +#else + bool returnValue = false; + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + if (_q_value == expectedValue) { + _q_value = newValue; + returnValue = true; + } + q_atomic_swp(&q_atomic_lock, 0); + return returnValue; +#endif +} + +inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +inline bool QBasicAtomicInt::testAndSetAcquire(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +// Fetch and store for integers + +#ifndef Q_CC_RVCT + +inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) +{ + int originalValue; + asm volatile("swp %0,%2,[%3]" + : "=&r"(originalValue), "=m" (_q_value) + : "r"(newValue), "r"(&_q_value) + : "cc", "memory"); + return originalValue; +} + +#endif + +inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +inline int QBasicAtomicInt::fetchAndStoreAcquire(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +inline int QBasicAtomicInt::fetchAndStoreRelease(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +// Fetch and add for integers + +inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) +{ +#ifndef QT_NO_ARM_EABI + register int originalValue; + register int newValue; + do { + originalValue = _q_value; + newValue = originalValue + valueToAdd; + } while (qt_atomic_eabi_cmpxchg_int(originalValue, newValue, &_q_value) != 0); + return originalValue; +#else + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + int originalValue = _q_value; + _q_value += valueToAdd; + q_atomic_swp(&q_atomic_lock, 0); + return originalValue; +#endif +} + +inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +inline int QBasicAtomicInt::fetchAndAddAcquire(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +inline int QBasicAtomicInt::fetchAndAddRelease(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +// Test and set for pointers + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) +{ +#ifndef QT_NO_ARM_EABI + register T *originalValue; + do { + originalValue = _q_value; + if (originalValue != expectedValue) + return false; + } while (qt_atomic_eabi_cmpxchg_ptr(expectedValue, newValue, &_q_value) != 0); + return true; +#else + bool returnValue = false; + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + if (_q_value == expectedValue) { + _q_value = newValue; + returnValue = true; + } + q_atomic_swp(&q_atomic_lock, 0); + return returnValue; +#endif +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetAcquire(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +// Fetch and store for pointers + +#ifdef Q_CC_RVCT + +template <typename T> +__asm T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) +{ + add r2, pc, #0 + bx r2 + arm + swp r2,r1,[r0] + mov r0, r2 + bx lr + thumb +} + +#else + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) +{ + T *originalValue; + asm volatile("swp %0,%2,[%3]" + : "=&r"(originalValue), "=m" (_q_value) + : "r"(newValue), "r"(&_q_value) + : "cc", "memory"); + return originalValue; +} + +#endif // Q_CC_RVCT + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreAcquire(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelease(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +// Fetch and add for pointers + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) +{ +#ifndef QT_NO_ARM_EABI + register T *originalValue; + register T *newValue; + do { + originalValue = _q_value; + newValue = originalValue + valueToAdd; + } while (qt_atomic_eabi_cmpxchg_ptr(originalValue, newValue, &_q_value) != 0); + return originalValue; +#else + int count = 0; + while (q_atomic_swp(&q_atomic_lock, ~0) != 0) + qt_atomic_yield(&count); + T *originalValue = (_q_value); + _q_value += valueToAdd; + q_atomic_swp(&q_atomic_lock, 0); + return originalValue; +#endif +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddAcquire(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelease(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QATOMIC_ARMV5_H diff --git a/src/corelib/arch/qatomic_armv6.h b/src/corelib/arch/qatomic_armv6.h index 4fb9963..3bd6058 100644 --- a/src/corelib/arch/qatomic_armv6.h +++ b/src/corelib/arch/qatomic_armv6.h @@ -45,6 +45,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE + #define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE inline bool QBasicAtomicInt::isReferenceCountingNative() @@ -102,6 +103,13 @@ Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree() #ifndef Q_CC_RVCT +#ifndef Q_DATA_MEMORY_BARRIER +# define Q_DATA_MEMORY_BARRIER asm volatile("":::"memory") +#endif +#ifndef Q_COMPILER_MEMORY_BARRIER +# define Q_COMPILER_MEMORY_BARRIER asm volatile("":::"memory") +#endif + inline bool QBasicAtomicInt::ref() { register int newValue; @@ -138,7 +146,7 @@ inline bool QBasicAtomicInt::deref() return newValue != 0; } -inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) +inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) { register int result; asm volatile("0:\n" @@ -152,11 +160,11 @@ inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) : [expectedValue] "r" (expectedValue), [newValue] "r" (newValue), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return result == 0; } -inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) +inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) { register int originalValue; register int result; @@ -170,11 +178,11 @@ inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) "+m" (_q_value) : [newValue] "r" (newValue), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return originalValue; } -inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) +inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) { register int originalValue; register int newValue; @@ -191,12 +199,12 @@ inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) "+m" (_q_value) : [valueToAdd] "r" (valueToAdd), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return originalValue; } template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) { register T *result; asm volatile("0:\n" @@ -210,12 +218,12 @@ Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValu : [expectedValue] "r" (expectedValue), [newValue] "r" (newValue), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return result == 0; } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) { register T *originalValue; register int result; @@ -229,12 +237,12 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) "+m" (_q_value) : [newValue] "r" (newValue), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return originalValue; } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) { register T *originalValue; register T *newValue; @@ -251,7 +259,7 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo "+m" (_q_value) : [valueToAdd] "r" (valueToAdd * sizeof(T)), [_q_value] "r" (&_q_value) - : "cc", "memory"); + : "cc"); return originalValue; } @@ -263,9 +271,18 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo // RVCT embedded assembly documentation: // http://www.keil.com/support/man/docs/armcc/armcc_chddbeib.htm -// save our pragma state and switch to ARM mode -#pragma push -#pragma arm +#if __TARGET_ARCH_THUMB-0 < 4 +// save our pragma state and switch to ARM mode (unless using Thumb2) +# pragma push +# pragma arm +#endif + +#ifndef Q_DATA_MEMORY_BARRIER +# define Q_DATA_MEMORY_BARRIER __schedule_barrier() +#endif +#ifndef Q_COMPILER_MEMORY_BARRIER +# define Q_COMPILER_MEMORY_BARRIER __schedule_barrier() +#endif inline bool QBasicAtomicInt::ref() { @@ -297,7 +314,7 @@ inline bool QBasicAtomicInt::deref() return newValue != 0; } -inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) +inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) { register int result; retry: @@ -311,7 +328,7 @@ inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) return result == 0; } -inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) +inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) { register int originalValue; register int result; @@ -325,7 +342,7 @@ inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) return originalValue; } -inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) +inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) { register int originalValue; register int newValue; @@ -342,7 +359,7 @@ inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) } template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) { register T *result; retry: @@ -357,7 +374,7 @@ Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValu } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) { register T *originalValue; register int result; @@ -372,7 +389,7 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) { register T *originalValue; register T *newValue; @@ -388,111 +405,153 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo return originalValue; } -// go back to the previous pragma state (probably Thumb mode) -#pragma pop +#if __TARGET_ARCH_THUMB-0 < 4 +# pragma pop #endif -// common code +#endif -inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) -{ - return testAndSetOrdered(expectedValue, newValue); -} +// common code inline bool QBasicAtomicInt::testAndSetAcquire(int expectedValue, int newValue) { - return testAndSetOrdered(expectedValue, newValue); + bool returnValue = testAndSetRelaxed(expectedValue, newValue); + Q_DATA_MEMORY_BARRIER; + return returnValue; } inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue) { - return testAndSetOrdered(expectedValue, newValue); + Q_DATA_MEMORY_BARRIER; + return testAndSetRelaxed(expectedValue, newValue); } -inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) +inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) { - return fetchAndStoreOrdered(newValue); + Q_DATA_MEMORY_BARRIER; + bool returnValue = testAndSetRelaxed(expectedValue, newValue); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; } inline int QBasicAtomicInt::fetchAndStoreAcquire(int newValue) { - return fetchAndStoreOrdered(newValue); + int returnValue = fetchAndStoreRelaxed(newValue); + Q_DATA_MEMORY_BARRIER; + return returnValue; } inline int QBasicAtomicInt::fetchAndStoreRelease(int newValue) { - return fetchAndStoreOrdered(newValue); + Q_DATA_MEMORY_BARRIER; + return fetchAndStoreRelaxed(newValue); } -inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) +inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) { - return fetchAndAddOrdered(valueToAdd); + Q_DATA_MEMORY_BARRIER; + int returnValue = fetchAndStoreRelaxed(newValue); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; } + inline int QBasicAtomicInt::fetchAndAddAcquire(int valueToAdd) { - return fetchAndAddOrdered(valueToAdd); + int returnValue = fetchAndAddRelaxed(valueToAdd); + Q_DATA_MEMORY_BARRIER; + return returnValue; } inline int QBasicAtomicInt::fetchAndAddRelease(int valueToAdd) { - return fetchAndAddOrdered(valueToAdd); + Q_DATA_MEMORY_BARRIER; + return fetchAndAddRelaxed(valueToAdd); } -template <typename T> -Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) +inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) { - return testAndSetOrdered(expectedValue, newValue); + Q_DATA_MEMORY_BARRIER; + int returnValue = fetchAndAddRelaxed(valueToAdd); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetAcquire(T *expectedValue, T *newValue) { - return testAndSetOrdered(expectedValue, newValue); + bool returnValue = testAndSetRelaxed(expectedValue, newValue); + Q_DATA_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValue, T *newValue) { - return testAndSetOrdered(expectedValue, newValue); + Q_DATA_MEMORY_BARRIER; + return testAndSetRelaxed(expectedValue, newValue); } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) { - return fetchAndStoreOrdered(newValue); + Q_DATA_MEMORY_BARRIER; + bool returnValue = testAndSetAcquire(expectedValue, newValue); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreAcquire(T *newValue) { - return fetchAndStoreOrdered(newValue); + T *returnValue = fetchAndStoreRelaxed(newValue); + Q_DATA_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelease(T *newValue) { - return fetchAndStoreOrdered(newValue); + Q_DATA_MEMORY_BARRIER; + return fetchAndStoreRelaxed(newValue); } template <typename T> -Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) { - return fetchAndAddOrdered(valueToAdd); + Q_DATA_MEMORY_BARRIER; + T *returnValue = fetchAndStoreRelaxed(newValue); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddAcquire(qptrdiff valueToAdd) { - return fetchAndAddOrdered(valueToAdd); + T *returnValue = fetchAndAddRelaxed(valueToAdd); + Q_DATA_MEMORY_BARRIER; + return returnValue; } template <typename T> Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelease(qptrdiff valueToAdd) { - return fetchAndAddOrdered(valueToAdd); + Q_DATA_MEMORY_BARRIER; + return fetchAndAddRelaxed(valueToAdd); } +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) +{ + Q_DATA_MEMORY_BARRIER; + T *returnValue = fetchAndAddRelaxed(valueToAdd); + Q_COMPILER_MEMORY_BARRIER; + return returnValue; +} + +#undef Q_DATA_MEMORY_BARRIER +#undef Q_COMPILER_MEMORY_BARRIER + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/arch/qatomic_armv7.h b/src/corelib/arch/qatomic_armv7.h new file mode 100644 index 0000000..b35866b --- /dev/null +++ b/src/corelib/arch/qatomic_armv7.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QATOMIC_ARMV7_H +#define QATOMIC_ARMV7_H + +QT_BEGIN_HEADER + +// use the DMB instruction when compiling for ARMv7, ... +#ifndef Q_CC_RCVT +# define Q_DATA_MEMORY_BARRIER asm volatile("dmb\n":::"memory") +#else +# define Q_DATA_MEMORY_BARRIER do{__asm { dmb } __schedule_barrier();}while(0) +#endif + +// ... but the implementation is otherwise identical to that for ARMv6 +QT_BEGIN_INCLUDE_HEADER +#include "QtCore/qatomic_armv6.h" +QT_END_INCLUDE_HEADER + +QT_END_HEADER + +#endif // QATOMIC_ARMV7_H diff --git a/src/corelib/arch/qatomic_integrity.h b/src/corelib/arch/qatomic_integrity.h new file mode 100644 index 0000000..6563903 --- /dev/null +++ b/src/corelib/arch/qatomic_integrity.h @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QATOMIC_INTEGRITY_H +#define QATOMIC_INTEGRITY_H + +#include <INTEGRITY.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define qt_i2addr(a) reinterpret_cast<Address *>(const_cast<int *>(a)) +#define qt_p2addr(a) reinterpret_cast<Address *>(const_cast<void *>(a)) +#define qt_addr(a) reinterpret_cast<Address>(a) + + +#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isReferenceCountingNative() +{ return false; } +inline bool QBasicAtomicInt::isReferenceCountingWaitFree() +{ return false; } + +#define Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isTestAndSetNative() +{ return true; } +inline bool QBasicAtomicInt::isTestAndSetWaitFree() +{ return true; } + +#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isFetchAndStoreNative() +{ return true; } +inline bool QBasicAtomicInt::isFetchAndStoreWaitFree() +{ return true; } + +#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE + +inline bool QBasicAtomicInt::isFetchAndAddNative() +{ return true; } +inline bool QBasicAtomicInt::isFetchAndAddWaitFree() +{ return true; } + +#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetNative() +{ return true; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isTestAndSetWaitFree() +{ return true; } + +#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreNative() +{ return true; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndStoreWaitFree() +{ return true; } + +#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddNative() +{ return true; } +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree() +{ return true; } + +// Reference counting + +inline bool QBasicAtomicInt::ref() +{ + int oldval; + AtomicModify(qt_i2addr(&_q_value), qt_i2addr(&oldval), 0, 1); + return _q_value != -1; +} + +inline bool QBasicAtomicInt::deref() +{ + int oldval; + AtomicModify(qt_i2addr(&_q_value), qt_i2addr(&oldval), 0, -1U); + return _q_value != 0; +} + +// Test and set for integers + +inline bool QBasicAtomicInt::testAndSetOrdered(int expectedValue, int newValue) +{ + return TestAndSet(qt_i2addr(&_q_value), expectedValue, newValue) == Success; +} + +inline bool QBasicAtomicInt::testAndSetRelaxed(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +inline bool QBasicAtomicInt::testAndSetAcquire(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +// Fetch and store for integers + +inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) +{ + int old_val; + do { + old_val = _q_value; + } while (TestAndSet(qt_i2addr(&_q_value), old_val, newValue) != Success); + return old_val; +} + +inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +inline int QBasicAtomicInt::fetchAndStoreAcquire(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +inline int QBasicAtomicInt::fetchAndStoreRelease(int newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +// Fetch and add for integers + +inline int QBasicAtomicInt::fetchAndAddOrdered(int valueToAdd) +{ + int old_val; + do { + old_val = _q_value; + } while (TestAndSet(qt_i2addr(&_q_value), old_val, old_val + valueToAdd) != Success); + return old_val; +} + +inline int QBasicAtomicInt::fetchAndAddRelaxed(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +inline int QBasicAtomicInt::fetchAndAddAcquire(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +inline int QBasicAtomicInt::fetchAndAddRelease(int valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +// Test and set for pointers + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expectedValue, T *newValue) +{ + return TestAndSet((Address*)&_q_value, qt_addr(expectedValue), qt_addr(newValue)) == Success; +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelaxed(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetAcquire(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValue, T *newValue) +{ + return testAndSetOrdered(expectedValue, newValue); +} + +// Fetch and store for pointers + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) +{ + Address old_val; + do { + old_val = *reinterpret_cast<Address *>(const_cast<T *>(newValue)); + } while (TestAndSet(reinterpret_cast<Address *>(const_cast<T **>(&_q_value)), old_val, qt_addr(newValue)) != Success); + return reinterpret_cast<T *>(old_val); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreAcquire(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelease(T *newValue) +{ + return fetchAndStoreOrdered(newValue); +} + +// Fetch and add for pointers + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueToAdd) +{ + AtomicModify(qt_p2addr(&_q_value), qt_addr(_q_value), qt_addr(_q_value) + valueToAdd * sizeof(T)); + return _q_value; +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelaxed(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddAcquire(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +template <typename T> +Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddRelease(qptrdiff valueToAdd) +{ + return fetchAndAddOrdered(valueToAdd); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QATOMIC_INTEGRITY_H + diff --git a/src/corelib/arch/symbian/arch.pri b/src/corelib/arch/symbian/arch.pri index 70daee3..2e2a9b6 100644 --- a/src/corelib/arch/symbian/arch.pri +++ b/src/corelib/arch/symbian/arch.pri @@ -2,7 +2,7 @@ # Symbian architecture # SOURCES += $$QT_ARCH_CPP/qatomic_symbian.cpp \ - $$QT_ARCH_CPP/../armv6/qatomic_generic_armv6.cpp \ + $$QT_ARCH_CPP/qatomic_generic_armv6.cpp \ $$QT_ARCH_CPP/heap_hybrid.cpp \ $$QT_ARCH_CPP/debugfunction.cpp \ $$QT_ARCH_CPP/qt_heapsetup_symbian.cpp diff --git a/src/corelib/arch/armv6/qatomic_generic_armv6.cpp b/src/corelib/arch/symbian/qatomic_generic_armv6.cpp index f9ce6fd..f9ce6fd 100644 --- a/src/corelib/arch/armv6/qatomic_generic_armv6.cpp +++ b/src/corelib/arch/symbian/qatomic_generic_armv6.cpp diff --git a/src/corelib/arch/symbian/qt_hybridheap_symbian_p.h b/src/corelib/arch/symbian/qt_hybridheap_symbian_p.h index 4eff0f3..d1ce705 100644 --- a/src/corelib/arch/symbian/qt_hybridheap_symbian_p.h +++ b/src/corelib/arch/symbian/qt_hybridheap_symbian_p.h @@ -43,8 +43,9 @@ #define QT_HYBRIDHEAP_SYMBIAN_H #include <qglobal.h> +#include <e32cmn.h> -#if !defined(SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS) && !defined(__WINS__) +#if !defined(__SYMBIAN_KERNEL_HYBRID_HEAP__) && !defined(__WINS__) //Enable the (backported) new allocator. When it is available in OS, //this flag should be disabled for that OS version onward #define QT_USE_NEW_SYMBIAN_ALLOCATOR diff --git a/src/corelib/codecs/qiconvcodec.cpp b/src/corelib/codecs/qiconvcodec.cpp index 4497b8c..053b3d2 100644 --- a/src/corelib/codecs/qiconvcodec.cpp +++ b/src/corelib/codecs/qiconvcodec.cpp @@ -173,6 +173,7 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState int invalidCount = 0; int remainingCount = 0; char *remainingBuffer = 0; + IconvState *temporaryState = 0; IconvState **pstate; if (convState) { @@ -193,11 +194,11 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState // we're running after the Q_GLOBAL_STATIC has been deleted // or before the QCoreApplication initialization // bad programmer, no cookie for you - return QString::fromLatin1(chars, len); + pstate = &temporaryState; + } else { + // stateless conversion -- use thread-local data + pstate = &toUnicodeState()->localData(); } - - // stateless conversion -- use thread-local data - pstate = &toUnicodeState()->localData(); } if (!*pstate) { @@ -280,6 +281,7 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); } + delete temporaryState; return QString::fromLatin1(chars, len); } } while (inBytesLeft != 0); @@ -294,6 +296,7 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); } + delete temporaryState; return s; } @@ -337,30 +340,22 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt char **inBytesPtr = &inBytes; #endif + IconvState *temporaryState = 0; QThreadStorage<QIconvCodec::IconvState *> *ts = fromUnicodeState(); - if (!qt_locale_initialized || !ts) { - // we're running after the Q_GLOBAL_STATIC has been deleted - // or before the QCoreApplication initialization - // bad programmer, no cookie for you - if (!len) - // this is a special case - zero-sized string should be - // translated to empty but not-null QByteArray. - return QByteArray(""); - return QString::fromRawData(uc, len).toLatin1(); - } - IconvState *&state = ts->localData(); + IconvState *&state = (qt_locale_initialized && ts) ? ts->localData() : temporaryState; if (!state) { - state = new IconvState(QIconvCodec::createIconv_t(0, UTF16)); - if (state->cd == reinterpret_cast<iconv_t>(-1)) { - if (!setByteOrder(state->cd)) { + iconv_t cd = QIconvCodec::createIconv_t(0, UTF16); + if (cd != reinterpret_cast<iconv_t>(-1)) { + if (!setByteOrder(cd)) { perror("QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv failed for BOM"); - iconv_close(state->cd); - state->cd = reinterpret_cast<iconv_t>(-1); + iconv_close(cd); + cd = reinterpret_cast<iconv_t>(-1); return QString(uc, len).toLatin1(); } } + state = new IconvState(cd); } if (state->cd == reinterpret_cast<iconv_t>(-1)) { static int reported = 0; @@ -368,6 +363,7 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt fprintf(stderr, "QIconvCodec::convertFromUnicode: using Latin-1 for conversion, iconv_open failed\n"); } + delete temporaryState; return QString(uc, len).toLatin1(); } @@ -430,6 +426,7 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt // reset to initial state iconv(state->cd, 0, &inBytesLeft, 0, &outBytesLeft); + delete temporaryState; return QString(uc, len).toLatin1(); } } @@ -445,6 +442,7 @@ QByteArray QIconvCodec::convertFromUnicode(const QChar *uc, int len, ConverterSt if (convState) convState->invalidChars = invalidCount; + delete temporaryState; return ba; } diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index 93ed5ed..3c3d39e 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -64,7 +64,7 @@ #ifndef QT_NO_CODECS # include "qtsciicodec_p.h" # include "qisciicodec_p.h" -#ifndef Q_OS_SYMBIAN +#if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_INTEGRITY) # if defined(QT_NO_ICONV) && !defined(QT_BOOTSTRAPPED) // no iconv(3) support, must build all codecs into the library # include "../../plugins/codecs/cn/qgb18030codec.h" @@ -772,7 +772,7 @@ static void setup() # endif // Q_WS_X11 -#ifndef Q_OS_SYMBIAN +#if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_INTEGRITY) # if defined(QT_NO_ICONV) && !defined(QT_BOOTSTRAPPED) // no asian codecs when bootstrapping, sorry (void)new QGb18030Codec; @@ -805,7 +805,7 @@ static void setup() (void)new QLatin1Codec; (void)new QUtf8Codec; -#ifndef Q_OS_SYMBIAN +#if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_INTEGRITY) #if defined(Q_OS_UNIX) && !defined(QT_NO_ICONV) && !defined(QT_BOOTSTRAPPED) // QIconvCodec depends on the UTF-16 codec, so it needs to be created last (void) new QIconvCodec(); diff --git a/src/corelib/concurrent/qfuture.h b/src/corelib/concurrent/qfuture.h index f638c98..e8a6e26 100644 --- a/src/corelib/concurrent/qfuture.h +++ b/src/corelib/concurrent/qfuture.h @@ -210,7 +210,7 @@ public: bool operator==(const QFuture &other) const { return (d == other.d); } bool operator!=(const QFuture &other) const { return (d != other.d); } -#if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(Q_CC_XLC) +#if !defined(Q_CC_XLC) template <typename T> QFuture(const QFuture<T> &other) : d(other.d) diff --git a/src/corelib/concurrent/qfuturewatcher.cpp b/src/corelib/concurrent/qfuturewatcher.cpp index 641db71..ea12bc9 100644 --- a/src/corelib/concurrent/qfuturewatcher.cpp +++ b/src/corelib/concurrent/qfuturewatcher.cpp @@ -359,6 +359,15 @@ void QFutureWatcherBase::connectNotify(const char * signal) Q_D(QFutureWatcherBase); if (qstrcmp(signal, SIGNAL(resultReadyAt(int))) == 0) d->resultAtConnected.ref(); +#ifndef QT_NO_DEBUG + if (qstrcmp(signal, SIGNAL(finished())) == 0) { + if (futureInterface().isRunning()) { + //connections should be established before calling stFuture to avoid race. + // (The future could finish before the connection is made.) + qWarning("QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race"); + } + } +#endif } void QFutureWatcherBase::disconnectNotify(const char * signal) diff --git a/src/corelib/concurrent/qtconcurrentcompilertest.h b/src/corelib/concurrent/qtconcurrentcompilertest.h index d02b859..fad0c35 100644 --- a/src/corelib/concurrent/qtconcurrentcompilertest.h +++ b/src/corelib/concurrent/qtconcurrentcompilertest.h @@ -51,12 +51,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Core) -#ifdef Q_CC_MSVC -# if _MSC_VER < 1300 -# define QT_CONURRENT_NONSTANDARD_COMPILER -# endif -#endif - #if defined (Q_CC_MSVC) && (_MSC_VER < 1300) # define QT_TYPENAME #else diff --git a/src/corelib/concurrent/qtconcurrentiteratekernel.h b/src/corelib/concurrent/qtconcurrentiteratekernel.h index a42aa9d..186f752 100644 --- a/src/corelib/concurrent/qtconcurrentiteratekernel.h +++ b/src/corelib/concurrent/qtconcurrentiteratekernel.h @@ -184,12 +184,9 @@ public: #if defined (QT_NO_STL) : begin(_begin), end(_end), current(_begin), currentIndex(0), forIteration(false), progressReportingEnabled(true) -#elif !defined(QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION) +#else : begin(_begin), end(_end), current(_begin), currentIndex(0), forIteration(selectIteration(typename std::iterator_traits<Iterator>::iterator_category())), progressReportingEnabled(true) -#else - : begin(_begin), end(_end), currentIndex(0), - forIteration(selectIteration(std::iterator_category(_begin))), progressReportingEnabled(true) #endif { #if defined (QT_NO_STL) diff --git a/src/corelib/concurrent/qtconcurrentrunbase.h b/src/corelib/concurrent/qtconcurrentrunbase.h index 5dacc7b..f26a3e3 100644 --- a/src/corelib/concurrent/qtconcurrentrunbase.h +++ b/src/corelib/concurrent/qtconcurrentrunbase.h @@ -100,7 +100,18 @@ public: this->reportFinished(); return; } - this->runFunctor(); +#ifndef QT_NO_EXCEPTIONS + try { +#endif + this->runFunctor(); +#ifndef QT_NO_EXCEPTIONS + } catch (QtConcurrent::Exception &e) { + QFutureInterface<T>::reportException(e); + } catch (...) { + QFutureInterface<T>::reportException(QtConcurrent::UnhandledException()); + } +#endif + this->reportResult(result); this->reportFinished(); } @@ -117,7 +128,17 @@ public: this->reportFinished(); return; } - this->runFunctor(); +#ifndef QT_NO_EXCEPTIONS + try { +#endif + this->runFunctor(); +#ifndef QT_NO_EXCEPTIONS + } catch (QtConcurrent::Exception &e) { + QFutureInterface<void>::reportException(e); + } catch (...) { + QFutureInterface<void>::reportException(QtConcurrent::UnhandledException()); + } +#endif this->reportFinished(); } }; diff --git a/src/corelib/concurrent/qthreadpool.cpp b/src/corelib/concurrent/qthreadpool.cpp index 49c59c9..1a07b5b 100644 --- a/src/corelib/concurrent/qthreadpool.cpp +++ b/src/corelib/concurrent/qthreadpool.cpp @@ -41,6 +41,7 @@ #include "qthreadpool.h" #include "qthreadpool_p.h" +#include "qelapsedtimer.h" #ifndef QT_NO_THREAD @@ -249,6 +250,7 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const void QThreadPoolPrivate::startThread(QRunnable *runnable) { QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this)); + thread->setObjectName(QLatin1String("Thread (pooled)")); allThreads.insert(thread.data()); ++activeThreads; @@ -288,11 +290,21 @@ void QThreadPoolPrivate::reset() isExiting = false; } -void QThreadPoolPrivate::waitForDone() +bool QThreadPoolPrivate::waitForDone(int msecs) { QMutexLocker locker(&mutex); - while (!(queue.isEmpty() && activeThreads == 0)) - noActiveThreads.wait(locker.mutex()); + if (msecs < 0) { + while (!(queue.isEmpty() && activeThreads == 0)) + noActiveThreads.wait(locker.mutex()); + } else { + QElapsedTimer timer; + timer.start(); + int t; + while (!(queue.isEmpty() && activeThreads == 0) && + ((t = msecs - timer.elapsed()) > 0)) + noActiveThreads.wait(locker.mutex(), t); + } + return queue.isEmpty() && activeThreads == 0; } /*! \internal @@ -617,6 +629,23 @@ void QThreadPool::waitForDone() d->reset(); } +/*! + \overload waitForDone() + \since 4.8 + + Waits up to \a msecs milliseconds for all threads to exit and removes all + threads from the thread pool. Returns true if all threads were removed; + otherwise it returns false. +*/ +bool QThreadPool::waitForDone(int msecs) +{ + Q_D(QThreadPool); + bool rc = d->waitForDone(msecs); + if (rc) + d->reset(); + return rc; +} + QT_END_NAMESPACE #endif diff --git a/src/corelib/concurrent/qthreadpool.h b/src/corelib/concurrent/qthreadpool.h index 9fc883d..d0326ca 100644 --- a/src/corelib/concurrent/qthreadpool.h +++ b/src/corelib/concurrent/qthreadpool.h @@ -85,6 +85,7 @@ public: void releaseThread(); void waitForDone(); + bool waitForDone(int msecs); }; QT_END_NAMESPACE diff --git a/src/corelib/concurrent/qthreadpool_p.h b/src/corelib/concurrent/qthreadpool_p.h index 6986c16..8f38df7 100644 --- a/src/corelib/concurrent/qthreadpool_p.h +++ b/src/corelib/concurrent/qthreadpool_p.h @@ -82,7 +82,7 @@ public: void startThread(QRunnable *runnable = 0); void reset(); - void waitForDone(); + bool waitForDone(int msecs = -1); bool startFrontRunnable(); void stealRunnable(QRunnable *); diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index 06aa191..58d2c7b 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -19,8 +19,14 @@ include(codecs/codecs.pri) include(statemachine/statemachine.pri) include(xml/xml.pri) -mac|darwin:LIBS_PRIVATE += -framework ApplicationServices - +!qpa:mac|darwin:LIBS_PRIVATE += -framework ApplicationServices +qpa { + contains(QT_CONFIG, coreservices) { + LIBS_PRIVATE += -framework CoreServices + } +} else:mac|darwin { + LIBS_PRIVATE += -framework CoreFoundation +} mac:lib_bundle:DEFINES += QT_NO_DEBUG_PLUGIN_CHECK win32:DEFINES-=QT_NO_CAST_TO_ASCII diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index 8ff423e..68582bf 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -19,7 +19,7 @@ INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global # Only used on platforms with CONFIG += precompile_header PRECOMPILED_HEADER = global/qt_pch.h -linux*:!static:!symbian-armcc:!symbian-gcce { +linux*:!static:!symbian-gcce:!*-armcc* { QMAKE_LFLAGS += -Wl,-e,qt_core_boilerplate prog=$$quote(if (/program interpreter: (.*)]/) { print $1; }) DEFINES += ELF_INTERPRETER=\\\"$$system(readelf -l /bin/ls | perl -n -e \'$$prog\')\\\" diff --git a/src/corelib/global/qconfig-nacl.h b/src/corelib/global/qconfig-nacl.h new file mode 100644 index 0000000..a44391c --- /dev/null +++ b/src/corelib/global/qconfig-nacl.h @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#define QT_FONTS_ARE_RESOURCES + +/* Data structures */ +#ifndef QT_NO_QUUID_STRING +# define QT_NO_QUUID_STRING +#endif +#ifndef QT_NO_STL +# define QT_NO_STL +#endif +#ifndef QT_NO_TEXTDATE +# define QT_NO_TEXTDATE +#endif +#ifndef QT_NO_DATESTRING +# define QT_NO_DATESTRING +#endif + +/* Dialogs */ +#ifndef QT_NO_FILEDIALOG +# define QT_NO_FILEDIALOG +#endif +#ifndef QT_NO_PRINTDIALOG +# define QT_NO_PRINTDIALOG +#endif +#ifndef QT_NO_PRINTPREVIEWDIALOG +# define QT_NO_PRINTPREVIEWDIALOG +#endif + + +/* File I/O */ +#ifndef QT_NO_DOM +# define QT_NO_DOM +#endif +#ifndef QT_NO_FILESYSTEMWATCHER +# define QT_NO_FILESYSTEMWATCHER +#endif +#ifndef QT_NO_FSFILEENGINE +# define QT_NO_FSFILEENGINE +#endif +#ifndef QT_NO_FILESYSTEMMODEL +# define QT_NO_FILESYSTEMMODEL +#endif +#ifndef QT_NO_FILESYSTEMMODEL +# define QT_NO_FILESYSTEMMODEL +#endif +#ifndef QT_NO_PROCESS +# define QT_NO_PROCESS +#endif +#ifndef QT_NO_TEMPORARYFILE +# define QT_NO_TEMPORARYFILE +#endif +#ifndef QT_NO_SETTINGS +# define QT_NO_SETTINGS +#endif +#ifndef QT_NO_LIBRARY +# define QT_NO_LIBRARY +#endif + +/* Fonts */ +#ifndef QT_NO_QWS_QPF2 +# define QT_NO_QWS_QPF2 +#endif + +/* Images */ +#ifndef QT_NO_IMAGEFORMATPLUGIN +# define QT_NO_IMAGEFORMATPLUGIN +#endif +#ifndef QT_NO_IMAGE_HEURISTIC_MASK +# define QT_NO_IMAGE_HEURISTIC_MASK +#endif +#ifndef QT_NO_IMAGE_TEXT +# define QT_NO_IMAGE_TEXT +#endif +#ifndef QT_NO_MOVIE +# define QT_NO_MOVIE +#endif + +/* Internationalization */ +#ifndef QT_NO_BIG_CODECS +# define QT_NO_BIG_CODECS +#endif +#ifndef QT_NO_QWS_INPUTMETHODS +# define QT_NO_QWS_INPUTMETHODS +#endif +#ifndef QT_NO_TEXTCODEC +# define QT_NO_TEXTCODEC +#endif +#ifndef QT_NO_CODECS +# define QT_NO_CODECS +#endif +#ifndef QT_NO_TEXTCODECPLUGIN +# define QT_NO_TEXTCODECPLUGIN +#endif +#ifndef QT_NO_TRANSLATION +# define QT_NO_TRANSLATION +#endif +#ifndef QT_NO_TRANSLATION_UTF8 +# define QT_NO_TRANSLATION_UTF8 +#endif + +/* ItemViews */ + +#ifndef QT_NO_DIRMODEL +# define QT_NO_DIRMODEL +#endif + +/* Kernel */ +#ifndef QT_NO_CLIPBOARD +# define QT_NO_CLIPBOARD +#endif +#ifndef QT_NO_CSSPARSER +# define QT_NO_CSSPARSER +#endif +#ifndef QT_NO_CURSOR +# define QT_NO_CURSOR +#endif +#ifndef QT_NO_DRAGANDDROP +# define QT_NO_DRAGANDDROP +#endif +#ifndef QT_NO_EFFECTS +# define QT_NO_EFFECTS +#endif +#ifndef QT_NO_SESSIONMANAGER +# define QT_NO_SESSIONMANAGER +#endif +#ifndef QT_NO_SHAREDMEMORY +# define QT_NO_SHAREDMEMORY +#endif +#ifndef QT_NO_SOUND +# define QT_NO_SOUND +#endif +#ifndef QT_NO_SYSTEMLOCALE +# define QT_NO_SYSTEMSEMAPHORE +#endif +#ifndef QT_NO_SYSTEMSEMAPHORE +# define QT_NO_SYSTEMSEMAPHORE +#endif +#ifndef QT_NO_TABLETEVENT +# define QT_NO_TABLETEVENT +#endif +#ifndef QT_NO_CRASHHANDLER +# define QT_NO_CRASHHANDLER +#endif +#ifndef QT_NO_CONCURRENT +# define QT_NO_CONCURRENT +#endif +#ifndef QT_NO_XMLSTREAM +# define QT_NO_XMLSTREAM +#endif +#ifndef QT_NO_XMLSTREAMREADER +# define QT_NO_XMLSTREAMREADER +#endif +#ifndef QT_NO_XMLSTREAMWRITER +# define QT_NO_XMLSTREAMWRITER +#endif + +/* Networking */ +#ifndef QT_NO_COP +# define QT_NO_COP +#endif +#ifndef QT_NO_HOSTINFO +# define QT_NO_HOSTINFO +#endif +#ifndef QT_NO_HTTP +# define QT_NO_HTTP +#endif +#ifndef QT_NO_NETWORKPROXY +# define QT_NO_NETWORKPROXY +#endif +#ifndef QT_NO_SOCKS5 +# define QT_NO_SOCKS5 +#endif +#ifndef QT_NO_UDPSOCKET +# define QT_NO_UDPSOCKET +#endif +#ifndef QT_NO_URLINFO +# define QT_NO_URLINFO +#endif +#ifndef QT_NO_FTP +# define QT_NO_FTP +#endif + +/* Painting */ +#ifndef QT_NO_COLORNAMES +# define QT_NO_COLORNAMES +#endif +#ifndef QT_NO_DIRECTPAINTER +# define QT_NO_DIRECTPAINTER +#endif +#ifndef QT_NO_PAINTONSCREEN +# define QT_NO_PAINTONSCREEN +#endif +#ifndef QT_NO_PAINT_DEBUG +# define QT_NO_PAINT_DEBUG +#endif +#ifndef QT_NO_PICTURE +# define QT_NO_PICTURE +#endif +#ifndef QT_NO_PRINTER +# define QT_NO_PRINTER +#endif +#ifndef QT_NO_CUPS +# define QT_NO_CUPS +#endif + +/* Qt for Embedded Linux */ +#ifndef QT_NO_QWSEMBEDWIDGET +# define QT_NO_QWSEMBEDWIDGET +#endif +#ifndef QT_NO_QWS_ALPHA_CURSOR +# define QT_NO_QWS_ALPHA_CURSOR +#endif +#ifndef QT_NO_QWS_CURSOR +# define QT_NO_QWS_CURSOR +#endif +#ifndef QT_NO_QWS_DECORATION_DEFAULT +# define QT_NO_QWS_DECORATION_DEFAULT +#endif +#ifndef QT_NO_QWS_DECORATION_STYLED +# define QT_NO_QWS_DECORATION_STYLED +#endif +#ifndef QT_NO_QWS_DECORATION_WINDOWS +# define QT_NO_QWS_DECORATION_WINDOWS +#endif +#ifndef QT_NO_QWS_MANAGER +# define QT_NO_QWS_MANAGER +#endif +#ifndef QT_NO_QWS_KEYBOARD +# define QT_NO_QWS_KEYBOARD +#endif +#ifndef QT_NO_QWS_MOUSE +# define QT_NO_QWS_MOUSE +#endif +#ifndef QT_NO_QWS_MOUSE_AUTO +# define QT_NO_QWS_MOUSE_AUTO +#endif +#ifndef QT_NO_QWS_MOUSE_MANUAL +# define QT_NO_QWS_MOUSE_MANUAL +#endif +#ifndef QT_NO_QWS_MULTIPROCESS +# define QT_NO_QWS_MULTIPROCESS +#endif +#ifndef QT_NO_QWS_SOUNDSERVER +# define QT_NO_QWS_SOUNDSERVER +#endif +#ifndef QT_NO_SXE +# define QT_NO_SXE +#endif +#ifndef QT_NO_QWS_PROPERTIES +# define QT_NO_QWS_PROPERTIES +#endif +#ifndef QT_NO_QWS_PROXYSCREEN +# define QT_NO_QWS_PROXYSCREEN +#endif +#ifndef QT_NO_QWS_DYNAMICSCREENTRANSFORMATION +# define QT_NO_QWS_DYNAMICSCREENTRANSFORMATION +#endif +#ifndef QT_NO_QWS_LINUXFB +# define QT_NO_QWS_LINUXFB +#endif +#ifndef QT_NO_QWS_MOUSE_PC +# define QT_NO_QWS_MOUSE_PC +#endif +#ifndef QT_NO_QWS_MOUSE_LINUXTP +# define QT_NO_QWS_MOUSE_LINUXTP +#endif +#ifndef QT_NO_QWS_QPF +# define QT_NO_QWS_QPF +#endif + +/* SVG */ +#ifndef QT_NO_SVG +# define QT_NO_SVG +#endif +#ifndef QT_NO_GRAPHICSSVGITEM +# define QT_NO_GRAPHICSSVGITEM +#endif +#ifndef QT_NO_SVGGENERATOR +# define QT_NO_SVGGENERATOR +#endif +#ifndef QT_NO_SVGRENDERER +# define QT_NO_SVGRENDERER +#endif +#ifndef QT_NO_SVGWIDGET +# define QT_NO_SVGWIDGET +#endif + +/* Styles */ +#ifndef QT_NO_STYLE_MOTIF +# define QT_NO_STYLE_MOTIF +#endif +#ifndef QT_NO_STYLE_CDE +# define QT_NO_STYLE_CDE +#endif +#ifndef QT_NO_STYLE_STYLESHEET +# define QT_NO_STYLE_STYLESHEET +#endif +#ifndef QT_NO_STYLE_WINDOWSCE +# define QT_NO_STYLE_WINDOWSCE +#endif +#ifndef QT_NO_STYLE_WINDOWSMOBILE +# define QT_NO_STYLE_WINDOWSMOBILE +#endif +#ifndef QT_NO_STYLE_WINDOWSVISTA +# define QT_NO_STYLE_WINDOWSVISTA +#endif +#ifndef QT_NO_STYLE_WINDOWSXP +# define QT_NO_STYLE_WINDOWSXP +#endif + +/* Utilities */ +#ifndef QT_NO_ACCESSIBILITY +# define QT_NO_ACCESSIBILITY +#endif +#ifndef QT_NO_COMPLETER +# define QT_NO_COMPLETER +#endif +#ifndef QT_NO_DESKTOPSERVICES +# define QT_NO_DESKTOPSERVICES +#endif +#ifndef QT_NO_SCRIPT +# define QT_NO_SCRIPT +#endif +#ifndef QT_NO_SYSTEMTRAYICON +# define QT_NO_SYSTEMTRAYICON +#endif + +/* Windows */ +#ifndef QT_NO_WIN_ACTIVEQT +# define QT_NO_WIN_ACTIVEQT +#endif diff --git a/src/corelib/global/qendian.h b/src/corelib/global/qendian.h index 9d47638..566307b 100644 --- a/src/corelib/global/qendian.h +++ b/src/corelib/global/qendian.h @@ -88,7 +88,7 @@ template <typename T> inline void qToUnaligned(const T src, uchar *dest) * and return the value in host-endian encoding. * There is no requirement that \a src must be aligned. */ -#if defined Q_CC_MSVC && _MSC_VER < 1300 || defined Q_CC_SUN +#if defined Q_CC_SUN inline quint64 qFromLittleEndian_helper(const uchar *src, quint64 *dest) { return 0 @@ -176,7 +176,7 @@ template <> inline qint16 qFromLittleEndian<qint16>(const uchar *src) * and return the value in host-endian encoding. * There is no requirement that \a src must be aligned. */ -#if defined Q_CC_MSVC && _MSC_VER < 1300 || defined Q_CC_SUN +#if defined Q_CC_SUN inline quint64 qFromBigEndian_helper(const uchar *src, quint64 *dest) { return 0 diff --git a/src/corelib/global/qfeatures.h b/src/corelib/global/qfeatures.h index de2ca85..604d08a 100644 --- a/src/corelib/global/qfeatures.h +++ b/src/corelib/global/qfeatures.h @@ -79,6 +79,9 @@ // Effects //#define QT_NO_EFFECTS +// QFileSystemIterator +//#define QT_NO_FILESYSTEMITERATOR + // QFileSystemWatcher //#define QT_NO_FILESYSTEMWATCHER @@ -523,11 +526,6 @@ #define QT_NO_FTP #endif -// Hyper Text Transfer Protocol -#if !defined(QT_NO_HTTP) && (defined(QT_NO_HOSTINFO)) -#define QT_NO_HTTP -#endif - // QInputContext #if !defined(QT_NO_IM) && (defined(QT_NO_LIBRARY)) #define QT_NO_IM @@ -583,6 +581,11 @@ #define QT_NO_GRAPHICSVIEW #endif +// Hyper Text Transfer Protocol +#if !defined(QT_NO_HTTP) && defined(QT_NO_HOSTINFO) +#define QT_NO_HTTP +#endif + // QMdiArea #if !defined(QT_NO_MDIAREA) && (defined(QT_NO_SCROLLAREA)) #define QT_NO_MDIAREA diff --git a/src/corelib/global/qfeatures.txt b/src/corelib/global/qfeatures.txt index 4d938a9..6b861d4 100644 --- a/src/corelib/global/qfeatures.txt +++ b/src/corelib/global/qfeatures.txt @@ -242,6 +242,14 @@ Requires: Name: QFileSystemWatcher SeeAlso: ??? +Feature: FILESYSTEMITERATOR +Description: Provides fast file-system iteration. +for modications. +Section: File I/O +Requires: +Name: QFileSystemIterator +SeeAlso: ??? + # Widgets Feature: TREEWIDGET diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 35719b1..bcaed41 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -74,7 +74,7 @@ # include <envLib.h> #endif -#if defined(Q_CC_MWERKS) && defined(Q_OS_MACX) +#if defined(Q_OS_MACX) && !defined(QT_NO_CORESERVICES) #include <CoreServices/CoreServices.h> #endif @@ -1173,6 +1173,7 @@ bool qSharedBuild() \value MV_10_4 Mac OS X 10.4 \value MV_10_5 Mac OS X 10.5 \value MV_10_6 Mac OS X 10.6 + \value MV_10_7 Mac OS X 10.7 \value MV_Unknown An unknown and currently unsupported platform \value MV_CHEETAH Apple codename for MV_10_0 @@ -1182,6 +1183,7 @@ bool qSharedBuild() \value MV_TIGER Apple codename for MV_10_4 \value MV_LEOPARD Apple codename for MV_10_5 \value MV_SNOWLEOPARD Apple codename for MV_10_6 + \value MV_LION Apple codename for MV_10_7 \sa WinVersion, SymbianVersion */ @@ -1232,7 +1234,7 @@ bool qSharedBuild() Defined on Mac OS X. - \sa Q_WS_WIN, Q_WS_X11, Q_WS_QWS, Q_WS_S60 + \sa Q_WS_WIN, Q_WS_X11, Q_WS_QWS, Q_WS_QPA, Q_WS_S60 */ /*! @@ -1241,7 +1243,7 @@ bool qSharedBuild() Defined on Windows. - \sa Q_WS_MAC, Q_WS_X11, Q_WS_QWS, Q_WS_S60 + \sa Q_WS_MAC, Q_WS_X11, Q_WS_QWS, Q_WS_QPA, Q_WS_S60 */ /*! @@ -1250,7 +1252,7 @@ bool qSharedBuild() Defined on X11. - \sa Q_WS_MAC, Q_WS_WIN, Q_WS_QWS, Q_WS_S60 + \sa Q_WS_MAC, Q_WS_WIN, Q_WS_QWS, Q_WS_QPA, Q_WS_S60 */ /*! @@ -1259,7 +1261,16 @@ bool qSharedBuild() Defined on Qt for Embedded Linux. - \sa Q_WS_MAC, Q_WS_WIN, Q_WS_X11, Q_WS_S60 + \sa Q_WS_MAC, Q_WS_WIN, Q_WS_X11, Q_WS_QPA, Q_WS_S60 +*/ + +/*! + \macro Q_WS_QPA + \relates <QtGlobal> + + Defined on Qt for Embedded Linux, Lite version. + + \sa Q_WS_MAC, Q_WS_WIN, Q_WS_X11, Q_WS_QWS, Q_WS_S60 */ /*! @@ -1634,7 +1645,7 @@ static const unsigned int qt_one = 1; const int QSysInfo::ByteOrder = ((*((unsigned char *) &qt_one) == 0) ? BigEndian : LittleEndian); #endif -#if !defined(QWS) && defined(Q_OS_MAC) +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) QT_BEGIN_INCLUDE_NAMESPACE #include "private/qcore_mac_p.h" @@ -1688,15 +1699,18 @@ Q_CORE_EXPORT void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding e Q_CORE_EXPORT QString qt_mac_from_pascal_string(const Str255 pstr) { return QCFString(CFStringCreateWithPascalString(0, pstr, CFStringGetSystemEncoding())); } +#endif //!defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) - +#if !defined(QWS) && defined(Q_OS_MAC) static QSysInfo::MacVersion macVersion() { +#ifndef QT_NO_CORESERVICES SInt32 gestalt_version; if (Gestalt(gestaltSystemVersion, &gestalt_version) == noErr) { return QSysInfo::MacVersion(((gestalt_version & 0x00F0) >> 4) + 2); } +#endif return QSysInfo::MV_Unknown; } const QSysInfo::MacVersion QSysInfo::MacintoshVersion = macVersion(); @@ -1726,7 +1740,7 @@ QSysInfo::WinVersion QSysInfo::windowsVersion() if (winver) return winver; winver = QSysInfo::WV_NT; - OSVERSIONINFOW osver; + OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(osver); GetVersionEx(&osver); #ifdef Q_OS_WINCE @@ -2007,7 +2021,7 @@ QSysInfo::S60Version QSysInfo::s60Version() */ void qt_check_pointer(const char *n, int l) { - qWarning("In file %s, line %d: Out of memory", n, l); + qFatal("In file %s, line %d: Out of memory", n, l); } /* \internal @@ -2238,7 +2252,8 @@ void qt_message_output(QtMsgType msgType, const char *buf) _LIT(format, "[Qt Message] %S"); const int maxBlockSize = 256 - ((const TDesC &)format).Length(); const TPtrC8 ptr(reinterpret_cast<const TUint8*>(buf)); - HBufC* hbuffer = q_check_ptr(HBufC::New(qMin(maxBlockSize, ptr.Length()))); + HBufC* hbuffer = HBufC::New(qMin(maxBlockSize, ptr.Length())); + Q_CHECK_PTR(hbuffer); for (int i = 0; i < ptr.Length(); i += hbuffer->Length()) { hbuffer->Des().Copy(ptr.Mid(i, qMin(maxBlockSize, ptr.Length()-i))); RDebug::Print(format, hbuffer); @@ -2576,7 +2591,7 @@ bool qputenv(const char *varName, const QByteArray& value) #endif } -#if (defined(Q_OS_UNIX) || defined(Q_OS_WIN)) && !defined(QT_NO_THREAD) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_THREAD) # if defined(Q_OS_INTEGRITY) && defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 500) // older versions of INTEGRITY used a long instead of a uint for the seed. @@ -2607,7 +2622,7 @@ Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value */ void qsrand(uint seed) { -#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && !defined(Q_OS_SYMBIAN) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_THREAD) SeedStorage *seedStorage = randTLS(); if (seedStorage) { SeedStorageType *pseed = seedStorage->localData(); @@ -2615,10 +2630,10 @@ void qsrand(uint seed) seedStorage->setLocalData(pseed = new SeedStorageType); *pseed = seed; } else { - //golbal static seed storage should always exist, + //global static seed storage should always exist, //except after being deleted by QGlobalStaticDeleter. //But since it still can be called from destructor of another - //global static object, fallback to sqrand(seed) + //global static object, fallback to srand(seed) srand(seed); } #else @@ -2646,7 +2661,7 @@ void qsrand(uint seed) */ int qrand() { -#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && !defined(Q_OS_SYMBIAN) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_THREAD) SeedStorage *seedStorage = randTLS(); if (seedStorage) { SeedStorageType *pseed = seedStorage->localData(); @@ -2656,10 +2671,10 @@ int qrand() } return rand_r(pseed); } else { - //golbal static seed storage should always exist, + //global static seed storage should always exist, //except after being deleted by QGlobalStaticDeleter. //But since it still can be called from destructor of another - //global static object, fallback to qrand() + //global static object, fallback to rand() return rand(); } #else diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 7768b46..d3b3e14 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -44,11 +44,11 @@ #include <stddef.h> -#define QT_VERSION_STR "4.7.4" +#define QT_VERSION_STR "4.8.0" /* QT_VERSION is (major << 16) + (minor << 8) + patch. */ -#define QT_VERSION 0x040704 +#define QT_VERSION 0x040800 /* can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)) */ @@ -64,6 +64,10 @@ #ifdef __cplusplus +#ifndef QT_NO_STL +#include <algorithm> +#endif + #ifndef QT_NAMESPACE /* user namespace */ # define QT_PREPEND_NAMESPACE(name) ::name @@ -216,6 +220,8 @@ namespace QT_NAMESPACE {} # define Q_OS_ULTRIX #elif defined(sinix) # define Q_OS_RELIANT +#elif defined(__native_client__) +# define Q_OS_NACL #elif defined(__linux__) || defined(__linux) # define Q_OS_LINUX #elif defined(__FreeBSD__) || defined(__DragonFly__) @@ -284,7 +290,7 @@ namespace QT_NAMESPACE {} # endif #endif -#if defined(Q_OS_MAC64) && !defined(QT_MAC_USE_COCOA) && !defined(QT_BUILD_QMAKE) && !defined(QT_BOOTSTRAPPED) +#if defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA) && !defined(QT_BUILD_QMAKE) && !defined(QT_BOOTSTRAPPED) #error "You are building a 64-bit application, but using a 32-bit version of Qt. Check your build configuration." #endif @@ -316,7 +322,10 @@ namespace QT_NAMESPACE {} # if !defined(MAC_OS_X_VERSION_10_6) # define MAC_OS_X_VERSION_10_6 MAC_OS_X_VERSION_10_5 + 1 # endif -# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6) +# if !defined(MAC_OS_X_VERSION_10_7) +# define MAC_OS_X_VERSION_10_7 MAC_OS_X_VERSION_10_6 + 1 +# endif +# if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_7) # warning "This version of Mac OS X is unsupported" # endif #endif @@ -356,13 +365,27 @@ namespace QT_NAMESPACE {} GCCE - GCCE (Symbian GCCE builds) RVCT - ARM Realview Compiler Suite NOKIAX86 - Nokia x86 (Symbian WINSCW builds) + CLANG - C++ front-end for the LLVM compiler Should be sorted most to least authoritative. */ #if defined(__ghs) -# define Q_OUTOFLINE_TEMPLATE inline +# define Q_OUTOFLINE_TEMPLATE inline + +/* the following are necessary because the GHS C++ name mangling relies on __*/ +# define Q_CONSTRUCTOR_FUNCTION0(AFUNC) \ + static const int AFUNC ## _init_variable_ = AFUNC(); +# define Q_CONSTRUCTOR_FUNCTION(AFUNC) Q_CONSTRUCTOR_FUNCTION0(AFUNC) +# define Q_DESTRUCTOR_FUNCTION0(AFUNC) \ + class AFUNC ## _dest_class_ { \ + public: \ + inline AFUNC ## _dest_class_() { } \ + inline ~ AFUNC ## _dest_class_() { AFUNC(); } \ + } AFUNC ## _dest_instance_; +# define Q_DESTRUCTOR_FUNCTION(AFUNC) Q_DESTRUCTOR_FUNCTION0(AFUNC) + #endif /* Symantec C++ is now Digital Mars */ @@ -383,31 +406,12 @@ namespace QT_NAMESPACE {} #elif defined(_MSC_VER) # define Q_CC_MSVC -/* proper support of bool for _MSC_VER >= 1100 */ +# define Q_CC_MSVC_NET # define Q_CANNOT_DELETE_CONSTANT # define Q_OUTOFLINE_TEMPLATE inline # define Q_NO_TEMPLATE_FRIENDS -# define QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION -# define Q_ALIGNOF(type) __alignof(type) -# define Q_DECL_ALIGN(n) __declspec(align(n)) - -/* Visual C++.Net issues for _MSC_VER >= 1300 */ -# if _MSC_VER >= 1300 -# define Q_CC_MSVC_NET -# if _MSC_VER < 1310 || (defined(Q_OS_WIN64) && defined(_M_IA64)) -# define Q_TYPENAME -# else -# undef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION -# endif -# else -# define Q_NO_USING_KEYWORD -# define QT_NO_MEMBER_TEMPLATES -# endif -# if _MSC_VER < 1310 -# define QT_NO_QOBJECT_CHECK -# define Q_TYPENAME -# define QT_NO_TEMPLATE_TEMPLATE_PARAMETERS -# endif +# define Q_ALIGNOF(type) __alignof(type) +# define Q_DECL_ALIGN(n) __declspec(align(n)) /* Intel C++ disguising as Visual C++: the `using' keyword avoids warnings */ # if defined(__INTEL_COMPILER) # define Q_CC_INTEL @@ -419,6 +423,19 @@ namespace QT_NAMESPACE {} # undef QT_HAVE_3DNOW # endif +#if defined(Q_CC_MSVC) && _MSC_VER >= 1600 +# define Q_COMPILER_RVALUE_REFS +# define Q_COMPILER_INITIALIZER_LISTS +# define Q_COMPILER_AUTO_TYPE +# define Q_COMPILER_LAMBDA +//# define Q_COMPILER_VARIADIC_TEMPLATES +//# define Q_COMPILER_CLASS_ENUM +//# define Q_COMPILER_DEFAULT_DELETE_MEMBERS +//# define Q_COMPILER_UNICODE_STRINGS +//# define Q_COMPILER_EXTERN_TEMPLATES +# endif + + #elif defined(__BORLANDC__) || defined(__TURBOC__) # define Q_CC_BOR # define Q_INLINE_TEMPLATE @@ -447,6 +464,9 @@ namespace QT_NAMESPACE {} # if __TARGET_ARCH_ARM >= 6 # define QT_HAVE_ARMV6 # endif +/* work-around for missing compiler intrinsics */ +# define __is_empty(X) false +# define __is_pod(X) false #elif defined(__GNUC__) # define Q_CC_GNU # define Q_C_CALLBACKS @@ -456,7 +476,10 @@ namespace QT_NAMESPACE {} # if defined(__INTEL_COMPILER) /* Intel C++ also masquerades as GCC 3.2.0 */ # define Q_CC_INTEL -# define Q_NO_TEMPLATE_FRIENDS +# endif +# if defined(__clang__) +/* Clang also masquerades as GCC 4.2.1 */ +# define Q_CC_CLANG # endif # ifdef __APPLE__ # define Q_NO_DEPRECATED_CONSTRUCTORS @@ -493,6 +516,26 @@ namespace QT_NAMESPACE {} # define QT_NO_ARM_EABI # endif # endif +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 + /* C++0x features supported in GCC 4.3: */ +# define Q_COMPILER_RVALUE_REFS +# endif +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 404 + /* C++0x features supported in GCC 4.4: */ +# define Q_COMPILER_VARIADIC_TEMPLATES +# define Q_COMPILER_AUTO_TYPE +# define Q_COMPILER_EXTERN_TEMPLATES +# define Q_COMPILER_DEFAULT_DELETE_MEMBERS +# define Q_COMPILER_CLASS_ENUM +# define Q_COMPILER_INITIALIZER_LISTS +# endif +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 + /* C++0x features supported in GCC 4.5: */ +# define Q_COMPILER_LAMBDA +# define Q_COMPILER_UNICODE_STRINGS +# endif +# endif /* IBM compiler versions are a bit messy. There are actually two products: the C product, and the C++ product. The C++ compiler is always packaged @@ -712,7 +755,7 @@ namespace QT_NAMESPACE {} # define Q_DECL_IMPORT __declspec(dllimport) # endif # if __HP_aCC-0 >= 061200 -# define Q_DECL_ALIGNED(n) __attribute__((aligned(n))) +# define Q_DECL_ALIGN(n) __attribute__((aligned(n))) # endif # if __HP_aCC-0 >= 062000 # define Q_DECL_EXPORT __attribute__((visibility("default"))) @@ -735,6 +778,24 @@ namespace QT_NAMESPACE {} # error "Qt has not been tested with this compiler - talk to qt-bugs@trolltech.com" #endif +#ifdef Q_CC_INTEL +# if __INTEL_COMPILER < 1200 +# define Q_NO_TEMPLATE_FRIENDS +# endif +# if defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) +# if __INTEL_COMPILER >= 1100 +# define Q_COMPILER_RVALUE_REFS +# define Q_COMPILER_EXTERN_TEMPLATES +# elif __INTEL_COMPILER >= 1200 +# define Q_COMPILER_VARIADIC_TEMPLATES +# define Q_COMPILER_AUTO_TYPE +# define Q_COMPILER_DEFAULT_DELETE_MEMBERS +# define Q_COMPILER_CLASS_ENUM +# define Q_COMPILER_LAMBDA +# endif +# endif +#endif + #ifndef Q_PACKED # define Q_PACKED # undef Q_NO_PACKED_REFERENCE @@ -803,7 +864,7 @@ namespace QT_NAMESPACE {} # define Q_WS_PM # error "Qt does not work with OS/2 Presentation Manager or Workplace Shell" #elif defined(Q_OS_UNIX) -# if defined(Q_OS_MAC) && !defined(__USE_WS_X11__) && !defined(Q_WS_QWS) +# if defined(Q_OS_MAC) && !defined(__USE_WS_X11__) && !defined(Q_WS_QWS) && !defined(Q_WS_QPA) # define Q_WS_MAC # define Q_WS_MACX # if defined(Q_OS_MAC64) @@ -815,7 +876,7 @@ namespace QT_NAMESPACE {} # if !defined(QT_NO_S60) # define Q_WS_S60 # endif -# elif !defined(Q_WS_QWS) +# elif !defined(Q_WS_QWS) && !defined(Q_WS_QPA) # define Q_WS_X11 # endif #endif @@ -924,10 +985,10 @@ redefine to built-in booleans to make autotests work properly */ #endif /* - Proper for-scoping in VC++6 and MIPSpro CC + Proper for-scoping in MIPSpro CC */ #ifndef QT_NO_KEYWORDS -# if (defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) && !defined(Q_CC_INTEL)) || defined(Q_CC_MIPS) || (defined(Q_CC_HPACC) && defined(__ia64)) +# if defined(Q_CC_MIPS) || (defined(Q_CC_HPACC) && defined(__ia64)) # define for if(0){}else for # endif #endif @@ -951,7 +1012,7 @@ redefine to built-in booleans to make autotests work properly */ # define Q_DECL_DEPRECATED Q_DECL_DEPRECATED #elif (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && (__GNUC__ - 0 > 3 || (__GNUC__ - 0 == 3 && __GNUC_MINOR__ - 0 >= 2))) || defined(Q_CC_RVCT) # define Q_DECL_DEPRECATED __attribute__ ((__deprecated__)) -#elif defined(Q_CC_MSVC) && (_MSC_VER >= 1300) +#elif defined(Q_CC_MSVC) # define Q_DECL_DEPRECATED __declspec(deprecated) # if defined (Q_CC_INTEL) # define Q_DECL_VARIABLE_DEPRECATED @@ -1050,12 +1111,12 @@ redefine to built-in booleans to make autotests work properly */ #if defined(__i386__) || defined(_WIN32) || defined(_WIN32_WCE) # if defined(Q_CC_GNU) -#if ((100*(__GNUC__ - 0) + 10*(__GNUC_MINOR__ - 0) + __GNUC_PATCHLEVEL__) >= 332) +#if !defined(Q_CC_INTEL) && ((100*(__GNUC__ - 0) + 10*(__GNUC_MINOR__ - 0) + __GNUC_PATCHLEVEL__) >= 332) # define QT_FASTCALL __attribute__((regparm(3))) #else # define QT_FASTCALL #endif -# elif defined(Q_CC_MSVC) && (_MSC_VER > 1300 || defined(Q_CC_INTEL)) +# elif defined(Q_CC_MSVC) # define QT_FASTCALL __fastcall # else # define QT_FASTCALL @@ -1077,7 +1138,7 @@ redefine to built-in booleans to make autotests work properly */ typedef int QNoImplicitBoolCast; -#if defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_MIPS) && (defined(Q_WS_QWS) || defined(Q_OS_WINCE))) || defined(QT_ARCH_SH) || defined(QT_ARCH_SH4A) +#if defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_MIPS) && (defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_WINCE))) || defined(QT_ARCH_SH) || defined(QT_ARCH_SH4A) #define QT_NO_FPU #endif @@ -1098,14 +1159,14 @@ template <typename T> inline T qAbs(const T &t) { return t >= 0 ? t : -t; } inline int qRound(qreal d) -{ return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1); } +{ return d >= qreal(0.0) ? int(d + qreal(0.5)) : int(d - int(d-1) + qreal(0.5)) + int(d-1); } #if defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) || defined(QT_ARCH_SYMBIAN) inline qint64 qRound64(double d) { return d >= 0.0 ? qint64(d + 0.5) : qint64(d - qreal(qint64(d-1)) + 0.5) + qint64(d-1); } #else inline qint64 qRound64(qreal d) -{ return d >= 0.0 ? qint64(d + 0.5) : qint64(d - qreal(qint64(d-1)) + 0.5) + qint64(d-1); } +{ return d >= qreal(0.0) ? qint64(d + qreal(0.5)) : qint64(d - qreal(qint64(d-1)) + qreal(0.5)) + qint64(d-1); } #endif template <typename T> @@ -1174,6 +1235,11 @@ class QDataStream; #define QT_SUPPORTS(FEATURE) (!defined(QT_NO_##FEATURE)) +#if defined(Q_OS_LINUX) && defined(Q_CC_RVCT) +# define Q_DECL_EXPORT __attribute__((visibility("default"))) +# define Q_DECL_IMPORT __attribute__((visibility("default"))) +#endif + #ifndef Q_DECL_EXPORT # if defined(Q_OS_WIN) || defined(Q_CC_NOKIAX86) || defined(Q_CC_RVCT) # define Q_DECL_EXPORT __declspec(dllexport) @@ -1362,15 +1428,22 @@ class QDataStream; # else # define Q_GUI_EXPORT_INLINE inline # endif +# if defined(QT_BUILD_COMPAT_LIB) +# define Q_COMPAT_EXPORT_INLINE Q_COMPAT_EXPORT inline +# else +# define Q_COMPAT_EXPORT_INLINE inline +# endif #elif defined(Q_CC_RVCT) // we force RVCT not to export inlines by passing --visibility_inlines_hidden // so we need to just inline it, rather than exporting and inlining // note: this affects the contents of the DEF files (ie. these functions do not appear) # define Q_CORE_EXPORT_INLINE inline # define Q_GUI_EXPORT_INLINE inline +# define Q_COMPAT_EXPORT_INLINE inline #else # define Q_CORE_EXPORT_INLINE Q_CORE_EXPORT inline # define Q_GUI_EXPORT_INLINE Q_GUI_EXPORT inline +# define Q_COMPAT_EXPORT_INLINE Q_COMPAT_EXPORT inline #endif /* @@ -1388,7 +1461,7 @@ class QDataStream; # define Q_AUTOTEST_EXPORT #endif -inline void qt_noop() {} +inline void qt_noop(void) {} /* These wrap try/catch so we can switch off exceptions later. @@ -1501,6 +1574,7 @@ public: MV_10_4 = 0x0006, MV_10_5 = 0x0007, MV_10_6 = 0x0008, + MV_10_7 = 0x0009, /* codenames */ MV_CHEETAH = MV_10_0, @@ -1509,7 +1583,8 @@ public: MV_PANTHER = MV_10_3, MV_TIGER = MV_10_4, MV_LEOPARD = MV_10_5, - MV_SNOWLEOPARD = MV_10_6 + MV_SNOWLEOPARD = MV_10_6, + MV_LION = MV_10_7 }; static const MacVersion MacintoshVersion; #endif @@ -1610,7 +1685,7 @@ inline void qUnused(T &x) { (void)x; } #endif #ifndef qPrintable -# define qPrintable(string) (string).toLocal8Bit().constData() +# define qPrintable(string) QString(string).toLocal8Bit().constData() #endif Q_CORE_EXPORT void qDebug(const char *, ...) /* print debug message */ @@ -1702,7 +1777,7 @@ Q_CORE_EXPORT void qBadAlloc(); #ifdef QT_NO_EXCEPTIONS # if defined(QT_NO_DEBUG) -# define Q_CHECK_PTR(p) qt_noop(); +# define Q_CHECK_PTR(p) qt_noop() # else # define Q_CHECK_PTR(p) do {if(!(p))qt_check_pointer(__FILE__,__LINE__);} while (0) # endif @@ -1716,12 +1791,7 @@ inline T *q_check_ptr(T *p) { Q_CHECK_PTR(p); return p; } #if (defined(Q_CC_GNU) && !defined(Q_OS_SOLARIS)) || defined(Q_CC_HPACC) || defined(Q_CC_DIAB) # define Q_FUNC_INFO __PRETTY_FUNCTION__ #elif defined(_MSC_VER) - /* MSVC 2002 doesn't have __FUNCSIG__ nor can it handle QT_STRINGIFY. */ -# if _MSC_VER <= 1300 -# define Q_FUNC_INFO __FILE__ "(line number unavailable)" -# else -# define Q_FUNC_INFO __FUNCSIG__ -# endif +# define Q_FUNC_INFO __FUNCSIG__ #else # if defined(Q_OS_SOLARIS) || defined(Q_CC_XLC) || defined(Q_OS_SYMBIAN) # define Q_FUNC_INFO __FILE__ "(line number unavailable)" @@ -1763,32 +1833,32 @@ public: inline ~QGlobalStatic() { pointer = 0; } }; -#define Q_GLOBAL_STATIC(TYPE, NAME) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME; \ - static QGlobalStatic<TYPE > global_##NAME(&this_##NAME); \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC(TYPE, NAME) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable; \ + static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME ARGS; \ - static QGlobalStatic<TYPE > global_##NAME(&this_##NAME); \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable ARGS; \ + static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ - static TYPE *NAME() \ - { \ - static TYPE this_##NAME; \ - static QGlobalStatic<TYPE > global_##NAME(0); \ - if (!global_##NAME.pointer) { \ - TYPE *x = global_##NAME.pointer = &this_##NAME; \ - INITIALIZER; \ - } \ - return global_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ + static TYPE *NAME() \ + { \ + static TYPE thisVariable; \ + static QGlobalStatic<TYPE > thisGlobalStatic(0); \ + if (!thisGlobalStatic.pointer) { \ + TYPE *x = thisGlobalStatic.pointer = &thisVariable; \ + INITIALIZER; \ + } \ + return thisGlobalStatic.pointer; \ } #else @@ -1823,50 +1893,50 @@ public: } }; -#define Q_GLOBAL_STATIC_INIT(TYPE, NAME) \ - static QGlobalStatic<TYPE > this_##NAME = { Q_BASIC_ATOMIC_INITIALIZER(0), false } - -#define Q_GLOBAL_STATIC(TYPE, NAME) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - TYPE *x = new TYPE; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC(TYPE, NAME) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + TYPE *x = new TYPE; \ + if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \ + delete x; \ + else \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + } \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - TYPE *x = new TYPE ARGS; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + TYPE *x = new TYPE ARGS; \ + if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \ + delete x; \ + else \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + } \ + return thisGlobalStatic.pointer; \ } -#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ - Q_GLOBAL_STATIC_INIT(TYPE, NAME); \ - static TYPE *NAME() \ - { \ - if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - QScopedPointer<TYPE > x(new TYPE); \ - INITIALIZER; \ - if (this_##NAME.pointer.testAndSetOrdered(0, x.data())) { \ - static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ - x.take(); \ - } \ - } \ - return this_##NAME.pointer; \ +#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \ + static TYPE *NAME() \ + { \ + static QGlobalStatic<TYPE > thisGlobalStatic \ + = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \ + if (!thisGlobalStatic.pointer && !thisGlobalStatic.destroyed) { \ + QScopedPointer<TYPE > x(new TYPE); \ + INITIALIZER; \ + if (thisGlobalStatic.pointer.testAndSetOrdered(0, x.data())) { \ + static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \ + x.take(); \ + } \ + } \ + return thisGlobalStatic.pointer; \ } #endif @@ -1971,8 +2041,6 @@ static inline bool qIsNull(float f) qIsDetached - data sharing functionality */ -#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION - /* The catch-all template. */ @@ -2005,28 +2073,6 @@ public: }; }; -#else - -template <typename T> char QTypeInfoHelper(T*(*)()); -void* QTypeInfoHelper(...); - -template <typename T> inline bool qIsDetached(T &) { return true; } - -template <typename T> -class QTypeInfo -{ -public: - enum { - isPointer = (1 == sizeof(QTypeInfoHelper((T(*)())0))), - isComplex = !isPointer, - isStatic = !isPointer, - isLarge = (sizeof(T)>sizeof(void*)), - isDummy = false - }; -}; - -#endif /* QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION */ - /* Specialize a specific type with: @@ -2043,8 +2089,7 @@ enum { /* TYPEINFO flags */ Q_DUMMY_TYPE = 0x4 }; -#define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ -template <> \ +#define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \ class QTypeInfo<TYPE > \ { \ public: \ @@ -2058,12 +2103,22 @@ public: \ static inline const char *name() { return #TYPE; } \ } +#define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ +template<> \ +Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) + + template <typename T> inline void qSwap(T &value1, T &value2) { +#ifdef QT_NO_STL const T t = value1; value1 = value2; value2 = t; +#else + using std::swap; + swap(value1, value2); +#endif } /* @@ -2075,12 +2130,23 @@ inline void qSwap(T &value1, T &value2) types must declare a 'bool isDetached(void) const;' member for this to work. */ +#ifdef QT_NO_STL +#define Q_DECLARE_SHARED_STL(TYPE) +#else +#define Q_DECLARE_SHARED_STL(TYPE) \ +QT_END_NAMESPACE \ +namespace std { \ + template<> inline void swap<QT_PREPEND_NAMESPACE(TYPE)>(QT_PREPEND_NAMESPACE(TYPE) &value1, QT_PREPEND_NAMESPACE(TYPE) &value2) \ + { swap(value1.data_ptr(), value2.data_ptr()); } \ +} \ +QT_BEGIN_NAMESPACE +#endif + #define Q_DECLARE_SHARED(TYPE) \ template <> inline bool qIsDetached<TYPE>(TYPE &t) { return t.isDetached(); } \ template <> inline void qSwap<TYPE>(TYPE &value1, TYPE &value2) \ -{ \ - qSwap(value1.data_ptr(), value2.data_ptr()); \ -} +{ qSwap(value1.data_ptr(), value2.data_ptr()); } \ +Q_DECLARE_SHARED_STL(TYPE) /* QTypeInfo primitive specializations @@ -2140,10 +2206,6 @@ Q_CORE_EXPORT void *qMemSet(void *dest, int c, size_t n); # pragma warning(disable: 4231) /* nonstandard extension used : 'extern' before template explicit instantiation */ # pragma warning(disable: 4710) /* function not inlined */ # pragma warning(disable: 4530) /* C++ exception handler used, but unwind semantics are not enabled. Specify -GX */ -# if _MSC_VER < 1300 -# pragma warning(disable: 4284) /* return type for 'type1::operator ->' is 'type2 *' */ - /* (ie; not a UDT or reference to a UDT. Will produce errors if applied using infix notation) */ -# endif # elif defined(Q_CC_BOR) # pragma option -w-inl # pragma option -w-aus @@ -2217,13 +2279,9 @@ public: #define Q_DECLARE_FLAGS(Flags, Enum)\ typedef QFlags<Enum> Flags; -#if defined Q_CC_MSVC && _MSC_VER < 1300 -# define Q_DECLARE_INCOMPATIBLE_FLAGS(Flags) -#else -# define Q_DECLARE_INCOMPATIBLE_FLAGS(Flags) \ +#define Q_DECLARE_INCOMPATIBLE_FLAGS(Flags) \ inline QIncompatibleFlag operator|(Flags::enum_type f1, int f2) \ { return QIncompatibleFlag(int(f1) | f2); } -#endif #define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) \ inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) \ @@ -2280,9 +2338,9 @@ template <typename T> inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) { return static_cast<const QForeachContainer<T> *>(base); } -#if (defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) && !defined(Q_CC_INTEL)) || defined(Q_CC_MIPS) +#if defined(Q_CC_MIPS) /* - Proper for-scoping in VC++6 and MIPSpro CC + Proper for-scoping in MIPSpro CC */ # define Q_FOREACH(variable,container) \ if(0){}else \ @@ -2650,12 +2708,6 @@ QT_LICENSED_MODULE(DBus) # define QT_NO_QFUTURE #endif -// MSVC 6.0 and MSVC .NET 2002, can`t handle the map(), etc templates, -// but the QFuture class compiles. -#if (defined(Q_CC_MSVC) && _MSC_VER <= 1300) -# define QT_NO_CONCURRENT -#endif - // gcc 3 version has problems with some of the // map/filter overloads. #if defined(Q_CC_GNU) && (__GNUC__ < 4) @@ -2674,6 +2726,12 @@ QT_LICENSED_MODULE(DBus) # define QT_NO_PROCESS #endif +#if defined (__ELF__) +# if defined (Q_OS_LINUX) || defined (Q_OS_SOLARIS) || defined (Q_OS_FREEBSD) || defined (Q_OS_OPENBSD) || defined (Q_OS_IRIX) +# define Q_OF_ELF +# endif +#endif + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 1fecc26..aca1cb1 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -529,6 +529,9 @@ void qt_core_boilerplate() #ifdef QT_BUILD_KEY_COMPAT2 "| " QT_BUILD_KEY_COMPAT2 " " #endif +#ifdef QT_BUILD_KEY_COMPAT3 + "| " QT_BUILD_KEY_COMPAT3 " " +#endif "|\n" "Build date: %s\n" "Installation prefix: %s\n" diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 4d70744..f015ed0 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -526,6 +526,8 @@ public: WA_X11DoNotAcceptFocus = 132, + WA_MacNoShadow = 133, + // Add new attributes before this line WA_AttributeCount }; @@ -542,6 +544,7 @@ public: AA_MacDontSwapCtrlAndMeta = 7, AA_S60DontConstructApplicationPanes = 8, AA_S60DisablePartialScreenInputMode = 9, + AA_X11InitThreads = 10, // Add new attributes before this line AA_AttributeCount @@ -1625,6 +1628,7 @@ public: AccessibleDescriptionRole = 12, // More general purpose SizeHintRole = 13, + InitialSortOrderRole = 14, // Internal UiLib roles. Start worrying when public roles go that high. DisplayPropertyRole = 27, DecorationPropertyRole = 28, @@ -1667,7 +1671,7 @@ public: typedef void *HANDLE; #elif defined(Q_WS_X11) typedef unsigned long HANDLE; -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) typedef void * HANDLE; #elif defined(Q_OS_SYMBIAN) typedef unsigned long int HANDLE; // equivalent to TUint32 diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 22ad83b..07a9503 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -163,6 +163,10 @@ is ensured that the focused text widget is visible. This is only supported in Symbian^3. (internal) + \value AA_X11InitThreads Calls XInitThreads() as part of the QApplication + construction in order to make Xlib calls thread-safe. This + attribute must be set before QApplication is constructed. + \omitvalue AA_AttributeCount */ @@ -1213,6 +1217,9 @@ ignoring changes to the display's orientation with respect to the user. \value WA_AutoOrientation Causes the widget to change orientation whenever the display changes orientation with respect to the user. + + \value WA_MacNoShadow Since Qt 4.8, this attribute disables drop shadows + for this top level window. Only affects Cocoa builds of Qt for Mac OS X. \omitvalue WA_SetLayoutDirection \omitvalue WA_InputMethodTransparent @@ -2636,19 +2643,22 @@ Roles describing appearance and meta data (with associated types): - \value FontRole The font used for items rendered with the default - delegate. (QFont) - \value TextAlignmentRole The alignment of the text for items rendered with the - default delegate. (Qt::AlignmentFlag) - \value BackgroundRole The background brush used for items rendered with - the default delegate. (QBrush) - \value BackgroundColorRole This role is obsolete. Use BackgroundRole instead. - \value ForegroundRole The foreground brush (text color, typically) - used for items rendered with the default delegate. - (QBrush) - \value TextColorRole This role is obsolete. Use ForegroundRole instead. - \value CheckStateRole This role is used to obtain the checked state of - an item. (Qt::CheckState) + \value FontRole The font used for items rendered with the default + delegate. (QFont) + \value TextAlignmentRole The alignment of the text for items rendered with the + default delegate. (Qt::AlignmentFlag) + \value BackgroundRole The background brush used for items rendered with + the default delegate. (QBrush) + \value BackgroundColorRole This role is obsolete. Use BackgroundRole instead. + \value ForegroundRole The foreground brush (text color, typically) + used for items rendered with the default delegate. + (QBrush) + \value TextColorRole This role is obsolete. Use ForegroundRole instead. + \value CheckStateRole This role is used to obtain the checked state of + an item. (Qt::CheckState) + \value InitialSortOrderRole This role is used to obtain the initial sort order + of a header view section. (Qt::SortOrder). This + role was introduced in Qt 4.8. Accessibility roles (with associated types): diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 3d964c6..2b61192 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -9,6 +9,7 @@ HEADERS += \ io/qdataurl_p.h \ io/qdebug.h \ io/qdir.h \ + io/qdir_p.h \ io/qdiriterator.h \ io/qfile.h \ io/qfileinfo.h \ @@ -29,7 +30,11 @@ HEADERS += \ io/qfsfileengine_p.h \ io/qfsfileengine_iterator_p.h \ io/qfilesystemwatcher.h \ - io/qfilesystemwatcher_p.h + io/qfilesystemwatcher_p.h \ + io/qfilesystementry_p.h \ + io/qfilesystemengine_p.h \ + io/qfilesystemmetadata_p.h \ + io/qfilesystemiterator_p.h SOURCES += \ io/qabstractfileengine.cpp \ @@ -52,25 +57,35 @@ SOURCES += \ io/qsettings.cpp \ io/qfsfileengine.cpp \ io/qfsfileengine_iterator.cpp \ - io/qfilesystemwatcher.cpp + io/qfilesystemwatcher.cpp \ + io/qfilesystementry.cpp \ + io/qfilesystemengine.cpp win32 { SOURCES += io/qsettings_win.cpp SOURCES += io/qprocess_win.cpp SOURCES += io/qfsfileengine_win.cpp - SOURCES += io/qfsfileengine_iterator_win.cpp SOURCES += io/qfilesystemwatcher_win.cpp HEADERS += io/qfilesystemwatcher_win_p.h HEADERS += io/qwindowspipewriter_p.h SOURCES += io/qwindowspipewriter.cpp + SOURCES += io/qfilesystemengine_win.cpp + SOURCES += io/qfilesystemiterator_win.cpp } else:unix { SOURCES += io/qfsfileengine_unix.cpp - SOURCES += io/qfsfileengine_iterator_unix.cpp - symbian:SOURCES += io/qprocess_symbian.cpp - else:SOURCES += io/qprocess_unix.cpp - macx-*: { + symbian { + SOURCES += io/qfilesystemengine_symbian.cpp + SOURCES += io/qprocess_symbian.cpp + SOURCES += io/qfilesystemiterator_symbian.cpp + } else { + SOURCES += io/qfilesystemengine_unix.cpp + SOURCES += io/qprocess_unix.cpp + SOURCES += io/qfilesystemiterator_unix.cpp + } + !nacl:macx-*: { HEADERS += io/qfilesystemwatcher_fsevents_p.h + SOURCES += io/qfilesystemengine_mac.cpp SOURCES += io/qsettings_mac.cpp io/qfilesystemwatcher_fsevents.cpp } @@ -84,9 +99,11 @@ win32 { io/qfilesystemwatcher_dnotify_p.h } - freebsd-*|macx-*|darwin-*|openbsd-*:{ - SOURCES += io/qfilesystemwatcher_kqueue.cpp - HEADERS += io/qfilesystemwatcher_kqueue_p.h + !nacl { + freebsd-*|macx-*|darwin-*|openbsd-*:{ + SOURCES += io/qfilesystemwatcher_kqueue.cpp + HEADERS += io/qfilesystemwatcher_kqueue_p.h + } } symbian { @@ -96,3 +113,7 @@ win32 { LIBS += -lplatformenv } } +integrity { + SOURCES += io/qfsfileengine_unix.cpp \ + io/qfsfileengine_iterator_unix.cpp +} diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp index 33b97ed..0a423c0 100644 --- a/src/corelib/io/qabstractfileengine.cpp +++ b/src/corelib/io/qabstractfileengine.cpp @@ -41,12 +41,20 @@ #include "qabstractfileengine.h" #include "private/qabstractfileengine_p.h" +#ifdef QT_BUILD_CORE_LIB +#include "private/qresource_p.h" +#endif #include "qdatetime.h" #include "qreadwritelock.h" #include "qvariant.h" // built-in handlers #include "qfsfileengine.h" #include "qdiriterator.h" +#include "qstringbuilder.h" + +#include <QtCore/private/qfilesystementry_p.h> +#include <QtCore/private/qfilesystemmetadata_p.h> +#include <QtCore/private/qfilesystemengine_p.h> QT_BEGIN_NAMESPACE @@ -94,6 +102,8 @@ QT_BEGIN_NAMESPACE \sa QAbstractFileEngine, QAbstractFileEngine::create() */ +static bool qt_file_engine_handlers_in_use = false; + /* All application-wide handlers are stored in this list. The mutex must be acquired to ensure thread safety. @@ -123,6 +133,7 @@ Q_GLOBAL_STATIC(QAbstractFileEngineHandlerList, fileEngineHandlers) QAbstractFileEngineHandler::QAbstractFileEngineHandler() { QWriteLocker locker(fileEngineHandlerMutex()); + qt_file_engine_handlers_in_use = true; fileEngineHandlers()->prepend(this); } @@ -134,8 +145,35 @@ QAbstractFileEngineHandler::~QAbstractFileEngineHandler() { QWriteLocker locker(fileEngineHandlerMutex()); // Remove this handler from the handler list only if the list is valid. - if (!qt_abstractfileenginehandlerlist_shutDown) - fileEngineHandlers()->removeAll(this); + if (!qt_abstractfileenginehandlerlist_shutDown) { + QAbstractFileEngineHandlerList *handlers = fileEngineHandlers(); + handlers->removeOne(this); + if (handlers->isEmpty()) + qt_file_engine_handlers_in_use = false; + } +} + +/* + \ìnternal + + Handles calls to custom file engine handlers. +*/ +QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path) +{ + QAbstractFileEngine *engine = 0; + + if (qt_file_engine_handlers_in_use) { + QReadLocker locker(fileEngineHandlerMutex()); + + // check for registered handlers that can load the file + QAbstractFileEngineHandlerList *handlers = fileEngineHandlers(); + for (int i = 0; i < handlers->size(); i++) { + if ((engine = handlers->at(i)->create(path))) + break; + } + } + + return engine; } /*! @@ -166,42 +204,17 @@ QAbstractFileEngineHandler::~QAbstractFileEngineHandler() */ QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName) { - { - QReadLocker locker(fileEngineHandlerMutex()); - - // check for registered handlers that can load the file - for (int i = 0; i < fileEngineHandlers()->size(); i++) { - if (QAbstractFileEngine *ret = fileEngineHandlers()->at(i)->create(fileName)) - return ret; - } - } - -#ifdef QT_BUILD_CORE_LIB - if (!fileName.startsWith(QLatin1Char('/'))) { - int prefixSeparator = fileName.indexOf(QLatin1Char(':')); - if (prefixSeparator > 1) { - QString prefix = fileName.left(prefixSeparator); - QString fileNameWithoutPrefix = fileName.mid(prefixSeparator + 1).prepend(QLatin1Char('/')); - const QStringList &paths = QDir::searchPaths(prefix); - for (int i = 0; i < paths.count(); i++) { - QString path = paths.at(i); - path.append(fileNameWithoutPrefix); - QAbstractFileEngine *engine = create(path); - if (engine && (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) { - return engine; - } - delete engine; - } - } - } + QFileSystemEntry entry(fileName); + QFileSystemMetaData metaData; + QAbstractFileEngine *engine = QFileSystemEngine::resolveEntryAndCreateLegacyEngine(entry, metaData); + +#ifndef QT_NO_FSFILEENGINE + if (!engine) + // fall back to regular file engine + return new QFSFileEngine(entry.filePath()); #endif -#ifdef QT_NO_FSFILEENGINE - return 0; -#else - // fall back to regular file engine - return new QFSFileEngine(fileName); -#endif + return engine; } /*! diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h index fe2b3cf..d64eaa5 100644 --- a/src/corelib/io/qabstractfileengine_p.h +++ b/src/corelib/io/qabstractfileengine_p.h @@ -74,6 +74,8 @@ public: Q_DECLARE_PUBLIC(QAbstractFileEngine) }; +QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path); + QT_END_NAMESPACE #endif // QABSTRACTFILEENGINE_P_H diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp index b36631b..0361d18 100644 --- a/src/corelib/io/qdatastream.cpp +++ b/src/corelib/io/qdatastream.cpp @@ -223,6 +223,7 @@ QT_BEGIN_NAMESPACE \value ReadPastEnd The data stream has read past the end of the data in the underlying device. \value ReadCorruptData The data stream has read corrupt data. + \value WriteFailed The data stream cannot write to the underlying device. */ /***************************************************************************** @@ -243,6 +244,11 @@ QT_BEGIN_NAMESPACE } #endif +#define CHECK_STREAM_WRITE_PRECOND(retVal) \ + CHECK_STREAM_PRECOND(retVal) \ + if (q_status != Ok) \ + return retVal; + enum { DefaultStreamVersion = QDataStream::Qt_4_6 }; @@ -495,6 +501,9 @@ void QDataStream::resetStatus() /*! Sets the status of the data stream to the \a status given. + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + \sa Status status() resetStatus() */ void QDataStream::setStatus(Status status) @@ -992,8 +1001,9 @@ int QDataStream::readRawData(char *s, int len) QDataStream &QDataStream::operator<<(qint8 i) { - CHECK_STREAM_PRECOND(*this) - dev->putChar(i); + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(i)) + q_status = WriteFailed; return *this; } @@ -1015,11 +1025,12 @@ QDataStream &QDataStream::operator<<(qint8 i) QDataStream &QDataStream::operator<<(qint16 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint16)); + if (dev->write((char *)&i, sizeof(qint16)) != sizeof(qint16)) + q_status = WriteFailed; return *this; } @@ -1032,11 +1043,12 @@ QDataStream &QDataStream::operator<<(qint16 i) QDataStream &QDataStream::operator<<(qint32 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint32)); + if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32)) + q_status = WriteFailed; return *this; } @@ -1057,7 +1069,7 @@ QDataStream &QDataStream::operator<<(qint32 i) QDataStream &QDataStream::operator<<(qint64 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (version() < 6) { quint32 i1 = i & 0xffffffff; quint32 i2 = i >> 32; @@ -1066,7 +1078,8 @@ QDataStream &QDataStream::operator<<(qint64 i) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint64)); + if (dev->write((char *)&i, sizeof(qint64)) != sizeof(qint64)) + q_status = WriteFailed; } return *this; } @@ -1086,8 +1099,9 @@ QDataStream &QDataStream::operator<<(qint64 i) QDataStream &QDataStream::operator<<(bool i) { - CHECK_STREAM_PRECOND(*this) - dev->putChar(qint8(i)); + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(qint8(i))) + q_status = WriteFailed; return *this; } @@ -1108,7 +1122,7 @@ QDataStream &QDataStream::operator<<(float f) return *this; } - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) float g = f; // fixes float-on-stack problem if (!noswap) { union { @@ -1119,7 +1133,8 @@ QDataStream &QDataStream::operator<<(float f) x.val2 = qbswap(x.val2); g = x.val1; } - dev->write((char *)&g, sizeof(float)); + if (dev->write((char *)&g, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; return *this; } @@ -1141,10 +1156,11 @@ QDataStream &QDataStream::operator<<(double f) return *this; } - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) #ifndef Q_DOUBLE_FORMAT if (noswap) { - dev->write((char *)&f, sizeof(double)); + if (dev->write((char *)&f, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; } else { union { double val1; @@ -1152,7 +1168,8 @@ QDataStream &QDataStream::operator<<(double f) } x; x.val1 = f; x.val2 = qbswap(x.val2); - dev->write((char *)&x.val2, sizeof(double)); + if (dev->write((char *)&x.val2, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; } #else union { @@ -1181,7 +1198,8 @@ QDataStream &QDataStream::operator<<(double f) b[Q_DF(1)] = *p++; b[Q_DF(0)] = *p; } - dev->write(b, 8); + if (dev->write(b, 8) != 8) + q_status = WriteFailed; #endif return *this; } @@ -1221,7 +1239,7 @@ QDataStream &QDataStream::operator<<(const char *s) QDataStream &QDataStream::writeBytes(const char *s, uint len) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) *this << (quint32)len; // write length specifier if (len) writeRawData(s, len); @@ -1239,8 +1257,11 @@ QDataStream &QDataStream::writeBytes(const char *s, uint len) int QDataStream::writeRawData(const char *s, int len) { - CHECK_STREAM_PRECOND(-1) - return dev->write(s, len); + CHECK_STREAM_WRITE_PRECOND(-1) + int ret = dev->write(s, len); + if (ret != len) + q_status = WriteFailed; + return ret; } /*! diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index dff172c..d19fcc5 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -85,10 +85,11 @@ public: Qt_4_4 = 10, Qt_4_5 = 11, Qt_4_6 = 12, - Qt_4_7 = Qt_4_6 -#if QT_VERSION >= 0x040800 -#error Add the datastream version for this Qt version + Qt_4_7 = Qt_4_6, Qt_4_8 = Qt_4_7 +#if QT_VERSION >= 0x040900 +#error Add the datastream version for this Qt version + Qt_4_9 = Qt_4_8 #endif }; @@ -100,7 +101,8 @@ public: enum Status { Ok, ReadPastEnd, - ReadCorruptData + ReadCorruptData, + WriteFailed }; enum FloatingPointPrecision { diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index 87d2a61..f463b75 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -137,10 +137,8 @@ public: inline QNoDebug &nospace() { return *this; } inline QNoDebug &maybeSpace() { return *this; } -#ifndef QT_NO_MEMBER_TEMPLATES template<typename T> inline QNoDebug &operator<<(const T &) { return *this; } -#endif }; Q_CORE_EXPORT_INLINE QDebug qCritical() { return QDebug(QtCriticalMsg); } @@ -285,11 +283,6 @@ Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); } inline QNoDebug qDebug() { return QNoDebug(); } #define qDebug QT_NO_QDEBUG_MACRO -#ifdef QT_NO_MEMBER_TEMPLATES -template<typename T> -inline QNoDebug operator<<(QNoDebug debug, const T &) { return debug; } -#endif - #endif #if !defined(QT_NO_WARNING_OUTPUT) diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index c589dfe..166513a 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -41,6 +41,7 @@ #include "qplatformdefs.h" #include "qdir.h" +#include "qdir_p.h" #include "qabstractfileengine.h" #ifndef QT_NO_DEBUG_STREAM #include "qdebug.h" @@ -53,6 +54,10 @@ #include "qvector.h" #include "qalgorithms.h" #include "qvarlengtharray.h" +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" +#include "qfilesystemengine_p.h" +#include <qstringbuilder.h> #ifdef QT_BUILD_CORE_LIB # include "qresource.h" @@ -81,129 +86,130 @@ static QString driveSpec(const QString &path) } //************* QDirPrivate -class QDirPrivate - : public QSharedData -{ -public: - QDirPrivate(const QString &path, - const QStringList &nameFilters_ = QStringList(), - QDir::SortFlags sort_ = QDir::SortFlags(QDir::Name | QDir::IgnoreCase), - QDir::Filters filters_ = QDir::AllEntries) - : QSharedData() - , nameFilters(nameFilters_) - , sort(sort_) - , filters(filters_) +QDirPrivate::QDirPrivate(const QString &path, const QStringList &nameFilters_, QDir::SortFlags sort_, QDir::Filters filters_) + : QSharedData() + , nameFilters(nameFilters_) + , sort(sort_) + , filters(filters_) #ifdef QT3_SUPPORT - , filterSepChar(0) - , matchAllDirs(false) + , filterSepChar(0) + , matchAllDirs(false) #endif - , fileListsInitialized(false) - { - setPath(path.isEmpty() ? QString::fromLatin1(".") : path); - - bool empty = nameFilters.isEmpty(); - if (!empty) { - empty = true; - for (int i = 0; i < nameFilters.size(); ++i) { - if (!nameFilters.at(i).isEmpty()) { - empty = false; - break; - } + , fileListsInitialized(false) +{ + setPath(path.isEmpty() ? QString::fromLatin1(".") : path); + + bool empty = nameFilters.isEmpty(); + if (!empty) { + empty = true; + for (int i = 0; i < nameFilters.size(); ++i) { + if (!nameFilters.at(i).isEmpty()) { + empty = false; + break; } } - if (empty) - nameFilters = QStringList(QString::fromLatin1("*")); } + if (empty) + nameFilters = QStringList(QString::fromLatin1("*")); +} - QDirPrivate(const QDirPrivate ©) - : QSharedData(copy) - , path(copy.path) - , nameFilters(copy.nameFilters) - , sort(copy.sort) - , filters(copy.filters) +QDirPrivate::QDirPrivate(const QDirPrivate ©) + : QSharedData(copy) + , nameFilters(copy.nameFilters) + , sort(copy.sort) + , filters(copy.filters) #ifdef QT3_SUPPORT - , filterSepChar(copy.filterSepChar) - , matchAllDirs(copy.matchAllDirs) + , filterSepChar(copy.filterSepChar) + , matchAllDirs(copy.matchAllDirs) #endif - , fileListsInitialized(false) - { - } + , fileListsInitialized(false) + , dirEntry(copy.dirEntry) + , metaData(copy.metaData) +{ +} - bool exists() const - { - const QAbstractFileEngine::FileFlags info = - fileEngine->fileFlags(QAbstractFileEngine::DirectoryType - | QAbstractFileEngine::ExistsFlag - | QAbstractFileEngine::Refresh); - if (!(info & QAbstractFileEngine::DirectoryType)) - return false; - return info & QAbstractFileEngine::ExistsFlag; +bool QDirPrivate::exists() const +{ + if (fileEngine.isNull()) { + QFileSystemEngine::fillMetaData(dirEntry, metaData, + QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType); // always stat + return metaData.exists() && metaData.isDirectory(); } + const QAbstractFileEngine::FileFlags info = + fileEngine->fileFlags(QAbstractFileEngine::DirectoryType + | QAbstractFileEngine::ExistsFlag + | QAbstractFileEngine::Refresh); + if (!(info & QAbstractFileEngine::DirectoryType)) + return false; + return info & QAbstractFileEngine::ExistsFlag; +} - void initFileEngine(); - void initFileLists() const; - - static void sortFileList(QDir::SortFlags, QFileInfoList &, QStringList *, QFileInfoList *); - - static inline QChar getFilterSepChar(const QString &nameFilter) - { - QChar sep(QLatin1Char(';')); - int i = nameFilter.indexOf(sep, 0); - if (i == -1 && nameFilter.indexOf(QLatin1Char(' '), 0) != -1) - sep = QChar(QLatin1Char(' ')); - return sep; - } +// static +inline QChar QDirPrivate::getFilterSepChar(const QString &nameFilter) +{ + QChar sep(QLatin1Char(';')); + int i = nameFilter.indexOf(sep, 0); + if (i == -1 && nameFilter.indexOf(QLatin1Char(' '), 0) != -1) + sep = QChar(QLatin1Char(' ')); + return sep; +} - static inline QStringList splitFilters(const QString &nameFilter, QChar sep = 0) - { - if (sep == 0) - sep = getFilterSepChar(nameFilter); - QStringList ret = nameFilter.split(sep); - for (int i = 0; i < ret.count(); ++i) - ret[i] = ret[i].trimmed(); - return ret; - } +// static +inline QStringList QDirPrivate::splitFilters(const QString &nameFilter, QChar sep) +{ + if (sep == 0) + sep = getFilterSepChar(nameFilter); + QStringList ret = nameFilter.split(sep); + for (int i = 0; i < ret.count(); ++i) + ret[i] = ret[i].trimmed(); + return ret; +} - inline void setPath(QString p) - { - if ((p.endsWith(QLatin1Char('/')) || p.endsWith(QLatin1Char('\\'))) - && p.length() > 1) { +inline void QDirPrivate::setPath(const QString &path) +{ + QString p = QDir::fromNativeSeparators(path); + if (p.endsWith(QLatin1Char('/')) + && p.length() > 1 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) - if (!(p.length() == 3 && p.at(1) == QLatin1Char(':'))) + && (!(p.length() == 3 && p.at(1).unicode() == ':' && p.at(0).isLetter())) #endif - p.truncate(p.length() - 1); - } - - path = p; - initFileEngine(); - - // set the path to be the qt friendly version so then we can operate on it using just / - path = fileEngine->fileName(QAbstractFileEngine::DefaultName); - clearFileLists(); + ) { + p.truncate(p.length() - 1); } - inline void clearFileLists() { - fileListsInitialized = false; - files.clear(); - fileInfos.clear(); - } + dirEntry = QFileSystemEntry(p, QFileSystemEntry::FromInternalPath()); + metaData.clear(); + initFileEngine(); + clearFileLists(); + absoluteDirEntry = QFileSystemEntry(); +} - QString path; - QStringList nameFilters; - QDir::SortFlags sort; - QDir::Filters filters; +inline void QDirPrivate::clearFileLists() +{ + fileListsInitialized = false; + files.clear(); + fileInfos.clear(); +} -#ifdef QT3_SUPPORT - QChar filterSepChar; - bool matchAllDirs; -#endif +inline void QDirPrivate::resolveAbsoluteEntry() const +{ + if (!absoluteDirEntry.isEmpty() || dirEntry.isEmpty()) + return; + + QString absoluteName; + if (fileEngine.isNull()) { + if (!dirEntry.isRelative()) { + absoluteDirEntry = dirEntry; + return; + } - QScopedPointer<QAbstractFileEngine> fileEngine; + absoluteName = QFileSystemEngine::absoluteName(dirEntry).filePath(); + } else { + absoluteName = fileEngine->fileName(QAbstractFileEngine::AbsoluteName); + } - mutable bool fileListsInitialized; - mutable QStringList files; - mutable QFileInfoList fileInfos; -}; + absoluteDirEntry = QFileSystemEntry(QDir::cleanPath(absoluteName), QFileSystemEntry::FromInternalPath()); +} /* For sorting */ struct QDirSortItem @@ -315,12 +321,11 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, } } } - -inline void QDirPrivate::initFileLists() const +inline void QDirPrivate::initFileLists(const QDir &dir) const { if (!fileListsInitialized) { QFileInfoList l; - QDirIterator it(path, nameFilters, filters); + QDirIterator it(dir); while (it.hasNext()) { it.next(); l.append(it.fileInfo()); @@ -332,7 +337,7 @@ inline void QDirPrivate::initFileLists() const inline void QDirPrivate::initFileEngine() { - fileEngine.reset(QAbstractFileEngine::create(path)); + fileEngine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(dirEntry, metaData)); } /*! @@ -597,7 +602,7 @@ void QDir::setPath(const QString &path) QString QDir::path() const { const QDirPrivate* d = d_ptr.constData(); - return d->path; + return d->dirEntry.filePath(); } /*! @@ -611,10 +616,8 @@ QString QDir::path() const QString QDir::absolutePath() const { const QDirPrivate* d = d_ptr.constData(); - QString ret = d->path; - if (QDir::isRelativePath(ret)) - ret = absoluteFilePath(QString::fromLatin1("")); - return cleanPath(ret); + d->resolveAbsoluteEntry(); + return d->absoluteDirEntry.filePath(); } /*! @@ -635,7 +638,12 @@ QString QDir::absolutePath() const */ QString QDir::canonicalPath() const { - return cleanPath(d_ptr->fileEngine->fileName(QAbstractFileEngine::CanonicalName)); + const QDirPrivate* d = d_ptr.constData(); + if (d->fileEngine.isNull()) { + QFileSystemEntry answer = QFileSystemEngine::canonicalName(d->dirEntry, d->metaData); + return answer.filePath(); + } + return d->fileEngine->fileName(QAbstractFileEngine::CanonicalName); } /*! @@ -652,10 +660,7 @@ QString QDir::canonicalPath() const QString QDir::dirName() const { const QDirPrivate* d = d_ptr.constData(); - int pos = d->path.lastIndexOf(QLatin1Char('/')); - if (pos == -1) - return d->path; - return d->path.mid(pos + 1); + return d->dirEntry.fileName(); } /*! @@ -673,7 +678,7 @@ QString QDir::filePath(const QString &fileName) const if (isAbsolutePath(fileName)) return QString(fileName); - QString ret = d->path; + QString ret = d->dirEntry.filePath(); if (!fileName.isEmpty()) { if (!ret.isEmpty() && ret[(int)ret.length()-1] != QLatin1Char('/') && fileName[0] != QLatin1Char('/')) ret += QLatin1Char('/'); @@ -696,22 +701,12 @@ QString QDir::absoluteFilePath(const QString &fileName) const if (isAbsolutePath(fileName)) return fileName; - QString ret; -#ifndef QT_NO_FSFILEENGINE - if (isRelativePath(d->path)) //get pwd - ret = QFSFileEngine::currentPath(fileName); -#endif - if (!d->path.isEmpty() && d->path != QLatin1String(".")) { - if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) - ret += QLatin1Char('/'); - ret += d->path; - } - if (!fileName.isEmpty()) { - if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) - ret += QLatin1Char('/'); - ret += fileName; - } - return ret; + d->resolveAbsoluteEntry(); + if (fileName.isEmpty()) + return d->absoluteDirEntry.filePath(); + if (!d->absoluteDirEntry.isRoot()) + return d->absoluteDirEntry.filePath() % QLatin1Char('/') % fileName; + return d->absoluteDirEntry.filePath() % fileName; } /*! @@ -723,7 +718,7 @@ QString QDir::absoluteFilePath(const QString &fileName) const */ QString QDir::relativeFilePath(const QString &fileName) const { - QString dir = absolutePath(); + QString dir = cleanPath(absolutePath()); QString file = cleanPath(fileName); if (isRelativePath(file) || isRelativePath(dir)) @@ -778,6 +773,7 @@ QString QDir::relativeFilePath(const QString &fileName) const return result; } +#ifndef QT_NO_DEPRECATED /*! \obsolete @@ -787,6 +783,7 @@ QString QDir::convertSeparators(const QString &pathName) { return toNativeSeparators(pathName); } +#endif /*! \since 4.2 @@ -857,21 +854,22 @@ bool QDir::cd(const QString &dirName) if (dirName.isEmpty() || dirName == QLatin1String(".")) return true; - QString newPath = d->path; + QString newPath; if (isAbsolutePath(dirName)) { newPath = cleanPath(dirName); } else { if (isRoot()) { if (dirName == QLatin1String("..")) return false; + newPath = d->dirEntry.filePath(); } else { - newPath += QLatin1Char('/'); + newPath = d->dirEntry.filePath() % QLatin1Char('/'); } newPath += dirName; if (dirName.indexOf(QLatin1Char('/')) >= 0 - || d->path == QLatin1String(".") - || dirName == QLatin1String("..")) { + || dirName == QLatin1String("..") + || d->dirEntry.filePath() == QLatin1String(".")) { newPath = cleanPath(newPath); /* If newPath starts with .., we convert it to absolute to @@ -889,7 +887,6 @@ bool QDir::cd(const QString &dirName) QScopedPointer<QDirPrivate> dir(new QDirPrivate(*d_ptr.constData())); dir->setPath(newPath); - if (!dir->exists()) return false; @@ -1202,7 +1199,7 @@ void QDir::setSorting(SortFlags sort) uint QDir::count() const { const QDirPrivate* d = d_ptr.constData(); - d->initFileLists(); + d->initFileLists(*this); return d->files.count(); } @@ -1216,7 +1213,7 @@ uint QDir::count() const QString QDir::operator[](int pos) const { const QDirPrivate* d = d_ptr.constData(); - d->initFileLists(); + d->initFileLists(*this); return d->files[pos]; } @@ -1299,12 +1296,12 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, sort = d->sort; if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) { - d->initFileLists(); + d->initFileLists(*this); return d->files; } QFileInfoList l; - QDirIterator it(d->path, nameFilters, filters); + QDirIterator it(d->dirEntry.filePath(), nameFilters, filters); while (it.hasNext()) { it.next(); l.append(it.fileInfo()); @@ -1345,12 +1342,12 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter sort = d->sort; if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) { - d->initFileLists(); + d->initFileLists(*this); return d->fileInfos; } QFileInfoList l; - QDirIterator it(d->path, nameFilters, filters); + QDirIterator it(d->dirEntry.filePath(), nameFilters, filters); while (it.hasNext()) { it.next(); l.append(it.fileInfo()); @@ -1365,8 +1362,11 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter Returns true on success; otherwise returns false. + If the directory already exists when this function is called, it will return false. + \sa rmdir() */ +// ### Qt5: behaviour when directory already exists should be made consistent for mkdir and mkpath bool QDir::mkdir(const QString &dirName) const { const QDirPrivate* d = d_ptr.constData(); @@ -1377,6 +1377,8 @@ bool QDir::mkdir(const QString &dirName) const } QString fn = filePath(dirName); + if (d->fileEngine.isNull()) + return QFileSystemEngine::createDirectory(QFileSystemEntry(fn), false); return d->fileEngine->mkdir(fn, false); } @@ -1399,6 +1401,9 @@ bool QDir::rmdir(const QString &dirName) const } QString fn = filePath(dirName); + if (d->fileEngine.isNull()) + return QFileSystemEngine::removeDirectory(QFileSystemEntry(fn), false); + return d->fileEngine->rmdir(fn, false); } @@ -1410,8 +1415,11 @@ bool QDir::rmdir(const QString &dirName) const Returns true if successful; otherwise returns false. + If the path already exists when this function is called, it will return true. + \sa rmpath() */ +// ### Qt5: behaviour when directory already exists should be made consistent for mkdir and mkpath bool QDir::mkpath(const QString &dirPath) const { const QDirPrivate* d = d_ptr.constData(); @@ -1422,6 +1430,8 @@ bool QDir::mkpath(const QString &dirPath) const } QString fn = filePath(dirPath); + if (d->fileEngine.isNull()) + return QFileSystemEngine::createDirectory(QFileSystemEntry(fn), true); return d->fileEngine->mkdir(fn, true); } @@ -1446,6 +1456,8 @@ bool QDir::rmpath(const QString &dirPath) const } QString fn = filePath(dirPath); + if (d->fileEngine.isNull()) + return QFileSystemEngine::removeDirectory(QFileSystemEntry(fn), true); return d->fileEngine->rmdir(fn, true); } @@ -1462,6 +1474,13 @@ bool QDir::isReadable() const { const QDirPrivate* d = d_ptr.constData(); + if (d->fileEngine.isNull()) { + if (!d->metaData.hasFlags(QFileSystemMetaData::UserReadPermission)) + QFileSystemEngine::fillMetaData(d->dirEntry, d->metaData, QFileSystemMetaData::UserReadPermission); + + return (d->metaData.permissions() & QFile::ReadUser) != 0; + } + const QAbstractFileEngine::FileFlags info = d->fileEngine->fileFlags(QAbstractFileEngine::DirectoryType | QAbstractFileEngine::PermsMask); @@ -1500,6 +1519,8 @@ bool QDir::exists() const */ bool QDir::isRoot() const { + if (d_ptr->fileEngine.isNull()) + return d_ptr->dirEntry.isRoot(); return d_ptr->fileEngine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::RootFlag; } @@ -1530,6 +1551,8 @@ bool QDir::isRoot() const */ bool QDir::isRelative() const { + if (d_ptr->fileEngine.isNull()) + return d_ptr->dirEntry.isRelative(); return d_ptr->fileEngine->isRelativePath(); } @@ -1541,20 +1564,23 @@ bool QDir::isRelative() const \sa isAbsolute() isAbsolutePath() isRelative() cleanPath() */ -bool QDir::makeAbsolute() // ### What do the return values signify? +bool QDir::makeAbsolute() { - QString absolutePath = d_ptr.constData()->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); - if (QDir::isRelativePath(absolutePath)) - return false; - - QScopedPointer<QDirPrivate> dir(new QDirPrivate(*d_ptr.constData())); - dir->setPath(absolutePath); - - d_ptr = dir.take(); - - if (!(d_ptr->fileEngine->fileFlags(QAbstractFileEngine::TypesMask) & QAbstractFileEngine::DirectoryType)) - return false; + const QDirPrivate *d = d_ptr.constData(); + QScopedPointer<QDirPrivate> dir; + if (!d->fileEngine.isNull()) { + QString absolutePath = d->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); + if (QDir::isRelativePath(absolutePath)) + return false; + dir.reset(new QDirPrivate(*d_ptr.constData())); + dir->setPath(absolutePath); + } else { // native FS + d->resolveAbsoluteEntry(); + dir.reset(new QDirPrivate(*d_ptr.constData())); + dir->setPath(d->absoluteDirEntry.filePath()); + } + d_ptr = dir.take(); // actually detach return true; } @@ -1574,17 +1600,24 @@ bool QDir::operator==(const QDir &dir) const if (d == other) return true; - if (d->fileEngine->caseSensitive() != other->fileEngine->caseSensitive()) - return false; + Qt::CaseSensitivity sensitive; + if (d->fileEngine.isNull() || other->fileEngine.isNull()) { + if (d->fileEngine.data() != other->fileEngine.data()) // one is native, the other is a custom file-engine + return false; + + sensitive = QFileSystemEngine::isCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; + } else { + if (d->fileEngine->caseSensitive() != other->fileEngine->caseSensitive()) + return false; + sensitive = d->fileEngine->caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; + } + if (d->filters == other->filters && d->sort == other->sort && d->nameFilters == other->nameFilters) { - QString dir1 = absolutePath(), dir2 = dir.absolutePath(); - if (!other->fileEngine->caseSensitive()) - return (dir1.toLower() == dir2.toLower()); - - return (dir1 == dir2); - + d->resolveAbsoluteEntry(); + other->resolveAbsoluteEntry(); + return d->absoluteDirEntry.filePath().compare(other->absoluteDirEntry.filePath(), sensitive) == 0; } return false; } @@ -1733,12 +1766,7 @@ QChar QDir::separator() */ bool QDir::setCurrent(const QString &path) { -#ifdef QT_NO_FSFILEENGINE - Q_UNUSED(path); - return false; -#else - return QFSFileEngine::setCurrentPath(path); -#endif + return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); } /*! @@ -1759,11 +1787,7 @@ bool QDir::setCurrent(const QString &path) */ QString QDir::currentPath() { -#ifdef QT_NO_FSFILEENGINE - return QString(); -#else - return QFSFileEngine::currentPath(); -#endif + return QFileSystemEngine::currentPath().filePath(); } /*! @@ -1821,11 +1845,7 @@ QString QDir::currentPath() */ QString QDir::homePath() { -#ifdef QT_NO_FSFILEENGINE - return QString(); -#else - return cleanPath(QFSFileEngine::homePath()); -#endif + return QFileSystemEngine::homePath(); } /*! @@ -1854,7 +1874,8 @@ QString QDir::homePath() /*! Returns the absolute path of the system's temporary directory. - On Unix/Linux systems this is usually \c{/tmp}; on Windows this is + On Unix/Linux systems this is the path in the \c TMPDIR environment + variable or \c{/tmp} if \c TMPDIR is not defined. On Windows this is usually the path in the \c TEMP or \c TMP environment variable. Whether a directory separator is added to the end or not, depends on the operating system. @@ -1863,11 +1884,7 @@ QString QDir::homePath() */ QString QDir::tempPath() { -#ifdef QT_NO_FSFILEENGINE - return QString(); -#else - return cleanPath(QFSFileEngine::tempPath()); -#endif + return QFileSystemEngine::tempPath(); } /*! @@ -1894,11 +1911,7 @@ QString QDir::tempPath() */ QString QDir::rootPath() { -#ifdef QT_NO_FSFILEENGINE - return QString(); -#else - return QFSFileEngine::rootPath(); -#endif + return QFileSystemEngine::rootPath(); } /*! @@ -2095,6 +2108,7 @@ bool QDir::isRelativePath(const QString &path) void QDir::refresh() const { QDirPrivate *d = const_cast<QDir*>(this)->d_ptr.data(); + d->metaData.clear(); d->initFileEngine(); d->clearFileLists(); } diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h index e090b18..1804940 100644 --- a/src/corelib/io/qdir.h +++ b/src/corelib/io/qdir.h @@ -128,6 +128,10 @@ public: QDir &operator=(const QDir &); QDir &operator=(const QString &path); +#ifdef Q_COMPILER_RVALUE_REFS + inline QDir &operator=(QDir &&other) + { qSwap(d_ptr, other.d_ptr); return *this; } +#endif void setPath(const QString &path); QString path() const; diff --git a/src/corelib/io/qdir_p.h b/src/corelib/io/qdir_p.h new file mode 100644 index 0000000..7f77a84 --- /dev/null +++ b/src/corelib/io/qdir_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIR_PRIVATE_H +#define QDIR_PRIVATE_H + +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" + +QT_BEGIN_NAMESPACE + +class QDirPrivate : public QSharedData +{ +public: + QDirPrivate(const QString &path, const QStringList &nameFilters_ = QStringList(), + QDir::SortFlags sort_ = QDir::SortFlags(QDir::Name | QDir::IgnoreCase), + QDir::Filters filters_ = QDir::AllEntries); + + QDirPrivate(const QDirPrivate ©); + + bool exists() const; + + void initFileEngine(); + void initFileLists(const QDir &dir) const; + + static void sortFileList(QDir::SortFlags, QFileInfoList &, QStringList *, QFileInfoList *); + + static inline QChar getFilterSepChar(const QString &nameFilter); + + static inline QStringList splitFilters(const QString &nameFilter, QChar sep = 0); + + inline void setPath(const QString &path); + + inline void clearFileLists(); + + inline void resolveAbsoluteEntry() const; + + QStringList nameFilters; + QDir::SortFlags sort; + QDir::Filters filters; + +#ifdef QT3_SUPPORT + QChar filterSepChar; + bool matchAllDirs; +#endif + + QScopedPointer<QAbstractFileEngine> fileEngine; + + mutable bool fileListsInitialized; + mutable QStringList files; + mutable QFileInfoList fileInfos; + + QFileSystemEntry dirEntry; + mutable QFileSystemEntry absoluteDirEntry; + mutable QFileSystemMetaData metaData; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/io/qdiriterator.cpp b/src/corelib/io/qdiriterator.cpp index 3b4adc3..c7e56bd 100644 --- a/src/corelib/io/qdiriterator.cpp +++ b/src/corelib/io/qdiriterator.cpp @@ -90,6 +90,7 @@ */ #include "qdiriterator.h" +#include "qdir_p.h" #include "qabstractfileengine.h" @@ -97,36 +98,41 @@ #include <QtCore/qstack.h> #include <QtCore/qvariant.h> +#include <QtCore/private/qfilesystemiterator_p.h> +#include <QtCore/private/qfilesystementry_p.h> +#include <QtCore/private/qfilesystemmetadata_p.h> +#include <QtCore/private/qfilesystemengine_p.h> +#include <QtCore/qfsfileengine.h> +#include <QtCore/private/qfileinfo_p.h> + QT_BEGIN_NAMESPACE -class QDirIteratorPrivateIteratorStack : public QStack<QAbstractFileEngineIterator *> +template <class Iterator> +class QDirIteratorPrivateIteratorStack : public QStack<Iterator *> { public: - ~QDirIteratorPrivateIteratorStack(); + ~QDirIteratorPrivateIteratorStack() + { + qDeleteAll(*this); + } }; -QDirIteratorPrivateIteratorStack::~QDirIteratorPrivateIteratorStack() -{ - qDeleteAll(*this); -} - - class QDirIteratorPrivate { public: - QDirIteratorPrivate(const QString &path, const QStringList &nameFilters, - QDir::Filters filters, QDirIterator::IteratorFlags flags); - ~QDirIteratorPrivate(); + QDirIteratorPrivate(const QFileSystemEntry &entry, const QStringList &nameFilters, + QDir::Filters filters, QDirIterator::IteratorFlags flags, bool resolveEngine = true); void advance(); + bool entryMatches(const QString & fileName, const QFileInfo &fileInfo); void pushDirectory(const QFileInfo &fileInfo); void checkAndPushDirectory(const QFileInfo &); bool matchesFilters(const QString &fileName, const QFileInfo &fi) const; QScopedPointer<QAbstractFileEngine> engine; - const QString path; + QFileSystemEntry dirEntry; const QStringList nameFilters; const QDir::Filters filters; const QDirIterator::IteratorFlags iteratorFlags; @@ -135,23 +141,24 @@ public: QVector<QRegExp> nameRegExps; #endif - QDirIteratorPrivateIteratorStack fileEngineIterators; + QDirIteratorPrivateIteratorStack<QAbstractFileEngineIterator> fileEngineIterators; +#ifndef QT_NO_FILESYSTEMITERATOR + QDirIteratorPrivateIteratorStack<QFileSystemIterator> nativeIterators; +#endif + QFileInfo currentFileInfo; QFileInfo nextFileInfo; // Loop protection QSet<QString> visitedLinks; - - QDirIterator *q; }; /*! \internal */ -QDirIteratorPrivate::QDirIteratorPrivate(const QString &path, const QStringList &nameFilters, - QDir::Filters filters, QDirIterator::IteratorFlags flags) - : engine(QAbstractFileEngine::create(path)) - , path(path) +QDirIteratorPrivate::QDirIteratorPrivate(const QFileSystemEntry &entry, const QStringList &nameFilters, + QDir::Filters filters, QDirIterator::IteratorFlags flags, bool resolveEngine) + : dirEntry(entry) , nameFilters(nameFilters.contains(QLatin1String("*")) ? QStringList() : nameFilters) , filters(QDir::NoFilter == filters ? QDir::AllEntries : filters) , iteratorFlags(flags) @@ -164,22 +171,19 @@ QDirIteratorPrivate::QDirIteratorPrivate(const QString &path, const QStringList (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard)); #endif + QFileSystemMetaData metaData; + if (resolveEngine) + engine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(dirEntry, metaData)); + QFileInfo fileInfo(new QFileInfoPrivate(dirEntry, metaData)); // Populate fields for hasNext() and next() - pushDirectory(QFileInfo(path)); + pushDirectory(fileInfo); advance(); } /*! \internal */ -QDirIteratorPrivate::~QDirIteratorPrivate() -{ -} - -/*! - \internal -*/ void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo) { QString path = fileInfo.filePath(); @@ -201,34 +205,67 @@ void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo) } else { // No iterator; no entry list. } + } else { +#ifndef QT_NO_FILESYSTEMITERATOR + QFileSystemIterator *it = new QFileSystemIterator(fileInfo.d_ptr->fileEntry, + filters, nameFilters, iteratorFlags); + nativeIterators << it; +#endif } } -/*! - \internal -*/ -void QDirIteratorPrivate::advance() +inline bool QDirIteratorPrivate::entryMatches(const QString & fileName, const QFileInfo &fileInfo) { - while (!fileEngineIterators.isEmpty()) { + checkAndPushDirectory(fileInfo); - // Find the next valid iterator that matches the filters. - while (fileEngineIterators.top()->hasNext()) { - QAbstractFileEngineIterator *it = fileEngineIterators.top(); - it->next(); + if (matchesFilters(fileName, fileInfo)) { + currentFileInfo = nextFileInfo; + nextFileInfo = fileInfo; - const QFileInfo info = it->currentFileInfo(); - checkAndPushDirectory(it->currentFileInfo()); + //We found a matching entry. + return true; + } - if (matchesFilters(it->currentFileName(), info)) { - currentFileInfo = nextFileInfo; - nextFileInfo = info; + return false; +} - //We found a matching entry. - return; +/*! + \internal +*/ +void QDirIteratorPrivate::advance() +{ + if (engine) { + while (!fileEngineIterators.isEmpty()) { + // Find the next valid iterator that matches the filters. + QAbstractFileEngineIterator *it; + while (it = fileEngineIterators.top(), it->hasNext()) { + it->next(); + if (entryMatches(it->currentFileName(), it->currentFileInfo())) + return; } + + fileEngineIterators.pop(); + delete it; } + } else { +#ifndef QT_NO_FILESYSTEMITERATOR + QFileSystemEntry nextEntry; + QFileSystemMetaData nextMetaData; + + while (!nativeIterators.isEmpty()) { + // Find the next valid iterator that matches the filters. + QFileSystemIterator *it; + while (it = nativeIterators.top(), it->advance(nextEntry, nextMetaData)) { + QFileInfo info(new QFileInfoPrivate(nextEntry, nextMetaData)); + + if (entryMatches(nextEntry.fileName(), info)) + return; + } - delete fileEngineIterators.pop(); + nativeIterators.pop(); + delete it; + } +#endif } currentFileInfo = nextFileInfo; @@ -262,7 +299,8 @@ void QDirIteratorPrivate::checkAndPushDirectory(const QFileInfo &fileInfo) return; // Stop link loops - if (visitedLinks.contains(fileInfo.canonicalFilePath())) + if (!visitedLinks.isEmpty() && + visitedLinks.contains(fileInfo.canonicalFilePath())) return; pushDirectory(fileInfo); @@ -373,9 +411,11 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf \sa hasNext(), next(), IteratorFlags */ QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags) - : d(new QDirIteratorPrivate(dir.path(), dir.nameFilters(), dir.filter(), flags)) { - d->q = this; + // little trick to get hold of the QDirPrivate while there is no API on QDir to give it to us + class MyQDir : public QDir { public: const QDirPrivate *priv() const { return d_ptr.constData(); } }; + const QDirPrivate *other = static_cast<const MyQDir*>(&dir)->priv(); + d.reset(new QDirIteratorPrivate(other->dirEntry, other->nameFilters, other->filters, flags, !other->fileEngine.isNull())); } /*! @@ -392,9 +432,8 @@ QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags) \sa hasNext(), next(), IteratorFlags */ QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags) - : d(new QDirIteratorPrivate(path, QStringList(), filters, flags)) + : d(new QDirIteratorPrivate(QFileSystemEntry(path), QStringList(), filters, flags)) { - d->q = this; } /*! @@ -410,9 +449,8 @@ QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorF \sa hasNext(), next(), IteratorFlags */ QDirIterator::QDirIterator(const QString &path, IteratorFlags flags) - : d(new QDirIteratorPrivate(path, QStringList(), QDir::NoFilter, flags)) + : d(new QDirIteratorPrivate(QFileSystemEntry(path), QStringList(), QDir::NoFilter, flags)) { - d->q = this; } /*! @@ -430,9 +468,8 @@ QDirIterator::QDirIterator(const QString &path, IteratorFlags flags) */ QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters, QDir::Filters filters, IteratorFlags flags) - : d(new QDirIteratorPrivate(path, nameFilters, filters, flags)) + : d(new QDirIteratorPrivate(QFileSystemEntry(path), nameFilters, filters, flags)) { - d->q = this; } /*! @@ -466,7 +503,14 @@ QString QDirIterator::next() */ bool QDirIterator::hasNext() const { - return !d->fileEngineIterators.isEmpty(); + if (d->engine) + return !d->fileEngineIterators.isEmpty(); + else +#ifndef QT_NO_FILESYSTEMITERATOR + return !d->nativeIterators.isEmpty(); +#else + return false; +#endif } /*! @@ -509,7 +553,7 @@ QFileInfo QDirIterator::fileInfo() const */ QString QDirIterator::path() const { - return d->path; + return d->dirEntry.filePath(); } QT_END_NAMESPACE diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index d0552b6..0ade573 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -48,6 +48,7 @@ #include "qfileinfo.h" #include "private/qiodevice_p.h" #include "private/qfile_p.h" +#include "private/qsystemerror_p.h" #if defined(QT_BUILD_CORE_LIB) # include "qcoreapplication.h" #endif @@ -102,7 +103,7 @@ QFilePrivate::~QFilePrivate() } bool -QFilePrivate::openExternalFile(int flags, int fd) +QFilePrivate::openExternalFile(int flags, int fd, QFile::FileHandleFlags handleFlags) { #ifdef QT_NO_FSFILEENGINE Q_UNUSED(flags); @@ -112,14 +113,13 @@ QFilePrivate::openExternalFile(int flags, int fd) delete fileEngine; fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; - fe->setFileName(fileName); fileEngine = fe; - return fe->open(QIODevice::OpenMode(flags), fd); + return fe->open(QIODevice::OpenMode(flags), fd, handleFlags); #endif } bool -QFilePrivate::openExternalFile(int flags, FILE *fh) +QFilePrivate::openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handleFlags) { #ifdef QT_NO_FSFILEENGINE Q_UNUSED(flags); @@ -129,12 +129,28 @@ QFilePrivate::openExternalFile(int flags, FILE *fh) delete fileEngine; fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; - fe->setFileName(fileName); fileEngine = fe; - return fe->open(QIODevice::OpenMode(flags), fh); + return fe->open(QIODevice::OpenMode(flags), fh, handleFlags); #endif } +#ifdef Q_OS_SYMBIAN +bool QFilePrivate::openExternalFile(int flags, const RFile &f, QFile::FileHandleFlags handleFlags) +{ +#ifdef QT_NO_FSFILEENGINE + Q_UNUSED(flags); + Q_UNUSED(fh); + return false; +#else + delete fileEngine; + fileEngine = 0; + QFSFileEngine *fe = new QFSFileEngine; + fileEngine = fe; + return fe->open(QIODevice::OpenMode(flags), f, handleFlags); +#endif +} +#endif + inline bool QFilePrivate::ensureFlushed() const { // This function ensures that the write buffer has been flushed (const @@ -340,6 +356,20 @@ QFilePrivate::setError(QFile::FileError err, int errNum) \snippet doc/src/snippets/ntfsp.cpp 1 */ +/*! + \enum QFile::FileHandleFlag + + This enum is used when opening a file to specify additional + options which only apply to files and not to a generic + QIODevice. + + \value AutoCloseHandle The file handle passed into open() should be + closed by close(), the default behaviour is that close just flushes + the file and the app is responsible for closing the file handle. When + opening a file by name, this flag is ignored as Qt always "owns" the + file handle and must close it. + */ + #ifdef QT3_SUPPORT /*! \typedef QFile::PermissionSpec @@ -992,8 +1022,14 @@ bool QFile::open(OpenMode mode) return false; } +#ifdef Q_OS_SYMBIAN + // For symbian, the unbuffered flag is used to control write-behind cache behaviour + if (fileEngine()->open(mode)) +#else // QIODevice provides the buffering, so there's no need to request it from the file engine. - if (fileEngine()->open(mode | QIODevice::Unbuffered)) { + if (fileEngine()->open(mode | QIODevice::Unbuffered)) +#endif + { QIODevice::open(mode); if (mode & Append) seek(size()); @@ -1051,8 +1087,57 @@ bool QFile::open(OpenMode mode) \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 4 */ +// ### Qt5: merge this into new overload with a default parameter bool QFile::open(FILE *fh, OpenMode mode) { + return open(fh, mode, DontCloseHandle); +} + +/*! + \overload + + Opens the existing file handle \a fh in the given \a mode. + Returns true if successful; otherwise returns false. + + Example: + \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 3 + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + \bold{Warning:} + \list 1 + \o If \a fh does not refer to a regular file, e.g., it is \c stdin, + \c stdout, or \c stderr, you may not be able to seek(). size() + returns \c 0 in those cases. See QIODevice::isSequential() for + more information. + \o Since this function opens the file without specifying the file name, + you cannot use this QFile with a QFileInfo. + \endlist + + \note For Windows CE you may not be able to call resize(). + + \sa close(), {qmake Variable Reference#CONFIG}{qmake Variable Reference} + + \bold{Note for the Windows Platform} + + \a fh must be opened in binary mode (i.e., the mode string must contain + 'b', as in "rb" or "wb") when accessing files and other random-access + devices. Qt will translate the end-of-line characters if you pass + QIODevice::Text to \a mode. Sequential devices, such as stdin and stdout, + are unaffected by this limitation. + + You need to enable support for console applications in order to use the + stdin, stdout and stderr streams at the console. To do this, add the + following declaration to your application's project file: + + \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 4 +*/ +bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags) +{ Q_D(QFile); if (isOpen()) { qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); @@ -1065,7 +1150,7 @@ bool QFile::open(FILE *fh, OpenMode mode) qWarning("QFile::open: File access not specified"); return false; } - if(d->openExternalFile(mode, fh)) { + if (d->openExternalFile(mode, fh, handleFlags)) { QIODevice::open(mode); if (mode & Append) { seek(size()); @@ -1111,8 +1196,44 @@ bool QFile::open(FILE *fh, OpenMode mode) \sa close() */ +// ### Qt5: merge this into new overload with a default parameter bool QFile::open(int fd, OpenMode mode) { + return open(fd, mode, DontCloseHandle); +} + +/*! + \overload + + Opens the existing file descriptor \a fd in the given \a mode. + Returns true if successful; otherwise returns false. + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + The QFile that is opened using this function is automatically set + to be in raw mode; this means that the file input/output functions + are slow. If you run into performance issues, you should try to + use one of the other open functions. + + \warning If \a fd is not a regular file, e.g, it is 0 (\c stdin), + 1 (\c stdout), or 2 (\c stderr), you may not be able to seek(). In + those cases, size() returns \c 0. See QIODevice::isSequential() + for more information. + + \warning For Windows CE you may not be able to call seek(), setSize(), + fileTime(). size() returns \c 0. + + \warning Since this function opens the file without specifying the file name, + you cannot use this QFile with a QFileInfo. + + \sa close() +*/ +bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags) +{ Q_D(QFile); if (isOpen()) { qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); @@ -1125,7 +1246,7 @@ bool QFile::open(int fd, OpenMode mode) qWarning("QFile::open: File access not specified"); return false; } - if(d->openExternalFile(mode, fd)) { + if (d->openExternalFile(mode, fd, handleFlags)) { QIODevice::open(mode); if (mode & Append) { seek(size()); @@ -1139,6 +1260,63 @@ bool QFile::open(int fd, OpenMode mode) return false; } +#ifdef Q_OS_SYMBIAN +/*! + \overload + + Opens the existing file object \a f in the given \a mode. + Returns true if successful; otherwise returns false. + + When a QFile is opened using this function, behaviour of close() is + controlled by the AutoCloseHandle flag. + If AutoCloseHandle is specified, and this function succeeds, + then calling close() closes the adopted handle. + Otherwise, close() does not actually close the file, but only flushes it. + + \warning If the file handle is adopted from another process, + you may not be able to use this QFile with a QFileInfo. + + \sa close() +*/ +bool QFile::open(const RFile &f, OpenMode mode, FileHandleFlags handleFlags) +{ + Q_D(QFile); + if (isOpen()) { + qWarning("QFile::open: File (%s) already open", qPrintable(fileName())); + return false; + } + if (mode & Append) + mode |= WriteOnly; + unsetError(); + if ((mode & (ReadOnly | WriteOnly)) == 0) { + qWarning("QFile::open: File access not specified"); + return false; + } + if (d->openExternalFile(mode, f, handleFlags)) { + bool ok = QIODevice::open(mode); + if (ok) { + if (mode & Append) { + ok = seek(size()); + } else { + qint64 pos = 0; + TInt err; +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + err = static_cast<const RFile64&>(f).Seek(ESeekCurrent, pos); +#else + TInt pos32 = 0; + err = f.Seek(ESeekCurrent, pos32); + pos = pos32; +#endif + ok = ok && (err == KErrNone); + ok = ok && seek(pos); + } + } + return ok; + } + return false; +} +#endif + /*! Returns the file handle of the file. @@ -1223,6 +1401,7 @@ bool QFile::unmap(uchar *address) d->setError(d->fileEngine->error(), d->fileEngine->errorString()); return success; } + d->setError(PermissionsError, tr("No file engine available or engine does not support UnMapExtension")); return false; } @@ -1477,7 +1656,17 @@ bool QFile::atEnd() const } /*! - \reimp + For random-access devices, this function sets the current position + to \a pos, returning true on success, or false if an error occurred. + For sequential devices, the default behavior is to do nothing and + return false. + + Seeking beyond the end of a file: + If the position is beyond the end of a file, then seek() shall not + immediately extend the file. If a write is performed at this position, + then the file shall be extended. The content of the file between the + previous end of file and the newly written data is UNDEFINED and + varies between platforms and file systems. */ bool QFile::seek(qint64 off) diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h index 9bfcfde..4183534 100644 --- a/src/corelib/io/qfile.h +++ b/src/corelib/io/qfile.h @@ -45,6 +45,9 @@ #include <QtCore/qiodevice.h> #include <QtCore/qstring.h> #include <stdio.h> +#ifdef Q_OS_SYMBIAN +#include <f32file.h> +#endif #ifdef open #error qfile.h must be included before any header file that defines open @@ -97,6 +100,12 @@ public: }; Q_DECLARE_FLAGS(Permissions, Permission) + enum FileHandleFlag { + AutoCloseHandle = 0x0001, + DontCloseHandle = 0 + }; + Q_DECLARE_FLAGS(FileHandleFlags, FileHandleFlag) + QFile(); QFile(const QString &name); #ifndef QT_NO_QOBJECT @@ -145,6 +154,11 @@ public: bool open(OpenMode flags); bool open(FILE *f, OpenMode flags); bool open(int fd, OpenMode flags); +#ifdef Q_OS_SYMBIAN + bool open(const RFile &f, OpenMode flags, FileHandleFlags handleFlags = DontCloseHandle); +#endif + bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags); + bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags); virtual void close(); qint64 size() const; diff --git a/src/corelib/io/qfile_p.h b/src/corelib/io/qfile_p.h index abe3db3..d647c95 100644 --- a/src/corelib/io/qfile_p.h +++ b/src/corelib/io/qfile_p.h @@ -67,8 +67,11 @@ protected: QFilePrivate(); ~QFilePrivate(); - bool openExternalFile(int flags, int fd); - bool openExternalFile(int flags, FILE *fh); + bool openExternalFile(int flags, int fd, QFile::FileHandleFlags handleFlags); + bool openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handleFlags); +#ifdef Q_OS_SYMBIAN + bool openExternalFile(int flags, const RFile& f, QFile::FileHandleFlags handleFlags); +#endif QString fileName; mutable QAbstractFileEngine *fileEngine; diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 17160f4..6b9c82c 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -51,7 +51,47 @@ QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const { if (cache_enabled && !fileNames[(int)name].isNull()) return fileNames[(int)name]; - QString ret = fileEngine->fileName(name); + + QString ret; + if (fileEngine == 0) { // local file; use the QFileSystemEngine directly + switch (name) { + case QAbstractFileEngine::CanonicalName: + case QAbstractFileEngine::CanonicalPathName: { + QFileSystemEntry entry = QFileSystemEngine::canonicalName(fileEntry, metaData); + if (cache_enabled) { // be smart and store both + fileNames[QAbstractFileEngine::CanonicalName] = entry.filePath(); + fileNames[QAbstractFileEngine::CanonicalPathName] = entry.path(); + } + if (name == QAbstractFileEngine::CanonicalName) + ret = entry.filePath(); + else + ret = entry.path(); + break; + } + case QAbstractFileEngine::LinkName: + ret = QFileSystemEngine::getLinkTarget(fileEntry, metaData).filePath(); + break; + case QAbstractFileEngine::BundleName: + ret = QFileSystemEngine::bundleName(fileEntry); + break; + case QAbstractFileEngine::AbsoluteName: + case QAbstractFileEngine::AbsolutePathName: { + QFileSystemEntry entry = QFileSystemEngine::absoluteName(fileEntry); + if (cache_enabled) { // be smart and store both + fileNames[QAbstractFileEngine::AbsoluteName] = entry.filePath(); + fileNames[QAbstractFileEngine::AbsolutePathName] = entry.path(); + } + if (name == QAbstractFileEngine::AbsoluteName) + ret = entry.filePath(); + else + ret = entry.path(); + break; + } + default: break; + } + } else { + ret = fileEngine->fileName(name); + } if (ret.isNull()) ret = QLatin1String(""); if (cache_enabled) @@ -63,7 +103,19 @@ QString QFileInfoPrivate::getFileOwner(QAbstractFileEngine::FileOwner own) const { if (cache_enabled && !fileOwners[(int)own].isNull()) return fileOwners[(int)own]; - QString ret = fileEngine->owner(own); + QString ret; + if (fileEngine == 0) { + switch (own) { + case QAbstractFileEngine::OwnerUser: + ret = QFileSystemEngine::resolveUserName(fileEntry, metaData); + break; + case QAbstractFileEngine::OwnerGroup: + ret = QFileSystemEngine::resolveGroupName(fileEntry, metaData); + break; + } + } else { + ret = fileEngine->owner(own); + } if (ret.isNull()) ret = QLatin1String(""); if (cache_enabled) @@ -73,6 +125,7 @@ QString QFileInfoPrivate::getFileOwner(QAbstractFileEngine::FileOwner own) const uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) const { + Q_ASSERT(fileEngine); // should never be called when using the native FS // We split the testing into tests for for LinkType, BundleType, PermsMask // and the rest. // Tests for file permissions on Windows can be slow, expecially on network @@ -133,6 +186,7 @@ uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) cons QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) const { + Q_ASSERT(fileEngine); // should never be called when using the native FS if (!cache_enabled) clearFlags(); uint cf; @@ -237,6 +291,13 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) */ /*! + \internal +*/ +QFileInfo::QFileInfo(QFileInfoPrivate *p) : d_ptr(p) +{ +} + +/*! Constructs an empty QFileInfo object. Note that an empty QFileInfo object contain no file reference. @@ -330,23 +391,22 @@ bool QFileInfo::operator==(const QFileInfo &fileinfo) const return true; if (d->isDefaultConstructed || fileinfo.d_ptr->isDefaultConstructed) return false; - if (d->fileEngine->caseSensitive() != fileinfo.d_ptr->fileEngine->caseSensitive()) - return false; - if (fileinfo.size() == size()) { //if the size isn't the same... - QString file1 = canonicalFilePath(), - file2 = fileinfo.canonicalFilePath(); - if (file1.length() == file2.length()) { - if (!fileinfo.d_ptr->fileEngine->caseSensitive()) { - for (int i = 0; i < file1.length(); i++) { - if (file1.at(i).toLower() != file2.at(i).toLower()) - return false; - } - return true; - } - return (file1 == file2); - } + Qt::CaseSensitivity sensitive; + if (d->fileEngine == 0 || fileinfo.d_ptr->fileEngine == 0) { + if (d->fileEngine != fileinfo.d_ptr->fileEngine) // one is native, the other is a custom file-engine + return false; + + sensitive = QFileSystemEngine::isCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; + } else { + if (d->fileEngine->caseSensitive() != fileinfo.d_ptr->fileEngine->caseSensitive()) + return false; + sensitive = d->fileEngine->caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; } - return false; + + if (fileinfo.size() != size()) //if the size isn't the same... + return false; + + return canonicalFilePath().compare(fileinfo.canonicalFilePath(), sensitive) == 0; } /*! @@ -502,7 +562,7 @@ QString QFileInfo::absolutePath() const if (d->isDefaultConstructed) { return QLatin1String(""); - } else if (d->fileName.isEmpty()) { + } else if (d->fileEntry.isEmpty()) { qWarning("QFileInfo::absolutePath: Constructed with empty filename"); return QLatin1String(""); } @@ -539,7 +599,7 @@ QString QFileInfo::path() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->getFileName(QAbstractFileEngine::PathName); + return d->fileEntry.path(); } /*! @@ -563,6 +623,8 @@ bool QFileInfo::isRelative() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return true; + if (d->fileEngine == 0) + return d->fileEntry.isRelative(); return d->fileEngine->isRelativePath(); } @@ -576,12 +638,10 @@ bool QFileInfo::isRelative() const bool QFileInfo::makeAbsolute() { if (d_ptr.constData()->isDefaultConstructed - || !d_ptr.constData()->fileEngine->isRelativePath()) + || !d_ptr.constData()->fileEntry.isRelative()) return false; - QString absFileName = d_ptr.constData()->getFileName(QAbstractFileEngine::AbsoluteName); - // QSharedDataPointer::operator->() will detach. - setFile(absFileName); + setFile(absoluteFilePath()); return true; } @@ -596,6 +656,11 @@ bool QFileInfo::exists() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ExistsAttribute)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ExistsAttribute); + return d->metaData.exists(); + } return d->getFileFlags(QAbstractFileEngine::ExistsFlag); } @@ -623,7 +688,7 @@ QString QFileInfo::filePath() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->getFileName(QAbstractFileEngine::DefaultName); + return d->fileEntry.filePath(); } /*! @@ -642,7 +707,7 @@ QString QFileInfo::fileName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->getFileName(QAbstractFileEngine::BaseName); + return d->fileEntry.fileName(); } /*! @@ -686,7 +751,7 @@ QString QFileInfo::baseName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->getFileName(QAbstractFileEngine::BaseName).section(QLatin1Char('.'), 0, 0); + return d->fileEntry.baseName(); } /*! @@ -705,9 +770,7 @@ QString QFileInfo::completeBaseName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - QString name = d->getFileName(QAbstractFileEngine::BaseName); - int index = name.lastIndexOf(QLatin1Char('.')); - return (index == -1) ? name : name.left(index); + return d->fileEntry.completeBaseName(); } /*! @@ -726,11 +789,7 @@ QString QFileInfo::completeSuffix() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - QString fileName = d->getFileName(QAbstractFileEngine::BaseName); - int firstDot = fileName.indexOf(QLatin1Char('.')); - if (firstDot == -1) - return QLatin1String(""); - return fileName.mid(firstDot + 1); + return d->fileEntry.completeSuffix(); } /*! @@ -753,11 +812,7 @@ QString QFileInfo::suffix() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - QString fileName = d->getFileName(QAbstractFileEngine::BaseName); - int lastDot = fileName.lastIndexOf(QLatin1Char('.')); - if (lastDot == -1) - return QLatin1String(""); - return fileName.mid(lastDot + 1); + return d->fileEntry.suffix(); } @@ -781,8 +836,9 @@ QString QFileInfo::suffix() const */ QDir QFileInfo::dir() const { + Q_D(const QFileInfo); // ### Qt5: Maybe rename this to parentDirectory(), considering what it actually do? - return QDir(path()); + return QDir(d->fileEntry.path()); } /*! @@ -818,6 +874,11 @@ bool QFileInfo::isReadable() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserReadPermission)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserReadPermission); + return (d->metaData.permissions() & QFile::ReadUser) != 0; + } return d->getFileFlags(QAbstractFileEngine::ReadUserPerm); } @@ -831,6 +892,11 @@ bool QFileInfo::isWritable() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserWritePermission)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserWritePermission); + return (d->metaData.permissions() & QFile::WriteUser) != 0; + } return d->getFileFlags(QAbstractFileEngine::WriteUserPerm); } @@ -844,6 +910,11 @@ bool QFileInfo::isExecutable() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserExecutePermission)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserExecutePermission); + return (d->metaData.permissions() & QFile::ExeUser) != 0; + } return d->getFileFlags(QAbstractFileEngine::ExeUserPerm); } @@ -858,6 +929,11 @@ bool QFileInfo::isHidden() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::HiddenAttribute)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::HiddenAttribute); + return d->metaData.isHidden(); + } return d->getFileFlags(QAbstractFileEngine::HiddenFlag); } @@ -873,6 +949,11 @@ bool QFileInfo::isFile() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::FileType)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::FileType); + return d->metaData.isFile(); + } return d->getFileFlags(QAbstractFileEngine::FileType); } @@ -887,6 +968,11 @@ bool QFileInfo::isDir() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::DirectoryType)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::DirectoryType); + return d->metaData.isDirectory(); + } return d->getFileFlags(QAbstractFileEngine::DirectoryType); } @@ -903,6 +989,11 @@ bool QFileInfo::isBundle() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::BundleType)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::BundleType); + return d->metaData.isBundle(); + } return d->getFileFlags(QAbstractFileEngine::BundleType); } @@ -928,6 +1019,11 @@ bool QFileInfo::isSymLink() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::LegacyLinkType)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::LegacyLinkType); + return d->metaData.isLegacyLink(); + } return d->getFileFlags(QAbstractFileEngine::LinkType); } @@ -941,6 +1037,20 @@ bool QFileInfo::isRoot() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return true; + if (d->fileEngine == 0) { + if (d->fileEntry.isRoot()) { +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + //the path is a drive root, but the drive may not exist + //for backward compatibility, return true only if the drive exists + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ExistsAttribute)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ExistsAttribute); + return d->metaData.exists(); +#else + return true; +#endif + } + return false; + } return d->getFileFlags(QAbstractFileEngine::RootFlag); } @@ -1003,6 +1113,11 @@ uint QFileInfo::ownerId() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return 0; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::UserId)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::UserId); + return d->metaData.userId(); + } return d->fileEngine->ownerId(QAbstractFileEngine::OwnerUser); } @@ -1037,6 +1152,11 @@ uint QFileInfo::groupId() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return 0; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::GroupId)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::GroupId); + return d->metaData.groupId(); + } return d->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup); } @@ -1058,6 +1178,13 @@ bool QFileInfo::permission(QFile::Permissions permissions) const Q_D(const QFileInfo); if (d->isDefaultConstructed) return false; + if (d->fileEngine == 0) { + // the QFileSystemMetaData::MetaDataFlag and QFile::Permissions overlap, so just static cast. + QFileSystemMetaData::MetaDataFlag permissionFlags = static_cast<QFileSystemMetaData::MetaDataFlag>((int)permissions); + if (!d->cache_enabled || !d->metaData.hasFlags(permissionFlags)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, permissionFlags); + return (d->metaData.permissions() & permissions) == permissions; + } return d->getFileFlags(QAbstractFileEngine::FileFlags((int)permissions)) == (uint)permissions; } @@ -1070,6 +1197,11 @@ QFile::Permissions QFileInfo::permissions() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return 0; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::Permissions)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::Permissions); + return d->metaData.permissions(); + } return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask); } @@ -1085,6 +1217,11 @@ qint64 QFileInfo::size() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return 0; + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::SizeAttribute)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::SizeAttribute); + return d->metaData.size(); + } if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) { d->setCachedFlag(QFileInfoPrivate::CachedSize); d->fileSize = d->fileEngine->size(); @@ -1110,6 +1247,11 @@ QDateTime QFileInfo::created() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QDateTime(); + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::CreationTime)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::CreationTime); + return d->metaData.creationTime(); + } return d->getFileTime(QAbstractFileEngine::CreationTime); } @@ -1123,6 +1265,11 @@ QDateTime QFileInfo::lastModified() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QDateTime(); + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ModificationTime)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ModificationTime); + return d->metaData.modificationTime(); + } return d->getFileTime(QAbstractFileEngine::ModificationTime); } @@ -1139,6 +1286,11 @@ QDateTime QFileInfo::lastRead() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QDateTime(); + if (d->fileEngine == 0) { + if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::AccessTime)) + QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::AccessTime); + return d->metaData.accessTime(); + } return d->getFileTime(QAbstractFileEngine::AccessTime); } diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h index e368af2..5cfefb3 100644 --- a/src/corelib/io/qfileinfo.h +++ b/src/corelib/io/qfileinfo.h @@ -53,12 +53,16 @@ QT_BEGIN_NAMESPACE QT_MODULE(Core) class QDir; +class QDirIteratorPrivate; class QDateTime; class QFileInfoPrivate; class Q_CORE_EXPORT QFileInfo { + friend class QDirIteratorPrivate; public: + explicit QFileInfo(QFileInfoPrivate *d); + QFileInfo(); QFileInfo(const QString &file); QFileInfo(const QFile &file); @@ -67,6 +71,10 @@ public: ~QFileInfo(); QFileInfo &operator=(const QFileInfo &fileinfo); +#ifdef Q_COMPILER_RVALUE_REFS + inline QFileInfo&operator=(QFileInfo &&other) + { qSwap(d_ptr, other.d_ptr); return *this; } +#endif bool operator==(const QFileInfo &fileinfo); // 5.0 - remove me bool operator==(const QFileInfo &fileinfo) const; inline bool operator!=(const QFileInfo &fileinfo) { return !(operator==(fileinfo)); } // 5.0 - remove me diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h index d094513..db904c7 100644 --- a/src/corelib/io/qfileinfo_p.h +++ b/src/corelib/io/qfileinfo_p.h @@ -58,13 +58,16 @@ #include "qdatetime.h" #include "qatomic.h" #include "qshareddata.h" +#include "qfilesystemengine_p.h" + +#include <QtCore/private/qfilesystementry_p.h> +#include <QtCore/private/qfilesystemmetadata_p.h> QT_BEGIN_NAMESPACE class QFileInfoPrivate : public QSharedData { public: - enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04, CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40, CachedSize =0x08, CachedPerms=0x80 }; @@ -76,8 +79,10 @@ public: cache_enabled(true), fileFlags(0), fileSize(0) {} inline QFileInfoPrivate(const QFileInfoPrivate ©) - : QSharedData(copy), fileEngine(QAbstractFileEngine::create(copy.fileName)), - fileName(copy.fileName), + : QSharedData(copy), + fileEntry(copy.fileEntry), + metaData(copy.metaData), + fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)), cachedFlags(0), #ifndef QT_NO_FSFILEENGINE isDefaultConstructed(false), @@ -87,8 +92,8 @@ public: cache_enabled(copy.cache_enabled), fileFlags(0), fileSize(0) {} inline QFileInfoPrivate(const QString &file) - : QSharedData(), fileEngine(QAbstractFileEngine::create(file)), - fileName(file), + : fileEntry(QDir::fromNativeSeparators(file)), + fileEngine(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(fileEntry, metaData)), cachedFlags(0), #ifndef QT_NO_FSFILEENGINE isDefaultConstructed(false), @@ -99,6 +104,16 @@ public: { } + inline QFileInfoPrivate(const QFileSystemEntry &file, const QFileSystemMetaData &data) + : QSharedData(), + fileEntry(file), + metaData(data), + cachedFlags(0), + isDefaultConstructed(false), + cache_enabled(true), fileFlags(0), fileSize(0) + { + } + inline void clearFlags() const { fileFlags = 0; cachedFlags = 0; @@ -106,6 +121,7 @@ public: (void)fileEngine->fileFlags(QAbstractFileEngine::Refresh); } inline void clear() { + metaData.clear(); clearFlags(); for (int i = QAbstractFileEngine::NFileNames - 1 ; i >= 0 ; --i) fileNames[i].clear(); @@ -118,9 +134,11 @@ public: QString getFileName(QAbstractFileEngine::FileName) const; QString getFileOwner(QAbstractFileEngine::FileOwner own) const; + QFileSystemEntry fileEntry; + mutable QFileSystemMetaData metaData; + QScopedPointer<QAbstractFileEngine> const fileEngine; - mutable QString fileName; mutable QString fileNames[QAbstractFileEngine::NFileNames]; mutable QString fileOwners[2]; @@ -134,6 +152,7 @@ public: { return cache_enabled ? (cachedFlags & c) : 0; } inline void setCachedFlag(uint c) const { if (cache_enabled) cachedFlags |= c; } + }; QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp new file mode 100644 index 0000000..00c33bd --- /dev/null +++ b/src/corelib/io/qfilesystemengine.cpp @@ -0,0 +1,391 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" +#include <QtCore/qdir.h> +#include <QtCore/qset.h> +#include <QtCore/qstringbuilder.h> +#include <QtCore/private/qabstractfileengine_p.h> +#ifdef QT_BUILD_CORE_LIB +#include <QtCore/private/qresource_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \internal + + Returns the canonicalized form of \a path (i.e., with all symlinks + resolved, and all redundant path elements removed. +*/ +QString QFileSystemEngine::slowCanonicalized(const QString &path) +{ + if (path.isEmpty()) + return path; + + QFileInfo fi; + const QChar slash(QLatin1Char('/')); + QString tmpPath = path; + int separatorPos = 0; + QSet<QString> nonSymlinks; + QSet<QString> known; + + known.insert(path); + do { +#ifdef Q_OS_WIN + if (separatorPos == 0) { + if (tmpPath.size() >= 2 && tmpPath.at(0) == slash && tmpPath.at(1) == slash) { + // UNC, skip past the first two elements + separatorPos = tmpPath.indexOf(slash, 2); + } else if (tmpPath.size() >= 3 && tmpPath.at(1) == QLatin1Char(':') && tmpPath.at(2) == slash) { + // volume root, skip since it can not be a symlink + separatorPos = 2; + } + } + if (separatorPos != -1) +#endif + separatorPos = tmpPath.indexOf(slash, separatorPos + 1); + QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos); + if ( +#ifdef Q_OS_SYMBIAN + // Symbian doesn't support directory symlinks, so do not check for link unless we + // are handling the last path element. This not only slightly improves performance, + // but also saves us from lot of unnecessary platform security check failures + // when dealing with files under *:/private directories. + separatorPos == -1 && +#endif + !nonSymlinks.contains(prefix)) { + fi.setFile(prefix); + if (fi.isSymLink()) { + QString target = fi.symLinkTarget(); + if(QFileInfo(target).isRelative()) + target = fi.absolutePath() + slash + target; + if (separatorPos != -1) { + if (fi.isDir() && !target.endsWith(slash)) + target.append(slash); + target.append(tmpPath.mid(separatorPos)); + } + tmpPath = QDir::cleanPath(target); + separatorPos = 0; + + if (known.contains(tmpPath)) + return QString(); + known.insert(tmpPath); + } else { + nonSymlinks.insert(prefix); + } + } + } while (separatorPos != -1); + + return QDir::cleanPath(tmpPath); +} + +static inline bool _q_checkEntry(QFileSystemEntry &entry, QFileSystemMetaData &data, bool resolvingEntry) +{ + if (resolvingEntry) { + if (!QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute) + || !data.exists()) { + data.clear(); + return false; + } + } + + return true; +} + +static inline bool _q_checkEntry(QAbstractFileEngine *&engine, bool resolvingEntry) +{ + if (resolvingEntry) { + if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) { + delete engine; + engine = 0; + return false; + } + } + + return true; +} + +static bool _q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry &entry, QFileSystemMetaData &data, + QAbstractFileEngine *&engine, bool resolvingEntry = false) +{ + QString const &filePath = entry.filePath(); + if ((engine = qt_custom_file_engine_handler_create(filePath))) + return _q_checkEntry(engine, resolvingEntry); + +#if defined(QT_BUILD_CORE_LIB) + for (int prefixSeparator = 0; prefixSeparator < filePath.size(); ++prefixSeparator) { + QChar const ch = filePath[prefixSeparator]; + if (ch == QLatin1Char('/')) + break; + + if (ch == QLatin1Char(':')) { + if (prefixSeparator == 0) { + engine = new QResourceFileEngine(filePath); + return _q_checkEntry(engine, resolvingEntry); + } + + if (prefixSeparator == 1) + break; + + const QStringList &paths = QDir::searchPaths(filePath.left(prefixSeparator)); + for (int i = 0; i < paths.count(); i++) { + entry = QFileSystemEntry(QDir::cleanPath(paths.at(i) % QLatin1Char('/') % filePath.mid(prefixSeparator + 1))); + // Recurse! + if (_q_resolveEntryAndCreateLegacyEngine_recursive(entry, data, engine, true)) + return true; + } + + // entry may have been clobbered at this point. + return false; + } + + // There's no need to fully validate the prefix here. Consulting the + // unicode tables could be expensive and validation is already + // performed in QDir::setSearchPaths. + // + // if (!ch.isLetterOrNumber()) + // break; + } +#endif // defined(QT_BUILD_CORE_LIB) + + return _q_checkEntry(entry, data, resolvingEntry); +} + +/*! + \internal + + Resolves the \a entry (see QDir::searchPaths) and returns an engine for + it, but never a QFSFileEngine. + + \returns a file engine that can be used to access the entry. Returns 0 if + QFileSystemEngine API should be used to query and interact with the file + system object. +*/ +QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine( + QFileSystemEntry &entry, QFileSystemMetaData &data) { + QFileSystemEntry copy = entry; + QAbstractFileEngine *engine = 0; + + if (_q_resolveEntryAndCreateLegacyEngine_recursive(copy, data, engine)) + // Reset entry to resolved copy. + entry = copy; + else + data.clear(); + + return engine; +} + +//these unix functions are in this file, because they are shared by symbian port +//for open C file handles. +#ifdef Q_OS_UNIX +//static +bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data) +{ + data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; + data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags; + + QT_STATBUF statBuffer; + if (QT_FSTAT(fd, &statBuffer) == 0) { + data.fillFromStatBuf(statBuffer); + return true; + } + + return false; +} + +void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer) +{ + // Permissions + if (statBuffer.st_mode & S_IRUSR) + entryFlags |= QFileSystemMetaData::OwnerReadPermission; + if (statBuffer.st_mode & S_IWUSR) + entryFlags |= QFileSystemMetaData::OwnerWritePermission; + if (statBuffer.st_mode & S_IXUSR) + entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + + if (statBuffer.st_mode & S_IRGRP) + entryFlags |= QFileSystemMetaData::GroupReadPermission; + if (statBuffer.st_mode & S_IWGRP) + entryFlags |= QFileSystemMetaData::GroupWritePermission; + if (statBuffer.st_mode & S_IXGRP) + entryFlags |= QFileSystemMetaData::GroupExecutePermission; + + if (statBuffer.st_mode & S_IROTH) + entryFlags |= QFileSystemMetaData::OtherReadPermission; + if (statBuffer.st_mode & S_IWOTH) + entryFlags |= QFileSystemMetaData::OtherWritePermission; + if (statBuffer.st_mode & S_IXOTH) + entryFlags |= QFileSystemMetaData::OtherExecutePermission; + + // Type + if ((statBuffer.st_mode & S_IFMT) == S_IFREG) + entryFlags |= QFileSystemMetaData::FileType; + else if ((statBuffer.st_mode & S_IFMT) == S_IFDIR) + entryFlags |= QFileSystemMetaData::DirectoryType; + else + entryFlags |= QFileSystemMetaData::SequentialType; + + // Attributes + entryFlags |= QFileSystemMetaData::ExistsAttribute; + size_ = statBuffer.st_size; +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) \ + && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (statBuffer.st_flags & UF_HIDDEN) { + entryFlags |= QFileSystemMetaData::HiddenAttribute; + knownFlagsMask |= QFileSystemMetaData::HiddenAttribute; + } +#endif + + // Times +#ifdef Q_OS_SYMBIAN + modificationTime_ = qt_symbian_time_t_To_TTime(statBuffer.st_mtime); +#else + creationTime_ = statBuffer.st_ctime ? statBuffer.st_ctime : statBuffer.st_mtime; + modificationTime_ = statBuffer.st_mtime; + accessTime_ = statBuffer.st_atime; + userId_ = statBuffer.st_uid; + groupId_ = statBuffer.st_gid; +#endif +} + +void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry) +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4) || defined(Q_OS_SYMBIAN) + // BSD4 includes Mac OS X + + // ### This will clear all entry flags and knownFlagsMask + switch (entry.d_type) + { + case DT_DIR: + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_BLK: + case DT_CHR: + case DT_FIFO: + case DT_SOCK: + // ### System attribute + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType + | QFileSystemMetaData::AliasType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_LNK: + knownFlagsMask = QFileSystemMetaData::LinkType; + entryFlags = QFileSystemMetaData::LinkType; + break; + + case DT_REG: + knownFlagsMask = QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::ExistsAttribute; + + entryFlags = QFileSystemMetaData::FileType + | QFileSystemMetaData::ExistsAttribute; + + break; + + case DT_UNKNOWN: + default: + clear(); + } +#else + Q_UNUSED(entry) +#endif +} + +#endif + +//static +QString QFileSystemEngine::resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData) +{ +#if defined (Q_OS_SYMBIAN) + Q_UNUSED(entry); + Q_UNUSED(metaData); + return QString(); +#elif defined(Q_OS_WIN) + Q_UNUSED(metaData); + return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerUser); +#else //(Q_OS_UNIX) + if (!metaData.hasFlags(QFileSystemMetaData::UserId)) + QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::UserId); + return resolveUserName(metaData.userId()); +#endif +} + +//static +QString QFileSystemEngine::resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &metaData) +{ +#if defined (Q_OS_SYMBIAN) + Q_UNUSED(entry); + Q_UNUSED(metaData); + return QString(); +#elif defined(Q_OS_WIN) + Q_UNUSED(metaData); + return QFileSystemEngine::owner(entry, QAbstractFileEngine::OwnerGroup); +#else //(Q_OS_UNIX) + if (!metaData.hasFlags(QFileSystemMetaData::GroupId)) + QFileSystemEngine::fillMetaData(entry, metaData, QFileSystemMetaData::GroupId); + return resolveGroupName(metaData.groupId()); +#endif +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine_mac.cpp b/src/corelib/io/qfilesystemengine_mac.cpp new file mode 100644 index 0000000..1c0056b --- /dev/null +++ b/src/corelib/io/qfilesystemengine_mac.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" + +QT_BEGIN_NAMESPACE + +// Mac-specific implementations only! + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine_p.h b/src/corelib/io/qfilesystemengine_p.h new file mode 100644 index 0000000..d6033e6 --- /dev/null +++ b/src/corelib/io/qfilesystemengine_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMENGINE_P_H_INCLUDED +#define QFILESYSTEMENGINE_P_H_INCLUDED + +// +// 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 "qfile.h" +#include "qfilesystementry_p.h" +#include "qfilesystemmetadata_p.h" +#include <QtCore/private/qsystemerror_p.h> + +QT_BEGIN_NAMESPACE + +class QFileSystemEngine +{ +public: + static bool isCaseSensitive(); + + static QFileSystemEntry getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data); + static QFileSystemEntry canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + static QFileSystemEntry absoluteName(const QFileSystemEntry &entry); + static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + static QString resolveGroupName(const QFileSystemEntry &entry, QFileSystemMetaData &data); + +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + static QString resolveUserName(uint userId); + static QString resolveGroupName(uint groupId); +#endif + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + static QString bundleName(const QFileSystemEntry &entry); +#else + static QString bundleName(const QFileSystemEntry &entry) { Q_UNUSED(entry) return QString(); } +#endif + + static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); +#if defined(Q_OS_UNIX) + static bool fillMetaData(int fd, QFileSystemMetaData &data); // what = PosixStatFlags +#endif +#if defined(Q_OS_WIN) + + static bool uncListSharesOnServer(const QString &server, QStringList *list); //Used also by QFSFileEngineIterator::hasNext() + static bool fillMetaData(int fd, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static bool fillMetaData(HANDLE fHandle, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static bool fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what); + static QString owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own); + static QString nativeAbsoluteFilePath(const QString &path); +#endif + //homePath, rootPath and tempPath shall return clean paths + static QString homePath(); + static QString rootPath(); + static QString tempPath(); + + static bool createDirectory(const QFileSystemEntry &entry, bool createParents); + static bool removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents); + + static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + + static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); + static bool removeFile(const QFileSystemEntry &entry, QSystemError &error); + + static bool setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, + QFileSystemMetaData *data = 0); + + static bool setCurrentPath(const QFileSystemEntry &entry); + static QFileSystemEntry currentPath(); + + static QAbstractFileEngine *resolveEntryAndCreateLegacyEngine(QFileSystemEntry &entry, + QFileSystemMetaData &data); +private: + static QString slowCanonicalized(const QString &path); +#if defined(Q_OS_WIN) + static void clearWinStatData(QFileSystemMetaData &data); +#endif +}; + +QT_END_NAMESPACE + +#endif // include guard diff --git a/src/corelib/io/qfilesystemengine_symbian.cpp b/src/corelib/io/qfilesystemengine_symbian.cpp new file mode 100644 index 0000000..41a550a --- /dev/null +++ b/src/corelib/io/qfilesystemengine_symbian.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" +#include "qfsfileengine.h" +#include <QtCore/private/qcore_symbian_p.h> +#include <QtCore/qcoreapplication.h> + +#include <f32file.h> +#include <pathinfo.h> +#include <wchar.h> + +QT_BEGIN_NAMESPACE + +bool QFileSystemEngine::isCaseSensitive() +{ + return false; +} + +//TODO: resolve this with QDir::cleanPath, without breaking the behaviour of that +//function which is documented only by autotest +//input: a dirty absolute path, e.g. c:/../../foo/./ +//output: a clean absolute path, e.g. c:/foo/ +static QString symbianCleanAbsolutePath(const QString& path) +{ + bool isDir = path.endsWith(QLatin1Char('/')); + //using SkipEmptyParts flag to eliminate duplicated slashes + QStringList components = path.split(QLatin1Char('/'), QString::SkipEmptyParts); + int cdups = 0; + for(int i=components.count() - 1; i>=0; --i) { + if(components.at(i) == QLatin1String("..")) { + components.removeAt(i); + cdups++; + } + else if(components.at(i) == QLatin1String(".")) { + components.removeAt(i); + } + else if(cdups && i > 0) { + --cdups; + components.removeAt(i); + } + } + QString result = components.join(QLatin1String("/")); + if ((isDir&& !result.endsWith(QLatin1Char('/'))) + || (result.length() == 2 && result.at(1).unicode() == ':')) + result.append(QLatin1Char('/')); + return result; +} + +//static +QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data) +{ + Q_UNUSED(data); + return link; +} + +//static +QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) +{ + if (entry.isEmpty() || entry.isRoot()) + return entry; + + QFileSystemEntry result = absoluteName(entry); + if (!data.hasFlags(QFileSystemMetaData::ExistsAttribute)) + fillMetaData(result, data, QFileSystemMetaData::ExistsAttribute); + if (!data.exists()) { + // file doesn't exist + return QFileSystemEntry(); + } else { + return result; + } +} + +//static +QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) +{ + QString orig = entry.filePath(); + const bool isAbsolute = entry.isAbsolute(); + const bool isDirty = (orig.contains(QLatin1String("/../")) || orig.contains(QLatin1String("/./")) || + orig.contains(QLatin1String("//")) || + orig.endsWith(QLatin1String("/..")) || orig.endsWith(QLatin1String("/."))); + if (isAbsolute && !isDirty) + return entry; + + const bool isRelative = entry.isRelative(); + const bool needsDrive = (!orig.isEmpty() && orig.at(0).unicode() == '/'); + const bool isDriveLetter = !needsDrive && !isAbsolute && !isRelative && orig.length() == 2; + const bool isDriveRelative = !needsDrive && !isAbsolute && !isRelative && orig.length() > 2; + + QString result; + if (needsDrive || isDriveLetter || isDriveRelative || !isAbsolute || orig.isEmpty()) { + QFileSystemEntry cur(currentPath()); + if(needsDrive) + result = cur.filePath().left(2); + else if(isDriveRelative && cur.filePath().at(0) != orig.at(0)) + result = orig.left(2); // for BC, see tst_QFileInfo::absolutePath(<not current drive>:my.dll) + else + result = cur.filePath(); + if(isDriveLetter) { + result[0] = orig.at(0); //copy drive letter + orig.clear(); + } + if(isDriveRelative) { + orig = orig.mid(2); //discard the drive specifier from orig + } + } + if (!orig.isEmpty() && !(orig.length() == 1 && orig.at(0).unicode() == '.')) { + if (!result.isEmpty() && !result.endsWith(QLatin1Char('/'))) + result.append(QLatin1Char('/')); + result.append(orig); + } + + return QFileSystemEntry(symbianCleanAbsolutePath(result), QFileSystemEntry::FromInternalPath()); +} + +void QFileSystemMetaData::fillFromTEntry(const TEntry& entry) +{ + entryFlags &= ~(QFileSystemMetaData::SymbianTEntryFlags); + knownFlagsMask |= QFileSystemMetaData::SymbianTEntryFlags; + //Symbian doesn't have unix type file permissions + entryFlags |= QFileSystemMetaData::ReadPermissions; + if(!entry.IsReadOnly()) { + entryFlags |= QFileSystemMetaData::WritePermissions; + } + //set the type + if(entry.IsDir()) + entryFlags |= (QFileSystemMetaData::DirectoryType | QFileSystemMetaData::ExecutePermissions); + else + entryFlags |= QFileSystemMetaData::FileType; + + //set the attributes + entryFlags |= QFileSystemMetaData::ExistsAttribute; + if(entry.IsHidden()) + entryFlags |= QFileSystemMetaData::HiddenAttribute; + +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + size_ = entry.FileSize(); +#else + size_ = (TUint)(entry.iSize); +#endif + + modificationTime_ = entry.iModified; +} + +void QFileSystemMetaData::fillFromVolumeInfo(const TVolumeInfo& info) +{ + entryFlags &= ~(QFileSystemMetaData::SymbianTEntryFlags); + knownFlagsMask |= QFileSystemMetaData::SymbianTEntryFlags; + entryFlags |= QFileSystemMetaData::ExistsAttribute; + entryFlags |= QFileSystemMetaData::Permissions; + if(info.iDrive.iDriveAtt & KDriveAttRom) { + entryFlags &= ~(QFileSystemMetaData::WritePermissions); + } + entryFlags |= QFileSystemMetaData::DirectoryType; + size_ = info.iSize; + modificationTime_ = qt_symbian_time_t_To_TTime(0); +} + +//static +bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what) +{ + if (what & QFileSystemMetaData::SymbianTEntryFlags) { + RFs& fs(qt_s60GetRFs()); + TInt err; + QFileSystemEntry absentry(absoluteName(entry)); + if (entry.isEmpty()) { + err = KErrNotFound; + } else if (absentry.isRoot()) { + //Root directories don't have an entry, and Entry() returns KErrBadName. + //Therefore get information about the volume instead. + TInt drive; + err = RFs::CharToDrive(TChar(absentry.nativeFilePath().at(0).unicode()), drive); + if (!err) { + TVolumeInfo info; + err = fs.Volume(info, drive); + if (!err) + data.fillFromVolumeInfo(info); + } + } else { + TEntry ent; + err = fs.Entry(qt_QString2TPtrC(absentry.nativeFilePath()), ent); + if (!err) + data.fillFromTEntry(ent); + } + if (err) { + data.size_ = 0; + data.modificationTime_ = TTime(0); + data.entryFlags &= ~(QFileSystemMetaData::SymbianTEntryFlags); + } + //files in /sys/bin on any drive are executable, even though we don't normally have permission to check whether they exist or not + if(absentry.filePath().midRef(1,10).compare(QLatin1String(":/sys/bin/"), Qt::CaseInsensitive) == 0) + data.entryFlags |= QFileSystemMetaData::ExecutePermissions; + } + return data.hasFlags(what); +} + +//static +bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) +{ + QString abspath = absoluteName(entry).nativeFilePath(); + if (!abspath.endsWith(QLatin1Char('\\'))) + abspath.append(QLatin1Char('\\')); + TInt r; + if (createParents) + r = qt_s60GetRFs().MkDirAll(qt_QString2TPtrC(abspath)); + else + r = qt_s60GetRFs().MkDir(qt_QString2TPtrC(abspath)); + if (createParents && r == KErrAlreadyExists) + return true; //# Qt5 - QDir::mkdir returns false for existing dir, QDir::mkpath returns true (should be made consistent in Qt 5) + return (r == KErrNone); +} + +//static +bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) +{ + QString abspath = absoluteName(entry).nativeFilePath(); + if (!abspath.endsWith(QLatin1Char('\\'))) + abspath.append(QLatin1Char('\\')); + TPtrC dir(qt_QString2TPtrC(abspath)); + RFs& fs = qt_s60GetRFs(); + bool ok = false; + //behaviour of FS file engine: + //returns true if the directory could be removed + //success/failure of removing parent directories does not matter + while (KErrNone == fs.RmDir(dir)) { + ok = true; + if (!removeEmptyParents) + break; + //RFs::RmDir treats "c:\foo\bar" and "c:\foo\" the same, so it is sufficient to remove the last \ to the end + dir.Set(dir.Left(dir.LocateReverse(TChar('\\')))); + } + return ok; +} + +//static +bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + Q_UNUSED(source) + Q_UNUSED(target) + error = QSystemError(KErrNotSupported, QSystemError::NativeError); + return false; +} + +//static +bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + //CFileMan is allocated each time because it is not thread-safe + CFileMan *fm = 0; + TRAPD(err, fm = CFileMan::NewL(qt_s60GetRFs())); + if (err == KErrNone) { + err = fm->Copy(qt_QString2TPtrC(absoluteName(source).nativeFilePath()), qt_QString2TPtrC(absoluteName(target).nativeFilePath()), 0); + delete fm; + } + if (err == KErrNone) + return true; + error = QSystemError(err, QSystemError::NativeError); + return false; +} + +//static +bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + QString sourcepath = absoluteName(source).nativeFilePath(); + QString targetpath = absoluteName(target).nativeFilePath(); + RFs& fs(qt_s60GetRFs()); + TInt err = fs.Rename(qt_QString2TPtrC(sourcepath), qt_QString2TPtrC(targetpath)); + if (err == KErrNone) + return true; + error = QSystemError(err, QSystemError::NativeError); + return false; +} + +//static +bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) +{ + QString targetpath = absoluteName(entry).nativeFilePath(); + RFs& fs(qt_s60GetRFs()); + TInt err = fs.Delete(qt_QString2TPtrC(targetpath)); + if (err == KErrNone) + return true; + error = QSystemError(err, QSystemError::NativeError); + return false; +} + +//static +bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data) +{ + QString targetpath = absoluteName(entry).nativeFilePath(); + TUint setmask = 0; + TUint clearmask = 0; + RFs& fs(qt_s60GetRFs()); + if (permissions & (QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)) + clearmask = KEntryAttReadOnly; //if anyone can write, it's not read-only + else + setmask = KEntryAttReadOnly; + TInt err = fs.SetAtt(qt_QString2TPtrC(targetpath), setmask, clearmask); + if (data && !err) { + data->entryFlags &= ~QFileSystemMetaData::Permissions; + data->entryFlags |= QFileSystemMetaData::MetaDataFlag(uint(permissions)); + data->knownFlagsMask |= QFileSystemMetaData::Permissions; + } + if (err == KErrNone) + return true; + error = QSystemError(err, QSystemError::NativeError); + return false; +} + +QString QFileSystemEngine::homePath() +{ + QString home = QDir::fromNativeSeparators(qt_TDesC2QString(PathInfo::PhoneMemoryRootPath())); + if(home.endsWith(QLatin1Char('/'))) + home.chop(1); + return home; +} + +QString QFileSystemEngine::rootPath() +{ + TChar drive; + TInt err = RFs::DriveToChar(RFs::GetSystemDrive(), drive); //RFs::GetSystemDriveChar not supported on S60 3.1 + Q_ASSERT(err == KErrNone); //RFs::GetSystemDrive() shall always return a convertible drive number on a valid OS configuration + return QString(QChar(drive)).append(QLatin1String(":/")); +} + +QString QFileSystemEngine::tempPath() +{ + return rootPath().append(QLatin1String("system/temp")); +} + +//static +bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry) +{ + QFileSystemMetaData meta; + QFileSystemEntry absname = absoluteName(entry); + fillMetaData(absname, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType); + if(!(meta.exists() && meta.isDirectory())) + return false; + + RFs& fs = qt_s60GetRFs(); + QString abspath = absname.nativeFilePath(); + if(!abspath.endsWith(QLatin1Char('\\'))) + abspath.append(QLatin1Char('\\')); + TInt r = fs.SetSessionPath(qt_QString2TPtrC(abspath)); + //SetSessionPath succeeds for non existent directory, which is why it's checked above + if (r == KErrNone) { + __ASSERT_COMPILE(sizeof(wchar_t) == sizeof(unsigned short)); + //attempt to set open C to the same path + r = ::wchdir(reinterpret_cast<const wchar_t *>(absname.filePath().utf16())); + if (r < 0) + qWarning("failed to sync path to open C"); + return true; + } + return false; +} + +//static +QFileSystemEntry QFileSystemEngine::currentPath() +{ + TFileName fn; + QFileSystemEntry ret; + TInt r = qt_s60GetRFs().SessionPath(fn); + if(r == KErrNone) { + //remove terminating slash from non root paths (session path is clean, absolute and always ends in a \) + if(fn.Length() > 3 && fn[fn.Length() - 1] == '\\') + fn.SetLength(fn.Length() - 1); + ret = QFileSystemEntry(qt_TDesC2QString(fn), QFileSystemEntry::FromNativePath()); + } + return ret; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp new file mode 100644 index 0000000..c9ebaa4 --- /dev/null +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -0,0 +1,660 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qfilesystemengine_p.h" +#include "qplatformdefs.h" +#include "qfsfileengine.h" +#include "qfile.h" + +#include <QtCore/qvarlengtharray.h> + +#include <stdlib.h> // for realpath() +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + + +#if defined(Q_OS_MAC) +# include <QtCore/private/qcore_mac_p.h> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) +static inline bool _q_isMacHidden(const char *nativePath) +{ + OSErr err; + + FSRef fsRef; + err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(nativePath), + kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0); + if (err != noErr) + return false; + + FSCatalogInfo catInfo; + err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL); + if (err != noErr) + return false; + + FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo); + return (fileInfo->finderFlags & kIsInvisible); +} +#else +static inline bool _q_isMacHidden(const char *nativePath) +{ + Q_UNUSED(nativePath); + // no-op + return false; +} +#endif + +bool QFileSystemEngine::isCaseSensitive() +{ + return true; +} + +//static +QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, QFileSystemMetaData &data) +{ +#if defined(__GLIBC__) && !defined(PATH_MAX) +#define PATH_CHUNK_SIZE 256 + char *s = 0; + int len = -1; + int size = PATH_CHUNK_SIZE; + + while (1) { + s = (char *) ::realloc(s, size); + Q_CHECK_PTR(s); + len = ::readlink(link.nativeFilePath().constData(), s, size); + if (len < 0) { + ::free(s); + break; + } + if (len < size) { + break; + } + size *= 2; + } +#else + char s[PATH_MAX+1]; + int len = readlink(link.nativeFilePath().constData(), s, PATH_MAX); +#endif + if (len > 0) { + QString ret; + if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) + fillMetaData(link, data, QFileSystemMetaData::DirectoryType); + if (data.isDirectory() && s[0] != '/') { + QDir parent(link.filePath()); + parent.cdUp(); + ret = parent.path(); + if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) + ret += QLatin1Char('/'); + } + s[len] = '\0'; + ret += QFile::decodeName(QByteArray(s)); +#if defined(__GLIBC__) && !defined(PATH_MAX) + ::free(s); +#endif + + if (!ret.startsWith(QLatin1Char('/'))) { + if (link.filePath().startsWith(QLatin1Char('/'))) { + ret.prepend(link.filePath().left(link.filePath().lastIndexOf(QLatin1Char('/'))) + + QLatin1Char('/')); + } else { + ret.prepend(QDir::currentPath() + QLatin1Char('/')); + } + } + ret = QDir::cleanPath(ret); + if (ret.size() > 1 && ret.endsWith(QLatin1Char('/'))) + ret.chop(1); + return QFileSystemEntry(ret); + } +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + { + FSRef fref; + if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(link.filePath())).data(), &fref, 0) == noErr) { + // TODO get the meta data info from the QFileSystemMetaData object + Boolean isAlias, isFolder; + if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) { + AliasHandle alias; + if (FSNewAlias(0, &fref, &alias) == noErr && alias) { + QCFString cfstr; + if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr) + return QFileSystemEntry(QCFString::toQString(cfstr)); + } + } + } + } +#endif + return QFileSystemEntry(); +} + +//static +QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) +{ + if (entry.isEmpty() || entry.isRoot()) + return entry; + +#if !defined(Q_OS_MAC) && _POSIX_VERSION < 200809L + // realpath(X,0) is not supported + Q_UNUSED(data); + return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath())); +#else + char *ret = 0; +# if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) + // Mac OS X 10.5.x doesn't support the realpath(X,0) extension we use here. + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { + ret = realpath(entry.nativeFilePath().constData(), (char*)0); + } else { + // on 10.5 we can use FSRef to resolve the file path. + QString path = QDir::cleanPath(entry.filePath()); + FSRef fsref; + if (FSPathMakeRef((const UInt8 *)path.toUtf8().data(), &fsref, 0) == noErr) { + CFURLRef urlref = CFURLCreateFromFSRef(NULL, &fsref); + CFStringRef canonicalPath = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle); + QString ret = QCFString::toQString(canonicalPath); + CFRelease(canonicalPath); + CFRelease(urlref); + return QFileSystemEntry(ret); + } + } +# else + ret = realpath(entry.nativeFilePath().constData(), (char*)0); +# endif + if (ret) { + data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute; + data.entryFlags |= QFileSystemMetaData::ExistsAttribute; + QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret)); + free(ret); + return QFileSystemEntry(canonicalPath); + } else if (errno == ENOENT) { // file doesn't exist + data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute; + data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute); + return QFileSystemEntry(); + } + return entry; +#endif +} + +//static +QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) +{ + if (entry.isAbsolute()) + return entry; + + QByteArray orig = entry.nativeFilePath(); + QByteArray result; + if (orig.isEmpty() || !orig.startsWith('/')) { + QFileSystemEntry cur(currentPath()); + result = cur.nativeFilePath(); + } + if (!orig.isEmpty() && !(orig.length() == 1 && orig[0] == '.')) { + if (!result.isEmpty() && !result.endsWith('/')) + result.append('/'); + result.append(orig); + } + + if (result.length() == 1 && result[0] == '/') + return QFileSystemEntry(result, QFileSystemEntry::FromNativePath()); + const bool isDir = result.endsWith('/'); + + /* as long as QDir::cleanPath() operates on a QString we have to convert to a string here. + * ideally we never convert to a string since that loses information. Please fix after + * we get a QByteArray version of QDir::cleanPath() + */ + QFileSystemEntry resultingEntry(result, QFileSystemEntry::FromNativePath()); + QString stringVersion = QDir::cleanPath(resultingEntry.filePath()); + if (isDir) + stringVersion.append(QLatin1Char('/')); + return QFileSystemEntry(stringVersion); +} + +//static +QString QFileSystemEngine::resolveUserName(uint userId) +{ +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + QVarLengthArray<char, 1024> buf(size_max); +#endif + + struct passwd *pw = 0; +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + struct passwd entry; + getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw); +#else + pw = getpwuid(userId); +#endif + if (pw) + return QFile::decodeName(QByteArray(pw->pw_name)); + return QString(); +} + +//static +QString QFileSystemEngine::resolveGroupName(uint groupId) +{ +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + QVarLengthArray<char, 1024> buf(size_max); +#endif + + struct group *gr = 0; +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + size_max = sysconf(_SC_GETGR_R_SIZE_MAX); + if (size_max == -1) + size_max = 1024; + buf.resize(size_max); + struct group entry; + // Some large systems have more members than the POSIX max size + // Loop over by doubling the buffer size (upper limit 250k) + for (unsigned size = size_max; size < 256000; size += size) + { + buf.resize(size); + // ERANGE indicates that the buffer was too small + if (!getgrgid_r(groupId, &entry, buf.data(), buf.size(), &gr) + || errno != ERANGE) + break; + } +#else + gr = getgrgid(groupId); +#endif + if (gr) + return QFile::decodeName(QByteArray(gr->gr_name)); + return QString(); +} + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) +//static +QString QFileSystemEngine::bundleName(const QFileSystemEntry &entry) +{ + QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(entry.filePath()), + kCFURLPOSIXPathStyle, true); + if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) { + if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) { + if (CFGetTypeID(name) == CFStringGetTypeID()) + return QCFString::toQString((CFStringRef)name); + } + } + return QString(); +} +#endif + +//static +bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + if (what & QFileSystemMetaData::BundleType) { + if (!data.hasFlags(QFileSystemMetaData::DirectoryType)) + what |= QFileSystemMetaData::DirectoryType; + } +#endif + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) \ + && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (what & QFileSystemMetaData::HiddenAttribute) { + // Mac OS >= 10.5: st_flags & UF_HIDDEN + what |= QFileSystemMetaData::PosixStatFlags; + } +#endif + + if (what & QFileSystemMetaData::PosixStatFlags) + what |= QFileSystemMetaData::PosixStatFlags; + + if (what & QFileSystemMetaData::ExistsAttribute) { + // FIXME: Would other queries being performed provide this bit? + what |= QFileSystemMetaData::PosixStatFlags; + } + + data.entryFlags &= ~what; + + const char * nativeFilePath; + int nativeFilePathLength; + { + const QByteArray &path = entry.nativeFilePath(); + nativeFilePath = path.constData(); + nativeFilePathLength = path.size(); + } + + bool entryExists = true; // innocent until proven otherwise + + QT_STATBUF statBuffer; + bool statBufferValid = false; + if (what & QFileSystemMetaData::LinkType) { + if (QT_LSTAT(nativeFilePath, &statBuffer) == 0) { + if (S_ISLNK(statBuffer.st_mode)) { + data.entryFlags |= QFileSystemMetaData::LinkType; + } else { + statBufferValid = true; + data.entryFlags &= ~QFileSystemMetaData::PosixStatFlags; + } + } else { + entryExists = false; + } + + data.knownFlagsMask |= QFileSystemMetaData::LinkType; + } + + if (statBufferValid || (what & QFileSystemMetaData::PosixStatFlags)) { + if (entryExists && !statBufferValid) + statBufferValid = (QT_STAT(nativeFilePath, &statBuffer) == 0); + + if (statBufferValid) + data.fillFromStatBuf(statBuffer); + else { + entryExists = false; + data.creationTime_ = 0; + data.modificationTime_ = 0; + data.accessTime_ = 0; + data.size_ = 0; + data.userId_ = (uint) -2; + data.groupId_ = (uint) -2; + } + + // reset the mask + data.knownFlagsMask |= QFileSystemMetaData::PosixStatFlags + | QFileSystemMetaData::ExistsAttribute; + } + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + if (what & QFileSystemMetaData::AliasType) + { + if (entryExists) { + FSRef fref; + if (FSPathMakeRef((const UInt8 *)nativeFilePath, &fref, NULL) == noErr) { + Boolean isAlias, isFolder; + if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr) { + if (isAlias) + data.entryFlags |= QFileSystemMetaData::AliasType; + } + } + } + data.knownFlagsMask |= QFileSystemMetaData::AliasType; + } +#endif + + if (what & QFileSystemMetaData::UserPermissions) { + // calculate user permissions + + if (entryExists) { + if (what & QFileSystemMetaData::UserReadPermission) { + if (QT_ACCESS(nativeFilePath, R_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + } + if (what & QFileSystemMetaData::UserWritePermission) { + if (QT_ACCESS(nativeFilePath, W_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserWritePermission; + } + if (what & QFileSystemMetaData::UserExecutePermission) { + if (QT_ACCESS(nativeFilePath, X_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserExecutePermission; + } + } + data.knownFlagsMask |= (what & QFileSystemMetaData::UserPermissions); + } + + if (what & QFileSystemMetaData::HiddenAttribute + && !data.isHidden()) { + QString fileName = entry.fileName(); + if ((fileName.size() > 0 && fileName.at(0) == QLatin1Char('.')) + || (entryExists && _q_isMacHidden(nativeFilePath))) + data.entryFlags |= QFileSystemMetaData::HiddenAttribute; + data.knownFlagsMask |= QFileSystemMetaData::HiddenAttribute; + } + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + if (what & QFileSystemMetaData::BundleType) { + if (entryExists && data.isDirectory()) { + QCFType<CFStringRef> path = CFStringCreateWithBytes(0, + (const UInt8*)nativeFilePath, nativeFilePathLength, + kCFStringEncodingUTF8, false); + QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, path, + kCFURLPOSIXPathStyle, true); + + UInt32 type, creator; + if (CFBundleGetPackageInfoInDirectory(url, &type, &creator)) + data.entryFlags |= QFileSystemMetaData::BundleType; + } + + data.knownFlagsMask |= QFileSystemMetaData::BundleType; + } +#endif + + return data.hasFlags(what); +} + +//static +bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) +{ + QString dirName = entry.filePath(); + if (createParents) { + dirName = QDir::cleanPath(dirName); + for (int oldslash = -1, slash=0; slash != -1; oldslash = slash) { + slash = dirName.indexOf(QDir::separator(), oldslash+1); + if (slash == -1) { + if (oldslash == dirName.length()) + break; + slash = dirName.length(); + } + if (slash) { + QByteArray chunk = QFile::encodeName(dirName.left(slash)); + QT_STATBUF st; + if (QT_STAT(chunk, &st) != -1) { + if ((st.st_mode & S_IFMT) != S_IFDIR) + return false; + } else if (QT_MKDIR(chunk, 0777) != 0) { + return false; + } + } + } + return true; + } +#if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s + if (dirName.endsWith(QLatin1Char('/'))) + dirName.chop(1); +#endif + return (QT_MKDIR(QFile::encodeName(dirName), 0777) == 0); +} + +//static +bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) +{ + if (removeEmptyParents) { + QString dirName = QDir::cleanPath(entry.filePath()); + for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { + QByteArray chunk = QFile::encodeName(dirName.left(slash)); + QT_STATBUF st; + if (QT_STAT(chunk, &st) != -1) { + if ((st.st_mode & S_IFMT) != S_IFDIR) + return false; + if (::rmdir(chunk) != 0) + return oldslash != 0; + } else { + return false; + } + slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); + } + return true; + } + return rmdir(QFile::encodeName(entry.filePath())) == 0; +} + +//static +bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + if (::symlink(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; +} + +//static +bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + Q_UNUSED(source); + Q_UNUSED(target); + error = QSystemError(ENOSYS, QSystemError::StandardLibraryError); //Function not implemented + return false; +} + +//static +bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; +} + +//static +bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) +{ + if (unlink(entry.nativeFilePath().constData()) == 0) + return true; + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; + +} + +//static +bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, QFileSystemMetaData *data) +{ + mode_t mode = 0; + if (permissions & QFile::ReadOwner) + mode |= S_IRUSR; + if (permissions & QFile::WriteOwner) + mode |= S_IWUSR; + if (permissions & QFile::ExeOwner) + mode |= S_IXUSR; + if (permissions & QFile::ReadUser) + mode |= S_IRUSR; + if (permissions & QFile::WriteUser) + mode |= S_IWUSR; + if (permissions & QFile::ExeUser) + mode |= S_IXUSR; + if (permissions & QFile::ReadGroup) + mode |= S_IRGRP; + if (permissions & QFile::WriteGroup) + mode |= S_IWGRP; + if (permissions & QFile::ExeGroup) + mode |= S_IXGRP; + if (permissions & QFile::ReadOther) + mode |= S_IROTH; + if (permissions & QFile::WriteOther) + mode |= S_IWOTH; + if (permissions & QFile::ExeOther) + mode |= S_IXOTH; + + bool success = ::chmod(entry.nativeFilePath().constData(), mode) == 0; + if (success && data) { + data->entryFlags &= ~QFileSystemMetaData::Permissions; + data->entryFlags |= QFileSystemMetaData::MetaDataFlag(uint(permissions)); + data->knownFlagsMask |= QFileSystemMetaData::Permissions; + } + if (!success) + error = QSystemError(errno, QSystemError::StandardLibraryError); + return success; +} + +QString QFileSystemEngine::homePath() +{ + QString home = QFile::decodeName(qgetenv("HOME")); + if (home.isNull()) + home = rootPath(); + return QDir::cleanPath(home); +} + +QString QFileSystemEngine::rootPath() +{ + return QLatin1String("/"); +} + +QString QFileSystemEngine::tempPath() +{ +#ifdef QT_UNIX_TEMP_PATH_OVERRIDE + return QLatin1String(QT_UNIX_TEMP_PATH_OVERRIDE); +#else + QString temp = QFile::decodeName(qgetenv("TMPDIR")); + if (temp.isEmpty()) + temp = QLatin1String("/tmp/"); + return QDir::cleanPath(temp); +#endif +} + +bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &path) +{ + int r; + r = QT_CHDIR(path.nativeFilePath()); + return r >= 0; +} + +QFileSystemEntry QFileSystemEngine::currentPath() +{ + QFileSystemEntry result; + QT_STATBUF st; + if (QT_STAT(".", &st) == 0) { +#if defined(__GLIBC__) && !defined(PATH_MAX) + char *currentName = ::get_current_dir_name(); + if (currentName) { + result = QFile::decodeName(QByteArray(currentName)); + ::free(currentName); + } +#else + char currentName[PATH_MAX+1]; + if (::getcwd(currentName, PATH_MAX)) + result = QFileSystemEntry(QByteArray(currentName), QFileSystemEntry::FromNativePath()); +# if defined(QT_DEBUG) + if (result.isEmpty()) + qWarning("QFSFileEngine::currentPath: getcwd() failed"); +# endif +#endif + } else { +# if defined(QT_DEBUG) + qWarning("QFSFileEngine::currentPath: stat(\".\") failed"); +# endif + } + return result; +} +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp new file mode 100644 index 0000000..82c6eba --- /dev/null +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -0,0 +1,1218 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemengine_p.h" + +#define _POSIX_ +#include "qplatformdefs.h" +#include "qabstractfileengine.h" +#include "private/qfsfileengine_p.h" +#include <private/qsystemlibrary_p.h> +#include <qdebug.h> + +#include "qfile.h" +#include "qdir.h" +#include "private/qmutexpool_p.h" +#include "qvarlengtharray.h" +#include "qdatetime.h" +#include "qt_windows.h" + +#if !defined(Q_OS_WINCE) +# include <sys/types.h> +# include <direct.h> +# include <winioctl.h> +#else +# include <types.h> +#endif +#include <objbase.h> +#include <shlobj.h> +#include <initguid.h> +#include <accctrl.h> +#include <ctype.h> +#include <limits.h> +#define SECURITY_WIN32 +#include <security.h> + +#ifndef SPI_GETPLATFORMTYPE +#define SPI_GETPLATFORMTYPE 257 +#endif + +#ifndef PATH_MAX +#define PATH_MAX FILENAME_MAX +#endif + +#ifndef _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef __int64 intptr_t; +#else +#ifdef _W64 +typedef _W64 int intptr_t; +#else +typedef INT_PTR intptr_t; +#endif +#endif +#define _INTPTR_T_DEFINED +#endif + +#ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES (DWORD (-1)) +#endif + +#if !defined(Q_OS_WINCE) +# if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +# endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) + +# ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +# define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384 +# endif +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +# endif +# ifndef FSCTL_GET_REPARSE_POINT +# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +# endif +#endif // !defined(Q_OS_WINCE) + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0; + +#if defined(Q_OS_WINCE) +static QString qfsPrivateCurrentDir = QLatin1String(""); +// As none of the functions we try to resolve do exist on Windows CE +// we use QT_NO_LIBRARY to shorten everything up a little bit. +#define QT_NO_LIBRARY 1 +#endif + +#if !defined(QT_NO_LIBRARY) +QT_BEGIN_INCLUDE_NAMESPACE +typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*); +static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0; +typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE); +static PtrLookupAccountSidW ptrLookupAccountSidW = 0; +typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID); +static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0; +typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK); +static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0; +static TRUSTEE_W currentUserTrusteeW; +static TRUSTEE_W worldTrusteeW; + +typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); +static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0; +typedef BOOL (WINAPI *PtrGetVolumePathNamesForVolumeNameW)(LPCWSTR,LPWSTR,DWORD,PDWORD); +static PtrGetVolumePathNamesForVolumeNameW ptrGetVolumePathNamesForVolumeNameW = 0; +QT_END_INCLUDE_NAMESPACE + + +static void resolveLibs() +{ + static bool triedResolve = false; + if (!triedResolve) { + // need to resolve the security info functions + + // protect initialization +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + // check triedResolve again, since another thread may have already + // done the initialization + if (triedResolve) { + // another thread did initialize the security function pointers, + // so we shouldn't do it again. + return; + } +#endif + + triedResolve = true; +#if !defined(Q_OS_WINCE) + HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32"); + if (advapiHnd) { + ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW"); + ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW"); + ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW"); + ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW"); + } + if (ptrBuildTrusteeWithSidW) { + // Create TRUSTEE for current user + HANDLE hnd = ::GetCurrentProcess(); + HANDLE token = 0; + if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) { + TOKEN_USER tu; + DWORD retsize; + if (::GetTokenInformation(token, TokenUser, &tu, sizeof(tu), &retsize)) + ptrBuildTrusteeWithSidW(¤tUserTrusteeW, tu.User.Sid); + ::CloseHandle(token); + } + + typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*); + PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid"); + typedef PVOID (WINAPI *PtrFreeSid)(PSID); + PtrFreeSid ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid"); + if (ptrAllocateAndInitializeSid && ptrFreeSid) { + // Create TRUSTEE for Everyone (World) + SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY }; + PSID pWorld = 0; + if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pWorld)) + ptrBuildTrusteeWithSidW(&worldTrusteeW, pWorld); + ptrFreeSid(pWorld); + } + } + HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv"); + if (userenvHnd) + ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW"); + HINSTANCE kernel32 = LoadLibrary(L"kernel32"); + if(kernel32) + ptrGetVolumePathNamesForVolumeNameW = (PtrGetVolumePathNamesForVolumeNameW)GetProcAddress(kernel32, "GetVolumePathNamesForVolumeNameW"); +#endif + } +} +#endif // QT_NO_LIBRARY + +typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD); +static PtrNetShareEnum ptrNetShareEnum = 0; +typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID); +static PtrNetApiBufferFree ptrNetApiBufferFree = 0; +typedef struct _SHARE_INFO_1 { + LPWSTR shi1_netname; + DWORD shi1_type; + LPWSTR shi1_remark; +} SHARE_INFO_1; + + +static bool resolveUNCLibs() +{ + static bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + if (triedResolve) { + return ptrNetShareEnum && ptrNetApiBufferFree; + } +#endif + triedResolve = true; +#if !defined(Q_OS_WINCE) + HINSTANCE hLib = QSystemLibrary::load(L"Netapi32"); + if (hLib) { + ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum"); + if (ptrNetShareEnum) + ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree"); + } +#endif + } + return ptrNetShareEnum && ptrNetApiBufferFree; +} + +static QString readSymLink(const QFileSystemEntry &link) +{ + QString result; +#if !defined(Q_OS_WINCE) + HANDLE handle = CreateFile((wchar_t*)link.nativeFilePath().utf16(), + FILE_READ_EA, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + 0); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)qMalloc(bufsize); + DWORD retsize = 0; + if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) { + if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + int length = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + const wchar_t* PathBuffer = &rdb->MountPointReparseBuffer.PathBuffer[offset]; + result = QString::fromWCharArray(PathBuffer, length); + } else if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); + const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset]; + result = QString::fromWCharArray(PathBuffer, length); + } + // cut-off "//?/" and "/??/" + if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\')) + result = result.mid(4); + } + qFree(rdb); + CloseHandle(handle); + +#if !defined(QT_NO_LIBRARY) + resolveLibs(); + if (ptrGetVolumePathNamesForVolumeNameW) { + QRegExp matchVolName(QLatin1String("^Volume\\{([a-z]|[0-9]|-)+\\}\\\\"), Qt::CaseInsensitive); + if(matchVolName.indexIn(result) == 0) { + DWORD len; + wchar_t buffer[MAX_PATH]; + QString volumeName = result.mid(0, matchVolName.matchedLength()).prepend(QLatin1String("\\\\?\\")); + if(ptrGetVolumePathNamesForVolumeNameW((wchar_t*)volumeName.utf16(), buffer, MAX_PATH, &len) != 0) + result.replace(0,matchVolName.matchedLength(), QString::fromWCharArray(buffer)); + } + } +#endif + } +#else + Q_UNUSED(link); +#endif // Q_OS_WINCE + return result; +} + +static QString readLink(const QFileSystemEntry &link) +{ +#if !defined(Q_OS_WINCE) +#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS) + QString ret; + + bool neededCoInit = false; + IShellLink *psl; // pointer to IShellLink i/f + WIN32_FIND_DATA wfd; + wchar_t szGotPath[MAX_PATH]; + + // Get pointer to the IShellLink interface. + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl); + + if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized + neededCoInit = true; + CoInitialize(NULL); + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLink, (LPVOID *)&psl); + } + if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. + IPersistFile *ppf; + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); + if (SUCCEEDED(hres)) { + hres = ppf->Load((LPOLESTR)link.nativeFilePath().utf16(), STGM_READ); + //The original path of the link is retrieved. If the file/folder + //was moved, the return value still have the old path. + if (SUCCEEDED(hres)) { + if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR) + ret = QString::fromWCharArray(szGotPath); + } + ppf->Release(); + } + psl->Release(); + } + if (neededCoInit) + CoUninitialize(); + + return ret; +#else + Q_UNUSED(link); + return QString(); +#endif // QT_NO_LIBRARY +#else + wchar_t target[MAX_PATH]; + QString result; + if (SHGetShortcutTarget((wchar_t*)QFileInfo(link.filePath()).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) { + result = QString::fromWCharArray(target); + if (result.startsWith(QLatin1Char('"'))) + result.remove(0,1); + if (result.endsWith(QLatin1Char('"'))) + result.remove(result.size()-1,1); + } + return result; +#endif // Q_OS_WINCE +} + +static bool uncShareExists(const QString &server) +{ + // This code assumes the UNC path is always like \\?\UNC\server... + QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts); + if (parts.count() >= 3) { + QStringList shares; + if (QFileSystemEngine::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(2), &shares)) + return parts.count() >= 4 ? shares.contains(parts.at(3), Qt::CaseInsensitive) : true; + } + return false; +} + +static inline bool getFindData(QString path, WIN32_FIND_DATA &findData) +{ + // path should not end with a trailing slash + while (path.endsWith(QLatin1Char('\\'))) + path.chop(1); + + // can't handle drives + if (!path.endsWith(QLatin1Char(':'))) { + HANDLE hFind = ::FindFirstFile((wchar_t*)path.utf16(), &findData); + if (hFind != INVALID_HANDLE_VALUE) { + ::FindClose(hFind); + return true; + } + } + + return false; +} + +bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list) +{ + if (resolveUNCLibs()) { + SHARE_INFO_1 *BufPtr, *p; + DWORD res; + DWORD er = 0, tr = 0, resume = 0, i; + do { + res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume); + if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) { + p = BufPtr; + for (i = 1; i <= er; ++i) { + if (list && p->shi1_type == 0) + list->append(QString::fromWCharArray(p->shi1_netname)); + p++; + } + } + ptrNetApiBufferFree(BufPtr); + } while (res == ERROR_MORE_DATA); + return res == ERROR_SUCCESS; + } + return false; +} + +void QFileSystemEngine::clearWinStatData(QFileSystemMetaData &data) +{ + data.size_ = 0; + data.fileAttribute_ = 0; + data.creationTime_ = FILETIME(); + data.lastAccessTime_ = FILETIME(); + data.lastWriteTime_ = FILETIME(); +} + +bool QFileSystemEngine::isCaseSensitive() +{ + return false; +} + +//static +QFileSystemEntry QFileSystemEngine::getLinkTarget(const QFileSystemEntry &link, + QFileSystemMetaData &data) +{ + if (data.missingFlags(QFileSystemMetaData::LinkType)) + QFileSystemEngine::fillMetaData(link, data, QFileSystemMetaData::LinkType); + + QString ret; + if (data.isLnkFile()) + ret = readLink(link); + else if (data.isLink()) + ret = readSymLink(link); + return QFileSystemEntry(ret); +} + +//static +QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data) +{ + if (data.missingFlags(QFileSystemMetaData::ExistsAttribute)) + QFileSystemEngine::fillMetaData(entry, data, QFileSystemMetaData::ExistsAttribute); + + if (data.exists()) + return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath())); + else + return QFileSystemEntry(); +} + +//static +QString QFileSystemEngine::nativeAbsoluteFilePath(const QString &path) +{ + // can be //server or //server/share + QString absPath; +#if !defined(Q_OS_WINCE) + QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1)); + wchar_t *fileName = 0; + DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); + if (retLen > (DWORD)buf.size()) { + buf.resize(retLen); + retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); + } + if (retLen != 0) + absPath = QString::fromWCharArray(buf.data(), retLen); +#else + if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'))) + absPath = QDir::toNativeSeparators(path); + else + absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path)); +#endif + // This is really ugly, but GetFullPathName strips off whitespace at the end. + // If you for instance write ". " in the lineedit of QFileDialog, + // (which is an invalid filename) this function will strip the space off and viola, + // the file is later reported as existing. Therefore, we re-add the whitespace that + // was at the end of path in order to keep the filename invalid. + if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' ')) + absPath.append(QLatin1Char(' ')); + return absPath; +} + +//static +QFileSystemEntry QFileSystemEngine::absoluteName(const QFileSystemEntry &entry) +{ + QString ret; + + if (!entry.isRelative()) { +#if !defined(Q_OS_WINCE) + if (entry.isAbsolute() + && !entry.filePath().contains(QLatin1String("/../")) + && !entry.filePath().contains(QLatin1String("/./")) + && !entry.filePath().endsWith(QLatin1String("/..")) + && !entry.filePath().endsWith(QLatin1String("/."))) { + ret = entry.filePath(); + } else { + ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(entry.filePath())); + } +#else + ret = entry.filePath(); +#endif + } else { + ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + entry.filePath()); + } + + // The path should be absolute at this point. + // From the docs : + // Absolute paths begin with the directory separator "/" + // (optionally preceded by a drive specification under Windows). + if (ret.at(0) != QLatin1Char('/')) { + Q_ASSERT(ret.length() >= 2); + Q_ASSERT(ret.at(0).isLetter()); + Q_ASSERT(ret.at(1) == QLatin1Char(':')); + + // Force uppercase drive letters. + ret[0] = ret.at(0).toUpper(); + } + return QFileSystemEntry(ret); +} + +//static +QString QFileSystemEngine::owner(const QFileSystemEntry &entry, QAbstractFileEngine::FileOwner own) +{ + QString name; +#if !defined(QT_NO_LIBRARY) + extern int qt_ntfs_permission_lookup; + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { + resolveLibs(); + if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) { + PSID pOwner = 0; + PSECURITY_DESCRIPTOR pSD; + if (ptrGetNamedSecurityInfoW((wchar_t*)entry.nativeFilePath().utf16(), SE_FILE_OBJECT, + own == QAbstractFileEngine::OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION, + own == QAbstractFileEngine::OwnerUser ? &pOwner : 0, own == QAbstractFileEngine::OwnerGroup ? &pOwner : 0, + 0, 0, &pSD) == ERROR_SUCCESS) { + DWORD lowner = 64; + DWORD ldomain = 64; + QVarLengthArray<wchar_t, 64> owner(lowner); + QVarLengthArray<wchar_t, 64> domain(ldomain); + SID_NAME_USE use = SidTypeUnknown; + // First call, to determine size of the strings (with '\0'). + if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, + (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + if (lowner > (DWORD)owner.size()) + owner.resize(lowner); + if (ldomain > (DWORD)domain.size()) + domain.resize(ldomain); + // Second call, try on resized buf-s + if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, + (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { + lowner = 0; + } + } else { + lowner = 0; + } + } + if (lowner != 0) + name = QString::fromWCharArray(owner.data()); + LocalFree(pSD); + } + } + } +#else + Q_UNUSED(own); +#endif + return name; +} + +//static +bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + QAbstractFileEngine::FileFlags ret = 0; + +#if !defined(QT_NO_LIBRARY) + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { + resolveLibs(); + if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) { + enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 }; + + QString fname = entry.filePath(); + PSID pOwner = 0; + PSID pGroup = 0; + PACL pDacl; + PSECURITY_DESCRIPTOR pSD; + DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + &pOwner, &pGroup, &pDacl, 0, &pSD); + if(res == ERROR_SUCCESS) { + ACCESS_MASK access_mask; + TRUSTEE_W trustee; + if (what & QFileSystemMetaData::UserPermissions) { // user + data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; + if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + if(access_mask & WriteMask) + data.entryFlags|= QFileSystemMetaData::UserWritePermission; + if(access_mask & ExecMask) + data.entryFlags|= QFileSystemMetaData::UserExecutePermission; + } + if (what & QFileSystemMetaData::OwnerPermissions) { // owner + data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions; + ptrBuildTrusteeWithSidW(&trustee, pOwner); + if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::OwnerReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::OwnerWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + } + if (what & QFileSystemMetaData::GroupPermissions) { // group + data.knownFlagsMask |= QFileSystemMetaData::GroupPermissions; + ptrBuildTrusteeWithSidW(&trustee, pGroup); + if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::GroupReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::GroupWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::GroupExecutePermission; + } + if (what & QFileSystemMetaData::OtherPermissions) { // other (world) + data.knownFlagsMask |= QFileSystemMetaData::OtherPermissions; + if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS) + access_mask = (ACCESS_MASK)-1; // ### + if(access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::OtherReadPermission; + if(access_mask & WriteMask) + data.entryFlags |= QFileSystemMetaData::OtherWritePermission; + if(access_mask & ExecMask) + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission; + } + LocalFree(pSD); + } + } + } else +#endif + { + //### what to do with permissions if we don't use NTFS + // for now just add all permissions and what about exe missions ?? + // also qt_ntfs_permission_lookup is now not set by default ... should it ? + data.entryFlags |= QFileSystemMetaData::OwnerReadPermission + | QFileSystemMetaData::GroupReadPermission + | QFileSystemMetaData::OtherReadPermission; + + if (!(data.fileAttribute_ & FILE_ATTRIBUTE_READONLY)) { + data.entryFlags |= QFileSystemMetaData::OwnerWritePermission + | QFileSystemMetaData::GroupWritePermission + | QFileSystemMetaData::OtherWritePermission; + } + + QString fname = entry.filePath(); + QString ext = fname.right(4).toLower(); + if (data.isDirectory() || + ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") || + ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) { + data.entryFlags |= QFileSystemMetaData::OwnerExecutePermission | QFileSystemMetaData::GroupExecutePermission + | QFileSystemMetaData::OtherExecutePermission | QFileSystemMetaData::UserExecutePermission; + } + data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions | QFileSystemMetaData::GroupPermissions + | QFileSystemMetaData::OtherPermissions | QFileSystemMetaData::UserExecutePermission; + // calculate user permissions + if (what & QFileSystemMetaData::UserReadPermission) { + if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), R_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + data.knownFlagsMask |= QFileSystemMetaData::UserReadPermission; + } + if (what & QFileSystemMetaData::UserWritePermission) { + if (::_waccess((wchar_t*)entry.nativeFilePath().utf16(), W_OK) == 0) + data.entryFlags |= QFileSystemMetaData::UserWritePermission; + data.knownFlagsMask |= QFileSystemMetaData::UserReadPermission; + } + } + + return data.hasFlags(what); +} + +static bool tryDriveUNCFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data) +{ + bool entryExists = false; + DWORD fileAttrib = 0; +#if !defined(Q_OS_WINCE) + if (fname.isDriveRoot()) { + // a valid drive ?? + DWORD drivesBitmask = ::GetLogicalDrives(); + int drivebit = 1 << (fname.filePath().at(0).toUpper().unicode() - QLatin1Char('A').unicode()); + if (drivesBitmask & drivebit) { + fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM; + entryExists = true; + } + } else { +#endif + const QString &path = fname.nativeFilePath(); + bool is_dir = false; + if (path.startsWith(QLatin1String("\\\\?\\UNC"))) { + // UNC - stat doesn't work for all cases (Windows bug) + int s = path.indexOf(path.at(0),7); + if (s > 0) { + // "\\?\UNC\server\..." + s = path.indexOf(path.at(0),s+1); + if (s > 0) { + // "\\?\UNC\server\share\..." + if (s == path.size() - 1) { + // "\\?\UNC\server\share\" + is_dir = true; + } else { + // "\\?\UNC\server\share\notfound" + } + } else { + // "\\?\UNC\server\share" + is_dir = true; + } + } else { + // "\\?\UNC\server" + is_dir = true; + } + } + if (is_dir && uncShareExists(path)) { + // looks like a UNC dir, is a dir. + fileAttrib = FILE_ATTRIBUTE_DIRECTORY; + entryExists = true; + } +#if !defined(Q_OS_WINCE) + } +#endif + if (entryExists) + data.fillFromFileAttribute(fileAttrib); + return entryExists; +} + +static bool tryFindFallback(const QFileSystemEntry &fname, QFileSystemMetaData &data) +{ + bool filledData = false; + // This assumes the last call to a Windows API failed. + int errorCode = GetLastError(); + if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { + WIN32_FIND_DATA findData; + if (getFindData(fname.nativeFilePath(), findData) + && findData.dwFileAttributes != INVALID_FILE_ATTRIBUTES) { + data.fillFromFindData(findData, true, fname.isDriveRoot()); + filledData = true; + } + } + return filledData; +} + +#if !defined(Q_OS_WINCE) +//static +bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + HANDLE fHandle = (HANDLE)_get_osfhandle(fd); + if (fHandle != INVALID_HANDLE_VALUE) { + return fillMetaData(fHandle, data, what); + } + return false; +} +#endif + +//static +bool QFileSystemEngine::fillMetaData(HANDLE fHandle, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + data.entryFlags &= ~what; + clearWinStatData(data); + BY_HANDLE_FILE_INFORMATION fileInfo; + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + if (GetFileInformationByHandle(fHandle , &fileInfo)) { + data.fillFromFindInfo(fileInfo); + } + SetErrorMode(oldmode); + return data.hasFlags(what); +} + +//static +bool QFileSystemEngine::fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, + QFileSystemMetaData::MetaDataFlags what) +{ + what |= QFileSystemMetaData::WinLnkType | QFileSystemMetaData::WinStatFlags; + data.entryFlags &= ~what; + + QFileSystemEntry fname; + data.knownFlagsMask |= QFileSystemMetaData::WinLnkType; + if(entry.filePath().endsWith(QLatin1String(".lnk"))) { + data.entryFlags |= QFileSystemMetaData::WinLnkType; + fname = QFileSystemEntry(readLink(entry)); + } else { + fname = entry; + } + + if (fname.isEmpty()) { + data.knownFlagsMask |= what; + clearWinStatData(data); + return false; + } + + if (what & QFileSystemMetaData::WinStatFlags) { + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + clearWinStatData(data); + WIN32_FIND_DATA findData; + // The memory structure for WIN32_FIND_DATA is same as WIN32_FILE_ATTRIBUTE_DATA + // for all members used by fillFindData(). + bool ok = ::GetFileAttributesEx((wchar_t*)fname.nativeFilePath().utf16(), GetFileExInfoStandard, + reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA *>(&findData)); + if (ok) { + data.fillFromFindData(findData, false, fname.isDriveRoot()); + } else { + if (!tryFindFallback(fname, data)) + tryDriveUNCFallback(fname, data); + } + SetErrorMode(oldmode); + } + + if (what & QFileSystemMetaData::Permissions) + fillPermissions(fname, data, what); + if ((what & QFileSystemMetaData::LinkType) + && data.missingFlags(QFileSystemMetaData::LinkType)) { + data.knownFlagsMask |= QFileSystemMetaData::LinkType; + if (data.fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) { + WIN32_FIND_DATA findData; + if (getFindData(fname.nativeFilePath(), findData)) + data.fillFromFindData(findData, true); + } + } + data.knownFlagsMask |= what; + return data.hasFlags(what); +} + +static inline bool mkDir(const QString &path) +{ +#if defined(Q_OS_WINCE) + // Unfortunately CreateDirectory returns true for paths longer than + // 256, but does not create a directory. It starts to fail, when + // path length > MAX_PATH, which is 260 usually on CE. + // This only happens on a Windows Mobile device. Windows CE seems + // not to be affected by this. + static int platformId = 0; + if (platformId == 0) { + wchar_t platformString[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) { + if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone")) + platformId = 1; + else + platformId = 2; + } + } + if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256) + return false; +#endif + return ::CreateDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), 0); +} + +static inline bool rmDir(const QString &path) +{ + return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); +} + +static bool isDirPath(const QString &dirPath, bool *existed) +{ + QString path = dirPath; + if (path.length() == 2 && path.at(1) == QLatin1Char(':')) + path += QLatin1Char('\\'); + + DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); + if (fileAttrib == INVALID_FILE_ATTRIBUTES) { + int errorCode = GetLastError(); + if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { + WIN32_FIND_DATA findData; + if (getFindData(QFSFileEnginePrivate::longFileName(path), findData)) + fileAttrib = findData.dwFileAttributes; + } + } + + if (existed) + *existed = fileAttrib != INVALID_FILE_ATTRIBUTES; + + if (fileAttrib == INVALID_FILE_ATTRIBUTES) + return false; + + return fileAttrib & FILE_ATTRIBUTE_DIRECTORY; +} + +//static +bool QFileSystemEngine::createDirectory(const QFileSystemEntry &entry, bool createParents) +{ + QString dirName = entry.filePath(); + if (createParents) { + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); + // We spefically search for / so \ would break it.. + int oldslash = -1; + if (dirName.startsWith(QLatin1String("\\\\"))) { + // Don't try to create the root path of a UNC path; + // CreateDirectory() will just return ERROR_INVALID_NAME. + for (int i = 0; i < dirName.size(); ++i) { + if (dirName.at(i) != QDir::separator()) { + oldslash = i; + break; + } + } + if (oldslash != -1) + oldslash = dirName.indexOf(QDir::separator(), oldslash); + } + for (int slash=0; slash != -1; oldslash = slash) { + slash = dirName.indexOf(QDir::separator(), oldslash+1); + if (slash == -1) { + if (oldslash == dirName.length()) + break; + slash = dirName.length(); + } + if (slash) { + QString chunk = dirName.left(slash); + bool existed = false; + if (!isDirPath(chunk, &existed)) { + if (!existed) { + if (!mkDir(chunk)) + return false; + } else { + return false; + } + } + } + } + return true; + } + return mkDir(entry.filePath()); +} + +//static +bool QFileSystemEngine::removeDirectory(const QFileSystemEntry &entry, bool removeEmptyParents) +{ + QString dirName = entry.filePath(); + if (removeEmptyParents) { + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); + for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { + QString chunk = dirName.left(slash); + if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':')) + break; + if (!isDirPath(chunk, 0)) + return false; + if (!rmDir(chunk)) + return oldslash != 0; + slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); + } + return true; + } + return rmDir(entry.filePath()); +} + +//static +QString QFileSystemEngine::rootPath() +{ +#if defined(Q_OS_WINCE) + QString ret = QLatin1String("/"); +#elif defined(Q_FS_FAT) + QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData()); + if (ret.isEmpty()) + ret = QLatin1String("c:"); + ret.append(QLatin1Char('/')); +#elif defined(Q_OS_OS2EMX) + char dir[4]; + _abspath(dir, QLatin1String("/"), _MAX_PATH); + QString ret(dir); +#endif + return ret; +} + +//static +QString QFileSystemEngine::homePath() +{ + QString ret; +#if !defined(QT_NO_LIBRARY) + resolveLibs(); + if (ptrGetUserProfileDirectoryW) { + HANDLE hnd = ::GetCurrentProcess(); + HANDLE token = 0; + BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token); + if (ok) { + DWORD dwBufferSize = 0; + // First call, to determine size of the strings (with '\0'). + ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize); + if (!ok && dwBufferSize != 0) { // We got the required buffer size + wchar_t *userDirectory = new wchar_t[dwBufferSize]; + // Second call, now we can fill the allocated buffer. + ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize); + if (ok) + ret = QString::fromWCharArray(userDirectory); + delete [] userDirectory; + } + ::CloseHandle(token); + } + } +#endif + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { + ret = QString::fromLocal8Bit(qgetenv("HOME").constData()); + if (ret.isEmpty() || !QFile::exists(ret)) { +#if defined(Q_OS_WINCE) + ret = QLatin1String("\\My Documents"); + if (!QFile::exists(ret)) +#endif + ret = rootPath(); + } + } + } + } + return QDir::fromNativeSeparators(ret); +} + +QString QFileSystemEngine::tempPath() +{ + QString ret; + wchar_t tempPath[MAX_PATH]; + DWORD len = GetTempPath(MAX_PATH, tempPath); + if (len) + ret = QString::fromWCharArray(tempPath, len); + if (!ret.isEmpty()) { + while (ret.endsWith(QLatin1Char('\\'))) + ret.chop(1); + ret = QDir::fromNativeSeparators(ret); + } + if (ret.isEmpty()) { +#if !defined(Q_OS_WINCE) + ret = QLatin1String("c:/tmp"); +#else + ret = QLatin1String("/Temp"); +#endif + } + return ret; +} + +bool QFileSystemEngine::setCurrentPath(const QFileSystemEntry &entry) +{ + QFileSystemMetaData meta; + fillMetaData(entry, meta, QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType); + if(!(meta.exists() && meta.isDirectory())) + return false; + +#if !defined(Q_OS_WINCE) + //TODO: this should really be using nativeFilePath(), but that returns a path in long format \\?\c:\foo + //which causes many problems later on when it's returned through currentPath() + return ::SetCurrentDirectory(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(entry.filePath()).utf16())) != 0; +#else + qfsPrivateCurrentDir = entry.filePath(); + return true; +#endif +} + +QFileSystemEntry QFileSystemEngine::currentPath() +{ + QString ret; +#if !defined(Q_OS_WINCE) + DWORD size = 0; + wchar_t currentName[PATH_MAX]; + size = ::GetCurrentDirectory(PATH_MAX, currentName); + if (size != 0) { + if (size > PATH_MAX) { + wchar_t *newCurrentName = new wchar_t[size]; + if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0) + ret = QString::fromWCharArray(newCurrentName, size); + delete [] newCurrentName; + } else { + ret = QString::fromWCharArray(currentName, size); + } + } + if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) + ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. +#else + Q_UNUSED(fileName); + //TODO - a race condition exists when using currentPath / setCurrentPath from multiple threads + if (qfsPrivateCurrentDir.isEmpty()) + qfsPrivateCurrentDir = QCoreApplication::applicationDirPath(); + + ret = qfsPrivateCurrentDir; +#endif + return QFileSystemEntry(ret, QFileSystemEntry::FromNativePath()); +} + +//static +bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + Q_ASSERT(false); + Q_UNUSED(source) + Q_UNUSED(target) + Q_UNUSED(error) + + return false; // TODO implement; - code needs to be moved from qfsfileengine_win.cpp +} + +//static +bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + bool ret = ::CopyFile((wchar_t*)source.nativeFilePath().utf16(), + (wchar_t*)target.nativeFilePath().utf16(), true) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ + bool ret = ::MoveFile((wchar_t*)source.nativeFilePath().utf16(), + (wchar_t*)target.nativeFilePath().utf16()) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &error) +{ + bool ret = ::DeleteFile((wchar_t*)entry.nativeFilePath().utf16()) != 0; + if(!ret) + error = QSystemError(::GetLastError(), QSystemError::NativeError); + return ret; +} + +//static +bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error, + QFileSystemMetaData *data) +{ + Q_UNUSED(data); + int mode = 0; + + if (permissions & QFile::ReadOwner || permissions & QFile::ReadUser + || permissions & QFile::ReadGroup || permissions & QFile::ReadOther) + mode |= _S_IREAD; + if (permissions & QFile::WriteOwner || permissions & QFile::WriteUser + || permissions & QFile::WriteGroup || permissions & QFile::WriteOther) + mode |= _S_IWRITE; + + if (mode == 0) // not supported + return false; + + bool ret = (::_wchmod((wchar_t*)entry.nativeFilePath().utf16(), mode) == 0); + if(!ret) + error = QSystemError(errno, QSystemError::StandardLibraryError); + return ret; +} + +static inline QDateTime fileTimeToQDateTime(const FILETIME *time) +{ + QDateTime ret; + +#if defined(Q_OS_WINCE) + SYSTEMTIME systime; + FILETIME ftime; + systime.wYear = 1970; + systime.wMonth = 1; + systime.wDay = 1; + systime.wHour = 0; + systime.wMinute = 0; + systime.wSecond = 0; + systime.wMilliseconds = 0; + systime.wDayOfWeek = 4; + SystemTimeToFileTime(&systime, &ftime); + unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime; + FileTimeToSystemTime(time, &systime); + unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime; + unsigned __int64 difftime = acttime - time1970; + difftime /= 10000000; + ret.setTime_t((unsigned int)difftime); +#else + SYSTEMTIME sTime, lTime; + FileTimeToSystemTime(time, &sTime); + SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime); + ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay)); + ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds)); +#endif + + return ret; +} + +QDateTime QFileSystemMetaData::creationTime() const +{ + return fileTimeToQDateTime(&creationTime_); +} +QDateTime QFileSystemMetaData::modificationTime() const +{ + return fileTimeToQDateTime(&lastWriteTime_); +} +QDateTime QFileSystemMetaData::accessTime() const +{ + return fileTimeToQDateTime(&lastAccessTime_); +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystementry.cpp b/src/corelib/io/qfilesystementry.cpp new file mode 100644 index 0000000..ccbb10d --- /dev/null +++ b/src/corelib/io/qfilesystementry.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystementry_p.h" + +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/private/qfsfileengine_p.h> +#ifdef Q_OS_WIN +#include <QtCore/qstringbuilder.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +static bool isUncRoot(const QString &server) +{ + QString localPath = QDir::toNativeSeparators(server); + if (!localPath.startsWith(QLatin1String("\\\\"))) + return false; + + int idx = localPath.indexOf(QLatin1Char('\\'), 2); + if (idx == -1 || idx + 1 == localPath.length()) + return true; + + localPath = localPath.right(localPath.length() - idx - 1).trimmed(); + return localPath.isEmpty(); +} + +static inline QString fixIfRelativeUncPath(const QString &path) +{ + QString currentPath = QDir::currentPath(); + if (currentPath.startsWith(QLatin1String("//"))) + return currentPath % QChar(QLatin1Char('/')) % path; + return path; +} +#endif + +QFileSystemEntry::QFileSystemEntry() + : m_lastSeparator(0), + m_firstDotInFileName(0), + m_lastDotInFileName(0) +{ +} + +/*! + \internal + Use this constructor when the path is supplied by user code, as it may contain a mix + of '/' and the native separator. + */ +QFileSystemEntry::QFileSystemEntry(const QString &filePath) + : m_filePath(QDir::fromNativeSeparators(filePath)), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +/*! + \internal + Use this constructor when the path is guaranteed to be in internal format, i.e. all + directory separators are '/' and not the native separator. + */ +QFileSystemEntry::QFileSystemEntry(const QString &filePath, FromInternalPath /* dummy */) + : m_filePath(filePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +/*! + \internal + Use this constructor when the path comes from a native API + */ +QFileSystemEntry::QFileSystemEntry(const NativePath &nativeFilePath, FromNativePath /* dummy */) + : m_nativeFilePath(nativeFilePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +QFileSystemEntry::QFileSystemEntry(const QString &filePath, const NativePath &nativeFilePath) + : m_filePath(QDir::fromNativeSeparators(filePath)), + m_nativeFilePath(nativeFilePath), + m_lastSeparator(-2), + m_firstDotInFileName(-2), + m_lastDotInFileName(0) +{ +} + +QString QFileSystemEntry::filePath() const +{ + resolveFilePath(); + return m_filePath; +} + +QFileSystemEntry::NativePath QFileSystemEntry::nativeFilePath() const +{ + resolveNativeFilePath(); + return m_nativeFilePath; +} + +void QFileSystemEntry::resolveFilePath() const +{ + if (m_filePath.isEmpty() && !m_nativeFilePath.isEmpty()) { +#if defined(QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16) + m_filePath = QDir::fromNativeSeparators(m_nativeFilePath); +#ifdef Q_OS_WIN + if (m_filePath.startsWith(QLatin1String("//?/UNC/"))) + m_filePath = m_filePath.remove(2,6); + if (m_filePath.startsWith(QLatin1String("//?/"))) + m_filePath = m_filePath.remove(0,4); +#endif +#else + m_filePath = QDir::fromNativeSeparators(QFile::decodeName(m_nativeFilePath)); +#endif + } +} + +void QFileSystemEntry::resolveNativeFilePath() const +{ + if (!m_filePath.isEmpty() && m_nativeFilePath.isEmpty()) { +#ifdef Q_OS_WIN + QString filePath = m_filePath; + if (isRelative()) + filePath = fixIfRelativeUncPath(m_filePath); + m_nativeFilePath = QFSFileEnginePrivate::longFileName(QDir::toNativeSeparators(filePath)); +#elif defined(QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16) + m_nativeFilePath = QDir::toNativeSeparators(m_filePath); +#else + m_nativeFilePath = QFile::encodeName(QDir::toNativeSeparators(m_filePath)); +#endif + } +} + +QString QFileSystemEntry::fileName() const +{ + findLastSeparator(); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2); +#endif + return m_filePath.mid(m_lastSeparator + 1); +} + +QString QFileSystemEntry::path() const +{ + findLastSeparator(); + if (m_lastSeparator == -1) { +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.left(2); +#endif + return QString(QLatin1Char('.')); + } + if (m_lastSeparator == 0) + return QString(QLatin1Char('/')); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (m_lastSeparator == 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.left(m_lastSeparator + 1); +#endif + return m_filePath.left(m_lastSeparator); +} + +QString QFileSystemEntry::baseName() const +{ + findFileNameSeparators(); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2); +#endif + int length = -1; + if (m_firstDotInFileName >= 0) { + length = m_firstDotInFileName; + if (m_lastSeparator != -1) // avoid off by one + length--; + } + return m_filePath.mid(m_lastSeparator + 1, length); +} + +QString QFileSystemEntry::completeBaseName() const +{ + findFileNameSeparators(); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (m_lastSeparator == -1 && m_filePath.length() >= 2 && m_filePath.at(1) == QLatin1Char(':')) + return m_filePath.mid(2); +#endif + int length = -1; + if (m_firstDotInFileName >= 0) { + length = m_firstDotInFileName + m_lastDotInFileName; + if (m_lastSeparator != -1) // avoid off by one + length--; + } + return m_filePath.mid(m_lastSeparator + 1, length); +} + +QString QFileSystemEntry::suffix() const +{ + findFileNameSeparators(); + + if (m_lastDotInFileName == -1) + return QString(); + + return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + m_lastDotInFileName + 1); +} + +QString QFileSystemEntry::completeSuffix() const +{ + findFileNameSeparators(); + if (m_firstDotInFileName == -1) + return QString(); + + return m_filePath.mid(qMax((qint16)0, m_lastSeparator) + m_firstDotInFileName + 1); +} + +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) +bool QFileSystemEntry::isRelative() const +{ + resolveFilePath(); + return (m_filePath.isEmpty() || (!m_filePath.isEmpty() && (m_filePath[0].unicode() != '/') + && (!(m_filePath.length() >= 2 && m_filePath[1].unicode() == ':')))); +} + +bool QFileSystemEntry::isAbsolute() const +{ + resolveFilePath(); + return (!m_filePath.isEmpty() && ((m_filePath.length() >= 3 + && (m_filePath[0].isLetter() && m_filePath[1].unicode() == ':' && m_filePath[2].unicode() == '/')) +#ifdef Q_OS_WIN + || (m_filePath.length() >= 2 && (m_filePath.at(0) == QLatin1Char('/') && m_filePath.at(1) == QLatin1Char('/'))) +#endif + )); +} +#else +bool QFileSystemEntry::isRelative() const +{ + return !isAbsolute(); +} + +bool QFileSystemEntry::isAbsolute() const +{ + resolveFilePath(); + return (!m_filePath.isEmpty() && (m_filePath[0].unicode() == '/')); +} +#endif + +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) +bool QFileSystemEntry::isDriveRoot() const +{ + resolveFilePath(); + return (m_filePath.length() == 3 + && m_filePath.at(0).isLetter() && m_filePath.at(1) == QLatin1Char(':') + && m_filePath.at(2) == QLatin1Char('/')); +} +#endif + +bool QFileSystemEntry::isRoot() const +{ + resolveFilePath(); + if (m_filePath == QLatin1String("/") +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + || isDriveRoot() +#if defined(Q_OS_WIN) + || isUncRoot(m_filePath) +#endif +#endif + ) + return true; + + return false; +} + +bool QFileSystemEntry::isEmpty() const +{ + resolveNativeFilePath(); + return m_nativeFilePath.isEmpty(); +} + +// private methods + +void QFileSystemEntry::findLastSeparator() const +{ + if (m_lastSeparator == -2) { + resolveFilePath(); + m_lastSeparator = -1; + for (int i = m_filePath.size() - 1; i >= 0; --i) { + if (m_filePath[i].unicode() == '/') { + m_lastSeparator = i; + break; + } + } + } +} + +void QFileSystemEntry::findFileNameSeparators() const +{ + if (m_firstDotInFileName == -2) { + resolveFilePath(); + int firstDotInFileName = -1; + int lastDotInFileName = -1; + int lastSeparator = m_lastSeparator; + + int stop; + if (lastSeparator < 0) { + lastSeparator = -1; + stop = 0; + } else { + stop = lastSeparator; + } + + int i = m_filePath.size() - 1; + for (; i >= stop; --i) { + if (m_filePath[i].unicode() == '.') { + firstDotInFileName = lastDotInFileName = i; + break; + } else if (m_filePath[i].unicode() == '/') { + lastSeparator = i; + break; + } + } + + if (lastSeparator != i) { + for (--i; i >= stop; --i) { + if (m_filePath[i].unicode() == '.') + firstDotInFileName = i; + else if (m_filePath[i].unicode() == '/') { + lastSeparator = i; + break; + } + } + } + m_lastSeparator = lastSeparator; + m_firstDotInFileName = firstDotInFileName == -1 ? -1 : firstDotInFileName - qMax(0, lastSeparator); + if (lastDotInFileName == -1) + m_lastDotInFileName = -1; + else if (firstDotInFileName == lastDotInFileName) + m_lastDotInFileName = 0; + else + m_lastDotInFileName = lastDotInFileName - firstDotInFileName; + } +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystementry_p.h b/src/corelib/io/qfilesystementry_p.h new file mode 100644 index 0000000..d4d16d0 --- /dev/null +++ b/src/corelib/io/qfilesystementry_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMENTRY_P_H_INCLUDED +#define QFILESYSTEMENTRY_P_H_INCLUDED + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> + +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) +#define QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16 +#endif + +QT_BEGIN_NAMESPACE + +class QFileSystemEntry +{ +public: + +#ifndef QFILESYSTEMENTRY_NATIVE_PATH_IS_UTF16 + typedef QByteArray NativePath; +#else + typedef QString NativePath; +#endif + struct FromNativePath{}; + struct FromInternalPath{}; + + QFileSystemEntry(); + explicit QFileSystemEntry(const QString &filePath); + + QFileSystemEntry(const QString &filePath, FromInternalPath dummy); + QFileSystemEntry(const NativePath &nativeFilePath, FromNativePath dummy); + QFileSystemEntry(const QString &filePath, const NativePath &nativeFilePath); + + QString filePath() const; + QString fileName() const; + QString path() const; + NativePath nativeFilePath() const; + QString baseName() const; + QString completeBaseName() const; + QString suffix() const; + QString completeSuffix() const; + bool isAbsolute() const; + bool isRelative() const; + +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + bool isDriveRoot() const; +#endif + bool isRoot() const; + + bool isEmpty() const; + void clear() + { + *this = QFileSystemEntry(); + } + +private: + // creates the QString version out of the bytearray version + void resolveFilePath() const; + // creates the bytearray version out of the QString version + void resolveNativeFilePath() const; + // resolves the separator + void findLastSeparator() const; + // resolves the dots and the separator + void findFileNameSeparators() const; + + mutable QString m_filePath; // always has slashes as separator + mutable NativePath m_nativeFilePath; // native encoding and separators + + mutable qint16 m_lastSeparator; // index in m_filePath of last separator + mutable qint16 m_firstDotInFileName; // index after m_filePath for first dot (.) + mutable qint16 m_lastDotInFileName; // index after m_firstDotInFileName for last dot (.) +}; + +QT_END_NAMESPACE + +#endif // include guard diff --git a/src/corelib/io/qfilesystemiterator_p.h b/src/corelib/io/qfilesystemiterator_p.h new file mode 100644 index 0000000..fb8bfe6 --- /dev/null +++ b/src/corelib/io/qfilesystemiterator_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMITERATOR_P_H_INCLUDED +#define QFILESYSTEMITERATOR_P_H_INCLUDED + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_FILESYSTEMITERATOR + +#include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> +#include <QtCore/qstringlist.h> + +#include <QtCore/private/qfilesystementry_p.h> +#include <QtCore/private/qfilesystemmetadata_p.h> + +// Platform-specific headers +#if defined(Q_OS_WIN) +#elif defined (Q_OS_SYMBIAN) +#include <f32file.h> +#else +#include <QtCore/qscopedpointer.h> +#endif + +QT_BEGIN_NAMESPACE + +class QFileSystemIterator +{ +public: + QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags + = QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + ~QFileSystemIterator(); + + bool advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData); + +private: + QFileSystemEntry::NativePath nativePath; + + // Platform-specific data +#if defined(Q_OS_WIN) + QFileSystemEntry::NativePath dirPath; + HANDLE findFileHandle; + QStringList uncShares; + bool uncFallback; + int uncShareIndex; + bool onlyDirs; +#elif defined (Q_OS_SYMBIAN) + RDir dirHandle; + TEntryArray entries; + TInt lastError; + TInt entryIndex; +#else + QT_DIR *dir; + QT_DIRENT *dirEntry; +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + // for readdir_r + QScopedPointer<QT_DIRENT, QScopedPointerPodDeleter> mt_file; +#endif + int lastError; +#endif + + Q_DISABLE_COPY(QFileSystemIterator) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_FILESYSTEMITERATOR + +#endif // include guard diff --git a/src/corelib/io/qfilesystemiterator_symbian.cpp b/src/corelib/io/qfilesystemiterator_symbian.cpp new file mode 100644 index 0000000..23c726a --- /dev/null +++ b/src/corelib/io/qfilesystemiterator_symbian.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemiterator_p.h" +#include "qfilesystemengine_p.h" +#include <QtCore/private/qcore_symbian_p.h> + +QT_BEGIN_NAMESPACE + +QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &path, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags iteratorFlags) + : lastError(KErrNone), entryIndex(-1) +{ + RFs& fs = qt_s60GetRFs(); + + nativePath = path.nativeFilePath(); + if (!nativePath.endsWith(QLatin1Char('\\'))) + nativePath.append(QLatin1Char('\\')); + + QString absPath = QFileSystemEngine::absoluteName(path).nativeFilePath(); + + if (!absPath.endsWith(QLatin1Char('\\'))) + absPath.append(QLatin1Char('\\')); + + int pathLen = absPath.length(); + if (pathLen > KMaxFileName) { + lastError = KErrBadName; + return; + } + + //set up server side filtering to reduce IPCs + //RDir won't accept all valid name filters e.g. "*. bar" + if (nameFilters.count() == 1 && !(filters & QDir::AllDirs) && iteratorFlags + == QDirIterator::NoIteratorFlags && pathLen + nameFilters[0].length() + <= KMaxFileName) { + //server side supports one mask - skip this for recursive mode or if only files should be filtered + absPath.append(nameFilters[0]); + } + + TUint symbianMask = 0; + if ((filters & QDir::Dirs) || (filters & QDir::AllDirs) || (iteratorFlags + & QDirIterator::Subdirectories)) + symbianMask |= KEntryAttDir; //include directories + if (filters & QDir::Hidden) + symbianMask |= KEntryAttHidden; + if (filters & QDir::System) + symbianMask |= KEntryAttSystem; + if (((filters & QDir::Files) == 0) && symbianMask == KEntryAttDir) + symbianMask |= KEntryAttMatchExclusive; //exclude non-directories + else if (symbianMask == 0) { + if ((filters & QDir::PermissionMask) == QDir::Writable) + symbianMask = KEntryAttMatchExclude | KEntryAttReadOnly; + else if ((filters & QDir::PermissionMask) == QDir::Readable) + symbianMask = KEntryAttMatchExclusive | KEntryAttReadOnly; + } + + lastError = dirHandle.Open(fs, qt_QString2TPtrC(absPath), symbianMask); +} + +QFileSystemIterator::~QFileSystemIterator() +{ + dirHandle.Close(); +} + +bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) +{ + //1st time, lastError is result of dirHandle.Open(), entries.Count() is 0 and entryIndex is -1 so initial read is triggered + //subsequent times, read is triggered each time we reach the end of the entry list + //final time, lastError is KErrEof so we don't need to read anymore. + ++entryIndex; + if (lastError == KErrNone && entryIndex >= entries.Count()) { + lastError = dirHandle.Read(entries); + entryIndex = 0; + } + + //each call to advance() gets the next entry from the entry list. + //from the final (or only) read call, KErrEof is returned together with a full buffer so we still need to go through the list + if ((lastError == KErrNone || lastError == KErrEof) && entryIndex < entries.Count()) { + Q_ASSERT(entryIndex >= 0); + const TEntry &entry(entries[entryIndex]); + fileEntry = QFileSystemEntry(nativePath + qt_TDesC2QString(entry.iName), QFileSystemEntry::FromNativePath()); + metaData.fillFromTEntry(entry); + return true; + } + + //TODO: error reporting, to allow user to distinguish empty directory from error condition. + + return false; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemiterator_unix.cpp b/src/corelib/io/qfilesystemiterator_unix.cpp new file mode 100644 index 0000000..3d6012b --- /dev/null +++ b/src/corelib/io/qfilesystemiterator_unix.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qfilesystemiterator_p.h" + +#ifndef QT_NO_FILESYSTEMITERATOR + +#include <stdlib.h> +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags) + : nativePath(entry.nativeFilePath()) + , dir(0) + , dirEntry(0) + , lastError(0) +{ + Q_UNUSED(filters) + Q_UNUSED(nameFilters) + Q_UNUSED(flags) + + if ((dir = QT_OPENDIR(nativePath.constData())) == 0) { + lastError = errno; + } else { + + if (!nativePath.endsWith('/')) + nativePath.append('/'); + +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + // ### Race condition; we should use fpathconf and dirfd(). + size_t maxPathName = ::pathconf(nativePath.constData(), _PC_NAME_MAX); + if (maxPathName == size_t(-1)) + maxPathName = FILENAME_MAX; + maxPathName += sizeof(QT_DIRENT) + 1; + + QT_DIRENT *p = reinterpret_cast<QT_DIRENT*>(::malloc(maxPathName)); + Q_CHECK_PTR(p); + + mt_file.reset(p); +#endif + } +} + +QFileSystemIterator::~QFileSystemIterator() +{ + if (dir) + QT_CLOSEDIR(dir); +} + +bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) +{ + if (!dir) + return false; + +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) + lastError = QT_READDIR_R(dir, mt_file.data(), &dirEntry); + if (lastError) + return false; +#else + // ### add local lock to prevent breaking reentrancy + dirEntry = QT_READDIR(dir); +#endif // _POSIX_THREAD_SAFE_FUNCTIONS + + if (dirEntry) { + fileEntry = QFileSystemEntry(nativePath + QByteArray(dirEntry->d_name), QFileSystemEntry::FromNativePath()); + metaData.fillFromDirEnt(*dirEntry); + return true; + } + + lastError = errno; + return false; +} + +QT_END_NAMESPACE + +#endif // QT_NO_FILESYSTEMITERATOR diff --git a/src/corelib/io/qfilesystemiterator_win.cpp b/src/corelib/io/qfilesystemiterator_win.cpp new file mode 100644 index 0000000..0e94130 --- /dev/null +++ b/src/corelib/io/qfilesystemiterator_win.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if _WIN32_WINNT < 0x0500 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include "qfilesystemiterator_p.h" +#include "qfilesystemengine_p.h" +#include "qplatformdefs.h" + +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +bool done = true; + +QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, + const QStringList &nameFilters, QDirIterator::IteratorFlags flags) + : nativePath(entry.nativeFilePath()) + , dirPath(entry.filePath()) + , findFileHandle(INVALID_HANDLE_VALUE) + , uncFallback(false) + , uncShareIndex(0) + , onlyDirs(false) +{ + Q_UNUSED(nameFilters) + Q_UNUSED(flags) + if (nativePath.endsWith(QLatin1String(".lnk"))) { + QFileSystemMetaData metaData; + QFileSystemEntry link = QFileSystemEngine::getLinkTarget(entry, metaData); + nativePath = link.nativeFilePath(); + } + if (!nativePath.endsWith(QLatin1Char('\\'))) + nativePath.append(QLatin1Char('\\')); + nativePath.append(QLatin1Char('*')); + if (!dirPath.endsWith(QLatin1Char('/'))) + dirPath.append(QLatin1Char('/')); + if ((filters & (QDir::Dirs|QDir::Drives)) && (!(filters & (QDir::Files)))) + onlyDirs = true; +} + +QFileSystemIterator::~QFileSystemIterator() +{ + if (findFileHandle != INVALID_HANDLE_VALUE) + FindClose(findFileHandle); +} + +bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) +{ + bool haveData = false; + WIN32_FIND_DATA findData; + + if (findFileHandle == INVALID_HANDLE_VALUE && !uncFallback) { + haveData = true; + int infoLevel = 0 ; // FindExInfoStandard; + DWORD dwAdditionalFlags = 0; + if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { + dwAdditionalFlags = 2; // FIND_FIRST_EX_LARGE_FETCH + infoLevel = 1 ; // FindExInfoBasic; + } + int searchOps = 0; // FindExSearchNameMatch + if (onlyDirs) + searchOps = 1 ; // FindExSearchLimitToDirectories + findFileHandle = FindFirstFileEx((const wchar_t *)nativePath.utf16(), FINDEX_INFO_LEVELS(infoLevel), &findData, + FINDEX_SEARCH_OPS(searchOps), 0, dwAdditionalFlags); + if (findFileHandle == INVALID_HANDLE_VALUE) { + if (nativePath.startsWith(QLatin1String("\\\\?\\UNC\\"))) { + QStringList parts = nativePath.split(QLatin1Char('\\'), QString::SkipEmptyParts); + if (parts.count() == 4 && QFileSystemEngine::uncListSharesOnServer( + QLatin1String("\\\\") + parts.at(2), &uncShares)) { + if (uncShares.isEmpty()) + return false; // No shares found in the server + else + uncFallback = true; + } + } + } + } + if (findFileHandle == INVALID_HANDLE_VALUE && !uncFallback) + return false; + // Retrieve the new file information. + if (!haveData) { + if (uncFallback) { + if (++uncShareIndex >= uncShares.count()) + return false; + } else { + if (!FindNextFile(findFileHandle, &findData)) + return false; + } + } + // Create the new file system entry & meta data. + if (uncFallback) { + fileEntry = QFileSystemEntry(dirPath + uncShares.at(uncShareIndex)); + metaData.fillFromFileAttribute(FILE_ATTRIBUTE_DIRECTORY); + return true; + } else { + QString fileName = QString::fromWCharArray(findData.cFileName); + fileEntry = QFileSystemEntry(dirPath + fileName); + metaData = QFileSystemMetaData(); + if (!fileName.endsWith(QLatin1String(".lnk"))) { + metaData.fillFromFindData(findData, true); + } + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h new file mode 100644 index 0000000..f7f1fa1 --- /dev/null +++ b/src/corelib/io/qfilesystemmetadata_p.h @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMMETADATA_P_H_INCLUDED +#define QFILESYSTEMMETADATA_P_H_INCLUDED + +// +// 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 "qplatformdefs.h" +#include <QtCore/qglobal.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qabstractfileengine.h> + +// Platform-specific includes +#if defined(Q_OS_WIN) +#ifndef IO_REPARSE_TAG_SYMLINK +#define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +#endif +#elif defined(Q_OS_SYMBIAN) +#include <f32file.h> +#include <QtCore/private/qdatetime_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QFileSystemEngine; + +class QFileSystemMetaData +{ +public: + QFileSystemMetaData() + : knownFlagsMask(0) + { + } + + enum MetaDataFlag { + // Permissions, overlaps with QFile::Permissions + OtherReadPermission = 0x00000004, OtherWritePermission = 0x00000002, OtherExecutePermission = 0x00000001, + GroupReadPermission = 0x00000040, GroupWritePermission = 0x00000020, GroupExecutePermission = 0x00000010, + UserReadPermission = 0x00000400, UserWritePermission = 0x00000200, UserExecutePermission = 0x00000100, + OwnerReadPermission = 0x00004000, OwnerWritePermission = 0x00002000, OwnerExecutePermission = 0x00001000, + + OtherPermissions = OtherReadPermission | OtherWritePermission | OtherExecutePermission, + GroupPermissions = GroupReadPermission | GroupWritePermission | GroupExecutePermission, + UserPermissions = UserReadPermission | UserWritePermission | UserExecutePermission, + OwnerPermissions = OwnerReadPermission | OwnerWritePermission | OwnerExecutePermission, + + ReadPermissions = OtherReadPermission | GroupReadPermission | UserReadPermission | OwnerReadPermission, + WritePermissions = OtherWritePermission | GroupWritePermission | UserWritePermission | OwnerWritePermission, + ExecutePermissions = OtherExecutePermission | GroupExecutePermission | UserExecutePermission | OwnerExecutePermission, + + Permissions = OtherPermissions | GroupPermissions | UserPermissions | OwnerPermissions, + + // Type +#ifdef Q_OS_SYMBIAN + LinkType = 0, +#else + LinkType = 0x00010000, +#endif + FileType = 0x00020000, + DirectoryType = 0x00040000, +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) + BundleType = 0x00080000, + AliasType = 0x08000000, +#else + BundleType = 0x0, + AliasType = 0x0, +#endif +#if defined(Q_OS_WIN) + WinLnkType = 0x08000000, // Note: Uses the same position for AliasType on Mac +#else + WinLnkType = 0x0, +#endif + SequentialType = 0x00800000, // Note: overlaps with QAbstractFileEngine::RootFlag + + LegacyLinkType = LinkType | AliasType | WinLnkType, + + Type = LinkType | FileType | DirectoryType | BundleType | SequentialType | AliasType, + + // Attributes + HiddenAttribute = 0x00100000, + SizeAttribute = 0x00200000, // Note: overlaps with QAbstractFileEngine::LocalDiskFlag + ExistsAttribute = 0x00400000, + + Attributes = HiddenAttribute | SizeAttribute | ExistsAttribute, + + // Times + CreationTime = 0x01000000, // Note: overlaps with QAbstractFileEngine::Refresh + ModificationTime = 0x02000000, + AccessTime = 0x04000000, + + Times = CreationTime | ModificationTime | AccessTime, + + // Owner IDs + UserId = 0x10000000, + GroupId = 0x20000000, + + OwnerIds = UserId | GroupId, + + PosixStatFlags = QFileSystemMetaData::OtherPermissions + | QFileSystemMetaData::GroupPermissions + | QFileSystemMetaData::OwnerPermissions + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::SizeAttribute + | QFileSystemMetaData::Times + | QFileSystemMetaData::OwnerIds, + + SymbianTEntryFlags = QFileSystemMetaData::Permissions + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::SequentialType + | QFileSystemMetaData::Attributes + | QFileSystemMetaData::Times, +#if defined(Q_OS_WIN) + WinStatFlags = QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::HiddenAttribute + | QFileSystemMetaData::ExistsAttribute + | QFileSystemMetaData::SizeAttribute + | QFileSystemMetaData::Times, +#endif + + AllMetaDataFlags = 0xFFFFFFFF + + }; + Q_DECLARE_FLAGS(MetaDataFlags, MetaDataFlag) + + bool hasFlags(MetaDataFlags flags) const + { + return ((knownFlagsMask & flags) == flags); + } + + MetaDataFlags missingFlags(MetaDataFlags flags) + { + return flags & ~knownFlagsMask; + } + + void clear() + { + knownFlagsMask = 0; + } + + void clearFlags(MetaDataFlags flags = AllMetaDataFlags) + { + knownFlagsMask &= ~flags; + } + + bool exists() const { return (entryFlags & ExistsAttribute); } + + bool isLink() const { return (entryFlags & LinkType); } + bool isFile() const { return (entryFlags & FileType); } + bool isDirectory() const { return (entryFlags & DirectoryType); } + bool isBundle() const; + bool isAlias() const; + bool isLegacyLink() const { return (entryFlags & LegacyLinkType); } + bool isSequential() const { return (entryFlags & SequentialType); } + bool isHidden() const { return (entryFlags & HiddenAttribute); } +#if defined(Q_OS_WIN) + bool isLnkFile() const { return (entryFlags & WinLnkType); } +#else + bool isLnkFile() const { return false; } +#endif + + qint64 size() const { return size_; } + + QFile::Permissions permissions() const { return QFile::Permissions(Permissions & entryFlags); } + + QDateTime creationTime() const; + QDateTime modificationTime() const; + QDateTime accessTime() const; + + QDateTime fileTime(QAbstractFileEngine::FileTime time) const; + uint userId() const; + uint groupId() const; + uint ownerId(QAbstractFileEngine::FileOwner owner) const; + +#ifdef Q_OS_UNIX + void fillFromStatBuf(const QT_STATBUF &statBuffer); + void fillFromDirEnt(const QT_DIRENT &statBuffer); +#endif +#ifdef Q_OS_SYMBIAN + void fillFromTEntry(const TEntry& entry); + void fillFromVolumeInfo(const TVolumeInfo& info); +#endif + +#if defined(Q_OS_WIN) + inline void fillFromFileAttribute(DWORD fileAttribute, bool isDriveRoot = false); + inline void fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType = false, bool isDriveRoot = false); + inline void fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo); +#endif +private: + friend class QFileSystemEngine; + + MetaDataFlags knownFlagsMask; + MetaDataFlags entryFlags; + + qint64 size_; + + // Platform-specific data goes here: +#if defined(Q_OS_WIN) + DWORD fileAttribute_; + FILETIME creationTime_; + FILETIME lastAccessTime_; + FILETIME lastWriteTime_; +#elif defined(Q_OS_SYMBIAN) + TTime modificationTime_; +#else + time_t creationTime_; + time_t modificationTime_; + time_t accessTime_; + + uint userId_; + uint groupId_; +#endif + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFileSystemMetaData::MetaDataFlags) + +#if !defined(QWS) && !defined(Q_WS_QPA) && defined(Q_OS_MAC) +inline bool QFileSystemMetaData::isBundle() const { return (entryFlags & BundleType); } +inline bool QFileSystemMetaData::isAlias() const { return (entryFlags & AliasType); } +#else +inline bool QFileSystemMetaData::isBundle() const { return false; } +inline bool QFileSystemMetaData::isAlias() const { return false; } +#endif + +#if (defined(Q_OS_UNIX) && !defined (Q_OS_SYMBIAN)) || defined (Q_OS_WIN) +inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime time) const +{ + switch (time) { + case QAbstractFileEngine::ModificationTime: + return modificationTime(); + + case QAbstractFileEngine::AccessTime: + return accessTime(); + + case QAbstractFileEngine::CreationTime: + return creationTime(); + } + + return QDateTime(); +} +#endif + +#if defined(Q_OS_UNIX) && !defined (Q_OS_SYMBIAN) +inline QDateTime QFileSystemMetaData::creationTime() const { return QDateTime::fromTime_t(creationTime_); } +inline QDateTime QFileSystemMetaData::modificationTime() const { return QDateTime::fromTime_t(modificationTime_); } +inline QDateTime QFileSystemMetaData::accessTime() const { return QDateTime::fromTime_t(accessTime_); } + +inline uint QFileSystemMetaData::userId() const { return userId_; } +inline uint QFileSystemMetaData::groupId() const { return groupId_; } + +inline uint QFileSystemMetaData::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + if (owner == QAbstractFileEngine::OwnerUser) + return userId(); + else + return groupId(); +} +#endif + +#ifdef Q_OS_SYMBIAN +inline QDateTime QFileSystemMetaData::creationTime() const { return modificationTime(); } +inline QDateTime QFileSystemMetaData::modificationTime() const { return qt_symbian_TTime_To_QDateTime(modificationTime_); } +inline QDateTime QFileSystemMetaData::accessTime() const { return modificationTime(); } + +inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime time) const +{ + Q_UNUSED(time); + return modificationTime(); +} +inline uint QFileSystemMetaData::userId() const { return (uint) -2; } +inline uint QFileSystemMetaData::groupId() const { return (uint) -2; } +inline uint QFileSystemMetaData::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + Q_UNUSED(owner); + return (uint) -2; +} +#endif + +#if defined(Q_OS_WIN) +inline uint QFileSystemMetaData::userId() const { return (uint) -2; } +inline uint QFileSystemMetaData::groupId() const { return (uint) -2; } +inline uint QFileSystemMetaData::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + if (owner == QAbstractFileEngine::OwnerUser) + return userId(); + else + return groupId(); +} + +inline void QFileSystemMetaData::fillFromFileAttribute(DWORD fileAttribute,bool isDriveRoot) +{ + fileAttribute_ = fileAttribute; + // Ignore the hidden attribute for drives. + if (!isDriveRoot && (fileAttribute_ & FILE_ATTRIBUTE_HIDDEN)) + entryFlags |= HiddenAttribute; + entryFlags |= ((fileAttribute & FILE_ATTRIBUTE_DIRECTORY) ? DirectoryType: FileType); + entryFlags |= ExistsAttribute; + knownFlagsMask |= FileType | DirectoryType | HiddenAttribute | ExistsAttribute; +} + +inline void QFileSystemMetaData::fillFromFindData(WIN32_FIND_DATA &findData, bool setLinkType, bool isDriveRoot) +{ + fillFromFileAttribute(findData.dwFileAttributes, isDriveRoot); + creationTime_ = findData.ftCreationTime; + lastAccessTime_ = findData.ftLastAccessTime; + lastWriteTime_ = findData.ftLastWriteTime; + if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) { + size_ = 0; + } else { + size_ = findData.nFileSizeHigh; + size_ <<= 32; + size_ += findData.nFileSizeLow; + } + knownFlagsMask |= Times | SizeAttribute; + if (setLinkType) { + knownFlagsMask |= LinkType; + entryFlags &= ~LinkType; +#if !defined(Q_OS_WINCE) + if ((fileAttribute_ & FILE_ATTRIBUTE_REPARSE_POINT) + && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK + || findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)) { + entryFlags |= LinkType; + } +#endif + + } +} + +inline void QFileSystemMetaData::fillFromFindInfo(BY_HANDLE_FILE_INFORMATION &fileInfo) +{ + fillFromFileAttribute(fileInfo.dwFileAttributes); + creationTime_ = fileInfo.ftCreationTime; + lastAccessTime_ = fileInfo.ftLastAccessTime; + lastWriteTime_ = fileInfo.ftLastWriteTime; + if (fileAttribute_ & FILE_ATTRIBUTE_DIRECTORY) { + size_ = 0; + } else { + size_ = fileInfo.nFileSizeHigh; + size_ <<= 32; + size_ += fileInfo.nFileSizeLow; + } + knownFlagsMask |= Times | SizeAttribute; +} +#endif + +QT_END_NAMESPACE + +#endif // include guard diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index e45c972..e0f2f44 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -198,7 +198,7 @@ QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &path void QPollingFileSystemWatcherEngine::stop() { - QMetaObject::invokeMethod(this, "quit"); + quit(); } void QPollingFileSystemWatcherEngine::timeout() @@ -432,11 +432,6 @@ QFileSystemWatcher::QFileSystemWatcher(const QStringList &paths, QObject *parent /*! Destroys the file system watcher. - - \note To avoid deadlocks on shutdown, all instances of QFileSystemWatcher - need to be destroyed before QCoreApplication. Note that passing - QCoreApplication::instance() as the parent object when creating - QFileSystemWatcher is not sufficient. */ QFileSystemWatcher::~QFileSystemWatcher() { diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.cpp b/src/corelib/io/qfilesystemwatcher_fsevents.cpp index 20846da..19b720c 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents.cpp +++ b/src/corelib/io/qfilesystemwatcher_fsevents.cpp @@ -45,6 +45,8 @@ #include "qfilesystemwatcher.h" #include "qfilesystemwatcher_fsevents_p.h" +#ifndef QT_NO_FILESYSTEMWATCHER + #include <qdebug.h> #include <qfile.h> #include <qdatetime.h> @@ -487,3 +489,4 @@ void QFSEventsFileSystemWatcherEngine::run() } QT_END_NAMESPACE +#endif //QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_fsevents_p.h b/src/corelib/io/qfilesystemwatcher_fsevents_p.h index b5bbe3a..bbfdd32 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents_p.h +++ b/src/corelib/io/qfilesystemwatcher_fsevents_p.h @@ -56,6 +56,8 @@ #include "qfilesystemwatcher_p.h" +#ifndef QT_NO_FILESYSTEMWATCHER + #include <QtCore/qmutex.h> #include <QtCore/qwaitcondition.h> #include <QtCore/qthread.h> @@ -123,6 +125,8 @@ private: #endif }; +#endif //QT_NO_FILESYSTEMWATCHER + #endif QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp index 1ceb011..8fc2d31 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -340,7 +340,7 @@ QStringList QInotifyFileSystemWatcherEngine::removePaths(const QStringList &path void QInotifyFileSystemWatcherEngine::stop() { - QMetaObject::invokeMethod(this, "quit"); + quit(); } void QInotifyFileSystemWatcherEngine::readFromInotify() @@ -353,24 +353,24 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() ioctl(inotifyFd, FIONREAD, (char *) &buffSize); QVarLengthArray<char, 4096> buffer(buffSize); buffSize = read(inotifyFd, buffer.data(), buffSize); - const char *at = buffer.data(); - const char * const end = at + buffSize; + char *at = buffer.data(); + char * const end = at + buffSize; - QMap<int, inotify_event> eventForId; + QHash<int, inotify_event *> eventForId; while (at < end) { - const inotify_event *event = reinterpret_cast<const inotify_event *>(at); + inotify_event *event = reinterpret_cast<inotify_event *>(at); if (eventForId.contains(event->wd)) - eventForId[event->wd].mask |= event->mask; + eventForId[event->wd]->mask |= event->mask; else - eventForId.insert(event->wd, *event); + eventForId.insert(event->wd, event); at += sizeof(inotify_event) + event->len; } - QMap<int, inotify_event>::const_iterator it = eventForId.constBegin(); + QHash<int, inotify_event *>::const_iterator it = eventForId.constBegin(); while (it != eventForId.constEnd()) { - inotify_event event = *it; + const inotify_event &event = **it; ++it; // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask; diff --git a/src/corelib/io/qfilesystemwatcher_kqueue.cpp b/src/corelib/io/qfilesystemwatcher_kqueue.cpp index 6466441..6c36a82 100644 --- a/src/corelib/io/qfilesystemwatcher_kqueue.cpp +++ b/src/corelib/io/qfilesystemwatcher_kqueue.cpp @@ -45,6 +45,8 @@ #include "qfilesystemwatcher_kqueue_p.h" #include "private/qcore_unix_p.h" +#ifndef QT_NO_FILESYSTEMWATCHER + #include <qdebug.h> #include <qfile.h> #include <qsocketnotifier.h> @@ -327,4 +329,6 @@ void QKqueueFileSystemWatcherEngine::run() } } +#endif //QT_NO_FILESYSTEMWATCHER + QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemwatcher_kqueue_p.h b/src/corelib/io/qfilesystemwatcher_kqueue_p.h index 1432064..38b8937 100644 --- a/src/corelib/io/qfilesystemwatcher_kqueue_p.h +++ b/src/corelib/io/qfilesystemwatcher_kqueue_p.h @@ -60,6 +60,7 @@ #include <QtCore/qthread.h> #include <QtCore/qvector.h> +#ifndef QT_NO_FILESYSTEMWATCHER struct kevent; QT_BEGIN_NAMESPACE @@ -92,4 +93,5 @@ private: QT_END_NAMESPACE +#endif //QT_NO_FILESYSTEMWATCHER #endif // FILEWATCHER_KQUEUE_P_H diff --git a/src/corelib/io/qfilesystemwatcher_symbian.cpp b/src/corelib/io/qfilesystemwatcher_symbian.cpp index 5ee27bd..29ec77a 100644 --- a/src/corelib/io/qfilesystemwatcher_symbian.cpp +++ b/src/corelib/io/qfilesystemwatcher_symbian.cpp @@ -215,7 +215,7 @@ void QSymbianFileSystemWatcherEngine::emitPathChanged(QNotifyChangeEvent *e) void QSymbianFileSystemWatcherEngine::stop() { - QMetaObject::invokeMethod(this, "quit"); + quit(); wait(); } diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp index e7bc8e0..26b83b2 100644 --- a/src/corelib/io/qfilesystemwatcher_win.cpp +++ b/src/corelib/io/qfilesystemwatcher_win.cpp @@ -86,7 +86,8 @@ QStringList QWindowsFileSystemWatcherEngine::addPaths(const QStringList &paths, while (it.hasNext()) { QString path = it.next(); QString normalPath = path; - if ((normalPath.endsWith(QLatin1Char('/')) || normalPath.endsWith(QLatin1Char('\\'))) + if ((normalPath.endsWith(QLatin1Char('/')) && !normalPath.endsWith(QLatin1String(":/"))) + || (normalPath.endsWith(QLatin1Char('\\')) && !normalPath.endsWith(QLatin1String(":\\"))) #ifdef Q_OS_WINCE && normalPath.size() > 1) #else diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 0950dc0..802d394 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -41,6 +41,7 @@ #include "qfsfileengine_p.h" #include "qfsfileengine_iterator_p.h" +#include "qfilesystemengine_p.h" #include "qdatetime.h" #include "qdiriterator.h" #include "qset.h" @@ -119,6 +120,9 @@ void QFSFileEnginePrivate::init() openMode = QIODevice::NotOpen; fd = -1; fh = 0; +#ifdef Q_OS_SYMBIAN + fileHandleForMaps = -1; +#endif lastIOCommand = IOFlushCommand; lastFlushFailed = false; closeFileHandle = false; @@ -133,109 +137,13 @@ void QFSFileEnginePrivate::init() } /*! - \internal - - Returns the canonicalized form of \a path (i.e., with all symlinks - resolved, and all redundant path elements removed. -*/ -QString QFSFileEnginePrivate::canonicalized(const QString &path) -{ - if (path.isEmpty()) - return path; - - // FIXME let's see if this stuff works, then we might be able to remove some of the other code. -#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) - if (path.size() == 1 && path.at(0) == QLatin1Char('/')) - return path; -#endif -#if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN) || defined(Q_OS_MAC) - // ... but Linux with uClibc does not have it -#if !defined(__UCLIBC__) - char *ret = 0; -#if defined(Q_OS_MAC) - // Mac OS X 10.5.x doesn't support the realpath(X,0) extension we use here. - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { - ret = realpath(path.toLocal8Bit().constData(), (char*)0); - } else { - // on 10.5 we can use FSRef to resolve the file path. - FSRef fsref; - if (FSPathMakeRef((const UInt8 *)QDir::cleanPath(path).toUtf8().data(), &fsref, 0) == noErr) { - CFURLRef urlref = CFURLCreateFromFSRef(NULL, &fsref); - CFStringRef canonicalPath = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle); - QString ret = QCFString::toQString(canonicalPath); - CFRelease(canonicalPath); - CFRelease(urlref); - return ret; - } - } -#else - ret = realpath(path.toLocal8Bit().constData(), (char*)0); -#endif - if (ret) { - QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret)); - free(ret); - return canonicalPath; - } -#endif -#endif - - QFileInfo fi; - const QChar slash(QLatin1Char('/')); - QString tmpPath = path; - int separatorPos = 0; - QSet<QString> nonSymlinks; - QSet<QString> known; - - known.insert(path); - do { -#ifdef Q_OS_WIN - // UNC, skip past the first two elements - if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//"))) - separatorPos = tmpPath.indexOf(slash, 2); - if (separatorPos != -1) -#endif - separatorPos = tmpPath.indexOf(slash, separatorPos + 1); - QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos); - if ( -#ifdef Q_OS_SYMBIAN - // Symbian doesn't support directory symlinks, so do not check for link unless we - // are handling the last path element. This not only slightly improves performance, - // but also saves us from lot of unnecessary platform security check failures - // when dealing with files under *:/private directories. - separatorPos == -1 && -#endif - !nonSymlinks.contains(prefix)) { - fi.setFile(prefix); - if (fi.isSymLink()) { - QString target = fi.symLinkTarget(); - if (separatorPos != -1) { - if (fi.isDir() && !target.endsWith(slash)) - target.append(slash); - target.append(tmpPath.mid(separatorPos)); - } - tmpPath = QDir::cleanPath(target); - separatorPos = 0; - - if (known.contains(tmpPath)) - return QString(); - known.insert(tmpPath); - } else { - nonSymlinks.insert(prefix); - } - } - } while (separatorPos != -1); - - return QDir::cleanPath(tmpPath); -} - -/*! Constructs a QFSFileEngine for the file name \a file. */ -QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate) +QFSFileEngine::QFSFileEngine(const QString &file) + : QAbstractFileEngine(*new QFSFileEnginePrivate) { Q_D(QFSFileEngine); - d->filePath = QDir::fromNativeSeparators(file); - d->nativeInitFileName(); + d->fileEntry = QFileSystemEntry(file); } /*! @@ -284,8 +192,7 @@ void QFSFileEngine::setFileName(const QString &file) { Q_D(QFSFileEngine); d->init(); - d->filePath = QDir::fromNativeSeparators(file); - d->nativeInitFileName(); + d->fileEntry = QFileSystemEntry(file); } /*! @@ -294,7 +201,7 @@ void QFSFileEngine::setFileName(const QString &file) bool QFSFileEngine::open(QIODevice::OpenMode openMode) { Q_D(QFSFileEngine); - if (d->filePath.isEmpty()) { + if (d->fileEntry.isEmpty()) { qWarning("QFSFileEngine::open: No file name specified"); setError(QFile::OpenError, QLatin1String("No file name specified")); return false; @@ -323,6 +230,11 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode) */ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) { + return open(openMode, fh, QFile::DontCloseHandle); +} + +bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags) +{ Q_D(QFSFileEngine); // Append implies WriteOnly. @@ -335,9 +247,8 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh) d->openMode = openMode; d->lastFlushFailed = false; - d->closeFileHandle = false; - d->nativeFilePath.clear(); - d->filePath.clear(); + d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); + d->fileEntry.clear(); d->tried_stat = 0; d->fd = -1; @@ -380,6 +291,11 @@ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) */ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) { + return open(openMode, fd, QFile::DontCloseHandle); +} + +bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags) +{ Q_D(QFSFileEngine); // Append implies WriteOnly. @@ -392,9 +308,8 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd) d->openMode = openMode; d->lastFlushFailed = false; - d->closeFileHandle = false; - d->nativeFilePath.clear(); - d->filePath.clear(); + d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); + d->fileEntry.clear(); d->fh = 0; d->fd = -1; d->tried_stat = 0; @@ -450,7 +365,12 @@ bool QFSFileEngine::close() bool QFSFileEnginePrivate::closeFdFh() { Q_Q(QFSFileEngine); - if (fd == -1 && !fh) + if (fd == -1 && !fh +#ifdef Q_OS_SYMBIAN + && !symbianFile.SubSessionHandle() + && fileHandleForMaps == -1 +#endif + ) return false; // Flush the file if it's buffered, and if the last flush didn't fail. @@ -458,10 +378,24 @@ bool QFSFileEnginePrivate::closeFdFh() bool closed = true; tried_stat = 0; +#ifdef Q_OS_SYMBIAN + // Map handle is always owned by us so always close it + if (fileHandleForMaps >= 0) { + QT_CLOSE(fileHandleForMaps); + fileHandleForMaps = -1; + } +#endif + // Close the file if we created the handle. if (closeFileHandle) { int ret; do { +#ifdef Q_OS_SYMBIAN + if (symbianFile.SubSessionHandle()) { + symbianFile.Close(); + ret = 0; + } else +#endif if (fh) { // Close buffered file. ret = fclose(fh) != 0 ? -1 : 0; @@ -541,28 +475,19 @@ qint64 QFSFileEngine::size() const /*! \internal */ +#ifndef Q_OS_WIN qint64 QFSFileEnginePrivate::sizeFdFh() const { Q_Q(const QFSFileEngine); - // ### Fix this function, it should not stat unless the file is closed. - QT_STATBUF st; - int ret = 0; const_cast<QFSFileEngine *>(q)->flush(); - if (fh && nativeFilePath.isEmpty()) { - // Buffered stdlib mode. - // ### This should really be an ftell - ret = QT_FSTAT(QT_FILENO(fh), &st); - } else if (fd == -1) { - // Stateless stat. - ret = QT_STAT(nativeFilePath.constData(), &st); - } else { - // Unbuffered stdio mode. - ret = QT_FSTAT(fd, &st); - } - if (ret == -1) + + tried_stat = 0; + metaData.clearFlags(QFileSystemMetaData::SizeAttribute); + if (!doStat(QFileSystemMetaData::SizeAttribute)) return 0; - return st.st_size; + return metaData.size(); } +#endif /*! \reimp @@ -869,18 +794,14 @@ bool QFSFileEngine::isSequential() const /*! \internal */ +#ifdef Q_OS_UNIX bool QFSFileEnginePrivate::isSequentialFdFh() const { - if (!tried_stat) - doStat(); - if (could_stat) { -#ifdef Q_OS_UNIX - return (st.st_mode & S_IFMT) != S_IFREG; - // ### WINDOWS! -#endif - } + if (doStat(QFileSystemMetaData::SequentialType)) + return metaData.isSequential(); return true; } +#endif /*! \reimp diff --git a/src/corelib/io/qfsfileengine.h b/src/corelib/io/qfsfileengine.h index 95ab33e..726d581 100644 --- a/src/corelib/io/qfsfileengine.h +++ b/src/corelib/io/qfsfileengine.h @@ -43,6 +43,9 @@ #define QFSFILEENGINE_H #include <QtCore/qabstractfileengine.h> +#ifdef Q_OS_SYMBIAN +#include <f32file.h> +#endif #ifndef QT_NO_FSFILEENGINE @@ -101,6 +104,11 @@ public: //FS only!! bool open(QIODevice::OpenMode flags, int fd); + bool open(QIODevice::OpenMode flags, int fd, QFile::FileHandleFlags handleFlags); + bool open(QIODevice::OpenMode flags, FILE *fh, QFile::FileHandleFlags handleFlags); +#ifdef Q_OS_SYMBIAN + bool open(QIODevice::OpenMode flags, const RFile &f, QFile::FileHandleFlags handleFlags); +#endif static bool setCurrentPath(const QString &path); static QString currentPath(const QString &path = QString()); static QString homePath(); diff --git a/src/corelib/io/qfsfileengine_iterator.cpp b/src/corelib/io/qfsfileengine_iterator.cpp index 99abb34..d4c0e3f 100644 --- a/src/corelib/io/qfsfileengine_iterator.cpp +++ b/src/corelib/io/qfsfileengine_iterator.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qfsfileengine_iterator_p.h" +#include "qfileinfo_p.h" #include "qvariant.h" #ifndef QT_NO_FSFILEENGINE @@ -48,13 +49,23 @@ QT_BEGIN_NAMESPACE QFSFileEngineIterator::QFSFileEngineIterator(QDir::Filters filters, const QStringList &filterNames) : QAbstractFileEngineIterator(filters, filterNames) + , done(false) { - newPlatformSpecifics(); } QFSFileEngineIterator::~QFSFileEngineIterator() { - deletePlatformSpecifics(); +} + +bool QFSFileEngineIterator::hasNext() const +{ + if (!done && !nativeIterator) { + nativeIterator.reset(new QFileSystemIterator(QFileSystemEntry(path()), + filters(), nameFilters())); + advance(); + } + + return !done; } QString QFSFileEngineIterator::next() @@ -66,14 +77,28 @@ QString QFSFileEngineIterator::next() return currentFilePath(); } +void QFSFileEngineIterator::advance() const +{ + currentInfo = nextInfo; + + QFileSystemEntry entry; + QFileSystemMetaData data; + if (nativeIterator->advance(entry, data)) { + nextInfo = QFileInfo(new QFileInfoPrivate(entry, data)); + } else { + done = true; + nativeIterator.reset(); + } +} + QString QFSFileEngineIterator::currentFileName() const { - return currentEntry; + return currentInfo.fileName(); } QFileInfo QFSFileEngineIterator::currentFileInfo() const { - return QAbstractFileEngineIterator::currentFileInfo(); + return currentInfo; } QT_END_NAMESPACE diff --git a/src/corelib/io/qfsfileengine_iterator_p.h b/src/corelib/io/qfsfileengine_iterator_p.h index 8ff5f6e..d4155d6 100644 --- a/src/corelib/io/qfsfileengine_iterator_p.h +++ b/src/corelib/io/qfsfileengine_iterator_p.h @@ -54,6 +54,7 @@ // #include "qabstractfileengine.h" +#include "qfilesystemiterator_p.h" #include "qdir.h" #ifndef QT_NO_FSFILEENGINE @@ -76,13 +77,11 @@ public: QFileInfo currentFileInfo() const; private: - QFSFileEngineIteratorPlatformSpecificData *platform; - friend class QFSFileEngineIteratorPlatformSpecificData; - void newPlatformSpecifics(); - void deletePlatformSpecifics(); - void advance(); - - QString currentEntry; + void advance() const; + mutable QScopedPointer<QFileSystemIterator> nativeIterator; + mutable QFileInfo currentInfo; + mutable QFileInfo nextInfo; + mutable bool done; }; QT_END_NAMESPACE diff --git a/src/corelib/io/qfsfileengine_iterator_unix.cpp b/src/corelib/io/qfsfileengine_iterator_unix.cpp deleted file mode 100644 index 6c78f3c..0000000 --- a/src/corelib/io/qfsfileengine_iterator_unix.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" -#include "qfsfileengine_iterator_p.h" - -#include <QtCore/qvariant.h> - -#ifndef QT_NO_FSFILEENGINE - -QT_BEGIN_NAMESPACE - -class QFSFileEngineIteratorPlatformSpecificData -{ -public: - inline QFSFileEngineIteratorPlatformSpecificData() - : dir(0), dirEntry(0), done(false) -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - , mt_file(0) -#endif - {} - - QT_DIR *dir; - QT_DIRENT *dirEntry; - bool done; - -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - // for readdir_r - QT_DIRENT *mt_file; -#endif -}; - -void QFSFileEngineIterator::advance() -{ - currentEntry = platform->dirEntry ? QFile::decodeName(QByteArray(platform->dirEntry->d_name)) : QString(); - - if (!platform->dir) - return; - -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - if (QT_READDIR_R(platform->dir, platform->mt_file, &platform->dirEntry) != 0) - platform->done = true; -#else - // ### add local lock to prevent breaking reentrancy - platform->dirEntry = QT_READDIR(platform->dir); -#endif // _POSIX_THREAD_SAFE_FUNCTIONS - if (!platform->dirEntry) { - QT_CLOSEDIR(platform->dir); - platform->dir = 0; - platform->done = true; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - delete [] platform->mt_file; - platform->mt_file = 0; -#endif - } -} - -void QFSFileEngineIterator::newPlatformSpecifics() -{ - platform = new QFSFileEngineIteratorPlatformSpecificData; -} - -void QFSFileEngineIterator::deletePlatformSpecifics() -{ - if (platform->dir) { - QT_CLOSEDIR(platform->dir); -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - delete [] platform->mt_file; - platform->mt_file = 0; -#endif - } - delete platform; - platform = 0; -} - -bool QFSFileEngineIterator::hasNext() const -{ - if (!platform->done && !platform->dir) { - QFSFileEngineIterator *that = const_cast<QFSFileEngineIterator *>(this); - if ((that->platform->dir = QT_OPENDIR(QFile::encodeName(path()).data())) == 0) { - that->platform->done = true; - } else { - // ### Race condition; we should use fpathconf and dirfd(). - long maxPathName = ::pathconf(QFile::encodeName(path()).data(), _PC_NAME_MAX); - if ((int) maxPathName == -1) - maxPathName = FILENAME_MAX; - maxPathName += sizeof(QT_DIRENT) + 1; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - if (that->platform->mt_file) - delete [] that->platform->mt_file; - that->platform->mt_file = (QT_DIRENT *)new char[maxPathName]; -#endif - - that->advance(); - } - } - return !platform->done; -} - -QT_END_NAMESPACE - -#endif // QT_NO_FSFILEENGINE diff --git a/src/corelib/io/qfsfileengine_iterator_win.cpp b/src/corelib/io/qfsfileengine_iterator_win.cpp deleted file mode 100644 index 883b36f..0000000 --- a/src/corelib/io/qfsfileengine_iterator_win.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfsfileengine_iterator_p.h" -#include "qfsfileengine_p.h" -#include "qplatformdefs.h" - -#include <QtCore/qvariant.h> - -QT_BEGIN_NAMESPACE - -class QFSFileEngineIteratorPlatformSpecificData -{ -public: - inline QFSFileEngineIteratorPlatformSpecificData() - : uncShareIndex(-1), findFileHandle(INVALID_HANDLE_VALUE), - done(false), uncFallback(false) - {} - - QFSFileEngineIterator *it; - - QStringList uncShares; - int uncShareIndex; - - HANDLE findFileHandle; - WIN32_FIND_DATA findData; - bool done; - bool uncFallback; - - void saveCurrentFileName(); -}; - -void QFSFileEngineIteratorPlatformSpecificData::saveCurrentFileName() -{ - if (uncFallback) { - // Windows share / UNC path - it->currentEntry = uncShares.at(uncShareIndex - 1); - } else { - // Local directory - it->currentEntry = QString::fromWCharArray(findData.cFileName); - } -} - -void QFSFileEngineIterator::advance() -{ - platform->saveCurrentFileName(); - - if (platform->done) - return; - - if (platform->uncFallback) { - ++platform->uncShareIndex; - } else if (platform->findFileHandle != INVALID_HANDLE_VALUE) { - if (!FindNextFile(platform->findFileHandle, &platform->findData)) { - platform->done = true; - FindClose(platform->findFileHandle); - } - } -} - -void QFSFileEngineIterator::newPlatformSpecifics() -{ - platform = new QFSFileEngineIteratorPlatformSpecificData; - platform->it = this; -} - -void QFSFileEngineIterator::deletePlatformSpecifics() -{ - delete platform; - platform = 0; -} - -bool QFSFileEngineIterator::hasNext() const -{ - if (platform->done) - return false; - - if (platform->uncFallback) - return platform->uncShareIndex > 0 && platform->uncShareIndex <= platform->uncShares.size(); - - if (platform->findFileHandle == INVALID_HANDLE_VALUE) { - QString path = this->path(); - // Local directory - if (path.endsWith(QLatin1String(".lnk"))) - path = QFileInfo(path).readLink(); - - if (!path.endsWith(QLatin1Char('/'))) - path.append(QLatin1Char('/')); - path.append(QLatin1String("*.*")); - - QString fileName = QFSFileEnginePrivate::longFileName(path); - platform->findFileHandle = FindFirstFile((const wchar_t *)fileName.utf16(), &platform->findData); - - if (platform->findFileHandle == INVALID_HANDLE_VALUE) { - if (path.startsWith(QLatin1String("//"))) { - path = this->path(); - // UNC - QStringList parts = QDir::toNativeSeparators(path).split(QLatin1Char('\\'), QString::SkipEmptyParts); - - if (parts.count() == 1 && QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), - &platform->uncShares)) { - if (platform->uncShares.isEmpty()) { - platform->done = true; - } else { - platform->uncShareIndex = 1; - } - platform->uncFallback = true; - } else { - platform->done = true; - } - } else { - platform->done = true; - } - } - - if (!platform->done && (!platform->uncFallback || !platform->uncShares.isEmpty())) - platform->saveCurrentFileName(); - } - - return !platform->done; -} - -QT_END_NAMESPACE diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h index 9f3e8b2..253f461 100644 --- a/src/corelib/io/qfsfileengine_p.h +++ b/src/corelib/io/qfsfileengine_p.h @@ -56,8 +56,14 @@ #include "qplatformdefs.h" #include "QtCore/qfsfileengine.h" #include "private/qabstractfileengine_p.h" +#include <QtCore/private/qfilesystementry_p.h> +#include <QtCore/private/qfilesystemmetadata_p.h> #include <qhash.h> +#ifdef Q_OS_SYMBIAN +#include <f32file.h> +#endif + #ifndef QT_NO_FSFILEENGINE QT_BEGIN_NAMESPACE @@ -74,13 +80,10 @@ public: #ifdef Q_WS_WIN static QString longFileName(const QString &path); #endif - static QString canonicalized(const QString &path); - QString filePath; - QByteArray nativeFilePath; + QFileSystemEntry fileEntry; QIODevice::OpenMode openMode; - void nativeInitFileName(); bool nativeOpen(QIODevice::OpenMode openMode); bool openFh(QIODevice::OpenMode flags, FILE *fh); bool openFd(QIODevice::OpenMode flags, int fd); @@ -89,7 +92,9 @@ public: bool nativeFlush(); bool flushFh(); qint64 nativeSize() const; +#ifndef Q_OS_WIN qint64 sizeFdFh() const; +#endif qint64 nativePos() const; qint64 posFdFh() const; bool nativeSeek(qint64); @@ -102,12 +107,42 @@ public: qint64 writeFdFh(const char *data, qint64 len); int nativeHandle() const; bool nativeIsSequential() const; +#ifndef Q_OS_WIN bool isSequentialFdFh() const; +#endif uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags); bool unmap(uchar *ptr); + mutable QFileSystemMetaData metaData; + FILE *fh; +#ifdef Q_OS_SYMBIAN +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + RFile64 symbianFile; + TInt64 symbianFilePos; +#else + RFile symbianFile; + + /** + * The cursor position in the underlying file. This differs + * from devicePos because the latter is updated on calls to + * writeData, even if no data was physically transferred to + * the file, but instead stored in the write buffer. + * + * iFilePos is updated on calls to RFile::Read and + * RFile::Write. It is also updated on calls to seek() but + * RFile::Seek is not called when that happens because + * Symbian supports positioned reads and writes, saving a file + * server call, and because Symbian does not support seeking + * past the end of a file. + */ + TInt symbianFilePos; +#endif + mutable int fileHandleForMaps; + int getMapHandle(); +#endif + #ifdef Q_WS_WIN HANDLE fileHandle; HANDLE mapHandle; @@ -120,7 +155,6 @@ public: mutable DWORD fileAttrib; #else QHash<uchar *, QPair<int /*offset % PageSize*/, size_t /*length + offset % PageSize*/> > maps; - mutable QT_STATBUF st; #endif int fd; @@ -142,23 +176,17 @@ public: mutable uint is_link : 1; #endif - bool doStat() const; +#if defined(Q_OS_WIN) + bool doStat(QFileSystemMetaData::MetaDataFlags flags) const; +#else + bool doStat(QFileSystemMetaData::MetaDataFlags flags = QFileSystemMetaData::PosixStatFlags) const; +#endif bool isSymlink() const; #if defined(Q_OS_WIN32) int sysOpen(const QString &, int flags); #endif -#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - static void resolveLibs(); - static bool resolveUNCLibs(); - static bool uncListSharesOnServer(const QString &server, QStringList *list); -#endif - -#ifdef Q_OS_SYMBIAN - void setSymbianError(int symbianError, QFile::FileError defaultError, QString defaultString); -#endif - protected: QFSFileEnginePrivate(); diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index c1d45ea..6c03b32 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -43,6 +43,8 @@ #include "qabstractfileengine.h" #include "private/qfsfileengine_p.h" #include "private/qcore_unix_p.h" +#include "qfilesystementry_p.h" +#include "qfilesystemengine_p.h" #ifndef QT_NO_FSFILEENGINE @@ -67,7 +69,6 @@ QT_BEGIN_NAMESPACE - #if defined(Q_OS_SYMBIAN) /*! \internal @@ -82,56 +83,25 @@ static bool isRelativePathSymbian(const QString& fileName) || (fileName.at(0) == QLatin1Char('/') && fileName.at(1) == QLatin1Char('/'))))); } -/*! - \internal - convert symbian error code to the one suitable for setError. - example usage: setSymbianError(err, QFile::CopyError, QLatin1String("copy error")) -*/ -void QFSFileEnginePrivate::setSymbianError(int symbianError, QFile::FileError defaultError, QString defaultString) -{ - Q_Q(QFSFileEngine); - switch (symbianError) { - case KErrNone: - q->setError(QFile::NoError, QLatin1String("")); - break; - case KErrAccessDenied: - q->setError(QFile::PermissionsError, QLatin1String("access denied")); - break; - case KErrPermissionDenied: - q->setError(QFile::PermissionsError, QLatin1String("permission denied")); - break; - case KErrAbort: - q->setError(QFile::AbortError, QLatin1String("aborted")); - break; - case KErrCancel: - q->setError(QFile::AbortError, QLatin1String("cancelled")); - break; - case KErrTimedOut: - q->setError(QFile::TimeOutError, QLatin1String("timed out")); - break; - default: - q->setError(defaultError, defaultString); - break; - } -} - #endif +#ifndef Q_OS_SYMBIAN /*! \internal Returns the stdlib open string corresponding to a QIODevice::OpenMode. */ -static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QByteArray &fileName) +static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QFileSystemEntry &fileEntry, + QFileSystemMetaData &metaData) { QByteArray mode; if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) { mode = "rb"; if (flags & QIODevice::WriteOnly) { - QT_STATBUF statBuf; - if (!fileName.isEmpty() - && QT_STAT(fileName, &statBuf) == 0 - && (statBuf.st_mode & S_IFMT) == S_IFREG) { + metaData.clearFlags(QFileSystemMetaData::FileType); + if (!fileEntry.isEmpty() + && QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::FileType) + && metaData.isFile()) { mode += '+'; } else { mode = "wb+"; @@ -155,6 +125,7 @@ static inline QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QB return mode; } +#endif /*! \internal @@ -184,6 +155,7 @@ static inline int openModeToOpenFlags(QIODevice::OpenMode mode) return oflags; } +#ifndef Q_OS_SYMBIAN /*! \internal @@ -194,15 +166,151 @@ static inline bool setCloseOnExec(int fd) { return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1; } +#endif +#ifdef Q_OS_SYMBIAN /*! \internal */ -void QFSFileEnginePrivate::nativeInitFileName() +bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) { - nativeFilePath = QFile::encodeName(filePath); + Q_Q(QFSFileEngine); + + fh = 0; + fd = -1; + + QString fn(QFileSystemEngine::absoluteName(fileEntry).nativeFilePath()); + RFs& fs = qt_s60GetRFs(); + + TUint symbianMode = 0; + + if(openMode & QIODevice::ReadOnly) + symbianMode |= EFileRead; + if(openMode & QIODevice::WriteOnly) + symbianMode |= EFileWrite; + if(openMode & QIODevice::Text) + symbianMode |= EFileStreamText; + + // pre Symbian 9.4, file I/O is always unbuffered, and the enum values don't exist + if(QSysInfo::symbianVersion() >= QSysInfo::SV_9_4) { + if (openMode & QFile::Unbuffered) { + if (openMode & QIODevice::WriteOnly) + symbianMode |= 0x00001000; //EFileWriteDirectIO; + // ### Unbuffered read is not used, because it prevents file open in /resource + // ### and has no obvious benefits + } else { + if (openMode & QIODevice::WriteOnly) + symbianMode |= 0x00000800; //EFileWriteBuffered; + // use implementation defaults for read buffering + } + } + + // Until Qt supports file sharing, we can't support EFileShareReadersOrWriters safely, + // but Qt does this on other platforms and autotests rely on it. + // The reason is that Unix locks are only advisory - the application needs to test the + // lock after opening the file. Symbian and Windows locks are mandatory - opening a + // locked file will fail. + symbianMode |= EFileShareReadersOrWriters; + + TInt r; + //note QIODevice::Truncate only has meaning for read/write access + //write-only files are always truncated unless append is specified + //reference openModeToOpenFlags in qfsfileengine_unix.cpp + if ((openMode & QIODevice::Truncate) || (!(openMode & QIODevice::ReadOnly) && !(openMode & QIODevice::Append))) { + r = symbianFile.Replace(fs, qt_QString2TPtrC(fn), symbianMode); + } else { + r = symbianFile.Open(fs, qt_QString2TPtrC(fn), symbianMode); + if (r == KErrNotFound && (openMode & QIODevice::WriteOnly)) { + r = symbianFile.Create(fs, qt_QString2TPtrC(fn), symbianMode); + } + } + + if (r == KErrNone) { +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + TInt64 size; +#else + TInt size; +#endif + r = symbianFile.Size(size); + if (r==KErrNone) { + if (openMode & QIODevice::Append) + symbianFilePos = size; + else + symbianFilePos = 0; + //TODO: port this (QFileSystemMetaData in open?) + //cachedSize = size; + } + } + + if (r != KErrNone) { + q->setError(QFile::OpenError, QSystemError(r, QSystemError::NativeError).toString()); + symbianFile.Close(); + return false; + } + + closeFileHandle = true; + return true; } +bool QFSFileEngine::open(QIODevice::OpenMode openMode, const RFile &file, QFile::FileHandleFlags handleFlags) +{ + Q_D(QFSFileEngine); + + // Append implies WriteOnly. + if (openMode & QFile::Append) + openMode |= QFile::WriteOnly; + + // WriteOnly implies Truncate if neither ReadOnly nor Append are sent. + if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append))) + openMode |= QFile::Truncate; + + d->openMode = openMode; + d->lastFlushFailed = false; + d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle); + d->fileEntry.clear(); + d->fh = 0; + d->fd = -1; + d->tried_stat = 0; + +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + //RFile64 adds only functions to RFile, no data members + d->symbianFile = static_cast<const RFile64&>(file); +#else + d->symbianFile = file; +#endif + TInt ret; + d->symbianFilePos = 0; + if (openMode & QFile::Append) { + // Seek to the end when in Append mode. + ret = d->symbianFile.Size(d->symbianFilePos); + } else { + // Seek to current otherwise + ret = d->symbianFile.Seek(ESeekCurrent, d->symbianFilePos); + } + + if (ret != KErrNone) { + setError(QFile::OpenError, QSystemError(ret, QSystemError::NativeError).toString()); + + d->openMode = QIODevice::NotOpen; +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + d->symbianFile = RFile64(); +#else + d->symbianFile = RFile(); +#endif + return false; + } + + // Extract filename (best effort) + TFileName fn; + TInt err = d->symbianFile.FullName(fn); + if (err == KErrNone) + d->fileEntry = QFileSystemEntry(qt_TDesC2QString(fn), QFileSystemEntry::FromNativePath()); + else + d->fileEntry.clear(); + + return true; +} +#else /*! \internal */ @@ -215,7 +323,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) // Try to open the file in unbuffered mode. do { - fd = QT_OPEN(nativeFilePath.constData(), flags, 0666); + fd = QT_OPEN(fileEntry.nativeFilePath().constData(), flags, 0666); } while (fd == -1 && errno == EINTR); // On failure, return and report the error. @@ -228,13 +336,11 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) if (!(openMode & QIODevice::WriteOnly)) { // we don't need this check if we tried to open for writing because then // we had received EISDIR anyway. - QT_STATBUF statBuf; - if (QT_FSTAT(fd, &statBuf) != -1) { - if ((statBuf.st_mode & S_IFMT) == S_IFDIR) { - q->setError(QFile::OpenError, QLatin1String("file to open is a directory")); - QT_CLOSE(fd); - return false; - } + if (QFileSystemEngine::fillMetaData(fd, metaData) + && metaData.isDirectory()) { + q->setError(QFile::OpenError, QLatin1String("file to open is a directory")); + QT_CLOSE(fd); + return false; } } @@ -254,11 +360,11 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) fh = 0; } else { - QByteArray fopenMode = openModeToFopenMode(openMode, nativeFilePath.constData()); + QByteArray fopenMode = openModeToFopenMode(openMode, fileEntry, metaData); // Try to open the file in buffered mode. do { - fh = QT_FOPEN(nativeFilePath.constData(), fopenMode.constData()); + fh = QT_FOPEN(fileEntry.nativeFilePath().constData(), fopenMode.constData()); } while (!fh && errno == EINTR); // On failure, return and report the error. @@ -271,13 +377,11 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) if (!(openMode & QIODevice::WriteOnly)) { // we don't need this check if we tried to open for writing because then // we had received EISDIR anyway. - QT_STATBUF statBuf; - if (QT_FSTAT(fileno(fh), &statBuf) != -1) { - if ((statBuf.st_mode & S_IFMT) == S_IFDIR) { - q->setError(QFile::OpenError, QLatin1String("file to open is a directory")); - fclose(fh); - return false; - } + if (QFileSystemEngine::fillMetaData(QT_FILENO(fh), metaData) + && metaData.isDirectory()) { + q->setError(QFile::OpenError, QLatin1String("file to open is a directory")); + fclose(fh); + return false; } } @@ -303,6 +407,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) closeFileHandle = true; return true; } +#endif /*! \internal @@ -318,6 +423,10 @@ bool QFSFileEnginePrivate::nativeClose() */ bool QFSFileEnginePrivate::nativeFlush() { +#ifdef Q_OS_SYMBIAN + if (symbianFile.SubSessionHandle()) + return (KErrNone == symbianFile.Flush()); +#endif return fh ? flushFh() : fd != -1; } @@ -328,6 +437,24 @@ qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len) { Q_Q(QFSFileEngine); +#ifdef Q_OS_SYMBIAN + if (symbianFile.SubSessionHandle()) { + if(len > KMaxTInt) { + //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..) + q->setError(QFile::ReadError, QLatin1String("Maximum 2GB in single read on this platform")); + return -1; + } + TPtr8 ptr(reinterpret_cast<TUint8*>(data), static_cast<TInt>(len)); + TInt r = symbianFile.Read(symbianFilePos, ptr); + if (r != KErrNone) + { + q->setError(QFile::ReadError, QSystemError(r, QSystemError::NativeError).toString()); + return -1; + } + symbianFilePos += ptr.Length(); + return qint64(ptr.Length()); + } +#endif if (fh && nativeIsSequential()) { size_t readBytes = 0; int oldFlags = fcntl(QT_FILENO(fh), F_GETFL); @@ -395,6 +522,40 @@ qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen) */ qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) { +#ifdef Q_OS_SYMBIAN + Q_Q(QFSFileEngine); + if (symbianFile.SubSessionHandle()) { + if(len > KMaxTInt) { + //this check is more likely to catch a corrupt length, since it isn't possible to allocate 2GB buffers (yet..) + q->setError(QFile::WriteError, QLatin1String("Maximum 2GB in single write on this platform")); + return -1; + } + const TPtrC8 ptr(reinterpret_cast<const TUint8*>(data), static_cast<TInt>(len)); +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + TInt64 eofpos = 0; +#else + TInt eofpos = 0; +#endif + //The end of file position is not cached because QFile is read/write sharable, therefore another + //process may have altered the file size. + TInt r = symbianFile.Seek(ESeekEnd, eofpos); + if (r == KErrNone && symbianFilePos > eofpos) { + //seek position is beyond end of file so file needs to be extended before write. + //note that SetSize does not zero-initialise (c.f. posix lseek) + r = symbianFile.SetSize(symbianFilePos); + } + if (r == KErrNone) { + //write to specific position in the file (i.e. use our own cursor rather than calling seek) + r = symbianFile.Write(symbianFilePos, ptr); + } + if (r != KErrNone) { + q->setError(QFile::WriteError, QSystemError(r, QSystemError::NativeError).toString()); + return -1; + } + symbianFilePos += len; + return len; + } +#endif return writeFdFh(data, len); } @@ -403,6 +564,12 @@ qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) */ qint64 QFSFileEnginePrivate::nativePos() const { +#ifdef Q_OS_SYMBIAN + const Q_Q(QFSFileEngine); + if (symbianFile.SubSessionHandle()) { + return symbianFilePos; + } +#endif return posFdFh(); } @@ -411,6 +578,19 @@ qint64 QFSFileEnginePrivate::nativePos() const */ bool QFSFileEnginePrivate::nativeSeek(qint64 pos) { +#ifdef Q_OS_SYMBIAN + Q_Q(QFSFileEngine); + if (symbianFile.SubSessionHandle()) { +#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + if(pos > KMaxTInt) { + q->setError(QFile::PositionError, QLatin1String("Maximum 2GB file position on this platform")); + return false; + } +#endif + symbianFilePos = pos; + return true; + } +#endif return seekFdFh(pos); } @@ -422,139 +602,110 @@ int QFSFileEnginePrivate::nativeHandle() const return fh ? fileno(fh) : fd; } +#ifdef Q_OS_SYMBIAN +int QFSFileEnginePrivate::getMapHandle() +{ + if (symbianFile.SubSessionHandle()) { + // Symbian file handle can't be used for open C mmap() so open the file with open C as well. + if (fileHandleForMaps < 0) { + int flags = openModeToOpenFlags(openMode); + flags &= ~(O_CREAT | O_TRUNC); + fileHandleForMaps = ::wopen((wchar_t*)(fileEntry.nativeFilePath().utf16()), flags, 0666); + } + return fileHandleForMaps; + } + return nativeHandle(); +} +#endif + /*! \internal */ bool QFSFileEnginePrivate::nativeIsSequential() const { +#ifdef Q_OS_SYMBIAN + if (symbianFile.SubSessionHandle()) + return false; +#endif return isSequentialFdFh(); } bool QFSFileEngine::remove() { Q_D(QFSFileEngine); - bool ret = unlink(d->nativeFilePath.constData()) == 0; - if (!ret) - setError(QFile::RemoveError, qt_error_string(errno)); + QSystemError error; + bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); + d->metaData.clear(); + if (!ret) { + setError(QFile::RemoveError, error.toString()); + } return ret; } bool QFSFileEngine::copy(const QString &newName) { -#if defined(Q_OS_SYMBIAN) Q_D(QFSFileEngine); - RFs rfs = qt_s60GetRFs(); - CFileMan* fm = NULL; - QString oldNative(QDir::toNativeSeparators(d->filePath)); - TPtrC oldPtr(qt_QString2TPtrC(oldNative)); - QFileInfo fi(newName); - QString absoluteNewName = fi.absoluteFilePath(); - QString newNative(QDir::toNativeSeparators(absoluteNewName)); - TPtrC newPtr(qt_QString2TPtrC(newNative)); - TRAPD (err, - fm = CFileMan::NewL(rfs); - RFile rfile; - err = rfile.Open(rfs, oldPtr, EFileShareReadersOrWriters); - if (err == KErrNone) { - err = fm->Copy(rfile, newPtr); - rfile.Close(); - } - ) // End TRAP - delete fm; - if (err == KErrNone) - return true; - d->setSymbianError(err, QFile::CopyError, QLatin1String("copy error")); - return false; -#else - Q_UNUSED(newName); - // ### Add copy code for Unix here - setError(QFile::UnspecifiedError, QLatin1String("Not implemented!")); - return false; -#endif + QSystemError error; + bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(newName), error); + if (!ret) { + setError(QFile::CopyError, error.toString()); + } + return ret; } bool QFSFileEngine::rename(const QString &newName) { Q_D(QFSFileEngine); - bool ret = ::rename(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0; - if (!ret) - setError(QFile::RenameError, qt_error_string(errno)); + QSystemError error; + bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); + + if (!ret) { + setError(QFile::RenameError, error.toString()); + } + return ret; } bool QFSFileEngine::link(const QString &newName) { Q_D(QFSFileEngine); - bool ret = ::symlink(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0; - if (!ret) - setError(QFile::RenameError, qt_error_string(errno)); + QSystemError error; + bool ret = QFileSystemEngine::createLink(d->fileEntry, QFileSystemEntry(newName), error); + if (!ret) { + setError(QFile::RenameError, error.toString()); + } return ret; } qint64 QFSFileEnginePrivate::nativeSize() const { +#ifdef Q_OS_SYMBIAN + const Q_Q(QFSFileEngine); + if (symbianFile.SubSessionHandle()) { +#ifdef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API + qint64 size; +#else + TInt size; +#endif + TInt err = symbianFile.Size(size); + if(err != KErrNone) { + const_cast<QFSFileEngine*>(q)->setError(QFile::PositionError, QSystemError(err, QSystemError::NativeError).toString()); + return 0; + } + return size; + } +#endif return sizeFdFh(); } bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const { - QString dirName = name; - if (createParentDirectories) { - dirName = QDir::cleanPath(dirName); -#if defined(Q_OS_SYMBIAN) - dirName = QDir::toNativeSeparators(dirName); -#endif - for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) { - slash = dirName.indexOf(QDir::separator(), oldslash+1); - if (slash == -1) { - if (oldslash == dirName.length()) - break; - slash = dirName.length(); - } - if (slash) { - QByteArray chunk = QFile::encodeName(dirName.left(slash)); - QT_STATBUF st; - if (QT_STAT(chunk, &st) != -1) { - if ((st.st_mode & S_IFMT) != S_IFDIR) - return false; - } else if (QT_MKDIR(chunk, 0777) != 0) { - return false; - } - } - } - return true; - } -#if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s - if (dirName.endsWith(QLatin1Char('/'))) - dirName.chop(1); -#endif - return (QT_MKDIR(QFile::encodeName(dirName), 0777) == 0); + return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); } bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const { - QString dirName = name; - if (recurseParentDirectories) { - dirName = QDir::cleanPath(dirName); -#if defined(Q_OS_SYMBIAN) - dirName = QDir::toNativeSeparators(dirName); -#endif - for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { - QByteArray chunk = QFile::encodeName(dirName.left(slash)); - QT_STATBUF st; - if (QT_STAT(chunk, &st) != -1) { - if ((st.st_mode & S_IFMT) != S_IFDIR) - return false; - if (::rmdir(chunk) != 0) - return oldslash != 0; - } else { - return false; - } - slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); - } - return true; - } - return ::rmdir(QFile::encodeName(dirName)) == 0; + return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); } bool QFSFileEngine::caseSensitive() const @@ -568,94 +719,27 @@ bool QFSFileEngine::caseSensitive() const bool QFSFileEngine::setCurrentPath(const QString &path) { - int r; - r = QT_CHDIR(QFile::encodeName(path)); - return r >= 0; + return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); } QString QFSFileEngine::currentPath(const QString &) { - QString result; - QT_STATBUF st; -#if defined(Q_OS_SYMBIAN) - char nativeCurrentName[PATH_MAX+1]; - if (::getcwd(nativeCurrentName, PATH_MAX)) - result = QDir::fromNativeSeparators(QFile::decodeName(QByteArray(nativeCurrentName))); - if (result.isEmpty()) { -# if defined(QT_DEBUG) - qWarning("QFSFileEngine::currentPath: getcwd() failed"); -# endif - } else -#endif - if (QT_STAT(".", &st) == 0) { -#if defined(__GLIBC__) && !defined(PATH_MAX) - char *currentName = ::get_current_dir_name(); - if (currentName) { - result = QFile::decodeName(QByteArray(currentName)); - ::free(currentName); - } -#elif !defined(Q_OS_SYMBIAN) - char currentName[PATH_MAX+1]; - if (::getcwd(currentName, PATH_MAX)) - result = QFile::decodeName(QByteArray(currentName)); -# if defined(QT_DEBUG) - if (result.isNull()) - qWarning("QFSFileEngine::currentPath: getcwd() failed"); -# endif -#endif - } else { -#if defined(Q_OS_SYMBIAN) - // If current dir returned by Open C doesn't exist, - // try to create it (can happen with application private dirs) - // Ignore mkdir failures; we want to be consistent with Open C - // current path regardless. - QT_MKDIR(QFile::encodeName(QLatin1String(nativeCurrentName)), 0777); -#else -# if defined(QT_DEBUG) - qWarning("QFSFileEngine::currentPath: stat(\".\") failed"); -# endif -#endif - } - return result; + return QFileSystemEngine::currentPath().filePath(); } QString QFSFileEngine::homePath() { -#if defined(Q_OS_SYMBIAN) - QString home = rootPath(); -#else - QString home = QFile::decodeName(qgetenv("HOME")); - if (home.isNull()) - home = rootPath(); -#endif - return home; + return QFileSystemEngine::homePath(); } QString QFSFileEngine::rootPath() { -#if defined(Q_OS_SYMBIAN) - TFileName symbianPath = PathInfo::PhoneMemoryRootPath(); - return QDir::cleanPath(QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath))); -#else - return QLatin1String("/"); -#endif + return QFileSystemEngine::rootPath(); } QString QFSFileEngine::tempPath() { -#if defined(Q_OS_SYMBIAN) - TFileName symbianPath = PathInfo::PhoneMemoryRootPath(); - QString temp = QDir::fromNativeSeparators(qt_TDesC2QString(symbianPath)); - temp += QLatin1String( "temp/"); - - // Just to verify that folder really exist on hardware - QT_MKDIR(QFile::encodeName(temp), 0777); -#else - QString temp = QFile::decodeName(qgetenv("TMPDIR")); - if (temp.isEmpty()) - temp = QLatin1String("/tmp/"); -#endif - return temp; + return QFileSystemEngine::tempPath(); } QFileInfoList QFSFileEngine::drives() @@ -683,123 +767,30 @@ QFileInfoList QFSFileEngine::drives() return ret; } -bool QFSFileEnginePrivate::doStat() const +bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const { - if (!tried_stat) { - tried_stat = true; - could_stat = false; - - if (fh && nativeFilePath.isEmpty()) { - // ### actually covers two cases: d->fh and when the file is not open - could_stat = (QT_FSTAT(QT_FILENO(fh), &st) == 0); - } else if (fd == -1) { - // ### actually covers two cases: d->fh and when the file is not open -#if defined(Q_OS_SYMBIAN) - // Optimization for Symbian where fileFlags() calls both doStat() and isSymlink(), but rarely on real links. - // When the filename is not a link, lstat will return the same info as stat, but this also removes - // any need for a further call to lstat to check if the file is a link. - need_lstat = false; - could_stat = (QT_LSTAT(nativeFilePath.constData(), &st) == 0); - is_link = could_stat ? S_ISLNK(st.st_mode) : false; - // if it turns out this was a link, we can call stat too. - if (is_link) -#endif - could_stat = (QT_STAT(nativeFilePath.constData(), &st) == 0); - } else { - could_stat = (QT_FSTAT(fd, &st) == 0); - } - } - return could_stat; -} + if (!tried_stat || !metaData.hasFlags(flags)) { + tried_stat = 1; -bool QFSFileEnginePrivate::isSymlink() const -{ - if (need_lstat) { - need_lstat = false; + int localFd = fd; + if (fh && fileEntry.isEmpty()) + localFd = QT_FILENO(fh); + if (localFd != -1) + QFileSystemEngine::fillMetaData(localFd, metaData); - QT_STATBUF st; // don't clobber our main one - is_link = (QT_LSTAT(nativeFilePath.constData(), &st) == 0) ? S_ISLNK(st.st_mode) : false; + if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) + QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); } - return is_link; -} - -#if defined(Q_OS_SYMBIAN) -static bool _q_isSymbianHidden(const QString &path, bool isDir) -{ - RFs rfs = qt_s60GetRFs(); - QFileInfo fi(path); - QString absPath = fi.absoluteFilePath(); - if (isDir && !absPath.endsWith(QLatin1Char('/'))) - absPath.append(QLatin1Char('/')); - QString native(QDir::toNativeSeparators(absPath)); - TPtrC ptr(qt_QString2TPtrC(native)); - TUint attributes; - TInt err = rfs.Att(ptr, attributes); - return (err == KErrNone && (attributes & KEntryAttHidden)); -} -#endif - -#if !defined(QWS) && defined(Q_OS_MAC) -static bool _q_isMacHidden(const QString &path) -{ - OSErr err = noErr; - - FSRef fsRef; - err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(QFile::encodeName(QDir::cleanPath(path)).constData()), - kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0); - if (err != noErr) - return false; - - FSCatalogInfo catInfo; - err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL); - if (err != noErr) - return false; - - FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo); - bool result = (fileInfo->finderFlags & kIsInvisible); - return result; + return metaData.exists(); } -#endif -QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const +bool QFSFileEnginePrivate::isSymlink() const { - QAbstractFileEngine::FileFlags ret = 0; - - if (st.st_mode & S_IRUSR) - ret |= QAbstractFileEngine::ReadOwnerPerm; - if (st.st_mode & S_IWUSR) - ret |= QAbstractFileEngine::WriteOwnerPerm; - if (st.st_mode & S_IXUSR) - ret |= QAbstractFileEngine::ExeOwnerPerm; - if (st.st_mode & S_IRGRP) - ret |= QAbstractFileEngine::ReadGroupPerm; - if (st.st_mode & S_IWGRP) - ret |= QAbstractFileEngine::WriteGroupPerm; - if (st.st_mode & S_IXGRP) - ret |= QAbstractFileEngine::ExeGroupPerm; - if (st.st_mode & S_IROTH) - ret |= QAbstractFileEngine::ReadOtherPerm; - if (st.st_mode & S_IWOTH) - ret |= QAbstractFileEngine::WriteOtherPerm; - if (st.st_mode & S_IXOTH) - ret |= QAbstractFileEngine::ExeOtherPerm; - - // calculate user permissions - if (type & QAbstractFileEngine::ReadUserPerm) { - if (QT_ACCESS(nativeFilePath.constData(), R_OK) == 0) - ret |= QAbstractFileEngine::ReadUserPerm; - } - if (type & QAbstractFileEngine::WriteUserPerm) { - if (QT_ACCESS(nativeFilePath.constData(), W_OK) == 0) - ret |= QAbstractFileEngine::WriteUserPerm; - } - if (type & QAbstractFileEngine::ExeUserPerm) { - if (QT_ACCESS(nativeFilePath.constData(), X_OK) == 0) - ret |= QAbstractFileEngine::ExeUserPerm; - } + if (!metaData.hasFlags(QFileSystemMetaData::LinkType)) + QFileSystemEngine::fillMetaData(fileEntry, metaData, QFileSystemMetaData::LinkType); - return ret; + return metaData.isLink(); } /*! @@ -808,360 +799,111 @@ QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFil QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const { Q_D(const QFSFileEngine); - // Force a stat, so that we're guaranteed to get up-to-date results - if (type & Refresh) { - d->tried_stat = 0; - d->need_lstat = 1; - } + + if (type & Refresh) + d->metaData.clear(); QAbstractFileEngine::FileFlags ret = 0; + if (type & FlagsMask) ret |= LocalDiskFlag; - bool exists = d->doStat(); - if (!exists && !d->isSymlink()) + + bool exists; + { + QFileSystemMetaData::MetaDataFlags queryFlags = 0; + + queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) + & QFileSystemMetaData::Permissions; + + if (type & TypesMask) + queryFlags |= QFileSystemMetaData::AliasType + | QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType; + + if (type & FlagsMask) + queryFlags |= QFileSystemMetaData::HiddenAttribute + | QFileSystemMetaData::ExistsAttribute; + + queryFlags |= QFileSystemMetaData::LinkType; + + exists = d->doStat(queryFlags); + } + + if (!exists && !d->metaData.isLink()) return ret; if (exists && (type & PermsMask)) - ret |= d->getPermissions(type); + ret |= FileFlags(uint(d->metaData.permissions())); + if (type & TypesMask) { -#if !defined(QWS) && defined(Q_OS_MAC) - bool foundAlias = false; - { - FSRef fref; - if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), - &fref, NULL) == noErr) { - Boolean isAlias, isFolder; - if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr && isAlias) { - foundAlias = true; - ret |= LinkType; - } - } - } - if (!foundAlias) -#endif - { - if ((type & LinkType) && d->isSymlink()) + if (d->metaData.isAlias()) { + ret |= LinkType; + } else { + if ((type & LinkType) && d->metaData.isLink()) ret |= LinkType; - if (exists && (d->st.st_mode & S_IFMT) == S_IFREG) - ret |= FileType; - else if (exists && (d->st.st_mode & S_IFMT) == S_IFDIR) - ret |= DirectoryType; -#if !defined(QWS) && defined(Q_OS_MAC) - if ((ret & DirectoryType) && (type & BundleType)) { - QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath), - kCFURLPOSIXPathStyle, true); - UInt32 type, creator; - if (CFBundleGetPackageInfoInDirectory(url, &type, &creator)) - ret |= BundleType; + if (exists) { + if (d->metaData.isFile()) { + ret |= FileType; + } else if (d->metaData.isDirectory()) { + ret |= DirectoryType; + if ((type & BundleType) && d->metaData.isBundle()) + ret |= BundleType; + } } -#endif } } + if (type & FlagsMask) { if (exists) ret |= ExistsFlag; -#if defined(Q_OS_SYMBIAN) - if (d->filePath == QLatin1String("/") - || (d->filePath.length() == 3 && d->filePath.at(0).isLetter() - && d->filePath.at(1) == QLatin1Char(':') && d->filePath.at(2) == QLatin1Char('/'))) { - ret |= RootFlag; - } else { - // In Symbian, all symlinks have hidden attribute for some reason; - // lets make them visible for better compatibility with other platforms. - // If somebody actually wants a hidden link, then they are out of luck. - if (!d->isSymlink() && _q_isSymbianHidden(d->filePath, ret & DirectoryType)) - ret |= HiddenFlag; - } -#else - if (d->filePath == QLatin1String("/")) { + if (d->fileEntry.isRoot()) ret |= RootFlag; - } else { - QString baseName = fileName(BaseName); - if ((baseName.size() > 0 && baseName.at(0) == QLatin1Char('.')) -# if !defined(QWS) && defined(Q_OS_MAC) - || _q_isMacHidden(d->filePath) -# endif - ) { - ret |= HiddenFlag; - } - } -#endif + else if (d->metaData.isHidden()) + ret |= HiddenFlag; } - return ret; -} -#if defined(Q_OS_SYMBIAN) -QString QFSFileEngine::fileName(FileName file) const -{ - Q_D(const QFSFileEngine); - const QLatin1Char slashChar('/'); - if(file == BaseName) { - int slash = d->filePath.lastIndexOf(slashChar); - if(slash == -1) { - int colon = d->filePath.lastIndexOf(QLatin1Char(':')); - if(colon != -1) - return d->filePath.mid(colon + 1); - return d->filePath; - } - return d->filePath.mid(slash + 1); - } else if(file == PathName) { - if(!d->filePath.size()) - return d->filePath; - - int slash = d->filePath.lastIndexOf(slashChar); - if(slash == -1) { - if(d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) - return d->filePath.left(2); - return QLatin1String("."); - } else { - if(!slash) - return QLatin1String("/"); - if(slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) - slash++; - return d->filePath.left(slash); - } - } else if(file == AbsoluteName || file == AbsolutePathName) { - QString ret; - if (!isRelativePathSymbian(d->filePath)) { - if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':') - && d->filePath.at(2) != slashChar){ - // It's a drive-relative path, so C:a.txt -> C:/currentpath/a.txt, - // or if it's different drive than current, Z:a.txt -> Z:/a.txt - QString currentPath = QDir::currentPath(); - if (0 == currentPath.left(1).compare(d->filePath.left(1), Qt::CaseInsensitive)) - ret = currentPath + slashChar + d->filePath.mid(2); - else - ret = d->filePath.left(2) + slashChar + d->filePath.mid(2); - } else if (d->filePath.startsWith(slashChar)) { - // It's a absolute path to the current drive, so /a.txt -> C:/a.txt - ret = QDir::currentPath().left(2) + d->filePath; - } else { - ret = d->filePath; - } - } else { - ret = QDir::currentPath() + slashChar + d->filePath; - } - - // The path should be absolute at this point. - // From the docs : - // Absolute paths begin with the directory separator "/" - // (optionally preceded by a drive specification under Windows). - if (ret.at(0) != slashChar) { - Q_ASSERT(ret.length() >= 2); - Q_ASSERT(ret.at(0).isLetter()); - Q_ASSERT(ret.at(1) == QLatin1Char(':')); - - // Force uppercase drive letters. - ret[0] = ret.at(0).toUpper(); - } - - // Clean up the path - bool isDir = ret.endsWith(slashChar); - ret = QDir::cleanPath(ret); - if (isDir && !ret.endsWith(slashChar)) - ret += slashChar; - - if (file == AbsolutePathName) { - int slash = ret.lastIndexOf(slashChar); - if (slash < 0) - return ret; - else if (ret.at(0) != slashChar && slash == 2) - return ret.left(3); // include the slash - else - return ret.left(slash > 0 ? slash : 1); - } - return ret; - } else if(file == CanonicalName || file == CanonicalPathName) { - if (!(fileFlags(ExistsFlag) & ExistsFlag)) - return QString(); - - QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName)); - if (file == CanonicalPathName && !ret.isEmpty()) { - int slash = ret.lastIndexOf(slashChar); - if (slash == -1) - ret = QDir::fromNativeSeparators(QDir::currentPath()); - else if (slash == 0) - ret = QLatin1String("/"); - ret = ret.left(slash); - } - return ret; - } else if(file == LinkName) { - if (d->isSymlink()) { - char s[PATH_MAX+1]; - int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX); - if (len > 0) { - s[len] = '\0'; - QString ret = QFile::decodeName(QByteArray(s)); - - if (isRelativePathSymbian(ret)) { - if (!isRelativePathSymbian(d->filePath)) { - ret.prepend(d->filePath.left(d->filePath.lastIndexOf(slashChar)) - + slashChar); - } else { - ret.prepend(QDir::currentPath() + slashChar); - } - } - ret = QDir::cleanPath(ret); - if (ret.size() > 1 && ret.endsWith(slashChar)) - ret.chop(1); - return ret; - } - } - return QString(); - } else if(file == BundleName) { - return QString(); - } - return d->filePath; + return ret; } -#else - QString QFSFileEngine::fileName(FileName file) const { Q_D(const QFSFileEngine); if (file == BundleName) { -#if !defined(QWS) && defined(Q_OS_MAC) - QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath), - kCFURLPOSIXPathStyle, true); - if (QCFType<CFDictionaryRef> dict = CFBundleCopyInfoDictionaryForURL(url)) { - if (CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) { - if (CFGetTypeID(name) == CFStringGetTypeID()) - return QCFString::toQString((CFStringRef)name); - } - } -#endif - return QString(); + return QFileSystemEngine::bundleName(d->fileEntry); } else if (file == BaseName) { - int slash = d->filePath.lastIndexOf(QLatin1Char('/')); - if (slash != -1) - return d->filePath.mid(slash + 1); + return d->fileEntry.fileName(); } else if (file == PathName) { - int slash = d->filePath.lastIndexOf(QLatin1Char('/')); - if (slash == -1) - return QLatin1String("."); - else if (!slash) - return QLatin1String("/"); - return d->filePath.left(slash); + return d->fileEntry.path(); } else if (file == AbsoluteName || file == AbsolutePathName) { - QString ret; - if (d->filePath.isEmpty() || !d->filePath.startsWith(QLatin1Char('/'))) - ret = QDir::currentPath(); - if (!d->filePath.isEmpty() && d->filePath != QLatin1String(".")) { - if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) - ret += QLatin1Char('/'); - ret += d->filePath; - } - if (ret == QLatin1String("/")) - return ret; - bool isDir = ret.endsWith(QLatin1Char('/')); - ret = QDir::cleanPath(ret); - if (isDir) - ret += QLatin1Char('/'); + QFileSystemEntry entry(QFileSystemEngine::absoluteName(d->fileEntry)); if (file == AbsolutePathName) { - int slash = ret.lastIndexOf(QLatin1Char('/')); - if (slash == -1) - return QDir::currentPath(); - else if (!slash) - return QLatin1String("/"); - return ret.left(slash); + return entry.path(); } - return ret; + return entry.filePath(); } else if (file == CanonicalName || file == CanonicalPathName) { - if (!(fileFlags(ExistsFlag) & ExistsFlag)) - return QString(); - - QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName)); - if (file == CanonicalPathName && !ret.isEmpty()) { - int slash = ret.lastIndexOf(QLatin1Char('/')); - if (slash == -1) - ret = QDir::currentPath(); - else if (slash == 0) - ret = QLatin1String("/"); - ret = ret.left(slash); - } - return ret; + QFileSystemEntry entry(QFileSystemEngine::canonicalName(d->fileEntry, d->metaData)); + if (file == CanonicalPathName) + return entry.path(); + return entry.filePath(); } else if (file == LinkName) { if (d->isSymlink()) { -#if defined(__GLIBC__) && !defined(PATH_MAX) -#define PATH_CHUNK_SIZE 256 - char *s = 0; - int len = -1; - int size = PATH_CHUNK_SIZE; - - while (1) { - s = q_check_ptr((char *) ::realloc(s, size)); - len = ::readlink(d->nativeFilePath.constData(), s, size); - if (len < 0) { - ::free(s); - break; - } - if (len < size) { - break; - } - size *= 2; - } -#else - char s[PATH_MAX+1]; - int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX); -#endif - if (len > 0) { - QString ret; - if (d->doStat() && S_ISDIR(d->st.st_mode) && s[0] != '/') { - QDir parent(d->filePath); - parent.cdUp(); - ret = parent.path(); - if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) - ret += QLatin1Char('/'); - } - s[len] = '\0'; - ret += QFile::decodeName(QByteArray(s)); -#if defined(__GLIBC__) && !defined(PATH_MAX) - ::free(s); -#endif - - if (!ret.startsWith(QLatin1Char('/'))) { - if (d->filePath.startsWith(QLatin1Char('/'))) { - ret.prepend(d->filePath.left(d->filePath.lastIndexOf(QLatin1Char('/'))) - + QLatin1Char('/')); - } else { - ret.prepend(QDir::currentPath() + QLatin1Char('/')); - } - } - ret = QDir::cleanPath(ret); - if (ret.size() > 1 && ret.endsWith(QLatin1Char('/'))) - ret.chop(1); - return ret; - } - } -#if !defined(QWS) && defined(Q_OS_MAC) - { - FSRef fref; - if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), &fref, 0) == noErr) { - Boolean isAlias, isFolder; - if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) { - AliasHandle alias; - if (FSNewAlias(0, &fref, &alias) == noErr && alias) { - QCFString cfstr; - if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr) - return QCFString::toQString(cfstr); - } - } - } + QFileSystemEntry entry = QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData); + return entry.filePath(); } -#endif return QString(); } - return d->filePath; + return d->fileEntry.filePath(); } -#endif // Q_OS_SYMBIAN bool QFSFileEngine::isRelativePath() const { Q_D(const QFSFileEngine); #if defined(Q_OS_SYMBIAN) - return isRelativePathSymbian(d->filePath); + return isRelativePathSymbian(d->fileEntry.filePath()); #else - return d->filePath.length() ? d->filePath[0] != QLatin1Char('/') : true; + return d->fileEntry.filePath().length() ? d->fileEntry.filePath()[0] != QLatin1Char('/') : true; #endif } @@ -1169,101 +911,73 @@ uint QFSFileEngine::ownerId(FileOwner own) const { Q_D(const QFSFileEngine); static const uint nobodyID = (uint) -2; - if (d->doStat()) { - if (own == OwnerUser) - return d->st.st_uid; - else - return d->st.st_gid; - } + + if (d->doStat(QFileSystemMetaData::OwnerIds)) + return d->metaData.ownerId(own); + return nobodyID; } QString QFSFileEngine::owner(FileOwner own) const { -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) - int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); - if (size_max == -1) - size_max = 1024; - QVarLengthArray<char, 1024> buf(size_max); -#endif - - if (own == OwnerUser) { - struct passwd *pw = 0; -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) - struct passwd entry; - getpwuid_r(ownerId(own), &entry, buf.data(), buf.size(), &pw); -#else - pw = getpwuid(ownerId(own)); -#endif - if (pw) - return QFile::decodeName(QByteArray(pw->pw_name)); - } else if (own == OwnerGroup) { -#if !defined(Q_OS_SYMBIAN) - struct group *gr = 0; -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) - size_max = sysconf(_SC_GETGR_R_SIZE_MAX); - if (size_max == -1) - size_max = 1024; - buf.resize(size_max); - struct group entry; - // Some large systems have more members than the POSIX max size - // Loop over by doubling the buffer size (upper limit 250k) - for (unsigned size = size_max; size < 256000; size += size) - { - buf.resize(size); - // ERANGE indicates that the buffer was too small - if (!getgrgid_r(ownerId(own), &entry, buf.data(), buf.size(), &gr) - || errno != ERANGE) - break; - } +#ifndef Q_OS_SYMBIAN + if (own == OwnerUser) + return QFileSystemEngine::resolveUserName(ownerId(own)); + return QFileSystemEngine::resolveGroupName(ownerId(own)); #else - gr = getgrgid(ownerId(own)); -#endif - if (gr) - return QFile::decodeName(QByteArray(gr->gr_name)); -#endif - } return QString(); +#endif } bool QFSFileEngine::setPermissions(uint perms) { Q_D(QFSFileEngine); + QSystemError error; + if (!QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0)) { + setError(QFile::PermissionsError, error.toString()); + return false; + } + return true; +} + +#ifdef Q_OS_SYMBIAN +bool QFSFileEngine::setSize(qint64 size) +{ + Q_D(QFSFileEngine); bool ret = false; - mode_t mode = 0; - if (perms & ReadOwnerPerm) - mode |= S_IRUSR; - if (perms & WriteOwnerPerm) - mode |= S_IWUSR; - if (perms & ExeOwnerPerm) - mode |= S_IXUSR; - if (perms & ReadUserPerm) - mode |= S_IRUSR; - if (perms & WriteUserPerm) - mode |= S_IWUSR; - if (perms & ExeUserPerm) - mode |= S_IXUSR; - if (perms & ReadGroupPerm) - mode |= S_IRGRP; - if (perms & WriteGroupPerm) - mode |= S_IWGRP; - if (perms & ExeGroupPerm) - mode |= S_IXGRP; - if (perms & ReadOtherPerm) - mode |= S_IROTH; - if (perms & WriteOtherPerm) - mode |= S_IWOTH; - if (perms & ExeOtherPerm) - mode |= S_IXOTH; - if (d->fd != -1) - ret = fchmod(d->fd, mode) == 0; - else - ret = ::chmod(d->nativeFilePath.constData(), mode) == 0; - if (!ret) - setError(QFile::PermissionsError, qt_error_string(errno)); + TInt err = KErrNone; + if (d->symbianFile.SubSessionHandle()) { + TInt err = d->symbianFile.SetSize(size); + ret = (err == KErrNone); + if (ret && d->symbianFilePos > size) + d->symbianFilePos = size; + } + else if (d->fd != -1) + ret = QT_FTRUNCATE(d->fd, size) == 0; + else if (d->fh) + ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0; + else { + RFile tmp; + QString symbianFilename(d->fileEntry.nativeFilePath()); + err = tmp.Open(qt_s60GetRFs(), qt_QString2TPtrC(symbianFilename), EFileWrite); + if (err == KErrNone) + { + err = tmp.SetSize(size); + tmp.Close(); + } + ret = (err == KErrNone); + } + if (!ret) { + QSystemError error; + if (err) + error = QSystemError(err, QSystemError::NativeError); + else + error = QSystemError(errno, QSystemError::StandardLibraryError); + setError(QFile::ResizeError, error.toString()); + } return ret; } - +#else bool QFSFileEngine::setSize(qint64 size) { Q_D(QFSFileEngine); @@ -1273,25 +987,21 @@ bool QFSFileEngine::setSize(qint64 size) else if (d->fh) ret = QT_FTRUNCATE(QT_FILENO(d->fh), size) == 0; else - ret = QT_TRUNCATE(d->nativeFilePath.constData(), size) == 0; + ret = QT_TRUNCATE(d->fileEntry.nativeFilePath().constData(), size) == 0; if (!ret) setError(QFile::ResizeError, qt_error_string(errno)); return ret; } +#endif QDateTime QFSFileEngine::fileTime(FileTime time) const { Q_D(const QFSFileEngine); - QDateTime ret; - if (d->doStat()) { - if (time == CreationTime) - ret.setTime_t(d->st.st_ctime ? d->st.st_ctime : d->st.st_mtime); - else if (time == ModificationTime) - ret.setTime_t(d->st.st_mtime); - else if (time == AccessTime) - ret.setTime_t(d->st.st_atime); - } - return ret; + + if (d->doStat(QFileSystemMetaData::Times)) + return d->metaData.fileTime(time); + + return QDateTime(); } uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags) @@ -1311,15 +1021,19 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla // If we know the mapping will extend beyond EOF, fail early to avoid // undefined behavior. Otherwise, let mmap have its say. - if (doStat() - && (QT_OFF_T(size) > st.st_size - QT_OFF_T(offset))) + if (doStat(QFileSystemMetaData::SizeAttribute) + && (QT_OFF_T(size) > metaData.size() - QT_OFF_T(offset))) qWarning("QFSFileEngine::map: Mapping a file beyond its size is not portable"); int access = 0; if (openMode & QIODevice::ReadOnly) access |= PROT_READ; if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE; +#if defined(Q_OS_INTEGRITY) + int pageSize = sysconf(_SC_PAGESIZE); +#else int pageSize = getpagesize(); +#endif int extra = offset % pageSize; if (quint64(size + extra) > quint64((size_t)-1)) { @@ -1334,7 +1048,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla #ifdef Q_OS_SYMBIAN void *mapAddress; TRAPD(err, mapAddress = QT_MMAP((void*)0, realSize, - access, MAP_SHARED, nativeHandle(), realOffset)); + access, MAP_SHARED, getMapHandle(), realOffset)); if (err != KErrNone) { qWarning("OpenC bug: leave from mmap %d", err); mapAddress = MAP_FAILED; @@ -1369,6 +1083,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla bool QFSFileEnginePrivate::unmap(uchar *ptr) { +#if !defined(Q_OS_INTEGRITY) Q_Q(QFSFileEngine); if (!maps.contains(ptr)) { q->setError(QFile::PermissionsError, qt_error_string(EACCES)); @@ -1383,6 +1098,9 @@ bool QFSFileEnginePrivate::unmap(uchar *ptr) } maps.remove(ptr); return true; +#else + return false; +#endif } QT_END_NAMESPACE diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index d51cae4..9c858c2 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -43,7 +43,7 @@ #include "qplatformdefs.h" #include "qabstractfileengine.h" #include "private/qfsfileengine_p.h" -#include <private/qsystemlibrary_p.h> +#include "qfilesystemengine_p.h" #include <qdebug.h> #include "qfile.h" @@ -69,226 +69,12 @@ #define SECURITY_WIN32 #include <security.h> -#ifndef _INTPTR_T_DEFINED -#ifdef _WIN64 -typedef __int64 intptr_t; -#else -#ifdef _W64 -typedef _W64 int intptr_t; -#else -typedef INT_PTR intptr_t; -#endif -#endif -#define _INTPTR_T_DEFINED -#endif - -#ifndef INVALID_FILE_ATTRIBUTES -# define INVALID_FILE_ATTRIBUTES (DWORD (-1)) +#ifndef PATH_MAX +#define PATH_MAX FILENAME_MAX #endif -#if !defined(Q_OS_WINCE) -# if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - }; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; -# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) -# endif // !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) - -# ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE -# define MAXIMUM_REPARSE_DATA_BUFFER_SIZE 16384 -# endif -# ifndef IO_REPARSE_TAG_SYMLINK -# define IO_REPARSE_TAG_SYMLINK (0xA000000CL) -# endif -# ifndef FSCTL_GET_REPARSE_POINT -# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) -# endif -#endif // !defined(Q_OS_WINCE) - QT_BEGIN_NAMESPACE -static QString readLink(const QString &link); - -Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0; - -#if defined(Q_OS_WINCE) -static QString qfsPrivateCurrentDir = QLatin1String(""); -// As none of the functions we try to resolve do exist on Windows CE -// we use QT_NO_LIBRARY to shorten everything up a little bit. -#define QT_NO_LIBRARY 1 -#endif - -#if !defined(QT_NO_LIBRARY) -QT_BEGIN_INCLUDE_NAMESPACE -typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*); -static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0; -typedef BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE); -static PtrLookupAccountSidW ptrLookupAccountSidW = 0; -typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID); -static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0; -typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK); -static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0; -static TRUSTEE_W currentUserTrusteeW; -static TRUSTEE_W worldTrusteeW; - -typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); -static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0; -QT_END_INCLUDE_NAMESPACE - - -void QFSFileEnginePrivate::resolveLibs() -{ - static bool triedResolve = false; - if (!triedResolve) { - // need to resolve the security info functions - - // protect initialization -#ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); - // check triedResolve again, since another thread may have already - // done the initialization - if (triedResolve) { - // another thread did initialize the security function pointers, - // so we shouldn't do it again. - return; - } -#endif - - triedResolve = true; -#if !defined(Q_OS_WINCE) - HINSTANCE advapiHnd = QSystemLibrary::load(L"advapi32"); - if (advapiHnd) { - ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW"); - ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW"); - ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW"); - ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW"); - } - if (ptrBuildTrusteeWithSidW) { - // Create TRUSTEE for current user - HANDLE hnd = ::GetCurrentProcess(); - HANDLE token = 0; - if (::OpenProcessToken(hnd, TOKEN_QUERY, &token)) { - TOKEN_USER tu; - DWORD retsize; - if (::GetTokenInformation(token, TokenUser, &tu, sizeof(tu), &retsize)) - ptrBuildTrusteeWithSidW(¤tUserTrusteeW, tu.User.Sid); - ::CloseHandle(token); - } - - typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*); - PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid"); - typedef PVOID (WINAPI *PtrFreeSid)(PSID); - PtrFreeSid ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid"); - if (ptrAllocateAndInitializeSid && ptrFreeSid) { - // Create TRUSTEE for Everyone (World) - SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY }; - PSID pWorld = 0; - if (ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pWorld)) - ptrBuildTrusteeWithSidW(&worldTrusteeW, pWorld); - ptrFreeSid(pWorld); - } - } - HINSTANCE userenvHnd = QSystemLibrary::load(L"userenv"); - if (userenvHnd) - ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW"); -#endif - } -} -#endif // QT_NO_LIBRARY - -typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD); -static PtrNetShareEnum ptrNetShareEnum = 0; -typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID); -static PtrNetApiBufferFree ptrNetApiBufferFree = 0; -typedef struct _SHARE_INFO_1 { - LPWSTR shi1_netname; - DWORD shi1_type; - LPWSTR shi1_remark; -} SHARE_INFO_1; - - -bool QFSFileEnginePrivate::resolveUNCLibs() -{ - static bool triedResolve = false; - if (!triedResolve) { -#ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); - if (triedResolve) { - return ptrNetShareEnum && ptrNetApiBufferFree; - } -#endif - triedResolve = true; -#if !defined(Q_OS_WINCE) - HINSTANCE hLib = QSystemLibrary::load(L"Netapi32"); - if (hLib) { - ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum"); - if (ptrNetShareEnum) - ptrNetApiBufferFree = (PtrNetApiBufferFree)GetProcAddress(hLib, "NetApiBufferFree"); - } -#endif - } - return ptrNetShareEnum && ptrNetApiBufferFree; -} - -bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list) -{ - if (resolveUNCLibs()) { - SHARE_INFO_1 *BufPtr, *p; - DWORD res; - DWORD er = 0, tr = 0, resume = 0, i; - do { - res = ptrNetShareEnum((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume); - if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) { - p = BufPtr; - for (i = 1; i <= er; ++i) { - if (list && p->shi1_type == 0) - list->append(QString::fromWCharArray(p->shi1_netname)); - p++; - } - } - ptrNetApiBufferFree(BufPtr); - } while (res == ERROR_MORE_DATA); - return res == ERROR_SUCCESS; - } - return false; -} - -static bool isUncRoot(const QString &server) -{ - QString localPath = QDir::toNativeSeparators(server); - if (!localPath.startsWith(QLatin1String("\\\\"))) - return false; - - int idx = localPath.indexOf(QLatin1Char('\\'), 2); - if (idx == -1 || idx + 1 == localPath.length()) - return true; - - localPath = localPath.right(localPath.length() - idx - 1).trimmed(); - return localPath.isEmpty(); -} - #if !defined(Q_OS_WINCE) static inline bool isUncPath(const QString &path) { @@ -298,73 +84,6 @@ static inline bool isUncPath(const QString &path) } #endif -static inline bool isRelativePath(const QString &path) -{ - // drive, e.g. "a:", or UNC root, e.q. "//" - return !(path.startsWith(QLatin1Char('/')) - || (path.length() >= 2 - && ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':')) - || (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/'))))); -} - -static QString fixIfRelativeUncPath(const QString &path) -{ - if (isRelativePath(path)) { - QString currentPath = QDir::currentPath() + QLatin1Char('/'); - if (currentPath.startsWith(QLatin1String("//"))) - return QString(path).prepend(currentPath); - } - return path; -} - -// can be //server or //server/share -static bool uncShareExists(const QString &server) -{ - QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts); - if (parts.count()) { - QStringList shares; - if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), &shares)) - return parts.count() >= 2 ? shares.contains(parts.at(1), Qt::CaseInsensitive) : true; - } - return false; -} - -static inline bool isDriveRoot(const QString &path) -{ - return (path.length() == 3 - && path.at(0).isLetter() && path.at(1) == QLatin1Char(':') - && path.at(2) == QLatin1Char('/')); -} - -static QString nativeAbsoluteFilePath(const QString &path) -{ - QString absPath; -#if !defined(Q_OS_WINCE) - QVarLengthArray<wchar_t, MAX_PATH> buf(qMax(MAX_PATH, path.size() + 1)); - wchar_t *fileName = 0; - DWORD retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); - if (retLen > (DWORD)buf.size()) { - buf.resize(retLen); - retLen = GetFullPathName((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName); - } - if (retLen != 0) - absPath = QString::fromWCharArray(buf.data(), retLen); -#else - if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'))) - absPath = QDir::toNativeSeparators(path); - else - absPath = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path)); -#endif - // This is really ugly, but GetFullPathName strips off whitespace at the end. - // If you for instance write ". " in the lineedit of QFileDialog, - // (which is an invalid filename) this function will strip the space off and viola, - // the file is later reported as existing. Therefore, we re-add the whitespace that - // was at the end of path in order to keep the filename invalid. - if (!path.isEmpty() && path.at(path.size() - 1) == QLatin1Char(' ')) - absPath.append(QLatin1Char(' ')); - return absPath; -} - /*! \internal */ @@ -373,7 +92,7 @@ QString QFSFileEnginePrivate::longFileName(const QString &path) if (path.startsWith(QLatin1String("\\\\.\\"))) return path; - QString absPath = nativeAbsoluteFilePath(path); + QString absPath = QFileSystemEngine::nativeAbsoluteFilePath(path); #if !defined(Q_OS_WINCE) QString prefix = QLatin1String("\\\\?\\"); if (isUncPath(absPath)) { @@ -389,15 +108,6 @@ QString QFSFileEnginePrivate::longFileName(const QString &path) /* \internal */ -void QFSFileEnginePrivate::nativeInitFileName() -{ - QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath))); - nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1); -} - -/* - \internal -*/ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) { Q_Q(QFSFileEngine); @@ -415,9 +125,8 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) // WriteOnly can create files, ReadOnly cannot. DWORD creationDisp = (openMode & QIODevice::WriteOnly) ? OPEN_ALWAYS : OPEN_EXISTING; - // Create the file handle. - fileHandle = CreateFile((const wchar_t*)nativeFilePath.constData(), + fileHandle = CreateFile((const wchar_t*)fileEntry.nativeFilePath().utf16(), accessRights, shareMode, &securityAtts, @@ -507,17 +216,9 @@ qint64 QFSFileEnginePrivate::nativeSize() const // ### Don't flush; for buffered files, we should get away with ftell. thatQ->flush(); -#if !defined(Q_OS_WINCE) - // stdlib/stdio mode. - if (fh || fd != -1) { - qint64 fileSize = _filelengthi64(fh ? QT_FILENO(fh) : fd); - if (fileSize == -1) { - fileSize = 0; - thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno)); - } - return fileSize; - } -#else // Q_OS_WINCE + // Always retrive the current information + metaData.clearFlags(QFileSystemMetaData::SizeAttribute); +#if defined(Q_OS_WINCE) // Buffered stdlib mode. if (fh) { QT_OFF_T oldPos = QT_FTELL(fh); @@ -530,70 +231,22 @@ qint64 QFSFileEnginePrivate::nativeSize() const } return fileSize; } -#endif - - // Not-open mode, where the file name is known: We'll check the - // file system directly. - if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) { - WIN32_FILE_ATTRIBUTE_DATA attribData; - bool ok = ::GetFileAttributesEx((const wchar_t*)nativeFilePath.constData(), - GetFileExInfoStandard, &attribData); - if (!ok) { - int errorCode = GetLastError(); - if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { - QByteArray path = nativeFilePath; - // path for the FindFirstFile should not end with a trailing slash - while (!path.isEmpty() && reinterpret_cast<const wchar_t *>( - path.constData() + path.length())[-1] == '\\') - path.chop(2); - - // FindFirstFile can not handle drives - if (!path.isEmpty() && reinterpret_cast<const wchar_t *>( - path.constData() + path.length())[-1] != ':') { - WIN32_FIND_DATA findData; - HANDLE hFind = ::FindFirstFile((const wchar_t*)path.constData(), - &findData); - if (hFind != INVALID_HANDLE_VALUE) { - ::FindClose(hFind); - ok = true; - attribData.nFileSizeHigh = findData.nFileSizeHigh; - attribData.nFileSizeLow = findData.nFileSizeLow; - } - } - } - } - if (ok) { - qint64 size = attribData.nFileSizeHigh; - size <<= 32; - size += attribData.nFileSizeLow; - return size; - } - thatQ->setError(QFile::UnspecifiedError, qt_error_string()); - return 0; - } - -#if defined(Q_OS_WINCE) - // Unbuffed stdio mode if (fd != -1) { thatQ->setError(QFile::UnspecifiedError, QLatin1String("Not implemented!")); return 0; } #endif + bool filled = false; + if (fileHandle != INVALID_HANDLE_VALUE && openMode != QIODevice::NotOpen ) + filled = QFileSystemEngine::fillMetaData(fileHandle, metaData, + QFileSystemMetaData::SizeAttribute); + else + filled = doStat(QFileSystemMetaData::SizeAttribute); - // Windows native mode. - if (fileHandle == INVALID_HANDLE_VALUE) - return 0; - - BY_HANDLE_FILE_INFORMATION fileInfo; - if (!GetFileInformationByHandle(fileHandle, &fileInfo)) { - thatQ->setError(QFile::UnspecifiedError, qt_error_string()); - return 0; + if (!filled) { + thatQ->setError(QFile::UnspecifiedError, qt_error_string(errno)); } - - qint64 size = fileInfo.nFileSizeHigh; - size <<= 32; - size += fileInfo.nFileSizeLow; - return size; + return metaData.size(); } /* @@ -827,159 +480,41 @@ bool QFSFileEnginePrivate::nativeIsSequential() const bool QFSFileEngine::remove() { Q_D(QFSFileEngine); - bool ret = ::DeleteFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0; + QSystemError error; + bool ret = QFileSystemEngine::removeFile(d->fileEntry, error); if (!ret) - setError(QFile::RemoveError, qt_error_string()); + setError(QFile::RemoveError, error.toString()); return ret; } bool QFSFileEngine::copy(const QString ©Name) { Q_D(QFSFileEngine); - bool ret = ::CopyFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), - (wchar_t*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0; + QSystemError error; + bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), error); if (!ret) - setError(QFile::CopyError, qt_error_string()); + setError(QFile::CopyError, error.toString()); return ret; } bool QFSFileEngine::rename(const QString &newName) { Q_D(QFSFileEngine); - bool ret = ::MoveFile((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), - (wchar_t*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0; + QSystemError error; + bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error); if (!ret) - setError(QFile::RenameError, qt_error_string()); + setError(QFile::RenameError, error.toString()); return ret; } -static inline bool mkDir(const QString &path) -{ -#if defined(Q_OS_WINCE) - // Unfortunately CreateDirectory returns true for paths longer than - // 256, but does not create a directory. It starts to fail, when - // path length > MAX_PATH, which is 260 usually on CE. - // This only happens on a Windows Mobile device. Windows CE seems - // not to be affected by this. - static int platformId = 0; - if (platformId == 0) { - wchar_t platformString[64]; - if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) { - if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone")) - platformId = 1; - else - platformId = 2; - } - } - if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256) - return false; -#endif - return ::CreateDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), 0); -} - -static inline bool rmDir(const QString &path) -{ - return ::RemoveDirectory((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); -} - -static bool isDirPath(const QString &dirPath, bool *existed) -{ - QString path = dirPath; - if (path.length() == 2 && path.at(1) == QLatin1Char(':')) - path += QLatin1Char('\\'); - - DWORD fileAttrib = ::GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16()); - if (fileAttrib == INVALID_FILE_ATTRIBUTES) { - int errorCode = GetLastError(); - if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { - // path for the FindFirstFile should not end with a trailing slash - while (path.endsWith(QLatin1Char('\\'))) - path.chop(1); - - // FindFirstFile can not handle drives - if (!path.endsWith(QLatin1Char(':'))) { - WIN32_FIND_DATA findData; - HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), - &findData); - if (hFind != INVALID_HANDLE_VALUE) { - ::FindClose(hFind); - fileAttrib = findData.dwFileAttributes; - } - } - } - } - - if (existed) - *existed = fileAttrib != INVALID_FILE_ATTRIBUTES; - - if (fileAttrib == INVALID_FILE_ATTRIBUTES) - return false; - - return fileAttrib & FILE_ATTRIBUTE_DIRECTORY; -} - bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const { - QString dirName = name; - if (createParentDirectories) { - dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); - // We spefically search for / so \ would break it.. - int oldslash = -1; - if (dirName.startsWith(QLatin1String("\\\\"))) { - // Don't try to create the root path of a UNC path; - // CreateDirectory() will just return ERROR_INVALID_NAME. - for (int i = 0; i < dirName.size(); ++i) { - if (dirName.at(i) != QDir::separator()) { - oldslash = i; - break; - } - } - if (oldslash != -1) - oldslash = dirName.indexOf(QDir::separator(), oldslash); - } - for (int slash=0; slash != -1; oldslash = slash) { - slash = dirName.indexOf(QDir::separator(), oldslash+1); - if (slash == -1) { - if (oldslash == dirName.length()) - break; - slash = dirName.length(); - } - if (slash) { - QString chunk = dirName.left(slash); - bool existed = false; - if (!isDirPath(chunk, &existed)) { - if (!existed) { - if (!mkDir(chunk)) - return false; - } else { - return false; - } - } - } - } - return true; - } - return mkDir(name); + return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories); } bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const { - QString dirName = name; - if (recurseParentDirectories) { - dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); - for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { - QString chunk = dirName.left(slash); - if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':')) - break; - if (!isDirPath(chunk, 0)) - return false; - if (!rmDir(chunk)) - return oldslash != 0; - slash = dirName.lastIndexOf(QDir::separator(), oldslash-1); - } - return true; - } - return rmDir(name); + return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), recurseParentDirectories); } bool QFSFileEngine::caseSensitive() const @@ -989,15 +524,7 @@ bool QFSFileEngine::caseSensitive() const bool QFSFileEngine::setCurrentPath(const QString &path) { - if (!QDir(path).exists()) - return false; - -#if !defined(Q_OS_WINCE) - return ::SetCurrentDirectory((wchar_t*)path.utf16()) != 0; -#else - qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path); - return true; -#endif + return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path)); } QString QFSFileEngine::currentPath(const QString &fileName) @@ -1016,115 +543,30 @@ QString QFSFileEngine::currentPath(const QString &fileName) } if (ret.isEmpty()) { //just the pwd - DWORD size = 0; - wchar_t currentName[PATH_MAX]; - size = ::GetCurrentDirectory(PATH_MAX, currentName); - if (size != 0) { - if (size > PATH_MAX) { - wchar_t *newCurrentName = new wchar_t[size]; - if (::GetCurrentDirectory(PATH_MAX, newCurrentName) != 0) - ret = QString::fromWCharArray(newCurrentName); - delete [] newCurrentName; - } else { - ret = QString::fromWCharArray(currentName); - } - } + ret = QFileSystemEngine::currentPath().filePath(); } if (ret.length() >= 2 && ret[1] == QLatin1Char(':')) ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters. - return QDir::fromNativeSeparators(ret); + return ret; #else Q_UNUSED(fileName); - if (qfsPrivateCurrentDir.isEmpty()) - qfsPrivateCurrentDir = QCoreApplication::applicationDirPath(); - - return QDir::fromNativeSeparators(qfsPrivateCurrentDir); + return QFileSystemEngine::currentPath(); #endif } QString QFSFileEngine::homePath() { - QString ret; -#if !defined(QT_NO_LIBRARY) - QFSFileEnginePrivate::resolveLibs(); - if (ptrGetUserProfileDirectoryW) { - HANDLE hnd = ::GetCurrentProcess(); - HANDLE token = 0; - BOOL ok = ::OpenProcessToken(hnd, TOKEN_QUERY, &token); - if (ok) { - DWORD dwBufferSize = 0; - // First call, to determine size of the strings (with '\0'). - ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize); - if (!ok && dwBufferSize != 0) { // We got the required buffer size - wchar_t *userDirectory = new wchar_t[dwBufferSize]; - // Second call, now we can fill the allocated buffer. - ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize); - if (ok) - ret = QString::fromWCharArray(userDirectory); - - delete [] userDirectory; - } - ::CloseHandle(token); - } - } -#endif - if (ret.isEmpty() || !QFile::exists(ret)) { - ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData()); - if (ret.isEmpty() || !QFile::exists(ret)) { - ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData()); - if (ret.isEmpty() || !QFile::exists(ret)) { - ret = QString::fromLocal8Bit(qgetenv("HOME").constData()); - if (ret.isEmpty() || !QFile::exists(ret)) { -#if defined(Q_OS_WINCE) - ret = QLatin1String("\\My Documents"); - if (!QFile::exists(ret)) -#endif - ret = rootPath(); - } - } - } - } - return QDir::fromNativeSeparators(ret); + return QFileSystemEngine::homePath(); } QString QFSFileEngine::rootPath() { -#if defined(Q_OS_WINCE) - QString ret = QLatin1String("/"); -#elif defined(Q_FS_FAT) - QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData()); - if (ret.isEmpty()) - ret = QLatin1String("c:"); - ret.append(QLatin1Char('/')); -#elif defined(Q_OS_OS2EMX) - char dir[4]; - _abspath(dir, QLatin1String("/"), _MAX_PATH); - QString ret(dir); -#endif - return ret; + return QFileSystemEngine::rootPath(); } QString QFSFileEngine::tempPath() { - QString ret; - { - wchar_t tempPath[MAX_PATH]; - if (GetTempPath(MAX_PATH, tempPath)) - ret = QString::fromWCharArray(tempPath); - if (!ret.isEmpty()) { - while (ret.endsWith(QLatin1Char('\\'))) - ret.chop(1); - ret = QDir::fromNativeSeparators(ret); - } - } - if (ret.isEmpty()) { -#if !defined(Q_OS_WINCE) - ret = QLatin1String("c:/tmp"); -#else - ret = QLatin1String("/Temp"); -#endif - } - return ret; + return QFileSystemEngine::tempPath(); } QFileInfoList QFSFileEngine::drives() @@ -1154,213 +596,25 @@ QFileInfoList QFSFileEngine::drives() #endif } -bool QFSFileEnginePrivate::doStat() const +bool QFSFileEnginePrivate::doStat(QFileSystemMetaData::MetaDataFlags flags) const { - if (!tried_stat) { + if (!tried_stat || !metaData.hasFlags(flags)) { tried_stat = true; - could_stat = false; - - if (filePath.isEmpty()) - return could_stat; - QString fname; - if(filePath.endsWith(QLatin1String(".lnk"))) { - fname = readLink(filePath); - if(fname.isEmpty()) - return could_stat; - } - else - fname = filePath; - - fname = fixIfRelativeUncPath(fname); - - UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); - - if (fd != -1) { #if !defined(Q_OS_WINCE) - HANDLE fh = (HANDLE)_get_osfhandle(fd); - if (fh != INVALID_HANDLE_VALUE) { - BY_HANDLE_FILE_INFORMATION fileInfo; - if (GetFileInformationByHandle(fh, &fileInfo)) { - could_stat = true; - fileAttrib = fileInfo.dwFileAttributes; - } - } -#else - DWORD tmpAttributes = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16()); - if (tmpAttributes != -1) { - fileAttrib = tmpAttributes; - could_stat = true; - } + int localFd = fd; + if (fh && fileEntry.isEmpty()) + localFd = QT_FILENO(fh); + if (localFd != -1) + QFileSystemEngine::fillMetaData(localFd, metaData, flags); #endif - } else { - fileAttrib = GetFileAttributes((wchar_t*)QFSFileEnginePrivate::longFileName(fname).utf16()); - if (fileAttrib == INVALID_FILE_ATTRIBUTES) { - int errorCode = GetLastError(); - if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { - QString path = QDir::toNativeSeparators(fname); - // path for the FindFirstFile should not end with a trailing slash - while (path.endsWith(QLatin1Char('\\'))) - path.chop(1); - - // FindFirstFile can not handle drives - if (!path.endsWith(QLatin1Char(':'))) { - WIN32_FIND_DATA findData; - HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), - &findData); - if (hFind != INVALID_HANDLE_VALUE) { - ::FindClose(hFind); - fileAttrib = findData.dwFileAttributes; - } - } - } - } - could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES; - if (!could_stat) { -#if !defined(Q_OS_WINCE) - if (isDriveRoot(fname)) { - // a valid drive ?? - DWORD drivesBitmask = ::GetLogicalDrives(); - int drivebit = 1 << (fname.at(0).toUpper().unicode() - QLatin1Char('A').unicode()); - if (drivesBitmask & drivebit) { - fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM; - could_stat = true; - } - } else { -#endif - QString path = QDir::toNativeSeparators(fname); - bool is_dir = false; - if (path.startsWith(QLatin1String("\\\\"))) { - // UNC - stat doesn't work for all cases (Windows bug) - int s = path.indexOf(path.at(0),2); - if (s > 0) { - // "\\server\..." - s = path.indexOf(path.at(0),s+1); - if (s > 0) { - // "\\server\share\..." - if (s == path.size() - 1) { - // "\\server\share\" - is_dir = true; - } else { - // "\\server\share\notfound" - } - } else { - // "\\server\share" - is_dir = true; - } - } else { - // "\\server" - is_dir = true; - } - } - if (is_dir && uncShareExists(path)) { - // looks like a UNC dir, is a dir. - fileAttrib = FILE_ATTRIBUTE_DIRECTORY; - could_stat = true; - } -#if !defined(Q_OS_WINCE) - } -#endif - } - } - - SetErrorMode(oldmode); + if (metaData.missingFlags(flags) && !fileEntry.isEmpty()) + QFileSystemEngine::fillMetaData(fileEntry, metaData, metaData.missingFlags(flags)); } - return could_stat; -} - -static QString readSymLink(const QString &link) -{ - QString result; -#if !defined(Q_OS_WINCE) - HANDLE handle = CreateFile((wchar_t*)QFSFileEnginePrivate::longFileName(link).utf16(), - FILE_READ_EA, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - 0); - if (handle != INVALID_HANDLE_VALUE) { - DWORD bufsize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; - REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)qMalloc(bufsize); - DWORD retsize = 0; - if (::DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, 0, 0, rdb, bufsize, &retsize, 0)) { - if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - int length = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); - int offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); - const wchar_t* PathBuffer = &rdb->SymbolicLinkReparseBuffer.PathBuffer[offset]; - result = QString::fromWCharArray(PathBuffer, length); - } - // cut-off "//?/" and "/??/" - if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\')) - result = result.mid(4); - } - qFree(rdb); - CloseHandle(handle); - } -#else - Q_UNUSED(link); -#endif // Q_OS_WINCE - return result; + return metaData.exists(); } -static QString readLink(const QString &link) -{ -#if !defined(Q_OS_WINCE) -#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS) - QString ret; - - bool neededCoInit = false; - IShellLink *psl; // pointer to IShellLink i/f - WIN32_FIND_DATA wfd; - wchar_t szGotPath[MAX_PATH]; - - // Get pointer to the IShellLink interface. - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl); - - if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized - neededCoInit = true; - CoInitialize(NULL); - hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, - IID_IShellLink, (LPVOID *)&psl); - } - if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. - IPersistFile *ppf; - hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); - if (SUCCEEDED(hres)) { - hres = ppf->Load((LPOLESTR)link.utf16(), STGM_READ); - //The original path of the link is retrieved. If the file/folder - //was moved, the return value still have the old path. - if (SUCCEEDED(hres)) { - if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR) - ret = QString::fromWCharArray(szGotPath); - } - ppf->Release(); - } - psl->Release(); - } - if (neededCoInit) - CoUninitialize(); - - return ret; -#else - Q_UNUSED(link); - return QString(); -#endif // QT_NO_LIBRARY -#else - wchar_t target[MAX_PATH]; - QString result; - if (SHGetShortcutTarget((wchar_t*)QFileInfo(link).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) { - result = QString::fromWCharArray(target); - if (result.startsWith(QLatin1Char('"'))) - result.remove(0,1); - if (result.endsWith(QLatin1Char('"'))) - result.remove(result.size()-1,1); - } - return result; -#endif // Q_OS_WINCE -} bool QFSFileEngine::link(const QString &newName) { @@ -1425,191 +679,63 @@ bool QFSFileEngine::link(const QString &newName) #endif // Q_OS_WINCE } -QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const +/*! + \reimp +*/ +QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const { + Q_D(const QFSFileEngine); + + if (type & Refresh) + d->metaData.clear(); + QAbstractFileEngine::FileFlags ret = 0; -#if !defined(QT_NO_LIBRARY) - if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { - resolveLibs(); - if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) { - enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 }; - - QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath; - PSID pOwner = 0; - PSID pGroup = 0; - PACL pDacl; - PSECURITY_DESCRIPTOR pSD; - DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - &pOwner, &pGroup, &pDacl, 0, &pSD); - if(res == ERROR_SUCCESS) { - ACCESS_MASK access_mask; - TRUSTEE_W trustee; - if (type & 0x0700) { // user - if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) - access_mask = (ACCESS_MASK)-1; - if(access_mask & ReadMask) - ret |= QAbstractFileEngine::ReadUserPerm; - if(access_mask & WriteMask) - ret |= QAbstractFileEngine::WriteUserPerm; - if(access_mask & ExecMask) - ret |= QAbstractFileEngine::ExeUserPerm; - } - if (type & 0x7000) { // owner - ptrBuildTrusteeWithSidW(&trustee, pOwner); - if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) - access_mask = (ACCESS_MASK)-1; - if(access_mask & ReadMask) - ret |= QAbstractFileEngine::ReadOwnerPerm; - if(access_mask & WriteMask) - ret |= QAbstractFileEngine::WriteOwnerPerm; - if(access_mask & ExecMask) - ret |= QAbstractFileEngine::ExeOwnerPerm; - } - if (type & 0x0070) { // group - ptrBuildTrusteeWithSidW(&trustee, pGroup); - if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS) - access_mask = (ACCESS_MASK)-1; - if(access_mask & ReadMask) - ret |= QAbstractFileEngine::ReadGroupPerm; - if(access_mask & WriteMask) - ret |= QAbstractFileEngine::WriteGroupPerm; - if(access_mask & ExecMask) - ret |= QAbstractFileEngine::ExeGroupPerm; - } - if (type & 0x0007) { // other (world) - if(ptrGetEffectiveRightsFromAclW(pDacl, &worldTrusteeW, &access_mask) != ERROR_SUCCESS) - access_mask = (ACCESS_MASK)-1; // ### - if(access_mask & ReadMask) - ret |= QAbstractFileEngine::ReadOtherPerm; - if(access_mask & WriteMask) - ret |= QAbstractFileEngine::WriteOtherPerm; - if(access_mask & ExecMask) - ret |= QAbstractFileEngine::ExeOtherPerm; - } - LocalFree(pSD); - } - } - } else -#endif + if (type & FlagsMask) + ret |= LocalDiskFlag; + + bool exists; { - //### what to do with permissions if we don't use NTFS - // for now just add all permissions and what about exe missions ?? - // also qt_ntfs_permission_lookup is now not set by default ... should it ? - ret |= QAbstractFileEngine::ReadOwnerPerm | QAbstractFileEngine::ReadGroupPerm - | QAbstractFileEngine::ReadOtherPerm; - - if (!(fileAttrib & FILE_ATTRIBUTE_READONLY)) { - ret |= QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteGroupPerm - | QAbstractFileEngine::WriteOtherPerm; - } + QFileSystemMetaData::MetaDataFlags queryFlags = 0; - QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath; - QString ext = fname.right(4).toLower(); - if ((fileAttrib & FILE_ATTRIBUTE_DIRECTORY) || - ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") || - ext == QLatin1String(".pif") || ext == QLatin1String(".cmd")) { - ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm - | QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm; - } + queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type)) + & QFileSystemMetaData::Permissions; - // calculate user permissions - if (type & QAbstractFileEngine::ReadUserPerm) { - if (::_waccess((wchar_t*)longFileName(fname).utf16(), R_OK) == 0) - ret |= QAbstractFileEngine::ReadUserPerm; - } - if (type & QAbstractFileEngine::WriteUserPerm) { - if (::_waccess((wchar_t*)longFileName(fname).utf16(), W_OK) == 0) - ret |= QAbstractFileEngine::WriteUserPerm; - } - } - return ret; -} + // AliasType and BundleType are 0x0 + if (type & TypesMask) + queryFlags |= QFileSystemMetaData::AliasType + | QFileSystemMetaData::LinkType + | QFileSystemMetaData::FileType + | QFileSystemMetaData::DirectoryType + | QFileSystemMetaData::BundleType; -/*! - \internal -*/ -bool QFSFileEnginePrivate::isSymlink() const -{ -#if !defined(Q_OS_WINCE) - if (need_lstat) { - need_lstat = false; - is_link = false; - - if (fileAttrib & FILE_ATTRIBUTE_REPARSE_POINT) { - QString path = QDir::toNativeSeparators(filePath); - // path for the FindFirstFile should not end with a trailing slash - while (path.endsWith(QLatin1Char('\\'))) - path.chop(1); - - WIN32_FIND_DATA findData; - HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), - &findData); - if (hFind != INVALID_HANDLE_VALUE) { - ::FindClose(hFind); - if ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - && findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { - is_link = true; - } - } - } - } - return is_link; -#else - return false; -#endif // Q_OS_WINCE -} + if (type & FlagsMask) + queryFlags |= QFileSystemMetaData::HiddenAttribute + | QFileSystemMetaData::ExistsAttribute; -/*! - \reimp -*/ -QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const -{ - Q_D(const QFSFileEngine); - QAbstractFileEngine::FileFlags ret = 0; - // Force a stat, so that we're guaranteed to get up-to-date results - if (type & Refresh) { - d->tried_stat = 0; -#if !defined(Q_OS_WINCE) - d->need_lstat = 1; -#endif - } + queryFlags |= QFileSystemMetaData::LinkType; - if (type & PermsMask) { - if (d->doStat()) { - ret |= ExistsFlag; - ret |= d->getPermissions(type); - } + exists = d->doStat(queryFlags); } + + if (exists && (type & PermsMask)) + ret |= FileFlags(uint(d->metaData.permissions())); + if (type & TypesMask) { - if (d->filePath.endsWith(QLatin1String(".lnk"))) { + if ((type & LinkType) && d->metaData.isLegacyLink()) ret |= LinkType; - QString l = readLink(d->filePath); - if (!l.isEmpty()) { - bool existed = false; - if (isDirPath(l, &existed) && existed) - ret |= DirectoryType; - else if (existed) - ret |= FileType; - } - } else if (d->doStat()) { - if ((type & LinkType) && d->isSymlink()) - ret |= LinkType; - if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) { - ret |= DirectoryType; - } else { - ret |= FileType; - } + if (d->metaData.isDirectory()) { + ret |= DirectoryType; + } else { + ret |= FileType; } } if (type & FlagsMask) { - ret |= LocalDiskFlag; - if (d->doStat()) { + if (d->metaData.exists()) { ret |= ExistsFlag; - if (d->filePath == QLatin1String("/") || isDriveRoot(d->filePath) || isUncRoot(d->filePath)) + if (d->fileEntry.isRoot()) ret |= RootFlag; - else if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN) + else if (d->metaData.isHidden()) ret |= HiddenFlag; } } @@ -1620,48 +746,28 @@ QString QFSFileEngine::fileName(FileName file) const { Q_D(const QFSFileEngine); if (file == BaseName) { - int slash = d->filePath.lastIndexOf(QLatin1Char('/')); - if (slash == -1) { - int colon = d->filePath.lastIndexOf(QLatin1Char(':')); - if (colon != -1) - return d->filePath.mid(colon + 1); - return d->filePath; - } - return d->filePath.mid(slash + 1); + return d->fileEntry.fileName(); } else if (file == PathName) { - if (!d->filePath.size()) - return d->filePath; - - int slash = d->filePath.lastIndexOf(QLatin1Char('/')); - if (slash == -1) { - if (d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) - return d->filePath.left(2); - return QString(QLatin1Char('.')); - } else { - if (!slash) - return QString(QLatin1Char('/')); - if (slash == 2 && d->filePath.length() >= 2 && d->filePath.at(1) == QLatin1Char(':')) - slash++; - return d->filePath.left(slash); - } + return d->fileEntry.path(); } else if (file == AbsoluteName || file == AbsolutePathName) { QString ret; if (!isRelativePath()) { #if !defined(Q_OS_WINCE) - if ((d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':') - && d->filePath.at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt - d->filePath.startsWith(QLatin1Char('/')) // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt - ) { - ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(d->filePath)); - } else { - ret = d->filePath; + if (d->fileEntry.filePath().startsWith(QLatin1Char('/')) || // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt + d->fileEntry.filePath().size() == 2 || // It's a drive letter that needs to get a working dir appended + (d->fileEntry.filePath().size() > 2 && d->fileEntry.filePath().at(2) != QLatin1Char('/')) || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt + d->fileEntry.filePath().contains(QLatin1String("/../")) || d->fileEntry.filePath().contains(QLatin1String("/./")) || + d->fileEntry.filePath().endsWith(QLatin1String("/..")) || d->fileEntry.filePath().endsWith(QLatin1String("/."))) + { + ret = QDir::fromNativeSeparators(QFileSystemEngine::nativeAbsoluteFilePath(d->fileEntry.filePath())); + } else +#endif + { + ret = d->fileEntry.filePath(); } -#else - ret = d->filePath; -#endif } else { - ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->filePath); + ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + d->fileEntry.filePath()); } // The path should be absolute at this point. @@ -1690,38 +796,24 @@ QString QFSFileEngine::fileName(FileName file) const } else if (file == CanonicalName || file == CanonicalPathName) { if (!(fileFlags(ExistsFlag) & ExistsFlag)) return QString(); + QFileSystemEntry entry(QFileSystemEngine::canonicalName(QFileSystemEntry(fileName(AbsoluteName)), d->metaData)); - QString ret = QFSFileEnginePrivate::canonicalized(fileName(AbsoluteName)); - if (!ret.isEmpty() && file == CanonicalPathName) { - int slash = ret.lastIndexOf(QLatin1Char('/')); - if (slash == -1) - ret = QDir::currentPath(); - else if (slash == 0) - ret = QString(QLatin1Char('/')); - ret = ret.left(slash); - } - return ret; + if (file == CanonicalPathName) + return entry.path(); + return entry.filePath(); } else if (file == LinkName) { - QString ret; - if (d->filePath.endsWith(QLatin1String(".lnk"))) - ret = readLink(d->filePath); - else if (d->doStat() && d->isSymlink()) - ret = readSymLink(d->filePath); - return QDir::fromNativeSeparators(ret); + return QFileSystemEngine::getLinkTarget(d->fileEntry, d->metaData).filePath(); } else if (file == BundleName) { return QString(); } - return d->filePath; + return d->fileEntry.filePath(); } bool QFSFileEngine::isRelativePath() const { Q_D(const QFSFileEngine); // drive, e.g. "a:", or UNC root, e.q. "//" - return !(d->filePath.startsWith(QLatin1Char('/')) - || (d->filePath.length() >= 2 - && ((d->filePath.at(0).isLetter() && d->filePath.at(1) == QLatin1Char(':')) - || (d->filePath.at(0) == QLatin1Char('/') && d->filePath.at(1) == QLatin1Char('/'))))); + return d->fileEntry.isRelative(); } uint QFSFileEngine::ownerId(FileOwner /*own*/) const @@ -1732,69 +824,17 @@ uint QFSFileEngine::ownerId(FileOwner /*own*/) const QString QFSFileEngine::owner(FileOwner own) const { - QString name; -#if !defined(QT_NO_LIBRARY) Q_D(const QFSFileEngine); - if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { - QFSFileEnginePrivate::resolveLibs(); - if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) { - PSID pOwner = 0; - PSECURITY_DESCRIPTOR pSD; - if (ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT, - own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION, - own == OwnerUser ? &pOwner : 0, own == OwnerGroup ? &pOwner : 0, - 0, 0, &pSD) == ERROR_SUCCESS) { - DWORD lowner = 64; - DWORD ldomain = 64; - QVarLengthArray<wchar_t, 64> owner(lowner); - QVarLengthArray<wchar_t, 64> domain(ldomain); - SID_NAME_USE use = SidTypeUnknown; - // First call, to determine size of the strings (with '\0'). - if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, - (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - if (lowner > (DWORD)owner.size()) - owner.resize(lowner); - if (ldomain > (DWORD)domain.size()) - domain.resize(ldomain); - // Second call, try on resized buf-s - if (!ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner.data(), &lowner, - (LPWSTR)domain.data(), &ldomain, (SID_NAME_USE*)&use)) { - lowner = 0; - } - } else { - lowner = 0; - } - } - if (lowner != 0) - name = QString::fromWCharArray(owner.data()); - LocalFree(pSD); - } - } - } -#else - Q_UNUSED(own); -#endif - return name; + return QFileSystemEngine::owner(d->fileEntry, own); } bool QFSFileEngine::setPermissions(uint perms) { Q_D(QFSFileEngine); - bool ret = false; - int mode = 0; - - if (perms & QFile::ReadOwner || perms & QFile::ReadUser || perms & QFile::ReadGroup || perms & QFile::ReadOther) - mode |= _S_IREAD; - if (perms & QFile::WriteOwner || perms & QFile::WriteUser || perms & QFile::WriteGroup || perms & QFile::WriteOther) - mode |= _S_IWRITE; - - if (mode == 0) // not supported - return false; - - ret = ::_wchmod((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), mode) == 0; + QSystemError error; + bool ret = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error); if (!ret) - setError(QFile::PermissionsError, qt_error_string(errno)); + setError(QFile::PermissionsError, error.toString()); return ret; } @@ -1802,12 +842,16 @@ bool QFSFileEngine::setSize(qint64 size) { Q_D(QFSFileEngine); - if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1) { + if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1 || d->fh) { // resize open file HANDLE fh = d->fileHandle; #if !defined(Q_OS_WINCE) - if (fh == INVALID_HANDLE_VALUE) - fh = (HANDLE)_get_osfhandle(d->fd); + if (fh == INVALID_HANDLE_VALUE) { + if (d->fh) + fh = (HANDLE)_get_osfhandle(QT_FILENO(d->fh)); + else + fh = (HANDLE)_get_osfhandle(d->fd); + } #endif if (fh == INVALID_HANDLE_VALUE) return false; @@ -1822,9 +866,9 @@ bool QFSFileEngine::setSize(qint64 size) return false; } - if (!d->nativeFilePath.isEmpty()) { + if (!d->fileEntry.isEmpty()) { // resize file on disk - QFile file(d->filePath); + QFile file(d->fileEntry.filePath()); if (file.open(QFile::ReadWrite)) { bool ret = file.resize(size); if (!ret) @@ -1836,94 +880,14 @@ bool QFSFileEngine::setSize(qint64 size) } -static inline QDateTime fileTimeToQDateTime(const FILETIME *time) -{ - QDateTime ret; - -#if defined(Q_OS_WINCE) - SYSTEMTIME systime; - FILETIME ftime; - systime.wYear = 1970; - systime.wMonth = 1; - systime.wDay = 1; - systime.wHour = 0; - systime.wMinute = 0; - systime.wSecond = 0; - systime.wMilliseconds = 0; - systime.wDayOfWeek = 4; - SystemTimeToFileTime(&systime, &ftime); - unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime; - FileTimeToSystemTime(time, &systime); - unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime; - unsigned __int64 difftime = acttime - time1970; - difftime /= 10000000; - ret.setTime_t((unsigned int)difftime); -#else - SYSTEMTIME sTime, lTime; - FileTimeToSystemTime(time, &sTime); - SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime); - ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay)); - ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds)); -#endif - - return ret; -} - QDateTime QFSFileEngine::fileTime(FileTime time) const { Q_D(const QFSFileEngine); - QDateTime ret; - if (d->fd != -1) { -#if !defined(Q_OS_WINCE) - HANDLE fh = (HANDLE)_get_osfhandle(d->fd); - if (fh != INVALID_HANDLE_VALUE) { - FILETIME creationTime, lastAccessTime, lastWriteTime; - if (GetFileTime(fh, &creationTime, &lastAccessTime, &lastWriteTime)) { - if(time == CreationTime) - ret = fileTimeToQDateTime(&creationTime); - else if(time == ModificationTime) - ret = fileTimeToQDateTime(&lastWriteTime); - else if(time == AccessTime) - ret = fileTimeToQDateTime(&lastAccessTime); - } - } -#endif - } else { - WIN32_FILE_ATTRIBUTE_DATA attribData; - bool ok = ::GetFileAttributesEx((wchar_t*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData); - if (!ok) { - int errorCode = GetLastError(); - if (errorCode == ERROR_ACCESS_DENIED || errorCode == ERROR_SHARING_VIOLATION) { - QString path = QDir::toNativeSeparators(d->filePath); - // path for the FindFirstFile should not end with a trailing slash - while (path.endsWith(QLatin1Char('\\'))) - path.chop(1); - - // FindFirstFile can not handle drives - if (!path.endsWith(QLatin1Char(':'))) { - WIN32_FIND_DATA findData; - HANDLE hFind = ::FindFirstFile((wchar_t*)QFSFileEnginePrivate::longFileName(path).utf16(), - &findData); - if (hFind != INVALID_HANDLE_VALUE) { - ::FindClose(hFind); - ok = true; - attribData.ftCreationTime = findData.ftCreationTime; - attribData.ftLastWriteTime = findData.ftLastWriteTime; - attribData.ftLastAccessTime = findData.ftLastAccessTime; - } - } - } - } - if (ok) { - if(time == CreationTime) - ret = fileTimeToQDateTime(&attribData.ftCreationTime); - else if(time == ModificationTime) - ret = fileTimeToQDateTime(&attribData.ftLastWriteTime); - else if(time == AccessTime) - ret = fileTimeToQDateTime(&attribData.ftLastAccessTime); - } - } - return ret; + + if (d->doStat(QFileSystemMetaData::Times)) + return d->metaData.fileTime(time); + + return QDateTime(); } uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, @@ -1952,7 +916,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, #ifdef Q_USE_DEPRECATED_MAP_API nativeClose(); // handle automatically closed by kernel with mapHandle (below). - handle = ::CreateFileForMapping((const wchar_t*)nativeFilePath.constData(), + handle = ::CreateFileForMapping((const wchar_t*)fileEntry.nativeFilePath().utf16(), GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0), 0, NULL, diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 7134ae9..8759f8b 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1637,6 +1637,12 @@ QString QIODevice::errorString() const This function is called by QIODevice. Reimplement this function when creating a subclass of QIODevice. + When reimplementing this function it is important that this function + reads all the required data before returning. This is required in order + for QDataStream to be able to operate on the class. QDataStream assumes + all the requested information was read and therefore does not retry reading + if there was a problem. + \sa read() readLine() writeData() */ @@ -1649,6 +1655,12 @@ QString QIODevice::errorString() const This function is called by QIODevice. Reimplement this function when creating a subclass of QIODevice. + When reimplementing this function it is important that this function + writes all the data available before returning. This is required in order + for QDataStream to be able to operate on the class. QDataStream assumes + all the information was written and therefore does not retry writing if + there was a problem. + \sa read() write() */ diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp index 235f671..71cf92d 100644 --- a/src/corelib/io/qnoncontiguousbytedevice.cpp +++ b/src/corelib/io/qnoncontiguousbytedevice.cpp @@ -153,6 +153,7 @@ void QNonContiguousByteDevice::disableReset() resetDisabled = true; } +// FIXME we should scrap this whole implementation and instead change the ByteArrayImpl to be able to cope with sub-arrays? QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice() { buffer = b; @@ -244,7 +245,7 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size() return byteArray->size(); } -QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb) +QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb) : QNonContiguousByteDevice(), currentPosition(0) { ringBuffer = rb; @@ -355,6 +356,11 @@ bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) // normal advancement currentReadBufferPosition += amount; + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + else + emit readProgress(totalAdvancements, size()); + // advancing over that what has actually been read before if (currentReadBufferPosition > currentReadBufferAmount) { qint64 i = currentReadBufferPosition - currentReadBufferAmount; @@ -370,10 +376,6 @@ bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) currentReadBufferAmount = 0; } - if (size() == -1) - emit readProgress(totalAdvancements, totalAdvancements); - else - emit readProgress(totalAdvancements, size()); return true; } @@ -505,7 +507,7 @@ QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *dev \internal */ -QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer) +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QSharedPointer<QRingBuffer> ringBuffer) { return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer); } diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h index ff946ed..5e0b1bb 100644 --- a/src/corelib/io/qnoncontiguousbytedevice_p.h +++ b/src/corelib/io/qnoncontiguousbytedevice_p.h @@ -57,6 +57,7 @@ #include <QtCore/qbytearray.h> #include <QtCore/qbuffer.h> #include <QtCore/qiodevice.h> +#include <QtCore/QSharedPointer> #include "private/qringbuffer_p.h" QT_BEGIN_NAMESPACE @@ -70,6 +71,7 @@ public: virtual bool atEnd() = 0; virtual bool reset() = 0; void disableReset(); + bool isResetDisabled() { return resetDisabled; } virtual qint64 size() = 0; virtual ~QNonContiguousByteDevice(); @@ -89,7 +91,7 @@ class Q_CORE_EXPORT QNonContiguousByteDeviceFactory public: static QNonContiguousByteDevice* create(QIODevice *device); static QNonContiguousByteDevice* create(QByteArray *byteArray); - static QNonContiguousByteDevice* create(QRingBuffer *ringBuffer); + static QNonContiguousByteDevice* create(QSharedPointer<QRingBuffer> ringBuffer); static QIODevice* wrap(QNonContiguousByteDevice* byteDevice); }; @@ -114,7 +116,7 @@ protected: class QNonContiguousByteDeviceRingBufferImpl : public QNonContiguousByteDevice { public: - QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb); + QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb); ~QNonContiguousByteDeviceRingBufferImpl(); const char* readPointer(qint64 maximumLength, qint64 &len); bool advanceReadPointer(qint64 amount); @@ -122,13 +124,14 @@ public: bool reset(); qint64 size(); protected: - QRingBuffer* ringBuffer; + QSharedPointer<QRingBuffer> ringBuffer; qint64 currentPosition; }; class QNonContiguousByteDeviceIoDeviceImpl : public QNonContiguousByteDevice { + Q_OBJECT public: QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d); ~QNonContiguousByteDeviceIoDeviceImpl(); @@ -150,6 +153,7 @@ protected: class QNonContiguousByteDeviceBufferImpl : public QNonContiguousByteDevice { + Q_OBJECT public: QNonContiguousByteDeviceBufferImpl(QBuffer *b); ~QNonContiguousByteDeviceBufferImpl(); diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 1ce1256..e11cef9 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -963,9 +963,6 @@ bool QProcessPrivate::_q_canWrite() destroyPipe(stdinChannel.pipe); processError = QProcess::WriteError; q->setErrorString(QProcess::tr("Error writing to process")); -#if defined(QPROCESS_DEBUG) && !defined(Q_OS_WINCE) - qDebug("QProcessPrivate::canWrite(), failed to write (%s)", strerror(errno)); -#endif emit q->error(processError); return false; } @@ -974,11 +971,13 @@ bool QProcessPrivate::_q_canWrite() qDebug("QProcessPrivate::canWrite(), wrote %d bytes to the process input", int(written)); #endif - writeBuffer.free(written); - if (!emittedBytesWritten) { - emittedBytesWritten = true; - emit q->bytesWritten(written); - emittedBytesWritten = false; + if (written != 0) { + writeBuffer.free(written); + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(written); + emittedBytesWritten = false; + } } if (stdinChannel.notifier && !writeBuffer.isEmpty()) stdinChannel.notifier->setEnabled(true); @@ -2233,10 +2232,10 @@ bool QProcess::startDetached(const QString &program) } QT_BEGIN_INCLUDE_NAMESPACE -#ifdef Q_OS_MAC +#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES) # include <crt_externs.h> # define environ (*_NSGetEnviron()) -#elif defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) +#elif defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) || (defined(Q_OS_MAC) && defined(QT_NO_CORESERVICES)) static char *qt_empty_environ[] = { 0 }; #define environ qt_empty_environ #elif !defined(Q_OS_WIN) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 024d210..ba61bda 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -95,7 +95,7 @@ QT_END_NAMESPACE #include <qfile.h> #include <qfileinfo.h> #include <qlist.h> -#include <qmap.h> +#include <qhash.h> #include <qmutex.h> #include <qsemaphore.h> #include <qsocketnotifier.h> @@ -163,7 +163,7 @@ public: private: QMutex mutex; - QMap<int, QProcessInfo *> children; + QHash<int, QProcessInfo *> children; }; @@ -267,7 +267,7 @@ void QProcessManager::catchDeadChildren() // try to catch all children whose pid we have registered, and whose // deathPipe is still valid (i.e, we have not already notified it). - QMap<int, QProcessInfo *>::Iterator it = children.begin(); + QHash<int, QProcessInfo *>::Iterator it = children.begin(); while (it != children.end()) { // notify all children that they may have died. they need to run // waitpid() in their own thread. @@ -306,15 +306,11 @@ void QProcessManager::remove(QProcess *process) QMutexLocker locker(&mutex); int serial = process->d_func()->serial; - QProcessInfo *info = children.value(serial); - if (!info) - return; - + QProcessInfo *info = children.take(serial); #if defined (QPROCESS_DEBUG) - qDebug() << "QProcessManager::remove() removing pid" << info->pid << "process" << info->process; + if (info) + qDebug() << "QProcessManager::remove() removing pid" << info->pid << "process" << info->process; #endif - - children.remove(serial); delete info; } @@ -649,7 +645,7 @@ void QProcessPrivate::startProcess() if (childPid < 0) { // Cleanup, report error and return #if defined (QPROCESS_DEBUG) - qDebug("qt_fork failed: %s", qt_error_string(lastForkErrno)); + qDebug("qt_fork failed: %s", qPrintable(qt_error_string(lastForkErrno))); #endif processManager()->unlock(); q->setProcessState(QProcess::NotRunning); @@ -852,7 +848,14 @@ qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen) #if defined QPROCESS_DEBUG qDebug("QProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld", data, qt_prettyDebug(data, maxlen, 16).constData(), maxlen, written); + if (written == -1) + qDebug("QProcessPrivate::writeToStdin(), failed to write (%s)", qPrintable(qt_error_string(errno))); #endif + // If the O_NONBLOCK flag is set and If some data can be written without blocking + // the process, write() will transfer what it can and return the number of bytes written. + // Otherwise, it will return -1 and set errno to EAGAIN + if (written == -1 && errno == EAGAIN) + written = 0; return written; } diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 207cda3..cd8c1a3 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -580,7 +580,7 @@ QResource::addSearchPath(const QString &path) \obsolete Use QDir::searchPaths() instead. - + Returns the current search path list. This list is consulted when creating a relative resource. @@ -928,7 +928,7 @@ public: } }; -#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined (Q_OS_NACL) && !defined(Q_OS_INTEGRITY) #define QT_USE_MMAP #endif @@ -1185,21 +1185,6 @@ QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot) return false; } -//file type handler -class QResourceFileEngineHandler : public QAbstractFileEngineHandler -{ -public: - QResourceFileEngineHandler() { } - ~QResourceFileEngineHandler() { } - QAbstractFileEngine *create(const QString &path) const; -}; -QAbstractFileEngine *QResourceFileEngineHandler::create(const QString &path) const -{ - if (path.size() > 0 && path.startsWith(QLatin1Char(':'))) - return new QResourceFileEngine(path); - return 0; -} - //resource engine class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate { @@ -1506,12 +1491,6 @@ bool QResourceFileEnginePrivate::unmap(uchar *ptr) return true; } -//Initialization and cleanup -Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler) - -static int qt_force_resource_init() { resource_file_handler(); return 1; } -Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); } -static int qt_forced_resource_init = qt_force_resource_init(); -Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init) +Q_CORE_EXPORT void qInitResourceIO() { } // ### Qt 5: remove QT_END_NAMESPACE diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 7d5434e..b084ca5 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -258,9 +258,7 @@ bool QConfFile::isWritable() const } else { // Create the directories to the file. QDir dir(fileInfo.absolutePath()); - if (dir.exists() && dir.isReadable()) { - return true; - } else { + if (!dir.exists()) { if (!dir.mkpath(dir.absolutePath())) return false; } @@ -1006,7 +1004,7 @@ void QConfFileSettingsPrivate::initFormat() readFunc = 0; writeFunc = 0; #if defined(Q_OS_MAC) - caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : Qt::CaseInsensitive; + caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : IniCaseSensitivity; #else caseSensitivity = IniCaseSensitivity; #endif @@ -1132,7 +1130,7 @@ static void initDefaultPaths(QMutexLocker *locker) if (env == 0) { userPath = homePath; userPath += QLatin1Char('/'); -#ifdef Q_WS_QWS +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) userPath += QLatin1String("Settings"); #else userPath += QLatin1String(".config"); diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index 8c666d7..ec5b6bb 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -70,7 +70,7 @@ QT_BEGIN_NAMESPACE -#if defined(Q_WS_QWS) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) #define QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER #endif diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 44542cf..56ed746 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -295,7 +295,7 @@ public: : QFSFileEngine(), filePathIsTemplate(fileIsTemplate) { Q_D(QFSFileEngine); - d->filePath = file; + d->fileEntry = QFileSystemEntry(file); if (!filePathIsTemplate) QFSFileEngine::setFileName(file); @@ -325,6 +325,9 @@ bool QTemporaryFileEngine::isReallyOpen() Q_D(QFSFileEngine); if (!((0 == d->fh) && (-1 == d->fd) +#if defined (Q_OS_SYMBIAN) + && (0 == d->symbianFile.SubSessionHandle()) +#endif #if defined Q_OS_WIN && (INVALID_HANDLE_VALUE == d->fileHandle) #endif @@ -346,7 +349,7 @@ void QTemporaryFileEngine::setFileTemplate(const QString &fileTemplate) { Q_D(QFSFileEngine); if (filePathIsTemplate) - d->filePath = fileTemplate; + d->fileEntry = QFileSystemEntry(fileTemplate); } bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) @@ -359,7 +362,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) if (!filePathIsTemplate) return QFSFileEngine::open(openMode); - QString qfilename = d->filePath; + QString qfilename = d->fileEntry.filePath(); if(!qfilename.contains(QLatin1String("XXXXXX"))) qfilename += QLatin1String(".XXXXXX"); @@ -377,9 +380,8 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) d->closeFileHandle = true; // Restore the file names (open() resets them). - d->filePath = QString::fromLocal8Bit(filename); //changed now! + d->fileEntry = QFileSystemEntry(QString::fromLocal8Bit(filename)); //note that filename is NOT a native path filePathIsTemplate = false; - d->nativeInitFileName(); delete [] filename; return true; } @@ -395,9 +397,8 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) return false; } - QString template_ = d->filePath; - d->filePath = QString::fromLocal8Bit(filename); - d->nativeInitFileName(); + QString template_ = d->fileEntry.filePath(); + d->fileEntry = QFileSystemEntry(QString::fromLocal8Bit(filename)); delete [] filename; if (QFSFileEngine::open(openMode)) { @@ -405,8 +406,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode) return true; } - d->filePath = template_; - d->nativeFilePath.clear(); + d->fileEntry = QFileSystemEntry(template_); return false; #endif } @@ -418,7 +418,7 @@ bool QTemporaryFileEngine::remove() // we must explicitly call QFSFileEngine::close() before we remove it. QFSFileEngine::close(); if (QFSFileEngine::remove()) { - d->filePath.clear(); + d->fileEntry.clear(); return true; } return false; diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 20a438a..a5837cb 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -224,6 +224,7 @@ static const int QTEXTSTREAM_BUFFERSIZE = 16384; \value ReadPastEnd The text stream has read past the end of the data in the underlying device. \value ReadCorruptData The text stream has read corrupt data. + \value WriteFailed The text stream cannot write to the underlying device. \sa status() */ @@ -396,19 +397,19 @@ public: npsInvalidPrefix }; - inline bool write(const QString &data); inline bool getChar(QChar *ch); inline void ungetChar(const QChar &ch); NumberParsingStatus getNumber(qulonglong *l); bool getReal(double *f); - bool putNumber(qulonglong number, bool negative); - inline bool putString(const QString &ch, bool number = false); + inline void write(const QString &data); + inline void putString(const QString &ch, bool number = false); + void putNumber(qulonglong number, bool negative); // buffers bool fillReadBuffer(qint64 maxBytes = -1); void resetReadBuffer(); - bool flushWriteBuffer(); + void flushWriteBuffer(); QString writeBuffer; QString readBuffer; int readBufferOffset; @@ -642,14 +643,20 @@ void QTextStreamPrivate::resetReadBuffer() /*! \internal */ -bool QTextStreamPrivate::flushWriteBuffer() +void QTextStreamPrivate::flushWriteBuffer() { // no buffer next to the QString itself; this function should only // be called internally, for devices. if (string || !device) - return false; + return; + + // Stream went bye-bye already. Appending further data may succeed again, + // but would create a corrupted stream anyway. + if (status != QTextStream::Ok) + return; + if (writeBuffer.isEmpty()) - return true; + return; #if defined (Q_OS_WIN) // handle text translation and bypass the Text flag in the device. @@ -681,8 +688,10 @@ bool QTextStreamPrivate::flushWriteBuffer() qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); #endif - if (bytesWritten <= 0) - return false; + if (bytesWritten <= 0) { + status = QTextStream::WriteFailed; + return; + } #if defined (Q_OS_WIN) // replace the text flag @@ -693,7 +702,7 @@ bool QTextStreamPrivate::flushWriteBuffer() // flush the file #ifndef QT_NO_QOBJECT QFile *file = qobject_cast<QFile *>(device); - bool flushed = file && file->flush(); + bool flushed = !file || file->flush(); #else bool flushed = true; #endif @@ -702,7 +711,8 @@ bool QTextStreamPrivate::flushWriteBuffer() qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes", int(bytesWritten)); #endif - return flushed && bytesWritten == qint64(data.size()); + if (!flushed || bytesWritten != qint64(data.size())) + status = QTextStream::WriteFailed; } QString QTextStreamPrivate::read(int maxlen) @@ -908,7 +918,7 @@ inline void QTextStreamPrivate::restoreToSavedConverterState() /*! \internal */ -inline bool QTextStreamPrivate::write(const QString &data) +inline void QTextStreamPrivate::write(const QString &data) { if (string) { // ### What about seek()?? @@ -916,9 +926,8 @@ inline bool QTextStreamPrivate::write(const QString &data) } else { writeBuffer += data; if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - return flushWriteBuffer(); + flushWriteBuffer(); } - return true; } /*! \internal @@ -959,7 +968,7 @@ inline void QTextStreamPrivate::ungetChar(const QChar &ch) /*! \internal */ -inline bool QTextStreamPrivate::putString(const QString &s, bool number) +inline void QTextStreamPrivate::putString(const QString &s, bool number) { QString tmp = s; @@ -993,7 +1002,7 @@ inline bool QTextStreamPrivate::putString(const QString &s, bool number) qt_prettyDebug(a.constData(), a.size(), qMax(16, a.size())).constData(), qt_prettyDebug(b.constData(), b.size(), qMax(16, b.size())).constData()); #endif - return write(tmp); + write(tmp); } /*! @@ -1255,6 +1264,7 @@ qint64 QTextStream::pos() const return qint64(-1); } thatd->readBufferOffset = oldReadBufferOffset; + thatd->readConverterSavedStateOffset = 0; // Return the device position. return d->device->pos(); @@ -1593,6 +1603,9 @@ void QTextStream::resetStatus() Sets the status of the text stream to the \a status given. + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + \sa Status status() resetStatus() */ void QTextStream::setStatus(Status status) @@ -2267,7 +2280,7 @@ QTextStream &QTextStream::operator>>(char *c) /*! \internal */ -bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) +void QTextStreamPrivate::putNumber(qulonglong number, bool negative) { QString result; @@ -2307,7 +2320,7 @@ bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) result.prepend(QLatin1Char('0')); } } - return putString(result, true); + putString(result, true); } /*! diff --git a/src/corelib/io/qtextstream.h b/src/corelib/io/qtextstream.h index ab8b1e1..fc2fee6 100644 --- a/src/corelib/io/qtextstream.h +++ b/src/corelib/io/qtextstream.h @@ -89,7 +89,8 @@ public: enum Status { Ok, ReadPastEnd, - ReadCorruptData + ReadCorruptData, + WriteFailed }; enum NumberFlag { ShowBase = 0x1, diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 6e55f93..efd3f45 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -965,14 +965,14 @@ static void QT_FASTCALL _fragment(const char **ptr, QUrlParseData *parseData) } struct NameprepCaseFoldingEntry { - int uc; + uint uc; ushort mapping[4]; }; -inline bool operator<(int one, const NameprepCaseFoldingEntry &other) +inline bool operator<(uint one, const NameprepCaseFoldingEntry &other) { return one < other.uc; } -inline bool operator<(const NameprepCaseFoldingEntry &one, int other) +inline bool operator<(const NameprepCaseFoldingEntry &one, uint other) { return one.uc < other; } static const NameprepCaseFoldingEntry NameprepCaseFolding[] = { @@ -1861,45 +1861,44 @@ static const NameprepCaseFoldingEntry NameprepCaseFolding[] = { { 0xFF38, { 0xFF58, 0x0000, 0x0000, 0x0000 } }, { 0xFF39, { 0xFF59, 0x0000, 0x0000, 0x0000 } }, { 0xFF3A, { 0xFF5A, 0x0000, 0x0000, 0x0000 } }, - // ##### -/* { 0x10400, { 0x10428, 0x0000, 0x0000, 0x0000 } }, - { 0x10401, { 0x10429, 0x0000, 0x0000, 0x0000 } }, - { 0x10402, { 0x1042A, 0x0000, 0x0000, 0x0000 } }, - { 0x10403, { 0x1042B, 0x0000, 0x0000, 0x0000 } }, - { 0x10404, { 0x1042C, 0x0000, 0x0000, 0x0000 } }, - { 0x10405, { 0x1042D, 0x0000, 0x0000, 0x0000 } }, - { 0x10406, { 0x1042E, 0x0000, 0x0000, 0x0000 } }, - { 0x10407, { 0x1042F, 0x0000, 0x0000, 0x0000 } }, - { 0x10408, { 0x10430, 0x0000, 0x0000, 0x0000 } }, - { 0x10409, { 0x10431, 0x0000, 0x0000, 0x0000 } }, - { 0x1040A, { 0x10432, 0x0000, 0x0000, 0x0000 } }, - { 0x1040B, { 0x10433, 0x0000, 0x0000, 0x0000 } }, - { 0x1040C, { 0x10434, 0x0000, 0x0000, 0x0000 } }, - { 0x1040D, { 0x10435, 0x0000, 0x0000, 0x0000 } }, - { 0x1040E, { 0x10436, 0x0000, 0x0000, 0x0000 } }, - { 0x1040F, { 0x10437, 0x0000, 0x0000, 0x0000 } }, - { 0x10410, { 0x10438, 0x0000, 0x0000, 0x0000 } }, - { 0x10411, { 0x10439, 0x0000, 0x0000, 0x0000 } }, - { 0x10412, { 0x1043A, 0x0000, 0x0000, 0x0000 } }, - { 0x10413, { 0x1043B, 0x0000, 0x0000, 0x0000 } }, - { 0x10414, { 0x1043C, 0x0000, 0x0000, 0x0000 } }, - { 0x10415, { 0x1043D, 0x0000, 0x0000, 0x0000 } }, - { 0x10416, { 0x1043E, 0x0000, 0x0000, 0x0000 } }, - { 0x10417, { 0x1043F, 0x0000, 0x0000, 0x0000 } }, - { 0x10418, { 0x10440, 0x0000, 0x0000, 0x0000 } }, - { 0x10419, { 0x10441, 0x0000, 0x0000, 0x0000 } }, - { 0x1041A, { 0x10442, 0x0000, 0x0000, 0x0000 } }, - { 0x1041B, { 0x10443, 0x0000, 0x0000, 0x0000 } }, - { 0x1041C, { 0x10444, 0x0000, 0x0000, 0x0000 } }, - { 0x1041D, { 0x10445, 0x0000, 0x0000, 0x0000 } }, - { 0x1041E, { 0x10446, 0x0000, 0x0000, 0x0000 } }, - { 0x1041F, { 0x10447, 0x0000, 0x0000, 0x0000 } }, - { 0x10420, { 0x10448, 0x0000, 0x0000, 0x0000 } }, - { 0x10421, { 0x10449, 0x0000, 0x0000, 0x0000 } }, - { 0x10422, { 0x1044A, 0x0000, 0x0000, 0x0000 } }, - { 0x10423, { 0x1044B, 0x0000, 0x0000, 0x0000 } }, - { 0x10424, { 0x1044C, 0x0000, 0x0000, 0x0000 } }, - { 0x10425, { 0x1044D, 0x0000, 0x0000, 0x0000 } },*/ + { 0x10400, { 0xd801, 0xdc28, 0x0000, 0x0000 } }, + { 0x10401, { 0xd801, 0xdc29, 0x0000, 0x0000 } }, + { 0x10402, { 0xd801, 0xdc2A, 0x0000, 0x0000 } }, + { 0x10403, { 0xd801, 0xdc2B, 0x0000, 0x0000 } }, + { 0x10404, { 0xd801, 0xdc2C, 0x0000, 0x0000 } }, + { 0x10405, { 0xd801, 0xdc2D, 0x0000, 0x0000 } }, + { 0x10406, { 0xd801, 0xdc2E, 0x0000, 0x0000 } }, + { 0x10407, { 0xd801, 0xdc2F, 0x0000, 0x0000 } }, + { 0x10408, { 0xd801, 0xdc30, 0x0000, 0x0000 } }, + { 0x10409, { 0xd801, 0xdc31, 0x0000, 0x0000 } }, + { 0x1040A, { 0xd801, 0xdc32, 0x0000, 0x0000 } }, + { 0x1040B, { 0xd801, 0xdc33, 0x0000, 0x0000 } }, + { 0x1040C, { 0xd801, 0xdc34, 0x0000, 0x0000 } }, + { 0x1040D, { 0xd801, 0xdc35, 0x0000, 0x0000 } }, + { 0x1040E, { 0xd801, 0xdc36, 0x0000, 0x0000 } }, + { 0x1040F, { 0xd801, 0xdc37, 0x0000, 0x0000 } }, + { 0x10410, { 0xd801, 0xdc38, 0x0000, 0x0000 } }, + { 0x10411, { 0xd801, 0xdc39, 0x0000, 0x0000 } }, + { 0x10412, { 0xd801, 0xdc3A, 0x0000, 0x0000 } }, + { 0x10413, { 0xd801, 0xdc3B, 0x0000, 0x0000 } }, + { 0x10414, { 0xd801, 0xdc3C, 0x0000, 0x0000 } }, + { 0x10415, { 0xd801, 0xdc3D, 0x0000, 0x0000 } }, + { 0x10416, { 0xd801, 0xdc3E, 0x0000, 0x0000 } }, + { 0x10417, { 0xd801, 0xdc3F, 0x0000, 0x0000 } }, + { 0x10418, { 0xd801, 0xdc40, 0x0000, 0x0000 } }, + { 0x10419, { 0xd801, 0xdc41, 0x0000, 0x0000 } }, + { 0x1041A, { 0xd801, 0xdc42, 0x0000, 0x0000 } }, + { 0x1041B, { 0xd801, 0xdc43, 0x0000, 0x0000 } }, + { 0x1041C, { 0xd801, 0xdc44, 0x0000, 0x0000 } }, + { 0x1041D, { 0xd801, 0xdc45, 0x0000, 0x0000 } }, + { 0x1041E, { 0xd801, 0xdc46, 0x0000, 0x0000 } }, + { 0x1041F, { 0xd801, 0xdc47, 0x0000, 0x0000 } }, + { 0x10420, { 0xd801, 0xdc48, 0x0000, 0x0000 } }, + { 0x10421, { 0xd801, 0xdc49, 0x0000, 0x0000 } }, + { 0x10422, { 0xd801, 0xdc4A, 0x0000, 0x0000 } }, + { 0x10423, { 0xd801, 0xdc4B, 0x0000, 0x0000 } }, + { 0x10424, { 0xd801, 0xdc4C, 0x0000, 0x0000 } }, + { 0x10425, { 0xd801, 0xdc4D, 0x0000, 0x0000 } }, { 0x1D400, { 0x0061, 0x0000, 0x0000, 0x0000 } }, { 0x1D401, { 0x0062, 0x0000, 0x0000, 0x0000 } }, { 0x1D402, { 0x0063, 0x0000, 0x0000, 0x0000 } }, @@ -2354,17 +2353,23 @@ static void mapToLowerCase(QString *str, int from) { int N = sizeof(NameprepCaseFolding) / sizeof(NameprepCaseFolding[0]); - QChar *d = 0; + ushort *d = 0; for (int i = from; i < str->size(); ++i) { - int uc = str->at(i).unicode(); + uint uc = str->at(i).unicode(); if (uc < 0x80) { if (uc <= 'Z' && uc >= 'A') { - uc |= 0x20; if (!d) - d = str->data(); - d[i] = QChar(uc); + d = reinterpret_cast<ushort *>(str->data()); + d[i] = (uc | 0x20); } } else { + if (QChar(uc).isHighSurrogate() && i < str->size() - 1) { + ushort low = str->at(i + 1).unicode(); + if (QChar(low).isLowSurrogate()) { + uc = QChar::surrogateToUcs4(uc, low); + ++i; + } + } const NameprepCaseFoldingEntry *entry = qBinaryFind(NameprepCaseFolding, NameprepCaseFolding + N, uc); @@ -2373,23 +2378,26 @@ static void mapToLowerCase(QString *str, int from) while (l < 4 && entry->mapping[l]) ++l; if (l > 1) { - str->replace(i, 1, (const QChar *)&entry->mapping[0], l); + if (uc <= 0xffff) + str->replace(i, 1, reinterpret_cast<const QChar *>(&entry->mapping[0]), l); + else + str->replace(i-1, 2, reinterpret_cast<const QChar *>(&entry->mapping[0]), l); d = 0; } else { if (!d) - d = str->data(); - d[i] = QChar(entry->mapping[0]); + d = reinterpret_cast<ushort *>(str->data()); + d[i] = entry->mapping[0]; } } } } } -static bool isMappedToNothing(const QChar &ch) +static bool isMappedToNothing(uint uc) { - if (ch.unicode() < 0xad) + if (uc < 0xad) return false; - switch (ch.unicode()) { + switch (uc) { case 0x00AD: case 0x034F: case 0x1806: case 0x180B: case 0x180C: case 0x180D: case 0x200B: case 0x200C: case 0x200D: case 0x2060: case 0xFE00: case 0xFE01: case 0xFE02: case 0xFE03: case 0xFE04: case 0xFE05: case 0xFE06: case 0xFE07: @@ -2408,66 +2416,72 @@ static void stripProhibitedOutput(QString *str, int from) const ushort *in = out; const ushort *end = (ushort *)str->data() + str->size(); while (in < end) { - ushort uc = *in; - if (uc < 0x80 || - !(uc <= 0x009F - || uc == 0x00A0 - || uc == 0x0340 - || uc == 0x0341 - || uc == 0x06DD - || uc == 0x070F - || uc == 0x1680 - || uc == 0x180E - || (uc >= 0x2000 && uc <= 0x200B) - || uc == 0x200C - || uc == 0x200D - || uc == 0x200E - || uc == 0x200F - || (uc >= 0x2028 && uc <= 0x202F) - || uc == 0x205F - || (uc >= 0x2060 && uc <= 0x2063) - || uc == 0x206A - || (uc >= 0x206A && uc <= 0x206F) - || (uc >= 0x2FF0 && uc <= 0x2FFB) - || uc == 0x3000 - || (uc >= 0xD800 && uc <= 0xDFFF) - || (uc >= 0xE000 && uc <= 0xF8FF) - || (uc >= 0xFDD0 && uc <= 0xFDEF) - || uc == 0xFEFF - || (uc >= 0xFFF9 && uc <= 0xFFFC) - || (uc >= 0xFFFA && (uc <= 0xFFFE || uc == 0xFFFF)) - /* ### Add NAMEPREP support for surrogates - || uc == 0xE0001 - || (uc >= 0x2FFFE && uc <= 0x2FFFF) - || (uc >= 0x1D173 && uc <= 0x1D17A) - || (uc >= 0x1FFFE && uc <= 0x1FFFF) - || (uc >= 0x3FFFE && uc <= 0x3FFFF) - || (uc >= 0x4FFFE && uc <= 0x4FFFF) - || (uc >= 0x5FFFE && uc <= 0x5FFFF) - || (uc >= 0x6FFFE && uc <= 0x6FFFF) - || (uc >= 0x7FFFE && uc <= 0x7FFFF) - || (uc >= 0x8FFFE && uc <= 0x8FFFF) - || (uc >= 0x9FFFE && uc <= 0x9FFFF) - || (uc >= 0xAFFFE && uc <= 0xAFFFF) - || (uc >= 0xBFFFE && uc <= 0xBFFFF) - || (uc >= 0xCFFFE && uc <= 0xCFFFF) - || (uc >= 0xDFFFE && uc <= 0xDFFFF) - || (uc >= 0xE0020 && uc <= 0xE007F) - || (uc >= 0xEFFFE && uc <= 0xEFFFF) - || (uc >= 0xF0000 && uc <= 0xFFFFD) - || (uc >= 0xFFFFE && uc <= 0xFFFFF) - || (uc >= 0x100000 && uc <= 0x10FFFD) - || (uc >= 0x10FFFE && uc <= 0x10FFFF)*/)) - *out++ = *in; + uint uc = *in; + if (QChar(uc).isHighSurrogate() && in < end - 1) { + ushort low = *(in + 1); + if (QChar(low).isLowSurrogate()) { + ++in; + uc = QChar::surrogateToUcs4(uc, low); + } + } + if (uc <= 0xFFFF) { + if (uc < 0x80 || + !(uc <= 0x009F + || uc == 0x00A0 + || uc == 0x0340 + || uc == 0x0341 + || uc == 0x06DD + || uc == 0x070F + || uc == 0x1680 + || uc == 0x180E + || (uc >= 0x2000 && uc <= 0x200F) + || (uc >= 0x2028 && uc <= 0x202F) + || uc == 0x205F + || (uc >= 0x2060 && uc <= 0x2063) + || (uc >= 0x206A && uc <= 0x206F) + || (uc >= 0x2FF0 && uc <= 0x2FFB) + || uc == 0x3000 + || (uc >= 0xD800 && uc <= 0xDFFF) + || (uc >= 0xE000 && uc <= 0xF8FF) + || (uc >= 0xFDD0 && uc <= 0xFDEF) + || uc == 0xFEFF + || (uc >= 0xFFF9 && uc <= 0xFFFF))) { + *out++ = *in; + } + } else { + if (!((uc >= 0x1D173 && uc <= 0x1D17A) + || (uc >= 0x1FFFE && uc <= 0x1FFFF) + || (uc >= 0x2FFFE && uc <= 0x2FFFF) + || (uc >= 0x3FFFE && uc <= 0x3FFFF) + || (uc >= 0x4FFFE && uc <= 0x4FFFF) + || (uc >= 0x5FFFE && uc <= 0x5FFFF) + || (uc >= 0x6FFFE && uc <= 0x6FFFF) + || (uc >= 0x7FFFE && uc <= 0x7FFFF) + || (uc >= 0x8FFFE && uc <= 0x8FFFF) + || (uc >= 0x9FFFE && uc <= 0x9FFFF) + || (uc >= 0xAFFFE && uc <= 0xAFFFF) + || (uc >= 0xBFFFE && uc <= 0xBFFFF) + || (uc >= 0xCFFFE && uc <= 0xCFFFF) + || (uc >= 0xDFFFE && uc <= 0xDFFFF) + || uc == 0xE0001 + || (uc >= 0xE0020 && uc <= 0xE007F) + || (uc >= 0xEFFFE && uc <= 0xEFFFF) + || (uc >= 0xF0000 && uc <= 0xFFFFD) + || (uc >= 0xFFFFE && uc <= 0xFFFFF) + || (uc >= 0x100000 && uc <= 0x10FFFD) + || (uc >= 0x10FFFE && uc <= 0x10FFFF))) { + *out++ = QChar::highSurrogate(uc); + *out++ = QChar::lowSurrogate(uc); + } + } ++in; } if (in != out) str->truncate(out - str->utf16()); } -static bool isBidirectionalRorAL(const QChar &c) +static bool isBidirectionalRorAL(uint uc) { - ushort uc = c.unicode(); if (uc < 0x5b0) return false; return uc == 0x05BE @@ -2506,9 +2520,8 @@ static bool isBidirectionalRorAL(const QChar &c) || (uc >= 0xFE76 && uc <= 0xFEFC); } -static bool isBidirectionalL(const QChar &ch) +static bool isBidirectionalL(uint uc) { - ushort uc = ch.unicode(); if (uc < 0xaa) return (uc >= 0x0041 && uc <= 0x005A) || (uc >= 0x0061 && uc <= 0x007A); @@ -2873,8 +2886,7 @@ static bool isBidirectionalL(const QChar &ch) return true; } - /* ### Add NAMEPREP support for surrogates - || (uc >= 0x10300 && uc <= 0x1031E) + if ((uc >= 0x10300 && uc <= 0x1031E) || (uc >= 0x10320 && uc <= 0x10323) || (uc >= 0x10330 && uc <= 0x1034A) || (uc >= 0x10400 && uc <= 0x10425) @@ -2910,7 +2922,9 @@ static bool isBidirectionalL(const QChar &ch) || (uc >= 0x20000 && uc <= 0x2A6D6) || (uc >= 0x2F800 && uc <= 0x2FA1D) || (uc >= 0xF0000 && uc <= 0xFFFFD) - || (uc >= 0x100000 && uc <= 0x10FFFD)*/ + || (uc >= 0x100000 && uc <= 0x10FFFD)) { + return true; + } return false; } @@ -2943,13 +2957,37 @@ void qt_nameprep(QString *source, int from) return; // everything was mapped easily (lowercased, actually) int firstNonAscii = out - src; + // Characters unassigned in Unicode 3.2 are not allowed in "stored string" scheme + // but allowed in "query" scheme + // (Table A.1) + const bool isUnassignedAllowed = false; // ### // Characters commonly mapped to nothing are simply removed // (Table B.1) const QChar *in = out; - while (in < e) { - if (!isMappedToNothing(*in)) - *out++ = *in; - ++in; + for ( ; in < e; ++in) { + uint uc = in->unicode(); + if (QChar(uc).isHighSurrogate() && in < e - 1) { + ushort low = in[1].unicode(); + if (QChar(low).isLowSurrogate()) { + ++in; + uc = QChar::surrogateToUcs4(uc, low); + } + } + if (!isUnassignedAllowed) { + QChar::UnicodeVersion version = QChar::unicodeVersion(uc); + if (version == QChar::Unicode_Unassigned || version > QChar::Unicode_3_2) { + source->resize(from); // not allowed, clear the label + return; + } + } + if (!isMappedToNothing(uc)) { + if (uc <= 0xFFFF) { + *out++ = *in; + } else { + *out++ = QChar::highSurrogate(uc); + *out++ = QChar::lowSurrogate(uc); + } + } } if (out != in) source->truncate(out - src); @@ -2960,7 +2998,8 @@ void qt_nameprep(QString *source, int from) // Normalize to Unicode 3.2 form KC extern void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar::UnicodeVersion version, int from); - qt_string_normalize(source, QString::NormalizationForm_KC, QChar::Unicode_3_2, firstNonAscii); + qt_string_normalize(source, QString::NormalizationForm_KC, QChar::Unicode_3_2, + firstNonAscii > from ? firstNonAscii - 1 : from); // Strip prohibited output stripProhibitedOutput(source, firstNonAscii); @@ -2971,14 +3010,22 @@ void qt_nameprep(QString *source, int from) src = source->data(); e = src + source->size(); for (in = src + from; in < e && (!containsLCat || !containsRandALCat); ++in) { - if (isBidirectionalL(*in)) + uint uc = in->unicode(); + if (QChar(uc).isHighSurrogate() && in < e - 1) { + ushort low = in[1].unicode(); + if (QChar(low).isLowSurrogate()) { + ++in; + uc = QChar::surrogateToUcs4(uc, low); + } + } + if (isBidirectionalL(uc)) containsLCat = true; - else if (isBidirectionalRorAL(*in)) + else if (isBidirectionalRorAL(uc)) containsRandALCat = true; } if (containsRandALCat) { - if (containsLCat || (!isBidirectionalRorAL(src[from]) - || !isBidirectionalRorAL(e[-1]))) + if (containsLCat || (!isBidirectionalRorAL(src[from].unicode()) + || !isBidirectionalRorAL(e[-1].unicode()))) source->resize(from); // not allowed, clear the label } } @@ -6002,6 +6049,14 @@ QUrl &QUrl::operator =(const QString &url) return *this; } +/*! + \fn void QUrl::swap(QUrl &other) + \since 4.8 + + Swaps URL \a other with this URL. This operation is very + fast and never fails. +*/ + /*! \internal Forces a detach. @@ -6024,19 +6079,22 @@ bool QUrl::isDetached() const /*! - Returns a QUrl representation of \a localFile, interpreted as a - local file. + Returns a QUrl representation of \a localFile, interpreted as a local + file. This function accepts paths separated by slashes as well as the + native separator for this platform. + + This function also accepts paths with a doubled leading slash (or + backslash) to indicate a remote file, as in + "//servername/path/to/file.txt". Note that only certain platforms can + actually open this file using QFile::open(). - \sa toLocalFile() + \sa toLocalFile(), isLocalFile(), QDir::toNativeSeparators */ QUrl QUrl::fromLocalFile(const QString &localFile) { QUrl url; url.setScheme(QLatin1String("file")); - QString deslashified = localFile; - deslashified.replace(QLatin1Char('\\'), QLatin1Char('/')); - - + QString deslashified = QDir::fromNativeSeparators(localFile); // magic for drives on windows if (deslashified.length() > 1 && deslashified.at(1) == QLatin1Char(':') && deslashified.at(0) != QLatin1Char('/')) { @@ -6055,35 +6113,61 @@ QUrl QUrl::fromLocalFile(const QString &localFile) } /*! - Returns the path of this URL formatted as a local file path. + Returns the path of this URL formatted as a local file path. The path + returned will use forward slashes, even if it was originally created + from one with backslashes. + + If this URL contains a non-empty hostname, it will be encoded in the + returned value in the form found on SMB networks (for example, + "//servername/path/to/file.txt"). - \sa fromLocalFile() + \sa fromLocalFile(), isLocalFile() */ QString QUrl::toLocalFile() const { - if (!d) return QString(); - if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse(); + // the call to isLocalFile() also ensures that we're parsed + if (!isLocalFile()) + return QString(); QString tmp; QString ourPath = path(); - if (d->scheme.isEmpty() || QString::compare(d->scheme, QLatin1String("file"), Qt::CaseInsensitive) == 0) { - // magic for shared drive on windows - if (!d->host.isEmpty()) { - tmp = QLatin1String("//") + d->host + (ourPath.length() > 0 && ourPath.at(0) != QLatin1Char('/') - ? QLatin1Char('/') + ourPath : ourPath); - } else { - tmp = ourPath; - // magic for drives on windows - if (ourPath.length() > 2 && ourPath.at(0) == QLatin1Char('/') && ourPath.at(2) == QLatin1Char(':')) - tmp.remove(0, 1); - } + // magic for shared drive on windows + if (!d->host.isEmpty()) { + tmp = QLatin1String("//") + d->host + (ourPath.length() > 0 && ourPath.at(0) != QLatin1Char('/') + ? QLatin1Char('/') + ourPath : ourPath); + } else { + tmp = ourPath; + // magic for drives on windows + if (ourPath.length() > 2 && ourPath.at(0) == QLatin1Char('/') && ourPath.at(2) == QLatin1Char(':')) + tmp.remove(0, 1); } return tmp; } /*! + \since 4.7 + Returns true if this URL is pointing to a local file path. A URL is a + local file path if the scheme is "file". + + Note that this function considers URLs with hostnames to be local file + paths, even if the eventual file path cannot be opened with + QFile::open(). + + \sa fromLocalFile(), toLocalFile() +*/ +bool QUrl::isLocalFile() const +{ + if (!d) return false; + if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse(); + + if (d->scheme.compare(QLatin1String("file"), Qt::CaseInsensitive) != 0) + return false; // not file + return true; +} + +/*! Returns true if this URL is a parent of \a childUrl. \a childUrl is a child of this URL if the two URLs share the same scheme and authority, and this URL's path is a parent of the path of \a childUrl. diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index 58d4e9e..7534b8d 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -94,8 +94,14 @@ public: #ifndef QT_NO_URL_CAST_FROM_STRING QUrl &operator =(const QString &url); #endif +#ifdef Q_COMPILER_RVALUE_REFS + inline QUrl &operator=(QUrl &&other) + { qSwap(d, other.d); return *this; } +#endif ~QUrl(); + inline void swap(QUrl &other) { qSwap(d, other.d); } + void setUrl(const QString &url); void setUrl(const QString &url, ParsingMode mode); // ### Qt 5: merge the two setUrl() functions, with mode = TolerantMode @@ -183,6 +189,7 @@ public: static QUrl fromLocalFile(const QString &localfile); QString toLocalFile() const; + bool isLocalFile() const; QString toString(FormattingOptions options = None) const; diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 1851e04..a3628b1 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -1,102 +1,111 @@ # Qt core object module HEADERS += \ - kernel/qabstracteventdispatcher.h \ + kernel/qabstracteventdispatcher.h \ kernel/qabstractitemmodel.h \ kernel/qabstractitemmodel_p.h \ - kernel/qbasictimer.h \ - kernel/qeventloop.h\ - kernel/qpointer.h \ + kernel/qbasictimer.h \ + kernel/qeventloop.h\ + kernel/qpointer.h \ kernel/qcorecmdlineargs_p.h \ - kernel/qcoreapplication.h \ - kernel/qcoreevent.h \ - kernel/qmetaobject.h \ - kernel/qmetatype.h \ - kernel/qmimedata.h \ - kernel/qobject.h \ - kernel/qobjectdefs.h \ - kernel/qsignalmapper.h \ - kernel/qsocketnotifier.h \ - kernel/qtimer.h \ - kernel/qtranslator.h \ + kernel/qcoreapplication.h \ + kernel/qcoreevent.h \ + kernel/qmetaobject.h \ + kernel/qmetatype.h \ + kernel/qmimedata.h \ + kernel/qobject.h \ + kernel/qobjectdefs.h \ + kernel/qsignalmapper.h \ + kernel/qsocketnotifier.h \ + kernel/qtimer.h \ + kernel/qtranslator.h \ kernel/qtranslator_p.h \ kernel/qvariant.h \ - kernel/qabstracteventdispatcher_p.h \ - kernel/qcoreapplication_p.h \ - kernel/qobjectcleanuphandler.h \ + kernel/qabstracteventdispatcher_p.h \ + kernel/qcoreapplication_p.h \ + kernel/qobjectcleanuphandler.h \ kernel/qvariant_p.h \ kernel/qmetaobject_p.h \ kernel/qobject_p.h \ - kernel/qcoreglobaldata_p.h \ - kernel/qsharedmemory.h \ + kernel/qcoreglobaldata_p.h \ + kernel/qsharedmemory.h \ kernel/qsharedmemory_p.h \ kernel/qsystemsemaphore.h \ kernel/qsystemsemaphore_p.h \ kernel/qfunctions_p.h \ - kernel/qmath.h + kernel/qmath.h \ + kernel/qsystemerror_p.h SOURCES += \ - kernel/qabstracteventdispatcher.cpp \ + kernel/qabstracteventdispatcher.cpp \ kernel/qabstractitemmodel.cpp \ - kernel/qbasictimer.cpp \ - kernel/qeventloop.cpp \ - kernel/qcoreapplication.cpp \ - kernel/qcoreevent.cpp \ - kernel/qmetaobject.cpp \ - kernel/qmetatype.cpp \ - kernel/qmimedata.cpp \ - kernel/qobject.cpp \ - kernel/qobjectcleanuphandler.cpp \ - kernel/qsignalmapper.cpp \ - kernel/qsocketnotifier.cpp \ - kernel/qtimer.cpp \ - kernel/qtranslator.cpp \ - kernel/qvariant.cpp \ + kernel/qbasictimer.cpp \ + kernel/qeventloop.cpp \ + kernel/qcoreapplication.cpp \ + kernel/qcoreevent.cpp \ + kernel/qmetaobject.cpp \ + kernel/qmetatype.cpp \ + kernel/qmimedata.cpp \ + kernel/qobject.cpp \ + kernel/qobjectcleanuphandler.cpp \ + kernel/qsignalmapper.cpp \ + kernel/qsocketnotifier.cpp \ + kernel/qtimer.cpp \ + kernel/qtranslator.cpp \ + kernel/qvariant.cpp \ kernel/qcoreglobaldata.cpp \ kernel/qsharedmemory.cpp \ kernel/qsystemsemaphore.cpp \ kernel/qpointer.cpp \ - kernel/qmath.cpp + kernel/qmath.cpp \ + kernel/qsystemerror.cpp win32 { - SOURCES += \ - kernel/qeventdispatcher_win.cpp \ - kernel/qcoreapplication_win.cpp \ - kernel/qwineventnotifier_p.cpp \ + SOURCES += \ + kernel/qeventdispatcher_win.cpp \ + kernel/qcoreapplication_win.cpp \ + kernel/qwineventnotifier_p.cpp \ kernel/qsharedmemory_win.cpp \ kernel/qsystemsemaphore_win.cpp - HEADERS += \ - kernel/qeventdispatcher_win_p.h \ - kernel/qwineventnotifier_p.h + HEADERS += \ + kernel/qeventdispatcher_win_p.h \ + kernel/qwineventnotifier_p.h } wince*: { - SOURCES += \ - kernel/qfunctions_wince.cpp - HEADERS += \ - kernel/qfunctions_wince.h + SOURCES += \ + kernel/qfunctions_wince.cpp + HEADERS += \ + kernel/qfunctions_wince.h } -mac:!embedded { +mac:!embedded:!qpa{ SOURCES += \ kernel/qcoreapplication_mac.cpp } -mac { +mac:!nacl { SOURCES += \ - kernel/qcore_mac.cpp + kernel/qcore_mac.cpp +} + +nacl { + SOURCES += \ + kernel/qfunctions_nacl.cpp + HEADERS += \ + kernel/qfunctions_nacl.h } unix:!symbian { - SOURCES += \ + SOURCES += \ kernel/qcore_unix.cpp \ kernel/qcrashhandler.cpp \ kernel/qsharedmemory_unix.cpp \ kernel/qsystemsemaphore_unix.cpp - HEADERS += \ + HEADERS += \ kernel/qcore_unix_p.h \ - kernel/qcrashhandler_p.h + kernel/qcrashhandler_p.h contains(QT_CONFIG, glib) { SOURCES += \ @@ -115,7 +124,7 @@ unix:!symbian { } symbian { - SOURCES += \ + SOURCES += \ kernel/qcore_unix.cpp \ kernel/qcrashhandler.cpp \ kernel/qeventdispatcher_symbian.cpp \ @@ -123,7 +132,7 @@ symbian { kernel/qsharedmemory_symbian.cpp \ kernel/qsystemsemaphore_symbian.cpp - HEADERS += \ + HEADERS += \ kernel/qcore_unix_p.h \ kernel/qcrashhandler_p.h \ kernel/qeventdispatcher_symbian_p.h \ @@ -131,9 +140,25 @@ symbian { } vxworks { - SOURCES += \ - kernel/qfunctions_vxworks.cpp - HEADERS += \ - kernel/qfunctions_vxworks.h + SOURCES += \ + kernel/qfunctions_vxworks.cpp + HEADERS += \ + kernel/qfunctions_vxworks.h +} + + +integrity { + SOURCES += \ + kernel/qcore_unix.cpp \ + kernel/qcrashhandler.cpp \ + kernel/qsharedmemory_unix.cpp \ + kernel/qsystemsemaphore_unix.cpp \ + kernel/qeventdispatcher_unix.cpp + HEADERS += \ + kernel/qcore_unix_p.h \ + kernel/qcrashhandler_p.h \ + kernel/qeventdispatcher_unix_p.h + + contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) } diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index 8fd010a..9dd22dc 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -532,7 +532,7 @@ bool QAbstractItemModelPrivate::variantLessThan(const QVariant &v1, const QVaria case 1: //floating point return v1.toReal() < v2.toReal(); default: - return v1.toString() < v2.toString(); + return v1.toString().localeAwareCompare(v2.toString()) < 0; } } diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index 40b8967..f7f6bfe 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -68,7 +68,15 @@ # undef qDebug #endif +#if defined(QT_BUILD_QMAKE) || defined(QT_BOOTSTRAPPED) #include <ApplicationServices/ApplicationServices.h> +#else +#include <CoreFoundation/CoreFoundation.h> +#endif + +#ifndef QT_NO_CORESERVICES +#include <CoreServices/CoreServices.h> +#endif #undef DEBUG #ifdef OLD_DEBUG diff --git a/src/corelib/kernel/qcore_symbian_p.cpp b/src/corelib/kernel/qcore_symbian_p.cpp index a7c5197..801d63f 100644 --- a/src/corelib/kernel/qcore_symbian_p.cpp +++ b/src/corelib/kernel/qcore_symbian_p.cpp @@ -61,7 +61,8 @@ Q_CORE_EXPORT HBufC* qt_QString2HBufC(const QString& aString) #else TPtrC16 ptr(qt_QString2TPtrC(aString)); #endif - buffer = q_check_ptr(HBufC::New(ptr.Length())); + buffer = HBufC::New(ptr.Length()); + Q_CHECK_PTR(buffer); buffer->Des().Copy(ptr); return buffer; } @@ -81,8 +82,9 @@ QHBufC::QHBufC() } QHBufC::QHBufC(const QHBufC &src) - : m_hBufC(q_check_ptr(src.m_hBufC->Alloc())) + : m_hBufC(src.m_hBufC->Alloc()) { + Q_CHECK_PTR(m_hBufC); } /*! diff --git a/src/corelib/kernel/qcore_symbian_p.h b/src/corelib/kernel/qcore_symbian_p.h index 3ef71e0..5b48689 100644 --- a/src/corelib/kernel/qcore_symbian_p.h +++ b/src/corelib/kernel/qcore_symbian_p.h @@ -158,6 +158,22 @@ Q_CORE_EXPORT RFs& qt_s60GetRFs(); // Defined in qlocale_symbian.cpp. Q_CORE_EXPORT QByteArray qt_symbianLocaleName(int code); +template <typename R> +struct QScopedPointerRCloser +{ + static inline void cleanup(R *rPointer) + { + // Enforce a complete type. + // If you get a compile error here, read the section on forward declared + // classes in the QScopedPointer documentation. + typedef char IsIncompleteType[ sizeof(R) ? 1 : -1 ]; + (void) sizeof(IsIncompleteType); + + if (rPointer) + rPointer->Close(); + } +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index 54972ec..834f05b 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -42,7 +42,8 @@ #include "qcore_unix_p.h" #include "qelapsedtimer.h" -#ifndef Q_OS_VXWORKS +#ifdef Q_OS_NACL +#elif !defined (Q_OS_VXWORKS) # if !defined(Q_OS_HPUX) || defined(__ia64) # include <sys/select.h> # endif diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 7d88b10..be86c58 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -71,6 +71,7 @@ # include <e32ldr.h> # include "qeventdispatcher_symbian_p.h" # include "private/qcore_symbian_p.h" +# include "private/qfilesystemengine_p.h" # include <apacmdln.h> #elif defined(Q_OS_UNIX) # if !defined(QT_NO_GLIB) @@ -174,6 +175,11 @@ CApaCommandLine* QCoreApplicationPrivate::symbianCommandLine() extern QString qAppFileName(); #endif +int QCoreApplicationPrivate::app_compile_version = 0x040000; //we don't know exactly, but it's at least 4.0.0 +#if defined(QT3_SUPPORT) +bool QCoreApplicationPrivate::useQt3Support = true; +#endif + #if !defined(Q_OS_WIN) #ifdef Q_OS_MAC QString QCoreApplicationPrivate::macMenuBarName() @@ -315,10 +321,14 @@ struct QCoreApplicationData { Q_GLOBAL_STATIC(QCoreApplicationData, coreappdata) -QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv) +QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint flags) : QObjectPrivate(), argc(aargc), argv(aargv), application_type(0), eventFilter(0), in_exec(false), aboutToQuitEmitted(false) { + app_compile_version = flags & 0xffffff; +#if defined(QT3_SUPPORT) + useQt3Support = !(flags & 0x01000000); +#endif static const char *const empty = ""; if (argc == 0 || argv == 0) { argc = 0; @@ -566,7 +576,7 @@ void QCoreApplication::flush() one valid character string. */ QCoreApplication::QCoreApplication(int &argc, char **argv) - : QObject(*new QCoreApplicationPrivate(argc, argv)) + : QObject(*new QCoreApplicationPrivate(argc, argv, 0x040000)) { init(); QCoreApplicationPrivate::eventDispatcher->startingUp(); @@ -582,6 +592,25 @@ QCoreApplication::QCoreApplication(int &argc, char **argv) #endif } +QCoreApplication::QCoreApplication(int &argc, char **argv, int _internal) +: QObject(*new QCoreApplicationPrivate(argc, argv, _internal)) +{ + init(); + QCoreApplicationPrivate::eventDispatcher->startingUp(); +#if defined(Q_OS_SYMBIAN) +#ifndef QT_NO_LIBRARY + // Refresh factoryloader, as text codecs are requested during lib path + // resolving process and won't be therefore properly loaded. + // Unknown if this is symbian specific issue. + QFactoryLoader::refreshAll(); +#endif +#ifndef QT_NO_SYSTEMLOCALE + d_func()->symbianInit(); +#endif +#endif //Q_OS_SYMBIAN +} + + // ### move to QCoreApplicationPrivate constructor? void QCoreApplication::init() { @@ -595,6 +624,12 @@ void QCoreApplication::init() Q_ASSERT_X(!self, "QCoreApplication", "there should be only one application object"); QCoreApplication::self = this; +#ifdef Q_OS_SYMBIAN + //ensure temp and working directories exist + QFileSystemEngine::createDirectory(QFileSystemEntry(QFileSystemEngine::tempPath()), true); + QFileSystemEngine::createDirectory(QFileSystemEntry(QFileSystemEngine::currentPath()), true); +#endif + #ifndef QT_NO_THREAD QThread::initialize(); #endif @@ -860,11 +895,6 @@ bool QCoreApplication::notify(QObject *receiver, QEvent *event) d->checkReceiverThread(receiver); #endif -#ifdef QT3_SUPPORT - if (event->type() == QEvent::ChildRemoved && !receiver->d_func()->pendingChildInsertedEvents.isEmpty()) - receiver->d_func()->removePendingChildInsertedEvents(static_cast<QChildEvent *>(event)->child()); -#endif // QT3_SUPPORT - return receiver->isWidgetType() ? false : d->notify_helper(receiver, event); } @@ -1074,6 +1104,7 @@ int QCoreApplication::exec() return returnCode; } + /*! Tells the application to exit with a return code. @@ -1530,7 +1561,7 @@ void QCoreApplication::removePostedEvents(QObject *receiver, int eventType) --pe.receiver->d_func()->postedEvents; #ifdef QT3_SUPPORT if (pe.event->type() == QEvent::ChildInsertedRequest) - pe.receiver->d_func()->removePendingChildInsertedEvents(0); + pe.receiver->d_func()->pendingChildInsertedEvents.clear(); #endif pe.event->posted = false; events.append(pe.event); diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 6383809..3957158 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -78,7 +78,23 @@ class Q_CORE_EXPORT QCoreApplication : public QObject Q_DECLARE_PRIVATE(QCoreApplication) public: - QCoreApplication(int &argc, char **argv); + enum { ApplicationFlags = QT_VERSION +#if !defined(QT3_SUPPORT) + | 0x01000000 +#endif + }; + +#if defined(QT_BUILD_CORE_LIB) || defined(qdoc) + QCoreApplication(int &argc, char **argv); // ### Qt5 remove +#endif +#if !defined(qdoc) + QCoreApplication(int &argc, char **argv, int +#if !defined(QT_BUILD_CORE_LIB) + = ApplicationFlags +#endif + ); +#endif + ~QCoreApplication(); #ifdef QT_DEPRECATED diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 703c825..add2a35 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -78,7 +78,7 @@ class Q_CORE_EXPORT QCoreApplicationPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QCoreApplication) public: - QCoreApplicationPrivate(int &aargc, char **aargv); + QCoreApplicationPrivate(int &aargc, char **aargv, uint flags); ~QCoreApplicationPrivate(); bool sendThroughApplicationEventFilters(QObject *, QEvent *); @@ -135,6 +135,10 @@ public: static uint attribs; static inline bool testAttribute(uint flag) { return attribs & (1 << flag); } + static int app_compile_version; +#if defined(QT3_SUPPORT) + static bool useQt3Support; +#endif }; QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index a8cf5f1..fbb08fa 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -231,6 +231,8 @@ QT_BEGIN_NAMESPACE \value WinIdChange The window system identifer for this native widget has changed \value Gesture A gesture was triggered (QGestureEvent) \value GestureOverride A gesture override was triggered (QGestureEvent) + \value ScrollPrepare The object needs to fill in its geometry information (QScrollPrepareEvent) + \value Scroll The object needs to scroll to the supplied position (QScrollEvent) User events should have values between \c User and \c{MaxUser}: diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 329c0a2..c9d311a 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -288,6 +288,9 @@ public: Gesture = 198, GestureOverride = 202, #endif + ScrollPrepare = 204, + Scroll = 205, + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event @@ -328,6 +331,7 @@ private: friend class QETWidget; friend class QGraphicsView; friend class QGraphicsViewPrivate; + friend class QGraphicsScene; friend class QGraphicsScenePrivate; #ifndef QT_NO_GESTURES friend class QGestureManager; diff --git a/src/corelib/kernel/qcrashhandler.cpp b/src/corelib/kernel/qcrashhandler.cpp index 59f3796..fbdbac0 100644 --- a/src/corelib/kernel/qcrashhandler.cpp +++ b/src/corelib/kernel/qcrashhandler.cpp @@ -339,6 +339,9 @@ static void print_backtrace(FILE *outb) "EOF\n", globalProgName, (int)getpid())) return; +#elif defined(Q_OS_INTEGRITY) + /* abort */ + CheckSuccess(Failure); #else /* All other platforms */ /* * TODO: SCO/UnixWare 7 must be something like (not tested) diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index ec70ff7..4e19414 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -180,6 +180,8 @@ static gboolean timerSourceCheck(GSource *source) static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer) { GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source); + if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers) + return true; timerSource->runWithIdlePriority = true; (void) timerSource->timerList.activateTimers(); return true; // ??? don't remove, right again? diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index 47dd558..53796be 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -657,7 +657,7 @@ public: : m_state(STATE_RUN), m_stop(false) { qt_symbian_throwIfError(m_lock.CreateLocal(0)); - TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, NULL, this); + TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this); if (err != KErrNone) m_lock.Close(); qt_symbian_throwIfError(err); @@ -692,6 +692,7 @@ public: private: static TInt idleDetectorThreadFunc(TAny* self) { + User::RenameThread(_L("IdleDetectorThread")); static_cast<QIdleDetectorThread*>(self)->IdleLoop(); return KErrNone; } @@ -809,7 +810,7 @@ bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags fla m_interrupt = false; #ifdef QT_SYMBIAN_PRIORITY_DROP - QTime eventTimer; + QElapsedTimer eventTimer; #endif while (1) { @@ -1028,7 +1029,8 @@ bool QEventDispatcherSymbian::hasPendingEvents() void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier ) { - QSocketActiveObject *socketAO = q_check_ptr(new QSocketActiveObject(this, notifier)); + QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier); + Q_CHECK_PTR(socketAO); m_notifiers.insert(notifier, socketAO); selectThread().requestSocketEvents(notifier, &socketAO->iStatus); } diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h index b785aea..bdb6dce 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian_p.h +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -63,6 +63,7 @@ #include <qwaitcondition.h> #include <qsocketnotifier.h> #include <qdatetime.h> +#include <qelapsedtimer.h> #include <e32base.h> @@ -284,7 +285,7 @@ private: int m_delay; int m_avgEventTime; - QTime m_lastIdleRequestTimer; + QElapsedTimer m_lastIdleRequestTimer; }; #ifdef QT_DEBUG diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index 050805f..dceb51d 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -112,9 +112,11 @@ QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate() bool pipefail = false; // initialize the common parts of the event loop -#if defined(Q_OS_INTEGRITY) +#if defined(Q_OS_NACL) + // do nothing. +#elif defined(Q_OS_INTEGRITY) // INTEGRITY doesn't like a "select" on pipes, so use socketpair instead - if (socketpair(AF_INET, SOCK_STREAM, PF_INET, thread_pipe) == -1) { + if (socketpair(AF_INET, SOCK_STREAM, 0, thread_pipe) == -1) { perror("QEventDispatcherUNIXPrivate(): Unable to create socket pair"); pipefail = true; } else { @@ -157,7 +159,9 @@ QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate() QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate() { -#if defined(Q_OS_VXWORKS) +#if defined(Q_OS_NACL) + // do nothing. +#elif defined(Q_OS_VXWORKS) close(thread_pipe[0]); char name[20]; @@ -312,7 +316,7 @@ int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, QTimerInfoList::QTimerInfoList() { -#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) +#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_NACL) if (!QElapsedTimer::isMonotonic()) { // not using monotonic timers, initialize the timeChanged() machinery previousTime = qt_gettime(); @@ -339,7 +343,7 @@ timeval QTimerInfoList::updateCurrentTime() return (currentTime = qt_gettime()); } -#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED) +#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED) template <> timeval qAbs(const timeval &t) @@ -364,6 +368,10 @@ timeval qAbs(const timeval &t) */ bool QTimerInfoList::timeChanged(timeval *delta) { +#ifdef Q_OS_NACL + Q_UNUSED(delta) + return false; // Calling "times" crashes. +#endif struct tms unused; clock_t currentTicks = times(&unused); diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index 8055425..96a3e4a 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -64,7 +64,7 @@ # include <sys/times.h> #else # include <sys/time.h> -# if !defined(Q_OS_HPUX) || defined(__ia64) +# if (!defined(Q_OS_HPUX) || defined(__ia64)) && !defined(Q_OS_NACL) # include <sys/select.h> # endif #endif diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 6d90a51..6badb6a 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -78,6 +78,8 @@ extern uint qGlobalPostedEventsCount(); #ifndef QT_NO_GESTURES #ifndef WM_GESTURE # define WM_GESTURE 0x0119 +#endif +#ifndef WM_GESTURENOTIFY # define WM_GESTURENOTIFY 0x011A #endif #endif // QT_NO_GESTURES @@ -490,7 +492,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA // we also use a Windows timer to send posted events when the message queue is full || (message == WM_TIMER && d->sendPostedEventsWindowsTimerId != 0 - && wp == d->sendPostedEventsWindowsTimerId)) { + && wp == (uint)d->sendPostedEventsWindowsTimerId)) { int localSerialNumber = d->serialNumber; if (localSerialNumber != d->lastSerialNumber) { d->lastSerialNumber = localSerialNumber; diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp index e78f86a..d213b0e 100644 --- a/src/corelib/kernel/qeventloop.cpp +++ b/src/corelib/kernel/qeventloop.cpp @@ -175,6 +175,8 @@ bool QEventLoop::processEvents(ProcessEventsFlags flags) int QEventLoop::exec(ProcessEventsFlags flags) { Q_D(QEventLoop); + //we need to protect from race condition with QThread::exit + QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex); if (d->threadData->quitNow) return -1; @@ -186,6 +188,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) d->exit = false; ++d->threadData->loopLevel; d->threadData->eventLoops.push(this); + locker.unlock(); // remove posted quit events when entering a new event loop QCoreApplication *app = QCoreApplication::instance(); @@ -205,6 +208,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) "reimplement QApplication::notify() and catch all exceptions there.\n"); // copied from below + locker.relock(); QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning @@ -216,6 +220,7 @@ int QEventLoop::exec(ProcessEventsFlags flags) #endif // copied above + locker.relock(); QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning diff --git a/src/corelib/kernel/qfunctions_nacl.cpp b/src/corelib/kernel/qfunctions_nacl.cpp new file mode 100644 index 0000000..f3d85ef --- /dev/null +++ b/src/corelib/kernel/qfunctions_nacl.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfunctions_nacl.h" +#include <pthread.h> +#include <qglobal.h> + +/* + The purpose of this file is to stub out certain functions + that are not provided by the Native Client SDK. This is + done as an alterative to sprinkling the Qt sources with + NACL ifdefs. + + There are two main classes of functions: + + - Functions that are called but can have no effect: + For these we simply give an empty implementation + + - Functions that are referenced in the source code, but + is not/must not be called at run-time: + These we either leave undefined or implement with a + qFatal. + + This is a work in progress. +*/ + +extern "C" { + +void pthread_cleanup_push(void (*)(void *), void *) +{ + +} + +void pthread_cleanup_pop(int) +{ + +} + +int pthread_setcancelstate(int, int *) +{ + return 0; +} + +int pthread_setcanceltype(int, int *) +{ + return 0; +} + +void pthread_testcancel(void) +{ + +} + + +int pthread_cancel(pthread_t) +{ + return 0; +} + +int pthread_attr_setinheritsched(pthread_attr_t *,int) +{ + return 0; +} + + +int pthread_attr_getinheritsched(const pthread_attr_t *, int *) +{ + return 0; +} + +// event dispatcher, select +//struct fd_set; +//struct timeval; + +int fcntl(int, int, ...) +{ + return 0; +} + +int sigaction(int, const struct sigaction *, struct sigaction *) +{ + return 0; +} + +int open(const char *, int, ...) +{ + return 0; +} + +int open64(const char *, int, ...) +{ + return 0; +} + +int access(const char *, int) +{ + return 0; +} + +typedef long off64_t; +off64_t ftello64(void *) +{ + qFatal("ftello64 called"); + return 0; +} + +off64_t lseek64(int, off_t, int) +{ + qFatal("lseek64 called"); + return 0; +} + +} // Extern C + +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *) +{ + return 0; +} diff --git a/src/corelib/kernel/qfunctions_nacl.h b/src/corelib/kernel/qfunctions_nacl.h new file mode 100644 index 0000000..db36ad5 --- /dev/null +++ b/src/corelib/kernel/qfunctions_nacl.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNACLUNIMPLEMENTED_H +#define QNACLUNIMPLEMENTED_H + +#ifdef Q_OS_NACL + +#include <sys/types.h> + +// pthread +#include <pthread.h> +#define PTHREAD_CANCEL_DISABLE 1 +#define PTHREAD_CANCEL_ENABLE 2 +#define PTHREAD_INHERIT_SCHED 3 + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +extern "C" { + +void pthread_cleanup_push(void (*handler)(void *), void *arg); +void pthread_cleanup_pop(int execute); + +int pthread_setcancelstate(int state, int *oldstate); +int pthread_setcanceltype(int type, int *oldtype); +void pthread_testcancel(void); +int pthread_cancel(pthread_t thread); + +int pthread_attr_setinheritsched(pthread_attr_t *attr, + int inheritsched); +int pthread_attr_getinheritsched(const pthread_attr_t *attr, + int *inheritsched); + +// event dispatcher, select +//struct fd_set; +//struct timeval; +int fcntl(int fildes, int cmd, ...); +int sigaction(int sig, const struct sigaction * act, struct sigaction * oact); + +typedef long off64_t; +off64_t ftello64(void *stream); +off64_t lseek64(int fildes, off_t offset, int whence); +int open64(const char *path, int oflag, ...); + +} + +int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //Q_OS_NACL + +#endif //QNACLUNIMPLEMENTED_H diff --git a/src/corelib/kernel/qfunctions_p.h b/src/corelib/kernel/qfunctions_p.h index 3d41c0c..d27fec3 100644 --- a/src/corelib/kernel/qfunctions_p.h +++ b/src/corelib/kernel/qfunctions_p.h @@ -59,6 +59,8 @@ # include "QtCore/qfunctions_wince.h" #elif defined(Q_OS_VXWORKS) # include "QtCore/qfunctions_vxworks.h" +#elif defined(Q_OS_NACL) +# include "QtCore/qfunctions_nacl.h" #endif #ifdef Q_CC_RVCT diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index d07b7cb..c5775f6 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -266,7 +266,25 @@ QObject *QMetaObject::cast(QObject *obj) const const QMetaObject *m = obj->metaObject(); do { if (m == this) - return const_cast<QObject*>(obj); + return obj; + } while ((m = m->d.superdata)); + } + return 0; +} + +/*! + \internal + + Returns \a obj if object \a obj inherits from this + meta-object; otherwise returns 0. +*/ +const QObject *QMetaObject::cast(const QObject *obj) const +{ + if (obj) { + const QMetaObject *m = obj->metaObject(); + do { + if (m == this) + return obj; } while ((m = m->d.superdata)); } return 0; @@ -1577,6 +1595,12 @@ bool QMetaMethod::invoke(QObject *object, : Qt::QueuedConnection; } +#ifdef QT_NO_THREAD + if (connectionType == Qt::BlockingQueuedConnection) { + connectionType = Qt::DirectConnection; + } +#endif + // invoke! void *param[] = { returnValue.data(), @@ -1595,7 +1619,7 @@ bool QMetaMethod::invoke(QObject *object, int methodIndex = ((handle - priv(mobj->d.data)->methodData) / 5) + mobj->methodOffset(); if (connectionType == Qt::DirectConnection) { return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0; - } else { + } else if (connectionType == Qt::QueuedConnection) { if (returnValue.data()) { qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " "queued connections"); @@ -1628,40 +1652,21 @@ bool QMetaMethod::invoke(QObject *object, } } - if (connectionType == Qt::QueuedConnection) { - QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, - 0, - -1, - nargs, - types, - args)); - } else { - if (currentThread == objectThread) { - qWarning("QMetaMethod::invoke: Dead lock detected in " - "BlockingQueuedConnection: Receiver is %s(%p)", - mobj->className(), object); - } + QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, + 0, -1, nargs, types, args)); + } else { // blocking queued connection +#ifndef QT_NO_THREAD + if (currentThread == objectThread) { + qWarning("QMetaMethod::invoke: Dead lock detected in " + "BlockingQueuedConnection: Receiver is %s(%p)", + mobj->className(), object); + } - // blocking queued connection -#ifdef QT_NO_THREAD - QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, - 0, - -1, - nargs, - types, - args)); -#else - QSemaphore semaphore; - QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, - 0, - -1, - nargs, - types, - args, - &semaphore)); - semaphore.acquire(); + QSemaphore semaphore; + QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, + 0, -1, 0, 0, param, &semaphore)); + semaphore.acquire(); #endif // QT_NO_THREAD - } } return true; } diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 7b2e938..e9bba45 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -139,6 +139,8 @@ private: const QMetaObject *mobj; uint handle; friend struct QMetaObject; + friend struct QMetaObjectPrivate; + friend class QObject; }; Q_DECLARE_TYPEINFO(QMetaMethod, Q_MOVABLE_TYPE); diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 1d97cb7..210b32c 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -133,6 +133,8 @@ struct QMetaObjectPrivate #ifndef QT_NO_QOBJECT //defined in qobject.cpp enum DisconnectType { DisconnectAll, DisconnectOne }; + static void memberIndexes(const QObject *obj, const QMetaMethod &member, + int *signalIndex, int *methodIndex); static bool connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type = 0, int *types = 0); diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 3296d1f..bdc96c6 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -311,6 +311,7 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ QT_ADD_STATIC_METATYPE("long long", QMetaType::LongLong), QT_ADD_STATIC_METATYPE("unsigned long long", QMetaType::ULongLong), QT_ADD_STATIC_METATYPE("qint8", QMetaType::Char), + QT_ADD_STATIC_METATYPE("signed char", QMetaType::Char), QT_ADD_STATIC_METATYPE("quint8", QMetaType::UChar), QT_ADD_STATIC_METATYPE("qint16", QMetaType::Short), QT_ADD_STATIC_METATYPE("quint16", QMetaType::UShort), diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 8eeee30..a2bb7d1 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -353,6 +353,7 @@ Q_DECLARE_BUILTIN_METATYPE(QChar, QChar) Q_DECLARE_BUILTIN_METATYPE(long, Long) Q_DECLARE_BUILTIN_METATYPE(short, Short) Q_DECLARE_BUILTIN_METATYPE(char, Char) +Q_DECLARE_BUILTIN_METATYPE(signed char, Char) Q_DECLARE_BUILTIN_METATYPE(ulong, ULong) Q_DECLARE_BUILTIN_METATYPE(ushort, UShort) Q_DECLARE_BUILTIN_METATYPE(uchar, UChar) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 4a5b63f..c6153cb 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -150,18 +150,33 @@ QObjectPrivate::QObjectPrivate(int version) postedEvents = 0; extraData = 0; connectedSignals[0] = connectedSignals[1] = 0; - inEventHandler = false; inThreadChangeEvent = false; +#ifdef QT_JAMBI_BUILD + inEventHandler = false; deleteWatch = 0; +#endif metaObject = 0; hasGuards = false; } QObjectPrivate::~QObjectPrivate() { + if (pendTimer) { + // unregister pending timers + if (threadData->eventDispatcher) + threadData->eventDispatcher->unregisterTimers(q_ptr); + } + + if (postedEvents) + QCoreApplication::removePostedEvents(q_ptr, 0); + + threadData->deref(); + delete static_cast<QAbstractDynamicMetaObject*>(metaObject); +#ifdef QT_JAMBI_BUILD if (deleteWatch) *deleteWatch = 1; +#endif #ifndef QT_NO_USERDATA if (extraData) qDeleteAll(extraData->userData); @@ -170,6 +185,7 @@ QObjectPrivate::~QObjectPrivate() } +#ifdef QT_JAMBI_BUILD int *QObjectPrivate::setDeleteWatch(QObjectPrivate *d, int *w) { int *old = d->deleteWatch; d->deleteWatch = w; @@ -184,18 +200,15 @@ void QObjectPrivate::resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int dele if (oldWatch) *oldWatch = deleteWatch; } - - - - +#endif #ifdef QT3_SUPPORT void QObjectPrivate::sendPendingChildInsertedEvents() { Q_Q(QObject); for (int i = 0; i < pendingChildInsertedEvents.size(); ++i) { - QObject *c = pendingChildInsertedEvents.at(i); - if (!c) + QObject *c = pendingChildInsertedEvents.at(i).data(); + if (!c || c->parent() != q) continue; QChildEvent childEvent(QEvent::ChildInserted, c); QCoreApplication::sendEvent(q, &childEvent); @@ -203,26 +216,6 @@ void QObjectPrivate::sendPendingChildInsertedEvents() pendingChildInsertedEvents.clear(); } -void QObjectPrivate::removePendingChildInsertedEvents(QObject *child) -{ - if (!child) { - pendingChildInsertedEvents.clear(); - return; - } - - // the QObject destructor calls QObject::removeChild, which calls - // QCoreApplication::sendEvent() directly. this can happen while the event - // loop is in the middle of posting events, and when we get here, we may - // not have any more posted events for this object. - - // if this is a child remove event and the child insert hasn't - // been dispatched yet, kill that insert - for (int i = 0; i < pendingChildInsertedEvents.size(); ++i) { - QObject *&c = pendingChildInsertedEvents[i]; - if (c == child) - c = 0; - } -} #endif @@ -426,6 +419,8 @@ void QMetaObject::removeGuard(QObject **ptr) if (!hash || hash->isEmpty()) return; QMutexLocker locker(guardHashLock()); + if (!*ptr) //check again, under the lock + return; GuardHash::iterator it = hash->find(*ptr); const GuardHash::iterator end = hash->end(); bool more = false; //if the QObject has more pointer attached to it. @@ -477,11 +472,6 @@ void QMetaObject::changeGuard(QObject **ptr, QObject *o) */ void QObjectPrivate::clearGuards(QObject *object) { - QObjectPrivate *priv = QObjectPrivate::get(object); - - if (!priv->hasGuards) - return; - GuardHash *hash = 0; QMutex *mutex = 0; QT_TRY { @@ -516,12 +506,14 @@ QMetaCallEvent::QMetaCallEvent(int id, const QObject *sender, int signalId, */ QMetaCallEvent::~QMetaCallEvent() { - for (int i = 0; i < nargs_; ++i) { - if (types_[i] && args_[i]) - QMetaType::destroy(types_[i], args_[i]); + if (types_) { + for (int i = 0; i < nargs_; ++i) { + if (types_[i] && args_[i]) + QMetaType::destroy(types_[i], args_[i]); + } + qFree(types_); + qFree(args_); } - if (types_) qFree(types_); - if (args_) qFree(args_); #ifndef QT_NO_THREAD if (semaphore_) semaphore_->release(); @@ -731,13 +723,15 @@ QObject::QObject(QObject *parent) d_ptr->q_ptr = this; d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); d->threadData->ref(); - QT_TRY { - if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) - parent = 0; - setParent(parent); - } QT_CATCH(...) { - d->threadData->deref(); - QT_RETHROW; + if (parent) { + QT_TRY { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + setParent(parent); + } QT_CATCH(...) { + d->threadData->deref(); + QT_RETHROW; + } } qt_addObject(this); } @@ -756,9 +750,11 @@ QObject::QObject(QObject *parent, const char *name) qt_addObject(d_ptr->q_ptr = this); d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); d->threadData->ref(); - if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) - parent = 0; - setParent(parent); + if (parent) { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + setParent(parent); + } setObjectName(QString::fromAscii(name)); } #endif @@ -772,21 +768,23 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent) d_ptr->q_ptr = this; d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); d->threadData->ref(); - QT_TRY { - if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) - parent = 0; - if (d->isWidget) { - if (parent) { - d->parent = parent; - d->parent->d_func()->children.append(this); + if (parent) { + QT_TRY { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + if (d->isWidget) { + if (parent) { + d->parent = parent; + d->parent->d_func()->children.append(this); + } + // no events sent here, this is done at the end of the QWidget constructor + } else { + setParent(parent); } - // no events sent here, this is done at the end of the QWidget constructor - } else { - setParent(parent); + } QT_CATCH(...) { + d->threadData->deref(); + QT_RETHROW; } - } QT_CATCH(...) { - d->threadData->deref(); - QT_RETHROW; } qt_addObject(this); } @@ -821,7 +819,7 @@ QObject::~QObject() d->wasDeleted = true; d->blockSig = 0; // unblock signals so we always emit destroyed() - if (!d->isWidget) { + if (d->hasGuards && !d->isWidget) { // set all QPointers for this object to zero - note that // ~QWidget() does this for us, so we don't have to do it twice QObjectPrivate::clearGuards(this); @@ -839,22 +837,16 @@ QObject::~QObject() delete d->sharedRefcount; } - QT_TRY { - emit destroyed(this); - } QT_CATCH(...) { - // all the signal/slots connections are still in place - if we don't - // quit now, we will crash pretty soon. - qWarning("Detected an unexpected exception in ~QObject while emitting destroyed()."); -#if defined(Q_BUILD_INTERNAL) && !defined(QT_NO_EXCEPTIONS) - struct AutotestException : public std::exception - { - const char *what() const throw() { return "autotest swallow"; } - } autotestException; - // throw autotestException; -#else - QT_RETHROW; -#endif + if (d->isSignalConnected(0)) { + QT_TRY { + emit destroyed(this); + } QT_CATCH(...) { + // all the signal/slots connections are still in place - if we don't + // quit now, we will crash pretty soon. + qWarning("Detected an unexpected exception in ~QObject while emitting destroyed()."); + QT_RETHROW; + } } if (d->declarativeData) @@ -892,7 +884,7 @@ QObject::~QObject() if (c->next) c->next->prev = c->prev; } if (needToUnlock) - m->unlock(); + m->unlockInline(); connectionList.first = c->nextConnectionList; delete c; @@ -916,7 +908,7 @@ QObject::~QObject() bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); //the node has maybe been removed while the mutex was unlocked in relock? if (!node || node->sender != sender) { - m->unlock(); + m->unlockInline(); continue; } node->receiver = 0; @@ -926,35 +918,18 @@ QObject::~QObject() node = node->next; if (needToUnlock) - m->unlock(); + m->unlockInline(); } } - if (d->pendTimer) { - // unregister pending timers - if (d->threadData->eventDispatcher) - d->threadData->eventDispatcher->unregisterTimers(this); - } - -#ifdef QT3_SUPPORT - d->pendingChildInsertedEvents.clear(); -#endif - - d->eventFilters.clear(); - if (!d->children.isEmpty()) d->deleteChildren(); qt_removeObject(this); - if (d->postedEvents) - QCoreApplication::removePostedEvents(this, 0); - if (d->parent) // remove it from parent object d->setParent_helper(0); - d->threadData->deref(); - #ifdef QT_JAMBI_BUILD if (d->inEventHandler) { qWarning("QObject: Do not delete object, '%s', during its event handler!", @@ -1202,7 +1177,9 @@ bool QObject::event(QEvent *e) case QEvent::MetaCall: { +#ifdef QT_JAMBI_BUILD d_func()->inEventHandler = false; +#endif QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e); QObjectPrivate::Sender currentSender; currentSender.sender = const_cast<QObject*>(mce->sender()); @@ -1520,11 +1497,14 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData currentSender->ref = 0; currentSender = 0; +#ifdef QT_JAMBI_BUILD // the current event thread also shouldn't restore the delete watch inEventHandler = false; + if (deleteWatch) *deleteWatch = 1; deleteWatch = 0; +#endif // set new thread data targetData->ref(); @@ -1774,11 +1754,7 @@ QObjectList QObject::queryList(const char *inheritsClass, \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 11 - \warning This function is not available with MSVC 6. Use - qFindChild() instead if you need to support that version of the - compiler. - - \sa findChildren(), qFindChild() + \sa findChildren() */ /*! @@ -1798,11 +1774,7 @@ QObjectList QObject::queryList(const char *inheritsClass, \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 13 - \warning This function is not available with MSVC 6. Use - qFindChildren() instead if you need to support that version of the - compiler. - - \sa findChild(), qFindChildren() + \sa findChild() */ /*! @@ -1813,20 +1785,19 @@ QObjectList QObject::queryList(const char *inheritsClass, and that have names matching the regular expression \a regExp, or an empty list if there are no such objects. The search is performed recursively. - - \warning This function is not available with MSVC 6. Use - qFindChildren() instead if you need to support that version of the - compiler. */ /*! \fn T qFindChild(const QObject *obj, const QString &name) \relates QObject + \obsolete This function is equivalent to - \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QObject::findChild() */ @@ -1834,11 +1805,14 @@ QObjectList QObject::queryList(const char *inheritsClass, /*! \fn QList<T> qFindChildren(const QObject *obj, const QString &name) \relates QObject + \obsolete This function is equivalent to - \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QObject::findChildren() */ @@ -1849,9 +1823,13 @@ QObjectList QObject::queryList(const char *inheritsClass, \overload qFindChildren() This function is equivalent to - \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a regExp). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a regExp). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChildren() */ /*! @@ -1861,9 +1839,11 @@ QObjectList QObject::queryList(const char *inheritsClass, \overload qFindChildren() This function is equivalent to - \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QObject::findChild() */ @@ -1875,9 +1855,11 @@ QObjectList QObject::queryList(const char *inheritsClass, \overload qFindChildren() This function is equivalent to - \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QObject::findChildren() */ @@ -1997,12 +1979,14 @@ void QObjectPrivate::setParent_helper(QObject *o) QChildEvent e(QEvent::ChildAdded, q); QCoreApplication::sendEvent(parent, &e); #ifdef QT3_SUPPORT - if (parent->d_func()->pendingChildInsertedEvents.isEmpty()) { - QCoreApplication::postEvent(parent, - new QEvent(QEvent::ChildInsertedRequest), - Qt::HighEventPriority); + if (QCoreApplicationPrivate::useQt3Support) { + if (parent->d_func()->pendingChildInsertedEvents.isEmpty()) { + QCoreApplication::postEvent(parent, + new QEvent(QEvent::ChildInsertedRequest), + Qt::HighEventPriority); + } + parent->d_func()->pendingChildInsertedEvents.append(q); } - parent->d_func()->pendingChildInsertedEvents.append(q); #endif } } @@ -2311,7 +2295,7 @@ static void err_info_about_objects(const char * func, a thread different from this object's thread. Do not use this function in this type of scenario. - \sa QSignalMapper + \sa senderSignalIndex(), QSignalMapper */ QObject *QObject::sender() const @@ -2322,13 +2306,53 @@ QObject *QObject::sender() const if (!d->currentSender) return 0; - // Return 0 if d->currentSender isn't in d->senders - bool found = false; - for (QObjectPrivate::Connection *c = d->senders; c && !found; c = c->next) - found = (c->sender == d->currentSender->sender); - if (!found) - return 0; - return d->currentSender->sender; + for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) { + if (c->sender == d->currentSender->sender) + return d->currentSender->sender; + } + + return 0; +} + +/*! + \since 4.8 + + Returns the meta-method index of the signal that called the currently + executing slot, which is a member of the class returned by sender(). + If called outside of a slot activated by a signal, -1 is returned. + + For signals with default parameters, this function will always return + the index with all parameters, regardless of which was used with + connect(). For example, the signal \c {destroyed(QObject *obj = 0)} + will have two different indexes (with and without the parameter), but + this function will always return the index with a parameter. This does + not apply when overloading signals with different parameters. + + \warning This function violates the object-oriented principle of + modularity. However, getting access to the signal index might be useful + when many signals are connected to a single slot. + + \warning The return value of this function is not valid when the slot + is called via a Qt::DirectConnection from a thread different from this + object's thread. Do not use this function in this type of scenario. + + \sa sender(), QMetaObject::indexOfSignal(), QMetaObject::method() +*/ + +int QObject::senderSignalIndex() const +{ + Q_D(const QObject); + + QMutexLocker locker(signalSlotLock(this)); + if (!d->currentSender) + return -1; + + for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) { + if (c->sender == d->currentSender->sender) + return d->currentSender->signal; + } + + return -1; } /*! @@ -2390,6 +2414,71 @@ int QObject::receivers(const char *signal) const } /*! + \internal + + This helper function calculates signal and method index for the given + member in the specified class. + + \li If member.mobj is 0 then both signalIndex and methodIndex are set to -1. + + \li If specified member is not a member of obj instance class (or one of + its parent classes) then both signalIndex and methodIndex are set to -1. + + This function is used by QObject::connect and QObject::disconnect which + are working with QMetaMethod. + + \param[out] signalIndex is set to the signal index of member. If the member + specified is not signal this variable is set to -1. + + \param[out] methodIndex is set to the method index of the member. If the + member is not a method of the object specified by obj param this variable + is set to -1. +*/ +void QMetaObjectPrivate::memberIndexes(const QObject *obj, + const QMetaMethod &member, + int *signalIndex, int *methodIndex) +{ + *signalIndex = -1; + *methodIndex = -1; + if (!obj || !member.mobj) + return; + const QMetaObject *m = obj->metaObject(); + // Check that member is member of obj class + while (m != 0 && m != member.mobj) + m = m->d.superdata; + if (!m) + return; + *signalIndex = *methodIndex = (member.handle - get(member.mobj)->methodData)/5; + + int signalOffset; + int methodOffset; + computeOffsets(m, &signalOffset, &methodOffset); + + *methodIndex += methodOffset; + if (member.methodType() == QMetaMethod::Signal) { + *signalIndex = originalClone(m, *signalIndex); + *signalIndex += signalOffset; + } else { + *signalIndex = -1; + } +} + +static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal, + const QMetaObject *receiver, const QMetaMethod &method) +{ + if (signal.attributes() & QMetaMethod::Compatibility) { + if (!(method.attributes() & QMetaMethod::Compatibility)) + qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", + sender->className(), signal.signature()); + } else if ((method.attributes() & QMetaMethod::Compatibility) && + method.methodType() == QMetaMethod::Signal) { + qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", + sender->className(), signal.signature(), + receiver->className(), method.signature()); + } +} + +/*! \threadsafe Creates a connection of the given \a type from the \a signal in @@ -2566,7 +2655,7 @@ bool QObject::connect(const QObject *sender, const char *signal, } int *types = 0; - if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) + if ((type == Qt::QueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) return false; @@ -2574,15 +2663,8 @@ bool QObject::connect(const QObject *sender, const char *signal, { QMetaMethod smethod = smeta->method(signal_absolute_index); QMetaMethod rmethod = rmeta->method(method_index); - if (warnCompat) { - if(smethod.attributes() & QMetaMethod::Compatibility) { - if (!(rmethod.attributes() & QMetaMethod::Compatibility)) - qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal); - } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) { - qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", - smeta->className(), signal, rmeta->className(), method); - } - } + if (warnCompat) + check_and_warn_compat(smeta, smethod, rmeta, rmethod); } #endif if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) @@ -2591,6 +2673,111 @@ bool QObject::connect(const QObject *sender, const char *signal, return true; } +/*! + \since 4.8 + + Creates a connection of the given \a type from the \a signal in + the \a sender object to the \a method in the \a receiver object. + Returns true if the connection succeeds; otherwise returns false. + + This function works in the same way as + connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) + but it uses QMetaMethod to specify signal and method. + + \see connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) + */ +bool QObject::connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type) +{ +#ifndef QT_NO_DEBUG + bool warnCompat = true; +#endif + if (type == Qt::AutoCompatConnection) { + type = Qt::AutoConnection; +#ifndef QT_NO_DEBUG + warnCompat = false; +#endif + } + + if (sender == 0 + || receiver == 0 + || signal.methodType() != QMetaMethod::Signal + || method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", + sender ? sender->metaObject()->className() : "(null)", + signal.signature(), + receiver ? receiver->metaObject()->className() : "(null)", + method.signature() ); + return false; + } + + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + + { + QByteArray methodSignature; + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + + + int signal_index; + int method_index; + { + int dummy; + QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); + QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); + } + + const QMetaObject *smeta = sender->metaObject(); + const QMetaObject *rmeta = receiver->metaObject(); + if (signal_index == -1) { + qWarning("QObject::connect: Can't find signal %s on instance of class %s", + signal.signature(), smeta->className()); + return false; + } + if (method_index == -1) { + qWarning("QObject::connect: Can't find method %s on instance of class %s", + method.signature(), rmeta->className()); + return false; + } + + if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + qWarning("QObject::connect: Incompatible sender/receiver arguments" + "\n %s::%s --> %s::%s", + smeta->className(), signal.signature(), + rmeta->className(), method.signature()); + return false; + } + + int *types = 0; + if ((type == Qt::QueuedConnection) + && !(types = queuedConnectionTypes(signal.parameterTypes()))) + return false; + +#ifndef QT_NO_DEBUG + if (warnCompat) + check_and_warn_compat(smeta, signal, rmeta, method); +#endif + if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) + return false; + + const_cast<QObject*>(sender)->connectNotify(signalSignature.constData()); + return true; +} /*! \fn bool QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const @@ -2770,6 +2957,107 @@ bool QObject::disconnect(const QObject *sender, const char *signal, return res; } +/*! + \since 4.8 + + Disconnects \a signal in object \a sender from \a method in object + \a receiver. Returns true if the connection is successfully broken; + otherwise returns false. + + This function provides the same posibilities like + disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) + but uses QMetaMethod to represent the signal and the method to be disconnected. + + Additionally this function returnsfalse and no signals and slots disconnected + if: + \list 1 + + \i \a signal is not a member of sender class or one of its parent classes. + + \i \a method is not a member of receiver class or one of its parent classes. + + \i \a signal instance represents not a signal. + + \endlist + + QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object". + In the same way 0 can be used for \a receiver in the meaning "any receiving object". In this case + method shoud also be QMetaMethod(). \a sender parameter should be never 0. + + \see disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) + */ +bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method) +{ + if (sender == 0 || (receiver == 0 && method.mobj != 0)) { + qWarning("Object::disconnect: Unexpected null parameter"); + return false; + } + if (signal.mobj) { + if(signal.methodType() != QMetaMethod::Signal) { + qWarning("Object::%s: Attempt to %s non-signal %s::%s", + "disconnect","unbind", + sender->metaObject()->className(), signal.signature()); + return false; + } + } + if (method.mobj) { + if(method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::disconect: cannot use constructor as argument %s::%s", + receiver->metaObject()->className(), method.signature()); + return false; + } + } + + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + if (signal.mobj) { + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + } + + { + QByteArray methodSignature; + if (method.mobj) { + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + } + const void *cbdata[] = { sender, signal.mobj ? signalSignature.constData() : 0, + receiver, method.mobj ? methodSignature.constData() : 0 }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + + int signal_index; + int method_index; + { + int dummy; + QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); + QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); + } + // If we are here sender is not null. If signal is not null while signal_index + // is -1 then this signal is not a member of sender. + if (signal.mobj && signal_index == -1) { + qWarning("QObject::disconect: signal %s not found on class %s", + signal.signature(), sender->metaObject()->className()); + return false; + } + // If this condition is true then method is not a member of receeiver. + if (receiver && method.mobj && method_index == -1) { + qWarning("QObject::disconect: method %s not found on class %s", + method.signature(), receiver->metaObject()->className()); + return false; + } + + if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) + return false; + + const_cast<QObject*>(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0); + return true; +} /*! \threadsafe @@ -2987,7 +3275,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, } if (needToUnlock) - receiverMutex->unlock(); + receiverMutex->unlockInline(); c->receiver = 0; @@ -3074,7 +3362,7 @@ void QMetaObject::connectSlotsByName(QObject *o) return; const QMetaObject *mo = o->metaObject(); Q_ASSERT(mo); - const QObjectList list = qFindChildren<QObject *>(o, QString()); + const QObjectList list = o->findChildren<QObject *>(QString()); for (int i = 0; i < mo->methodCount(); ++i) { const char *slot = mo->method(i).signature(); Q_ASSERT(slot); @@ -3121,8 +3409,7 @@ void QMetaObject::connectSlotsByName(QObject *o) } } -static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, - void **argv, QSemaphore *semaphore = 0) +static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) { if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { QMetaMethod m = sender->metaObject()->method(signal); @@ -3152,30 +3439,9 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect signal, nargs, types, - args, - semaphore)); + args)); } -static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) -{ - if (QThread::currentThread() == c->receiver->thread()) { - qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " - "Sender is %s(%p), receiver is %s(%p)", - sender->metaObject()->className(), sender, - c->receiver->metaObject()->className(), c->receiver); - } - -#ifdef QT_NO_THREAD - queued_activate(sender, signal, c, argv); -#else - QSemaphore semaphore; - queued_activate(sender, signal, c, argv, &semaphore); - QMutex *mutex = signalSlotLock(sender); - mutex->unlock(); - semaphore.acquire(); - mutex->lock(); -#endif -} /*!\internal \obsolete. @@ -3239,23 +3505,36 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign continue; QObject * const receiver = c->receiver; + const int method = c->method; + const bool receiverInSameThread = currentThreadData == receiver->d_func()->threadData; // determine if this connection should be sent immediately or // put into the event queue - if ((c->connectionType == Qt::AutoConnection - && (currentThreadData != sender->d_func()->threadData - || receiver->d_func()->threadData != sender->d_func()->threadData)) + if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectionType == Qt::QueuedConnection)) { queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv); continue; +#ifndef QT_NO_THREAD } else if (c->connectionType == Qt::BlockingQueuedConnection) { - blocking_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv); + locker.unlock(); + if (receiverInSameThread) { + qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " + "Sender is %s(%p), receiver is %s(%p)", + sender->metaObject()->className(), sender, + receiver->metaObject()->className(), receiver); + } + QSemaphore semaphore; + QCoreApplication::postEvent(receiver, new QMetaCallEvent(method, + sender, signal_absolute_index, + 0, 0, + argv ? argv : empty_argv, + &semaphore)); + semaphore.acquire(); + locker.relock(); continue; +#endif } - - const int method = c->method; QObjectPrivate::Sender currentSender; - const bool receiverInSameThread = currentThreadData == receiver->d_func()->threadData; QObjectPrivate::Sender *previousSender = 0; if (receiverInSameThread) { currentSender.sender = sender; diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 2c9c106..0ad73f5 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -77,19 +77,9 @@ class QObjectUserData; typedef QList<QObject*> QObjectList; -#if defined Q_CC_MSVC && _MSC_VER < 1300 -template<typename T> inline T qFindChild(const QObject *o, const QString &name = QString(), T = 0); -template<typename T> inline QList<T> qFindChildren(const QObject *o, const QString &name = QString(), T = 0); -# ifndef QT_NO_REGEXP -template<typename T> inline QList<T> qFindChildren(const QObject *o, const QRegExp &re, T = 0); -# endif -#else -template<typename T> inline T qFindChild(const QObject *, const QString & = QString()); -template<typename T> inline QList<T> qFindChildren(const QObject *, const QString & = QString()); -# ifndef QT_NO_REGEXP -template<typename T> inline QList<T> qFindChildren(const QObject *, const QRegExp &); -# endif -#endif +Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QString &name, const QRegExp *re, + const QMetaObject &mo, QList<void *> *list); +Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo); class #if defined(__INTEL_COMPILER) && defined(Q_OS_WIN) @@ -109,7 +99,7 @@ public: uint ownObjectName : 1; uint sendChildEvents : 1; uint receiveChildEvents : 1; - uint inEventHandler : 1; + uint inEventHandler : 1; //only used if QT_JAMBI_BUILD uint inThreadChangeEvent : 1; uint hasGuards : 1; //true iff there is one or more QPointer attached to this object uint unused : 22; @@ -164,20 +154,36 @@ public: int startTimer(int interval); void killTimer(int id); -#ifndef QT_NO_MEMBER_TEMPLATES template<typename T> inline T findChild(const QString &aName = QString()) const - { return qFindChild<T>(this, aName); } + { return static_cast<T>(qt_qFindChild_helper(this, aName, reinterpret_cast<T>(0)->staticMetaObject)); } template<typename T> inline QList<T> findChildren(const QString &aName = QString()) const - { return qFindChildren<T>(this, aName); } + { + QList<T> list; + union { + QList<T> *typedList; + QList<void *> *voidList; + } u; + u.typedList = &list; + qt_qFindChildren_helper(this, aName, 0, reinterpret_cast<T>(0)->staticMetaObject, u.voidList); + return list; + } #ifndef QT_NO_REGEXP template<typename T> inline QList<T> findChildren(const QRegExp &re) const - { return qFindChildren<T>(this, re); } -#endif + { + QList<T> list; + union { + QList<T> *typedList; + QList<void *> *voidList; + } u; + u.typedList = &list; + qt_qFindChildren_helper(this, QString(), &re, reinterpret_cast<T>(0)->staticMetaObject, u.voidList); + return list; + } #endif #ifdef QT3_SUPPORT @@ -207,6 +213,21 @@ public: #endif #endif ); + + static bool connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type = +#ifdef qdoc + Qt::AutoConnection +#else +#ifdef QT3_SUPPORT + Qt::AutoCompatConnection +#else + Qt::AutoConnection +#endif +#endif + ); + inline bool connect(const QObject *sender, const char *signal, const char *member, Qt::ConnectionType type = #ifdef qdoc @@ -222,6 +243,8 @@ public: static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member); + static bool disconnect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &member); inline bool disconnect(const char *signal = 0, const QObject *receiver = 0, const char *member = 0) { return disconnect(this, signal, receiver, member); } @@ -257,6 +280,7 @@ public Q_SLOTS: protected: QObject *sender() const; + int senderSignalIndex() const; int receivers(const char* signal) const; virtual void timerEvent(QTimerEvent *); @@ -321,62 +345,43 @@ public: }; #endif -Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QString &name, const QRegExp *re, - const QMetaObject &mo, QList<void *> *list); -Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo); - +#ifdef QT_DEPRECATED template<typename T> -inline T qFindChild(const QObject *o, const QString &name) -{ return static_cast<T>(qt_qFindChild_helper(o, name, reinterpret_cast<T>(0)->staticMetaObject)); } +inline QT_DEPRECATED T qFindChild(const QObject *o, const QString &name = QString()) +{ return o->findChild<T>(name); } template<typename T> -inline QList<T> qFindChildren(const QObject *o, const QString &name) +inline QT_DEPRECATED QList<T> qFindChildren(const QObject *o, const QString &name = QString()) { - QList<T> list; - union { - QList<T> *typedList; - QList<void *> *voidList; - } u; - u.typedList = &list; - qt_qFindChildren_helper(o, name, 0, reinterpret_cast<T>(0)->staticMetaObject, u.voidList); - return list; + return o->findChildren<T>(name); } #ifndef QT_NO_REGEXP template<typename T> -inline QList<T> qFindChildren(const QObject *o, const QRegExp &re) +inline QT_DEPRECATED QList<T> qFindChildren(const QObject *o, const QRegExp &re) { - QList<T> list; - union { - QList<T> *typedList; - QList<void *> *voidList; - } u; - u.typedList = &list; - qt_qFindChildren_helper(o, QString(), &re, reinterpret_cast<T>(0)->staticMetaObject, u.voidList); - return list; + return o->findChildren<T>(re); } #endif +#endif //QT_DEPRECATED + template <class T> inline T qobject_cast(QObject *object) { -#if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(QT_NO_QOBJECT_CHECK) - reinterpret_cast<T>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(object)); +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast<T>(object)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(object)); #endif - return static_cast<T>(reinterpret_cast<T>(0)->staticMetaObject.cast(object)); + return static_cast<T>(reinterpret_cast<T>(object)->staticMetaObject.cast(object)); } template <class T> inline T qobject_cast(const QObject *object) { - // this will cause a compilation error if T is not const - register T ptr = static_cast<T>(object); - Q_UNUSED(ptr); - -#if !defined(QT_NO_MEMBER_TEMPLATES) && !defined(QT_NO_QOBJECT_CHECK) - reinterpret_cast<T>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(const_cast<QObject *>(object))); +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast<T>(object)->qt_check_for_QOBJECT_macro(*reinterpret_cast<T>(const_cast<QObject *>(object))); #endif - return static_cast<T>(const_cast<QObject *>(reinterpret_cast<T>(0)->staticMetaObject.cast(const_cast<QObject *>(object)))); + return static_cast<T>(reinterpret_cast<T>(object)->staticMetaObject.cast(object)); } diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 4fcaef3..c7555be 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -55,6 +55,7 @@ #include "QtCore/qobject.h" #include "QtCore/qpointer.h" +#include "QtCore/qsharedpointer.h" #include "QtCore/qcoreevent.h" #include "QtCore/qlist.h" #include "QtCore/qvector.h" @@ -154,7 +155,6 @@ public: #ifdef QT3_SUPPORT void sendPendingChildInsertedEvents(); - void removePendingChildInsertedEvents(QObject *child); #endif static inline Sender *setCurrentSender(QObject *receiver, @@ -162,8 +162,10 @@ public: static inline void resetCurrentSender(QObject *receiver, Sender *currentSender, Sender *previousSender); +#ifdef QT_JAMBI_BUILD static int *setDeleteWatch(QObjectPrivate *d, int *newWatch); static void resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch); +#endif static void clearGuards(QObject *); static QObjectPrivate *get(QObject *o) { @@ -185,7 +187,7 @@ public: mutable quint32 connectedSignals[2]; #ifdef QT3_SUPPORT - QList<QObject *> pendingChildInsertedEvents; + QVector< QWeakPointer<QObject> > pendingChildInsertedEvents; #else // preserve binary compatibility with code compiled without Qt 3 support // keeping the binary layout stable helps the Qt Creator debugger @@ -201,7 +203,9 @@ public: // these objects are all used to indicate that a QObject was deleted // plus QPointer, which keeps a separate list QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount; +#ifdef QT_JAMBI_BUILD int *deleteWatch; +#endif }; diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index b727187..54b5ab2 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -118,7 +118,7 @@ class QString; # define QT_TR_FUNCTIONS #endif -#if defined(QT_NO_MEMBER_TEMPLATES) || defined(QT_NO_QOBJECT_CHECK) +#if defined(QT_NO_QOBJECT_CHECK) /* tmake ignore Q_OBJECT */ #define Q_OBJECT_CHECK #else @@ -145,7 +145,7 @@ inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; } template <typename T1, typename T2> inline void qYouForgotTheQ_OBJECT_Macro(T1, T2) {} -#endif // QT_NO_MEMBER_TEMPLATES +#endif // QT_NO_QOBJECT_CHECK #ifdef Q_NO_DATA_RELOCATION #define Q_OBJECT_GETSTATICMETAOBJECT static const QMetaObject &getStaticMetaObject(); @@ -217,11 +217,15 @@ Q_CORE_EXPORT const char *qFlagLocation(const char *method); #define QTOSTRING(s) QTOSTRING_HELPER(s) #ifndef QT_NO_DEBUG # define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__) -# define METHOD(a) qFlagLocation("0"#a QLOCATION) +# ifndef QT_NO_KEYWORDS +# define METHOD(a) qFlagLocation("0"#a QLOCATION) +# endif # define SLOT(a) qFlagLocation("1"#a QLOCATION) # define SIGNAL(a) qFlagLocation("2"#a QLOCATION) #else -# define METHOD(a) "0"#a +# ifndef QT_NO_KEYWORDS +# define METHOD(a) "0"#a +# endif # define SLOT(a) "1"#a # define SIGNAL(a) "2"#a #endif @@ -300,6 +304,7 @@ struct Q_CORE_EXPORT QMetaObject const QMetaObject *superClass() const; QObject *cast(QObject *obj) const; + const QObject *cast(const QObject *obj) const; #ifndef QT_NO_TRANSLATION // ### Qt 4: Merge overloads diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index 85b37d0..78715a1 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -73,14 +73,13 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, QString part1 = key; part1.replace(QRegExp(QLatin1String("[^A-Za-z]")), QString()); result.append(part1); -#ifdef Q_OS_SYMBIAN - return result.left(KMaxKernelName); -#endif QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex(); result.append(QLatin1String(hex)); #ifdef Q_OS_WIN return result; +#elif defined(Q_OS_SYMBIAN) + return result.left(KMaxKernelName); #else return QDir::tempPath() + QLatin1Char('/') + result; #endif @@ -142,9 +141,12 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, remain. Do not mix using QtSharedMemory and QSharedMemory. Port everything to QSharedMemory. - \warning QSharedMemory changes the key in a Qt-specific way. - It is therefore currently not possible to use the shared memory of - non-Qt applications with QSharedMemory. + \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise + specified. Interoperation with non-Qt applications is achieved by first creating + a default shared memory with QSharedMemory() and then setting a native key with + setNativeKey(). When using native keys, shared memory is not protected against + multiple accesses on it (e.g. unable to lock()) and a user-defined mechanism + should be used to achieve a such protection. */ /*! @@ -153,8 +155,8 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, Constructs a shared memory object with the given \a parent. The shared memory object's key is not set by the constructor, so the shared memory object does not have an underlying shared memory - segment attached. The key must be set with setKey() before create() - or attach() can be used. + segment attached. The key must be set with setKey() or setNativeKey() + before create() or attach() can be used. \sa setKey() */ @@ -191,24 +193,62 @@ QSharedMemory::~QSharedMemory() } /*! - Sets a new \a key for this shared memory object. If \a key and the - current key are the same, the function returns without doing - anything. If the shared memory object is attached to an underlying - shared memory segment, it will \l {detach()} {detach} from it before - setting the new key. This function does not do an attach(). + Sets the platform independent \a key for this shared memory object. If \a key + is the same as the current key, the function returns without doing anything. - \sa key() isAttached() - */ + You can call key() to retrieve the platform independent key. Internally, + QSharedMemory converts this key into a platform specific key. If you instead + call nativeKey(), you will get the platform specific, converted key. + + If the shared memory object is attached to an underlying shared memory + segment, it will \l {detach()} {detach} from it before setting the new key. + This function does not do an attach(). + + \sa key() nativeKey() isAttached() +*/ void QSharedMemory::setKey(const QString &key) { Q_D(QSharedMemory); - if (key == d->key) + if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey) return; if (isAttached()) detach(); d->cleanHandle(); d->key = key; + d->nativeKey = d->makePlatformSafeKey(key); +} + +/*! + \since 4.8 + + Sets the native, platform specific, \a key for this shared memory object. If + \a key is the same as the current native key, the function returns without + doing anything. If all you want is to assign a key to a segment, you should + call setKey() instead. + + You can call nativeKey() to retrieve the native key. If a native key has been + assigned, calling key() will return a null string. + + If the shared memory object is attached to an underlying shared memory + segment, it will \l {detach()} {detach} from it before setting the new key. + This function does not do an attach(). + + The application will not be portable if you set a native key. + + \sa nativeKey() key() isAttached() +*/ +void QSharedMemory::setNativeKey(const QString &key) +{ + Q_D(QSharedMemory); + if (key == d->nativeKey && d->key.isNull()) + return; + + if (isAttached()) + detach(); + d->cleanHandle(); + d->key = QString(); + d->nativeKey = key; } bool QSharedMemoryPrivate::initKey() @@ -251,13 +291,15 @@ bool QSharedMemoryPrivate::initKey() } /*! - Returns the key assigned to this shared memory. The key is the - identifier used by the operating system to identify the shared - memory segment. When QSharedMemory is used for interprocess - communication, the key is how each process attaches to the shared - memory segment through which the IPC occurs. + Returns the key assigned with setKey() to this shared memory, or a null key + if no key has been assigned, or if the segment is using a nativeKey(). The + key is the identifier used by Qt applications to identify the shared memory + segment. - \sa setKey() + You can find the native, platform specific, key used by the operating system + by calling nativeKey(). + + \sa setKey() setNativeKey() */ QString QSharedMemory::key() const { @@ -266,13 +308,30 @@ QString QSharedMemory::key() const } /*! - Creates a shared memory segment of \a size bytes with the key passed - to the constructor or set with setKey(), attaches to the new shared - memory segment with the given access \a mode, and returns \tt true. - If a shared memory segment identified by the key already exists, the - attach operation is not performed, and \tt false is returned. When - the return value is \tt false, call error() to determine which error - occurred. + \since 4.8 + + Returns the native, platform specific, key for this shared memory object. The + native key is the identifier used by the operating system to identify the + shared memory segment. + + You can use the native key to access shared memory segments that have not + been created by Qt, or to grant shared memory access to non-Qt applications. + + \sa setKey() setNativeKey() +*/ +QString QSharedMemory::nativeKey() const +{ + Q_D(const QSharedMemory); + return d->nativeKey; +} + +/*! + Creates a shared memory segment of \a size bytes with the key passed to the + constructor, set with setKey() or set with setNativeKey(), then attaches to + the new shared memory segment with the given access \a mode and returns + \tt true. If a shared memory segment identified by the key already exists, + the attach operation is not performed and \tt false is returned. When the + return value is \tt false, call error() to determine which error occurred. \sa error() */ @@ -294,7 +353,7 @@ bool QSharedMemory::create(int size, AccessMode mode) QString function = QLatin1String("QSharedMemory::create"); #ifndef QT_NO_SYSTEMSEMAPHORE QSharedMemoryLocker lock(this); - if (!d->tryLocker(&lock, function)) + if (!d->key.isNull() && !d->tryLocker(&lock, function)) return false; #endif @@ -338,7 +397,7 @@ int QSharedMemory::size() const /*! Attempts to attach the process to the shared memory segment identified by the key that was passed to the constructor or to a - call to setKey(). The access \a mode is \l {QSharedMemory::} + call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::} {ReadWrite} by default. It can also be \l {QSharedMemory::} {ReadOnly}. Returns true if the attach operation is successful. If false is returned, call error() to determine which error occurred. @@ -355,7 +414,7 @@ bool QSharedMemory::attach(AccessMode mode) return false; #ifndef QT_NO_SYSTEMSEMAPHORE QSharedMemoryLocker lock(this); - if (!d->tryLocker(&lock, QLatin1String("QSharedMemory::attach"))) + if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::attach"))) return false; #endif @@ -395,7 +454,7 @@ bool QSharedMemory::detach() #ifndef QT_NO_SYSTEMSEMAPHORE QSharedMemoryLocker lock(this); - if (!d->tryLocker(&lock, QLatin1String("QSharedMemory::detach"))) + if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::detach"))) return false; #endif @@ -447,9 +506,9 @@ const void *QSharedMemory::data() const by this process and returns true. If another process has locked the segment, this function blocks until the lock is released. Then it acquires the lock and returns true. If this function returns false, - it means either that you have ignored a false return from create() - or attach(), or that QSystemSemaphore::acquire() failed due to an - unknown system error. + it means that you have ignored a false return from create() or attach(), + that you have set the key with setNativeKey() or that + QSystemSemaphore::acquire() failed due to an unknown system error. \sa unlock(), data(), QSystemSemaphore::acquire() */ diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h index f6f3623..4a61003 100644 --- a/src/corelib/kernel/qsharedmemory.h +++ b/src/corelib/kernel/qsharedmemory.h @@ -85,6 +85,8 @@ public: void setKey(const QString &key); QString key() const; + void setNativeKey(const QString &key); + QString nativeKey() const; bool create(int size, AccessMode mode = ReadWrite); int size() const; diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h index d40fd38..72eda37 100644 --- a/src/corelib/kernel/qsharedmemory_p.h +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -122,6 +122,7 @@ public: void *memory; int size; QString key; + QString nativeKey; QSharedMemory::SharedMemoryError error; QString errorString; #ifndef QT_NO_SYSTEMSEMAPHORE diff --git a/src/corelib/kernel/qsharedmemory_symbian.cpp b/src/corelib/kernel/qsharedmemory_symbian.cpp index dbcebc0..3565e26 100644 --- a/src/corelib/kernel/qsharedmemory_symbian.cpp +++ b/src/corelib/kernel/qsharedmemory_symbian.cpp @@ -107,16 +107,14 @@ bool QSharedMemoryPrivate::cleanHandle() bool QSharedMemoryPrivate::create(int size) { - // Get a windows acceptable key - QString safeKey = makePlatformSafeKey(key); QString function = QLatin1String("QSharedMemory::create"); - if (safeKey.isEmpty()) { + if (nativeKey.isEmpty()) { error = QSharedMemory::KeyError; errorString = QSharedMemory::tr("%1: key error").arg(function); return false; } - TPtrC ptr(qt_QString2TPtrC(safeKey)); + TPtrC ptr(qt_QString2TPtrC(nativeKey)); TInt err = chunk.CreateGlobal(ptr, size, size); @@ -136,14 +134,13 @@ bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode /* mode */) // Grab a pointer to the memory block if (!chunk.Handle()) { QString function = QLatin1String("QSharedMemory::handle"); - QString safeKey = makePlatformSafeKey(key); - if (safeKey.isEmpty()) { + if (nativeKey.isEmpty()) { error = QSharedMemory::KeyError; errorString = QSharedMemory::tr("%1: unable to make key").arg(function); return false; } - TPtrC ptr(qt_QString2TPtrC(safeKey)); + TPtrC ptr(qt_QString2TPtrC(nativeKey)); TInt err = KErrNoMemory; diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp index ddb0e34..b30117f 100644 --- a/src/corelib/kernel/qsharedmemory_unix.cpp +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -116,21 +116,20 @@ key_t QSharedMemoryPrivate::handle() return unix_key; // don't allow making handles on empty keys - if (key.isEmpty()) { + if (nativeKey.isEmpty()) { errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:")); error = QSharedMemory::KeyError; return 0; } // ftok requires that an actual file exists somewhere - QString fileName = makePlatformSafeKey(key); - if (!QFile::exists(fileName)) { + if (!QFile::exists(nativeKey)) { errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:")); error = QSharedMemory::NotFound; return 0; } - unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q'); + unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q'); if (-1 == unix_key) { errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:")); error = QSharedMemory::KeyError; @@ -181,7 +180,7 @@ bool QSharedMemoryPrivate::create(int size) { // build file if needed bool createdFile = false; - int built = createUnixKeyFile(makePlatformSafeKey(key)); + int built = createUnixKeyFile(nativeKey); if (built == -1) { errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:")); error = QSharedMemory::KeyError; @@ -194,7 +193,7 @@ bool QSharedMemoryPrivate::create(int size) // get handle if (!handle()) { if (createdFile) - QFile::remove(makePlatformSafeKey(key)); + QFile::remove(nativeKey); return false; } @@ -210,7 +209,7 @@ bool QSharedMemoryPrivate::create(int size) setErrorString(function); } if (createdFile && error != QSharedMemory::AlreadyExists) - QFile::remove(makePlatformSafeKey(key)); + QFile::remove(nativeKey); return false; } @@ -291,7 +290,7 @@ bool QSharedMemoryPrivate::detach() } // remove file - if (!QFile::remove(makePlatformSafeKey(key))) + if (!QFile::remove(nativeKey)) return false; } return true; diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp index ac9c86a..bc13e33 100644 --- a/src/corelib/kernel/qsharedmemory_win.cpp +++ b/src/corelib/kernel/qsharedmemory_win.cpp @@ -99,18 +99,17 @@ HANDLE QSharedMemoryPrivate::handle() { if (!hand) { QString function = QLatin1String("QSharedMemory::handle"); - QString safeKey = makePlatformSafeKey(key); - if (safeKey.isEmpty()) { + if (nativeKey.isEmpty()) { error = QSharedMemory::KeyError; errorString = QSharedMemory::tr("%1: unable to make key").arg(function); return false; } #ifndef Q_OS_WINCE - hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, (wchar_t*)safeKey.utf16()); + hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, (wchar_t*)nativeKey.utf16()); #else // This works for opening a mapping too, but always opens it with read/write access in // attach as it seems. - hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 0, (wchar_t*)safeKey.utf16()); + hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 0, (wchar_t*)nativeKey.utf16()); #endif if (!hand) { setErrorString(function); @@ -133,17 +132,15 @@ bool QSharedMemoryPrivate::cleanHandle() bool QSharedMemoryPrivate::create(int size) { - // Get a windows acceptable key - QString safeKey = makePlatformSafeKey(key); QString function = QLatin1String("QSharedMemory::create"); - if (safeKey.isEmpty()) { + if (nativeKey.isEmpty()) { error = QSharedMemory::KeyError; errorString = QSharedMemory::tr("%1: key error").arg(function); return false; } // Create the file mapping. - hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size, (wchar_t*)safeKey.utf16()); + hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size, (wchar_t*)nativeKey.utf16()); setErrorString(function); // hand is valid when it already exists unlike unix so explicitly check diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp new file mode 100644 index 0000000..3381afa --- /dev/null +++ b/src/corelib/kernel/qsystemerror.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qglobal.h> +#include "qsystemerror_p.h" +#if !defined(Q_OS_WINCE) +# include <errno.h> +# if defined(Q_CC_MSVC) +# include <crtdbg.h> +# endif +#endif +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN) && !defined(QT_NO_THREAD) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) && \ + defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L +namespace { + // There are two incompatible versions of strerror_r: + // a) the XSI/POSIX.1 version, which returns an int, + // indicating success or not + // b) the GNU version, which returns a char*, which may or may not + // be the beginning of the buffer we used + // The GNU libc manpage for strerror_r says you should use the the XSI + // version in portable code. However, it's impossible to do that if + // _GNU_SOURCE is defined so we use C++ overloading to decide what to do + // depending on the return type + static inline QString fromstrerror_helper(int, const QByteArray &buf) + { + return QString::fromLocal8Bit(buf); + } + static inline QString fromstrerror_helper(const char *str, const QByteArray &) + { + return QString::fromLocal8Bit(str); + } +} +#endif + +static QString standardLibraryErrorString(int errorCode) +{ + const char *s = 0; + QString ret; + switch (errorCode) { + case 0: + break; + case EACCES: + s = QT_TRANSLATE_NOOP("QIODevice", "Permission denied"); + break; + case EMFILE: + s = QT_TRANSLATE_NOOP("QIODevice", "Too many open files"); + break; + case ENOENT: + s = QT_TRANSLATE_NOOP("QIODevice", "No such file or directory"); + break; + case ENOSPC: + s = QT_TRANSLATE_NOOP("QIODevice", "No space left on device"); + break; + default: { + #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) + QByteArray buf(1024, '\0'); + ret = fromstrerror_helper(strerror_r(errorCode, buf.data(), buf.size()), buf); + #else + ret = QString::fromLocal8Bit(strerror(errorCode)); + #endif + break; } + } + if (s) { + // ######## this breaks moc build currently + // ret = QCoreApplication::translate("QIODevice", s); + ret = QString::fromLatin1(s); + } + return ret.trimmed(); +} + +#ifdef Q_OS_WIN +static QString windowsErrorString(int errorCode) +{ + QString ret; + wchar_t *string = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&string, + 0, + NULL); + ret = QString::fromWCharArray(string); + LocalFree((HLOCAL)string); + + if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND) + ret = QString::fromLatin1("The specified module could not be found."); + return ret; +} +#endif + +#ifdef Q_OS_SYMBIAN +static QString symbianErrorString(int errorCode) +{ + switch (errorCode) { + case KErrNotFound: + return QLatin1String("not found"); + case KErrCancel: + return QLatin1String("cancelled"); + case KErrNoMemory: + return QLatin1String("out of memory"); + case KErrNotSupported: + return QLatin1String("not supported"); + case KErrBadHandle: + return QLatin1String("bad handle"); //KERN-EXEC 0 panic is more likely + case KErrAlreadyExists: + return QLatin1String("already exists"); + case KErrPathNotFound: + return QLatin1String("path not found"); + case KErrInUse: + return QLatin1String("in use"); + case KErrNotReady: + return QLatin1String("not ready (e.g. FS dismounted, no memory card)"); + case KErrCorrupt: + return QLatin1String("corrupt"); + case KErrAccessDenied: + return QLatin1String("access denied"); + case KErrLocked: + return QLatin1String("locked"); + case KErrWrite: + return QLatin1String("incomplete write error"); + case KErrDisMounted: + return QLatin1String("file system dismounted during operation"); //i.e. a forcible dismount was done while we had files open + case KErrEof: + return QLatin1String("end of file"); + case KErrDiskFull: + return QLatin1String("no space in file system"); + case KErrBadName: + return QLatin1String("invalid filename"); + case KErrTimedOut: + return QLatin1String("timed out"); + case KErrBadDescriptor: + return QLatin1String("bad descriptor (passed address on stack to async call?)"); + case KErrAbort: + return QLatin1String("aborted"); + case KErrTooBig: + return QLatin1String("too big"); //e.g. trying to open a >2GB file with 32 bit API + case KErrBadPower: + return QLatin1String("insufficient power"); + case KErrDirFull: + return QLatin1String("no space in directory table"); + case KErrHardwareNotAvailable: + return QLatin1String("hardware not available"); + case KErrSessionClosed: + return QLatin1String("session closed"); + case KErrPermissionDenied: + return QLatin1String("permission denied"); + default: + return QString(QLatin1String("symbian error %d")).arg(errorCode); + } +} +#endif + +QString QSystemError::toString() +{ + switch(errorScope) { + case NativeError: +#if defined (Q_OS_WIN) + return windowsErrorString(errorCode); +#elif defined (Q_OS_SYMBIAN) + return symbianErrorString(errorCode); +#else + //unix: fall through as native and standard library are the same +#endif + case StandardLibraryError: + return standardLibraryErrorString(errorCode); + default: + qWarning("invalid error scope"); + //fall through + case NoError: + return QLatin1String("No error"); + } +} + +QT_END_NAMESPACE + diff --git a/src/corelib/kernel/qsystemerror_p.h b/src/corelib/kernel/qsystemerror_p.h new file mode 100644 index 0000000..e96e85a --- /dev/null +++ b/src/corelib/kernel/qsystemerror_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSYSTEMERROR_P_H +#define QSYSTEMERROR_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 <qstring.h> + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QSystemError +{ +public: + enum ErrorScope + { + NoError, + StandardLibraryError, + NativeError + }; + + inline QSystemError(int error, ErrorScope scope); + inline QSystemError(); + + QString toString(); + inline ErrorScope scope(); + inline int error(); + + //data members + int errorCode; + ErrorScope errorScope; +}; + +QSystemError::QSystemError(int error, QSystemError::ErrorScope scope) +: errorCode(error), errorScope(scope) +{ + +} + +QSystemError::QSystemError() +: errorCode(0), errorScope(NoError) +{ + +} + +QSystemError::ErrorScope QSystemError::scope() +{ + return errorScope; +} + +int QSystemError::error() +{ + return errorCode; +} + + +QT_END_NAMESPACE + +#endif // QSYSTEMERROR_P_H diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp index 9a58d32..a68abd3 100644 --- a/src/corelib/kernel/qsystemsemaphore_unix.cpp +++ b/src/corelib/kernel/qsystemsemaphore_unix.cpp @@ -50,11 +50,11 @@ #include <sys/types.h> #include <sys/ipc.h> +#include <sys/sem.h> #include <fcntl.h> #include <errno.h> -#include <sys/shm.h> -#include <sys/sem.h> +#include "private/qcore_unix_p.h" // OpenBSD 4.2 doesn't define EIDRM, see BUGS section: // http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 @@ -218,7 +218,10 @@ bool QSystemSemaphorePrivate::modifySemaphore(int count) operation.sem_num = 0; operation.sem_op = count; operation.sem_flg = SEM_UNDO; - if (-1 == semop(semaphore, &operation, 1)) { + + register int res; + EINTR_LOOP(res, semop(semaphore, &operation, 1)); + if (-1 == res) { // If the semaphore was removed be nice and create it and then modifySemaphore again if (errno == EINVAL || errno == EIDRM) { semaphore = -1; diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 73a32c4..3570564 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -56,7 +56,7 @@ #include "qhash.h" #include "qtranslator_p.h" -#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_INTEGRITY) #define QT_USE_MMAP #include "private/qcore_unix_p.h" #endif diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 3741ae1..f065238 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -947,11 +947,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, case QMetaType::UChar: case QMetaType::UShort: case QMetaType::ULong: -#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) - *f = (double)(qlonglong)qMetaTypeUNumber(d); -#else *f = double(qMetaTypeUNumber(d)); -#endif break; default: *f = 0.0; @@ -986,11 +982,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, case QMetaType::UChar: case QMetaType::UShort: case QMetaType::ULong: -#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) - *f = (float)(qlonglong)qMetaTypeUNumber(d); -#else *f = float(qMetaTypeUNumber(d)); -#endif break; default: *f = 0.0f; @@ -1084,7 +1076,7 @@ static void streamDebug(QDebug dbg, const QVariant &v) dbg.nospace() << v.toFloat(); break; case QMetaType::QObjectStar: - dbg.nospace() << qVariantValue<QObject *>(v); + dbg.nospace() << qvariant_cast<QObject *>(v); break; case QVariant::Double: dbg.nospace() << v.toDouble(); @@ -1245,7 +1237,7 @@ const QVariant::Handler *QVariant::handler = &qt_kernel_variant_handler; conversion functions to data types defined in QtGui, such as QColor, QImage, and QPixmap. In other words, there is no \c toColor() function. Instead, you can use the QVariant::value() or - the qVariantValue() template function. For example: + the qvariant_cast() template function. For example: \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 2 @@ -1360,12 +1352,12 @@ const QVariant::Handler *QVariant::handler = &qt_kernel_variant_handler; Note that you have to pass the address of the variable you want stored. - Usually, you never have to use this constructor, use qVariantFromValue() + Usually, you never have to use this constructor, use QVariant::fromValue() instead to construct variants from the pointer types represented by \c QMetaType::VoidStar, \c QMetaType::QObjectStar and \c QMetaType::QWidgetStar. - \sa qVariantFromValue(), Type + \sa QVariant::fromValue(), Type */ /*! @@ -1816,6 +1808,14 @@ QVariant& QVariant::operator=(const QVariant &variant) } /*! + \fn void QVariant::swap(QVariant &other) + \since 4.8 + + Swaps variant \a other with this variant. This operation is very + fast and never fails. +*/ + +/*! \fn void QVariant::detach() \internal @@ -3074,10 +3074,6 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 4 - \warning This function is not available with MSVC 6. Use - qVariantSetValue() instead if you need to support that version of - the compiler. - \sa value(), fromValue(), canConvert() */ @@ -3095,10 +3091,6 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 5 - \warning This function is not available with MSVC 6. Use - qVariantValue() or qvariant_cast() instead if you need to support - that version of the compiler. - \sa setValue(), fromValue(), canConvert() */ @@ -3111,10 +3103,6 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 6 - \warning This function is not available with MSVC 6. Use - qVariantCanConvert() instead if you need to support that version - of the compiler. - \sa convert() */ @@ -3130,23 +3118,22 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) \note If you are working with custom types, you should use the Q_DECLARE_METATYPE() macro to register your custom type. - \warning This function is not available with MSVC 6. Use - qVariantFromValue() instead if you need to support that version - of the compiler. - \sa setValue(), value() */ /*! \fn QVariant qVariantFromValue(const T &value) \relates QVariant + \obsolete Returns a variant containing a copy of the given \a value with template type \c{T}. - This function is equivalent to QVariant::fromValue(\a value). It - is provided as a work-around for MSVC 6, which doesn't support - member template functions. + This function is equivalent to QVariant::fromValue(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. For example, a QObject pointer can be stored in a variant with the following code: @@ -3158,13 +3145,16 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) /*! \fn void qVariantSetValue(QVariant &variant, const T &value) \relates QVariant + \obsolete Sets the contents of the given \a variant to a copy of the \a value with the specified template type \c{T}. - This function is equivalent to QVariant::setValue(\a value). It - is provided as a work-around for MSVC 6, which doesn't support - member template functions. + This function is equivalent to QVariant::setValue(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QVariant::setValue() */ @@ -3175,33 +3165,39 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p) Returns the given \a value converted to the template type \c{T}. - This function is equivalent to qVariantValue(). + This function is equivalent to QVariant::value(). - \sa qVariantValue(), QVariant::value() + \sa QVariant::value() */ /*! \fn T qVariantValue(const QVariant &value) \relates QVariant + \obsolete Returns the given \a value converted to the template type \c{T}. This function is equivalent to - \l{QVariant::value()}{QVariant::value}<T>(\a value). It is - provided as a work-around for MSVC 6, which doesn't support - member template functions. + \l{QVariant::value()}{QVariant::value}<T>(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QVariant::value(), qvariant_cast() */ /*! \fn bool qVariantCanConvert(const QVariant &value) \relates QVariant + \obsolete Returns true if the given \a value can be converted to the template type specified; otherwise returns false. - This function is equivalent to QVariant::canConvert(\a value). It - is provided as a work-around for MSVC 6, which doesn't support - member template functions. + This function is equivalent to QVariant::canConvert(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. \sa QVariant::canConvert() */ diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index d9bc49a..1b8cb7f 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -83,19 +83,11 @@ class QUrl; class QVariant; class QVariantComparisonHelper; -#ifndef QT_NO_MEMBER_TEMPLATES template <typename T> inline QVariant qVariantFromValue(const T &); -template <typename T> -inline void qVariantSetValue(QVariant &, const T &); - template<typename T> -inline T qVariantValue(const QVariant &); - -template<typename T> -inline bool qVariantCanConvert(const QVariant &); -#endif +inline T qvariant_cast(const QVariant &); class Q_CORE_EXPORT QVariant { @@ -227,6 +219,12 @@ class Q_CORE_EXPORT QVariant QVariant(Qt::GlobalColor color); QVariant& operator=(const QVariant &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QVariant &operator=(QVariant &&other) + { qSwap(d, other.d); return *this; } +#endif + + inline void swap(QVariant &other) { qSwap(d, other.d); } Type type() const; int userType() const; @@ -327,13 +325,12 @@ class Q_CORE_EXPORT QVariant const void *constData() const; inline const void *data() const { return constData(); } -#ifndef QT_NO_MEMBER_TEMPLATES template<typename T> inline void setValue(const T &value); template<typename T> inline T value() const - { return qVariantValue<T>(*this); } + { return qvariant_cast<T>(*this); } template<typename T> static inline QVariant fromValue(const T &value) @@ -341,8 +338,7 @@ class Q_CORE_EXPORT QVariant template<typename T> bool canConvert() const - { return qVariantCanConvert<T>(*this); } -#endif + { return canConvert(Type(qMetaTypeId<T>())); } public: #ifndef qdoc @@ -527,11 +523,9 @@ inline QSize &QVariant::asSize() { return *reinterpret_cast<QSize *>(castOrDetach(Size)); } #endif //QT3_SUPPORT -#ifndef QT_NO_MEMBER_TEMPLATES template<typename T> inline void QVariant::setValue(const T &avalue) { qVariantSetValue(*this, avalue); } -#endif #ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream& operator>> (QDataStream& s, QVariant& p); @@ -594,16 +588,16 @@ template<> inline QVariant qvariant_cast<QVariant>(const QVariant &v) return v; } +#ifdef QT_DEPRECATED template<typename T> -inline T qVariantValue(const QVariant &variant) +inline QT_DEPRECATED T qVariantValue(const QVariant &variant) { return qvariant_cast<T>(variant); } template<typename T> -inline bool qVariantCanConvert(const QVariant &variant) -{ - return variant.canConvert(static_cast<QVariant::Type>( - qMetaTypeId<T>(static_cast<T *>(0)))); -} +inline QT_DEPRECATED bool qVariantCanConvert(const QVariant &variant) +{ return variant.template canConvert<T>(); } +#endif + #endif Q_DECLARE_SHARED(QVariant) Q_DECLARE_TYPEINFO(QVariant, Q_MOVABLE_TYPE); diff --git a/src/corelib/plugin/plugin.pri b/src/corelib/plugin/plugin.pri index ba86353..eb7a7f7 100644 --- a/src/corelib/plugin/plugin.pri +++ b/src/corelib/plugin/plugin.pri @@ -8,13 +8,15 @@ HEADERS += \ plugin/qplugin.h \ plugin/quuid.h \ plugin/qfactoryloader_p.h \ - plugin/qsystemlibrary_p.h + plugin/qsystemlibrary_p.h \ + plugin/qelfparser_p.h SOURCES += \ plugin/qpluginloader.cpp \ plugin/qfactoryloader.cpp \ plugin/quuid.cpp \ - plugin/qlibrary.cpp + plugin/qlibrary.cpp \ + plugin/qelfparser_p.cpp win32 { SOURCES += \ @@ -26,4 +28,8 @@ unix { SOURCES += plugin/qlibrary_unix.cpp } +integrity { + SOURCES += plugin/qlibrary_unix.cpp +} + LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp new file mode 100644 index 0000000..198c6d7 --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qelfparser_p.h" + +#ifndef QT_NO_LIBRARY +#if defined (Q_OF_ELF) && defined(Q_CC_GNU) + +#include "qlibrary_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +// #define QELFPARSER_DEBUG 1 + +const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh) +{ + sh->name = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_name + sh->type = read<qelfword_t>(data); + data += sizeof(qelfword_t) // sh_type + + sizeof(qelfaddr_t) // sh_flags + + sizeof(qelfaddr_t); // sh_addr + sh->offset = read<qelfoff_t>(data); + data += sizeof(qelfoff_t); // sh_offset + sh->size = read<qelfword_t>(data); + data += sizeof(qelfword_t); // sh_size + return data; +} + +int QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen) +{ +#if defined(QELFPARSER_DEBUG) + qDebug() << "QElfParser::parse " << library; +#endif + + if (fdlen < 64){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object (%2)").arg(library).arg(QLatin1String("file too small")); + return NotElf; + } + const char *data = dataStart; + if (qstrncmp(data, "\177ELF", 4) != 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is not an ELF object").arg(library); + return NotElf; + } + // 32 or 64 bit + if (data[4] != 1 && data[4] != 2) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd cpu architecture")); + return Corrupt; + } + m_bits = (data[4] << 5); + + /* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs + to match the _plugin_ architecture. + */ + if ((sizeof(void*) == 4 && m_bits != 32) || (sizeof(void*) == 8 && m_bits != 64)) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("wrong cpu architecture")); + return Corrupt; + } + // endian + if (data[5] == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("odd endianess")); + return Corrupt; + } + m_endian = (data[5] == 1 ? ElfLittleEndian : ElfBigEndian); + + data += 16 // e_ident + + sizeof(qelfhalf_t) // e_type + + sizeof(qelfhalf_t) // e_machine + + sizeof(qelfword_t) // e_version + + sizeof(qelfaddr_t) // e_entry + + sizeof(qelfoff_t); // e_phoff + + qelfoff_t e_shoff = read<qelfoff_t> (data); + data += sizeof(qelfoff_t) // e_shoff + + sizeof(qelfword_t); // e_flags + + qelfhalf_t e_shsize = read<qelfhalf_t> (data); + + if (e_shsize > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shsize")); + return Corrupt; + } + + data += sizeof(qelfhalf_t) // e_ehsize + + sizeof(qelfhalf_t) // e_phentsize + + sizeof(qelfhalf_t); // e_phnum + + qelfhalf_t e_shentsize = read<qelfhalf_t> (data); + + if (e_shentsize % 4){ + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library).arg(QLatin1String("unexpected e_shentsize")); + return Corrupt; + } + data += sizeof(qelfhalf_t); // e_shentsize + qelfhalf_t e_shnum = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shnum + qelfhalf_t e_shtrndx = read<qelfhalf_t> (data); + data += sizeof(qelfhalf_t); // e_shtrndx + + if ((quint32)(e_shnum * e_shentsize) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("announced %2 sections, each %3 bytes, exceed file size")) + .arg(e_shnum).arg(e_shentsize); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes"; +#endif + + ElfSectionHeader strtab; + qulonglong soff = e_shoff + e_shentsize * (e_shtrndx); + + if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("shstrtab section header seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + + parseSectionHeader(dataStart + soff, &strtab); + m_stringTableFileOffset = strtab.offset; + + if ((quint32)(m_stringTableFileOffset + e_shentsize) >= fdlen || m_stringTableFileOffset == 0) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("string table seems to be at %1")) + .arg(QString::number(soff, 16)); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data()); +#endif + + const char *s = dataStart + e_shoff; + for (int i = 0; i < e_shnum; ++i) { + ElfSectionHeader sh; + parseSectionHeader(s, &sh); + if (sh.name == 0) { + s += e_shentsize; + continue; + } + const char *shnam = dataStart + m_stringTableFileOffset + sh.name; + + if (m_stringTableFileOffset + sh.name > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("section name %2 of %3 behind end of file")) + .arg(i).arg(e_shnum); + return Corrupt; + } + +#if defined(QELFPARSER_DEBUG) + qDebug() << "++++" << i << shnam; +#endif + + if (qstrcmp(shnam, ".qtplugin") == 0 || qstrcmp(shnam, ".rodata") == 0) { + if (!(sh.type & 0x1)) { + if (shnam[1] == 'r') { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("empty .rodata. not a library.")); + return Corrupt; + } +#if defined(QELFPARSER_DEBUG) + qDebug()<<"section is not program data. skipped."; +#endif + s += e_shentsize; + continue; + } + + if (sh.offset == 0 || (sh.offset + sh.size) > fdlen) { + if (lib) + lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library) + .arg(QLatin1String("missing section data. This is not a library.")); + return Corrupt; + } + *pos = sh.offset; + *sectionlen = sh.size - 1; + if (shnam[1] == 'q') + return Ok; + } + s += e_shentsize; + } + return NoQtSection; +} + +QT_END_NAMESPACE + +#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) +#endif // QT_NO_LIBRARY diff --git a/src/corelib/plugin/qelfparser_p.h b/src/corelib/plugin/qelfparser_p.h new file mode 100644 index 0000000..3ccce10 --- /dev/null +++ b/src/corelib/plugin/qelfparser_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QELFPARSER_P_H +#define QELFPARSER_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 <qendian.h> +#include <qglobal.h> + +#ifndef QT_NO_LIBRARY +#if defined (Q_OF_ELF) && defined(Q_CC_GNU) + +QT_BEGIN_NAMESPACE + +class QString; +class QLibraryPrivate; + +typedef quint16 qelfhalf_t; +typedef quint32 qelfword_t; +typedef quintptr qelfoff_t; +typedef quintptr qelfaddr_t; + +class QElfParser +{ +public: + enum {Ok = 0, NotElf = 1, NoQtSection = 2, Corrupt = 3}; + enum {ElfLittleEndian = 0, ElfBigEndian = 1}; + + struct ElfSectionHeader + { + qelfword_t name; + qelfword_t type; + qelfoff_t offset; + qelfword_t size; + }; + + int m_endian; + int m_bits; + int m_stringTableFileOffset; + + template <typename T> + T read(const char *s) + { + if (m_endian == ElfBigEndian) + return qFromBigEndian<T>(reinterpret_cast<const uchar *>(s)); + else + return qFromLittleEndian<T>(reinterpret_cast<const uchar *>(s)); + } + + const char *parseSectionHeader(const char* s, ElfSectionHeader *sh); + int parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib, long *pos, ulong *sectionlen); +}; + +QT_END_NAMESPACE + +#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) +#endif // QT_NO_LIBRARY + +#endif // QELFPARSER_P_H diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 1be32ad..c0b947a 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -155,9 +155,11 @@ void QFactoryLoader::update() continue; } QObject *instance = library->instance(); - if (!instance) + if (!instance) { + library->release(); // ignore plugins that have a valid signature but cannot be loaded. continue; + } QFactoryInterface *factory = qobject_cast<QFactoryInterface*>(instance); if (instance && factory && instance->qt_metacast(d->iid)) keys = factory->keys(); diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index c6ed78a..80e927b 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -1,3 +1,4 @@ + /**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). @@ -38,7 +39,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qplatformdefs.h" #include "qlibrary.h" @@ -61,11 +61,10 @@ #include <qdebug.h> #include <qvector.h> #include <qdir.h> +#include "qelfparser_p.h" QT_BEGIN_NAMESPACE -//#define QT_DEBUG_COMPONENT - #ifdef QT_NO_DEBUG # define QLIBRARY_AS_DEBUG false #else @@ -365,11 +364,35 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB fdlen = data.size(); } - // verify that the pattern is present in the plugin + /* + ELF binaries on GNU, have .qplugin sections. + */ + long pos = 0; const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA"; const ulong plen = qstrlen(pattern); - long pos = qt_find_pattern(filedata, fdlen, pattern, plen); - +#if defined (Q_OF_ELF) && defined(Q_CC_GNU) + int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen); + if (r == QElfParser::NoQtSection) { + if (pos > 0) { + // find inside .rodata + long rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen); + if (rel < 0) { + pos = -1; + } else { + pos += rel; + } + } else { + pos = qt_find_pattern(filedata, fdlen, pattern, plen); + } + } else if (r != QElfParser::Ok) { + if (lib && qt_debug_component()) { + qWarning("QElfParser: %s",qPrintable(lib->errorString)); + } + return false; + } +#else + pos = qt_find_pattern(filedata, fdlen, pattern, plen); +#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU) bool ret = false; if (pos >= 0) ret = qt_parse_pattern(filedata + pos, version, debug, key); @@ -392,6 +415,7 @@ struct LibraryData { QSettings *settings; LibraryMap libraryMap; + QSet<QLibraryPrivate*> loadedLibs; }; Q_GLOBAL_STATIC(LibraryData, libraryData) @@ -443,7 +467,18 @@ bool QLibraryPrivate::load() return true; if (fileName.isEmpty()) return false; - return load_sys(); + + bool ret = load_sys(); + if (ret) { + //when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted + //this allows to unload the library at a later time + if (LibraryData *lib = libraryData()) { + lib->loadedLibs += this; + libraryRefCount.ref(); + } + } + + return ret; } bool QLibraryPrivate::unload() @@ -451,10 +486,16 @@ bool QLibraryPrivate::unload() if (!pHnd) return false; if (!libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to - if (instance) - delete instance(); + delete inst.data(); if (unload_sys()) { - instance = 0; + if (qt_debug_component()) + qWarning() << "QLibraryPrivate::unload succeeded on" << fileName; + //when the library is unloaded, we release the reference on it so that 'this' + //can get deleted + if (LibraryData *lib = libraryData()) { + if (lib->loadedLibs.remove(this)) + libraryRefCount.deref(); + } pHnd = 0; } } @@ -475,6 +516,8 @@ bool QLibraryPrivate::loadPlugin() libraryUnloadCount.ref(); return true; } + if (pluginState == IsNotAPlugin) + return false; if (load()) { instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); #if defined(Q_OS_SYMBIAN) @@ -487,6 +530,9 @@ bool QLibraryPrivate::loadPlugin() #endif return instance; } + if (qt_debug_component()) + qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString; + pluginState = IsNotAPlugin; return false; } @@ -496,7 +542,7 @@ bool QLibraryPrivate::loadPlugin() \table \header \i Platform \i Valid suffixes - \row \i Windows \i \c .dll + \row \i Windows \i \c .dll, \c .DLL \row \i Unix/Linux \i \c .so \row \i AIX \i \c .a \row \i HP-UX \i \c .sl, \c .so (HP-UXi) @@ -509,7 +555,7 @@ bool QLibraryPrivate::loadPlugin() bool QLibrary::isLibrary(const QString &fileName) { #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) - return fileName.endsWith(QLatin1String(".dll")); + return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive); #elif defined(Q_OS_SYMBIAN) // Plugin stubs are also considered libraries in Symbian. return (fileName.endsWith(QLatin1String(".dll")) || @@ -571,6 +617,46 @@ bool QLibrary::isLibrary(const QString &fileName) } +#if defined (Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_CC_INTEL) +#define QT_USE_MS_STD_EXCEPTION 1 +const char* qt_try_versioninfo(void *pfn, bool *exceptionThrown) +{ + *exceptionThrown = false; + const char *szData = 0; + typedef const char * (*VerificationFunction)(); + VerificationFunction func = reinterpret_cast<VerificationFunction>(pfn); + __try { + if(func) + szData = func(); + } __except(EXCEPTION_EXECUTE_HANDLER) { + *exceptionThrown = true; + } + return szData; +} +#endif + +#ifdef Q_CC_BOR +typedef const char * __stdcall (*QtPluginQueryVerificationDataFunction)(); +#else +typedef const char * (*QtPluginQueryVerificationDataFunction)(); +#endif + +bool qt_get_verificationdata(QtPluginQueryVerificationDataFunction pfn, uint *qt_version, bool *debug, QByteArray *key, bool *exceptionThrown) +{ + *exceptionThrown = false; + const char *szData = 0; + if (!pfn) + return false; +#ifdef QT_USE_MS_STD_EXCEPTION + szData = qt_try_versioninfo((void *)pfn, exceptionThrown); + if (*exceptionThrown) + return false; +#else + szData = pfn(); +#endif + return qt_parse_pattern(szData, qt_version, debug, key); +} + bool QLibraryPrivate::isPlugin(QSettings *settings) { errorString.clear(); @@ -606,7 +692,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) .arg((QT_VERSION & 0xff00) >> 8) .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false")) .arg(fileName); -#ifdef Q_WS_MAC +#ifdef Q_WS_MAC // On Mac, add the application arch to the reg key in order to // cache plugin information separately for each arch. This prevents // Qt from wrongly caching plugin load failures when the archs @@ -621,7 +707,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) regkey += QLatin1String("-ppc"); #endif #endif // Q_WS_MAC - + QStringList reg; #ifndef QT_NO_SETTINGS if (!settings) { @@ -646,70 +732,82 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) } else #endif { - bool temporary_load = false; + bool retryLoadLibrary = false; // Only used on Windows with MS compiler.(false in other cases) + do { + bool temporary_load = false; #ifdef Q_OS_WIN - HMODULE hTempModule = 0; + HMODULE hTempModule = 0; #endif - if (!pHnd) { + if (!pHnd) { #ifdef Q_OS_WIN - //avoid 'Bad Image' message box - UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); - hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, DONT_RESOLVE_DLL_REFERENCES); - SetErrorMode(oldmode); + DWORD dwFlags = (retryLoadLibrary) ? 0: DONT_RESOLVE_DLL_REFERENCES; + //avoid 'Bad Image' message box + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, dwFlags); + SetErrorMode(oldmode); #else # if defined(Q_OS_SYMBIAN) - //Guard against accidentally trying to load non-plugin libraries by making sure the stub exists - if (fileinfo.exists()) + //Guard against accidentally trying to load non-plugin libraries by making sure the stub exists + if (fileinfo.exists()) # endif - temporary_load = load_sys(); + temporary_load = load_sys(); #endif - } -# ifdef Q_CC_BOR - typedef const char * __stdcall (*QtPluginQueryVerificationDataFunction)(); -# else - typedef const char * (*QtPluginQueryVerificationDataFunction)(); -# endif + } #ifdef Q_OS_WIN - QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = hTempModule - ? (QtPluginQueryVerificationDataFunction) + QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = hTempModule ? (QtPluginQueryVerificationDataFunction) #ifdef Q_OS_WINCE - ::GetProcAddress(hTempModule, L"qt_plugin_query_verification_data") + ::GetProcAddress(hTempModule, L"qt_plugin_query_verification_data") #else - ::GetProcAddress(hTempModule, "qt_plugin_query_verification_data") + ::GetProcAddress(hTempModule, "qt_plugin_query_verification_data") #endif : (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); #else - QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = NULL; + QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = NULL; # if defined(Q_OS_SYMBIAN) - if (temporary_load) { - qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); - // If resolving with function name failed (i.e. not STDDLL), try resolving using known ordinal - if (!qtPluginQueryVerificationDataFunction) - qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("1"); - } + if (temporary_load) { + qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); + // If resolving with function name failed (i.e. not STDDLL), try resolving using known ordinal + if (!qtPluginQueryVerificationDataFunction) + qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("1"); + } # else - qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); + qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); # endif #endif - - if (!qtPluginQueryVerificationDataFunction - || !qt_parse_pattern(qtPluginQueryVerificationDataFunction(), &qt_version, &debug, &key)) { - qt_version = 0; - key = "unknown"; - if (temporary_load) - unload_sys(); - } else { - success = true; - } -#ifdef Q_OS_WIN - if (hTempModule) { - BOOL ok = ::FreeLibrary(hTempModule); - if (ok) { - hTempModule = 0; + bool exceptionThrown = false; + bool ret = qt_get_verificationdata(qtPluginQueryVerificationDataFunction, + &qt_version, &debug, &key, &exceptionThrown); + if (!exceptionThrown) { + if (!ret) { + qt_version = 0; + key = "unknown"; + if (temporary_load) + unload_sys(); + } else { + success = true; + } + retryLoadLibrary = false; + } +#ifdef QT_USE_MS_STD_EXCEPTION + else { + // An exception was thrown when calling qt_plugin_query_verification_data(). + // This usually happens when plugin is compiled with the /clr compiler flag, + // & will only work if the dependencies are loaded & DLLMain() is called. + // LoadLibrary() will do this, try once with this & if it fails dont load. + retryLoadLibrary = !retryLoadLibrary; } +#endif +#ifdef Q_OS_WIN + if (hTempModule) { + BOOL ok = ::FreeLibrary(hTempModule); + if (ok) { + hTempModule = 0; + } - } + } #endif + } while(retryLoadLibrary); // Will be 'false' in all cases other than when an + // exception is thrown(will happen only when using a MS compiler) } // Qt 4.5 compatibility: stl doesn't affect binary compatibility @@ -759,6 +857,9 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) #ifdef QT_BUILD_KEY_COMPAT2 && key != QT_BUILD_KEY_COMPAT2 #endif +#ifdef QT_BUILD_KEY_COMPAT3 + && key != QT_BUILD_KEY_COMPAT3 +#endif ) { if (qt_debug_component()) { qWarning("In %s:\n" @@ -1039,7 +1140,7 @@ void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &ver */ void *QLibrary::resolve(const char *symbol) { - if (!load()) + if (!isLoaded() && !load()) return 0; return d->resolve(symbol); } @@ -1182,15 +1283,11 @@ QLibrary::LoadHints QLibrary::loadHints() const /* Internal, for debugging */ bool qt_debug_component() { -#if defined(QT_DEBUG_COMPONENT) - return true; //compatibility? -#else static int debug_env = -1; if (debug_env == -1) debug_env = QT_PREPEND_NAMESPACE(qgetenv)("QT_DEBUG_PLUGINS").toInt(); return debug_env != 0; -#endif } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 2650ffe..e2e16d9 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -60,6 +60,7 @@ #include "QtCore/qpointer.h" #include "QtCore/qstringlist.h" #include "QtCore/qplugin.h" +#include "QtCore/qsharedpointer.h" #ifndef QT_NO_LIBRARY @@ -90,6 +91,7 @@ public: static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString()); + QWeakPointer<QObject> inst; QtPluginInstanceFunction instance; uint qt_version; QString lastModified; diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 9dad7e6..e32729a 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -56,9 +56,13 @@ #include <string.h> #endif +#if defined(Q_OS_VXWORKS) || defined (Q_OS_NACL) +#define QT_NO_DYNAMIC_LIBRARY +#endif + QT_BEGIN_NAMESPACE -#if !defined(QT_HPUX_LD) && !defined(Q_OS_VXWORKS) +#if !defined(QT_HPUX_LD) && !defined(QT_NO_DYNAMIC_LIBRARY) QT_BEGIN_INCLUDE_NAMESPACE #include <dlfcn.h> QT_END_INCLUDE_NAMESPACE @@ -66,8 +70,8 @@ QT_END_INCLUDE_NAMESPACE static QString qdlerror() { -#if defined(Q_OS_VXWORKS) - const char *err = "VxWorks does not support dynamic libraries."; +#if defined(QT_NO_DYNAMIC_LIBRARY) + const char *err = "This platform does not support dynamic libraries."; #elif !defined(QT_HPUX_LD) const char *err = dlerror(); #else @@ -79,7 +83,7 @@ static QString qdlerror() bool QLibraryPrivate::load_sys() { QString attempt; -#if !defined(Q_OS_VXWORKS) +#if !defined(QT_NO_DYNAMIC_LIBRARY) QFileInfo fi(fileName); #if defined(Q_OS_SYMBIAN) @@ -223,15 +227,19 @@ bool QLibraryPrivate::load_sys() #ifdef Q_OS_MAC if (!pHnd) { - if (CFBundleRef bundle = CFBundleGetBundleWithIdentifier(QCFString(fileName))) { + QByteArray utf8Bundle = fileName.toUtf8(); + QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true); + QCFType<CFBundleRef> bundle = CFBundleCreate(NULL, bundleUrl); + if(bundle) { QCFType<CFURLRef> url = CFBundleCopyExecutableURL(bundle); - QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); - pHnd = dlopen(QFile::encodeName(str), dlFlags); - attempt = str; + char executableFile[FILENAME_MAX]; + CFURLGetFileSystemRepresentation(url, true, reinterpret_cast<UInt8*>(executableFile), FILENAME_MAX); + attempt = QString::fromUtf8(executableFile); + pHnd = dlopen(QFile::encodeName(attempt), dlFlags); } } #endif -#endif // Q_OS_VXWORKS +#endif // QT_NO_DYNAMIC_LIBRARY if (!pHnd) { errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName).arg(qdlerror()); } @@ -244,7 +252,7 @@ bool QLibraryPrivate::load_sys() bool QLibraryPrivate::unload_sys() { -#if !defined(Q_OS_VXWORKS) +#if !defined(QT_NO_DYNAMIC_LIBRARY) # if defined(QT_HPUX_LD) if (shl_unload((shl_t)pHnd)) { # else @@ -278,7 +286,7 @@ void* QLibraryPrivate::resolve_sys(const char* symbol) void* address = 0; if (shl_findsym((shl_t*)&pHnd, symbol, TYPE_UNDEFINED, &address) < 0) address = 0; -#elif defined(Q_OS_VXWORKS) +#elif defined (QT_NO_DYNAMIC_LIBRARY) void *address = 0; #else void* address = dlsym(pHnd, symbol); diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index d8e4bf3..2fe67ae 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -101,13 +101,17 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio // NOTE: if you change pattern, you MUST change the pattern in // qlibrary.cpp as well. changing the pattern will break all // backwards compatibility as well (no old plugins will be loaded). +// QT5: should probably remove the entire pattern thing and do the section +// trick for all platforms. for now, keep it and fallback to scan for it. # ifdef QPLUGIN_DEBUG_STR # undef QPLUGIN_DEBUG_STR # endif # ifdef QT_NO_DEBUG # define QPLUGIN_DEBUG_STR "false" +# define QPLUGIN_SECTION_DEBUG_STR "" # else # define QPLUGIN_DEBUG_STR "true" +# define QPLUGIN_SECTION_DEBUG_STR ".debug" # endif # define Q_PLUGIN_VERIFICATION_DATA \ static const char qt_plugin_verification_data[] = \ @@ -116,6 +120,13 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio "debug="QPLUGIN_DEBUG_STR"\n" \ "buildkey="QT_BUILD_KEY; +# if defined (Q_OF_ELF) && defined (Q_CC_GNU) +# define Q_PLUGIN_VERIFICATION_SECTION \ + __attribute__ ((section (".qtplugin"))) __attribute__((used)) +# else +# define Q_PLUGIN_VERIFICATION_SECTION +# endif + # if defined (Q_OS_WIN32) && defined(Q_CC_BOR) # define Q_STANDARD_CALL __stdcall # else @@ -123,7 +134,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio # endif # define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \ - Q_PLUGIN_VERIFICATION_DATA \ + Q_PLUGIN_VERIFICATION_SECTION Q_PLUGIN_VERIFICATION_DATA \ Q_EXTERN_C Q_DECL_EXPORT \ const char * Q_STANDARD_CALL qt_plugin_query_verification_data() \ { return qt_plugin_verification_data; } \ diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index 399eb8d..2997293 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -198,11 +198,11 @@ QPluginLoader::~QPluginLoader() */ QObject *QPluginLoader::instance() { - if (!load()) + if (!isLoaded() && !load()) return 0; - if (d->instance) - return d->instance(); - return 0; + if (!d->inst && d->instance) + d->inst = d->instance(); + return d->inst.data(); } /*! diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index e52d73e..b2af5ca 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -793,7 +793,7 @@ void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &tr } // We require that at least one animation is valid. // ### generalize - QList<QVariantAnimation*> variantAnims = qFindChildren<QVariantAnimation*>(anim); + QList<QVariantAnimation*> variantAnims = anim->findChildren<QVariantAnimation*>(); if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim)) variantAnims.append(va); @@ -1178,7 +1178,7 @@ void QStateMachinePrivate::removeStartState() void QStateMachinePrivate::clearHistory() { Q_Q(QStateMachine); - QList<QHistoryState*> historyStates = qFindChildren<QHistoryState*>(q); + QList<QHistoryState*> historyStates = q->findChildren<QHistoryState*>(); for (int i = 0; i < historyStates.size(); ++i) { QHistoryState *h = historyStates.at(i); QHistoryStatePrivate::get(h)->configuration.clear(); @@ -1245,9 +1245,7 @@ void QStateMachinePrivate::_q_process() #endif while (processing) { if (stop) { - stop = false; processing = false; - stopProcessingReason = Stopped; break; } QSet<QAbstractTransition*> enabledTransitions; @@ -1299,6 +1297,11 @@ void QStateMachinePrivate::_q_process() #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": finished the event processing loop"; #endif + if (stop) { + stop = false; + stopProcessingReason = Stopped; + } + switch (stopProcessingReason) { case EventQueueEmpty: break; @@ -1389,7 +1392,7 @@ void QStateMachinePrivate::cancelAllDelayedEvents() delayedEvents.clear(); } -namespace { +namespace _QStateMachine_Internal{ class GoToStateTransition : public QAbstractTransition { @@ -1403,7 +1406,9 @@ protected: }; } // namespace - +// mingw compiler tries to export QObject::findChild<GoToStateTransition>(), +// which doesn't work if its in an anonymous namespace. +using namespace _QStateMachine_Internal; /*! \internal @@ -1440,7 +1445,7 @@ void QStateMachinePrivate::goToState(QAbstractState *targetState) Q_ASSERT(sourceState != 0); // Reuse previous GoToStateTransition in case of several calls to // goToState() in a row. - GoToStateTransition *trans = qFindChild<GoToStateTransition*>(sourceState); + GoToStateTransition *trans = sourceState->findChild<GoToStateTransition*>(); if (!trans) { trans = new GoToStateTransition(targetState); sourceState->addTransition(trans); @@ -1562,7 +1567,7 @@ void QStateMachinePrivate::unregisterAllTransitions() { Q_Q(QStateMachine); { - QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState()); + QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>(); for (int i = 0; i < transitions.size(); ++i) { QSignalTransition *t = transitions.at(i); if (t->machine() == q) @@ -1570,7 +1575,7 @@ void QStateMachinePrivate::unregisterAllTransitions() } } { - QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState()); + QList<QEventTransition*> transitions = rootState()->findChildren<QEventTransition*>(); for (int i = 0; i < transitions.size(); ++i) { QEventTransition *t = transitions.at(i); if (t->machine() == q) diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index cafcfef..001506c 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -45,6 +45,7 @@ #ifndef QT_NO_THREAD #include "qatomic.h" +#include "qelapsedtimer.h" #include "qthread.h" #include "qmutex_p.h" @@ -130,7 +131,7 @@ QMutex::QMutex(RecursionMode mode) \warning Destroying a locked mutex may result in undefined behavior. */ QMutex::~QMutex() -{ delete d; } +{ delete static_cast<QMutexPrivate *>(d); } /*! Locks the mutex. If another thread has locked the mutex then this @@ -146,6 +147,7 @@ QMutex::~QMutex() */ void QMutex::lock() { + QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); Qt::HANDLE self; if (d->recursive) { @@ -156,20 +158,12 @@ void QMutex::lock() return; } - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { -#ifndef QT_NO_DEBUG - if (d->owner == self) - qWarning() << "QMutex::lock: Deadlock detected in thread" << d->owner; -#endif - // didn't get the lock, wait for it isLocked = d->wait(); Q_ASSERT_X(isLocked, "QMutex::lock", "Internal error, infinite wait has timed out."); - - // don't need to wait for the lock anymore - d->contenders.deref(); } d->owner = self; @@ -178,54 +172,10 @@ void QMutex::lock() return; } -#ifndef QT_NO_DEBUG - self = QThread::currentThreadId(); -#endif - - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { - int spinCount = 0; - int lastSpinCount = d->lastSpinCount; - - enum { AdditionalSpins = 20, SpinCountPenalizationDivisor = 4 }; - const int maximumSpinCount = lastSpinCount + AdditionalSpins; - - do { - if (spinCount++ > maximumSpinCount) { - // puts("spinning useless, sleeping"); - isLocked = d->contenders.fetchAndAddAcquire(1) == 0; - if (!isLocked) { -#ifndef QT_NO_DEBUG - if (d->owner == self) - qWarning() << "QMutex::lock: Deadlock detected in thread" << d->owner; -#endif - - // didn't get the lock, wait for it - isLocked = d->wait(); - Q_ASSERT_X(isLocked, "QMutex::lock", - "Internal error, infinite wait has timed out."); - - // don't need to wait for the lock anymore - d->contenders.deref(); - } - // decrease the lastSpinCount since we didn't actually get the lock by spinning - spinCount = -d->lastSpinCount / SpinCountPenalizationDivisor; - break; - } - - isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); - } while (!isLocked); - - // adjust the last spin lock count - lastSpinCount = d->lastSpinCount; - d->lastSpinCount = spinCount >= 0 - ? qMax(lastSpinCount, spinCount) - : lastSpinCount + spinCount; + lockInternal(); } - -#ifndef QT_NO_DEBUG - d->owner = self; -#endif } /*! @@ -247,6 +197,7 @@ void QMutex::lock() */ bool QMutex::tryLock() { + QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); Qt::HANDLE self; if (d->recursive) { @@ -257,7 +208,7 @@ bool QMutex::tryLock() return true; } - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { // some other thread has the mutex locked, or we tried to // recursively lock an non-recursive mutex @@ -270,19 +221,7 @@ bool QMutex::tryLock() return isLocked; } -#ifndef QT_NO_DEBUG - self = QThread::currentThreadId(); -#endif - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - // some other thread has the mutex locked, or we tried to - // recursively lock an non-recursive mutex - return isLocked; - } -#ifndef QT_NO_DEBUG - d->owner = self; -#endif - return isLocked; + return d->contenders.testAndSetAcquire(0, 1); } /*! \overload @@ -310,6 +249,7 @@ bool QMutex::tryLock() */ bool QMutex::tryLock(int timeout) { + QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); Qt::HANDLE self; if (d->recursive) { @@ -320,13 +260,10 @@ bool QMutex::tryLock(int timeout) return true; } - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { // didn't get the lock, wait for it isLocked = d->wait(timeout); - - // don't need to wait for the lock anymore - d->contenders.deref(); if (!isLocked) return false; } @@ -337,23 +274,9 @@ bool QMutex::tryLock(int timeout) return true; } -#ifndef QT_NO_DEBUG - self = QThread::currentThreadId(); -#endif - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; - if (!isLocked) { - // didn't get the lock, wait for it - isLocked = d->wait(timeout); - - // don't need to wait for the lock anymore - d->contenders.deref(); - if (!isLocked) - return false; - } -#ifndef QT_NO_DEBUG - d->owner = self; -#endif - return true; + return (d->contenders.testAndSetAcquire(0, 1) + // didn't get the lock, wait for it + || d->wait(timeout)); } @@ -366,9 +289,7 @@ bool QMutex::tryLock(int timeout) */ void QMutex::unlock() { - Q_ASSERT_X(d->owner == QThread::currentThreadId(), "QMutex::unlock()", - "A mutex must be unlocked in the same thread that locked it."); - + QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); if (d->recursive) { if (!--d->count) { d->owner = 0; @@ -376,9 +297,6 @@ void QMutex::unlock() d->wakeUp(); } } else { -#ifndef QT_NO_DEBUG - d->owner = 0; -#endif if (!d->contenders.testAndSetRelease(1, 0)) d->wakeUp(); } @@ -506,6 +424,92 @@ void QMutex::unlock() Use the constructor that takes a RecursionMode parameter instead. */ +/*! + \internal helper for lockInline() + */ +void QMutex::lockInternal() +{ + QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); + + if (QThread::idealThreadCount() == 1) { + // don't spin on single cpu machines + bool isLocked = d->wait(); + Q_ASSERT_X(isLocked, "QMutex::lock", + "Internal error, infinite wait has timed out."); + Q_UNUSED(isLocked); + return; + } + + QElapsedTimer elapsedTimer; + elapsedTimer.start(); + do { + qint64 spinTime = elapsedTimer.nsecsElapsed(); + if (spinTime > d->maximumSpinTime) { + // didn't get the lock, wait for it, since we're not going to gain anything by spinning more + elapsedTimer.start(); + bool isLocked = d->wait(); + Q_ASSERT_X(isLocked, "QMutex::lock", + "Internal error, infinite wait has timed out."); + Q_UNUSED(isLocked); + + qint64 maximumSpinTime = d->maximumSpinTime; + qint64 averageWaitTime = d->averageWaitTime; + qint64 actualWaitTime = elapsedTimer.nsecsElapsed(); + if (actualWaitTime < (QMutexPrivate::MaximumSpinTimeThreshold * 3 / 2)) { + // measure the wait times + averageWaitTime = d->averageWaitTime = qMin((averageWaitTime + actualWaitTime) / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + } + + // adjust the spin count when spinning does not benefit contention performance + if ((spinTime + actualWaitTime) - qint64(QMutexPrivate::MaximumSpinTimeThreshold) >= qint64(QMutexPrivate::MaximumSpinTimeThreshold)) { + // long waits, stop spinning + d->maximumSpinTime = 0; + } else { + // allow spinning if wait times decrease, but never spin more than the average wait time (otherwise we may perform worse) + d->maximumSpinTime = qBound(qint64(averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + } + return; + } + // be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not + QThread::yieldCurrentThread(); + } while (d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1)); + + // spinning is working, do not change the spin time (unless we are using much less time than allowed to spin) + qint64 maximumSpinTime = d->maximumSpinTime; + qint64 spinTime = elapsedTimer.nsecsElapsed(); + if (spinTime < maximumSpinTime / 2) { + // we are using much less time than we need, adjust the limit + d->maximumSpinTime = qBound(qint64(d->averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + } +} + +/*! + \internal +*/ +void QMutex::unlockInternal() +{ + static_cast<QMutexPrivate *>(d)->wakeUp(); +} + +/*! + \fn QMutex::lockInline() + \internal + inline version of QMutex::lock() +*/ + +/*! + \fn QMutex::unlockInline() + \internal + inline version of QMutex::unlock() +*/ + +/*! + \fn QMutex::tryLockInline() + \internal + inline version of QMutex::tryLock() +*/ + + QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 52a0fc3..5f75195 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -43,6 +43,7 @@ #define QMUTEX_H #include <QtCore/qglobal.h> +#include <QtCore/qatomic.h> #include <new> QT_BEGIN_HEADER @@ -53,7 +54,8 @@ QT_MODULE(Core) #ifndef QT_NO_THREAD -class QMutexPrivate; +class QAtomicInt; +class QMutexData; class Q_CORE_EXPORT QMutex { @@ -66,10 +68,13 @@ public: explicit QMutex(RecursionMode mode = NonRecursive); ~QMutex(); - void lock(); - bool tryLock(); + void lock(); //### Qt5: make inline; + inline void lockInline(); + bool tryLock(); //### Qt5: make inline; bool tryLock(int timeout); - void unlock(); + inline bool tryLockInline(); + void unlock(); //### Qt5: make inline; + inline void unlockInline(); #if defined(QT3_SUPPORT) inline QT3_SUPPORT bool locked() @@ -86,9 +91,11 @@ public: #endif private: + void lockInternal(); + void unlockInternal(); Q_DISABLE_COPY(QMutex) - QMutexPrivate *d; + QMutexData *d; }; class Q_CORE_EXPORT QMutexLocker @@ -99,7 +106,7 @@ public: Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0), "QMutexLocker", "QMutex pointer is misaligned"); if (m) { - m->lock(); + m->lockInline(); val = reinterpret_cast<quintptr>(m) | quintptr(1u); } else { val = 0; @@ -111,7 +118,7 @@ public: { if ((val & quintptr(1u)) == quintptr(1u)) { val &= ~quintptr(1u); - mutex()->unlock(); + mutex()->unlockInline(); } } @@ -119,7 +126,7 @@ public: { if (val) { if ((val & quintptr(1u)) == quintptr(0u)) { - mutex()->lock(); + mutex()->lockInline(); val |= quintptr(1u); } } @@ -145,6 +152,53 @@ private: quintptr val; }; +class QMutexData +{ + public: + QAtomicInt contenders; + const uint recursive : 1; + uint reserved : 31; + protected: + QMutexData(QMutex::RecursionMode mode); + ~QMutexData(); +}; + +#ifdef QT_NO_DEBUG +inline void QMutex::unlockInline() +{ + if (d->recursive) { + unlock(); + } else if (!d->contenders.testAndSetRelease(1, 0)) { + unlockInternal(); + } +} + +inline bool QMutex::tryLockInline() +{ + if (d->recursive) { + return tryLock(); + } else { + return d->contenders.testAndSetAcquire(0, 1); + } +} + +inline void QMutex::lockInline() +{ + if (d->recursive) { + lock(); + } else if(!tryLockInline()) { + lockInternal(); + } +} +#else // QT_NO_DEBUG +//in debug we do not use inline calls in order to allow debugging tools +// to hook the mutex locking functions. +inline void QMutex::unlockInline() { unlock(); } +inline bool QMutex::tryLockInline() { return tryLock(); } +inline void QMutex::lockInline() { lock(); } +#endif // QT_NO_DEBUG + + #else // QT_NO_THREAD @@ -157,9 +211,11 @@ public: inline ~QMutex() {} static inline void lock() {} - static inline bool tryLock() { return true; } - static inline bool tryLock(int timeout) { Q_UNUSED(timeout); return true; } - static void unlock() {} + static inline void lockInline() {} + static inline bool tryLock(int timeout = 0) { Q_UNUSED(timeout); return true; } + static inline bool tryLockInline() { return true; } + static inline void unlock() {} + static inline void unlockInline() {} #if defined(QT3_SUPPORT) static inline QT3_SUPPORT bool locked() { return false; } diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 0373b3f..1a31c87 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -56,25 +56,32 @@ #include <QtCore/qglobal.h> #include <QtCore/qnamespace.h> +#include <QtCore/qmutex.h> + +#if defined(Q_OS_MAC) +# include <mach/semaphore.h> +#endif QT_BEGIN_NAMESPACE -class QMutexPrivate { +class QMutexPrivate : public QMutexData { public: QMutexPrivate(QMutex::RecursionMode mode); ~QMutexPrivate(); - ulong self(); bool wait(int timeout = -1); void wakeUp(); - const bool recursive; - QAtomicInt contenders; - volatile int lastSpinCount; + // 1ms = 1000000ns + enum { MaximumSpinTimeThreshold = 1000000 }; + volatile qint64 maximumSpinTime; + volatile qint64 averageWaitTime; Qt::HANDLE owner; uint count; -#if defined(Q_OS_UNIX) +#if defined(Q_OS_MAC) + semaphore_t mach_semaphore; +#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) volatile bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; @@ -83,6 +90,12 @@ public: #endif }; +inline QMutexData::QMutexData(QMutex::RecursionMode mode) + : recursive(mode == QMutex::Recursive) +{} + +inline QMutexData::~QMutexData() {} + QT_END_NAMESPACE #endif // QMUTEX_P_H diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp index dd7e5d1..11e2060 100644 --- a/src/corelib/thread/qmutex_unix.cpp +++ b/src/corelib/thread/qmutex_unix.cpp @@ -53,30 +53,116 @@ #undef wakeup #endif +#if defined(Q_OS_MAC) +# include <mach/mach.h> +# include <mach/task.h> +#elif defined(Q_OS_LINUX) +# include <linux/futex.h> +# include <sys/syscall.h> +# include <unistd.h> +#endif + QT_BEGIN_NAMESPACE +#if !defined(Q_OS_MAC) && !defined(Q_OS_LINUX) static void report_error(int code, const char *where, const char *what) { if (code != 0) qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code))); } +#endif QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : recursive(mode == QMutex::Recursive), contenders(0), lastSpinCount(0), owner(0), count(0), wakeup(false) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) { +#if defined(Q_OS_MAC) + kern_return_t r = semaphore_create(mach_task_self(), &mach_semaphore, SYNC_POLICY_FIFO, 0); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to create semaphore, error %d", r); +#elif !defined(Q_OS_LINUX) + wakeup = false; report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init"); report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init"); +#endif } QMutexPrivate::~QMutexPrivate() { +#if defined(Q_OS_MAC) + kern_return_t r = semaphore_destroy(mach_task_self(), mach_semaphore); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to destroy semaphore, error %d", r); +#elif !defined(Q_OS_LINUX) report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy"); report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy"); +#endif +} + +#if defined(Q_OS_MAC) + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + bool returnValue; + if (timeout < 0) { + returnValue = semaphore_wait(mach_semaphore) == KERN_SUCCESS; + } else { + mach_timespec_t ts; + ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; + ts.tv_sec = (timeout / 1000); + kern_return_t r = semaphore_timedwait(mach_semaphore, ts); + returnValue = r == KERN_SUCCESS; + } + contenders.deref(); + return returnValue; +} + +void QMutexPrivate::wakeUp() +{ + semaphore_signal(mach_semaphore); +} + +#elif defined(Q_OS_LINUX) + +static inline int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2) +{ + return syscall(SYS_futex, addr, op, val, timeout, addr2, val2); } bool QMutexPrivate::wait(int timeout) { + while (contenders.fetchAndStoreAcquire(2) > 0) { + struct timespec ts, *pts = 0; + if (timeout >= 0) { + ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; + ts.tv_sec = (timeout / 1000); + pts = &ts; + } + int r = _q_futex(&contenders._q_value, FUTEX_WAIT, 2, pts, 0, 0); + if (r != 0 && errno == ETIMEDOUT) + return false; + } + return true; +} + +void QMutexPrivate::wakeUp() +{ + (void) contenders.fetchAndStoreRelease(0); + (void) _q_futex(&contenders._q_value, FUTEX_WAKE, 1, 0, 0, 0); +} + +#else // !Q_OS_MAC && !Q_OS_LINUX + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock"); int errorCode = 0; while (!wakeup) { @@ -104,6 +190,7 @@ bool QMutexPrivate::wait(int timeout) } wakeup = false; report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock"); + contenders.deref(); return errorCode == 0; } @@ -115,6 +202,8 @@ void QMutexPrivate::wakeUp() report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock"); } +#endif // !Q_OS_MAC && !Q_OS_LINUX + QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp index e2f1227..3a535c1 100644 --- a/src/corelib/thread/qmutex_win.cpp +++ b/src/corelib/thread/qmutex_win.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : recursive(mode == QMutex::Recursive), contenders(0), lastSpinCount(0), owner(0), count(0) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) { event = CreateEvent(0, FALSE, FALSE, 0); if (!event) @@ -60,7 +60,13 @@ QMutexPrivate::~QMutexPrivate() bool QMutexPrivate::wait(int timeout) { - return WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0; + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + bool returnValue = (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0); + contenders.deref(); + return returnValue; } void QMutexPrivate::wakeUp() diff --git a/src/corelib/thread/qorderedmutexlocker_p.h b/src/corelib/thread/qorderedmutexlocker_p.h index 5e9c5a8..ddabfb7 100644 --- a/src/corelib/thread/qorderedmutexlocker_p.h +++ b/src/corelib/thread/qorderedmutexlocker_p.h @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE -class QMutex; +#include <QtCore/qmutex.h> /* Locks 2 mutexes in a defined order, avoiding a recursive lock if @@ -79,8 +79,8 @@ public: void relock() { if (!locked) { - if (mtx1) mtx1->lock(); - if (mtx2) mtx2->lock(); + if (mtx1) mtx1->lockInline(); + if (mtx2) mtx2->lockInline(); locked = true; } } @@ -88,8 +88,8 @@ public: void unlock() { if (locked) { - if (mtx1) mtx1->unlock(); - if (mtx2) mtx2->unlock(); + if (mtx1) mtx1->unlockInline(); + if (mtx2) mtx2->unlockInline(); locked = false; } } @@ -100,10 +100,10 @@ public: if (mtx1 == mtx2) return false; if (mtx1 < mtx2) { - mtx2->lock(); + mtx2->lockInline(); return true; } - if (!mtx2->tryLock()) { + if (!mtx2->tryLockInline()) { mtx1->unlock(); mtx2->lock(); mtx1->lock(); diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index 721d41f..ba6fa6e 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -223,8 +223,9 @@ bool QSemaphore::tryAcquire(int n, int timeout) QElapsedTimer timer; timer.start(); while (n > d->avail) { - if (timer.hasExpired(timeout) - || !d->cond.wait(locker.mutex(), timeout - timer.elapsed())) + const qint64 elapsed = timer.elapsed(); + if (timeout - elapsed <= 0 + || !d->cond.wait(locker.mutex(), timeout - elapsed)) return false; } } diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 8223cff..326f494 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -173,7 +173,8 @@ void QAdoptedThread::run() */ QThreadPrivate::QThreadPrivate(QThreadData *d) - : QObjectPrivate(), running(false), finished(false), terminated(false), exited(false), returnCode(-1), + : QObjectPrivate(), running(false), finished(false), terminated(false), + isInFinish(false), exited(false), returnCode(-1), stackSize(0), priority(QThread::InheritPriority), data(d) { #if defined (Q_OS_UNIX) @@ -403,6 +404,11 @@ QThread::~QThread() Q_D(QThread); { QMutexLocker locker(&d->mutex); + if (d->isInFinish) { + locker.unlock(); + wait(); + locker.relock(); + } if (d->running && !d->finished) qWarning("QThread: Destroyed while thread is still running"); diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index e8f7ef5..36e07c0 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -126,6 +126,7 @@ public: bool running; bool finished; bool terminated; + bool isInFinish; //when in QThreadPrivate::finish bool exited; int returnCode; @@ -208,8 +209,6 @@ public: bool canWait; QVector<void *> tls; - QMutex mutex; - # ifdef Q_OS_SYMBIAN RThread symbian_thread_handle; # endif diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index c2bc895..811a193 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -94,7 +94,9 @@ # define old_qDebug qDebug # undef qDebug # endif +#ifndef QT_NO_CORESERVICES # include <CoreServices/CoreServices.h> +#endif //QT_NO_CORESERVICES # ifdef old_qDebug # undef qDebug @@ -119,6 +121,17 @@ QT_BEGIN_NAMESPACE enum { ThreadPriorityResetFlag = 0x80000000 }; +#if defined(Q_OS_LINUX) && defined(__GLIBC__) && (defined(Q_CC_GNU) || defined(Q_CC_INTEL)) +#define HAVE_TLS +#endif +#if defined(Q_CC_XLC) || defined (Q_CC_SUN) +#define HAVE_TLS +#endif + +#ifdef HAVE_TLS +static __thread QThreadData *currentThreadData = 0; +#endif + static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT; static pthread_key_t current_thread_data_key; @@ -154,6 +167,7 @@ static void create_current_thread_data_key() static void destroy_current_thread_data_key() { + pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_key_delete(current_thread_data_key); } Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) @@ -165,7 +179,9 @@ Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) // that pthread has, so pthread_setspecific is also used. static QThreadData *get_thread_data() { -#ifdef Q_OS_SYMBIAN +#ifdef HAVE_TLS + return currentThreadData; +#elif defined Q_OS_SYMBIAN return reinterpret_cast<QThreadData *>(Dll::Tls()); #else pthread_once(¤t_thread_data_once, create_current_thread_data_key); @@ -175,7 +191,9 @@ static QThreadData *get_thread_data() static void set_thread_data(QThreadData *data) { -#ifdef Q_OS_SYMBIAN +#ifdef HAVE_TLS + currentThreadData = data; +#elif defined Q_OS_SYMBIAN qt_symbian_throwIfError(Dll::SetTls(data)); #endif pthread_once(¤t_thread_data_once, create_current_thread_data_key); @@ -184,7 +202,9 @@ static void set_thread_data(QThreadData *data) static void clear_thread_data() { -#ifdef Q_OS_SYMBIAN +#ifdef HAVE_TLS + currentThreadData = 0; +#elif defined Q_OS_SYMBIAN Dll::FreeTls(); #endif pthread_setspecific(current_thread_data_key, 0); @@ -318,7 +338,10 @@ void *QThreadPrivate::start(void *arg) set_thread_data(data); data->ref(); - data->quitNow = false; + { + QMutexLocker locker(&thr->d_func()->mutex); + data->quitNow = thr->d_func()->exited; + } // ### TODO: allow the user to create a custom event dispatcher createEventDispatcher(data); @@ -347,39 +370,45 @@ void QThreadPrivate::finish(void *arg) { QThread *thr = reinterpret_cast<QThread *>(arg); QThreadPrivate *d = thr->d_func(); + #ifdef Q_OS_SYMBIAN - if (lockAnyway) + QMutexLocker locker(lockAnyway ? &d->mutex : 0); +#else + QMutexLocker locker(&d->mutex); #endif - d->mutex.lock(); + d->isInFinish = true; d->priority = QThread::InheritPriority; - d->running = false; - d->finished = true; - if (d->terminated) + bool terminated = d->terminated; + void *data = &d->data->tls; + locker.unlock(); + if (terminated) emit thr->terminated(); - d->terminated = false; emit thr->finished(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QThreadStorageData::finish((void **)data); + locker.relock(); + d->terminated = false; - if (d->data->eventDispatcher) { - d->data->eventDispatcher->closingDown(); - QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + if (eventDispatcher) { d->data->eventDispatcher = 0; + locker.unlock(); + eventDispatcher->closingDown(); delete eventDispatcher; + locker.relock(); } - void *data = &d->data->tls; - QThreadStorageData::finish((void **)data); - d->thread_id = 0; #ifdef Q_OS_SYMBIAN if (closeNativeHandle) d->data->symbian_thread_handle.Close(); #endif + d->running = false; + d->finished = true; + + d->isInFinish = false; d->thread_done.wakeAll(); -#ifdef Q_OS_SYMBIAN - if (lockAnyway) -#endif - d->mutex.unlock(); } @@ -404,7 +433,7 @@ int QThread::idealThreadCount() { int cores = -1; -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) && !defined(Q_WS_QPA) // Mac OS X cores = MPProcessorsScheduled(); #elif defined(Q_OS_HPUX) @@ -568,6 +597,10 @@ void QThread::start(Priority priority) { Q_D(QThread); QMutexLocker locker(&d->mutex); + + if (d->isInFinish) + d->thread_done.wait(locker.mutex()); + if (d->running) return; diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index 66161d7..6b7932b 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -298,7 +298,10 @@ unsigned int __stdcall QThreadPrivate::start(void *arg) QThread::setTerminationEnabled(false); - data->quitNow = false; + { + QMutexLocker locker(&thr->d_func()->mutex); + data->quitNow = thr->d_func()->exited; + } // ### TODO: allow the user to create a custom event dispatcher createEventDispatcher(data); @@ -323,24 +326,33 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) QThread *thr = reinterpret_cast<QThread *>(arg); QThreadPrivate *d = thr->d_func(); - if (lockAnyway) - d->mutex.lock(); + QMutexLocker locker(lockAnyway ? &d->mutex : 0); + d->isInFinish = true; d->priority = QThread::InheritPriority; - d->running = false; - d->finished = true; - if (d->terminated) + bool terminated = d->terminated; + void **tls_data = reinterpret_cast<void **>(&d->data->tls); + locker.unlock(); + if (terminated) emit thr->terminated(); - d->terminated = false; emit thr->finished(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QThreadStorageData::finish(tls_data); + locker.relock(); - if (d->data->eventDispatcher) { - d->data->eventDispatcher->closingDown(); - QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + d->terminated = false; + + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + if (eventDispatcher) { d->data->eventDispatcher = 0; + locker.unlock(); + eventDispatcher->closingDown(); delete eventDispatcher; + locker.relock(); } - QThreadStorageData::finish(reinterpret_cast<void **>(&d->data->tls)); + d->running = false; + d->finished = true; + d->isInFinish = false; if (!d->waiters) { CloseHandle(d->handle); @@ -349,8 +361,6 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) d->id = 0; - if (lockAnyway) - d->mutex.unlock(); } /************************************************************************** @@ -399,6 +409,12 @@ void QThread::start(Priority priority) Q_D(QThread); QMutexLocker locker(&d->mutex); + if (d->isInFinish) { + locker.unlock(); + wait(); + locker.relock(); + } + if (d->running) return; diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index ebf79af..2b35333 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -178,11 +178,12 @@ void QThreadStorageData::finish(void **p) return; // nothing to do DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread()); - - for(int i = tls->size() - 1; i >= 0; i--) { - void *&value = (*tls)[i]; + while (!tls->isEmpty()) { + void *&value = tls->last(); void *q = value; value = 0; + int i = tls->size() - 1; + tls->resize(i); if (!q) { // data already deleted @@ -200,6 +201,11 @@ void QThreadStorageData::finish(void **p) continue; } destructor(q); //crash here might mean the thread exited after qthreadstorage was destroyed + + if (tls->size() > i) { + //re reset the tls in case it has been recreated by its own destructor. + (*tls)[i] = 0; + } } tls->clear(); } @@ -215,19 +221,18 @@ void QThreadStorageData::finish(void **p) QThreadStorage is a template class that provides per-thread data storage. - \e{Note that due to compiler limitations, QThreadStorage can only - store pointers.} - The setLocalData() function stores a single thread-specific value for the calling thread. The data can be accessed later using - localData(). QThreadStorage takes ownership of the data (which - must be created on the heap with \c new) and deletes it when the - thread exits, either normally or via termination. + localData(). The hasLocalData() function allows the programmer to determine if data has previously been set using the setLocalData() function. This is also useful for lazy initializiation. + If T is a pointer type, QThreadStorage takes ownership of the data + (which must be created on the heap with \c new) and deletes it when + the thread exits, either normally or via termination. + For example, the following code uses QThreadStorage to store a single cache for each thread that calls the cacheObject() and removeFromCache() functions. The cache is automatically @@ -241,9 +246,6 @@ void QThreadStorageData::finish(void **p) \list - \o As noted above, QThreadStorage can only store pointers due to - compiler limitations. - \o The QThreadStorage destructor does not delete per-thread data. QThreadStorage only deletes per-thread data when the thread exits or when setLocalData() is called multiple times. @@ -279,8 +281,11 @@ void QThreadStorageData::finish(void **p) /*! \fn bool QThreadStorage::hasLocalData() const - Returns true if the calling thread has non-zero data available; - otherwise returns false. + If T is a pointer type, returns true if the calling thread has + non-zero data available. + + If T is a value type, returns whether the data has already been + constructed by calling setLocalData or localData. \sa localData() */ @@ -291,10 +296,8 @@ void QThreadStorageData::finish(void **p) Returns a reference to the data that was set by the calling thread. - Note: QThreadStorage can only store pointers. This function - returns a reference to the pointer that was set by the calling - thread. The value of this reference is 0 if no data was set by - the calling thread, + If no data has been set, this will create a default constructed + instance of type T. \sa hasLocalData() */ @@ -305,10 +308,6 @@ void QThreadStorageData::finish(void **p) Returns a copy of the data that was set by the calling thread. - Note: QThreadStorage can only store pointers. This function - returns a pointer to the data that was set by the calling thread. - If no data was set by the calling thread, this function returns 0. - \sa hasLocalData() */ @@ -318,19 +317,9 @@ void QThreadStorageData::finish(void **p) Sets the local data for the calling thread to \a data. It can be accessed later using the localData() functions. - If \a data is 0, this function deletes the previous data (if - any) and returns immediately. - - If \a data is non-zero, QThreadStorage takes ownership of the \a - data and deletes it automatically either when the thread exits - (either normally or via termination) or when setLocalData() is - called again. - - Note: QThreadStorage can only store pointers. The \a data - argument must be either a pointer to an object created on the heap - (i.e. using \c new) or 0. You should not delete \a data - yourself; QThreadStorage takes ownership and will delete the \a - data itself. + If T is a pointer type, QThreadStorage takes ownership of the data + and deletes it automatically either when the thread exits (either + normally or via termination) or when setLocalData() is called again. \sa localData(), hasLocalData() */ diff --git a/src/corelib/thread/qthreadstorage.h b/src/corelib/thread/qthreadstorage.h index 3fa0971..e1cafb6 100644 --- a/src/corelib/thread/qthreadstorage.h +++ b/src/corelib/thread/qthreadstorage.h @@ -91,7 +91,10 @@ inline void qThreadStorage_setLocalData(QThreadStorageData &d, T **t) { (void) d.set(*t); } -#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION +template <typename T> +inline +void qThreadStorage_deleteData(void *d, T **) +{ delete static_cast<T *>(d); } // value-based specialization template <typename T> @@ -116,7 +119,11 @@ inline void qThreadStorage_setLocalData(QThreadStorageData &d, T *t) { (void) d.set(new T(*t)); } -#endif // QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION +template <typename T> +inline +void qThreadStorage_deleteData(void *d, T *) +{ delete static_cast<T *>(d); } + // MOC_SKIP_END #endif @@ -130,7 +137,7 @@ private: Q_DISABLE_COPY(QThreadStorage) static inline void deleteData(void *x) - { delete static_cast<T>(x); } + { qThreadStorage_deleteData(x, reinterpret_cast<T*>(0)); } public: inline QThreadStorage() : d(deleteData) { } diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 03f661d..90583bb 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -31,3 +31,7 @@ unix:SOURCES += thread/qmutex_unix.cpp \ win32:SOURCES += thread/qmutex_win.cpp \ thread/qthread_win.cpp \ thread/qwaitcondition_win.cpp + +integrity:SOURCES += thread/qmutex_unix.cpp \ + thread/qthread_unix.cpp \ + thread/qwaitcondition_unix.cpp diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index a3cd7ad..6181c82 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -418,6 +418,13 @@ void QBitArray::fill(bool value, int begin, int end) this bit array. */ +/*! \fn void QBitArray::swap(QBitArray &other) + \since 4.8 + + Swaps bit array \a other with this bit array. This operation is very + fast and never fails. +*/ + /*! \fn bool QBitArray::operator==(const QBitArray &other) const Returns true if \a other is equal to this bit array; otherwise diff --git a/src/corelib/tools/qbitarray.h b/src/corelib/tools/qbitarray.h index d2f9bf3..cec338c 100644 --- a/src/corelib/tools/qbitarray.h +++ b/src/corelib/tools/qbitarray.h @@ -63,6 +63,12 @@ public: explicit QBitArray(int size, bool val = false); QBitArray(const QBitArray &other) : d(other.d) {} inline QBitArray &operator=(const QBitArray &other) { d = other.d; return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + inline QBitArray &operator=(QBitArray &&other) + { qSwap(d, other.d); return *this; } +#endif + + inline void swap(QBitArray &other) { qSwap(d, other.d); } inline int size() const { return (d.size() << 3) - *d.constData(); } inline int count() const { return (d.size() << 3) - *d.constData(); } diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 641f8d5..1c9b654 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -541,12 +541,19 @@ QByteArray qUncompress(const uchar* data, int nbytes) forever { ulong alloc = len; - d.reset(q_check_ptr(static_cast<QByteArray::Data *>(qRealloc(d.take(), sizeof(QByteArray::Data) + alloc)))); - if (!d) { + if (len >= (1 << 31) - 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 *>(qRealloc(d.data(), sizeof(QByteArray::Data) + alloc)); + 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); int res = ::uncompress((uchar*)d->array, &len, (uchar*)data+4, nbytes-4); @@ -554,12 +561,19 @@ QByteArray qUncompress(const uchar* data, int nbytes) switch (res) { case Z_OK: if (len != alloc) { - d.reset(q_check_ptr(static_cast<QByteArray::Data *>(qRealloc(d.take(), sizeof(QByteArray::Data) + len)))); - if (!d) { + if (len >= (1 << 31) - 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 *>(qRealloc(d.data(), sizeof(QByteArray::Data) + len)); + 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 = 1; d->alloc = d->size = len; @@ -673,12 +687,12 @@ QByteArray::Data QByteArray::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), values. To set all the bytes to a particular value, call fill(). To obtain a pointer to the actual character data, call data() or - constData(). These functions return a pointer to the beginning of - the data. The pointer is guaranteed to remain valid until a - non-const function is called on the QByteArray. It is also - guaranteed that the data ends with a '\\0' byte. This '\\0' byte - is automatically provided by QByteArray and is not counted in - size(). + constData(). These functions return a pointer to the beginning of the data. + The pointer is guaranteed to remain valid until a non-const function is + called on the QByteArray. It is also guaranteed that the data ends with a + '\\0' byte unless the QByteArray was created from a \l{fromRawData()}{raw + data}. This '\\0' byte is automatically provided by QByteArray and is not + counted in size(). QByteArray provides the following basic functions for modifying the byte data: append(), prepend(), insert(), replace(), and @@ -900,15 +914,24 @@ QByteArray &QByteArray::operator=(const char *str) return *this; } +/*! \fn void QByteArray::swap(QByteArray &other) + \since 4.8 + + Swaps byte array \a other with this byte array. This operation is very + fast and never fails. +*/ + /*! \fn int QByteArray::size() const Returns the number of bytes in this byte array. - The last byte in the byte array is at position size() - 1. In - addition, QByteArray ensures that the byte at position size() is - always '\\0', so that you can use the return value of data() and - constData() as arguments to functions that expect '\\0'-terminated - strings. + The last byte in the byte array is at position size() - 1. In addition, + QByteArray ensures that the byte at position size() is always '\\0', so + that you can use the return value of data() and constData() as arguments to + functions that expect '\\0'-terminated strings. If the QByteArray object + was created from a \l{fromRawData()}{raw data} that didn't include the + trailing null-termination character then QByteArray doesn't add it + automaticall unless the \l{deep copy} is created. Example: \snippet doc/src/snippets/code/src_corelib_tools_qbytearray.cpp 6 @@ -1039,10 +1062,11 @@ QByteArray &QByteArray::operator=(const char *str) /*! \fn const char *QByteArray::constData() const - Returns a pointer to the data stored in the byte array. The - pointer can be used to access the bytes that compose the array. - The data is '\\0'-terminated. The pointer remains valid as long - as the byte array isn't reallocated or destroyed. + Returns a pointer to the data stored in the byte array. The pointer can be + used to access the bytes that compose the array. The data is + '\\0'-terminated unless the QByteArray object was created from raw data. + The pointer remains valid as long as the byte array isn't reallocated or + destroyed. This function is mostly useful to pass a byte array to a function that accepts a \c{const char *}. @@ -1051,7 +1075,7 @@ QByteArray &QByteArray::operator=(const char *str) but most functions that take \c{char *} arguments assume that the data ends at the first '\\0' they encounter. - \sa data(), operator[]() + \sa data(), operator[](), fromRawData() */ /*! \fn void QByteArray::detach() diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 58decb8..70d865a 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -144,6 +144,12 @@ public: QByteArray &operator=(const QByteArray &); QByteArray &operator=(const char *str); +#ifdef Q_COMPILER_RVALUE_REFS + inline QByteArray &operator=(QByteArray &&other) + { qSwap(d, other.d); return *this; } +#endif + + inline void swap(QByteArray &other) { qSwap(d, other.d); } inline int size() const; bool isEmpty() const; diff --git a/src/corelib/tools/qcache.h b/src/corelib/tools/qcache.h index 27a87d3..7f6b869 100644 --- a/src/corelib/tools/qcache.h +++ b/src/corelib/tools/qcache.h @@ -61,7 +61,7 @@ class QCache }; Node *f, *l; QHash<Key, Node> hash; - void *unused; + void *unused; // ### Qt5: remove int mx, total; inline void unlink(Node &n) { diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index 7cdc256..a903007 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -48,11 +48,11 @@ # undef QT_NO_CAST_TO_ASCII #endif #include "qchar.h" + #include "qdatastream.h" #include "qtextcodec.h" #include "qunicodetables_p.h" - #include "qunicodetables.cpp" QT_BEGIN_NAMESPACE @@ -677,7 +677,7 @@ bool QChar::isSymbol() const \since 4.7 Returns true if the UCS-4-encoded character specified by \a ucs4 - is the high part of a utf16 surrogate + is the low part of a utf16 surrogate (ie. if its code point is between 0xdc00 and 0xdfff, inclusive). */ @@ -686,7 +686,7 @@ bool QChar::isSymbol() const \since 4.7 Returns true if the UCS-4-encoded character specified by \a ucs4 - can be splited to the high and low parts of a utf16 surrogate + can be split into the high and low parts of a utf16 surrogate (ie. if its code point is greater than or equals to 0x10000). */ @@ -1069,6 +1069,15 @@ QChar::UnicodeVersion QChar::unicodeVersion(ushort ucs2) return (QChar::UnicodeVersion) qGetProp(ucs2)->unicodeVersion; } +/*! + \since 4.8 + + Returns the most recent supported Unicode version. +*/ +QChar::UnicodeVersion QChar::currentUnicodeVersion() +{ + return UNICODE_DATA_VERSION; +} /*! Returns the lowercase equivalent if the character is uppercase or titlecase; @@ -1489,6 +1498,16 @@ static void decomposeHelper(QString *str, bool canonical, QChar::UnicodeVersion } +struct UCS2Pair { + ushort u1; + ushort u2; +}; + +inline bool operator<(ushort u1, const UCS2Pair &ligature) +{ return u1 < ligature.u1; } +inline bool operator<(const UCS2Pair &ligature, ushort u1) +{ return ligature.u1 < u1; } + static ushort ligatureHelper(ushort u1, ushort u2) { // hangul L-V pair @@ -1511,12 +1530,14 @@ static ushort ligatureHelper(ushort u1, ushort u2) if (index == 0xffff) return 0; const unsigned short *ligatures = uc_ligature_map+index; - ushort length = *ligatures; - ++ligatures; - // ### use bsearch - for (uint i = 0; i < length; ++i) - if (ligatures[2*i] == u1) - return ligatures[2*i+1]; + ushort length = *ligatures++; + { + const UCS2Pair *data = reinterpret_cast<const UCS2Pair *>(ligatures); + const UCS2Pair *r = qBinaryFind(data, data + length, u1); + if (r != data + length) + return r->u2; + } + return 0; } diff --git a/src/corelib/tools/qchar.h b/src/corelib/tools/qchar.h index 441c89f..5080536 100644 --- a/src/corelib/tools/qchar.h +++ b/src/corelib/tools/qchar.h @@ -105,7 +105,7 @@ public: enum Category { - NoCategory, + NoCategory, // ### Qt 5: replace with Other_NotAssigned Mark_NonSpacing, // Mn Mark_SpacingCombining, // Mc @@ -144,7 +144,7 @@ public: Symbol_Modifier, // Sk Symbol_Other, // So - Punctuation_Dask = Punctuation_Dash // oops + Punctuation_Dask = Punctuation_Dash // ### Qt 5: remove }; enum Direction @@ -210,7 +210,7 @@ public: }; enum UnicodeVersion { - Unicode_Unassigned, + Unicode_Unassigned, // ### Qt 5: assign with some constantly big value Unicode_1_1, Unicode_2_0, Unicode_2_1_2, @@ -334,6 +334,8 @@ public: static UnicodeVersion QT_FASTCALL unicodeVersion(uint ucs4); static UnicodeVersion QT_FASTCALL unicodeVersion(ushort ucs2); + static UnicodeVersion QT_FASTCALL currentUnicodeVersion(); + static QString QT_FASTCALL decomposition(uint ucs4); #ifdef QT3_SUPPORT diff --git a/src/corelib/tools/qcontiguouscache.cpp b/src/corelib/tools/qcontiguouscache.cpp index 6197d90..8e1c4c4 100644 --- a/src/corelib/tools/qcontiguouscache.cpp +++ b/src/corelib/tools/qcontiguouscache.cpp @@ -194,6 +194,13 @@ MyRecord record(int row) const Assigns \a other to this cache and returns a reference to this cache. */ +/*! \fn void QContiguousCache::swap(QContiguousCache<T> &other) + \since 4.8 + + Swaps cache \a other with this cache. This operation is very + fast and never fails. +*/ + /*! \fn bool QContiguousCache::operator==(const QContiguousCache<T> &other) const Returns true if \a other is equal to this cache; otherwise returns false. diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index 1d6fdf4..7880c2e 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -110,6 +110,11 @@ public: inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } QContiguousCache<T> &operator=(const QContiguousCache<T> &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QContiguousCache<T> &operator=(QContiguousCache<T> &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QContiguousCache<T> &other) { qSwap(d, other.d); } bool operator==(const QContiguousCache<T> &other) const; inline bool operator!=(const QContiguousCache<T> &other) const { return !(*this == other); } diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index c167ee3..a3a8884 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -3426,8 +3426,9 @@ QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f) QString tz = parts.at(5); if (!tz.startsWith(QLatin1String("GMT"), Qt::CaseInsensitive)) return QDateTime(); - int tzoffset = 0; + QDateTime dt(date, time, Qt::UTC); if (tz.length() > 3) { + int tzoffset = 0; QChar sign = tz.at(3); if ((sign != QLatin1Char('+')) && (sign != QLatin1Char('-'))) { @@ -3442,8 +3443,9 @@ QDateTime QDateTime::fromString(const QString& s, Qt::DateFormat f) tzoffset = (tzhour*60 + tzminute) * 60; if (sign == QLatin1Char('-')) tzoffset = -tzoffset; + dt.setUtcOffset(tzoffset); } - return QDateTime(date, time, Qt::UTC).addSecs(-tzoffset).toLocalTime(); + return dt.toLocalTime(); } #endif //QT_NO_TEXTDATE } @@ -5838,6 +5840,41 @@ bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::S return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count); } +#ifdef Q_OS_SYMBIAN +const static TTime UnixEpochOffset(I64LIT(0xdcddb30f2f8000)); +const static TInt64 MinimumMillisecondTime(KMinTInt64 / 1000); +const static TInt64 MaximumMillisecondTime(KMaxTInt64 / 1000); +QDateTime qt_symbian_TTime_To_QDateTime(const TTime& time) +{ + TTimeIntervalMicroSeconds absolute = time.MicroSecondsFrom(UnixEpochOffset); + + return QDateTime::fromMSecsSinceEpoch(absolute.Int64() / 1000); +} + +TTime qt_symbian_QDateTime_To_TTime(const QDateTime& datetime) +{ + qint64 absolute = datetime.toMSecsSinceEpoch(); + if(absolute > MaximumMillisecondTime) + return TTime(KMaxTInt64); + if(absolute < MinimumMillisecondTime) + return TTime(KMinTInt64); + return TTime(absolute * 1000); +} + +time_t qt_symbian_TTime_To_time_t(const TTime& time) +{ + TTimeIntervalSeconds interval; + TInt err = time.SecondsFrom(UnixEpochOffset, interval); + if (err || interval.Int() < 0) + return (time_t) 0; + return (time_t) interval.Int(); +} + +TTime qt_symbian_time_t_To_TTime(time_t time) +{ + return UnixEpochOffset + TTimeIntervalSeconds(time); +} +#endif //Q_OS_SYMBIAN #endif // QT_BOOTSTRAPPED diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h index ec21f98..ad4055d 100644 --- a/src/corelib/tools/qdatetime_p.h +++ b/src/corelib/tools/qdatetime_p.h @@ -275,6 +275,12 @@ Q_CORE_EXPORT bool operator==(const QDateTimeParser::SectionNode &s1, const QDat Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::Sections) Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::FieldInfo) +#ifdef Q_OS_SYMBIAN +QDateTime qt_symbian_TTime_To_QDateTime(const TTime& time); +TTime qt_symbian_QDateTime_To_TTime(const QDateTime& datetime); +time_t qt_symbian_TTime_To_time_t(const TTime& time); +TTime qt_symbian_time_t_To_TTime(time_t time); +#endif //Q_OS_SYMBIAN #endif // QT_BOOTSTRAPPED diff --git a/src/corelib/tools/qelapsedtimer.cpp b/src/corelib/tools/qelapsedtimer.cpp index da64359..0b4b63c 100644 --- a/src/corelib/tools/qelapsedtimer.cpp +++ b/src/corelib/tools/qelapsedtimer.cpp @@ -136,10 +136,11 @@ QT_BEGIN_NAMESPACE implementations, to guarantee that the same reference clock is being used. - \value SystemTime The human-readable system time. This clock is not monotonic. - \value MonotonicClock The system's monotonic clock, usually found in Unix systems. This clock is monotonic and does not overflow. - \value TickCounter The system's tick counter, used on Windows and Symbian systems. This clock may overflow. - \value MachAbsoluteTime The Mach kernel's absolute time (Mac OS X). This clock is monotonic and does not overflow. + \value SystemTime The human-readable system time. This clock is not monotonic. + \value MonotonicClock The system's monotonic clock, usually found in Unix systems. This clock is monotonic and does not overflow. + \value TickCounter The system's tick counter, used on Windows and Symbian systems. This clock may overflow. + \value MachAbsoluteTime The Mach kernel's absolute time (Mac OS X). This clock is monotonic and does not overflow. + \value PerformanceCounter The high-resolution performance counter provided by Windows. This clock is monotonic and does not overflow. \section2 SystemTime @@ -163,7 +164,9 @@ QT_BEGIN_NAMESPACE The tick counter clock type is based on the system's or the processor's tick counter, multiplied by the duration of a tick. This clock type is - used on Windows and Symbian platforms. + used on Windows and Symbian platforms. If the high-precision performance + counter is available on Windows, the \tt{PerformanceCounter} clock type + is used instead. The TickCounter clock type is the only clock type that may overflow. Windows Vista and Windows Server 2008 support the extended 64-bit tick @@ -191,6 +194,16 @@ QT_BEGIN_NAMESPACE This clock is monotonic and does not overflow. + \section2 PerformanceCounter + + This clock uses the Windows functions \tt{QueryPerformanceCounter} and + \tt{QueryPerformanceFrequency} to access the system's high-precision + performance counter. Since this counter may not be available on all + systems, QElapsedTimer will fall back to the \tt{TickCounter} clock + automatically, if this clock cannot be used. + + This clock is monotonic and does not overflow. + \sa clockType(), isMonotonic() */ diff --git a/src/corelib/tools/qelapsedtimer.h b/src/corelib/tools/qelapsedtimer.h index 27344f5..3cf4a70 100644 --- a/src/corelib/tools/qelapsedtimer.h +++ b/src/corelib/tools/qelapsedtimer.h @@ -57,7 +57,8 @@ public: SystemTime, MonotonicClock, TickCounter, - MachAbsoluteTime + MachAbsoluteTime, + PerformanceCounter }; static ClockType clockType(); static bool isMonotonic(); @@ -67,6 +68,7 @@ public: void invalidate(); bool isValid() const; + qint64 nsecsElapsed() const; qint64 elapsed() const; bool hasExpired(qint64 timeout) const; diff --git a/src/corelib/tools/qelapsedtimer_generic.cpp b/src/corelib/tools/qelapsedtimer_generic.cpp index 78aef50..508c94f 100644 --- a/src/corelib/tools/qelapsedtimer_generic.cpp +++ b/src/corelib/tools/qelapsedtimer_generic.cpp @@ -103,6 +103,22 @@ qint64 QElapsedTimer::restart() return t1 - old; } +/*! \since 4.8 + + Returns the number of nanoseconds since this QElapsedTimer was last + started. Calling this function in a QElapsedTimer that was invalidated + will result in undefined results. + + On platforms that do not provide nanosecond resolution, the value returned + will be the best estimate available. + + \sa start(), restart(), hasExpired(), invalidate() +*/ +qint64 QElapsedTimer::nsecsElapsed() const +{ + return elapsed() * 1000000; +} + /*! Returns the number of milliseconds since this QElapsedTimer was last started. Calling this function in a QElapsedTimer that was invalidated diff --git a/src/corelib/tools/qelapsedtimer_mac.cpp b/src/corelib/tools/qelapsedtimer_mac.cpp index 42342dd..110549a 100644 --- a/src/corelib/tools/qelapsedtimer_mac.cpp +++ b/src/corelib/tools/qelapsedtimer_mac.cpp @@ -97,6 +97,12 @@ qint64 QElapsedTimer::restart() return absoluteToMSecs(t1 - old); } +qint64 QElapsedTimer::nsecsElapsed() const +{ + uint64_t cpu_time = mach_absolute_time(); + return absoluteToNSecs(cpu_time - t1); +} + qint64 QElapsedTimer::elapsed() const { uint64_t cpu_time = mach_absolute_time(); diff --git a/src/corelib/tools/qelapsedtimer_symbian.cpp b/src/corelib/tools/qelapsedtimer_symbian.cpp index 80b7ca1..b831e03 100644 --- a/src/corelib/tools/qelapsedtimer_symbian.cpp +++ b/src/corelib/tools/qelapsedtimer_symbian.cpp @@ -64,11 +64,6 @@ static quint64 getMicrosecondFromTick() return nanokernel_tick_period * (val | (quint64(highdword) << 32)); } -static quint64 getMillisecondFromTick() -{ - return getMicrosecondFromTick() / 1000; -} - timeval qt_gettime() { timeval tv; @@ -91,36 +86,41 @@ bool QElapsedTimer::isMonotonic() void QElapsedTimer::start() { - t1 = getMillisecondFromTick(); + t1 = getMicrosecondFromTick(); t2 = 0; } qint64 QElapsedTimer::restart() { qint64 oldt1 = t1; - t1 = getMillisecondFromTick(); + t1 = getMicrosecondFromTick(); t2 = 0; return t1 - oldt1; } +qint64 QElapsedTimer::nsecsElapsed() const +{ + return (getMicrosecondFromTick() - t1) * 1000; +} + qint64 QElapsedTimer::elapsed() const { - return getMillisecondFromTick() - t1; + return (getMicrosecondFromTick() - t1) / 1000; } qint64 QElapsedTimer::msecsSinceReference() const { - return t1; + return t1 / 1000; } qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const { - return other.t1 - t1; + return (other.t1 - t1) / 1000; } qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const { - return msecsTo(other) / 1000; + return msecsTo(other) / 1000000; } bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) diff --git a/src/corelib/tools/qelapsedtimer_unix.cpp b/src/corelib/tools/qelapsedtimer_unix.cpp index e26f566..f24c6fb 100644 --- a/src/corelib/tools/qelapsedtimer_unix.cpp +++ b/src/corelib/tools/qelapsedtimer_unix.cpp @@ -167,6 +167,17 @@ qint64 QElapsedTimer::restart() return elapsedAndRestart(t1, t2, &t1, &t2); } +qint64 QElapsedTimer::nsecsElapsed() const +{ + qint64 sec, frac; + do_gettime(&sec, &frac); + sec = sec - t1; + frac = frac - t2; + if (!monotonicClockAvailable) + frac *= 1000; + return sec * Q_INT64_C(1000000000) + frac; +} + qint64 QElapsedTimer::elapsed() const { qint64 sec, frac; diff --git a/src/corelib/tools/qelapsedtimer_win.cpp b/src/corelib/tools/qelapsedtimer_win.cpp index aa5abaf..b3210f1 100644 --- a/src/corelib/tools/qelapsedtimer_win.cpp +++ b/src/corelib/tools/qelapsedtimer_win.cpp @@ -42,6 +42,9 @@ #include "qelapsedtimer.h" #include <windows.h> +// Result of QueryPerformanceFrequency, 0 indicates that the high resolution timer is unavailable +static quint64 counterFrequency = 0; + typedef ULONGLONG (WINAPI *PtrGetTickCount64)(void); static PtrGetTickCount64 ptrGetTickCount64 = 0; @@ -65,12 +68,44 @@ static void resolveLibs() ptrGetTickCount64 = (PtrGetTickCount64)GetProcAddress(kernel32, "GetTickCount64"); #endif + // Retrieve the number of high-resolution performance counter ticks per second + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) { + counterFrequency = 0; + } else { + counterFrequency = frequency.QuadPart; + } + done = true; } +static inline qint64 ticksToNanoseconds(qint64 ticks) +{ + if (counterFrequency > 0) { + // QueryPerformanceCounter uses an arbitrary frequency + return ticks * 1000000000 / counterFrequency; + } else { + // GetTickCount(64) return milliseconds + return ticks * 1000000; + } +} + static quint64 getTickCount() { resolveLibs(); + + // This avoids a division by zero and disables the high performance counter if it's not available + if (counterFrequency > 0) { + LARGE_INTEGER counter; + + if (QueryPerformanceCounter(&counter)) { + return counter.QuadPart; + } else { + qWarning("QueryPerformanceCounter failed, although QueryPerformanceFrequency succeeded."); + return 0; + } + } + if (ptrGetTickCount64) return ptrGetTickCount64(); @@ -85,7 +120,12 @@ static quint64 getTickCount() QElapsedTimer::ClockType QElapsedTimer::clockType() { - return TickCounter; + resolveLibs(); + + if (counterFrequency > 0) + return PerformanceCounter; + else + return TickCounter; } bool QElapsedTimer::isMonotonic() @@ -104,22 +144,30 @@ qint64 QElapsedTimer::restart() qint64 oldt1 = t1; t1 = getTickCount(); t2 = 0; - return t1 - oldt1; + return ticksToNanoseconds(t1 - oldt1) / 1000000; +} + +qint64 QElapsedTimer::nsecsElapsed() const +{ + qint64 elapsed = getTickCount() - t1; + return ticksToNanoseconds(elapsed); } qint64 QElapsedTimer::elapsed() const { - return getTickCount() - t1; + qint64 elapsed = getTickCount() - t1; + return ticksToNanoseconds(elapsed) / 1000000; } qint64 QElapsedTimer::msecsSinceReference() const { - return t1; + return ticksToNanoseconds(t1) / 1000000; } qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const { - return other.t1 - t1; + qint64 difference = other.t1 - t1; + return ticksToNanoseconds(difference) / 1000000; } qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 32463e0..b6ba8b2 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -54,6 +54,10 @@ QT_BEGIN_NAMESPACE + +// ### Qt 5: see tests/benchmarks/corelib/tools/qhash/qhash_string.cpp +// Hashing of the whole string is a waste of cycles. + /* These functions are based on Peter J. Weinberger's hash function (from the Dragon Book). The constant 24 in the original function @@ -730,6 +734,20 @@ void QHashData::checkSanity() Assigns \a other to this hash and returns a reference to this hash. */ +/*! \fn void QHash::swap(QHash<Key, T> &other) + \since 4.8 + + Swaps hash \a other with this hash. This operation is very + fast and never fails. +*/ + +/*! \fn void QMultiHash::swap(QMultiHash<Key, T> &other) + \since 4.8 + + Swaps hash \a other with this hash. This operation is very + fast and never fails. +*/ + /*! \fn bool QHash::operator==(const QHash<Key, T> &other) const Returns true if \a other is equal to this hash; otherwise returns diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 06984bf..4982144 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -225,7 +225,7 @@ struct QHashNode inline bool same_key(uint h0, const Key &key0) { return h0 == h && key0 == key; } }; -#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION + #define Q_HASH_DECLARE_INT_NODES(key_type) \ template <class T> \ struct QHashDummyNode<key_type, T> { \ @@ -253,7 +253,6 @@ Q_HASH_DECLARE_INT_NODES(ushort); Q_HASH_DECLARE_INT_NODES(int); Q_HASH_DECLARE_INT_NODES(uint); #undef Q_HASH_DECLARE_INT_NODES -#endif // QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION template <class Key, class T> class QHash @@ -284,6 +283,11 @@ public: inline ~QHash() { if (!d->ref.deref()) freeData(d); } QHash<Key, T> &operator=(const QHash<Key, T> &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QHash<Key, T> &operator=(QHash<Key, T> &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QHash<Key, T> &other) { qSwap(d, other.d); } bool operator==(const QHash<Key, T> &other) const; inline bool operator!=(const QHash<Key, T> &other) const { return !(*this == other); } @@ -513,8 +517,6 @@ Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode2(QHashData::Node *node) { #ifdef Q_CC_BOR concrete(node)->~QHashNode<Key, T>(); -#elif defined(QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION) - concrete(node)->~QHashNode(); #else concrete(node)->~Node(); #endif @@ -924,6 +926,7 @@ class QMultiHash : public QHash<Key, T> public: QMultiHash() {} QMultiHash(const QHash<Key, T> &other) : QHash<Key, T>(other) {} + inline void swap(QMultiHash<Key, T> &other) { QHash<Key, T>::swap(other); } // prevent QMultiHash<->QHash swaps inline typename QHash<Key, T>::iterator replace(const Key &key, const T &value) { return QHash<Key, T>::insert(key, value); } diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index ec46cc5..724cace 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -147,6 +147,13 @@ QLinkedListData QLinkedListData::shared_null = { list. */ +/*! \fn void QLinkedList::swap(QLinkedList<T> &other) + \since 4.8 + + Swaps list \a other with this list. This operation is very + fast and never fails. +*/ + /*! \fn bool QLinkedList::operator==(const QLinkedList<T> &other) const Returns true if \a other is equal to this list; otherwise returns diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 0737a73..b01dcc4 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -85,6 +85,11 @@ public: inline QLinkedList(const QLinkedList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach(); } ~QLinkedList(); QLinkedList<T> &operator=(const QLinkedList<T> &); +#ifdef Q_COMPILER_RVALUE_REFS + inline QLinkedList<T> &operator=(QLinkedList<T> &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QLinkedList<T> &other) { qSwap(d, other.d); } bool operator==(const QLinkedList<T> &l) const; inline bool operator!=(const QLinkedList<T> &l) const { return !(*this == l); } diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 88b9245..7bbdc16 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -602,6 +602,13 @@ void **QListData::erase(void **xi) list. */ +/*! \fn void QList::swap(QList<T> &other) + \since 4.8 + + Swaps list \a other with this list. This operation is very + fast and never fails. +*/ + /*! \fn bool QList::operator==(const QList<T> &other) const Returns true if \a other is equal to this list; otherwise returns diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index b353148..da70b80 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -50,6 +50,10 @@ #include <iterator> #include <list> #endif +#ifdef Q_COMPILER_INITIALIZER_LISTS +#include <iterator> +#include <initializer_list> +#endif #include <new> #include <limits.h> @@ -118,6 +122,15 @@ public: inline QList(const QList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } ~QList(); QList<T> &operator=(const QList<T> &l); +#ifdef Q_COMPILER_RVALUE_REFS + inline QList &operator=(QList &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QList<T> &other) { qSwap(d, other.d); } +#ifdef Q_COMPILER_INITIALIZER_LISTS + inline QList(std::initializer_list<T> args) : d(&QListData::shared_null) + { d->ref.ref(); qCopy(args.begin(), args.end(), std::back_inserter(*this)); } +#endif bool operator==(const QList<T> &l) const; inline bool operator!=(const QList<T> &l) const { return !(*this == l); } @@ -713,7 +726,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::detach_helper() template <typename T> Q_OUTOFLINE_TEMPLATE QList<T>::~QList() { - if (d && !d->ref.deref()) + if (!d->ref.deref()) free(d); } @@ -741,8 +754,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::free(QListData::Data *data) { node_destruct(reinterpret_cast<Node *>(data->array + data->begin), reinterpret_cast<Node *>(data->array + data->end)); - if (data->ref == 0) - qFree(data); + qFree(data); } diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 2c028af..9e13fcc 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -404,6 +404,20 @@ void QMapData::dump() Assigns \a other to this map and returns a reference to this map. */ +/*! \fn void QMap::swap(QMap<Key, T> &other) + \since 4.8 + + Swaps map \a other with this map. This operation is very + fast and never fails. +*/ + +/*! \fn void QMultiMap::swap(QMultiMap<Key, T> &other) + \since 4.8 + + Swaps map \a other with this map. This operation is very + fast and never fails. +*/ + /*! \fn bool QMap::operator==(const QMap<Key, T> &other) const Returns true if \a other is equal to this map; otherwise returns diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index a302cf8..6ac30dd 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -107,7 +107,6 @@ template <class Key> inline bool qMapLessThanKey(const Key &key1, const Key &key return key1 < key2; } -#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION template <class Ptr> inline bool qMapLessThanKey(Ptr *key1, Ptr *key2) { Q_ASSERT(sizeof(quintptr) == sizeof(Ptr *)); @@ -119,7 +118,6 @@ template <class Ptr> inline bool qMapLessThanKey(const Ptr *key1, const Ptr *key Q_ASSERT(sizeof(quintptr) == sizeof(const Ptr *)); return quintptr(key1) < quintptr(key2); } -#endif // QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION template <class Key, class T> struct QMapNode { @@ -187,6 +185,11 @@ public: inline ~QMap() { if (!d) return; if (!d->ref.deref()) freeData(d); } QMap<Key, T> &operator=(const QMap<Key, T> &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QMap<Key, T> &operator=(QMap<Key, T> &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QMap<Key, T> &other) { qSwap(d, other.d); } #ifndef QT_NO_STL explicit QMap(const typename std::map<Key, T> &other); std::map<Key, T> toStdMap() const; @@ -640,13 +643,13 @@ Q_OUTOFLINE_TEMPLATE void QMap<Key, T>::freeData(QMapData *x) while (next != x) { cur = next; next = cur->forward[0]; -#if defined(_MSC_VER) && (_MSC_VER >= 1300) +#if defined(_MSC_VER) #pragma warning(disable:4189) #endif Node *concreteNode = concrete(reinterpret_cast<QMapData::Node *>(cur)); concreteNode->key.~Key(); concreteNode->value.~T(); -#if defined(_MSC_VER) && (_MSC_VER >= 1300) +#if defined(_MSC_VER) #pragma warning(default:4189) #endif } @@ -971,6 +974,7 @@ class QMultiMap : public QMap<Key, T> public: QMultiMap() {} QMultiMap(const QMap<Key, T> &other) : QMap<Key, T>(other) {} + inline void swap(QMultiMap<Key, T> &other) { QMap<Key, T>::swap(other); } inline typename QMap<Key, T>::iterator replace(const Key &key, const T &value) { return QMap<Key, T>::insert(key, value); } diff --git a/src/corelib/tools/qpoint.cpp b/src/corelib/tools/qpoint.cpp index 63fc498..2a13fc4 100644 --- a/src/corelib/tools/qpoint.cpp +++ b/src/corelib/tools/qpoint.cpp @@ -187,7 +187,19 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QPoint &QPoint::operator*=(qreal factor) + \fn QPoint &QPoint::operator*=(float factor) + + Multiplies this point's coordinates by the given \a factor, and + returns a reference to this point. + + Note that the result is rounded to the nearest integer as points are held as + integers. Use QPointF for floating point accuracy. + + \sa operator/=() +*/ + +/*! + \fn QPoint &QPoint::operator*=(double factor) Multiplies this point's coordinates by the given \a factor, and returns a reference to this point. For example: @@ -200,6 +212,14 @@ QT_BEGIN_NAMESPACE \sa operator/=() */ +/*! + \fn QPoint &QPoint::operator*=(int factor) + + Multiplies this point's coordinates by the given \a factor, and + returns a reference to this point. + + \sa operator/=() +*/ /*! \fn bool operator==(const QPoint &p1, const QPoint &p2) @@ -237,7 +257,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn const QPoint operator*(const QPoint &point, qreal factor) + \fn const QPoint operator*(const QPoint &point, float factor) \relates QPoint Returns a copy of the given \a point multiplied by the given \a factor. @@ -249,7 +269,44 @@ QT_BEGIN_NAMESPACE */ /*! - \fn const QPoint operator*(qreal factor, const QPoint &point) + \fn const QPoint operator*(const QPoint &point, double factor) + \relates QPoint + + Returns a copy of the given \a point multiplied by the given \a factor. + + Note that the result is rounded to the nearest integer as points + are held as integers. Use QPointF for floating point accuracy. + + \sa QPoint::operator*=() +*/ + +/*! + \fn const QPoint operator*(const QPoint &point, int factor) + \relates QPoint + + Returns a copy of the given \a point multiplied by the given \a factor. + + \sa QPoint::operator*=() +*/ + +/*! + \fn const QPoint operator*(float factor, const QPoint &point) + \overload + \relates QPoint + + Returns a copy of the given \a point multiplied by the given \a factor. +*/ + +/*! + \fn const QPoint operator*(double factor, const QPoint &point) + \overload + \relates QPoint + + Returns a copy of the given \a point multiplied by the given \a factor. +*/ + +/*! + \fn const QPoint operator*(int factor, const QPoint &point) \overload \relates QPoint diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h index 719c239..4496127 100644 --- a/src/corelib/tools/qpoint.h +++ b/src/corelib/tools/qpoint.h @@ -70,15 +70,23 @@ public: QPoint &operator+=(const QPoint &p); QPoint &operator-=(const QPoint &p); - QPoint &operator*=(qreal c); + + QPoint &operator*=(float c); + QPoint &operator*=(double c); + QPoint &operator*=(int c); + QPoint &operator/=(qreal c); friend inline bool operator==(const QPoint &, const QPoint &); friend inline bool operator!=(const QPoint &, const QPoint &); friend inline const QPoint operator+(const QPoint &, const QPoint &); friend inline const QPoint operator-(const QPoint &, const QPoint &); - friend inline const QPoint operator*(const QPoint &, qreal); - friend inline const QPoint operator*(qreal, const QPoint &); + friend inline const QPoint operator*(const QPoint &, float); + friend inline const QPoint operator*(float, const QPoint &); + friend inline const QPoint operator*(const QPoint &, double); + friend inline const QPoint operator*(double, const QPoint &); + friend inline const QPoint operator*(const QPoint &, int); + friend inline const QPoint operator*(int, const QPoint &); friend inline const QPoint operator-(const QPoint &); friend inline const QPoint operator/(const QPoint &, qreal); @@ -141,9 +149,15 @@ inline QPoint &QPoint::operator+=(const QPoint &p) inline QPoint &QPoint::operator-=(const QPoint &p) { xp-=p.xp; yp-=p.yp; return *this; } -inline QPoint &QPoint::operator*=(qreal c) +inline QPoint &QPoint::operator*=(float c) +{ xp = qRound(xp*c); yp = qRound(yp*c); return *this; } + +inline QPoint &QPoint::operator*=(double c) { xp = qRound(xp*c); yp = qRound(yp*c); return *this; } +inline QPoint &QPoint::operator*=(int c) +{ xp = xp*c; yp = yp*c; return *this; } + inline bool operator==(const QPoint &p1, const QPoint &p2) { return p1.xp == p2.xp && p1.yp == p2.yp; } @@ -156,12 +170,24 @@ inline const QPoint operator+(const QPoint &p1, const QPoint &p2) inline const QPoint operator-(const QPoint &p1, const QPoint &p2) { return QPoint(p1.xp-p2.xp, p1.yp-p2.yp); } -inline const QPoint operator*(const QPoint &p, qreal c) +inline const QPoint operator*(const QPoint &p, float c) { return QPoint(qRound(p.xp*c), qRound(p.yp*c)); } -inline const QPoint operator*(qreal c, const QPoint &p) +inline const QPoint operator*(const QPoint &p, double c) { return QPoint(qRound(p.xp*c), qRound(p.yp*c)); } +inline const QPoint operator*(const QPoint &p, int c) +{ return QPoint(p.xp*c, p.yp*c); } + +inline const QPoint operator*(float c, const QPoint &p) +{ return QPoint(qRound(p.xp*c), qRound(p.yp*c)); } + +inline const QPoint operator*(double c, const QPoint &p) +{ return QPoint(qRound(p.xp*c), qRound(p.yp*c)); } + +inline const QPoint operator*(int c, const QPoint &p) +{ return QPoint(p.xp*c, p.yp*c); } + inline const QPoint operator-(const QPoint &p) { return QPoint(-p.xp, -p.yp); } diff --git a/src/corelib/tools/qqueue.cpp b/src/corelib/tools/qqueue.cpp index 922aee7..a256b03 100644 --- a/src/corelib/tools/qqueue.cpp +++ b/src/corelib/tools/qqueue.cpp @@ -91,6 +91,14 @@ */ /*! + \fn void QQueue::swap(QQueue<T> &other) + \since 4.8 + + Swaps queue \a other with this queue. This operation is very + fast and never fails. +*/ + +/*! \fn void QQueue::enqueue(const T& t) Adds value \a t to the tail of the queue. diff --git a/src/corelib/tools/qqueue.h b/src/corelib/tools/qqueue.h index 1916cd3..c0d5ce8 100644 --- a/src/corelib/tools/qqueue.h +++ b/src/corelib/tools/qqueue.h @@ -56,6 +56,7 @@ class QQueue : public QList<T> public: inline QQueue() {} inline ~QQueue() {} + inline void swap(QQueue<T> &other) { QList<T>::swap(other); } // prevent QList<->QQueue swaps inline void enqueue(const T &t) { QList<T>::append(t); } inline T dequeue() { return QList<T>::takeFirst(); } inline T &head() { return QList<T>::first(); } diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index c46d170..486a5df 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -3858,6 +3858,14 @@ QRegExp &QRegExp::operator=(const QRegExp &rx) } /*! + \fn void QRegExp::swap(QRegExp &other) + \since 4.8 + + Swaps regular expression \a other with this regular + expression. This operation is very fast and never fails. +*/ + +/*! Returns true if this regular expression is equal to \a rx; otherwise returns false. @@ -4168,6 +4176,8 @@ int QRegExp::matchedLength() const } #ifndef QT_NO_REGEXP_CAPTURE + +#ifndef QT_NO_DEPRECATED /*! \obsolete Returns the number of captures contained in the regular expression. @@ -4178,6 +4188,7 @@ int QRegExp::numCaptures() const { return captureCount(); } +#endif /*! \since 4.6 diff --git a/src/corelib/tools/qregexp.h b/src/corelib/tools/qregexp.h index 1cf8522..98d82f9 100644 --- a/src/corelib/tools/qregexp.h +++ b/src/corelib/tools/qregexp.h @@ -76,6 +76,11 @@ public: QRegExp(const QRegExp &rx); ~QRegExp(); QRegExp &operator=(const QRegExp &rx); +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegExp &operator=(QRegExp &&other) + { qSwap(priv,other.priv); return *this; } +#endif + inline void swap(QRegExp &other) { qSwap(priv, other.priv); } bool operator==(const QRegExp &rx) const; inline bool operator!=(const QRegExp &rx) const { return !operator==(rx); } diff --git a/src/corelib/tools/qscopedpointer.h b/src/corelib/tools/qscopedpointer.h index f8a3613..92bccf5 100644 --- a/src/corelib/tools/qscopedpointer.h +++ b/src/corelib/tools/qscopedpointer.h @@ -54,7 +54,7 @@ struct QScopedPointerDeleter static inline void cleanup(T *pointer) { // Enforce a complete type. - // If you get a compile error here, read the secion on forward declared + // If you get a compile error here, read the section on forward declared // classes in the QScopedPointer documentation. typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; (void) sizeof(IsIncompleteType); @@ -69,7 +69,7 @@ struct QScopedPointerArrayDeleter static inline void cleanup(T *pointer) { // Enforce a complete type. - // If you get a compile error here, read the secion on forward declared + // If you get a compile error here, read the section on forward declared // classes in the QScopedPointer documentation. typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; (void) sizeof(IsIncompleteType); @@ -186,11 +186,30 @@ template <class T, class Cleanup> Q_INLINE_TEMPLATE void qSwap(QScopedPointer<T, Cleanup> &p1, QScopedPointer<T, Cleanup> &p2) { p1.swap(p2); } +#ifndef QT_NO_STL +QT_END_NAMESPACE +namespace std { + template <class T, class Cleanup> + Q_INLINE_TEMPLATE void swap(QT_PREPEND_NAMESPACE(QScopedPointer)<T, Cleanup> &p1, QT_PREPEND_NAMESPACE(QScopedPointer)<T, Cleanup> &p2) + { p1.swap(p2); } +} +QT_BEGIN_NAMESPACE +#endif + + + +namespace QtPrivate { + template <typename X, typename Y> struct QScopedArrayEnsureSameType; + template <typename X> struct QScopedArrayEnsureSameType<X,X> { typedef X* Type; }; + template <typename X> struct QScopedArrayEnsureSameType<const X, X> { typedef X* Type; }; +} + template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> > class QScopedArrayPointer : public QScopedPointer<T, Cleanup> { public: - explicit inline QScopedArrayPointer(T *p = 0) + template <typename D> + explicit inline QScopedArrayPointer(D *p = 0, typename QtPrivate::QScopedArrayEnsureSameType<T,D>::Type = 0) : QScopedPointer<T, Cleanup>(p) { } @@ -206,6 +225,17 @@ public: } private: + explicit inline QScopedArrayPointer(void *) { + // Enforce the same type. + + // If you get a compile error here, make sure you declare + // QScopedArrayPointer with the same template type as you pass to the + // constructor. See also the QScopedPointer documentation. + + // Storing a scalar array as a pointer to a different type is not + // allowed and results in undefined behavior. + } + Q_DISABLE_COPY(QScopedArrayPointer) }; diff --git a/src/corelib/tools/qscopedvaluerollback.cpp b/src/corelib/tools/qscopedvaluerollback.cpp new file mode 100644 index 0000000..8933efc --- /dev/null +++ b/src/corelib/tools/qscopedvaluerollback.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscopedvaluerollback.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QScopedValueRollback + \brief The QScopedValueRollback resets a variable to its previous value on destruction + \since 4.8 + \ingroup misc + + The QScopedAssignment class can be used to revert state when an + exception is thrown without needing to write try-catch blocks. + + It can also be used to manage variables that are temporarily set, + such as reentrancy guards. By using this class, the variable will + be reset whether the function is exited normally, exited early by + a return statement, or exited by an exception. + + The template can only be instantiated with a type that supports assignment. + + \sa QScopedPointer +*/ + +/*! + \fn QScopedValueRollback::QScopedValueRollback(T &var) + + Stores the previous value of var internally, for revert on destruction. +*/ + +/*! + \fn QScopedValueRollback::~QScopedValueRollback() + + Assigns the previous value to the managed variable. + This is the value at construction time, or at the last call to commit() +*/ + +/*! + \fn void QScopedValueRollback::commit() + + Updates the previous value of the managed variable to its current value. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qscopedvaluerollback.h b/src/corelib/tools/qscopedvaluerollback.h new file mode 100644 index 0000000..e874428 --- /dev/null +++ b/src/corelib/tools/qscopedvaluerollback.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCOPEDVALUEROLLBACK_H +#define QSCOPEDVALUEROLLBACK_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +QT_MODULE(Core) + +template <typename T> +class QScopedValueRollback +{ +public: + QScopedValueRollback(T &var) : + varRef(var) + { + oldValue = varRef; + } + + ~QScopedValueRollback() + { + varRef = oldValue; + } + + void commit() + { + oldValue = varRef; + } + +private: + T& varRef; + T oldValue; + + Q_DISABLE_COPY(QScopedValueRollback) +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QSCOPEDVALUEROLLBACK_H diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 1074f99..a2a8052 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -61,6 +61,11 @@ public: inline QSet<T> &operator=(const QSet<T> &other) { q_hash = other.q_hash; return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + inline QSet<T> &operator=(QSet<T> &&other) + { qSwap(q_hash, other.q_hash); return *this; } +#endif + inline void swap(QSet<T> &other) { q_hash.swap(other.q_hash); } inline bool operator==(const QSet<T> &other) const { return q_hash == other.q_hash; } diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc index 5249182..8748fd9 100644 --- a/src/corelib/tools/qset.qdoc +++ b/src/corelib/tools/qset.qdoc @@ -123,6 +123,13 @@ */ /*! + \fn void QSet::swap(QSet<T> &other) + + Swaps set \a other with this set. This operation is very fast and + never fails. +*/ + +/*! \fn bool QSet::operator==(const QSet<T> &other) const Returns true if the \a other set is equal to this set; otherwise diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h index 0b36c60..1494373 100644 --- a/src/corelib/tools/qshareddata.h +++ b/src/corelib/tools/qshareddata.h @@ -113,6 +113,11 @@ public: } return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + QSharedDataPointer(QSharedDataPointer &&o) : d(o.d) { o.d = 0; } + inline QSharedDataPointer<T> &operator=(QSharedDataPointer<T> &&other) + { qSwap(d, other.d); return *this; } +#endif inline bool operator!() const { return !d; } @@ -163,14 +168,12 @@ public: explicit QExplicitlySharedDataPointer(T *data); inline QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<T> &o) : d(o.d) { if (d) d->ref.ref(); } -#ifndef QT_NO_MEMBER_TEMPLATES template<class X> inline QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) : d(static_cast<T *>(o.data())) { if(d) d->ref.ref(); } -#endif inline QExplicitlySharedDataPointer<T> & operator=(const QExplicitlySharedDataPointer<T> &o) { if (o.d != d) { @@ -194,6 +197,11 @@ public: } return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + inline QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) : d(o.d) { o.d = 0; } + inline QExplicitlySharedDataPointer<T> &operator=(QExplicitlySharedDataPointer<T> &&other) + { qSwap(d, other.d); return *this; } +#endif inline bool operator!() const { return !d; } @@ -257,6 +265,20 @@ template <class T> Q_INLINE_TEMPLATE void qSwap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2) { p1.swap(p2); } +#ifndef QT_NO_STL +QT_END_NAMESPACE +namespace std { + template <class T> + Q_INLINE_TEMPLATE void swap(QT_PREPEND_NAMESPACE(QSharedDataPointer)<T> &p1, QT_PREPEND_NAMESPACE(QSharedDataPointer)<T> &p2) + { p1.swap(p2); } + + template <class T> + Q_INLINE_TEMPLATE void swap(QT_PREPEND_NAMESPACE(QExplicitlySharedDataPointer)<T> &p1, QT_PREPEND_NAMESPACE(QExplicitlySharedDataPointer)<T> &p2) + { p1.swap(p2); } +} +QT_BEGIN_NAMESPACE +#endif + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 36131b1..b1f77b4 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -1275,8 +1275,6 @@ QT_END_NAMESPACE -#if !defined(QT_NO_MEMBER_TEMPLATES) - //# define QT_SHARED_POINTER_BACKTRACE_SUPPORT # ifdef QT_SHARED_POINTER_BACKTRACE_SUPPORT # if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE) @@ -1501,5 +1499,3 @@ void QtSharedPointer::internalSafetyCheckCleanCheck() } QT_END_NAMESPACE - -#endif diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h index 899bd1a..a57abb3 100644 --- a/src/corelib/tools/qsharedpointer.h +++ b/src/corelib/tools/qsharedpointer.h @@ -47,10 +47,7 @@ #include <QtCore/qshareddata.h> #ifndef Q_QDOC -# if !defined(QT_NO_MEMBER_TEMPLATES) -// QSharedPointer requires member template support # include <QtCore/qsharedpointer_impl.h> -# endif #else QT_BEGIN_HEADER diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 0337c1f..b29e5b6 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -119,7 +119,6 @@ namespace QtSharedPointer { template <class T> inline void normalDeleter(T *t) { delete t; } // this uses partial template specialization - // the only compilers that didn't support this were MSVC 6.0 and 2002 template <class T> struct RemovePointer; template <class T> struct RemovePointer<T *> { typedef T Type; }; template <class T> struct RemovePointer<QSharedPointer<T> > { typedef T Type; }; @@ -333,7 +332,6 @@ namespace QtSharedPointer { protected: typedef ExternalRefCountData Data; - inline void ref() const { d->weakref.ref(); d->strongref.ref(); } inline void deref() { deref(d, this->value); } static inline void deref(Data *d, T *value) @@ -405,7 +403,13 @@ namespace QtSharedPointer { template <class X> inline void internalCopy(const ExternalRefCount<X> &other) { - internalSet(other.d, other.data()); + Data *o = other.d; + T *actual = other.value; + if (o) + other.ref(); + qSwap(d, o); + qSwap(this->value, actual); + deref(o, actual); } inline void internalSwap(ExternalRefCount &other) @@ -421,6 +425,7 @@ namespace QtSharedPointer { template <class X> friend class QT_PREPEND_NAMESPACE(QWeakPointer); template <class X, class Y> friend QSharedPointer<X> copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src); #endif + inline void ref() const { d->weakref.ref(); d->strongref.ref(); } inline void internalSet(Data *o, T *actual) { @@ -478,6 +483,13 @@ public: BaseClass::internalCopy(other); return *this; } +#ifdef Q_COMPILER_RVALUE_REFS + inline QSharedPointer<T> &operator=(QSharedPointer<T> &&other) + { + QSharedPointer<T>::internalSwap(other); + return *this; + } +#endif template <class X> inline QSharedPointer(const QSharedPointer<X> &other) : BaseClass(other) @@ -782,6 +794,16 @@ inline void qSwap(QSharedPointer<T> &p1, QSharedPointer<T> &p2) p1.swap(p2); } +#ifndef QT_NO_STL +QT_END_NAMESPACE +namespace std { + template <class T> + inline void swap(QT_PREPEND_NAMESPACE(QSharedPointer)<T> &p1, QT_PREPEND_NAMESPACE(QSharedPointer)<T> &p2) + { p1.swap(p2); } +} +QT_BEGIN_NAMESPACE +#endif + namespace QtSharedPointer { // helper functions: template <class X, class T> @@ -862,9 +884,13 @@ qobject_cast(const QWeakPointer<T> &src) { return qSharedPointerObjectCast<typename QtSharedPointer::RemovePointer<X>::Type, T>(src); } - #endif + +template<typename T> Q_DECLARE_TYPEINFO_BODY(QWeakPointer<T>, Q_MOVABLE_TYPE); +template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedPointer<T>, Q_MOVABLE_TYPE); + + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index 0aea58c..f96703d 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -72,7 +72,12 @@ QT_BEGIN_NAMESPACE -#if defined (Q_OS_WINCE) +#if defined (Q_OS_NACL) +static inline uint detectProcessorFeatures() +{ + return 0; +} +#elif defined (Q_OS_WINCE) static inline uint detectProcessorFeatures() { uint features = 0; diff --git a/src/corelib/tools/qstack.cpp b/src/corelib/tools/qstack.cpp index 94595ec..f967b63 100644 --- a/src/corelib/tools/qstack.cpp +++ b/src/corelib/tools/qstack.cpp @@ -91,6 +91,14 @@ */ /*! + \fn void QStack::swap(QStack<T> &other) + \since 4.8 + + Swaps stack \a other with this stack. This operation is very fast and + never fails. +*/ + +/*! \fn void QStack::push(const T& t) Adds element \a t to the top of the stack. diff --git a/src/corelib/tools/qstack.h b/src/corelib/tools/qstack.h index 72a2c49..6e10103 100644 --- a/src/corelib/tools/qstack.h +++ b/src/corelib/tools/qstack.h @@ -56,6 +56,7 @@ class QStack : public QVector<T> public: inline QStack() {} inline ~QStack() {} + inline void swap(QStack<T> &other) { QVector<T>::swap(other); } // prevent QVector<->QStack swaps inline void push(const T &t) { QVector<T>::append(t); } T pop(); T &top(); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index d01899d..b7272ec 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -111,7 +111,23 @@ int qFindString(const QChar *haystack, int haystackLen, int from, const QChar *needle, int needleLen, Qt::CaseSensitivity cs); int qFindStringBoyerMoore(const QChar *haystack, int haystackLen, int from, const QChar *needle, int needleLen, Qt::CaseSensitivity cs); - +static inline int qt_last_index_of(const QChar *haystack, int haystackLen, const QChar &needle, + int from, Qt::CaseSensitivity cs); +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, + Qt::CaseSensitivity cs); +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar &needle, Qt::CaseSensitivity cs); +static inline int qt_find_latin1_string(const QChar *hay, int size, const QLatin1String &needle, + int from, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs); // Unicode case-insensitive comparison static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be) @@ -350,11 +366,7 @@ inline char qToLower(char ch) return ch; } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 -const QString::Null QString::null; -#else const QString::Null QString::null = { }; -#endif /*! \macro QT_NO_CAST_FROM_ASCII @@ -703,7 +715,7 @@ const QString::Null QString::null = { }; formats, the \e precision represents the maximum number of significant digits (trailing zeroes are omitted). - \section1 More Efficient String Construction + \section1 More Efficient String Construction Using the QString \c{'+'} operator, it is easy to construct a complex string from multiple substrings. You will often write code @@ -913,7 +925,7 @@ int QString::grow(int size) /*! \since 4.2 - Returns a copy of the \a string, where the encoding of \a string depends on + Returns a copy of the \a string, where the encoding of \a string depends on the size of wchar. If wchar is 4 bytes, the \a string is interpreted as ucs-4, if wchar is 2 bytes it is interpreted as ucs-2. @@ -946,6 +958,24 @@ QString QString::fromWCharArray(const wchar_t *string, int size) \sa utf16(), toAscii(), toLatin1(), toUtf8(), toLocal8Bit() */ +template<typename T> int toUcs4_helper(const unsigned short *uc, int length, T *out) +{ + int i = 0; + for (; i < length; ++i) { + uint u = uc[i]; + if (QChar::isHighSurrogate(u) && i < length-1) { + ushort low = uc[i+1]; + if (QChar::isLowSurrogate(low)) { + ++i; + u = QChar::surrogateToUcs4(u, low); + } + } + *out = T(u); + ++out; + } + return i; +} + /*! \since 4.2 @@ -970,21 +1000,7 @@ int QString::toWCharArray(wchar_t *array) const memcpy(array, utf16(), sizeof(wchar_t)*length()); return length(); } else { - wchar_t *a = array; - const unsigned short *uc = utf16(); - for (int i = 0; i < length(); ++i) { - uint u = uc[i]; - if (QChar::isHighSurrogate(u) && i + 1 < length()) { - ushort low = uc[i+1]; - if (QChar::isLowSurrogate(low)) { - u = QChar::surrogateToUcs4(u, low); - ++i; - } - } - *a = wchar_t(u); - ++a; - } - return a - array; + return toUcs4_helper<wchar_t>(utf16(), length(), array); } } @@ -1159,6 +1175,13 @@ QString::QString(QChar ch) */ +/*! \fn void QString::swap(QString &other) + \since 4.8 + + Swaps string \a other with this string. This operation is very fast and + never fails. +*/ + /*! \fn void QString::detach() \internal @@ -1317,7 +1340,9 @@ void QString::realloc(int alloc) asciiCache->remove(d); } #endif - d = static_cast<Data *>(q_check_ptr(qRealloc(d, sizeof(Data) + alloc * sizeof(QChar)))); + Data *p = static_cast<Data *>(qRealloc(d, sizeof(Data) + alloc * sizeof(QChar))); + Q_CHECK_PTR(p); + d = p; d->alloc = alloc; d->data = d->array; } @@ -2481,14 +2506,10 @@ int QString::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const \sa lastIndexOf(), contains(), count() */ + int QString::indexOf(const QLatin1String &str, int from, Qt::CaseSensitivity cs) const { - int len = qstrlen(str.latin1()); - QVarLengthArray<ushort> s(len); - for (int i = 0; i < len; ++i) - s[i] = str.latin1()[i]; - - return qFindString(unicode(), length(), from, (const QChar *)s.data(), len, cs); + return qt_find_latin1_string(unicode(), size(), str, from, cs); } int qFindString( @@ -2578,6 +2599,23 @@ int QString::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const return findChar(unicode(), length(), ch, from, cs); } +/*! + \since 4.8 + + \overload indexOf() + + Returns the index position of the first occurrence of the string + reference \a str in this string, searching forward from index + position \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. +*/ +int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), length(), from, str.unicode(), str.length(), cs); +} + static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *needle, int sl, Qt::CaseSensitivity cs) { /* @@ -2657,12 +2695,13 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c if (from > delta) from = delta; - return lastIndexOfHelper(d->data, from, str.d->data, str.d->size, cs); } /*! \since 4.5 + \overload lastIndexOf() + Returns the index position of the last occurrence of the string \a str in this string, searching backward from index position \a from. If \a from is -1 (default), the search starts at the last @@ -2710,26 +2749,43 @@ int QString::lastIndexOf(const QLatin1String &str, int from, Qt::CaseSensitivity */ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const { - ushort c = ch.unicode(); - if (from < 0) - from += d->size; - if (from < 0 || from >= d->size) - return -1; - if (from >= 0) { - const ushort *n = d->data + from; - const ushort *b = d->data; - if (cs == Qt::CaseSensitive) { - for (; n >= b; --n) - if (*n == c) - return n - b; - } else { - c = foldCase(c); - for (; n >= b; --n) - if (foldCase(*n) == c) - return n - b; - } + return qt_last_index_of(unicode(), size(), ch, from, cs); } + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string + reference \a str in this string, searching backward from index + position \a from. If \a from is -1 (default), the search starts at + the last character; if \a from is -2, at the next to last character + and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa indexOf(), contains(), count() +*/ +int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = d->size; + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(d->data, from, reinterpret_cast<const ushort*>(str.unicode()), + str.size(), cs); } #ifndef QT_NO_REGEXP @@ -2904,19 +2960,10 @@ QString& QString::replace(const QRegExp &rx, const QString &after) \sa contains(), indexOf() */ + int QString::count(const QString &str, Qt::CaseSensitivity cs) const { - int num = 0; - int i = -1; - if (d->size > 500 && str.d->size > 5) { - QStringMatcher matcher(str, cs); - while ((i = matcher.indexIn(*this, i + 1)) != -1) - ++num; - } else { - while ((i = indexOf(str, i + 1, cs)) != -1) - ++num; - } - return num; + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); } /*! @@ -2924,25 +2971,29 @@ int QString::count(const QString &str, Qt::CaseSensitivity cs) const Returns the number of occurrences of character \a ch in the string. */ + int QString::count(QChar ch, Qt::CaseSensitivity cs) const { - ushort c = ch.unicode(); - int num = 0; - const ushort *i = d->data + d->size; - const ushort *b = d->data; - if (cs == Qt::CaseSensitive) { - while (i != b) - if (*--i == c) - ++num; - } else { - c = foldCase(c); - while (i != b) - if (foldCase(*(--i)) == c) - ++num; + return qt_string_count(unicode(), size(), ch, cs); } - return num; + +/*! + \since 4.8 + \overload count() + Returns the number of (potentially overlapping) occurrences of the + string reference \a str in this string. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa contains(), indexOf() +*/ +int QString::count(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); } + /*! \fn bool QString::contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const Returns true if this string contains an occurrence of the string @@ -2965,6 +3016,18 @@ int QString::count(QChar ch, Qt::CaseSensitivity cs) const character \a ch; otherwise returns false. */ +/*! \fn bool QString::contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \since 4.8 + + Returns true if this string contains an occurrence of the string + reference \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + /*! \fn bool QString::contains(const QRegExp &rx) const \overload contains() @@ -3366,22 +3429,8 @@ QString QString::mid(int position, int n) const */ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.d == &shared_null); - if (d->size == 0) - return s.d->size == 0; - if (s.d->size > d->size) - return false; - if (cs == Qt::CaseSensitive) { - return qMemEquals(d->data, s.d->data, s.d->size); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < s.d->size; ++i) - if (foldCase(d->data[i], last) != foldCase(s.d->data[i], olast)) - return false; - } - return true; + return qt_starts_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } /*! @@ -3389,24 +3438,7 @@ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const */ bool QString::startsWith(const QLatin1String& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.latin1() == 0); - if (d->size == 0) - return !s.latin1() || *s.latin1() == 0; - int slen = qstrlen(s.latin1()); - if (slen > d->size) - return false; - const uchar *latin = (const uchar *)s.latin1(); - if (cs == Qt::CaseSensitive) { - for (int i = 0; i < slen; ++i) - if (d->data[i] != latin[i]) - return false; - } else { - for (int i = 0; i < slen; ++i) - if (foldCase(d->data[i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_starts_with(isNull() ? 0 : unicode(), size(), s, cs); } /*! @@ -3424,6 +3456,23 @@ bool QString::startsWith(const QChar &c, Qt::CaseSensitivity cs) const } /*! + \since 4.8 + \overload + Returns true if the string starts with the string reference \a s; + otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa endsWith() +*/ +bool QString::startsWith(const QStringRef &s, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); +} + +/*! Returns true if the string ends with \a s; otherwise returns false. @@ -3436,49 +3485,34 @@ bool QString::startsWith(const QChar &c, Qt::CaseSensitivity cs) const */ bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.d == &shared_null); - if (d->size == 0) - return s.d->size == 0; - int pos = d->size - s.d->size; - if (pos < 0) - return false; - if (cs == Qt::CaseSensitive) { - return qMemEquals(d->data + pos, s.d->data, s.d->size); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < s.length(); i++) - if (foldCase(d->data[pos+i], last) != foldCase(s.d->data[i], olast)) - return false; + return qt_ends_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } - return true; + +/*! + \since 4.8 + \overload endsWith() + Returns true if the string ends with the string reference \a s; + otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa startsWith() +*/ +bool QString::endsWith(const QStringRef &s, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + s.isNull() ? 0 : s.unicode(), s.size(), cs); } + /*! \overload endsWith() */ bool QString::endsWith(const QLatin1String& s, Qt::CaseSensitivity cs) const { - if (d == &shared_null) - return (s.latin1() == 0); - if (d->size == 0) - return !s.latin1() || *s.latin1() == 0; - int slen = qstrlen(s.latin1()); - int pos = d->size - slen; - const uchar *latin = (const uchar *)s.latin1(); - if (pos < 0) - return false; - if (cs == Qt::CaseSensitive) { - for (int i = 0; i < slen; i++) - if (d->data[pos+i] != latin[i]) - return false; - } else { - for (int i = 0; i < slen; i++) - if (foldCase(d->data[pos+i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_ends_with(isNull() ? 0 : unicode(), size(), s, cs); } /*! @@ -3717,20 +3751,8 @@ QVector<uint> QString::toUcs4() const { QVector<uint> v(length()); uint *a = v.data(); - const unsigned short *uc = utf16(); - for (int i = 0; i < length(); ++i) { - uint u = uc[i]; - if (QChar(u).isHighSurrogate() && i < length()-1) { - ushort low = uc[i+1]; - if (QChar(low).isLowSurrogate()) { - ++i; - u = QChar::surrogateToUcs4(u, low); - } - } - *a = u; - ++a; - } - v.resize(a - v.data()); + int len = toUcs4_helper<uint>(utf16(), length(), a); + v.resize(len); return v; } @@ -6239,28 +6261,32 @@ void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar:: if (version == QChar::Unicode_Unassigned) { version = UNICODE_DATA_VERSION; } else if (version != UNICODE_DATA_VERSION) { - QString &s = *data; + const QString &s = *data; + QChar *d = 0; for (int i = 0; i < NumNormalizationCorrections; ++i) { const NormalizationCorrection &n = uc_normalization_corrections[i]; if (n.version > version) { int pos = from; - if (n.ucs4 > 0xffff) { + if (QChar::requiresSurrogates(n.ucs4)) { ushort ucs4High = QChar::highSurrogate(n.ucs4); ushort ucs4Low = QChar::lowSurrogate(n.ucs4); ushort oldHigh = QChar::highSurrogate(n.old_mapping); ushort oldLow = QChar::lowSurrogate(n.old_mapping); while (pos < s.length() - 1) { if (s.at(pos).unicode() == ucs4High && s.at(pos + 1).unicode() == ucs4Low) { - s[pos] = oldHigh; - s[pos + 1] = oldLow; - ++pos; + if (!d) + d = data->data(); + d[pos] = QChar(oldHigh); + d[++pos] = QChar(oldLow); } ++pos; } } else { while (pos < s.length()) { if (s.at(pos).unicode() == n.ucs4) { - s[pos] = n.old_mapping; + if (!d) + d = data->data(); + d[pos] = QChar(n.old_mapping); } ++pos; } @@ -7445,31 +7471,17 @@ QDataStream &operator<<(QDataStream &out, const QString &str) out << str.toLatin1(); } else { if (!str.isNull() || out.version() < 3) { - int byteOrder = out.byteOrder(); - const QChar* ub = str.unicode(); - static const uint auto_size = 1024; - char t[auto_size]; - char *b; - if (str.length()*sizeof(QChar) > auto_size) { - b = new char[str.length()*sizeof(QChar)]; + if ((out.byteOrder() == QDataStream::BigEndian) == (QSysInfo::ByteOrder == QSysInfo::BigEndian)) { + out.writeBytes(reinterpret_cast<const char *>(str.unicode()), sizeof(QChar) * str.length()); } else { - b = t; - } - int l = str.length(); - char *c=b; - while (l--) { - if (byteOrder == QDataStream::BigEndian) { - *c++ = (char)ub->row(); - *c++ = (char)ub->cell(); - } else { - *c++ = (char)ub->cell(); - *c++ = (char)ub->row(); + QVarLengthArray<ushort> buffer(str.length()); + const ushort *data = reinterpret_cast<const ushort *>(str.constData()); + for (int i = 0; i < str.length(); i++) { + buffer[i] = qbswap(*data); + ++data; } - ub++; + out.writeBytes(reinterpret_cast<const char *>(buffer.data()), sizeof(ushort) * buffer.size()); } - out.writeBytes(b, sizeof(QChar)*str.length()); - if (str.length()*sizeof(QChar) > auto_size) - delete [] b; } else { // write null marker out << (quint32)0xffffffff; @@ -7662,6 +7674,7 @@ QDataStream &operator>>(QDataStream &in, QString &str) Use the startsWith(QString, Qt::CaseSensitive) overload instead. */ + /*! \fn bool QString::endsWith(const QString &s, bool cs) const @@ -8367,4 +8380,717 @@ QStringRef QString::midRef(int position, int n) const return QStringRef(this, position, n); } +/*! + \since 4.8 + + Returns the index position of the first occurrence of the string \a + str in this string reference, searching forward from index position + \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + If \a from is -1, the search starts at the last character; if it is + -2, at the next to last character and so on. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), length(), from, str.unicode(), str.length(), cs); +} + +/*! + \since 4.8 + \overload indexOf() + + Returns the index position of the first occurrence of the + character \a ch in the string reference, searching forward from + index position \a from. Returns -1 if \a ch could not be found. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const +{ + return findChar(unicode(), length(), ch, from, cs); +} + +/*! + \since 4.8 + + Returns the index position of the first occurrence of the string \a + str in this string reference, searching forward from index position + \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + If \a from is -1, the search starts at the last character; if it is + -2, at the next to last character and so on. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const +{ + return qt_find_latin1_string(unicode(), size(), str, from, cs); +} + +/*! + \since 4.8 + + \overload indexOf() + + Returns the index position of the first occurrence of the string + reference \a str in this string reference, searching forward from + index position \a from. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::indexOf(), lastIndexOf(), contains(), count() +*/ +int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + return qFindString(unicode(), size(), from, str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + + Returns the index position of the last occurrence of the string \a + str in this string reference, searching backward from index position + \a from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = size();; + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(reinterpret_cast<const ushort*>(unicode()), from, + reinterpret_cast<const ushort*>(str.unicode()), str.size(), cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the character + \a ch, searching backward from position \a from. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const +{ + return qt_last_index_of(unicode(), size(), ch, from, cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string \a + str in this string reference, searching backward from index position + \a from. If \a from is -1 (default), the search starts at the last + character; if \a from is -2, at the next to last character and so + on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = qstrlen(str.latin1()); + if (sl == 1) + return lastIndexOf(QLatin1Char(str.latin1()[0]), from, cs); + + const int l = size(); + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + QVarLengthArray<ushort> s(sl); + for (int i = 0; i < sl; ++i) + s[i] = str.latin1()[i]; + + return lastIndexOfHelper(reinterpret_cast<const ushort*>(unicode()), from, s.data(), sl, cs); +} + +/*! + \since 4.8 + \overload lastIndexOf() + + Returns the index position of the last occurrence of the string + reference \a str in this string reference, searching backward from + index position \a from. If \a from is -1 (default), the search + starts at the last character; if \a from is -2, at the next to last + character and so on. Returns -1 if \a str is not found. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::lastIndexOf(), indexOf(), contains(), count() +*/ +int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const +{ + const int sl = str.size(); + if (sl == 1) + return lastIndexOf(str.at(0), from, cs); + + const int l = size(); + if (from < 0) + from += l; + int delta = l - sl; + if (from == l && sl == 0) + return from; + if (from < 0 || from >= l || delta < 0) + return -1; + if (from > delta) + from = delta; + + return lastIndexOfHelper(reinterpret_cast<const ushort*>(unicode()), from, + reinterpret_cast<const ushort*>(str.unicode()), + str.size(), cs); +} + +/*! + \since 4.8 + Returns the number of (potentially overlapping) occurrences of + the string \a str in this string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload count() + + Returns the number of occurrences of the character \a ch in the + string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(QChar ch, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), ch, cs); +} + +/*! + \since 4.8 + \overload count() + + Returns the number of (potentially overlapping) occurrences of the + string reference \a str in this string reference. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::count(), contains(), indexOf() +*/ +int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_string_count(unicode(), size(), str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + + Returns true if the string reference starts with \a str; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload startsWith() + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(QLatin1String str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), str, cs); +} + +/*! + \since 4.8 + \overload startsWith() + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_starts_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload startsWith() + + Returns true if the string reference starts with \a ch; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::startsWith(), endsWith() +*/ +bool QStringRef::startsWith(QChar ch, Qt::CaseSensitivity cs) const +{ + if (!isEmpty()) { + const ushort *data = reinterpret_cast<const ushort*>(unicode()); + return (cs == Qt::CaseSensitive + ? data[0] == ch + : foldCase(data[0]) == foldCase(ch.unicode())); + } else { + return false; + } +} + +/*! + \since 4.8 + Returns true if the string reference ends with \a str; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::endsWith(), startsWith() +*/ +bool QStringRef::endsWith(const QString &str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + +/*! + \since 4.8 + \overload endsWith() + + Returns true if the string reference ends with \a ch; otherwise + returns false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(QChar ch, Qt::CaseSensitivity cs) const +{ + if (!isEmpty()) { + const ushort *data = reinterpret_cast<const ushort*>(unicode()); + const int size = length(); + return (cs == Qt::CaseSensitive + ? data[size - 1] == ch + : foldCase(data[size - 1]) == foldCase(ch.unicode())); + } else { + return false; + } +} + +/*! + \since 4.8 + \overload endsWith() + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(QLatin1String str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), str, cs); +} + +/*! + \since 4.8 + \overload endsWith() + \sa QString::endsWith(), endsWith() +*/ +bool QStringRef::endsWith(const QStringRef &str, Qt::CaseSensitivity cs) const +{ + return qt_ends_with(isNull() ? 0 : unicode(), size(), + str.isNull() ? 0 : str.unicode(), str.size(), cs); +} + + +/*! \fn bool QStringRef::contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + + \since 4.8 + Returns true if this string reference contains an occurrence of + the string \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +/*! \fn bool QStringRef::contains(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + + \overload contains() + \since 4.8 + + Returns true if this string contains an occurrence of the + character \a ch; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + +*/ + +/*! \fn bool QStringRef::contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \overload contains() + \since 4.8 + + Returns true if this string reference contains an occurrence of + the string reference \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +/*! \fn bool QStringRef::contains(QLatin1String str, Qt::CaseSensitivity cs) const + \since 4,8 + \overload contains() + + Returns true if this string reference contains an occurrence of + the string \a str; otherwise returns false. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa indexOf(), count() +*/ + +static inline int qt_last_index_of(const QChar *haystack, int haystackLen, const QChar &needle, + int from, Qt::CaseSensitivity cs) +{ + ushort c = needle.unicode(); + if (from < 0) + from += haystackLen; + if (from < 0 || from >= haystackLen) + return -1; + if (from >= 0) { + const ushort *b = reinterpret_cast<const ushort*>(haystack); + const ushort *n = b + from; + if (cs == Qt::CaseSensitive) { + for (; n >= b; --n) + if (*n == c) + return n - b; + } else { + c = foldCase(c); + for (; n >= b; --n) + if (foldCase(*n) == c) + return n - b; + } + } + return -1; + + +} + +static inline int qt_string_count(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, + Qt::CaseSensitivity cs) +{ + int num = 0; + int i = -1; + if (haystackLen > 500 && needleLen > 5) { + QStringMatcher matcher(needle, needleLen, cs); + while ((i = matcher.indexIn(haystack, haystackLen, i + 1)) != -1) + ++num; + } else { + while ((i = qFindString(haystack, haystackLen, i + 1, needle, needleLen, cs)) != -1) + ++num; + } + return num; +} + +static inline int qt_string_count(const QChar *unicode, int size, const QChar &ch, + Qt::CaseSensitivity cs) +{ + ushort c = ch.unicode(); + int num = 0; + const ushort *b = reinterpret_cast<const ushort*>(unicode); + const ushort *i = b + size; + if (cs == Qt::CaseSensitive) { + while (i != b) + if (*--i == c) + ++num; + } else { + c = foldCase(c); + while (i != b) + if (foldCase(*(--i)) == c) + ++num; + } + return num; +} + +static inline int qt_find_latin1_string(const QChar *haystack, int size, + const QLatin1String &needle, + int from, Qt::CaseSensitivity cs) +{ + const char *latin1 = needle.latin1(); + int len = qstrlen(latin1); + QVarLengthArray<ushort> s(len); + for (int i = 0; i < len; ++i) + s[i] = latin1[i]; + + return qFindString(haystack, size, from, + reinterpret_cast<const QChar*>(s.constData()), len, cs); +} + +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle; + if (haystackLen == 0) + return needleLen == 0; + if (needleLen > haystackLen) + return false; + + const ushort *h = reinterpret_cast<const ushort*>(haystack); + const ushort *n = reinterpret_cast<const ushort*>(needle); + + if (cs == Qt::CaseSensitive) { + return qMemEquals(h, n, needleLen); + } else { + uint last = 0; + uint olast = 0; + for (int i = 0; i < needleLen; ++i) + if (foldCase(h[i], last) != foldCase(n[i], olast)) + return false; + } + return true; +} + +static inline bool qt_starts_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle.latin1(); + if (haystackLen == 0) + return !needle.latin1() || *needle.latin1() == 0; + const int slen = qstrlen(needle.latin1()); + if (slen > haystackLen) + return false; + const ushort *data = reinterpret_cast<const ushort*>(haystack); + const uchar *latin = reinterpret_cast<const uchar*>(needle.latin1()); + if (cs == Qt::CaseSensitive) { + for (int i = 0; i < slen; ++i) + if (data[i] != latin[i]) + return false; + } else { + for (int i = 0; i < slen; ++i) + if (foldCase(data[i]) != foldCase((ushort)latin[i])) + return false; + } + return true; +} + +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle; + if (haystackLen == 0) + return needleLen == 0; + const int pos = haystackLen - needleLen; + if (pos < 0) + return false; + + const ushort *h = reinterpret_cast<const ushort*>(haystack); + const ushort *n = reinterpret_cast<const ushort*>(needle); + + if (cs == Qt::CaseSensitive) { + return qMemEquals(h + pos, n, needleLen); + } else { + uint last = 0; + uint olast = 0; + for (int i = 0; i < needleLen; i++) + if (foldCase(h[pos+i], last) != foldCase(n[i], olast)) + return false; + } + return true; +} + + +static inline bool qt_ends_with(const QChar *haystack, int haystackLen, + const QLatin1String &needle, Qt::CaseSensitivity cs) +{ + if (!haystack) + return !needle.latin1(); + if (haystackLen == 0) + return !needle.latin1() || *needle.latin1() == 0; + const int slen = qstrlen(needle.latin1()); + int pos = haystackLen - slen; + if (pos < 0) + return false; + const uchar *latin = reinterpret_cast<const uchar*>(needle.latin1()); + const ushort *data = reinterpret_cast<const ushort*>(haystack); + if (cs == Qt::CaseSensitive) { + for (int i = 0; i < slen; i++) + if (data[pos+i] != latin[i]) + return false; + } else { + for (int i = 0; i < slen; i++) + if (foldCase(data[pos+i]) != foldCase((ushort)latin[i])) + return false; + } + return true; +} + +/*! + \since 4.8 + + Returns a Latin-1 representation of the string as a QByteArray. + + The returned byte array is undefined if the string contains non-Latin1 + characters. Those characters may be suppressed or replaced with a + question mark. + + \sa toAscii(), toUtf8(), toLocal8Bit(), QTextCodec +*/ +QByteArray QStringRef::toLatin1() const +{ + return toLatin1_helper(unicode(), length()); +} + +/*! + \since 4.8 + + Returns an 8-bit representation of the string as a QByteArray. + + If a codec has been set using QTextCodec::setCodecForCStrings(), + it is used to convert Unicode to 8-bit char; otherwise this + function does the same as toLatin1(). + + Note that, despite the name, this function does not necessarily return an US-ASCII + (ANSI X3.4-1986) string and its result may not be US-ASCII compatible. + + \sa toLatin1(), toUtf8(), toLocal8Bit(), QTextCodec +*/ +QByteArray QStringRef::toAscii() const +{ +#ifndef QT_NO_TEXTCODEC + if (QString::codecForCStrings) + return QString::codecForCStrings->fromUnicode(unicode(), length()); +#endif // QT_NO_TEXTCODEC + return toLatin1(); +} + +/*! + \since 4.8 + + Returns the local 8-bit representation of the string as a + QByteArray. The returned byte array is undefined if the string + contains characters not supported by the local 8-bit encoding. + + QTextCodec::codecForLocale() is used to perform the conversion from + Unicode. If the locale encoding could not be determined, this function + does the same as toLatin1(). + + If this string contains any characters that cannot be encoded in the + locale, the returned byte array is undefined. Those characters may be + suppressed or replaced by another. + + \sa toAscii(), toLatin1(), toUtf8(), QTextCodec +*/ +QByteArray QStringRef::toLocal8Bit() const +{ +#ifndef QT_NO_TEXTCODEC + if (QTextCodec::codecForLocale()) + return QTextCodec::codecForLocale()->fromUnicode(unicode(), length()); +#endif // QT_NO_TEXTCODEC + return toLatin1(); +} + +/*! + \since 4.8 + + Returns a UTF-8 representation of the string as a QByteArray. + + UTF-8 is a Unicode codec and can represent all characters in a Unicode + string like QString. + + However, in the Unicode range, there are certain codepoints that are not + considered characters. The Unicode standard reserves the last two + codepoints in each Unicode Plane (U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, + U+2FFFE, etc.), as well as 16 codepoints in the range U+FDD0..U+FDDF, + inclusive, as non-characters. If any of those appear in the string, they + may be discarded and will not appear in the UTF-8 representation, or they + may be replaced by one or more replacement characters. + + \sa toAscii(), toLatin1(), toLocal8Bit(), QTextCodec +*/ +QByteArray QStringRef::toUtf8() const +{ + if (isNull()) + return QByteArray(); + + return QUtf8::convertFromUnicode(constData(), length(), 0); +} + +/*! + \since 4.8 + + Returns a UCS-4/UTF-32 representation of the string as a QVector<uint>. + + UCS-4 is a Unicode codec and is lossless. All characters from this string + can be encoded in UCS-4. + + \sa fromUtf8(), toAscii(), toLatin1(), toLocal8Bit(), QTextCodec, fromUcs4(), toWCharArray() +*/ +QVector<uint> QStringRef::toUcs4() const +{ + QVector<uint> v(length()); + uint *a = v.data(); + int len = toUcs4_helper<uint>(reinterpret_cast<const unsigned short *>(unicode()), length(), a); + v.resize(len); + return v; +} + QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 36503fd..b54258f 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -51,14 +51,7 @@ #endif #ifndef QT_NO_STL -# if defined (Q_CC_MSVC_NET) && _MSC_VER < 1310 // Avoids nasty warning for xlocale, line 450 -# pragma warning (push) -# pragma warning (disable : 4189) -# include <string> -# pragma warning (pop) -# else -# include <string> -# endif +# include <string> # ifndef QT_NO_STL_WCHAR // workaround for some headers not typedef'ing std::wstring @@ -111,7 +104,11 @@ public: QString &operator=(QChar c); QString &operator=(const QString &); inline QString &operator=(const QLatin1String &); - +#ifdef Q_COMPILER_RVALUE_REFS + inline QString &operator=(QString &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QString &other) { qSwap(d, other.d); } inline int size() const { return d->size; } inline int count() const { return d->size; } inline int length() const; @@ -198,14 +195,18 @@ public: int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int indexOf(const QLatin1String &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(const QStringRef &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int lastIndexOf(const QLatin1String &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QStringRef &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; inline QBool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; inline QBool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int count(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; int count(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; #ifndef QT_NO_REGEXP int indexOf(const QRegExp &, int from = 0) const; @@ -241,9 +242,11 @@ public: QStringRef midRef(int position, int n = -1) const Q_REQUIRED_RESULT; bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool startsWith(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(const QLatin1String &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(const QChar &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QLatin1String &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(const QChar &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; @@ -902,6 +905,8 @@ inline QString::const_iterator QString::constEnd() const { return reinterpret_cast<const QChar*>(d->data + d->size); } inline QBool QString::contains(const QString &s, Qt::CaseSensitivity cs) const { return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QString::contains(const QStringRef &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } inline QBool QString::contains(QChar c, Qt::CaseSensitivity cs) const { return QBool(indexOf(c, 0, cs) != -1); } @@ -1123,6 +1128,34 @@ public: m_size = other.m_size; return *this; } + int indexOf(const QString &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(QChar ch, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(QLatin1String str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int indexOf(const QStringRef &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QString &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(QChar ch, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(QLatin1String str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + inline QBool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(QLatin1String str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QBool contains(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + int count(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + int count(const QStringRef &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) 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; + bool startsWith(const QStringRef &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + + bool endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool endsWith(const QStringRef &c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline QStringRef &operator=(const QString *string); inline const QChar *unicode() const { @@ -1133,6 +1166,12 @@ public: inline const QChar *data() const { return unicode(); } inline const QChar *constData() const { return unicode(); } + QByteArray toAscii() const Q_REQUIRED_RESULT; + QByteArray toLatin1() const Q_REQUIRED_RESULT; + QByteArray toUtf8() const Q_REQUIRED_RESULT; + QByteArray toLocal8Bit() const Q_REQUIRED_RESULT; + QVector<uint> toUcs4() const Q_REQUIRED_RESULT; + inline void clear() { m_string = 0; m_position = m_size = 0; } QString toString() const; inline bool isEmpty() const { return m_size == 0; } @@ -1241,6 +1280,16 @@ inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QString &s inline int QStringRef::localeAwareCompare(const QStringRef &s1, const QStringRef &s2) { return QString::localeAwareCompare_helper(s1.constData(), s1.length(), s2.constData(), s2.length()); } +inline QBool QStringRef::contains(const QString &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QStringRef::contains(QLatin1String s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } +inline QBool QStringRef::contains(QChar c, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(c, 0, cs) != -1); } +inline QBool QStringRef::contains(const QStringRef &s, Qt::CaseSensitivity cs) const +{ return QBool(indexOf(s, 0, cs) != -1); } + + QT_END_NAMESPACE diff --git a/src/corelib/tools/qstringbuilder.cpp b/src/corelib/tools/qstringbuilder.cpp index 53368eb..7d75de7 100644 --- a/src/corelib/tools/qstringbuilder.cpp +++ b/src/corelib/tools/qstringbuilder.cpp @@ -150,8 +150,8 @@ QT_BEGIN_NAMESPACE void QAbstractConcatenable::convertFromAscii(const char *a, int len, QChar *&out) { #ifndef QT_NO_TEXTCODEC - if (QString::codecForCStrings) { - QString tmp = QString::fromAscii(a); + if (QString::codecForCStrings && len) { + QString tmp = QString::fromAscii(a, len > 0 ? len - 1 : -1); memcpy(out, reinterpret_cast<const char *>(tmp.constData()), sizeof(QChar) * tmp.size()); out += tmp.length(); return; diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index 8d9537c..d230d67 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -270,10 +270,11 @@ template <> struct QConcatenable<QByteArray> : private QAbstractConcatenable { typedef QByteArray type; enum { ExactSize = false }; - static int size(const QByteArray &ba) { return qstrnlen(ba.constData(), ba.size()); } + static int size(const QByteArray &ba) { return ba.size(); } static inline void appendTo(const QByteArray &ba, QChar *&out) { - QAbstractConcatenable::convertFromAscii(ba.constData(), -1, out); + // adding 1 because convertFromAscii expects the size including the null-termination + QAbstractConcatenable::convertFromAscii(ba.constData(), ba.size() + 1, out); } }; #endif diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index b1b5bd8..6a98755 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -44,7 +44,10 @@ #include <QtCore/qcontainerfwd.h> #include <QtCore/qglobal.h> +#include <QtCore/qalgorithms.h> + #include <new> +#include <string.h> QT_BEGIN_HEADER @@ -123,6 +126,18 @@ public: } } void append(const T *buf, int size); + inline QVarLengthArray<T, Prealloc> &operator<<(const T &t) + { append(t); return *this; } + inline QVarLengthArray<T, Prealloc> &operator+=(const T &t) + { append(t); return *this; } + + void prepend(const T &t); + void insert(int i, const T &t); + void insert(int i, int n, const T &t); + void replace(int i, const T &t); + void remove(int i); + void remove(int i, int n); + inline T *data() { return ptr; } inline const T *data() const { return ptr; } @@ -135,6 +150,21 @@ public: typedef const value_type &const_reference; typedef qptrdiff difference_type; + + typedef T* iterator; + typedef const T* const_iterator; + + inline iterator begin() { return ptr; } + inline const_iterator begin() const { return ptr; } + inline const_iterator constBegin() const { return ptr; } + inline iterator end() { return ptr + s; } + inline const_iterator end() const { return ptr + s; } + inline const_iterator constEnd() const { return ptr + s; } + iterator insert(iterator before, int n, const T &x); + inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } + iterator erase(iterator begin, iterator end); + inline iterator erase(iterator pos) { return erase(pos, pos+1); } + private: friend class QPodList<T, Prealloc>; void realloc(int size, int alloc); @@ -272,6 +302,100 @@ Q_OUTOFLINE_TEMPLATE T QVarLengthArray<T, Prealloc>::value(int i, const T &defau return (i < 0 || i >= size()) ? defaultValue : at(i); } +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::insert(int i, const T &t) +{ Q_ASSERT_X(i >= 0 && i <= s, "QVarLengthArray::insert", "index out of range"); + insert(begin() + i, 1, t); } +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::insert(int i, int n, const T &t) +{ Q_ASSERT_X(i >= 0 && i <= s, "QVarLengthArray::insert", "index out of range"); + insert(begin() + i, n, t); } +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::remove(int i, int n) +{ Q_ASSERT_X(i >= 0 && n >= 0 && i + n <= s, "QVarLengthArray::remove", "index out of range"); + erase(begin() + i, begin() + i + n); } +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::remove(int i) +{ Q_ASSERT_X(i >= 0 && i < s, "QVarLengthArray::remove", "index out of range"); + erase(begin() + i, begin() + i + 1); } +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::prepend(const T &t) +{ insert(begin(), 1, t); } + +template <class T, int Prealloc> +inline void QVarLengthArray<T, Prealloc>::replace(int i, const T &t) +{ + Q_ASSERT_X(i >= 0 && i < s, "QVarLengthArray::replace", "index out of range"); + const T copy(t); + data()[i] = copy; +} + + +template <class T, int Prealloc> +Q_OUTOFLINE_TEMPLATE typename QVarLengthArray<T, Prealloc>::iterator QVarLengthArray<T, Prealloc>::insert(iterator before, size_type n, const T &t) +{ + int offset = int(before - ptr); + if (n != 0) { + resize(s + n); + const T copy(t); + if (QTypeInfo<T>::isStatic) { + T *b = ptr + offset; + T *j = ptr + s; + T *i = j - n; + while (i != b) + *--j = *--i; + i = b + n; + while (i != b) + *--i = copy; + } else { + T *b = ptr + offset; + T *i = b + n; + memmove(i, b, (s - offset - n) * sizeof(T)); + while (i != b) + new (--i) T(copy); + } + } + return ptr + offset; +} + +template <class T, int Prealloc> +Q_OUTOFLINE_TEMPLATE typename QVarLengthArray<T, Prealloc>::iterator QVarLengthArray<T, Prealloc>::erase(iterator abegin, iterator aend) +{ + int f = int(abegin - ptr); + int l = int(aend - ptr); + int n = l - f; + if (QTypeInfo<T>::isComplex) { + qCopy(ptr + l, ptr + s, ptr + f); + T *i = ptr + s; + T *b = ptr + s - n; + while (i != b) { + --i; + i->~T(); + } + } else { + memmove(ptr + f, ptr + l, (s - l) * sizeof(T)); + } + s -= n; + return ptr + f; +} + +template <typename T, int Prealloc1, int Prealloc2> +bool operator==(const QVarLengthArray<T, Prealloc1> &l, const QVarLengthArray<T, Prealloc2> &r) +{ + if (l.size() != r.size()) + return false; + for (int i = 0; i < l.size(); i++) { + if (l.at(i) != r.at(i)) + return false; + } + return true; +} + +template <typename T, int Prealloc1, int Prealloc2> +bool operator!=(const QVarLengthArray<T, Prealloc1> &l, const QVarLengthArray<T, Prealloc2> &r) +{ + return !(l == r); +} QT_END_NAMESPACE diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index 996ca7f..3c80c1c 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -337,3 +337,211 @@ Typedef for const T &. Provided for STL compatibility. */ +/*! \fn void QVarLengthArray::prepend(const T &value) + + \since 4.8 + Inserts \a value at the beginning of the array. + + + This is the same as vector.insert(0, \a value). + + For large arrays, this operation can be slow (\l{linear time}), + because it requires moving all the items in the vector by one + position further in memory. If you want a container class that + provides a fast prepend() function, use QList or QLinkedList + instead. + + \sa append(), insert() +*/ + +/*! \fn void QVarLengthArray::replace(int i, const T &value) + + \since 4.8 + Replaces the item at index position \a i with \a value. + + \a i must be a valid index position in the array (i.e., 0 <= \a + i < size()). + + \sa operator[](), remove() +*/ + +/*! \fn void QVarLengthArray::remove(int i) + + \overload + \since 4.8 + + Removes the element at index position \a i. + + \sa insert(), replace() +*/ + +/*! \fn void QVarLengthArray::remove(int i, int count) + + \overload + \since 4.8 + + Removes \a count elements from the middle of the array, starting at + index position \a i. + + \sa insert(), replace() +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::begin() + \since 4.8 + + Returns an \l{STL-style iterator} pointing to the first item in + the array. + + \sa constBegin(), end() +*/ + +/*! \fn QVarLengthArray::const_iterator QVarLengthArray::begin() const + \since 4.8 + \overload +*/ + +/*! \fn QVarLengthArray::const_iterator QVarLengthArray::constBegin() const + \since 4.8 + + Returns a const \l{STL-style iterator} pointing to the first item + in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::end() + \since 4.8 + + Returns an \l{STL-style iterator} pointing to the imaginary item + after the last item in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn QVarLengthArray::const_iterator QVarLengthArray::end() const + \since 4.8 + + \overload +*/ + +/*! \fn QVarLengthArray::const_iterator QVarLengthArray::constEnd() const + \since 4.8 + + Returns a const \l{STL-style iterator} pointing to the imaginary + item after the last item in the array. + + \sa constBegin(), end() +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::erase(iterator pos) + \since 4.8 + + Removes the item pointed to by the iterator \a pos from the + vector, and returns an iterator to the next item in the vector + (which may be end()). + + \sa insert(), remove() +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::erase(iterator begin, iterator end) + + \overload + \since 4.8 + + Removes all the items from \a begin up to (but not including) \a + end. Returns an iterator to the same item that \a end referred to + before the call. +*/ + +/*! \fn void QVarLengthArray::insert(int i, const T &value) + \since 4.8 + + Inserts \a value at index position \a i in the array. If \a i is + 0, the value is prepended to the vector. If \a i is size(), the + value is appended to the vector. + + For large arrays, this operation can be slow (\l{linear time}), + because it requires moving all the items at indexes \a i and + above by one position further in memory. If you want a container + class that provides a fast insert() function, use QLinkedList + instead. + + \sa remove() +*/ + +/*! \fn void QVarLengthArray::insert(int i, int count, const T &value) + + \overload + \since 4.8 + + Inserts \a count copies of \a value at index position \a i in the + vector. +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::insert(iterator before, const T &value) + + \overload + \since 4.8 + + Inserts \a value in front of the item pointed to by the iterator + \a before. Returns an iterator pointing at the inserted item. +*/ + +/*! \fn QVarLengthArray::iterator QVarLengthArray::insert(iterator before, int count, const T &value) + + \since 4.8 + Inserts \a count copies of \a value in front of the item pointed to + by the iterator \a before. Returns an iterator pointing at the + first of the inserted items. +*/ + + + +/*! \fn bool operator==(const QVarLengthArray<T, Prealloc1> &left, const QVarLengthArray<T, Prealloc2> &right) + + \relates QVarLengthArray + \since 4.8 + Returns true if the two array are equal; + + Two arrays are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator!=() +*/ + +/*! \fn bool operator!=(const QVarLengthArray<T, Prealloc1> &left, const QVarLengthArray<T, Prealloc2> &right) + + \relates QVarLengthArray + \since 4.8 + Returns true if the two array are different; + + Two arrays are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator==() +*/ + +/*! \fn QVarLengthArray &QVarLengthArray::operator<<(const T &value) + + \since 4.8 + Appends \a value to the array and returns a reference to this + vector. + + \sa append(), operator+=() +*/ + +/*! \fn QVarLengthArray &QVarLengthArray::operator+=(const T &value) + + \since 4.8 + Appends \a value to the array and returns a reference to this + vector. + + \sa append(), operator<<() +*/ + diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index d428d9f..e2e366d 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -285,6 +285,13 @@ int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive vector. */ +/*! \fn void QVector::swap(QVector<T> &other) + \since 4.8 + + Swaps vector \a other with this vector. This operation is very fast and + never fails. +*/ + /*! \fn bool QVector::operator==(const QVector<T> &other) const Returns true if \a other is equal to this vector; otherwise diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 551dbca..9418642 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -53,6 +53,9 @@ #endif #include <stdlib.h> #include <string.h> +#ifdef Q_COMPILER_INITIALIZER_LISTS +#include <initializer_list> +#endif QT_BEGIN_HEADER @@ -112,12 +115,22 @@ class QVector }; public: + // ### Qt 5: Consider making QVector non-shared to get at least one + // "really fast" container. See tests/benchmarks/corelib/tools/qvector/ inline QVector() : d(&QVectorData::shared_null) { d->ref.ref(); } explicit QVector(int size); QVector(int size, const T &t); inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } QVector<T> &operator=(const QVector<T> &v); +#ifdef Q_COMPILER_RVALUE_REFS + inline QVector<T> operator=(QVector<T> &&other) + { qSwap(p, other.p); return *this; } +#endif + inline void swap(QVector<T> &other) { qSwap(d, other.d); } +#ifdef Q_COMPILER_INITIALIZER_LISTS + inline QVector(std::initializer_list<T> args); +#endif bool operator==(const QVector<T> &v) const; inline bool operator!=(const QVector<T> &v) const { return !(*this == v); } @@ -293,11 +306,10 @@ public: #ifndef QT_NO_STL static inline QVector<T> fromStdVector(const std::vector<T> &vector) - { QVector<T> tmp; tmp.reserve(vector.size()); qCopy(vector.begin(), vector.end(), std::back_inserter(tmp)); return tmp; } + { QVector<T> tmp; tmp.reserve(int(vector.size())); qCopy(vector.begin(), vector.end(), std::back_inserter(tmp)); return tmp; } inline std::vector<T> toStdVector() const { std::vector<T> tmp; tmp.reserve(size()); qCopy(constBegin(), constEnd(), std::back_inserter(tmp)); return tmp; } #endif - private: friend class QRegion; // Optimization for QRegion::rects() @@ -426,6 +438,22 @@ QVector<T>::QVector(int asize, const T &t) new (--i) T(t); } +#ifdef Q_COMPILER_INITIALIZER_LISTS +template <typename T> +QVector<T>::QVector(std::initializer_list<T> args) +{ + d = malloc(int(args.size())); + d->ref = 1; + d->alloc = d->size = int(args.size()); + d->sharable = true; + d->capacity = false; + T* i = p->array + d->size; + auto it = args.end(); + while (i != p->array) + new (--i) T(*(--it)); +} +#endif + template <typename T> void QVector<T>::free(Data *x) { @@ -790,11 +818,8 @@ QT_END_INCLUDE_NAMESPACE #else #define Q_TEMPLATE_EXTERN extern #endif -# pragma warning(push) /* MSVC 6.0 doesn't care about the disabling in qglobal.h (why?), so do it here */ -# pragma warning(disable: 4231) /* nonstandard extension used : 'extern' before template explicit instantiation */ Q_TEMPLATE_EXTERN template class Q_CORE_EXPORT QVector<QPointF>; Q_TEMPLATE_EXTERN template class Q_CORE_EXPORT QVector<QPoint>; -# pragma warning(pop) #endif QT_END_NAMESPACE diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index d81ab04..fb1b466 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -29,6 +29,9 @@ HEADERS += \ tools/qrect.h \ tools/qregexp.h \ tools/qringbuffer_p.h \ + tools/qscopedpointer.h \ + tools/qscopedpointer_p.h \ + tools/qscopedvaluerollback.h \ tools/qshareddata.h \ tools/qsharedpointer.h \ tools/qsharedpointer_impl.h \ @@ -45,9 +48,7 @@ HEADERS += \ tools/qelapsedtimer.h \ tools/qunicodetables_p.h \ tools/qvarlengtharray.h \ - tools/qvector.h \ - tools/qscopedpointer.h \ - tools/qscopedpointer_p.h + tools/qvector.h SOURCES += \ @@ -83,10 +84,11 @@ SOURCES += \ symbian:SOURCES+=tools/qlocale_symbian.cpp -mac:SOURCES += tools/qelapsedtimer_mac.cpp +!nacl:mac:SOURCES += tools/qelapsedtimer_mac.cpp else:symbian:SOURCES += tools/qelapsedtimer_symbian.cpp else:unix:SOURCES += tools/qelapsedtimer_unix.cpp else:win32:SOURCES += tools/qelapsedtimer_win.cpp +else:integrity:SOURCES += tools/qelapsedtimer_unix.cpp else:SOURCES += tools/qelapsedtimer_generic.cpp contains(QT_CONFIG, zlib):include($$PWD/../../3rdparty/zlib.pri) diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index 18b4118..2ef0386 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -2941,6 +2941,9 @@ QStringRef QXmlStreamReader::documentEncoding() const By default, QXmlStreamWriter encodes XML in UTF-8. Different encodings can be enforced using setCodec(). + If an error occurs while writing to the underlying device, hasError() + starts returning true and subsequent writes are ignored. + The \l{QXmlStream Bookmarks Example} illustrates how to use a stream writer to write an XML bookmark file (XBEL) that was previously read in by a QXmlStreamReader. @@ -2965,7 +2968,8 @@ public: void write(const QStringRef &); void write(const QString &); void writeEscaped(const QString &, bool escapeWhitespace = false); - void write(const char *s); + void write(const char *s, int len); + template <int N> void write(const char (&s)[N]) { write(s, N - 1); } bool finishStartElement(bool contents = true); void writeStartElement(const QString &namespaceUri, const QString &name); QIODevice *device; @@ -2975,6 +2979,7 @@ public: uint inEmptyElement :1; uint lastWasStartElement :1; uint wroteSomething :1; + uint hasError :1; uint autoFormatting :1; QByteArray autoFormattingIndent; NamespaceDeclaration emptyNamespace; @@ -3007,6 +3012,7 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) #endif inStartElement = inEmptyElement = false; wroteSomething = false; + hasError = false; lastWasStartElement = false; lastNamespaceDeclaration = 1; autoFormatting = false; @@ -3016,11 +3022,15 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) void QXmlStreamWriterPrivate::write(const QStringRef &s) { if (device) { + if (hasError) + return; #ifdef QT_NO_TEXTCODEC - device->write(s.toString().toLatin1(), s.size()); + QByteArray bytes = s.toLatin1(); #else - device->write(encoder->fromUnicode(s.constData(), s.size())); + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); #endif + if (device->write(bytes) != bytes.size()) + hasError = true; } else if (stringDevice) s.appendTo(stringDevice); @@ -3031,11 +3041,15 @@ void QXmlStreamWriterPrivate::write(const QStringRef &s) void QXmlStreamWriterPrivate::write(const QString &s) { if (device) { + if (hasError) + return; #ifdef QT_NO_TEXTCODEC - device->write(s.toLatin1(), s.size()); + QByteArray bytes = s.toLatin1(); #else - device->write(encoder->fromUnicode(s)); + QByteArray bytes = encoder->fromUnicode(s); #endif + if (device->write(bytes) != bytes.size()) + hasError = true; } else if (stringDevice) stringDevice->append(s); @@ -3070,31 +3084,19 @@ void QXmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespa escaped += QChar(c); } } - if (device) { -#ifdef QT_NO_TEXTCODEC - device->write(escaped.toLatin1(), escaped.size()); -#else - device->write(encoder->fromUnicode(escaped)); -#endif - } - else if (stringDevice) - stringDevice->append(escaped); - else - qWarning("QXmlStreamWriter: No device"); + write(escaped); } - -void QXmlStreamWriterPrivate::write(const char *s) +// ASCII only! +void QXmlStreamWriterPrivate::write(const char *s, int len) { if (device) { -#ifndef QT_NO_TEXTCODEC - if (codec->mibEnum() != 106) - device->write(encoder->fromUnicode(QLatin1String(s))); - else -#endif - device->write(s, strlen(s)); + if (hasError) + return; + if (device->write(s, len) != len) + hasError = true; } else if (stringDevice) { - stringDevice->append(QLatin1String(s)); + stringDevice->append(QString::fromLatin1(s, len)); } else qWarning("QXmlStreamWriter: No device"); } @@ -3172,7 +3174,7 @@ void QXmlStreamWriterPrivate::indent(int level) { write("\n"); for (int i = level; i > 0; --i) - write(autoFormattingIndent.constData()); + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); } @@ -3373,6 +3375,17 @@ int QXmlStreamWriter::autoFormattingIndent() const return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); } +/*! + Returns \c true if the stream failed to write to the underlying device. + + The error status is never reset. Writes happening after the error + occurred are ignored, even if the error condition is cleared. + */ +bool QXmlStreamWriter::hasError() const +{ + Q_D(const QXmlStreamWriter); + return d->hasError; +} /*! \overload @@ -3759,7 +3772,7 @@ void QXmlStreamWriter::writeStartDocument(const QString &version) #ifdef QT_NO_TEXTCODEC d->write("iso-8859-1"); #else - d->write(d->codec->name().constData()); + d->write(d->codec->name().constData(), d->codec->name().length()); #endif } d->write("\"?>"); @@ -3782,12 +3795,13 @@ void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalon #ifdef QT_NO_TEXTCODEC d->write("iso-8859-1"); #else - d->write(d->codec->name().constData()); + d->write(d->codec->name().constData(), d->codec->name().length()); #endif } - d->write("\" standalone=\""); - d->write(standalone ? "yes" : "no"); - d->write("\"?>"); + if (standalone) + d->write("\" standalone=\"yes\"?>"); + else + d->write("\" standalone=\"no\"?>"); } diff --git a/src/corelib/xml/qxmlstream.h b/src/corelib/xml/qxmlstream.h index 1a91326..e5cb61e 100644 --- a/src/corelib/xml/qxmlstream.h +++ b/src/corelib/xml/qxmlstream.h @@ -169,6 +169,7 @@ Q_DECLARE_TYPEINFO(QXmlStreamAttribute, Q_MOVABLE_TYPE); class Q_XMLSTREAM_EXPORT QXmlStreamAttributes : public QVector<QXmlStreamAttribute> { public: + inline QXmlStreamAttributes() {} QStringRef value(const QString &namespaceUri, const QString &name) const; QStringRef value(const QString &namespaceUri, const QLatin1String &name) const; QStringRef value(const QLatin1String &namespaceUri, const QLatin1String &name) const; @@ -473,6 +474,8 @@ public: void writeCurrentToken(const QXmlStreamReader &reader); #endif + bool hasError() const; + private: Q_DISABLE_COPY(QXmlStreamWriter) Q_DECLARE_PRIVATE(QXmlStreamWriter) |