synchronize qmake project parser with qt creator
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>
Wed, 1 Jun 2011 15:08:39 +0000 (17:08 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 12 Sep 2011 15:29:43 +0000 (17:29 +0200)
qt creator as of ddb918f.
not feeling like replaying the whole history ...

(cherry picked from commit f1249f94c07654f0b76e7a90fb81ed5b58cd30f7)

Conflicts:
tools/linguist/shared/profileevaluator.cpp

Change-Id: Ief6f9bc0eed4e8a759e95ed321d158f4017710d4
Reviewed-on: http://codereview.qt-project.org/4605
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
14 files changed:
src/linguist/lrelease/main.cpp
src/linguist/lupdate/main.cpp
src/linguist/shared/abstractproitemvisitor.h [deleted file]
src/linguist/shared/ioutils.cpp [new file with mode: 0644]
src/linguist/shared/ioutils.h [new file with mode: 0644]
src/linguist/shared/profileevaluator.cpp
src/linguist/shared/profileevaluator.h
src/linguist/shared/profileparser.cpp [new file with mode: 0644]
src/linguist/shared/profileparser.h [new file with mode: 0644]
src/linguist/shared/proitems.cpp
src/linguist/shared/proitems.h
src/linguist/shared/proparser.pri
src/linguist/shared/proparser_global.h [new file with mode: 0644]
src/linguist/shared/proparserutils.h [deleted file]

index 73414b0..f171813 100644 (file)
@@ -40,7 +40,9 @@
 ****************************************************************************/
 
 #include "translator.h"
-#include "profileevaluator.h"
+
+#include <profileparser.h>
+#include <profileevaluator.h>
 
 #ifndef QT_BOOTSTRAPPED
 #include <QtCore/QCoreApplication>
@@ -59,6 +61,8 @@
 QT_USE_NAMESPACE
 
 #ifdef QT_BOOTSTRAPPED
+static QString binDir;
+
 static void initBinaryDir(
 #ifndef Q_OS_WIN
         const char *argv0
@@ -186,6 +190,40 @@ static bool releaseTsFile(const QString& tsFileName,
     return releaseTranslator(tor, qmFileName, cd, removeIdentical);
 }
 
+static void print(const QString &fileName, int lineNo, const QString &msg)
+{
+    if (lineNo)
+        printErr(QString::fromLatin1("%2(%1): %3").arg(lineNo).arg(fileName, msg));
+    else
+        printErr(msg);
+}
+
+class ParseHandler : public ProFileParserHandler {
+public:
+    virtual void parseError(const QString &fileName, int lineNo, const QString &msg)
+        { if (verbose) print(fileName, lineNo, msg); }
+
+    bool verbose;
+};
+
+class EvalHandler : public ProFileEvaluatorHandler {
+public:
+    virtual void configError(const QString &msg)
+        { printErr(msg); }
+    virtual void evalError(const QString &fileName, int lineNo, const QString &msg)
+        { if (verbose) print(fileName, lineNo, msg); }
+    virtual void fileMessage(const QString &msg)
+        { printErr(msg); }
+
+    virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
+    virtual void doneWithEval(ProFile *) {}
+
+    bool verbose;
+};
+
+static ParseHandler parseHandler;
+static EvalHandler evalHandler;
+
 int main(int argc, char **argv)
 {
 #ifdef QT_BOOTSTRAPPED
@@ -273,23 +311,32 @@ int main(int argc, char **argv)
         if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
             || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
             QFileInfo fi(inputFile);
-            ProFile pro(fi.absoluteFilePath());
 
-            ProFileEvaluator visitor;
-            visitor.setVerbose(cd.isVerbose());
+            parseHandler.verbose = evalHandler.verbose = cd.isVerbose();
+            ProFileOption option;
+#ifdef QT_BOOTSTRAPPED
+            option.initProperties(binDir + QLatin1String("/qmake"));
+#else
+            option.initProperties(app.applicationDirPath() + QLatin1String("/qmake"));
+#endif
+            ProFileParser parser(0, &parseHandler);
+            ProFileEvaluator visitor(&option, &parser, &evalHandler);
 
-            if (!visitor.queryProFile(&pro)) {
+            ProFile *pro;
+            if (!(pro = parser.parsedProFile(QDir::cleanPath(fi.absoluteFilePath())))) {
                 printErr(LR::tr(
                           "lrelease error: cannot read project file '%1'.\n")
                           .arg(inputFile));
                 continue;
             }
-            if (!visitor.accept(&pro)) {
+            if (!visitor.accept(pro)) {
                 printErr(LR::tr(
                           "lrelease error: cannot process project file '%1'.\n")
                           .arg(inputFile));
+                pro->deref();
                 continue;
             }
+            pro->deref();
 
             QStringList translations = visitor.values(QLatin1String("TRANSLATIONS"));
             if (translations.isEmpty()) {
@@ -325,8 +372,6 @@ int main(int argc, char **argv)
 # include <windows.h>
 #endif
 
-static QString binDir;
-
 static void initBinaryDir(
 #ifndef Q_OS_WIN
         const char *_argv0
index 737bfd0..34bb792 100644 (file)
@@ -42,6 +42,7 @@
 #include "lupdate.h"
 
 #include <translator.h>
+#include <profileparser.h>
 #include <profileevaluator.h>
 
 #include <QtCore/QCoreApplication>
@@ -227,6 +228,40 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
     }
 }
 
+static void print(const QString &fileName, int lineNo, const QString &msg)
+{
+    if (lineNo)
+        printErr(QString::fromLatin1("%2(%1): %3").arg(lineNo).arg(fileName, msg));
+    else
+        printErr(msg);
+}
+
+class ParseHandler : public ProFileParserHandler {
+public:
+    virtual void parseError(const QString &fileName, int lineNo, const QString &msg)
+        { if (verbose) print(fileName, lineNo, msg); }
+
+    bool verbose;
+};
+
+class EvalHandler : public ProFileEvaluatorHandler {
+public:
+    virtual void configError(const QString &msg)
+        { printErr(msg); }
+    virtual void evalError(const QString &fileName, int lineNo, const QString &msg)
+        { if (verbose) print(fileName, lineNo, msg); }
+    virtual void fileMessage(const QString &msg)
+        { printErr(msg); }
+
+    virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
+    virtual void doneWithEval(ProFile *) {}
+
+    bool verbose;
+};
+
+static ParseHandler parseHandler;
+static EvalHandler evalHandler;
+
 static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
                               const QString &projectDir, const ProFileEvaluator &visitor)
 {
@@ -288,12 +323,14 @@ static void processSources(Translator &fetchedTor,
 
 static void processProjects(
         bool topLevel, bool nestComplain, const QStringList &proFiles,
+        ProFileOption *option, ProFileParser *parser,
         UpdateOptions options, const QByteArray &codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *parentTor, bool *fail);
 
 static void processProject(
-        bool nestComplain, const QFileInfo &pfi, ProFileEvaluator &visitor,
+        bool nestComplain, const QFileInfo &pfi,
+        ProFileOption *option, ProFileParser *parser, ProFileEvaluator &visitor,
         UpdateOptions options, const QByteArray &_codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *fetchedTor, bool *fail)
@@ -321,7 +358,7 @@ static void processProject(
             else
                 subProFiles << subPro;
         }
-        processProjects(false, nestComplain, subProFiles, options, codecForSource,
+        processProjects(false, nestComplain, subProFiles, option, parser, options, codecForSource,
                         targetLanguage, sourceLanguage, fetchedTor, fail);
     } else {
         ConversionData cd;
@@ -347,23 +384,25 @@ static void processProject(
 
 static void processProjects(
         bool topLevel, bool nestComplain, const QStringList &proFiles,
+        ProFileOption *option, ProFileParser *parser,
         UpdateOptions options, const QByteArray &codecForSource,
         const QString &targetLanguage, const QString &sourceLanguage,
         Translator *parentTor, bool *fail)
 {
     foreach (const QString &proFile, proFiles) {
-        ProFileEvaluator visitor;
-        visitor.setVerbose(options & Verbose);
-
-        QHash<QString, QStringList> lupdateConfig;
-        lupdateConfig.insert(QLatin1String("CONFIG"), QStringList(QLatin1String("lupdate_run")));
-        visitor.addVariables(lupdateConfig);
-
         QFileInfo pfi(proFile);
-        ProFile pro(pfi.absoluteFilePath());
-        if (!visitor.queryProFile(&pro) || !visitor.accept(&pro)) {
+
+        ProFileEvaluator visitor(option, parser, &evalHandler);
+        ProFile *pro;
+        if (!(pro = parser->parsedProFile(QDir::cleanPath(pfi.absoluteFilePath())))) {
+            if (topLevel)
+                *fail = true;
+            continue;
+        }
+        if (!visitor.accept(pro)) {
             if (topLevel)
                 *fail = true;
+            pro->deref();
             continue;
         }
 
@@ -376,6 +415,7 @@ static void processProjects(
                 } else if (nestComplain) {
                     printErr(LU::tr("lupdate warning: TS files from command line "
                                     "prevent recursing into %1.\n").arg(proFile));
+                    pro->deref();
                     continue;
                 }
             }
@@ -387,6 +427,7 @@ static void processProjects(
                 // This might mean either a buggy PRO file or an intentional detach -
                 // we can't know without seeing the actual RHS of the assignment ...
                 // Just assume correctness and be silent.
+                pro->deref();
                 continue;
             }
             Translator tor;
@@ -398,9 +439,10 @@ static void processProjects(
                 tor.setCodecName(tmp.last().toLatin1());
                 setCodec = true;
             }
-            processProject(false, pfi, visitor, options, codecForSource,
+            processProject(false, pfi, option, parser, visitor, options, codecForSource,
                            targetLanguage, sourceLanguage, &tor, fail);
             updateTsFiles(tor, tsFiles, setCodec, sourceLanguage, targetLanguage, options, fail);
+            pro->deref();
             continue;
         }
       noTrans:
@@ -409,12 +451,13 @@ static void processProjects(
                 printErr(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
                                 "will be produced for '%1'.\n").arg(proFile));
             Translator tor;
-            processProject(nestComplain, pfi, visitor, options, codecForSource,
+            processProject(nestComplain, pfi, option, parser, visitor, options, codecForSource,
                            targetLanguage, sourceLanguage, &tor, fail);
         } else {
-            processProject(nestComplain, pfi, visitor, options, codecForSource,
+            processProject(nestComplain, pfi, option, parser, visitor, options, codecForSource,
                            targetLanguage, sourceLanguage, parentTor, fail);
         }
+        pro->deref();
     }
 }
 
@@ -714,15 +757,22 @@ int main(int argc, char **argv)
                             " Both project and source files / include paths specified.\n"));
             return 1;
         }
+
+        parseHandler.verbose = evalHandler.verbose = !!(options & Verbose);
+        ProFileOption option;
+        option.initProperties(app.applicationDirPath() + QLatin1String("/qmake"));
+        option.setCommandLineArguments(QStringList() << QLatin1String("CONFIG+=lupdate_run"));
+        ProFileParser parser(0, &parseHandler);
+
         if (!tsFileNames.isEmpty()) {
             Translator fetchedTor;
             fetchedTor.setCodecName(codecForTr);
-            processProjects(true, true, proFiles, options, QByteArray(),
+            processProjects(true, true, proFiles, &option, &parser, options, QByteArray(),
                             targetLanguage, sourceLanguage, &fetchedTor, &fail);
             updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
                           sourceLanguage, targetLanguage, options, &fail);
         } else {
-            processProjects(true, false, proFiles, options, QByteArray(),
+            processProjects(true, false, proFiles, &option, &parser, options, QByteArray(),
                             targetLanguage, sourceLanguage, 0, &fail);
         }
     }
diff --git a/src/linguist/shared/abstractproitemvisitor.h b/src/linguist/shared/abstractproitemvisitor.h
deleted file mode 100644 (file)
index 73614c5..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the Qt Linguist of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** 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.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef ABSTRACTPROITEMVISITOR
-#define ABSTRACTPROITEMVISITOR
-
-#include "proitems.h"
-
-QT_BEGIN_NAMESPACE
-
-struct AbstractProItemVisitor
-{
-    virtual ~AbstractProItemVisitor() {}
-
-    virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
-    virtual void visitEndProBlock(ProBlock *block) = 0;
-
-    virtual ProItem::ProItemReturn visitProLoopIteration() = 0;
-    virtual void visitProLoopCleanup() = 0;
-
-    virtual void visitBeginProVariable(ProVariable *variable) = 0;
-    virtual void visitEndProVariable(ProVariable *variable) = 0;
-
-    virtual ProItem::ProItemReturn visitBeginProFile(ProFile *value) = 0;
-    virtual ProItem::ProItemReturn visitEndProFile(ProFile *value) = 0;
-
-    virtual void visitProValue(ProValue *value) = 0;
-    virtual ProItem::ProItemReturn visitProFunction(ProFunction *function) = 0;
-    virtual void visitProOperator(ProOperator *function) = 0;
-    virtual void visitProCondition(ProCondition *function) = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif // ABSTRACTPROITEMVISITOR
-
diff --git a/src/linguist/shared/ioutils.cpp b/src/linguist/shared/ioutils.cpp
new file mode 100644 (file)
index 0000000..fbee8fc
--- /dev/null
@@ -0,0 +1,152 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "ioutils.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+
+#ifdef Q_OS_WIN
+#  include <windows.h>
+#else
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#  include <unistd.h>
+#endif
+
+using namespace ProFileEvaluatorInternal;
+
+IoUtils::FileType IoUtils::fileType(const QString &fileName)
+{
+    Q_ASSERT(fileName.isEmpty() || isAbsolutePath(fileName));
+#ifdef Q_OS_WIN
+    DWORD attr = GetFileAttributesW((WCHAR*)fileName.utf16());
+    if (attr == INVALID_FILE_ATTRIBUTES)
+        return FileNotFound;
+    return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FileIsDir : FileIsRegular;
+#else
+    struct ::stat st;
+    if (::stat(fileName.toLocal8Bit().constData(), &st))
+        return FileNotFound;
+    return S_ISDIR(st.st_mode) ? FileIsDir : FileIsRegular;
+#endif
+}
+
+bool IoUtils::isRelativePath(const QString &path)
+{
+    if (path.startsWith(QLatin1Char('/')))
+        return false;
+#ifdef Q_OS_WIN
+    if (path.startsWith(QLatin1Char('\\')))
+        return false;
+    // Unlike QFileInfo, this won't accept a relative path with a drive letter.
+    // Such paths result in a royal mess anyway ...
+    if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter()
+        && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\')))
+        return false;
+#endif
+    return true;
+}
+
+QStringRef IoUtils::fileName(const QString &fileName)
+{
+    return fileName.midRef(fileName.lastIndexOf(QLatin1Char('/')) + 1);
+}
+
+QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
+{
+    if (fileName.isEmpty())
+        return QString();
+    if (isAbsolutePath(fileName))
+        return QDir::cleanPath(fileName);
+    return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
+}
+
+#ifdef QT_BOOTSTRAPPED
+inline static bool isSpecialChar(ushort c)
+{
+    // Chars that should be quoted (TM). This includes:
+#ifdef Q_OS_WIN
+    // - control chars & space
+    // - the shell meta chars "&()<>^|
+    // - the potential separators ,;=
+    static const uchar iqm[] = {
+        0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
+        0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
+    };
+#else
+    static const uchar iqm[] = {
+        0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
+        0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
+    }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+#endif
+
+    return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
+}
+
+inline static bool hasSpecialChars(const QString &arg)
+{
+    for (int x = arg.length() - 1; x >= 0; --x)
+        if (isSpecialChar(arg.unicode()[x].unicode()))
+            return true;
+    return false;
+}
+
+QString IoUtils::shellQuote(const QString &arg)
+{
+    if (!arg.length())
+        return QString::fromLatin1("\"\"");
+
+    QString ret(arg);
+    if (hasSpecialChars(ret)) {
+#ifdef Q_OS_WIN
+        // Quotes are escaped and their preceding backslashes are doubled.
+        // It's impossible to escape anything inside a quoted string on cmd
+        // level, so the outer quoting must be "suspended".
+        ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
+        // 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\"
+        int i = ret.length();
+        while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
+            --i;
+        ret.insert(i, QLatin1Char('"'));
+        ret.prepend(QLatin1Char('"'));
+#else // Q_OS_WIN
+        ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
+        ret.prepend(QLatin1Char('\''));
+        ret.append(QLatin1Char('\''));
+#endif // Q_OS_WIN
+    }
+    return ret;
+}
+#endif
diff --git a/src/linguist/shared/ioutils.h b/src/linguist/shared/ioutils.h
new file mode 100644 (file)
index 0000000..d04ddc7
--- /dev/null
@@ -0,0 +1,65 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef IOUTILS_H
+#define IOUTILS_H
+
+#include <QtCore/QString>
+
+namespace ProFileEvaluatorInternal {
+
+/*!
+  This class provides replacement functionality for QFileInfo, QFile & QDir,
+  as these are abysmally slow.
+*/
+class IoUtils {
+public:
+    enum FileType {
+        FileNotFound = 0,
+        FileIsRegular = 1,
+        FileIsDir = 2
+    };
+
+    static FileType fileType(const QString &fileName);
+    static bool exists(const QString &fileName) { return fileType(fileName) != FileNotFound; }
+    static bool isRelativePath(const QString &fileName);
+    static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
+    static QStringRef fileName(const QString &fileName); // Requires normalized path
+    static QString resolvePath(const QString &baseDir, const QString &fileName);
+#ifdef QT_BOOTSTRAPPED
+    static QString shellQuote(const QString &arg);
+#endif
+};
+
+}
+
+#endif // IOUTILS_H
index 03e01c0..f3b4c3d 100644 (file)
@@ -1,47 +1,39 @@
-/****************************************************************************
+/**************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
 **
-** This file is part of the Qt Linguist of the Qt Toolkit.
 **
-** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
+**
 ** 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:
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
 ** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** 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.
-**
 ** Other Usage
+**
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
 **
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
 
 #include "profileevaluator.h"
-#include "proparserutils.h"
-#include "proitems.h"
+
+#include "profileparser.h"
+#include "ioutils.h"
 
 #include <QtCore/QByteArray>
 #include <QtCore/QDateTime>
@@ -56,6 +48,9 @@
 #include <QtCore/QString>
 #include <QtCore/QStringList>
 #include <QtCore/QTextStream>
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <QtCore/QThreadPool>
+#endif
 
 #ifdef Q_OS_UNIX
 #include <unistd.h>
 #define QT_PCLOSE pclose
 #endif
 
+using namespace ProFileEvaluatorInternal;
+
 QT_BEGIN_NAMESPACE
 
+using namespace ProStringConstants;
+
+
+#define fL1S(s) QString::fromLatin1(s)
+
 ///////////////////////////////////////////////////////////////////////
 //
-// Option
+// ProFileOption
 //
 ///////////////////////////////////////////////////////////////////////
 
-QString
-Option::fixString(QString string, uchar flags)
+ProFileOption::ProFileOption()
 {
-    // XXX Ripped out caching, so this will be slow. Should not matter for current uses.
+#ifdef Q_OS_WIN
+    dirlist_sep = QLatin1Char(';');
+    dir_sep = QLatin1Char('\\');
+#else
+    dirlist_sep = QLatin1Char(':');
+    dir_sep = QLatin1Char('/');
+#endif
+    qmakespec = getEnv(QLatin1String("QMAKESPEC"));
 
-    //fix the environment variables
-    if (flags & Option::FixEnvVars) {
-        int rep;
-        QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)"));
-        reg_variableName.setMinimal(true);
-        while ((rep = reg_variableName.indexIn(string)) != -1)
-            string.replace(rep, reg_variableName.matchedLength(),
-                           QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
-    }
+    host_mode = HOST_UNKNOWN_MODE;
+    target_mode = TARG_UNKNOWN_MODE;
 
-    //canonicalize it (and treat as a path)
-    if (flags & Option::FixPathCanonicalize) {
-#if 0
-        string = QFileInfo(string).canonicalFilePath();
+#ifdef PROEVALUATOR_THREAD_SAFE
+    base_inProgress = false;
 #endif
-        string = QDir::cleanPath(string);
-    }
+}
 
-    if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':'))
-        string[0] = string[0].toLower();
+ProFileOption::~ProFileOption()
+{
+}
 
-    //fix separators
-    Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators)));
-    if (flags & Option::FixPathToLocalSeparators) {
-#if defined(Q_OS_WIN32)
-        string = string.replace(QLatin1Char('/'), QLatin1Char('\\'));
-#else
-        string = string.replace(QLatin1Char('\\'), QLatin1Char('/'));
-#endif
-    } else if (flags & Option::FixPathToTargetSeparators) {
-        string = string.replace(QLatin1Char('/'), Option::dir_sep)
-                       .replace(QLatin1Char('\\'), Option::dir_sep);
+void ProFileOption::setCommandLineArguments(const QStringList &args)
+{
+    QStringList _precmds, _preconfigs, _postcmds, _postconfigs;
+    bool after = false;
+
+    bool isConf = false;
+    foreach (const QString &arg, args) {
+        if (isConf) {
+            isConf = false;
+            if (after)
+                _postconfigs << arg;
+            else
+                _preconfigs << arg;
+        } else if (arg.startsWith(QLatin1Char('-'))) {
+            if (arg == QLatin1String("-after")) {
+                after = true;
+            } else if (arg == QLatin1String("-config")) {
+                isConf = true;
+            } else if (arg == QLatin1String("-win32")) {
+                host_mode = HOST_WIN_MODE;
+                target_mode = TARG_WIN_MODE;
+            } else if (arg == QLatin1String("-unix")) {
+                host_mode = HOST_UNIX_MODE;
+                target_mode = TARG_UNIX_MODE;
+            } else if (arg == QLatin1String("-macx")) {
+                host_mode = HOST_MACX_MODE;
+                target_mode = TARG_MACX_MODE;
+            }
+        } else if (arg.contains(QLatin1Char('='))) {
+            if (after)
+                _postcmds << arg;
+            else
+                _precmds << arg;
+        }
     }
 
-    if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) ||
-        (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\''))))
-        string = string.mid(1, string.length() - 2);
+    if (!_preconfigs.isEmpty())
+        _precmds << (fL1S("CONFIG += ") + _preconfigs.join(fL1S(" ")));
+    precmds = _precmds.join(fL1S("\n"));
+    if (!_postconfigs.isEmpty())
+        _postcmds << (fL1S("CONFIG += ") + _postconfigs.join(fL1S(" ")));
+    postcmds = _postcmds.join(fL1S("\n"));
 
-    return string;
+    if (host_mode != HOST_UNKNOWN_MODE)
+        applyHostMode();
+}
+
+void ProFileOption::applyHostMode()
+{
+   if (host_mode == HOST_WIN_MODE) {
+       dir_sep = fL1S("\\");
+   } else {
+       dir_sep = fL1S("/");
+   }
+}
+
+QString ProFileOption::getEnv(const QString &var) const
+{
+#ifndef QT_BOOTSTRAPPED
+    if (!environment.isEmpty())
+        return environment.value(var);
+#endif
+    return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
 }
 
+#ifdef PROEVALUATOR_INIT_PROPS
+bool ProFileOption::initProperties(const QString &qmake)
+{
+    QByteArray data;
+#ifndef QT_BOOTSTRAPPED
+    QProcess proc;
+    proc.start(qmake, QStringList() << QLatin1String("-query"));
+    if (!proc.waitForFinished())
+        return false;
+    data = proc.readAll();
+#else
+    if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake) + QLatin1String(" -query"))
+                              .toLocal8Bit(), "r")) {
+        char buff[1024];
+        while (!feof(proc))
+            data.append(buff, int(fread(buff, 1, 1023, proc)));
+        QT_PCLOSE(proc);
+    }
+#endif
+    foreach (QByteArray line, data.split('\n'))
+        if (!line.startsWith("QMAKE_")) {
+            int off = line.indexOf(':');
+            if (off < 0) // huh?
+                continue;
+            if (line.endsWith('\r'))
+                line.chop(1);
+            properties.insert(QString::fromLatin1(line.left(off)),
+                              QString::fromLocal8Bit(line.mid(off + 1)));
+        }
+    return true;
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////
 //
 // ProFileEvaluator::Private
 //
 ///////////////////////////////////////////////////////////////////////
 
-class ProFileEvaluator::Private : public AbstractProItemVisitor
+class ProFileEvaluator::Private
 {
 public:
-    Private(ProFileEvaluator *q_);
+    static void initStatics();
+    Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
+            ProFileEvaluatorHandler *handler);
+    ~Private();
 
     ProFileEvaluator *q;
-    int m_lineNo;                                   // Error reporting
-    bool m_verbose;
-
-    /////////////// Reading pro file
-
-    bool read(ProFile *pro);
-
-    ProBlock *currentBlock();
-    void updateItem();
-    bool parseLine(const QString &line);
-    void insertVariable(const QString &line, int *i);
-    void insertOperator(const char op);
-    void insertComment(const QString &comment);
-    void enterScope(bool multiLine);
-    void leaveScope();
-    void finalizeBlock();
-
-    QStack<ProBlock *> m_blockstack;
-    ProBlock *m_block;
-
-    ProItem *m_commentItem;
-    QString m_proitem;
-    QString m_pendingComment;
-    bool m_syntaxError;
-    bool m_contNextLine;
-    bool m_inQuote;
-    int m_parens;
-
-    /////////////// Evaluating pro file contents
-
-    // implementation of AbstractProItemVisitor
-    ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
-    void visitEndProBlock(ProBlock *block);
-    ProItem::ProItemReturn visitProLoopIteration();
-    void visitProLoopCleanup();
-    void visitBeginProVariable(ProVariable *variable);
-    void visitEndProVariable(ProVariable *variable);
-    ProItem::ProItemReturn visitBeginProFile(ProFile *value);
-    ProItem::ProItemReturn visitEndProFile(ProFile *value);
-    void visitProValue(ProValue *value);
-    ProItem::ProItemReturn visitProFunction(ProFunction *function);
-    void visitProOperator(ProOperator *oper);
-    void visitProCondition(ProCondition *condition);
-
-    QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
-    QStringList values(const QString &variableName) const;
-    QStringList values(const QString &variableName, const ProFile *pro) const;
-    QStringList values(const QString &variableName, const QHash<QString, QStringList> &place,
-                       const ProFile *pro) const;
-    QString propertyValue(const QString &val) const;
 
+    enum VisitReturn {
+        ReturnFalse,
+        ReturnTrue,
+        ReturnBreak,
+        ReturnNext,
+        ReturnReturn
+    };
+
+    static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
+    ProString getStr(const ushort *&tokPtr);
+    ProString getHashStr(const ushort *&tokPtr);
+    void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
+    static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
+    static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
+    void skipExpression(const ushort *&tokPtr);
+
+    void visitCmdLine(const QString &cmds);
+    VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
+                             ProFileEvaluator::LoadFlags flags);
+    VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
+    VisitReturn visitProBlock(const ushort *tokPtr);
+    VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
+                             const ushort *tokPtr);
+    void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
+    void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
+
+    static inline const ProString &map(const ProString &var);
+    QHash<ProString, ProStringList> *findValues(const ProString &variableName,
+                                                QHash<ProString, ProStringList>::Iterator *it);
+    ProStringList &valuesRef(const ProString &variableName);
+    ProStringList valuesDirect(const ProString &variableName) const;
+    ProStringList values(const ProString &variableName) const;
+    QString propertyValue(const QString &val, bool complain) const;
+
+    ProStringList split_value_list(const QString &vals, const ProFile *source = 0);
     bool isActiveConfig(const QString &config, bool regex = false);
-    QStringList expandVariableReferences(const QString &value);
-    void doVariableReplace(QString *str);
-    QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
-    QString format(const char *format) const;
+    ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
+    ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
+    ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
+    ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
+    ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
+    void evalError(const QString &msg) const;
 
     QString currentFileName() const;
     QString currentDirectory() const;
     ProFile *currentProFile() const;
-
-    ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
-    bool evaluateFile(const QString &fileName);
+    QString resolvePath(const QString &fileName) const
+        { return IoUtils::resolvePath(currentDirectory(), fileName); }
+
+    VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
+    VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
+    VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
+    bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+                            ProFileEvaluator::LoadFlags flags);
+    bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+                      ProFileEvaluator::LoadFlags flags);
     bool evaluateFeatureFile(const QString &fileName);
+    enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
+    bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+                          QHash<ProString, ProStringList> *values, FunctionDefs *defs,
+                          EvalIntoMode mode); // values are output-only, defs are input-only
+
+    static ALWAYS_INLINE VisitReturn returnBool(bool b)
+        { return b ? ReturnTrue : ReturnFalse; }
+
+    QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
+    QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
+    ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
+    VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
+                                     const ProString &function);
+
+    bool modesForGenerator(const QString &gen,
+            ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const;
+    void validateModes() const;
+    QStringList qmakeMkspecPaths() const;
+    QStringList qmakeFeaturePaths() const;
+
+    QString expandEnvVars(const QString &str) const;
+    QString fixPathToLocalOS(const QString &str) const;
+    QString sysrootify(const QString &path, const QString &baseDir) const;
+
+#ifndef QT_BOOTSTRAPPED
+    void runProcess(QProcess *proc, const QString &command, QProcess::ProcessChannel chan) const;
+#endif
 
-    static inline ProItem::ProItemReturn returnBool(bool b)
-        { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }
-
-    QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);
-
-    QStringList qmakeFeaturePaths();
-
-    struct State {
-        bool condition;
-        bool prevCondition;
-    } m_sts;
-    bool m_invertNext; // Short-lived, so not in State
     int m_skipLevel;
+    int m_loopLevel; // To report unexpected break() and next()s
+#ifdef PROEVALUATOR_CUMULATIVE
     bool m_cumulative;
-    bool m_isFirstVariableValue;
-    QString m_lastVarName;
-    ProVariable::VariableOperator m_variableOperator;
-    QString m_origfile;
-    QString m_oldPath;                              // To restore the current path to the path
-    QStack<ProFile*> m_profileStack;                // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
-    struct ProLoop {
-        QString variable;
-        QStringList oldVarVal;
-        QStringList list;
-        int index;
-        bool infinite;
+#else
+    enum { m_cumulative = 0 };
+#endif
+
+    struct Location {
+        Location() : pro(0), line(0) {}
+        Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
+        ProFile *pro;
+        int line;
     };
-    QStack<ProLoop> m_loopStack;
 
-    // we need the following two variables for handling
-    // CONFIG = foo bar $$CONFIG
-    QHash<QString, QStringList> m_tempValuemap;         // used while evaluating (variable operator value1 value2 ...)
-    QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...)
+    Location m_current; // Currently evaluated location
+    QStack<Location> m_locationStack; // All execution location changes
+    QStack<ProFile *> m_profileStack; // Includes only
 
-    QHash<QString, QStringList> m_valuemap;         // VariableName must be us-ascii, the content however can be non-us-ascii.
-    QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
-    QHash<QString, QString> m_properties;
     QString m_outputDir;
 
-    bool m_definingTest;
-    QString m_definingFunc;
-    QHash<QString, ProBlock *> m_testFunctions;
-    QHash<QString, ProBlock *> m_replaceFunctions;
-    QStringList m_returnValue;
-    QStack<QHash<QString, QStringList> > m_valuemapStack;
-    QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;
+    int m_listCount;
+    FunctionDefs m_functionDefs;
+    ProStringList m_returnValue;
+    QStack<QHash<ProString, ProStringList> > m_valuemapStack;         // VariableName must be us-ascii, the content however can be non-us-ascii.
+    QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
+
+    ProFileOption *m_option;
+    ProFileParser *m_parser;
+    ProFileEvaluatorHandler *m_handler;
+
+    enum ExpandFunc {
+        E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
+        E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+        E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
+        E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
+        E_REPLACE
+    };
+
+    enum TestFunc {
+        T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
+        T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
+        T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
+        T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
+    };
 
-    int m_prevLineNo;                               // Checking whether we're assigning the same TARGET
-    ProFile *m_prevProFile;                         // See m_prevLineNo
+    enum VarName {
+        V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
+        V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
+        V_OUT_PWD, V_PWD, V_IN_PWD,
+        V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
+        V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
+        V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
+        V__DATE_, V__QMAKE_CACHE_
+    };
 };
 
-Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
+static struct {
+    QString field_sep;
+    QString strtrue;
+    QString strfalse;
+    QString strunix;
+    QString strmacx;
+    QString strmac;
+    QString strwin32;
+    QString strsymbian;
+    ProString strCONFIG;
+    ProString strARGS;
+    QString strDot;
+    QString strDotDot;
+    QString strever;
+    QString strforever;
+    ProString strTEMPLATE;
+    ProString strQMAKE_DIR_SEP;
+    QHash<ProString, int> expands;
+    QHash<ProString, int> functions;
+    QHash<ProString, int> varList;
+    QHash<ProString, ProString> varMap;
+    QRegExp reg_variableName;
+    ProStringList fakeValue;
+} statics;
+
+void ProFileEvaluator::Private::initStatics()
+{
+    if (!statics.field_sep.isNull())
+        return;
+
+    statics.field_sep = QLatin1String(" ");
+    statics.strtrue = QLatin1String("true");
+    statics.strfalse = QLatin1String("false");
+    statics.strunix = QLatin1String("unix");
+    statics.strmacx = QLatin1String("macx");
+    statics.strmac = QLatin1String("mac");
+    statics.strwin32 = QLatin1String("win32");
+    statics.strsymbian = QLatin1String("symbian");
+    statics.strCONFIG = ProString("CONFIG");
+    statics.strARGS = ProString("ARGS");
+    statics.strDot = QLatin1String(".");
+    statics.strDotDot = QLatin1String("..");
+    statics.strever = QLatin1String("ever");
+    statics.strforever = QLatin1String("forever");
+    statics.strTEMPLATE = ProString("TEMPLATE");
+    statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
+
+    statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
+    statics.reg_variableName.setMinimal(true);
+
+    statics.fakeValue.detach(); // It has to have a unique begin() value
+
+    static const struct {
+        const char * const name;
+        const ExpandFunc func;
+    } expandInits[] = {
+        { "member", E_MEMBER },
+        { "first", E_FIRST },
+        { "last", E_LAST },
+        { "size", E_SIZE },
+        { "cat", E_CAT },
+        { "fromfile", E_FROMFILE },
+        { "eval", E_EVAL },
+        { "list", E_LIST },
+        { "sprintf", E_SPRINTF },
+        { "join", E_JOIN },
+        { "split", E_SPLIT },
+        { "basename", E_BASENAME },
+        { "dirname", E_DIRNAME },
+        { "section", E_SECTION },
+        { "find", E_FIND },
+        { "system", E_SYSTEM },
+        { "unique", E_UNIQUE },
+        { "quote", E_QUOTE },
+        { "escape_expand", E_ESCAPE_EXPAND },
+        { "upper", E_UPPER },
+        { "lower", E_LOWER },
+        { "re_escape", E_RE_ESCAPE },
+        { "files", E_FILES },
+        { "prompt", E_PROMPT }, // interactive, so cannot be implemented
+        { "replace", E_REPLACE }
+    };
+    for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
+        statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
+
+    static const struct {
+        const char * const name;
+        const TestFunc func;
+    } testInits[] = {
+        { "requires", T_REQUIRES },
+        { "greaterThan", T_GREATERTHAN },
+        { "lessThan", T_LESSTHAN },
+        { "equals", T_EQUALS },
+        { "isEqual", T_EQUALS },
+        { "exists", T_EXISTS },
+        { "export", T_EXPORT },
+        { "clear", T_CLEAR },
+        { "unset", T_UNSET },
+        { "eval", T_EVAL },
+        { "CONFIG", T_CONFIG },
+        { "if", T_IF },
+        { "isActiveConfig", T_CONFIG },
+        { "system", T_SYSTEM },
+        { "return", T_RETURN },
+        { "break", T_BREAK },
+        { "next", T_NEXT },
+        { "defined", T_DEFINED },
+        { "contains", T_CONTAINS },
+        { "infile", T_INFILE },
+        { "count", T_COUNT },
+        { "isEmpty", T_ISEMPTY },
+        { "load", T_LOAD },
+        { "include", T_INCLUDE },
+        { "debug", T_DEBUG },
+        { "message", T_MESSAGE },
+        { "warning", T_MESSAGE },
+        { "error", T_MESSAGE },
+    };
+    for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
+        statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
+
+    static const char * const names[] = {
+        "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
+        "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
+        "OUT_PWD", "PWD", "IN_PWD",
+        "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
+        "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
+        "QMAKE_HOST.version", "QMAKE_HOST.version_string",
+        "_DATE_", "_QMAKE_CACHE_"
+    };
+    for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
+        statics.varList.insert(ProString(names[i]), i);
+
+    static const struct {
+        const char * const oldname, * const newname;
+    } mapInits[] = {
+        { "INTERFACES", "FORMS" },
+        { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
+        { "TARGETDEPS", "POST_TARGETDEPS" },
+        { "LIBPATH", "QMAKE_LIBDIR" },
+        { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
+        { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
+        { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
+        { "PRECOMPH", "PRECOMPILED_HEADER" },
+        { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
+        { "INCPATH", "INCLUDEPATH" },
+        { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
+        { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
+        { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
+        { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
+        { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
+        { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
+        { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
+        { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
+        { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
+    };
+    for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
+        statics.varMap.insert(ProString(mapInits[i].oldname),
+                              ProString(mapInits[i].newname));
+}
+
+const ProString &ProFileEvaluator::Private::map(const ProString &var)
+{
+    QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
+    return (it != statics.varMap.constEnd()) ? it.value() : var;
+}
+
 
-ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
-  : q(q_)
+ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
+                                   ProFileParser *parser, ProFileEvaluatorHandler *handler)
+  : q(q_), m_option(option), m_parser(parser), m_handler(handler)
 {
-    // Global parser state
-    m_prevLineNo = 0;
-    m_prevProFile = 0;
+    // So that single-threaded apps don't have to call initialize() for now.
+    initStatics();
 
     // Configuration, more or less
-    m_verbose = true;
+#ifdef PROEVALUATOR_CUMULATIVE
     m_cumulative = true;
+#endif
 
     // Evaluator state
-    m_sts.condition = false;
-    m_sts.prevCondition = false;
-    m_invertNext = false;
     m_skipLevel = 0;
-    m_isFirstVariableValue = true;
-    m_definingFunc.clear();
+    m_loopLevel = 0;
+    m_listCount = 0;
+    m_valuemapStack.push(QHash<ProString, ProStringList>());
 }
 
-bool ProFileEvaluator::Private::read(ProFile *pro)
+ProFileEvaluator::Private::~Private()
 {
-    QFile file(pro->fileName());
-    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-        q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
-        return false;
-    }
-
-    // Parser state
-    m_block = 0;
-    m_commentItem = 0;
-    m_inQuote = false;
-    m_parens = 0;
-    m_contNextLine = false;
-    m_syntaxError = false;
-    m_lineNo = 1;
-    m_blockstack.clear();
-    m_blockstack.push(pro);
-
-    QTextStream ts(&file);
-    while (!ts.atEnd()) {
-        QString line = ts.readLine();
-        if (!parseLine(line)) {
-            q->errorMessage(format(".pro parse failure."));
-            return false;
-        }
-        ++m_lineNo;
-    }
-    return true;
 }
 
-bool ProFileEvaluator::Private::parseLine(const QString &line0)
-{
-    if (m_blockstack.isEmpty())
-        return false;
-
-    int parens = m_parens;
-    bool inQuote = m_inQuote;
-    bool escaped = false;
-    QString line = line0.simplified();
+//////// Evaluator tools /////////
 
-    for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
-        ushort c = line.at(i).unicode();
-        if (c == '#') { // Yep - no escaping possible
-            insertComment(line.mid(i + 1));
-            escaped = m_contNextLine;
-            break;
-        }
-        if (!escaped) {
-            if (c == '\\') {
-                escaped = true;
-                m_proitem += c;
-                continue;
-            } else if (c == '"') {
-                inQuote = !inQuote;
-                m_proitem += c;
-                continue;
-            }
-        } else {
-            escaped = false;
-        }
-        if (!inQuote) {
-            if (c == '(') {
-                ++parens;
-            } else if (c == ')') {
-                --parens;
-            } else if (!parens) {
-                if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
-                    if (c == ' ')
-                        updateItem();
-                    else
-                        m_proitem += c;
-                    continue;
-                }
-                if (c == ':') {
-                    enterScope(false);
-                    continue;
-                }
-                if (c == '{') {
-                    enterScope(true);
-                    continue;
-                }
-                if (c == '}') {
-                    leaveScope();
-                    continue;
-                }
-                if (c == '=') {
-                    insertVariable(line, &i);
-                    continue;
-                }
-                if (c == '|' || c == '!') {
-                    insertOperator(c);
-                    continue;
-                }
-            }
-        }
-
-        m_proitem += c;
-    }
-    m_inQuote = inQuote;
-    m_parens = parens;
-    m_contNextLine = escaped;
-    if (escaped) {
-        m_proitem.chop(1);
-        updateItem();
-        return true;
-    } else {
-        if (!m_syntaxError) {
-            updateItem();
-            finalizeBlock();
-            return true;
-        }
-        return false;
-    }
+uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
+{
+    uint len = *tokPtr++;
+    len |= (uint)*tokPtr++ << 16;
+    return len;
 }
 
-void ProFileEvaluator::Private::finalizeBlock()
+ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
 {
-    if (m_blockstack.isEmpty()) {
-        m_syntaxError = true;
-    } else {
-        if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
-            leaveScope();
-        m_block = 0;
-        m_commentItem = 0;
-    }
+    uint len = *tokPtr++;
+    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
+    ret.setSource(m_current.pro);
+    tokPtr += len;
+    return ret;
 }
 
-void ProFileEvaluator::Private::insertVariable(const QString &line, int *i)
+ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
 {
-    ProVariable::VariableOperator opkind;
-
-    if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker
-        return;
-
-    switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
-        case '+':
-            m_proitem.chop(1);
-            opkind = ProVariable::AddOperator;
-            break;
-        case '-':
-            m_proitem.chop(1);
-            opkind = ProVariable::RemoveOperator;
-            break;
-        case '*':
-            m_proitem.chop(1);
-            opkind = ProVariable::UniqueAddOperator;
-            break;
-        case '~':
-            m_proitem.chop(1);
-            opkind = ProVariable::ReplaceOperator;
-            break;
-        default:
-            opkind = ProVariable::SetOperator;
-    }
+    uint hash = getBlockLen(tokPtr);
+    uint len = *tokPtr++;
+    ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
+    tokPtr += len;
+    return ret;
+}
 
-    ProBlock *block = m_blockstack.top();
-    m_proitem = m_proitem.trimmed();
-    ProVariable *variable = new ProVariable(m_proitem, block);
-    variable->setLineNumber(m_lineNo);
-    variable->setVariableOperator(opkind);
-    block->appendItem(variable);
-    m_block = variable;
-
-    if (!m_pendingComment.isEmpty()) {
-        m_block->setComment(m_pendingComment);
-        m_pendingComment.clear();
-    }
-    m_commentItem = variable;
+void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr)
+{
+    uint len = *tokPtr++;
+    tokPtr += len;
+}
 
-    m_proitem.clear();
+void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr)
+{
+    tokPtr += 2;
+    uint len = *tokPtr++;
+    tokPtr += len;
+}
 
-    if (opkind == ProVariable::ReplaceOperator) {
-        // skip util end of line or comment
-        while (1) {
-            ++(*i);
+// FIXME: this should not build new strings for direct sections.
+// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
+ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals, const ProFile *source)
+{
+    QString build;
+    ProStringList ret;
+    QStack<char> quote;
 
-            // end of line?
-            if (*i >= line.count())
-                break;
+    const ushort SPACE = ' ';
+    const ushort LPAREN = '(';
+    const ushort RPAREN = ')';
+    const ushort SINGLEQUOTE = '\'';
+    const ushort DOUBLEQUOTE = '"';
+    const ushort BACKSLASH = '\\';
 
-            // comment?
-            if (line.at(*i).unicode() == '#') {
-                --(*i);
-                break;
-            }
+    if (!source)
+        source = currentProFile();
+
+    ushort unicode;
+    const QChar *vals_data = vals.data();
+    const int vals_len = vals.length();
+    for (int x = 0, parens = 0; x < vals_len; x++) {
+        unicode = vals_data[x].unicode();
+        if (x != (int)vals_len-1 && unicode == BACKSLASH &&
+            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
+            build += vals_data[x++]; //get that 'escape'
+        } else if (!quote.isEmpty() && unicode == quote.top()) {
+            quote.pop();
+        } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+            quote.push(unicode);
+        } else if (unicode == RPAREN) {
+            --parens;
+        } else if (unicode == LPAREN) {
+            ++parens;
+        }
 
-            m_proitem += line.at(*i);
+        if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
+            ret << ProString(build, NoHash).setSource(source);
+            build.clear();
+        } else {
+            build += vals_data[x];
         }
-        m_proitem = m_proitem.trimmed();
     }
+    if (!build.isEmpty())
+        ret << ProString(build, NoHash).setSource(source);
+    return ret;
 }
 
-void ProFileEvaluator::Private::insertOperator(const char op)
+static void zipEmpty(ProStringList *value)
 {
-    updateItem();
-
-    ProOperator::OperatorKind opkind;
-    switch(op) {
-        case '!':
-            opkind = ProOperator::NotOperator;
-            break;
-        case '|':
-            opkind = ProOperator::OrOperator;
-            break;
-        default:
-            opkind = ProOperator::OrOperator;
-    }
-
-    ProBlock * const block = currentBlock();
-    ProOperator * const proOp = new ProOperator(opkind);
-    proOp->setLineNumber(m_lineNo);
-    block->appendItem(proOp);
-    m_commentItem = proOp;
+    for (int i = value->size(); --i >= 0;)
+        if (value->at(i).isEmpty())
+            value->remove(i);
 }
 
-void ProFileEvaluator::Private::insertComment(const QString &comment)
+static void insertUnique(ProStringList *varlist, const ProStringList &value)
 {
-    updateItem();
-
-    QString strComment;
-    if (!m_commentItem)
-        strComment = m_pendingComment;
-    else
-        strComment = m_commentItem->comment();
-
-    if (strComment.isEmpty())
-        strComment = comment;
-    else {
-        strComment += QLatin1Char('\n');
-        strComment += comment.trimmed();
-    }
-
-    strComment = strComment.trimmed();
-
-    if (!m_commentItem)
-        m_pendingComment = strComment;
-    else
-        m_commentItem->setComment(strComment);
+    foreach (const ProString &str, value)
+        if (!str.isEmpty() && !varlist->contains(str))
+            varlist->append(str);
 }
 
-void ProFileEvaluator::Private::enterScope(bool multiLine)
+static void removeAll(ProStringList *varlist, const ProString &value)
 {
-    updateItem();
-
-    ProBlock *parent = currentBlock();
-    ProBlock *block = new ProBlock(parent);
-    block->setLineNumber(m_lineNo);
-    parent->setBlockKind(ProBlock::ScopeKind);
-
-    parent->appendItem(block);
-
-    if (multiLine)
-        block->setBlockKind(ProBlock::ScopeContentsKind);
-    else
-        block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);
-
-    m_blockstack.push(block);
-    m_block = 0;
+    for (int i = varlist->size(); --i >= 0; )
+        if (varlist->at(i) == value)
+            varlist->remove(i);
 }
 
-void ProFileEvaluator::Private::leaveScope()
+static void removeEach(ProStringList *varlist, const ProStringList &value)
 {
-    updateItem();
-    m_blockstack.pop();
-    finalizeBlock();
+    foreach (const ProString &str, value)
+        if (!str.isEmpty())
+            removeAll(varlist, str);
 }
 
-ProBlock *ProFileEvaluator::Private::currentBlock()
+static void replaceInList(ProStringList *varlist,
+        const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
 {
-    if (m_block)
-        return m_block;
-
-    ProBlock *parent = m_blockstack.top();
-    m_block = new ProBlock(parent);
-    m_block->setLineNumber(m_lineNo);
-    parent->appendItem(m_block);
-
-    if (!m_pendingComment.isEmpty()) {
-        m_block->setComment(m_pendingComment);
-        m_pendingComment.clear();
+    for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
+        QString val = varit->toQString(tmp);
+        QString copy = val; // Force detach and have a reference value
+        val.replace(regexp, replace);
+        if (!val.isSharedWith(copy)) {
+            if (val.isEmpty()) {
+                varit = varlist->erase(varit);
+            } else {
+                (*varit).setValue(val, NoHash);
+                ++varit;
+            }
+            if (!global)
+                break;
+        } else {
+            ++varit;
+        }
     }
+}
 
-    m_commentItem = m_block;
-
-    return m_block;
+QString ProFileEvaluator::Private::expandEnvVars(const QString &str) const
+{
+    QString string = str;
+    int rep;
+    QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
+    while ((rep = reg_variableName.indexIn(string)) != -1)
+        string.replace(rep, reg_variableName.matchedLength(),
+                       m_option->getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3)));
+    return string;
 }
 
-void ProFileEvaluator::Private::updateItem()
+// This is braindead, but we want qmake compat
+QString ProFileEvaluator::Private::fixPathToLocalOS(const QString &str) const
 {
-    m_proitem = m_proitem.trimmed();
-    if (m_proitem.isEmpty())
-        return;
+    QString string = expandEnvVars(str);
 
-    ProBlock *block = currentBlock();
-    if (block->blockKind() & ProBlock::VariableKind) {
-        m_commentItem = new ProValue(m_proitem, static_cast<ProVariable*>(block));
-    } else if (m_proitem.endsWith(QLatin1Char(')'))) {
-        m_commentItem = new ProFunction(m_proitem);
-    } else {
-        m_commentItem = new ProCondition(m_proitem);
-    }
-    m_commentItem->setLineNumber(m_lineNo);
-    block->appendItem(m_commentItem);
+    if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
+        string[0] = string[0].toLower();
 
-    m_proitem.clear();
+#if defined(Q_OS_WIN32)
+    string.replace(QLatin1Char('/'), QLatin1Char('\\'));
+#else
+    string.replace(QLatin1Char('\\'), QLatin1Char('/'));
+#endif
+    return string;
 }
 
+static bool isTrue(const ProString &_str, QString &tmp)
+{
+    const QString &str = _str.toQString(tmp);
+    return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
+}
 
-ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
+//////// Evaluator /////////
+
+static ALWAYS_INLINE void addStr(
+        const ProString &str, ProStringList *ret, bool &pending, bool joined)
 {
-    if (block->blockKind() & ProBlock::ScopeContentsKind) {
-        if (!m_definingFunc.isEmpty()) {
-            if (!m_skipLevel || m_cumulative) {
-                QHash<QString, ProBlock *> *hash =
-                        (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
-                if (ProBlock *def = hash->value(m_definingFunc))
-                    def->deref();
-                hash->insert(m_definingFunc, block);
-                block->ref();
-                block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
-            }
-            m_definingFunc.clear();
-            return ProItem::ReturnSkip;
-        } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
-            if (!m_sts.condition)
-                ++m_skipLevel;
-            else
-                Q_ASSERT(!m_skipLevel);
-        }
+    if (joined) {
+        ret->last().append(str, &pending);
     } else {
-        if (!m_skipLevel) {
-            if (m_sts.condition) {
-                m_sts.prevCondition = true;
-                m_sts.condition = false;
-            }
+        if (!pending) {
+            pending = true;
+            *ret << str;
         } else {
-            Q_ASSERT(!m_sts.condition);
+            ret->last().append(str);
         }
     }
-    return ProItem::ReturnTrue;
 }
 
-void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
+static ALWAYS_INLINE void addStrList(
+        const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
 {
-    if ((block->blockKind() & ProBlock::ScopeContentsKind)
-        && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
-        if (m_skipLevel) {
-            Q_ASSERT(!m_sts.condition);
-            --m_skipLevel;
-        } else if (!(block->blockKind() & ProBlock::SingleLine)) {
-            // Conditionals contained inside this block may have changed the state.
-            // So we reset it here to make an else following us do the right thing.
-            m_sts.condition = true;
+    if (!list.isEmpty()) {
+        if (joined) {
+            ret->last().append(list, &pending, !(tok & TokQuoted));
+        } else {
+            if (tok & TokQuoted) {
+                if (!pending) {
+                    pending = true;
+                    *ret << ProString();
+                }
+                ret->last().append(list);
+            } else {
+                if (!pending) {
+                    // Another qmake bizzarity: if nothing is pending and the
+                    // first element is empty, it will be eaten
+                    if (!list.at(0).isEmpty()) {
+                        // The common case
+                        pending = true;
+                        *ret += list;
+                        return;
+                    }
+                } else {
+                    ret->last().append(list.at(0));
+                }
+                // This is somewhat slow, but a corner case
+                for (int j = 1; j < list.size(); ++j) {
+                    pending = true;
+                    *ret << list.at(j);
+                }
+            }
         }
     }
 }
 
-ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
-{
-    ProLoop &loop = m_loopStack.top();
-
-    if (loop.infinite) {
-        if (!loop.variable.isEmpty())
-            m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
-        if (loop.index > 1000) {
-            q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
-            return ProItem::ReturnFalse;
+void ProFileEvaluator::Private::evaluateExpression(
+        const ushort *&tokPtr, ProStringList *ret, bool joined)
+{
+    if (joined)
+        *ret << ProString();
+    bool pending = false;
+    forever {
+        ushort tok = *tokPtr++;
+        if (tok & TokNewStr)
+            pending = false;
+        ushort maskedTok = tok & TokMask;
+        switch (maskedTok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            break;
+        case TokLiteral:
+            addStr(getStr(tokPtr), ret, pending, joined);
+            break;
+        case TokHashLiteral:
+            addStr(getHashStr(tokPtr), ret, pending, joined);
+            break;
+        case TokVariable:
+            addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
+            break;
+        case TokProperty:
+            addStr(ProString(propertyValue(
+                      getStr(tokPtr).toQString(m_tmp1), true), NoHash).setSource(currentProFile()),
+                   ret, pending, joined);
+            break;
+        case TokEnvVar:
+            addStrList(split_value_list(m_option->getEnv(getStr(tokPtr).toQString(m_tmp1))),
+                       tok, ret, pending, joined);
+            break;
+        case TokFuncName: {
+            ProString func = getHashStr(tokPtr);
+            addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
+            break; }
+        default:
+            tokPtr--;
+            return;
         }
-    } else {
-        QString val;
-        do {
-            if (loop.index >= loop.list.count())
-                return ProItem::ReturnFalse;
-            val = loop.list.at(loop.index++);
-        } while (val.isEmpty()); // stupid, but qmake is like that
-        m_valuemap[loop.variable] = QStringList(val);
     }
-    return ProItem::ReturnTrue;
 }
 
-void ProFileEvaluator::Private::visitProLoopCleanup()
+void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
 {
-    ProLoop &loop = m_loopStack.top();
-    m_valuemap[loop.variable] = loop.oldVarVal;
-    m_loopStack.pop_back();
+    const ushort *tokPtr = pTokPtr;
+    forever {
+        ushort tok = *tokPtr++;
+        switch (tok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            break;
+        case TokValueTerminator:
+        case TokFuncTerminator:
+            pTokPtr = tokPtr;
+            return;
+        case TokArgSeparator:
+            break;
+        default:
+            switch (tok & TokMask) {
+            case TokLiteral:
+            case TokProperty:
+            case TokEnvVar:
+                skipStr(tokPtr);
+                break;
+            case TokHashLiteral:
+            case TokVariable:
+                skipHashStr(tokPtr);
+                break;
+            case TokFuncName:
+                skipHashStr(tokPtr);
+                pTokPtr = tokPtr;
+                skipExpression(pTokPtr);
+                tokPtr = pTokPtr;
+                break;
+            default:
+                Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
+                break;
+            }
+        }
+    }
 }
 
-void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
-{
-    m_lastVarName = variable->variable();
-    m_variableOperator = variable->variableOperator();
-    m_isFirstVariableValue = true;
-    m_tempValuemap = m_valuemap;
-    m_tempFilevaluemap = m_filevaluemap;
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
+        ProFile *pro, const ushort *tokPtr)
+{
+    m_current.pro = pro;
+    m_current.line = 0;
+    return visitProBlock(tokPtr);
+}
+
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
+        const ushort *tokPtr)
+{
+    ProStringList curr;
+    bool okey = true, or_op = false, invert = false;
+    uint blockLen;
+    VisitReturn ret = ReturnTrue;
+    while (ushort tok = *tokPtr++) {
+        switch (tok) {
+        case TokLine:
+            m_current.line = *tokPtr++;
+            continue;
+        case TokAssign:
+        case TokAppend:
+        case TokAppendUnique:
+        case TokRemove:
+        case TokReplace:
+            visitProVariable(tok, curr, tokPtr);
+            curr.clear();
+            continue;
+        case TokBranch:
+            blockLen = getBlockLen(tokPtr);
+            if (m_cumulative) {
+                if (!okey)
+                    m_skipLevel++;
+                ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+                tokPtr += blockLen;
+                blockLen = getBlockLen(tokPtr);
+                if (!okey)
+                    m_skipLevel--;
+                else
+                    m_skipLevel++;
+                if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
+                    ret = visitProBlock(tokPtr);
+                if (okey)
+                    m_skipLevel--;
+            } else {
+                if (okey)
+                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+                tokPtr += blockLen;
+                blockLen = getBlockLen(tokPtr);
+                if (!okey)
+                    ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            break;
+        case TokForLoop:
+            if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
+                skipHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                ret = visitProBlock(tokPtr);
+            } else if (okey != or_op) {
+                const ProString &variable = getHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                const ushort *exprPtr = tokPtr;
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                ret = visitProLoop(variable, exprPtr, tokPtr);
+            } else {
+                skipHashStr(tokPtr);
+                uint exprLen = getBlockLen(tokPtr);
+                tokPtr += exprLen;
+                blockLen = getBlockLen(tokPtr);
+                ret = ReturnTrue;
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            break;
+        case TokTestDef:
+        case TokReplaceDef:
+            if (m_cumulative || okey != or_op) {
+                const ProString &name = getHashStr(tokPtr);
+                blockLen = getBlockLen(tokPtr);
+                visitProFunctionDef(tok, name, tokPtr);
+            } else {
+                skipHashStr(tokPtr);
+                blockLen = getBlockLen(tokPtr);
+            }
+            tokPtr += blockLen;
+            okey = true, or_op = false; // force next evaluation
+            continue;
+        case TokNot:
+            invert ^= true;
+            continue;
+        case TokAnd:
+            or_op = false;
+            continue;
+        case TokOr:
+            or_op = true;
+            continue;
+        case TokCondition:
+            if (!m_skipLevel && okey != or_op) {
+                if (curr.size() != 1) {
+                    if (!m_cumulative || !curr.isEmpty())
+                        evalError(fL1S("Conditional must expand to exactly one word."));
+                    okey = false;
+                } else {
+                    okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
+                }
+            }
+            or_op = !okey; // tentatively force next evaluation
+            invert = false;
+            curr.clear();
+            continue;
+        case TokTestCall:
+            if (!m_skipLevel && okey != or_op) {
+                if (curr.size() != 1) {
+                    if (!m_cumulative || !curr.isEmpty())
+                        evalError(fL1S("Test name must expand to exactly one word."));
+                    skipExpression(tokPtr);
+                    okey = false;
+                } else {
+                    ret = evaluateConditionalFunction(curr.at(0), tokPtr);
+                    switch (ret) {
+                    case ReturnTrue: okey = true; break;
+                    case ReturnFalse: okey = false; break;
+                    default: return ret;
+                    }
+                    okey ^= invert;
+                }
+            } else if (m_cumulative) {
+                m_skipLevel++;
+                if (curr.size() != 1)
+                    skipExpression(tokPtr);
+                else
+                    evaluateConditionalFunction(curr.at(0), tokPtr);
+                m_skipLevel--;
+            } else {
+                skipExpression(tokPtr);
+            }
+            or_op = !okey; // tentatively force next evaluation
+            invert = false;
+            curr.clear();
+            continue;
+        default: {
+                const ushort *oTokPtr = --tokPtr;
+                evaluateExpression(tokPtr, &curr, false);
+                if (tokPtr != oTokPtr)
+                    continue;
+            }
+            Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
+        }
+        if (ret != ReturnTrue && ret != ReturnFalse)
+            break;
+    }
+    return ret;
 }
 
-void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
-{
-    Q_UNUSED(variable);
-    m_valuemap = m_tempValuemap;
-    m_filevaluemap = m_tempFilevaluemap;
-    m_lastVarName.clear();
-}
 
-void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
+void ProFileEvaluator::Private::visitProFunctionDef(
+        ushort tok, const ProString &name, const ushort *tokPtr)
 {
-    m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
+    QHash<ProString, FunctionDef> *hash =
+            (tok == TokTestDef
+             ? &m_functionDefs.testFunctions
+             : &m_functionDefs.replaceFunctions);
+    hash->insert(name, FunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
 }
 
-void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(
+        const ProString &_variable, const ushort *exprPtr, const ushort *tokPtr)
 {
-    if (!m_skipLevel) {
-        if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
-            m_sts.condition = !m_sts.prevCondition;
-        } else {
-            m_sts.prevCondition = false;
-            if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
-                m_sts.condition = true;
+    VisitReturn ret = ReturnTrue;
+    bool infinite = false;
+    int index = 0;
+    ProString variable;
+    ProStringList oldVarVal;
+    ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
+    if (_variable.isEmpty()) {
+        if (it_list != statics.strever) {
+            evalError(fL1S("Invalid loop expression."));
+            return ReturnFalse;
         }
+        it_list = ProString(statics.strforever);
+    } else {
+        variable = map(_variable);
+        oldVarVal = valuesDirect(variable);
     }
-    m_invertNext = false;
-}
-
-ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
-{
-    PRE(pro);
-    m_lineNo = pro->lineNumber();
-    if (m_origfile.isEmpty())
-        m_origfile = pro->fileName();
-    if (m_oldPath.isEmpty()) {
-        // change the working directory for the initial profile we visit, since
-        // that is *the* profile. All the other times we reach this function will be due to
-        // include(file) or load(file)
-
-        m_oldPath = QDir::currentPath();
-
-        m_profileStack.push(pro);
-
-        const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
-        if (!mkspecDirectory.isEmpty()) {
-            bool cumulative = m_cumulative;
-            m_cumulative = false;
-            // This is what qmake does, everything set in the mkspec is also set
-            // But this also creates a lot of problems
-            evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"));
-            evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"));
-            m_cumulative = cumulative;
-        }
-
-        return returnBool(QDir::setCurrent(pro->directoryName()));
-    }
-
-    return ProItem::ReturnTrue;
-}
-
-ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
-{
-    PRE(pro);
-    m_lineNo = pro->lineNumber();
-    if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
-        const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
-        if (!mkspecDirectory.isEmpty()) {
-            bool cumulative = m_cumulative;
-            m_cumulative = false;
-
-            evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"));
-
-            QSet<QString> processed;
-            forever {
-                bool finished = true;
-                QStringList configs = valuesDirect(QLatin1String("CONFIG"));
-                for (int i = configs.size() - 1; i >= 0; --i) {
-                    const QString config = configs[i].toLower();
-                    if (!processed.contains(config)) {
-                        processed.insert(config);
-                        if (evaluateFile(mkspecDirectory + QLatin1String("/features/")
-                                         + config + QLatin1String(".prf"))) {
-                            finished = false;
-                            break;
+    ProStringList list = valuesDirect(it_list);
+    if (list.isEmpty()) {
+        if (it_list == statics.strforever) {
+            infinite = true;
+        } else {
+            const QString &itl = it_list.toQString(m_tmp1);
+            int dotdot = itl.indexOf(statics.strDotDot);
+            if (dotdot != -1) {
+                bool ok;
+                int start = itl.left(dotdot).toInt(&ok);
+                if (ok) {
+                    int end = itl.mid(dotdot+2).toInt(&ok);
+                    if (ok) {
+                        if (start < end) {
+                            for (int i = start; i <= end; i++)
+                                list << ProString(QString::number(i), NoHash);
+                        } else {
+                            for (int i = start; i >= end; i--)
+                                list << ProString(QString::number(i), NoHash);
                         }
                     }
                 }
-                if (finished)
-                    break;
             }
-
-            foreach (ProBlock *itm, m_replaceFunctions)
-                itm->deref();
-            m_replaceFunctions.clear();
-            foreach (ProBlock *itm, m_testFunctions)
-                itm->deref();
-            m_testFunctions.clear();
-
-            m_cumulative = cumulative;
         }
-
-        m_profileStack.pop();
-        return returnBool(QDir::setCurrent(m_oldPath));
     }
 
-    return ProItem::ReturnTrue;
-}
-
-static void replaceInList(QStringList *varlist,
-        const QRegExp &regexp, const QString &replace, bool global)
-{
-    for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
-        if ((*varit).contains(regexp)) {
-            (*varit).replace(regexp, replace);
-            if ((*varit).isEmpty())
-                varit = varlist->erase(varit);
-            else
-                ++varit;
-            if(!global)
+    m_loopLevel++;
+    forever {
+        if (infinite) {
+            if (!variable.isEmpty())
+                m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++), NoHash));
+            if (index > 1000) {
+                evalError(fL1S("ran into infinite loop (> 1000 iterations)."));
                 break;
+            }
         } else {
-            ++varit;
+            ProString val;
+            do {
+                if (index >= list.count())
+                    goto do_break;
+                val = list.at(index++);
+            } while (val.isEmpty()); // stupid, but qmake is like that
+            m_valuemapStack.top()[variable] = ProStringList(val);
+        }
+
+        ret = visitProBlock(tokPtr);
+        switch (ret) {
+        case ReturnTrue:
+        case ReturnFalse:
+            break;
+        case ReturnNext:
+            ret = ReturnTrue;
+            break;
+        case ReturnBreak:
+            ret = ReturnTrue;
+            goto do_break;
+        default:
+            goto do_break;
         }
     }
+  do_break:
+    m_loopLevel--;
+
+    if (!variable.isEmpty())
+        m_valuemapStack.top()[variable] = oldVarVal;
+    return ret;
 }
 
-void ProFileEvaluator::Private::visitProValue(ProValue *value)
+void ProFileEvaluator::Private::visitProVariable(
+        ushort tok, const ProStringList &curr, const ushort *&tokPtr)
 {
-    PRE(value);
-    m_lineNo = value->lineNumber();
-    QString val = value->value();
+    int sizeHint = *tokPtr++;
 
-    QString varName = m_lastVarName;
+    if (curr.size() != 1) {
+        skipExpression(tokPtr);
+        if (!m_cumulative || !curr.isEmpty())
+            evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
+        return;
+    }
+    const ProString &varName = map(curr.first());
 
-    QStringList v = expandVariableReferences(val);
+    if (tok == TokReplace) {      // ~=
+        // DEFINES ~= s/a/b/?[gqi]
 
-    // Since qmake combines different values for the TARGET variable, we join
-    // TARGET values that are on the same line. We can't do this later with all
-    // values because this parser isn't scope-aware, so we'd risk joining
-    // scope-specific targets together.
-    if (varName == QLatin1String("TARGET")
-            && m_lineNo == m_prevLineNo
-            && currentProFile() == m_prevProFile) {
-        QStringList targets = m_tempValuemap.value(QLatin1String("TARGET"));
-        m_tempValuemap.remove(QLatin1String("TARGET"));
-        QStringList lastTarget(targets.takeLast());
-        lastTarget << v.join(QLatin1String(" "));
-        targets.push_back(lastTarget.join(QLatin1String(" ")));
-        v = targets;
-    }
-    m_prevLineNo = m_lineNo;
-    m_prevProFile = currentProFile();
+        const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
+        const QString &val = varVal.at(0).toQString(m_tmp1);
+        if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
+            evalError(fL1S("the ~= operator can handle only the s/// function."));
+            return;
+        }
+        QChar sep = val.at(1);
+        QStringList func = val.split(sep);
+        if (func.count() < 3 || func.count() > 4) {
+            evalError(fL1S("the s/// function expects 3 or 4 arguments."));
+            return;
+        }
+
+        bool global = false, quote = false, case_sense = false;
+        if (func.count() == 4) {
+            global = func[3].indexOf(QLatin1Char('g')) != -1;
+            case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
+            quote = func[3].indexOf(QLatin1Char('q')) != -1;
+        }
+        QString pattern = func[1];
+        QString replace = func[2];
+        if (quote)
+            pattern = QRegExp::escape(pattern);
+
+        QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
 
-    switch (m_variableOperator) {
-        case ProVariable::SetOperator:          // =
+        if (!m_skipLevel || m_cumulative) {
+            // We could make a union of modified and unmodified values,
+            // but this will break just as much as it fixes, so leave it as is.
+            replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
+        }
+    } else {
+        ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
+        switch (tok) {
+        default: // whatever - cannot happen
+        case TokAssign:          // =
             if (!m_cumulative) {
                 if (!m_skipLevel) {
-                    if (m_isFirstVariableValue) {
-                        m_tempValuemap[varName] = v;
-                        m_tempFilevaluemap[currentProFile()][varName] = v;
-                    } else { // handle lines "CONFIG = foo bar"
-                        m_tempValuemap[varName] += v;
-                        m_tempFilevaluemap[currentProFile()][varName] += v;
-                    }
+                    zipEmpty(&varVal);
+                    m_valuemapStack.top()[varName] = varVal;
                 }
             } else {
-                // We are greedy for values.
-                m_tempValuemap[varName] += v;
-                m_tempFilevaluemap[currentProFile()][varName] += v;
+                zipEmpty(&varVal);
+                if (!varVal.isEmpty()) {
+                    // We are greedy for values. But avoid exponential growth.
+                    ProStringList &v = valuesRef(varName);
+                    if (v.isEmpty()) {
+                        v = varVal;
+                    } else {
+                        ProStringList old = v;
+                        v = varVal;
+                        QSet<ProString> has;
+                        has.reserve(v.size());
+                        foreach (const ProString &s, v)
+                            has.insert(s);
+                        v.reserve(v.size() + old.size());
+                        foreach (const ProString &s, old)
+                            if (!has.contains(s))
+                                v << s;
+                    }
+                }
             }
             break;
-        case ProVariable::UniqueAddOperator:    // *=
-            if (!m_skipLevel || m_cumulative) {
-                insertUnique(&m_tempValuemap, varName, v);
-                insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v);
-            }
+        case TokAppendUnique:    // *=
+            if (!m_skipLevel || m_cumulative)
+                insertUnique(&valuesRef(varName), varVal);
             break;
-        case ProVariable::AddOperator:          // +=
+        case TokAppend:          // +=
             if (!m_skipLevel || m_cumulative) {
-                m_tempValuemap[varName] += v;
-                m_tempFilevaluemap[currentProFile()][varName] += v;
+                zipEmpty(&varVal);
+                valuesRef(varName) += varVal;
             }
             break;
-        case ProVariable::RemoveOperator:       // -=
+        case TokRemove:       // -=
             if (!m_cumulative) {
-                if (!m_skipLevel) {
-                    removeEach(&m_tempValuemap, varName, v);
-                    removeEach(&m_tempFilevaluemap[currentProFile()], varName, v);
-                }
+                if (!m_skipLevel)
+                    removeEach(&valuesRef(varName), varVal);
             } else {
                 // We are stingy with our values, too.
             }
             break;
-        case ProVariable::ReplaceOperator:      // ~=
-            {
-                // DEFINES ~= s/a/b/?[gqi]
+        }
+    }
+}
 
-                doVariableReplace(&val);
-                if (val.length() < 4 || val[0] != QLatin1Char('s')) {
-                    q->logMessage(format("the ~= operator can handle only the s/// function."));
-                    break;
+void ProFileEvaluator::Private::visitCmdLine(const QString &cmds)
+{
+    if (!cmds.isEmpty()) {
+        if (ProFile *pro = m_parser->parsedProBlock(fL1S("(command line)"), cmds)) {
+            m_locationStack.push(m_current);
+            visitProBlock(pro, pro->tokPtr());
+            m_current = m_locationStack.pop();
+            pro->deref();
+        }
+    }
+}
+
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(
+        ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
+        ProFileEvaluator::LoadFlags flags)
+{
+    if (!m_cumulative && !pro->isOk())
+        return ReturnFalse;
+
+    m_handler->aboutToEval(currentProFile(), pro, type);
+    m_profileStack.push(pro);
+    if (flags & LoadPreFiles) {
+#ifdef PROEVALUATOR_THREAD_SAFE
+        {
+            QMutexLocker locker(&m_option->mutex);
+            if (m_option->base_inProgress) {
+                QThreadPool::globalInstance()->releaseThread();
+                m_option->cond.wait(&m_option->mutex);
+                QThreadPool::globalInstance()->reserveThread();
+            } else
+#endif
+            if (m_option->base_valuemap.isEmpty()) {
+#ifdef PROEVALUATOR_THREAD_SAFE
+                m_option->base_inProgress = true;
+                locker.unlock();
+#endif
+
+#ifdef PROEVALUATOR_CUMULATIVE
+                bool cumulative = m_cumulative;
+                m_cumulative = false;
+#endif
+
+                // ### init QMAKE_QMAKE, QMAKE_SH
+                // ### init QMAKE_EXT_{C,H,CPP,OBJ}
+                // ### init TEMPLATE_PREFIX
+
+                QString qmake_cache = m_option->cachefile;
+                if (qmake_cache.isEmpty() && !m_outputDir.isEmpty())  { //find it as it has not been specified
+                    QDir dir(m_outputDir);
+                    forever {
+                        qmake_cache = dir.path() + QLatin1String("/.qmake.cache");
+                        if (IoUtils::exists(qmake_cache))
+                            break;
+                        if (!dir.cdUp() || dir.isRoot()) {
+                            qmake_cache.clear();
+                            break;
+                        }
+                    }
                 }
-                QChar sep = val.at(1);
-                QStringList func = val.split(sep);
-                if (func.count() < 3 || func.count() > 4) {
-                    q->logMessage(format("the s/// function expects 3 or 4 arguments."));
-                    break;
+                if (!qmake_cache.isEmpty()) {
+                    qmake_cache = resolvePath(qmake_cache);
+                    QHash<ProString, ProStringList> cache_valuemap;
+                    if (evaluateFileInto(qmake_cache, ProFileEvaluatorHandler::EvalConfigFile,
+                                         &cache_valuemap, 0, EvalProOnly)) {
+                        if (m_option->qmakespec.isEmpty()) {
+                            const ProStringList &vals = cache_valuemap.value(ProString("QMAKESPEC"));
+                            if (!vals.isEmpty())
+                                m_option->qmakespec = vals.first().toQString();
+                        }
+                    } else {
+                        qmake_cache.clear();
+                    }
+                }
+                m_option->cachefile = qmake_cache;
+
+                QStringList mkspec_roots = qmakeMkspecPaths();
+
+                QString qmakespec = expandEnvVars(m_option->qmakespec);
+                if (qmakespec.isEmpty()) {
+                    foreach (const QString &root, mkspec_roots) {
+                        QString mkspec = root + QLatin1String("/default");
+                        if (IoUtils::fileType(mkspec) == IoUtils::FileIsDir) {
+                            qmakespec = mkspec;
+                            break;
+                        }
+                    }
+                    if (qmakespec.isEmpty()) {
+                        m_handler->configError(fL1S("Could not find qmake configuration directory"));
+                        // Unlike in qmake, not finding the spec is not critical ...
+                    }
                 }
 
-                bool global = false, quote = false, case_sense = false;
-                if (func.count() == 4) {
-                    global = func[3].indexOf(QLatin1Char('g')) != -1;
-                    case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
-                    quote = func[3].indexOf(QLatin1Char('q')) != -1;
+                if (IoUtils::isRelativePath(qmakespec)) {
+                    if (IoUtils::exists(currentDirectory() + QLatin1Char('/') + qmakespec
+                                        + QLatin1String("/qmake.conf"))) {
+                        qmakespec = currentDirectory() + QLatin1Char('/') + qmakespec;
+                    } else if (!m_outputDir.isEmpty()
+                               && IoUtils::exists(m_outputDir + QLatin1Char('/') + qmakespec
+                                                  + QLatin1String("/qmake.conf"))) {
+                        qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
+                    } else {
+                        foreach (const QString &root, mkspec_roots) {
+                            QString mkspec = root + QLatin1Char('/') + qmakespec;
+                            if (IoUtils::exists(mkspec)) {
+                                qmakespec = mkspec;
+                                goto cool;
+                            }
+                        }
+                        m_handler->configError(fL1S("Could not find qmake configuration file"));
+                        // Unlike in qmake, a missing config is not critical ...
+                        qmakespec.clear();
+                      cool: ;
+                    }
                 }
-                QString pattern = func[1];
-                QString replace = func[2];
-                if (quote)
-                    pattern = QRegExp::escape(pattern);
-
-                QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
-
-                if (!m_skipLevel || m_cumulative) {
-                    // We could make a union of modified and unmodified values,
-                    // but this will break just as much as it fixes, so leave it as is.
-                    replaceInList(&m_tempValuemap[varName], regexp, replace, global);
-                    replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global);
+
+                if (!qmakespec.isEmpty()) {
+                    m_option->qmakespec = QDir::cleanPath(qmakespec);
+
+                    QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
+                    if (!evaluateFileDirect(spec, ProFileEvaluatorHandler::EvalConfigFile,
+                                            ProFileEvaluator::LoadProOnly)) {
+                        m_handler->configError(
+                                fL1S("Could not read qmake configuration file %1").arg(spec));
+                    } else if (!m_option->cachefile.isEmpty()) {
+                        evaluateFileDirect(m_option->cachefile,
+                                           ProFileEvaluatorHandler::EvalConfigFile,
+                                           ProFileEvaluator::LoadProOnly);
+                    }
+                    m_option->qmakespec_name = IoUtils::fileName(m_option->qmakespec).toString();
+                    if (m_option->qmakespec_name == QLatin1String("default")) {
+#ifdef Q_OS_UNIX
+                        char buffer[1024];
+                        int l = ::readlink(m_option->qmakespec.toLocal8Bit().constData(), buffer, 1024);
+                        if (l != -1)
+                            m_option->qmakespec_name =
+                                    IoUtils::fileName(QString::fromLocal8Bit(buffer, l)).toString();
+#else
+                        // We can't resolve symlinks as they do on Unix, so configure.exe puts
+                        // the source of the qmake.conf at the end of the default/qmake.conf in
+                        // the QMAKESPEC_ORG variable.
+                        const ProStringList &spec_org =
+                                m_option->base_valuemap.value(ProString("QMAKESPEC_ORIGINAL"));
+                        if (!spec_org.isEmpty())
+                            m_option->qmakespec_name =
+                                    IoUtils::fileName(spec_org.first().toQString()).toString();
+#endif
+                    }
                 }
+
+                evaluateFeatureFile(QLatin1String("default_pre.prf"));
+
+                m_option->base_valuemap = m_valuemapStack.top();
+                m_option->base_functions = m_functionDefs;
+
+#ifdef PROEVALUATOR_CUMULATIVE
+                m_cumulative = cumulative;
+#endif
+
+#ifdef PROEVALUATOR_THREAD_SAFE
+                locker.relock();
+                m_option->base_inProgress = false;
+                m_option->cond.wakeAll();
+#endif
+                goto fresh;
             }
-            break;
+#ifdef PROEVALUATOR_THREAD_SAFE
+        }
+#endif
+
+            m_valuemapStack.top() = m_option->base_valuemap;
+            m_functionDefs = m_option->base_functions;
+
+          fresh:
+            ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")];
+            if (tgt.isEmpty())
+                tgt.append(ProString(QFileInfo(pro->fileName()).baseName(), NoHash));
 
+        visitCmdLine(m_option->precmds);
     }
-    m_isFirstVariableValue = false;
-}
 
-ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
-{
-    // Make sure that called subblocks don't inherit & destroy the state
-    bool invertThis = m_invertNext;
-    m_invertNext = false;
-    if (!m_skipLevel)
-        m_sts.prevCondition = false;
-    if (m_cumulative || !m_sts.condition) {
-        QString text = func->text();
-        int lparen = text.indexOf(QLatin1Char('('));
-        int rparen = text.lastIndexOf(QLatin1Char(')'));
-        Q_ASSERT(lparen < rparen);
-        QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
-        QString funcName = text.left(lparen);
-        m_lineNo = func->lineNumber();
-        ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
-        if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
-            return result;
-        if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
-            m_sts.condition = true;
+    visitProBlock(pro, pro->tokPtr());
+
+    if (flags & LoadPostFiles) {
+        visitCmdLine(m_option->postcmds);
+
+            evaluateFeatureFile(QLatin1String("default_post.prf"));
+
+            QSet<QString> processed;
+            forever {
+                bool finished = true;
+                ProStringList configs = valuesDirect(statics.strCONFIG);
+                for (int i = configs.size() - 1; i >= 0; --i) {
+                    QString config = configs.at(i).toQString(m_tmp1).toLower();
+                    if (!processed.contains(config)) {
+                        config.detach();
+                        processed.insert(config);
+                        if (evaluateFeatureFile(config)) {
+                            finished = false;
+                            break;
+                        }
+                    }
+                }
+                if (finished)
+                    break;
+            }
     }
-    return ProItem::ReturnTrue;
+    m_profileStack.pop();
+    m_handler->doneWithEval(currentProFile());
+
+    return ReturnTrue;
 }
 
 
-QStringList ProFileEvaluator::Private::qmakeFeaturePaths()
+QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
+{
+    QStringList ret;
+    const QString concat = QLatin1String("/mkspecs");
+
+    QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
+    if (!qmakepath.isEmpty())
+        foreach (const QString &it, qmakepath.split(m_option->dirlist_sep))
+            ret << QDir::cleanPath(it) + concat;
+
+    QString builtIn = propertyValue(QLatin1String("QT_INSTALL_DATA"), false) + concat;
+    if (!ret.contains(builtIn))
+        ret << builtIn;
+
+    return ret;
+}
+
+QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
 {
+    QString mkspecs_concat = QLatin1String("/mkspecs");
+    QString features_concat = QLatin1String("/features");
     QStringList concat;
-    {
-        const QString base_concat = QDir::separator() + QLatin1String("features");
-        concat << base_concat + QDir::separator() + QLatin1String("mac");
-        concat << base_concat + QDir::separator() + QLatin1String("macx");
-        concat << base_concat + QDir::separator() + QLatin1String("unix");
-        concat << base_concat + QDir::separator() + QLatin1String("win32");
-        concat << base_concat + QDir::separator() + QLatin1String("mac9");
-        concat << base_concat + QDir::separator() + QLatin1String("qnx6");
-        concat << base_concat;
+
+    validateModes();
+    switch (m_option->target_mode) {
+    case ProFileOption::TARG_MACX_MODE:
+        concat << QLatin1String("/features/mac");
+        concat << QLatin1String("/features/macx");
+        concat << QLatin1String("/features/unix");
+        break;
+    default: // Can't happen, just make the compiler shut up
+    case ProFileOption::TARG_UNIX_MODE:
+        concat << QLatin1String("/features/unix");
+        break;
+    case ProFileOption::TARG_WIN_MODE:
+        concat << QLatin1String("/features/win32");
+        break;
+    case ProFileOption::TARG_SYMBIAN_MODE:
+        concat << QLatin1String("/features/symbian");
+        break;
     }
-    const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs");
+    concat << features_concat;
+
     QStringList feature_roots;
-    QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
-    if (!mkspec_path.isNull())
-        feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
-    /*
-    if (prop)
-        feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
-    if (!Option::mkfile::cachefile.isEmpty()) {
-        QString path;
-        int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
-        if (last_slash != -1)
-            path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
+
+    QString mkspec_path = m_option->getEnv(QLatin1String("QMAKEFEATURES"));
+    if (!mkspec_path.isEmpty())
+        foreach (const QString &f, mkspec_path.split(m_option->dirlist_sep))
+            feature_roots += resolvePath(f);
+
+    feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
+            m_option->dirlist_sep, QString::SkipEmptyParts);
+
+    if (!m_option->cachefile.isEmpty()) {
+        QString path = m_option->cachefile.left(m_option->cachefile.lastIndexOf((ushort)'/'));
         foreach (const QString &concat_it, concat)
             feature_roots << (path + concat_it);
     }
-    */
 
-    QByteArray qmakepath = qgetenv("QMAKEPATH");
+    QString qmakepath = m_option->getEnv(QLatin1String("QMAKEPATH"));
     if (!qmakepath.isNull()) {
-        const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
+        const QStringList lst = qmakepath.split(m_option->dirlist_sep);
         foreach (const QString &item, lst) {
+            QString citem = resolvePath(item);
             foreach (const QString &concat_it, concat)
-                feature_roots << (item + mkspecs_concat + concat_it);
+                feature_roots << (citem + mkspecs_concat + concat_it);
         }
     }
-    //if (!Option::mkfile::qmakespec.isEmpty())
-    //    feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
-    //if (!Option::mkfile::qmakespec.isEmpty()) {
-    //    QFileInfo specfi(Option::mkfile::qmakespec);
-    //    QDir specdir(specfi.absoluteFilePath());
-    //    while (!specdir.isRoot()) {
-    //        if (!specdir.cdUp() || specdir.isRoot())
-    //            break;
-    //        if (QFile::exists(specdir.path() + QDir::separator() + "features")) {
-    //            foreach (const QString &concat_it, concat)
-    //                feature_roots << (specdir.path() + concat_it);
-    //            break;
-    //        }
-    //    }
-    //}
+
+    if (!m_option->qmakespec.isEmpty()) {
+        QString qmakespec = resolvePath(m_option->qmakespec);
+        feature_roots << (qmakespec + features_concat);
+
+        QDir specdir(qmakespec);
+        while (!specdir.isRoot()) {
+            if (!specdir.cdUp() || specdir.isRoot())
+                break;
+            if (IoUtils::exists(specdir.path() + features_concat)) {
+                foreach (const QString &concat_it, concat)
+                    feature_roots << (specdir.path() + concat_it);
+                break;
+            }
+        }
+    }
+
     foreach (const QString &concat_it, concat)
-        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
+        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX"), false) +
                           mkspecs_concat + concat_it);
     foreach (const QString &concat_it, concat)
-        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
+        feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA"), false) +
                           mkspecs_concat + concat_it);
+
+    for (int i = 0; i < feature_roots.count(); ++i)
+        if (!feature_roots.at(i).endsWith((ushort)'/'))
+            feature_roots[i].append((ushort)'/');
+
+    feature_roots.removeDuplicates();
+
     return feature_roots;
 }
 
-QString ProFileEvaluator::Private::propertyValue(const QString &name) const
-{
-    if (m_properties.contains(name))
-        return m_properties.value(name);
-    if (name == QLatin1String("QT_INSTALL_PREFIX"))
-        return QLibraryInfo::location(QLibraryInfo::PrefixPath);
-    if (name == QLatin1String("QT_INSTALL_DATA"))
-        return QLibraryInfo::location(QLibraryInfo::DataPath);
-    if (name == QLatin1String("QT_INSTALL_DOCS"))
-        return QLibraryInfo::location(QLibraryInfo::DocumentationPath);
-    if (name == QLatin1String("QT_INSTALL_HEADERS"))
-        return QLibraryInfo::location(QLibraryInfo::HeadersPath);
-    if (name == QLatin1String("QT_INSTALL_LIBS"))
-        return QLibraryInfo::location(QLibraryInfo::LibrariesPath);
-    if (name == QLatin1String("QT_INSTALL_BINS"))
-        return QLibraryInfo::location(QLibraryInfo::BinariesPath);
-    if (name == QLatin1String("QT_INSTALL_PLUGINS"))
-        return QLibraryInfo::location(QLibraryInfo::PluginsPath);
-    if (name == QLatin1String("QT_INSTALL_TRANSLATIONS"))
-        return QLibraryInfo::location(QLibraryInfo::TranslationsPath);
-    if (name == QLatin1String("QT_INSTALL_CONFIGURATION"))
-        return QLibraryInfo::location(QLibraryInfo::SettingsPath);
-    if (name == QLatin1String("QT_INSTALL_EXAMPLES"))
-        return QLibraryInfo::location(QLibraryInfo::ExamplesPath);
+QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
+{
+    if (m_option->properties.contains(name))
+        return m_option->properties.value(name);
     if (name == QLatin1String("QMAKE_MKSPECS"))
-        return qmake_mkspec_paths().join(Option::dirlist_sep);
+        return qmakeMkspecPaths().join(m_option->dirlist_sep);
     if (name == QLatin1String("QMAKE_VERSION"))
         return QLatin1String("1.0");        //### FIXME
-        //return qmake_version();
-#ifdef QT_VERSION_STR
-    if (name == QLatin1String("QT_VERSION"))
-        return QLatin1String(QT_VERSION_STR);
-#endif
-    return QLatin1String("UNKNOWN");        //###
+    if (complain)
+        evalError(fL1S("Querying unknown property %1").arg(name));
+    return QString();
 }
 
 ProFile *ProFileEvaluator::Private::currentProFile() const
@@ -1055,17 +1568,114 @@ QString ProFileEvaluator::Private::currentDirectory() const
     return cur->directoryName();
 }
 
-void ProFileEvaluator::Private::doVariableReplace(QString *str)
+QString ProFileEvaluator::Private::sysrootify(const QString &path, const QString &baseDir) const
 {
-    *str = expandVariableReferences(*str).join(QString(Option::field_sep));
+    const bool isHostSystemPath = m_option->sysroot.isEmpty() || path.startsWith(m_option->sysroot)
+        || path.startsWith(baseDir) || path.startsWith(m_outputDir);
+    return isHostSystemPath ? path : m_option->sysroot + path;
 }
 
-QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
+#ifndef QT_BOOTSTRAPPED
+void ProFileEvaluator::Private::runProcess(QProcess *proc, const QString &command,
+                                           QProcess::ProcessChannel chan) const
 {
-    QStringList ret;
+    proc->setWorkingDirectory(currentDirectory());
+    if (!m_option->environment.isEmpty())
+        proc->setProcessEnvironment(m_option->environment);
+# ifdef Q_OS_WIN
+    proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
+    proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
+# else
+    proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
+# endif
+    proc->waitForFinished(-1);
+    proc->setReadChannel(chan);
+    QByteArray errout = proc->readAll();
+    if (errout.endsWith('\n'))
+        errout.chop(1);
+    m_handler->evalError(QString(), 0, QString::fromLocal8Bit(errout));
+}
+#endif
+
+// The (QChar*)current->constData() constructs below avoid pointless detach() calls
+// FIXME: This is inefficient. Should not make new string if it is a straight subsegment
+static ALWAYS_INLINE void appendChar(ushort unicode,
+    QString *current, QChar **ptr, ProString *pending)
+{
+    if (!pending->isEmpty()) {
+        int len = pending->size();
+        current->resize(current->size() + len);
+        ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
+        pending->clear();
+        *ptr = (QChar*)current->constData() + len;
+    }
+    *(*ptr)++ = QChar(unicode);
+}
+
+static void appendString(const ProString &string,
+    QString *current, QChar **ptr, ProString *pending)
+{
+    if (string.isEmpty())
+        return;
+    QChar *uc = (QChar*)current->constData();
+    int len;
+    if (*ptr != uc) {
+        len = *ptr - uc;
+        current->resize(current->size() + string.size());
+    } else if (!pending->isEmpty()) {
+        len = pending->size();
+        current->resize(current->size() + len + string.size());
+        ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
+        pending->clear();
+    } else {
+        *pending = string;
+        return;
+    }
+    *ptr = (QChar*)current->constData() + len;
+    ::memcpy(*ptr, string.constData(), string.size() * 2);
+    *ptr += string.size();
+}
+
+static void flushCurrent(ProStringList *ret,
+    QString *current, QChar **ptr, ProString *pending, bool joined)
+{
+    QChar *uc = (QChar*)current->constData();
+    int len = *ptr - uc;
+    if (len) {
+        ret->append(ProString(QString(uc, len), NoHash));
+        *ptr = uc;
+    } else if (!pending->isEmpty()) {
+        ret->append(*pending);
+        pending->clear();
+    } else if (joined) {
+        ret->append(ProString());
+    }
+}
+
+static inline void flushFinal(ProStringList *ret,
+    const QString &current, const QChar *ptr, const ProString &pending,
+    const ProString &str, bool replaced, bool joined)
+{
+    int len = ptr - current.data();
+    if (len) {
+        if (!replaced && len == str.size())
+            ret->append(str);
+        else
+            ret->append(ProString(QString(current.data(), len), NoHash));
+    } else if (!pending.isEmpty()) {
+        ret->append(pending);
+    } else if (joined) {
+        ret->append(ProString());
+    }
+}
+
+ProStringList ProFileEvaluator::Private::expandVariableReferences(
+    const ProString &str, int *pos, bool joined)
+{
+    ProStringList ret;
 //    if (ok)
 //        *ok = true;
-    if (str.isEmpty())
+    if (str.isEmpty() && !pos)
         return ret;
 
     const ushort LSQUARE = '[';
@@ -1080,42 +1690,45 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &s
     const ushort DOT = '.';
     const ushort SPACE = ' ';
     const ushort TAB = '\t';
+    const ushort COMMA = ',';
     const ushort SINGLEQUOTE = '\'';
     const ushort DOUBLEQUOTE = '"';
 
-    ushort unicode, quote = 0;
-    const QChar *str_data = str.data();
-    const int str_len = str.length();
-
-    ushort term;
-    QString var, args;
-
-    int replaced = 0;
-    QString current;
-    for (int i = 0; i < str_len; ++i) {
-        unicode = str_data[i].unicode();
-        const int start_var = i;
-        if (unicode == DOLLAR && str_len > i+2) {
-            unicode = str_data[++i].unicode();
-            if (unicode == DOLLAR) {
-                term = 0;
-                var.clear();
-                args.clear();
+    ushort unicode, quote = 0, parens = 0;
+    const ushort *str_data = (const ushort *)str.constData();
+    const int str_len = str.size();
+
+    ProString var, args;
+
+    bool replaced = false;
+    bool putSpace = false;
+    QString current; // Buffer for successively assembled string segments
+    current.resize(str.size());
+    QChar *ptr = current.data();
+    ProString pending; // Buffer for string segments from variables
+    // Only one of the above buffers can be filled at a given time.
+    for (int i = pos ? *pos : 0; i < str_len; ++i) {
+        unicode = str_data[i];
+        if (unicode == DOLLAR) {
+            if (str_len > i+2 && str_data[i+1] == DOLLAR) {
+                ++i;
+                ushort term = 0;
                 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
-                unicode = str_data[++i].unicode();
+                unicode = str_data[++i];
                 if (unicode == LSQUARE) {
-                    unicode = str_data[++i].unicode();
+                    unicode = str_data[++i];
                     term = RSQUARE;
                     var_type = PROPERTY;
                 } else if (unicode == LCURLY) {
-                    unicode = str_data[++i].unicode();
+                    unicode = str_data[++i];
                     var_type = VAR;
                     term = RCURLY;
                 } else if (unicode == LPAREN) {
-                    unicode = str_data[++i].unicode();
+                    unicode = str_data[++i];
                     var_type = ENVIRON;
                     term = RPAREN;
                 }
+                int name_start = i;
                 forever {
                     if (!(unicode & (0xFF<<8)) &&
                        unicode != DOT && unicode != UNDERSCORE &&
@@ -1123,19 +1736,20 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &s
                        (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
                        (unicode < '0' || unicode > '9'))
                         break;
-                    var.append(QChar(unicode));
                     if (++i == str_len)
                         break;
-                    unicode = str_data[i].unicode();
+                    unicode = str_data[i];
                     // at this point, i points to either the 'term' or 'next' character (which is in unicode)
                 }
+                var = str.mid(name_start, i - name_start);
                 if (var_type == VAR && unicode == LPAREN) {
                     var_type = FUNCTION;
+                    name_start = i + 1;
                     int depth = 0;
                     forever {
                         if (++i == str_len)
                             break;
-                        unicode = str_data[i].unicode();
+                        unicode = str_data[i];
                         if (unicode == LPAREN) {
                             depth++;
                         } else if (unicode == RPAREN) {
@@ -1143,10 +1757,10 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &s
                                 break;
                             --depth;
                         }
-                        args.append(QChar(unicode));
                     }
+                    args = str.mid(name_start, i - name_start);
                     if (++i < str_len)
-                        unicode = str_data[i].unicode();
+                        unicode = str_data[i];
                     else
                         unicode = 0;
                     // at this point i is pointing to the 'next' character (which is in unicode)
@@ -1154,232 +1768,415 @@ QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &s
                 }
                 if (term) {
                     if (unicode != term) {
-                        q->logMessage(format("Missing %1 terminator [found %2]")
-                            .arg(QChar(term))
-                            .arg(unicode ? QString(unicode) : QString::fromLatin1(("end-of-line"))));
+                        evalError(fL1S("Missing %1 terminator [found %2]")
+                                  .arg(QChar(term))
+                                  .arg(unicode ? QString(unicode) : fL1S("end-of-line")));
 //                        if (ok)
 //                            *ok = false;
-                        return QStringList();
+                        if (pos)
+                            *pos = str_len;
+                        return ProStringList();
                     }
                 } else {
                     // move the 'cursor' back to the last char of the thing we were looking at
                     --i;
                 }
-                // since i never points to the 'next' character, there is no reason for this to be set
-                unicode = 0;
 
-                QStringList replacement;
+                ProStringList replacement;
                 if (var_type == ENVIRON) {
-                    replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
+                    replacement = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
                 } else if (var_type == PROPERTY) {
-                    replacement << propertyValue(var);
+                    replacement << ProString(propertyValue(var.toQString(m_tmp1), true), NoHash);
                 } else if (var_type == FUNCTION) {
-                    replacement << evaluateExpandFunction(var, args);
+                    replacement += evaluateExpandFunction(var, args);
                 } else if (var_type == VAR) {
-                    replacement = values(var);
+                    replacement = values(map(var));
                 }
-                if (!(replaced++) && start_var)
-                    current = str.left(start_var);
                 if (!replacement.isEmpty()) {
-                    if (quote) {
-                        current += replacement.join(QString(Option::field_sep));
+                    if (quote || joined) {
+                        if (putSpace) {
+                            putSpace = false;
+                            if (!replacement.at(0).isEmpty()) // Bizarre, indeed
+                                appendChar(' ', &current, &ptr, &pending);
+                        }
+                        appendString(ProString(replacement.join(statics.field_sep), NoHash),
+                                     &current, &ptr, &pending);
                     } else {
-                        current += replacement.takeFirst();
-                        if (!replacement.isEmpty()) {
-                            if (!current.isEmpty())
-                                ret.append(current);
-                            current = replacement.takeLast();
-                            if (!replacement.isEmpty())
-                                ret += replacement;
+                        appendString(replacement.at(0), &current, &ptr, &pending);
+                        if (replacement.size() > 1) {
+                            flushCurrent(&ret, &current, &ptr, &pending, false);
+                            int j = 1;
+                            if (replacement.size() > 2) {
+                                // FIXME: ret.reserve(ret.size() + replacement.size() - 2);
+                                for (; j < replacement.size() - 1; ++j)
+                                    ret << replacement.at(j);
+                            }
+                            pending = replacement.at(j);
                         }
                     }
+                    replaced = true;
                 }
-            } else {
-                if (replaced)
-                    current.append(QLatin1Char('$'));
+                continue;
             }
-        }
-        if (quote && unicode == quote) {
-            unicode = 0;
-            quote = 0;
         } else if (unicode == BACKSLASH) {
-            bool escape = false;
-            const char *symbols = "[]{}()$\\'\"";
-            for (const char *s = symbols; *s; ++s) {
-                if (str_data[i+1].unicode() == (ushort)*s) {
-                    i++;
-                    escape = true;
-                    if (!(replaced++))
-                        current = str.left(start_var);
-                    current.append(str.at(i));
-                    break;
-                }
+            static const char symbols[] = "[]{}()$\\'\"";
+            ushort unicode2 = str_data[i+1];
+            if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
+                unicode = unicode2;
+                ++i;
+            }
+        } else if (quote) {
+            if (unicode == quote) {
+                quote = 0;
+                continue;
             }
-            if (escape || !replaced)
-                unicode =0;
-        } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
-            quote = unicode;
-            unicode = 0;
-            if (!(replaced++) && i)
-                current = str.left(i);
-        } else if (!quote && (unicode == SPACE || unicode == TAB)) {
-            unicode = 0;
-            if (!current.isEmpty()) {
-                ret.append(current);
-                current.clear();
+        } else {
+            if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+                quote = unicode;
+                continue;
+            } else if (unicode == SPACE || unicode == TAB) {
+                if (!joined)
+                    flushCurrent(&ret, &current, &ptr, &pending, false);
+                else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
+                    putSpace = true;
+                continue;
+            } else if (pos) {
+                if (unicode == LPAREN) {
+                    ++parens;
+                } else if (unicode == RPAREN) {
+                    --parens;
+                } else if (!parens && unicode == COMMA) {
+                    if (!joined) {
+                        *pos = i + 1;
+                        flushFinal(&ret, current, ptr, pending, str, replaced, false);
+                        return ret;
+                    }
+                    flushCurrent(&ret, &current, &ptr, &pending, true);
+                    putSpace = false;
+                    continue;
+                }
             }
         }
-        if (replaced && unicode)
-            current.append(QChar(unicode));
+        if (putSpace) {
+            putSpace = false;
+            appendChar(' ', &current, &ptr, &pending);
+        }
+        appendChar(unicode, &current, &ptr, &pending);
     }
-    if (!replaced)
-        ret = QStringList(str);
-    else if (!current.isEmpty())
-        ret.append(current);
+    if (pos)
+        *pos = str_len;
+    flushFinal(&ret, current, ptr, pending, str, replaced, joined);
     return ret;
 }
 
+bool ProFileEvaluator::Private::modesForGenerator(const QString &gen,
+        ProFileOption::HOST_MODE *host_mode, ProFileOption::TARG_MODE *target_mode) const
+{
+    if (gen == fL1S("UNIX")) {
+#ifdef Q_OS_MAC
+        *host_mode = ProFileOption::HOST_MACX_MODE;
+        *target_mode = ProFileOption::TARG_MACX_MODE;
+#else
+        *host_mode = ProFileOption::HOST_UNIX_MODE;
+        *target_mode = ProFileOption::TARG_UNIX_MODE;
+#endif
+    } else if (gen == fL1S("MSVC.NET") || gen == fL1S("BMAKE") || gen == fL1S("MSBUILD")) {
+        *host_mode = ProFileOption::HOST_WIN_MODE;
+        *target_mode = ProFileOption::TARG_WIN_MODE;
+    } else if (gen == fL1S("MINGW")) {
+#if defined(Q_OS_MAC)
+        *host_mode = ProFileOption::HOST_MACX_MODE;
+#elif defined(Q_OS_UNIX)
+        *host_mode = ProFileOption::HOST_UNIX_MODE;
+#else
+        *host_mode = ProFileOption::HOST_WIN_MODE;
+#endif
+        *target_mode = ProFileOption::TARG_WIN_MODE;
+    } else if (gen == fL1S("PROJECTBUILDER") || gen == fL1S("XCODE")) {
+        *host_mode = ProFileOption::HOST_MACX_MODE;
+        *target_mode = ProFileOption::TARG_MACX_MODE;
+    } else if (gen == fL1S("SYMBIAN_ABLD") || gen == fL1S("SYMBIAN_SBSV2")
+               || gen == fL1S("SYMBIAN_UNIX") || gen == fL1S("SYMBIAN_MINGW")) {
+#if defined(Q_OS_MAC)
+        *host_mode = ProFileOption::HOST_MACX_MODE;
+#elif defined(Q_OS_UNIX)
+        *host_mode = ProFileOption::HOST_UNIX_MODE;
+#else
+        *host_mode = ProFileOption::HOST_WIN_MODE;
+#endif
+        *target_mode = ProFileOption::TARG_SYMBIAN_MODE;
+    } else {
+        evalError(fL1S("Unknown generator specified: %1").arg(gen));
+        return false;
+    }
+    return true;
+}
+
+void ProFileEvaluator::Private::validateModes() const
+{
+    if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE
+        || m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
+        const QHash<ProString, ProStringList> &vals =
+                m_option->base_valuemap.isEmpty() ? m_valuemapStack[0] : m_option->base_valuemap;
+        ProFileOption::HOST_MODE host_mode;
+        ProFileOption::TARG_MODE target_mode;
+        const ProStringList &gen = vals.value(ProString("MAKEFILE_GENERATOR"));
+        if (gen.isEmpty()) {
+            evalError(fL1S("Using OS scope before setting MAKEFILE_GENERATOR"));
+        } else if (modesForGenerator(gen.at(0).toQString(), &host_mode, &target_mode)) {
+            if (m_option->host_mode == ProFileOption::HOST_UNKNOWN_MODE) {
+                m_option->host_mode = host_mode;
+                m_option->applyHostMode();
+            }
+
+            if (m_option->target_mode == ProFileOption::TARG_UNKNOWN_MODE) {
+                const ProStringList &tgt = vals.value(ProString("TARGET_PLATFORM"));
+                if (!tgt.isEmpty()) {
+                    const QString &os = tgt.at(0).toQString();
+                    if (os == statics.strunix)
+                        m_option->target_mode = ProFileOption::TARG_UNIX_MODE;
+                    else if (os == statics.strmacx)
+                        m_option->target_mode = ProFileOption::TARG_MACX_MODE;
+                    else if (os == statics.strsymbian)
+                        m_option->target_mode = ProFileOption::TARG_SYMBIAN_MODE;
+                    else if (os == statics.strwin32)
+                        m_option->target_mode = ProFileOption::TARG_WIN_MODE;
+                    else
+                        evalError(fL1S("Unknown target platform specified: %1").arg(os));
+                } else {
+                    m_option->target_mode = target_mode;
+                }
+            }
+        }
+    }
+}
+
 bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
 {
     // magic types for easy flipping
-    if (config == QLatin1String("true"))
+    if (config == statics.strtrue)
         return true;
-    if (config == QLatin1String("false"))
+    if (config == statics.strfalse)
         return false;
 
-    // mkspecs
-    if ((Option::target_mode == Option::TARG_MACX_MODE
-            || Option::target_mode == Option::TARG_QNX6_MODE
-            || Option::target_mode == Option::TARG_UNIX_MODE)
-          && config == QLatin1String("unix"))
-        return true;
-    if (Option::target_mode == Option::TARG_MACX_MODE && config == QLatin1String("macx"))
-        return true;
-    if (Option::target_mode == Option::TARG_QNX6_MODE && config == QLatin1String("qnx6"))
-        return true;
-    if (Option::target_mode == Option::TARG_MAC9_MODE && config == QLatin1String("mac9"))
-        return true;
-    if ((Option::target_mode == Option::TARG_MAC9_MODE
-            || Option::target_mode == Option::TARG_MACX_MODE)
-          && config == QLatin1String("mac"))
-        return true;
-    if (Option::target_mode == Option::TARG_WIN_MODE && config == QLatin1String("win32"))
-        return true;
+    if (config == statics.strunix) {
+        validateModes();
+        return m_option->target_mode == ProFileOption::TARG_UNIX_MODE
+               || m_option->target_mode == ProFileOption::TARG_MACX_MODE
+               || m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
+    } else if (config == statics.strmacx || config == statics.strmac) {
+        validateModes();
+        return m_option->target_mode == ProFileOption::TARG_MACX_MODE;
+    } else if (config == statics.strsymbian) {
+        validateModes();
+        return m_option->target_mode == ProFileOption::TARG_SYMBIAN_MODE;
+    } else if (config == statics.strwin32) {
+        validateModes();
+        return m_option->target_mode == ProFileOption::TARG_WIN_MODE;
+    }
+
+    if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
+        QString cfg = config;
+        cfg.detach(); // Keep m_tmp out of QRegExp's cache
+        QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
+
+        // mkspecs
+        if (re.exactMatch(m_option->qmakespec_name))
+            return true;
+
+        // CONFIG variable
+        int t = 0;
+        foreach (const ProString &configValue, valuesDirect(statics.strCONFIG)) {
+            if (re.exactMatch(configValue.toQString(m_tmp[t])))
+                return true;
+            t ^= 1;
+        }
+    } else {
+        // mkspecs
+        if (m_option->qmakespec_name == config)
+            return true;
+
+        // CONFIG variable
+        if (valuesDirect(statics.strCONFIG).contains(ProString(config, NoHash)))
+            return true;
+    }
+
+    return false;
+}
+
+ProStringList ProFileEvaluator::Private::expandVariableReferences(
+        const ushort *&tokPtr, int sizeHint, bool joined)
+{
+    ProStringList ret;
+    ret.reserve(sizeHint);
+    forever {
+        evaluateExpression(tokPtr, &ret, joined);
+        switch (*tokPtr) {
+        case TokValueTerminator:
+        case TokFuncTerminator:
+            tokPtr++;
+            return ret;
+        case TokArgSeparator:
+            if (joined) {
+                tokPtr++;
+                continue;
+            }
+            // fallthrough
+        default:
+            Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
+            break;
+        }
+    }
+}
 
-    QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
-    QString spec = Option::qmakespec;
-    if ((regex && re.exactMatch(spec)) || (!regex && spec == config))
-        return true;
+QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ushort *&tokPtr)
+{
+    QList<ProStringList> args_list;
+    if (*tokPtr != TokFuncTerminator) {
+        for (;; tokPtr++) {
+            ProStringList arg;
+            evaluateExpression(tokPtr, &arg, false);
+            args_list << arg;
+            if (*tokPtr == TokFuncTerminator)
+                break;
+            Q_ASSERT(*tokPtr == TokArgSeparator);
+        }
+    }
+    tokPtr++;
+    return args_list;
+}
 
-    return false;
+QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProString &arguments)
+{
+    QList<ProStringList> args_list;
+    for (int pos = 0; pos < arguments.size(); )
+        args_list << expandVariableReferences(arguments, &pos);
+    return args_list;
 }
 
-QStringList ProFileEvaluator::Private::evaluateFunction(
-        ProBlock *funcPtr, const QStringList &argumentsList, bool *ok)
+ProStringList ProFileEvaluator::Private::evaluateFunction(
+        const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
 {
     bool oki;
-    QStringList ret;
+    ProStringList ret;
 
     if (m_valuemapStack.count() >= 100) {
-        q->errorMessage(format("ran into infinite recursion (depth > 100)."));
+        evalError(fL1S("ran into infinite recursion (depth > 100)."));
         oki = false;
     } else {
-        State sts = m_sts;
-        m_valuemapStack.push(m_valuemap);
-        m_filevaluemapStack.push(m_filevaluemap);
+        m_valuemapStack.push(QHash<ProString, ProStringList>());
+        m_locationStack.push(m_current);
+        int loopLevel = m_loopLevel;
+        m_loopLevel = 0;
 
-        QStringList args;
+        ProStringList args;
         for (int i = 0; i < argumentsList.count(); ++i) {
-            QStringList theArgs = expandVariableReferences(argumentsList[i]);
-            args += theArgs;
-            m_valuemap[QString::number(i+1)] = theArgs;
+            args += argumentsList[i];
+            m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i];
         }
-        m_valuemap[QLatin1String("ARGS")] = args;
-        oki = (funcPtr->Accept(this) != ProItem::ReturnFalse); // True || Return
+        m_valuemapStack.top()[statics.strARGS] = args;
+        oki = (visitProBlock(func.pro(), func.tokPtr()) != ReturnFalse); // True || Return
         ret = m_returnValue;
         m_returnValue.clear();
 
-        m_valuemap = m_valuemapStack.pop();
-        m_filevaluemap = m_filevaluemapStack.pop();
-        m_sts = sts;
+        m_loopLevel = loopLevel;
+        m_current = m_locationStack.pop();
+        m_valuemapStack.pop();
     }
     if (ok)
         *ok = oki;
     if (oki)
         return ret;
-    return QStringList();
-}
-
-QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
-{
-    QStringList argumentsList = split_arg_list(arguments);
-
-    if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
-        return evaluateFunction(funcPtr, argumentsList, 0);
-
-    QStringList args;
-    for (int i = 0; i < argumentsList.count(); ++i)
-        args += expandVariableReferences(argumentsList[i]).join(Option::field_sep);
-
-    enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
-                      E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
-                      E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
-                      E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
-                      E_REPLACE };
-
-    static QHash<QString, int> expands;
-    if (expands.isEmpty()) {
-        expands.insert(QLatin1String("member"), E_MEMBER);
-        expands.insert(QLatin1String("first"), E_FIRST);
-        expands.insert(QLatin1String("last"), E_LAST);
-        expands.insert(QLatin1String("cat"), E_CAT);
-        expands.insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
-        expands.insert(QLatin1String("eval"), E_EVAL);
-        expands.insert(QLatin1String("list"), E_LIST);
-        expands.insert(QLatin1String("sprintf"), E_SPRINTF);
-        expands.insert(QLatin1String("join"), E_JOIN);
-        expands.insert(QLatin1String("split"), E_SPLIT);
-        expands.insert(QLatin1String("basename"), E_BASENAME);
-        expands.insert(QLatin1String("dirname"), E_DIRNAME);
-        expands.insert(QLatin1String("section"), E_SECTION);
-        expands.insert(QLatin1String("find"), E_FIND);
-        expands.insert(QLatin1String("system"), E_SYSTEM);
-        expands.insert(QLatin1String("unique"), E_UNIQUE);
-        expands.insert(QLatin1String("quote"), E_QUOTE);
-        expands.insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
-        expands.insert(QLatin1String("upper"), E_UPPER);
-        expands.insert(QLatin1String("lower"), E_LOWER);
-        expands.insert(QLatin1String("re_escape"), E_RE_ESCAPE);
-        expands.insert(QLatin1String("files"), E_FILES);
-        expands.insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
-        expands.insert(QLatin1String("replace"), E_REPLACE);
+    return ProStringList();
+}
+
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateBoolFunction(
+        const FunctionDef &func, const QList<ProStringList> &argumentsList,
+        const ProString &function)
+{
+    bool ok;
+    ProStringList ret = evaluateFunction(func, argumentsList, &ok);
+    if (ok) {
+        if (ret.isEmpty())
+            return ReturnTrue;
+        if (ret.at(0) != statics.strfalse) {
+            if (ret.at(0) == statics.strtrue)
+                return ReturnTrue;
+            int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
+            if (ok) {
+                if (val)
+                    return ReturnTrue;
+            } else {
+                evalError(fL1S("Unexpected return value from test '%1': %2")
+                          .arg(function.toQString(m_tmp1))
+                          .arg(ret.join(QLatin1String(" :: "))));
+            }
+        }
     }
-    ExpandFunc func_t = ExpandFunc(expands.value(func.toLower()));
+    return ReturnFalse;
+}
 
-    QStringList ret;
+ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
+        const ProString &func, const ushort *&tokPtr)
+{
+    QHash<ProString, FunctionDef>::ConstIterator it =
+            m_functionDefs.replaceFunctions.constFind(func);
+    if (it != m_functionDefs.replaceFunctions.constEnd())
+        return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
+
+    //why don't the builtin functions just use args_list? --Sam
+    return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
+}
+
+ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
+        const ProString &func, const ProString &arguments)
+{
+    QHash<ProString, FunctionDef>::ConstIterator it =
+            m_functionDefs.replaceFunctions.constFind(func);
+    if (it != m_functionDefs.replaceFunctions.constEnd())
+        return evaluateFunction(*it, prepareFunctionArgs(arguments), 0);
+
+    //why don't the builtin functions just use args_list? --Sam
+    int pos = 0;
+    return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
+}
+
+ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
+        const ProString &func, const ProStringList &args)
+{
+    ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
+    if (func_t == 0) {
+        const QString &fn = func.toQString(m_tmp1);
+        const QString &lfn = fn.toLower();
+        if (!fn.isSharedWith(lfn))
+            func_t = ExpandFunc(statics.expands.value(ProString(lfn)));
+    }
+
+    ProStringList ret;
 
     switch (func_t) {
         case E_BASENAME:
         case E_DIRNAME:
         case E_SECTION: {
             bool regexp = false;
-            QString sep, var;
+            QString sep;
+            ProString var;
             int beg = 0;
             int end = -1;
             if (func_t == E_SECTION) {
                 if (args.count() != 3 && args.count() != 4) {
-                    q->logMessage(format("%1(var) section(var, sep, begin, end) "
-                        "requires three or four arguments.").arg(func));
+                    evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
+                                   " three or four arguments.").arg(func.toQString(m_tmp1)));
                 } else {
                     var = args[0];
-                    sep = args[1];
-                    beg = args[2].toInt();
+                    sep = args.at(1).toQString();
+                    beg = args.at(2).toQString(m_tmp2).toInt();
                     if (args.count() == 4)
-                        end = args[3].toInt();
+                        end = args.at(3).toQString(m_tmp2).toInt();
                 }
             } else {
                 if (args.count() != 1) {
-                    q->logMessage(format("%1(var) requires one argument.").arg(func));
+                    evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
                 } else {
                     var = args[0];
                     regexp = true;
@@ -1390,67 +2187,81 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                         beg = -1;
                 }
             }
-            if (!var.isNull()) {
-                foreach (const QString str, values(var)) {
-                    if (regexp)
-                        ret += str.section(QRegExp(sep), beg, end);
-                    else
-                        ret += str.section(sep, beg, end);
+            if (!var.isEmpty()) {
+                if (regexp) {
+                    QRegExp sepRx(sep);
+                    foreach (const ProString &str, values(map(var))) {
+                        const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
+                        ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
+                    }
+                } else {
+                    foreach (const ProString &str, values(map(var))) {
+                        const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
+                        ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash).setSource(str));
+                    }
                 }
             }
             break;
         }
         case E_SPRINTF:
             if(args.count() < 1) {
-                q->logMessage(format("sprintf(format, ...) requires at least one argument"));
+                evalError(fL1S("sprintf(format, ...) requires at least one argument"));
             } else {
-                QString tmp = args.at(0);
+                QString tmp = args.at(0).toQString(m_tmp1);
                 for (int i = 1; i < args.count(); ++i)
-                    tmp = tmp.arg(args.at(i));
+                    tmp = tmp.arg(args.at(i).toQString(m_tmp2));
+                // Note: this depends on split_value_list() making a deep copy
                 ret = split_value_list(tmp);
             }
             break;
         case E_JOIN: {
             if (args.count() < 1 || args.count() > 4) {
-                q->logMessage(format("join(var, glue, before, after) requires one to four arguments."));
+                evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
             } else {
-                QString glue, before, after;
+                QString glue;
+                ProString before, after;
                 if (args.count() >= 2)
-                    glue = args[1];
+                    glue = args.at(1).toQString(m_tmp1);
                 if (args.count() >= 3)
                     before = args[2];
                 if (args.count() == 4)
                     after = args[3];
-                const QStringList &var = values(args.first());
-                if (!var.isEmpty())
-                    ret.append(before + var.join(glue) + after);
+                const ProStringList &var = values(map(args.at(0)));
+                if (!var.isEmpty()) {
+                    const ProFile *src = currentProFile();
+                    foreach (const ProString &v, var)
+                        if (const ProFile *s = v.sourceFile()) {
+                            src = s;
+                            break;
+                        }
+                    ret.append(ProString(before + var.join(glue) + after, NoHash).setSource(src));
+                }
             }
             break;
         }
-        case E_SPLIT: {
+        case E_SPLIT:
             if (args.count() != 2) {
-                q->logMessage(format("split(var, sep) requires one or two arguments"));
+                evalError(fL1S("split(var, sep) requires one or two arguments"));
             } else {
-                const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep);
-                foreach (const QString &var, values(args.first()))
-                    foreach (const QString &splt, var.split(sep))
-                        ret.append(splt);
+                const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
+                foreach (const ProString &var, values(map(args.at(0))))
+                    foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
+                        ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt, NoHash).setSource(var));
             }
             break;
-        }
-        case E_MEMBER: {
+        case E_MEMBER:
             if (args.count() < 1 || args.count() > 3) {
-                q->logMessage(format("member(var, start, end) requires one to three arguments."));
+                evalError(fL1S("member(var, start, end) requires one to three arguments."));
             } else {
                 bool ok = true;
-                const QStringList var = values(args.first());
+                const ProStringList &var = values(map(args.at(0)));
                 int start = 0, end = 0;
                 if (args.count() >= 2) {
-                    QString start_str = args[1];
+                    const QString &start_str = args.at(1).toQString(m_tmp1);
                     start = start_str.toInt(&ok);
                     if (!ok) {
                         if (args.count() == 2) {
-                            int dotdot = start_str.indexOf(QLatin1String(".."));
+                            int dotdot = start_str.indexOf(statics.strDotDot);
                             if (dotdot != -1) {
                                 start = start_str.left(dotdot).toInt(&ok);
                                 if (ok)
@@ -1458,15 +2269,15 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                             }
                         }
                         if (!ok)
-                            q->logMessage(format("member() argument 2 (start) '%2' invalid.")
-                                .arg(start_str));
+                            evalError(fL1S("member() argument 2 (start) '%2' invalid.")
+                                      .arg(start_str));
                     } else {
                         end = start;
                         if (args.count() == 3)
-                            end = args[2].toInt(&ok);
+                            end = args.at(2).toQString(m_tmp1).toInt(&ok);
                         if (!ok)
-                            q->logMessage(format("member() argument 3 (end) '%2' invalid.\n")
-                                .arg(args[2]));
+                            evalError(fL1S("member() argument 3 (end) '%2' invalid.\n")
+                                      .arg(args.at(2).toQString(m_tmp1)));
                     }
                 }
                 if (ok) {
@@ -1486,13 +2297,12 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                 }
             }
             break;
-        }
         case E_FIRST:
-        case E_LAST: {
+        case E_LAST:
             if (args.count() != 1) {
-                q->logMessage(format("%1(var) requires one argument.").arg(func));
+                evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
             } else {
-                const QStringList var = values(args.first());
+                const ProStringList &var = values(map(args.at(0)));
                 if (!var.isEmpty()) {
                     if (func_t == E_FIRST)
                         ret.append(var[0]);
@@ -1501,92 +2311,96 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                 }
             }
             break;
-        }
+        case E_SIZE:
+            if (args.count() != 1)
+                evalError(fL1S("size(var) requires one argument."));
+            else
+                ret.append(ProString(QString::number(values(map(args.at(0))).size()), NoHash));
+            break;
         case E_CAT:
             if (args.count() < 1 || args.count() > 2) {
-                q->logMessage(format("cat(file, singleline=true) requires one or two arguments."));
+                evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
             } else {
-                QString file = args[0];
-                file = Option::fixPathToLocalOS(file);
+                const QString &file = args.at(0).toQString(m_tmp1);
 
                 bool singleLine = true;
                 if (args.count() > 1)
-                    singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
+                    singleLine = isTrue(args.at(1), m_tmp2);
 
-                QFile qfile(file);
+                QFile qfile(resolvePath(expandEnvVars(file)));
                 if (qfile.open(QIODevice::ReadOnly)) {
                     QTextStream stream(&qfile);
                     while (!stream.atEnd()) {
                         ret += split_value_list(stream.readLine().trimmed());
                         if (!singleLine)
-                            ret += QLatin1String("\n");
+                            ret += ProString("\n", NoHash);
                     }
                     qfile.close();
                 }
             }
             break;
-#if 0 // Used only by Qt's configure for caching
         case E_FROMFILE:
             if (args.count() != 2) {
-                q->logMessage(format("fromfile(file, variable) requires two arguments."));
+                evalError(fL1S("fromfile(file, variable) requires two arguments."));
             } else {
-                QString file = args[0], seek_variableName = args[1];
-
-                ProFile pro(Option::fixPathToLocalOS(file));
-
-                ProFileEvaluator visitor;
-                visitor.setVerbose(m_verbose);
-                visitor.setCumulative(m_cumulative);
-
-                if (!visitor.queryProFile(&pro))
-                    break;
-
-                if (!visitor.accept(&pro))
-                    break;
-
-                ret = visitor.values(seek_variableName);
+                QHash<ProString, ProStringList> vars;
+                QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
+                fn.detach();
+                if (evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
+                                     &vars, &m_functionDefs, EvalWithDefaults))
+                    ret = vars.value(map(args.at(1)));
             }
             break;
-#endif
-        case E_EVAL: {
+        case E_EVAL:
             if (args.count() != 1) {
-                q->logMessage(format("eval(variable) requires one argument"));
-
+                evalError(fL1S("eval(variable) requires one argument"));
             } else {
-                ret += values(args.at(0));
+                ret += values(map(args.at(0)));
             }
-            break; }
+            break;
         case E_LIST: {
-            static int x = 0;
             QString tmp;
-            tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++);
-            ret = QStringList(tmp);
-            QStringList lst;
-            foreach (const QString &arg, args)
-                lst += split_value_list(arg);
-            m_valuemap[tmp] = lst;
+            tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
+            ret = ProStringList(ProString(tmp, NoHash));
+            ProStringList lst;
+            foreach (const ProString &arg, args)
+                lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy
+            m_valuemapStack.top()[ret.at(0)] = lst;
             break; }
         case E_FIND:
             if (args.count() != 2) {
-                q->logMessage(format("find(var, str) requires two arguments."));
+                evalError(fL1S("find(var, str) requires two arguments."));
             } else {
-                QRegExp regx(args[1]);
-                foreach (const QString &val, values(args.first()))
-                    if (regx.indexIn(val) != -1)
+                QRegExp regx(args.at(1).toQString());
+                int t = 0;
+                foreach (const ProString &val, values(map(args.at(0)))) {
+                    if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
                         ret += val;
+                    t ^= 1;
+                }
             }
             break;
         case E_SYSTEM:
             if (!m_skipLevel) {
                 if (args.count() < 1 || args.count() > 2) {
-                    q->logMessage(format("system(execute) requires one or two arguments."));
+                    evalError(fL1S("system(execute) requires one or two arguments."));
                 } else {
-                    char buff[256];
-                    FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
                     bool singleLine = true;
                     if (args.count() > 1)
-                        singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
-                    QString output;
+                        singleLine = isTrue(args.at(1), m_tmp2);
+                    QByteArray output;
+#ifndef QT_BOOTSTRAPPED
+                    QProcess proc;
+                    runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardError);
+                    output = proc.readAllStandardOutput();
+                    output.replace('\t', ' ');
+                    if (singleLine)
+                        output.replace('\n', ' ');
+#else
+                    char buff[256];
+                    FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
+                                           + IoUtils::shellQuote(currentDirectory())
+                                           + QLatin1String(" && ") + args[0]).toLocal8Bit(), "r");
                     while (proc && !feof(proc)) {
                         int read_in = int(fread(buff, 1, 255, proc));
                         if (!read_in)
@@ -1595,32 +2409,31 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                             if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
                                 buff[i] = ' ';
                         }
-                        buff[read_in] = '\0';
-                        output += QLatin1String(buff);
+                        output.append(buff, read_in);
                     }
-                    ret += split_value_list(output);
                     if (proc)
                         QT_PCLOSE(proc);
+#endif
+                    ret += split_value_list(QString::fromLocal8Bit(output));
                 }
             }
             break;
         case E_UNIQUE:
             if(args.count() != 1) {
-                q->logMessage(format("unique(var) requires one argument."));
+                evalError(fL1S("unique(var) requires one argument."));
             } else {
-                foreach (const QString &var, values(args.first()))
-                    if (!ret.contains(var))
-                        ret.append(var);
+                ret = values(map(args.at(0)));
+                ret.removeDuplicates();
             }
             break;
         case E_QUOTE:
-            for (int i = 0; i < args.count(); ++i)
-                ret += QStringList(args.at(i));
+            ret += args;
             break;
         case E_ESCAPE_EXPAND:
             for (int i = 0; i < args.size(); ++i) {
-                QChar *i_data = args[i].data();
-                int i_len = args[i].length();
+                QString str = args.at(i).toQString();
+                QChar *i_data = str.data();
+                int i_len = str.length();
                 for (int x = 0; x < i_len; ++x) {
                     if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
                         if (*(i_data+x+1) == QLatin1Char('\\')) {
@@ -1646,299 +2459,238 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
                         }
                     }
                 }
-                ret.append(QString(i_data, i_len));
+                ret.append(ProString(QString(i_data, i_len), NoHash).setSource(args.at(i)));
             }
             break;
         case E_RE_ESCAPE:
-            for (int i = 0; i < args.size(); ++i)
-                ret += QRegExp::escape(args[i]);
+            for (int i = 0; i < args.size(); ++i) {
+                const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
+                ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
+            }
             break;
         case E_UPPER:
         case E_LOWER:
-            for (int i = 0; i < args.count(); ++i)
-                if (func_t == E_UPPER)
-                    ret += args[i].toUpper();
-                else
-                    ret += args[i].toLower();
+            for (int i = 0; i < args.count(); ++i) {
+                QString rstr = args.at(i).toQString(m_tmp1);
+                rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
+                ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash).setSource(args.at(i)));
+            }
             break;
         case E_FILES:
             if (args.count() != 1 && args.count() != 2) {
-                q->logMessage(format("files(pattern, recursive=false) requires one or two arguments"));
+                evalError(fL1S("files(pattern, recursive=false) requires one or two arguments"));
             } else {
                 bool recursive = false;
                 if (args.count() == 2)
-                    recursive = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive) || args[1].toInt());
+                    recursive = isTrue(args.at(1), m_tmp2);
                 QStringList dirs;
-                QString r = Option::fixPathToLocalOS(args[0]);
+                QString r = fixPathToLocalOS(args.at(0).toQString(m_tmp1));
+                QString pfx;
+                if (IoUtils::isRelativePath(r)) {
+                    pfx = currentDirectory();
+                    if (!pfx.endsWith(QLatin1Char('/')))
+                        pfx += QLatin1Char('/');
+                }
                 int slash = r.lastIndexOf(QDir::separator());
                 if (slash != -1) {
-                    dirs.append(r.left(slash));
+                    dirs.append(r.left(slash+1));
                     r = r.mid(slash+1);
                 } else {
                     dirs.append(QString());
                 }
 
+                r.detach(); // Keep m_tmp out of QRegExp's cache
                 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
                 for (int d = 0; d < dirs.count(); d++) {
                     QString dir = dirs[d];
-                    if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep))
-                        dir += QLatin1Char('/');
-
-                    QDir qdir(dir);
+                    QDir qdir(pfx + dir);
                     for (int i = 0; i < (int)qdir.count(); ++i) {
-                        if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String(".."))
+                        if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
                             continue;
                         QString fname = dir + qdir[i];
-                        if (QFileInfo(fname).isDir()) {
+                        if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
                             if (recursive)
-                                dirs.append(fname);
+                                dirs.append(fname + QDir::separator());
                         }
                         if (regex.exactMatch(qdir[i]))
-                            ret += fname;
+                            ret += ProString(fname, NoHash).setSource(currentProFile());
                     }
                 }
             }
             break;
         case E_REPLACE:
             if(args.count() != 3 ) {
-                q->logMessage(format("replace(var, before, after) requires three arguments"));
+                evalError(fL1S("replace(var, before, after) requires three arguments"));
             } else {
-                const QRegExp before(args[1]);
-                const QString after(args[2]);
-                foreach (QString val, values(args.first()))
-                    ret += val.replace(before, after);
+                const QRegExp before(args.at(1).toQString());
+                const QString &after(args.at(2).toQString(m_tmp2));
+                foreach (const ProString &val, values(map(args.at(0)))) {
+                    QString rstr = val.toQString(m_tmp1);
+                    QString copy = rstr; // Force a detach on modify
+                    rstr.replace(before, after);
+                    ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr, NoHash).setSource(val));
+                }
             }
             break;
-        case 0:
-            q->logMessage(format("'%1' is not a recognized replace function").arg(func));
+        case E_INVALID:
+            evalError(fL1S("'%1' is not a recognized replace function")
+                      .arg(func.toQString(m_tmp1)));
             break;
         default:
-            q->logMessage(format("Function '%1' is not implemented").arg(func));
+            evalError(fL1S("Function '%1' is not implemented").arg(func.toQString(m_tmp1)));
             break;
     }
 
     return ret;
 }
 
-ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
-        const QString &function, const QString &arguments)
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
+        const ProString &function, const ProString &arguments)
 {
-    QStringList argumentsList = split_arg_list(arguments);
+    QHash<ProString, FunctionDef>::ConstIterator it =
+            m_functionDefs.testFunctions.constFind(function);
+    if (it != m_functionDefs.testFunctions.constEnd())
+        return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
 
-    if (ProBlock *funcPtr = m_testFunctions.value(function, 0)) {
-        bool ok;
-        QStringList ret = evaluateFunction(funcPtr, argumentsList, &ok);
-        if (ok) {
-            if (ret.isEmpty()) {
-                return ProItem::ReturnTrue;
-            } else {
-                if (ret.first() != QLatin1String("false")) {
-                    if (ret.first() == QLatin1String("true")) {
-                        return ProItem::ReturnTrue;
-                    } else {
-                        bool ok;
-                        int val = ret.first().toInt(&ok);
-                        if (ok) {
-                            if (val)
-                                return ProItem::ReturnTrue;
-                        } else {
-                            q->logMessage(format("Unexpected return value from test '%1': %2")
-                                          .arg(function).arg(ret.join(QLatin1String(" :: "))));
-                        }
-                    }
-                }
-            }
-        }
-        return ProItem::ReturnFalse;
-    }
+    //why don't the builtin functions just use args_list? --Sam
+    int pos = 0;
+    return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
+}
 
-    QString sep;
-    sep.append(Option::field_sep);
-    QStringList args;
-    for (int i = 0; i < argumentsList.count(); ++i)
-        args += expandVariableReferences(argumentsList[i]).join(sep);
-
-    enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
-                    T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
-                    T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
-                    T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
-                    T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
-
-    static QHash<QString, int> functions;
-    if (functions.isEmpty()) {
-        functions.insert(QLatin1String("requires"), T_REQUIRES);
-        functions.insert(QLatin1String("greaterThan"), T_GREATERTHAN);
-        functions.insert(QLatin1String("lessThan"), T_LESSTHAN);
-        functions.insert(QLatin1String("equals"), T_EQUALS);
-        functions.insert(QLatin1String("isEqual"), T_EQUALS);
-        functions.insert(QLatin1String("exists"), T_EXISTS);
-        functions.insert(QLatin1String("export"), T_EXPORT);
-        functions.insert(QLatin1String("clear"), T_CLEAR);
-        functions.insert(QLatin1String("unset"), T_UNSET);
-        functions.insert(QLatin1String("eval"), T_EVAL);
-        functions.insert(QLatin1String("CONFIG"), T_CONFIG);
-        functions.insert(QLatin1String("if"), T_IF);
-        functions.insert(QLatin1String("isActiveConfig"), T_CONFIG);
-        functions.insert(QLatin1String("system"), T_SYSTEM);
-        functions.insert(QLatin1String("return"), T_RETURN);
-        functions.insert(QLatin1String("break"), T_BREAK);
-        functions.insert(QLatin1String("next"), T_NEXT);
-        functions.insert(QLatin1String("defined"), T_DEFINED);
-        functions.insert(QLatin1String("contains"), T_CONTAINS);
-        functions.insert(QLatin1String("infile"), T_INFILE);
-        functions.insert(QLatin1String("count"), T_COUNT);
-        functions.insert(QLatin1String("isEmpty"), T_ISEMPTY);
-        functions.insert(QLatin1String("load"), T_LOAD);         //v
-        functions.insert(QLatin1String("include"), T_INCLUDE);   //v
-        functions.insert(QLatin1String("debug"), T_DEBUG);
-        functions.insert(QLatin1String("message"), T_MESSAGE);   //v
-        functions.insert(QLatin1String("warning"), T_MESSAGE);   //v
-        functions.insert(QLatin1String("error"), T_MESSAGE);     //v
-        functions.insert(QLatin1String("for"), T_FOR);     //v
-        functions.insert(QLatin1String("defineTest"), T_DEFINE_TEST);        //v
-        functions.insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE);  //v
-    }
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
+        const ProString &function, const ushort *&tokPtr)
+{
+    QHash<ProString, FunctionDef>::ConstIterator it =
+            m_functionDefs.testFunctions.constFind(function);
+    if (it != m_functionDefs.testFunctions.constEnd())
+        return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
+
+    //why don't the builtin functions just use args_list? --Sam
+    return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
+}
 
-    TestFunc func_t = (TestFunc)functions.value(function);
+ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
+        const ProString &function, const ProStringList &args)
+{
+    TestFunc func_t = (TestFunc)statics.functions.value(function);
 
     switch (func_t) {
-        case T_DEFINE_TEST:
-            m_definingTest = true;
-            goto defineFunc;
-        case T_DEFINE_REPLACE:
-            m_definingTest = false;
-          defineFunc:
-            if (args.count() != 1) {
-                q->logMessage(format("%s(function) requires one argument.").arg(function));
-                return ProItem::ReturnFalse;
-            }
-            m_definingFunc = args.first();
-            return ProItem::ReturnTrue;
         case T_DEFINED:
             if (args.count() < 1 || args.count() > 2) {
-                q->logMessage(format("defined(function, [\"test\"|\"replace\"])"
-                                     " requires one or two arguments."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
+                               " requires one or two arguments."));
+                return ReturnFalse;
             }
             if (args.count() > 1) {
                 if (args[1] == QLatin1String("test"))
-                    return returnBool(m_testFunctions.contains(args[0]));
+                    return returnBool(m_functionDefs.testFunctions.contains(args[0]));
                 else if (args[1] == QLatin1String("replace"))
-                    return returnBool(m_replaceFunctions.contains(args[0]));
-                q->logMessage(format("defined(function, type):"
-                                     " unexpected type [%1].\n").arg(args[1]));
-                return ProItem::ReturnFalse;
+                    return returnBool(m_functionDefs.replaceFunctions.contains(args[0]));
+                evalError(fL1S("defined(function, type): unexpected type [%1].\n")
+                          .arg(args.at(1).toQString(m_tmp1)));
+                return ReturnFalse;
             }
-            return returnBool(m_replaceFunctions.contains(args[0])
-                              || m_testFunctions.contains(args[0]));
+            return returnBool(m_functionDefs.replaceFunctions.contains(args[0])
+                              || m_functionDefs.testFunctions.contains(args[0]));
         case T_RETURN:
             m_returnValue = args;
             // It is "safe" to ignore returns - due to qmake brokeness
             // they cannot be used to terminate loops anyway.
             if (m_skipLevel || m_cumulative)
-                return ProItem::ReturnTrue;
+                return ReturnTrue;
             if (m_valuemapStack.isEmpty()) {
-                q->logMessage(format("unexpected return()."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("unexpected return()."));
+                return ReturnFalse;
             }
-            return ProItem::ReturnReturn;
-        case T_EXPORT:
+            return ReturnReturn;
+        case T_EXPORT: {
             if (m_skipLevel && !m_cumulative)
-                return ProItem::ReturnTrue;
+                return ReturnTrue;
             if (args.count() != 1) {
-                q->logMessage(format("export(variable) requires one argument."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("export(variable) requires one argument."));
+                return ReturnFalse;
+            }
+            const ProString &var = map(args.at(0));
+            for (int i = m_valuemapStack.size(); --i > 0; ) {
+                QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(var);
+                if (it != m_valuemapStack.at(i).end()) {
+                    if (it->constBegin() == statics.fakeValue.constBegin()) {
+                        // This is stupid, but qmake doesn't propagate deletions
+                        m_valuemapStack[0][var] = ProStringList();
+                    } else {
+                        m_valuemapStack[0][var] = *it;
+                    }
+                    m_valuemapStack[i].erase(it);
+                    while (--i)
+                        m_valuemapStack[i].remove(var);
+                    break;
+                }
             }
-            for (int i = 0; i < m_valuemapStack.size(); ++i) {
-                m_valuemapStack[i][args[0]] = m_valuemap[args[0]];
-                m_filevaluemapStack[i][currentProFile()][args[0]] =
-                        m_filevaluemap[currentProFile()][args[0]];
+            return ReturnTrue;
+        }
+        case T_INFILE:
+            if (args.count() < 2 || args.count() > 3) {
+                evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
+            } else {
+                QHash<ProString, ProStringList> vars;
+                QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
+                fn.detach();
+                if (!evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
+                                      &vars, &m_functionDefs, EvalWithDefaults))
+                    return ReturnFalse;
+                if (args.count() == 2)
+                    return returnBool(vars.contains(args.at(1)));
+                QRegExp regx;
+                const QString &qry = args.at(2).toQString(m_tmp1);
+                if (qry != QRegExp::escape(qry)) {
+                    QString copy = qry;
+                    copy.detach();
+                    regx.setPattern(copy);
+                }
+                int t = 0;
+                foreach (const ProString &s, vars.value(map(args.at(1)))) {
+                    if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
+                        return ReturnTrue;
+                    t ^= 1;
+                }
             }
-            return ProItem::ReturnTrue;
+            return ReturnFalse;
 #if 0
-        case T_INFILE:
         case T_REQUIRES:
-        case T_EVAL:
 #endif
-        case T_FOR: {
-            if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
-                return ProItem::ReturnTrue;
-            if (m_skipLevel)
-                return ProItem::ReturnFalse;
-            if (args.count() > 2 || args.count() < 1) {
-                q->logMessage(format("for({var, list|var, forever|ever})"
-                                     " requires one or two arguments."));
-                return ProItem::ReturnFalse;
-            }
-            ProLoop loop;
-            loop.infinite = false;
-            loop.index = 0;
-            QString it_list;
-            if (args.count() == 1) {
-                doVariableReplace(&args[0]);
-                it_list = args[0];
-                if (args[0] != QLatin1String("ever")) {
-                    q->logMessage(format("for({var, list|var, forever|ever})"
-                                         " requires one or two arguments."));
-                    return ProItem::ReturnFalse;
-                }
-                it_list = QLatin1String("forever");
-            } else {
-                loop.variable = args[0];
-                loop.oldVarVal = m_valuemap.value(loop.variable);
-                doVariableReplace(&args[1]);
-                it_list = args[1];
-            }
-            loop.list = m_valuemap[it_list];
-            if (loop.list.isEmpty()) {
-                if (it_list == QLatin1String("forever")) {
-                    loop.infinite = true;
-                } else {
-                    int dotdot = it_list.indexOf(QLatin1String(".."));
-                    if (dotdot != -1) {
-                        bool ok;
-                        int start = it_list.left(dotdot).toInt(&ok);
-                        if (ok) {
-                            int end = it_list.mid(dotdot+2).toInt(&ok);
-                            if (ok) {
-                                if (start < end) {
-                                    for (int i = start; i <= end; i++)
-                                        loop.list << QString::number(i);
-                                } else {
-                                    for (int i = start; i >= end; i--)
-                                        loop.list << QString::number(i);
-                                }
-                            }
-                        }
-                    }
-                }
+        case T_EVAL: {
+                ProFile *pro = m_parser->parsedProBlock(fL1S("(eval)"),
+                                                        args.join(statics.field_sep));
+                if (!pro)
+                    return ReturnFalse;
+                m_locationStack.push(m_current);
+                VisitReturn ret = visitProBlock(pro, pro->tokPtr());
+                m_current = m_locationStack.pop();
+                pro->deref();
+                return ret;
             }
-            m_loopStack.push(loop);
-            m_sts.condition = true;
-            return ProItem::ReturnLoop;
-        }
         case T_BREAK:
             if (m_skipLevel)
-                return ProItem::ReturnFalse;
-            if (!m_loopStack.isEmpty())
-                return ProItem::ReturnBreak;
-            // ### missing: breaking out of multiline blocks
-            q->logMessage(format("unexpected break()."));
-            return ProItem::ReturnFalse;
+                return ReturnFalse;
+            if (m_loopLevel)
+                return ReturnBreak;
+            evalError(fL1S("unexpected break()."));
+            return ReturnFalse;
         case T_NEXT:
             if (m_skipLevel)
-                return ProItem::ReturnFalse;
-            if (!m_loopStack.isEmpty())
-                return ProItem::ReturnNext;
-            q->logMessage(format("unexpected next()."));
-            return ProItem::ReturnFalse;
+                return ReturnFalse;
+            if (m_loopLevel)
+                return ReturnNext;
+            evalError(fL1S("unexpected next()."));
+            return ReturnFalse;
         case T_IF: {
+            if (m_skipLevel && !m_cumulative)
+                return ReturnFalse;
             if (args.count() != 1) {
-                q->logMessage(format("if(condition) requires one argument."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("if(condition) requires one argument."));
+                return ReturnFalse;
             }
-            QString cond = args.first();
-            bool escaped = false; // This is more than qmake does
+            const ProString &cond = args.at(0);
             bool quoted = false;
             bool ret = true;
             bool orOp = false;
@@ -1947,83 +2699,69 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
             int parens = 0;
             QString test;
             test.reserve(20);
-            QString args;
-            args.reserve(50);
-            const QChar *d = cond.unicode();
-            const QChar *ed = d + cond.length();
+            QString argsString;
+            argsString.reserve(50);
+            const QChar *d = cond.constData();
+            const QChar *ed = d + cond.size();
             while (d < ed) {
                 ushort c = (d++)->unicode();
-                if (!escaped) {
-                    if (c == '\\') {
-                        escaped = true;
-                        args += c; // Assume no-one quotes the test name
-                        continue;
-                    } else if (c == '"') {
-                        quoted = !quoted;
-                        args += c; // Ditto
-                        continue;
-                    }
-                } else {
-                    escaped = false;
-                }
+                bool isOp = false;
                 if (quoted) {
-                    args += c; // Ditto
+                    if (c == '"')
+                        quoted = false;
+                    else if (c == '!' && test.isEmpty())
+                        invert = true;
+                    else
+                        test += c;
+                } else if (c == '(') {
+                    isFunc = true;
+                    if (parens)
+                        argsString += c;
+                    ++parens;
+                } else if (c == ')') {
+                    --parens;
+                    if (parens)
+                        argsString += c;
+                } else if (!parens) {
+                    if (c == '"')
+                        quoted = true;
+                    else if (c == ':' || c == '|')
+                        isOp = true;
+                    else if (c == '!' && test.isEmpty())
+                        invert = true;
+                    else
+                        test += c;
                 } else {
-                    bool isOp = false;
-                    if (c == '(') {
-                        isFunc = true;
-                        if (parens)
-                            args += c;
-                        ++parens;
-                    } else if (c == ')') {
-                        --parens;
-                        if (parens)
-                            args += c;
-                    } else if (!parens) {
-                        if (c == ':' || c == '|')
-                            isOp = true;
-                        else if (c == '!')
-                            invert = true;
-                        else
-                            test += c;
-                    } else {
-                        args += c;
-                    }
-                    if (!parens && (isOp || d == ed)) {
-                        // Yes, qmake doesn't shortcut evaluations here. We can't, either,
-                        // as some test functions have side effects.
-                        bool success;
-                        if (isFunc) {
-                            success = evaluateConditionalFunction(test, args);
-                        } else {
-                            success = isActiveConfig(test, true);
-                        }
-                        success ^= invert;
-                        if (orOp)
-                            ret |= success;
+                    argsString += c;
+                }
+                if (!quoted && !parens && (isOp || d == ed)) {
+                    if (m_cumulative || (orOp != ret)) {
+                        test = test.trimmed();
+                        if (isFunc)
+                            ret = evaluateConditionalFunction(ProString(test), ProString(argsString, NoHash));
                         else
-                            ret &= success;
-                        orOp = (c == '|');
-                        invert = false;
-                        isFunc = false;
-                        test.clear();
-                        args.clear();
+                            ret = isActiveConfig(test, true);
+                        ret ^= invert;
                     }
+                    orOp = (c == '|');
+                    invert = false;
+                    isFunc = false;
+                    test.clear();
+                    argsString.clear();
                 }
             }
             return returnBool(ret);
         }
         case T_CONFIG: {
             if (args.count() < 1 || args.count() > 2) {
-                q->logMessage(format("CONFIG(config) requires one or two arguments."));
-                return ProItem::ReturnFalse;
-            }
-            if (args.count() == 1) {
-                //cond = isActiveConfig(args.first()); XXX
-                return ProItem::ReturnFalse;
+                evalError(fL1S("CONFIG(config) requires one or two arguments."));
+                return ReturnFalse;
             }
-            const QStringList mutuals = args[1].split(QLatin1Char('|'));
-            const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
+            if (args.count() == 1)
+                return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
+            const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
+            const ProStringList &configs = valuesDirect(statics.strCONFIG);
+
             for (int i = configs.size() - 1; i >= 0; i--) {
                 for (int mut = 0; mut < mutuals.count(); mut++) {
                     if (configs[i] == mutuals[mut].trimmed()) {
@@ -2031,68 +2769,81 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
                     }
                 }
             }
-            return ProItem::ReturnFalse;
+            return ReturnFalse;
         }
         case T_CONTAINS: {
             if (args.count() < 2 || args.count() > 3) {
-                q->logMessage(format("contains(var, val) requires two or three arguments."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("contains(var, val) requires two or three arguments."));
+                return ReturnFalse;
             }
 
-            QRegExp regx(args[1]);
-            const QStringList &l = values(args.first());
+            const QString &qry = args.at(1).toQString(m_tmp1);
+            QRegExp regx;
+            if (qry != QRegExp::escape(qry)) {
+                QString copy = qry;
+                copy.detach();
+                regx.setPattern(copy);
+            }
+            const ProStringList &l = values(map(args.at(0)));
             if (args.count() == 2) {
+                int t = 0;
                 for (int i = 0; i < l.size(); ++i) {
-                    const QString val = l[i];
-                    if (regx.exactMatch(val) || val == args[1]) {
-                        return ProItem::ReturnTrue;
-                    }
+                    const ProString &val = l[i];
+                    if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
+                        return ReturnTrue;
+                    t ^= 1;
                 }
             } else {
-                const QStringList mutuals = args[2].split(QLatin1Char('|'));
+                const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
                 for (int i = l.size() - 1; i >= 0; i--) {
-                    const QString val = l[i];
+                    const ProString val = l[i];
                     for (int mut = 0; mut < mutuals.count(); mut++) {
                         if (val == mutuals[mut].trimmed()) {
-                            return returnBool(regx.exactMatch(val) || val == args[1]);
+                            return returnBool((!regx.isEmpty()
+                                               && regx.exactMatch(val.toQString(m_tmp2)))
+                                              || val == qry);
                         }
                     }
                 }
             }
-            return ProItem::ReturnFalse;
+            return ReturnFalse;
         }
         case T_COUNT: {
             if (args.count() != 2 && args.count() != 3) {
-                q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
+                return ReturnFalse;
             }
+            int cnt = values(map(args.at(0))).count();
             if (args.count() == 3) {
-                QString comp = args[2];
+                const ProString &comp = args.at(2);
+                const int val = args.at(1).toQString(m_tmp1).toInt();
                 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
-                    return returnBool(values(args.first()).count() > args[1].toInt());
+                    return returnBool(cnt > val);
                 } else if (comp == QLatin1String(">=")) {
-                    return returnBool(values(args.first()).count() >= args[1].toInt());
+                    return returnBool(cnt >= val);
                 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
-                    return returnBool(values(args.first()).count() < args[1].toInt());
+                    return returnBool(cnt < val);
                 } else if (comp == QLatin1String("<=")) {
-                    return returnBool(values(args.first()).count() <= args[1].toInt());
+                    return returnBool(cnt <= val);
                 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
                            || comp == QLatin1String("=") || comp == QLatin1String("==")) {
-                    return returnBool(values(args.first()).count() == args[1].toInt());
+                    return returnBool(cnt == val);
                 } else {
-                    q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
-                    return ProItem::ReturnFalse;
+                    evalError(fL1S("unexpected modifier to count(%2)").arg(comp.toQString(m_tmp1)));
+                    return ReturnFalse;
                 }
             }
-            return returnBool(values(args.first()).count() == args[1].toInt());
+            return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
         }
         case T_GREATERTHAN:
         case T_LESSTHAN: {
             if (args.count() != 2) {
-                q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("%1(variable, value) requires two arguments.")
+                          .arg(function.toQString(m_tmp1)));
+                return ReturnFalse;
             }
-            QString rhs(args[1]), lhs(values(args[0]).join(QString(Option::field_sep)));
+            const QString &rhs(args.at(1).toQString(m_tmp1)),
+                          &lhs(values(map(args.at(0))).join(statics.field_sep));
             bool ok;
             int rhs_int = rhs.toInt(&ok);
             if (ok) { // do integer compare
@@ -2109,206 +2860,300 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
         }
         case T_EQUALS:
             if (args.count() != 2) {
-                q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("%1(variable, value) requires two arguments.")
+                          .arg(function.toQString(m_tmp1)));
+                return ReturnFalse;
             }
-            return returnBool(values(args[0]).join(QString(Option::field_sep)) == args[1]);
+            return returnBool(values(map(args.at(0))).join(statics.field_sep)
+                              == args.at(1).toQString(m_tmp1));
         case T_CLEAR: {
             if (m_skipLevel && !m_cumulative)
-                return ProItem::ReturnFalse;
+                return ReturnFalse;
             if (args.count() != 1) {
-                q->logMessage(format("%1(variable) requires one argument.").arg(function));
-                return ProItem::ReturnFalse;
-            }
-            QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
-            if (it == m_valuemap.end())
-                return ProItem::ReturnFalse;
-            it->clear();
-            return ProItem::ReturnTrue;
+                evalError(fL1S("%1(variable) requires one argument.")
+                          .arg(function.toQString(m_tmp1)));
+                return ReturnFalse;
+            }
+            QHash<ProString, ProStringList> *hsh;
+            QHash<ProString, ProStringList>::Iterator it;
+            const ProString &var = map(args.at(0));
+            if (!(hsh = findValues(var, &it)))
+                return ReturnFalse;
+            if (hsh == &m_valuemapStack.top())
+                it->clear();
+            else
+                m_valuemapStack.top()[var].clear();
+            return ReturnTrue;
         }
         case T_UNSET: {
             if (m_skipLevel && !m_cumulative)
-                return ProItem::ReturnFalse;
+                return ReturnFalse;
             if (args.count() != 1) {
-                q->logMessage(format("%1(variable) requires one argument.").arg(function));
-                return ProItem::ReturnFalse;
-            }
-            QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
-            if (it == m_valuemap.end())
-                return ProItem::ReturnFalse;
-            m_valuemap.erase(it);
-            return ProItem::ReturnTrue;
+                evalError(fL1S("%1(variable) requires one argument.")
+                          .arg(function.toQString(m_tmp1)));
+                return ReturnFalse;
+            }
+            QHash<ProString, ProStringList> *hsh;
+            QHash<ProString, ProStringList>::Iterator it;
+            const ProString &var = map(args.at(0));
+            if (!(hsh = findValues(var, &it)))
+                return ReturnFalse;
+            if (m_valuemapStack.size() == 1)
+                hsh->erase(it);
+            else if (hsh == &m_valuemapStack.top())
+                *it = statics.fakeValue;
+            else
+                m_valuemapStack.top()[var] = statics.fakeValue;
+            return ReturnTrue;
         }
         case T_INCLUDE: {
             if (m_skipLevel && !m_cumulative)
-                return ProItem::ReturnFalse;
+                return ReturnFalse;
             QString parseInto;
             // the third optional argument to include() controls warnings
             //      and is not used here
-            if ((args.count() == 2) || (args.count() == 3)) {
-                parseInto = args[1];
+            if ((args.count() == 2) || (args.count() == 3) ) {
+                parseInto = args.at(1).toQString(m_tmp2);
             } else if (args.count() != 1) {
-                q->logMessage(format("include(file) requires one, two or three arguments."));
-                return ProItem::ReturnFalse;
-            }
-            QString fileName = args.first();
-            // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
-            QDir currentProPath(currentDirectory());
-            fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
-            State sts = m_sts;
-            bool ok = evaluateFile(fileName);
-            m_sts = sts;
+                evalError(fL1S("include(file, into, silent) requires one, two or three arguments."));
+                return ReturnFalse;
+            }
+            QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
+            fn.detach();
+            bool ok;
+            if (parseInto.isEmpty()) {
+                ok = evaluateFile(fn, ProFileEvaluatorHandler::EvalIncludeFile,
+                                  ProFileEvaluator::LoadProOnly);
+            } else {
+                QHash<ProString, ProStringList> symbols;
+                if ((ok = evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
+                                           &symbols, 0, EvalWithSetup))) {
+                    QHash<ProString, ProStringList> newMap;
+                    for (QHash<ProString, ProStringList>::ConstIterator
+                            it = m_valuemapStack.top().constBegin(),
+                            end = m_valuemapStack.top().constEnd();
+                            it != end; ++it) {
+                        const QString &ky = it.key().toQString(m_tmp1);
+                        if (!(ky.startsWith(parseInto) &&
+                              (ky.length() == parseInto.length()
+                               || ky.at(parseInto.length()) == QLatin1Char('.'))))
+                            newMap[it.key()] = it.value();
+                    }
+                    for (QHash<ProString, ProStringList>::ConstIterator it = symbols.constBegin();
+                         it != symbols.constEnd(); ++it) {
+                        const QString &ky = it.key().toQString(m_tmp1);
+                        if (!ky.startsWith(QLatin1Char('.')))
+                            newMap.insert(ProString(parseInto + QLatin1Char('.') + ky), it.value());
+                    }
+                    m_valuemapStack.top() = newMap;
+                }
+            }
             return returnBool(ok);
         }
         case T_LOAD: {
             if (m_skipLevel && !m_cumulative)
-                return ProItem::ReturnFalse;
-            QString parseInto;
+                return ReturnFalse;
             bool ignore_error = false;
             if (args.count() == 2) {
-                QString sarg = args[1];
-                ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
+                ignore_error = isTrue(args.at(1), m_tmp2);
             } else if (args.count() != 1) {
-                q->logMessage(format("load(feature) requires one or two arguments."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("load(feature) requires one or two arguments."));
+                return ReturnFalse;
             }
             // XXX ignore_error unused
-            return returnBool(evaluateFeatureFile(args.first()));
+            return returnBool(evaluateFeatureFile(expandEnvVars(args.at(0).toQString())));
         }
         case T_DEBUG:
             // Yup - do nothing. Nothing is going to enable debug output anyway.
-            return ProItem::ReturnFalse;
+            return ReturnFalse;
         case T_MESSAGE: {
             if (args.count() != 1) {
-                q->logMessage(format("%1(message) requires one argument.").arg(function));
-                return ProItem::ReturnFalse;
-            }
-            QString msg = fixEnvVariables(args.first());
-            q->fileMessage(QString::fromLatin1("Project %1: %2").arg(function.toUpper(), msg));
+                evalError(fL1S("%1(message) requires one argument.")
+                          .arg(function.toQString(m_tmp1)));
+                return ReturnFalse;
+            }
+            const QString &msg = expandEnvVars(args.at(0).toQString(m_tmp2));
+            if (!m_skipLevel)
+                m_handler->fileMessage(fL1S("Project %1: %2")
+                                       .arg(function.toQString(m_tmp1).toUpper(), msg));
             // ### Consider real termination in non-cumulative mode
             return returnBool(function != QLatin1String("error"));
         }
 #if 0 // Way too dangerous to enable.
         case T_SYSTEM: {
             if (args.count() != 1) {
-                q->logMessage(format("system(exec) requires one argument."));
-                ProItem::ReturnFalse;
-            }
-            return returnBool(system(args.first().toLatin1().constData()) == 0);
+                evalError(fL1S("system(exec) requires one argument."));
+                return ReturnFalse;
+            }
+#ifndef QT_BOOTSTRAPPED
+            QProcess proc;
+            proc.setProcessChannelMode(QProcess::MergedChannels);
+            runProcess(&proc, args.at(0).toQString(m_tmp2), QProcess::StandardOutput);
+            return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0);
+#else
+            return returnBool(system((QLatin1String("cd ")
+                                      + IoUtils::shellQuote(currentDirectory())
+                                      + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
+#endif
         }
 #endif
         case T_ISEMPTY: {
             if (args.count() != 1) {
-                q->logMessage(format("isEmpty(var) requires one argument."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("isEmpty(var) requires one argument."));
+                return ReturnFalse;
             }
-            QStringList sl = values(args.first());
+            const ProStringList &sl = values(map(args.at(0)));
             if (sl.count() == 0) {
-                return ProItem::ReturnTrue;
+                return ReturnTrue;
             } else if (sl.count() > 0) {
-                QString var = sl.first();
+                const ProString &var = sl.first();
                 if (var.isEmpty())
-                    return ProItem::ReturnTrue;
+                    return ReturnTrue;
             }
-            return ProItem::ReturnFalse;
+            return ReturnFalse;
         }
         case T_EXISTS: {
             if (args.count() != 1) {
-                q->logMessage(format("exists(file) requires one argument."));
-                return ProItem::ReturnFalse;
+                evalError(fL1S("exists(file) requires one argument."));
+                return ReturnFalse;
             }
-            QString file = args.first();
-            file = Option::fixPathToLocalOS(file);
+            const QString &file = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
 
-            if (QFile::exists(file)) {
-                return ProItem::ReturnTrue;
+            if (IoUtils::exists(file)) {
+                return ReturnTrue;
             }
-            //regular expression I guess
-            QString dirstr = currentDirectory();
-            int slsh = file.lastIndexOf(Option::dir_sep);
-            if (slsh != -1) {
-                dirstr = file.left(slsh+1);
-                file = file.right(file.length() - slsh - 1);
+            int slsh = file.lastIndexOf(QLatin1Char('/'));
+            QString fn = file.mid(slsh+1);
+            if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
+                QString dirstr = file.left(slsh+1);
+                if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
+                    return ReturnTrue;
             }
-            if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?')))
-                if (!QDir(dirstr).entryList(QStringList(file)).isEmpty())
-                    return ProItem::ReturnTrue;
 
-            return ProItem::ReturnFalse;
+            return ReturnFalse;
         }
-        case 0:
-            q->logMessage(format("'%1' is not a recognized test function").arg(function));
-            return ProItem::ReturnFalse;
+        case T_INVALID:
+            evalError(fL1S("'%1' is not a recognized test function")
+                      .arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
         default:
-            q->logMessage(format("Function '%1' is not implemented").arg(function));
-            return ProItem::ReturnFalse;
+            evalError(fL1S("Function '%1' is not implemented").arg(function.toQString(m_tmp1)));
+            return ReturnFalse;
+    }
+}
+
+QHash<ProString, ProStringList> *ProFileEvaluator::Private::findValues(
+        const ProString &variableName, QHash<ProString, ProStringList>::Iterator *rit)
+{
+    for (int i = m_valuemapStack.size(); --i >= 0; ) {
+        QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(variableName);
+        if (it != m_valuemapStack[i].end()) {
+            if (it->constBegin() == statics.fakeValue.constBegin())
+                return 0;
+            *rit = it;
+            return &m_valuemapStack[i];
+        }
+    }
+    return 0;
+}
+
+ProStringList &ProFileEvaluator::Private::valuesRef(const ProString &variableName)
+{
+    QHash<ProString, ProStringList>::Iterator it = m_valuemapStack.top().find(variableName);
+    if (it != m_valuemapStack.top().end())
+        return *it;
+    for (int i = m_valuemapStack.size() - 1; --i >= 0; ) {
+        QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
+        if (it != m_valuemapStack.at(i).constEnd()) {
+            ProStringList &ret = m_valuemapStack.top()[variableName];
+            ret = *it;
+            return ret;
+        }
+    }
+    return m_valuemapStack.top()[variableName];
+}
+
+ProStringList ProFileEvaluator::Private::valuesDirect(const ProString &variableName) const
+{
+    for (int i = m_valuemapStack.size(); --i >= 0; ) {
+        QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
+        if (it != m_valuemapStack.at(i).constEnd()) {
+            if (it->constBegin() == statics.fakeValue.constBegin())
+                break;
+            return *it;
+        }
     }
+    return ProStringList();
 }
 
-QStringList ProFileEvaluator::Private::values(const QString &variableName,
-                                              const QHash<QString, QStringList> &place,
-                                              const ProFile *pro) const
-{
-    if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token
-        return QStringList(QLatin1String("\t"));
-    if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $
-        return QStringList(QLatin1String("$"));
-    if (variableName == QLatin1String("LITERAL_HASH")) //a real #
-        return QStringList(QLatin1String("#"));
-    if (variableName == QLatin1String("OUT_PWD")) //the out going dir
-        return QStringList(m_outputDir);
-    if (variableName == QLatin1String("PWD") ||  //current working dir (of _FILE_)
-        variableName == QLatin1String("IN_PWD"))
-        return QStringList(currentDirectory());
-    if (variableName == QLatin1String("DIR_SEPARATOR"))
-        return QStringList(Option::dir_sep);
-    if (variableName == QLatin1String("DIRLIST_SEPARATOR"))
-        return QStringList(Option::dirlist_sep);
-    if (variableName == QLatin1String("_LINE_")) //parser line number
-        return QStringList(QString::number(m_lineNo));
-    if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here
-        return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName());
-    if (variableName == QLatin1String("_DATE_")) //current date/time
-        return QStringList(QDateTime::currentDateTime().toString());
-    if (variableName == QLatin1String("_PRO_FILE_"))
-        return QStringList(m_origfile);
-    if (variableName == QLatin1String("_PRO_FILE_PWD_"))
-        return  QStringList(QFileInfo(m_origfile).absolutePath());
-    if (variableName == QLatin1String("_QMAKE_CACHE_"))
-        return QStringList(); // FIXME?
-    if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) {
-        QString ret, type = variableName.mid(11);
+ProStringList ProFileEvaluator::Private::values(const ProString &variableName) const
+{
+    QHash<ProString, int>::ConstIterator vli = statics.varList.find(variableName);
+    if (vli != statics.varList.constEnd()) {
+        int vlidx = *vli;
+        QString ret;
+        switch ((VarName)vlidx) {
+        case V_LITERAL_WHITESPACE: ret = QLatin1String("\t"); break;
+        case V_LITERAL_DOLLAR: ret = QLatin1String("$"); break;
+        case V_LITERAL_HASH: ret = QLatin1String("#"); break;
+        case V_OUT_PWD: // the outgoing dir (shadow of _PRO_FILE_PWD_)
+            ret = m_outputDir;
+            break;
+        case V_PWD: // containing directory of most nested project/include file
+        case V_IN_PWD:
+            ret = currentDirectory();
+            break;
+        case V_DIR_SEPARATOR:
+            validateModes();
+            ret = m_option->dir_sep;
+            break;
+        case V_DIRLIST_SEPARATOR:
+            ret = m_option->dirlist_sep;
+            break;
+        case V__LINE_: // currently executed line number
+            ret = QString::number(m_current.line);
+            break;
+        case V__FILE_: // currently executed file
+            ret = m_current.pro->fileName();
+            break;
+        case V__DATE_: //current date/time
+            ret = QDateTime::currentDateTime().toString();
+            break;
+        case V__PRO_FILE_:
+            ret = m_profileStack.first()->fileName();
+            break;
+        case V__PRO_FILE_PWD_:
+            ret = m_profileStack.first()->directoryName();
+            break;
+        case V__QMAKE_CACHE_:
+            ret = m_option->cachefile;
+            break;
 #if defined(Q_OS_WIN32)
-        if (type == QLatin1String("os")) {
-            ret = QLatin1String("Windows");
-        } else if (type == QLatin1String("name")) {
+        case V_QMAKE_HOST_os: ret = QLatin1String("Windows"); break;
+        case V_QMAKE_HOST_name: {
             DWORD name_length = 1024;
-            wchar_t name[1024];
+            TCHAR name[1024];
             if (GetComputerName(name, &name_length))
-                ret = QString::fromWCharArray(name);
-        } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) {
-            QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
-            if (type == QLatin1String("version"))
-                ret = QString::number(ver);
-            else if (ver == QSysInfo::WV_Me)
-                ret = QLatin1String("WinMe");
-            else if (ver == QSysInfo::WV_95)
-                ret = QLatin1String("Win95");
-            else if (ver == QSysInfo::WV_98)
-                ret = QLatin1String("Win98");
-            else if (ver == QSysInfo::WV_NT)
-                ret = QLatin1String("WinNT");
-            else if (ver == QSysInfo::WV_2000)
-                ret = QLatin1String("Win2000");
-            else if (ver == QSysInfo::WV_2000)
-                ret = QLatin1String("Win2003");
-            else if (ver == QSysInfo::WV_XP)
-                ret = QLatin1String("WinXP");
-            else if (ver == QSysInfo::WV_VISTA)
-                ret = QLatin1String("WinVista");
-            else
-                ret = QLatin1String("Unknown");
-        } else if (type == QLatin1String("arch")) {
+                ret = QString::fromUtf16((ushort*)name, name_length);
+            break;
+        }
+        case V_QMAKE_HOST_version:
+            ret = QString::number(QSysInfo::WindowsVersion);
+            break;
+        case V_QMAKE_HOST_version_string:
+            switch (QSysInfo::WindowsVersion) {
+            case QSysInfo::WV_Me: ret = QLatin1String("WinMe"); break;
+            case QSysInfo::WV_95: ret = QLatin1String("Win95"); break;
+            case QSysInfo::WV_98: ret = QLatin1String("Win98"); break;
+            case QSysInfo::WV_NT: ret = QLatin1String("WinNT"); break;
+            case QSysInfo::WV_2000: ret = QLatin1String("Win2000"); break;
+            case QSysInfo::WV_2003: ret = QLatin1String("Win2003"); break;
+            case QSysInfo::WV_XP: ret = QLatin1String("WinXP"); break;
+            case QSysInfo::WV_VISTA: ret = QLatin1String("WinVista"); break;
+            default: ret = QLatin1String("Unknown"); break;
+            }
+            break;
+        case V_QMAKE_HOST_arch:
             SYSTEM_INFO info;
             GetSystemInfo(&info);
             switch(info.wProcessorArchitecture) {
@@ -2330,115 +3175,154 @@ QStringList ProFileEvaluator::Private::values(const QString &variableName,
                 ret = QLatin1String("Unknown");
                 break;
             }
-        }
+            break;
 #elif defined(Q_OS_UNIX)
-        struct utsname name;
-        if (!uname(&name)) {
-            if (type == QLatin1String("os"))
-                ret = QString::fromLatin1(name.sysname);
-            else if (type == QLatin1String("name"))
-                ret = QString::fromLatin1(name.nodename);
-            else if (type == QLatin1String("version"))
-                ret = QString::fromLatin1(name.release);
-            else if (type == QLatin1String("version_string"))
-                ret = QString::fromLatin1(name.version);
-            else if (type == QLatin1String("arch"))
-                ret = QString::fromLatin1(name.machine);
-        }
+        case V_QMAKE_HOST_os:
+        case V_QMAKE_HOST_name:
+        case V_QMAKE_HOST_version:
+        case V_QMAKE_HOST_version_string:
+        case V_QMAKE_HOST_arch:
+            {
+                struct utsname name;
+                const char *what;
+                if (!uname(&name)) {
+                    switch (vlidx) {
+                    case V_QMAKE_HOST_os: what = name.sysname; break;
+                    case V_QMAKE_HOST_name: what = name.nodename; break;
+                    case V_QMAKE_HOST_version: what = name.release; break;
+                    case V_QMAKE_HOST_version_string: what = name.version; break;
+                    case V_QMAKE_HOST_arch: what = name.machine; break;
+                    }
+                    ret = QString::fromLocal8Bit(what);
+                }
+            }
 #endif
-        return QStringList(ret);
+        }
+        return ProStringList(ProString(ret, NoHash));
     }
 
-    QStringList result = place[variableName];
+    ProStringList result = valuesDirect(variableName);
     if (result.isEmpty()) {
-        if (variableName == QLatin1String("TARGET")) {
-            result.append(QFileInfo(m_origfile).baseName());
-        } else if (variableName == QLatin1String("TEMPLATE")) {
-            result.append(QLatin1String("app"));
-        } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) {
-            result.append(Option::dirlist_sep);
+        if (variableName == statics.strTEMPLATE) {
+            result.append(ProString("app", NoHash));
+        } else if (variableName == statics.strQMAKE_DIR_SEP) {
+            result.append(ProString(m_option->dirlist_sep, NoHash));
         }
     }
     return result;
 }
 
-QStringList ProFileEvaluator::Private::values(const QString &variableName) const
-{
-    return values(variableName, m_valuemap, currentProFile());
-}
-
-QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
-{
-    return values(variableName, m_filevaluemap[pro], pro);
-}
-
-ProFile *ProFileEvaluator::parsedProFile(const QString &fileName)
+bool ProFileEvaluator::Private::evaluateFileDirect(
+        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+        ProFileEvaluator::LoadFlags flags)
 {
-    QFileInfo fi(fileName);
-    if (fi.exists()) {
-        QString fn = QDir::cleanPath(fi.absoluteFilePath());
-        foreach (const ProFile *pf, d->m_profileStack)
-            if (pf->fileName() == fn) {
-                errorMessage(d->format("circular inclusion of %1").arg(fn));
-                return 0;
-            }
-        ProFile *pro = new ProFile(fn);
-        if (d->read(pro))
-            return pro;
-        delete pro;
+    if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
+        m_locationStack.push(m_current);
+        bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
+        m_current = m_locationStack.pop();
+        pro->deref();
+        return ok;
+    } else {
+        return false;
     }
-    return 0;
-}
-
-void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
-{
-    delete proFile;
 }
 
-bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
+bool ProFileEvaluator::Private::evaluateFile(
+        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+        ProFileEvaluator::LoadFlags flags)
 {
-    ProFile *pro = q->parsedProFile(fileName);
-    if (pro) {
-        m_profileStack.push(pro);
-        bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
-        m_profileStack.pop();
-        q->releaseParsedProFile(pro);
-        return ok;
-    } else {
+    if (fileName.isEmpty())
         return false;
-    }
+    foreach (const ProFile *pf, m_profileStack)
+        if (pf->fileName() == fileName) {
+            evalError(fL1S("circular inclusion of %1").arg(fileName));
+            return false;
+        }
+    return evaluateFileDirect(fileName, type, flags);
 }
 
 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
 {
-    QString fn;
-    foreach (const QString &path, qmakeFeaturePaths()) {
-        QString fname = path + QLatin1Char('/') + fileName;
-        if (QFileInfo(fname).exists()) {
-            fn = fname;
-            break;
+    QString fn = fileName;
+    if (!fn.endsWith(QLatin1String(".prf")))
+        fn += QLatin1String(".prf");
+
+    if ((!fileName.contains((ushort)'/') && !fileName.contains((ushort)'\\'))
+        || !IoUtils::exists(resolvePath(fn))) {
+        if (m_option->feature_roots.isEmpty())
+            m_option->feature_roots = qmakeFeaturePaths();
+        int start_root = 0;
+        QString currFn = currentFileName();
+        if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
+            for (int root = 0; root < m_option->feature_roots.size(); ++root)
+                if (currFn == m_option->feature_roots.at(root) + fn) {
+                    start_root = root + 1;
+                    break;
+                }
         }
-        fname += QLatin1String(".prf");
-        if (QFileInfo(fname).exists()) {
-            fn = fname;
-            break;
+        for (int root = start_root; root < m_option->feature_roots.size(); ++root) {
+            QString fname = m_option->feature_roots.at(root) + fn;
+            if (IoUtils::exists(fname)) {
+                fn = fname;
+                goto cool;
+            }
         }
-    }
-    if (fn.isEmpty())
         return false;
+
+      cool:
+        // It's beyond me why qmake has this inside this if ...
+        ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES"));
+        ProString afn(fn, NoHash);
+        if (already.contains(afn))
+            return true;
+        already.append(afn);
+    } else {
+        fn = resolvePath(fn);
+    }
+
+#ifdef PROEVALUATOR_CUMULATIVE
     bool cumulative = m_cumulative;
     m_cumulative = false;
-    bool ok = evaluateFile(fn);
+#endif
+
+    // The path is fully normalized already.
+    bool ok = evaluateFileDirect(fn, ProFileEvaluatorHandler::EvalFeatureFile,
+                                 ProFileEvaluator::LoadProOnly);
+
+#ifdef PROEVALUATOR_CUMULATIVE
     m_cumulative = cumulative;
+#endif
     return ok;
 }
 
-QString ProFileEvaluator::Private::format(const char *fmt) const
+bool ProFileEvaluator::Private::evaluateFileInto(
+        const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
+        QHash<ProString, ProStringList> *values, FunctionDefs *funcs, EvalIntoMode mode)
 {
-    ProFile *pro = currentProFile();
-    QString fileName = pro ? pro->fileName() : QLatin1String("Not a file");
-    int lineNumber = pro ? m_lineNo : 0;
-    return QString::fromLatin1("%1(%2):").arg(fileName).arg(lineNumber) + QString::fromAscii(fmt);
+    ProFileEvaluator visitor(m_option, m_parser, m_handler);
+#ifdef PROEVALUATOR_CUMULATIVE
+    visitor.d->m_cumulative = false;
+#endif
+    visitor.d->m_outputDir = m_outputDir;
+//    visitor.d->m_valuemapStack.top() = *values;
+    if (funcs)
+        visitor.d->m_functionDefs = *funcs;
+    if (mode == EvalWithDefaults)
+        visitor.d->evaluateFeatureFile(QLatin1String("default_pre.prf"));
+    if (!visitor.d->evaluateFile(fileName, type,
+            (mode == EvalWithSetup) ? ProFileEvaluator::LoadAll : ProFileEvaluator::LoadProOnly))
+        return false;
+    *values = visitor.d->m_valuemapStack.top();
+//    if (funcs)
+//        *funcs = visitor.d->m_functionDefs;
+    return true;
+}
+
+void ProFileEvaluator::Private::evalError(const QString &message) const
+{
+    if (!m_skipLevel)
+        m_handler->evalError(m_current.line ? m_current.pro->fileName() : QString(),
+                             m_current.line, message);
 }
 
 
@@ -2448,10 +3332,15 @@ QString ProFileEvaluator::Private::format(const char *fmt) const
 //
 ///////////////////////////////////////////////////////////////////////
 
-ProFileEvaluator::ProFileEvaluator()
-  : d(new Private(this))
+void ProFileEvaluator::initialize()
+{
+    Private::initStatics();
+}
+
+ProFileEvaluator::ProFileEvaluator(ProFileOption *option, ProFileParser *parser,
+                                   ProFileEvaluatorHandler *handler)
+  : d(new Private(this, option, parser, handler))
 {
-    Option::init();
 }
 
 ProFileEvaluator::~ProFileEvaluator()
@@ -2461,26 +3350,38 @@ ProFileEvaluator::~ProFileEvaluator()
 
 bool ProFileEvaluator::contains(const QString &variableName) const
 {
-    return d->m_valuemap.contains(variableName);
+    return d->m_valuemapStack.top().contains(ProString(variableName));
 }
 
-inline QStringList fixEnvVariables(const QStringList &x)
+QString ProFileEvaluator::value(const QString &variable) const
 {
-    QStringList ret;
-    foreach (const QString &str, x)
-        ret << Option::fixString(str, Option::FixEnvVars);
-    return ret;
-}
+    const QStringList &vals = values(variable);
+    if (!vals.isEmpty())
+        return vals.first();
 
+    return QString();
+}
 
 QStringList ProFileEvaluator::values(const QString &variableName) const
 {
-    return fixEnvVariables(d->values(variableName));
+    const ProStringList &values = d->values(ProString(variableName));
+    QStringList ret;
+    ret.reserve(values.size());
+    foreach (const ProString &str, values)
+        ret << d->expandEnvVars(str.toQString());
+    return ret;
 }
 
 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
 {
-    return fixEnvVariables(d->values(variableName, pro));
+    // It makes no sense to put any kind of magic into expanding these
+    const ProStringList &values = d->m_valuemapStack.at(0).value(ProString(variableName));
+    QStringList ret;
+    ret.reserve(values.size());
+    foreach (const ProString &str, values)
+        if (str.sourceFile() == pro)
+            ret << d->expandEnvVars(str.toQString());
+    return ret;
 }
 
 QStringList ProFileEvaluator::absolutePathValues(
@@ -2488,9 +3389,10 @@ QStringList ProFileEvaluator::absolutePathValues(
 {
     QStringList result;
     foreach (const QString &el, values(variable)) {
-        const QFileInfo info = QFileInfo(baseDirectory, el);
-        if (info.isDir())
-            result << QDir::cleanPath(info.absoluteFilePath());
+        QString absEl = IoUtils::isAbsolutePath(el)
+            ? d->sysrootify(el, baseDirectory) : IoUtils::resolvePath(baseDirectory, el);
+        if (IoUtils::fileType(absEl) == IoUtils::FileIsDir)
+            result << QDir::cleanPath(absEl);
     }
     return result;
 }
@@ -2501,34 +3403,40 @@ QStringList ProFileEvaluator::absoluteFileValues(
 {
     QStringList result;
     foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
-        QFileInfo info(el);
-        if (info.isAbsolute()) {
-            if (info.exists()) {
-                result << QDir::cleanPath(el);
+        QString absEl;
+        if (IoUtils::isAbsolutePath(el)) {
+            const QString elWithSysroot = d->sysrootify(el, baseDirectory);
+            if (IoUtils::exists(elWithSysroot)) {
+                result << QDir::cleanPath(elWithSysroot);
                 goto next;
             }
+            absEl = elWithSysroot;
         } else {
             foreach (const QString &dir, searchDirs) {
-                QFileInfo info(dir, el);
-                if (info.isFile()) {
-                    result << QDir::cleanPath(info.filePath());
+                QString fn = dir + QLatin1Char('/') + el;
+                if (IoUtils::exists(fn)) {
+                    result << QDir::cleanPath(fn);
                     goto next;
                 }
             }
             if (baseDirectory.isEmpty())
                 goto next;
-            info = QFileInfo(baseDirectory, el);
+            absEl = baseDirectory + QLatin1Char('/') + el;
         }
         {
-            QFileInfo baseInfo(info.absolutePath());
-            if (baseInfo.exists()) {
-                QString wildcard = info.fileName();
+            absEl = QDir::cleanPath(absEl);
+            int nameOff = absEl.lastIndexOf(QLatin1Char('/'));
+            QString absDir = d->m_tmp1.setRawData(absEl.constData(), nameOff);
+            if (IoUtils::exists(absDir)) {
+                QString wildcard = d->m_tmp2.setRawData(absEl.constData() + nameOff + 1,
+                                                        absEl.length() - nameOff - 1);
                 if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
-                    QDir theDir(QDir::cleanPath(baseInfo.filePath()));
+                    wildcard.detach(); // Keep m_tmp out of QRegExp's cache
+                    QDir theDir(absDir);
                     foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
-                        if (fn != QLatin1String(".") && fn != QLatin1String(".."))
-                            result << theDir.absoluteFilePath(fn);
-                }
+                        if (fn != statics.strDot && fn != statics.strDotDot)
+                            result << absDir + QLatin1Char('/') + fn;
+                } // else if (acceptMissing)
             }
         }
       next: ;
@@ -2536,86 +3444,41 @@ QStringList ProFileEvaluator::absoluteFileValues(
     return result;
 }
 
-ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
+ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
 {
-    QStringList templ = values(QLatin1String("TEMPLATE"));
+    const ProStringList &templ = d->values(statics.strTEMPLATE);
     if (templ.count() >= 1) {
-        const QString &t = templ.last();
+        const QString &t = templ.at(0).toQString();
         if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
             return TT_Application;
         if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
             return TT_Library;
         if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
             return TT_Script;
+        if (!t.compare(QLatin1String("aux"), Qt::CaseInsensitive))
+            return TT_Aux;
         if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
             return TT_Subdirs;
     }
     return TT_Unknown;
 }
 
-bool ProFileEvaluator::queryProFile(ProFile *pro)
-{
-    return d->read(pro);
-}
-
-bool ProFileEvaluator::accept(ProFile *pro)
+bool ProFileEvaluator::accept(ProFile *pro, LoadFlags flags)
 {
-    return pro->Accept(d);
+    return d->visitProFile(pro, ProFileEvaluatorHandler::EvalProjectFile, flags) == Private::ReturnTrue;
 }
 
 QString ProFileEvaluator::propertyValue(const QString &name) const
 {
-    return d->propertyValue(name);
-}
-
-namespace {
-    template<class K, class T> void insert(QHash<K,T> *out, const QHash<K,T> &in)
-    {
-        typename QHash<K,T>::const_iterator i = in.begin();
-        while (i != in.end()) {
-            out->insert(i.key(), i.value());
-            ++i;
-        }
-    }
-} // anon namespace
-
-void ProFileEvaluator::addVariables(const QHash<QString, QStringList> &variables)
-{
-    insert(&(d->m_valuemap), variables);
-}
-
-void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties)
-{
-    insert(&(d->m_properties), properties);
-}
-
-void ProFileEvaluator::logMessage(const QString &message)
-{
-    if (d->m_verbose && !d->m_skipLevel)
-        fprintf(stderr, "%s\n", qPrintable(message));
-}
-
-void ProFileEvaluator::fileMessage(const QString &message)
-{
-    if (!d->m_skipLevel)
-        fprintf(stderr, "%s\n", qPrintable(message));
-}
-
-void ProFileEvaluator::errorMessage(const QString &message)
-{
-    if (!d->m_skipLevel)
-        fprintf(stderr, "%s\n", qPrintable(message));
-}
-
-void ProFileEvaluator::setVerbose(bool on)
-{
-    d->m_verbose = on;
+    return d->propertyValue(name, false);
 }
 
+#ifdef PROEVALUATOR_CUMULATIVE
 void ProFileEvaluator::setCumulative(bool on)
 {
     d->m_cumulative = on;
 }
+#endif
 
 void ProFileEvaluator::setOutputDir(const QString &dir)
 {
index d4cdbcd..003042b 100644 (file)
-/****************************************************************************
+/**************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
 **
-** This file is part of the Qt Linguist of the Qt Toolkit.
 **
-** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
+**
 ** 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:
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
 ** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** 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.
-**
 ** Other Usage
+**
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
 **
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
 
 #ifndef PROFILEEVALUATOR_H
 #define PROFILEEVALUATOR_H
 
+#include "proparser_global.h"
 #include "proitems.h"
-#include "abstractproitemvisitor.h"
 
-#include <QtCore/QIODevice>
 #include <QtCore/QHash>
 #include <QtCore/QStringList>
-#include <QtCore/QStack>
-
-#if (!defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) && !defined(__SUNPRO_CC)
-# define HAVE_TEMPLATE_CLASS_FRIENDS
+#ifndef QT_BOOTSTRAPPED
+# include <QtCore/QProcess>
+#endif
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <QtCore/QMutex>
+# include <QtCore/QWaitCondition>
 #endif
 
 QT_BEGIN_NAMESPACE
 
-class ProFileEvaluator
+struct ProFileOption;
+class ProFileParser;
+
+class PROPARSER_EXPORT ProFileEvaluatorHandler
 {
-#ifdef HAVE_TEMPLATE_CLASS_FRIENDS
-private:
-#else
 public:
-#endif
+    // qmake/project configuration error
+    virtual void configError(const QString &msg) = 0;
+    // Some error during evaluation
+    virtual void evalError(const QString &filename, int lineNo, const QString &msg) = 0;
+    // error() and message() from .pro file
+    virtual void fileMessage(const QString &msg) = 0;
+
+    enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile };
+    virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0;
+    virtual void doneWithEval(ProFile *parent) = 0;
+};
+
+
+class PROPARSER_EXPORT ProFileEvaluator
+{
     class Private;
 
 public:
+    class FunctionDef {
+    public:
+        FunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
+        FunctionDef(const FunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
+        ~FunctionDef() { m_pro->deref(); }
+        FunctionDef &operator=(const FunctionDef &o)
+        {
+            if (this != &o) {
+                m_pro->deref();
+                m_pro = o.m_pro;
+                m_pro->ref();
+                m_offset = o.m_offset;
+            }
+            return *this;
+        }
+        ProFile *pro() const { return m_pro; }
+        const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
+    private:
+        ProFile *m_pro;
+        int m_offset;
+    };
+
+    struct FunctionDefs {
+        QHash<ProString, FunctionDef> testFunctions;
+        QHash<ProString, FunctionDef> replaceFunctions;
+    };
+
     enum TemplateType {
         TT_Unknown = 0,
         TT_Application,
         TT_Library,
         TT_Script,
+        TT_Aux,
         TT_Subdirs
     };
 
-    ProFileEvaluator();
-    virtual ~ProFileEvaluator();
+    // Call this from a concurrency-free context
+    static void initialize();
+
+    ProFileEvaluator(ProFileOption *option, ProFileParser *parser, ProFileEvaluatorHandler *handler);
+    ~ProFileEvaluator();
 
-    ProFileEvaluator::TemplateType templateType();
-    virtual bool contains(const QString &variableName) const;
-    void setVerbose(bool on); // Default is false
+    ProFileEvaluator::TemplateType templateType() const;
+#ifdef PROEVALUATOR_CUMULATIVE
     void setCumulative(bool on); // Default is true!
+#endif
     void setOutputDir(const QString &dir); // Default is empty
 
-    bool queryProFile(ProFile *pro);
-    bool accept(ProFile *pro);
+    enum LoadFlag {
+        LoadProOnly = 0,
+        LoadPreFiles = 1,
+        LoadPostFiles = 2,
+        LoadAll = LoadPreFiles|LoadPostFiles
+    };
+    Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
+    bool accept(ProFile *pro, LoadFlags flags = LoadAll);
 
-    void addVariables(const QHash<QString, QStringList> &variables);
-    void addProperties(const QHash<QString, QString> &properties);
+    bool contains(const QString &variableName) const;
+    QString value(const QString &variableName) const;
     QStringList values(const QString &variableName) const;
     QStringList values(const QString &variableName, const ProFile *pro) const;
     QStringList absolutePathValues(const QString &variable, const QString &baseDirectory) const;
@@ -96,21 +139,83 @@ public:
             const ProFile *pro) const;
     QString propertyValue(const QString &val) const;
 
-    // for our descendents
-    virtual ProFile *parsedProFile(const QString &fileName);
-    virtual void releaseParsedProFile(ProFile *proFile);
-    virtual void logMessage(const QString &msg);
-    virtual void errorMessage(const QString &msg); // .pro parse errors
-    virtual void fileMessage(const QString &msg); // error() and message() from .pro file
-
 private:
     Private *d;
 
-#ifdef HAVE_TEMPLATE_CLASS_FRIENDS
-    template<typename T> friend class QTypeInfo;
+    friend struct ProFileOption;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ProFileEvaluator::LoadFlags)
+
+// This struct is from qmake, but we are not using everything.
+struct PROPARSER_EXPORT ProFileOption
+{
+    ProFileOption();
+    ~ProFileOption();
+
+    //simply global convenience
+    //QString libtool_ext;
+    //QString pkgcfg_ext;
+    //QString prf_ext;
+    //QString prl_ext;
+    //QString ui_ext;
+    //QStringList h_ext;
+    //QStringList cpp_ext;
+    //QString h_moc_ext;
+    //QString cpp_moc_ext;
+    //QString obj_ext;
+    //QString lex_ext;
+    //QString yacc_ext;
+    //QString h_moc_mod;
+    //QString cpp_moc_mod;
+    //QString lex_mod;
+    //QString yacc_mod;
+    QString dir_sep;
+    QString dirlist_sep;
+    QString qmakespec;
+    QString cachefile;
+    QHash<QString, QString> properties;
+#ifndef QT_BOOTSTRAPPED
+    QProcessEnvironment environment;
+#endif
+    QString sysroot;
+
+    //QString pro_ext;
+    //QString res_ext;
+
+    // -nocache, -cache, -spec, QMAKESPEC
+    // -set persistent value
+    void setCommandLineArguments(const QStringList &args);
+#ifdef PROEVALUATOR_INIT_PROPS
+    bool initProperties(const QString &qmake);
+#endif
+
+  private:
+    friend class ProFileEvaluator;
+    friend class ProFileEvaluator::Private;
+
+    void applyHostMode();
+    QString getEnv(const QString &) const;
+
+    QHash<ProString, ProStringList> base_valuemap; // Cached results of qmake.conf, .qmake.cache & default_pre.prf
+    ProFileEvaluator::FunctionDefs base_functions;
+    QStringList feature_roots;
+    QString qmakespec_name;
+    QString precmds, postcmds;
+    enum HOST_MODE { HOST_UNKNOWN_MODE, HOST_UNIX_MODE, HOST_WIN_MODE, HOST_MACX_MODE };
+    HOST_MODE host_mode;
+    enum TARG_MODE { TARG_UNKNOWN_MODE, TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE,
+                     TARG_SYMBIAN_MODE };
+    TARG_MODE target_mode;
+#ifdef PROEVALUATOR_THREAD_SAFE
+    QMutex mutex;
+    QWaitCondition cond;
+    bool base_inProgress;
 #endif
 };
 
+Q_DECLARE_TYPEINFO(ProFileEvaluator::FunctionDef, Q_MOVABLE_TYPE);
+
 QT_END_NAMESPACE
 
 #endif // PROFILEEVALUATOR_H
diff --git a/src/linguist/shared/profileparser.cpp b/src/linguist/shared/profileparser.cpp
new file mode 100644 (file)
index 0000000..a541ab3
--- /dev/null
@@ -0,0 +1,1028 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "profileparser.h"
+
+#include "ioutils.h"
+using namespace ProFileEvaluatorInternal;
+
+#include <QtCore/QFile>
+#ifdef PROPARSER_THREAD_SAFE
+# include <QtCore/QThreadPool>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileCache
+//
+///////////////////////////////////////////////////////////////////////
+
+ProFileCache::~ProFileCache()
+{
+    foreach (const Entry &ent, parsed_files)
+        if (ent.pro)
+            ent.pro->deref();
+}
+
+void ProFileCache::discardFile(const QString &fileName)
+{
+#ifdef PROPARSER_THREAD_SAFE
+    QMutexLocker lck(&mutex);
+#endif
+    QHash<QString, Entry>::Iterator it = parsed_files.find(fileName);
+    if (it != parsed_files.end()) {
+        if (it->pro)
+            it->pro->deref();
+        parsed_files.erase(it);
+    }
+}
+
+void ProFileCache::discardFiles(const QString &prefix)
+{
+#ifdef PROPARSER_THREAD_SAFE
+    QMutexLocker lck(&mutex);
+#endif
+    QHash<QString, Entry>::Iterator
+            it = parsed_files.begin(),
+            end = parsed_files.end();
+    while (it != end)
+        if (it.key().startsWith(prefix)) {
+            if (it->pro)
+                it->pro->deref();
+            it = parsed_files.erase(it);
+        } else {
+            ++it;
+        }
+}
+
+
+////////// Parser ///////////
+
+#define fL1S(s) QString::fromLatin1(s)
+
+namespace { // MSVC2010 doesn't seem to know the semantics of "static" ...
+
+static struct {
+    QString strelse;
+    QString strfor;
+    QString strdefineTest;
+    QString strdefineReplace;
+} statics;
+
+}
+
+void ProFileParser::initialize()
+{
+    if (!statics.strelse.isNull())
+        return;
+
+    statics.strelse = QLatin1String("else");
+    statics.strfor = QLatin1String("for");
+    statics.strdefineTest = QLatin1String("defineTest");
+    statics.strdefineReplace = QLatin1String("defineReplace");
+}
+
+ProFileParser::ProFileParser(ProFileCache *cache, ProFileParserHandler *handler)
+    : m_cache(cache)
+    , m_handler(handler)
+{
+    // So that single-threaded apps don't have to call initialize() for now.
+    initialize();
+}
+
+ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const QString *contents)
+{
+    ProFile *pro;
+    if (cache && m_cache) {
+        ProFileCache::Entry *ent;
+#ifdef PROPARSER_THREAD_SAFE
+        QMutexLocker locker(&m_cache->mutex);
+#endif
+        QHash<QString, ProFileCache::Entry>::Iterator it = m_cache->parsed_files.find(fileName);
+        if (it != m_cache->parsed_files.end()) {
+            ent = &*it;
+#ifdef PROPARSER_THREAD_SAFE
+            if (ent->locker && !ent->locker->done) {
+                ++ent->locker->waiters;
+                QThreadPool::globalInstance()->releaseThread();
+                ent->locker->cond.wait(locker.mutex());
+                QThreadPool::globalInstance()->reserveThread();
+                if (!--ent->locker->waiters) {
+                    delete ent->locker;
+                    ent->locker = 0;
+                }
+            }
+#endif
+            if ((pro = ent->pro))
+                pro->ref();
+        } else {
+            ent = &m_cache->parsed_files[fileName];
+#ifdef PROPARSER_THREAD_SAFE
+            ent->locker = new ProFileCache::Entry::Locker;
+            locker.unlock();
+#endif
+            pro = new ProFile(fileName);
+            if (!(!contents ? read(pro) : read(pro, *contents))) {
+                delete pro;
+                pro = 0;
+            } else {
+                pro->ref();
+            }
+            ent->pro = pro;
+#ifdef PROPARSER_THREAD_SAFE
+            locker.relock();
+            if (ent->locker->waiters) {
+                ent->locker->done = true;
+                ent->locker->cond.wakeAll();
+            } else {
+                delete ent->locker;
+                ent->locker = 0;
+            }
+#endif
+        }
+    } else {
+        pro = new ProFile(fileName);
+        if (!(!contents ? read(pro) : read(pro, *contents))) {
+            delete pro;
+            pro = 0;
+        }
+    }
+    return pro;
+}
+
+bool ProFileParser::read(ProFile *pro)
+{
+    QFile file(pro->fileName());
+    if (!file.open(QIODevice::ReadOnly)) {
+        if (m_handler && IoUtils::exists(pro->fileName()))
+            m_handler->parseError(QString(), 0, fL1S("%1 not readable.").arg(pro->fileName()));
+        return false;
+    }
+
+    QString content(QString::fromLocal8Bit(file.readAll()));
+    file.close();
+    return read(pro, content);
+}
+
+void ProFileParser::putTok(ushort *&tokPtr, ushort tok)
+{
+    *tokPtr++ = tok;
+}
+
+void ProFileParser::putBlockLen(ushort *&tokPtr, uint len)
+{
+    *tokPtr++ = (ushort)len;
+    *tokPtr++ = (ushort)(len >> 16);
+}
+
+void ProFileParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len)
+{
+    memcpy(tokPtr, buf, len * 2);
+    tokPtr += len;
+}
+
+void ProFileParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
+{
+    uint hash = ProString::hash((const QChar *)buf, len);
+    ushort *tokPtr = pTokPtr;
+    *tokPtr++ = (ushort)hash;
+    *tokPtr++ = (ushort)(hash >> 16);
+    *tokPtr++ = (ushort)len;
+    memcpy(tokPtr, buf, len * 2);
+    pTokPtr = tokPtr + len;
+}
+
+void ProFileParser::finalizeHashStr(ushort *buf, uint len)
+{
+    buf[-4] = TokHashLiteral;
+    buf[-1] = len;
+    uint hash = ProString::hash((const QChar *)buf, len);
+    buf[-3] = (ushort)hash;
+    buf[-2] = (ushort)(hash >> 16);
+}
+
+bool ProFileParser::read(ProFile *pro, const QString &in)
+{
+    m_proFile = pro;
+    m_lineNo = 1;
+
+    // Final precompiled token stream buffer
+    QString tokBuff;
+    // Worst-case size calculations:
+    // - line marker adds 1 (2-nl) to 1st token of each line
+    // - empty assignment "A=":2 =>
+    //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
+    //   TokValueTerminator(1) == 7 (8)
+    // - non-empty assignment "A=B C":5 =>
+    //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
+    //   TokLiteral(1) + len(1) + "B"(1) +
+    //   TokLiteral(1) + len(1) + "C"(1) + TokValueTerminator(1) == 13 (14)
+    // - variable expansion: "$$f":3 =>
+    //   TokVariable(1) + hash(2) + len(1) + "f"(1) = 5
+    // - function expansion: "$$f()":5 =>
+    //   TokFuncName(1) + hash(2) + len(1) + "f"(1) + TokFuncTerminator(1) = 6
+    // - scope: "X:":2 =>
+    //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokCondition(1) +
+    //   TokBranch(1) + len(2) + ... + len(2) + ... == 10
+    // - test: "X():":4 =>
+    //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokTestCall(1) + TokFuncTerminator(1) +
+    //   TokBranch(1) + len(2) + ... + len(2) + ... == 11
+    // - "for(A,B):":9 =>
+    //   TokForLoop(1) + hash(2) + len(1) + "A"(1) +
+    //   len(2) + TokLiteral(1) + len(1) + "B"(1) + TokValueTerminator(1) +
+    //   len(2) + ... + TokTerminator(1) == 14 (15)
+    tokBuff.reserve((in.size() + 1) * 5);
+    ushort *tokPtr = (ushort *)tokBuff.constData(); // Current writing position
+
+    // Expression precompiler buffer.
+    QString xprBuff;
+    xprBuff.reserve(tokBuff.capacity()); // Excessive, but simple
+    ushort * const buf = (ushort *)xprBuff.constData();
+
+    // Parser state
+    m_blockstack.clear();
+    m_blockstack.resize(1);
+
+    QStack<ParseCtx> xprStack;
+    xprStack.reserve(10);
+
+    // We rely on QStrings being null-terminated, so don't maintain a global end pointer.
+    const ushort *cur = (const ushort *)in.unicode();
+    m_canElse = false;
+  freshLine:
+    m_state = StNew;
+    m_invert = false;
+    m_operator = NoOperator;
+    m_markLine = m_lineNo;
+    m_inError = false;
+    Context context = CtxTest;
+    int parens = 0; // Braces in value context
+    int argc = 0;
+    int wordCount = 0; // Number of words in currently accumulated expression
+    bool putSpace = false; // Only ever true inside quoted string
+    bool lineMarked = true; // For in-expression markers
+    ushort needSep = TokNewStr; // Complementary to putSpace: separator outside quotes
+    ushort quote = 0;
+    ushort term = 0;
+
+    ushort *ptr = buf;
+    ptr += 4;
+    ushort *xprPtr = ptr;
+
+#define FLUSH_LHS_LITERAL() \
+    do { \
+        if ((tlen = ptr - xprPtr)) { \
+            finalizeHashStr(xprPtr, tlen); \
+            if (needSep) { \
+                wordCount++; \
+                needSep = 0; \
+            } \
+        } else { \
+            ptr -= 4; \
+        } \
+    } while (0)
+
+#define FLUSH_RHS_LITERAL() \
+    do { \
+        if ((tlen = ptr - xprPtr)) { \
+            xprPtr[-2] = TokLiteral | needSep; \
+            xprPtr[-1] = tlen; \
+            if (needSep) { \
+                wordCount++; \
+                needSep = 0; \
+            } \
+        } else { \
+            ptr -= 2; \
+        } \
+    } while (0)
+
+#define FLUSH_LITERAL() \
+    do { \
+        if (context == CtxTest) \
+            FLUSH_LHS_LITERAL(); \
+        else \
+            FLUSH_RHS_LITERAL(); \
+    } while (0)
+
+#define FLUSH_VALUE_LIST() \
+    do { \
+        if (wordCount > 1) { \
+            xprPtr = tokPtr; \
+            if (*xprPtr == TokLine) \
+                xprPtr += 2; \
+            tokPtr[-1] = ((*xprPtr & TokMask) == TokLiteral) ? wordCount : 0; \
+        } else { \
+            tokPtr[-1] = 0; \
+        } \
+        tokPtr = ptr; \
+        putTok(tokPtr, TokValueTerminator); \
+    } while (0)
+
+    forever {
+        ushort c;
+
+        // First, skip leading whitespace
+        for (;; ++cur) {
+            c = *cur;
+            if (c == '\n') {
+                ++cur;
+                goto flushLine;
+            } else if (!c) {
+                goto flushLine;
+            } else if (c != ' ' && c != '\t' && c != '\r') {
+                break;
+            }
+        }
+
+        // Then strip comments. Yep - no escaping is possible.
+        const ushort *end; // End of this line
+        const ushort *cptr; // Start of next line
+        for (cptr = cur;; ++cptr) {
+            c = *cptr;
+            if (c == '#') {
+                for (end = cptr; (c = *++cptr);) {
+                    if (c == '\n') {
+                        ++cptr;
+                        break;
+                    }
+                }
+                if (end == cur) { // Line with only a comment (sans whitespace)
+                    if (m_markLine == m_lineNo)
+                        m_markLine++;
+                    // Qmake bizarreness: such lines do not affect line continuations
+                    goto ignore;
+                }
+                break;
+            }
+            if (!c) {
+                end = cptr;
+                break;
+            }
+            if (c == '\n') {
+                end = cptr++;
+                break;
+            }
+        }
+
+        // Then look for line continuations. Yep - no escaping here as well.
+        bool lineCont;
+        forever {
+            // We don't have to check for underrun here, as we already determined
+            // that the line is non-empty.
+            ushort ec = *(end - 1);
+            if (ec == '\\') {
+                --end;
+                lineCont = true;
+                break;
+            }
+            if (ec != ' ' && ec != '\t' && ec != '\r') {
+                lineCont = false;
+                break;
+            }
+            --end;
+        }
+
+            // Finally, do the tokenization
+            ushort tok, rtok;
+            int tlen;
+          newWord:
+            do {
+                if (cur == end)
+                    goto lineEnd;
+                c = *cur++;
+            } while (c == ' ' || c == '\t');
+            forever {
+                if (c == '$') {
+                    if (*cur == '$') { // may be EOF, EOL, WS, '#' or '\\' if past end
+                        cur++;
+                        if (putSpace) {
+                            putSpace = false;
+                            *ptr++ = ' ';
+                        }
+                        FLUSH_LITERAL();
+                        if (!lineMarked) {
+                            lineMarked = true;
+                            *ptr++ = TokLine;
+                            *ptr++ = (ushort)m_lineNo;
+                        }
+                        term = 0;
+                        tok = TokVariable;
+                        c = *cur;
+                        if (c == '[') {
+                            ptr += 2;
+                            tok = TokProperty;
+                            term = ']';
+                            c = *++cur;
+                        } else if (c == '{') {
+                            ptr += 4;
+                            term = '}';
+                            c = *++cur;
+                        } else if (c == '(') {
+                            // FIXME: could/should expand this immediately
+                            ptr += 2;
+                            tok = TokEnvVar;
+                            term = ')';
+                            c = *++cur;
+                        } else {
+                            ptr += 4;
+                        }
+                        xprPtr = ptr;
+                        rtok = tok;
+                        while ((c & 0xFF00) || c == '.' || c == '_' ||
+                               (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+                               (c >= '0' && c <= '9')) {
+                            *ptr++ = c;
+                            if (++cur == end) {
+                                c = 0;
+                                goto notfunc;
+                            }
+                            c = *cur;
+                        }
+                        if (tok == TokVariable && c == '(')
+                            tok = TokFuncName;
+                      notfunc:
+                        if (quote)
+                            tok |= TokQuoted;
+                        if (needSep) {
+                            tok |= needSep;
+                            wordCount++;
+                        }
+                        tlen = ptr - xprPtr;
+                        if (rtok == TokVariable) {
+                            xprPtr[-4] = tok;
+                            uint hash = ProString::hash((const QChar *)xprPtr, tlen);
+                            xprPtr[-3] = (ushort)hash;
+                            xprPtr[-2] = (ushort)(hash >> 16);
+                        } else {
+                            xprPtr[-2] = tok;
+                        }
+                        xprPtr[-1] = tlen;
+                        if ((tok & TokMask) == TokFuncName) {
+                            cur++;
+                          funcCall:
+                            {
+                                xprStack.resize(xprStack.size() + 1);
+                                ParseCtx &top = xprStack.top();
+                                top.parens = parens;
+                                top.quote = quote;
+                                top.terminator = term;
+                                top.context = context;
+                                top.argc = argc;
+                                top.wordCount = wordCount;
+                            }
+                            parens = 0;
+                            quote = 0;
+                            term = 0;
+                            argc = 1;
+                            context = CtxArgs;
+                          nextToken:
+                            wordCount = 0;
+                          nextWord:
+                            ptr += (context == CtxTest) ? 4 : 2;
+                            xprPtr = ptr;
+                            needSep = TokNewStr;
+                            goto newWord;
+                        }
+                        if (term) {
+                            cur++;
+                          checkTerm:
+                            if (c != term) {
+                                parseError(fL1S("Missing %1 terminator [found %2]")
+                                    .arg(QChar(term))
+                                    .arg(c ? QString(c) : QString::fromLatin1("end-of-line")));
+                                pro->setOk(false);
+                                m_inError = true;
+                                if (c)
+                                    cur--;
+                                // Just parse on, as if there was a terminator ...
+                            }
+                        }
+                      joinToken:
+                        ptr += (context == CtxTest) ? 4 : 2;
+                        xprPtr = ptr;
+                        needSep = 0;
+                        goto nextChr;
+                    }
+                } else if (c == '\\' && cur != end) {
+                    static const char symbols[] = "[]{}()$\\'\"";
+                    ushort c2 = *cur;
+                    if (!(c2 & 0xff00) && strchr(symbols, c2)) {
+                        c = c2;
+                        cur++;
+                    }
+                } else if (quote) {
+                    if (c == quote) {
+                        quote = 0;
+                        if (putSpace) {
+                            putSpace = false;
+                            *ptr++ = ' ';
+                        }
+                        goto nextChr;
+                    } else if (c == ' ' || c == '\t') {
+                        putSpace = true;
+                        goto nextChr;
+                    } else if (c == '!' && ptr == xprPtr && context == CtxTest) {
+                        m_invert ^= true;
+                        goto nextChr;
+                    }
+                } else if (c == '\'' || c == '"') {
+                    quote = c;
+                    goto nextChr;
+                } else if (c == ' ' || c == '\t') {
+                    FLUSH_LITERAL();
+                    goto nextWord;
+                } else if (context == CtxArgs) {
+                    // Function arg context
+                    if (c == '(') {
+                        ++parens;
+                    } else if (c == ')') {
+                        if (--parens < 0) {
+                            FLUSH_RHS_LITERAL();
+                            *ptr++ = TokFuncTerminator;
+                            int theargc = argc;
+                            {
+                                ParseCtx &top = xprStack.top();
+                                parens = top.parens;
+                                quote = top.quote;
+                                term = top.terminator;
+                                context = top.context;
+                                argc = top.argc;
+                                wordCount = top.wordCount;
+                                xprStack.resize(xprStack.size() - 1);
+                            }
+                            if (term == ':') {
+                                finalizeCall(tokPtr, buf, ptr, theargc);
+                                goto nextItem;
+                            } else if (term == '}') {
+                                c = (cur == end) ? 0 : *cur++;
+                                goto checkTerm;
+                            } else {
+                                Q_ASSERT(!term);
+                                goto joinToken;
+                            }
+                        }
+                    } else if (!parens && c == ',') {
+                        FLUSH_RHS_LITERAL();
+                        *ptr++ = TokArgSeparator;
+                        argc++;
+                        goto nextToken;
+                    }
+                } else if (context == CtxTest) {
+                    // Test or LHS context
+                    if (c == '(') {
+                        FLUSH_LHS_LITERAL();
+                        if (wordCount != 1) {
+                            if (wordCount)
+                                parseError(fL1S("Extra characters after test expression."));
+                            else
+                                parseError(fL1S("Opening parenthesis without prior test name."));
+                            pro->setOk(false);
+                            ptr = buf; // Put empty function name
+                        }
+                        *ptr++ = TokTestCall;
+                        term = ':';
+                        goto funcCall;
+                    } else if (c == '!' && ptr == xprPtr) {
+                        m_invert ^= true;
+                        goto nextChr;
+                    } else if (c == ':') {
+                        FLUSH_LHS_LITERAL();
+                        finalizeCond(tokPtr, buf, ptr, wordCount);
+                        if (m_state == StNew)
+                            parseError(fL1S("And operator without prior condition."));
+                        else
+                            m_operator = AndOperator;
+                      nextItem:
+                        ptr = buf;
+                        goto nextToken;
+                    } else if (c == '|') {
+                        FLUSH_LHS_LITERAL();
+                        finalizeCond(tokPtr, buf, ptr, wordCount);
+                        if (m_state != StCond)
+                            parseError(fL1S("Or operator without prior condition."));
+                        else
+                            m_operator = OrOperator;
+                        goto nextItem;
+                    } else if (c == '{') {
+                        FLUSH_LHS_LITERAL();
+                        finalizeCond(tokPtr, buf, ptr, wordCount);
+                        flushCond(tokPtr);
+                        ++m_blockstack.top().braceLevel;
+                        goto nextItem;
+                    } else if (c == '}') {
+                        FLUSH_LHS_LITERAL();
+                        finalizeCond(tokPtr, buf, ptr, wordCount);
+                        flushScopes(tokPtr);
+                      closeScope:
+                        if (!m_blockstack.top().braceLevel) {
+                            parseError(fL1S("Excess closing brace."));
+                        } else if (!--m_blockstack.top().braceLevel
+                                   && m_blockstack.count() != 1) {
+                            leaveScope(tokPtr);
+                            m_state = StNew;
+                            m_canElse = false;
+                            m_markLine = m_lineNo;
+                        }
+                        goto nextItem;
+                    } else if (c == '+') {
+                        tok = TokAppend;
+                        goto do2Op;
+                    } else if (c == '-') {
+                        tok = TokRemove;
+                        goto do2Op;
+                    } else if (c == '*') {
+                        tok = TokAppendUnique;
+                        goto do2Op;
+                    } else if (c == '~') {
+                        tok = TokReplace;
+                      do2Op:
+                        if (*cur == '=') {
+                            cur++;
+                            goto doOp;
+                        }
+                    } else if (c == '=') {
+                        tok = TokAssign;
+                      doOp:
+                        FLUSH_LHS_LITERAL();
+                        flushCond(tokPtr);
+                        putLineMarker(tokPtr);
+                        if (wordCount != 1) {
+                            parseError(fL1S("Assignment needs exactly one word on the left hand side."));
+                            pro->setOk(false);
+                            // Put empty variable name.
+                        } else {
+                            putBlock(tokPtr, buf, ptr - buf);
+                        }
+                        putTok(tokPtr, tok);
+                        context = CtxValue;
+                        ptr = ++tokPtr;
+                        goto nextToken;
+                    }
+                } else { // context == CtxValue
+                    if (c == '{') {
+                        ++parens;
+                    } else if (c == '}') {
+                        if (!parens) {
+                            FLUSH_RHS_LITERAL();
+                            FLUSH_VALUE_LIST();
+                            context = CtxTest;
+                            goto closeScope;
+                        }
+                        --parens;
+                    }
+                }
+                if (putSpace) {
+                    putSpace = false;
+                    *ptr++ = ' ';
+                }
+                *ptr++ = c;
+              nextChr:
+                if (cur == end)
+                    goto lineEnd;
+                c = *cur++;
+            }
+
+          lineEnd:
+            if (lineCont) {
+                if (quote) {
+                    putSpace = true;
+                } else {
+                    FLUSH_LITERAL();
+                    needSep = TokNewStr;
+                    ptr += (context == CtxTest) ? 4 : 2;
+                    xprPtr = ptr;
+                }
+            } else {
+                c = '\n';
+                cur = cptr;
+              flushLine:
+                FLUSH_LITERAL();
+                if (quote) {
+                    parseError(fL1S("Missing closing %1 quote").arg(QChar(quote)));
+                    if (!xprStack.isEmpty()) {
+                        context = xprStack.at(0).context;
+                        xprStack.clear();
+                    }
+                    goto flErr;
+                } else if (!xprStack.isEmpty()) {
+                    parseError(fL1S("Missing closing parenthesis in function call"));
+                    context = xprStack.at(0).context;
+                    xprStack.clear();
+                  flErr:
+                    pro->setOk(false);
+                    if (context == CtxValue) {
+                        tokPtr[-1] = 0; // sizehint
+                        putTok(tokPtr, TokValueTerminator);
+                    } else {
+                        bogusTest(tokPtr);
+                    }
+                } else if (context == CtxValue) {
+                    FLUSH_VALUE_LIST();
+                } else {
+                    finalizeCond(tokPtr, buf, ptr, wordCount);
+                }
+                if (!c)
+                    break;
+                ++m_lineNo;
+                goto freshLine;
+            }
+
+        if (!lineCont) {
+            cur = cptr;
+            ++m_lineNo;
+            goto freshLine;
+        }
+        lineMarked = false;
+      ignore:
+        cur = cptr;
+        ++m_lineNo;
+    }
+
+    flushScopes(tokPtr);
+    if (m_blockstack.size() > 1) {
+        parseError(fL1S("Missing closing brace(s)."));
+        pro->setOk(false);
+    }
+    while (m_blockstack.size())
+        leaveScope(tokPtr);
+    xprBuff.clear();
+    *pro->itemsRef() = QString(tokBuff.constData(), tokPtr - (ushort *)tokBuff.constData());
+    return true;
+
+#undef FLUSH_VALUE_LIST
+#undef FLUSH_LITERAL
+#undef FLUSH_LHS_LITERAL
+#undef FLUSH_RHS_LITERAL
+}
+
+void ProFileParser::putLineMarker(ushort *&tokPtr)
+{
+    if (m_markLine) {
+        *tokPtr++ = TokLine;
+        *tokPtr++ = (ushort)m_markLine;
+        m_markLine = 0;
+    }
+}
+
+void ProFileParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
+{
+    m_blockstack.resize(m_blockstack.size() + 1);
+    m_blockstack.top().special = special;
+    m_blockstack.top().start = tokPtr;
+    tokPtr += 2;
+    m_state = state;
+    m_canElse = false;
+    if (special)
+        m_markLine = m_lineNo;
+}
+
+void ProFileParser::leaveScope(ushort *&tokPtr)
+{
+    if (m_blockstack.top().inBranch) {
+        // Put empty else block
+        putBlockLen(tokPtr, 0);
+    }
+    if (ushort *start = m_blockstack.top().start) {
+        putTok(tokPtr, TokTerminator);
+        uint len = tokPtr - start - 2;
+        start[0] = (ushort)len;
+        start[1] = (ushort)(len >> 16);
+    }
+    m_blockstack.resize(m_blockstack.size() - 1);
+}
+
+// If we are on a fresh line, close all open one-line scopes.
+void ProFileParser::flushScopes(ushort *&tokPtr)
+{
+    if (m_state == StNew) {
+        while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1)
+            leaveScope(tokPtr);
+        if (m_blockstack.top().inBranch) {
+            m_blockstack.top().inBranch = false;
+            // Put empty else block
+            putBlockLen(tokPtr, 0);
+        }
+        m_canElse = false;
+    }
+}
+
+// If there is a pending conditional, enter a new scope, otherwise flush scopes.
+void ProFileParser::flushCond(ushort *&tokPtr)
+{
+    if (m_state == StCond) {
+        putTok(tokPtr, TokBranch);
+        m_blockstack.top().inBranch = true;
+        enterScope(tokPtr, false, StNew);
+    } else {
+        flushScopes(tokPtr);
+    }
+}
+
+void ProFileParser::finalizeTest(ushort *&tokPtr)
+{
+    flushScopes(tokPtr);
+    putLineMarker(tokPtr);
+    if (m_operator != NoOperator) {
+        putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
+        m_operator = NoOperator;
+    }
+    if (m_invert) {
+        putTok(tokPtr, TokNot);
+        m_invert = false;
+    }
+    m_state = StCond;
+    m_canElse = true;
+}
+
+void ProFileParser::bogusTest(ushort *&tokPtr)
+{
+    flushScopes(tokPtr);
+    m_operator = NoOperator;
+    m_invert = false;
+    m_state = StCond;
+    m_canElse = true;
+    m_proFile->setOk(false);
+}
+
+void ProFileParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount)
+{
+    if (wordCount != 1) {
+        if (wordCount) {
+            parseError(fL1S("Extra characters after test expression."));
+            bogusTest(tokPtr);
+        }
+        return;
+    }
+
+    // Check for magic tokens
+    if (*uc == TokHashLiteral) {
+        uint nlen = uc[3];
+        ushort *uce = uc + 4 + nlen;
+        if (uce == ptr) {
+            m_tmp.setRawData((QChar *)uc + 4, nlen);
+            if (!m_tmp.compare(statics.strelse, Qt::CaseInsensitive)) {
+                if (m_invert || m_operator != NoOperator) {
+                    parseError(fL1S("Unexpected operator in front of else."));
+                    return;
+                }
+                BlockScope &top = m_blockstack.top();
+                if (m_canElse && (!top.special || top.braceLevel)) {
+                    // A list of tests (the last one likely with side effects),
+                    // but no assignment, scope, etc.
+                    putTok(tokPtr, TokBranch);
+                    // Put empty then block
+                    putBlockLen(tokPtr, 0);
+                    enterScope(tokPtr, false, StCtrl);
+                    return;
+                }
+                forever {
+                    BlockScope &top = m_blockstack.top();
+                    if (top.inBranch && (!top.special || top.braceLevel)) {
+                        top.inBranch = false;
+                        enterScope(tokPtr, false, StCtrl);
+                        return;
+                    }
+                    if (top.braceLevel || m_blockstack.size() == 1)
+                        break;
+                    leaveScope(tokPtr);
+                }
+                parseError(fL1S("Unexpected 'else'."));
+                return;
+            }
+        }
+    }
+
+    finalizeTest(tokPtr);
+    putBlock(tokPtr, uc, ptr - uc);
+    putTok(tokPtr, TokCondition);
+}
+
+void ProFileParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc)
+{
+    // Check for magic tokens
+    if (*uc == TokHashLiteral) {
+        uint nlen = uc[3];
+        ushort *uce = uc + 4 + nlen;
+        if (*uce == TokTestCall) {
+            uce++;
+            m_tmp.setRawData((QChar *)uc + 4, nlen);
+            const QString *defName;
+            ushort defType;
+            if (m_tmp == statics.strfor) {
+                flushCond(tokPtr);
+                putLineMarker(tokPtr);
+                if (m_invert || m_operator == OrOperator) {
+                    // '|' could actually work reasonably, but qmake does nonsense here.
+                    parseError(fL1S("Unexpected operator in front of for()."));
+                    return;
+                }
+                if (*uce == (TokLiteral|TokNewStr)) {
+                    nlen = uce[1];
+                    uc = uce + 2 + nlen;
+                    if (*uc == TokFuncTerminator) {
+                        // for(literal) (only "ever" would be legal if qmake was sane)
+                        putTok(tokPtr, TokForLoop);
+                        putHashStr(tokPtr, (ushort *)0, (uint)0);
+                        putBlockLen(tokPtr, 1 + 3 + nlen + 1);
+                        putTok(tokPtr, TokHashLiteral);
+                        putHashStr(tokPtr, uce + 2, nlen);
+                      didFor:
+                        putTok(tokPtr, TokValueTerminator);
+                        enterScope(tokPtr, true, StCtrl);
+                        return;
+                    } else if (*uc == TokArgSeparator && argc == 2) {
+                        // for(var, something)
+                        uc++;
+                        putTok(tokPtr, TokForLoop);
+                        putHashStr(tokPtr, uce + 2, nlen);
+                      doFor:
+                        nlen = ptr - uc;
+                        putBlockLen(tokPtr, nlen + 1);
+                        putBlock(tokPtr, uc, nlen);
+                        goto didFor;
+                    }
+                } else if (argc == 1) {
+                    // for(non-literal) (this wouldn't be here if qmake was sane)
+                    putTok(tokPtr, TokForLoop);
+                    putHashStr(tokPtr, (ushort *)0, (uint)0);
+                    uc = uce;
+                    goto doFor;
+                }
+                parseError(fL1S("Syntax is for(var, list), for(var, forever) or for(ever)."));
+                return;
+            } else if (m_tmp == statics.strdefineReplace) {
+                defName = &statics.strdefineReplace;
+                defType = TokReplaceDef;
+                goto deffunc;
+            } else if (m_tmp == statics.strdefineTest) {
+                defName = &statics.strdefineTest;
+                defType = TokTestDef;
+              deffunc:
+                flushScopes(tokPtr);
+                putLineMarker(tokPtr);
+                if (m_invert) {
+                    parseError(fL1S("Unexpected operator in front of function definition."));
+                    return;
+                }
+                if (*uce == (TokLiteral|TokNewStr)) {
+                    uint nlen = uce[1];
+                    if (uce[nlen + 2] == TokFuncTerminator) {
+                        if (m_operator != NoOperator) {
+                            putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
+                            m_operator = NoOperator;
+                        }
+                        putTok(tokPtr, defType);
+                        putHashStr(tokPtr, uce + 2, nlen);
+                        uc = uce + 2 + nlen + 1;
+                        enterScope(tokPtr, true, StCtrl);
+                        return;
+                    }
+                }
+                parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
+                return;
+            }
+        }
+    }
+
+    finalizeTest(tokPtr);
+    putBlock(tokPtr, uc, ptr - uc);
+}
+
+void ProFileParser::parseError(const QString &msg) const
+{
+    if (!m_inError && m_handler)
+        m_handler->parseError(m_proFile->fileName(), m_lineNo, msg);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/profileparser.h b/src/linguist/shared/profileparser.h
new file mode 100644 (file)
index 0000000..3f4593a
--- /dev/null
@@ -0,0 +1,186 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef PROFILEPARSER_H
+#define PROFILEPARSER_H
+
+#include "proparser_global.h"
+#include "proitems.h"
+#include <QtCore/QHash>
+#include <QtCore/QStack>
+#ifdef PROPARSER_THREAD_SAFE
+# include <QtCore/QMutex>
+# include <QtCore/QWaitCondition>
+#endif
+
+// Be fast even for debug builds
+#ifdef __GNUC__
+# define ALWAYS_INLINE inline __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define ALWAYS_INLINE __forceinline
+#else
+# define ALWAYS_INLINE inline
+#endif
+
+QT_BEGIN_NAMESPACE
+class PROPARSER_EXPORT ProFileParserHandler
+{
+public:
+    // Some error during parsing
+    virtual void parseError(const QString &filename, int lineNo, const QString &msg) = 0;
+};
+
+class ProFileCache;
+
+class PROPARSER_EXPORT ProFileParser
+{
+public:
+    // Call this from a concurrency-free context
+    static void initialize();
+
+    ProFileParser(ProFileCache *cache, ProFileParserHandler *handler);
+
+    // fileName is expected to be absolute and cleanPath()ed.
+    // If contents is non-null, it will be used instead of the file's actual content
+    ProFile *parsedProFile(const QString &fileName, bool cache = false,
+                           const QString *contents = 0);
+    ProFile *parsedProBlock(const QString &name, const QString &contents)
+        { return parsedProFile(name, false, &contents); }
+
+private:
+    struct BlockScope {
+        BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {}
+        BlockScope(const BlockScope &other) { *this = other; }
+        ushort *start; // Where this block started; store length here
+        int braceLevel; // Nesting of braces in scope
+        bool special; // Single-line conditionals inside loops, etc. cannot have else branches
+        bool inBranch; // The 'else' branch of the previous TokBranch is still open
+    };
+
+    enum ScopeState {
+        StNew,  // Fresh scope
+        StCtrl, // Control statement (for or else) met on current line
+        StCond  // Conditionals met on current line
+    };
+
+    enum Context { CtxTest, CtxValue, CtxArgs };
+    struct ParseCtx {
+        int parens; // Nesting of non-functional parentheses
+        int argc; // Number of arguments in current function call
+        int wordCount; // Number of words in current expression
+        Context context;
+        ushort quote; // Enclosing quote type
+        ushort terminator; // '}' if replace function call is braced, ':' if test function
+    };
+
+    bool read(ProFile *pro);
+    bool read(ProFile *pro, const QString &content);
+
+    ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
+    ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
+    ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len);
+    void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
+    void finalizeHashStr(ushort *buf, uint len);
+    void putLineMarker(ushort *&tokPtr);
+    void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
+    void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
+    void finalizeTest(ushort *&tokPtr);
+    void bogusTest(ushort *&tokPtr);
+    void enterScope(ushort *&tokPtr, bool special, ScopeState state);
+    void leaveScope(ushort *&tokPtr);
+    void flushCond(ushort *&tokPtr);
+    void flushScopes(ushort *&tokPtr);
+
+    void parseError(const QString &msg) const;
+
+    // Current location
+    ProFile *m_proFile;
+    int m_lineNo;
+
+    QStack<BlockScope> m_blockstack;
+    ScopeState m_state;
+    int m_markLine; // Put marker for this line
+    bool m_inError; // Current line had a parsing error; suppress followup error messages
+    bool m_canElse; // Conditionals met on previous line, but no scope was opened
+    bool m_invert; // Pending conditional is negated
+    enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed
+
+    QString m_tmp; // Temporary for efficient toQString
+
+    ProFileCache *m_cache;
+    ProFileParserHandler *m_handler;
+
+    // This doesn't help gcc 3.3 ...
+    template<typename T> friend class QTypeInfo;
+
+    friend class ProFileCache;
+};
+
+class PROPARSER_EXPORT ProFileCache
+{
+public:
+    ProFileCache() {}
+    ~ProFileCache();
+
+    void discardFile(const QString &fileName);
+    void discardFiles(const QString &prefix);
+
+private:
+    struct Entry {
+        ProFile *pro;
+#ifdef PROPARSER_THREAD_SAFE
+        struct Locker {
+            Locker() : waiters(0), done(false) {}
+            QWaitCondition cond;
+            int waiters;
+            bool done;
+        };
+        Locker *locker;
+#endif
+    };
+
+    QHash<QString, Entry> parsed_files;
+#ifdef PROPARSER_THREAD_SAFE
+    QMutex mutex;
+#endif
+
+    friend class ProFileParser;
+};
+
+#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
+Q_DECLARE_TYPEINFO(ProFileParser::BlockScope, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(ProFileParser::Context, Q_PRIMITIVE_TYPE);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // PROFILEPARSER_H
index 5c88686..0b65126 100644 (file)
-/****************************************************************************
+/**************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
 **
-** This file is part of the Qt Linguist of the Qt Toolkit.
 **
-** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
+**
 ** 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:
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
 ** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** 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.
-**
 ** Other Usage
+**
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
 **
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
 
 #include "proitems.h"
-#include "abstractproitemvisitor.h"
 
 #include <QtCore/QFileInfo>
+#include <QtCore/QSet>
 
 QT_BEGIN_NAMESPACE
 
-// --------------- ProItem ------------
-void ProItem::setComment(const QString &comment)
-{
-    m_comment = comment;
-}
-
-QString ProItem::comment() const
-{
-    return m_comment;
-}
-
-// --------------- ProBlock ----------------
-
-ProBlock::ProBlock(ProBlock *parent)
-{
-    m_blockKind = 0;
-    m_parent = parent;
-    m_refCount = 1;
-}
-
-ProBlock::~ProBlock()
-{
-    foreach (ProItem *itm, m_proitems)
-        if (itm->kind() == BlockKind)
-            static_cast<ProBlock *>(itm)->deref();
-        else
-            delete itm;
-}
-
-void ProBlock::appendItem(ProItem *proitem)
-{
-    m_proitems << proitem;
-}
-
-void ProBlock::setItems(const QList<ProItem *> &proitems)
-{
-    m_proitems = proitems;
-}
-
-QList<ProItem *> ProBlock::items() const
-{
-    return m_proitems;
-}
-
-void ProBlock::setBlockKind(int blockKind)
-{
-    m_blockKind = blockKind;
-}
+using namespace ProStringConstants;
 
-int ProBlock::blockKind() const
+// from qhash.cpp
+uint ProString::hash(const QChar *p, int n)
 {
-    return m_blockKind;
-}
+    uint h = 0;
 
-void ProBlock::setParent(ProBlock *parent)
-{
-    m_parent = parent;
+    while (n--) {
+        h = (h << 4) + (*p++).unicode();
+        h ^= (h & 0xf0000000) >> 23;
+        h &= 0x0fffffff;
+    }
+    return h;
 }
 
-ProBlock *ProBlock::parent() const
+ProString::ProString() :
+    m_offset(0), m_length(0), m_file(0), m_hash(0x80000000)
 {
-    return m_parent;
 }
 
-ProItem::ProItemKind ProBlock::kind() const
+ProString::ProString(const ProString &other) :
+    m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash)
 {
-    return ProItem::BlockKind;
 }
 
-ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
+ProString::ProString(const ProString &other, OmitPreHashing) :
+    m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000)
 {
-    if (visitor->visitBeginProBlock(this) == ReturnSkip)
-        return ReturnTrue;
-    ProItemReturn rt = ReturnTrue;
-    for (int i = 0; i < m_proitems.count(); ++i) {
-        rt = m_proitems.at(i)->Accept(visitor);
-        if (rt != ReturnTrue && rt != ReturnFalse) {
-            if (rt == ReturnLoop) {
-                rt = ReturnTrue;
-                while (visitor->visitProLoopIteration())
-                    for (int j = i; ++j < m_proitems.count(); ) {
-                        rt = m_proitems.at(j)->Accept(visitor);
-                        if (rt != ReturnTrue && rt != ReturnFalse) {
-                            if (rt == ReturnNext) {
-                                rt = ReturnTrue;
-                                break;
-                            }
-                            if (rt == ReturnBreak)
-                                rt = ReturnTrue;
-                            goto do_break;
-                        }
-                    }
-              do_break:
-                visitor->visitProLoopCleanup();
-            }
-            break;
-        }
-    }
-    visitor->visitEndProBlock(this);
-    return rt;
 }
 
-// --------------- ProVariable ----------------
-ProVariable::ProVariable(const QString &name, ProBlock *parent)
-    : ProBlock(parent)
+ProString::ProString(const QString &str) :
+    m_string(str), m_offset(0), m_length(str.length()), m_file(0)
 {
-    setBlockKind(ProBlock::VariableKind);
-    m_variable = name;
-    m_variableKind = SetOperator;
+    updatedHash();
 }
 
-void ProVariable::setVariableOperator(VariableOperator variableKind)
+ProString::ProString(const QString &str, OmitPreHashing) :
+    m_string(str), m_offset(0), m_length(str.length()), m_file(0), m_hash(0x80000000)
 {
-    m_variableKind = variableKind;
 }
 
-ProVariable::VariableOperator ProVariable::variableOperator() const
+ProString::ProString(const char *str) :
+    m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0)
 {
-    return m_variableKind;
+    updatedHash();
 }
 
-void ProVariable::setVariable(const QString &name)
+ProString::ProString(const char *str, OmitPreHashing) :
+    m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000)
 {
-    m_variable = name;
 }
 
-QString ProVariable::variable() const
+ProString::ProString(const QString &str, int offset, int length) :
+    m_string(str), m_offset(offset), m_length(length), m_file(0)
 {
-    return m_variable;
+    updatedHash();
 }
 
-ProItem::ProItemReturn ProVariable::Accept(AbstractProItemVisitor *visitor)
+ProString::ProString(const QString &str, int offset, int length, uint hash) :
+    m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash)
 {
-    visitor->visitBeginProVariable(this);
-    foreach (ProItem *item, m_proitems)
-        item->Accept(visitor); // cannot fail
-    visitor->visitEndProVariable(this);
-    return ReturnTrue;
 }
 
-// --------------- ProValue ----------------
-ProValue::ProValue(const QString &value, ProVariable *variable)
+ProString::ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing) :
+    m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000)
 {
-    m_variable = variable;
-    m_value = value;
 }
 
-void ProValue::setValue(const QString &value)
+void ProString::setValue(const QString &str)
 {
-    m_value = value;
+    m_string = str, m_offset = 0, m_length = str.length();
+    updatedHash();
 }
 
-QString ProValue::value() const
+void ProString::setValue(const QString &str, OmitPreHashing)
 {
-    return m_value;
+    m_string = str, m_offset = 0, m_length = str.length(), m_hash = 0x80000000;
 }
 
-void ProValue::setVariable(ProVariable *variable)
+uint ProString::updatedHash() const
 {
-    m_variable = variable;
+     return (m_hash = hash(m_string.constData() + m_offset, m_length));
 }
 
-ProVariable *ProValue::variable() const
+uint qHash(const ProString &str)
 {
-    return m_variable;
+    if (!(str.m_hash & 0x80000000))
+        return str.m_hash;
+    return str.updatedHash();
 }
 
-ProItem::ProItemKind ProValue::kind() const
+QString ProString::toQString() const
 {
-    return ProItem::ValueKind;
+    return m_string.mid(m_offset, m_length);
 }
 
-ProItem::ProItemReturn ProValue::Accept(AbstractProItemVisitor *visitor)
+QString &ProString::toQString(QString &tmp) const
 {
-    visitor->visitProValue(this);
-    return ReturnTrue;
+    return tmp.setRawData(m_string.constData() + m_offset, m_length);
 }
 
-// --------------- ProFunction ----------------
-ProFunction::ProFunction(const QString &text)
+bool ProString::operator==(const ProString &other) const
 {
-    m_text = text;
+    if (m_length != other.m_length)
+        return false;
+    return !memcmp(m_string.constData() + m_offset,
+                   other.m_string.constData() + other.m_offset, m_length * 2);
 }
 
-void ProFunction::setText(const QString &text)
+bool ProString::operator==(const QString &other) const
 {
-    m_text = text;
+    if (m_length != other.length())
+        return false;
+    return !memcmp(m_string.constData() + m_offset, other.constData(), m_length * 2);
 }
 
-QString ProFunction::text() const
+bool ProString::operator==(const QLatin1String &other) const
 {
-    return m_text;
-}
+    const ushort *uc = (ushort *)m_string.constData() + m_offset;
+    const ushort *e = uc + m_length;
+    const uchar *c = (uchar *)other.latin1();
 
-ProItem::ProItemKind ProFunction::kind() const
-{
-    return ProItem::FunctionKind;
-}
+    if (!c)
+        return isEmpty();
 
-ProItem::ProItemReturn ProFunction::Accept(AbstractProItemVisitor *visitor)
-{
-    return visitor->visitProFunction(this);
+    while (*c) {
+        if (uc == e || *uc != *c)
+            return false;
+        ++uc;
+        ++c;
+    }
+    return (uc == e);
+}
+
+QChar *ProString::prepareAppend(int extraLen)
+{
+    if (m_string.isDetached() && m_length + extraLen <= m_string.capacity()) {
+        m_string.reserve(0); // Prevent the resize() below from reallocating
+        QChar *ptr = (QChar *)m_string.constData();
+        if (m_offset)
+            memmove(ptr, ptr + m_offset, m_length * 2);
+        ptr += m_length;
+        m_offset = 0;
+        m_length += extraLen;
+        m_string.resize(m_length);
+        m_hash = 0x80000000;
+        return ptr;
+    } else {
+        QString neu(m_length + extraLen, Qt::Uninitialized);
+        QChar *ptr = (QChar *)neu.constData();
+        memcpy(ptr, m_string.constData() + m_offset, m_length * 2);
+        ptr += m_length;
+        *this = ProString(neu, NoHash);
+        return ptr;
+    }
 }
 
-// --------------- ProCondition ----------------
-ProCondition::ProCondition(const QString &text)
+// If pending != 0, prefix with space if appending to non-empty non-pending
+ProString &ProString::append(const ProString &other, bool *pending)
 {
-    m_text = text;
+    if (other.m_length) {
+        if (!m_length) {
+            *this = other;
+        } else {
+            QChar *ptr;
+            if (pending && !*pending) {
+                ptr = prepareAppend(1 + other.m_length);
+                *ptr++ = 32;
+            } else {
+                ptr = prepareAppend(other.m_length);
+            }
+            memcpy(ptr, other.m_string.constData() + other.m_offset, other.m_length * 2);
+            if (other.m_file)
+                m_file = other.m_file;
+        }
+        if (pending)
+            *pending = true;
+    }
+    return *this;
 }
 
-void ProCondition::setText(const QString &text)
+ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st)
 {
-    m_text = text;
+    if (const int sz = other.size()) {
+        int startIdx = 0;
+        if (pending && !*pending && skipEmpty1st && other.at(0).isEmpty()) {
+            if (sz == 1)
+                return *this;
+            startIdx = 1;
+        }
+        if (!m_length && sz == startIdx + 1) {
+            *this = other.at(startIdx);
+        } else {
+            int totalLength = sz - startIdx;
+            for (int i = startIdx; i < sz; ++i)
+                totalLength += other.at(i).size();
+            bool putSpace = false;
+            if (pending && !*pending && m_length)
+                putSpace = true;
+            else
+                totalLength--;
+
+            QChar *ptr = prepareAppend(totalLength);
+            for (int i = startIdx; i < sz; ++i) {
+                if (putSpace)
+                    *ptr++ = 32;
+                else
+                    putSpace = true;
+                const ProString &str = other.at(i);
+                memcpy(ptr, str.m_string.constData() + str.m_offset, str.m_length * 2);
+                ptr += str.m_length;
+            }
+            if (other.last().m_file)
+                m_file = other.last().m_file;
+        }
+        if (pending)
+            *pending = true;
+    }
+    return *this;
 }
 
-QString ProCondition::text() const
+QString operator+(const ProString &one, const ProString &two)
 {
-    return m_text;
+    if (two.m_length) {
+        if (!one.m_length) {
+            return two.toQString();
+        } else {
+            QString neu(one.m_length + two.m_length, Qt::Uninitialized);
+            ushort *ptr = (ushort *)neu.constData();
+            memcpy(ptr, one.m_string.constData() + one.m_offset, one.m_length * 2);
+            memcpy(ptr + one.m_length, two.m_string.constData() + two.m_offset, two.m_length * 2);
+            return neu;
+        }
+    }
+    return one.toQString();
 }
 
-ProItem::ProItemKind ProCondition::kind() const
-{
-    return ProItem::ConditionKind;
-}
 
-ProItem::ProItemReturn ProCondition::Accept(AbstractProItemVisitor *visitor)
+ProString ProString::mid(int off, int len) const
 {
-    visitor->visitProCondition(this);
-    return ReturnTrue;
+    ProString ret(*this, NoHash);
+    if (off > m_length)
+        off = m_length;
+    ret.m_offset += off;
+    ret.m_length -= off;
+    if (ret.m_length > len)
+        ret.m_length = len;
+    return ret;
 }
 
-// --------------- ProOperator ----------------
-ProOperator::ProOperator(OperatorKind operatorKind)
+ProString ProString::trimmed() const
 {
-    m_operatorKind = operatorKind;
+    ProString ret(*this, NoHash);
+    int cur = m_offset;
+    int end = cur + m_length;
+    const QChar *data = m_string.constData();
+    for (; cur < end; cur++)
+        if (!data[cur].isSpace()) {
+            // No underrun check - we know there is at least one non-whitespace
+            while (data[end - 1].isSpace())
+                end--;
+            break;
+        }
+    ret.m_offset = cur;
+    ret.m_length = end - cur;
+    return ret;
 }
 
-void ProOperator::setOperatorKind(OperatorKind operatorKind)
+QString ProStringList::join(const QString &sep) const
 {
-    m_operatorKind = operatorKind;
-}
+    int totalLength = 0;
+    const int sz = size();
 
-ProOperator::OperatorKind ProOperator::operatorKind() const
-{
-    return m_operatorKind;
-}
+    for (int i = 0; i < sz; ++i)
+        totalLength += at(i).size();
 
-ProItem::ProItemKind ProOperator::kind() const
-{
-    return ProItem::OperatorKind;
-}
+    if (sz)
+        totalLength += sep.size() * (sz - 1);
 
-ProItem::ProItemReturn ProOperator::Accept(AbstractProItemVisitor *visitor)
-{
-    visitor->visitProOperator(this);
-    return ReturnTrue;
+    QString res(totalLength, Qt::Uninitialized);
+    QChar *ptr = (QChar *)res.constData();
+    for (int i = 0; i < sz; ++i) {
+        if (i) {
+            memcpy(ptr, sep.constData(), sep.size() * 2);
+            ptr += sep.size();
+        }
+        memcpy(ptr, at(i).constData(), at(i).size() * 2);
+        ptr += at(i).size();
+    }
+    return res;
+}
+
+void ProStringList::removeDuplicates()
+{
+    int n = size();
+    int j = 0;
+    QSet<ProString> seen;
+    seen.reserve(n);
+    for (int i = 0; i < n; ++i) {
+        const ProString &s = at(i);
+        if (seen.contains(s))
+            continue;
+        seen.insert(s);
+        if (j != i)
+            (*this)[j] = s;
+        ++j;
+    }
+    if (n != j)
+        erase(begin() + j, end());
 }
 
-// --------------- ProFile ----------------
 ProFile::ProFile(const QString &fileName)
-    : ProBlock(0)
+    : m_refCount(1),
+      m_fileName(fileName),
+      m_ok(true)
 {
-    m_modified = false;
-    setBlockKind(ProBlock::ProFileKind);
-    m_fileName = fileName;
-
-    QFileInfo fi(fileName);
-    m_displayFileName = fi.fileName();
-    m_directoryName = fi.absolutePath();
+    if (!fileName.startsWith(QLatin1Char('(')))
+        m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory!
+                fileName.left(fileName.lastIndexOf(QLatin1Char('/')))).canonicalFilePath();
 }
 
 ProFile::~ProFile()
 {
 }
 
-QString ProFile::displayFileName() const
-{
-    return m_displayFileName;
-}
-
-QString ProFile::fileName() const
-{
-    return m_fileName;
-}
-
-QString ProFile::directoryName() const
-{
-    return m_directoryName;
-}
-
-void ProFile::setModified(bool modified)
-{
-    m_modified = modified;
-}
-
-bool ProFile::isModified() const
-{
-    return m_modified;
-}
-
-ProItem::ProItemReturn ProFile::Accept(AbstractProItemVisitor *visitor)
-{
-    ProItemReturn rt;
-    if ((rt = visitor->visitBeginProFile(this)) != ReturnTrue)
-        return rt;
-    ProBlock::Accept(visitor); // cannot fail
-    return visitor->visitEndProFile(this);
-}
-
 QT_END_NAMESPACE
index f4bf183..4967b27 100644 (file)
-/****************************************************************************
+/**************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
 **
-** This file is part of the Qt Linguist of the Qt Toolkit.
 **
-** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
+**
 ** 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:
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
 ** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** 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.
-**
 ** Other Usage
+**
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
 **
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
 
 #ifndef PROITEMS_H
 #define PROITEMS_H
 
+#include "proparser_global.h"
 #include <QtCore/QString>
-#include <QtCore/QList>
+#include <QtCore/QVector>
 
 QT_BEGIN_NAMESPACE
 
-struct AbstractProItemVisitor;
-
-class ProItem
-{
+#ifdef PROPARSER_THREAD_SAFE
+typedef QAtomicInt ProItemRefCount;
+#else
+class ProItemRefCount {
 public:
-    enum ProItemKind {
-        ValueKind,
-        FunctionKind,
-        ConditionKind,
-        OperatorKind,
-        BlockKind
-    };
-
-    enum ProItemReturn {
-        ReturnFalse,
-        ReturnTrue,
-        ReturnBreak,
-        ReturnNext,
-        ReturnLoop,
-        ReturnSkip,
-        ReturnReturn
-   };
-
-    ProItem() : m_lineNumber(0) {}
-    virtual ~ProItem() {}
-
-    virtual ProItemKind kind() const = 0;
-
-    void setComment(const QString &comment);
-    QString comment() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor) = 0;
-    int lineNumber() const { return m_lineNumber; }
-    void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
-
+    ProItemRefCount(int cnt = 0) : m_cnt(cnt) {}
+    bool ref() { return ++m_cnt != 0; }
+    bool deref() { return --m_cnt != 0; }
+    ProItemRefCount &operator=(int value) { m_cnt = value; return *this; }
 private:
-    QString m_comment;
-    int m_lineNumber;
+    int m_cnt;
 };
+#endif
 
-class ProBlock : public ProItem
-{
-public:
-    enum ProBlockKind {
-        NormalKind          = 0x00,
-        ScopeKind           = 0x01,
-        ScopeContentsKind   = 0x02,
-        VariableKind        = 0x04,
-        ProFileKind         = 0x08,
-        FunctionBodyKind    = 0x10,
-        SingleLine          = 0x80
-    };
-
-    ProBlock(ProBlock *parent);
-    ~ProBlock();
-
-    void appendItem(ProItem *proitem);
-    void setItems(const QList<ProItem *> &proitems);
-    QList<ProItem *> items() const;
-
-    void setBlockKind(int blockKind);
-    int blockKind() const;
-
-    void setParent(ProBlock *parent);
-    ProBlock *parent() const;
-
-    void ref() { ++m_refCount; }
-    void deref() { if (!--m_refCount) delete this; }
-
-    ProItem::ProItemKind kind() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
-protected:
-    QList<ProItem *> m_proitems;
-private:
-    ProBlock *m_parent;
-    int m_blockKind;
-    int m_refCount;
-};
-
-class ProVariable : public ProBlock
-{
-public:
-    enum VariableOperator {
-        AddOperator         = 0,
-        RemoveOperator      = 1,
-        ReplaceOperator     = 2,
-        SetOperator         = 3,
-        UniqueAddOperator   = 4
-    };
-
-    ProVariable(const QString &name, ProBlock *parent);
+namespace ProStringConstants {
+enum OmitPreHashing { NoHash };
+}
 
-    void setVariableOperator(VariableOperator variableKind);
-    VariableOperator variableOperator() const;
+class ProStringList;
+class ProFile;
 
-    void setVariable(const QString &name);
-    QString variable() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
-private:
-    VariableOperator m_variableKind;
-    QString m_variable;
-};
-
-class ProValue : public ProItem
-{
+class ProString {
 public:
-    ProValue(const QString &value, ProVariable *variable);
-
-    void setValue(const QString &value);
-    QString value() const;
-
-    void setVariable(ProVariable *variable);
-    ProVariable *variable() const;
+    ProString();
+    ProString(const ProString &other);
+    ProString(const ProString &other, ProStringConstants::OmitPreHashing);
+    explicit ProString(const QString &str);
+    ProString(const QString &str, ProStringConstants::OmitPreHashing);
+    explicit ProString(const char *str);
+    ProString(const char *str, ProStringConstants::OmitPreHashing);
+    ProString(const QString &str, int offset, int length);
+    ProString(const QString &str, int offset, int length, uint hash);
+    ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing);
+    void setValue(const QString &str);
+    void setValue(const QString &str, ProStringConstants::OmitPreHashing);
+    ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; }
+    ProString &setSource(const ProFile *pro) { m_file = pro; return *this; }
+    const ProFile *sourceFile() const { return m_file; }
+    QString toQString() const;
+    QString &toQString(QString &tmp) const;
+    ProString &operator+=(const ProString &other);
+    ProString &append(const ProString &other, bool *pending = 0);
+    ProString &append(const ProStringList &other, bool *pending = 0, bool skipEmpty1st = false);
+    bool operator==(const ProString &other) const;
+    bool operator==(const QString &other) const;
+    bool operator==(const QLatin1String &other) const;
+    bool operator!=(const ProString &other) const { return !(*this == other); }
+    bool operator!=(const QString &other) const { return !(*this == other); }
+    bool operator!=(const QLatin1String &other) const { return !(*this == other); }
+    bool isEmpty() const { return !m_length; }
+    int size() const { return m_length; }
+    const QChar *constData() const { return m_string.constData() + m_offset; }
+    ProString mid(int off, int len = -1) const;
+    ProString left(int len) const { return mid(0, len); }
+    ProString right(int len) const { return mid(qMax(0, size() - len)); }
+    ProString trimmed() const;
+    void clear() { m_string.clear(); m_length = 0; }
+
+    static uint hash(const QChar *p, int n);
 
-    ProItem::ProItemKind kind() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
 private:
-    QString m_value;
-    ProVariable *m_variable;
+    QString m_string;
+    int m_offset, m_length;
+    const ProFile *m_file;
+    mutable uint m_hash;
+    QChar *prepareAppend(int extraLen);
+    uint updatedHash() const;
+    friend uint qHash(const ProString &str);
+    friend QString operator+(const ProString &one, const ProString &two);
 };
+Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE);
 
-class ProFunction : public ProItem
-{
-public:
-    explicit ProFunction(const QString &text);
-
-    void setText(const QString &text);
-    QString text() const;
+uint qHash(const ProString &str);
+QString operator+(const ProString &one, const ProString &two);
+inline QString operator+(const ProString &one, const QString &two)
+    { return one + ProString(two, ProStringConstants::NoHash); }
+inline QString operator+(const QString &one, const ProString &two)
+    { return ProString(one, ProStringConstants::NoHash) + two; }
 
-    ProItem::ProItemKind kind() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
-private:
-    QString m_text;
-};
-
-class ProCondition : public ProItem
-{
+class ProStringList : public QVector<ProString> {
 public:
-    explicit ProCondition(const QString &text);
-
-    void setText(const QString &text);
-    QString text() const;
-
-    ProItem::ProItemKind kind() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
-private:
-    QString m_text;
+    ProStringList() {}
+    ProStringList(const ProString &str) { *this << str; }
+    QString join(const QString &sep) const;
+    void removeDuplicates();
 };
 
-class ProOperator : public ProItem
-{
-public:
-    enum OperatorKind {
-        OrOperator      = 1,
-        NotOperator     = 2
-    };
-
-    explicit ProOperator(OperatorKind operatorKind);
-
-    void setOperatorKind(OperatorKind operatorKind);
-    OperatorKind operatorKind() const;
-
-    ProItem::ProItemKind kind() const;
-
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
-private:
-    OperatorKind m_operatorKind;
+// These token definitions affect both ProFileEvaluator and ProWriter
+enum ProToken {
+    TokTerminator = 0,  // end of stream (possibly not included in length; must be zero)
+    TokLine,            // line marker:
+                        // - line (1)
+    TokAssign,          // variable =
+    TokAppend,          // variable +=
+    TokAppendUnique,    // variable *=
+    TokRemove,          // variable -=
+    TokReplace,         // variable ~=
+                        // previous literal/expansion is a variable manipulation
+                        // - value expression + TokValueTerminator
+    TokValueTerminator, // assignment value terminator
+    TokLiteral,         // literal string (fully dequoted)
+                        // - length (1)
+                        // - string data (length; unterminated)
+    TokHashLiteral,     // literal string with hash (fully dequoted)
+                        // - hash (2)
+                        // - length (1)
+                        // - string data (length; unterminated)
+    TokVariable,        // qmake variable expansion
+                        // - hash (2)
+                        // - name length (1)
+                        // - name (name length; unterminated)
+    TokProperty,        // qmake property expansion
+                        // - name length (1)
+                        // - name (name length; unterminated)
+    TokEnvVar,          // environment variable expansion
+                        // - name length (1)
+                        // - name (name length; unterminated)
+    TokFuncName,        // replace function expansion
+                        // - hash (2)
+                        // - name length (1)
+                        // - name (name length; unterminated)
+                        // - ((nested expansion + TokArgSeparator)* + nested expansion)?
+                        // - TokFuncTerminator
+    TokArgSeparator,    // function argument separator
+    TokFuncTerminator,  // function argument list terminator
+    TokCondition,       // previous literal/expansion is a conditional
+    TokTestCall,        // previous literal/expansion is a test function call
+                        // - ((nested expansion + TokArgSeparator)* + nested expansion)?
+                        // - TokFuncTerminator
+    TokNot,             // '!' operator
+    TokAnd,             // ':' operator
+    TokOr,              // '|' operator
+    TokBranch,          // branch point:
+                        // - then block length (2)
+                        // - then block + TokTerminator (then block length)
+                        // - else block length (2)
+                        // - else block + TokTerminator (else block length)
+    TokForLoop,         // for loop:
+                        // - variable name: hash (2), length (1), chars (length)
+                        // - expression: length (2), bytes + TokValueTerminator (length)
+                        // - body length (2)
+                        // - body + TokTerminator (body length)
+    TokTestDef,         // test function definition:
+    TokReplaceDef,      // replace function definition:
+                        // - function name: hash (2), length (1), chars (length)
+                        // - body length (2)
+                        // - body + TokTerminator (body length)
+    TokMask = 0xff,
+    TokQuoted = 0x100,  // The expression is quoted => join expanded stringlist
+    TokNewStr = 0x200   // Next stringlist element
 };
 
-class ProFile : public ProBlock
+class PROPARSER_EXPORT ProFile
 {
 public:
     explicit ProFile(const QString &fileName);
     ~ProFile();
 
-    QString displayFileName() const;
-    QString fileName() const;
-    QString directoryName() const;
+    QString fileName() const { return m_fileName; }
+    QString directoryName() const { return m_directoryName; }
+    const QString &items() const { return m_proitems; }
+    QString *itemsRef() { return &m_proitems; }
+    const ushort *tokPtr() const { return (const ushort *)m_proitems.constData(); }
 
-    void setModified(bool modified);
-    bool isModified() const;
+    void ref() { m_refCount.ref(); }
+    void deref() { if (!m_refCount.deref()) delete this; }
 
-    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+    bool isOk() const { return m_ok; }
+    void setOk(bool ok) { m_ok = ok; }
 
 private:
+    ProItemRefCount m_refCount;
+    QString m_proitems;
     QString m_fileName;
-    QString m_displayFileName;
     QString m_directoryName;
-    bool m_modified;
+    bool m_ok;
 };
 
 QT_END_NAMESPACE
index 372247e..829c8cd 100644 (file)
@@ -1,12 +1,17 @@
 
 INCLUDEPATH *= $$PWD
 
+DEFINES += PROEVALUATOR_CUMULATIVE PROEVALUATOR_INIT_PROPS
+
 HEADERS += \
-    $$PWD/abstractproitemvisitor.h \
+    $$PWD/proparser_global.h \
+    $$PWD/ioutils.h \
     $$PWD/proitems.h \
-    $$PWD/profileevaluator.h \
-    $$PWD/proparserutils.h 
+    $$PWD/profileparser.h \
+    $$PWD/profileevaluator.h
 
 SOURCES += \
+    $$PWD/ioutils.cpp \
     $$PWD/proitems.cpp \
-    $$PWD/profileevaluator.cpp 
+    $$PWD/profileparser.cpp \
+    $$PWD/profileevaluator.cpp
diff --git a/src/linguist/shared/proparser_global.h b/src/linguist/shared/proparser_global.h
new file mode 100644 (file)
index 0000000..dd3114b
--- /dev/null
@@ -0,0 +1,48 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef PROPARSER_GLOBAL_H
+#define PROPARSER_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(PROPARSER_AS_LIBRARY)
+#  if defined(PROPARSER_LIBRARY)
+#    define PROPARSER_EXPORT Q_DECL_EXPORT
+#  else
+#    define PROPARSER_EXPORT Q_DECL_IMPORT
+#  endif
+#else
+#  define PROPARSER_EXPORT
+#endif
+
+#endif
diff --git a/src/linguist/shared/proparserutils.h b/src/linguist/shared/proparserutils.h
deleted file mode 100644 (file)
index d567a73..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the Qt Linguist of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** 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.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PROPARSERUTILS_H
-#define PROPARSERUTILS_H
-
-#include <QtCore/QDir>
-#ifndef QT_BOOTSTRAPPED
-#include <QtCore/QLibraryInfo>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#ifdef QT_BOOTSTRAPPED
-// this is a stripped down version of the one found in QtCore
-class QLibraryInfo
-{
-public:
-    enum LibraryLocation
-    {
-        PrefixPath,
-        DocumentationPath,
-        HeadersPath,
-        LibrariesPath,
-        BinariesPath,
-        PluginsPath,
-        DataPath,
-        TranslationsPath,
-        SettingsPath,
-        DemosPath,
-        ExamplesPath
-    };
-    static QString location(LibraryLocation);
-};
-#endif
-
-// Pre- and postcondition macros
-#define PRE(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
-#define POST(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
-
-// This struct is from qmake, but we are not using everything.
-struct Option
-{
-    //simply global convenience
-    //static QString libtool_ext;
-    //static QString pkgcfg_ext;
-    //static QString prf_ext;
-    //static QString prl_ext;
-    //static QString ui_ext;
-    //static QStringList h_ext;
-    //static QStringList cpp_ext;
-    //static QString h_moc_ext;
-    //static QString cpp_moc_ext;
-    //static QString obj_ext;
-    //static QString lex_ext;
-    //static QString yacc_ext;
-    //static QString h_moc_mod;
-    //static QString cpp_moc_mod;
-    //static QString lex_mod;
-    //static QString yacc_mod;
-    static QString dir_sep;
-    static QString dirlist_sep;
-    static QString qmakespec;
-    static QChar field_sep;
-
-    enum TARG_MODE { TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE, TARG_MAC9_MODE, TARG_QNX6_MODE };
-    static TARG_MODE target_mode;
-    //static QString pro_ext;
-    //static QString res_ext;
-
-    static void init()
-    {
-#ifdef Q_OS_WIN
-        Option::dirlist_sep = QLatin1Char(';');
-        Option::dir_sep = QLatin1Char('\\');
-#else
-        Option::dirlist_sep = QLatin1Char(':');
-        Option::dir_sep = QLatin1Char(QLatin1Char('/'));
-#endif
-        Option::qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data());
-        Option::field_sep = QLatin1Char(' ');
-    }
-
-    enum StringFixFlags {
-        FixNone                 = 0x00,
-        FixEnvVars              = 0x01,
-        FixPathCanonicalize     = 0x02,
-        FixPathToLocalSeparators  = 0x04,
-        FixPathToTargetSeparators = 0x08
-    };
-    static QString fixString(QString string, uchar flags);
-
-    inline static QString fixPathToLocalOS(const QString &in, bool fix_env = true, bool canonical = true)
-    {
-        uchar flags = FixPathToLocalSeparators;
-        if (fix_env)
-            flags |= FixEnvVars;
-        if (canonical)
-            flags |= FixPathCanonicalize;
-        return fixString(in, flags);
-    }
-};
-#if defined(Q_OS_WIN32)
-Option::TARG_MODE Option::target_mode = Option::TARG_WIN_MODE;
-#elif defined(Q_OS_MAC)
-Option::TARG_MODE Option::target_mode = Option::TARG_MACX_MODE;
-#elif defined(Q_OS_QNX6)
-Option::TARG_MODE Option::target_mode = Option::TARG_QNX6_MODE;
-#else
-Option::TARG_MODE Option::target_mode = Option::TARG_UNIX_MODE;
-#endif
-
-QString Option::qmakespec;
-QString Option::dirlist_sep;
-QString Option::dir_sep;
-QChar Option::field_sep;
-
-static void insertUnique(QHash<QString, QStringList> *map,
-    const QString &key, const QStringList &value)
-{
-    QStringList &sl = (*map)[key];
-    foreach (const QString &str, value)
-        if (!sl.contains(str))
-            sl.append(str);
-}
-
-static void removeEach(QHash<QString, QStringList> *map,
-    const QString &key, const QStringList &value)
-{
-    QStringList &sl = (*map)[key];
-    foreach (const QString &str, value)
-        sl.removeAll(str);
-}
-
-/*
-  See ProFileEvaluator::Private::visitProValue(...)
-
-static QStringList replaceInList(const QStringList &varList, const QRegExp &regexp,
-                           const QString &replace, bool global)
-{
-    QStringList resultList = varList;
-
-    for (QStringList::Iterator varit = resultList.begin(); varit != resultList.end();) {
-        if (varit->contains(regexp)) {
-            *varit = varit->replace(regexp, replace);
-            if (varit->isEmpty())
-                varit = resultList.erase(varit);
-            else
-                ++varit;
-            if (!global)
-                break;
-        } else {
-            ++varit;
-        }
-    }
-    return resultList;
-}
-*/
-
-inline QString fixEnvVariables(const QString &x)
-{
-    return Option::fixString(x, Option::FixEnvVars);
-}
-
-inline QStringList splitPathList(const QString &paths)
-{
-    return paths.split(Option::dirlist_sep);
-}
-
-static QStringList split_arg_list(QString params)
-{
-    int quote = 0;
-    QStringList args;
-
-    const ushort LPAREN = '(';
-    const ushort RPAREN = ')';
-    const ushort SINGLEQUOTE = '\'';
-    const ushort DOUBLEQUOTE = '"';
-    const ushort COMMA = ',';
-    const ushort SPACE = ' ';
-    //const ushort TAB = '\t';
-
-    ushort unicode;
-    const QChar *params_data = params.data();
-    const int params_len = params.length();
-    int last = 0;
-    while (last < params_len && ((params_data+last)->unicode() == SPACE
-                                /*|| (params_data+last)->unicode() == TAB*/))
-        ++last;
-    for (int x = last, parens = 0; x <= params_len; x++) {
-        unicode = (params_data+x)->unicode();
-        if (x == params_len) {
-            while (x && (params_data+(x-1))->unicode() == SPACE)
-                --x;
-            QString mid(params_data+last, x-last);
-            if (quote) {
-                if (mid[0] == quote && mid[(int)mid.length()-1] == quote)
-                    mid = mid.mid(1, mid.length()-2);
-                quote = 0;
-            }
-            args << mid;
-            break;
-        }
-        if (unicode == LPAREN) {
-            --parens;
-        } else if (unicode == RPAREN) {
-            ++parens;
-        } else if (quote && unicode == quote) {
-            quote = 0;
-        } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
-            quote = unicode;
-        }
-        if (!parens && !quote && unicode == COMMA) {
-            QString mid = params.mid(last, x - last).trimmed();
-            args << mid;
-            last = x+1;
-            while (last < params_len && ((params_data+last)->unicode() == SPACE
-                                        /*|| (params_data+last)->unicode() == TAB*/))
-                ++last;
-        }
-    }
-    return args;
-}
-
-static QStringList split_value_list(const QString &vals, bool do_semicolon=false)
-{
-    QString build;
-    QStringList ret;
-    QStack<char> quote;
-
-    const ushort LPAREN = '(';
-    const ushort RPAREN = ')';
-    const ushort SINGLEQUOTE = '\'';
-    const ushort DOUBLEQUOTE = '"';
-    const ushort BACKSLASH = '\\';
-    const ushort SEMICOLON = ';';
-
-    ushort unicode;
-    const QChar *vals_data = vals.data();
-    const int vals_len = vals.length();
-    for (int x = 0, parens = 0; x < vals_len; x++) {
-        unicode = vals_data[x].unicode();
-        if (x != (int)vals_len-1 && unicode == BACKSLASH &&
-            (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
-            build += vals_data[x++]; //get that 'escape'
-        } else if (!quote.isEmpty() && unicode == quote.top()) {
-            quote.pop();
-        } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
-            quote.push(unicode);
-        } else if (unicode == RPAREN) {
-            --parens;
-        } else if (unicode == LPAREN) {
-            ++parens;
-        }
-
-        if (!parens && quote.isEmpty() && ((do_semicolon && unicode == SEMICOLON) ||
-                                           vals_data[x] == Option::field_sep)) {
-            ret << build;
-            build.clear();
-        } else {
-            build += vals_data[x];
-        }
-    }
-    if (!build.isEmpty())
-        ret << build;
-    return ret;
-}
-
-static QStringList qmake_mkspec_paths()
-{
-    QStringList ret;
-    const QString concat = QDir::separator() + QLatin1String("mkspecs");
-    QByteArray qmakepath = qgetenv("QMAKEPATH");
-    if (!qmakepath.isEmpty()) {
-        const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
-        for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it)
-            ret << ((*it) + concat);
-    }
-    ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat;
-
-    return ret;
-}
-
-QT_END_NAMESPACE
-
-#endif // PROPARSERUTILS_H