diff options
Diffstat (limited to 'src/corelib')
121 files changed, 7636 insertions, 970 deletions
diff --git a/src/corelib/arch/arch.pri b/src/corelib/arch/arch.pri index 18f54ee..a25027b 100644 --- a/src/corelib/arch/arch.pri +++ b/src/corelib/arch/arch.pri @@ -4,9 +4,12 @@ win32:HEADERS += arch/qatomic_windows.h \ mac:HEADERS += arch/qatomic_macosx.h \ arch/qatomic_generic.h +symbian:HEADERS += arch/qatomic_symbian.h \ + arch/qatomic_generic.h + vxworks:HEADERS += arch/qatomic_vxworks.h -!wince*:!win32:!mac:HEADERS += arch/qatomic_alpha.h \ +!wince*:!win32:!mac:!symbian:HEADERS += arch/qatomic_alpha.h \ arch/qatomic_avr32.h \ arch/qatomic_ia64.h \ arch/qatomic_parisc.h \ diff --git a/src/corelib/arch/generic/qatomic_generic_unix.cpp b/src/corelib/arch/generic/qatomic_generic_unix.cpp index 0f0df4f..61f3410 100644 --- a/src/corelib/arch/generic/qatomic_generic_unix.cpp +++ b/src/corelib/arch/generic/qatomic_generic_unix.cpp @@ -39,10 +39,13 @@ ** ****************************************************************************/ +#if !defined(Q_OS_SYMBIAN) || (defined(Q_OS_SYMBIAN) && !defined(Q_CC_RVCT)) + #include "qplatformdefs.h" #include <QtCore/qatomic.h> +QT_BEGIN_NAMESPACE static pthread_mutex_t qAtomicMutex = PTHREAD_MUTEX_INITIALIZER; Q_CORE_EXPORT @@ -116,3 +119,5 @@ void *QBasicAtomicPointer_fetchAndAddOrdered(void * volatile *_q_value, qptrdiff pthread_mutex_unlock(&qAtomicMutex); return returnValue; } +QT_END_NAMESPACE +#endif //!defined(Q_OS_SYMBIAN) && !defined(Q_CC_RVCT) diff --git a/src/corelib/arch/qatomic_arch.h b/src/corelib/arch/qatomic_arch.h index fcfff72..faa168a 100644 --- a/src/corelib/arch/qatomic_arch.h +++ b/src/corelib/arch/qatomic_arch.h @@ -82,6 +82,8 @@ QT_BEGIN_HEADER # include "QtCore/qatomic_windowsce.h" #elif defined(QT_ARCH_X86_64) # include "QtCore/qatomic_x86_64.h" +#elif defined(QT_ARCH_SYMBIAN) +# include "QtCore/qatomic_symbian.h" #elif defined(QT_ARCH_SH) # include "QtCore/qatomic_sh.h" #elif defined(QT_ARCH_SH4A) diff --git a/src/corelib/arch/qatomic_arm.h b/src/corelib/arch/qatomic_arm.h index a709b3b..6bba2e1 100644 --- a/src/corelib/arch/qatomic_arm.h +++ b/src/corelib/arch/qatomic_arm.h @@ -116,6 +116,12 @@ extern "C" typedef int (qt_atomic_eabi_cmpxchg_ptr_t)(void *oldval, void *newval 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; @@ -126,7 +132,9 @@ inline char q_atomic_swp(volatile char *ptr, char newval) return ret; } -#endif +#endif // Q_CC_RVCT + +#endif // QT_NO_ARM_EABI // Reference counting @@ -213,6 +221,8 @@ inline bool QBasicAtomicInt::testAndSetRelease(int expectedValue, int newValue) // Fetch and store for integers +#ifndef Q_CC_RVCT + inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) { int originalValue; @@ -223,6 +233,8 @@ inline int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) return originalValue; } +#endif + inline int QBasicAtomicInt::fetchAndStoreRelaxed(int newValue) { return fetchAndStoreOrdered(newValue); @@ -323,6 +335,22 @@ Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetRelease(T *expectedValu // 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) { @@ -334,6 +362,8 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreOrdered(T *newValue) return originalValue; } +#endif // Q_CC_RVCT + template <typename T> Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndStoreRelaxed(T *newValue) { diff --git a/src/corelib/arch/qatomic_symbian.h b/src/corelib/arch/qatomic_symbian.h new file mode 100644 index 0000000..74a0ca8 --- /dev/null +++ b/src/corelib/arch/qatomic_symbian.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QATOMIC_SYMBIAN_H +#define QATOMIC_SYMBIAN_H + +QT_BEGIN_HEADER + +#if defined(Q_CC_RVCT) +# define QT_NO_ARM_EABI +# include <QtCore/qatomic_arm.h> +#elif defined(Q_CC_NOKIAX86) || defined(Q_CC_GCCE) +# include <QtCore/qatomic_generic.h> +#endif + +QT_END_HEADER + +#endif // QATOMIC_SYMBIAN_H diff --git a/src/corelib/arch/qatomic_windows.h b/src/corelib/arch/qatomic_windows.h index b9f0280..c5d9c97 100644 --- a/src/corelib/arch/qatomic_windows.h +++ b/src/corelib/arch/qatomic_windows.h @@ -107,7 +107,7 @@ template <typename T> Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::isFetchAndAddWaitFree() { return true; } -#if defined(Q_CC_MSVC) +#if defined(Q_CC_MSVC) || defined(Q_CC_MWERKS) // MSVC++ 6.0 doesn't generate correct code when optimizations are turned on! #if _MSC_VER < 1300 && defined (_M_IX86) @@ -218,7 +218,7 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo #else -#if !defined(Q_OS_WINCE) +#if !defined(Q_OS_WINCE) && !defined(Q_CC_MWERKS) // use compiler intrinsics for all atomic functions //those functions need to be define in the global namespace QT_END_NAMESPACE @@ -319,7 +319,7 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo #else // Q_OS_WINCE -#if _WIN32_WCE < 0x600 && defined(_X86_) +#if (_WIN32_WCE < 0x600 && defined(_X86_)) || defined(Q_CC_MWERKS) // For X86 Windows CE build we need to include winbase.h to be able // to catch the inline functions which overwrite the regular // definitions inside of coredll.dll. Though one could use the @@ -327,8 +327,7 @@ Q_INLINE_TEMPLATE T *QBasicAtomicPointer<T>::fetchAndAddOrdered(qptrdiff valueTo // exported at all. #include <winbase.h> #else - -#if _WIN32_WCE >= 0x600 +#if _WIN32_WCE >= 0x600 || defined(Q_CC_MWERKS) #define Q_ARGUMENT_TYPE volatile # if defined(_X86_) # define InterlockedIncrement _InterlockedIncrement diff --git a/src/corelib/arch/symbian/arch.pri b/src/corelib/arch/symbian/arch.pri new file mode 100644 index 0000000..deb94b1 --- /dev/null +++ b/src/corelib/arch/symbian/arch.pri @@ -0,0 +1,5 @@ +# +# Symbian architecture +# +SOURCES += $$QT_ARCH_CPP/qatomic_symbian.cpp \ + $$QT_ARCH_CPP/../generic/qatomic_generic_unix.cpp diff --git a/src/corelib/arch/symbian/qatomic_symbian.cpp b/src/corelib/arch/symbian/qatomic_symbian.cpp new file mode 100644 index 0000000..b08a468 --- /dev/null +++ b/src/corelib/arch/symbian/qatomic_symbian.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> +#include <QtCore/qatomic.h> + +#include <e32debug.h> + +// Heap and handle info printer. This code is placed here as it happens to make it the very last static to be destroyed in a Qt app. +// This way we can report on heap cells and handles that are really not owned by anything which still exists. +// This information can be used to detect whether memory leaks are happening, particularly if these numbers grow as the app is used more. +struct SPrintExitInfo +{ + SPrintExitInfo() + { + RThread().HandleCount(initProcessHandleCount,initThreadHandleCount); + initCells = User::CountAllocCells(); + } + ~SPrintExitInfo() + { + RProcess myProc; + TFullName fullName = myProc.FileName(); + TInt cells = User::CountAllocCells(); + TInt processHandleCount=0; + TInt threadHandleCount=0; + RThread().HandleCount(processHandleCount,threadHandleCount); + RDebug::Print(_L("%S exiting with %d allocated cells, %d handles"), + &fullName, + cells - initCells, + (processHandleCount + threadHandleCount) - (initProcessHandleCount + initThreadHandleCount)); + } + TInt initCells; + TInt initProcessHandleCount; + TInt initThreadHandleCount; +} printExitInfo; + + +#if defined(Q_CC_RVCT) + +#include "../arm/qatomic_arm.cpp" + +QT_BEGIN_NAMESPACE + +// This declspec needs to be explicit. RVCT has a bug which prevents embedded +// assembler functions from being exported (normally all functions are +// exported, and Q_CORE_EXPORT resolves to nothing). +__declspec(dllexport) __asm char q_atomic_swp(volatile char *ptr, char newval) +{ + add r2, pc, #0 + bx r2 + arm + swpb r2,r1,[r0] + mov r0, r2 + bx lr + thumb +} + +__declspec(dllexport) __asm int QBasicAtomicInt::fetchAndStoreOrdered(int newValue) +{ + add r2, pc, #0 + bx r2 + arm + swp r2,r1,[r0] + mov r0, r2 + bx lr + thumb +} + +QT_END_NAMESPACE + +#endif // Q_CC_RVCT diff --git a/src/corelib/codecs/qisciicodec.cpp b/src/corelib/codecs/qisciicodec.cpp index 6852748..c68a47c 100644 --- a/src/corelib/codecs/qisciicodec.cpp +++ b/src/corelib/codecs/qisciicodec.cpp @@ -38,7 +38,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qisciicodec_p.h" #include "qlist.h" diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index b150e22..adfe2ed 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -92,6 +92,11 @@ # define QT_NO_SETLOCALE #endif +#if 0 // ### TODO - remove me! +// enabling this is not exception safe! +#define Q_DEBUG_TEXTCODEC +#endif + QT_BEGIN_NAMESPACE #ifndef QT_NO_TEXTCODECPLUGIN @@ -169,7 +174,9 @@ static QTextCodec *createForMib(int mib) } static QList<QTextCodec*> *all = 0; +#ifdef Q_DEBUG_TEXTCODEC static bool destroying_is_ok = false; +#endif static QTextCodec *localeMapper = 0; QTextCodec *QTextCodec::cftr = 0; @@ -191,15 +198,21 @@ QTextCodecCleanup::~QTextCodecCleanup() if (!all) return; +#ifdef Q_DEBUG_TEXTCODEC destroying_is_ok = true; +#endif - while (all->size()) - delete all->takeFirst(); + for (QList<QTextCodec *>::const_iterator it = all->constBegin() + ; it != all->constEnd(); ++it) { + delete *it; + } delete all; all = 0; localeMapper = 0; +#ifdef Q_DEBUG_TEXTCODEC destroying_is_ok = false; +#endif } Q_GLOBAL_STATIC(QTextCodecCleanup, createQTextCodecCleanup) @@ -658,8 +671,10 @@ static void setup() if (all) return; +#ifdef Q_DEBUG_TEXTCODEC if (destroying_is_ok) qWarning("QTextCodec: Creating new codec during codec cleanup"); +#endif all = new QList<QTextCodec*>; // create the cleanup object to cleanup all codecs on exit (void) createQTextCodecCleanup(); @@ -914,8 +929,10 @@ QTextCodec::QTextCodec() */ QTextCodec::~QTextCodec() { +#ifdef Q_DEBUG_TEXTCODEC if (!destroying_is_ok) qWarning("QTextCodec::~QTextCodec: Called by application"); +#endif if (all) all->removeAll(this); } diff --git a/src/corelib/concurrent/qtconcurrentiteratekernel.cpp b/src/corelib/concurrent/qtconcurrentiteratekernel.cpp index 3bbb38d..06a66bc 100644 --- a/src/corelib/concurrent/qtconcurrentiteratekernel.cpp +++ b/src/corelib/concurrent/qtconcurrentiteratekernel.cpp @@ -104,11 +104,17 @@ static qint64 getticks() return 0; return (ts.tv_sec * 1000000000) + ts.tv_nsec; #else + +#ifdef Q_OS_SYMBIAN + return clock(); +#else // no clock_gettime(), fall back to wall time struct timeval tv; gettimeofday(&tv, 0); return (tv.tv_sec * 1000000) + tv.tv_usec; #endif + +#endif } #elif defined(Q_OS_WIN) diff --git a/src/corelib/concurrent/qtconcurrentiteratekernel.h b/src/corelib/concurrent/qtconcurrentiteratekernel.h index 120a328..51019a2 100644 --- a/src/corelib/concurrent/qtconcurrentiteratekernel.h +++ b/src/corelib/concurrent/qtconcurrentiteratekernel.h @@ -49,8 +49,10 @@ #include <QtCore/qatomic.h> #include <QtCore/qtconcurrentmedian.h> #include <QtCore/qtconcurrentthreadengine.h> -#include <iterator> +#ifndef QT_NO_STL +# include <iterator> +#endif QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -148,6 +150,7 @@ public: inline void * getPointer() { return 0; } }; +#ifndef QT_NO_STL inline bool selectIteration(std::bidirectional_iterator_tag) { return false; // while @@ -162,6 +165,14 @@ inline bool selectIteration(std::random_access_iterator_tag) { return true; // for } +#else +// no stl support, always use while iteration +template <typename T> +inline bool selectIteration(T) +{ + return false; // while +} +#endif template <typename Iterator, typename T> class IterateKernel : public ThreadEngine<T> @@ -170,7 +181,10 @@ public: typedef T ResultType; IterateKernel(Iterator _begin, Iterator _end) -#ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION +#if defined (QT_NO_STL) + : begin(_begin), end(_end), current(_begin), currentIndex(0), + forIteration(false), progressReportingEnabled(true) +#elif !defined(QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION) : begin(_begin), end(_end), current(_begin), currentIndex(0), forIteration(selectIteration(typename std::iterator_traits<Iterator>::iterator_category())), progressReportingEnabled(true) #else @@ -178,7 +192,12 @@ public: forIteration(selectIteration(std::iterator_category(_begin))), progressReportingEnabled(true) #endif { +#if defined (QT_NO_STL) + iterationCount = 0; +#else iterationCount = forIteration ? std::distance(_begin, _end) : 0; + +#endif } virtual ~IterateKernel() { } diff --git a/src/corelib/concurrent/qthreadpool.cpp b/src/corelib/concurrent/qthreadpool.cpp index 2dfac31..fd1a79e 100644 --- a/src/corelib/concurrent/qthreadpool.cpp +++ b/src/corelib/concurrent/qthreadpool.cpp @@ -248,14 +248,14 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const */ void QThreadPoolPrivate::startThread(QRunnable *runnable) { - QThreadPoolThread *thread = new QThreadPoolThread(this); - allThreads.insert(thread); + QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this)); + allThreads.insert(thread.data()); ++activeThreads; if (runnable->autoDelete()) ++runnable->ref; thread->runnable = runnable; - thread->start(); + thread.take()->start(); } /*! \internal diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index db51d43..96e2b5c 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -28,3 +28,11 @@ QMAKE_LIBS += $$QMAKE_LIBS_CORE QMAKE_DYNAMIC_LIST_FILE = $$PWD/QtCore.dynlist contains(DEFINES,QT_EVAL):include(eval.pri) + +symbian: { + TARGET.UID3=0x2001B2DC + + # Workaroud for problems with paging this dll + MMP_RULES -= PAGED + MMP_RULES *= UNPAGED +}
\ No newline at end of file diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index b2046c9..da70216 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -44,6 +44,8 @@ #include "qvector.h" #include "qlist.h" #include "qthreadstorage.h" +#include "qdir.h" +#include "qstringlist.h" #ifndef QT_NO_QOBJECT #include <private/qthread_p.h> @@ -66,10 +68,15 @@ # include <envLib.h> #endif -#ifdef Q_CC_MWERKS +#if defined(Q_CC_MWERKS) && defined(Q_OS_MACX) #include <CoreServices/CoreServices.h> #endif +#if defined(Q_OS_SYMBIAN) +#include <e32def.h> +#include <e32debug.h> +#endif + QT_BEGIN_NAMESPACE @@ -1053,6 +1060,20 @@ bool qSharedBuild() */ /*! + \fn QSysInfo::SymVersion QSysInfo::symbianVersion() + + Returns the version of the Symbian operating system on which the + application is run (Symbian only). +*/ + +/*! + \fn QSysInfo::S60Version QSysInfo::s60Version() + + Returns the version of the S60 SDK system on which the + application is run (S60 only). +*/ + +/*! \enum QSysInfo::Endian \value BigEndian Big-endian byte order (also called Network byte order) @@ -1108,7 +1129,7 @@ bool qSharedBuild() \value WV_NT_based NT-based version of Windows \value WV_CE_based CE-based version of Windows - \sa MacVersion + \sa MacVersion, SymVersion */ /*! @@ -1137,7 +1158,39 @@ bool qSharedBuild() \value MV_LEOPARD Apple codename for MV_10_5 \value MV_SNOWLEOPARD Apple codename for MV_10_6 - \sa WinVersion + \sa WinVersion, SymVersion +*/ + +/*! + \enum QSysInfo::SymVersion + + This enum provides symbolic names for the various versions of the + Symbian operating system. On Symbian, the + QSysInfo::symbianVersion() function gives the version of the + system on which the application is run. + + \value SV_9_2 Symbian OS 9.2 + \value SV_9_3 Symbian OS 9.3 + \value SV_9_4 Symbian OS 9.4 + \value SV_Unknown An unknown and currently unsupported platform + + \sa S60Version, WinVersion, MacVersion +*/ + +/*! + \enum QSysInfo::S60Version + + This enum provides symbolic names for the various versions of the + S60 SDK. On S60, the + QSysInfo::s60Version() function gives the version of the + SDK on which the application is run. + + \value SV_S60_3_1 S60 3rd Edition Feature Pack 1 + \value SV_S60_3_2 S60 3rd Edition Feature Pack 2 + \value SV_S60_5_0 S60 5th Edition + \value SV_S60_Unknown An unknown and currently unsupported platform + + \sa SymVersion, WinVersion, MacVersion */ /*! @@ -1704,6 +1757,87 @@ const QSysInfo::WinVersion QSysInfo::WindowsVersion = QSysInfo::windowsVersion() #endif +#ifdef Q_OS_SYMBIAN +# ifdef Q_WS_S60 +static QSysInfo::S60Version cachedS60Version = QSysInfo::S60Version(-1); + +QSysInfo::S60Version QSysInfo::s60Version() +{ +# ifdef Q_CC_NOKIAX86 + // For emulator builds. Emulators don't support the trick we use to figure + // out which SDK we are running under, so simply hardcode it there. +# if defined(__SERIES60_31__) + return SV_S60_3_1; + +# elif defined(__S60_32__) + return SV_S60_3_2; + +# elif defined(__S60_50__) + return SV_S60_5_0; + +# else + return SV_S60_Unknown; + +# endif + +# else + // For hardware builds. + if (cachedS60Version != -1) + return cachedS60Version; + + QDir dir(QLatin1String("z:\\system\\install")); + QStringList filters; + filters << QLatin1String("Series60v?.*.sis"); + dir.setNameFilters(filters); + + QStringList names = dir.entryList(QDir::NoFilter, QDir::Name | QDir::Reversed | QDir::IgnoreCase); + if (names.size() == 0) + return cachedS60Version = SV_S60_Unknown; + + int major, minor; + major = names[0][9].toAscii() - '0'; + minor = names[0][11].toAscii() - '0'; + if (major == 3) { + if (minor == 1) { + return cachedS60Version = SV_S60_3_1; + } else if (minor == 2) { + return cachedS60Version = SV_S60_3_2; + } + } else if (major == 5) { + if (minor == 0) { + return cachedS60Version = SV_S60_5_0; + } + } + + return cachedS60Version = SV_S60_Unknown; +# endif +} +QSysInfo::SymVersion QSysInfo::symbianVersion() +{ + switch (s60Version()) { + case SV_S60_3_1: + return SV_9_2; + case SV_S60_3_2: + return SV_9_3; + case SV_S60_5_0: + return SV_9_4; + default: + return SV_Unknown; + } +} +#else +QSysInfo::S60Version QSysInfo::s60Version() +{ + return SV_S60_None; +} + +QSysInfo::SymVersion QSysInfo::symbianVersion() +{ + return SV_Unknown; +} +# endif // ifdef Q_WS_S60 +#endif // ifdef Q_OS_SYMBIAN + /*! \macro void Q_ASSERT(bool test) \relates <QtGlobal> @@ -1769,6 +1903,15 @@ const QSysInfo::WinVersion QSysInfo::WindowsVersion = QSysInfo::windowsVersion() */ /*! + T *q_check_ptr(T *pointer) + \relates <QtGlobal> + + Users Q_CHECK_PTR on \a pointer, then returns \a pointer. + + This can be used as an inline version of Q_CHECK_PTR. +*/ + +/*! \macro const char* Q_FUNC_INFO() \relates <QtGlobal> @@ -1796,6 +1939,17 @@ void qt_check_pointer(const char *n, int l) qWarning("In file %s, line %d: Out of memory", n, l); } +#ifndef QT_NO_EXCEPTIONS +/* \internal + Allows you to throw an exception without including <new> + Called internally from Q_CHECK_PTR on certain OS combinations +*/ +void qBadAlloc() +{ + QT_THROW(std::bad_alloc()); +} +#endif + /* The Q_ASSERT macro calls this function when the test fails. */ @@ -1853,7 +2007,7 @@ void *qMemSet(void *dest, int c, size_t n) { return memset(dest, c, n); } static QtMsgHandler handler = 0; // pointer to debug handler -#ifdef Q_CC_MWERKS +#if defined(Q_CC_MWERKS) && defined(Q_OS_MACX) extern bool qt_is_gui_used; static void mac_default_handler(const char *msg) { @@ -1865,7 +2019,7 @@ static void mac_default_handler(const char *msg) fprintf(stderr, msg); } } -#endif // Q_CC_MWERKS +#endif // Q_CC_MWERKS && Q_OS_MACX @@ -1941,8 +2095,8 @@ QString qt_error_string(int errorCode) warnings, critical and fatal error messages. The Qt library (debug mode) contains hundreds of warning messages that are printed when internal errors (usually invalid function arguments) - occur. Qt built in release mode also contains such warnings unless - QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during + occur. Qt built in release mode also contains such warnings unless + QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during compilation. If you implement your own message handler, you get total control of these messages. @@ -1986,12 +2140,23 @@ void qt_message_output(QtMsgType msgType, const char *buf) if (handler) { (*handler)(msgType, buf); } else { -#if defined(Q_CC_MWERKS) +#if defined(Q_CC_MWERKS) && defined(Q_OS_MACX) mac_default_handler(buf); #elif defined(Q_OS_WINCE) QString fstr = QString::fromLatin1(buf); fstr += QLatin1Char('\n'); OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16())); +#elif defined(Q_OS_SYMBIAN) + // RDebug::Print has a cap of 256 characters so break it up + _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()))); + 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); + } + delete hbuffer; #else fprintf(stderr, "%s\n", buf); fflush(stderr); @@ -2018,7 +2183,14 @@ void qt_message_output(QtMsgType msgType, const char *buf) _CrtDbgBreak(); #endif -#if (defined(Q_OS_UNIX) || defined(Q_CC_MINGW)) +#if defined(Q_OS_SYMBIAN) + __DEBUGGER(); // on the emulator, get the debugger to kick in if there's one around + TBuf<256> tmp; + TPtrC8 ptr(reinterpret_cast<const TUint8*>(buf)); + TInt len = Min(tmp.MaxLength(), ptr.Length()); + tmp.Copy(ptr.Left(len)); + User::Panic(tmp, 0); // Panic the current thread +#elif (defined(Q_OS_UNIX) || defined(Q_CC_MINGW)) abort(); // trap; generates core dump #else exit(1); // goodbye cruel world @@ -2026,6 +2198,48 @@ void qt_message_output(QtMsgType msgType, const char *buf) } } +#if !defined(QT_NO_EXCEPTIONS) +/*! + \internal + Uses a local buffer to output the message. Not locale safe + cuts off + everything after character 1023, but will work in out of memory situations. +*/ +static void qEmergencyOut(QtMsgType msgType, const char *msg, va_list ap) +{ + char emergency_buf[256] = { '\0' }; + emergency_buf[255] = '\0'; + if (msg) + qvsnprintf(emergency_buf, 255, msg, ap); + qt_message_output(msgType, emergency_buf); +} +#endif + +/*! + \internal +*/ +static void qt_message(QtMsgType msgType, const char *msg, va_list ap) +{ +#if !defined(QT_NO_EXCEPTIONS) + if (std::uncaught_exception()) { + qEmergencyOut(msgType, msg, ap); + return; + } +#endif + QByteArray buf; + if (msg) { + QT_TRY { + buf = QString().vsprintf(msg, ap).toLocal8Bit(); + } QT_CATCH(const std::bad_alloc &) { +#if !defined(QT_NO_EXCEPTIONS) + qEmergencyOut(msgType, msg, ap); + // don't rethrow - we use qWarning and friends in destructors. + return; +#endif + } + } + qt_message_output(msgType, buf.constData()); +} + #undef qDebug /*! \relates <QtGlobal> @@ -2063,14 +2277,10 @@ void qt_message_output(QtMsgType msgType, const char *buf) */ void qDebug(const char *msg, ...) { - QString buf; va_list ap; - va_start(ap, msg); // use variable arg list - if (msg) - buf.vsprintf(msg, ap); + va_start(ap, msg); // use variable arg list + qt_message(QtDebugMsg, msg, ap); va_end(ap); - - qt_message_output(QtDebugMsg, buf.toLocal8Bit().constData()); } #undef qWarning @@ -2107,14 +2317,10 @@ void qDebug(const char *msg, ...) */ void qWarning(const char *msg, ...) { - QString buf; va_list ap; va_start(ap, msg); // use variable arg list - if (msg) - buf.vsprintf(msg, ap); + qt_message(QtWarningMsg, msg, ap); va_end(ap); - - qt_message_output(QtWarningMsg, buf.toLocal8Bit().constData()); } /*! @@ -2147,15 +2353,12 @@ void qWarning(const char *msg, ...) */ void qCritical(const char *msg, ...) { - QString buf; va_list ap; va_start(ap, msg); // use variable arg list - if (msg) - buf.vsprintf(msg, ap); + qt_message(QtCriticalMsg, msg, ap); va_end(ap); - - qt_message_output(QtCriticalMsg, buf.toLocal8Bit().constData()); } + #ifdef QT3_SUPPORT void qSystemWarning(const char *msg, int code) { qCritical("%s (%s)", msg, qt_error_string(code).toLocal8Bit().constData()); } @@ -2163,6 +2366,8 @@ void qSystemWarning(const char *msg, int code) void qErrnoWarning(const char *msg, ...) { + // qt_error_string() will allocate anyway, so we don't have + // to be careful here (like we do in plain qWarning()) QString buf; va_list ap; va_start(ap, msg); @@ -2175,6 +2380,8 @@ void qErrnoWarning(const char *msg, ...) void qErrnoWarning(int code, const char *msg, ...) { + // qt_error_string() will allocate anyway, so we don't have + // to be careful here (like we do in plain qWarning()) QString buf; va_list ap; va_start(ap, msg); @@ -2211,14 +2418,10 @@ void qErrnoWarning(int code, const char *msg, ...) */ void qFatal(const char *msg, ...) { - QString buf; va_list ap; va_start(ap, msg); // use variable arg list - if (msg) - buf.vsprintf(msg, ap); + qt_message(QtFatalMsg, msg, ap); va_end(ap); - - qt_message_output(QtFatalMsg, buf.toLocal8Bit().constData()); } // getenv is declared as deprecated in VS2005. This function @@ -2250,7 +2453,11 @@ bool qputenv(const char *varName, const QByteArray& value) QByteArray buffer(varName); buffer += '='; buffer += value; - return putenv(qstrdup(buffer.constData())) == 0; + char* envVar = qstrdup(buffer.constData()); + int result = putenv(envVar); + if (result != 0) // error. we have to delete the string. + delete[] envVar; + return result == 0; #endif } @@ -2264,7 +2471,9 @@ typedef uint SeedStorageType; # endif typedef QThreadStorage<SeedStorageType *> SeedStorage; +#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && !defined(Q_OS_SYMBIAN) Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value +#endif #endif @@ -2287,7 +2496,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) +#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && !defined(Q_OS_SYMBIAN) SeedStorageType *pseed = randTLS()->localData(); if (!pseed) randTLS()->setLocalData(pseed = new SeedStorageType); @@ -2316,7 +2525,7 @@ void qsrand(uint seed) */ int qrand() { -#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) +#if defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && !defined(Q_OS_SYMBIAN) SeedStorageType *pseed = randTLS()->localData(); if (!pseed) { randTLS()->setLocalData(pseed = new SeedStorageType); @@ -3006,7 +3215,7 @@ bool QInternal::callFunction(InternalFunction func, void **args) Compares the floating point value \a p1 and \a p2 and returns \c true if they are considered equal, otherwise \c false. - Note that comparing values where either \a p1 or \a p2 is 0.0 will not work. + Note that comparing values where either \a p1 or \a p2 is 0.0 will not work. The solution to this is to compare against values greater than or equal to 1.0. \snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 46 @@ -3067,4 +3276,185 @@ bool QInternal::callFunction(InternalFunction func, void **args) \sa Q_DECL_EXPORT */ +#if defined(Q_OS_SYMBIAN) + +#include <typeinfo> + +/*! \macro QT_TRAP_THROWING(function) + \relates <QtGlobal> + \ingroup qts60 + + TRAP leaves from Symbian \a function and throws an appropriate + standard C++ exception instead. + This must be used when calling Symbian OS leaving functions + from inside Qt or standard C++ code, so that the code can respond + correctly to the exception. + + \warning This macro is only available on Symbian. + + Example: + + \code + // A Symbian leaving function is being called within a Qt function. + // Any leave must be converted to an exception + CAknTitlePane* titlePane = S60->titlePane(); + if (titlePane) { + TPtrC captionPtr(qt_QString2TPtrC(caption)); + QT_TRAP_THROWING(titlePane->SetTextL(captionPtr)); + } + \endcode + + \sa QT_TRYCATCH_ERROR(), QT_TRYCATCH_LEAVING() +*/ + +/*! \macro QT_TRYCATCH_ERROR(error, function) + \relates <QtGlobal> + \ingroup qts60 + + Catch standard C++ exceptions from a \a function and convert them to a Symbian OS + \a error code, or \c KErrNone if there is no exception. + This must be used inside Qt or standard C++ code when using exception throwing + code (practically anything) and returning an error code to Symbian OS. + + \warning This macro is only available on Symbian. + + Example: + + \code + // An exception might be thrown in this Symbian TInt error returning function. + // It is caught and translated to an error code + TInt QServerApp::Connect(const QString &serverName) + { + TPtrC name; + TInt err; + QT_TRYCATCH_ERROR(err, name.Set(qt_QString2TPtrC(serverName))); + if (err != KErrNone) + return err; + return iServer.Connect(name); + } + \endcode +} + + \sa QT_TRYCATCH_LEAVING(), QT_TRAP_THROWING() +*/ + +/*! \macro QT_TRYCATCH_LEAVING(function) + \relates <QtGlobal> + \ingroup qts60 + + Catch standard C++ exceptions from \a function and convert them to Symbian OS + leaves. This must be used inside Qt or standard C++ code when using exception + throwing code (practically anything) and returning to Symbian OS from a leaving function. + For example inside a Symbian active object's \c RunL function implemented with Qt code. + + \warning This macro is only available on Symbian. + + Example: + + \code + // This active object signals Qt code + // Exceptions from the Qt code must be converted to Symbian OS leaves for the active scheduler + void QWakeUpActiveObject::RunL() + { + iStatus = KRequestPending; + SetActive(); + QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled()); + } + \endcode + + \sa QT_TRAP_THROWING(), QT_TRYCATCH_ERROR() +*/ + +#include <stdexcept> + +class QSymbianLeaveException : public std::exception +{ +public: + inline QSymbianLeaveException(int err) : error(err) {} + inline const char* what() const throw() { return "Symbian leave exception"; } + +public: + int error; +}; + +/*! \relates <QtGlobal> + \ingroup qts60 + + Throws an exception if the \a error parameter is a symbian error code. + This is the exception throwing equivalent of Symbian's User::LeaveIfError. + + \warning This function is only available on Symbian. + + \sa qt_exception2SymbianLeaveL(), qt_exception2SymbianError() +*/ +void qt_throwIfError(int error) +{ + if (error >= KErrNone) + return; // do nothing - not an exception + switch (error) { + case KErrNoMemory: + throw std::bad_alloc(); + case KErrArgument: + throw std::invalid_argument("from Symbian error"); + case KErrOverflow: + throw std::overflow_error("from Symbian error"); + case KErrUnderflow: + throw std::underflow_error("from Symbian error"); + default: + throw QSymbianLeaveException(error); + } +} + +/*! \relates <QtGlobal> + \ingroup qts60 + + Convert a caught standard C++ exception \a aThrow to a Symbian leave + + \warning This function is only available on Symbian. + + \sa qt_throwIfError(), qt_exception2SymbianError() +*/ +void qt_exception2SymbianLeaveL(const std::exception& aThrow) +{ + User::Leave(qt_exception2SymbianError(aThrow)); +} + +/*! \relates <QtGlobal> + \ingroup qts60 + + Convert a caught standard C++ exception \a aThrow to a Symbian error code + + \warning This function is only available on Symbian. + + \sa qt_throwIfError(), qt_exception2SymbianLeaveL() +*/ +int qt_exception2SymbianError(const std::exception& aThrow) +{ + const std::type_info& atype = typeid(aThrow); + int err = KErrGeneral; + + if(atype == typeid (std::bad_alloc)) + err = KErrNoMemory; + else if(atype == typeid(QSymbianLeaveException)) + err = static_cast<const QSymbianLeaveException&>(aThrow).error; + else { + if(atype == typeid(std::invalid_argument)) + err = KErrArgument; + else if(atype == typeid(std::out_of_range)) + // std::out_of_range is of type logic_error which by definition means that it is + // "presumably detectable before the program executes". + // std::out_of_range is used to report an argument is not within the expected range. + // The description of KErrArgument says an argument is out of range. Hence the mapping. + err = KErrArgument; + else if(atype == typeid(std::overflow_error)) + err = KErrOverflow; + else if(atype == typeid(std::underflow_error)) + err = KErrUnderflow; + qWarning("translation from std exception \"%s\" to %d", aThrow.what(), err); + } + + return err; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 7076a1e..35edb20 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -110,7 +110,7 @@ namespace QT_NAMESPACE {} This expands to a "using QT_NAMESPACE" also in _header files_. It is the only way the feature can be used without too much pain, but if people _really_ do not want it they can add - DEFINES += QT_NO_USING_NAMESPACE to theur .pro files. + DEFINES += QT_NO_USING_NAMESPACE to their .pro files. */ QT_USE_NAMESPACE # endif @@ -144,6 +144,7 @@ namespace QT_NAMESPACE {} The operating system, must be one of: (Q_OS_x) DARWIN - Darwin OS (synonym for Q_OS_MAC) + SYMBIAN - Symbian MSDOS - MS-DOS and Windows OS2 - OS/2 OS2EMX - XFree86 on OS/2 (not PM) @@ -182,6 +183,11 @@ namespace QT_NAMESPACE {} # else # define Q_OS_DARWIN32 # endif +#elif defined(__SYMBIAN32__) || defined(SYMBIAN) +# define Q_OS_SYMBIAN +# define Q_NO_POSIX_SIGNALS +// TODO: should this be in qconfig.h +# define QT_NO_GETIFADDRS #elif defined(__CYGWIN__) # define Q_OS_CYGWIN #elif defined(MSDOS) || defined(_MSDOS) @@ -348,7 +354,9 @@ namespace QT_NAMESPACE {} HIGHC - MetaWare High C/C++ PGI - Portland Group C++ GHS - Green Hills Optimizing C++ Compilers + GCCE - GCCE (Symbian GCCE builds) RVCT - ARM Realview Compiler Suite + NOKIAX86 - Nokia x86 (Symbian WINSCW builds) Should be sorted most to least authoritative. @@ -369,6 +377,9 @@ namespace QT_NAMESPACE {} #elif defined(__MWERKS__) # define Q_CC_MWERKS +# if defined(__EMU_SYMBIAN_OS__) +# define Q_CC_NOKIAX86 +# endif /* "explicit" recognized since 4.0d1 */ #elif defined(_MSC_VER) @@ -423,7 +434,15 @@ namespace QT_NAMESPACE {} #elif defined(__WATCOMC__) # define Q_CC_WAT -#elif defined(__CC_ARM) +/* Symbian GCCE */ +#elif defined(__GCCE__) +# define Q_CC_GCCE +# define QT_VISIBILITY_AVAILABLE + +/* ARM Realview Compiler Suite + RVCT compiler also defines __EDG__ and __GNUC__ (if --gnu flag is given), + so check for it before that */ +#elif defined(__ARMCC__) || defined(__CC_ARM) # define Q_CC_RVCT #elif defined(__GNUC__) @@ -705,6 +724,11 @@ namespace QT_NAMESPACE {} # endif # define Q_NO_USING_KEYWORD /* ### check "using" status */ +#elif defined(__WINSCW__) && !defined(Q_CC_NOKIAX86) +# define Q_CC_NOKIAX86 +// # define Q_CC_MWERKS // May be required + + #else # error "Qt has not been tested with this compiler - talk to qt-bugs@trolltech.com" #endif @@ -752,6 +776,7 @@ namespace QT_NAMESPACE {} QWS - Qt for Embedded Linux WIN32 - Windows X11 - X Window System + S60 - Symbian S60 PM - unsupported WIN16 - unsupported */ @@ -784,6 +809,10 @@ namespace QT_NAMESPACE {} # elif defined(Q_OS_MAC32) # define Q_WS_MAC32 # endif +# elif defined(Q_OS_SYMBIAN) +# if (defined(__SERIES60_31__) || defined(__S60_32__) || defined(__S60_50__)) && !defined(QT_NO_S60) +# define Q_WS_S60 +# endif # elif !defined(Q_WS_QWS) # define Q_WS_X11 # endif @@ -809,7 +838,7 @@ typedef short qint16; /* 16 bit signed */ typedef unsigned short quint16; /* 16 bit unsigned */ typedef int qint32; /* 32 bit signed */ typedef unsigned int quint32; /* 32 bit unsigned */ -#if defined(Q_OS_WIN) && !defined(Q_CC_GNU) +#if defined(Q_OS_WIN) && !defined(Q_CC_GNU) && !defined(Q_CC_MWERKS) # define Q_INT64_C(c) c ## i64 /* signed 64 bit constant */ # define Q_UINT64_C(c) c ## ui64 /* unsigned 64 bit constant */ typedef __int64 qint64; /* 64 bit signed */ @@ -827,7 +856,7 @@ typedef quint64 qulonglong; #ifndef QT_POINTER_SIZE # if defined(Q_OS_WIN64) # define QT_POINTER_SIZE 8 -# elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +# elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) # define QT_POINTER_SIZE 4 # endif #endif @@ -874,6 +903,12 @@ QT_END_INCLUDE_NAMESPACE */ #ifndef QT_LINUXBASE /* the LSB defines TRUE and FALSE for us */ +/* Symbian OS defines TRUE = 1 and FALSE = 0, +redefine to built-in booleans to make autotests work properly */ +#ifdef Q_OS_SYMBIAN + #undef TRUE + #undef FALSE +#endif # ifndef TRUE # define TRUE true # define FALSE false @@ -1028,7 +1063,7 @@ typedef int QNoImplicitBoolCast; // This logic must match the one in qmetatype.h #if defined(QT_COORD_TYPE) typedef QT_COORD_TYPE qreal; -#elif defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) +#elif defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) || defined(QT_ARCH_SYMBIAN) typedef float qreal; #else typedef double qreal; @@ -1044,8 +1079,13 @@ 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); } +#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 - qint64(d-1) + 0.5) + qint64(d-1); } +#else inline qint64 qRound64(qreal d) { return d >= 0.0 ? qint64(d + 0.5) : qint64(d - qint64(d-1) + 0.5) + qint64(d-1); } +#endif template <typename T> inline const T &qMin(const T &a, const T &b) { if (a < b) return a; return b; } @@ -1116,6 +1156,8 @@ class QDataStream; #ifndef Q_DECL_EXPORT # ifdef Q_OS_WIN # define Q_DECL_EXPORT __declspec(dllexport) +# elif defined(Q_CC_NOKIAX86) || defined(Q_CC_RVCT) +# define Q_DECL_EXPORT __declspec(dllexport) # elif defined(QT_VISIBILITY_AVAILABLE) # define Q_DECL_EXPORT __attribute__((visibility("default"))) # endif @@ -1126,6 +1168,8 @@ class QDataStream; #ifndef Q_DECL_IMPORT # if defined(Q_OS_WIN) # define Q_DECL_IMPORT __declspec(dllimport) +# elif defined(Q_CC_NOKIAX86) || defined(Q_CC_RVCT) +# define Q_DECL_IMPORT __declspec(dllimport) # else # define Q_DECL_IMPORT # endif @@ -1135,7 +1179,7 @@ class QDataStream; Create Qt DLL if QT_DLL is defined (Windows only) */ -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) # if defined(QT_NODLL) # undef QT_MAKEDLL # undef QT_DLL @@ -1295,16 +1339,42 @@ class QDataStream; for Qt's internal unit tests. If you want slower loading times and more symbols that can vanish from version to version, feel free to define QT_BUILD_INTERNAL. */ -#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_WIN) && defined(QT_MAKEDLL) +#if defined(QT_BUILD_INTERNAL) && (defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_MAKEDLL) # define Q_AUTOTEST_EXPORT Q_DECL_EXPORT -#elif defined(QT_BUILD_INTERNAL) && defined(Q_OS_WIN) && defined(QT_DLL) +#elif defined(QT_BUILD_INTERNAL) && (defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_DLL) # define Q_AUTOTEST_EXPORT Q_DECL_IMPORT -#elif defined(QT_BUILD_INTERNAL) && !defined(Q_OS_WIN) && defined(QT_SHARED) +#elif defined(QT_BUILD_INTERNAL) && !(defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_SHARED) # define Q_AUTOTEST_EXPORT Q_DECL_EXPORT #else # define Q_AUTOTEST_EXPORT #endif +inline void qt_noop() {} + +/* These wrap try/catch so we can switch off exceptions later. + + Beware - do not use more than one QT_CATCH per QT_TRY, and do not use + the exception instance in the catch block. + If you can't live with those constraints, don't use these macros. + Use the QT_NO_EXCEPTIONS macro to protect your code instead. +*/ + +#ifdef QT_BOOTSTRAPPED +# define QT_NO_EXCEPTIONS +#endif + +#ifdef QT_NO_EXCEPTIONS +# define QT_TRY if (true) +# define QT_CATCH(A) else +# define QT_THROW(A) qt_noop() +# define QT_RETHROW qt_noop() +#else +# define QT_TRY try +# define QT_CATCH(A) catch (A) +# define QT_THROW(A) throw A +# define QT_RETHROW throw +#endif + /* System information */ @@ -1401,6 +1471,23 @@ public: }; static const MacVersion MacintoshVersion; #endif +#ifdef Q_OS_SYMBIAN + enum SymVersion { + SV_Unknown = 0x0000, + SV_9_2 = 0x0001, + SV_9_3 = 0x0002, + SV_9_4 = 0x0004 + }; + static SymVersion symbianVersion(); + enum S60Version { + SV_S60_None = 0x0000, + SV_S60_Unknown = 0x0001, + SV_S60_3_1 = 0x0002, + SV_S60_3_2 = 0x0004, + SV_S60_5_0 = 0x0008 + }; + static S60Version s60Version(); +#endif }; Q_CORE_EXPORT const char *qVersion(); @@ -1445,7 +1532,7 @@ inline QT3_SUPPORT int qWinVersion() { return QSysInfo::WindowsVersion; } Avoid "unused parameter" warnings */ -#if defined(Q_CC_INTEL) && !defined(Q_OS_WIN) +#if defined(Q_CC_INTEL) && !defined(Q_OS_WIN) || defined(Q_CC_RVCT) template <typename T> inline void qUnused(T &x) { (void)x; } # define Q_UNUSED(x) qUnused(x); @@ -1457,6 +1544,10 @@ inline void qUnused(T &x) { (void)x; } Debugging and error handling */ +#if defined(Q_OS_SYMBIAN) && defined(NDEBUG) && !defined(QT_NO_DEBUG) +# define QT_NO_DEBUG +#endif + #if !defined(QT_NO_DEBUG) && !defined(QT_DEBUG) # define QT_DEBUG #endif @@ -1525,8 +1616,6 @@ inline QNoDebug qDebug(); #endif -inline void qt_noop() {} - Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line); #if !defined(Q_ASSERT) @@ -1553,12 +1642,23 @@ Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char * Q_CORE_EXPORT void qt_check_pointer(const char *, int); -#ifndef QT_NO_DEBUG -# define Q_CHECK_PTR(p) do {if(!(p))qt_check_pointer(__FILE__,__LINE__);} while (0) +#ifndef QT_NO_EXCEPTIONS +Q_CORE_EXPORT void qBadAlloc(); +#endif + +#ifdef QT_NO_EXCEPTIONS +# if defined(QT_NO_DEBUG) +# define Q_CHECK_PTR(p) qt_noop(); +# else +# define Q_CHECK_PTR(p) do {if(!(p))qt_check_pointer(__FILE__,__LINE__);} while (0) +# endif #else -# define Q_CHECK_PTR(p) +# define Q_CHECK_PTR(p) do { if (!(p)) qBadAlloc(); } while (0) #endif +template <typename T> +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) @@ -1569,7 +1669,7 @@ Q_CORE_EXPORT void qt_check_pointer(const char *, int); # define Q_FUNC_INFO __FUNCSIG__ # endif #else -# if defined(Q_OS_SOLARIS) || defined(Q_CC_XLC) +# if defined(Q_OS_SOLARIS) || defined(Q_CC_XLC) || defined(Q_OS_SYMBIAN) # define Q_FUNC_INFO __FILE__ "(line number unavailable)" # else /* These two macros makes it possible to turn the builtin line expander into a @@ -1578,9 +1678,9 @@ Q_CORE_EXPORT void qt_check_pointer(const char *, int); # define QT_STRINGIFY(x) QT_STRINGIFY2(x) # define Q_FUNC_INFO __FILE__ ":" QT_STRINGIFY(__LINE__) # endif - /* The MIPSpro compiler postpones macro expansion, and therefore macros must be in scope - * when being used. */ -# if !defined(Q_CC_MIPS) + /* The MIPSpro and RVCT compilers postpones macro expansion, + and therefore macros must be in scope when being used. */ +# if !defined(Q_CC_MIPS) && !defined(Q_CC_RVCT) && !defined(Q_CC_NOKIAX86) # undef QT_STRINGIFY2 # undef QT_STRINGIFY # endif @@ -1705,12 +1805,12 @@ public: static TYPE *NAME() \ { \ if (!this_##NAME.pointer && !this_##NAME.destroyed) { \ - TYPE *x = new TYPE; \ + QScopedPointer<TYPE > x(new TYPE); \ INITIALIZER; \ - if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \ - delete x; \ - else \ + if (this_##NAME.pointer.testAndSetOrdered(0, x.data())) { \ static QGlobalStaticDeleter<TYPE > cleanup(this_##NAME); \ + x.take(); \ + } \ } \ return this_##NAME.pointer; \ } @@ -1913,7 +2013,7 @@ public: \ types must declare a 'bool isDetached(void) const;' member for this to work. */ -#if defined Q_CC_MSVC && _MSC_VER < 1300 +#if (defined Q_CC_MSVC && _MSC_VER < 1300) || defined(Q_CC_MWERKS) template <typename T> inline void qSwap_helper(T &value1, T &value2, T*) { @@ -2096,7 +2196,7 @@ typedef uint Flags; #endif /* Q_NO_TYPESAFE_FLAGS */ -#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) +#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_RVCT) /* make use of typeof-extension */ template <typename T> class QForeachContainer { @@ -2190,9 +2290,12 @@ inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase #endif #endif +template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; } +template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); } + #define Q_DECLARE_PRIVATE(Class) \ - inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } \ - inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } \ + inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \ + inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \ friend class Class##Private; #define Q_DECLARE_PRIVATE_D(Dptr, Class) \ @@ -2252,7 +2355,9 @@ Q_CORE_EXPORT QString qtTrId(const char *id, int n = -1); Class(const Class &); \ Class &operator=(const Class &); #else -# define Q_DISABLE_COPY(Class) +# define Q_DISABLE_COPY(Class) \ + Class(const Class &); \ + Class &operator=(const Class &); #endif class QByteArray; @@ -2289,6 +2394,43 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathTranslations(); QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #endif +#if defined(Q_OS_SYMBIAN) +QT_END_NAMESPACE +// forward declare std::exception +#ifdef __cplusplus +namespace std { class exception; } +#endif +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT void qt_throwIfError(int error); +Q_CORE_EXPORT void qt_exception2SymbianLeaveL(const std::exception& ex); +Q_CORE_EXPORT int qt_exception2SymbianError(const std::exception& ex); + +#define QT_TRAP_THROWING(_f) \ + { \ + TInt ____error; \ + TRAP(____error, _f); \ + qt_throwIfError(____error); \ + } + +#define QT_TRYCATCH_ERROR(_err, _f) \ + { \ + _err = KErrNone; \ + try { \ + _f; \ + } catch (const std::exception &____ex) { \ + _err = qt_exception2SymbianError(____ex); \ + } \ + } + +#define QT_TRYCATCH_LEAVING(_f) \ + { \ + TInt ____err; \ + QT_TRYCATCH_ERROR(____err, _f) \ + User::LeaveIfError(____err); \ + } +#endif + + /* This gives us the possibility to check which modules the user can use. These are purely compile time checks and will generate no code. @@ -2367,6 +2509,9 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #define QT_LICENSED_MODULE(x) \ enum QtValidLicenseFor##x##Module { Licensed##x = true }; +/* qdoc is really unhappy with the following block of preprocessor checks, + making it difficult to document classes properly after this point. */ + #if (QT_EDITION & QT_MODULE_CORE) QT_LICENSED_MODULE(Core) #endif diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 51c7988..ba87340 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -40,11 +40,11 @@ ****************************************************************************/ #include "qdir.h" -#include "qfile.h" +#include "qfile.h" #include "qconfig.h" #include "qsettings.h" #include "qlibraryinfo.h" -#include "qpointer.h" +#include "qscopedpointer.h" #ifdef QT_BUILD_QMAKE QT_BEGIN_NAMESPACE @@ -67,8 +67,7 @@ QT_BEGIN_NAMESPACE struct QLibrarySettings { QLibrarySettings(); - ~QLibrarySettings() { delete static_cast<QSettings *>(settings); } - QSettings *settings; + QScopedPointer<QSettings> settings; }; Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings) @@ -79,32 +78,19 @@ public: static void cleanup() { QLibrarySettings *ls = qt_library_settings(); - if (ls) { - delete static_cast<QSettings *>(ls->settings); - ls->settings = 0; - } + if (ls) + ls->settings.reset(0); } static QSettings *configuration() { -#ifdef QT_NO_THREAD - // This recursion guard should be a temporary solution; the recursive - // dependency should be found and removed. - static bool initializing = false; - if (initializing) - return 0; - initializing = true; -#endif QLibrarySettings *ls = qt_library_settings(); -#ifdef QT_NO_THREAD - initializing = false; -#endif - return ls ? static_cast<QSettings *>(qt_library_settings()->settings) : (QSettings*)0; + return ls ? ls->settings.data() : 0; } }; QLibrarySettings::QLibrarySettings() + : settings(QLibraryInfoPrivate::findConfiguration()) { - settings = QLibraryInfoPrivate::findConfiguration(); #ifndef QT_BUILD_QMAKE qAddPostRoutine(QLibraryInfoPrivate::cleanup); #endif diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 3c95f2c..f86ffd3 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -44,6 +44,10 @@ #include <QtCore/qglobal.h> +#ifdef Q_OS_SYMBIAN +# include <e32def.h> +#endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -1404,21 +1408,27 @@ public: ImFont, ImCursorPosition, ImSurroundingText, - ImCurrentSelection + ImCurrentSelection, + ImMaximumTextLength, + ImAnchorPosition }; enum InputMethodHint { ImhNone = 0x0, ImhHiddenText = 0x1, - ImhNumbersOnly = 0x2, - ImhUppercaseOnly = 0x4, - ImhLowercaseOnly = 0x8, - ImhNoAutoUppercase = 0x10, - ImhPreferNumbers = 0x20, - ImhPreferUppercase = 0x40, - ImhPreferLowercase = 0x80, - ImhNoPredictiveText = 0x100, - ImhDialableCharactersOnly = 0x200 + ImhNoAutoUppercase = 0x2, + ImhPreferNumbers = 0x4, + ImhPreferUppercase = 0x8, + ImhPreferLowercase = 0x10, + ImhNoPredictiveText = 0x20, + + ImhDigitsOnly = 0x10000, + ImhFormattedNumbersOnly = 0x20000, + ImhUppercaseOnly = 0x40000, + ImhLowercaseOnly = 0x80000, + ImhDialableCharactersOnly = 0x100000, + + ImhExclusiveInputMask = 0xffff0000 }; Q_DECLARE_FLAGS(InputMethodHints, InputMethodHint) @@ -1515,6 +1525,8 @@ public: typedef unsigned long HANDLE; #elif defined(Q_WS_QWS) typedef void * HANDLE; +#elif defined(Q_OS_SYMBIAN) + typedef unsigned long int HANDLE; // equivalent to TUint32 #endif typedef WindowFlags WFlags; @@ -1606,8 +1618,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::DropActions) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::ItemFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::MatchFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TextInteractionFlags) -Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TouchPointStates) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::InputMethodHints) +Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TouchPointStates) typedef bool (*qInternalCallback)(void **); diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index bd41f5e..b49554e 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -64,7 +64,8 @@ win32 { } else:unix { SOURCES += io/qfsfileengine_unix.cpp SOURCES += io/qfsfileengine_iterator_unix.cpp - SOURCES += io/qprocess_unix.cpp + symbian:SOURCES += io/qprocess_symbian.cpp + else:SOURCES += io/qprocess_unix.cpp macx-*: { HEADERS += io/qfilesystemwatcher_fsevents_p.h SOURCES += io/qsettings_mac.cpp io/qfilesystemwatcher_fsevents.cpp @@ -84,4 +85,9 @@ win32 { SOURCES += io/qfilesystemwatcher_kqueue.cpp HEADERS += io/qfilesystemwatcher_kqueue_p.h } + + symbian { + SOURCES += io/qfilesystemwatcher_symbian.cpp + HEADERS += io/qfilesystemwatcher_symbian_p.h + } } diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp index 28a543b..c50263b 100644 --- a/src/corelib/io/qabstractfileengine.cpp +++ b/src/corelib/io/qabstractfileengine.cpp @@ -356,8 +356,6 @@ QAbstractFileEngine::QAbstractFileEngine(QAbstractFileEnginePrivate &dd) : d_ptr */ QAbstractFileEngine::~QAbstractFileEngine() { - delete d_ptr; - d_ptr = 0; } /*! @@ -885,7 +883,6 @@ QAbstractFileEngineIterator::QAbstractFileEngineIterator(QDir::Filters filters, */ QAbstractFileEngineIterator::~QAbstractFileEngineIterator() { - delete d; } /*! diff --git a/src/corelib/io/qabstractfileengine.h b/src/corelib/io/qabstractfileengine.h index 1bd79da..7029e8a 100644 --- a/src/corelib/io/qabstractfileengine.h +++ b/src/corelib/io/qabstractfileengine.h @@ -194,7 +194,7 @@ protected: QAbstractFileEngine(); QAbstractFileEngine(QAbstractFileEnginePrivate &); - QAbstractFileEnginePrivate *d_ptr; + QScopedPointer<QAbstractFileEnginePrivate> d_ptr; private: Q_DECLARE_PRIVATE(QAbstractFileEngine) Q_DISABLE_COPY(QAbstractFileEngine) @@ -238,7 +238,7 @@ private: friend class QDirIterator; friend class QDirIteratorPrivate; void setPath(const QString &path); - QAbstractFileEngineIteratorPrivate *d; + QScopedPointer<QAbstractFileEngineIteratorPrivate> d; }; QT_END_NAMESPACE diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index e78a35b..bd7b91c 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -80,8 +80,11 @@ public: inline QDebug &operator=(const QDebug &other); inline ~QDebug() { if (!--stream->ref) { - if(stream->message_output) - qt_message_output(stream->type, stream->buffer.toLocal8Bit().data()); + if(stream->message_output) { + QT_TRY { + qt_message_output(stream->type, stream->buffer.toLocal8Bit().data()); + } QT_CATCH(std::bad_alloc) { /* We're out of memory - give up. */ } + } delete stream; } } diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 92aef3c..8eb1ce7 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE static QString driveSpec(const QString &path) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) if (path.size() < 2) return QString(); char c = path.at(0).toAscii(); @@ -86,6 +86,7 @@ class QDirPrivate QDir *q_ptr; Q_DECLARE_PUBLIC(QDir) + friend struct QScopedPointerDeleter<QDirPrivate>; protected: QDirPrivate(QDir*, const QDir *copy=0); ~QDirPrivate(); @@ -150,7 +151,7 @@ private: QString path = p; if ((path.endsWith(QLatin1Char('/')) || path.endsWith(QLatin1Char('\\'))) && path.length() > 1) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) if (!(path.length() == 3 && path.at(1) == QLatin1Char(':'))) #endif path.truncate(path.length() - 1); @@ -287,10 +288,10 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, names->append(l.at(i).fileName()); } } else { - QDirSortItem *si = new QDirSortItem[n]; + QScopedArrayPointer<QDirSortItem> si(new QDirSortItem[n]); for (int i = 0; i < n; ++i) si[i].item = l.at(i); - qSort(si, si+n, QDirSortItemComparator(sort)); + qSort(si.data(), si.data()+n, QDirSortItemComparator(sort)); // put them back in the list(s) if(infos) { for (int i = 0; i < n; ++i) @@ -300,7 +301,6 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, for (int i = 0; i < n; ++i) names->append(si[i].item.fileName()); } - delete [] si; } } } @@ -333,8 +333,9 @@ void QDirPrivate::detach(bool createFileEngine) { qAtomicDetach(data); if (createFileEngine) { + QAbstractFileEngine *newFileEngine = QAbstractFileEngine::create(data->path); delete data->fileEngine; - data->fileEngine = QAbstractFileEngine::create(data->path); + data->fileEngine = newFileEngine; } } @@ -588,8 +589,6 @@ QDir::QDir(const QDir &dir) : d_ptr(new QDirPrivate(this, &dir)) QDir::~QDir() { - delete d_ptr; - d_ptr = 0; } /*! @@ -787,6 +786,8 @@ QString QDir::relativeFilePath(const QString &fileName) const if (fileDrive.toLower() != dirDrive.toLower() || (file.startsWith(QLatin1String("//")) && !dir.startsWith(QLatin1String("//")))) +#elif defined(Q_OS_SYMBIAN) + if (fileDrive.toLower() != dirDrive.toLower()) #else if (fileDrive != dirDrive) #endif @@ -802,7 +803,7 @@ QString QDir::relativeFilePath(const QString &fileName) const int i = 0; while (i < dirElts.size() && i < fileElts.size() && -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) dirElts.at(i).toLower() == fileElts.at(i).toLower()) #else dirElts.at(i) == fileElts.at(i)) @@ -849,7 +850,7 @@ QString QDir::convertSeparators(const QString &pathName) QString QDir::toNativeSeparators(const QString &pathName) { QString n(pathName); -#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) +#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) || defined(Q_OS_SYMBIAN) for (int i=0; i<(int)n.length(); i++) { if (n[i] == QLatin1Char('/')) n[i] = QLatin1Char('\\'); @@ -873,7 +874,7 @@ QString QDir::toNativeSeparators(const QString &pathName) QString QDir::fromNativeSeparators(const QString &pathName) { QString n(pathName); -#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) +#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) || defined(Q_OS_SYMBIAN) for (int i=0; i<(int)n.length(); i++) { if (n[i] == QLatin1Char('\\')) n[i] = QLatin1Char('/'); @@ -1826,10 +1827,10 @@ QFileInfoList QDir::drives() QChar QDir::separator() { -#if defined(Q_OS_UNIX) - return QLatin1Char('/'); -#elif defined (Q_FS_FAT) || defined(Q_WS_WIN) +#if defined (Q_FS_FAT) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) return QLatin1Char('\\'); +#elif defined(Q_OS_UNIX) + return QLatin1Char('/'); #elif defined (Q_OS_MAC) return QLatin1Char(':'); #else @@ -1929,7 +1930,8 @@ QString QDir::currentPath() Under non-Windows operating systems the \c HOME environment variable is used if it exists, otherwise the path returned by the - rootPath() function is used. + rootPath() function is used, except in Symbian, where c:\\data is + returned. \sa home(), currentPath(), rootPath(), tempPath() */ @@ -2151,7 +2153,7 @@ QString QDir::cleanPath(const QString &path) levels++; } } else if(last != -1 && iwrite - last == 1) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) eaten = (iwrite > 2); #else eaten = true; diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h index 79a5be635..faec0f5 100644 --- a/src/corelib/io/qdir.h +++ b/src/corelib/io/qdir.h @@ -45,6 +45,7 @@ #include <QtCore/qstring.h> #include <QtCore/qfileinfo.h> #include <QtCore/qstringlist.h> +#include <QtCore/qscopedpointer.h> QT_BEGIN_HEADER @@ -57,7 +58,7 @@ class QDirPrivate; class Q_CORE_EXPORT QDir { protected: - QDirPrivate *d_ptr; + QScopedPointer<QDirPrivate> d_ptr; private: Q_DECLARE_PRIVATE(QDir) public: diff --git a/src/corelib/io/qdiriterator.cpp b/src/corelib/io/qdiriterator.cpp index d9df480..3b7b203 100644 --- a/src/corelib/io/qdiriterator.cpp +++ b/src/corelib/io/qdiriterator.cpp @@ -99,6 +99,18 @@ QT_BEGIN_NAMESPACE +class QDirIteratorPrivateIteratorStack : public QStack<QAbstractFileEngineIterator *> +{ +public: + ~QDirIteratorPrivateIteratorStack(); +}; + +QDirIteratorPrivateIteratorStack::~QDirIteratorPrivateIteratorStack() +{ + qDeleteAll(*this); +} + + class QDirIteratorPrivate { public: @@ -112,7 +124,7 @@ public: void checkAndPushDirectory(const QFileInfo &); bool matchesFilters(const QString &fileName, const QFileInfo &fi) const; - QAbstractFileEngine * const engine; + QScopedPointer<QAbstractFileEngine> engine; const QString path; const QStringList nameFilters; @@ -123,7 +135,7 @@ public: QVector<QRegExp> nameRegExps; #endif - QStack<QAbstractFileEngineIterator *> fileEngineIterators; + QDirIteratorPrivateIteratorStack fileEngineIterators; QFileInfo currentFileInfo; QFileInfo nextFileInfo; @@ -163,7 +175,6 @@ QDirIteratorPrivate::QDirIteratorPrivate(const QString &path, const QStringList */ QDirIteratorPrivate::~QDirIteratorPrivate() { - delete engine; } /*! @@ -431,8 +442,6 @@ QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters, */ QDirIterator::~QDirIterator() { - qDeleteAll(d->fileEngineIterators); - delete d; } /*! diff --git a/src/corelib/io/qdiriterator.h b/src/corelib/io/qdiriterator.h index a3500cf..d495a0b 100644 --- a/src/corelib/io/qdiriterator.h +++ b/src/corelib/io/qdiriterator.h @@ -84,7 +84,7 @@ public: private: Q_DISABLE_COPY(QDirIterator) - QDirIteratorPrivate *d; + QScopedPointer<QDirIteratorPrivate> d; friend class QDir; }; diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 1718f3b..69f1ff5 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -109,6 +109,7 @@ QFilePrivate::openExternalFile(int flags, int fd) return false; #else delete fileEngine; + fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; fe->setFileName(fileName); fileEngine = fe; @@ -125,6 +126,7 @@ QFilePrivate::openExternalFile(int flags, FILE *fh) return false; #else delete fileEngine; + fileEngine = 0; QFSFileEngine *fe = new QFSFileEngine; fe->setFileName(fileName); fileEngine = fe; @@ -408,9 +410,6 @@ QFile::QFile(QFilePrivate &dd, QObject *parent) QFile::~QFile() { close(); -#ifdef QT_NO_QOBJECT - delete d_ptr; -#endif } /*! @@ -745,9 +744,10 @@ QFile::rename(const QString &newName) error = true; } } - if (error) + if (error) { out.remove(); - else { + } else { + fileEngine()->setFileName(newName); setPermissions(permissions()); unsetError(); setFileName(newName); @@ -793,6 +793,9 @@ QFile::rename(const QString &oldName, const QString &newName) \note To create a valid link on Windows, \a linkName must have a \c{.lnk} file extension. + \note On Symbian, no link is created and false is returned if fileName() + currently specifies a directory. + \sa setFileName() */ diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 7873c6a..dc42a6f 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -371,8 +371,6 @@ QFileInfo::QFileInfo(const QFileInfo &fileinfo) : d_ptr(new QFileInfoPrivate(&fi QFileInfo::~QFileInfo() { - delete d_ptr; - d_ptr = 0; } /*! diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h index 1a21fa7..598b9de 100644 --- a/src/corelib/io/qfileinfo.h +++ b/src/corelib/io/qfileinfo.h @@ -44,6 +44,7 @@ #include <QtCore/qfile.h> #include <QtCore/qlist.h> +#include <QtCore/qscopedpointer.h> QT_BEGIN_HEADER @@ -165,7 +166,7 @@ public: #endif protected: - QFileInfoPrivate *d_ptr; + QScopedPointer<QFileInfoPrivate> d_ptr; private: Q_DECLARE_PRIVATE(QFileInfo) }; diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index f7cc489..068f56a 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -62,6 +62,8 @@ # include "qfilesystemwatcher_fsevents_p.h" # endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) # include "qfilesystemwatcher_kqueue_p.h" +#elif defined(Q_OS_SYMBIAN) +# include "qfilesystemwatcher_symbian_p.h" #endif QT_BEGIN_NAMESPACE @@ -252,6 +254,8 @@ QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine() else # endif return QKqueueFileSystemWatcherEngine::create(); +#elif defined(Q_OS_SYMBIAN) + return new QSymbianFileSystemWatcherEngine; #else return 0; #endif diff --git a/src/corelib/io/qfilesystemwatcher_symbian.cpp b/src/corelib/io/qfilesystemwatcher_symbian.cpp new file mode 100644 index 0000000..49a5f34 --- /dev/null +++ b/src/corelib/io/qfilesystemwatcher_symbian.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemwatcher.h" +#include "qfilesystemwatcher_symbian_p.h" +#include "qfileinfo.h" +#include "qdebug.h" +#include "private/qcore_symbian_p.h" +#include <QDir> + +#ifndef QT_NO_FILESYSTEMWATCHER + + +QT_BEGIN_NAMESPACE + +CNotifyChangeEvent* CNotifyChangeEvent::New(RFs &fs, const TDesC& file, + QSymbianFileSystemWatcherEngine* e) +{ + CNotifyChangeEvent* self = new CNotifyChangeEvent(fs, file, e); + return self; +} + +CNotifyChangeEvent::CNotifyChangeEvent(RFs &fs, const TDesC& file, + QSymbianFileSystemWatcherEngine* e, TInt aPriority) : + CActive(aPriority), + fsSession(fs), + watchedPath(file), + engine(e) +{ + fsSession.NotifyChange(ENotifyAll, iStatus, file); + CActiveScheduler::Add(this); + SetActive(); +} + +CNotifyChangeEvent::~CNotifyChangeEvent() +{ + Cancel(); +} + +void CNotifyChangeEvent::RunL() +{ + if (iStatus.Int() == KErrNone){ + fsSession.NotifyChange(ENotifyAll, iStatus, watchedPath); + SetActive(); + QT_TRYCATCH_LEAVING(engine->emitPathChanged(this)); + } else { + qWarning("CNotifyChangeEvent::RunL() - Failed to order change notifications: %d", iStatus.Int()); + } +} + +void CNotifyChangeEvent::DoCancel() +{ + fsSession.NotifyChangeCancel(); +} + +QSymbianFileSystemWatcherEngine::QSymbianFileSystemWatcherEngine() : + watcherStarted(false) +{ + moveToThread(this); +} + +QSymbianFileSystemWatcherEngine::~QSymbianFileSystemWatcherEngine() +{ + stop(); +} + +QStringList QSymbianFileSystemWatcherEngine::addPaths(const QStringList &paths, + QStringList *files, QStringList *directories) +{ + QMutexLocker locker(&mutex); + QStringList p = paths; + + if (!startWatcher()) { + qWarning("Could not start QSymbianFileSystemWatcherEngine thread"); + + return p; + } + + QMutableListIterator<QString> it(p); + while (it.hasNext()) { + QString path = it.next(); + QFileInfo fi(path); + if (!fi.exists()) + continue; + + bool isDir = fi.isDir(); + if (isDir) { + if (directories->contains(path)) + continue; + } else { + if (files->contains(path)) + continue; + } + + // Use absolute filepath as relative paths seem to have some issues. + QString filePath = fi.absoluteFilePath(); + if(isDir && filePath.at(filePath.size()-1) != QChar('/')) { + filePath += QChar('/'); + } + + currentEvent = NULL; + QMetaObject::invokeMethod(this, + "addNativeListener", + Qt::QueuedConnection, + Q_ARG(QString, filePath)); + + syncCondition.wait(&mutex); + + if (currentEvent) { + currentEvent->isDir = isDir; + + activeObjectToPath.insert(currentEvent, path); + it.remove(); + + if (isDir) + directories->append(path); + else + files->append(path); + } + } + + return p; +} + +QStringList QSymbianFileSystemWatcherEngine::removePaths(const QStringList &paths, + QStringList *files, QStringList *directories) +{ + QMutexLocker locker(&mutex); + + QStringList p = paths; + QMutableListIterator<QString> it(p); + while (it.hasNext()) { + QString path = it.next(); + + currentEvent = activeObjectToPath.key(path); + if (!currentEvent) + continue; + activeObjectToPath.remove(currentEvent); + + QMetaObject::invokeMethod(this, + "removeNativeListener", + Qt::QueuedConnection); + + syncCondition.wait(&mutex); + + it.remove(); + + files->removeAll(path); + directories->removeAll(path); + } + + if (activeObjectToPath.size() == 0) + stop(); + + return p; +} + +void QSymbianFileSystemWatcherEngine::emitPathChanged(CNotifyChangeEvent *e) +{ + QMutexLocker locker(&mutex); + + QString path = activeObjectToPath.value(e); + QFileInfo fi(path); + + if (e->isDir) { + emit directoryChanged(path, !fi.exists()); + } else { + emit fileChanged(path, !fi.exists()); + } +} + +void QSymbianFileSystemWatcherEngine::stop() +{ + QMetaObject::invokeMethod(this, "quit"); + wait(); +} + +// This method must be called inside mutex +bool QSymbianFileSystemWatcherEngine::startWatcher() +{ + bool retval = true; + + if (!watcherStarted) { +#if defined(Q_OS_SYMBIAN) + setStackSize(0x5000); +#endif + start(); + syncCondition.wait(&mutex); + + if (errorCode != KErrNone) { + retval = false; + } else { + watcherStarted = true; + } + } + return retval; +} + + +void QSymbianFileSystemWatcherEngine::run() +{ + // Initialize file session + + errorCode = fsSession.Connect(); + + mutex.lock(); + syncCondition.wakeOne(); + mutex.unlock(); + + if (errorCode == KErrNone) { + exec(); + + foreach(CNotifyChangeEvent* e, activeObjectToPath.keys()) { + e->Cancel(); + delete e; + } + + activeObjectToPath.clear(); + fsSession.Close(); + watcherStarted = false; + } +} + +void QSymbianFileSystemWatcherEngine::addNativeListener(const QString &directoryPath) +{ + QMutexLocker locker(&mutex); + QString nativeDir(QDir::toNativeSeparators(directoryPath)); + TPtrC ptr(qt_QString2TPtrC(nativeDir)); + currentEvent = CNotifyChangeEvent::New(fsSession, ptr, this); + syncCondition.wakeOne(); +} + +void QSymbianFileSystemWatcherEngine::removeNativeListener() +{ + QMutexLocker locker(&mutex); + currentEvent->Cancel(); + delete currentEvent; + currentEvent = NULL; + syncCondition.wakeOne(); +} + + +QT_END_NAMESPACE +#endif // QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_symbian_p.h b/src/corelib/io/qfilesystemwatcher_symbian_p.h new file mode 100644 index 0000000..53b2b13 --- /dev/null +++ b/src/corelib/io/qfilesystemwatcher_symbian_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMWATCHER_SYMBIAN_P_H +#define QFILESYSTEMWATCHER_SYMBIAN_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 "qfilesystemwatcher_p.h" +#include "qhash.h" +#include "qmutex.h" +#include "qwaitcondition.h" + +#ifndef QT_NO_FILESYSTEMWATCHER + +#include <e32base.h> +#include <f32file.h> + +QT_BEGIN_NAMESPACE + +class QSymbianFileSystemWatcherEngine; + +class CNotifyChangeEvent : public CActive +{ +public: + CNotifyChangeEvent(RFs &fsSession, const TDesC& file, QSymbianFileSystemWatcherEngine* engine, + TInt aPriority = EPriorityStandard); + ~CNotifyChangeEvent(); + static CNotifyChangeEvent* New(RFs &fsSession, const TDesC& file, + QSymbianFileSystemWatcherEngine* engine); + + bool isDir; + +private: + void RunL(); + void DoCancel(); + + RFs &fsSession; + TPath watchedPath; + QSymbianFileSystemWatcherEngine *engine; +}; + +class QSymbianFileSystemWatcherEngine : public QFileSystemWatcherEngine +{ + Q_OBJECT + +public: + QSymbianFileSystemWatcherEngine(); + ~QSymbianFileSystemWatcherEngine(); + + QStringList addPaths(const QStringList &paths, QStringList *files, + QStringList *directories); + QStringList removePaths(const QStringList &paths, QStringList *files, + QStringList *directories); + + void stop(); + +protected: + void run(); + +public Q_SLOTS: + void addNativeListener(const QString &directoryPath); + void removeNativeListener(); + +private: + friend class CNotifyChangeEvent; + void emitPathChanged(CNotifyChangeEvent *e); + + bool startWatcher(); + + RFs fsSession; + QHash<CNotifyChangeEvent*, QString> activeObjectToPath; + QMutex mutex; + QWaitCondition syncCondition; + int errorCode; + bool watcherStarted; + CNotifyChangeEvent *currentEvent; +}; + +#endif // QT_NO_FILESYSTEMWATCHER + +QT_END_NAMESPACE + +#endif // QFILESYSTEMWATCHER_WIN_P_H diff --git a/src/corelib/io/qfsfileengine.h b/src/corelib/io/qfsfileengine.h index 9be8a4c..f6db91c 100644 --- a/src/corelib/io/qfsfileengine.h +++ b/src/corelib/io/qfsfileengine.h @@ -83,6 +83,9 @@ public: FileFlags fileFlags(FileFlags type) const; bool setPermissions(uint perms); QString fileName(FileName file) const; +#ifdef Q_OS_SYMBIAN + QString fileNameSymbian(FileName file) const; +#endif uint ownerId(FileOwner) const; QString owner(FileOwner) const; QDateTime fileTime(FileTime time) const; diff --git a/src/corelib/io/qfsfileengine_iterator_unix.cpp b/src/corelib/io/qfsfileengine_iterator_unix.cpp index c167546..61c17ba 100644 --- a/src/corelib/io/qfsfileengine_iterator_unix.cpp +++ b/src/corelib/io/qfsfileengine_iterator_unix.cpp @@ -53,7 +53,7 @@ class QFSFileEngineIteratorPlatformSpecificData public: inline QFSFileEngineIteratorPlatformSpecificData() : dir(0), dirEntry(0), done(false) -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) , mt_file(0) #endif {} @@ -62,7 +62,7 @@ public: dirent *dirEntry; bool done; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) // for readdir_r dirent *mt_file; #endif @@ -75,7 +75,7 @@ void QFSFileEngineIterator::advance() if (!platform->dir) return; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) if (::readdir_r(platform->dir, platform->mt_file, &platform->dirEntry) != 0) platform->done = true; #else @@ -86,7 +86,7 @@ void QFSFileEngineIterator::advance() ::closedir(platform->dir); platform->dir = 0; platform->done = true; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) delete [] platform->mt_file; platform->mt_file = 0; #endif @@ -102,7 +102,7 @@ void QFSFileEngineIterator::deletePlatformSpecifics() { if (platform->dir) { ::closedir(platform->dir); -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) delete [] platform->mt_file; platform->mt_file = 0; #endif @@ -123,7 +123,7 @@ bool QFSFileEngineIterator::hasNext() const if ((int) maxPathName == -1) maxPathName = FILENAME_MAX; maxPathName += sizeof(dirent) + 1; -#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) +#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 = (dirent *)new char[maxPathName]; diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index dc7fafd..d543541 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -52,12 +52,16 @@ #include "qfile.h" #include "qdir.h" #include "qdatetime.h" -#include "qdebug.h" #include "qvarlengtharray.h" #include <sys/mman.h> #include <stdlib.h> #include <limits.h> +#if defined(Q_OS_SYMBIAN) +# include <syslimits.h> +# include <f32file.h> +# include "private/qcore_symbian_p.h" +#endif #include <errno.h> #if !defined(QWS) && defined(Q_OS_MAC) # include <private/qcore_mac_p.h> @@ -65,6 +69,22 @@ QT_BEGIN_NAMESPACE + +#ifdef Q_OS_SYMBIAN +/*! + \internal + + Returns true if supplied path is a relative path +*/ +static bool isRelativePathSymbian(const QString& fileName) +{ + return !(fileName.startsWith(QLatin1Char('/')) + || (fileName.length() >= 2 + && ((fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) + || (fileName.at(0) == QLatin1Char('/') && fileName.at(1) == QLatin1Char('/'))))); +} +#endif + /*! \internal @@ -376,10 +396,37 @@ bool QFSFileEngine::remove() return unlink(d->nativeFilePath.constData()) == 0; } -bool QFSFileEngine::copy(const QString &) +bool QFSFileEngine::copy(const QString &newName) { +#if defined(Q_OS_SYMBIAN) + Q_D(QFSFileEngine); + RFs rfs; + TInt err = rfs.Connect(); + if (err == KErrNone) { + CFileMan* fm = NULL; + QString oldNative(QDir::toNativeSeparators(d->filePath)); + TPtrC oldPtr(qt_QString2TPtrC(oldNative)); + QFileInfo fi(newName); + QString absoluteNewName = fi.absolutePath() + QDir::separator() + fi.fileName(); + QString newNative(QDir::toNativeSeparators(absoluteNewName)); + TPtrC newPtr(qt_QString2TPtrC(newNative)); + TRAP (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; + rfs.Close(); + } + return (err == KErrNone); +#else // ### Add copy code for Unix here return false; +#endif } bool QFSFileEngine::rename(const QString &newName) @@ -403,7 +450,11 @@ bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) con { QString dirName = name; if (createParentDirectories) { +#if defined(Q_OS_SYMBIAN) + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); +#else dirName = QDir::cleanPath(dirName); +#endif for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) { slash = dirName.indexOf(QDir::separator(), oldslash+1); if (slash == -1) { @@ -435,7 +486,11 @@ bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) co { QString dirName = name; if (recurseParentDirectories) { +#if defined(Q_OS_SYMBIAN) + dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName)); +#else dirName = QDir::cleanPath(dirName); +#endif for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) { QByteArray chunk = QFile::encodeName(dirName.left(slash)); QT_STATBUF st; @@ -456,7 +511,11 @@ bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) co bool QFSFileEngine::caseSensitive() const { +#if defined(Q_OS_SYMBIAN) + return false; +#else return true; +#endif } bool QFSFileEngine::setCurrentPath(const QString &path) @@ -470,6 +529,16 @@ QString QFSFileEngine::currentPath(const QString &) { QString result; QT_STATBUF st; +#if defined(Q_OS_SYMBIAN) + char currentName[PATH_MAX+1]; + if (::getcwd(currentName, PATH_MAX)) + result = QDir::fromNativeSeparators(QFile::decodeName(QByteArray(currentName))); + if (result.isEmpty()) { +# if defined(QT_DEBUG) + qWarning("QDir::currentPath: getcwd() failed"); +# endif + } else +#endif if (QT_STAT(".", &st) == 0) { #if defined(__GLIBC__) && !defined(PATH_MAX) char *currentName = ::get_current_dir_name(); @@ -477,18 +546,26 @@ QString QFSFileEngine::currentPath(const QString &) result = QFile::decodeName(QByteArray(currentName)); ::free(currentName); } -#else +#elif !defined(Q_OS_SYMBIAN) char currentName[PATH_MAX+1]; if (::getcwd(currentName, PATH_MAX)) result = QFile::decodeName(QByteArray(currentName)); -#endif -#if defined(QT_DEBUG) +# if defined(QT_DEBUG) if (result.isNull()) qWarning("QDir::currentPath: getcwd() failed"); +# endif #endif } else { -#if defined(QT_DEBUG) +#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. + ::mkdir(QFile::encodeName(currentName), 0777); +#else +# if defined(QT_DEBUG) qWarning("QDir::currentPath: stat(\".\") failed"); +# endif #endif } return result; @@ -497,28 +574,61 @@ QString QFSFileEngine::currentPath(const QString &) QString QFSFileEngine::homePath() { QString home = QFile::decodeName(qgetenv("HOME")); +#if defined(Q_OS_SYMBIAN) + if (home.isEmpty()) + home = QLatin1String("C:/Data"); +#else if (home.isNull()) home = rootPath(); +#endif return home; } QString QFSFileEngine::rootPath() { +#if defined(Q_OS_SYMBIAN) + return QString::fromLatin1("C:/"); +#else return QString::fromLatin1("/"); +#endif } QString QFSFileEngine::tempPath() { - QString temp = QFile::decodeName(qgetenv("TMPDIR")); - if (temp.isEmpty()) - temp = QString::fromLatin1("/tmp/"); +#ifdef Q_OS_SYMBIAN + QString temp = QDir::currentPath().left(2); + temp += QString::fromLatin1( "/system/temp/"); +#else + QString temp = QFile::decodeName(qgetenv("TMPDIR")); + if (temp.isEmpty()) + temp = QString::fromLatin1("/tmp/"); +#endif return temp; } QFileInfoList QFSFileEngine::drives() { QFileInfoList ret; +#if defined(Q_OS_SYMBIAN) + TDriveList driveList; + RFs rfs; + TInt err = rfs.Connect(); + if (err == KErrNone) { + err = rfs.DriveList(driveList); + if (err == KErrNone) { + for(char i=0; i < KMaxDrives; i++) { + if (driveList[i]) { + ret.append(QString("%1:/").arg(QChar('A'+i))); + } + } + } else { + qWarning("QDir::drives: Getting drives failed"); + } + rfs.Close(); + } +#else ret.append(rootPath()); +#endif return ret; } @@ -552,6 +662,32 @@ bool QFSFileEnginePrivate::isSymlink() const return is_link; } +#if defined(Q_OS_SYMBIAN) +static bool _q_isSymbianHidden(const QString &path, bool isDir) +{ + bool retval = false; + RFs rfs; + TInt err = rfs.Connect(); + if (err == KErrNone) { + QFileInfo fi(path); + QString absPath = fi.absoluteFilePath(); + if (isDir && absPath.at(absPath.size()-1) != QChar('/')) { + absPath += QChar('/'); + } + QString native(QDir::toNativeSeparators(absPath)); + TPtrC ptr(qt_QString2TPtrC(native)); + TUint attributes; + err = rfs.Att(ptr, attributes); + rfs.Close(); + if (err == KErrNone && (attributes & KEntryAttHidden)) { + retval = true; + } + } + + return retval; +} +#endif + #if !defined(QWS) && defined(Q_OS_MAC) static bool _q_isMacHidden(const QString &path) { @@ -656,6 +792,16 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const ret |= LocalDiskFlag; if (exists) ret |= ExistsFlag; +#if defined(Q_OS_SYMBIAN) + if (d->filePath == QLatin1String("/") || (d->filePath.at(0).isLetter() && d->filePath.mid(1,d->filePath.length()) == QLatin1String(":/"))) + ret |= RootFlag; + + // 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 (!(ret & RootFlag) && !d->isSymlink()) + if(_q_isSymbianHidden(d->filePath, ret & DirectoryType) +#else if (d->filePath == QLatin1String("/")) { ret |= RootFlag; } else { @@ -665,6 +811,7 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const #if !defined(QWS) && defined(Q_OS_MAC) || _q_isMacHidden(d->filePath) #endif +#endif // Q_OS_SYMBIAN ) { ret |= HiddenFlag; } @@ -673,8 +820,122 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const return ret; } +#ifdef Q_OS_SYMBIAN +QString QFSFileEngine::fileNameSymbian(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); + } 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::fromLatin1("."); + } else { + if(!slash) + return QString::fromLatin1("/"); + 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 (!isRelativePath()) { + 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 = QString(QDir::currentPath().left(2) + QDir::fromNativeSeparators(d->filePath)); + } else { + ret = d->filePath; + } + } else { + ret = QDir::cleanPath(QDir::currentPath() + QLatin1Char('/') + 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) != 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(); + } + + if (file == AbsolutePathName) { + int slash = ret.lastIndexOf(QLatin1Char('/')); + if (slash < 0) + return ret; + else if (ret.at(0) != QLatin1Char('/') && 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 (!ret.isEmpty() && file == CanonicalPathName) { + int slash = ret.lastIndexOf(QLatin1Char('/')); + 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(QLatin1Char('/'))) + + QLatin1Char('/')); + } else { + ret.prepend(QDir::currentPath() + QLatin1Char('/')); + } + } + ret = QDir::cleanPath(ret); + if (ret.size() > 1 && ret.endsWith(QLatin1Char('/'))) + ret.chop(1); + return ret; + } + } + return QString(); + } else if(file == BundleName) { + return QString(); + } + return d->filePath; +} +#endif + QString QFSFileEngine::fileName(FileName file) const { +#ifdef Q_OS_SYMBIAN + return fileNameSymbian(file); +#endif Q_D(const QFSFileEngine); if (file == BundleName) { #if !defined(QWS) && defined(Q_OS_MAC) @@ -746,11 +1007,7 @@ QString QFSFileEngine::fileName(FileName file) const int size = PATH_CHUNK_SIZE; while (1) { - s = (char *) ::realloc(s, size); - if (s == 0) { - len = -1; - break; - } + s = q_check_ptr((char *) ::realloc(s, size)); len = ::readlink(d->nativeFilePath.constData(), s, size); if (len < 0) { ::free(s); @@ -818,10 +1075,14 @@ QString QFSFileEngine::fileName(FileName file) const bool QFSFileEngine::isRelativePath() const { Q_D(const QFSFileEngine); +#ifdef Q_OS_SYMBIAN + return isRelativePathSymbian(d->filePath); +#else int len = d->filePath.length(); if (len == 0) return true; return d->filePath[0] != QLatin1Char('/'); +#endif } uint QFSFileEngine::ownerId(FileOwner own) const @@ -857,6 +1118,9 @@ QString QFSFileEngine::owner(FileOwner own) const if (pw) return QFile::decodeName(QByteArray(pw->pw_name)); } else if (own == OwnerGroup) { +#ifdef Q_OS_SYMBIAN + return QString(); +#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); diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 3394a00..b7f4e48 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -1257,7 +1257,7 @@ bool QFSFileEnginePrivate::doStat() const static QString readLink(const QString &link) { #if !defined(Q_OS_WINCE) -#if !defined(QT_NO_LIBRARY) +#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS) QString ret; bool neededCoInit = false; @@ -1322,7 +1322,7 @@ QString QFSFileEnginePrivate::getLink() const bool QFSFileEngine::link(const QString &newName) { #if !defined(Q_OS_WINCE) -#if !defined(QT_NO_LIBRARY) +#if !defined(QT_NO_LIBRARY) && !defined(Q_CC_MWERKS) bool ret = false; QString linkName = newName; diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index 4108136..2df7a27 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -1036,6 +1036,15 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize) if (readSoFar) debugBinaryString(data, int(readSoFar)); #endif +#if defined(Q_OS_SYMBIAN) + // Open C fgets strips '\r' but readSoFar gets returned as if it was still there + if ((d->openMode & Text) && + readSoFar > 1 && + data[readSoFar - 1] == '\0' && + data[readSoFar - 2] == '\n') { + --readSoFar; + } +#endif if (readSoFar && data[readSoFar - 1] == '\n') { if (d->openMode & Text) { // QRingBuffer::readLine() isn't Text aware. @@ -1074,6 +1083,12 @@ qint64 QIODevice::readLine(char *data, qint64 maxSize) data[readSoFar] = '\0'; if (d->openMode & Text) { +#if defined(Q_OS_SYMBIAN) + // Open C fgets strips '\r' but readSoFar gets returned as if it was still there + if (readSoFar > 1 && data[readSoFar - 1] == '\0' && data[readSoFar - 2] == '\n') { + --readSoFar; + } +#endif if (readSoFar > 1 && data[readSoFar - 1] == '\n' && data[readSoFar - 2] == '\r') { data[readSoFar - 2] = '\n'; data[readSoFar - 1] = '\0'; diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h index fff56fd..5bc415b 100644 --- a/src/corelib/io/qiodevice.h +++ b/src/corelib/io/qiodevice.h @@ -46,6 +46,7 @@ #include <QtCore/qobject.h> #else #include <QtCore/qobjectdefs.h> +#include <QtCore/qscopedpointer.h> #endif #include <QtCore/qstring.h> @@ -160,7 +161,7 @@ protected: void setErrorString(const QString &errorString); #ifdef QT_NO_QOBJECT - QIODevicePrivate *d_ptr; + QScopedPointer<QIODevicePrivate> d_ptr; #endif private: diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 133d51e..6fc6710 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -176,7 +176,8 @@ void QProcessPrivate::Channel::clear() used as an input source for QXmlReader, or for generating data to be uploaded using QFtp. - \note On Windows CE, reading and writing to a process is not supported. + \note On Windows CE and Symbian, reading and writing to a process + is not supported. When the process exits, QProcess reenters the \l NotRunning state (the initial state), and emits finished(). @@ -226,6 +227,10 @@ void QProcessPrivate::Channel::clear() setWorkingDirectory(). By default, processes are run in the current working directory of the calling process. + \note On Symbian, setting environment or working directory + is not supported. The working directory will always be the private + directory of the running process. + \section1 Synchronous Process API QProcess provides a set of functions which allow it to be used @@ -460,6 +465,10 @@ QProcessPrivate::QProcessPrivate() #ifdef Q_OS_UNIX serial = 0; #endif +#ifdef Q_OS_SYMBIAN + symbianProcess = NULL; + processLaunched = false; +#endif } /*! \internal @@ -533,6 +542,13 @@ void QProcessPrivate::cleanup() #ifdef Q_OS_UNIX serial = 0; #endif +#ifdef Q_OS_SYMBIAN + if (symbianProcess) { + symbianProcess->Close(); + delete symbianProcess; + symbianProcess = NULL; + } +#endif } /*! \internal @@ -790,7 +806,7 @@ void QProcessPrivate::closeWriteChannel() if (stdinChannel.notifier) { qDeleteInEventHandler(stdinChannel.notifier); stdinChannel.notifier = 0; - } + } } #ifdef Q_OS_WIN // ### Find a better fix, feeding the process little by little @@ -1089,6 +1105,10 @@ QString QProcess::workingDirectory() const process in this directory. The default behavior is to start the process in the working directory of the calling process. + \note The working directory setting is ignored on Symbian; + the private directory of the process is considered its working + directory. + \sa workingDirectory(), start() */ void QProcess::setWorkingDirectory(const QString &dir) @@ -1229,7 +1249,7 @@ void QProcess::setEnvironment(const QStringList &environment) using setEnvironment() or setEnvironmentHash(). If no environment has been set, the environment of the calling process will be used. - \note The environment settings are ignored on Windows CE, + \note The environment settings are ignored on Windows CE and Symbian, as there is no concept of an environment. \sa environmentHash(), setEnvironment(), systemEnvironment() @@ -1715,6 +1735,9 @@ void QProcess::start(const QString &program, OpenMode mode) event loop does not handle the WM_CLOSE message, can only be terminated by calling kill(). + \note Terminating running processes from other processes will typically + cause a panic in Symbian due to platform security. + \sa kill() */ void QProcess::terminate() @@ -1729,6 +1752,9 @@ void QProcess::terminate() On Windows, kill() uses TerminateProcess, and on Unix and Mac OS X, the SIGKILL signal is sent to the process. + \note Killing running processes from other processes will typically + cause a panic in Symbian due to platform security. + \sa terminate() */ void QProcess::kill() @@ -1875,9 +1901,9 @@ QT_BEGIN_INCLUDE_NAMESPACE #ifdef Q_OS_MAC # include <crt_externs.h> # define environ (*_NSGetEnviron()) -#elif defined(Q_OS_WINCE) - static char *qt_wince_environ[] = { 0 }; -#define environ qt_wince_environ +#elif defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + static char *qt_empty_environ[] = { 0 }; +#define environ qt_empty_environ #elif !defined(Q_OS_WIN) extern char **environ; #endif diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 096f625..5faca5c 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -55,8 +55,13 @@ QT_MODULE(Core) template <class Key, class T> class QHash; -#if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)) || defined(qdoc) +#if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)) || defined(qdoc) typedef qint64 Q_PID; +#elif defined(Q_OS_SYMBIAN) +QT_END_NAMESPACE +# include <e32std.h> +QT_BEGIN_NAMESPACE +typedef TProcessId Q_PID; #else QT_END_NAMESPACE typedef struct _PROCESS_INFORMATION *Q_PID; diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 5482871..34797b9 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -180,7 +180,7 @@ public: QWinEventNotifier *processFinishedNotifier; void startProcess(); -#ifdef Q_OS_UNIX +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) void execChild(const char *workingDirectory, char **path, char **argv, char **envp); #endif bool processStarted(); @@ -221,6 +221,11 @@ public: #ifdef Q_OS_UNIX static void initializeProcessManager(); #endif + +#ifdef Q_OS_SYMBIAN + bool processLaunched; + RProcess* symbianProcess; +#endif }; QT_END_NAMESPACE diff --git a/src/corelib/io/qprocess_symbian.cpp b/src/corelib/io/qprocess_symbian.cpp new file mode 100644 index 0000000..64519f6 --- /dev/null +++ b/src/corelib/io/qprocess_symbian.cpp @@ -0,0 +1,1035 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QPROCESS_DEBUG + +#ifdef QPROCESS_DEBUG +#include "qdebug.h" +#define QPROCESS_DEBUG_PRINT(args...) qDebug(args); +#else +#define QPROCESS_DEBUG_PRINT(args...) +#endif + +#ifndef QT_NO_PROCESS + +#define QPROCESS_ASSERT(check, panicReason, args...) \ + if (!(check)) { \ + qWarning(args); \ + User::Panic(KQProcessPanic, panicReason); \ + } + +#include <exception> +#include <e32base.h> +#include <e32std.h> +#include <stdio.h> +#include "qplatformdefs.h" + +#include "qstring.h" +#include "qprocess.h" +#include "qprocess_p.h" +#include "qeventdispatcher_symbian_p.h" + +#include <private/qthread_p.h> +#include <qmutex.h> +#include <qmap.h> +#include <qsocketnotifier.h> + +#include <errno.h> + + +QT_BEGIN_NAMESPACE + +_LIT(KQProcessManagerThreadName, "QProcManThread"); +_LIT(KQProcessPanic, "QPROCESS"); +enum TQProcessPanic { + EProcessManagerMediatorRunError = 1, + EProcessManagerMediatorInactive = 2, + EProcessManagerMediatorNotPending = 3, + EProcessManagerMediatorInvalidCmd = 4, + EProcessManagerMediatorCreationFailed = 5, + EProcessManagerMediatorThreadOpenFailed = 6, + EProcessManagerMediatorNullObserver = 7, + EProcessActiveRunError = 10, + EProcessActiveNullParameter = 11, + EProcessManagerMutexCreationFail = 20, + EProcessManagerThreadCreationFail = 21, + EProcessManagerSchedulerCreationFail = 22, + EProcessManagerNullParam = 23 +}; + +// Forward declarations +class QProcessManager; + + +// Active object to listen for child process death +class CProcessActive : public CActive +{ +public: + static CProcessActive* construct(QProcess* process, + RProcess** proc, + int serial, + int deathPipe); + + virtual ~CProcessActive(); + + void start(); + void stop(); + + bool error(); + +protected: + + // From CActive + void RunL(); + TInt RunError(TInt aError); + void DoCancel(); + + CProcessActive(); + +private: + + QProcess* process; + RProcess** pproc; + int serial; + int deathPipe; + bool errorValue; +}; + +// Active object to communicate synchronously with process manager thread +class CProcessManagerMediator : public CActive +{ +public: + static CProcessManagerMediator* construct(); + + virtual ~CProcessManagerMediator(); + + bool add(CProcessActive* processObserver); + void remove(CProcessActive* processObserver); + void terminate(); + +protected: + + enum Commands { + ENoCommand, + EAdd, + ERemove, + ETerminate + }; + + // From CActive + void RunL(); + TInt RunError(TInt aError); + void DoCancel(); + + CProcessManagerMediator(); + + bool notify(CProcessActive* processObserver, Commands command); + +private: + CProcessActive* currentObserver; + Commands currentCommand; + + RThread processManagerThread; +}; + +// Process manager manages child process death listeners +class QProcessManager +{ +public: + QProcessManager(); + ~QProcessManager(); + + void startThread(); + + TInt run(void* param); + bool add(QProcess *process); + void remove(QProcess *process); + + inline void setMediator(CProcessManagerMediator* newMediator) {mediator = newMediator;}; + +private: + inline void lock() {managerMutex.Wait();}; + inline void unlock() {managerMutex.Signal();}; + + QMap<int, CProcessActive *> children; + CProcessManagerMediator* mediator; + RMutex managerMutex; + bool threadStarted; + RThread managerThread; +}; + +static bool qt_rprocess_running(RProcess* proc) +{ + if(proc && proc->Handle()) { + TExitType et = proc->ExitType(); + if (et == EExitPending) { + return true; + } + } + + return false; +} + +static void qt_create_symbian_commandline(const QStringList &arguments, QString& commandLine) +{ + for (int i=0; i<arguments.size(); ++i) { + QString tmp = arguments.at(i); + // in the case of \" already being in the string the \ must also be escaped + tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") ); + // escape a single " because the arguments will be parsed + tmp.replace( QLatin1String("\""), QLatin1String("\\\"") ); + if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + QString endQuote(QLatin1String("\"")); + int i = tmp.length(); + while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) { + --i; + endQuote += QLatin1String("\\"); + } + commandLine += QLatin1String(" \"") + tmp.left(i) + endQuote; + } else { + commandLine += QLatin1Char(' ') + tmp; + } + } +} + +static TInt qt_create_symbian_process(RProcess **proc, const QString &programName, const QStringList &arguments) +{ + RProcess* newProc = NULL; + newProc = new RProcess(); + + if (!newProc) { + return KErrNoMemory; + } + + QString commandLine; + qt_create_symbian_commandline(arguments, commandLine); + + TPtrC program_ptr(reinterpret_cast<const TText*>(programName.constData())); + TPtrC cmdline_ptr(reinterpret_cast<const TText*>(commandLine.constData())); + + TInt err = newProc->Create(program_ptr, cmdline_ptr); + + if (err == KErrNotFound){ + // Strip path from program name and try again (i.e. try from default location "\sys\bin") + int index = programName.lastIndexOf(QChar('\\')); + int index2 = programName.lastIndexOf(QChar('/')); + index = qMax(index, index2); + + if(index != -1 && programName.length() >= index){ + QString strippedName; + strippedName = programName.mid(index+1); + QPROCESS_DEBUG_PRINT("qt_create_symbian_process() Process '%s' not found, try stripped version '%s'", qPrintable(programName), qPrintable(strippedName)); + + TPtrC stripped_ptr(reinterpret_cast<const TText*>(strippedName.constData())); + err = newProc->Create(stripped_ptr, cmdline_ptr); + + if (err != KErrNone) { + QPROCESS_DEBUG_PRINT("qt_create_symbian_process() Unable to create process '%s': %d", qPrintable(strippedName), err); + } + } + } + + if (err == KErrNone) { + *proc = newProc; + } else { + delete newProc; + } + + return err; +} + +static qint64 qt_native_read(int fd, char *data, qint64 maxlen) +{ + qint64 ret = 0; + do { + ret = ::read(fd, data, maxlen); + } while (ret == -1 && errno == EINTR); + + QPROCESS_DEBUG_PRINT("qt_native_read(): fd: %d, result: %d, errno = %d", fd, (int)ret, errno); + + return ret; +} + +static qint64 qt_native_write(int fd, const char *data, qint64 len) +{ + qint64 ret = 0; + do { + ret = ::write(fd, data, len); + } while (ret == -1 && errno == EINTR); + + QPROCESS_DEBUG_PRINT("qt_native_write(): fd: %d, result: %d, errno = %d", fd, (int)ret, errno); + + return ret; +} + +static void qt_native_close(int fd) +{ + int ret; + do { + ret = ::close(fd); + } while (ret == -1 && errno == EINTR); +} + +static void qt_create_pipe(int *pipe) +{ + if (pipe[0] != -1) + qt_native_close(pipe[0]); + if (pipe[1] != -1) + qt_native_close(pipe[1]); + if (::pipe(pipe) != 0) { + qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", + pipe, qPrintable(qt_error_string(errno))); + } else { + QPROCESS_DEBUG_PRINT("qt_create_pipe(): Created pipe %d - %d", pipe[0], pipe[1]); + } +} + +// Called from ProcessManagerThread +CProcessActive* CProcessActive::construct(QProcess* process, + RProcess** proc, + int serial, + int deathPipe) +{ + QPROCESS_ASSERT((process || proc || *proc), + EProcessActiveNullParameter, + "CProcessActive::construct(): process (0x%x), proc (0x%x) or *proc == NULL, not creating an instance", process, proc) + + CProcessActive* newInstance = new CProcessActive(); + + if (!newInstance) { + QPROCESS_DEBUG_PRINT("CProcessActive::construct(): Failed to create new instance"); + } else { + newInstance->process = process; + newInstance->pproc = proc; + newInstance->serial = serial; + newInstance->deathPipe = deathPipe; + newInstance->errorValue = false; + } + + return newInstance; +} + +// Called from ProcessManagerThread +CProcessActive::CProcessActive() + : CActive(CActive::EPriorityStandard) +{ + // Nothing to do +} + +// Called from ProcessManagerThread +CProcessActive::~CProcessActive() +{ + process = NULL; + pproc = NULL; +} + +// Called from ProcessManagerThread +void CProcessActive::start() +{ + if (qt_rprocess_running(*pproc)) { + CActiveScheduler::Add(this); + (*pproc)->Logon(iStatus); + SetActive(); + QPROCESS_DEBUG_PRINT("CProcessActive::start(): Started monitoring for process exit."); + } else { + QPROCESS_DEBUG_PRINT("CProcessActive::start(): Process doesn't exist or is already dead"); + // Assume process has already died + qt_native_write(deathPipe, "", 1); + errorValue = true; + } +} + +// Called from ProcessManagerThread +void CProcessActive::stop() +{ + QPROCESS_DEBUG_PRINT("CProcessActive::stop()"); + + // Remove this from scheduler (also cancels the request) + Deque(); +} + +bool CProcessActive::error() +{ + return errorValue; +} + +// Called from ProcessManagerThread +void CProcessActive::RunL() +{ + // If this method gets executed, the monitored process has died + + // Notify main thread + qt_native_write(deathPipe, "", 1); + QPROCESS_DEBUG_PRINT("CProcessActive::RunL() sending death notice to %d", deathPipe); +} + +// Called from ProcessManagerThread +TInt CProcessActive::RunError(TInt aError) +{ + Q_UNUSED(aError); + // Handle RunL leave (should never happen) + QPROCESS_ASSERT(0, EProcessActiveRunError, "CProcessActive::RunError(): Should never get here!") + return 0; +} + +// Called from ProcessManagerThread +void CProcessActive::DoCancel() +{ + QPROCESS_DEBUG_PRINT("CProcessActive::DoCancel()"); + + if (qt_rprocess_running(*pproc)) { + (*pproc)->LogonCancel(iStatus); + QPROCESS_DEBUG_PRINT("CProcessActive::DoCancel(): Stopped monitoring for process exit."); + } else { + QPROCESS_DEBUG_PRINT("CProcessActive::DoCancel(): Process doesn't exist"); + } +} + + +// Called from ProcessManagerThread +CProcessManagerMediator* CProcessManagerMediator::construct() +{ + CProcessManagerMediator* newInstance = new CProcessManagerMediator; + TInt err(KErrNone); + + newInstance->currentCommand = ENoCommand; + newInstance->currentObserver = NULL; + + if (newInstance) { + err = newInstance->processManagerThread.Open(newInstance->processManagerThread.Id()); + QPROCESS_ASSERT((err == KErrNone), + EProcessManagerMediatorThreadOpenFailed, + "CProcessManagerMediator::construct(): Failed to open processManagerThread (err:%d)", err) + } else { + QPROCESS_ASSERT(0, + EProcessManagerMediatorCreationFailed, + "CProcessManagerMediator::construct(): Failed to open construct mediator") + } + + // Activate mediator + CActiveScheduler::Add(newInstance); + newInstance->iStatus = KRequestPending; + newInstance->SetActive(); + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::construct(): new instance successfully created and activated"); + + return newInstance; +} + +// Called from ProcessManagerThread +CProcessManagerMediator::CProcessManagerMediator() + : CActive(CActive::EPriorityStandard) +{ + // Nothing to do +} + +// Called from ProcessManagerThread +CProcessManagerMediator::~CProcessManagerMediator() +{ + processManagerThread.Close(); + currentCommand = ENoCommand; + currentObserver = NULL; +} + +// Called from main thread +bool CProcessManagerMediator::add(CProcessActive* processObserver) +{ + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::add()"); + return notify(processObserver, EAdd); +} + +// Called from main thread +void CProcessManagerMediator::remove(CProcessActive* processObserver) +{ + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::remove()"); + notify(processObserver, ERemove); +} + +// Called from main thread +void CProcessManagerMediator::terminate() +{ + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::terminate()"); + notify(NULL, ETerminate); +} + +// Called from main thread +bool CProcessManagerMediator::notify(CProcessActive* processObserver, Commands command) +{ + bool success(true); + + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::Notify(): Command: %d, processObserver: 0x%x", command, processObserver); + + QPROCESS_ASSERT((command == ETerminate || processObserver), + EProcessManagerMediatorNullObserver, + "CProcessManagerMediator::Notify(): NULL processObserver not allowed for command: %d", command) + + QPROCESS_ASSERT(IsActive(), + EProcessManagerMediatorInactive, + "CProcessManagerMediator::Notify(): Mediator is not active!") + + QPROCESS_ASSERT(iStatus==KRequestPending, + EProcessManagerMediatorNotPending, + "CProcessManagerMediator::Notify(): Mediator request not pending!") + + currentObserver = processObserver; + currentCommand = command; + + // Sync with process manager thread + TRequestStatus pmStatus; + processManagerThread.Rendezvous(pmStatus); + + // Complete request -> RunL will run in the process manager thread + TRequestStatus* status = &iStatus; + processManagerThread.RequestComplete(status, command); + + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::Notify(): Waiting process manager to complete..."); + User::WaitForRequest(pmStatus); + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::Notify(): Wait over"); + + if (currentObserver) { + success = !(currentObserver->error()); + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::Notify(): success = %d", success); + } + + currentObserver = NULL; + currentCommand = ENoCommand; + + return success; +} + +// Called from ProcessManagerThread +void CProcessManagerMediator::RunL() +{ + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::RunL(): currentCommand: %d, iStatus: %d", currentCommand, iStatus.Int()); + switch (currentCommand) { + case EAdd: + currentObserver->start(); + break; + case ERemove: + currentObserver->stop(); + break; + case ETerminate: + Deque(); + CActiveScheduler::Stop(); + return; + default: + QPROCESS_ASSERT(0, + EProcessManagerMediatorInvalidCmd, + "CProcessManagerMediator::RunL(): Invalid command!") + break; + } + + iStatus = KRequestPending; + SetActive(); + + // Notify main thread that we are done + RThread::Rendezvous(KErrNone); +} + +// Called from ProcessManagerThread +TInt CProcessManagerMediator::RunError(TInt aError) +{ + Q_UNUSED(aError); + // Handle RunL leave (should never happen) + QPROCESS_ASSERT(0, + EProcessManagerMediatorRunError, + "CProcessManagerMediator::RunError(): Should never get here!") + return 0; +} + +// Called from ProcessManagerThread +void CProcessManagerMediator::DoCancel() +{ + QPROCESS_DEBUG_PRINT("CProcessManagerMediator::DoCancel()"); + TRequestStatus* status = &iStatus; + processManagerThread.RequestComplete(status, KErrCancel); +} + +Q_GLOBAL_STATIC(QProcessManager, processManager) + +TInt processManagerThreadFunction(TAny* param) +{ + QPROCESS_ASSERT(param, + EProcessManagerNullParam, + "processManagerThreadFunction(): NULL param") + + QProcessManager* manager = reinterpret_cast<QProcessManager*>(param); + + CActiveScheduler* scheduler = new CQtActiveScheduler(); + + QPROCESS_ASSERT(scheduler, + EProcessManagerSchedulerCreationFail, + "processManagerThreadFunction(): Scheduler creation failed") + + CActiveScheduler::Install(scheduler); + + //Creating mediator also adds it to scheduler and activates it. Failure will panic. + manager->setMediator(CProcessManagerMediator::construct()); + RThread::Rendezvous(KErrNone); + + CActiveScheduler::Start(); + + CActiveScheduler::Install(NULL); + delete scheduler; + + return KErrNone; +} + +QProcessManager::QProcessManager() + : mediator(NULL), threadStarted(false) +{ + TInt err = managerMutex.CreateLocal(); + + QPROCESS_ASSERT(err == KErrNone, + EProcessManagerMutexCreationFail, + "QProcessManager::QProcessManager(): Failed to create new managerMutex (err: %d)", err) +} + +QProcessManager::~QProcessManager() +{ + QPROCESS_DEBUG_PRINT("QProcessManager::~QProcessManager()"); + // Cancel death listening for all child processes + if (mediator) { + QMap<int, CProcessActive *>::Iterator it = children.begin(); + while (it != children.end()) { + // Remove all monitors + CProcessActive *active = it.value(); + mediator->remove(active); + + QPROCESS_DEBUG_PRINT("QProcessManager::~QProcessManager() removed listening for a process"); + ++it; + } + + // Terminate process manager thread. + mediator->terminate(); + delete mediator; + } + + qDeleteAll(children.values()); + children.clear(); + managerThread.Close(); + managerMutex.Close(); +} + +void QProcessManager::startThread() +{ + lock(); + + if (!threadStarted) { + TInt err = managerThread.Create(KQProcessManagerThreadName, + processManagerThreadFunction, + 0x5000, + (RAllocator*)NULL, + (TAny*)this, + EOwnerProcess); + + QPROCESS_ASSERT(err == KErrNone, + EProcessManagerThreadCreationFail, + "QProcessManager::startThread(): Failed to create new managerThread (err:%d)", err) + + threadStarted = true; + + // Manager thread must start running before we continue, so sync with rendezvous + TRequestStatus status; + managerThread.Rendezvous(status); + managerThread.Resume(); + User::WaitForRequest(status); + } + + unlock(); +} + +static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(1); + +bool QProcessManager::add(QProcess *process) +{ + QPROCESS_ASSERT(process, + EProcessManagerNullParam, + "QProcessManager::add(): Failed to add CProcessActive to ProcessManager - NULL process") + + lock(); + + int serial = idCounter.fetchAndAddRelaxed(1); + process->d_func()->serial = serial; + + QPROCESS_DEBUG_PRINT("QProcessManager::add(): serial: %d, deathPipe: %d - %d, symbianProcess: 0x%x", serial, process->d_func()->deathPipe[0], process->d_func()->deathPipe[1], process->d_func()->symbianProcess); + + CProcessActive* newActive = + CProcessActive::construct(process, + &(process->d_func()->symbianProcess), + serial, + process->d_func()->deathPipe[1]); + + if (newActive){ + if (mediator->add(newActive)) { + children.insert(serial, newActive); + unlock(); + return true; + } else { + QPROCESS_DEBUG_PRINT("QProcessManager::add(): Failed to add CProcessActive to ProcessManager"); + delete newActive; + } + } + + unlock(); + + return false; +} + +void QProcessManager::remove(QProcess *process) +{ + QPROCESS_ASSERT(process, + EProcessManagerNullParam, + "QProcessManager::remove(): Failed to remove CProcessActive from ProcessManager - NULL process") + + lock(); + + int serial = process->d_func()->serial; + CProcessActive *active = children.value(serial); + if (!active) { + unlock(); + return; + } + + mediator->remove(active); + + children.remove(serial); + delete active; + + unlock(); +} + +void QProcessPrivate::destroyPipe(int *pipe) +{ + if (pipe[1] != -1) { + qt_native_close(pipe[1]); + pipe[1] = -1; + } + if (pipe[0] != -1) { + qt_native_close(pipe[0]); + pipe[0] = -1; + } +} + +bool QProcessPrivate::createChannel(Channel &channel) +{ + Q_UNUSED(channel); + // No channels used + return false; +} + +void QProcessPrivate::startProcess() +{ + Q_Q(QProcess); + + QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess()"); + + // Start the process (platform dependent) + q->setProcessState(QProcess::Starting); + + processManager()->startThread(); + + qt_create_pipe(deathPipe); + if (threadData->eventDispatcher) { + deathNotifier = new QSocketNotifier(deathPipe[0], + QSocketNotifier::Read, q); + QObject::connect(deathNotifier, SIGNAL(activated(int)), + q, SLOT(_q_processDied())); + } + + TInt err = qt_create_symbian_process(&symbianProcess, program, arguments); + + if (err == KErrNone) { + pid = symbianProcess->Id(); + + ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK); + + if (!processManager()->add(q)) { + qWarning("QProcessPrivate::startProcess(): Failed to start monitoring for process death."); + err = KErrNoMemory; + } + } + + if (err != KErrNone) { + // Cleanup, report error and return + QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess() Process open failed, err: %d, '%s'", err, qPrintable(program)); + q->setProcessState(QProcess::NotRunning); + processError = QProcess::FailedToStart; + q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Resource error (qt_create_symbian_process failure)"))); + emit q->error(processError); + cleanup(); + return; + } + + processLaunched = true; + + symbianProcess->Resume(); + + QPROCESS_DEBUG_PRINT("QProcessPrivate::startProcess(): this: 0x%x, pid: %d", this, (TUint)pid); + + // Notify child start + _q_startupNotification(); + +} + +bool QProcessPrivate::processStarted() +{ + QPROCESS_DEBUG_PRINT("QProcessPrivate::processStarted() == %s", processLaunched ? "true" : "false"); + + // Since we cannot get information whether process has actually been launched + // or not in Symbian, we need to fake it. Assume process is started if launch was + // successful. + + return processLaunched; +} + +qint64 QProcessPrivate::bytesAvailableFromStdout() const +{ + // In Symbian, zero bytes are always available + return 0; +} + +qint64 QProcessPrivate::bytesAvailableFromStderr() const +{ + // In Symbian, zero bytes are always available + return 0; +} + +qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + // In Symbian, zero bytes are always read + return 0; +} + +qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + // In Symbian, zero bytes are always read + return 0; +} + +qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen) +{ + Q_UNUSED(data); + Q_UNUSED(maxlen); + // In Symbian, zero bytes are always written + return 0; +} + +void QProcessPrivate::terminateProcess() +{ + // Not allowed by platform security - will panic kern-exec 46 if process has been started. + // Works if process is not yet started. + if (qt_rprocess_running(symbianProcess)) { + symbianProcess->Terminate(0); + } else { + QPROCESS_DEBUG_PRINT("QProcessPrivate::terminateProcess(), Process not running"); + } +} + +void QProcessPrivate::killProcess() +{ + // Not allowed by platform security - will panic kern-exec 46 if process has been started. + // Works if process is not yet started. + if (qt_rprocess_running(symbianProcess)) { + symbianProcess->Kill(0); + } else { + QPROCESS_DEBUG_PRINT("QProcessPrivate::killProcess(), Process not running"); + } +} + +bool QProcessPrivate::waitForStarted(int msecs) +{ + Q_UNUSED(msecs); + // Since we can get no actual feedback from process beyond its death, + // assume that started has already been emitted if process has been launched + return processLaunched; +} + +bool QProcessPrivate::waitForReadyRead(int msecs) +{ + // Functionality not supported in Symbian + Q_UNUSED(msecs); + return false; +} + +bool QProcessPrivate::waitForBytesWritten(int msecs) +{ + // Functionality not supported in Symbian + Q_UNUSED(msecs); + return false; +} + +bool QProcessPrivate::waitForFinished(int msecs) +{ + Q_Q(QProcess); + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished(%d)", msecs); + + TRequestStatus timerStatus = 0; + TRequestStatus logonStatus = 0; + bool timeoutOccurred = false; + + // Logon to process to observe its death + if (qt_rprocess_running(symbianProcess)) { + symbianProcess->Logon(logonStatus); + + // Create timer + RTimer timer; + timer.CreateLocal(); + TTimeIntervalMicroSeconds32 interval(msecs*1000); + timer.After(timerStatus, interval); + + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Waiting..."); + User::WaitForRequest(logonStatus, timerStatus); + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Wait completed"); + + if (timerStatus == KErrNone) { + timeoutOccurred = true; + } + + timer.Cancel(); + timer.Close(); + + symbianProcess->LogonCancel(logonStatus); + + // Eat cancel request completion so that it won't mess up main thread scheduling later + User::WaitForRequest(logonStatus, timerStatus); + } else { + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished(), qt_rprocess_running returned false"); + } + + if (timeoutOccurred) { + processError = QProcess::Timedout; + q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Process operation timed out"))); + return false; + } + + _q_processDied(); + + return true; +} + +bool QProcessPrivate::waitForWrite(int msecs) +{ + // Functionality not supported in Symbian + Q_UNUSED(msecs); + return false; +} + +// Deceptively named function. Exit code is actually got in waitForDeadChild(). +void QProcessPrivate::findExitCode() +{ + Q_Q(QProcess); + processManager()->remove(q); +} + +bool QProcessPrivate::waitForDeadChild() +{ + Q_Q(QProcess); + + // read a byte from the death pipe + char c; + qt_native_read(deathPipe[0], &c, 1); + + if (symbianProcess && symbianProcess->Handle()) { + TExitType et = symbianProcess->ExitType(); + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForDeadChild() symbianProcess->ExitType: %d", et); + if (et != EExitPending) { + processManager()->remove(q); + exitCode = symbianProcess->ExitReason(); + crashed = (et == EExitPanic); +#if defined QPROCESS_DEBUG + TExitCategoryName catName = symbianProcess->ExitCategory(); + qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode" + << exitCode << ", crashed:" << crashed + << ", category:" << QString::fromUtf16(catName.Ptr()); +#endif + } else { + QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForDeadChild() not dead!"); + } + } + + return true; +} + +void QProcessPrivate::_q_notified() +{ + // Nothing to do in Symbian +} + +/*! \internal + */ +bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) +{ + QPROCESS_DEBUG_PRINT("QProcessPrivate::startDetached()"); + Q_UNUSED(workingDirectory); + + RProcess* newProc = NULL; + + TInt err = qt_create_symbian_process(&newProc, program, arguments); + + if (err == KErrNone) { + if (pid) { + *pid = (qint64)newProc->Id(); + } + + newProc->Resume(); + newProc->Close(); + return true; + } + + return false; +} + + +void QProcessPrivate::initializeProcessManager() +{ + (void) processManager(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_PROCESS diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 958c7fc..34c21d7 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -318,9 +318,8 @@ QResourcePrivate::ensureInitialized() const if(path.startsWith(QLatin1Char(':'))) path = path.mid(1); - bool found = false; if(path.startsWith(QLatin1Char('/'))) { - found = that->load(path); + that->load(path); } else { QMutexLocker lock(resourceMutex()); QStringList searchPaths = *resourceSearchPaths(); @@ -328,7 +327,6 @@ QResourcePrivate::ensureInitialized() const for(int i = 0; i < searchPaths.size(); ++i) { const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path); if(that->load(searchPath)) { - found = true; that->absoluteFilePath = QLatin1Char(':') + searchPath; break; } @@ -390,7 +388,6 @@ QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QRe */ QResource::~QResource() { - delete d_ptr; } /*! diff --git a/src/corelib/io/qresource.h b/src/corelib/io/qresource.h index a162391..dc2df92 100644 --- a/src/corelib/io/qresource.h +++ b/src/corelib/io/qresource.h @@ -91,7 +91,7 @@ protected: QStringList children() const; protected: - QResourcePrivate *d_ptr; + QScopedPointer<QResourcePrivate> d_ptr; private: Q_DECLARE_PRIVATE(QResource) diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 81bd25a..d7cf561 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -221,6 +221,11 @@ QConfFile::QConfFile(const QString &fileName, bool _userPerms) usedHashFunc()->insert(name, this); } +QConfFile::~QConfFile() +{ + usedHashFunc()->remove(name); +} + ParsedSettingsMap QConfFile::mergedKeyMap() const { ParsedSettingsMap result = originalKeys; @@ -267,7 +272,7 @@ QConfFile *QConfFile::fromName(const QString &fileName, bool _userPerms) ConfFileHash *usedHash = usedHashFunc(); ConfFileCache *unusedCache = unusedCacheFunc(); - QConfFile *confFile; + QConfFile *confFile = 0; QMutexLocker locker(globalMutex()); if (!(confFile = usedHash->value(absPath))) { @@ -1093,10 +1098,10 @@ static QString getPath(QSettings::Format format, QSettings::Scope scope) QString homePath = QDir::homePath(); QString systemPath; - globalMutex()->lock(); + QMutexLocker locker(globalMutex()); PathHash *pathHash = pathHashFunc(); bool loadSystemPath = pathHash->isEmpty(); - globalMutex()->unlock(); + locker.unlock(); if (loadSystemPath) { /* @@ -1108,7 +1113,7 @@ static QString getPath(QSettings::Format format, QSettings::Scope scope) systemPath += QLatin1Char('/'); } - QMutexLocker locker(globalMutex()); + locker.relock(); if (pathHash->isEmpty()) { /* Lazy initialization of pathHash. We initialize the @@ -1168,9 +1173,6 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, int i; initFormat(); - for (i = 0; i < NumConfFiles; ++i) - confFiles[i] = 0; - QString org = organization; if (org.isEmpty()) { setStatus(QSettings::AccessError); @@ -1183,14 +1185,14 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, if (scope == QSettings::UserScope) { QString userPath = getPath(format, QSettings::UserScope); if (!application.isEmpty()) - confFiles[F_User | F_Application] = QConfFile::fromName(userPath + appFile, true); - confFiles[F_User | F_Organization] = QConfFile::fromName(userPath + orgFile, true); + confFiles[F_User | F_Application].reset(QConfFile::fromName(userPath + appFile, true)); + confFiles[F_User | F_Organization].reset(QConfFile::fromName(userPath + orgFile, true)); } QString systemPath = getPath(format, QSettings::SystemScope); if (!application.isEmpty()) - confFiles[F_System | F_Application] = QConfFile::fromName(systemPath + appFile, false); - confFiles[F_System | F_Organization] = QConfFile::fromName(systemPath + orgFile, false); + confFiles[F_System | F_Application].reset(QConfFile::fromName(systemPath + appFile, false)); + confFiles[F_System | F_Organization].reset(QConfFile::fromName(systemPath + orgFile, false)); for (i = 0; i < NumConfFiles; ++i) { if (confFiles[i]) { @@ -1209,9 +1211,7 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, { initFormat(); - confFiles[0] = QConfFile::fromName(fileName, true); - for (int i = 1; i < NumConfFiles; ++i) - confFiles[i] = 0; + confFiles[0].reset(QConfFile::fromName(fileName, true)); initAccess(); } @@ -1224,23 +1224,30 @@ QConfFileSettingsPrivate::~QConfFileSettingsPrivate() for (int i = 0; i < NumConfFiles; ++i) { if (confFiles[i] && !confFiles[i]->ref.deref()) { - if (usedHash) - usedHash->remove(confFiles[i]->name); - if (confFiles[i]->size == 0) { - delete confFiles[i]; + delete confFiles[i].take(); } else if (unusedCache) { - // compute a better size? - unusedCache->insert(confFiles[i]->name, confFiles[i], + if (usedHash) + usedHash->remove(confFiles[i]->name); + QT_TRY { + // compute a better size? + unusedCache->insert(confFiles[i]->name, confFiles[i].data(), 10 + (confFiles[i]->originalKeys.size() / 4)); + confFiles[i].take(); + } QT_CATCH(...) { + // out of memory. Do not cache the file. + delete confFiles[i].take(); + } } } + // prevent the ScopedPointer to deref it again. + confFiles[i].take(); } } void QConfFileSettingsPrivate::remove(const QString &key) { - QConfFile *confFile = confFiles[spec]; + QConfFile *confFile = confFiles[spec].data(); if (!confFile) return; @@ -1267,7 +1274,7 @@ void QConfFileSettingsPrivate::remove(const QString &key) void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value) { - QConfFile *confFile = confFiles[spec]; + QConfFile *confFile = confFiles[spec].data(); if (!confFile) return; @@ -1284,7 +1291,7 @@ bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const bool found = false; for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i]) { + if (QConfFile *confFile = confFiles[i].data()) { QMutexLocker locker(&confFile->mutex); if (!confFile->addedKeys.isEmpty()) { @@ -1319,7 +1326,7 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec int startPos = prefix.size(); for (int i = 0; i < NumConfFiles; ++i) { - if (QConfFile *confFile = confFiles[i]) { + if (QConfFile *confFile = confFiles[i].data()) { QMutexLocker locker(&confFile->mutex); if (thePrefix.isEmpty()) { @@ -1352,7 +1359,7 @@ QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec void QConfFileSettingsPrivate::clear() { - QConfFile *confFile = confFiles[spec]; + QConfFile *confFile = confFiles[spec].data(); if (!confFile) return; @@ -1368,7 +1375,7 @@ void QConfFileSettingsPrivate::sync() // error we just try to go on and make the best of it for (int i = 0; i < NumConfFiles; ++i) { - QConfFile *confFile = confFiles[i]; + QConfFile *confFile = confFiles[i].data(); if (confFile) { QMutexLocker locker(&confFile->mutex); syncConfFile(i); @@ -1383,7 +1390,7 @@ void QConfFileSettingsPrivate::flush() QString QConfFileSettingsPrivate::fileName() const { - QConfFile *confFile = confFiles[spec]; + QConfFile *confFile = confFiles[spec].data(); if (!confFile) return QString(); return confFile->name; @@ -1394,7 +1401,7 @@ bool QConfFileSettingsPrivate::isWritable() const if (format > QSettings::IniFormat && !writeFunc) return false; - QConfFile *confFile = confFiles[spec]; + QConfFile *confFile = confFiles[spec].data(); if (!confFile) return false; @@ -1403,7 +1410,7 @@ bool QConfFileSettingsPrivate::isWritable() const void QConfFileSettingsPrivate::syncConfFile(int confFileNo) { - QConfFile *confFile = confFiles[confFileNo]; + QConfFile *confFile = confFiles[confFileNo].data(); bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty(); bool ok; @@ -2738,11 +2745,13 @@ QSettings::QSettings(const QString &fileName, Format format) QSettings::~QSettings() { Q_D(QSettings); - if (d->pendingChanges) - d->flush(); -#ifdef QT_NO_QOBJECT - delete d; -#endif + if (d->pendingChanges) { + QT_TRY { + d->flush(); + } QT_CATCH(...) { + ; // ok. then don't flush but at least don't throw in the destructor + } + } } /*! @@ -3545,8 +3554,7 @@ void QSettings::setPath_helper(Scope scope, const QString &organization, const Q QSettingsPrivate *oldPriv = d; QSettingsPrivate *newPriv = QSettingsPrivate::create(oldPriv->format, scope, organization, application); static_cast<QObjectPrivate &>(*newPriv) = static_cast<QObjectPrivate &>(*oldPriv); // copy the QObject stuff over (hack) - delete oldPriv; - d_ptr = newPriv; + d_ptr.reset(newPriv); } /*! \fn bool QSettings::writeEntry(const QString &key, bool value) diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h index 23ff273..a1ce361 100644 --- a/src/corelib/io/qsettings.h +++ b/src/corelib/io/qsettings.h @@ -78,7 +78,7 @@ class Q_CORE_EXPORT QSettings #ifndef QT_NO_QOBJECT Q_OBJECT #else - QSettingsPrivate *d_ptr; + QScopedPointer<QSettingsPrivate> d_ptr; #endif Q_DECLARE_PRIVATE(QSettings) diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index 90ba8d5..8811544 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -149,6 +149,8 @@ inline QString QSettingsGroup::toString() const class Q_AUTOTEST_EXPORT QConfFile { public: + ~QConfFile(); + ParsedSettingsMap mergedKeyMap() const; bool isWritable() const; @@ -300,7 +302,7 @@ private: void ensureAllSectionsParsed(QConfFile *confFile) const; void ensureSectionParsed(QConfFile *confFile, const QSettingsKey &key) const; - QConfFile *confFiles[NumConfFiles]; + QScopedSharedPointer<QConfFile> confFiles[NumConfFiles]; QSettings::ReadFunc readFunc; QSettings::WriteFunc writeFunc; QString extension; diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 2fbf93f..13a0e4c 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -514,6 +514,10 @@ QTemporaryFile::QTemporaryFile() { Q_D(QTemporaryFile); d->templateName = QDir::tempPath() + QLatin1String("/qt_temp.XXXXXX"); +#ifdef Q_OS_SYMBIAN + //Just for verify that folder really exist on hardware + fileEngine()->mkdir( QDir::tempPath(), true ); +#endif } /*! diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 151a4a2..3b94c03 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -1114,9 +1114,6 @@ QTextStream::~QTextStream() #endif if (!d->writeBuffer.isEmpty()) d->flushWriteBuffer(); - - delete d; - d_ptr = 0; } /*! diff --git a/src/corelib/io/qtextstream.h b/src/corelib/io/qtextstream.h index 5fa6afa..ddf5f69 100644 --- a/src/corelib/io/qtextstream.h +++ b/src/corelib/io/qtextstream.h @@ -46,6 +46,7 @@ #include <QtCore/qstring.h> #include <QtCore/qchar.h> #include <QtCore/qlocale.h> +#include <QtCore/qscopedpointer.h> #ifndef QT_NO_TEXTCODEC # ifdef QT3_SUPPORT @@ -256,7 +257,7 @@ private: Q_DISABLE_COPY(QTextStream) - QTextStreamPrivate *d_ptr; + QScopedPointer<QTextStreamPrivate> d_ptr; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QTextStream::NumberFlags) diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 3493784..124b2b3 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -87,7 +87,7 @@ mac { kernel/qcore_mac.cpp } -unix { +unix:!symbian { SOURCES += \ kernel/qcore_unix.cpp \ kernel/qcrashhandler.cpp \ @@ -113,6 +113,22 @@ unix { contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) } +symbian { + SOURCES += \ + kernel/qcore_unix.cpp \ + kernel/qcrashhandler.cpp \ + kernel/qeventdispatcher_symbian.cpp \ + kernel/qcore_symbian_p.cpp \ + kernel/qsharedmemory_symbian.cpp \ + kernel/qsystemsemaphore_symbian.cpp + + HEADERS += \ + kernel/qcore_unix_p.h \ + kernel/qcrashhandler_p.h \ + kernel/qeventdispatcher_symbian_p.h \ + kernel/qcore_symbian_p.h +} + vxworks { SOURCES += \ kernel/qfunctions_vxworks.cpp diff --git a/src/corelib/kernel/qcore_symbian_p.cpp b/src/corelib/kernel/qcore_symbian_p.cpp new file mode 100644 index 0000000..c7264cf --- /dev/null +++ b/src/corelib/kernel/qcore_symbian_p.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <exception> +#include <e32base.h> +#include <e32uid.h> +#include "qcore_symbian_p.h" +#include <string> + +QT_BEGIN_NAMESPACE + +/* + Helper function for calling into Symbian classes that expect a TDes&. + This function converts a QString to a TDes by allocating memory that + must be deleted by the caller. +*/ + +Q_CORE_EXPORT HBufC* qt_QString2HBufC(const QString& aString) +{ + HBufC *buffer; +#ifdef QT_NO_UNICODE + TPtrC8 ptr(reinterpret_cast<const TUint8*>(aString.toLocal8Bit().constData())); +#else + TPtrC16 ptr(qt_QString2TPtrC(aString)); +#endif + buffer = q_check_ptr(HBufC::New(ptr.Length())); + buffer->Des().Copy(ptr); + return buffer; +} + +Q_CORE_EXPORT QString qt_TDesC2QString(const TDesC& aDescriptor) +{ +#ifdef QT_NO_UNICODE + return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length()); +#else + return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length()); +#endif +} + +QHBufC::QHBufC() + : m_hBufC(0) +{ +} + +QHBufC::QHBufC(const QHBufC &src) + : m_hBufC(q_check_ptr(src.m_hBufC->Alloc())) +{ +} + +/*! + \internal + Constructs a QHBufC from an HBufC. Note that the QHBufC instance takes + ownership of the HBufC. +*/ +QHBufC::QHBufC(HBufC *src) + : m_hBufC(src) +{ +} + +QHBufC::QHBufC(const QString &src) +{ + m_hBufC = qt_QString2HBufC(src); +} + +QHBufC::~QHBufC() +{ + if (m_hBufC) + delete m_hBufC; +} + +class QS60PluginResolver +{ +public: + QS60PluginResolver() + : initTried(false) {} + + ~QS60PluginResolver() { + lib.Close(); + } + + TLibraryFunction resolve(int ordinal) { + if (!initTried) { + init(); + initTried = true; + } + + if (lib.Handle()) + return lib.Lookup(ordinal); + else + return reinterpret_cast<TLibraryFunction>(NULL); + } + +private: + void init() + { +#ifdef Q_WS_S60 + _LIT(KLibName_3_1, "qts60plugin_3_1.dll"); + _LIT(KLibName_3_2, "qts60plugin_3_2.dll"); + _LIT(KLibName_5_0, "qts60plugin_5_0.dll"); + TPtrC libName; + TInt uidValue; + switch (QSysInfo::s60Version()) { + case QSysInfo::SV_S60_3_1: + libName.Set(KLibName_3_1); + uidValue = 0x2001E620; + break; + case QSysInfo::SV_S60_3_2: + libName.Set(KLibName_3_2); + uidValue = 0x2001E621; + break; + case QSysInfo::SV_S60_5_0: // Fall through to default + default: + // Default to 5.0 version, as any unknown platform is likely to be newer than that + libName.Set(KLibName_5_0); + uidValue = 0x2001E622; + break; + } + + TUidType libUid(KDynamicLibraryUid, KSharedLibraryUid, TUid::Uid(uidValue)); + lib.Load(libName, libUid); +#endif + } + + RLibrary lib; + bool initTried; +}; + +Q_GLOBAL_STATIC(QS60PluginResolver, qt_s60_plugin_resolver); + +/*! + \internal + Resolves a platform version specific function from S60 plugin. + If plugin is missing or resolving fails for another reason, NULL is returned. +*/ +Q_CORE_EXPORT TLibraryFunction qt_resolveS60PluginFunc(int ordinal) +{ + return qt_s60_plugin_resolver()->resolve(ordinal); +} + + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_symbian_p.h b/src/corelib/kernel/qcore_symbian_p.h new file mode 100644 index 0000000..391774f --- /dev/null +++ b/src/corelib/kernel/qcore_symbian_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCORE_SYMBIAN_P_H +#define QCORE_SYMBIAN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <e32std.h> +#include <QtCore/qglobal.h> +#include <qstring.h> +#include <qrect.h> +#include <qhash.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT HBufC* qt_QString2HBufC(const QString& aString); + +Q_CORE_EXPORT QString qt_TDesC2QString(const TDesC& aDescriptor); +inline QString qt_TDes2QString(const TDes& aDescriptor) { return qt_TDesC2QString(aDescriptor); } + +static inline QSize qt_TSize2QSize(const TSize& ts) +{ + return QSize(ts.iWidth, ts.iHeight); +} + +static inline TSize qt_QSize2TSize(const QSize& qs) +{ + return TSize(qs.width(), qs.height()); +} + +static inline QRect qt_TRect2QRect(const TRect& tr) +{ + return QRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height()); +} + +static inline TRect qt_QRect2TRect(const QRect& qr) +{ + return TRect(TPoint(qr.left(), qr.top()), TSize(qr.width(), qr.height())); +} + +// Returned TPtrC is valid as long as the given parameter is valid and unmodified +static inline TPtrC qt_QString2TPtrC( const QString& string ) +{ + return TPtrC16(static_cast<const TUint16*>(string.utf16()), string.length()); +} + +class Q_CORE_EXPORT QHBufC +{ +public: + QHBufC(); + QHBufC(const QHBufC &src); + QHBufC(HBufC *src); + QHBufC(const QString &src); + ~QHBufC(); + + inline operator HBufC *() { return m_hBufC; } + inline operator const HBufC *() const { return m_hBufC; } + inline HBufC *data() { return m_hBufC; } + inline const HBufC *data() const { return m_hBufC; } + inline HBufC & operator*() { return *m_hBufC; } + inline const HBufC & operator*() const { return *m_hBufC; } + inline HBufC * operator->() { return m_hBufC; } + inline const HBufC * operator->() const { return m_hBufC; } + + inline bool operator==(const QHBufC ¶m) const { return data() == param.data(); } + inline bool operator!=(const QHBufC ¶m) const { return data() != param.data(); } + +private: + HBufC *m_hBufC; +}; + +inline uint qHash(TUid uid) +{ + return qHash(uid.iUid); +} + +// S60 version specific function ordinals that can be resolved +enum S60PluginFuncOrdinals +{ + S60Plugin_TimeFormatL = 1, + S60Plugin_GetTimeFormatSpec = 2, + S60Plugin_GetLongDateFormatSpec = 3, + S60Plugin_GetShortDateFormatSpec = 4, + S60Plugin_LocalizedDirectoryName = 5 +}; + +Q_CORE_EXPORT TLibraryFunction qt_resolveS60PluginFunc(int ordinal); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QCORE_SYMBIAN_P_H diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 4fe31a6..c83c24b 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -274,8 +274,8 @@ static inline int qt_safe_close(int fd) #undef QT_CLOSE #define QT_CLOSE qt_safe_close -#ifndef Q_OS_VXWORKS // no processes in VxWorks - +// Open C does not (yet?) implement these on Symbian OS and VxWorks doesn't have processes +#if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_VXWORKS) static inline int qt_safe_execve(const char *filename, char *const argv[], char *const envp[]) { @@ -297,7 +297,9 @@ static inline int qt_safe_execvp(const char *file, char *const argv[]) EINTR_LOOP(ret, ::execvp(file, argv)); return ret; } +#endif +#ifndef Q_OS_VXWORKS // no processes on VxWorks static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options) { register int ret; diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 86221a1..4a4817d 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -64,7 +64,12 @@ #include <private/qfactoryloader_p.h> #include <private/qfunctions_p.h> -#ifdef Q_OS_UNIX +#ifdef Q_OS_SYMBIAN +# include <exception> +# include <f32file.h> +# include "qeventdispatcher_symbian_p.h" +# include "private/qcore_symbian_p.h" +#elif defined(Q_OS_UNIX) # if !defined(QT_NO_GLIB) # include "qeventdispatcher_glib_p.h" # endif @@ -91,6 +96,21 @@ QT_BEGIN_NAMESPACE +class QLockedMutexUnlocker +{ +public: + inline explicit QLockedMutexUnlocker(QMutex *m) + : mtx(m) + { } + inline ~QLockedMutexUnlocker() { unlock(); } + inline void unlock() { if (mtx) mtx->unlock(); mtx = 0; } + +private: + Q_DISABLE_COPY(QLockedMutexUnlocker) + + QMutex *mtx; +}; + #if defined(Q_WS_WIN) || defined(Q_WS_MAC) extern QString qAppFileName(); #endif @@ -162,7 +182,14 @@ void qRemovePostRoutine(QtCleanUpFunction p) void Q_CORE_EXPORT qt_call_post_routines() { - QVFuncList *list = postRList(); + QVFuncList *list = 0; + QT_TRY { + list = postRList(); + } QT_CATCH(const std::bad_alloc &) { + // ignore - if we can't allocate a post routine list, + // there's a high probability that there's no post + // routine to be executed :) + } if (!list) return; while (!list->isEmpty()) @@ -251,30 +278,34 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv) QCoreApplicationPrivate::~QCoreApplicationPrivate() { + if (threadData) { #ifndef QT_NO_THREAD - void *data = &threadData->tls; - QThreadStorageData::finish((void **)data); + void *data = &threadData->tls; + QThreadStorageData::finish((void **)data); #endif - // need to clear the state of the mainData, just in case a new QCoreApplication comes along. - QMutexLocker locker(&threadData->postEventList.mutex); - for (int i = 0; i < threadData->postEventList.size(); ++i) { - const QPostEvent &pe = threadData->postEventList.at(i); - if (pe.event) { - --pe.receiver->d_func()->postedEvents; - pe.event->posted = false; - delete pe.event; + // need to clear the state of the mainData, just in case a new QCoreApplication comes along. + QMutexLocker locker(&threadData->postEventList.mutex); + for (int i = 0; i < threadData->postEventList.size(); ++i) { + const QPostEvent &pe = threadData->postEventList.at(i); + if (pe.event) { + --pe.receiver->d_func()->postedEvents; + pe.event->posted = false; + delete pe.event; + } } + threadData->postEventList.clear(); + threadData->postEventList.recursion = 0; + threadData->quitNow = false; } - threadData->postEventList.clear(); - threadData->postEventList.recursion = 0; - threadData->quitNow = false; } void QCoreApplicationPrivate::createEventDispatcher() { Q_Q(QCoreApplication); -#if defined(Q_OS_UNIX) +#if defined(Q_OS_SYMBIAN) + eventDispatcher = new QEventDispatcherSymbian(q); +#elif defined(Q_OS_UNIX) # if !defined(QT_NO_GLIB) if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported()) eventDispatcher = new QEventDispatcherGlib(q); @@ -312,6 +343,11 @@ void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver) Q_UNUSED(currentThread); Q_UNUSED(thr); } +#elif defined(Q_OS_SYMBIAN) && defined (QT_NO_DEBUG) +// no implementation in release builds, but keep the symbol present +void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver) +{ +} #endif void QCoreApplicationPrivate::appendApplicationPathToLibraryPaths() @@ -319,10 +355,17 @@ void QCoreApplicationPrivate::appendApplicationPathToLibraryPaths() #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) QStringList *app_libpaths = coreappdata()->app_libpaths; Q_ASSERT(app_libpaths); +# if defined(Q_OS_SYMBIAN) + QString app_location( QCoreApplication::applicationDirPath() ); + // File existence check for application's private dir requires additional '\' or + // platform security will not allow it. + if (app_location != QLibraryInfo::location(QLibraryInfo::PluginsPath) && QFile::exists(app_location + QLatin1Char('\\')) && !app_libpaths->contains(app_location)) +# else QString app_location( QCoreApplication::applicationFilePath() ); app_location.truncate(app_location.lastIndexOf(QLatin1Char('/'))); app_location = QDir(app_location).canonicalPath(); if (app_location != QLibraryInfo::location(QLibraryInfo::PluginsPath) && QFile::exists(app_location) && !app_libpaths->contains(app_location)) +# endif app_libpaths->append(app_location); #endif } @@ -450,6 +493,13 @@ QCoreApplication::QCoreApplication(int &argc, char **argv) { init(); QCoreApplicationPrivate::eventDispatcher->startingUp(); +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) && defined(Q_OS_SYMBIAN) + // 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 + } extern void set_winapp_name(); @@ -526,7 +576,14 @@ QCoreApplication::~QCoreApplication() #if !defined(QT_NO_THREAD) #if !defined(QT_NO_CONCURRENT) // Synchronize and stop the global thread pool threads. - QThreadPool::globalInstance()->waitForDone(); + QThreadPool *globalThreadPool = 0; + QT_TRY { + globalThreadPool = QThreadPool::globalInstance(); + } QT_CATCH (...) { + // swallow the exception, since destructors shouldn't throw + } + if (globalThreadPool) + globalThreadPool->waitForDone(); #endif QThread::cleanup(); #endif @@ -622,17 +679,13 @@ bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) d->inEventHandler = true; #endif -#if defined(QT_NO_EXCEPTIONS) - bool returnValue = notify(receiver, event); -#else bool returnValue; - try { + QT_TRY { returnValue = notify(receiver, event); - } catch(...) { + } QT_CATCH (...) { --threadData->loopLevel; - throw; + QT_RETHROW; } -#endif #ifdef QT_JAMBI_BUILD // Restore the previous state if the object was not deleted.. @@ -961,7 +1014,7 @@ void QCoreApplication::exit(int returnCode) The event is \e not deleted when the event has been sent. The normal approach is to create the event on the stack, for example: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 0 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 0 \sa postEvent(), notify() */ @@ -1053,21 +1106,23 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority) data->postEventList.mutex.lock(); } + QLockedMutexUnlocker locker(&data->postEventList.mutex); + // if this is one of the compressible events, do compression if (receiver->d_func()->postedEvents && self && self->compressEvent(event, receiver, &data->postEventList)) { - data->postEventList.mutex.unlock(); return; } - event->posted = true; - ++receiver->d_func()->postedEvents; if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) { // remember the current running eventloop for DeferredDelete // events posted in the receiver's thread event->d = reinterpret_cast<QEventPrivate *>(quintptr(data->loopLevel)); } + // delete the event on exceptions to protect against memory leaks till the event is + // properly owned in the postEventList + QScopedPointer<QEvent> eventDeleter(event); if (data->postEventList.isEmpty() || data->postEventList.last().priority >= priority) { // optimization: we can simply append if the last event in // the queue has higher or equal priority @@ -1082,8 +1137,11 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority) QPostEventList::iterator at = qUpperBound(begin, end, priority); data->postEventList.insert(at, QPostEvent(receiver, event, priority)); } + eventDeleter.take(); + event->posted = true; + ++receiver->d_func()->postedEvents; data->canWait = false; - data->postEventList.mutex.unlock(); + locker.unlock(); if (data->eventDispatcher) data->eventDispatcher->wakeUp(); @@ -1479,7 +1537,7 @@ bool QCoreApplication::event(QEvent *e) Example: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 1 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 1 \sa exit(), aboutToQuit(), QApplication::lastWindowClosed() */ @@ -1713,6 +1771,10 @@ bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator) function also assumes that the current directory has not been changed by the application. + In Symbian this function will return the application private directory + in C-drive, not the path to executable itself, as those are always in + /sys/bin. + \sa applicationFilePath() */ QString QCoreApplication::applicationDirPath() @@ -1724,7 +1786,32 @@ QString QCoreApplication::applicationDirPath() QCoreApplicationPrivate *d = self->d_func(); if (d->cachedApplicationDirPath.isNull()) +#if defined(Q_OS_SYMBIAN) + { + QString appPath; + RProcess proc; + TInt err = proc.Open(proc.Id()); + if (err == KErrNone) { +#if defined(Q_CC_NOKIAX86) + // In emulator, always resolve the private dir on C-drive + appPath.append(QChar('C')); +#else + appPath.append(QChar((proc.FileName())[0])); +#endif + appPath.append(QLatin1String(":\\private\\")); + QString sid; + sid.setNum(proc.SecureId().iId, 16); + appPath.append(sid); + appPath.append(QLatin1Char('\\')); + proc.Close(); + } + + QFileInfo fi(appPath); + d->cachedApplicationDirPath = fi.exists() ? fi.canonicalFilePath() : QString(); + } +#else d->cachedApplicationDirPath = QFileInfo(applicationFilePath()).path(); +#endif return d->cachedApplicationDirPath; } @@ -1765,7 +1852,20 @@ QString QCoreApplication::applicationFilePath() return d->cachedApplicationFilePath; } #endif -#if defined( Q_OS_UNIX ) +#if defined(Q_OS_SYMBIAN) + QString appPath; + RProcess proc; + TInt err = proc.Open(proc.Id()); + if (err == KErrNone) { + TFileName procName = proc.FileName(); + appPath.append(QString(reinterpret_cast<const QChar*>(procName.Ptr()), procName.Length())); + proc.Close(); + } + + d->cachedApplicationFilePath = appPath; + return d->cachedApplicationFilePath; + +#elif defined( Q_OS_UNIX ) # ifdef Q_OS_LINUX // Try looking for a /proc/<pid>/exe symlink first which points to // the absolute path of the executable @@ -2057,7 +2157,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMutex, libraryPathMutex, (QMutex::Recursive)) If you want to iterate over the list, you can use the \l foreach pseudo-keyword: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 2 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 2 \sa setLibraryPaths(), addLibraryPath(), removeLibraryPath(), QLibrary, {How to Create Qt Plugins} @@ -2068,12 +2168,37 @@ QStringList QCoreApplication::libraryPaths() if (!coreappdata()->app_libpaths) { QStringList *app_libpaths = coreappdata()->app_libpaths = new QStringList; QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); +#if defined(Q_OS_SYMBIAN) + // Add existing path on all drives for relative PluginsPath in Symbian + if (installPathPlugins.at(1) != QChar(':')) { + QString tempPath = installPathPlugins; + if (tempPath.at(tempPath.length()-1) != QChar('\\')) { + tempPath += QChar('\\'); + } + RFs fs; + TInt err = fs.Connect(); + if (err == KErrNone) { + TPtrC tempPathPtr(reinterpret_cast<const TText*>(tempPath.constData())); + TFindFile finder(fs); + err = finder.FindByDir(tempPathPtr, tempPathPtr); + while (err == KErrNone) { + QString foundDir = QString::fromUtf16(finder.File().Ptr(), finder.File().Length()); + foundDir = QDir(foundDir).canonicalPath(); + if (!app_libpaths->contains(foundDir)) + app_libpaths->append(foundDir); + err = finder.Find(); + } + fs.Close(); + } + } +#else if (QFile::exists(installPathPlugins)) { // Make sure we convert from backslashes to slashes. installPathPlugins = QDir(installPathPlugins).canonicalPath(); if (!app_libpaths->contains(installPathPlugins)) app_libpaths->append(installPathPlugins); } +#endif // If QCoreApplication is not yet instantiated, // make sure we add the application path when we construct the QCoreApplication @@ -2081,7 +2206,7 @@ QStringList QCoreApplication::libraryPaths() const QByteArray libPathEnv = qgetenv("QT_PLUGIN_PATH"); if (!libPathEnv.isEmpty()) { -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) QLatin1Char pathSep(';'); #else QLatin1Char pathSep(':'); @@ -2183,7 +2308,7 @@ void QCoreApplication::removeLibraryPath(const QString &path) A function with the following signature that can be used as an event filter: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 3 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 3 \sa setEventFilter() */ @@ -2396,7 +2521,7 @@ int QCoreApplication::loopLevel() The function specified by \a ptr should take no arguments and should return nothing. For example: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 4 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 4 Note that for an application- or module-wide cleanup, qAddPostRoutine() is often not suitable. For example, if the @@ -2410,7 +2535,7 @@ int QCoreApplication::loopLevel() parent-child mechanism to call a cleanup function at the right time: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 5 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 5 By selecting the right parent object, this can often be made to clean up the module's data at the right moment. @@ -2424,7 +2549,7 @@ int QCoreApplication::loopLevel() translation functions, \c tr() and \c trUtf8(), with these signatures: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 6 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 6 This macro is useful if you want to use QObject::tr() or QObject::trUtf8() in classes that don't inherit from QObject. @@ -2433,7 +2558,7 @@ int QCoreApplication::loopLevel() class definition (before the first \c{public:} or \c{protected:}). For example: - \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 7 + \snippet doc/src/snippets/code/src.corelib.kernel.qcoreapplication.cpp 7 The \a context parameter is normally the class name, but it can be any string. diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 1cee8af..bf238c3 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -165,7 +165,7 @@ public: virtual bool winEventFilter(MSG *message, long *result); #endif -#ifdef Q_OS_UNIX +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) static void watchUnixSignal(int signal, bool watch); #endif @@ -195,6 +195,8 @@ private: void init(); static QCoreApplication *self; + + Q_DISABLE_COPY(QCoreApplication) friend class QEventDispatcherUNIXPrivate; friend class QApplication; diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index ef776c7..169835f 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -91,7 +91,7 @@ public: static bool checkInstance(const char *method); static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data); -#if !defined (QT_NO_DEBUG) || defined (QT_MAC_FRAMEWORK_BUILD) +#if !defined (QT_NO_DEBUG) || defined (QT_MAC_FRAMEWORK_BUILD) || defined (Q_OS_SYMBIAN) void checkReceiverThread(QObject *receiver); #endif int &argc; diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index f661d47..ed896a6 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -108,6 +108,7 @@ QT_BEGIN_NAMESPACE \value ApplicationLayoutDirectionChange The default application layout direction has changed. \value ApplicationPaletteChange The default application palette has changed. \value ApplicationWindowIconChange The application's icon has changed. + \value CloseSoftwareInputPanel A widget wants to close the software input panel (SIP). \value ChildAdded An object gets a child (QChildEvent). \value ChildInserted An object gets a child (QChildEvent). Qt3Support only, use ChildAdded instead. \value ChildPolished A widget child gets polished (QChildEvent). @@ -186,6 +187,7 @@ QT_BEGIN_NAMESPACE \value Polish The widget is polished. \value PolishRequest The widget should be polished. \value QueryWhatsThis The widget should accept the event if it has "What's This?" help. + \value RequestSoftwareInputPanel A widget wants to open a software input panel (SIP). \value Resize Widget's size changed (QResizeEvent). \value Shortcut Key press in child for shortcut key handling (QShortcutEvent). \value ShortcutOverride Key press in child, for overriding shortcut key handling (QKeyEvent). @@ -269,6 +271,7 @@ QT_BEGIN_NAMESPACE \omitvalue FutureCallOut \omitvalue CocoaRequestModal \omitvalue Signal + \omitvalue SymbianDeferredFocusChanged \omitvalue NativeGesture */ diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index bbe0ac7..babb89d 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -278,6 +278,11 @@ public: NativeGesture = 197, // Internal for platform gesture support + RequestSoftwareInputPanel = 199, + CloseSoftwareInputPanel = 200, + + SymbianDeferredFocusChanged = 201, // Internal for generating asynchronous focus events on Symbian + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp new file mode 100644 index 0000000..02edfb0 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -0,0 +1,1004 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_symbian_p.h" +#include <private/qthread_p.h> +#include <qcoreapplication.h> +#include <private/qcoreapplication_p.h> +#include <qdatetime.h> + +#include <unistd.h> +#include <errno.h> + +QT_BEGIN_NAMESPACE + +#define WAKE_UP_PRIORITY CActive::EPriorityStandard +#define TIMER_PRIORITY CActive::EPriorityHigh +#define NULLTIMER_PRIORITY CActive::EPriorityLow +#define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle + +static inline int qt_pipe_write(int socket, const char *data, qint64 len) +{ + return ::write(socket, data, len); +} +#if defined(write) +# undef write +#endif + +static inline int qt_pipe_close(int socket) +{ + return ::close(socket); +} +#if defined(close) +# undef close +#endif + +static inline int qt_pipe_fcntl(int socket, int command) +{ + return ::fcntl(socket, command); +} +static inline int qt_pipe2_fcntl(int socket, int command, int option) +{ + return ::fcntl(socket, command, option); +} +#if defined(fcntl) +# undef fcntl +#endif + +static inline int qt_socket_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + return ::select(nfds, readfds, writefds, exceptfds, timeout); +} + +// This simply interrupts the select and locks the mutex until destroyed. +class QSelectMutexGrabber +{ +public: + QSelectMutexGrabber(int fd, QMutex *mutex) + : m_mutex(mutex) + { + if (m_mutex->tryLock()) + return; + + char dummy = 0; + qt_pipe_write(fd, &dummy, 1); + + m_mutex->lock(); + } + + ~QSelectMutexGrabber() + { + m_mutex->unlock(); + } + +private: + QMutex *m_mutex; +}; + +/* + * This class is designed to aid in implementing event handling in a more round robin fashion. We + * cannot change active objects that we do not own, but the active objects that Qt owns will use + * this as a base class with convenience functions. + * + * Here is how it works: On every RunL, the deriving class should call okToRun(). This will allow + * exactly one run of the active object, and mark it as such. If it is called again, it will return + * false, and add the object to a queue so it can be run later. + * + * The QCompleteDeferredAOs class is a special object that runs after all others, which will + * reactivate the objects that were previously not run. + */ +inline QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher) + : CActive(priority), + m_dispatcher(dispatcher), + m_hasAlreadyRun(false), + m_hasRunAgain(false), + m_iterationCount(1) +{ +} + +QActiveObject::~QActiveObject() +{ + if (m_hasRunAgain) + m_dispatcher->removeDeferredActiveObject(this); +} + +bool QActiveObject::okToRun() +{ + Q_ASSERT(!m_hasRunAgain); + + if (!m_hasAlreadyRun || m_dispatcher->iterationCount() != m_iterationCount) { + // First occurrence of this event in this iteration. + m_hasAlreadyRun = true; + m_iterationCount = m_dispatcher->iterationCount(); + return true; + } else { + // The event has already occurred. + m_dispatcher->addDeferredActiveObject(this); + m_hasRunAgain = true; + return false; + } +} + +void QActiveObject::reactivateAndComplete() +{ + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + + m_hasRunAgain = false; + m_hasAlreadyRun = false; +} + +QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher) + : CActive(WAKE_UP_PRIORITY), + m_dispatcher(dispatcher) +{ + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QWakeUpActiveObject::~QWakeUpActiveObject() +{ + Cancel(); +} + +void QWakeUpActiveObject::DoCancel() +{ + if (iStatus.Int() & KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QWakeUpActiveObject::RunL() +{ + iStatus = KRequestPending; + SetActive(); + QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled()); +} + +QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo) + : QActiveObject((timerInfo->interval) ? TIMER_PRIORITY : NULLTIMER_PRIORITY , dispatcher), + m_timerInfo(timerInfo) +{ +} + +QTimerActiveObject::~QTimerActiveObject() +{ + Cancel(); +} + +void QTimerActiveObject::DoCancel() +{ + if (m_timerInfo->interval > 0) { + m_rTimer.Cancel(); + m_rTimer.Close(); + } else { + if (iStatus.Int() & KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } + } +} + +void QTimerActiveObject::RunL() +{ + int error; + QT_TRYCATCH_ERROR(error, Run()); + if (error < 0) { + CActiveScheduler::Current()->Error(error); // stop and report here, as this timer will be deleted on scope exit + } +} + +void QTimerActiveObject::Run() +{ + if (!okToRun()) + return; + + if (m_timerInfo->interval > 0) { + // Start a new timer immediately so that we don't lose time. + iStatus = KRequestPending; + SetActive(); + m_rTimer.After(iStatus, m_timerInfo->interval*1000); + + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); + } else { + // However, we only complete zero timers after the event has finished, + // in order to prevent busy looping when doing nested loops. + + // Keep the refpointer around in order to avoid deletion until the end of this function. + SymbianTimerInfoPtr timerInfoPtr(m_timerInfo); + + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); + + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QTimerActiveObject::Start() +{ + CActiveScheduler::Add(this); + if (m_timerInfo->interval > 0) { + m_rTimer.CreateLocal(); + iStatus = KRequestPending; + SetActive(); + m_rTimer.After(iStatus, m_timerInfo->interval*1000); + } else { + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +SymbianTimerInfo::SymbianTimerInfo() +: timerAO(0) +{ +} + +SymbianTimerInfo::~SymbianTimerInfo() +{ + delete timerAO; +} + +QCompleteDeferredAOs::QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher) + : CActive(COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY), + m_dispatcher(dispatcher) +{ + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QCompleteDeferredAOs::~QCompleteDeferredAOs() +{ + Cancel(); +} + +void QCompleteDeferredAOs::complete() +{ + if (iStatus.Int() & KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QCompleteDeferredAOs::DoCancel() +{ + if (iStatus.Int() & KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QCompleteDeferredAOs::RunL() +{ + iStatus = KRequestPending; + SetActive(); + + QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects()); +} + +QSelectThread::QSelectThread() + : m_quit(false) +{ + if (::pipe(m_pipeEnds) != 0) { + qWarning("Select thread was unable to open a pipe, errno: %i", errno); + } else { + int flags0 = qt_pipe_fcntl(m_pipeEnds[0], F_GETFL); + int flags1 = qt_pipe_fcntl(m_pipeEnds[1], F_GETFL); + // We should check the error code here, but Open C has a bug that returns + // failure even though the operation was successful. + qt_pipe2_fcntl(m_pipeEnds[0], F_SETFL, flags0 | O_NONBLOCK); + qt_pipe2_fcntl(m_pipeEnds[1], F_SETFL, flags1 | O_NONBLOCK); + } +} + +QSelectThread::~QSelectThread() +{ + qt_pipe_close(m_pipeEnds[1]); + qt_pipe_close(m_pipeEnds[0]); +} + +void QSelectThread::run() +{ + Q_D(QThread); + + m_mutex.lock(); + + while (!m_quit) { + fd_set readfds; + fd_set writefds; + fd_set exceptionfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptionfds); + + int maxfd = 0; + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Read, &readfds)); + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Write, &writefds)); + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Exception, &exceptionfds)); + maxfd = qMax(maxfd, m_pipeEnds[0]); + maxfd++; + + FD_SET(m_pipeEnds[0], &readfds); + + int ret; + int savedSelectErrno; + //do { + ret = qt_socket_select(maxfd, &readfds, &writefds, &exceptionfds, 0); + savedSelectErrno = errno; + //} while (ret == 0); + + char buffer; + + while (::read(m_pipeEnds[0], &buffer, 1) > 0) {} + + if(ret == 0) { + // do nothing + } else if (ret < 0) { + switch (savedSelectErrno) { + case EBADF: + case EINVAL: + case ENOMEM: + case EFAULT: + qWarning("::select() returned an error: %i", savedSelectErrno); + break; + case ECONNREFUSED: + case EPIPE: + qWarning("::select() returned an error: %i (go through sockets)", savedSelectErrno); + // prepare to go through all sockets + // mark in fd sets both: + // good ones + // ones that return -1 in select + // after loop update notifiers for all of them + + // as we dont have "exception" notifier type + // we should force monitoring fd_set of this + // type as well + + // clean @ start + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptionfds); + { + for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + + fd_set onefds; + FD_ZERO(&onefds); + FD_SET(i.key()->socket(), &onefds); + + fd_set excfds; + FD_ZERO(&excfds); + FD_SET(i.key()->socket(), &excfds); + + maxfd = i.key()->socket() + 1; + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + ret = 0; + + if(i.key()->type() == QSocketNotifier::Read) { + ret = ::select(maxfd, &onefds, 0, &excfds, &timeout); + if(ret != 0) FD_SET(i.key()->socket(), &readfds); + } else if(i.key()->type() == QSocketNotifier::Write) { + ret = ::select(maxfd, 0, &onefds, &excfds, &timeout); + if(ret != 0) FD_SET(i.key()->socket(), &writefds); + } + + } // end for + } + + // traversed all, so update + updateActivatedNotifiers(QSocketNotifier::Read, &readfds); + updateActivatedNotifiers(QSocketNotifier::Write, &writefds); + updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); + + break; + case EINTR: // Should never occur on Symbian, but this is future proof! + default: + qWarning("::select() returned an unknown error: %i", savedSelectErrno); + + break; + } + } else { + updateActivatedNotifiers(QSocketNotifier::Read, &readfds); + updateActivatedNotifiers(QSocketNotifier::Write, &writefds); + updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); + } + + m_waitCond.wait(&m_mutex); + } + + m_mutex.unlock(); +} + +void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status ) +{ + Q_D(QThread); + + if (!isRunning()) { + start(); + } + + QSelectMutexGrabber lock(m_pipeEnds[1], &m_mutex); + + Q_ASSERT(!m_AOStatuses.contains(notifier)); + + m_AOStatuses.insert(notifier, status); + + m_waitCond.wakeAll(); +} + +void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier ) +{ + QSelectMutexGrabber lock(m_pipeEnds[1], &m_mutex); + + m_AOStatuses.remove(notifier); + + m_waitCond.wakeAll(); +} + +void QSelectThread::restart() +{ + QSelectMutexGrabber lock(m_pipeEnds[1], &m_mutex); + + m_waitCond.wakeAll(); +} + +int QSelectThread::updateSocketSet(QSocketNotifier::Type type, fd_set *fds) +{ + int maxfd = 0; + if(m_AOStatuses.isEmpty()) { + /* + * Wonder if should return -1 + * to signal that no descriptors + * added to fds + */ + return maxfd; + } + for ( QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + if (i.key()->type() == type) { + FD_SET(i.key()->socket(), fds); + maxfd = qMax(maxfd, i.key()->socket()); + } else if(type == QSocketNotifier::Exception) { + /* + * We are registering existing sockets + * always to exception set + * + * Doing double FD_SET shouldn't + * matter + */ + FD_SET(i.key()->socket(), fds); + maxfd = qMax(maxfd, i.key()->socket()); + } + } + + return maxfd; +} + +void QSelectThread::updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds) +{ + Q_D(QThread); + if(m_AOStatuses.isEmpty()) { + return; + } + QList<QSocketNotifier *> toRemove; + for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + if (i.key()->type() == type && FD_ISSET(i.key()->socket(), fds)) { + toRemove.append(i.key()); + TRequestStatus *status = i.value(); + // Thread data is still owned by the main thread. + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } else if(type == QSocketNotifier::Exception && FD_ISSET(i.key()->socket(), fds)) { + /* + * check if socket is in exception set + * then signal RequestComplete for it + */ + qWarning("exception on %d", i.key()->socket()); + toRemove.append(i.key()); + TRequestStatus *status = i.value(); + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } + } + + for (int c = 0; c < toRemove.size(); ++c) { + m_AOStatuses.remove(toRemove[c]); + } +} + +void QSelectThread::stop() +{ + m_quit = true; + restart(); + wait(); +} + +QSocketActiveObject::QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier) + : QActiveObject(CActive::EPriorityStandard, dispatcher), + m_notifier(notifier), + m_inSocketEvent(false), + m_deleteLater(false) +{ + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QSocketActiveObject::~QSocketActiveObject() +{ + Cancel(); +} + +void QSocketActiveObject::DoCancel() +{ + if (iStatus.Int() & KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QSocketActiveObject::RunL() +{ + if (!okToRun()) + return; + + QT_TRYCATCH_LEAVING(m_dispatcher->socketFired(this)); +} + +void QSocketActiveObject::deleteLater() +{ + if (m_inSocketEvent) { + m_deleteLater = true; + } else { + delete this; + } +} + +QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent) + : QAbstractEventDispatcher(parent), + m_activeScheduler(0), + m_wakeUpAO(0), + m_completeDeferredAOs(0), + m_interrupt(false), + m_wakeUpDone(0), + m_iterationCount(0), + m_noSocketEvents(false) +{ +} + +QEventDispatcherSymbian::~QEventDispatcherSymbian() +{ + m_processHandle.Close(); +} + +void QEventDispatcherSymbian::startingUp() +{ + if( !CActiveScheduler::Current() ) { + m_activeScheduler = q_check_ptr(new CQtActiveScheduler()); // CBase derived class needs to be checked on new + CActiveScheduler::Install(m_activeScheduler); + } + m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this)); + m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this)); + // We already might have posted events, wakeup once to process them + wakeUp(); +} + +void QEventDispatcherSymbian::closingDown() +{ + if (m_selectThread.isRunning()) { + m_selectThread.stop(); + } + + delete m_completeDeferredAOs; + delete m_wakeUpAO; + if (m_activeScheduler) { + delete m_activeScheduler; + } +} + +bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags ) +{ + bool handledAnyEvent = false; + + QT_TRY { + Q_D(QAbstractEventDispatcher); + + // It is safe if this counter overflows. The main importance is that each + // iteration count is different from the last. + m_iterationCount++; + + RThread &thread = d->threadData->symbian_thread_handle; + + bool block; + if (flags & QEventLoop::WaitForMoreEvents) { + block = true; + emit aboutToBlock(); + } else { + block = false; + } + + bool oldNoSocketEventsValue = m_noSocketEvents; + if (flags & QEventLoop::ExcludeSocketNotifiers) { + m_noSocketEvents = true; + } else { + m_noSocketEvents = false; + handledAnyEvent = sendDeferredSocketEvents(); + } + + bool handledSymbianEvent = false; + m_interrupt = false; + + /* + * This QTime variable is used to measure the time it takes to finish + * the event loop. If we take too long in the loop, other processes + * may be starved and killed. After the first event has completed, we + * take the current time, and if the remaining events take longer than + * a preset time, we temporarily lower the priority to force a context + * switch. For applications that do not take unecessarily long in the + * event loop, the priority will not be altered. + */ + QTime time; + enum { + FirstRun, + SubsequentRun, + TimeStarted + } timeState = FirstRun; + + TProcessPriority priority; + + while (1) { + if (block) { + // This is where Qt will spend most of its time. + CActiveScheduler::Current()->WaitForAnyRequest(); + } else { + if (thread.RequestCount() == 0) { + break; + } + // This one should return without delay. + CActiveScheduler::Current()->WaitForAnyRequest(); + } + + if (timeState == SubsequentRun) { + time.start(); + timeState = TimeStarted; + } + + TInt error; + handledSymbianEvent = CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle); + if (error) { + qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error); + CActiveScheduler::Current()->Error(error); + } + + if (!handledSymbianEvent) { + qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal"); + } + handledAnyEvent = true; + if (m_interrupt) { + break; + } + block = false; + if (timeState == TimeStarted && time.elapsed() > 100) { + priority = m_processHandle.Priority(); + m_processHandle.SetPriority(EPriorityLow); + time.start(); + // Slight chance of race condition in the next lines, but nothing fatal + // will happen, just wrong priority. + if (m_processHandle.Priority() == EPriorityLow) { + m_processHandle.SetPriority(priority); + } + } + if (timeState == FirstRun) + timeState = SubsequentRun; + }; + + emit awake(); + + m_noSocketEvents = oldNoSocketEventsValue; + } QT_CATCH (const std::exception& ex) { +#ifndef QT_NO_EXCEPTIONS + CActiveScheduler::Current()->Error(qt_exception2SymbianError(ex)); +#endif + } + + return handledAnyEvent; +} + +void QEventDispatcherSymbian::timerFired(int timerId) +{ + QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId); + if (i == m_timerList.end()) { + // The timer has been deleted. Ignore this event. + return; + } + + SymbianTimerInfoPtr timerInfo = *i; + + // Prevent infinite timer recursion. + if (timerInfo->inTimerEvent) { + return; + } + + timerInfo->inTimerEvent = true; + + QTimerEvent event(timerInfo->timerId); + QCoreApplication::sendEvent(timerInfo->receiver, &event); + + timerInfo->inTimerEvent = false; + + return; +} + +void QEventDispatcherSymbian::socketFired(QSocketActiveObject *socketAO) +{ + if (m_noSocketEvents) { + m_deferredSocketEvents.append(socketAO); + return; + } + + QEvent e(QEvent::SockAct); + socketAO->m_inSocketEvent = true; + QCoreApplication::sendEvent(socketAO->m_notifier, &e); + socketAO->m_inSocketEvent = false; + + if (socketAO->m_deleteLater) { + delete socketAO; + } else { + socketAO->iStatus = KRequestPending; + socketAO->SetActive(); + reactivateSocketNotifier(socketAO->m_notifier); + } +} + +void QEventDispatcherSymbian::wakeUpWasCalled() +{ + // The reactivation should happen in RunL, right before the call to this function. + // This is because m_wakeUpDone is the "signal" that the object can be completed + // once more. + // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee + // that no posted event notification will be lost. If we did it the other way + // around, it would be possible for another thread to post an event right after + // the sendPostedEvents was done, but before the object was ready to be completed + // again. This could deadlock the application if there are no other posted events. + m_wakeUpDone.fetchAndStoreOrdered(0); + sendPostedEvents(); +} + +void QEventDispatcherSymbian::interrupt() +{ + m_interrupt = true; + wakeUp(); +} + +void QEventDispatcherSymbian::wakeUp() +{ + Q_D(QAbstractEventDispatcher); + + if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) { + TRequestStatus *status = &m_wakeUpAO->iStatus; + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } +} + +bool QEventDispatcherSymbian::sendPostedEvents() +{ + Q_D(QAbstractEventDispatcher); + + // moveToThread calls this and canWait == true -> Events will never get processed + // if we check for d->threadData->canWait + // + // QCoreApplication::postEvent sets canWait = false, but after the object and events + // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect + // the flag on old thread. That's why events in a new thread will not get processed. + // This migth be actually bug in moveToThread functionality, but because other platforms + // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from + // here as well. + + //if (!d->threadData->canWait) { + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + return true; + //} + //return false; +} + +inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object) +{ + if (m_deferredActiveObjects.isEmpty()) { + m_completeDeferredAOs->complete(); + } + m_deferredActiveObjects.append(object); +} + +inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object) +{ + m_deferredActiveObjects.removeAll(object); +} + +void QEventDispatcherSymbian::reactivateDeferredActiveObjects() +{ + while (!m_deferredActiveObjects.isEmpty()) { + QActiveObject *object = m_deferredActiveObjects.takeFirst(); + object->reactivateAndComplete(); + } + + // We do this because we want to return from processEvents. This is because + // each invocation of processEvents should only run each active object once. + // The active scheduler should run them continously, however. + m_interrupt = true; +} + +bool QEventDispatcherSymbian::sendDeferredSocketEvents() +{ + bool sentAnyEvents = false; + while (!m_deferredSocketEvents.isEmpty()) { + sentAnyEvents = true; + socketFired(m_deferredSocketEvents.takeFirst()); + } + + return sentAnyEvents; +} + +void QEventDispatcherSymbian::flush() +{ +} + +bool QEventDispatcherSymbian::hasPendingEvents() +{ + Q_D(QAbstractEventDispatcher); + return (d->threadData->symbian_thread_handle.RequestCount() != 0 + || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty()); +} + +void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier ) +{ + QSocketActiveObject *socketAO = q_check_ptr(new QSocketActiveObject(this, notifier)); + m_notifiers.insert(notifier, socketAO); + m_selectThread.requestSocketEvents(notifier, &socketAO->iStatus); +} + +void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier ) +{ + m_selectThread.cancelSocketEvents(notifier); + if (m_notifiers.contains(notifier)) { + QSocketActiveObject *sockObj = *m_notifiers.find(notifier); + m_deferredSocketEvents.removeAll(sockObj); + sockObj->deleteLater(); + m_notifiers.remove(notifier); + } +} + +void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier) +{ + m_selectThread.requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus); +} + +void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object ) +{ + if (interval < 0) { + qWarning("Timer interval < 0"); + interval = 0; + } + + SymbianTimerInfoPtr timer(new SymbianTimerInfo); + timer->timerId = timerId; + timer->interval = interval; + timer->inTimerEvent = false; + timer->receiver = object; + timer->dispatcher = this; + timer->timerAO = q_check_ptr(new QTimerActiveObject(this, timer.data())); + m_timerList.insert(timerId, timer); + + timer->timerAO->Start(); +} + +bool QEventDispatcherSymbian::unregisterTimer ( int timerId ) +{ + if (!m_timerList.contains(timerId)) { + return false; + } + + SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId); + + if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(timerId); + + return true; +} + +bool QEventDispatcherSymbian::unregisterTimers ( QObject * object ) +{ + if (m_timerList.isEmpty()) + return false; + + bool unregistered = false; + for (QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.begin(); i != m_timerList.end(); ) { + if ((*i)->receiver == object) { + i = m_timerList.erase(i); + unregistered = true; + } else { + ++i; + } + } + + return unregistered; +} + +QList<QEventDispatcherSymbian::TimerInfo> QEventDispatcherSymbian::registeredTimers ( QObject * object ) const +{ + QList<TimerInfo> list; + for (QHash<int, SymbianTimerInfoPtr>::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) { + if ((*i)->receiver == object) { + list.push_back(TimerInfo((*i)->timerId, (*i)->interval)); + } + } + + return list; +} + +/* + * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves + * or exceptions from Qt that fall back to the scheduler. + * It will be used in cases where there is no existing active scheduler installed. + * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread + * instead of this one. But this would be used in other threads in the UI. + * An app could replace this behaviour by installing an alternative active scheduler. + */ +void CQtActiveScheduler::Error(TInt aError) const +{ + QT_TRY { + qWarning("Error from active scheduler %d", aError); + } + QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h new file mode 100644 index 0000000..28fee9e --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_SYMBIAN_P_H +#define QEVENTDISPATCHER_SYMBIAN_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 <qhash.h> +#include <qset.h> +#include <qshareddata.h> +#include <qabstracteventdispatcher.h> +#include <private/qabstracteventdispatcher_p.h> +#include <qthread.h> +#include <qmutex.h> +#include <qwaitcondition.h> +#include <qsocketnotifier.h> + +#include <e32base.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/select.h> + +QT_BEGIN_NAMESPACE + + +class QEventDispatcherSymbian; +class QTimerActiveObject; + +class QActiveObject : public CActive +{ +public: + QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher); + ~QActiveObject(); + + bool okToRun(); + + void reactivateAndComplete(); + +protected: + QEventDispatcherSymbian *m_dispatcher; + +private: + bool m_hasAlreadyRun : 1; + bool m_hasRunAgain : 1; + int m_iterationCount; +}; + +class QWakeUpActiveObject : public CActive +{ +public: + QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher); + ~QWakeUpActiveObject(); + + void Complete(); + +protected: + void DoCancel(); + void RunL(); + +private: + QEventDispatcherSymbian *m_dispatcher; +}; + +struct SymbianTimerInfo : public QSharedData +{ + SymbianTimerInfo(); + ~SymbianTimerInfo(); + + int timerId; + int interval; + bool inTimerEvent; + QObject *receiver; + QTimerActiveObject *timerAO; + QEventDispatcherSymbian *dispatcher; +}; + +typedef QExplicitlySharedDataPointer<SymbianTimerInfo> SymbianTimerInfoPtr; + +// This is a bit of a proxy class. See comments in SetActive and Start for details. +class QTimerActiveObject : public QActiveObject +{ +public: + QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo); + ~QTimerActiveObject(); + + void Start(); + +protected: + void DoCancel(); + void RunL(); + +private: + void Run(); + +private: + SymbianTimerInfo *m_timerInfo; + RTimer m_rTimer; +}; + +class QCompleteDeferredAOs : public CActive +{ +public: + QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher); + ~QCompleteDeferredAOs(); + + void complete(); + +protected: + void DoCancel(); + void RunL(); + +private: + QEventDispatcherSymbian *m_dispatcher; +}; + +class QSocketActiveObject : public QActiveObject +{ +public: + QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier); + ~QSocketActiveObject(); + + void deleteLater(); + +protected: + void DoCancel(); + void RunL(); + +private: + QSocketNotifier *m_notifier; + bool m_inSocketEvent; + bool m_deleteLater; + + friend class QEventDispatcherSymbian; +}; + +class QSelectThread : public QThread +{ + Q_DECLARE_PRIVATE(QThread) + +public: + QSelectThread(); + ~QSelectThread(); + + void requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status ); + void cancelSocketEvents ( QSocketNotifier *notifier ); + void restart(); + void stop(); + +protected: + void run(); + +private: + int updateSocketSet(QSocketNotifier::Type type, fd_set *fds); + void updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds); + +private: + int m_pipeEnds[2]; + QHash<QSocketNotifier *, TRequestStatus *> m_AOStatuses; + QMutex m_mutex; + QWaitCondition m_waitCond; + bool m_quit; +}; + +class Q_CORE_EXPORT CQtActiveScheduler : public CActiveScheduler +{ +public: // from CActiveScheduler + virtual void Error(TInt aError) const; +}; + +class Q_CORE_EXPORT QEventDispatcherSymbian : public QAbstractEventDispatcher +{ + Q_DECLARE_PRIVATE(QAbstractEventDispatcher) + +public: + QEventDispatcherSymbian(QObject *parent = 0); + ~QEventDispatcherSymbian(); + + void flush(); + bool hasPendingEvents(); + void interrupt(); + bool processEvents ( QEventLoop::ProcessEventsFlags flags ); + void registerSocketNotifier ( QSocketNotifier * notifier ); + void registerTimer ( int timerId, int interval, QObject * object ); + QList<TimerInfo> registeredTimers ( QObject * object ) const; + void unregisterSocketNotifier ( QSocketNotifier * notifier ); + bool unregisterTimer ( int timerId ); + bool unregisterTimers ( QObject * object ); + void wakeUp(); + + void startingUp(); + void closingDown(); + + void timerFired(int timerId); + void socketFired(QSocketActiveObject *socketAO); + void wakeUpWasCalled(); + void reactivateSocketNotifier(QSocketNotifier *notifier); + + void addDeferredActiveObject(QActiveObject *object); + void removeDeferredActiveObject(QActiveObject *object); + void reactivateDeferredActiveObjects(); + + inline int iterationCount() const { return m_iterationCount; } + + static void RequestComplete(TRequestStatus *&status, TInt reason); + static void RequestComplete(RThread &threadHandle, TRequestStatus *&status, TInt reason); + +private: + bool sendPostedEvents(); + bool sendDeferredSocketEvents(); + +private: + QSelectThread m_selectThread; + + CQtActiveScheduler *m_activeScheduler; + + QHash<int, SymbianTimerInfoPtr> m_timerList; + QHash<QSocketNotifier *, QSocketActiveObject *> m_notifiers; + + QWakeUpActiveObject *m_wakeUpAO; + QCompleteDeferredAOs *m_completeDeferredAOs; + + volatile bool m_interrupt; + QAtomicInt m_wakeUpDone; + + unsigned char m_iterationCount; + bool m_noSocketEvents; + QList<QSocketActiveObject *> m_deferredSocketEvents; + + QList<QActiveObject *> m_deferredActiveObjects; + + RProcess m_processHandle; +}; + +#ifdef QT_DEBUG +// EActive is defined to 1 and ERequestPending to 2, but they are both private. +// A little dangerous to rely on, but it is only for debugging. +# define REQUEST_STATUS_ACTIVE_AND_PENDING 3 +# define VERIFY_PENDING_REQUEST_STATUS \ + Q_ASSERT(status->Int() & REQUEST_STATUS_ACTIVE_AND_PENDING == REQUEST_STATUS_ACTIVE_AND_PENDING); +#else +# define REQUEST_STATUS_ACTIVE_AND_PENDING +# define VERIFY_PENDING_REQUEST_STATUS +#endif + +// Convenience functions for doing some sanity checking on our own complete code. +// Unless QT_DEBUG is defined, it is exactly equivalent to the Symbian version. +inline void QEventDispatcherSymbian::RequestComplete(TRequestStatus *&status, TInt reason) +{ + VERIFY_PENDING_REQUEST_STATUS + User::RequestComplete(status, reason); +} +inline void QEventDispatcherSymbian::RequestComplete(RThread &threadHandle, TRequestStatus *&status, TInt reason) +{ + VERIFY_PENDING_REQUEST_STATUS + threadHandle.RequestComplete(status, reason); +} + +#undef REQUEST_STATUS_ACTIVE_AND_PENDING +#undef VERIFY_PENDING_REQUEST_STATUS + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_SYMBIAN_P_H diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index 74ff0ec..dded12d 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -232,7 +232,7 @@ int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, continue; for (int i = 0; i < list.size(); ++i) { - QSockNot *sn = list.at(i); + QSockNot *sn = list[i]; FD_ZERO(&fdset); FD_SET(sn->fd, &fdset); @@ -295,7 +295,7 @@ int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, for (int i=0; i<3; i++) { QSockNotType::List &list = sn_vec[i].list; for (int j = 0; j < list.size(); ++j) { - QSockNot *sn = list.at(j); + QSockNot *sn = list[j]; if (FD_ISSET(sn->fd, &sn_vec[i].select_fds)) q->setSocketNotifierPending(sn->obj); } @@ -622,7 +622,10 @@ QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObj { } QEventDispatcherUNIX::~QEventDispatcherUNIX() -{ } +{ + Q_D(QEventDispatcherUNIX); + d->threadData->eventDispatcher = 0; +} int QEventDispatcherUNIX::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timeval *timeout) @@ -711,8 +714,8 @@ QSockNotType::QSockNotType() QSockNotType::~QSockNotType() { - while (!list.isEmpty()) - delete list.takeFirst(); + for (int i = 0; i < list.size(); ++i) + delete list[i]; } /***************************************************************************** @@ -748,7 +751,7 @@ void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier) int i; for (i = 0; i < list.size(); ++i) { - QSockNot *p = list.at(i); + QSockNot *p = list[i]; if (p->fd < sockfd) break; if (p->fd == sockfd) { @@ -786,7 +789,7 @@ void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier) QSockNot *sn = 0; int i; for (i = 0; i < list.size(); ++i) { - sn = list.at(i); + sn = list[i]; if(sn->obj == notifier && sn->fd == sockfd) break; } @@ -804,7 +807,7 @@ void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier) for (int i=0; i<3; i++) { if (!d->sn_vec[i].list.isEmpty()) d->sn_highest = qMax(d->sn_highest, // list is fd-sorted - d->sn_vec[i].list.first()->fd); + d->sn_vec[i].list[0]->fd); } } } @@ -828,7 +831,7 @@ void QEventDispatcherUNIX::setSocketNotifierPending(QSocketNotifier *notifier) QSockNot *sn = 0; int i; for (i = 0; i < list.size(); ++i) { - sn = list.at(i); + sn = list[i]; if(sn->obj == notifier && sn->fd == sockfd) break; } diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index cf0e09f..c3f165a 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -58,6 +58,7 @@ #include "private/qabstracteventdispatcher_p.h" #include "private/qcore_unix_p.h" #include "private/qpodlist_p.h" +#include "QtCore/qvarlengtharray.h" #if defined(Q_OS_VXWORKS) # include <sys/times.h> diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 522f0dc..2e4d840 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1573,7 +1573,9 @@ bool QMetaMethod::invoke(QObject *object, int nargs = 1; // include return type void **args = (void **) qMalloc(paramCount * sizeof(void *)); + Q_CHECK_PTR(args); int *types = (int *) qMalloc(paramCount * sizeof(int)); + Q_CHECK_PTR(types); types[0] = 0; // return type args[0] = 0; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 2afab1a..6384d17 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -73,6 +73,7 @@ static int DIRECT_CONNECTION_ONLY = 0; static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) { int *types = new int [typeNames.count() + 1]; + Q_CHECK_PTR(types); for (int i = 0; i < typeNames.count(); ++i) { const QByteArray typeName = typeNames.at(i); if (typeName.endsWith('*')) @@ -405,7 +406,9 @@ void QMetaObject::removeGuard(QObject **ptr) if (!*ptr) return; GuardHash *hash = guardHash(); - if (!hash) + /* check that the hash is empty - otherwise we might detach + the shared_null hash, which will alloc, which is not nice */ + if (!hash || hash->isEmpty()) return; QMutexLocker locker(guardHashLock()); GuardHash::iterator it = hash->find(*ptr); @@ -433,6 +436,10 @@ void QMetaObject::changeGuard(QObject **ptr, QObject *o) return; } QMutexLocker locker(guardHashLock()); + if (o) { + hash->insert(o, ptr); + QObjectPrivate::get(o)->hasGuards = true; + } if (*ptr) { bool more = false; //if the QObject has more pointer attached to it. GuardHash::iterator it = hash->find(*ptr); @@ -449,10 +456,6 @@ void QMetaObject::changeGuard(QObject **ptr, QObject *o) QObjectPrivate::get(*ptr)->hasGuards = false; } *ptr = o; - if (*ptr) { - hash->insert(*ptr, ptr); - QObjectPrivate::get(*ptr)->hasGuards = true; - } } /*! \internal @@ -476,9 +479,20 @@ void QObjectPrivate::clearGuards(QObject *object) if (!priv->hasGuards) return; - GuardHash *hash = guardHash(); - if (hash) { - QMutexLocker locker(guardHashLock()); + + GuardHash *hash = 0; + QMutex *mutex = 0; + QT_TRY { + hash = guardHash(); + mutex = guardHashLock(); + } QT_CATCH(const std::bad_alloc &) { + // do nothing in case of OOM - code below is safe + } + + /* check that the hash is empty - otherwise we might detach + the shared_null hash, which will alloc, which is not nice */ + if (hash && !hash->isEmpty()) { + QMutexLocker locker(mutex); GuardHash::iterator it = hash->find(object); const GuardHash::iterator end = hash->end(); while (it.key() == object && it != end) { @@ -702,12 +716,18 @@ QObject::QObject(QObject *parent) : d_ptr(new QObjectPrivate) { Q_D(QObject); - qt_addObject(d_ptr->q_ptr = this); + 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); + 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); } #ifdef QT3_SUPPORT @@ -737,20 +757,26 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent) : d_ptr(&dd) { Q_D(QObject); - qt_addObject(d_ptr->q_ptr = this); + 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; - if (d->isWidget) { - if (parent) { - d->parent = parent; - d->parent->d_func()->children.append(this); + 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_addObject(this); } /*! @@ -808,12 +834,35 @@ QObject::~QObject() delete d->sharedRefcount; } - emit destroyed(this); - if (d->declarativeData) - d->declarativeData->destroyed(this); + QT_TRY { + emit destroyed(this); + if (d->declarativeData) + d->declarativeData->destroyed(this); // ### TODO: Can this throw? + } 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_AUTOTEST_EXPORT) && !defined(QT_NO_EXCEPTIONS) + struct AutotestException : public std::exception + { + const char *what() const throw() { return "autotest swallow"; } + } autotestException; + // throw autotestException; + +#else + QT_RETHROW; +#endif + } + { - QMutexLocker locker(signalSlotLock(this)); + QMutex *signalSlotMutex = 0; + QT_TRY { + signalSlotMutex = signalSlotLock(this); + } QT_CATCH(const std::bad_alloc &) { + // out of memory - swallow to prevent a crash + } + QMutexLocker locker(signalSlotMutex); // set ref to zero to indicate that this object has been deleted if (d->currentSender != 0) @@ -909,9 +958,6 @@ QObject::~QObject() objectName().isNull() ? "unnamed" : qPrintable(objectName())); } #endif - - delete d; - d_ptr = 0; } QObjectPrivate::Connection::~Connection() @@ -1160,11 +1206,11 @@ bool QObject::event(QEvent *e) #if defined(QT_NO_EXCEPTIONS) mce->placeMetaCall(this); #else - try { + QT_TRY { mce->placeMetaCall(this); - } catch (...) { + } QT_CATCH(...) { QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); - throw; + QT_RETHROW; } #endif QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); @@ -1457,8 +1503,10 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData ++eventsMoved; } } - if (eventsMoved > 0 && targetData->eventDispatcher) + if (eventsMoved > 0 && targetData->eventDispatcher) { + targetData->canWait = false; targetData->eventDispatcher->wakeUp(); + } // the current emitting thread shouldn't restore currentSender after calling moveToThread() if (currentSender) @@ -2731,8 +2779,14 @@ bool QObject::disconnect(const QObject *sender, const char *signal, QByteArray signal_name; bool signal_found = false; if (signal) { - signal_name = QMetaObject::normalizedSignature(signal); - signal = signal_name; + QT_TRY { + signal_name = QMetaObject::normalizedSignature(signal); + signal = signal_name.constData(); + } QT_CATCH (const std::bad_alloc &) { + // if the signal is already normalized, we can continue. + if (sender->metaObject()->indexOfSignal(signal + 1) == -1) + QT_RETHROW; + } if (!check_signal_macro(sender, signal, "disconnect", "unbind")) return false; @@ -2744,8 +2798,15 @@ bool QObject::disconnect(const QObject *sender, const char *signal, int membcode = -1; bool method_found = false; if (method) { - method_name = QMetaObject::normalizedSignature(method); - method = method_name; + QT_TRY { + method_name = QMetaObject::normalizedSignature(method); + method = method_name.constData(); + } QT_CATCH(const std::bad_alloc &) { + // if the method is already normalized, we can continue. + if (receiver->metaObject()->indexOfMethod(method + 1) == -1) + QT_RETHROW; + } + membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "disconnect")) return false; @@ -2907,14 +2968,20 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, c->connectionType = type; c->argumentTypes = types; c->nextConnectionList = 0; + + QT_TRY { + s->d_func()->addConnection(signal_index, c); + } QT_CATCH(...) { + delete c; + QT_RETHROW; + } + c->prev = &r->d_func()->senders; c->next = *c->prev; *c->prev = c; if (c->next) c->next->prev = &c->next; - s->d_func()->addConnection(signal_index, c); - if (signal_index < 0) { for (uint i = 0; i < (sizeof sender->d_func()->connectedSignals / sizeof sender->d_func()->connectedSignals[0] ); ++i) @@ -3106,7 +3173,9 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect while (c->argumentTypes[nargs-1]) ++nargs; int *types = (int *) qMalloc(nargs*sizeof(int)); + Q_CHECK_PTR(types); void **args = (void **) qMalloc(nargs*sizeof(void *)); + Q_CHECK_PTR(args); types[0] = 0; // return type args[0] = 0; // return value for (int n = 1; n < nargs; ++n) @@ -3219,9 +3288,9 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal #if defined(QT_NO_EXCEPTIONS) metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); #else - try { + QT_TRY { metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); - } catch (...) { + } QT_CATCH(...) { locker.relock(); QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); @@ -3230,7 +3299,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal Q_ASSERT(connectionLists->inUse >= 0); if (connectionLists->orphaned && !connectionLists->inUse) delete connectionLists; - throw; + QT_RETHROW; } #endif diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 52c5d9e..aa538bc 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -51,6 +51,7 @@ #ifdef QT_INCLUDE_COMPAT #include <QtCore/qcoreevent.h> #endif +#include <QtCore/qscopedpointer.h> QT_BEGIN_HEADER @@ -288,7 +289,7 @@ protected: QObject(QObjectPrivate &dd, QObject *parent = 0); protected: - QObjectData *d_ptr; + QScopedPointer<QObjectData> d_ptr; static const QMetaObject staticQtMetaObject; diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index 168bf29..cd69b59 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -44,7 +44,9 @@ #include "qsystemsemaphore.h" #include <qdir.h> #include <qcryptographichash.h> - +#ifdef Q_OS_SYMBIAN +#include <e32const.h> +#endif #include <qdebug.h> QT_BEGIN_NAMESPACE @@ -57,6 +59,7 @@ QT_BEGIN_NAMESPACE the subset that the win/unix kernel allows. On Unix this will be a file name + On Symbian key will be truncated to 80 characters */ QString QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, @@ -70,10 +73,12 @@ 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; #else @@ -118,6 +123,14 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, process. This means that QSharedMemory should not be used across multiple threads in the same process in HP-UX. + \o Symbian: QSharedMemory does not "own" the shared memory segment. + When all threads or processes that have an instance of QSharedMemory + attached to a particular shared memory segment have either destroyed + their instance of QSharedMemory or exited, the Symbian kernel + releases the shared memory segment automatically. + Also, access to a shared memory segment cannot be limited to read-only + in Symbian. + \endlist Remember to lock the shared memory with lock() before reading from diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h index f4d7fae..d349021 100644 --- a/src/corelib/kernel/qsharedmemory_p.h +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -71,6 +71,9 @@ namespace QSharedMemoryPrivate #ifdef Q_OS_WIN #include <qt_windows.h> +#elif defined(Q_OS_SYMBIAN) +#include <e32std.h> +#include <sys/types.h> #else #include <sys/sem.h> #endif @@ -140,7 +143,11 @@ public: bool attach(QSharedMemory::AccessMode mode); bool detach(); +#ifdef Q_OS_SYMBIAN + void setErrorString(const QString &function, TInt errorCode); +#else void setErrorString(const QString &function); +#endif #ifndef QT_NO_SYSTEMSEMAPHORE bool tryLocker(QSharedMemoryLocker *locker, const QString function) { @@ -156,6 +163,8 @@ public: private: #ifdef Q_OS_WIN HANDLE hand; +#elif defined(Q_OS_SYMBIAN) + RChunk chunk; #else key_t unix_key; #endif diff --git a/src/corelib/kernel/qsharedmemory_symbian.cpp b/src/corelib/kernel/qsharedmemory_symbian.cpp new file mode 100644 index 0000000..d35c21f --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_symbian.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include "qcore_symbian_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SHAREDMEMORY + +#define QSHAREDMEMORY_DEBUG + +QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(), + memory(0), size(0), error(QSharedMemory::NoError), + systemSemaphore(QString()), lockedByMe(false) +{ +} + +void QSharedMemoryPrivate::setErrorString(const QString &function, TInt errorCode) +{ + if (errorCode == KErrNone) + return; + switch (errorCode) { + case KErrAlreadyExists: + error = QSharedMemory::AlreadyExists; + errorString = QSharedMemory::tr("%1: already exists").arg(function); + break; + case KErrNotFound: + error = QSharedMemory::NotFound; + errorString = QSharedMemory::tr("%1: doesn't exists").arg(function); + break; + case KErrArgument: + error = QSharedMemory::InvalidSize; + errorString = QSharedMemory::tr("%1: invalid size").arg(function); + break; + case KErrNoMemory: + error = QSharedMemory::OutOfResources; + errorString = QSharedMemory::tr("%1: out of resources").arg(function); + break; + case KErrPermissionDenied: + error = QSharedMemory::PermissionDenied; + errorString = QSharedMemory::tr("%1: permission denied").arg(function); + break; + default: + errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errorCode); + error = QSharedMemory::UnknownError; +#if defined QSHAREDMEMORY_DEBUG + qDebug() << errorString << "key" << key; +#endif + } +} + +key_t QSharedMemoryPrivate::handle() +{ + // Not really cost effective to check here if shared memory is attachable, as it requires + // exactly the same call as attaching, so always assume handle is valid and return failure + // from attach. + return 1; +} + +bool QSharedMemoryPrivate::cleanHandle() +{ + chunk.Close(); + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + // Get a windows acceptable key + QString safeKey = makePlatformSafeKey(key); + QString function = QLatin1String("QSharedMemory::create"); + if (safeKey.isEmpty()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: key error").arg(function); + return false; + } + + TPtrC ptr(qt_QString2TPtrC(safeKey)); + + TInt err = chunk.CreateGlobal(ptr, size, size); + + setErrorString(function, err); + + if (err != KErrNone) + return false; + + // Zero out the created chunk + Mem::FillZ(chunk.Base(), chunk.Size()); + + return true; +} + +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()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: unable to make key").arg(function); + return false; + } + + TPtrC ptr(qt_QString2TPtrC(safeKey)); + + TInt err = KErrNoMemory; + + err = chunk.OpenGlobal(ptr, false); + + if (err != KErrNone) { + setErrorString(function, err); + return false; + } + } + + size = chunk.Size(); + memory = chunk.Base(); + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + chunk.Close(); + + memory = 0; + size = 0; + + return true; +} + +#endif //QT_NO_SHAREDMEMORY + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsystemsemaphore.cpp b/src/corelib/kernel/qsystemsemaphore.cpp index 95dc8a0..f885662 100644 --- a/src/corelib/kernel/qsystemsemaphore.cpp +++ b/src/corelib/kernel/qsystemsemaphore.cpp @@ -123,6 +123,11 @@ QT_BEGIN_NAMESPACE operations that were not released. Thus if the process acquires a resource and then exits without releasing it, Unix will release that resource. + + \o Symbian: QSystemSemaphore behaves the same as Windows semaphores. + In other words, the operating system owns the semaphore and ignores + QSystemSemaphore::AccessMode. + \endlist \sa QSharedMemory, QSemaphore @@ -147,7 +152,7 @@ QT_BEGIN_NAMESPACE creates a new semaphore for that key and sets its resource count to \a initialValue. - In Windows, \a mode is ignored, and the system always tries to + In Windows and in Symbian, \a mode is ignored, and the system always tries to create a semaphore for the specified \a key. If the system does not already have a semaphore identified as \a key, it creates the semaphore and sets its resource count to \a initialValue. But if the @@ -165,8 +170,8 @@ QT_BEGIN_NAMESPACE \sa acquire(), key() */ QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode) + : d(new QSystemSemaphorePrivate) { - d = new QSystemSemaphorePrivate; setKey(key, initialValue, mode); } @@ -188,7 +193,6 @@ QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessM QSystemSemaphore::~QSystemSemaphore() { d->cleanHandle(); - delete d; } /*! @@ -198,7 +202,7 @@ QSystemSemaphore::~QSystemSemaphore() enable handling the problem in Unix implementations of semaphores that survive a crash. In Unix, when a semaphore survives a crash, we need a way to force it to reset its resource count, when the system - reuses the semaphore. In Windows, where semaphores can't survive a + reuses the semaphore. In Windows and in Symbian, where semaphores can't survive a crash, this enum has no effect. \value Open If the semaphore already exists, its initial resource @@ -211,7 +215,7 @@ QSystemSemaphore::~QSystemSemaphore() This value should be passed to the constructor, when the first semaphore for a particular key is constructed and you know that if the semaphore already exists it could only be because of a crash. In - Windows, where a semaphore can't survive a crash, Create and Open + Windows and in Symbian, where a semaphore can't survive a crash, Create and Open have the same behavior. */ @@ -231,7 +235,7 @@ void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode m return; d->error = NoError; d->errorString = QString(); -#ifndef Q_OS_WIN +#if !defined(Q_OS_WIN) && !defined(Q_OS_SYMBIAN) // optimization to not destroy/create the file & semaphore if (key == d->key && mode == Create && d->createdSemaphore && d->createdFile) { d->initialValue = initialValue; diff --git a/src/corelib/kernel/qsystemsemaphore.h b/src/corelib/kernel/qsystemsemaphore.h index c9e56cc..c1f1115 100644 --- a/src/corelib/kernel/qsystemsemaphore.h +++ b/src/corelib/kernel/qsystemsemaphore.h @@ -43,6 +43,7 @@ #define QSYSTEMSEMAPHORE_H #include <QtCore/qstring.h> +#include <QtCore/qscopedpointer.h> QT_BEGIN_HEADER @@ -89,7 +90,7 @@ public: private: Q_DISABLE_COPY(QSystemSemaphore) - QSystemSemaphorePrivate *d; + QScopedPointer<QSystemSemaphorePrivate> d; }; #endif // QT_NO_SYSTEMSEMAPHORE diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h index b3b2006..548e754 100644 --- a/src/corelib/kernel/qsystemsemaphore_p.h +++ b/src/corelib/kernel/qsystemsemaphore_p.h @@ -62,6 +62,10 @@ # include <sys/types.h> #endif +#ifdef Q_OS_SYMBIAN +class RSemaphore; +#endif + QT_BEGIN_NAMESPACE class QSystemSemaphorePrivate @@ -77,10 +81,14 @@ public: #ifdef Q_OS_WIN HANDLE handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function); +#elif defined(Q_OS_SYMBIAN) + int handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function,int err = 0); #else key_t handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); -#endif void setErrorString(const QString &function); +#endif void cleanHandle(); bool modifySemaphore(int count); @@ -90,13 +98,14 @@ public: #ifdef Q_OS_WIN HANDLE semaphore; HANDLE semaphoreLock; +#elif defined(Q_OS_SYMBIAN) + RSemaphore semaphore; #else int semaphore; bool createdFile; bool createdSemaphore; key_t unix_key; #endif - QString errorString; QSystemSemaphore::SystemSemaphoreError error; }; diff --git a/src/corelib/kernel/qsystemsemaphore_symbian.cpp b/src/corelib/kernel/qsystemsemaphore_symbian.cpp new file mode 100644 index 0000000..a14db7e --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_symbian.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" +#include "qcoreapplication.h" +#include <qdebug.h> + +#include <qcore_symbian_p.h> +#include <e32cmn.h> +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SYSTEMSEMAPHORE + +QSystemSemaphorePrivate::QSystemSemaphorePrivate() : + error(QSystemSemaphore::NoError) +{ +} + +void QSystemSemaphorePrivate::setErrorString(const QString &function, int err) +{ + if (err == KErrNone){ + return; + } + switch(err){ + case KErrAlreadyExists: + errorString = QCoreApplication::tr("%1: already exists", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::AlreadyExists; + break; + case KErrNotFound: + errorString = QCoreApplication::tr("%1: doesn't exists", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::NotFound; + break; + case KErrNoMemory: + case KErrInUse: + errorString = QCoreApplication::tr("%1: out of resources", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::OutOfResources; + break; +default: + errorString = QCoreApplication::tr("%1: unknown error %2", "QSystemSemaphore").arg(function).arg(err); + error = QSystemSemaphore::UnknownError; + } + +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << errorString << "key" << key; +#endif +} + +int QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode) +{ + // don't allow making handles on empty keys + if (key.isEmpty()) + return 0; + QString safeName = makeKeyFileName(); + TPtrC name(qt_QString2TPtrC(safeName)); + int err; + err = semaphore.OpenGlobal(name,EOwnerProcess); + if (err == KErrNotFound){ + err = semaphore.CreateGlobal(name,initialValue, EOwnerProcess); + } + if (err){ + setErrorString(QLatin1String("QSystemSemaphore::handle"),err); + return 0; + } + return semaphore.Handle(); +} + +void QSystemSemaphorePrivate::cleanHandle() +{ + semaphore.Close(); +} + +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (0 == handle()) + return false; + + if (count > 0) { + semaphore.Signal(count); + } else { + semaphore.Wait(); + } + return true; +} + +#endif //QT_NO_SYSTEMSEMAPHORE + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index dfed4db..54b4f67 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -156,7 +156,16 @@ static void construct(QVariant::Private *x, const void *copy) x->data.b = copy ? *static_cast<const bool *>(copy) : false; break; case QVariant::Double: +#if defined(Q_CC_RVCT) + // Using trinary operator with 64bit constants crashes when ran on Symbian device + if (copy){ + x->data.d = *static_cast<const double*>(copy); + } else { + x->data.d = 0.0; + } +#else x->data.d = copy ? *static_cast<const double*>(copy) : 0.0; +#endif break; case QMetaType::Float: x->data.f = copy ? *static_cast<const float*>(copy) : 0.0f; @@ -165,10 +174,28 @@ static void construct(QVariant::Private *x, const void *copy) x->data.o = copy ? *static_cast<QObject *const*>(copy) : 0; break; case QVariant::LongLong: +#if defined(Q_CC_RVCT) + // Using trinary operator with 64bit constants crashes when ran on Symbian device + if (copy){ + x->data.ll = *static_cast<const qlonglong *>(copy); + } else { + x->data.ll = Q_INT64_C(0); + } +#else x->data.ll = copy ? *static_cast<const qlonglong *>(copy) : Q_INT64_C(0); +#endif break; case QVariant::ULongLong: +#if defined(Q_CC_RVCT) + // Using trinary operator with 64bit constants crashes when ran on Symbian device + if (copy){ + x->data.ull = *static_cast<const qulonglong *>(copy); + } else { + x->data.ull = Q_UINT64_C(0); + } +#else x->data.ull = copy ? *static_cast<const qulonglong *>(copy) : Q_UINT64_C(0); +#endif break; case QVariant::Invalid: case QVariant::UserType: @@ -598,7 +625,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, ok = &dummy; switch (uint(t)) { - case QVariant::Url: + case QVariant::Url: switch (d->type) { case QVariant::String: *static_cast<QUrl *>(result) = QUrl(*v_cast<QString>(d)); @@ -1197,8 +1224,8 @@ const QVariant::Handler *QVariant::handler = &qt_kernel_variant_handler; and versatile, but may prove less memory and speed efficient than storing specific types in standard data structures. - QVariant also supports the notion of null values, where you can - have a defined type with no value set. However, note that QVariant + QVariant also supports the notion of null values, where you can + have a defined type with no value set. However, note that QVariant types can only be cast when they have had a value set. \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 1 diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 487ce3a..69dfa13 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -64,6 +64,7 @@ class QFactoryLoaderPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QFactoryLoader) public: QFactoryLoaderPrivate(){} + ~QFactoryLoaderPrivate(); mutable QMutex mutex; QByteArray iid; QList<QLibraryPrivate*> libraryList; @@ -76,6 +77,12 @@ public: void unloadPath(const QString &path); }; +QFactoryLoaderPrivate::~QFactoryLoaderPrivate() +{ + for (int i = 0; i < libraryList.count(); ++i) + libraryList.at(i)->release(); +} + QFactoryLoader::QFactoryLoader(const char *iid, const QString &suffix, Qt::CaseSensitivity cs) @@ -89,8 +96,8 @@ QFactoryLoader::QFactoryLoader(const char *iid, QMutexLocker locker(qt_factoryloader_mutex()); - qt_factory_loaders()->append(this); update(); + qt_factory_loaders()->append(this); } @@ -197,10 +204,6 @@ void QFactoryLoader::update() QFactoryLoader::~QFactoryLoader() { - Q_D(QFactoryLoader); - for (int i = 0; i < d->libraryList.count(); ++i) - d->libraryList.at(i)->release(); - QMutexLocker locker(qt_factoryloader_mutex()); qt_factory_loaders()->removeAll(this); } diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 116d617..5d2ed13 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -98,10 +98,10 @@ Q_GLOBAL_STATIC(QMutex, qt_library_mutex) Unix), unless the file name has an absolute path. If the file cannot be found, QLibrary tries the name with different platform-specific file suffixes, like ".so" on Unix, ".dylib" on - the Mac, or ".dll" on Windows. This makes it possible to specify - shared libraries that are only identified by their basename (i.e. - without their suffix), so the same code will work on different - operating systems. + the Mac, or ".dll" on Windows and Symbian. This makes it possible + to specify shared libraries that are only identified by their + basename (i.e. without their suffix), so the same code will work + on different operating systems. The most important functions are load() to dynamically load the library file, isLoaded() to check whether loading was successful, @@ -120,6 +120,11 @@ Q_GLOBAL_STATIC(QMutex, qt_library_mutex) linking", which is done by the link step in the build process when linking an executable against a library. + Note: In Symbian resolving symbols using their names is supported + only if the library is built as STDDLL. Otherwise ordinals must + be used. Also, in Symbian the path of the library is ignored and + system default library location is always used. + The following code snippet loads a library, resolves the symbol "mysymbol", and calls the function if everything succeeded. If something goes wrong, e.g. the library file does not exist or the @@ -288,7 +293,7 @@ static bool qt_parse_pattern(const char *s, uint *version, bool *debug, QByteArr } #endif // QT_NO_PLUGIN_CHECK -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_PLUGIN_CHECK) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_PLUGIN_CHECK) #if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) # define USE_MMAP @@ -361,7 +366,7 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB char *filedata = 0; ulong fdlen = 0; -#ifdef USE_MMAP +# ifdef USE_MMAP char *mapaddr = 0; size_t maplen = file.size(); mapaddr = (char *) mmap(mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0); @@ -378,14 +383,14 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB lib->errorString = QLibrary::tr("Could not mmap '%1': %2") .arg(library) .arg(qt_error_string()); -#endif // USE_MMAP +# endif // USE_MMAP // try reading the data into memory instead data = file.readAll(); filedata = data.data(); fdlen = data.size(); -#ifdef USE_MMAP +# ifdef USE_MMAP } -#endif // USE_MMAP +# endif // USE_MMAP // verify that the pattern is present in the plugin const char pattern[] = "pattern=QT_PLUGIN_VERIFICATION_DATA"; @@ -398,7 +403,7 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB if (!ret && lib) lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library); -#ifdef USE_MMAP +# ifdef USE_MMAP if (mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0) { if (qt_debug_component()) qWarning("munmap: %s", qPrintable(qt_error_string(errno))); @@ -407,13 +412,13 @@ static bool qt_unix_query(const QString &library, uint *version, bool *debug, QB .arg(library) .arg( qt_error_string() ); } -#endif // USE_MMAP +# endif // USE_MMAP file.close(); return ret; } -#endif // Q_OS_UNIX && !Q_OS_MAC && !defined(QT_NO_PLUGIN_CHECK) +#endif // Q_OS_UNIX && !Q_OS_MAC && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_PLUGIN_CHECK) typedef QMap<QString, QLibraryPrivate*> LibraryMap; Q_GLOBAL_STATIC(LibraryMap, libraryMap) @@ -493,6 +498,11 @@ bool QLibraryPrivate::loadPlugin() } if (load()) { instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); +#if defined(Q_OS_SYMBIAN) + // If resolving with function name failed (i.e. not STDDLL), try resolving using known ordinal + if (!instance) + instance = (QtPluginInstanceFunction)resolve("2"); +#endif return instance; } return false; @@ -517,6 +527,10 @@ bool QLibrary::isLibrary(const QString &fileName) { #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) return fileName.endsWith(QLatin1String(".dll")); +#elif defined(Q_OS_SYMBIAN) + // Plugin stubs are also considered libraries in Symbian. + return (fileName.endsWith(QLatin1String(".dll")) || + fileName.endsWith(QLatin1String(".qtplugin"))); #else QString completeSuffix = QFileInfo(fileName).completeSuffix(); if (completeSuffix.isEmpty()) @@ -597,10 +611,10 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) .arg(fileName); QStringList reg; #ifndef QT_NO_SETTINGS - bool madeSettings = false; + QScopedPointer<QSettings> madeSettings; if (!settings) { settings = new QSettings(QSettings::UserScope, QLatin1String("Trolltech")); - madeSettings = true; + madeSettings.reset(settings); } reg = settings->value(regkey).toStringList(); #endif @@ -610,7 +624,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) key = reg.at(2).toLatin1(); success = qt_version != 0; } else { -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN) if (!pHnd) { // use unix shortcut to avoid loading the library success = qt_unix_query(fileName, &qt_version, &debug, &key, this); @@ -625,6 +639,10 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) #ifdef Q_OS_WIN hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, DONT_RESOLVE_DLL_REFERENCES); #else +# if defined(Q_OS_SYMBIAN) + //Guard against accidentally trying to load non-plugin libraries by making sure the stub exists + if (fileinfo.exists()) +# endif temporary_load = load_sys(); #endif } @@ -643,8 +661,17 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) #endif : (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); #else - QtPluginQueryVerificationDataFunction qtPluginQueryVerificationDataFunction = - (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); + 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"); + } +# else + qtPluginQueryVerificationDataFunction = (QtPluginQueryVerificationDataFunction) resolve("qt_plugin_query_verification_data"); +# endif #endif if (!qtPluginQueryVerificationDataFunction @@ -680,8 +707,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) #endif } #ifndef QT_NO_SETTINGS - if (madeSettings) - delete settings; + madeSettings.reset(); #endif if (!success) { @@ -818,6 +844,8 @@ QLibrary::QLibrary(QObject *parent) QLibrary will automatically look for the file with the appropriate suffix in accordance with the platform, e.g. ".so" on Unix, ".dylib" on Mac OS X, and ".dll" on Windows. (See \l{fileName}.) + + Note: In Symbian the path portion of the \a fileName is ignored. */ QLibrary::QLibrary(const QString& fileName, QObject *parent) :QObject(parent), d(0), did_load(false) @@ -829,13 +857,15 @@ QLibrary::QLibrary(const QString& fileName, QObject *parent) /*! Constructs a library object with the given \a parent that will load the library specified by \a fileName and major version number \a verNum. - Currently, the version number is ignored on Windows. + Currently, the version number is ignored on Windows and Symbian. We recommend omitting the file's suffix in \a fileName, since QLibrary will automatically look for the file with the appropriate suffix in accordance with the platform, e.g. ".so" on Unix, ".dylib" on Mac OS X, and ".dll" on Windows. (See \l{fileName}.) - */ + + Note: In Symbian the path portion of the \a fileName is ignored. +*/ QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent) :QObject(parent), d(0), did_load(false) { @@ -845,12 +875,14 @@ QLibrary::QLibrary(const QString& fileName, int verNum, QObject *parent) /*! Constructs a library object with the given \a parent that will load the library specified by \a fileName and full version number \a version. - Currently, the version number is ignored on Windows. + Currently, the version number is ignored on Windows and Symbian. We recommend omitting the file's suffix in \a fileName, since QLibrary will automatically look for the file with the appropriate suffix in accordance with the platform, e.g. ".so" on Unix, ".dylib" on Mac OS X, and ".dll" on Windows. (See \l{fileName}.) + + Note: In Symbian the path portion of the \a fileName is ignored. */ QLibrary::QLibrary(const QString& fileName, const QString &version, QObject *parent) :QObject(parent), d(0), did_load(false) @@ -892,6 +924,8 @@ QLibrary::~QLibrary() platforms, fileName() will return "libGL.so". If the file name was originally passed as "/usr/lib/libGL", fileName() will return "/usr/lib/libGL.so". + + Note: In Symbian the path portion of the \a fileName is ignored. */ void QLibrary::setFileName(const QString &fileName) @@ -919,7 +953,10 @@ QString QLibrary::fileName() const Sets the fileName property and major version number to \a fileName and \a versionNumber respectively. - The \a versionNumber is ignored on Windows. + The \a versionNumber is ignored on Windows and Symbian. + + Note: In Symbian the path portion of the \a fileName is ignored. + \sa setFileName() */ void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum) @@ -940,7 +977,10 @@ void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum) Sets the fileName property and full version number to \a fileName and \a version respectively. - The \a version parameter is ignored on Windows. + The \a version parameter is ignored on Windows and Symbian. + + Note: In Symbian the path portion of the \a fileName is ignored. + \sa setFileName() */ void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version) @@ -976,6 +1016,8 @@ void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &ver \snippet doc/src/snippets/code/src_corelib_plugin_qlibrary.cpp 4 + Note: In Symbian resolving with symbol names works only if the loaded + library was built as STDDLL. Otherwise, the ordinals must be used. */ void *QLibrary::resolve(const char *symbol) { @@ -995,6 +1037,9 @@ void *QLibrary::resolve(const char *symbol) The function returns 0 if the symbol could not be resolved or if the library could not be loaded. + Note: In Symbian resolving with symbol names works only if the loaded + library was built as STDDLL. Otherwise, the ordinals must be used. + \sa resolve() */ void *QLibrary::resolve(const QString &fileName, const char *symbol) @@ -1015,6 +1060,9 @@ void *QLibrary::resolve(const QString &fileName, const char *symbol) The function returns 0 if the symbol could not be resolved or if the library could not be loaded. + Note: In Symbian resolving with symbol names works only if the loaded + library was built as STDDLL. Otherwise, the ordinals must be used. + \sa resolve() */ void *QLibrary::resolve(const QString &fileName, int verNum, const char *symbol) @@ -1036,6 +1084,9 @@ void *QLibrary::resolve(const QString &fileName, int verNum, const char *symbol) The function returns 0 if the symbol could not be resolved or if the library could not be loaded. + Note: In Symbian resolving with symbol names works only if the loaded + library was built as STDDLL. Otherwise, the ordinals must be used. + \sa resolve() */ void *QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol) diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 35c1073..62e6464 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -81,18 +81,31 @@ bool QLibraryPrivate::load_sys() QString attempt; #if !defined(Q_OS_VXWORKS) QFileInfo fi(fileName); + +#if defined(Q_OS_SYMBIAN) + QString path; // In Symbian, always resolve with just the filename + QString name; + + // Replace possible ".qtplugin" suffix with ".dll" + if (fi.suffix() == QLatin1String("qtplugin")) + name = fi.completeBaseName() + QLatin1String(".dll"); + else + name = fi.fileName(); +#else QString path = fi.path(); QString name = fi.fileName(); if (path == QLatin1String(".") && !fileName.startsWith(path)) path.clear(); else path += QLatin1Char('/'); - +#endif // The first filename we want to attempt to load is the filename as the callee specified. // Thus, the first attempt we do must be with an empty prefix and empty suffix. QStringList suffixes(QLatin1String("")), prefixes(QLatin1String("")); if (pluginState != IsAPlugin) { +#if !defined(Q_OS_SYMBIAN) prefixes << QLatin1String("lib"); +#endif #if defined(Q_OS_HPUX) // according to // http://docs.hp.com/en/B2355-90968/linkerdifferencesiapa.htm @@ -120,6 +133,9 @@ bool QLibraryPrivate::load_sys() } #elif defined(Q_OS_AIX) suffixes << ".a"; + +#elif defined(Q_OS_SYMBIAN) + suffixes << QLatin1String(".dll"); #else if (!fullVersion.isEmpty()) { suffixes << QString::fromLatin1(".so.%1").arg(fullVersion); @@ -157,7 +173,7 @@ bool QLibraryPrivate::load_sys() else { #if defined(Q_OS_MAC) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) -#endif +#endif dlFlags |= RTLD_LOCAL; } #endif @@ -188,6 +204,12 @@ bool QLibraryPrivate::load_sys() #else pHnd = dlopen(QFile::encodeName(attempt), dlFlags); #endif + +#if defined(Q_OS_SYMBIAN) + // Never try again in symbian, dlopen already handles the library search logic, + // and there is only one possible suffix. + retry = false; +#else if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) { // We only want to continue if dlopen failed due to that the shared library did not exist. // However, we are only able to apply this check for absolute filenames (since they are @@ -195,6 +217,7 @@ bool QLibraryPrivate::load_sys() // This is all because dlerror is flawed and cannot tell us the reason why it failed. retry = false; } +#endif } } diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index 2d9e20e..33972f2 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -46,6 +46,7 @@ #include <qfileinfo.h> #include "qlibrary_p.h" #include "qdebug.h" +#include "qdir.h" #ifndef QT_NO_LIBRARY @@ -116,6 +117,16 @@ QT_BEGIN_NAMESPACE link to plugins statically. You can use QLibrary if you need to load dynamic libraries in a statically linked application. + \note In Symbian the plugin stub files must be used whenever a + path to plugin is needed. For the purposes of loading plugins, + the stubs can be considered to have the same name as the actual + plugin binary. In practice they have ".qtplugin" extension + instead of ".dll", but this difference is handled transparently + by QPluginLoader and QLibrary to avoid need for Symbian specific + plugin handling in most Qt applications. Plugin stubs are needed + because Symbian Platform Security denies all access to the directory + where the actual plugin binaries are located. + \sa QLibrary, {Plug & Paint Example} */ @@ -136,6 +147,8 @@ QPluginLoader::QPluginLoader(QObject *parent) Unix, - \c .dylib on Mac OS X, and \c .dll on Windows. The suffix can be verified with QLibrary::isLibrary(). + Note: In Symbian the \a fileName must point to plugin stub file. + \sa setFileName() */ QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent) @@ -170,7 +183,7 @@ QPluginLoader::~QPluginLoader() The root component, returned by this function, is not deleted when the QPluginLoader is destroyed. If you want to ensure that the root component is deleted, you should call unload() as soon you don't - need to access the core component anymore. When the library is + need to access the core component anymore. When the library is finally unloaded, the root component will automatically be deleted. The component object is a QObject. Use qobject_cast() to access @@ -221,9 +234,9 @@ bool QPluginLoader::load() call will fail, and unloading will only happen when every instance has called unload(). - Don't try to delete the root component. Instead rely on + Don't try to delete the root component. Instead rely on that unload() will automatically delete it when needed. - + \sa instance(), load() */ bool QPluginLoader::unload() @@ -261,6 +274,8 @@ bool QPluginLoader::isLoaded() const By default, this property contains an empty string. + Note: In Symbian the \a fileName must point to plugin stub file. + \sa load() */ void QPluginLoader::setFileName(const QString &fileName) @@ -273,7 +288,42 @@ void QPluginLoader::setFileName(const QString &fileName) d = 0; did_load = false; } + +#if defined(Q_OS_SYMBIAN) + // In Symbian we actually look for plugin stub, so modify the filename + // to make canonicalFilePath find the file, if .dll is specified. + QFileInfo fi(fileName); + + if (fi.suffix() == QLatin1String("dll")) { + QString stubName = fileName; + stubName.chop(3); + stubName += QLatin1String("qtplugin"); + fi = QFileInfo(stubName); + } + + QString fn = fi.canonicalFilePath(); + // If not found directly, check also all the available drives + if (!fn.length()) { + QString stubPath(fi.fileName().length() ? fi.absoluteFilePath() : QString()); + if (stubPath.length() > 1) { + if (stubPath.at(1).toAscii() == ':') + stubPath.remove(0,2); + QFileInfoList driveList(QDir::drives()); + foreach(const QFileInfo& drive, driveList) { + QString testFilePath(drive.absolutePath() + stubPath); + testFilePath = QDir::cleanPath(testFilePath); + if (QFile::exists(testFilePath)) { + fn = testFilePath; + break; + } + } + } + } + +#else QString fn = QFileInfo(fileName).canonicalFilePath(); +#endif + d = QLibraryPrivate::findOrCreate(fn); d->loadHints = lh; if (fn.isEmpty()) diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index fd5bc63..3d11f53 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -558,7 +558,7 @@ bool QUuid::operator>(const QUuid &other) const \sa variant(), version() */ -#if defined(Q_OS_WIN32) +#if defined(Q_OS_WIN32) && ! defined(Q_CC_MWERKS) QT_BEGIN_INCLUDE_NAMESPACE #include <objbase.h> // For CoCreateGuid diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 1163aa4..d6946de 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -2065,7 +2065,7 @@ int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a) switch (_id) { case 0: { // ### in Qt 4.6 we can use QObject::senderSignalIndex() - QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr); + QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr.data()); int signalIndex = -1; QObject *sender = this->sender(); if (sender && d->currentSender) diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 53ac3c7..80954c7 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -129,35 +129,6 @@ void QThreadData::deref() #endif } - -#ifndef QT_NO_THREAD -/* - QThreadPrivate -*/ - -QThreadPrivate::QThreadPrivate(QThreadData *d) - : QObjectPrivate(), running(false), finished(false), terminated(false), - stackSize(0), priority(QThread::InheritPriority), data(d) -{ -#if defined (Q_OS_UNIX) - thread_id = 0; -#elif defined (Q_WS_WIN) - handle = 0; - id = 0; - waiters = 0; - terminationEnabled = true; - terminatePending = false; -#endif - - if (!data) - data = new QThreadData; -} - -QThreadPrivate::~QThreadPrivate() -{ - data->deref(); -} - /* QAdoptedThread */ @@ -167,25 +138,28 @@ QAdoptedThread::QAdoptedThread(QThreadData *data) { // thread should be running and not finished for the lifetime // of the application (even if QCoreApplication goes away) +#ifndef QT_NO_THREAD d_func()->running = true; d_func()->finished = false; init(); +#endif // fprintf(stderr, "new QAdoptedThread = %p\n", this); } QAdoptedThread::~QAdoptedThread() { +#ifndef QT_NO_THREAD QThreadPrivate::finish(this); - +#endif // fprintf(stderr, "~QAdoptedThread = %p\n", this); } QThread *QAdoptedThread::createThreadForAdoption() { - QThread *t = new QAdoptedThread(0); - t->moveToThread(t); - return t; + QScopedPointer<QThread> t(new QAdoptedThread(0)); + t->moveToThread(t.data()); + return t.take(); } void QAdoptedThread::run() @@ -193,6 +167,35 @@ void QAdoptedThread::run() // this function should never be called qFatal("QAdoptedThread::run(): Internal error, this implementation should never be called."); } +#ifndef QT_NO_THREAD +/* + QThreadPrivate +*/ + +QThreadPrivate::QThreadPrivate(QThreadData *d) + : QObjectPrivate(), running(false), finished(false), terminated(false), + stackSize(0), priority(QThread::InheritPriority), data(d) +{ +#if defined (Q_OS_UNIX) + thread_id = 0; +#elif defined (Q_WS_WIN) + handle = 0; + id = 0; + waiters = 0; +#endif +#if defined (Q_WS_WIN) || defined (Q_OS_SYMBIAN) + terminationEnabled = true; + terminatePending = false; +#endif + + if (!data) + data = new QThreadData; +} + +QThreadPrivate::~QThreadPrivate() +{ + data->deref(); +} /*! \class QThread @@ -479,10 +482,10 @@ uint QThread::stackSize() const int QThread::exec() { Q_D(QThread); - d->mutex.lock(); + QMutexLocker locker(&d->mutex); d->data->quitNow = false; QEventLoop eventLoop; - d->mutex.unlock(); + locker.unlock(); int returnCode = eventLoop.exec(); return returnCode; } @@ -715,25 +718,37 @@ QThread::Priority QThread::priority() const #else // QT_NO_THREAD -QT_BEGIN_INCLUDE_NAMESPACE -#include <private/qcoreapplication_p.h> -QT_END_INCLUDE_NAMESPACE - -Q_GLOBAL_STATIC_WITH_ARGS(QThreadData, staticThreadData, (0)); -QThread* QThread::instance = 0; - -QThread::QThread() : QObject(*new QThreadPrivate, (QObject*)0) -{ +QThread::QThread(QObject *parent) + : QObject(*(new QThreadPrivate), (QObject*)0){ Q_D(QThread); d->data->thread = this; - QCoreApplicationPrivate::theMainThread = this; +} + +QThread *QThread::currentThread() +{ + return QThreadData::current()->thread; } QThreadData* QThreadData::current() { - if (QThread::instance) - return QThread::instance->d_func()->data; - return staticThreadData(); + static QThreadData *data = 0; // reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key)); + if (!data) { + QScopedPointer<QThreadData> newdata(new QThreadData); + newdata->thread = new QAdoptedThread(newdata.data()); + data = newdata.take(); + data->deref(); + } + return data; +} + +/*! \internal + */ +QThread::QThread(QThreadPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + Q_D(QThread); + // fprintf(stderr, "QThreadData %p taken from private data for thread %p\n", d->data, this); + d->data->thread = this; } #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 8b26251..9707d6f 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -142,15 +142,18 @@ class Q_CORE_EXPORT QThread : public QObject { public: static Qt::HANDLE currentThreadId() { return Qt::HANDLE(currentThread()); } - static QThread* currentThread() - { if (!instance) instance = new QThread(); return instance; } + static QThread* currentThread(); + +protected: + QThread(QThreadPrivate &dd, QObject *parent = 0); private: - QThread(); + explicit QThread(QObject *parent = 0); static QThread *instance; friend class QCoreApplication; friend class QThreadData; + friend class QAdoptedThread; Q_DECLARE_PRIVATE(QThread) }; diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 9bbdaa2..5445a77 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -62,6 +62,10 @@ #include "QtCore/qmap.h" #include "private/qobject_p.h" +#ifdef Q_OS_SYMBIAN +#include <e32base.h> +#endif + QT_BEGIN_NAMESPACE class QAbstractEventDispatcher; @@ -132,46 +136,37 @@ public: QWaitCondition thread_done; static void *start(void *arg); - static void finish(void *arg); +#if defined(Q_OS_SYMBIAN) + static void finish(void *arg, bool lockAnyway=true, bool closeNativeHandle=true); +#else + static void finish(void *); #endif +#endif // Q_OS_UNIX #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) HANDLE handle; unsigned int id; int waiters; - bool terminationEnabled, terminatePending; static unsigned int __stdcall start(void *); static void finish(void *, bool lockAnyway=true); #endif // Q_OS_WIN32 +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN) + bool terminationEnabled, terminatePending; +# endif QThreadData *data; static void createEventDispatcher(QThreadData *data); }; -// thread wrapper for the main() thread -class QAdoptedThread : public QThread -{ - Q_DECLARE_PRIVATE(QThread) - -public: - QAdoptedThread(QThreadData *data = 0); - ~QAdoptedThread(); - void init(); - - static QThread *createThreadForAdoption(); -private: - void run(); -}; - #else // QT_NO_THREAD class QThreadPrivate : public QObjectPrivate { public: - QThreadPrivate() : data(QThreadData::current()) {} - ~QThreadPrivate() { } + QThreadPrivate(QThreadData *d = 0) : data(d ? d : new QThreadData) {} + ~QThreadPrivate() { delete data; } QThreadData *data; @@ -210,6 +205,25 @@ public: QMap<int, void *> tls; QMutex mutex; + +# ifdef Q_OS_SYMBIAN + RThread symbian_thread_handle; +# endif +}; + +// thread wrapper for the main() thread +class QAdoptedThread : public QThread +{ + Q_DECLARE_PRIVATE(QThread) + +public: + QAdoptedThread(QThreadData *data = 0); + ~QAdoptedThread(); + void init(); + + static QThread *createThreadForAdoption(); +private: + void run(); }; QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 6308f2c..b0b285e 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -47,7 +47,12 @@ #if !defined(QT_NO_GLIB) # include "../kernel/qeventdispatcher_glib_p.h" #endif + +#ifdef Q_OS_SYMBIAN +#include <private/qeventdispatcher_symbian_p.h> +#else #include <private/qeventdispatcher_unix_p.h> +#endif #include "qthreadstorage.h" @@ -138,7 +143,14 @@ QThreadData *QThreadData::current() } else { data = new QThreadData; pthread_setspecific(current_thread_data_key, data); - data->thread = new QAdoptedThread(data); + QT_TRY { + data->thread = new QAdoptedThread(data); + } QT_CATCH(...) { + pthread_setspecific(current_thread_data_key, 0); + data->deref(); + data = 0; + QT_RETHROW; + } data->deref(); } if (!QCoreApplicationPrivate::theMainThread) @@ -150,7 +162,13 @@ QThreadData *QThreadData::current() void QAdoptedThread::init() { - d_func()->thread_id = pthread_self(); + Q_D(QThread); + d->thread_id = pthread_self(); +#ifdef Q_OS_SYMBIAN + d->data->symbian_thread_handle = RThread(); + TThreadId threadId = d->data->symbian_thread_handle.Id(); + d->data->symbian_thread_handle.Open(threadId); +#endif } /* @@ -178,7 +196,11 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) data->eventDispatcher = new QEventDispatcherGlib; else #endif +#ifdef Q_OS_SYMBIAN + data->eventDispatcher = new QEventDispatcherSymbian; +#else data->eventDispatcher = new QEventDispatcherUNIX; +#endif data->eventDispatcher->startingUp(); } @@ -186,12 +208,24 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) void *QThreadPrivate::start(void *arg) { +#ifndef Q_OS_SYMBIAN pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_push(QThreadPrivate::finish, arg); +#endif QThread *thr = reinterpret_cast<QThread *>(arg); QThreadData *data = QThreadData::get2(thr); +#ifdef Q_OS_SYMBIAN + // Because Symbian Open C does not provide a way to convert between + // RThread and pthread_t, we must delay initialization of the RThread + // handle when creating a thread, until we are running in the new thread. + // Here, we pick up the current thread and assign that to the handle. + data->symbian_thread_handle = RThread(); + TThreadId threadId = data->symbian_thread_handle.Id(); + data->symbian_thread_handle.Open(threadId); +#endif + pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_setspecific(current_thread_data_key, data); @@ -202,19 +236,33 @@ void *QThreadPrivate::start(void *arg) createEventDispatcher(data); emit thr->started(); +#ifndef Q_OS_SYMBIAN pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); +#endif thr->run(); +#ifndef Q_OS_SYMBIAN pthread_cleanup_pop(1); +#else + QThreadPrivate::finish(arg); +#endif + return 0; } +#ifdef Q_OS_SYMBIAN +void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle) +#else void QThreadPrivate::finish(void *arg) +#endif { QThread *thr = reinterpret_cast<QThread *>(arg); QThreadPrivate *d = thr->d_func(); - QMutexLocker locker(&d->mutex); +#ifdef Q_OS_SYMBIAN + if (lockAnyway) +#endif + d->mutex.lock(); d->priority = QThread::InheritPriority; d->running = false; @@ -235,7 +283,15 @@ void QThreadPrivate::finish(void *arg) QThreadStorageData::finish((void **)data); d->thread_id = 0; +#ifdef Q_OS_SYMBIAN + if (closeNativeHandle) + d->data->symbian_thread_handle.Close(); +#endif d->thread_done.wakeAll(); +#ifdef Q_OS_SYMBIAN + if (lockAnyway) +#endif + d->mutex.unlock(); } @@ -288,6 +344,9 @@ int QThread::idealThreadCount() #elif defined(Q_OS_INTEGRITY) // as of aug 2008 Integrity only supports one single core CPU cores = 1; +#elif defined(Q_OS_SYMBIAN) + // ### TODO - Get the number of cores from HAL? when multicore architectures (SMP) are supported + cores = 1; #elif defined(Q_OS_VXWORKS) // VxWorks # if defined(QT_VXWORKS_HAS_CPUSET) @@ -389,7 +448,8 @@ void QThread::start(Priority priority) d->priority = priority; -#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) +#if defined(Q_OS_DARWIN) || !defined(Q_OS_OPENBSD) && !defined(Q_OS_SYMBIAN) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0) +// ### Need to implement thread sheduling and priorities for symbian os. Implementation removed for now switch (priority) { case InheritPriority: { @@ -447,6 +507,13 @@ void QThread::start(Priority priority) } #endif // _POSIX_THREAD_PRIORITY_SCHEDULING +#ifdef Q_OS_SYMBIAN + if (d->stackSize == 0) + // The default stack size on Symbian is very small, making even basic + // operations like file I/O fail, so we increase it by default. + d->stackSize = 0x14000; // Maximum stack size on Symbian. +#endif + if (d->stackSize > 0) { #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0) int code = pthread_attr_setstacksize(&attr, d->stackSize); @@ -471,7 +538,9 @@ void QThread::start(Priority priority) if (code == EPERM) { // caller does not have permission to set the scheduling // parameters/policy +#ifndef Q_OS_SYMBIAN pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); +#endif code = pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this); } @@ -484,6 +553,9 @@ void QThread::start(Priority priority) d->running = false; d->finished = false; d->thread_id = 0; +#ifdef Q_OS_SYMBIAN + d->data->symbian_thread_handle.Close(); +#endif } } @@ -495,6 +567,7 @@ void QThread::terminate() if (!d->thread_id) return; +#ifndef Q_OS_SYMBIAN int code = pthread_cancel(d->thread_id); if (code) { qWarning("QThread::start: Thread termination error: %s", @@ -502,6 +575,21 @@ void QThread::terminate() } else { d->terminated = true; } +#else + if (!d->running) + return; + if (!d->terminationEnabled) { + d->terminatePending = true; + return; + } + + d->terminated = true; + QThreadPrivate::finish(this, false, false); + d->data->symbian_thread_handle.Terminate(KErrNone); + d->data->symbian_thread_handle.Close(); +#endif + + } bool QThread::wait(unsigned long time) @@ -526,11 +614,24 @@ bool QThread::wait(unsigned long time) void QThread::setTerminationEnabled(bool enabled) { - Q_ASSERT_X(currentThread() != 0, "QThread::setTerminationEnabled()", + QThread *thr = currentThread(); + Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()", "Current thread was not started with QThread."); +#ifndef Q_OS_SYMBIAN pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); if (enabled) pthread_testcancel(); +#else + QThreadPrivate *d = thr->d_func(); + QMutexLocker locker(&d->mutex); + d->terminationEnabled = enabled; + if (enabled && d->terminatePending) { + d->terminated = true; + QThreadPrivate::finish(thr, false); + locker.unlock(); // don't leave the mutex locked! + pthread_exit(NULL); + } +#endif } void QThread::setPriority(Priority priority) diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index 2b32b61..12ee413 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -112,7 +112,14 @@ QThreadData *QThreadData::current() // This needs to be called prior to new AdoptedThread() to // avoid recursion. TlsSetValue(qt_current_thread_data_tls_index, threadData); - threadData->thread = new QAdoptedThread(threadData); + QT_TRY { + threadData->thread = new QAdoptedThread(threadData); + } QT_CATCH(...) { + TlsSetValue(qt_current_thread_data_tls_index, 0); + threadData->deref(); + threadData = 0; + QT_RETHROW; + } threadData->deref(); } diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 25194f4..fa77fe8 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -101,12 +101,14 @@ void **QThreadStorageData::get() const qWarning("QThreadStorage::get: QThreadStorage can only be used with threads started with QThread"); return 0; } - QMap<int, void *>::iterator it = data->tls.find(id); + QMap<int, void *>::const_iterator it = data->tls.constFind(id); DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p", id, it != data->tls.end() ? it.value() : 0, data->thread); - return it != data->tls.end() && it.value() != 0 ? &it.value() : 0; + // const_cast below is a bit evil - but we have to make sure not to detach here + // otherwise we'll go bonkers in oom situations + return it != data->tls.constEnd() && it.value() != 0 ? const_cast<void **>(&it.value()) : 0; } void **QThreadStorageData::set(void *p) @@ -129,9 +131,9 @@ void **QThreadStorageData::set(void *p) void *q = it.value(); it.value() = 0; - mutex()->lock(); + QMutexLocker locker(mutex()); void (*destructor)(void *) = destructors()->value(id); - mutex()->unlock(); + locker.unlock(); destructor(q); } @@ -167,9 +169,9 @@ void QThreadStorageData::finish(void **p) continue; } - mutex()->lock(); + QMutexLocker locker(mutex()); void (*destructor)(void *) = destructors()->value(id); - mutex()->unlock(); + locker.unlock(); if (!destructor) { if (QThread::currentThread()) diff --git a/src/corelib/tools/qalgorithms.h b/src/corelib/tools/qalgorithms.h index 6f623d9..d334a03 100644 --- a/src/corelib/tools/qalgorithms.h +++ b/src/corelib/tools/qalgorithms.h @@ -142,7 +142,7 @@ inline void qCount(const Container &container, const T &value, Size &n) } -#if defined Q_CC_MSVC && _MSC_VER < 1300 +#if (defined Q_CC_MSVC && _MSC_VER < 1300) || defined(Q_CC_MWERKS) template <typename T> inline void qSwap(T &value1, T &value2) { diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 284dad2..bc5d89f 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -209,6 +209,8 @@ void QBitArray::resize(int size) uchar* c = reinterpret_cast<uchar*>(d.data()); if (size > (s << 3)) memset(c + s, 0, d.size() - s); + else if ( size % 8) + *(c+1+size/8) &= (1 << (size%8)) - 1; *c = d.size()*8 - size; } } diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 5197d9a..9600579 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -538,9 +538,13 @@ QByteArray qUncompress(const uchar* data, int nbytes) QByteArray baunzip; int res; do { - baunzip.resize(len); - res = ::uncompress((uchar*)baunzip.data(), &len, - (uchar*)data+4, nbytes-4); + QT_TRY { + baunzip.resize(len); + res = ::uncompress((uchar*)baunzip.data(), &len, + (uchar*)data+4, nbytes-4); + } QT_CATCH (const std::bad_alloc &) { + res = Z_MEM_ERROR; + } switch (res) { case Z_OK: @@ -1233,14 +1237,11 @@ QByteArray::QByteArray(const char *str) } else { int len = qstrlen(str); d = static_cast<Data *>(qMalloc(sizeof(Data)+len)); - if (!d) { - d = &shared_null; - } else { - d->ref = 0;; - d->alloc = d->size = len; - d->data = d->array; - memcpy(d->array, str, len+1); // include null terminator - } + Q_CHECK_PTR(d); + d->ref = 0;; + d->alloc = d->size = len; + d->data = d->array; + memcpy(d->array, str, len+1); // include null terminator } d->ref.ref(); } @@ -1264,15 +1265,12 @@ QByteArray::QByteArray(const char *data, int size) d = &shared_empty; } else { d = static_cast<Data *>(qMalloc(sizeof(Data) + size)); - if (!d) { - d = &shared_null; - } else { - d->ref = 0; - d->alloc = d->size = size; - d->data = d->array; - memcpy(d->array, data, size); - d->array[size] = '\0'; - } + Q_CHECK_PTR(d); + d->ref = 0; + d->alloc = d->size = size; + d->data = d->array; + memcpy(d->array, data, size); + d->array[size] = '\0'; } d->ref.ref(); } @@ -1290,15 +1288,12 @@ QByteArray::QByteArray(int size, char ch) d = &shared_null; } else { d = static_cast<Data *>(qMalloc(sizeof(Data)+size)); - if (!d) { - d = &shared_null; - } else { - d->ref = 0; - d->alloc = d->size = size; - d->data = d->array; - d->array[size] = '\0'; - memset(d->array, ch, size); - } + Q_CHECK_PTR(d); + d->ref = 0; + d->alloc = d->size = size; + d->data = d->array; + d->array[size] = '\0'; + memset(d->array, ch, size); } d->ref.ref(); } @@ -1312,6 +1307,7 @@ QByteArray::QByteArray(int size, char ch) QByteArray::QByteArray(int size, Qt::Initialization) { d = static_cast<Data *>(qMalloc(sizeof(Data)+size)); + Q_CHECK_PTR(d); d->ref = 1; d->alloc = d->size = size; d->data = d->array; @@ -1349,8 +1345,7 @@ void QByteArray::resize(int size) // QByteArray a(sz); // Data *x = static_cast<Data *>(qMalloc(sizeof(Data)+size)); - if (!x) - return; + Q_CHECK_PTR(x); x->ref = 1; x->alloc = x->size = size; x->data = x->array; @@ -1392,8 +1387,7 @@ void QByteArray::realloc(int alloc) { if (d->ref != 1 || d->data != d->array) { Data *x = static_cast<Data *>(qMalloc(sizeof(Data) + alloc)); - if (!x) - return; + Q_CHECK_PTR(x); x->size = qMin(alloc, d->size); ::memcpy(x->array, d->data, x->size); x->array[x->size] = '\0'; @@ -1405,8 +1399,7 @@ void QByteArray::realloc(int alloc) d = x; } else { Data *x = static_cast<Data *>(qRealloc(d, sizeof(Data) + alloc)); - if (!x) - return; + Q_CHECK_PTR(x); x->alloc = alloc; x->data = x->array; d = x; @@ -1827,11 +1820,13 @@ QByteArray &QByteArray::replace(const char *before, int bsize, const char *after const char *b = before; if (after >= d->data && after < d->data + d->size) { char *copy = (char *)malloc(asize); + Q_CHECK_PTR(copy); memcpy(copy, after, asize); a = copy; } if (before >= d->data && before < d->data + d->size) { char *copy = (char *)malloc(bsize); + Q_CHECK_PTR(copy); memcpy(copy, before, bsize); b = copy; } @@ -3752,6 +3747,7 @@ QByteArray QByteArray::number(double n, char f, int prec) QByteArray QByteArray::fromRawData(const char *data, int size) { Data *x = static_cast<Data *>(qMalloc(sizeof(Data))); + Q_CHECK_PTR(x); if (data) { x->data = const_cast<char *>(data); } else { diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 635c799..4c4f8fb 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -122,6 +122,17 @@ template <typename T> class QList; class Q_CORE_EXPORT QByteArray { +private: + struct Data { + QBasicAtomicInt ref; + int alloc, size; + // ### Qt 5.0: We need to add the missing capacity bit + // (like other tool classes have), to maintain the + // reserved memory on resize. + char *data; + char array[1]; + }; + public: inline QByteArray(); QByteArray(const char *); @@ -348,15 +359,6 @@ public: private: operator QNoImplicitBoolCast() const; - struct Data { - QBasicAtomicInt ref; - int alloc, size; - // ### Qt 5.0: We need to add the missing capacity bit - // (like other tool classes have), to maintain the - // reserved memory on resize. - char *data; - char array[1]; - }; static Data shared_null; static Data shared_empty; Data *d; diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index efc50ed..63fe389 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -41,6 +41,10 @@ #include <qcryptographichash.h> +#ifdef Q_OS_SYMBIAN +#define _MD5_H_ // Needed to disable system header +#endif + #include "../../3rdparty/md5/md5.h" #include "../../3rdparty/md5/md5.cpp" #include "../../3rdparty/md4/md4.h" diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 08a8385..0758afa 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -1842,7 +1842,8 @@ QTime QTime::currentTime() #else t = localtime(<ime); #endif - + Q_CHECK_PTR(t); + ct.mds = MSECS_PER_HOUR * t->tm_hour + MSECS_PER_MIN * t->tm_min + 1000 * t->tm_sec + tv.tv_usec / 1000; #else @@ -2199,8 +2200,8 @@ int QTime::elapsed() const \sa isValid() */ QDateTime::QDateTime() + : d(new QDateTimePrivate) { - d = new QDateTimePrivate; } @@ -2210,8 +2211,8 @@ QDateTime::QDateTime() */ QDateTime::QDateTime(const QDate &date) + : d(new QDateTimePrivate) { - d = new QDateTimePrivate; d->date = date; d->time = QTime(0, 0, 0); } @@ -2224,8 +2225,8 @@ QDateTime::QDateTime(const QDate &date) */ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec) + : d(new QDateTimePrivate) { - d = new QDateTimePrivate; d->date = date; d->time = date.isValid() && !time.isValid() ? QTime(0, 0, 0) : time; d->spec = (spec == Qt::UTC) ? QDateTimePrivate::UTC : QDateTimePrivate::LocalUnknown; @@ -2236,8 +2237,8 @@ QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec) */ QDateTime::QDateTime(const QDateTime &other) + : d(other.d.data()) { - d = other.d; d->ref.ref(); } @@ -2246,8 +2247,6 @@ QDateTime::QDateTime(const QDateTime &other) */ QDateTime::~QDateTime() { - if (!d->ref.deref()) - delete d; } /*! @@ -2257,7 +2256,7 @@ QDateTime::~QDateTime() QDateTime &QDateTime::operator=(const QDateTime &other) { - qAtomicAssign(d, other.d); + d.assign(other.d.data()); return *this; } @@ -3290,7 +3289,7 @@ QDateTime QDateTime::fromString(const QString &string, const QString &format) */ void QDateTime::detach() { - qAtomicDetach(d); + d.detach(); } /***************************************************************************** diff --git a/src/corelib/tools/qdatetime.h b/src/corelib/tools/qdatetime.h index 62a42d5..84d3e83 100644 --- a/src/corelib/tools/qdatetime.h +++ b/src/corelib/tools/qdatetime.h @@ -44,6 +44,7 @@ #include <QtCore/qstring.h> #include <QtCore/qnamespace.h> +#include <QtCore/qscopedpointer.h> QT_BEGIN_HEADER @@ -284,7 +285,7 @@ public: private: friend class QDateTimePrivate; void detach(); - QDateTimePrivate *d; + QScopedSharedPointer<QDateTimePrivate> d; #ifndef QT_NO_DATASTREAM friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QDateTime &); diff --git a/src/corelib/tools/qharfbuzz.cpp b/src/corelib/tools/qharfbuzz.cpp index a8d180a..a43e6f3 100644 --- a/src/corelib/tools/qharfbuzz.cpp +++ b/src/corelib/tools/qharfbuzz.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "qharfbuzz_p.h" - #include "qunicodetables_p.h" #include "qlibrary.h" #include "qtextcodec.h" +#include "qharfbuzz_p.h" + QT_USE_NAMESPACE extern "C" { @@ -126,6 +126,7 @@ char *HB_TextCodec_ConvertFromUnicode(void *codec, const HB_UChar16 *unicode, hb QByteArray data = reinterpret_cast<QTextCodec *>(codec)->fromUnicode((const QChar *)unicode, length); // ### suboptimal char *output = (char *)malloc(data.length() + 1); + Q_CHECK_PTR(output); memcpy(output, data.constData(), data.length() + 1); if (outputLength) *outputLength = data.length(); diff --git a/src/corelib/tools/qharfbuzz_p.h b/src/corelib/tools/qharfbuzz_p.h index 02f3f5f..a7da845 100644 --- a/src/corelib/tools/qharfbuzz_p.h +++ b/src/corelib/tools/qharfbuzz_p.h @@ -53,8 +53,8 @@ #ifndef QHARFBUZZ_P_H #define QHARFBUZZ_P_H -#include <harfbuzz-shaper.h> #include <QtCore/qglobal.h> +#include <harfbuzz-shaper.h> QT_BEGIN_NAMESPACE diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 5e13794..308ba23 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -171,7 +171,9 @@ QHashData QHashData::shared_null = { void *QHashData::allocateNode() { - return qMalloc(nodeSize); + void *ptr = qMalloc(nodeSize); + Q_CHECK_PTR(ptr); + return ptr; } void QHashData::freeNode(void *node) @@ -181,6 +183,13 @@ void QHashData::freeNode(void *node) QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize) { + return detach_helper( node_duplicate, 0, nodeSize ); +} + +QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), + void (*node_delete)(Node *), + int nodeSize) +{ union { QHashData *d; Node *e; @@ -197,18 +206,43 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), int d->sharable = true; if (numBuckets) { - d->buckets = new Node *[numBuckets]; + QT_TRY { + d->buckets = new Node *[numBuckets]; + } QT_CATCH(...) { + // restore a consistent state for d + d->numBuckets = 0; + // roll back + d->free_helper(node_delete); + QT_RETHROW; + } + Node *this_e = reinterpret_cast<Node *>(this); for (int i = 0; i < numBuckets; ++i) { Node **nextNode = &d->buckets[i]; Node *oldNode = buckets[i]; while (oldNode != this_e) { - Node *dup = static_cast<Node *>(allocateNode()); - node_duplicate(oldNode, dup); - dup->h = oldNode->h; - *nextNode = dup; - nextNode = &dup->next; - oldNode = oldNode->next; + QT_TRY { + Node *dup = static_cast<Node *>(allocateNode()); + + QT_TRY { + node_duplicate(oldNode, dup); + } QT_CATCH(...) { + freeNode( dup ); + QT_RETHROW; + } + + dup->h = oldNode->h; + *nextNode = dup; + nextNode = &dup->next; + oldNode = oldNode->next; + } QT_CATCH(...) { + // restore a consistent state for d + *nextNode = e; + d->numBuckets = i+1; + // roll back + d->free_helper(node_delete); + QT_RETHROW; + } } *nextNode = e; } @@ -216,6 +250,26 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), int return d; } +void QHashData::free_helper(void (*node_delete)(Node *)) +{ + if (node_delete) { + Node *this_e = reinterpret_cast<Node *>(this); + Node **bucket = reinterpret_cast<Node **>(this->buckets); + + int n = numBuckets; + while (n--) { + Node *cur = *bucket++; + while (cur != this_e) { + Node *next = cur->next; + node_delete(cur); + cur = next; + } + } + } + delete [] buckets; + delete this; +} + QHashData::Node *QHashData::nextNode(Node *node) { union { @@ -298,9 +352,10 @@ void QHashData::rehash(int hint) Node **oldBuckets = buckets; int oldNumBuckets = numBuckets; + int nb = primeForNumBits(hint); + buckets = new Node *[nb]; numBits = hint; - numBuckets = primeForNumBits(hint); - buckets = new Node *[numBuckets]; + numBuckets = nb; for (int i = 0; i < numBuckets; ++i) buckets[i] = e; @@ -327,8 +382,7 @@ void QHashData::rehash(int hint) void QHashData::destroyAndFree() { - delete [] buckets; - delete this; + free_helper(0); } #ifdef QT_QHASH_DEBUG diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index b4fe337..67ee895 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -129,12 +129,15 @@ struct Q_CORE_EXPORT QHashData void *allocateNode(); void freeNode(void *node); - QHashData *detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize); + QHashData *detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize); // ### Qt5 remove me + QHashData *detach_helper(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *), + int nodeSize); void mightGrow(); bool willGrow(); void hasShrunk(); void rehash(int hint); - void destroyAndFree(); + void free_helper(void (*node_delete)(Node *)); + void destroyAndFree(); // ### Qt5 remove me Node *firstNode(); #ifdef QT_QHASH_DEBUG void dump(); @@ -147,10 +150,10 @@ struct Q_CORE_EXPORT QHashData }; inline void QHashData::mightGrow() // ### Qt 5: eliminate -{ +{ if (size >= numBuckets) rehash(numBits + 1); -} +} inline bool QHashData::willGrow() { @@ -164,8 +167,13 @@ inline bool QHashData::willGrow() inline void QHashData::hasShrunk() { - if (size <= (numBuckets >> 3) && numBits > userNumBits) - rehash(qMax(int(numBits) - 2, int(userNumBits))); + if (size <= (numBuckets >> 3) && numBits > userNumBits) { + QT_TRY { + rehash(qMax(int(numBits) - 2, int(userNumBits))); + } QT_CATCH(const std::bad_alloc &) { + // ignore bad allocs - shrinking shouldn't throw. rehash is exception safe. + } + } } inline QHashData::Node *QHashData::firstNode() @@ -476,21 +484,30 @@ private: Node **findNode(const Key &key, uint *hp = 0) const; Node *createNode(uint h, const Key &key, const T &value, Node **nextNode); void deleteNode(Node *node); + static void deleteNode(QHashData::Node *node); static void duplicateNode(QHashData::Node *originalNode, void *newNode); }; + template <class Key, class T> Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode(Node *node) { + deleteNode(reinterpret_cast<QHashData::Node*>(node)); +} + + +template <class Key, class T> +Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode(QHashData::Node *node) +{ #ifdef Q_CC_BOR - node->~QHashNode<Key, T>(); + concrete(node)->~QHashNode<Key, T>(); #elif defined(QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION) - node->~QHashNode(); + concrete(node)->~QHashNode(); #else - node->~Node(); + concrete(node)->~Node(); #endif - d->freeNode(node); + qFree(node); } template <class Key, class T> @@ -538,18 +555,7 @@ Q_INLINE_TEMPLATE QHash<Key, T> &QHash<Key, T>::unite(const QHash<Key, T> &other template <class Key, class T> Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::freeData(QHashData *x) { - Node *e_for_x = reinterpret_cast<Node *>(x); - Node **bucket = reinterpret_cast<Node **>(x->buckets); - int n = x->numBuckets; - while (n--) { - Node *cur = *bucket++; - while (cur != e_for_x) { - Node *next = cur->next; - deleteNode(cur); - cur = next; - } - } - x->destroyAndFree(); + x->free_helper(deleteNode); } template <class Key, class T> @@ -561,7 +567,7 @@ Q_INLINE_TEMPLATE void QHash<Key, T>::clear() template <class Key, class T> Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::detach_helper() { - QHashData *x = d->detach_helper(duplicateNode, + QHashData *x = d->detach_helper(duplicateNode, deleteNode, QTypeInfo<T>::isDummy ? sizeof(DummyNode) : sizeof(Node)); if (!d->ref.deref()) freeData(d); @@ -760,6 +766,8 @@ Q_INLINE_TEMPLATE typename QHash<Key, T>::iterator QHash<Key, T>::insertMulti(co template <class Key, class T> Q_OUTOFLINE_TEMPLATE int QHash<Key, T>::remove(const Key &akey) { + if (isEmpty()) // prevents detaching shared null + return 0; detach(); int oldSize = d->size; @@ -781,6 +789,8 @@ Q_OUTOFLINE_TEMPLATE int QHash<Key, T>::remove(const Key &akey) template <class Key, class T> Q_OUTOFLINE_TEMPLATE T QHash<Key, T>::take(const Key &akey) { + if (isEmpty()) // prevents detaching shared null + return T(); detach(); Node **node = findNode(akey); @@ -911,7 +921,8 @@ public: inline QMultiHash operator+(const QMultiHash &other) const { QMultiHash result = *this; result += other; return result; } -#ifndef Q_NO_USING_KEYWORD +#if !defined(Q_NO_USING_KEYWORD) && !defined(Q_CC_RVCT) + // RVCT compiler doesn't handle using-keyword right when used functions are overloaded in child class using QHash<Key, T>::contains; using QHash<Key, T>::remove; using QHash<Key, T>::count; @@ -981,7 +992,12 @@ Q_INLINE_TEMPLATE int QMultiHash<Key, T>::remove(const Key &key, const T &value) typename QHash<Key, T>::iterator end(QHash<Key, T>::end()); while (i != end && i.key() == key) { if (i.value() == value) { +#if defined(Q_CC_RVCT) + // RVCT has problems with scoping, apparently. + i = QHash<Key, T>::erase(i); +#else i = erase(i); +#endif ++n; } else { ++i; diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 750f686..f3e4bb7 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -265,15 +265,22 @@ void QLinkedList<T>::detach_helper() x.d->ref = 1; x.d->size = d->size; x.d->sharable = true; - Node *i = e->n, *j = x.e; - while (i != e) { - j->n = new Node(i->t); - j->n->p = j; - i = i->n; - j = j->n; + Node *original = e->n; + Node *copy = x.e; + while (original != e) { + QT_TRY { + copy->n = new Node(original->t); + copy->n->p = copy; + original = original->n; + copy = copy->n; + } QT_CATCH(...) { + copy->n = x.e; + free(x.d); + QT_RETHROW; + } } - j->n = x.e; - x.e->p = j; + copy->n = x.e; + x.e->p = copy; if (!d->ref.deref()) free(d); d = x.d; @@ -474,14 +481,21 @@ QLinkedList<T> &QLinkedList<T>::operator+=(const QLinkedList<T> &l) detach(); int n = l.d->size; d->size += n; - Node *o = l.e->n; + Node *original = l.e->n; while (n--) { - Node *i = new Node(o->t); - o = o->n; - i->n = e; - i->p = e->p; - i->p->n = i; - e->p = i; + QT_TRY { + Node *copy = new Node(original->t); + original = original->n; + copy->n = e; + copy->p = e->p; + copy->p->n = copy; + e->p = copy; + } QT_CATCH(...) { + // restore the original list + while (n++<d->size) + removeLast(); + QT_RETHROW; + } } return *this; } diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index e57986b..ab6f7bd 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -117,6 +117,14 @@ public: inline int size() const { return p.size(); } inline void detach() { if (d->ref != 1) detach_helper(); } + + inline void detachShared() + { + // The "this->" qualification is needed for GCCE. + if (d->ref != 1 && this->d != &QListData::shared_null) + detach_helper(); + } + inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } @@ -352,12 +360,31 @@ Q_INLINE_TEMPLATE void QList<T>::node_destruct(Node *n) template <typename T> Q_INLINE_TEMPLATE void QList<T>::node_copy(Node *from, Node *to, Node *src) { - if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) - while(from != to) - (from++)->v = new T(*reinterpret_cast<T*>((src++)->v)); - else if (QTypeInfo<T>::isComplex) - while(from != to) - new (from++) T(*reinterpret_cast<T*>(src++)); + Node *current = from; + if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) { + QT_TRY { + while(current != to) { + (current++)->v = new T(*reinterpret_cast<T*>((src++)->v)); + } + } QT_CATCH(...) { + while (current != from) + delete reinterpret_cast<T*>(current--); + QT_RETHROW; + } + + } else if (QTypeInfo<T>::isComplex) { + QT_TRY { + while(current != to) + new (current++) T(*reinterpret_cast<T*>(src++)); + } QT_CATCH(...) { + while (current != from) + (reinterpret_cast<T*>(current--))->~T(); + QT_RETHROW; + } + } else { + if (src != from && to - from > 0) + memcpy(from, src, (to - from) * sizeof(Node *)); + } } template <typename T> @@ -384,8 +411,17 @@ Q_INLINE_TEMPLATE QList<T> &QList<T>::operator=(const QList<T> &l) } template <typename T> inline typename QList<T>::iterator QList<T>::insert(iterator before, const T &t) -{ Node *n = reinterpret_cast<Node *>(p.insert(before.i-reinterpret_cast<Node *>(p.begin()))); - node_construct(n,t); return n; } +{ + int iBefore = before.i - reinterpret_cast<Node *>(p.begin()); + Node *n = reinterpret_cast<Node *>(p.insert(iBefore)); + QT_TRY { + node_construct(n, t); + } QT_CATCH(...) { + p.remove(iBefore); + QT_RETHROW; + } + return n; +} template <typename T> inline typename QList<T>::iterator QList<T>::erase(iterator it) { node_destruct(it.i); @@ -423,10 +459,22 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) { detach(); if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) { - node_construct(reinterpret_cast<Node *>(p.append()), t); + Node *n = reinterpret_cast<Node *>(p.append()); + QT_TRY { + node_construct(n, t); + } QT_CATCH(...) { + --d->end; + QT_RETHROW; + } } else { const T cpy(t); - node_construct(reinterpret_cast<Node *>(p.append()), cpy); + Node *n = reinterpret_cast<Node *>(p.append()); + QT_TRY { + node_construct(n, cpy); + } QT_CATCH(...) { + --d->end; + QT_RETHROW; + } } } @@ -435,10 +483,22 @@ inline void QList<T>::prepend(const T &t) { detach(); if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) { - node_construct(reinterpret_cast<Node *>(p.prepend()), t); + Node *n = reinterpret_cast<Node *>(p.prepend()); + QT_TRY { + node_construct(n, t); + } QT_CATCH(...) { + ++d->begin; + QT_RETHROW; + } } else { const T cpy(t); - node_construct(reinterpret_cast<Node *>(p.prepend()), cpy); + Node *n = reinterpret_cast<Node *>(p.prepend()); + QT_TRY { + node_construct(n, cpy); + } QT_CATCH(...) { + ++d->begin; + QT_RETHROW; + } } } @@ -447,10 +507,22 @@ inline void QList<T>::insert(int i, const T &t) { detach(); if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic) { - node_construct(reinterpret_cast<Node *>(p.insert(i)), t); + Node *n = reinterpret_cast<Node *>(p.insert(i)); + QT_TRY { + node_construct(n, t); + } QT_CATCH(...) { + p.remove(i); + QT_RETHROW; + } } else { const T cpy(t); - node_construct(reinterpret_cast<Node *>(p.insert(i)), cpy); + Node *n = reinterpret_cast<Node *>(p.insert(i)); + QT_TRY { + node_construct(n, cpy); + } QT_CATCH(...) { + p.remove(i); + QT_RETHROW; + } } } @@ -522,7 +594,14 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::detach_helper() { Node *n = reinterpret_cast<Node *>(p.begin()); QListData::Data *x = p.detach2(); - node_copy(reinterpret_cast<Node *>(p.begin()), reinterpret_cast<Node *>(p.end()), n); + QT_TRY { + node_copy(reinterpret_cast<Node *>(p.begin()), reinterpret_cast<Node *>(p.end()), n); + } QT_CATCH(...) { + qFree(d); + d = x; + QT_RETHROW; + } + if (!x->ref.deref()) free(x); } @@ -572,7 +651,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::clear() template <typename T> Q_OUTOFLINE_TEMPLATE int QList<T>::removeAll(const T &_t) { - detach(); + detachShared(); const T t = _t; int removedCount=0, i=0; Node *n; @@ -590,7 +669,7 @@ Q_OUTOFLINE_TEMPLATE int QList<T>::removeAll(const T &_t) template <typename T> Q_OUTOFLINE_TEMPLATE bool QList<T>::removeOne(const T &_t) { - detach(); + detachShared(); int index = indexOf(_t); if (index != -1) { removeAt(index); @@ -615,7 +694,13 @@ Q_OUTOFLINE_TEMPLATE QList<T> &QList<T>::operator+=(const QList<T> &l) { detach(); Node *n = reinterpret_cast<Node *>(p.append(l.p)); - node_copy(n, reinterpret_cast<Node *>(p.end()), reinterpret_cast<Node *>(l.p.begin())); + QT_TRY{ + node_copy(n, reinterpret_cast<Node *>(p.end()), reinterpret_cast<Node *>(l.p.begin())); + } QT_CATCH(...) { + // restore the old end + d->end -= (reinterpret_cast<Node *>(p.end()) - n); + QT_RETHROW; + } return *this; } diff --git a/src/corelib/tools/qlistdata.cpp b/src/corelib/tools/qlistdata.cpp index 4885b43..be57359 100644 --- a/src/corelib/tools/qlistdata.cpp +++ b/src/corelib/tools/qlistdata.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include <new> #include "qlist.h" #include "qtools_p.h" #include <string.h> @@ -71,15 +72,18 @@ static int grow(int size) QListData::Data *QListData::detach() { Data *x = static_cast<Data *>(qMalloc(DataHeaderSize + d->alloc * sizeof(void *))); - if (!x) - qFatal("QList: Out of memory"); + Q_CHECK_PTR(x); - ::memcpy(x, d, DataHeaderSize + d->alloc * sizeof(void *)); - x->alloc = d->alloc; x->ref = 1; x->sharable = true; - if (!x->alloc) - x->begin = x->end = 0; + x->alloc = d->alloc; + if (!x->alloc) { + x->begin = 0; + x->end = 0; + } else { + x->begin = d->begin; + x->end = d->end; + } qSwap(d, x); if (!x->ref.deref()) @@ -87,20 +91,30 @@ QListData::Data *QListData::detach() return 0; } -// Returns the old (shared) data, it is up to the caller to deref() and free() +/*! + * Detaches the QListData by reallocating new memory. + * Returns the old (shared) data, it is up to the caller to deref() and free() + * For the new data node_copy needs to be called. + * + * \internal + */ QListData::Data *QListData::detach2() { Data *x = d; - d = static_cast<Data *>(qMalloc(DataHeaderSize + x->alloc * sizeof(void *))); - if (!d) - qFatal("QList: Out of memory"); - - ::memcpy(d, x, DataHeaderSize + x->alloc * sizeof(void *)); - d->alloc = x->alloc; - d->ref = 1; - d->sharable = true; - if (!d->alloc) - d->begin = d->end = 0; + Data* t = static_cast<Data *>(qMalloc(DataHeaderSize + x->alloc * sizeof(void *))); + Q_CHECK_PTR(t); + + t->ref = 1; + t->sharable = true; + t->alloc = x->alloc; + if (!t->alloc) { + t->begin = 0; + t->end = 0; + } else { + t->begin = x->begin; + t->end = x->end; + } + d = t; return x; } @@ -109,8 +123,7 @@ void QListData::realloc(int alloc) { Q_ASSERT(d->ref == 1); Data *x = static_cast<Data *>(qRealloc(d, DataHeaderSize + alloc * sizeof(void *))); - if (!x) - qFatal("QList: Out of memory"); + Q_CHECK_PTR(x); d = x; d->alloc = alloc; @@ -118,12 +131,14 @@ void QListData::realloc(int alloc) d->begin = d->end = 0; } +// ensures that enough space is available to append one element void **QListData::append() { Q_ASSERT(d->ref == 1); if (d->end == d->alloc) { int n = d->end - d->begin; if (d->begin > 2 * d->alloc / 3) { + // we have enough space. Just not at the end -> move it. ::memcpy(d->array + n, d->array + d->begin, n * sizeof(void *)); d->begin = n; d->end = n * 2; @@ -134,6 +149,7 @@ void **QListData::append() return d->array + d->end++; } +// ensures that enough space is available to append the list void **QListData::append(const QListData& l) { Q_ASSERT(d->ref == 1); @@ -142,7 +158,6 @@ void **QListData::append(const QListData& l) if (n) { if (e + n > d->alloc) realloc(grow(e + l.d->end - l.d->begin)); - ::memcpy(d->array + d->end, l.d->array + l.d->begin, n * sizeof(void*)); d->end += n; } return d->array + e; @@ -514,6 +529,15 @@ void **QListData::erase(void **xi) \internal */ +/*! \fn void QList::detachShared() + + \internal + + like detach(), but does nothing if we're shared_null. + This prevents needless mallocs, and makes QList more exception safe + in case of cleanup work done in destructors on empty lists. +*/ + /*! \fn bool QList::isDetached() const \internal diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 6913141..7c4d0c0 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -122,6 +122,13 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); static qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok); static qulonglong qstrtoull(const char *nptr, const char **endptr, register int base, bool *ok); +#if defined(Q_CC_MWERKS) && defined(Q_OS_WIN32) +inline bool isascii(int c) +{ + return (c >= 0 && c <=127); +} +#endif + /****************************************************************************** ** Helpers for accessing Qt locale database */ @@ -288,7 +295,7 @@ static bool splitLocaleName(const QString &name, QChar *lang_begin, QChar *cntry return lang_len == 2 || lang_len == 3; } -static void getLangAndCountry(const QString &name, QLocale::Language &lang, QLocale::Country &cntry) +void getLangAndCountry(const QString &name, QLocale::Language &lang, QLocale::Country &cntry) { lang = QLocale::C; cntry = QLocale::AnyCountry; @@ -1199,7 +1206,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const return QVariant(); } -#elif defined(Q_OS_UNIX) +#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) static uint unixGetSystemMeasurementSystem() { @@ -1243,7 +1250,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant /* in */) const } } -#else +#elif !defined(Q_OS_SYMBIAN) /*! Returns a fallback locale, that will get used for everything that @@ -3877,7 +3884,13 @@ QString QLocalePrivate::doubleToString(double d, char *rve = 0; char *buff = 0; - digits = QLatin1String(qdtoa(d, mode, pr, &decpt, &sign, &rve, &buff)); + QT_TRY { + digits = QLatin1String(qdtoa(d, mode, pr, &decpt, &sign, &rve, &buff)); + } QT_CATCH(...) { + if (buff != 0) + free(buff); + QT_RETHROW; + } if (buff != 0) free(buff); #endif // QT_QLOCALE_USES_FCVT @@ -5050,6 +5063,7 @@ static Bigint *Balloc(int k) x = 1 << k; rv = static_cast<Bigint *>(MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long))); + Q_CHECK_PTR(rv); rv->k = k; rv->maxwds = x; rv->sign = rv->wds = 0; @@ -6726,7 +6740,13 @@ static char *_qdtoa( NEEDS_VOLATILE double d, int mode, int ndigits, int *decpt, if (i <= 0) i = 1; } - *resultp = static_cast<char *>(malloc(i + 1)); + QT_TRY { + *resultp = static_cast<char *>(malloc(i + 1)); + Q_CHECK_PTR(*resultp); + } QT_CATCH(...) { + Bfree(b); + QT_RETHROW; + } s = s0 = *resultp; if (ilim >= 0 && ilim <= Quick_max && try_quick) { @@ -7148,6 +7168,7 @@ Q_CORE_EXPORT char *qdtoa( double d, int mode, int ndigits, int *decpt, int *sig n = i + 1; } *resultp = static_cast<char*>(malloc(n + 1)); + Q_CHECK_PTR(resultp); qstrncpy(*resultp, res, n + 1); return *resultp; } diff --git a/src/corelib/tools/qlocale_symbian.cpp b/src/corelib/tools/qlocale_symbian.cpp new file mode 100644 index 0000000..dc9692b --- /dev/null +++ b/src/corelib/tools/qlocale_symbian.cpp @@ -0,0 +1,879 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDate> +#include <QLocale> +#include <QTime> +#include <QVariant> + +#include <e32std.h> +#include "private/qcore_symbian_p.h" + + +QT_BEGIN_NAMESPACE + +// Located in qlocale.cpp +extern void getLangAndCountry(const QString &name, QLocale::Language &lang, QLocale::Country &cntry); + +static TExtendedLocale _s60Locale; + +// Type definitions for runtime resolved function pointers +typedef void (*FormatFunc)(TTime&, TDes&, const TDesC&, const TLocale&); +typedef TPtrC (*FormatSpecFunc)(TExtendedLocale&); + +// Runtime resolved functions +static FormatFunc ptrTimeFormatL = NULL; +static FormatSpecFunc ptrGetTimeFormatSpec = NULL; +static FormatSpecFunc ptrGetLongDateFormatSpec = NULL; +static FormatSpecFunc ptrGetShortDateFormatSpec = NULL; + +// Default functions if functions cannot be resolved +static void defaultTimeFormatL(TTime&, TDes& des, const TDesC&, const TLocale&) +{ + des.Zero(); +} + +static TPtrC defaultFormatSpec(TExtendedLocale&) +{ + return TPtrC(KNullDesC); +} + +/*! + Definition of struct for mapping Symbian to ISO locale +*/ +struct symbianToISO { + int symbian_language; + char iso_name[8]; +}; + + +/*! + Mapping from Symbian to ISO locale +*/ +static const symbianToISO symbian_to_iso_list[] = { + { ELangEnglish, "en_GB" }, + { ELangFrench, "fr_FR" }, + { ELangGerman, "de_DE" }, + { ELangSpanish, "es_ES" }, + { ELangItalian, "it_IT" }, + { ELangSwedish, "sv_SE" }, + { ELangDanish, "da_DK" }, + { ELangNorwegian, "no_NO" }, + { ELangFinnish, "fi_FI" }, + { ELangAmerican, "en_US" }, + { ELangPortuguese, "pt_PT" }, + { ELangTurkish, "tr_TR" }, + { ELangIcelandic, "is_IS" }, + { ELangRussian, "ru_RU" }, + { ELangHungarian, "hu_HU" }, + { ELangDutch, "nl_NL" }, + { ELangBelgianFlemish, "nl_BE" }, + { ELangCzech, "cs_CZ" }, + { ELangSlovak, "sk_SK" }, + { ELangPolish, "pl_PL" }, + { ELangSlovenian, "sl_SI" }, + { ELangTaiwanChinese, "zh_TW" }, + { ELangHongKongChinese, "zh_HK" }, + { ELangPrcChinese, "zh_CN" }, + { ELangJapanese, "ja_JP" }, + { ELangThai, "th_TH" }, + { ELangArabic, "ar_AE" }, + { ELangTagalog, "tl_PH" }, + { ELangBulgarian, "bg_BG" }, + { ELangCatalan, "ca_ES" }, + { ELangCroatian, "hr_HR" }, + { ELangEstonian, "et_EE" }, + { ELangFarsi, "fa_IR" }, + { ELangCanadianFrench, "fr_CA" }, + { ELangGreek, "el_GR" }, + { ELangHebrew, "he_IL" }, + { ELangHindi, "hi_IN" }, + { ELangIndonesian, "id_ID" }, + { ELangLatvian, "lv_LV" }, + { ELangLithuanian, "lt_LT" }, + { ELangMalay, "ms_MY" }, + { ELangBrazilianPortuguese, "pt_BR" }, + { ELangRomanian, "ro_RO" }, + { ELangSerbian, "sr_YU" }, + { ELangLatinAmericanSpanish, "es" }, + { ELangUkrainian, "uk_UA" }, + { ELangUrdu, "ur_PK" }, // India/Pakistan + { ELangVietnamese, "vi_VN" }, +#ifdef __E32LANG_H__ +// 5.0 + { ELangBasque, "eu_ES" }, + { ELangGalician, "gl_ES" }, +#endif +#if !defined(__SERIES60_31__) + { ELangEnglish_Apac, "en" }, + { ELangEnglish_Taiwan, "en_TW" }, + { ELangEnglish_HongKong, "en_HK" }, + { ELangEnglish_Prc, "en_CN" }, + { ELangEnglish_Japan, "en_JP"}, + { ELangEnglish_Thailand, "en_TH" }, + { ELangMalay_Apac, "ms" } +#endif +}; + +/*! + Returns ISO name corresponding to the Symbian locale code \a sys_fmt. +*/ +static QByteArray symbianLocaleName(int code) +{ + //Number of Symbian to ISO locale mappings + static const int symbian_to_iso_count + = sizeof(symbian_to_iso_list)/sizeof(symbianToISO); + + int cmp = code - symbian_to_iso_list[0].symbian_language; + if (cmp < 0) + return 0; + + if (cmp == 0) + return symbian_to_iso_list[0].iso_name; + + int begin = 0; + int end = symbian_to_iso_count; + + while (end - begin > 1) { + uint mid = (begin + end)/2; + + const symbianToISO *elt = symbian_to_iso_list + mid; + int cmp = code - elt->symbian_language; + if (cmp < 0) + end = mid; + else if (cmp > 0) + begin = mid; + else + return elt->iso_name; + } + + return 0; +} + + +// order is: normal, abbr, nmode, nmode+abbr +static const char *us_locale_dep[] = { + "MM", "dd", "yyyy", "MM", "dd", + "M", "d", "yy", "M", "d", + "MMMM", "dd", "yyyy", "MMMM", "dd", + "MMM", "d", "yy", "MMM", "d" }; + +static const char *eu_locale_dep[] = { + "dd", "MM", "yyyy", "dd", "MM", + "d", "M", "yy", "d", "M", + "dd", "MMMM", "yyyy", "dd", "MMMM", + "d", "MMM", "yy", "d", "MMM" }; + +static const char *jp_locale_dep[] = { + "yyyy", "MM", "dd", "MM", "dd", + "yy", "M", "d", "M", "d", + "yyyy", "MMMM", "dd", "MMMM", "dd", + "yy", "MMM", "d", "MMM", "d" }; + +/*! + Returns a Qt version of the given \a sys_fmt Symbian locale format string. +*/ +static QString s60ToQtFormat(const QString &sys_fmt) +{ + TLocale *locale = _s60Locale.GetLocale(); + + QString result; + QString other; + QString qtformatchars = QString::fromLatin1("adhmsyzAHM"); + + QChar c; + int i = 0; + bool open_escape = false; + bool abbrev_next = false; + bool locale_indep_ordering = false; + bool minus_mode = false; + bool plus_mode = false; + bool n_mode = false; + TTimeFormat tf = locale->TimeFormat(); + + while (i < sys_fmt.size()) { + + c = sys_fmt.at(i); + + // let formatting thru + if (c.unicode() == '%') { + // if we have gathered string, concat it + if (!other.isEmpty()) { + result += other; + other.clear(); + } + // if we have open escape, end it + if (open_escape) { + result += QLatin1Char('\''); + open_escape = false; + } + + ++i; + if (i >= sys_fmt.size()) + break; + + c = sys_fmt.at(i); + + // process specials + abbrev_next = c.unicode() == '*'; + plus_mode = c.unicode() == '+'; + minus_mode = c.unicode() == '-'; + + if (abbrev_next || plus_mode || minus_mode) { + ++i; + if (i >= sys_fmt.size()) + break; + + c = sys_fmt.at(i); + + if (plus_mode || minus_mode) { + // break on undefined plus/minus mode + if (c.unicode() != 'A' && c.unicode() != 'B') + break; + } + } + + switch (c.unicode()) { + case 'F': + { + // locale indep mode on + locale_indep_ordering = true; + break; + } + + case '/': + { + // date sep 0-3 + ++i; + if (i >= sys_fmt.size()) + break; + + c = sys_fmt.at(i); + if (c.isDigit() && c.digitValue() <= 3) { + TChar s = locale->DateSeparator(c.digitValue()); + TUint val = s; + // some indexes return zero for empty + if (val > 0) + result += QChar(val); + } + break; + } + + case 'D': + { + if (!locale_indep_ordering) + break; + + if (!abbrev_next) + result += QLatin1String("dd"); + else + result += QLatin1Char('d'); + + break; + } + + case 'M': + { + if (!locale_indep_ordering) + break; + + if (!n_mode) { + if (!abbrev_next) + result += QLatin1String("MM"); + else + result += QLatin1String("M"); + } else { + if (!abbrev_next) + result += QLatin1String("MMMM"); + else + result += QLatin1String("MMM"); + } + + break; + } + + case 'N': + { + n_mode = true; + + if (!locale_indep_ordering) + break; + + if (!abbrev_next) + result += QLatin1String("MMMM"); + else + result += QLatin1String("MMM"); + + break; + } + + case 'Y': + { + if (!locale_indep_ordering) + break; + + if (!abbrev_next) + result += QLatin1String("yyyy"); + else + result += QLatin1String("yy"); + + break; + } + + case 'E': + { + if (!abbrev_next) + result += QLatin1String("dddd"); + else + result += QLatin1String("ddd"); + + break; + } + + case ':': + { + // timesep 0-3 + ++i; + if (i >= sys_fmt.size()) + break; + + c = sys_fmt.at(i); + if (c.isDigit() && c.digitValue() <= 3) { + TChar s = locale->TimeSeparator(c.digitValue()); + TUint val = s; + // some indexes return zero for empty + if (val > 0) + result += QChar(val); + } + + break; + } + + case 'J': + { + if (tf == ETime24 && !abbrev_next) + result += QLatin1String("hh"); + else + result += QLatin1Char('h'); + + break; + } + + case 'H': + { + if (!abbrev_next) + result += QLatin1String("hh"); + else + result += QLatin1Char('h'); + + break; + } + + case 'I': + { + result += QLatin1Char('h'); + break; + } + + case 'T': + { + if (!abbrev_next) + result += QLatin1String("mm"); + else + result += QLatin1Char('m'); + + break; + } + + case 'S': + { + if (!abbrev_next) + result += QLatin1String("ss"); + else + result += QLatin1Char('s'); + + break; + } + + case 'B': + { + // only done for 12h clock + if (tf == ETime24) + break; + } + + // fallthru to A + case 'A': { + // quickie to get capitalization, can't use s60 string as is because Qt 'hh' format's am/pm logic + TAmPmName ampm = TAmPmName(); + TChar first(ampm[0]); + QString qtampm = QString::fromLatin1(first.IsUpper() ? "AP" : "ap"); + + int pos = locale->AmPmSymbolPosition(); + + if ((minus_mode && pos != ELocaleBefore) || + (plus_mode && pos != ELocaleAfter)) + break; + + if (!abbrev_next && locale->AmPmSpaceBetween()) { + if (pos == ELocaleBefore) + qtampm.append(QLatin1Char(' ')); + else + qtampm.prepend(QLatin1Char(' ')); + } + + result += qtampm; + } + break; + + case '.': { + // decimal sep + TChar s = locale->DecimalSeparator(); + TUint val = s; + if (val > 0) + result += QChar(val); + } + break; + + case 'C': + { + // six digits in s60, three digits in qt + if (!abbrev_next) { + result += QLatin1String("zzz"); + } else { + // next char is number from 0-6, how many digits to display + ++i; + if (i >= sys_fmt.size()) + break; + + c = sys_fmt.at(i); + + if (c.isDigit()) { + // try to match wanted digits + QChar val(c.digitValue()); + + if (val >= 3) { + result += QLatin1String("zzz"); + } else if (val > 0) { + result += QLatin1Char('z'); + } + } + } + break; + } + + // these cases fallthru + case '1': + case '2': + case '3': + case '4': + case '5': + { + + // shouldn't parse these with %F + if (locale_indep_ordering) + break; + + TDateFormat df = locale->DateFormat(); + + const char **locale_dep; + switch (df) { + default: // fallthru to american + case EDateAmerican: + locale_dep = us_locale_dep; + break; + case EDateEuropean: + locale_dep = eu_locale_dep; + break; + case EDateJapanese: + locale_dep = jp_locale_dep; + break; + } + int offset = 0; + if (abbrev_next) + offset += 5; + if (n_mode) + offset += 10; + + result += QLatin1String(locale_dep[offset + (c.digitValue()-1)]); + break; + } + + case '%': // fallthru percent + { + // any junk gets copied as is + } + default: + { + result += c; + break; + } + + case 'Z': // Qt doesn't support these :( + case 'X': + case 'W': + { + break; + } + } + } else { + // double any single quotes, don't begin escape + if (c.unicode() == '\'') { + // end open escape + if (open_escape) { + result += other; + other.clear(); + result += QLatin1Char('\''); + open_escape = false; + } + + other += c; + } + + // gather chars and escape them in one go if any format chars are found + if (!open_escape && qtformatchars.indexOf(c) != -1) { + result += QLatin1Char('\''); + open_escape = true; + } + other += c; + } + + ++i; + } + + if (!other.isEmpty()) + result += other; + if (open_escape) + result += QLatin1Char('\''); + + return result; +} + +/*! + Retrieves Symbian locale decimal separator. +*/ +static QString symbianDecimalPoint() +{ + TLocale *locale = _s60Locale.GetLocale(); + + TChar decPoint = locale->DecimalSeparator(); + int val = decPoint; + return QChar(val); +} + +/*! + Retrieves Symbian locale group separator. +*/ +static QString symbianGroupSeparator() +{ + TLocale *locale = _s60Locale.GetLocale(); + + TChar grpSep = locale->ThousandsSeparator(); + int val = grpSep; + return QChar(val); +} + +/*! + Retrieves Symbian locale zero digit. +*/ +static QString symbianZeroDigit() +{ + TLocale *locale = _s60Locale.GetLocale(); + + // TDigitType enumeration value returned by TLocale + // will always correspond to zero digit unicode value. + TDigitType digit = locale->DigitType(); + return QChar(digit); +} + +/*! + Retrieves a day name from Symbian locale. The \a day is an integer + from 1 to 7. When \a short_format is true the method returns + the day in short format. Otherwise it returns the day in a long format. +*/ +static QString symbianDayName(int day, bool short_format) +{ + day -= 1; + + if (day < 0 || day > 6) + return QString(); + + if (short_format) { + return qt_TDes2QString(TDayNameAbb(TDay(day))); + } else { + return qt_TDes2QString(TDayName(TDay(day))); + } +} + +/*! + Retrieves a month name from Symbian locale. The \a month is an integer + from 1 to 12. When \a short_format is true the method returns + the month in short format. Otherwise it returns the month in a long format. +*/ +static QString symbianMonthName(int month, bool short_format) +{ + month -= 1; + if (month < 0 || month > 11) + return QString(); + + if (short_format) { + return qt_TDes2QString(TMonthNameAbb(TMonth(month))); + } else { + return qt_TDes2QString(TMonthName(TMonth(month))); + } +} + +/*! + Retrieves date format from Symbian locale and + transforms it to Qt format. + + When \a short_format is true the method returns + short date format. Otherwise it returns the long format. +*/ +static QString symbianDateFormat(bool short_format) +{ + TPtrC dateFormat; + + if (short_format) { + dateFormat.Set(ptrGetShortDateFormatSpec(_s60Locale)); + } else { + dateFormat.Set(ptrGetLongDateFormatSpec(_s60Locale)); + } + + return s60ToQtFormat(qt_TDesC2QString(dateFormat)); +} + +/*! + Retrieves time format from Symbian locale and + transforms it to Qt format. +*/ +static QString symbianTimeFormat() +{ + return s60ToQtFormat(qt_TDesC2QString(ptrGetTimeFormatSpec(_s60Locale))); +} + +/*! + Returns localized string representation of given \a date + formatted with Symbian locale date format. + + If \a short_format is true the format will be a short version. + Otherwise it uses a longer version. +*/ +static QString symbianDateToString(const QDate &date, bool short_format) +{ + int month = date.month() - 1; + int day = date.day() - 1; + int year = date.year(); + + TDateTime dateTime; + dateTime.Set(year, TMonth(month), day, 0, 0, 0, 0); + + TTime timeStr(dateTime); + TBuf<KMaxLongDateFormatSpec*2> buffer; + + TPtrC dateFormat; + if (short_format) { + dateFormat.Set(ptrGetShortDateFormatSpec(_s60Locale)); + } else { + dateFormat.Set(ptrGetLongDateFormatSpec(_s60Locale)); + } + + TRAPD(err, ptrTimeFormatL(timeStr, buffer, dateFormat, *_s60Locale.GetLocale());) + + if (err == KErrNone) + return qt_TDes2QString(buffer); + else + return QString(); +} + +/*! + Returns localized string representation of given \a time + formatted with Symbian locale time format. +*/ +static QString symbianTimeToString(const QTime &time) +{ + int hour = time.hour(); + int minute = time.minute(); + int second = time.second(); + int milliseconds = 0; + + TDateTime dateTime; + dateTime.Set(0, TMonth(0), 0, hour, minute, second, milliseconds); + + TTime timeStr(dateTime); + TBuf<KMaxTimeFormatSpec*2> buffer; + + TRAPD(err, ptrTimeFormatL( + timeStr, + buffer, + ptrGetTimeFormatSpec(_s60Locale), + *_s60Locale.GetLocale()); + ) + + if (err == KErrNone) + return qt_TDes2QString(buffer); + else + return QString(); +} + +/*! + Returns the measurement system stored in Symbian locale + + \sa QLocale::MeasurementSystem +*/ +static QLocale::MeasurementSystem symbianMeasurementSystem() +{ + TLocale *locale = _s60Locale.GetLocale(); + + TUnitsFormat unitFormat = locale->UnitsGeneral(); + if (unitFormat == EUnitsImperial) + return QLocale::ImperialSystem; + else + return QLocale::MetricSystem; +} + +QLocale QSystemLocale::fallbackLocale() const +{ + // load system data before query calls + static bool initDone = false; + if (!initDone) { + _s60Locale.LoadSystemSettings(); + + // Initialize platform version dependent function pointers + ptrTimeFormatL = reinterpret_cast<FormatFunc> + (qt_resolveS60PluginFunc(S60Plugin_TimeFormatL)); + ptrGetTimeFormatSpec = reinterpret_cast<FormatSpecFunc> + (qt_resolveS60PluginFunc(S60Plugin_GetTimeFormatSpec)); + ptrGetLongDateFormatSpec = reinterpret_cast<FormatSpecFunc> + (qt_resolveS60PluginFunc(S60Plugin_GetLongDateFormatSpec)); + ptrGetShortDateFormatSpec = reinterpret_cast<FormatSpecFunc> + (qt_resolveS60PluginFunc(S60Plugin_GetShortDateFormatSpec)); + if (!ptrTimeFormatL) + ptrTimeFormatL = &defaultTimeFormatL; + if (!ptrGetTimeFormatSpec) + ptrGetTimeFormatSpec = &defaultFormatSpec; + if (!ptrGetLongDateFormatSpec) + ptrGetLongDateFormatSpec = &defaultFormatSpec; + if (!ptrGetShortDateFormatSpec) + ptrGetShortDateFormatSpec = &defaultFormatSpec; + } + + TLanguage lang = User::Language(); + QString locale = symbianLocaleName(lang); + return QLocale(locale); +} + +/*! + Generic query method for locale data. Provides indirection. + Denotes the \a type of the query + with \a in as input data depending on the query. + + \sa QSystemLocale::QueryType +*/ +QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const +{ + switch(type) { + case DecimalPoint: + return symbianDecimalPoint(); + case GroupSeparator: + return symbianGroupSeparator(); + + case ZeroDigit: + return symbianZeroDigit(); + + case DayNameLong: + case DayNameShort: + return symbianDayName(in.toInt(), (type == DayNameShort) ); + + case MonthNameLong: + case MonthNameShort: + return symbianMonthName(in.toInt(), (type == MonthNameShort) ); + + case DateFormatLong: + case DateFormatShort: + return symbianDateFormat( (type == DateFormatShort) ); + case TimeFormatLong: + case TimeFormatShort: + return symbianTimeFormat(); + case DateTimeFormatLong: + case DateTimeFormatShort: + return symbianDateFormat( (type == DateTimeFormatShort) ) + QLatin1Char(' ') + symbianTimeFormat(); + case DateToStringShort: + case DateToStringLong: + return symbianDateToString(in.toDate(), (type == DateToStringShort) ); + case TimeToStringShort: + case TimeToStringLong: + return symbianTimeToString(in.toTime()); + case DateTimeToStringShort: + case DateTimeToStringLong: { + const QDateTime dt = in.toDateTime(); + return symbianDateToString(dt.date(), (type == DateTimeToStringShort) ) + + QLatin1Char(' ') + symbianTimeToString(dt.time()); + } + case MeasurementSystem: + return static_cast<int>(symbianMeasurementSystem()); + case LanguageId: + case CountryId: { + TLanguage language = User::Language(); + QString locale = symbianLocaleName(language); + QLocale::Language lang; + QLocale::Country cntry; + getLangAndCountry(locale, lang, cntry); + if (type == LanguageId) + return lang; + // few iso codes have no country and will use this + if (cntry == QLocale::AnyCountry) + return fallbackLocale().country(); + + return cntry; + } + case NegativeSign: + case PositiveSign: + case AMText: + case PMText: + break; + default: + break; + } + return QVariant(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index f212f62..ed33764 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -59,6 +59,7 @@ QMapData QMapData::shared_null = { QMapData *QMapData::createData() { QMapData *d = new QMapData; + Q_CHECK_PTR(d); Node *e = reinterpret_cast<Node *>(d); e->backward = e; e->forward[0] = e; @@ -84,6 +85,15 @@ void QMapData::continueFreeData(int offset) delete this; } +/*! + Creates a new node inside the data structure. + + \a update is an array with pointers to the node after which the new node + should be inserted. Because of the strange skip list data structure there + could be several pointers to this node on different levels. + \a offset is an amount of bytes that needs to reserved just before the + QMapData::Node structure. +*/ QMapData::Node *QMapData::node_create(Node *update[], int offset) { int level = 0; @@ -94,10 +104,6 @@ QMapData::Node *QMapData::node_create(Node *update[], int offset) mask <<= Sparseness; } - ++randomBits; - if (level == 3 && !insertInOrder) - randomBits = qrand(); - if (level > topLevel) { Node *e = reinterpret_cast<Node *>(this); level = ++topLevel; @@ -105,7 +111,13 @@ QMapData::Node *QMapData::node_create(Node *update[], int offset) update[level] = e; } + ++randomBits; + if (level == 3 && !insertInOrder) + randomBits = qrand(); + void *concreteNode = qMalloc(offset + sizeof(Node) + level * sizeof(Node *)); + Q_CHECK_PTR(concreteNode); + Node *abstractNode = reinterpret_cast<Node *>(reinterpret_cast<char *>(concreteNode) + offset); abstractNode->backward = update[0]; @@ -116,6 +128,7 @@ QMapData::Node *QMapData::node_create(Node *update[], int offset) update[i]->forward[i] = abstractNode; update[i] = abstractNode; } + // update[level+1]=reinterpret_cast<Node *>(this); ++size; return abstractNode; } @@ -146,7 +159,7 @@ uint QMapData::adjust_ptr(Node *node) void QMapData::dump() { - qDebug("Map data (ref = %d, size = %d, randomBits = %#.8x)", ref.atomic, size, randomBits); + qDebug("Map data (ref = %d, size = %d, randomBits = %#.8x)", int(ref), size, randomBits); QString preOutput; QVector<QString> output(topLevel + 1); @@ -158,12 +171,12 @@ void QMapData::dump() Node *update[LastLevel + 1]; for (int i = 0; i <= topLevel; ++i) { - str.sprintf("%d: [%.8x] -", i, adjust_ptr(forward[i])); + str.sprintf("%d: [%.8x] -", i, adjust_ptr(reinterpret_cast<Node *>(forward[i]))); output[i] += str; - update[i] = forward[i]; + update[i] = reinterpret_cast<Node *>(forward[i]); } - Node *node = forward[0]; + Node *node = reinterpret_cast<Node *>(forward[0]); while (node != e) { int level = 0; while (level < topLevel && update[level + 1] == node) @@ -178,13 +191,13 @@ void QMapData::dump() update[i] = node->forward[i]; } for (int j = level + 1; j <= topLevel; ++j) - output[j] += "---------------"; + output[j] += QString("---------------"); node = node->forward[0]; } - qDebug(preOutput.ascii()); + qDebug("%s", preOutput.ascii()); for (int i = 0; i <= topLevel; ++i) - qDebug(output[i].ascii()); + qDebug("%s", output[i].ascii()); } #endif diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index ba647e9..f4853bd 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -416,9 +416,29 @@ Q_INLINE_TEMPLATE typename QMapData::Node * QMap<Key, T>::node_create(QMapData *adt, QMapData::Node *aupdate[], const Key &akey, const T &avalue) { QMapData::Node *abstractNode = adt->node_create(aupdate, payload()); - Node *concreteNode = concrete(abstractNode); - new (&concreteNode->key) Key(akey); - new (&concreteNode->value) T(avalue); + QT_TRY { + Node *concreteNode = concrete(abstractNode); + new (&concreteNode->key) Key(akey); + QT_TRY { + new (&concreteNode->value) T(avalue); + } QT_CATCH(...) { + concreteNode->key.~Key(); + QT_RETHROW; + } + } QT_CATCH(...) { + adt->node_delete(aupdate, payload(), abstractNode); + QT_RETHROW; + } + + // clean up the update array for further insertions + /* + for (int i = 0; i <= d->topLevel; ++i) { + if ( aupdate[i]==reinterpret_cast<QMapData::Node *>(adt) || aupdate[i]->forward[i] != abstractNode) + break; + aupdate[i] = abstractNode; + } +*/ + return abstractNode; } @@ -704,8 +724,13 @@ Q_OUTOFLINE_TEMPLATE void QMap<Key, T>::detach_helper() QMapData::Node *cur = e->forward[0]; update[0] = x.e; while (cur != e) { - Node *concreteNode = concrete(cur); - node_create(x.d, update, concreteNode->key, concreteNode->value); + QT_TRY { + Node *concreteNode = concrete(cur); + node_create(x.d, update, concreteNode->key, concreteNode->value); + } QT_CATCH(...) { + freeData(x.d); + QT_RETHROW; + } cur = cur->forward[0]; } x.d->insertInOrder = false; @@ -923,7 +948,8 @@ public: inline QMultiMap operator+(const QMultiMap &other) const { QMultiMap result = *this; result += other; return result; } -#ifndef Q_NO_USING_KEYWORD +#if !defined(Q_NO_USING_KEYWORD) && !defined(Q_CC_RVCT) + // RVCT compiler doesn't handle using-keyword right when used functions are overloaded in child class using QMap<Key, T>::contains; using QMap<Key, T>::remove; using QMap<Key, T>::count; @@ -993,7 +1019,12 @@ Q_INLINE_TEMPLATE int QMultiMap<Key, T>::remove(const Key &key, const T &value) typename QMap<Key, T>::iterator end(QMap<Key, T>::end()); while (i != end && !qMapLessThanKey<Key>(key, i.key())) { if (i.value() == value) { +#if defined(Q_CC_RVCT) + // RVCT has problems with scoping, apparently. + i = QMap<Key, T>::erase(i); +#else i = erase(i); +#endif ++n; } else { ++i; diff --git a/src/corelib/tools/qpodlist_p.h b/src/corelib/tools/qpodlist_p.h index 76778dd..ed51182 100644 --- a/src/corelib/tools/qpodlist_p.h +++ b/src/corelib/tools/qpodlist_p.h @@ -53,9 +53,7 @@ // We mean it. // -#include <QtCore/qcontainerfwd.h> -#include <QtCore/qglobal.h> -#include <new> +#include <QtCore/qvarlengtharray.h> QT_BEGIN_HEADER @@ -63,87 +61,29 @@ QT_BEGIN_NAMESPACE QT_MODULE(Core) -template<class T, int Prealloc> -class QPodList +template <typename T, int Prealloc> +class QPodList : public QVarLengthArray<T, Prealloc> { + using QVarLengthArray<T, Prealloc>::s; + using QVarLengthArray<T, Prealloc>::a; + using QVarLengthArray<T, Prealloc>::ptr; + using QVarLengthArray<T, Prealloc>::realloc; public: - inline explicit QPodList(int size = 0); + inline explicit QPodList(int size = 0) + : QVarLengthArray<T, Prealloc>(size) + {} - inline QPodList(const QPodList<T, Prealloc> &other) - : a(Prealloc), s(0), ptr(reinterpret_cast<T *>(array)) + inline void insert(int idx, const T &t) { - append(other.constData(), other.size()); - } - - inline ~QPodList() { - if (ptr != reinterpret_cast<T *>(array)) - qFree(ptr); - } - inline QPodList<T, Prealloc> &operator=(const QPodList<T, Prealloc> &other) - { - if (this != &other) { - clear(); - append(other.constData(), other.size()); - } - return *this; - } - - inline int size() const { return s; } - inline int count() const { return s; } - inline bool isEmpty() const { return (s == 0); } - inline void resize(int size); - inline void clear() { resize(0); } - - inline int capacity() const { return a; } - inline void reserve(int size); - - inline T &operator[](int idx) { - Q_ASSERT(idx >= 0 && idx < s); - return ptr[idx]; - } - inline const T &operator[](int idx) const { - Q_ASSERT(idx >= 0 && idx < s); - return ptr[idx]; - } - - inline const T &at(int idx) const { - Q_ASSERT(idx >= 0 && idx < s); - return ptr[idx]; - } - - inline const T &first() const { - return at(0); - } - - inline T& append() { - const int idx = s++; - if (s == a) - realloc(s, s<<1); - return ptr[idx]; - } - inline void append(const T &t) { - append() = t; - } - - inline T& insert(int idx) { - Q_ASSERT(idx >= 0 && idx <= s); const int sz = s++; if (s == a) - realloc(s, s<<1); + realloc(s, s << 1); ::memmove(ptr + idx + 1, ptr + idx, (sz - idx) * sizeof(T)); - return ptr[idx]; - } - inline void insert(int idx, const T &t) { - insert(idx) = t; + ptr[idx] = t; } - inline void removeAt(int idx) { - Q_ASSERT(idx >= 0 && idx < s); - ::memmove(ptr + idx, ptr + idx + 1, (s - idx - 1) * sizeof(T)); - --s; - } - - inline void removeAll(const T &t) { + inline void removeAll(const T &t) + { int i = 0; for (int j = 0; j < s; ++j) { if (ptr[j] != t) @@ -152,110 +92,22 @@ public: s = i; } - inline int indexOf(const T &t, int from = 0) const { - if (from < 0) - from = qMax(from + s, 0); - if (from < s) { - const T *n = ptr + from - 1; - const T *e = ptr + s; - while (++n != e) - if (*n == t) - return n - ptr; - } - return -1; - } - - inline bool contains(const T &t) const { - return indexOf(t) >= 0; + inline void removeAt(int idx) + { + Q_ASSERT(idx >= 0 && idx < s); + ::memmove(ptr + idx, ptr + idx + 1, (s - idx - 1) * sizeof(T)); + --s; } - inline T takeFirst() { + inline T takeFirst() + { Q_ASSERT(s > 0); T tmp = ptr[0]; removeAt(0); return tmp; } - - inline T *data() { return ptr; } - inline const T *data() const { return ptr; } - inline const T * constData() const { return ptr; } - -private: - void append(const T *buf, int size); - void realloc(int size, int alloc); - - int a; - int s; - T *ptr; - union { - // ### Qt 5: Use 'Prealloc * sizeof(T)' as array size - char array[sizeof(qint64) * (((Prealloc * sizeof(T)) / sizeof(qint64)) + 1)]; - qint64 q_for_alignment_1; - double q_for_alignment_2; - }; }; -template <class T, int Prealloc> -Q_INLINE_TEMPLATE QPodList<T, Prealloc>::QPodList(int asize) - : s(asize) { - if (s > Prealloc) { - ptr = reinterpret_cast<T *>(qMalloc(s * sizeof(T))); - a = s; - } else { - ptr = reinterpret_cast<T *>(array); - a = Prealloc; - } -} - -template <class T, int Prealloc> -Q_INLINE_TEMPLATE void QPodList<T, Prealloc>::resize(int asize) -{ realloc(asize, qMax(asize, a)); } - -template <class T, int Prealloc> -Q_INLINE_TEMPLATE void QPodList<T, Prealloc>::reserve(int asize) -{ if (asize > a) realloc(s, asize); } - -template <class T, int Prealloc> -Q_OUTOFLINE_TEMPLATE void QPodList<T, Prealloc>::append(const T *abuf, int asize) -{ - Q_ASSERT(abuf); - if (asize <= 0) - return; - - const int idx = s; - const int news = s + asize; - if (news >= a) - realloc(news, news<<1); - else - s = news; - - qMemCopy(&ptr[idx], abuf, asize * sizeof(T)); -} - -template <class T, int Prealloc> -Q_OUTOFLINE_TEMPLATE void QPodList<T, Prealloc>::realloc(int asize, int aalloc) -{ - Q_ASSERT(aalloc >= asize); - T *oldPtr = ptr; - int osize = s; - s = asize; - - if (aalloc != a) { - ptr = reinterpret_cast<T *>(qMalloc(aalloc * sizeof(T))); - if (ptr) { - a = aalloc; - qMemCopy(ptr, oldPtr, osize * sizeof(T)); - } else { - ptr = oldPtr; - s = 0; - asize = 0; - } - } - - if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != ptr) - qFree(oldPtr); -} - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index 8c3662a..fd3b33e 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -1225,7 +1225,7 @@ private: int yyPos; // the position of the next character to read int yyLen; // the length of yyIn int yyCh; // the last character read - QRegExpCharClass *yyCharClass; // attribute for Tok_CharClass tokens + QScopedPointer<QRegExpCharClass> yyCharClass; // attribute for Tok_CharClass tokens int yyMinRep; // attribute for Tok_Quantifier int yyMaxRep; // ditto QString yyError; // syntax error or overflow during parsing? @@ -1310,14 +1310,19 @@ void QRegExpMatchState::prepareForMatch(QRegExpEngine *eng) int ns = eng->s.size(); // number of states int ncap = eng->ncap; #ifndef QT_NO_REGEXP_OPTIM - slideTabSize = qMax(eng->minl + 1, 16); + int newSlideTabSize = qMax(eng->minl + 1, 16); #else - slideTabSize = 0; + int newSlideTabSize = 0; #endif int numCaptures = eng->numCaptures(); - capturedSize = 2 + 2 * numCaptures; - bigArray = (int *)realloc(bigArray, ((3 + 4 * ncap) * ns + 4 * ncap + slideTabSize + capturedSize)*sizeof(int)); + int newCapturedSize = 2 + 2 * numCaptures; + bigArray = q_check_ptr((int *)realloc(bigArray, ((3 + 4 * ncap) * ns + 4 * ncap + newSlideTabSize + newCapturedSize)*sizeof(int))); + // set all internal variables only _after_ bigArray is realloc'ed + // to prevent a broken regexp in oom case + + slideTabSize = newSlideTabSize; + capturedSize = newCapturedSize; inNextStack = bigArray; memset(inNextStack, -1, ns * sizeof(int)); curStack = inNextStack + ns; @@ -3118,7 +3123,7 @@ void QRegExpEngine::startTokenizer(const QChar *rx, int len) yyPos = 0; yyLen = len; yyCh = getChar(); - yyCharClass = new QRegExpCharClass; + yyCharClass.reset(new QRegExpCharClass); yyMinRep = 0; yyMaxRep = 0; yyError = QString(); @@ -3312,8 +3317,7 @@ int QRegExpEngine::parse(const QChar *pattern, int len) #endif box.cat(middleBox); box.cat(rightBox); - delete yyCharClass; - yyCharClass = 0; + yyCharClass.reset(0); #ifndef QT_NO_REGEXP_CAPTURE for (int i = 0; i < nf; ++i) { @@ -3610,10 +3614,15 @@ static void derefEngine(QRegExpEngine *eng, const QRegExpEngineKey &key) #if !defined(QT_NO_REGEXP_OPTIM) if (globalEngineCache()) { QMutexLocker locker(mutex()); - globalEngineCache()->insert(key, eng, 4 + key.pattern.length() / 4); - } - else + QT_TRY { + globalEngineCache()->insert(key, eng, 4 + key.pattern.length() / 4); + } QT_CATCH(const std::bad_alloc &) { + // in case of an exception (e.g. oom), just delete the engine + delete eng; + } + } else { delete eng; + } #else Q_UNUSED(key); delete eng; diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index 9a2b614..4370cc6 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -245,9 +245,7 @@ public: inline void clear() { if(!buffers.isEmpty()) { - QByteArray tmp = buffers[0]; - buffers.clear(); - buffers << tmp; + buffers.erase(buffers.begin() + 1, buffers.end()); if (buffers.at(0).size() != basicBlockSize) buffers[0].resize(basicBlockSize); } diff --git a/src/corelib/tools/qscopedpointer.cpp b/src/corelib/tools/qscopedpointer.cpp new file mode 100644 index 0000000..5b8991e --- /dev/null +++ b/src/corelib/tools/qscopedpointer.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscopedpointer.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QScopedPointer + \brief The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon destruction. + \since 4.6 + \reentrant + \ingroup misc + + Managing heap allocated objects manually is hard and error prone, with the + common result that code leaks memory and is hard to maintain. + QScopedPointer is a small utility class that heavily simplifies this by + assigning stack-based memory ownership to heap allocations, more generally + called resource acquisition is initialization(RAII). + + QScopedPointer guarantees that the object pointed to will get deleted when + the current scope dissapears. + + Consider this function which does heap allocations, and have various exit points: + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 0 + + It's encumbered by the manual delete calls. With QScopedPointer, the code + can be simplified to: + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 1 + + The code the compiler generates for QScopedPointer is the same as when + writing it manually. Code that makes use of \a delete are candidates for + QScopedPointer usage (and if not, possibly another type of smart pointer + such as QSharedPointer). QScopedPointer intentionally has no copy + constructor or assignment operator, such that ownership and lifetime is + clearly communicated. + + The const qualification on a regular C++ pointer can also be expressed with + a QScopedPointer: + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 2 + + \section1 Custom cleanup handlers + + Arrays as well as pointers that have been allocated with \c malloc must + not be deleted using \c delete. QScopedPointer's second template parameter + can be used for custom cleanup handlers. + + The following custom cleanup handlers exist: + + \list + \i QScopedPointerDeleter - the default, deletes the pointer using \c delete + \i QScopedPointerArrayDeleter - deletes the pointer using \c{delete []}. Use + this handler for pointers that were allocated with \c{new []}. + \i QScopedPointerPodDeleter - deletes the pointer using \c{free()}. Use this + handler for pointers that were allocated with \c{malloc()}. + \endlist + + You can pass your own classes as handlers, provided that they have a public + static function \c{void cleanup(T *pointer)}. + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 5 + + \section1 Forward Declared Pointers + + Classes that are forward declared can be used within QScopedPointer, as + long as the destructor of the forward declared class is available whenever + a QScopedPointer needs to clean up. + + Concretely, this means that all classes containing a QScopedPointer that + points to a forward declared class must have non-inline constructors, + destructors and assignment operators: + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 4 + + Otherwise, the compiler output a warning about not being able to destruct + \c MyPrivateClass. + + \sa QSharedPointer +*/ + +/*! + \fn QScopedPointer::QScopedPointer(T *p = 0) + + Constructs this QScopedPointer instance and sets its pointer to \a p. +*/ + +/*! + \fn QScopedPointer::~QScopedPointer() + + Destroys this QScopedPointer object. Delete the object its pointer points + to. +*/ + +/*! + \fn T *QScopedPointer::data() const + + Returns the value of the pointer referenced by this object. QScopedPointer + still owns the object pointed to. +*/ + +/*! + \fn T &QScopedPointer::operator*() const + + Provides access to the scoped pointer's object. + + If the contained pointer is \c null, behavior is undefined. + \sa isNull() +*/ + +/*! + \fn T *QScopedPointer::operator->() const + + Provides access to the scoped pointer's object. + + If the contained pointer is \c null, behavior is undefined. + + \sa isNull() +*/ + +/*! + \fn QScopedPointer::operator bool() const + + Returns \c true if this object is not \c null. This function is suitable + for use in \tt if-constructs, like: + + \snippet doc/src/snippets/code/src_corelib_tools_qscopedpointer.cpp 3 + + \sa isNull() +*/ + +/*! + \fn bool QScopedPointer::operator==(const QScopedPointer<T> &other) const + + Equality operator. Returns true if the scoped pointer \a other + is pointing to the same object as this pointer, otherwise returns false. +*/ + + +/*! + \fn bool QScopedPointer::operator!=(const QScopedPointer<T> &other) const + + Inequality operator. Returns true if the scoped pointer \a other + is not pointing to the same object as this pointer, otherwise returns false. +*/ + +/*! + \fn bool QScopedPointer::isNull() const + + Returns \c true if this object is holding a pointer that is \c null. +*/ + +/*! + \fn void QScopedPointer::reset(T *other = 0) + + Deletes the existing object it is pointing to if any, and sets its pointer to + \a other. QScopedPointer now owns \a other and will delete it in its + destructor. +*/ + +/*! + \fn T *QScopedPointer::take() + + Returns the value of the pointer referenced by this object. The pointer of this + QScopedPointer object will be reset to \c null. + + Callers of this function take ownership of the pointer. +*/ + +/*! \fn bool QScopedPointer::operator!() const + + Returns \c true if the pointer referenced by this object is \c null, otherwise + returns \c false. + + \sa isNull() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qscopedpointer.h b/src/corelib/tools/qscopedpointer.h new file mode 100644 index 0000000..344964b --- /dev/null +++ b/src/corelib/tools/qscopedpointer.h @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCOPEDPOINTER_H +#define QSCOPEDPOINTER_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +QT_MODULE(Core) + +template <typename T> +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 + // classes in the QScopedPointer documentation. + typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; + (void) sizeof(IsIncompleteType); + + delete pointer; + } +}; + +template <typename T> +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 + // classes in the QScopedPointer documentation. + typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; + (void) sizeof(IsIncompleteType); + + delete [] pointer; + } +}; + +struct QScopedPointerPodDeleter +{ + static inline void cleanup(void *pointer) { if (pointer) qFree(pointer); } +}; + +template <typename T, typename Cleanup = QScopedPointerDeleter<T> > +class QScopedPointer +{ +#ifndef Q_CC_NOKIAX86 + typedef T *QScopedPointer:: *RestrictedBool; +#endif +public: + explicit inline QScopedPointer(T *p = 0) : d(p) + { + } + + inline ~QScopedPointer() + { + T *oldD = this->d; + Cleanup::cleanup(oldD); + this->d = 0; + } + + inline T &operator*() const + { + Q_ASSERT(d); + return *d; + } + + inline T *operator->() const + { + Q_ASSERT(d); + return d; + } + + inline bool operator==(const QScopedPointer<T, Cleanup> &other) const + { + return d == other.d; + } + + inline bool operator!=(const QScopedPointer<T, Cleanup> &other) const + { + return d != other.d; + } + + inline bool operator!() const + { + return !d; + } + +#if defined(Q_CC_NOKIAX86) || defined(Q_QDOC) + inline operator bool() const + { + return isNull() ? 0 : &QScopedPointer::d; + } +#else + inline operator RestrictedBool() const + { + return isNull() ? 0 : &QScopedPointer::d; + } +#endif + + inline T *data() const + { + return d; + } + + inline bool isNull() const + { + return !d; + } + + inline void reset(T *other = 0) + { + if (d == other) + return; + T *oldD = d; + d = other; + Cleanup::cleanup(oldD); + } + + inline T *take() + { + T *oldD = d; + d = 0; + return oldD; + } + + typedef T *pointer; + +protected: + T *d; + +private: + Q_DISABLE_COPY(QScopedPointer) +}; + +template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> > +class QScopedArrayPointer : public QScopedPointer<T, Cleanup> +{ +public: + explicit inline QScopedArrayPointer(T *p = 0) + : QScopedPointer<T, Cleanup>(p) + { + } + + inline T &operator[](int i) + { + return this->d[i]; + } + + inline const T &operator[](int i) const + { + return this->d[i]; + } + + inline bool operator==(const QScopedArrayPointer<T, Cleanup> &other) const + { + return this->d == other.d; + } + + inline bool operator!=(const QScopedArrayPointer<T, Cleanup> &other) const + { + return this->d != other.d; + } + +private: + Q_DISABLE_COPY(QScopedArrayPointer) +}; + +/* Internal helper class - exposes the data through data_ptr (legacy from QShared). + Required for some internal Qt classes, do not use otherwise. */ +template <typename T, typename Cleanup = QScopedPointerDeleter<T> > +class QCustomScopedPointer : public QScopedPointer<T, Cleanup> +{ +public: + explicit inline QCustomScopedPointer(T *p = 0) + : QScopedPointer<T, Cleanup>(p) + { + } + + inline T *&data_ptr() + { + return this->d; + } + + inline bool operator==(const QCustomScopedPointer<T, Cleanup> &other) const + { + return this->d == other.d; + } + + inline bool operator!=(const QCustomScopedPointer<T, Cleanup> &other) const + { + return this->d != other.d; + } + +private: + Q_DISABLE_COPY(QCustomScopedPointer) +}; + +/* Internal helper class - a handler for QShared* classes, to be used in QCustomScopedPointer */ +template <typename T> +class QScopedPointerSharedDeleter +{ +public: + static inline void cleanup(T *d) + { + if (d && !d->ref.deref()) + delete d; + } +}; + +/* Internal. + This class is basically a scoped pointer pointing to a ref-counted object + */ +template <typename T> +class QScopedSharedPointer : public QCustomScopedPointer<T, QScopedPointerSharedDeleter<T> > +{ +public: + explicit inline QScopedSharedPointer(T *p = 0) + : QCustomScopedPointer<T, QScopedPointerSharedDeleter<T> >(p) + { + } + + inline void detach() + { + qAtomicDetach(this->d); + } + + inline void assign(T *other) + { + if (this->d == other) + return; + if (other) + other->ref.ref(); + T *oldD = this->d; + this->d = other; + QScopedPointerSharedDeleter<T>::cleanup(oldD); + } + + inline bool operator==(const QScopedSharedPointer<T> &other) const + { + return this->d == other.d; + } + + inline bool operator!=(const QScopedSharedPointer<T> &other) const + { + return this->d != other.d; + } + +private: + Q_DISABLE_COPY(QScopedSharedPointer) +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QSCOPEDPOINTER_H diff --git a/src/corelib/tools/qshareddata.cpp b/src/corelib/tools/qshareddata.cpp index dd98499..c96e058 100644 --- a/src/corelib/tools/qshareddata.cpp +++ b/src/corelib/tools/qshareddata.cpp @@ -231,7 +231,7 @@ QT_BEGIN_NAMESPACE In the member function documentation, \e{d pointer} always refers to the internal pointer to the shared data object. - \sa QSharedData, QExplicitlySharedDataPointer + \sa QSharedData, QExplicitlySharedDataPointer, QScopedPointer, QSharedPointer */ /*! \fn T& QSharedDataPointer::operator*() diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index e3e1db6..f0e85d2 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -102,12 +102,17 @@ that it only detaches if QExplicitlySharedDataPointer::detach() is explicitly called (hence the name). + QScopedPointer simply holds a pointer to a heap allocated object and + deletes it in its destructor. This class is useful when an object needs to + be heap allocated and deleted, but no more. QScopedPointer is lightweight, + it makes no use of additional structure or reference counting. + Finally, QPointer holds a pointer to a QObject-derived object, but it does so weakly. QPointer can be replaced by QWeakPointer in almost all cases, since they have the same functionality. See \l{QWeakPointer#tracking-qobject} for more information. - \sa QSharedDataPointer, QWeakPointer + \sa QSharedDataPointer, QWeakPointer, QScopedPointer */ /*! @@ -182,7 +187,7 @@ QWeakPointers created from QObject should never be passed to code that hasn't been recompiled. - \sa QSharedPointer + \sa QSharedPointer, QScopedPointer */ /*! diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index ba479f9..6eb0109 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -118,7 +118,9 @@ namespace QtSharedPointer { template <class T> class Basic { +#ifndef Q_CC_NOKIAX86 typedef T *Basic:: *RestrictedBool; +#endif public: typedef T Type; typedef T element_type; @@ -131,7 +133,11 @@ namespace QtSharedPointer { inline T *data() const { return value; } inline bool isNull() const { return !data(); } +#ifndef Q_CC_NOKIAX86 inline operator RestrictedBool() const { return isNull() ? 0 : &Basic::value; } +#else + inline operator bool() const { return isNull() ? 0 : &Basic::value; } +#endif inline bool operator !() const { return isNull(); } inline T &operator*() const { return *data(); } inline T *operator->() const { return data(); } @@ -415,12 +421,12 @@ public: { internalConstruct(ptr); } template <typename Deleter> - inline QSharedPointer(T *ptr, Deleter d) { internalConstruct(ptr, d); } + inline QSharedPointer(T *ptr, Deleter d) { BaseClass::internalConstruct(ptr, d); } inline QSharedPointer(const QSharedPointer<T> &other) : BaseClass(other) { } inline QSharedPointer<T> &operator=(const QSharedPointer<T> &other) { - internalCopy(other); + BaseClass::internalCopy(other); return *this; } @@ -432,7 +438,7 @@ public: inline QSharedPointer<T> &operator=(const QSharedPointer<X> &other) { QSHAREDPOINTER_VERIFY_AUTO_CAST(T, X); // if you get an error in this line, the cast is invalid - internalCopy(other); + BaseClass::internalCopy(other); return *this; } @@ -493,7 +499,9 @@ public: template <class T> class QWeakPointer { +#ifndef Q_CC_NOKIAX86 typedef T *QWeakPointer:: *RestrictedBool; +#endif typedef QtSharedPointer::ExternalRefCountData Data; public: @@ -506,7 +514,11 @@ public: typedef ptrdiff_t difference_type; inline bool isNull() const { return d == 0 || d->strongref == 0 || value == 0; } +#ifndef Q_CC_NOKIAX86 inline operator RestrictedBool() const { return isNull() ? 0 : &QWeakPointer::value; } +#else + inline operator bool() const { return isNull() ? 0 : &QWeakPointer::value; } +#endif inline bool operator !() const { return isNull(); } inline T *data() const { return d == 0 || d->strongref == 0 ? 0 : value; } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index dc23142..c36bf6e 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -978,6 +978,7 @@ QString::QString(const QChar *unicode, int size) d->ref.ref(); } else { d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + Q_CHECK_PTR(d); d->ref = 1; d->alloc = d->size = size; d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; @@ -1001,6 +1002,7 @@ QString::QString(int size, QChar ch) d->ref.ref(); } else { d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + Q_CHECK_PTR(d); d->ref = 1; d->alloc = d->size = size; d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; @@ -1023,6 +1025,7 @@ QString::QString(int size, QChar ch) QString::QString(int size, Qt::Initialization) { d = (Data*) qMalloc(sizeof(Data)+size*sizeof(QChar)); + Q_CHECK_PTR(d); d->ref = 1; d->alloc = d->size = size; d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; @@ -1042,7 +1045,9 @@ QString::QString(int size, Qt::Initialization) */ QString::QString(QChar ch) { - d = (Data *)qMalloc(sizeof(Data) + sizeof(QChar)); + void *buf = qMalloc(sizeof(Data) + sizeof(QChar)); + Q_CHECK_PTR(buf); + d = reinterpret_cast<Data *>(buf); d->ref = 1; d->alloc = d->size = 1; d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; @@ -1212,8 +1217,7 @@ void QString::realloc(int alloc) { if (d->ref != 1 || d->data != d->array) { Data *x = static_cast<Data *>(qMalloc(sizeof(Data) + alloc * sizeof(QChar))); - if (!x) - return; + Q_CHECK_PTR(x); x->size = qMin(alloc, d->size); ::memcpy(x->array, d->data, x->size * sizeof(QChar)); x->array[x->size] = 0; @@ -1235,12 +1239,9 @@ void QString::realloc(int alloc) asciiCache->remove(d); } #endif - Data *x = static_cast<Data *>(qRealloc(d, sizeof(Data) + alloc * sizeof(QChar))); - if (!x) - return; - x->alloc = alloc; - x->data = x->array; - d = x; + d = static_cast<Data *>(q_check_ptr(qRealloc(d, sizeof(Data) + alloc * sizeof(QChar)))); + d->alloc = alloc; + d->data = d->array; } } @@ -1394,6 +1395,7 @@ QString& QString::insert(int i, const QChar *unicode, int size) if (s >= d->data && s < d->data + d->alloc) { // Part of me - take a copy ushort *tmp = static_cast<ushort *>(qMalloc(size * sizeof(QChar))); + Q_CHECK_PTR(tmp); memcpy(tmp, s, size * sizeof(QChar)); insert(i, reinterpret_cast<const QChar *>(tmp), size); qFree(tmp); @@ -1748,51 +1750,69 @@ QString &QString::replace(const QString &before, const QString &after, Qt::CaseS */ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar *after, int alen) { - if (blen == alen) { - detach(); - for (int i = 0; i < nIndices; ++i) - memcpy(d->data + indices[i], after, alen * sizeof(QChar)); - } else if (alen < blen) { + // copy *after in case it lies inside our own d->data area + // (which we could possibly invalidate via a realloc or corrupt via memcpy operations.) + QChar *afterBuffer = const_cast<QChar *>(after); + if (after >= reinterpret_cast<QChar *>(d->data) && after < reinterpret_cast<QChar *>(d->data) + d->size) { + afterBuffer = static_cast<QChar *>(qMalloc(alen*sizeof(QChar))); + Q_CHECK_PTR(afterBuffer); + ::memcpy(afterBuffer, after, alen*sizeof(QChar)); + } + + QT_TRY { detach(); - uint to = indices[0]; - if (alen) - memcpy(d->data+to, after, alen*sizeof(QChar)); - to += alen; - uint movestart = indices[0] + blen; - for (int i = 1; i < nIndices; ++i) { - int msize = indices[i] - movestart; - if (msize > 0) { - memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); - to += msize; + if (blen == alen) { + // replace in place + for (int i = 0; i < nIndices; ++i) + memcpy(d->data + indices[i], afterBuffer, alen * sizeof(QChar)); + } else if (alen < blen) { + // replace from front + uint to = indices[0]; + if (alen) + memcpy(d->data+to, after, alen*sizeof(QChar)); + to += alen; + uint movestart = indices[0] + blen; + for (int i = 1; i < nIndices; ++i) { + int msize = indices[i] - movestart; + if (msize > 0) { + memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); + to += msize; + } + if (alen) { + memcpy(d->data + to, afterBuffer, alen*sizeof(QChar)); + to += alen; + } + movestart = indices[i] + blen; } - if (alen) { - memcpy(d->data + to, after, alen*sizeof(QChar)); - to += alen; + int msize = d->size - movestart; + if (msize > 0) + memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); + resize(d->size - nIndices*(blen-alen)); + } else { + // replace from back + int adjust = nIndices*(alen-blen); + int newLen = d->size + adjust; + int moveend = d->size; + resize(newLen); + + while (nIndices) { + --nIndices; + int movestart = indices[nIndices] + blen; + int insertstart = indices[nIndices] + nIndices*(alen-blen); + int moveto = insertstart + alen; + memmove(d->data + moveto, d->data + movestart, + (moveend - movestart)*sizeof(QChar)); + memcpy(d->data + insertstart, afterBuffer, alen*sizeof(QChar)); + moveend = movestart-blen; } - movestart = indices[i] + blen; - } - int msize = d->size - movestart; - if (msize > 0) - memmove(d->data + to, d->data + movestart, msize * sizeof(QChar)); - resize(d->size - nIndices*(blen-alen)); - } else { - // we have a table of replacement positions, use them for fast replacing - int adjust = nIndices*(alen-blen); - int newLen = d->size + adjust; - int moveend = d->size; - resize(newLen); - - while (nIndices) { - --nIndices; - int movestart = indices[nIndices] + blen; - int insertstart = indices[nIndices] + nIndices*(alen-blen); - int moveto = insertstart + alen; - memmove(d->data + moveto, d->data + movestart, - (moveend - movestart)*sizeof(QChar)); - memcpy(d->data + insertstart, after, alen*sizeof(QChar)); - moveend = movestart-blen; } + } QT_CATCH(const std::bad_alloc &) { + if (afterBuffer != after) + qFree(afterBuffer); + QT_RETHROW; } + if (afterBuffer != after) + qFree(afterBuffer); } /*! @@ -1820,21 +1840,7 @@ QString &QString::replace(const QChar *before, int blen, if (alen == 0 && blen == 0) return *this; - // protect against before or after being part of this - const QChar *a = after; - const QChar *b = before; - if (after >= (const QChar *)d->data && after < (const QChar *)d->data + d->size) { - QChar *copy = (QChar *)malloc(alen*sizeof(QChar)); - memcpy(copy, after, alen*sizeof(QChar)); - a = copy; - } - if (before >= (const QChar *)d->data && before < (const QChar *)d->data + d->size) { - QChar *copy = (QChar *)malloc(blen*sizeof(QChar)); - memcpy(copy, before, blen*sizeof(QChar)); - b = copy; - } - - QStringMatcher matcher(b, blen, cs); + QStringMatcher matcher(before, blen, cs); int index = 0; while (1) { @@ -1853,7 +1859,7 @@ QString &QString::replace(const QChar *before, int blen, if (!pos) break; - replace_helper(indices, pos, blen, a, alen); + replace_helper(indices, pos, blen, after, alen); if (index == -1) break; @@ -1861,11 +1867,6 @@ QString &QString::replace(const QChar *before, int blen, index += pos*(alen-blen); } - if (a != after) - ::free((QChar *)a); - if (b != before) - ::free((QChar *)b); - return *this; } @@ -3600,6 +3601,7 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) if (size < 0) size = qstrlen(str); d = static_cast<Data *>(qMalloc(sizeof(Data) + size * sizeof(QChar))); + Q_CHECK_PTR(d); d->ref = 1; d->alloc = d->size = size; d->clean = d->asciiCache = d->simpletext = d->righttoleft = d->capacity = 0; @@ -3607,7 +3609,7 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) ushort *i = d->data; d->array[size] = '\0'; while (size--) - *i++ = (uchar)*str++; + *i++ = (uchar)*str++; } return d; } @@ -6923,6 +6925,7 @@ void QString::updateProperties() const QString QString::fromRawData(const QChar *unicode, int size) { Data *x = static_cast<Data *>(qMalloc(sizeof(Data))); + Q_CHECK_PTR(x); if (unicode) { x->data = (ushort *)unicode; } else { diff --git a/src/corelib/tools/qtextboundaryfinder.cpp b/src/corelib/tools/qtextboundaryfinder.cpp index 956a963..bf1d654 100644 --- a/src/corelib/tools/qtextboundaryfinder.cpp +++ b/src/corelib/tools/qtextboundaryfinder.cpp @@ -38,11 +38,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "private/qharfbuzz_p.h" #include <QtCore/qtextboundaryfinder.h> #include <QtCore/qvarlengtharray.h> #include <private/qunicodetables_p.h> #include <qdebug.h> +#include "private/qharfbuzz_p.h" QT_BEGIN_NAMESPACE @@ -176,6 +176,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(const QTextBoundaryFinder &other) , freePrivate(true) { d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes)); + Q_CHECK_PTR(d); memcpy(d, other.d, length*sizeof(HB_CharAttributes)); } @@ -193,8 +194,11 @@ QTextBoundaryFinder &QTextBoundaryFinder::operator=(const QTextBoundaryFinder &o length = other.length; pos = other.pos; freePrivate = true; - - d = (QTextBoundaryFinderPrivate *) realloc(d, length*sizeof(HB_CharAttributes)); + + QTextBoundaryFinderPrivate *newD = (QTextBoundaryFinderPrivate *) + realloc(d, length*sizeof(HB_CharAttributes)); + Q_CHECK_PTR(newD); + d = newD; memcpy(d, other.d, length*sizeof(HB_CharAttributes)); return *this; @@ -221,6 +225,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QString &strin , freePrivate(true) { d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes)); + Q_CHECK_PTR(d); init(t, chars, length, d->attributes); } @@ -248,6 +253,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QChar *chars, freePrivate = false; } else { d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes)); + Q_CHECK_PTR(d); freePrivate = true; } init(t, chars, length, d->attributes); diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 83f69b6..2998244 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -52,6 +52,9 @@ QT_BEGIN_NAMESPACE QT_MODULE(Core) +template<class T, int Prealloc> +class QPodList; + // Prealloc = 256 by default, specified in qcontainerfwd.h template<class T, int Prealloc> class QVarLengthArray @@ -122,6 +125,7 @@ public: inline const T * constData() const { return ptr; } private: + friend class QPodList<T, Prealloc>; void realloc(int size, int alloc); int a; @@ -140,6 +144,7 @@ Q_INLINE_TEMPLATE QVarLengthArray<T, Prealloc>::QVarLengthArray(int asize) : s(asize) { if (s > Prealloc) { ptr = reinterpret_cast<T *>(qMalloc(s * sizeof(T))); + Q_CHECK_PTR(ptr); a = s; } else { ptr = reinterpret_cast<T *>(array); @@ -161,25 +166,24 @@ Q_INLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::reserve(int asize) { if (asize > a) realloc(s, asize); } template <class T, int Prealloc> -Q_OUTOFLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::append(const T *abuf, int asize) +Q_OUTOFLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::append(const T *abuf, int increment) { Q_ASSERT(abuf); - if (asize <= 0) + if (increment <= 0) return; - const int idx = s; - const int news = s + asize; - if (news >= a) - realloc(s, qMax(s<<1, news)); - s = news; + const int asize = s + increment; + + if (asize >= a) + realloc(s, qMax(s*2, asize)); if (QTypeInfo<T>::isComplex) { - T *i = ptr + idx; - T *j = i + asize; - while (i < j) - new (i++) T(*abuf++); + // call constructor for new objects (which can throw) + while (s < asize) + new (ptr+(s++)) T(*abuf++); } else { - qMemCopy(&ptr[idx], abuf, asize * sizeof(T)); + qMemCopy(&ptr[s], abuf, increment * sizeof(T)); + s = asize; } } @@ -189,46 +193,58 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::realloc(int asize, int a Q_ASSERT(aalloc >= asize); T *oldPtr = ptr; int osize = s; - s = asize; + // s = asize; if (aalloc != a) { ptr = reinterpret_cast<T *>(qMalloc(aalloc * sizeof(T))); + Q_CHECK_PTR(ptr); if (ptr) { + s = 0; a = aalloc; if (QTypeInfo<T>::isStatic) { - T *i = ptr + osize; - T *j = oldPtr + osize; - while (i != ptr) { - new (--i) T(*--j); - j->~T(); + QT_TRY { + while (s < asize) { + new (ptr+s) T(*(oldPtr+s)); + (oldPtr+s)->~T(); + s++; + } + } QT_CATCH(...) { + // clean up all the old objects and then free the old ptr + int sClean = s; + while (sClean < osize) + (oldPtr+(sClean++))->~T(); + if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != ptr) + qFree(oldPtr); + QT_RETHROW; } } else { - qMemCopy(ptr, oldPtr, osize * sizeof(T)); + qMemCopy(ptr, oldPtr, qMin(asize, osize) * sizeof(T)); + s = asize; } } else { ptr = oldPtr; - s = 0; - asize = 0; + return; } } if (QTypeInfo<T>::isComplex) { - if (asize < osize) { - T *i = oldPtr + osize; - T *j = oldPtr + asize; - while (i-- != j) - i->~T(); - } else { - T *i = ptr + asize; - T *j = ptr + osize; - while (i != j) - new (--i) T; - } + while (osize > asize) + (oldPtr+(--osize))->~T(); + if( oldPtr == ptr ) + s = osize; } if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != ptr) qFree(oldPtr); + + if (QTypeInfo<T>::isComplex) { + // call default constructor for new objects (which can throw) + while (s < asize) + new (ptr+(s++)) T; + } else { + s = asize; + } } QT_END_NAMESPACE diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 8e454ed..67c4f74 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -50,6 +50,7 @@ QVectorData QVectorData::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, tr QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) { QVectorData* p = (QVectorData *)qMalloc(sizeofTypedData + (size - 1) * sizeofT); + Q_CHECK_PTR(p); ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT); return p; } diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 2555bb5..85f92ea 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -77,6 +77,7 @@ struct Q_CORE_EXPORT QVectorData static QVectorData shared_null; // ### Qt 5: rename to 'allocate()'. The current name causes problems for // some debugges when the QVector is member of a class within an unnamed namespace. + // ### Qt 5: can be removed completely. (Ralf) static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); }; @@ -372,7 +373,9 @@ QVector<T> &QVector<T>::operator=(const QVector<T> &v) template <typename T> inline QVectorData *QVector<T>::malloc(int aalloc) { - return static_cast<QVectorData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + QVectorData *data = static_cast<QVectorData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + Q_CHECK_PTR(data); + return data; } template <typename T> @@ -423,74 +426,79 @@ void QVector<T>::free(Data *x) template <typename T> void QVector<T>::realloc(int asize, int aalloc) { - T *j, *i, *b; + T *pOld; + T *pNew; union { QVectorData *d; Data *p; } x; x.d = d; - if (QTypeInfo<T>::isComplex && aalloc == d->alloc && d->ref == 1) { - // pure resize - i = p->array + d->size; - j = p->array + asize; - if (i > j) { - while (i-- != j) - i->~T(); - } else { - while (j-- != i) - new (j) T; + if (QTypeInfo<T>::isComplex && asize < d->size && d->ref == 1 ) { + // call the destructor on all objects that need to be + // destroyed when shrinking + pOld = p->array + d->size; + pNew = p->array + asize; + while (asize < d->size) { + (--pOld)->~T(); + d->size--; } - d->size = asize; - return; } if (aalloc != d->alloc || d->ref != 1) { // (re)allocate memory if (QTypeInfo<T>::isStatic) { x.d = malloc(aalloc); + Q_CHECK_PTR(x.p); + x.d->size = 0; } else if (d->ref != 1) { - x.d = QVectorData::malloc(sizeOfTypedData(), aalloc, sizeof(T), d); - } else { + x.d = malloc(aalloc); + Q_CHECK_PTR(x.p); if (QTypeInfo<T>::isComplex) { - // call the destructor on all objects that need to be - // destroyed when shrinking - if (asize < d->size) { - j = p->array + asize; - i = p->array + d->size; - while (i-- != j) - i->~T(); - i = p->array + asize; - } + x.d->size = 0; + } else { + ::memcpy(x.p, p, sizeOfTypedData() + (qMin(aalloc, d->alloc) - 1) * sizeof(T)); + x.d->size = d->size; + } + } else { + QT_TRY { + QVectorData *mem = static_cast<QVectorData *>(qRealloc(p, sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + Q_CHECK_PTR(mem); + x.d = d = mem; + x.d->size = d->size; + } QT_CATCH (const std::bad_alloc &) { + if (aalloc > d->alloc) // ignore the error in case we are just shrinking. + QT_RETHROW; } - x.d = d = static_cast<QVectorData *>(qRealloc(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T))); } x.d->ref = 1; + x.d->alloc = aalloc; x.d->sharable = true; x.d->capacity = d->capacity; - } + if (QTypeInfo<T>::isComplex) { - if (asize < d->size) { - j = p->array + asize; - i = x.p->array + asize; - } else { - // construct all new objects when growing - i = x.p->array + asize; - j = x.p->array + d->size; - while (i != j) - new (--i) T; - j = p->array + d->size; - } - if (i != j) { + QT_TRY { + pOld = p->array + x.d->size; + pNew = x.p->array + x.d->size; // copy objects from the old array into the new array - b = x.p->array; - while (i != b) - new (--i) T(*--j); + while (x.d->size < qMin(asize, d->size)) { + new (pNew++) T(*pOld++); + x.d->size++; + } + // construct all new objects when growing + while (x.d->size < asize) { + new (pNew++) T; + x.d->size++; + } + } QT_CATCH (...) { + free(x.p); + QT_RETHROW; } - } else if (asize > d->size) { + + } else if (asize > x.d->size) { // initialize newly allocated memory to 0 - qMemSet(x.p->array + d->size, 0, (asize - d->size) * sizeof(T)); + qMemSet(x.p->array + x.d->size, 0, (asize - x.d->size) * sizeof(T)); } x.d->size = asize; - x.d->alloc = aalloc; + if (d != x.d) { if (!d->ref.deref()) free(p); diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 44fbb62..e6c6169 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -42,7 +42,8 @@ HEADERS += \ tools/qtimeline.h \ tools/qunicodetables_p.h \ tools/qvarlengtharray.h \ - tools/qvector.h + tools/qvector.h \ + tools/qscopedpointer.h SOURCES += \ @@ -73,6 +74,7 @@ SOURCES += \ tools/qvector.cpp \ tools/qvsnprintf.cpp +symbian:SOURCES+=tools/qlocale_symbian.cpp #zlib support contains(QT_CONFIG, zlib) { @@ -109,4 +111,7 @@ SOURCES += ../3rdparty/harfbuzz/src/harfbuzz-buffer.c \ tools/qharfbuzz.cpp HEADERS += tools/qharfbuzz_p.h -!macx-icc:!vxworks:unix:LIBS += -lm +INCLUDEPATH += ../3rdparty/md5 \ + ../3rdparty/md4 + +!macx-icc:unix:!symbian:!vxworks:LIBS += -lm diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index fa396f1..b5fe241 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -434,7 +434,6 @@ QXmlStreamReader::~QXmlStreamReader() Q_D(QXmlStreamReader); if (d->deleteDevice) delete d->device; - delete d; } /*! \fn bool QXmlStreamReader::hasError() const @@ -823,7 +822,9 @@ inline void QXmlStreamReaderPrivate::reallocateStack() { stack_size <<= 1; sym_stack = reinterpret_cast<Value*> (qRealloc(sym_stack, stack_size * sizeof(Value))); + Q_CHECK_PTR(sym_stack); state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + Q_CHECK_PTR(sym_stack); } @@ -3135,8 +3136,6 @@ QXmlStreamWriter::QXmlStreamWriter(QString *string) */ QXmlStreamWriter::~QXmlStreamWriter() { - Q_D(QXmlStreamWriter); - delete d; } diff --git a/src/corelib/xml/qxmlstream.h b/src/corelib/xml/qxmlstream.h index 3353cd4..c7ebf28 100644 --- a/src/corelib/xml/qxmlstream.h +++ b/src/corelib/xml/qxmlstream.h @@ -48,6 +48,7 @@ #include <QtCore/QString> #include <QtCore/QVector> +#include <QtCore/QScopedPointer> QT_BEGIN_HEADER @@ -392,7 +393,7 @@ public: private: Q_DISABLE_COPY(QXmlStreamReader) Q_DECLARE_PRIVATE(QXmlStreamReader) - QXmlStreamReaderPrivate *d_ptr; + QScopedPointer<QXmlStreamReaderPrivate> d_ptr; }; #endif // QT_NO_XMLSTREAMREADER @@ -465,7 +466,7 @@ public: private: Q_DISABLE_COPY(QXmlStreamWriter) Q_DECLARE_PRIVATE(QXmlStreamWriter) - QXmlStreamWriterPrivate *d_ptr; + QScopedPointer<QXmlStreamWriterPrivate> d_ptr; }; #endif // QT_NO_XMLSTREAMWRITER diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h index d4b5503..a9babc7 100644 --- a/src/corelib/xml/qxmlstream_p.h +++ b/src/corelib/xml/qxmlstream_p.h @@ -652,6 +652,7 @@ public: if (tos + extraCapacity + 1 > cap) { cap = qMax(tos + extraCapacity + 1, cap << 1 ); data = reinterpret_cast<T *>(qRealloc(data, cap * sizeof(T))); + Q_CHECK_PTR(data); } } |