Ignore:
Timestamp:
Feb 11, 2010, 11:19:06 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/tools/linguist/shared/profileevaluator.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4545
    4646#include <QtCore/QByteArray>
     47#include <QtCore/QDateTime>
    4748#include <QtCore/QDebug>
    4849#include <QtCore/QDir>
     
    5758#include <QtCore/QTextStream>
    5859
     60#ifdef Q_OS_UNIX
     61#include <unistd.h>
     62#include <sys/utsname.h>
     63#else
     64#include <Windows.h>
     65#endif
     66#include <stdio.h>
     67#include <stdlib.h>
     68
    5969#ifdef Q_OS_WIN32
    6070#define QT_POPEN _popen
     71#define QT_PCLOSE _pclose
    6172#else
    6273#define QT_POPEN popen
     74#define QT_PCLOSE pclose
    6375#endif
    6476
    6577QT_BEGIN_NAMESPACE
     78
     79///////////////////////////////////////////////////////////////////////
     80//
     81// Option
     82//
     83///////////////////////////////////////////////////////////////////////
     84
     85QString
     86Option::fixString(QString string, uchar flags)
     87{
     88    // XXX Ripped out caching, so this will be slow. Should not matter for current uses.
     89
     90    //fix the environment variables
     91    if (flags & Option::FixEnvVars) {
     92        int rep;
     93        QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)"));
     94        reg_variableName.setMinimal(true);
     95        while ((rep = reg_variableName.indexIn(string)) != -1)
     96            string.replace(rep, reg_variableName.matchedLength(),
     97                           QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
     98    }
     99
     100    //canonicalize it (and treat as a path)
     101    if (flags & Option::FixPathCanonicalize) {
     102#if 0
     103        string = QFileInfo(string).canonicalFilePath();
     104#endif
     105        string = QDir::cleanPath(string);
     106    }
     107
     108    if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':'))
     109        string[0] = string[0].toLower();
     110
     111    //fix separators
     112    Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators)));
     113    if (flags & Option::FixPathToLocalSeparators) {
     114#if defined(Q_OS_WIN32)
     115        string = string.replace(QLatin1Char('/'), QLatin1Char('\\'));
     116#else
     117        string = string.replace(QLatin1Char('\\'), QLatin1Char('/'));
     118#endif
     119    } else if (flags & Option::FixPathToTargetSeparators) {
     120        string = string.replace(QLatin1Char('/'), Option::dir_sep)
     121                       .replace(QLatin1Char('\\'), Option::dir_sep);
     122    }
     123
     124    if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) ||
     125        (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\''))))
     126        string = string.mid(1, string.length() - 2);
     127
     128    return string;
     129}
    66130
    67131///////////////////////////////////////////////////////////////////////
     
    75139public:
    76140    Private(ProFileEvaluator *q_);
     141
     142    ProFileEvaluator *q;
     143    int m_lineNo;                                   // Error reporting
     144    bool m_verbose;
     145
     146    /////////////// Reading pro file
    77147
    78148    bool read(ProFile *pro);
     
    88158    void finalizeBlock();
    89159
    90     // implementation of AbstractProItemVisitor
    91     bool visitBeginProBlock(ProBlock *block);
    92     bool visitEndProBlock(ProBlock *block);
    93     bool visitBeginProVariable(ProVariable *variable);
    94     bool visitEndProVariable(ProVariable *variable);
    95     bool visitBeginProFile(ProFile *value);
    96     bool visitEndProFile(ProFile *value);
    97     bool visitProValue(ProValue *value);
    98     bool visitProFunction(ProFunction *function);
    99     bool visitProOperator(ProOperator *oper);
    100     bool visitProCondition(ProCondition *condition);
    101 
    102     QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
    103     QStringList values(const QString &variableName) const;
    104     QStringList values(const QString &variableName, const ProFile *pro) const;
    105     QString propertyValue(const QString &val) const;
    106 
    107     bool isActiveConfig(const QString &config, bool regex = false);
    108     QStringList expandPattern(const QString &pattern);
    109     void expandPatternHelper(const QString &relName, const QString &absName,
    110         QStringList &sources_out);
    111     QStringList expandVariableReferences(const QString &value);
    112     QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
    113     QString format(const char *format) const;
    114 
    115     QString currentFileName() const;
    116     QString getcwd() const;
    117     ProFile *currentProFile() const;
    118 
    119     bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result);
    120     bool evaluateFile(const QString &fileName, bool *result);
    121     bool evaluateFeatureFile(const QString &fileName, bool *result);
    122 
    123     QStringList qmakeFeaturePaths();
    124 
    125     ProFileEvaluator *q;
    126 
    127160    QStack<ProBlock *> m_blockstack;
    128161    ProBlock *m_block;
     
    133166    bool m_syntaxError;
    134167    bool m_contNextLine;
    135     bool m_condition;
    136     bool m_invertNext;
     168    bool m_inQuote;
     169    int m_parens;
     170
     171    /////////////// Evaluating pro file contents
     172
     173    // implementation of AbstractProItemVisitor
     174    ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
     175    void visitEndProBlock(ProBlock *block);
     176    ProItem::ProItemReturn visitProLoopIteration();
     177    void visitProLoopCleanup();
     178    void visitBeginProVariable(ProVariable *variable);
     179    void visitEndProVariable(ProVariable *variable);
     180    ProItem::ProItemReturn visitBeginProFile(ProFile *value);
     181    ProItem::ProItemReturn visitEndProFile(ProFile *value);
     182    void visitProValue(ProValue *value);
     183    ProItem::ProItemReturn visitProFunction(ProFunction *function);
     184    void visitProOperator(ProOperator *oper);
     185    void visitProCondition(ProCondition *condition);
     186
     187    QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
     188    QStringList values(const QString &variableName) const;
     189    QStringList values(const QString &variableName, const ProFile *pro) const;
     190    QStringList values(const QString &variableName, const QHash<QString, QStringList> &place,
     191                       const ProFile *pro) const;
     192    QString propertyValue(const QString &val) const;
     193
     194    bool isActiveConfig(const QString &config, bool regex = false);
     195    QStringList expandVariableReferences(const QString &value);
     196    void doVariableReplace(QString *str);
     197    QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
     198    QString format(const char *format) const;
     199
     200    QString currentFileName() const;
     201    QString currentDirectory() const;
     202    ProFile *currentProFile() const;
     203
     204    ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
     205    bool evaluateFile(const QString &fileName);
     206    bool evaluateFeatureFile(const QString &fileName);
     207
     208    static inline ProItem::ProItemReturn returnBool(bool b)
     209        { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }
     210
     211    QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);
     212
     213    QStringList qmakeFeaturePaths();
     214
     215    struct State {
     216        bool condition;
     217        bool prevCondition;
     218    } m_sts;
     219    bool m_invertNext; // Short-lived, so not in State
     220    int m_skipLevel;
     221    bool m_cumulative;
     222    bool m_isFirstVariableValue;
    137223    QString m_lastVarName;
    138224    ProVariable::VariableOperator m_variableOperator;
    139     int m_lineNo;                                   // Error reporting
     225    QString m_origfile;
    140226    QString m_oldPath;                              // To restore the current path to the path
    141227    QStack<ProFile*> m_profileStack;                // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
     228    struct ProLoop {
     229        QString variable;
     230        QStringList oldVarVal;
     231        QStringList list;
     232        int index;
     233        bool infinite;
     234    };
     235    QStack<ProLoop> m_loopStack;
     236
     237    // we need the following two variables for handling
     238    // CONFIG = foo bar $$CONFIG
     239    QHash<QString, QStringList> m_tempValuemap;         // used while evaluating (variable operator value1 value2 ...)
     240    QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...)
    142241
    143242    QHash<QString, QStringList> m_valuemap;         // VariableName must be us-ascii, the content however can be non-us-ascii.
    144243    QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
    145244    QHash<QString, QString> m_properties;
    146     QString m_origfile;
     245    QString m_outputDir;
     246
     247    bool m_definingTest;
     248    QString m_definingFunc;
     249    QHash<QString, ProBlock *> m_testFunctions;
     250    QHash<QString, ProBlock *> m_replaceFunctions;
     251    QStringList m_returnValue;
     252    QStack<QHash<QString, QStringList> > m_valuemapStack;
     253    QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;
    147254
    148255    int m_prevLineNo;                               // Checking whether we're assigning the same TARGET
    149256    ProFile *m_prevProFile;                         // See m_prevLineNo
    150 
    151     bool m_verbose;
    152257};
     258
     259Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
     260Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
    153261
    154262ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
    155263  : q(q_)
    156264{
     265    // Global parser state
    157266    m_prevLineNo = 0;
    158267    m_prevProFile = 0;
     268
     269    // Configuration, more or less
    159270    m_verbose = true;
     271    m_cumulative = true;
     272
     273    // Evaluator state
     274    m_sts.condition = false;
     275    m_sts.prevCondition = false;
     276    m_invertNext = false;
     277    m_skipLevel = 0;
     278    m_isFirstVariableValue = true;
     279    m_definingFunc.clear();
    160280}
    161281
     
    168288    }
    169289
     290    // Parser state
    170291    m_block = 0;
    171292    m_commentItem = 0;
     293    m_inQuote = false;
     294    m_parens = 0;
    172295    m_contNextLine = false;
    173296    m_syntaxError = false;
     
    193316        return false;
    194317
    195     ushort quote = 0;
    196     int parens = 0;
    197     bool contNextLine = false;
     318    int parens = m_parens;
     319    bool inQuote = m_inQuote;
     320    bool escaped = false;
    198321    QString line = line0.simplified();
    199322
    200323    for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
    201324        ushort c = line.at(i).unicode();
    202         if (quote && c == quote)
    203             quote = 0;
    204         else if (c == '(')
    205             ++parens;
    206         else if (c == ')')
    207             --parens;
    208         else if (c == '"' && (i == 0 || line.at(i - 1).unicode() != '\\'))
    209             quote = c;
    210         else if (!parens && !quote) {
    211             if (c == '#') {
    212                 insertComment(line.mid(i + 1));
    213                 contNextLine = m_contNextLine;
    214                 break;
    215             }
    216             if (c == '\\' && i >= line.count() - 1) {
    217                 updateItem();
    218                 contNextLine = true;
     325        if (c == '#') { // Yep - no escaping possible
     326            insertComment(line.mid(i + 1));
     327            escaped = m_contNextLine;
     328            break;
     329        }
     330        if (!escaped) {
     331            if (c == '\\') {
     332                escaped = true;
     333                m_proitem += c;
    219334                continue;
    220             }
    221             if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
    222                 if (c == ' ')
    223                     updateItem();
    224                 else
    225                     m_proitem += c;
     335            } else if (c == '"') {
     336                inQuote = !inQuote;
     337                m_proitem += c;
    226338                continue;
    227339            }
    228             if (c == ':') {
    229                 enterScope(false);
    230                 continue;
    231             }
    232             if (c == '{') {
    233                 enterScope(true);
    234                 continue;
    235             }
    236             if (c == '}') {
    237                 leaveScope();
    238                 continue;
    239             }
    240             if (c == '=') {
    241                 insertVariable(line, &i);
    242                 continue;
    243             }
    244             if (c == '|' || c == '!') {
    245                 insertOperator(c);
    246                 continue;
     340        } else {
     341            escaped = false;
     342        }
     343        if (!inQuote) {
     344            if (c == '(') {
     345                ++parens;
     346            } else if (c == ')') {
     347                --parens;
     348            } else if (!parens) {
     349                if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
     350                    if (c == ' ')
     351                        updateItem();
     352                    else
     353                        m_proitem += c;
     354                    continue;
     355                }
     356                if (c == ':') {
     357                    enterScope(false);
     358                    continue;
     359                }
     360                if (c == '{') {
     361                    enterScope(true);
     362                    continue;
     363                }
     364                if (c == '}') {
     365                    leaveScope();
     366                    continue;
     367                }
     368                if (c == '=') {
     369                    insertVariable(line, &i);
     370                    continue;
     371                }
     372                if (c == '|' || c == '!') {
     373                    insertOperator(c);
     374                    continue;
     375                }
    247376            }
    248377        }
     
    250379        m_proitem += c;
    251380    }
    252     m_contNextLine = contNextLine;
    253 
    254     if (!m_syntaxError) {
     381    m_inQuote = inQuote;
     382    m_parens = parens;
     383    m_contNextLine = escaped;
     384    if (escaped) {
     385        m_proitem.chop(1);
    255386        updateItem();
    256         if (!m_contNextLine)
     387        return true;
     388    } else {
     389        if (!m_syntaxError) {
     390            updateItem();
    257391            finalizeBlock();
    258     }
    259     return !m_syntaxError;
     392            return true;
     393        }
     394        return false;
     395    }
    260396}
    261397
     
    276412    ProVariable::VariableOperator opkind;
    277413
     414    if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker
     415        return;
     416
    278417    switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
    279418        case '+':
     
    450589
    451590
    452 bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
    453 {
    454     if (block->blockKind() == ProBlock::ScopeKind) {
    455         m_invertNext = false;
    456         m_condition = false;
    457     }
    458     return true;
    459 }
    460 
    461 bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
    462 {
    463     Q_UNUSED(block);
    464     return true;
    465 }
    466 
    467 bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
     591ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
     592{
     593    if (block->blockKind() & ProBlock::ScopeContentsKind) {
     594        if (!m_definingFunc.isEmpty()) {
     595            if (!m_skipLevel || m_cumulative) {
     596                QHash<QString, ProBlock *> *hash =
     597                        (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
     598                if (ProBlock *def = hash->value(m_definingFunc))
     599                    def->deref();
     600                hash->insert(m_definingFunc, block);
     601                block->ref();
     602                block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
     603            }
     604            m_definingFunc.clear();
     605            return ProItem::ReturnSkip;
     606        } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
     607            if (!m_sts.condition)
     608                ++m_skipLevel;
     609            else
     610                Q_ASSERT(!m_skipLevel);
     611        }
     612    } else {
     613        if (!m_skipLevel) {
     614            if (m_sts.condition) {
     615                m_sts.prevCondition = true;
     616                m_sts.condition = false;
     617            }
     618        } else {
     619            Q_ASSERT(!m_sts.condition);
     620        }
     621    }
     622    return ProItem::ReturnTrue;
     623}
     624
     625void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
     626{
     627    if ((block->blockKind() & ProBlock::ScopeContentsKind)
     628        && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
     629        if (m_skipLevel) {
     630            Q_ASSERT(!m_sts.condition);
     631            --m_skipLevel;
     632        } else if (!(block->blockKind() & ProBlock::SingleLine)) {
     633            // Conditionals contained inside this block may have changed the state.
     634            // So we reset it here to make an else following us do the right thing.
     635            m_sts.condition = true;
     636        }
     637    }
     638}
     639
     640ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
     641{
     642    ProLoop &loop = m_loopStack.top();
     643
     644    if (loop.infinite) {
     645        if (!loop.variable.isEmpty())
     646            m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
     647        if (loop.index > 1000) {
     648            q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
     649            return ProItem::ReturnFalse;
     650        }
     651    } else {
     652        QString val;
     653        do {
     654            if (loop.index >= loop.list.count())
     655                return ProItem::ReturnFalse;
     656            val = loop.list.at(loop.index++);
     657        } while (val.isEmpty()); // stupid, but qmake is like that
     658        m_valuemap[loop.variable] = QStringList(val);
     659    }
     660    return ProItem::ReturnTrue;
     661}
     662
     663void ProFileEvaluator::Private::visitProLoopCleanup()
     664{
     665    ProLoop &loop = m_loopStack.top();
     666    m_valuemap[loop.variable] = loop.oldVarVal;
     667    m_loopStack.pop_back();
     668}
     669
     670void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
    468671{
    469672    m_lastVarName = variable->variable();
    470673    m_variableOperator = variable->variableOperator();
    471     return true;
    472 }
    473 
    474 bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
     674    m_isFirstVariableValue = true;
     675    m_tempValuemap = m_valuemap;
     676    m_tempFilevaluemap = m_filevaluemap;
     677}
     678
     679void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
    475680{
    476681    Q_UNUSED(variable);
     682    m_valuemap = m_tempValuemap;
     683    m_filevaluemap = m_tempFilevaluemap;
    477684    m_lastVarName.clear();
    478     return true;
    479 }
    480 
    481 bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
     685}
     686
     687void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
    482688{
    483689    m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
    484     return true;
    485 }
    486 
    487 bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
    488 {
    489     if (!m_condition) {
    490         if (m_invertNext)
    491             m_condition |= !isActiveConfig(cond->text(), true);
    492         else
    493             m_condition |= isActiveConfig(cond->text(), true);
    494     }
    495     return true;
    496 }
    497 
    498 bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
     690}
     691
     692void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
     693{
     694    if (!m_skipLevel) {
     695        if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
     696            m_sts.condition = !m_sts.prevCondition;
     697        } else {
     698            m_sts.prevCondition = false;
     699            if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
     700                m_sts.condition = true;
     701        }
     702    }
     703    m_invertNext = false;
     704}
     705
     706ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
    499707{
    500708    PRE(pro);
    501     bool ok = true;
    502709    m_lineNo = pro->lineNumber();
     710    if (m_origfile.isEmpty())
     711        m_origfile = pro->fileName();
    503712    if (m_oldPath.isEmpty()) {
    504713        // change the working directory for the initial profile we visit, since
    505714        // that is *the* profile. All the other times we reach this function will be due to
    506715        // include(file) or load(file)
     716
    507717        m_oldPath = QDir::currentPath();
     718
    508719        m_profileStack.push(pro);
    509         ok = QDir::setCurrent(pro->directoryName());
    510     }
    511 
    512     if (m_origfile.isEmpty())
    513         m_origfile = pro->fileName();
    514 
    515     return ok;
    516 }
    517 
    518 bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
     720
     721        const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
     722        if (!mkspecDirectory.isEmpty()) {
     723            bool cumulative = m_cumulative;
     724            m_cumulative = false;
     725            // This is what qmake does, everything set in the mkspec is also set
     726            // But this also creates a lot of problems
     727            evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"));
     728            evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"));
     729            m_cumulative = cumulative;
     730        }
     731
     732        return returnBool(QDir::setCurrent(pro->directoryName()));
     733    }
     734
     735    return ProItem::ReturnTrue;
     736}
     737
     738ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
    519739{
    520740    PRE(pro);
    521     bool ok = true;
    522741    m_lineNo = pro->lineNumber();
    523742    if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
     743        const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
     744        if (!mkspecDirectory.isEmpty()) {
     745            bool cumulative = m_cumulative;
     746            m_cumulative = false;
     747
     748            evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"));
     749
     750            QSet<QString> processed;
     751            forever {
     752                bool finished = true;
     753                QStringList configs = valuesDirect(QLatin1String("CONFIG"));
     754                for (int i = configs.size() - 1; i >= 0; --i) {
     755                    const QString config = configs[i].toLower();
     756                    if (!processed.contains(config)) {
     757                        processed.insert(config);
     758                        if (evaluateFile(mkspecDirectory + QLatin1String("/features/")
     759                                         + config + QLatin1String(".prf"))) {
     760                            finished = false;
     761                            break;
     762                        }
     763                    }
     764                }
     765                if (finished)
     766                    break;
     767            }
     768
     769            foreach (ProBlock *itm, m_replaceFunctions)
     770                itm->deref();
     771            m_replaceFunctions.clear();
     772            foreach (ProBlock *itm, m_testFunctions)
     773                itm->deref();
     774            m_testFunctions.clear();
     775
     776            m_cumulative = cumulative;
     777        }
     778
    524779        m_profileStack.pop();
    525         ok = QDir::setCurrent(m_oldPath);
    526     }
    527     return ok;
    528 }
    529 
    530 bool ProFileEvaluator::Private::visitProValue(ProValue *value)
     780        return returnBool(QDir::setCurrent(m_oldPath));
     781    }
     782
     783    return ProItem::ReturnTrue;
     784}
     785
     786static void replaceInList(QStringList *varlist,
     787        const QRegExp &regexp, const QString &replace, bool global)
     788{
     789    for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
     790        if ((*varit).contains(regexp)) {
     791            (*varit).replace(regexp, replace);
     792            if ((*varit).isEmpty())
     793                varit = varlist->erase(varit);
     794            else
     795                ++varit;
     796            if(!global)
     797                break;
     798        } else {
     799            ++varit;
     800        }
     801    }
     802}
     803
     804void ProFileEvaluator::Private::visitProValue(ProValue *value)
    531805{
    532806    PRE(value);
     
    545819            && m_lineNo == m_prevLineNo
    546820            && currentProFile() == m_prevProFile) {
    547         QStringList targets = m_valuemap.value(QLatin1String("TARGET"));
    548         m_valuemap.remove(QLatin1String("TARGET"));
     821        QStringList targets = m_tempValuemap.value(QLatin1String("TARGET"));
     822        m_tempValuemap.remove(QLatin1String("TARGET"));
    549823        QStringList lastTarget(targets.takeLast());
    550824        lastTarget << v.join(QLatin1String(" "));
     
    555829    m_prevProFile = currentProFile();
    556830
    557     // The following two blocks fix bug 180128 by making all "interesting"
    558     // file name absolute in each .pro file, not just the top most one
    559     if (varName == QLatin1String("SOURCES")
    560             || varName == QLatin1String("HEADERS")
    561             || varName == QLatin1String("INTERFACES")
    562             || varName == QLatin1String("FORMS")
    563             || varName == QLatin1String("FORMS3")
    564             || varName == QLatin1String("RESOURCES")) {
    565         // matches only existent files, expand certain(?) patterns
    566         QStringList vv;
    567         for (int i = v.count(); --i >= 0; )
    568             vv << expandPattern(v[i]);
    569         v = vv;
    570     }
    571 
    572     if (varName == QLatin1String("TRANSLATIONS")) {
    573         // also matches non-existent files, but does not expand pattern
    574         QString dir = QFileInfo(currentFileName()).absolutePath();
    575         dir += QLatin1Char('/');
    576         for (int i = v.count(); --i >= 0; )
    577             v[i] = QFileInfo(dir, v[i]).absoluteFilePath();
    578     }
    579 
    580831    switch (m_variableOperator) {
    581         case ProVariable::UniqueAddOperator:    // *
    582             insertUnique(&m_valuemap, varName, v, true);
    583             insertUnique(&m_filevaluemap[currentProFile()], varName, v, true);
    584             break;
    585832        case ProVariable::SetOperator:          // =
    586         case ProVariable::AddOperator:          // +
    587             insertUnique(&m_valuemap, varName, v, false);
    588             insertUnique(&m_filevaluemap[currentProFile()], varName, v, false);
    589             break;
    590         case ProVariable::RemoveOperator:       // -
    591             // fix me: interaction between AddOperator and RemoveOperator
    592             insertUnique(&m_valuemap, varName.prepend(QLatin1Char('-')), v, false);
    593             insertUnique(&m_filevaluemap[currentProFile()],
    594                          varName.prepend(QLatin1Char('-')), v, false);
    595             break;
    596         case ProVariable::ReplaceOperator:      // ~
     833            if (!m_cumulative) {
     834                if (!m_skipLevel) {
     835                    if (m_isFirstVariableValue) {
     836                        m_tempValuemap[varName] = v;
     837                        m_tempFilevaluemap[currentProFile()][varName] = v;
     838                    } else { // handle lines "CONFIG = foo bar"
     839                        m_tempValuemap[varName] += v;
     840                        m_tempFilevaluemap[currentProFile()][varName] += v;
     841                    }
     842                }
     843            } else {
     844                // We are greedy for values.
     845                m_tempValuemap[varName] += v;
     846                m_tempFilevaluemap[currentProFile()][varName] += v;
     847            }
     848            break;
     849        case ProVariable::UniqueAddOperator:    // *=
     850            if (!m_skipLevel || m_cumulative) {
     851                insertUnique(&m_tempValuemap, varName, v);
     852                insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v);
     853            }
     854            break;
     855        case ProVariable::AddOperator:          // +=
     856            if (!m_skipLevel || m_cumulative) {
     857                m_tempValuemap[varName] += v;
     858                m_tempFilevaluemap[currentProFile()][varName] += v;
     859            }
     860            break;
     861        case ProVariable::RemoveOperator:       // -=
     862            if (!m_cumulative) {
     863                if (!m_skipLevel) {
     864                    removeEach(&m_tempValuemap, varName, v);
     865                    removeEach(&m_tempFilevaluemap[currentProFile()], varName, v);
     866                }
     867            } else {
     868                // We are stingy with our values, too.
     869            }
     870            break;
     871        case ProVariable::ReplaceOperator:      // ~=
    597872            {
    598873                // DEFINES ~= s/a/b/?[gqi]
    599874
    600 /*              Create a superset by executing replacement + adding items that have changed
    601                 to original list. We're not sure if this is really the right approach, so for
    602                 the time being we will just do nothing ...
    603 
     875                doVariableReplace(&val);
     876                if (val.length() < 4 || val[0] != QLatin1Char('s')) {
     877                    q->logMessage(format("the ~= operator can handle only the s/// function."));
     878                    break;
     879                }
    604880                QChar sep = val.at(1);
    605881                QStringList func = val.split(sep);
    606882                if (func.count() < 3 || func.count() > 4) {
    607                     q->logMessage(format("'~= operator '(function s///) expects 3 or 4 arguments."));
    608                     return false;
    609                 }
    610                 if (func[0] != QLatin1String("s")) {
    611                     q->logMessage(format("~= operator can only handle s/// function."));
    612                     return false;
     883                    q->logMessage(format("the s/// function expects 3 or 4 arguments."));
     884                    break;
    613885                }
    614886
     
    626898                QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
    627899
    628                 QStringList replaceList = replaceInList(m_valuemap.value(varName), regexp, replace,
    629                                                         global);
    630                 // Add changed entries to list
    631                 foreach (const QString &entry, replaceList)
    632                     if (!m_valuemap.value(varName).contains(entry))
    633                         insertUnique(&m_valuemap, varName, QStringList() << entry, false);
    634 
    635                 replaceList = replaceInList(m_filevaluemap[currentProFile()].value(varName), regexp,
    636                                       replace, global);
    637                 foreach (const QString &entry, replaceList)
    638                     if (!m_filevaluemap[currentProFile()].value(varName).contains(entry))
    639                         insertUnique(&m_filevaluemap[currentProFile()], varName,
    640                                      QStringList() << entry, false); */
    641             }
    642             break;
    643 
    644     }
    645     return true;
    646 }
    647 
    648 bool ProFileEvaluator::Private::visitProFunction(ProFunction *func)
    649 {
    650     m_lineNo = func->lineNumber();
    651     bool result = true;
    652     bool ok = true;
    653     QString text = func->text();
    654     int lparen = text.indexOf(QLatin1Char('('));
    655     int rparen = text.lastIndexOf(QLatin1Char(')'));
    656     Q_ASSERT(lparen < rparen);
    657 
    658     QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
    659     QString funcName = text.left(lparen);
    660     ok &= evaluateConditionalFunction(funcName.trimmed(), arguments, &result);
    661     return ok;
     900                if (!m_skipLevel || m_cumulative) {
     901                    // We could make a union of modified and unmodified values,
     902                    // but this will break just as much as it fixes, so leave it as is.
     903                    replaceInList(&m_tempValuemap[varName], regexp, replace, global);
     904                    replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global);
     905                }
     906            }
     907            break;
     908
     909    }
     910    m_isFirstVariableValue = false;
     911}
     912
     913ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
     914{
     915    // Make sure that called subblocks don't inherit & destroy the state
     916    bool invertThis = m_invertNext;
     917    m_invertNext = false;
     918    if (!m_skipLevel)
     919        m_sts.prevCondition = false;
     920    if (m_cumulative || !m_sts.condition) {
     921        QString text = func->text();
     922        int lparen = text.indexOf(QLatin1Char('('));
     923        int rparen = text.lastIndexOf(QLatin1Char(')'));
     924        Q_ASSERT(lparen < rparen);
     925        QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
     926        QString funcName = text.left(lparen);
     927        m_lineNo = func->lineNumber();
     928        ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
     929        if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
     930            return result;
     931        if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
     932            m_sts.condition = true;
     933    }
     934    return ProItem::ReturnTrue;
    662935}
    663936
     
    667940    QStringList concat;
    668941    {
    669         const QString base_concat = QDir::separator() + QString(QLatin1String("features"));
     942        const QString base_concat = QDir::separator() + QLatin1String("features");
    670943        concat << base_concat + QDir::separator() + QLatin1String("mac");
    671944        concat << base_concat + QDir::separator() + QLatin1String("macx");
     
    676949        concat << base_concat;
    677950    }
    678     const QString mkspecs_concat = QDir::separator() + QString(QLatin1String("mkspecs"));
     951    const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs");
    679952    QStringList feature_roots;
    680953    QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
     
    7791052}
    7801053
    781 QString ProFileEvaluator::Private::getcwd() const
     1054QString ProFileEvaluator::Private::currentDirectory() const
    7821055{
    7831056    ProFile *cur = m_profileStack.top();
    7841057    return cur->directoryName();
     1058}
     1059
     1060void ProFileEvaluator::Private::doVariableReplace(QString *str)
     1061{
     1062    *str = expandVariableReferences(*str).join(QString(Option::field_sep));
    7851063}
    7861064
     
    9991277}
    10001278
     1279QStringList ProFileEvaluator::Private::evaluateFunction(
     1280        ProBlock *funcPtr, const QStringList &argumentsList, bool *ok)
     1281{
     1282    bool oki;
     1283    QStringList ret;
     1284
     1285    if (m_valuemapStack.count() >= 100) {
     1286        q->errorMessage(format("ran into infinite recursion (depth > 100)."));
     1287        oki = false;
     1288    } else {
     1289        State sts = m_sts;
     1290        m_valuemapStack.push(m_valuemap);
     1291        m_filevaluemapStack.push(m_filevaluemap);
     1292
     1293        QStringList args;
     1294        for (int i = 0; i < argumentsList.count(); ++i) {
     1295            QStringList theArgs = expandVariableReferences(argumentsList[i]);
     1296            args += theArgs;
     1297            m_valuemap[QString::number(i+1)] = theArgs;
     1298        }
     1299        m_valuemap[QLatin1String("ARGS")] = args;
     1300        oki = (funcPtr->Accept(this) != ProItem::ReturnFalse); // True || Return
     1301        ret = m_returnValue;
     1302        m_returnValue.clear();
     1303
     1304        m_valuemap = m_valuemapStack.pop();
     1305        m_filevaluemap = m_filevaluemapStack.pop();
     1306        m_sts = sts;
     1307    }
     1308    if (ok)
     1309        *ok = oki;
     1310    if (oki)
     1311        return ret;
     1312    return QStringList();
     1313}
     1314
    10011315QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
    10021316{
    10031317    QStringList argumentsList = split_arg_list(arguments);
     1318
     1319    if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
     1320        return evaluateFunction(funcPtr, argumentsList, 0);
    10041321
    10051322    QStringList args;
     
    10131330                      E_REPLACE };
    10141331
    1015     static QHash<QString, int> *expands = 0;
    1016     if (!expands) {
    1017         expands = new QHash<QString, int>;
    1018         expands->insert(QLatin1String("member"), E_MEMBER);                //v (implemented)
    1019         expands->insert(QLatin1String("first"), E_FIRST);                  //v
    1020         expands->insert(QLatin1String("last"), E_LAST);                    //v
    1021         expands->insert(QLatin1String("cat"), E_CAT);
    1022         expands->insert(QLatin1String("fromfile"), E_FROMFILE);
    1023         expands->insert(QLatin1String("eval"), E_EVAL);
    1024         expands->insert(QLatin1String("list"), E_LIST);
    1025         expands->insert(QLatin1String("sprintf"), E_SPRINTF);
    1026         expands->insert(QLatin1String("join"), E_JOIN);                    //v
    1027         expands->insert(QLatin1String("split"), E_SPLIT);                  //v
    1028         expands->insert(QLatin1String("basename"), E_BASENAME);            //v
    1029         expands->insert(QLatin1String("dirname"), E_DIRNAME);              //v
    1030         expands->insert(QLatin1String("section"), E_SECTION);
    1031         expands->insert(QLatin1String("find"), E_FIND);
    1032         expands->insert(QLatin1String("system"), E_SYSTEM);                //v
    1033         expands->insert(QLatin1String("unique"), E_UNIQUE);
    1034         expands->insert(QLatin1String("quote"), E_QUOTE);                  //v
    1035         expands->insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
    1036         expands->insert(QLatin1String("upper"), E_UPPER);
    1037         expands->insert(QLatin1String("lower"), E_LOWER);
    1038         expands->insert(QLatin1String("re_escape"), E_RE_ESCAPE);
    1039         expands->insert(QLatin1String("files"), E_FILES);
    1040         expands->insert(QLatin1String("prompt"), E_PROMPT);
    1041         expands->insert(QLatin1String("replace"), E_REPLACE);
    1042     }
    1043     ExpandFunc func_t = ExpandFunc(expands->value(func.toLower()));
     1332    static QHash<QString, int> expands;
     1333    if (expands.isEmpty()) {
     1334        expands.insert(QLatin1String("member"), E_MEMBER);
     1335        expands.insert(QLatin1String("first"), E_FIRST);
     1336        expands.insert(QLatin1String("last"), E_LAST);
     1337        expands.insert(QLatin1String("cat"), E_CAT);
     1338        expands.insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
     1339        expands.insert(QLatin1String("eval"), E_EVAL);
     1340        expands.insert(QLatin1String("list"), E_LIST);
     1341        expands.insert(QLatin1String("sprintf"), E_SPRINTF);
     1342        expands.insert(QLatin1String("join"), E_JOIN);
     1343        expands.insert(QLatin1String("split"), E_SPLIT);
     1344        expands.insert(QLatin1String("basename"), E_BASENAME);
     1345        expands.insert(QLatin1String("dirname"), E_DIRNAME);
     1346        expands.insert(QLatin1String("section"), E_SECTION);
     1347        expands.insert(QLatin1String("find"), E_FIND);
     1348        expands.insert(QLatin1String("system"), E_SYSTEM);
     1349        expands.insert(QLatin1String("unique"), E_UNIQUE);
     1350        expands.insert(QLatin1String("quote"), E_QUOTE);
     1351        expands.insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
     1352        expands.insert(QLatin1String("upper"), E_UPPER);
     1353        expands.insert(QLatin1String("lower"), E_LOWER);
     1354        expands.insert(QLatin1String("re_escape"), E_RE_ESCAPE);
     1355        expands.insert(QLatin1String("files"), E_FILES);
     1356        expands.insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
     1357        expands.insert(QLatin1String("replace"), E_REPLACE);
     1358    }
     1359    ExpandFunc func_t = ExpandFunc(expands.value(func.toLower()));
    10441360
    10451361    QStringList ret;
     
    10871403            break;
    10881404        }
     1405        case E_SPRINTF:
     1406            if(args.count() < 1) {
     1407                q->logMessage(format("sprintf(format, ...) requires at least one argument"));
     1408            } else {
     1409                QString tmp = args.at(0);
     1410                for (int i = 1; i < args.count(); ++i)
     1411                    tmp = tmp.arg(args.at(i));
     1412                ret = split_value_list(tmp);
     1413            }
     1414            break;
    10891415        case E_JOIN: {
    10901416            if (args.count() < 1 || args.count() > 4) {
     
    11061432        case E_SPLIT: {
    11071433            if (args.count() != 2) {
    1108                 q->logMessage(format("split(var, sep) requires two arguments"));
     1434                q->logMessage(format("split(var, sep) requires one or two arguments"));
    11091435            } else {
    1110                 QString sep = args.at(1);
     1436                const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep);
    11111437                foreach (const QString &var, values(args.first()))
    11121438                    foreach (const QString &splt, var.split(sep))
     
    11791505            break;
    11801506        }
    1181         case E_SYSTEM: {
    1182             if (m_condition) {
     1507        case E_CAT:
     1508            if (args.count() < 1 || args.count() > 2) {
     1509                q->logMessage(format("cat(file, singleline=true) requires one or two arguments."));
     1510            } else {
     1511                QString file = args[0];
     1512                file = Option::fixPathToLocalOS(file);
     1513
     1514                bool singleLine = true;
     1515                if (args.count() > 1)
     1516                    singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
     1517
     1518                QFile qfile(file);
     1519                if (qfile.open(QIODevice::ReadOnly)) {
     1520                    QTextStream stream(&qfile);
     1521                    while (!stream.atEnd()) {
     1522                        ret += split_value_list(stream.readLine().trimmed());
     1523                        if (!singleLine)
     1524                            ret += QLatin1String("\n");
     1525                    }
     1526                    qfile.close();
     1527                }
     1528            }
     1529            break;
     1530#if 0 // Used only by Qt's configure for caching
     1531        case E_FROMFILE:
     1532            if (args.count() != 2) {
     1533                q->logMessage(format("fromfile(file, variable) requires two arguments."));
     1534            } else {
     1535                QString file = args[0], seek_variableName = args[1];
     1536
     1537                ProFile pro(Option::fixPathToLocalOS(file));
     1538
     1539                ProFileEvaluator visitor;
     1540                visitor.setVerbose(m_verbose);
     1541                visitor.setCumulative(m_cumulative);
     1542
     1543                if (!visitor.queryProFile(&pro))
     1544                    break;
     1545
     1546                if (!visitor.accept(&pro))
     1547                    break;
     1548
     1549                ret = visitor.values(seek_variableName);
     1550            }
     1551            break;
     1552#endif
     1553        case E_EVAL: {
     1554            if (args.count() != 1) {
     1555                q->logMessage(format("eval(variable) requires one argument"));
     1556
     1557            } else {
     1558                ret += values(args.at(0));
     1559            }
     1560            break; }
     1561        case E_LIST: {
     1562            static int x = 0;
     1563            QString tmp;
     1564            tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++);
     1565            ret = QStringList(tmp);
     1566            QStringList lst;
     1567            foreach (const QString &arg, args)
     1568                lst += split_value_list(arg);
     1569            m_valuemap[tmp] = lst;
     1570            break; }
     1571        case E_FIND:
     1572            if (args.count() != 2) {
     1573                q->logMessage(format("find(var, str) requires two arguments."));
     1574            } else {
     1575                QRegExp regx(args[1]);
     1576                foreach (const QString &val, values(args.first()))
     1577                    if (regx.indexIn(val) != -1)
     1578                        ret += val;
     1579            }
     1580            break;
     1581        case E_SYSTEM:
     1582            if (!m_skipLevel) {
    11831583                if (args.count() < 1 || args.count() > 2) {
    11841584                    q->logMessage(format("system(execute) requires one or two arguments."));
     
    11881588                    bool singleLine = true;
    11891589                    if (args.count() > 1)
    1190                         singleLine = (args[1].toLower() == QLatin1String("true"));
     1590                        singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
    11911591                    QString output;
    11921592                    while (proc && !feof(proc)) {
     
    12021602                    }
    12031603                    ret += split_value_list(output);
    1204                 }
    1205             }
    1206             break; }
     1604                    if (proc)
     1605                        QT_PCLOSE(proc);
     1606                }
     1607            }
     1608            break;
     1609        case E_UNIQUE:
     1610            if(args.count() != 1) {
     1611                q->logMessage(format("unique(var) requires one argument."));
     1612            } else {
     1613                foreach (const QString &var, values(args.first()))
     1614                    if (!ret.contains(var))
     1615                        ret.append(var);
     1616            }
     1617            break;
    12071618        case E_QUOTE:
    12081619            for (int i = 0; i < args.count(); ++i)
    12091620                ret += QStringList(args.at(i));
    12101621            break;
     1622        case E_ESCAPE_EXPAND:
     1623            for (int i = 0; i < args.size(); ++i) {
     1624                QChar *i_data = args[i].data();
     1625                int i_len = args[i].length();
     1626                for (int x = 0; x < i_len; ++x) {
     1627                    if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
     1628                        if (*(i_data+x+1) == QLatin1Char('\\')) {
     1629                            ++x;
     1630                        } else {
     1631                            struct {
     1632                                char in, out;
     1633                            } mapped_quotes[] = {
     1634                                { 'n', '\n' },
     1635                                { 't', '\t' },
     1636                                { 'r', '\r' },
     1637                                { 0, 0 }
     1638                            };
     1639                            for (int i = 0; mapped_quotes[i].in; ++i) {
     1640                                if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
     1641                                    *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
     1642                                    if (x < i_len-2)
     1643                                        memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
     1644                                    --i_len;
     1645                                    break;
     1646                                }
     1647                            }
     1648                        }
     1649                    }
     1650                }
     1651                ret.append(QString(i_data, i_len));
     1652            }
     1653            break;
     1654        case E_RE_ESCAPE:
     1655            for (int i = 0; i < args.size(); ++i)
     1656                ret += QRegExp::escape(args[i]);
     1657            break;
     1658        case E_UPPER:
     1659        case E_LOWER:
     1660            for (int i = 0; i < args.count(); ++i)
     1661                if (func_t == E_UPPER)
     1662                    ret += args[i].toUpper();
     1663                else
     1664                    ret += args[i].toLower();
     1665            break;
     1666        case E_FILES:
     1667            if (args.count() != 1 && args.count() != 2) {
     1668                q->logMessage(format("files(pattern, recursive=false) requires one or two arguments"));
     1669            } else {
     1670                bool recursive = false;
     1671                if (args.count() == 2)
     1672                    recursive = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive) || args[1].toInt());
     1673                QStringList dirs;
     1674                QString r = Option::fixPathToLocalOS(args[0]);
     1675                int slash = r.lastIndexOf(QDir::separator());
     1676                if (slash != -1) {
     1677                    dirs.append(r.left(slash));
     1678                    r = r.mid(slash+1);
     1679                } else {
     1680                    dirs.append(QString());
     1681                }
     1682
     1683                const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
     1684                for (int d = 0; d < dirs.count(); d++) {
     1685                    QString dir = dirs[d];
     1686                    if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep))
     1687                        dir += QLatin1Char('/');
     1688
     1689                    QDir qdir(dir);
     1690                    for (int i = 0; i < (int)qdir.count(); ++i) {
     1691                        if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String(".."))
     1692                            continue;
     1693                        QString fname = dir + qdir[i];
     1694                        if (QFileInfo(fname).isDir()) {
     1695                            if (recursive)
     1696                                dirs.append(fname);
     1697                        }
     1698                        if (regex.exactMatch(qdir[i]))
     1699                            ret += fname;
     1700                    }
     1701                }
     1702            }
     1703            break;
     1704        case E_REPLACE:
     1705            if(args.count() != 3 ) {
     1706                q->logMessage(format("replace(var, before, after) requires three arguments"));
     1707            } else {
     1708                const QRegExp before(args[1]);
     1709                const QString after(args[2]);
     1710                foreach (QString val, values(args.first()))
     1711                    ret += val.replace(before, after);
     1712            }
     1713            break;
    12111714        case 0:
    1212             q->logMessage(format("'%1' is not a function").arg(func));
     1715            q->logMessage(format("'%1' is not a recognized replace function").arg(func));
    12131716            break;
    12141717        default:
     
    12201723}
    12211724
    1222 bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &function,
    1223     const QString &arguments, bool *result)
     1725ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
     1726        const QString &function, const QString &arguments)
    12241727{
    12251728    QStringList argumentsList = split_arg_list(arguments);
     1729
     1730    if (ProBlock *funcPtr = m_testFunctions.value(function, 0)) {
     1731        bool ok;
     1732        QStringList ret = evaluateFunction(funcPtr, argumentsList, &ok);
     1733        if (ok) {
     1734            if (ret.isEmpty()) {
     1735                return ProItem::ReturnTrue;
     1736            } else {
     1737                if (ret.first() != QLatin1String("false")) {
     1738                    if (ret.first() == QLatin1String("true")) {
     1739                        return ProItem::ReturnTrue;
     1740                    } else {
     1741                        bool ok;
     1742                        int val = ret.first().toInt(&ok);
     1743                        if (ok) {
     1744                            if (val)
     1745                                return ProItem::ReturnTrue;
     1746                        } else {
     1747                            q->logMessage(format("Unexpected return value from test '%1': %2")
     1748                                          .arg(function).arg(ret.join(QLatin1String(" :: "))));
     1749                        }
     1750                    }
     1751                }
     1752            }
     1753        }
     1754        return ProItem::ReturnFalse;
     1755    }
     1756
    12261757    QString sep;
    12271758    sep.append(Option::field_sep);
    1228 
    12291759    QStringList args;
    12301760    for (int i = 0; i < argumentsList.count(); ++i)
    12311761        args += expandVariableReferences(argumentsList[i]).join(sep);
    12321762
    1233     enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE,
    1234         CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE};
    1235 
    1236     static QHash<QString, int> *functions = 0;
    1237     if (!functions) {
    1238         functions = new QHash<QString, int>;
    1239         functions->insert(QLatin1String("load"), CF_LOAD);         //v
    1240         functions->insert(QLatin1String("include"), CF_INCLUDE);   //v
    1241         functions->insert(QLatin1String("message"), CF_MESSAGE);   //v
    1242         functions->insert(QLatin1String("warning"), CF_MESSAGE);   //v
    1243         functions->insert(QLatin1String("error"), CF_MESSAGE);     //v
    1244     }
    1245 
    1246     bool cond = false;
    1247     bool ok = true;
    1248 
    1249     ConditionFunc func_t = (ConditionFunc)functions->value(function);
     1763    enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
     1764                    T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
     1765                    T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
     1766                    T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
     1767                    T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
     1768
     1769    static QHash<QString, int> functions;
     1770    if (functions.isEmpty()) {
     1771        functions.insert(QLatin1String("requires"), T_REQUIRES);
     1772        functions.insert(QLatin1String("greaterThan"), T_GREATERTHAN);
     1773        functions.insert(QLatin1String("lessThan"), T_LESSTHAN);
     1774        functions.insert(QLatin1String("equals"), T_EQUALS);
     1775        functions.insert(QLatin1String("isEqual"), T_EQUALS);
     1776        functions.insert(QLatin1String("exists"), T_EXISTS);
     1777        functions.insert(QLatin1String("export"), T_EXPORT);
     1778        functions.insert(QLatin1String("clear"), T_CLEAR);
     1779        functions.insert(QLatin1String("unset"), T_UNSET);
     1780        functions.insert(QLatin1String("eval"), T_EVAL);
     1781        functions.insert(QLatin1String("CONFIG"), T_CONFIG);
     1782        functions.insert(QLatin1String("if"), T_IF);
     1783        functions.insert(QLatin1String("isActiveConfig"), T_CONFIG);
     1784        functions.insert(QLatin1String("system"), T_SYSTEM);
     1785        functions.insert(QLatin1String("return"), T_RETURN);
     1786        functions.insert(QLatin1String("break"), T_BREAK);
     1787        functions.insert(QLatin1String("next"), T_NEXT);
     1788        functions.insert(QLatin1String("defined"), T_DEFINED);
     1789        functions.insert(QLatin1String("contains"), T_CONTAINS);
     1790        functions.insert(QLatin1String("infile"), T_INFILE);
     1791        functions.insert(QLatin1String("count"), T_COUNT);
     1792        functions.insert(QLatin1String("isEmpty"), T_ISEMPTY);
     1793        functions.insert(QLatin1String("load"), T_LOAD);         //v
     1794        functions.insert(QLatin1String("include"), T_INCLUDE);   //v
     1795        functions.insert(QLatin1String("debug"), T_DEBUG);
     1796        functions.insert(QLatin1String("message"), T_MESSAGE);   //v
     1797        functions.insert(QLatin1String("warning"), T_MESSAGE);   //v
     1798        functions.insert(QLatin1String("error"), T_MESSAGE);     //v
     1799        functions.insert(QLatin1String("for"), T_FOR);     //v
     1800        functions.insert(QLatin1String("defineTest"), T_DEFINE_TEST);        //v
     1801        functions.insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE);  //v
     1802    }
     1803
     1804    TestFunc func_t = (TestFunc)functions.value(function);
    12501805
    12511806    switch (func_t) {
    1252         case CF_CONFIG: {
     1807        case T_DEFINE_TEST:
     1808            m_definingTest = true;
     1809            goto defineFunc;
     1810        case T_DEFINE_REPLACE:
     1811            m_definingTest = false;
     1812          defineFunc:
     1813            if (args.count() != 1) {
     1814                q->logMessage(format("%s(function) requires one argument.").arg(function));
     1815                return ProItem::ReturnFalse;
     1816            }
     1817            m_definingFunc = args.first();
     1818            return ProItem::ReturnTrue;
     1819        case T_DEFINED:
     1820            if (args.count() < 1 || args.count() > 2) {
     1821                q->logMessage(format("defined(function, [\"test\"|\"replace\"])"
     1822                                     " requires one or two arguments."));
     1823                return ProItem::ReturnFalse;
     1824            }
     1825            if (args.count() > 1) {
     1826                if (args[1] == QLatin1String("test"))
     1827                    return returnBool(m_testFunctions.contains(args[0]));
     1828                else if (args[1] == QLatin1String("replace"))
     1829                    return returnBool(m_replaceFunctions.contains(args[0]));
     1830                q->logMessage(format("defined(function, type):"
     1831                                     " unexpected type [%1].\n").arg(args[1]));
     1832                return ProItem::ReturnFalse;
     1833            }
     1834            return returnBool(m_replaceFunctions.contains(args[0])
     1835                              || m_testFunctions.contains(args[0]));
     1836        case T_RETURN:
     1837            m_returnValue = args;
     1838            // It is "safe" to ignore returns - due to qmake brokeness
     1839            // they cannot be used to terminate loops anyway.
     1840            if (m_skipLevel || m_cumulative)
     1841                return ProItem::ReturnTrue;
     1842            if (m_valuemapStack.isEmpty()) {
     1843                q->logMessage(format("unexpected return()."));
     1844                return ProItem::ReturnFalse;
     1845            }
     1846            return ProItem::ReturnReturn;
     1847        case T_EXPORT:
     1848            if (m_skipLevel && !m_cumulative)
     1849                return ProItem::ReturnTrue;
     1850            if (args.count() != 1) {
     1851                q->logMessage(format("export(variable) requires one argument."));
     1852                return ProItem::ReturnFalse;
     1853            }
     1854            for (int i = 0; i < m_valuemapStack.size(); ++i) {
     1855                m_valuemapStack[i][args[0]] = m_valuemap[args[0]];
     1856                m_filevaluemapStack[i][currentProFile()][args[0]] =
     1857                        m_filevaluemap[currentProFile()][args[0]];
     1858            }
     1859            return ProItem::ReturnTrue;
     1860#if 0
     1861        case T_INFILE:
     1862        case T_REQUIRES:
     1863        case T_EVAL:
     1864#endif
     1865        case T_FOR: {
     1866            if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
     1867                return ProItem::ReturnTrue;
     1868            if (m_skipLevel)
     1869                return ProItem::ReturnFalse;
     1870            if (args.count() > 2 || args.count() < 1) {
     1871                q->logMessage(format("for({var, list|var, forever|ever})"
     1872                                     " requires one or two arguments."));
     1873                return ProItem::ReturnFalse;
     1874            }
     1875            ProLoop loop;
     1876            loop.infinite = false;
     1877            loop.index = 0;
     1878            QString it_list;
     1879            if (args.count() == 1) {
     1880                doVariableReplace(&args[0]);
     1881                it_list = args[0];
     1882                if (args[0] != QLatin1String("ever")) {
     1883                    q->logMessage(format("for({var, list|var, forever|ever})"
     1884                                         " requires one or two arguments."));
     1885                    return ProItem::ReturnFalse;
     1886                }
     1887                it_list = QLatin1String("forever");
     1888            } else {
     1889                loop.variable = args[0];
     1890                loop.oldVarVal = m_valuemap.value(loop.variable);
     1891                doVariableReplace(&args[1]);
     1892                it_list = args[1];
     1893            }
     1894            loop.list = m_valuemap[it_list];
     1895            if (loop.list.isEmpty()) {
     1896                if (it_list == QLatin1String("forever")) {
     1897                    loop.infinite = true;
     1898                } else {
     1899                    int dotdot = it_list.indexOf(QLatin1String(".."));
     1900                    if (dotdot != -1) {
     1901                        bool ok;
     1902                        int start = it_list.left(dotdot).toInt(&ok);
     1903                        if (ok) {
     1904                            int end = it_list.mid(dotdot+2).toInt(&ok);
     1905                            if (ok) {
     1906                                if (start < end) {
     1907                                    for (int i = start; i <= end; i++)
     1908                                        loop.list << QString::number(i);
     1909                                } else {
     1910                                    for (int i = start; i >= end; i--)
     1911                                        loop.list << QString::number(i);
     1912                                }
     1913                            }
     1914                        }
     1915                    }
     1916                }
     1917            }
     1918            m_loopStack.push(loop);
     1919            m_sts.condition = true;
     1920            return ProItem::ReturnLoop;
     1921        }
     1922        case T_BREAK:
     1923            if (m_skipLevel)
     1924                return ProItem::ReturnFalse;
     1925            if (!m_loopStack.isEmpty())
     1926                return ProItem::ReturnBreak;
     1927            // ### missing: breaking out of multiline blocks
     1928            q->logMessage(format("unexpected break()."));
     1929            return ProItem::ReturnFalse;
     1930        case T_NEXT:
     1931            if (m_skipLevel)
     1932                return ProItem::ReturnFalse;
     1933            if (!m_loopStack.isEmpty())
     1934                return ProItem::ReturnNext;
     1935            q->logMessage(format("unexpected next()."));
     1936            return ProItem::ReturnFalse;
     1937        case T_IF: {
     1938            if (args.count() != 1) {
     1939                q->logMessage(format("if(condition) requires one argument."));
     1940                return ProItem::ReturnFalse;
     1941            }
     1942            QString cond = args.first();
     1943            bool escaped = false; // This is more than qmake does
     1944            bool quoted = false;
     1945            bool ret = true;
     1946            bool orOp = false;
     1947            bool invert = false;
     1948            bool isFunc = false;
     1949            int parens = 0;
     1950            QString test;
     1951            test.reserve(20);
     1952            QString args;
     1953            args.reserve(50);
     1954            const QChar *d = cond.unicode();
     1955            const QChar *ed = d + cond.length();
     1956            while (d < ed) {
     1957                ushort c = (d++)->unicode();
     1958                if (!escaped) {
     1959                    if (c == '\\') {
     1960                        escaped = true;
     1961                        args += c; // Assume no-one quotes the test name
     1962                        continue;
     1963                    } else if (c == '"') {
     1964                        quoted = !quoted;
     1965                        args += c; // Ditto
     1966                        continue;
     1967                    }
     1968                } else {
     1969                    escaped = false;
     1970                }
     1971                if (quoted) {
     1972                    args += c; // Ditto
     1973                } else {
     1974                    bool isOp = false;
     1975                    if (c == '(') {
     1976                        isFunc = true;
     1977                        if (parens)
     1978                            args += c;
     1979                        ++parens;
     1980                    } else if (c == ')') {
     1981                        --parens;
     1982                        if (parens)
     1983                            args += c;
     1984                    } else if (!parens) {
     1985                        if (c == ':' || c == '|')
     1986                            isOp = true;
     1987                        else if (c == '!')
     1988                            invert = true;
     1989                        else
     1990                            test += c;
     1991                    } else {
     1992                        args += c;
     1993                    }
     1994                    if (!parens && (isOp || d == ed)) {
     1995                        // Yes, qmake doesn't shortcut evaluations here. We can't, either,
     1996                        // as some test functions have side effects.
     1997                        bool success;
     1998                        if (isFunc) {
     1999                            success = evaluateConditionalFunction(test, args);
     2000                        } else {
     2001                            success = isActiveConfig(test, true);
     2002                        }
     2003                        success ^= invert;
     2004                        if (orOp)
     2005                            ret |= success;
     2006                        else
     2007                            ret &= success;
     2008                        orOp = (c == '|');
     2009                        invert = false;
     2010                        isFunc = false;
     2011                        test.clear();
     2012                        args.clear();
     2013                    }
     2014                }
     2015            }
     2016            return returnBool(ret);
     2017        }
     2018        case T_CONFIG: {
    12532019            if (args.count() < 1 || args.count() > 2) {
    12542020                q->logMessage(format("CONFIG(config) requires one or two arguments."));
    1255                 ok = false;
    1256                 break;
     2021                return ProItem::ReturnFalse;
    12572022            }
    12582023            if (args.count() == 1) {
    12592024                //cond = isActiveConfig(args.first()); XXX
    1260                 break;
     2025                return ProItem::ReturnFalse;
    12612026            }
    12622027            const QStringList mutuals = args[1].split(QLatin1Char('|'));
    12632028            const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
    1264             for (int i = configs.size() - 1 && ok; i >= 0; i--) {
     2029            for (int i = configs.size() - 1; i >= 0; i--) {
    12652030                for (int mut = 0; mut < mutuals.count(); mut++) {
    12662031                    if (configs[i] == mutuals[mut].trimmed()) {
    1267                         cond = (configs[i] == args[0]);
    1268                         goto done_T_CONFIG;
     2032                        return returnBool(configs[i] == args[0]);
    12692033                    }
    12702034                }
    12712035            }
    1272           done_T_CONFIG:
    1273             break;
    1274         }
    1275         case CF_CONTAINS: {
     2036            return ProItem::ReturnFalse;
     2037        }
     2038        case T_CONTAINS: {
    12762039            if (args.count() < 2 || args.count() > 3) {
    12772040                q->logMessage(format("contains(var, val) requires two or three arguments."));
    1278                 ok = false;
    1279                 break;
     2041                return ProItem::ReturnFalse;
    12802042            }
    12812043
     
    12862048                    const QString val = l[i];
    12872049                    if (regx.exactMatch(val) || val == args[1]) {
    1288                         cond = true;
    1289                         break;
     2050                        return ProItem::ReturnTrue;
    12902051                    }
    12912052                }
     
    12962057                    for (int mut = 0; mut < mutuals.count(); mut++) {
    12972058                        if (val == mutuals[mut].trimmed()) {
    1298                             cond = (regx.exactMatch(val) || val == args[1]);
    1299                             goto done_T_CONTAINS;
     2059                            return returnBool(regx.exactMatch(val) || val == args[1]);
    13002060                        }
    13012061                    }
    13022062                }
    13032063            }
    1304           done_T_CONTAINS:
    1305             break;
    1306         }
    1307         case CF_COUNT: {
     2064            return ProItem::ReturnFalse;
     2065        }
     2066        case T_COUNT: {
    13082067            if (args.count() != 2 && args.count() != 3) {
    1309                 q->logMessage(format("count(var, count) requires two or three arguments."));
    1310                 ok = false;
    1311                 break;
     2068                q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
     2069                return ProItem::ReturnFalse;
    13122070            }
    13132071            if (args.count() == 3) {
    13142072                QString comp = args[2];
    13152073                if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
    1316                     cond = values(args.first()).count() > args[1].toInt();
     2074                    return returnBool(values(args.first()).count() > args[1].toInt());
    13172075                } else if (comp == QLatin1String(">=")) {
    1318                     cond = values(args.first()).count() >= args[1].toInt();
     2076                    return returnBool(values(args.first()).count() >= args[1].toInt());
    13192077                } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
    1320                     cond = values(args.first()).count() < args[1].toInt();
     2078                    return returnBool(values(args.first()).count() < args[1].toInt());
    13212079                } else if (comp == QLatin1String("<=")) {
    1322                     cond = values(args.first()).count() <= args[1].toInt();
    1323                 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual") || comp == QLatin1String("=") || comp == QLatin1String("==")) {
    1324                     cond = values(args.first()).count() == args[1].toInt();
     2080                    return returnBool(values(args.first()).count() <= args[1].toInt());
     2081                } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
     2082                           || comp == QLatin1String("=") || comp == QLatin1String("==")) {
     2083                    return returnBool(values(args.first()).count() == args[1].toInt());
    13252084                } else {
    1326                     ok = false;
    13272085                    q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
    1328                 }
    1329                 break;
    1330             }
    1331             cond = values(args.first()).count() == args[1].toInt();
    1332             break;
    1333         }
    1334         case CF_INCLUDE: {
     2086                    return ProItem::ReturnFalse;
     2087                }
     2088            }
     2089            return returnBool(values(args.first()).count() == args[1].toInt());
     2090        }
     2091        case T_GREATERTHAN:
     2092        case T_LESSTHAN: {
     2093            if (args.count() != 2) {
     2094                q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
     2095                return ProItem::ReturnFalse;
     2096            }
     2097            QString rhs(args[1]), lhs(values(args[0]).join(QString(Option::field_sep)));
     2098            bool ok;
     2099            int rhs_int = rhs.toInt(&ok);
     2100            if (ok) { // do integer compare
     2101                int lhs_int = lhs.toInt(&ok);
     2102                if (ok) {
     2103                    if (func_t == T_GREATERTHAN)
     2104                        return returnBool(lhs_int > rhs_int);
     2105                    return returnBool(lhs_int < rhs_int);
     2106                }
     2107            }
     2108            if (func_t == T_GREATERTHAN)
     2109                return returnBool(lhs > rhs);
     2110            return returnBool(lhs < rhs);
     2111        }
     2112        case T_EQUALS:
     2113            if (args.count() != 2) {
     2114                q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
     2115                return ProItem::ReturnFalse;
     2116            }
     2117            return returnBool(values(args[0]).join(QString(Option::field_sep)) == args[1]);
     2118        case T_CLEAR: {
     2119            if (m_skipLevel && !m_cumulative)
     2120                return ProItem::ReturnFalse;
     2121            if (args.count() != 1) {
     2122                q->logMessage(format("%1(variable) requires one argument.").arg(function));
     2123                return ProItem::ReturnFalse;
     2124            }
     2125            QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
     2126            if (it == m_valuemap.end())
     2127                return ProItem::ReturnFalse;
     2128            it->clear();
     2129            return ProItem::ReturnTrue;
     2130        }
     2131        case T_UNSET: {
     2132            if (m_skipLevel && !m_cumulative)
     2133                return ProItem::ReturnFalse;
     2134            if (args.count() != 1) {
     2135                q->logMessage(format("%1(variable) requires one argument.").arg(function));
     2136                return ProItem::ReturnFalse;
     2137            }
     2138            QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
     2139            if (it == m_valuemap.end())
     2140                return ProItem::ReturnFalse;
     2141            m_valuemap.erase(it);
     2142            return ProItem::ReturnTrue;
     2143        }
     2144        case T_INCLUDE: {
     2145            if (m_skipLevel && !m_cumulative)
     2146                return ProItem::ReturnFalse;
    13352147            QString parseInto;
    1336             if (args.count() == 2) {
     2148            // the third optional argument to include() controls warnings
     2149            //      and is not used here
     2150            if ((args.count() == 2) || (args.count() == 3)) {
    13372151                parseInto = args[1];
    13382152            } else if (args.count() != 1) {
    1339                 q->logMessage(format("include(file) requires one or two arguments."));
    1340                 ok = false;
    1341                 break;
     2153                q->logMessage(format("include(file) requires one, two or three arguments."));
     2154                return ProItem::ReturnFalse;
    13422155            }
    13432156            QString fileName = args.first();
    13442157            // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
    1345             QDir currentProPath(getcwd());
     2158            QDir currentProPath(currentDirectory());
    13462159            fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
    1347             ok = evaluateFile(fileName, &ok);
    1348             break;
    1349         }
    1350         case CF_LOAD: {
     2160            State sts = m_sts;
     2161            bool ok = evaluateFile(fileName);
     2162            m_sts = sts;
     2163            return returnBool(ok);
     2164        }
     2165        case T_LOAD: {
     2166            if (m_skipLevel && !m_cumulative)
     2167                return ProItem::ReturnFalse;
    13512168            QString parseInto;
    13522169            bool ignore_error = false;
    13532170            if (args.count() == 2) {
    13542171                QString sarg = args[1];
    1355                 ignore_error = (sarg.toLower() == QLatin1String("true") || sarg.toInt());
     2172                ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
    13562173            } else if (args.count() != 1) {
    13572174                q->logMessage(format("load(feature) requires one or two arguments."));
    1358                 ok = false;
    1359                 break;
    1360             }
    1361             ok = evaluateFeatureFile( args.first(), &cond);
    1362             break;
    1363         }
    1364         case CF_MESSAGE: {
     2175                return ProItem::ReturnFalse;
     2176            }
     2177            // XXX ignore_error unused
     2178            return returnBool(evaluateFeatureFile(args.first()));
     2179        }
     2180        case T_DEBUG:
     2181            // Yup - do nothing. Nothing is going to enable debug output anyway.
     2182            return ProItem::ReturnFalse;
     2183        case T_MESSAGE: {
    13652184            if (args.count() != 1) {
    13662185                q->logMessage(format("%1(message) requires one argument.").arg(function));
    1367                 ok = false;
    1368                 break;
    1369             }
    1370             QString msg = args.first();
    1371             if (function == QLatin1String("error")) {
    1372                 QStringList parents;
    1373                 foreach (ProFile *proFile, m_profileStack)
    1374                     parents.append(proFile->fileName());
    1375                 if (!parents.isEmpty())
    1376                     parents.takeLast();
    1377                 if (parents.isEmpty())
    1378                     q->fileMessage(format("Project ERROR: %1").arg(msg));
    1379                 else
    1380                     q->fileMessage(format("Project ERROR: %1. File was included from: '%2'")
    1381                         .arg(msg).arg(parents.join(QLatin1String("', '"))));
    1382             } else {
    1383                 q->fileMessage(format("Project MESSAGE: %1").arg(msg));
    1384             }
    1385             break;
    1386         }
    1387         case CF_SYSTEM: {
     2186                return ProItem::ReturnFalse;
     2187            }
     2188            QString msg = fixEnvVariables(args.first());
     2189            q->fileMessage(QString::fromLatin1("Project %1: %2").arg(function.toUpper(), msg));
     2190            // ### Consider real termination in non-cumulative mode
     2191            return returnBool(function != QLatin1String("error"));
     2192        }
     2193#if 0 // Way too dangerous to enable.
     2194        case T_SYSTEM: {
    13882195            if (args.count() != 1) {
    13892196                q->logMessage(format("system(exec) requires one argument."));
    1390                 ok = false;
    1391                 break;
    1392             }
    1393             ok = system(args.first().toLatin1().constData()) == 0;
    1394             break;
    1395         }
    1396         case CF_ISEMPTY: {
     2197                ProItem::ReturnFalse;
     2198            }
     2199            return returnBool(system(args.first().toLatin1().constData()) == 0);
     2200        }
     2201#endif
     2202        case T_ISEMPTY: {
    13972203            if (args.count() != 1) {
    13982204                q->logMessage(format("isEmpty(var) requires one argument."));
    1399                 ok = false;
    1400                 break;
     2205                return ProItem::ReturnFalse;
    14012206            }
    14022207            QStringList sl = values(args.first());
    14032208            if (sl.count() == 0) {
    1404                 cond = true;
     2209                return ProItem::ReturnTrue;
    14052210            } else if (sl.count() > 0) {
    14062211                QString var = sl.first();
    1407                 cond = (var.isEmpty());
    1408             }
    1409             break;
    1410         }
    1411         case CF_EXISTS: {
     2212                if (var.isEmpty())
     2213                    return ProItem::ReturnTrue;
     2214            }
     2215            return ProItem::ReturnFalse;
     2216        }
     2217        case T_EXISTS: {
    14122218            if (args.count() != 1) {
    14132219                q->logMessage(format("exists(file) requires one argument."));
    1414                 ok = false;
    1415                 break;
     2220                return ProItem::ReturnFalse;
    14162221            }
    14172222            QString file = args.first();
    1418 
    1419             file = QDir::cleanPath(file);
     2223            file = Option::fixPathToLocalOS(file);
    14202224
    14212225            if (QFile::exists(file)) {
    1422                 cond = true;
    1423                 break;
     2226                return ProItem::ReturnTrue;
    14242227            }
    14252228            //regular expression I guess
    1426             QString dirstr = getcwd();
     2229            QString dirstr = currentDirectory();
    14272230            int slsh = file.lastIndexOf(Option::dir_sep);
    14282231            if (slsh != -1) {
     
    14302233                file = file.right(file.length() - slsh - 1);
    14312234            }
    1432             cond = QDir(dirstr).entryList(QStringList(file)).count();
    1433 
    1434             break;
    1435         }
    1436     }
    1437 
    1438     if (result)
    1439         *result = cond;
    1440 
    1441     return ok;
     2235            if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?')))
     2236                if (!QDir(dirstr).entryList(QStringList(file)).isEmpty())
     2237                    return ProItem::ReturnTrue;
     2238
     2239            return ProItem::ReturnFalse;
     2240        }
     2241        case 0:
     2242            q->logMessage(format("'%1' is not a recognized test function").arg(function));
     2243            return ProItem::ReturnFalse;
     2244        default:
     2245            q->logMessage(format("Function '%1' is not implemented").arg(function));
     2246            return ProItem::ReturnFalse;
     2247    }
     2248}
     2249
     2250QStringList ProFileEvaluator::Private::values(const QString &variableName,
     2251                                              const QHash<QString, QStringList> &place,
     2252                                              const ProFile *pro) const
     2253{
     2254    if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token
     2255        return QStringList(QLatin1String("\t"));
     2256    if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $
     2257        return QStringList(QLatin1String("$"));
     2258    if (variableName == QLatin1String("LITERAL_HASH")) //a real #
     2259        return QStringList(QLatin1String("#"));
     2260    if (variableName == QLatin1String("OUT_PWD")) //the out going dir
     2261        return QStringList(m_outputDir);
     2262    if (variableName == QLatin1String("PWD") ||  //current working dir (of _FILE_)
     2263        variableName == QLatin1String("IN_PWD"))
     2264        return QStringList(currentDirectory());
     2265    if (variableName == QLatin1String("DIR_SEPARATOR"))
     2266        return QStringList(Option::dir_sep);
     2267    if (variableName == QLatin1String("DIRLIST_SEPARATOR"))
     2268        return QStringList(Option::dirlist_sep);
     2269    if (variableName == QLatin1String("_LINE_")) //parser line number
     2270        return QStringList(QString::number(m_lineNo));
     2271    if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here
     2272        return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName());
     2273    if (variableName == QLatin1String("_DATE_")) //current date/time
     2274        return QStringList(QDateTime::currentDateTime().toString());
     2275    if (variableName == QLatin1String("_PRO_FILE_"))
     2276        return QStringList(m_origfile);
     2277    if (variableName == QLatin1String("_PRO_FILE_PWD_"))
     2278        return  QStringList(QFileInfo(m_origfile).absolutePath());
     2279    if (variableName == QLatin1String("_QMAKE_CACHE_"))
     2280        return QStringList(); // FIXME?
     2281    if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) {
     2282        QString ret, type = variableName.mid(11);
     2283#if defined(Q_OS_WIN32)
     2284        if (type == QLatin1String("os")) {
     2285            ret = QLatin1String("Windows");
     2286        } else if (type == QLatin1String("name")) {
     2287            DWORD name_length = 1024;
     2288            wchar_t name[1024];
     2289            if (GetComputerName(name, &name_length))
     2290                ret = QString::fromWCharArray(name);
     2291        } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) {
     2292            QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
     2293            if (type == QLatin1String("version"))
     2294                ret = QString::number(ver);
     2295            else if (ver == QSysInfo::WV_Me)
     2296                ret = QLatin1String("WinMe");
     2297            else if (ver == QSysInfo::WV_95)
     2298                ret = QLatin1String("Win95");
     2299            else if (ver == QSysInfo::WV_98)
     2300                ret = QLatin1String("Win98");
     2301            else if (ver == QSysInfo::WV_NT)
     2302                ret = QLatin1String("WinNT");
     2303            else if (ver == QSysInfo::WV_2000)
     2304                ret = QLatin1String("Win2000");
     2305            else if (ver == QSysInfo::WV_2000)
     2306                ret = QLatin1String("Win2003");
     2307            else if (ver == QSysInfo::WV_XP)
     2308                ret = QLatin1String("WinXP");
     2309            else if (ver == QSysInfo::WV_VISTA)
     2310                ret = QLatin1String("WinVista");
     2311            else
     2312                ret = QLatin1String("Unknown");
     2313        } else if (type == QLatin1String("arch")) {
     2314            SYSTEM_INFO info;
     2315            GetSystemInfo(&info);
     2316            switch(info.wProcessorArchitecture) {
     2317#ifdef PROCESSOR_ARCHITECTURE_AMD64
     2318            case PROCESSOR_ARCHITECTURE_AMD64:
     2319                ret = QLatin1String("x86_64");
     2320                break;
     2321#endif
     2322            case PROCESSOR_ARCHITECTURE_INTEL:
     2323                ret = QLatin1String("x86");
     2324                break;
     2325            case PROCESSOR_ARCHITECTURE_IA64:
     2326#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
     2327            case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
     2328#endif
     2329                ret = QLatin1String("IA64");
     2330                break;
     2331            default:
     2332                ret = QLatin1String("Unknown");
     2333                break;
     2334            }
     2335        }
     2336#elif defined(Q_OS_UNIX)
     2337        struct utsname name;
     2338        if (!uname(&name)) {
     2339            if (type == QLatin1String("os"))
     2340                ret = QString::fromLatin1(name.sysname);
     2341            else if (type == QLatin1String("name"))
     2342                ret = QString::fromLatin1(name.nodename);
     2343            else if (type == QLatin1String("version"))
     2344                ret = QString::fromLatin1(name.release);
     2345            else if (type == QLatin1String("version_string"))
     2346                ret = QString::fromLatin1(name.version);
     2347            else if (type == QLatin1String("arch"))
     2348                ret = QString::fromLatin1(name.machine);
     2349        }
     2350#endif
     2351        return QStringList(ret);
     2352    }
     2353
     2354    QStringList result = place[variableName];
     2355    if (result.isEmpty()) {
     2356        if (variableName == QLatin1String("TARGET")) {
     2357            result.append(QFileInfo(m_origfile).baseName());
     2358        } else if (variableName == QLatin1String("TEMPLATE")) {
     2359            result.append(QLatin1String("app"));
     2360        } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) {
     2361            result.append(Option::dirlist_sep);
     2362        }
     2363    }
     2364    return result;
    14422365}
    14432366
    14442367QStringList ProFileEvaluator::Private::values(const QString &variableName) const
    14452368{
    1446     if (variableName == QLatin1String("TARGET")) {
    1447         QStringList list = m_valuemap.value(variableName);
    1448         if (!m_origfile.isEmpty())
    1449             list.append(QFileInfo(m_origfile).baseName());
    1450         return list;
    1451     }
    1452     if (variableName == QLatin1String("PWD")) {
    1453         return QStringList(getcwd());
    1454     }
    1455     return m_valuemap.value(variableName);
     2369    return values(variableName, m_valuemap, currentProFile());
    14562370}
    14572371
    14582372QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
    14592373{
    1460     if (variableName == QLatin1String("TARGET")) {
    1461         QStringList list = m_filevaluemap[pro].value(variableName);
    1462         if (!m_origfile.isEmpty())
    1463             list.append(QFileInfo(m_origfile).baseName());
    1464         return list;
    1465     }
    1466     if (variableName == QLatin1String("PWD")) {
    1467         return QStringList(QFileInfo(pro->fileName()).absoluteFilePath());
    1468     }
    1469     return m_filevaluemap[pro].value(variableName);
     2374    return values(variableName, m_filevaluemap[pro], pro);
    14702375}
    14712376
     
    14742379    QFileInfo fi(fileName);
    14752380    if (fi.exists()) {
    1476         ProFile *pro = new ProFile(fi.absoluteFilePath());
     2381        QString fn = QDir::cleanPath(fi.absoluteFilePath());
     2382        foreach (const ProFile *pf, d->m_profileStack)
     2383            if (pf->fileName() == fn) {
     2384                errorMessage(d->format("circular inclusion of %1").arg(fn));
     2385                return 0;
     2386            }
     2387        ProFile *pro = new ProFile(fn);
    14772388        if (d->read(pro))
    14782389            return pro;
     
    14872398}
    14882399
    1489 bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *result)
    1490 {
    1491     bool ok = true;
     2400bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
     2401{
    14922402    ProFile *pro = q->parsedProFile(fileName);
    14932403    if (pro) {
    14942404        m_profileStack.push(pro);
    1495         ok = (currentProFile() ? pro->Accept(this) : false);
     2405        bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
    14962406        m_profileStack.pop();
    14972407        q->releaseParsedProFile(pro);
    1498 
    1499         if (result)
    1500             *result = true;
     2408        return ok;
    15012409    } else {
    1502         if (result)
    1503             *result = false;
    1504     }
    1505 /*    if (ok && readFeatures) {
    1506         QStringList configs = values("CONFIG");
    1507         QSet<QString> processed;
    1508         foreach (const QString &fn, configs) {
    1509             if (!processed.contains(fn)) {
    1510                 processed.insert(fn);
    1511                 evaluateFeatureFile(fn, 0);
    1512             }
    1513         }
    1514     } */
    1515 
    1516     return ok;
    1517 }
    1518 
    1519 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, bool *result)
     2410        return false;
     2411    }
     2412}
     2413
     2414bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
    15202415{
    15212416    QString fn;
     
    15322427        }
    15332428    }
    1534     return fn.isEmpty() ? false : evaluateFile(fn, result);
    1535 }
    1536 
    1537 void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName,
    1538         QStringList &sources_out)
    1539 {
    1540     const QStringList vpaths = values(QLatin1String("VPATH"))
    1541         + values(QLatin1String("QMAKE_ABSOLUTE_SOURCE_PATH"))
    1542         + values(QLatin1String("DEPENDPATH"))
    1543         + values(QLatin1String("VPATH_SOURCES"));
    1544 
    1545     QFileInfo fi(absName);
    1546     bool found = fi.exists();
    1547     // Search in all vpaths
    1548     if (!found) {
    1549         foreach (const QString &vpath, vpaths) {
    1550             fi.setFile(vpath + QDir::separator() + relName);
    1551             if (fi.exists()) {
    1552                 found = true;
    1553                 break;
    1554             }
    1555         }
    1556     }
    1557 
    1558     if (found) {
    1559         sources_out += fi.absoluteFilePath(); // Not resolving symlinks
    1560     } else {
    1561         QString val = relName;
    1562         QString dir;
    1563         QString wildcard = val;
    1564         QString real_dir;
    1565         if (wildcard.lastIndexOf(QLatin1Char('/')) != -1) {
    1566             dir = wildcard.left(wildcard.lastIndexOf(QLatin1Char('/')) + 1);
    1567             real_dir = dir;
    1568             wildcard = wildcard.right(wildcard.length() - dir.length());
    1569         }
    1570 
    1571         if (real_dir.isEmpty() || QFileInfo(real_dir).exists()) {
    1572             QStringList files = QDir(real_dir).entryList(QStringList(wildcard));
    1573             if (files.isEmpty()) {
    1574                 q->logMessage(format("Failure to find %1").arg(val));
    1575             } else {
    1576                 QString a;
    1577                 for (int i = files.count() - 1; i >= 0; --i) {
    1578                     if (files[i] == QLatin1String(".") || files[i] == QLatin1String(".."))
    1579                         continue;
    1580                     a = dir + files[i];
    1581                     sources_out += a;
    1582                 }
    1583             }
    1584         } else {
    1585             q->logMessage(format("Cannot match %1/%2, as %3 does not exist.")
    1586                 .arg(real_dir).arg(wildcard).arg(real_dir));
    1587         }
    1588     }
    1589 }
    1590 
    1591 
    1592 /*
    1593  * Lookup of files are done in this order:
    1594  *  1. look in pwd
    1595  *  2. look in vpaths
    1596  *  3. expand wild card files relative from the profiles folder
    1597  **/
    1598 
    1599 // FIXME: This code supports something that I'd consider a flaw in .pro file syntax
    1600 // which is not even documented. So arguably this can be ditched completely...
    1601 QStringList ProFileEvaluator::Private::expandPattern(const QString& pattern)
    1602 {
    1603     if (!currentProFile())
    1604         return QStringList();
    1605 
    1606     QStringList sources_out;
    1607     const QString absName = QDir::cleanPath(QDir::current().absoluteFilePath(pattern));
    1608 
    1609     expandPatternHelper(pattern, absName, sources_out);
    1610     return sources_out;
     2429    if (fn.isEmpty())
     2430        return false;
     2431    bool cumulative = m_cumulative;
     2432    m_cumulative = false;
     2433    bool ok = evaluateFile(fn);
     2434    m_cumulative = cumulative;
     2435    return ok;
    16112436}
    16122437
     
    16422467}
    16432468
     2469inline QStringList fixEnvVariables(const QStringList &x)
     2470{
     2471    QStringList ret;
     2472    foreach (const QString &str, x)
     2473        ret << Option::fixString(str, Option::FixEnvVars);
     2474    return ret;
     2475}
     2476
     2477
    16442478QStringList ProFileEvaluator::values(const QString &variableName) const
    16452479{
    1646     return d->values(variableName);
     2480    return fixEnvVariables(d->values(variableName));
    16472481}
    16482482
    16492483QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
    16502484{
    1651     return d->values(variableName, pro);
     2485    return fixEnvVariables(d->values(variableName, pro));
     2486}
     2487
     2488QStringList ProFileEvaluator::absolutePathValues(
     2489        const QString &variable, const QString &baseDirectory) const
     2490{
     2491    QStringList result;
     2492    foreach (const QString &el, values(variable)) {
     2493        const QFileInfo info = QFileInfo(baseDirectory, el);
     2494        if (info.isDir())
     2495            result << QDir::cleanPath(info.absoluteFilePath());
     2496    }
     2497    return result;
     2498}
     2499
     2500QStringList ProFileEvaluator::absoluteFileValues(
     2501        const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
     2502        const ProFile *pro) const
     2503{
     2504    QStringList result;
     2505    foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
     2506        QFileInfo info(el);
     2507        if (info.isAbsolute()) {
     2508            if (info.exists()) {
     2509                result << QDir::cleanPath(el);
     2510                goto next;
     2511            }
     2512        } else {
     2513            foreach (const QString &dir, searchDirs) {
     2514                QFileInfo info(dir, el);
     2515                if (info.isFile()) {
     2516                    result << QDir::cleanPath(info.filePath());
     2517                    goto next;
     2518                }
     2519            }
     2520            if (baseDirectory.isEmpty())
     2521                goto next;
     2522            info = QFileInfo(baseDirectory, el);
     2523        }
     2524        {
     2525            QFileInfo baseInfo(info.absolutePath());
     2526            if (baseInfo.exists()) {
     2527                QString wildcard = info.fileName();
     2528                if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
     2529                    QDir theDir(QDir::cleanPath(baseInfo.filePath()));
     2530                    foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
     2531                        if (fn != QLatin1String(".") && fn != QLatin1String(".."))
     2532                            result << theDir.absoluteFilePath(fn);
     2533                }
     2534            }
     2535        }
     2536      next: ;
     2537    }
     2538    return result;
    16522539}
    16532540
     
    16562543    QStringList templ = values(QLatin1String("TEMPLATE"));
    16572544    if (templ.count() >= 1) {
    1658         QString t = templ.last().toLower();
    1659         if (t == QLatin1String("app"))
     2545        const QString &t = templ.last();
     2546        if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
    16602547            return TT_Application;
    1661         if (t == QLatin1String("lib"))
     2548        if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
    16622549            return TT_Library;
    1663         if (t == QLatin1String("subdirs"))
     2550        if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
     2551            return TT_Script;
     2552        if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
    16642553            return TT_Subdirs;
    16652554    }
     
    17052594void ProFileEvaluator::logMessage(const QString &message)
    17062595{
    1707     if (d->m_verbose)
     2596    if (d->m_verbose && !d->m_skipLevel)
    17082597        qWarning("%s", qPrintable(message));
    17092598}
     
    17112600void ProFileEvaluator::fileMessage(const QString &message)
    17122601{
    1713     qWarning("%s", qPrintable(message));
     2602    if (!d->m_skipLevel)
     2603        qWarning("%s", qPrintable(message));
    17142604}
    17152605
    17162606void ProFileEvaluator::errorMessage(const QString &message)
    17172607{
    1718     qWarning("%s", qPrintable(message));
     2608    if (!d->m_skipLevel)
     2609        qWarning("%s", qPrintable(message));
    17192610}
    17202611
     
    17242615}
    17252616
    1726 void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap)
    1727 {
    1728     QStringList sourceFiles;
    1729     QString codecForTr;
    1730     QString codecForSource;
    1731     QStringList tsFileNames;
    1732 
    1733     // app/lib template
    1734     sourceFiles += visitor.values(QLatin1String("SOURCES"));
    1735     sourceFiles += visitor.values(QLatin1String("HEADERS"));
    1736     tsFileNames = visitor.values(QLatin1String("TRANSLATIONS"));
    1737 
    1738     QStringList trcodec = visitor.values(QLatin1String("CODEC"))
    1739         + visitor.values(QLatin1String("DEFAULTCODEC"))
    1740         + visitor.values(QLatin1String("CODECFORTR"));
    1741     if (!trcodec.isEmpty())
    1742         codecForTr = trcodec.last();
    1743 
    1744     QStringList srccodec = visitor.values(QLatin1String("CODECFORSRC"));
    1745     if (!srccodec.isEmpty())
    1746         codecForSource = srccodec.last();
    1747 
    1748     QStringList forms = visitor.values(QLatin1String("INTERFACES"))
    1749         + visitor.values(QLatin1String("FORMS"))
    1750         + visitor.values(QLatin1String("FORMS3"));
    1751     sourceFiles << forms;
    1752 
    1753     sourceFiles.sort();
    1754     sourceFiles.removeDuplicates();
    1755     tsFileNames.sort();
    1756     tsFileNames.removeDuplicates();
    1757 
    1758     varMap->insert("SOURCES", sourceFiles);
    1759     varMap->insert("CODECFORTR", QStringList() << codecForTr);
    1760     varMap->insert("CODECFORSRC", QStringList() << codecForSource);
    1761     varMap->insert("TRANSLATIONS", tsFileNames);
    1762 }
    1763 
    1764 bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap)
    1765 {
    1766     QFileInfo fi(fileName);
    1767     if (!fi.exists())
    1768         return false;
    1769 
    1770     ProFile pro(fi.absoluteFilePath());
    1771 
    1772     ProFileEvaluator visitor;
    1773     visitor.setVerbose(verbose);
    1774 
    1775     if (!visitor.queryProFile(&pro))
    1776         return false;
    1777 
    1778     if (!visitor.accept(&pro))
    1779         return false;
    1780 
    1781     evaluateProFile(visitor, varMap);
    1782 
    1783     return true;
     2617void ProFileEvaluator::setCumulative(bool on)
     2618{
     2619    d->m_cumulative = on;
     2620}
     2621
     2622void ProFileEvaluator::setOutputDir(const QString &dir)
     2623{
     2624    d->m_outputDir = dir;
    17842625}
    17852626
Note: See TracChangeset for help on using the changeset viewer.