summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io')
-rw-r--r--src/corelib/io/io.pri82
-rw-r--r--src/corelib/io/qabstractfileengine.cpp1219
-rw-r--r--src/corelib/io/qabstractfileengine.h247
-rw-r--r--src/corelib/io/qabstractfileengine_p.h79
-rw-r--r--src/corelib/io/qbuffer.cpp475
-rw-r--r--src/corelib/io/qbuffer.h112
-rw-r--r--src/corelib/io/qdatastream.cpp1260
-rw-r--r--src/corelib/io/qdatastream.h427
-rw-r--r--src/corelib/io/qdebug.cpp51
-rw-r--r--src/corelib/io/qdebug.h262
-rw-r--r--src/corelib/io/qdir.cpp2472
-rw-r--r--src/corelib/io/qdir.h264
-rw-r--r--src/corelib/io/qdiriterator.cpp559
-rw-r--r--src/corelib/io/qdiriterator.h97
-rw-r--r--src/corelib/io/qfile.cpp1638
-rw-r--r--src/corelib/io/qfile.h204
-rw-r--r--src/corelib/io/qfile_p.h95
-rw-r--r--src/corelib/io/qfileinfo.cpp1427
-rw-r--r--src/corelib/io/qfileinfo.h187
-rw-r--r--src/corelib/io/qfileinfo_p.h145
-rw-r--r--src/corelib/io/qfilesystemwatcher.cpp620
-rw-r--r--src/corelib/io/qfilesystemwatcher.h89
-rw-r--r--src/corelib/io/qfilesystemwatcher_dnotify.cpp461
-rw-r--r--src/corelib/io/qfilesystemwatcher_dnotify_p.h131
-rw-r--r--src/corelib/io/qfilesystemwatcher_inotify.cpp376
-rw-r--r--src/corelib/io/qfilesystemwatcher_inotify_p.h95
-rw-r--r--src/corelib/io/qfilesystemwatcher_kqueue.cpp334
-rw-r--r--src/corelib/io/qfilesystemwatcher_kqueue_p.h95
-rw-r--r--src/corelib/io/qfilesystemwatcher_p.h120
-rw-r--r--src/corelib/io/qfilesystemwatcher_win.cpp333
-rw-r--r--src/corelib/io/qfilesystemwatcher_win_p.h143
-rw-r--r--src/corelib/io/qfsfileengine.cpp873
-rw-r--r--src/corelib/io/qfsfileengine.h121
-rw-r--r--src/corelib/io/qfsfileengine_iterator.cpp82
-rw-r--r--src/corelib/io/qfsfileengine_iterator_p.h92
-rw-r--r--src/corelib/io/qfsfileengine_iterator_unix.cpp140
-rw-r--r--src/corelib/io/qfsfileengine_iterator_win.cpp183
-rw-r--r--src/corelib/io/qfsfileengine_p.h174
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp1021
-rw-r--r--src/corelib/io/qfsfileengine_win.cpp2242
-rw-r--r--src/corelib/io/qiodevice.cpp1748
-rw-r--r--src/corelib/io/qiodevice.h253
-rw-r--r--src/corelib/io/qiodevice_p.h109
-rw-r--r--src/corelib/io/qprocess.cpp1827
-rw-r--r--src/corelib/io/qprocess.h202
-rw-r--r--src/corelib/io/qprocess_p.h230
-rw-r--r--src/corelib/io/qprocess_unix.cpp1380
-rw-r--r--src/corelib/io/qprocess_win.cpp909
-rw-r--r--src/corelib/io/qresource.cpp1460
-rw-r--r--src/corelib/io/qresource.h104
-rw-r--r--src/corelib/io/qresource_iterator.cpp92
-rw-r--r--src/corelib/io/qresource_iterator_p.h80
-rw-r--r--src/corelib/io/qresource_p.h119
-rw-r--r--src/corelib/io/qsettings.cpp3822
-rw-r--r--src/corelib/io/qsettings.h313
-rw-r--r--src/corelib/io/qsettings_mac.cpp654
-rw-r--r--src/corelib/io/qsettings_p.h313
-rw-r--r--src/corelib/io/qsettings_win.cpp957
-rw-r--r--src/corelib/io/qtemporaryfile.cpp706
-rw-r--r--src/corelib/io/qtemporaryfile.h108
-rw-r--r--src/corelib/io/qtextstream.cpp3387
-rw-r--r--src/corelib/io/qtextstream.h375
-rw-r--r--src/corelib/io/qurl.cpp5999
-rw-r--r--src/corelib/io/qurl.h281
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp170
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h161
66 files changed, 44786 insertions, 0 deletions
diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri
new file mode 100644
index 0000000..3690d4b
--- /dev/null
+++ b/src/corelib/io/io.pri
@@ -0,0 +1,82 @@
+# Qt core io module
+
+HEADERS += \
+ io/qabstractfileengine.h \
+ io/qabstractfileengine_p.h \
+ io/qbuffer.h \
+ io/qdatastream.h \
+ io/qdebug.h \
+ io/qdir.h \
+ io/qdiriterator.h \
+ io/qfile.h \
+ io/qfileinfo.h \
+ io/qfileinfo_p.h \
+ io/qiodevice.h \
+ io/qiodevice_p.h \
+ io/qprocess.h \
+ io/qprocess_p.h \
+ io/qtextstream.h \
+ io/qtemporaryfile.h \
+ io/qresource_p.h \
+ io/qresource_iterator_p.h \
+ io/qurl.h \
+ io/qsettings.h \
+ io/qsettings_p.h \
+ io/qfsfileengine.h \
+ io/qfsfileengine_p.h \
+ io/qfsfileengine_iterator_p.h \
+ io/qfilesystemwatcher.h \
+ io/qfilesystemwatcher_p.h
+
+SOURCES += \
+ io/qabstractfileengine.cpp \
+ io/qbuffer.cpp \
+ io/qdatastream.cpp \
+ io/qdebug.cpp \
+ io/qdir.cpp \
+ io/qdiriterator.cpp \
+ io/qfile.cpp \
+ io/qfileinfo.cpp \
+ io/qiodevice.cpp \
+ io/qprocess.cpp \
+ io/qtextstream.cpp \
+ io/qtemporaryfile.cpp \
+ io/qresource.cpp \
+ io/qresource_iterator.cpp \
+ io/qurl.cpp \
+ io/qsettings.cpp \
+ io/qfsfileengine.cpp \
+ io/qfsfileengine_iterator.cpp \
+ io/qfilesystemwatcher.cpp
+
+win32 {
+ SOURCES += io/qsettings_win.cpp
+ SOURCES += io/qprocess_win.cpp
+ SOURCES += io/qfsfileengine_win.cpp
+
+ SOURCES += io/qfsfileengine_iterator_win.cpp
+ SOURCES += io/qfilesystemwatcher_win.cpp
+ HEADERS += io/qfilesystemwatcher_win_p.h
+ HEADERS += io/qwindowspipewriter_p.h
+ SOURCES += io/qwindowspipewriter.cpp
+} else:unix {
+ SOURCES += io/qfsfileengine_unix.cpp
+ SOURCES += io/qfsfileengine_iterator_unix.cpp
+ SOURCES += io/qprocess_unix.cpp
+ mac:SOURCES += io/qsettings_mac.cpp
+
+ linux-*:{
+ SOURCES += \
+ io/qfilesystemwatcher_inotify.cpp \
+ io/qfilesystemwatcher_dnotify.cpp
+
+ HEADERS += \
+ io/qfilesystemwatcher_inotify_p.h \
+ io/qfilesystemwatcher_dnotify_p.h
+ }
+
+ freebsd-*|macx-*|darwin-*|openbsd-*:{
+ SOURCES += io/qfilesystemwatcher_kqueue.cpp
+ HEADERS += io/qfilesystemwatcher_kqueue_p.h
+ }
+}
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
new file mode 100644
index 0000000..bedc121
--- /dev/null
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -0,0 +1,1219 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractfileengine.h"
+#include "private/qabstractfileengine_p.h"
+#include "qdatetime.h"
+#include "qmutex.h"
+#include "qvariant.h"
+// built-in handlers
+#include "qfsfileengine.h"
+#include "qdiriterator.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractFileEngineHandler
+ \reentrant
+
+ \brief The QAbstractFileEngineHandler class provides a way to register
+ custom file engines with your application.
+
+ \ingroup io
+ \since 4.1
+
+ QAbstractFileEngineHandler is a factory for creating QAbstractFileEngine
+ objects (file engines), which are used internally by QFile, QFileInfo, and
+ QDir when working with files and directories.
+
+ When you open a file, Qt chooses a suitable file engine by passing the
+ file name from QFile or QDir through an internal list of registered file
+ engine handlers. The first handler to recognize the file name is used to
+ create the engine. Qt provides internal file engines for working with
+ regular files and resources, but you can also register your own
+ QAbstractFileEngine subclasses.
+
+ To install an application-specific file engine, you subclass
+ QAbstractFileEngineHandler and reimplement create(). When you instantiate
+ the handler (e.g. by creating an instance on the stack or on the heap), it
+ will automatically register with Qt. (The latest registered handler takes
+ precedence over existing handlers.)
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qabstractfileengine.cpp 0
+
+ When the handler is destroyed, it is automatically removed from Qt.
+
+ The most common approach to registering a handler is to create an instance
+ as part of the start-up phase of your application. It is also possible to
+ limit the scope of the file engine handler to a particular area of
+ interest (e.g. a special file dialog that needs a custom file engine). By
+ creating the handler inside a local scope, you can precisely control the
+ area in which your engine will be applied without disturbing file
+ operations in other parts of your application.
+
+ \sa QAbstractFileEngine, QAbstractFileEngine::create()
+*/
+
+/*
+ All application-wide handlers are stored in this list. The mutex must be
+ acquired to ensure thread safety.
+ */
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, fileEngineHandlerMutex, (QMutex::Recursive))
+static bool qt_abstractfileenginehandlerlist_shutDown = false;
+class QAbstractFileEngineHandlerList : public QList<QAbstractFileEngineHandler *>
+{
+public:
+ ~QAbstractFileEngineHandlerList()
+ {
+ QMutexLocker locker(fileEngineHandlerMutex());
+ qt_abstractfileenginehandlerlist_shutDown = true;
+ }
+};
+Q_GLOBAL_STATIC(QAbstractFileEngineHandlerList, fileEngineHandlers)
+
+/*!
+ Constructs a file handler and registers it with Qt. Once created this
+ handler's create() function will be called (along with all the other
+ handlers) for any paths used. The most recently created handler that
+ recognizes the given path (i.e. that returns a QAbstractFileEngine) is
+ used for the new path.
+
+ \sa create()
+ */
+QAbstractFileEngineHandler::QAbstractFileEngineHandler()
+{
+ QMutexLocker locker(fileEngineHandlerMutex());
+ fileEngineHandlers()->prepend(this);
+}
+
+/*!
+ Destroys the file handler. This will automatically unregister the handler
+ from Qt.
+ */
+QAbstractFileEngineHandler::~QAbstractFileEngineHandler()
+{
+ QMutexLocker locker(fileEngineHandlerMutex());
+ // Remove this handler from the handler list only if the list is valid.
+ if (!qt_abstractfileenginehandlerlist_shutDown)
+ fileEngineHandlers()->removeAll(this);
+}
+
+/*!
+ \fn QAbstractFileEngine *QAbstractFileEngineHandler::create(const QString &fileName) const
+
+ Creates a file engine for file \a fileName. Returns 0 if this
+ file handler cannot handle \a fileName.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qabstractfileengine.cpp 1
+
+ \sa QAbstractFileEngine::create()
+*/
+
+/*!
+ Creates and returns a QAbstractFileEngine suitable for processing \a
+ fileName.
+
+ You should not need to call this function; use QFile, QFileInfo or
+ QDir directly instead.
+
+ If you reimplemnt this function, it should only return file
+ engines that knows how to handle \a fileName; otherwise, it should
+ return 0.
+
+ \sa QAbstractFileEngineHandler
+*/
+QAbstractFileEngine *QAbstractFileEngine::create(const QString &fileName)
+{
+ QMutexLocker locker(fileEngineHandlerMutex());
+
+ // check for registered handlers that can load the file
+ for (int i = 0; i < fileEngineHandlers()->size(); i++) {
+ if (QAbstractFileEngine *ret = fileEngineHandlers()->at(i)->create(fileName))
+ return ret;
+ }
+
+#ifdef QT_BUILD_CORE_LIB
+ if (!fileName.startsWith(QLatin1Char('/'))) {
+ int prefixSeparator = fileName.indexOf(QLatin1Char(':'));
+ if (prefixSeparator > 1) {
+ QString prefix = fileName.left(prefixSeparator);
+ QString fileNameWithoutPrefix = fileName.mid(prefixSeparator + 1).prepend(QLatin1Char('/'));
+ const QStringList &paths = QDir::searchPaths(prefix);
+ for (int i = 0; i < paths.count(); i++) {
+ QString path = paths.at(i);
+ path.append(fileNameWithoutPrefix);
+ QAbstractFileEngine *engine = create(path);
+ if (engine && (engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) {
+ return engine;
+ }
+ delete engine;
+ }
+ }
+ }
+#endif
+
+#ifdef QT_NO_FSFILEENGINE
+ return 0;
+#else
+ // fall back to regular file engine
+ return new QFSFileEngine(fileName);
+#endif
+}
+
+/*!
+ \class QAbstractFileEngine
+ \reentrant
+
+ \brief The QAbstractFileEngine class provides an abstraction for accessing
+ the filesystem.
+
+ \ingroup io
+ \since 4.1
+
+ The QDir, QFile, and QFileInfo classes all make use of a
+ QAbstractFileEngine internally. If you create your own QAbstractFileEngine
+ subclass (and register it with Qt by creating a QAbstractFileEngineHandler
+ subclass), your file engine will be used when the path is one that your
+ file engine handles.
+
+ A QAbstractFileEngine refers to one file or one directory. If the referent
+ is a file, the setFileName(), rename(), and remove() functions are
+ applicable. If the referent is a directory the mkdir(), rmdir(), and
+ entryList() functions are applicable. In all cases the caseSensitive(),
+ isRelativePath(), fileFlags(), ownerId(), owner(), and fileTime()
+ functions are applicable.
+
+ A QAbstractFileEngine subclass can be created to do synchronous network I/O
+ based file system operations, local file system operations, or to operate
+ as a resource system to access file based resources.
+
+ \sa QAbstractFileEngineHandler
+*/
+
+/*!
+ \enum QAbstractFileEngine::FileName
+
+ These values are used to request a file name in a particular
+ format.
+
+ \value DefaultName The same filename that was passed to the
+ QAbstractFileEngine.
+ \value BaseName The name of the file excluding the path.
+ \value PathName The path to the file excluding the base name.
+ \value AbsoluteName The absolute path to the file (including
+ the base name).
+ \value AbsolutePathName The absolute path to the file (excluding
+ the base name).
+ \value LinkName The full file name of the file that this file is a
+ link to. (This will be empty if this file is not a link.)
+ \value CanonicalName Often very similar to LinkName. Will return the true path to the file.
+ \value CanonicalPathName Same as CanonicalName, excluding the base name.
+ \value BundleName Returns the name of the bundle implies BundleType is set.
+
+ \sa fileName(), setFileName()
+*/
+
+/*!
+ \enum QAbstractFileEngine::FileFlag
+
+ The permissions and types of a file, suitable for OR'ing together.
+
+ \value ReadOwnerPerm The owner of the file has permission to read
+ it.
+ \value WriteOwnerPerm The owner of the file has permission to
+ write to it.
+ \value ExeOwnerPerm The owner of the file has permission to
+ execute it.
+ \value ReadUserPerm The current user has permission to read the
+ file.
+ \value WriteUserPerm The current user has permission to write to
+ the file.
+ \value ExeUserPerm The current user has permission to execute the
+ file.
+ \value ReadGroupPerm Members of the current user's group have
+ permission to read the file.
+ \value WriteGroupPerm Members of the current user's group have
+ permission to write to the file.
+ \value ExeGroupPerm Members of the current user's group have
+ permission to execute the file.
+ \value ReadOtherPerm All users have permission to read the file.
+ \value WriteOtherPerm All users have permission to write to the
+ file.
+ \value ExeOtherPerm All users have permission to execute the file.
+
+ \value LinkType The file is a link to another file (or link) in
+ the file system (i.e. not a file or directory).
+ \value FileType The file is a regular file to the file system
+ (i.e. not a link or directory)
+ \value BundleType The file is a Mac OS X bundle implies DirectoryType
+ \value DirectoryType The file is a directory in the file system
+ (i.e. not a link or file).
+
+ \value HiddenFlag The file is hidden.
+ \value ExistsFlag The file actually exists in the file system.
+ \value RootFlag The file or the file pointed to is the root of the filesystem.
+ \value LocalDiskFlag The file resides on the local disk and can be passed to standard file functions.
+ \value Refresh Passing this flag will force the file engine to refresh all flags.
+
+ \omitvalue PermsMask
+ \omitvalue TypesMask
+ \omitvalue FlagsMask
+ \omitvalue FileInfoAll
+
+ \sa fileFlags(), setFileName()
+*/
+
+/*!
+ \enum QAbstractFileEngine::FileTime
+
+ These are used by the fileTime() function.
+
+ \value CreationTime When the file was created.
+ \value ModificationTime When the file was most recently modified.
+ \value AccessTime When the file was most recently accessed (e.g.
+ read or written to).
+
+ \sa setFileName()
+*/
+
+/*!
+ \enum QAbstractFileEngine::FileOwner
+
+ \value OwnerUser The user who owns the file.
+ \value OwnerGroup The group who owns the file.
+
+ \sa owner(), ownerId(), setFileName()
+*/
+
+/*!
+ Constructs a new QAbstractFileEngine that does not refer to any file or directory.
+
+ \sa setFileName()
+ */
+QAbstractFileEngine::QAbstractFileEngine() : d_ptr(new QAbstractFileEnginePrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ \internal
+
+ Constructs a QAbstractFileEngine.
+ */
+QAbstractFileEngine::QAbstractFileEngine(QAbstractFileEnginePrivate &dd) : d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the QAbstractFileEngine.
+ */
+QAbstractFileEngine::~QAbstractFileEngine()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+
+/*!
+ \fn bool QAbstractFileEngine::open(QIODevice::OpenMode mode)
+
+ Opens the file in the specified \a mode. Returns true if the file
+ was successfully opened; otherwise returns false.
+
+ The \a mode is an OR combination of QIODevice::OpenMode and
+ QIODevice::HandlingMode values.
+*/
+bool QAbstractFileEngine::open(QIODevice::OpenMode openMode)
+{
+ Q_UNUSED(openMode);
+ return false;
+}
+
+/*!
+ Closes the file, returning true if successful; otherwise returns false.
+
+ The default implementation always returns false.
+*/
+bool QAbstractFileEngine::close()
+{
+ return false;
+}
+
+/*!
+ Flushes the open file, returning true if successful; otherwise returns
+ false.
+
+ The default implementation always returns false.
+*/
+bool QAbstractFileEngine::flush()
+{
+ return false;
+}
+
+/*!
+ Returns the size of the file.
+*/
+qint64 QAbstractFileEngine::size() const
+{
+ return 0;
+}
+
+/*!
+ Returns the current file position.
+
+ This is the position of the data read/write head of the file.
+*/
+qint64 QAbstractFileEngine::pos() const
+{
+ return 0;
+}
+
+/*!
+ \fn bool QAbstractFileEngine::seek(qint64 offset)
+
+ Sets the file position to the given \a offset. Returns true if
+ the position was successfully set; otherwise returns false.
+
+ The offset is from the beginning of the file, unless the
+ file is sequential.
+
+ \sa isSequential()
+*/
+bool QAbstractFileEngine::seek(qint64 pos)
+{
+ Q_UNUSED(pos);
+ return false;
+}
+
+/*!
+ Returns true if the file is a sequential access device; returns
+ false if the file is a direct access device.
+
+ Operations involving size() and seek(int) are not valid on
+ sequential devices.
+*/
+bool QAbstractFileEngine::isSequential() const
+{
+ return false;
+}
+
+/*!
+ Requests that the file is deleted from the file system. If the
+ operation succeeds return true; otherwise return false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName() rmdir()
+ */
+bool QAbstractFileEngine::remove()
+{
+ return false;
+}
+
+/*!
+ Copies the contents of this file to a file with the name \a newName.
+ Returns true on success; otherwise, false is returned.
+*/
+bool QAbstractFileEngine::copy(const QString &newName)
+{
+ Q_UNUSED(newName);
+ return false;
+}
+
+/*!
+ Requests that the file be renamed to \a newName in the file
+ system. If the operation succeeds return true; otherwise return
+ false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName()
+ */
+bool QAbstractFileEngine::rename(const QString &newName)
+{
+ Q_UNUSED(newName);
+ return false;
+}
+
+/*!
+ Creates a link from the file currently specified by fileName() to
+ \a newName. What a link is depends on the underlying filesystem
+ (be it a shortcut on Windows or a symbolic link on Unix). Returns
+ true if successful; otherwise returns false.
+*/
+bool QAbstractFileEngine::link(const QString &newName)
+{
+ Q_UNUSED(newName);
+ return false;
+}
+
+/*!
+ Requests that the directory \a dirName be created. If
+ \a createParentDirectories is true, then any sub-directories in \a dirName
+ that don't exist must be created. If \a createParentDirectories is false then
+ any sub-directories in \a dirName must already exist for the function to
+ succeed. If the operation succeeds return true; otherwise return
+ false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName() rmdir() isRelativePath()
+ */
+bool QAbstractFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ Q_UNUSED(dirName);
+ Q_UNUSED(createParentDirectories);
+ return false;
+}
+
+/*!
+ Requests that the directory \a dirName is deleted from the file
+ system. When \a recurseParentDirectories is true, then any empty
+ parent-directories in \a dirName must also be deleted. If
+ \a recurseParentDirectories is false, only the \a dirName leaf-node
+ should be deleted. In most file systems a directory cannot be deleted
+ using this function if it is non-empty. If the operation succeeds
+ return true; otherwise return false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName() remove() mkdir() isRelativePath()
+ */
+bool QAbstractFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ Q_UNUSED(dirName);
+ Q_UNUSED(recurseParentDirectories);
+ return false;
+}
+
+/*!
+ Requests that the file be set to size \a size. If \a size is larger
+ than the current file then it is filled with 0's, if smaller it is
+ simply truncated. If the operations succceeds return true; otherwise
+ return false;
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa size()
+*/
+bool QAbstractFileEngine::setSize(qint64 size)
+{
+ Q_UNUSED(size);
+ return false;
+}
+
+/*!
+ Should return true if the underlying file system is case-sensitive;
+ otherwise return false.
+
+ This virtual function must be reimplemented by all subclasses.
+ */
+bool QAbstractFileEngine::caseSensitive() const
+{
+ return false;
+}
+
+/*!
+ Return true if the file referred to by this file engine has a
+ relative path; otherwise return false.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName()
+ */
+bool QAbstractFileEngine::isRelativePath() const
+{
+ return false;
+}
+
+/*!
+ Requests that a list of all the files matching the \a filters
+ list based on the \a filterNames in the file engine's directory
+ are returned.
+
+ Should return an empty list if the file engine refers to a file
+ rather than a directory, or if the directory is unreadable or does
+ not exist or if nothing matches the specifications.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName()
+ */
+QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ QStringList ret;
+ QDirIterator it(fileName(), filterNames, filters);
+ while (it.hasNext()) {
+ it.next();
+ ret << it.fileName();
+ }
+ return ret;
+}
+
+/*!
+ This function should return the set of OR'd flags that are true
+ for the file engine's file, and that are in the \a type's OR'd
+ members.
+
+ In your reimplementation you can use the \a type argument as an
+ optimization hint and only return the OR'd set of members that are
+ true and that match those in \a type; in other words you can
+ ignore any members not mentioned in \a type, thus avoiding some
+ potentially expensive lookups or system calls.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName()
+*/
+QAbstractFileEngine::FileFlags QAbstractFileEngine::fileFlags(FileFlags type) const
+{
+ Q_UNUSED(type);
+ return 0;
+}
+
+/*!
+ Requests that the file's permissions be set to \a perms. The argument
+ perms will be set to the OR-ed together combination of
+ QAbstractFileEngine::FileInfo, with only the QAbstractFileEngine::PermsMask being
+ honored. If the operations succceeds return true; otherwise return
+ false;
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa size()
+*/
+bool QAbstractFileEngine::setPermissions(uint perms)
+{
+ Q_UNUSED(perms);
+ return false;
+}
+
+/*!
+ Return the file engine's current file name in the format
+ specified by \a file.
+
+ If you don't handle some \c FileName possibilities, return the
+ file name set in setFileName() when an unhandled format is
+ requested.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName(), FileName
+ */
+QString QAbstractFileEngine::fileName(FileName file) const
+{
+ Q_UNUSED(file);
+ return QString();
+}
+
+/*!
+ If \a owner is \c OwnerUser return the ID of the user who owns
+ the file. If \a owner is \c OwnerGroup return the ID of the group
+ that own the file. If you can't determine the owner return -2.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa owner() setFileName(), FileOwner
+ */
+uint QAbstractFileEngine::ownerId(FileOwner owner) const
+{
+ Q_UNUSED(owner);
+ return 0;
+}
+
+/*!
+ If \a owner is \c OwnerUser return the name of the user who owns
+ the file. If \a owner is \c OwnerGroup return the name of the group
+ that own the file. If you can't determine the owner return
+ QString().
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa ownerId() setFileName(), FileOwner
+ */
+QString QAbstractFileEngine::owner(FileOwner owner) const
+{
+ Q_UNUSED(owner);
+ return QString();
+}
+
+/*!
+ If \a time is \c CreationTime, return when the file was created.
+ If \a time is \c ModificationTime, return when the file was most
+ recently modified. If \a time is \c AccessTime, return when the
+ file was most recently accessed (e.g. read or written).
+ If the time cannot be determined return QDateTime() (an invalid
+ date time).
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa setFileName(), QDateTime, QDateTime::isValid(), FileTime
+ */
+QDateTime QAbstractFileEngine::fileTime(FileTime time) const
+{
+ Q_UNUSED(time);
+ return QDateTime();
+}
+
+/*!
+ Sets the file engine's file name to \a file. This file name is the
+ file that the rest of the virtual functions will operate on.
+
+ This virtual function must be reimplemented by all subclasses.
+
+ \sa rename()
+ */
+void QAbstractFileEngine::setFileName(const QString &file)
+{
+ Q_UNUSED(file);
+}
+
+/*!
+ Returns the native file handle for this file engine. This handle must be
+ used with care; its value and type are platform specific, and using it
+ will most likely lead to non-portable code.
+*/
+int QAbstractFileEngine::handle() const
+{
+ return -1;
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the current position is at the end of the file; otherwise,
+ returns false.
+
+ This function bases its behavior on calling extension() with
+ AtEndExtension. If the engine does not support this extension, false is
+ returned.
+
+ \sa extension(), supportsExtension(), QFile::atEnd()
+*/
+bool QAbstractFileEngine::atEnd() const
+{
+ return const_cast<QAbstractFileEngine *>(this)->extension(AtEndExtension);
+}
+
+/*!
+ \since 4.4
+
+ Maps \a size bytes of the file into memory starting at \a offset.
+ Returns a pointer to the memory if successful; otherwise returns false
+ if, for example, an error occurs.
+
+ This function bases its behavior on calling extension() with
+ MapExtensionOption. If the engine does not support this extension, 0 is
+ returned.
+
+ \a flags is currently not used, but could be used in the future.
+
+ \sa unmap(), supportsExtension()
+ */
+
+uchar *QAbstractFileEngine::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
+{
+ MapExtensionOption option;
+ option.offset = offset;
+ option.size = size;
+ option.flags = flags;
+ MapExtensionReturn r;
+ if (!extension(MapExtension, &option, &r))
+ return 0;
+ return r.address;
+}
+
+/*!
+ \since 4.4
+
+ Unmaps the memory \a address. Returns true if the unmap succeeds; otherwise
+ returns false.
+
+ This function bases its behavior on calling extension() with
+ UnMapExtensionOption. If the engine does not support this extension, false is
+ returned.
+
+ \sa map(), supportsExtension()
+ */
+bool QAbstractFileEngine::unmap(uchar *address)
+{
+ UnMapExtensionOption options;
+ options.address = address;
+ return extension(UnMapExtension, &options);
+}
+
+/*!
+ \since 4.3
+ \class QAbstractFileEngineIterator
+ \brief The QAbstractFileEngineIterator class provides an iterator
+ interface for custom file engines.
+
+ If all you want is to iterate over entries in a directory, see
+ QDirIterator instead. This class is only for custom file engine authors.
+
+ QAbstractFileEngineIterator is a unidirectional single-use virtual
+ iterator that plugs into QDirIterator, providing transparent proxy
+ iteration for custom file engines.
+
+ You can subclass QAbstractFileEngineIterator to provide an iterator when
+ writing your own file engine. To plug the iterator into your file system,
+ you simply return an instance of this subclass from a reimplementation of
+ QAbstractFileEngine::beginEntryList().
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qabstractfileengine.cpp 2
+
+ QAbstractFileEngineIterator is associated with a path, name filters, and
+ entry filters. The path is the directory that the iterator lists entries
+ in. The name filters and entry filters are provided for file engines that
+ can optimize directory listing at the iterator level (e.g., network file
+ systems that need to minimize network traffic), but they can also be
+ ignored by the iterator subclass; QAbstractFileEngineIterator already
+ provides the required filtering logics in the matchesFilters() function.
+ You can call dirName() to get the directory name, nameFilters() to get a
+ stringlist of name filters, and filters() to get the entry filters.
+
+ The pure virual function hasNext() returns true if the current directory
+ has at least one more entry (i.e., the directory name is valid and
+ accessible, and we have not reached the end of the entry list), and false
+ otherwise. Reimplement next() to seek to the next entry.
+
+ The pure virtual function currentFileName() returns the name of the
+ current entry without advancing the iterator. The currentFilePath()
+ function is provided for convenience; it returns the full path of the
+ current entry.
+
+ Here is an example of how to implement an interator that returns each of
+ three fixed entries in sequence.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qabstractfileengine.cpp 3
+
+ Note: QAbstractFileEngineIterator does not deal with QDir::IteratorFlags;
+ it simply returns entries for a single directory.
+
+ \sa QDirIterator
+*/
+
+/*!
+ \enum QAbstractFileEngineIterator::EntryInfoType
+ \internal
+
+ This enum describes the different types of information that can be
+ requested through the QAbstractFileEngineIterator::entryInfo() function.
+*/
+
+/*!
+ \typedef QAbstractFileEngine::Iterator
+ \since 4.3
+ \relates QAbstractFileEngine
+
+ Synonym for QAbstractFileEngineIterator.
+*/
+
+class QAbstractFileEngineIteratorPrivate
+{
+public:
+ QString path;
+ QDir::Filters filters;
+ QStringList nameFilters;
+ QFileInfo fileInfo;
+};
+
+/*!
+ Constructs a QAbstractFileEngineIterator, using the entry filters \a
+ filters, and wildcard name filters \a nameFilters.
+*/
+QAbstractFileEngineIterator::QAbstractFileEngineIterator(QDir::Filters filters,
+ const QStringList &nameFilters)
+ : d(new QAbstractFileEngineIteratorPrivate)
+{
+ d->nameFilters = nameFilters;
+ d->filters = filters;
+}
+
+/*!
+ Destroys the QAbstractFileEngineIterator.
+
+ \sa QDirIterator
+*/
+QAbstractFileEngineIterator::~QAbstractFileEngineIterator()
+{
+ delete d;
+}
+
+/*!
+ Returns the path for this iterator. QDirIterator is responsible for
+ assigning this path; it cannot change during the iterator's lifetime.
+
+ \sa nameFilters(), filters()
+*/
+QString QAbstractFileEngineIterator::path() const
+{
+ return d->path;
+}
+
+/*!
+ \internal
+
+ Sets the iterator path to \a path. This function is called from within
+ QDirIterator.
+*/
+void QAbstractFileEngineIterator::setPath(const QString &path)
+{
+ d->path = path;
+}
+
+/*!
+ Returns the name filters for this iterator.
+
+ \sa QDir::nameFilters(), filters(), path()
+*/
+QStringList QAbstractFileEngineIterator::nameFilters() const
+{
+ return d->nameFilters;
+}
+
+/*!
+ Returns the entry filters for this iterator.
+
+ \sa QDir::filter(), nameFilters(), path()
+*/
+QDir::Filters QAbstractFileEngineIterator::filters() const
+{
+ return d->filters;
+}
+
+/*!
+ \fn QString QAbstractFileEngineIterator::currentFileName() const = 0
+
+ This pure virtual function returns the name of the current directory
+ entry, excluding the path.
+
+ \sa currentFilePath()
+*/
+
+/*!
+ Returns the path to the current directory entry. It's the same as
+ prepending path() to the return value of currentFileName().
+
+ \sa currentFileName()
+*/
+QString QAbstractFileEngineIterator::currentFilePath() const
+{
+ QString name = currentFileName();
+ if (!name.isNull()) {
+ QString tmp = path();
+ if (!tmp.isEmpty()) {
+ if (!tmp.endsWith(QLatin1Char('/')))
+ tmp.append(QLatin1Char('/'));
+ name.prepend(tmp);
+ }
+ }
+ return name;
+}
+
+/*!
+ The virtual function returns a QFileInfo for the current directory
+ entry. This function is provided for convenience. It can also be slightly
+ faster that creating a QFileInfo object yourself, as the object returned
+ by this function might contain cached information that QFileInfo otherwise
+ would have to access through the file engine.
+
+ \sa currentFileName()
+*/
+QFileInfo QAbstractFileEngineIterator::currentFileInfo() const
+{
+ QString path = currentFilePath();
+ if (d->fileInfo.filePath() != path)
+ d->fileInfo.setFile(path);
+
+ // return a shallow copy
+ return d->fileInfo;
+}
+
+/*!
+ \internal
+
+ Returns the entry info \a type for this iterator's current directory entry
+ as a QVariant. If \a type is undefined for this entry, a null QVariant is
+ returned.
+
+ \sa QAbstractFileEngine::beginEntryList(), QDir::beginEntryList()
+*/
+QVariant QAbstractFileEngineIterator::entryInfo(EntryInfoType type) const
+{
+ Q_UNUSED(type)
+ return QVariant();
+}
+
+/*!
+ \fn virtual QString QAbstractFileEngineIterator::next() = 0
+
+ This pure virtual function advances the iterator to the next directory
+ entry, and returns the file path to the current entry.
+
+ This function can optionally make use of nameFilters() and filters() to
+ optimize its performance.
+
+ Reimplement this function in a subclass to advance the iterator.
+
+ \sa QDirIterator::next()
+*/
+
+/*!
+ \fn virtual bool QAbstractFileEngineIterator::hasNext() const = 0
+
+ This pure virtual function returns true if there is at least one more
+ entry in the current directory (i.e., the iterator path is valid and
+ accessible, and the iterator has not reached the end of the entry list).
+
+ \sa QDirIterator::hasNext()
+*/
+
+/*!
+ Returns an instance of a QAbstractFileEngineIterator using \a filters for
+ entry filtering and \a filterNames for name filtering. This function is
+ called by QDirIterator to initiate directory iteration.
+
+ QDirIterator takes ownership of the returned instance, and deletes it when
+ it's done.
+
+ \sa QDirIterator
+*/
+QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
+{
+ Q_UNUSED(filters);
+ Q_UNUSED(filterNames);
+ return 0;
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::Iterator *QAbstractFileEngine::endEntryList()
+{
+ return 0;
+}
+
+/*!
+ Reads a number of characters from the file into \a data. At most
+ \a maxlen characters will be read.
+
+ Returns -1 if a fatal error occurs, or 0 if there are no bytes to
+ read.
+*/
+qint64 QAbstractFileEngine::read(char *data, qint64 maxlen)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxlen);
+ return -1;
+}
+
+/*!
+ Writes \a len bytes from \a data to the file. Returns the number
+ of characters written on success; otherwise returns -1.
+*/
+qint64 QAbstractFileEngine::write(const char *data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ return -1;
+}
+
+/*!
+ This function reads one line, terminated by a '\n' character, from the
+ file info \a data. At most \a maxlen characters will be read. The
+ end-of-line character is included.
+*/
+qint64 QAbstractFileEngine::readLine(char *data, qint64 maxlen)
+{
+ qint64 readSoFar = 0;
+ while (readSoFar < maxlen) {
+ char c;
+ qint64 readResult = read(&c, 1);
+ if (readResult <= 0)
+ return (readSoFar > 0) ? readSoFar : -1;
+ ++readSoFar;
+ *data++ = c;
+ if (c == '\n')
+ return readSoFar;
+ }
+ return readSoFar;
+}
+
+/*!
+ \enum QAbstractFileEngine::Extension
+ \since 4.3
+
+ This enum describes the types of extensions that the file engine can
+ support. Before using these extensions, you must verify that the extension
+ is supported (i.e., call supportsExtension()).
+
+ \value AtEndExtension Whether the current file position is at the end of
+ the file or not. This extension allows file engines that implement local
+ buffering to report end-of-file status without having to check the size of
+ the file. It is also useful for sequential files, where the size of the
+ file cannot be used to determine whether or not you have reached the end.
+ This extension returns true if the file is at the end; otherwise it returns
+ false. The input and output arguments to extension() are ignored.
+
+ \value FastReadLineExtension Whether the file engine provides a
+ fast implementation for readLine() or not. If readLine() remains
+ unimplemented in the file engine, QAbstractFileEngine will provide
+ an implementation based on calling read() repeatedly. If
+ supportsExtension() returns false for this extension, however,
+ QIODevice can provide a faster implementation by making use of its
+ internal buffer. For engines that already provide a fast readLine()
+ implementation, returning false for this extension can avoid
+ unnnecessary double-buffering in QIODevice.
+
+ \value MapExtension Whether the file engine provides the ability to map
+ a file to memory.
+
+ \value UnMapExtension Whether the file engine provides the ability to
+ unmap memory that was previously mapped.
+*/
+
+/*!
+ \class QAbstractFileEngine::ExtensionOption
+ \since 4.3
+ \brief provides an extended input argument to QAbstractFileEngine's
+ extension support.
+
+ \sa QAbstractFileEngine::extension()
+*/
+
+/*!
+ \class QAbstractFileEngine::ExtensionReturn
+ \since 4.3
+ \brief provides an extended output argument to QAbstractFileEngine's
+ extension support.
+
+ \sa QAbstractFileEngine::extension()
+*/
+
+/*!
+ \since 4.3
+
+ This virtual function can be reimplemented in a QAbstractFileEngine
+ subclass to provide support for extensions. The \a option argument is
+ provided as input to the extension, and this function can store output
+ results in \a output.
+
+ The behavior of this function is determined by \a extension; see the
+ Extension documentation for details.
+
+ You can call supportsExtension() to check if an extension is supported by
+ the file engine.
+
+ By default, no extensions are supported, and this function returns false.
+
+ \sa supportsExtension(), Extension
+*/
+bool QAbstractFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ Q_UNUSED(extension);
+ Q_UNUSED(option);
+ Q_UNUSED(output);
+ return false;
+}
+
+/*!
+ \since 4.3
+
+ This virtual function returns true if the file engine supports \a
+ extension; otherwise, false is returned. By default, no extensions are
+ supported.
+
+ \sa extension()
+*/
+bool QAbstractFileEngine::supportsExtension(Extension extension) const
+{
+ Q_UNUSED(extension);
+ return false;
+}
+
+/*!
+ Returns the QFile::FileError that resulted from the last failed
+ operation. If QFile::UnspecifiedError is returned, QFile will
+ use its own idea of the error status.
+
+ \sa QFile::FileError, errorString()
+ */
+QFile::FileError QAbstractFileEngine::error() const
+{
+ Q_D(const QAbstractFileEngine);
+ return d->fileError;
+}
+
+/*!
+ Returns the human-readable message appropriate to the current error
+ reported by error(). If no suitable string is available, an
+ empty string is returned.
+
+ \sa error()
+ */
+QString QAbstractFileEngine::errorString() const
+{
+ Q_D(const QAbstractFileEngine);
+ return d->errorString;
+}
+
+/*!
+ Sets the error type to \a error, and the error string to \a errorString.
+ Call this function to set the error values returned by the higher-level
+ classes.
+
+ \sa QFile::error(), QIODevice::errorString(), QIODevice::setErrorString()
+*/
+void QAbstractFileEngine::setError(QFile::FileError error, const QString &errorString)
+{
+ Q_D(QAbstractFileEngine);
+ d->fileError = error;
+ d->errorString = errorString;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qabstractfileengine.h b/src/corelib/io/qabstractfileengine.h
new file mode 100644
index 0000000..d742467
--- /dev/null
+++ b/src/corelib/io/qabstractfileengine.h
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTFILEENGINE_H
+#define QABSTRACTFILEENGINE_H
+
+#include <QtCore/qdir.h>
+
+#ifdef open
+#error qabstractfileengine.h must be included before any header file that defines open
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QFileExtension;
+class QFileExtensionResult;
+class QVariant;
+class QAbstractFileEngineIterator;
+class QAbstractFileEnginePrivate;
+
+class Q_CORE_EXPORT QAbstractFileEngine
+{
+public:
+ enum FileFlag {
+ //perms (overlaps the QFile::Permission)
+ ReadOwnerPerm = 0x4000, WriteOwnerPerm = 0x2000, ExeOwnerPerm = 0x1000,
+ ReadUserPerm = 0x0400, WriteUserPerm = 0x0200, ExeUserPerm = 0x0100,
+ ReadGroupPerm = 0x0040, WriteGroupPerm = 0x0020, ExeGroupPerm = 0x0010,
+ ReadOtherPerm = 0x0004, WriteOtherPerm = 0x0002, ExeOtherPerm = 0x0001,
+
+ //types
+ LinkType = 0x10000,
+ FileType = 0x20000,
+ DirectoryType = 0x40000,
+ BundleType = 0x80000,
+
+ //flags
+ HiddenFlag = 0x0100000,
+ LocalDiskFlag = 0x0200000,
+ ExistsFlag = 0x0400000,
+ RootFlag = 0x0800000,
+ Refresh = 0x1000000,
+
+ //masks
+ PermsMask = 0x0000FFFF,
+ TypesMask = 0x000F0000,
+ FlagsMask = 0x0FF00000,
+ FileInfoAll = FlagsMask | PermsMask | TypesMask
+ };
+ Q_DECLARE_FLAGS(FileFlags, FileFlag)
+
+ enum FileName {
+ DefaultName,
+ BaseName,
+ PathName,
+ AbsoluteName,
+ AbsolutePathName,
+ LinkName,
+ CanonicalName,
+ CanonicalPathName,
+ BundleName
+ };
+ enum FileOwner {
+ OwnerUser,
+ OwnerGroup
+ };
+ enum FileTime {
+ CreationTime,
+ ModificationTime,
+ AccessTime
+ };
+
+ virtual ~QAbstractFileEngine();
+
+ virtual bool open(QIODevice::OpenMode openMode);
+ virtual bool close();
+ virtual bool flush();
+ virtual qint64 size() const;
+ virtual qint64 pos() const;
+ virtual bool seek(qint64 pos);
+ virtual bool isSequential() const;
+ virtual bool remove();
+ virtual bool copy(const QString &newName);
+ virtual bool rename(const QString &newName);
+ virtual bool link(const QString &newName);
+ virtual bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ virtual bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
+ virtual bool setSize(qint64 size);
+ virtual bool caseSensitive() const;
+ virtual bool isRelativePath() const;
+ virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+ virtual FileFlags fileFlags(FileFlags type=FileInfoAll) const;
+ virtual bool setPermissions(uint perms);
+ virtual QString fileName(FileName file=DefaultName) const;
+ virtual uint ownerId(FileOwner) const;
+ virtual QString owner(FileOwner) const;
+ virtual QDateTime fileTime(FileTime time) const;
+ virtual void setFileName(const QString &file);
+ virtual int handle() const;
+ bool atEnd() const;
+ uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
+ bool unmap(uchar *ptr);
+
+ typedef QAbstractFileEngineIterator Iterator;
+ virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+ virtual Iterator *endEntryList();
+
+ virtual qint64 read(char *data, qint64 maxlen);
+ virtual qint64 readLine(char *data, qint64 maxlen);
+ virtual qint64 write(const char *data, qint64 len);
+
+ QFile::FileError error() const;
+ QString errorString() const;
+
+ enum Extension {
+ AtEndExtension,
+ FastReadLineExtension,
+ MapExtension,
+ UnMapExtension
+ };
+ class ExtensionOption
+ {};
+ class ExtensionReturn
+ {};
+
+ class MapExtensionOption : public ExtensionOption {
+ public:
+ qint64 offset;
+ qint64 size;
+ QFile::MemoryMapFlags flags;
+ };
+ class MapExtensionReturn : public ExtensionReturn {
+ public:
+ uchar *address;
+ };
+
+ class UnMapExtensionOption : public ExtensionOption {
+ public:
+ uchar *address;
+ };
+
+ virtual bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0);
+ virtual bool supportsExtension(Extension extension) const;
+
+ // Factory
+ static QAbstractFileEngine *create(const QString &fileName);
+
+protected:
+ void setError(QFile::FileError error, const QString &str);
+
+ QAbstractFileEngine();
+ QAbstractFileEngine(QAbstractFileEnginePrivate &);
+
+ QAbstractFileEnginePrivate *d_ptr;
+private:
+ Q_DECLARE_PRIVATE(QAbstractFileEngine)
+ Q_DISABLE_COPY(QAbstractFileEngine)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractFileEngine::FileFlags)
+
+class Q_CORE_EXPORT QAbstractFileEngineHandler
+{
+public:
+ QAbstractFileEngineHandler();
+ virtual ~QAbstractFileEngineHandler();
+ virtual QAbstractFileEngine *create(const QString &fileName) const = 0;
+};
+
+class QAbstractFileEngineIteratorPrivate;
+class Q_CORE_EXPORT QAbstractFileEngineIterator
+{
+public:
+ QAbstractFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters);
+ virtual ~QAbstractFileEngineIterator();
+
+ virtual QString next() = 0;
+ virtual bool hasNext() const = 0;
+
+ QString path() const;
+ QStringList nameFilters() const;
+ QDir::Filters filters() const;
+
+ virtual QString currentFileName() const = 0;
+ virtual QFileInfo currentFileInfo() const;
+ QString currentFilePath() const;
+
+protected:
+ enum EntryInfoType {
+ };
+ virtual QVariant entryInfo(EntryInfoType type) const;
+
+private:
+ Q_DISABLE_COPY(QAbstractFileEngineIterator)
+ friend class QDirIterator;
+ friend class QDirIteratorPrivate;
+ void setPath(const QString &path);
+ QAbstractFileEngineIteratorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTFILEENGINE_H
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h
new file mode 100644
index 0000000..34a5a02
--- /dev/null
+++ b/src/corelib/io/qabstractfileengine_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTFILEENGINE_P_H
+#define QABSTRACTFILEENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qabstractfileengine.h"
+#include "QtCore/qfile.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractFileEnginePrivate
+{
+public:
+ inline QAbstractFileEnginePrivate()
+ : fileError(QFile::UnspecifiedError)
+ {
+ }
+ inline virtual ~QAbstractFileEnginePrivate() { }
+
+ QFile::FileError fileError;
+ QString errorString;
+
+ QAbstractFileEngine *q_ptr;
+ Q_DECLARE_PUBLIC(QAbstractFileEngine)
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTFILEENGINE_P_H
diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp
new file mode 100644
index 0000000..a34f7ba
--- /dev/null
+++ b/src/corelib/io/qbuffer.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbuffer.h"
+#include "private/qiodevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/** QBufferPrivate **/
+class QBufferPrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QBuffer)
+
+public:
+ QBufferPrivate()
+ : buf(0)
+#ifndef QT_NO_QOBJECT
+ , writtenSinceLastEmit(0), signalConnectionCount(0), signalsEmitted(false)
+#endif
+ { }
+ ~QBufferPrivate() { }
+
+ QByteArray *buf;
+ QByteArray defaultBuf;
+ int ioIndex;
+
+#ifndef QT_NO_QOBJECT
+ // private slots
+ void _q_emitSignals();
+
+ qint64 writtenSinceLastEmit;
+ int signalConnectionCount;
+ bool signalsEmitted;
+#endif
+};
+
+#ifndef QT_NO_QOBJECT
+void QBufferPrivate::_q_emitSignals()
+{
+ Q_Q(QBuffer);
+ emit q->bytesWritten(writtenSinceLastEmit);
+ writtenSinceLastEmit = 0;
+ emit q->readyRead();
+ signalsEmitted = false;
+}
+#endif
+
+/*!
+ \class QBuffer
+ \reentrant
+ \brief The QBuffer class provides a QIODevice interface for a QByteArray.
+
+ \ingroup io
+
+ QBuffer allows you to access a QByteArray using the QIODevice
+ interface. The QByteArray is treated just as a standard random-accessed
+ file. Example:
+
+ \snippet doc/src/snippets/buffer/buffer.cpp 0
+
+ By default, an internal QByteArray buffer is created for you when
+ you create a QBuffer. You can access this buffer directly by
+ calling buffer(). You can also use QBuffer with an existing
+ QByteArray by calling setBuffer(), or by passing your array to
+ QBuffer's constructor.
+
+ Call open() to open the buffer. Then call write() or
+ putChar() to write to the buffer, and read(), readLine(),
+ readAll(), or getChar() to read from it. size() returns the
+ current size of the buffer, and you can seek to arbitrary
+ positions in the buffer by calling seek(). When you are done with
+ accessing the buffer, call close().
+
+ The following code snippet shows how to write data to a
+ QByteArray using QDataStream and QBuffer:
+
+ \snippet doc/src/snippets/buffer/buffer.cpp 1
+
+ Effectively, we convert the application's QPalette into a byte
+ array. Here's how to read the data from the QByteArray:
+
+ \snippet doc/src/snippets/buffer/buffer.cpp 2
+
+ QTextStream and QDataStream also provide convenience constructors
+ that take a QByteArray and that create a QBuffer behind the
+ scenes.
+
+ QBuffer emits readyRead() when new data has arrived in the
+ buffer. By connecting to this signal, you can use QBuffer to
+ store temporary data before processing it. For example, you can
+ pass the buffer to QFtp when downloading a file from an FTP
+ server. Whenever a new payload of data has been downloaded,
+ readyRead() is emitted, and you can process the data that just
+ arrived. QBuffer also emits bytesWritten() every time new data
+ has been written to the buffer.
+
+ \sa QFile, QDataStream, QTextStream, QByteArray
+*/
+
+#ifdef QT_NO_QOBJECT
+QBuffer::QBuffer()
+ : QIODevice(*new QBufferPrivate)
+{
+ Q_D(QBuffer);
+ d->buf = &d->defaultBuf;
+ d->ioIndex = 0;
+}
+QBuffer::QBuffer(QByteArray *buf)
+ : QIODevice(*new QBufferPrivate)
+{
+ Q_D(QBuffer);
+ d->buf = buf ? buf : &d->defaultBuf;
+ d->ioIndex = 0;
+ d->defaultBuf.clear();
+}
+#else
+/*!
+ Constructs an empty buffer with the given \a parent. You can call
+ setData() to fill the buffer with data, or you can open it in
+ write mode and use write().
+
+ \sa open()
+*/
+QBuffer::QBuffer(QObject *parent)
+ : QIODevice(*new QBufferPrivate, parent)
+{
+ Q_D(QBuffer);
+ d->buf = &d->defaultBuf;
+ d->ioIndex = 0;
+}
+
+/*!
+ Constructs a QBuffer that uses the QByteArray pointed to by \a
+ byteArray as its internal buffer, and with the given \a parent.
+ The caller is responsible for ensuring that \a byteArray remains
+ valid until the QBuffer is destroyed, or until setBuffer() is
+ called to change the buffer. QBuffer doesn't take ownership of
+ the QByteArray.
+
+ If you open the buffer in write-only mode or read-write mode and
+ write something into the QBuffer, \a byteArray will be modified.
+
+ Example:
+
+ \snippet doc/src/snippets/buffer/buffer.cpp 3
+
+ \sa open(), setBuffer(), setData()
+*/
+QBuffer::QBuffer(QByteArray *byteArray, QObject *parent)
+ : QIODevice(*new QBufferPrivate, parent)
+{
+ Q_D(QBuffer);
+ d->buf = byteArray ? byteArray : &d->defaultBuf;
+ d->defaultBuf.clear();
+ d->ioIndex = 0;
+}
+#endif
+
+/*!
+ Destroys the buffer.
+*/
+
+QBuffer::~QBuffer()
+{
+}
+
+/*!
+ Makes QBuffer uses the QByteArray pointed to by \a
+ byteArray as its internal buffer. The caller is responsible for
+ ensuring that \a byteArray remains valid until the QBuffer is
+ destroyed, or until setBuffer() is called to change the buffer.
+ QBuffer doesn't take ownership of the QByteArray.
+
+ Does nothing if isOpen() is true.
+
+ If you open the buffer in write-only mode or read-write mode and
+ write something into the QBuffer, \a byteArray will be modified.
+
+ Example:
+
+ \snippet doc/src/snippets/buffer/buffer.cpp 4
+
+ If \a byteArray is 0, the buffer creates its own internal
+ QByteArray to work on. This byte array is initially empty.
+
+ \sa buffer(), setData(), open()
+*/
+
+void QBuffer::setBuffer(QByteArray *byteArray)
+{
+ Q_D(QBuffer);
+ if (isOpen()) {
+ qWarning("QBuffer::setBuffer: Buffer is open");
+ return;
+ }
+ if (byteArray) {
+ d->buf = byteArray;
+ } else {
+ d->buf = &d->defaultBuf;
+ }
+ d->defaultBuf.clear();
+ d->ioIndex = 0;
+}
+
+/*!
+ Returns a reference to the QBuffer's internal buffer. You can use
+ it to modify the QByteArray behind the QBuffer's back.
+
+ \sa setBuffer(), data()
+*/
+
+QByteArray &QBuffer::buffer()
+{
+ Q_D(QBuffer);
+ return *d->buf;
+}
+
+/*!
+ \overload
+
+ This is the same as data().
+*/
+
+const QByteArray &QBuffer::buffer() const
+{
+ Q_D(const QBuffer);
+ return *d->buf;
+}
+
+
+/*!
+ Returns the data contained in the buffer.
+
+ This is the same as buffer().
+
+ \sa setData(), setBuffer()
+*/
+
+const QByteArray &QBuffer::data() const
+{
+ Q_D(const QBuffer);
+ return *d->buf;
+}
+
+/*!
+ Sets the contents of the internal buffer to be \a data. This is
+ the same as assigning \a data to buffer().
+
+ Does nothing if isOpen() is true.
+
+ \sa setBuffer()
+*/
+void QBuffer::setData(const QByteArray &data)
+{
+ Q_D(QBuffer);
+ if (isOpen()) {
+ qWarning("QBuffer::setData: Buffer is open");
+ return;
+ }
+ *d->buf = data;
+ d->ioIndex = 0;
+}
+
+/*!
+ \fn void QBuffer::setData(const char *data, int size)
+
+ \overload
+
+ Sets the contents of the internal buffer to be the first \a size
+ bytes of \a data.
+*/
+
+/*!
+ \reimp
+*/
+bool QBuffer::open(OpenMode flags)
+{
+ Q_D(QBuffer);
+
+ if ((flags & Append) == Append)
+ flags |= WriteOnly;
+ setOpenMode(flags);
+ if (!(isReadable() || isWritable())) {
+ qWarning("QFile::open: File access not specified");
+ return false;
+ }
+
+ if ((flags & QIODevice::Truncate) == QIODevice::Truncate) {
+ d->buf->resize(0);
+ }
+ if ((flags & QIODevice::Append) == QIODevice::Append) // append to end of buffer
+ seek(d->buf->size());
+ else
+ seek(0);
+
+ return true;
+}
+
+/*!
+ \reimp
+*/
+void QBuffer::close()
+{
+ QIODevice::close();
+}
+
+/*!
+ \reimp
+*/
+qint64 QBuffer::pos() const
+{
+ return QIODevice::pos();
+}
+
+/*!
+ \reimp
+*/
+qint64 QBuffer::size() const
+{
+ Q_D(const QBuffer);
+ return qint64(d->buf->size());
+}
+
+/*!
+ \reimp
+*/
+bool QBuffer::seek(qint64 pos)
+{
+ Q_D(QBuffer);
+ if (pos > d->buf->size() && isWritable()) {
+ if (seek(d->buf->size())) {
+ const qint64 gapSize = pos - d->buf->size();
+ if (write(QByteArray(gapSize, 0)) != gapSize) {
+ qWarning("QBuffer::seek: Unable to fill gap");
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else if (pos > d->buf->size() || pos < 0) {
+ qWarning("QBuffer::seek: Invalid pos: %d", int(pos));
+ return false;
+ }
+ d->ioIndex = int(pos);
+ return QIODevice::seek(pos);
+}
+
+/*!
+ \reimp
+*/
+bool QBuffer::atEnd() const
+{
+ return QIODevice::atEnd();
+}
+
+/*!
+ \reimp
+*/
+bool QBuffer::canReadLine() const
+{
+ Q_D(const QBuffer);
+ if (!isOpen())
+ return false;
+
+ return d->buf->indexOf('\n', int(pos())) != -1 || QIODevice::canReadLine();
+}
+
+/*!
+ \reimp
+*/
+qint64 QBuffer::readData(char *data, qint64 len)
+{
+ Q_D(QBuffer);
+ if ((len = qMin(len, qint64(d->buf->size()) - d->ioIndex)) <= 0)
+ return qint64(0);
+ memcpy(data, d->buf->constData() + d->ioIndex, len);
+ d->ioIndex += int(len);
+ return len;
+}
+
+/*!
+ \reimp
+*/
+qint64 QBuffer::writeData(const char *data, qint64 len)
+{
+ Q_D(QBuffer);
+ int extraBytes = d->ioIndex + len - d->buf->size();
+ if (extraBytes > 0) { // overflow
+ int newSize = d->buf->size() + extraBytes;
+ d->buf->resize(newSize);
+ if (d->buf->size() != newSize) { // could not resize
+ qWarning("QBuffer::writeData: Memory allocation error");
+ return -1;
+ }
+ }
+
+ memcpy(d->buf->data() + d->ioIndex, (uchar *)data, int(len));
+ d->ioIndex += int(len);
+
+#ifndef QT_NO_QOBJECT
+ d->writtenSinceLastEmit += len;
+ if (d->signalConnectionCount && !d->signalsEmitted && !signalsBlocked()) {
+ d->signalsEmitted = true;
+ QMetaObject::invokeMethod(this, "_q_emitSignals", Qt::QueuedConnection);
+ }
+#endif
+ return len;
+}
+
+#ifndef QT_NO_QOBJECT
+/*!
+ \reimp
+ \internal
+*/
+void QBuffer::connectNotify(const char *signal)
+{
+ if (strcmp(signal + 1, "readyRead()") == 0 || strcmp(signal + 1, "bytesWritten(qint64)"))
+ d_func()->signalConnectionCount++;
+}
+
+/*!
+ \reimp
+ \internal
+*/
+void QBuffer::disconnectNotify(const char *signal)
+{
+ if (!signal || strcmp(signal + 1, "readyRead()") == 0 || strcmp(signal + 1, "bytesWritten(qint64)") == 0)
+ d_func()->signalConnectionCount--;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#ifndef QT_NO_QOBJECT
+# include "moc_qbuffer.cpp"
+#endif
+
diff --git a/src/corelib/io/qbuffer.h b/src/corelib/io/qbuffer.h
new file mode 100644
index 0000000..37aefb2
--- /dev/null
+++ b/src/corelib/io/qbuffer.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBUFFER_H
+#define QBUFFER_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qbytearray.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QObject;
+class QBufferPrivate;
+
+class Q_CORE_EXPORT QBuffer : public QIODevice
+{
+#ifndef QT_NO_QOBJECT
+ Q_OBJECT
+#endif
+
+public:
+#ifndef QT_NO_QOBJECT
+ explicit QBuffer(QObject *parent = 0);
+ QBuffer(QByteArray *buf, QObject *parent = 0);
+#else
+ QBuffer();
+ explicit QBuffer(QByteArray *buf);
+#endif
+ ~QBuffer();
+
+ QByteArray &buffer();
+ const QByteArray &buffer() const;
+ void setBuffer(QByteArray *a);
+
+ void setData(const QByteArray &data);
+ inline void setData(const char *data, int len);
+ const QByteArray &data() const;
+
+ bool open(OpenMode openMode);
+
+ void close();
+ qint64 size() const;
+ qint64 pos() const;
+ bool seek(qint64 off);
+ bool atEnd() const;
+ bool canReadLine() const;
+
+protected:
+#ifndef QT_NO_QOBJECT
+ void connectNotify(const char*);
+ void disconnectNotify(const char*);
+#endif
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private:
+ Q_DECLARE_PRIVATE(QBuffer)
+ Q_DISABLE_COPY(QBuffer)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_emitSignals())
+};
+
+inline void QBuffer::setData(const char *adata, int alen)
+{ setData(QByteArray(adata, alen)); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBUFFER_H
diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp
new file mode 100644
index 0000000..9990696
--- /dev/null
+++ b/src/corelib/io/qdatastream.cpp
@@ -0,0 +1,1260 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdatastream.h"
+
+#ifndef QT_NO_DATASTREAM
+#include "qbuffer.h"
+#include "qstring.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QDataStream
+ \reentrant
+ \brief The QDataStream class provides serialization of binary data
+ to a QIODevice.
+
+ \ingroup io
+ \mainclass
+
+ A data stream is a binary stream of encoded information which is
+ 100% independent of the host computer's operating system, CPU or
+ byte order. For example, a data stream that is written by a PC
+ under Windows can be read by a Sun SPARC running Solaris.
+
+ You can also use a data stream to read/write \l{raw}{raw
+ unencoded binary data}. If you want a "parsing" input stream, see
+ QTextStream.
+
+ The QDataStream class implements the serialization of C++'s basic
+ data types, like \c char, \c short, \c int, \c{char *}, etc.
+ Serialization of more complex data is accomplished by breaking up
+ the data into primitive units.
+
+ A data stream cooperates closely with a QIODevice. A QIODevice
+ represents an input/output medium one can read data from and write
+ data to. The QFile class is an example of an I/O device.
+
+ Example (write binary data to a stream):
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 0
+
+ Example (read binary data from a stream):
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 1
+
+ Each item written to the stream is written in a predefined binary
+ format that varies depending on the item's type. Supported Qt
+ types include QBrush, QColor, QDateTime, QFont, QPixmap, QString,
+ QVariant and many others. For the complete list of all Qt types
+ supporting data streaming see the \l{Format of the QDataStream
+ operators}.
+
+ For integers it is best to always cast to a Qt integer type for
+ writing, and to read back into the same Qt integer type. This
+ ensures that you get integers of the size you want and insulates
+ you from compiler and platform differences.
+
+ To take one example, a \c{char *} string is written as a 32-bit
+ integer equal to the length of the string including the '\\0' byte,
+ followed by all the characters of the string including the
+ '\\0' byte. When reading a \c{char *} string, 4 bytes are read to
+ create the 32-bit length value, then that many characters for the
+ \c {char *} string including the '\\0' terminator are read.
+
+ The initial I/O device is usually set in the constructor, but can be
+ changed with setDevice(). If you've reached the end of the data
+ (or if there is no I/O device set) atEnd() will return true.
+
+ \section1 Versioning
+
+ QDataStream's binary format has evolved since Qt 1.0, and is
+ likely to continue evolving to reflect changes done in Qt. When
+ inputting or outputting complex types, it's very important to
+ make sure that the same version of the stream (version()) is used
+ for reading and writing. If you need both forward and backward
+ compatibility, you can hardcode the version number in the
+ application:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 2
+
+ If you are producing a new binary data format, such as a file
+ format for documents created by your application, you could use a
+ QDataStream to write the data in a portable format. Typically, you
+ would write a brief header containing a magic string and a version
+ number to give yourself room for future expansion. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 3
+
+ Then read it in with:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 4
+
+ You can select which byte order to use when serializing data. The
+ default setting is big endian (MSB first). Changing it to little
+ endian breaks the portability (unless the reader also changes to
+ little endian). We recommend keeping this setting unless you have
+ special requirements.
+
+ \target raw
+ \section1 Reading and writing raw binary data
+
+ You may wish to read/write your own raw binary data to/from the
+ data stream directly. Data may be read from the stream into a
+ preallocated \c{char *} using readRawData(). Similarly data can be
+ written to the stream using writeRawData(). Note that any
+ encoding/decoding of the data must be done by you.
+
+ A similar pair of functions is readBytes() and writeBytes(). These
+ differ from their \e raw counterparts as follows: readBytes()
+ reads a quint32 which is taken to be the length of the data to be
+ read, then that number of bytes is read into the preallocated
+ \c{char *}; writeBytes() writes a quint32 containing the length of the
+ data, followed by the data. Note that any encoding/decoding of
+ the data (apart from the length quint32) must be done by you.
+
+ \target Serializing Qt Classes
+ \section1 Reading and writing other Qt classes.
+
+ In addition to the overloaded stream operators documented here,
+ any Qt classes that you might want to serialize to a QDataStream
+ will have appropriate stream operators declared as non-member of
+ the class:
+
+ \code
+ QDataStream &operator<<(QDataStream &, const QXxx &);
+ QDataStream &operator>>(QDataStream &, QXxx &);
+ \endcode
+
+ For example, here are the stream operators declared as non-members
+ of the QImage class:
+
+ \code
+ QDataStream & operator<< (QDataStream& stream, const QImage& image);
+ QDataStream & operator>> (QDataStream& stream, QImage& image);
+ \endcode
+
+ To see if your favorite Qt class has similar stream operators
+ defined, check the \bold {Related Non-Members} section of the
+ class's documentation page.
+
+ \sa QTextStream QVariant
+*/
+
+/*!
+ \enum QDataStream::ByteOrder
+
+ The byte order used for reading/writing the data.
+
+ \value BigEndian Most significant byte first (the default)
+ \value LittleEndian Least significant byte first
+*/
+
+/*!
+ \enum QDataStream::Status
+
+ This enum describes the current status of the data stream.
+
+ \value Ok The data stream is operating normally.
+ \value ReadPastEnd The data stream has read past the end of the
+ data in the underlying device.
+ \value ReadCorruptData The data stream has read corrupt data.
+*/
+
+/*****************************************************************************
+ QDataStream member functions
+ *****************************************************************************/
+
+#undef CHECK_STREAM_PRECOND
+#ifndef QT_NO_DEBUG
+#define CHECK_STREAM_PRECOND(retVal) \
+ if (!dev) { \
+ qWarning("QDataStream: No device"); \
+ return retVal; \
+ }
+#else
+#define CHECK_STREAM_PRECOND(retVal) \
+ if (!dev) { \
+ return retVal; \
+ }
+#endif
+
+enum {
+ DefaultStreamVersion = QDataStream::Qt_4_5
+};
+
+// ### 5.0: when streaming invalid QVariants, just the type should
+// be written, no "data" after it
+
+/*!
+ Constructs a data stream that has no I/O device.
+
+ \sa setDevice()
+*/
+
+QDataStream::QDataStream()
+{
+ dev = 0;
+ owndev = false;
+ byteorder = BigEndian;
+ ver = DefaultStreamVersion;
+ noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian;
+ q_status = Ok;
+}
+
+/*!
+ Constructs a data stream that uses the I/O device \a d.
+
+ \warning If you use QSocket or QSocketDevice as the I/O device \a d
+ for reading data, you must make sure that enough data is available
+ on the socket for the operation to successfully proceed;
+ QDataStream does not have any means to handle or recover from
+ short-reads.
+
+ \sa setDevice(), device()
+*/
+
+QDataStream::QDataStream(QIODevice *d)
+{
+ dev = d; // set device
+ owndev = false;
+ byteorder = BigEndian; // default byte order
+ ver = DefaultStreamVersion;
+ noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian;
+ q_status = Ok;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QDataStream::QDataStream(QByteArray *array, int mode)
+ \compat
+
+ Constructs a data stream that operates on the given \a array. The
+ \a mode specifies how the byte array is to be used, and is
+ usually either QIODevice::ReadOnly or QIODevice::WriteOnly.
+*/
+QDataStream::QDataStream(QByteArray *a, int mode)
+{
+ QBuffer *buf = new QBuffer(a);
+#ifndef QT_NO_QOBJECT
+ buf->blockSignals(true);
+#endif
+ buf->open(QIODevice::OpenMode(mode));
+ dev = buf;
+ owndev = true;
+ byteorder = BigEndian;
+ ver = DefaultStreamVersion;
+ noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian;
+ q_status = Ok;
+}
+#endif
+
+/*!
+ \fn QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode mode)
+
+ Constructs a data stream that operates on a byte array, \a a. The
+ \a mode describes how the device is to be used.
+
+ Alternatively, you can use QDataStream(const QByteArray &) if you
+ just want to read from a byte array.
+
+ Since QByteArray is not a QIODevice subclass, internally a QBuffer
+ is created to wrap the byte array.
+*/
+
+QDataStream::QDataStream(QByteArray *a, QIODevice::OpenMode flags)
+{
+ QBuffer *buf = new QBuffer(a);
+#ifndef QT_NO_QOBJECT
+ buf->blockSignals(true);
+#endif
+ buf->open(flags);
+ dev = buf;
+ owndev = true;
+ byteorder = BigEndian;
+ ver = DefaultStreamVersion;
+ noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian;
+ q_status = Ok;
+}
+
+/*!
+ Constructs a read-only data stream that operates on byte array \a a.
+ Use QDataStream(QByteArray*, int) if you want to write to a byte
+ array.
+
+ Since QByteArray is not a QIODevice subclass, internally a QBuffer
+ is created to wrap the byte array.
+*/
+QDataStream::QDataStream(const QByteArray &a)
+{
+ QBuffer *buf = new QBuffer;
+#ifndef QT_NO_QOBJECT
+ buf->blockSignals(true);
+#endif
+ buf->setData(a);
+ buf->open(QIODevice::ReadOnly);
+ dev = buf;
+ owndev = true;
+ byteorder = BigEndian;
+ ver = DefaultStreamVersion;
+ noswap = QSysInfo::ByteOrder == QSysInfo::BigEndian;
+ q_status = Ok;
+}
+
+/*!
+ Destroys the data stream.
+
+ The destructor will not affect the current I/O device, unless it is
+ an internal I/O device (e.g. a QBuffer) processing a QByteArray
+ passed in the \e constructor, in which case the internal I/O device
+ is destroyed.
+*/
+
+QDataStream::~QDataStream()
+{
+ if (owndev)
+ delete dev;
+}
+
+
+/*!
+ \fn QIODevice *QDataStream::device() const
+
+ Returns the I/O device currently set.
+
+ \sa setDevice(), unsetDevice()
+*/
+
+/*!
+ void QDataStream::setDevice(QIODevice *d)
+
+ Sets the I/O device to \a d.
+
+ \sa device(), unsetDevice()
+*/
+
+void QDataStream::setDevice(QIODevice *d)
+{
+ if (owndev) {
+ delete dev;
+ owndev = false;
+ }
+ dev = d;
+}
+
+/*!
+ \obsolete
+ Unsets the I/O device.
+ Use setDevice(0) instead.
+*/
+
+void QDataStream::unsetDevice()
+{
+ setDevice(0);
+}
+
+
+/*!
+ \fn bool QDataStream::atEnd() const
+
+ Returns true if the I/O device has reached the end position (end of
+ the stream or file) or if there is no I/O device set; otherwise
+ returns false.
+
+ \sa QIODevice::atEnd()
+*/
+
+bool QDataStream::atEnd() const
+{
+ return dev ? dev->atEnd() : true;
+}
+
+/*!
+ Returns the status of the data stream.
+
+ \sa Status setStatus() resetStatus()
+*/
+
+QDataStream::Status QDataStream::status() const
+{
+ return q_status;
+}
+
+/*!
+ Resets the status of the data stream.
+
+ \sa Status status() setStatus()
+*/
+void QDataStream::resetStatus()
+{
+ q_status = Ok;
+}
+
+/*!
+ Sets the status of the data stream to the \a status given.
+
+ \sa Status status() resetStatus()
+*/
+void QDataStream::setStatus(Status status)
+{
+ if (q_status == Ok)
+ q_status = status;
+}
+
+/*!\fn bool QDataStream::eof() const
+
+ Use atEnd() instead.
+*/
+
+/*!
+ \fn int QDataStream::byteOrder() const
+
+ Returns the current byte order setting -- either BigEndian or
+ LittleEndian.
+
+ \sa setByteOrder()
+*/
+
+/*!
+ Sets the serialization byte order to \a bo.
+
+ The \a bo parameter can be QDataStream::BigEndian or
+ QDataStream::LittleEndian.
+
+ The default setting is big endian. We recommend leaving this
+ setting unless you have special requirements.
+
+ \sa byteOrder()
+*/
+
+void QDataStream::setByteOrder(ByteOrder bo)
+{
+ byteorder = bo;
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ noswap = (byteorder == BigEndian);
+ else
+ noswap = (byteorder == LittleEndian);
+}
+
+
+/*!
+ \fn bool QDataStream::isPrintableData() const
+
+ In Qt 4, this function always returns false.
+
+ \sa setPrintableData()
+*/
+
+/*!
+ \fn void QDataStream::setPrintableData(bool enable)
+
+ In Qt 3, this function enabled output in a human-readable
+ format if \a enable was false.
+
+ In Qt 4, QDataStream no longer provides a human-readable output.
+ This function does nothing.
+*/
+
+/*!
+ \enum QDataStream::Version
+
+ This enum provides symbolic synonyms for the data serialization
+ format version numbers.
+
+ \value Qt_1_0 Version 1 (Qt 1.x)
+ \value Qt_2_0 Version 2 (Qt 2.0)
+ \value Qt_2_1 Version 3 (Qt 2.1, 2.2, 2.3)
+ \value Qt_3_0 Version 4 (Qt 3.0)
+ \value Qt_3_1 Version 5 (Qt 3.1, 3.2)
+ \value Qt_3_3 Version 6 (Qt 3.3)
+ \value Qt_4_0 Version 7 (Qt 4.0, Qt 4.1)
+ \value Qt_4_1 Version 7 (Qt 4.0, Qt 4.1)
+ \value Qt_4_2 Version 8 (Qt 4.2)
+ \value Qt_4_3 Version 9 (Qt 4.3)
+ \value Qt_4_4 Version 10 (Qt 4.4)
+ \value Qt_4_5 Version 10 (Qt 4.5)
+ \omitvalue Qt_4_6
+
+ \sa setVersion(), version()
+*/
+
+/*!
+ \fn int QDataStream::version() const
+
+ Returns the version number of the data serialization format.
+
+ \sa setVersion(), Version
+*/
+
+/*!
+ \fn void QDataStream::setVersion(int v)
+
+ Sets the version number of the data serialization format to \a v.
+
+ You don't \e have to set a version if you are using the current
+ version of Qt, but for your own custom binary formats we
+ recommend that you do; see \l{Versioning} in the Detailed
+ Description.
+
+ In order to accommodate new functionality, the datastream
+ serialization format of some Qt classes has changed in some
+ versions of Qt. If you want to read data that was created by an
+ earlier version of Qt, or write data that can be read by a
+ program that was compiled with an earlier version of Qt, use this
+ function to modify the serialization format used by QDataStream.
+
+ \table
+ \header \i Qt Version \i QDataStream Version
+ \row \i Qt 4.4 \i 10
+ \row \i Qt 4.3 \i 9
+ \row \i Qt 4.2 \i 8
+ \row \i Qt 4.0, 4.1 \i 7
+ \row \i Qt 3.3 \i 6
+ \row \i Qt 3.1, 3.2 \i 5
+ \row \i Qt 3.0 \i 4
+ \row \i Qt 2.1, 2.2, 2.3 \i 3
+ \row \i Qt 2.0 \i 2
+ \row \i Qt 1.x \i 1
+ \endtable
+
+ The \l Version enum provides symbolic constants for the different
+ versions of Qt. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdatastream.cpp 5
+
+ \sa version(), Version
+*/
+
+/*****************************************************************************
+ QDataStream read functions
+ *****************************************************************************/
+
+/*!
+ \fn QDataStream &QDataStream::operator>>(quint8 &i)
+ \overload
+
+ Reads an unsigned byte from the stream into \a i, and returns a
+ reference to the stream.
+*/
+
+/*!
+ Reads a signed byte from the stream into \a i, and returns a
+ reference to the stream.
+*/
+
+QDataStream &QDataStream::operator>>(qint8 &i)
+{
+ i = 0;
+ CHECK_STREAM_PRECOND(*this)
+ char c;
+ if (!dev->getChar(&c))
+ setStatus(ReadPastEnd);
+ else
+ i = qint8(c);
+ return *this;
+}
+
+
+/*!
+ \fn QDataStream &QDataStream::operator>>(quint16 &i)
+ \overload
+
+ Reads an unsigned 16-bit integer from the stream into \a i, and
+ returns a reference to the stream.
+*/
+
+/*!
+ \overload
+
+ Reads a signed 16-bit integer from the stream into \a i, and
+ returns a reference to the stream.
+*/
+
+QDataStream &QDataStream::operator>>(qint16 &i)
+{
+ i = 0;
+ CHECK_STREAM_PRECOND(*this)
+ if (noswap) {
+ if (dev->read((char *)&i, 2) != 2) {
+ i = 0;
+ setStatus(ReadPastEnd);
+ }
+ } else {
+ register uchar *p = (uchar *)(&i);
+ char b[2];
+ if (dev->read(b, 2) == 2) {
+ *p++ = b[1];
+ *p = b[0];
+ } else {
+ setStatus(ReadPastEnd);
+ }
+ }
+ return *this;
+}
+
+
+/*!
+ \fn QDataStream &QDataStream::operator>>(quint32 &i)
+ \overload
+
+ Reads an unsigned 32-bit integer from the stream into \a i, and
+ returns a reference to the stream.
+*/
+
+/*!
+ \overload
+
+ Reads a signed 32-bit integer from the stream into \a i, and
+ returns a reference to the stream.
+*/
+
+QDataStream &QDataStream::operator>>(qint32 &i)
+{
+ i = 0;
+ CHECK_STREAM_PRECOND(*this)
+ if (noswap) {
+ if (dev->read((char *)&i, 4) != 4) {
+ i = 0;
+ setStatus(ReadPastEnd);
+ }
+ } else { // swap bytes
+ uchar *p = (uchar *)(&i);
+ char b[4];
+ if (dev->read(b, 4) == 4) {
+ *p++ = b[3];
+ *p++ = b[2];
+ *p++ = b[1];
+ *p = b[0];
+ } else {
+ setStatus(ReadPastEnd);
+ }
+ }
+ return *this;
+}
+
+/*!
+ \fn QDataStream &QDataStream::operator>>(quint64 &i)
+ \overload
+
+ Reads an unsigned 64-bit integer from the stream, into \a i, and
+ returns a reference to the stream.
+*/
+
+/*!
+ \overload
+
+ Reads a signed 64-bit integer from the stream into \a i, and
+ returns a reference to the stream.
+*/
+
+QDataStream &QDataStream::operator>>(qint64 &i)
+{
+ i = qint64(0);
+ CHECK_STREAM_PRECOND(*this)
+ if (version() < 6) {
+ quint32 i1, i2;
+ *this >> i2 >> i1;
+ i = ((quint64)i1 << 32) + i2;
+ } else if (noswap) { // no conversion needed
+ if (dev->read((char *)&i, 8) != 8) {
+ i = qint64(0);
+ setStatus(ReadPastEnd);
+ }
+ } else { // swap bytes
+ uchar *p = (uchar *)(&i);
+ char b[8];
+ if (dev->read(b, 8) == 8) {
+ *p++ = b[7];
+ *p++ = b[6];
+ *p++ = b[5];
+ *p++ = b[4];
+ *p++ = b[3];
+ *p++ = b[2];
+ *p++ = b[1];
+ *p = b[0];
+ } else {
+ setStatus(ReadPastEnd);
+ }
+ }
+ return *this;
+}
+
+/*!
+ Reads a boolean value from the stream into \a i. Returns a
+ reference to the stream.
+*/
+QDataStream &QDataStream::operator>>(bool &i)
+{
+ qint8 v;
+ *this >> v;
+ i = !!v;
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a 32-bit floating point number from the stream into \a f,
+ using the standard IEEE 754 format. Returns a reference to the
+ stream.
+*/
+
+QDataStream &QDataStream::operator>>(float &f)
+{
+ f = 0.0f;
+ CHECK_STREAM_PRECOND(*this)
+ if (noswap) {
+ if (dev->read((char *)&f, 4) != 4) {
+ f = 0.0f;
+ setStatus(ReadPastEnd);
+ }
+ } else { // swap bytes
+ uchar *p = (uchar *)(&f);
+ char b[4];
+ if (dev->read(b, 4) == 4) {
+ *p++ = b[3];
+ *p++ = b[2];
+ *p++ = b[1];
+ *p = b[0];
+ } else {
+ setStatus(ReadPastEnd);
+ }
+ }
+ return *this;
+}
+
+#if defined(Q_DOUBLE_FORMAT)
+#define Q_DF(x) Q_DOUBLE_FORMAT[(x)] - '0'
+#endif
+
+/*!
+ \overload
+
+ Reads a 64-bit floating point number from the stream into \a f,
+ using the standard IEEE 754 format. Returns a reference to the
+ stream.
+*/
+
+QDataStream &QDataStream::operator>>(double &f)
+{
+ f = 0.0;
+ CHECK_STREAM_PRECOND(*this)
+#ifndef Q_DOUBLE_FORMAT
+ if (noswap) {
+ if (dev->read((char *)&f, 8) != 8) {
+ f = 0.0;
+ setStatus(ReadPastEnd);
+ }
+ } else { // swap bytes
+ register uchar *p = (uchar *)(&f);
+ char b[8];
+ if (dev->read(b, 8) == 8) {
+ *p++ = b[7];
+ *p++ = b[6];
+ *p++ = b[5];
+ *p++ = b[4];
+ *p++ = b[3];
+ *p++ = b[2];
+ *p++ = b[1];
+ *p = b[0];
+ } else {
+ setStatus(ReadPastEnd);
+ }
+ }
+#else
+ //non-standard floating point format
+ register uchar *p = (uchar *)(&f);
+ char b[8];
+ if (dev->read(b, 8) == 8) {
+ if (noswap) {
+ *p++ = b[Q_DF(0)];
+ *p++ = b[Q_DF(1)];
+ *p++ = b[Q_DF(2)];
+ *p++ = b[Q_DF(3)];
+ *p++ = b[Q_DF(4)];
+ *p++ = b[Q_DF(5)];
+ *p++ = b[Q_DF(6)];
+ *p = b[Q_DF(7)];
+ } else {
+ *p++ = b[Q_DF(7)];
+ *p++ = b[Q_DF(6)];
+ *p++ = b[Q_DF(5)];
+ *p++ = b[Q_DF(4)];
+ *p++ = b[Q_DF(3)];
+ *p++ = b[Q_DF(2)];
+ *p++ = b[Q_DF(1)];
+ *p = b[Q_DF(0)];
+ }
+ } else {
+ setStatus(ReadPastEnd);
+ }
+#endif
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads the '\0'-terminated string \a s from the stream and returns
+ a reference to the stream.
+
+ Space for the string is allocated using \c new -- the caller must
+ destroy it with \c{delete[]}.
+*/
+
+QDataStream &QDataStream::operator>>(char *&s)
+{
+ uint len = 0;
+ return readBytes(s, len);
+}
+
+
+/*!
+ Reads the buffer \a s from the stream and returns a reference to
+ the stream.
+
+ The buffer \a s is allocated using \c new. Destroy it with the \c
+ delete[] operator.
+
+ The \a l parameter is set to the length of the buffer. If the
+ string read is empty, \a l is set to 0 and \a s is set to
+ a null pointer.
+
+ The serialization format is a quint32 length specifier first,
+ then \a l bytes of data.
+
+ \sa readRawData(), writeBytes()
+*/
+
+QDataStream &QDataStream::readBytes(char *&s, uint &l)
+{
+ s = 0;
+ l = 0;
+ CHECK_STREAM_PRECOND(*this)
+
+ quint32 len;
+ *this >> len;
+ if (len == 0)
+ return *this;
+
+ const quint32 Step = 1024 * 1024;
+ quint32 allocated = 0;
+ char *prevBuf = 0;
+ char *curBuf = 0;
+
+ do {
+ int blockSize = qMin(Step, len - allocated);
+ prevBuf = curBuf;
+ curBuf = new char[allocated + blockSize + 1];
+ if (prevBuf) {
+ memcpy(curBuf, prevBuf, allocated);
+ delete [] prevBuf;
+ }
+ if (dev->read(curBuf + allocated, blockSize) != blockSize) {
+ delete [] curBuf;
+ setStatus(ReadPastEnd);
+ return *this;
+ }
+ allocated += blockSize;
+ } while (allocated < len);
+
+ s = curBuf;
+ s[len] = '\0';
+ l = (uint)len;
+ return *this;
+}
+
+/*!
+ Reads at most \a len bytes from the stream into \a s and returns the number of
+ bytes read. If an error occurs, this function returns -1.
+
+ The buffer \a s must be preallocated. The data is \e not encoded.
+
+ \sa readBytes(), QIODevice::read(), writeRawData()
+*/
+
+int QDataStream::readRawData(char *s, int len)
+{
+ CHECK_STREAM_PRECOND(-1)
+ return dev->read(s, len);
+}
+
+
+/*****************************************************************************
+ QDataStream write functions
+ *****************************************************************************/
+
+
+/*!
+ \fn QDataStream &QDataStream::operator<<(quint8 i)
+ \overload
+
+ Writes an unsigned byte, \a i, to the stream and returns a
+ reference to the stream.
+*/
+
+/*!
+ Writes a signed byte, \a i, to the stream and returns a reference
+ to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(qint8 i)
+{
+ CHECK_STREAM_PRECOND(*this)
+ dev->putChar(i);
+ return *this;
+}
+
+
+/*!
+ \fn QDataStream &QDataStream::operator<<(quint16 i)
+ \overload
+
+ Writes an unsigned 16-bit integer, \a i, to the stream and returns
+ a reference to the stream.
+*/
+
+/*!
+ \overload
+
+ Writes a signed 16-bit integer, \a i, to the stream and returns a
+ reference to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(qint16 i)
+{
+ CHECK_STREAM_PRECOND(*this)
+ if (noswap) {
+ dev->write((char *)&i, sizeof(qint16));
+ } else { // swap bytes
+ register uchar *p = (uchar *)(&i);
+ char b[2];
+ b[1] = *p++;
+ b[0] = *p;
+ dev->write(b, 2);
+ }
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes a signed 32-bit integer, \a i, to the stream and returns a
+ reference to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(qint32 i)
+{
+ CHECK_STREAM_PRECOND(*this)
+ if (noswap) {
+ dev->write((char *)&i, sizeof(qint32));
+ } else { // swap bytes
+ register uchar *p = (uchar *)(&i);
+ char b[4];
+ b[3] = *p++;
+ b[2] = *p++;
+ b[1] = *p++;
+ b[0] = *p;
+ dev->write(b, 4);
+ }
+ return *this;
+}
+
+/*!
+ \fn QDataStream &QDataStream::operator<<(quint64 i)
+ \overload
+
+ Writes an unsigned 64-bit integer, \a i, to the stream and returns a
+ reference to the stream.
+*/
+
+/*!
+ \overload
+
+ Writes a signed 64-bit integer, \a i, to the stream and returns a
+ reference to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(qint64 i)
+{
+ CHECK_STREAM_PRECOND(*this)
+ if (version() < 6) {
+ quint32 i1 = i & 0xffffffff;
+ quint32 i2 = i >> 32;
+ *this << i2 << i1;
+ } else if (noswap) { // no conversion needed
+ dev->write((char *)&i, sizeof(qint64));
+ } else { // swap bytes
+ register uchar *p = (uchar *)(&i);
+ char b[8];
+ b[7] = *p++;
+ b[6] = *p++;
+ b[5] = *p++;
+ b[4] = *p++;
+ b[3] = *p++;
+ b[2] = *p++;
+ b[1] = *p++;
+ b[0] = *p;
+ dev->write(b, 8);
+ }
+ return *this;
+}
+
+/*!
+ \fn QDataStream &QDataStream::operator<<(quint32 i)
+ \overload
+
+ Writes an unsigned integer, \a i, to the stream as a 32-bit
+ unsigned integer (quint32). Returns a reference to the stream.
+*/
+
+/*!
+ Writes a boolean value, \a i, to the stream. Returns a reference
+ to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(bool i)
+{
+ CHECK_STREAM_PRECOND(*this)
+ dev->putChar(qint8(i));
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes a 32-bit floating point number, \a f, to the stream using
+ the standard IEEE 754 format. Returns a reference to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(float f)
+{
+ CHECK_STREAM_PRECOND(*this)
+ float g = f; // fixes float-on-stack problem
+ if (noswap) { // no conversion needed
+ dev->write((char *)&g, sizeof(float));
+ } else { // swap bytes
+ register uchar *p = (uchar *)(&g);
+ char b[4];
+ b[3] = *p++;
+ b[2] = *p++;
+ b[1] = *p++;
+ b[0] = *p;
+ dev->write(b, 4);
+ }
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Writes a 64-bit floating point number, \a f, to the stream using
+ the standard IEEE 754 format. Returns a reference to the stream.
+*/
+
+QDataStream &QDataStream::operator<<(double f)
+{
+ CHECK_STREAM_PRECOND(*this)
+#ifndef Q_DOUBLE_FORMAT
+ if (noswap) {
+ dev->write((char *)&f, sizeof(double));
+ } else {
+ register uchar *p = (uchar *)(&f);
+ char b[8];
+ b[7] = *p++;
+ b[6] = *p++;
+ b[5] = *p++;
+ b[4] = *p++;
+ b[3] = *p++;
+ b[2] = *p++;
+ b[1] = *p++;
+ b[0] = *p;
+ dev->write(b, 8);
+ }
+#else
+ register uchar *p = (uchar *)(&f);
+ char b[8];
+ if (noswap) {
+ b[Q_DF(0)] = *p++;
+ b[Q_DF(1)] = *p++;
+ b[Q_DF(2)] = *p++;
+ b[Q_DF(3)] = *p++;
+ b[Q_DF(4)] = *p++;
+ b[Q_DF(5)] = *p++;
+ b[Q_DF(6)] = *p++;
+ b[Q_DF(7)] = *p;
+ } else {
+ b[Q_DF(7)] = *p++;
+ b[Q_DF(6)] = *p++;
+ b[Q_DF(5)] = *p++;
+ b[Q_DF(4)] = *p++;
+ b[Q_DF(3)] = *p++;
+ b[Q_DF(2)] = *p++;
+ b[Q_DF(1)] = *p++;
+ b[Q_DF(0)] = *p;
+ }
+ dev->write(b, 8);
+#endif
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Writes the '\0'-terminated string \a s to the stream and returns a
+ reference to the stream.
+
+ The string is serialized using writeBytes().
+*/
+
+QDataStream &QDataStream::operator<<(const char *s)
+{
+ if (!s) {
+ *this << (quint32)0;
+ return *this;
+ }
+ uint len = qstrlen(s) + 1; // also write null terminator
+ *this << (quint32)len; // write length specifier
+ writeRawData(s, len);
+ return *this;
+}
+
+
+/*!
+ Writes the length specifier \a len and the buffer \a s to the
+ stream and returns a reference to the stream.
+
+ The \a len is serialized as a quint32, followed by \a len bytes
+ from \a s. Note that the data is \e not encoded.
+
+ \sa writeRawData(), readBytes()
+*/
+
+QDataStream &QDataStream::writeBytes(const char *s, uint len)
+{
+ CHECK_STREAM_PRECOND(*this)
+ *this << (quint32)len; // write length specifier
+ if (len)
+ writeRawData(s, len);
+ return *this;
+}
+
+
+/*!
+ Writes \a len bytes from \a s to the stream. Returns the
+ number of bytes actually written, or -1 on error.
+ The data is \e not encoded.
+
+ \sa writeBytes(), QIODevice::write(), readRawData()
+*/
+
+int QDataStream::writeRawData(const char *s, int len)
+{
+ CHECK_STREAM_PRECOND(-1)
+ return dev->write(s, len);
+}
+
+/*!
+ \since 4.1
+
+ Skips \a len bytes from the device. Returns the number of bytes
+ actually skipped, or -1 on error.
+
+ This is equivalent to calling readRawData() on a buffer of length
+ \a len and ignoring the buffer.
+
+ \sa QIODevice::seek()
+*/
+int QDataStream::skipRawData(int len)
+{
+ CHECK_STREAM_PRECOND(-1)
+
+ if (dev->isSequential()) {
+ char buf[4096];
+ int sumRead = 0;
+
+ while (len > 0) {
+ int blockSize = qMin(len, (int)sizeof(buf));
+ int n = dev->read(buf, blockSize);
+ if (n == -1)
+ return -1;
+ if (n == 0)
+ return sumRead;
+
+ sumRead += n;
+ len -= blockSize;
+ }
+ return sumRead;
+ } else {
+ qint64 pos = dev->pos();
+ qint64 size = dev->size();
+ if (pos + len > size)
+ len = size - pos;
+ if (!dev->seek(pos + len))
+ return -1;
+ return len;
+ }
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn QDataStream &QDataStream::readRawBytes(char *str, uint len)
+
+ Use readRawData() instead.
+*/
+
+/*!
+ \fn QDataStream &QDataStream::writeRawBytes(const char *str, uint len)
+
+ Use writeRawData() instead.
+*/
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAM
diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h
new file mode 100644
index 0000000..4c8295e
--- /dev/null
+++ b/src/corelib/io/qdatastream.h
@@ -0,0 +1,427 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATASTREAM_H
+#define QDATASTREAM_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qglobal.h>
+
+#ifdef Status
+#error qdatastream.h must be included before any header file that defines Status
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QByteArray;
+class QIODevice;
+
+template <typename T> class QList;
+template <typename T> class QLinkedList;
+template <typename T> class QVector;
+template <typename T> class QSet;
+template <class Key, class T> class QHash;
+template <class Key, class T> class QMap;
+
+class QDataStreamPrivate;
+
+#ifndef QT_NO_DATASTREAM
+class Q_CORE_EXPORT QDataStream
+{
+public:
+ enum Version {
+ Qt_1_0 = 1,
+ Qt_2_0 = 2,
+ Qt_2_1 = 3,
+ Qt_3_0 = 4,
+ Qt_3_1 = 5,
+ Qt_3_3 = 6,
+ Qt_4_0 = 7,
+ Qt_4_1 = Qt_4_0,
+ Qt_4_2 = 8,
+ Qt_4_3 = 9,
+ Qt_4_4 = 10,
+ Qt_4_5 = 11
+#if QT_VERSION >= 0x040600
+#error Add the datastream version for this Qt version
+ , Qt_4_6 = Qt_4_5
+#endif
+ };
+
+ enum ByteOrder {
+ BigEndian = QSysInfo::BigEndian,
+ LittleEndian = QSysInfo::LittleEndian
+ };
+
+ enum Status {
+ Ok,
+ ReadPastEnd,
+ ReadCorruptData
+ };
+
+ QDataStream();
+ explicit QDataStream(QIODevice *);
+#ifdef QT3_SUPPORT
+ QDataStream(QByteArray *, int mode);
+#endif
+ QDataStream(QByteArray *, QIODevice::OpenMode flags);
+ QDataStream(const QByteArray &);
+ virtual ~QDataStream();
+
+ QIODevice *device() const;
+ void setDevice(QIODevice *);
+ void unsetDevice();
+
+ bool atEnd() const;
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT bool eof() const { return atEnd(); }
+#endif
+
+ Status status() const;
+ void setStatus(Status status);
+ void resetStatus();
+
+ ByteOrder byteOrder() const;
+ void setByteOrder(ByteOrder);
+
+ int version() const;
+ void setVersion(int);
+
+ QDataStream &operator>>(qint8 &i);
+ QDataStream &operator>>(quint8 &i);
+ QDataStream &operator>>(qint16 &i);
+ QDataStream &operator>>(quint16 &i);
+ QDataStream &operator>>(qint32 &i);
+ QDataStream &operator>>(quint32 &i);
+ QDataStream &operator>>(qint64 &i);
+ QDataStream &operator>>(quint64 &i);
+
+ QDataStream &operator>>(bool &i);
+ QDataStream &operator>>(float &f);
+ QDataStream &operator>>(double &f);
+ QDataStream &operator>>(char *&str);
+
+ QDataStream &operator<<(qint8 i);
+ QDataStream &operator<<(quint8 i);
+ QDataStream &operator<<(qint16 i);
+ QDataStream &operator<<(quint16 i);
+ QDataStream &operator<<(qint32 i);
+ QDataStream &operator<<(quint32 i);
+ QDataStream &operator<<(qint64 i);
+ QDataStream &operator<<(quint64 i);
+ QDataStream &operator<<(bool i);
+ QDataStream &operator<<(float f);
+ QDataStream &operator<<(double f);
+ QDataStream &operator<<(const char *str);
+
+ QDataStream &readBytes(char *&, uint &len);
+ int readRawData(char *, int len);
+
+ QDataStream &writeBytes(const char *, uint len);
+ int writeRawData(const char *, int len);
+
+ int skipRawData(int len);
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QDataStream &readRawBytes(char *str, uint len)
+ { readRawData(str, static_cast<int>(len)); return *this; }
+ inline QT3_SUPPORT QDataStream &writeRawBytes(const char *str, uint len)
+ { writeRawData(str, static_cast<int>(len)); return *this; }
+ inline QT3_SUPPORT bool isPrintableData() const { return false; }
+ inline QT3_SUPPORT void setPrintableData(bool) {}
+#endif
+
+private:
+ Q_DISABLE_COPY(QDataStream)
+
+ QDataStreamPrivate *d;
+
+ QIODevice *dev;
+ bool owndev;
+ bool noswap;
+ ByteOrder byteorder;
+ int ver;
+ Status q_status;
+};
+
+
+/*****************************************************************************
+ QDataStream inline functions
+ *****************************************************************************/
+
+inline QIODevice *QDataStream::device() const
+{ return dev; }
+
+inline QDataStream::ByteOrder QDataStream::byteOrder() const
+{ return byteorder; }
+
+inline int QDataStream::version() const
+{ return ver; }
+
+inline void QDataStream::setVersion(int v)
+{ ver = v; }
+
+inline QDataStream &QDataStream::operator>>(quint8 &i)
+{ return *this >> reinterpret_cast<qint8&>(i); }
+
+inline QDataStream &QDataStream::operator>>(quint16 &i)
+{ return *this >> reinterpret_cast<qint16&>(i); }
+
+inline QDataStream &QDataStream::operator>>(quint32 &i)
+{ return *this >> reinterpret_cast<qint32&>(i); }
+
+inline QDataStream &QDataStream::operator>>(quint64 &i)
+{ return *this >> reinterpret_cast<qint64&>(i); }
+
+inline QDataStream &QDataStream::operator<<(quint8 i)
+{ return *this << qint8(i); }
+
+inline QDataStream &QDataStream::operator<<(quint16 i)
+{ return *this << qint16(i); }
+
+inline QDataStream &QDataStream::operator<<(quint32 i)
+{ return *this << qint32(i); }
+
+inline QDataStream &QDataStream::operator<<(quint64 i)
+{ return *this << qint64(i); }
+
+template <typename T>
+QDataStream& operator>>(QDataStream& s, QList<T>& l)
+{
+ l.clear();
+ quint32 c;
+ s >> c;
+ for(quint32 i = 0; i < c; ++i)
+ {
+ T t;
+ s >> t;
+ l.append(t);
+ if (s.atEnd())
+ break;
+ }
+ return s;
+}
+
+template <typename T>
+QDataStream& operator<<(QDataStream& s, const QList<T>& l)
+{
+ s << quint32(l.size());
+ for (int i = 0; i < l.size(); ++i)
+ s << l.at(i);
+ return s;
+}
+
+template <typename T>
+QDataStream& operator>>(QDataStream& s, QLinkedList<T>& l)
+{
+ l.clear();
+ quint32 c;
+ s >> c;
+ for(quint32 i = 0; i < c; ++i)
+ {
+ T t;
+ s >> t;
+ l.append(t);
+ if (s.atEnd())
+ break;
+ }
+ return s;
+}
+
+template <typename T>
+QDataStream& operator<<(QDataStream& s, const QLinkedList<T>& l)
+{
+ s << quint32(l.size());
+ typename QLinkedList<T>::ConstIterator it = l.constBegin();
+ for(; it != l.constEnd(); ++it)
+ s << *it;
+ return s;
+}
+
+template<typename T>
+QDataStream& operator>>(QDataStream& s, QVector<T>& v)
+{
+ v.clear();
+ quint32 c;
+ s >> c;
+ v.resize(c);
+ for(quint32 i = 0; i < c; ++i) {
+ T t;
+ s >> t;
+ v[i] = t;
+ }
+ return s;
+}
+
+template<typename T>
+QDataStream& operator<<(QDataStream& s, const QVector<T>& v)
+{
+ s << quint32(v.size());
+ for (typename QVector<T>::const_iterator it = v.begin(); it != v.end(); ++it)
+ s << *it;
+ return s;
+}
+
+template <typename T>
+QDataStream &operator>>(QDataStream &in, QSet<T> &set)
+{
+ set.clear();
+ quint32 c;
+ in >> c;
+ for (quint32 i = 0; i < c; ++i) {
+ T t;
+ in >> t;
+ set << t;
+ if (in.atEnd())
+ break;
+ }
+ return in;
+}
+
+template <typename T>
+QDataStream& operator<<(QDataStream &out, const QSet<T> &set)
+{
+ out << quint32(set.size());
+ typename QSet<T>::const_iterator i = set.constBegin();
+ while (i != set.constEnd()) {
+ out << *i;
+ ++i;
+ }
+ return out;
+}
+
+template <class Key, class T>
+Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QHash<Key, T> &hash)
+{
+ QDataStream::Status oldStatus = in.status();
+ in.resetStatus();
+ hash.clear();
+
+ quint32 n;
+ in >> n;
+
+ for (quint32 i = 0; i < n; ++i) {
+ if (in.status() != QDataStream::Ok)
+ break;
+
+ Key k;
+ T t;
+ in >> k >> t;
+ hash.insertMulti(k, t);
+ }
+
+ if (in.status() != QDataStream::Ok)
+ hash.clear();
+ if (oldStatus != QDataStream::Ok)
+ in.setStatus(oldStatus);
+ return in;
+}
+
+template <class Key, class T>
+Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QHash<Key, T>& hash)
+{
+ out << quint32(hash.size());
+ typename QHash<Key, T>::ConstIterator it = hash.end();
+ typename QHash<Key, T>::ConstIterator begin = hash.begin();
+ while (it != begin) {
+ --it;
+ out << it.key() << it.value();
+ }
+ return out;
+}
+#ifdef qdoc
+template <class Key, class T>
+Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<Key, T> &map)
+#else
+template <class aKey, class aT>
+Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &map)
+#endif
+{
+ QDataStream::Status oldStatus = in.status();
+ in.resetStatus();
+ map.clear();
+
+ quint32 n;
+ in >> n;
+
+ map.detach();
+ map.setInsertInOrder(true);
+ for (quint32 i = 0; i < n; ++i) {
+ if (in.status() != QDataStream::Ok)
+ break;
+
+ aKey key;
+ aT value;
+ in >> key >> value;
+ map.insertMulti(key, value);
+ }
+ map.setInsertInOrder(false);
+ if (in.status() != QDataStream::Ok)
+ map.clear();
+ if (oldStatus != QDataStream::Ok)
+ in.setStatus(oldStatus);
+ return in;
+}
+
+template <class Key, class T>
+Q_OUTOFLINE_TEMPLATE QDataStream &operator<<(QDataStream &out, const QMap<Key, T> &map)
+{
+ out << quint32(map.size());
+ typename QMap<Key, T>::ConstIterator it = map.end();
+ typename QMap<Key, T>::ConstIterator begin = map.begin();
+ while (it != begin) {
+ --it;
+ out << it.key() << it.value();
+ }
+ return out;
+}
+
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDATASTREAM_H
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp
new file mode 100644
index 0000000..0fa31f8
--- /dev/null
+++ b/src/corelib/io/qdebug.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef QT_NO_DEBUG
+#undef QT_NO_DEBUG
+#endif
+#ifdef qDebug
+#undef qDebug
+#endif
+
+#include "qdebug.h"
+
+// This file is needed to force compilation of QDebug into the kernel library.
diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h
new file mode 100644
index 0000000..8334146
--- /dev/null
+++ b/src/corelib/io/qdebug.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDEBUG_H
+#define QDEBUG_H
+
+#include <QtCore/qalgorithms.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qset.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class Q_CORE_EXPORT QDebug
+{
+ struct Stream {
+ Stream(QIODevice *device) : ts(device), ref(1), type(QtDebugMsg), space(true), message_output(false) {}
+ Stream(QString *string) : ts(string, QIODevice::WriteOnly), ref(1), type(QtDebugMsg), space(true), message_output(false) {}
+ Stream(QtMsgType t) : ts(&buffer, QIODevice::WriteOnly), ref(1), type(t), space(true), message_output(true) {}
+ QTextStream ts;
+ QString buffer;
+ int ref;
+ QtMsgType type;
+ bool space;
+ bool message_output;
+ } *stream;
+public:
+ inline QDebug(QIODevice *device) : stream(new Stream(device)) {}
+ inline QDebug(QString *string) : stream(new Stream(string)) {}
+ inline QDebug(QtMsgType t) : stream(new Stream(t)) {}
+ inline QDebug(const QDebug &o):stream(o.stream) { ++stream->ref; }
+ inline QDebug &operator=(const QDebug &other);
+ inline ~QDebug() {
+ if (!--stream->ref) {
+ if(stream->message_output)
+ qt_message_output(stream->type, stream->buffer.toLocal8Bit().data());
+ delete stream;
+ }
+ }
+ inline QDebug &space() { stream->space = true; stream->ts << " "; return *this; }
+ inline QDebug &nospace() { stream->space = false; return *this; }
+ inline QDebug &maybeSpace() { if (stream->space) stream->ts << " "; return *this; }
+
+ inline QDebug &operator<<(QChar t) { stream->ts << "\'" << t << "\'"; return maybeSpace(); }
+ inline QDebug &operator<<(QBool t) { stream->ts << (bool(t) ? "true" : "false"); return maybeSpace(); }
+ inline QDebug &operator<<(bool t) { stream->ts << (t ? "true" : "false"); return maybeSpace(); }
+ inline QDebug &operator<<(char t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(signed short t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(unsigned short t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(signed int t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(unsigned int t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(signed long t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(unsigned long t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(qint64 t)
+ { stream->ts << QString::number(t); return maybeSpace(); }
+ inline QDebug &operator<<(quint64 t)
+ { stream->ts << QString::number(t); return maybeSpace(); }
+ inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(const char* t) { stream->ts << QString::fromAscii(t); return maybeSpace(); }
+ inline QDebug &operator<<(const QString & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); }
+ inline QDebug &operator<<(const QStringRef & t) { return operator<<(t.toString()); }
+ inline QDebug &operator<<(const QLatin1String &t) { stream->ts << "\"" << t.latin1() << "\""; return maybeSpace(); }
+ inline QDebug &operator<<(const QByteArray & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); }
+ inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); }
+ inline QDebug &operator<<(QTextStreamFunction f) {
+ stream->ts << f;
+ return *this;
+ }
+
+ inline QDebug &operator<<(QTextStreamManipulator m)
+ { stream->ts << m; return *this; }
+};
+
+class QNoDebug
+{
+public:
+ inline QNoDebug(){}
+ inline QNoDebug(const QDebug &){}
+ inline ~QNoDebug(){}
+#if !defined( QT_NO_TEXTSTREAM )
+ inline QNoDebug &operator<<(QTextStreamFunction) { return *this; }
+ inline QNoDebug &operator<<(QTextStreamManipulator) { return *this; }
+#endif
+ inline QNoDebug &space() { return *this; }
+ inline QNoDebug &nospace() { return *this; }
+ inline QNoDebug &maybeSpace() { return *this; }
+
+#ifndef QT_NO_MEMBER_TEMPLATES
+ template<typename T>
+ inline QNoDebug &operator<<(const T &) { return *this; }
+#endif
+};
+
+Q_CORE_EXPORT_INLINE QDebug qCritical() { return QDebug(QtCriticalMsg); }
+
+inline QDebug &QDebug::operator=(const QDebug &other)
+{
+ if (this != &other) {
+ QDebug copy(other);
+ qSwap(stream, copy.stream);
+ }
+ return *this;
+}
+
+#if defined(FORCE_UREF)
+template <class T>
+inline QDebug &operator<<(QDebug debug, const QList<T> &list)
+#else
+template <class T>
+inline QDebug operator<<(QDebug debug, const QList<T> &list)
+#endif
+{
+ debug.nospace() << "(";
+ for (Q_TYPENAME QList<T>::size_type i = 0; i < list.count(); ++i) {
+ if (i)
+ debug << ", ";
+ debug << list.at(i);
+ }
+ debug << ")";
+ return debug.space();
+}
+
+#if defined(FORCE_UREF)
+template <typename T>
+inline QDebug &operator<<(QDebug debug, const QVector<T> &vec)
+#else
+template <typename T>
+inline QDebug operator<<(QDebug debug, const QVector<T> &vec)
+#endif
+{
+ debug.nospace() << "QVector";
+ return operator<<(debug, vec.toList());
+}
+
+#if defined(FORCE_UREF)
+template <class aKey, class aT>
+inline QDebug &operator<<(QDebug debug, const QMap<aKey, aT> &map)
+#else
+template <class aKey, class aT>
+inline QDebug operator<<(QDebug debug, const QMap<aKey, aT> &map)
+#endif
+{
+ debug.nospace() << "QMap(";
+ for (typename QMap<aKey, aT>::const_iterator it = map.constBegin();
+ it != map.constEnd(); ++it) {
+ debug << "(" << it.key() << ", " << it.value() << ")";
+ }
+ debug << ")";
+ return debug.space();
+}
+
+#if defined(FORCE_UREF)
+template <class aKey, class aT>
+inline QDebug &operator<<(QDebug debug, const QHash<aKey, aT> &hash)
+#else
+template <class aKey, class aT>
+inline QDebug operator<<(QDebug debug, const QHash<aKey, aT> &hash)
+#endif
+{
+ debug.nospace() << "QHash(";
+ for (typename QHash<aKey, aT>::const_iterator it = hash.constBegin();
+ it != hash.constEnd(); ++it)
+ debug << "(" << it.key() << ", " << it.value() << ")";
+ debug << ")";
+ return debug.space();
+}
+
+#if defined(FORCE_UREF)
+template <class T1, class T2>
+inline QDebug &operator<<(QDebug debug, const QPair<T1, T2> &pair)
+#else
+template <class T1, class T2>
+inline QDebug operator<<(QDebug debug, const QPair<T1, T2> &pair)
+#endif
+{
+ debug.nospace() << "QPair(" << pair.first << "," << pair.second << ")";
+ return debug.space();
+}
+
+template <typename T>
+inline QDebug operator<<(QDebug debug, const QSet<T> &set)
+{
+ debug.nospace() << "QSet";
+ return operator<<(debug, set.toList());
+}
+
+#if !defined(QT_NO_DEBUG_STREAM)
+Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); }
+
+#else // QT_NO_DEBUG_STREAM
+#undef qDebug
+inline QNoDebug qDebug() { return QNoDebug(); }
+#define qDebug QT_NO_QDEBUG_MACRO
+
+#ifdef QT_NO_MEMBER_TEMPLATES
+template<typename T>
+inline QNoDebug operator<<(QNoDebug debug, const T &) { return debug; }
+#endif
+
+#endif
+
+#if !defined(QT_NO_WARNING_OUTPUT)
+Q_CORE_EXPORT_INLINE QDebug qWarning() { return QDebug(QtWarningMsg); }
+#else
+#undef qWarning
+inline QNoDebug qWarning() { return QNoDebug(); }
+#define qWarning QT_NO_QWARNING_MACRO
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDEBUG_H
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
new file mode 100644
index 0000000..6d75c59
--- /dev/null
+++ b/src/corelib/io/qdir.cpp
@@ -0,0 +1,2472 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qdir.h"
+#include "qabstractfileengine.h"
+#ifndef QT_NO_DEBUG_STREAM
+#include "qdebug.h"
+#endif
+#include "qfsfileengine.h"
+#include "qdatetime.h"
+#include "qstring.h"
+#include "qregexp.h"
+#include "qvector.h"
+#ifdef QT_BUILD_CORE_LIB
+# include "qresource.h"
+#endif
+
+#include "../kernel/qcoreglobaldata_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+static QString driveSpec(const QString &path)
+{
+#ifdef Q_OS_WIN
+ if (path.size() < 2)
+ return QString();
+ char c = path.at(0).toAscii();
+ if (c < 'a' && c > 'z' && c < 'A' && c > 'Z')
+ return QString();
+ if (path.at(1).toAscii() != ':')
+ return QString();
+ return path.mid(0, 2);
+#else
+ Q_UNUSED(path);
+ return QString();
+#endif
+}
+
+//************* QDirPrivate
+class QDirPrivate
+{
+ QDir *q_ptr;
+ Q_DECLARE_PUBLIC(QDir)
+
+protected:
+ QDirPrivate(QDir*, const QDir *copy=0);
+ ~QDirPrivate();
+
+ QString initFileEngine(const QString &file);
+
+ void updateFileLists() const;
+ void sortFileList(QDir::SortFlags, QStringList &, QStringList *, QFileInfoList *) const;
+
+private:
+#ifdef QT3_SUPPORT
+ QChar filterSepChar;
+ bool matchAllDirs;
+#endif
+ static inline QChar getFilterSepChar(const QString &nameFilter)
+ {
+ QChar sep(QLatin1Char(';'));
+ int i = nameFilter.indexOf(sep, 0);
+ if (i == -1 && nameFilter.indexOf(QLatin1Char(' '), 0) != -1)
+ sep = QChar(QLatin1Char(' '));
+ return sep;
+ }
+ static inline QStringList splitFilters(const QString &nameFilter, QChar sep=0) {
+ if(sep == 0)
+ sep = getFilterSepChar(nameFilter);
+ QStringList ret = nameFilter.split(sep);
+ for(int i = 0; i < ret.count(); i++)
+ ret[i] = ret[i].trimmed();
+ return ret;
+ }
+
+ struct Data {
+ inline Data()
+ : ref(1), fileEngine(0)
+ { clear(); }
+ inline Data(const Data &copy)
+ : ref(1), path(copy.path), nameFilters(copy.nameFilters), sort(copy.sort),
+ filters(copy.filters), fileEngine(0)
+ { clear(); }
+ inline ~Data()
+ { delete fileEngine; }
+
+ inline void clear() {
+ listsDirty = 1;
+ }
+ mutable QAtomicInt ref;
+
+ QString path;
+ QStringList nameFilters;
+ QDir::SortFlags sort;
+ QDir::Filters filters;
+
+ mutable QAbstractFileEngine *fileEngine;
+
+ mutable uint listsDirty : 1;
+ mutable QStringList files;
+ mutable QFileInfoList fileInfos;
+ } *data;
+ inline void setPath(const QString &p)
+ {
+ detach(false);
+ QString path = p;
+ if ((path.endsWith(QLatin1Char('/')) || path.endsWith(QLatin1Char('\\')))
+ && path.length() > 1) {
+#ifdef Q_OS_WIN
+ if (!(path.length() == 3 && path.at(1) == QLatin1Char(':')))
+#endif
+ path.truncate(path.length() - 1);
+ }
+ if(!data->fileEngine || !QDir::isRelativePath(path))
+ path = initFileEngine(path);
+ data->fileEngine->setFileName(path);
+ // set the path to be the qt friendly version so then we can operate on it using just /
+ data->path = data->fileEngine->fileName(QAbstractFileEngine::DefaultName);
+ data->clear();
+ }
+ inline void reset() {
+ detach();
+ data->clear();
+ }
+ void detach(bool createFileEngine = true);
+};
+
+QDirPrivate::QDirPrivate(QDir *qq, const QDir *copy) : q_ptr(qq)
+#ifdef QT3_SUPPORT
+ , filterSepChar(0)
+ , matchAllDirs(false)
+#endif
+{
+ if(copy) {
+ copy->d_func()->data->ref.ref();
+ data = copy->d_func()->data;
+ } else {
+ data = new QDirPrivate::Data;
+ data->clear();
+ }
+}
+
+QDirPrivate::~QDirPrivate()
+{
+ if (!data->ref.deref())
+ delete data;
+ data = 0;
+ q_ptr = 0;
+}
+
+/* For sorting */
+struct QDirSortItem {
+ QString filename_cache;
+ QString suffix_cache;
+ QFileInfo item;
+};
+static int qt_cmp_si_sort_flags;
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+#ifdef Q_OS_WINCE
+static int __cdecl qt_cmp_si(const void *n1, const void *n2)
+#else
+static int qt_cmp_si(const void *n1, const void *n2)
+#endif
+{
+ if (!n1 || !n2)
+ return 0;
+
+ QDirSortItem* f1 = (QDirSortItem*)n1;
+ QDirSortItem* f2 = (QDirSortItem*)n2;
+
+ if ((qt_cmp_si_sort_flags & QDir::DirsFirst) && (f1->item.isDir() != f2->item.isDir()))
+ return f1->item.isDir() ? -1 : 1;
+ if ((qt_cmp_si_sort_flags & QDir::DirsLast) && (f1->item.isDir() != f2->item.isDir()))
+ return f1->item.isDir() ? 1 : -1;
+
+ int r = 0;
+ int sortBy = (qt_cmp_si_sort_flags & QDir::SortByMask)
+ | (qt_cmp_si_sort_flags & QDir::Type);
+
+ switch (sortBy) {
+ case QDir::Time:
+ r = f1->item.lastModified().secsTo(f2->item.lastModified());
+ break;
+ case QDir::Size:
+ r = int(qBound<qint64>(-1, f2->item.size() - f1->item.size(), 1));
+ break;
+ case QDir::Type:
+ {
+ bool ic = qt_cmp_si_sort_flags & QDir::IgnoreCase;
+
+ if (f1->suffix_cache.isNull())
+ f1->suffix_cache = ic ? f1->item.suffix().toLower()
+ : f1->item.suffix();
+ if (f2->suffix_cache.isNull())
+ f2->suffix_cache = ic ? f2->item.suffix().toLower()
+ : f2->item.suffix();
+
+ r = qt_cmp_si_sort_flags & QDir::LocaleAware
+ ? f1->suffix_cache.localeAwareCompare(f2->suffix_cache)
+ : f1->suffix_cache.compare(f2->suffix_cache);
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (r == 0 && sortBy != QDir::Unsorted) {
+ // Still not sorted - sort by name
+ bool ic = qt_cmp_si_sort_flags & QDir::IgnoreCase;
+
+ if (f1->filename_cache.isNull())
+ f1->filename_cache = ic ? f1->item.fileName().toLower()
+ : f1->item.fileName();
+ if (f2->filename_cache.isNull())
+ f2->filename_cache = ic ? f2->item.fileName().toLower()
+ : f2->item.fileName();
+
+ r = qt_cmp_si_sort_flags & QDir::LocaleAware
+ ? f1->filename_cache.localeAwareCompare(f2->filename_cache)
+ : f1->filename_cache.compare(f2->filename_cache);
+ }
+
+ if (r == 0) // Enforce an order - the order the items appear in the array
+ r = (char*)n1 - (char*)n2;
+
+ if (qt_cmp_si_sort_flags & QDir::Reversed)
+ return -r;
+ return r;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QStringList &l,
+ QStringList *names, QFileInfoList *infos) const
+{
+ if(names)
+ names->clear();
+ if(infos)
+ infos->clear();
+ if(!l.isEmpty()) {
+ QDirSortItem *si= new QDirSortItem[l.count()];
+ int i;
+ for (i = 0; i < l.size(); ++i) {
+ QString path = data->path;
+ if (!path.isEmpty() && !path.endsWith(QLatin1Char('/')))
+ path += QLatin1Char('/');
+ si[i].item = QFileInfo(path + l.at(i));
+ }
+ qt_cmp_si_sort_flags = sort;
+ if ((sort & QDir::SortByMask) != QDir::Unsorted)
+ qsort(si, i, sizeof(si[0]), qt_cmp_si);
+ // put them back in the list(s)
+ for (int j = 0; j<i; j++) {
+ if(infos)
+ infos->append(si[j].item);
+ if(names)
+ names->append(si[j].item.fileName());
+ }
+ delete [] si;
+ }
+}
+
+inline void QDirPrivate::updateFileLists() const
+{
+ if(data->listsDirty) {
+ QStringList l = data->fileEngine->entryList(data->filters, data->nameFilters);
+ sortFileList(data->sort, l, &data->files, &data->fileInfos);
+ data->listsDirty = 0;
+ }
+}
+
+QString QDirPrivate::initFileEngine(const QString &path)
+{
+ detach(false);
+ delete data->fileEngine;
+ data->fileEngine = 0;
+ data->clear();
+ data->fileEngine = QAbstractFileEngine::create(path);
+ return data->fileEngine->fileName(QAbstractFileEngine::DefaultName);
+}
+
+void QDirPrivate::detach(bool createFileEngine)
+{
+ qAtomicDetach(data);
+ if (createFileEngine) {
+ delete data->fileEngine;
+ data->fileEngine = QAbstractFileEngine::create(data->path);
+ }
+}
+
+/*!
+ \class QDir
+ \brief The QDir class provides access to directory structures and their contents.
+
+ \ingroup io
+ \ingroup shared
+ \reentrant
+ \mainclass
+
+ A QDir is used to manipulate path names, access information
+ regarding paths and files, and manipulate the underlying file
+ system. It can also be used to access Qt's \l{resource system}.
+
+ Qt uses "/" as a universal directory separator in the same way
+ that "/" is used as a path separator in URLs. If you always use
+ "/" as a directory separator, Qt will translate your paths to
+ conform to the underlying operating system.
+
+ A QDir can point to a file using either a relative or an absolute
+ path. Absolute paths begin with the directory separator
+ (optionally preceded by a drive specification under Windows).
+ Relative file names begin with a directory name or a file name and
+ specify a path relative to the current directory.
+
+ Examples of absolute paths:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 0
+
+ On Windows, the second example above will be translated to
+ \c{C:\Documents and Settings} when used to access files.
+
+ Examples of relative paths:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 1
+
+ You can use the isRelative() or isAbsolute() functions to check if
+ a QDir is using a relative or an absolute file path. Call
+ makeAbsolute() to convert a relative QDir to an absolute one.
+
+ \section1 Navigation and Directory Operations
+
+ A directory's path can be obtained with the path() function, and
+ a new path set with the setPath() function. The absolute path to
+ a directory is found by calling absolutePath().
+
+ The name of a directory is found using the dirName() function. This
+ typically returns the last element in the absolute path that specifies
+ the location of the directory. However, it can also return "." if
+ the QDir represents the current directory.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 2
+
+ The path for a directory can also be changed with the cd() and cdUp()
+ functions, both of which operate like familiar shell commands.
+ When cd() is called with the name of an existing directory, the QDir
+ object changes directory so that it represents that directory instead.
+ The cdUp() function changes the directory of the QDir object so that
+ it refers to its parent directory; i.e. cd("..") is equivalent to
+ cdUp().
+
+ Directories can be created with mkdir(), renamed with rename(), and
+ removed with rmdir().
+
+ You can test for the presence of a directory with a given name by
+ using exists(), and the properties of a directory can be tested with
+ isReadable(), isAbsolute(), isRelative(), and isRoot().
+
+ The refresh() function re-reads the directory's data from disk.
+
+ \section1 Files and Directory Contents
+
+ Directories contain a number of entries, representing files,
+ directories, and symbolic links. The number of entries in a
+ directory is returned by count().
+ A string list of the names of all the entries in a directory can be
+ obtained with entryList(). If you need information about each
+ entry, use entryInfoList() to obtain a list of QFileInfo objects.
+
+ Paths to files and directories within a directory can be
+ constructed using filePath() and absoluteFilePath().
+ The filePath() function returns a path to the specified file
+ or directory relative to the path of the QDir object;
+ absoluteFilePath() returns an absolute path to the specified
+ file or directory. Neither of these functions checks for the
+ existence of files or directory; they only construct paths.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 3
+
+ Files can be removed by using the remove() function. Directories
+ cannot be removed in the same way as files; use rmdir() to remove
+ them instead.
+
+ It is possible to reduce the number of entries returned by
+ entryList() and entryInfoList() by applying filters to a QDir object.
+ You can apply a name filter to specify a pattern with wildcards that
+ file names need to match, an attribute filter that selects properties
+ of entries and can distinguish between files and directories, and a
+ sort order.
+
+ Name filters are lists of strings that are passed to setNameFilters().
+ Attribute filters consist of a bitwise OR combination of Filters, and
+ these are specified when calling setFilter().
+ The sort order is specified using setSorting() with a bitwise OR
+ combination of SortFlags.
+
+ You can test to see if a filename matches a filter using the match()
+ function.
+
+ Filter and sort order flags may also be specified when calling
+ entryList() and entryInfoList() in order to override previously defined
+ behavior.
+
+ \section1 The Current Directory and Other Special Paths
+
+ Access to some common directories is provided with a number of static
+ functions that return QDir objects. There are also corresponding functions
+ for these that return strings:
+
+ \table
+ \header \o QDir \o QString \o Return Value
+ \row \o current() \o currentPath() \o The application's working directory
+ \row \o home() \o homePath() \o The user's home directory
+ \row \o root() \o rootPath() \o The root directory
+ \row \o temp() \o tempPath() \o The system's temporary directory
+ \endtable
+
+ The setCurrent() static function can also be used to set the application's
+ working directory.
+
+ If you want to find the directory containing the application's executable,
+ see \l{QCoreApplication::applicationDirPath()}.
+
+ The drives() static function provides a list of root directories for each
+ device that contains a filing system. On Unix systems this returns a list
+ containing a single root directory "/"; on Windows the list will usually
+ contain \c{C:/}, and possibly other drive letters such as \c{D:/}, depending
+ on the configuration of the user's system.
+
+ \section1 Path Manipulation and Strings
+
+ Paths containing "." elements that reference the current directory at that
+ point in the path, ".." elements that reference the parent directory, and
+ symbolic links can be reduced to a canonical form using the canonicalPath()
+ function.
+
+ Paths can also be simplified by using cleanPath() to remove redundant "/"
+ and ".." elements.
+
+ It is sometimes necessary to be able to show a path in the native
+ representation for the user's platform. The static toNativeSeparators()
+ function returns a copy of the specified path in which each directory
+ separator is replaced by the appropriate separator for the underlying
+ operating system.
+
+ \section1 Examples
+
+ Check if a directory exists:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 4
+
+ (We could also use the static convenience function
+ QFile::exists().)
+
+ Traversing directories and reading a file:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 5
+
+ A program that lists all the files in the current directory
+ (excluding symbolic links), sorted by size, smallest first:
+
+ \snippet doc/src/snippets/qdir-listfiles/main.cpp 0
+
+ \sa QFileInfo, QFile, QFileDialog, QApplication::applicationDirPath(), {Find Files Example}
+*/
+
+/*!
+ Constructs a QDir pointing to the given directory \a path. If path
+ is empty the program's working directory, ("."), is used.
+
+ \sa currentPath()
+*/
+
+QDir::QDir(const QString &path) : d_ptr(new QDirPrivate(this))
+{
+ Q_D(QDir);
+ d->setPath(path.isEmpty() ? QString::fromLatin1(".") : path);
+ d->data->nameFilters = QStringList(QString::fromLatin1("*"));
+ d->data->filters = AllEntries;
+ d->data->sort = SortFlags(Name | IgnoreCase);
+}
+
+/*!
+ Constructs a QDir with path \a path, that filters its entries by
+ name using \a nameFilter and by attributes using \a filters. It
+ also sorts the names using \a sort.
+
+ The default \a nameFilter is an empty string, which excludes
+ nothing; the default \a filters is \l AllEntries, which also means
+ exclude nothing. The default \a sort is \l Name | \l IgnoreCase,
+ i.e. sort by name case-insensitively.
+
+ If \a path is an empty string, QDir uses "." (the current
+ directory). If \a nameFilter is an empty string, QDir uses the
+ name filter "*" (all files).
+
+ Note that \a path need not exist.
+
+ \sa exists(), setPath(), setNameFilter(), setFilter(), setSorting()
+*/
+
+QDir::QDir(const QString &path, const QString &nameFilter,
+ SortFlags sort, Filters filters) : d_ptr(new QDirPrivate(this))
+{
+ Q_D(QDir);
+ d->setPath(path.isEmpty() ? QString::fromLatin1(".") : path);
+ d->data->nameFilters = QDir::nameFiltersFromString(nameFilter);
+ bool empty = d->data->nameFilters.isEmpty();
+ if(!empty) {
+ empty = true;
+ for(int i = 0; i < d->data->nameFilters.size(); ++i) {
+ if(!d->data->nameFilters.at(i).isEmpty()) {
+ empty = false;
+ break;
+ }
+ }
+ }
+ if (empty)
+ d->data->nameFilters = QStringList(QString::fromLatin1("*"));
+ d->data->sort = sort;
+ d->data->filters = filters;
+}
+
+/*!
+ Constructs a QDir object that is a copy of the QDir object for
+ directory \a dir.
+
+ \sa operator=()
+*/
+
+QDir::QDir(const QDir &dir) : d_ptr(new QDirPrivate(this, &dir))
+{
+}
+
+/*!
+ Destroys the QDir object frees up its resources. This has no
+ effect on the underlying directory in the file system.
+*/
+
+QDir::~QDir()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+
+/*!
+ Sets the path of the directory to \a path. The path is cleaned of
+ redundant ".", ".." and of multiple separators. No check is made
+ to see whether a directory with this path actually exists; but you
+ can check for yourself using exists().
+
+ The path can be either absolute or relative. Absolute paths begin
+ with the directory separator "/" (optionally preceded by a drive
+ specification under Windows). Relative file names begin with a
+ directory name or a file name and specify a path relative to the
+ current directory. An example of an absolute path is the string
+ "/tmp/quartz", a relative path might look like "src/fatlib".
+
+ \sa path(), absolutePath(), exists(), cleanPath(), dirName(),
+ absoluteFilePath(), isRelative(), makeAbsolute()
+*/
+
+void QDir::setPath(const QString &path)
+{
+ Q_D(QDir);
+ d->setPath(path);
+}
+
+/*!
+ Returns the path. This may contain symbolic links, but never
+ contains redundant ".", ".." or multiple separators.
+
+ The returned path can be either absolute or relative (see
+ setPath()).
+
+ \sa setPath(), absolutePath(), exists(), cleanPath(), dirName(),
+ absoluteFilePath(), toNativeSeparators(), makeAbsolute()
+*/
+
+QString QDir::path() const
+{
+ Q_D(const QDir);
+ return d->data->path;
+}
+
+/*!
+ Returns the absolute path (a path that starts with "/" or with a
+ drive specification), which may contain symbolic links, but never
+ contains redundant ".", ".." or multiple separators.
+
+ \sa setPath(), canonicalPath(), exists(), cleanPath(),
+ dirName(), absoluteFilePath()
+*/
+
+QString QDir::absolutePath() const
+{
+ Q_D(const QDir);
+ QString ret = d->data->path;
+ if (QDir::isRelativePath(ret))
+ ret = absoluteFilePath(QString::fromLatin1(""));
+ return cleanPath(ret);
+}
+
+
+/*!
+ Returns the canonical path, i.e. a path without symbolic links or
+ redundant "." or ".." elements.
+
+ On systems that do not have symbolic links this function will
+ always return the same string that absolutePath() returns. If the
+ canonical path does not exist (normally due to dangling symbolic
+ links) canonicalPath() returns an empty string.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 6
+
+ \sa path(), absolutePath(), exists(), cleanPath(), dirName(),
+ absoluteFilePath()
+*/
+
+QString QDir::canonicalPath() const
+{
+ Q_D(const QDir);
+
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return cleanPath(d->data->fileEngine->fileName(QAbstractFileEngine::CanonicalName));
+}
+
+/*!
+ Returns the name of the directory; this is \e not the same as the
+ path, e.g. a directory with the name "mail", might have the path
+ "/var/spool/mail". If the directory has no name (e.g. it is the
+ root directory) an empty string is returned.
+
+ No check is made to ensure that a directory with this name
+ actually exists; but see exists().
+
+ \sa path(), filePath(), absolutePath(), absoluteFilePath()
+*/
+
+QString QDir::dirName() const
+{
+ Q_D(const QDir);
+ int pos = d->data->path.lastIndexOf(QLatin1Char('/'));
+ if (pos == -1)
+ return d->data->path;
+ return d->data->path.mid(pos + 1);
+}
+
+/*!
+ Returns the path name of a file in the directory. Does \e not
+ check if the file actually exists in the directory; but see
+ exists(). If the QDir is relative the returned path name will also
+ be relative. Redundant multiple separators or "." and ".."
+ directories in \a fileName are not removed (see cleanPath()).
+
+ \sa dirName() absoluteFilePath(), isRelative(), canonicalPath()
+*/
+
+QString QDir::filePath(const QString &fileName) const
+{
+ Q_D(const QDir);
+ if (isAbsolutePath(fileName))
+ return QString(fileName);
+
+ QString ret = d->data->path;
+ if(!fileName.isEmpty()) {
+ if (!ret.isEmpty() && ret[(int)ret.length()-1] != QLatin1Char('/') && fileName[0] != QLatin1Char('/'))
+ ret += QLatin1Char('/');
+ ret += fileName;
+ }
+ return ret;
+}
+
+/*!
+ Returns the absolute path name of a file in the directory. Does \e
+ not check if the file actually exists in the directory; but see
+ exists(). Redundant multiple separators or "." and ".."
+ directories in \a fileName are not removed (see cleanPath()).
+
+ \sa relativeFilePath() filePath() canonicalPath()
+*/
+
+QString QDir::absoluteFilePath(const QString &fileName) const
+{
+ Q_D(const QDir);
+ if (isAbsolutePath(fileName))
+ return fileName;
+ if(!d->data->fileEngine)
+ return fileName;
+
+ QString ret;
+#ifndef QT_NO_FSFILEENGINE
+ if (isRelativePath(d->data->path)) //get pwd
+ ret = QFSFileEngine::currentPath(fileName);
+#endif
+ if(!d->data->path.isEmpty() && d->data->path != QLatin1String(".")) {
+ if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
+ ret += QLatin1Char('/');
+ ret += d->data->path;
+ }
+ if (!fileName.isEmpty()) {
+ if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
+ ret += QLatin1Char('/');
+ ret += fileName;
+ }
+ return ret;
+}
+
+/*!
+ Returns the path to \a fileName relative to the directory.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 7
+
+ \sa absoluteFilePath() filePath() canonicalPath()
+*/
+
+QString QDir::relativeFilePath(const QString &fileName) const
+{
+ QString dir = absolutePath();
+ QString file = cleanPath(fileName);
+
+ if (isRelativePath(file) || isRelativePath(dir))
+ return file;
+
+ QString dirDrive = driveSpec(dir);
+ QString fileDrive = driveSpec(file);
+
+ bool fileDriveMissing = false;
+ if (fileDrive.isEmpty()) {
+ fileDrive = dirDrive;
+ fileDriveMissing = true;
+ }
+
+#ifdef Q_OS_WIN
+ if (fileDrive.toLower() != dirDrive.toLower()
+ || (file.startsWith(QLatin1String("//"))
+ && !dir.startsWith(QLatin1String("//"))))
+#else
+ if (fileDrive != dirDrive)
+#endif
+ return file;
+
+ dir.remove(0, dirDrive.size());
+ if (!fileDriveMissing)
+ file.remove(0, fileDrive.size());
+
+ QString result;
+ QStringList dirElts = dir.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ QStringList fileElts = file.split(QLatin1Char('/'), QString::SkipEmptyParts);
+
+ int i = 0;
+ while (i < dirElts.size() && i < fileElts.size() &&
+#ifdef Q_OS_WIN
+ dirElts.at(i).toLower() == fileElts.at(i).toLower())
+#else
+ dirElts.at(i) == fileElts.at(i))
+#endif
+ ++i;
+
+ for (int j = 0; j < dirElts.size() - i; ++j)
+ result += QLatin1String("../");
+
+ for (int j = i; j < fileElts.size(); ++j) {
+ result += fileElts.at(j);
+ if (j < fileElts.size() - 1)
+ result += QLatin1Char('/');
+ }
+
+ return result;
+}
+
+/*!
+ \obsolete
+
+ Use QDir::toNativeSeparators() instead.
+*/
+QString QDir::convertSeparators(const QString &pathName)
+{
+ return toNativeSeparators(pathName);
+}
+
+/*!
+ \since 4.2
+
+ Returns \a pathName with the '/' separators converted to
+ separators that are appropriate for the underlying operating
+ system.
+
+ On Windows, toNativeSeparators("c:/winnt/system32") returns
+ "c:\\winnt\\system32".
+
+ The returned string may be the same as the argument on some
+ operating systems, for example on Unix.
+
+ \sa fromNativeSeparators(), separator()
+*/
+QString QDir::toNativeSeparators(const QString &pathName)
+{
+ QString n(pathName);
+#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX)
+ for (int i=0; i<(int)n.length(); i++) {
+ if (n[i] == QLatin1Char('/'))
+ n[i] = QLatin1Char('\\');
+ }
+#endif
+ return n;
+}
+
+/*!
+ \since 4.2
+
+ Returns \a pathName using '/' as file separator. On Windows,
+ for instance, fromNativeSeparators("\c{c:\\winnt\\system32}") returns
+ "c:/winnt/system32".
+
+ The returned string may be the same as the argument on some
+ operating systems, for example on Unix.
+
+ \sa toNativeSeparators(), separator()
+*/
+QString QDir::fromNativeSeparators(const QString &pathName)
+{
+ QString n(pathName);
+#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX)
+ for (int i=0; i<(int)n.length(); i++) {
+ if (n[i] == QLatin1Char('\\'))
+ n[i] = QLatin1Char('/');
+ }
+#endif
+ return n;
+}
+
+/*!
+ Changes the QDir's directory to \a dirName.
+
+ Returns true if the new directory exists and is readable;
+ otherwise returns false. Note that the logical cd() operation is
+ not performed if the new directory does not exist.
+
+ Calling cd("..") is equivalent to calling cdUp().
+
+ \sa cdUp(), isReadable(), exists(), path()
+*/
+
+bool QDir::cd(const QString &dirName)
+{
+ Q_D(QDir);
+
+ if (dirName.isEmpty() || dirName == QLatin1String("."))
+ return true;
+ QString newPath = d->data->path;
+ if (isAbsolutePath(dirName)) {
+ newPath = cleanPath(dirName);
+ } else {
+ if (isRoot()) {
+ if (dirName == QLatin1String(".."))
+ return false;
+ } else {
+ newPath += QLatin1Char('/');
+ }
+
+ newPath += dirName;
+ if (dirName.indexOf(QLatin1Char('/')) >= 0
+ || d->data->path == QLatin1String(".")
+ || dirName == QLatin1String("..")) {
+ newPath = cleanPath(newPath);
+ /*
+ If newPath starts with .., we convert it to absolute to
+ avoid infinite looping on
+
+ QDir dir(".");
+ while (dir.cdUp())
+ ;
+ */
+ if (newPath.startsWith(QLatin1String(".."))) {
+ newPath = QFileInfo(newPath).absoluteFilePath();
+ }
+ }
+ }
+ {
+ QFileInfo fi(newPath);
+ if (!(fi.exists() && fi.isDir()))
+ return false;
+ }
+
+ d->setPath(newPath);
+ refresh();
+ return true;
+}
+
+/*!
+ Changes directory by moving one directory up from the QDir's
+ current directory.
+
+ Returns true if the new directory exists and is readable;
+ otherwise returns false. Note that the logical cdUp() operation is
+ not performed if the new directory does not exist.
+
+ \sa cd(), isReadable(), exists(), path()
+*/
+
+bool QDir::cdUp()
+{
+ return cd(QString::fromLatin1(".."));
+}
+
+/*!
+ Returns the string list set by setNameFilters()
+*/
+
+QStringList QDir::nameFilters() const
+{
+ Q_D(const QDir);
+
+ return d->data->nameFilters;
+}
+
+/*!
+ Sets the name filters used by entryList() and entryInfoList() to the
+ list of filters specified by \a nameFilters.
+
+ Each name filter is a wildcard (globbing) filter that understands
+ \c{*} and \c{?} wildcards. (See \l{QRegExp wildcard matching}.)
+
+ For example, the following code sets three name filters on a QDir
+ to ensure that only files with extensions typically used for C++
+ source files are listed:
+
+ \snippet doc/src/snippets/qdir-namefilters/main.cpp 0
+
+ \sa nameFilters(), setFilter()
+*/
+
+void QDir::setNameFilters(const QStringList &nameFilters)
+{
+ Q_D(QDir);
+ d->detach();
+ d->data->nameFilters = nameFilters;
+}
+
+/*!
+ \obsolete
+ Adds \a path to the search paths searched in to find resources
+ that are not specified with an absolute path. The default search
+ path is to search only in the root (\c{:/}).
+
+ Use QDir::addSearchPath() with a prefix instead.
+
+ \sa {The Qt Resource System}, QResource::addSearchPath()
+*/
+
+void QDir::addResourceSearchPath(const QString &path)
+{
+#ifdef QT_BUILD_CORE_LIB
+ QResource::addSearchPath(path);
+#else
+ Q_UNUSED(path)
+#endif
+}
+
+#ifdef QT_BUILD_CORE_LIB
+/*!
+ \since 4.3
+
+ Sets or replaces Qt's search paths for file names with the prefix \a prefix
+ to \a searchPaths.
+
+ To specify a prefix for a file name, prepend the prefix followed by a single
+ colon (e.g., "images:undo.png", "xmldocs:books.xml"). \a prefix can only
+ contain letters or numbers (e.g., it cannot contain a colon, nor a slash).
+
+ Qt uses this search path to locate files with a known prefix. The search
+ path entries are tested in order, starting with the first entry.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 8
+
+ File name prefix must be at least 2 characters long to avoid conflicts with
+ Windows drive letters.
+
+ Search paths may contain paths to \l{The Qt Resource System}.
+*/
+void QDir::setSearchPaths(const QString &prefix, const QStringList &searchPaths)
+{
+ if (prefix.length() < 2) {
+ qWarning("QDir::setSearchPaths: Prefix must be longer than 1 character");
+ return;
+ }
+
+ for (int i = 0; i < prefix.count(); i++) {
+ if (!prefix.at(i).isLetterOrNumber()) {
+ qWarning("QDir::setSearchPaths: Prefix can only contain letters or numbers");
+ return;
+ }
+ }
+
+ QWriteLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock);
+ QMap<QString, QStringList> &paths = QCoreGlobalData::instance()->dirSearchPaths;
+ if (searchPaths.isEmpty()) {
+ paths.remove(prefix);
+ } else {
+ paths.insert(prefix, searchPaths);
+ }
+}
+
+/*!
+ \since 4.3
+
+ Adds \a path to the search path for \a prefix.
+
+ \sa setSearchPaths()
+*/
+void QDir::addSearchPath(const QString &prefix, const QString &path)
+{
+ if (path.isEmpty())
+ return;
+
+ QWriteLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock);
+ QCoreGlobalData::instance()->dirSearchPaths[prefix] += path;
+}
+
+/*!
+ \since 4.3
+
+ Returns the search paths for \a prefix.
+
+ \sa setSearchPaths(), addSearchPath()
+*/
+QStringList QDir::searchPaths(const QString &prefix)
+{
+ QReadLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock);
+ return QCoreGlobalData::instance()->dirSearchPaths.value(prefix);
+}
+
+#endif // QT_BUILD_CORE_LIB
+
+/*!
+ Returns the value set by setFilter()
+*/
+
+QDir::Filters QDir::filter() const
+{
+ Q_D(const QDir);
+
+ return d->data->filters;
+}
+
+/*!
+ \enum QDir::Filter
+
+ This enum describes the filtering options available to QDir; e.g.
+ for entryList() and entryInfoList(). The filter value is specified
+ by combining values from the following list using the bitwise OR
+ operator:
+
+ \value Dirs List directories that match the filters.
+ \value AllDirs List all directories; i.e. don't apply the filters
+ to directory names.
+ \value Files List files.
+ \value Drives List disk drives (ignored under Unix).
+ \value NoSymLinks Do not list symbolic links (ignored by operating
+ systems that don't support symbolic links).
+ \value NoDotAndDotDot Do not list the special entries "." and "..".
+ \value AllEntries List directories, files, drives and symlinks (this does not list
+ broken symlinks unless you specify System).
+ \value Readable List files for which the application has read
+ access. The Readable value needs to be combined
+ with Dirs or Files.
+ \value Writable List files for which the application has write
+ access. The Writable value needs to be combined
+ with Dirs or Files.
+ \value Executable List files for which the application has
+ execute access. The Executable value needs to be
+ combined with Dirs or Files.
+ \value Modified Only list files that have been modified (ignored
+ under Unix).
+ \value Hidden List hidden files (on Unix, files starting with a .).
+ \value System List system files (on Unix, FIFOs, sockets and
+ device files)
+ \value CaseSensitive The filter should be case sensitive.
+
+ \omitvalue DefaultFilter
+ \omitvalue TypeMask
+ \omitvalue All
+ \omitvalue RWEMask
+ \omitvalue AccessMask
+ \omitvalue PermissionMask
+ \omitvalue NoFilter
+
+ Functions that use Filter enum values to filter lists of files
+ and directories will include symbolic links to files and directories
+ unless you set the NoSymLinks value.
+
+ A default constructed QDir will not filter out files based on
+ their permissions, so entryList() and entryInfoList() will return
+ all files that are readable, writable, executable, or any
+ combination of the three. This makes the default easy to write,
+ and at the same time useful.
+
+ For example, setting the \c Readable, \c Writable, and \c Files
+ flags allows all files to be listed for which the application has read
+ access, write access or both. If the \c Dirs and \c Drives flags are
+ also included in this combination then all drives, directories, all
+ files that the application can read, write, or execute, and symlinks
+ to such files/directories can be listed.
+
+ To retrieve the permissons for a directory, use the
+ entryInfoList() function to get the associated QFileInfo objects
+ and then use the QFileInfo::permissons() to obtain the permissions
+ and ownership for each file.
+*/
+
+/*!
+ Sets the filter used by entryList() and entryInfoList() to \a
+ filters. The filter is used to specify the kind of files that
+ should be returned by entryList() and entryInfoList(). See
+ \l{QDir::Filter}.
+
+ \sa filter(), setNameFilters()
+*/
+
+void QDir::setFilter(Filters filters)
+{
+ Q_D(QDir);
+
+ d->detach();
+ d->data->filters = filters;
+}
+
+/*!
+ Returns the value set by setSorting()
+
+ \sa setSorting() SortFlag
+*/
+
+QDir::SortFlags QDir::sorting() const
+{
+ Q_D(const QDir);
+
+ return d->data->sort;
+}
+
+/*!
+ \enum QDir::SortFlag
+
+ This enum describes the sort options available to QDir, e.g. for
+ entryList() and entryInfoList(). The sort value is specified by
+ OR-ing together values from the following list:
+
+ \value Name Sort by name.
+ \value Time Sort by time (modification time).
+ \value Size Sort by file size.
+ \value Type Sort by file type (extension).
+ \value Unsorted Do not sort.
+ \value NoSort Not sorted by default.
+
+ \value DirsFirst Put the directories first, then the files.
+ \value DirsLast Put the files first, then the directories.
+ \value Reversed Reverse the sort order.
+ \value IgnoreCase Sort case-insensitively.
+ \value LocaleAware Sort items appropriately using the current locale settings.
+
+ \omitvalue SortByMask
+ \omitvalue DefaultSort
+
+ You can only specify one of the first four.
+
+ If you specify both DirsFirst and Reversed, directories are
+ still put first, but in reverse order; the files will be listed
+ after the directories, again in reverse order.
+*/
+
+/*!
+ Sets the sort order used by entryList() and entryInfoList().
+
+ The \a sort is specified by OR-ing values from the enum
+ \l{QDir::SortFlag}.
+
+ \sa sorting() SortFlag
+*/
+
+void QDir::setSorting(SortFlags sort)
+{
+ Q_D(QDir);
+
+ d->detach();
+ d->data->sort = sort;
+}
+
+
+/*!
+ Returns the total number of directories and files in the directory.
+
+ Equivalent to entryList().count().
+
+ \sa operator[](), entryList()
+*/
+
+uint QDir::count() const
+{
+ Q_D(const QDir);
+
+ d->updateFileLists();
+ return d->data->files.count();
+}
+
+/*!
+ Returns the file name at position \a pos in the list of file
+ names. Equivalent to entryList().at(index).
+
+ Returns an empty string if \a pos is out of range or if the
+ entryList() function failed.
+
+ \sa count(), entryList()
+*/
+
+QString QDir::operator[](int pos) const
+{
+ Q_D(const QDir);
+
+ d->updateFileLists();
+ return d->data->files[pos];
+}
+
+/*!
+ \overload
+
+ Returns a list of the names of all the files and directories in
+ the directory, ordered according to the name and attribute filters
+ previously set with setNameFilters() and setFilter(), and sorted according
+ to the flags set with setSorting().
+
+ The attribute filter and sorting specifications can be overridden using the
+ \a filters and \a sort arguments.
+
+ Returns an empty list if the directory is unreadable, does not
+ exist, or if nothing matches the specification.
+
+ \note To list symlinks that point to non existing files, \l System must be
+ passed to the filter.
+
+ \sa entryInfoList(), setNameFilters(), setSorting(), setFilter()
+*/
+
+QStringList QDir::entryList(Filters filters, SortFlags sort) const
+{
+ Q_D(const QDir);
+
+ return entryList(d->data->nameFilters, filters, sort);
+}
+
+
+/*!
+ \overload
+
+ Returns a list of QFileInfo objects for all the files and directories in
+ the directory, ordered according to the name and attribute filters
+ previously set with setNameFilters() and setFilter(), and sorted according
+ to the flags set with setSorting().
+
+ The attribute filter and sorting specifications can be overridden using the
+ \a filters and \a sort arguments.
+
+ Returns an empty list if the directory is unreadable, does not
+ exist, or if nothing matches the specification.
+
+ \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists()
+*/
+
+QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const
+{
+ Q_D(const QDir);
+
+ return entryInfoList(d->data->nameFilters, filters, sort);
+}
+
+/*!
+ Returns a list of the names of all the files and
+ directories in the directory, ordered according to the name
+ and attribute filters previously set with setNameFilters()
+ and setFilter(), and sorted according to the flags set with
+ setSorting().
+
+ The name filter, file attribute filter, and sorting specification
+ can be overridden using the \a nameFilters, \a filters, and \a sort
+ arguments.
+
+ Returns an empty list if the directory is unreadable, does not
+ exist, or if nothing matches the specification.
+
+ \sa entryInfoList(), setNameFilters(), setSorting(), setFilter()
+*/
+
+QStringList QDir::entryList(const QStringList &nameFilters, Filters filters,
+ SortFlags sort) const
+{
+ Q_D(const QDir);
+
+ if (filters == NoFilter)
+ filters = d->data->filters;
+#ifdef QT3_SUPPORT
+ if (d->matchAllDirs)
+ filters |= AllDirs;
+#endif
+ if (sort == NoSort)
+ sort = d->data->sort;
+ if (filters == NoFilter && sort == NoSort && nameFilters == d->data->nameFilters) {
+ d->updateFileLists();
+ return d->data->files;
+ }
+ QStringList l = d->data->fileEngine->entryList(filters, nameFilters);
+ if ((sort & QDir::SortByMask) == QDir::Unsorted)
+ return l;
+
+ QStringList ret;
+ d->sortFileList(sort, l, &ret, 0);
+ return ret;
+}
+
+/*!
+ Returns a list of QFileInfo objects for all the files and
+ directories in the directory, ordered according to the name
+ and attribute filters previously set with setNameFilters()
+ and setFilter(), and sorted according to the flags set with
+ setSorting().
+
+ The name filter, file attribute filter, and sorting specification
+ can be overridden using the \a nameFilters, \a filters, and \a sort
+ arguments.
+
+ Returns an empty list if the directory is unreadable, does not
+ exist, or if nothing matches the specification.
+
+ \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists()
+*/
+
+QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filters,
+ SortFlags sort) const
+{
+ Q_D(const QDir);
+
+ if (filters == NoFilter)
+ filters = d->data->filters;
+#ifdef QT3_SUPPORT
+ if (d->matchAllDirs)
+ filters |= AllDirs;
+#endif
+ if (sort == NoSort)
+ sort = d->data->sort;
+ if (filters == NoFilter && sort == NoSort && nameFilters == d->data->nameFilters) {
+ d->updateFileLists();
+ return d->data->fileInfos;
+ }
+ QFileInfoList ret;
+ QStringList l = d->data->fileEngine->entryList(filters, nameFilters);
+ d->sortFileList(sort, l, 0, &ret);
+ return ret;
+}
+
+/*!
+ Creates a sub-directory called \a dirName.
+
+ Returns true on success; otherwise returns false.
+
+ \sa rmdir()
+*/
+
+bool QDir::mkdir(const QString &dirName) const
+{
+ Q_D(const QDir);
+
+ if (dirName.isEmpty()) {
+ qWarning("QDir::mkdir: Empty or null file name(s)");
+ return false;
+ }
+ if(!d->data->fileEngine)
+ return false;
+
+ QString fn = filePath(dirName);
+ return d->data->fileEngine->mkdir(fn, false);
+}
+
+/*!
+ Removes the directory specified by \a dirName.
+
+ The directory must be empty for rmdir() to succeed.
+
+ Returns true if successful; otherwise returns false.
+
+ \sa mkdir()
+*/
+
+bool QDir::rmdir(const QString &dirName) const
+{
+ Q_D(const QDir);
+
+ if (dirName.isEmpty()) {
+ qWarning("QDir::rmdir: Empty or null file name(s)");
+ return false;
+ }
+ if(!d->data->fileEngine)
+ return false;
+
+ QString fn = filePath(dirName);
+ return d->data->fileEngine->rmdir(fn, false);
+}
+
+/*!
+ Creates the directory path \a dirPath.
+
+ The function will create all parent directories necessary to
+ create the directory.
+
+ Returns true if successful; otherwise returns false.
+
+ \sa rmpath()
+*/
+
+bool QDir::mkpath(const QString &dirPath) const
+{
+ Q_D(const QDir);
+
+ if (dirPath.isEmpty()) {
+ qWarning("QDir::mkpath: Empty or null file name(s)");
+ return false;
+ }
+ if(!d->data->fileEngine)
+ return false;
+
+ QString fn = filePath(dirPath);
+ return d->data->fileEngine->mkdir(fn, true);
+}
+
+/*!
+ Removes the directory path \a dirPath.
+
+ The function will remove all parent directories in \a dirPath,
+ provided that they are empty. This is the opposite of
+ mkpath(dirPath).
+
+ Returns true if successful; otherwise returns false.
+
+ \sa mkpath()
+*/
+bool QDir::rmpath(const QString &dirPath) const
+{
+ Q_D(const QDir);
+
+ if (dirPath.isEmpty()) {
+ qWarning("QDir::rmpath: Empty or null file name(s)");
+ return false;
+ }
+ if(!d->data->fileEngine)
+ return false;
+
+ QString fn = filePath(dirPath);
+ return d->data->fileEngine->rmdir(fn, true);
+}
+
+/*!
+ Returns true if the directory is readable \e and we can open files
+ by name; otherwise returns false.
+
+ \warning A false value from this function is not a guarantee that
+ files in the directory are not accessible.
+
+ \sa QFileInfo::isReadable()
+*/
+
+
+bool QDir::isReadable() const
+{
+ Q_D(const QDir);
+
+ if(!d->data->fileEngine)
+ return false;
+ const QAbstractFileEngine::FileFlags info = d->data->fileEngine->fileFlags(QAbstractFileEngine::DirectoryType
+ |QAbstractFileEngine::PermsMask);
+ if(!(info & QAbstractFileEngine::DirectoryType))
+ return false;
+ return info & QAbstractFileEngine::ReadUserPerm;
+}
+
+/*!
+ \overload
+
+ Returns true if the \e directory exists; otherwise returns false.
+ (If a file with the same name is found this function will return
+ false).
+
+ \sa QFileInfo::exists(), QFile::exists()
+*/
+
+bool QDir::exists() const
+{
+ Q_D(const QDir);
+
+ if(!d->data->fileEngine)
+ return false;
+ const QAbstractFileEngine::FileFlags info =
+ d->data->fileEngine->fileFlags(
+ QAbstractFileEngine::DirectoryType
+ | QAbstractFileEngine::ExistsFlag
+ | QAbstractFileEngine::Refresh);
+ if(!(info & QAbstractFileEngine::DirectoryType))
+ return false;
+ return info & QAbstractFileEngine::ExistsFlag;
+}
+
+/*!
+ Returns true if the directory is the root directory; otherwise
+ returns false.
+
+ Note: If the directory is a symbolic link to the root directory
+ this function returns false. If you want to test for this use
+ canonicalPath(), e.g.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 9
+
+ \sa root(), rootPath()
+*/
+
+bool QDir::isRoot() const
+{
+ Q_D(const QDir);
+
+ if(!d->data->fileEngine)
+ return true;
+ return d->data->fileEngine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::RootFlag;
+}
+
+/*!
+ \fn bool QDir::isAbsolute() const
+
+ Returns true if the directory's path is absolute; otherwise
+ returns false. See isAbsolutePath().
+
+ \sa isRelative() makeAbsolute() cleanPath()
+*/
+
+/*!
+ \fn bool QDir::isAbsolutePath(const QString &)
+
+ Returns true if \a path is absolute; returns false if it is
+ relative.
+
+ \sa isAbsolute() isRelativePath() makeAbsolute() cleanPath()
+*/
+
+/*!
+ Returns true if the directory path is relative; otherwise returns
+ false. (Under Unix a path is relative if it does not start with a
+ "/").
+
+ \sa makeAbsolute() isAbsolute() isAbsolutePath() cleanPath()
+*/
+
+bool QDir::isRelative() const
+{
+ Q_D(const QDir);
+
+ if(!d->data->fileEngine)
+ return false;
+ return d->data->fileEngine->isRelativePath();
+}
+
+
+/*!
+ Converts the directory path to an absolute path. If it is already
+ absolute nothing happens. Returns true if the conversion
+ succeeded; otherwise returns false.
+
+ \sa isAbsolute() isAbsolutePath() isRelative() cleanPath()
+*/
+
+bool QDir::makeAbsolute() // ### What do the return values signify?
+{
+ Q_D(QDir);
+
+ if(!d->data->fileEngine)
+ return false;
+ QString absolutePath = d->data->fileEngine->fileName(QAbstractFileEngine::AbsoluteName);
+ if(QDir::isRelativePath(absolutePath))
+ return false;
+ d->detach();
+ d->data->path = absolutePath;
+ d->data->fileEngine->setFileName(absolutePath);
+ if(!(d->data->fileEngine->fileFlags(QAbstractFileEngine::TypesMask) & QAbstractFileEngine::DirectoryType))
+ return false;
+ return true;
+}
+
+/*!
+ Returns true if directory \a dir and this directory have the same
+ path and their sort and filter settings are the same; otherwise
+ returns false.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 10
+*/
+
+bool QDir::operator==(const QDir &dir) const
+{
+ const QDirPrivate *d = d_func();
+ const QDirPrivate *other = dir.d_func();
+
+ if(d->data == other->data)
+ return true;
+ Q_ASSERT(d->data->fileEngine && other->data->fileEngine);
+ if(d->data->fileEngine->caseSensitive() != other->data->fileEngine->caseSensitive())
+ return false;
+ if(d->data->filters == other->data->filters
+ && d->data->sort == other->data->sort
+ && d->data->nameFilters == other->data->nameFilters) {
+ QString dir1 = absolutePath(), dir2 = dir.absolutePath();
+ if(!other->data->fileEngine->caseSensitive())
+ return (dir1.toLower() == dir2.toLower());
+
+ return (dir1 == dir2);
+
+ }
+ return false;
+}
+
+/*!
+ Makes a copy of the \a dir object and assigns it to this QDir
+ object.
+*/
+
+QDir &QDir::operator=(const QDir &dir)
+{
+ if (this == &dir)
+ return *this;
+
+ Q_D(QDir);
+ qAtomicAssign(d->data, dir.d_func()->data);
+ return *this;
+}
+
+/*!
+ \overload
+ \obsolete
+
+ Sets the directory path to the given \a path.
+
+ Use setPath() instead.
+*/
+
+QDir &QDir::operator=(const QString &path)
+{
+ Q_D(QDir);
+
+ d->setPath(path);
+ return *this;
+}
+
+/*!
+ \fn bool QDir::operator!=(const QDir &dir) const
+
+ Returns true if directory \a dir and this directory have different
+ paths or different sort or filter settings; otherwise returns
+ false.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 11
+*/
+
+
+/*!
+ Removes the file, \a fileName.
+
+ Returns true if the file is removed successfully; otherwise
+ returns false.
+*/
+
+bool QDir::remove(const QString &fileName)
+{
+ if (fileName.isEmpty()) {
+ qWarning("QDir::remove: Empty or null file name");
+ return false;
+ }
+ QString p = filePath(fileName);
+ return QFile::remove(p);
+}
+
+/*!
+ Renames a file or directory from \a oldName to \a newName, and returns
+ true if successful; otherwise returns false.
+
+ On most file systems, rename() fails only if \a oldName does not
+ exist, if \a newName and \a oldName are not on the same
+ partition or if a file with the new name already exists.
+ However, there are also other reasons why rename() can
+ fail. For example, on at least one file system rename() fails if
+ \a newName points to an open file.
+*/
+
+bool QDir::rename(const QString &oldName, const QString &newName)
+{
+ Q_D(QDir);
+
+ if (oldName.isEmpty() || newName.isEmpty()) {
+ qWarning("QDir::rename: Empty or null file name(s)");
+ return false;
+ }
+ if(!d->data->fileEngine)
+ return false;
+
+ QFile file(filePath(oldName));
+ if(!file.exists())
+ return false;
+ return file.rename(filePath(newName));
+}
+
+/*!
+ Returns true if the file called \a name exists; otherwise returns
+ false. Unless \a name contains an absolute file path, the file
+ name is assumed to be relative to the current directory.
+
+ \sa QFileInfo::exists(), QFile::exists()
+*/
+
+bool QDir::exists(const QString &name) const
+{
+ if (name.isEmpty()) {
+ qWarning("QDir::exists: Empty or null file name");
+ return false;
+ }
+ QString tmp = filePath(name);
+ return QFile::exists(tmp);
+}
+
+/*!
+ Returns a list of the root directories on this system.
+
+ On Windows this returns a list of QFileInfo objects containing "C:/",
+ "D:/", etc. On other operating systems, it returns a list containing
+ just one root directory (i.e. "/").
+
+ \sa root(), rootPath()
+*/
+
+QFileInfoList QDir::drives()
+{
+#ifdef QT_NO_FSFILEENGINE
+ return QFileInfoList();
+#else
+ return QFSFileEngine::drives();
+#endif
+}
+
+/*!
+ Returns the native directory separator: "/" under Unix (including
+ Mac OS X) and "\\" under Windows.
+
+ You do not need to use this function to build file paths. If you
+ always use "/", Qt will translate your paths to conform to the
+ underlying operating system. If you want to display paths to the
+ user using their operating system's separator use
+ toNativeSeparators().
+*/
+
+QChar QDir::separator()
+{
+#if defined(Q_OS_UNIX)
+ return QLatin1Char('/');
+#elif defined (Q_FS_FAT) || defined(Q_WS_WIN)
+ return QLatin1Char('\\');
+#elif defined (Q_OS_MAC)
+ return QLatin1Char(':');
+#else
+ return QLatin1Char('/');
+#endif
+}
+
+/*!
+ Sets the application's current working directory to \a path.
+ Returns true if the directory was successfully changed; otherwise
+ returns false.
+
+ \sa current() currentPath() home() root() temp()
+*/
+
+bool QDir::setCurrent(const QString &path)
+{
+#ifdef QT_NO_FSFILEENGINE
+ Q_UNUSED(path);
+ return false;
+#else
+ return QFSFileEngine::setCurrentPath(path);
+#endif
+}
+
+/*!
+ \fn QDir QDir::current()
+
+ Returns the application's current directory.
+
+ The directory is constructed using the absolute path of the current directory,
+ ensuring that its path() will be the same as its absolutePath().
+
+ \sa currentPath(), home(), root(), temp()
+*/
+
+/*!
+ Returns the absolute path of the application's current directory.
+
+ \sa current(), homePath(), rootPath(), tempPath()
+*/
+QString QDir::currentPath()
+{
+#ifdef QT_NO_FSFILEENGINE
+ return QString();
+#else
+ return QFSFileEngine::currentPath();
+#endif
+}
+
+/*!
+ \fn QString QDir::currentDirPath()
+ Returns the absolute path of the application's current directory.
+
+ Use currentPath() instead.
+
+ \sa currentPath()
+*/
+
+/*!
+ \fn QDir QDir::home()
+
+ Returns the user's home directory.
+
+ The directory is constructed using the absolute path of the home directory,
+ ensuring that its path() will be the same as its absolutePath().
+
+ See homePath() for details.
+
+ \sa drives(), current(), root(), temp()
+*/
+
+/*!
+ Returns the absolute path of the user's home directory.
+
+ Under Windows this function will return the directory of the
+ current user's profile. Typically, this is:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 12
+
+ Use the toNativeSeparators() function to convert the separators to
+ the ones that are appropriate for the underlying operating system.
+
+ If the directory of the current user's profile does not exist or
+ cannot be retrieved, the following alternatives will be checked (in
+ the given order) until an existing and available path is found:
+
+ \list 1
+ \o The path specified by the \c USERPROFILE environment variable.
+ \o The path formed by concatenating the \c HOMEDRIVE and \c HOMEPATH
+ environment variables.
+ \o The path specified by the \c HOME environment variable.
+ \o The path returned by the rootPath() function (which uses the \c SystemDrive
+ environment variable)
+ \o The \c{C:/} directory.
+ \endlist
+
+ 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.
+
+ \sa home(), currentPath(), rootPath(), tempPath()
+*/
+QString QDir::homePath()
+{
+#ifdef QT_NO_FSFILEENGINE
+ return QString();
+#else
+ return cleanPath(QFSFileEngine::homePath());
+#endif
+}
+
+/*!
+ \fn QString QDir::homeDirPath()
+
+ Returns the absolute path of the user's home directory.
+
+ Use homePath() instead.
+
+ \sa homePath()
+ */
+
+/*!
+ \fn QDir QDir::temp()
+
+ Returns the system's temporary directory.
+
+ The directory is constructed using the absolute path of the temporary directory,
+ ensuring that its path() will be the same as its absolutePath().
+
+ See tempPath() for details.
+
+ \sa drives(), current(), home(), root()
+*/
+
+/*!
+ Returns the absolute path of the system's temporary directory.
+
+ On Unix/Linux systems this is usually \c{/tmp}; on Windows this is
+ usually the path in the \c TEMP or \c TMP environment
+ variable. Whether a directory separator is added to the end or
+ not, depends on the operating system.
+
+ \sa temp(), currentPath(), homePath(), rootPath()
+*/
+QString QDir::tempPath()
+{
+#ifdef QT_NO_FSFILEENGINE
+ return QString();
+#else
+ return cleanPath(QFSFileEngine::tempPath());
+#endif
+}
+
+/*!
+ \fn QDir QDir::root()
+
+ Returns the root directory.
+
+ The directory is constructed using the absolute path of the root directory,
+ ensuring that its path() will be the same as its absolutePath().
+
+ See rootPath() for details.
+
+ \sa drives(), current(), home(), temp()
+*/
+
+/*!
+ Returns the absolute path of the root directory.
+
+ For Unix operating systems this returns "/". For Windows file
+ systems this normally returns "c:/".
+
+ \sa root(), drives(), currentPath(), homePath(), tempPath()
+*/
+QString QDir::rootPath()
+{
+#ifdef QT_NO_FSFILEENGINE
+ return QString();
+#else
+ return QFSFileEngine::rootPath();
+#endif
+}
+
+/*!
+ \fn QString QDir::rootDirPath()
+
+ Returns the absolute path of the root directory.
+
+ Use rootPath() instead.
+
+ \sa rootPath()
+*/
+
+#ifndef QT_NO_REGEXP
+/*!
+ \overload
+
+ Returns true if the \a fileName matches any of the wildcard (glob)
+ patterns in the list of \a filters; otherwise returns false. The
+ matching is case insensitive.
+
+ \sa {QRegExp wildcard matching}, QRegExp::exactMatch() entryList() entryInfoList()
+*/
+
+
+bool QDir::match(const QStringList &filters, const QString &fileName)
+{
+ for(QStringList::ConstIterator sit = filters.begin(); sit != filters.end(); ++sit) {
+ QRegExp rx(*sit, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (rx.exactMatch(fileName))
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns true if the \a fileName matches the wildcard (glob)
+ pattern \a filter; otherwise returns false. The \a filter may
+ contain multiple patterns separated by spaces or semicolons.
+ The matching is case insensitive.
+
+ \sa {QRegExp wildcard matching}, QRegExp::exactMatch() entryList() entryInfoList()
+*/
+
+bool QDir::match(const QString &filter, const QString &fileName)
+{
+ return match(nameFiltersFromString(filter), fileName);
+}
+#endif
+
+/*!
+ Removes all multiple directory separators "/" and resolves any
+ "."s or ".."s found in the path, \a path.
+
+ Symbolic links are kept. This function does not return the
+ canonical path, but rather the simplest version of the input.
+ For example, "./local" becomes "local", "local/../bin" becomes
+ "bin" and "/local/usr/../bin" becomes "/local/bin".
+
+ \sa absolutePath() canonicalPath()
+*/
+
+QString QDir::cleanPath(const QString &path)
+{
+ if (path.isEmpty())
+ return path;
+ QString name = path;
+ QChar dir_separator = separator();
+ if(dir_separator != QLatin1Char('/'))
+ name.replace(dir_separator, QLatin1Char('/'));
+
+ int used = 0, levels = 0;
+ const int len = name.length();
+ QVector<QChar> out(len);
+ const QChar *p = name.unicode();
+ for(int i = 0, last = -1, iwrite = 0; i < len; i++) {
+ if(p[i] == QLatin1Char('/')) {
+ while(i < len-1 && p[i+1] == QLatin1Char('/')) {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) //allow unc paths
+ if(!i)
+ break;
+#endif
+ i++;
+ }
+ bool eaten = false;
+ if(i < len - 1 && p[i+1] == QLatin1Char('.')) {
+ int dotcount = 1;
+ if(i < len - 2 && p[i+2] == QLatin1Char('.'))
+ dotcount++;
+ if(i == len - dotcount - 1) {
+ if(dotcount == 1) {
+ break;
+ } else if(levels) {
+ if(last == -1) {
+ for(int i2 = iwrite-1; i2 >= 0; i2--) {
+ if(out[i2] == QLatin1Char('/')) {
+ last = i2;
+ break;
+ }
+ }
+ }
+ used -= iwrite - last - 1;
+ break;
+ }
+ } else if(p[i+dotcount+1] == QLatin1Char('/')) {
+ if(dotcount == 2 && levels) {
+ if(last == -1 || iwrite - last == 1) {
+ for(int i2 = (last == -1) ? (iwrite-1) : (last-1); i2 >= 0; i2--) {
+ if(out[i2] == QLatin1Char('/')) {
+ eaten = true;
+ last = i2;
+ break;
+ }
+ }
+ } else {
+ eaten = true;
+ }
+ if(eaten) {
+ levels--;
+ used -= iwrite - last;
+ iwrite = last;
+ last = -1;
+ }
+ } else if (dotcount == 2 && i > 0 && p[i - 1] != QLatin1Char('.')) {
+ eaten = true;
+ used -= iwrite - qMax(0, last);
+ iwrite = qMax(0, last);
+ last = -1;
+ ++i;
+ } else if(dotcount == 1) {
+ eaten = true;
+ }
+ if(eaten)
+ i += dotcount;
+ } else {
+ levels++;
+ }
+ } else if(last != -1 && iwrite - last == 1) {
+#ifdef Q_OS_WIN
+ eaten = (iwrite > 2);
+#else
+ eaten = true;
+#endif
+ last = -1;
+ } else if(last != -1 && i == len-1) {
+ eaten = true;
+ } else {
+ levels++;
+ }
+ if(!eaten)
+ last = i - (i - iwrite);
+ else
+ continue;
+ } else if(!i && p[i] == QLatin1Char('.')) {
+ int dotcount = 1;
+ if(len >= 1 && p[1] == QLatin1Char('.'))
+ dotcount++;
+ if(len >= dotcount && p[dotcount] == QLatin1Char('/')) {
+ if(dotcount == 1) {
+ i++;
+ while(i+1 < len-1 && p[i+1] == QLatin1Char('/'))
+ i++;
+ continue;
+ }
+ }
+ }
+ out[iwrite++] = p[i];
+ used++;
+ }
+ QString ret;
+ if(used == len)
+ ret = name;
+ else
+ ret = QString(out.data(), used);
+
+ // Strip away last slash except for root directories
+ if (ret.endsWith(QLatin1Char('/'))
+ && !(ret.size() == 1 || (ret.size() == 3 && ret.at(1) == QLatin1Char(':'))))
+ ret = ret.left(ret.length() - 1);
+
+ return ret;
+}
+
+/*!
+ Returns true if \a path is relative; returns false if it is
+ absolute.
+
+ \sa isRelative() isAbsolutePath() makeAbsolute()
+*/
+
+bool QDir::isRelativePath(const QString &path)
+{
+ return QFileInfo(path).isRelative();
+}
+
+/*!
+ Refreshes the directory information.
+*/
+
+void QDir::refresh() const
+{
+ Q_D(const QDir);
+
+ d->data->clear();
+}
+
+/*!
+ \internal
+
+ Returns a list of name filters from the given \a nameFilter. (If
+ there is more than one filter, each pair of filters is separated
+ by a space or by a semicolon.)
+*/
+
+QStringList QDir::nameFiltersFromString(const QString &nameFilter)
+{
+ return QDirPrivate::splitFilters(nameFilter);
+}
+
+/*!
+ \macro void Q_INIT_RESOURCE(name)
+ \relates QDir
+
+ Initializes the resources specified by the \c .qrc file with the
+ specified base \a name. Normally, Qt resources are loaded
+ automatically at startup. The Q_INIT_RESOURCE() macro is
+ necessary on some platforms for resources stored in a static
+ library.
+
+ For example, if your application's resources are listed in a file
+ called \c myapp.qrc, you can ensure that the resources are
+ initialized at startup by adding this line to your \c main()
+ function:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 13
+
+ If the file name contains characters that cannot be part of a valid C++ function name
+ (such as '-'), they have to be replaced by the underscore character ('_').
+
+ Note: This macro cannot be used in a namespace. It should be called from
+ main(). If that is not possible, the following workaround can be used
+ to init the resource \c myapp from the function \c{MyNamespace::myFunction}:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 14
+
+ \sa Q_CLEANUP_RESOURCE(), {The Qt Resource System}
+*/
+
+/*!
+ \since 4.1
+ \macro void Q_CLEANUP_RESOURCE(name)
+ \relates QDir
+
+ Unloads the resources specified by the \c .qrc file with the base
+ name \a name.
+
+ Normally, Qt resources are unloaded automatically when the
+ application terminates, but if the resources are located in a
+ plugin that is being unloaded, call Q_CLEANUP_RESOURCE() to force
+ removal of your resources.
+
+ Note: This macro cannot be used in a namespace. Please see the
+ Q_INIT_RESOURCE documentation for a workaround.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 15
+
+ \sa Q_INIT_RESOURCE(), {The Qt Resource System}
+*/
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \fn bool QDir::matchAllDirs() const
+
+ Use filter() & AllDirs instead.
+*/
+bool QDir::matchAllDirs() const
+{
+ Q_D(const QDir);
+ return d->matchAllDirs;
+}
+
+
+/*!
+ \fn void QDir::setMatchAllDirs(bool on)
+
+ Use setFilter() instead.
+*/
+void QDir::setMatchAllDirs(bool on)
+{
+ Q_D(QDir);
+ d->matchAllDirs = on;
+}
+
+/*!
+ Use nameFilters() instead.
+*/
+QString QDir::nameFilter() const
+{
+ Q_D(const QDir);
+
+ return nameFilters().join(QString(d->filterSepChar));
+}
+
+/*!
+ Use setNameFilters() instead.
+
+ The \a nameFilter is a wildcard (globbing) filter that understands
+ "*" and "?" wildcards. (See \l{QRegExp wildcard matching}.) You may
+ specify several filter entries, each separated by spaces or by
+ semicolons.
+
+ For example, if you want entryList() and entryInfoList() to list
+ all files ending with either ".cpp" or ".h", you would use either
+ dir.setNameFilters("*.cpp *.h") or dir.setNameFilters("*.cpp;*.h").
+
+ \oldcode
+ QString filter = "*.cpp *.cxx *.cc";
+ dir.setNameFilter(filter);
+ \newcode
+ QString filter = "*.cpp *.cxx *.cc";
+ dir.setNameFilters(filter.split(' '));
+ \endcode
+*/
+void QDir::setNameFilter(const QString &nameFilter)
+{
+ Q_D(QDir);
+
+ d->filterSepChar = QDirPrivate::getFilterSepChar(nameFilter);
+ setNameFilters(QDirPrivate::splitFilters(nameFilter, d->filterSepChar));
+}
+
+/*!
+ \fn QString QDir::absPath() const
+
+ Use absolutePath() instead.
+*/
+
+/*!
+ \fn QString QDir::absFilePath(const QString &fileName, bool acceptAbsPath) const
+
+ Use absoluteFilePath(\a fileName) instead.
+
+ The \a acceptAbsPath parameter is ignored.
+*/
+
+/*!
+ \fn bool QDir::mkdir(const QString &dirName, bool acceptAbsPath) const
+
+ Use mkdir(\a dirName) instead.
+
+ The \a acceptAbsPath parameter is ignored.
+*/
+
+/*!
+ \fn bool QDir::rmdir(const QString &dirName, bool acceptAbsPath) const
+
+ Use rmdir(\a dirName) instead.
+
+ The \a acceptAbsPath parameter is ignored.
+*/
+
+/*!
+ \fn QStringList QDir::entryList(const QString &nameFilter, Filters filters,
+ SortFlags sort) const
+ \overload
+
+ Use the overload that takes a name filter string list as first
+ argument instead of a combination of attribute filter flags.
+*/
+
+/*!
+ \fn QFileInfoList QDir::entryInfoList(const QString &nameFilter, Filters filters,
+ SortFlags sort) const
+ \overload
+
+ Use the overload that takes a name filter string list as first
+ argument instead of a combination of attribute filter flags.
+*/
+
+/*!
+ \fn void QDir::convertToAbs()
+
+ Use makeAbsolute() instead.
+*/
+
+/*!
+ \fn QString QDir::cleanDirPath(const QString &name)
+
+ Use cleanPath() instead.
+*/
+
+/*!
+ \typedef QDir::FilterSpec
+
+ Use QDir::Filters instead.
+*/
+
+/*!
+ \typedef QDir::SortSpec
+
+ Use QDir::SortFlags instead.
+*/
+#endif
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QDir::Filters filters)
+{
+ QStringList flags;
+ if (filters == QDir::NoFilter) {
+ flags << QLatin1String("NoFilter");
+ } else {
+ if (filters & QDir::Dirs) flags << QLatin1String("Dirs");
+ if (filters & QDir::AllDirs) flags << QLatin1String("AllDirs");
+ if (filters & QDir::Files) flags << QLatin1String("Files");
+ if (filters & QDir::Drives) flags << QLatin1String("Drives");
+ if (filters & QDir::NoSymLinks) flags << QLatin1String("NoSymLinks");
+ if (filters & QDir::NoDotAndDotDot) flags << QLatin1String("NoDotAndDotDot");
+ if ((filters & QDir::AllEntries) == QDir::AllEntries) flags << QLatin1String("AllEntries");
+ if (filters & QDir::Readable) flags << QLatin1String("Readable");
+ if (filters & QDir::Writable) flags << QLatin1String("Writable");
+ if (filters & QDir::Executable) flags << QLatin1String("Executable");
+ if (filters & QDir::Modified) flags << QLatin1String("Modified");
+ if (filters & QDir::Hidden) flags << QLatin1String("Hidden");
+ if (filters & QDir::System) flags << QLatin1String("System");
+ if (filters & QDir::CaseSensitive) flags << QLatin1String("CaseSensitive");
+ }
+ debug << "QDir::Filters(" << qPrintable(flags.join(QLatin1String("|"))) << ")";
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QDir::SortFlags sorting)
+{
+ if (sorting == QDir::NoSort) {
+ debug << "QDir::SortFlags(NoSort)";
+ } else {
+ QString type;
+ if ((sorting & 3) == QDir::Name) type = QLatin1String("Name");
+ if ((sorting & 3) == QDir::Time) type = QLatin1String("Time");
+ if ((sorting & 3) == QDir::Size) type = QLatin1String("Size");
+ if ((sorting & 3) == QDir::Unsorted) type = QLatin1String("Unsorted");
+
+ QStringList flags;
+ if (sorting & QDir::DirsFirst) flags << QLatin1String("DirsFirst");
+ if (sorting & QDir::DirsLast) flags << QLatin1String("DirsLast");
+ if (sorting & QDir::IgnoreCase) flags << QLatin1String("IgnoreCase");
+ if (sorting & QDir::LocaleAware) flags << QLatin1String("LocaleAware");
+ if (sorting & QDir::Type) flags << QLatin1String("Type");
+ debug << "QDir::SortFlags(" << qPrintable(type)
+ << "|"
+ << qPrintable(flags.join(QLatin1String("|"))) << ")";
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QDir &dir)
+{
+ debug.maybeSpace() << "QDir(" << dir.path()
+ << ", nameFilters = {"
+ << qPrintable(dir.nameFilters().join(QLatin1String(",")))
+ << "}, "
+ << dir.sorting()
+ << ","
+ << dir.filter()
+ << ")";
+ return debug.space();
+}
+
+
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h
new file mode 100644
index 0000000..c4f6b1a
--- /dev/null
+++ b/src/corelib/io/qdir.h
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIR_H
+#define QDIR_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QDirPrivate;
+
+class Q_CORE_EXPORT QDir
+{
+protected:
+ QDirPrivate *d_ptr;
+private:
+ Q_DECLARE_PRIVATE(QDir)
+public:
+ enum Filter { Dirs = 0x001,
+ Files = 0x002,
+ Drives = 0x004,
+ NoSymLinks = 0x008,
+ AllEntries = Dirs | Files | Drives,
+ TypeMask = 0x00f,
+#ifdef QT3_SUPPORT
+ All = AllEntries,
+#endif
+
+ Readable = 0x010,
+ Writable = 0x020,
+ Executable = 0x040,
+ PermissionMask = 0x070,
+#ifdef QT3_SUPPORT
+ RWEMask = 0x070,
+#endif
+
+ Modified = 0x080,
+ Hidden = 0x100,
+ System = 0x200,
+
+ AccessMask = 0x3F0,
+
+ AllDirs = 0x400,
+ CaseSensitive = 0x800,
+ NoDotAndDotDot = 0x1000,
+
+ NoFilter = -1
+#ifdef QT3_SUPPORT
+ ,DefaultFilter = NoFilter
+#endif
+ };
+ Q_DECLARE_FLAGS(Filters, Filter)
+#ifdef QT3_SUPPORT
+ typedef Filters FilterSpec;
+#endif
+
+ enum SortFlag { Name = 0x00,
+ Time = 0x01,
+ Size = 0x02,
+ Unsorted = 0x03,
+ SortByMask = 0x03,
+
+ DirsFirst = 0x04,
+ Reversed = 0x08,
+ IgnoreCase = 0x10,
+ DirsLast = 0x20,
+ LocaleAware = 0x40,
+ Type = 0x80,
+ NoSort = -1
+#ifdef QT3_SUPPORT
+ ,DefaultSort = NoSort
+#endif
+ };
+ Q_DECLARE_FLAGS(SortFlags, SortFlag)
+
+ QDir(const QDir &);
+ QDir(const QString &path = QString());
+ QDir(const QString &path, const QString &nameFilter,
+ SortFlags sort = SortFlags(Name | IgnoreCase), Filters filter = AllEntries);
+ ~QDir();
+
+ QDir &operator=(const QDir &);
+ QDir &operator=(const QString &path);
+
+ void setPath(const QString &path);
+ QString path() const;
+ QString absolutePath() const;
+ QString canonicalPath() const;
+
+ static void addResourceSearchPath(const QString &path);
+
+ static void setSearchPaths(const QString &prefix, const QStringList &searchPaths);
+ static void addSearchPath(const QString &prefix, const QString &path);
+ static QStringList searchPaths(const QString &prefix);
+
+ QString dirName() const;
+ QString filePath(const QString &fileName) const;
+ QString absoluteFilePath(const QString &fileName) const;
+ QString relativeFilePath(const QString &fileName) const;
+
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED static QString convertSeparators(const QString &pathName);
+#endif
+ static QString toNativeSeparators(const QString &pathName);
+ static QString fromNativeSeparators(const QString &pathName);
+
+ bool cd(const QString &dirName);
+ bool cdUp();
+
+ QStringList nameFilters() const;
+ void setNameFilters(const QStringList &nameFilters);
+
+ Filters filter() const;
+ void setFilter(Filters filter);
+ SortFlags sorting() const;
+ void setSorting(SortFlags sort);
+
+ uint count() const;
+ QString operator[](int) const;
+
+ static QStringList nameFiltersFromString(const QString &nameFilter);
+
+ QStringList entryList(Filters filters = NoFilter, SortFlags sort = NoSort) const;
+ QStringList entryList(const QStringList &nameFilters, Filters filters = NoFilter,
+ SortFlags sort = NoSort) const;
+
+ QFileInfoList entryInfoList(Filters filters = NoFilter, SortFlags sort = NoSort) const;
+ QFileInfoList entryInfoList(const QStringList &nameFilters, Filters filters = NoFilter,
+ SortFlags sort = NoSort) const;
+
+ bool mkdir(const QString &dirName) const;
+ bool rmdir(const QString &dirName) const;
+ bool mkpath(const QString &dirPath) const;
+ bool rmpath(const QString &dirPath) const;
+
+ bool isReadable() const;
+ bool exists() const;
+ bool isRoot() const;
+
+ static bool isRelativePath(const QString &path);
+ inline static bool isAbsolutePath(const QString &path) { return !isRelativePath(path); }
+ bool isRelative() const;
+ inline bool isAbsolute() const { return !isRelative(); }
+ bool makeAbsolute();
+
+ bool operator==(const QDir &dir) const;
+ inline bool operator!=(const QDir &dir) const { return !operator==(dir); }
+
+ bool remove(const QString &fileName);
+ bool rename(const QString &oldName, const QString &newName);
+ bool exists(const QString &name) const;
+
+ static QFileInfoList drives();
+
+ static QChar separator();
+
+ static bool setCurrent(const QString &path);
+ static inline QDir current() { return QDir(currentPath()); }
+ static QString currentPath();
+
+ static inline QDir home() { return QDir(homePath()); }
+ static QString homePath();
+ static inline QDir root() { return QDir(rootPath()); }
+ static QString rootPath();
+ static inline QDir temp() { return QDir(tempPath()); }
+ static QString tempPath();
+
+#ifndef QT_NO_REGEXP
+ static bool match(const QStringList &filters, const QString &fileName);
+ static bool match(const QString &filter, const QString &fileName);
+#endif
+ static QString cleanPath(const QString &path);
+ void refresh() const;
+
+#ifdef QT3_SUPPORT
+ typedef SortFlags SortSpec;
+ inline QT3_SUPPORT QString absPath() const { return absolutePath(); }
+ inline QT3_SUPPORT QString absFilePath(const QString &fileName, bool acceptAbsPath = true) const
+ { Q_UNUSED(acceptAbsPath); return absoluteFilePath(fileName); }
+ QT3_SUPPORT bool matchAllDirs() const;
+ QT3_SUPPORT void setMatchAllDirs(bool on);
+ inline QT3_SUPPORT QStringList entryList(const QString &nameFilter, Filters filters = NoFilter,
+ SortFlags sort = NoSort) const
+ { return entryList(nameFiltersFromString(nameFilter), filters, sort); }
+ inline QT3_SUPPORT QFileInfoList entryInfoList(const QString &nameFilter,
+ Filters filters = NoFilter,
+ SortFlags sort = NoSort) const
+ { return entryInfoList(nameFiltersFromString(nameFilter), filters, sort); }
+
+ QT3_SUPPORT QString nameFilter() const;
+ QT3_SUPPORT void setNameFilter(const QString &nameFilter);
+
+ inline QT3_SUPPORT bool mkdir(const QString &dirName, bool acceptAbsPath) const
+ { Q_UNUSED(acceptAbsPath); return mkdir(dirName); }
+ inline QT3_SUPPORT bool rmdir(const QString &dirName, bool acceptAbsPath) const
+ { Q_UNUSED(acceptAbsPath); return rmdir(dirName); }
+
+ inline QT3_SUPPORT void convertToAbs() { makeAbsolute(); }
+ inline QT3_SUPPORT static QString currentDirPath() { return currentPath(); }
+ inline QT3_SUPPORT static QString homeDirPath() { return homePath(); }
+ inline QT3_SUPPORT static QString rootDirPath() { return rootPath(); }
+ inline QT3_SUPPORT static QString cleanDirPath(const QString &name) { return cleanPath(name); }
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDir::Filters)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDir::SortFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, QDir::Filters filters);
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QDir &dir);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDIR_H
diff --git a/src/corelib/io/qdiriterator.cpp b/src/corelib/io/qdiriterator.cpp
new file mode 100644
index 0000000..46c7dd8
--- /dev/null
+++ b/src/corelib/io/qdiriterator.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \since 4.3
+ \class QDirIterator
+ \brief The QDirIterator class provides an iterator for directory entrylists.
+
+ You can use QDirIterator to navigate entries of a directory one at a time.
+ It is similar to QDir::entryList() and QDir::entryInfoList(), but because
+ it lists entries one at a time instead of all at once, it scales better
+ and is more suitable for large directories. It also supports listing
+ directory contents recursively, and following symbolic links. Unlike
+ QDir::entryList(), QDirIterator does not support sorting.
+
+ The QDirIterator constructor takes a QDir or a directory as
+ argument. After construction, the iterator is located before the first
+ directory entry. Here's how to iterate over all the entries sequentially:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qdiriterator.cpp 0
+
+ The next() function returns the path to the next directory entry and
+ advances the iterator. You can also call filePath() to get the current
+ file path without advancing the iterator. The fileName() function returns
+ only the name of the file, similar to how QDir::entryList() works. You can
+ also call fileInfo() to get a QFileInfo for the current entry.
+
+ Unlike Qt's container iterators, QDirIterator is uni-directional (i.e.,
+ you cannot iterate directories in reverse order) and does not allow random
+ access.
+
+ QDirIterator works with all supported file engines, and is implemented
+ using QAbstractFileEngineIterator.
+
+ \sa QDir, QDir::entryList(), QAbstractFileEngineIterator
+*/
+
+/*! \enum QDirIterator::IteratorFlag
+
+ This enum describes flags that you can combine to configure the behavior
+ of QDirIterator.
+
+ \value NoIteratorFlags The default value, representing no flags. The
+ iterator will return entries for the assigned path.
+
+ \value Subdirectories List entries inside all subdirectories as well.
+
+ \value FollowSymlinks When combined with Subdirectories, this flag
+ enables iterating through all subdirectories of the assigned path,
+ following all symbolic links. Symbolic link loops (e.g., "link" => "." or
+ "link" => "..") are automatically detected and ignored.
+*/
+
+#include "qdiriterator.h"
+
+#include "qabstractfileengine.h"
+
+#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDirIteratorPrivate
+{
+public:
+ QDirIteratorPrivate(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters, QDirIterator::IteratorFlags flags);
+ ~QDirIteratorPrivate();
+
+ void pushSubDirectory(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters);
+ void advance();
+ bool shouldFollowDirectory(const QFileInfo &);
+ bool matchesFilters(const QAbstractFileEngineIterator *it) const;
+
+ QSet<QString> visitedLinks;
+ QAbstractFileEngine *engine;
+ QStack<QAbstractFileEngineIterator *> fileEngineIterators;
+ QString path;
+ QFileInfo fileInfo;
+ QString currentFilePath;
+ QDirIterator::IteratorFlags iteratorFlags;
+ QDir::Filters filters;
+ QStringList nameFilters;
+ bool followNextDir;
+ bool first;
+ bool done;
+
+ QDirIterator *q;
+};
+
+/*!
+ \internal
+*/
+QDirIteratorPrivate::QDirIteratorPrivate(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters, QDirIterator::IteratorFlags flags)
+ : engine(0), path(path), iteratorFlags(flags), followNextDir(false), first(true), done(false)
+{
+ if (filters == QDir::NoFilter)
+ filters = QDir::AllEntries;
+ this->filters = filters;
+ this->nameFilters = nameFilters;
+
+ fileInfo.setFile(path);
+ pushSubDirectory(fileInfo.isSymLink() ? fileInfo.canonicalFilePath() : path,
+ nameFilters, filters);
+}
+
+/*!
+ \internal
+*/
+QDirIteratorPrivate::~QDirIteratorPrivate()
+{
+ delete engine;
+}
+
+/*!
+ \internal
+*/
+void QDirIteratorPrivate::pushSubDirectory(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters)
+{
+ if (iteratorFlags & QDirIterator::FollowSymlinks) {
+ if (fileInfo.filePath() != path)
+ fileInfo.setFile(path);
+ if (fileInfo.isSymLink()) {
+ visitedLinks << fileInfo.canonicalFilePath();
+ } else {
+ visitedLinks << fileInfo.absoluteFilePath();
+ }
+ }
+
+ if (engine || (engine = QAbstractFileEngine::create(this->path))) {
+ engine->setFileName(path);
+ QAbstractFileEngineIterator *it = engine->beginEntryList(filters, nameFilters);
+ if (it) {
+ it->setPath(path);
+ fileEngineIterators << it;
+ } else {
+ // No iterator; no entry list.
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QDirIteratorPrivate::advance()
+{
+ // Store the current entry
+ if (!fileEngineIterators.isEmpty())
+ currentFilePath = fileEngineIterators.top()->currentFilePath();
+
+ // Advance to the next entry
+ if (followNextDir) {
+ // Start by navigating into the current directory.
+ followNextDir = false;
+
+ QAbstractFileEngineIterator *it = fileEngineIterators.top();
+
+ QString subDir = it->currentFilePath();
+#ifdef Q_OS_WIN
+ if (fileInfo.isSymLink())
+ subDir = fileInfo.canonicalFilePath();
+#endif
+ pushSubDirectory(subDir, it->nameFilters(), it->filters());
+ }
+
+ while (!fileEngineIterators.isEmpty()) {
+ QAbstractFileEngineIterator *it = fileEngineIterators.top();
+
+ // Find the next valid iterator that matches the filters.
+ bool foundDirectory = false;
+ while (it->hasNext()) {
+ it->next();
+ if (matchesFilters(it)) {
+ fileInfo = it->currentFileInfo();
+ // Signal that we want to follow this entry.
+ followNextDir = shouldFollowDirectory(fileInfo);
+
+ //We found a matching entry.
+ return;
+
+ } else if (iteratorFlags & QDirIterator::Subdirectories) {
+ QFileInfo fileInfo = it->currentFileInfo();
+ if (!shouldFollowDirectory(fileInfo))
+ continue;
+ QString subDir = it->currentFilePath();
+#ifdef Q_OS_WIN
+ if (fileInfo.isSymLink())
+ subDir = fileInfo.canonicalFilePath();
+#endif
+ pushSubDirectory(subDir, it->nameFilters(), it->filters());
+
+ foundDirectory = true;
+ break;
+ }
+ }
+ if (!foundDirectory)
+ delete fileEngineIterators.pop();
+ }
+ done = true;
+}
+
+/*!
+ \internal
+ */
+bool QDirIteratorPrivate::shouldFollowDirectory(const QFileInfo &fileInfo)
+{
+ // If we're doing flat iteration, we're done.
+ if (!(iteratorFlags & QDirIterator::Subdirectories))
+ return false;
+
+ // Never follow non-directory entries
+ if (!fileInfo.isDir())
+ return false;
+
+
+ // Never follow . and ..
+ if (fileInfo.fileName() == QLatin1String(".") || fileInfo.fileName() == QLatin1String(".."))
+ return false;
+
+
+ // Check symlinks
+ if (fileInfo.isSymLink() && !(iteratorFlags & QDirIterator::FollowSymlinks)) {
+ // Follow symlinks only if FollowSymlinks was passed
+ return false;
+ }
+
+ // Stop link loops
+ if (visitedLinks.contains(fileInfo.canonicalFilePath()))
+ return false;
+
+ return true;
+}
+
+
+/*!
+ \internal
+
+ This convenience function implements the iterator's filtering logics and
+ applies then to the current directory entry.
+
+ It returns true if the current entry matches the filters (i.e., the
+ current entry will be returned as part of the directory iteration);
+ otherwise, false is returned.
+*/
+bool QDirIteratorPrivate::matchesFilters(const QAbstractFileEngineIterator *it) const
+{
+ const bool filterPermissions = ((filters & QDir::PermissionMask)
+ && (filters & QDir::PermissionMask) != QDir::PermissionMask);
+ const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
+ const bool skipFiles = !(filters & QDir::Files);
+ const bool skipSymlinks = (filters & QDir::NoSymLinks);
+ const bool doReadable = !filterPermissions || (filters & QDir::Readable);
+ const bool doWritable = !filterPermissions || (filters & QDir::Writable);
+ const bool doExecutable = !filterPermissions || (filters & QDir::Executable);
+ const bool includeHidden = (filters & QDir::Hidden);
+ const bool includeSystem = (filters & QDir::System);
+
+#ifndef QT_NO_REGEXP
+ // Prepare name filters
+ QList<QRegExp> regexps;
+ bool hasNameFilters = !nameFilters.isEmpty() && !(nameFilters.contains(QLatin1String("*")));
+ if (hasNameFilters) {
+ for (int i = 0; i < nameFilters.size(); ++i) {
+ regexps << QRegExp(nameFilters.at(i),
+ (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive,
+ QRegExp::Wildcard);
+ }
+ }
+#endif
+
+ QString fileName = it->currentFileName();
+ if (fileName.isEmpty()) {
+ // invalid entry
+ return false;
+ }
+
+ QFileInfo fi = it->currentFileInfo();
+ QString filePath = it->currentFilePath();
+
+#ifndef QT_NO_REGEXP
+ // Pass all entries through name filters, except dirs if the AllDirs
+ // filter is passed.
+ if (hasNameFilters && !((filters & QDir::AllDirs) && fi.isDir())) {
+ bool matched = false;
+ for (int i = 0; i < regexps.size(); ++i) {
+ if (regexps.at(i).exactMatch(fileName)) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched)
+ return false;
+ }
+#endif
+
+ bool dotOrDotDot = (fileName == QLatin1String(".") || fileName == QLatin1String(".."));
+ if ((filters & QDir::NoDotAndDotDot) && dotOrDotDot)
+ return false;
+
+ bool isHidden = !dotOrDotDot && fi.isHidden();
+ if (!includeHidden && isHidden)
+ return false;
+
+ bool isSystem = (!fi.isFile() && !fi.isDir() && !fi.isSymLink())
+ || (!fi.exists() && fi.isSymLink());
+ if (!includeSystem && isSystem)
+ return false;
+
+ bool alwaysShow = (filters & QDir::TypeMask) == 0
+ && ((isHidden && includeHidden)
+ || (includeSystem && isSystem));
+
+ // Skip files and directories
+ if ((filters & QDir::AllDirs) == 0 && skipDirs && fi.isDir()) {
+ if (!alwaysShow)
+ return false;
+ }
+
+ if ((skipFiles && (fi.isFile() || !fi.exists()))
+ || (skipSymlinks && fi.isSymLink())) {
+ if (!alwaysShow)
+ return false;
+ }
+
+ if (filterPermissions
+ && ((doReadable && !fi.isReadable())
+ || (doWritable && !fi.isWritable())
+ || (doExecutable && !fi.isExecutable()))) {
+ return false;
+ }
+
+ if (!includeSystem && !dotOrDotDot && ((fi.exists() && !fi.isFile() && !fi.isDir() && !fi.isSymLink())
+ || (!fi.exists() && fi.isSymLink()))) {
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a dir's entrylist, using
+ \a dir's name filters and regular filters. You can pass options via \a
+ flags to decide how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as in QDir::entryList().
+
+ The sorting in \a dir is ignored.
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
+ : d(new QDirIteratorPrivate(dir.path(), dir.nameFilters(), dir.filter(), flags))
+{
+ d->q = this;
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path, with no name
+ filtering and \a filters for entry filtering. You can pass options via \a
+ flags to decide how the directory should be iterated.
+
+ By default, \a filters is QDir::NoFilter, and \a flags is NoIteratorFlags,
+ which provides the same behavior as in QDir::entryList().
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \warning This constructor expects \a flags to be left at its default value. Use
+ the constructors that do not take the \a filters argument instead.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
+ : d(new QDirIteratorPrivate(path, QStringList(QLatin1String("*")), filters, flags))
+{
+ d->q = this;
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path. You can pass
+ options via \a flags to decide how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as in QDir::entryList().
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
+ : d(new QDirIteratorPrivate(path, QStringList(QLatin1String("*")), QDir::NoFilter, flags))
+{
+ d->q = this;
+}
+
+/*!
+ Constructs a QDirIterator that can iterate over \a path, using \a
+ nameFilters and \a filters. You can pass options via \a flags to decide
+ how the directory should be iterated.
+
+ By default, \a flags is NoIteratorFlags, which provides the same behavior
+ as QDir::entryList().
+
+ \note To list symlinks that point to non existing files, QDir::System must be
+ passed to the flags.
+
+ \warning This constructor expects \c flags to be left at its default value. Use the
+ constructors that do not take the \a filters argument instead.
+
+ \sa hasNext(), next(), IteratorFlags
+*/
+QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
+ QDir::Filters filters, IteratorFlags flags)
+ : d(new QDirIteratorPrivate(path, nameFilters, filters, flags))
+{
+ d->q = this;
+}
+
+/*!
+ Destroys the QDirIterator.
+*/
+QDirIterator::~QDirIterator()
+{
+ qDeleteAll(d->fileEngineIterators);
+ delete d;
+}
+
+/*!
+ Advances the iterator to the next entry, and returns the file path of this
+ new entry. If hasNext() returns false, this function does nothing, and
+ returns a null QString.
+
+ You can call fileName() or filePath() to get the current entry file name
+ or path, or fileInfo() to get a QFileInfo for the current entry.
+
+ \sa hasNext(), fileName(), filePath(), fileInfo()
+*/
+QString QDirIterator::next()
+{
+ if (!hasNext())
+ return QString();
+ d->advance();
+ return filePath();
+}
+
+/*!
+ Returns true if there is at least one more entry in the directory;
+ otherwise, false is returned.
+
+ \sa next(), fileName(), filePath(), fileInfo()
+*/
+bool QDirIterator::hasNext() const
+{
+ if (d->first) {
+ d->first = false;
+ d->advance();
+ }
+ return !d->done;
+}
+
+/*!
+ Returns the file name for the current directory entry, without the path
+ prepended. If the current entry is invalid (i.e., isValid() returns
+ false), a null QString is returned.
+
+ This function is provided for the convenience when iterating single
+ directories. For recursive iteration, you should call filePath() or
+ fileInfo() instead.
+
+ \sa filePath(), fileInfo()
+*/
+QString QDirIterator::fileName() const
+{
+ if (d->fileInfo.path() != d->currentFilePath)
+ d->fileInfo.setFile(d->currentFilePath);
+ return d->fileInfo.fileName();
+}
+
+/*!
+ Returns the full file path for the current directory entry. If the current
+ entry is invalid (i.e., isValid() returns false), a null QString is
+ returned.
+
+ \sa fileInfo(), fileName()
+*/
+QString QDirIterator::filePath() const
+{
+ return d->currentFilePath;
+}
+
+/*!
+ Returns a QFileInfo for the current directory entry. If the current entry
+ is invalid (i.e., isValid() returns false), a null QFileInfo is returned.
+
+ \sa filePath(), fileName()
+*/
+QFileInfo QDirIterator::fileInfo() const
+{
+ if (d->fileInfo.filePath() != d->currentFilePath)
+ d->fileInfo.setFile(d->currentFilePath);
+ return d->fileInfo;
+}
+
+/*!
+ Returns the base directory of the iterator.
+*/
+QString QDirIterator::path() const
+{
+ return d->path;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qdiriterator.h b/src/corelib/io/qdiriterator.h
new file mode 100644
index 0000000..540ad4f
--- /dev/null
+++ b/src/corelib/io/qdiriterator.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDIRITERATOR_H
+#define QDIRITERATOR_H
+
+#include <QtCore/qdir.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QDirIteratorPrivate;
+class Q_CORE_EXPORT QDirIterator {
+public:
+ enum IteratorFlag {
+ NoIteratorFlags = 0x0,
+ FollowSymlinks = 0x1,
+ Subdirectories = 0x2
+ };
+ Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag)
+
+ QDirIterator(const QDir &dir, IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ QDir::Filters filter,
+ IteratorFlags flags = NoIteratorFlags);
+ QDirIterator(const QString &path,
+ const QStringList &nameFilters,
+ QDir::Filters filters = QDir::NoFilter,
+ IteratorFlags flags = NoIteratorFlags);
+
+ virtual ~QDirIterator();
+
+ QString next();
+ bool hasNext() const;
+
+ QString fileName() const;
+ QString filePath() const;
+ QFileInfo fileInfo() const;
+ QString path() const;
+
+private:
+ Q_DISABLE_COPY(QDirIterator)
+
+ QDirIteratorPrivate *d;
+ friend class QDir;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QDirIterator::IteratorFlags)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
new file mode 100644
index 0000000..d8f08c9
--- /dev/null
+++ b/src/corelib/io/qfile.cpp
@@ -0,0 +1,1638 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qdebug.h"
+#include "qfile.h"
+#include "qfsfileengine.h"
+#include "qtemporaryfile.h"
+#include "qlist.h"
+#include "qfileinfo.h"
+#include "private/qiodevice_p.h"
+#include "private/qfile_p.h"
+#if defined(QT_BUILD_CORE_LIB)
+# include "qcoreapplication.h"
+#endif
+
+#if !defined(Q_OS_WINCE)
+#include <errno.h>
+#endif
+
+#ifdef QT_NO_QOBJECT
+#define tr(X) QString::fromLatin1(X)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const int QFILE_WRITEBUFFER_SIZE = 16384;
+
+static QByteArray locale_encode(const QString &f)
+{
+#ifndef Q_OS_DARWIN
+ return f.toLocal8Bit();
+#else
+ // Mac always expects UTF-8... and decomposed...
+ return f.normalized(QString::NormalizationForm_D).toUtf8();
+#endif
+}
+
+static QString locale_decode(const QByteArray &f)
+{
+#ifndef Q_OS_DARWIN
+ return QString::fromLocal8Bit(f);
+#else
+ // Mac always gives us UTF-8 and decomposed, we want that composed...
+ return QString::fromUtf8(f).normalized(QString::NormalizationForm_C);
+#endif
+}
+
+//************* QFilePrivate
+QFile::EncoderFn QFilePrivate::encoder = locale_encode;
+QFile::DecoderFn QFilePrivate::decoder = locale_decode;
+
+QFilePrivate::QFilePrivate()
+ : fileEngine(0), lastWasWrite(false),
+ writeBuffer(QFILE_WRITEBUFFER_SIZE), error(QFile::NoError)
+{
+}
+
+QFilePrivate::~QFilePrivate()
+{
+ delete fileEngine;
+ fileEngine = 0;
+}
+
+bool
+QFilePrivate::openExternalFile(int flags, int fd)
+{
+#ifdef QT_NO_FSFILEENGINE
+ Q_UNUSED(flags);
+ Q_UNUSED(fd);
+ return false;
+#else
+ delete fileEngine;
+ QFSFileEngine *fe = new QFSFileEngine;
+ fe->setFileName(fileName);
+ fileEngine = fe;
+ return fe->open(QIODevice::OpenMode(flags), fd);
+#endif
+}
+
+bool
+QFilePrivate::openExternalFile(int flags, FILE *fh)
+{
+#ifdef QT_NO_FSFILEENGINE
+ Q_UNUSED(flags);
+ Q_UNUSED(fh);
+ return false;
+#else
+ delete fileEngine;
+ QFSFileEngine *fe = new QFSFileEngine;
+ fe->setFileName(fileName);
+ fileEngine = fe;
+ return fe->open(QIODevice::OpenMode(flags), fh);
+#endif
+}
+
+inline bool QFilePrivate::ensureFlushed() const
+{
+ // This function ensures that the write buffer has been flushed (const
+ // because certain const functions need to call it.
+ if (lastWasWrite) {
+ const_cast<QFilePrivate *>(this)->lastWasWrite = false;
+ if (!const_cast<QFile *>(q_func())->flush())
+ return false;
+ }
+ return true;
+}
+
+void
+QFilePrivate::setError(QFile::FileError err)
+{
+ error = err;
+ errorString.clear();
+}
+
+void
+QFilePrivate::setError(QFile::FileError err, const QString &errStr)
+{
+ Q_Q(QFile);
+ error = err;
+ q->setErrorString(errStr);
+}
+
+void
+QFilePrivate::setError(QFile::FileError err, int errNum)
+{
+ Q_Q(QFile);
+ error = err;
+ q->setErrorString(qt_error_string(errNum));
+}
+
+//************* QFile
+
+/*!
+ \class QFile
+ \brief The QFile class provides an interface for reading from and writing to files.
+
+ \ingroup io
+ \mainclass
+ \reentrant
+
+ QFile is an I/O device for reading and writing text and binary
+ files and \l{The Qt Resource System}{resources}. A QFile may be
+ used by itself or, more conveniently, with a QTextStream or
+ QDataStream.
+
+ The file name is usually passed in the constructor, but it can be
+ set at any time using setFileName(). QFile expects the file
+ separator to be '/' regardless of operating system. The use of
+ other separators (e.g., '\\') is not supported.
+
+ You can check for a file's existence using exists(), and remove a
+ file using remove(). (More advanced file system related operations
+ are provided by QFileInfo and QDir.)
+
+ The file is opened with open(), closed with close(), and flushed
+ with flush(). Data is usually read and written using QDataStream
+ or QTextStream, but you can also call the QIODevice-inherited
+ functions read(), readLine(), readAll(), write(). QFile also
+ inherits getChar(), putChar(), and ungetChar(), which work one
+ character at a time.
+
+ The size of the file is returned by size(). You can get the
+ current file position using pos(), or move to a new file position
+ using seek(). If you've reached the end of the file, atEnd()
+ returns true.
+
+ \section1 Reading Files Directly
+
+ The following example reads a text file line by line:
+
+ \snippet doc/src/snippets/file/file.cpp 0
+
+ The QIODevice::Text flag passed to open() tells Qt to convert
+ Windows-style line terminators ("\\r\\n") into C++-style
+ terminators ("\\n"). By default, QFile assumes binary, i.e. it
+ doesn't perform any conversion on the bytes stored in the file.
+
+ \section1 Using Streams to Read Files
+
+ The next example uses QTextStream to read a text file
+ line by line:
+
+ \snippet doc/src/snippets/file/file.cpp 1
+
+ QTextStream takes care of converting the 8-bit data stored on
+ disk into a 16-bit Unicode QString. By default, it assumes that
+ the user system's local 8-bit encoding is used (e.g., ISO 8859-1
+ for most of Europe; see QTextCodec::codecForLocale() for
+ details). This can be changed using setCodec().
+
+ To write text, we can use operator<<(), which is overloaded to
+ take a QTextStream on the left and various data types (including
+ QString) on the right:
+
+ \snippet doc/src/snippets/file/file.cpp 2
+
+ QDataStream is similar, in that you can use operator<<() to write
+ data and operator>>() to read it back. See the class
+ documentation for details.
+
+ When you use QFile, QFileInfo, and QDir to access the file system
+ with Qt, you can use Unicode file names. On Unix, these file
+ names are converted to an 8-bit encoding. If you want to use
+ standard C++ APIs (\c <cstdio> or \c <iostream>) or
+ platform-specific APIs to access files instead of QFile, you can
+ use the encodeName() and decodeName() functions to convert
+ between Unicode file names and 8-bit file names.
+
+ On Unix, there are some special system files (e.g. in \c /proc) for which
+ size() will always return 0, yet you may still be able to read more data
+ from such a file; the data is generated in direct response to you calling
+ read(). In this case, however, you cannot use atEnd() to determine if
+ there is more data to read (since atEnd() will return true for a file that
+ claims to have size 0). Instead, you should either call readAll(), or call
+ read() or readLine() repeatedly until no more data can be read. The next
+ example uses QTextStream to read \c /proc/modules line by line:
+
+ \snippet doc/src/snippets/file/file.cpp 3
+
+ \section1 Signals
+
+ Unlike other QIODevice implementations, such as QTcpSocket, QFile does not
+ emit the aboutToClose(), bytesWritten(), or readyRead() signals. This
+ implementation detail means that QFile is not suitable for reading and
+ writing certain types of files, such as device files on Unix platforms.
+
+ \section1 Platform Specific Issues
+
+ File permissions are handled differently on Linux/Mac OS X and
+ Windows. In a non \l{QIODevice::isWritable()}{writable}
+ directory on Linux, files cannot be created. This is not always
+ the case on Windows, where, for instance, the 'My Documents'
+ directory usually is not writable, but it is still possible to
+ create files in it.
+
+ \sa QTextStream, QDataStream, QFileInfo, QDir, {The Qt Resource System}
+*/
+
+/*!
+ \enum QFile::FileError
+
+ This enum describes the errors that may be returned by the error()
+ function.
+
+ \value NoError No error occurred.
+ \value ReadError An error occurred when reading from the file.
+ \value WriteError An error occurred when writing to the file.
+ \value FatalError A fatal error occurred.
+ \value ResourceError
+ \value OpenError The file could not be opened.
+ \value AbortError The operation was aborted.
+ \value TimeOutError A timeout occurred.
+ \value UnspecifiedError An unspecified error occurred.
+ \value RemoveError The file could not be removed.
+ \value RenameError The file could not be renamed.
+ \value PositionError The position in the file could not be changed.
+ \value ResizeError The file could not be resized.
+ \value PermissionsError The file could not be accessed.
+ \value CopyError The file could not be copied.
+
+ \omitvalue ConnectError
+*/
+
+/*!
+ \enum QFile::Permission
+
+ This enum is used by the permission() function to report the
+ permissions and ownership of a file. The values may be OR-ed
+ together to test multiple permissions and ownership values.
+
+ \value ReadOwner The file is readable by the owner of the file.
+ \value WriteOwner The file is writable by the owner of the file.
+ \value ExeOwner The file is executable by the owner of the file.
+ \value ReadUser The file is readable by the user.
+ \value WriteUser The file is writable by the user.
+ \value ExeUser The file is executable by the user.
+ \value ReadGroup The file is readable by the group.
+ \value WriteGroup The file is writable by the group.
+ \value ExeGroup The file is executable by the group.
+ \value ReadOther The file is readable by anyone.
+ \value WriteOther The file is writable by anyone.
+ \value ExeOther The file is executable by anyone.
+
+ \warning Because of differences in the platforms supported by Qt,
+ the semantics of ReadUser, WriteUser and ExeUser are
+ platform-dependent: On Unix, the rights of the owner of the file
+ are returned and on Windows the rights of the current user are
+ returned. This behavior might change in a future Qt version.
+
+ Note that Qt does not by default check for permissions on NTFS
+ file systems, as this may decrease the performance of file
+ handling considerably. It is possible to force permission checking
+ on NTFS by including the following code in your source:
+
+ \snippet doc/src/snippets/ntfsp.cpp 0
+
+ Permission checking is then turned on and off by incrementing and
+ decrementing \c qt_ntfs_permission_lookup by 1.
+
+ \snippet doc/src/snippets/ntfsp.cpp 1
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \typedef QFile::PermissionSpec
+
+ Use QFile::Permission instead.
+*/
+#endif
+
+#ifdef QT_NO_QOBJECT
+QFile::QFile()
+ : QIODevice(*new QFilePrivate)
+{
+}
+QFile::QFile(const QString &name)
+ : QIODevice(*new QFilePrivate)
+{
+ d_func()->fileName = name;
+}
+QFile::QFile(QFilePrivate &dd)
+ : QIODevice(dd)
+{
+}
+#else
+/*!
+ \internal
+*/
+QFile::QFile()
+ : QIODevice(*new QFilePrivate, 0)
+{
+}
+/*!
+ Constructs a new file object with the given \a parent.
+*/
+QFile::QFile(QObject *parent)
+ : QIODevice(*new QFilePrivate, parent)
+{
+}
+/*!
+ Constructs a new file object to represent the file with the given \a name.
+*/
+QFile::QFile(const QString &name)
+ : QIODevice(*new QFilePrivate, 0)
+{
+ Q_D(QFile);
+ d->fileName = name;
+}
+/*!
+ Constructs a new file object with the given \a parent to represent the
+ file with the specified \a name.
+*/
+QFile::QFile(const QString &name, QObject *parent)
+ : QIODevice(*new QFilePrivate, parent)
+{
+ Q_D(QFile);
+ d->fileName = name;
+}
+/*!
+ \internal
+*/
+QFile::QFile(QFilePrivate &dd, QObject *parent)
+ : QIODevice(dd, parent)
+{
+}
+#endif
+
+/*!
+ Destroys the file object, closing it if necessary.
+*/
+QFile::~QFile()
+{
+ close();
+#ifdef QT_NO_QOBJECT
+ delete d_ptr;
+#endif
+}
+
+/*!
+ Returns the name set by setFileName() or to the QFile
+ constructors.
+
+ \sa setFileName(), QFileInfo::fileName()
+*/
+QString QFile::fileName() const
+{
+ return fileEngine()->fileName(QAbstractFileEngine::DefaultName);
+}
+
+/*!
+ Sets the \a name of the file. The name can have no path, a
+ relative path, or an absolute path.
+
+ Do not call this function if the file has already been opened.
+
+ If the file name has no path or a relative path, the path used
+ will be the application's current directory path
+ \e{at the time of the open()} call.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 0
+
+ Note that the directory separator "/" works for all operating
+ systems supported by Qt.
+
+ \sa fileName(), QFileInfo, QDir
+*/
+void
+QFile::setFileName(const QString &name)
+{
+ Q_D(QFile);
+ if (isOpen()) {
+ qWarning("QFile::setFileName: File (%s) is already opened",
+ qPrintable(fileName()));
+ close();
+ }
+ if(d->fileEngine) { //get a new file engine later
+ delete d->fileEngine;
+ d->fileEngine = 0;
+ }
+ d->fileName = name;
+}
+
+/*!
+ \fn QString QFile::decodeName(const char *localFileName)
+
+ \overload
+
+ Returns the Unicode version of the given \a localFileName. See
+ encodeName() for details.
+*/
+
+/*!
+ By default, this function converts \a fileName to the local 8-bit
+ encoding determined by the user's locale. This is sufficient for
+ file names that the user chooses. File names hard-coded into the
+ application should only use 7-bit ASCII filename characters.
+
+ \sa decodeName() setEncodingFunction()
+*/
+
+QByteArray
+QFile::encodeName(const QString &fileName)
+{
+ return (*QFilePrivate::encoder)(fileName);
+}
+
+/*!
+ \typedef QFile::EncoderFn
+
+ This is a typedef for a pointer to a function with the following
+ signature:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 1
+
+ \sa setEncodingFunction(), encodeName()
+*/
+
+/*!
+ This does the reverse of QFile::encodeName() using \a localFileName.
+
+ \sa setDecodingFunction(), encodeName()
+*/
+
+QString
+QFile::decodeName(const QByteArray &localFileName)
+{
+ return (*QFilePrivate::decoder)(localFileName);
+}
+
+/*!
+ \fn void QFile::setEncodingFunction(EncoderFn function)
+
+ \nonreentrant
+
+ Sets the \a function for encoding Unicode file names. The
+ default encodes in the locale-specific 8-bit encoding.
+
+ \sa encodeName(), setDecodingFunction()
+*/
+
+void
+QFile::setEncodingFunction(EncoderFn f)
+{
+ if (!f)
+ f = locale_encode;
+ QFilePrivate::encoder = f;
+}
+
+/*!
+ \typedef QFile::DecoderFn
+
+ This is a typedef for a pointer to a function with the following
+ signature:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 2
+
+ \sa setDecodingFunction()
+*/
+
+/*!
+ \fn void QFile::setDecodingFunction(DecoderFn function)
+
+ \nonreentrant
+
+ Sets the \a function for decoding 8-bit file names. The
+ default uses the locale-specific 8-bit encoding.
+
+ \sa setEncodingFunction(), decodeName()
+*/
+
+void
+QFile::setDecodingFunction(DecoderFn f)
+{
+ if (!f)
+ f = locale_decode;
+ QFilePrivate::decoder = f;
+}
+
+/*!
+ \overload
+
+ Returns true if the file specified by fileName() exists; otherwise
+ returns false.
+
+ \sa fileName(), setFileName()
+*/
+
+bool
+QFile::exists() const
+{
+ // 0x1000000 = QAbstractFileEngine::Refresh, forcing an update
+ return (fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask
+ | QAbstractFileEngine::FileFlag(0x1000000)) & QAbstractFileEngine::ExistsFlag);
+}
+
+/*!
+ Returns true if the file specified by \a fileName exists; otherwise
+ returns false.
+*/
+
+bool
+QFile::exists(const QString &fileName)
+{
+ return QFileInfo(fileName).exists();
+}
+
+/*!
+ \fn QString QFile::symLinkTarget() const
+ \since 4.2
+ \overload
+
+ Returns the absolute path of the file or directory a symlink (or shortcut
+ on Windows) points to, or a an empty string if the object isn't a symbolic
+ link.
+
+ This name may not represent an existing file; it is only a string.
+ QFile::exists() returns true if the symlink points to an existing file.
+
+ \sa fileName() setFileName()
+*/
+
+/*!
+ \obsolete
+
+ Use symLinkTarget() instead.
+*/
+QString
+QFile::readLink() const
+{
+ return fileEngine()->fileName(QAbstractFileEngine::LinkName);
+}
+
+/*!
+ \fn static QString QFile::symLinkTarget(const QString &fileName)
+ \since 4.2
+
+ Returns the absolute path of the file or directory referred to by the
+ symlink (or shortcut on Windows) specified by \a fileName, or returns an
+ empty string if the \a fileName does not correspond to a symbolic link.
+
+ This name may not represent an existing file; it is only a string.
+ QFile::exists() returns true if the symlink points to an existing file.
+*/
+
+/*!
+ \obsolete
+
+ Use symLinkTarget() instead.
+*/
+QString
+QFile::readLink(const QString &fileName)
+{
+ return QFileInfo(fileName).readLink();
+}
+
+/*!
+ Removes the file specified by fileName(). Returns true if successful;
+ otherwise returns false.
+
+ The file is closed before it is removed.
+
+ \sa setFileName()
+*/
+
+bool
+QFile::remove()
+{
+ Q_D(QFile);
+ if (d->fileName.isEmpty()) {
+ qWarning("QFile::remove: Empty or null file name");
+ return false;
+ }
+ close();
+ if(error() == QFile::NoError) {
+ if(fileEngine()->remove()) {
+ unsetError();
+ return true;
+ }
+#if defined(Q_OS_WIN)
+ d->setError(QFile::RemoveError, GetLastError());
+#else
+ d->setError(QFile::RemoveError, errno);
+#endif
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Removes the file specified by the \a fileName given.
+
+ Returns true if successful; otherwise returns false.
+
+ \sa remove()
+*/
+
+bool
+QFile::remove(const QString &fileName)
+{
+ return QFile(fileName).remove();
+}
+
+/*!
+ Renames the file currently specified by fileName() to \a newName.
+ Returns true if successful; otherwise returns false.
+
+ If a file with the name \a newName already exists, rename() returns false
+ (i.e., QFile will not overwrite it).
+
+ The file is closed before it is renamed.
+
+ \sa setFileName()
+*/
+
+bool
+QFile::rename(const QString &newName)
+{
+ Q_D(QFile);
+ if (d->fileName.isEmpty()) {
+ qWarning("QFile::rename: Empty or null file name");
+ return false;
+ }
+ if (QFile(newName).exists()) {
+ // ### Race condition. If a file is moved in after this, it /will/ be
+ // overwritten. On Unix, the proper solution is to use hardlinks:
+ // return ::link(old, new) && ::remove(old);
+ d->setError(QFile::RenameError, tr("Destination file exists"));
+ return false;
+ }
+ close();
+ if(error() == QFile::NoError) {
+ if (fileEngine()->rename(newName)) {
+ unsetError();
+ return true;
+ }
+
+ QFile in(fileName());
+ QFile out(newName);
+ if (in.open(QIODevice::ReadOnly)) {
+ if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ bool error = false;
+ char block[4096];
+ qint64 read;
+ while ((read = in.read(block, sizeof(block))) > 0) {
+ if (read != out.write(block, read)) {
+ d->setError(QFile::RenameError, out.errorString());
+ error = true;
+ break;
+ }
+ }
+ if (read == -1) {
+ d->setError(QFile::RenameError, in.errorString());
+ return true;
+ }
+ if(!error)
+ in.remove();
+ return !error;
+ }
+ }
+ d->setError(QFile::RenameError, out.isOpen() ? in.errorString() : out.errorString());
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Renames the file \a oldName to \a newName. Returns true if
+ successful; otherwise returns false.
+
+ If a file with the name \a newName already exists, rename() returns false
+ (i.e., QFile will not overwrite it).
+
+ \sa rename()
+*/
+
+bool
+QFile::rename(const QString &oldName, const QString &newName)
+{
+ return QFile(oldName).rename(newName);
+}
+
+/*!
+
+ Creates a link named \a linkName that points to the file currently specified by
+ fileName(). What a link is depends on the underlying filesystem (be it a
+ shortcut on Windows or a symbolic link on Unix). Returns true if successful;
+ otherwise returns false.
+
+ This function will not overwrite an already existing entity in the file system;
+ in this case, \c link() will return false and set \l{QFile::}{error()} to
+ return \l{QFile::}{RenameError}.
+
+ \note To create a valid link on Windows, \a linkName must have a \c{.lnk} file extension.
+
+ \sa setFileName()
+*/
+
+bool
+QFile::link(const QString &linkName)
+{
+ Q_D(QFile);
+ if (d->fileName.isEmpty()) {
+ qWarning("QFile::link: Empty or null file name");
+ return false;
+ }
+ QFileInfo fi(linkName);
+ if(fileEngine()->link(fi.absoluteFilePath())) {
+ unsetError();
+ return true;
+ }
+ d->setError(QFile::RenameError, errno);
+ return false;
+}
+
+/*!
+ \overload
+
+ Creates a link named \a linkName that points to the file \a fileName. What a link is
+ depends on the underlying filesystem (be it a shortcut on Windows
+ or a symbolic link on Unix). Returns true if successful; otherwise
+ returns false.
+
+ \sa link()
+*/
+
+bool
+QFile::link(const QString &fileName, const QString &linkName)
+{
+ return QFile(fileName).link(linkName);
+}
+
+/*!
+ Copies the file currently specified by fileName() to a file called
+ \a newName. Returns true if successful; otherwise returns false.
+
+ Note that if a file with the name \a newName already exists,
+ copy() returns false (i.e. QFile will not overwrite it).
+
+ The source file is closed before it is copied.
+
+ \sa setFileName()
+*/
+
+bool
+QFile::copy(const QString &newName)
+{
+ Q_D(QFile);
+ if (d->fileName.isEmpty()) {
+ qWarning("QFile::copy: Empty or null file name");
+ return false;
+ }
+ if (QFile(newName).exists()) {
+ // ### Race condition. If a file is moved in after this, it /will/ be
+ // overwritten. On Unix, the proper solution is to use hardlinks:
+ // return ::link(old, new) && ::remove(old); See also rename().
+ d->setError(QFile::CopyError, tr("Destination file exists"));
+ return false;
+ }
+ close();
+ if(error() == QFile::NoError) {
+ if(fileEngine()->copy(newName)) {
+ unsetError();
+ return true;
+ } else {
+ bool error = false;
+ if(!open(QFile::ReadOnly)) {
+ error = true;
+ d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
+ } else {
+ QString fileTemplate = QLatin1String("%1/qt_temp.XXXXXX");
+#ifdef QT_NO_TEMPORARYFILE
+ QFile out(fileTemplate.arg(QFileInfo(newName).path()));
+ if (!out.open(QIODevice::ReadWrite))
+ error = true;
+#else
+ QTemporaryFile out(fileTemplate.arg(QFileInfo(newName).path()));
+ if (!out.open()) {
+ out.setFileTemplate(fileTemplate.arg(QDir::tempPath()));
+ if (!out.open())
+ error = true;
+ }
+#endif
+ if (error) {
+ out.close();
+ d->setError(QFile::CopyError, tr("Cannot open for output"));
+ } else {
+ char block[4096];
+ qint64 totalRead = 0;
+ while(!atEnd()) {
+ qint64 in = read(block, sizeof(block));
+ if (in <= 0)
+ break;
+ totalRead += in;
+ if(in != out.write(block, in)) {
+ d->setError(QFile::CopyError, tr("Failure to write block"));
+ error = true;
+ break;
+ }
+ }
+
+ if (totalRead != size()) {
+ // Unable to read from the source. The error string is
+ // already set from read().
+ error = true;
+ }
+ if (!error && !out.rename(newName)) {
+ error = true;
+ d->setError(QFile::CopyError, tr("Cannot create %1 for output").arg(newName));
+ }
+#ifndef QT_NO_TEMPORARYFILE
+ if (!error)
+ out.setAutoRemove(false);
+#endif
+ }
+ }
+ if(!error) {
+ QFile::setPermissions(newName, permissions());
+ unsetError();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*!
+ \overload
+
+ Copies the file \a fileName to \a newName. Returns true if successful;
+ otherwise returns false.
+
+ If a file with the name \a newName already exists, copy() returns false
+ (i.e., QFile will not overwrite it).
+
+ \sa rename()
+*/
+
+bool
+QFile::copy(const QString &fileName, const QString &newName)
+{
+ return QFile(fileName).copy(newName);
+}
+
+/*!
+ Returns true if the file can only be manipulated sequentially;
+ otherwise returns false.
+
+ Most files support random-access, but some special files may not.
+
+ \sa QIODevice::isSequential()
+*/
+bool QFile::isSequential() const
+{
+ Q_D(const QFile);
+ return d->fileEngine && d->fileEngine->isSequential();
+}
+
+/*!
+ Opens the file using OpenMode \a mode, returning true if successful;
+ otherwise false.
+
+ The \a mode must be QIODevice::ReadOnly, QIODevice::WriteOnly, or
+ QIODevice::ReadWrite. It may also have additional flags, such as
+ QIODevice::Text and QIODevice::Unbuffered.
+
+ \note In \l{QIODevice::}{WriteOnly} or \l{QIODevice::}{ReadWrite}
+ mode, if the relevant file does not already exist, this function
+ will try to create a new file before opening it.
+
+ \note Because of limitations in the native API, QFile ignores the
+ Unbuffered flag on Windows.
+
+ \sa QIODevice::OpenMode, setFileName()
+*/
+bool QFile::open(OpenMode mode)
+{
+ Q_D(QFile);
+ if (isOpen()) {
+ qWarning("QFile::open: File (%s) already open", qPrintable(fileName()));
+ return false;
+ }
+ if (mode & Append)
+ mode |= WriteOnly;
+
+ unsetError();
+ if ((mode & (ReadOnly | WriteOnly)) == 0) {
+ qWarning("QIODevice::open: File access not specified");
+ return false;
+ }
+ if (fileEngine()->open(mode)) {
+ QIODevice::open(mode);
+ if (mode & Append)
+ seek(size());
+ return true;
+ }
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::OpenError;
+ d->setError(err, fileEngine()->errorString());
+ return false;
+}
+
+/*! \fn QFile::open(OpenMode, FILE*)
+
+ Use open(FILE *, OpenMode) instead.
+*/
+
+/*!
+ \overload
+
+ Opens the existing file handle \a fh in the given \a mode.
+ Returns true if successful; otherwise returns false.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 3
+
+ When a QFile is opened using this function, close() does not actually
+ close the file, but only flushes it.
+
+ \bold{Warning:}
+ \list 1
+ \o If \a fh is \c stdin, \c stdout, or \c stderr, you may not be able
+ to seek(). See QIODevice::isSequentialAccess() for more information.
+ \o Since this function opens the file without specifying the file name,
+ you cannot use this QFile with a QFileInfo.
+ \endlist
+
+ \note For Windows CE you may not be able to call seek() and resize().
+ Also, size() is set to \c 0.
+
+ \sa close(), {qmake Variable Reference#CONFIG}{qmake Variable Reference}
+
+ \bold{Note for the Windows Platform}
+
+ \a fh must be opened in binary mode (i.e., the mode string must contain
+ 'b', as in "rb" or "wb") when accessing files and other random-access
+ devices. Qt will translate the end-of-line characters if you pass
+ QIODevice::Text to \a mode. Sequential devices, such as stdin and stdout,
+ are unaffected by this limitation.
+
+ You need to enable support for console applications in order to use the
+ stdin, stdout and stderr streams at the console. To do this, add the
+ following declaration to your application's project file:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfile.cpp 4
+*/
+bool QFile::open(FILE *fh, OpenMode mode)
+{
+ Q_D(QFile);
+ if (isOpen()) {
+ qWarning("QFile::open: File (%s) already open", qPrintable(fileName()));
+ return false;
+ }
+ if (mode & Append)
+ mode |= WriteOnly;
+ unsetError();
+ if ((mode & (ReadOnly | WriteOnly)) == 0) {
+ qWarning("QFile::open: File access not specified");
+ return false;
+ }
+ if(d->openExternalFile(mode, fh)) {
+ QIODevice::open(mode);
+ if (mode & Append) {
+ seek(size());
+ } else {
+ long pos = ftell(fh);
+ if (pos != -1)
+ seek(pos);
+ }
+ return true;
+ }
+ return false;
+}
+
+/*! \fn QFile::open(OpenMode, int)
+
+ Use open(int, OpenMode) instead.
+*/
+
+/*!
+ \overload
+
+ Opens the existing file descripter \a fd in the given \a mode.
+ Returns true if successful; otherwise returns false.
+
+ When a QFile is opened using this function, close() does not
+ actually close the file.
+
+ The QFile that is opened using this function is automatically set
+ to be in raw mode; this means that the file input/output functions
+ are slow. If you run into performance issues, you should try to
+ use one of the other open functions.
+
+ \warning If \a fd is 0 (\c stdin), 1 (\c stdout), or 2 (\c
+ stderr), you may not be able to seek(). size() is set to \c
+ LLONG_MAX (in \c <climits>).
+
+ \warning For Windows CE you may not be able to call seek(), setSize(),
+ fileTime(). size() is set to \c 0.
+
+ \warning Since this function opens the file without specifying the file name,
+ you cannot use this QFile with a QFileInfo.
+
+ \sa close()
+*/
+bool QFile::open(int fd, OpenMode mode)
+{
+ Q_D(QFile);
+ if (isOpen()) {
+ qWarning("QFile::open: File (%s) already open", qPrintable(fileName()));
+ return false;
+ }
+ if (mode & Append)
+ mode |= WriteOnly;
+ unsetError();
+ if ((mode & (ReadOnly | WriteOnly)) == 0) {
+ qWarning("QFile::open: File access not specified");
+ return false;
+ }
+ if(d->openExternalFile(mode, fd)) {
+ QIODevice::open(mode);
+ if (mode & Append)
+ seek(size());
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the file handle of the file.
+
+ This is a small positive integer, suitable for use with C library
+ functions such as fdopen() and fcntl(). On systems that use file
+ descriptors for sockets (i.e. Unix systems, but not Windows) the handle
+ can be used with QSocketNotifier as well.
+
+ If the file is not open, or there is an error, handle() returns -1.
+
+ This function is not supported on Windows CE.
+
+ \sa QSocketNotifier
+*/
+
+int
+QFile::handle() const
+{
+ if (!isOpen())
+ return -1;
+
+ if (QAbstractFileEngine *engine = fileEngine())
+ return engine->handle();
+ return -1;
+}
+
+/*!
+ \enum QFile::MemoryMapFlags
+ \since 4.4
+
+ This enum describes special options that may be used by the map()
+ function.
+
+ \value NoOptions No options.
+*/
+
+/*!
+ \since 4.4
+ Maps \a size bytes of the file into memory starting at \a offset. A file
+ should be open for a map to succeed but the file does not need to stay
+ open after the memory has been mapped. When the QFile is destroyed
+ or a new file is opened with this object, any maps that have not been
+ unmapped will automatically be unmapped.
+
+ Any mapping options can be passed through \a flags.
+
+ Returns a pointer to the memory or 0 if there is an error.
+
+ \note On Windows CE 5.0 the file will be closed before mapping occurs.
+
+ \sa unmap(), QAbstractFileEngine::supportsExtension()
+ */
+uchar *QFile::map(qint64 offset, qint64 size, MemoryMapFlags flags)
+{
+ Q_D(QFile);
+ QAbstractFileEngine *engine = fileEngine();
+ if (engine
+ && engine->supportsExtension(QAbstractFileEngine::MapExtension)) {
+ unsetError();
+ uchar *address = engine->map(offset, size, flags);
+ if (address == 0)
+ d->setError(engine->error(), engine->errorString());
+ return address;
+ }
+ return 0;
+}
+
+/*!
+ \since 4.4
+ Unmaps the memory \a address.
+
+ Returns true if the unmap succeeds; false otherwise.
+
+ \sa map(), QAbstractFileEngine::supportsExtension()
+ */
+bool QFile::unmap(uchar *address)
+{
+ Q_D(QFile);
+ QAbstractFileEngine *engine = fileEngine();
+ if (engine
+ && engine->supportsExtension(QAbstractFileEngine::UnMapExtension)) {
+ unsetError();
+ bool success = engine->unmap(address);
+ if (!success)
+ d->setError(engine->error(), engine->errorString());
+ return success;
+ }
+ return false;
+}
+
+/*!
+ \fn QString QFile::name() const
+
+ Use fileName() instead.
+*/
+
+/*!
+ \fn void QFile::setName(const QString &name)
+
+ Use setFileName() instead.
+*/
+
+/*!
+ Sets the file size (in bytes) \a sz. Returns true if the file if the
+ resize succeeds; false otherwise. If \a sz is larger than the file
+ currently is the new bytes will be set to 0, if \a sz is smaller the
+ file is simply truncated.
+
+ \sa size(), setFileName()
+*/
+
+bool
+QFile::resize(qint64 sz)
+{
+ Q_D(QFile);
+ if (!d->ensureFlushed())
+ return false;
+ if (isOpen() && fileEngine()->pos() > sz)
+ seek(sz);
+ if(fileEngine()->setSize(sz)) {
+ unsetError();
+ return true;
+ }
+ d->setError(QFile::ResizeError, errno);
+ return false;
+}
+
+/*!
+ \overload
+
+ Sets \a fileName to size (in bytes) \a sz. Returns true if the file if
+ the resize succeeds; false otherwise. If \a sz is larger than \a
+ fileName currently is the new bytes will be set to 0, if \a sz is
+ smaller the file is simply truncated.
+
+ \sa resize()
+*/
+
+bool
+QFile::resize(const QString &fileName, qint64 sz)
+{
+ return QFile(fileName).resize(sz);
+}
+
+/*!
+ Returns the complete OR-ed together combination of
+ QFile::Permission for the file.
+
+ \sa setPermissions(), setFileName()
+*/
+
+QFile::Permissions
+QFile::permissions() const
+{
+ QAbstractFileEngine::FileFlags perms = fileEngine()->fileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask;
+ return QFile::Permissions((int)perms); //ewww
+}
+
+/*!
+ \overload
+
+ Returns the complete OR-ed together combination of
+ QFile::Permission for \a fileName.
+*/
+
+QFile::Permissions
+QFile::permissions(const QString &fileName)
+{
+ return QFile(fileName).permissions();
+}
+
+/*!
+ Sets the permissions for the file to the \a permissions specified.
+ Returns true if successful, or false if the permissions cannot be
+ modified.
+
+ \sa permissions(), setFileName()
+*/
+
+bool
+QFile::setPermissions(Permissions permissions)
+{
+ Q_D(QFile);
+ if(fileEngine()->setPermissions(permissions)) {
+ unsetError();
+ return true;
+ }
+ d->setError(QFile::PermissionsError, errno);
+ return false;
+}
+
+/*!
+ \overload
+
+ Sets the permissions for \a fileName file to \a permissions.
+*/
+
+bool
+QFile::setPermissions(const QString &fileName, Permissions permissions)
+{
+ return QFile(fileName).setPermissions(permissions);
+}
+
+static inline qint64 _qfile_writeData(QAbstractFileEngine *engine, QRingBuffer *buffer)
+{
+ qint64 ret = engine->write(buffer->readPointer(), buffer->size());
+ if (ret > 0)
+ buffer->free(ret);
+ return ret;
+}
+
+/*!
+ Flushes any buffered data to the file. Returns true if successful;
+ otherwise returns false.
+*/
+
+bool
+QFile::flush()
+{
+ Q_D(QFile);
+ if (!d->writeBuffer.isEmpty()) {
+ qint64 size = d->writeBuffer.size();
+ if (_qfile_writeData(d->fileEngine ? d->fileEngine : fileEngine(),
+ &d->writeBuffer) != size) {
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::WriteError;
+ d->setError(err, fileEngine()->errorString());
+ return false;
+ }
+ }
+
+ if (!fileEngine()->flush()) {
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::WriteError;
+ d->setError(err, fileEngine()->errorString());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Flushes the file and then closes it.
+
+ \sa QIODevice::close()
+*/
+void
+QFile::close()
+{
+ Q_D(QFile);
+ if(!isOpen())
+ return;
+ flush();
+ QIODevice::close();
+
+ unsetError();
+ if(!fileEngine()->close())
+ d->setError(fileEngine()->error(), fileEngine()->errorString());
+}
+
+/*!
+ Returns the size of the file.
+
+ For regular empty files on Unix (e.g. those in \c /proc), this function
+ returns 0; the contents of such a file are generated on demand in response
+ to you calling read().
+*/
+
+qint64 QFile::size() const
+{
+ Q_D(const QFile);
+ if (!d->ensureFlushed())
+ return 0;
+ return fileEngine()->size();
+}
+
+/*!
+ \reimp
+*/
+
+qint64 QFile::pos() const
+{
+ return QIODevice::pos();
+}
+
+/*!
+ Returns true if the end of the file has been reached; otherwise returns
+ false.
+
+ For regular empty files on Unix (e.g. those in \c /proc), this function
+ returns true, since the file system reports that the size of such a file is
+ 0. Therefore, you should not depend on atEnd() when reading data from such a
+ file, but rather call read() until no more data can be read.
+*/
+
+bool QFile::atEnd() const
+{
+ Q_D(const QFile);
+
+ if (!isOpen())
+ return true;
+
+ if (!d->ensureFlushed())
+ return false;
+
+ // If there's buffered data left, we're not at the end.
+ if (!d->buffer.isEmpty())
+ return false;
+
+ // If the file engine knows best, say what it says.
+ if (fileEngine()->supportsExtension(QAbstractFileEngine::AtEndExtension)) {
+ // Check if the file engine supports AtEndExtension, and if it does,
+ // check if the file engine claims to be at the end.
+ return fileEngine()->atEnd();
+ }
+
+ // Fall back to checking how much is available (will stat files).
+ return bytesAvailable() == 0;
+}
+
+/*!
+ \reimp
+*/
+
+bool QFile::seek(qint64 off)
+{
+ Q_D(QFile);
+ if (!isOpen()) {
+ qWarning("QFile::seek: IODevice is not open");
+ return false;
+ }
+
+ if (!d->ensureFlushed())
+ return false;
+
+ if (!fileEngine()->seek(off) || !QIODevice::seek(off)) {
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::PositionError;
+ d->setError(err, fileEngine()->errorString());
+ return false;
+ }
+ d->error = NoError;
+ return true;
+}
+
+/*!
+ \reimp
+*/
+qint64 QFile::readLineData(char *data, qint64 maxlen)
+{
+ Q_D(QFile);
+ if (!d->ensureFlushed())
+ return -1;
+
+ if (fileEngine()->supportsExtension(QAbstractFileEngine::FastReadLineExtension))
+ return fileEngine()->readLine(data, maxlen);
+
+ // Fall back to QIODevice's readLine implementation if the engine
+ // cannot do it faster.
+ return QIODevice::readLineData(data, maxlen);
+}
+
+/*!
+ \reimp
+*/
+
+qint64 QFile::readData(char *data, qint64 len)
+{
+ Q_D(QFile);
+ d->error = NoError;
+ if (!d->ensureFlushed())
+ return -1;
+
+ qint64 ret = -1;
+ qint64 read = fileEngine()->read(data, len);
+ if (read != -1)
+ ret = read;
+
+ if(ret < 0) {
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::ReadError;
+ d->setError(err, fileEngine()->errorString());
+ }
+ return ret;
+}
+
+/*!
+ \internal
+*/
+bool QFilePrivate::putCharHelper(char c)
+{
+#ifdef QT_NO_QOBJECT
+ return QIODevicePrivate::putCharHelper(c);
+#else
+
+ // Cutoff for code that doesn't only touch the buffer.
+ int writeBufferSize = writeBuffer.size();
+ if ((openMode & QIODevice::Unbuffered) || writeBufferSize + 1 >= QFILE_WRITEBUFFER_SIZE
+#ifdef Q_OS_WIN
+ || ((openMode & QIODevice::Text) && c == '\n' && writeBufferSize + 2 >= QFILE_WRITEBUFFER_SIZE)
+#endif
+ ) {
+ return QIODevicePrivate::putCharHelper(c);
+ }
+
+ if (!(openMode & QIODevice::WriteOnly)) {
+ if (openMode == QIODevice::NotOpen)
+ qWarning("QIODevice::putChar: Closed device");
+ else
+ qWarning("QIODevice::putChar: ReadOnly device");
+ return false;
+ }
+
+ // Make sure the device is positioned correctly.
+ const bool sequential = isSequential();
+ if (pos != devicePos && !sequential && !q_func()->seek(pos))
+ return false;
+
+ lastWasWrite = true;
+
+ int len = 1;
+#ifdef Q_OS_WIN
+ if ((openMode & QIODevice::Text) && c == '\n') {
+ ++len;
+ *writeBuffer.reserve(1) = '\r';
+ }
+#endif
+
+ // Write to buffer.
+ *writeBuffer.reserve(1) = c;
+
+ if (!sequential) {
+ pos += len;
+ devicePos += len;
+ if (!buffer.isEmpty())
+ buffer.skip(len);
+ }
+
+ return true;
+#endif
+}
+
+/*!
+ \reimp
+*/
+
+qint64
+QFile::writeData(const char *data, qint64 len)
+{
+ Q_D(QFile);
+ d->error = NoError;
+ d->lastWasWrite = true;
+ bool buffered = !(d->openMode & Unbuffered);
+
+ // Flush buffered data if this read will overflow.
+ if (buffered && (d->writeBuffer.size() + len) > QFILE_WRITEBUFFER_SIZE) {
+ if (!flush())
+ return -1;
+ }
+
+ // Write directly to the engine if the block size is larger than
+ // the write buffer size.
+ if (!buffered || len > QFILE_WRITEBUFFER_SIZE) {
+ QAbstractFileEngine *fe = d->fileEngine ? d->fileEngine : fileEngine();
+ qint64 ret = fe->write(data, len);
+ if(ret < 0) {
+ QFile::FileError err = fileEngine()->error();
+ if(err == QFile::UnspecifiedError)
+ err = QFile::WriteError;
+ d->setError(err, fileEngine()->errorString());
+ }
+ return ret;
+ }
+
+ // Write to the buffer.
+ char *writePointer = d->writeBuffer.reserve(len);
+ if (len == 1)
+ *writePointer = *data;
+ else
+ ::memcpy(writePointer, data, len);
+ return len;
+}
+
+/*!
+ \internal
+ Returns the QIOEngine for this QFile object.
+*/
+QAbstractFileEngine *QFile::fileEngine() const
+{
+ Q_D(const QFile);
+ if(!d->fileEngine)
+ d->fileEngine = QAbstractFileEngine::create(d->fileName);
+ return d->fileEngine;
+}
+
+/*!
+ Returns the file error status.
+
+ The I/O device status returns an error code. For example, if open()
+ returns false, or a read/write operation returns -1, this function can
+ be called to find out the reason why the operation failed.
+
+ \sa unsetError()
+*/
+
+QFile::FileError
+QFile::error() const
+{
+ Q_D(const QFile);
+ return d->error;
+}
+
+/*!
+ Sets the file's error to QFile::NoError.
+
+ \sa error()
+*/
+void
+QFile::unsetError()
+{
+ Q_D(QFile);
+ d->setError(QFile::NoError);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h
new file mode 100644
index 0000000..e4ca9cc
--- /dev/null
+++ b/src/corelib/io/qfile.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILE_H
+#define QFILE_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstring.h>
+#include <stdio.h>
+
+#ifdef open
+#error qfile.h must be included before any header file that defines open
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QAbstractFileEngine;
+class QFilePrivate;
+
+class Q_CORE_EXPORT QFile : public QIODevice
+{
+#ifndef QT_NO_QOBJECT
+ Q_OBJECT
+#endif
+ Q_DECLARE_PRIVATE(QFile)
+
+public:
+
+ enum FileError {
+ NoError = 0,
+ ReadError = 1,
+ WriteError = 2,
+ FatalError = 3,
+ ResourceError = 4,
+ OpenError = 5,
+ AbortError = 6,
+ TimeOutError = 7,
+ UnspecifiedError = 8,
+ RemoveError = 9,
+ RenameError = 10,
+ PositionError = 11,
+ ResizeError = 12,
+ PermissionsError = 13,
+ CopyError = 14
+#ifdef QT3_SUPPORT
+ , ConnectError = 30
+#endif
+ };
+
+ enum Permission {
+ ReadOwner = 0x4000, WriteOwner = 0x2000, ExeOwner = 0x1000,
+ ReadUser = 0x0400, WriteUser = 0x0200, ExeUser = 0x0100,
+ ReadGroup = 0x0040, WriteGroup = 0x0020, ExeGroup = 0x0010,
+ ReadOther = 0x0004, WriteOther = 0x0002, ExeOther = 0x0001
+ };
+ Q_DECLARE_FLAGS(Permissions, Permission)
+
+ QFile();
+ QFile(const QString &name);
+#ifndef QT_NO_QOBJECT
+ explicit QFile(QObject *parent);
+ QFile(const QString &name, QObject *parent);
+#endif
+ ~QFile();
+
+ FileError error() const;
+ void unsetError();
+
+ QString fileName() const;
+ void setFileName(const QString &name);
+
+ typedef QByteArray (*EncoderFn)(const QString &fileName);
+ typedef QString (*DecoderFn)(const QByteArray &localfileName);
+ static QByteArray encodeName(const QString &fileName);
+ static QString decodeName(const QByteArray &localFileName);
+ inline static QString decodeName(const char *localFileName)
+ { return decodeName(QByteArray(localFileName)); };
+ static void setEncodingFunction(EncoderFn);
+ static void setDecodingFunction(DecoderFn);
+
+ bool exists() const;
+ static bool exists(const QString &fileName);
+
+ QString readLink() const;
+ static QString readLink(const QString &fileName);
+ inline QString symLinkTarget() const { return readLink(); }
+ inline static QString symLinkTarget(const QString &fileName) { return readLink(fileName); }
+
+ bool remove();
+ static bool remove(const QString &fileName);
+
+ bool rename(const QString &newName);
+ static bool rename(const QString &oldName, const QString &newName);
+
+ bool link(const QString &newName);
+ static bool link(const QString &oldname, const QString &newName);
+
+ bool copy(const QString &newName);
+ static bool copy(const QString &fileName, const QString &newName);
+
+ bool isSequential() const;
+
+ bool open(OpenMode flags);
+ bool open(FILE *f, OpenMode flags);
+ bool open(int fd, OpenMode flags);
+ virtual void close();
+
+ qint64 size() const;
+ qint64 pos() const;
+ bool seek(qint64 offset);
+ bool atEnd() const;
+ bool flush();
+
+ bool resize(qint64 sz);
+ static bool resize(const QString &filename, qint64 sz);
+
+ Permissions permissions() const;
+ static Permissions permissions(const QString &filename);
+ bool setPermissions(Permissions permissionSpec);
+ static bool setPermissions(const QString &filename, Permissions permissionSpec);
+
+ int handle() const;
+
+ enum MemoryMapFlags {
+ NoOptions = 0
+ };
+
+ uchar *map(qint64 offset, qint64 size, MemoryMapFlags flags = NoOptions);
+ bool unmap(uchar *address);
+
+ virtual QAbstractFileEngine *fileEngine() const;
+
+#ifdef QT3_SUPPORT
+ typedef Permission PermissionSpec;
+ inline QT3_SUPPORT QString name() const { return fileName(); }
+ inline QT3_SUPPORT void setName(const QString &aName) { setFileName(aName); }
+ inline QT3_SUPPORT bool open(OpenMode aFlags, FILE *f) { return open(f, aFlags); }
+ inline QT3_SUPPORT bool open(OpenMode aFlags, int fd) { return open(fd, aFlags); }
+#endif
+
+protected:
+#ifdef QT_NO_QOBJECT
+ QFile(QFilePrivate &dd);
+#else
+ QFile(QFilePrivate &dd, QObject *parent = 0);
+#endif
+
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+ qint64 readLineData(char *data, qint64 maxlen);
+
+private:
+ Q_DISABLE_COPY(QFile)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFile::Permissions)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFILE_H
diff --git a/src/corelib/io/qfile_p.h b/src/corelib/io/qfile_p.h
new file mode 100644
index 0000000..ee6f56a
--- /dev/null
+++ b/src/corelib/io/qfile_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILE_P_H
+#define QFILE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qabstractfileengine.h"
+#include "private/qiodevice_p.h"
+#include "private/qringbuffer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFilePrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QFile)
+
+protected:
+ QFilePrivate();
+ ~QFilePrivate();
+
+ bool openExternalFile(int flags, int fd);
+ bool openExternalFile(int flags, FILE *fh);
+
+ QString fileName;
+ mutable QAbstractFileEngine *fileEngine;
+ bool isOpen;
+
+ bool lastWasWrite;
+ QRingBuffer writeBuffer;
+ inline bool ensureFlushed() const;
+
+ bool putCharHelper(char c);
+
+ QFile::FileError error;
+ void setError(QFile::FileError err);
+ void setError(QFile::FileError err, const QString &errorString);
+ void setError(QFile::FileError err, int errNum);
+
+private:
+ static QFile::EncoderFn encoder;
+ static QFile::DecoderFn decoder;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFILE_P_H
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
new file mode 100644
index 0000000..a062317
--- /dev/null
+++ b/src/corelib/io/qfileinfo.cpp
@@ -0,0 +1,1427 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qfileinfo.h"
+#include "qdatetime.h"
+#include "qabstractfileengine.h"
+#include "qfsfileengine_p.h"
+#include "qglobal.h"
+#include "qatomic.h"
+#include "qhash.h"
+#include "qdir.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFileInfoPrivate
+{
+public:
+ QFileInfoPrivate(const QFileInfo *copy=0);
+ ~QFileInfoPrivate();
+
+ void initFileEngine(const QString &);
+
+ enum Access {
+ ReadAccess,
+ WriteAccess,
+ ExecuteAccess
+ };
+ bool hasAccess(Access access) const;
+
+ uint getFileFlags(QAbstractFileEngine::FileFlags) const;
+ QDateTime &getFileTime(QAbstractFileEngine::FileTime) const;
+ QString getFileName(QAbstractFileEngine::FileName) const;
+
+ enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04,
+ CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40,
+ CachedSize =0x08 };
+ struct Data {
+ inline Data()
+ : ref(1), fileEngine(0), cache_enabled(1)
+ { clear(); }
+ inline Data(const Data &copy)
+ : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)),
+ fileName(copy.fileName), cache_enabled(copy.cache_enabled)
+ { clear(); }
+ inline ~Data() { delete fileEngine; }
+ inline void clearFlags() {
+ fileFlags = 0;
+ cachedFlags = 0;
+ if (fileEngine)
+ (void)fileEngine->fileFlags(QFSFileEngine::Refresh);
+ }
+ inline void clear() {
+ fileNames.clear();
+ clearFlags();
+ }
+ mutable QAtomicInt ref;
+
+ QAbstractFileEngine *fileEngine;
+ mutable QString fileName;
+ mutable QHash<int, QString> fileNames;
+
+ mutable uint cachedFlags : 31;
+ mutable uint cache_enabled : 1;
+ mutable uint fileFlags;
+ mutable qint64 fileSize;
+ mutable QDateTime fileTimes[3];
+ inline bool getCachedFlag(uint c) const
+ { return cache_enabled ? (cachedFlags & c) : 0; }
+ inline void setCachedFlag(uint c)
+ { if (cache_enabled) cachedFlags |= c; }
+ } *data;
+ inline void reset() {
+ detach();
+ data->clear();
+ }
+ void detach();
+};
+
+QFileInfoPrivate::QFileInfoPrivate(const QFileInfo *copy)
+{
+ if(copy) {
+ copy->d_func()->data->ref.ref();
+ data = copy->d_func()->data;
+ } else {
+ data = new QFileInfoPrivate::Data;
+ data->clear();
+ }
+}
+
+QFileInfoPrivate::~QFileInfoPrivate()
+{
+ if (!data->ref.deref())
+ delete data;
+ data = 0;
+}
+
+void QFileInfoPrivate::initFileEngine(const QString &file)
+{
+ detach();
+ delete data->fileEngine;
+ data->fileEngine = 0;
+ data->clear();
+ data->fileEngine = QAbstractFileEngine::create(file);
+ data->fileName = file;
+}
+
+bool QFileInfoPrivate::hasAccess(Access access) const
+{
+ if (!(data->fileEngine->fileFlags() & QAbstractFileEngine::LocalDiskFlag)) {
+ switch (access) {
+ case ReadAccess:
+ return getFileFlags(QAbstractFileEngine::ReadUserPerm);
+ case WriteAccess:
+ return getFileFlags(QAbstractFileEngine::WriteUserPerm);
+ case ExecuteAccess:
+ return getFileFlags(QAbstractFileEngine::ExeUserPerm);
+ default:
+ return false;
+ }
+ }
+
+ int mode = 0;
+ switch (access) {
+ case ReadAccess:
+ mode = R_OK;
+ break;
+ case WriteAccess:
+ mode = W_OK;
+ break;
+ case ExecuteAccess:
+ mode = X_OK;
+ break;
+ };
+#ifdef Q_OS_UNIX
+ return QT_ACCESS(QFile::encodeName(data->fileName).data(), mode) == 0;
+#endif
+#ifdef Q_OS_WIN
+ if ((access == ReadAccess && !getFileFlags(QAbstractFileEngine::ReadUserPerm))
+ || (access == WriteAccess && !getFileFlags(QAbstractFileEngine::WriteUserPerm))) {
+ return false;
+ }
+ if (access == ExecuteAccess)
+ return getFileFlags(QAbstractFileEngine::ExeUserPerm);
+
+ QT_WA( {
+ return ::_waccess((TCHAR *)QFSFileEnginePrivate::longFileName(data->fileName).utf16(), mode) == 0;
+ } , {
+ return QT_ACCESS(QFSFileEnginePrivate::win95Name(data->fileName), mode) == 0;
+ } );
+#endif
+ return false;
+}
+
+void QFileInfoPrivate::detach()
+{
+ qAtomicDetach(data);
+}
+
+QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const
+{
+ if(data->cache_enabled && data->fileNames.contains((int)name))
+ return data->fileNames.value(name);
+ QString ret = data->fileEngine->fileName(name);
+ if(data->cache_enabled)
+ data->fileNames.insert((int)name, ret);
+ return ret;
+}
+
+uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) const
+{
+ // We split the testing into tests for for LinkType, BundleType and the rest.
+ // In order to determine if a file is a symlink or not, we have to lstat().
+ // If we're not interested in that information, we might as well avoid one
+ // extra syscall. Bundle detecton on Mac can be slow, expecially on network
+ // paths, so we separate out that as well.
+
+ QAbstractFileEngine::FileFlags flags;
+ if (!data->getCachedFlag(CachedFileFlags)) {
+ QAbstractFileEngine::FileFlags req = QAbstractFileEngine::FileInfoAll;
+ req &= (~QAbstractFileEngine::LinkType);
+ req &= (~QAbstractFileEngine::BundleType);
+
+ flags = data->fileEngine->fileFlags(req);
+ data->setCachedFlag(CachedFileFlags);
+ data->fileFlags |= uint(flags);
+ } else {
+ flags = QAbstractFileEngine::FileFlags(data->fileFlags & request);
+ }
+
+ if (request & QAbstractFileEngine::LinkType) {
+ if (!data->getCachedFlag(CachedLinkTypeFlag)) {
+ QAbstractFileEngine::FileFlags linkflag;
+ linkflag = data->fileEngine->fileFlags(QAbstractFileEngine::LinkType);
+
+ data->setCachedFlag(CachedLinkTypeFlag);
+ data->fileFlags |= uint(linkflag);
+ flags |= linkflag;
+ }
+ }
+
+ if (request & QAbstractFileEngine::BundleType) {
+ if (!data->getCachedFlag(CachedBundleTypeFlag)) {
+ QAbstractFileEngine::FileFlags bundleflag;
+ bundleflag = data->fileEngine->fileFlags(QAbstractFileEngine::BundleType);
+
+ data->setCachedFlag(CachedBundleTypeFlag);
+ data->fileFlags |= uint(bundleflag);
+ flags |= bundleflag;
+ }
+ }
+
+ // no else branch
+ // if we had it cached, it was caught in the previous else branch
+
+ return flags & request;
+}
+
+QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) const
+{
+ if (!data->cache_enabled)
+ data->clearFlags();
+ if(request == QAbstractFileEngine::CreationTime) {
+ if(data->getCachedFlag(CachedCTime))
+ return data->fileTimes[request];
+ data->setCachedFlag(CachedCTime);
+ return (data->fileTimes[request] = data->fileEngine->fileTime(request));
+ }
+ if(request == QAbstractFileEngine::ModificationTime) {
+ if(data->getCachedFlag(CachedMTime))
+ return data->fileTimes[request];
+ data->setCachedFlag(CachedMTime);
+ return (data->fileTimes[request] = data->fileEngine->fileTime(request));
+ }
+ if(request == QAbstractFileEngine::AccessTime) {
+ if(data->getCachedFlag(CachedATime))
+ return data->fileTimes[request];
+ data->setCachedFlag(CachedATime);
+ return (data->fileTimes[request] = data->fileEngine->fileTime(request));
+ }
+ return data->fileTimes[0]; //cannot really happen
+}
+
+//************* QFileInfo
+
+/*!
+ \class QFileInfo
+ \reentrant
+ \brief The QFileInfo class provides system-independent file information.
+
+ \ingroup io
+ \ingroup shared
+
+ QFileInfo provides information about a file's name and position
+ (path) in the file system, its access rights and whether it is a
+ directory or symbolic link, etc. The file's size and last
+ modified/read times are also available. QFileInfo can also be
+ used to obtain information about a Qt \l{resource
+ system}{resource}.
+
+ A QFileInfo can point to a file with either a relative or an
+ absolute file path. Absolute file paths begin with the directory
+ separator "/" (or with a drive specification on Windows). Relative
+ file names begin with a directory name or a file name and specify
+ a path relative to the current working directory. An example of an
+ absolute path is the string "/tmp/quartz". A relative path might
+ look like "src/fatlib". You can use the function isRelative() to
+ check whether a QFileInfo is using a relative or an absolute file
+ path. You can call the function makeAbsolute() to convert a
+ relative QFileInfo's path to an absolute path.
+
+ The file that the QFileInfo works on is set in the constructor or
+ later with setFile(). Use exists() to see if the file exists and
+ size() to get its size.
+
+ The file's type is obtained with isFile(), isDir() and
+ isSymLink(). The symLinkTarget() function provides the name of the file
+ the symlink points to.
+
+ On Unix (including Mac OS X), the symlink has the same size() has
+ the file it points to, because Unix handles symlinks
+ transparently; similarly, opening a symlink using QFile
+ effectively opens the link's target. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 0
+
+ On Windows, symlinks (shortcuts) are \c .lnk files. The reported
+ size() is that of the symlink (not the link's target), and
+ opening a symlink using QFile opens the \c .lnk file. For
+ example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 1
+
+ Elements of the file's name can be extracted with path() and
+ fileName(). The fileName()'s parts can be extracted with
+ baseName(), suffix() or completeSuffix(). QFileInfo objects to
+ directories created by Qt classes will not have a trailing file
+ separator. If you wish to use trailing separators in your own file
+ info objects, just append one to the file name given to the constructors
+ or setFile().
+
+ The file's dates are returned by created(), lastModified() and
+ lastRead(). Information about the file's access permissions is
+ obtained with isReadable(), isWritable() and isExecutable(). The
+ file's ownership is available from owner(), ownerId(), group() and
+ groupId(). You can examine a file's permissions and ownership in a
+ single statement using the permission() function.
+
+ \section1 Performance Issues
+
+ Some of QFileInfo's functions query the file system, but for
+ performance reasons, some functions only operate on the
+ file name itself. For example: To return the absolute path of
+ a relative file name, absolutePath() has to query the file system.
+ The path() function, however, can work on the file name directly,
+ and so it is faster.
+
+ \note To speed up performance, QFileInfo caches information about
+ the file.
+
+ To speed up performance, QFileInfo caches information about the
+ file. Because files can be changed by other users or programs, or
+ even by other parts of the same program, there is a function that
+ refreshes the file information: refresh(). If you want to switch
+ off a QFileInfo's caching and force it to access the file system
+ every time you request information from it call setCaching(false).
+
+ \sa QDir, QFile
+*/
+
+/*!
+ Constructs an empty QFileInfo object.
+
+ Note that an empty QFileInfo object contain no file reference.
+
+ \sa setFile()
+*/
+
+QFileInfo::QFileInfo() : d_ptr(new QFileInfoPrivate())
+{
+}
+
+/*!
+ Constructs a new QFileInfo that gives information about the given
+ file. The \a file can also include an absolute or relative path.
+
+ \sa setFile(), isRelative(), QDir::setCurrent(), QDir::isRelativePath()
+*/
+
+QFileInfo::QFileInfo(const QString &file) : d_ptr(new QFileInfoPrivate())
+{
+ d_ptr->initFileEngine(file);
+}
+
+/*!
+ Constructs a new QFileInfo that gives information about file \a
+ file.
+
+ If the \a file has a relative path, the QFileInfo will also have a
+ relative path.
+
+ \sa isRelative()
+*/
+
+QFileInfo::QFileInfo(const QFile &file) : d_ptr(new QFileInfoPrivate())
+{
+ d_ptr->initFileEngine(file.fileName());
+}
+
+/*!
+ Constructs a new QFileInfo that gives information about the given
+ \a file in the directory \a dir.
+
+ If \a dir has a relative path, the QFileInfo will also have a
+ relative path.
+
+ If \a file is an absolute path, then the directory specified
+ by \a dir will be disregarded.
+
+ \sa isRelative()
+*/
+
+QFileInfo::QFileInfo(const QDir &dir, const QString &file) : d_ptr(new QFileInfoPrivate())
+{
+ d_ptr->initFileEngine(dir.filePath(file));
+}
+
+/*!
+ Constructs a new QFileInfo that is a copy of the given \a fileinfo.
+*/
+
+QFileInfo::QFileInfo(const QFileInfo &fileinfo) : d_ptr(new QFileInfoPrivate(&fileinfo))
+{
+
+}
+
+/*!
+ Destroys the QFileInfo and frees its resources.
+*/
+
+
+QFileInfo::~QFileInfo()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+
+/*!
+ \fn bool QFileInfo::operator!=(const QFileInfo &fileinfo)
+
+ Returns true if this QFileInfo object refers to a different file
+ than the one specified by \a fileinfo; otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ \overload
+ \fn bool QFileInfo::operator!=(const QFileInfo &fileinfo) const
+*/
+
+/*!
+ \overload
+*/
+bool QFileInfo::operator==(const QFileInfo &fileinfo) const
+{
+ Q_D(const QFileInfo);
+ // ### Qt 5: understand long and short file names on Windows
+ // ### (GetFullPathName()).
+ if(fileinfo.d_func()->data == d->data)
+ return true;
+ if(!d->data->fileEngine || !fileinfo.d_func()->data->fileEngine)
+ return false;
+ if(d->data->fileEngine->caseSensitive() != fileinfo.d_func()->data->fileEngine->caseSensitive())
+ return false;
+ if(fileinfo.size() == size()) { //if the size isn't the same...
+ QString file1 = canonicalFilePath(),
+ file2 = fileinfo.canonicalFilePath();
+ if(file1.length() == file2.length()) {
+ if(!fileinfo.d_func()->data->fileEngine->caseSensitive()) {
+ for(int i = 0; i < file1.length(); i++) {
+ if(file1.at(i).toLower() != file2.at(i).toLower())
+ return false;
+ }
+ return true;
+ }
+ return (file1 == file2);
+ }
+ }
+ return false;
+}
+
+/*!
+ Returns true if this QFileInfo object refers to a file in the same
+ location as \a fileinfo; otherwise returns false.
+
+ Note that the result of comparing two empty QFileInfo objects,
+ containing no file references, is undefined.
+
+ \warning This will not compare two different symbolic links
+ pointing to the same file.
+
+ \warning Long and short file names that refer to the same file on Windows
+ are treated as if they referred to different files.
+
+ \sa operator!=()
+*/
+bool QFileInfo::operator==(const QFileInfo &fileinfo)
+{
+ return const_cast<const QFileInfo *>(this)->operator==(fileinfo);
+}
+
+/*!
+ Makes a copy of the given \a fileinfo and assigns it to this QFileInfo.
+*/
+
+QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo)
+{
+ Q_D(QFileInfo);
+ qAtomicAssign(d->data, fileinfo.d_func()->data);
+ return *this;
+}
+
+/*!
+ Sets the file that the QFileInfo provides information about to \a
+ file.
+
+ The \a file can also include an absolute or relative file path.
+ Absolute paths begin with the directory separator (e.g. "/" under
+ Unix) or a drive specification (under Windows). Relative file
+ names begin with a directory name or a file name and specify a
+ path relative to the current directory.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 2
+
+ \sa isRelative(), QDir::setCurrent(), QDir::isRelativePath()
+*/
+
+void QFileInfo::setFile(const QString &file)
+{
+ Q_D(QFileInfo);
+ d->initFileEngine(file);
+}
+
+/*!
+ \overload
+
+ Sets the file that the QFileInfo provides information about to \a
+ file.
+
+ If \a file includes a relative path, the QFileInfo will also have
+ a relative path.
+
+ \sa isRelative()
+*/
+
+void QFileInfo::setFile(const QFile &file)
+{
+ Q_D(QFileInfo);
+ d->initFileEngine(file.fileName());
+}
+
+/*!
+ \overload
+
+ Sets the file that the QFileInfo provides information about to \a
+ file in directory \a dir.
+
+ If \a file includes a relative path, the QFileInfo will also
+ have a relative path.
+
+ \sa isRelative()
+*/
+
+void QFileInfo::setFile(const QDir &dir, const QString &file)
+{
+ Q_D(QFileInfo);
+ d->initFileEngine(dir.filePath(file));
+}
+
+/*!
+ Returns an absolute path including the file name.
+
+ The absolute path name consists of the full path and the file
+ name. On Unix this will always begin with the root, '/',
+ directory. On Windows this will always begin 'D:/' where D is a
+ drive letter, except for network shares that are not mapped to a
+ drive letter, in which case the path will begin '//sharename/'.
+ QFileInfo will uppercase drive letters. Note that QDir does not do
+ this. The code snippet below shows this.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp newstuff
+
+ This function returns the same as filePath(), unless isRelative()
+ is true. In contrast to canonicalFilePath(), symbolic links or
+ redundant "." or ".." elements are not necessarily removed.
+
+ If the QFileInfo is empty it returns QDir::currentPath().
+
+ \sa filePath(), canonicalFilePath(), isRelative()
+*/
+QString QFileInfo::absoluteFilePath() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::AbsoluteName);
+}
+
+/*!
+ Returns the canonical path including the file name, i.e. an absolute
+ path without symbolic links or redundant "." or ".." elements.
+
+ If the file does not exist, canonicalFilePath() returns an empty
+ string.
+
+ \sa filePath(), absoluteFilePath(), dir()
+*/
+
+QString QFileInfo::canonicalFilePath() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::CanonicalName);
+}
+
+
+/*!
+ Returns a file's path absolute path. This doesn't include the
+ file name.
+
+ On Unix the absolute path will always begin with the root, '/',
+ directory. On Windows this will always begin 'D:/' where D is a
+ drive letter, except for network shares that are not mapped to a
+ drive letter, in which case the path will begin '//sharename/'.
+
+ In contrast to canonicalPath() symbolic links or redundant "." or
+ ".." elements are not necessarily removed.
+
+ \warning If the QFileInfo object was created with an empty QString,
+ the behavior of this function is undefined.
+
+ \sa absoluteFilePath(), path(), canonicalPath(), fileName(), isRelative()
+*/
+
+QString QFileInfo::absolutePath() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::AbsolutePathName);
+}
+
+/*!
+ Returns the file's path canonical path (excluding the file name),
+ i.e. an absolute path without symbolic links or redundant "." or ".." elements.
+
+ If the file does not exist, canonicalPath() returns an empty string.
+
+ \sa path(), absolutePath()
+*/
+
+QString QFileInfo::canonicalPath() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::CanonicalPathName);
+}
+
+
+/*!
+ Returns the file's path. This doesn't include the file name.
+
+ Note that, if this QFileInfo object is given a path ending in a
+ slash, the name of the file is considered empty and this function
+ will return the entire path.
+
+ \sa filePath(), absolutePath(), canonicalPath(), dir(), fileName(), isRelative()
+*/
+
+QString QFileInfo::path() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::PathName);
+}
+
+/*!
+ \fn bool QFileInfo::isAbsolute() const
+
+ Returns true if the file path name is absolute, otherwise returns
+ false if the path is relative.
+
+ \sa isRelative()
+*/
+
+/*!
+ Returns true if the file path name is relative, otherwise returns
+ false if the path is absolute (e.g. under Unix a path is absolute
+ if it begins with a "/").
+
+ \sa isAbsolute()
+*/
+
+bool QFileInfo::isRelative() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return true;
+ return d->data->fileEngine->isRelativePath();
+}
+
+
+/*!
+ Converts the file's path to an absolute path if it is not already in that form.
+ Returns true to indicate that the path was converted; otherwise returns false
+ to indicate that the path was already absolute.
+
+ \sa filePath(), isRelative()
+*/
+
+bool QFileInfo::makeAbsolute()
+{
+ Q_D(QFileInfo);
+ if(!d->data->fileEngine || !d->data->fileEngine->isRelativePath())
+ return false;
+ QString absFileName = d->getFileName(QAbstractFileEngine::AbsoluteName);
+ d->initFileEngine(absFileName);
+ return true;
+}
+
+/*!
+ Returns true if the file exists; otherwise returns false.
+
+ \note If the file is a symlink that points to a non existing
+ file, false is returned.
+*/
+
+bool QFileInfo::exists() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::ExistsFlag);
+}
+
+/*!
+ Refreshes the information about the file, i.e. reads in information
+ from the file system the next time a cached property is fetched.
+
+ \note On Windows CE, there might be a delay for the file system driver
+ to detect changes on the file.
+*/
+
+void QFileInfo::refresh()
+{
+ Q_D(QFileInfo);
+ d->reset();
+}
+
+/*!
+ Returns the file name, including the path (which may be absolute
+ or relative).
+
+ \sa absoluteFilePath(), canonicalFilePath(), isRelative()
+*/
+
+QString QFileInfo::filePath() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::DefaultName);
+}
+
+/*!
+ Returns the name of the file, excluding the path.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 3
+
+ Note that, if this QFileInfo object is given a path ending in a
+ slash, the name of the file is considered empty.
+
+ \sa isRelative(), filePath(), baseName(), extension()
+*/
+
+QString QFileInfo::fileName() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::BaseName);
+}
+
+/*!
+ \since 4.3
+ Returns the name of the bundle.
+
+ On Mac OS X this returns the proper localized name for a bundle if the
+ path isBundle(). On all other platforms an empty QString is returned.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 4
+
+ \sa isBundle(), filePath(), baseName(), extension()
+*/
+
+QString QFileInfo::bundleName() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::BundleName);
+}
+
+/*!
+ Returns the base name of the file without the path.
+
+ The base name consists of all characters in the file up to (but
+ not including) the \e first '.' character.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 5
+
+
+ The base name of a file is computed equally on all platforms, independent
+ of file naming conventions (e.g., ".bashrc" on Unix has an empty base
+ name, and the suffix is "bashrc").
+
+ \sa fileName(), suffix(), completeSuffix(), completeBaseName()
+*/
+
+QString QFileInfo::baseName() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::BaseName).section(QLatin1Char('.'), 0, 0);
+}
+
+/*!
+ Returns the complete base name of the file without the path.
+
+ The complete base name consists of all characters in the file up
+ to (but not including) the \e last '.' character.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 6
+
+ \sa fileName(), suffix(), completeSuffix(), baseName()
+*/
+
+QString QFileInfo::completeBaseName() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ QString name = d->getFileName(QAbstractFileEngine::BaseName);
+ int index = name.lastIndexOf(QLatin1Char('.'));
+ return (index == -1) ? name : name.left(index);
+}
+
+/*!
+ Returns the complete suffix of the file.
+
+ The complete suffix consists of all characters in the file after
+ (but not including) the first '.'.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 7
+
+ \sa fileName(), suffix(), baseName(), completeBaseName()
+*/
+
+QString QFileInfo::completeSuffix() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ QString fileName = d->getFileName(QAbstractFileEngine::BaseName);
+ int firstDot = fileName.indexOf(QLatin1Char('.'));
+ if (firstDot == -1)
+ return QLatin1String("");
+ return fileName.mid(firstDot + 1);
+}
+
+/*!
+ Returns the suffix of the file.
+
+ The suffix consists of all characters in the file after (but not
+ including) the last '.'.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 8
+
+ The suffix of a file is computed equally on all platforms, independent of
+ file naming conventions (e.g., ".bashrc" on Unix has an empty base name,
+ and the suffix is "bashrc").
+
+ \sa fileName(), completeSuffix(), baseName(), completeBaseName()
+*/
+
+QString QFileInfo::suffix() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ QString fileName = d->getFileName(QAbstractFileEngine::BaseName);
+ int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
+ if (lastDot == -1)
+ return QLatin1String("");
+ return fileName.mid(lastDot + 1);
+}
+
+
+/*!
+ Returns the path of the object's parent directory as a QDir object.
+
+ \bold{Note:} The QDir returned always corresponds to the object's
+ parent directory, even if the QFileInfo represents a directory.
+
+ For each of the follwing, dir() returns a QDir for
+ \c{"~/examples/191697"}.
+
+ \snippet doc/src/snippets/fileinfo/main.cpp 0
+
+ For each of the follwing, dir() returns a QDir for
+ \c{"."}.
+
+ \snippet doc/src/snippets/fileinfo/main.cpp 1
+
+ \sa absolutePath(), filePath(), fileName(), isRelative(), absoluteDir()
+*/
+
+QDir QFileInfo::dir() const
+{
+ // ### Qt5: Maybe rename this to parentDirectory(), considering what it actually do?
+ return QDir(path());
+}
+
+/*!
+ Returns the file's absolute path as a QDir object.
+
+ \sa dir(), filePath(), fileName(), isRelative()
+*/
+
+QDir QFileInfo::absoluteDir() const
+{
+ return QDir(absolutePath());
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use absoluteDir() or the dir() overload that takes no parameters
+ instead.
+*/
+QDir QFileInfo::dir(bool absPath) const
+{
+ if(absPath)
+ return absoluteDir();
+ return dir();
+}
+#endif //QT3_SUPPORT
+
+/*!
+ Returns true if the user can read the file; otherwise returns false.
+
+ \sa isWritable(), isExecutable(), permission()
+*/
+
+bool QFileInfo::isReadable() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->hasAccess(QFileInfoPrivate::ReadAccess);
+}
+
+/*!
+ Returns true if the user can write to the file; otherwise returns false.
+
+ \sa isReadable(), isExecutable(), permission()
+*/
+
+bool QFileInfo::isWritable() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->hasAccess(QFileInfoPrivate::WriteAccess);
+}
+
+/*!
+ Returns true if the file is executable; otherwise returns false.
+
+ \sa isReadable(), isWritable(), permission()
+*/
+
+bool QFileInfo::isExecutable() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->hasAccess(QFileInfoPrivate::ExecuteAccess);
+}
+
+/*!
+ Returns true if this is a `hidden' file; otherwise returns false.
+
+ \bold{Note:} This function returns true for the special entries
+ "." and ".." on Unix, even though QDir::entryList threats them as shown.
+*/
+bool QFileInfo::isHidden() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::HiddenFlag);
+}
+
+/*!
+ Returns true if this object points to a file or to a symbolic
+ link to a file. Returns false if the
+ object points to something which isn't a file, such as a directory.
+
+ \sa isDir(), isSymLink(), isBundle()
+*/
+
+bool QFileInfo::isFile() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::FileType);
+}
+
+/*!
+ Returns true if this object points to a directory or to a symbolic
+ link to a directory; otherwise returns false.
+
+ \sa isFile(), isSymLink(), isBundle()
+*/
+
+bool QFileInfo::isDir() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::DirectoryType);
+}
+
+
+/*!
+ \since 4.3
+ Returns true if this object points to a bundle or to a symbolic
+ link to a bundle on Mac OS X; otherwise returns false.
+
+ \sa isDir(), isSymLink(), isFile()
+*/
+
+bool QFileInfo::isBundle() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::BundleType);
+}
+
+/*!
+ Returns true if this object points to a symbolic link (or to a
+ shortcut on Windows); otherwise returns false.
+
+ On Unix (including Mac OS X), opening a symlink effectively opens
+ the \l{symLinkTarget()}{link's target}. On Windows, it opens the \c
+ .lnk file itself.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 9
+
+ \note If the symlink points to a non existing file, exists() returns
+ false.
+
+ \sa isFile(), isDir(), symLinkTarget()
+*/
+
+bool QFileInfo::isSymLink() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::LinkType);
+}
+
+/*!
+ Returns true if the object points to a directory or to a symbolic
+ link to a directory, and that directory is the root directory; otherwise
+ returns false.
+*/
+
+bool QFileInfo::isRoot() const
+{
+ Q_D(const QFileInfo);
+ if (!d->data->fileEngine)
+ return true;
+ return d->getFileFlags(QAbstractFileEngine::RootFlag);
+}
+
+/*!
+ \fn QString QFileInfo::symLinkTarget() const
+ \since 4.2
+
+ Returns the absolute path to the file or directory a symlink (or shortcut
+ on Windows) points to, or a an empty string if the object isn't a symbolic
+ link.
+
+ This name may not represent an existing file; it is only a string.
+ QFileInfo::exists() returns true if the symlink points to an
+ existing file.
+
+ \sa exists(), isSymLink(), isDir(), isFile()
+*/
+
+/*!
+ \obsolete
+
+ Use symLinkTarget() instead.
+*/
+QString QFileInfo::readLink() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->getFileName(QAbstractFileEngine::LinkName);
+}
+
+/*!
+ Returns the owner of the file. On systems where files
+ do not have owners, or if an error occurs, an empty string is
+ returned.
+
+ This function can be time consuming under Unix (in the order of
+ milliseconds).
+
+ \sa ownerId(), group(), groupId()
+*/
+
+QString QFileInfo::owner() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->data->fileEngine->owner(QAbstractFileEngine::OwnerUser);
+}
+
+/*!
+ Returns the id of the owner of the file.
+
+ On Windows and on systems where files do not have owners this
+ function returns ((uint) -2).
+
+ \sa owner(), group(), groupId()
+*/
+
+uint QFileInfo::ownerId() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return 0;
+ return d->data->fileEngine->ownerId(QAbstractFileEngine::OwnerUser);
+}
+
+/*!
+ Returns the group of the file. On Windows, on systems where files
+ do not have groups, or if an error occurs, an empty string is
+ returned.
+
+ This function can be time consuming under Unix (in the order of
+ milliseconds).
+
+ \sa groupId(), owner(), ownerId()
+*/
+
+QString QFileInfo::group() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QLatin1String("");
+ return d->data->fileEngine->owner(QAbstractFileEngine::OwnerGroup);
+}
+
+/*!
+ Returns the id of the group the file belongs to.
+
+ On Windows and on systems where files do not have groups this
+ function always returns (uint) -2.
+
+ \sa group(), owner(), ownerId()
+*/
+
+uint QFileInfo::groupId() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return 0;
+ return d->data->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup);
+}
+
+/*!
+ Tests for file permissions. The \a permissions argument can be
+ several flags of type QFile::Permissions OR-ed together to check
+ for permission combinations.
+
+ On systems where files do not have permissions this function
+ always returns true.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qfileinfo.cpp 10
+
+ \sa isReadable(), isWritable(), isExecutable()
+*/
+
+bool QFileInfo::permission(QFile::Permissions permissions) const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return false;
+ return d->getFileFlags(QAbstractFileEngine::FileFlags((int)permissions)) == (uint)permissions;
+}
+
+/*!
+ Returns the complete OR-ed together combination of
+ QFile::Permissions for the file.
+*/
+
+QFile::Permissions QFileInfo::permissions() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return 0;
+ return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask);
+}
+
+
+/*!
+ Returns the file size in bytes. If the file does not exist or cannot be
+ fetched, 0 is returned.
+
+ \sa exists()
+*/
+
+qint64 QFileInfo::size() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return 0;
+ if(!d->data->getCachedFlag(QFileInfoPrivate::CachedSize)) {
+ d->data->setCachedFlag(QFileInfoPrivate::CachedSize);
+ d->data->fileSize = d->data->fileEngine->size();
+ }
+ return d->data->fileSize;
+}
+
+/*!
+ Returns the date and time when the file was created.
+
+ On most Unix systems, this function returns the time of the last
+ status change. A status change occurs when the file is created,
+ but it also occurs whenever the user writes or sets inode
+ information (for example, changing the file permissions).
+
+ If neither creation time nor "last status change" time are not
+ available, returns the same as lastModified().
+
+ \sa lastModified() lastRead()
+*/
+
+QDateTime QFileInfo::created() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QDateTime();
+ return d->getFileTime(QAbstractFileEngine::CreationTime);
+}
+
+/*!
+ Returns the date and time when the file was last modified.
+
+ \sa created() lastRead()
+*/
+
+QDateTime QFileInfo::lastModified() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QDateTime();
+ return d->getFileTime(QAbstractFileEngine::ModificationTime);
+}
+
+/*!
+ Returns the date and time when the file was last read (accessed).
+
+ On platforms where this information is not available, returns the
+ same as lastModified().
+
+ \sa created() lastModified()
+*/
+
+QDateTime QFileInfo::lastRead() const
+{
+ Q_D(const QFileInfo);
+ if(!d->data->fileEngine)
+ return QDateTime();
+ return d->getFileTime(QAbstractFileEngine::AccessTime);
+}
+
+/*! \internal
+ Detaches all internal data.
+*/
+
+void QFileInfo::detach()
+{
+ Q_D(QFileInfo);
+ d->detach();
+}
+
+/*!
+ Returns true if caching is enabled; otherwise returns false.
+
+ \sa setCaching(), refresh()
+*/
+
+bool QFileInfo::caching() const
+{
+ Q_D(const QFileInfo);
+ return d->data->cache_enabled;
+}
+
+/*!
+ If \a enable is true, enables caching of file information. If \a
+ enable is false caching is disabled.
+
+ When caching is enabled, QFileInfo reads the file information from
+ the file system the first time it's needed, but generally not
+ later.
+
+ Caching is enabled by default.
+
+ \sa refresh(), caching()
+*/
+
+void QFileInfo::setCaching(bool enable)
+{
+ Q_D(QFileInfo);
+ detach();
+ d->data->cache_enabled = enable;
+}
+
+/*!
+ \fn QString QFileInfo::baseName(bool complete)
+
+ Use completeBaseName() or the baseName() overload that takes no
+ parameters instead.
+*/
+
+/*!
+ \fn QString QFileInfo::extension(bool complete = true) const
+
+ Use completeSuffix() or suffix() instead.
+*/
+
+/*!
+ \fn QString QFileInfo::absFilePath() const
+
+ Use absoluteFilePath() instead.
+*/
+
+/*!
+ \fn QString QFileInfo::dirPath(bool absPath) const
+
+ Use absolutePath() if the absolute path is wanted (\a absPath
+ is true) or path() if it's not necessary (\a absPath is false).
+*/
+
+/*!
+ \fn bool QFileInfo::convertToAbs()
+
+ Use makeAbsolute() instead.
+*/
+
+/*!
+ \enum QFileInfo::Permission
+
+ \compat
+
+ \value ReadOwner
+ \value WriteOwner
+ \value ExeOwner
+ \value ReadUser
+ \value WriteUser
+ \value ExeUser
+ \value ReadGroup
+ \value WriteGroup
+ \value ExeGroup
+ \value ReadOther
+ \value WriteOther
+ \value ExeOther
+*/
+
+/*!
+ \fn bool QFileInfo::permission(PermissionSpec permissions) const
+ \compat
+
+ Use permission() instead.
+*/
+
+/*!
+ \typedef QFileInfoList
+ \relates QFileInfo
+
+ Synonym for QList<QFileInfo>.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qfileinfo.h b/src/corelib/io/qfileinfo.h
new file mode 100644
index 0000000..97997a3
--- /dev/null
+++ b/src/corelib/io/qfileinfo.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEINFO_H
+#define QFILEINFO_H
+
+#include <QtCore/qfile.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QDir;
+class QDateTime;
+class QFileInfoPrivate;
+
+class Q_CORE_EXPORT QFileInfo
+{
+public:
+ QFileInfo();
+ QFileInfo(const QString &file);
+ QFileInfo(const QFile &file);
+ QFileInfo(const QDir &dir, const QString &file);
+ QFileInfo(const QFileInfo &fileinfo);
+ ~QFileInfo();
+
+ QFileInfo &operator=(const QFileInfo &fileinfo);
+ bool operator==(const QFileInfo &fileinfo); // 5.0 - remove me
+ bool operator==(const QFileInfo &fileinfo) const;
+ inline bool operator!=(const QFileInfo &fileinfo) { return !(operator==(fileinfo)); } // 5.0 - remove me
+ inline bool operator!=(const QFileInfo &fileinfo) const { return !(operator==(fileinfo)); }
+
+ void setFile(const QString &file);
+ void setFile(const QFile &file);
+ void setFile(const QDir &dir, const QString &file);
+ bool exists() const;
+ void refresh();
+
+ QString filePath() const;
+ QString absoluteFilePath() const;
+ QString canonicalFilePath() const;
+ QString fileName() const;
+ QString baseName() const;
+ QString completeBaseName() const;
+ QString suffix() const;
+ QString bundleName() const;
+ QString completeSuffix() const;
+
+ QString path() const;
+ QString absolutePath() const;
+ QString canonicalPath() const;
+ QDir dir() const;
+ QDir absoluteDir() const;
+
+ bool isReadable() const;
+ bool isWritable() const;
+ bool isExecutable() const;
+ bool isHidden() const;
+
+ bool isRelative() const;
+ inline bool isAbsolute() const { return !isRelative(); }
+ bool makeAbsolute();
+
+ bool isFile() const;
+ bool isDir() const;
+ bool isSymLink() const;
+ bool isRoot() const;
+ bool isBundle() const;
+
+ QString readLink() const;
+ inline QString symLinkTarget() const { return readLink(); }
+
+ QString owner() const;
+ uint ownerId() const;
+ QString group() const;
+ uint groupId() const;
+
+ bool permission(QFile::Permissions permissions) const;
+ QFile::Permissions permissions() const;
+
+ qint64 size() const;
+
+ QDateTime created() const;
+ QDateTime lastModified() const;
+ QDateTime lastRead() const;
+
+ void detach();
+
+ bool caching() const;
+ void setCaching(bool on);
+
+#ifdef QT3_SUPPORT
+ enum Permission {
+ ReadOwner = QFile::ReadOwner, WriteOwner = QFile::WriteOwner, ExeOwner = QFile::ExeOwner,
+ ReadUser = QFile::ReadUser, WriteUser = QFile::WriteUser, ExeUser = QFile::ExeUser,
+ ReadGroup = QFile::ReadGroup, WriteGroup = QFile::WriteGroup, ExeGroup = QFile::ExeGroup,
+ ReadOther = QFile::ReadOther, WriteOther = QFile::WriteOther, ExeOther = QFile::ExeOther
+ };
+ Q_DECLARE_FLAGS(PermissionSpec, Permission)
+
+ inline QT3_SUPPORT QString baseName(bool complete) {
+ if(complete)
+ return completeBaseName();
+ return baseName();
+ }
+ inline QT3_SUPPORT QString extension(bool complete = true) const {
+ if(complete)
+ return completeSuffix();
+ return suffix();
+ }
+ inline QT3_SUPPORT QString absFilePath() const { return absoluteFilePath(); }
+
+ inline QT3_SUPPORT QString dirPath(bool absPath = false) const {
+ if(absPath)
+ return absolutePath();
+ return path();
+ }
+ QT3_SUPPORT QDir dir(bool absPath) const;
+ inline QT3_SUPPORT bool convertToAbs() { return makeAbsolute(); }
+#if !defined(Q_NO_TYPESAFE_FLAGS)
+ inline QT3_SUPPORT bool permission(PermissionSpec permissions) const
+ { return permission(QFile::Permissions(static_cast<int>(permissions))); }
+#endif
+#endif
+
+protected:
+ QFileInfoPrivate *d_ptr;
+private:
+ Q_DECLARE_PRIVATE(QFileInfo)
+};
+Q_DECLARE_TYPEINFO(QFileInfo, Q_MOVABLE_TYPE);
+
+#ifdef QT3_SUPPORT
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFileInfo::PermissionSpec)
+#endif
+
+typedef QList<QFileInfo> QFileInfoList;
+#ifdef QT3_SUPPORT
+typedef QList<QFileInfo>::Iterator QFileInfoListIterator;
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFILEINFO_H
diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h
new file mode 100644
index 0000000..7d66581
--- /dev/null
+++ b/src/corelib/io/qfileinfo_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILEINFO_P_H
+#define QFILEINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QIODevice. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfileinfo.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFileInfoPrivate
+{
+public:
+ QFileInfoPrivate(const QFileInfo *copy=0);
+ ~QFileInfoPrivate();
+
+ void initFileEngine(const QString &);
+
+ enum Access {
+ ReadAccess,
+ WriteAccess,
+ ExecuteAccess
+ };
+ bool hasAccess(Access access) const;
+
+ uint getFileFlags(QAbstractFileEngine::FileFlags) const;
+ QDateTime &getFileTime(QAbstractFileEngine::FileTime) const;
+ QString getFileName(QAbstractFileEngine::FileName) const;
+
+ enum {
+ CachedFileFlags = 0x01,
+ CachedLinkTypeFlag = 0x02,
+ CachedBundleTypeFlag= 0x04,
+ CachedMTime = 0x10,
+ CachedCTime = 0x20,
+ CachedATime = 0x40,
+ CachedSize = 0x08
+ };
+
+ struct Data
+ {
+ inline Data()
+ : ref(1), fileEngine(0), cache_enabled(1)
+ {
+ clear();
+ }
+
+ inline Data(const Data &copy)
+ : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)),
+ fileName(copy.fileName), cache_enabled(copy.cache_enabled)
+ {
+ clear();
+ }
+
+ inline ~Data()
+ {
+ delete fileEngine;
+ }
+
+ inline void clear()
+ {
+ fileNames.clear();
+ fileFlags = 0;
+ cachedFlags = 0;
+ }
+
+ mutable QAtomicInt ref;
+
+ QAbstractFileEngine *fileEngine;
+ mutable QString fileName;
+ mutable QHash<int, QString> fileNames;
+ mutable uint cachedFlags : 31;
+ mutable uint cache_enabled : 1;
+ mutable uint fileFlags;
+ mutable qint64 fileSize;
+ mutable QDateTime fileTimes[3];
+
+ inline bool getCachedFlag(uint c) const
+ { return cache_enabled ? (cachedFlags & c) : 0; }
+
+ inline void setCachedFlag(uint c)
+ { if (cache_enabled) cachedFlags |= c; }
+ } *data;
+
+ inline void reset() {
+ detach();
+ data->clear();
+ }
+
+ void detach();
+};
+
+
+QT_END_NAMESPACE
+#endif
+
diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp
new file mode 100644
index 0000000..e073a08
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher.cpp
@@ -0,0 +1,620 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfilesystemwatcher.h"
+#include "qfilesystemwatcher_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qdatetime.h>
+#include <qdebug.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qmutex.h>
+#include <qset.h>
+#include <qtimer.h>
+
+#if defined(Q_OS_WIN)
+# include "qfilesystemwatcher_win_p.h"
+#elif defined(Q_OS_LINUX)
+# include "qfilesystemwatcher_inotify_p.h"
+# include "qfilesystemwatcher_dnotify_p.h"
+#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
+# include "qfilesystemwatcher_kqueue_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+enum { PollingInterval = 1000 };
+
+class QPollingFileSystemWatcherEngine : public QFileSystemWatcherEngine
+{
+ Q_OBJECT
+
+ class FileInfo
+ {
+ uint ownerId;
+ uint groupId;
+ QFile::Permissions permissions;
+ QDateTime lastModified;
+ QStringList entries;
+
+ public:
+ FileInfo(const QFileInfo &fileInfo)
+ : ownerId(fileInfo.ownerId()),
+ groupId(fileInfo.groupId()),
+ permissions(fileInfo.permissions()),
+ lastModified(fileInfo.lastModified())
+ {
+ if (fileInfo.isDir()) {
+ entries = fileInfo.absoluteDir().entryList(QDir::AllEntries);
+ }
+ }
+ FileInfo &operator=(const QFileInfo &fileInfo)
+ {
+ *this = FileInfo(fileInfo);
+ return *this;
+ }
+
+ bool operator!=(const QFileInfo &fileInfo) const
+ {
+ if (fileInfo.isDir() && entries != fileInfo.absoluteDir().entryList(QDir::AllEntries))
+ return true;
+ return (ownerId != fileInfo.ownerId()
+ || groupId != fileInfo.groupId()
+ || permissions != fileInfo.permissions()
+ || lastModified != fileInfo.lastModified());
+ }
+ };
+
+ mutable QMutex mutex;
+ QHash<QString, FileInfo> files, directories;
+
+public:
+ QPollingFileSystemWatcherEngine();
+
+ void run();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ void stop();
+
+private slots:
+ void timeout();
+};
+
+QPollingFileSystemWatcherEngine::QPollingFileSystemWatcherEngine()
+{
+#ifndef QT_NO_THREAD
+ moveToThread(this);
+#endif
+}
+
+void QPollingFileSystemWatcherEngine::run()
+{
+ QTimer timer;
+ connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
+ timer.start(PollingInterval);
+ (void) exec();
+}
+
+QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories)
+{
+ QMutexLocker locker(&mutex);
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ QFileInfo fi(path);
+ if (!fi.exists())
+ continue;
+ if (fi.isDir()) {
+ if (!directories->contains(path))
+ directories->append(path);
+ if (!path.endsWith(QLatin1Char('/')))
+ fi = QFileInfo(path + QLatin1Char('/'));
+ this->directories.insert(path, fi);
+ } else {
+ if (!files->contains(path))
+ files->append(path);
+ this->files.insert(path, fi);
+ }
+ it.remove();
+ }
+ start();
+ return p;
+}
+
+QStringList QPollingFileSystemWatcherEngine::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();
+ if (this->directories.remove(path)) {
+ directories->removeAll(path);
+ it.remove();
+ } else if (this->files.remove(path)) {
+ files->removeAll(path);
+ it.remove();
+ }
+ }
+ if (this->files.isEmpty() && this->directories.isEmpty()) {
+ locker.unlock();
+ stop();
+ wait();
+ }
+ return p;
+}
+
+void QPollingFileSystemWatcherEngine::stop()
+{
+ QMetaObject::invokeMethod(this, "quit");
+}
+
+void QPollingFileSystemWatcherEngine::timeout()
+{
+ QMutexLocker locker(&mutex);
+ QMutableHashIterator<QString, FileInfo> fit(files);
+ while (fit.hasNext()) {
+ QHash<QString, FileInfo>::iterator x = fit.next();
+ QString path = x.key();
+ QFileInfo fi(path);
+ if (!fi.exists()) {
+ fit.remove();
+ emit fileChanged(path, true);
+ } else if (x.value() != fi) {
+ x.value() = fi;
+ emit fileChanged(path, false);
+ }
+ }
+ QMutableHashIterator<QString, FileInfo> dit(directories);
+ while (dit.hasNext()) {
+ QHash<QString, FileInfo>::iterator x = dit.next();
+ QString path = x.key();
+ QFileInfo fi(path);
+ if (!path.endsWith(QLatin1Char('/')))
+ fi = QFileInfo(path + QLatin1Char('/'));
+ if (!fi.exists()) {
+ dit.remove();
+ emit directoryChanged(path, true);
+ } else if (x.value() != fi) {
+ x.value() = fi;
+ emit directoryChanged(path, false);
+ }
+
+ }
+}
+
+
+
+
+QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine()
+{
+#if defined(Q_OS_WIN)
+ return new QWindowsFileSystemWatcherEngine;
+#elif defined(Q_OS_LINUX)
+ QFileSystemWatcherEngine *eng = QInotifyFileSystemWatcherEngine::create();
+ if(!eng)
+ eng = QDnotifyFileSystemWatcherEngine::create();
+ return eng;
+#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
+ return QKqueueFileSystemWatcherEngine::create();
+#else
+ return 0;
+#endif
+}
+
+QFileSystemWatcherPrivate::QFileSystemWatcherPrivate()
+ : native(0), poller(0), forced(0)
+{
+}
+
+void QFileSystemWatcherPrivate::init()
+{
+ Q_Q(QFileSystemWatcher);
+ native = createNativeEngine();
+ if (native) {
+ QObject::connect(native,
+ SIGNAL(fileChanged(QString,bool)),
+ q,
+ SLOT(_q_fileChanged(QString,bool)));
+ QObject::connect(native,
+ SIGNAL(directoryChanged(QString,bool)),
+ q,
+ SLOT(_q_directoryChanged(QString,bool)));
+ }
+}
+
+void QFileSystemWatcherPrivate::initForcedEngine(const QString &forceName)
+{
+ if(forced)
+ return;
+
+ Q_Q(QFileSystemWatcher);
+
+#if defined(Q_OS_LINUX)
+ if(forceName == QLatin1String("inotify")) {
+ forced = QInotifyFileSystemWatcherEngine::create();
+ } else if(forceName == QLatin1String("dnotify")) {
+ forced = QDnotifyFileSystemWatcherEngine::create();
+ }
+#else
+ Q_UNUSED(forceName);
+#endif
+
+ if(forced) {
+ QObject::connect(forced,
+ SIGNAL(fileChanged(QString,bool)),
+ q,
+ SLOT(_q_fileChanged(QString,bool)));
+ QObject::connect(forced,
+ SIGNAL(directoryChanged(QString,bool)),
+ q,
+ SLOT(_q_directoryChanged(QString,bool)));
+ }
+}
+
+void QFileSystemWatcherPrivate::initPollerEngine()
+{
+ if(poller)
+ return;
+
+ Q_Q(QFileSystemWatcher);
+ poller = new QPollingFileSystemWatcherEngine; // that was a mouthful
+ QObject::connect(poller,
+ SIGNAL(fileChanged(QString,bool)),
+ q,
+ SLOT(_q_fileChanged(QString,bool)));
+ QObject::connect(poller,
+ SIGNAL(directoryChanged(QString,bool)),
+ q,
+ SLOT(_q_directoryChanged(QString,bool)));
+}
+
+void QFileSystemWatcherPrivate::_q_fileChanged(const QString &path, bool removed)
+{
+ Q_Q(QFileSystemWatcher);
+ if (!files.contains(path)) {
+ // the path was removed after a change was detected, but before we delivered the signal
+ return;
+ }
+ if (removed)
+ files.removeAll(path);
+ emit q->fileChanged(path);
+}
+
+void QFileSystemWatcherPrivate::_q_directoryChanged(const QString &path, bool removed)
+{
+ Q_Q(QFileSystemWatcher);
+ if (!directories.contains(path)) {
+ // perhaps the path was removed after a change was detected, but before we delivered the signal
+ return;
+ }
+ if (removed)
+ directories.removeAll(path);
+ emit q->directoryChanged(path);
+}
+
+
+
+/*!
+ \class QFileSystemWatcher
+ \brief The QFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+ \ingroup io
+ \since 4.2
+ \reentrant
+
+ QFileSystemWatcher monitors the file system for changes to files
+ and directories by watching a list of specified paths.
+
+ Call addPath() to watch a particular file or directory. Multiple
+ paths can be added using the addPaths() function. Existing paths can
+ be removed by using the removePath() and removePaths() functions.
+
+ QFileSystemWatcher examines each path added to it. Files that have
+ been added to the QFileSystemWatcher can be accessed using the
+ files() function, and directories using the directories() function.
+
+ The fileChanged() signal is emitted when a file has been modified,
+ renamed or removed from disk. Similarly, the directoryChanged()
+ signal is emitted when a directory or its contents is modified or
+ removed. Note that QFileSystemWatcher stops monitoring files once
+ they have been renamed or removed from disk, and directories once
+ they have been removed from disk.
+
+ \note On systems running a Linux kernel without inotify support,
+ file systems that contain watched paths cannot be unmounted.
+
+ \note Windows CE does not support directory monitoring by
+ default as this depends on the file system driver installed.
+
+ \note The act of monitoring files and directories for
+ modifications consumes system resources. This implies there is a
+ limit to the number of files and directories your process can
+ monitor simultaneously. On Mac OS and all BSD variants, for
+ example, an open file descriptor is required for each monitored
+ file. The system limits the number of open file descriptors to 256
+ by default. This means that addPath() and addPaths() will fail if
+ your process tries to add more than 256 files or directories to
+ the file system monitor. Also note that your process may have
+ other file descriptors open in addition to the ones for files
+ being monitored, and these other open descriptors also count in
+ the total.
+
+ \sa QFile, QDir
+*/
+
+
+/*!
+ Constructs a new file system watcher object with the given \a parent.
+*/
+QFileSystemWatcher::QFileSystemWatcher(QObject *parent)
+ : QObject(*new QFileSystemWatcherPrivate, parent)
+{
+ d_func()->init();
+}
+
+/*!
+ Constructs a new file system watcher object with the given \a parent
+ which monitors the specified \a paths list.
+*/
+QFileSystemWatcher::QFileSystemWatcher(const QStringList &paths, QObject *parent)
+ : QObject(*new QFileSystemWatcherPrivate, parent)
+{
+ d_func()->init();
+ addPaths(paths);
+}
+
+/*!
+ Destroys the file system watcher.
+*/
+QFileSystemWatcher::~QFileSystemWatcher()
+{
+ Q_D(QFileSystemWatcher);
+ if (d->native) {
+ d->native->stop();
+ d->native->wait();
+ delete d->native;
+ d->native = 0;
+ }
+ if (d->poller) {
+ d->poller->stop();
+ d->poller->wait();
+ delete d->poller;
+ d->poller = 0;
+ }
+ if (d->forced) {
+ d->forced->stop();
+ d->forced->wait();
+ delete d->forced;
+ d->forced = 0;
+ }
+}
+
+/*!
+ Adds \a path to the file system watcher if \a path exists. The
+ path is not added if it does not exist, or if it is already being
+ monitored by the file system watcher.
+
+ If \a path specifies a directory, the directoryChanged() signal
+ will be emitted when \a path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when \a path is
+ modified, renamed or removed.
+
+ \note There is a system dependent limit to the number of files and
+ directories that can be monitored simultaneously. If this limit
+ has been reached, \a path will not be added to the file system
+ watcher, and a warning message will be printed to \e{stderr}.
+
+ \sa addPaths(), removePath()
+*/
+void QFileSystemWatcher::addPath(const QString &path)
+{
+ if (path.isEmpty()) {
+ qWarning("QFileSystemWatcher::addPath: path is empty");
+ return;
+ }
+ addPaths(QStringList(path));
+}
+
+/*!
+ Adds each path in \a paths to the file system watcher. Paths are
+ not added if they not exist, or if they are already being
+ monitored by the file system watcher.
+
+ If a path specifies a directory, the directoryChanged() signal
+ will be emitted when the path is modified or removed from disk;
+ otherwise the fileChanged() signal is emitted when the path is
+ modified, renamed, or removed.
+
+ \note There is a system dependent limit to the number of files and
+ directories that can be monitored simultaneously. If this limit
+ has been reached, the excess \a paths will not be added to the
+ file system watcher, and a warning message will be printed to
+ \e{stderr} for each path that could not be added.
+
+ \sa addPath(), removePaths()
+*/
+void QFileSystemWatcher::addPaths(const QStringList &paths)
+{
+ Q_D(QFileSystemWatcher);
+ if (paths.isEmpty()) {
+ qWarning("QFileSystemWatcher::addPaths: list is empty");
+ return;
+ }
+
+ QStringList p = paths;
+ QFileSystemWatcherEngine *engine = 0;
+
+ if(!objectName().startsWith(QLatin1String("_qt_autotest_force_engine_"))) {
+ // Normal runtime case - search intelligently for best engine
+ if(d->native) {
+ engine = d->native;
+ } else {
+ d_func()->initPollerEngine();
+ engine = d->poller;
+ }
+
+ } else {
+ // Autotest override case - use the explicitly selected engine only
+ QString forceName = objectName().mid(26);
+ if(forceName == QLatin1String("poller")) {
+ qDebug() << "QFileSystemWatcher: skipping native engine, using only polling engine";
+ d_func()->initPollerEngine();
+ engine = d->poller;
+ } else if(forceName == QLatin1String("native")) {
+ qDebug() << "QFileSystemWatcher: skipping polling engine, using only native engine";
+ engine = d->native;
+ } else {
+ qDebug() << "QFileSystemWatcher: skipping polling and native engine, using only explicit" << forceName << "engine";
+ d_func()->initForcedEngine(forceName);
+ engine = d->forced;
+ }
+ }
+
+ if(engine)
+ p = engine->addPaths(p, &d->files, &d->directories);
+
+ if (!p.isEmpty())
+ qWarning("QFileSystemWatcher: failed to add paths: %s",
+ qPrintable(p.join(QLatin1String(", "))));
+}
+
+/*!
+ Removes the specified \a path from the file system watcher.
+
+ \sa removePaths(), addPath()
+*/
+void QFileSystemWatcher::removePath(const QString &path)
+{
+ if (path.isEmpty()) {
+ qWarning("QFileSystemWatcher::removePath: path is empty");
+ return;
+ }
+ removePaths(QStringList(path));
+}
+
+/*!
+ Removes the specified \a paths from the file system watcher.
+
+ \sa removePath(), addPaths()
+*/
+void QFileSystemWatcher::removePaths(const QStringList &paths)
+{
+ if (paths.isEmpty()) {
+ qWarning("QFileSystemWatcher::removePaths: list is empty");
+ return;
+ }
+ Q_D(QFileSystemWatcher);
+ QStringList p = paths;
+ if (d->native)
+ p = d->native->removePaths(p, &d->files, &d->directories);
+ if (d->poller)
+ p = d->poller->removePaths(p, &d->files, &d->directories);
+ if (d->forced)
+ p = d->forced->removePaths(p, &d->files, &d->directories);
+}
+
+/*!
+ \fn void QFileSystemWatcher::fileChanged(const QString &path)
+
+ This signal is emitted when the file at the specified \a path is
+ modified, renamed or removed from disk.
+
+ \sa directoryChanged()
+*/
+
+/*!
+ \fn void QFileSystemWatcher::directoryChanged(const QString &path)
+
+ This signal is emitted when the directory at a specified \a path,
+ is modified (e.g., when a file is added, modified or deleted) or
+ removed from disk. Note that if there are several changes during a
+ short period of time, some of the changes might not emit this
+ signal. However, the last change in the sequence of changes will
+ always generate this signal.
+
+ \sa fileChanged()
+*/
+
+/*!
+ \fn QStringList QFileSystemWatcher::directories() const
+
+ Returns a list of paths to directories that are being watched.
+
+ \sa files()
+*/
+
+/*!
+ \fn QStringList QFileSystemWatcher::files() const
+
+ Returns a list of paths to files that are being watched.
+
+ \sa directories()
+*/
+
+QStringList QFileSystemWatcher::directories() const
+{
+ Q_D(const QFileSystemWatcher);
+ return d->directories;
+}
+
+QStringList QFileSystemWatcher::files() const
+{
+ Q_D(const QFileSystemWatcher);
+ return d->files;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qfilesystemwatcher.cpp"
+
+#include "qfilesystemwatcher.moc"
+
+#endif // QT_NO_FILESYSTEMWATCHER
+
diff --git a/src/corelib/io/qfilesystemwatcher.h b/src/corelib/io/qfilesystemwatcher.h
new file mode 100644
index 0000000..b9cea53
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMWATCHER_H
+#define QFILESYSTEMWATCHER_H
+
+#include <QtCore/qobject.h>
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QFileSystemWatcherPrivate;
+
+class Q_CORE_EXPORT QFileSystemWatcher : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QFileSystemWatcher)
+
+public:
+ QFileSystemWatcher(QObject *parent = 0);
+ QFileSystemWatcher(const QStringList &paths, QObject *parent = 0);
+ ~QFileSystemWatcher();
+
+ void addPath(const QString &file);
+ void addPaths(const QStringList &files);
+ void removePath(const QString &file);
+ void removePaths(const QStringList &files);
+
+ QStringList files() const;
+ QStringList directories() const;
+
+Q_SIGNALS:
+ void fileChanged(const QString &path);
+ void directoryChanged(const QString &path);
+
+private:
+ Q_PRIVATE_SLOT(d_func(), void _q_fileChanged(const QString &path, bool removed))
+ Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &path, bool removed))
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_FILESYSTEMWATCHER
+#endif // QFILESYSTEMWATCHER_H
diff --git a/src/corelib/io/qfilesystemwatcher_dnotify.cpp b/src/corelib/io/qfilesystemwatcher_dnotify.cpp
new file mode 100644
index 0000000..e87375a
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_dnotify.cpp
@@ -0,0 +1,461 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfilesystemwatcher.h"
+#include "qfilesystemwatcher_dnotify_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qsocketnotifier.h>
+#include <qcoreapplication.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <qwaitcondition.h>
+#include <qmutex.h>
+#include <dirent.h>
+#include <qdir.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#ifdef QT_LINUXBASE
+
+/* LSB doesn't standardize these */
+#define F_NOTIFY 1026
+#define DN_ACCESS 0x00000001
+#define DN_MODIFY 0x00000002
+#define DN_CREATE 0x00000004
+#define DN_DELETE 0x00000008
+#define DN_RENAME 0x00000010
+#define DN_ATTRIB 0x00000020
+#define DN_MULTISHOT 0x80000000
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static int qfswd_fileChanged_pipe[2];
+static void (*qfswd_old_sigio_handler)(int) = 0;
+static void (*qfswd_old_sigio_action)(int, siginfo_t *, void *) = 0;
+static void qfswd_sigio_monitor(int signum, siginfo_t *i, void *v)
+{
+ ::write(qfswd_fileChanged_pipe[1], &i->si_fd, sizeof(int));
+
+ if (qfswd_old_sigio_handler && qfswd_old_sigio_handler != SIG_IGN)
+ qfswd_old_sigio_handler(signum);
+ if (qfswd_old_sigio_action)
+ qfswd_old_sigio_action(signum, i, v);
+}
+
+class QDnotifySignalThread : public QThread
+{
+Q_OBJECT
+public:
+ QDnotifySignalThread();
+ virtual ~QDnotifySignalThread();
+
+ void startNotify();
+
+ virtual void run();
+
+signals:
+ void fdChanged(int);
+
+protected:
+ virtual bool event(QEvent *);
+
+private slots:
+ void readFromDnotify();
+
+private:
+ QMutex mutex;
+ QWaitCondition wait;
+ bool isExecing;
+};
+
+Q_GLOBAL_STATIC(QDnotifySignalThread, dnotifySignal);
+
+QDnotifySignalThread::QDnotifySignalThread()
+: isExecing(false)
+{
+ moveToThread(this);
+
+ ::pipe(qfswd_fileChanged_pipe);
+ ::fcntl(qfswd_fileChanged_pipe[0], F_SETFL,
+ ::fcntl(qfswd_fileChanged_pipe[0], F_GETFL) | O_NONBLOCK);
+
+ struct sigaction oldAction;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_sigaction = qfswd_sigio_monitor;
+ action.sa_flags = SA_SIGINFO;
+ ::sigaction(SIGIO, &action, &oldAction);
+ if (!(oldAction.sa_flags & SA_SIGINFO))
+ qfswd_old_sigio_handler = oldAction.sa_handler;
+ else
+ qfswd_old_sigio_action = oldAction.sa_sigaction;
+}
+
+QDnotifySignalThread::~QDnotifySignalThread()
+{
+ if(isRunning()) {
+ quit();
+ QThread::wait();
+ }
+}
+
+bool QDnotifySignalThread::event(QEvent *e)
+{
+ if(e->type() == QEvent::User) {
+ QMutexLocker locker(&mutex);
+ isExecing = true;
+ wait.wakeAll();
+ return true;
+ } else {
+ return QThread::event(e);
+ }
+}
+
+void QDnotifySignalThread::startNotify()
+{
+ // Note: All this fancy waiting for the thread to enter its event
+ // loop is to avoid nasty messages at app shutdown when the
+ // QDnotifySignalThread singleton is deleted
+ start();
+ mutex.lock();
+ while(!isExecing)
+ wait.wait(&mutex);
+ mutex.unlock();
+}
+
+void QDnotifySignalThread::run()
+{
+ QSocketNotifier sn(qfswd_fileChanged_pipe[0], QSocketNotifier::Read, this);
+ connect(&sn, SIGNAL(activated(int)), SLOT(readFromDnotify()));
+
+ QCoreApplication::instance()->postEvent(this, new QEvent(QEvent::User));
+ (void) exec();
+}
+
+void QDnotifySignalThread::readFromDnotify()
+{
+ int fd;
+ int readrv = ::read(qfswd_fileChanged_pipe[0], &fd,sizeof(int));
+ // Only expect EAGAIN or EINTR. Other errors are assumed to be impossible.
+ if(readrv != -1) {
+ Q_ASSERT(readrv == sizeof(int));
+ Q_UNUSED(readrv);
+
+ if(0 == fd)
+ quit();
+ else
+ emit fdChanged(fd);
+ }
+}
+
+QDnotifyFileSystemWatcherEngine::QDnotifyFileSystemWatcherEngine()
+{
+ QObject::connect(dnotifySignal(), SIGNAL(fdChanged(int)),
+ this, SLOT(refresh(int)), Qt::DirectConnection);
+}
+
+QDnotifyFileSystemWatcherEngine::~QDnotifyFileSystemWatcherEngine()
+{
+ QMutexLocker locker(&mutex);
+
+ for(QHash<int, Directory>::ConstIterator iter = fdToDirectory.constBegin();
+ iter != fdToDirectory.constEnd();
+ ++iter) {
+ ::close(iter->fd);
+ if(iter->parentFd)
+ ::close(iter->parentFd);
+ }
+}
+
+QDnotifyFileSystemWatcherEngine *QDnotifyFileSystemWatcherEngine::create()
+{
+ return new QDnotifyFileSystemWatcherEngine();
+}
+
+void QDnotifyFileSystemWatcherEngine::run()
+{
+ qFatal("QDnotifyFileSystemWatcherEngine thread should not be run");
+}
+
+QStringList QDnotifyFileSystemWatcherEngine::addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
+{
+ QMutexLocker locker(&mutex);
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+
+ while (it.hasNext()) {
+ QString path = it.next();
+
+ QFileInfo fi(path);
+
+ if(!fi.exists()) {
+ continue;
+ }
+
+ bool isDir = fi.isDir();
+
+ if (isDir && directories->contains(path)) {
+ continue; // Skip monitored directories
+ } else if(!isDir && files->contains(path)) {
+ continue; // Skip monitored files
+ }
+
+ if(!isDir)
+ path = fi.canonicalPath();
+
+ // Locate the directory entry (creating if needed)
+ int fd = pathToFD[path];
+
+ if(fd == 0) {
+
+ DIR *d = ::opendir(path.toUtf8().constData());
+ if(!d) continue; // Could not open directory
+ DIR *parent = 0;
+
+ QDir parentDir(path);
+ if(!parentDir.isRoot()) {
+ parentDir.cdUp();
+ parent = ::opendir(parentDir.path().toUtf8().constData());
+ if(!parent) {
+ ::closedir(d);
+ continue;
+ }
+ }
+
+ fd = ::dirfd(d);
+ int parentFd = parent?::dirfd(parent):0;
+
+ Q_ASSERT(fd);
+ if(::fcntl(fd, F_SETSIG, SIGIO) ||
+ ::fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_DELETE |
+ DN_RENAME | DN_ATTRIB | DN_MULTISHOT) ||
+ (parent && ::fcntl(parentFd, F_SETSIG, SIGIO)) ||
+ (parent && ::fcntl(parentFd, F_NOTIFY, DN_DELETE | DN_RENAME |
+ DN_MULTISHOT))) {
+
+ ::closedir(d);
+ if(parent) ::closedir(parent);
+
+ continue; // Could not set appropriate flags
+ }
+
+ Directory dir;
+ dir.path = path;
+ dir.fd = fd;
+ dir.parentFd = parentFd;
+
+ fdToDirectory.insert(fd, dir);
+ pathToFD.insert(path, fd);
+ if(parentFd)
+ parentToFD.insert(parentFd, fd);
+ }
+
+ Directory &directory = fdToDirectory[fd];
+
+ if(isDir) {
+ directory.isMonitored = true;
+ } else {
+ Directory::File file;
+ file.path = fi.filePath();
+ file.lastWrite = fi.lastModified();
+ directory.files.append(file);
+ pathToFD.insert(fi.filePath(), fd);
+ }
+
+ it.remove();
+
+ if(isDir) {
+ directories->append(path);
+ } else {
+ files->append(fi.filePath());
+ }
+ }
+
+ dnotifySignal()->startNotify();
+
+ return p;
+}
+
+QStringList QDnotifyFileSystemWatcherEngine::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();
+ int fd = pathToFD.take(path);
+
+ if(!fd)
+ continue;
+
+ Directory &directory = fdToDirectory[fd];
+ bool isDir = false;
+ if(directory.path == path) {
+ isDir = true;
+ directory.isMonitored = false;
+ } else {
+ for(int ii = 0; ii < directory.files.count(); ++ii) {
+ if(directory.files.at(ii).path == path) {
+ directory.files.removeAt(ii);
+ break;
+ }
+ }
+ }
+
+ if(!directory.isMonitored && directory.files.isEmpty()) {
+ // No longer needed
+ ::close(directory.fd);
+ pathToFD.remove(directory.path);
+ fdToDirectory.remove(fd);
+ }
+
+ if(isDir) {
+ directories->removeAll(path);
+ } else {
+ files->removeAll(path);
+ }
+
+ it.remove();
+ }
+
+ return p;
+}
+
+void QDnotifyFileSystemWatcherEngine::refresh(int fd)
+{
+ QMutexLocker locker(&mutex);
+
+ bool wasParent = false;
+ QHash<int, Directory>::Iterator iter = fdToDirectory.find(fd);
+ if(iter == fdToDirectory.end()) {
+ QHash<int, int>::Iterator pIter = parentToFD.find(fd);
+ if(pIter == parentToFD.end())
+ return;
+
+ iter = fdToDirectory.find(*pIter);
+ if (iter == fdToDirectory.end())
+ return;
+ wasParent = true;
+ }
+
+ Directory &directory = *iter;
+
+ if(!wasParent) {
+ for(int ii = 0; ii < directory.files.count(); ++ii) {
+ Directory::File &file = directory.files[ii];
+ if(file.updateInfo()) {
+ // Emit signal
+ QString filePath = file.path;
+ bool removed = !QFileInfo(filePath).exists();
+
+ if(removed) {
+ directory.files.removeAt(ii);
+ --ii;
+ }
+
+ emit fileChanged(filePath, removed);
+ }
+ }
+ }
+
+ if(directory.isMonitored) {
+ // Emit signal
+ bool removed = !QFileInfo(directory.path).exists();
+ QString path = directory.path;
+
+ if(removed)
+ directory.isMonitored = false;
+
+ emit directoryChanged(path, removed);
+ }
+
+ if(!directory.isMonitored && directory.files.isEmpty()) {
+ ::close(directory.fd);
+ if(directory.parentFd) {
+ ::close(directory.parentFd);
+ parentToFD.remove(directory.parentFd);
+ }
+ fdToDirectory.erase(iter);
+ }
+}
+
+void QDnotifyFileSystemWatcherEngine::stop()
+{
+}
+
+bool QDnotifyFileSystemWatcherEngine::Directory::File::updateInfo()
+{
+ QFileInfo fi(path);
+ QDateTime nLastWrite = fi.lastModified();
+ uint nOwnerId = fi.ownerId();
+ uint nGroupId = fi.groupId();
+ QFile::Permissions nPermissions = fi.permissions();
+
+ if(nLastWrite != lastWrite ||
+ nOwnerId != ownerId ||
+ nGroupId != groupId ||
+ nPermissions != permissions) {
+ ownerId = nOwnerId;
+ groupId = nGroupId;
+ permissions = nPermissions;
+ lastWrite = nLastWrite;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qfilesystemwatcher_dnotify.moc"
+
+#endif // QT_NO_FILESYSTEMWATCHER
diff --git a/src/corelib/io/qfilesystemwatcher_dnotify_p.h b/src/corelib/io/qfilesystemwatcher_dnotify_p.h
new file mode 100644
index 0000000..81a70ed
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_dnotify_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMWATCHER_DNOTIFY_P_H
+#define QFILESYSTEMWATCHER_DNOTIFY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfilesystemwatcher_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qmutex.h>
+#include <qhash.h>
+#include <qdatetime.h>
+#include <qfile.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDnotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine
+{
+ Q_OBJECT
+
+public:
+ virtual ~QDnotifyFileSystemWatcherEngine();
+
+ static QDnotifyFileSystemWatcherEngine *create();
+
+ void run();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ void stop();
+
+private Q_SLOTS:
+ void refresh(int);
+
+private:
+ struct Directory {
+ Directory() : fd(0), parentFd(0), isMonitored(false) {}
+ Directory(const Directory &o) : path(o.path),
+ fd(o.fd),
+ parentFd(o.parentFd),
+ isMonitored(o.isMonitored),
+ files(o.files) {}
+ QString path;
+ int fd;
+ int parentFd;
+ bool isMonitored;
+
+ struct File {
+ File() : ownerId(0u), groupId(0u), permissions(0u) { }
+ File(const File &o) : path(o.path),
+ ownerId(o.ownerId),
+ groupId(o.groupId),
+ permissions(o.permissions),
+ lastWrite(o.lastWrite) {}
+ QString path;
+
+ bool updateInfo();
+
+ uint ownerId;
+ uint groupId;
+ QFile::Permissions permissions;
+ QDateTime lastWrite;
+ };
+
+ QList<File> files;
+ };
+
+ QDnotifyFileSystemWatcherEngine();
+
+ QMutex mutex;
+ QHash<QString, int> pathToFD;
+ QHash<int, Directory> fdToDirectory;
+ QHash<int, int> parentToFD;
+};
+
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_FILESYSTEMWATCHER
+#endif // QFILESYSTEMWATCHER_DNOTIFY_P_H
diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp
new file mode 100644
index 0000000..4445e3c
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp
@@ -0,0 +1,376 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfilesystemwatcher.h"
+#include "qfilesystemwatcher_inotify_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qdebug.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qsocketnotifier.h>
+#include <qvarlengtharray.h>
+
+#include <sys/syscall.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#if defined(QT_NO_INOTIFY)
+#include <linux/types.h>
+
+#if defined(__i386__)
+# define __NR_inotify_init 291
+# define __NR_inotify_add_watch 292
+# define __NR_inotify_rm_watch 293
+#elif defined(__x86_64__)
+# define __NR_inotify_init 253
+# define __NR_inotify_add_watch 254
+# define __NR_inotify_rm_watch 255
+#elif defined(__powerpc__) || defined(__powerpc64__)
+# define __NR_inotify_init 275
+# define __NR_inotify_add_watch 276
+# define __NR_inotify_rm_watch 277
+#elif defined (__ia64__)
+# define __NR_inotify_init 1277
+# define __NR_inotify_add_watch 1278
+# define __NR_inotify_rm_watch 1279
+#elif defined (__s390__) || defined (__s390x__)
+# define __NR_inotify_init 284
+# define __NR_inotify_add_watch 285
+# define __NR_inotify_rm_watch 286
+#elif defined (__alpha__)
+# define __NR_inotify_init 444
+# define __NR_inotify_add_watch 445
+# define __NR_inotify_rm_watch 446
+#elif defined (__sparc__) || defined (__sparc64__)
+# define __NR_inotify_init 151
+# define __NR_inotify_add_watch 152
+# define __NR_inotify_rm_watch 156
+#elif defined (__arm__)
+# define __NR_inotify_init 316
+# define __NR_inotify_add_watch 317
+# define __NR_inotify_rm_watch 318
+#elif defined (__sh__)
+# define __NR_inotify_init 290
+# define __NR_inotify_add_watch 291
+# define __NR_inotify_rm_watch 292
+#elif defined (__sh64__)
+# define __NR_inotify_init 318
+# define __NR_inotify_add_watch 319
+# define __NR_inotify_rm_watch 320
+#elif defined (__mips__)
+# define __NR_inotify_init 284
+# define __NR_inotify_add_watch 285
+# define __NR_inotify_rm_watch 286
+#elif defined (__hppa__)
+# define __NR_inotify_init 269
+# define __NR_inotify_add_watch 270
+# define __NR_inotify_rm_watch 271
+#elif defined (__avr32__)
+# define __NR_inotify_init 240
+# define __NR_inotify_add_watch 241
+# define __NR_inotify_rm_watch 242
+#elif defined (__mc68000__)
+# define __NR_inotify_init 284
+# define __NR_inotify_add_watch 285
+# define __NR_inotify_rm_watch 286
+#else
+# error "This architecture is not supported. Please talk to qt-bugs@trolltech.com"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_LINUXBASE
+// ### the LSB doesn't standardize syscall, need to wait until glib2.4 is standardized
+static inline int syscall(...) { return -1; }
+#endif
+
+static inline int inotify_init()
+{
+ return syscall(__NR_inotify_init);
+}
+
+static inline int inotify_add_watch(int fd, const char *name, __u32 mask)
+{
+ return syscall(__NR_inotify_add_watch, fd, name, mask);
+}
+
+static inline int inotify_rm_watch(int fd, __u32 wd)
+{
+ return syscall(__NR_inotify_rm_watch, fd, wd);
+}
+
+// the following struct and values are documented in linux/inotify.h
+extern "C" {
+
+struct inotify_event {
+ __s32 wd;
+ __u32 mask;
+ __u32 cookie;
+ __u32 len;
+ char name[0];
+};
+
+#define IN_ACCESS 0x00000001
+#define IN_MODIFY 0x00000002
+#define IN_ATTRIB 0x00000004
+#define IN_CLOSE_WRITE 0x00000008
+#define IN_CLOSE_NOWRITE 0x00000010
+#define IN_OPEN 0x00000020
+#define IN_MOVED_FROM 0x00000040
+#define IN_MOVED_TO 0x00000080
+#define IN_CREATE 0x00000100
+#define IN_DELETE 0x00000200
+#define IN_DELETE_SELF 0x00000400
+#define IN_MOVE_SELF 0x00000800
+#define IN_UNMOUNT 0x00002000
+#define IN_Q_OVERFLOW 0x00004000
+#define IN_IGNORED 0x00008000
+
+#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
+#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
+}
+
+QT_END_NAMESPACE
+
+// --------- inotify.h end ----------
+
+#else /* QT_NO_INOTIFY */
+
+#include <sys/inotify.h>
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QInotifyFileSystemWatcherEngine *QInotifyFileSystemWatcherEngine::create()
+{
+ int fd = inotify_init();
+ if (fd <= 0)
+ return 0;
+ return new QInotifyFileSystemWatcherEngine(fd);
+}
+
+QInotifyFileSystemWatcherEngine::QInotifyFileSystemWatcherEngine(int fd)
+ : inotifyFd(fd)
+{
+ fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
+
+ moveToThread(this);
+}
+
+QInotifyFileSystemWatcherEngine::~QInotifyFileSystemWatcherEngine()
+{
+ foreach (int id, pathToID.values())
+ inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
+
+ ::close(inotifyFd);
+}
+
+void QInotifyFileSystemWatcherEngine::run()
+{
+ QSocketNotifier sn(inotifyFd, QSocketNotifier::Read, this);
+ connect(&sn, SIGNAL(activated(int)), SLOT(readFromInotify()));
+ (void) exec();
+}
+
+QStringList QInotifyFileSystemWatcherEngine::addPaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories)
+{
+ QMutexLocker locker(&mutex);
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ QFileInfo fi(path);
+ bool isDir = fi.isDir();
+ if (isDir) {
+ if (directories->contains(path))
+ continue;
+ } else {
+ if (files->contains(path))
+ continue;
+ }
+
+ int wd = inotify_add_watch(inotifyFd,
+ QFile::encodeName(path),
+ (isDir
+ ? (0
+ | IN_ATTRIB
+ | IN_MOVE
+ | IN_CREATE
+ | IN_DELETE
+ | IN_DELETE_SELF
+ )
+ : (0
+ | IN_ATTRIB
+ | IN_MODIFY
+ | IN_MOVE
+ | IN_MOVE_SELF
+ | IN_DELETE_SELF
+ )));
+ if (wd <= 0) {
+ perror("QInotifyFileSystemWatcherEngine::addPaths: inotify_add_watch failed");
+ continue;
+ }
+
+ it.remove();
+
+ int id = isDir ? -wd : wd;
+ if (id < 0) {
+ directories->append(path);
+ } else {
+ files->append(path);
+ }
+
+ pathToID.insert(path, id);
+ idToPath.insert(id, path);
+ }
+
+ start();
+
+ return p;
+}
+
+QStringList QInotifyFileSystemWatcherEngine::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();
+ int id = pathToID.take(path);
+ QString x = idToPath.take(id);
+ if (x.isEmpty() || x != path)
+ continue;
+
+ int wd = id < 0 ? -id : id;
+ // qDebug() << "removing watch for path" << path << "wd" << wd;
+ inotify_rm_watch(inotifyFd, wd);
+
+ it.remove();
+ if (id < 0) {
+ directories->removeAll(path);
+ } else {
+ files->removeAll(path);
+ }
+ }
+
+ return p;
+}
+
+void QInotifyFileSystemWatcherEngine::stop()
+{
+ QMetaObject::invokeMethod(this, "quit");
+}
+
+void QInotifyFileSystemWatcherEngine::readFromInotify()
+{
+ QMutexLocker locker(&mutex);
+
+ // qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify";
+
+ int buffSize = 0;
+ ioctl(inotifyFd, FIONREAD, &buffSize);
+ QVarLengthArray<char, 4096> buffer(buffSize);
+ buffSize = read(inotifyFd, buffer.data(), buffSize);
+ const char *at = buffer.data();
+ const char * const end = at + buffSize;
+
+ QMap<int, inotify_event> eventForId;
+ while (at < end) {
+ const inotify_event *event = reinterpret_cast<const inotify_event *>(at);
+
+ if (eventForId.contains(event->wd))
+ eventForId[event->wd].mask |= event->mask;
+ else
+ eventForId.insert(event->wd, *event);
+
+ at += sizeof(inotify_event) + event->len;
+ }
+
+ QMap<int, inotify_event>::const_iterator it = eventForId.constBegin();
+ while (it != eventForId.constEnd()) {
+ inotify_event event = *it;
+ ++it;
+
+ // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask;
+
+ int id = event.wd;
+ QString path = idToPath.value(id);
+ if (path.isEmpty()) {
+ // perhaps a directory?
+ id = -id;
+ path = idToPath.value(id);
+ if (path.isEmpty())
+ continue;
+ }
+
+ // qDebug() << "event for path" << path;
+
+ if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
+ pathToID.remove(path);
+ idToPath.remove(id);
+ inotify_rm_watch(inotifyFd, event.wd);
+
+ if (id < 0)
+ emit directoryChanged(path, true);
+ else
+ emit fileChanged(path, true);
+ } else {
+ if (id < 0)
+ emit directoryChanged(path, false);
+ else
+ emit fileChanged(path, false);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FILESYSTEMWATCHER
diff --git a/src/corelib/io/qfilesystemwatcher_inotify_p.h b/src/corelib/io/qfilesystemwatcher_inotify_p.h
new file mode 100644
index 0000000..8f3595c
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_inotify_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMWATCHER_INOTIFY_P_H
+#define QFILESYSTEMWATCHER_INOTIFY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfilesystemwatcher_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qhash.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QInotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine
+{
+ Q_OBJECT
+
+public:
+ ~QInotifyFileSystemWatcherEngine();
+
+ static QInotifyFileSystemWatcherEngine *create();
+
+ void run();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ void stop();
+
+private Q_SLOTS:
+ void readFromInotify();
+
+private:
+ QInotifyFileSystemWatcherEngine(int fd);
+ int inotifyFd;
+ QMutex mutex;
+ QHash<QString, int> pathToID;
+ QHash<int, QString> idToPath;
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_FILESYSTEMWATCHER
+#endif // QFILESYSTEMWATCHER_INOTIFY_P_H
diff --git a/src/corelib/io/qfilesystemwatcher_kqueue.cpp b/src/corelib/io/qfilesystemwatcher_kqueue.cpp
new file mode 100644
index 0000000..cdf436e
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_kqueue.cpp
@@ -0,0 +1,334 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+
+#include "qfilesystemwatcher.h"
+#include "qfilesystemwatcher_kqueue_p.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+#include <qsocketnotifier.h>
+#include <qvarlengtharray.h>
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+QT_BEGIN_NAMESPACE
+
+// #define KEVENT_DEBUG
+#ifdef KEVENT_DEBUG
+# define DEBUG qDebug
+#else
+# define DEBUG if(false)qDebug
+#endif
+
+QKqueueFileSystemWatcherEngine *QKqueueFileSystemWatcherEngine::create()
+{
+ int kqfd = kqueue();
+ if (kqfd == -1)
+ return 0;
+ return new QKqueueFileSystemWatcherEngine(kqfd);
+}
+
+QKqueueFileSystemWatcherEngine::QKqueueFileSystemWatcherEngine(int kqfd)
+ : kqfd(kqfd)
+{
+ fcntl(kqfd, F_SETFD, FD_CLOEXEC);
+
+ if (pipe(kqpipe) == -1) {
+ perror("QKqueueFileSystemWatcherEngine: cannot create pipe");
+ kqpipe[0] = kqpipe[1] = -1;
+ return;
+ }
+ fcntl(kqpipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(kqpipe[1], F_SETFD, FD_CLOEXEC);
+
+ struct kevent kev;
+ EV_SET(&kev,
+ kqpipe[0],
+ EVFILT_READ,
+ EV_ADD | EV_ENABLE,
+ 0,
+ 0,
+ 0);
+ if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
+ perror("QKqueueFileSystemWatcherEngine: cannot watch pipe, kevent returned");
+ return;
+ }
+}
+
+QKqueueFileSystemWatcherEngine::~QKqueueFileSystemWatcherEngine()
+{
+ stop();
+ wait();
+
+ close(kqfd);
+ close(kqpipe[0]);
+ close(kqpipe[1]);
+
+ foreach (int id, pathToID.values())
+ ::close(id < 0 ? -id : id);
+}
+
+QStringList QKqueueFileSystemWatcherEngine::addPaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories)
+{
+ QMutexLocker locker(&mutex);
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ int fd;
+#if defined(O_EVTONLY)
+ fd = ::open(QFile::encodeName(path), O_EVTONLY);
+#else
+ fd = ::open(QFile::encodeName(path), O_RDONLY);
+#endif
+ if (fd == -1) {
+ perror("QKqueueFileSystemWatcherEngine::addPaths: open");
+ continue;
+ }
+
+ QT_STATBUF st;
+ if (QT_FSTAT(fd, &st) == -1) {
+ perror("QKqueueFileSystemWatcherEngine::addPaths: fstat");
+ ::close(fd);
+ continue;
+ }
+ int id = (S_ISDIR(st.st_mode)) ? -fd : fd;
+ if (id < 0) {
+ if (directories->contains(path)) {
+ ::close(fd);
+ continue;
+ }
+ } else {
+ if (files->contains(path)) {
+ ::close(fd);
+ continue;
+ }
+ }
+
+ struct kevent kev;
+ EV_SET(&kev,
+ fd,
+ EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
+ 0,
+ 0);
+ if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
+ perror("QKqueueFileSystemWatcherEngine::addPaths: kevent");
+ ::close(fd);
+ continue;
+ }
+
+ it.remove();
+ if (id < 0) {
+ DEBUG() << "QKqueueFileSystemWatcherEngine: added directory path" << path;
+ directories->append(path);
+ } else {
+ DEBUG() << "QKqueueFileSystemWatcherEngine: added file path" << path;
+ files->append(path);
+ }
+
+ pathToID.insert(path, id);
+ idToPath.insert(id, path);
+ }
+
+ if (!isRunning())
+ start();
+ else
+ write(kqpipe[1], "@", 1);
+
+ return p;
+}
+
+QStringList QKqueueFileSystemWatcherEngine::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();
+ int id = pathToID.take(path);
+ QString x = idToPath.take(id);
+ if (x.isEmpty() || x != path)
+ continue;
+
+ int fd = id < 0 ? -id : id;
+ struct kevent kev;
+ EV_SET(&kev,
+ fd,
+ EVFILT_VNODE,
+ EV_DELETE,
+ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
+ 0,
+ 0);
+ if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
+ perror("QKqueueFileSystemWatcherEngine::removeWatch: kevent");
+ }
+ ::close(fd);
+
+ it.remove();
+ if (id < 0)
+ directories->removeAll(path);
+ else
+ files->removeAll(path);
+ }
+
+ if (pathToID.isEmpty()) {
+ stop();
+ locker.unlock();
+ wait();
+ locker.relock();
+ } else {
+ write(kqpipe[1], "@", 1);
+ }
+
+ return p;
+}
+
+void QKqueueFileSystemWatcherEngine::stop()
+{
+ write(kqpipe[1], "q", 1);
+}
+
+void QKqueueFileSystemWatcherEngine::run()
+{
+ static const struct timespec ZeroTimeout = { 0, 0 };
+
+ forever {
+ struct kevent kev;
+ DEBUG() << "QKqueueFileSystemWatcherEngine: waiting for kevents...";
+ int r = kevent(kqfd, 0, 0, &kev, 1, 0);
+ if (r < 0) {
+ perror("QKqueueFileSystemWatcherEngine: error during kevent wait");
+ return;
+ }
+
+ QMutexLocker locker(&mutex);
+ do {
+ int fd = kev.ident;
+
+ DEBUG() << "QKqueueFileSystemWatcherEngine: processing kevent" << kev.ident << kev.filter;
+ if (fd == kqpipe[0]) {
+ char c;
+ if (read(kqpipe[0], &c, 1) != 1) {
+ perror("QKqueueFileSystemWatcherEngine: error reading from pipe");
+ return;
+ }
+ switch (c) {
+ case 'q':
+ DEBUG() << "QKqueueFileSystemWatcherEngine: thread received 'q', exiting...";
+ return;
+ case '@':
+ DEBUG() << "QKqueueFileSystemWatcherEngine: thread received '@', continuing...";
+ break;
+ default:
+ DEBUG() << "QKqueueFileSystemWatcherEngine: thread received unknow message" << c;
+ break;
+ }
+ } else {
+ int id = fd;
+ QString path = idToPath.value(id);
+ if (path.isEmpty()) {
+ // perhaps a directory?
+ id = -id;
+ path = idToPath.value(id);
+ if (path.isEmpty()) {
+ DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent for a file we're not watching";
+ continue;
+ }
+ }
+ if (kev.filter != EVFILT_VNODE) {
+ DEBUG() << "QKqueueFileSystemWatcherEngine: received a kevent with the wrong filter";
+ continue;
+ }
+
+ if ((kev.fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) != 0) {
+ DEBUG() << path << "removed, removing watch also";
+
+ pathToID.remove(path);
+ idToPath.remove(id);
+ ::close(fd);
+
+ if (id < 0)
+ emit directoryChanged(path, true);
+ else
+ emit fileChanged(path, true);
+ } else {
+ DEBUG() << path << "changed, re-enabling watch";
+
+ if (id < 0)
+ emit directoryChanged(path, false);
+ else
+ emit fileChanged(path, false);
+
+ // renable the watch
+ EV_SET(&kev,
+ fd,
+ EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
+ 0,
+ 0);
+ if (kevent(kqfd, &kev, 1, 0, 0, 0) == -1) {
+ perror("QKqueueFileSystemWatcherEngine::processKqueueEvents: kevent EV_ADD");
+ }
+ }
+ }
+
+ // are there any more?
+ r = kevent(kqfd, 0, 0, &kev, 1, &ZeroTimeout);
+ } while (r > 0);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qfilesystemwatcher_kqueue_p.h b/src/corelib/io/qfilesystemwatcher_kqueue_p.h
new file mode 100644
index 0000000..789fb3a
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_kqueue_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FILEWATCHER_KQUEUE_P_H
+#define FILEWATCHER_KQUEUE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfilesystemwatcher_p.h"
+
+#include <QtCore/qhash.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qvector.h>
+
+struct kevent;
+
+QT_BEGIN_NAMESPACE
+
+class QKqueueFileSystemWatcherEngine : public QFileSystemWatcherEngine
+{
+ Q_OBJECT
+public:
+ ~QKqueueFileSystemWatcherEngine();
+
+ static QKqueueFileSystemWatcherEngine *create();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ void stop();
+
+private:
+ QKqueueFileSystemWatcherEngine(int kqfd);
+
+ void run();
+
+ int kqfd;
+ int kqpipe[2];
+
+ QMutex mutex;
+ QHash<QString, int> pathToID;
+ QHash<int, QString> idToPath;
+};
+
+QT_END_NAMESPACE
+
+#endif // FILEWATCHER_KQUEUE_P_H
diff --git a/src/corelib/io/qfilesystemwatcher_p.h b/src/corelib/io/qfilesystemwatcher_p.h
new file mode 100644
index 0000000..7cc9153
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMWATCHER_P_H
+#define QFILESYSTEMWATCHER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfilesystemwatcher.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <private/qobject_p.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFileSystemWatcherEngine : public QThread
+{
+ Q_OBJECT
+
+protected:
+ inline QFileSystemWatcherEngine()
+ {
+ moveToThread(this);
+ }
+
+public:
+ // fills \a files and \a directires with the \a paths it could
+ // watch, and returns a list of paths this engine could not watch
+ virtual QStringList addPaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories) = 0;
+ // removes \a paths from \a files and \a directories, and returns
+ // a list of paths this engine does not know about (either addPath
+ // failed or wasn't called)
+ virtual QStringList removePaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories) = 0;
+
+ virtual void stop() = 0;
+
+Q_SIGNALS:
+ void fileChanged(const QString &path, bool removed);
+ void directoryChanged(const QString &path, bool removed);
+};
+
+class QFileSystemWatcherPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QFileSystemWatcher)
+
+ static QFileSystemWatcherEngine *createNativeEngine();
+
+public:
+ QFileSystemWatcherPrivate();
+ void init();
+ void initPollerEngine();
+ void initForcedEngine(const QString &);
+
+ QFileSystemWatcherEngine *native, *poller, *forced;
+ QStringList files, directories;
+
+ // private slots
+ void _q_fileChanged(const QString &path, bool removed);
+ void _q_directoryChanged(const QString &path, bool removed);
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_FILESYSTEMWATCHER
+#endif // QFILESYSTEMWATCHER_P_H
diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp
new file mode 100644
index 0000000..74e3e27
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_win.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfilesystemwatcher.h"
+#include "qfilesystemwatcher_win_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <qdebug.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qset.h>
+#include <qdatetime.h>
+#include <qdir.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsFileSystemWatcherEngine::QWindowsFileSystemWatcherEngine()
+ : msg(0)
+{
+ HANDLE h = QT_WA_INLINE(CreateEventW(0, false, false, 0),
+ CreateEventA(0, false, false, 0));
+ if (h != INVALID_HANDLE_VALUE) {
+ handles.reserve(MAXIMUM_WAIT_OBJECTS);
+ handles.append(h);
+ }
+}
+
+QWindowsFileSystemWatcherEngine::~QWindowsFileSystemWatcherEngine()
+{
+ if (handles.isEmpty())
+ return;
+
+ stop();
+ wait();
+
+ CloseHandle(handles.at(0));
+ handles[0] = INVALID_HANDLE_VALUE;
+
+ foreach (HANDLE h, handles) {
+ if (h == INVALID_HANDLE_VALUE)
+ continue;
+ FindCloseChangeNotification(h);
+ }
+}
+
+void QWindowsFileSystemWatcherEngine::run()
+{
+ QMutexLocker locker(&mutex);
+ forever {
+ QVector<HANDLE> handlesCopy = handles;
+ locker.unlock();
+ // qDebug() << "QWindowsFileSystemWatcherEngine: waiting on" << handlesCopy.count() << "handles";
+ DWORD r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, INFINITE);
+ locker.relock();
+ do {
+ if (r == WAIT_OBJECT_0) {
+ int m = msg;
+ msg = 0;
+ if (m == 'q') {
+ // qDebug() << "thread told to quit";
+ return;
+ }
+ if (m != '@') {
+ qDebug("QWindowsFileSystemWatcherEngine: unknown message '%c' send to thread", char(m));
+ }
+ break;
+ } else if (r > WAIT_OBJECT_0 && r < WAIT_OBJECT_0 + uint(handlesCopy.count())) {
+ int at = r - WAIT_OBJECT_0;
+ Q_ASSERT(at < handlesCopy.count());
+ HANDLE handle = handlesCopy.at(at);
+
+ // When removing a path, FindCloseChangeNotification might actually fire a notification
+ // for some reason, so we must check if the handle exist in the handles vector
+ if (handles.contains(handle)) {
+ // qDebug("Acknowledged handle: %d, %p", at, handle);
+ if (!FindNextChangeNotification(handle)) {
+ qErrnoWarning("QFileSystemWatcher: FindNextChangeNotification failed");
+ }
+
+ QHash<QString, PathInfo> &h = pathInfoForHandle[handle];
+ QMutableHashIterator<QString, PathInfo> it(h);
+ while (it.hasNext()) {
+ QHash<QString, PathInfo>::iterator x = it.next();
+ QString absolutePath = x.value().absolutePath;
+ QFileInfo fileInfo(x.value().path);
+ // qDebug() << "checking" << x.key();
+ if (!fileInfo.exists()) {
+ // qDebug() << x.key() << "removed!";
+ if (x.value().isDir)
+ emit directoryChanged(x.value().path, true);
+ else
+ emit fileChanged(x.value().path, true);
+ h.erase(x);
+
+ // close the notification handle if the directory has been removed
+ if (h.isEmpty()) {
+ // qDebug() << "Thread closing handle" << handle;
+ FindCloseChangeNotification(handle); // This one might generate a notification
+
+ int indexOfHandle = handles.indexOf(handle);
+ Q_ASSERT(indexOfHandle != -1);
+ handles.remove(indexOfHandle);
+
+ handleForDir.remove(absolutePath);
+ // h is now invalid
+ }
+ } else if (x.value().isDir) {
+ // qDebug() << x.key() << "directory changed!";
+ emit directoryChanged(x.value().path, false);
+ x.value() = fileInfo;
+ } else if (x.value() != fileInfo) {
+ // qDebug() << x.key() << "file changed!";
+ emit fileChanged(x.value().path, false);
+ x.value() = fileInfo;
+ }
+ }
+ }
+ } else {
+ // qErrnoWarning("QFileSystemWatcher: error while waiting for change notification");
+ break; // avoid endless loop
+ }
+ handlesCopy = handles;
+ r = WaitForMultipleObjects(handlesCopy.count(), handlesCopy.constData(), false, 0);
+ } while (r != WAIT_TIMEOUT);
+ }
+}
+
+QStringList QWindowsFileSystemWatcherEngine::addPaths(const QStringList &paths,
+ QStringList *files,
+ QStringList *directories)
+{
+ if (handles.isEmpty() || handles.count() == MAXIMUM_WAIT_OBJECTS)
+ return paths;
+
+ QMutexLocker locker(&mutex);
+
+ QStringList p = paths;
+ QMutableListIterator<QString> it(p);
+ while (it.hasNext()) {
+ QString path = it.next();
+ QString normalPath = path;
+ if ((normalPath.endsWith(QLatin1Char('/')) || normalPath.endsWith(QLatin1Char('\\')))
+#ifdef Q_OS_WINCE
+ && normalPath.size() > 1)
+#else
+ )
+#endif
+ normalPath.chop(1);
+ QFileInfo fileInfo(normalPath.toLower());
+ if (!fileInfo.exists())
+ continue;
+
+ bool isDir = fileInfo.isDir();
+ if (isDir) {
+ if (directories->contains(path))
+ continue;
+ } else {
+ if (files->contains(path))
+ continue;
+ }
+
+ const QString absolutePath = isDir ? fileInfo.absoluteFilePath() : fileInfo.absolutePath();
+ const uint flags = isDir
+ ? (FILE_NOTIFY_CHANGE_DIR_NAME
+ | FILE_NOTIFY_CHANGE_FILE_NAME)
+ : (FILE_NOTIFY_CHANGE_DIR_NAME
+ | FILE_NOTIFY_CHANGE_FILE_NAME
+ | FILE_NOTIFY_CHANGE_ATTRIBUTES
+ | FILE_NOTIFY_CHANGE_SIZE
+ | FILE_NOTIFY_CHANGE_LAST_WRITE
+ | FILE_NOTIFY_CHANGE_SECURITY);
+
+ Handle handle = handleForDir.value(absolutePath);
+ if (handle.handle == INVALID_HANDLE_VALUE || handle.flags != flags) {
+ // Volume and folder paths need a trailing slash for proper notification
+ // (e.g. "c:" -> "c:/").
+ const QString effectiveAbsolutePath =
+ isDir ? (absolutePath + QLatin1Char('/')) : absolutePath;
+
+ QT_WA({
+ handle.handle = FindFirstChangeNotificationW((TCHAR *) QDir::toNativeSeparators(effectiveAbsolutePath).utf16(),
+ false, flags);
+ },{
+ handle.handle = FindFirstChangeNotificationA(QDir::toNativeSeparators(effectiveAbsolutePath).toLocal8Bit(),
+ false, flags);
+ })
+ handle.flags = flags;
+ if (handle.handle == INVALID_HANDLE_VALUE)
+ continue;
+ // qDebug() << "Added handle" << handle << "for" << absolutePath << "to watch" << fileInfo.absoluteFilePath();
+ handles.append(handle.handle);
+ handleForDir.insert(absolutePath, handle);
+ }
+
+ PathInfo pathInfo;
+ pathInfo.absolutePath = absolutePath;
+ pathInfo.isDir = isDir;
+ pathInfo.path = path;
+ pathInfo = fileInfo;
+ QHash<QString, PathInfo> &h = pathInfoForHandle[handle.handle];
+ if (!h.contains(fileInfo.absoluteFilePath())) {
+ pathInfoForHandle[handle.handle].insert(fileInfo.absoluteFilePath(), pathInfo);
+ if (isDir)
+ directories->append(path);
+ else
+ files->append(path);
+ }
+
+ it.remove();
+ }
+
+ if (!isRunning()) {
+ msg = '@';
+ start();
+ } else {
+ wakeup();
+ }
+
+ return p;
+}
+
+QStringList QWindowsFileSystemWatcherEngine::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();
+ QString normalPath = path;
+ if (normalPath.endsWith(QLatin1Char('/')) || normalPath.endsWith(QLatin1Char('\\')))
+ normalPath.chop(1);
+ QFileInfo fileInfo(normalPath.toLower());
+
+ QString absolutePath = fileInfo.absoluteFilePath();
+ Handle handle = handleForDir.value(absolutePath);
+ if (handle.handle == INVALID_HANDLE_VALUE) {
+ // perhaps path is a file?
+ absolutePath = fileInfo.absolutePath();
+ handle = handleForDir.value(absolutePath);
+ if (handle.handle == INVALID_HANDLE_VALUE)
+ continue;
+ }
+
+ QHash<QString, PathInfo> &h = pathInfoForHandle[handle.handle];
+ if (h.remove(fileInfo.absoluteFilePath())) {
+ // ###
+ files->removeAll(path);
+ directories->removeAll(path);
+
+ if (h.isEmpty()) {
+ // qDebug() << "Closing handle" << handle;
+ FindCloseChangeNotification(handle.handle); // This one might generate a notification
+
+ int indexOfHandle = handles.indexOf(handle.handle);
+ Q_ASSERT(indexOfHandle != -1);
+ handles.remove(indexOfHandle);
+
+ handleForDir.remove(absolutePath);
+ // h is now invalid
+
+ it.remove();
+ }
+ }
+ }
+
+ if (handleForDir.isEmpty()) {
+ stop();
+ locker.unlock();
+ wait();
+ locker.relock();
+ } else {
+ wakeup();
+ }
+
+ return p;
+}
+
+void QWindowsFileSystemWatcherEngine::stop()
+{
+ msg = 'q';
+ SetEvent(handles.at(0));
+}
+
+void QWindowsFileSystemWatcherEngine::wakeup()
+{
+ msg = '@';
+ SetEvent(handles.at(0));
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_FILESYSTEMWATCHER
diff --git a/src/corelib/io/qfilesystemwatcher_win_p.h b/src/corelib/io/qfilesystemwatcher_win_p.h
new file mode 100644
index 0000000..525e32f
--- /dev/null
+++ b/src/corelib/io/qfilesystemwatcher_win_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFILESYSTEMWATCHER_WIN_P_H
+#define QFILESYSTEMWATCHER_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qfilesystemwatcher_p.h"
+
+#ifndef QT_NO_FILESYSTEMWATCHER
+
+#include <windows.h>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsFileSystemWatcherEngine : public QFileSystemWatcherEngine
+{
+ Q_OBJECT
+
+public:
+ QWindowsFileSystemWatcherEngine();
+ ~QWindowsFileSystemWatcherEngine();
+
+ void run();
+
+ QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+ void stop();
+
+private:
+ void wakeup();
+
+ QMutex mutex;
+ QVector<HANDLE> handles;
+ int msg;
+
+ class Handle
+ {
+ public:
+ HANDLE handle;
+ uint flags;
+
+ Handle()
+ : handle(INVALID_HANDLE_VALUE), flags(0u)
+ { }
+ Handle(const Handle &other)
+ : handle(other.handle), flags(other.flags)
+ { }
+ };
+ QHash<QString, Handle> handleForDir;
+
+ class PathInfo {
+ public:
+ QString absolutePath;
+ QString path;
+ bool isDir;
+
+ // fileinfo bits
+ uint ownerId;
+ uint groupId;
+ QFile::Permissions permissions;
+ QDateTime lastModified;
+
+ PathInfo &operator=(const QFileInfo &fileInfo)
+ {
+ ownerId = fileInfo.ownerId();
+ groupId = fileInfo.groupId();
+ permissions = fileInfo.permissions();
+ lastModified = fileInfo.lastModified();
+ return *this;
+ }
+
+ bool operator!=(const QFileInfo &fileInfo) const
+ {
+ return (ownerId != fileInfo.ownerId()
+ || groupId != fileInfo.groupId()
+ || permissions != fileInfo.permissions()
+ || lastModified != fileInfo.lastModified());
+ }
+ };
+ QHash<HANDLE, QHash<QString, PathInfo> > pathInfoForHandle;
+};
+#endif // QT_NO_FILESYSTEMWATCHER
+
+QT_END_NAMESPACE
+
+#endif // QFILESYSTEMWATCHER_WIN_P_H
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
new file mode 100644
index 0000000..61ea7cc
--- /dev/null
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -0,0 +1,873 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfsfileengine_p.h"
+#include "qfsfileengine_iterator_p.h"
+#include "qdatetime.h"
+#include "qdiriterator.h"
+#include "qset.h"
+
+#ifndef QT_NO_FSFILEENGINE
+
+#if !defined(Q_OS_WINCE)
+#include <errno.h>
+#endif
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_WIN
+# ifndef S_ISREG
+# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
+# endif
+# ifndef S_ISCHR
+# define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR)
+# endif
+# ifndef S_ISFIFO
+# define S_ISFIFO(x) false
+# endif
+# ifndef S_ISSOCK
+# define S_ISSOCK(x) false
+# endif
+# ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES (DWORD (-1))
+# endif
+#endif
+
+
+/*! \class QFSFileEngine
+ \brief The QFSFileEngine class implements Qt's default file engine.
+ \since 4.1
+
+ This class is part of the file engine framework in Qt. If you only want to
+ access files or directories, use QFile, QFileInfo or QDir instead.
+
+ QFSFileEngine is the default file engine for accessing regular files. It
+ is provided for convenience; by subclassing this class, you can alter its
+ behavior slightly, without having to write a complete QAbstractFileEngine
+ subclass. To install your custom file engine, you must also subclass
+ QAbstractFileEngineHandler and create an instance of your handler.
+
+ It can also be useful to create a QFSFileEngine object directly if you
+ need to use the local file system inside QAbstractFileEngine::create(), in
+ order to avoid recursion (as higher-level classes tend to call
+ QAbstractFileEngine::create()).
+*/
+
+//**************** QFSFileEnginePrivate
+QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
+{
+ init();
+}
+
+/*!
+ \internal
+*/
+void QFSFileEnginePrivate::init()
+{
+ is_sequential = 0;
+ tried_stat = 0;
+#ifdef Q_OS_UNIX
+ need_lstat = 1;
+ is_link = 0;
+#endif
+ openMode = QIODevice::NotOpen;
+ fd = -1;
+ fh = 0;
+ lastIOCommand = IOFlushCommand;
+ lastFlushFailed = false;
+ closeFileHandle = false;
+#ifdef Q_OS_WIN
+ fileAttrib = INVALID_FILE_ATTRIBUTES;
+ fileHandle = INVALID_HANDLE_VALUE;
+ cachedFd = -1;
+#endif
+#ifdef Q_USE_DEPRECATED_MAP_API
+ fileMapHandle = INVALID_HANDLE_VALUE;
+#endif
+}
+
+/*!
+ \internal
+
+ Returns the canonicalized form of \a path (i.e., with all symlinks
+ resolved, and all redundant path elements removed.
+*/
+QString QFSFileEnginePrivate::canonicalized(const QString &path)
+{
+ if (path.isEmpty())
+ return path;
+
+ QFileInfo fi;
+ const QChar slash(QLatin1Char('/'));
+ QString tmpPath = path;
+ int separatorPos = 0;
+ QSet<QString> nonSymlinks;
+ QSet<QString> known;
+
+ known.insert(path);
+ do {
+#ifdef Q_OS_WIN
+ // UNC, skip past the first two elements
+ if (separatorPos == 0 && tmpPath.startsWith(QLatin1String("//")))
+ separatorPos = tmpPath.indexOf(slash, 2);
+ if (separatorPos != -1)
+#endif
+ separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
+ QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
+ if (!nonSymlinks.contains(prefix)) {
+ fi.setFile(prefix);
+ if (fi.isSymLink()) {
+ QString target = fi.symLinkTarget();
+ if (separatorPos != -1) {
+ if (fi.isDir() && !target.endsWith(slash))
+ target.append(slash);
+ target.append(tmpPath.mid(separatorPos));
+ }
+ tmpPath = QDir::cleanPath(target);
+ separatorPos = 0;
+
+ if (known.contains(tmpPath))
+ return QString();
+ known.insert(tmpPath);
+ } else {
+ nonSymlinks.insert(prefix);
+ }
+ }
+ } while (separatorPos != -1);
+
+ return QDir::cleanPath(tmpPath);
+}
+
+/*!
+ Constructs a QFSFileEngine for the file name \a file.
+*/
+QFSFileEngine::QFSFileEngine(const QString &file) : QAbstractFileEngine(*new QFSFileEnginePrivate)
+{
+ Q_D(QFSFileEngine);
+ d->filePath = QDir::fromNativeSeparators(file);
+ d->nativeInitFileName();
+}
+
+/*!
+ Constructs a QFSFileEngine.
+*/
+QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
+{
+}
+
+/*!
+ \internal
+*/
+QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
+ : QAbstractFileEngine(dd)
+{
+}
+
+/*!
+ Destructs the QFSFileEngine.
+*/
+QFSFileEngine::~QFSFileEngine()
+{
+ Q_D(QFSFileEngine);
+ if (d->closeFileHandle) {
+ if (d->fh) {
+ int ret;
+ do {
+ ret = fclose(d->fh);
+ } while (ret == EOF && errno == EINTR);
+ } else if (d->fd != -1) {
+ int ret;
+ do {
+ ret = QT_CLOSE(d->fd);
+ } while (ret == -1 && errno == EINTR);
+ }
+ }
+ QList<uchar*> keys = d->maps.keys();
+ for (int i = 0; i < keys.count(); ++i)
+ unmap(keys.at(i));
+}
+
+/*!
+ \reimp
+*/
+void QFSFileEngine::setFileName(const QString &file)
+{
+ Q_D(QFSFileEngine);
+ d->init();
+ d->filePath = QDir::fromNativeSeparators(file);
+ d->nativeInitFileName();
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::open(QIODevice::OpenMode openMode)
+{
+ Q_D(QFSFileEngine);
+ if (d->filePath.isEmpty()) {
+ qWarning("QFSFileEngine::open: No file name specified");
+ setError(QFile::OpenError, QLatin1String("No file name specified"));
+ return false;
+ }
+
+ // Append implies WriteOnly.
+ if (openMode & QFile::Append)
+ openMode |= QFile::WriteOnly;
+
+ // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
+ if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
+ openMode |= QFile::Truncate;
+
+ d->openMode = openMode;
+ d->lastFlushFailed = false;
+ d->tried_stat = 0;
+ d->fh = 0;
+ d->fd = -1;
+
+ return d->nativeOpen(openMode);
+}
+
+/*!
+ Opens the file handle \a fh in \a openMode mode. Returns true on
+ success; otherwise returns false.
+*/
+bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
+{
+ Q_D(QFSFileEngine);
+
+ // Append implies WriteOnly.
+ if (openMode & QFile::Append)
+ openMode |= QFile::WriteOnly;
+
+ // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
+ if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
+ openMode |= QFile::Truncate;
+
+ d->openMode = openMode;
+ d->lastFlushFailed = false;
+ d->closeFileHandle = false;
+ d->nativeFilePath.clear();
+ d->filePath.clear();
+ d->tried_stat = 0;
+ d->fd = -1;
+
+ return d->openFh(openMode, fh);
+}
+
+/*!
+ Opens the file handle \a fh using the open mode \a flags.
+*/
+bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
+{
+ Q_Q(QFSFileEngine);
+ this->fh = fh;
+ fd = -1;
+
+ // Seek to the end when in Append mode.
+ if (openMode & QIODevice::Append) {
+ int ret;
+ do {
+ ret = QT_FSEEK(fh, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ Opens the file descriptor \a fd in \a openMode mode. Returns true
+ on success; otherwise returns false.
+*/
+bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
+{
+ Q_D(QFSFileEngine);
+
+ // Append implies WriteOnly.
+ if (openMode & QFile::Append)
+ openMode |= QFile::WriteOnly;
+
+ // WriteOnly implies Truncate if neither ReadOnly nor Append are sent.
+ if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append)))
+ openMode |= QFile::Truncate;
+
+ d->lastFlushFailed = false;
+ d->closeFileHandle = false;
+ d->nativeFilePath.clear();
+ d->filePath.clear();
+ d->fh = 0;
+ d->fd = -1;
+ d->tried_stat = 0;
+
+ return d->openFd(openMode, fd);
+}
+
+
+/*!
+ Opens the file descriptor \a fd to the file engine, using the open mode \a
+ flags.
+*/
+bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
+{
+ Q_Q(QFSFileEngine);
+ this->fd = fd;
+ fh = 0;
+
+ // Seek to the end when in Append mode.
+ if (openMode & QFile::Append) {
+ int ret;
+ do {
+ ret = QT_LSEEK(fd, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::close()
+{
+ Q_D(QFSFileEngine);
+ d->openMode = QIODevice::NotOpen;
+ return d->nativeClose();
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::closeFdFh()
+{
+ Q_Q(QFSFileEngine);
+ if (fd == -1 && !fh)
+ return false;
+
+ // Flush the file if it's buffered, and if the last flush didn't fail.
+ bool flushed = !fh || (!lastFlushFailed && q->flush());
+ bool closed = true;
+ tried_stat = 0;
+
+ // Close the file if we created the handle.
+ if (closeFileHandle) {
+ int ret;
+ do {
+ if (fh) {
+ // Close buffered file.
+ ret = fclose(fh) != 0 ? -1 : 0;
+ } else {
+ // Close unbuffered file.
+ ret = QT_CLOSE(fd);
+ }
+ } while (ret == -1 && errno == EINTR);
+
+ // We must reset these guys regardless; calling close again after a
+ // failed close causes crashes on some systems.
+ fh = 0;
+ fd = -1;
+ closed = (ret == 0);
+ }
+
+ // Report errors.
+ if (!flushed || !closed) {
+ if (flushed) {
+ // If not flushed, we want the flush error to fall through.
+ q->setError(QFile::UnspecifiedError, qt_error_string(errno));
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::flush()
+{
+ Q_D(QFSFileEngine);
+ if ((d->openMode & QIODevice::WriteOnly) == 0) {
+ // Nothing in the write buffers, so flush succeeds in doing
+ // nothing.
+ return true;
+ }
+ return d->nativeFlush();
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::flushFh()
+{
+ Q_Q(QFSFileEngine);
+
+ // Never try to flush again if the last flush failed. Otherwise you can
+ // get crashes on some systems (AIX).
+ if (lastFlushFailed)
+ return false;
+
+ int ret = fflush(fh);
+
+ lastFlushFailed = (ret != 0);
+ lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
+
+ if (ret != 0) {
+ q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
+ qt_error_string(errno));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+qint64 QFSFileEngine::size() const
+{
+ Q_D(const QFSFileEngine);
+ return d->nativeSize();
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::sizeFdFh() const
+{
+ Q_Q(const QFSFileEngine);
+ // ### Fix this function, it should not stat unless the file is closed.
+ QT_STATBUF st;
+ int ret = 0;
+ const_cast<QFSFileEngine *>(q)->flush();
+ if (fh && nativeFilePath.isEmpty()) {
+ // Buffered stdlib mode.
+ // ### This should really be an ftell
+ ret = QT_FSTAT(QT_FILENO(fh), &st);
+ } else if (fd == -1) {
+ // Stateless stat.
+ ret = QT_STAT(nativeFilePath.constData(), &st);
+ } else {
+ // Unbuffered stdio mode.
+ ret = QT_FSTAT(fd, &st);
+ }
+ if (ret == -1)
+ return 0;
+ return st.st_size;
+}
+
+/*!
+ \reimp
+*/
+qint64 QFSFileEngine::pos() const
+{
+ Q_D(const QFSFileEngine);
+ return d->nativePos();
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::posFdFh() const
+{
+ if (fh)
+ return qint64(QT_FTELL(fh));
+ return QT_LSEEK(fd, 0, SEEK_CUR);
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::seek(qint64 pos)
+{
+ Q_D(QFSFileEngine);
+ return d->nativeSeek(pos);
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
+{
+ Q_Q(QFSFileEngine);
+
+ // On Windows' stdlib implementation, the results of calling fread and
+ // fwrite are undefined if not called either in sequence, or if preceded
+ // with a call to fflush().
+ if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
+ return false;
+
+ if (fh) {
+ // Buffered stdlib mode.
+ int ret;
+ do {
+ ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ return false;
+ }
+ } else {
+ // Unbuffered stdio mode.
+ if (QT_LSEEK(fd, pos, SEEK_SET) == -1) {
+ qWarning("QFile::at: Cannot set file position %lld", pos);
+ q->setError(QFile::PositionError, qt_error_string(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+int QFSFileEngine::handle() const
+{
+ Q_D(const QFSFileEngine);
+ return d->nativeHandle();
+}
+
+/*!
+ \reimp
+*/
+qint64 QFSFileEngine::read(char *data, qint64 maxlen)
+{
+ Q_D(QFSFileEngine);
+
+ // On Windows' stdlib implementation, the results of calling fread and
+ // fwrite are undefined if not called either in sequence, or if preceded
+ // with a call to fflush().
+ if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
+ flush();
+ d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
+ }
+
+ return d->nativeRead(data, maxlen);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
+{
+ Q_Q(QFSFileEngine);
+
+ // Buffered stdlib mode.
+ if (fh) {
+ qint64 readBytes = 0;
+ qint64 read = 0;
+ int retry = 0;
+
+ // Read in blocks of 4k to avoid platform limitations (Windows
+ // commonly bails out if you read or write too large blocks at once).
+ qint64 bytesToRead;
+ do {
+ if (retry == 1)
+ retry = 2;
+
+ bytesToRead = qMin<qint64>(4096, len - read);
+ do {
+ readBytes = fread(data + read, 1, size_t(bytesToRead), fh);
+ } while (readBytes == 0 && !feof(fh) && errno == EINTR);
+
+ if (readBytes > 0) {
+ read += readBytes;
+ } else if (!retry && feof(fh)) {
+ // Synchronize and try again (just once though).
+ if (++retry == 1)
+ QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET);
+ }
+ } while (retry == 1 || (readBytes == bytesToRead && read < len));
+
+ // Return the number of bytes read, or if nothing was read, return -1
+ // if an error occurred, or 0 if we detected EOF.
+ if (read == 0) {
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ if (!feof(fh))
+ read = -1;
+ }
+ return read;
+ }
+
+ // Unbuffered stdio mode.
+ qint64 ret = 0;
+ if (len) {
+ int result;
+ qint64 read = 0;
+ errno = 0;
+
+ // Read in blocks of 4k to avoid platform limitations (Windows
+ // commonly bails out if you read or write too large blocks at once).
+ do {
+ qint64 bytesToRead = qMin<qint64>(4096, len - read);
+ do {
+ result = QT_READ(fd, data + read, int(bytesToRead));
+ } while (result == -1 && errno == EINTR);
+ if (result > 0)
+ read += result;
+ } while (result > 0 && read < len);
+
+ // Return the number of bytes read, or if nothing was read, return -1
+ // if an error occurred.
+ if (read > 0) {
+ ret += read;
+ } else if (read == 0 && result < 0) {
+ ret = -1;
+ q->setError(QFile::ReadError, qt_error_string(errno));
+ }
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+qint64 QFSFileEngine::readLine(char *data, qint64 maxlen)
+{
+ Q_D(QFSFileEngine);
+
+ // On Windows' stdlib implementation, the results of calling fread and
+ // fwrite are undefined if not called either in sequence, or if preceded
+ // with a call to fflush().
+ if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
+ flush();
+ d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
+ }
+
+ return d->nativeReadLine(data, maxlen);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
+{
+ Q_Q(QFSFileEngine);
+ if (!fh)
+ return q->QAbstractFileEngine::readLine(data, maxlen);
+
+ QT_OFF_T oldPos = 0;
+#ifdef Q_OS_WIN
+ bool seq = q->isSequential();
+ if (!seq)
+#endif
+ oldPos = QT_FTELL(fh);
+
+ // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData()
+ // because it has made space for the '\0' at the end of data. But fgets
+ // does the same, so we'd get two '\0' at the end - passing maxlen + 1
+ // solves this.
+ if (!fgets(data, int(maxlen + 1), fh)) {
+ if (!feof(fh))
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ return -1; // error
+ }
+
+#ifdef Q_OS_WIN
+ if (seq)
+ return qstrlen(data);
+#endif
+
+ qint64 lineLength = QT_FTELL(fh) - oldPos;
+ return lineLength > 0 ? lineLength : qstrlen(data);
+}
+
+/*!
+ \reimp
+*/
+qint64 QFSFileEngine::write(const char *data, qint64 len)
+{
+ Q_D(QFSFileEngine);
+
+ // On Windows' stdlib implementation, the results of calling fread and
+ // fwrite are undefined if not called either in sequence, or if preceded
+ // with a call to fflush().
+ if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) {
+ flush();
+ d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand;
+ }
+
+ return d->nativeWrite(data, len);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
+{
+ Q_Q(QFSFileEngine);
+ qint64 result;
+ qint64 written = 0;
+
+ do {
+ // Write blocks of 4k to avoid platform limitations (Windows commonly
+ // bails out if you read or write too large blocks at once).
+ qint64 bytesToWrite = qMin<qint64>(4096, len - written);
+ if (fh) {
+ do {
+ // Buffered stdlib mode.
+ result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh));
+ } while (result == 0 && errno == EINTR);
+ if (bytesToWrite > 0 && result == 0)
+ result = -1;
+ } else {
+ do {
+ // Unbuffered stdio mode.
+ result = QT_WRITE(fd, data + written, bytesToWrite);
+ } while (result == -1 && errno == EINTR);
+ }
+ if (result > 0)
+ written += qint64(result);
+ } while (written < len && result > 0);
+
+ // If we read anything, return that with success. Otherwise, set an error,
+ // and return the last return value.
+ if (result > 0)
+ return written;
+ q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
+ return result;
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
+{
+ return new QFSFileEngineIterator(filters, filterNames);
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
+{
+ return 0;
+}
+
+/*!
+ \internal
+*/
+QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ return QAbstractFileEngine::entryList(filters, filterNames);
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::isSequential() const
+{
+ Q_D(const QFSFileEngine);
+ if (d->is_sequential == 0)
+ d->is_sequential = d->nativeIsSequential() ? 1 : 2;
+ return d->is_sequential == 1;
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::isSequentialFdFh() const
+{
+ if (!tried_stat)
+ doStat();
+ if (could_stat) {
+#ifdef Q_OS_UNIX
+ return (st.st_mode & S_IFMT) != S_IFREG;
+ // ### WINDOWS!
+#endif
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ Q_D(QFSFileEngine);
+ if (extension == AtEndExtension && d->fh && isSequential())
+ return feof(d->fh);
+
+ if (extension == MapExtension) {
+ const MapExtensionOption *options = (MapExtensionOption*)(option);
+ MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
+ returnValue->address = d->map(options->offset, options->size, options->flags);
+ return (returnValue->address != 0);
+ }
+ if (extension == UnMapExtension) {
+ UnMapExtensionOption *options = (UnMapExtensionOption*)option;
+ return d->unmap(options->address);
+ }
+
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::supportsExtension(Extension extension) const
+{
+ Q_D(const QFSFileEngine);
+ if (extension == AtEndExtension && d->fh && isSequential())
+ return true;
+ if (extension == FastReadLineExtension && d->fh)
+ return true;
+ if (extension == FastReadLineExtension && d->fd != -1 && isSequential())
+ return true;
+ if (extension == UnMapExtension || extension == MapExtension)
+ return true;
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
diff --git a/src/corelib/io/qfsfileengine.h b/src/corelib/io/qfsfileengine.h
new file mode 100644
index 0000000..a2b732e
--- /dev/null
+++ b/src/corelib/io/qfsfileengine.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFSFILEENGINE_H
+#define QFSFILEENGINE_H
+
+#include <QtCore/qabstractfileengine.h>
+
+#ifndef QT_NO_FSFILEENGINE
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QFSFileEnginePrivate;
+
+class Q_CORE_EXPORT QFSFileEngine : public QAbstractFileEngine
+{
+ Q_DECLARE_PRIVATE(QFSFileEngine)
+public:
+ QFSFileEngine();
+ explicit QFSFileEngine(const QString &file);
+ ~QFSFileEngine();
+
+ bool open(QIODevice::OpenMode openMode);
+ bool open(QIODevice::OpenMode flags, FILE *fh);
+ bool close();
+ bool flush();
+ qint64 size() const;
+ qint64 pos() const;
+ bool seek(qint64);
+ bool isSequential() const;
+ bool remove();
+ bool copy(const QString &newName);
+ bool rename(const QString &newName);
+ bool link(const QString &newName);
+ bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
+ bool setSize(qint64 size);
+ bool caseSensitive() const;
+ bool isRelativePath() const;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+ FileFlags fileFlags(FileFlags type) const;
+ bool setPermissions(uint perms);
+ QString fileName(FileName file) const;
+ uint ownerId(FileOwner) const;
+ QString owner(FileOwner) const;
+ QDateTime fileTime(FileTime time) const;
+ void setFileName(const QString &file);
+ int handle() const;
+
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+ Iterator *endEntryList();
+
+ qint64 read(char *data, qint64 maxlen);
+ qint64 readLine(char *data, qint64 maxlen);
+ qint64 write(const char *data, qint64 len);
+
+ bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0);
+ bool supportsExtension(Extension extension) const;
+
+ //FS only!!
+ bool open(QIODevice::OpenMode flags, int fd);
+ static bool setCurrentPath(const QString &path);
+ static QString currentPath(const QString &path = QString());
+ static QString homePath();
+ static QString rootPath();
+ static QString tempPath();
+ static QFileInfoList drives();
+
+protected:
+ QFSFileEngine(QFSFileEnginePrivate &dd);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_FSFILEENGINE
+
+#endif // QFSFILEENGINE_H
diff --git a/src/corelib/io/qfsfileengine_iterator.cpp b/src/corelib/io/qfsfileengine_iterator.cpp
new file mode 100644
index 0000000..c5e23f2
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_iterator.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfsfileengine_iterator_p.h"
+#include "qvariant.h"
+
+#ifndef QT_NO_FSFILEENGINE
+
+QT_BEGIN_NAMESPACE
+
+QFSFileEngineIterator::QFSFileEngineIterator(QDir::Filters filters, const QStringList &filterNames)
+ : QAbstractFileEngineIterator(filters, filterNames)
+{
+ newPlatformSpecifics();
+}
+
+QFSFileEngineIterator::~QFSFileEngineIterator()
+{
+ deletePlatformSpecifics();
+}
+
+QString QFSFileEngineIterator::next()
+{
+ if (!hasNext())
+ return QString();
+
+ advance();
+ return currentFilePath();
+}
+
+QString QFSFileEngineIterator::currentFileName() const
+{
+ return currentEntry;
+}
+
+QFileInfo QFSFileEngineIterator::currentFileInfo() const
+{
+ return QAbstractFileEngineIterator::currentFileInfo();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
+
diff --git a/src/corelib/io/qfsfileengine_iterator_p.h b/src/corelib/io/qfsfileengine_iterator_p.h
new file mode 100644
index 0000000..7dcdbe2
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_iterator_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFSFILEENGINE_ITERATOR_P_H
+#define QFSFILEENGINE_ITERATOR_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 "qabstractfileengine.h"
+#include "qdir.h"
+
+#ifndef QT_NO_FSFILEENGINE
+
+QT_BEGIN_NAMESPACE
+
+class QFSFileEngineIteratorPrivate;
+class QFSFileEngineIteratorPlatformSpecificData;
+
+class QFSFileEngineIterator : public QAbstractFileEngineIterator
+{
+public:
+ QFSFileEngineIterator(QDir::Filters filters, const QStringList &filterNames);
+ ~QFSFileEngineIterator();
+
+ QString next();
+ bool hasNext() const;
+
+ QString currentFileName() const;
+ QFileInfo currentFileInfo() const;
+
+private:
+ QFSFileEngineIteratorPlatformSpecificData *platform;
+ friend class QFSFileEngineIteratorPlatformSpecificData;
+ void newPlatformSpecifics();
+ void deletePlatformSpecifics();
+ void advance();
+
+ QString currentEntry;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
+
+#endif
diff --git a/src/corelib/io/qfsfileengine_iterator_unix.cpp b/src/corelib/io/qfsfileengine_iterator_unix.cpp
new file mode 100644
index 0000000..2284d9b
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_iterator_unix.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qfsfileengine_iterator_p.h"
+
+#include <QtCore/qvariant.h>
+
+#ifndef QT_NO_FSFILEENGINE
+
+QT_BEGIN_NAMESPACE
+
+class QFSFileEngineIteratorPlatformSpecificData
+{
+public:
+ inline QFSFileEngineIteratorPlatformSpecificData()
+ : dir(0), dirEntry(0), done(false)
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ , mt_file(0)
+#endif
+ { }
+
+ DIR *dir;
+ dirent *dirEntry;
+ bool done;
+
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ // for readdir_r
+ dirent *mt_file;
+#endif
+};
+
+void QFSFileEngineIterator::advance()
+{
+ currentEntry = platform->dirEntry ? QFile::decodeName(QByteArray(platform->dirEntry->d_name)) : QString();
+
+ if (!platform->dir)
+ return;
+
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ if (::readdir_r(platform->dir, platform->mt_file, &platform->dirEntry) != 0)
+ platform->done = true;
+#else
+ // ### add local lock to prevent breaking reentrancy
+ platform->dirEntry = ::readdir(platform->dir);
+#endif // _POSIX_THREAD_SAFE_FUNCTIONS
+ if (!platform->dirEntry) {
+ ::closedir(platform->dir);
+ platform->dir = 0;
+ platform->done = true;
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ delete [] platform->mt_file;
+ platform->mt_file = 0;
+#endif
+ }
+}
+
+void QFSFileEngineIterator::newPlatformSpecifics()
+{
+ platform = new QFSFileEngineIteratorPlatformSpecificData;
+}
+
+void QFSFileEngineIterator::deletePlatformSpecifics()
+{
+ if (platform->dir) {
+ ::closedir(platform->dir);
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ delete [] platform->mt_file;
+ platform->mt_file = 0;
+#endif
+ }
+ delete platform;
+ platform = 0;
+}
+
+bool QFSFileEngineIterator::hasNext() const
+{
+ if (!platform->done && !platform->dir) {
+ QFSFileEngineIterator *that = const_cast<QFSFileEngineIterator *>(this);
+ if ((that->platform->dir = ::opendir(QFile::encodeName(path()).data())) == 0) {
+ that->platform->done = true;
+ } else {
+ // ### Race condition; we should use fpathconf and dirfd().
+ long maxPathName = ::pathconf(QFile::encodeName(path()).data(), _PC_NAME_MAX);
+ if ((int) maxPathName == -1)
+ maxPathName = FILENAME_MAX;
+ maxPathName += sizeof(dirent) + 1;
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+ if (that->platform->mt_file)
+ delete [] that->platform->mt_file;
+ that->platform->mt_file = (dirent *)new char[maxPathName];
+#endif
+
+ that->advance();
+ }
+ }
+ return !platform->done;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
diff --git a/src/corelib/io/qfsfileengine_iterator_win.cpp b/src/corelib/io/qfsfileengine_iterator_win.cpp
new file mode 100644
index 0000000..3212d73
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_iterator_win.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdebug.h"
+#include "qfsfileengine_iterator_p.h"
+#include "qfsfileengine_p.h"
+#include "qplatformdefs.h"
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qmutex.h>
+#include <private/qmutexpool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFSFileEngineIteratorPlatformSpecificData
+{
+public:
+ inline QFSFileEngineIteratorPlatformSpecificData()
+ : uncShareIndex(-1), findFileHandle(INVALID_HANDLE_VALUE),
+ done(false), uncFallback(false)
+ { }
+
+ QFSFileEngineIterator *it;
+
+ QStringList uncShares;
+ int uncShareIndex;
+
+ HANDLE findFileHandle;
+ WIN32_FIND_DATA findData;
+ bool done;
+ bool uncFallback;
+
+ void advance();
+ void saveCurrentFileName();
+};
+
+void QFSFileEngineIteratorPlatformSpecificData::saveCurrentFileName()
+{
+ if (uncFallback) {
+ // Windows share / UNC path
+ it->currentEntry = uncShares.at(uncShareIndex - 1);
+ } else {
+ // Local directory
+ QT_WA({
+ it->currentEntry = QString::fromUtf16((unsigned short *)findData.cFileName);
+ } , {
+ it->currentEntry = QString::fromLocal8Bit((const char *)findData.cFileName);
+ });
+ }
+}
+
+void QFSFileEngineIterator::advance()
+{
+ platform->saveCurrentFileName();
+
+ if (platform->done)
+ return;
+
+ if (platform->uncFallback) {
+ ++platform->uncShareIndex;
+ } else if (platform->findFileHandle != INVALID_HANDLE_VALUE) {
+ QT_WA({
+ if (!FindNextFile(platform->findFileHandle, &platform->findData)) {
+ platform->done = true;
+ FindClose(platform->findFileHandle);
+ }
+ } , {
+ if (!FindNextFileA(platform->findFileHandle, (WIN32_FIND_DATAA *)&platform->findData)) {
+ platform->done = true;
+ FindClose(platform->findFileHandle);
+ }
+ });
+ }
+}
+
+void QFSFileEngineIterator::newPlatformSpecifics()
+{
+ platform = new QFSFileEngineIteratorPlatformSpecificData;
+ platform->it = this;
+}
+
+void QFSFileEngineIterator::deletePlatformSpecifics()
+{
+ delete platform;
+ platform = 0;
+}
+
+bool QFSFileEngineIterator::hasNext() const
+{
+ if (platform->done)
+ return false;
+
+ if (platform->uncFallback)
+ return platform->uncShareIndex > 0 && platform->uncShareIndex <= platform->uncShares.size();
+
+ if (platform->findFileHandle == INVALID_HANDLE_VALUE) {
+ QString path = this->path();
+ // Local directory
+ if (path.endsWith(QLatin1String(".lnk")))
+ path = QFileInfo(path).readLink();
+
+ if (!path.endsWith(QLatin1Char('/')))
+ path.append(QLatin1Char('/'));
+ path.append(QLatin1String("*.*"));
+
+ QT_WA({
+ QString fileName = QFSFileEnginePrivate::longFileName(path);
+ platform->findFileHandle = FindFirstFileW((TCHAR *)fileName.utf16(),
+ &platform->findData);
+ }, {
+ // Cast is safe, since char is at end of WIN32_FIND_DATA
+ platform->findFileHandle = FindFirstFileA(QFSFileEnginePrivate::win95Name(path),
+ (WIN32_FIND_DATAA*)&platform->findData);
+ });
+
+ if (platform->findFileHandle == INVALID_HANDLE_VALUE) {
+ if (path.startsWith(QLatin1String("//"))) {
+ path = this->path();
+ // UNC
+ QStringList parts = QDir::toNativeSeparators(path).split(QLatin1Char('\\'), QString::SkipEmptyParts);
+
+ if (parts.count() == 1 && QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0),
+ &platform->uncShares)) {
+ if (platform->uncShares.isEmpty()) {
+ platform->done = true;
+ } else {
+ platform->uncShareIndex = 1;
+ }
+ platform->uncFallback = true;
+ } else {
+ platform->done = true;
+ }
+ } else {
+ platform->done = true;
+ }
+ }
+
+ if (!platform->done && (!platform->uncFallback || !platform->uncShares.isEmpty()))
+ platform->saveCurrentFileName();
+ }
+
+ return !platform->done;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qfsfileengine_p.h b/src/corelib/io/qfsfileengine_p.h
new file mode 100644
index 0000000..8b6cc53
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_p.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFSFILEENGINE_P_H
+#define QFSFILEENGINE_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 "qplatformdefs.h"
+#include "QtCore/qfsfileengine.h"
+#include "private/qabstractfileengine_p.h"
+#include <qhash.h>
+
+#ifndef QT_NO_FSFILEENGINE
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE_STD) && _WIN32_WCE < 0x600
+#define Q_USE_DEPRECATED_MAP_API 1
+#endif
+
+class QFSFileEnginePrivate : public QAbstractFileEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QFSFileEngine)
+
+public:
+#ifdef Q_WS_WIN
+ static QByteArray win95Name(const QString &path);
+ static QString longFileName(const QString &path);
+#endif
+ static QString canonicalized(const QString &path);
+
+ QString filePath;
+ QByteArray nativeFilePath;
+ QIODevice::OpenMode openMode;
+
+ void nativeInitFileName();
+ bool nativeOpen(QIODevice::OpenMode openMode);
+ bool openFh(QIODevice::OpenMode flags, FILE *fh);
+ bool openFd(QIODevice::OpenMode flags, int fd);
+ bool nativeClose();
+ bool closeFdFh();
+ bool nativeFlush();
+ bool flushFh();
+ qint64 nativeSize() const;
+ qint64 sizeFdFh() const;
+ qint64 nativePos() const;
+ qint64 posFdFh() const;
+ bool nativeSeek(qint64);
+ bool seekFdFh(qint64);
+ qint64 nativeRead(char *data, qint64 maxlen);
+ qint64 readFdFh(char *data, qint64 maxlen);
+ qint64 nativeReadLine(char *data, qint64 maxlen);
+ qint64 readLineFdFh(char *data, qint64 maxlen);
+ qint64 nativeWrite(const char *data, qint64 len);
+ qint64 writeFdFh(const char *data, qint64 len);
+ int nativeHandle() const;
+ bool nativeIsSequential() const;
+ bool isSequentialFdFh() const;
+
+ uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
+ bool unmap(uchar *ptr);
+
+ FILE *fh;
+#ifdef Q_WS_WIN
+ HANDLE fileHandle;
+ QHash<uchar *, QPair<int /*offset*/, HANDLE /*handle*/> > maps;
+ mutable int cachedFd;
+ mutable DWORD fileAttrib;
+#else
+ QHash<uchar *, QPair<int /*offset*/, int /*handle|len*/> > maps;
+ mutable QT_STATBUF st;
+#endif
+ int fd;
+
+#ifdef Q_USE_DEPRECATED_MAP_API
+ void mapHandleClose();
+ HANDLE fileMapHandle;
+#endif
+
+ enum LastIOCommand
+ {
+ IOFlushCommand,
+ IOReadCommand,
+ IOWriteCommand
+ };
+ LastIOCommand lastIOCommand;
+ bool lastFlushFailed;
+ bool closeFileHandle;
+
+ mutable uint is_sequential : 2;
+ mutable uint could_stat : 1;
+ mutable uint tried_stat : 1;
+#ifdef Q_OS_UNIX
+ mutable uint need_lstat : 1;
+ mutable uint is_link : 1;
+#endif
+ bool doStat() const;
+ bool isSymlink() const;
+
+#if defined(Q_OS_WIN32)
+ int sysOpen(const QString &, int flags);
+#endif
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ static void resolveLibs();
+ static bool resolveUNCLibs_NT();
+ static bool resolveUNCLibs_9x();
+ static bool uncListSharesOnServer(const QString &server, QStringList *list);
+#endif
+
+protected:
+ QFSFileEnginePrivate();
+
+ void init();
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ QAbstractFileEngine::FileFlags getPermissions() const;
+ QString getLink() const;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
+
+#endif // QFSFILEENGINE_P_H
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
new file mode 100644
index 0000000..0d88b06
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -0,0 +1,1021 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qabstractfileengine.h"
+#include "private/qfsfileengine_p.h"
+
+#ifndef QT_NO_FSFILEENGINE
+
+#ifndef QT_NO_REGEXP
+# include "qregexp.h"
+#endif
+#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>
+#include <errno.h>
+#if !defined(QWS) && defined(Q_OS_MAC)
+# include <private/qcore_mac_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ Returns the stdlib open string corresponding to a QIODevice::OpenMode.
+*/
+static QByteArray openModeToFopenMode(QIODevice::OpenMode flags, const QString &fileName = QString())
+{
+ QByteArray mode;
+ if ((flags & QIODevice::ReadOnly) && !(flags & QIODevice::Truncate)) {
+ mode = "rb";
+ if (flags & QIODevice::WriteOnly) {
+ if (!fileName.isEmpty() &&QFile::exists(fileName))
+ mode = "rb+";
+ else
+ mode = "wb+";
+ }
+ } else if (flags & QIODevice::WriteOnly) {
+ mode = "wb";
+ if (flags & QIODevice::ReadOnly)
+ mode += "+";
+ }
+ if (flags & QIODevice::Append) {
+ mode = "ab";
+ if (flags & QIODevice::ReadOnly)
+ mode += "+";
+ }
+ return mode;
+}
+
+/*!
+ \internal
+
+ Returns the stdio open flags corresponding to a QIODevice::OpenMode.
+*/
+static int openModeToOpenFlags(QIODevice::OpenMode mode)
+{
+ int oflags = QT_OPEN_RDONLY;
+#ifdef QT_LARGEFILE_SUPPORT
+ oflags |= QT_OPEN_LARGEFILE;
+#endif
+
+ if ((mode & QFile::ReadWrite) == QFile::ReadWrite) {
+ oflags = QT_OPEN_RDWR | QT_OPEN_CREAT;
+ } else if (mode & QFile::WriteOnly) {
+ oflags = QT_OPEN_WRONLY | QT_OPEN_CREAT;
+ }
+
+ if (mode & QFile::Append) {
+ oflags |= QT_OPEN_APPEND;
+ } else if (mode & QFile::WriteOnly) {
+ if ((mode & QFile::Truncate) || !(mode & QFile::ReadOnly))
+ oflags |= QT_OPEN_TRUNC;
+ }
+
+#ifdef O_CLOEXEC
+ // supported on Linux >= 2.6.23; avoids one extra system call
+ // and avoids a race condition: if another thread forks, we could
+ // end up leaking a file descriptor...
+ oflags |= O_CLOEXEC;
+#endif
+ return oflags;
+}
+
+/*!
+ \internal
+
+ Sets the file descriptor to close on exec. That is, the file
+ descriptor is not inherited by child processes.
+*/
+static bool setCloseOnExec(int fd)
+{
+ return fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) != -1;
+}
+
+/*!
+ \internal
+*/
+void QFSFileEnginePrivate::nativeInitFileName()
+{
+ nativeFilePath = QFile::encodeName(filePath);
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
+{
+ Q_Q(QFSFileEngine);
+
+ if (openMode & QIODevice::Unbuffered) {
+ int flags = openModeToOpenFlags(openMode);
+
+ // Try to open the file in unbuffered mode.
+ do {
+ fd = QT_OPEN(nativeFilePath.constData(), flags, 0666);
+ } while (fd == -1 && errno == EINTR);
+
+ // On failure, return and report the error.
+ if (fd == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(errno));
+ return false;
+ }
+
+ QT_STATBUF statBuf;
+ if (QT_FSTAT(fd, &statBuf) != -1) {
+ if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
+ q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
+ QT_CLOSE(fd);
+ return false;
+ }
+ }
+
+#ifndef O_CLOEXEC
+ // not needed on Linux >= 2.6.23
+ setCloseOnExec(fd); // ignore failure
+#endif
+
+ // Seek to the end when in Append mode.
+ if (flags & QFile::Append) {
+ int ret;
+ do {
+ ret = QT_LSEEK(fd, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ fh = 0;
+ } else {
+ QByteArray fopenMode = openModeToFopenMode(openMode, filePath);
+
+ // Try to open the file in buffered mode.
+ do {
+ fh = QT_FOPEN(nativeFilePath.constData(), fopenMode.constData());
+ } while (!fh && errno == EINTR);
+
+ // On failure, return and report the error.
+ if (!fh) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+
+ QT_STATBUF statBuf;
+ if (QT_FSTAT(fileno(fh), &statBuf) != -1) {
+ if ((statBuf.st_mode & S_IFMT) == S_IFDIR) {
+ q->setError(QFile::OpenError, QLatin1String("file to open is a directory"));
+ fclose(fh);
+ return false;
+ }
+ }
+
+ setCloseOnExec(fileno(fh)); // ignore failure
+
+ // Seek to the end when in Append mode.
+ if (openMode & QIODevice::Append) {
+ int ret;
+ do {
+ ret = QT_FSEEK(fh, 0, SEEK_END);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
+ qt_error_string(int(errno)));
+ return false;
+ }
+ }
+
+ fd = -1;
+ }
+
+ closeFileHandle = true;
+ return true;
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeClose()
+{
+ return closeFdFh();
+}
+
+/*!
+ \internal
+
+*/
+bool QFSFileEnginePrivate::nativeFlush()
+{
+ return fh ? flushFh() : fd != -1;
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 len)
+{
+ Q_Q(QFSFileEngine);
+
+ if (fh && nativeIsSequential()) {
+ size_t readBytes = 0;
+ int oldFlags = fcntl(QT_FILENO(fh), F_GETFL);
+ for (int i = 0; i < 2; ++i) {
+ // Unix: Make the underlying file descriptor non-blocking
+ int v = 1;
+ if ((oldFlags & O_NONBLOCK) == 0)
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags | O_NONBLOCK, &v, sizeof(v));
+
+ // Cross platform stdlib read
+ size_t read = 0;
+ do {
+ read = fread(data + readBytes, 1, size_t(len - readBytes), fh);
+ } while (read == 0 && !feof(fh) && errno == EINTR);
+ if (read > 0) {
+ readBytes += read;
+ break;
+ } else {
+ if (readBytes)
+ break;
+ readBytes = read;
+ }
+
+ // Unix: Restore the blocking state of the underlying socket
+ if ((oldFlags & O_NONBLOCK) == 0) {
+ int v = 1;
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags, &v, sizeof(v));
+ if (readBytes == 0) {
+ int readByte = 0;
+ do {
+ readByte = fgetc(fh);
+ } while (readByte == -1 && errno == EINTR);
+ if (readByte != -1) {
+ *data = uchar(readByte);
+ readBytes += 1;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ // Unix: Restore the blocking state of the underlying socket
+ if ((oldFlags & O_NONBLOCK) == 0) {
+ int v = 1;
+ fcntl(QT_FILENO(fh), F_SETFL, oldFlags, &v, sizeof(v));
+ }
+ if (readBytes == 0 && !feof(fh)) {
+ // if we didn't read anything and we're not at EOF, it must be an error
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ return -1;
+ }
+ return readBytes;
+ }
+
+ return readFdFh(data, len);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
+{
+ return readLineFdFh(data, maxlen);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
+{
+ return writeFdFh(data, len);
+}
+
+/*!
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativePos() const
+{
+ return posFdFh();
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
+{
+ return seekFdFh(pos);
+}
+
+/*!
+ \internal
+*/
+int QFSFileEnginePrivate::nativeHandle() const
+{
+ return fh ? fileno(fh) : fd;
+}
+
+/*!
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeIsSequential() const
+{
+ return isSequentialFdFh();
+}
+
+bool QFSFileEngine::remove()
+{
+ Q_D(QFSFileEngine);
+ return unlink(d->nativeFilePath.constData()) == 0;
+}
+
+bool QFSFileEngine::copy(const QString &)
+{
+ // ### Add copy code for Unix here
+ return false;
+}
+
+bool QFSFileEngine::rename(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ return ::rename(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
+}
+
+bool QFSFileEngine::link(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ return ::symlink(d->nativeFilePath.constData(), QFile::encodeName(newName).constData()) == 0;
+}
+
+qint64 QFSFileEnginePrivate::nativeSize() const
+{
+ return sizeFdFh();
+}
+
+bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
+{
+ QString dirName = name;
+ if (createParentDirectories) {
+ dirName = QDir::cleanPath(dirName);
+ for(int oldslash = -1, slash=0; slash != -1; oldslash = slash) {
+ slash = dirName.indexOf(QDir::separator(), oldslash+1);
+ if (slash == -1) {
+ if (oldslash == dirName.length())
+ break;
+ slash = dirName.length();
+ }
+ if (slash) {
+ QByteArray chunk = QFile::encodeName(dirName.left(slash));
+ QT_STATBUF st;
+ if (QT_STAT(chunk, &st) != -1) {
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+ return false;
+ } else if (::mkdir(chunk, 0777) != 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+#if defined(Q_OS_DARWIN) // Mac X doesn't support trailing /'s
+ if (dirName[dirName.length() - 1] == QLatin1Char('/'))
+ dirName = dirName.left(dirName.length() - 1);
+#endif
+ return (::mkdir(QFile::encodeName(dirName), 0777) == 0);
+}
+
+bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
+{
+ QString dirName = name;
+ if (recurseParentDirectories) {
+ dirName = QDir::cleanPath(dirName);
+ for(int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
+ QByteArray chunk = QFile::encodeName(dirName.left(slash));
+ QT_STATBUF st;
+ if (QT_STAT(chunk, &st) != -1) {
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+ return false;
+ if (::rmdir(chunk) != 0)
+ return oldslash != 0;
+ } else {
+ return false;
+ }
+ slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
+ }
+ return true;
+ }
+ return ::rmdir(QFile::encodeName(dirName)) == 0;
+}
+
+bool QFSFileEngine::caseSensitive() const
+{
+ return true;
+}
+
+bool QFSFileEngine::setCurrentPath(const QString &path)
+{
+ int r;
+ r = ::chdir(QFile::encodeName(path));
+ return r >= 0;
+}
+
+QString QFSFileEngine::currentPath(const QString &)
+{
+ QString result;
+ QT_STATBUF st;
+ if (QT_STAT(".", &st) == 0) {
+#if defined(__GLIBC__) && !defined(PATH_MAX)
+ char *currentName = ::get_current_dir_name();
+ if (currentName) {
+ result = QFile::decodeName(QByteArray(currentName));
+ ::free(currentName);
+ }
+#else
+ char currentName[PATH_MAX+1];
+ if (::getcwd(currentName, PATH_MAX))
+ result = QFile::decodeName(QByteArray(currentName));
+#endif
+#if defined(QT_DEBUG)
+ if (result.isNull())
+ qWarning("QDir::currentPath: getcwd() failed");
+#endif
+ } else {
+#if defined(QT_DEBUG)
+ qWarning("QDir::currentPath: stat(\".\") failed");
+#endif
+ }
+ return result;
+}
+
+QString QFSFileEngine::homePath()
+{
+ QString home = QFile::decodeName(qgetenv("HOME"));
+ if (home.isNull())
+ home = rootPath();
+ return home;
+}
+
+QString QFSFileEngine::rootPath()
+{
+ return QString::fromLatin1("/");
+}
+
+QString QFSFileEngine::tempPath()
+{
+ QString temp = QFile::decodeName(qgetenv("TMPDIR"));
+ if (temp.isEmpty())
+ temp = QString::fromLatin1("/tmp/");
+ return temp;
+}
+
+QFileInfoList QFSFileEngine::drives()
+{
+ QFileInfoList ret;
+ ret.append(rootPath());
+ return ret;
+}
+
+bool QFSFileEnginePrivate::doStat() const
+{
+ if (tried_stat == 0) {
+ QFSFileEnginePrivate *that = const_cast<QFSFileEnginePrivate*>(this);
+ if (fh && nativeFilePath.isEmpty()) {
+ // ### actually covers two cases: d->fh and when the file is not open
+ that->could_stat = (QT_FSTAT(fileno(fh), &st) == 0);
+ } else if (fd == -1) {
+ // ### actually covers two cases: d->fh and when the file is not open
+ that->could_stat = (QT_STAT(nativeFilePath.constData(), &st) == 0);
+ } else {
+ that->could_stat = (QT_FSTAT(fd, &st) == 0);
+ }
+ that->tried_stat = 1;
+ }
+ return could_stat;
+}
+
+bool QFSFileEnginePrivate::isSymlink() const
+{
+ if (need_lstat) {
+ QFSFileEnginePrivate *that = const_cast<QFSFileEnginePrivate *>(this);
+ that->need_lstat = false;
+ QT_STATBUF st; // don't clobber our main one
+ that->is_link = (QT_LSTAT(nativeFilePath.constData(), &st) == 0) ? S_ISLNK(st.st_mode) : false;
+ }
+ return is_link;
+}
+
+#if !defined(QWS) && defined(Q_OS_MAC)
+static bool _q_isMacHidden(const QString &path)
+{
+ OSErr err = noErr;
+
+ FSRef fsRef;
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ err = FSPathMakeRefWithOptions(reinterpret_cast<const UInt8 *>(QFile::encodeName(QDir::cleanPath(path)).constData()),
+ kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0);
+ } else
+#endif
+ {
+ QFileInfo fi(path);
+ FSRef parentRef;
+ err = FSPathMakeRef(reinterpret_cast<const UInt8 *>(fi.absoluteDir().absolutePath().toUtf8().constData()),
+ &parentRef, 0);
+ if (err == noErr) {
+ QString fileName = fi.fileName();
+ err = FSMakeFSRefUnicode(&parentRef, fileName.length(),
+ reinterpret_cast<const UniChar *>(fileName.unicode()),
+ kTextEncodingUnknown, &fsRef);
+ }
+ }
+ if (err != noErr)
+ return false;
+
+ FSCatalogInfo catInfo;
+ err = FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, NULL, NULL, NULL);
+ if (err != noErr)
+ return false;
+
+ FileInfo * const fileInfo = reinterpret_cast<FileInfo*>(&catInfo.finderInfo);
+ bool result = (fileInfo->finderFlags & kIsInvisible);
+ return result;
+}
+#endif
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
+{
+ Q_D(const QFSFileEngine);
+ // Force a stat, so that we're guaranteed to get up-to-date results
+ if (type & QAbstractFileEngine::FileFlag(QAbstractFileEngine::Refresh)) {
+ d->tried_stat = 0;
+ d->need_lstat = 1;
+ }
+
+ QAbstractFileEngine::FileFlags ret = 0;
+ bool exists = d->doStat();
+ if (!exists && !d->isSymlink())
+ return ret;
+
+ if (exists && (type & PermsMask)) {
+ if (d->st.st_mode & S_IRUSR)
+ ret |= ReadOwnerPerm;
+ if (d->st.st_mode & S_IWUSR)
+ ret |= WriteOwnerPerm;
+ if (d->st.st_mode & S_IXUSR)
+ ret |= ExeOwnerPerm;
+ if (d->st.st_mode & S_IRUSR)
+ ret |= ReadUserPerm;
+ if (d->st.st_mode & S_IWUSR)
+ ret |= WriteUserPerm;
+ if (d->st.st_mode & S_IXUSR)
+ ret |= ExeUserPerm;
+ if (d->st.st_mode & S_IRGRP)
+ ret |= ReadGroupPerm;
+ if (d->st.st_mode & S_IWGRP)
+ ret |= WriteGroupPerm;
+ if (d->st.st_mode & S_IXGRP)
+ ret |= ExeGroupPerm;
+ if (d->st.st_mode & S_IROTH)
+ ret |= ReadOtherPerm;
+ if (d->st.st_mode & S_IWOTH)
+ ret |= WriteOtherPerm;
+ if (d->st.st_mode & S_IXOTH)
+ ret |= ExeOtherPerm;
+ }
+ if (type & TypesMask) {
+#if !defined(QWS) && defined(Q_OS_MAC)
+ bool foundAlias = false;
+ {
+ FSRef fref;
+ if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(),
+ &fref, NULL) == noErr) {
+ Boolean isAlias, isFolder;
+ if (FSIsAliasFile(&fref, &isAlias, &isFolder) == noErr && isAlias) {
+ foundAlias = true;
+ ret |= LinkType;
+ }
+ }
+ }
+ if (!foundAlias)
+#endif
+ {
+ if ((type & LinkType) && d->isSymlink())
+ ret |= LinkType;
+ if (exists && (d->st.st_mode & S_IFMT) == S_IFREG)
+ ret |= FileType;
+ else if (exists && (d->st.st_mode & S_IFMT) == S_IFDIR)
+ ret |= DirectoryType;
+#if !defined(QWS) && defined(Q_OS_MAC)
+ if((ret & DirectoryType) && (type & BundleType)) {
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
+ kCFURLPOSIXPathStyle, true);
+ UInt32 type, creator;
+ if(CFBundleGetPackageInfoInDirectory(url, &type, &creator))
+ ret |= BundleType;
+ }
+#endif
+ }
+ }
+ if (type & FlagsMask) {
+ ret |= LocalDiskFlag;
+ if (exists)
+ ret |= ExistsFlag;
+ if (fileName(BaseName)[0] == QLatin1Char('.')
+#if !defined(QWS) && defined(Q_OS_MAC)
+ || _q_isMacHidden(d->filePath)
+#endif
+ )
+ ret |= HiddenFlag;
+ if (d->filePath == QLatin1String("/"))
+ ret |= RootFlag;
+ }
+ return ret;
+}
+
+QString QFSFileEngine::fileName(FileName file) const
+{
+ Q_D(const QFSFileEngine);
+ if (file == BundleName) {
+#if !defined(QWS) && defined(Q_OS_MAC)
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(d->filePath),
+ kCFURLPOSIXPathStyle, true);
+ if(CFDictionaryRef dict = CFBundleCopyInfoDictionaryForURL(url)) {
+ if(CFTypeRef name = (CFTypeRef)CFDictionaryGetValue(dict, kCFBundleNameKey)) {
+ if(CFGetTypeID(name) == CFStringGetTypeID())
+ return QCFString::toQString((CFStringRef)name);
+ }
+ }
+#endif
+ return QString();
+ } else if (file == BaseName) {
+ int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1)
+ return d->filePath.mid(slash + 1);
+ } else if (file == PathName) {
+ int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
+ if (slash == -1)
+ return QLatin1String(".");
+ else if (!slash)
+ return QLatin1String("/");
+ return d->filePath.left(slash);
+ } else if (file == AbsoluteName || file == AbsolutePathName) {
+ QString ret;
+ if (d->filePath.isEmpty() || !d->filePath.startsWith(QLatin1Char('/')))
+ ret = QDir::currentPath();
+ if (!d->filePath.isEmpty() && d->filePath != QLatin1String(".")) {
+ if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
+ ret += QLatin1Char('/');
+ ret += d->filePath;
+ }
+ if (ret == QLatin1String("/"))
+ return ret;
+ bool isDir = ret.endsWith(QLatin1Char('/'));
+ ret = QDir::cleanPath(ret);
+ if (isDir)
+ ret += QLatin1String("/");
+ if (file == AbsolutePathName) {
+ int slash = ret.lastIndexOf(QLatin1Char('/'));
+ if (slash == -1)
+ return QDir::currentPath();
+ else if (!slash)
+ return QLatin1String("/");
+ return ret.left(slash);
+ }
+ 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::currentPath();
+ else if (slash == 0)
+ ret = QLatin1String("/");
+ ret = ret.left(slash);
+ }
+ return ret;
+ } else if (file == LinkName) {
+ if (d->isSymlink()) {
+#if defined(__GLIBC__) && !defined(PATH_MAX)
+#define PATH_CHUNK_SIZE 256
+ char *s = 0;
+ int len = -1;
+ int size = PATH_CHUNK_SIZE;
+
+ while (1) {
+ s = (char *) ::realloc(s, size);
+ if (s == 0) {
+ len = -1;
+ break;
+ }
+ len = ::readlink(d->nativeFilePath.constData(), s, size);
+ if (len < 0) {
+ ::free(s);
+ break;
+ }
+ if (len < size) {
+ break;
+ }
+ size *= 2;
+ }
+#else
+ char s[PATH_MAX+1];
+ int len = readlink(d->nativeFilePath.constData(), s, PATH_MAX);
+#endif
+ if (len > 0) {
+ QString ret;
+ if (S_ISDIR(d->st.st_mode) && s[0] != '/') {
+ QDir parent(d->filePath);
+ parent.cdUp();
+ ret = parent.path();
+ if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/')))
+ ret += QLatin1Char('/');
+ }
+ s[len] = '\0';
+ ret += QFile::decodeName(QByteArray(s));
+#if defined(__GLIBC__) && !defined(PATH_MAX)
+ ::free(s);
+#endif
+
+ if (!ret.startsWith(QLatin1Char('/'))) {
+ if (d->filePath.startsWith(QLatin1Char('/'))) {
+ ret.prepend(d->filePath.left(d->filePath.lastIndexOf(QLatin1Char('/')))
+ + QLatin1Char('/'));
+ } else {
+ ret.prepend(QDir::currentPath() + QLatin1Char('/'));
+ }
+ }
+ ret = QDir::cleanPath(ret);
+ if (ret.size() > 1 && ret.endsWith(QLatin1Char('/')))
+ ret.chop(1);
+ return ret;
+ }
+ }
+#if !defined(QWS) && defined(Q_OS_MAC)
+ {
+ FSRef fref;
+ if (FSPathMakeRef((const UInt8 *)QFile::encodeName(QDir::cleanPath(d->filePath)).data(), &fref, 0) == noErr) {
+ Boolean isAlias, isFolder;
+ if (FSResolveAliasFile(&fref, true, &isFolder, &isAlias) == noErr && isAlias) {
+ AliasHandle alias;
+ if (FSNewAlias(0, &fref, &alias) == noErr && alias) {
+ CFStringRef cfstr;
+ if (FSCopyAliasInfo(alias, 0, 0, &cfstr, 0, 0) == noErr)
+ return QCFString::toQString(cfstr);
+ }
+ }
+ }
+ }
+#endif
+ return QString();
+ }
+ return d->filePath;
+}
+
+bool QFSFileEngine::isRelativePath() const
+{
+ Q_D(const QFSFileEngine);
+ int len = d->filePath.length();
+ if (len == 0)
+ return true;
+ return d->filePath[0] != QLatin1Char('/');
+}
+
+uint QFSFileEngine::ownerId(FileOwner own) const
+{
+ Q_D(const QFSFileEngine);
+ static const uint nobodyID = (uint) -2;
+ if (d->doStat()) {
+ if (own == OwnerUser)
+ return d->st.st_uid;
+ else
+ return d->st.st_gid;
+ }
+ return nobodyID;
+}
+
+QString QFSFileEngine::owner(FileOwner own) const
+{
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
+ int size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (size_max == -1)
+ size_max = 1024;
+ QVarLengthArray<char, 1024> buf(size_max);
+#endif
+
+ if (own == OwnerUser) {
+ struct passwd *pw = 0;
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
+ struct passwd entry;
+ getpwuid_r(ownerId(own), &entry, buf.data(), buf.size(), &pw);
+#else
+ pw = getpwuid(ownerId(own));
+#endif
+ if (pw)
+ return QFile::decodeName(QByteArray(pw->pw_name));
+ } else if (own == OwnerGroup) {
+ struct group *gr = 0;
+#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
+ size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (size_max == -1)
+ size_max = 1024;
+ buf.resize(size_max);
+ struct group entry;
+ // Some large systems have more members than the POSIX max size
+ // Loop over by doubling the buffer size (upper limit 250k)
+ for (unsigned size = size_max; size < 256000; size += size)
+ {
+ buf.resize(size);
+ // ERANGE indicates that the buffer was too small
+ if (!getgrgid_r(ownerId(own), &entry, buf.data(), buf.size(), &gr)
+ || errno != ERANGE)
+ break;
+ }
+
+#else
+ gr = getgrgid(ownerId(own));
+#endif
+ if (gr)
+ return QFile::decodeName(QByteArray(gr->gr_name));
+ }
+ return QString();
+}
+
+bool QFSFileEngine::setPermissions(uint perms)
+{
+ Q_D(QFSFileEngine);
+ mode_t mode = 0;
+ if (perms & ReadOwnerPerm)
+ mode |= S_IRUSR;
+ if (perms & WriteOwnerPerm)
+ mode |= S_IWUSR;
+ if (perms & ExeOwnerPerm)
+ mode |= S_IXUSR;
+ if (perms & ReadUserPerm)
+ mode |= S_IRUSR;
+ if (perms & WriteUserPerm)
+ mode |= S_IWUSR;
+ if (perms & ExeUserPerm)
+ mode |= S_IXUSR;
+ if (perms & ReadGroupPerm)
+ mode |= S_IRGRP;
+ if (perms & WriteGroupPerm)
+ mode |= S_IWGRP;
+ if (perms & ExeGroupPerm)
+ mode |= S_IXGRP;
+ if (perms & ReadOtherPerm)
+ mode |= S_IROTH;
+ if (perms & WriteOtherPerm)
+ mode |= S_IWOTH;
+ if (perms & ExeOtherPerm)
+ mode |= S_IXOTH;
+ if (d->fd != -1)
+ return !fchmod(d->fd, mode);
+ return !::chmod(d->nativeFilePath.constData(), mode);
+}
+
+bool QFSFileEngine::setSize(qint64 size)
+{
+ Q_D(QFSFileEngine);
+ if (d->fd != -1)
+ return !QT_FTRUNCATE(d->fd, size);
+ return !QT_TRUNCATE(d->nativeFilePath.constData(), size);
+}
+
+QDateTime QFSFileEngine::fileTime(FileTime time) const
+{
+ Q_D(const QFSFileEngine);
+ QDateTime ret;
+ if (d->doStat()) {
+ if (time == CreationTime)
+ ret.setTime_t(d->st.st_ctime ? d->st.st_ctime : d->st.st_mtime);
+ else if (time == ModificationTime)
+ ret.setTime_t(d->st.st_mtime);
+ else if (time == AccessTime)
+ ret.setTime_t(d->st.st_atime);
+ }
+ return ret;
+}
+
+uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
+{
+ Q_Q(QFSFileEngine);
+ Q_UNUSED(flags);
+ if (offset < 0) {
+ q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
+ return 0;
+ }
+ if (openMode == QIODevice::NotOpen) {
+ q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
+ return 0;
+ }
+ int access = 0;
+ if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
+ if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
+
+ int pagesSize = getpagesize();
+ int realOffset = offset / pagesSize;
+ int extra = offset % pagesSize;
+
+ void *mapAddress = mmap((void*)0, (size_t)size + extra,
+ access, MAP_SHARED, nativeHandle(), realOffset * pagesSize);
+ if (MAP_FAILED != mapAddress) {
+ uchar *address = extra + static_cast<uchar*>(mapAddress);
+ maps[address] = QPair<int,int>(extra, size);
+ return address;
+ }
+
+ switch(errno) {
+ case EBADF:
+ q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
+ break;
+ case ENFILE:
+ case ENOMEM:
+ q->setError(QFile::ResourceError, qt_error_string(int(errno)));
+ break;
+ case EINVAL:
+ // size are out of bounds
+ default:
+ q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
+ break;
+ }
+ return 0;
+}
+
+bool QFSFileEnginePrivate::unmap(uchar *ptr)
+{
+ Q_Q(QFSFileEngine);
+ if (!maps.contains(ptr)) {
+ q->setError(QFile::PermissionsError, qt_error_string(EACCES));
+ return false;
+ }
+
+ uchar *start = ptr - maps[ptr].first;
+ int len = maps[ptr].second;
+ if (-1 == munmap(start, len)) {
+ q->setError(QFile::UnspecifiedError, qt_error_string(errno));
+ return false;
+ }
+ maps.remove(ptr);
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FSFILEENGINE
diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp
new file mode 100644
index 0000000..612f916
--- /dev/null
+++ b/src/corelib/io/qfsfileengine_win.cpp
@@ -0,0 +1,2242 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#define _POSIX_
+#include "qplatformdefs.h"
+#include "qabstractfileengine.h"
+#include "private/qfsfileengine_p.h"
+#include <qdebug.h>
+
+#include "qfile.h"
+#include "qdir.h"
+#include "qtemporaryfile.h"
+#ifndef QT_NO_REGEXP
+# include "qregexp.h"
+#endif
+#include "private/qmutexpool_p.h"
+#include "qvarlengtharray.h"
+#include "qdatetime.h"
+#include "qt_windows.h"
+
+#if !defined(Q_OS_WINCE)
+# include <sys/types.h>
+# include <direct.h>
+#else
+# include <types.h>
+#endif
+#include <objbase.h>
+#include <shlobj.h>
+#include <initguid.h>
+#include <accctrl.h>
+#include <ctype.h>
+#include <limits.h>
+#define SECURITY_WIN32
+#ifdef Q_CC_MINGW
+// A workaround for a certain version of MinGW, the define UNICODE_STRING.
+#include <subauth.h>
+#endif
+#include <security.h>
+
+#ifndef _INTPTR_T_DEFINED
+#ifdef _WIN64
+typedef __int64 intptr_t;
+#else
+#ifdef _W64
+typedef _W64 int intptr_t;
+#else
+typedef INT_PTR intptr_t;
+#endif
+#endif
+#define _INTPTR_T_DEFINED
+#endif
+
+#ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES (DWORD (-1))
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static QString readLink(const QString &link);
+
+Q_CORE_EXPORT int qt_ntfs_permission_lookup = 0;
+
+#if defined(Q_OS_WINCE)
+static QString qfsPrivateCurrentDir = QLatin1String("");
+// As none of the functions we try to resolve do exist on Windows CE
+// we use QT_NO_LIBRARY to shorten everything up a little bit.
+#define QT_NO_LIBRARY 1
+#endif
+
+#if !defined(QT_NO_LIBRARY)
+QT_BEGIN_INCLUDE_NAMESPACE
+typedef DWORD (WINAPI *PtrGetNamedSecurityInfoW)(LPWSTR, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID*, PSID*, PACL*, PACL*, PSECURITY_DESCRIPTOR*);
+static PtrGetNamedSecurityInfoW ptrGetNamedSecurityInfoW = 0;
+typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrLookupAccountSidW)(LPCWSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, PSID_NAME_USE);
+static PtrLookupAccountSidW ptrLookupAccountSidW = 0;
+typedef DECLSPEC_IMPORT BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
+static PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = 0;
+typedef VOID (WINAPI *PtrBuildTrusteeWithSidW)(PTRUSTEE_W, PSID);
+static PtrBuildTrusteeWithSidW ptrBuildTrusteeWithSidW = 0;
+typedef VOID (WINAPI *PtrBuildTrusteeWithNameW)(PTRUSTEE_W, unsigned short*);
+static PtrBuildTrusteeWithNameW ptrBuildTrusteeWithNameW = 0;
+typedef DWORD (WINAPI *PtrGetEffectiveRightsFromAclW)(PACL, PTRUSTEE_W, OUT PACCESS_MASK);
+static PtrGetEffectiveRightsFromAclW ptrGetEffectiveRightsFromAclW = 0;
+typedef DECLSPEC_IMPORT PVOID (WINAPI *PtrFreeSid)(PSID);
+static PtrFreeSid ptrFreeSid = 0;
+static TRUSTEE_W currentUserTrusteeW;
+
+typedef BOOL (WINAPI *PtrOpenProcessToken)(HANDLE, DWORD, PHANDLE );
+static PtrOpenProcessToken ptrOpenProcessToken = 0;
+typedef BOOL (WINAPI *PtrGetUserProfileDirectoryW)( HANDLE, LPWSTR, LPDWORD);
+static PtrGetUserProfileDirectoryW ptrGetUserProfileDirectoryW = 0;
+typedef BOOL (WINAPI *PtrSetFilePointerEx)(HANDLE, LARGE_INTEGER, PLARGE_INTEGER, DWORD);
+static PtrSetFilePointerEx ptrSetFilePointerEx = 0;
+QT_END_INCLUDE_NAMESPACE
+
+
+void QFSFileEnginePrivate::resolveLibs()
+{
+ static bool triedResolve = false;
+ if(!triedResolve) {
+ // need to resolve the security info functions
+
+ // protect initialization
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ // check triedResolve again, since another thread may have already
+ // done the initialization
+ if(triedResolve) {
+ // another thread did initialize the security function pointers,
+ // so we shouldn't do it again.
+ return;
+ }
+#endif
+
+ triedResolve = true;
+#if !defined(Q_OS_WINCE)
+ if(QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) {
+ HINSTANCE advapiHnd = LoadLibraryW(L"advapi32");
+ if (advapiHnd) {
+ ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW");
+ ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW");
+ ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid");
+ ptrBuildTrusteeWithSidW = (PtrBuildTrusteeWithSidW)GetProcAddress(advapiHnd, "BuildTrusteeWithSidW");
+ ptrBuildTrusteeWithNameW = (PtrBuildTrusteeWithNameW)GetProcAddress(advapiHnd, "BuildTrusteeWithNameW");
+ ptrGetEffectiveRightsFromAclW = (PtrGetEffectiveRightsFromAclW)GetProcAddress(advapiHnd, "GetEffectiveRightsFromAclW");
+ ptrFreeSid = (PtrFreeSid)GetProcAddress(advapiHnd, "FreeSid");
+ }
+ if (ptrBuildTrusteeWithNameW) {
+ HINSTANCE versionHnd = LoadLibraryW(L"version");
+ if (versionHnd) {
+ typedef DWORD (WINAPI *PtrGetFileVersionInfoSizeW)(LPWSTR lptstrFilename,LPDWORD lpdwHandle);
+ PtrGetFileVersionInfoSizeW ptrGetFileVersionInfoSizeW = (PtrGetFileVersionInfoSizeW)GetProcAddress(versionHnd, "GetFileVersionInfoSizeW");
+ typedef BOOL (WINAPI *PtrGetFileVersionInfoW)(LPWSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData);
+ PtrGetFileVersionInfoW ptrGetFileVersionInfoW = (PtrGetFileVersionInfoW)GetProcAddress(versionHnd, "GetFileVersionInfoW");
+ typedef BOOL (WINAPI *PtrVerQueryValueW)(const LPVOID pBlock,LPWSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen);
+ PtrVerQueryValueW ptrVerQueryValueW = (PtrVerQueryValueW)GetProcAddress(versionHnd, "VerQueryValueW");
+ if(ptrGetFileVersionInfoSizeW && ptrGetFileVersionInfoW && ptrVerQueryValueW) {
+ DWORD fakeHandle;
+ DWORD versionSize = ptrGetFileVersionInfoSizeW(L"secur32.dll", &fakeHandle);
+ if(versionSize) {
+ LPVOID versionData;
+ versionData = malloc(versionSize);
+ if(ptrGetFileVersionInfoW(L"secur32.dll", 0, versionSize, versionData)) {
+ UINT puLen;
+ VS_FIXEDFILEINFO *pLocalInfo;
+ if(ptrVerQueryValueW(versionData, L"\\", (void**)&pLocalInfo, &puLen)) {
+ WORD wVer1, wVer2, wVer3, wVer4;
+ wVer1 = HIWORD(pLocalInfo->dwFileVersionMS);
+ wVer2 = LOWORD(pLocalInfo->dwFileVersionMS);
+ wVer3 = HIWORD(pLocalInfo->dwFileVersionLS);
+ wVer4 = LOWORD(pLocalInfo->dwFileVersionLS);
+ // It will not work with secur32.dll version 5.0.2195.2862
+ if(!(wVer1 == 5 && wVer2 == 0 && wVer3 == 2195 && (wVer4 == 2862 || wVer4 == 4587))) {
+ HINSTANCE userHnd = LoadLibraryW(L"secur32");
+ if (userHnd) {
+ typedef BOOL (WINAPI *PtrGetUserNameExW)(EXTENDED_NAME_FORMAT nameFormat, ushort* lpBuffer, LPDWORD nSize);
+ PtrGetUserNameExW ptrGetUserNameExW = (PtrGetUserNameExW)GetProcAddress(userHnd, "GetUserNameExW");
+ if(ptrGetUserNameExW) {
+ static TCHAR buffer[258];
+ DWORD bufferSize = 257;
+ ptrGetUserNameExW(NameSamCompatible, (ushort*)buffer, &bufferSize);
+ ptrBuildTrusteeWithNameW(&currentUserTrusteeW, (ushort*)buffer);
+ }
+ FreeLibrary(userHnd);
+ }
+ }
+ }
+ }
+ free(versionData);
+ }
+ }
+ FreeLibrary(versionHnd);
+ }
+ }
+ ptrOpenProcessToken = (PtrOpenProcessToken)GetProcAddress(advapiHnd, "OpenProcessToken");
+ HINSTANCE userenvHnd = LoadLibraryW(L"userenv");
+ if (userenvHnd) {
+ ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW");
+ }
+ HINSTANCE kernelHnd = LoadLibraryW(L"kernel32");
+ if (kernelHnd)
+ ptrSetFilePointerEx = (PtrSetFilePointerEx)GetProcAddress(kernelHnd, "SetFilePointerEx");
+ }
+#endif
+ }
+}
+#endif // QT_NO_LIBRARY
+
+// UNC functions NT
+typedef DWORD (WINAPI *PtrNetShareEnum_NT)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD);
+static PtrNetShareEnum_NT ptrNetShareEnum_NT = 0;
+typedef DWORD (WINAPI *PtrNetApiBufferFree_NT)(LPVOID);
+static PtrNetApiBufferFree_NT ptrNetApiBufferFree_NT = 0;
+typedef struct _SHARE_INFO_1_NT {
+ LPWSTR shi1_netname;
+ DWORD shi1_type;
+ LPWSTR shi1_remark;
+} SHARE_INFO_1_NT;
+
+
+bool QFSFileEnginePrivate::resolveUNCLibs_NT()
+{
+ static bool triedResolve = false;
+ if (!triedResolve) {
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ if (triedResolve) {
+ return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
+ }
+#endif
+ triedResolve = true;
+#if !defined(Q_OS_WINCE)
+ HINSTANCE hLib = LoadLibraryW(L"Netapi32");
+ if (hLib) {
+ ptrNetShareEnum_NT = (PtrNetShareEnum_NT)GetProcAddress(hLib, "NetShareEnum");
+ if (ptrNetShareEnum_NT)
+ ptrNetApiBufferFree_NT = (PtrNetApiBufferFree_NT)GetProcAddress(hLib, "NetApiBufferFree");
+ }
+#endif
+ }
+ return ptrNetShareEnum_NT && ptrNetApiBufferFree_NT;
+}
+
+// UNC functions 9x
+typedef DWORD (WINAPI *PtrNetShareEnum_9x)(const char FAR *, short, char FAR *, unsigned short, unsigned short FAR *, unsigned short FAR *);
+static PtrNetShareEnum_9x ptrNetShareEnum_9x = 0;
+#ifdef LM20_NNLEN
+# define LM20_NNLEN_9x LM20_NNLEN
+#else
+# define LM20_NNLEN_9x 12
+#endif
+typedef struct _SHARE_INFO_1_9x {
+ char shi1_netname[LM20_NNLEN_9x+1];
+ char shi1_pad1;
+ unsigned short shi1_type;
+ char FAR* shi1_remark;
+} SHARE_INFO_1_9x;
+
+bool QFSFileEnginePrivate::resolveUNCLibs_9x()
+{
+ static bool triedResolve = false;
+ if (!triedResolve) {
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ if (triedResolve) {
+ return ptrNetShareEnum_9x;
+ }
+#endif
+ triedResolve = true;
+#if !defined(Q_OS_WINCE)
+ HINSTANCE hLib = LoadLibraryA("Svrapi");
+ if (hLib)
+ ptrNetShareEnum_9x = (PtrNetShareEnum_9x)GetProcAddress(hLib, "NetShareEnum");
+#endif
+ }
+ return ptrNetShareEnum_9x;
+}
+
+bool QFSFileEnginePrivate::uncListSharesOnServer(const QString &server, QStringList *list)
+{
+ if (resolveUNCLibs_NT()) {
+ SHARE_INFO_1_NT *BufPtr, *p;
+ DWORD res;
+ DWORD er=0,tr=0,resume=0, i;
+ do {
+ res = ptrNetShareEnum_NT((wchar_t*)server.utf16(), 1, (LPBYTE *)&BufPtr, DWORD(-1), &er, &tr, &resume);
+ if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA) {
+ p=BufPtr;
+ for (i = 1; i <= er; ++i) {
+ if (list && p->shi1_type == 0)
+ list->append(QString::fromUtf16((unsigned short *)p->shi1_netname));
+ p++;
+ }
+ }
+ ptrNetApiBufferFree_NT(BufPtr);
+ } while (res==ERROR_MORE_DATA);
+ return res == ERROR_SUCCESS;
+
+ } else if (resolveUNCLibs_9x()) {
+ SHARE_INFO_1_9x *pBuf = 0;
+ short cbBuffer;
+ unsigned short nEntriesRead = 0;
+ unsigned short nTotalEntries = 0;
+ short numBuffs = 20;
+ DWORD nStatus = 0;
+ do {
+ cbBuffer = numBuffs * sizeof(SHARE_INFO_1_9x);
+ pBuf = (SHARE_INFO_1_9x *)malloc(cbBuffer);
+ if (pBuf) {
+ nStatus = ptrNetShareEnum_9x(server.toLocal8Bit().constData(), 1, (char FAR *)pBuf, cbBuffer, &nEntriesRead, &nTotalEntries);
+ if ((nStatus == ERROR_SUCCESS)) {
+ for (int i = 0; i < nEntriesRead; ++i) {
+ if (list && pBuf[i].shi1_type == 0)
+ list->append(QString::fromLocal8Bit(pBuf[i].shi1_netname));
+ }
+ free(pBuf);
+ break;
+ }
+ free(pBuf);
+ numBuffs *=2;
+ }
+ } while (nStatus == ERROR_MORE_DATA);
+ return nStatus == ERROR_SUCCESS;
+ }
+ return false;
+}
+
+static bool isUncRoot(const QString &server)
+{
+ QString localPath = QDir::toNativeSeparators(server);
+ QStringList parts = localPath.split(QLatin1Char('\\'), QString::SkipEmptyParts);
+ return localPath.startsWith(QLatin1String("\\\\")) && parts.count() <= 1;
+}
+
+static bool isUncPath(const QString &path)
+{
+ // Starts with // or \\, but not \\. or //.
+ return (path.startsWith(QLatin1String("//"))
+ || path.startsWith(QLatin1String("\\\\")))
+ && (path.size() > 2 && path.at(2) != QLatin1Char('.'));
+}
+
+static bool isRelativePath(const QString &path)
+{
+ return !(path.startsWith(QLatin1Char('/'))
+ || (path.length() >= 2
+ && ((path.at(0).isLetter() && path.at(1) == QLatin1Char(':'))
+ || (path.at(0) == QLatin1Char('/') && path.at(1) == QLatin1Char('/'))))); // drive, e.g. a:
+}
+
+static QString fixIfRelativeUncPath(const QString &path)
+{
+ if (isRelativePath(path)) {
+ QString currentPath = QDir::currentPath() + QLatin1Char('/');
+ if (currentPath.startsWith(QLatin1String("//")))
+ return QString(path).prepend(currentPath);
+ }
+ return path;
+}
+
+// can be //server or //server/share
+static bool uncShareExists(const QString &server)
+{
+ QStringList parts = server.split(QLatin1Char('\\'), QString::SkipEmptyParts);
+ if (parts.count()) {
+ QStringList shares;
+ if (QFSFileEnginePrivate::uncListSharesOnServer(QLatin1String("\\\\") + parts.at(0), &shares)) {
+ if (parts.count() >= 2)
+ return shares.contains(parts.at(1), Qt::CaseInsensitive);
+ else
+ return true;
+ }
+ }
+ return false;
+}
+
+#if !defined(Q_OS_WINCE)
+// If you change this function, remember to also change the UNICODE version
+static QString nativeAbsoluteFilePathA(const QString &path)
+{
+ QString ret;
+ QVarLengthArray<char, MAX_PATH> buf(MAX_PATH);
+ char *fileName = 0;
+ QByteArray ba = path.toLocal8Bit();
+ DWORD retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
+ if (retLen > (DWORD)buf.size()) {
+ buf.resize(retLen);
+ retLen = GetFullPathNameA(ba.constData(), buf.size(), buf.data(), &fileName);
+ }
+ if (retLen != 0)
+ ret = QString::fromLocal8Bit(buf.data(), retLen);
+ return ret;
+}
+#endif
+
+// If you change this function, remember to also change the NON-UNICODE version
+static QString nativeAbsoluteFilePathW(const QString &path)
+{
+ QString ret;
+#if !defined(Q_OS_WINCE)
+ QVarLengthArray<wchar_t, MAX_PATH> buf(MAX_PATH);
+ wchar_t *fileName = 0;
+ DWORD retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
+ if (retLen > (DWORD)buf.size()) {
+ buf.resize(retLen);
+ retLen = GetFullPathNameW((wchar_t*)path.utf16(), buf.size(), buf.data(), &fileName);
+ }
+ if (retLen != 0)
+ ret = QString::fromUtf16((unsigned short *)buf.data(), retLen);
+#else
+ if (path.startsWith(QLatin1String("/")) || path.startsWith(QLatin1String("\\")))
+ ret = QDir::toNativeSeparators(path);
+ else
+ ret = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path));
+#endif
+ return ret;
+}
+
+static QString nativeAbsoluteFilePath(const QString &path)
+{
+ QString absPath = QT_WA_INLINE(nativeAbsoluteFilePathW(path), nativeAbsoluteFilePathA(path));
+ // This is really ugly, but GetFullPathName strips off whitespace at the end.
+ // If you for instance write ". " in the lineedit of QFileDialog,
+ // (which is an invalid filename) this function will strip the space off and viola,
+ // the file is later reported as existing. Therefore, we re-add the whitespace that
+ // was at the end of path in order to keep the filename invalid.
+ int i = path.size() - 1;
+ while (i >= 0 && path.at(i) == QLatin1Char(' ')) --i;
+ int extraws = path.size() - 1 - i;
+ if (extraws >= 0) {
+ while (extraws) {
+ absPath.append(QLatin1Char(' '));
+ --extraws;
+ }
+ }
+ return absPath;
+}
+
+QByteArray QFSFileEnginePrivate::win95Name(const QString &path)
+{
+ QString ret(path);
+ if(path.length() > 1 && path[0] == QLatin1Char('/') && path[1] == QLatin1Char('/')) {
+ // Win95 cannot handle slash-slash needs slosh-slosh.
+ ret[0] = QLatin1Char('\\');
+ ret[1] = QLatin1Char('\\');
+ int n = ret.indexOf(QLatin1Char('/'));
+ if(n >= 0)
+ ret[n] = QLatin1Char('\\');
+ } else if(path.length() > 3 && path[2] == QLatin1Char('/') && path[3] == QLatin1Char('/')) {
+ ret[2] = QLatin1Char('\\');
+ ret.remove(3, 1);
+ int n = ret.indexOf(QLatin1Char('/'));
+ if(n >= 0)
+ ret[n] = QLatin1Char('\\');
+ }
+ return ret.toLocal8Bit();
+}
+
+/*!
+ \internal
+*/
+QString QFSFileEnginePrivate::longFileName(const QString &path)
+{
+ if (path.startsWith(QLatin1String("\\\\.\\")))
+ return path;
+
+ QString absPath = nativeAbsoluteFilePath(path);
+#if !defined(Q_OS_WINCE)
+ QString prefix = QLatin1String("\\\\?\\");
+ if (isUncPath(path)) {
+ prefix = QLatin1String("\\\\?\\UNC\\");
+ absPath.remove(0, 2);
+ }
+ return prefix + absPath;
+#else
+ return absPath;
+#endif
+}
+
+/*
+ \internal
+*/
+void QFSFileEnginePrivate::nativeInitFileName()
+{
+ QT_WA({
+ QString path = longFileName(QDir::toNativeSeparators(fixIfRelativeUncPath(filePath)));
+ nativeFilePath = QByteArray((const char *)path.utf16(), path.size() * 2 + 1);
+ }, {
+ QString path = fixIfRelativeUncPath(filePath);
+ nativeFilePath = win95Name(path).replace('/', '\\');
+ });
+}
+
+/*
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
+{
+ Q_Q(QFSFileEngine);
+
+ // All files are opened in share mode (both read and write).
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ int accessRights = 0;
+ if (openMode & QIODevice::ReadOnly)
+ accessRights |= GENERIC_READ;
+ if (openMode & QIODevice::WriteOnly)
+ accessRights |= GENERIC_WRITE;
+
+ SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
+
+ // WriteOnly can create files, ReadOnly cannot.
+ DWORD creationDisp = (openMode & QIODevice::WriteOnly)
+ ? OPEN_ALWAYS : OPEN_EXISTING;
+
+ // Create the file handle.
+ QT_WA({
+ fileHandle = CreateFileW((TCHAR *)nativeFilePath.constData(),
+ accessRights,
+ shareMode,
+ &securityAtts,
+ creationDisp,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }, {
+ fileHandle = CreateFileA(nativeFilePath.constData(),
+ accessRights,
+ shareMode,
+ &securityAtts,
+ creationDisp,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ });
+
+ // Bail out on error.
+ if (fileHandle == INVALID_HANDLE_VALUE) {
+ q->setError(QFile::OpenError, qt_error_string());
+ return false;
+ }
+
+ // Truncate the file after successfully opening it if Truncate is passed.
+ if (openMode & QIODevice::Truncate)
+ q->setSize(0);
+
+ return true;
+}
+
+/*
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeClose()
+{
+ Q_Q(QFSFileEngine);
+ if (fh || fd != -1) {
+ // stdlib / stdio mode.
+ return closeFdFh();
+ }
+
+ // Windows native mode.
+ bool ok = true;
+ if ((fileHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileHandle))
+#ifdef Q_USE_DEPRECATED_MAP_API
+ && (fileMapHandle == INVALID_HANDLE_VALUE || !CloseHandle(fileMapHandle))
+#endif
+ ) {
+ q->setError(QFile::UnspecifiedError, qt_error_string());
+ ok = false;
+ }
+ fileHandle = INVALID_HANDLE_VALUE;
+ cachedFd = -1; // gets closed by CloseHandle above
+
+ return ok;
+}
+
+/*
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeFlush()
+{
+ if (fh) {
+ // Buffered stdlib mode.
+ return flushFh();
+ }
+ if (fd != -1) {
+ // Unbuffered stdio mode; always succeeds (no buffer).
+ return true;
+ }
+
+ // Windows native mode; flushing is
+ // unnecessary. FlushFileBuffers(), the equivalent of sync() or
+ // fsync() on Unix, does a low-level flush to the disk, and we
+ // don't expose an API for this.
+ return true;
+}
+
+/*
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeSize() const
+{
+ Q_Q(const QFSFileEngine);
+ QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
+
+ // ### Don't flush; for buffered files, we should get away with ftell.
+ thatQ->flush();
+
+ // Buffered stdlib mode.
+ if (fh) {
+ QT_OFF_T oldPos = QT_FTELL(fh);
+ QT_FSEEK(fh, 0, SEEK_END);
+ QT_OFF_T fileSize = QT_FTELL(fh);
+ QT_FSEEK(fh, oldPos, SEEK_SET);
+ return qint64(fileSize);
+ }
+
+ // Not-open mode, where the file name is known: We'll check the
+ // file system directly.
+ if (openMode == QIODevice::NotOpen && !nativeFilePath.isEmpty()) {
+ bool ok = false;
+ WIN32_FILE_ATTRIBUTE_DATA attribData;
+ QT_WA({
+ ok = ::GetFileAttributesExW((TCHAR *)nativeFilePath.constData(),
+ GetFileExInfoStandard, &attribData);
+ } , {
+ ok = ::GetFileAttributesExA(nativeFilePath.constData(),
+ GetFileExInfoStandard, &attribData);
+ });
+ if (ok) {
+ qint64 size = attribData.nFileSizeHigh;
+ size <<= 32;
+ size += attribData.nFileSizeLow;
+ return size;
+ }
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+ // Unbuffed stdio mode.
+ if(fd != -1) {
+#if !defined(Q_OS_WINCE)
+ HANDLE handle = (HANDLE)_get_osfhandle(fd);
+ if (handle != INVALID_HANDLE_VALUE) {
+ BY_HANDLE_FILE_INFORMATION fileInfo;
+ if (GetFileInformationByHandle(handle, &fileInfo)) {
+ qint64 size = fileInfo.nFileSizeHigh;
+ size <<= 32;
+ size += fileInfo.nFileSizeLow;
+ return size;
+ }
+ }
+#endif
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+ // Windows native mode.
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return 0;
+
+ BY_HANDLE_FILE_INFORMATION fileInfo;
+ if (!GetFileInformationByHandle(fileHandle, &fileInfo)) {
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+ qint64 size = fileInfo.nFileSizeHigh;
+ size <<= 32;
+ size += fileInfo.nFileSizeLow;
+ return size;
+}
+
+/*
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativePos() const
+{
+ Q_Q(const QFSFileEngine);
+ QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
+
+ if (fh || fd != -1) {
+ // stdlib / stido mode.
+ return posFdFh();
+ }
+
+ // Windows native mode.
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return 0;
+
+#if !defined(QT_NO_LIBRARY)
+ QFSFileEnginePrivate::resolveLibs();
+ if (!ptrSetFilePointerEx) {
+#endif
+ DWORD newFilePointer = SetFilePointer(fileHandle, 0, NULL, FILE_CURRENT);
+ if (newFilePointer == 0xFFFFFFFF) {
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+ // Note: returns <4GB; does not work with large files. This is the
+ // case for MOC, UIC, qmake and other bootstrapped tools, and for
+ // Win9x/ME.
+ return qint64(newFilePointer);
+#if !defined(QT_NO_LIBRARY)
+ }
+
+ // This approach supports large files.
+ LARGE_INTEGER currentFilePos;
+ LARGE_INTEGER offset;
+ offset.LowPart = 0;
+ offset.HighPart = 0;
+ if (!ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_CURRENT)) {
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+ return qint64(currentFilePos.QuadPart);
+#endif
+}
+
+/*
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeSeek(qint64 pos)
+{
+ Q_Q(const QFSFileEngine);
+ QFSFileEngine *thatQ = const_cast<QFSFileEngine *>(q);
+
+ if (fh || fd != -1) {
+ // stdlib / stdio mode.
+ return seekFdFh(pos);
+ }
+
+#if !defined(QT_NO_LIBRARY)
+ QFSFileEnginePrivate::resolveLibs();
+ if (!ptrSetFilePointerEx) {
+#endif
+ LONG seekToPos = LONG(pos); // <- lossy
+ DWORD newFilePointer = SetFilePointer(fileHandle, seekToPos, NULL, FILE_BEGIN);
+ if (newFilePointer == 0xFFFFFFFF) {
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return false;
+ }
+
+ // Note: does not work with large files. This is the case for MOC,
+ // UIC, qmake and other bootstrapped tools, and for Win9x/ME.
+ return true;
+#if !defined(QT_NO_LIBRARY)
+ }
+
+ // This approach supports large files.
+ LARGE_INTEGER currentFilePos;
+ LARGE_INTEGER offset;
+ offset.LowPart = (unsigned int)(quint64(pos) & Q_UINT64_C(0xffffffff));
+ offset.HighPart = (unsigned int)((quint64(pos) >> 32) & Q_UINT64_C(0xffffffff));
+ if (ptrSetFilePointerEx(fileHandle, offset, &currentFilePos, FILE_BEGIN) == 0) {
+ thatQ->setError(QFile::UnspecifiedError, qt_error_string());
+ return false;
+ }
+ return true;
+#endif
+}
+
+/*
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen)
+{
+ Q_Q(QFSFileEngine);
+
+ if (fh || fd != -1) {
+ // stdio / stdlib mode.
+ if (fh && nativeIsSequential() && feof(fh)) {
+ q->setError(QFile::ReadError, qt_error_string(int(errno)));
+ return -1;
+ }
+
+ return readFdFh(data, maxlen);
+ }
+
+ // Windows native mode.
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return -1;
+
+ DWORD bytesToRead = DWORD(maxlen); // <- lossy
+
+ // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
+ // the chunks are too large, so we limit the block size to 32MB.
+ static const DWORD maxBlockSize = 32 * 1024 * 1024;
+
+ qint64 totalRead = 0;
+ do {
+ DWORD blockSize = qMin<DWORD>(bytesToRead, maxBlockSize);
+ DWORD bytesRead;
+ if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) {
+ if (totalRead == 0) {
+ // Note: only return failure if the first ReadFile fails.
+ q->setError(QFile::ReadError, qt_error_string());
+ return -1;
+ }
+ break;
+ }
+ if (bytesRead == 0)
+ break;
+ totalRead += bytesRead;
+ bytesToRead -= bytesRead;
+ } while (totalRead < maxlen);
+ return qint64(totalRead);
+}
+
+/*
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeReadLine(char *data, qint64 maxlen)
+{
+ Q_Q(QFSFileEngine);
+
+ if (fh || fd != -1) {
+ // stdio / stdlib mode.
+ return readLineFdFh(data, maxlen);
+ }
+
+ // Windows native mode.
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return -1;
+
+ // ### No equivalent in Win32?
+ return q->QAbstractFileEngine::readLine(data, maxlen);
+}
+
+/*
+ \internal
+*/
+qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
+{
+ Q_Q(QFSFileEngine);
+
+ if (fh || fd != -1) {
+ // stdio / stdlib mode.
+ return writeFdFh(data, len);
+ }
+
+ // Windows native mode.
+ if (fileHandle == INVALID_HANDLE_VALUE)
+ return -1;
+
+ qint64 bytesToWrite = DWORD(len); // <- lossy
+
+ // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
+ // the chunks are too large, so we limit the block size to 32MB.
+ static const DWORD maxBlockSize = 32 * 1024 * 1024;
+
+ qint64 totalWritten = 0;
+ do {
+ DWORD blockSize = qMin<DWORD>(bytesToWrite, maxBlockSize);
+ DWORD bytesWritten;
+ if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) {
+ if (totalWritten == 0) {
+ // Note: Only return error if the first WriteFile failed.
+ q->setError(QFile::WriteError, qt_error_string());
+ return -1;
+ }
+ break;
+ }
+ if (bytesWritten == 0)
+ break;
+ totalWritten += bytesWritten;
+ bytesToWrite -= bytesWritten;
+ } while (totalWritten < len);
+ return qint64(totalWritten);
+}
+
+/*
+ \internal
+*/
+int QFSFileEnginePrivate::nativeHandle() const
+{
+ if (fh || fd != -1)
+ return fh ? QT_FILENO(fh) : fd;
+#ifndef Q_OS_WINCE
+ if (cachedFd != -1)
+ return cachedFd;
+
+ int flags = 0;
+ if (openMode & QIODevice::Append)
+ flags |= _O_APPEND;
+ if (!(openMode & QIODevice::WriteOnly))
+ flags |= _O_RDONLY;
+ cachedFd = _open_osfhandle((intptr_t) fileHandle, flags);
+ return cachedFd;
+#else
+ return -1;
+#endif
+}
+
+/*
+ \internal
+*/
+bool QFSFileEnginePrivate::nativeIsSequential() const
+{
+#if !defined(Q_OS_WINCE)
+ // stdlib / Windows native mode.
+ if (fh || fileHandle != INVALID_HANDLE_VALUE) {
+ if (fh == stdin || fh == stdout || fh == stderr)
+ return true;
+
+ HANDLE handle = fileHandle;
+ if (fileHandle == INVALID_HANDLE_VALUE) {
+ // Rare case: using QFile::open(FILE*) to open a pipe.
+ handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
+ return false;
+ }
+
+ DWORD fileType = GetFileType(handle);
+ return fileType == FILE_TYPE_PIPE;
+ }
+
+ // stdio mode.
+ if (fd != -1)
+ return isSequentialFdFh();
+#endif
+ return false;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::remove()
+{
+ Q_D(QFSFileEngine);
+ QT_WA({
+ return ::DeleteFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16()) != 0;
+ } , {
+ return ::DeleteFileA(QFSFileEnginePrivate::win95Name(d->filePath)) != 0;
+ });
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::copy(const QString &copyName)
+{
+ Q_D(QFSFileEngine);
+ QT_WA({
+ return ::CopyFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
+ (TCHAR*)QFSFileEnginePrivate::longFileName(copyName).utf16(), true) != 0;
+ } , {
+ return ::CopyFileA(QFSFileEnginePrivate::win95Name(d->filePath),
+ QFSFileEnginePrivate::win95Name(copyName), true) != 0;
+ });
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::rename(const QString &newName)
+{
+ Q_D(QFSFileEngine);
+ QT_WA({
+ return ::MoveFileW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(),
+ (TCHAR*)QFSFileEnginePrivate::longFileName(newName).utf16()) != 0;
+ } , {
+ return ::MoveFileA(QFSFileEnginePrivate::win95Name(d->filePath),
+ QFSFileEnginePrivate::win95Name(newName)) != 0;
+ });
+}
+
+static inline bool mkDir(const QString &path)
+{
+#if defined(Q_OS_WINCE)
+ // Unfortunately CreateDirectory returns true for paths longer than
+ // 256, but does not create a directory. It starts to fail, when
+ // path length > MAX_PATH, which is 260 usually on CE.
+ // This only happens on a Windows Mobile device. Windows CE seems
+ // not to be affected by this.
+ static int platformId = 0;
+ if (platformId == 0) {
+ wchar_t platformString[64];
+ if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(platformString)/sizeof(*platformString),platformString,0)) {
+ if (0 == wcscmp(platformString, L"PocketPC") || 0 == wcscmp(platformString, L"Smartphone"))
+ platformId = 1;
+ else
+ platformId = 2;
+ }
+ }
+ if (platformId == 1 && QFSFileEnginePrivate::longFileName(path).size() > 256)
+ return false;
+#endif
+ QT_WA({
+ return ::CreateDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16(), 0);
+ } , {
+ return ::CreateDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()), 0);
+ });
+}
+
+/*!
+ \reimp
+*/
+static inline bool rmDir(const QString &path)
+{
+ QT_WA({
+ return ::RemoveDirectoryW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
+ } , {
+ return ::RemoveDirectoryA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
+ });
+}
+
+/*!
+ \reimp
+*/
+static inline bool isDirPath(const QString &dirPath, bool *existed)
+{
+ QString path = dirPath;
+ if (path.length() == 2 &&path.at(1) == QLatin1Char(':'))
+ path += QLatin1Char('\\');
+
+ DWORD fileAttrib = INVALID_FILE_ATTRIBUTES;
+ QT_WA({
+ fileAttrib = ::GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(path).utf16());
+ } , {
+ fileAttrib = ::GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(path).absoluteFilePath()));
+ });
+
+ if (existed)
+ *existed = fileAttrib != INVALID_FILE_ATTRIBUTES;
+
+ if (fileAttrib == INVALID_FILE_ATTRIBUTES)
+ return false;
+
+ return fileAttrib & FILE_ATTRIBUTE_DIRECTORY;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
+{
+ QString dirName = name;
+ if (createParentDirectories) {
+ dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
+ // We spefically search for / so \ would break it..
+ int oldslash = -1;
+ if (dirName.startsWith(QLatin1String("\\\\"))) {
+ // Don't try to create the root path of a UNC path;
+ // CreateDirectory() will just return ERROR_INVALID_NAME.
+ for (int i = 0; i < dirName.size(); ++i) {
+ if (dirName.at(i) != QDir::separator()) {
+ oldslash = i;
+ break;
+ }
+ }
+ if (oldslash != -1)
+ oldslash = dirName.indexOf(QDir::separator(), oldslash);
+ }
+ for (int slash=0; slash != -1; oldslash = slash) {
+ slash = dirName.indexOf(QDir::separator(), oldslash+1);
+ if (slash == -1) {
+ if(oldslash == dirName.length())
+ break;
+ slash = dirName.length();
+ }
+ if (slash) {
+ QString chunk = dirName.left(slash);
+ bool existed = false;
+ if (!isDirPath(chunk, &existed) && !existed) {
+ if (!mkDir(chunk))
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return mkDir(name);
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
+{
+ QString dirName = name;
+ if (recurseParentDirectories) {
+ dirName = QDir::toNativeSeparators(QDir::cleanPath(dirName));
+ for (int oldslash = 0, slash=dirName.length(); slash > 0; oldslash = slash) {
+ QString chunk = dirName.left(slash);
+ if (chunk.length() == 2 && chunk.at(0).isLetter() && chunk.at(1) == QLatin1Char(':'))
+ break;
+ if (!isDirPath(chunk, 0))
+ return false;
+ if (!rmDir(chunk))
+ return oldslash != 0;
+ slash = dirName.lastIndexOf(QDir::separator(), oldslash-1);
+ }
+ return true;
+ }
+ return rmDir(name);
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::caseSensitive() const
+{
+ return false;
+}
+
+/*!
+ Sets the current path (e.g., for QDir), to \a path. Returns true if the
+ new path exists; otherwise this function does nothing, and returns false.
+
+ \sa currentPath()
+*/
+bool QFSFileEngine::setCurrentPath(const QString &path)
+{
+ if (!QDir(path).exists())
+ return false;
+
+#if !defined(Q_OS_WINCE)
+ int r;
+ QT_WA({
+ r = ::SetCurrentDirectoryW((WCHAR*)path.utf16());
+ } , {
+ r = ::SetCurrentDirectoryA(QFSFileEnginePrivate::win95Name(path));
+ });
+ return r != 0;
+#else
+ qfsPrivateCurrentDir = QFSFileEnginePrivate::longFileName(path);
+ return true;
+#endif
+}
+
+/*!
+ Returns the canonicalized form of the current path used by the file
+ engine for the drive specified by \a fileName.
+
+ On Windows, each drive has its own current directory, so a different
+ path is returned for file names that include different drive names
+ (e.g. A: or C:).
+
+ \sa setCurrentPath()
+*/
+QString QFSFileEngine::currentPath(const QString &fileName)
+{
+#if !defined(Q_OS_WINCE)
+ QString ret;
+ //if filename is a drive: then get the pwd of that drive
+ if (fileName.length() >= 2 &&
+ fileName.at(0).isLetter() && fileName.at(1) == QLatin1Char(':')) {
+ int drv = fileName.toUpper().at(0).toLatin1() - 'A' + 1;
+ if (_getdrive() != drv) {
+ QT_WA({
+ TCHAR buf[PATH_MAX];
+ ::_wgetdcwd(drv, buf, PATH_MAX);
+ ret.setUtf16((ushort*)buf, uint(::wcslen(buf)));
+ }, {
+ char buf[PATH_MAX];
+ ::_getdcwd(drv, buf, PATH_MAX);
+ ret = QString::fromLatin1(buf);
+ });
+ }
+ }
+ if (ret.isEmpty()) {
+ //just the pwd
+ QT_WA({
+ DWORD size = 0;
+ WCHAR currentName[PATH_MAX];
+ size = ::GetCurrentDirectoryW(PATH_MAX, currentName);
+ if (size !=0) {
+ if (size > PATH_MAX) {
+ WCHAR * newCurrentName = new WCHAR[size];
+ if (::GetCurrentDirectoryW(PATH_MAX, newCurrentName) != 0)
+ ret = QString::fromUtf16((ushort*)newCurrentName);
+ delete [] newCurrentName;
+ } else {
+ ret = QString::fromUtf16((ushort*)currentName);
+ }
+ }
+ } , {
+ DWORD size = 0;
+ char currentName[PATH_MAX];
+ size = ::GetCurrentDirectoryA(PATH_MAX, currentName);
+ if (size !=0)
+ ret = QString::fromLocal8Bit(currentName);
+ });
+ }
+ if (ret.length() >= 2 && ret[1] == QLatin1Char(':'))
+ ret[0] = ret.at(0).toUpper(); // Force uppercase drive letters.
+ return QDir::fromNativeSeparators(ret);
+#else
+ Q_UNUSED(fileName);
+ if (qfsPrivateCurrentDir.isEmpty())
+ qfsPrivateCurrentDir = QCoreApplication::applicationDirPath();
+
+ return QDir::fromNativeSeparators(qfsPrivateCurrentDir);
+#endif
+}
+
+/*!
+ Returns the home path of the current user.
+
+ \sa rootPath()
+*/
+QString QFSFileEngine::homePath()
+{
+ QString ret;
+#if !defined(QT_NO_LIBRARY)
+ QT_WA (
+ {
+ QFSFileEnginePrivate::resolveLibs();
+ if (ptrOpenProcessToken && ptrGetUserProfileDirectoryW) {
+ HANDLE hnd = ::GetCurrentProcess();
+ HANDLE token = 0;
+ BOOL ok = ::ptrOpenProcessToken(hnd, TOKEN_QUERY, &token);
+ if (ok) {
+ DWORD dwBufferSize = 0;
+ // First call, to determine size of the strings (with '\0').
+ ok = ::ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize);
+ if (!ok && dwBufferSize != 0) { // We got the required buffer size
+ wchar_t *userDirectory = new wchar_t[dwBufferSize];
+ // Second call, now we can fill the allocated buffer.
+ ok = ::ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize);
+ if (ok)
+ ret = QString::fromUtf16((ushort*)userDirectory);
+
+ delete [] userDirectory;
+ }
+ ::CloseHandle(token);
+ }
+ }
+ }
+ ,
+ {
+ // GetUserProfileDirectory is only available from NT 4.0,
+ // so fall through for Win98 and friends version.
+ })
+#endif
+ if(ret.isEmpty() || !QFile::exists(ret)) {
+ ret = QString::fromLocal8Bit(qgetenv("USERPROFILE").constData());
+ if(ret.isEmpty() || !QFile::exists(ret)) {
+ ret = QString::fromLocal8Bit(qgetenv("HOMEDRIVE").constData()) + QString::fromLocal8Bit(qgetenv("HOMEPATH").constData());
+ if(ret.isEmpty() || !QFile::exists(ret)) {
+ ret = QString::fromLocal8Bit(qgetenv("HOME").constData());
+ if(ret.isEmpty() || !QFile::exists(ret)) {
+#if defined(Q_OS_WINCE)
+ ret = QString::fromLatin1("\\My Documents");
+ if (!QFile::exists(ret))
+#endif
+ ret = rootPath();
+ }
+ }
+ }
+ }
+ return QDir::fromNativeSeparators(ret);
+}
+
+/*!
+ Returns the root path.
+
+ \sa homePath()
+*/
+QString QFSFileEngine::rootPath()
+{
+#if defined(Q_OS_WINCE)
+ QString ret = QString::fromLatin1("/");
+#elif defined(Q_FS_FAT)
+ QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData());
+ if(ret.isEmpty())
+ ret = QLatin1String("c:");
+ ret += QLatin1String("/");
+#elif defined(Q_OS_OS2EMX)
+ char dir[4];
+ _abspath(dir, QLatin1String("/"), _MAX_PATH);
+ QString ret(dir);
+#endif
+ return ret;
+}
+
+/*!
+ Returns the temporary path (i.e., a path in which it is safe to store
+ temporary files).
+*/
+QString QFSFileEngine::tempPath()
+{
+ QString ret;
+ int success;
+ QT_WA({
+ wchar_t tempPath[MAX_PATH];
+ success = GetTempPathW(MAX_PATH, tempPath);
+ ret = QString::fromUtf16((ushort*)tempPath);
+ } , {
+ char tempPath[MAX_PATH];
+ success = GetTempPathA(MAX_PATH, tempPath);
+ ret = QString::fromLocal8Bit(tempPath);
+ });
+ if (ret.isEmpty() || !success) {
+#if !defined(Q_OS_WINCE)
+ ret = QString::fromLatin1("c:/tmp");
+#else
+ ret = QString::fromLatin1("\\Temp");
+#endif
+ } else {
+ ret = QDir::fromNativeSeparators(ret);
+ while (ret.at(ret.length()-1) == QLatin1Char('/'))
+ ret = ret.left(ret.length()-1);
+ }
+ return ret;
+}
+
+/*!
+ Returns the list of drives in the file system as a list of QFileInfo
+ objects. On unix, Mac OS X and Windows CE, only the root path is returned.
+ On Windows, this function returns all drives (A:\, C:\, D:\, etc.).
+*/
+QFileInfoList QFSFileEngine::drives()
+{
+ QFileInfoList ret;
+#if !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN32)
+ quint32 driveBits = (quint32) GetLogicalDrives() & 0x3ffffff;
+#elif defined(Q_OS_OS2EMX)
+ quint32 driveBits, cur;
+ if(DosQueryCurrentDisk(&cur,&driveBits) != NO_ERROR)
+ exit(1);
+ driveBits &= 0x3ffffff;
+#endif
+ char driveName[4];
+
+ qstrcpy(driveName, "A:/");
+
+ while(driveBits) {
+ if(driveBits & 1)
+ ret.append(QString::fromLatin1(driveName).toUpper());
+ driveName[0]++;
+ driveBits = driveBits >> 1;
+ }
+ return ret;
+#else
+ ret.append(QString::fromLatin1("/").toUpper());
+ return ret;
+#endif
+}
+
+bool QFSFileEnginePrivate::doStat() const
+{
+ if (!tried_stat) {
+ tried_stat = true;
+ could_stat = false;
+
+ if (filePath.isEmpty())
+ return could_stat;
+ QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
+ fname = fixIfRelativeUncPath(fname);
+
+ UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+
+ if (fd != -1) {
+#if !defined(Q_OS_WINCE)
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ BY_HANDLE_FILE_INFORMATION fileInfo;
+ if (GetFileInformationByHandle(fh, &fileInfo)) {
+ could_stat = true;
+ fileAttrib = fileInfo.dwFileAttributes;
+ }
+ }
+#else
+ DWORD tmpAttributes = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
+ if (tmpAttributes != -1) {
+ fileAttrib = tmpAttributes;
+ could_stat = true;
+ } else {
+ return false;
+ }
+#endif
+ } else {
+ QT_WA({
+ fileAttrib = GetFileAttributesW((TCHAR*)QFSFileEnginePrivate::longFileName(fname).utf16());
+ } , {
+ fileAttrib = GetFileAttributesA(QFSFileEnginePrivate::win95Name(QFileInfo(fname).absoluteFilePath()));
+ });
+ could_stat = fileAttrib != INVALID_FILE_ATTRIBUTES;
+ if (!could_stat) {
+#if !defined(Q_OS_WINCE)
+ if (!fname.isEmpty() && fname.at(0).isLetter() && fname.mid(1, fname.length()) == QLatin1String(":/")) {
+ // an empty drive ??
+ DWORD drivesBitmask = ::GetLogicalDrives();
+ int drivebit = 1 << (fname.at(0).toUpper().unicode() - QLatin1Char('A').unicode());
+ if (drivesBitmask & drivebit) {
+ fileAttrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
+ could_stat = true;
+ }
+ } else {
+#endif
+ QString path = QDir::toNativeSeparators(fname);
+ bool is_dir = false;
+ if (path.startsWith(QLatin1String("\\\\"))) {
+ // UNC - stat doesn't work for all cases (Windows bug)
+ int s = path.indexOf(path.at(0),2);
+ if (s > 0) {
+ // "\\server\..."
+ s = path.indexOf(path.at(0),s+1);
+ if (s > 0) {
+ // "\\server\share\..."
+ if (s == path.size() - 1) {
+ // "\\server\share\"
+ is_dir = true;
+ } else {
+ // "\\server\share\notfound"
+ }
+ } else {
+ // "\\server\share"
+ is_dir = true;
+ }
+ } else {
+ // "\\server"
+ is_dir = true;
+ }
+ }
+ if (is_dir && uncShareExists(path)) {
+ // looks like a UNC dir, is a dir.
+ fileAttrib = FILE_ATTRIBUTE_DIRECTORY;
+ could_stat = true;
+ }
+#if !defined(Q_OS_WINCE)
+ }
+#endif
+ }
+ }
+ SetErrorMode(oldmode);
+ }
+ return could_stat;
+}
+
+
+static QString readLink(const QString &link)
+{
+#if !defined(Q_OS_WINCE)
+#if !defined(QT_NO_LIBRARY)
+ QString ret;
+ QT_WA({
+ bool neededCoInit = false;
+ IShellLink *psl; // pointer to IShellLink i/f
+ HRESULT hres;
+ WIN32_FIND_DATA wfd;
+ TCHAR szGotPath[MAX_PATH];
+ // Get pointer to the IShellLink interface.
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLink, (LPVOID *)&psl);
+
+ if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
+ neededCoInit = true;
+ CoInitialize(NULL);
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLink, (LPVOID *)&psl);
+ }
+ if(SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
+ IPersistFile *ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
+ if(SUCCEEDED(hres)) {
+ hres = ppf->Load((LPOLESTR)link.utf16(), STGM_READ);
+ //The original path of the link is retrieved. If the file/folder
+ //was moved, the return value still have the old path.
+ if(SUCCEEDED(hres)) {
+ if (psl->GetPath(szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
+ ret = QString::fromUtf16((ushort*)szGotPath);
+ }
+ ppf->Release();
+ }
+ psl->Release();
+ }
+ if(neededCoInit)
+ CoUninitialize();
+ } , {
+ bool neededCoInit = false;
+ IShellLinkA *psl; // pointer to IShellLink i/f
+ HRESULT hres;
+ WIN32_FIND_DATAA wfd;
+ char szGotPath[MAX_PATH];
+ // Get pointer to the IShellLink interface.
+
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkA, (LPVOID *)&psl);
+
+ if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
+ neededCoInit = true;
+ CoInitialize(NULL);
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkA, (LPVOID *)&psl);
+ }
+ if(SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
+ IPersistFile *ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
+ if(SUCCEEDED(hres)) {
+ hres = ppf->Load((LPOLESTR)QFileInfo(link).absoluteFilePath().utf16(), STGM_READ);
+ //The original path of the link is retrieved. If the file/folder
+ //was moved, the return value still have the old path.
+ if(SUCCEEDED(hres)) {
+ if (psl->GetPath((char*)szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY) == NOERROR)
+ ret = QString::fromLocal8Bit(szGotPath);
+ }
+ ppf->Release();
+ }
+ psl->Release();
+ }
+ if(neededCoInit)
+ CoUninitialize();
+ });
+ return ret;
+#else
+ Q_UNUSED(link);
+ return QString();
+#endif // QT_NO_LIBRARY
+#else
+ wchar_t target[MAX_PATH];
+ QString result;
+ if (SHGetShortcutTarget((wchar_t*)QFileInfo(link).absoluteFilePath().replace(QLatin1Char('/'),QLatin1Char('\\')).utf16(), target, MAX_PATH)) {
+ result = QString::fromUtf16(reinterpret_cast<const ushort *> (target));
+ if (result.startsWith(QLatin1Char('"')))
+ result.remove(0,1);
+ if (result.endsWith(QLatin1Char('"')))
+ result.remove(result.size()-1,1);
+ }
+ return result;
+#endif // Q_OS_WINCE
+}
+
+/*!
+ \internal
+*/
+QString QFSFileEnginePrivate::getLink() const
+{
+ return readLink(filePath);
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::link(const QString &newName)
+{
+#if !defined(Q_OS_WINCE)
+#if !defined(QT_NO_LIBRARY)
+ bool ret = false;
+
+ QString linkName = newName;
+ //### assume that they add .lnk
+
+ QT_WA({
+ HRESULT hres;
+ IShellLink *psl;
+ bool neededCoInit = false;
+
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+ if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
+ neededCoInit = true;
+ CoInitialize(NULL);
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+ }
+ if (SUCCEEDED(hres)) {
+ hres = psl->SetPath((wchar_t *)fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
+ if (SUCCEEDED(hres)) {
+ hres = psl->SetWorkingDirectory((wchar_t *)fileName(AbsolutePathName).replace(QLatin1Char('/'), QLatin1Char('\\')).utf16());
+ if (SUCCEEDED(hres)) {
+ IPersistFile *ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
+ if (SUCCEEDED(hres)) {
+ hres = ppf->Save((TCHAR*)linkName.utf16(), TRUE);
+ if (SUCCEEDED(hres))
+ ret = true;
+ ppf->Release();
+ }
+ }
+ }
+ psl->Release();
+ }
+ if(neededCoInit)
+ CoUninitialize();
+ } , {
+ // the SetPath() call _sometimes_ changes the current path and when it does it sometimes
+ // does not let us change it back unless we call currentPath() many times.
+ QString cwd = currentPath();
+ HRESULT hres;
+ IShellLinkA *psl;
+ bool neededCoInit = false;
+
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+ if(hres == CO_E_NOTINITIALIZED) { // COM was not initialized
+ neededCoInit = true;
+ CoInitialize(NULL);
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+ }
+ if (SUCCEEDED(hres)) {
+ currentPath();
+ hres = psl->SetPath((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsoluteName))).utf16());
+ currentPath();
+ if (SUCCEEDED(hres)) {
+ hres = psl->SetWorkingDirectory((char *)QString::fromLocal8Bit(QFSFileEnginePrivate::win95Name(fileName(AbsolutePathName))).utf16());
+ currentPath();
+ if (SUCCEEDED(hres)) {
+ IPersistFile *ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
+ if (SUCCEEDED(hres)) {
+ currentPath();
+ hres = ppf->Save((LPCOLESTR)linkName.utf16(), TRUE);
+ currentPath();
+ if (SUCCEEDED(hres))
+ ret = true;
+ ppf->Release();
+ }
+ }
+ psl->Release();
+ }
+ }
+ if(neededCoInit)
+ CoUninitialize();
+ setCurrentPath(cwd);
+ });
+ return ret;
+#else
+ Q_UNUSED(newName);
+ return false;
+#endif // QT_NO_LIBRARY
+#else
+ QString linkName = newName;
+ if (!linkName.endsWith(QLatin1String(".lnk")))
+ linkName += QLatin1String(".lnk");
+ QString orgName = fileName(AbsoluteName).replace(QLatin1Char('/'), QLatin1Char('\\'));
+ // Need to append on our own
+ orgName.prepend(QLatin1Char('"'));
+ orgName.append(QLatin1Char('"'));
+ return SUCCEEDED(SHCreateShortcut((wchar_t*)linkName.utf16(), (wchar_t*)orgName.utf16()));
+#endif // Q_OS_WINCE
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions() const
+{
+ QAbstractFileEngine::FileFlags ret = 0;
+
+#if !defined(QT_NO_LIBRARY)
+ if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
+ PSID pOwner = 0;
+ PSID pGroup = 0;
+ PACL pDacl;
+ PSECURITY_DESCRIPTOR pSD;
+ ACCESS_MASK access_mask;
+
+ enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 };
+ resolveLibs();
+ if(ptrGetNamedSecurityInfoW && ptrAllocateAndInitializeSid && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW && ptrFreeSid) {
+
+ QString fname = filePath.endsWith(QLatin1String(".lnk")) ? readLink(filePath) : filePath;
+ DWORD res = ptrGetNamedSecurityInfoW((wchar_t*)fname.utf16(), SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &pOwner, &pGroup, &pDacl, 0, &pSD);
+
+ if(res == ERROR_SUCCESS) {
+ TRUSTEE_W trustee;
+ { //user
+ if(ptrGetEffectiveRightsFromAclW(pDacl, &currentUserTrusteeW, &access_mask) != ERROR_SUCCESS)
+ access_mask = (ACCESS_MASK)-1;
+ if(access_mask & ReadMask)
+ ret |= QAbstractFileEngine::ReadUserPerm;
+ if(access_mask & WriteMask)
+ ret |= QAbstractFileEngine::WriteUserPerm;
+ if(access_mask & ExecMask)
+ ret |= QAbstractFileEngine::ExeUserPerm;
+ }
+ { //owner
+ ptrBuildTrusteeWithSidW(&trustee, pOwner);
+ if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ access_mask = (ACCESS_MASK)-1;
+ if(access_mask & ReadMask)
+ ret |= QAbstractFileEngine::ReadOwnerPerm;
+ if(access_mask & WriteMask)
+ ret |= QAbstractFileEngine::WriteOwnerPerm;
+ if(access_mask & ExecMask)
+ ret |= QAbstractFileEngine::ExeOwnerPerm;
+ }
+ { //group
+ ptrBuildTrusteeWithSidW(&trustee, pGroup);
+ if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ access_mask = (ACCESS_MASK)-1;
+ if(access_mask & ReadMask)
+ ret |= QAbstractFileEngine::ReadGroupPerm;
+ if(access_mask & WriteMask)
+ ret |= QAbstractFileEngine::WriteGroupPerm;
+ if(access_mask & ExecMask)
+ ret |= QAbstractFileEngine::ExeGroupPerm;
+ }
+ { //other (world)
+ // Create SID for Everyone (World)
+ SID_IDENTIFIER_AUTHORITY worldAuth = { SECURITY_WORLD_SID_AUTHORITY };
+ PSID pWorld = 0;
+ if(ptrAllocateAndInitializeSid(&worldAuth, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pWorld)) {
+ ptrBuildTrusteeWithSidW(&trustee, pWorld);
+ if(ptrGetEffectiveRightsFromAclW(pDacl, &trustee, &access_mask) != ERROR_SUCCESS)
+ access_mask = (ACCESS_MASK)-1; // ###
+ if(access_mask & ReadMask)
+ ret |= QAbstractFileEngine::ReadOtherPerm;
+ if(access_mask & WriteMask)
+ ret |= QAbstractFileEngine::WriteOtherPerm;
+ if(access_mask & ExecMask)
+ ret |= QAbstractFileEngine::ExeOtherPerm;
+ }
+ ptrFreeSid(pWorld);
+ }
+ LocalFree(pSD);
+ }
+ }
+ } else
+#endif
+ {
+ //### what to do with permissions if we don't use ntfs or are not on a NT system
+ // for now just add all permissions and what about exe missions ??
+ // also qt_ntfs_permission_lookup is now not set by defualt ... should it ?
+ ret |= QAbstractFileEngine::ReadOtherPerm | QAbstractFileEngine::ReadGroupPerm
+ | QAbstractFileEngine::ReadOwnerPerm | QAbstractFileEngine::ReadUserPerm
+ | QAbstractFileEngine::WriteUserPerm | QAbstractFileEngine::WriteOwnerPerm
+ | QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm;
+ }
+
+ if (doStat()) {
+ if (ret & (QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
+ QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm)) {
+ if (fileAttrib & FILE_ATTRIBUTE_READONLY)
+ ret &= ~(QAbstractFileEngine::WriteOwnerPerm | QAbstractFileEngine::WriteUserPerm |
+ QAbstractFileEngine::WriteGroupPerm | QAbstractFileEngine::WriteOtherPerm);
+ }
+
+ QString ext = filePath.right(4).toLower();
+ if (ext == QLatin1String(".exe") || ext == QLatin1String(".com") || ext == QLatin1String(".bat") ||
+ ext == QLatin1String(".pif") || ext == QLatin1String(".cmd") || (fileAttrib & FILE_ATTRIBUTE_DIRECTORY))
+ ret |= QAbstractFileEngine::ExeOwnerPerm | QAbstractFileEngine::ExeGroupPerm |
+ QAbstractFileEngine::ExeOtherPerm | QAbstractFileEngine::ExeUserPerm;
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
+{
+ Q_D(const QFSFileEngine);
+ QAbstractFileEngine::FileFlags ret = 0;
+ // Force a stat, so that we're guaranteed to get up-to-date results
+ if (type & QAbstractFileEngine::FileFlag(QAbstractFileEngine::Refresh)) {
+ d->tried_stat = 0;
+ }
+
+ if (type & PermsMask) {
+ ret |= d->getPermissions();
+ // ### Workaround pascals ### above. Since we always set all properties to true
+ // we need to disable read and exec access if the file does not exists
+ if (d->doStat())
+ ret |= ExistsFlag;
+ else
+ ret &= 0x2222;
+ }
+ if (type & TypesMask) {
+ if (d->filePath.endsWith(QLatin1String(".lnk"))) {
+ ret |= LinkType;
+ QString l = readLink(d->filePath);
+ if (!l.isEmpty()) {
+ if (isDirPath(l, 0))
+ ret |= DirectoryType;
+ else
+ ret |= FileType;
+ }
+ } else if (d->doStat()) {
+ if (d->fileAttrib & FILE_ATTRIBUTE_DIRECTORY) {
+ ret |= DirectoryType;
+ } else {
+ ret |= FileType;
+ }
+ }
+ }
+ if (type & FlagsMask) {
+ if(d->doStat()) {
+ ret |= QAbstractFileEngine::FileFlags(ExistsFlag | LocalDiskFlag);
+ if (d->fileAttrib & FILE_ATTRIBUTE_HIDDEN)
+ ret |= HiddenFlag;
+ if (d->filePath == QLatin1String("/") || (d->filePath.at(0).isLetter() && d->filePath.mid(1,d->filePath.length()) == QLatin1String(":/"))
+ || isUncRoot(d->filePath)) {
+ ret |= RootFlag;
+ ret &= ~HiddenFlag;
+ }
+ }
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QString QFSFileEngine::fileName(FileName file) const
+{
+ Q_D(const QFSFileEngine);
+ if(file == BaseName) {
+ int slash = d->filePath.lastIndexOf(QLatin1Char('/'));
+ if(slash == -1) {
+ int colon = d->filePath.lastIndexOf(QLatin1Char(':'));
+ if(colon != -1)
+ return d->filePath.mid(colon + 1);
+ return d->filePath;
+ }
+ return d->filePath.mid(slash + 1);
+ } 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 !defined(Q_OS_WINCE)
+ if (d->filePath.size() > 2 && d->filePath.at(1) == QLatin1Char(':')
+ && d->filePath.at(2) != QLatin1Char('/') || // It's a drive-relative path, so Z:a.txt -> Z:\currentpath\a.txt
+ d->filePath.startsWith(QLatin1Char('/')) // It's a absolute path to the current drive, so \a.txt -> Z:\a.txt
+ ) {
+ ret = QDir::fromNativeSeparators(nativeAbsoluteFilePath(d->filePath));
+ } else {
+ ret = d->filePath;
+ }
+#else
+ ret = d->filePath;
+#endif
+ } 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::currentPath();
+ else if (slash == 0)
+ ret = QLatin1String("/");
+ ret = ret.left(slash);
+ }
+ return ret;
+ } else if(file == LinkName) {
+ return QDir::fromNativeSeparators(d->getLink());
+ } else if(file == BundleName) {
+ return QString();
+ }
+ return d->filePath;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::isRelativePath() const
+{
+ Q_D(const QFSFileEngine);
+ return !(d->filePath.startsWith(QLatin1Char('/'))
+ || (d->filePath.length() >= 2
+ && ((d->filePath.at(0).isLetter() && d->filePath.at(1) == QLatin1Char(':'))
+ || (d->filePath.at(0) == QLatin1Char('/') && d->filePath.at(1) == QLatin1Char('/'))))); // drive, e.g. a:
+}
+
+/*!
+ \reimp
+*/
+uint QFSFileEngine::ownerId(FileOwner /*own*/) const
+{
+ static const uint nobodyID = (uint) -2;
+ return nobodyID;
+}
+
+/*!
+ \reimp
+*/
+QString QFSFileEngine::owner(FileOwner own) const
+{
+#if !defined(QT_NO_LIBRARY)
+ Q_D(const QFSFileEngine);
+ if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) {
+ PSID pOwner = 0;
+ PSECURITY_DESCRIPTOR pSD;
+ QString name;
+ QFSFileEnginePrivate::resolveLibs();
+
+ if(ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) {
+ if(ptrGetNamedSecurityInfoW((wchar_t*)d->filePath.utf16(), SE_FILE_OBJECT,
+ own == OwnerGroup ? GROUP_SECURITY_INFORMATION : OWNER_SECURITY_INFORMATION,
+ NULL, &pOwner, NULL, NULL, &pSD) == ERROR_SUCCESS) {
+ DWORD lowner = 0, ldomain = 0;
+ SID_NAME_USE use;
+ // First call, to determine size of the strings (with '\0').
+ ptrLookupAccountSidW(NULL, pOwner, NULL, &lowner, NULL, &ldomain, (SID_NAME_USE*)&use);
+ wchar_t *owner = new wchar_t[lowner];
+ wchar_t *domain = new wchar_t[ldomain];
+ // Second call, size is without '\0'
+ if(ptrLookupAccountSidW(NULL, pOwner, (LPWSTR)owner, &lowner,
+ (LPWSTR)domain, &ldomain, (SID_NAME_USE*)&use)) {
+ name = QString::fromUtf16((ushort*)owner);
+ }
+ LocalFree(pSD);
+ delete [] owner;
+ delete [] domain;
+ }
+ }
+ return name;
+ }
+#else
+ Q_UNUSED(own);
+#endif
+ return QString(QLatin1String(""));
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::setPermissions(uint perms)
+{
+ Q_D(QFSFileEngine);
+ bool ret = false;
+ int mode = 0;
+
+ if (perms & QFile::ReadOwner || perms & QFile::ReadUser || perms & QFile::ReadGroup || perms & QFile::ReadOther)
+ mode |= _S_IREAD;
+ if (perms & QFile::WriteOwner || perms & QFile::WriteUser || perms & QFile::WriteGroup || perms & QFile::WriteOther)
+ mode |= _S_IWRITE;
+
+ if (mode == 0) // not supported
+ return false;
+
+#if !defined(Q_OS_WINCE)
+ QT_WA({
+ ret = ::_wchmod((TCHAR*)d->filePath.utf16(), mode) == 0;
+ } , {
+ ret = ::_chmod(d->filePath.toLocal8Bit(), mode) == 0;
+ });
+#else
+ ret = ::_wchmod((TCHAR*)d->longFileName(d->filePath).utf16(), mode);
+#endif
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+bool QFSFileEngine::setSize(qint64 size)
+{
+ Q_D(QFSFileEngine);
+
+ if (d->fileHandle != INVALID_HANDLE_VALUE || d->fd != -1) {
+ // resize open file
+ HANDLE fh = d->fileHandle;
+#if !defined(Q_OS_WINCE)
+ if (fh == INVALID_HANDLE_VALUE)
+ fh = (HANDLE)_get_osfhandle(d->fd);
+#endif
+ if (fh == INVALID_HANDLE_VALUE)
+ return false;
+ qint64 currentPos = pos();
+
+ if (seek(size) && SetEndOfFile(fh)) {
+ seek(qMin(currentPos, size));
+ return true;
+ }
+
+ seek(currentPos);
+ return false;
+ }
+
+ if (!d->nativeFilePath.isEmpty()) {
+ // resize file on disk
+ QFile file(d->filePath);
+ if (file.open(QFile::ReadWrite)) {
+ return file.resize(size);
+ }
+ }
+ return false;
+}
+
+
+static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
+{
+ QDateTime ret;
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) {
+ // SystemTimeToTzSpecificLocalTime is not available on Win98/ME so we have to pull it off ourselves.
+ SYSTEMTIME systime;
+ FILETIME ftime;
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+ systime.wDayOfWeek = 4;
+ SystemTimeToFileTime(&systime, &ftime);
+ unsigned __int64 acttime = (unsigned __int64)time->dwHighDateTime << 32 | time->dwLowDateTime;
+ FileTimeToSystemTime(time, &systime);
+ unsigned __int64 time1970 = (unsigned __int64)ftime.dwHighDateTime << 32 | ftime.dwLowDateTime;
+ unsigned __int64 difftime = acttime - time1970;
+ difftime /= 10000000;
+ ret.setTime_t((unsigned int)difftime);
+ } else {
+#ifndef Q_OS_WINCE
+ SYSTEMTIME sTime, lTime;
+ FileTimeToSystemTime(time, &sTime);
+ SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime);
+ ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay));
+ ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds));
+#endif
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+QDateTime QFSFileEngine::fileTime(FileTime time) const
+{
+ Q_D(const QFSFileEngine);
+ QDateTime ret;
+ if (d->fd != -1) {
+#if !defined(Q_OS_WINCE)
+ HANDLE fh = (HANDLE)_get_osfhandle(d->fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ FILETIME creationTime, lastAccessTime, lastWriteTime;
+ if (GetFileTime(fh, &creationTime, &lastAccessTime, &lastWriteTime)) {
+ if(time == CreationTime)
+ ret = fileTimeToQDateTime(&creationTime);
+ else if(time == ModificationTime)
+ ret = fileTimeToQDateTime(&lastWriteTime);
+ else if(time == AccessTime)
+ ret = fileTimeToQDateTime(&lastAccessTime);
+ }
+ }
+#endif
+ } else {
+ bool ok = false;
+ WIN32_FILE_ATTRIBUTE_DATA attribData;
+ QT_WA({
+ ok = ::GetFileAttributesExW((TCHAR*)QFSFileEnginePrivate::longFileName(d->filePath).utf16(), GetFileExInfoStandard, &attribData);
+ } , {
+ ok = ::GetFileAttributesExA(QFSFileEnginePrivate::win95Name(QFileInfo(d->filePath).absoluteFilePath()), GetFileExInfoStandard, &attribData);
+ });
+ if (ok) {
+ if(time == CreationTime)
+ ret = fileTimeToQDateTime(&attribData.ftCreationTime);
+ else if(time == ModificationTime)
+ ret = fileTimeToQDateTime(&attribData.ftLastWriteTime);
+ else if(time == AccessTime)
+ ret = fileTimeToQDateTime(&attribData.ftLastAccessTime);
+ }
+ }
+ return ret;
+}
+
+uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
+ QFile::MemoryMapFlags flags)
+{
+ Q_Q(QFSFileEngine);
+ Q_UNUSED(flags);
+ if (openMode == QFile::NotOpen) {
+ q->setError(QFile::PermissionsError, qt_error_string());
+ return 0;
+ }
+ if (offset == 0 && size == 0) {
+ q->setError(QFile::UnspecifiedError, qt_error_string());
+ return 0;
+ }
+
+
+ // get handle to the file
+ HANDLE handle = fileHandle;
+#ifndef Q_OS_WINCE
+ if (handle == INVALID_HANDLE_VALUE && fh)
+ handle = (HANDLE)_get_osfhandle(QT_FILENO(fh));
+#else
+ #ifdef Q_USE_DEPRECATED_MAP_API
+ nativeClose();
+ if (fileMapHandle == INVALID_HANDLE_VALUE) {
+ fileMapHandle = CreateFileForMappingW((TCHAR *)nativeFilePath.constData(),
+ GENERIC_READ | (openMode & QIODevice::WriteOnly ? GENERIC_WRITE : 0),
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ handle = fileMapHandle;
+ #endif
+ if (handle == INVALID_HANDLE_VALUE && fh)
+ return 0;
+#endif
+
+ // first create the file mapping handle
+ HANDLE mapHandle = 0;
+ DWORD protection = (openMode & QIODevice::WriteOnly) ? PAGE_READWRITE : PAGE_READONLY;
+ QT_WA({
+ mapHandle = ::CreateFileMappingW(handle, 0, protection,
+ 0, 0, 0);
+ },{
+ mapHandle = ::CreateFileMappingA(handle, 0, protection,
+ 0, 0, 0);
+ });
+ if (mapHandle == NULL) {
+ q->setError(QFile::PermissionsError, qt_error_string());
+#ifdef Q_USE_DEPRECATED_MAP_API
+ mapHandleClose();
+#endif
+ return 0;
+ }
+
+ // setup args to map
+ DWORD access = 0;
+ if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ;
+ if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE;
+
+ DWORD offsetHi = offset >> 32;
+ DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
+ SYSTEM_INFO sysinfo;
+ ::GetSystemInfo(&sysinfo);
+ int mask = sysinfo.dwAllocationGranularity - 1;
+ int extra = offset & mask;
+ if (extra)
+ offsetLo &= ~mask;
+
+ // attempt to create the map
+ LPVOID mapAddress = MapViewOfFile(mapHandle, access,
+ offsetHi, offsetLo, size + extra);
+ if (mapAddress) {
+ uchar *address = extra + static_cast<uchar*>(mapAddress);
+ maps[address] = QPair<int, HANDLE>(extra, mapHandle);
+ return address;
+ }
+
+ switch(GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ q->setError(QFile::PermissionsError, qt_error_string());
+ break;
+ case ERROR_INVALID_PARAMETER:
+ // size are out of bounds
+ default:
+ q->setError(QFile::UnspecifiedError, qt_error_string());
+ }
+ CloseHandle(mapHandle);
+#ifdef Q_USE_DEPRECATED_MAP_API
+ mapHandleClose();
+#endif
+ return 0;
+}
+
+bool QFSFileEnginePrivate::unmap(uchar *ptr)
+{
+ Q_Q(QFSFileEngine);
+ if (!maps.contains(ptr)) {
+ q->setError(QFile::PermissionsError, qt_error_string());
+ return false;
+ }
+ uchar *start = ptr - maps[ptr].first;
+ if (!UnmapViewOfFile(start)) {
+ q->setError(QFile::PermissionsError, qt_error_string());
+ return false;
+ }
+
+ if (!CloseHandle((HANDLE)maps[ptr].second)) {
+ q->setError(QFile::UnspecifiedError, qt_error_string());
+ return false;
+ }
+ maps.remove(ptr);
+
+#ifdef Q_USE_DEPRECATED_MAP_API
+ mapHandleClose();
+#endif
+ return true;
+}
+
+#ifdef Q_USE_DEPRECATED_MAP_API
+void QFSFileEnginePrivate::mapHandleClose()
+{
+ if (maps.isEmpty()) {
+ CloseHandle(fileMapHandle);
+ fileMapHandle = INVALID_HANDLE_VALUE;
+ }
+}
+#endif
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
new file mode 100644
index 0000000..c739054
--- /dev/null
+++ b/src/corelib/io/qiodevice.cpp
@@ -0,0 +1,1748 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QIODEVICE_DEBUG
+
+#include "qbytearray.h"
+#include "qdebug.h"
+#include "qiodevice_p.h"
+#include "qfile.h"
+#include "qstringlist.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QIODEVICE_DEBUG
+void debugBinaryString(const QByteArray &input)
+{
+ QByteArray tmp;
+ int startOffset = 0;
+ for (int i = 0; i < input.size(); ++i) {
+ tmp += input[i];
+
+ if ((i % 16) == 15 || i == (input.size() - 1)) {
+ printf("\n%15d:", startOffset);
+ startOffset += tmp.size();
+
+ for (int j = 0; j < tmp.size(); ++j)
+ printf(" %02x", int(uchar(tmp[j])));
+ for (int j = tmp.size(); j < 16 + 1; ++j)
+ printf(" ");
+ for (int j = 0; j < tmp.size(); ++j)
+ printf("%c", isprint(int(uchar(tmp[j]))) ? tmp[j] : '.');
+ tmp.clear();
+ }
+ }
+ printf("\n\n");
+}
+
+void debugBinaryString(const char *data, qint64 maxlen)
+{
+ debugBinaryString(QByteArray(data, maxlen));
+}
+#endif
+
+#ifndef QIODEVICE_BUFFERSIZE
+#define QIODEVICE_BUFFERSIZE Q_INT64_C(16384)
+#endif
+
+#define Q_VOID
+
+#define CHECK_MAXLEN(function, returnType) \
+ do { \
+ if (maxSize < 0) { \
+ qWarning("QIODevice::"#function": Called with maxSize < 0"); \
+ return returnType; \
+ } \
+ } while (0)
+
+#define CHECK_WRITABLE(function, returnType) \
+ do { \
+ if ((d->openMode & WriteOnly) == 0) { \
+ if (d->openMode == NotOpen) \
+ return returnType; \
+ qWarning("QIODevice::"#function": ReadOnly device"); \
+ return returnType; \
+ } \
+ } while (0)
+
+#define CHECK_READABLE(function, returnType) \
+ do { \
+ if ((d->openMode & ReadOnly) == 0) { \
+ if (d->openMode == NotOpen) \
+ return returnType; \
+ qWarning("QIODevice::"#function": WriteOnly device"); \
+ return returnType; \
+ } \
+ } while (0)
+
+/*! \internal
+ */
+QIODevicePrivate::QIODevicePrivate()
+ : openMode(QIODevice::NotOpen), buffer(QIODEVICE_BUFFERSIZE),
+ pos(0), devicePos(0), accessMode(Unset)
+{
+}
+
+/*! \internal
+ */
+QIODevicePrivate::~QIODevicePrivate()
+{
+}
+
+/*!
+ \class QIODevice
+ \reentrant
+
+ \brief The QIODevice class is the base interface class of all I/O
+ devices in Qt.
+
+ \ingroup io
+
+ QIODevice provides both a common implementation and an abstract
+ interface for devices that support reading and writing of blocks
+ of data, such as QFile, QBuffer and QTcpSocket. QIODevice is
+ abstract and can not be instantiated, but it is common to use the
+ interface it defines to provide device-independent I/O features.
+ For example, Qt's XML classes operate on a QIODevice pointer,
+ allowing them to be used with various devices (such as files and
+ buffers).
+
+ Before accessing the device, open() must be called to set the
+ correct OpenMode (such as ReadOnly or ReadWrite). You can then
+ write to the device with write() or putChar(), and read by calling
+ either read(), readLine(), or readAll(). Call close() when you are
+ done with the device.
+
+ QIODevice distinguishes between two types of devices:
+ random-access devices and sequential devices.
+
+ \list
+ \o Random-access devices support seeking to arbitrary
+ positions using seek(). The current position in the file is
+ available by calling pos(). QFile and QBuffer are examples of
+ random-access devices.
+
+ \o Sequential devices don't support seeking to arbitrary
+ positions. The data must be read in one pass. The functions
+ pos() and size() don't work for sequential devices.
+ QTcpSocket and QProcess are examples of sequential devices.
+ \endlist
+
+ You can use isSequential() to determine the type of device.
+
+ QIODevice emits readyRead() when new data is available for
+ reading; for example, if new data has arrived on the network or if
+ additional data is appended to a file that you are reading
+ from. You can call bytesAvailable() to determine the number of
+ bytes that are currently available for reading. It's common to use
+ bytesAvailable() together with the readyRead() signal when
+ programming with asynchronous devices such as QTcpSocket, where
+ fragments of data can arrive at arbitrary points in
+ time. QIODevice emits the bytesWritten() signal every time a
+ payload of data has been written to the device. Use bytesToWrite()
+ to determine the current amount of data waiting to be written.
+
+ Certain subclasses of QIODevice, such as QTcpSocket and QProcess,
+ are asynchronous. This means that I/O functions such as write()
+ or read() always return immediately, while communication with the
+ device itself may happen when control goes back to the event loop.
+ QIODevice provides functions that allow you to force these
+ operations to be performed immediately, while blocking the
+ calling thread and without entering the event loop. This allows
+ QIODevice subclasses to be used without an event loop, or in
+ a separate thread:
+
+ \list
+ \o waitForReadyRead() - This function suspends operation in the
+ calling thread until new data is available for reading.
+
+ \o waitForBytesWritten() - This function suspends operation in the
+ calling thread until one payload of data has been written to the
+ device.
+
+ \o waitFor....() - Subclasses of QIODevice implement blocking
+ functions for device-specific operations. For example, QProcess
+ has a function called waitForStarted() which suspends operation in
+ the calling thread until the process has started.
+ \endlist
+
+ Calling these functions from the main, GUI thread, may cause your
+ user interface to freeze. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 0
+
+ By subclassing QIODevice, you can provide the same interface to
+ your own I/O devices. Subclasses of QIODevice are only required to
+ implement the protected readData() and writeData() functions.
+ QIODevice uses these functions to implement all its convenience
+ functions, such as getChar(), readLine() and write(). QIODevice
+ also handles access control for you, so you can safely assume that
+ the device is opened in write mode if writeData() is called.
+
+ Some subclasses, such as QFile and QTcpSocket, are implemented
+ using a memory buffer for intermediate storing of data. This
+ reduces the number of required device accessing calls, which are
+ often very slow. Buffering makes functions like getChar() and
+ putChar() fast, as they can operate on the memory buffer instead
+ of directly on the device itself. Certain I/O operations, however,
+ don't work well with a buffer. For example, if several users open
+ the same device and read it character by character, they may end
+ up reading the same data when they meant to read a separate chunk
+ each. For this reason, QIODevice allows you to bypass any
+ buffering by passing the Unbuffered flag to open(). When
+ subclassing QIODevice, remember to bypass any buffer you may use
+ when the device is open in Unbuffered mode.
+
+ \sa QBuffer QFile QTcpSocket
+*/
+
+/*!
+ \typedef QIODevice::Offset
+ \compat
+
+ Use \c qint64 instead.
+*/
+
+/*!
+ \typedef QIODevice::Status
+ \compat
+
+ Use QIODevice::OpenMode instead, or see the documentation for
+ specific devices.
+*/
+
+/*!
+ \enum QIODevice::OpenModeFlag
+
+ This enum is used with open() to describe the mode in which a device
+ is opened. It is also returned by openMode().
+
+ \value NotOpen The device is not open.
+ \value ReadOnly The device is open for reading.
+ \value WriteOnly The device is open for writing.
+ \value ReadWrite The device is open for reading and writing.
+ \value Append The device is opened in append mode, so that all data is
+ written to the end of the file.
+ \value Truncate If possible, the device is truncated before it is opened.
+ All earlier contents of the device are lost.
+ \value Text When reading, the end-of-line terminators are
+ translated to '\n'. When writing, the end-of-line
+ terminators are translated to the local encoding, for
+ example '\r\n' for Win32.
+ \value Unbuffered Any buffer in the device is bypassed.
+
+ Certain flags, such as \c Unbuffered and \c Truncate, are
+ meaningless when used with some subclasses. Some of these
+ restrictions are implied by the type of device that is represented
+ by a subclass; for example, access to a QBuffer is always
+ unbuffered. In other cases, the restriction may be due to the
+ implementation, or may be imposed by the underlying platform; for
+ example, QTcpSocket does not support \c Unbuffered mode, and
+ limitations in the native API prevent QFile from supporting \c
+ Unbuffered on Windows.
+*/
+
+/*! \fn QIODevice::bytesWritten(qint64 bytes)
+
+ This signal is emitted every time a payload of data has been
+ written to the device. The \a bytes argument is set to the number
+ of bytes that were written in this payload.
+
+ bytesWritten() is not emitted recursively; if you reenter the event loop
+ or call waitForBytesWritten() inside a slot connected to the
+ bytesWritten() signal, the signal will not be reemitted (although
+ waitForBytesWritten() may still return true).
+
+ \sa readyRead()
+*/
+
+/*!
+ \fn QIODevice::readyRead()
+
+ This signal is emitted once every time new data is available for
+ reading from the device. It will only be emitted again once new
+ data is available, such as when a new payload of network data has
+ arrived on your network socket, or when a new block of data has
+ been appended to your device.
+
+ readyRead() is not emitted recursively; if you reenter the event loop or
+ call waitForReadyRead() inside a slot connected to the readyRead() signal,
+ the signal will not be reemitted (although waitForReadyRead() may still
+ return true).
+
+ Note for developers implementing classes derived from QIODevice:
+ you should always emit readyRead() when new data has arrived (do not
+ emit it only because there's data still to be read in your
+ buffers). Do not emit readyRead() in other conditions.
+
+ \sa bytesWritten()
+*/
+
+/*! \fn QIODevice::aboutToClose()
+
+ This signal is emitted when the device is about to close. Connect
+ this signal if you have operations that need to be performed
+ before the device closes (e.g., if you have data in a separate
+ buffer that needs to be written to the device).
+*/
+
+/*!
+ \fn QIODevice::readChannelFinished()
+ \since 4.4
+
+ This signal is emitted when the input (reading) stream is closed
+ in this device. It is emitted as soon as the closing is detected,
+ which means that there might still be data available for reading
+ with read().
+
+ \sa atEnd(), read()
+*/
+
+#ifdef QT_NO_QOBJECT
+QIODevice::QIODevice()
+ : d_ptr(new QIODevicePrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*! \internal
+*/
+QIODevice::QIODevice(QIODevicePrivate &dd)
+ : d_ptr(&dd)
+{
+ d_ptr->q_ptr = this;
+}
+#else
+
+/*!
+ Constructs a QIODevice object.
+*/
+
+QIODevice::QIODevice()
+ : QObject(*new QIODevicePrivate, 0)
+{
+#if defined QIODEVICE_DEBUG
+ QFile *file = qobject_cast<QFile *>(this);
+ printf("%p QIODevice::QIODevice(\"%s\") %s\n", this, className(),
+ qPrintable(file ? file->fileName() : QString()));
+#endif
+}
+
+/*!
+ Constructs a QIODevice object with the given \a parent.
+*/
+
+QIODevice::QIODevice(QObject *parent)
+ : QObject(*new QIODevicePrivate, parent)
+{
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::QIODevice(%p \"%s\")\n", this, parent, className());
+#endif
+}
+
+/*! \internal
+*/
+QIODevice::QIODevice(QIODevicePrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+#endif
+
+
+/*!
+ Destructs the QIODevice object.
+*/
+QIODevice::~QIODevice()
+{
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::~QIODevice()\n", this);
+#endif
+}
+
+/*!
+ Returns true if this device is sequential; otherwise returns
+ false.
+
+ Sequential devices, as opposed to a random-access devices, have no
+ concept of a start, an end, a size, or a current position, and they
+ do not support seeking. You can only read from the device when it
+ reports that data is available. The most common example of a
+ sequential device is a network socket. On Unix, special files such
+ as /dev/zero and fifo pipes are sequential.
+
+ Regular files, on the other hand, do support random access. They
+ have both a size and a current position, and they also support
+ seeking backwards and forwards in the data stream. Regular files
+ are non-sequential.
+
+ \sa bytesAvailable()
+*/
+bool QIODevice::isSequential() const
+{
+ return false;
+}
+
+/*!
+ Returns the mode in which the device has been opened;
+ i.e. ReadOnly or WriteOnly.
+
+ \sa OpenMode
+*/
+QIODevice::OpenMode QIODevice::openMode() const
+{
+ return d_func()->openMode;
+}
+
+/*!
+ Sets the OpenMode of the device to \a openMode. Call this
+ function to set the open mode if the flags change after the device
+ has been opened.
+
+ \sa openMode() OpenMode
+*/
+void QIODevice::setOpenMode(OpenMode openMode)
+{
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::setOpenMode(0x%x)\n", this, int(openMode));
+#endif
+ d_func()->openMode = openMode;
+ d_func()->accessMode = QIODevicePrivate::Unset;
+}
+
+/*!
+ If \a enabled is true, this function sets the \l Text flag on the device;
+ otherwise the \l Text flag is removed. This feature is useful for classes
+ that provide custom end-of-line handling on a QIODevice.
+
+ \sa open(), setOpenMode()
+ */
+void QIODevice::setTextModeEnabled(bool enabled)
+{
+ Q_D(QIODevice);
+ if (enabled)
+ d->openMode |= Text;
+ else
+ d->openMode &= ~Text;
+}
+
+/*!
+ Returns true if the \l Text flag is enabled; otherwise returns false.
+
+ \sa setTextModeEnabled()
+*/
+bool QIODevice::isTextModeEnabled() const
+{
+ return d_func()->openMode & Text;
+}
+
+/*!
+ Returns true if the device is open; otherwise returns false. A
+ device is open if it can be read from and/or written to. By
+ default, this function returns false if openMode() returns
+ \c NotOpen.
+
+ \sa openMode() OpenMode
+*/
+bool QIODevice::isOpen() const
+{
+ return d_func()->openMode != NotOpen;
+}
+
+/*!
+ Returns true if data can be read from the device; otherwise returns
+ false. Use bytesAvailable() to determine how many bytes can be read.
+
+ This is a convenience function which checks if the OpenMode of the
+ device contains the ReadOnly flag.
+
+ \sa openMode() OpenMode
+*/
+bool QIODevice::isReadable() const
+{
+ return (openMode() & ReadOnly) != 0;
+}
+
+/*!
+ Returns true if data can be written to the device; otherwise returns
+ false.
+
+ This is a convenience function which checks if the OpenMode of the
+ device contains the WriteOnly flag.
+
+ \sa openMode() OpenMode
+*/
+bool QIODevice::isWritable() const
+{
+ return (openMode() & WriteOnly) != 0;
+}
+
+/*!
+ Opens the device and sets its OpenMode to \a mode. Returns true if successful;
+ otherwise returns false. This function should be called from any
+ reimplementations of open() or other functions that open the device.
+
+ \sa openMode() OpenMode
+*/
+bool QIODevice::open(OpenMode mode)
+{
+ Q_D(QIODevice);
+ d->openMode = mode;
+ d->pos = (mode & Append) ? size() : qint64(0);
+ d->buffer.clear();
+ d->accessMode = QIODevicePrivate::Unset;
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::open(0x%x)\n", this, quint32(mode));
+#endif
+ return true;
+}
+
+/*!
+ First emits aboutToClose(), then closes the device and sets its
+ OpenMode to NotOpen. The error string is also reset.
+
+ \sa setOpenMode() OpenMode
+*/
+void QIODevice::close()
+{
+ Q_D(QIODevice);
+ if (d->openMode == NotOpen)
+ return;
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::close()\n", this);
+#endif
+
+#ifndef QT_NO_QOBJECT
+ emit aboutToClose();
+#endif
+ d->openMode = NotOpen;
+ d->errorString.clear();
+ d->pos = 0;
+ d->buffer.clear();
+}
+
+/*!
+ For random-access devices, this function returns the position that
+ data is written to or read from. For sequential devices or closed
+ devices, where there is no concept of a "current position", 0 is
+ returned.
+
+ The current read/write position of the device is maintained internally by
+ QIODevice, so reimplementing this function is not necessary. When
+ subclassing QIODevice, use QIODevice::seek() to notify QIODevice about
+ changes in the device position.
+
+ \sa isSequential(), seek()
+*/
+qint64 QIODevice::pos() const
+{
+ Q_D(const QIODevice);
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::pos() == %d\n", this, int(d->pos));
+#endif
+ return d->pos;
+}
+
+/*!
+ For open random-access devices, this function returns the size of the
+ device. For open sequential devices, bytesAvailable() is returned.
+
+ If the device is closed, the size returned will not reflect the actual
+ size of the device.
+
+ \sa isSequential(), pos()
+*/
+qint64 QIODevice::size() const
+{
+ return d_func()->isSequential() ? bytesAvailable() : qint64(0);
+}
+
+/*!
+ For random-access devices, this function sets the current position
+ to \a pos, returning true on success, or false if an error occurred.
+ For sequential devices, the default behavior is to do nothing and
+ return false.
+
+ When subclassing QIODevice, you must call QIODevice::seek() at the
+ start of your function to ensure integrity with QIODevice's
+ built-in buffer. The base implementation always returns true.
+
+ \sa pos(), isSequential()
+*/
+bool QIODevice::seek(qint64 pos)
+{
+ if (d_func()->openMode == NotOpen) {
+ qWarning("QIODevice::seek: The device is not open");
+ return false;
+ }
+ if (pos < 0) {
+ qWarning("QIODevice::seek: Invalid pos: %d", int(pos));
+ return false;
+ }
+
+ Q_D(QIODevice);
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::seek(%d), before: d->pos = %d, d->buffer.size() = %d\n",
+ this, int(pos), int(d->pos), d->buffer.size());
+#endif
+
+ qint64 offset = pos - d->pos;
+ if (!d->isSequential()) {
+ d->pos = pos;
+ d->devicePos = pos;
+ }
+
+ if (offset > 0 && !d->buffer.isEmpty()) {
+ // When seeking forwards, we need to pop bytes off the front of the
+ // buffer.
+ do {
+ int bytesToSkip = int(qMin<qint64>(offset, INT_MAX));
+ d->buffer.skip(bytesToSkip);
+ offset -= bytesToSkip;
+ } while (offset > 0);
+ } else if (offset < 0) {
+ // When seeking backwards, an operation that is only allowed for
+ // random-access devices, the buffer is cleared. The next read
+ // operation will then refill the buffer. We can optimize this, if we
+ // find that seeking backwards becomes a significant performance hit.
+ d->buffer.clear();
+ }
+#if defined QIODEVICE_DEBUG
+ printf("%p \tafter: d->pos == %d, d->buffer.size() == %d\n", this, int(d->pos),
+ d->buffer.size());
+#endif
+ return true;
+}
+
+/*!
+ Returns true if the current read and write position is at the end
+ of the device (i.e. there is no more data available for reading on
+ the device); otherwise returns false.
+
+ For some devices, atEnd() can return true even though there is more data
+ to read. This special case only applies to devices that generate data in
+ direct response to you calling read() (e.g., \c /dev or \c /proc files on
+ Unix and Mac OS X, or console input / \c stdin on all platforms).
+
+ \sa bytesAvailable(), read(), isSequential()
+*/
+bool QIODevice::atEnd() const
+{
+ Q_D(const QIODevice);
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::atEnd() returns %s, d->openMode == %d, d->pos == %d\n", this, (d->openMode == NotOpen || d->pos == size()) ? "true" : "false",
+ int(d->openMode), int(d->pos));
+#endif
+ return d->openMode == NotOpen || (d->buffer.isEmpty() && bytesAvailable() == 0);
+}
+
+/*!
+ Seeks to the start of input for random-access devices. Returns
+ true on success; otherwise returns false (for example, if the
+ device is not open).
+
+ Note that when using a QTextStream on a QFile, calling reset() on
+ the QFile will not have the expected result because QTextStream
+ buffers the file. Use the QTextStream::seek() function instead.
+
+ \sa seek()
+*/
+bool QIODevice::reset()
+{
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::reset()\n", this);
+#endif
+ return seek(0);
+}
+
+/*!
+ Returns the number of bytes that are available for reading. This
+ function is commonly used with sequential devices to determine the
+ number of bytes to allocate in a buffer before reading.
+
+ Subclasses that reimplement this function must call the base
+ implementation in order to include the size of QIODevices' buffer. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 1
+
+ \sa bytesToWrite(), readyRead(), isSequential()
+*/
+qint64 QIODevice::bytesAvailable() const
+{
+ Q_D(const QIODevice);
+ if (!d->isSequential())
+ return qMax(size() - d->pos, qint64(0));
+ return d->buffer.size();
+}
+
+/*!
+ For buffered devices, this function returns the number of bytes
+ waiting to be written. For devices with no buffer, this function
+ returns 0.
+
+ \sa bytesAvailable(), bytesWritten(), isSequential()
+*/
+qint64 QIODevice::bytesToWrite() const
+{
+ return qint64(0);
+}
+
+/*!
+ Reads at most \a maxSize bytes from the device into \a data, and
+ returns the number of bytes read. If an error occurs, such as when
+ attempting to read from a device opened in WriteOnly mode, this
+ function returns -1.
+
+ 0 is returned when no more data is available for reading. However,
+ reading past the end of the stream is considered an error, so this
+ function returns -1 in those cases (that is, reading on a closed
+ socket or after a process has died).
+
+ \sa readData() readLine() write()
+*/
+qint64 QIODevice::read(char *data, qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_READABLE(read, qint64(-1));
+ CHECK_MAXLEN(read, qint64(-1));
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::read(%p, %d), d->pos = %d, d->buffer.size() = %d\n",
+ this, data, int(maxSize), int(d->pos), int(d->buffer.size()));
+#endif
+ const bool sequential = d->isSequential();
+
+ // Short circuit for getChar()
+ if (maxSize == 1) {
+ int chint = d->buffer.getChar();
+ if (chint != -1) {
+ char c = char(uchar(chint));
+ if (c == '\r' && (d->openMode & Text)) {
+ d->buffer.ungetChar(c);
+ } else {
+ if (data)
+ *data = c;
+ if (!sequential)
+ ++d->pos;
+#if defined QIODEVICE_DEBUG
+ printf("%p \tread 0x%hhx (%c) returning 1 (shortcut)\n", this,
+ int(c), isprint(c) ? c : '?');
+#endif
+ return qint64(1);
+ }
+ }
+ }
+
+ qint64 readSoFar = 0;
+ bool moreToRead = true;
+ do {
+ int lastReadChunkSize = 0;
+
+ // Try reading from the buffer.
+ if (!d->buffer.isEmpty()) {
+ lastReadChunkSize = d->buffer.read(data + readSoFar, maxSize - readSoFar);
+ readSoFar += lastReadChunkSize;
+ if (!sequential)
+ d->pos += lastReadChunkSize;
+#if defined QIODEVICE_DEBUG
+ printf("%p \treading %d bytes from buffer into position %d\n", this, lastReadChunkSize,
+ int(readSoFar) - lastReadChunkSize);
+#endif
+ } else if ((d->openMode & Unbuffered) == 0 && maxSize < QIODEVICE_BUFFERSIZE) {
+ // In buffered mode, we try to fill up the QIODevice buffer before
+ // we do anything else.
+ int bytesToBuffer = qMax(maxSize - readSoFar, QIODEVICE_BUFFERSIZE);
+ char *writePointer = d->buffer.reserve(bytesToBuffer);
+
+ // Make sure the device is positioned correctly.
+ if (d->pos != d->devicePos && !sequential && !seek(d->pos))
+ return qint64(-1);
+ qint64 readFromDevice = readData(writePointer, bytesToBuffer);
+ d->buffer.chop(bytesToBuffer - (readFromDevice < 0 ? 0 : int(readFromDevice)));
+
+ if (readFromDevice > 0) {
+ if (!sequential)
+ d->devicePos += readFromDevice;
+#if defined QIODEVICE_DEBUG
+ printf("%p \treading %d from device into buffer\n", this, int(readFromDevice));
+#endif
+
+ if (readFromDevice < bytesToBuffer)
+ d->buffer.truncate(readFromDevice < 0 ? 0 : int(readFromDevice));
+ if (!d->buffer.isEmpty()) {
+ lastReadChunkSize = d->buffer.read(data + readSoFar, maxSize - readSoFar);
+ readSoFar += lastReadChunkSize;
+ if (!sequential)
+ d->pos += lastReadChunkSize;
+#if defined QIODEVICE_DEBUG
+ printf("%p \treading %d bytes from buffer at position %d\n", this,
+ lastReadChunkSize, int(readSoFar));
+#endif
+ }
+ }
+ }
+
+ // If we need more, try reading from the device.
+ if (readSoFar < maxSize) {
+ // Make sure the device is positioned correctly.
+ if (d->pos != d->devicePos && !sequential && !seek(d->pos))
+ return qint64(-1);
+ qint64 readFromDevice = readData(data + readSoFar, maxSize - readSoFar);
+#if defined QIODEVICE_DEBUG
+ printf("%p \treading %d bytes from device (total %d)\n", this, int(readFromDevice), int(readSoFar));
+#endif
+ if (readFromDevice == -1 && readSoFar == 0) {
+ // error and we haven't read anything: return immediately
+ return -1;
+ }
+ if (readFromDevice <= 0) {
+ moreToRead = false;
+ } else {
+ // see if we read as much data as we asked for
+ if (readFromDevice < maxSize - readSoFar)
+ moreToRead = false;
+
+ lastReadChunkSize += int(readFromDevice);
+ readSoFar += readFromDevice;
+ if (!sequential) {
+ d->pos += readFromDevice;
+ d->devicePos += readFromDevice;
+ }
+ }
+ } else {
+ moreToRead = false;
+ }
+
+ if (readSoFar && d->openMode & Text) {
+ char *readPtr = data + readSoFar - lastReadChunkSize;
+ const char *endPtr = data + readSoFar;
+
+ if (readPtr < endPtr) {
+ // optimization to avoid initial self-assignment
+ while (*readPtr != '\r') {
+ if (++readPtr == endPtr)
+ return readSoFar;
+ }
+
+ char *writePtr = readPtr;
+
+ while (readPtr < endPtr) {
+ char ch = *readPtr++;
+ if (ch != '\r')
+ *writePtr++ = ch;
+ else
+ --readSoFar;
+ }
+
+ // Make sure we get more data if there is room for more. This
+ // is very important for when someone seeks to the start of a
+ // '\r\n' and reads one character - they should get the '\n'.
+ moreToRead = (readPtr != writePtr);
+ }
+ }
+ } while (moreToRead);
+
+#if defined QIODEVICE_DEBUG
+ printf("%p \treturning %d, d->pos == %d, d->buffer.size() == %d\n", this,
+ int(readSoFar), int(d->pos), d->buffer.size());
+ debugBinaryString(data, readSoFar);
+#endif
+ return readSoFar;
+}
+
+/*!
+ \overload
+
+ Reads at most \a maxSize bytes from the device, and returns the
+ data read as a QByteArray.
+
+ This function has no way of reporting errors; returning an empty
+ QByteArray() can mean either that no data was currently available
+ for reading, or that an error occurred.
+*/
+QByteArray QIODevice::read(qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_MAXLEN(read, QByteArray());
+ QByteArray tmp;
+ qint64 readSoFar = 0;
+ char buffer[4096];
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::read(%d), d->pos = %d, d->buffer.size() = %d\n",
+ this, int(maxSize), int(d->pos), int(d->buffer.size()));
+#else
+ Q_UNUSED(d);
+#endif
+
+ do {
+ qint64 bytesToRead = qMin(int(maxSize - readSoFar), int(sizeof(buffer)));
+ qint64 readBytes = read(buffer, bytesToRead);
+ if (readBytes <= 0)
+ break;
+ tmp.append(buffer, (int) readBytes);
+ readSoFar += readBytes;
+ } while (readSoFar < maxSize && bytesAvailable() > 0);
+
+ return tmp;
+}
+
+/*!
+ \overload
+
+ Reads all available data from the device, and returns it as a
+ QByteArray.
+
+ This function has no way of reporting errors; returning an empty
+ QByteArray() can mean either that no data was currently available
+ for reading, or that an error occurred.
+*/
+QByteArray QIODevice::readAll()
+{
+ Q_D(QIODevice);
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::readAll(), d->pos = %d, d->buffer.size() = %d\n",
+ this, int(d->pos), int(d->buffer.size()));
+#endif
+
+ QByteArray tmp;
+ if (d->isSequential() || size() == 0) {
+ // Read it in chunks, bytesAvailable() is unreliable for sequential
+ // devices.
+ const int chunkSize = 4096;
+ qint64 totalRead = 0;
+ forever {
+ tmp.resize(tmp.size() + chunkSize);
+ qint64 readBytes = read(tmp.data() + totalRead, chunkSize);
+ tmp.chop(chunkSize - (readBytes < 0 ? 0 : readBytes));
+ if (readBytes <= 0)
+ return tmp;
+ totalRead += readBytes;
+ }
+ } else {
+ // Read it all in one go.
+ tmp.resize(int(bytesAvailable()));
+ qint64 readBytes = read(tmp.data(), tmp.size());
+ tmp.resize(readBytes < 0 ? 0 : int(readBytes));
+ }
+ return tmp;
+}
+
+/*!
+ This function reads a line of ASCII characters from the device, up
+ to a maximum of \a maxSize - 1 bytes, stores the characters in \a
+ data, and returns the number of bytes read. If a line could not be
+ read but no error ocurred, this function returns 0. If an error
+ occurs, this function returns what it could the length of what
+ could be read, or -1 if nothing was read.
+
+ A terminating '\0' byte is always appended to \a data, so \a
+ maxSize must be larger than 1.
+
+ Data is read until either of the following conditions are met:
+
+ \list
+ \o The first '\n' character is read.
+ \o \a maxSize - 1 bytes are read.
+ \o The end of the device data is detected.
+ \endlist
+
+ For example, the following code reads a line of characters from a
+ file:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 2
+
+ The newline character ('\n') is included in the buffer. If a
+ newline is not encountered before maxSize - 1 bytes are read, a
+ newline will not be inserted into the buffer. On windows newline
+ characters are replaced with '\n'.
+
+ This function calls readLineData(), which is implemented using
+ repeated calls to getChar(). You can provide a more efficient
+ implementation by reimplementing readLineData() in your own
+ subclass.
+
+ \sa getChar(), read(), write()
+*/
+qint64 QIODevice::readLine(char *data, qint64 maxSize)
+{
+ Q_D(QIODevice);
+ if (maxSize < 2) {
+ qWarning("QIODevice::readLine: Called with maxSize < 2");
+ return qint64(-1);
+ }
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::readLine(%p, %d), d->pos = %d, d->buffer.size() = %d\n",
+ this, data, int(maxSize), int(d->pos), int(d->buffer.size()));
+#endif
+
+ // Leave room for a '\0'
+ --maxSize;
+
+ const bool sequential = d->isSequential();
+
+ qint64 readSoFar = 0;
+ if (!d->buffer.isEmpty()) {
+ readSoFar = d->buffer.readLine(data, maxSize);
+ if (!sequential)
+ d->pos += readSoFar;
+#if defined QIODEVICE_DEBUG
+ printf("%p \tread from buffer: %d bytes, last character read: %hhx\n", this,
+ int(readSoFar), data[int(readSoFar) - 1]);
+ if (readSoFar)
+ debugBinaryString(data, int(readSoFar));
+#endif
+ if (readSoFar && data[readSoFar - 1] == '\n') {
+ if (d->openMode & Text) {
+ // QRingBuffer::readLine() isn't Text aware.
+ if (readSoFar > 1 && data[readSoFar - 2] == '\r') {
+ --readSoFar;
+ data[readSoFar - 1] = '\n';
+ }
+ }
+ data[readSoFar] = '\0';
+ return readSoFar;
+ }
+ }
+
+ if (d->pos != d->devicePos && !sequential && !seek(d->pos))
+ return qint64(-1);
+ d->baseReadLineDataCalled = false;
+ qint64 readBytes = readLineData(data + readSoFar, maxSize - readSoFar);
+#if defined QIODEVICE_DEBUG
+ printf("%p \tread from readLineData: %d bytes, readSoFar = %d bytes\n", this,
+ int(readBytes), int(readSoFar));
+ if (readBytes > 0) {
+ debugBinaryString(data, int(readSoFar + readBytes));
+ }
+#endif
+ if (readBytes < 0) {
+ data[readSoFar] = '\0';
+ return readSoFar ? readSoFar : -1;
+ }
+ readSoFar += readBytes;
+ if (!d->baseReadLineDataCalled && !sequential) {
+ d->pos += readBytes;
+ // If the base implementation was not called, then we must
+ // assume the device position is invalid and force a seek.
+ d->devicePos = qint64(-1);
+ }
+ data[readSoFar] = '\0';
+
+ if (d->openMode & Text) {
+ if (readSoFar > 1 && data[readSoFar - 1] == '\n' && data[readSoFar - 2] == '\r') {
+ data[readSoFar - 2] = '\n';
+ data[readSoFar - 1] = '\0';
+ --readSoFar;
+ }
+ }
+
+#if defined QIODEVICE_DEBUG
+ printf("%p \treturning %d, d->pos = %d, d->buffer.size() = %d, size() = %d\n",
+ this, int(readSoFar), int(d->pos), d->buffer.size(), int(size()));
+ debugBinaryString(data, int(readSoFar));
+#endif
+ return readSoFar;
+}
+
+/*!
+ \overload
+
+ Reads a line from the device, but no more than \a maxSize characters,
+ and returns the result as a QByteArray.
+
+ This function has no way of reporting errors; returning an empty
+ QByteArray() can mean either that no data was currently available
+ for reading, or that an error occurred.
+*/
+QByteArray QIODevice::readLine(qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_MAXLEN(readLine, QByteArray());
+ QByteArray tmp;
+ const int BufferGrowth = 4096;
+ qint64 readSoFar = 0;
+ qint64 readBytes = 0;
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::readLine(%d), d->pos = %d, d->buffer.size() = %d\n",
+ this, int(maxSize), int(d->pos), int(d->buffer.size()));
+#else
+ Q_UNUSED(d);
+#endif
+
+ do {
+ if (maxSize != 0)
+ tmp.resize(int(readSoFar + qMin(int(maxSize), BufferGrowth)));
+ else
+ tmp.resize(int(readSoFar + BufferGrowth));
+ readBytes = readLine(tmp.data() + readSoFar, tmp.size() - readSoFar);
+ if (readBytes <= 0)
+ break;
+
+ readSoFar += readBytes;
+ } while ((!maxSize || readSoFar < maxSize) &&
+ readSoFar + 1 == tmp.size() && // +1 due to the ending null
+ tmp.at(readSoFar - 1) != '\n');
+
+ if (readSoFar == 0 && readBytes == -1)
+ tmp.clear(); // return Null if we found an error
+ else
+ tmp.resize(int(readSoFar));
+ return tmp;
+}
+
+/*!
+ Reads up to \a maxSize characters into \a data and returns the
+ number of characters read.
+
+ This function is called by readLine(), and provides its base
+ implementation, using getChar(). Buffered devices can improve the
+ performance of readLine() by reimplementing this function.
+
+ readLine() appends a '\0' byte to \a data; readLineData() does not
+ need to do this.
+
+ If you reimplement this function, be careful to return the correct
+ value: it should return the number of bytes read in this line,
+ including the terminating newline, or 0 if there is no line to be
+ read at this point. If an error occurs, it should return -1 if and
+ only if no bytes were read. Reading past EOF is considered an error.
+*/
+qint64 QIODevice::readLineData(char *data, qint64 maxSize)
+{
+ Q_D(QIODevice);
+ qint64 readSoFar = 0;
+ char c;
+ int lastReadReturn = 0;
+ d->baseReadLineDataCalled = true;
+
+ while (readSoFar < maxSize && (lastReadReturn = read(&c, 1)) == 1) {
+ *data++ = c;
+ ++readSoFar;
+ if (c == '\n')
+ break;
+ }
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::readLineData(%p, %d), d->pos = %d, d->buffer.size() = %d, returns %d\n",
+ this, data, int(maxSize), int(d->pos), int(d->buffer.size()), int(readSoFar));
+#endif
+ if (lastReadReturn != 1 && readSoFar == 0)
+ return isSequential() ? lastReadReturn : -1;
+ return readSoFar;
+}
+
+/*!
+ Returns true if a complete line of data can be read from the device;
+ otherwise returns false.
+
+ Note that unbuffered devices, which have no way of determining what
+ can be read, always return false.
+
+ This function is often called in conjunction with the readyRead()
+ signal.
+
+ Subclasses that reimplement this function must call the base
+ implementation in order to include the contents of the QIODevice's buffer. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 3
+
+ \sa readyRead(), readLine()
+*/
+bool QIODevice::canReadLine() const
+{
+ return d_func()->buffer.canReadLine();
+}
+
+/*!
+ Writes at most \a maxSize bytes of data from \a data to the
+ device. Returns the number of bytes that were actually written, or
+ -1 if an error occurred.
+
+ \sa read() writeData()
+*/
+qint64 QIODevice::write(const char *data, qint64 maxSize)
+{
+ Q_D(QIODevice);
+ CHECK_WRITABLE(write, qint64(-1));
+ CHECK_MAXLEN(write, qint64(-1));
+
+ const bool sequential = d->isSequential();
+ // Make sure the device is positioned correctly.
+ if (d->pos != d->devicePos && !sequential && !seek(d->pos))
+ return qint64(-1);
+
+#ifdef Q_OS_WIN
+ if (d->openMode & Text) {
+ const char *endOfData = data + maxSize;
+ const char *startOfBlock = data;
+
+ qint64 writtenSoFar = 0;
+
+ forever {
+ const char *endOfBlock = startOfBlock;
+ while (endOfBlock < endOfData && *endOfBlock != '\n')
+ ++endOfBlock;
+
+ qint64 blockSize = endOfBlock - startOfBlock;
+ if (blockSize > 0) {
+ qint64 ret = writeData(startOfBlock, blockSize);
+ if (ret <= 0) {
+ if (writtenSoFar && !sequential)
+ d->buffer.skip(writtenSoFar);
+ return writtenSoFar ? writtenSoFar : ret;
+ }
+ if (!sequential) {
+ d->pos += ret;
+ d->devicePos += ret;
+ }
+ writtenSoFar += ret;
+ }
+
+ if (endOfBlock == endOfData)
+ break;
+
+ qint64 ret = writeData("\r\n", 2);
+ if (ret <= 0) {
+ if (writtenSoFar && !sequential)
+ d->buffer.skip(writtenSoFar);
+ return writtenSoFar ? writtenSoFar : ret;
+ }
+ if (!sequential) {
+ d->pos += ret;
+ d->devicePos += ret;
+ }
+ ++writtenSoFar;
+
+ startOfBlock = endOfBlock + 1;
+ }
+
+ if (writtenSoFar && !sequential)
+ d->buffer.skip(writtenSoFar);
+ return writtenSoFar;
+ }
+#endif
+
+ qint64 written = writeData(data, maxSize);
+ if (written > 0) {
+ if (!sequential) {
+ d->pos += written;
+ d->devicePos += written;
+ }
+ if (!d->buffer.isEmpty() && !sequential)
+ d->buffer.skip(written);
+ }
+ return written;
+}
+
+/*!
+ \since 4.5
+
+ \overload
+
+ Writes data from a zero-terminated string of 8-bit characters to the
+ device. Returns the number of bytes that were actually written, or
+ -1 if an error occurred. This is equivalent to
+ \code
+ ...
+ QIODevice::write(data, qstrlen(data));
+ ...
+ \endcode
+
+ \sa read() writeData()
+*/
+qint64 QIODevice::write(const char *data)
+{
+ return write(data, qstrlen(data));
+}
+
+/*! \fn qint64 QIODevice::write(const QByteArray &byteArray)
+
+ \overload
+
+ Writes the content of \a byteArray to the device. Returns the number of
+ bytes that were actually written, or -1 if an error occurred.
+
+ \sa read() writeData()
+*/
+
+/*!
+ Puts the character \a c back into the device, and decrements the
+ current position unless the position is 0. This function is
+ usually called to "undo" a getChar() operation, such as when
+ writing a backtracking parser.
+
+ If \a c was not previously read from the device, the behavior is
+ undefined.
+*/
+void QIODevice::ungetChar(char c)
+{
+ Q_D(QIODevice);
+ CHECK_READABLE(read, Q_VOID);
+
+#if defined QIODEVICE_DEBUG
+ printf("%p QIODevice::ungetChar(0x%hhx '%c')\n", this, c, isprint(c) ? c : '?');
+#endif
+
+ d->buffer.ungetChar(c);
+ if (!d->isSequential())
+ --d->pos;
+}
+
+/*! \fn bool QIODevice::putChar(char c)
+
+ Writes the character \a c to the device. Returns true on success;
+ otherwise returns false.
+
+ \sa write() getChar() ungetChar()
+*/
+bool QIODevice::putChar(char c)
+{
+ return d_func()->putCharHelper(c);
+}
+
+/*!
+ \internal
+*/
+bool QIODevicePrivate::putCharHelper(char c)
+{
+ return q_func()->write(&c, 1) == 1;
+}
+
+/*! \fn bool QIODevice::getChar(char *c)
+
+ Reads one character from the device and stores it in \a c. If \a c
+ is 0, the character is discarded. Returns true on success;
+ otherwise returns false.
+
+ \sa read() putChar() ungetChar()
+*/
+bool QIODevice::getChar(char *c)
+{
+ Q_D(QIODevice);
+ const OpenMode openMode = d->openMode;
+ if (!(openMode & ReadOnly)) {
+ if (openMode == NotOpen)
+ qWarning("QIODevice::getChar: Closed device");
+ else
+ qWarning("QIODevice::getChar: WriteOnly device");
+ return false;
+ }
+
+ // Shortcut for QIODevice::read(c, 1)
+ QRingBuffer *buffer = &d->buffer;
+ const int chint = buffer->getChar();
+ if (chint != -1) {
+ char ch = char(uchar(chint));
+ if ((openMode & Text) && ch == '\r') {
+ buffer->ungetChar(ch);
+ } else {
+ if (c)
+ *c = ch;
+ if (!d->isSequential())
+ ++d->pos;
+ return true;
+ }
+ }
+
+ // Fall back to read().
+ char ch;
+ if (read(&ch, 1) == 1) {
+ if (c)
+ *c = ch;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \since 4.1
+
+ Reads at most \a maxSize bytes from the device into \a data, without side
+ effects (i.e., if you call read() after peek(), you will get the same
+ data). Returns the number of bytes read. If an error occurs, such as
+ when attempting to peek a device opened in WriteOnly mode, this function
+ returns -1.
+
+ 0 is returned when no more data is available for reading.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 4
+
+ \sa read()
+*/
+qint64 QIODevice::peek(char *data, qint64 maxSize)
+{
+ qint64 readBytes = read(data, maxSize);
+ int i = readBytes;
+ while (i > 0)
+ ungetChar(data[i-- - 1]);
+ return readBytes;
+}
+
+/*!
+ \since 4.1
+ \overload
+
+ Peeks at most \a maxSize bytes from the device, returning the data peeked
+ as a QByteArray.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qiodevice.cpp 5
+
+ This function has no way of reporting errors; returning an empty
+ QByteArray() can mean either that no data was currently available
+ for peeking, or that an error occurred.
+
+ \sa read()
+*/
+QByteArray QIODevice::peek(qint64 maxSize)
+{
+ QByteArray result = read(maxSize);
+ int i = result.size();
+ const char *data = result.constData();
+ while (i > 0)
+ ungetChar(data[i-- - 1]);
+ return result;
+}
+
+/*!
+ Blocks until data is available for reading and the readyRead()
+ signal has been emitted, or until \a msecs milliseconds have
+ passed. If msecs is -1, this function will not time out.
+
+ Returns true if data is available for reading; otherwise returns
+ false (if the operation timed out or if an error occurred).
+
+ This function can operate without an event loop. It is
+ useful when writing non-GUI applications and when performing
+ I/O operations in a non-GUI thread.
+
+ If called from within a slot connected to the readyRead() signal,
+ readyRead() will not be reemitted.
+
+ Reimplement this function to provide a blocking API for a custom
+ device. The default implementation does nothing, and returns false.
+
+ \warning Calling this function from the main (GUI) thread
+ might cause your user interface to freeze.
+
+ \sa waitForBytesWritten()
+*/
+bool QIODevice::waitForReadyRead(int msecs)
+{
+ Q_UNUSED(msecs);
+ return false;
+}
+
+/*!
+ For buffered devices, this function waits until a payload of
+ buffered written data has been written to the device and the
+ bytesWritten() signal has been emitted, or until \a msecs
+ milliseconds have passed. If msecs is -1, this function will
+ not time out. For unbuffered devices, it returns immediately.
+
+ Returns true if a payload of data was written to the device;
+ otherwise returns false (i.e. if the operation timed out, or if an
+ error occurred).
+
+ This function can operate without an event loop. It is
+ useful when writing non-GUI applications and when performing
+ I/O operations in a non-GUI thread.
+
+ If called from within a slot connected to the bytesWritten() signal,
+ bytesWritten() will not be reemitted.
+
+ Reimplement this function to provide a blocking API for a custom
+ device. The default implementation does nothing, and returns false.
+
+ \warning Calling this function from the main (GUI) thread
+ might cause your user interface to freeze.
+
+ \sa waitForReadyRead()
+*/
+bool QIODevice::waitForBytesWritten(int msecs)
+{
+ Q_UNUSED(msecs);
+ return false;
+}
+
+/*!
+ Sets the human readable description of the last device error that
+ occurred to \a str.
+
+ \sa errorString()
+*/
+void QIODevice::setErrorString(const QString &str)
+{
+ d_func()->errorString = str;
+}
+
+/*!
+ Returns a human-readable description of the last device error that
+ occurred.
+
+ \sa setErrorString()
+*/
+QString QIODevice::errorString() const
+{
+ Q_D(const QIODevice);
+ if (d->errorString.isEmpty()) {
+#ifdef QT_NO_QOBJECT
+ return QLatin1String(QT_TRANSLATE_NOOP(QIODevice, "Unknown error"));
+#else
+ return tr("Unknown error");
+#endif
+ }
+ return d->errorString;
+}
+
+/*!
+ \fn qint64 QIODevice::readData(char *data, qint64 maxSize)
+
+ Reads up to \a maxSize bytes from the device into \a data, and
+ returns the number of bytes read or -1 if an error occurred. If
+ there are no bytes to be read, this function should return -1 if
+ there can never be more bytes available (for example: socket
+ closed, pipe closed, sub-process finished).
+
+ This function is called by QIODevice. Reimplement this function
+ when creating a subclass of QIODevice.
+
+ \sa read() readLine() writeData()
+*/
+
+/*!
+ \fn qint64 QIODevice::writeData(const char *data, qint64 maxSize)
+
+ Writes up to \a maxSize bytes from \a data to the device. Returns
+ the number of bytes written, or -1 if an error occurred.
+
+ This function is called by QIODevice. Reimplement this function
+ when creating a subclass of QIODevice.
+
+ \sa read() write()
+*/
+
+/*!
+ \fn QIODevice::Offset QIODevice::status() const
+
+ For device specific error handling, please refer to the
+ individual device documentation.
+
+ \sa qobject_cast()
+*/
+
+/*!
+ \fn QIODevice::Offset QIODevice::at() const
+
+ Use pos() instead.
+*/
+
+/*!
+ \fn bool QIODevice::at(Offset offset)
+
+ Use seek(\a offset) instead.
+*/
+
+/*! \fn int QIODevice::flags() const
+
+ Use openMode() instead.
+*/
+
+/*! \fn int QIODevice::getch()
+
+ Use getChar() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isAsynchronous() const
+
+ This functionality is no longer available. This function always
+ returns true.
+*/
+
+/*!
+ \fn bool QIODevice::isBuffered() const
+
+ Use !(openMode() & QIODevice::Unbuffered) instead.
+*/
+
+/*!
+ \fn bool QIODevice::isCombinedAccess() const
+
+ Use openMode() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isDirectAccess() const
+
+ Use !isSequential() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isInactive() const
+
+ Use isOpen(), isReadable(), or isWritable() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isRaw() const
+
+ Use openMode() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isSequentialAccess() const
+
+ Use isSequential() instead.
+*/
+
+/*!
+ \fn bool QIODevice::isSynchronous() const
+
+ This functionality is no longer available. This function always
+ returns false.
+*/
+
+/*!
+ \fn bool QIODevice::isTranslated() const
+
+ Use openMode() instead.
+*/
+
+/*!
+ \fn bool QIODevice::mode() const
+
+ Use openMode() instead.
+*/
+
+/*! \fn int QIODevice::putch(int ch)
+
+ Use putChar(\a ch) instead.
+*/
+
+/*! \fn int QIODevice::ungetch(int ch)
+
+ Use ungetChar(\a ch) instead.
+*/
+
+/*!
+ \fn quint64 QIODevice::readBlock(char *data, quint64 size)
+
+ Use read(\a data, \a size) instead.
+*/
+
+/*! \fn int QIODevice::state() const
+
+ Use isOpen() instead.
+*/
+
+/*!
+ \fn qint64 QIODevice::writeBlock(const char *data, quint64 size)
+
+ Use write(\a data, \a size) instead.
+*/
+
+/*!
+ \fn qint64 QIODevice::writeBlock(const QByteArray &data)
+
+ Use write(\a data) instead.
+*/
+
+#if defined QT3_SUPPORT
+QIODevice::Status QIODevice::status() const
+{
+#if !defined(QT_NO_QOBJECT)
+ const QFile *f = qobject_cast<const QFile *>(this);
+ if (f) return (int) f->error();
+#endif
+ return isOpen() ? 0 /* IO_Ok */ : 8 /* IO_UnspecifiedError */;
+}
+
+/*!
+ For device specific error handling, please refer to the
+ individual device documentation.
+
+ \sa qobject_cast()
+*/
+void QIODevice::resetStatus()
+{
+#if !defined(QT_NO_QOBJECT)
+ QFile *f = qobject_cast<QFile *>(this);
+ if (f) f->unsetError();
+#endif
+}
+#endif
+
+#if !defined(QT_NO_DEBUG_STREAM)
+QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)
+{
+ debug << "OpenMode(";
+ QStringList modeList;
+ if (modes == QIODevice::NotOpen) {
+ modeList << QLatin1String("NotOpen");
+ } else {
+ if (modes & QIODevice::ReadOnly)
+ modeList << QLatin1String("ReadOnly");
+ if (modes & QIODevice::WriteOnly)
+ modeList << QLatin1String("WriteOnly");
+ if (modes & QIODevice::Append)
+ modeList << QLatin1String("Append");
+ if (modes & QIODevice::Truncate)
+ modeList << QLatin1String("Truncate");
+ if (modes & QIODevice::Text)
+ modeList << QLatin1String("Text");
+ if (modes & QIODevice::Unbuffered)
+ modeList << QLatin1String("Unbuffered");
+ }
+ qSort(modeList);
+ debug << modeList.join(QLatin1String("|"));
+ debug << ")";
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h
new file mode 100644
index 0000000..76d47ca
--- /dev/null
+++ b/src/corelib/io/qiodevice.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIODEVICE_H
+#define QIODEVICE_H
+
+#ifndef QT_NO_QOBJECT
+#include <QtCore/qobject.h>
+#else
+#include <QtCore/qobjectdefs.h>
+#endif
+#include <QtCore/qstring.h>
+
+#ifdef open
+#error qiodevice.h must be included before any header file that defines open
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QByteArray;
+class QIODevicePrivate;
+
+class Q_CORE_EXPORT QIODevice
+#ifndef QT_NO_QOBJECT
+ : public QObject
+#endif
+{
+#ifndef QT_NO_QOBJECT
+ Q_OBJECT
+#endif
+public:
+ enum OpenModeFlag {
+ NotOpen = 0x0000,
+ ReadOnly = 0x0001,
+ WriteOnly = 0x0002,
+ ReadWrite = ReadOnly | WriteOnly,
+ Append = 0x0004,
+ Truncate = 0x0008,
+ Text = 0x0010,
+ Unbuffered = 0x0020
+ };
+ Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)
+
+ QIODevice();
+#ifndef QT_NO_QOBJECT
+ explicit QIODevice(QObject *parent);
+#endif
+ virtual ~QIODevice();
+
+ OpenMode openMode() const;
+
+ void setTextModeEnabled(bool enabled);
+ bool isTextModeEnabled() const;
+
+ bool isOpen() const;
+ bool isReadable() const;
+ bool isWritable() const;
+ virtual bool isSequential() const;
+
+ virtual bool open(OpenMode mode);
+ virtual void close();
+
+ // ### Qt 5: pos() and seek() should not be virtual, and
+ // ### seek() should call a virtual seekData() function.
+ virtual qint64 pos() const;
+ virtual qint64 size() const;
+ virtual bool seek(qint64 pos);
+ virtual bool atEnd() const;
+ virtual bool reset();
+
+ virtual qint64 bytesAvailable() const;
+ virtual qint64 bytesToWrite() const;
+
+ qint64 read(char *data, qint64 maxlen);
+ QByteArray read(qint64 maxlen);
+ QByteArray readAll();
+ qint64 readLine(char *data, qint64 maxlen);
+ QByteArray readLine(qint64 maxlen = 0);
+ virtual bool canReadLine() const;
+
+ qint64 write(const char *data, qint64 len);
+ qint64 write(const char *data);
+ inline qint64 write(const QByteArray &data)
+ { return write(data.constData(), data.size()); }
+
+ qint64 peek(char *data, qint64 maxlen);
+ QByteArray peek(qint64 maxlen);
+
+ virtual bool waitForReadyRead(int msecs);
+ virtual bool waitForBytesWritten(int msecs);
+
+ void ungetChar(char c);
+ bool putChar(char c);
+ bool getChar(char *c);
+
+ QString errorString() const;
+
+#ifndef QT_NO_QOBJECT
+Q_SIGNALS:
+ void readyRead();
+ void bytesWritten(qint64 bytes);
+ void aboutToClose();
+ void readChannelFinished();
+#endif
+
+protected:
+#ifdef QT_NO_QOBJECT
+ QIODevice(QIODevicePrivate &dd);
+#else
+ QIODevice(QIODevicePrivate &dd, QObject *parent = 0);
+#endif
+ virtual qint64 readData(char *data, qint64 maxlen) = 0;
+ virtual qint64 readLineData(char *data, qint64 maxlen);
+ virtual qint64 writeData(const char *data, qint64 len) = 0;
+
+ void setOpenMode(OpenMode openMode);
+
+ void setErrorString(const QString &errorString);
+
+#ifdef QT_NO_QOBJECT
+ QIODevicePrivate *d_ptr;
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QIODevice)
+ Q_DISABLE_COPY(QIODevice)
+
+#ifdef QT3_SUPPORT
+public:
+ typedef qint64 Offset;
+
+ inline QT3_SUPPORT int flags() const { return static_cast<int>(openMode()); }
+ inline QT3_SUPPORT int mode() const { return static_cast<int>(openMode()); }
+ inline QT3_SUPPORT int state() const;
+
+ inline QT3_SUPPORT bool isDirectAccess() const { return !isSequential(); }
+ inline QT3_SUPPORT bool isSequentialAccess() const { return isSequential(); }
+ inline QT3_SUPPORT bool isCombinedAccess() const { return false; }
+ inline QT3_SUPPORT bool isBuffered() const { return true; }
+ inline QT3_SUPPORT bool isRaw() const { return false; }
+ inline QT3_SUPPORT bool isSynchronous() const { return true; }
+ inline QT3_SUPPORT bool isAsynchronous() const { return false; }
+ inline QT3_SUPPORT bool isTranslated() const { return (openMode() & Text) != 0; }
+ inline QT3_SUPPORT bool isInactive() const { return !isOpen(); }
+
+ typedef int Status;
+ QT3_SUPPORT Status status() const;
+ QT3_SUPPORT void resetStatus();
+
+ inline QT3_SUPPORT Offset at() const { return pos(); }
+ inline QT3_SUPPORT bool at(Offset offset) { return seek(offset); }
+
+ inline QT3_SUPPORT qint64 readBlock(char *data, quint64 maxlen) { return read(data, maxlen); }
+ inline QT3_SUPPORT qint64 writeBlock(const char *data, quint64 len) { return write(data, len); }
+ inline QT3_SUPPORT qint64 writeBlock(const QByteArray &data) { return write(data); }
+
+ inline QT3_SUPPORT int getch() { char c; return getChar(&c) ? int(uchar(c)) : -1; }
+ inline QT3_SUPPORT int putch(int c) { return putChar(c) ? int(uchar(c)) : -1; }
+ inline QT3_SUPPORT int ungetch(int c) { ungetChar(uchar(c)); return c; }
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QIODevice::OpenMode)
+
+#ifdef QT3_SUPPORT
+static QT3_SUPPORT_VARIABLE const uint IO_Direct = 0x0100;
+static QT3_SUPPORT_VARIABLE const uint IO_Sequential = 0x0200;
+static QT3_SUPPORT_VARIABLE const uint IO_Combined = 0x0300;
+static QT3_SUPPORT_VARIABLE const uint IO_TypeMask = 0x0300;
+
+static QT3_SUPPORT_VARIABLE const uint IO_Raw = 0x0000;
+static QT3_SUPPORT_VARIABLE const uint IO_Async = 0x0000;
+
+#define IO_ReadOnly QIODevice::ReadOnly
+#define IO_WriteOnly QIODevice::WriteOnly
+#define IO_ReadWrite QIODevice::ReadWrite
+#define IO_Append QIODevice::Append
+#define IO_Truncate QIODevice::Truncate
+#define IO_Translate QIODevice::Text
+#define IO_ModeMask 0x00ff
+
+static QT3_SUPPORT_VARIABLE const uint IO_Open = 0x1000;
+static QT3_SUPPORT_VARIABLE const uint IO_StateMask = 0xf000;
+
+static QT3_SUPPORT_VARIABLE const uint IO_Ok = 0;
+static QT3_SUPPORT_VARIABLE const uint IO_ReadError = 1;
+static QT3_SUPPORT_VARIABLE const uint IO_WriteError = 2;
+static QT3_SUPPORT_VARIABLE const uint IO_FatalError = 3;
+static QT3_SUPPORT_VARIABLE const uint IO_ResourceError = 4;
+static QT3_SUPPORT_VARIABLE const uint IO_OpenError = 5;
+static QT3_SUPPORT_VARIABLE const uint IO_ConnectError = 5;
+static QT3_SUPPORT_VARIABLE const uint IO_AbortError = 6;
+static QT3_SUPPORT_VARIABLE const uint IO_TimeOutError = 7;
+static QT3_SUPPORT_VARIABLE const uint IO_UnspecifiedError = 8;
+
+inline QT3_SUPPORT int QIODevice::state() const
+{
+ return isOpen() ? 0x1000 : 0;
+}
+#endif
+
+#if !defined(QT_NO_DEBUG_STREAM)
+class QDebug;
+Q_CORE_EXPORT QDebug operator<<(QDebug debug, QIODevice::OpenMode modes);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QIODEVICE_H
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h
new file mode 100644
index 0000000..b996225
--- /dev/null
+++ b/src/corelib/io/qiodevice_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIODEVICE_P_H
+#define QIODEVICE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QIODevice. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qiodevice.h"
+#include "QtCore/qbytearray.h"
+#include "QtCore/qobjectdefs.h"
+#include "QtCore/qstring.h"
+#include "private/qringbuffer_p.h"
+#ifndef QT_NO_QOBJECT
+#include "private/qobject_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QIODevicePrivate
+#ifndef QT_NO_QOBJECT
+ : public QObjectPrivate
+#endif
+{
+ Q_DECLARE_PUBLIC(QIODevice)
+
+public:
+ QIODevicePrivate();
+ virtual ~QIODevicePrivate();
+
+ QIODevice::OpenMode openMode;
+ QString errorString;
+
+ QRingBuffer buffer;
+ qint64 pos;
+ qint64 devicePos;
+ bool baseReadLineDataCalled;
+
+ virtual bool putCharHelper(char c);
+
+ enum AccessMode {
+ Unset,
+ Sequential,
+ RandomAccess
+ };
+ mutable AccessMode accessMode;
+ inline bool isSequential() const
+ {
+ if (accessMode == Unset)
+ accessMode = q_func()->isSequential() ? Sequential : RandomAccess;
+ return accessMode == Sequential;
+ }
+
+
+#ifdef QT_NO_QOBJECT
+ QIODevice *q_ptr;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QIODEVICE_P_H
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
new file mode 100644
index 0000000..a9d8ee2
--- /dev/null
+++ b/src/corelib/io/qprocess.cpp
@@ -0,0 +1,1827 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QPROCESS_DEBUG
+
+#if defined QPROCESS_DEBUG
+#include <qdebug.h>
+#include <qstring.h>
+#include <ctype.h>
+#if !defined(Q_OS_WINCE)
+#include <errno.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len && i < maxSize; ++i) {
+ char c = data[i];
+ if (isprint(c)) {
+ out += c;
+ } else switch (c) {
+ case '\n': out += "\\n"; break;
+ case '\r': out += "\\r"; break;
+ case '\t': out += "\\t"; break;
+ default:
+ char buf[5];
+ qsnprintf(buf, sizeof(buf), "\\%3o", c);
+ buf[4] = '\0';
+ out += QByteArray(buf);
+ }
+ }
+
+ if (len < maxSize)
+ out += "...";
+
+ return out;
+}
+
+QT_END_NAMESPACE
+
+#endif
+
+#include "qprocess.h"
+#include "qprocess_p.h"
+
+#include <qbytearray.h>
+#include <qdatetime.h>
+#include <qcoreapplication.h>
+#include <qsocketnotifier.h>
+#include <qtimer.h>
+
+#ifdef Q_WS_WIN
+#include <private/qwineventnotifier_p.h>
+#endif
+
+#ifndef QT_NO_PROCESS
+
+QT_BEGIN_NAMESPACE
+
+void QProcessPrivate::Channel::clear()
+{
+ switch (type) {
+ case PipeSource:
+ Q_ASSERT(process);
+ process->stdinChannel.type = Normal;
+ process->stdinChannel.process = 0;
+ break;
+ case PipeSink:
+ Q_ASSERT(process);
+ process->stdoutChannel.type = Normal;
+ process->stdoutChannel.process = 0;
+ break;
+ }
+
+ type = Normal;
+ file.clear();
+ process = 0;
+}
+
+/*!
+ \class QProcess
+
+ \brief The QProcess class is used to start external programs and
+ to communicate with them.
+
+ \ingroup io
+ \ingroup misc
+ \mainclass
+ \reentrant
+
+ To start a process, pass the name and command line arguments of
+ the program you want to run as arguments to start(). For example:
+
+ \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 0
+ \dots
+ \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 1
+ \snippet doc/src/snippets/qprocess/qprocess-simpleexecution.cpp 2
+
+ QProcess then enters the \l Starting state, and when the program
+ has started, QProcess enters the \l Running state and emits
+ started().
+
+ QProcess allows you to treat a process as a sequential I/O
+ device. You can write to and read from the process just as you
+ would access a network connection using QTcpSocket. You can then
+ write to the process's standard input by calling write(), and
+ read the standard output by calling read(), readLine(), and
+ getChar(). Because it inherits QIODevice, QProcess can also be
+ 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.
+
+ When the process exits, QProcess reenters the \l NotRunning state
+ (the initial state), and emits finished().
+
+ The finished() signal provides the exit code and exit status of
+ the process as arguments, and you can also call exitCode() to
+ obtain the exit code of the last process that finished, and
+ exitStatus() to obtain its exit status. If an error occurs at
+ any point in time, QProcess will emit the error() signal. You
+ can also call error() to find the type of error that occurred
+ last, and state() to find the current process state.
+
+ \section1 Communicating via Channels
+
+ Processes have two predefined output channels: The standard
+ output channel (\c stdout) supplies regular console output, and
+ the standard error channel (\c stderr) usually supplies the
+ errors that are printed by the process. These channels represent
+ two separate streams of data. You can toggle between them by
+ calling setReadChannel(). QProcess emits readyRead() when data is
+ available on the current read channel. It also emits
+ readyReadStandardOutput() when new standard output data is
+ available, and when new standard error data is available,
+ readyReadStandardError() is emitted. Instead of calling read(),
+ readLine(), or getChar(), you can explicitly read all data from
+ either of the two channels by calling readAllStandardOutput() or
+ readAllStandardError().
+
+ The terminology for the channels can be misleading. Be aware that
+ the process's output channels correspond to QProcess's
+ \e read channels, whereas the process's input channels correspond
+ to QProcess's \e write channels. This is because what we read
+ using QProcess is the process's output, and what we write becomes
+ the process's input.
+
+ QProcess can merge the two output channels, so that standard
+ output and standard error data from the running process both use
+ the standard output channel. Call setProcessChannelMode() with
+ MergedChannels before starting the process to activative
+ this feature. You also have the option of forwarding the output of
+ the running process to the calling, main process, by passing
+ ForwardedChannels as the argument.
+
+ Certain processes need special environment settings in order to
+ operate. You can set environment variables for your process by
+ calling setEnvironment(). To set a working directory, call
+ setWorkingDirectory(). By default, processes are run in the
+ current working directory of the calling process.
+
+ \section1 Synchronous Process API
+
+ QProcess provides a set of functions which allow it to be used
+ without an event loop, by suspending the calling thread until
+ certain signals are emitted:
+
+ \list
+ \o waitForStarted() blocks until the process has started.
+
+ \o waitForReadyRead() blocks until new data is
+ available for reading on the current read channel.
+
+ \o waitForBytesWritten() blocks until one payload of
+ data has been written to the process.
+
+ \o waitForFinished() blocks until the process has finished.
+ \endlist
+
+ Calling these functions from the main thread (the thread that
+ calls QApplication::exec()) may cause your user interface to
+ freeze.
+
+ The following example runs \c gzip to compress the string "Qt
+ rocks!", without an event loop:
+
+ \snippet doc/src/snippets/process/process.cpp 0
+
+ \section1 Notes for Windows Users
+
+ Some Windows commands (for example, \c dir) are not provided by
+ separate applications, but by the command interpreter itself.
+ If you attempt to use QProcess to execute these commands directly,
+ it won't work. One possible solution is to execute the command
+ interpreter itself (\c{cmd.exe} on some Windows systems), and ask
+ the interpreter to execute the desired command.
+
+ \sa QBuffer, QFile, QTcpSocket
+*/
+
+/*!
+ \enum QProcess::ProcessChannel
+
+ This enum describes the process channels used by the running process.
+ Pass one of these values to setReadChannel() to set the
+ current read channel of QProcess.
+
+ \value StandardOutput The standard output (stdout) of the running
+ process.
+
+ \value StandardError The standard error (stderr) of the running
+ process.
+
+ \sa setReadChannel()
+*/
+
+/*!
+ \enum QProcess::ProcessChannelMode
+
+ This enum describes the process channel modes of QProcess. Pass
+ one of these values to setProcessChannelMode() to set the
+ current read channel mode.
+
+ \value SeparateChannels QProcess manages the output of the
+ running process, keeping standard output and standard error data
+ in separate internal buffers. You can select the QProcess's
+ current read channel by calling setReadChannel(). This is the
+ default channel mode of QProcess.
+
+ \value MergedChannels QProcess merges the output of the running
+ process into the standard output channel (\c stdout). The
+ standard error channel (\c stderr) will not receive any data. The
+ standard output and standard error data of the running process
+ are interleaved.
+
+ \value ForwardedChannels QProcess forwards the output of the
+ running process onto the main process. Anything the child process
+ writes to its standard output and standard error will be written
+ to the standard output and standard error of the main process.
+
+ \sa setReadChannelMode()
+*/
+
+/*!
+ \enum QProcess::ProcessError
+
+ This enum describes the different types of errors that are
+ reported by QProcess.
+
+ \value FailedToStart The process failed to start. Either the
+ invoked program is missing, or you may have insufficient
+ permissions to invoke the program.
+
+ \value Crashed The process crashed some time after starting
+ successfully.
+
+ \value Timedout The last waitFor...() function timed out. The
+ state of QProcess is unchanged, and you can try calling
+ waitFor...() again.
+
+ \value WriteError An error occurred when attempting to write to the
+ process. For example, the process may not be running, or it may
+ have closed its input channel.
+
+ \value ReadError An error occurred when attempting to read from
+ the process. For example, the process may not be running.
+
+ \value UnknownError An unknown error occurred. This is the default
+ return value of error().
+
+ \sa error()
+*/
+
+/*!
+ \enum QProcess::ProcessState
+
+ This enum describes the different states of QProcess.
+
+ \value NotRunning The process is not running.
+
+ \value Starting The process is starting, but the program has not
+ yet been invoked.
+
+ \value Running The process is running and is ready for reading and
+ writing.
+
+ \sa state()
+*/
+
+/*!
+ \enum QProcess::ExitStatus
+
+ This enum describes the different exit statuses of QProcess.
+
+ \value NormalExit The process exited normally.
+
+ \value CrashExit The process crashed.
+
+ \sa exitStatus()
+*/
+
+/*!
+ \fn void QProcess::error(QProcess::ProcessError error)
+
+ This signal is emitted when an error occurs with the process. The
+ specified \a error describes the type of error that occurred.
+*/
+
+/*!
+ \fn void QProcess::started()
+
+ This signal is emitted by QProcess when the process has started,
+ and state() returns \l Running.
+*/
+
+/*!
+ \fn void QProcess::stateChanged(QProcess::ProcessState newState)
+
+ This signal is emitted whenever the state of QProcess changes. The
+ \a newState argument is the state QProcess changed to.
+*/
+
+/*!
+ \fn void QProcess::finished(int exitCode)
+ \obsolete
+ \overload
+
+ Use finished(int exitCode, QProcess::ExitStatus status) instead.
+*/
+
+/*!
+ \fn void QProcess::finished(int exitCode, QProcess::ExitStatus exitStatus)
+
+ This signal is emitted when the process finishes. \a exitCode is the exit
+ code of the process, and \a exitStatus is the exit status. After the
+ process has finished, the buffers in QProcess are still intact. You can
+ still read any data that the process may have written before it finished.
+
+ \sa exitStatus()
+*/
+
+/*!
+ \fn void QProcess::readyReadStandardOutput()
+
+ This signal is emitted when the process has made new data
+ available through its standard output channel (\c stdout). It is
+ emitted regardless of the current \l{readChannel()}{read channel}.
+
+ \sa readAllStandardOutput(), readChannel()
+*/
+
+/*!
+ \fn void QProcess::readyReadStandardError()
+
+ This signal is emitted when the process has made new data
+ available through its standard error channel (\c stderr). It is
+ emitted regardless of the current \l{readChannel()}{read
+ channel}.
+
+ \sa readAllStandardError(), readChannel()
+*/
+
+/*! \internal
+*/
+QProcessPrivate::QProcessPrivate()
+{
+ processChannel = QProcess::StandardOutput;
+ processChannelMode = QProcess::SeparateChannels;
+ processError = QProcess::UnknownError;
+ processState = QProcess::NotRunning;
+ pid = 0;
+ sequenceNumber = 0;
+ exitCode = 0;
+ exitStatus = QProcess::NormalExit;
+ startupSocketNotifier = 0;
+ deathNotifier = 0;
+ notifier = 0;
+ pipeWriter = 0;
+ childStartedPipe[0] = INVALID_Q_PIPE;
+ childStartedPipe[1] = INVALID_Q_PIPE;
+ deathPipe[0] = INVALID_Q_PIPE;
+ deathPipe[1] = INVALID_Q_PIPE;
+ exitCode = 0;
+ crashed = false;
+ dying = false;
+ emittedReadyRead = false;
+ emittedBytesWritten = false;
+#ifdef Q_WS_WIN
+ pipeWriter = 0;
+ processFinishedNotifier = 0;
+#endif // Q_WS_WIN
+#ifdef Q_OS_UNIX
+ serial = 0;
+#endif
+}
+
+/*! \internal
+*/
+QProcessPrivate::~QProcessPrivate()
+{
+ if (stdinChannel.process)
+ stdinChannel.process->stdoutChannel.clear();
+ if (stdoutChannel.process)
+ stdoutChannel.process->stdinChannel.clear();
+}
+
+/*! \internal
+*/
+void QProcessPrivate::cleanup()
+{
+ q_func()->setProcessState(QProcess::NotRunning);
+#ifdef Q_OS_WIN
+ if (pid) {
+ CloseHandle(pid->hThread);
+ CloseHandle(pid->hProcess);
+ delete pid;
+ pid = 0;
+ }
+ if (processFinishedNotifier) {
+ processFinishedNotifier->setEnabled(false);
+ qDeleteInEventHandler(processFinishedNotifier);
+ processFinishedNotifier = 0;
+ }
+
+#endif
+ pid = 0;
+ sequenceNumber = 0;
+ dying = false;
+
+ if (stdoutChannel.notifier) {
+ stdoutChannel.notifier->setEnabled(false);
+ qDeleteInEventHandler(stdoutChannel.notifier);
+ stdoutChannel.notifier = 0;
+ }
+ if (stderrChannel.notifier) {
+ stderrChannel.notifier->setEnabled(false);
+ qDeleteInEventHandler(stderrChannel.notifier);
+ stderrChannel.notifier = 0;
+ }
+ if (stdinChannel.notifier) {
+ stdinChannel.notifier->setEnabled(false);
+ qDeleteInEventHandler(stdinChannel.notifier);
+ stdinChannel.notifier = 0;
+ }
+ if (startupSocketNotifier) {
+ startupSocketNotifier->setEnabled(false);
+ qDeleteInEventHandler(startupSocketNotifier);
+ startupSocketNotifier = 0;
+ }
+ if (deathNotifier) {
+ deathNotifier->setEnabled(false);
+ qDeleteInEventHandler(deathNotifier);
+ deathNotifier = 0;
+ }
+ if (notifier) {
+ qDeleteInEventHandler(notifier);
+ notifier = 0;
+ }
+ destroyPipe(stdoutChannel.pipe);
+ destroyPipe(stderrChannel.pipe);
+ destroyPipe(stdinChannel.pipe);
+ destroyPipe(childStartedPipe);
+ destroyPipe(deathPipe);
+#ifdef Q_OS_UNIX
+ serial = 0;
+#endif
+}
+
+/*! \internal
+*/
+bool QProcessPrivate::_q_canReadStandardOutput()
+{
+ Q_Q(QProcess);
+ qint64 available = bytesAvailableFromStdout();
+ if (available == 0) {
+ if (stdoutChannel.notifier)
+ stdoutChannel.notifier->setEnabled(false);
+ destroyPipe(stdoutChannel.pipe);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::canReadStandardOutput(), 0 bytes available");
+#endif
+ return false;
+ }
+
+ char *ptr = outputReadBuffer.reserve(available);
+ qint64 readBytes = readFromStdout(ptr, available);
+ if (readBytes == -1) {
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::canReadStandardOutput(), failed to read from the process");
+#endif
+ return false;
+ }
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::canReadStandardOutput(), read %d bytes from the process' output",
+ int(readBytes));
+#endif
+
+ if (stdoutChannel.closed) {
+ outputReadBuffer.chop(readBytes);
+ return false;
+ }
+
+ outputReadBuffer.chop(available - readBytes);
+
+ bool didRead = false;
+ if (readBytes == 0) {
+ if (stdoutChannel.notifier)
+ stdoutChannel.notifier->setEnabled(false);
+ } else if (processChannel == QProcess::StandardOutput) {
+ didRead = true;
+ if (!emittedReadyRead) {
+ emittedReadyRead = true;
+ emit q->readyRead();
+ emittedReadyRead = false;
+ }
+ }
+ emit q->readyReadStandardOutput();
+ return didRead;
+}
+
+/*! \internal
+*/
+bool QProcessPrivate::_q_canReadStandardError()
+{
+ Q_Q(QProcess);
+ qint64 available = bytesAvailableFromStderr();
+ if (available == 0) {
+ if (stderrChannel.notifier)
+ stderrChannel.notifier->setEnabled(false);
+ destroyPipe(stderrChannel.pipe);
+ return false;
+ }
+
+ char *ptr = errorReadBuffer.reserve(available);
+ qint64 readBytes = readFromStderr(ptr, available);
+ if (readBytes == -1) {
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+ return false;
+ }
+ if (stderrChannel.closed) {
+ errorReadBuffer.chop(readBytes);
+ return false;
+ }
+
+ errorReadBuffer.chop(available - readBytes);
+
+ bool didRead = false;
+ if (readBytes == 0) {
+ if (stderrChannel.notifier)
+ stderrChannel.notifier->setEnabled(false);
+ } else if (processChannel == QProcess::StandardError) {
+ didRead = true;
+ if (!emittedReadyRead) {
+ emittedReadyRead = true;
+ emit q->readyRead();
+ emittedReadyRead = false;
+ }
+ }
+ emit q->readyReadStandardError();
+ return didRead;
+}
+
+/*! \internal
+*/
+bool QProcessPrivate::_q_canWrite()
+{
+ Q_Q(QProcess);
+ if (stdinChannel.notifier)
+ stdinChannel.notifier->setEnabled(false);
+
+ if (writeBuffer.isEmpty()) {
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
+#endif
+ return false;
+ }
+
+ qint64 written = writeToStdin(writeBuffer.readPointer(),
+ writeBuffer.nextDataBlockSize());
+ if (written < 0) {
+ destroyPipe(stdinChannel.pipe);
+ processError = QProcess::WriteError;
+ q->setErrorString(QProcess::tr("Error writing to process"));
+#if defined(QPROCESS_DEBUG) && !defined(Q_OS_WINCE)
+ qDebug("QProcessPrivate::canWrite(), failed to write (%s)", strerror(errno));
+#endif
+ emit q->error(processError);
+ return false;
+ }
+
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::canWrite(), wrote %d bytes to the process input", int(written));
+#endif
+
+ writeBuffer.free(written);
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(written);
+ emittedBytesWritten = false;
+ }
+ if (stdinChannel.notifier && !writeBuffer.isEmpty())
+ stdinChannel.notifier->setEnabled(true);
+ if (writeBuffer.isEmpty() && stdinChannel.closed)
+ closeWriteChannel();
+ return true;
+}
+
+/*! \internal
+*/
+bool QProcessPrivate::_q_processDied()
+{
+ Q_Q(QProcess);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::_q_processDied()");
+#endif
+#ifdef Q_OS_UNIX
+ if (!waitForDeadChild())
+ return false;
+#endif
+#ifdef Q_OS_WIN
+ if (processFinishedNotifier)
+ processFinishedNotifier->setEnabled(false);
+#endif
+
+ // the process may have died before it got a chance to report that it was
+ // either running or stopped, so we will call _q_startupNotification() and
+ // give it a chance to emit started() or error(FailedToStart).
+ if (processState == QProcess::Starting) {
+ if (!_q_startupNotification())
+ return true;
+ }
+
+ if (dying) {
+ // at this point we know the process is dead. prevent
+ // reentering this slot recursively by calling waitForFinished()
+ // or opening a dialog inside slots connected to the readyRead
+ // signals emitted below.
+ return true;
+ }
+ dying = true;
+
+ // in case there is data in the pipe line and this slot by chance
+ // got called before the read notifications, call these two slots
+ // so the data is made available before the process dies.
+ _q_canReadStandardOutput();
+ _q_canReadStandardError();
+
+ findExitCode();
+
+ if (crashed) {
+ exitStatus = QProcess::CrashExit;
+ processError = QProcess::Crashed;
+ q->setErrorString(QProcess::tr("Process crashed"));
+ emit q->error(processError);
+ }
+
+ bool wasRunning = (processState == QProcess::Running);
+
+ cleanup();
+
+ if (wasRunning) {
+ // we received EOF now:
+ emit q->readChannelFinished();
+ // in the future:
+ //emit q->standardOutputClosed();
+ //emit q->standardErrorClosed();
+
+ emit q->finished(exitCode);
+ emit q->finished(exitCode, exitStatus);
+ }
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::_q_processDied() process is dead");
+#endif
+ return true;
+}
+
+/*! \internal
+*/
+bool QProcessPrivate::_q_startupNotification()
+{
+ Q_Q(QProcess);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::startupNotification()");
+#endif
+
+ if (startupSocketNotifier)
+ startupSocketNotifier->setEnabled(false);
+ if (processStarted()) {
+ q->setProcessState(QProcess::Running);
+ emit q->started();
+ return true;
+ }
+
+ q->setProcessState(QProcess::NotRunning);
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+#ifdef Q_OS_UNIX
+ // make sure the process manager removes this entry
+ waitForDeadChild();
+ findExitCode();
+#endif
+ cleanup();
+ return false;
+}
+
+/*! \internal
+*/
+void QProcessPrivate::closeWriteChannel()
+{
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::closeWriteChannel()");
+#endif
+ if (stdinChannel.notifier) {
+ extern void qDeleteInEventHandler(QObject *o);
+ stdinChannel.notifier->setEnabled(false);
+ if (stdinChannel.notifier) {
+ qDeleteInEventHandler(stdinChannel.notifier);
+ stdinChannel.notifier = 0;
+ }
+ }
+#ifdef Q_OS_WIN
+ // ### Find a better fix, feeding the process little by little
+ // instead.
+ flushPipeWriter();
+#endif
+ destroyPipe(stdinChannel.pipe);
+}
+
+/*!
+ Constructs a QProcess object with the given \a parent.
+*/
+QProcess::QProcess(QObject *parent)
+ : QIODevice(*new QProcessPrivate, parent)
+{
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::QProcess(%p)", parent);
+#endif
+}
+
+/*!
+ Destructs the QProcess object, i.e., killing the process.
+
+ Note that this function will not return until the process is
+ terminated.
+*/
+QProcess::~QProcess()
+{
+ Q_D(QProcess);
+ if (d->processState != NotRunning) {
+ qWarning("QProcess: Destroyed while process is still running.");
+ kill();
+ waitForFinished();
+ }
+#ifdef Q_OS_UNIX
+ // make sure the process manager removes this entry
+ d->findExitCode();
+#endif
+ d->cleanup();
+}
+
+/*!
+ \obsolete
+ Returns the read channel mode of the QProcess. This function is
+ equivalent to processChannelMode()
+
+ \sa processChannelMode()
+*/
+QProcess::ProcessChannelMode QProcess::readChannelMode() const
+{
+ return processChannelMode();
+}
+
+/*!
+ \obsolete
+
+ Use setProcessChannelMode(\a mode) instead.
+
+ \sa setProcessChannelMode()
+*/
+void QProcess::setReadChannelMode(ProcessChannelMode mode)
+{
+ setProcessChannelMode(mode);
+}
+
+/*!
+ \since 4.2
+
+ Returns the channel mode of the QProcess standard output and
+ standard error channels.
+
+ \sa setReadChannelMode(), ProcessChannelMode, setReadChannel()
+*/
+QProcess::ProcessChannelMode QProcess::processChannelMode() const
+{
+ Q_D(const QProcess);
+ return d->processChannelMode;
+}
+
+/*!
+ \since 4.2
+
+ Sets the channel mode of the QProcess standard output and standard
+ error channels to the \a mode specified.
+ This mode will be used the next time start() is called. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 0
+
+ \sa readChannelMode(), ProcessChannelMode, setReadChannel()
+*/
+void QProcess::setProcessChannelMode(ProcessChannelMode mode)
+{
+ Q_D(QProcess);
+ d->processChannelMode = mode;
+}
+
+/*!
+ Returns the current read channel of the QProcess.
+
+ \sa setReadChannel()
+*/
+QProcess::ProcessChannel QProcess::readChannel() const
+{
+ Q_D(const QProcess);
+ return d->processChannel;
+}
+
+/*!
+ Sets the current read channel of the QProcess to the given \a
+ channel. The current input channel is used by the functions
+ read(), readAll(), readLine(), and getChar(). It also determines
+ which channel triggers QProcess to emit readyRead().
+
+ \sa readChannel()
+*/
+void QProcess::setReadChannel(ProcessChannel channel)
+{
+ Q_D(QProcess);
+ if (d->processChannel != channel) {
+ QByteArray buf = d->buffer.readAll();
+ if (d->processChannel == QProcess::StandardOutput) {
+ for (int i = buf.size() - 1; i >= 0; --i)
+ d->outputReadBuffer.ungetChar(buf.at(i));
+ } else {
+ for (int i = buf.size() - 1; i >= 0; --i)
+ d->errorReadBuffer.ungetChar(buf.at(i));
+ }
+ }
+ d->processChannel = channel;
+}
+
+/*!
+ Closes the read channel \a channel. After calling this function,
+ QProcess will no longer receive data on the channel. Any data that
+ has already been received is still available for reading.
+
+ Call this function to save memory, if you are not interested in
+ the output of the process.
+
+ \sa closeWriteChannel(), setReadChannel()
+*/
+void QProcess::closeReadChannel(ProcessChannel channel)
+{
+ Q_D(QProcess);
+
+ if (channel == StandardOutput)
+ d->stdoutChannel.closed = true;
+ else
+ d->stderrChannel.closed = true;
+}
+
+/*!
+ Schedules the write channel of QProcess to be closed. The channel
+ will close once all data has been written to the process. After
+ calling this function, any attempts to write to the process will
+ fail.
+
+ Closing the write channel is necessary for programs that read
+ input data until the channel has been closed. For example, the
+ program "more" is used to display text data in a console on both
+ Unix and Windows. But it will not display the text data until
+ QProcess's write channel has been closed. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 1
+
+ The write channel is implicitly opened when start() is called.
+
+ \sa closeReadChannel()
+*/
+void QProcess::closeWriteChannel()
+{
+ Q_D(QProcess);
+ d->stdinChannel.closed = true; // closing
+ if (d->writeBuffer.isEmpty())
+ d->closeWriteChannel();
+}
+
+/*!
+ \since 4.2
+
+ Redirects the process' standard input to the file indicated by \a
+ fileName. When an input redirection is in place, the QProcess
+ object will be in read-only mode (calling write() will result in
+ error).
+
+ If the file \a fileName does not exist at the moment start() is
+ called or is not readable, starting the process will fail.
+
+ Calling setStandardInputFile() after the process has started has no
+ effect.
+
+ \sa setStandardOutputFile(), setStandardErrorFile(),
+ setStandardOutputProcess()
+*/
+void QProcess::setStandardInputFile(const QString &fileName)
+{
+ Q_D(QProcess);
+ d->stdinChannel = fileName;
+}
+
+/*!
+ \since 4.2
+
+ Redirects the process' standard output to the file \a
+ fileName. When the redirection is in place, the standard output
+ read channel is closed: reading from it using read() will always
+ fail, as will readAllStandardOutput().
+
+ If the file \a fileName doesn't exist at the moment start() is
+ called, it will be created. If it cannot be created, the starting
+ will fail.
+
+ If the file exists and \a mode is QIODevice::Truncate, the file
+ will be truncated. Otherwise (if \a mode is QIODevice::Append),
+ the file will be appended to.
+
+ Calling setStandardOutputFile() after the process has started has
+ no effect.
+
+ \sa setStandardInputFile(), setStandardErrorFile(),
+ setStandardOutputProcess()
+*/
+void QProcess::setStandardOutputFile(const QString &fileName, OpenMode mode)
+{
+ Q_ASSERT(mode == Append || mode == Truncate);
+ Q_D(QProcess);
+
+ d->stdoutChannel = fileName;
+ d->stdoutChannel.append = mode == Append;
+}
+
+/*!
+ \since 4.2
+
+ Redirects the process' standard error to the file \a
+ fileName. When the redirection is in place, the standard error
+ read channel is closed: reading from it using read() will always
+ fail, as will readAllStandardError(). The file will be appended to
+ if \a mode is Append, otherwise, it will be truncated.
+
+ See setStandardOutputFile() for more information on how the file
+ is opened.
+
+ Note: if setProcessChannelMode() was called with an argument of
+ QProcess::MergedChannels, this function has no effect.
+
+ \sa setStandardInputFile(), setStandardOutputFile(),
+ setStandardOutputProcess()
+*/
+void QProcess::setStandardErrorFile(const QString &fileName, OpenMode mode)
+{
+ Q_ASSERT(mode == Append || mode == Truncate);
+ Q_D(QProcess);
+
+ d->stderrChannel = fileName;
+ d->stderrChannel.append = mode == Append;
+}
+
+/*!
+ \since 4.2
+
+ Pipes the standard output stream of this process to the \a
+ destination process' standard input.
+
+ The following shell command:
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 2
+
+ Can be accomplished with QProcesses with the following code:
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 3
+*/
+void QProcess::setStandardOutputProcess(QProcess *destination)
+{
+ QProcessPrivate *dfrom = d_func();
+ QProcessPrivate *dto = destination->d_func();
+ dfrom->stdoutChannel.pipeTo(dto);
+ dto->stdinChannel.pipeFrom(dfrom);
+}
+
+/*!
+ If QProcess has been assigned a working directory, this function returns
+ the working directory that the QProcess will enter before the program has
+ started. Otherwise, (i.e., no directory has been assigned,) an empty
+ string is returned, and QProcess will use the application's current
+ working directory instead.
+
+ \sa setWorkingDirectory()
+*/
+QString QProcess::workingDirectory() const
+{
+ Q_D(const QProcess);
+ return d->workingDirectory;
+}
+
+/*!
+ Sets the working directory to \a dir. QProcess will start the
+ process in this directory. The default behavior is to start the
+ process in the working directory of the calling process.
+
+ \sa workingDirectory(), start()
+*/
+void QProcess::setWorkingDirectory(const QString &dir)
+{
+ Q_D(QProcess);
+ d->workingDirectory = dir;
+}
+
+/*!
+ Returns the native process identifier for the running process, if
+ available. If no process is currently running, 0 is returned.
+*/
+Q_PID QProcess::pid() const
+{
+ Q_D(const QProcess);
+ return d->pid;
+}
+
+/*! \reimp
+
+ This function operates on the current read channel.
+
+ \sa readChannel(), setReadChannel()
+*/
+bool QProcess::canReadLine() const
+{
+ Q_D(const QProcess);
+ const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
+ ? &d->errorReadBuffer
+ : &d->outputReadBuffer;
+ return readBuffer->canReadLine() || QIODevice::canReadLine();
+}
+
+/*!
+ Closes all communication with the process and kills it. After calling this
+ function, QProcess will no longer emit readyRead(), and data can no
+ longer be read or written.
+*/
+void QProcess::close()
+{
+ emit aboutToClose();
+ while (waitForBytesWritten(-1))
+ ;
+ kill();
+ waitForFinished(-1);
+ QIODevice::close();
+}
+
+/*! \reimp
+
+ Returns true if the process is not running, and no more data is available
+ for reading; otherwise returns false.
+*/
+bool QProcess::atEnd() const
+{
+ Q_D(const QProcess);
+ const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
+ ? &d->errorReadBuffer
+ : &d->outputReadBuffer;
+ return QIODevice::atEnd() && (!isOpen() || readBuffer->isEmpty());
+}
+
+/*! \reimp
+*/
+bool QProcess::isSequential() const
+{
+ return true;
+}
+
+/*! \reimp
+*/
+qint64 QProcess::bytesAvailable() const
+{
+ Q_D(const QProcess);
+ const QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
+ ? &d->errorReadBuffer
+ : &d->outputReadBuffer;
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::bytesAvailable() == %i (%s)", readBuffer->size(),
+ (d->processChannel == QProcess::StandardError) ? "stderr" : "stdout");
+#endif
+ return readBuffer->size() + QIODevice::bytesAvailable();
+}
+
+/*! \reimp
+*/
+qint64 QProcess::bytesToWrite() const
+{
+ Q_D(const QProcess);
+ qint64 size = d->writeBuffer.size();
+#ifdef Q_OS_WIN
+ size += d->pipeWriterBytesToWrite();
+#endif
+ return size;
+}
+
+/*!
+ Returns the type of error that occurred last.
+
+ \sa state()
+*/
+QProcess::ProcessError QProcess::error() const
+{
+ Q_D(const QProcess);
+ return d->processError;
+}
+
+/*!
+ Returns the current state of the process.
+
+ \sa stateChanged(), error()
+*/
+QProcess::ProcessState QProcess::state() const
+{
+ Q_D(const QProcess);
+ return d->processState;
+}
+
+/*!
+ Sets the environment that QProcess will use when starting a process to the
+ \a environment specified which consists of a list of key=value pairs.
+
+ For example, the following code adds the \c{C:\\BIN} directory to the list of
+ executable paths (\c{PATHS}) on Windows:
+
+ \snippet doc/src/snippets/qprocess-environment/main.cpp 0
+
+ \sa environment(), systemEnvironment()
+*/
+void QProcess::setEnvironment(const QStringList &environment)
+{
+ Q_D(QProcess);
+ d->environment = environment;
+}
+
+/*!
+ Returns the environment that QProcess will use when starting a
+ process, or an empty QStringList if no environment has been set
+ using setEnvironment(). If no environment has been set, the
+ environment of the calling process will be used.
+
+ \note The environment settings are ignored on Windows CE,
+ as there is no concept of an environment.
+
+ \sa setEnvironment(), systemEnvironment()
+*/
+QStringList QProcess::environment() const
+{
+ Q_D(const QProcess);
+ return d->environment;
+}
+
+/*!
+ Blocks until the process has started and the started() signal has
+ been emitted, or until \a msecs milliseconds have passed.
+
+ Returns true if the process was started successfully; otherwise
+ returns false (if the operation timed out or if an error
+ occurred).
+
+ This function can operate without an event loop. It is
+ useful when writing non-GUI applications and when performing
+ I/O operations in a non-GUI thread.
+
+ \warning Calling this function from the main (GUI) thread
+ might cause your user interface to freeze.
+
+ If msecs is -1, this function will not time out.
+
+ \sa started(), waitForReadyRead(), waitForBytesWritten(), waitForFinished()
+*/
+bool QProcess::waitForStarted(int msecs)
+{
+ Q_D(QProcess);
+ if (d->processState == QProcess::Starting) {
+ if (!d->waitForStarted(msecs))
+ return false;
+ setProcessState(QProcess::Running);
+ emit started();
+ }
+ return d->processState == QProcess::Running;
+}
+
+/*! \reimp
+*/
+bool QProcess::waitForReadyRead(int msecs)
+{
+ Q_D(QProcess);
+
+ if (d->processState == QProcess::NotRunning)
+ return false;
+ if (d->processChannel == QProcess::StandardOutput && d->stdoutChannel.closed)
+ return false;
+ if (d->processChannel == QProcess::StandardError && d->stderrChannel.closed)
+ return false;
+ return d->waitForReadyRead(msecs);
+}
+
+/*! \reimp
+*/
+bool QProcess::waitForBytesWritten(int msecs)
+{
+ Q_D(QProcess);
+ if (d->processState == QProcess::NotRunning)
+ return false;
+ if (d->processState == QProcess::Starting) {
+ QTime stopWatch;
+ stopWatch.start();
+ bool started = waitForStarted(msecs);
+ if (!started)
+ return false;
+ if (msecs != -1)
+ msecs -= stopWatch.elapsed();
+ }
+
+ return d->waitForBytesWritten(msecs);
+}
+
+/*!
+ Blocks until the process has finished and the finished() signal
+ has been emitted, or until \a msecs milliseconds have passed.
+
+ Returns true if the process finished; otherwise returns false (if
+ the operation timed out or if an error occurred).
+
+ This function can operate without an event loop. It is
+ useful when writing non-GUI applications and when performing
+ I/O operations in a non-GUI thread.
+
+ \warning Calling this function from the main (GUI) thread
+ might cause your user interface to freeze.
+
+ If msecs is -1, this function will not time out.
+
+ \sa finished(), waitForStarted(), waitForReadyRead(), waitForBytesWritten()
+*/
+bool QProcess::waitForFinished(int msecs)
+{
+ Q_D(QProcess);
+ if (d->processState == QProcess::NotRunning)
+ return false;
+ if (d->processState == QProcess::Starting) {
+ QTime stopWatch;
+ stopWatch.start();
+ bool started = waitForStarted(msecs);
+ if (!started)
+ return false;
+ if (msecs != -1)
+ msecs -= stopWatch.elapsed();
+ }
+
+ return d->waitForFinished(msecs);
+}
+
+/*!
+ Sets the current state of the QProcess to the \a state specified.
+
+ \sa state()
+*/
+void QProcess::setProcessState(ProcessState state)
+{
+ Q_D(QProcess);
+ if (d->processState == state)
+ return;
+ d->processState = state;
+ emit stateChanged(state);
+}
+
+/*!
+ This function is called in the child process context just before the
+ program is executed on Unix or Mac OS X (i.e., after \e fork(), but before
+ \e execve()). Reimplement this function to do last minute initialization
+ of the child process. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 4
+
+ You cannot exit the process (by calling exit(), for instance) from
+ this function. If you need to stop the program before it starts
+ execution, your workaround is to emit finished() and then call
+ exit().
+
+ \warning This function is called by QProcess on Unix and Mac OS X
+ only. On Windows, it is not called.
+*/
+void QProcess::setupChildProcess()
+{
+}
+
+/*! \reimp
+*/
+qint64 QProcess::readData(char *data, qint64 maxlen)
+{
+ Q_D(QProcess);
+ QRingBuffer *readBuffer = (d->processChannel == QProcess::StandardError)
+ ? &d->errorReadBuffer
+ : &d->outputReadBuffer;
+
+ if (maxlen == 1 && !readBuffer->isEmpty()) {
+ int c = readBuffer->getChar();
+ if (c == -1) {
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::readData(%p \"%s\", %d) == -1",
+ data, qt_prettyDebug(data, 1, maxlen).constData(), 1);
+#endif
+ return -1;
+ }
+ *data = (char) c;
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::readData(%p \"%s\", %d) == 1",
+ data, qt_prettyDebug(data, 1, maxlen).constData(), 1);
+#endif
+ return 1;
+ }
+
+ qint64 bytesToRead = qint64(qMin(readBuffer->size(), (int)maxlen));
+ qint64 readSoFar = 0;
+ while (readSoFar < bytesToRead) {
+ const char *ptr = readBuffer->readPointer();
+ int bytesToReadFromThisBlock = qMin<qint64>(bytesToRead - readSoFar,
+ readBuffer->nextDataBlockSize());
+ memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
+ readSoFar += bytesToReadFromThisBlock;
+ readBuffer->free(bytesToReadFromThisBlock);
+ }
+
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::readData(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, readSoFar, 16).constData(), maxlen, readSoFar);
+#endif
+ if (!readSoFar && d->processState == QProcess::NotRunning)
+ return -1; // EOF
+ return readSoFar;
+}
+
+/*! \reimp
+*/
+qint64 QProcess::writeData(const char *data, qint64 len)
+{
+ Q_D(QProcess);
+
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ d->processError = QProcess::WriteError;
+ setErrorString(tr("Error writing to process"));
+ emit error(d->processError);
+ return -1;
+#endif
+
+ if (d->stdinChannel.closed) {
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
+ data, qt_prettyDebug(data, len, 16).constData(), len);
+#endif
+ return 0;
+ }
+
+ if (len == 1) {
+ d->writeBuffer.putChar(*data);
+ if (d->stdinChannel.notifier)
+ d->stdinChannel.notifier->setEnabled(true);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::writeData(%p \"%s\", %lld) == 1 (written to buffer)",
+ data, qt_prettyDebug(data, len, 16).constData(), len);
+#endif
+ return 1;
+ }
+
+ char *dest = d->writeBuffer.reserve(len);
+ memcpy(dest, data, len);
+ if (d->stdinChannel.notifier)
+ d->stdinChannel.notifier->setEnabled(true);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
+ data, qt_prettyDebug(data, len, 16).constData(), len, len);
+#endif
+ return len;
+}
+
+/*!
+ Regardless of the current read channel, this function returns all
+ data available from the standard output of the process as a
+ QByteArray.
+
+ \sa readyReadStandardOutput(), readAllStandardError(), readChannel(), setReadChannel()
+*/
+QByteArray QProcess::readAllStandardOutput()
+{
+ ProcessChannel tmp = readChannel();
+ setReadChannel(StandardOutput);
+ QByteArray data = readAll();
+ setReadChannel(tmp);
+ return data;
+}
+
+/*!
+ Regardless of the current read channel, this function returns all
+ data available from the standard error of the process as a
+ QByteArray.
+
+ \sa readyReadStandardError(), readAllStandardOutput(), readChannel(), setReadChannel()
+*/
+QByteArray QProcess::readAllStandardError()
+{
+ ProcessChannel tmp = readChannel();
+ setReadChannel(StandardError);
+ QByteArray data = readAll();
+ setReadChannel(tmp);
+ return data;
+}
+
+/*!
+ Starts the program \a program in a new process, passing the
+ command line arguments in \a arguments. The OpenMode is set to \a
+ mode. QProcess will immediately enter the Starting state. If the
+ process starts successfully, QProcess will emit started();
+ otherwise, error() will be emitted.
+
+ Note that arguments that contain spaces are not passed to the
+ process as separate arguments.
+
+ \bold{Windows:} Arguments that contain spaces are wrapped in quotes.
+
+ \note Processes are started asynchronously, which means the started()
+ and error() signals may be delayed. Call waitForStarted() to make
+ sure the process has started (or has failed to start) and those signals
+ have been emitted.
+
+ \sa pid(), started(), waitForStarted()
+*/
+void QProcess::start(const QString &program, const QStringList &arguments, OpenMode mode)
+{
+ Q_D(QProcess);
+ if (d->processState != NotRunning) {
+ qWarning("QProcess::start: Process is already running");
+ return;
+ }
+
+#if defined QPROCESS_DEBUG
+ qDebug() << "QProcess::start(" << program << "," << arguments << "," << mode << ")";
+#endif
+
+ d->outputReadBuffer.clear();
+ d->errorReadBuffer.clear();
+
+ if (d->stdinChannel.type != QProcessPrivate::Channel::Normal)
+ mode &= ~WriteOnly; // not open for writing
+ if (d->stdoutChannel.type != QProcessPrivate::Channel::Normal &&
+ (d->stderrChannel.type != QProcessPrivate::Channel::Normal ||
+ d->processChannelMode == MergedChannels))
+ mode &= ~ReadOnly; // not open for reading
+ if (mode == 0)
+ mode = Unbuffered;
+ QIODevice::open(mode);
+
+ d->stdinChannel.closed = false;
+ d->stdoutChannel.closed = false;
+ d->stderrChannel.closed = false;
+
+ d->program = program;
+ d->arguments = arguments;
+
+ d->exitCode = 0;
+ d->exitStatus = NormalExit;
+ d->processError = QProcess::UnknownError;
+ d->errorString.clear();
+ d->startProcess();
+}
+
+
+static QStringList parseCombinedArgString(const QString &program)
+{
+ QStringList args;
+ QString tmp;
+ int quoteCount = 0;
+ bool inQuote = false;
+
+ // handle quoting. tokens can be surrounded by double quotes
+ // "hello world". three consecutive double quotes represent
+ // the quote character itself.
+ for (int i = 0; i < program.size(); ++i) {
+ if (program.at(i) == QLatin1Char('"')) {
+ ++quoteCount;
+ if (quoteCount == 3) {
+ // third consecutive quote
+ quoteCount = 0;
+ tmp += program.at(i);
+ }
+ continue;
+ }
+ if (quoteCount) {
+ if (quoteCount == 1)
+ inQuote = !inQuote;
+ quoteCount = 0;
+ }
+ if (!inQuote && program.at(i).isSpace()) {
+ if (!tmp.isEmpty()) {
+ args += tmp;
+ tmp.clear();
+ }
+ } else {
+ tmp += program.at(i);
+ }
+ }
+ if (!tmp.isEmpty())
+ args += tmp;
+
+ return args;
+}
+
+/*!
+ \overload
+
+ Starts the program \a program in a new process. \a program is a
+ single string of text containing both the program name and its
+ arguments. The arguments are separated by one or more
+ spaces. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 5
+
+ The \a program string can also contain quotes, to ensure that arguments
+ containing spaces are correctly supplied to the new process. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 6
+
+ Note that, on Windows, quotes need to be both escaped and quoted.
+ For example, the above code would be specified in the following
+ way to ensure that \c{"My Documents"} is used as the argument to
+ the \c dir executable:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 7
+
+ The OpenMode is set to \a mode.
+*/
+void QProcess::start(const QString &program, OpenMode mode)
+{
+ QStringList args = parseCombinedArgString(program);
+
+ QString prog = args.first();
+ args.removeFirst();
+
+ start(prog, args, mode);
+}
+
+/*!
+ Attempts to terminate the process.
+
+ The process may not exit as a result of calling this function (it is given
+ the chance to prompt the user for any unsaved files, etc).
+
+ On Windows, terminate() posts a WM_CLOSE message to all toplevel windows
+ of the process and then to the main thread of the process itself. On Unix
+ and Mac OS X the SIGTERM signal is sent.
+
+ Console applications on Windows that do not run an event loop, or whose
+ event loop does not handle the WM_CLOSE message, can only be terminated by
+ calling kill().
+
+ \sa kill()
+*/
+void QProcess::terminate()
+{
+ Q_D(QProcess);
+ d->terminateProcess();
+}
+
+/*!
+ Kills the current process, causing it to exit immediately.
+
+ On Windows, kill() uses TerminateProcess, and on Unix and Mac OS X, the
+ SIGKILL signal is sent to the process.
+
+ \sa terminate()
+*/
+void QProcess::kill()
+{
+ Q_D(QProcess);
+ d->killProcess();
+}
+
+/*!
+ Returns the exit code of the last process that finished.
+*/
+int QProcess::exitCode() const
+{
+ Q_D(const QProcess);
+ return d->exitCode;
+}
+
+/*!
+ \since 4.1
+
+ Returns the exit status of the last process that finished.
+
+ On Windows, if the process was terminated with TerminateProcess()
+ from another application this function will still return NormalExit
+ unless the exit code is less than 0.
+*/
+QProcess::ExitStatus QProcess::exitStatus() const
+{
+ Q_D(const QProcess);
+ return d->exitStatus;
+}
+
+/*!
+ Starts the program \a program with the arguments \a arguments in a
+ new process, waits for it to finish, and then returns the exit
+ code of the process. Any data the new process writes to the
+ console is forwarded to the calling process.
+
+ The environment and working directory are inherited by the calling
+ process.
+
+ On Windows, arguments that contain spaces are wrapped in quotes.
+*/
+int QProcess::execute(const QString &program, const QStringList &arguments)
+{
+ QProcess process;
+ process.setReadChannelMode(ForwardedChannels);
+ process.start(program, arguments);
+ process.waitForFinished(-1);
+ return process.exitCode();
+}
+
+/*!
+ \overload
+
+ Starts the program \a program in a new process. \a program is a
+ single string of text containing both the program name and its
+ arguments. The arguments are separated by one or more spaces.
+*/
+int QProcess::execute(const QString &program)
+{
+ QProcess process;
+ process.setReadChannelMode(ForwardedChannels);
+ process.start(program);
+ process.waitForFinished(-1);
+ return process.exitCode();
+}
+
+/*!
+ Starts the program \a program with the arguments \a arguments in a
+ new process, and detaches from it. Returns true on success;
+ otherwise returns false. If the calling process exits, the
+ detached process will continue to live.
+
+ Note that arguments that contain spaces are not passed to the
+ process as separate arguments.
+
+ \bold{Unix:} The started process will run in its own session and act
+ like a daemon.
+
+ \bold{Windows:} Arguments that contain spaces are wrapped in quotes.
+ The started process will run as a regular standalone process.
+
+ The process will be started in the directory \a workingDirectory.
+
+ If the function is successful then *\a pid is set to the process
+ identifier of the started process.
+*/
+bool QProcess::startDetached(const QString &program,
+ const QStringList &arguments,
+ const QString &workingDirectory,
+ qint64 *pid)
+{
+ return QProcessPrivate::startDetached(program,
+ arguments,
+ workingDirectory,
+ pid);
+}
+
+/*!
+ Starts the program \a program with the given \a arguments in a
+ new process, and detaches from it. Returns true on success;
+ otherwise returns false. If the calling process exits, the
+ detached process will continue to live.
+
+ Note that arguments that contain spaces are not passed to the
+ process as separate arguments.
+
+ \bold{Unix:} The started process will run in its own session and act
+ like a daemon.
+
+ \bold{Windows:} Arguments that contain spaces are wrapped in quotes.
+ The started process will run as a regular standalone process.
+*/
+bool QProcess::startDetached(const QString &program,
+ const QStringList &arguments)
+{
+ return QProcessPrivate::startDetached(program, arguments);
+}
+
+/*!
+ \overload
+
+ Starts the program \a program in a new process. \a program is a
+ single string of text containing both the program name and its
+ arguments. The arguments are separated by one or more spaces.
+
+ The \a program string can also contain quotes, to ensure that arguments
+ containing spaces are correctly supplied to the new process.
+*/
+bool QProcess::startDetached(const QString &program)
+{
+ QStringList args = parseCombinedArgString(program);
+
+ QString prog = args.first();
+ args.removeFirst();
+
+ return QProcessPrivate::startDetached(prog, args);
+}
+
+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_WIN)
+ extern char **environ;
+#endif
+QT_END_INCLUDE_NAMESPACE
+
+/*!
+ \since 4.1
+
+ Returns the environment of the calling process as a list of
+ key=value pairs. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 8
+
+ \sa environment(), setEnvironment()
+*/
+QStringList QProcess::systemEnvironment()
+{
+ QStringList tmp;
+ char *entry = 0;
+ int count = 0;
+ while ((entry = environ[count++]))
+ tmp << QString::fromLocal8Bit(entry);
+ return tmp;
+}
+
+/*!
+ \typedef Q_PID
+ \relates QProcess
+
+ Typedef for the identifiers used to represent processes on the underlying
+ platform. On Unix, this corresponds to \l qint64; on Windows, it
+ corresponds to \c{_PROCESS_INFORMATION*}.
+
+ \sa QProcess::pid()
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qprocess.cpp"
+
+#endif // QT_NO_PROCESS
+
diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h
new file mode 100644
index 0000000..54a96c7
--- /dev/null
+++ b/src/corelib/io/qprocess.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROCESS_H
+#define QPROCESS_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_PROCESS
+
+#if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)) || defined(qdoc)
+typedef qint64 Q_PID;
+#else
+QT_END_NAMESPACE
+typedef struct _PROCESS_INFORMATION *Q_PID;
+QT_BEGIN_NAMESPACE
+#endif
+
+class QProcessPrivate;
+
+class Q_CORE_EXPORT QProcess : public QIODevice
+{
+ Q_OBJECT
+public:
+ enum ProcessError {
+ FailedToStart, //### file not found, resource error
+ Crashed,
+ Timedout,
+ ReadError,
+ WriteError,
+ UnknownError
+ };
+ enum ProcessState {
+ NotRunning,
+ Starting,
+ Running
+ };
+ enum ProcessChannel {
+ StandardOutput,
+ StandardError
+ };
+ enum ProcessChannelMode {
+ SeparateChannels,
+ MergedChannels,
+ ForwardedChannels
+ };
+ enum ExitStatus {
+ NormalExit,
+ CrashExit
+ };
+
+ explicit QProcess(QObject *parent = 0);
+ virtual ~QProcess();
+
+ void start(const QString &program, const QStringList &arguments, OpenMode mode = ReadWrite);
+ void start(const QString &program, OpenMode mode = ReadWrite);
+
+ ProcessChannelMode readChannelMode() const;
+ void setReadChannelMode(ProcessChannelMode mode);
+ ProcessChannelMode processChannelMode() const;
+ void setProcessChannelMode(ProcessChannelMode mode);
+
+ ProcessChannel readChannel() const;
+ void setReadChannel(ProcessChannel channel);
+
+ void closeReadChannel(ProcessChannel channel);
+ void closeWriteChannel();
+
+ void setStandardInputFile(const QString &fileName);
+ void setStandardOutputFile(const QString &fileName, OpenMode mode = Truncate);
+ void setStandardErrorFile(const QString &fileName, OpenMode mode = Truncate);
+ void setStandardOutputProcess(QProcess *destination);
+
+ QString workingDirectory() const;
+ void setWorkingDirectory(const QString &dir);
+
+ void setEnvironment(const QStringList &environment);
+ QStringList environment() const;
+
+ QProcess::ProcessError error() const;
+ QProcess::ProcessState state() const;
+
+ // #### Qt 5: Q_PID is a pointer on Windows and a value on Unix
+ Q_PID pid() const;
+
+ bool waitForStarted(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000);
+ bool waitForBytesWritten(int msecs = 30000);
+ bool waitForFinished(int msecs = 30000);
+
+ QByteArray readAllStandardOutput();
+ QByteArray readAllStandardError();
+
+ int exitCode() const;
+ QProcess::ExitStatus exitStatus() const;
+
+ // QIODevice
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+ bool isSequential() const;
+ bool canReadLine() const;
+ void close();
+ bool atEnd() const;
+
+ static int execute(const QString &program, const QStringList &arguments);
+ static int execute(const QString &program);
+
+ static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory,
+ qint64 *pid = 0);
+ static bool startDetached(const QString &program, const QStringList &arguments);
+ static bool startDetached(const QString &program);
+
+ static QStringList systemEnvironment();
+
+public Q_SLOTS:
+ void terminate();
+ void kill();
+
+Q_SIGNALS:
+ void started();
+ void finished(int exitCode);
+ void finished(int exitCode, QProcess::ExitStatus exitStatus);
+ void error(QProcess::ProcessError error);
+ void stateChanged(QProcess::ProcessState state);
+
+ void readyReadStandardOutput();
+ void readyReadStandardError();
+
+protected:
+ void setProcessState(ProcessState state);
+
+ virtual void setupChildProcess();
+
+ // QIODevice
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private:
+ Q_DECLARE_PRIVATE(QProcess)
+ Q_DISABLE_COPY(QProcess)
+
+ Q_PRIVATE_SLOT(d_func(), bool _q_canReadStandardOutput())
+ Q_PRIVATE_SLOT(d_func(), bool _q_canReadStandardError())
+ Q_PRIVATE_SLOT(d_func(), bool _q_canWrite())
+ Q_PRIVATE_SLOT(d_func(), bool _q_startupNotification())
+ Q_PRIVATE_SLOT(d_func(), bool _q_processDied())
+ Q_PRIVATE_SLOT(d_func(), void _q_notified())
+ friend class QProcessManager;
+};
+
+#endif // QT_NO_PROCESS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPROCESS_H
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
new file mode 100644
index 0000000..33059dd
--- /dev/null
+++ b/src/corelib/io/qprocess_p.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPROCESS_P_H
+#define QPROCESS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qprocess.h"
+#include "QtCore/qstringlist.h"
+#include "private/qringbuffer_p.h"
+#include "private/qiodevice_p.h"
+
+#ifdef Q_OS_WIN
+#include "QtCore/qt_windows.h"
+typedef HANDLE Q_PIPE;
+#define INVALID_Q_PIPE INVALID_HANDLE_VALUE
+#else
+typedef int Q_PIPE;
+#define INVALID_Q_PIPE -1
+#endif
+
+#ifndef QT_NO_PROCESS
+
+QT_BEGIN_NAMESPACE
+
+class QSocketNotifier;
+class QWindowsPipeWriter;
+class QWinEventNotifier;
+class QTimer;
+
+class QProcessPrivate : public QIODevicePrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QProcess)
+
+ struct Channel {
+ enum ProcessChannelType {
+ Normal = 0,
+ PipeSource = 1,
+ PipeSink = 2,
+ Redirect = 3
+ // if you add "= 4" here, increase the number of bits below
+ };
+
+ Channel() : process(0), notifier(0), type(Normal), closed(false), append(false)
+ {
+ pipe[0] = INVALID_Q_PIPE;
+ pipe[1] = INVALID_Q_PIPE;
+ }
+
+ void clear();
+
+ Channel &operator=(const QString &fileName)
+ {
+ clear();
+ file = fileName;
+ type = fileName.isEmpty() ? Normal : Redirect;
+ return *this;
+ }
+
+ void pipeTo(QProcessPrivate *other)
+ {
+ clear();
+ process = other;
+ type = PipeSource;
+ }
+
+ void pipeFrom(QProcessPrivate *other)
+ {
+ clear();
+ process = other;
+ type = PipeSink;
+ }
+
+ QString file;
+ QProcessPrivate *process;
+ QSocketNotifier *notifier;
+ Q_PIPE pipe[2];
+
+ unsigned type : 2;
+ bool closed : 1;
+ bool append : 1;
+ };
+
+ QProcessPrivate();
+ virtual ~QProcessPrivate();
+
+ // private slots
+ bool _q_canReadStandardOutput();
+ bool _q_canReadStandardError();
+ bool _q_canWrite();
+ bool _q_startupNotification();
+ bool _q_processDied();
+ void _q_notified();
+
+ QProcess::ProcessChannel processChannel;
+ QProcess::ProcessChannelMode processChannelMode;
+ QProcess::ProcessError processError;
+ QProcess::ProcessState processState;
+ QString workingDirectory;
+ Q_PID pid;
+ int sequenceNumber;
+
+ bool dying;
+ bool emittedReadyRead;
+ bool emittedBytesWritten;
+
+ Channel stdinChannel;
+ Channel stdoutChannel;
+ Channel stderrChannel;
+ bool createChannel(Channel &channel);
+ void closeWriteChannel();
+
+ QString program;
+ QStringList arguments;
+ QStringList environment;
+
+ QRingBuffer outputReadBuffer;
+ QRingBuffer errorReadBuffer;
+ QRingBuffer writeBuffer;
+
+ Q_PIPE childStartedPipe[2];
+ Q_PIPE deathPipe[2];
+ void destroyPipe(Q_PIPE pipe[2]);
+
+ QSocketNotifier *startupSocketNotifier;
+ QSocketNotifier *deathNotifier;
+
+ // the wonderful windows notifier
+ QTimer *notifier;
+ QWindowsPipeWriter *pipeWriter;
+ QWinEventNotifier *processFinishedNotifier;
+
+ void startProcess();
+#ifdef Q_OS_UNIX
+ void execChild(const char *workingDirectory, char **path, char **argv, char **envp);
+#endif
+ bool processStarted();
+ void terminateProcess();
+ void killProcess();
+ void findExitCode();
+#ifdef Q_OS_UNIX
+ bool waitForDeadChild();
+#endif
+#ifdef Q_OS_WIN
+ void flushPipeWriter();
+ qint64 pipeWriterBytesToWrite() const;
+#endif
+
+ static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(),
+ qint64 *pid = 0);
+
+ int exitCode;
+ QProcess::ExitStatus exitStatus;
+ bool crashed;
+#ifdef Q_OS_UNIX
+ int serial;
+#endif
+
+ bool waitForStarted(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000);
+ bool waitForBytesWritten(int msecs = 30000);
+ bool waitForFinished(int msecs = 30000);
+ bool waitForWrite(int msecs = 30000);
+
+ qint64 bytesAvailableFromStdout() const;
+ qint64 bytesAvailableFromStderr() const;
+ qint64 readFromStdout(char *data, qint64 maxlen);
+ qint64 readFromStderr(char *data, qint64 maxlen);
+ qint64 writeToStdin(const char *data, qint64 maxlen);
+
+ void cleanup();
+#ifdef Q_OS_UNIX
+ static void initializeProcessManager();
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS
+
+#endif // QPROCESS_P_H
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
new file mode 100644
index 0000000..37173c8
--- /dev/null
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -0,0 +1,1380 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QPROCESS_DEBUG
+#include "qdebug.h"
+
+#ifndef QT_NO_PROCESS
+
+#if defined QPROCESS_DEBUG
+#include "qstring.h"
+#include <ctype.h>
+
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+QT_BEGIN_NAMESPACE
+static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(c)) {
+ out += c;
+ } else switch (c) {
+ case '\n': out += "\\n"; break;
+ case '\r': out += "\\r"; break;
+ case '\t': out += "\\t"; break;
+ default:
+ QString tmp;
+ tmp.sprintf("\\%o", c);
+ out += tmp.toLatin1();
+ }
+ }
+
+ if (len < maxSize)
+ out += "...";
+
+ return out;
+}
+QT_END_NAMESPACE
+#endif
+
+#include "qplatformdefs.h"
+
+#include "qprocess.h"
+#include "qprocess_p.h"
+
+#ifdef Q_OS_MAC
+#include <private/qcore_mac_p.h>
+#endif
+
+#include <private/qcoreapplication_p.h>
+#include <private/qthread_p.h>
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qlist.h>
+#include <qmap.h>
+#include <qmutex.h>
+#include <qsemaphore.h>
+#include <qsocketnotifier.h>
+#include <qthread.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_INTEGRITY
+static inline char *strdup(const char *data)
+{
+ return qstrdup(data);
+}
+#endif
+
+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);
+ 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);
+ return ret;
+}
+
+static void qt_native_close(int fd)
+{
+ int ret;
+ do {
+ ret = ::close(fd);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ int ret;
+ do {
+ ret = ::sigaction(signum, act, oldact);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_dup2(int oldfd, int newfd)
+{
+ int ret;
+ do {
+ ret = ::dup2(oldfd, newfd);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_chdir(const char *path)
+{
+ int ret;
+ do {
+ ret = ::chdir(path);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execve(const char *filename, char *const argv[],
+ char *const envp[])
+{
+ int ret;
+ do {
+ ret = ::execve(filename, argv, envp);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execv(const char *path, char *const argv[])
+{
+ int ret;
+ do {
+ ret = ::execv(path, argv);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static void qt_native_execvp(const char *file, char *const argv[])
+{
+ int ret;
+ do {
+ ret = ::execvp(file, argv);
+ } while (ret == -1 && errno == EINTR);
+}
+
+static int qt_qprocess_deadChild_pipe[2];
+static void (*qt_sa_old_sigchld_handler)(int) = 0;
+static void qt_sa_sigchld_handler(int signum)
+{
+ qt_native_write(qt_qprocess_deadChild_pipe[1], "", 1);
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "*** SIGCHLD\n");
+#endif
+
+ if (qt_sa_old_sigchld_handler && qt_sa_old_sigchld_handler != SIG_IGN)
+ qt_sa_old_sigchld_handler(signum);
+}
+
+struct QProcessInfo {
+ QProcess *process;
+ int deathPipe;
+ int exitResult;
+ pid_t pid;
+ int serialNumber;
+};
+
+class QProcessManager : public QThread
+{
+ Q_OBJECT
+public:
+ QProcessManager();
+ ~QProcessManager();
+
+ void run();
+ void catchDeadChildren();
+ void add(pid_t pid, QProcess *process);
+ void remove(QProcess *process);
+ void lock();
+ void unlock();
+
+private:
+ QMutex mutex;
+ QMap<int, QProcessInfo *> children;
+};
+
+Q_GLOBAL_STATIC(QProcessManager, processManager)
+
+QProcessManager::QProcessManager()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::QProcessManager()";
+#endif
+ // initialize the dead child pipe and make it non-blocking. in the
+ // extremely unlikely event that the pipe fills up, we do not under any
+ // circumstances want to block.
+ ::pipe(qt_qprocess_deadChild_pipe);
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFD, FD_CLOEXEC);
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFL,
+ ::fcntl(qt_qprocess_deadChild_pipe[0], F_GETFL) | O_NONBLOCK);
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFL,
+ ::fcntl(qt_qprocess_deadChild_pipe[1], F_GETFL) | O_NONBLOCK);
+
+ // set up the SIGCHLD handler, which writes a single byte to the dead
+ // child pipe every time a child dies.
+ struct sigaction oldAction;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = qt_sa_sigchld_handler;
+ action.sa_flags = SA_NOCLDSTOP;
+ qt_native_sigaction(SIGCHLD, &action, &oldAction);
+ if (oldAction.sa_handler != qt_sa_sigchld_handler)
+ qt_sa_old_sigchld_handler = oldAction.sa_handler;
+}
+
+QProcessManager::~QProcessManager()
+{
+ // notify the thread that we're shutting down.
+ qt_native_write(qt_qprocess_deadChild_pipe[1], "@", 1);
+ qt_native_close(qt_qprocess_deadChild_pipe[1]);
+ wait();
+
+ // on certain unixes, closing the reading end of the pipe will cause
+ // select in run() to block forever, rather than return with EBADF.
+ qt_native_close(qt_qprocess_deadChild_pipe[0]);
+
+ qt_qprocess_deadChild_pipe[0] = -1;
+ qt_qprocess_deadChild_pipe[1] = -1;
+
+ qDeleteAll(children.values());
+ children.clear();
+
+ struct sigaction oldAction;
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = qt_sa_old_sigchld_handler;
+ action.sa_flags = SA_NOCLDSTOP;
+ qt_native_sigaction(SIGCHLD, &action, &oldAction);
+ if (oldAction.sa_handler != qt_sa_sigchld_handler) {
+ qt_native_sigaction(SIGCHLD, &oldAction, 0);
+ }
+}
+
+void QProcessManager::run()
+{
+ forever {
+ fd_set readset;
+ FD_ZERO(&readset);
+ FD_SET(qt_qprocess_deadChild_pipe[0], &readset);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::run() waiting for children to die";
+#endif
+
+ // block forever, or until activity is detected on the dead child
+ // pipe. the only other peers are the SIGCHLD signal handler, and the
+ // QProcessManager destructor.
+ int nselect = select(qt_qprocess_deadChild_pipe[0] + 1, &readset, 0, 0, 0);
+ if (nselect < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ // empty only one byte from the pipe, even though several SIGCHLD
+ // signals may have been delivered in the meantime, to avoid race
+ // conditions.
+ char c;
+ if (qt_native_read(qt_qprocess_deadChild_pipe[0], &c, 1) < 0 || c == '@')
+ break;
+
+ // catch any and all children that we can.
+ catchDeadChildren();
+ }
+}
+
+void QProcessManager::catchDeadChildren()
+{
+ QMutexLocker locker(&mutex);
+
+ // try to catch all children whose pid we have registered, and whose
+ // deathPipe is still valid (i.e, we have not already notified it).
+ QMap<int, QProcessInfo *>::Iterator it = children.begin();
+ while (it != children.end()) {
+ // notify all children that they may have died. they need to run
+ // waitpid() in their own thread.
+ QProcessInfo *info = it.value();
+ qt_native_write(info->deathPipe, "", 1);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::run() sending death notice to" << info->process;
+#endif
+ ++it;
+ }
+}
+
+static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+void QProcessManager::add(pid_t pid, QProcess *process)
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::add() adding pid" << pid << "process" << process;
+#endif
+
+ // insert a new info structure for this process
+ QProcessInfo *info = new QProcessInfo;
+ info->process = process;
+ info->deathPipe = process->d_func()->deathPipe[1];
+ info->exitResult = 0;
+ info->pid = pid;
+
+ int serial = idCounter.fetchAndAddRelaxed(1);
+ process->d_func()->serial = serial;
+ children.insert(serial, info);
+}
+
+void QProcessManager::remove(QProcess *process)
+{
+ QMutexLocker locker(&mutex);
+
+ int serial = process->d_func()->serial;
+ QProcessInfo *info = children.value(serial);
+ if (!info)
+ return;
+
+#if defined (QPROCESS_DEBUG)
+ qDebug() << "QProcessManager::remove() removing pid" << info->pid << "process" << info->process;
+#endif
+
+ children.remove(serial);
+ delete info;
+}
+
+void QProcessManager::lock()
+{
+ mutex.lock();
+}
+
+void QProcessManager::unlock()
+{
+ mutex.unlock();
+}
+
+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]);
+#ifdef Q_OS_IRIX
+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) == -1) {
+ qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s",
+ pipe, qPrintable(qt_error_string(errno)));
+ }
+#else
+ if (::pipe(pipe) != 0) {
+ qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s",
+ pipe, qPrintable(qt_error_string(errno)));
+ }
+#endif
+ ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC);
+}
+
+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;
+ }
+}
+
+/*
+ Create the pipes to a QProcessPrivate::Channel.
+
+ This function must be called in order: stdin, stdout, stderr
+*/
+bool QProcessPrivate::createChannel(Channel &channel)
+{
+ Q_Q(QProcess);
+
+ if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
+ channel.pipe[0] = -1;
+ channel.pipe[1] = -1;
+ return true;
+ }
+
+ if (channel.type == Channel::Normal) {
+ // we're piping this channel to our own process
+ qt_create_pipe(channel.pipe);
+
+ // create the socket notifiers
+ if (threadData->eventDispatcher) {
+ if (&channel == &stdinChannel) {
+ channel.notifier = new QSocketNotifier(channel.pipe[1],
+ QSocketNotifier::Write, q);
+ channel.notifier->setEnabled(false);
+ QObject::connect(channel.notifier, SIGNAL(activated(int)),
+ q, SLOT(_q_canWrite()));
+ } else {
+ channel.notifier = new QSocketNotifier(channel.pipe[0],
+ QSocketNotifier::Read, q);
+ const char *receiver;
+ if (&channel == &stdoutChannel)
+ receiver = SLOT(_q_canReadStandardOutput());
+ else
+ receiver = SLOT(_q_canReadStandardError());
+ QObject::connect(channel.notifier, SIGNAL(activated(int)),
+ q, receiver);
+ }
+ }
+
+ return true;
+ } else if (channel.type == Channel::Redirect) {
+ // we're redirecting the channel to/from a file
+ QByteArray fname = QFile::encodeName(channel.file);
+
+ if (&channel == &stdinChannel) {
+ // try to open in read-only mode
+ channel.pipe[1] = -1;
+ if ( (channel.pipe[0] = QT_OPEN(fname, O_RDONLY)) != -1)
+ return true; // success
+
+ q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
+ } else {
+ int mode = O_WRONLY | O_CREAT;
+ if (channel.append)
+ mode |= O_APPEND;
+ else
+ mode |= O_TRUNC;
+
+ channel.pipe[0] = -1;
+ if ( (channel.pipe[1] = QT_OPEN(fname, mode, 0666)) != -1)
+ return true; // success
+
+ q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
+ }
+
+ // could not open file
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+ cleanup();
+ return false;
+ } else {
+ Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
+
+ Channel *source;
+ Channel *sink;
+
+ if (channel.type == Channel::PipeSource) {
+ // we are the source
+ source = &channel;
+ sink = &channel.process->stdinChannel;
+
+ Q_ASSERT(source == &stdoutChannel);
+ Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
+ } else {
+ // we are the sink;
+ source = &channel.process->stdoutChannel;
+ sink = &channel;
+
+ Q_ASSERT(sink == &stdinChannel);
+ Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
+ }
+
+ if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) {
+ // already created, do nothing
+ return true;
+ } else {
+ Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE);
+ Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE);
+
+ Q_PIPE pipe[2] = { -1, -1 };
+ qt_create_pipe(pipe);
+ sink->pipe[0] = pipe[0];
+ source->pipe[1] = pipe[1];
+
+ return true;
+ }
+ }
+}
+
+static char **_q_dupEnvironment(const QStringList &environment, int *envc)
+{
+ // if LD_LIBRARY_PATH exists in the current environment, but
+ // not in the environment list passed by the programmer, then
+ // copy it over.
+#if defined(Q_OS_MAC)
+ static const char libraryPath[] = "DYLD_LIBRARY_PATH";
+#else
+ static const char libraryPath[] = "LD_LIBRARY_PATH";
+#endif
+ const QString libraryPathString = QLatin1String(libraryPath);
+ QStringList env = environment;
+ QStringList matches = env.filter(
+ QRegExp(QLatin1Char('^') + libraryPathString + QLatin1Char('=')));
+ const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath));
+ if (matches.isEmpty() && !envLibraryPath.isEmpty()) {
+ QString entry = libraryPathString;
+ entry += QLatin1Char('=');
+ entry += envLibraryPath;
+ env << libraryPathString + QLatin1Char('=') + envLibraryPath;
+ }
+
+ char **envp = new char *[env.count() + 1];
+ envp[env.count()] = 0;
+
+ for (int j = 0; j < env.count(); ++j) {
+ QString item = env.at(j);
+ envp[j] = ::strdup(item.toLocal8Bit().constData());
+ }
+
+ *envc = env.count();
+ return envp;
+}
+
+// under QNX RTOS we have to use vfork() when multithreading
+inline pid_t qt_fork()
+{
+#if defined(Q_OS_QNX)
+ return vfork();
+#else
+ return fork();
+#endif
+}
+
+#ifdef Q_OS_MAC
+Q_GLOBAL_STATIC(QMutex, cfbundleMutex);
+#endif
+
+void QProcessPrivate::startProcess()
+{
+ Q_Q(QProcess);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::startProcess()");
+#endif
+
+ processManager()->start();
+
+ // Initialize pipes
+ qt_create_pipe(childStartedPipe);
+ if (threadData->eventDispatcher) {
+ startupSocketNotifier = new QSocketNotifier(childStartedPipe[0],
+ QSocketNotifier::Read, q);
+ QObject::connect(startupSocketNotifier, SIGNAL(activated(int)),
+ q, SLOT(_q_startupNotification()));
+ }
+
+ qt_create_pipe(deathPipe);
+ ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC);
+ ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC);
+ if (threadData->eventDispatcher) {
+ deathNotifier = new QSocketNotifier(deathPipe[0],
+ QSocketNotifier::Read, q);
+ QObject::connect(deathNotifier, SIGNAL(activated(int)),
+ q, SLOT(_q_processDied()));
+ }
+
+ if (!createChannel(stdinChannel) ||
+ !createChannel(stdoutChannel) ||
+ !createChannel(stderrChannel))
+ return;
+
+ // Start the process (platform dependent)
+ q->setProcessState(QProcess::Starting);
+
+ // Create argument list with right number of elements, and set the final
+ // one to 0.
+ char **argv = new char *[arguments.count() + 2];
+ argv[arguments.count() + 1] = 0;
+
+ // Encode the program name.
+ QByteArray encodedProgramName = QFile::encodeName(program);
+#ifdef Q_OS_MAC
+ // allow invoking of .app bundles on the Mac.
+ QFileInfo fileInfo(QString::fromUtf8(encodedProgramName.constData()));
+ if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) {
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
+ QCFString(fileInfo.absoluteFilePath()),
+ kCFURLPOSIXPathStyle, true);
+ {
+ // CFBundle is not reentrant, since CFBundleCreate might return a reference
+ // to a cached bundle object. Protect the bundle calls with a mutex lock.
+ QMutexLocker lock(cfbundleMutex());
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
+ url = CFBundleCopyExecutableURL(bundle);
+ }
+ if (url) {
+ QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ encodedProgramName += "/Contents/MacOS/" + static_cast<QString>(str).toUtf8();
+ }
+ }
+#endif
+
+ // Add the program name to the argument list.
+ char *dupProgramName = ::strdup(encodedProgramName.constData());
+ argv[0] = dupProgramName;
+
+ // Add every argument to the list
+ for (int i = 0; i < arguments.count(); ++i) {
+ QString arg = arguments.at(i);
+#ifdef Q_OS_MAC
+ // Mac OS X uses UTF8 for exec, regardless of the system locale.
+ argv[i + 1] = ::strdup(arg.toUtf8().constData());
+#else
+ argv[i + 1] = ::strdup(arg.toLocal8Bit().constData());
+#endif
+ }
+
+ // Duplicate the environment.
+ int envc = 0;
+ char **envp = _q_dupEnvironment(environment, &envc);
+
+ // Encode the working directory if it's non-empty, otherwise just pass 0.
+ const char *workingDirPtr = 0;
+ QByteArray encodedWorkingDirectory;
+ if (!workingDirectory.isEmpty()) {
+ encodedWorkingDirectory = QFile::encodeName(workingDirectory);
+ workingDirPtr = encodedWorkingDirectory.constData();
+ }
+
+ // If the program does not specify a path, generate a list of possible
+ // locations for the binary using the PATH environment variable.
+ char **path = 0;
+ int pathc = 0;
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString pathEnv = QString::fromLocal8Bit(::getenv("PATH"));
+ if (!pathEnv.isEmpty()) {
+ QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts);
+ if (!pathEntries.isEmpty()) {
+ pathc = pathEntries.size();
+ path = new char *[pathc + 1];
+ path[pathc] = 0;
+
+ for (int k = 0; k < pathEntries.size(); ++k) {
+ QByteArray tmp = QFile::encodeName(pathEntries.at(k));
+ if (!tmp.endsWith('/')) tmp += '/';
+ tmp += encodedProgramName;
+ path[k] = ::strdup(tmp.constData());
+ }
+ }
+ }
+ }
+
+ // Start the process manager, and fork off the child process.
+ processManager()->lock();
+ pid_t childPid = qt_fork();
+ int lastForkErrno = errno;
+ if (childPid != 0) {
+ // Clean up duplicated memory.
+ free(dupProgramName);
+ for (int i = 1; i <= arguments.count(); ++i)
+ free(argv[i]);
+ for (int i = 0; i < envc; ++i)
+ free(envp[i]);
+ for (int i = 0; i < pathc; ++i)
+ free(path[i]);
+ delete [] argv;
+ delete [] envp;
+ delete [] path;
+ }
+ if (childPid < 0) {
+ // Cleanup, report error and return
+#if defined (QPROCESS_DEBUG)
+ qDebug("qt_fork failed: %s", qt_error_string(lastForkErrno));
+#endif
+ processManager()->unlock();
+ q->setProcessState(QProcess::NotRunning);
+ processError = QProcess::FailedToStart;
+ q->setErrorString(QProcess::tr("Resource error (fork failure): %1").arg(qt_error_string(lastForkErrno)));
+ emit q->error(processError);
+ cleanup();
+ return;
+ }
+
+ // Start the child.
+ if (childPid == 0) {
+ execChild(workingDirPtr, path, argv, envp);
+ ::_exit(-1);
+ }
+
+ // Register the child. In the mean time, we can get a SIGCHLD, so we need
+ // to keep the lock held to avoid a race to catch the child.
+ processManager()->add(childPid, q);
+ pid = Q_PID(childPid);
+ processManager()->unlock();
+
+ // parent
+ // close the ends we don't use and make all pipes non-blocking
+ ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK);
+ qt_native_close(childStartedPipe[1]);
+ childStartedPipe[1] = -1;
+
+ if (stdinChannel.pipe[0] != -1) {
+ qt_native_close(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = -1;
+ }
+
+ if (stdinChannel.pipe[1] != -1)
+ ::fcntl(stdinChannel.pipe[1], F_SETFL, ::fcntl(stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK);
+
+ if (stdoutChannel.pipe[1] != -1) {
+ qt_native_close(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = -1;
+ }
+
+ if (stdoutChannel.pipe[0] != -1)
+ ::fcntl(stdoutChannel.pipe[0], F_SETFL, ::fcntl(stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK);
+
+ if (stderrChannel.pipe[1] != -1) {
+ qt_native_close(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = -1;
+ }
+ if (stderrChannel.pipe[0] != -1)
+ ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
+}
+
+void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp)
+{
+ ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
+
+ Q_Q(QProcess);
+
+ // copy the stdin socket
+ qt_native_dup2(stdinChannel.pipe[0], fileno(stdin));
+
+ // copy the stdout and stderr if asked to
+ if (processChannelMode != QProcess::ForwardedChannels) {
+ qt_native_dup2(stdoutChannel.pipe[1], fileno(stdout));
+
+ // merge stdout and stderr if asked to
+ if (processChannelMode == QProcess::MergedChannels) {
+ qt_native_dup2(fileno(stdout), fileno(stderr));
+ } else {
+ qt_native_dup2(stderrChannel.pipe[1], fileno(stderr));
+ }
+ }
+
+ // make sure this fd is closed if execvp() succeeds
+ qt_native_close(childStartedPipe[0]);
+ ::fcntl(childStartedPipe[1], F_SETFD, FD_CLOEXEC);
+
+ // enter the working directory
+ if (workingDir)
+ qt_native_chdir(workingDir);
+
+ // this is a virtual call, and it base behavior is to do nothing.
+ q->setupChildProcess();
+
+ // execute the process
+ if (environment.isEmpty()) {
+ qt_native_execvp(argv[0], argv);
+ } else {
+ if (path) {
+ char **arg = path;
+ while (*arg) {
+ argv[0] = *arg;
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() searching / starting %s\n", argv[0]);
+#endif
+ qt_native_execve(argv[0], argv, envp);
+ ++arg;
+ }
+ } else {
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);
+#endif
+ qt_native_execve(argv[0], argv, envp);
+ }
+ }
+
+ // notify failure
+#if defined (QPROCESS_DEBUG)
+ fprintf(stderr, "QProcessPrivate::execChild() failed, notifying parent process\n");
+#endif
+ qt_native_write(childStartedPipe[1], "", 1);
+ qt_native_close(childStartedPipe[1]);
+ childStartedPipe[1] = -1;
+}
+
+bool QProcessPrivate::processStarted()
+{
+ char c;
+ int i = qt_native_read(childStartedPipe[0], &c, 1);
+ if (startupSocketNotifier) {
+ startupSocketNotifier->setEnabled(false);
+ startupSocketNotifier->deleteLater();
+ startupSocketNotifier = 0;
+ }
+ qt_native_close(childStartedPipe[0]);
+ childStartedPipe[0] = -1;
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::processStarted() == %s", i <= 0 ? "true" : "false");
+#endif
+ return i <= 0;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStdout() const
+{
+ size_t nbytes = 0;
+ qint64 available = 0;
+ if (::ioctl(stdoutChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0)
+ available = (qint64) *((int *) &nbytes);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::bytesAvailableFromStdout() == %lld", available);
+#endif
+ return available;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStderr() const
+{
+ size_t nbytes = 0;
+ qint64 available = 0;
+ if (::ioctl(stderrChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0)
+ available = (qint64) *((int *) &nbytes);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::bytesAvailableFromStderr() == %lld", available);
+#endif
+ return available;
+}
+
+qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
+{
+ qint64 bytesRead = qt_native_read(stdoutChannel.pipe[0], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::readFromStdout(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);
+#endif
+ return bytesRead;
+}
+
+qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
+{
+ qint64 bytesRead = qt_native_read(stderrChannel.pipe[0], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::readFromStderr(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);
+#endif
+ return bytesRead;
+}
+
+static void qt_ignore_sigpipe()
+{
+ // Set to ignore SIGPIPE once only.
+ static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (atom.testAndSetRelaxed(0, 1)) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+ }
+}
+
+qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
+{
+ qt_ignore_sigpipe();
+
+ qint64 written = qt_native_write(stdinChannel.pipe[1], data, maxlen);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld",
+ data, qt_prettyDebug(data, maxlen, 16).constData(), maxlen, written);
+#endif
+ return written;
+}
+
+void QProcessPrivate::terminateProcess()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::killProcess()");
+#endif
+ if (pid)
+ ::kill(pid_t(pid), SIGTERM);
+}
+
+void QProcessPrivate::killProcess()
+{
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::killProcess()");
+#endif
+ if (pid)
+ ::kill(pid_t(pid), SIGKILL);
+}
+
+static int qt_native_select(fd_set *fdread, fd_set *fdwrite, int timeout)
+{
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ int ret;
+ do {
+ ret = select(FD_SETSIZE, fdread, fdwrite, 0, timeout < 0 ? 0 : &tv);
+ } while (ret < 0 && (errno == EINTR));
+ return ret;
+}
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+bool QProcessPrivate::waitForStarted(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted(%d) waiting for child to start (fd = %d)", msecs,
+ childStartedPipe[0]);
+#endif
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(childStartedPipe[0], &fds);
+ int ret;
+ do {
+ ret = qt_native_select(&fds, 0, msecs);
+ } while (ret < 0 && errno == EINTR);
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted(%d) == false (timed out)", msecs);
+#endif
+ return false;
+ }
+
+ bool startedEmitted = _q_startupNotification();
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForStarted() == %s", startedEmitted ? "true" : "false");
+#endif
+ return startedEmitted;
+}
+
+bool QProcessPrivate::waitForReadyRead(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForReadyRead(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ forever {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+
+ bool readyReadEmitted = false;
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) {
+ bool canRead = _q_canReadStandardOutput();
+ if (processChannel == QProcess::StandardOutput && canRead)
+ readyReadEmitted = true;
+ }
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) {
+ bool canRead = _q_canReadStandardError();
+ if (processChannel == QProcess::StandardError && canRead)
+ readyReadEmitted = true;
+ }
+ if (readyReadEmitted)
+ return true;
+
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ _q_canWrite();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return false;
+ }
+ }
+ return false;
+}
+
+bool QProcessPrivate::waitForBytesWritten(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForBytesWritten(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ while (!writeBuffer.isEmpty()) {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ return _q_canWrite();
+
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread))
+ _q_canReadStandardOutput();
+
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread))
+ _q_canReadStandardError();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool QProcessPrivate::waitForFinished(int msecs)
+{
+ Q_Q(QProcess);
+#if defined (QPROCESS_DEBUG)
+ qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
+#endif
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ forever {
+ fd_set fdread;
+ fd_set fdwrite;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+
+ if (processState == QProcess::Starting)
+ FD_SET(childStartedPipe[0], &fdread);
+
+ if (stdoutChannel.pipe[0] != -1)
+ FD_SET(stdoutChannel.pipe[0], &fdread);
+ if (stderrChannel.pipe[0] != -1)
+ FD_SET(stderrChannel.pipe[0], &fdread);
+
+ if (processState == QProcess::Running)
+ FD_SET(deathPipe[0], &fdread);
+
+ if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1)
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ int ret = qt_native_select(&fdread, &fdwrite, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (ret == 0) {
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+ }
+
+ if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) {
+ if (!_q_startupNotification())
+ return false;
+ }
+ if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite))
+ _q_canWrite();
+
+ if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread))
+ _q_canReadStandardOutput();
+
+ if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread))
+ _q_canReadStandardError();
+
+ if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) {
+ if (_q_processDied())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QProcessPrivate::waitForWrite(int msecs)
+{
+ fd_set fdwrite;
+ FD_ZERO(&fdwrite);
+ FD_SET(stdinChannel.pipe[1], &fdwrite);
+
+ int ret;
+ do {
+ ret = qt_native_select(0, &fdwrite, msecs < 0 ? 0 : msecs) == 1;
+ } while (ret < 0 && errno == EINTR);
+ return ret == 1;
+}
+
+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);
+
+ // check if our process is dead
+ int exitStatus;
+ pid_t waitResult = 0;
+ do {
+ waitResult = waitpid(pid_t(pid), &exitStatus, WNOHANG);
+ } while ((waitResult == -1 && errno == EINTR));
+ if (waitResult > 0) {
+ processManager()->remove(q);
+ crashed = !WIFEXITED(exitStatus);
+ exitCode = WEXITSTATUS(exitStatus);
+#if defined QPROCESS_DEBUG
+ qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode"
+ << exitCode << ", crashed?" << crashed;
+#endif
+ return true;
+ }
+#if defined QPROCESS_DEBUG
+ qDebug() << "QProcessPrivate::waitForDeadChild() not dead!";
+#endif
+ return false;
+}
+
+void QProcessPrivate::_q_notified()
+{
+}
+
+/*! \internal
+ */
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+ processManager()->start();
+
+ QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
+
+ // To catch the startup of the child
+ int startedPipe[2];
+ ::pipe(startedPipe);
+ // To communicate the pid of the child
+ int pidPipe[2];
+ ::pipe(pidPipe);
+
+ pid_t childPid = qt_fork();
+ if (childPid == 0) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ ::setsid();
+
+ qt_native_close(startedPipe[0]);
+ qt_native_close(pidPipe[0]);
+
+ pid_t doubleForkPid = qt_fork();
+ if (doubleForkPid == 0) {
+ ::fcntl(startedPipe[1], F_SETFD, FD_CLOEXEC);
+ qt_native_close(pidPipe[1]);
+
+ if (!encodedWorkingDirectory.isEmpty())
+ qt_native_chdir(encodedWorkingDirectory.constData());
+
+ char **argv = new char *[arguments.size() + 2];
+ for (int i = 0; i < arguments.size(); ++i) {
+#ifdef Q_OS_MAC
+ argv[i + 1] = ::strdup(arguments.at(i).toUtf8().constData());
+#else
+ argv[i + 1] = ::strdup(arguments.at(i).toLocal8Bit().constData());
+#endif
+ }
+ argv[arguments.size() + 1] = 0;
+
+ if (!program.contains(QLatin1Char('/'))) {
+ const QString path = QString::fromLocal8Bit(::getenv("PATH"));
+ if (!path.isEmpty()) {
+ QStringList pathEntries = path.split(QLatin1Char(':'));
+ for (int k = 0; k < pathEntries.size(); ++k) {
+ QByteArray tmp = QFile::encodeName(pathEntries.at(k));
+ if (!tmp.endsWith('/')) tmp += '/';
+ tmp += QFile::encodeName(program);
+ argv[0] = tmp.data();
+ qt_native_execv(argv[0], argv);
+ }
+ }
+ } else {
+ QByteArray tmp = QFile::encodeName(program);
+ argv[0] = tmp.data();
+ qt_native_execv(argv[0], argv);
+ }
+
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ // '\1' means execv failed
+ char c = '\1';
+ qt_native_write(startedPipe[1], &c, 1);
+ qt_native_close(startedPipe[1]);
+ ::_exit(1);
+ } else if (doubleForkPid == -1) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ qt_native_sigaction(SIGPIPE, &noaction, 0);
+
+ // '\2' means internal error
+ char c = '\2';
+ qt_native_write(startedPipe[1], &c, 1);
+ }
+
+ qt_native_close(startedPipe[1]);
+ qt_native_write(pidPipe[1], (const char *)&doubleForkPid, sizeof(pid_t));
+ qt_native_chdir("/");
+ ::_exit(1);
+ }
+
+ qt_native_close(startedPipe[1]);
+ qt_native_close(pidPipe[1]);
+
+ if (childPid == -1) {
+ qt_native_close(startedPipe[0]);
+ qt_native_close(pidPipe[0]);
+ return false;
+ }
+
+ char reply = '\0';
+ int startResult = qt_native_read(startedPipe[0], &reply, 1);
+ int result;
+ qt_native_close(startedPipe[0]);
+ while (::waitpid(childPid, &result, 0) == -1 && errno == EINTR)
+ { }
+ bool success = (startResult != -1 && reply == '\0');
+ if (success && pid) {
+ pid_t actualPid = 0;
+ if (qt_native_read(pidPipe[0], (char *)&actualPid, sizeof(pid_t)) == sizeof(pid_t)) {
+ *pid = actualPid;
+ } else {
+ *pid = 0;
+ }
+ }
+ qt_native_close(pidPipe[0]);
+ return success;
+}
+
+void QProcessPrivate::initializeProcessManager()
+{
+ (void) processManager();
+}
+
+QT_END_NAMESPACE
+
+#include "qprocess_unix.moc"
+
+#endif // QT_NO_PROCESS
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
new file mode 100644
index 0000000..0e36760
--- /dev/null
+++ b/src/corelib/io/qprocess_win.cpp
@@ -0,0 +1,909 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprocess.h"
+#include "qprocess_p.h"
+#include "qwindowspipewriter_p.h"
+
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <private/qwineventnotifier_p.h>
+#include <private/qthread_p.h>
+#include <qdebug.h>
+
+#include "private/qfsfileengine_p.h" // for longFileName and win95FileName
+
+
+#ifndef QT_NO_PROCESS
+
+QT_BEGIN_NAMESPACE
+
+//#define QPROCESS_DEBUG
+
+#define NOTIFYTIMEOUT 100
+
+static void qt_create_pipe(Q_PIPE *pipe, bool in)
+{
+ // Open the pipes. Make non-inheritable copies of input write and output
+ // read handles to avoid non-closable handles (this is done by the
+ // DuplicateHandle() call).
+
+#if !defined(Q_OS_WINCE)
+ SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
+
+ HANDLE tmpHandle;
+ if (in) { // stdin
+ if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024))
+ return;
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
+ &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS))
+ return;
+ } else { // stdout or stderr
+ if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024))
+ return;
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
+ &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS))
+ return;
+ }
+
+ CloseHandle(tmpHandle);
+#else
+ Q_UNUSED(pipe);
+ Q_UNUSED(in);
+#endif
+}
+
+/*
+ Create the pipes to a QProcessPrivate::Channel.
+
+ This function must be called in order: stdin, stdout, stderr
+*/
+bool QProcessPrivate::createChannel(Channel &channel)
+{
+ Q_Q(QProcess);
+
+ if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
+ return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
+ &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
+ }
+
+ if (channel.type == Channel::Normal) {
+ // we're piping this channel to our own process
+ qt_create_pipe(channel.pipe, &channel == &stdinChannel);
+
+ return true;
+ } else if (channel.type == Channel::Redirect) {
+ // we're redirecting the channel to/from a file
+ SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+
+ if (&channel == &stdinChannel) {
+ // try to open in read-only mode
+ channel.pipe[1] = INVALID_Q_PIPE;
+ QT_WA({
+ channel.pipe[0] =
+ CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }, {
+ channel.pipe[0] =
+ CreateFileA(QFSFileEnginePrivate::win95Name(channel.file),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ });
+ if (channel.pipe[0] != INVALID_Q_PIPE)
+ return true;
+
+ q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
+ } else {
+ // open in write mode
+ channel.pipe[0] = INVALID_Q_PIPE;
+ DWORD creation;
+ if (channel.append)
+ creation = OPEN_ALWAYS;
+ else
+ creation = CREATE_ALWAYS;
+
+ QT_WA({
+ channel.pipe[1] =
+ CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ creation,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }, {
+ channel.pipe[1] =
+ CreateFileA(QFSFileEnginePrivate::win95Name(channel.file),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &secAtt,
+ creation,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ });
+ if (channel.pipe[1] != INVALID_Q_PIPE) {
+ if (channel.append) {
+ SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
+ }
+ return true;
+ }
+
+ q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
+ }
+
+ // could not open file
+ processError = QProcess::FailedToStart;
+ emit q->error(processError);
+ cleanup();
+ return false;
+ } else {
+ Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
+
+ Channel *source;
+ Channel *sink;
+
+ if (channel.type == Channel::PipeSource) {
+ // we are the source
+ source = &channel;
+ sink = &channel.process->stdinChannel;
+
+ if (source->pipe[1] != INVALID_Q_PIPE) {
+ // already constructed by the sink
+ // make it inheritable
+ HANDLE tmpHandle = source->pipe[1];
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
+ GetCurrentProcess(), &source->pipe[1],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ return false;
+
+ CloseHandle(tmpHandle);
+ return true;
+ }
+
+ Q_ASSERT(source == &stdoutChannel);
+ Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
+
+ qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
+ sink->pipe[0] = source->pipe[0];
+ source->pipe[0] = INVALID_Q_PIPE;
+
+ return true;
+ } else {
+ // we are the sink;
+ source = &channel.process->stdoutChannel;
+ sink = &channel;
+
+ if (sink->pipe[0] != INVALID_Q_PIPE) {
+ // already constructed by the source
+ // make it inheritable
+ HANDLE tmpHandle = sink->pipe[0];
+ if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
+ GetCurrentProcess(), &sink->pipe[0],
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ return false;
+
+ CloseHandle(tmpHandle);
+ return true;
+ }
+ Q_ASSERT(sink == &stdinChannel);
+ Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
+
+ qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
+ source->pipe[1] = sink->pipe[1];
+ sink->pipe[1] = INVALID_Q_PIPE;
+
+ return true;
+ }
+ }
+}
+
+void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
+{
+ if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) {
+ delete pipeWriter;
+ pipeWriter = 0;
+ }
+
+ if (pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(pipe[0]);
+ pipe[0] = INVALID_Q_PIPE;
+ }
+ if (pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(pipe[1]);
+ pipe[1] = INVALID_Q_PIPE;
+ }
+}
+
+
+static QString qt_create_commandline(const QString &program, const QStringList &arguments)
+{
+ QString args;
+ if (!program.isEmpty()) {
+ QString programName = program;
+ if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" ")))
+ programName = QLatin1String("\"") + programName + QLatin1String("\"");
+ programName.replace(QLatin1String("/"), QLatin1String("\\"));
+
+ // add the prgram as the first arg ... it works better
+ args = programName + QLatin1String(" ");
+ }
+
+ 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("\\");
+ }
+ args += QLatin1String(" \"") + tmp.left(i) + endQuote;
+ } else {
+ args += QLatin1Char(' ') + tmp;
+ }
+ }
+ return args;
+}
+
+static QByteArray qt_create_environment(const QStringList &environment)
+{
+ QByteArray envlist;
+ if (!environment.isEmpty()) {
+ QStringList envStrings = environment;
+ int pos = 0;
+ // add PATH if necessary (for DLL loading)
+ if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) {
+ QByteArray path = qgetenv("PATH");
+ if (!path.isEmpty())
+ envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path)));
+ }
+ // add systemroot if needed
+ if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) {
+ QByteArray systemRoot = qgetenv("SystemRoot");
+ if (!systemRoot.isEmpty())
+ envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot)));
+ }
+#ifdef UNICODE
+ if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
+ for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) {
+ QString tmp = *it;
+ uint tmpSize = sizeof(TCHAR) * (tmp.length()+1);
+ envlist.resize(envlist.size() + tmpSize);
+ memcpy(envlist.data()+pos, tmp.utf16(), tmpSize);
+ pos += tmpSize;
+ }
+ // add the 2 terminating 0 (actually 4, just to be on the safe side)
+ envlist.resize( envlist.size()+4 );
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ } else
+#endif // UNICODE
+ {
+ for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++) {
+ QByteArray tmp = (*it).toLocal8Bit();
+ uint tmpSize = tmp.length() + 1;
+ envlist.resize(envlist.size() + tmpSize);
+ memcpy(envlist.data()+pos, tmp.data(), tmpSize);
+ pos += tmpSize;
+ }
+ // add the terminating 0 (actually 2, just to be on the safe side)
+ envlist.resize(envlist.size()+2);
+ envlist[pos++] = 0;
+ envlist[pos++] = 0;
+ }
+ }
+ return envlist;
+}
+
+void QProcessPrivate::startProcess()
+{
+ Q_Q(QProcess);
+
+ bool success = false;
+
+ if (pid) {
+ CloseHandle(pid->hThread);
+ CloseHandle(pid->hProcess);
+ delete pid;
+ pid = 0;
+ }
+ pid = new PROCESS_INFORMATION;
+ memset(pid, 0, sizeof(PROCESS_INFORMATION));
+
+ q->setProcessState(QProcess::Starting);
+
+ if (!createChannel(stdinChannel) ||
+ !createChannel(stdoutChannel) ||
+ !createChannel(stderrChannel))
+ return;
+
+#if defined(Q_OS_WINCE)
+ QString args = qt_create_commandline(QString(), arguments);
+#else
+ QString args = qt_create_commandline(program, arguments);
+ QByteArray envlist = qt_create_environment(environment);
+#endif
+
+#if defined QPROCESS_DEBUG
+ qDebug("Creating process");
+ qDebug(" program : [%s]", program.toLatin1().constData());
+ qDebug(" args : %s", args.toLatin1().constData());
+ qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
+#endif
+
+ DWORD dwCreationFlags = 0;
+ if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based))
+ dwCreationFlags |= CREATE_NO_WINDOW;
+
+#ifdef UNICODE
+ if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
+#if defined(Q_OS_WINCE)
+ QString fullPathProgram = program;
+ if (!QDir::isAbsolutePath(fullPathProgram))
+ fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
+ fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\"));
+ success = CreateProcessW((WCHAR*)fullPathProgram.utf16(),
+ (WCHAR*)args.utf16(),
+ 0, 0, false, 0, 0, 0, 0, pid);
+#else
+ dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
+ STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
+ };
+ success = CreateProcessW(0, (WCHAR*)args.utf16(),
+ 0, 0, TRUE, dwCreationFlags,
+ environment.isEmpty() ? 0 : envlist.data(),
+ workingDirectory.isEmpty() ? 0
+ : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(),
+ &startupInfo, pid);
+#endif
+ } else
+#endif // UNICODE
+ {
+#ifndef Q_OS_WINCE
+ STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
+ };
+
+ success = CreateProcessA(0, args.toLocal8Bit().data(),
+ 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(),
+ workingDirectory.isEmpty() ? 0
+ : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(),
+ &startupInfo, pid);
+#endif // Q_OS_WINCE
+ }
+#ifndef Q_OS_WINCE
+ if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = INVALID_Q_PIPE;
+ }
+ if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+#endif // Q_OS_WINCE
+
+ if (!success) {
+ cleanup();
+ processError = QProcess::FailedToStart;
+ q->setErrorString(QProcess::tr("Process failed to start"));
+ emit q->error(processError);
+ q->setProcessState(QProcess::NotRunning);
+ return;
+ }
+
+ q->setProcessState(QProcess::Running);
+ // User can call kill()/terminate() from the stateChanged() slot
+ // so check before proceeding
+ if (!pid)
+ return;
+
+ if (threadData->eventDispatcher) {
+ processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
+ QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
+ processFinishedNotifier->setEnabled(true);
+ notifier = new QTimer(q);
+ QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
+ notifier->start(NOTIFYTIMEOUT);
+ }
+
+ // give the process a chance to start ...
+ Sleep(SLEEPMIN*2);
+ _q_startupNotification();
+}
+
+bool QProcessPrivate::processStarted()
+{
+ return processState == QProcess::Running;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStdout() const
+{
+ if (stdoutChannel.pipe[0] == INVALID_Q_PIPE)
+ return 0;
+
+ DWORD bytesAvail = 0;
+#if !defined(Q_OS_WINCE)
+ PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
+#endif
+ if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
+ QByteArray buf(bytesAvail, 0);
+ DWORD bytesRead = 0;
+ if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
+ HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdout) {
+ DWORD bytesWritten = 0;
+ WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0);
+ }
+ }
+ bytesAvail = 0;
+ }
+#endif
+ return bytesAvail;
+}
+
+qint64 QProcessPrivate::bytesAvailableFromStderr() const
+{
+ if (stderrChannel.pipe[0] == INVALID_Q_PIPE)
+ return 0;
+
+ DWORD bytesAvail = 0;
+#if !defined(Q_OS_WINCE)
+ PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
+#endif
+ if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
+ QByteArray buf(bytesAvail, 0);
+ DWORD bytesRead = 0;
+ if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
+ HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
+ if (hStderr) {
+ DWORD bytesWritten = 0;
+ WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0);
+ }
+ }
+ bytesAvail = 0;
+ }
+#endif
+ return bytesAvail;
+}
+
+qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
+{
+ DWORD read = qMin(maxlen, bytesAvailableFromStdout());
+ DWORD bytesRead = 0;
+
+ if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0))
+ return -1;
+ return bytesRead;
+}
+
+qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
+{
+ DWORD read = qMin(maxlen, bytesAvailableFromStderr());
+ DWORD bytesRead = 0;
+
+ if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0))
+ return -1;
+ return bytesRead;
+}
+
+
+static BOOL CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
+{
+ DWORD currentProcId = 0;
+ GetWindowThreadProcessId(hwnd, &currentProcId);
+ if (currentProcId == (DWORD)procId)
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+
+ return TRUE;
+}
+
+void QProcessPrivate::terminateProcess()
+{
+ if (pid) {
+ EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
+ PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
+ }
+}
+
+void QProcessPrivate::killProcess()
+{
+ if (pid)
+ TerminateProcess(pid->hProcess, 0xf291);
+}
+
+bool QProcessPrivate::waitForStarted(int)
+{
+ Q_Q(QProcess);
+
+ if (processStarted())
+ return true;
+
+ if (processError == QProcess::FailedToStart)
+ return false;
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+bool QProcessPrivate::waitForReadyRead(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+ return false;
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ if (!writeBuffer.isEmpty() && !_q_canWrite())
+ return false;
+ if (pipeWriter && pipeWriter->waitForWrite(0))
+ timer.resetIncrements();
+ bool readyReadEmitted = false;
+ if (bytesAvailableFromStdout() != 0) {
+ readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted;
+ timer.resetIncrements();
+ }
+
+ if (bytesAvailableFromStderr() != 0) {
+ readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted;
+ timer.resetIncrements();
+ }
+
+ if (readyReadEmitted)
+ return true;
+
+ if (!pid)
+ return false;
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
+ // find the return value if there is noew data to read
+ _q_processDied();
+ return false;
+ }
+
+ Sleep(timer.nextSleepTime());
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+bool QProcessPrivate::waitForBytesWritten(int msecs)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::ReadError;
+ q->setErrorString(QProcess::tr("Error reading from process"));
+ emit q->error(processError);
+ return false;
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ // Check if we have any data pending: the pipe writer has
+ // bytes waiting to written, or it has written data since the
+ // last time we called pipeWriter->waitForWrite().
+ bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
+
+ // If we don't have pending data, and our write buffer is
+ // empty, we fail.
+ if (!pendingDataInPipe && writeBuffer.isEmpty())
+ return false;
+
+ // If we don't have pending data and we do have data in our
+ // write buffer, try to flush that data over to the pipe
+ // writer. Fail on error.
+ if (!pendingDataInPipe) {
+ if (!_q_canWrite())
+ return false;
+ }
+
+ // Wait for the pipe writer to acknowledge that it has
+ // written. This will succeed if either the pipe writer has
+ // already written the data, or if it manages to write data
+ // within the given timeout. If the write buffer was non-empty
+ // and the pipeWriter is now dead, that means _q_canWrite()
+ // destroyed the writer after it successfully wrote the last
+ // batch.
+ if (!pipeWriter || pipeWriter->waitForWrite(0))
+ return true;
+
+ // If we wouldn't write anything, check if we can read stdout.
+ if (bytesAvailableFromStdout() != 0) {
+ _q_canReadStandardOutput();
+ timer.resetIncrements();
+ }
+
+ // Check if we can read stderr.
+ if (bytesAvailableFromStderr() != 0) {
+ _q_canReadStandardError();
+ timer.resetIncrements();
+ }
+
+ // Check if the process died while reading.
+ if (!pid)
+ return false;
+
+ // Wait for the process to signal any change in its state,
+ // such as incoming data, or if the process died.
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
+ _q_processDied();
+ return false;
+ }
+
+ // Only wait for as long as we've been asked.
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+
+bool QProcessPrivate::waitForFinished(int msecs)
+{
+ Q_Q(QProcess);
+#if defined QPROCESS_DEBUG
+ qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
+#endif
+
+ QIncrementalSleepTimer timer(msecs);
+
+ forever {
+ if (!writeBuffer.isEmpty() && !_q_canWrite())
+ return false;
+ if (pipeWriter && pipeWriter->waitForWrite(0))
+ timer.resetIncrements();
+
+ if (bytesAvailableFromStdout() != 0) {
+ _q_canReadStandardOutput();
+ timer.resetIncrements();
+ }
+
+ if (bytesAvailableFromStderr() != 0) {
+ _q_canReadStandardError();
+ timer.resetIncrements();
+ }
+
+ if (!pid)
+ return true;
+
+ if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
+ _q_processDied();
+ return true;
+ }
+
+ if (timer.hasTimedOut())
+ break;
+ }
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+
+void QProcessPrivate::findExitCode()
+{
+ DWORD theExitCode;
+ if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
+ exitCode = theExitCode;
+ //### for now we assume a crash if exit code is less than -1 or the magic number
+ crashed = (exitCode == 0xf291 || (int)exitCode < 0);
+ }
+}
+
+void QProcessPrivate::flushPipeWriter()
+{
+ if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
+ pipeWriter->waitForWrite(ULONG_MAX);
+ }
+}
+
+qint64 QProcessPrivate::pipeWriterBytesToWrite() const
+{
+ return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
+}
+
+qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
+{
+ Q_Q(QProcess);
+
+#if defined(Q_OS_WINCE)
+ processError = QProcess::WriteError;
+ q->setErrorString(QProcess::tr("Error writing to process"));
+ emit q->error(processError);
+ return -1;
+#endif
+
+ if (!pipeWriter) {
+ pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
+ pipeWriter->start();
+ }
+
+ return pipeWriter->write(data, maxlen);
+}
+
+bool QProcessPrivate::waitForWrite(int msecs)
+{
+ Q_Q(QProcess);
+
+ if (!pipeWriter || pipeWriter->waitForWrite(msecs))
+ return true;
+
+ processError = QProcess::Timedout;
+ q->setErrorString(QProcess::tr("Process operation timed out"));
+ return false;
+}
+
+void QProcessPrivate::_q_notified()
+{
+ notifier->stop();
+
+ if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0)))
+ _q_canWrite();
+
+ if (bytesAvailableFromStdout())
+ _q_canReadStandardOutput();
+
+ if (bytesAvailableFromStderr())
+ _q_canReadStandardError();
+
+ if (processState != QProcess::NotRunning)
+ notifier->start(NOTIFYTIMEOUT);
+}
+
+bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
+{
+#if defined(Q_OS_WINCE)
+ Q_UNUSED(workingDir);
+ QString args = qt_create_commandline(QString(), arguments);
+#else
+ QString args = qt_create_commandline(program, arguments);
+#endif
+
+ bool success = false;
+
+ PROCESS_INFORMATION pinfo;
+
+#ifdef UNICODE
+ if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) {
+#if defined(Q_OS_WINCE)
+ QString fullPathProgram = program;
+ if (!QDir::isAbsolutePath(fullPathProgram))
+ fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/")));
+ fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\"));
+ success = CreateProcessW((WCHAR*)fullPathProgram.utf16(),
+ (WCHAR*)args.utf16(),
+ 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
+#else
+ STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ success = CreateProcessW(0, (WCHAR*)args.utf16(),
+ 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
+ workingDir.isEmpty() ? (const WCHAR *)0 : (const WCHAR *)workingDir.utf16(),
+ &startupInfo, &pinfo);
+#endif
+ } else
+#endif // UNICODE
+ {
+#ifndef Q_OS_WINCE
+ STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ success = CreateProcessA(0, args.toLocal8Bit().data(),
+ 0, 0, FALSE, CREATE_NEW_CONSOLE, 0,
+ workingDir.isEmpty() ? (LPCSTR)0 : workingDir.toLocal8Bit().constData(),
+ &startupInfo, &pinfo);
+#endif // Q_OS_WINCE
+ }
+
+ if (success) {
+ CloseHandle(pinfo.hThread);
+ CloseHandle(pinfo.hProcess);
+ if (pid)
+ *pid = pinfo.dwProcessId;
+ }
+
+ return success;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PROCESS
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
new file mode 100644
index 0000000..a1f921e
--- /dev/null
+++ b/src/corelib/io/qresource.cpp
@@ -0,0 +1,1460 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qresource.h"
+#include "qresource_p.h"
+#include "qresource_iterator_p.h"
+#include "qset.h"
+#include "qhash.h"
+#include "qmutex.h"
+#include "qdebug.h"
+#include "qlocale.h"
+#include "qglobal.h"
+#include "qvector.h"
+#include "qdatetime.h"
+#include "qbytearray.h"
+#include "qstringlist.h"
+#include <qshareddata.h>
+#include <qplatformdefs.h>
+#include "private/qabstractfileengine_p.h"
+
+//#define DEBUG_RESOURCE_MATCH
+
+QT_BEGIN_NAMESPACE
+
+//resource glue
+class QResourceRoot
+{
+ enum Flags
+ {
+ Compressed = 0x01,
+ Directory = 0x02
+ };
+ const uchar *tree, *names, *payloads;
+ inline int findOffset(int node) const { return node * 14; } //sizeof each tree element
+ int hash(int node) const;
+ QString name(int node) const;
+ short flags(int node) const;
+public:
+ mutable QAtomicInt ref;
+
+ inline QResourceRoot(): tree(0), names(0), payloads(0) {}
+ inline QResourceRoot(const uchar *t, const uchar *n, const uchar *d) { setSource(t, n, d); }
+ virtual ~QResourceRoot() { }
+ int findNode(const QString &path, const QLocale &locale=QLocale()) const;
+ inline bool isContainer(int node) const { return flags(node) & Directory; }
+ inline bool isCompressed(int node) const { return flags(node) & Compressed; }
+ const uchar *data(int node, qint64 *size) const;
+ QStringList children(int node) const;
+ virtual QString mappingRoot() const { return QString(); }
+ bool mappingRootSubdir(const QString &path, QString *match=0) const;
+ inline bool operator==(const QResourceRoot &other) const
+ { return tree == other.tree && names == other.names && payloads == other.payloads; }
+ inline bool operator!=(const QResourceRoot &other) const
+ { return !operator==(other); }
+ enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
+ virtual ResourceRootType type() const { return Resource_Builtin; }
+
+protected:
+ inline void setSource(const uchar *t, const uchar *n, const uchar *d) {
+ tree = t;
+ names = n;
+ payloads = d;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
+
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive))
+
+typedef QList<QResourceRoot*> ResourceList;
+Q_GLOBAL_STATIC(ResourceList, resourceList)
+
+Q_GLOBAL_STATIC(QStringList, resourceSearchPaths)
+
+/*!
+ \class QResource
+ \brief The QResource class provides an interface for reading directly from resources.
+
+ \ingroup io
+ \mainclass
+ \reentrant
+ \since 4.2
+
+ QResource is an object that represents a set of data (and possibly
+ children) relating to a single resource entity. QResource gives direct
+ access to the bytes in their raw format. In this way direct access
+ allows reading data without buffer copying or indirection. Indirection
+ is often useful when interacting with the resource entity as if it is a
+ file, this can be achieved with QFile. The data and children behind a
+ QResource are normally compiled into an application/library, but it is
+ also possible to load a resource at runtime. When loaded at run time
+ the resource file will be loaded as one big set of data and then given
+ out in pieces via references into the resource tree.
+
+ A QResource can either be loaded with an absolute path, either treated
+ as a file system rooted with a \c{/} character, or in resource notation
+ rooted with a \c{:} character. A relative resource can also be opened
+ which will be found through the searchPaths().
+
+ A QResource that is representing a file will have data backing it, this
+ data can possibly be compressed, in which case qUncompress() must be
+ used to access the real data; this happens implicitly when accessed
+ through a QFile. A QResource that is representing a directory will have
+ only children and no data.
+
+ \section1 Dynamic Resource Loading
+
+ A resource can be left out of an application's binary and loaded when
+ it is needed at run-time by using the registerResource() function. The
+ resource file passed into registerResource() must be a binary resource
+ as created by rcc. Further information about binary resources can be
+ found in \l{The Qt Resource System} documentation.
+
+ This can often be useful when loading a large set of application icons
+ that may change based on a setting, or that can be edited by a user and
+ later recreated. The resource is immediately loaded into memory, either
+ as a result of a single file read operation, or as a memory mapped file.
+
+ This approach can prove to be a significant performance gain as only a
+ single file will be loaded, and pieces of data will be given out via the
+ path requested in setFileName().
+
+ The unregisterResource() function removes a reference to a particular
+ file. If there are QResources that currently reference resources related
+ to the unregistered file, they will continue to be valid but the resource
+ file itself will be removed from the resource roots, and thus no further
+ QResource can be created pointing into this resource data. The resource
+ itself will be unmapped from memory when the last QResource that points
+ to it is destroyed.
+
+ \sa {The Qt Resource System}, QFile, QDir, QFileInfo
+*/
+
+class QResourcePrivate {
+public:
+ inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
+ inline ~QResourcePrivate() { clear(); }
+
+ void ensureInitialized() const;
+ void ensureChildren() const;
+
+ bool load(const QString &file);
+ void clear();
+
+ QLocale locale;
+ QString fileName, absoluteFilePath;
+ QList<QResourceRoot*> related;
+ uint container : 1;
+ mutable uint compressed : 1;
+ mutable qint64 size;
+ mutable const uchar *data;
+ mutable QStringList children;
+
+ QResource *q_ptr;
+ Q_DECLARE_PUBLIC(QResource)
+};
+
+void
+QResourcePrivate::clear()
+{
+ absoluteFilePath.clear();
+ compressed = 0;
+ data = 0;
+ size = 0;
+ children.clear();
+ container = 0;
+ for(int i = 0; i < related.size(); ++i) {
+ QResourceRoot *root = related.at(i);
+ if(!root->ref.deref())
+ delete root;
+ }
+ related.clear();
+}
+
+bool
+QResourcePrivate::load(const QString &file)
+{
+ related.clear();
+ QMutexLocker lock(resourceMutex());
+ const ResourceList *list = resourceList();
+ for(int i = 0; i < list->size(); ++i) {
+ QResourceRoot *res = list->at(i);
+ const int node = res->findNode(file);
+ if(node != -1) {
+ if(related.isEmpty()) {
+ container = res->isContainer(node);
+ if(!container) {
+ data = res->data(node, &size);
+ compressed = res->isCompressed(node);
+ } else {
+ data = 0;
+ size = 0;
+ compressed = 0;
+ }
+ } else if(res->isContainer(node) != container) {
+ qWarning("QResourceInfo: Resource [%s] has both data and children!", file.toLatin1().constData());
+ }
+ res->ref.ref();
+ related.append(res);
+ } else if(res->mappingRootSubdir(file)) {
+ container = true;
+ data = 0;
+ size = 0;
+ compressed = 0;
+ res->ref.ref();
+ related.append(res);
+ }
+ }
+ return !related.isEmpty();
+}
+
+void
+QResourcePrivate::ensureInitialized() const
+{
+ if(!related.isEmpty())
+ return;
+ QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
+ if(fileName == QLatin1String(":"))
+ that->fileName += QLatin1Char('/');
+ that->absoluteFilePath = fileName;
+ if(!that->absoluteFilePath.startsWith(QLatin1Char(':')))
+ that->absoluteFilePath.prepend(QLatin1Char(':'));
+
+ QString path = fileName;
+ if(path.startsWith(QLatin1Char(':')))
+ path = path.mid(1);
+
+ bool found = false;
+ if(path.startsWith(QLatin1Char('/'))) {
+ found = that->load(path);
+ } else {
+ QMutexLocker lock(resourceMutex());
+ QStringList searchPaths = *resourceSearchPaths();
+ searchPaths << QLatin1String("");
+ 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;
+ }
+ }
+ }
+}
+
+void
+QResourcePrivate::ensureChildren() const
+{
+ ensureInitialized();
+ if(!children.isEmpty() || !container || related.isEmpty())
+ return;
+
+ QString path = absoluteFilePath, k;
+ if(path.startsWith(QLatin1Char(':')))
+ path = path.mid(1);
+ QSet<QString> kids;
+ for(int i = 0; i < related.size(); ++i) {
+ QResourceRoot *res = related.at(i);
+ if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
+ if(!kids.contains(k)) {
+ children += k;
+ kids.insert(k);
+ }
+ } else {
+ const int node = res->findNode(path);
+ if(node != -1) {
+ QStringList related_children = res->children(node);
+ for(int kid = 0; kid < related_children.size(); ++kid) {
+ k = related_children.at(kid);
+ if(!kids.contains(k)) {
+ children += k;
+ kids.insert(k);
+ }
+ }
+ }
+ }
+ }
+}
+
+/*!
+ Constructs a QResource pointing to \a file. \a locale is used to
+ load a specific localization of a resource data.
+
+ \sa QFileInfo, searchPaths(), setFileName(), setLocale()
+*/
+
+QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QResourcePrivate(this))
+{
+ Q_D(QResource);
+ d->fileName = file;
+ d->locale = locale;
+}
+
+/*!
+ Releases the resources of the QResource object.
+*/
+QResource::~QResource()
+{
+ delete d_ptr;
+}
+
+/*!
+ Sets a QResource to only load the localization of resource to for \a
+ locale. If a resource for the specific locale is not found then the
+ C locale is used.
+
+ \sa setFileName()
+*/
+
+void QResource::setLocale(const QLocale &locale)
+{
+ Q_D(QResource);
+ d->clear();
+ d->locale = locale;
+}
+
+/*!
+ Returns the locale used to locate the data for the QResource.
+*/
+
+QLocale QResource::locale() const
+{
+ Q_D(const QResource);
+ return d->locale;
+}
+
+/*!
+ Sets a QResource to point to \a file. \a file can either be absolute,
+ in which case it is opened directly, if relative then the file will be
+ tried to be found in searchPaths().
+
+ \sa absoluteFilePath()
+*/
+
+void QResource::setFileName(const QString &file)
+{
+ Q_D(QResource);
+ d->clear();
+ d->fileName = file;
+}
+
+/*!
+ Returns the full path to the file that this QResource represents as it
+ was passed.
+
+ \sa absoluteFilePath()
+*/
+
+QString QResource::fileName() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->fileName;
+}
+
+/*!
+ Returns the real path that this QResource represents, if the resource
+ was found via the searchPaths() it will be indicated in the path.
+
+ \sa fileName()
+*/
+
+QString QResource::absoluteFilePath() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->absoluteFilePath;
+}
+
+/*!
+ Returns true if the resource really exists in the resource heirarchy,
+ false otherwise.
+
+*/
+
+bool QResource::isValid() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return !d->related.isEmpty();
+}
+
+/*!
+ \fn bool QResource::isFile() const
+
+ Returns true if the resource represents a file and thus has data
+ backing it, false if it represents a directory.
+
+ \sa isDir()
+*/
+
+
+/*!
+ Returns true if the resource represents a file and the data backing it
+ is in a compressed format, false otherwise.
+
+ \sa data(), isFile()
+*/
+
+bool QResource::isCompressed() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->compressed;
+}
+
+/*!
+ Returns the size of the data backing the resource.
+
+ \sa data(), isFile()
+*/
+
+qint64 QResource::size() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->size;
+}
+
+/*!
+ Returns direct access to a read only segment of data that this resource
+ represents. If the resource is compressed the data returns is
+ compressed and qUncompress() must be used to access the data. If the
+ resource is a directory 0 is returned.
+
+ \sa size(), isCompressed(), isFile()
+*/
+
+const uchar *QResource::data() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->data;
+}
+
+/*!
+ Returns true if the resource represents a directory and thus may have
+ children() in it, false if it represents a file.
+
+ \sa isFile()
+*/
+
+bool QResource::isDir() const
+{
+ Q_D(const QResource);
+ d->ensureInitialized();
+ return d->container;
+}
+
+/*!
+ Returns a list of all resources in this directory, if the resource
+ represents a file the list will be empty.
+
+ \sa isDir()
+*/
+
+QStringList QResource::children() const
+{
+ Q_D(const QResource);
+ d->ensureChildren();
+ return d->children;
+}
+
+/*!
+ \obsolete
+
+ Adds \a path to the search paths searched in to find resources that are
+ not specified with an absolute path. The \a path must be an absolute
+ path (start with \c{/}).
+
+ The default search path is to search only in the root (\c{:/}). The last
+ path added will be consulted first upon next QResource creation.
+
+ Use QDir::addSearchPath() with a prefix instead.
+*/
+
+void
+QResource::addSearchPath(const QString &path)
+{
+ if (!path.startsWith(QLatin1Char('/'))) {
+ qWarning("QResource::addResourceSearchPath: Search paths must be absolute (start with /) [%s]",
+ path.toLocal8Bit().data());
+ return;
+ }
+ QMutexLocker lock(resourceMutex());
+ resourceSearchPaths()->prepend(path);
+}
+
+/*!
+ Returns the current search path list. This list is consulted when
+ creating a relative resource.
+
+ \sa addSearchPath()
+*/
+
+QStringList
+QResource::searchPaths()
+{
+ QMutexLocker lock(resourceMutex());
+ return *resourceSearchPaths();
+}
+
+inline int QResourceRoot::hash(int node) const
+{
+ if(!node) //root
+ return 0;
+ const int offset = findOffset(node);
+ int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ name_offset += 2; //jump past name length
+ return (names[name_offset+0] << 24) + (names[name_offset+1] << 16) +
+ (names[name_offset+2] << 8) + (names[name_offset+3] << 0);
+}
+inline QString QResourceRoot::name(int node) const
+{
+ if(!node) // root
+ return QString();
+ const int offset = findOffset(node);
+
+ QString ret;
+ int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ const short name_length = (names[name_offset+0] << 8) +
+ (names[name_offset+1] << 0);
+ name_offset += 2;
+ name_offset += 4; //jump past hash
+ for(int i = 0; i < name_length*2; i+=2)
+ ret += QChar(names[name_offset+i+1], names[name_offset+i]);
+ return ret;
+}
+int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
+{
+ QString path = QDir::cleanPath(_path);
+ // QDir::cleanPath does not remove two trailing slashes under _Windows_
+ // due to support for UNC paths. Remove those manually.
+ if (path.startsWith(QLatin1String("//")))
+ path.remove(0, 1);
+
+ {
+ QString root = mappingRoot();
+ if(!root.isEmpty()) {
+ if(root == path) {
+ path = QLatin1String("/");
+ } else {
+ if(!root.endsWith(QLatin1String("/")))
+ root += QLatin1String("/");
+ if(path.size() >= root.size() && path.startsWith(root))
+ path = path.mid(root.length()-1);
+ if(path.isEmpty())
+ path = QLatin1String("/");
+ }
+ }
+ }
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
+#endif
+
+ if(path == QLatin1String("/"))
+ return 0;
+
+ //the root node is always first
+ int child_count = (tree[6] << 24) + (tree[7] << 16) +
+ (tree[8] << 8) + (tree[9] << 0);
+ int child = (tree[10] << 24) + (tree[11] << 16) +
+ (tree[12] << 8) + (tree[13] << 0);
+
+ //now iterate up the tree
+ int node = -1;
+ QStringList segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << "****" << segments;
+#endif
+ for(int i = 0; child_count && i < segments.size(); ++i) {
+ const QString &segment = segments[i];
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << " CHILDREN" << segment;
+ for(int j = 0; j < child_count; ++j) {
+ qDebug() << " " << child+j << " :: " << name(child+j);
+ }
+#endif
+ const int h = qHash(segment);
+
+ //do the binary search for the hash
+ int l = 0, r = child_count-1;
+ int sub_node = (l+r+1)/2;
+ while(r != l) {
+ const int sub_node_hash = hash(child+sub_node);
+ if(h == sub_node_hash)
+ break;
+ else if(h < sub_node_hash)
+ r = sub_node - 1;
+ else
+ l = sub_node;
+ sub_node = (l + r + 1) / 2;
+ }
+ sub_node += child;
+
+ //now do the "harder" compares
+ bool found = false;
+ if(hash(sub_node) == h) {
+ while(sub_node > child && hash(sub_node-1) == h) //backup for collisions
+ --sub_node;
+ for(; sub_node < child+child_count && hash(sub_node) == h; ++sub_node) { //here we go...
+ if(name(sub_node) == segment) {
+ found = true;
+ int offset = findOffset(sub_node);
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << " TRY" << sub_node << name(sub_node) << offset;
+#endif
+ offset += 4; //jump past name
+
+ const short flags = (tree[offset+0] << 8) +
+ (tree[offset+1] << 0);
+ offset += 2;
+
+ if(i == segments.size()-1) {
+ if(!(flags & Directory)) {
+ const short country = (tree[offset+0] << 8) +
+ (tree[offset+1] << 0);
+ offset += 2;
+
+ const short language = (tree[offset+0] << 8) +
+ (tree[offset+1] << 0);
+ offset += 2;
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << " " << "LOCALE" << country << language;
+#endif
+ if(country == locale.country() && language == locale.language()) {
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
+#endif
+ return sub_node;
+ } else if((country == QLocale::AnyCountry && language == locale.language()) ||
+ (country == QLocale::AnyCountry && language == QLocale::C && node == -1)) {
+ node = sub_node;
+ }
+ continue;
+ } else {
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
+#endif
+
+ return sub_node;
+ }
+ }
+
+ if(!(flags & Directory))
+ return -1;
+
+ child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ offset += 4;
+ child = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ break;
+ }
+ }
+ }
+ if(!found)
+ break;
+ }
+#ifdef DEBUG_RESOURCE_MATCH
+ qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
+#endif
+ return node;
+}
+short QResourceRoot::flags(int node) const
+{
+ if(node == -1)
+ return 0;
+ const int offset = findOffset(node) + 4; //jump past name
+ return (tree[offset+0] << 8) + (tree[offset+1] << 0);
+}
+const uchar *QResourceRoot::data(int node, qint64 *size) const
+{
+ if(node == -1) {
+ *size = 0;
+ return 0;
+ }
+ int offset = findOffset(node) + 4; //jump past name
+
+ const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
+ offset += 2;
+
+ offset += 4; //jump past locale
+
+ if(!(flags & Directory)) {
+ const int data_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ const uint data_length = (payloads[data_offset+0] << 24) + (payloads[data_offset+1] << 16) +
+ (payloads[data_offset+2] << 8) + (payloads[data_offset+3] << 0);
+ const uchar *ret = payloads+data_offset+4;
+ *size = data_length;
+ return ret;
+ }
+ *size = 0;
+ return 0;
+}
+QStringList QResourceRoot::children(int node) const
+{
+ if(node == -1)
+ return QStringList();
+ int offset = findOffset(node) + 4; //jump past name
+
+ const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
+ offset += 2;
+
+ QStringList ret;
+ if(flags & Directory) {
+ const int child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ offset += 4;
+ const int child_off = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
+ (tree[offset+2] << 8) + (tree[offset+3] << 0);
+ for(int i = child_off; i < child_off+child_count; ++i)
+ ret << name(i);
+ }
+ return ret;
+}
+bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
+{
+ const QString root = mappingRoot();
+ if(!root.isEmpty()) {
+ const QStringList root_segments = root.split(QLatin1Char('/'), QString::SkipEmptyParts),
+ path_segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ if(path_segments.size() <= root_segments.size()) {
+ int matched = 0;
+ for(int i = 0; i < path_segments.size(); ++i) {
+ if(root_segments[i] != path_segments[i])
+ break;
+ ++matched;
+ }
+ if(matched == path_segments.size()) {
+ if(match && root_segments.size() > matched)
+ *match = root_segments.at(matched);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
+ const unsigned char *name, const unsigned char *data)
+{
+ QMutexLocker lock(resourceMutex());
+ if(version == 0x01 && resourceList()) {
+ bool found = false;
+ QResourceRoot res(tree, name, data);
+ for(int i = 0; i < resourceList()->size(); ++i) {
+ if(*resourceList()->at(i) == res) {
+ found = true;
+ break;
+ }
+ }
+ if(!found) {
+ QResourceRoot *root = new QResourceRoot(tree, name, data);
+ root->ref.ref();
+ resourceList()->append(root);
+ }
+ return true;
+ }
+ return false;
+}
+
+Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
+ const unsigned char *name, const unsigned char *data)
+{
+ QMutexLocker lock(resourceMutex());
+ if(version == 0x01 && resourceList()) {
+ QResourceRoot res(tree, name, data);
+ for(int i = 0; i < resourceList()->size(); ) {
+ if(*resourceList()->at(i) == res) {
+ QResourceRoot *root = resourceList()->takeAt(i);
+ if(!root->ref.deref())
+ delete root;
+ } else {
+ ++i;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+//run time resource creation
+
+class QDynamicBufferResourceRoot: public QResourceRoot
+{
+ QString root;
+ const uchar *buffer;
+
+public:
+ inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { }
+ inline ~QDynamicBufferResourceRoot() { }
+ inline const uchar *mappingBuffer() const { return buffer; }
+ virtual QString mappingRoot() const { return root; }
+ virtual ResourceRootType type() const { return Resource_Buffer; }
+
+ bool registerSelf(const uchar *b) {
+ //setup the data now
+ int offset = 0;
+
+ //magic number
+ if(b[offset+0] != 'q' || b[offset+1] != 'r' ||
+ b[offset+2] != 'e' || b[offset+3] != 's') {
+ return false;
+ }
+ offset += 4;
+
+ const int version = (b[offset+0] << 24) + (b[offset+1] << 16) +
+ (b[offset+2] << 8) + (b[offset+3] << 0);
+ offset += 4;
+
+ const int tree_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
+ (b[offset+2] << 8) + (b[offset+3] << 0);
+ offset += 4;
+
+ const int data_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
+ (b[offset+2] << 8) + (b[offset+3] << 0);
+ offset += 4;
+
+ const int name_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
+ (b[offset+2] << 8) + (b[offset+3] << 0);
+ offset += 4;
+
+ if(version == 0x01) {
+ buffer = b;
+ setSource(b+tree_offset, b+name_offset, b+data_offset);
+ return true;
+ }
+ return false;
+ }
+};
+
+#if defined(Q_OS_UNIX)
+#define QT_USE_MMAP
+#endif
+
+// most of the headers below are already included in qplatformdefs.h
+// also this lacks Large File support but that's probably irrelevant
+#if defined(QT_USE_MMAP)
+// for mmap
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <sys/mman.h>
+#include <errno.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+
+
+class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot
+{
+ QString fileName;
+ // for mmap'ed files, this is what needs to be unmapped.
+ uchar *unmapPointer;
+ unsigned int unmapLength;
+
+public:
+ inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { }
+ ~QDynamicFileResourceRoot() {
+#if defined(QT_USE_MMAP)
+ if (unmapPointer) {
+ munmap((char*)unmapPointer, unmapLength);
+ unmapPointer = 0;
+ unmapLength = 0;
+ } else
+#endif
+ {
+ delete [] (uchar *)mappingBuffer();
+ }
+ }
+ QString mappingFile() const { return fileName; }
+ virtual ResourceRootType type() const { return Resource_File; }
+
+ bool registerSelf(const QString &f) {
+ bool fromMM = false;
+ uchar *data = 0;
+ unsigned int data_len = 0;
+
+#ifdef QT_USE_MMAP
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED -1
+#endif
+
+ int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY,
+#if defined(Q_OS_WIN)
+ _S_IREAD | _S_IWRITE
+#else
+ 0666
+#endif
+ );
+ if (fd >= 0) {
+ QT_STATBUF st;
+ if (!QT_FSTAT(fd, &st)) {
+ uchar *ptr;
+ ptr = reinterpret_cast<uchar *>(
+ mmap(0, st.st_size, // any address, whole file
+ PROT_READ, // read-only memory
+ MAP_FILE | MAP_PRIVATE, // swap-backed map from file
+ fd, 0)); // from offset 0 of fd
+ if (ptr && ptr != reinterpret_cast<uchar *>(MAP_FAILED)) {
+ data = ptr;
+ data_len = st.st_size;
+ fromMM = true;
+ }
+ }
+ ::close(fd);
+ }
+#endif // QT_USE_MMAP
+ if(!data) {
+ QFile file(f);
+ if (!file.exists())
+ return false;
+ data_len = file.size();
+ data = new uchar[data_len];
+
+ bool ok = false;
+ if (file.open(QIODevice::ReadOnly))
+ ok = (data_len == (uint)file.read((char*)data, data_len));
+ if (!ok) {
+ delete [] data;
+ data = 0;
+ data_len = 0;
+ return false;
+ }
+ fromMM = false;
+ }
+ if(data && QDynamicBufferResourceRoot::registerSelf(data)) {
+ if(fromMM) {
+ unmapPointer = data;
+ unmapLength = data_len;
+ }
+ fileName = f;
+ return true;
+ }
+ return false;
+ }
+};
+
+static QString qt_resource_fixResourceRoot(QString r) {
+ if(!r.isEmpty()) {
+ if(r.startsWith(QLatin1Char(':')))
+ r = r.mid(1);
+ if(!r.isEmpty())
+ r = QDir::cleanPath(r);
+ }
+ return r;
+}
+
+
+/*!
+ \fn bool QResource::registerResource(const QString &rccFileName, const QString &mapRoot)
+
+ Registers the resource with the given \a rccFileName at the location in the
+ resource tree specified by \a mapRoot, and returns true if the file is
+ successfully opened; otherwise returns false.
+
+ \sa unregisterResource()
+*/
+
+bool
+QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
+{
+ QString r = qt_resource_fixResourceRoot(resourceRoot);
+ if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
+ qWarning("QDir::registerResource: Registering a resource [%s] must be rooted in an absolute path (start with /) [%s]",
+ rccFilename.toLocal8Bit().data(), resourceRoot.toLocal8Bit().data());
+ return false;
+ }
+
+ QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
+ if(root->registerSelf(rccFilename)) {
+ root->ref.ref();
+ QMutexLocker lock(resourceMutex());
+ resourceList()->append(root);
+ return true;
+ }
+ delete root;
+ return false;
+}
+
+/*!
+ \fn bool QResource::unregisterResource(const QString &rccFileName, const QString &mapRoot)
+
+ Unregisters the resource with the given \a rccFileName at the location in
+ the resource tree specified by \a mapRoot, and returns true if the
+ resource is successfully unloaded and no references exist for the
+ resource; otherwise returns false.
+
+ \sa registerResource()
+*/
+
+bool
+QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
+{
+ QString r = qt_resource_fixResourceRoot(resourceRoot);
+
+ QMutexLocker lock(resourceMutex());
+ ResourceList *list = resourceList();
+ for(int i = 0; i < list->size(); ++i) {
+ QResourceRoot *res = list->at(i);
+ if(res->type() == QResourceRoot::Resource_File) {
+ QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot*>(res);
+ if(root->mappingFile() == rccFilename && root->mappingRoot() == r) {
+ resourceList()->removeAt(i);
+ if(!root->ref.deref()) {
+ delete root;
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+
+/*!
+ \fn bool QResource::registerResource(const uchar *rccData, const QString &mapRoot)
+ \since 4.3
+
+ Registers the resource with the given \a rccData at the location in the
+ resource tree specified by \a mapRoot, and returns true if the file is
+ successfully opened; otherwise returns false.
+
+ \warning The data must remain valid throughout the life of any QFile
+ that may reference the resource data.
+
+ \sa unregisterResource()
+*/
+
+bool
+QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
+{
+ QString r = qt_resource_fixResourceRoot(resourceRoot);
+ if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
+ qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an absolute path (start with /) [%s]",
+ rccData, resourceRoot.toLocal8Bit().data());
+ return false;
+ }
+
+ QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
+ if(root->registerSelf(rccData)) {
+ root->ref.ref();
+ QMutexLocker lock(resourceMutex());
+ resourceList()->append(root);
+ return true;
+ }
+ delete root;
+ return false;
+}
+
+/*!
+ \fn bool QResource::unregisterResource(const uchar *rccData, const QString &mapRoot)
+ \since 4.3
+
+ Unregisters the resource with the given \a rccData at the location in the
+ resource tree specified by \a mapRoot, and returns true if the resource is
+ successfully unloaded and no references exist into the resource; otherwise returns false.
+
+ \sa registerResource()
+*/
+
+bool
+QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
+{
+ QString r = qt_resource_fixResourceRoot(resourceRoot);
+
+ QMutexLocker lock(resourceMutex());
+ ResourceList *list = resourceList();
+ for(int i = 0; i < list->size(); ++i) {
+ QResourceRoot *res = list->at(i);
+ if(res->type() == QResourceRoot::Resource_Buffer) {
+ QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot*>(res);
+ if(root->mappingBuffer() == rccData && root->mappingRoot() == r) {
+ resourceList()->removeAt(i);
+ if(!root->ref.deref()) {
+ delete root;
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+//file type handler
+class QResourceFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ QResourceFileEngineHandler() { }
+ ~QResourceFileEngineHandler() { }
+ QAbstractFileEngine *create(const QString &path) const;
+};
+QAbstractFileEngine *QResourceFileEngineHandler::create(const QString &path) const
+{
+ if (path.size() > 0 && path.startsWith(QLatin1Char(':')))
+ return new QResourceFileEngine(path);
+ return 0;
+}
+
+//resource engine
+class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
+{
+protected:
+ Q_DECLARE_PUBLIC(QResourceFileEngine)
+private:
+ uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
+ bool unmap(uchar *ptr);
+ qint64 offset;
+ QResource resource;
+ QByteArray uncompressed;
+protected:
+ QResourceFileEnginePrivate() : offset(0) { }
+};
+
+bool QResourceFileEngine::mkdir(const QString &, bool) const
+{
+ return false;
+}
+
+bool QResourceFileEngine::rmdir(const QString &, bool) const
+{
+ return false;
+}
+
+bool QResourceFileEngine::setSize(qint64)
+{
+ return false;
+}
+
+QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ return QAbstractFileEngine::entryList(filters, filterNames);
+}
+
+bool QResourceFileEngine::caseSensitive() const
+{
+ return true;
+}
+
+QResourceFileEngine::QResourceFileEngine(const QString &file) :
+ QAbstractFileEngine(*new QResourceFileEnginePrivate)
+{
+ Q_D(QResourceFileEngine);
+ d->resource.setFileName(file);
+ if(d->resource.isCompressed() && d->resource.size()) {
+#ifndef QT_NO_COMPRESS
+ d->uncompressed = qUncompress(d->resource.data(), d->resource.size());
+#else
+ Q_ASSERT(!"QResourceFileEngine::open: Qt built without support for compression");
+#endif
+ }
+}
+
+QResourceFileEngine::~QResourceFileEngine()
+{
+}
+
+void QResourceFileEngine::setFileName(const QString &file)
+{
+ Q_D(QResourceFileEngine);
+ d->resource.setFileName(file);
+}
+
+bool QResourceFileEngine::open(QIODevice::OpenMode flags)
+{
+ Q_D(QResourceFileEngine);
+ if (d->resource.fileName().isEmpty()) {
+ qWarning("QResourceFileEngine::open: Missing file name");
+ return false;
+ }
+ if(flags & QIODevice::WriteOnly)
+ return false;
+ if(!d->resource.isValid())
+ return false;
+ return true;
+}
+
+bool QResourceFileEngine::close()
+{
+ Q_D(QResourceFileEngine);
+ d->offset = 0;
+ d->uncompressed.clear();
+ return true;
+}
+
+bool QResourceFileEngine::flush()
+{
+ return false;
+}
+
+qint64 QResourceFileEngine::read(char *data, qint64 len)
+{
+ Q_D(QResourceFileEngine);
+ if(len > size()-d->offset)
+ len = size()-d->offset;
+ if(len <= 0)
+ return 0;
+ if(d->resource.isCompressed())
+ memcpy(data, d->uncompressed.constData()+d->offset, len);
+ else
+ memcpy(data, d->resource.data()+d->offset, len);
+ d->offset += len;
+ return len;
+}
+
+qint64 QResourceFileEngine::write(const char *, qint64)
+{
+ return -1;
+}
+
+bool QResourceFileEngine::remove()
+{
+ return false;
+}
+
+bool QResourceFileEngine::copy(const QString &)
+{
+ return false;
+}
+
+bool QResourceFileEngine::rename(const QString &)
+{
+ return false;
+}
+
+bool QResourceFileEngine::link(const QString &)
+{
+ return false;
+}
+
+qint64 QResourceFileEngine::size() const
+{
+ Q_D(const QResourceFileEngine);
+ if(!d->resource.isValid())
+ return 0;
+ if(d->resource.isCompressed())
+ return d->uncompressed.size();
+ return d->resource.size();
+}
+
+qint64 QResourceFileEngine::pos() const
+{
+ Q_D(const QResourceFileEngine);
+ return d->offset;
+}
+
+bool QResourceFileEngine::atEnd() const
+{
+ Q_D(const QResourceFileEngine);
+ if(!d->resource.isValid())
+ return true;
+ return d->offset == size();
+}
+
+bool QResourceFileEngine::seek(qint64 pos)
+{
+ Q_D(QResourceFileEngine);
+ if(!d->resource.isValid())
+ return false;
+
+ if(d->offset > size())
+ return false;
+ d->offset = pos;
+ return true;
+}
+
+bool QResourceFileEngine::isSequential() const
+{
+ return false;
+}
+
+QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
+{
+ Q_D(const QResourceFileEngine);
+ QAbstractFileEngine::FileFlags ret = 0;
+ if(!d->resource.isValid())
+ return ret;
+
+ if(type & PermsMask)
+ ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
+ if(type & TypesMask) {
+ if(d->resource.isDir())
+ ret |= DirectoryType;
+ else
+ ret |= FileType;
+ }
+ if(type & FlagsMask) {
+ ret |= ExistsFlag;
+ if(d->resource.absoluteFilePath() == QLatin1String(":/"))
+ ret |= RootFlag;
+ }
+ return ret;
+}
+
+bool QResourceFileEngine::setPermissions(uint)
+{
+ return false;
+}
+
+QString QResourceFileEngine::fileName(FileName file) const
+{
+ Q_D(const QResourceFileEngine);
+ if(file == BaseName) {
+ int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
+ if (slash == -1)
+ return d->resource.fileName();
+ return d->resource.fileName().mid(slash + 1);
+ } else if(file == PathName || file == AbsolutePathName) {
+ const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath() : d->resource.fileName();
+ const int slash = path.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1)
+ return path.left(slash);
+ } else if(file == CanonicalName || file == CanonicalPathName) {
+ const QString absoluteFilePath = d->resource.absoluteFilePath();
+ if(file == CanonicalPathName) {
+ const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
+ if (slash != -1)
+ return absoluteFilePath.left(slash);
+ }
+ return absoluteFilePath;
+ }
+ return d->resource.fileName();
+}
+
+bool QResourceFileEngine::isRelativePath() const
+{
+ return false;
+}
+
+uint QResourceFileEngine::ownerId(FileOwner) const
+{
+ static const uint nobodyID = (uint) -2;
+ return nobodyID;
+}
+
+QString QResourceFileEngine::owner(FileOwner) const
+{
+ return QString();
+}
+
+QDateTime QResourceFileEngine::fileTime(FileTime) const
+{
+ return QDateTime();
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
+{
+ return new QResourceFileEngineIterator(filters, filterNames);
+}
+
+/*!
+ \internal
+*/
+QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
+{
+ return 0;
+}
+
+bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ Q_D(QResourceFileEngine);
+ if (extension == MapExtension) {
+ const MapExtensionOption *options = (MapExtensionOption*)(option);
+ MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
+ returnValue->address = d->map(options->offset, options->size, options->flags);
+ return (returnValue->address != 0);
+ }
+ if (extension == UnMapExtension) {
+ UnMapExtensionOption *options = (UnMapExtensionOption*)option;
+ return d->unmap(options->address);
+ }
+ return false;
+}
+
+bool QResourceFileEngine::supportsExtension(Extension extension) const
+{
+ return (extension == UnMapExtension || extension == MapExtension);
+}
+
+uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
+{
+ Q_Q(QResourceFileEngine);
+ Q_UNUSED(flags);
+ if (!resource.isValid()
+ || offset < 0
+ || size < 0
+ || offset + size > resource.size()
+ || (size == 0)) {
+ q->setError(QFile::UnspecifiedError, QString());
+ return 0;
+ }
+ uchar *address = const_cast<uchar *>(resource.data());
+ return (address + offset);
+}
+
+bool QResourceFileEnginePrivate::unmap(uchar *ptr)
+{
+ Q_UNUSED(ptr);
+ return true;
+}
+
+//Initialization and cleanup
+Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler)
+
+static int qt_force_resource_init() { resource_file_handler(); return 1; }
+Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); }
+static int qt_forced_resource_init = qt_force_resource_init();
+Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init)
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qresource.h b/src/corelib/io/qresource.h
new file mode 100644
index 0000000..1e9b2d5
--- /dev/null
+++ b/src/corelib/io/qresource.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRESOURCE_H
+#define QRESOURCE_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QResourcePrivate;
+
+class Q_CORE_EXPORT QResource
+{
+public:
+ QResource(const QString &file=QString(), const QLocale &locale=QLocale());
+ ~QResource();
+
+ void setFileName(const QString &file);
+ QString fileName() const;
+ QString absoluteFilePath() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ bool isValid() const;
+
+ bool isCompressed() const;
+ qint64 size() const;
+ const uchar *data() const;
+
+ static void addSearchPath(const QString &path);
+ static QStringList searchPaths();
+
+ static bool registerResource(const QString &rccFilename, const QString &resourceRoot=QString());
+ static bool unregisterResource(const QString &rccFilename, const QString &resourceRoot=QString());
+
+ static bool registerResource(const uchar *rccData, const QString &resourceRoot=QString());
+ static bool unregisterResource(const uchar *rccData, const QString &resourceRoot=QString());
+
+protected:
+ friend class QResourceFileEngine;
+ friend class QResourceFileEngineIterator;
+ bool isDir() const;
+ inline bool isFile() const { return !isDir(); }
+ QStringList children() const;
+
+protected:
+ QResourcePrivate *d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(QResource)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QRESOURCE_H
diff --git a/src/corelib/io/qresource_iterator.cpp b/src/corelib/io/qresource_iterator.cpp
new file mode 100644
index 0000000..0e7c0dd
--- /dev/null
+++ b/src/corelib/io/qresource_iterator.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qresource.h"
+#include "qresource_iterator_p.h"
+
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+QResourceFileEngineIterator::QResourceFileEngineIterator(QDir::Filters filters,
+ const QStringList &filterNames)
+ : QAbstractFileEngineIterator(filters, filterNames), index(-1)
+{
+}
+
+QResourceFileEngineIterator::~QResourceFileEngineIterator()
+{
+}
+
+QString QResourceFileEngineIterator::next()
+{
+ if (!hasNext())
+ return QString();
+ ++index;
+ return currentFilePath();
+}
+
+bool QResourceFileEngineIterator::hasNext() const
+{
+ if (index == -1) {
+ // Lazy initialization of the iterator
+ QResource resource(path());
+ if (!resource.isValid())
+ return false;
+
+ // Initialize and move to the next entry.
+ QResourceFileEngineIterator *that = const_cast<QResourceFileEngineIterator *>(this);
+ that->entries = resource.children();
+ if (!that->entries.isEmpty())
+ that->index = 0;
+ }
+
+ return index <= entries.size();
+}
+
+QString QResourceFileEngineIterator::currentFileName() const
+{
+ if (index <= 0 || index > entries.size())
+ return QString();
+ return entries.at(index - 1);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qresource_iterator_p.h b/src/corelib/io/qresource_iterator_p.h
new file mode 100644
index 0000000..0cf8fe7
--- /dev/null
+++ b/src/corelib/io/qresource_iterator_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRESOURCE_ITERATOR_P_H
+#define QRESOURCE_ITERATOR_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 "qabstractfileengine.h"
+#include "qdir.h"
+
+QT_BEGIN_NAMESPACE
+
+class QResourceFileEngineIteratorPrivate;
+class QResourceFileEngineIterator : public QAbstractFileEngineIterator
+{
+public:
+ QResourceFileEngineIterator(QDir::Filters filters, const QStringList &filterNames);
+ ~QResourceFileEngineIterator();
+
+ QString next();
+ bool hasNext() const;
+
+ QString currentFileName() const;
+
+private:
+ QStringList entries;
+ int index;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/io/qresource_p.h b/src/corelib/io/qresource_p.h
new file mode 100644
index 0000000..9706e93
--- /dev/null
+++ b/src/corelib/io/qresource_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRESOURCE_P_H
+#define QRESOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qabstractfileengine.h"
+
+QT_BEGIN_NAMESPACE
+
+class QResourceFileEnginePrivate;
+class QResourceFileEngine : public QAbstractFileEngine
+{
+private:
+ Q_DECLARE_PRIVATE(QResourceFileEngine)
+public:
+ explicit QResourceFileEngine(const QString &path);
+ ~QResourceFileEngine();
+
+ virtual void setFileName(const QString &file);
+
+ virtual bool open(QIODevice::OpenMode flags) ;
+ virtual bool close();
+ virtual bool flush();
+ virtual qint64 size() const;
+ virtual qint64 pos() const;
+ virtual bool atEnd() const;
+ virtual bool seek(qint64);
+ virtual qint64 read(char *data, qint64 maxlen);
+ virtual qint64 write(const char *data, qint64 len);
+
+ virtual bool remove();
+ virtual bool copy(const QString &newName);
+ virtual bool rename(const QString &newName);
+ virtual bool link(const QString &newName);
+
+ virtual bool isSequential() const;
+
+ virtual bool isRelativePath() const;
+
+ virtual bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ virtual bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
+
+ virtual bool setSize(qint64 size);
+
+ virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+
+ virtual bool caseSensitive() const;
+
+ virtual FileFlags fileFlags(FileFlags type) const;
+
+ virtual bool setPermissions(uint perms);
+
+ virtual QString fileName(QAbstractFileEngine::FileName file) const;
+
+ virtual uint ownerId(FileOwner) const;
+ virtual QString owner(FileOwner) const;
+
+ virtual QDateTime fileTime(FileTime time) const;
+
+ virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+ virtual Iterator *endEntryList();
+
+ bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0);
+ bool supportsExtension(Extension extension) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QRESOURCE_P_H
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
new file mode 100644
index 0000000..62b4ed5
--- /dev/null
+++ b/src/corelib/io/qsettings.cpp
@@ -0,0 +1,3822 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include "qplatformdefs.h"
+#include "qsettings.h"
+
+#ifndef QT_NO_SETTINGS
+
+#include "qsettings_p.h"
+#include "qcache.h"
+#include "qfile.h"
+#include "qdir.h"
+#include "qfileinfo.h"
+#include "qmutex.h"
+#include "qlibraryinfo.h"
+#include "qtemporaryfile.h"
+
+#ifndef QT_NO_TEXTCODEC
+# include "qtextcodec.h"
+#endif
+
+#ifndef QT_NO_GEOM_VARIANT
+#include "qsize.h"
+#include "qpoint.h"
+#include "qrect.h"
+#endif // !QT_NO_GEOM_VARIANT
+
+#ifndef QT_NO_QOBJECT
+#include "qcoreapplication.h"
+
+#ifdef Q_OS_WIN // for homedirpath reading from registry
+#include "qt_windows.h"
+#include "qlibrary.h"
+
+#endif // Q_OS_WIN
+#endif // QT_NO_QOBJECT
+
+#include <stdlib.h>
+
+#ifndef CSIDL_COMMON_APPDATA
+#define CSIDL_COMMON_APPDATA 0x0023 // All Users\Application Data
+#endif
+
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a // <username>\Application Data
+#endif
+
+// ************************************************************************
+// QConfFile
+
+/*
+ QConfFile objects are explicitly shared within the application.
+ This ensures that modification to the settings done through one
+ QSettings object are immediately reflected in other setting
+ objects of the same application.
+*/
+
+QT_BEGIN_NAMESPACE
+
+struct QConfFileCustomFormat
+{
+ QString extension;
+ QSettings::ReadFunc readFunc;
+ QSettings::WriteFunc writeFunc;
+ Qt::CaseSensitivity caseSensitivity;
+};
+
+typedef QHash<QString, QConfFile *> ConfFileHash;
+typedef QCache<QString, QConfFile> ConfFileCache;
+typedef QHash<int, QString> PathHash;
+typedef QVector<QConfFileCustomFormat> CustomFormatVector;
+
+Q_GLOBAL_STATIC(ConfFileHash, usedHashFunc)
+Q_GLOBAL_STATIC(ConfFileCache, unusedCacheFunc)
+Q_GLOBAL_STATIC(PathHash, pathHashFunc)
+Q_GLOBAL_STATIC(CustomFormatVector, customFormatVectorFunc)
+Q_GLOBAL_STATIC(QMutex, globalMutex)
+static QSettings::Format globalDefaultFormat = QSettings::NativeFormat;
+
+#ifndef Q_OS_WIN
+inline bool qt_isEvilFsTypeName(const char *name)
+{
+ return (qstrncmp(name, "nfs", 3) == 0
+ || qstrncmp(name, "autofs", 6) == 0
+ || qstrncmp(name, "cachefs", 7) == 0);
+}
+
+#if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <sys/param.h>
+# include <sys/mount.h>
+QT_END_INCLUDE_NAMESPACE
+
+static bool isLikelyToBeNfs(int handle)
+{
+ struct statfs buf;
+ if (fstatfs(handle, &buf) != 0)
+ return false;
+ return qt_isEvilFsTypeName(buf.f_fstypename);
+}
+
+#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <sys/vfs.h>
+# ifdef QT_LINUXBASE
+ // LSB 3.2 has fstatfs in sys/statfs.h, sys/vfs.h is just an empty dummy header
+# include <sys/statfs.h>
+# endif
+QT_END_INCLUDE_NAMESPACE
+# ifndef NFS_SUPER_MAGIC
+# define NFS_SUPER_MAGIC 0x00006969
+# endif
+# ifndef AUTOFS_SUPER_MAGIC
+# define AUTOFS_SUPER_MAGIC 0x00000187
+# endif
+# ifndef AUTOFSNG_SUPER_MAGIC
+# define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0
+# endif
+
+static bool isLikelyToBeNfs(int handle)
+{
+ struct statfs buf;
+ if (fstatfs(handle, &buf) != 0)
+ return false;
+ return buf.f_type == NFS_SUPER_MAGIC
+ || buf.f_type == AUTOFS_SUPER_MAGIC
+ || buf.f_type == AUTOFSNG_SUPER_MAGIC;
+}
+
+#elif defined(Q_OS_SOLARIS) || defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) \
+ || defined(Q_OS_OSF) || defined(Q_OS_QNX) || defined(Q_OS_QNX6) || defined(Q_OS_SCO) \
+ || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) || defined(Q_OS_NETBSD)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <sys/statvfs.h>
+QT_END_INCLUDE_NAMESPACE
+
+static bool isLikelyToBeNfs(int handle)
+{
+ struct statvfs buf;
+ if (fstatvfs(handle, &buf) != 0)
+ return false;
+#if defined(Q_OS_NETBSD)
+ return qt_isEvilFsTypeName(buf.f_fstypename);
+#else
+ return qt_isEvilFsTypeName(buf.f_basetype);
+#endif
+}
+#else
+static inline bool isLikelyToBeNfs(int /* handle */)
+{
+ return true;
+}
+#endif
+
+static bool unixLock(int handle, int lockType)
+{
+ /*
+ NFS hangs on the fcntl() call below when statd or lockd isn't
+ running. There's no way to detect this. Our work-around for
+ now is to disable locking when we detect NFS (or AutoFS or
+ CacheFS, which are probably wrapping NFS).
+ */
+ if (isLikelyToBeNfs(handle))
+ return false;
+
+ struct flock fl;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = lockType;
+ return fcntl(handle, F_SETLKW, &fl) == 0;
+}
+#endif
+
+QConfFile::QConfFile(const QString &fileName, bool _userPerms)
+ : name(fileName), size(0), ref(1), userPerms(_userPerms)
+{
+ usedHashFunc()->insert(name, this);
+}
+
+ParsedSettingsMap QConfFile::mergedKeyMap() const
+{
+ ParsedSettingsMap result = originalKeys;
+ ParsedSettingsMap::const_iterator i;
+
+ for (i = removedKeys.begin(); i != removedKeys.end(); ++i)
+ result.remove(i.key());
+ for (i = addedKeys.begin(); i != addedKeys.end(); ++i)
+ result.insert(i.key(), i.value());
+ return result;
+}
+
+bool QConfFile::isWritable() const
+{
+ QFileInfo fileInfo(name);
+
+#ifndef QT_NO_TEMPORARYFILE
+ if (fileInfo.exists()) {
+#endif
+ QFile file(name);
+ return file.open(QFile::ReadWrite);
+#ifndef QT_NO_TEMPORARYFILE
+ } else {
+ // Create the directories to the file.
+ QDir dir(fileInfo.absolutePath());
+ if (dir.exists() && dir.isReadable()) {
+ return true;
+ } else {
+ if (!dir.mkpath(dir.absolutePath()))
+ return false;
+ }
+
+ // we use a temporary file to avoid race conditions
+ QTemporaryFile file(name);
+ return file.open();
+ }
+#endif
+}
+
+QConfFile *QConfFile::fromName(const QString &fileName, bool _userPerms)
+{
+ QString absPath = QFileInfo(fileName).absoluteFilePath();
+
+ ConfFileHash *usedHash = usedHashFunc();
+ ConfFileCache *unusedCache = unusedCacheFunc();
+
+ QConfFile *confFile;
+ QMutexLocker locker(globalMutex());
+
+ if (!(confFile = usedHash->value(absPath))) {
+ if ((confFile = unusedCache->take(absPath)))
+ usedHash->insert(absPath, confFile);
+ }
+ if (confFile) {
+ confFile->ref.ref();
+ return confFile;
+ }
+ return new QConfFile(absPath, _userPerms);
+}
+
+void QConfFile::clearCache()
+{
+ QMutexLocker locker(globalMutex());
+ unusedCacheFunc()->clear();
+}
+
+// ************************************************************************
+// QSettingsPrivate
+
+QSettingsPrivate::QSettingsPrivate(QSettings::Format format)
+ : format(format), scope(QSettings::UserScope /* nothing better to put */), iniCodec(0), spec(0), fallbacks(true),
+ pendingChanges(false), status(QSettings::NoError)
+{
+}
+
+QSettingsPrivate::QSettingsPrivate(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application)
+ : format(format), scope(scope), organizationName(organization), applicationName(application),
+ iniCodec(0), spec(0), fallbacks(true), pendingChanges(false), status(QSettings::NoError)
+{
+}
+
+QSettingsPrivate::~QSettingsPrivate()
+{
+}
+
+QString QSettingsPrivate::actualKey(const QString &key) const
+{
+ QString n = normalizedKey(key);
+ Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key");
+ n.prepend(groupPrefix);
+ return n;
+}
+
+/*
+ Returns a string that never starts nor ends with a slash (or an
+ empty string). Examples:
+
+ "foo" becomes "foo"
+ "/foo//bar///" becomes "foo/bar"
+ "///" becomes ""
+
+ This function is optimized to avoid a QString deep copy in the
+ common case where the key is already normalized.
+*/
+QString QSettingsPrivate::normalizedKey(const QString &key)
+{
+ QString result = key;
+
+ int i = 0;
+ while (i < result.size()) {
+ while (result.at(i) == QLatin1Char('/')) {
+ result.remove(i, 1);
+ if (i == result.size())
+ goto after_loop;
+ }
+ while (result.at(i) != QLatin1Char('/')) {
+ ++i;
+ if (i == result.size())
+ return result;
+ }
+ ++i; // leave the slash alone
+ }
+
+after_loop:
+ if (!result.isEmpty())
+ result.truncate(i - 1); // remove the trailing slash
+ return result;
+}
+
+// see also qsettings_win.cpp and qsettings_mac.cpp
+
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
+QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application)
+{
+ return new QConfFileSettingsPrivate(format, scope, organization, application);
+}
+#endif
+
+#if !defined(Q_OS_WIN)
+QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
+{
+ return new QConfFileSettingsPrivate(fileName, format);
+}
+#endif
+
+void QSettingsPrivate::processChild(QString key, ChildSpec spec, QMap<QString, QString> &result)
+{
+ if (spec != AllKeys) {
+ int slashPos = key.indexOf(QLatin1Char('/'));
+ if (slashPos == -1) {
+ if (spec != ChildKeys)
+ return;
+ } else {
+ if (spec != ChildGroups)
+ return;
+ key.truncate(slashPos);
+ }
+ }
+ result.insert(key, QString());
+}
+
+void QSettingsPrivate::beginGroupOrArray(const QSettingsGroup &group)
+{
+ groupStack.push(group);
+ if (!group.name().isEmpty()) {
+ groupPrefix += group.name();
+ groupPrefix += QLatin1Char('/');
+ }
+}
+
+/*
+ We only set an error if there isn't one set already. This way the user always gets the
+ first error that occurred. We always allow clearing errors.
+*/
+
+void QSettingsPrivate::setStatus(QSettings::Status status) const
+{
+ if (status == QSettings::NoError || this->status == QSettings::NoError)
+ this->status = status;
+}
+
+void QSettingsPrivate::update()
+{
+ flush();
+ pendingChanges = false;
+}
+
+void QSettingsPrivate::requestUpdate()
+{
+ if (!pendingChanges) {
+ pendingChanges = true;
+#ifndef QT_NO_QOBJECT
+ Q_Q(QSettings);
+ QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
+#else
+ update();
+#endif
+ }
+}
+
+QStringList QSettingsPrivate::variantListToStringList(const QVariantList &l)
+{
+ QStringList result;
+ QVariantList::const_iterator it = l.constBegin();
+ for (; it != l.constEnd(); ++it)
+ result.append(variantToString(*it));
+ return result;
+}
+
+QVariant QSettingsPrivate::stringListToVariantList(const QStringList &l)
+{
+ QStringList outStringList = l;
+ for (int i = 0; i < outStringList.count(); ++i) {
+ const QString &str = outStringList.at(i);
+
+ if (str.startsWith(QLatin1Char('@'))) {
+ if (str.length() >= 2 && str.at(1) == QLatin1Char('@')) {
+ outStringList[i].remove(0, 1);
+ } else {
+ QVariantList variantList;
+ for (int j = 0; j < l.count(); ++j)
+ variantList.append(stringToVariant(l.at(j)));
+ return variantList;
+ }
+ }
+ }
+ return outStringList;
+}
+
+QString QSettingsPrivate::variantToString(const QVariant &v)
+{
+ QString result;
+
+ switch (v.type()) {
+ case QVariant::Invalid:
+ result = QLatin1String("@Invalid()");
+ break;
+
+ case QVariant::ByteArray: {
+ QByteArray a = v.toByteArray();
+ result = QLatin1String("@ByteArray(");
+ result += QString::fromLatin1(a.constData(), a.size());
+ result += QLatin1Char(')');
+ break;
+ }
+
+ case QVariant::String:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::Bool:
+ case QVariant::Double:
+ case QVariant::KeySequence: {
+ result = v.toString();
+ if (result.startsWith(QLatin1Char('@')))
+ result.prepend(QLatin1Char('@'));
+ break;
+ }
+#ifndef QT_NO_GEOM_VARIANT
+ case QVariant::Rect: {
+ QRect r = qvariant_cast<QRect>(v);
+ result += QLatin1String("@Rect(");
+ result += QString::number(r.x());
+ result += QLatin1Char(' ');
+ result += QString::number(r.y());
+ result += QLatin1Char(' ');
+ result += QString::number(r.width());
+ result += QLatin1Char(' ');
+ result += QString::number(r.height());
+ result += QLatin1Char(')');
+ break;
+ }
+ case QVariant::Size: {
+ QSize s = qvariant_cast<QSize>(v);
+ result += QLatin1String("@Size(");
+ result += QString::number(s.width());
+ result += QLatin1Char(' ');
+ result += QString::number(s.height());
+ result += QLatin1Char(')');
+ break;
+ }
+ case QVariant::Point: {
+ QPoint p = qvariant_cast<QPoint>(v);
+ result += QLatin1String("@Point(");
+ result += QString::number(p.x());
+ result += QLatin1Char(' ');
+ result += QString::number(p.y());
+ result += QLatin1Char(')');
+ break;
+ }
+#endif // !QT_NO_GEOM_VARIANT
+
+ default: {
+#ifndef QT_NO_DATASTREAM
+ QByteArray a;
+ {
+ QDataStream s(&a, QIODevice::WriteOnly);
+ s.setVersion(QDataStream::Qt_4_0);
+ s << v;
+ }
+
+ result = QLatin1String("@Variant(");
+ result += QString::fromLatin1(a.constData(), a.size());
+ result += QLatin1Char(')');
+#else
+ Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support");
+#endif
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+QVariant QSettingsPrivate::stringToVariant(const QString &s)
+{
+ if (s.startsWith(QLatin1Char('@'))) {
+ if (s.endsWith(QLatin1Char(')'))) {
+ if (s.startsWith(QLatin1String("@ByteArray("))) {
+ return QVariant(s.toLatin1().mid(11, s.size() - 12));
+ } else if (s.startsWith(QLatin1String("@Variant("))) {
+#ifndef QT_NO_DATASTREAM
+ QByteArray a(s.toLatin1().mid(9));
+ QDataStream stream(&a, QIODevice::ReadOnly);
+ stream.setVersion(QDataStream::Qt_4_0);
+ QVariant result;
+ stream >> result;
+ return result;
+#else
+ Q_ASSERT(!"QSettings: Cannot load custom types without QDataStream support");
+#endif
+#ifndef QT_NO_GEOM_VARIANT
+ } else if (s.startsWith(QLatin1String("@Rect("))) {
+ QStringList args = QSettingsPrivate::splitArgs(s, 5);
+ if (args.size() == 4)
+ return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()));
+ } else if (s.startsWith(QLatin1String("@Size("))) {
+ QStringList args = QSettingsPrivate::splitArgs(s, 5);
+ if (args.size() == 2)
+ return QVariant(QSize(args[0].toInt(), args[1].toInt()));
+ } else if (s.startsWith(QLatin1String("@Point("))) {
+ QStringList args = QSettingsPrivate::splitArgs(s, 6);
+ if (args.size() == 2)
+ return QVariant(QPoint(args[0].toInt(), args[1].toInt()));
+#endif
+ } else if (s == QLatin1String("@Invalid()")) {
+ return QVariant();
+ }
+
+ }
+ if (s.startsWith(QLatin1String("@@")))
+ return QVariant(s.mid(1));
+ }
+
+ return QVariant(s);
+}
+
+static const char hexDigits[] = "0123456789ABCDEF";
+
+void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result)
+{
+ result.reserve(result.length() + key.length() * 3 / 2);
+ for (int i = 0; i < key.size(); ++i) {
+ uint ch = key.at(i).unicode();
+
+ if (ch == '/') {
+ result += '\\';
+ } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
+ || ch == '_' || ch == '-' || ch == '.') {
+ result += (char)ch;
+ } else if (ch <= 0xFF) {
+ result += '%';
+ result += hexDigits[ch / 16];
+ result += hexDigits[ch % 16];
+ } else {
+ result += "%U";
+ QByteArray hexCode;
+ for (int i = 0; i < 4; ++i) {
+ hexCode.prepend(hexDigits[ch % 16]);
+ ch >>= 4;
+ }
+ result += hexCode;
+ }
+ }
+}
+
+bool QSettingsPrivate::iniUnescapedKey(const QByteArray &key, int from, int to, QString &result)
+{
+ bool lowercaseOnly = true;
+ int i = from;
+ result.reserve(result.length() + (to - from));
+ while (i < to) {
+ int ch = (uchar)key.at(i);
+
+ if (ch == '\\') {
+ result += QLatin1Char('/');
+ ++i;
+ continue;
+ }
+
+ if (ch != '%' || i == to - 1) {
+ if (uint(ch - 'A') <= 'Z' - 'A') // only for ASCII
+ lowercaseOnly = false;
+ result += QLatin1Char(ch);
+ ++i;
+ continue;
+ }
+
+ int numDigits = 2;
+ int firstDigitPos = i + 1;
+
+ ch = key.at(i + 1);
+ if (ch == 'U') {
+ ++firstDigitPos;
+ numDigits = 4;
+ }
+
+ if (firstDigitPos + numDigits > to) {
+ result += QLatin1Char('%');
+ // ### missing U
+ ++i;
+ continue;
+ }
+
+ bool ok;
+ ch = key.mid(firstDigitPos, numDigits).toInt(&ok, 16);
+ if (!ok) {
+ result += QLatin1Char('%');
+ // ### missing U
+ ++i;
+ continue;
+ }
+
+ QChar qch(ch);
+ if (qch.isUpper())
+ lowercaseOnly = false;
+ result += qch;
+ i = firstDigitPos + numDigits;
+ }
+ return lowercaseOnly;
+}
+
+void QSettingsPrivate::iniEscapedString(const QString &str, QByteArray &result, QTextCodec *codec)
+{
+ bool needsQuotes = false;
+ bool escapeNextIfDigit = false;
+ int i;
+ int startPos = result.size();
+
+ result.reserve(startPos + str.size() * 3 / 2);
+ for (i = 0; i < str.size(); ++i) {
+ uint ch = str.at(i).unicode();
+ if (ch == ';' || ch == ',' || ch == '=')
+ needsQuotes = true;
+
+ if (escapeNextIfDigit
+ && ((ch >= '0' && ch <= '9')
+ || (ch >= 'a' && ch <= 'f')
+ || (ch >= 'A' && ch <= 'F'))) {
+ result += "\\x";
+ result += QByteArray::number(ch, 16);
+ continue;
+ }
+
+ escapeNextIfDigit = false;
+
+ switch (ch) {
+ case '\0':
+ result += "\\0";
+ escapeNextIfDigit = true;
+ break;
+ case '\a':
+ result += "\\a";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ case '\v':
+ result += "\\v";
+ break;
+ case '"':
+ case '\\':
+ result += '\\';
+ result += (char)ch;
+ break;
+ default:
+ if (ch <= 0x1F || (ch >= 0x7F && !codec)) {
+ result += "\\x";
+ result += QByteArray::number(ch, 16);
+ escapeNextIfDigit = true;
+#ifndef QT_NO_TEXTCODEC
+ } else if (codec) {
+ // slow
+ result += codec->fromUnicode(str.at(i));
+#endif
+ } else {
+ result += (char)ch;
+ }
+ }
+ }
+
+ if (needsQuotes
+ || (startPos < result.size() && (result.at(startPos) == ' '
+ || result.at(result.size() - 1) == ' '))) {
+ result.insert(startPos, '"');
+ result += '"';
+ }
+}
+
+inline static void iniChopTrailingSpaces(QString &str)
+{
+ int n = str.size() - 1;
+ QChar ch;
+ while (n >= 0 && ((ch = str.at(n)) == QLatin1Char(' ') || ch == QLatin1Char('\t')))
+ str.truncate(n--);
+}
+
+void QSettingsPrivate::iniEscapedStringList(const QStringList &strs, QByteArray &result, QTextCodec *codec)
+{
+ if (strs.isEmpty()) {
+ /*
+ We need to distinguish between empty lists and one-item
+ lists that contain an empty string. Ideally, we'd have a
+ @EmptyList() symbol but that would break compatibility
+ with Qt 4.0. @Invalid() stands for QVariant(), and
+ QVariant().toStringList() returns an empty QStringList,
+ so we're in good shape.
+
+ ### Qt 5: Use a nicer syntax, e.g. @List, for variant lists
+ */
+ result += "@Invalid()";
+ } else {
+ for (int i = 0; i < strs.size(); ++i) {
+ if (i != 0)
+ result += ", ";
+ iniEscapedString(strs.at(i), result, codec);
+ }
+ }
+}
+
+bool QSettingsPrivate::iniUnescapedStringList(const QByteArray &str, int from, int to,
+ QString &stringResult, QStringList &stringListResult,
+ QTextCodec *codec)
+{
+ static const char escapeCodes[][2] =
+ {
+ { 'a', '\a' },
+ { 'b', '\b' },
+ { 'f', '\f' },
+ { 'n', '\n' },
+ { 'r', '\r' },
+ { 't', '\t' },
+ { 'v', '\v' },
+ { '"', '"' },
+ { '?', '?' },
+ { '\'', '\'' },
+ { '\\', '\\' }
+ };
+ static const int numEscapeCodes = sizeof(escapeCodes) / sizeof(escapeCodes[0]);
+
+ bool isStringList = false;
+ bool inQuotedString = false;
+ bool currentValueIsQuoted = false;
+ int escapeVal = 0;
+ int i = from;
+ char ch;
+
+StSkipSpaces:
+ while (i < to && ((ch = str.at(i)) == ' ' || ch == '\t'))
+ ++i;
+ // fallthrough
+
+StNormal:
+ while (i < to) {
+ switch (str.at(i)) {
+ case '\\':
+ ++i;
+ if (i >= to)
+ goto end;
+
+ ch = str.at(i++);
+ for (int j = 0; j < numEscapeCodes; ++j) {
+ if (ch == escapeCodes[j][0]) {
+ stringResult += QLatin1Char(escapeCodes[j][1]);
+ goto StNormal;
+ }
+ }
+
+ if (ch == 'x') {
+ escapeVal = 0;
+
+ if (i >= to)
+ goto end;
+
+ ch = str.at(i);
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
+ goto StHexEscape;
+ } else if (ch >= '0' && ch <= '7') {
+ escapeVal = ch - '0';
+ goto StOctEscape;
+ } else if (ch == '\n' || ch == '\r') {
+ if (i < to) {
+ char ch2 = str.at(i);
+ // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
+ if ((ch2 == '\n' || ch2 == '\r') && ch2 != ch)
+ ++i;
+ }
+ } else {
+ // the character is skipped
+ }
+ break;
+ case '"':
+ ++i;
+ currentValueIsQuoted = true;
+ inQuotedString = !inQuotedString;
+ if (!inQuotedString)
+ goto StSkipSpaces;
+ break;
+ case ',':
+ if (!inQuotedString) {
+ if (!currentValueIsQuoted)
+ iniChopTrailingSpaces(stringResult);
+ if (!isStringList) {
+ isStringList = true;
+ stringListResult.clear();
+ stringResult.squeeze();
+ }
+ stringListResult.append(stringResult);
+ stringResult.clear();
+ currentValueIsQuoted = false;
+ ++i;
+ goto StSkipSpaces;
+ }
+ // fallthrough
+ default: {
+ int j = i + 1;
+ while (j < to) {
+ ch = str.at(j);
+ if (ch == '\\' || ch == '"' || ch == ',')
+ break;
+ ++j;
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ if (codec) {
+ stringResult += codec->toUnicode(str.constData() + i, j - i);
+ } else
+#endif
+ {
+ int n = stringResult.size();
+ stringResult.resize(n + (j - i));
+ QChar *resultData = stringResult.data() + n;
+ for (int k = i; k < j; ++k)
+ *resultData++ = QLatin1Char(str.at(k));
+ }
+ i = j;
+ }
+ }
+ }
+ goto end;
+
+StHexEscape:
+ if (i >= to) {
+ stringResult += QChar(escapeVal);
+ goto end;
+ }
+
+ ch = str.at(i);
+ if (ch >= 'a')
+ ch -= 'a' - 'A';
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
+ escapeVal <<= 4;
+ escapeVal += strchr(hexDigits, ch) - hexDigits;
+ ++i;
+ goto StHexEscape;
+ } else {
+ stringResult += QChar(escapeVal);
+ goto StNormal;
+ }
+
+StOctEscape:
+ if (i >= to) {
+ stringResult += QChar(escapeVal);
+ goto end;
+ }
+
+ ch = str.at(i);
+ if (ch >= '0' && ch <= '7') {
+ escapeVal <<= 3;
+ escapeVal += ch - '0';
+ ++i;
+ goto StOctEscape;
+ } else {
+ stringResult += QChar(escapeVal);
+ goto StNormal;
+ }
+
+end:
+ if (!currentValueIsQuoted)
+ iniChopTrailingSpaces(stringResult);
+ if (isStringList)
+ stringListResult.append(stringResult);
+ return isStringList;
+}
+
+QStringList QSettingsPrivate::splitArgs(const QString &s, int idx)
+{
+ int l = s.length();
+ Q_ASSERT(l > 0);
+ Q_ASSERT(s.at(idx) == QLatin1Char('('));
+ Q_ASSERT(s.at(l - 1) == QLatin1Char(')'));
+
+ QStringList result;
+ QString item;
+
+ for (++idx; idx < l; ++idx) {
+ QChar c = s.at(idx);
+ if (c == QLatin1Char(')')) {
+ Q_ASSERT(idx == l - 1);
+ result.append(item);
+ } else if (c == QLatin1Char(' ')) {
+ result.append(item);
+ item.clear();
+ } else {
+ item.append(c);
+ }
+ }
+
+ return result;
+}
+
+// ************************************************************************
+// QConfFileSettingsPrivate
+
+/*
+ If we don't have the permission to read the file, returns false.
+ If the file doesn't exist, returns true.
+*/
+static bool checkAccess(const QString &name)
+{
+ QFileInfo fileInfo(name);
+
+ if (fileInfo.exists()) {
+ QFile file(name);
+ // if the file exists but we can't open it, report an error
+ return file.open(QFile::ReadOnly);
+ } else {
+ return true;
+ }
+}
+
+void QConfFileSettingsPrivate::initFormat()
+{
+ extension = (format == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini");
+ readFunc = 0;
+ writeFunc = 0;
+#if defined(Q_OS_MAC)
+ caseSensitivity = (format == QSettings::NativeFormat) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+#else
+ caseSensitivity = IniCaseSensitivity;
+#endif
+
+ if (format > QSettings::IniFormat) {
+ QMutexLocker locker(globalMutex());
+ const CustomFormatVector *customFormatVector = customFormatVectorFunc();
+
+ int i = (int)format - (int)QSettings::CustomFormat1;
+ if (i >= 0 && i < customFormatVector->size()) {
+ QConfFileCustomFormat info = customFormatVector->at(i);
+ extension = info.extension;
+ readFunc = info.readFunc;
+ writeFunc = info.writeFunc;
+ caseSensitivity = info.caseSensitivity;
+ }
+ }
+}
+
+void QConfFileSettingsPrivate::initAccess()
+{
+ bool readAccess = false;
+ if (confFiles[spec]) {
+ readAccess = checkAccess(confFiles[spec]->name);
+ if (format > QSettings::IniFormat) {
+ if (!readFunc)
+ readAccess = false;
+ }
+ }
+
+ if (!readAccess)
+ setStatus(QSettings::AccessError);
+
+ sync(); // loads the files the first time
+}
+
+#ifdef Q_OS_WIN
+static QString windowsConfigPath(int type)
+{
+ QString result;
+
+#ifndef QT_NO_QOBJECT
+ // We can't use QLibrary if there is QT_NO_QOBJECT is defined
+ // This only happens when bootstrapping qmake.
+#ifndef Q_OS_WINCE
+ QLibrary library(QLatin1String("shell32"));
+ QT_WA( {
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL);
+ GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathW");
+ if (SHGetSpecialFolderPath) {
+ TCHAR path[MAX_PATH];
+ SHGetSpecialFolderPath(0, path, type, FALSE);
+ result = QString::fromUtf16((ushort*)path);
+ }
+ } , {
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, char*, int, BOOL);
+ GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPathA");
+ if (SHGetSpecialFolderPath) {
+ char path[MAX_PATH];
+ SHGetSpecialFolderPath(0, path, type, FALSE);
+ result = QString::fromLocal8Bit(path);
+ }
+ } );
+#else
+ QLibrary library(QLatin1String("coredll"));
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPTSTR, int, BOOL);
+ GetSpecialFolderPath SHGetSpecialFolderPath = (GetSpecialFolderPath)library.resolve("SHGetSpecialFolderPath");
+ if (SHGetSpecialFolderPath) {
+ wchar_t path[MAX_PATH];
+ SHGetSpecialFolderPath(0, path, type, FALSE);
+ result = QString::fromUtf16((ushort*)path);
+ }
+#endif // Q_OS_WINCE
+
+#endif // QT_NO_QOBJECT
+
+ if (result.isEmpty()) {
+ switch (type) {
+#ifndef Q_OS_WINCE
+ case CSIDL_COMMON_APPDATA:
+ result = QLatin1String("C:\\temp\\qt-common");
+ break;
+ case CSIDL_APPDATA:
+ result = QLatin1String("C:\\temp\\qt-user");
+ break;
+#else
+ case CSIDL_COMMON_APPDATA:
+ result = QLatin1String("\\Temp\\qt-common");
+ break;
+ case CSIDL_APPDATA:
+ result = QLatin1String("\\Temp\\qt-user");
+ break;
+#endif
+ default:
+ ;
+ }
+ }
+
+ return result;
+}
+#endif // Q_OS_WIN
+
+static inline int pathHashKey(QSettings::Format format, QSettings::Scope scope)
+{
+ return int((uint(format) << 1) | uint(scope == QSettings::SystemScope));
+}
+
+static QString getPath(QSettings::Format format, QSettings::Scope scope)
+{
+ Q_ASSERT((int)QSettings::NativeFormat == 0);
+ Q_ASSERT((int)QSettings::IniFormat == 1);
+
+ QString homePath = QDir::homePath();
+ QString systemPath;
+
+ globalMutex()->lock();
+ PathHash *pathHash = pathHashFunc();
+ bool loadSystemPath = pathHash->isEmpty();
+ globalMutex()->unlock();
+
+ if (loadSystemPath) {
+ /*
+ QLibraryInfo::location() uses QSettings, so in order to
+ avoid a dead-lock, we can't hold the global mutex while
+ calling it.
+ */
+ systemPath = QLibraryInfo::location(QLibraryInfo::SettingsPath);
+ systemPath += QLatin1Char('/');
+ }
+
+ QMutexLocker locker(globalMutex());
+ if (pathHash->isEmpty()) {
+ /*
+ Lazy initialization of pathHash. We initialize the
+ IniFormat paths and (on Unix) the NativeFormat paths.
+ (The NativeFormat paths are not configurable for the
+ Windows registry and the Mac CFPreferences.)
+ */
+#ifdef Q_OS_WIN
+ pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope),
+ windowsConfigPath(CSIDL_APPDATA) + QDir::separator());
+ pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope),
+ windowsConfigPath(CSIDL_COMMON_APPDATA) + QDir::separator());
+#else
+ QString userPath;
+ char *env = getenv("XDG_CONFIG_HOME");
+ if (env == 0) {
+ userPath = homePath;
+ userPath += QLatin1Char('/');
+#ifdef Q_WS_QWS
+ userPath += QLatin1String("Settings");
+#else
+ userPath += QLatin1String(".config");
+#endif
+ } else if (*env == '/') {
+ userPath = QLatin1String(env);
+ } else {
+ userPath = homePath;
+ userPath += QLatin1Char('/');
+ userPath += QLatin1String(env);
+ }
+ userPath += QLatin1Char('/');
+
+ pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::UserScope), userPath);
+ pathHash->insert(pathHashKey(QSettings::IniFormat, QSettings::SystemScope), systemPath);
+#ifndef Q_OS_MAC
+ pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::UserScope), userPath);
+ pathHash->insert(pathHashKey(QSettings::NativeFormat, QSettings::SystemScope), systemPath);
+#endif
+#endif
+ }
+
+ QString result = pathHash->value(pathHashKey(format, scope));
+ if (!result.isEmpty())
+ return result;
+
+ // fall back on INI path
+ return pathHash->value(pathHashKey(QSettings::IniFormat, scope));
+}
+
+QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format,
+ QSettings::Scope scope,
+ const QString &organization,
+ const QString &application)
+ : QSettingsPrivate(format, scope, organization, application),
+ nextPosition(0x40000000) // big positive number
+{
+ int i;
+ initFormat();
+
+ for (i = 0; i < NumConfFiles; ++i)
+ confFiles[i] = 0;
+
+ QString org = organization;
+ if (org.isEmpty()) {
+ setStatus(QSettings::AccessError);
+ org = QLatin1String("Unknown Organization");
+ }
+
+ QString appFile = org + QDir::separator() + application + extension;
+ QString orgFile = org + extension;
+
+ 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);
+ }
+
+ 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);
+
+ for (i = 0; i < NumConfFiles; ++i) {
+ if (confFiles[i]) {
+ spec = i;
+ break;
+ }
+ }
+
+ initAccess();
+}
+
+QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName,
+ QSettings::Format format)
+ : QSettingsPrivate(format),
+ nextPosition(0x40000000) // big positive number
+{
+ initFormat();
+
+ confFiles[0] = QConfFile::fromName(fileName, true);
+ for (int i = 1; i < NumConfFiles; ++i)
+ confFiles[i] = 0;
+
+ initAccess();
+}
+
+QConfFileSettingsPrivate::~QConfFileSettingsPrivate()
+{
+ QMutexLocker locker(globalMutex());
+ ConfFileHash *usedHash = usedHashFunc();
+ ConfFileCache *unusedCache = unusedCacheFunc();
+
+ 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];
+ } else if (unusedCache) {
+ // compute a better size?
+ unusedCache->insert(confFiles[i]->name, confFiles[i],
+ 10 + (confFiles[i]->originalKeys.size() / 4));
+ }
+ }
+ }
+}
+
+void QConfFileSettingsPrivate::remove(const QString &key)
+{
+ QConfFile *confFile = confFiles[spec];
+ if (!confFile)
+ return;
+
+ QSettingsKey theKey(key, caseSensitivity);
+ QSettingsKey prefix(key + QLatin1Char('/'), caseSensitivity);
+ QMutexLocker locker(&confFile->mutex);
+
+ ensureSectionParsed(confFile, theKey);
+ ensureSectionParsed(confFile, prefix);
+
+ ParsedSettingsMap::iterator i = confFile->addedKeys.lowerBound(prefix);
+ while (i != confFile->addedKeys.end() && i.key().startsWith(prefix))
+ i = confFile->addedKeys.erase(i);
+ confFile->addedKeys.remove(theKey);
+
+ ParsedSettingsMap::const_iterator j = const_cast<const ParsedSettingsMap *>(&confFile->originalKeys)->lowerBound(prefix);
+ while (j != confFile->originalKeys.constEnd() && j.key().startsWith(prefix)) {
+ confFile->removedKeys.insert(j.key(), QVariant());
+ ++j;
+ }
+ if (confFile->originalKeys.contains(theKey))
+ confFile->removedKeys.insert(theKey, QVariant());
+}
+
+void QConfFileSettingsPrivate::set(const QString &key, const QVariant &value)
+{
+ QConfFile *confFile = confFiles[spec];
+ if (!confFile)
+ return;
+
+ QSettingsKey theKey(key, caseSensitivity, nextPosition++);
+ QMutexLocker locker(&confFile->mutex);
+ confFile->removedKeys.remove(theKey);
+ confFile->addedKeys.insert(theKey, value);
+}
+
+bool QConfFileSettingsPrivate::get(const QString &key, QVariant *value) const
+{
+ QSettingsKey theKey(key, caseSensitivity);
+ ParsedSettingsMap::const_iterator j;
+ bool found = false;
+
+ for (int i = 0; i < NumConfFiles; ++i) {
+ if (QConfFile *confFile = confFiles[i]) {
+ QMutexLocker locker(&confFile->mutex);
+
+ if (!confFile->addedKeys.isEmpty()) {
+ j = confFile->addedKeys.constFind(theKey);
+ found = (j != confFile->addedKeys.constEnd());
+ }
+ if (!found) {
+ ensureSectionParsed(confFile, theKey);
+ j = confFile->originalKeys.constFind(theKey);
+ found = (j != confFile->originalKeys.constEnd()
+ && !confFile->removedKeys.contains(theKey));
+ }
+
+ if (found && value)
+ *value = *j;
+
+ if (found)
+ return true;
+ if (!fallbacks)
+ break;
+ }
+ }
+ return false;
+}
+
+QStringList QConfFileSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
+{
+ QMap<QString, QString> result;
+ ParsedSettingsMap::const_iterator j;
+
+ QSettingsKey thePrefix(prefix, caseSensitivity);
+ int startPos = prefix.size();
+
+ for (int i = 0; i < NumConfFiles; ++i) {
+ if (QConfFile *confFile = confFiles[i]) {
+ QMutexLocker locker(&confFile->mutex);
+
+ if (thePrefix.isEmpty()) {
+ ensureAllSectionsParsed(confFile);
+ } else {
+ ensureSectionParsed(confFile, thePrefix);
+ }
+
+ j = const_cast<const ParsedSettingsMap *>(
+ &confFile->originalKeys)->lowerBound( thePrefix);
+ while (j != confFile->originalKeys.constEnd() && j.key().startsWith(thePrefix)) {
+ if (!confFile->removedKeys.contains(j.key()))
+ processChild(j.key().originalCaseKey().mid(startPos), spec, result);
+ ++j;
+ }
+
+ j = const_cast<const ParsedSettingsMap *>(
+ &confFile->addedKeys)->lowerBound(thePrefix);
+ while (j != confFile->addedKeys.constEnd() && j.key().startsWith(thePrefix)) {
+ processChild(j.key().originalCaseKey().mid(startPos), spec, result);
+ ++j;
+ }
+
+ if (!fallbacks)
+ break;
+ }
+ }
+ return result.keys();
+}
+
+void QConfFileSettingsPrivate::clear()
+{
+ QConfFile *confFile = confFiles[spec];
+ if (!confFile)
+ return;
+
+ QMutexLocker locker(&confFile->mutex);
+ ensureAllSectionsParsed(confFile);
+ confFile->addedKeys.clear();
+ confFile->removedKeys = confFile->originalKeys;
+}
+
+void QConfFileSettingsPrivate::sync()
+{
+ // people probably won't be checking the status a whole lot, so in case of
+ // error we just try to go on and make the best of it
+
+ for (int i = 0; i < NumConfFiles; ++i) {
+ QConfFile *confFile = confFiles[i];
+ if (confFile) {
+ QMutexLocker locker(&confFile->mutex);
+ syncConfFile(i);
+ }
+ }
+}
+
+void QConfFileSettingsPrivate::flush()
+{
+ sync();
+}
+
+QString QConfFileSettingsPrivate::fileName() const
+{
+ QConfFile *confFile = confFiles[spec];
+ if (!confFile)
+ return QString();
+ return confFile->name;
+}
+
+bool QConfFileSettingsPrivate::isWritable() const
+{
+ if (format > QSettings::IniFormat && !writeFunc)
+ return false;
+
+ QConfFile *confFile = confFiles[spec];
+ if (!confFile)
+ return false;
+
+ return confFile->isWritable();
+}
+
+void QConfFileSettingsPrivate::syncConfFile(int confFileNo)
+{
+ QConfFile *confFile = confFiles[confFileNo];
+ bool readOnly = confFile->addedKeys.isEmpty() && confFile->removedKeys.isEmpty();
+ bool ok;
+
+ /*
+ We can often optimize the read-only case, if the file on disk
+ hasn't changed.
+ */
+ if (readOnly) {
+ QFileInfo fileInfo(confFile->name);
+ if (confFile->size == fileInfo.size() && confFile->timeStamp == fileInfo.lastModified())
+ return;
+ }
+
+ /*
+ Open the configuration file and try to use it using a named
+ semaphore on Windows and an advisory lock on Unix-based
+ systems. This protect us against other QSettings instances
+ trying to access the same file from other threads or
+ processes.
+
+ As it stands now, the locking mechanism doesn't work for
+ .plist files.
+ */
+ QFile file(confFile->name);
+ bool createFile = !file.exists();
+ if (!readOnly && confFile->isWritable())
+ file.open(QFile::ReadWrite);
+ if (!file.isOpen())
+ file.open(QFile::ReadOnly);
+
+#ifdef Q_OS_WIN
+ HANDLE readSemaphore = 0;
+ HANDLE writeSemaphore = 0;
+ static const int FileLockSemMax = 50;
+ int numReadLocks = readOnly ? 1 : FileLockSemMax;
+
+ if (file.isOpen()) {
+ // Acquire the write lock if we will be writing
+ if (!readOnly) {
+ QString writeSemName = QLatin1String("QSettingsWriteSem ");
+ writeSemName.append(file.fileName());
+
+ QT_WA( {
+ writeSemaphore = CreateSemaphoreW(0, 1, 1, reinterpret_cast<const wchar_t *>(writeSemName.utf16()));
+ } , {
+ writeSemaphore = CreateSemaphoreA(0, 1, 1, writeSemName.toLocal8Bit());
+ } );
+
+ if (writeSemaphore) {
+ WaitForSingleObject(writeSemaphore, INFINITE);
+ } else {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+ }
+
+ // Acquire all the read locks if we will be writing, to make sure nobody
+ // reads while we're writing. If we are only reading, acquire a single
+ // read lock.
+ QString readSemName(QLatin1String("QSettingsReadSem "));
+ readSemName.append(file.fileName());
+
+ QT_WA( {
+ readSemaphore = CreateSemaphoreW(0, FileLockSemMax, FileLockSemMax, reinterpret_cast<const wchar_t *>(readSemName.utf16()));
+ } , {
+ readSemaphore = CreateSemaphoreA(0, FileLockSemMax, FileLockSemMax, readSemName.toLocal8Bit());
+ } );
+
+ if (readSemaphore) {
+ for (int i = 0; i < numReadLocks; ++i)
+ WaitForSingleObject(readSemaphore, INFINITE);
+ } else {
+ setStatus(QSettings::AccessError);
+ if (writeSemaphore != 0) {
+ ReleaseSemaphore(writeSemaphore, 1, 0);
+ CloseHandle(writeSemaphore);
+ }
+ return;
+ }
+ }
+#else
+ if (file.isOpen())
+ unixLock(file.handle(), readOnly ? F_RDLCK : F_WRLCK);
+#endif
+
+ // If we have created the file, apply the file perms
+ if (file.isOpen()) {
+ if (createFile) {
+ QFile::Permissions perms = file.permissions() | QFile::ReadOwner | QFile::WriteOwner;
+ if (!confFile->userPerms)
+ perms |= QFile::ReadGroup | QFile::ReadOther;
+ file.setPermissions(perms);
+ }
+ }
+
+ /*
+ We hold the lock. Let's reread the file if it has changed
+ since last time we read it.
+ */
+ QFileInfo fileInfo(confFile->name);
+ bool mustReadFile = true;
+
+ if (!readOnly)
+ mustReadFile = (confFile->size != fileInfo.size()
+ || (confFile->size != 0 && confFile->timeStamp != fileInfo.lastModified()));
+
+ if (mustReadFile) {
+ confFile->unparsedIniSections.clear();
+ confFile->originalKeys.clear();
+
+ /*
+ Files that we can't read (because of permissions or
+ because they don't exist) are treated as empty files.
+ */
+ if (file.isReadable() && fileInfo.size() != 0) {
+#ifdef Q_OS_MAC
+ if (format == QSettings::NativeFormat) {
+ ok = readPlistFile(confFile->name, &confFile->originalKeys);
+ } else
+#endif
+ {
+ if (format <= QSettings::IniFormat) {
+ QByteArray data = file.readAll();
+ ok = readIniFile(data, &confFile->unparsedIniSections);
+ } else {
+ if (readFunc) {
+ QSettings::SettingsMap tempNewKeys;
+ ok = readFunc(file, tempNewKeys);
+
+ if (ok) {
+ QSettings::SettingsMap::const_iterator i = tempNewKeys.constBegin();
+ while (i != tempNewKeys.constEnd()) {
+ confFile->originalKeys.insert(QSettingsKey(i.key(),
+ caseSensitivity),
+ i.value());
+ ++i;
+ }
+ }
+ } else {
+ ok = false;
+ }
+ }
+ }
+
+ if (!ok)
+ setStatus(QSettings::FormatError);
+ }
+
+ confFile->size = fileInfo.size();
+ confFile->timeStamp = fileInfo.lastModified();
+ }
+
+ /*
+ We also need to save the file. We still hold the file lock,
+ so everything is under control.
+ */
+ if (!readOnly) {
+ ensureAllSectionsParsed(confFile);
+ ParsedSettingsMap mergedKeys = confFile->mergedKeyMap();
+
+ if (file.isWritable()) {
+#ifdef Q_OS_MAC
+ if (format == QSettings::NativeFormat) {
+ ok = writePlistFile(confFile->name, mergedKeys);
+ } else
+#endif
+ {
+ file.seek(0);
+ file.resize(0);
+
+ if (format <= QSettings::IniFormat) {
+ ok = writeIniFile(file, mergedKeys);
+ if (!ok) {
+ // try to restore old data; might work if the disk was full and the new data
+ // was larger than the old data
+ file.seek(0);
+ file.resize(0);
+ writeIniFile(file, confFile->originalKeys);
+ }
+ } else {
+ if (writeFunc) {
+ QSettings::SettingsMap tempOriginalKeys;
+
+ ParsedSettingsMap::const_iterator i = mergedKeys.constBegin();
+ while (i != mergedKeys.constEnd()) {
+ tempOriginalKeys.insert(i.key(), i.value());
+ ++i;
+ }
+ ok = writeFunc(file, tempOriginalKeys);
+ } else {
+ ok = false;
+ }
+ }
+ }
+ } else {
+ ok = false;
+ }
+
+ if (ok) {
+ confFile->unparsedIniSections.clear();
+ confFile->originalKeys = mergedKeys;
+ confFile->addedKeys.clear();
+ confFile->removedKeys.clear();
+
+ QFileInfo fileInfo(confFile->name);
+ confFile->size = fileInfo.size();
+ confFile->timeStamp = fileInfo.lastModified();
+ } else {
+ setStatus(QSettings::AccessError);
+ }
+ }
+
+ /*
+ Release the file lock.
+ */
+#ifdef Q_OS_WIN
+ if (readSemaphore != 0) {
+ ReleaseSemaphore(readSemaphore, numReadLocks, 0);
+ CloseHandle(readSemaphore);
+ }
+ if (writeSemaphore != 0) {
+ ReleaseSemaphore(writeSemaphore, 1, 0);
+ CloseHandle(writeSemaphore);
+ }
+#endif
+}
+
+enum { Space = 0x1, Special = 0x2 };
+
+static const char charTraits[256] =
+{
+ // Space: '\t', '\n', '\r', ' '
+ // Special: '\n', '\r', '"', ';', '=', '\\'
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Space, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, Special, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+bool QConfFileSettingsPrivate::readIniLine(const QByteArray &data, int &dataPos,
+ int &lineStart, int &lineLen, int &equalsPos)
+{
+ int dataLen = data.length();
+ bool inQuotes = false;
+
+ equalsPos = -1;
+
+ lineStart = dataPos;
+ while (lineStart < dataLen && (charTraits[uint(uchar(data.at(lineStart)))] & Space))
+ ++lineStart;
+
+ int i = lineStart;
+ while (i < dataLen) {
+ while (!(charTraits[uint(uchar(data.at(i)))] & Special)) {
+ if (++i == dataLen)
+ goto break_out_of_outer_loop;
+ }
+
+ char ch = data.at(i++);
+ if (ch == '=') {
+ if (!inQuotes && equalsPos == -1)
+ equalsPos = i - 1;
+ } else if (ch == '\n' || ch == '\r') {
+ if (i == lineStart + 1) {
+ ++lineStart;
+ } else if (!inQuotes) {
+ --i;
+ goto break_out_of_outer_loop;
+ }
+ } else if (ch == '\\') {
+ if (i < dataLen) {
+ char ch = data.at(i++);
+ if (i < dataLen) {
+ char ch2 = data.at(i);
+ // \n, \r, \r\n, and \n\r are legitimate line terminators in INI files
+ if ((ch == '\n' && ch2 == '\r') || (ch == '\r' && ch2 == '\n'))
+ ++i;
+ }
+ }
+ } else if (ch == '"') {
+ inQuotes = !inQuotes;
+ } else {
+ Q_ASSERT(ch == ';');
+
+ if (i == lineStart + 1) {
+ char ch;
+ while (i < dataLen && ((ch = data.at(i) != '\n') && ch != '\r'))
+ ++i;
+ lineStart = i;
+ } else if (!inQuotes) {
+ --i;
+ goto break_out_of_outer_loop;
+ }
+ }
+ }
+
+break_out_of_outer_loop:
+ dataPos = i;
+ lineLen = i - lineStart;
+ return lineLen > 0;
+}
+
+/*
+ Returns false on parse error. However, as many keys are read as
+ possible, so if the user doesn't check the status he will get the
+ most out of the file anyway.
+*/
+bool QConfFileSettingsPrivate::readIniFile(const QByteArray &data,
+ UnparsedSettingsMap *unparsedIniSections)
+{
+#define FLUSH_CURRENT_SECTION() \
+ { \
+ QByteArray &sectionData = (*unparsedIniSections)[QSettingsKey(currentSection, \
+ IniCaseSensitivity, \
+ sectionPosition)]; \
+ if (!sectionData.isEmpty()) \
+ sectionData.append('\n'); \
+ sectionData += data.mid(currentSectionStart, lineStart - currentSectionStart); \
+ sectionPosition = ++position; \
+ }
+
+ QString currentSection;
+ int currentSectionStart = 0;
+ int dataPos = 0;
+ int lineStart;
+ int lineLen;
+ int equalsPos;
+ int position = 0;
+ int sectionPosition = 0;
+ bool ok = true;
+
+ while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
+ char ch = data.at(lineStart);
+ if (ch == '[') {
+ FLUSH_CURRENT_SECTION();
+
+ // this is a section
+ QByteArray iniSection;
+ int idx = data.indexOf(']', lineStart);
+ if (idx == -1 || idx >= lineStart + lineLen) {
+ ok = false;
+ iniSection = data.mid(lineStart + 1, lineLen - 1);
+ } else {
+ iniSection = data.mid(lineStart + 1, idx - lineStart - 1);
+ }
+
+ iniSection = iniSection.trimmed();
+
+ if (qstricmp(iniSection, "general") == 0) {
+ currentSection.clear();
+ } else {
+ if (qstricmp(iniSection, "%general") == 0) {
+ currentSection = QLatin1String(iniSection.constData() + 1);
+ } else {
+ currentSection.clear();
+ iniUnescapedKey(iniSection, 0, iniSection.size(), currentSection);
+ }
+ currentSection += QLatin1Char('/');
+ }
+ currentSectionStart = dataPos;
+ }
+ ++position;
+ }
+
+ Q_ASSERT(lineStart == data.length());
+ FLUSH_CURRENT_SECTION();
+
+ return ok;
+
+#undef FLUSH_CURRENT_SECTION
+}
+
+bool QConfFileSettingsPrivate::readIniSection(const QSettingsKey &section, const QByteArray &data,
+ ParsedSettingsMap *settingsMap, QTextCodec *codec)
+{
+ QStringList strListValue;
+ bool sectionIsLowercase = (section == section.originalCaseKey());
+ int equalsPos;
+
+ bool ok = true;
+ int dataPos = 0;
+ int lineStart;
+ int lineLen;
+ int position = section.originalKeyPosition();
+
+ while (readIniLine(data, dataPos, lineStart, lineLen, equalsPos)) {
+ char ch = data.at(lineStart);
+ Q_ASSERT(ch != '[');
+
+ if (equalsPos == -1) {
+ if (ch != ';')
+ ok = false;
+ continue;
+ }
+
+ int keyEnd = equalsPos;
+ while (keyEnd > lineStart && ((ch = data.at(keyEnd - 1)) == ' ' || ch == '\t'))
+ --keyEnd;
+ int valueStart = equalsPos + 1;
+
+ QString key = section.originalCaseKey();
+ bool keyIsLowercase = (iniUnescapedKey(data, lineStart, keyEnd, key) && sectionIsLowercase);
+
+ QString strValue;
+ strValue.reserve(lineLen - (valueStart - lineStart));
+ bool isStringList = iniUnescapedStringList(data, valueStart, lineStart + lineLen,
+ strValue, strListValue, codec);
+ QVariant variant;
+ if (isStringList) {
+ variant = stringListToVariantList(strListValue);
+ } else {
+ variant = stringToVariant(strValue);
+ }
+
+ /*
+ We try to avoid the expensive toLower() call in
+ QSettingsKey by passing Qt::CaseSensitive when the
+ key is already in lowercase.
+ */
+ settingsMap->insert(QSettingsKey(key, keyIsLowercase ? Qt::CaseSensitive
+ : IniCaseSensitivity,
+ position),
+ variant);
+ ++position;
+ }
+
+ return ok;
+}
+
+class QSettingsIniKey : public QString
+{
+public:
+ inline QSettingsIniKey() : position(-1) {}
+ inline QSettingsIniKey(const QString &str, int pos = -1) : QString(str), position(pos) {}
+
+ int position;
+};
+
+static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2)
+{
+ if (k1.position != k2.position)
+ return k1.position < k2.position;
+ return static_cast<const QString &>(k1) < static_cast<const QString &>(k2);
+}
+
+typedef QMap<QSettingsIniKey, QVariant> IniKeyMap;
+
+struct QSettingsIniSection
+{
+ int position;
+ IniKeyMap keyMap;
+
+ inline QSettingsIniSection() : position(-1) {}
+};
+
+typedef QMap<QString, QSettingsIniSection> IniMap;
+
+/*
+ This would be more straightforward if we didn't try to remember the original
+ key order in the .ini file, but we do.
+*/
+bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map)
+{
+ IniMap iniMap;
+ IniMap::const_iterator i;
+
+#ifdef Q_OS_WIN
+ const char * const eol = "\r\n";
+#else
+ const char eol = '\n';
+#endif
+
+ for (ParsedSettingsMap::const_iterator j = map.constBegin(); j != map.constEnd(); ++j) {
+ QString section;
+ QSettingsIniKey key(j.key().originalCaseKey(), j.key().originalKeyPosition());
+ int slashPos;
+
+ if ((slashPos = key.indexOf(QLatin1Char('/'))) != -1) {
+ section = key.left(slashPos);
+ key.remove(0, slashPos + 1);
+ }
+
+ QSettingsIniSection &iniSection = iniMap[section];
+
+ // -1 means infinity
+ if (uint(key.position) < uint(iniSection.position))
+ iniSection.position = key.position;
+ iniSection.keyMap[key] = j.value();
+ }
+
+ const int sectionCount = iniMap.size();
+ QVector<QSettingsIniKey> sections;
+ sections.reserve(sectionCount);
+ for (i = iniMap.constBegin(); i != iniMap.constEnd(); ++i)
+ sections.append(QSettingsIniKey(i.key(), i.value().position));
+ qSort(sections);
+
+ bool writeError = false;
+ for (int j = 0; !writeError && j < sectionCount; ++j) {
+ i = iniMap.constFind(sections.at(j));
+ Q_ASSERT(i != iniMap.constEnd());
+
+ QByteArray realSection;
+
+ iniEscapedKey(i.key(), realSection);
+
+ if (realSection.isEmpty()) {
+ realSection = "[General]";
+ } else if (qstricmp(realSection, "general") == 0) {
+ realSection = "[%General]";
+ } else {
+ realSection.prepend('[');
+ realSection.append(']');
+ }
+
+ if (j != 0)
+ realSection.prepend(eol);
+ realSection += eol;
+
+ device.write(realSection);
+
+ const IniKeyMap &ents = i.value().keyMap;
+ for (IniKeyMap::const_iterator j = ents.constBegin(); j != ents.constEnd(); ++j) {
+ QByteArray block;
+ iniEscapedKey(j.key(), block);
+ block += '=';
+
+ const QVariant &value = j.value();
+
+ /*
+ The size() != 1 trick is necessary because
+ QVariant(QString("foo")).toList() returns an empty
+ list, not a list containing "foo".
+ */
+ if (value.type() == QVariant::StringList
+ || (value.type() == QVariant::List && value.toList().size() != 1)) {
+ iniEscapedStringList(variantListToStringList(value.toList()), block, iniCodec);
+ } else {
+ iniEscapedString(variantToString(value), block, iniCodec);
+ }
+ block += eol;
+ if (device.write(block) == -1) {
+ writeError = true;
+ break;
+ }
+ }
+ }
+ return !writeError;
+}
+
+void QConfFileSettingsPrivate::ensureAllSectionsParsed(QConfFile *confFile) const
+{
+ UnparsedSettingsMap::const_iterator i = confFile->unparsedIniSections.constBegin();
+ const UnparsedSettingsMap::const_iterator end = confFile->unparsedIniSections.constEnd();
+
+ for (; i != end; ++i) {
+ if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec))
+ setStatus(QSettings::FormatError);
+ }
+ confFile->unparsedIniSections.clear();
+}
+
+void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
+ const QSettingsKey &key) const
+{
+ if (confFile->unparsedIniSections.isEmpty())
+ return;
+
+ UnparsedSettingsMap::iterator i;
+
+ int indexOfSlash = key.indexOf(QLatin1Char('/'));
+ if (indexOfSlash != -1) {
+ i = confFile->unparsedIniSections.upperBound(key);
+ if (i == confFile->unparsedIniSections.begin())
+ return;
+ --i;
+ if (i.key().isEmpty() || !key.startsWith(i.key()))
+ return;
+ } else {
+ i = confFile->unparsedIniSections.begin();
+ if (i == confFile->unparsedIniSections.end() || !i.key().isEmpty())
+ return;
+ }
+
+ if (!QConfFileSettingsPrivate::readIniSection(i.key(), i.value(), &confFile->originalKeys, iniCodec))
+ setStatus(QSettings::FormatError);
+ confFile->unparsedIniSections.erase(i);
+}
+
+/*!
+ \class QSettings
+ \brief The QSettings class provides persistent platform-independent application settings.
+
+ \ingroup io
+ \ingroup misc
+ \mainclass
+ \reentrant
+
+ Users normally expect an application to remember its settings
+ (window sizes and positions, options, etc.) across sessions. This
+ information is often stored in the system registry on Windows,
+ and in XML preferences files on Mac OS X. On Unix systems, in the
+ absence of a standard, many applications (including the KDE
+ applications) use INI text files.
+
+ QSettings is an abstraction around these technologies, enabling
+ you to save and restore application settings in a portable
+ manner. It also supports \l{registerFormat()}{custom storage
+ formats}.
+
+ QSettings's API is based on QVariant, allowing you to save
+ most value-based types, such as QString, QRect, and QImage,
+ with the minimum of effort.
+
+ If all you need is a non-persistent memory-based structure,
+ consider using QMap<QString, QVariant> instead.
+
+ \tableofcontents section1
+
+ \section1 Basic Usage
+
+ When creating a QSettings object, you must pass the name of your
+ company or organization as well as the name of your application.
+ For example, if your product is called Star Runner and your
+ company is called MySoft, you would construct the QSettings
+ object as follows:
+
+ \snippet doc/src/snippets/settings/settings.cpp 0
+
+ QSettings objects can be created either on the stack or on
+ the heap (i.e. using \c new). Constructing and destroying a
+ QSettings object is very fast.
+
+ If you use QSettings from many places in your application, you
+ might want to specify the organization name and the application
+ name using QCoreApplication::setOrganizationName() and
+ QCoreApplication::setApplicationName(), and then use the default
+ QSettings constructor:
+
+ \snippet doc/src/snippets/settings/settings.cpp 1
+ \snippet doc/src/snippets/settings/settings.cpp 2
+ \snippet doc/src/snippets/settings/settings.cpp 3
+ \dots
+ \snippet doc/src/snippets/settings/settings.cpp 4
+
+ (Here, we also specify the organization's Internet domain. When
+ the Internet domain is set, it is used on Mac OS X instead of the
+ organization name, since Mac OS X applications conventionally use
+ Internet domains to identify themselves. If no domain is set, a
+ fake domain is derived from the organization name. See the
+ \l{Platform-Specific Notes} below for details.)
+
+ QSettings stores settings. Each setting consists of a QString
+ that specifies the setting's name (the \e key) and a QVariant
+ that stores the data associated with the key. To write a setting,
+ use setValue(). For example:
+
+ \snippet doc/src/snippets/settings/settings.cpp 5
+
+ If there already exists a setting with the same key, the existing
+ value is overwritten by the new value. For efficiency, the
+ changes may not be saved to permanent storage immediately. (You
+ can always call sync() to commit your changes.)
+
+ You can get a setting's value back using value():
+
+ \snippet doc/src/snippets/settings/settings.cpp 6
+
+ If there is no setting with the specified name, QSettings
+ returns a null QVariant (which can be converted to the integer 0).
+ You can specify another default value by passing a second
+ argument to value():
+
+ \snippet doc/src/snippets/settings/settings.cpp 7
+
+ To test whether a given key exists, call contains(). To remove
+ the setting associated with a key, call remove(). To obtain the
+ list of all keys, call allKeys(). To remove all keys, call
+ clear().
+
+ \section1 QVariant and GUI Types
+
+ Because QVariant is part of the \l QtCore library, it cannot provide
+ conversion functions to data types such as QColor, QImage, and
+ QPixmap, which are part of \l QtGui. In other words, there is no
+ \c toColor(), \c toImage(), or \c toPixmap() functions in QVariant.
+
+ Instead, you can use the QVariant::value() or the qVariantValue()
+ template function. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 0
+
+ The inverse conversion (e.g., from QColor to QVariant) is
+ automatic for all data types supported by QVariant, including
+ GUI-related types:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 1
+
+ Custom types registered using qRegisterMetaType() and
+ qRegisterMetaTypeStreamOperators() can be stored using QSettings.
+
+ \section1 Key Syntax
+
+ Setting keys can contain any Unicode characters. The Windows
+ registry and INI files use case-insensitive keys, whereas the
+ Carbon Preferences API on Mac OS X uses case-sensitive keys. To
+ avoid portability problems, follow these two simple rules:
+
+ \list 1
+ \o Always refer to the same key using the same case. For example,
+ if you refer to a key as "text fonts" in one place in your
+ code, don't refer to it as "Text Fonts" somewhere else.
+
+ \o Avoid key names that are identical except for the case. For
+ example, if you have a key called "MainWindow", don't try to
+ save another key as "mainwindow".
+
+ \o Do not use slashes ('/' and '\\') in key names; the
+ backslash character is used to separate sub keys (see below). On
+ windows '\\' are converted by QSettings to '/', which makes
+ them identical.
+ \endlist
+
+ You can form hierarchical keys using the '/' character as a
+ separator, similar to Unix file paths. For example:
+
+ \snippet doc/src/snippets/settings/settings.cpp 8
+ \snippet doc/src/snippets/settings/settings.cpp 9
+ \snippet doc/src/snippets/settings/settings.cpp 10
+
+ If you want to save or restore many settings with the same
+ prefix, you can specify the prefix using beginGroup() and call
+ endGroup() at the end. Here's the same example again, but this
+ time using the group mechanism:
+
+ \snippet doc/src/snippets/settings/settings.cpp 11
+ \codeline
+ \snippet doc/src/snippets/settings/settings.cpp 12
+
+ If a group is set using beginGroup(), the behavior of most
+ functions changes consequently. Groups can be set recursively.
+
+ In addition to groups, QSettings also supports an "array"
+ concept. See beginReadArray() and beginWriteArray() for details.
+
+ \section1 Fallback Mechanism
+
+ Let's assume that you have created a QSettings object with the
+ organization name MySoft and the application name Star Runner.
+ When you look up a value, up to four locations are searched in
+ that order:
+
+ \list 1
+ \o a user-specific location for the Star Runner application
+ \o a user-specific location for all applications by MySoft
+ \o a system-wide location for the Star Runner application
+ \o a system-wide location for all applications by MySoft
+ \endlist
+
+ (See \l{Platform-Specific Notes} below for information on what
+ these locations are on the different platforms supported by Qt.)
+
+ If a key cannot be found in the first location, the search goes
+ on in the second location, and so on. This enables you to store
+ system-wide or organization-wide settings and to override them on
+ a per-user or per-application basis. To turn off this mechanism,
+ call setFallbacksEnabled(false).
+
+ Although keys from all four locations are available for reading,
+ only the first file (the user-specific location for the
+ application at hand) is accessible for writing. To write to any
+ of the other files, omit the application name and/or specify
+ QSettings::SystemScope (as opposed to QSettings::UserScope, the
+ default).
+
+ Let's see with an example:
+
+ \snippet doc/src/snippets/settings/settings.cpp 13
+ \snippet doc/src/snippets/settings/settings.cpp 14
+
+ The table below summarizes which QSettings objects access
+ which location. "\bold{X}" means that the location is the main
+ location associated to the QSettings object and is used both
+ for reading and for writing; "o" means that the location is used
+ as a fallback when reading.
+
+ \table
+ \header \o Locations \o \c{obj1} \o \c{obj2} \o \c{obj3} \o \c{obj4}
+ \row \o 1. User, Application \o \bold{X} \o \o \o
+ \row \o 2. User, Organization \o o \o \bold{X} \o \o
+ \row \o 3. System, Application \o o \o \o \bold{X} \o
+ \row \o 4. System, Organization \o o \o o \o o \o \bold{X}
+ \endtable
+
+ The beauty of this mechanism is that it works on all platforms
+ supported by Qt and that it still gives you a lot of flexibility,
+ without requiring you to specify any file names or registry
+ paths.
+
+ If you want to use INI files on all platforms instead of the
+ native API, you can pass QSettings::IniFormat as the first
+ argument to the QSettings constructor, followed by the scope, the
+ organization name, and the application name:
+
+ \snippet doc/src/snippets/settings/settings.cpp 15
+
+ The \l{tools/settingseditor}{Settings Editor} example lets you
+ experiment with different settings location and with fallbacks
+ turned on or off.
+
+ \section1 Restoring the State of a GUI Application
+
+ QSettings is often used to store the state of a GUI
+ application. The following example illustrates how to use QSettings
+ to save and restore the geometry of an application's main window.
+
+ \snippet doc/src/snippets/settings/settings.cpp 16
+ \codeline
+ \snippet doc/src/snippets/settings/settings.cpp 17
+
+ See \l{Window Geometry} for a discussion on why it is better to
+ call QWidget::resize() and QWidget::move() rather than QWidget::setGeometry()
+ to restore a window's geometry.
+
+ The \c readSettings() and \c writeSettings() functions must be
+ called from the main window's constructor and close event handler
+ as follows:
+
+ \snippet doc/src/snippets/settings/settings.cpp 18
+ \dots
+ \snippet doc/src/snippets/settings/settings.cpp 19
+ \snippet doc/src/snippets/settings/settings.cpp 20
+ \codeline
+ \snippet doc/src/snippets/settings/settings.cpp 21
+
+ See the \l{mainwindows/application}{Application} example for a
+ self-contained example that uses QSettings.
+
+ \section1 Accessing Settings from Multiple Threads or Processes Simultaneously
+
+ QSettings is \l{reentrant}. This means that you can use
+ distinct QSettings object in different threads
+ simultaneously. This guarantee stands even when the QSettings
+ objects refer to the same files on disk (or to the same entries
+ in the system registry). If a setting is modified through one
+ QSettings object, the change will immediately be visible in
+ any other QSettings objects that operate on the same location
+ and that live in the same process.
+
+ QSettings can safely be used from different processes (which can
+ be different instances of your application running at the same
+ time or different applications altogether) to read and write to
+ the same system locations. It uses advisory file locking and a
+ smart merging algorithm to ensure data integrity. Changes
+ performed by another process aren't visible in the current
+ process until sync() is called.
+
+ \section1 Platform-Specific Notes
+
+ \section2 Locations Where Application Settings Are Stored
+
+ As mentioned in the \l{Fallback Mechanism} section, QSettings
+ stores settings for an application in up to four locations,
+ depending on whether the settings are user-specific or
+ system-wide and whether the the settings are application-specific
+ or organization-wide. For simplicity, we're assuming the
+ organization is called MySoft and the application is called Star
+ Runner.
+
+ On Unix systems, if the file format is NativeFormat, the
+ following files are used by default:
+
+ \list 1
+ \o \c{$HOME/.config/MySoft/Star Runner.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.conf})
+ \o \c{$HOME/.config/MySoft.conf} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.conf})
+ \o \c{/etc/xdg/MySoft/Star Runner.conf}
+ \o \c{/etc/xdg/MySoft.conf}
+ \endlist
+
+ On Mac OS X versions 10.2 and 10.3, these files are used by
+ default:
+
+ \list 1
+ \o \c{$HOME/Library/Preferences/com.MySoft.Star Runner.plist}
+ \o \c{$HOME/Library/Preferences/com.MySoft.plist}
+ \o \c{/Library/Preferences/com.MySoft.Star Runner.plist}
+ \o \c{/Library/Preferences/com.MySoft.plist}
+ \endlist
+
+ On Windows, NativeFormat settings are stored in the following
+ registry paths:
+
+ \list 1
+ \o \c{HKEY_CURRENT_USER\Software\MySoft\Star Runner}
+ \o \c{HKEY_CURRENT_USER\Software\MySoft}
+ \o \c{HKEY_LOCAL_MACHINE\Software\MySoft\Star Runner}
+ \o \c{HKEY_LOCAL_MACHINE\Software\MySoft}
+ \endlist
+
+ If the file format is IniFormat, the following files are
+ used on Unix and Mac OS X:
+
+ \list 1
+ \o \c{$HOME/.config/MySoft/Star Runner.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft/Star Runner.ini})
+ \o \c{$HOME/.config/MySoft.ini} (Qt for Embedded Linux: \c{$HOME/Settings/MySoft.ini})
+ \o \c{/etc/xdg/MySoft/Star Runner.ini}
+ \o \c{/etc/xdg/MySoft.ini}
+ \endlist
+
+ On Windows, the following files are used:
+
+ \list 1
+ \o \c{%APPDATA%\MySoft\Star Runner.ini}
+ \o \c{%APPDATA%\MySoft.ini}
+ \o \c{%COMMON_APPDATA%\MySoft\Star Runner.ini}
+ \o \c{%COMMON_APPDATA%\MySoft.ini}
+ \endlist
+
+ The \c %APPDATA% path is usually \tt{C:\\Documents and
+ Settings\\\e{User Name}\\Application Data}; the \c
+ %COMMON_APPDATA% path is usually \tt{C:\\Documents and
+ Settings\\All Users\\Application Data}.
+
+ The paths for the \c .ini and \c .conf files can be changed using
+ setPath(). On Unix and Mac OS X, the user can override them by by
+ setting the \c XDG_CONFIG_HOME environment variable; see
+ setPath() for details.
+
+ \section2 Accessing INI and .plist Files Directly
+
+ Sometimes you do want to access settings stored in a specific
+ file or registry path. On all platforms, if you want to read an
+ INI file directly, you can use the QSettings constructor that
+ takes a file name as first argument and pass QSettings::IniFormat
+ as second argument. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 2
+
+ You can then use the QSettings object to read and write settings
+ in the file.
+
+ On Mac OS X, you can access XML-based \c .plist files by passing
+ QSettings::NativeFormat as second argument. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 3
+
+ \section2 Accessing the Windows Registry Directly
+
+ On Windows, QSettings lets you access settings that have been
+ written with QSettings (or settings in a supported format, e.g., string
+ data) in the system registry. This is done by constructing a QSettings
+ object with a path in the registry and QSettings::NativeFormat.
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 4
+
+ All the registry entries that appear under the specified path can
+ be read or written through the QSettings object as usual (using
+ forward slashes instead of backslashes). For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 5
+
+ Note that the backslash character is, as mentioned, used by
+ QSettings to separate subkeys. As a result, you cannot read or
+ write windows registry entries that contain slashes or
+ backslashes; you should use a native windows API if you need to do
+ so.
+
+ \section2 Accessing Common Registry Settings on Windows
+
+ On Windows, it is possible for a key to have both a value and subkeys.
+ Its default value is accessed by using "Default" or "." in
+ place of a subkey:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 6
+
+ On other platforms than Windows, "Default" and "." would be
+ treated as regular subkeys.
+
+ \section2 Platform Limitations
+
+ While QSettings attempts to smooth over the differences between
+ the different supported platforms, there are still a few
+ differences that you should be aware of when porting your
+ application:
+
+ \list
+ \o The Windows system registry has the following limitations: A
+ subkey may not exceed 255 characters, an entry's value may
+ not exceed 16,383 characters, and all the values of a key may
+ not exceed 65,535 characters. One way to work around these
+ limitations is to store the settings using the IniFormat
+ instead of the NativeFormat.
+
+ \o On Mac OS X, allKeys() will return some extra keys for global
+ settings that apply to all applications. These keys can be
+ read using value() but cannot be changed, only shadowed.
+ Calling setFallbacksEnabled(false) will hide these global
+ settings.
+
+ \o On Mac OS X, the CFPreferences API used by QSettings expects
+ Internet domain names rather than organization names. To
+ provide a uniform API, QSettings derives a fake domain name
+ from the organization name (unless the organization name
+ already is a domain name, e.g. OpenOffice.org). The algorithm
+ appends ".com" to the company name and replaces spaces and
+ other illegal characters with hyphens. If you want to specify
+ a different domain name, call
+ QCoreApplication::setOrganizationDomain(),
+ QCoreApplication::setOrganizationName(), and
+ QCoreApplication::setApplicationName() in your \c main()
+ function and then use the default QSettings constructor.
+ Another solution is to use preprocessor directives, for
+ example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 7
+
+ \o On Unix and Mac OS X systems, the advisory file locking is disabled
+ if NFS (or AutoFS or CacheFS) is detected to work around a bug in the
+ NFS fcntl() implementation, which hangs forever if statd or lockd aren't
+ running. Also, the locking isn't performed when accessing \c .plist
+ files.
+
+ \endlist
+
+ \sa QVariant, QSessionManager, {Settings Editor Example}, {Application Example}
+*/
+
+/*! \enum QSettings::Status
+
+ The following status values are possible:
+
+ \value NoError No error occurred.
+ \value AccessError An access error occurred (e.g. trying to write to a read-only file).
+ \value FormatError A format error occurred (e.g. loading a malformed INI file).
+
+ \sa status()
+*/
+
+/*! \enum QSettings::Format
+
+ This enum type specifies the storage format used by QSettings.
+
+ \value NativeFormat Store the settings using the most
+ appropriate storage format for the platform.
+ On Windows, this means the system registry;
+ on Mac OS X, this means the CFPreferences
+ API; on Unix, this means textual
+ configuration files in INI format.
+ \value IniFormat Store the settings in INI files.
+ \value InvalidFormat Special value returned by registerFormat().
+ \omitvalue CustomFormat1
+ \omitvalue CustomFormat2
+ \omitvalue CustomFormat3
+ \omitvalue CustomFormat4
+ \omitvalue CustomFormat5
+ \omitvalue CustomFormat6
+ \omitvalue CustomFormat7
+ \omitvalue CustomFormat8
+ \omitvalue CustomFormat9
+ \omitvalue CustomFormat10
+ \omitvalue CustomFormat11
+ \omitvalue CustomFormat12
+ \omitvalue CustomFormat13
+ \omitvalue CustomFormat14
+ \omitvalue CustomFormat15
+ \omitvalue CustomFormat16
+
+ On Unix, NativeFormat and IniFormat mean the same thing, except
+ that the file extension is different (\c .conf for NativeFormat,
+ \c .ini for IniFormat).
+
+ The INI file format is a Windows file format that Qt supports on
+ all platforms. In the absence of an INI standard, we try to
+ follow what Microsoft does, with the following exceptions:
+
+ \list
+ \o If you store types that QVariant can't convert to QString
+ (e.g., QPoint, QRect, and QSize), Qt uses an \c{@}-based
+ syntax to encode the type. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 8
+
+ To minimize compatibility issues, any \c @ that doesn't
+ appear at the first position in the value or that isn't
+ followed by a Qt type (\c Point, \c Rect, \c Size, etc.) is
+ treated as a normal character.
+
+ \o Although backslash is a special character in INI files, most
+ Windows applications don't escape backslashes (\c{\}) in file
+ paths:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 9
+
+ QSettings always treats backslash as a special character and
+ provides no API for reading or writing such entries.
+
+ \o The INI file format has severe restrictions on the syntax of
+ a key. Qt works around this by using \c % as an escape
+ character in keys. In addition, if you save a top-level
+ setting (a key with no slashes in it, e.g., "someKey"), it
+ will appear in the INI file's "General" section. To avoid
+ overwriting other keys, if you save something using the a key
+ such as "General/someKey", the key will be located in the
+ "%General" section, \e not in the "General" section.
+
+ \o Following the philosophy that we should be liberal in what
+ we accept and conservative in what we generate, QSettings
+ will accept Latin-1 encoded INI files, but generate pure
+ ASCII files, where non-ASCII values are encoded using standard
+ INI escape sequences. To make the INI files more readable (but
+ potentially less compatible), call setIniCodec().
+ \endlist
+
+ \sa registerFormat(), setPath()
+*/
+
+/*! \enum QSettings::Scope
+
+ This enum specifies whether settings are user-specific or shared
+ by all users of the same system.
+
+ \value UserScope Store settings in a location specific to the
+ current user (e.g., in the user's home
+ directory).
+ \value SystemScope Store settings in a global location, so that
+ all users on the same machine access the same
+ set of settings.
+ \omitvalue User
+ \omitvalue Global
+
+ \sa setPath()
+*/
+
+#ifndef QT_NO_QOBJECT
+/*!
+ Constructs a QSettings object for accessing settings of the
+ application called \a application from the organization called \a
+ organization, and with parent \a parent.
+
+ Example:
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 10
+
+ The scope is QSettings::UserScope and the format is
+ QSettings::NativeFormat.
+
+ \sa setDefaultFormat(), {Fallback Mechanism}
+*/
+QSettings::QSettings(const QString &organization, const QString &application, QObject *parent)
+ : QObject(*QSettingsPrivate::create(NativeFormat, UserScope, organization, application),
+ parent)
+{
+}
+
+/*!
+ Constructs a QSettings object for accessing settings of the
+ application called \a application from the organization called \a
+ organization, and with parent \a parent.
+
+ If \a scope is QSettings::UserScope, the QSettings object searches
+ user-specific settings first, before it searches system-wide
+ settings as a fallback. If \a scope is
+ QSettings::SystemScope, the QSettings object ignores user-specific
+ settings and provides access to system-wide settings.
+
+ The storage format is QSettings::NativeFormat.
+
+ If no application name is given, the QSettings object will
+ only access the organization-wide \l{Fallback Mechanism}{locations}.
+
+ \sa setDefaultFormat()
+*/
+QSettings::QSettings(Scope scope, const QString &organization, const QString &application,
+ QObject *parent)
+ : QObject(*QSettingsPrivate::create(NativeFormat, scope, organization, application), parent)
+{
+}
+
+/*!
+ Constructs a QSettings object for accessing settings of the
+ application called \a application from the organization called
+ \a organization, and with parent \a parent.
+
+ If \a scope is QSettings::UserScope, the QSettings object searches
+ user-specific settings first, before it searches system-wide
+ settings as a fallback. If \a scope is
+ QSettings::SystemScope, the QSettings object ignores user-specific
+ settings and provides access to system-wide settings.
+
+ If \a format is QSettings::NativeFormat, the native API is used for
+ storing settings. If \a format is QSettings::IniFormat, the INI format
+ is used.
+
+ If no application name is given, the QSettings object will
+ only access the organization-wide \l{Fallback Mechanism}{locations}.
+*/
+QSettings::QSettings(Format format, Scope scope, const QString &organization,
+ const QString &application, QObject *parent)
+ : QObject(*QSettingsPrivate::create(format, scope, organization, application), parent)
+{
+}
+
+/*!
+ Constructs a QSettings object for accessing the settings
+ stored in the file called \a fileName, with parent \a parent. If
+ the file doesn't already exist, it is created.
+
+ If \a format is QSettings::NativeFormat, the meaning of \a
+ fileName depends on the platform. On Unix, \a fileName is the
+ name of an INI file. On Mac OS X, \a fileName is the name of a
+ \c .plist file. On Windows, \a fileName is a path in the system
+ registry.
+
+ If \a format is QSettings::IniFormat, \a fileName is the name of an INI
+ file.
+
+ \warning This function is provided for convenience. It works well for
+ accessing INI or \c .plist files generated by Qt, but might fail on some
+ syntaxes found in such files originated by other programs. In particular,
+ be aware of the following limitations:
+
+ \list
+ \o QSettings provides no way of reading INI "path" entries, i.e., entries
+ with unescaped slash characters. (This is because these entries are
+ ambiguous and cannot be resolved automatically.)
+ \o In INI files, QSettings uses the \c @ character as a metacharacter in some
+ contexts, to encode Qt-specific data types (e.g., \c @Rect), and might
+ therefore misinterpret it when it occurs in pure INI files.
+ \endlist
+
+ \sa fileName()
+*/
+QSettings::QSettings(const QString &fileName, Format format, QObject *parent)
+ : QObject(*QSettingsPrivate::create(fileName, format), parent)
+{
+}
+
+/*!
+ Constructs a QSettings object for accessing settings of the
+ application and organization set previously with a call to
+ QCoreApplication::setOrganizationName(),
+ QCoreApplication::setOrganizationDomain(), and
+ QCoreApplication::setApplicationName().
+
+ The scope is QSettings::UserScope and the format is
+ defaultFormat() (QSettings::NativeFormat by default).
+
+ The code
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 11
+
+ is equivalent to
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 12
+
+ If QCoreApplication::setOrganizationName() and
+ QCoreApplication::setApplicationName() has not been previously
+ called, the QSettings object will not be able to read or write
+ any settings, and status() will return AccessError.
+
+ On Mac OS X, if both a name and an Internet domain are specified
+ for the organization, the domain is preferred over the name. On
+ other platforms, the name is preferred over the domain.
+
+ \sa QCoreApplication::setOrganizationName(),
+ QCoreApplication::setOrganizationDomain(),
+ QCoreApplication::setApplicationName(),
+ setDefaultFormat()
+*/
+QSettings::QSettings(QObject *parent)
+ : QObject(*QSettingsPrivate::create(globalDefaultFormat, UserScope,
+#ifdef Q_OS_MAC
+ QCoreApplication::organizationDomain().isEmpty()
+ ? QCoreApplication::organizationName()
+ : QCoreApplication::organizationDomain()
+#else
+ QCoreApplication::organizationName().isEmpty()
+ ? QCoreApplication::organizationDomain()
+ : QCoreApplication::organizationName()
+#endif
+ , QCoreApplication::applicationName()),
+ parent)
+{
+}
+
+#else
+QSettings::QSettings(const QString &organization, const QString &application)
+ : d_ptr(QSettingsPrivate::create(globalDefaultFormat, QSettings::UserScope, organization, application))
+{
+ d_ptr->q_ptr = this;
+}
+
+QSettings::QSettings(Scope scope, const QString &organization, const QString &application)
+ : d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope, organization, application))
+{
+ d_ptr->q_ptr = this;
+}
+
+QSettings::QSettings(Format format, Scope scope, const QString &organization,
+ const QString &application)
+ : d_ptr(QSettingsPrivate::create(format, scope, organization, application))
+{
+ d_ptr->q_ptr = this;
+}
+
+QSettings::QSettings(const QString &fileName, Format format)
+ : d_ptr(QSettingsPrivate::create(fileName, format))
+{
+ d_ptr->q_ptr = this;
+}
+#endif
+
+/*!
+ Destroys the QSettings object.
+
+ Any unsaved changes will eventually be written to permanent
+ storage.
+
+ \sa sync()
+*/
+QSettings::~QSettings()
+{
+ Q_D(QSettings);
+ if (d->pendingChanges)
+ d->flush();
+#ifdef QT_NO_QOBJECT
+ delete d;
+#endif
+}
+
+/*!
+ Removes all entries in the primary location associated to this
+ QSettings object.
+
+ Entries in fallback locations are not removed.
+
+ If you only want to remove the entries in the current group(),
+ use remove("") instead.
+
+ \sa remove(), setFallbacksEnabled()
+*/
+void QSettings::clear()
+{
+ Q_D(QSettings);
+ d->clear();
+ d->requestUpdate();
+}
+
+/*!
+ Writes any unsaved changes to permanent storage, and reloads any
+ settings that have been changed in the meantime by another
+ application.
+
+ This function is called automatically from QSettings's destructor and
+ by the event loop at regular intervals, so you normally don't need to
+ call it yourself.
+
+ \sa status()
+*/
+void QSettings::sync()
+{
+ Q_D(QSettings);
+ d->sync();
+}
+
+/*!
+ Returns the path where settings written using this QSettings
+ object are stored.
+
+ On Windows, if the format is QSettings::NativeFormat, the return value
+ is a system registry path, not a file path.
+
+ \sa isWritable(), format()
+*/
+QString QSettings::fileName() const
+{
+ Q_D(const QSettings);
+ return d->fileName();
+}
+
+/*!
+ \since 4.4
+
+ Returns the format used for storing the settings.
+
+ \sa defaultFormat(), fileName(), scope(), organizationName(), applicationName()
+*/
+QSettings::Format QSettings::format() const
+{
+ Q_D(const QSettings);
+ return d->format;
+}
+
+/*!
+ \since 4.4
+
+ Returns the scope used for storing the settings.
+
+ \sa format(), organizationName(), applicationName()
+*/
+QSettings::Scope QSettings::scope() const
+{
+ Q_D(const QSettings);
+ return d->scope;
+}
+
+/*!
+ \since 4.4
+
+ Returns the organization name used for storing the settings.
+
+ \sa QCoreApplication::organizationName(), format(), scope(), applicationName()
+*/
+QString QSettings::organizationName() const
+{
+ Q_D(const QSettings);
+ return d->organizationName;
+}
+
+/*!
+ \since 4.4
+
+ Returns the application name used for storing the settings.
+
+ \sa QCoreApplication::applicationName(), format(), scope(), organizationName()
+*/
+QString QSettings::applicationName() const
+{
+ Q_D(const QSettings);
+ return d->applicationName;
+}
+
+#ifndef QT_NO_TEXTCODEC
+
+/*!
+ \since 4.5
+
+ Sets the codec for accessing INI files (including \c .conf files on Unix)
+ to \a codec. The codec is used for decoding any data that is read from
+ the INI file, and for encoding any data that is written to the file. By
+ default, no codec is used, and non-ASCII characters are encoded using
+ standard INI escape sequences.
+
+ \warning The codec must be set immediately after creating the QSettings
+ object, before accessing any data.
+
+ \sa iniCodec()
+*/
+void QSettings::setIniCodec(QTextCodec *codec)
+{
+ Q_D(QSettings);
+ d->iniCodec = codec;
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Sets the codec for accessing INI files (including \c .conf files on Unix)
+ to the QTextCodec for the encoding specified by \a codecName. Common
+ values for \c codecName include "ISO 8859-1", "UTF-8", and "UTF-16".
+ If the encoding isn't recognized, nothing happens.
+
+ \sa QTextCodec::codecForName()
+*/
+void QSettings::setIniCodec(const char *codecName)
+{
+ Q_D(QSettings);
+ if (QTextCodec *codec = QTextCodec::codecForName(codecName))
+ d->iniCodec = codec;
+}
+
+/*!
+ \since 4.5
+
+ Returns the codec that is used for accessing INI files. By default,
+ no codec is used, so a null pointer is returned.
+*/
+
+QTextCodec *QSettings::iniCodec() const
+{
+ Q_D(const QSettings);
+ return d->iniCodec;
+}
+
+#endif // QT_NO_TEXTCODEC
+
+/*!
+ Returns a status code indicating the first error that was met by
+ QSettings, or QSettings::NoError if no error occurred.
+
+ Be aware that QSettings delays performing some operations. For this
+ reason, you might want to call sync() to ensure that the data stored
+ in QSettings is written to disk before calling status().
+
+ \sa sync()
+*/
+QSettings::Status QSettings::status() const
+{
+ Q_D(const QSettings);
+ return d->status;
+}
+
+/*!
+ Appends \a prefix to the current group.
+
+ The current group is automatically prepended to all keys
+ specified to QSettings. In addition, query functions such as
+ childGroups(), childKeys(), and allKeys() are based on the group.
+ By default, no group is set.
+
+ Groups are useful to avoid typing in the same setting paths over
+ and over. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 13
+
+ This will set the value of three settings:
+
+ \list
+ \o \c mainwindow/size
+ \o \c mainwindow/fullScreen
+ \o \c outputpanel/visible
+ \endlist
+
+ Call endGroup() to reset the current group to what it was before
+ the corresponding beginGroup() call. Groups can be nested.
+
+ \sa endGroup(), group()
+*/
+void QSettings::beginGroup(const QString &prefix)
+{
+ Q_D(QSettings);
+ d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix)));
+}
+
+/*!
+ Resets the group to what it was before the corresponding
+ beginGroup() call.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 14
+
+ \sa beginGroup(), group()
+*/
+void QSettings::endGroup()
+{
+ Q_D(QSettings);
+ if (d->groupStack.isEmpty()) {
+ qWarning("QSettings::endGroup: No matching beginGroup()");
+ return;
+ }
+
+ QSettingsGroup group = d->groupStack.pop();
+ int len = group.toString().size();
+ if (len > 0)
+ d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
+
+ if (group.isArray())
+ qWarning("QSettings::endGroup: Expected endArray() instead");
+}
+
+/*!
+ Returns the current group.
+
+ \sa beginGroup(), endGroup()
+*/
+QString QSettings::group() const
+{
+ Q_D(const QSettings);
+ return d->groupPrefix.left(d->groupPrefix.size() - 1);
+}
+
+/*!
+ Adds \a prefix to the current group and starts reading from an
+ array. Returns the size of the array.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 15
+
+ Use beginWriteArray() to write the array in the first place.
+
+ \sa beginWriteArray(), endArray(), setArrayIndex()
+*/
+int QSettings::beginReadArray(const QString &prefix)
+{
+ Q_D(QSettings);
+ d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), false));
+ return value(QLatin1String("size")).toInt();
+}
+
+/*!
+ Adds \a prefix to the current group and starts writing an array
+ of size \a size. If \a size is -1 (the default), it is automatically
+ determined based on the indexes of the entries written.
+
+ If you have many occurrences of a certain set of keys, you can
+ use arrays to make your life easier. For example, let's suppose
+ that you want to save a variable-length list of user names and
+ passwords. You could then write:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 16
+
+ The generated keys will have the form
+
+ \list
+ \o \c logins/size
+ \o \c logins/1/userName
+ \o \c logins/1/password
+ \o \c logins/2/userName
+ \o \c logins/2/password
+ \o \c logins/3/userName
+ \o \c logins/3/password
+ \o ...
+ \endlist
+
+ To read back an array, use beginReadArray().
+
+ \sa beginReadArray(), endArray(), setArrayIndex()
+*/
+void QSettings::beginWriteArray(const QString &prefix, int size)
+{
+ Q_D(QSettings);
+ d->beginGroupOrArray(QSettingsGroup(d->normalizedKey(prefix), size < 0));
+
+ if (size < 0)
+ remove(QLatin1String("size"));
+ else
+ setValue(QLatin1String("size"), size);
+}
+
+/*!
+ Closes the array that was started using beginReadArray() or
+ beginWriteArray().
+
+ \sa beginReadArray(), beginWriteArray()
+*/
+void QSettings::endArray()
+{
+ Q_D(QSettings);
+ if (d->groupStack.isEmpty()) {
+ qWarning("QSettings::endArray: No matching beginArray()");
+ return;
+ }
+
+ QSettingsGroup group = d->groupStack.top();
+ int len = group.toString().size();
+ d->groupStack.pop();
+ if (len > 0)
+ d->groupPrefix.truncate(d->groupPrefix.size() - (len + 1));
+
+ if (group.arraySizeGuess() != -1)
+ setValue(group.name() + QLatin1String("/size"), group.arraySizeGuess());
+
+ if (!group.isArray())
+ qWarning("QSettings::endArray: Expected endGroup() instead");
+}
+
+/*!
+ Sets the current array index to \a i. Calls to functions such as
+ setValue(), value(), remove(), and contains() will operate on the
+ array entry at that index.
+
+ You must call beginReadArray() or beginWriteArray() before you
+ can call this function.
+*/
+void QSettings::setArrayIndex(int i)
+{
+ Q_D(QSettings);
+ if (d->groupStack.isEmpty() || !d->groupStack.top().isArray()) {
+ qWarning("QSettings::setArrayIndex: Missing beginArray()");
+ return;
+ }
+
+ QSettingsGroup &top = d->groupStack.top();
+ int len = top.toString().size();
+ top.setArrayIndex(qMax(i, 0));
+ d->groupPrefix.replace(d->groupPrefix.size() - len - 1, len, top.toString());
+}
+
+/*!
+ Returns a list of all keys, including subkeys, that can be read
+ using the QSettings object.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 17
+
+ If a group is set using beginGroup(), only the keys in the group
+ are returned, without the group prefix:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 18
+
+ \sa childGroups(), childKeys()
+*/
+QStringList QSettings::allKeys() const
+{
+ Q_D(const QSettings);
+ return d->children(d->groupPrefix, QSettingsPrivate::AllKeys);
+}
+
+/*!
+ Returns a list of all top-level keys that can be read using the
+ QSettings object.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 19
+
+ If a group is set using beginGroup(), the top-level keys in that
+ group are returned, without the group prefix:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 20
+
+ You can navigate through the entire setting hierarchy using
+ childKeys() and childGroups() recursively.
+
+ \sa childGroups(), allKeys()
+*/
+QStringList QSettings::childKeys() const
+{
+ Q_D(const QSettings);
+ return d->children(d->groupPrefix, QSettingsPrivate::ChildKeys);
+}
+
+/*!
+ Returns a list of all key top-level groups that contain keys that
+ can be read using the QSettings object.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 21
+
+ If a group is set using beginGroup(), the first-level keys in
+ that group are returned, without the group prefix.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 22
+
+ You can navigate through the entire setting hierarchy using
+ childKeys() and childGroups() recursively.
+
+ \sa childKeys(), allKeys()
+*/
+QStringList QSettings::childGroups() const
+{
+ Q_D(const QSettings);
+ return d->children(d->groupPrefix, QSettingsPrivate::ChildGroups);
+}
+
+/*!
+ Returns true if settings can be written using this QSettings
+ object; returns false otherwise.
+
+ One reason why isWritable() might return false is if
+ QSettings operates on a read-only file.
+
+ \warning This function is not perfectly reliable, because the
+ file permissions can change at any time.
+
+ \sa fileName(), status(), sync()
+*/
+bool QSettings::isWritable() const
+{
+ Q_D(const QSettings);
+ return d->isWritable();
+}
+
+/*!
+
+ Sets the value of setting \a key to \a value. If the \a key already
+ exists, the previous value is overwritten.
+
+ Note that the Windows registry and INI files use case-insensitive
+ keys, whereas the Carbon Preferences API on Mac OS X uses
+ case-sensitive keys. To avoid portability problems, see the \l{Key
+ Syntax} rules.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 23
+
+ \sa value(), remove(), contains()
+*/
+void QSettings::setValue(const QString &key, const QVariant &value)
+{
+ Q_D(QSettings);
+ QString k = d->actualKey(key);
+ d->set(k, value);
+ d->requestUpdate();
+}
+
+/*!
+ Removes the setting \a key and any sub-settings of \a key.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 24
+
+ Be aware that if one of the fallback locations contains a setting
+ with the same key, that setting will be visible after calling
+ remove().
+
+ If \a key is an empty string, all keys in the current group() are
+ removed. For example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 25
+
+ Note that the Windows registry and INI files use case-insensitive
+ keys, whereas the Carbon Preferences API on Mac OS X uses
+ case-sensitive keys. To avoid portability problems, see the \l{Key
+ Syntax} rules.
+
+ \sa setValue(), value(), contains()
+*/
+void QSettings::remove(const QString &key)
+{
+ Q_D(QSettings);
+ /*
+ We cannot use actualKey(), because remove() supports empty
+ keys. The code is also tricky because of slash handling.
+ */
+ QString theKey = d->normalizedKey(key);
+ if (theKey.isEmpty())
+ theKey = group();
+ else
+ theKey.prepend(d->groupPrefix);
+
+ if (theKey.isEmpty()) {
+ d->clear();
+ } else {
+ d->remove(theKey);
+ }
+ d->requestUpdate();
+}
+
+/*!
+ Returns true if there exists a setting called \a key; returns
+ false otherwise.
+
+ If a group is set using beginGroup(), \a key is taken to be
+ relative to that group.
+
+ Note that the Windows registry and INI files use case-insensitive
+ keys, whereas the Carbon Preferences API on Mac OS X uses
+ case-sensitive keys. To avoid portability problems, see the \l{Key
+ Syntax} rules.
+
+ \sa value(), setValue()
+*/
+bool QSettings::contains(const QString &key) const
+{
+ Q_D(const QSettings);
+ QString k = d->actualKey(key);
+ return d->get(k, 0);
+}
+
+/*!
+ Sets whether fallbacks are enabled to \a b.
+
+ By default, fallbacks are enabled.
+
+ \sa fallbacksEnabled()
+*/
+void QSettings::setFallbacksEnabled(bool b)
+{
+ Q_D(QSettings);
+ d->fallbacks = !!b;
+}
+
+/*!
+ Returns true if fallbacks are enabled; returns false otherwise.
+
+ By default, fallbacks are enabled.
+
+ \sa setFallbacksEnabled()
+*/
+bool QSettings::fallbacksEnabled() const
+{
+ Q_D(const QSettings);
+ return d->fallbacks;
+}
+
+#ifndef QT_NO_QOBJECT
+/*!
+ \reimp
+*/
+bool QSettings::event(QEvent *event)
+{
+ Q_D(QSettings);
+ if (event->type() == QEvent::UpdateRequest) {
+ d->update();
+ return true;
+ }
+ return QObject::event(event);
+}
+#endif
+
+/*!
+ Returns the value for setting \a key. If the setting doesn't
+ exist, returns \a defaultValue.
+
+ If no default value is specified, a default QVariant is
+ returned.
+
+ Note that the Windows registry and INI files use case-insensitive
+ keys, whereas the Carbon Preferences API on Mac OS X uses
+ case-sensitive keys. To avoid portability problems, see the \l{Key
+ Syntax} rules.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 26
+
+ \sa setValue(), contains(), remove()
+*/
+QVariant QSettings::value(const QString &key, const QVariant &defaultValue) const
+{
+ Q_D(const QSettings);
+ QVariant result = defaultValue;
+ QString k = d->actualKey(key);
+ d->get(k, &result);
+ return result;
+}
+
+/*!
+ \since 4.4
+
+ Sets the default file format to the given \a format, used for storing
+ settings for the QSettings(QObject *) constructor.
+
+ If no default format is set, QSettings::NativeFormat is used.
+
+ \sa format()
+*/
+void QSettings::setDefaultFormat(Format format)
+{
+ globalDefaultFormat = format;
+}
+
+/*!
+ \since 4.4
+
+ Returns default file format used for storing settings for the QSettings(QObject *) constructor.
+ If no default format is set, QSettings::NativeFormat is used.
+
+ \sa format()
+*/
+QSettings::Format QSettings::defaultFormat()
+{
+ return globalDefaultFormat;
+}
+
+/*!
+ \obsolete
+
+ Use setPath() instead.
+
+ \oldcode
+ setSystemIniPath(path);
+ \newcode
+ setPath(QSettings::NativeFormat, QSettings::SystemScope, path);
+ setPath(QSettings::IniFormat, QSettings::SystemScope, path);
+ \endcode
+*/
+void QSettings::setSystemIniPath(const QString &dir)
+{
+ setPath(IniFormat, SystemScope, dir);
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
+ setPath(NativeFormat, SystemScope, dir);
+#endif
+}
+
+/*!
+ \obsolete
+
+ Use setPath() instead.
+*/
+
+void QSettings::setUserIniPath(const QString &dir)
+{
+ setPath(IniFormat, UserScope, dir);
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
+ setPath(NativeFormat, UserScope, dir);
+#endif
+}
+
+/*!
+ \since 4.1
+
+ Sets the path used for storing settings for the given \a format
+ and \a scope, to \a path. The \a format can be a custom format.
+
+ The table below summarizes the default values:
+
+ \table
+ \header \o Platform \o Format \o Scope \o Path
+ \row \o{1,2} Windows \o{1,2} IniFormat \o UserScope \o \c %APPDATA%
+ \row \o SystemScope \o \c %COMMON_APPDATA%
+ \row \o{1,2} Unix \o{1,2} NativeFormat, IniFormat \o UserScope \o \c $HOME/.config
+ \row \o SystemScope \o \c /etc/xdg
+ \row \o{1,2} Qt for Embedded Linux \o{1,2} NativeFormat, IniFormat \o UserScope \o \c $HOME/Settings
+ \row \o SystemScope \o \c /etc/xdg
+ \row \o{1,2} Mac OS X \o{1,2} IniFormat \o UserScope \o \c $HOME/.config
+ \row \o SystemScope \o \c /etc/xdg
+ \endtable
+
+ The default UserScope paths on Unix and Mac OS X (\c
+ $HOME/.config or $HOME/Settings) can be overridden by the user by setting the
+ \c XDG_CONFIG_HOME environment variable. The default SystemScope
+ paths on Unix and Mac OS X (\c /etc/xdg) can be overridden when
+ building the Qt library using the \c configure script's \c
+ --sysconfdir flag (see QLibraryInfo for details).
+
+ Setting the NativeFormat paths on Windows and Mac OS X has no
+ effect.
+
+ \warning This function doesn't affect existing QSettings objects.
+
+ \sa registerFormat()
+*/
+void QSettings::setPath(Format format, Scope scope, const QString &path)
+{
+ QMutexLocker locker(globalMutex());
+ PathHash *pathHash = pathHashFunc();
+ pathHash->insert(pathHashKey(format, scope), path + QDir::separator());
+}
+
+/*!
+ \typedef QSettings::SettingsMap
+
+ Typedef for QMap<QString, QVariant>.
+
+ \sa registerFormat()
+*/
+
+/*!
+ \typedef QSettings::ReadFunc
+
+ Typedef for a pointer to a function with the following signature:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 27
+
+ \sa WriteFunc, registerFormat()
+*/
+
+/*!
+ \typedef QSettings::WriteFunc
+
+ Typedef for a pointer to a function with the following signature:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 28
+
+ \sa ReadFunc, registerFormat()
+*/
+
+/*!
+ \since 4.1
+ \threadsafe
+
+ Registers a custom storage format. On success, returns a special
+ Format value that can then be passed to the QSettings constuctor.
+ On failure, returns InvalidFormat.
+
+ The \a extension is the file
+ extension associated to the format (without the '.').
+
+ The \a readFunc and \a writeFunc parameters are pointers to
+ functions that read and write a set of (key, value) pairs. The
+ QIODevice parameter to the read and write functions is always
+ opened in binary mode (i.e., without the QIODevice::Text flag).
+
+ The \a caseSensitivity parameter specifies whether keys are case
+ sensitive or not. This makes a difference when looking up values
+ using QSettings. The default is case sensitive.
+
+ By default, if you use one of the constructors that work in terms
+ of an organization name and an application name, the file system
+ locations used are the same as for IniFormat. Use setPath() to
+ specify other locations.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qsettings.cpp 29
+
+ \sa setPath()
+*/
+QSettings::Format QSettings::registerFormat(const QString &extension, ReadFunc readFunc,
+ WriteFunc writeFunc,
+ Qt::CaseSensitivity caseSensitivity)
+{
+#ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER
+ Q_ASSERT(caseSensitivity == Qt::CaseSensitive);
+#endif
+
+ QMutexLocker locker(globalMutex());
+ CustomFormatVector *customFormatVector = customFormatVectorFunc();
+ int index = customFormatVector->size();
+ if (index == 16) // the QSettings::Format enum has room for 16 custom formats
+ return QSettings::InvalidFormat;
+
+ QConfFileCustomFormat info;
+ info.extension = QLatin1Char('.');
+ info.extension += extension;
+ info.readFunc = readFunc;
+ info.writeFunc = writeFunc;
+ info.caseSensitivity = caseSensitivity;
+ customFormatVector->append(info);
+
+ return QSettings::Format((int)QSettings::CustomFormat1 + index);
+}
+
+#ifdef QT3_SUPPORT
+void QSettings::setPath_helper(Scope scope, const QString &organization, const QString &application)
+{
+ Q_D(QSettings);
+ if (d->pendingChanges)
+ d->flush();
+ 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;
+}
+
+/*! \fn bool QSettings::writeEntry(const QString &key, bool value)
+
+ Sets the value of setting \a key to \a value.
+
+ Use setValue() instead.
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, double value)
+
+ \overload
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, int value)
+
+ \overload
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, const char *value)
+
+ \overload
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, const QString &value)
+
+ \overload
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, const QStringList &value)
+
+ \overload
+*/
+
+/*! \fn bool QSettings::writeEntry(const QString &key, const QStringList &value, QChar separator)
+
+ \overload
+
+ Use setValue(\a key, \a value) instead. You don't need \a separator.
+*/
+
+/*! \fn QStringList QSettings::readListEntry(const QString &key, bool *ok = 0)
+
+ Returns the value of setting \a key converted to a QStringList.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ QStringList list = settings.readListEntry("recentFiles", &ok);
+ \newcode
+ bool ok = settings.contains("recentFiles");
+ QStringList list = settings.value("recentFiles").toStringList();
+ \endcode
+*/
+
+/*! \fn QStringList QSettings::readListEntry(const QString &key, QChar separator, bool *ok)
+
+ Returns the value of setting \a key converted to a QStringList.
+ \a separator is ignored.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ QStringList list = settings.readListEntry("recentFiles", ":", &ok);
+ \newcode
+ bool ok = settings.contains("recentFiles");
+ QStringList list = settings.value("recentFiles").toStringList();
+ \endcode
+*/
+
+/*! \fn QString QSettings::readEntry(const QString &key, const QString &defaultValue, bool *ok)
+
+ Returns the value for setting \a key converted to a QString. If
+ the setting doesn't exist, returns \a defaultValue.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ QString str = settings.readEntry("userName", "administrator", &ok);
+ \newcode
+ bool ok = settings.contains("userName");
+ QString str = settings.value("userName", "administrator").toString();
+ \endcode
+*/
+
+/*! \fn int QSettings::readNumEntry(const QString &key, int defaultValue, bool *ok)
+
+ Returns the value for setting \a key converted to an \c int. If
+ the setting doesn't exist, returns \a defaultValue.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ int max = settings.readNumEntry("maxConnections", 30, &ok);
+ \newcode
+ bool ok = settings.contains("maxConnections");
+ int max = settings.value("maxConnections", 30).toInt();
+ \endcode
+*/
+
+/*! \fn double QSettings::readDoubleEntry(const QString &key, double defaultValue, bool *ok)
+
+ Returns the value for setting \a key converted to a \c double. If
+ the setting doesn't exist, returns \a defaultValue.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ double pi = settings.readDoubleEntry("pi", 3.141592, &ok);
+ \newcode
+ bool ok = settings.contains("pi");
+ double pi = settings.value("pi", 3.141592).toDouble();
+ \endcode
+*/
+
+/*! \fn bool QSettings::readBoolEntry(const QString &key, bool defaultValue, bool *ok)
+
+ Returns the value for setting \a key converted to a \c bool. If
+ the setting doesn't exist, returns \a defaultValue.
+
+ If \a ok is not 0, *\a{ok} is set to true if the key exists,
+ otherwise *\a{ok} is set to false.
+
+ Use value() instead.
+
+ \oldcode
+ bool ok;
+ bool grid = settings.readBoolEntry("showGrid", true, &ok);
+ \newcode
+ bool ok = settings.contains("showGrid");
+ bool grid = settings.value("showGrid", true).toBool();
+ \endcode
+*/
+
+/*! \fn bool QSettings::removeEntry(const QString &key)
+
+ Use remove() instead.
+*/
+
+/*! \enum QSettings::System
+ \compat
+
+ \value Unix Unix systems (X11 and Embedded Linux)
+ \value Windows Microsoft Windows systems
+ \value Mac Mac OS X systems
+
+ \sa insertSearchPath(), removeSearchPath()
+*/
+
+/*! \fn void QSettings::insertSearchPath(System system, const QString &path)
+
+ This function is implemented as a no-op. It is provided for
+ source compatibility with Qt 3. The new QSettings class has no
+ concept of "search path".
+*/
+
+/*! \fn void QSettings::removeSearchPath(System system, const QString &path)
+
+ This function is implemented as a no-op. It is provided for
+ source compatibility with Qt 3. The new QSettings class has no
+ concept of "search path".
+*/
+
+/*! \fn void QSettings::setPath(const QString &organization, const QString &application, \
+ Scope scope)
+
+ Specifies the \a organization, \a application, and \a scope to
+ use by the QSettings object.
+
+ Use the appropriate constructor instead, with QSettings::UserScope
+ instead of QSettings::User and QSettings::SystemScope instead of
+ QSettings::Global.
+
+ \oldcode
+ QSettings settings;
+ settings.setPath("twikimaster.com", "Kanooth", QSettings::Global);
+ \newcode
+ QSettings settings(QSettings::SystemScope, "twikimaster.com", "Kanooth");
+ \endcode
+*/
+
+/*! \fn void QSettings::resetGroup()
+
+ Sets the current group to be the empty string.
+
+ Use endGroup() instead (possibly multiple times).
+
+ \oldcode
+ QSettings settings;
+ settings.beginGroup("mainWindow");
+ settings.beginGroup("leftPanel");
+ ...
+ settings.resetGroup();
+ \newcode
+ QSettings settings;
+ settings.beginGroup("mainWindow");
+ settings.beginGroup("leftPanel");
+ ...
+ settings.endGroup();
+ settings.endGroup();
+ \endcode
+*/
+
+/*! \fn QStringList QSettings::entryList(const QString &key) const
+
+ Returns a list of all sub-keys of \a key.
+
+ Use childKeys() instead.
+
+ \oldcode
+ QSettings settings;
+ QStringList keys = settings.entryList("cities");
+ ...
+ \newcode
+ QSettings settings;
+ settings.beginGroup("cities");
+ QStringList keys = settings.childKeys();
+ ...
+ settings.endGroup();
+ \endcode
+*/
+
+/*! \fn QStringList QSettings::subkeyList(const QString &key) const
+
+ Returns a list of all sub-keys of \a key.
+
+ Use childGroups() instead.
+
+ \oldcode
+ QSettings settings;
+ QStringList groups = settings.entryList("cities");
+ ...
+ \newcode
+ QSettings settings;
+ settings.beginGroup("cities");
+ QStringList groups = settings.childKeys();
+ ...
+ settings.endGroup();
+ \endcode
+*/
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SETTINGS
diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h
new file mode 100644
index 0000000..cbb24ba
--- /dev/null
+++ b/src/corelib/io/qsettings.h
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSETTINGS_H
+#define QSETTINGS_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+QT_MODULE(Core)
+QT_END_NAMESPACE
+
+#ifndef QT_NO_SETTINGS
+
+#ifdef QT3_SUPPORT
+#include <QtCore/qstringlist.h>
+#endif
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Status // we seem to pick up a macro Status --> int somewhere
+#undef Status
+#endif
+
+class QIODevice;
+class QSettingsPrivate;
+
+#ifndef QT_NO_QOBJECT
+class Q_CORE_EXPORT QSettings : public QObject
+#else
+class Q_CORE_EXPORT QSettings
+#endif
+{
+#ifndef QT_NO_QOBJECT
+ Q_OBJECT
+#else
+ QSettingsPrivate *d_ptr;
+#endif
+ Q_DECLARE_PRIVATE(QSettings)
+
+public:
+ enum Status {
+ NoError = 0,
+ AccessError,
+ FormatError
+ };
+
+ enum Format {
+ NativeFormat,
+ IniFormat,
+
+ InvalidFormat = 16,
+ CustomFormat1,
+ CustomFormat2,
+ CustomFormat3,
+ CustomFormat4,
+ CustomFormat5,
+ CustomFormat6,
+ CustomFormat7,
+ CustomFormat8,
+ CustomFormat9,
+ CustomFormat10,
+ CustomFormat11,
+ CustomFormat12,
+ CustomFormat13,
+ CustomFormat14,
+ CustomFormat15,
+ CustomFormat16
+ };
+
+ enum Scope {
+ UserScope,
+ SystemScope
+#ifdef QT3_SUPPORT
+ ,
+ User = UserScope,
+ Global = SystemScope
+#endif
+ };
+
+#ifndef QT_NO_QOBJECT
+ explicit QSettings(const QString &organization,
+ const QString &application = QString(), QObject *parent = 0);
+ QSettings(Scope scope, const QString &organization,
+ const QString &application = QString(), QObject *parent = 0);
+ QSettings(Format format, Scope scope, const QString &organization,
+ const QString &application = QString(), QObject *parent = 0);
+ QSettings(const QString &fileName, Format format, QObject *parent = 0);
+ explicit QSettings(QObject *parent = 0);
+#else
+ explicit QSettings(const QString &organization,
+ const QString &application = QString());
+ QSettings(Scope scope, const QString &organization,
+ const QString &application = QString());
+ QSettings(Format format, Scope scope, const QString &organization,
+ const QString &application = QString());
+ QSettings(const QString &fileName, Format format);
+#endif
+ ~QSettings();
+
+ void clear();
+ void sync();
+ Status status() const;
+
+ void beginGroup(const QString &prefix);
+ void endGroup();
+ QString group() const;
+
+ int beginReadArray(const QString &prefix);
+ void beginWriteArray(const QString &prefix, int size = -1);
+ void endArray();
+ void setArrayIndex(int i);
+
+ QStringList allKeys() const;
+ QStringList childKeys() const;
+ QStringList childGroups() const;
+ bool isWritable() const;
+
+ void setValue(const QString &key, const QVariant &value);
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+
+ void remove(const QString &key);
+ bool contains(const QString &key) const;
+
+ void setFallbacksEnabled(bool b);
+ bool fallbacksEnabled() const;
+
+ QString fileName() const;
+ Format format() const;
+ Scope scope() const;
+ QString organizationName() const;
+ QString applicationName() const;
+
+#ifndef QT_NO_TEXTCODEC
+ void setIniCodec(QTextCodec *codec);
+ void setIniCodec(const char *codecName);
+ QTextCodec *iniCodec() const;
+#endif
+
+ static void setDefaultFormat(Format format);
+ static Format defaultFormat();
+ static void setSystemIniPath(const QString &dir); // ### remove in 5.0 (use setPath() instead)
+ static void setUserIniPath(const QString &dir); // ### remove in 5.0 (use setPath() instead)
+ static void setPath(Format format, Scope scope, const QString &path);
+
+ typedef QMap<QString, QVariant> SettingsMap;
+ typedef bool (*ReadFunc)(QIODevice &device, SettingsMap &map);
+ typedef bool (*WriteFunc)(QIODevice &device, const SettingsMap &map);
+
+ static Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc,
+ Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive);
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT bool writeEntry(const QString &key, bool value)
+ { setValue(key, value); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, double value)
+ { setValue(key, value); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, int value)
+ { setValue(key, value); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, const char *value)
+ { setValue(key, QString::fromAscii(value)); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, const QString &value)
+ { setValue(key, value); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, const QStringList &value)
+ { setValue(key, value); return isWritable(); }
+ inline QT3_SUPPORT bool writeEntry(const QString &key, const QStringList &value, QChar separator)
+ { setValue(key, value.join(QString(separator))); return isWritable(); }
+ inline QT3_SUPPORT QStringList readListEntry(const QString &key, bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ return value(key).toStringList();
+ }
+ inline QT3_SUPPORT QStringList readListEntry(const QString &key, QChar separator, bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ QString str = value(key).toString();
+ if (str.isEmpty())
+ return QStringList();
+ return str.split(separator);
+ }
+ inline QT3_SUPPORT QString readEntry(const QString &key, const QString &defaultValue = QString(),
+ bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ return value(key, defaultValue).toString();
+ }
+ inline QT3_SUPPORT int readNumEntry(const QString &key, int defaultValue = 0, bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ return value(key, defaultValue).toInt();
+ }
+ inline QT3_SUPPORT double readDoubleEntry(const QString &key, double defaultValue = 0,
+ bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ return value(key, defaultValue).toDouble();
+ }
+ inline QT3_SUPPORT bool readBoolEntry(const QString &key, bool defaultValue = false,
+ bool *ok = 0)
+ {
+ if (ok)
+ *ok = contains(key);
+ return value(key, defaultValue).toBool();
+ }
+ inline QT3_SUPPORT bool removeEntry(const QString &key)
+ { remove(key); return true; }
+
+ enum System { Unix, Windows, Mac };
+ inline QT3_SUPPORT void insertSearchPath(System, const QString &) {}
+ inline QT3_SUPPORT void removeSearchPath(System, const QString &) {}
+
+ inline QT3_SUPPORT void setPath(const QString &organization, const QString &application,
+ Scope scope = Global)
+ {
+ setPath_helper(scope == Global ? QSettings::SystemScope : QSettings::UserScope,
+ organization, application);
+ }
+ inline QT3_SUPPORT void resetGroup()
+ {
+ while (!group().isEmpty())
+ endGroup();
+ }
+ inline QT3_SUPPORT QStringList entryList(const QString &key) const
+ {
+ QSettings *that = const_cast<QSettings *>(this);
+ QStringList result;
+
+ that->beginGroup(key);
+ result = that->childKeys();
+ that->endGroup();
+ return result;
+ }
+ inline QT3_SUPPORT QStringList subkeyList(const QString &key) const
+ {
+ QSettings *that = const_cast<QSettings *>(this);
+ QStringList result;
+
+ that->beginGroup(key);
+ result = that->childGroups();
+ that->endGroup();
+ return result;
+ }
+#endif
+
+protected:
+#ifndef QT_NO_QOBJECT
+ bool event(QEvent *event);
+#endif
+
+private:
+#ifdef QT3_SUPPORT
+ void setPath_helper(Scope scope, const QString &organization, const QString &application);
+#endif
+
+ Q_DISABLE_COPY(QSettings)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SETTINGS
+
+QT_END_HEADER
+
+#endif // QSETTINGS_H
diff --git a/src/corelib/io/qsettings_mac.cpp b/src/corelib/io/qsettings_mac.cpp
new file mode 100644
index 0000000..9a9e2d7
--- /dev/null
+++ b/src/corelib/io/qsettings_mac.cpp
@@ -0,0 +1,654 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsettings.h"
+#include "qsettings_p.h"
+#include "qdatetime.h"
+#include "qdir.h"
+#include "qvarlengtharray.h"
+#include "private/qcore_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static const CFStringRef hostNames[2] = { kCFPreferencesCurrentHost, kCFPreferencesAnyHost };
+static const int numHostNames = 2;
+
+/*
+ On the Mac, it is more natural to use '.' as the key separator
+ than '/'. Therefore, it makes sense to replace '/' with '.' in
+ keys. Then we replace '.' with middle dots (which we can't show
+ here) and middle dots with '/'. A key like "4.0/BrowserCommand"
+ becomes "4<middot>0.BrowserCommand".
+*/
+
+enum RotateShift { Macify = 1, Qtify = 2 };
+
+static QString rotateSlashesDotsAndMiddots(const QString &key, int shift)
+{
+ static const int NumKnights = 3;
+ static const char knightsOfTheRoundTable[NumKnights] = { '/', '.', '\xb7' };
+ QString result = key;
+
+ for (int i = 0; i < result.size(); ++i) {
+ for (int j = 0; j < NumKnights; ++j) {
+ if (result.at(i) == QLatin1Char(knightsOfTheRoundTable[j])) {
+ result[i] = QLatin1Char(knightsOfTheRoundTable[(j + shift) % NumKnights]).unicode();
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static QCFType<CFStringRef> macKey(const QString &key)
+{
+ return QCFString::toCFStringRef(rotateSlashesDotsAndMiddots(key, Macify));
+}
+
+static QString qtKey(CFStringRef cfkey)
+{
+ return rotateSlashesDotsAndMiddots(QCFString::toQString(cfkey), Qtify);
+}
+
+static QCFType<CFPropertyListRef> macValue(const QVariant &value);
+
+static CFArrayRef macList(const QList<QVariant> &list)
+{
+ int n = list.size();
+ QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(n);
+ for (int i = 0; i < n; ++i)
+ cfvalues[i] = macValue(list.at(i));
+ return CFArrayCreate(kCFAllocatorDefault, reinterpret_cast<const void **>(cfvalues.data()),
+ CFIndex(n), &kCFTypeArrayCallBacks);
+}
+
+static QCFType<CFPropertyListRef> macValue(const QVariant &value)
+{
+ CFPropertyListRef result = 0;
+
+ switch (value.type()) {
+ case QVariant::ByteArray:
+ {
+ QByteArray ba = value.toByteArray();
+ result = CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(ba.data()),
+ CFIndex(ba.size()));
+ }
+ break;
+ // should be same as below (look for LIST)
+ case QVariant::List:
+ case QVariant::StringList:
+ case QVariant::Polygon:
+ result = macList(value.toList());
+ break;
+ case QVariant::Map:
+ {
+ /*
+ QMap<QString, QVariant> is potentially a multimap,
+ whereas CFDictionary is a single-valued map. To allow
+ for multiple values with the same key, we store
+ multiple values in a CFArray. To avoid ambiguities,
+ we also wrap lists in a CFArray singleton.
+ */
+ QMap<QString, QVariant> map = value.toMap();
+ QMap<QString, QVariant>::const_iterator i = map.constBegin();
+
+ int maxUniqueKeys = map.size();
+ int numUniqueKeys = 0;
+ QVarLengthArray<QCFType<CFPropertyListRef> > cfkeys(maxUniqueKeys);
+ QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(maxUniqueKeys);
+
+ while (i != map.constEnd()) {
+ const QString &key = i.key();
+ QList<QVariant> values;
+
+ do {
+ values << i.value();
+ ++i;
+ } while (i != map.constEnd() && i.key() == key);
+
+ bool singleton = (values.count() == 1);
+ if (singleton) {
+ switch (values.first().type()) {
+ // should be same as above (look for LIST)
+ case QVariant::List:
+ case QVariant::StringList:
+ case QVariant::Polygon:
+ singleton = false;
+ default:
+ ;
+ }
+ }
+
+ cfkeys[numUniqueKeys] = QCFString::toCFStringRef(key);
+ cfvalues[numUniqueKeys] = singleton ? macValue(values.first()) : macList(values);
+ ++numUniqueKeys;
+ }
+
+ result = CFDictionaryCreate(kCFAllocatorDefault,
+ reinterpret_cast<const void **>(cfkeys.data()),
+ reinterpret_cast<const void **>(cfvalues.data()),
+ CFIndex(numUniqueKeys),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ break;
+ case QVariant::DateTime:
+ {
+ /*
+ CFDate, unlike QDateTime, doesn't store timezone information.
+ */
+ QDateTime dt = value.toDateTime();
+ if (dt.timeSpec() == Qt::LocalTime) {
+ QDateTime reference;
+ reference.setTime_t((uint)kCFAbsoluteTimeIntervalSince1970);
+ result = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTime(reference.secsTo(dt)));
+ } else {
+ goto string_case;
+ }
+ }
+ break;
+ case QVariant::Bool:
+ result = value.toBool() ? kCFBooleanTrue : kCFBooleanFalse;
+ break;
+ case QVariant::Int:
+ case QVariant::UInt:
+ {
+ int n = value.toInt();
+ result = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n);
+ }
+ break;
+ case QVariant::Double:
+ {
+ double n = value.toDouble();
+ result = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &n);
+ }
+ break;
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ {
+ qint64 n = value.toLongLong();
+ result = CFNumberCreate(0, kCFNumberLongLongType, &n);
+ }
+ break;
+ case QVariant::String:
+ string_case:
+ default:
+ result = QCFString::toCFStringRef(QSettingsPrivate::variantToString(value));
+ }
+ return result;
+}
+
+static QVariant qtValue(CFPropertyListRef cfvalue)
+{
+ if (!cfvalue)
+ return QVariant();
+
+ CFTypeID typeId = CFGetTypeID(cfvalue);
+
+ /*
+ Sorted grossly from most to least frequent type.
+ */
+ if (typeId == CFStringGetTypeID()) {
+ return QSettingsPrivate::stringToVariant(QCFString::toQString(static_cast<CFStringRef>(cfvalue)));
+ } else if (typeId == CFNumberGetTypeID()) {
+ CFNumberRef cfnumber = static_cast<CFNumberRef>(cfvalue);
+ if (CFNumberIsFloatType(cfnumber)) {
+ double d;
+ CFNumberGetValue(cfnumber, kCFNumberDoubleType, &d);
+ return d;
+ } else {
+ int i;
+ qint64 ll;
+
+ if (CFNumberGetValue(cfnumber, kCFNumberIntType, &i))
+ return i;
+ CFNumberGetValue(cfnumber, kCFNumberLongLongType, &ll);
+ return ll;
+ }
+ } else if (typeId == CFArrayGetTypeID()) {
+ CFArrayRef cfarray = static_cast<CFArrayRef>(cfvalue);
+ QList<QVariant> list;
+ CFIndex size = CFArrayGetCount(cfarray);
+ bool metNonString = false;
+ for (CFIndex i = 0; i < size; ++i) {
+ QVariant value = qtValue(CFArrayGetValueAtIndex(cfarray, i));
+ if (value.type() != QVariant::String)
+ metNonString = true;
+ list << value;
+ }
+ if (metNonString)
+ return list;
+ else
+ return QVariant(list).toStringList();
+ } else if (typeId == CFBooleanGetTypeID()) {
+ return (bool)CFBooleanGetValue(static_cast<CFBooleanRef>(cfvalue));
+ } else if (typeId == CFDataGetTypeID()) {
+ CFDataRef cfdata = static_cast<CFDataRef>(cfvalue);
+ return QByteArray(reinterpret_cast<const char *>(CFDataGetBytePtr(cfdata)),
+ CFDataGetLength(cfdata));
+ } else if (typeId == CFDictionaryGetTypeID()) {
+ CFDictionaryRef cfdict = static_cast<CFDictionaryRef>(cfvalue);
+ CFTypeID arrayTypeId = CFArrayGetTypeID();
+ int size = (int)CFDictionaryGetCount(cfdict);
+ QVarLengthArray<CFPropertyListRef> keys(size);
+ QVarLengthArray<CFPropertyListRef> values(size);
+ CFDictionaryGetKeysAndValues(cfdict, keys.data(), values.data());
+
+ QMultiMap<QString, QVariant> map;
+ for (int i = 0; i < size; ++i) {
+ QString key = QCFString::toQString(static_cast<CFStringRef>(keys[i]));
+
+ if (CFGetTypeID(values[i]) == arrayTypeId) {
+ CFArrayRef cfarray = static_cast<CFArrayRef>(values[i]);
+ CFIndex arraySize = CFArrayGetCount(cfarray);
+ for (CFIndex j = arraySize - 1; j >= 0; --j)
+ map.insert(key, qtValue(CFArrayGetValueAtIndex(cfarray, j)));
+ } else {
+ map.insert(key, qtValue(values[i]));
+ }
+ }
+ return map;
+ } else if (typeId == CFDateGetTypeID()) {
+ QDateTime dt;
+ dt.setTime_t((uint)kCFAbsoluteTimeIntervalSince1970);
+ return dt.addSecs((int)CFDateGetAbsoluteTime(static_cast<CFDateRef>(cfvalue)));
+ }
+ return QVariant();
+}
+
+static QString comify(const QString &organization)
+{
+ for (int i = organization.size() - 1; i >= 0; --i) {
+ QChar ch = organization.at(i);
+ if (ch == QLatin1Char('.') || ch == QChar(0x3002) || ch == QChar(0xff0e)
+ || ch == QChar(0xff61)) {
+ QString suffix = organization.mid(i + 1).toLower();
+ if (suffix.size() == 2 || suffix == QLatin1String("com")
+ || suffix == QLatin1String("org") || suffix == QLatin1String("net")
+ || suffix == QLatin1String("edu") || suffix == QLatin1String("gov")
+ || suffix == QLatin1String("mil") || suffix == QLatin1String("biz")
+ || suffix == QLatin1String("info") || suffix == QLatin1String("name")
+ || suffix == QLatin1String("pro") || suffix == QLatin1String("aero")
+ || suffix == QLatin1String("coop") || suffix == QLatin1String("museum")) {
+ QString result = organization;
+ result.replace(QLatin1Char('/'), QLatin1Char(' '));
+ return result;
+ }
+ break;
+ }
+ int uc = ch.unicode();
+ if ((uc < 'a' || uc > 'z') && (uc < 'A' || uc > 'Z'))
+ break;
+ }
+
+ QString domain;
+ for (int i = 0; i < organization.size(); ++i) {
+ QChar ch = organization.at(i);
+ int uc = ch.unicode();
+ if ((uc >= 'a' && uc <= 'z') || (uc >= '0' && uc <= '9')) {
+ domain += ch;
+ } else if (uc >= 'A' && uc <= 'Z') {
+ domain += ch.toLower();
+ } else {
+ domain += QLatin1Char(' ');
+ }
+ }
+ domain = domain.simplified();
+ domain.replace(QLatin1Char(' '), QLatin1Char('-'));
+ if (!domain.isEmpty())
+ domain.append(QLatin1String(".com"));
+ return domain;
+}
+
+class QMacSettingsPrivate : public QSettingsPrivate
+{
+public:
+ QMacSettingsPrivate(QSettings::Scope scope, const QString &organization,
+ const QString &application);
+ ~QMacSettingsPrivate();
+
+ void remove(const QString &key);
+ void set(const QString &key, const QVariant &value);
+ bool get(const QString &key, QVariant *value) const;
+ QStringList children(const QString &prefix, ChildSpec spec) const;
+ void clear();
+ void sync();
+ void flush();
+ bool isWritable() const;
+ QString fileName() const;
+
+private:
+ struct SearchDomain
+ {
+ CFStringRef userName;
+ CFStringRef applicationOrSuiteId;
+ };
+
+ QCFString applicationId;
+ QCFString suiteId;
+ QCFString hostName;
+ SearchDomain domains[6];
+ int numDomains;
+};
+
+QMacSettingsPrivate::QMacSettingsPrivate(QSettings::Scope scope, const QString &organization,
+ const QString &application)
+ : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
+{
+ QString javaPackageName;
+ int curPos = 0;
+ int nextDot;
+
+ QString domainName = comify(organization);
+ if (domainName.isEmpty()) {
+ setStatus(QSettings::AccessError);
+ domainName = QLatin1String("unknown-organization.trolltech.com");
+ }
+
+ while ((nextDot = domainName.indexOf(QLatin1Char('.'), curPos)) != -1) {
+ javaPackageName.prepend(domainName.mid(curPos, nextDot - curPos));
+ javaPackageName.prepend(QLatin1Char('.'));
+ curPos = nextDot + 1;
+ }
+ javaPackageName.prepend(domainName.mid(curPos));
+ javaPackageName = javaPackageName.toLower();
+ if (curPos == 0)
+ javaPackageName.prepend(QLatin1String("com."));
+ suiteId = javaPackageName;
+
+ if (scope == QSettings::SystemScope)
+ spec |= F_System;
+
+ if (application.isEmpty()) {
+ spec |= F_Organization;
+ } else {
+ javaPackageName += QLatin1Char('.');
+ javaPackageName += application;
+ applicationId = javaPackageName;
+ }
+
+ numDomains = 0;
+ for (int i = (spec & F_System) ? 1 : 0; i < 2; ++i) {
+ for (int j = (spec & F_Organization) ? 1 : 0; j < 3; ++j) {
+ SearchDomain &domain = domains[numDomains++];
+ domain.userName = (i == 0) ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser;
+ if (j == 0)
+ domain.applicationOrSuiteId = applicationId;
+ else if (j == 1)
+ domain.applicationOrSuiteId = suiteId;
+ else
+ domain.applicationOrSuiteId = kCFPreferencesAnyApplication;
+ }
+ }
+
+ hostName = (scope == QSettings::SystemScope) ? kCFPreferencesCurrentHost : kCFPreferencesAnyHost;
+ sync();
+}
+
+QMacSettingsPrivate::~QMacSettingsPrivate()
+{
+}
+
+void QMacSettingsPrivate::remove(const QString &key)
+{
+ QStringList keys = children(key + QLatin1Char('/'), AllKeys);
+
+ // If i == -1, then delete "key" itself.
+ for (int i = -1; i < keys.size(); ++i) {
+ QString subKey = key;
+ if (i >= 0) {
+ subKey += QLatin1Char('/');
+ subKey += keys.at(i);
+ }
+ CFPreferencesSetValue(macKey(subKey), 0, domains[0].applicationOrSuiteId,
+ domains[0].userName, hostName);
+ }
+}
+
+void QMacSettingsPrivate::set(const QString &key, const QVariant &value)
+{
+ CFPreferencesSetValue(macKey(key), macValue(value), domains[0].applicationOrSuiteId,
+ domains[0].userName, hostName);
+}
+
+bool QMacSettingsPrivate::get(const QString &key, QVariant *value) const
+{
+ QCFString k = macKey(key);
+ for (int i = 0; i < numDomains; ++i) {
+ for (int j = 0; j < numHostNames; ++j) {
+ QCFType<CFPropertyListRef> ret =
+ CFPreferencesCopyValue(k, domains[i].applicationOrSuiteId, domains[i].userName,
+ hostNames[j]);
+ if (ret) {
+ if (value)
+ *value = qtValue(ret);
+ return true;
+ }
+ }
+
+ if (!fallbacks)
+ break;
+ }
+ return false;
+}
+
+QStringList QMacSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
+{
+ QMap<QString, QString> result;
+ int startPos = prefix.size();
+
+ for (int i = 0; i < numDomains; ++i) {
+ for (int j = 0; j < numHostNames; ++j) {
+ QCFType<CFArrayRef> cfarray = CFPreferencesCopyKeyList(domains[i].applicationOrSuiteId,
+ domains[i].userName,
+ hostNames[j]);
+ if (cfarray) {
+ CFIndex size = CFArrayGetCount(cfarray);
+ for (CFIndex k = 0; k < size; ++k) {
+ QString currentKey =
+ qtKey(static_cast<CFStringRef>(CFArrayGetValueAtIndex(cfarray, k)));
+ if (currentKey.startsWith(prefix))
+ processChild(currentKey.mid(startPos), spec, result);
+ }
+ }
+ }
+
+ if (!fallbacks)
+ break;
+ }
+ return result.keys();
+}
+
+void QMacSettingsPrivate::clear()
+{
+ QCFType<CFArrayRef> cfarray = CFPreferencesCopyKeyList(domains[0].applicationOrSuiteId,
+ domains[0].userName, hostName);
+ CFPreferencesSetMultiple(0, cfarray, domains[0].applicationOrSuiteId, domains[0].userName,
+ hostName);
+}
+
+void QMacSettingsPrivate::sync()
+{
+ for (int i = 0; i < numDomains; ++i) {
+ for (int j = 0; j < numHostNames; ++j) {
+ Boolean ok = CFPreferencesSynchronize(domains[i].applicationOrSuiteId,
+ domains[i].userName, hostNames[j]);
+ // only report failures for the primary file (the one we write to)
+ if (!ok && i == 0 && hostNames[j] == hostName && status == QSettings::NoError) {
+#if 1
+ // work around what seems to be a bug in CFPreferences:
+ // don't report an error if there are no preferences for the application
+ QCFType<CFArrayRef> appIds = CFPreferencesCopyApplicationList(domains[i].userName,
+ hostNames[j]);
+
+ // iterate through all the applications and see if we're there
+ CFIndex size = CFArrayGetCount(appIds);
+ for (CFIndex k = 0; k < size; ++k) {
+ const void *cfvalue = CFArrayGetValueAtIndex(appIds, k);
+ if (CFGetTypeID(cfvalue) == CFStringGetTypeID()) {
+ if (CFStringCompare(static_cast<CFStringRef>(cfvalue),
+ domains[i].applicationOrSuiteId,
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ setStatus(QSettings::AccessError);
+ break;
+ }
+ }
+ }
+#else
+ setStatus(QSettings::AccessError);
+#endif
+ }
+ }
+ }
+}
+
+void QMacSettingsPrivate::flush()
+{
+ sync();
+}
+
+bool QMacSettingsPrivate::isWritable() const
+{
+ QMacSettingsPrivate *that = const_cast<QMacSettingsPrivate *>(this);
+ QString impossibleKey(QLatin1String("qt_internal/"));
+
+ QSettings::Status oldStatus = that->status;
+ that->status = QSettings::NoError;
+
+ that->set(impossibleKey, QVariant());
+ that->sync();
+ bool writable = (status == QSettings::NoError) && that->get(impossibleKey, 0);
+ that->remove(impossibleKey);
+ that->sync();
+
+ that->status = oldStatus;
+ return writable;
+}
+
+QString QMacSettingsPrivate::fileName() const
+{
+ QString result;
+ if ((spec & F_System) == 0)
+ result = QDir::homePath();
+ result += QLatin1String("/Library/Preferences/");
+ result += QCFString::toQString(domains[0].applicationOrSuiteId);
+ result += QLatin1String(".plist");
+ return result;
+}
+
+QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,
+ QSettings::Scope scope,
+ const QString &organization,
+ const QString &application)
+{
+ if (format == QSettings::NativeFormat) {
+ return new QMacSettingsPrivate(scope, organization, application);
+ } else {
+ return new QConfFileSettingsPrivate(format, scope, organization, application);
+ }
+}
+
+static QCFType<CFURLRef> urlFromFileName(const QString &fileName)
+{
+ return CFURLCreateWithFileSystemPath(kCFAllocatorDefault, QCFString(fileName),
+ kCFURLPOSIXPathStyle, false);
+}
+
+bool QConfFileSettingsPrivate::readPlistFile(const QString &fileName, ParsedSettingsMap *map) const
+{
+ QCFType<CFDataRef> resource;
+ SInt32 code;
+ if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, urlFromFileName(fileName),
+ &resource, 0, 0, &code))
+ return false;
+
+ QCFString errorStr;
+ QCFType<CFPropertyListRef> propertyList =
+ CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable,
+ &errorStr);
+
+ if (!propertyList)
+ return true;
+ if (CFGetTypeID(propertyList) != CFDictionaryGetTypeID())
+ return false;
+
+ CFDictionaryRef cfdict =
+ static_cast<CFDictionaryRef>(static_cast<CFPropertyListRef>(propertyList));
+ int size = (int)CFDictionaryGetCount(cfdict);
+ QVarLengthArray<CFPropertyListRef> keys(size);
+ QVarLengthArray<CFPropertyListRef> values(size);
+ CFDictionaryGetKeysAndValues(cfdict, keys.data(), values.data());
+
+ for (int i = 0; i < size; ++i) {
+ QString key = qtKey(static_cast<CFStringRef>(keys[i]));
+ map->insert(QSettingsKey(key, Qt::CaseSensitive), qtValue(values[i]));
+ }
+ return true;
+}
+
+bool QConfFileSettingsPrivate::writePlistFile(const QString &fileName,
+ const ParsedSettingsMap &map) const
+{
+ QVarLengthArray<QCFType<CFStringRef> > cfkeys(map.size());
+ QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(map.size());
+ int i = 0;
+ ParsedSettingsMap::const_iterator j;
+ for (j = map.constBegin(); j != map.constEnd(); ++j) {
+ cfkeys[i] = macKey(j.key());
+ cfvalues[i] = macValue(j.value());
+ ++i;
+ }
+
+ QCFType<CFDictionaryRef> propertyList =
+ CFDictionaryCreate(kCFAllocatorDefault,
+ reinterpret_cast<const void **>(cfkeys.data()),
+ reinterpret_cast<const void **>(cfvalues.data()),
+ CFIndex(map.size()),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ QCFType<CFDataRef> xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, propertyList);
+
+ SInt32 code;
+ return CFURLWriteDataAndPropertiesToResource(urlFromFileName(fileName), xmlData, 0, &code);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h
new file mode 100644
index 0000000..dd72fd9
--- /dev/null
+++ b/src/corelib/io/qsettings_p.h
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSETTINGS_P_H
+#define QSETTINGS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qdatetime.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qmutex.h"
+#include "QtCore/qiodevice.h"
+#include "QtCore/qstack.h"
+#include "QtCore/qstringlist.h"
+#ifndef QT_NO_QOBJECT
+#include "private/qobject_p.h"
+#endif
+
+#ifdef Q_OS_WIN
+#include "QtCore/qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_WS_QWS)
+#define QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER
+#endif
+
+// used in testing framework
+#define QSETTINGS_P_H_VERSION 3
+
+#ifdef QT_QSETTINGS_ALWAYS_CASE_SENSITIVE_AND_FORGET_ORIGINAL_KEY_ORDER
+static const Qt::CaseSensitivity IniCaseSensitivity = Qt::CaseSensitive;
+
+class QSettingsKey : public QString
+{
+public:
+ inline QSettingsKey(const QString &key, Qt::CaseSensitivity cs, int /* position */ = -1)
+ : QString(key) { Q_ASSERT(cs == Qt::CaseSensitive); Q_UNUSED(cs); }
+
+ inline QString originalCaseKey() const { return *this; }
+ inline int originalKeyPosition() const { return -1; }
+};
+#else
+static const Qt::CaseSensitivity IniCaseSensitivity = Qt::CaseInsensitive;
+
+class QSettingsKey : public QString
+{
+public:
+ inline QSettingsKey(const QString &key, Qt::CaseSensitivity cs, int position = -1)
+ : QString(key), theOriginalKey(key), theOriginalKeyPosition(position)
+ {
+ if (cs == Qt::CaseInsensitive)
+ QString::operator=(toLower());
+ }
+
+ inline QString originalCaseKey() const { return theOriginalKey; }
+ inline int originalKeyPosition() const { return theOriginalKeyPosition; }
+
+private:
+ QString theOriginalKey;
+ int theOriginalKeyPosition;
+};
+#endif
+
+typedef QMap<QSettingsKey, QByteArray> UnparsedSettingsMap;
+typedef QMap<QSettingsKey, QVariant> ParsedSettingsMap;
+
+class QSettingsGroup
+{
+public:
+ inline QSettingsGroup()
+ : num(-1), maxNum(-1) {}
+ inline QSettingsGroup(const QString &s)
+ : str(s), num(-1), maxNum(-1) {}
+ inline QSettingsGroup(const QString &s, bool guessArraySize)
+ : str(s), num(0), maxNum(guessArraySize ? 0 : -1) {}
+
+ inline QString name() const { return str; }
+ inline QString toString() const;
+ inline bool isArray() const { return num != -1; }
+ inline int arraySizeGuess() const { return maxNum; }
+ inline void setArrayIndex(int i)
+ { num = i + 1; if (maxNum != -1 && num > maxNum) maxNum = num; }
+
+ QString str;
+ int num;
+ int maxNum;
+};
+
+inline QString QSettingsGroup::toString() const
+{
+ QString result;
+ result = str;
+ if (num > 0) {
+ result += QLatin1Char('/');
+ result += QString::number(num);
+ }
+ return result;
+}
+
+class Q_CORE_EXPORT QConfFile
+{
+public:
+ ParsedSettingsMap mergedKeyMap() const;
+ bool isWritable() const;
+
+ static QConfFile *fromName(const QString &name, bool _userPerms);
+ static void clearCache();
+
+ QString name;
+ QDateTime timeStamp;
+ qint64 size;
+ UnparsedSettingsMap unparsedIniSections;
+ ParsedSettingsMap originalKeys;
+ ParsedSettingsMap addedKeys;
+ ParsedSettingsMap removedKeys;
+ QAtomicInt ref;
+ QMutex mutex;
+ bool userPerms;
+
+private:
+#ifdef Q_DISABLE_COPY
+ QConfFile(const QConfFile &);
+ QConfFile &operator=(const QConfFile &);
+#endif
+ QConfFile(const QString &name, bool _userPerms);
+
+ friend class QConfFile_createsItself; // silences compiler warning
+};
+
+class Q_AUTOTEST_EXPORT QSettingsPrivate
+#ifndef QT_NO_QOBJECT
+ : public QObjectPrivate
+#endif
+{
+#ifdef QT_NO_QOBJECT
+ QSettings *q_ptr;
+#endif
+ Q_DECLARE_PUBLIC(QSettings)
+
+public:
+ QSettingsPrivate(QSettings::Format format);
+ QSettingsPrivate(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application);
+ virtual ~QSettingsPrivate();
+
+ virtual void remove(const QString &key) = 0;
+ virtual void set(const QString &key, const QVariant &value) = 0;
+ virtual bool get(const QString &key, QVariant *value) const = 0;
+
+ enum ChildSpec { AllKeys, ChildKeys, ChildGroups };
+ virtual QStringList children(const QString &prefix, ChildSpec spec) const = 0;
+
+ virtual void clear() = 0;
+ virtual void sync() = 0;
+ virtual void flush() = 0;
+ virtual bool isWritable() const = 0;
+ virtual QString fileName() const = 0;
+
+ QString actualKey(const QString &key) const;
+ void beginGroupOrArray(const QSettingsGroup &group);
+ void setStatus(QSettings::Status status) const;
+ void requestUpdate();
+ void update();
+
+ static QString normalizedKey(const QString &key);
+ static QSettingsPrivate *create(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application);
+ static QSettingsPrivate *create(const QString &fileName, QSettings::Format format);
+
+ static void processChild(QString key, ChildSpec spec, QMap<QString, QString> &result);
+
+ // Variant streaming functions
+ static QStringList variantListToStringList(const QVariantList &l);
+ static QVariant stringListToVariantList(const QStringList &l);
+
+ // parser functions
+ static QString variantToString(const QVariant &v);
+ static QVariant stringToVariant(const QString &s);
+ static void iniEscapedKey(const QString &key, QByteArray &result);
+ static bool iniUnescapedKey(const QByteArray &key, int from, int to, QString &result);
+ static void iniEscapedString(const QString &str, QByteArray &result, QTextCodec *codec);
+ static void iniEscapedStringList(const QStringList &strs, QByteArray &result, QTextCodec *codec);
+ static bool iniUnescapedStringList(const QByteArray &str, int from, int to,
+ QString &stringResult, QStringList &stringListResult,
+ QTextCodec *codec);
+ static QStringList splitArgs(const QString &s, int idx);
+
+ /*
+ The numeric values of these enums define their search order. For example,
+ F_User | F_Organization is searched before F_System | F_Application,
+ because their values are respectively 1 and 2.
+ */
+ enum {
+ F_Application = 0x0,
+ F_Organization = 0x1,
+ F_User = 0x0,
+ F_System = 0x2,
+ NumConfFiles = 4
+ };
+
+ QSettings::Format format;
+ QSettings::Scope scope;
+ QString organizationName;
+ QString applicationName;
+ QTextCodec *iniCodec;
+
+protected:
+ QStack<QSettingsGroup> groupStack;
+ QString groupPrefix;
+ int spec;
+ bool fallbacks;
+ bool pendingChanges;
+ mutable QSettings::Status status;
+};
+
+class QConfFileSettingsPrivate : public QSettingsPrivate
+{
+public:
+ QConfFileSettingsPrivate(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application);
+ QConfFileSettingsPrivate(const QString &fileName, QSettings::Format format);
+ ~QConfFileSettingsPrivate();
+
+ void remove(const QString &key);
+ void set(const QString &key, const QVariant &value);
+ bool get(const QString &key, QVariant *value) const;
+
+ QStringList children(const QString &prefix, ChildSpec spec) const;
+
+ void clear();
+ void sync();
+ void flush();
+ bool isWritable() const;
+ QString fileName() const;
+
+ static bool readIniFile(const QByteArray &data, UnparsedSettingsMap *unparsedIniSections);
+ static bool readIniSection(const QSettingsKey &section, const QByteArray &data,
+ ParsedSettingsMap *settingsMap, QTextCodec *codec);
+ static bool readIniLine(const QByteArray &data, int &dataPos, int &lineStart, int &lineLen,
+ int &equalsPos);
+
+private:
+ void initFormat();
+ void initAccess();
+ void syncConfFile(int confFileNo);
+ bool writeIniFile(QIODevice &device, const ParsedSettingsMap &map);
+#ifdef Q_OS_MAC
+ bool readPlistFile(const QString &fileName, ParsedSettingsMap *map) const;
+ bool writePlistFile(const QString &fileName, const ParsedSettingsMap &map) const;
+#endif
+ void ensureAllSectionsParsed(QConfFile *confFile) const;
+ void ensureSectionParsed(QConfFile *confFile, const QSettingsKey &key) const;
+
+ QConfFile *confFiles[NumConfFiles];
+ QSettings::ReadFunc readFunc;
+ QSettings::WriteFunc writeFunc;
+ QString extension;
+ Qt::CaseSensitivity caseSensitivity;
+ int nextPosition;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSETTINGS_P_H
diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp
new file mode 100644
index 0000000..67d2eeb
--- /dev/null
+++ b/src/corelib/io/qsettings_win.cpp
@@ -0,0 +1,957 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsettings.h"
+
+#ifndef QT_NO_SETTINGS
+
+#include "qsettings_p.h"
+#include "qvector.h"
+#include "qmap.h"
+#include "qt_windows.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user"
+ key, ie. "foo/bar/alpha/beta". If the variable name starts with 'r', this is a "registry"
+ key, ie. "\foo\bar\alpha\beta". */
+
+/*******************************************************************************
+** Some convenience functions
+*/
+
+/*
+ We don't use KEY_ALL_ACCESS because it gives more rights than what we
+ need. See task 199061.
+ */
+static const REGSAM registryPermissions = KEY_READ | KEY_WRITE;
+
+static QString keyPath(const QString &rKey)
+{
+ int idx = rKey.lastIndexOf(QLatin1Char('\\'));
+ if (idx == -1)
+ return QString();
+ return rKey.left(idx + 1);
+}
+
+static QString keyName(const QString &rKey)
+{
+ int idx = rKey.lastIndexOf(QLatin1Char('\\'));
+
+ QString res;
+ if (idx == -1)
+ res = rKey;
+ else
+ res = rKey.mid(idx + 1);
+
+ if (res == QLatin1String("Default") || res == QLatin1String("."))
+ res = QLatin1String("");
+
+ return res;
+}
+
+static QString escapedKey(QString uKey)
+{
+ QChar *data = uKey.data();
+ int l = uKey.length();
+ for (int i = 0; i < l; ++i) {
+ ushort &ucs = data[i].unicode();
+ if (ucs == '\\')
+ ucs = '/';
+ else if (ucs == '/')
+ ucs = '\\';
+ }
+ return uKey;
+}
+
+static QString unescapedKey(QString rKey)
+{
+ return escapedKey(rKey);
+}
+
+typedef QMap<QString, QString> NameSet;
+
+static void mergeKeySets(NameSet *dest, const NameSet &src)
+{
+ NameSet::const_iterator it = src.constBegin();
+ for (; it != src.constEnd(); ++it)
+ dest->insert(unescapedKey(it.key()), QString());
+}
+
+static void mergeKeySets(NameSet *dest, const QStringList &src)
+{
+ QStringList::const_iterator it = src.constBegin();
+ for (; it != src.constEnd(); ++it)
+ dest->insert(unescapedKey(*it), QString());
+}
+
+/*******************************************************************************
+** Wrappers for the insane windows registry API
+*/
+
+static QString errorCodeToString(DWORD errorCode)
+{
+ QString result;
+ QT_WA({
+ wchar_t *data = 0;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, errorCode, 0,
+ data, 0, 0);
+ result = QString::fromUtf16(reinterpret_cast<const ushort *> (data));
+ if (data != 0)
+ LocalFree(data);
+ }, {
+ char *data = 0;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, errorCode, 0,
+ (char *)&data, 0, 0);
+ result = QString::fromLocal8Bit(data);
+ if (data != 0)
+ LocalFree(data);
+ })
+ if (result.endsWith(QLatin1String("\n")))
+ result.truncate(result.length() - 1);
+
+ return result;
+}
+
+// Open a key with the specified perms
+static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey)
+{
+ HKEY resultHandle = 0;
+
+ LONG res;
+ QT_WA( {
+ res = RegOpenKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()),
+ 0, perms, &resultHandle);
+ } , {
+ res = RegOpenKeyExA(parentHandle, rSubKey.toLocal8Bit(),
+ 0, perms, &resultHandle);
+ } );
+
+ if (res == ERROR_SUCCESS)
+ return resultHandle;
+
+ return 0;
+}
+
+// Open a key with the specified perms, create it if it does not exist
+static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey)
+{
+ // try to open it
+ HKEY resultHandle = openKey(parentHandle, perms, rSubKey);
+ if (resultHandle != 0)
+ return resultHandle;
+
+ // try to create it
+ LONG res;
+ QT_WA( {
+ res = RegCreateKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, 0,
+ REG_OPTION_NON_VOLATILE, perms, 0, &resultHandle, 0);
+ } , {
+ res = RegCreateKeyExA(parentHandle, rSubKey.toLocal8Bit(), 0, 0,
+ REG_OPTION_NON_VOLATILE, perms, 0, &resultHandle, 0);
+ } );
+
+ if (res == ERROR_SUCCESS)
+ return resultHandle;
+
+ //qWarning("QSettings: Failed to create subkey \"%s\": %s",
+ // rSubKey.toLatin1().data(), errorCodeToString(res).toLatin1().data());
+
+ return 0;
+}
+
+// Open or create a key in read-write mode if possible, otherwise read-only
+static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly)
+{
+ // try to open or create it read/write
+ HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey);
+ if (resultHandle != 0) {
+ if (readOnly != 0)
+ *readOnly = false;
+ return resultHandle;
+ }
+
+ // try to open or create it read/only
+ resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey);
+ if (resultHandle != 0) {
+ if (readOnly != 0)
+ *readOnly = true;
+ return resultHandle;
+ }
+ return 0;
+}
+
+static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildSpec spec)
+{
+ QStringList result;
+ LONG res;
+ DWORD numKeys;
+ DWORD maxKeySize;
+ DWORD numSubgroups;
+ DWORD maxSubgroupSize;
+
+ // Find the number of keys and subgroups, as well as the max of their lengths.
+ QT_WA( {
+ res = RegQueryInfoKeyW(parentHandle, 0, 0, 0, &numSubgroups, &maxSubgroupSize, 0,
+ &numKeys, &maxKeySize, 0, 0, 0);
+ }, {
+ res = RegQueryInfoKeyA(parentHandle, 0, 0, 0, &numSubgroups, &maxSubgroupSize, 0,
+ &numKeys, &maxKeySize, 0, 0, 0);
+ } );
+
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: RegQueryInfoKey() failed: %s", errorCodeToString(res).toLatin1().data());
+ return result;
+ }
+
+ ++maxSubgroupSize;
+ ++maxKeySize;
+
+ int n;
+ int m;
+ if (spec == QSettingsPrivate::ChildKeys) {
+ n = numKeys;
+ m = maxKeySize;
+ } else {
+ n = numSubgroups;
+ m = maxSubgroupSize;
+ }
+
+ /* Windows NT/2000/XP: The size does not include the terminating null character.
+ Windows Me/98/95: The size includes the terminating null character. */
+ ++m;
+
+ // Get the list
+ QByteArray buff(m*sizeof(ushort), 0);
+ for (int i = 0; i < n; ++i) {
+ QString item;
+ QT_WA( {
+ DWORD l = buff.size() / sizeof(ushort);
+ if (spec == QSettingsPrivate::ChildKeys) {
+ res = RegEnumValueW(parentHandle, i,
+ reinterpret_cast<wchar_t *>(buff.data()),
+ &l, 0, 0, 0, 0);
+ } else {
+ res = RegEnumKeyExW(parentHandle, i,
+ reinterpret_cast<wchar_t *>(buff.data()),
+ &l, 0, 0, 0, 0);
+ }
+ if (res == ERROR_SUCCESS)
+ item = QString::fromUtf16(reinterpret_cast<ushort*>(buff.data()), l);
+ }, {
+ DWORD l = buff.size();
+ if (spec == QSettingsPrivate::ChildKeys)
+ res = RegEnumValueA(parentHandle, i, buff.data(), &l, 0, 0, 0, 0);
+ else
+ res = RegEnumKeyExA(parentHandle, i, buff.data(), &l, 0, 0, 0, 0);
+ if (res == ERROR_SUCCESS)
+ item = QString::fromLocal8Bit(buff.data(), l);
+ } );
+
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: RegEnumValue failed: %s", errorCodeToString(res).toLatin1().data());
+ continue;
+ }
+ if (item.isEmpty())
+ item = QLatin1String(".");
+ result.append(item);
+ }
+ return result;
+}
+
+static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result)
+{
+ HKEY handle = openKey(parentHandle, KEY_READ, rSubKey);
+ if (handle == 0)
+ return;
+
+ QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
+ QStringList childGroups = childKeysOrGroups(handle, QSettingsPrivate::ChildGroups);
+ RegCloseKey(handle);
+
+ for (int i = 0; i < childKeys.size(); ++i) {
+ QString s = rSubKey;
+ if (!s.isEmpty())
+ s += QLatin1Char('\\');
+ s += childKeys.at(i);
+ result->insert(s, QString());
+ }
+
+ for (int i = 0; i < childGroups.size(); ++i) {
+ QString s = rSubKey;
+ if (!s.isEmpty())
+ s += QLatin1Char('\\');
+ s += childGroups.at(i);
+ allKeys(parentHandle, s, result);
+ }
+}
+
+static void deleteChildGroups(HKEY parentHandle)
+{
+ QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups);
+
+ for (int i = 0; i < childGroups.size(); ++i) {
+ QString group = childGroups.at(i);
+
+ // delete subgroups in group
+ HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group);
+ if (childGroupHandle == 0)
+ continue;
+ deleteChildGroups(childGroupHandle);
+ RegCloseKey(childGroupHandle);
+
+ // delete group itself
+ LONG res;
+ QT_WA( {
+ res = RegDeleteKeyW(parentHandle, reinterpret_cast<const wchar_t *>(group.utf16()));
+ }, {
+ res = RegDeleteKeyA(parentHandle, group.toLocal8Bit());
+ } );
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: RegDeleteKey failed on subkey \"%s\": %s",
+ group.toLatin1().data(), errorCodeToString(res).toLatin1().data());
+ return;
+ }
+ }
+}
+
+/*******************************************************************************
+** class RegistryKey
+*/
+
+class RegistryKey
+{
+public:
+ RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true);
+ QString key() const;
+ HKEY handle() const;
+ HKEY parentHandle() const;
+ bool readOnly() const;
+ void close();
+private:
+ HKEY m_parent_handle;
+ mutable HKEY m_handle;
+ QString m_key;
+ mutable bool m_read_only;
+};
+
+RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only)
+{
+ m_parent_handle = parent_handle;
+ m_handle = 0;
+ m_read_only = read_only;
+ m_key = key;
+}
+
+QString RegistryKey::key() const
+{
+ return m_key;
+}
+
+HKEY RegistryKey::handle() const
+{
+ if (m_handle != 0)
+ return m_handle;
+
+ if (m_read_only)
+ m_handle = openKey(m_parent_handle, KEY_READ, m_key);
+ else
+ m_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only);
+
+ return m_handle;
+}
+
+HKEY RegistryKey::parentHandle() const
+{
+ return m_parent_handle;
+}
+
+bool RegistryKey::readOnly() const
+{
+ return m_read_only;
+}
+
+void RegistryKey::close()
+{
+ if (m_handle != 0)
+ RegCloseKey(m_handle);
+ m_handle = 0;
+}
+
+typedef QVector<RegistryKey> RegistryKeyList;
+
+/*******************************************************************************
+** class QWinSettingsPrivate
+*/
+
+class QWinSettingsPrivate : public QSettingsPrivate
+{
+public:
+ QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
+ const QString &application);
+ QWinSettingsPrivate(QString rKey);
+ ~QWinSettingsPrivate();
+
+ void remove(const QString &uKey);
+ void set(const QString &uKey, const QVariant &value);
+ bool get(const QString &uKey, QVariant *value) const;
+ QStringList children(const QString &uKey, ChildSpec spec) const;
+ void clear();
+ void sync();
+ void flush();
+ bool isWritable() const;
+ HKEY writeHandle() const;
+ bool readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const;
+ QString fileName() const;
+
+private:
+ RegistryKeyList regList; // list of registry locations to search for keys
+ bool deleteWriteHandleOnExit;
+};
+
+QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
+ const QString &application)
+ : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
+{
+ deleteWriteHandleOnExit = false;
+
+ if (!organization.isEmpty()) {
+ QString prefix = QLatin1String("Software\\") + organization;
+ QString orgPrefix = prefix + QLatin1String("\\OrganizationDefaults");
+ QString appPrefix = prefix + QLatin1Char('\\') + application;
+
+ if (scope == QSettings::UserScope) {
+ if (!application.isEmpty())
+ regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty()));
+
+ regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty()));
+ }
+
+ if (!application.isEmpty())
+ regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty()));
+
+ regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty()));
+ }
+
+ if (regList.isEmpty())
+ setStatus(QSettings::AccessError);
+}
+
+QWinSettingsPrivate::QWinSettingsPrivate(QString rPath)
+ : QSettingsPrivate(QSettings::NativeFormat)
+{
+ deleteWriteHandleOnExit = false;
+
+ if (rPath.startsWith(QLatin1String("\\")))
+ rPath = rPath.mid(1);
+
+ if (rPath.startsWith(QLatin1String("HKEY_CURRENT_USER\\")))
+ regList.append(RegistryKey(HKEY_CURRENT_USER, rPath.mid(18), false));
+ else if (rPath == QLatin1String("HKEY_CURRENT_USER"))
+ regList.append(RegistryKey(HKEY_CURRENT_USER, QString(), false));
+ else if (rPath.startsWith(QLatin1String("HKEY_LOCAL_MACHINE\\")))
+ regList.append(RegistryKey(HKEY_LOCAL_MACHINE, rPath.mid(19), false));
+ else if (rPath == QLatin1String("HKEY_LOCAL_MACHINE"))
+ regList.append(RegistryKey(HKEY_LOCAL_MACHINE, QString(), false));
+ else if (rPath.startsWith(QLatin1String("HKEY_CLASSES_ROOT\\")))
+ regList.append(RegistryKey(HKEY_CLASSES_ROOT, rPath.mid(18), false));
+ else if (rPath == QLatin1String("HKEY_CLASSES_ROOT"))
+ regList.append(RegistryKey(HKEY_CLASSES_ROOT, QString(), false));
+ else if (rPath.startsWith(QLatin1String("HKEY_USERS\\")))
+ regList.append(RegistryKey(HKEY_USERS, rPath.mid(11), false));
+ else if (rPath == QLatin1String(QLatin1String("HKEY_USERS")))
+ regList.append(RegistryKey(HKEY_USERS, QString(), false));
+ else
+ regList.append(RegistryKey(HKEY_LOCAL_MACHINE, rPath, false));
+}
+
+bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const
+{
+ QString rSubkeyName = keyName(rSubKey);
+ QString rSubkeyPath = keyPath(rSubKey);
+
+ // open a handle on the subkey
+ HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath);
+ if (handle == 0)
+ return false;
+
+ // get the size and type of the value
+ DWORD dataType;
+ DWORD dataSize;
+ LONG res;
+ QT_WA( {
+ res = RegQueryValueExW(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, &dataType, 0, &dataSize);
+ }, {
+ res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, &dataType, 0, &dataSize);
+ } );
+ if (res != ERROR_SUCCESS) {
+ RegCloseKey(handle);
+ return false;
+ }
+
+ // get the value
+ QByteArray data(dataSize, 0);
+ QT_WA( {
+ res = RegQueryValueExW(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, 0,
+ reinterpret_cast<unsigned char*>(data.data()), &dataSize);
+ }, {
+ res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, 0,
+ reinterpret_cast<unsigned char*>(data.data()), &dataSize);
+ } );
+ if (res != ERROR_SUCCESS) {
+ RegCloseKey(handle);
+ return false;
+ }
+
+ switch (dataType) {
+ case REG_EXPAND_SZ:
+ case REG_SZ: {
+ QString s;
+ QT_WA( {
+ s = QString::fromUtf16(((const ushort*)data.constData()));
+ }, {
+ s = QString::fromLocal8Bit(data.constData());
+ } );
+
+ if (value != 0)
+ *value = stringToVariant(s);
+ break;
+ }
+
+ case REG_MULTI_SZ: {
+ QStringList l;
+ int i = 0;
+ for (;;) {
+ QString s;
+ QT_WA( {
+ s = QString::fromUtf16((const ushort*)data.constData() + i);
+ }, {
+ s = QString::fromLocal8Bit(data.constData() + i);
+ } );
+ i += s.length() + 1;
+
+ if (s.isEmpty())
+ break;
+ l.append(s);
+ }
+ if (value != 0)
+ *value = stringListToVariantList(l);
+ break;
+ }
+
+ case REG_NONE:
+ case REG_BINARY: {
+ QString s;
+ QT_WA( {
+ s = QString::fromUtf16((const ushort*)data.constData(), data.size()/2);
+ }, {
+ s = QString::fromLocal8Bit(data.constData(), data.size());
+ } );
+ if (value != 0)
+ *value = stringToVariant(s);
+ break;
+ }
+
+ case REG_DWORD_BIG_ENDIAN:
+ case REG_DWORD: {
+ Q_ASSERT(data.size() == sizeof(int));
+ int i;
+ memcpy((char*)&i, data.constData(), sizeof(int));
+ if (value != 0)
+ *value = i;
+ break;
+ }
+
+ default:
+ qWarning("QSettings: Unknown data %d type in Windows registry", static_cast<int>(dataType));
+ if (value != 0)
+ *value = QVariant();
+ break;
+ }
+
+ RegCloseKey(handle);
+ return true;
+}
+
+HKEY QWinSettingsPrivate::writeHandle() const
+{
+ if (regList.isEmpty())
+ return 0;
+ const RegistryKey &key = regList.at(0);
+ if (key.handle() == 0 || key.readOnly())
+ return 0;
+ return key.handle();
+}
+
+QWinSettingsPrivate::~QWinSettingsPrivate()
+{
+ if (deleteWriteHandleOnExit && writeHandle() != 0) {
+#if defined(Q_OS_WINCE)
+ remove(regList.at(0).key());
+#else
+ DWORD res;
+ QString emptyKey;
+ QT_WA( {
+ res = RegDeleteKeyW(writeHandle(), reinterpret_cast<const wchar_t *>(emptyKey.utf16()));
+ }, {
+ res = RegDeleteKeyA(writeHandle(), emptyKey.toLocal8Bit());
+ } );
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: Failed to delete key \"%s\": %s",
+ regList.at(0).key().toLatin1().data(), errorCodeToString(res).toLatin1().data());
+ }
+#endif
+ }
+
+ for (int i = 0; i < regList.size(); ++i)
+ regList[i].close();
+}
+
+void QWinSettingsPrivate::remove(const QString &uKey)
+{
+ if (writeHandle() == 0) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
+ QString rKey = escapedKey(uKey);
+
+ // try to delete value bar in key foo
+ LONG res;
+ HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey));
+ if (handle != 0) {
+ QT_WA( {
+ res = RegDeleteValueW(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()));
+ }, {
+ res = RegDeleteValueA(handle, keyName(rKey).toLocal8Bit());
+ } );
+ RegCloseKey(handle);
+ }
+
+ // try to delete key foo/bar and all subkeys
+ handle = openKey(writeHandle(), registryPermissions, rKey);
+ if (handle != 0) {
+ deleteChildGroups(handle);
+
+ if (rKey.isEmpty()) {
+ QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
+
+ for (int i = 0; i < childKeys.size(); ++i) {
+ QString group = childKeys.at(i);
+
+ LONG res;
+ QT_WA( {
+ res = RegDeleteValueW(handle, reinterpret_cast<const wchar_t *>(group.utf16()));
+ }, {
+ res = RegDeleteValueA(handle, group.toLocal8Bit());
+ } );
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: RegDeleteValue failed on subkey \"%s\": %s",
+ group.toLatin1().data(), errorCodeToString(res).toLatin1().data());
+ }
+ }
+ } else {
+#if defined(Q_OS_WINCE)
+ // For WinCE always Close the handle first.
+ RegCloseKey(handle);
+#endif
+ QT_WA( {
+ res = RegDeleteKeyW(writeHandle(), reinterpret_cast<const wchar_t *>(rKey.utf16()));
+ }, {
+ res = RegDeleteKeyA(writeHandle(), rKey.toLocal8Bit());
+ } );
+
+ if (res != ERROR_SUCCESS) {
+ qWarning("QSettings: RegDeleteKey failed on key \"%s\": %s",
+ rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data());
+ }
+ }
+ RegCloseKey(handle);
+ }
+}
+
+static bool stringContainsNullChar(const QString &s)
+{
+ for (int i = 0; i < s.length(); ++i) {
+ if (s.at(i).unicode() == 0)
+ return true;
+ }
+ return false;
+}
+
+void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value)
+{
+ if (writeHandle() == 0) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
+ QString rKey = escapedKey(uKey);
+
+ HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey));
+ if (handle == 0) {
+ setStatus(QSettings::AccessError);
+ return;
+ }
+
+ DWORD type;
+ QByteArray regValueBuff;
+
+ // Determine the type
+ switch (value.type()) {
+ case QVariant::List:
+ case QVariant::StringList: {
+ // If none of the elements contains '\0', we can use REG_MULTI_SZ, the
+ // native registry string list type. Otherwise we use REG_BINARY.
+ type = REG_MULTI_SZ;
+ QStringList l = variantListToStringList(value.toList());
+ QStringList::const_iterator it = l.constBegin();
+ for (; it != l.constEnd(); ++it) {
+ if ((*it).length() == 0 || stringContainsNullChar(*it)) {
+ type = REG_BINARY;
+ break;
+ }
+ }
+
+ if (type == REG_BINARY) {
+ QString s = variantToString(value);
+ QT_WA( {
+ regValueBuff = QByteArray((const char*)s.utf16(), s.length()*2);
+ }, {
+ regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length());
+ } );
+ } else {
+ QStringList::const_iterator it = l.constBegin();
+ for (; it != l.constEnd(); ++it) {
+ const QString &s = *it;
+ QT_WA( {
+ regValueBuff += QByteArray((const char*)s.utf16(), (s.length() + 1)*2);
+ }, {
+ regValueBuff += QByteArray((const char*)s.toLocal8Bit(), s.length() + 1);
+ } );
+ }
+ QT_WA( {
+ regValueBuff.append((char)0);
+ regValueBuff.append((char)0);
+ }, {
+ regValueBuff.append((char)0);
+ } );
+ }
+ break;
+ }
+
+ case QVariant::Int: {
+ type = REG_DWORD;
+ int i = value.toInt();
+ regValueBuff = QByteArray((const char*)&i, sizeof(int));
+ break;
+ }
+
+ case QVariant::ByteArray:
+ // On Win95/98/Me QString::toLocal8Bit() fails to handle chars > 0x7F. So we don't go through variantToString() at all.
+ if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) {
+ QByteArray ba = value.toByteArray();
+ regValueBuff = "@ByteArray(";
+ regValueBuff += ba;
+ regValueBuff += ')';
+ if (ba.contains('\0')) {
+ type = REG_BINARY;
+ } else {
+ type = REG_SZ;
+ regValueBuff += '\0';
+ }
+
+ break;
+ }
+ // fallthrough intended
+
+ default: {
+ // If the string does not contain '\0', we can use REG_SZ, the native registry
+ // string type. Otherwise we use REG_BINARY.
+ QString s = variantToString(value);
+ type = stringContainsNullChar(s) ? REG_BINARY : REG_SZ;
+ if (type == REG_BINARY) {
+ QT_WA( {
+ regValueBuff = QByteArray((const char*)s.utf16(), s.length()*2);
+ }, {
+ regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length());
+ } );
+ } else {
+ QT_WA( {
+ regValueBuff = QByteArray((const char*)s.utf16(), (s.length() + 1)*2);
+ }, {
+ regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length() + 1);
+ } );
+ }
+ break;
+ }
+ }
+
+ // set the value
+ LONG res;
+ QT_WA( {
+ res = RegSetValueExW(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()), 0, type,
+ reinterpret_cast<const unsigned char*>(regValueBuff.constData()),
+ regValueBuff.size());
+ }, {
+ res = RegSetValueExA(handle, keyName(rKey).toLocal8Bit(), 0, type,
+ reinterpret_cast<const unsigned char*>(regValueBuff.constData()),
+ regValueBuff.size());
+ } );
+
+ if (res == ERROR_SUCCESS) {
+ deleteWriteHandleOnExit = false;
+ } else {
+ qWarning("QSettings: failed to set subkey \"%s\": %s",
+ rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data());
+ setStatus(QSettings::AccessError);
+ }
+
+ RegCloseKey(handle);
+}
+
+bool QWinSettingsPrivate::get(const QString &uKey, QVariant *value) const
+{
+ QString rKey = escapedKey(uKey);
+
+ for (int i = 0; i < regList.size(); ++i) {
+ HKEY handle = regList.at(i).handle();
+ if (handle != 0 && readKey(handle, rKey, value))
+ return true;
+
+ if (!fallbacks)
+ return false;
+ }
+
+ return false;
+}
+
+QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) const
+{
+ NameSet result;
+ QString rKey = escapedKey(uKey);
+
+ for (int i = 0; i < regList.size(); ++i) {
+ HKEY parent_handle = regList.at(i).handle();
+ if (parent_handle == 0)
+ continue;
+ HKEY handle = openKey(parent_handle, KEY_READ, rKey);
+ if (handle == 0)
+ continue;
+
+ if (spec == AllKeys) {
+ NameSet keys;
+ allKeys(handle, QLatin1String(""), &keys);
+ mergeKeySets(&result, keys);
+ } else { // ChildGroups or ChildKeys
+ QStringList names = childKeysOrGroups(handle, spec);
+ mergeKeySets(&result, names);
+ }
+
+ RegCloseKey(handle);
+
+ if (!fallbacks)
+ return result.keys();
+ }
+
+ return result.keys();
+}
+
+void QWinSettingsPrivate::clear()
+{
+ remove(QString());
+ deleteWriteHandleOnExit = true;
+}
+
+void QWinSettingsPrivate::sync()
+{
+ RegFlushKey(writeHandle());
+}
+
+void QWinSettingsPrivate::flush()
+{
+ // Windows does this for us.
+}
+
+QString QWinSettingsPrivate::fileName() const
+{
+ if (regList.isEmpty())
+ return QString();
+
+ const RegistryKey &key = regList.at(0);
+ QString result;
+ if (key.parentHandle() == HKEY_CURRENT_USER)
+ result = QLatin1String("\\HKEY_CURRENT_USER\\");
+ else
+ result = QLatin1String("\\HKEY_LOCAL_MACHINE\\");
+
+ return result + regList.at(0).key();
+}
+
+bool QWinSettingsPrivate::isWritable() const
+{
+ return writeHandle() != 0;
+}
+
+QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
+ const QString &organization, const QString &application)
+{
+ if (format == QSettings::NativeFormat) {
+ return new QWinSettingsPrivate(scope, organization, application);
+ } else {
+ return new QConfFileSettingsPrivate(format, scope, organization, application);
+ }
+}
+
+QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
+{
+ if (format == QSettings::NativeFormat) {
+ return new QWinSettingsPrivate(fileName);
+ } else {
+ return new QConfFileSettingsPrivate(fileName, format);
+ }
+}
+
+QT_END_NAMESPACE
+#endif // QT_NO_SETTINGS
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
new file mode 100644
index 0000000..3cfce83
--- /dev/null
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -0,0 +1,706 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtemporaryfile.h"
+
+#ifndef QT_NO_TEMPORARYFILE
+
+#include "qplatformdefs.h"
+#include "qabstractfileengine.h"
+#include "private/qfile_p.h"
+#include "private/qabstractfileengine_p.h"
+#include "private/qfsfileengine_p.h"
+
+#include <stdlib.h>
+#if !defined(Q_OS_WINCE)
+# include <errno.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+#endif
+
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+# include <process.h>
+# if defined(_MSC_VER) && _MSC_VER >= 1400
+# include <share.h>
+# endif
+#endif
+
+#if defined(Q_OS_WINCE)
+# include <types.h>
+# include "qfunctions_wince.h"
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+static int _gettemp(char *path, int *doopen, int domkdir, int slen)
+{
+ char *start, *trv, *suffp;
+ QT_STATBUF sbuf;
+ int rval;
+#if defined(Q_OS_WIN)
+ int pid;
+#else
+ pid_t pid;
+#endif
+
+ if (doopen && domkdir) {
+ errno = EINVAL;
+ return(0);
+ }
+
+ for (trv = path; *trv; ++trv)
+ ;
+ trv -= slen;
+ suffp = trv;
+ --trv;
+ if (trv < path) {
+ errno = EINVAL;
+ return (0);
+ }
+#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
+ pid = _getpid();
+#else
+ pid = getpid();
+#endif
+ while (trv >= path && *trv == 'X' && pid != 0) {
+ *trv-- = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+ while (trv >= path && *trv == 'X') {
+ char c;
+
+ // CHANGE arc4random() -> random()
+ pid = (qrand() & 0xffff) % (26+26);
+ if (pid < 26)
+ c = pid + 'A';
+ else
+ c = (pid - 26) + 'a';
+ *trv-- = c;
+ }
+ start = trv + 1;
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ if (doopen || domkdir) {
+ for (;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+#if defined (Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (trv - path == 2 && path[1] == ':') {
+ // Special case for Windows drives
+ // (e.g., "C:" => "C:\").
+ // ### Better to use a Windows
+ // call for this.
+ char drive[] = "c:\\";
+ drive[0] = path[0];
+ rval = QT_STAT(drive, &sbuf);
+ } else
+#endif
+ rval = QT_STAT(path, &sbuf);
+ *trv = '/';
+ if (rval != 0)
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ break;
+ }
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_sopen_s(doopen, path, QT_OPEN_CREAT|O_EXCL|QT_OPEN_RDWR|QT_OPEN_BINARY
+#ifdef QT_LARGEFILE_SUPPORT
+ |QT_OPEN_LARGEFILE
+#endif
+ , _SH_DENYNO, _S_IREAD | _S_IWRITE)== 0)
+#else
+#if defined(Q_OS_WINCE)
+ QString targetPath;
+ if (QDir::isAbsolutePath(QString::fromLatin1(path)))
+ targetPath = QLatin1String(path);
+ else
+ targetPath = QDir::currentPath().append(QLatin1String("/")) + QLatin1String(path);
+
+ if ((*doopen =
+ QT_OPEN(targetPath.toLocal8Bit(), O_CREAT|O_EXCL|O_RDWR
+#else
+ if ((*doopen =
+ open(path, QT_OPEN_CREAT|O_EXCL|QT_OPEN_RDWR
+#endif
+#ifdef QT_LARGEFILE_SUPPORT
+ |QT_OPEN_LARGEFILE
+#endif
+# if defined(Q_OS_WINCE)
+ |_O_BINARY
+# elif defined(Q_OS_WIN)
+ |O_BINARY
+# endif
+ , 0600)) >= 0)
+#endif
+
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ } else if (domkdir) {
+#ifdef Q_OS_WIN
+ if (QT_MKDIR(path) == 0)
+#else
+ if (mkdir(path, 0700) == 0)
+#endif
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+#ifndef Q_OS_WIN
+ else if (QT_LSTAT(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+#else
+ if (!QFileInfo(QLatin1String(path)).exists())
+ return 1;
+#endif
+
+ /* tricky little algorwwithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return (0);
+ if (*trv == 'Z') {
+ if (trv == suffp)
+ return (0);
+ *trv++ = 'a';
+ } else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else if (*trv == 'z') /* inc from z to A */
+ *trv = 'A';
+ else {
+ if (trv == suffp)
+ return (0);
+ ++*trv;
+ }
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+static int qt_mkstemps(char *path, int slen)
+{
+ int fd = 0;
+ return (_gettemp(path, &fd, 0, slen) ? fd : -1);
+}
+
+//************* QTemporaryFileEngine
+class QTemporaryFileEngine : public QFSFileEngine
+{
+ Q_DECLARE_PRIVATE(QFSFileEngine)
+public:
+ QTemporaryFileEngine(const QString &file) : QFSFileEngine(file) { }
+ ~QTemporaryFileEngine();
+
+ bool open(QIODevice::OpenMode flags);
+ bool remove();
+ bool close();
+};
+
+QTemporaryFileEngine::~QTemporaryFileEngine()
+{
+ QFSFileEngine::close();
+}
+
+bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
+{
+ Q_D(QFSFileEngine);
+
+ QString qfilename = d->filePath;
+ if(!qfilename.contains(QLatin1String("XXXXXX")))
+ qfilename += QLatin1String(".XXXXXX");
+
+ int suffixLength = qfilename.length() - (qfilename.lastIndexOf(QLatin1String("XXXXXX"), -1, Qt::CaseSensitive) + 6);
+ d->closeFileHandle = true;
+ char *filename = qstrdup(qfilename.toLocal8Bit());
+
+#ifndef Q_WS_WIN
+ int fd = qt_mkstemps(filename, suffixLength);
+ if (fd != -1) {
+ // First open the fd as an external file descriptor to
+ // initialize the engine properly.
+ QFSFileEngine::open(openMode, fd);
+
+ // Allow the engine to close the handle even if it's "external".
+ d->closeFileHandle = true;
+
+ // Restore the file names (open() resets them).
+ d->filePath = QString::fromLocal8Bit(filename); //changed now!
+ d->nativeInitFileName();
+ delete [] filename;
+ return true;
+ }
+ delete [] filename;
+ setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError, qt_error_string(errno));
+ return false;
+#else
+ if (!_gettemp(filename, 0, 0, suffixLength)) {
+ delete [] filename;
+ return false;
+ }
+
+ d->filePath = QString::fromLocal8Bit(filename);
+ d->nativeInitFileName();
+ d->closeFileHandle = true;
+ delete [] filename;
+ return QFSFileEngine::open(openMode);
+#endif
+}
+
+bool QTemporaryFileEngine::remove()
+{
+ Q_D(QFSFileEngine);
+ // Since the QTemporaryFileEngine::close() does not really close the file,
+ // we must explicitly call QFSFileEngine::close() before we remove it.
+ QFSFileEngine::close();
+ bool removed = QFSFileEngine::remove();
+ d->filePath.clear();
+ return removed;
+}
+
+bool QTemporaryFileEngine::close()
+{
+ // Don't close the file, just seek to the front.
+ seek(0);
+ setError(QFile::UnspecifiedError, QString());
+ return true;
+}
+
+//************* QTemporaryFilePrivate
+class QTemporaryFilePrivate : public QFilePrivate
+{
+ Q_DECLARE_PUBLIC(QTemporaryFile)
+
+protected:
+ QTemporaryFilePrivate();
+ ~QTemporaryFilePrivate();
+
+ bool autoRemove;
+ QString templateName;
+ mutable QTemporaryFileEngine *fileEngine;
+};
+
+QTemporaryFilePrivate::QTemporaryFilePrivate() : autoRemove(true), fileEngine(0)
+{
+}
+
+QTemporaryFilePrivate::~QTemporaryFilePrivate()
+{
+ delete fileEngine;
+ fileEngine = 0;
+}
+
+//************* QTemporaryFile
+
+/*!
+ \class QTemporaryFile
+ \reentrant
+ \brief The QTemporaryFile class is an I/O device that operates on temporary files.
+
+ \ingroup io
+ \mainclass
+
+ QTemporaryFile is used to create unique temporary files safely.
+ The file itself is created by calling open(). The name of the
+ temporary file is guaranteed to be unique (i.e., you are
+ guaranteed to not overwrite an existing file), and the file will
+ subsequently be removed upon destruction of the QTemporaryFile
+ object. This is an important technique that avoids data
+ corruption for applications that store data in temporary files.
+ The file name is either auto-generated, or created based on a
+ template, which is passed to QTemporaryFile's constructor.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtemporaryfile.cpp 0
+
+ Reopening a QTemporaryFile after calling close() is safe. For as long as
+ the QTemporaryFile object itself is not destroyed, the unique temporary
+ file will exist and be kept open internally by QTemporaryFile.
+
+ The file name of the temporary file can be found by calling fileName().
+ Note that this is only defined while the file is open; the function returns
+ an empty string before the file is opened and after it is closed.
+
+ A temporary file will have some static part of the name and some
+ part that is calculated to be unique. The default filename \c
+ qt_temp will be placed into the temporary path as returned by
+ QDir::tempPath(). If you specify your own filename, a relative
+ file path will not be placed in the temporary directory by
+ default, but be relative to the current working directory.
+
+ Specified filenames can contain the following template \c XXXXXX
+ (six upper case "X" characters), which will be replaced by the
+ auto-generated portion of the filename. Note that the template is
+ case sensitive. If the template is not present in the filename,
+ QTemporaryFile appends the generated part to the filename given.
+
+ \sa QDir::tempPath(), QFile
+*/
+
+#ifdef QT_NO_QOBJECT
+QTemporaryFile::QTemporaryFile()
+ : QFile(*new QTemporaryFilePrivate)
+{
+ Q_D(QTemporaryFile);
+ d->templateName = QDir::tempPath() + QLatin1String("/qt_temp.XXXXXX");
+}
+
+QTemporaryFile::QTemporaryFile(const QString &templateName)
+ : QFile(*new QTemporaryFilePrivate)
+{
+ Q_D(QTemporaryFile);
+ d->templateName = templateName;
+}
+
+#else
+/*!
+ Constructs a QTemporaryFile in QDir::tempPath(), using the file template
+ "qt_temp.XXXXXX". The file is stored in the system's temporary directory.
+
+ \sa setFileTemplate(), QDir::tempPath()
+*/
+QTemporaryFile::QTemporaryFile()
+ : QFile(*new QTemporaryFilePrivate, 0)
+{
+ Q_D(QTemporaryFile);
+ d->templateName = QDir::tempPath() + QLatin1String("/qt_temp.XXXXXX");
+}
+
+/*!
+ Constructs a QTemporaryFile with a template filename of \a
+ templateName. Upon opening the temporary file this will be used to create
+ a unique filename.
+
+ If the \a templateName does not contain XXXXXX it will automatically be
+ appended and used as the dynamic portion of the filename.
+
+ If \a templateName is a relative path, the path will be relative to the
+ current working directory. You can use QDir::tempPath() to construct \a
+ templateName if you want use the system's temporary directory.
+
+ \sa open(), fileTemplate()
+*/
+QTemporaryFile::QTemporaryFile(const QString &templateName)
+ : QFile(*new QTemporaryFilePrivate, 0)
+{
+ setFileTemplate(templateName);
+}
+
+/*!
+ Constructs a QTemporaryFile (with the given \a parent) in
+ QDir::tempPath(), using the file template "qt_temp.XXXXXX".
+
+ \sa setFileTemplate()
+*/
+QTemporaryFile::QTemporaryFile(QObject *parent)
+ : QFile(*new QTemporaryFilePrivate, parent)
+{
+ Q_D(QTemporaryFile);
+ d->templateName = QDir::tempPath() + QLatin1String("/qt_temp.XXXXXX");
+}
+
+/*!
+ Constructs a QTemporaryFile with a template filename of \a
+ templateName and the specified \a parent.
+ Upon opening the temporary file this will be used to
+ create a unique filename.
+
+ If the \a templateName does not contain XXXXXX it will automatically be
+ appended and used as the dynamic portion of the filename.
+
+ If \a templateName is a relative path, the path will be relative to the
+ current working directory. You can use QDir::tempPath() to construct \a
+ templateName if you want use the system's temporary directory.
+
+ \sa open(), fileTemplate()
+*/
+QTemporaryFile::QTemporaryFile(const QString &templateName, QObject *parent)
+ : QFile(*new QTemporaryFilePrivate, parent)
+{
+ setFileTemplate(templateName);
+}
+#endif
+
+/*!
+ Destroys the temporary file object, the file is automatically
+ closed if necessary and if in auto remove mode it will
+ automatically delete the file.
+
+ \sa autoRemove()
+*/
+QTemporaryFile::~QTemporaryFile()
+{
+ Q_D(QTemporaryFile);
+ close();
+ if (!d->fileName.isEmpty() && d->autoRemove)
+ remove();
+}
+
+/*!
+ \fn bool QTemporaryFile::open()
+
+ A QTemporaryFile will always be opened in QIODevice::ReadWrite mode,
+ this allows easy access to the data in the file. This function will
+ return true upon success and will set the fileName() to the unique
+ filename used.
+
+ \sa fileName()
+*/
+
+/*!
+ Returns true if the QTemporaryFile is in auto remove
+ mode. Auto-remove mode will automatically delete the filename from
+ disk upon destruction. This makes it very easy to create your
+ QTemporaryFile object on the stack, fill it with data, read from
+ it, and finally on function return it will automatically clean up
+ after itself.
+
+ Auto-remove is on by default.
+
+ \sa setAutoRemove(), remove()
+*/
+bool QTemporaryFile::autoRemove() const
+{
+ Q_D(const QTemporaryFile);
+ return d->autoRemove;
+}
+
+/*!
+ Sets the QTemporaryFile into auto-remove mode if \a b is true.
+
+ Auto-remove is on by default.
+
+ \sa autoRemove(), remove()
+*/
+void QTemporaryFile::setAutoRemove(bool b)
+{
+ Q_D(QTemporaryFile);
+ d->autoRemove = b;
+}
+
+/*!
+ Returns the complete unique filename backing the QTemporaryFile
+ object. This string is null before the QTemporaryFile is opened,
+ afterwards it will contain the fileTemplate() plus
+ additional characters to make it unique.
+
+ \sa fileTemplate()
+*/
+
+QString QTemporaryFile::fileName() const
+{
+ if(!isOpen())
+ return QString();
+ return fileEngine()->fileName(QAbstractFileEngine::DefaultName);
+}
+
+/*!
+ Returns the set file template. The default file template will be
+ called qt_temp and be placed in QDir::tempPath().
+
+ \sa setFileTemplate()
+*/
+QString QTemporaryFile::fileTemplate() const
+{
+ Q_D(const QTemporaryFile);
+ return d->templateName;
+}
+
+/*!
+ Sets the static portion of the file name to \a name. If the file
+ template ends in XXXXXX that will automatically be replaced with
+ the unique part of the filename, otherwise a filename will be
+ determined automatically based on the static portion specified.
+
+ If \a name contains a relative file path, the path will be relative to the
+ current working directory. You can use QDir::tempPath() to construct \a
+ name if you want use the system's temporary directory.
+
+ \sa fileTemplate()
+*/
+void QTemporaryFile::setFileTemplate(const QString &name)
+{
+ Q_ASSERT(!isOpen());
+ Q_D(QTemporaryFile);
+ fileEngine()->setFileName(name);
+ d->templateName = name;
+}
+
+/*!
+ \fn QTemporaryFile *QTemporaryFile::createLocalFile(const QString &fileName)
+ \overload
+
+ Works on the given \a fileName rather than an existing QFile
+ object.
+*/
+
+
+/*!
+ If \a file is not on a local disk, a temporary file is created
+ on a local disk, \a file is copied into the temporary local file,
+ and a pointer to the temporary local file is returned. If \a file
+ is already on a local disk, a copy is not created and 0 is returned.
+*/
+QTemporaryFile *QTemporaryFile::createLocalFile(QFile &file)
+{
+ if (QAbstractFileEngine *engine = file.fileEngine()) {
+ if(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
+ return 0; //local already
+ //cache
+ bool wasOpen = file.isOpen();
+ qint64 old_off = 0;
+ if(wasOpen)
+ old_off = file.pos();
+ else
+ file.open(QIODevice::ReadOnly);
+ //dump data
+ QTemporaryFile *ret = new QTemporaryFile;
+ ret->open();
+ file.seek(0);
+ char buffer[1024];
+ while(true) {
+ qint64 len = file.read(buffer, 1024);
+ if(len < 1)
+ break;
+ ret->write(buffer, len);
+ }
+ ret->seek(0);
+ //restore
+ if(wasOpen)
+ file.seek(old_off);
+ else
+ file.close();
+ //done
+ return ret;
+ }
+ return 0;
+}
+
+/*!
+ \internal
+*/
+
+QAbstractFileEngine *QTemporaryFile::fileEngine() const
+{
+ Q_D(const QTemporaryFile);
+ if(!d->fileEngine)
+ d->fileEngine = new QTemporaryFileEngine(d->templateName);
+ return d->fileEngine;
+}
+
+/*!
+ \reimp
+
+ Creates a unique file name for the temporary file, and opens it. You can
+ get the unique name later by calling fileName(). The file is guaranteed to
+ have been created by this function (i.e., it has never existed before).
+*/
+bool QTemporaryFile::open(OpenMode flags)
+{
+ Q_D(QTemporaryFile);
+ if (!d->fileName.isEmpty()) {
+ setOpenMode(flags);
+ return true;
+ }
+
+ if (QFile::open(flags)) {
+ d->fileName = d->fileEngine->fileName(QAbstractFileEngine::DefaultName);
+ return true;
+ }
+ return false;
+}
+
+#endif // QT_NO_TEMPORARYFILE
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qtemporaryfile.h b/src/corelib/io/qtemporaryfile.h
new file mode 100644
index 0000000..4c3eb9a
--- /dev/null
+++ b/src/corelib/io/qtemporaryfile.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEMPORARYFILE_H
+#define QTEMPORARYFILE_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qfile.h>
+
+#ifdef open
+#error qtemporaryfile.h must be included before any header file that defines open
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_TEMPORARYFILE
+
+class QTemporaryFilePrivate;
+
+class Q_CORE_EXPORT QTemporaryFile : public QFile
+{
+#ifndef QT_NO_QOBJECT
+ Q_OBJECT
+#endif
+ Q_DECLARE_PRIVATE(QTemporaryFile)
+
+public:
+ QTemporaryFile();
+ explicit QTemporaryFile(const QString &templateName);
+#ifndef QT_NO_QOBJECT
+ explicit QTemporaryFile(QObject *parent);
+ QTemporaryFile(const QString &templateName, QObject *parent);
+#endif
+ ~QTemporaryFile();
+
+ bool autoRemove() const;
+ void setAutoRemove(bool b);
+
+ // ### Hides open(flags)
+ bool open() { return open(QIODevice::ReadWrite); }
+
+ QString fileName() const;
+ QString fileTemplate() const;
+ void setFileTemplate(const QString &name);
+
+ inline static QTemporaryFile *createLocalFile(const QString &fileName)
+ { QFile file(fileName); return createLocalFile(file); }
+ static QTemporaryFile *createLocalFile(QFile &file);
+
+ virtual QAbstractFileEngine *fileEngine() const;
+
+protected:
+ bool open(OpenMode flags);
+
+private:
+ friend class QFile;
+ Q_DISABLE_COPY(QTemporaryFile)
+};
+
+#endif // QT_NO_TEMPORARYFILE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEMPORARYFILE_H
diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp
new file mode 100644
index 0000000..5227d4f
--- /dev/null
+++ b/src/corelib/io/qtextstream.cpp
@@ -0,0 +1,3387 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QTEXTSTREAM_DEBUG
+static const int QTEXTSTREAM_BUFFERSIZE = 16384;
+
+/*!
+ \class QTextStream
+
+ \brief The QTextStream class provides a convenient interface for
+ reading and writing text.
+
+ \ingroup io
+ \ingroup text
+ \reentrant
+
+ QTextStream can operate on a QIODevice, a QByteArray or a
+ QString. Using QTextStream's streaming operators, you can
+ conveniently read and write words, lines and numbers. For
+ generating text, QTextStream supports formatting options for field
+ padding and alignment, and formatting of numbers. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 0
+
+ It's also common to use QTextStream to read console input and write
+ console output. QTextStream is locale aware, and will automatically decode
+ standard input using the correct codec. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 1
+
+ Note that you cannot use QTextStream::atEnd(), which returns true when you
+ have reached the end of the data stream, with stdin.
+
+ Besides using QTextStream's constructors, you can also set the
+ device or string QTextStream operates on by calling setDevice() or
+ setString(). You can seek to a position by calling seek(), and
+ atEnd() will return true when there is no data left to be read. If
+ you call flush(), QTextStream will empty all data from its write
+ buffer into the device and call flush() on the device.
+
+ Internally, QTextStream uses a Unicode based buffer, and
+ QTextCodec is used by QTextStream to automatically support
+ different character sets. By default, QTextCodec::codecForLocale()
+ is used for reading and writing, but you can also set the codec by
+ calling setCodec(). Automatic Unicode detection is also
+ supported. When this feature is enabled (the default behavior),
+ QTextStream will detect the UTF-16 or the UTF-32 BOM (Byte Order Mark) and
+ switch to the appropriate UTF codec when reading. QTextStream
+ does not write a BOM by default, but you can enable this by calling
+ setGenerateByteOrderMark(true). When QTextStream operates on a QString
+ directly, the codec is disabled.
+
+ There are three general ways to use QTextStream when reading text
+ files:
+
+ \list
+
+ \o Chunk by chunk, by calling readLine() or readAll().
+
+ \o Word by word. QTextStream supports streaming into QStrings,
+ QByteArrays and char* buffers. Words are delimited by space, and
+ leading white space is automatically skipped.
+
+ \o Character by character, by streaming into QChar or char types.
+ This method is often used for convenient input handling when
+ parsing files, independent of character encoding and end-of-line
+ semantics. To skip white space, call skipWhiteSpace().
+
+ \endlist
+
+ Since the text stream uses a buffer, you should not read from
+ the stream using the implementation of a superclass. For instance,
+ if you have a QFile and read from it directly using
+ QFile::readLine() instead of using the stream, the text stream's
+ internal position will be out of sync with the file's position.
+
+ By default, when reading numbers from a stream of text,
+ QTextStream will automatically detect the number's base
+ representation. For example, if the number starts with "0x", it is
+ assumed to be in hexadecimal form. If it starts with the digits
+ 1-9, it is assumed to be in decimal form, and so on. You can set
+ the integer base, thereby disabling the automatic detection, by
+ calling setIntegerBase(). Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 2
+
+ QTextStream supports many formatting options for generating text.
+ You can set the field width and pad character by calling
+ setFieldWidth() and setPadChar(). Use setFieldAlignment() to set
+ the alignment within each field. For real numbers, call
+ setRealNumberNotation() and setRealNumberPrecision() to set the
+ notation (SmartNotation, ScientificNotation, FixedNotation) and precision in
+ digits of the generated number. Some extra number formatting
+ options are also available through setNumberFlags().
+
+ \keyword QTextStream manipulators
+
+ Like \c <iostream> in the standard C++ library, QTextStream also
+ defines several global manipulator functions:
+
+ \table
+ \header \o Manipulator \o Description
+ \row \o \c bin \o Same as setIntegerBase(2).
+ \row \o \c oct \o Same as setIntegerBase(8).
+ \row \o \c dec \o Same as setIntegerBase(10).
+ \row \o \c hex \o Same as setIntegerBase(16).
+ \row \o \c showbase \o Same as setNumberFlags(numberFlags() | ShowBase).
+ \row \o \c forcesign \o Same as setNumberFlags(numberFlags() | ForceSign).
+ \row \o \c forcepoint \o Same as setNumberFlags(numberFlags() | ForcePoint).
+ \row \o \c noshowbase \o Same as setNumberFlags(numberFlags() & ~ShowBase).
+ \row \o \c noforcesign \o Same as setNumberFlags(numberFlags() & ~ForceSign).
+ \row \o \c noforcepoint \o Same as setNumberFlags(numberFlags() & ~ForcePoint).
+ \row \o \c uppercasebase \o Same as setNumberFlags(numberFlags() | UppercaseBase).
+ \row \o \c uppercasedigits \o Same as setNumberFlags(numberFlags() | UppercaseDigits).
+ \row \o \c lowercasebase \o Same as setNumberFlags(numberFlags() & ~UppercaseBase).
+ \row \o \c lowercasedigits \o Same as setNumberFlags(numberFlags() & ~UppercaseDigits).
+ \row \o \c fixed \o Same as setRealNumberNotation(FixedNotation).
+ \row \o \c scientific \o Same as setRealNumberNotation(ScientificNotation).
+ \row \o \c left \o Same as setFieldAlignment(AlignLeft).
+ \row \o \c right \o Same as setFieldAlignment(AlignRight).
+ \row \o \c center \o Same as setFieldAlignment(AlignCenter).
+ \row \o \c endl \o Same as operator<<('\n') and flush().
+ \row \o \c flush \o Same as flush().
+ \row \o \c reset \o Same as reset().
+ \row \o \c ws \o Same as skipWhiteSpace().
+ \row \o \c bom \o Same as setGenerateByteOrderMark(true).
+ \endtable
+
+ In addition, Qt provides three global manipulators that take a
+ parameter: qSetFieldWidth(), qSetPadChar(), and
+ qSetRealNumberPrecision().
+
+ \sa QDataStream, QIODevice, QFile, QBuffer, QTcpSocket, {Codecs Example}
+*/
+
+/*! \enum QTextStream::RealNumberNotation
+
+ This enum specifies which notations to use for expressing \c
+ float and \c double as strings.
+
+ \value ScientificNotation Scientific notation (\c{printf()}'s \c %e flag).
+ \value FixedNotation Fixed-point notation (\c{printf()}'s \c %f flag).
+ \value SmartNotation Scientific or fixed-point notation, depending on which makes most sense (\c{printf()}'s \c %g flag).
+
+ \sa setRealNumberNotation()
+*/
+
+/*! \enum QTextStream::FieldAlignment
+
+ This enum specifies how to align text in fields when the field is
+ wider than the text that occupies it.
+
+ \value AlignLeft Pad on the right side of fields.
+ \value AlignRight Pad on the left side of fields.
+ \value AlignCenter Pad on both sides of field.
+ \value AlignAccountingStyle Same as AlignRight, except that the
+ sign of a number is flush left.
+
+ \sa setFieldAlignment()
+*/
+
+/*! \enum QTextStream::NumberFlag
+
+ This enum specifies various flags that can be set to affect the
+ output of integers, \c{float}s, and \c{double}s.
+
+ \value ShowBase Show the base as a prefix if the base
+ is 16 ("0x"), 8 ("0"), or 2 ("0b").
+ \value ForcePoint Always put the decimal separator in numbers, even if
+ there are no decimals.
+ \value ForceSign Always put the sign in numbers, even for positive numbers.
+ \value UppercaseBase Use uppercase versions of base prefixes ("0X", "0B").
+ \value UppercaseDigits Use uppercase letters for expressing
+ digits 10 to 35 instead of lowercase.
+
+ \sa setNumberFlags()
+*/
+
+/*! \enum QTextStream::Status
+
+ This enum describes the current status of the text stream.
+
+ \value Ok The text stream is operating normally.
+ \value ReadPastEnd The text stream has read past the end of the
+ data in the underlying device.
+ \value ReadCorruptData The text stream has read corrupt data.
+
+ \sa status()
+*/
+
+#include "qtextstream.h"
+#include "qbuffer.h"
+#include "qfile.h"
+#include "qnumeric.h"
+#ifndef QT_NO_TEXTCODEC
+#include "qtextcodec.h"
+#endif
+#ifndef Q_OS_WINCE
+#include <locale.h>
+#endif
+#include "private/qlocale_p.h"
+
+#include <stdlib.h>
+#include <new>
+
+#if defined QTEXTSTREAM_DEBUG
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+// Returns a human readable representation of the first \a len
+// characters in \a data.
+static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(int(uchar(c)))) {
+ out += c;
+ } else switch (c) {
+ case '\n': out += "\\n"; break;
+ case '\r': out += "\\r"; break;
+ case '\t': out += "\\t"; break;
+ default:
+ QString tmp;
+ tmp.sprintf("\\x%x", (unsigned int)(unsigned char)c);
+ out += tmp.toLatin1();
+ }
+ }
+
+ if (len < maxSize)
+ out += "...";
+
+ return out;
+}
+QT_END_NAMESPACE
+
+#endif
+
+// A precondition macro
+#define Q_VOID
+#define CHECK_VALID_STREAM(x) do { \
+ if (!d->string && !d->device) { \
+ qWarning("QTextStream: No device"); \
+ return x; \
+ } } while (0)
+
+// Base implementations of operator>> for ints and reals
+#define IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(type) do { \
+ Q_D(QTextStream); \
+ CHECK_VALID_STREAM(*this); \
+ qulonglong tmp; \
+ switch (d->getNumber(&tmp)) { \
+ case QTextStreamPrivate::npsOk: \
+ i = (type)tmp; \
+ break; \
+ case QTextStreamPrivate::npsMissingDigit: \
+ case QTextStreamPrivate::npsInvalidPrefix: \
+ i = (type)0; \
+ setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \
+ break; \
+ } \
+ return *this; } while (0)
+
+#define IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(type) do { \
+ Q_D(QTextStream); \
+ CHECK_VALID_STREAM(*this); \
+ double tmp; \
+ if (d->getReal(&tmp)) { \
+ f = (type)tmp; \
+ } else { \
+ f = (type)0; \
+ setStatus(atEnd() ? QTextStream::ReadPastEnd : QTextStream::ReadCorruptData); \
+ } \
+ return *this; } while (0)
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QOBJECT
+class QDeviceClosedNotifier : public QObject
+{
+ Q_OBJECT
+public:
+ inline QDeviceClosedNotifier()
+ { }
+
+ inline void setupDevice(QTextStream *stream, QIODevice *device)
+ {
+ disconnect();
+ if (device)
+ connect(device, SIGNAL(aboutToClose()), this, SLOT(flushStream()));
+ this->stream = stream;
+ }
+
+public slots:
+ inline void flushStream() { stream->flush(); }
+
+private:
+ QTextStream *stream;
+};
+#endif
+
+//-------------------------------------------------------------------
+class QTextStreamPrivate
+{
+ Q_DECLARE_PUBLIC(QTextStream)
+public:
+ QTextStreamPrivate(QTextStream *q_ptr);
+ ~QTextStreamPrivate();
+ void reset();
+
+ // device
+ QIODevice *device;
+#ifndef QT_NO_QOBJECT
+ QDeviceClosedNotifier deviceClosedNotifier;
+#endif
+ bool deleteDevice;
+
+ // string
+ QString *string;
+ int stringOffset;
+ QIODevice::OpenMode stringOpenMode;
+
+#ifndef QT_NO_TEXTCODEC
+ // codec
+ QTextCodec *codec;
+ QTextCodec::ConverterState readConverterState;
+ QTextCodec::ConverterState writeConverterState;
+ QTextCodec::ConverterState *readConverterSavedState;
+ bool autoDetectUnicode;
+#endif
+
+ // i/o
+ enum TokenDelimiter {
+ Space,
+ NotSpace,
+ EndOfLine,
+ EndOfFile
+ };
+
+ bool scan(const QChar **ptr, int *tokenLength,
+ int maxlen, TokenDelimiter delimiter);
+ inline const QChar *readPtr() const;
+ inline void consumeLastToken();
+ inline void consume(int nchars);
+ void saveConverterState(qint64 newPos);
+ void restoreToSavedConverterState();
+ int lastTokenSize;
+
+ // Return value type for getNumber()
+ enum NumberParsingStatus {
+ npsOk,
+ npsMissingDigit,
+ npsInvalidPrefix
+ };
+
+ inline bool write(const QString &data);
+ inline bool getChar(QChar *ch);
+ inline void ungetChar(const QChar &ch);
+ NumberParsingStatus getNumber(qulonglong *l);
+ bool getReal(double *f);
+
+ bool putNumber(qulonglong number, bool negative);
+ inline bool putString(const QString &ch, bool number = false);
+
+ // buffers
+ bool fillReadBuffer(qint64 maxBytes = -1);
+ void resetReadBuffer();
+ bool flushWriteBuffer();
+ QString writeBuffer;
+ QString readBuffer;
+ int readBufferOffset;
+ qint64 readBufferStartDevicePos;
+
+ // streaming parameters
+ int realNumberPrecision;
+ int integerBase;
+ int fieldWidth;
+ QChar padChar;
+ QTextStream::FieldAlignment fieldAlignment;
+ QTextStream::RealNumberNotation realNumberNotation;
+ QTextStream::NumberFlags numberFlags;
+
+ // status
+ QTextStream::Status status;
+
+ QLocale locale;
+
+ QTextStream *q_ptr;
+};
+
+/*! \internal
+*/
+QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr)
+ :
+#ifndef QT_NO_TEXTCODEC
+ readConverterSavedState(0),
+#endif
+ locale(QLocale::C)
+{
+ this->q_ptr = q_ptr;
+ reset();
+}
+
+/*! \internal
+*/
+QTextStreamPrivate::~QTextStreamPrivate()
+{
+ if (deleteDevice) {
+#ifndef QT_NO_QOBJECT
+ device->blockSignals(true);
+#endif
+ delete device;
+ }
+#ifndef QT_NO_TEXTCODEC
+ delete readConverterSavedState;
+#endif
+}
+
+#ifndef QT_NO_TEXTCODEC
+static void resetCodecConverterStateHelper(QTextCodec::ConverterState *state)
+{
+ state->~ConverterState();
+ new (state) QTextCodec::ConverterState;
+}
+
+static void copyConverterStateHelper(QTextCodec::ConverterState *dest,
+ const QTextCodec::ConverterState *src)
+{
+ // ### QTextCodec::ConverterState's copy constructors and assignments are
+ // private. This function copies the structure manually.
+ Q_ASSERT(!src->d);
+ dest->flags = src->flags;
+ dest->invalidChars = src->invalidChars;
+ dest->state_data[0] = src->state_data[0];
+ dest->state_data[1] = src->state_data[1];
+ dest->state_data[2] = src->state_data[2];
+}
+#endif
+
+/*! \internal
+*/
+void QTextStreamPrivate::reset()
+{
+ realNumberPrecision = 6;
+ integerBase = 0;
+ fieldWidth = 0;
+ padChar = QLatin1Char(' ');
+ fieldAlignment = QTextStream::AlignRight;
+ realNumberNotation = QTextStream::SmartNotation;
+ numberFlags = 0;
+
+ device = 0;
+ deleteDevice = false;
+ string = 0;
+ stringOffset = 0;
+ stringOpenMode = QIODevice::NotOpen;
+
+ readBufferOffset = 0;
+ readBufferStartDevicePos = 0;
+ lastTokenSize = 0;
+
+#ifndef QT_NO_TEXTCODEC
+ codec = QTextCodec::codecForLocale();
+ resetCodecConverterStateHelper(&readConverterState);
+ resetCodecConverterStateHelper(&writeConverterState);
+ delete readConverterSavedState;
+ readConverterSavedState = 0;
+ writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ autoDetectUnicode = true;
+#endif
+}
+
+/*! \internal
+*/
+bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes)
+{
+ // no buffer next to the QString itself; this function should only
+ // be called internally, for devices.
+ Q_ASSERT(!string);
+ Q_ASSERT(device);
+
+ // handle text translation and bypass the Text flag in the device.
+ bool textModeEnabled = device->isTextModeEnabled();
+ if (textModeEnabled)
+ device->setTextModeEnabled(false);
+
+ // read raw data into a temporary buffer
+ char buf[QTEXTSTREAM_BUFFERSIZE];
+ qint64 bytesRead = 0;
+#if defined(Q_OS_WIN)
+ // On Windows, there is no non-blocking stdin - so we fall back to reading
+ // lines instead. If there is no QOBJECT, we read lines for all sequential
+ // devices; otherwise, we read lines only for stdin.
+ QFile *file = 0;
+ Q_UNUSED(file);
+ if (device->isSequential()
+#if !defined(QT_NO_QOBJECT)
+ && (file = qobject_cast<QFile *>(device)) && file->handle() == 0
+#endif
+ ) {
+ if (maxBytes != -1)
+ bytesRead = device->readLine(buf, qMin<qint64>(sizeof(buf), maxBytes));
+ else
+ bytesRead = device->readLine(buf, sizeof(buf));
+ } else
+#endif
+ {
+ if (maxBytes != -1)
+ bytesRead = device->read(buf, qMin<qint64>(sizeof(buf), maxBytes));
+ else
+ bytesRead = device->read(buf, sizeof(buf));
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ // codec auto detection, explicitly defaults to locale encoding if the
+ // codec has been set to 0.
+ if (!codec || autoDetectUnicode) {
+ autoDetectUnicode = false;
+
+ if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0)
+ || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) {
+ codec = QTextCodec::codecForName("UTF-32");
+ } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe)
+ || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) {
+ codec = QTextCodec::codecForName("UTF-16");
+ } else if (!codec) {
+ codec = QTextCodec::codecForLocale();
+ writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ }
+ }
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::fillReadBuffer(), using %s codec",
+ codec->name().constData());
+#endif
+#endif
+
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::fillReadBuffer(), device->read(\"%s\", %d) == %d",
+ qt_prettyDebug(buf, qMin(32,int(bytesRead)) , int(bytesRead)).constData(), sizeof(buf), int(bytesRead));
+#endif
+
+ if (bytesRead <= 0)
+ return false;
+
+ int oldReadBufferSize = readBuffer.size();
+#ifndef QT_NO_TEXTCODEC
+ // convert to unicode
+ readBuffer += codec->toUnicode(buf, bytesRead, &readConverterState);
+#else
+ readBuffer += QString::fromLatin1(QByteArray(buf, bytesRead).constData());
+#endif
+
+ // reset the Text flag.
+ if (textModeEnabled)
+ device->setTextModeEnabled(true);
+
+ // remove all '\r\n' in the string.
+ if (readBuffer.size() > oldReadBufferSize && textModeEnabled) {
+ QChar CR = QLatin1Char('\r');
+ QChar *writePtr = readBuffer.data() + oldReadBufferSize;
+ QChar *readPtr = readBuffer.data() + oldReadBufferSize;
+ QChar *endPtr = readBuffer.data() + readBuffer.size();
+
+ int n = oldReadBufferSize;
+ if (readPtr < endPtr) {
+ // Cut-off to avoid unnecessary self-copying.
+ while (*readPtr++ != CR) {
+ ++n;
+ if (++writePtr == endPtr)
+ break;
+ }
+ }
+ while (readPtr < endPtr) {
+ QChar ch = *readPtr++;
+ if (ch != CR) {
+ *writePtr++ = ch;
+ } else {
+ if (n < readBufferOffset)
+ --readBufferOffset;
+ --bytesRead;
+ }
+ ++n;
+ }
+ readBuffer.resize(writePtr - readBuffer.data());
+ }
+
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::fillReadBuffer() read %d bytes from device. readBuffer = [%s]", int(bytesRead),
+ qt_prettyDebug(readBuffer.toLatin1(), readBuffer.size(), readBuffer.size()).data());
+#endif
+ return true;
+}
+
+/*! \internal
+*/
+void QTextStreamPrivate::resetReadBuffer()
+{
+ readBuffer.clear();
+ readBufferOffset = 0;
+ readBufferStartDevicePos = (device ? device->pos() : 0);
+}
+
+/*! \internal
+*/
+bool QTextStreamPrivate::flushWriteBuffer()
+{
+ // no buffer next to the QString itself; this function should only
+ // be called internally, for devices.
+ if (string || !device)
+ return false;
+ if (writeBuffer.isEmpty())
+ return true;
+
+#if defined (Q_OS_WIN)
+ // handle text translation and bypass the Text flag in the device.
+ bool textModeEnabled = device->isTextModeEnabled();
+ if (textModeEnabled) {
+ device->setTextModeEnabled(false);
+ writeBuffer.replace(QLatin1Char('\n'), QLatin1String("\r\n"));
+ }
+#endif
+
+#ifndef QT_NO_TEXTCODEC
+ if (!codec)
+ codec = QTextCodec::codecForLocale();
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::flushWriteBuffer(), using %s codec (%s generating BOM)",
+ codec->name().constData(), writeConverterState.flags & QTextCodec::IgnoreHeader ? "not" : "");
+#endif
+
+ // convert from unicode to raw data
+ QByteArray data = codec->fromUnicode(writeBuffer.data(), writeBuffer.size(), &writeConverterState);
+#else
+ QByteArray data = writeBuffer.toLocal8Bit();
+#endif
+ writeBuffer.clear();
+
+ // write raw data to the device
+ qint64 bytesWritten = device->write(data);
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d",
+ qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten));
+#endif
+ if (bytesWritten <= 0)
+ return false;
+
+#if defined (Q_OS_WIN)
+ // replace the text flag
+ if (textModeEnabled)
+ device->setTextModeEnabled(true);
+#endif
+
+ // flush the file
+#ifndef QT_NO_QOBJECT
+ QFile *file = qobject_cast<QFile *>(device);
+ bool flushed = file && file->flush();
+#else
+ bool flushed = true;
+#endif
+
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes",
+ int(bytesWritten));
+#endif
+ return flushed && bytesWritten == qint64(data.size());
+}
+
+/*! \internal
+
+ Scans no more than \a maxlen QChars in the current buffer for the
+ first \a delimiter. Stores a pointer to the start offset of the
+ token in \a ptr, and the length in QChars in \a length.
+*/
+bool QTextStreamPrivate::scan(const QChar **ptr, int *length, int maxlen, TokenDelimiter delimiter)
+{
+ int totalSize = 0;
+ int delimSize = 0;
+ bool consumeDelimiter = false;
+ bool foundToken = false;
+ int startOffset = device ? readBufferOffset : stringOffset;
+ QChar lastChar;
+
+ bool canStillReadFromDevice = true;
+ do {
+ int endOffset;
+ const QChar *chPtr;
+ if (device) {
+ chPtr = readBuffer.constData();
+ endOffset = readBuffer.size();
+ } else {
+ chPtr = string->constData();
+ endOffset = string->size();
+ }
+ chPtr += startOffset;
+
+ for (; !foundToken && startOffset < endOffset && (!maxlen || totalSize < maxlen); ++startOffset) {
+ const QChar ch = *chPtr++;
+ ++totalSize;
+
+ if (delimiter == Space && ch.isSpace()) {
+ foundToken = true;
+ delimSize = 1;
+ } else if (delimiter == NotSpace && !ch.isSpace()) {
+ foundToken = true;
+ delimSize = 1;
+ } else if (delimiter == EndOfLine && ch == QLatin1Char('\n')) {
+ foundToken = true;
+ delimSize = (lastChar == QLatin1Char('\r')) ? 2 : 1;
+ consumeDelimiter = true;
+ }
+
+ lastChar = ch;
+ }
+ } while (!foundToken
+ && (!maxlen || totalSize < maxlen)
+ && (device && (canStillReadFromDevice = fillReadBuffer())));
+
+ // if the token was not found, but we reached the end of input,
+ // then we accept what we got. if we are not at the end of input,
+ // we return false.
+ if (!foundToken && (!maxlen || totalSize < maxlen)
+ && (totalSize == 0
+ || (string && stringOffset + totalSize < string->size())
+ || (device && !device->atEnd() && canStillReadFromDevice))) {
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::scan() did not find the token.");
+#endif
+ return false;
+ }
+
+ // if we find a '\r' at the end of the data when reading lines,
+ // don't make it part of the line.
+ if (totalSize > 0 && !foundToken && delimiter == EndOfLine) {
+ if (((string && stringOffset + totalSize == string->size()) || (device && device->atEnd()))
+ && lastChar == QLatin1Char('\r')) {
+ consumeDelimiter = true;
+ ++delimSize;
+ }
+ }
+
+ // set the read offset and length of the token
+ if (length)
+ *length = totalSize - delimSize;
+ if (ptr)
+ *ptr = readPtr();
+
+ // update last token size. the callee will call consumeLastToken() when
+ // done.
+ lastTokenSize = totalSize;
+ if (!consumeDelimiter)
+ lastTokenSize -= delimSize;
+
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::scan(%p, %p, %d, %x) token length = %d, delimiter = %d",
+ ptr, length, maxlen, (int)delimiter, totalSize - delimSize, delimSize);
+#endif
+ return true;
+}
+
+/*! \internal
+*/
+inline const QChar *QTextStreamPrivate::readPtr() const
+{
+ Q_ASSERT(readBufferOffset <= readBuffer.size());
+ if (string)
+ return string->constData() + stringOffset;
+ return readBuffer.constData() + readBufferOffset;
+}
+
+/*! \internal
+*/
+inline void QTextStreamPrivate::consumeLastToken()
+{
+ if (lastTokenSize)
+ consume(lastTokenSize);
+ lastTokenSize = 0;
+}
+
+/*! \internal
+*/
+inline void QTextStreamPrivate::consume(int size)
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStreamPrivate::consume(%d)", size);
+#endif
+ if (string) {
+ stringOffset += size;
+ if (stringOffset > string->size())
+ stringOffset = string->size();
+ } else {
+ readBufferOffset += size;
+ if (readBufferOffset >= readBuffer.size()) {
+ readBufferOffset = 0;
+ readBuffer.clear();
+ saveConverterState(device->pos());
+ }
+ }
+}
+
+/*! \internal
+*/
+inline void QTextStreamPrivate::saveConverterState(qint64 newPos)
+{
+#ifndef QT_NO_TEXTCODEC
+ if (readConverterState.d) {
+ // converter cannot be copied, so don't save anything
+ // don't update readBufferStartDevicePos either
+ return;
+ }
+
+ if (!readConverterSavedState)
+ readConverterSavedState = new QTextCodec::ConverterState;
+ copyConverterStateHelper(readConverterSavedState, &readConverterState);
+#endif
+
+ readBufferStartDevicePos = newPos;
+}
+
+/*! \internal
+*/
+inline void QTextStreamPrivate::restoreToSavedConverterState()
+{
+#ifndef QT_NO_TEXTCODEC
+ if (readConverterSavedState) {
+ // we have a saved state
+ // that means the converter can be copied
+ copyConverterStateHelper(&readConverterState, readConverterSavedState);
+ } else {
+ // the only state we could save was the initial
+ // so reset to that
+ resetCodecConverterStateHelper(&readConverterState);
+ }
+#endif
+}
+
+/*! \internal
+*/
+inline bool QTextStreamPrivate::write(const QString &data)
+{
+ if (string) {
+ // ### What about seek()??
+ string->append(data);
+ } else {
+ writeBuffer += data;
+ if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE)
+ return flushWriteBuffer();
+ }
+ return true;
+}
+
+/*! \internal
+*/
+inline bool QTextStreamPrivate::getChar(QChar *ch)
+{
+ if ((string && stringOffset == string->size())
+ || (device && readBuffer.isEmpty() && !fillReadBuffer())) {
+ if (ch)
+ *ch = 0;
+ return false;
+ }
+ if (ch)
+ *ch = *readPtr();
+ consume(1);
+ return true;
+}
+
+/*! \internal
+*/
+inline void QTextStreamPrivate::ungetChar(const QChar &ch)
+{
+ if (string) {
+ if (stringOffset == 0)
+ string->prepend(ch);
+ else
+ (*string)[--stringOffset] = ch;
+ return;
+ }
+
+ if (readBufferOffset == 0) {
+ readBuffer.prepend(ch);
+ return;
+ }
+
+ readBuffer[--readBufferOffset] = ch;
+}
+
+/*! \internal
+*/
+inline bool QTextStreamPrivate::putString(const QString &s, bool number)
+{
+ QString tmp = s;
+
+ // handle padding
+ int padSize = fieldWidth - s.size();
+ if (padSize > 0) {
+ QString pad(padSize > 0 ? padSize : 0, padChar);
+ if (fieldAlignment == QTextStream::AlignLeft) {
+ tmp.append(QString(padSize, padChar));
+ } else if (fieldAlignment == QTextStream::AlignRight
+ || fieldAlignment == QTextStream::AlignAccountingStyle) {
+ tmp.prepend(QString(padSize, padChar));
+ if (fieldAlignment == QTextStream::AlignAccountingStyle && number) {
+ const QChar sign = s.size() > 0 ? s.at(0) : QChar();
+ if (sign == locale.negativeSign() || sign == locale.positiveSign()) {
+ QChar *data = tmp.data();
+ data[padSize] = tmp.at(0);
+ data[0] = sign;
+ }
+ }
+ } else if (fieldAlignment == QTextStream::AlignCenter) {
+ tmp.prepend(QString(padSize/2, padChar));
+ tmp.append(QString(padSize - padSize/2, padChar));
+ }
+ }
+
+#if defined (QTEXTSTREAM_DEBUG)
+ QByteArray a = s.toUtf8();
+ QByteArray b = tmp.toUtf8();
+ qDebug("QTextStreamPrivate::putString(\"%s\") calls write(\"%s\")",
+ qt_prettyDebug(a.constData(), a.size(), qMax(16, a.size())).constData(),
+ qt_prettyDebug(b.constData(), b.size(), qMax(16, b.size())).constData());
+#endif
+ return write(tmp);
+}
+
+/*!
+ Constructs a QTextStream. Before you can use it for reading or
+ writing, you must assign a device or a string.
+
+ \sa setDevice(), setString()
+*/
+QTextStream::QTextStream()
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream()");
+#endif
+ Q_D(QTextStream);
+ d->status = Ok;
+}
+
+/*!
+ Constructs a QTextStream that operates on \a device.
+*/
+QTextStream::QTextStream(QIODevice *device)
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream(QIODevice *device == *%p)",
+ device);
+#endif
+ Q_D(QTextStream);
+ d->device = device;
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.setupDevice(this, d->device);
+#endif
+ d->status = Ok;
+}
+
+/*!
+ Constructs a QTextStream that operates on \a string, using \a
+ openMode to define the open mode.
+*/
+QTextStream::QTextStream(QString *string, QIODevice::OpenMode openMode)
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream(QString *string == *%p, openMode = %d)",
+ string, int(openMode));
+#endif
+ Q_D(QTextStream);
+ d->string = string;
+ d->stringOpenMode = openMode;
+ d->status = Ok;
+}
+
+/*!
+ Constructs a QTextStream that operates on \a array, using \a
+ openMode to define the open mode. Internally, the array is wrapped
+ by a QBuffer.
+*/
+QTextStream::QTextStream(QByteArray *array, QIODevice::OpenMode openMode)
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream(QByteArray *array == *%p, openMode = %d)",
+ array, int(openMode));
+#endif
+ Q_D(QTextStream);
+ d->device = new QBuffer(array);
+ d->device->open(openMode);
+ d->deleteDevice = true;
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.setupDevice(this, d->device);
+#endif
+ d->status = Ok;
+}
+
+/*!
+ Constructs a QTextStream that operates on \a array, using \a
+ openMode to define the open mode. The array is accessed as
+ read-only, regardless of the values in \a openMode.
+
+ This constructor is convenient for working on constant
+ strings. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 3
+*/
+QTextStream::QTextStream(const QByteArray &array, QIODevice::OpenMode openMode)
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream(const QByteArray &array == *(%p), openMode = %d)",
+ &array, int(openMode));
+#endif
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(array);
+ buffer->open(openMode);
+
+ Q_D(QTextStream);
+ d->device = buffer;
+ d->deleteDevice = true;
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.setupDevice(this, d->device);
+#endif
+ d->status = Ok;
+}
+
+/*!
+ Constructs a QTextStream that operates on \a fileHandle, using \a
+ openMode to define the open mode. Internally, a QFile is created
+ to handle the FILE pointer.
+
+ This constructor is useful for working directly with the common
+ FILE based input and output streams: stdin, stdout and stderr. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 4
+*/
+
+QTextStream::QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode)
+ : d_ptr(new QTextStreamPrivate(this))
+{
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::QTextStream(FILE *fileHandle = %p, openMode = %d)",
+ fileHandle, int(openMode));
+#endif
+ QFile *file = new QFile;
+ file->open(fileHandle, openMode);
+
+ Q_D(QTextStream);
+ d->device = file;
+ d->deleteDevice = true;
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.setupDevice(this, d->device);
+#endif
+ d->status = Ok;
+}
+
+/*!
+ Destroys the QTextStream.
+
+ If the stream operates on a device, flush() will be called
+ implicitly. Otherwise, the device is unaffected.
+*/
+QTextStream::~QTextStream()
+{
+ Q_D(QTextStream);
+#if defined (QTEXTSTREAM_DEBUG)
+ qDebug("QTextStream::~QTextStream()");
+#endif
+ if (!d->writeBuffer.isEmpty())
+ d->flushWriteBuffer();
+
+ delete d;
+ d_ptr = 0;
+}
+
+/*!
+ Resets QTextStream's formatting options, bringing it back to its
+ original constructed state. The device, string and any buffered
+ data is left untouched.
+*/
+void QTextStream::reset()
+{
+ Q_D(QTextStream);
+
+ d->realNumberPrecision = 6;
+ d->integerBase = 0;
+ d->fieldWidth = 0;
+ d->padChar = QLatin1Char(' ');
+ d->fieldAlignment = QTextStream::AlignRight;
+ d->realNumberNotation = QTextStream::SmartNotation;
+ d->numberFlags = 0;
+}
+
+/*!
+ Flushes any buffered data waiting to be written to the device.
+
+ If QTextStream operates on a string, this function does nothing.
+*/
+void QTextStream::flush()
+{
+ Q_D(QTextStream);
+ d->flushWriteBuffer();
+}
+
+/*!
+ Seeks to the position \a pos in the device. Returns true on
+ success; otherwise returns false.
+*/
+bool QTextStream::seek(qint64 pos)
+{
+ Q_D(QTextStream);
+ d->lastTokenSize = 0;
+
+ if (d->device) {
+ // Empty the write buffer
+ d->flushWriteBuffer();
+ if (!d->device->seek(pos))
+ return false;
+ d->resetReadBuffer();
+
+#ifndef QT_NO_TEXTCODEC
+ // Reset the codec converter states.
+ resetCodecConverterStateHelper(&d->readConverterState);
+ resetCodecConverterStateHelper(&d->writeConverterState);
+ delete d->readConverterSavedState;
+ d->readConverterSavedState = 0;
+#endif
+ return true;
+ }
+
+ // string
+ if (d->string && pos <= d->string->size()) {
+ d->stringOffset = int(pos);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \since 4.2
+
+ Returns the device position corresponding to the current position of the
+ stream, or -1 if an error occurs (e.g., if there is no device or string,
+ or if there's a device error).
+
+ Because QTextStream is buffered, this function may have to
+ seek the device to reconstruct a valid device position. This
+ operation can be expensive, so you may want to avoid calling this
+ function in a tight loop.
+
+ \sa seek()
+*/
+qint64 QTextStream::pos() const
+{
+ Q_D(const QTextStream);
+ if (d->device) {
+ // Cutoff
+ if (d->readBuffer.isEmpty())
+ return d->device->pos();
+ if (d->device->isSequential())
+ return 0;
+
+ // Seek the device
+ if (!d->device->seek(d->readBufferStartDevicePos))
+ return qint64(-1);
+
+ // Reset the read buffer
+ QTextStreamPrivate *thatd = const_cast<QTextStreamPrivate *>(d);
+ thatd->readBuffer.clear();
+
+#ifndef QT_NO_TEXTCODEC
+ thatd->restoreToSavedConverterState();
+ if (d->readBufferStartDevicePos == 0)
+ thatd->autoDetectUnicode = true;
+#endif
+
+ // Rewind the device to get to the current position Ensure that
+ // readBufferOffset is unaffected by fillReadBuffer()
+ int oldReadBufferOffset = d->readBufferOffset;
+ while (d->readBuffer.size() < oldReadBufferOffset) {
+ if (!thatd->fillReadBuffer(1))
+ return qint64(-1);
+ }
+ thatd->readBufferOffset = oldReadBufferOffset;
+
+ // Return the device position.
+ return d->device->pos();
+ }
+
+ if (d->string)
+ return d->stringOffset;
+
+ qWarning("QTextStream::pos: no device");
+ return qint64(-1);
+}
+
+/*!
+ Reads and discards whitespace from the stream until either a
+ non-space character is detected, or until atEnd() returns
+ true. This function is useful when reading a stream character by
+ character.
+
+ Whitespace characters are all characters for which
+ QChar::isSpace() returns true.
+
+ \sa operator>>()
+*/
+void QTextStream::skipWhiteSpace()
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(Q_VOID);
+ d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->consumeLastToken();
+}
+
+/*!
+ Sets the current device to \a device. If a device has already been
+ assigned, QTextStream will call flush() before the old device is
+ replaced.
+
+ \note This function resets locale to the default locale ('C')
+ and codec to the default codec, QTextCodec::codecForLocale().
+
+ \sa device(), setString()
+*/
+void QTextStream::setDevice(QIODevice *device)
+{
+ Q_D(QTextStream);
+ flush();
+ if (d->deleteDevice) {
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.disconnect();
+#endif
+ delete d->device;
+ d->deleteDevice = false;
+ }
+
+ d->reset();
+ d->status = Ok;
+ d->device = device;
+ d->resetReadBuffer();
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.setupDevice(this, d->device);
+#endif
+}
+
+/*!
+ Returns the current device associated with the QTextStream,
+ or 0 if no device has been assigned.
+
+ \sa setDevice(), string()
+*/
+QIODevice *QTextStream::device() const
+{
+ Q_D(const QTextStream);
+ return d->device;
+}
+
+/*!
+ Sets the current string to \a string, using the given \a
+ openMode. If a device has already been assigned, QTextStream will
+ call flush() before replacing it.
+
+ \sa string(), setDevice()
+*/
+void QTextStream::setString(QString *string, QIODevice::OpenMode openMode)
+{
+ Q_D(QTextStream);
+ flush();
+ if (d->deleteDevice) {
+#ifndef QT_NO_QOBJECT
+ d->deviceClosedNotifier.disconnect();
+ d->device->blockSignals(true);
+#endif
+ delete d->device;
+ d->deleteDevice = false;
+ }
+
+ d->reset();
+ d->status = Ok;
+ d->string = string;
+ d->stringOpenMode = openMode;
+}
+
+/*!
+ Returns the current string assigned to the QTextStream, or 0 if no
+ string has been assigned.
+
+ \sa setString(), device()
+*/
+QString *QTextStream::string() const
+{
+ Q_D(const QTextStream);
+ return d->string;
+}
+
+/*!
+ Sets the field alignment to \a mode. When used together with
+ setFieldWidth(), this function allows you to generate formatted
+ output with text aligned to the left, to the right or center
+ aligned.
+
+ \sa fieldAlignment(), setFieldWidth()
+*/
+void QTextStream::setFieldAlignment(FieldAlignment mode)
+{
+ Q_D(QTextStream);
+ d->fieldAlignment = mode;
+}
+
+/*!
+ Returns the current field alignment.
+
+ \sa setFieldAlignment(), fieldWidth()
+*/
+QTextStream::FieldAlignment QTextStream::fieldAlignment() const
+{
+ Q_D(const QTextStream);
+ return d->fieldAlignment;
+}
+
+/*!
+ Sets the pad character to \a ch. The default value is the ASCII
+ space character (' '), or QChar(0x20). This character is used to
+ fill in the space in fields when generating text.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 5
+
+ Output:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 6
+
+ \sa padChar(), setFieldWidth()
+*/
+void QTextStream::setPadChar(QChar ch)
+{
+ Q_D(QTextStream);
+ d->padChar = ch;
+}
+
+/*!
+ Returns the current pad character.
+
+ \sa setPadChar(), setFieldWidth()
+*/
+QChar QTextStream::padChar() const
+{
+ Q_D(const QTextStream);
+ return d->padChar;
+}
+
+/*!
+ Sets the current field width to \a width. If \a width is 0 (the
+ default), the field width is equal to the length of the generated
+ text.
+
+ \note The field width applies to every element appended to this
+ stream after this function has been called (e.g., it also pads
+ endl). This behavior is different from similar classes in the STL,
+ where the field width only applies to the next element.
+
+ \sa fieldWidth(), setPadChar()
+*/
+void QTextStream::setFieldWidth(int width)
+{
+ Q_D(QTextStream);
+ d->fieldWidth = width;
+}
+
+/*!
+ Returns the current field width.
+
+ \sa setFieldWidth()
+*/
+int QTextStream::fieldWidth() const
+{
+ Q_D(const QTextStream);
+ return d->fieldWidth;
+}
+
+/*!
+ Sets the current number flags to \a flags. \a flags is a set of
+ flags from the NumberFlag enum, and describes options for
+ formatting generated code (e.g., whether or not to always write
+ the base or sign of a number).
+
+ \sa numberFlags(), setIntegerBase(), setRealNumberNotation()
+*/
+void QTextStream::setNumberFlags(NumberFlags flags)
+{
+ Q_D(QTextStream);
+ d->numberFlags = flags;
+}
+
+/*!
+ Returns the current number flags.
+
+ \sa setNumberFlags(), integerBase(), realNumberNotation()
+*/
+QTextStream::NumberFlags QTextStream::numberFlags() const
+{
+ Q_D(const QTextStream);
+ return d->numberFlags;
+}
+
+/*!
+ Sets the base of integers to \a base, both for reading and for
+ generating numbers. \a base can be either 2 (binary), 8 (octal),
+ 10 (decimal) or 16 (hexadecimal). If \a base is 0, QTextStream
+ will attempt to detect the base by inspecting the data on the
+ stream. When generating numbers, QTextStream assumes base is 10
+ unless the base has been set explicitly.
+
+ \sa integerBase(), QString::number(), setNumberFlags()
+*/
+void QTextStream::setIntegerBase(int base)
+{
+ Q_D(QTextStream);
+ d->integerBase = base;
+}
+
+/*!
+ Returns the current base of integers. 0 means that the base is
+ detected when reading, or 10 (decimal) when generating numbers.
+
+ \sa setIntegerBase(), QString::number(), numberFlags()
+*/
+int QTextStream::integerBase() const
+{
+ Q_D(const QTextStream);
+ return d->integerBase;
+}
+
+/*!
+ Sets the real number notation to \a notation (SmartNotation,
+ FixedNotation, ScientificNotation). When reading and generating
+ numbers, QTextStream uses this value to detect the formatting of
+ real numbers.
+
+ \sa realNumberNotation(), setRealNumberPrecision(), setNumberFlags(), setIntegerBase()
+*/
+void QTextStream::setRealNumberNotation(RealNumberNotation notation)
+{
+ Q_D(QTextStream);
+ d->realNumberNotation = notation;
+}
+
+/*!
+ Returns the current real number notation.
+
+ \sa setRealNumberNotation(), realNumberPrecision(), numberFlags(), integerBase()
+*/
+QTextStream::RealNumberNotation QTextStream::realNumberNotation() const
+{
+ Q_D(const QTextStream);
+ return d->realNumberNotation;
+}
+
+/*!
+ Sets the precision of real numbers to \a precision. This value
+ describes the number of fraction digits QTextStream should
+ write when generating real numbers.
+
+ The precision cannot be a negative value. The default value is 6.
+
+ \sa realNumberPrecision(), setRealNumberNotation()
+*/
+void QTextStream::setRealNumberPrecision(int precision)
+{
+ Q_D(QTextStream);
+ if (precision < 0) {
+ qWarning("QTextStream::setRealNumberPrecision: Invalid precision (%d)", precision);
+ d->realNumberPrecision = 6;
+ return;
+ }
+ d->realNumberPrecision = precision;
+}
+
+/*!
+ Returns the current real number precision, or the number of fraction
+ digits QTextStream will write when generating real numbers.
+
+ \sa setRealNumberNotation(), realNumberNotation(), numberFlags(), integerBase()
+*/
+int QTextStream::realNumberPrecision() const
+{
+ Q_D(const QTextStream);
+ return d->realNumberPrecision;
+}
+
+/*!
+ Returns the status of the text stream.
+
+ \sa QTextStream::Status, setStatus(), resetStatus()
+*/
+
+QTextStream::Status QTextStream::status() const
+{
+ Q_D(const QTextStream);
+ return d->status;
+}
+
+/*!
+ \since 4.1
+
+ Resets the status of the text stream.
+
+ \sa QTextStream::Status, status(), setStatus()
+*/
+void QTextStream::resetStatus()
+{
+ Q_D(QTextStream);
+ d->status = Ok;
+}
+
+/*!
+ \since 4.1
+
+ Sets the status of the text stream to the \a status given.
+
+ \sa Status status() resetStatus()
+*/
+void QTextStream::setStatus(Status status)
+{
+ Q_D(QTextStream);
+ if (d->status == Ok)
+ d->status = status;
+}
+
+/*!
+ Returns true if there is no more data to be read from the
+ QTextStream; otherwise returns false. This is similar to, but not
+ the same as calling QIODevice::atEnd(), as QTextStream also takes
+ into account its internal Unicode buffer.
+*/
+bool QTextStream::atEnd() const
+{
+ Q_D(const QTextStream);
+ CHECK_VALID_STREAM(true);
+
+ if (d->string)
+ return d->string->size() == d->stringOffset;
+ return d->readBuffer.isEmpty() && d->device->atEnd();
+}
+
+/*!
+ Reads the entire content of the stream, and returns it as a
+ QString. Avoid this function when working on large files, as it
+ will consume a significant amount of memory.
+
+ Calling readLine() is better if you do not know how much data is
+ available.
+
+ \sa readLine()
+*/
+QString QTextStream::readAll()
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(QString());
+
+ const QChar *readPtr;
+ int length;
+ if (!d->scan(&readPtr, &length, /* maxlen = */ 0, QTextStreamPrivate::EndOfFile))
+ return QString();
+
+ QString tmp = QString(readPtr, length);
+ d->consumeLastToken();
+ return tmp;
+}
+
+/*!
+ Reads one line of text from the stream, and returns it as a
+ QString. The maximum allowed line length is set to \a maxlen. If
+ the stream contains lines longer than this, then the lines will be
+ split after \a maxlen characters and returned in parts.
+
+ If \a maxlen is 0, the lines can be of any length. A common value
+ for \a maxlen is 75.
+
+ The returned line has no trailing end-of-line characters ("\\n"
+ or "\\r\\n"), so calling QString::trimmed() is unnecessary.
+
+ If the stream has read to the end of the file, readLine() will return a
+ null QString. For strings, or for devices that support it, you can
+ explicitly test for the end of the stream using atEnd().
+
+ \sa readAll(), QIODevice::readLine()
+*/
+QString QTextStream::readLine(qint64 maxlen)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(QString());
+
+ const QChar *readPtr;
+ int length;
+ if (!d->scan(&readPtr, &length, int(maxlen), QTextStreamPrivate::EndOfLine))
+ return QString();
+
+ QString tmp = QString(readPtr, length);
+ d->consumeLastToken();
+ return tmp;
+}
+
+/*!
+ \since 4.1
+
+ Reads at most \a maxlen characters from the stream, and returns the data
+ read as a QString.
+
+ \sa readAll(), readLine(), QIODevice::read()
+*/
+QString QTextStream::read(qint64 maxlen)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(QString());
+
+ if (maxlen <= 0)
+ return QString::fromLatin1(""); // empty, not null
+
+ const QChar *readPtr;
+ int length;
+ if (!d->scan(&readPtr, &length, int(maxlen), QTextStreamPrivate::EndOfFile))
+ return QString();
+
+ QString tmp = QString(readPtr, length);
+ d->consumeLastToken();
+ return tmp;
+}
+
+/*! \internal
+*/
+QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret)
+{
+ scan(0, 0, 0, NotSpace);
+ consumeLastToken();
+
+ // detect int encoding
+ int base = integerBase;
+ if (base == 0) {
+ QChar ch;
+ if (!getChar(&ch))
+ return npsInvalidPrefix;
+ if (ch == QLatin1Char('0')) {
+ QChar ch2;
+ if (!getChar(&ch2)) {
+ // Result is the number 0
+ *ret = 0;
+ return npsOk;
+ }
+ ch2 = ch2.toLower();
+
+ if (ch2 == QLatin1Char('x')) {
+ base = 16;
+ } else if (ch2 == QLatin1Char('b')) {
+ base = 2;
+ } else if (ch2.isDigit() && ch2.digitValue() >= 0 && ch2.digitValue() <= 7) {
+ base = 8;
+ } else {
+ base = 10;
+ }
+ ungetChar(ch2);
+ } else if (ch == locale.negativeSign() || ch == locale.positiveSign() || ch.isDigit()) {
+ base = 10;
+ } else {
+ ungetChar(ch);
+ return npsInvalidPrefix;
+ }
+ ungetChar(ch);
+ // State of the stream is now the same as on entry
+ // (cursor is at prefix),
+ // and local variable 'base' has been set appropriately.
+ }
+
+ qulonglong val=0;
+ switch (base) {
+ case 2: {
+ QChar pf1, pf2, dig;
+ // Parse prefix '0b'
+ if (!getChar(&pf1) || pf1 != QLatin1Char('0'))
+ return npsInvalidPrefix;
+ if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('b'))
+ return npsInvalidPrefix;
+ // Parse digits
+ int ndigits = 0;
+ while (getChar(&dig)) {
+ int n = dig.toLower().unicode();
+ if (n == '0' || n == '1') {
+ val <<= 1;
+ val += n - '0';
+ } else {
+ ungetChar(dig);
+ break;
+ }
+ ndigits++;
+ }
+ if (ndigits == 0) {
+ // Unwind the prefix and abort
+ ungetChar(pf2);
+ ungetChar(pf1);
+ return npsMissingDigit;
+ }
+ break;
+ }
+ case 8: {
+ QChar pf, dig;
+ // Parse prefix '0'
+ if (!getChar(&pf) || pf != QLatin1Char('0'))
+ return npsInvalidPrefix;
+ // Parse digits
+ int ndigits = 0;
+ while (getChar(&dig)) {
+ int n = dig.toLower().unicode();
+ if (n >= '0' && n <= '7') {
+ val *= 8;
+ val += n - '0';
+ } else {
+ ungetChar(dig);
+ break;
+ }
+ ndigits++;
+ }
+ if (ndigits == 0) {
+ // Unwind the prefix and abort
+ ungetChar(pf);
+ return npsMissingDigit;
+ }
+ break;
+ }
+ case 10: {
+ // Parse sign (or first digit)
+ QChar sign;
+ int ndigits = 0;
+ if (!getChar(&sign))
+ return npsMissingDigit;
+ if (sign != locale.negativeSign() && sign != locale.positiveSign()) {
+ if (!sign.isDigit()) {
+ ungetChar(sign);
+ return npsMissingDigit;
+ }
+ val += sign.digitValue();
+ ndigits++;
+ }
+ // Parse digits
+ QChar ch;
+ while (getChar(&ch)) {
+ if (ch.isDigit()) {
+ val *= 10;
+ val += ch.digitValue();
+ } else if (locale.language() != QLocale::C
+ && ch == locale.groupSeparator()) {
+ continue;
+ } else {
+ ungetChar(ch);
+ break;
+ }
+ ndigits++;
+ }
+ if (ndigits == 0)
+ return npsMissingDigit;
+ if (sign == locale.negativeSign()) {
+ qlonglong ival = qlonglong(val);
+ if (ival > 0)
+ ival = -ival;
+ val = qulonglong(ival);
+ }
+ break;
+ }
+ case 16: {
+ QChar pf1, pf2, dig;
+ // Parse prefix ' 0x'
+ if (!getChar(&pf1) || pf1 != QLatin1Char('0'))
+ return npsInvalidPrefix;
+ if (!getChar(&pf2) || pf2.toLower() != QLatin1Char('x'))
+ return npsInvalidPrefix;
+ // Parse digits
+ int ndigits = 0;
+ while (getChar(&dig)) {
+ int n = dig.toLower().unicode();
+ if (n >= '0' && n <= '9') {
+ val <<= 4;
+ val += n - '0';
+ } else if (n >= 'a' && n <= 'f') {
+ val <<= 4;
+ val += 10 + (n - 'a');
+ } else {
+ ungetChar(dig);
+ break;
+ }
+ ndigits++;
+ }
+ if (ndigits == 0) {
+ return npsMissingDigit;
+ }
+ break;
+ }
+ default:
+ // Unsupported integerBase
+ return npsInvalidPrefix;
+ }
+
+ if (ret)
+ *ret = val;
+ return npsOk;
+}
+
+/*! \internal
+ (hihi)
+*/
+bool QTextStreamPrivate::getReal(double *f)
+{
+ // We use a table-driven FSM to parse floating point numbers
+ // strtod() cannot be used directly since we may be reading from a
+ // QIODevice.
+ enum ParserState {
+ Init = 0,
+ Sign = 1,
+ Mantissa = 2,
+ Dot = 3,
+ Abscissa = 4,
+ ExpMark = 5,
+ ExpSign = 6,
+ Exponent = 7,
+ Nan1 = 8,
+ Nan2 = 9,
+ Inf1 = 10,
+ Inf2 = 11,
+ NanInf = 12,
+ Done = 13
+ };
+ enum InputToken {
+ None = 0,
+ InputSign = 1,
+ InputDigit = 2,
+ InputDot = 3,
+ InputExp = 4,
+ InputI = 5,
+ InputN = 6,
+ InputF = 7,
+ InputA = 8,
+ InputT = 9
+ };
+
+ static const uchar table[13][10] = {
+ // None InputSign InputDigit InputDot InputExp InputI InputN InputF InputA InputT
+ { 0, Sign, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 0 Init
+ { 0, 0, Mantissa, Dot, 0, Inf1, Nan1, 0, 0, 0 }, // 1 Sign
+ { Done, Done, Mantissa, Dot, ExpMark, 0, 0, 0, 0, 0 }, // 2 Mantissa
+ { 0, 0, Abscissa, 0, 0, 0, 0, 0, 0, 0 }, // 3 Dot
+ { Done, Done, Abscissa, Done, ExpMark, 0, 0, 0, 0, 0 }, // 4 Abscissa
+ { 0, ExpSign, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 5 ExpMark
+ { 0, 0, Exponent, 0, 0, 0, 0, 0, 0, 0 }, // 6 ExpSign
+ { Done, Done, Exponent, Done, Done, 0, 0, 0, 0, 0 }, // 7 Exponent
+ { 0, 0, 0, 0, 0, 0, 0, 0, Nan2, 0 }, // 8 Nan1
+ { 0, 0, 0, 0, 0, 0, NanInf, 0, 0, 0 }, // 9 Nan2
+ { 0, 0, 0, 0, 0, 0, Inf2, 0, 0, 0 }, // 10 Inf1
+ { 0, 0, 0, 0, 0, 0, 0, NanInf, 0, 0 }, // 11 Inf2
+ { Done, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 11 NanInf
+ };
+
+ ParserState state = Init;
+ InputToken input = None;
+
+ scan(0, 0, 0, NotSpace);
+ consumeLastToken();
+
+ const int BufferSize = 128;
+ char buf[BufferSize];
+ int i = 0;
+
+ QChar c;
+ while (getChar(&c)) {
+ switch (c.unicode()) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ input = InputDigit;
+ break;
+ case 'i': case 'I':
+ input = InputI;
+ break;
+ case 'n': case 'N':
+ input = InputN;
+ break;
+ case 'f': case 'F':
+ input = InputF;
+ break;
+ case 'a': case 'A':
+ input = InputA;
+ break;
+ case 't': case 'T':
+ input = InputT;
+ break;
+ default: {
+ QChar lc = c.toLower();
+ if (lc == locale.decimalPoint().toLower())
+ input = InputDot;
+ else if (lc == locale.exponential().toLower())
+ input = InputExp;
+ else if (lc == locale.negativeSign().toLower()
+ || lc == locale.positiveSign().toLower())
+ input = InputSign;
+ else if (locale.language() != QLocale::C // backward-compatibility
+ && lc == locale.groupSeparator().toLower())
+ input = InputDigit; // well, it isn't a digit, but no one cares.
+ else
+ input = None;
+ }
+ break;
+ }
+
+ state = ParserState(table[state][input]);
+
+ if (state == Init || state == Done || i > (BufferSize - 5)) {
+ ungetChar(c);
+ if (i > (BufferSize - 5)) { // ignore rest of digits
+ while (getChar(&c)) {
+ if (!c.isDigit()) {
+ ungetChar(c);
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ buf[i++] = c.toLatin1();
+ }
+
+ if (i == 0)
+ return false;
+ if (!f)
+ return true;
+ buf[i] = '\0';
+
+ // backward-compatibility. Old implmentation supported +nan/-nan
+ // for some reason. QLocale only checks for lower-case
+ // nan/+inf/-inf, so here we also check for uppercase and mixed
+ // case versions.
+ if (!qstricmp(buf, "nan") || !qstricmp(buf, "+nan") || !qstricmp(buf, "-nan")) {
+ *f = qSNaN();
+ return true;
+ } else if (!qstricmp(buf, "+inf") || !qstricmp(buf, "inf")) {
+ *f = qInf();
+ return true;
+ } else if (!qstricmp(buf, "-inf")) {
+ *f = -qInf();
+ return true;
+ }
+ bool ok;
+ *f = locale.toDouble(QString::fromLatin1(buf), &ok);
+ return ok;
+}
+
+/*!
+ Reads a character from the stream and stores it in \a c. Returns a
+ reference to the QTextStream, so several operators can be
+ nested. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 7
+
+ Whitespace is \e not skipped.
+*/
+
+QTextStream &QTextStream::operator>>(QChar &c)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ if (!d->getChar(&c))
+ setStatus(ReadPastEnd);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a character from the stream and stores it in \a c. The
+ character from the stream is converted to ISO-5589-1 before it is
+ stored.
+
+ \sa QChar::toLatin1()
+*/
+QTextStream &QTextStream::operator>>(char &c)
+{
+ QChar ch;
+ *this >> ch;
+ c = ch.toLatin1();
+ return *this;
+}
+
+/*!
+ Reads an integer from the stream and stores it in \a i, then
+ returns a reference to the QTextStream. The number is cast to
+ the correct type before it is stored. If no number was detected on
+ the stream, \a i is set to 0.
+
+ By default, QTextStream will attempt to detect the base of the
+ number using the following rules:
+
+ \table
+ \header \o Prefix \o Base
+ \row \o "0b" or "0B" \o 2 (binary)
+ \row \o "0" followed by "0-7" \o 8 (octal)
+ \row \o "0" otherwise \o 10 (decimal)
+ \row \o "0x" or "0X" \o 16 (hexadecimal)
+ \row \o "1" to "9" \o 10 (decimal)
+ \endtable
+
+ By calling setIntegerBase(), you can specify the integer base
+ explicitly. This will disable the auto-detection, and speed up
+ QTextStream slightly.
+
+ Leading whitespace is skipped.
+*/
+QTextStream &QTextStream::operator>>(signed short &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed short);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the unsigned short \a i.
+*/
+QTextStream &QTextStream::operator>>(unsigned short &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned short);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the signed int \a i.
+*/
+QTextStream &QTextStream::operator>>(signed int &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed int);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the unsigned int \a i.
+*/
+QTextStream &QTextStream::operator>>(unsigned int &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned int);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the signed long \a i.
+*/
+QTextStream &QTextStream::operator>>(signed long &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(signed long);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the unsigned long \a i.
+*/
+QTextStream &QTextStream::operator>>(unsigned long &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(unsigned long);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the qlonglong \a i.
+*/
+QTextStream &QTextStream::operator>>(qlonglong &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qlonglong);
+}
+
+/*!
+ \overload
+
+ Stores the integer in the qulonglong \a i.
+*/
+QTextStream &QTextStream::operator>>(qulonglong &i)
+{
+ IMPLEMENT_STREAM_RIGHT_INT_OPERATOR(qulonglong);
+}
+
+/*!
+ Reads a real number from the stream and stores it in \a f, then
+ returns a reference to the QTextStream. The number is cast to
+ the correct type. If no real number is detect on the stream, \a f
+ is set to 0.0.
+
+ As a special exception, QTextStream allows the strings "nan" and "inf" to
+ represent NAN and INF floats or doubles.
+
+ Leading whitespace is skipped.
+*/
+QTextStream &QTextStream::operator>>(float &f)
+{
+ IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(float);
+}
+
+/*!
+ \overload
+
+ Stores the real number in the double \a f.
+*/
+QTextStream &QTextStream::operator>>(double &f)
+{
+ IMPLEMENT_STREAM_RIGHT_REAL_OPERATOR(double);
+}
+
+/*!
+ Reads a word from the stream and stores it in \a str, then returns
+ a reference to the stream. Words are separated by whitespace
+ (i.e., all characters for which QChar::isSpace() returns true).
+
+ Leading whitespace is skipped.
+*/
+QTextStream &QTextStream::operator>>(QString &str)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+
+ str.clear();
+ d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->consumeLastToken();
+
+ const QChar *ptr;
+ int length;
+ if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) {
+ setStatus(ReadPastEnd);
+ return *this;
+ }
+
+ str = QString(ptr, length);
+ d->consumeLastToken();
+ return *this;
+}
+
+/*!
+ \overload
+
+ Converts the word to ISO-8859-1, then stores it in \a array.
+
+ \sa QString::toLatin1()
+*/
+QTextStream &QTextStream::operator>>(QByteArray &array)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+
+ array.clear();
+ d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->consumeLastToken();
+
+ const QChar *ptr;
+ int length;
+ if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) {
+ setStatus(ReadPastEnd);
+ return *this;
+ }
+
+ for (int i = 0; i < length; ++i)
+ array += ptr[i].toLatin1();
+
+ d->consumeLastToken();
+ return *this;
+}
+
+/*!
+ \overload
+
+ Stores the word in \a c, terminated by a '\0' character. If no word is
+ available, only the '\0' character is stored.
+
+ Warning: Although convenient, this operator is dangerous and must
+ be used with care. QTextStream assumes that \a c points to a
+ buffer with enough space to hold the word. If the buffer is too
+ small, your application may crash.
+
+ If possible, use the QByteArray operator instead.
+*/
+QTextStream &QTextStream::operator>>(char *c)
+{
+ Q_D(QTextStream);
+ *c = 0;
+ CHECK_VALID_STREAM(*this);
+ d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->consumeLastToken();
+
+ const QChar *ptr;
+ int length;
+ if (!d->scan(&ptr, &length, 0, QTextStreamPrivate::Space)) {
+ setStatus(ReadPastEnd);
+ return *this;
+ }
+
+ for (int i = 0; i < length; ++i)
+ *c++ = ptr[i].toLatin1();
+ *c = '\0';
+ d->consumeLastToken();
+ return *this;
+}
+
+/*! \internal
+ */
+bool QTextStreamPrivate::putNumber(qulonglong number, bool negative)
+{
+ QString result;
+
+ unsigned flags = 0;
+ if (numberFlags & QTextStream::ShowBase)
+ flags |= QLocalePrivate::ShowBase;
+ if (numberFlags & QTextStream::ForceSign)
+ flags |= QLocalePrivate::AlwaysShowSign;
+ if (numberFlags & QTextStream::UppercaseBase)
+ flags |= QLocalePrivate::UppercaseBase;
+ if (numberFlags & QTextStream::UppercaseDigits)
+ flags |= QLocalePrivate::CapitalEorX;
+
+ // add thousands group separators. For backward compatibility we
+ // don't add a group separator for C locale.
+ if (locale.language() != QLocale::C)
+ flags |= QLocalePrivate::ThousandsGroup;
+
+ const QLocalePrivate *dd = locale.d();
+ int base = integerBase ? integerBase : 10;
+ if (negative && base == 10) {
+ result = dd->longLongToString(-static_cast<qlonglong>(number), -1,
+ base, -1, flags);
+ } else if (negative) {
+ // Workaround for backward compatibility for writing negative
+ // numbers in octal and hex:
+ // QTextStream(result) << showbase << hex << -1 << oct << -1
+ // should output: -0x1 -0b1
+ result = dd->unsLongLongToString(number, -1, base, -1, flags);
+ result.prepend(locale.negativeSign());
+ } else {
+ result = dd->unsLongLongToString(number, -1, base, -1, flags);
+ // workaround for backward compatibility - in octal form with
+ // ShowBase flag set zero should be written as '00'
+ if (number == 0 && base == 8 && numberFlags & QTextStream::ShowBase
+ && result == QLatin1String("0")) {
+ result.prepend(QLatin1String("0"));
+ }
+ }
+ return putString(result, true);
+}
+
+/*!
+ \internal
+ \overload
+*/
+QTextStream &QTextStream::operator<<(QBool b)
+{
+ return *this << bool(b);
+}
+
+/*!
+ Writes the character \a c to the stream, then returns a reference
+ to the QTextStream.
+
+ \sa setFieldWidth()
+*/
+QTextStream &QTextStream::operator<<(QChar c)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putString(QString(c));
+ return *this;
+}
+
+/*!
+ \overload
+
+ Converts \a c from ASCII to a QChar, then writes it to the stream.
+*/
+QTextStream &QTextStream::operator<<(char c)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putString(QString(QChar::fromAscii(c)));
+ return *this;
+}
+
+/*!
+ Writes the integer number \a i to the stream, then returns a
+ reference to the QTextStream. By default, the number is stored in
+ decimal form, but you can also set the base by calling
+ setIntegerBase().
+
+ \sa setFieldWidth(), setNumberFlags()
+*/
+QTextStream &QTextStream::operator<<(signed short i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the unsigned short \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(unsigned short i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)i, false);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the signed int \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(signed int i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the unsigned int \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(unsigned int i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)i, false);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the signed long \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(signed long i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)qAbs(qlonglong(i)), i < 0);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the unsigned long \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(unsigned long i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)i, false);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the qlonglong \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(qlonglong i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber((qulonglong)qAbs(i), i < 0);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the qulonglong \a i to the stream.
+*/
+QTextStream &QTextStream::operator<<(qulonglong i)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putNumber(i, false);
+ return *this;
+}
+
+/*!
+ Writes the real number \a f to the stream, then returns a
+ reference to the QTextStream. By default, QTextStream stores it
+ using SmartNotation, with up to 6 digits of precision. You can
+ change the textual representation QTextStream will use for real
+ numbers by calling setRealNumberNotation(),
+ setRealNumberPrecision() and setNumberFlags().
+
+ \sa setFieldWidth(), setRealNumberNotation(),
+ setRealNumberPrecision(), setNumberFlags()
+*/
+QTextStream &QTextStream::operator<<(float f)
+{
+ return *this << double(f);
+}
+
+/*!
+ \overload
+
+ Writes the double \a f to the stream.
+*/
+QTextStream &QTextStream::operator<<(double f)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+
+ QLocalePrivate::DoubleForm form = QLocalePrivate::DFDecimal;
+ switch (realNumberNotation()) {
+ case FixedNotation:
+ form = QLocalePrivate::DFDecimal;
+ break;
+ case ScientificNotation:
+ form = QLocalePrivate::DFExponent;
+ break;
+ case SmartNotation:
+ form = QLocalePrivate::DFSignificantDigits;
+ break;
+ }
+
+ uint flags = 0;
+ if (numberFlags() & ShowBase)
+ flags |= QLocalePrivate::ShowBase;
+ if (numberFlags() & ForceSign)
+ flags |= QLocalePrivate::AlwaysShowSign;
+ if (numberFlags() & UppercaseBase)
+ flags |= QLocalePrivate::UppercaseBase;
+ if (numberFlags() & UppercaseDigits)
+ flags |= QLocalePrivate::CapitalEorX;
+ if (numberFlags() & ForcePoint)
+ flags |= QLocalePrivate::Alternate;
+
+ const QLocalePrivate *dd = d->locale.d();
+ QString num = dd->doubleToString(f, d->realNumberPrecision, form, -1, flags);
+ d->putString(num, true);
+ return *this;
+}
+
+/*!
+ Writes the string \a string to the stream, and returns a reference
+ to the QTextStream. The string is first encoded using the assigned
+ codec (the default codec is QTextCodec::codecForLocale()) before
+ it is written to the stream.
+
+ \sa setFieldWidth(), setCodec()
+*/
+QTextStream &QTextStream::operator<<(const QString &string)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putString(string);
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes \a array to the stream. The contents of \a array are
+ converted with QString::fromAscii().
+*/
+QTextStream &QTextStream::operator<<(const QByteArray &array)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putString(QString::fromAscii(array.constData(), array.length()));
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes the constant string pointed to by \a string to the stream. \a
+ string is assumed to be in ISO-8859-1 encoding. This operator
+ is convenient when working with constant string data. Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 8
+
+ Warning: QTextStream assumes that \a string points to a string of
+ text, terminated by a '\0' character. If there is no terminating
+ '\0' character, your application may crash.
+*/
+QTextStream &QTextStream::operator<<(const char *string)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ d->putString(QLatin1String(string));
+ return *this;
+}
+
+/*!
+ \overload
+
+ Writes \a ptr to the stream as a hexadecimal number with a base.
+*/
+
+QTextStream &QTextStream::operator<<(const void *ptr)
+{
+ Q_D(QTextStream);
+ CHECK_VALID_STREAM(*this);
+ int oldBase = d->integerBase;
+ NumberFlags oldFlags = d->numberFlags;
+ d->integerBase = 16;
+ d->numberFlags |= ShowBase;
+ d->putNumber(reinterpret_cast<quintptr>(ptr), false);
+ d->integerBase = oldBase;
+ d->numberFlags = oldFlags;
+ return *this;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setIntegerBase(2) on \a stream and returns \a
+ stream.
+
+ \sa oct(), dec(), hex(), {QTextStream manipulators}
+*/
+QTextStream &bin(QTextStream &stream)
+{
+ stream.setIntegerBase(2);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setIntegerBase(8) on \a stream and returns \a
+ stream.
+
+ \sa bin(), dec(), hex(), {QTextStream manipulators}
+*/
+QTextStream &oct(QTextStream &stream)
+{
+ stream.setIntegerBase(8);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setIntegerBase(10) on \a stream and returns \a
+ stream.
+
+ \sa bin(), oct(), hex(), {QTextStream manipulators}
+*/
+QTextStream &dec(QTextStream &stream)
+{
+ stream.setIntegerBase(10);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setIntegerBase(16) on \a stream and returns \a
+ stream.
+
+ \note The hex modifier can only be used for writing to streams.
+ \sa bin(), oct(), dec(), {QTextStream manipulators}
+*/
+QTextStream &hex(QTextStream &stream)
+{
+ stream.setIntegerBase(16);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() |
+ QTextStream::ShowBase) on \a stream and returns \a stream.
+
+ \sa noshowbase(), forcesign(), forcepoint(), {QTextStream manipulators}
+*/
+QTextStream &showbase(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() | QTextStream::ShowBase);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() |
+ QTextStream::ForceSign) on \a stream and returns \a stream.
+
+ \sa noforcesign(), forcepoint(), showbase(), {QTextStream manipulators}
+*/
+QTextStream &forcesign(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() | QTextStream::ForceSign);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() |
+ QTextStream::ForcePoint) on \a stream and returns \a stream.
+
+ \sa noforcepoint(), forcesign(), showbase(), {QTextStream manipulators}
+*/
+QTextStream &forcepoint(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() | QTextStream::ForcePoint);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() &
+ ~QTextStream::ShowBase) on \a stream and returns \a stream.
+
+ \sa showbase(), noforcesign(), noforcepoint(), {QTextStream manipulators}
+*/
+QTextStream &noshowbase(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ShowBase);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() &
+ ~QTextStream::ForceSign) on \a stream and returns \a stream.
+
+ \sa forcesign(), noforcepoint(), noshowbase(), {QTextStream manipulators}
+*/
+QTextStream &noforcesign(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForceSign);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() &
+ ~QTextStream::ForcePoint) on \a stream and returns \a stream.
+
+ \sa forcepoint(), noforcesign(), noshowbase(), {QTextStream manipulators}
+*/
+QTextStream &noforcepoint(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() &= ~QTextStream::ForcePoint);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() |
+ QTextStream::UppercaseBase) on \a stream and returns \a stream.
+
+ \sa lowercasebase(), uppercasedigits(), {QTextStream manipulators}
+*/
+QTextStream &uppercasebase(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseBase);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() |
+ QTextStream::UppercaseDigits) on \a stream and returns \a stream.
+
+ \sa lowercasedigits(), uppercasebase(), {QTextStream manipulators}
+*/
+QTextStream &uppercasedigits(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() | QTextStream::UppercaseDigits);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() &
+ ~QTextStream::UppercaseBase) on \a stream and returns \a stream.
+
+ \sa uppercasebase(), lowercasedigits(), {QTextStream manipulators}
+*/
+QTextStream &lowercasebase(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseBase);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setNumberFlags(QTextStream::numberFlags() &
+ ~QTextStream::UppercaseDigits) on \a stream and returns \a stream.
+
+ \sa uppercasedigits(), lowercasebase(), {QTextStream manipulators}
+*/
+QTextStream &lowercasedigits(QTextStream &stream)
+{
+ stream.setNumberFlags(stream.numberFlags() & ~QTextStream::UppercaseDigits);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setRealNumberNotation(QTextStream::FixedNotation)
+ on \a stream and returns \a stream.
+
+ \sa scientific(), {QTextStream manipulators}
+*/
+QTextStream &fixed(QTextStream &stream)
+{
+ stream.setRealNumberNotation(QTextStream::FixedNotation);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setRealNumberNotation(QTextStream::ScientificNotation)
+ on \a stream and returns \a stream.
+
+ \sa fixed(), {QTextStream manipulators}
+*/
+QTextStream &scientific(QTextStream &stream)
+{
+ stream.setRealNumberNotation(QTextStream::ScientificNotation);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setFieldAlignment(QTextStream::AlignLeft)
+ on \a stream and returns \a stream.
+
+ \sa right(), center(), {QTextStream manipulators}
+*/
+QTextStream &left(QTextStream &stream)
+{
+ stream.setFieldAlignment(QTextStream::AlignLeft);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setFieldAlignment(QTextStream::AlignRight)
+ on \a stream and returns \a stream.
+
+ \sa left(), center(), {QTextStream manipulators}
+*/
+QTextStream &right(QTextStream &stream)
+{
+ stream.setFieldAlignment(QTextStream::AlignRight);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter)
+ on \a stream and returns \a stream.
+
+ \sa left(), right(), {QTextStream manipulators}
+*/
+QTextStream &center(QTextStream &stream)
+{
+ stream.setFieldAlignment(QTextStream::AlignCenter);
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Writes '\n' to the \a stream and flushes the stream.
+
+ Equivalent to
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 9
+
+ Note: On Windows, all '\n' characters are written as '\r\n' if
+ QTextStream's device or string is opened using the QIODevice::Text flag.
+
+ \sa flush(), reset(), {QTextStream manipulators}
+*/
+QTextStream &endl(QTextStream &stream)
+{
+ return stream << QLatin1Char('\n') << flush;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::flush() on \a stream and returns \a stream.
+
+ \sa endl(), reset(), {QTextStream manipulators}
+*/
+QTextStream &flush(QTextStream &stream)
+{
+ stream.flush();
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls QTextStream::reset() on \a stream and returns \a stream.
+
+ \sa flush(), {QTextStream manipulators}
+*/
+QTextStream &reset(QTextStream &stream)
+{
+ stream.reset();
+ return stream;
+}
+
+/*!
+ \relates QTextStream
+
+ Calls skipWhiteSpace() on \a stream and returns \a stream.
+
+ \sa {QTextStream manipulators}
+*/
+QTextStream &ws(QTextStream &stream)
+{
+ stream.skipWhiteSpace();
+ return stream;
+}
+
+/*!
+ \fn QTextStreamManipulator qSetFieldWidth(int width)
+ \relates QTextStream
+
+ Equivalent to QTextStream::setFieldWidth(\a width).
+*/
+
+/*!
+ \fn QTextStreamManipulator qSetPadChar(QChar ch)
+ \relates QTextStream
+
+ Equivalent to QTextStream::setPadChar(\a ch).
+*/
+
+/*!
+ \fn QTextStreamManipulator qSetRealNumberPrecision(int precision)
+ \relates QTextStream
+
+ Equivalent to QTextStream::setRealNumberPrecision(\a precision).
+*/
+
+#ifndef QT_NO_TEXTCODEC
+/*!
+ \relates QTextStream
+
+ Toggles insertion of the Byte Order Mark on \a stream when QTextStream is
+ used with a UTF codec.
+
+ \sa QTextStream::setGenerateByteOrderMark(), {QTextStream manipulators}
+*/
+QTextStream &bom(QTextStream &stream)
+{
+ stream.setGenerateByteOrderMark(true);
+ return stream;
+}
+
+/*!
+ Sets the codec for this stream to \a codec. The codec is used for
+ decoding any data that is read from the assigned device, and for
+ encoding any data that is written. By default,
+ QTextCodec::codecForLocale() is used, and automatic unicode
+ detection is enabled.
+
+ If QTextStream operates on a string, this function does nothing.
+
+ \warning If you call this function while the text stream is reading
+ from an open sequential socket, the internal buffer may still contain
+ text decoded using the old codec.
+
+ \sa codec(), setAutoDetectUnicode(), setLocale()
+*/
+void QTextStream::setCodec(QTextCodec *codec)
+{
+ Q_D(QTextStream);
+ qint64 seekPos = -1;
+ if (!d->readBuffer.isEmpty()) {
+ if (!d->device->isSequential()) {
+ seekPos = pos();
+ }
+ }
+ d->codec = codec;
+ if (seekPos >=0 && !d->readBuffer.isEmpty())
+ seek(seekPos);
+}
+
+/*!
+ Sets the codec for this stream to the QTextCodec for the encoding
+ specified by \a codecName. Common values for \c codecName include
+ "ISO 8859-1", "UTF-8", and "UTF-16". If the encoding isn't
+ recognized, nothing happens.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qtextstream.cpp 10
+
+ \sa QTextCodec::codecForName(), setLocale()
+*/
+void QTextStream::setCodec(const char *codecName)
+{
+ QTextCodec *codec = QTextCodec::codecForName(codecName);
+ if (codec)
+ setCodec(codec);
+}
+
+/*!
+ Returns the codec that is current assigned to the stream.
+
+ \sa setCodec(), setAutoDetectUnicode(), locale()
+*/
+QTextCodec *QTextStream::codec() const
+{
+ Q_D(const QTextStream);
+ return d->codec;
+}
+
+/*!
+ If \a enabled is true, QTextStream will attempt to detect Unicode
+ encoding by peeking into the stream data to see if it can find the
+ UTF-16 or UTF-32 BOM (Byte Order Mark). If this mark is found, QTextStream
+ will replace the current codec with the UTF codec.
+
+ This function can be used together with setCodec(). It is common
+ to set the codec to UTF-8, and then enable UTF-16 detection.
+
+ \sa autoDetectUnicode(), setCodec()
+*/
+void QTextStream::setAutoDetectUnicode(bool enabled)
+{
+ Q_D(QTextStream);
+ d->autoDetectUnicode = enabled;
+}
+
+/*!
+ Returns true if automatic Unicode detection is enabled; otherwise
+ returns false.
+
+ \sa setAutoDetectUnicode(), setCodec()
+*/
+bool QTextStream::autoDetectUnicode() const
+{
+ Q_D(const QTextStream);
+ return d->autoDetectUnicode;
+}
+
+/*!
+ If \a generate is true and a UTF codec is used, QTextStream will insert
+ the BOM (Byte Order Mark) before any data has been written to the
+ device. If \a generate is false, no BOM will be inserted. This function
+ must be called before any data is written. Otherwise, it does nothing.
+
+ \sa generateByteOrderMark(), bom()
+*/
+void QTextStream::setGenerateByteOrderMark(bool generate)
+{
+ Q_D(QTextStream);
+ if (d->writeBuffer.isEmpty()) {
+ if (generate)
+ d->writeConverterState.flags &= ~QTextCodec::IgnoreHeader;
+ else
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ }
+}
+
+/*!
+ Returns true if QTextStream is set to generate the UTF BOM (Byte Order
+ Mark) when using a UTF codec; otherwise returns false.
+
+ \sa setGenerateByteOrderMark()
+*/
+bool QTextStream::generateByteOrderMark() const
+{
+ Q_D(const QTextStream);
+ return (d->writeConverterState.flags & QTextCodec::IgnoreHeader) == 0;
+}
+
+#endif
+
+/*!
+ \since 4.5
+
+ Sets the locale for this stream to \a locale. The specified locale is
+ used for conversions between numbers and their string representations.
+
+ The default locale is C and it is a special case - the thousands
+ group separator is not used for backward compatibility reasons.
+
+ \sa locale()
+*/
+void QTextStream::setLocale(const QLocale &locale)
+{
+ Q_D(QTextStream);
+ d->locale = locale;
+}
+
+/*!
+ \since 4.5
+
+ Returns the locale for this stream. The default locale is C.
+
+ \sa setLocale()
+*/
+QLocale QTextStream::locale() const
+{
+ Q_D(const QTextStream);
+ return d->locale;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ \class QTextIStream
+ \brief The QTextIStream class is a convenience class for input streams.
+
+ \compat
+ \reentrant
+ \ingroup io
+ \ingroup text
+
+ Use QTextStream instead.
+*/
+
+/*!
+ \fn QTextIStream::QTextIStream(const QString *string)
+
+ Use QTextStream(&\a{string}, QIODevice::ReadOnly) instead.
+*/
+/*!
+ \fn QTextIStream::QTextIStream(QByteArray *byteArray)
+
+ Use QTextStream(&\a{byteArray}, QIODevice::ReadOnly) instead.
+*/
+/*!
+ \fn QTextIStream::QTextIStream(FILE *file)
+
+ Use QTextStream(\a{file}, QIODevice::ReadOnly) instead.
+*/
+
+/*!
+ \class QTextOStream
+ \brief The QTextOStream class is a convenience class for output streams.
+
+ \compat
+ \reentrant
+ \ingroup io
+ \ingroup text
+
+ Use QTextStream instead.
+*/
+
+/*!
+ \fn QTextOStream::QTextOStream(QString *string)
+
+ Use QTextStream(&\a{string}, QIODevice::WriteOnly) instead.
+*/
+/*!
+ \fn QTextOStream::QTextOStream(QByteArray *byteArray)
+
+ Use QTextStream(&\a{byteArray}, QIODevice::WriteOnly) instead.
+*/
+/*!
+ \fn QTextOStream::QTextOStream(FILE *file)
+
+ Use QTextStream(\a{file}, QIODevice::WriteOnly) instead.
+*/
+
+/*! \internal
+*/
+int QTextStream::flagsInternal() const
+{
+ Q_D(const QTextStream);
+
+ int f = 0;
+ switch (d->fieldAlignment) {
+ case AlignLeft: f |= left; break;
+ case AlignRight: f |= right; break;
+ case AlignCenter: f |= internal; break;
+ default:
+ break;
+ }
+ switch (d->integerBase) {
+ case 2: f |= bin; break;
+ case 8: f |= oct; break;
+ case 10: f |= dec; break;
+ case 16: f |= hex; break;
+ default:
+ break;
+ }
+ switch (d->realNumberNotation) {
+ case FixedNotation: f |= fixed; break;
+ case ScientificNotation: f |= scientific; break;
+ default:
+ break;
+ }
+ if (d->numberFlags & ShowBase)
+ f |= showbase;
+ if (d->numberFlags & ForcePoint)
+ f |= showpoint;
+ if (d->numberFlags & ForceSign)
+ f |= showpos;
+ if (d->numberFlags & UppercaseBase)
+ f |= uppercase;
+ return f;
+}
+
+/*! \internal
+*/
+int QTextStream::flagsInternal(int newFlags)
+{
+ int oldFlags = flagsInternal();
+
+ if (newFlags & left)
+ setFieldAlignment(AlignLeft);
+ else if (newFlags & right)
+ setFieldAlignment(AlignRight);
+ else if (newFlags & internal)
+ setFieldAlignment(AlignCenter);
+
+ if (newFlags & bin)
+ setIntegerBase(2);
+ else if (newFlags & oct)
+ setIntegerBase(8);
+ else if (newFlags & dec)
+ setIntegerBase(10);
+ else if (newFlags & hex)
+ setIntegerBase(16);
+
+ if (newFlags & showbase)
+ setNumberFlags(numberFlags() | ShowBase);
+ if (newFlags & showpos)
+ setNumberFlags(numberFlags() | ForceSign);
+ if (newFlags & showpoint)
+ setNumberFlags(numberFlags() | ForcePoint);
+ if (newFlags & uppercase)
+ setNumberFlags(numberFlags() | UppercaseBase);
+
+ if (newFlags & fixed)
+ setRealNumberNotation(FixedNotation);
+ else if (newFlags & scientific)
+ setRealNumberNotation(ScientificNotation);
+
+ return oldFlags;
+}
+
+#ifndef QT_NO_TEXTCODEC
+/*!
+ Use setCodec() and setAutoDetectUnicode() instead.
+*/
+void QTextStream::setEncoding(Encoding encoding)
+{
+ Q_D(QTextStream);
+ resetCodecConverterStateHelper(&d->readConverterState);
+ resetCodecConverterStateHelper(&d->writeConverterState);
+
+ switch (encoding) {
+ case Locale:
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForLocale());
+ d->autoDetectUnicode = true;
+ break;
+ case Latin1:
+ d->readConverterState.flags |= QTextCodec::IgnoreHeader;
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForName("ISO-8859-1"));
+ d->autoDetectUnicode = false;
+ break;
+ case Unicode:
+ setCodec(QTextCodec::codecForName("UTF-16"));
+ d->autoDetectUnicode = false;
+ break;
+ case RawUnicode:
+ d->readConverterState.flags |= QTextCodec::IgnoreHeader;
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForName("UTF-16"));
+ d->autoDetectUnicode = false;
+ break;
+ case UnicodeNetworkOrder:
+ d->readConverterState.flags |= QTextCodec::IgnoreHeader;
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForName("UTF-16BE"));
+ d->autoDetectUnicode = false;
+ break;
+ case UnicodeReverse:
+ d->readConverterState.flags |= QTextCodec::IgnoreHeader;
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForName("UTF-16LE"));
+ d->autoDetectUnicode = false;
+ break;
+ case UnicodeUTF8:
+ d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
+ setCodec(QTextCodec::codecForName("UTF-8"));
+ d->autoDetectUnicode = true;
+ break;
+ }
+}
+#endif
+
+/*!
+ \enum QTextStream::Encoding
+ \compat
+
+ \value Latin1 Use setCodec(QTextCodec::codecForName("ISO-8859-1")) instead.
+ \value Locale Use setCodec(QTextCodec::codecForLocale()) instead.
+ \value RawUnicode Use setCodec(QTextCodec::codecForName("UTF-16")) instead.
+ \value Unicode Use setCodec(QTextCodec::codecForName("UTF-16")) instead.
+ \value UnicodeNetworkOrder Use setCodec(QTextCodec::codecForName("UTF-16BE")) instead.
+ \value UnicodeReverse Use setCodec(QTextCodec::codecForName("UTF-16LE")) instead.
+ \value UnicodeUTF8 Use setCodec(QTextCodec::codecForName("UTF-8")) instead.
+
+ Also, for all encodings except QTextStream::Latin1 and
+ QTextStream::UTF8, you need to call setAutoDetectUnicode(false)
+ to obtain the Qt 3 behavior in addition to the setCodec() call.
+
+ \sa setCodec(), setAutoDetectUnicode()
+*/
+
+/*!
+ \fn int QTextStream::flags() const
+
+ Use fieldAlignment(), padChar(), fieldWidth(), numberFlags(),
+ integerBase(), realNumberNotation(), and realNumberNotation
+ instead.
+*/
+
+/*!
+ \fn int QTextStream::flags(int)
+
+ Use setFieldAlignment(), setPadChar(), setFieldWidth(),
+ setNumberFlags(), setIntegerBase(), setRealNumberNotation(), and
+ setRealNumberNotation instead.
+*/
+
+/*!
+ \fn int QTextStream::setf(int)
+
+ Use setFieldAlignment(), setPadChar(), setFieldWidth(),
+ setNumberFlags(), setIntegerBase(), setRealNumberNotation(), and
+ setRealNumberNotation instead.
+*/
+
+/*!
+ \fn int QTextStream::setf(int, int)
+
+ Use setFieldAlignment(), setPadChar(), setFieldWidth(),
+ setNumberFlags(), setIntegerBase(), setRealNumberNotation(), and
+ setRealNumberNotation instead.
+*/
+
+/*!
+ \fn int QTextStream::unsetf(int)
+
+ Use setFieldAlignment(), setPadChar(), setFieldWidth(),
+ setNumberFlags(), setIntegerBase(), setRealNumberNotation(), and
+ setRealNumberNotation instead.
+*/
+
+/*!
+ \fn int QTextStream::width(int)
+
+ Use setFieldWidth() instead.
+*/
+
+/*!
+ \fn int QTextStream::fill(int)
+
+ Use setPadChar() instead.
+*/
+
+/*!
+ \fn int QTextStream::precision(int)
+
+ Use setRealNumberPrecision() instead.
+*/
+
+/*!
+ \fn int QTextStream::read()
+
+ Use readAll() or readLine() instead.
+*/
+
+/*!
+ \fn int QTextStream::unsetDevice()
+
+ Use setDevice(0) instead.
+*/
+
+/*!
+ \variable QTextStream::skipws
+ \variable QTextStream::left
+ \variable QTextStream::right
+ \variable QTextStream::internal
+ \variable QTextStream::bin
+ \variable QTextStream::oct
+ \variable QTextStream::dec
+ \variable QTextStream::hex
+ \variable QTextStream::showbase
+ \variable QTextStream::showpoint
+ \variable QTextStream::uppercase
+ \variable QTextStream::showpos
+ \variable QTextStream::scientific
+ \variable QTextStream::fixed
+ \variable QTextStream::basefield
+ \variable QTextStream::adjustfield
+ \variable QTextStream::floatfield
+ \compat
+
+ Use the new \l{QTextStream manipulators} instead.
+*/
+
+#endif
+
+QT_END_NAMESPACE
+
+#ifndef QT_NO_QOBJECT
+#include "qtextstream.moc"
+#endif
+
diff --git a/src/corelib/io/qtextstream.h b/src/corelib/io/qtextstream.h
new file mode 100644
index 0000000..6302d34
--- /dev/null
+++ b/src/corelib/io/qtextstream.h
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTSTREAM_H
+#define QTEXTSTREAM_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qchar.h>
+#include <QtCore/qlocale.h>
+
+#ifndef QT_NO_TEXTCODEC
+# ifdef QT3_SUPPORT
+# include <QtCore/qtextcodec.h>
+# endif
+#endif
+
+#include <stdio.h>
+
+#ifdef Status
+#error qtextstream.h must be included before any header file that defines Status
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QTextCodec;
+class QTextDecoder;
+
+class QTextStreamPrivate;
+class Q_CORE_EXPORT QTextStream // text stream class
+{
+ Q_DECLARE_PRIVATE(QTextStream)
+
+public:
+ enum RealNumberNotation {
+ SmartNotation,
+ FixedNotation,
+ ScientificNotation
+ };
+ enum FieldAlignment {
+ AlignLeft,
+ AlignRight,
+ AlignCenter,
+ AlignAccountingStyle
+ };
+ enum Status {
+ Ok,
+ ReadPastEnd,
+ ReadCorruptData
+ };
+ enum NumberFlag {
+ ShowBase = 0x1,
+ ForcePoint = 0x2,
+ ForceSign = 0x4,
+ UppercaseBase = 0x8,
+ UppercaseDigits = 0x10
+ };
+ Q_DECLARE_FLAGS(NumberFlags, NumberFlag)
+
+ QTextStream();
+ explicit QTextStream(QIODevice *device);
+ explicit QTextStream(FILE *fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite);
+ explicit QTextStream(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite);
+ explicit QTextStream(QByteArray *array, QIODevice::OpenMode openMode = QIODevice::ReadWrite);
+ explicit QTextStream(const QByteArray &array, QIODevice::OpenMode openMode = QIODevice::ReadOnly);
+ virtual ~QTextStream();
+
+#ifndef QT_NO_TEXTCODEC
+ void setCodec(QTextCodec *codec);
+ void setCodec(const char *codecName);
+ QTextCodec *codec() const;
+ void setAutoDetectUnicode(bool enabled);
+ bool autoDetectUnicode() const;
+ void setGenerateByteOrderMark(bool generate);
+ bool generateByteOrderMark() const;
+#endif
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void setString(QString *string, QIODevice::OpenMode openMode = QIODevice::ReadWrite);
+ QString *string() const;
+
+ Status status() const;
+ void setStatus(Status status);
+ void resetStatus();
+
+ bool atEnd() const;
+ void reset();
+ void flush();
+ bool seek(qint64 pos);
+ qint64 pos() const;
+
+ void skipWhiteSpace();
+
+ QString readLine(qint64 maxlen = 0);
+ QString readAll();
+ QString read(qint64 maxlen);
+
+ void setFieldAlignment(FieldAlignment alignment);
+ FieldAlignment fieldAlignment() const;
+
+ void setPadChar(QChar ch);
+ QChar padChar() const;
+
+ void setFieldWidth(int width);
+ int fieldWidth() const;
+
+ void setNumberFlags(NumberFlags flags);
+ NumberFlags numberFlags() const;
+
+ void setIntegerBase(int base);
+ int integerBase() const;
+
+ void setRealNumberNotation(RealNumberNotation notation);
+ RealNumberNotation realNumberNotation() const;
+
+ void setRealNumberPrecision(int precision);
+ int realNumberPrecision() const;
+
+ QTextStream &operator>>(QChar &ch);
+ QTextStream &operator>>(char &ch);
+ QTextStream &operator>>(signed short &i);
+ QTextStream &operator>>(unsigned short &i);
+ QTextStream &operator>>(signed int &i);
+ QTextStream &operator>>(unsigned int &i);
+ QTextStream &operator>>(signed long &i);
+ QTextStream &operator>>(unsigned long &i);
+ QTextStream &operator>>(qlonglong &i);
+ QTextStream &operator>>(qulonglong &i);
+ QTextStream &operator>>(float &f);
+ QTextStream &operator>>(double &f);
+ QTextStream &operator>>(QString &s);
+ QTextStream &operator>>(QByteArray &array);
+ QTextStream &operator>>(char *c);
+
+ QTextStream &operator<<(QBool b);
+ QTextStream &operator<<(QChar ch);
+ QTextStream &operator<<(char ch);
+ QTextStream &operator<<(signed short i);
+ QTextStream &operator<<(unsigned short i);
+ QTextStream &operator<<(signed int i);
+ QTextStream &operator<<(unsigned int i);
+ QTextStream &operator<<(signed long i);
+ QTextStream &operator<<(unsigned long i);
+ QTextStream &operator<<(qlonglong i);
+ QTextStream &operator<<(qulonglong i);
+ QTextStream &operator<<(float f);
+ QTextStream &operator<<(double f);
+ QTextStream &operator<<(const QString &s);
+ QTextStream &operator<<(const QByteArray &array);
+ QTextStream &operator<<(const char *c);
+ QTextStream &operator<<(const void *ptr);
+
+#ifdef QT3_SUPPORT
+ // not marked as QT3_SUPPORT to avoid double compiler warnings, as
+ // they are used in the QT3_SUPPORT functions below.
+ inline QT3_SUPPORT int flags() const { return flagsInternal(); }
+ inline QT3_SUPPORT int flags(int f) { return flagsInternal(f); }
+
+ inline QT3_SUPPORT int setf(int bits)
+ { int old = flagsInternal(); flagsInternal(flagsInternal() | bits); return old; }
+ inline QT3_SUPPORT int setf(int bits, int mask)
+ { int old = flagsInternal(); flagsInternal(flagsInternal() | (bits & mask)); return old; }
+ inline QT3_SUPPORT int unsetf(int bits)
+ { int old = flagsInternal(); flagsInternal(flagsInternal() & ~bits); return old; }
+
+ inline QT3_SUPPORT int width(int w)
+ { int old = fieldWidth(); setFieldWidth(w); return old; }
+ inline QT3_SUPPORT int fill(int f)
+ { QChar ch = padChar(); setPadChar(QChar(f)); return ch.unicode(); }
+ inline QT3_SUPPORT int precision(int p)
+ { int old = realNumberPrecision(); setRealNumberPrecision(p); return old; }
+
+ enum {
+ skipws = 0x0001, // skip whitespace on input
+ left = 0x0002, // left-adjust output
+ right = 0x0004, // right-adjust output
+ internal = 0x0008, // pad after sign
+ bin = 0x0010, // binary format integer
+ oct = 0x0020, // octal format integer
+ dec = 0x0040, // decimal format integer
+ hex = 0x0080, // hex format integer
+ showbase = 0x0100, // show base indicator
+ showpoint = 0x0200, // force decimal point (float)
+ uppercase = 0x0400, // upper-case hex output
+ showpos = 0x0800, // add '+' to positive integers
+ scientific = 0x1000, // scientific float output
+ fixed = 0x2000 // fixed float output
+ };
+ enum {
+ basefield = bin | oct | dec | hex,
+ adjustfield = left | right | internal,
+ floatfield = scientific | fixed
+ };
+
+#ifndef QT_NO_TEXTCODEC
+ enum Encoding { Locale, Latin1, Unicode, UnicodeNetworkOrder,
+ UnicodeReverse, RawUnicode, UnicodeUTF8 };
+ QT3_SUPPORT void setEncoding(Encoding encoding);
+#endif
+ inline QT3_SUPPORT QString read() { return readAll(); }
+ inline QT3_SUPPORT void unsetDevice() { setDevice(0); }
+#endif
+
+private:
+#ifdef QT3_SUPPORT
+ int flagsInternal() const;
+ int flagsInternal(int flags);
+#endif
+
+ Q_DISABLE_COPY(QTextStream)
+
+ QTextStreamPrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextStream::NumberFlags)
+
+/*****************************************************************************
+ QTextStream manipulators
+ *****************************************************************************/
+
+typedef QTextStream & (*QTextStreamFunction)(QTextStream &);// manipulator function
+typedef void (QTextStream::*QTSMFI)(int); // manipulator w/int argument
+typedef void (QTextStream::*QTSMFC)(QChar); // manipulator w/QChar argument
+
+class Q_CORE_EXPORT QTextStreamManipulator
+{
+public:
+ QTextStreamManipulator(QTSMFI m, int a) { mf = m; mc = 0; arg = a; }
+ QTextStreamManipulator(QTSMFC m, QChar c) { mf = 0; mc = m; ch = c; }
+ void exec(QTextStream &s) { if (mf) { (s.*mf)(arg); } else { (s.*mc)(ch); } }
+
+private:
+ QTSMFI mf; // QTextStream member function
+ QTSMFC mc; // QTextStream member function
+ int arg; // member function argument
+ QChar ch;
+};
+
+inline QTextStream &operator>>(QTextStream &s, QTextStreamFunction f)
+{ return (*f)(s); }
+
+inline QTextStream &operator<<(QTextStream &s, QTextStreamFunction f)
+{ return (*f)(s); }
+
+inline QTextStream &operator<<(QTextStream &s, QTextStreamManipulator m)
+{ m.exec(s); return s; }
+
+Q_CORE_EXPORT QTextStream &bin(QTextStream &s);
+Q_CORE_EXPORT QTextStream &oct(QTextStream &s);
+Q_CORE_EXPORT QTextStream &dec(QTextStream &s);
+Q_CORE_EXPORT QTextStream &hex(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &showbase(QTextStream &s);
+Q_CORE_EXPORT QTextStream &forcesign(QTextStream &s);
+Q_CORE_EXPORT QTextStream &forcepoint(QTextStream &s);
+Q_CORE_EXPORT QTextStream &noshowbase(QTextStream &s);
+Q_CORE_EXPORT QTextStream &noforcesign(QTextStream &s);
+Q_CORE_EXPORT QTextStream &noforcepoint(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &uppercasebase(QTextStream &s);
+Q_CORE_EXPORT QTextStream &uppercasedigits(QTextStream &s);
+Q_CORE_EXPORT QTextStream &lowercasebase(QTextStream &s);
+Q_CORE_EXPORT QTextStream &lowercasedigits(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &fixed(QTextStream &s);
+Q_CORE_EXPORT QTextStream &scientific(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &left(QTextStream &s);
+Q_CORE_EXPORT QTextStream &right(QTextStream &s);
+Q_CORE_EXPORT QTextStream &center(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &endl(QTextStream &s);
+Q_CORE_EXPORT QTextStream &flush(QTextStream &s);
+Q_CORE_EXPORT QTextStream &reset(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &bom(QTextStream &s);
+
+Q_CORE_EXPORT QTextStream &ws(QTextStream &s);
+
+inline QTextStreamManipulator qSetFieldWidth(int width)
+{
+ QTSMFI func = &QTextStream::setFieldWidth;
+ return QTextStreamManipulator(func,width);
+}
+
+inline QTextStreamManipulator qSetPadChar(QChar ch)
+{
+ QTSMFC func = &QTextStream::setPadChar;
+ return QTextStreamManipulator(func, ch);
+}
+
+inline QTextStreamManipulator qSetRealNumberPrecision(int precision)
+{
+ QTSMFI func = &QTextStream::setRealNumberPrecision;
+ return QTextStreamManipulator(func, precision);
+}
+
+#ifdef QT3_SUPPORT
+typedef QTextStream QTS;
+
+class Q_CORE_EXPORT QTextIStream : public QTextStream
+{
+public:
+ inline explicit QTextIStream(const QString *s) : QTextStream(const_cast<QString *>(s), QIODevice::ReadOnly) {}
+ inline explicit QTextIStream(QByteArray *a) : QTextStream(a, QIODevice::ReadOnly) {}
+ inline QTextIStream(FILE *f) : QTextStream(f, QIODevice::ReadOnly) {}
+
+private:
+ Q_DISABLE_COPY(QTextIStream)
+};
+
+class Q_CORE_EXPORT QTextOStream : public QTextStream
+{
+public:
+ inline explicit QTextOStream(QString *s) : QTextStream(s, QIODevice::WriteOnly) {}
+ inline explicit QTextOStream(QByteArray *a) : QTextStream(a, QIODevice::WriteOnly) {}
+ inline QTextOStream(FILE *f) : QTextStream(f, QIODevice::WriteOnly) {}
+
+private:
+ Q_DISABLE_COPY(QTextOStream)
+};
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTEXTSTREAM_H
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
new file mode 100644
index 0000000..9ce9a2e
--- /dev/null
+++ b/src/corelib/io/qurl.cpp
@@ -0,0 +1,5999 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QUrl
+
+ \brief The QUrl class provides a convenient interface for working
+ with URLs.
+
+ \reentrant
+ \ingroup io
+ \ingroup misc
+ \ingroup shared
+ \mainclass
+
+ It can parse and construct URLs in both encoded and unencoded
+ form. QUrl also has support for internationalized domain names
+ (IDNs).
+
+ The most common way to use QUrl is to initialize it via the
+ constructor by passing a QString. Otherwise, setUrl() and
+ setEncodedUrl() can also be used.
+
+ URLs can be represented in two forms: encoded or unencoded. The
+ unencoded representation is suitable for showing to users, but
+ the encoded representation is typically what you would send to
+ a web server. For example, the unencoded URL
+ "http://b\uuml\c{}hler.example.com" would be sent to the server as
+ "http://xn--bhler-kva.example.com/List%20of%20applicants.xml".
+
+ A URL can also be constructed piece by piece by calling
+ setScheme(), setUserName(), setPassword(), setHost(), setPort(),
+ setPath(), setEncodedQuery() and setFragment(). Some convenience
+ functions are also available: setAuthority() sets the user name,
+ password, host and port. setUserInfo() sets the user name and
+ password at once.
+
+ Call isValid() to check if the URL is valid. This can be done at
+ any point during the constructing of a URL.
+
+ Constructing a query is particularly convenient through the use
+ of setQueryItems(), addQueryItem() and removeQueryItem(). Use
+ setQueryDelimiters() to customize the delimiters used for
+ generating the query string.
+
+ For the convenience of generating encoded URL strings or query
+ strings, there are two static functions called
+ fromPercentEncoding() and toPercentEncoding() which deal with
+ percent encoding and decoding of QStrings.
+
+ Calling isRelative() will tell whether or not the URL is
+ relative. A relative URL can be resolved by passing it as argument
+ to resolved(), which returns an absolute URL. isParentOf() is used
+ for determining whether one URL is a parent of another.
+
+ fromLocalFile() constructs a QUrl by parsing a local
+ file path. toLocalFile() converts a URL to a local file path.
+
+ The human readable representation of the URL is fetched with
+ toString(). This representation is appropriate for displaying a
+ URL to a user in unencoded form. The encoded form however, as
+ returned by toEncoded(), is for internal use, passing to web
+ servers, mail clients and so on.
+
+ QUrl conforms to the URI specification from
+ \l{RFC 3986} (Uniform Resource Identifier: Generic Syntax), and includes
+ scheme extensions from \l{RFC 1738} (Uniform Resource Locators). Case
+ folding rules in QUrl conform to \l{RFC 3491} (Nameprep: A Stringprep
+ Profile for Internationalized Domain Names (IDN)).
+
+ \sa QUrlInfo
+*/
+
+/*!
+ \enum QUrl::ParsingMode
+
+ The parsing mode controls the way QUrl parses strings.
+
+ \value TolerantMode QUrl will try to correct some common errors in URLs.
+ This mode is useful when processing URLs entered by
+ users.
+
+ \value StrictMode Only valid URLs are accepted. This mode is useful for
+ general URL validation.
+
+ In TolerantMode, the parser corrects the following invalid input:
+
+ \list
+
+ \o Spaces and "%20": If an encoded URL contains a space, this will be
+ replaced with "%20". If a decoded URL contains "%20", this will be
+ replaced with a single space before the URL is parsed.
+
+ \o Single "%" characters: Any occurrences of a percent character "%" not
+ followed by exactly two hexadecimal characters (e.g., "13% coverage.html")
+ will be replaced by "%25".
+
+ \o Reserved and unreserved characters: An encoded URL should only
+ contain a few characters as literals; all other characters should
+ be percent-encoded. In TolerantMode, these characters will be
+ automatically percent-encoded where they are not allowed:
+ space / double-quote / "<" / ">" / "[" / "\" /
+ "]" / "^" / "`" / "{" / "|" / "}"
+
+ \endlist
+*/
+
+/*!
+ \enum QUrl::FormattingOption
+
+ The formatting options define how the URL is formatted when written out
+ as text.
+
+ \value None The format of the URL is unchanged.
+ \value RemoveScheme The scheme is removed from the URL.
+ \value RemovePassword Any password in the URL is removed.
+ \value RemoveUserInfo Any user information in the URL is removed.
+ \value RemovePort Any specified port is removed from the URL.
+ \value RemoveAuthority
+ \value RemovePath The URL's path is removed, leaving only the scheme,
+ host address, and port (if present).
+ \value RemoveQuery The query part of the URL (following a '?' character)
+ is removed.
+ \value RemoveFragment
+ \value StripTrailingSlash The trailing slash is removed if one is present.
+
+ Note that the case folding rules in \l{RFC 3491}{Nameprep}, which QUrl
+ conforms to, require host names to always be converted to lower case,
+ regardless of the Qt::FormattingOptions used.
+*/
+
+#include "qplatformdefs.h"
+#include "qurl.h"
+#include "private/qunicodetables_p.h"
+#include "qatomic.h"
+#include "qbytearray.h"
+#include "qlist.h"
+#ifndef QT_NO_REGEXP
+#include "qregexp.h"
+#endif
+#include "qstring.h"
+#include "qstringlist.h"
+#include "qstack.h"
+#include "qvarlengtharray.h"
+#include "qdebug.h"
+#if defined QT3_SUPPORT
+#include "qfileinfo.h"
+#endif
+
+#if defined(Q_OS_WINCE_WM)
+#pragma optimize("g", off)
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern void q_normalizePercentEncoding(QByteArray *ba, const char *exclude);
+extern void q_toPercentEncoding(QByteArray *ba, const char *exclude, const char *include = 0);
+extern void q_fromPercentEncoding(QByteArray *ba);
+
+static QByteArray toPercentEncodingHelper(const QString &s, const char *exclude, const char *include = 0)
+{
+ if (s.isNull())
+ return QByteArray(); // null
+ QByteArray ba = s.toUtf8();
+ q_toPercentEncoding(&ba, exclude, include);
+ return ba;
+}
+
+static QString fromPercentEncodingHelper(const QByteArray &ba)
+{
+ if (ba.isNull())
+ return QString(); // null
+ QByteArray copy = ba;
+ q_fromPercentEncoding(&copy);
+ return QString::fromUtf8(copy.constData(), copy.length());
+}
+
+static QString fromPercentEncodingMutable(QByteArray *ba)
+{
+ if (ba->isNull())
+ return QString(); // null
+ q_fromPercentEncoding(ba);
+ return QString::fromUtf8(ba->constData(), ba->length());
+}
+
+// ### Qt 5: Consider accepting empty strings as valid. See task 144227.
+
+//#define QURL_DEBUG
+
+// implemented in qvsnprintf.cpp
+Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt, ...);
+
+// needed by the punycode encoder/decoder
+#define Q_MAXINT ((uint)((uint)(-1)>>1))
+static const uint base = 36;
+static const uint tmin = 1;
+static const uint tmax = 26;
+static const uint skew = 38;
+static const uint damp = 700;
+static const uint initial_bias = 72;
+static const uint initial_n = 128;
+
+#define QURL_SETFLAG(a, b) { (a) |= (b); }
+#define QURL_UNSETFLAG(a, b) { (a) &= ~(b); }
+#define QURL_HASFLAG(a, b) (((a) & (b)) == (b))
+
+struct QUrlErrorInfo {
+ inline QUrlErrorInfo() : _source(0), _message(0), _expected(0), _found(0)
+ { }
+
+ const char *_source;
+ const char *_message;
+ char _expected;
+ char _found;
+
+ inline void setParams(const char *source, const char *message, char expected, char found)
+ {
+ _source = source;
+ _message = message;
+ _expected = expected;
+ _found = found;
+ }
+};
+
+struct QUrlParseData
+{
+ const char *scheme;
+ int schemeLength;
+
+ const char *userInfo;
+ int userInfoDelimIndex;
+ int userInfoLength;
+
+ const char *host;
+ int hostLength;
+ int port;
+
+ const char *path;
+ int pathLength;
+ const char *query;
+ int queryLength;
+ const char *fragment;
+ int fragmentLength;
+};
+
+
+class QUrlPrivate
+{
+public:
+ QUrlPrivate();
+ QUrlPrivate(const QUrlPrivate &other);
+
+ bool setUrl(const QString &url);
+
+ QString canonicalHost() const;
+ void ensureEncodedParts() const;
+ QString authority(QUrl::FormattingOptions options = QUrl::None) const;
+ void setAuthority(const QString &auth);
+ void setUserInfo(const QString &userInfo);
+ QString userInfo(QUrl::FormattingOptions options = QUrl::None) const;
+ void setEncodedAuthority(const QByteArray &authority);
+ void setEncodedUserInfo(const QUrlParseData *parseData);
+
+ QByteArray mergePaths(const QByteArray &relativePath) const;
+
+ void queryItem(int pos, int *value, int *end);
+
+ enum ParseOptions {
+ ParseAndSet,
+ ParseOnly
+ };
+
+ void validate() const;
+ void parse(ParseOptions parseOptions = ParseAndSet) const;
+ void clear();
+
+ QByteArray toEncoded(QUrl::FormattingOptions options = QUrl::None) const;
+
+ QAtomicInt ref;
+
+ QString scheme;
+ QString userName;
+ QString password;
+ QString host;
+ QString path;
+ QByteArray query;
+ QString fragment;
+
+ QByteArray encodedOriginal;
+ QByteArray encodedUserName;
+ QByteArray encodedPassword;
+ QByteArray encodedPath;
+ QByteArray encodedFragment;
+
+ int port;
+ QUrl::ParsingMode parsingMode;
+
+ bool hasQuery;
+ bool hasFragment;
+ bool isValid;
+
+ char valueDelimiter;
+ char pairDelimiter;
+
+ enum State {
+ Parsed = 0x1,
+ Validated = 0x2,
+ Normalized = 0x4,
+ HostCanonicalized = 0x8
+ };
+ int stateFlags;
+
+ QByteArray encodedNormalized;
+ const QByteArray & normalized();
+
+ mutable QUrlErrorInfo errorInfo;
+ QString createErrorString();
+};
+
+
+static bool QT_FASTCALL _HEXDIG(const char **ptr)
+{
+ char ch = **ptr;
+ if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
+ ++(*ptr);
+ return true;
+ }
+
+ return false;
+}
+
+// pct-encoded = "%" HEXDIG HEXDIG
+static bool QT_FASTCALL _pctEncoded(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+
+ if (**ptr != '%')
+ return false;
+ ++(*ptr);
+
+ if (!_HEXDIG(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+ if (!_HEXDIG(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ return true;
+}
+
+#if 0
+// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+static bool QT_FASTCALL _genDelims(const char **ptr, char *c)
+{
+ char ch = **ptr;
+ switch (ch) {
+ case ':': case '/': case '?': case '#':
+ case '[': case ']': case '@':
+ *c = ch;
+ ++(*ptr);
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+// / "*" / "+" / "," / ";" / "="
+static bool QT_FASTCALL _subDelims(const char **ptr)
+{
+ char ch = **ptr;
+ switch (ch) {
+ case '!': case '$': case '&': case '\'':
+ case '(': case ')': case '*': case '+':
+ case ',': case ';': case '=':
+ ++(*ptr);
+ return true;
+ default:
+ return false;
+ }
+}
+
+// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+static bool QT_FASTCALL _unreserved(const char **ptr)
+{
+ char ch = **ptr;
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
+ || (ch >= '0' && ch <= '9')
+ || ch == '-' || ch == '.' || ch == '_' || ch == '~') {
+ ++(*ptr);
+ return true;
+ }
+ return false;
+}
+
+// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+static void QT_FASTCALL _scheme(const char **ptr, QUrlParseData *parseData)
+{
+ bool first = true;
+
+ parseData->scheme = *ptr;
+ for (;;) {
+ char ch = **ptr;
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+ ;
+ } else if (!first && ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')) {
+ ;
+ } else {
+ break;
+ }
+
+ ++(*ptr);
+ first = false;
+ }
+
+ if (**ptr != ':') {
+ *ptr = parseData->scheme;
+ } else {
+ parseData->schemeLength = *ptr - parseData->scheme;
+ ++(*ptr); // skip ':'
+ }
+}
+
+// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
+static bool QT_FASTCALL _IPvFuture(const char **ptr)
+{
+ if (**ptr != 'v')
+ return false;
+
+ const char *ptrBackup = *ptr;
+ ++(*ptr);
+
+ if (!_HEXDIG(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ while (_HEXDIG(ptr))
+ ;
+
+ if (**ptr != '.') {
+ *ptr = ptrBackup;
+ return false;
+ }
+ ++(*ptr);
+
+ if (!_unreserved(ptr) && !_subDelims(ptr) && *((*ptr)++) != ':') {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+
+ while (_unreserved(ptr) || _subDelims(ptr) || *((*ptr)++) == ':')
+ ;
+
+ return true;
+}
+
+// h16 = 1*4HEXDIG
+// ; 16 bits of address represented in hexadecimal
+static bool QT_FASTCALL _h16(const char **ptr)
+{
+ int i = 0;
+ for (; i < 4; ++i) {
+ if (!_HEXDIG(ptr))
+ break;
+ }
+ return (i != 0);
+}
+
+// dec-octet = DIGIT ; 0-9
+// / %x31-39 DIGIT ; 10-99
+// / "1" 2DIGIT ; 100-199
+// / "2" %x30-34 DIGIT ; 200-249
+// / "25" %x30-35 ; 250-255
+static bool QT_FASTCALL _decOctet(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+ char c1 = **ptr;
+
+ if (c1 < '0' || c1 > '9')
+ return false;
+
+ ++(*ptr);
+
+ if (c1 == '0')
+ return true;
+
+ char c2 = **ptr;
+
+ if (c2 < '0' || c2 > '9')
+ return true;
+
+ ++(*ptr);
+
+ char c3 = **ptr;
+ if (c3 < '0' || c3 > '9')
+ return true;
+
+ // If there is a three digit number larger than 255, reject the
+ // whole token.
+ if (c1 >= '2' && c2 >= '5' && c3 > '5') {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ ++(*ptr);
+
+ return true;
+}
+
+// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+static bool QT_FASTCALL _IPv4Address(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+
+ if (!_decOctet(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ char ch = *((*ptr)++);
+ if (ch != '.') {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ if (!_decOctet(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// ls32 = ( h16 ":" h16 ) / IPv4address
+// ; least-significant 32 bits of address
+static bool QT_FASTCALL _ls32(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+ if (_h16(ptr) && *((*ptr)++) == ':' && _h16(ptr))
+ return true;
+
+ *ptr = ptrBackup;
+ return _IPv4Address(ptr);
+}
+
+// IPv6address = 6( h16 ":" ) ls32 // case 1
+// / "::" 5( h16 ":" ) ls32 // case 2
+// / [ h16 ] "::" 4( h16 ":" ) ls32 // case 3
+// / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 // case 4
+// / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 // case 5
+// / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 // case 6
+// / [ *4( h16 ":" ) h16 ] "::" ls32 // case 7
+// / [ *5( h16 ":" ) h16 ] "::" h16 // case 8
+// / [ *6( h16 ":" ) h16 ] "::" // case 9
+static bool QT_FASTCALL _IPv6Address(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+
+ // count of (h16 ":") to the left of and including ::
+ int leftHexColons = 0;
+ // count of (h16 ":") to the right of ::
+ int rightHexColons = 0;
+
+ // first count the number of (h16 ":") on the left of ::
+ while (_h16(ptr)) {
+
+ // an h16 not followed by a colon is considered an
+ // error.
+ if (**ptr != ':') {
+ *ptr = ptrBackup;
+ return false;
+ }
+ ++(*ptr);
+ ++leftHexColons;
+
+ // check for case 1, the only time when there can be no ::
+ if (leftHexColons == 6 && _ls32(ptr)) {
+ return true;
+ }
+ }
+
+ // check for case 2 where the address starts with a :
+ if (leftHexColons == 0 && *((*ptr)++) != ':') {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ // check for the second colon in ::
+ if (*((*ptr)++) != ':') {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ int canBeCase = -1;
+ bool ls32WasRead = false;
+
+ const char *tmpBackup = *ptr;
+
+ // count the number of (h16 ":") on the right of ::
+ for (;;) {
+ tmpBackup = *ptr;
+ if (!_h16(ptr)) {
+ if (!_ls32(ptr)) {
+ if (rightHexColons != 0) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ // the address ended with :: (case 9)
+ // only valid if 1 <= leftHexColons <= 7
+ canBeCase = 9;
+ } else {
+ ls32WasRead = true;
+ }
+ break;
+ }
+ ++rightHexColons;
+ if (**ptr != ':') {
+ // no colon could mean that what was read as an h16
+ // was in fact the first part of an ls32. we backtrack
+ // and retry.
+ const char *pb = *ptr;
+ *ptr = tmpBackup;
+ if (_ls32(ptr)) {
+ ls32WasRead = true;
+ --rightHexColons;
+ } else {
+ *ptr = pb;
+ // address ends with only 1 h16 after :: (case 8)
+ if (rightHexColons == 1)
+ canBeCase = 8;
+ }
+ break;
+ }
+ ++(*ptr);
+ }
+
+ // determine which case it is based on the number of rightHexColons
+ if (canBeCase == -1) {
+
+ // check if a ls32 was read. If it wasn't and rightHexColons >= 2 then the
+ // last 2 HexColons are in fact a ls32
+ if (!ls32WasRead && rightHexColons >= 2)
+ rightHexColons -= 2;
+
+ canBeCase = 7 - rightHexColons;
+ }
+
+ // based on the case we need to check that the number of leftHexColons is valid
+ if (leftHexColons > (canBeCase - 2)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ return true;
+}
+
+// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
+static bool QT_FASTCALL _IPLiteral(const char **ptr)
+{
+ const char *ptrBackup = *ptr;
+ if (**ptr != '[')
+ return false;
+ ++(*ptr);
+
+ if (!_IPv6Address(ptr) && !_IPvFuture(ptr)) {
+ *ptr = ptrBackup;
+ return false;
+ }
+
+ if (**ptr != ']') {
+ *ptr = ptrBackup;
+ return false;
+ }
+ ++(*ptr);
+
+ return true;
+}
+
+// reg-name = *( unreserved / pct-encoded / sub-delims )
+static void QT_FASTCALL _regName(const char **ptr)
+{
+ for (;;) {
+ if (!_unreserved(ptr) && !_subDelims(ptr)) {
+ if (!_pctEncoded(ptr))
+ break;
+ }
+ }
+}
+
+// host = IP-literal / IPv4address / reg-name
+static void QT_FASTCALL _host(const char **ptr, QUrlParseData *parseData)
+{
+ parseData->host = *ptr;
+ if (!_IPLiteral(ptr)) {
+ if (_IPv4Address(ptr)) {
+ char ch = **ptr;
+ if (ch && ch != ':' && ch != '/') {
+ // reset
+ *ptr = parseData->host;
+ _regName(ptr);
+ }
+ } else {
+ _regName(ptr);
+ }
+ }
+ parseData->hostLength = *ptr - parseData->host;
+}
+
+// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+static void QT_FASTCALL _userInfo(const char **ptr, QUrlParseData *parseData)
+{
+ parseData->userInfo = *ptr;
+ for (;;) {
+ if (_unreserved(ptr) || _subDelims(ptr)) {
+ ;
+ } else {
+ if (_pctEncoded(ptr)) {
+ ;
+ } else if (**ptr == ':') {
+ parseData->userInfoDelimIndex = *ptr - parseData->userInfo;
+ ++(*ptr);
+ } else {
+ break;
+ }
+ }
+ }
+ if (**ptr != '@') {
+ *ptr = parseData->userInfo;
+ parseData->userInfoDelimIndex = -1;
+ return;
+ }
+ parseData->userInfoLength = *ptr - parseData->userInfo;
+ ++(*ptr);
+}
+
+// port = *DIGIT
+static void QT_FASTCALL _port(const char **ptr, int *port)
+{
+ bool first = true;
+
+ for (;;) {
+ const char *ptrBackup = *ptr;
+ char ch = *((*ptr)++);
+ if (ch < '0' || ch > '9') {
+ *ptr = ptrBackup;
+ break;
+ }
+
+ if (first) {
+ first = false;
+ *port = 0;
+ }
+
+ *port *= 10;
+ *port += ch - '0';
+ }
+}
+
+// authority = [ userinfo "@" ] host [ ":" port ]
+static void QT_FASTCALL _authority(const char **ptr, QUrlParseData *parseData)
+{
+ _userInfo(ptr, parseData);
+ _host(ptr, parseData);
+
+ if (**ptr != ':')
+ return;
+
+ ++(*ptr);
+ _port(ptr, &parseData->port);
+}
+
+// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+static bool QT_FASTCALL _pchar(const char **ptr)
+{
+ char c = *(*ptr);
+
+ switch (c) {
+ case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case ';': case '=': case ':': case '@':
+ case '-': case '.': case '_': case '~':
+ ++(*ptr);
+ return true;
+ default:
+ break;
+ };
+
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
+ ++(*ptr);
+ return true;
+ }
+
+ if (_pctEncoded(ptr))
+ return true;
+
+ return false;
+}
+
+// segment = *pchar
+static bool QT_FASTCALL _segmentNZ(const char **ptr)
+{
+ if (!_pchar(ptr))
+ return false;
+
+ while(_pchar(ptr))
+ ;
+
+ return true;
+}
+
+// path-abempty = *( "/" segment )
+static void QT_FASTCALL _pathAbEmpty(const char **ptr)
+{
+ for (;;) {
+ if (**ptr != '/')
+ break;
+ ++(*ptr);
+
+ while (_pchar(ptr))
+ ;
+ }
+}
+
+// path-abs = "/" [ segment-nz *( "/" segment ) ]
+static bool QT_FASTCALL _pathAbs(const char **ptr)
+{
+ // **ptr == '/' already checked in caller
+ ++(*ptr);
+
+ // we might be able to unnest this to gain some performance.
+ if (!_segmentNZ(ptr))
+ return true;
+
+ _pathAbEmpty(ptr);
+
+ return true;
+}
+
+// path-rootless = segment-nz *( "/" segment )
+static bool QT_FASTCALL _pathRootless(const char **ptr)
+{
+ // we might be able to unnest this to gain some performance.
+ if (!_segmentNZ(ptr))
+ return false;
+
+ _pathAbEmpty(ptr);
+
+ return true;
+}
+
+
+// hier-part = "//" authority path-abempty
+// / path-abs
+// / path-rootless
+// / path-empty
+static void QT_FASTCALL _hierPart(const char **ptr, QUrlParseData *parseData)
+{
+ const char *ptrBackup = *ptr;
+ const char *pathStart = 0;
+ if (*((*ptr)++) == '/' && *((*ptr)++) == '/') {
+ _authority(ptr, parseData);
+ pathStart = *ptr;
+ _pathAbEmpty(ptr);
+ } else {
+ *ptr = ptrBackup;
+ pathStart = *ptr;
+ if (**ptr == '/')
+ _pathAbs(ptr);
+ else
+ _pathRootless(ptr);
+ }
+ parseData->path = pathStart;
+ parseData->pathLength = *ptr - pathStart;
+}
+
+// query = *( pchar / "/" / "?" )
+static void QT_FASTCALL _query(const char **ptr, QUrlParseData *parseData)
+{
+ parseData->query = *ptr;
+ for (;;) {
+ if (_pchar(ptr)) {
+ ;
+ } else if (**ptr == '/' || **ptr == '?') {
+ ++(*ptr);
+ } else {
+ break;
+ }
+ }
+ parseData->queryLength = *ptr - parseData->query;
+}
+
+// fragment = *( pchar / "/" / "?" )
+static void QT_FASTCALL _fragment(const char **ptr, QUrlParseData *parseData)
+{
+ parseData->fragment = *ptr;
+ for (;;) {
+ if (_pchar(ptr)) {
+ ;
+ } else if (**ptr == '/' || **ptr == '?' || **ptr == '#') {
+ ++(*ptr);
+ } else {
+ break;
+ }
+ }
+ parseData->fragmentLength = *ptr - parseData->fragment;
+}
+
+struct NameprepCaseFoldingEntry {
+ int uc;
+ ushort mapping[4];
+};
+
+inline bool operator<(int one, const NameprepCaseFoldingEntry &other)
+{ return one < other.uc; }
+
+inline bool operator<(const NameprepCaseFoldingEntry &one, int other)
+{ return one.uc < other; }
+
+static const NameprepCaseFoldingEntry NameprepCaseFolding[] = {
+/* { 0x0041, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x0042, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x0043, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x0044, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x0045, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x0046, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x0047, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x0048, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x0049, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x004A, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x004B, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x004C, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x004D, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x004E, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x004F, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0050, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x0051, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x0052, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x0053, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x0054, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x0055, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x0056, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x0057, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x0058, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x0059, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x005A, { 0x007A, 0x0000, 0x0000, 0x0000 } },*/
+ { 0x00B5, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C0, { 0x00E0, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C1, { 0x00E1, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C2, { 0x00E2, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C3, { 0x00E3, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C4, { 0x00E4, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C5, { 0x00E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C6, { 0x00E6, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C7, { 0x00E7, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C8, { 0x00E8, 0x0000, 0x0000, 0x0000 } },
+ { 0x00C9, { 0x00E9, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CA, { 0x00EA, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CB, { 0x00EB, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CC, { 0x00EC, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CD, { 0x00ED, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CE, { 0x00EE, 0x0000, 0x0000, 0x0000 } },
+ { 0x00CF, { 0x00EF, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D0, { 0x00F0, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D1, { 0x00F1, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D2, { 0x00F2, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D3, { 0x00F3, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D4, { 0x00F4, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D5, { 0x00F5, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D6, { 0x00F6, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D8, { 0x00F8, 0x0000, 0x0000, 0x0000 } },
+ { 0x00D9, { 0x00F9, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DA, { 0x00FA, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DB, { 0x00FB, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DC, { 0x00FC, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DD, { 0x00FD, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DE, { 0x00FE, 0x0000, 0x0000, 0x0000 } },
+ { 0x00DF, { 0x0073, 0x0073, 0x0000, 0x0000 } },
+ { 0x0100, { 0x0101, 0x0000, 0x0000, 0x0000 } },
+ { 0x0102, { 0x0103, 0x0000, 0x0000, 0x0000 } },
+ { 0x0104, { 0x0105, 0x0000, 0x0000, 0x0000 } },
+ { 0x0106, { 0x0107, 0x0000, 0x0000, 0x0000 } },
+ { 0x0108, { 0x0109, 0x0000, 0x0000, 0x0000 } },
+ { 0x010A, { 0x010B, 0x0000, 0x0000, 0x0000 } },
+ { 0x010C, { 0x010D, 0x0000, 0x0000, 0x0000 } },
+ { 0x010E, { 0x010F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0110, { 0x0111, 0x0000, 0x0000, 0x0000 } },
+ { 0x0112, { 0x0113, 0x0000, 0x0000, 0x0000 } },
+ { 0x0114, { 0x0115, 0x0000, 0x0000, 0x0000 } },
+ { 0x0116, { 0x0117, 0x0000, 0x0000, 0x0000 } },
+ { 0x0118, { 0x0119, 0x0000, 0x0000, 0x0000 } },
+ { 0x011A, { 0x011B, 0x0000, 0x0000, 0x0000 } },
+ { 0x011C, { 0x011D, 0x0000, 0x0000, 0x0000 } },
+ { 0x011E, { 0x011F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0120, { 0x0121, 0x0000, 0x0000, 0x0000 } },
+ { 0x0122, { 0x0123, 0x0000, 0x0000, 0x0000 } },
+ { 0x0124, { 0x0125, 0x0000, 0x0000, 0x0000 } },
+ { 0x0126, { 0x0127, 0x0000, 0x0000, 0x0000 } },
+ { 0x0128, { 0x0129, 0x0000, 0x0000, 0x0000 } },
+ { 0x012A, { 0x012B, 0x0000, 0x0000, 0x0000 } },
+ { 0x012C, { 0x012D, 0x0000, 0x0000, 0x0000 } },
+ { 0x012E, { 0x012F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0130, { 0x0069, 0x0307, 0x0000, 0x0000 } },
+ { 0x0132, { 0x0133, 0x0000, 0x0000, 0x0000 } },
+ { 0x0134, { 0x0135, 0x0000, 0x0000, 0x0000 } },
+ { 0x0136, { 0x0137, 0x0000, 0x0000, 0x0000 } },
+ { 0x0139, { 0x013A, 0x0000, 0x0000, 0x0000 } },
+ { 0x013B, { 0x013C, 0x0000, 0x0000, 0x0000 } },
+ { 0x013D, { 0x013E, 0x0000, 0x0000, 0x0000 } },
+ { 0x013F, { 0x0140, 0x0000, 0x0000, 0x0000 } },
+ { 0x0141, { 0x0142, 0x0000, 0x0000, 0x0000 } },
+ { 0x0143, { 0x0144, 0x0000, 0x0000, 0x0000 } },
+ { 0x0145, { 0x0146, 0x0000, 0x0000, 0x0000 } },
+ { 0x0147, { 0x0148, 0x0000, 0x0000, 0x0000 } },
+ { 0x0149, { 0x02BC, 0x006E, 0x0000, 0x0000 } },
+ { 0x014A, { 0x014B, 0x0000, 0x0000, 0x0000 } },
+ { 0x014C, { 0x014D, 0x0000, 0x0000, 0x0000 } },
+ { 0x014E, { 0x014F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0150, { 0x0151, 0x0000, 0x0000, 0x0000 } },
+ { 0x0152, { 0x0153, 0x0000, 0x0000, 0x0000 } },
+ { 0x0154, { 0x0155, 0x0000, 0x0000, 0x0000 } },
+ { 0x0156, { 0x0157, 0x0000, 0x0000, 0x0000 } },
+ { 0x0158, { 0x0159, 0x0000, 0x0000, 0x0000 } },
+ { 0x015A, { 0x015B, 0x0000, 0x0000, 0x0000 } },
+ { 0x015C, { 0x015D, 0x0000, 0x0000, 0x0000 } },
+ { 0x015E, { 0x015F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0160, { 0x0161, 0x0000, 0x0000, 0x0000 } },
+ { 0x0162, { 0x0163, 0x0000, 0x0000, 0x0000 } },
+ { 0x0164, { 0x0165, 0x0000, 0x0000, 0x0000 } },
+ { 0x0166, { 0x0167, 0x0000, 0x0000, 0x0000 } },
+ { 0x0168, { 0x0169, 0x0000, 0x0000, 0x0000 } },
+ { 0x016A, { 0x016B, 0x0000, 0x0000, 0x0000 } },
+ { 0x016C, { 0x016D, 0x0000, 0x0000, 0x0000 } },
+ { 0x016E, { 0x016F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0170, { 0x0171, 0x0000, 0x0000, 0x0000 } },
+ { 0x0172, { 0x0173, 0x0000, 0x0000, 0x0000 } },
+ { 0x0174, { 0x0175, 0x0000, 0x0000, 0x0000 } },
+ { 0x0176, { 0x0177, 0x0000, 0x0000, 0x0000 } },
+ { 0x0178, { 0x00FF, 0x0000, 0x0000, 0x0000 } },
+ { 0x0179, { 0x017A, 0x0000, 0x0000, 0x0000 } },
+ { 0x017B, { 0x017C, 0x0000, 0x0000, 0x0000 } },
+ { 0x017D, { 0x017E, 0x0000, 0x0000, 0x0000 } },
+ { 0x017F, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x0181, { 0x0253, 0x0000, 0x0000, 0x0000 } },
+ { 0x0182, { 0x0183, 0x0000, 0x0000, 0x0000 } },
+ { 0x0184, { 0x0185, 0x0000, 0x0000, 0x0000 } },
+ { 0x0186, { 0x0254, 0x0000, 0x0000, 0x0000 } },
+ { 0x0187, { 0x0188, 0x0000, 0x0000, 0x0000 } },
+ { 0x0189, { 0x0256, 0x0000, 0x0000, 0x0000 } },
+ { 0x018A, { 0x0257, 0x0000, 0x0000, 0x0000 } },
+ { 0x018B, { 0x018C, 0x0000, 0x0000, 0x0000 } },
+ { 0x018E, { 0x01DD, 0x0000, 0x0000, 0x0000 } },
+ { 0x018F, { 0x0259, 0x0000, 0x0000, 0x0000 } },
+ { 0x0190, { 0x025B, 0x0000, 0x0000, 0x0000 } },
+ { 0x0191, { 0x0192, 0x0000, 0x0000, 0x0000 } },
+ { 0x0193, { 0x0260, 0x0000, 0x0000, 0x0000 } },
+ { 0x0194, { 0x0263, 0x0000, 0x0000, 0x0000 } },
+ { 0x0196, { 0x0269, 0x0000, 0x0000, 0x0000 } },
+ { 0x0197, { 0x0268, 0x0000, 0x0000, 0x0000 } },
+ { 0x0198, { 0x0199, 0x0000, 0x0000, 0x0000 } },
+ { 0x019C, { 0x026F, 0x0000, 0x0000, 0x0000 } },
+ { 0x019D, { 0x0272, 0x0000, 0x0000, 0x0000 } },
+ { 0x019F, { 0x0275, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A0, { 0x01A1, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A2, { 0x01A3, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A4, { 0x01A5, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A6, { 0x0280, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A7, { 0x01A8, 0x0000, 0x0000, 0x0000 } },
+ { 0x01A9, { 0x0283, 0x0000, 0x0000, 0x0000 } },
+ { 0x01AC, { 0x01AD, 0x0000, 0x0000, 0x0000 } },
+ { 0x01AE, { 0x0288, 0x0000, 0x0000, 0x0000 } },
+ { 0x01AF, { 0x01B0, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B1, { 0x028A, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B2, { 0x028B, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B3, { 0x01B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B5, { 0x01B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B7, { 0x0292, 0x0000, 0x0000, 0x0000 } },
+ { 0x01B8, { 0x01B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x01BC, { 0x01BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x01C4, { 0x01C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x01C5, { 0x01C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x01C7, { 0x01C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x01C8, { 0x01C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x01CA, { 0x01CC, 0x0000, 0x0000, 0x0000 } },
+ { 0x01CB, { 0x01CC, 0x0000, 0x0000, 0x0000 } },
+ { 0x01CD, { 0x01CE, 0x0000, 0x0000, 0x0000 } },
+ { 0x01CF, { 0x01D0, 0x0000, 0x0000, 0x0000 } },
+ { 0x01D1, { 0x01D2, 0x0000, 0x0000, 0x0000 } },
+ { 0x01D3, { 0x01D4, 0x0000, 0x0000, 0x0000 } },
+ { 0x01D5, { 0x01D6, 0x0000, 0x0000, 0x0000 } },
+ { 0x01D7, { 0x01D8, 0x0000, 0x0000, 0x0000 } },
+ { 0x01D9, { 0x01DA, 0x0000, 0x0000, 0x0000 } },
+ { 0x01DB, { 0x01DC, 0x0000, 0x0000, 0x0000 } },
+ { 0x01DE, { 0x01DF, 0x0000, 0x0000, 0x0000 } },
+ { 0x01E0, { 0x01E1, 0x0000, 0x0000, 0x0000 } },
+ { 0x01E2, { 0x01E3, 0x0000, 0x0000, 0x0000 } },
+ { 0x01E4, { 0x01E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x01E6, { 0x01E7, 0x0000, 0x0000, 0x0000 } },
+ { 0x01E8, { 0x01E9, 0x0000, 0x0000, 0x0000 } },
+ { 0x01EA, { 0x01EB, 0x0000, 0x0000, 0x0000 } },
+ { 0x01EC, { 0x01ED, 0x0000, 0x0000, 0x0000 } },
+ { 0x01EE, { 0x01EF, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F0, { 0x006A, 0x030C, 0x0000, 0x0000 } },
+ { 0x01F1, { 0x01F3, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F2, { 0x01F3, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F4, { 0x01F5, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F6, { 0x0195, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F7, { 0x01BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x01F8, { 0x01F9, 0x0000, 0x0000, 0x0000 } },
+ { 0x01FA, { 0x01FB, 0x0000, 0x0000, 0x0000 } },
+ { 0x01FC, { 0x01FD, 0x0000, 0x0000, 0x0000 } },
+ { 0x01FE, { 0x01FF, 0x0000, 0x0000, 0x0000 } },
+ { 0x0200, { 0x0201, 0x0000, 0x0000, 0x0000 } },
+ { 0x0202, { 0x0203, 0x0000, 0x0000, 0x0000 } },
+ { 0x0204, { 0x0205, 0x0000, 0x0000, 0x0000 } },
+ { 0x0206, { 0x0207, 0x0000, 0x0000, 0x0000 } },
+ { 0x0208, { 0x0209, 0x0000, 0x0000, 0x0000 } },
+ { 0x020A, { 0x020B, 0x0000, 0x0000, 0x0000 } },
+ { 0x020C, { 0x020D, 0x0000, 0x0000, 0x0000 } },
+ { 0x020E, { 0x020F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0210, { 0x0211, 0x0000, 0x0000, 0x0000 } },
+ { 0x0212, { 0x0213, 0x0000, 0x0000, 0x0000 } },
+ { 0x0214, { 0x0215, 0x0000, 0x0000, 0x0000 } },
+ { 0x0216, { 0x0217, 0x0000, 0x0000, 0x0000 } },
+ { 0x0218, { 0x0219, 0x0000, 0x0000, 0x0000 } },
+ { 0x021A, { 0x021B, 0x0000, 0x0000, 0x0000 } },
+ { 0x021C, { 0x021D, 0x0000, 0x0000, 0x0000 } },
+ { 0x021E, { 0x021F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0220, { 0x019E, 0x0000, 0x0000, 0x0000 } },
+ { 0x0222, { 0x0223, 0x0000, 0x0000, 0x0000 } },
+ { 0x0224, { 0x0225, 0x0000, 0x0000, 0x0000 } },
+ { 0x0226, { 0x0227, 0x0000, 0x0000, 0x0000 } },
+ { 0x0228, { 0x0229, 0x0000, 0x0000, 0x0000 } },
+ { 0x022A, { 0x022B, 0x0000, 0x0000, 0x0000 } },
+ { 0x022C, { 0x022D, 0x0000, 0x0000, 0x0000 } },
+ { 0x022E, { 0x022F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0230, { 0x0231, 0x0000, 0x0000, 0x0000 } },
+ { 0x0232, { 0x0233, 0x0000, 0x0000, 0x0000 } },
+ { 0x0345, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x037A, { 0x0020, 0x03B9, 0x0000, 0x0000 } },
+ { 0x0386, { 0x03AC, 0x0000, 0x0000, 0x0000 } },
+ { 0x0388, { 0x03AD, 0x0000, 0x0000, 0x0000 } },
+ { 0x0389, { 0x03AE, 0x0000, 0x0000, 0x0000 } },
+ { 0x038A, { 0x03AF, 0x0000, 0x0000, 0x0000 } },
+ { 0x038C, { 0x03CC, 0x0000, 0x0000, 0x0000 } },
+ { 0x038E, { 0x03CD, 0x0000, 0x0000, 0x0000 } },
+ { 0x038F, { 0x03CE, 0x0000, 0x0000, 0x0000 } },
+ { 0x0390, { 0x03B9, 0x0308, 0x0301, 0x0000 } },
+ { 0x0391, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x0392, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x0393, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x0394, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x0395, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x0396, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x0397, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x0398, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x0399, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x039A, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x039B, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x039C, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x039D, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x039E, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x039F, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A0, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A1, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A3, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A4, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A5, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A6, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A7, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A8, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x03A9, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x03AA, { 0x03CA, 0x0000, 0x0000, 0x0000 } },
+ { 0x03AB, { 0x03CB, 0x0000, 0x0000, 0x0000 } },
+ { 0x03B0, { 0x03C5, 0x0308, 0x0301, 0x0000 } },
+ { 0x03C2, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D0, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D1, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D2, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D3, { 0x03CD, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D4, { 0x03CB, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D5, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D6, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x03D8, { 0x03D9, 0x0000, 0x0000, 0x0000 } },
+ { 0x03DA, { 0x03DB, 0x0000, 0x0000, 0x0000 } },
+ { 0x03DC, { 0x03DD, 0x0000, 0x0000, 0x0000 } },
+ { 0x03DE, { 0x03DF, 0x0000, 0x0000, 0x0000 } },
+ { 0x03E0, { 0x03E1, 0x0000, 0x0000, 0x0000 } },
+ { 0x03E2, { 0x03E3, 0x0000, 0x0000, 0x0000 } },
+ { 0x03E4, { 0x03E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x03E6, { 0x03E7, 0x0000, 0x0000, 0x0000 } },
+ { 0x03E8, { 0x03E9, 0x0000, 0x0000, 0x0000 } },
+ { 0x03EA, { 0x03EB, 0x0000, 0x0000, 0x0000 } },
+ { 0x03EC, { 0x03ED, 0x0000, 0x0000, 0x0000 } },
+ { 0x03EE, { 0x03EF, 0x0000, 0x0000, 0x0000 } },
+ { 0x03F0, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x03F1, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x03F2, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x03F4, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x03F5, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x0400, { 0x0450, 0x0000, 0x0000, 0x0000 } },
+ { 0x0401, { 0x0451, 0x0000, 0x0000, 0x0000 } },
+ { 0x0402, { 0x0452, 0x0000, 0x0000, 0x0000 } },
+ { 0x0403, { 0x0453, 0x0000, 0x0000, 0x0000 } },
+ { 0x0404, { 0x0454, 0x0000, 0x0000, 0x0000 } },
+ { 0x0405, { 0x0455, 0x0000, 0x0000, 0x0000 } },
+ { 0x0406, { 0x0456, 0x0000, 0x0000, 0x0000 } },
+ { 0x0407, { 0x0457, 0x0000, 0x0000, 0x0000 } },
+ { 0x0408, { 0x0458, 0x0000, 0x0000, 0x0000 } },
+ { 0x0409, { 0x0459, 0x0000, 0x0000, 0x0000 } },
+ { 0x040A, { 0x045A, 0x0000, 0x0000, 0x0000 } },
+ { 0x040B, { 0x045B, 0x0000, 0x0000, 0x0000 } },
+ { 0x040C, { 0x045C, 0x0000, 0x0000, 0x0000 } },
+ { 0x040D, { 0x045D, 0x0000, 0x0000, 0x0000 } },
+ { 0x040E, { 0x045E, 0x0000, 0x0000, 0x0000 } },
+ { 0x040F, { 0x045F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0410, { 0x0430, 0x0000, 0x0000, 0x0000 } },
+ { 0x0411, { 0x0431, 0x0000, 0x0000, 0x0000 } },
+ { 0x0412, { 0x0432, 0x0000, 0x0000, 0x0000 } },
+ { 0x0413, { 0x0433, 0x0000, 0x0000, 0x0000 } },
+ { 0x0414, { 0x0434, 0x0000, 0x0000, 0x0000 } },
+ { 0x0415, { 0x0435, 0x0000, 0x0000, 0x0000 } },
+ { 0x0416, { 0x0436, 0x0000, 0x0000, 0x0000 } },
+ { 0x0417, { 0x0437, 0x0000, 0x0000, 0x0000 } },
+ { 0x0418, { 0x0438, 0x0000, 0x0000, 0x0000 } },
+ { 0x0419, { 0x0439, 0x0000, 0x0000, 0x0000 } },
+ { 0x041A, { 0x043A, 0x0000, 0x0000, 0x0000 } },
+ { 0x041B, { 0x043B, 0x0000, 0x0000, 0x0000 } },
+ { 0x041C, { 0x043C, 0x0000, 0x0000, 0x0000 } },
+ { 0x041D, { 0x043D, 0x0000, 0x0000, 0x0000 } },
+ { 0x041E, { 0x043E, 0x0000, 0x0000, 0x0000 } },
+ { 0x041F, { 0x043F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0420, { 0x0440, 0x0000, 0x0000, 0x0000 } },
+ { 0x0421, { 0x0441, 0x0000, 0x0000, 0x0000 } },
+ { 0x0422, { 0x0442, 0x0000, 0x0000, 0x0000 } },
+ { 0x0423, { 0x0443, 0x0000, 0x0000, 0x0000 } },
+ { 0x0424, { 0x0444, 0x0000, 0x0000, 0x0000 } },
+ { 0x0425, { 0x0445, 0x0000, 0x0000, 0x0000 } },
+ { 0x0426, { 0x0446, 0x0000, 0x0000, 0x0000 } },
+ { 0x0427, { 0x0447, 0x0000, 0x0000, 0x0000 } },
+ { 0x0428, { 0x0448, 0x0000, 0x0000, 0x0000 } },
+ { 0x0429, { 0x0449, 0x0000, 0x0000, 0x0000 } },
+ { 0x042A, { 0x044A, 0x0000, 0x0000, 0x0000 } },
+ { 0x042B, { 0x044B, 0x0000, 0x0000, 0x0000 } },
+ { 0x042C, { 0x044C, 0x0000, 0x0000, 0x0000 } },
+ { 0x042D, { 0x044D, 0x0000, 0x0000, 0x0000 } },
+ { 0x042E, { 0x044E, 0x0000, 0x0000, 0x0000 } },
+ { 0x042F, { 0x044F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0460, { 0x0461, 0x0000, 0x0000, 0x0000 } },
+ { 0x0462, { 0x0463, 0x0000, 0x0000, 0x0000 } },
+ { 0x0464, { 0x0465, 0x0000, 0x0000, 0x0000 } },
+ { 0x0466, { 0x0467, 0x0000, 0x0000, 0x0000 } },
+ { 0x0468, { 0x0469, 0x0000, 0x0000, 0x0000 } },
+ { 0x046A, { 0x046B, 0x0000, 0x0000, 0x0000 } },
+ { 0x046C, { 0x046D, 0x0000, 0x0000, 0x0000 } },
+ { 0x046E, { 0x046F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0470, { 0x0471, 0x0000, 0x0000, 0x0000 } },
+ { 0x0472, { 0x0473, 0x0000, 0x0000, 0x0000 } },
+ { 0x0474, { 0x0475, 0x0000, 0x0000, 0x0000 } },
+ { 0x0476, { 0x0477, 0x0000, 0x0000, 0x0000 } },
+ { 0x0478, { 0x0479, 0x0000, 0x0000, 0x0000 } },
+ { 0x047A, { 0x047B, 0x0000, 0x0000, 0x0000 } },
+ { 0x047C, { 0x047D, 0x0000, 0x0000, 0x0000 } },
+ { 0x047E, { 0x047F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0480, { 0x0481, 0x0000, 0x0000, 0x0000 } },
+ { 0x048A, { 0x048B, 0x0000, 0x0000, 0x0000 } },
+ { 0x048C, { 0x048D, 0x0000, 0x0000, 0x0000 } },
+ { 0x048E, { 0x048F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0490, { 0x0491, 0x0000, 0x0000, 0x0000 } },
+ { 0x0492, { 0x0493, 0x0000, 0x0000, 0x0000 } },
+ { 0x0494, { 0x0495, 0x0000, 0x0000, 0x0000 } },
+ { 0x0496, { 0x0497, 0x0000, 0x0000, 0x0000 } },
+ { 0x0498, { 0x0499, 0x0000, 0x0000, 0x0000 } },
+ { 0x049A, { 0x049B, 0x0000, 0x0000, 0x0000 } },
+ { 0x049C, { 0x049D, 0x0000, 0x0000, 0x0000 } },
+ { 0x049E, { 0x049F, 0x0000, 0x0000, 0x0000 } },
+ { 0x04A0, { 0x04A1, 0x0000, 0x0000, 0x0000 } },
+ { 0x04A2, { 0x04A3, 0x0000, 0x0000, 0x0000 } },
+ { 0x04A4, { 0x04A5, 0x0000, 0x0000, 0x0000 } },
+ { 0x04A6, { 0x04A7, 0x0000, 0x0000, 0x0000 } },
+ { 0x04A8, { 0x04A9, 0x0000, 0x0000, 0x0000 } },
+ { 0x04AA, { 0x04AB, 0x0000, 0x0000, 0x0000 } },
+ { 0x04AC, { 0x04AD, 0x0000, 0x0000, 0x0000 } },
+ { 0x04AE, { 0x04AF, 0x0000, 0x0000, 0x0000 } },
+ { 0x04B0, { 0x04B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x04B2, { 0x04B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x04B4, { 0x04B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x04B6, { 0x04B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x04B8, { 0x04B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x04BA, { 0x04BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x04BC, { 0x04BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x04BE, { 0x04BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x04C1, { 0x04C2, 0x0000, 0x0000, 0x0000 } },
+ { 0x04C3, { 0x04C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x04C5, { 0x04C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x04C7, { 0x04C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x04C9, { 0x04CA, 0x0000, 0x0000, 0x0000 } },
+ { 0x04CB, { 0x04CC, 0x0000, 0x0000, 0x0000 } },
+ { 0x04CD, { 0x04CE, 0x0000, 0x0000, 0x0000 } },
+ { 0x04D0, { 0x04D1, 0x0000, 0x0000, 0x0000 } },
+ { 0x04D2, { 0x04D3, 0x0000, 0x0000, 0x0000 } },
+ { 0x04D4, { 0x04D5, 0x0000, 0x0000, 0x0000 } },
+ { 0x04D6, { 0x04D7, 0x0000, 0x0000, 0x0000 } },
+ { 0x04D8, { 0x04D9, 0x0000, 0x0000, 0x0000 } },
+ { 0x04DA, { 0x04DB, 0x0000, 0x0000, 0x0000 } },
+ { 0x04DC, { 0x04DD, 0x0000, 0x0000, 0x0000 } },
+ { 0x04DE, { 0x04DF, 0x0000, 0x0000, 0x0000 } },
+ { 0x04E0, { 0x04E1, 0x0000, 0x0000, 0x0000 } },
+ { 0x04E2, { 0x04E3, 0x0000, 0x0000, 0x0000 } },
+ { 0x04E4, { 0x04E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x04E6, { 0x04E7, 0x0000, 0x0000, 0x0000 } },
+ { 0x04E8, { 0x04E9, 0x0000, 0x0000, 0x0000 } },
+ { 0x04EA, { 0x04EB, 0x0000, 0x0000, 0x0000 } },
+ { 0x04EC, { 0x04ED, 0x0000, 0x0000, 0x0000 } },
+ { 0x04EE, { 0x04EF, 0x0000, 0x0000, 0x0000 } },
+ { 0x04F0, { 0x04F1, 0x0000, 0x0000, 0x0000 } },
+ { 0x04F2, { 0x04F3, 0x0000, 0x0000, 0x0000 } },
+ { 0x04F4, { 0x04F5, 0x0000, 0x0000, 0x0000 } },
+ { 0x04F8, { 0x04F9, 0x0000, 0x0000, 0x0000 } },
+ { 0x0500, { 0x0501, 0x0000, 0x0000, 0x0000 } },
+ { 0x0502, { 0x0503, 0x0000, 0x0000, 0x0000 } },
+ { 0x0504, { 0x0505, 0x0000, 0x0000, 0x0000 } },
+ { 0x0506, { 0x0507, 0x0000, 0x0000, 0x0000 } },
+ { 0x0508, { 0x0509, 0x0000, 0x0000, 0x0000 } },
+ { 0x050A, { 0x050B, 0x0000, 0x0000, 0x0000 } },
+ { 0x050C, { 0x050D, 0x0000, 0x0000, 0x0000 } },
+ { 0x050E, { 0x050F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0531, { 0x0561, 0x0000, 0x0000, 0x0000 } },
+ { 0x0532, { 0x0562, 0x0000, 0x0000, 0x0000 } },
+ { 0x0533, { 0x0563, 0x0000, 0x0000, 0x0000 } },
+ { 0x0534, { 0x0564, 0x0000, 0x0000, 0x0000 } },
+ { 0x0535, { 0x0565, 0x0000, 0x0000, 0x0000 } },
+ { 0x0536, { 0x0566, 0x0000, 0x0000, 0x0000 } },
+ { 0x0537, { 0x0567, 0x0000, 0x0000, 0x0000 } },
+ { 0x0538, { 0x0568, 0x0000, 0x0000, 0x0000 } },
+ { 0x0539, { 0x0569, 0x0000, 0x0000, 0x0000 } },
+ { 0x053A, { 0x056A, 0x0000, 0x0000, 0x0000 } },
+ { 0x053B, { 0x056B, 0x0000, 0x0000, 0x0000 } },
+ { 0x053C, { 0x056C, 0x0000, 0x0000, 0x0000 } },
+ { 0x053D, { 0x056D, 0x0000, 0x0000, 0x0000 } },
+ { 0x053E, { 0x056E, 0x0000, 0x0000, 0x0000 } },
+ { 0x053F, { 0x056F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0540, { 0x0570, 0x0000, 0x0000, 0x0000 } },
+ { 0x0541, { 0x0571, 0x0000, 0x0000, 0x0000 } },
+ { 0x0542, { 0x0572, 0x0000, 0x0000, 0x0000 } },
+ { 0x0543, { 0x0573, 0x0000, 0x0000, 0x0000 } },
+ { 0x0544, { 0x0574, 0x0000, 0x0000, 0x0000 } },
+ { 0x0545, { 0x0575, 0x0000, 0x0000, 0x0000 } },
+ { 0x0546, { 0x0576, 0x0000, 0x0000, 0x0000 } },
+ { 0x0547, { 0x0577, 0x0000, 0x0000, 0x0000 } },
+ { 0x0548, { 0x0578, 0x0000, 0x0000, 0x0000 } },
+ { 0x0549, { 0x0579, 0x0000, 0x0000, 0x0000 } },
+ { 0x054A, { 0x057A, 0x0000, 0x0000, 0x0000 } },
+ { 0x054B, { 0x057B, 0x0000, 0x0000, 0x0000 } },
+ { 0x054C, { 0x057C, 0x0000, 0x0000, 0x0000 } },
+ { 0x054D, { 0x057D, 0x0000, 0x0000, 0x0000 } },
+ { 0x054E, { 0x057E, 0x0000, 0x0000, 0x0000 } },
+ { 0x054F, { 0x057F, 0x0000, 0x0000, 0x0000 } },
+ { 0x0550, { 0x0580, 0x0000, 0x0000, 0x0000 } },
+ { 0x0551, { 0x0581, 0x0000, 0x0000, 0x0000 } },
+ { 0x0552, { 0x0582, 0x0000, 0x0000, 0x0000 } },
+ { 0x0553, { 0x0583, 0x0000, 0x0000, 0x0000 } },
+ { 0x0554, { 0x0584, 0x0000, 0x0000, 0x0000 } },
+ { 0x0555, { 0x0585, 0x0000, 0x0000, 0x0000 } },
+ { 0x0556, { 0x0586, 0x0000, 0x0000, 0x0000 } },
+ { 0x0587, { 0x0565, 0x0582, 0x0000, 0x0000 } },
+ { 0x1E00, { 0x1E01, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E02, { 0x1E03, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E04, { 0x1E05, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E06, { 0x1E07, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E08, { 0x1E09, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E0A, { 0x1E0B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E0C, { 0x1E0D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E0E, { 0x1E0F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E10, { 0x1E11, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E12, { 0x1E13, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E14, { 0x1E15, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E16, { 0x1E17, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E18, { 0x1E19, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E1A, { 0x1E1B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E1C, { 0x1E1D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E1E, { 0x1E1F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E20, { 0x1E21, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E22, { 0x1E23, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E24, { 0x1E25, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E26, { 0x1E27, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E28, { 0x1E29, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E2A, { 0x1E2B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E2C, { 0x1E2D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E2E, { 0x1E2F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E30, { 0x1E31, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E32, { 0x1E33, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E34, { 0x1E35, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E36, { 0x1E37, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E38, { 0x1E39, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E3A, { 0x1E3B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E3C, { 0x1E3D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E3E, { 0x1E3F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E40, { 0x1E41, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E42, { 0x1E43, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E44, { 0x1E45, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E46, { 0x1E47, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E48, { 0x1E49, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E4A, { 0x1E4B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E4C, { 0x1E4D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E4E, { 0x1E4F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E50, { 0x1E51, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E52, { 0x1E53, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E54, { 0x1E55, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E56, { 0x1E57, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E58, { 0x1E59, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E5A, { 0x1E5B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E5C, { 0x1E5D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E5E, { 0x1E5F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E60, { 0x1E61, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E62, { 0x1E63, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E64, { 0x1E65, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E66, { 0x1E67, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E68, { 0x1E69, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E6A, { 0x1E6B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E6C, { 0x1E6D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E6E, { 0x1E6F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E70, { 0x1E71, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E72, { 0x1E73, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E74, { 0x1E75, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E76, { 0x1E77, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E78, { 0x1E79, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E7A, { 0x1E7B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E7C, { 0x1E7D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E7E, { 0x1E7F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E80, { 0x1E81, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E82, { 0x1E83, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E84, { 0x1E85, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E86, { 0x1E87, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E88, { 0x1E89, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E8A, { 0x1E8B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E8C, { 0x1E8D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E8E, { 0x1E8F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E90, { 0x1E91, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E92, { 0x1E93, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E94, { 0x1E95, 0x0000, 0x0000, 0x0000 } },
+ { 0x1E96, { 0x0068, 0x0331, 0x0000, 0x0000 } },
+ { 0x1E97, { 0x0074, 0x0308, 0x0000, 0x0000 } },
+ { 0x1E98, { 0x0077, 0x030A, 0x0000, 0x0000 } },
+ { 0x1E99, { 0x0079, 0x030A, 0x0000, 0x0000 } },
+ { 0x1E9A, { 0x0061, 0x02BE, 0x0000, 0x0000 } },
+ { 0x1E9B, { 0x1E61, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EA0, { 0x1EA1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EA2, { 0x1EA3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EA4, { 0x1EA5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EA6, { 0x1EA7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EA8, { 0x1EA9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EAA, { 0x1EAB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EAC, { 0x1EAD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EAE, { 0x1EAF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EB0, { 0x1EB1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EB2, { 0x1EB3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EB4, { 0x1EB5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EB6, { 0x1EB7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EB8, { 0x1EB9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EBA, { 0x1EBB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EBC, { 0x1EBD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EBE, { 0x1EBF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EC0, { 0x1EC1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EC2, { 0x1EC3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EC4, { 0x1EC5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EC6, { 0x1EC7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EC8, { 0x1EC9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ECA, { 0x1ECB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ECC, { 0x1ECD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ECE, { 0x1ECF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ED0, { 0x1ED1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ED2, { 0x1ED3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ED4, { 0x1ED5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ED6, { 0x1ED7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1ED8, { 0x1ED9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EDA, { 0x1EDB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EDC, { 0x1EDD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EDE, { 0x1EDF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EE0, { 0x1EE1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EE2, { 0x1EE3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EE4, { 0x1EE5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EE6, { 0x1EE7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EE8, { 0x1EE9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EEA, { 0x1EEB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EEC, { 0x1EED, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EEE, { 0x1EEF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EF0, { 0x1EF1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EF2, { 0x1EF3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EF4, { 0x1EF5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EF6, { 0x1EF7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1EF8, { 0x1EF9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F08, { 0x1F00, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F09, { 0x1F01, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0A, { 0x1F02, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0B, { 0x1F03, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0C, { 0x1F04, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0D, { 0x1F05, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0E, { 0x1F06, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F0F, { 0x1F07, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F18, { 0x1F10, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F19, { 0x1F11, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F1A, { 0x1F12, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F1B, { 0x1F13, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F1C, { 0x1F14, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F1D, { 0x1F15, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F28, { 0x1F20, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F29, { 0x1F21, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2A, { 0x1F22, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2B, { 0x1F23, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2C, { 0x1F24, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2D, { 0x1F25, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2E, { 0x1F26, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F2F, { 0x1F27, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F38, { 0x1F30, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F39, { 0x1F31, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3A, { 0x1F32, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3B, { 0x1F33, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3C, { 0x1F34, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3D, { 0x1F35, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3E, { 0x1F36, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F3F, { 0x1F37, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F48, { 0x1F40, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F49, { 0x1F41, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F4A, { 0x1F42, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F4B, { 0x1F43, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F4C, { 0x1F44, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F4D, { 0x1F45, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F50, { 0x03C5, 0x0313, 0x0000, 0x0000 } },
+ { 0x1F52, { 0x03C5, 0x0313, 0x0300, 0x0000 } },
+ { 0x1F54, { 0x03C5, 0x0313, 0x0301, 0x0000 } },
+ { 0x1F56, { 0x03C5, 0x0313, 0x0342, 0x0000 } },
+ { 0x1F59, { 0x1F51, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F5B, { 0x1F53, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F5D, { 0x1F55, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F5F, { 0x1F57, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F68, { 0x1F60, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F69, { 0x1F61, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6A, { 0x1F62, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6B, { 0x1F63, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6C, { 0x1F64, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6D, { 0x1F65, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6E, { 0x1F66, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F6F, { 0x1F67, 0x0000, 0x0000, 0x0000 } },
+ { 0x1F80, { 0x1F00, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F81, { 0x1F01, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F82, { 0x1F02, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F83, { 0x1F03, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F84, { 0x1F04, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F85, { 0x1F05, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F86, { 0x1F06, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F87, { 0x1F07, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F88, { 0x1F00, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F89, { 0x1F01, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8A, { 0x1F02, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8B, { 0x1F03, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8C, { 0x1F04, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8D, { 0x1F05, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8E, { 0x1F06, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F8F, { 0x1F07, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F90, { 0x1F20, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F91, { 0x1F21, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F92, { 0x1F22, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F93, { 0x1F23, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F94, { 0x1F24, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F95, { 0x1F25, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F96, { 0x1F26, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F97, { 0x1F27, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F98, { 0x1F20, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F99, { 0x1F21, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9A, { 0x1F22, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9B, { 0x1F23, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9C, { 0x1F24, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9D, { 0x1F25, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9E, { 0x1F26, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1F9F, { 0x1F27, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA0, { 0x1F60, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA1, { 0x1F61, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA2, { 0x1F62, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA3, { 0x1F63, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA4, { 0x1F64, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA5, { 0x1F65, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA6, { 0x1F66, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA7, { 0x1F67, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA8, { 0x1F60, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FA9, { 0x1F61, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAA, { 0x1F62, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAB, { 0x1F63, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAC, { 0x1F64, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAD, { 0x1F65, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAE, { 0x1F66, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FAF, { 0x1F67, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FB2, { 0x1F70, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FB3, { 0x03B1, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FB4, { 0x03AC, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FB6, { 0x03B1, 0x0342, 0x0000, 0x0000 } },
+ { 0x1FB7, { 0x03B1, 0x0342, 0x03B9, 0x0000 } },
+ { 0x1FB8, { 0x1FB0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FB9, { 0x1FB1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FBA, { 0x1F70, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FBB, { 0x1F71, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FBC, { 0x03B1, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FBE, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FC2, { 0x1F74, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FC3, { 0x03B7, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FC4, { 0x03AE, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FC6, { 0x03B7, 0x0342, 0x0000, 0x0000 } },
+ { 0x1FC7, { 0x03B7, 0x0342, 0x03B9, 0x0000 } },
+ { 0x1FC8, { 0x1F72, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FC9, { 0x1F73, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FCA, { 0x1F74, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FCB, { 0x1F75, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FCC, { 0x03B7, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FD2, { 0x03B9, 0x0308, 0x0300, 0x0000 } },
+ { 0x1FD3, { 0x03B9, 0x0308, 0x0301, 0x0000 } },
+ { 0x1FD6, { 0x03B9, 0x0342, 0x0000, 0x0000 } },
+ { 0x1FD7, { 0x03B9, 0x0308, 0x0342, 0x0000 } },
+ { 0x1FD8, { 0x1FD0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FD9, { 0x1FD1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FDA, { 0x1F76, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FDB, { 0x1F77, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FE2, { 0x03C5, 0x0308, 0x0300, 0x0000 } },
+ { 0x1FE3, { 0x03C5, 0x0308, 0x0301, 0x0000 } },
+ { 0x1FE4, { 0x03C1, 0x0313, 0x0000, 0x0000 } },
+ { 0x1FE6, { 0x03C5, 0x0342, 0x0000, 0x0000 } },
+ { 0x1FE7, { 0x03C5, 0x0308, 0x0342, 0x0000 } },
+ { 0x1FE8, { 0x1FE0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FE9, { 0x1FE1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FEA, { 0x1F7A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FEB, { 0x1F7B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FEC, { 0x1FE5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FF2, { 0x1F7C, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FF3, { 0x03C9, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FF4, { 0x03CE, 0x03B9, 0x0000, 0x0000 } },
+ { 0x1FF6, { 0x03C9, 0x0342, 0x0000, 0x0000 } },
+ { 0x1FF7, { 0x03C9, 0x0342, 0x03B9, 0x0000 } },
+ { 0x1FF8, { 0x1F78, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FF9, { 0x1F79, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FFA, { 0x1F7C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FFB, { 0x1F7D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1FFC, { 0x03C9, 0x03B9, 0x0000, 0x0000 } },
+ { 0x20A8, { 0x0072, 0x0073, 0x0000, 0x0000 } },
+ { 0x2102, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x2103, { 0x00B0, 0x0063, 0x0000, 0x0000 } },
+ { 0x2107, { 0x025B, 0x0000, 0x0000, 0x0000 } },
+ { 0x2109, { 0x00B0, 0x0066, 0x0000, 0x0000 } },
+ { 0x210B, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x210C, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x210D, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x2110, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x2111, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x2112, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x2115, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x2116, { 0x006E, 0x006F, 0x0000, 0x0000 } },
+ { 0x2119, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x211A, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x211B, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x211C, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x211D, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x2120, { 0x0073, 0x006D, 0x0000, 0x0000 } },
+ { 0x2121, { 0x0074, 0x0065, 0x006C, 0x0000 } },
+ { 0x2122, { 0x0074, 0x006D, 0x0000, 0x0000 } },
+ { 0x2124, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x2126, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x2128, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x212A, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x212B, { 0x00E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x212C, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x212D, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x2130, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x2131, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x2133, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x213E, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x213F, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x2145, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x2160, { 0x2170, 0x0000, 0x0000, 0x0000 } },
+ { 0x2161, { 0x2171, 0x0000, 0x0000, 0x0000 } },
+ { 0x2162, { 0x2172, 0x0000, 0x0000, 0x0000 } },
+ { 0x2163, { 0x2173, 0x0000, 0x0000, 0x0000 } },
+ { 0x2164, { 0x2174, 0x0000, 0x0000, 0x0000 } },
+ { 0x2165, { 0x2175, 0x0000, 0x0000, 0x0000 } },
+ { 0x2166, { 0x2176, 0x0000, 0x0000, 0x0000 } },
+ { 0x2167, { 0x2177, 0x0000, 0x0000, 0x0000 } },
+ { 0x2168, { 0x2178, 0x0000, 0x0000, 0x0000 } },
+ { 0x2169, { 0x2179, 0x0000, 0x0000, 0x0000 } },
+ { 0x216A, { 0x217A, 0x0000, 0x0000, 0x0000 } },
+ { 0x216B, { 0x217B, 0x0000, 0x0000, 0x0000 } },
+ { 0x216C, { 0x217C, 0x0000, 0x0000, 0x0000 } },
+ { 0x216D, { 0x217D, 0x0000, 0x0000, 0x0000 } },
+ { 0x216E, { 0x217E, 0x0000, 0x0000, 0x0000 } },
+ { 0x216F, { 0x217F, 0x0000, 0x0000, 0x0000 } },
+ { 0x24B6, { 0x24D0, 0x0000, 0x0000, 0x0000 } },
+ { 0x24B7, { 0x24D1, 0x0000, 0x0000, 0x0000 } },
+ { 0x24B8, { 0x24D2, 0x0000, 0x0000, 0x0000 } },
+ { 0x24B9, { 0x24D3, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BA, { 0x24D4, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BB, { 0x24D5, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BC, { 0x24D6, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BD, { 0x24D7, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BE, { 0x24D8, 0x0000, 0x0000, 0x0000 } },
+ { 0x24BF, { 0x24D9, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C0, { 0x24DA, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C1, { 0x24DB, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C2, { 0x24DC, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C3, { 0x24DD, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C4, { 0x24DE, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C5, { 0x24DF, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C6, { 0x24E0, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C7, { 0x24E1, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C8, { 0x24E2, 0x0000, 0x0000, 0x0000 } },
+ { 0x24C9, { 0x24E3, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CA, { 0x24E4, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CB, { 0x24E5, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CC, { 0x24E6, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CD, { 0x24E7, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CE, { 0x24E8, 0x0000, 0x0000, 0x0000 } },
+ { 0x24CF, { 0x24E9, 0x0000, 0x0000, 0x0000 } },
+ { 0x3371, { 0x0068, 0x0070, 0x0061, 0x0000 } },
+ { 0x3373, { 0x0061, 0x0075, 0x0000, 0x0000 } },
+ { 0x3375, { 0x006F, 0x0076, 0x0000, 0x0000 } },
+ { 0x3380, { 0x0070, 0x0061, 0x0000, 0x0000 } },
+ { 0x3381, { 0x006E, 0x0061, 0x0000, 0x0000 } },
+ { 0x3382, { 0x03BC, 0x0061, 0x0000, 0x0000 } },
+ { 0x3383, { 0x006D, 0x0061, 0x0000, 0x0000 } },
+ { 0x3384, { 0x006B, 0x0061, 0x0000, 0x0000 } },
+ { 0x3385, { 0x006B, 0x0062, 0x0000, 0x0000 } },
+ { 0x3386, { 0x006D, 0x0062, 0x0000, 0x0000 } },
+ { 0x3387, { 0x0067, 0x0062, 0x0000, 0x0000 } },
+ { 0x338A, { 0x0070, 0x0066, 0x0000, 0x0000 } },
+ { 0x338B, { 0x006E, 0x0066, 0x0000, 0x0000 } },
+ { 0x338C, { 0x03BC, 0x0066, 0x0000, 0x0000 } },
+ { 0x3390, { 0x0068, 0x007A, 0x0000, 0x0000 } },
+ { 0x3391, { 0x006B, 0x0068, 0x007A, 0x0000 } },
+ { 0x3392, { 0x006D, 0x0068, 0x007A, 0x0000 } },
+ { 0x3393, { 0x0067, 0x0068, 0x007A, 0x0000 } },
+ { 0x3394, { 0x0074, 0x0068, 0x007A, 0x0000 } },
+ { 0x33A9, { 0x0070, 0x0061, 0x0000, 0x0000 } },
+ { 0x33AA, { 0x006B, 0x0070, 0x0061, 0x0000 } },
+ { 0x33AB, { 0x006D, 0x0070, 0x0061, 0x0000 } },
+ { 0x33AC, { 0x0067, 0x0070, 0x0061, 0x0000 } },
+ { 0x33B4, { 0x0070, 0x0076, 0x0000, 0x0000 } },
+ { 0x33B5, { 0x006E, 0x0076, 0x0000, 0x0000 } },
+ { 0x33B6, { 0x03BC, 0x0076, 0x0000, 0x0000 } },
+ { 0x33B7, { 0x006D, 0x0076, 0x0000, 0x0000 } },
+ { 0x33B8, { 0x006B, 0x0076, 0x0000, 0x0000 } },
+ { 0x33B9, { 0x006D, 0x0076, 0x0000, 0x0000 } },
+ { 0x33BA, { 0x0070, 0x0077, 0x0000, 0x0000 } },
+ { 0x33BB, { 0x006E, 0x0077, 0x0000, 0x0000 } },
+ { 0x33BC, { 0x03BC, 0x0077, 0x0000, 0x0000 } },
+ { 0x33BD, { 0x006D, 0x0077, 0x0000, 0x0000 } },
+ { 0x33BE, { 0x006B, 0x0077, 0x0000, 0x0000 } },
+ { 0x33BF, { 0x006D, 0x0077, 0x0000, 0x0000 } },
+ { 0x33C0, { 0x006B, 0x03C9, 0x0000, 0x0000 } },
+ { 0x33C1, { 0x006D, 0x03C9, 0x0000, 0x0000 } },
+ { 0x33C3, { 0x0062, 0x0071, 0x0000, 0x0000 } },
+ { 0x33C6, { 0x0063, 0x2215, 0x006B, 0x0067 } },
+ { 0x33C7, { 0x0063, 0x006F, 0x002E, 0x0000 } },
+ { 0x33C8, { 0x0064, 0x0062, 0x0000, 0x0000 } },
+ { 0x33C9, { 0x0067, 0x0079, 0x0000, 0x0000 } },
+ { 0x33CB, { 0x0068, 0x0070, 0x0000, 0x0000 } },
+ { 0x33CD, { 0x006B, 0x006B, 0x0000, 0x0000 } },
+ { 0x33CE, { 0x006B, 0x006D, 0x0000, 0x0000 } },
+ { 0x33D7, { 0x0070, 0x0068, 0x0000, 0x0000 } },
+ { 0x33D9, { 0x0070, 0x0070, 0x006D, 0x0000 } },
+ { 0x33DA, { 0x0070, 0x0072, 0x0000, 0x0000 } },
+ { 0x33DC, { 0x0073, 0x0076, 0x0000, 0x0000 } },
+ { 0x33DD, { 0x0077, 0x0062, 0x0000, 0x0000 } },
+ { 0xFB00, { 0x0066, 0x0066, 0x0000, 0x0000 } },
+ { 0xFB01, { 0x0066, 0x0069, 0x0000, 0x0000 } },
+ { 0xFB02, { 0x0066, 0x006C, 0x0000, 0x0000 } },
+ { 0xFB03, { 0x0066, 0x0066, 0x0069, 0x0000 } },
+ { 0xFB04, { 0x0066, 0x0066, 0x006C, 0x0000 } },
+ { 0xFB05, { 0x0073, 0x0074, 0x0000, 0x0000 } },
+ { 0xFB06, { 0x0073, 0x0074, 0x0000, 0x0000 } },
+ { 0xFB13, { 0x0574, 0x0576, 0x0000, 0x0000 } },
+ { 0xFB14, { 0x0574, 0x0565, 0x0000, 0x0000 } },
+ { 0xFB15, { 0x0574, 0x056B, 0x0000, 0x0000 } },
+ { 0xFB16, { 0x057E, 0x0576, 0x0000, 0x0000 } },
+ { 0xFB17, { 0x0574, 0x056D, 0x0000, 0x0000 } },
+ { 0xFF21, { 0xFF41, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF22, { 0xFF42, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF23, { 0xFF43, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF24, { 0xFF44, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF25, { 0xFF45, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF26, { 0xFF46, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF27, { 0xFF47, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF28, { 0xFF48, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF29, { 0xFF49, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2A, { 0xFF4A, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2B, { 0xFF4B, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2C, { 0xFF4C, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2D, { 0xFF4D, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2E, { 0xFF4E, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF2F, { 0xFF4F, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF30, { 0xFF50, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF31, { 0xFF51, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF32, { 0xFF52, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF33, { 0xFF53, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF34, { 0xFF54, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF35, { 0xFF55, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF36, { 0xFF56, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF37, { 0xFF57, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF38, { 0xFF58, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF39, { 0xFF59, 0x0000, 0x0000, 0x0000 } },
+ { 0xFF3A, { 0xFF5A, 0x0000, 0x0000, 0x0000 } },
+ // #####
+/* { 0x10400, { 0x10428, 0x0000, 0x0000, 0x0000 } },
+ { 0x10401, { 0x10429, 0x0000, 0x0000, 0x0000 } },
+ { 0x10402, { 0x1042A, 0x0000, 0x0000, 0x0000 } },
+ { 0x10403, { 0x1042B, 0x0000, 0x0000, 0x0000 } },
+ { 0x10404, { 0x1042C, 0x0000, 0x0000, 0x0000 } },
+ { 0x10405, { 0x1042D, 0x0000, 0x0000, 0x0000 } },
+ { 0x10406, { 0x1042E, 0x0000, 0x0000, 0x0000 } },
+ { 0x10407, { 0x1042F, 0x0000, 0x0000, 0x0000 } },
+ { 0x10408, { 0x10430, 0x0000, 0x0000, 0x0000 } },
+ { 0x10409, { 0x10431, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040A, { 0x10432, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040B, { 0x10433, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040C, { 0x10434, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040D, { 0x10435, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040E, { 0x10436, 0x0000, 0x0000, 0x0000 } },
+ { 0x1040F, { 0x10437, 0x0000, 0x0000, 0x0000 } },
+ { 0x10410, { 0x10438, 0x0000, 0x0000, 0x0000 } },
+ { 0x10411, { 0x10439, 0x0000, 0x0000, 0x0000 } },
+ { 0x10412, { 0x1043A, 0x0000, 0x0000, 0x0000 } },
+ { 0x10413, { 0x1043B, 0x0000, 0x0000, 0x0000 } },
+ { 0x10414, { 0x1043C, 0x0000, 0x0000, 0x0000 } },
+ { 0x10415, { 0x1043D, 0x0000, 0x0000, 0x0000 } },
+ { 0x10416, { 0x1043E, 0x0000, 0x0000, 0x0000 } },
+ { 0x10417, { 0x1043F, 0x0000, 0x0000, 0x0000 } },
+ { 0x10418, { 0x10440, 0x0000, 0x0000, 0x0000 } },
+ { 0x10419, { 0x10441, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041A, { 0x10442, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041B, { 0x10443, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041C, { 0x10444, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041D, { 0x10445, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041E, { 0x10446, 0x0000, 0x0000, 0x0000 } },
+ { 0x1041F, { 0x10447, 0x0000, 0x0000, 0x0000 } },
+ { 0x10420, { 0x10448, 0x0000, 0x0000, 0x0000 } },
+ { 0x10421, { 0x10449, 0x0000, 0x0000, 0x0000 } },
+ { 0x10422, { 0x1044A, 0x0000, 0x0000, 0x0000 } },
+ { 0x10423, { 0x1044B, 0x0000, 0x0000, 0x0000 } },
+ { 0x10424, { 0x1044C, 0x0000, 0x0000, 0x0000 } },
+ { 0x10425, { 0x1044D, 0x0000, 0x0000, 0x0000 } },*/
+ { 0x1D400, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D401, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D402, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D403, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D404, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D405, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D406, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D407, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D408, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D409, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40A, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40B, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40C, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40D, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40E, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D40F, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D410, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D411, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D412, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D413, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D414, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D415, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D416, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D417, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D418, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D419, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D434, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D435, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D436, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D437, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D438, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D439, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43A, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43B, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43C, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43D, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43E, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D43F, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D440, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D441, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D442, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D443, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D444, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D445, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D446, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D447, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D448, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D449, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D44A, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D44B, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D44C, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D44D, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D468, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D469, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46A, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46B, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46C, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46D, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46E, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D46F, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D470, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D471, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D472, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D473, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D474, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D475, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D476, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D477, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D478, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D479, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47A, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47B, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47C, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47D, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47E, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D47F, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D480, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D481, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D49C, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D49E, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D49F, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4A2, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4A5, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4A6, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4A9, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4AA, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4AB, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4AC, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4AE, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4AF, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B0, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B1, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B2, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B3, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B4, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4B5, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D0, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D1, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D2, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D3, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D4, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D5, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D6, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D7, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D8, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4D9, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DA, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DB, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DC, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DD, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DE, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4DF, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E0, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E1, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E2, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E3, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E4, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E5, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E6, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E7, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E8, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D4E9, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D504, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D505, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D507, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D508, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D509, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D50A, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D50D, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D50E, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D50F, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D510, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D511, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D512, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D513, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D514, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D516, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D517, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D518, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D519, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D51A, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D51B, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D51C, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D538, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D539, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D53B, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D53C, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D53D, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D53E, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D540, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D541, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D542, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D543, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D544, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D546, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54A, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54B, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54C, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54D, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54E, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D54F, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D550, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D56C, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D56D, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D56E, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D56F, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D570, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D571, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D572, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D573, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D574, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D575, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D576, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D577, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D578, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D579, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57A, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57B, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57C, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57D, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57E, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D57F, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D580, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D581, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D582, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D583, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D584, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D585, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A0, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A1, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A2, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A3, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A4, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A5, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A6, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A7, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A8, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5A9, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AA, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AB, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AC, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AD, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AE, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5AF, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B0, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B1, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B2, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B3, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B4, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B5, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B6, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B7, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B8, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5B9, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D4, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D5, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D6, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D7, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D8, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5D9, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DA, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DB, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DC, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DD, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DE, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5DF, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E0, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E1, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E2, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E3, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E4, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E5, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E6, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E7, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E8, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5E9, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5EA, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5EB, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5EC, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D5ED, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D608, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D609, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60A, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60B, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60C, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60D, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60E, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D60F, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D610, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D611, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D612, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D613, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D614, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D615, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D616, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D617, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D618, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D619, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61A, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61B, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61C, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61D, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61E, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D61F, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D620, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D621, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D63C, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D63D, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D63E, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D63F, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D640, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D641, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D642, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D643, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D644, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D645, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D646, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D647, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D648, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D649, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64A, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64B, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64C, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64D, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64E, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D64F, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D650, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D651, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D652, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D653, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D654, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D655, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D670, { 0x0061, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D671, { 0x0062, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D672, { 0x0063, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D673, { 0x0064, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D674, { 0x0065, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D675, { 0x0066, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D676, { 0x0067, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D677, { 0x0068, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D678, { 0x0069, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D679, { 0x006A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67A, { 0x006B, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67B, { 0x006C, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67C, { 0x006D, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67D, { 0x006E, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67E, { 0x006F, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D67F, { 0x0070, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D680, { 0x0071, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D681, { 0x0072, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D682, { 0x0073, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D683, { 0x0074, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D684, { 0x0075, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D685, { 0x0076, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D686, { 0x0077, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D687, { 0x0078, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D688, { 0x0079, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D689, { 0x007A, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6A8, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6A9, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AA, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AB, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AC, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AD, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AE, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6AF, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B0, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B1, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B2, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B3, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B4, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B5, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B6, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B7, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B8, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6B9, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BA, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BB, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BC, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BD, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BE, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6BF, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6C0, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6D3, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E2, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E3, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E4, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E5, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E6, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E7, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E8, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6E9, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6EA, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6EB, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6EC, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6ED, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6EE, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6EF, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F0, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F1, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F2, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F3, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F4, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F5, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F6, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F7, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F8, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6F9, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D6FA, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D70D, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D71C, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D71D, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D71E, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D71F, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D720, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D721, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D722, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D723, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D724, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D725, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D726, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D727, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D728, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D729, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72A, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72B, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72C, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72D, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72E, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D72F, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D730, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D731, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D732, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D733, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D734, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D747, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D756, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D757, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D758, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D759, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75A, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75B, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75C, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75D, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75E, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D75F, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D760, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D761, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D762, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D763, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D764, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D765, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D766, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D767, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D768, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D769, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D76A, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D76B, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D76C, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D76D, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D76E, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D781, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D790, { 0x03B1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D791, { 0x03B2, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D792, { 0x03B3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D793, { 0x03B4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D794, { 0x03B5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D795, { 0x03B6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D796, { 0x03B7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D797, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D798, { 0x03B9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D799, { 0x03BA, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79A, { 0x03BB, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79B, { 0x03BC, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79C, { 0x03BD, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79D, { 0x03BE, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79E, { 0x03BF, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D79F, { 0x03C0, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A0, { 0x03C1, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A1, { 0x03B8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A2, { 0x03C3, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A3, { 0x03C4, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A4, { 0x03C5, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A5, { 0x03C6, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A6, { 0x03C7, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A7, { 0x03C8, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7A8, { 0x03C9, 0x0000, 0x0000, 0x0000 } },
+ { 0x1D7BB, { 0x03C3, 0x0000, 0x0000, 0x0000 } }
+};
+
+static void mapToLowerCase(QString *str)
+{
+ int N = sizeof(NameprepCaseFolding) / sizeof(NameprepCaseFolding[0]);
+
+ QChar *d = 0;
+ for (int i = 0; i < str->size(); ++i) {
+ int uc = str->at(i).unicode();
+ if (uc < 0x80) {
+ if (uc <= 'Z' && uc >= 'A') {
+ uc |= 0x20;
+ if (!d)
+ d = str->data();
+ d[i] = QChar(uc);
+ }
+ } else {
+ const NameprepCaseFoldingEntry *entry = qBinaryFind(NameprepCaseFolding,
+ NameprepCaseFolding + N,
+ uc);
+ if ((entry - NameprepCaseFolding) != N) {
+ int l = 1;
+ while (l < 4 && entry->mapping[l])
+ ++l;
+ if (l > 1) {
+ str->replace(i, 1, (const QChar *)&entry->mapping[0], l);
+ d = 0;
+ } else {
+ if (!d)
+ d = str->data();
+ d[i] = QChar(entry->mapping[0]);
+ }
+ }
+ }
+ }
+}
+
+static bool isMappedToNothing(const QChar &ch)
+{
+ if (ch.unicode() < 0xad)
+ return false;
+ switch (ch.unicode()) {
+ case 0x00AD: case 0x034F: case 0x1806: case 0x180B: case 0x180C: case 0x180D:
+ case 0x200B: case 0x200C: case 0x200D: case 0x2060: case 0xFE00: case 0xFE01:
+ case 0xFE02: case 0xFE03: case 0xFE04: case 0xFE05: case 0xFE06: case 0xFE07:
+ case 0xFE08: case 0xFE09: case 0xFE0A: case 0xFE0B: case 0xFE0C: case 0xFE0D:
+ case 0xFE0E: case 0xFE0F: case 0xFEFF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+static void stripProhibitedOutput(QString *str)
+{
+ ushort *out = (ushort *)str->data();
+ const ushort *in = out;
+ const ushort *end = out + str->size();
+ while (in < end) {
+ ushort uc = *in;
+ if (uc < 0x80 ||
+ !(uc <= 0x009F
+ || uc == 0x00A0
+ || uc == 0x0340
+ || uc == 0x0341
+ || uc == 0x06DD
+ || uc == 0x070F
+ || uc == 0x1680
+ || uc == 0x180E
+ || (uc >= 0x2000 && uc <= 0x200B)
+ || uc == 0x200C
+ || uc == 0x200D
+ || uc == 0x200E
+ || uc == 0x200F
+ || (uc >= 0x2028 && uc <= 0x202F)
+ || uc == 0x205F
+ || (uc >= 0x2060 && uc <= 0x2063)
+ || uc == 0x206A
+ || (uc >= 0x206A && uc <= 0x206F)
+ || (uc >= 0x2FF0 && uc <= 0x2FFB)
+ || uc == 0x3000
+ || (uc >= 0xD800 && uc <= 0xDFFF)
+ || (uc >= 0xE000 && uc <= 0xF8FF)
+ || (uc >= 0xFDD0 && uc <= 0xFDEF)
+ || uc == 0xFEFF
+ || (uc >= 0xFFF9 && uc <= 0xFFFC)
+ || (uc >= 0xFFFA && (uc <= 0xFFFE || uc == 0xFFFF))
+ /* ### Add NAMEPREP support for surrogates
+ || uc == 0xE0001
+ || (uc >= 0x2FFFE && uc <= 0x2FFFF)
+ || (uc >= 0x1D173 && uc <= 0x1D17A)
+ || (uc >= 0x1FFFE && uc <= 0x1FFFF)
+ || (uc >= 0x3FFFE && uc <= 0x3FFFF)
+ || (uc >= 0x4FFFE && uc <= 0x4FFFF)
+ || (uc >= 0x5FFFE && uc <= 0x5FFFF)
+ || (uc >= 0x6FFFE && uc <= 0x6FFFF)
+ || (uc >= 0x7FFFE && uc <= 0x7FFFF)
+ || (uc >= 0x8FFFE && uc <= 0x8FFFF)
+ || (uc >= 0x9FFFE && uc <= 0x9FFFF)
+ || (uc >= 0xAFFFE && uc <= 0xAFFFF)
+ || (uc >= 0xBFFFE && uc <= 0xBFFFF)
+ || (uc >= 0xCFFFE && uc <= 0xCFFFF)
+ || (uc >= 0xDFFFE && uc <= 0xDFFFF)
+ || (uc >= 0xE0020 && uc <= 0xE007F)
+ || (uc >= 0xEFFFE && uc <= 0xEFFFF)
+ || (uc >= 0xF0000 && uc <= 0xFFFFD)
+ || (uc >= 0xFFFFE && uc <= 0xFFFFF)
+ || (uc >= 0x100000 && uc <= 0x10FFFD)
+ || (uc >= 0x10FFFE && uc <= 0x10FFFF)*/))
+ *out++ = *in;
+ ++in;
+ }
+ if (in != out)
+ str->truncate(out - str->utf16());
+}
+
+static bool isBidirectionalRorAL(const QChar &c)
+{
+ ushort uc = c.unicode();
+ if (uc < 0x5b0)
+ return false;
+ return uc == 0x05BE
+ || uc == 0x05C0
+ || uc == 0x05C3
+ || (uc >= 0x05D0 && uc <= 0x05EA)
+ || (uc >= 0x05F0 && uc <= 0x05F4)
+ || uc == 0x061B
+ || uc == 0x061F
+ || (uc >= 0x0621 && uc <= 0x063A)
+ || (uc >= 0x0640 && uc <= 0x064A)
+ || (uc >= 0x066D && uc <= 0x066F)
+ || (uc >= 0x0671 && uc <= 0x06D5)
+ || uc == 0x06DD
+ || (uc >= 0x06E5 && uc <= 0x06E6)
+ || (uc >= 0x06FA && uc <= 0x06FE)
+ || (uc >= 0x0700 && uc <= 0x070D)
+ || uc == 0x0710
+ || (uc >= 0x0712 && uc <= 0x072C)
+ || (uc >= 0x0780 && uc <= 0x07A5)
+ || uc == 0x07B1
+ || uc == 0x200F
+ || uc == 0xFB1D
+ || (uc >= 0xFB1F && uc <= 0xFB28)
+ || (uc >= 0xFB2A && uc <= 0xFB36)
+ || (uc >= 0xFB38 && uc <= 0xFB3C)
+ || uc == 0xFB3E
+ || (uc >= 0xFB40 && uc <= 0xFB41)
+ || (uc >= 0xFB43 && uc <= 0xFB44)
+ || (uc >= 0xFB46 && uc <= 0xFBB1)
+ || (uc >= 0xFBD3 && uc <= 0xFD3D)
+ || (uc >= 0xFD50 && uc <= 0xFD8F)
+ || (uc >= 0xFD92 && uc <= 0xFDC7)
+ || (uc >= 0xFDF0 && uc <= 0xFDFC)
+ || (uc >= 0xFE70 && uc <= 0xFE74)
+ || (uc >= 0xFE76 && uc <= 0xFEFC);
+}
+
+static bool isBidirectionalL(const QChar &ch)
+{
+ ushort uc = ch.unicode();
+ if (uc < 0xaa)
+ return (uc >= 0x0041 && uc <= 0x005A)
+ || (uc >= 0x0061 && uc <= 0x007A);
+
+ if (uc == 0x00AA
+ || uc == 0x00B5
+ || uc == 0x00BA
+ || (uc >= 0x00C0 && uc <= 0x00D6)
+ || (uc >= 0x00D8 && uc <= 0x00F6)
+ || (uc >= 0x00F8 && uc <= 0x0220)
+ || (uc >= 0x0222 && uc <= 0x0233)
+ || (uc >= 0x0250 && uc <= 0x02AD)
+ || (uc >= 0x02B0 && uc <= 0x02B8)
+ || (uc >= 0x02BB && uc <= 0x02C1)
+ || (uc >= 0x02D0 && uc <= 0x02D1)
+ || (uc >= 0x02E0 && uc <= 0x02E4)
+ || uc == 0x02EE
+ || uc == 0x037A
+ || uc == 0x0386
+ || (uc >= 0x0388 && uc <= 0x038A)) {
+ return true;
+ }
+
+ if (uc == 0x038C
+ || (uc >= 0x038E && uc <= 0x03A1)
+ || (uc >= 0x03A3 && uc <= 0x03CE)
+ || (uc >= 0x03D0 && uc <= 0x03F5)
+ || (uc >= 0x0400 && uc <= 0x0482)
+ || (uc >= 0x048A && uc <= 0x04CE)
+ || (uc >= 0x04D0 && uc <= 0x04F5)
+ || (uc >= 0x04F8 && uc <= 0x04F9)
+ || (uc >= 0x0500 && uc <= 0x050F)
+ || (uc >= 0x0531 && uc <= 0x0556)
+ || (uc >= 0x0559 && uc <= 0x055F)
+ || (uc >= 0x0561 && uc <= 0x0587)
+ || uc == 0x0589
+ || uc == 0x0903
+ || (uc >= 0x0905 && uc <= 0x0939)
+ || (uc >= 0x093D && uc <= 0x0940)
+ || (uc >= 0x0949 && uc <= 0x094C)
+ || uc == 0x0950) {
+ return true;
+ }
+
+ if ((uc >= 0x0958 && uc <= 0x0961)
+ || (uc >= 0x0964 && uc <= 0x0970)
+ || (uc >= 0x0982 && uc <= 0x0983)
+ || (uc >= 0x0985 && uc <= 0x098C)
+ || (uc >= 0x098F && uc <= 0x0990)
+ || (uc >= 0x0993 && uc <= 0x09A8)
+ || (uc >= 0x09AA && uc <= 0x09B0)
+ || uc == 0x09B2
+ || (uc >= 0x09B6 && uc <= 0x09B9)
+ || (uc >= 0x09BE && uc <= 0x09C0)
+ || (uc >= 0x09C7 && uc <= 0x09C8)
+ || (uc >= 0x09CB && uc <= 0x09CC)
+ || uc == 0x09D7
+ || (uc >= 0x09DC && uc <= 0x09DD)
+ || (uc >= 0x09DF && uc <= 0x09E1)
+ || (uc >= 0x09E6 && uc <= 0x09F1)
+ || (uc >= 0x09F4 && uc <= 0x09FA)
+ || (uc >= 0x0A05 && uc <= 0x0A0A)
+ || (uc >= 0x0A0F && uc <= 0x0A10)
+ || (uc >= 0x0A13 && uc <= 0x0A28)
+ || (uc >= 0x0A2A && uc <= 0x0A30)
+ || (uc >= 0x0A32 && uc <= 0x0A33)) {
+ return true;
+ }
+
+ if ((uc >= 0x0A35 && uc <= 0x0A36)
+ || (uc >= 0x0A38 && uc <= 0x0A39)
+ || (uc >= 0x0A3E && uc <= 0x0A40)
+ || (uc >= 0x0A59 && uc <= 0x0A5C)
+ || uc == 0x0A5E
+ || (uc >= 0x0A66 && uc <= 0x0A6F)
+ || (uc >= 0x0A72 && uc <= 0x0A74)
+ || uc == 0x0A83
+ || (uc >= 0x0A85 && uc <= 0x0A8B)
+ || uc == 0x0A8D
+ || (uc >= 0x0A8F && uc <= 0x0A91)
+ || (uc >= 0x0A93 && uc <= 0x0AA8)
+ || (uc >= 0x0AAA && uc <= 0x0AB0)
+ || (uc >= 0x0AB2 && uc <= 0x0AB3)
+ || (uc >= 0x0AB5 && uc <= 0x0AB9)
+ || (uc >= 0x0ABD && uc <= 0x0AC0)
+ || uc == 0x0AC9
+ || (uc >= 0x0ACB && uc <= 0x0ACC)
+ || uc == 0x0AD0
+ || uc == 0x0AE0
+ || (uc >= 0x0AE6 && uc <= 0x0AEF)
+ || (uc >= 0x0B02 && uc <= 0x0B03)
+ || (uc >= 0x0B05 && uc <= 0x0B0C)
+ || (uc >= 0x0B0F && uc <= 0x0B10)
+ || (uc >= 0x0B13 && uc <= 0x0B28)
+ || (uc >= 0x0B2A && uc <= 0x0B30)) {
+ return true;
+ }
+
+ if ((uc >= 0x0B32 && uc <= 0x0B33)
+ || (uc >= 0x0B36 && uc <= 0x0B39)
+ || (uc >= 0x0B3D && uc <= 0x0B3E)
+ || uc == 0x0B40
+ || (uc >= 0x0B47 && uc <= 0x0B48)
+ || (uc >= 0x0B4B && uc <= 0x0B4C)
+ || uc == 0x0B57
+ || (uc >= 0x0B5C && uc <= 0x0B5D)
+ || (uc >= 0x0B5F && uc <= 0x0B61)
+ || (uc >= 0x0B66 && uc <= 0x0B70)
+ || uc == 0x0B83
+ || (uc >= 0x0B85 && uc <= 0x0B8A)
+ || (uc >= 0x0B8E && uc <= 0x0B90)
+ || (uc >= 0x0B92 && uc <= 0x0B95)
+ || (uc >= 0x0B99 && uc <= 0x0B9A)
+ || uc == 0x0B9C
+ || (uc >= 0x0B9E && uc <= 0x0B9F)
+ || (uc >= 0x0BA3 && uc <= 0x0BA4)
+ || (uc >= 0x0BA8 && uc <= 0x0BAA)
+ || (uc >= 0x0BAE && uc <= 0x0BB5)
+ || (uc >= 0x0BB7 && uc <= 0x0BB9)
+ || (uc >= 0x0BBE && uc <= 0x0BBF)
+ || (uc >= 0x0BC1 && uc <= 0x0BC2)
+ || (uc >= 0x0BC6 && uc <= 0x0BC8)
+ || (uc >= 0x0BCA && uc <= 0x0BCC)
+ || uc == 0x0BD7
+ || (uc >= 0x0BE7 && uc <= 0x0BF2)
+ || (uc >= 0x0C01 && uc <= 0x0C03)
+ || (uc >= 0x0C05 && uc <= 0x0C0C)
+ || (uc >= 0x0C0E && uc <= 0x0C10)
+ || (uc >= 0x0C12 && uc <= 0x0C28)
+ || (uc >= 0x0C2A && uc <= 0x0C33)
+ || (uc >= 0x0C35 && uc <= 0x0C39)) {
+ return true;
+ }
+ if ((uc >= 0x0C41 && uc <= 0x0C44)
+ || (uc >= 0x0C60 && uc <= 0x0C61)
+ || (uc >= 0x0C66 && uc <= 0x0C6F)
+ || (uc >= 0x0C82 && uc <= 0x0C83)
+ || (uc >= 0x0C85 && uc <= 0x0C8C)
+ || (uc >= 0x0C8E && uc <= 0x0C90)
+ || (uc >= 0x0C92 && uc <= 0x0CA8)
+ || (uc >= 0x0CAA && uc <= 0x0CB3)
+ || (uc >= 0x0CB5 && uc <= 0x0CB9)
+ || uc == 0x0CBE
+ || (uc >= 0x0CC0 && uc <= 0x0CC4)
+ || (uc >= 0x0CC7 && uc <= 0x0CC8)
+ || (uc >= 0x0CCA && uc <= 0x0CCB)
+ || (uc >= 0x0CD5 && uc <= 0x0CD6)
+ || uc == 0x0CDE
+ || (uc >= 0x0CE0 && uc <= 0x0CE1)
+ || (uc >= 0x0CE6 && uc <= 0x0CEF)
+ || (uc >= 0x0D02 && uc <= 0x0D03)
+ || (uc >= 0x0D05 && uc <= 0x0D0C)
+ || (uc >= 0x0D0E && uc <= 0x0D10)
+ || (uc >= 0x0D12 && uc <= 0x0D28)
+ || (uc >= 0x0D2A && uc <= 0x0D39)
+ || (uc >= 0x0D3E && uc <= 0x0D40)
+ || (uc >= 0x0D46 && uc <= 0x0D48)
+ || (uc >= 0x0D4A && uc <= 0x0D4C)
+ || uc == 0x0D57
+ || (uc >= 0x0D60 && uc <= 0x0D61)
+ || (uc >= 0x0D66 && uc <= 0x0D6F)
+ || (uc >= 0x0D82 && uc <= 0x0D83)
+ || (uc >= 0x0D85 && uc <= 0x0D96)
+ || (uc >= 0x0D9A && uc <= 0x0DB1)
+ || (uc >= 0x0DB3 && uc <= 0x0DBB)
+ || uc == 0x0DBD) {
+ return true;
+ }
+ if ((uc >= 0x0DC0 && uc <= 0x0DC6)
+ || (uc >= 0x0DCF && uc <= 0x0DD1)
+ || (uc >= 0x0DD8 && uc <= 0x0DDF)
+ || (uc >= 0x0DF2 && uc <= 0x0DF4)
+ || (uc >= 0x0E01 && uc <= 0x0E30)
+ || (uc >= 0x0E32 && uc <= 0x0E33)
+ || (uc >= 0x0E40 && uc <= 0x0E46)
+ || (uc >= 0x0E4F && uc <= 0x0E5B)
+ || (uc >= 0x0E81 && uc <= 0x0E82)
+ || uc == 0x0E84
+ || (uc >= 0x0E87 && uc <= 0x0E88)
+ || uc == 0x0E8A
+ || uc == 0x0E8D
+ || (uc >= 0x0E94 && uc <= 0x0E97)
+ || (uc >= 0x0E99 && uc <= 0x0E9F)
+ || (uc >= 0x0EA1 && uc <= 0x0EA3)
+ || uc == 0x0EA5
+ || uc == 0x0EA7
+ || (uc >= 0x0EAA && uc <= 0x0EAB)
+ || (uc >= 0x0EAD && uc <= 0x0EB0)
+ || (uc >= 0x0EB2 && uc <= 0x0EB3)
+ || uc == 0x0EBD
+ || (uc >= 0x0EC0 && uc <= 0x0EC4)
+ || uc == 0x0EC6
+ || (uc >= 0x0ED0 && uc <= 0x0ED9)
+ || (uc >= 0x0EDC && uc <= 0x0EDD)
+ || (uc >= 0x0F00 && uc <= 0x0F17)
+ || (uc >= 0x0F1A && uc <= 0x0F34)
+ || uc == 0x0F36
+ || uc == 0x0F38
+ || (uc >= 0x0F3E && uc <= 0x0F47)
+ || (uc >= 0x0F49 && uc <= 0x0F6A)
+ || uc == 0x0F7F
+ || uc == 0x0F85
+ || (uc >= 0x0F88 && uc <= 0x0F8B)
+ || (uc >= 0x0FBE && uc <= 0x0FC5)
+ || (uc >= 0x0FC7 && uc <= 0x0FCC)
+ || uc == 0x0FCF) {
+ return true;
+ }
+
+ if ((uc >= 0x1000 && uc <= 0x1021)
+ || (uc >= 0x1023 && uc <= 0x1027)
+ || (uc >= 0x1029 && uc <= 0x102A)
+ || uc == 0x102C
+ || uc == 0x1031
+ || uc == 0x1038
+ || (uc >= 0x1040 && uc <= 0x1057)
+ || (uc >= 0x10A0 && uc <= 0x10C5)
+ || (uc >= 0x10D0 && uc <= 0x10F8)
+ || uc == 0x10FB
+ || (uc >= 0x1100 && uc <= 0x1159)
+ || (uc >= 0x115F && uc <= 0x11A2)
+ || (uc >= 0x11A8 && uc <= 0x11F9)
+ || (uc >= 0x1200 && uc <= 0x1206)
+ || (uc >= 0x1208 && uc <= 0x1246)
+ || uc == 0x1248
+ || (uc >= 0x124A && uc <= 0x124D)
+ || (uc >= 0x1250 && uc <= 0x1256)
+ || uc == 0x1258
+ || (uc >= 0x125A && uc <= 0x125D)
+ || (uc >= 0x1260 && uc <= 0x1286)
+ || uc == 0x1288
+ || (uc >= 0x128A && uc <= 0x128D)
+ || (uc >= 0x1290 && uc <= 0x12AE)
+ || uc == 0x12B0
+ || (uc >= 0x12B2 && uc <= 0x12B5)
+ || (uc >= 0x12B8 && uc <= 0x12BE)
+ || uc == 0x12C0
+ || (uc >= 0x12C2 && uc <= 0x12C5)
+ || (uc >= 0x12C8 && uc <= 0x12CE)
+ || (uc >= 0x12D0 && uc <= 0x12D6)
+ || (uc >= 0x12D8 && uc <= 0x12EE)
+ || (uc >= 0x12F0 && uc <= 0x130E)
+ || uc == 0x1310) {
+ return true;
+ }
+
+ if ((uc >= 0x1312 && uc <= 0x1315)
+ || (uc >= 0x1318 && uc <= 0x131E)
+ || (uc >= 0x1320 && uc <= 0x1346)
+ || (uc >= 0x1348 && uc <= 0x135A)
+ || (uc >= 0x1361 && uc <= 0x137C)
+ || (uc >= 0x13A0 && uc <= 0x13F4)
+ || (uc >= 0x1401 && uc <= 0x1676)
+ || (uc >= 0x1681 && uc <= 0x169A)
+ || (uc >= 0x16A0 && uc <= 0x16F0)
+ || (uc >= 0x1700 && uc <= 0x170C)
+ || (uc >= 0x170E && uc <= 0x1711)
+ || (uc >= 0x1720 && uc <= 0x1731)
+ || (uc >= 0x1735 && uc <= 0x1736)
+ || (uc >= 0x1740 && uc <= 0x1751)
+ || (uc >= 0x1760 && uc <= 0x176C)
+ || (uc >= 0x176E && uc <= 0x1770)
+ || (uc >= 0x1780 && uc <= 0x17B6)
+ || (uc >= 0x17BE && uc <= 0x17C5)
+ || (uc >= 0x17C7 && uc <= 0x17C8)
+ || (uc >= 0x17D4 && uc <= 0x17DA)
+ || uc == 0x17DC
+ || (uc >= 0x17E0 && uc <= 0x17E9)
+ || (uc >= 0x1810 && uc <= 0x1819)
+ || (uc >= 0x1820 && uc <= 0x1877)
+ || (uc >= 0x1880 && uc <= 0x18A8)
+ || (uc >= 0x1E00 && uc <= 0x1E9B)
+ || (uc >= 0x1EA0 && uc <= 0x1EF9)
+ || (uc >= 0x1F00 && uc <= 0x1F15)
+ || (uc >= 0x1F18 && uc <= 0x1F1D)
+ || (uc >= 0x1F20 && uc <= 0x1F45)
+ || (uc >= 0x1F48 && uc <= 0x1F4D)
+ || (uc >= 0x1F50 && uc <= 0x1F57)
+ || uc == 0x1F59
+ || uc == 0x1F5B
+ || uc == 0x1F5D) {
+ return true;
+ }
+
+ if ((uc >= 0x1F5F && uc <= 0x1F7D)
+ || (uc >= 0x1F80 && uc <= 0x1FB4)
+ || (uc >= 0x1FB6 && uc <= 0x1FBC)
+ || uc == 0x1FBE
+ || (uc >= 0x1FC2 && uc <= 0x1FC4)
+ || (uc >= 0x1FC6 && uc <= 0x1FCC)
+ || (uc >= 0x1FD0 && uc <= 0x1FD3)
+ || (uc >= 0x1FD6 && uc <= 0x1FDB)
+ || (uc >= 0x1FE0 && uc <= 0x1FEC)
+ || (uc >= 0x1FF2 && uc <= 0x1FF4)
+ || (uc >= 0x1FF6 && uc <= 0x1FFC)
+ || uc == 0x200E
+ || uc == 0x2071
+ || uc == 0x207F
+ || uc == 0x2102
+ || uc == 0x2107
+ || (uc >= 0x210A && uc <= 0x2113)
+ || uc == 0x2115
+ || (uc >= 0x2119 && uc <= 0x211D)) {
+ return true;
+ }
+
+ if (uc == 0x2124
+ || uc == 0x2126
+ || uc == 0x2128
+ || (uc >= 0x212A && uc <= 0x212D)
+ || (uc >= 0x212F && uc <= 0x2131)
+ || (uc >= 0x2133 && uc <= 0x2139)
+ || (uc >= 0x213D && uc <= 0x213F)
+ || (uc >= 0x2145 && uc <= 0x2149)
+ || (uc >= 0x2160 && uc <= 0x2183)
+ || (uc >= 0x2336 && uc <= 0x237A)
+ || uc == 0x2395
+ || (uc >= 0x249C && uc <= 0x24E9)
+ || (uc >= 0x3005 && uc <= 0x3007)
+ || (uc >= 0x3021 && uc <= 0x3029)
+ || (uc >= 0x3031 && uc <= 0x3035)
+ || (uc >= 0x3038 && uc <= 0x303C)
+ || (uc >= 0x3041 && uc <= 0x3096)
+ || (uc >= 0x309D && uc <= 0x309F)
+ || (uc >= 0x30A1 && uc <= 0x30FA)) {
+ return true;
+ }
+
+ if ((uc >= 0x30FC && uc <= 0x30FF)
+ || (uc >= 0x3105 && uc <= 0x312C)
+ || (uc >= 0x3131 && uc <= 0x318E)
+ || (uc >= 0x3190 && uc <= 0x31B7)
+ || (uc >= 0x31F0 && uc <= 0x321C)
+ || (uc >= 0x3220 && uc <= 0x3243)) {
+ return true;
+ }
+
+ if ((uc >= 0x3260 && uc <= 0x327B)
+ || (uc >= 0x327F && uc <= 0x32B0)
+ || (uc >= 0x32C0 && uc <= 0x32CB)
+ || (uc >= 0x32D0 && uc <= 0x32FE)
+ || (uc >= 0x3300 && uc <= 0x3376)
+ || (uc >= 0x337B && uc <= 0x33DD)) {
+ return true;
+ }
+ if ((uc >= 0x33E0 && uc <= 0x33FE)
+ || (uc >= 0x3400 && uc <= 0x4DB5)
+ || (uc >= 0x4E00 && uc <= 0x9FA5)
+ || (uc >= 0xA000 && uc <= 0xA48C)
+ || (uc >= 0xAC00 && uc <= 0xD7A3)
+ || (uc >= 0xD800 && uc <= 0xFA2D)
+ || (uc >= 0xFA30 && uc <= 0xFA6A)
+ || (uc >= 0xFB00 && uc <= 0xFB06)
+ || (uc >= 0xFB13 && uc <= 0xFB17)
+ || (uc >= 0xFF21 && uc <= 0xFF3A)
+ || (uc >= 0xFF41 && uc <= 0xFF5A)
+ || (uc >= 0xFF66 && uc <= 0xFFBE)
+ || (uc >= 0xFFC2 && uc <= 0xFFC7)
+ || (uc >= 0xFFCA && uc <= 0xFFCF)
+ || (uc >= 0xFFD2 && uc <= 0xFFD7)
+ || (uc >= 0xFFDA && uc <= 0xFFDC)) {
+ return true;
+ }
+
+ /* ### Add NAMEPREP support for surrogates
+ || (uc >= 0x10300 && uc <= 0x1031E)
+ || (uc >= 0x10320 && uc <= 0x10323)
+ || (uc >= 0x10330 && uc <= 0x1034A)
+ || (uc >= 0x10400 && uc <= 0x10425)
+ || (uc >= 0x10428 && uc <= 0x1044D)
+ || (uc >= 0x1D000 && uc <= 0x1D0F5)
+ || (uc >= 0x1D100 && uc <= 0x1D126)
+ || (uc >= 0x1D12A && uc <= 0x1D166)
+ || (uc >= 0x1D16A && uc <= 0x1D172)
+ || (uc >= 0x1D183 && uc <= 0x1D184)
+ || (uc >= 0x1D18C && uc <= 0x1D1A9)
+ || (uc >= 0x1D1AE && uc <= 0x1D1DD)
+ || (uc >= 0x1D400 && uc <= 0x1D454)
+ || (uc >= 0x1D456 && uc <= 0x1D49C)
+ || (uc >= 0x1D49E && uc <= 0x1D49F)
+ || uc == 0x1D4A2
+ || (uc >= 0x1D4A5 && uc <= 0x1D4A6)
+ || (uc >= 0x1D4A9 && uc <= 0x1D4AC)
+ || (uc >= 0x1D4AE && uc <= 0x1D4B9)
+ || uc == 0x1D4BB
+ || (uc >= 0x1D4BD && uc <= 0x1D4C0)
+ || (uc >= 0x1D4C2 && uc <= 0x1D4C3)
+ || (uc >= 0x1D4C5 && uc <= 0x1D505)
+ || (uc >= 0x1D507 && uc <= 0x1D50A)
+ || (uc >= 0x1D50D && uc <= 0x1D514)
+ || (uc >= 0x1D516 && uc <= 0x1D51C)
+ || (uc >= 0x1D51E && uc <= 0x1D539)
+ || (uc >= 0x1D53B && uc <= 0x1D53E)
+ || (uc >= 0x1D540 && uc <= 0x1D544)
+ || uc == 0x1D546
+ || (uc >= 0x1D54A && uc <= 0x1D550)
+ || (uc >= 0x1D552 && uc <= 0x1D6A3)
+ || (uc >= 0x1D6A8 && uc <= 0x1D7C9)
+ || (uc >= 0x20000 && uc <= 0x2A6D6)
+ || (uc >= 0x2F800 && uc <= 0x2FA1D)
+ || (uc >= 0xF0000 && uc <= 0xFFFFD)
+ || (uc >= 0x100000 && uc <= 0x10FFFD)*/
+
+ return false;
+}
+
+
+Q_AUTOTEST_EXPORT QString qt_nameprep(const QString &source)
+{
+ QString mapped = source;
+
+ bool simple = true;
+ for (int i = 0; i < mapped.size(); ++i) {
+ ushort uc = mapped.at(i).unicode();
+ if (uc > 0x80) {
+ simple = false;
+ break;
+ } else if (uc >= 'A' && uc <= 'Z') {
+ mapped[i] = QChar(uc | 0x20);
+ }
+ }
+ if (simple)
+ return mapped;
+
+ // Characters commonly mapped to nothing are simply removed
+ // (Table B.1)
+ QChar *out = mapped.data();
+ const QChar *in = out;
+ const QChar *e = in + mapped.size();
+ while (in < e) {
+ if (!isMappedToNothing(*in))
+ *out++ = *in;
+ ++in;
+ }
+ if (out != in)
+ mapped.truncate(out - mapped.constData());
+
+ // Map to lowercase (Table B.2)
+ mapToLowerCase(&mapped);
+
+ // Normalize to Unicode 3.2 form KC
+ mapped = mapped.normalized(QString::NormalizationForm_KC, QChar::Unicode_3_2);
+
+ // Strip prohibited output
+ stripProhibitedOutput(&mapped);
+
+ // Check for valid bidirectional characters
+ bool containsLCat = false;
+ bool containsRandALCat = false;
+ for (int j = 0; j < mapped.size() && (!containsLCat || !containsRandALCat); ++j) {
+ if (isBidirectionalL(mapped.at(j)))
+ containsLCat = true;
+ else if (isBidirectionalRorAL(mapped.at(j)))
+ containsRandALCat = true;
+ }
+ if (containsRandALCat) {
+ if (containsLCat || (!isBidirectionalRorAL(mapped.at(0))
+ || !isBidirectionalRorAL(mapped.at(mapped.size() - 1))))
+ mapped.clear();
+ }
+
+ return mapped;
+}
+
+
+static inline char encodeDigit(uint digit)
+{
+ return digit + 22 + 75 * (digit < 26);
+}
+
+static inline uint adapt(uint delta, uint numpoints, bool firsttime)
+{
+ delta /= (firsttime ? damp : 2);
+ delta += (delta / numpoints);
+
+ uint k = 0;
+ for (; delta > ((base - tmin) * tmax) / 2; k += base)
+ delta /= (base - tmin);
+
+ return k + (((base - tmin + 1) * delta) / (delta + skew));
+}
+
+static inline void appendEncode(QByteArray* output, uint& delta, uint& bias, uint& b, uint& h)
+{
+ uint qq;
+ uint k;
+ uint t;
+
+ // insert the variable length delta integer; fail on
+ // overflow.
+ for (qq = delta, k = base;; k += base) {
+ // stop generating digits when the threshold is
+ // detected.
+ t = (k <= bias) ? tmin : (k >= bias + tmax) ? tmax : k - bias;
+ if (qq < t) break;
+
+ *output += encodeDigit(t + (qq - t) % (base - t));
+ qq = (qq - t) / (base - t);
+ }
+
+ *output += encodeDigit(qq);
+ bias = adapt(delta, h + 1, h == b);
+ delta = 0;
+ ++h;
+}
+
+static void toPunycodeHelper(const QChar *s, int ucLength, QByteArray *output)
+{
+ uint n = initial_n;
+ uint delta = 0;
+ uint bias = initial_bias;
+
+ int outLen = output->length();
+ output->resize(outLen + ucLength);
+
+ char *d = output->data() + outLen;
+ bool skipped = false;
+ // copy all basic code points verbatim to output.
+ for (uint j = 0; j < (uint) ucLength; ++j) {
+ ushort js = s[j].unicode();
+ if (js < 0x80)
+ *d++ = js;
+ else
+ skipped = true;
+ }
+
+ // if there were only basic code points, just return them
+ // directly; don't do any encoding.
+ if (!skipped)
+ return;
+
+ output->truncate(d - output->constData());
+ int copied = output->size() - outLen;
+
+ // h and b now contain the number of basic code points in input.
+ uint b = copied;
+ uint h = copied;
+
+ // if basic code points were copied, add the delimiter character.
+ if (h > 0)
+ *output += 0x2d;
+
+ // while there are still unprocessed non-basic code points left in
+ // the input string...
+ while (h < (uint) ucLength) {
+ // find the character in the input string with the lowest
+ // unicode value.
+ uint m = Q_MAXINT;
+ uint j;
+ for (j = 0; j < (uint) ucLength; ++j) {
+ if (s[j].unicode() >= n && s[j].unicode() < m)
+ m = (uint) s[j].unicode();
+ }
+
+ // reject out-of-bounds unicode characters
+ if (m - n > (Q_MAXINT - delta) / (h + 1)) {
+ output->truncate(outLen);
+ return; // punycode_overflow
+ }
+
+ delta += (m - n) * (h + 1);
+ n = m;
+
+ // for each code point in the input string
+ for (j = 0; j < (uint) ucLength; ++j) {
+
+ // increase delta until we reach the character with the
+ // lowest unicode code. fail if delta overflows.
+ if (s[j].unicode() < n) {
+ ++delta;
+ if (!delta) {
+ output->truncate(outLen);
+ return; // punycode_overflow
+ }
+ }
+
+ // if j is the index of the character with the lowest
+ // unicode code...
+ if (s[j].unicode() == n) {
+ appendEncode(output, delta, bias, b, h);
+ }
+ }
+
+ ++delta;
+ ++n;
+ }
+
+ // prepend ACE prefix
+ output->insert(outLen, "xn--");
+ return;
+}
+
+
+static const char * const idn_whitelist[] = {
+ "ac", "at",
+ "br",
+ "cat", "ch", "cl", "cn",
+ "de", "dk",
+ "fi",
+ "gr",
+ "hu",
+ "info", "io", "is",
+ "jp",
+ "kr",
+ "li", "lt",
+ "museum",
+ "no",
+ "org",
+ "se", "sh",
+ "th", "tm", "tw",
+ "vn",
+};
+
+static QStringList *user_idn_whitelist = 0;
+
+static bool lessThan(const QChar *a, int l, const char *c)
+{
+ const ushort *uc = (const ushort *)a;
+ const ushort *e = uc + l;
+
+ if (!c || *c == 0)
+ return false;
+
+ while (*c) {
+ if (uc == e || *uc != *c)
+ break;
+ ++uc;
+ ++c;
+ }
+ return (uc == e ? *c : *uc < *c);
+}
+
+static bool equal(const QChar *a, int l, const char *b)
+{
+ while (l && a->unicode() && *b) {
+ if (*a != QLatin1Char(*b))
+ return false;
+ ++a;
+ ++b;
+ --l;
+ }
+ return l == 0;
+}
+
+static bool qt_is_idn_enabled(const QString &domain)
+{
+ int idx = domain.lastIndexOf(QLatin1Char('.'));
+ if (idx == -1)
+ return false;
+ const QChar *tld = domain.constData() + idx + 1;
+ int len = domain.size() - idx - 1;
+
+ if (user_idn_whitelist)
+ return user_idn_whitelist->contains(QString(tld, len));
+
+ int l = 0;
+ int r = sizeof(idn_whitelist)/sizeof(const char *) - 1;
+ int i = (l + r + 1) / 2;
+
+ while (r != l) {
+ if (lessThan(tld, len, idn_whitelist[i]))
+ r = i - 1;
+ else
+ l = i;
+ i = (l + r + 1) / 2;
+ }
+ return equal(tld, len, idn_whitelist[i]);
+}
+
+static QString qt_from_ACE(const QString &domainMC)
+{
+ QString domain = domainMC.toLower();
+ int idx = domain.indexOf(QLatin1Char('.'));
+ if (idx != -1) {
+ if (!domain.contains(QLatin1String("xn--"))) {
+ bool simple = true;
+ for (int i = 0; i < domain.size(); ++i) {
+ ushort ch = domain.at(i).unicode();
+ if (ch > 'z' || ch < '-' || ch == '/' || (ch > '9' && ch < 'A') || (ch > 'Z' && ch < 'a')) {
+ simple = false;
+ break;
+ }
+ }
+ if (simple)
+ return domain;
+ }
+
+ const bool isIdnEnabled = qt_is_idn_enabled(domain);
+ int lastIdx = 0;
+ QString result;
+ while (1) {
+ // Nameprep the host. If the labels in the hostname are Punycode
+ // encoded, we decode them immediately, then nameprep them.
+ QByteArray label;
+ toPunycodeHelper(domain.constData() + lastIdx, idx - lastIdx, &label);
+ result += qt_nameprep(isIdnEnabled ? QUrl::fromPunycode(label) : QString::fromLatin1(label));
+ lastIdx = idx + 1;
+ if (lastIdx < domain.size() + 1)
+ result += QLatin1Char('.');
+ else
+ break;
+ idx = domain.indexOf(QLatin1Char('.'), lastIdx);
+ if (idx == -1)
+ idx = domain.size();
+ }
+ return result;
+ } else {
+ return qt_nameprep(domain);
+ }
+}
+
+
+QUrlPrivate::QUrlPrivate()
+{
+ ref = 1;
+ port = -1;
+ isValid = false;
+ parsingMode = QUrl::TolerantMode;
+ valueDelimiter = '=';
+ pairDelimiter = '&';
+ stateFlags = 0;
+ hasFragment = false;
+ hasQuery = false;
+}
+
+QUrlPrivate::QUrlPrivate(const QUrlPrivate &copy)
+ : scheme(copy.scheme),
+ userName(copy.userName),
+ password(copy.password),
+ host(copy.host),
+ path(copy.path),
+ query(copy.query),
+ fragment(copy.fragment),
+ encodedOriginal(copy.encodedOriginal),
+ encodedUserName(copy.encodedUserName),
+ encodedPassword(copy.encodedPassword),
+ encodedPath(copy.encodedPath),
+ encodedFragment(copy.encodedFragment),
+ port(copy.port),
+ parsingMode(copy.parsingMode),
+ hasQuery(copy.hasQuery),
+ hasFragment(copy.hasFragment),
+ isValid(copy.isValid),
+ valueDelimiter(copy.valueDelimiter),
+ pairDelimiter(copy.pairDelimiter),
+ stateFlags(copy.stateFlags),
+ encodedNormalized(copy.encodedNormalized)
+{ ref = 1; }
+
+QString QUrlPrivate::canonicalHost() const
+{
+ if (QURL_HASFLAG(stateFlags, HostCanonicalized))
+ return host;
+
+ QUrlPrivate *that = const_cast<QUrlPrivate *>(this);
+ QURL_SETFLAG(that->stateFlags, HostCanonicalized);
+ that->host = qt_from_ACE(host);
+ return that->host;
+}
+
+// From RFC 3896, Appendix A Collected ABNF for URI
+// authority = [ userinfo "@" ] host [ ":" port ]
+// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+// host = IP-literal / IPv4address / reg-name
+// port = *DIGIT
+//[...]
+// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+//
+// query = *( pchar / "/" / "?" )
+//
+// fragment = *( pchar / "/" / "?" )
+//
+// pct-encoded = "%" HEXDIG HEXDIG
+//
+// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+// reserved = gen-delims / sub-delims
+// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+// / "*" / "+" / "," / ";" / "="
+
+// use defines for concatenation:
+#define ABNF_sub_delims "!$&'()*+,;="
+#define ABNF_gen_delims ":/?#[]@"
+#define ABNF_pchar ABNF_sub_delims ":@"
+#define ABNF_reserved ABNF_sub_delims ABNF_gen_delims
+
+// list the characters that don't have to be converted according to the list above.
+// "unreserved" is already automatically not encoded, so we don't have to list it.
+// the path component has a complex ABNF that basically boils down to
+// slash-separated segments of "pchar"
+
+static const char userNameExcludeChars[] = ABNF_sub_delims;
+static const char passwordExcludeChars[] = ABNF_sub_delims ":";
+static const char pathExcludeChars[] = ABNF_pchar "/";
+static const char queryExcludeChars[] = ABNF_pchar "/?";
+static const char fragmentExcludeChars[] = ABNF_pchar "/?";
+
+void QUrlPrivate::ensureEncodedParts() const
+{
+ QUrlPrivate *that = const_cast<QUrlPrivate *>(this);
+
+ if (encodedUserName.isNull())
+ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+ that->encodedUserName = toPercentEncodingHelper(userName, userNameExcludeChars);
+ if (encodedPassword.isNull())
+ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+ that->encodedPassword = toPercentEncodingHelper(password, passwordExcludeChars);
+ if (encodedPath.isNull())
+ // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" ... also "/"
+ that->encodedPath = toPercentEncodingHelper(path, pathExcludeChars);
+ if (encodedFragment.isNull())
+ // fragment = *( pchar / "/" / "?" )
+ that->encodedFragment = toPercentEncodingHelper(fragment, fragmentExcludeChars);
+}
+
+QString QUrlPrivate::authority(QUrl::FormattingOptions options) const
+{
+ if ((options & QUrl::RemoveAuthority) == QUrl::RemoveAuthority)
+ return QString();
+
+ QString tmp = userInfo(options);
+ if (!tmp.isEmpty())
+ tmp += QLatin1Char('@');
+ tmp += canonicalHost();
+ if (!(options & QUrl::RemovePort) && port != -1)
+ tmp += QLatin1Char(':') + QString::number(port);
+
+ return tmp;
+}
+
+void QUrlPrivate::setAuthority(const QString &auth)
+{
+ if (auth.isEmpty())
+ return;
+
+ // find the port section of the authority by searching from the
+ // end towards the beginning for numbers until a ':' is reached.
+ int portIndex = auth.length() - 1;
+ if (portIndex == 0) {
+ portIndex = -1;
+ } else {
+ short c = auth.at(portIndex--).unicode();
+ if (c < '0' || c > '9') {
+ portIndex = -1;
+ } else while (portIndex >= 0) {
+ c = auth.at(portIndex).unicode();
+ if (c == ':') {
+ break;
+ } else if (c == '.') {
+ portIndex = -1;
+ break;
+ }
+ --portIndex;
+ }
+ }
+
+ if (portIndex != -1) {
+ port = 0;
+ for (int i = portIndex + 1; i < auth.length(); ++i)
+ port = (port * 10) + (auth.at(i).unicode() - '0');
+ } else {
+ port = -1;
+ }
+
+ int userInfoIndex = auth.indexOf(QLatin1Char('@'));
+ if (userInfoIndex != -1 && (portIndex == -1 || userInfoIndex < portIndex))
+ setUserInfo(auth.left(userInfoIndex));
+
+ int hostIndex = 0;
+ if (userInfoIndex != -1)
+ hostIndex = userInfoIndex + 1;
+ int hostLength = auth.length() - hostIndex;
+ if (portIndex != -1)
+ hostLength -= (auth.length() - portIndex);
+
+ host = auth.mid(hostIndex, hostLength).trimmed();
+}
+
+void QUrlPrivate::setUserInfo(const QString &userInfo)
+{
+ encodedUserName.clear();
+ encodedPassword.clear();
+
+ int delimIndex = userInfo.indexOf(QLatin1Char(':'));
+ if (delimIndex == -1) {
+ userName = userInfo;
+ password.clear();
+ return;
+ }
+ userName = userInfo.left(delimIndex);
+ password = userInfo.right(userInfo.length() - delimIndex - 1);
+}
+
+void QUrlPrivate::setEncodedUserInfo(const QUrlParseData *parseData)
+{
+ userName.clear();
+ password.clear();
+ if (!parseData->userInfoLength) {
+ encodedUserName.clear();
+ encodedPassword.clear();
+ } else if (parseData->userInfoDelimIndex == -1) {
+ encodedUserName = QByteArray(parseData->userInfo, parseData->userInfoLength);
+ encodedPassword.clear();
+ } else {
+ encodedUserName = QByteArray(parseData->userInfo, parseData->userInfoDelimIndex);
+ encodedPassword = QByteArray(parseData->userInfo + parseData->userInfoDelimIndex + 1,
+ parseData->userInfoLength - parseData->userInfoDelimIndex - 1);
+ }
+}
+
+QString QUrlPrivate::userInfo(QUrl::FormattingOptions options) const
+{
+ if ((options & QUrl::RemoveUserInfo) == QUrl::RemoveUserInfo)
+ return QString();
+
+ QUrlPrivate *that = const_cast<QUrlPrivate *>(this);
+ if (userName.isNull())
+ that->userName = fromPercentEncodingHelper(encodedUserName);
+ if (password.isNull())
+ that->password = fromPercentEncodingHelper(encodedPassword);
+
+ QString tmp = userName;
+
+ if (!(options & QUrl::RemovePassword) && !password.isEmpty()) {
+ tmp += QLatin1Char(':');
+ tmp += password;
+ }
+
+ return tmp;
+}
+
+/*
+ From http://www.ietf.org/rfc/rfc3986.txt, 5.2.3: Merge paths
+
+ Returns a merge of the current path with the relative path passed
+ as argument.
+*/
+QByteArray QUrlPrivate::mergePaths(const QByteArray &relativePath) const
+{
+ if (encodedPath.isNull())
+ ensureEncodedParts();
+
+ // If the base URI has a defined authority component and an empty
+ // path, then return a string consisting of "/" concatenated with
+ // the reference's path; otherwise,
+ if (!authority().isEmpty() && encodedPath.isEmpty())
+ return '/' + relativePath;
+
+ // Return a string consisting of the reference's path component
+ // appended to all but the last segment of the base URI's path
+ // (i.e., excluding any characters after the right-most "/" in the
+ // base URI path, or excluding the entire base URI path if it does
+ // not contain any "/" characters).
+ QByteArray newPath;
+ if (!encodedPath.contains('/'))
+ newPath = relativePath;
+ else
+ newPath = encodedPath.left(encodedPath.lastIndexOf('/') + 1) + relativePath;
+
+ return newPath;
+}
+
+void QUrlPrivate::queryItem(int pos, int *value, int *end)
+{
+ *end = query.indexOf(pairDelimiter, pos);
+ if (*end == -1)
+ *end = query.size();
+ *value = pos;
+ while (*value < *end) {
+ if (query[*value] == valueDelimiter)
+ break;
+ ++*value;
+ }
+}
+
+/*
+ From http://www.ietf.org/rfc/rfc3986.txt, 5.2.4: Remove dot segments
+
+ Removes unnecessary ../ and ./ from the path. Used for normalizing
+ the URL.
+*/
+static void removeDotsFromPath(QByteArray *path)
+{
+ // The input buffer is initialized with the now-appended path
+ // components and the output buffer is initialized to the empty
+ // string.
+ char *out = path->data();
+ const char *in = out;
+ const char *end = out + path->size();
+
+ // If the input buffer consists only of
+ // "." or "..", then remove that from the input
+ // buffer;
+ if (path->size() == 1 && in[0] == '.')
+ ++in;
+ else if (path->size() == 2 && in[0] == '.' && in[1] == '.')
+ in += 2;
+ // While the input buffer is not empty, loop:
+ while (in < end) {
+
+ // otherwise, if the input buffer begins with a prefix of "../" or "./",
+ // then remove that prefix from the input buffer;
+ if (path->size() >= 2 && in[0] == '.' && in[1] == '/')
+ in += 2;
+ else if (path->size() >= 3 && in[0] == '.' && in[1] == '.' && in[2] == '/')
+ in += 3;
+
+ // otherwise, if the input buffer begins with a prefix of
+ // "/./" or "/.", where "." is a complete path segment,
+ // then replace that prefix with "/" in the input buffer;
+ if (in <= end - 3 && in[0] == '/' && in[1] == '.' && in[2] == '/') {
+ in += 2;
+ continue;
+ } else if (in == end - 2 && in[0] == '/' && in[1] == '.') {
+ *out++ = '/';
+ in += 2;
+ break;
+ }
+
+ // otherwise, if the input buffer begins with a prefix
+ // of "/../" or "/..", where ".." is a complete path
+ // segment, then replace that prefix with "/" in the
+ // input buffer and remove the last //segment and its
+ // preceding "/" (if any) from the output buffer;
+ if (in <= end - 4 && in[0] == '/' && in[1] == '.' && in[2] == '.' && in[3] == '/') {
+ while (out > path->constData() && *(--out) != '/')
+ ;
+ if (out == path->constData() && *out != '/')
+ ++in;
+ in += 3;
+ continue;
+ } else if (in == end - 3 && in[0] == '/' && in[1] == '.' && in[2] == '.') {
+ while (out > path->constData() && *(--out) != '/')
+ ;
+ if (*out == '/')
+ ++out;
+ in += 3;
+ break;
+ }
+
+ // otherwise move the first path segment in
+ // the input buffer to the end of the output
+ // buffer, including the initial "/" character
+ // (if any) and any subsequent characters up
+ // to, but not including, the next "/"
+ // character or the end of the input buffer.
+ *out++ = *in++;
+ while (in < end && *in != '/')
+ *out++ = *in++;
+ }
+ path->truncate(out - path->constData());
+}
+
+void QUrlPrivate::validate() const
+{
+ QUrlPrivate *that = (QUrlPrivate *)this;
+ that->encodedOriginal = that->toEncoded(); // may detach
+ parse(ParseOnly);
+
+ QURL_SETFLAG(that->stateFlags, Validated);
+
+ if (!isValid)
+ return;
+
+ QString auth = authority(); // causes the non-encoded forms to be valid
+
+ if (scheme == QLatin1String("mailto")) {
+ if (!host.isEmpty() || port != -1 || !userName.isEmpty() || !password.isEmpty()) {
+ that->isValid = false;
+ that->errorInfo.setParams(0, QT_TRANSLATE_NOOP(QUrl, "expected empty host, username,"
+ "port and password"),
+ 0, 0);
+ }
+ } else if (scheme == QLatin1String("ftp") || scheme == QLatin1String("http")) {
+ if (host.isEmpty() && !(path.isEmpty() && encodedPath.isEmpty())) {
+ that->isValid = false;
+ that->errorInfo.setParams(0, QT_TRANSLATE_NOOP(QUrl, "the host is empty, but not the path"),
+ 0, 0);
+ }
+ }
+}
+
+void QUrlPrivate::parse(ParseOptions parseOptions) const
+{
+ QUrlPrivate *that = (QUrlPrivate *)this;
+ that->errorInfo.setParams(0, 0, 0, 0);
+ if (encodedOriginal.isEmpty()) {
+ that->isValid = false;
+ that->errorInfo.setParams(0, QT_TRANSLATE_NOOP(QUrl, "empty"),
+ 0, 0);
+ QURL_SETFLAG(that->stateFlags, Validated | Parsed);
+ return;
+ }
+
+
+ QUrlParseData parseData;
+ memset(&parseData, 0, sizeof(parseData));
+ parseData.userInfoDelimIndex = -1;
+ parseData.port = -1;
+
+ const char *pptr = (char *) encodedOriginal.constData();
+ const char **ptr = &pptr;
+
+#if defined (QURL_DEBUG)
+ qDebug("QUrlPrivate::parse(), parsing \"%s\"", pptr);
+#endif
+
+ // optional scheme
+ _scheme(ptr, &parseData);
+
+ // hierpart
+ _hierPart(ptr, &parseData);
+
+ // optional query
+ char ch = *((*ptr)++);
+ if (ch == '?') {
+ that->hasQuery = true;
+ _query(ptr, &parseData);
+ ch = *((*ptr)++);
+ }
+
+ // optional fragment
+ if (ch == '#') {
+ that->hasFragment = true;
+ _fragment(ptr, &parseData);
+ } else if (ch != '\0') {
+ that->isValid = false;
+ that->errorInfo.setParams(*ptr, QT_TRANSLATE_NOOP(QUrl, "expected end of URL"),
+ 0, ch);
+ QURL_SETFLAG(that->stateFlags, Validated | Parsed);
+#if defined (QURL_DEBUG)
+ qDebug("QUrlPrivate::parse(), unrecognized: %c%s", ch, *ptr);
+#endif
+ return;
+ }
+
+ // when doing lazy validation, this function is called after
+ // encodedOriginal has been constructed from the individual parts,
+ // only to see if the constructed URL can be parsed. in that case,
+ // parse() is called in ParseOnly mode; we don't want to set all
+ // the members over again.
+ if (parseOptions == ParseAndSet) {
+ QURL_UNSETFLAG(that->stateFlags, HostCanonicalized);
+
+ if (parseData.scheme) {
+ QByteArray s(parseData.scheme, parseData.schemeLength);
+ that->scheme = fromPercentEncodingMutable(&s);
+ }
+
+ that->setEncodedUserInfo(&parseData);
+
+ QByteArray h(parseData.host, parseData.hostLength);
+ that->host = fromPercentEncodingMutable(&h);
+ that->port = parseData.port;
+
+ that->path.clear();
+ that->encodedPath = QByteArray(parseData.path, parseData.pathLength);
+
+ if (that->hasQuery)
+ that->query = QByteArray(parseData.query, parseData.queryLength);
+ else
+ that->query.clear();
+
+ that->fragment.clear();
+ if (that->hasFragment) {
+ that->encodedFragment = QByteArray(parseData.fragment, parseData.fragmentLength);
+ } else {
+ that->encodedFragment.clear();
+ }
+ }
+
+ that->isValid = true;
+ QURL_SETFLAG(that->stateFlags, Parsed);
+
+#if defined (QURL_DEBUG)
+ qDebug("QUrl::setUrl(), scheme = %s", that->scheme.toLatin1().constData());
+ qDebug("QUrl::setUrl(), userInfo = %s", that->userInfo.toLatin1().constData());
+ qDebug("QUrl::setUrl(), host = %s", that->host.toLatin1().constData());
+ qDebug("QUrl::setUrl(), port = %i", that->port);
+ qDebug("QUrl::setUrl(), path = %s", fromPercentEncodingHelper(__path).toLatin1().constData());
+ qDebug("QUrl::setUrl(), query = %s", __query.constData());
+ qDebug("QUrl::setUrl(), fragment = %s", fromPercentEncodingHelper(__fragment).toLatin1().constData());
+#endif
+}
+
+void QUrlPrivate::clear()
+{
+ scheme.clear();
+ userName.clear();
+ password.clear();
+ host.clear();
+ port = -1;
+ path.clear();
+ query.clear();
+ fragment.clear();
+
+ encodedOriginal.clear();
+ encodedUserName.clear();
+ encodedPassword.clear();
+ encodedPath.clear();
+ encodedFragment.clear();
+ encodedNormalized.clear();
+
+ isValid = false;
+ hasQuery = false;
+ hasFragment = false;
+
+ valueDelimiter = '=';
+ pairDelimiter = '&';
+
+ QURL_UNSETFLAG(stateFlags, Parsed | Validated | Normalized | HostCanonicalized);
+}
+
+QByteArray QUrlPrivate::toEncoded(QUrl::FormattingOptions options) const
+{
+ if (!QURL_HASFLAG(stateFlags, Parsed)) parse();
+ else ensureEncodedParts();
+
+ QByteArray url;
+
+ if (!(options & QUrl::RemoveScheme) && !scheme.isEmpty()) {
+ url += scheme.toLatin1();
+ url += ':';
+ }
+ QString auth = authority();
+ bool doFileScheme = scheme == QLatin1String("file") && encodedPath.startsWith('/');
+ if ((options & QUrl::RemoveAuthority) != QUrl::RemoveAuthority && (!auth.isEmpty() || doFileScheme)) {
+ if (doFileScheme && !encodedPath.startsWith('/'))
+ url += '/';
+ url += "//";
+
+ if ((options & QUrl::RemoveUserInfo) != QUrl::RemoveUserInfo) {
+ if (!userName.isEmpty()) {
+ url += encodedUserName;
+ if (!(options & QUrl::RemovePassword) && !password.isEmpty()) {
+ url += ':';
+ url += encodedPassword;
+ }
+ url += '@';
+ }
+ }
+
+ url += QUrl::toAce(host);
+ if (!(options & QUrl::RemovePort) && port != -1) {
+ url += ':';
+ url += QString::number(port).toAscii();
+ }
+ }
+
+ if (!(options & QUrl::RemovePath)) {
+ // check if we need to insert a slash
+ if (!encodedPath.isEmpty() && !auth.isEmpty()) {
+ if (!encodedPath.startsWith('/'))
+ url += '/';
+ }
+ url += encodedPath;
+
+ // check if we need to remove trailing slashes
+ while ((options & QUrl::StripTrailingSlash) && url.endsWith('/'))
+ url.chop(1);
+ }
+
+ if (!(options & QUrl::RemoveQuery) && hasQuery) {
+ url += '?';
+ url += query;
+ }
+ if (!(options & QUrl::RemoveFragment) && hasFragment) {
+ url += '#';
+ url += encodedFragment;
+ }
+
+ return url;
+}
+
+#define qToLower(ch) (((ch|32) >= 'a' && (ch|32) <= 'z') ? (ch|32) : ch)
+
+const QByteArray &QUrlPrivate::normalized()
+{
+ if (QURL_HASFLAG(stateFlags, QUrlPrivate::Normalized))
+ return encodedNormalized;
+
+ QURL_SETFLAG(stateFlags, QUrlPrivate::Normalized);
+
+ QUrlPrivate tmp = *this;
+ tmp.scheme = tmp.scheme.toLower();
+ tmp.host = tmp.canonicalHost();
+
+ // ensure the encoded and normalized parts of the URL
+ tmp.ensureEncodedParts();
+ if (tmp.encodedUserName.contains('%'))
+ q_normalizePercentEncoding(&tmp.encodedUserName, userNameExcludeChars);
+ if (tmp.encodedPassword.contains('%'))
+ q_normalizePercentEncoding(&tmp.encodedPassword, passwordExcludeChars);
+ if (tmp.encodedFragment.contains('%'))
+ q_normalizePercentEncoding(&tmp.encodedFragment, fragmentExcludeChars);
+
+ if (tmp.encodedPath.contains('%')) {
+ // the path is a bit special:
+ // the slashes shouldn't be encoded or decoded.
+ // They should remain exactly like they are right now
+ //
+ // treat the path as a slash-separated sequence of pchar
+ QByteArray result;
+ result.reserve(tmp.encodedPath.length());
+ if (tmp.encodedPath.startsWith('/'))
+ result.append('/');
+
+ const char *data = tmp.encodedPath.constData();
+ int lastSlash = 0;
+ int nextSlash;
+ do {
+ ++lastSlash;
+ nextSlash = tmp.encodedPath.indexOf('/', lastSlash);
+ int len;
+ if (nextSlash == -1)
+ len = tmp.encodedPath.length() - lastSlash;
+ else
+ len = nextSlash - lastSlash;
+
+ if (memchr(data + lastSlash, '%', len)) {
+ // there's at least one percent before the next slash
+ QByteArray block = QByteArray(data + lastSlash, len);
+ q_normalizePercentEncoding(&block, pathExcludeChars);
+ result.append(block);
+ } else {
+ // no percents in this path segment, append wholesale
+ result.append(data + lastSlash, len);
+ }
+
+ // append the slash too, if it's there
+ if (nextSlash != -1)
+ result.append('/');
+
+ lastSlash = nextSlash;
+ } while (lastSlash != -1);
+
+ tmp.encodedPath = result;
+ }
+
+ if (!tmp.scheme.isEmpty()) // relative test
+ removeDotsFromPath(&tmp.encodedPath);
+
+ int qLen = tmp.query.length();
+ for (int i = 0; i < qLen; i++) {
+ if (qLen - i > 2 && tmp.query.at(i) == '%') {
+ ++i;
+ tmp.query[i] = qToLower(tmp.query.at(i));
+ ++i;
+ tmp.query[i] = qToLower(tmp.query.at(i));
+ }
+ }
+ encodedNormalized = tmp.toEncoded();
+
+ return encodedNormalized;
+}
+
+QString QUrlPrivate::createErrorString()
+{
+ if (isValid)
+ return QString();
+
+ QString errorString(QLatin1String(QT_TRANSLATE_NOOP(QUrl, "Invalid URL \"")));
+ errorString += QLatin1String(encodedOriginal.constData());
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\""));
+
+ if (errorInfo._source) {
+ int position = encodedOriginal.indexOf(errorInfo._source) - 1;
+ if (position > 0) {
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": error at position "));
+ errorString += QString::number(position);
+ } else {
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": "));
+ errorString += QLatin1String(errorInfo._source);
+ }
+ }
+
+ if (errorInfo._expected) {
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": expected \'"));
+ errorString += QLatin1Char(errorInfo._expected);
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\'"));
+ } else {
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": "));
+ errorString += QLatin1String(errorInfo._message);
+ }
+ if (errorInfo._found) {
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ", but found \'"));
+ errorString += QLatin1Char(errorInfo._found);
+ errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\'"));
+ }
+ return errorString;
+}
+
+/*!
+ Constructs a URL by parsing \a url. \a url is assumed to be in human
+ readable representation, with no percent encoding. QUrl will automatically
+ percent encode all characters that are not allowed in a URL.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 0
+
+ To construct a URL from an encoded string, call fromEncoded():
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 1
+
+ \sa setUrl(), setEncodedUrl(), fromEncoded(), TolerantMode
+*/
+QUrl::QUrl(const QString &url) : d(new QUrlPrivate)
+{
+ if (!url.isEmpty())
+ setUrl(url);
+}
+
+/*!
+ \overload
+
+ Parses the \a url using the parser mode \a parsingMode.
+
+ \sa setUrl()
+*/
+QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(new QUrlPrivate)
+{
+ if (!url.isEmpty())
+ setUrl(url, parsingMode);
+ else
+ d->parsingMode = parsingMode;
+}
+
+/*!
+ Constructs an empty QUrl object.
+*/
+QUrl::QUrl() : d(new QUrlPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QUrl::QUrl(const QUrl &other) : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ Destructor; called immediately before the object is deleted.
+*/
+QUrl::~QUrl()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Returns true if the URL is valid; otherwise returns false.
+
+ The URL is run through a conformance test. Every part of the URL
+ must conform to the standard encoding rules of the URI standard
+ for the URL to be reported as valid.
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 2
+*/
+bool QUrl::isValid() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Validated)) d->validate();
+
+ return d->isValid;
+}
+
+/*!
+ Returns true if the URL has no data; otherwise returns false.
+*/
+bool QUrl::isEmpty() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed))
+ return d->encodedOriginal.isEmpty();
+ else
+ return d->scheme.isEmpty() // no encodedScheme
+ && d->userName.isEmpty() && d->encodedUserName.isEmpty()
+ && d->password.isEmpty() && d->encodedPassword.isEmpty()
+ && d->host.isEmpty() // no encodedHost
+ && d->port == -1
+ && d->path.isEmpty() && d->encodedPath.isEmpty()
+ && d->query.isEmpty()
+ && d->fragment.isEmpty() && d->encodedFragment.isEmpty();
+}
+
+/*!
+ Resets the content of the QUrl. After calling this function, the
+ QUrl is equal to one that has been constructed with the default
+ empty constructor.
+*/
+void QUrl::clear()
+{
+ detach();
+ d->clear();
+}
+
+/*!
+ Constructs a URL by parsing the contents of \a url.
+
+ \a url is assumed to be in unicode format, with no percent
+ encoding.
+
+ Calling isValid() will tell whether or not a valid URL was
+ constructed.
+
+ \sa setEncodedUrl()
+*/
+void QUrl::setUrl(const QString &url)
+{
+ setUrl(url, TolerantMode);
+}
+
+/*!
+ \overload
+
+ Parses \a url using the parsing mode \a parsingMode.
+
+ \sa setEncodedUrl()
+*/
+void QUrl::setUrl(const QString &url, ParsingMode parsingMode)
+{
+ // escape all reserved characters and delimiters
+ // reserved = gen-delims / sub-delims
+ if (parsingMode != TolerantMode) {
+ setEncodedUrl(toPercentEncodingHelper(url, ABNF_reserved), parsingMode);
+ return;
+ }
+
+ // Tolerant preprocessing
+ QString tmp = url;
+
+ // Allow %20 in the QString variant
+ tmp.replace(QLatin1String("%20"), QLatin1String(" "));
+
+ // Percent-encode unsafe ASCII characters after host part
+ int start = tmp.indexOf(QLatin1String("//"));
+ if (start != -1) {
+ // Has host part, find delimiter
+ start += 2; // skip "//"
+ const char delims[] = "/#?";
+ const char *d = delims;
+ int hostEnd = -1;
+ while (*d && (hostEnd = tmp.indexOf(QLatin1Char(*d), start)) == -1)
+ ++d;
+ start = (hostEnd == -1) ? -1 : hostEnd + 1;
+ } else {
+ start = 0; // Has no host part
+ }
+ QByteArray encodedUrl;
+ if (start != -1) {
+ QString hostPart = tmp.left(start);
+ QString otherPart = tmp.mid(start);
+ encodedUrl = toPercentEncodingHelper(hostPart, ":/?#[]@!$&'()*+,;=")
+ + toPercentEncodingHelper(otherPart, ":/?#@!$&'()*+,;=");
+ } else {
+ encodedUrl = toPercentEncodingHelper(tmp, ABNF_reserved);
+ }
+ setEncodedUrl(encodedUrl, StrictMode);
+}
+
+/*!
+ Constructs a URL by parsing the contents of \a encodedUrl.
+
+ \a encodedUrl is assumed to be a URL string in percent encoded
+ form, containing only ASCII characters.
+
+ Use isValid() to determine if a valid URL was constructed.
+
+ \sa setUrl()
+*/
+void QUrl::setEncodedUrl(const QByteArray &encodedUrl)
+{
+ setEncodedUrl(encodedUrl, TolerantMode);
+}
+
+inline static bool isHex(char c)
+{
+ c |= 0x20;
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
+}
+
+static inline char toHex(quint8 c)
+{
+ return c > 9 ? c - 10 + 'A' : c + '0';
+}
+
+/*!
+ Constructs a URL by parsing the contents of \a encodedUrl using
+ the given \a parsingMode.
+*/
+void QUrl::setEncodedUrl(const QByteArray &encodedUrl, ParsingMode parsingMode)
+{
+ clear();
+ QByteArray tmp = encodedUrl;
+ if ((d->parsingMode = parsingMode) == TolerantMode) {
+ // Replace stray % with %25
+ QByteArray copy = tmp;
+ for (int i = 0, j = 0; i < copy.size(); ++i, ++j) {
+ if (copy.at(i) == '%') {
+ if (i + 2 >= copy.size() || !isHex(copy.at(i + 1)) || !isHex(copy.at(i + 2))) {
+ tmp.replace(j, 1, "%25");
+ j += 2;
+ }
+ }
+ }
+
+ // Find the host part
+ int hostStart = tmp.indexOf("//");
+ int hostEnd = -1;
+ if (hostStart != -1) {
+ // Has host part, find delimiter
+ hostStart += 2; // skip "//"
+ hostEnd = tmp.indexOf('/', hostStart);
+ if (hostEnd == -1)
+ hostEnd = tmp.indexOf('#', hostStart);
+ if (hostEnd == -1)
+ hostEnd = tmp.indexOf('?');
+ if (hostEnd == -1)
+ hostEnd = tmp.length() - 1;
+ }
+
+ // Reserved and unreserved characters are fine
+// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+// reserved = gen-delims / sub-delims
+// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+// / "*" / "+" / "," / ";" / "="
+ // Replace everything else with percent encoding
+ static const char doEncode[] = " \"<>[\\]^`{|}";
+ static const char doEncodeHost[] = " \"<>\\^`{|}";
+ for (int i = 0; i < tmp.size(); ++i) {
+ quint8 c = quint8(tmp.at(i));
+ if (c < 32 || c > 127 ||
+ strchr(hostStart <= i && i <= hostEnd ? doEncodeHost : doEncode, c)) {
+ char buf[4];
+ buf[0] = '%';
+ buf[1] = toHex(c >> 4);
+ buf[2] = toHex(c & 0xf);
+ buf[3] = '\0';
+ tmp.replace(i, 1, buf);
+ i += 2;
+ }
+ }
+ }
+
+ d->encodedOriginal = tmp;
+}
+
+/*!
+ Sets the scheme of the URL to \a scheme. As a scheme can only
+ contain ASCII characters, no conversion or encoding is done on the
+ input.
+
+ The scheme describes the type (or protocol) of the URL. It's
+ represented by one or more ASCII characters at the start the URL,
+ and is followed by a ':'. The following example shows a URL where
+ the scheme is "ftp":
+
+ \img qurl-authority2.png
+
+ The scheme can also be empty, in which case the URL is interpreted
+ as relative.
+
+ \sa scheme(), isRelative()
+*/
+void QUrl::setScheme(const QString &scheme)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->scheme = scheme;
+}
+
+/*!
+ Returns the scheme of the URL. If an empty string is returned,
+ this means the scheme is undefined and the URL is then relative.
+
+ \sa setScheme(), isRelative()
+*/
+QString QUrl::scheme() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->scheme;
+}
+
+/*!
+ Sets the authority of the URL to \a authority.
+
+ The authority of a URL is the combination of user info, a host
+ name and a port. All of these elements are optional; an empty
+ authority is therefore valid.
+
+ The user info and host are separated by a '@', and the host and
+ port are separated by a ':'. If the user info is empty, the '@'
+ must be omitted; although a stray ':' is permitted if the port is
+ empty.
+
+ The following example shows a valid authority string:
+
+ \img qurl-authority.png
+*/
+void QUrl::setAuthority(const QString &authority)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->setAuthority(authority);
+}
+
+/*!
+ Returns the authority of the URL if it is defined; otherwise
+ an empty string is returned.
+
+ \sa setAuthority()
+*/
+QString QUrl::authority() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->authority();
+}
+
+/*!
+ Sets the user info of the URL to \a userInfo. The user info is an
+ optional part of the authority of the URL, as described in
+ setAuthority().
+
+ The user info consists of a user name and optionally a password,
+ separated by a ':'. If the password is empty, the colon must be
+ omitted. The following example shows a valid user info string:
+
+ \img qurl-authority3.png
+
+ \sa userInfo(), setUserName(), setPassword(), setAuthority()
+*/
+void QUrl::setUserInfo(const QString &userInfo)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->setUserInfo(userInfo.trimmed());
+}
+
+/*!
+ Returns the user info of the URL, or an empty string if the user
+ info is undefined.
+*/
+QString QUrl::userInfo() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->userInfo();
+}
+
+/*!
+ Sets the URL's user name to \a userName. The \a userName is part
+ of the user info element in the authority of the URL, as described
+ in setUserInfo().
+
+ \sa setEncodedUserName(), userName(), setUserInfo()
+*/
+void QUrl::setUserName(const QString &userName)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->userName = userName;
+ d->encodedUserName.clear();
+}
+
+/*!
+ Returns the user name of the URL if it is defined; otherwise
+ an empty string is returned.
+
+ \sa setUserName(), encodedUserName()
+*/
+QString QUrl::userName() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->userInfo(); // causes the unencoded form to be set
+ return d->userName;
+}
+
+/*!
+ \since 4.4
+
+ Sets the URL's user name to the percent-encoded \a userName. The \a
+ userName is part of the user info element in the authority of the
+ URL, as described in setUserInfo().
+
+ Note: this function does not verify that \a userName is properly
+ encoded. It is the caller's responsibility to ensure that the any
+ delimiters (such as colons or slashes) are properly encoded.
+
+ \sa setUserName(), encodedUserName(), setUserInfo()
+*/
+void QUrl::setEncodedUserName(const QByteArray &userName)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->encodedUserName = userName;
+ d->userName.clear();
+}
+
+/*!
+ \since 4.4
+
+ Returns the user name of the URL if it is defined; otherwise
+ an empty string is returned. The returned value will have its
+ non-ASCII and other control characters percent-encoded, as in
+ toEncoded().
+
+ \sa setEncodedUserName()
+*/
+QByteArray QUrl::encodedUserName() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->ensureEncodedParts();
+ return d->encodedUserName;
+}
+
+/*!
+ Sets the URL's password to \a password. The \a password is part of
+ the user info element in the authority of the URL, as described in
+ setUserInfo().
+
+ \sa password(), setUserInfo()
+*/
+void QUrl::setPassword(const QString &password)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->password = password;
+ d->encodedPassword.clear();
+}
+
+/*!
+ Returns the password of the URL if it is defined; otherwise
+ an empty string is returned.
+
+ \sa setPassword()
+*/
+QString QUrl::password() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->userInfo(); // causes the unencoded form to be set
+ return d->password;
+}
+
+/*!
+ \since 4.4
+
+ Sets the URL's password to the percent-encoded \a password. The \a
+ password is part of the user info element in the authority of the
+ URL, as described in setUserInfo().
+
+ Note: this function does not verify that \a password is properly
+ encoded. It is the caller's responsibility to ensure that the any
+ delimiters (such as colons or slashes) are properly encoded.
+
+ \sa setPassword(), encodedPassword(), setUserInfo()
+*/
+void QUrl::setEncodedPassword(const QByteArray &password)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->encodedPassword = password;
+ d->password.clear();
+}
+
+/*!
+ \since 4.4
+
+ Returns the password of the URL if it is defined; otherwise an
+ empty string is returned. The returned value will have its
+ non-ASCII and other control characters percent-encoded, as in
+ toEncoded().
+
+ \sa setEncodedPassword(), toEncoded()
+*/
+QByteArray QUrl::encodedPassword() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->ensureEncodedParts();
+ return d->encodedPassword;
+}
+
+/*!
+ Sets the host of the URL to \a host. The host is part of the
+ authority.
+
+ \sa host(), setAuthority()
+*/
+void QUrl::setHost(const QString &host)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized | QUrlPrivate::HostCanonicalized);
+
+ d->host = host;
+ if (d->host.contains(QLatin1Char(':')))
+ d->host = QLatin1Char('[') + d->host + QLatin1Char(']');
+}
+
+/*!
+ Returns the host of the URL if it is defined; otherwise
+ an empty string is returned.
+*/
+QString QUrl::host() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ if (d->host.isEmpty() || d->host.at(0) != QLatin1Char('['))
+ return d->canonicalHost();
+ QString tmp = d->host.mid(1);
+ tmp.truncate(tmp.length() - 1);
+ return tmp;
+}
+
+/*!
+ \since 4.4
+
+ Sets the URL's host to the ACE- or percent-encoded \a host. The \a
+ host is part of the user info element in the authority of the
+ URL, as described in setAuthority().
+
+ \sa setHost(), encodedHost(), setAuthority(), fromAce()
+*/
+void QUrl::setEncodedHost(const QByteArray &host)
+{
+ setHost(fromPercentEncodingHelper(host));
+}
+
+/*!
+ \since 4.4
+
+ Returns the host part of the URL if it is defined; otherwise
+ an empty string is returned.
+
+ Note: encodedHost() does not return percent-encoded hostnames. Instead,
+ the ACE-encoded (bare ASCII in Punycode encoding) form will be
+ returned for any non-ASCII hostname.
+
+ This function is equivalent to calling QUrl::toAce() on the return
+ value of host().
+
+ \sa setEncodedHost()
+*/
+QByteArray QUrl::encodedHost() const
+{
+ // should we cache this in d->encodedHost?
+ return QUrl::toAce(host());
+}
+
+/*!
+ Sets the port of the URL to \a port. The port is part of the
+ authority of the URL, as described in setAuthority().
+
+ \a port must be between 0 and 65535 inclusive. Setting the
+ port to -1 indicates that the port is unspecified.
+*/
+void QUrl::setPort(int port)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ if (port < -1 || port > 65535) {
+ qWarning("QUrl::setPort: Out of range");
+ port = -1;
+ }
+
+ d->port = port;
+}
+
+/*!
+ Returns the port of the URL, or -1 if the port is unspecified.
+*/
+int QUrl::port() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Validated)) d->validate();
+ return d->port;
+}
+
+/*!
+ \overload
+ \since 4.1
+
+ Returns the port of the URL, or \a defaultPort if the port is
+ unspecified.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 3
+*/
+int QUrl::port(int defaultPort) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ return d->port == -1 ? defaultPort : d->port;
+}
+
+/*!
+ Sets the path of the URL to \a path. The path is the part of the
+ URL that comes after the authority but before the query string.
+
+ \img qurl-ftppath.png
+
+ For non-hierarchical schemes, the path will be everything
+ following the scheme declaration, as in the following example:
+
+ \img qurl-mailtopath.png
+
+ \sa path()
+*/
+void QUrl::setPath(const QString &path)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->path = path;
+ d->encodedPath.clear();
+}
+
+/*!
+ Returns the path of the URL.
+
+ \sa setPath()
+*/
+QString QUrl::path() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ if (d->path.isNull()) {
+ QUrlPrivate *that = const_cast<QUrlPrivate *>(d);
+ that->path = fromPercentEncodingHelper(d->encodedPath);
+ }
+ return d->path;
+}
+
+/*!
+ \since 4.4
+
+ Sets the URL's path to the percent-encoded \a path. The path is
+ the part of the URL that comes after the authority but before the
+ query string.
+
+ \img qurl-ftppath.png
+
+ For non-hierarchical schemes, the path will be everything
+ following the scheme declaration, as in the following example:
+
+ \img qurl-mailtopath.png
+
+ Note: this function does not verify that \a path is properly
+ encoded. It is the caller's responsibility to ensure that the any
+ delimiters (such as '?' and '#') are properly encoded.
+
+ \sa setPath(), encodedPath(), setUserInfo()
+*/
+void QUrl::setEncodedPath(const QByteArray &path)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->encodedPath = path;
+ d->path.clear();
+}
+
+/*!
+ \since 4.4
+
+ Returns the path of the URL if it is defined; otherwise an
+ empty string is returned. The returned value will have its
+ non-ASCII and other control characters percent-encoded, as in
+ toEncoded().
+
+ \sa setEncodedPath(), toEncoded()
+*/
+QByteArray QUrl::encodedPath() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->ensureEncodedParts();
+ return d->encodedPath;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this URL contains a Query (i.e., if ? was seen on it).
+
+ \sa hasQueryItem(), encodedQuery()
+*/
+bool QUrl::hasQuery() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->hasQuery;
+}
+
+/*!
+ Sets the characters used for delimiting between keys and values,
+ and between key-value pairs in the URL's query string. The default
+ value delimiter is '=' and the default pair delimiter is '&'.
+
+ \img qurl-querystring.png
+
+ \a valueDelimiter will be used for separating keys from values,
+ and \a pairDelimiter will be used to separate key-value pairs.
+ Any occurrences of these delimiting characters in the encoded
+ representation of the keys and values of the query string are
+ percent encoded.
+
+ If \a valueDelimiter is set to '-' and \a pairDelimiter is '/',
+ the above query string would instead be represented like this:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 4
+
+ Calling this function does not change the delimiters of the
+ current query string. It only affects queryItems(),
+ setQueryItems() and addQueryItems().
+*/
+void QUrl::setQueryDelimiters(char valueDelimiter, char pairDelimiter)
+{
+ detach();
+
+ d->valueDelimiter = valueDelimiter;
+ d->pairDelimiter = pairDelimiter;
+}
+
+/*!
+ Returns the character used to delimit between key-value pairs in
+ the query string of the URL.
+*/
+char QUrl::queryPairDelimiter() const
+{
+ return d->pairDelimiter;
+}
+
+/*!
+ Returns the character used to delimit between keys and values in
+ the query string of the URL.
+*/
+char QUrl::queryValueDelimiter() const
+{
+ return d->valueDelimiter;
+}
+
+/*!
+ Sets the query string of the URL to \a query. The string is
+ inserted as-is, and no further encoding is performed when calling
+ toEncoded().
+
+ This function is useful if you need to pass a query string that
+ does not fit into the key-value pattern, or that uses a different
+ scheme for encoding special characters than what is suggested by
+ QUrl.
+
+ Passing a value of QByteArray() to \a query (a null QByteArray) unsets
+ the query completely. However, passing a value of QByteArray("")
+ will set the query to an empty value, as if the original URL
+ had a lone "?".
+
+ \sa encodedQuery(), hasQuery()
+*/
+void QUrl::setEncodedQuery(const QByteArray &query)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->query = query;
+ d->hasQuery = !query.isNull();
+}
+
+/*!
+ Sets the query string of the URL to an encoded version of \a
+ query. The contents of \a query are converted to a string
+ internally, each pair delimited by the character returned by
+ pairDelimiter(), and the key and value are delimited by
+ valueDelimiter().
+
+ \sa setQueryDelimiters(), queryItems(), setEncodedQueryItems()
+*/
+void QUrl::setQueryItems(const QList<QPair<QString, QString> > &query)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ char alsoEncode[3];
+ alsoEncode[0] = d->valueDelimiter;
+ alsoEncode[1] = d->pairDelimiter;
+ alsoEncode[2] = 0;
+
+ QByteArray queryTmp;
+ for (int i = 0; i < query.size(); i++) {
+ if (i) queryTmp += d->pairDelimiter;
+ // query = *( pchar / "/" / "?" )
+ queryTmp += toPercentEncodingHelper(query.at(i).first, queryExcludeChars, alsoEncode);
+ queryTmp += d->valueDelimiter;
+ // query = *( pchar / "/" / "?" )
+ queryTmp += toPercentEncodingHelper(query.at(i).second, queryExcludeChars, alsoEncode);
+ }
+
+ d->query = queryTmp;
+ d->hasQuery = !query.isEmpty();
+}
+
+/*!
+ \since 4.4
+
+ Sets the query string of the URL to the encoded version of \a
+ query. The contents of \a query are converted to a string
+ internally, each pair delimited by the character returned by
+ pairDelimiter(), and the key and value are delimited by
+ valueDelimiter().
+
+ Note: this function does not verify that the key-value pairs
+ are properly encoded. It is the caller's responsibility to ensure
+ that the query delimiters are properly encoded, if any.
+
+ \sa setQueryDelimiters(), encodedQueryItems(), setQueryItems()
+*/
+void QUrl::setEncodedQueryItems(const QList<QPair<QByteArray, QByteArray> > &query)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ QByteArray queryTmp;
+ for (int i = 0; i < query.size(); i++) {
+ if (i) queryTmp += d->pairDelimiter;
+ queryTmp += query.at(i).first;
+ queryTmp += d->valueDelimiter;
+ queryTmp += query.at(i).second;
+ }
+
+ d->query = queryTmp;
+ d->hasQuery = !query.isEmpty();
+}
+
+/*!
+ Inserts the pair \a key = \a value into the query string of the
+ URL.
+
+ \sa addEncodedQueryItem()
+*/
+void QUrl::addQueryItem(const QString &key, const QString &value)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ char alsoEncode[3];
+ alsoEncode[0] = d->valueDelimiter;
+ alsoEncode[1] = d->pairDelimiter;
+ alsoEncode[2] = 0;
+
+ if (!d->query.isEmpty())
+ d->query += d->pairDelimiter;
+
+ // query = *( pchar / "/" / "?" )
+ d->query += toPercentEncodingHelper(key, queryExcludeChars, alsoEncode);
+ d->query += d->valueDelimiter;
+ // query = *( pchar / "/" / "?" )
+ d->query += toPercentEncodingHelper(value, queryExcludeChars, alsoEncode);
+
+ d->hasQuery = !d->query.isEmpty();
+}
+
+/*!
+ \since 4.4
+
+ Inserts the pair \a key = \a value into the query string of the
+ URL.
+
+ Note: this function does not verify that either \a key or \a value
+ are properly encoded. It is the caller's responsibility to ensure
+ that the query delimiters are properly encoded, if any.
+
+ \sa addQueryItem(), setQueryDelimiters()
+*/
+void QUrl::addEncodedQueryItem(const QByteArray &key, const QByteArray &value)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ if (!d->query.isEmpty())
+ d->query += d->pairDelimiter;
+
+ d->query += key;
+ d->query += d->valueDelimiter;
+ d->query += value;
+
+ d->hasQuery = !d->query.isEmpty();
+}
+
+/*!
+ Returns the query string of the URL, as a map of keys and values.
+
+ \sa setQueryItems(), setEncodedQuery()
+*/
+QList<QPair<QString, QString> > QUrl::queryItems() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QList<QPair<QString, QString> > itemMap;
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ QByteArray q(query + pos, valuedelim - pos);
+ if (valuedelim < end) {
+ QByteArray v(query + valuedelim + 1, end - valuedelim - 1);
+ itemMap += qMakePair(fromPercentEncodingMutable(&q),
+ fromPercentEncodingMutable(&v));
+ } else {
+ itemMap += qMakePair(fromPercentEncodingMutable(&q), QString());
+ }
+ pos = end + 1;
+ }
+
+ return itemMap;
+}
+
+/*!
+ \since 4.4
+
+ Returns the query string of the URL, as a map of encoded keys and values.
+
+ \sa setEncodedQueryItems(), setQueryItems(), setEncodedQuery()
+*/
+QList<QPair<QByteArray, QByteArray> > QUrl::encodedQueryItems() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QList<QPair<QByteArray, QByteArray> > itemMap;
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (valuedelim < end)
+ itemMap += qMakePair(QByteArray(query + pos, valuedelim - pos),
+ QByteArray(query + valuedelim + 1, end - valuedelim - 1));
+ else
+ itemMap += qMakePair(QByteArray(query + pos, valuedelim - pos), QByteArray());
+ pos = end + 1;
+ }
+
+ return itemMap;
+}
+
+/*!
+ Returns true if there is a query string pair whose key is equal
+ to \a key from the URL.
+
+ \sa hasEncodedQueryItem()
+*/
+bool QUrl::hasQueryItem(const QString &key) const
+{
+ return hasEncodedQueryItem(toPercentEncoding(key, queryExcludeChars));
+}
+
+/*!
+ \since 4.4
+
+ Returns true if there is a query string pair whose key is equal
+ to \a key from the URL.
+
+ Note: if the encoded \a key does not match the encoded version of
+ the query, this function will return false. That is, if the
+ encoded query of this URL is "search=Qt%20Rules", calling this
+ function with \a key = "%73earch" will return false.
+
+ \sa hasQueryItem()
+*/
+bool QUrl::hasEncodedQueryItem(const QByteArray &key) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (key == QByteArray::fromRawData(query + pos, valuedelim - pos))
+ return true;
+ pos = end + 1;
+ }
+ return false;
+}
+
+/*!
+ Returns the first query string value whose key is equal to \a key
+ from the URL.
+
+ \sa allQueryItemValues()
+*/
+QString QUrl::queryItemValue(const QString &key) const
+{
+ QByteArray tmp = encodedQueryItemValue(toPercentEncoding(key, queryExcludeChars));
+ return fromPercentEncodingMutable(&tmp);
+}
+
+/*!
+ \since 4.4
+
+ Returns the first query string value whose key is equal to \a key
+ from the URL.
+
+ Note: if the encoded \a key does not match the encoded version of
+ the query, this function will not work. That is, if the
+ encoded query of this URL is "search=Qt%20Rules", calling this
+ function with \a key = "%73earch" will return an empty string.
+
+ \sa queryItemValue(), allQueryItemValues()
+*/
+QByteArray QUrl::encodedQueryItemValue(const QByteArray &key) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (key == QByteArray::fromRawData(query + pos, valuedelim - pos))
+ return valuedelim < end ?
+ QByteArray(query + valuedelim + 1, end - valuedelim - 1) : QByteArray();
+ pos = end + 1;
+ }
+ return QByteArray();
+}
+
+/*!
+ Returns the a list of query string values whose key is equal to
+ \a key from the URL.
+
+ \sa queryItemValue()
+*/
+QStringList QUrl::allQueryItemValues(const QString &key) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QByteArray encodedKey = toPercentEncoding(key, queryExcludeChars);
+ QStringList values;
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (encodedKey == QByteArray::fromRawData(query + pos, valuedelim - pos)) {
+ QByteArray v(query + valuedelim + 1, end - valuedelim - 1);
+ values += valuedelim < end ?
+ fromPercentEncodingMutable(&v)
+ : QString();
+ }
+ pos = end + 1;
+ }
+
+ return values;
+}
+
+/*!
+ \since 4.4
+
+ Returns the a list of query string values whose key is equal to
+ \a key from the URL.
+
+ Note: if the encoded \a key does not match the encoded version of
+ the query, this function will not work. That is, if the
+ encoded query of this URL is "search=Qt%20Rules", calling this
+ function with \a key = "%73earch" will return an empty list.
+
+ \sa allQueryItemValues(), queryItemValue(), encodedQueryItemValue()
+*/
+QList<QByteArray> QUrl::allEncodedQueryItemValues(const QByteArray &key) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QList<QByteArray> values;
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (key == QByteArray::fromRawData(query + pos, valuedelim - pos))
+ values += valuedelim < end ?
+ QByteArray(query + valuedelim + 1, end - valuedelim - 1)
+ : QByteArray();
+ pos = end + 1;
+ }
+
+ return values;
+}
+
+/*!
+ Removes the first query string pair whose key is equal to \a key
+ from the URL.
+
+ \sa removeAllQueryItems()
+*/
+void QUrl::removeQueryItem(const QString &key)
+{
+ removeEncodedQueryItem(toPercentEncoding(key, queryExcludeChars));
+}
+
+/*!
+ \since 4.4
+
+ Removes the first query string pair whose key is equal to \a key
+ from the URL.
+
+ Note: if the encoded \a key does not match the encoded version of
+ the query, this function will not work. That is, if the
+ encoded query of this URL is "search=Qt%20Rules", calling this
+ function with \a key = "%73earch" will do nothing.
+
+ \sa removeQueryItem(), removeAllQueryItems()
+*/
+void QUrl::removeEncodedQueryItem(const QByteArray &key)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (key == QByteArray::fromRawData(query + pos, valuedelim - pos)) {
+ if (end < d->query.size())
+ ++end; // remove additional '%'
+ d->query.remove(pos, end - pos);
+ return;
+ }
+ pos = end + 1;
+ }
+}
+
+/*!
+ Removes all the query string pairs whose key is equal to \a key
+ from the URL.
+
+ \sa removeQueryItem()
+*/
+void QUrl::removeAllQueryItems(const QString &key)
+{
+ removeAllEncodedQueryItems(toPercentEncoding(key, queryExcludeChars));
+}
+
+/*!
+ \since 4.4
+
+ Removes all the query string pairs whose key is equal to \a key
+ from the URL.
+
+ Note: if the encoded \a key does not match the encoded version of
+ the query, this function will not work. That is, if the
+ encoded query of this URL is "search=Qt%20Rules", calling this
+ function with \a key = "%73earch" will do nothing.
+
+ \sa removeQueryItem()
+*/
+void QUrl::removeAllEncodedQueryItems(const QByteArray &key)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+
+ int pos = 0;
+ const char *query = d->query.constData();
+ while (pos < d->query.size()) {
+ int valuedelim, end;
+ d->queryItem(pos, &valuedelim, &end);
+ if (key == QByteArray::fromRawData(query + pos, valuedelim - pos)) {
+ if (end < d->query.size())
+ ++end; // remove additional '%'
+ d->query.remove(pos, end - pos);
+ } else {
+ pos = end + 1;
+ }
+ }
+}
+
+/*!
+ Returns the query string of the URL in percent encoded form.
+*/
+QByteArray QUrl::encodedQuery() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->query;
+}
+
+/*!
+ Sets the fragment of the URL to \a fragment. The fragment is the
+ last part of the URL, represented by a '#' followed by a string of
+ characters. It is typically used in HTTP for referring to a
+ certain link or point on a page:
+
+ \img qurl-fragment.png
+
+ The fragment is sometimes also referred to as the URL "reference".
+
+ Passing an argument of QString() (a null QString) will unset the fragment.
+ Passing an argument of QString("") (an empty but not null QString)
+ will set the fragment to an empty string (as if the original URL
+ had a lone "#").
+
+ \sa fragment(), hasFragment()
+*/
+void QUrl::setFragment(const QString &fragment)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->fragment = fragment;
+ d->hasFragment = !fragment.isNull();
+ d->encodedFragment.clear();
+}
+
+/*!
+ Returns the fragment of the URL.
+
+ \sa setFragment()
+*/
+QString QUrl::fragment() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ if (d->fragment.isNull() && !d->encodedFragment.isNull()) {
+ QUrlPrivate *that = const_cast<QUrlPrivate *>(d);
+ that->fragment = fromPercentEncodingHelper(d->encodedFragment);
+ }
+ return d->fragment;
+}
+
+/*!
+ \since 4.4
+
+ Sets the URL's fragment to the percent-encoded \a fragment. The fragment is the
+ last part of the URL, represented by a '#' followed by a string of
+ characters. It is typically used in HTTP for referring to a
+ certain link or point on a page:
+
+ \img qurl-fragment.png
+
+ The fragment is sometimes also referred to as the URL "reference".
+
+ Passing an argument of QByteArray() (a null QByteArray) will unset
+ the fragment. Passing an argument of QByteArray("") (an empty but
+ not null QByteArray) will set the fragment to an empty string (as
+ if the original URL had a lone "#").
+
+ \sa setFragment(), encodedFragment()
+*/
+void QUrl::setEncodedFragment(const QByteArray &fragment)
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ detach();
+ QURL_UNSETFLAG(d->stateFlags, QUrlPrivate::Validated | QUrlPrivate::Normalized);
+
+ d->encodedFragment = fragment;
+ d->hasFragment = !fragment.isNull();
+ d->fragment.clear();
+}
+
+/*!
+ \since 4.4
+
+ Returns the fragment of the URL if it is defined; otherwise an
+ empty string is returned. The returned value will have its
+ non-ASCII and other control characters percent-encoded, as in
+ toEncoded().
+
+ \sa setEncodedFragment(), toEncoded()
+*/
+QByteArray QUrl::encodedFragment() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ d->ensureEncodedParts();
+ return d->encodedFragment;
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this URL contains a fragment (i.e., if # was seen on it).
+
+ \sa fragment(), setFragment()
+*/
+bool QUrl::hasFragment() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->hasFragment;
+}
+
+/*!
+ Returns the result of the merge of this URL with \a relative. This
+ URL is used as a base to convert \a relative to an absolute URL.
+
+ If \a relative is not a relative URL, this function will return \a
+ relative directly. Otherwise, the paths of the two URLs are
+ merged, and the new URL returned has the scheme and authority of
+ the base URL, but with the merged path, as in the following
+ example:
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 5
+
+ Calling resolved() with ".." returns a QUrl whose directory is
+ one level higher than the original. Similarly, calling resolved()
+ with "../.." removes two levels from the path. If \a relative is
+ "/", the path becomes "/".
+
+ \sa isRelative()
+*/
+QUrl QUrl::resolved(const QUrl &relative) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ if (!QURL_HASFLAG(relative.d->stateFlags, QUrlPrivate::Parsed))
+ relative.d->parse();
+
+ d->ensureEncodedParts();
+ relative.d->ensureEncodedParts();
+
+ QUrl t;
+ // be non strict and allow scheme in relative url
+ if (!relative.d->scheme.isEmpty() && relative.d->scheme != d->scheme) {
+ t = relative;
+ } else {
+ if (!relative.authority().isEmpty()) {
+ t = relative;
+ } else {
+ if (relative.d->encodedPath.isEmpty()) {
+ t.d->encodedPath = d->encodedPath;
+ t.setEncodedQuery(relative.d->hasQuery ? relative.d->query : d->query);
+ } else {
+ t.d->encodedPath = relative.d->encodedPath.at(0) == '/'
+ ? relative.d->encodedPath
+ : d->mergePaths(relative.d->encodedPath);
+ t.setEncodedQuery(relative.d->query);
+ }
+ t.d->encodedUserName = d->encodedUserName;
+ t.d->encodedPassword = d->encodedPassword;
+ t.d->host = d->host;
+ t.d->port = d->port;
+ }
+ t.setScheme(d->scheme);
+ }
+ t.setFragment(relative.fragment());
+ removeDotsFromPath(&t.d->encodedPath);
+ t.d->path.clear();
+
+ return t;
+}
+
+/*!
+ Returns true if the URL is relative; otherwise returns false. A
+ URL is relative if its scheme is undefined; this function is
+ therefore equivalent to calling scheme().isEmpty().
+*/
+bool QUrl::isRelative() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ return d->scheme.isEmpty();
+}
+
+/*!
+ Returns the human-displayable string representation of the
+ URL. The output can be customized by passing flags with \a
+ options.
+
+ \sa FormattingOptions, toEncoded()
+*/
+QString QUrl::toString(FormattingOptions options) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QString url;
+
+ if (!(options & QUrl::RemoveScheme) && !d->scheme.isEmpty())
+ url += d->scheme + QLatin1Char(':');
+ QString ourPath = path();
+ if ((options & QUrl::RemoveAuthority) != QUrl::RemoveAuthority) {
+ bool doFileScheme = d->scheme == QLatin1String("file") && ourPath.startsWith(QLatin1Char('/'));
+ QString tmp = d->authority(options);
+ if (!tmp.isEmpty() || doFileScheme) {
+ if (doFileScheme && !ourPath.startsWith(QLatin1Char('/')))
+ url += QLatin1Char('/');
+ url += QLatin1String("//");
+ url += tmp;
+ }
+ }
+ if (!(options & QUrl::RemovePath)) {
+ // check if we need to insert a slash
+ if ((options & QUrl::RemoveAuthority) != QUrl::RemoveAuthority
+ && !d->authority(options).isEmpty() && !ourPath.isEmpty() && ourPath.at(0) != QLatin1Char('/'))
+ url += QLatin1Char('/');
+ url += ourPath;
+ // check if we need to remove trailing slashes
+ while ((options & StripTrailingSlash) && url.right(1) == QLatin1String("/"))
+ url.chop(1);
+ }
+
+ if (!(options & QUrl::RemoveQuery) && d->hasQuery) {
+ url += QLatin1Char('?');
+ url += fromPercentEncoding(d->query);
+ }
+ if (!(options & QUrl::RemoveFragment) && d->hasFragment) {
+ url += QLatin1Char('#');
+ url += fragment();
+ }
+
+ return url;
+}
+
+/*!
+ Returns the encoded representation of the URL if it's valid;
+ otherwise an empty QByteArray is returned. The output can be
+ customized by passing flags with \a options.
+
+ The user info, path and fragment are all converted to UTF-8, and
+ all non-ASCII characters are then percent encoded. The host name
+ is encoded using Punycode.
+*/
+QByteArray QUrl::toEncoded(FormattingOptions options) const
+{
+ return d->toEncoded(options);
+}
+
+/*!
+ Parses \a input and returns the corresponding QUrl. \a input is
+ assumed to be in encoded form, containing only ASCII characters.
+
+ The URL is parsed using TolerantMode.
+
+ \sa toEncoded(), setUrl()
+*/
+QUrl QUrl::fromEncoded(const QByteArray &input)
+{
+ QUrl tmp;
+ tmp.setEncodedUrl(input, TolerantMode);
+ return tmp;
+}
+
+/*!
+ \overload
+
+ Parses the URL using \a parsingMode.
+
+ \sa toEncoded(), setUrl()
+*/
+QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode parsingMode)
+{
+ QUrl tmp;
+ tmp.setEncodedUrl(input, parsingMode);
+ return tmp;
+}
+
+/*!
+ Returns a decoded copy of \a input. \a input is first decoded from
+ percent encoding, then converted from UTF-8 to unicode.
+*/
+QString QUrl::fromPercentEncoding(const QByteArray &input)
+{
+ return fromPercentEncodingHelper(input);
+}
+
+/*!
+ Returns an encoded copy of \a input. \a input is first converted
+ to UTF-8, and all ASCII-characters that are not in the unreserved group
+ are percent encoded. To prevent characters from being percent encoded
+ pass them to \a exclude. To force characters to be percent encoded pass
+ them to \a include.
+
+ Unreserved is defined as:
+ ALPHA / DIGIT / "-" / "." / "_" / "~"
+
+ \snippet doc/src/snippets/code/src_corelib_io_qurl.cpp 6
+*/
+QByteArray QUrl::toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
+{
+ return toPercentEncodingHelper(input, exclude.constData(), include.constData());
+}
+
+/*!
+ \obsolete
+ Returns a \a uc in Punycode encoding.
+
+ Punycode is a Unicode encoding used for internationalized domain
+ names, as defined in RFC3492. If you want to convert a domain name from
+ Unicode to its ASCII-compatible representation, use toAce().
+*/
+QByteArray QUrl::toPunycode(const QString &uc)
+{
+ QByteArray output;
+ toPunycodeHelper(uc.constData(), uc.size(), &output);
+ return output;
+}
+
+/*!
+ \obsolete
+ Returns the Punycode decoded representation of \a pc.
+
+ Punycode is a Unicode encoding used for internationalized domain
+ names, as defined in RFC3492. If you want to convert a domain from
+ its ASCII-compatible encoding to the Unicode representation, use
+ fromAce().
+*/
+QString QUrl::fromPunycode(const QByteArray &pc)
+{
+ uint n = initial_n;
+ uint i = 0;
+ uint bias = initial_bias;
+
+ // strip any ACE prefix
+ int start = pc.startsWith("xn--") ? 4 : 0;
+ if (!start)
+ return QString::fromLatin1(pc);
+
+ // find the last delimiter character '-' in the input array. copy
+ // all data before this delimiter directly to the output array.
+ int delimiterPos = pc.lastIndexOf(0x2d);
+ QString output = delimiterPos < 4 ?
+ QString() : QString::fromLatin1(pc.constData() + start, delimiterPos - start);
+
+ // if a delimiter was found, skip to the position after it;
+ // otherwise start at the front of the input string. everything
+ // before the delimiter is assumed to be basic code points.
+ uint cnt = delimiterPos + 1;
+
+ // loop through the rest of the input string, inserting non-basic
+ // characters into output as we go.
+ while (cnt < (uint) pc.size()) {
+ uint oldi = i;
+ uint w = 1;
+
+ // find the next index for inserting a non-basic character.
+ for (uint k = base; cnt < (uint) pc.size(); k += base) {
+ // grab a character from the punycode input and find its
+ // delta digit (each digit code is part of the
+ // variable-length integer delta)
+ uint digit = pc.at(cnt++);
+ if (digit - 48 < 10) digit -= 22;
+ else if (digit - 65 < 26) digit -= 65;
+ else if (digit - 97 < 26) digit -= 97;
+ else digit = base;
+
+ // reject out of range digits
+ if (digit >= base || digit > (Q_MAXINT - i) / w)
+ return QLatin1String("");
+
+ i += (digit * w);
+
+ // detect threshold to stop reading delta digits
+ uint t;
+ if (k <= bias) t = tmin;
+ else if (k >= bias + tmax) t = tmax;
+ else t = k - bias;
+ if (digit < t) break;
+
+ w *= (base - t);
+ }
+
+ // find new bias and calculate the next non-basic code
+ // character.
+ bias = adapt(i - oldi, output.length() + 1, oldi == 0);
+ n += i / (output.length() + 1);
+
+ // allow the deltas to wrap around
+ i %= (output.length() + 1);
+
+ // insert the character n at position i
+ output.insert((uint) i, QChar((ushort) n));
+ ++i;
+ }
+
+ return output;
+}
+
+/*!
+ \since 4.2
+
+ Returns the Unicode form of the given domain name
+ \a domain, which is encoded in the ASCII Compatible Encoding (ACE).
+ The result of this function is considered equivalent to \a domain.
+
+ If the value in \a domain cannot be encoded, it will be converted
+ to QString and returned.
+
+ The ASCII Compatible Encoding (ACE) is defined by RFC 3490, RFC 3491
+ and RFC 3492. It is part of the Internationalizing Domain Names in
+ Applications (IDNA) specification, which allows for domain names
+ (like \c "qtsoftware.com") to be written using international
+ characters.
+*/
+QString QUrl::fromAce(const QByteArray &domain)
+{
+ return qt_from_ACE(QString::fromLatin1(domain));
+}
+
+/*!
+ \since 4.2
+
+ Returns the ASCII Compatible Encoding of the given domain name \a domain.
+ The result of this function is considered equivalent to \a domain.
+
+ The ASCII-Compatible Encoding (ACE) is defined by RFC 3490, RFC 3491
+ and RFC 3492. It is part of the Internationalizing Domain Names in
+ Applications (IDNA) specification, which allows for domain names
+ (like \c "qtsoftware.com") to be written using international
+ characters.
+*/
+QByteArray QUrl::toAce(const QString &domain)
+{
+ // IDNA / rfc3490 describes these four delimiters used for
+ // separating labels in unicode international domain
+ // names.
+ QString nameprepped = qt_nameprep(domain);
+ int lastIdx = 0;
+ QByteArray result;
+ for (int i = 0; i < nameprepped.size(); ++i) {
+ ushort uc = nameprepped.at(i).unicode();
+ if (uc == 0x2e || uc == 0x3002 || uc == 0xff0e || uc == 0xff61) {
+ if (lastIdx)
+ result += '.';
+ toPunycodeHelper(nameprepped.constData() + lastIdx, i - lastIdx, &result);
+ lastIdx = i + 1;
+ }
+ }
+ if (lastIdx)
+ result += '.';
+ toPunycodeHelper(nameprepped.constData() + lastIdx, nameprepped.size() - lastIdx, &result);
+
+ return result;
+}
+
+/*!
+ \since 4.2
+
+ Returns the current whitelist of top-level domains that are allowed
+ to have non-ASCII characters in their compositions.
+
+ See setIdnWhitelist() for the rationale of this list.
+*/
+QStringList QUrl::idnWhitelist()
+{
+ if (user_idn_whitelist)
+ return *user_idn_whitelist;
+ QStringList list;
+ unsigned int i = 0;
+ while (i < sizeof(idn_whitelist)/sizeof(const char *)) {
+ list << QLatin1String(idn_whitelist[i]);
+ ++i;
+ }
+ return list;
+}
+
+/*!
+ \since 4.2
+
+ Sets the whitelist of Top-Level Domains (TLDs) that are allowed to have
+ non-ASCII characters in domains to the value of \a list.
+
+ Qt has comes a default list that contains the Internet top-level domains
+ that have published support for Internationalized Domain Names (IDNs)
+ and rules to guarantee that no deception can happen between similarly-looking
+ characters (such as the Latin lowercase letter \c 'a' and the Cyrillic
+ equivalent, which in most fonts are visually identical).
+
+ This list is periodically maintained, as registrars publish new rules.
+
+ This function is provided for those who need to manipulate the list, in
+ order to add or remove a TLD. It is not recommended to change its value
+ for purposes other than testing, as it may expose users to security risks.
+*/
+void QUrl::setIdnWhitelist(const QStringList &list)
+{
+ if (!user_idn_whitelist)
+ user_idn_whitelist = new QStringList;
+ *user_idn_whitelist = list;
+}
+
+/*!
+ \internal
+
+ Returns true if this URL is "less than" the given \a url. This
+ provides a means of ordering URLs.
+*/
+bool QUrl::operator <(const QUrl &url) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ if (!QURL_HASFLAG(url.d->stateFlags, QUrlPrivate::Parsed)) url.d->parse();
+ return d->normalized() < url.d->normalized();
+}
+
+/*!
+ Returns true if this URL and the given \a url are equal;
+ otherwise returns false.
+*/
+bool QUrl::operator ==(const QUrl &url) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+ if (!QURL_HASFLAG(url.d->stateFlags, QUrlPrivate::Parsed)) url.d->parse();
+ return d->normalized() == url.d->normalized();
+}
+
+/*!
+ Returns true if this URL and the given \a url are not equal;
+ otherwise returns false.
+*/
+bool QUrl::operator !=(const QUrl &url) const
+{
+ return !(*this == url);
+}
+
+/*!
+ Assigns the specified \a url to this object.
+*/
+QUrl &QUrl::operator =(const QUrl &url)
+{
+ qAtomicAssign(d, url.d);
+ return *this;
+}
+
+/*!
+ Assigns the specified \a url to this object.
+*/
+QUrl &QUrl::operator =(const QString &url)
+{
+ QUrl tmp(url);
+ qAtomicAssign(d, tmp.d);
+ return *this;
+}
+
+/*! \internal
+
+ Forces a detach.
+*/
+void QUrl::detach()
+{ qAtomicDetach(d); }
+
+/*!
+ \internal
+*/
+bool QUrl::isDetached() const
+{
+ return d->ref == 1;
+}
+
+
+/*!
+ Returns a QUrl representation of \a localFile, interpreted as a
+ local file.
+
+ \sa toLocalFile()
+*/
+QUrl QUrl::fromLocalFile(const QString &localFile)
+{
+ QUrl url;
+ url.setScheme(QLatin1String("file"));
+ QString deslashified = localFile;
+ deslashified.replace(QLatin1Char('\\'), QLatin1Char('/'));
+
+
+
+ // magic for drives on windows
+ if (deslashified.length() > 1 && deslashified.at(1) == QLatin1Char(':') && deslashified.at(0) != QLatin1Char('/')) {
+ url.setPath(QLatin1String("/") + deslashified);
+ // magic for shared drive on windows
+ } else if (deslashified.startsWith(QLatin1String("//"))) {
+ int indexOfPath = deslashified.indexOf(QLatin1Char('/'), 2);
+ url.setHost(deslashified.mid(2, indexOfPath - 2));
+ if (indexOfPath > 2)
+ url.setPath(deslashified.right(deslashified.length() - indexOfPath));
+ } else {
+ url.setPath(deslashified);
+ }
+
+ return url;
+}
+
+/*!
+ Returns the path of this URL formatted as a local file path.
+
+ \sa fromLocalFile()
+*/
+QString QUrl::toLocalFile() const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QString tmp;
+ QString ourPath = path();
+ if (d->scheme.isEmpty() || d->scheme.toLower() == QLatin1String("file")) {
+
+ // magic for shared drive on windows
+ if (!d->host.isEmpty()) {
+ tmp = QLatin1String("//") + d->host + (ourPath.length() > 0 && ourPath.at(0) != QLatin1Char('/')
+ ? QLatin1String("/") + ourPath : ourPath);
+ } else {
+ tmp = ourPath;
+ // magic for drives on windows
+ if (ourPath.length() > 2 && ourPath.at(0) == QLatin1Char('/') && ourPath.at(2) == QLatin1Char(':'))
+ tmp.remove(0, 1);
+ }
+ }
+
+ return tmp;
+}
+
+/*!
+ Returns true if this URL is a parent of \a childUrl. \a childUrl is a child
+ of this URL if the two URLs share the same scheme and authority,
+ and this URL's path is a parent of the path of \a childUrl.
+*/
+bool QUrl::isParentOf(const QUrl &childUrl) const
+{
+ if (!QURL_HASFLAG(d->stateFlags, QUrlPrivate::Parsed)) d->parse();
+
+ QString childPath = childUrl.path();
+ QString ourPath = path();
+
+ return ((childUrl.scheme().isEmpty() || d->scheme == childUrl.scheme())
+ && (childUrl.authority().isEmpty() || d->authority() == childUrl.authority())
+ && childPath.startsWith(ourPath)
+ && ((ourPath.endsWith(QLatin1Char('/')) && childPath.length() > ourPath.length())
+ || (!ourPath.endsWith(QLatin1Char('/'))
+ && childPath.length() > ourPath.length() && childPath.at(ourPath.length()) == QLatin1Char('/'))));
+}
+
+/*!
+ \fn void QUrl::setProtocol(const QString &s)
+
+ Use setScheme() instead.
+*/
+
+/*!
+ \fn void QUrl::setUser(const QString &s)
+
+ Use setUserName() instead.
+*/
+
+/*!
+ \fn bool QUrl::hasUser() const
+
+ Use !userName().isEmpty() instead.
+*/
+
+/*!
+ \fn bool QUrl::hasPassword() const
+
+ Use !password().isEmpty() instead.
+*/
+
+/*!
+ \fn bool QUrl::hasHost() const
+
+ Use !host().isEmpty() instead.
+*/
+
+/*!
+ \fn bool QUrl::hasPort() const
+
+ Use port() != -1 instead.
+*/
+
+/*!
+ \fn bool QUrl::hasPath() const
+
+ Use !path().isEmpty() instead.
+*/
+
+/*!
+ \fn void QUrl::setQuery(const QString &txt)
+
+ Use setEncodedQuery() instead.
+*/
+
+/*!
+ \fn void QUrl::setRef(const QString &txt)
+
+ Use setFragment() instead.
+*/
+
+/*!
+ \fn bool QUrl::hasRef() const
+
+ Use !fragment().isEmpty() instead.
+*/
+
+/*!
+ \fn void QUrl::addPath(const QString &p)
+
+ Use setPath() instead.
+*/
+
+/*!
+ \fn void QUrl::setFileName(const QString &txt)
+
+ Use setPath() instead.
+*/
+
+/*!
+ \fn void QUrl::decode(QString &url)
+
+ Use fromPercentEncoding() instead.
+*/
+
+/*!
+ \fn void QUrl::encode(QString &url)
+
+ Use toPercentEncoding() instead.
+*/
+
+/*!
+ \fn bool QUrl::cdUp()
+
+ Use resolved("..") instead.
+
+ \oldcode
+ QUrl url("http://qtsoftware.com/Developer/");
+ url.cdUp();
+ \newcode
+ QUrl url("http://qtsoftware.com/Developer/");
+ url = url.resolved("..");
+ \endcode
+*/
+
+/*!
+ \fn bool QUrl::isRelativeUrl(const QString &url)
+
+ Use isRelative() instead.
+*/
+
+/*!
+ \fn void QUrl::reset()
+
+ Use clear() instead.
+*/
+
+/*!
+ \fn QUrl::operator QString() const
+
+ Use toString() instead.
+*/
+
+/*!
+ \fn QString QUrl::protocol() const
+
+ Use scheme() instead.
+*/
+
+/*!
+ \fn QString QUrl::user() const
+
+ Use userName() instead.
+*/
+
+/*!
+ \fn QString QUrl::query() const
+
+ Use encodedQuery() instead.
+*/
+
+/*!
+ \fn QString QUrl::ref() const
+
+ Use fragment() instead.
+*/
+
+/*!
+ \fn QString QUrl::fileName() const
+
+ Use QFileInfo(path()).fileName() instead.
+*/
+
+/*!
+ \fn QString QUrl::dirPath() const
+
+ Use QFileInfo(path()).absolutePath() or QFileInfo(path()) instead.
+*/
+
+#ifdef QT3_SUPPORT
+void QUrl::setFileName(const QString &txt)
+{
+ QFileInfo fileInfo(path());
+ fileInfo.setFile(txt);
+ setPath(fileInfo.filePath());
+}
+
+QString QUrl::fileName() const
+{
+ QFileInfo fileInfo(path());
+ return fileInfo.fileName();
+}
+
+QString QUrl::dirPath() const
+{
+ QFileInfo fileInfo(path());
+ if (fileInfo.isAbsolute()) {
+ QString absPath = fileInfo.absolutePath();
+#ifdef Q_OS_WIN
+ if (absPath.size() > 1 && absPath.at(1) == QLatin1Char(':'))
+ absPath = absPath.mid(2);
+#endif
+ return absPath;
+ }
+ return fileInfo.path();
+}
+#endif
+
+
+#ifndef QT_NO_DATASTREAM
+/*! \relates QUrl
+
+ Writes url \a url to the stream \a out and returns a reference
+ to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator<<(QDataStream &out, const QUrl &url)
+{
+ QByteArray u = url.toEncoded();
+ out << u;
+ return out;
+}
+
+/*! \relates QUrl
+
+ Reads a url into \a url from the stream \a in and returns a
+ reference to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+QDataStream &operator>>(QDataStream &in, QUrl &url)
+{
+ QByteArray u;
+ in >> u;
+ url = QUrl::fromEncoded(u);
+ return in;
+}
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QUrl &url)
+{
+ d.maybeSpace() << "QUrl(" << url.toString() << ")";
+ return d.space();
+}
+#endif
+
+/*!
+ \since 4.2
+
+ Returns a text string that explains why an URL is invalid in the case being;
+ otherwise returns an empty string.
+*/
+QString QUrl::errorString() const
+{
+ return d->createErrorString();
+}
+
+/*!
+ \typedef QUrl::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QUrl::data_ptr()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h
new file mode 100644
index 0000000..9242092
--- /dev/null
+++ b/src/corelib/io/qurl.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QURL_H
+#define QURL_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+class QUrlPrivate;
+class QDataStream;
+
+class Q_CORE_EXPORT QUrl
+{
+public:
+ enum ParsingMode {
+ TolerantMode,
+ StrictMode
+ };
+
+ // encoding / toString values
+ enum FormattingOption {
+ None = 0x0,
+ RemoveScheme = 0x1,
+ RemovePassword = 0x2,
+ RemoveUserInfo = RemovePassword | 0x4,
+ RemovePort = 0x8,
+ RemoveAuthority = RemoveUserInfo | RemovePort | 0x10,
+ RemovePath = 0x20,
+ RemoveQuery = 0x40,
+ RemoveFragment = 0x80,
+
+ StripTrailingSlash = 0x10000
+ };
+ Q_DECLARE_FLAGS(FormattingOptions, FormattingOption)
+
+ QUrl();
+ QUrl(const QString &url);
+ QUrl(const QString &url, ParsingMode mode);
+ // ### Qt 5: merge the two constructors, with mode = TolerantMode
+ QUrl(const QUrl &copy);
+ QUrl &operator =(const QUrl &copy);
+ QUrl &operator =(const QString &url);
+ ~QUrl();
+
+ void setUrl(const QString &url);
+ void setUrl(const QString &url, ParsingMode mode);
+ // ### Qt 5: merge the two setUrl() functions, with mode = TolerantMode
+ void setEncodedUrl(const QByteArray &url);
+ void setEncodedUrl(const QByteArray &url, ParsingMode mode);
+ // ### Qt 5: merge the two setEncodedUrl() functions, with mode = TolerantMode
+
+ bool isValid() const;
+
+ bool isEmpty() const;
+
+ void clear();
+
+ void setScheme(const QString &scheme);
+ QString scheme() const;
+
+ void setAuthority(const QString &authority);
+ QString authority() const;
+
+ void setUserInfo(const QString &userInfo);
+ QString userInfo() const;
+
+ void setUserName(const QString &userName);
+ QString userName() const;
+ void setEncodedUserName(const QByteArray &userName);
+ QByteArray encodedUserName() const;
+
+ void setPassword(const QString &password);
+ QString password() const;
+ void setEncodedPassword(const QByteArray &password);
+ QByteArray encodedPassword() const;
+
+ void setHost(const QString &host);
+ QString host() const;
+ void setEncodedHost(const QByteArray &host);
+ QByteArray encodedHost() const;
+
+ void setPort(int port);
+ int port() const;
+ int port(int defaultPort) const;
+ // ### Qt 5: merge the two port() functions, with defaultPort = -1
+
+ void setPath(const QString &path);
+ QString path() const;
+ void setEncodedPath(const QByteArray &path);
+ QByteArray encodedPath() const;
+
+ bool hasQuery() const;
+
+ void setEncodedQuery(const QByteArray &query);
+ QByteArray encodedQuery() const;
+
+ void setQueryDelimiters(char valueDelimiter, char pairDelimiter);
+ char queryValueDelimiter() const;
+ char queryPairDelimiter() const;
+
+ void setQueryItems(const QList<QPair<QString, QString> > &query);
+ void addQueryItem(const QString &key, const QString &value);
+ QList<QPair<QString, QString> > queryItems() const;
+ bool hasQueryItem(const QString &key) const;
+ QString queryItemValue(const QString &key) const;
+ QStringList allQueryItemValues(const QString &key) const;
+ void removeQueryItem(const QString &key);
+ void removeAllQueryItems(const QString &key);
+
+ void setEncodedQueryItems(const QList<QPair<QByteArray, QByteArray> > &query);
+ void addEncodedQueryItem(const QByteArray &key, const QByteArray &value);
+ QList<QPair<QByteArray, QByteArray> > encodedQueryItems() const;
+ bool hasEncodedQueryItem(const QByteArray &key) const;
+ QByteArray encodedQueryItemValue(const QByteArray &key) const;
+ QList<QByteArray> allEncodedQueryItemValues(const QByteArray &key) const;
+ void removeEncodedQueryItem(const QByteArray &key);
+ void removeAllEncodedQueryItems(const QByteArray &key);
+
+ void setFragment(const QString &fragment);
+ QString fragment() const;
+ void setEncodedFragment(const QByteArray &fragment);
+ QByteArray encodedFragment() const;
+ bool hasFragment() const;
+
+ QUrl resolved(const QUrl &relative) const;
+
+ bool isRelative() const;
+ bool isParentOf(const QUrl &url) const;
+
+ static QUrl fromLocalFile(const QString &localfile);
+ QString toLocalFile() const;
+
+ QString toString(FormattingOptions options = None) const;
+
+ QByteArray toEncoded(FormattingOptions options = None) const;
+ static QUrl fromEncoded(const QByteArray &url);
+ static QUrl fromEncoded(const QByteArray &url, ParsingMode mode);
+ // ### Qt 5: merge the two fromEncoded() functions, with mode = TolerantMode
+
+ void detach();
+ bool isDetached() const;
+
+ bool operator <(const QUrl &url) const;
+ bool operator ==(const QUrl &url) const;
+ bool operator !=(const QUrl &url) const;
+
+ static QString fromPercentEncoding(const QByteArray &);
+ static QByteArray toPercentEncoding(const QString &,
+ const QByteArray &exclude = QByteArray(),
+ const QByteArray &include = QByteArray());
+ static QString fromPunycode(const QByteArray &);
+ static QByteArray toPunycode(const QString &);
+ static QString fromAce(const QByteArray &);
+ static QByteArray toAce(const QString &);
+ static QStringList idnWhitelist();
+ static void setIdnWhitelist(const QStringList &);
+
+#if defined QT3_SUPPORT
+ inline QT3_SUPPORT QString protocol() const { return scheme(); }
+ inline QT3_SUPPORT void setProtocol(const QString &s) { setScheme(s); }
+ inline QT3_SUPPORT void setUser(const QString &s) { setUserName(s); }
+ inline QT3_SUPPORT QString user() const { return userName(); }
+ inline QT3_SUPPORT bool hasUser() const { return !userName().isEmpty(); }
+ inline QT3_SUPPORT bool hasPassword() const { return !password().isEmpty(); }
+ inline QT3_SUPPORT bool hasHost() const { return !host().isEmpty(); }
+ inline QT3_SUPPORT bool hasPort() const { return port() != -1; }
+ inline QT3_SUPPORT bool hasPath() const { return !path().isEmpty(); }
+ inline QT3_SUPPORT void setQuery(const QString &txt)
+ {
+ setEncodedQuery(txt.toLatin1());
+ }
+ inline QT3_SUPPORT QString query() const
+ {
+ return QString::fromLatin1(encodedQuery().constData());
+ }
+ inline QT3_SUPPORT QString ref() const { return fragment(); }
+ inline QT3_SUPPORT void setRef(const QString &txt) { setFragment(txt); }
+ inline QT3_SUPPORT bool hasRef() const { return !fragment().isEmpty(); }
+ inline QT3_SUPPORT void addPath(const QString &p) { setPath(path() + QLatin1String("/") + p); }
+ QT3_SUPPORT void setFileName(const QString &txt);
+ QT3_SUPPORT QString fileName() const;
+ QT3_SUPPORT QString dirPath() const;
+ static inline QT3_SUPPORT void decode(QString &url)
+ {
+ url = QUrl::fromPercentEncoding(url.toLatin1());
+ }
+ static inline QT3_SUPPORT void encode(QString &url)
+ {
+ url = QString::fromLatin1(QUrl::toPercentEncoding(url).constData());
+ }
+ inline QT3_SUPPORT operator QString() const { return toString(); }
+ inline QT3_SUPPORT bool cdUp()
+ {
+ *this = resolved(QUrl(QLatin1String("..")));
+ return true;
+ }
+ static inline QT3_SUPPORT bool isRelativeUrl(const QString &url)
+ {
+ return QUrl(url).isRelative();
+ }
+#endif
+
+ QString errorString() const;
+
+protected:
+#if defined (QT3_SUPPORT)
+ inline QT3_SUPPORT void reset() { clear(); }
+#endif
+
+private:
+ QUrlPrivate *d;
+public:
+ typedef QUrlPrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+Q_DECLARE_TYPEINFO(QUrl, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QUrl)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QUrl::FormattingOptions)
+
+#ifndef QT_NO_DATASTREAM
+Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QUrl &);
+Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QUrl &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug, const QUrl &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QURL_H
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
new file mode 100644
index 0000000..5971987
--- /dev/null
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowspipewriter_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_THREAD
+
+QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipe, QObject * parent)
+ : QThread(parent),
+ writePipe(INVALID_HANDLE_VALUE),
+ quitNow(false),
+ hasWritten(false)
+{
+#if !defined(Q_OS_WINCE) || (_WIN32_WCE >= 0x600)
+ DuplicateHandle(GetCurrentProcess(), pipe, GetCurrentProcess(),
+ &writePipe, 0, FALSE, DUPLICATE_SAME_ACCESS);
+#else
+ Q_UNUSED(pipe);
+ writePipe = GetCurrentProcess();
+#endif
+}
+
+QWindowsPipeWriter::~QWindowsPipeWriter()
+{
+ lock.lock();
+ quitNow = true;
+ waitCondition.wakeOne();
+ lock.unlock();
+ if (!wait(100))
+ terminate();
+#if !defined(Q_OS_WINCE) || (_WIN32_WCE >= 0x600)
+ CloseHandle(writePipe);
+#endif
+}
+
+bool QWindowsPipeWriter::waitForWrite(int msecs)
+{
+ QMutexLocker locker(&lock);
+ bool hadWritten = hasWritten;
+ hasWritten = false;
+ if (hadWritten)
+ return true;
+ if (!waitCondition.wait(&lock, msecs))
+ return false;
+ hadWritten = hasWritten;
+ hasWritten = false;
+ return hadWritten;
+}
+
+qint64 QWindowsPipeWriter::write(const char *ptr, qint64 maxlen)
+{
+ if (!isRunning())
+ return -1;
+
+ QMutexLocker locker(&lock);
+ data.append(QByteArray(ptr, maxlen));
+ waitCondition.wakeOne();
+ return maxlen;
+}
+
+void QWindowsPipeWriter::run()
+{
+ OVERLAPPED overl = {0, 0, 0, 0, NULL};
+ overl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ forever {
+ lock.lock();
+ while(data.isEmpty() && (!quitNow)) {
+ waitCondition.wakeOne();
+ waitCondition.wait(&lock);
+ }
+
+ if (quitNow) {
+ lock.unlock();
+ quitNow = false;
+ break;
+ }
+
+ QByteArray copy = data;
+
+ lock.unlock();
+
+ const char *ptrData = copy.data();
+ qint64 maxlen = copy.size();
+ qint64 totalWritten = 0;
+ overl.Offset = 0;
+ overl.OffsetHigh = 0;
+ while ((!quitNow) && totalWritten < maxlen) {
+ DWORD written = 0;
+ // Write 2k at a time to prevent flooding the pipe. If you
+ // write too much (4k-8k), the pipe can close
+ // unexpectedly.
+ if (!WriteFile(writePipe, ptrData + totalWritten,
+ qMin<int>(2048, maxlen - totalWritten), &written, &overl)) {
+ if (GetLastError() == 0xE8/*NT_STATUS_INVALID_USER_BUFFER*/) {
+ // give the os a rest
+ msleep(100);
+ continue;
+ }
+#ifndef Q_OS_WINCE
+ if (GetLastError() == ERROR_IO_PENDING) {
+ if (!GetOverlappedResult(writePipe, &overl, &written, TRUE)) {
+ CloseHandle(overl.hEvent);
+ return;
+ }
+ } else {
+ CloseHandle(overl.hEvent);
+ return;
+ }
+#else
+ return;
+#endif
+ }
+ totalWritten += written;
+#if defined QPIPEWRITER_DEBUG
+ qDebug("QWindowsPipeWriter::run() wrote %d %d/%d bytes",
+ written, int(totalWritten), int(maxlen));
+#endif
+ lock.lock();
+ data.remove(0, written);
+ hasWritten = true;
+ lock.unlock();
+ }
+ emit canWrite();
+ }
+ CloseHandle(overl.hEvent);
+}
+
+#endif //QT_NO_THREAD
+
+QT_END_NAMESPACE
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
new file mode 100644
index 0000000..ce0c7f9
--- /dev/null
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSPIPEWRITER_P_H
+#define QWINDOWSPIPEWRITER_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 <qdatetime.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <qt_windows.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_THREAD
+
+#define SLEEPMIN 10
+#define SLEEPMAX 500
+
+class QIncrementalSleepTimer
+{
+
+public:
+ QIncrementalSleepTimer(int msecs)
+ : totalTimeOut(msecs)
+ , nextSleep(qMin(SLEEPMIN, totalTimeOut))
+ {
+ if (totalTimeOut == -1)
+ nextSleep = SLEEPMIN;
+ timer.start();
+ }
+
+ int nextSleepTime()
+ {
+ int tmp = nextSleep;
+ nextSleep = qMin(nextSleep * 2, qMin(SLEEPMAX, timeLeft()));
+ return tmp;
+ }
+
+ int timeLeft() const
+ {
+ if (totalTimeOut == -1)
+ return SLEEPMAX;
+ return qMax(totalTimeOut - timer.elapsed(), 0);
+ }
+
+ bool hasTimedOut() const
+ {
+ if (totalTimeOut == -1)
+ return false;
+ return timer.elapsed() >= totalTimeOut;
+ }
+
+ void resetIncrements()
+ {
+ nextSleep = qMin(SLEEPMIN, timeLeft());
+ }
+
+private:
+ QTime timer;
+ int totalTimeOut;
+ int nextSleep;
+};
+
+class Q_CORE_EXPORT QWindowsPipeWriter : public QThread
+{
+ Q_OBJECT
+
+Q_SIGNALS:
+ void canWrite();
+
+public:
+ QWindowsPipeWriter(HANDLE writePipe, QObject * parent = 0);
+ ~QWindowsPipeWriter();
+
+ bool waitForWrite(int msecs);
+ qint64 write(const char *data, qint64 maxlen);
+
+ qint64 bytesToWrite() const
+ {
+ QMutexLocker locker(&lock);
+ return data.size();
+ }
+
+ bool hadWritten() const
+ {
+ return hasWritten;
+ }
+
+protected:
+ void run();
+
+private:
+ QByteArray data;
+ QWaitCondition waitCondition;
+ mutable QMutex lock;
+ HANDLE writePipe;
+ volatile bool quitNow;
+ bool hasWritten;
+};
+
+#endif //QT_NO_THREAD
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_PROCESS