tools/linguist/shared/profileevaluator.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Linguist of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "profileevaluator.h"
       
    43 #include "proparserutils.h"
       
    44 #include "proitems.h"
       
    45 
       
    46 #include <QtCore/QByteArray>
       
    47 #include <QtCore/QDateTime>
       
    48 #include <QtCore/QDebug>
       
    49 #include <QtCore/QDir>
       
    50 #include <QtCore/QFile>
       
    51 #include <QtCore/QFileInfo>
       
    52 #include <QtCore/QList>
       
    53 #include <QtCore/QRegExp>
       
    54 #include <QtCore/QSet>
       
    55 #include <QtCore/QStack>
       
    56 #include <QtCore/QString>
       
    57 #include <QtCore/QStringList>
       
    58 #include <QtCore/QTextStream>
       
    59 
       
    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 
       
    69 #ifdef Q_OS_WIN32
       
    70 #define QT_POPEN _popen
       
    71 #define QT_PCLOSE _pclose
       
    72 #else
       
    73 #define QT_POPEN popen
       
    74 #define QT_PCLOSE pclose
       
    75 #endif
       
    76 
       
    77 QT_BEGIN_NAMESPACE
       
    78 
       
    79 ///////////////////////////////////////////////////////////////////////
       
    80 //
       
    81 // Option
       
    82 //
       
    83 ///////////////////////////////////////////////////////////////////////
       
    84 
       
    85 QString
       
    86 Option::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 }
       
   130 
       
   131 ///////////////////////////////////////////////////////////////////////
       
   132 //
       
   133 // ProFileEvaluator::Private
       
   134 //
       
   135 ///////////////////////////////////////////////////////////////////////
       
   136 
       
   137 class ProFileEvaluator::Private : public AbstractProItemVisitor
       
   138 {
       
   139 public:
       
   140     Private(ProFileEvaluator *q_);
       
   141 
       
   142     ProFileEvaluator *q;
       
   143     int m_lineNo;                                   // Error reporting
       
   144     bool m_verbose;
       
   145 
       
   146     /////////////// Reading pro file
       
   147 
       
   148     bool read(ProFile *pro);
       
   149 
       
   150     ProBlock *currentBlock();
       
   151     void updateItem();
       
   152     bool parseLine(const QString &line);
       
   153     void insertVariable(const QString &line, int *i);
       
   154     void insertOperator(const char op);
       
   155     void insertComment(const QString &comment);
       
   156     void enterScope(bool multiLine);
       
   157     void leaveScope();
       
   158     void finalizeBlock();
       
   159 
       
   160     QStack<ProBlock *> m_blockstack;
       
   161     ProBlock *m_block;
       
   162 
       
   163     ProItem *m_commentItem;
       
   164     QString m_proitem;
       
   165     QString m_pendingComment;
       
   166     bool m_syntaxError;
       
   167     bool m_contNextLine;
       
   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;
       
   223     QString m_lastVarName;
       
   224     ProVariable::VariableOperator m_variableOperator;
       
   225     QString m_origfile;
       
   226     QString m_oldPath;                              // To restore the current path to the path
       
   227     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 ...)
       
   241 
       
   242     QHash<QString, QStringList> m_valuemap;         // VariableName must be us-ascii, the content however can be non-us-ascii.
       
   243     QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
       
   244     QHash<QString, QString> m_properties;
       
   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;
       
   254 
       
   255     int m_prevLineNo;                               // Checking whether we're assigning the same TARGET
       
   256     ProFile *m_prevProFile;                         // See m_prevLineNo
       
   257 };
       
   258 
       
   259 Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
       
   260 Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
       
   261 
       
   262 ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
       
   263   : q(q_)
       
   264 {
       
   265     // Global parser state
       
   266     m_prevLineNo = 0;
       
   267     m_prevProFile = 0;
       
   268 
       
   269     // Configuration, more or less
       
   270     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();
       
   280 }
       
   281 
       
   282 bool ProFileEvaluator::Private::read(ProFile *pro)
       
   283 {
       
   284     QFile file(pro->fileName());
       
   285     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
       
   286         q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
       
   287         return false;
       
   288     }
       
   289 
       
   290     // Parser state
       
   291     m_block = 0;
       
   292     m_commentItem = 0;
       
   293     m_inQuote = false;
       
   294     m_parens = 0;
       
   295     m_contNextLine = false;
       
   296     m_syntaxError = false;
       
   297     m_lineNo = 1;
       
   298     m_blockstack.clear();
       
   299     m_blockstack.push(pro);
       
   300 
       
   301     QTextStream ts(&file);
       
   302     while (!ts.atEnd()) {
       
   303         QString line = ts.readLine();
       
   304         if (!parseLine(line)) {
       
   305             q->errorMessage(format(".pro parse failure."));
       
   306             return false;
       
   307         }
       
   308         ++m_lineNo;
       
   309     }
       
   310     return true;
       
   311 }
       
   312 
       
   313 bool ProFileEvaluator::Private::parseLine(const QString &line0)
       
   314 {
       
   315     if (m_blockstack.isEmpty())
       
   316         return false;
       
   317 
       
   318     int parens = m_parens;
       
   319     bool inQuote = m_inQuote;
       
   320     bool escaped = false;
       
   321     QString line = line0.simplified();
       
   322 
       
   323     for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
       
   324         ushort c = line.at(i).unicode();
       
   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;
       
   334                 continue;
       
   335             } else if (c == '"') {
       
   336                 inQuote = !inQuote;
       
   337                 m_proitem += c;
       
   338                 continue;
       
   339             }
       
   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                 }
       
   376             }
       
   377         }
       
   378 
       
   379         m_proitem += c;
       
   380     }
       
   381     m_inQuote = inQuote;
       
   382     m_parens = parens;
       
   383     m_contNextLine = escaped;
       
   384     if (escaped) {
       
   385         m_proitem.chop(1);
       
   386         updateItem();
       
   387         return true;
       
   388     } else {
       
   389         if (!m_syntaxError) {
       
   390             updateItem();
       
   391             finalizeBlock();
       
   392             return true;
       
   393         }
       
   394         return false;
       
   395     }
       
   396 }
       
   397 
       
   398 void ProFileEvaluator::Private::finalizeBlock()
       
   399 {
       
   400     if (m_blockstack.isEmpty()) {
       
   401         m_syntaxError = true;
       
   402     } else {
       
   403         if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
       
   404             leaveScope();
       
   405         m_block = 0;
       
   406         m_commentItem = 0;
       
   407     }
       
   408 }
       
   409 
       
   410 void ProFileEvaluator::Private::insertVariable(const QString &line, int *i)
       
   411 {
       
   412     ProVariable::VariableOperator opkind;
       
   413 
       
   414     if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker
       
   415         return;
       
   416 
       
   417     switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
       
   418         case '+':
       
   419             m_proitem.chop(1);
       
   420             opkind = ProVariable::AddOperator;
       
   421             break;
       
   422         case '-':
       
   423             m_proitem.chop(1);
       
   424             opkind = ProVariable::RemoveOperator;
       
   425             break;
       
   426         case '*':
       
   427             m_proitem.chop(1);
       
   428             opkind = ProVariable::UniqueAddOperator;
       
   429             break;
       
   430         case '~':
       
   431             m_proitem.chop(1);
       
   432             opkind = ProVariable::ReplaceOperator;
       
   433             break;
       
   434         default:
       
   435             opkind = ProVariable::SetOperator;
       
   436     }
       
   437 
       
   438     ProBlock *block = m_blockstack.top();
       
   439     m_proitem = m_proitem.trimmed();
       
   440     ProVariable *variable = new ProVariable(m_proitem, block);
       
   441     variable->setLineNumber(m_lineNo);
       
   442     variable->setVariableOperator(opkind);
       
   443     block->appendItem(variable);
       
   444     m_block = variable;
       
   445 
       
   446     if (!m_pendingComment.isEmpty()) {
       
   447         m_block->setComment(m_pendingComment);
       
   448         m_pendingComment.clear();
       
   449     }
       
   450     m_commentItem = variable;
       
   451 
       
   452     m_proitem.clear();
       
   453 
       
   454     if (opkind == ProVariable::ReplaceOperator) {
       
   455         // skip util end of line or comment
       
   456         while (1) {
       
   457             ++(*i);
       
   458 
       
   459             // end of line?
       
   460             if (*i >= line.count())
       
   461                 break;
       
   462 
       
   463             // comment?
       
   464             if (line.at(*i).unicode() == '#') {
       
   465                 --(*i);
       
   466                 break;
       
   467             }
       
   468 
       
   469             m_proitem += line.at(*i);
       
   470         }
       
   471         m_proitem = m_proitem.trimmed();
       
   472     }
       
   473 }
       
   474 
       
   475 void ProFileEvaluator::Private::insertOperator(const char op)
       
   476 {
       
   477     updateItem();
       
   478 
       
   479     ProOperator::OperatorKind opkind;
       
   480     switch(op) {
       
   481         case '!':
       
   482             opkind = ProOperator::NotOperator;
       
   483             break;
       
   484         case '|':
       
   485             opkind = ProOperator::OrOperator;
       
   486             break;
       
   487         default:
       
   488             opkind = ProOperator::OrOperator;
       
   489     }
       
   490 
       
   491     ProBlock * const block = currentBlock();
       
   492     ProOperator * const proOp = new ProOperator(opkind);
       
   493     proOp->setLineNumber(m_lineNo);
       
   494     block->appendItem(proOp);
       
   495     m_commentItem = proOp;
       
   496 }
       
   497 
       
   498 void ProFileEvaluator::Private::insertComment(const QString &comment)
       
   499 {
       
   500     updateItem();
       
   501 
       
   502     QString strComment;
       
   503     if (!m_commentItem)
       
   504         strComment = m_pendingComment;
       
   505     else
       
   506         strComment = m_commentItem->comment();
       
   507 
       
   508     if (strComment.isEmpty())
       
   509         strComment = comment;
       
   510     else {
       
   511         strComment += QLatin1Char('\n');
       
   512         strComment += comment.trimmed();
       
   513     }
       
   514 
       
   515     strComment = strComment.trimmed();
       
   516 
       
   517     if (!m_commentItem)
       
   518         m_pendingComment = strComment;
       
   519     else
       
   520         m_commentItem->setComment(strComment);
       
   521 }
       
   522 
       
   523 void ProFileEvaluator::Private::enterScope(bool multiLine)
       
   524 {
       
   525     updateItem();
       
   526 
       
   527     ProBlock *parent = currentBlock();
       
   528     ProBlock *block = new ProBlock(parent);
       
   529     block->setLineNumber(m_lineNo);
       
   530     parent->setBlockKind(ProBlock::ScopeKind);
       
   531 
       
   532     parent->appendItem(block);
       
   533 
       
   534     if (multiLine)
       
   535         block->setBlockKind(ProBlock::ScopeContentsKind);
       
   536     else
       
   537         block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);
       
   538 
       
   539     m_blockstack.push(block);
       
   540     m_block = 0;
       
   541 }
       
   542 
       
   543 void ProFileEvaluator::Private::leaveScope()
       
   544 {
       
   545     updateItem();
       
   546     m_blockstack.pop();
       
   547     finalizeBlock();
       
   548 }
       
   549 
       
   550 ProBlock *ProFileEvaluator::Private::currentBlock()
       
   551 {
       
   552     if (m_block)
       
   553         return m_block;
       
   554 
       
   555     ProBlock *parent = m_blockstack.top();
       
   556     m_block = new ProBlock(parent);
       
   557     m_block->setLineNumber(m_lineNo);
       
   558     parent->appendItem(m_block);
       
   559 
       
   560     if (!m_pendingComment.isEmpty()) {
       
   561         m_block->setComment(m_pendingComment);
       
   562         m_pendingComment.clear();
       
   563     }
       
   564 
       
   565     m_commentItem = m_block;
       
   566 
       
   567     return m_block;
       
   568 }
       
   569 
       
   570 void ProFileEvaluator::Private::updateItem()
       
   571 {
       
   572     m_proitem = m_proitem.trimmed();
       
   573     if (m_proitem.isEmpty())
       
   574         return;
       
   575 
       
   576     ProBlock *block = currentBlock();
       
   577     if (block->blockKind() & ProBlock::VariableKind) {
       
   578         m_commentItem = new ProValue(m_proitem, static_cast<ProVariable*>(block));
       
   579     } else if (m_proitem.endsWith(QLatin1Char(')'))) {
       
   580         m_commentItem = new ProFunction(m_proitem);
       
   581     } else {
       
   582         m_commentItem = new ProCondition(m_proitem);
       
   583     }
       
   584     m_commentItem->setLineNumber(m_lineNo);
       
   585     block->appendItem(m_commentItem);
       
   586 
       
   587     m_proitem.clear();
       
   588 }
       
   589 
       
   590 
       
   591 ProItem::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 
       
   625 void 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 
       
   640 ProItem::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 
       
   663 void ProFileEvaluator::Private::visitProLoopCleanup()
       
   664 {
       
   665     ProLoop &loop = m_loopStack.top();
       
   666     m_valuemap[loop.variable] = loop.oldVarVal;
       
   667     m_loopStack.pop_back();
       
   668 }
       
   669 
       
   670 void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
       
   671 {
       
   672     m_lastVarName = variable->variable();
       
   673     m_variableOperator = variable->variableOperator();
       
   674     m_isFirstVariableValue = true;
       
   675     m_tempValuemap = m_valuemap;
       
   676     m_tempFilevaluemap = m_filevaluemap;
       
   677 }
       
   678 
       
   679 void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
       
   680 {
       
   681     Q_UNUSED(variable);
       
   682     m_valuemap = m_tempValuemap;
       
   683     m_filevaluemap = m_tempFilevaluemap;
       
   684     m_lastVarName.clear();
       
   685 }
       
   686 
       
   687 void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
       
   688 {
       
   689     m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
       
   690 }
       
   691 
       
   692 void 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 
       
   706 ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
       
   707 {
       
   708     PRE(pro);
       
   709     m_lineNo = pro->lineNumber();
       
   710     if (m_origfile.isEmpty())
       
   711         m_origfile = pro->fileName();
       
   712     if (m_oldPath.isEmpty()) {
       
   713         // change the working directory for the initial profile we visit, since
       
   714         // that is *the* profile. All the other times we reach this function will be due to
       
   715         // include(file) or load(file)
       
   716 
       
   717         m_oldPath = QDir::currentPath();
       
   718 
       
   719         m_profileStack.push(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 
       
   738 ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
       
   739 {
       
   740     PRE(pro);
       
   741     m_lineNo = pro->lineNumber();
       
   742     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 
       
   779         m_profileStack.pop();
       
   780         return returnBool(QDir::setCurrent(m_oldPath));
       
   781     }
       
   782 
       
   783     return ProItem::ReturnTrue;
       
   784 }
       
   785 
       
   786 static 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 
       
   804 void ProFileEvaluator::Private::visitProValue(ProValue *value)
       
   805 {
       
   806     PRE(value);
       
   807     m_lineNo = value->lineNumber();
       
   808     QString val = value->value();
       
   809 
       
   810     QString varName = m_lastVarName;
       
   811 
       
   812     QStringList v = expandVariableReferences(val);
       
   813 
       
   814     // Since qmake combines different values for the TARGET variable, we join
       
   815     // TARGET values that are on the same line. We can't do this later with all
       
   816     // values because this parser isn't scope-aware, so we'd risk joining
       
   817     // scope-specific targets together.
       
   818     if (varName == QLatin1String("TARGET")
       
   819             && m_lineNo == m_prevLineNo
       
   820             && currentProFile() == m_prevProFile) {
       
   821         QStringList targets = m_tempValuemap.value(QLatin1String("TARGET"));
       
   822         m_tempValuemap.remove(QLatin1String("TARGET"));
       
   823         QStringList lastTarget(targets.takeLast());
       
   824         lastTarget << v.join(QLatin1String(" "));
       
   825         targets.push_back(lastTarget.join(QLatin1String(" ")));
       
   826         v = targets;
       
   827     }
       
   828     m_prevLineNo = m_lineNo;
       
   829     m_prevProFile = currentProFile();
       
   830 
       
   831     switch (m_variableOperator) {
       
   832         case ProVariable::SetOperator:          // =
       
   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:      // ~=
       
   872             {
       
   873                 // DEFINES ~= s/a/b/?[gqi]
       
   874 
       
   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                 }
       
   880                 QChar sep = val.at(1);
       
   881                 QStringList func = val.split(sep);
       
   882                 if (func.count() < 3 || func.count() > 4) {
       
   883                     q->logMessage(format("the s/// function expects 3 or 4 arguments."));
       
   884                     break;
       
   885                 }
       
   886 
       
   887                 bool global = false, quote = false, case_sense = false;
       
   888                 if (func.count() == 4) {
       
   889                     global = func[3].indexOf(QLatin1Char('g')) != -1;
       
   890                     case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
       
   891                     quote = func[3].indexOf(QLatin1Char('q')) != -1;
       
   892                 }
       
   893                 QString pattern = func[1];
       
   894                 QString replace = func[2];
       
   895                 if (quote)
       
   896                     pattern = QRegExp::escape(pattern);
       
   897 
       
   898                 QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
       
   899 
       
   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 
       
   913 ProItem::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;
       
   935 }
       
   936 
       
   937 
       
   938 QStringList ProFileEvaluator::Private::qmakeFeaturePaths()
       
   939 {
       
   940     QStringList concat;
       
   941     {
       
   942         const QString base_concat = QDir::separator() + QLatin1String("features");
       
   943         concat << base_concat + QDir::separator() + QLatin1String("mac");
       
   944         concat << base_concat + QDir::separator() + QLatin1String("macx");
       
   945         concat << base_concat + QDir::separator() + QLatin1String("unix");
       
   946         concat << base_concat + QDir::separator() + QLatin1String("win32");
       
   947         concat << base_concat + QDir::separator() + QLatin1String("mac9");
       
   948         concat << base_concat + QDir::separator() + QLatin1String("qnx6");
       
   949         concat << base_concat;
       
   950     }
       
   951     const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs");
       
   952     QStringList feature_roots;
       
   953     QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
       
   954     if (!mkspec_path.isNull())
       
   955         feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
       
   956     /*
       
   957     if (prop)
       
   958         feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
       
   959     if (!Option::mkfile::cachefile.isEmpty()) {
       
   960         QString path;
       
   961         int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
       
   962         if (last_slash != -1)
       
   963             path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
       
   964         foreach (const QString &concat_it, concat)
       
   965             feature_roots << (path + concat_it);
       
   966     }
       
   967     */
       
   968 
       
   969     QByteArray qmakepath = qgetenv("QMAKEPATH");
       
   970     if (!qmakepath.isNull()) {
       
   971         const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
       
   972         foreach (const QString &item, lst) {
       
   973             foreach (const QString &concat_it, concat)
       
   974                 feature_roots << (item + mkspecs_concat + concat_it);
       
   975         }
       
   976     }
       
   977     //if (!Option::mkfile::qmakespec.isEmpty())
       
   978     //    feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
       
   979     //if (!Option::mkfile::qmakespec.isEmpty()) {
       
   980     //    QFileInfo specfi(Option::mkfile::qmakespec);
       
   981     //    QDir specdir(specfi.absoluteFilePath());
       
   982     //    while (!specdir.isRoot()) {
       
   983     //        if (!specdir.cdUp() || specdir.isRoot())
       
   984     //            break;
       
   985     //        if (QFile::exists(specdir.path() + QDir::separator() + "features")) {
       
   986     //            foreach (const QString &concat_it, concat)
       
   987     //                feature_roots << (specdir.path() + concat_it);
       
   988     //            break;
       
   989     //        }
       
   990     //    }
       
   991     //}
       
   992     foreach (const QString &concat_it, concat)
       
   993         feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
       
   994                           mkspecs_concat + concat_it);
       
   995     foreach (const QString &concat_it, concat)
       
   996         feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
       
   997                           mkspecs_concat + concat_it);
       
   998     return feature_roots;
       
   999 }
       
  1000 
       
  1001 QString ProFileEvaluator::Private::propertyValue(const QString &name) const
       
  1002 {
       
  1003     if (m_properties.contains(name))
       
  1004         return m_properties.value(name);
       
  1005     if (name == QLatin1String("QT_INSTALL_PREFIX"))
       
  1006         return QLibraryInfo::location(QLibraryInfo::PrefixPath);
       
  1007     if (name == QLatin1String("QT_INSTALL_DATA"))
       
  1008         return QLibraryInfo::location(QLibraryInfo::DataPath);
       
  1009     if (name == QLatin1String("QT_INSTALL_DOCS"))
       
  1010         return QLibraryInfo::location(QLibraryInfo::DocumentationPath);
       
  1011     if (name == QLatin1String("QT_INSTALL_HEADERS"))
       
  1012         return QLibraryInfo::location(QLibraryInfo::HeadersPath);
       
  1013     if (name == QLatin1String("QT_INSTALL_LIBS"))
       
  1014         return QLibraryInfo::location(QLibraryInfo::LibrariesPath);
       
  1015     if (name == QLatin1String("QT_INSTALL_BINS"))
       
  1016         return QLibraryInfo::location(QLibraryInfo::BinariesPath);
       
  1017     if (name == QLatin1String("QT_INSTALL_PLUGINS"))
       
  1018         return QLibraryInfo::location(QLibraryInfo::PluginsPath);
       
  1019     if (name == QLatin1String("QT_INSTALL_TRANSLATIONS"))
       
  1020         return QLibraryInfo::location(QLibraryInfo::TranslationsPath);
       
  1021     if (name == QLatin1String("QT_INSTALL_CONFIGURATION"))
       
  1022         return QLibraryInfo::location(QLibraryInfo::SettingsPath);
       
  1023     if (name == QLatin1String("QT_INSTALL_EXAMPLES"))
       
  1024         return QLibraryInfo::location(QLibraryInfo::ExamplesPath);
       
  1025     if (name == QLatin1String("QT_INSTALL_DEMOS"))
       
  1026         return QLibraryInfo::location(QLibraryInfo::DemosPath);
       
  1027     if (name == QLatin1String("QMAKE_MKSPECS"))
       
  1028         return qmake_mkspec_paths().join(Option::dirlist_sep);
       
  1029     if (name == QLatin1String("QMAKE_VERSION"))
       
  1030         return QLatin1String("1.0");        //### FIXME
       
  1031         //return qmake_version();
       
  1032 #ifdef QT_VERSION_STR
       
  1033     if (name == QLatin1String("QT_VERSION"))
       
  1034         return QLatin1String(QT_VERSION_STR);
       
  1035 #endif
       
  1036     return QLatin1String("UNKNOWN");        //###
       
  1037 }
       
  1038 
       
  1039 ProFile *ProFileEvaluator::Private::currentProFile() const
       
  1040 {
       
  1041     if (m_profileStack.count() > 0)
       
  1042         return m_profileStack.top();
       
  1043     return 0;
       
  1044 }
       
  1045 
       
  1046 QString ProFileEvaluator::Private::currentFileName() const
       
  1047 {
       
  1048     ProFile *pro = currentProFile();
       
  1049     if (pro)
       
  1050         return pro->fileName();
       
  1051     return QString();
       
  1052 }
       
  1053 
       
  1054 QString ProFileEvaluator::Private::currentDirectory() const
       
  1055 {
       
  1056     ProFile *cur = m_profileStack.top();
       
  1057     return cur->directoryName();
       
  1058 }
       
  1059 
       
  1060 void ProFileEvaluator::Private::doVariableReplace(QString *str)
       
  1061 {
       
  1062     *str = expandVariableReferences(*str).join(QString(Option::field_sep));
       
  1063 }
       
  1064 
       
  1065 QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
       
  1066 {
       
  1067     QStringList ret;
       
  1068 //    if (ok)
       
  1069 //        *ok = true;
       
  1070     if (str.isEmpty())
       
  1071         return ret;
       
  1072 
       
  1073     const ushort LSQUARE = '[';
       
  1074     const ushort RSQUARE = ']';
       
  1075     const ushort LCURLY = '{';
       
  1076     const ushort RCURLY = '}';
       
  1077     const ushort LPAREN = '(';
       
  1078     const ushort RPAREN = ')';
       
  1079     const ushort DOLLAR = '$';
       
  1080     const ushort BACKSLASH = '\\';
       
  1081     const ushort UNDERSCORE = '_';
       
  1082     const ushort DOT = '.';
       
  1083     const ushort SPACE = ' ';
       
  1084     const ushort TAB = '\t';
       
  1085     const ushort SINGLEQUOTE = '\'';
       
  1086     const ushort DOUBLEQUOTE = '"';
       
  1087 
       
  1088     ushort unicode, quote = 0;
       
  1089     const QChar *str_data = str.data();
       
  1090     const int str_len = str.length();
       
  1091 
       
  1092     ushort term;
       
  1093     QString var, args;
       
  1094 
       
  1095     int replaced = 0;
       
  1096     QString current;
       
  1097     for (int i = 0; i < str_len; ++i) {
       
  1098         unicode = str_data[i].unicode();
       
  1099         const int start_var = i;
       
  1100         if (unicode == DOLLAR && str_len > i+2) {
       
  1101             unicode = str_data[++i].unicode();
       
  1102             if (unicode == DOLLAR) {
       
  1103                 term = 0;
       
  1104                 var.clear();
       
  1105                 args.clear();
       
  1106                 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
       
  1107                 unicode = str_data[++i].unicode();
       
  1108                 if (unicode == LSQUARE) {
       
  1109                     unicode = str_data[++i].unicode();
       
  1110                     term = RSQUARE;
       
  1111                     var_type = PROPERTY;
       
  1112                 } else if (unicode == LCURLY) {
       
  1113                     unicode = str_data[++i].unicode();
       
  1114                     var_type = VAR;
       
  1115                     term = RCURLY;
       
  1116                 } else if (unicode == LPAREN) {
       
  1117                     unicode = str_data[++i].unicode();
       
  1118                     var_type = ENVIRON;
       
  1119                     term = RPAREN;
       
  1120                 }
       
  1121                 forever {
       
  1122                     if (!(unicode & (0xFF<<8)) &&
       
  1123                        unicode != DOT && unicode != UNDERSCORE &&
       
  1124                        //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
       
  1125                        (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
       
  1126                        (unicode < '0' || unicode > '9'))
       
  1127                         break;
       
  1128                     var.append(QChar(unicode));
       
  1129                     if (++i == str_len)
       
  1130                         break;
       
  1131                     unicode = str_data[i].unicode();
       
  1132                     // at this point, i points to either the 'term' or 'next' character (which is in unicode)
       
  1133                 }
       
  1134                 if (var_type == VAR && unicode == LPAREN) {
       
  1135                     var_type = FUNCTION;
       
  1136                     int depth = 0;
       
  1137                     forever {
       
  1138                         if (++i == str_len)
       
  1139                             break;
       
  1140                         unicode = str_data[i].unicode();
       
  1141                         if (unicode == LPAREN) {
       
  1142                             depth++;
       
  1143                         } else if (unicode == RPAREN) {
       
  1144                             if (!depth)
       
  1145                                 break;
       
  1146                             --depth;
       
  1147                         }
       
  1148                         args.append(QChar(unicode));
       
  1149                     }
       
  1150                     if (++i < str_len)
       
  1151                         unicode = str_data[i].unicode();
       
  1152                     else
       
  1153                         unicode = 0;
       
  1154                     // at this point i is pointing to the 'next' character (which is in unicode)
       
  1155                     // this might actually be a term character since you can do $${func()}
       
  1156                 }
       
  1157                 if (term) {
       
  1158                     if (unicode != term) {
       
  1159                         q->logMessage(format("Missing %1 terminator [found %2]")
       
  1160                             .arg(QChar(term))
       
  1161                             .arg(unicode ? QString(unicode) : QString::fromLatin1(("end-of-line"))));
       
  1162 //                        if (ok)
       
  1163 //                            *ok = false;
       
  1164                         return QStringList();
       
  1165                     }
       
  1166                 } else {
       
  1167                     // move the 'cursor' back to the last char of the thing we were looking at
       
  1168                     --i;
       
  1169                 }
       
  1170                 // since i never points to the 'next' character, there is no reason for this to be set
       
  1171                 unicode = 0;
       
  1172 
       
  1173                 QStringList replacement;
       
  1174                 if (var_type == ENVIRON) {
       
  1175                     replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
       
  1176                 } else if (var_type == PROPERTY) {
       
  1177                     replacement << propertyValue(var);
       
  1178                 } else if (var_type == FUNCTION) {
       
  1179                     replacement << evaluateExpandFunction(var, args);
       
  1180                 } else if (var_type == VAR) {
       
  1181                     replacement = values(var);
       
  1182                 }
       
  1183                 if (!(replaced++) && start_var)
       
  1184                     current = str.left(start_var);
       
  1185                 if (!replacement.isEmpty()) {
       
  1186                     if (quote) {
       
  1187                         current += replacement.join(QString(Option::field_sep));
       
  1188                     } else {
       
  1189                         current += replacement.takeFirst();
       
  1190                         if (!replacement.isEmpty()) {
       
  1191                             if (!current.isEmpty())
       
  1192                                 ret.append(current);
       
  1193                             current = replacement.takeLast();
       
  1194                             if (!replacement.isEmpty())
       
  1195                                 ret += replacement;
       
  1196                         }
       
  1197                     }
       
  1198                 }
       
  1199             } else {
       
  1200                 if (replaced)
       
  1201                     current.append(QLatin1Char('$'));
       
  1202             }
       
  1203         }
       
  1204         if (quote && unicode == quote) {
       
  1205             unicode = 0;
       
  1206             quote = 0;
       
  1207         } else if (unicode == BACKSLASH) {
       
  1208             bool escape = false;
       
  1209             const char *symbols = "[]{}()$\\'\"";
       
  1210             for (const char *s = symbols; *s; ++s) {
       
  1211                 if (str_data[i+1].unicode() == (ushort)*s) {
       
  1212                     i++;
       
  1213                     escape = true;
       
  1214                     if (!(replaced++))
       
  1215                         current = str.left(start_var);
       
  1216                     current.append(str.at(i));
       
  1217                     break;
       
  1218                 }
       
  1219             }
       
  1220             if (escape || !replaced)
       
  1221                 unicode =0;
       
  1222         } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
       
  1223             quote = unicode;
       
  1224             unicode = 0;
       
  1225             if (!(replaced++) && i)
       
  1226                 current = str.left(i);
       
  1227         } else if (!quote && (unicode == SPACE || unicode == TAB)) {
       
  1228             unicode = 0;
       
  1229             if (!current.isEmpty()) {
       
  1230                 ret.append(current);
       
  1231                 current.clear();
       
  1232             }
       
  1233         }
       
  1234         if (replaced && unicode)
       
  1235             current.append(QChar(unicode));
       
  1236     }
       
  1237     if (!replaced)
       
  1238         ret = QStringList(str);
       
  1239     else if (!current.isEmpty())
       
  1240         ret.append(current);
       
  1241     return ret;
       
  1242 }
       
  1243 
       
  1244 bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
       
  1245 {
       
  1246     // magic types for easy flipping
       
  1247     if (config == QLatin1String("true"))
       
  1248         return true;
       
  1249     if (config == QLatin1String("false"))
       
  1250         return false;
       
  1251 
       
  1252     // mkspecs
       
  1253     if ((Option::target_mode == Option::TARG_MACX_MODE
       
  1254             || Option::target_mode == Option::TARG_QNX6_MODE
       
  1255             || Option::target_mode == Option::TARG_UNIX_MODE)
       
  1256           && config == QLatin1String("unix"))
       
  1257         return true;
       
  1258     if (Option::target_mode == Option::TARG_MACX_MODE && config == QLatin1String("macx"))
       
  1259         return true;
       
  1260     if (Option::target_mode == Option::TARG_QNX6_MODE && config == QLatin1String("qnx6"))
       
  1261         return true;
       
  1262     if (Option::target_mode == Option::TARG_MAC9_MODE && config == QLatin1String("mac9"))
       
  1263         return true;
       
  1264     if ((Option::target_mode == Option::TARG_MAC9_MODE
       
  1265             || Option::target_mode == Option::TARG_MACX_MODE)
       
  1266           && config == QLatin1String("mac"))
       
  1267         return true;
       
  1268     if (Option::target_mode == Option::TARG_WIN_MODE && config == QLatin1String("win32"))
       
  1269         return true;
       
  1270 
       
  1271     QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
       
  1272     QString spec = Option::qmakespec;
       
  1273     if ((regex && re.exactMatch(spec)) || (!regex && spec == config))
       
  1274         return true;
       
  1275 
       
  1276     return false;
       
  1277 }
       
  1278 
       
  1279 QStringList 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 
       
  1315 QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
       
  1316 {
       
  1317     QStringList argumentsList = split_arg_list(arguments);
       
  1318 
       
  1319     if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
       
  1320         return evaluateFunction(funcPtr, argumentsList, 0);
       
  1321 
       
  1322     QStringList args;
       
  1323     for (int i = 0; i < argumentsList.count(); ++i)
       
  1324         args += expandVariableReferences(argumentsList[i]).join(Option::field_sep);
       
  1325 
       
  1326     enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
       
  1327                       E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
       
  1328                       E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
       
  1329                       E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
       
  1330                       E_REPLACE };
       
  1331 
       
  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()));
       
  1360 
       
  1361     QStringList ret;
       
  1362 
       
  1363     switch (func_t) {
       
  1364         case E_BASENAME:
       
  1365         case E_DIRNAME:
       
  1366         case E_SECTION: {
       
  1367             bool regexp = false;
       
  1368             QString sep, var;
       
  1369             int beg = 0;
       
  1370             int end = -1;
       
  1371             if (func_t == E_SECTION) {
       
  1372                 if (args.count() != 3 && args.count() != 4) {
       
  1373                     q->logMessage(format("%1(var) section(var, sep, begin, end) "
       
  1374                         "requires three or four arguments.").arg(func));
       
  1375                 } else {
       
  1376                     var = args[0];
       
  1377                     sep = args[1];
       
  1378                     beg = args[2].toInt();
       
  1379                     if (args.count() == 4)
       
  1380                         end = args[3].toInt();
       
  1381                 }
       
  1382             } else {
       
  1383                 if (args.count() != 1) {
       
  1384                     q->logMessage(format("%1(var) requires one argument.").arg(func));
       
  1385                 } else {
       
  1386                     var = args[0];
       
  1387                     regexp = true;
       
  1388                     sep = QLatin1String("[\\\\/]");
       
  1389                     if (func_t == E_DIRNAME)
       
  1390                         end = -2;
       
  1391                     else
       
  1392                         beg = -1;
       
  1393                 }
       
  1394             }
       
  1395             if (!var.isNull()) {
       
  1396                 foreach (const QString str, values(var)) {
       
  1397                     if (regexp)
       
  1398                         ret += str.section(QRegExp(sep), beg, end);
       
  1399                     else
       
  1400                         ret += str.section(sep, beg, end);
       
  1401                 }
       
  1402             }
       
  1403             break;
       
  1404         }
       
  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;
       
  1415         case E_JOIN: {
       
  1416             if (args.count() < 1 || args.count() > 4) {
       
  1417                 q->logMessage(format("join(var, glue, before, after) requires one to four arguments."));
       
  1418             } else {
       
  1419                 QString glue, before, after;
       
  1420                 if (args.count() >= 2)
       
  1421                     glue = args[1];
       
  1422                 if (args.count() >= 3)
       
  1423                     before = args[2];
       
  1424                 if (args.count() == 4)
       
  1425                     after = args[3];
       
  1426                 const QStringList &var = values(args.first());
       
  1427                 if (!var.isEmpty())
       
  1428                     ret.append(before + var.join(glue) + after);
       
  1429             }
       
  1430             break;
       
  1431         }
       
  1432         case E_SPLIT: {
       
  1433             if (args.count() != 2) {
       
  1434                 q->logMessage(format("split(var, sep) requires one or two arguments"));
       
  1435             } else {
       
  1436                 const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep);
       
  1437                 foreach (const QString &var, values(args.first()))
       
  1438                     foreach (const QString &splt, var.split(sep))
       
  1439                         ret.append(splt);
       
  1440             }
       
  1441             break;
       
  1442         }
       
  1443         case E_MEMBER: {
       
  1444             if (args.count() < 1 || args.count() > 3) {
       
  1445                 q->logMessage(format("member(var, start, end) requires one to three arguments."));
       
  1446             } else {
       
  1447                 bool ok = true;
       
  1448                 const QStringList var = values(args.first());
       
  1449                 int start = 0, end = 0;
       
  1450                 if (args.count() >= 2) {
       
  1451                     QString start_str = args[1];
       
  1452                     start = start_str.toInt(&ok);
       
  1453                     if (!ok) {
       
  1454                         if (args.count() == 2) {
       
  1455                             int dotdot = start_str.indexOf(QLatin1String(".."));
       
  1456                             if (dotdot != -1) {
       
  1457                                 start = start_str.left(dotdot).toInt(&ok);
       
  1458                                 if (ok)
       
  1459                                     end = start_str.mid(dotdot+2).toInt(&ok);
       
  1460                             }
       
  1461                         }
       
  1462                         if (!ok)
       
  1463                             q->logMessage(format("member() argument 2 (start) '%2' invalid.")
       
  1464                                 .arg(start_str));
       
  1465                     } else {
       
  1466                         end = start;
       
  1467                         if (args.count() == 3)
       
  1468                             end = args[2].toInt(&ok);
       
  1469                         if (!ok)
       
  1470                             q->logMessage(format("member() argument 3 (end) '%2' invalid.\n")
       
  1471                                 .arg(args[2]));
       
  1472                     }
       
  1473                 }
       
  1474                 if (ok) {
       
  1475                     if (start < 0)
       
  1476                         start += var.count();
       
  1477                     if (end < 0)
       
  1478                         end += var.count();
       
  1479                     if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
       
  1480                         //nothing
       
  1481                     } else if (start < end) {
       
  1482                         for (int i = start; i <= end && var.count() >= i; i++)
       
  1483                             ret.append(var[i]);
       
  1484                     } else {
       
  1485                         for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
       
  1486                             ret += var[i];
       
  1487                     }
       
  1488                 }
       
  1489             }
       
  1490             break;
       
  1491         }
       
  1492         case E_FIRST:
       
  1493         case E_LAST: {
       
  1494             if (args.count() != 1) {
       
  1495                 q->logMessage(format("%1(var) requires one argument.").arg(func));
       
  1496             } else {
       
  1497                 const QStringList var = values(args.first());
       
  1498                 if (!var.isEmpty()) {
       
  1499                     if (func_t == E_FIRST)
       
  1500                         ret.append(var[0]);
       
  1501                     else
       
  1502                         ret.append(var.last());
       
  1503                 }
       
  1504             }
       
  1505             break;
       
  1506         }
       
  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) {
       
  1583                 if (args.count() < 1 || args.count() > 2) {
       
  1584                     q->logMessage(format("system(execute) requires one or two arguments."));
       
  1585                 } else {
       
  1586                     char buff[256];
       
  1587                     FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
       
  1588                     bool singleLine = true;
       
  1589                     if (args.count() > 1)
       
  1590                         singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
       
  1591                     QString output;
       
  1592                     while (proc && !feof(proc)) {
       
  1593                         int read_in = int(fread(buff, 1, 255, proc));
       
  1594                         if (!read_in)
       
  1595                             break;
       
  1596                         for (int i = 0; i < read_in; i++) {
       
  1597                             if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
       
  1598                                 buff[i] = ' ';
       
  1599                         }
       
  1600                         buff[read_in] = '\0';
       
  1601                         output += QLatin1String(buff);
       
  1602                     }
       
  1603                     ret += split_value_list(output);
       
  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;
       
  1618         case E_QUOTE:
       
  1619             for (int i = 0; i < args.count(); ++i)
       
  1620                 ret += QStringList(args.at(i));
       
  1621             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;
       
  1714         case 0:
       
  1715             q->logMessage(format("'%1' is not a recognized replace function").arg(func));
       
  1716             break;
       
  1717         default:
       
  1718             q->logMessage(format("Function '%1' is not implemented").arg(func));
       
  1719             break;
       
  1720     }
       
  1721 
       
  1722     return ret;
       
  1723 }
       
  1724 
       
  1725 ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
       
  1726         const QString &function, const QString &arguments)
       
  1727 {
       
  1728     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 
       
  1757     QString sep;
       
  1758     sep.append(Option::field_sep);
       
  1759     QStringList args;
       
  1760     for (int i = 0; i < argumentsList.count(); ++i)
       
  1761         args += expandVariableReferences(argumentsList[i]).join(sep);
       
  1762 
       
  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);
       
  1805 
       
  1806     switch (func_t) {
       
  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: {
       
  2019             if (args.count() < 1 || args.count() > 2) {
       
  2020                 q->logMessage(format("CONFIG(config) requires one or two arguments."));
       
  2021                 return ProItem::ReturnFalse;
       
  2022             }
       
  2023             if (args.count() == 1) {
       
  2024                 //cond = isActiveConfig(args.first()); XXX
       
  2025                 return ProItem::ReturnFalse;
       
  2026             }
       
  2027             const QStringList mutuals = args[1].split(QLatin1Char('|'));
       
  2028             const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
       
  2029             for (int i = configs.size() - 1; i >= 0; i--) {
       
  2030                 for (int mut = 0; mut < mutuals.count(); mut++) {
       
  2031                     if (configs[i] == mutuals[mut].trimmed()) {
       
  2032                         return returnBool(configs[i] == args[0]);
       
  2033                     }
       
  2034                 }
       
  2035             }
       
  2036             return ProItem::ReturnFalse;
       
  2037         }
       
  2038         case T_CONTAINS: {
       
  2039             if (args.count() < 2 || args.count() > 3) {
       
  2040                 q->logMessage(format("contains(var, val) requires two or three arguments."));
       
  2041                 return ProItem::ReturnFalse;
       
  2042             }
       
  2043 
       
  2044             QRegExp regx(args[1]);
       
  2045             const QStringList &l = values(args.first());
       
  2046             if (args.count() == 2) {
       
  2047                 for (int i = 0; i < l.size(); ++i) {
       
  2048                     const QString val = l[i];
       
  2049                     if (regx.exactMatch(val) || val == args[1]) {
       
  2050                         return ProItem::ReturnTrue;
       
  2051                     }
       
  2052                 }
       
  2053             } else {
       
  2054                 const QStringList mutuals = args[2].split(QLatin1Char('|'));
       
  2055                 for (int i = l.size() - 1; i >= 0; i--) {
       
  2056                     const QString val = l[i];
       
  2057                     for (int mut = 0; mut < mutuals.count(); mut++) {
       
  2058                         if (val == mutuals[mut].trimmed()) {
       
  2059                             return returnBool(regx.exactMatch(val) || val == args[1]);
       
  2060                         }
       
  2061                     }
       
  2062                 }
       
  2063             }
       
  2064             return ProItem::ReturnFalse;
       
  2065         }
       
  2066         case T_COUNT: {
       
  2067             if (args.count() != 2 && args.count() != 3) {
       
  2068                 q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
       
  2069                 return ProItem::ReturnFalse;
       
  2070             }
       
  2071             if (args.count() == 3) {
       
  2072                 QString comp = args[2];
       
  2073                 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
       
  2074                     return returnBool(values(args.first()).count() > args[1].toInt());
       
  2075                 } else if (comp == QLatin1String(">=")) {
       
  2076                     return returnBool(values(args.first()).count() >= args[1].toInt());
       
  2077                 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
       
  2078                     return returnBool(values(args.first()).count() < args[1].toInt());
       
  2079                 } else if (comp == QLatin1String("<=")) {
       
  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());
       
  2084                 } else {
       
  2085                     q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
       
  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;
       
  2147             QString parseInto;
       
  2148             // the third optional argument to include() controls warnings
       
  2149             //      and is not used here
       
  2150             if ((args.count() == 2) || (args.count() == 3)) {
       
  2151                 parseInto = args[1];
       
  2152             } else if (args.count() != 1) {
       
  2153                 q->logMessage(format("include(file) requires one, two or three arguments."));
       
  2154                 return ProItem::ReturnFalse;
       
  2155             }
       
  2156             QString fileName = args.first();
       
  2157             // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
       
  2158             QDir currentProPath(currentDirectory());
       
  2159             fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
       
  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;
       
  2168             QString parseInto;
       
  2169             bool ignore_error = false;
       
  2170             if (args.count() == 2) {
       
  2171                 QString sarg = args[1];
       
  2172                 ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
       
  2173             } else if (args.count() != 1) {
       
  2174                 q->logMessage(format("load(feature) requires one or two arguments."));
       
  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: {
       
  2184             if (args.count() != 1) {
       
  2185                 q->logMessage(format("%1(message) requires one argument.").arg(function));
       
  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: {
       
  2195             if (args.count() != 1) {
       
  2196                 q->logMessage(format("system(exec) requires one argument."));
       
  2197                 ProItem::ReturnFalse;
       
  2198             }
       
  2199             return returnBool(system(args.first().toLatin1().constData()) == 0);
       
  2200         }
       
  2201 #endif
       
  2202         case T_ISEMPTY: {
       
  2203             if (args.count() != 1) {
       
  2204                 q->logMessage(format("isEmpty(var) requires one argument."));
       
  2205                 return ProItem::ReturnFalse;
       
  2206             }
       
  2207             QStringList sl = values(args.first());
       
  2208             if (sl.count() == 0) {
       
  2209                 return ProItem::ReturnTrue;
       
  2210             } else if (sl.count() > 0) {
       
  2211                 QString var = sl.first();
       
  2212                 if (var.isEmpty())
       
  2213                     return ProItem::ReturnTrue;
       
  2214             }
       
  2215             return ProItem::ReturnFalse;
       
  2216         }
       
  2217         case T_EXISTS: {
       
  2218             if (args.count() != 1) {
       
  2219                 q->logMessage(format("exists(file) requires one argument."));
       
  2220                 return ProItem::ReturnFalse;
       
  2221             }
       
  2222             QString file = args.first();
       
  2223             file = Option::fixPathToLocalOS(file);
       
  2224 
       
  2225             if (QFile::exists(file)) {
       
  2226                 return ProItem::ReturnTrue;
       
  2227             }
       
  2228             //regular expression I guess
       
  2229             QString dirstr = currentDirectory();
       
  2230             int slsh = file.lastIndexOf(Option::dir_sep);
       
  2231             if (slsh != -1) {
       
  2232                 dirstr = file.left(slsh+1);
       
  2233                 file = file.right(file.length() - slsh - 1);
       
  2234             }
       
  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 
       
  2250 QStringList 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;
       
  2365 }
       
  2366 
       
  2367 QStringList ProFileEvaluator::Private::values(const QString &variableName) const
       
  2368 {
       
  2369     return values(variableName, m_valuemap, currentProFile());
       
  2370 }
       
  2371 
       
  2372 QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
       
  2373 {
       
  2374     return values(variableName, m_filevaluemap[pro], pro);
       
  2375 }
       
  2376 
       
  2377 ProFile *ProFileEvaluator::parsedProFile(const QString &fileName)
       
  2378 {
       
  2379     QFileInfo fi(fileName);
       
  2380     if (fi.exists()) {
       
  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);
       
  2388         if (d->read(pro))
       
  2389             return pro;
       
  2390         delete pro;
       
  2391     }
       
  2392     return 0;
       
  2393 }
       
  2394 
       
  2395 void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
       
  2396 {
       
  2397     delete proFile;
       
  2398 }
       
  2399 
       
  2400 bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
       
  2401 {
       
  2402     ProFile *pro = q->parsedProFile(fileName);
       
  2403     if (pro) {
       
  2404         m_profileStack.push(pro);
       
  2405         bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
       
  2406         m_profileStack.pop();
       
  2407         q->releaseParsedProFile(pro);
       
  2408         return ok;
       
  2409     } else {
       
  2410         return false;
       
  2411     }
       
  2412 }
       
  2413 
       
  2414 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
       
  2415 {
       
  2416     QString fn;
       
  2417     foreach (const QString &path, qmakeFeaturePaths()) {
       
  2418         QString fname = path + QLatin1Char('/') + fileName;
       
  2419         if (QFileInfo(fname).exists()) {
       
  2420             fn = fname;
       
  2421             break;
       
  2422         }
       
  2423         fname += QLatin1String(".prf");
       
  2424         if (QFileInfo(fname).exists()) {
       
  2425             fn = fname;
       
  2426             break;
       
  2427         }
       
  2428     }
       
  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;
       
  2436 }
       
  2437 
       
  2438 QString ProFileEvaluator::Private::format(const char *fmt) const
       
  2439 {
       
  2440     ProFile *pro = currentProFile();
       
  2441     QString fileName = pro ? pro->fileName() : QLatin1String("Not a file");
       
  2442     int lineNumber = pro ? m_lineNo : 0;
       
  2443     return QString::fromLatin1("%1(%2):").arg(fileName).arg(lineNumber) + QString::fromAscii(fmt);
       
  2444 }
       
  2445 
       
  2446 
       
  2447 ///////////////////////////////////////////////////////////////////////
       
  2448 //
       
  2449 // ProFileEvaluator
       
  2450 //
       
  2451 ///////////////////////////////////////////////////////////////////////
       
  2452 
       
  2453 ProFileEvaluator::ProFileEvaluator()
       
  2454   : d(new Private(this))
       
  2455 {
       
  2456     Option::init();
       
  2457 }
       
  2458 
       
  2459 ProFileEvaluator::~ProFileEvaluator()
       
  2460 {
       
  2461     delete d;
       
  2462 }
       
  2463 
       
  2464 bool ProFileEvaluator::contains(const QString &variableName) const
       
  2465 {
       
  2466     return d->m_valuemap.contains(variableName);
       
  2467 }
       
  2468 
       
  2469 inline 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 
       
  2478 QStringList ProFileEvaluator::values(const QString &variableName) const
       
  2479 {
       
  2480     return fixEnvVariables(d->values(variableName));
       
  2481 }
       
  2482 
       
  2483 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
       
  2484 {
       
  2485     return fixEnvVariables(d->values(variableName, pro));
       
  2486 }
       
  2487 
       
  2488 QStringList 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 
       
  2500 QStringList 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;
       
  2539 }
       
  2540 
       
  2541 ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
       
  2542 {
       
  2543     QStringList templ = values(QLatin1String("TEMPLATE"));
       
  2544     if (templ.count() >= 1) {
       
  2545         const QString &t = templ.last();
       
  2546         if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
       
  2547             return TT_Application;
       
  2548         if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
       
  2549             return TT_Library;
       
  2550         if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
       
  2551             return TT_Script;
       
  2552         if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
       
  2553             return TT_Subdirs;
       
  2554     }
       
  2555     return TT_Unknown;
       
  2556 }
       
  2557 
       
  2558 bool ProFileEvaluator::queryProFile(ProFile *pro)
       
  2559 {
       
  2560     return d->read(pro);
       
  2561 }
       
  2562 
       
  2563 bool ProFileEvaluator::accept(ProFile *pro)
       
  2564 {
       
  2565     return pro->Accept(d);
       
  2566 }
       
  2567 
       
  2568 QString ProFileEvaluator::propertyValue(const QString &name) const
       
  2569 {
       
  2570     return d->propertyValue(name);
       
  2571 }
       
  2572 
       
  2573 namespace {
       
  2574     template<class K, class T> void insert(QHash<K,T> *out, const QHash<K,T> &in)
       
  2575     {
       
  2576         typename QHash<K,T>::const_iterator i = in.begin();
       
  2577         while (i != in.end()) {
       
  2578             out->insert(i.key(), i.value());
       
  2579             ++i;
       
  2580         }
       
  2581     }
       
  2582 } // anon namespace
       
  2583 
       
  2584 void ProFileEvaluator::addVariables(const QHash<QString, QStringList> &variables)
       
  2585 {
       
  2586     insert(&(d->m_valuemap), variables);
       
  2587 }
       
  2588 
       
  2589 void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties)
       
  2590 {
       
  2591     insert(&(d->m_properties), properties);
       
  2592 }
       
  2593 
       
  2594 void ProFileEvaluator::logMessage(const QString &message)
       
  2595 {
       
  2596     if (d->m_verbose && !d->m_skipLevel)
       
  2597         qWarning("%s", qPrintable(message));
       
  2598 }
       
  2599 
       
  2600 void ProFileEvaluator::fileMessage(const QString &message)
       
  2601 {
       
  2602     if (!d->m_skipLevel)
       
  2603         qWarning("%s", qPrintable(message));
       
  2604 }
       
  2605 
       
  2606 void ProFileEvaluator::errorMessage(const QString &message)
       
  2607 {
       
  2608     if (!d->m_skipLevel)
       
  2609         qWarning("%s", qPrintable(message));
       
  2610 }
       
  2611 
       
  2612 void ProFileEvaluator::setVerbose(bool on)
       
  2613 {
       
  2614     d->m_verbose = on;
       
  2615 }
       
  2616 
       
  2617 void ProFileEvaluator::setCumulative(bool on)
       
  2618 {
       
  2619     d->m_cumulative = on;
       
  2620 }
       
  2621 
       
  2622 void ProFileEvaluator::setOutputDir(const QString &dir)
       
  2623 {
       
  2624     d->m_outputDir = dir;
       
  2625 }
       
  2626 
       
  2627 QT_END_NAMESPACE