OSDN Git Service

whitespace-trim test names inside if()
[qt-creator-jp/qt-creator-jp.git] / src / shared / proparser / profileevaluator.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "profileevaluator.h"
31
32 #include "profileparser.h"
33 #include "ioutils.h"
34
35 #include <QtCore/QByteArray>
36 #include <QtCore/QDateTime>
37 #include <QtCore/QDebug>
38 #include <QtCore/QDir>
39 #include <QtCore/QFile>
40 #include <QtCore/QFileInfo>
41 #include <QtCore/QList>
42 #include <QtCore/QRegExp>
43 #include <QtCore/QSet>
44 #include <QtCore/QStack>
45 #include <QtCore/QString>
46 #include <QtCore/QStringList>
47 #include <QtCore/QTextStream>
48 #ifdef PROEVALUATOR_THREAD_SAFE
49 # include <QtCore/QThreadPool>
50 #endif
51
52 #ifdef Q_OS_UNIX
53 #include <unistd.h>
54 #include <sys/utsname.h>
55 #else
56 #include <Windows.h>
57 #endif
58 #include <stdio.h>
59 #include <stdlib.h>
60
61 #ifdef Q_OS_WIN32
62 #define QT_POPEN _popen
63 #define QT_PCLOSE _pclose
64 #else
65 #define QT_POPEN popen
66 #define QT_PCLOSE pclose
67 #endif
68
69 using namespace ProFileEvaluatorInternal;
70
71 QT_BEGIN_NAMESPACE
72
73 using namespace ProStringConstants;
74
75
76 ///////////////////////////////////////////////////////////////////////
77 //
78 // ProFileOption
79 //
80 ///////////////////////////////////////////////////////////////////////
81
82 ProFileOption::ProFileOption()
83 {
84 #ifdef Q_OS_WIN
85     dirlist_sep = QLatin1Char(';');
86     dir_sep = QLatin1Char('\\');
87 #else
88     dirlist_sep = QLatin1Char(':');
89     dir_sep = QLatin1Char('/');
90 #endif
91     qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").data());
92
93 #if defined(Q_OS_WIN32)
94     target_mode = TARG_WIN_MODE;
95 #elif defined(Q_OS_MAC)
96     target_mode = TARG_MACX_MODE;
97 #elif defined(Q_OS_QNX6)
98     target_mode = TARG_QNX6_MODE;
99 #else
100     target_mode = TARG_UNIX_MODE;
101 #endif
102
103 #ifdef PROEVALUATOR_THREAD_SAFE
104     base_inProgress = false;
105 #endif
106 }
107
108 ProFileOption::~ProFileOption()
109 {
110 }
111
112 ///////////////////////////////////////////////////////////////////////
113 //
114 // ProFileEvaluator::Private
115 //
116 ///////////////////////////////////////////////////////////////////////
117
118 #define fL1S(s) QString::fromLatin1(s)
119
120 class ProFileEvaluator::Private
121 {
122 public:
123     static void initStatics();
124     Private(ProFileEvaluator *q_, ProFileOption *option, ProFileParser *parser,
125             ProFileEvaluatorHandler *handler);
126     ~Private();
127
128     ProFileEvaluator *q;
129
130     enum VisitReturn {
131         ReturnFalse,
132         ReturnTrue,
133         ReturnBreak,
134         ReturnNext,
135         ReturnReturn
136     };
137
138     static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
139     ProString getStr(const ushort *&tokPtr);
140     ProString getHashStr(const ushort *&tokPtr);
141     void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
142     static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
143     static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
144     void skipExpression(const ushort *&tokPtr);
145
146     VisitReturn visitProFile(ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
147                              ProFileEvaluator::LoadFlags flags);
148     VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
149     VisitReturn visitProBlock(const ushort *tokPtr);
150     VisitReturn visitProLoop(const ProString &variable, const ushort *exprPtr,
151                              const ushort *tokPtr);
152     void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr);
153     void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
154
155     static inline const ProString &map(const ProString &var);
156     QHash<ProString, ProStringList> *findValues(const ProString &variableName,
157                                                 QHash<ProString, ProStringList>::Iterator *it);
158     ProStringList &valuesRef(const ProString &variableName);
159     ProStringList valuesDirect(const ProString &variableName) const;
160     ProStringList values(const ProString &variableName) const;
161     QString propertyValue(const QString &val, bool complain = true) const;
162
163     static ProStringList split_value_list(const QString &vals);
164     bool isActiveConfig(const QString &config, bool regex = false);
165     ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false);
166     ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false);
167     ProStringList evaluateExpandFunction(const ProString &function, const ProString &arguments);
168     ProStringList evaluateExpandFunction(const ProString &function, const ushort *&tokPtr);
169     ProStringList evaluateExpandFunction(const ProString &function, const ProStringList &args);
170     void evalError(const QString &msg) const;
171
172     QString currentFileName() const;
173     QString currentDirectory() const;
174     ProFile *currentProFile() const;
175     QString resolvePath(const QString &fileName) const
176         { return IoUtils::resolvePath(currentDirectory(), fileName); }
177
178     VisitReturn evaluateConditionalFunction(const ProString &function, const ProString &arguments);
179     VisitReturn evaluateConditionalFunction(const ProString &function, const ushort *&tokPtr);
180     VisitReturn evaluateConditionalFunction(const ProString &function, const ProStringList &args);
181     bool evaluateFileDirect(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
182                             ProFileEvaluator::LoadFlags flags);
183     bool evaluateFile(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
184                       ProFileEvaluator::LoadFlags flags);
185     bool evaluateFeatureFile(const QString &fileName);
186     enum EvalIntoMode { EvalProOnly, EvalWithDefaults, EvalWithSetup };
187     bool evaluateFileInto(const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
188                           QHash<ProString, ProStringList> *values, FunctionDefs *defs,
189                           EvalIntoMode mode); // values are output-only, defs are input-only
190
191     static ALWAYS_INLINE VisitReturn returnBool(bool b)
192         { return b ? ReturnTrue : ReturnFalse; }
193
194     QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr);
195     QList<ProStringList> prepareFunctionArgs(const ProString &arguments);
196     ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok);
197     VisitReturn evaluateBoolFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList,
198                                      const ProString &function);
199
200     QStringList qmakeMkspecPaths() const;
201     QStringList qmakeFeaturePaths() const;
202
203     int m_skipLevel;
204     int m_loopLevel; // To report unexpected break() and next()s
205     bool m_cumulative;
206
207     struct Location {
208         Location() : pro(0), line(0) {}
209         Location(ProFile *_pro, int _line) : pro(_pro), line(_line) {}
210         ProFile *pro;
211         int line;
212     };
213
214     Location m_current; // Currently evaluated location
215     QStack<Location> m_locationStack; // All execution location changes
216     QStack<ProFile *> m_profileStack; // Includes only
217
218     QString m_outputDir;
219
220     int m_listCount;
221     FunctionDefs m_functionDefs;
222     ProStringList m_returnValue;
223     QStack<QHash<ProString, ProStringList> > m_valuemapStack;         // VariableName must be us-ascii, the content however can be non-us-ascii.
224     QHash<const ProFile*, QHash<ProString, ProStringList> > m_filevaluemap; // Variables per include file
225     QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
226
227     QStringList m_addUserConfigCmdArgs;
228     QStringList m_removeUserConfigCmdArgs;
229
230     ProFileOption *m_option;
231     ProFileParser *m_parser;
232     ProFileEvaluatorHandler *m_handler;
233
234     enum ExpandFunc {
235         E_MEMBER=1, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
236         E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
237         E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
238         E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
239         E_REPLACE
240     };
241
242     enum TestFunc {
243         T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
244         T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
245         T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
246         T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF
247     };
248
249     enum VarName {
250         V_LITERAL_DOLLAR, V_LITERAL_HASH, V_LITERAL_WHITESPACE,
251         V_DIRLIST_SEPARATOR, V_DIR_SEPARATOR,
252         V_OUT_PWD, V_PWD, V_IN_PWD,
253         V__FILE_, V__LINE_, V__PRO_FILE_, V__PRO_FILE_PWD_,
254         V_QMAKE_HOST_arch, V_QMAKE_HOST_name, V_QMAKE_HOST_os,
255         V_QMAKE_HOST_version, V_QMAKE_HOST_version_string,
256         V__DATE_, V__QMAKE_CACHE_
257     };
258 };
259
260 static struct {
261     QString field_sep;
262     QString strtrue;
263     QString strfalse;
264     QString strunix;
265     QString strmacx;
266     QString strqnx6;
267     QString strmac9;
268     QString strmac;
269     QString strwin32;
270     ProString strCONFIG;
271     ProString strARGS;
272     QString strDot;
273     QString strDotDot;
274     QString strever;
275     QString strforever;
276     ProString strTEMPLATE;
277     ProString strQMAKE_DIR_SEP;
278     QHash<ProString, int> expands;
279     QHash<ProString, int> functions;
280     QHash<ProString, int> varList;
281     QHash<ProString, ProString> varMap;
282     QRegExp reg_variableName;
283     ProStringList fakeValue;
284 } statics;
285
286 void ProFileEvaluator::Private::initStatics()
287 {
288     if (!statics.field_sep.isNull())
289         return;
290
291     statics.field_sep = QLatin1String(" ");
292     statics.strtrue = QLatin1String("true");
293     statics.strfalse = QLatin1String("false");
294     statics.strunix = QLatin1String("unix");
295     statics.strmacx = QLatin1String("macx");
296     statics.strqnx6 = QLatin1String("qnx6");
297     statics.strmac9 = QLatin1String("mac9");
298     statics.strmac = QLatin1String("mac");
299     statics.strwin32 = QLatin1String("win32");
300     statics.strCONFIG = ProString("CONFIG");
301     statics.strARGS = ProString("ARGS");
302     statics.strDot = QLatin1String(".");
303     statics.strDotDot = QLatin1String("..");
304     statics.strever = QLatin1String("ever");
305     statics.strforever = QLatin1String("forever");
306     statics.strTEMPLATE = ProString("TEMPLATE");
307     statics.strQMAKE_DIR_SEP = ProString("QMAKE_DIR_SEP");
308
309     statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)"));
310     statics.reg_variableName.setMinimal(true);
311
312     statics.fakeValue.detach(); // It has to have a unique begin() value
313
314     static const struct {
315         const char * const name;
316         const ExpandFunc func;
317     } expandInits[] = {
318         { "member", E_MEMBER },
319         { "first", E_FIRST },
320         { "last", E_LAST },
321         { "size", E_SIZE },
322         { "cat", E_CAT },
323         { "fromfile", E_FROMFILE },
324         { "eval", E_EVAL },
325         { "list", E_LIST },
326         { "sprintf", E_SPRINTF },
327         { "join", E_JOIN },
328         { "split", E_SPLIT },
329         { "basename", E_BASENAME },
330         { "dirname", E_DIRNAME },
331         { "section", E_SECTION },
332         { "find", E_FIND },
333         { "system", E_SYSTEM },
334         { "unique", E_UNIQUE },
335         { "quote", E_QUOTE },
336         { "escape_expand", E_ESCAPE_EXPAND },
337         { "upper", E_UPPER },
338         { "lower", E_LOWER },
339         { "re_escape", E_RE_ESCAPE },
340         { "files", E_FILES },
341         { "prompt", E_PROMPT }, // interactive, so cannot be implemented
342         { "replace", E_REPLACE }
343     };
344     for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
345         statics.expands.insert(ProString(expandInits[i].name), expandInits[i].func);
346
347     static const struct {
348         const char * const name;
349         const TestFunc func;
350     } testInits[] = {
351         { "requires", T_REQUIRES },
352         { "greaterThan", T_GREATERTHAN },
353         { "lessThan", T_LESSTHAN },
354         { "equals", T_EQUALS },
355         { "isEqual", T_EQUALS },
356         { "exists", T_EXISTS },
357         { "export", T_EXPORT },
358         { "clear", T_CLEAR },
359         { "unset", T_UNSET },
360         { "eval", T_EVAL },
361         { "CONFIG", T_CONFIG },
362         { "if", T_IF },
363         { "isActiveConfig", T_CONFIG },
364         { "system", T_SYSTEM },
365         { "return", T_RETURN },
366         { "break", T_BREAK },
367         { "next", T_NEXT },
368         { "defined", T_DEFINED },
369         { "contains", T_CONTAINS },
370         { "infile", T_INFILE },
371         { "count", T_COUNT },
372         { "isEmpty", T_ISEMPTY },
373         { "load", T_LOAD },
374         { "include", T_INCLUDE },
375         { "debug", T_DEBUG },
376         { "message", T_MESSAGE },
377         { "warning", T_MESSAGE },
378         { "error", T_MESSAGE },
379     };
380     for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i)
381         statics.functions.insert(ProString(testInits[i].name), testInits[i].func);
382
383     static const char * const names[] = {
384         "LITERAL_DOLLAR", "LITERAL_HASH", "LITERAL_WHITESPACE",
385         "DIRLIST_SEPARATOR", "DIR_SEPARATOR",
386         "OUT_PWD", "PWD", "IN_PWD",
387         "_FILE_", "_LINE_", "_PRO_FILE_", "_PRO_FILE_PWD_",
388         "QMAKE_HOST.arch", "QMAKE_HOST.name", "QMAKE_HOST.os",
389         "QMAKE_HOST.version", "QMAKE_HOST.version_string",
390         "_DATE_", "_QMAKE_CACHE_"
391     };
392     for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
393         statics.varList.insert(ProString(names[i]), i);
394
395     static const struct {
396         const char * const oldname, * const newname;
397     } mapInits[] = {
398         { "INTERFACES", "FORMS" },
399         { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" },
400         { "TARGETDEPS", "POST_TARGETDEPS" },
401         { "LIBPATH", "QMAKE_LIBDIR" },
402         { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" },
403         { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" },
404         { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" },
405         { "PRECOMPH", "PRECOMPILED_HEADER" },
406         { "PRECOMPCPP", "PRECOMPILED_SOURCE" },
407         { "INCPATH", "INCLUDEPATH" },
408         { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
409         { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" },
410         { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" },
411         { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" },
412         { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" },
413         { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" },
414         { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" },
415         { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" },
416         { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }
417     };
418     for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i)
419         statics.varMap.insert(ProString(mapInits[i].oldname),
420                               ProString(mapInits[i].newname));
421 }
422
423 const ProString &ProFileEvaluator::Private::map(const ProString &var)
424 {
425     QHash<ProString, ProString>::ConstIterator it = statics.varMap.constFind(var);
426     return (it != statics.varMap.constEnd()) ? it.value() : var;
427 }
428
429
430 ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option,
431                                    ProFileParser *parser, ProFileEvaluatorHandler *handler)
432   : q(q_), m_option(option), m_parser(parser), m_handler(handler)
433 {
434     // So that single-threaded apps don't have to call initialize() for now.
435     initStatics();
436
437     // Configuration, more or less
438     m_cumulative = true;
439
440     // Evaluator state
441     m_skipLevel = 0;
442     m_loopLevel = 0;
443     m_listCount = 0;
444     m_valuemapStack.push(QHash<ProString, ProStringList>());
445 }
446
447 ProFileEvaluator::Private::~Private()
448 {
449 }
450
451 //////// Evaluator tools /////////
452
453 uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr)
454 {
455     uint len = *tokPtr++;
456     len |= (uint)*tokPtr++ << 16;
457     return len;
458 }
459
460 ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr)
461 {
462     uint len = *tokPtr++;
463     ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, NoHash);
464     tokPtr += len;
465     return ret;
466 }
467
468 ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr)
469 {
470     uint hash = getBlockLen(tokPtr);
471     uint len = *tokPtr++;
472     ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash);
473     tokPtr += len;
474     return ret;
475 }
476
477 void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr)
478 {
479     uint len = *tokPtr++;
480     tokPtr += len;
481 }
482
483 void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr)
484 {
485     tokPtr += 2;
486     uint len = *tokPtr++;
487     tokPtr += len;
488 }
489
490 // FIXME: this should not build new strings for direct sections.
491 // Note that the E_SPRINTF and E_LIST implementations rely on the deep copy.
492 ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals)
493 {
494     QString build;
495     ProStringList ret;
496     QStack<char> quote;
497
498     const ushort SPACE = ' ';
499     const ushort LPAREN = '(';
500     const ushort RPAREN = ')';
501     const ushort SINGLEQUOTE = '\'';
502     const ushort DOUBLEQUOTE = '"';
503     const ushort BACKSLASH = '\\';
504
505     ushort unicode;
506     const QChar *vals_data = vals.data();
507     const int vals_len = vals.length();
508     for (int x = 0, parens = 0; x < vals_len; x++) {
509         unicode = vals_data[x].unicode();
510         if (x != (int)vals_len-1 && unicode == BACKSLASH &&
511             (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
512             build += vals_data[x++]; //get that 'escape'
513         } else if (!quote.isEmpty() && unicode == quote.top()) {
514             quote.pop();
515         } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
516             quote.push(unicode);
517         } else if (unicode == RPAREN) {
518             --parens;
519         } else if (unicode == LPAREN) {
520             ++parens;
521         }
522
523         if (!parens && quote.isEmpty() && vals_data[x] == SPACE) {
524             ret << ProString(build, NoHash);
525             build.clear();
526         } else {
527             build += vals_data[x];
528         }
529     }
530     if (!build.isEmpty())
531         ret << ProString(build, NoHash);
532     return ret;
533 }
534
535 static void zipEmpty(ProStringList *value)
536 {
537     for (int i = value->size(); --i >= 0;)
538         if (value->at(i).isEmpty())
539             value->remove(i);
540 }
541
542 static void insertUnique(ProStringList *varlist, const ProStringList &value)
543 {
544     foreach (const ProString &str, value)
545         if (!str.isEmpty() && !varlist->contains(str))
546             varlist->append(str);
547 }
548
549 static void removeAll(ProStringList *varlist, const ProString &value)
550 {
551     for (int i = varlist->size(); --i >= 0; )
552         if (varlist->at(i) == value)
553             varlist->remove(i);
554 }
555
556 static void removeEach(ProStringList *varlist, const ProStringList &value)
557 {
558     foreach (const ProString &str, value)
559         if (!str.isEmpty())
560             removeAll(varlist, str);
561 }
562
563 static void replaceInList(ProStringList *varlist,
564         const QRegExp &regexp, const QString &replace, bool global, QString &tmp)
565 {
566     for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
567         QString val = varit->toQString(tmp);
568         QString copy = val; // Force detach and have a reference value
569         val.replace(regexp, replace);
570         if (!val.isSharedWith(copy)) {
571             if (val.isEmpty()) {
572                 varit = varlist->erase(varit);
573             } else {
574                 *varit = ProString(val);
575                 ++varit;
576             }
577             if (!global)
578                 break;
579         } else {
580             ++varit;
581         }
582     }
583 }
584
585 static QString expandEnvVars(const QString &str)
586 {
587     QString string = str;
588     int rep;
589     QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety
590     while ((rep = reg_variableName.indexIn(string)) != -1)
591         string.replace(rep, reg_variableName.matchedLength(),
592                        QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
593     return string;
594 }
595
596 // This is braindead, but we want qmake compat
597 static QString fixPathToLocalOS(const QString &str)
598 {
599     QString string = expandEnvVars(str);
600
601     if (string.length() > 2 && string.at(0).isLetter() && string.at(1) == QLatin1Char(':'))
602         string[0] = string[0].toLower();
603
604 #if defined(Q_OS_WIN32)
605     string.replace(QLatin1Char('/'), QLatin1Char('\\'));
606 #else
607     string.replace(QLatin1Char('\\'), QLatin1Char('/'));
608 #endif
609     return string;
610 }
611
612 static bool isTrue(const ProString &_str, QString &tmp)
613 {
614     const QString &str = _str.toQString(tmp);
615     return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt();
616 }
617
618 //////// Evaluator /////////
619
620 static ALWAYS_INLINE void addStr(
621         const ProString &str, ProStringList *ret, bool &pending, bool joined)
622 {
623     if (joined) {
624         ret->last().append(str, &pending);
625     } else {
626         if (!pending) {
627             pending = true;
628             *ret << str;
629         } else {
630             ret->last().append(str);
631         }
632     }
633 }
634
635 static ALWAYS_INLINE void addStrList(
636         const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined)
637 {
638     if (!list.isEmpty()) {
639         if (joined) {
640             ret->last().append(list, &pending, !(tok & TokQuoted));
641         } else {
642             if (tok & TokQuoted) {
643                 if (!pending) {
644                     pending = true;
645                     *ret << ProString();
646                 }
647                 ret->last().append(list);
648             } else {
649                 if (!pending) {
650                     // Another qmake bizzarity: if nothing is pending and the
651                     // first element is empty, it will be eaten
652                     if (!list.at(0).isEmpty()) {
653                         // The common case
654                         pending = true;
655                         *ret += list;
656                         return;
657                     }
658                 } else {
659                     ret->last().append(list.at(0));
660                 }
661                 // This is somewhat slow, but a corner case
662                 for (int j = 1; j < list.size(); ++j) {
663                     pending = true;
664                     *ret << list.at(j);
665                 }
666             }
667         }
668     }
669 }
670
671 void ProFileEvaluator::Private::evaluateExpression(
672         const ushort *&tokPtr, ProStringList *ret, bool joined)
673 {
674     if (joined)
675         *ret << ProString();
676     bool pending = false;
677     forever {
678         ushort tok = *tokPtr++;
679         if (tok & TokNewStr)
680             pending = false;
681         ushort maskedTok = tok & TokMask;
682         switch (maskedTok) {
683         case TokLine:
684             m_current.line = *tokPtr++;
685             break;
686         case TokLiteral:
687             addStr(getStr(tokPtr), ret, pending, joined);
688             break;
689         case TokHashLiteral:
690             addStr(getHashStr(tokPtr), ret, pending, joined);
691             break;
692         case TokVariable:
693             addStrList(values(map(getHashStr(tokPtr))), tok, ret, pending, joined);
694             break;
695         case TokProperty:
696             addStr(ProString(propertyValue(
697                     getStr(tokPtr).toQString(m_tmp1)), NoHash), ret, pending, joined);
698             break;
699         case TokEnvVar:
700             addStrList(split_value_list(QString::fromLocal8Bit(qgetenv(
701                     getStr(tokPtr).toQString(m_tmp1).toLatin1().constData()))), tok, ret, pending, joined);
702             break;
703         case TokFuncName: {
704             ProString func = getHashStr(tokPtr);
705             addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined);
706             break; }
707         default:
708             tokPtr--;
709             return;
710         }
711     }
712 }
713
714 void ProFileEvaluator::Private::skipExpression(const ushort *&pTokPtr)
715 {
716     const ushort *tokPtr = pTokPtr;
717     forever {
718         ushort tok = *tokPtr++;
719         switch (tok) {
720         case TokLine:
721             m_current.line = *tokPtr++;
722             break;
723         case TokValueTerminator:
724         case TokFuncTerminator:
725             pTokPtr = tokPtr;
726             return;
727         case TokArgSeparator:
728             break;
729         default:
730             switch (tok & TokMask) {
731             case TokLiteral:
732             case TokProperty:
733             case TokEnvVar:
734                 skipStr(tokPtr);
735                 break;
736             case TokHashLiteral:
737             case TokVariable:
738                 skipHashStr(tokPtr);
739                 break;
740             case TokFuncName:
741                 skipHashStr(tokPtr);
742                 pTokPtr = tokPtr;
743                 skipExpression(pTokPtr);
744                 tokPtr = pTokPtr;
745                 break;
746             default:
747                 Q_ASSERT_X(false, "skipExpression", "Unrecognized token");
748                 break;
749             }
750         }
751     }
752 }
753
754 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
755         ProFile *pro, const ushort *tokPtr)
756 {
757     m_current.pro = pro;
758     m_current.line = 0;
759     return visitProBlock(tokPtr);
760 }
761
762 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(
763         const ushort *tokPtr)
764 {
765     ProStringList curr;
766     bool okey = true, or_op = false, invert = false;
767     uint blockLen;
768     VisitReturn ret = ReturnTrue;
769     while (ushort tok = *tokPtr++) {
770         switch (tok) {
771         case TokLine:
772             m_current.line = *tokPtr++;
773             continue;
774         case TokAssign:
775         case TokAppend:
776         case TokAppendUnique:
777         case TokRemove:
778         case TokReplace:
779             visitProVariable(tok, curr, tokPtr);
780             curr.clear();
781             continue;
782         case TokBranch:
783             blockLen = getBlockLen(tokPtr);
784             if (m_cumulative) {
785                 if (!okey)
786                     m_skipLevel++;
787                 ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
788                 tokPtr += blockLen;
789                 blockLen = getBlockLen(tokPtr);
790                 if (!okey)
791                     m_skipLevel--;
792                 else
793                     m_skipLevel++;
794                 if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen)
795                     ret = visitProBlock(tokPtr);
796                 if (okey)
797                     m_skipLevel--;
798             } else {
799                 if (okey)
800                     ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
801                 tokPtr += blockLen;
802                 blockLen = getBlockLen(tokPtr);
803                 if (!okey)
804                     ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue;
805             }
806             tokPtr += blockLen;
807             okey = true, or_op = false; // force next evaluation
808             break;
809         case TokForLoop:
810             if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
811                 skipHashStr(tokPtr);
812                 uint exprLen = getBlockLen(tokPtr);
813                 tokPtr += exprLen;
814                 blockLen = getBlockLen(tokPtr);
815                 ret = visitProBlock(tokPtr);
816             } else if (okey != or_op) {
817                 const ProString &variable = getHashStr(tokPtr);
818                 uint exprLen = getBlockLen(tokPtr);
819                 const ushort *exprPtr = tokPtr;
820                 tokPtr += exprLen;
821                 blockLen = getBlockLen(tokPtr);
822                 ret = visitProLoop(variable, exprPtr, tokPtr);
823             } else {
824                 skipHashStr(tokPtr);
825                 uint exprLen = getBlockLen(tokPtr);
826                 tokPtr += exprLen;
827                 blockLen = getBlockLen(tokPtr);
828                 ret = ReturnTrue;
829             }
830             tokPtr += blockLen;
831             okey = true, or_op = false; // force next evaluation
832             break;
833         case TokTestDef:
834         case TokReplaceDef:
835             if (m_cumulative || okey != or_op) {
836                 const ProString &name = getHashStr(tokPtr);
837                 blockLen = getBlockLen(tokPtr);
838                 visitProFunctionDef(tok, name, tokPtr);
839             } else {
840                 skipHashStr(tokPtr);
841                 blockLen = getBlockLen(tokPtr);
842             }
843             tokPtr += blockLen;
844             okey = true, or_op = false; // force next evaluation
845             continue;
846         case TokNot:
847             invert ^= true;
848             continue;
849         case TokAnd:
850             or_op = false;
851             continue;
852         case TokOr:
853             or_op = true;
854             continue;
855         case TokCondition:
856             if (!m_skipLevel && okey != or_op) {
857                 if (curr.size() != 1) {
858                     evalError(fL1S("Conditional must expand to exactly one word."));
859                     okey = false;
860                 } else {
861                     okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true) ^ invert;
862                 }
863             }
864             or_op = !okey; // tentatively force next evaluation
865             invert = false;
866             curr.clear();
867             continue;
868         case TokTestCall:
869             if (!m_skipLevel && okey != or_op) {
870                 if (curr.size() != 1) {
871                     evalError(fL1S("Test name must expand to exactly one word."));
872                     skipExpression(tokPtr);
873                     okey = false;
874                 } else {
875                     ret = evaluateConditionalFunction(curr.at(0), tokPtr);
876                     switch (ret) {
877                     case ReturnTrue: okey = true; break;
878                     case ReturnFalse: okey = false; break;
879                     default: return ret;
880                     }
881                     okey ^= invert;
882                 }
883             } else if (m_cumulative) {
884                 m_skipLevel++;
885                 if (curr.size() != 1)
886                     skipExpression(tokPtr);
887                 else
888                     evaluateConditionalFunction(curr.at(0), tokPtr);
889                 m_skipLevel--;
890             } else {
891                 skipExpression(tokPtr);
892             }
893             or_op = !okey; // tentatively force next evaluation
894             invert = false;
895             curr.clear();
896             continue;
897         default: {
898                 const ushort *oTokPtr = --tokPtr;
899                 evaluateExpression(tokPtr, &curr, false);
900                 if (tokPtr != oTokPtr)
901                     continue;
902             }
903             Q_ASSERT_X(false, "visitProBlock", "unexpected item type");
904         }
905         if (ret != ReturnTrue && ret != ReturnFalse)
906             break;
907     }
908     return ret;
909 }
910
911
912 void ProFileEvaluator::Private::visitProFunctionDef(
913         ushort tok, const ProString &name, const ushort *tokPtr)
914 {
915     QHash<ProString, FunctionDef> *hash =
916             (tok == TokTestDef
917              ? &m_functionDefs.testFunctions
918              : &m_functionDefs.replaceFunctions);
919     hash->insert(name, FunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr()));
920 }
921
922 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(
923         const ProString &_variable, const ushort *exprPtr, const ushort *tokPtr)
924 {
925     VisitReturn ret = ReturnTrue;
926     bool infinite = false;
927     int index = 0;
928     ProString variable;
929     ProStringList oldVarVal;
930     ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0);
931     if (_variable.isEmpty()) {
932         if (it_list != statics.strever) {
933             evalError(fL1S("Invalid loop expression."));
934             return ReturnFalse;
935         }
936         it_list = ProString(statics.strforever);
937     } else {
938         variable = map(_variable);
939         oldVarVal = valuesDirect(variable);
940     }
941     ProStringList list = valuesDirect(it_list);
942     if (list.isEmpty()) {
943         if (it_list == statics.strforever) {
944             infinite = true;
945         } else {
946             const QString &itl = it_list.toQString(m_tmp1);
947             int dotdot = itl.indexOf(statics.strDotDot);
948             if (dotdot != -1) {
949                 bool ok;
950                 int start = itl.left(dotdot).toInt(&ok);
951                 if (ok) {
952                     int end = itl.mid(dotdot+2).toInt(&ok);
953                     if (ok) {
954                         if (start < end) {
955                             for (int i = start; i <= end; i++)
956                                 list << ProString(QString::number(i), NoHash);
957                         } else {
958                             for (int i = start; i >= end; i--)
959                                 list << ProString(QString::number(i), NoHash);
960                         }
961                     }
962                 }
963             }
964         }
965     }
966
967     m_loopLevel++;
968     forever {
969         if (infinite) {
970             if (!variable.isEmpty())
971                 m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++), NoHash));
972             if (index > 1000) {
973                 evalError(fL1S("ran into infinite loop (> 1000 iterations)."));
974                 break;
975             }
976         } else {
977             ProString val;
978             do {
979                 if (index >= list.count())
980                     goto do_break;
981                 val = list.at(index++);
982             } while (val.isEmpty()); // stupid, but qmake is like that
983             m_valuemapStack.top()[variable] = ProStringList(val);
984         }
985
986         ret = visitProBlock(tokPtr);
987         switch (ret) {
988         case ReturnTrue:
989         case ReturnFalse:
990             break;
991         case ReturnNext:
992             ret = ReturnTrue;
993             break;
994         case ReturnBreak:
995             ret = ReturnTrue;
996             goto do_break;
997         default:
998             goto do_break;
999         }
1000     }
1001   do_break:
1002     m_loopLevel--;
1003
1004     if (!variable.isEmpty())
1005         m_valuemapStack.top()[variable] = oldVarVal;
1006     return ret;
1007 }
1008
1009 void ProFileEvaluator::Private::visitProVariable(
1010         ushort tok, const ProStringList &curr, const ushort *&tokPtr)
1011 {
1012     int sizeHint = *tokPtr++;
1013
1014     if (curr.size() != 1) {
1015         skipExpression(tokPtr);
1016         evalError(fL1S("Left hand side of assignment must expand to exactly one word."));
1017         return;
1018     }
1019     const ProString &varName = map(curr.first());
1020
1021     if (tok == TokReplace) {      // ~=
1022         // DEFINES ~= s/a/b/?[gqi]
1023
1024         const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true);
1025         const QString &val = varVal.at(0).toQString(m_tmp1);
1026         if (val.length() < 4 || val.at(0) != QLatin1Char('s')) {
1027             evalError(fL1S("the ~= operator can handle only the s/// function."));
1028             return;
1029         }
1030         QChar sep = val.at(1);
1031         QStringList func = val.split(sep);
1032         if (func.count() < 3 || func.count() > 4) {
1033             evalError(fL1S("the s/// function expects 3 or 4 arguments."));
1034             return;
1035         }
1036
1037         bool global = false, quote = false, case_sense = false;
1038         if (func.count() == 4) {
1039             global = func[3].indexOf(QLatin1Char('g')) != -1;
1040             case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
1041             quote = func[3].indexOf(QLatin1Char('q')) != -1;
1042         }
1043         QString pattern = func[1];
1044         QString replace = func[2];
1045         if (quote)
1046             pattern = QRegExp::escape(pattern);
1047
1048         QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
1049
1050         if (!m_skipLevel || m_cumulative) {
1051             // We could make a union of modified and unmodified values,
1052             // but this will break just as much as it fixes, so leave it as is.
1053             replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2);
1054             replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global, m_tmp2);
1055         }
1056     } else {
1057         ProStringList varVal = expandVariableReferences(tokPtr, sizeHint);
1058         switch (tok) {
1059         default: // whatever - cannot happen
1060         case TokAssign:          // =
1061             if (!m_cumulative) {
1062                 if (!m_skipLevel) {
1063                     zipEmpty(&varVal);
1064                     m_valuemapStack.top()[varName] = varVal;
1065                     m_filevaluemap[currentProFile()][varName] = varVal;
1066                 }
1067             } else {
1068                 zipEmpty(&varVal);
1069                 if (!varVal.isEmpty()) {
1070                     // We are greedy for values. But avoid exponential growth.
1071                     ProStringList &v = valuesRef(varName);
1072                     if (v.isEmpty()) {
1073                         v = varVal;
1074                     } else {
1075                         ProStringList old = v;
1076                         v = varVal;
1077                         QSet<ProString> has;
1078                         has.reserve(v.size());
1079                         foreach (const ProString &s, v)
1080                             has.insert(s);
1081                         v.reserve(v.size() + old.size());
1082                         foreach (const ProString &s, old)
1083                             if (!has.contains(s))
1084                                 v << s;
1085                     }
1086                     // These values will not be used for further processing inside
1087                     // the evaluator. Duplicate elimination happens later.
1088                     m_filevaluemap[currentProFile()][varName] += varVal;
1089                 }
1090             }
1091             break;
1092         case TokAppendUnique:    // *=
1093             if (!m_skipLevel || m_cumulative) {
1094                 insertUnique(&valuesRef(varName), varVal);
1095                 insertUnique(&m_filevaluemap[currentProFile()][varName], varVal);
1096             }
1097             break;
1098         case TokAppend:          // +=
1099             if (!m_skipLevel || m_cumulative) {
1100                 zipEmpty(&varVal);
1101                 valuesRef(varName) += varVal;
1102                 m_filevaluemap[currentProFile()][varName] += varVal;
1103             }
1104             break;
1105         case TokRemove:       // -=
1106             if (!m_cumulative) {
1107                 if (!m_skipLevel) {
1108                     removeEach(&valuesRef(varName), varVal);
1109                     removeEach(&m_filevaluemap[currentProFile()][varName], varVal);
1110                 }
1111             } else {
1112                 // We are stingy with our values, too.
1113             }
1114             break;
1115         }
1116     }
1117 }
1118
1119 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(
1120         ProFile *pro, ProFileEvaluatorHandler::EvalFileType type,
1121         ProFileEvaluator::LoadFlags flags)
1122 {
1123     m_handler->aboutToEval(currentProFile(), pro, type);
1124     m_profileStack.push(pro);
1125     if (flags & LoadPreFiles) {
1126 #ifdef PROEVALUATOR_THREAD_SAFE
1127         {
1128             QMutexLocker locker(&m_option->mutex);
1129             if (m_option->base_inProgress) {
1130                 QThreadPool::globalInstance()->releaseThread();
1131                 m_option->cond.wait(&m_option->mutex);
1132                 QThreadPool::globalInstance()->reserveThread();
1133             } else
1134 #endif
1135             if (m_option->base_valuemap.isEmpty()) {
1136 #ifdef PROEVALUATOR_THREAD_SAFE
1137                 m_option->base_inProgress = true;
1138                 locker.unlock();
1139 #endif
1140
1141                 bool cumulative = m_cumulative;
1142                 m_cumulative = false;
1143
1144                 // ### init QMAKE_QMAKE, QMAKE_SH
1145                 // ### init QMAKE_EXT_{C,H,CPP,OBJ}
1146                 // ### init TEMPLATE_PREFIX
1147
1148                 QString qmake_cache = m_option->cachefile;
1149                 if (qmake_cache.isEmpty() && !m_outputDir.isEmpty())  { //find it as it has not been specified
1150                     QDir dir(m_outputDir);
1151                     forever {
1152                         qmake_cache = dir.path() + QLatin1String("/.qmake.cache");
1153                         if (IoUtils::exists(qmake_cache))
1154                             break;
1155                         if (!dir.cdUp() || dir.isRoot()) {
1156                             qmake_cache.clear();
1157                             break;
1158                         }
1159                     }
1160                 }
1161                 if (!qmake_cache.isEmpty()) {
1162                     qmake_cache = resolvePath(qmake_cache);
1163                     QHash<ProString, ProStringList> cache_valuemap;
1164                     if (evaluateFileInto(qmake_cache, ProFileEvaluatorHandler::EvalConfigFile,
1165                                          &cache_valuemap, 0, EvalProOnly)) {
1166                         if (m_option->qmakespec.isEmpty()) {
1167                             const ProStringList &vals = cache_valuemap.value(ProString("QMAKESPEC"));
1168                             if (!vals.isEmpty())
1169                                 m_option->qmakespec = vals.first().toQString();
1170                         }
1171                     } else {
1172                         qmake_cache.clear();
1173                     }
1174                 }
1175                 m_option->cachefile = qmake_cache;
1176
1177                 QStringList mkspec_roots = qmakeMkspecPaths();
1178
1179                 QString qmakespec = expandEnvVars(m_option->qmakespec);
1180                 if (qmakespec.isEmpty()) {
1181                     foreach (const QString &root, mkspec_roots) {
1182                         QString mkspec = root + QLatin1String("/default");
1183                         if (IoUtils::fileType(mkspec) == IoUtils::FileIsDir) {
1184                             qmakespec = mkspec;
1185                             break;
1186                         }
1187                     }
1188                     if (qmakespec.isEmpty()) {
1189                         m_handler->configError(fL1S("Could not find qmake configuration directory"));
1190                         // Unlike in qmake, not finding the spec is not critical ...
1191                     }
1192                 }
1193
1194                 if (IoUtils::isRelativePath(qmakespec)) {
1195                     if (IoUtils::exists(currentDirectory() + QLatin1Char('/') + qmakespec
1196                                         + QLatin1String("/qmake.conf"))) {
1197                         qmakespec = currentDirectory() + QLatin1Char('/') + qmakespec;
1198                     } else if (!m_outputDir.isEmpty()
1199                                && IoUtils::exists(m_outputDir + QLatin1Char('/') + qmakespec
1200                                                   + QLatin1String("/qmake.conf"))) {
1201                         qmakespec = m_outputDir + QLatin1Char('/') + qmakespec;
1202                     } else {
1203                         foreach (const QString &root, mkspec_roots) {
1204                             QString mkspec = root + QLatin1Char('/') + qmakespec;
1205                             if (IoUtils::exists(mkspec)) {
1206                                 qmakespec = mkspec;
1207                                 goto cool;
1208                             }
1209                         }
1210                         m_handler->configError(fL1S("Could not find qmake configuration file"));
1211                         // Unlike in qmake, a missing config is not critical ...
1212                         qmakespec.clear();
1213                       cool: ;
1214                     }
1215                 }
1216
1217                 if (!qmakespec.isEmpty()) {
1218                     m_option->qmakespec = QDir::cleanPath(qmakespec);
1219
1220                     QString spec = m_option->qmakespec + QLatin1String("/qmake.conf");
1221                     if (!evaluateFileDirect(spec, ProFileEvaluatorHandler::EvalConfigFile,
1222                                             ProFileEvaluator::LoadProOnly)) {
1223                         m_handler->configError(
1224                                 fL1S("Could not read qmake configuration file %1").arg(spec));
1225                     } else if (!m_option->cachefile.isEmpty()) {
1226                         evaluateFileDirect(m_option->cachefile,
1227                                            ProFileEvaluatorHandler::EvalConfigFile,
1228                                            ProFileEvaluator::LoadProOnly);
1229                     }
1230                     m_option->qmakespec_name = IoUtils::fileName(m_option->qmakespec).toString();
1231                     if (m_option->qmakespec_name == QLatin1String("default")) {
1232 #ifdef Q_OS_UNIX
1233                         char buffer[1024];
1234                         int l = ::readlink(m_option->qmakespec.toLocal8Bit().constData(), buffer, 1024);
1235                         if (l != -1)
1236                             m_option->qmakespec_name =
1237                                     IoUtils::fileName(QString::fromLocal8Bit(buffer, l)).toString();
1238 #else
1239                         // We can't resolve symlinks as they do on Unix, so configure.exe puts
1240                         // the source of the qmake.conf at the end of the default/qmake.conf in
1241                         // the QMAKESPEC_ORG variable.
1242                         const ProStringList &spec_org =
1243                                 m_option->base_valuemap.value(ProString("QMAKESPEC_ORIGINAL"));
1244                         if (!spec_org.isEmpty())
1245                             m_option->qmakespec_name =
1246                                     IoUtils::fileName(spec_org.first().toQString()).toString();
1247 #endif
1248                     }
1249                 }
1250
1251                 evaluateFeatureFile(QLatin1String("default_pre.prf"));
1252
1253                 m_option->base_valuemap = m_valuemapStack.top();
1254                 m_option->base_functions = m_functionDefs;
1255
1256                 m_cumulative = cumulative;
1257
1258 #ifdef PROEVALUATOR_THREAD_SAFE
1259                 locker.relock();
1260                 m_option->base_inProgress = false;
1261                 m_option->cond.wakeAll();
1262 #endif
1263                 goto fresh;
1264             }
1265 #ifdef PROEVALUATOR_THREAD_SAFE
1266         }
1267 #endif
1268
1269             m_valuemapStack.top() = m_option->base_valuemap;
1270             m_functionDefs = m_option->base_functions;
1271
1272           fresh:
1273             ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")];
1274             if (tgt.isEmpty())
1275                 tgt.append(ProString(QFileInfo(pro->fileName()).baseName(), NoHash));
1276
1277             ProStringList &tmp = m_valuemapStack.top()[ProString("CONFIG")];
1278             foreach (const QString &add, m_addUserConfigCmdArgs)
1279                 tmp.append(ProString(add, NoHash));
1280             foreach (const QString &remove, m_removeUserConfigCmdArgs)
1281                 removeAll(&tmp, ProString(remove, NoHash));
1282     }
1283
1284     visitProBlock(pro, pro->tokPtr());
1285
1286     if (flags & LoadPostFiles) {
1287             evaluateFeatureFile(QLatin1String("default_post.prf"));
1288
1289             QSet<QString> processed;
1290             forever {
1291                 bool finished = true;
1292                 ProStringList configs = valuesDirect(statics.strCONFIG);
1293                 for (int i = configs.size() - 1; i >= 0; --i) {
1294                     QString config = configs.at(i).toQString(m_tmp1).toLower();
1295                     if (!processed.contains(config)) {
1296                         config.detach();
1297                         processed.insert(config);
1298                         if (evaluateFeatureFile(config)) {
1299                             finished = false;
1300                             break;
1301                         }
1302                     }
1303                 }
1304                 if (finished)
1305                     break;
1306             }
1307     }
1308     m_profileStack.pop();
1309     m_handler->doneWithEval(currentProFile());
1310
1311     return ReturnTrue;
1312 }
1313
1314
1315 QStringList ProFileEvaluator::Private::qmakeMkspecPaths() const
1316 {
1317     QStringList ret;
1318     const QString concat = QLatin1String("/mkspecs");
1319
1320     QByteArray qmakepath = qgetenv("QMAKEPATH");
1321     if (!qmakepath.isEmpty())
1322         foreach (const QString &it, QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep))
1323             ret << QDir::cleanPath(it) + concat;
1324
1325     QString builtIn = propertyValue(QLatin1String("QT_INSTALL_DATA")) + concat;
1326     if (!ret.contains(builtIn))
1327         ret << builtIn;
1328
1329     return ret;
1330 }
1331
1332 QStringList ProFileEvaluator::Private::qmakeFeaturePaths() const
1333 {
1334     QString mkspecs_concat = QLatin1String("/mkspecs");
1335     QString features_concat = QLatin1String("/features");
1336     QStringList concat;
1337     switch (m_option->target_mode) {
1338     case ProFileOption::TARG_MACX_MODE:
1339         concat << QLatin1String("/features/mac");
1340         concat << QLatin1String("/features/macx");
1341         concat << QLatin1String("/features/unix");
1342         break;
1343     case ProFileOption::TARG_UNIX_MODE:
1344         concat << QLatin1String("/features/unix");
1345         break;
1346     case ProFileOption::TARG_WIN_MODE:
1347         concat << QLatin1String("/features/win32");
1348         break;
1349     case ProFileOption::TARG_MAC9_MODE:
1350         concat << QLatin1String("/features/mac");
1351         concat << QLatin1String("/features/mac9");
1352         break;
1353     case ProFileOption::TARG_QNX6_MODE:
1354         concat << QLatin1String("/features/qnx6");
1355         concat << QLatin1String("/features/unix");
1356         break;
1357     }
1358     concat << features_concat;
1359
1360     QStringList feature_roots;
1361
1362     QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
1363     if (!mkspec_path.isEmpty())
1364         foreach (const QString &f, QString::fromLocal8Bit(mkspec_path).split(m_option->dirlist_sep))
1365             feature_roots += resolvePath(f);
1366
1367     feature_roots += propertyValue(QLatin1String("QMAKEFEATURES"), false).split(
1368             m_option->dirlist_sep, QString::SkipEmptyParts);
1369
1370     if (!m_option->cachefile.isEmpty()) {
1371         QString path = m_option->cachefile.left(m_option->cachefile.lastIndexOf((ushort)'/'));
1372         foreach (const QString &concat_it, concat)
1373             feature_roots << (path + concat_it);
1374     }
1375
1376     QByteArray qmakepath = qgetenv("QMAKEPATH");
1377     if (!qmakepath.isNull()) {
1378         const QStringList lst = QString::fromLocal8Bit(qmakepath).split(m_option->dirlist_sep);
1379         foreach (const QString &item, lst) {
1380             QString citem = resolvePath(item);
1381             foreach (const QString &concat_it, concat)
1382                 feature_roots << (citem + mkspecs_concat + concat_it);
1383         }
1384     }
1385
1386     if (!m_option->qmakespec.isEmpty()) {
1387         QString qmakespec = resolvePath(m_option->qmakespec);
1388         feature_roots << (qmakespec + features_concat);
1389
1390         QDir specdir(qmakespec);
1391         while (!specdir.isRoot()) {
1392             if (!specdir.cdUp() || specdir.isRoot())
1393                 break;
1394             if (IoUtils::exists(specdir.path() + features_concat)) {
1395                 foreach (const QString &concat_it, concat)
1396                     feature_roots << (specdir.path() + concat_it);
1397                 break;
1398             }
1399         }
1400     }
1401
1402     foreach (const QString &concat_it, concat)
1403         feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
1404                           mkspecs_concat + concat_it);
1405     foreach (const QString &concat_it, concat)
1406         feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
1407                           mkspecs_concat + concat_it);
1408
1409     for (int i = 0; i < feature_roots.count(); ++i)
1410         if (!feature_roots.at(i).endsWith((ushort)'/'))
1411             feature_roots[i].append((ushort)'/');
1412
1413     feature_roots.removeDuplicates();
1414
1415     return feature_roots;
1416 }
1417
1418 QString ProFileEvaluator::Private::propertyValue(const QString &name, bool complain) const
1419 {
1420     if (m_option->properties.contains(name))
1421         return m_option->properties.value(name);
1422     if (name == QLatin1String("QMAKE_MKSPECS"))
1423         return qmakeMkspecPaths().join(m_option->dirlist_sep);
1424     if (name == QLatin1String("QMAKE_VERSION"))
1425         return QLatin1String("1.0");        //### FIXME
1426     if (complain)
1427         evalError(fL1S("Querying unknown property %1").arg(name));
1428     return QString();
1429 }
1430
1431 ProFile *ProFileEvaluator::Private::currentProFile() const
1432 {
1433     if (m_profileStack.count() > 0)
1434         return m_profileStack.top();
1435     return 0;
1436 }
1437
1438 QString ProFileEvaluator::Private::currentFileName() const
1439 {
1440     ProFile *pro = currentProFile();
1441     if (pro)
1442         return pro->fileName();
1443     return QString();
1444 }
1445
1446 QString ProFileEvaluator::Private::currentDirectory() const
1447 {
1448     ProFile *cur = m_profileStack.top();
1449     return cur->directoryName();
1450 }
1451
1452 // The (QChar*)current->constData() constructs below avoid pointless detach() calls
1453 // FIXME: This is inefficient. Should not make new string if it is a straight subsegment
1454 static ALWAYS_INLINE void appendChar(ushort unicode,
1455     QString *current, QChar **ptr, ProString *pending)
1456 {
1457     if (!pending->isEmpty()) {
1458         int len = pending->size();
1459         current->resize(current->size() + len);
1460         ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1461         pending->clear();
1462         *ptr = (QChar*)current->constData() + len;
1463     }
1464     *(*ptr)++ = QChar(unicode);
1465 }
1466
1467 static void appendString(const ProString &string,
1468     QString *current, QChar **ptr, ProString *pending)
1469 {
1470     if (string.isEmpty())
1471         return;
1472     QChar *uc = (QChar*)current->constData();
1473     int len;
1474     if (*ptr != uc) {
1475         len = *ptr - uc;
1476         current->resize(current->size() + string.size());
1477     } else if (!pending->isEmpty()) {
1478         len = pending->size();
1479         current->resize(current->size() + len + string.size());
1480         ::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
1481         pending->clear();
1482     } else {
1483         *pending = string;
1484         return;
1485     }
1486     *ptr = (QChar*)current->constData() + len;
1487     ::memcpy(*ptr, string.constData(), string.size() * 2);
1488     *ptr += string.size();
1489 }
1490
1491 static void flushCurrent(ProStringList *ret,
1492     QString *current, QChar **ptr, ProString *pending, bool joined)
1493 {
1494     QChar *uc = (QChar*)current->constData();
1495     int len = *ptr - uc;
1496     if (len) {
1497         ret->append(ProString(QString(uc, len), NoHash));
1498         *ptr = uc;
1499     } else if (!pending->isEmpty()) {
1500         ret->append(*pending);
1501         pending->clear();
1502     } else if (joined) {
1503         ret->append(ProString());
1504     }
1505 }
1506
1507 static inline void flushFinal(ProStringList *ret,
1508     const QString &current, const QChar *ptr, const ProString &pending,
1509     const ProString &str, bool replaced, bool joined)
1510 {
1511     int len = ptr - current.data();
1512     if (len) {
1513         if (!replaced && len == str.size())
1514             ret->append(str);
1515         else
1516             ret->append(ProString(QString(current.data(), len), NoHash));
1517     } else if (!pending.isEmpty()) {
1518         ret->append(pending);
1519     } else if (joined) {
1520         ret->append(ProString());
1521     }
1522 }
1523
1524 ProStringList ProFileEvaluator::Private::expandVariableReferences(
1525     const ProString &str, int *pos, bool joined)
1526 {
1527     ProStringList ret;
1528 //    if (ok)
1529 //        *ok = true;
1530     if (str.isEmpty() && !pos)
1531         return ret;
1532
1533     const ushort LSQUARE = '[';
1534     const ushort RSQUARE = ']';
1535     const ushort LCURLY = '{';
1536     const ushort RCURLY = '}';
1537     const ushort LPAREN = '(';
1538     const ushort RPAREN = ')';
1539     const ushort DOLLAR = '$';
1540     const ushort BACKSLASH = '\\';
1541     const ushort UNDERSCORE = '_';
1542     const ushort DOT = '.';
1543     const ushort SPACE = ' ';
1544     const ushort TAB = '\t';
1545     const ushort COMMA = ',';
1546     const ushort SINGLEQUOTE = '\'';
1547     const ushort DOUBLEQUOTE = '"';
1548
1549     ushort unicode, quote = 0, parens = 0;
1550     const ushort *str_data = (const ushort *)str.constData();
1551     const int str_len = str.size();
1552
1553     ProString var, args;
1554
1555     bool replaced = false;
1556     bool putSpace = false;
1557     QString current; // Buffer for successively assembled string segments
1558     current.resize(str.size());
1559     QChar *ptr = current.data();
1560     ProString pending; // Buffer for string segments from variables
1561     // Only one of the above buffers can be filled at a given time.
1562     for (int i = pos ? *pos : 0; i < str_len; ++i) {
1563         unicode = str_data[i];
1564         if (unicode == DOLLAR) {
1565             if (str_len > i+2 && str_data[i+1] == DOLLAR) {
1566                 ++i;
1567                 ushort term = 0;
1568                 enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
1569                 unicode = str_data[++i];
1570                 if (unicode == LSQUARE) {
1571                     unicode = str_data[++i];
1572                     term = RSQUARE;
1573                     var_type = PROPERTY;
1574                 } else if (unicode == LCURLY) {
1575                     unicode = str_data[++i];
1576                     var_type = VAR;
1577                     term = RCURLY;
1578                 } else if (unicode == LPAREN) {
1579                     unicode = str_data[++i];
1580                     var_type = ENVIRON;
1581                     term = RPAREN;
1582                 }
1583                 int name_start = i;
1584                 forever {
1585                     if (!(unicode & (0xFF<<8)) &&
1586                        unicode != DOT && unicode != UNDERSCORE &&
1587                        //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
1588                        (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
1589                        (unicode < '0' || unicode > '9'))
1590                         break;
1591                     if (++i == str_len)
1592                         break;
1593                     unicode = str_data[i];
1594                     // at this point, i points to either the 'term' or 'next' character (which is in unicode)
1595                 }
1596                 var = str.mid(name_start, i - name_start);
1597                 if (var_type == VAR && unicode == LPAREN) {
1598                     var_type = FUNCTION;
1599                     name_start = i + 1;
1600                     int depth = 0;
1601                     forever {
1602                         if (++i == str_len)
1603                             break;
1604                         unicode = str_data[i];
1605                         if (unicode == LPAREN) {
1606                             depth++;
1607                         } else if (unicode == RPAREN) {
1608                             if (!depth)
1609                                 break;
1610                             --depth;
1611                         }
1612                     }
1613                     args = str.mid(name_start, i - name_start);
1614                     if (++i < str_len)
1615                         unicode = str_data[i];
1616                     else
1617                         unicode = 0;
1618                     // at this point i is pointing to the 'next' character (which is in unicode)
1619                     // this might actually be a term character since you can do $${func()}
1620                 }
1621                 if (term) {
1622                     if (unicode != term) {
1623                         evalError(fL1S("Missing %1 terminator [found %2]")
1624                                   .arg(QChar(term))
1625                                   .arg(unicode ? QString(unicode) : fL1S("end-of-line")));
1626 //                        if (ok)
1627 //                            *ok = false;
1628                         if (pos)
1629                             *pos = str_len;
1630                         return ProStringList();
1631                     }
1632                 } else {
1633                     // move the 'cursor' back to the last char of the thing we were looking at
1634                     --i;
1635                 }
1636
1637                 ProStringList replacement;
1638                 if (var_type == ENVIRON) {
1639                     replacement = split_value_list(QString::fromLocal8Bit(qgetenv(
1640                             var.toQString(m_tmp1).toLocal8Bit().constData())));
1641                 } else if (var_type == PROPERTY) {
1642                     replacement << ProString(propertyValue(var.toQString(m_tmp1)), NoHash);
1643                 } else if (var_type == FUNCTION) {
1644                     replacement += evaluateExpandFunction(var, args);
1645                 } else if (var_type == VAR) {
1646                     replacement = values(map(var));
1647                 }
1648                 if (!replacement.isEmpty()) {
1649                     if (quote || joined) {
1650                         if (putSpace) {
1651                             putSpace = false;
1652                             if (!replacement.at(0).isEmpty()) // Bizarre, indeed
1653                                 appendChar(' ', &current, &ptr, &pending);
1654                         }
1655                         appendString(ProString(replacement.join(statics.field_sep), NoHash),
1656                                      &current, &ptr, &pending);
1657                     } else {
1658                         appendString(replacement.at(0), &current, &ptr, &pending);
1659                         if (replacement.size() > 1) {
1660                             flushCurrent(&ret, &current, &ptr, &pending, false);
1661                             int j = 1;
1662                             if (replacement.size() > 2) {
1663                                 // FIXME: ret.reserve(ret.size() + replacement.size() - 2);
1664                                 for (; j < replacement.size() - 1; ++j)
1665                                     ret << replacement.at(j);
1666                             }
1667                             pending = replacement.at(j);
1668                         }
1669                     }
1670                     replaced = true;
1671                 }
1672                 continue;
1673             }
1674         } else if (unicode == BACKSLASH) {
1675             static const char symbols[] = "[]{}()$\\'\"";
1676             ushort unicode2 = str_data[i+1];
1677             if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
1678                 unicode = unicode2;
1679                 ++i;
1680             }
1681         } else if (quote) {
1682             if (unicode == quote) {
1683                 quote = 0;
1684                 continue;
1685             }
1686         } else {
1687             if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
1688                 quote = unicode;
1689                 continue;
1690             } else if (unicode == SPACE || unicode == TAB) {
1691                 if (!joined)
1692                     flushCurrent(&ret, &current, &ptr, &pending, false);
1693                 else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
1694                     putSpace = true;
1695                 continue;
1696             } else if (pos) {
1697                 if (unicode == LPAREN) {
1698                     ++parens;
1699                 } else if (unicode == RPAREN) {
1700                     --parens;
1701                 } else if (!parens && unicode == COMMA) {
1702                     if (!joined) {
1703                         *pos = i + 1;
1704                         flushFinal(&ret, current, ptr, pending, str, replaced, false);
1705                         return ret;
1706                     }
1707                     flushCurrent(&ret, &current, &ptr, &pending, true);
1708                     putSpace = false;
1709                     continue;
1710                 }
1711             }
1712         }
1713         if (putSpace) {
1714             putSpace = false;
1715             appendChar(' ', &current, &ptr, &pending);
1716         }
1717         appendChar(unicode, &current, &ptr, &pending);
1718     }
1719     if (pos)
1720         *pos = str_len;
1721     flushFinal(&ret, current, ptr, pending, str, replaced, joined);
1722     return ret;
1723 }
1724
1725 bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
1726 {
1727     // magic types for easy flipping
1728     if (config == statics.strtrue)
1729         return true;
1730     if (config == statics.strfalse)
1731         return false;
1732
1733     // mkspecs
1734     if ((m_option->target_mode == m_option->TARG_MACX_MODE
1735             || m_option->target_mode == m_option->TARG_QNX6_MODE
1736             || m_option->target_mode == m_option->TARG_UNIX_MODE)
1737           && config == statics.strunix)
1738         return true;
1739     if (m_option->target_mode == m_option->TARG_MACX_MODE && config == statics.strmacx)
1740         return true;
1741     if (m_option->target_mode == m_option->TARG_QNX6_MODE && config == statics.strqnx6)
1742         return true;
1743     if (m_option->target_mode == m_option->TARG_MAC9_MODE && config == statics.strmac9)
1744         return true;
1745     if ((m_option->target_mode == m_option->TARG_MAC9_MODE
1746             || m_option->target_mode == m_option->TARG_MACX_MODE)
1747           && config == statics.strmac)
1748         return true;
1749     if (m_option->target_mode == m_option->TARG_WIN_MODE && config == statics.strwin32)
1750         return true;
1751
1752     if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) {
1753         QString cfg = config;
1754         cfg.detach(); // Keep m_tmp out of QRegExp's cache
1755         QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard);
1756
1757         if (re.exactMatch(m_option->qmakespec_name))
1758             return true;
1759
1760         // CONFIG variable
1761         int t = 0;
1762         foreach (const ProString &configValue, valuesDirect(statics.strCONFIG)) {
1763             if (re.exactMatch(configValue.toQString(m_tmp[t])))
1764                 return true;
1765             t ^= 1;
1766         }
1767     } else {
1768         // mkspecs
1769         if (m_option->qmakespec_name == config)
1770             return true;
1771
1772         // CONFIG variable
1773         if (valuesDirect(statics.strCONFIG).contains(ProString(config, NoHash)))
1774             return true;
1775     }
1776
1777     return false;
1778 }
1779
1780 ProStringList ProFileEvaluator::Private::expandVariableReferences(
1781         const ushort *&tokPtr, int sizeHint, bool joined)
1782 {
1783     ProStringList ret;
1784     ret.reserve(sizeHint);
1785     forever {
1786         evaluateExpression(tokPtr, &ret, joined);
1787         switch (*tokPtr) {
1788         case TokValueTerminator:
1789         case TokFuncTerminator:
1790             tokPtr++;
1791             return ret;
1792         case TokArgSeparator:
1793             if (joined) {
1794                 tokPtr++;
1795                 continue;
1796             }
1797             // fallthrough
1798         default:
1799             Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token");
1800             break;
1801         }
1802     }
1803 }
1804
1805 QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ushort *&tokPtr)
1806 {
1807     QList<ProStringList> args_list;
1808     if (*tokPtr != TokFuncTerminator) {
1809         for (;; tokPtr++) {
1810             ProStringList arg;
1811             evaluateExpression(tokPtr, &arg, false);
1812             args_list << arg;
1813             if (*tokPtr == TokFuncTerminator)
1814                 break;
1815             Q_ASSERT(*tokPtr == TokArgSeparator);
1816         }
1817     }
1818     tokPtr++;
1819     return args_list;
1820 }
1821
1822 QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProString &arguments)
1823 {
1824     QList<ProStringList> args_list;
1825     for (int pos = 0; pos < arguments.size(); )
1826         args_list << expandVariableReferences(arguments, &pos);
1827     return args_list;
1828 }
1829
1830 ProStringList ProFileEvaluator::Private::evaluateFunction(
1831         const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
1832 {
1833     bool oki;
1834     ProStringList ret;
1835
1836     if (m_valuemapStack.count() >= 100) {
1837         evalError(fL1S("ran into infinite recursion (depth > 100)."));
1838         oki = false;
1839     } else {
1840         m_valuemapStack.push(QHash<ProString, ProStringList>());
1841         m_locationStack.push(m_current);
1842         int loopLevel = m_loopLevel;
1843         m_loopLevel = 0;
1844
1845         ProStringList args;
1846         for (int i = 0; i < argumentsList.count(); ++i) {
1847             args += argumentsList[i];
1848             m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i];
1849         }
1850         m_valuemapStack.top()[statics.strARGS] = args;
1851         oki = (visitProBlock(func.pro(), func.tokPtr()) != ReturnFalse); // True || Return
1852         ret = m_returnValue;
1853         m_returnValue.clear();
1854
1855         m_loopLevel = loopLevel;
1856         m_current = m_locationStack.pop();
1857         m_valuemapStack.pop();
1858     }
1859     if (ok)
1860         *ok = oki;
1861     if (oki)
1862         return ret;
1863     return ProStringList();
1864 }
1865
1866 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateBoolFunction(
1867         const FunctionDef &func, const QList<ProStringList> &argumentsList,
1868         const ProString &function)
1869 {
1870     bool ok;
1871     ProStringList ret = evaluateFunction(func, argumentsList, &ok);
1872     if (ok) {
1873         if (ret.isEmpty())
1874             return ReturnTrue;
1875         if (ret.at(0) != statics.strfalse) {
1876             if (ret.at(0) == statics.strtrue)
1877                 return ReturnTrue;
1878             int val = ret.at(0).toQString(m_tmp1).toInt(&ok);
1879             if (ok) {
1880                 if (val)
1881                     return ReturnTrue;
1882             } else {
1883                 evalError(fL1S("Unexpected return value from test '%1': %2")
1884                           .arg(function.toQString(m_tmp1))
1885                           .arg(ret.join(QLatin1String(" :: "))));
1886             }
1887         }
1888     }
1889     return ReturnFalse;
1890 }
1891
1892 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1893         const ProString &func, const ushort *&tokPtr)
1894 {
1895     QHash<ProString, FunctionDef>::ConstIterator it =
1896             m_functionDefs.replaceFunctions.constFind(func);
1897     if (it != m_functionDefs.replaceFunctions.constEnd())
1898         return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
1899
1900     //why don't the builtin functions just use args_list? --Sam
1901     return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
1902 }
1903
1904 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1905         const ProString &func, const ProString &arguments)
1906 {
1907     QHash<ProString, FunctionDef>::ConstIterator it =
1908             m_functionDefs.replaceFunctions.constFind(func);
1909     if (it != m_functionDefs.replaceFunctions.constEnd())
1910         return evaluateFunction(*it, prepareFunctionArgs(arguments), 0);
1911
1912     //why don't the builtin functions just use args_list? --Sam
1913     int pos = 0;
1914     return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
1915 }
1916
1917 ProStringList ProFileEvaluator::Private::evaluateExpandFunction(
1918         const ProString &func, const ProStringList &args)
1919 {
1920     ExpandFunc func_t = ExpandFunc(statics.expands.value(func));
1921     if (func_t == 0) {
1922         const QString &fn = func.toQString(m_tmp1);
1923         const QString &lfn = fn.toLower();
1924         if (!fn.isSharedWith(lfn))
1925             func_t = ExpandFunc(statics.expands.value(ProString(lfn)));
1926     }
1927
1928     ProStringList ret;
1929
1930     switch (func_t) {
1931         case E_BASENAME:
1932         case E_DIRNAME:
1933         case E_SECTION: {
1934             bool regexp = false;
1935             QString sep;
1936             ProString var;
1937             int beg = 0;
1938             int end = -1;
1939             if (func_t == E_SECTION) {
1940                 if (args.count() != 3 && args.count() != 4) {
1941                     evalError(fL1S("%1(var) section(var, sep, begin, end) requires"
1942                                    " three or four arguments.").arg(func.toQString(m_tmp1)));
1943                 } else {
1944                     var = args[0];
1945                     sep = args.at(1).toQString();
1946                     beg = args.at(2).toQString(m_tmp2).toInt();
1947                     if (args.count() == 4)
1948                         end = args.at(3).toQString(m_tmp2).toInt();
1949                 }
1950             } else {
1951                 if (args.count() != 1) {
1952                     evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
1953                 } else {
1954                     var = args[0];
1955                     regexp = true;
1956                     sep = QLatin1String("[\\\\/]");
1957                     if (func_t == E_DIRNAME)
1958                         end = -2;
1959                     else
1960                         beg = -1;
1961                 }
1962             }
1963             if (!var.isEmpty()) {
1964                 if (regexp) {
1965                     QRegExp sepRx(sep);
1966                     foreach (const ProString &str, values(map(var))) {
1967                         const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end);
1968                         ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash));
1969                     }
1970                 } else {
1971                     foreach (const ProString &str, values(map(var))) {
1972                         const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end);
1973                         ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr, NoHash));
1974                     }
1975                 }
1976             }
1977             break;
1978         }
1979         case E_SPRINTF:
1980             if(args.count() < 1) {
1981                 evalError(fL1S("sprintf(format, ...) requires at least one argument"));
1982             } else {
1983                 QString tmp = args.at(0).toQString(m_tmp1);
1984                 for (int i = 1; i < args.count(); ++i)
1985                     tmp = tmp.arg(args.at(i).toQString(m_tmp2));
1986                 // Note: this depends on split_value_list() making a deep copy
1987                 ret = split_value_list(tmp);
1988             }
1989             break;
1990         case E_JOIN: {
1991             if (args.count() < 1 || args.count() > 4) {
1992                 evalError(fL1S("join(var, glue, before, after) requires one to four arguments."));
1993             } else {
1994                 QString glue;
1995                 ProString before, after;
1996                 if (args.count() >= 2)
1997                     glue = args.at(1).toQString(m_tmp1);
1998                 if (args.count() >= 3)
1999                     before = args[2];
2000                 if (args.count() == 4)
2001                     after = args[3];
2002                 const ProStringList &var = values(map(args.at(0)));
2003                 if (!var.isEmpty())
2004                     ret.append(ProString(before + var.join(glue) + after, NoHash));
2005             }
2006             break;
2007         }
2008         case E_SPLIT:
2009             if (args.count() != 2) {
2010                 evalError(fL1S("split(var, sep) requires one or two arguments"));
2011             } else {
2012                 const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep;
2013                 foreach (const ProString &var, values(map(args.at(0))))
2014                     foreach (const QString &splt, var.toQString(m_tmp2).split(sep))
2015                         ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt, NoHash));
2016             }
2017             break;
2018         case E_MEMBER:
2019             if (args.count() < 1 || args.count() > 3) {
2020                 evalError(fL1S("member(var, start, end) requires one to three arguments."));
2021             } else {
2022                 bool ok = true;
2023                 const ProStringList &var = values(map(args.at(0)));
2024                 int start = 0, end = 0;
2025                 if (args.count() >= 2) {
2026                     const QString &start_str = args.at(1).toQString(m_tmp1);
2027                     start = start_str.toInt(&ok);
2028                     if (!ok) {
2029                         if (args.count() == 2) {
2030                             int dotdot = start_str.indexOf(statics.strDotDot);
2031                             if (dotdot != -1) {
2032                                 start = start_str.left(dotdot).toInt(&ok);
2033                                 if (ok)
2034                                     end = start_str.mid(dotdot+2).toInt(&ok);
2035                             }
2036                         }
2037                         if (!ok)
2038                             evalError(fL1S("member() argument 2 (start) '%2' invalid.")
2039                                       .arg(start_str));
2040                     } else {
2041                         end = start;
2042                         if (args.count() == 3)
2043                             end = args.at(2).toQString(m_tmp1).toInt(&ok);
2044                         if (!ok)
2045                             evalError(fL1S("member() argument 3 (end) '%2' invalid.\n")
2046                                       .arg(args.at(2).toQString(m_tmp1)));
2047                     }
2048                 }
2049                 if (ok) {
2050                     if (start < 0)
2051                         start += var.count();
2052                     if (end < 0)
2053                         end += var.count();
2054                     if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
2055                         //nothing
2056                     } else if (start < end) {
2057                         for (int i = start; i <= end && var.count() >= i; i++)
2058                             ret.append(var[i]);
2059                     } else {
2060                         for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
2061                             ret += var[i];
2062                     }
2063                 }
2064             }
2065             break;
2066         case E_FIRST:
2067         case E_LAST:
2068             if (args.count() != 1) {
2069                 evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1)));
2070             } else {
2071                 const ProStringList &var = values(map(args.at(0)));
2072                 if (!var.isEmpty()) {
2073                     if (func_t == E_FIRST)
2074                         ret.append(var[0]);
2075                     else
2076                         ret.append(var.last());
2077                 }
2078             }
2079             break;
2080         case E_SIZE:
2081             if(args.count() != 1)
2082                 evalError(fL1S("size(var) requires one argument."));
2083             else
2084                 ret.append(ProString(QString::number(values(map(args.at(0))).size()), NoHash));
2085             break;
2086         case E_CAT:
2087             if (args.count() < 1 || args.count() > 2) {
2088                 evalError(fL1S("cat(file, singleline=true) requires one or two arguments."));
2089             } else {
2090                 const QString &file = args.at(0).toQString(m_tmp1);
2091
2092                 bool singleLine = true;
2093                 if (args.count() > 1)
2094                     singleLine = isTrue(args.at(1), m_tmp2);
2095
2096                 QFile qfile(resolvePath(expandEnvVars(file)));
2097                 if (qfile.open(QIODevice::ReadOnly)) {
2098                     QTextStream stream(&qfile);
2099                     while (!stream.atEnd()) {
2100                         ret += split_value_list(stream.readLine().trimmed());
2101                         if (!singleLine)
2102                             ret += ProString("\n", NoHash);
2103                     }
2104                     qfile.close();
2105                 }
2106             }
2107             break;
2108         case E_FROMFILE:
2109             if (args.count() != 2) {
2110                 evalError(fL1S("fromfile(file, variable) requires two arguments."));
2111             } else {
2112                 QHash<ProString, ProStringList> vars;
2113                 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2114                 fn.detach();
2115                 if (evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2116                                      &vars, &m_functionDefs, EvalWithDefaults))
2117                     ret = vars.value(map(args.at(1)));
2118             }
2119             break;
2120         case E_EVAL:
2121             if (args.count() != 1) {
2122                 evalError(fL1S("eval(variable) requires one argument"));
2123             } else {
2124                 ret += values(map(args.at(0)));
2125             }
2126             break;
2127         case E_LIST: {
2128             QString tmp;
2129             tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++);
2130             ret = ProStringList(ProString(tmp, NoHash));
2131             ProStringList lst;
2132             foreach (const ProString &arg, args)
2133                 lst += split_value_list(arg.toQString(m_tmp1)); // Relies on deep copy
2134             m_valuemapStack.top()[ret.at(0)] = lst;
2135             break; }
2136         case E_FIND:
2137             if (args.count() != 2) {
2138                 evalError(fL1S("find(var, str) requires two arguments."));
2139             } else {
2140                 QRegExp regx(args.at(1).toQString());
2141                 int t = 0;
2142                 foreach (const ProString &val, values(map(args.at(0)))) {
2143                     if (regx.indexIn(val.toQString(m_tmp[t])) != -1)
2144                         ret += val;
2145                     t ^= 1;
2146                 }
2147             }
2148             break;
2149         case E_SYSTEM:
2150             if (!m_skipLevel) {
2151                 if (args.count() < 1 || args.count() > 2) {
2152                     evalError(fL1S("system(execute) requires one or two arguments."));
2153                 } else {
2154                     char buff[256];
2155                     FILE *proc = QT_POPEN(QString(QLatin1String("cd ")
2156                                            + IoUtils::shellQuote(currentDirectory())
2157                                            + QLatin1String(" && ") + args[0]).toLocal8Bit(), "r");
2158                     bool singleLine = true;
2159                     if (args.count() > 1)
2160                         singleLine = isTrue(args.at(1), m_tmp2);
2161                     QString output;
2162                     while (proc && !feof(proc)) {
2163                         int read_in = int(fread(buff, 1, 255, proc));
2164                         if (!read_in)
2165                             break;
2166                         for (int i = 0; i < read_in; i++) {
2167                             if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
2168                                 buff[i] = ' ';
2169                         }
2170                         buff[read_in] = '\0';
2171                         output += QString::fromLocal8Bit(buff);
2172                     }
2173                     ret += split_value_list(output);
2174                     if (proc)
2175                         QT_PCLOSE(proc);
2176                 }
2177             }
2178             break;
2179         case E_UNIQUE:
2180             if(args.count() != 1) {
2181                 evalError(fL1S("unique(var) requires one argument."));
2182             } else {
2183                 ret = values(map(args.at(0)));
2184                 ret.removeDuplicates();
2185             }
2186             break;
2187         case E_QUOTE:
2188             ret += args;
2189             break;
2190         case E_ESCAPE_EXPAND:
2191             for (int i = 0; i < args.size(); ++i) {
2192                 QString str = args.at(i).toQString();
2193                 QChar *i_data = str.data();
2194                 int i_len = str.length();
2195                 for (int x = 0; x < i_len; ++x) {
2196                     if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
2197                         if (*(i_data+x+1) == QLatin1Char('\\')) {
2198                             ++x;
2199                         } else {
2200                             struct {
2201                                 char in, out;
2202                             } mapped_quotes[] = {
2203                                 { 'n', '\n' },
2204                                 { 't', '\t' },
2205                                 { 'r', '\r' },
2206                                 { 0, 0 }
2207                             };
2208                             for (int i = 0; mapped_quotes[i].in; ++i) {
2209                                 if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
2210                                     *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
2211                                     if (x < i_len-2)
2212                                         memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
2213                                     --i_len;
2214                                     break;
2215                                 }
2216                             }
2217                         }
2218                     }
2219                 }
2220                 ret.append(ProString(QString(i_data, i_len), NoHash));
2221             }
2222             break;
2223         case E_RE_ESCAPE:
2224             for (int i = 0; i < args.size(); ++i) {
2225                 const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1));
2226                 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash));
2227             }
2228             break;
2229         case E_UPPER:
2230         case E_LOWER:
2231             for (int i = 0; i < args.count(); ++i) {
2232                 QString rstr = args.at(i).toQString(m_tmp1);
2233                 rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower();
2234                 ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr, NoHash));
2235             }
2236             break;
2237         case E_FILES:
2238             if (args.count() != 1 && args.count() != 2) {
2239                 evalError(fL1S("files(pattern, recursive=false) requires one or two arguments"));
2240             } else {
2241                 bool recursive = false;
2242                 if (args.count() == 2)
2243                     recursive = isTrue(args.at(1), m_tmp2);
2244                 QStringList dirs;
2245                 QString r = fixPathToLocalOS(args.at(0).toQString(m_tmp1));
2246                 QString pfx;
2247                 if (IoUtils::isRelativePath(r)) {
2248                     pfx = currentDirectory();
2249                     if (!pfx.endsWith(QLatin1Char('/')))
2250                         pfx += QLatin1Char('/');
2251                 }
2252                 int slash = r.lastIndexOf(QDir::separator());
2253                 if (slash != -1) {
2254                     dirs.append(r.left(slash+1));
2255                     r = r.mid(slash+1);
2256                 } else {
2257                     dirs.append(QString());
2258                 }
2259
2260                 r.detach(); // Keep m_tmp out of QRegExp's cache
2261                 const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
2262                 for (int d = 0; d < dirs.count(); d++) {
2263                     QString dir = dirs[d];
2264                     QDir qdir(pfx + dir);
2265                     for (int i = 0; i < (int)qdir.count(); ++i) {
2266                         if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot)
2267                             continue;
2268                         QString fname = dir + qdir[i];
2269                         if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) {
2270                             if (recursive)
2271                                 dirs.append(fname + QDir::separator());
2272                         }
2273                         if (regex.exactMatch(qdir[i]))
2274                             ret += ProString(fname, NoHash);
2275                     }
2276                 }
2277             }
2278             break;
2279         case E_REPLACE:
2280             if(args.count() != 3 ) {
2281                 evalError(fL1S("replace(var, before, after) requires three arguments"));
2282             } else {
2283                 const QRegExp before(args.at(1).toQString());
2284                 const QString &after(args.at(2).toQString(m_tmp2));
2285                 foreach (const ProString &val, values(map(args.at(0)))) {
2286                     QString rstr = val.toQString(m_tmp1);
2287                     QString copy = rstr; // Force a detach on modify
2288                     rstr.replace(before, after);
2289                     ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr, NoHash));
2290                 }
2291             }
2292             break;
2293         case 0:
2294             evalError(fL1S("'%1' is not a recognized replace function")
2295                       .arg(func.toQString(m_tmp1)));
2296             break;
2297         default:
2298             evalError(fL1S("Function '%1' is not implemented").arg(func.toQString(m_tmp1)));
2299             break;
2300     }
2301
2302     return ret;
2303 }
2304
2305 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2306         const ProString &function, const ProString &arguments)
2307 {
2308     QHash<ProString, FunctionDef>::ConstIterator it =
2309             m_functionDefs.testFunctions.constFind(function);
2310     if (it != m_functionDefs.testFunctions.constEnd())
2311         return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
2312
2313     //why don't the builtin functions just use args_list? --Sam
2314     int pos = 0;
2315     return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
2316 }
2317
2318 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2319         const ProString &function, const ushort *&tokPtr)
2320 {
2321     QHash<ProString, FunctionDef>::ConstIterator it =
2322             m_functionDefs.testFunctions.constFind(function);
2323     if (it != m_functionDefs.testFunctions.constEnd())
2324         return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
2325
2326     //why don't the builtin functions just use args_list? --Sam
2327     return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
2328 }
2329
2330 ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction(
2331         const ProString &function, const ProStringList &args)
2332 {
2333     TestFunc func_t = (TestFunc)statics.functions.value(function);
2334
2335     switch (func_t) {
2336         case T_DEFINED:
2337             if (args.count() < 1 || args.count() > 2) {
2338                 evalError(fL1S("defined(function, [\"test\"|\"replace\"])"
2339                                " requires one or two arguments."));
2340                 return ReturnFalse;
2341             }
2342             if (args.count() > 1) {
2343                 if (args[1] == QLatin1String("test"))
2344                     return returnBool(m_functionDefs.testFunctions.contains(args[0]));
2345                 else if (args[1] == QLatin1String("replace"))
2346                     return returnBool(m_functionDefs.replaceFunctions.contains(args[0]));
2347                 evalError(fL1S("defined(function, type): unexpected type [%1].\n")
2348                           .arg(args.at(1).toQString(m_tmp1)));
2349                 return ReturnFalse;
2350             }
2351             return returnBool(m_functionDefs.replaceFunctions.contains(args[0])
2352                               || m_functionDefs.testFunctions.contains(args[0]));
2353         case T_RETURN:
2354             m_returnValue = args;
2355             // It is "safe" to ignore returns - due to qmake brokeness
2356             // they cannot be used to terminate loops anyway.
2357             if (m_skipLevel || m_cumulative)
2358                 return ReturnTrue;
2359             if (m_valuemapStack.isEmpty()) {
2360                 evalError(fL1S("unexpected return()."));
2361                 return ReturnFalse;
2362             }
2363             return ReturnReturn;
2364         case T_EXPORT: {
2365             if (m_skipLevel && !m_cumulative)
2366                 return ReturnTrue;
2367             if (args.count() != 1) {
2368                 evalError(fL1S("export(variable) requires one argument."));
2369                 return ReturnFalse;
2370             }
2371             const ProString &var = map(args.at(0));
2372             for (int i = m_valuemapStack.size(); --i > 0; ) {
2373                 QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(var);
2374                 if (it != m_valuemapStack.at(i).end()) {
2375                     if (it->constBegin() == statics.fakeValue.constBegin()) {
2376                         // This is stupid, but qmake doesn't propagate deletions
2377                         m_valuemapStack[0][var] = ProStringList();
2378                     } else {
2379                         m_valuemapStack[0][var] = *it;
2380                     }
2381                     m_valuemapStack[i].erase(it);
2382                     while (--i)
2383                         m_valuemapStack[i].remove(var);
2384                     break;
2385                 }
2386             }
2387             return ReturnTrue;
2388         }
2389         case T_INFILE:
2390             if (args.count() < 2 || args.count() > 3) {
2391                 evalError(fL1S("infile(file, var, [values]) requires two or three arguments."));
2392             } else {
2393                 QHash<ProString, ProStringList> vars;
2394                 QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2395                 fn.detach();
2396                 if (!evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2397                                       &vars, &m_functionDefs, EvalWithDefaults))
2398                     return ReturnFalse;
2399                 if (args.count() == 2)
2400                     return returnBool(vars.contains(args.at(1)));
2401                 QRegExp regx;
2402                 const QString &qry = args.at(2).toQString(m_tmp1);
2403                 if (qry != QRegExp::escape(qry)) {
2404                     QString copy = qry;
2405                     copy.detach();
2406                     regx.setPattern(copy);
2407                 }
2408                 int t = 0;
2409                 foreach (const ProString &s, vars.value(map(args.at(1)))) {
2410                     if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry)
2411                         return ReturnTrue;
2412                     t ^= 1;
2413                 }
2414             }
2415             return ReturnFalse;
2416 #if 0
2417         case T_REQUIRES:
2418 #endif
2419         case T_EVAL: {
2420                 ProFile *pro = m_parser->parsedProFile(fL1S("(eval)"), false,
2421                                                        args.join(statics.field_sep));
2422                 if (!pro)
2423                     return ReturnFalse;
2424                 m_locationStack.push(m_current);
2425                 VisitReturn ret = visitProBlock(pro, pro->tokPtr());
2426                 m_current = m_locationStack.pop();
2427                 pro->deref();
2428                 return ret;
2429             }
2430         case T_BREAK:
2431             if (m_skipLevel)
2432                 return ReturnFalse;
2433             if (m_loopLevel)
2434                 return ReturnBreak;
2435             evalError(fL1S("unexpected break()."));
2436             return ReturnFalse;
2437         case T_NEXT:
2438             if (m_skipLevel)
2439                 return ReturnFalse;
2440             if (m_loopLevel)
2441                 return ReturnNext;
2442             evalError(fL1S("unexpected next()."));
2443             return ReturnFalse;
2444         case T_IF: {
2445             if (m_skipLevel && !m_cumulative)
2446                 return ReturnFalse;
2447             if (args.count() != 1) {
2448                 evalError(fL1S("if(condition) requires one argument."));
2449                 return ReturnFalse;
2450             }
2451             const ProString &cond = args.at(0);
2452             bool quoted = false;
2453             bool ret = true;
2454             bool orOp = false;
2455             bool invert = false;
2456             bool isFunc = false;
2457             int parens = 0;
2458             QString test;
2459             test.reserve(20);
2460             QString argsString;
2461             argsString.reserve(50);
2462             const QChar *d = cond.constData();
2463             const QChar *ed = d + cond.size();
2464             while (d < ed) {
2465                 ushort c = (d++)->unicode();
2466                 bool isOp = false;
2467                 if (quoted) {
2468                     if (c == '"')
2469                         quoted = false;
2470                     else if (c == '!' && test.isEmpty())
2471                         invert = true;
2472                     else
2473                         test += c;
2474                 } else if (c == '(') {
2475                     isFunc = true;
2476                     if (parens)
2477                         argsString += c;
2478                     ++parens;
2479                 } else if (c == ')') {
2480                     --parens;
2481                     if (parens)
2482                         argsString += c;
2483                 } else if (!parens) {
2484                     if (c == '"')
2485                         quoted = true;
2486                     else if (c == ':' || c == '|')
2487                         isOp = true;
2488                     else if (c == '!' && test.isEmpty())
2489                         invert = true;
2490                     else
2491                         test += c;
2492                 } else {
2493                     argsString += c;
2494                 }
2495                 if (!quoted && !parens && (isOp || d == ed)) {
2496                     if (m_cumulative || (orOp != ret)) {
2497                         test = test.trimmed();
2498                         if (isFunc)
2499                             ret = evaluateConditionalFunction(ProString(test), ProString(argsString, NoHash));
2500                         else
2501                             ret = isActiveConfig(test, true);
2502                         ret ^= invert;
2503                     }
2504                     orOp = (c == '|');
2505                     invert = false;
2506                     isFunc = false;
2507                     test.clear();
2508                     argsString.clear();
2509                 }
2510             }
2511             return returnBool(ret);
2512         }
2513         case T_CONFIG: {
2514             if (args.count() < 1 || args.count() > 2) {
2515                 evalError(fL1S("CONFIG(config) requires one or two arguments."));
2516                 return ReturnFalse;
2517             }
2518             if (args.count() == 1)
2519                 return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2)));
2520             const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|'));
2521             const ProStringList &configs = valuesDirect(statics.strCONFIG);
2522
2523             for (int i = configs.size() - 1; i >= 0; i--) {
2524                 for (int mut = 0; mut < mutuals.count(); mut++) {
2525                     if (configs[i] == mutuals[mut].trimmed()) {
2526                         return returnBool(configs[i] == args[0]);
2527                     }
2528                 }
2529             }
2530             return ReturnFalse;
2531         }
2532         case T_CONTAINS: {
2533             if (args.count() < 2 || args.count() > 3) {
2534                 evalError(fL1S("contains(var, val) requires two or three arguments."));
2535                 return ReturnFalse;
2536             }
2537
2538             const QString &qry = args.at(1).toQString(m_tmp1);
2539             QRegExp regx;
2540             if (qry != QRegExp::escape(qry)) {
2541                 QString copy = qry;
2542                 copy.detach();
2543                 regx.setPattern(copy);
2544             }
2545             const ProStringList &l = values(map(args.at(0)));
2546             if (args.count() == 2) {
2547                 int t = 0;
2548                 for (int i = 0; i < l.size(); ++i) {
2549                     const ProString &val = l[i];
2550                     if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry)
2551                         return ReturnTrue;
2552                     t ^= 1;
2553                 }
2554             } else {
2555                 const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|'));
2556                 for (int i = l.size() - 1; i >= 0; i--) {
2557                     const ProString val = l[i];
2558                     for (int mut = 0; mut < mutuals.count(); mut++) {
2559                         if (val == mutuals[mut].trimmed()) {
2560                             return returnBool((!regx.isEmpty()
2561                                                && regx.exactMatch(val.toQString(m_tmp2)))
2562                                               || val == qry);
2563                         }
2564                     }
2565                 }
2566             }
2567             return ReturnFalse;
2568         }
2569         case T_COUNT: {
2570             if (args.count() != 2 && args.count() != 3) {
2571                 evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments."));
2572                 return ReturnFalse;
2573             }
2574             int cnt = values(map(args.at(0))).count();
2575             if (args.count() == 3) {
2576                 const ProString &comp = args.at(2);
2577                 const int val = args.at(1).toQString(m_tmp1).toInt();
2578                 if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
2579                     return returnBool(cnt > val);
2580                 } else if (comp == QLatin1String(">=")) {
2581                     return returnBool(cnt >= val);
2582                 } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
2583                     return returnBool(cnt < val);
2584                 } else if (comp == QLatin1String("<=")) {
2585                     return returnBool(cnt <= val);
2586                 } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
2587                            || comp == QLatin1String("=") || comp == QLatin1String("==")) {
2588                     return returnBool(cnt == val);
2589                 } else {
2590                     evalError(fL1S("unexpected modifier to count(%2)").arg(comp.toQString(m_tmp1)));
2591                     return ReturnFalse;
2592                 }
2593             }
2594             return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt());
2595         }
2596         case T_GREATERTHAN:
2597         case T_LESSTHAN: {
2598             if (args.count() != 2) {
2599                 evalError(fL1S("%1(variable, value) requires two arguments.")
2600                           .arg(function.toQString(m_tmp1)));
2601                 return ReturnFalse;
2602             }
2603             const QString &rhs(args.at(1).toQString(m_tmp1)),
2604                           &lhs(values(map(args.at(0))).join(statics.field_sep));
2605             bool ok;
2606             int rhs_int = rhs.toInt(&ok);
2607             if (ok) { // do integer compare
2608                 int lhs_int = lhs.toInt(&ok);
2609                 if (ok) {
2610                     if (func_t == T_GREATERTHAN)
2611                         return returnBool(lhs_int > rhs_int);
2612                     return returnBool(lhs_int < rhs_int);
2613                 }
2614             }
2615             if (func_t == T_GREATERTHAN)
2616                 return returnBool(lhs > rhs);
2617             return returnBool(lhs < rhs);
2618         }
2619         case T_EQUALS:
2620             if (args.count() != 2) {
2621                 evalError(fL1S("%1(variable, value) requires two arguments.")
2622                           .arg(function.toQString(m_tmp1)));
2623                 return ReturnFalse;
2624             }
2625             return returnBool(values(map(args.at(0))).join(statics.field_sep)
2626                               == args.at(1).toQString(m_tmp1));
2627         case T_CLEAR: {
2628             if (m_skipLevel && !m_cumulative)
2629                 return ReturnFalse;
2630             if (args.count() != 1) {
2631                 evalError(fL1S("%1(variable) requires one argument.")
2632                           .arg(function.toQString(m_tmp1)));
2633                 return ReturnFalse;
2634             }
2635             QHash<ProString, ProStringList> *hsh;
2636             QHash<ProString, ProStringList>::Iterator it;
2637             const ProString &var = map(args.at(0));
2638             if (!(hsh = findValues(var, &it)))
2639                 return ReturnFalse;
2640             if (hsh == &m_valuemapStack.top())
2641                 it->clear();
2642             else
2643                 m_valuemapStack.top()[var].clear();
2644             return ReturnTrue;
2645         }
2646         case T_UNSET: {
2647             if (m_skipLevel && !m_cumulative)
2648                 return ReturnFalse;
2649             if (args.count() != 1) {
2650                 evalError(fL1S("%1(variable) requires one argument.")
2651                           .arg(function.toQString(m_tmp1)));
2652                 return ReturnFalse;
2653             }
2654             QHash<ProString, ProStringList> *hsh;
2655             QHash<ProString, ProStringList>::Iterator it;
2656             const ProString &var = map(args.at(0));
2657             if (!(hsh = findValues(var, &it)))
2658                 return ReturnFalse;
2659             if (m_valuemapStack.size() == 1)
2660                 hsh->erase(it);
2661             else if (hsh == &m_valuemapStack.top())
2662                 *it = statics.fakeValue;
2663             else
2664                 m_valuemapStack.top()[var] = statics.fakeValue;
2665             return ReturnTrue;
2666         }
2667         case T_INCLUDE: {
2668             if (m_skipLevel && !m_cumulative)
2669                 return ReturnFalse;
2670             QString parseInto;
2671             // the third optional argument to include() controls warnings
2672             //      and is not used here
2673             if ((args.count() == 2) || (args.count() == 3) ) {
2674                 parseInto = args.at(1).toQString(m_tmp2);
2675             } else if (args.count() != 1) {
2676                 evalError(fL1S("include(file, into, silent) requires one, two or three arguments."));
2677                 return ReturnFalse;
2678             }
2679             QString fn = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2680             fn.detach();
2681             bool ok;
2682             if (parseInto.isEmpty()) {
2683                 ok = evaluateFile(fn, ProFileEvaluatorHandler::EvalIncludeFile,
2684                                   ProFileEvaluator::LoadProOnly);
2685             } else {
2686                 QHash<ProString, ProStringList> symbols;
2687                 if ((ok = evaluateFileInto(fn, ProFileEvaluatorHandler::EvalAuxFile,
2688                                            &symbols, 0, EvalWithSetup))) {
2689                     QHash<ProString, ProStringList> newMap;
2690                     for (QHash<ProString, ProStringList>::ConstIterator
2691                             it = m_valuemapStack.top().constBegin(),
2692                             end = m_valuemapStack.top().constEnd();
2693                             it != end; ++it) {
2694                         const QString &ky = it.key().toQString(m_tmp1);
2695                         if (!(ky.startsWith(parseInto) &&
2696                               (ky.length() == parseInto.length()
2697                                || ky.at(parseInto.length()) == QLatin1Char('.'))))
2698                             newMap[it.key()] = it.value();
2699                     }
2700                     for (QHash<ProString, ProStringList>::ConstIterator it = symbols.constBegin();
2701                          it != symbols.constEnd(); ++it) {
2702                         const QString &ky = it.key().toQString(m_tmp1);
2703                         if (!ky.startsWith(QLatin1Char('.')))
2704                             newMap.insert(ProString(parseInto + QLatin1Char('.') + ky), it.value());
2705                     }
2706                     m_valuemapStack.top() = newMap;
2707                 }
2708             }
2709             return returnBool(ok);
2710         }
2711         case T_LOAD: {
2712             if (m_skipLevel && !m_cumulative)
2713                 return ReturnFalse;
2714             bool ignore_error = false;
2715             if (args.count() == 2) {
2716                 ignore_error = isTrue(args.at(1), m_tmp2);
2717             } else if (args.count() != 1) {
2718                 evalError(fL1S("load(feature) requires one or two arguments."));
2719                 return ReturnFalse;
2720             }
2721             // XXX ignore_error unused
2722             return returnBool(evaluateFeatureFile(expandEnvVars(args.at(0).toQString())));
2723         }
2724         case T_DEBUG:
2725             // Yup - do nothing. Nothing is going to enable debug output anyway.
2726             return ReturnFalse;
2727         case T_MESSAGE: {
2728             if (args.count() != 1) {
2729                 evalError(fL1S("%1(message) requires one argument.")
2730                           .arg(function.toQString(m_tmp1)));
2731                 return ReturnFalse;
2732             }
2733             const QString &msg = expandEnvVars(args.at(0).toQString(m_tmp2));
2734             if (!m_skipLevel)
2735                 m_handler->fileMessage(fL1S("Project %1: %2")
2736                                        .arg(function.toQString(m_tmp1).toUpper(), msg));
2737             // ### Consider real termination in non-cumulative mode
2738             return returnBool(function != QLatin1String("error"));
2739         }
2740 #if 0 // Way too dangerous to enable.
2741         case T_SYSTEM: {
2742             if (args.count() != 1) {
2743                 evalError(fL1S("system(exec) requires one argument."));
2744                 ReturnFalse;
2745             }
2746             return returnBool(system((QLatin1String("cd ")
2747                                       + IoUtils::shellQuote(currentDirectory())
2748                                       + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0);
2749         }
2750 #endif
2751         case T_ISEMPTY: {
2752             if (args.count() != 1) {
2753                 evalError(fL1S("isEmpty(var) requires one argument."));
2754                 return ReturnFalse;
2755             }
2756             const ProStringList &sl = values(map(args.at(0)));
2757             if (sl.count() == 0) {
2758                 return ReturnTrue;
2759             } else if (sl.count() > 0) {
2760                 const ProString &var = sl.first();
2761                 if (var.isEmpty())
2762                     return ReturnTrue;
2763             }
2764             return ReturnFalse;
2765         }
2766         case T_EXISTS: {
2767             if (args.count() != 1) {
2768                 evalError(fL1S("exists(file) requires one argument."));
2769                 return ReturnFalse;
2770             }
2771             const QString &file = resolvePath(expandEnvVars(args.at(0).toQString(m_tmp1)));
2772
2773             if (IoUtils::exists(file)) {
2774                 return ReturnTrue;
2775             }
2776             int slsh = file.lastIndexOf(QLatin1Char('/'));
2777             QString fn = file.mid(slsh+1);
2778             if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) {
2779                 QString dirstr = file.left(slsh+1);
2780                 if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty())
2781                     return ReturnTrue;
2782             }
2783
2784             return ReturnFalse;
2785         }
2786         case 0:
2787             evalError(fL1S("'%1' is not a recognized test function")
2788                       .arg(function.toQString(m_tmp1)));
2789             return ReturnFalse;
2790         default:
2791             evalError(fL1S("Function '%1' is not implemented").arg(function.toQString(m_tmp1)));
2792             return ReturnFalse;
2793     }
2794 }
2795
2796 QHash<ProString, ProStringList> *ProFileEvaluator::Private::findValues(
2797         const ProString &variableName, QHash<ProString, ProStringList>::Iterator *rit)
2798 {
2799     for (int i = m_valuemapStack.size(); --i >= 0; ) {
2800         QHash<ProString, ProStringList>::Iterator it = m_valuemapStack[i].find(variableName);
2801         if (it != m_valuemapStack[i].end()) {
2802             if (it->constBegin() == statics.fakeValue.constBegin())
2803                 return 0;
2804             *rit = it;
2805             return &m_valuemapStack[i];
2806         }
2807     }
2808     return 0;
2809 }
2810
2811 ProStringList &ProFileEvaluator::Private::valuesRef(const ProString &variableName)
2812 {
2813     QHash<ProString, ProStringList>::Iterator it = m_valuemapStack.top().find(variableName);
2814     if (it != m_valuemapStack.top().end())
2815         return *it;
2816     for (int i = m_valuemapStack.size() - 1; --i >= 0; ) {
2817         QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
2818         if (it != m_valuemapStack.at(i).constEnd()) {
2819             ProStringList &ret = m_valuemapStack.top()[variableName];
2820             ret = *it;
2821             return ret;
2822         }
2823     }
2824     return m_valuemapStack.top()[variableName];
2825 }
2826
2827 ProStringList ProFileEvaluator::Private::valuesDirect(const ProString &variableName) const
2828 {
2829     for (int i = m_valuemapStack.size(); --i >= 0; ) {
2830         QHash<ProString, ProStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName);
2831         if (it != m_valuemapStack.at(i).constEnd()) {
2832             if (it->constBegin() == statics.fakeValue.constBegin())
2833                 break;
2834             return *it;
2835         }
2836     }
2837     return ProStringList();
2838 }
2839
2840 ProStringList ProFileEvaluator::Private::values(const ProString &variableName) const
2841 {
2842     QHash<ProString, int>::ConstIterator vli = statics.varList.find(variableName);
2843     if (vli != statics.varList.constEnd()) {
2844         int vlidx = *vli;
2845         QString ret;
2846         switch ((VarName)vlidx) {
2847         case V_LITERAL_WHITESPACE: ret = QLatin1String("\t"); break;
2848         case V_LITERAL_DOLLAR: ret = QLatin1String("$"); break;
2849         case V_LITERAL_HASH: ret = QLatin1String("#"); break;
2850         case V_OUT_PWD: // the outgoing dir (shadow of _PRO_FILE_PWD_)
2851             ret = m_outputDir;
2852             break;
2853         case V_PWD: // containing directory of most nested project/include file
2854         case V_IN_PWD:
2855             ret = currentDirectory();
2856             break;
2857         case V_DIR_SEPARATOR:
2858             ret = m_option->dir_sep;
2859             break;
2860         case V_DIRLIST_SEPARATOR:
2861             ret = m_option->dirlist_sep;
2862             break;
2863         case V__LINE_: // currently executed line number
2864             ret = QString::number(m_current.line);
2865             break;
2866         case V__FILE_: // currently executed file
2867             ret = m_current.pro->fileName();
2868             break;
2869         case V__DATE_: //current date/time
2870             ret = QDateTime::currentDateTime().toString();
2871             break;
2872         case V__PRO_FILE_:
2873             ret = m_profileStack.first()->fileName();
2874             break;
2875         case V__PRO_FILE_PWD_:
2876             ret = m_profileStack.first()->directoryName();
2877             break;
2878         case V__QMAKE_CACHE_:
2879             ret = m_option->cachefile;
2880             break;
2881 #if defined(Q_OS_WIN32)
2882         case V_QMAKE_HOST_os: ret = QLatin1String("Windows"); break;
2883         case V_QMAKE_HOST_name: {
2884             DWORD name_length = 1024;
2885             TCHAR name[1024];
2886             if (GetComputerName(name, &name_length))
2887                 ret = QString::fromUtf16((ushort*)name, name_length);
2888             break;
2889         }
2890         case V_QMAKE_HOST_version:
2891             ret = QString::number(QSysInfo::WindowsVersion);
2892             break;
2893         case V_QMAKE_HOST_version_string:
2894             switch (QSysInfo::WindowsVersion) {
2895             case QSysInfo::WV_Me: ret = QLatin1String("WinMe"); break;
2896             case QSysInfo::WV_95: ret = QLatin1String("Win95"); break;
2897             case QSysInfo::WV_98: ret = QLatin1String("Win98"); break;
2898             case QSysInfo::WV_NT: ret = QLatin1String("WinNT"); break;
2899             case QSysInfo::WV_2000: ret = QLatin1String("Win2000"); break;
2900             case QSysInfo::WV_2003: ret = QLatin1String("Win2003"); break;
2901             case QSysInfo::WV_XP: ret = QLatin1String("WinXP"); break;
2902             case QSysInfo::WV_VISTA: ret = QLatin1String("WinVista"); break;
2903             default: ret = QLatin1String("Unknown"); break;
2904             }
2905             break;
2906         case V_QMAKE_HOST_arch:
2907             SYSTEM_INFO info;
2908             GetSystemInfo(&info);
2909             switch(info.wProcessorArchitecture) {
2910 #ifdef PROCESSOR_ARCHITECTURE_AMD64
2911             case PROCESSOR_ARCHITECTURE_AMD64:
2912                 ret = QLatin1String("x86_64");
2913                 break;
2914 #endif
2915             case PROCESSOR_ARCHITECTURE_INTEL:
2916                 ret = QLatin1String("x86");
2917                 break;
2918             case PROCESSOR_ARCHITECTURE_IA64:
2919 #ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
2920             case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
2921 #endif
2922                 ret = QLatin1String("IA64");
2923                 break;
2924             default:
2925                 ret = QLatin1String("Unknown");
2926                 break;
2927             }
2928             break;
2929 #elif defined(Q_OS_UNIX)
2930         case V_QMAKE_HOST_os:
2931         case V_QMAKE_HOST_name:
2932         case V_QMAKE_HOST_version:
2933         case V_QMAKE_HOST_version_string:
2934         case V_QMAKE_HOST_arch:
2935             {
2936                 struct utsname name;
2937                 const char *what;
2938                 if (!uname(&name)) {
2939                     switch (vlidx) {
2940                     case V_QMAKE_HOST_os: what = name.sysname; break;
2941                     case V_QMAKE_HOST_name: what = name.nodename; break;
2942                     case V_QMAKE_HOST_version: what = name.release; break;
2943                     case V_QMAKE_HOST_version_string: what = name.version; break;
2944                     case V_QMAKE_HOST_arch: what = name.machine; break;
2945                     }
2946                     ret = QString::fromLocal8Bit(what);
2947                 }
2948             }
2949 #endif
2950         }
2951         return ProStringList(ProString(ret, NoHash));
2952     }
2953
2954     ProStringList result = valuesDirect(variableName);
2955     if (result.isEmpty()) {
2956         if (variableName == statics.strTEMPLATE) {
2957             result.append(ProString("app", NoHash));
2958         } else if (variableName == statics.strQMAKE_DIR_SEP) {
2959             result.append(ProString(m_option->dirlist_sep, NoHash));
2960         }
2961     }
2962     return result;
2963 }
2964
2965 bool ProFileEvaluator::Private::evaluateFileDirect(
2966         const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
2967         ProFileEvaluator::LoadFlags flags)
2968 {
2969     if (ProFile *pro = m_parser->parsedProFile(fileName, true)) {
2970         m_locationStack.push(m_current);
2971         bool ok = (visitProFile(pro, type, flags) == ReturnTrue);
2972         m_current = m_locationStack.pop();
2973         pro->deref();
2974         return ok;
2975     } else {
2976         return false;
2977     }
2978 }
2979
2980 bool ProFileEvaluator::Private::evaluateFile(
2981         const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
2982         ProFileEvaluator::LoadFlags flags)
2983 {
2984     if (fileName.isEmpty())
2985         return false;
2986     foreach (const ProFile *pf, m_profileStack)
2987         if (pf->fileName() == fileName) {
2988             evalError(fL1S("circular inclusion of %1").arg(fileName));
2989             return false;
2990         }
2991     return evaluateFileDirect(fileName, type, flags);
2992 }
2993
2994 bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
2995 {
2996     QString fn = fileName;
2997     if (!fn.endsWith(QLatin1String(".prf")))
2998         fn += QLatin1String(".prf");
2999
3000     if (!fileName.contains((ushort)'/') || !IoUtils::exists(resolvePath(fn))) {
3001         if (m_option->feature_roots.isEmpty())
3002             m_option->feature_roots = qmakeFeaturePaths();
3003         int start_root = 0;
3004         QString currFn = currentFileName();
3005         if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
3006             for (int root = 0; root < m_option->feature_roots.size(); ++root)
3007                 if (currFn == m_option->feature_roots.at(root) + fn) {
3008                     start_root = root + 1;
3009                     break;
3010                 }
3011         }
3012         for (int root = start_root; root < m_option->feature_roots.size(); ++root) {
3013             QString fname = m_option->feature_roots.at(root) + fn;
3014             if (IoUtils::exists(fname)) {
3015                 fn = fname;
3016                 goto cool;
3017             }
3018         }
3019         return false;
3020
3021       cool:
3022         // It's beyond me why qmake has this inside this if ...
3023         ProStringList &already = valuesRef(ProString("QMAKE_INTERNAL_INCLUDED_FEATURES"));
3024         ProString afn(fn, NoHash);
3025         if (already.contains(afn))
3026             return true;
3027         already.append(afn);
3028     } else {
3029         fn = resolvePath(fn);
3030     }
3031
3032     bool cumulative = m_cumulative;
3033     m_cumulative = false;
3034
3035     // The path is fully normalized already.
3036     bool ok = evaluateFileDirect(fn, ProFileEvaluatorHandler::EvalFeatureFile,
3037                                  ProFileEvaluator::LoadProOnly);
3038
3039     m_cumulative = cumulative;
3040     return ok;
3041 }
3042
3043 bool ProFileEvaluator::Private::evaluateFileInto(
3044         const QString &fileName, ProFileEvaluatorHandler::EvalFileType type,
3045         QHash<ProString, ProStringList> *values, FunctionDefs *funcs, EvalIntoMode mode)
3046 {
3047     ProFileEvaluator visitor(m_option, m_parser, m_handler);
3048     visitor.d->m_cumulative = false;
3049     visitor.d->m_outputDir = m_outputDir;
3050 //    visitor.d->m_valuemapStack.top() = *values;
3051     if (funcs)
3052         visitor.d->m_functionDefs = *funcs;
3053     if (mode == EvalWithDefaults)
3054         visitor.d->evaluateFeatureFile(QLatin1String("default_pre.prf"));
3055     if (!visitor.d->evaluateFile(fileName, type,
3056             (mode == EvalWithSetup) ? ProFileEvaluator::LoadAll : ProFileEvaluator::LoadProOnly))
3057         return false;
3058     *values = visitor.d->m_valuemapStack.top();
3059 //    if (funcs)
3060 //        *funcs = visitor.d->m_functionDefs;
3061     return true;
3062 }
3063
3064 void ProFileEvaluator::Private::evalError(const QString &message) const
3065 {
3066     if (!m_skipLevel)
3067         m_handler->evalError(m_current.pro->fileName(), m_current.line, message);
3068 }
3069
3070
3071 ///////////////////////////////////////////////////////////////////////
3072 //
3073 // ProFileEvaluator
3074 //
3075 ///////////////////////////////////////////////////////////////////////
3076
3077 void ProFileEvaluator::initialize()
3078 {
3079     Private::initStatics();
3080 }
3081
3082 ProFileEvaluator::ProFileEvaluator(ProFileOption *option, ProFileParser *parser,
3083                                    ProFileEvaluatorHandler *handler)
3084   : d(new Private(this, option, parser, handler))
3085 {
3086 }
3087
3088 ProFileEvaluator::~ProFileEvaluator()
3089 {
3090     delete d;
3091 }
3092
3093 bool ProFileEvaluator::contains(const QString &variableName) const
3094 {
3095     return d->m_valuemapStack.top().contains(ProString(variableName));
3096 }
3097
3098 static QStringList expandEnvVars(const ProStringList &x)
3099 {
3100     QStringList ret;
3101     foreach (const ProString &str, x)
3102         ret << expandEnvVars(str.toQString());
3103     return ret;
3104 }
3105
3106 QString ProFileEvaluator::value(const QString &variable) const
3107 {
3108     const QStringList &vals = values(variable);
3109     if (!vals.isEmpty())
3110         return vals.first();
3111
3112     return QString();
3113 }
3114
3115 QStringList ProFileEvaluator::values(const QString &variableName) const
3116 {
3117     return expandEnvVars(d->values(ProString(variableName)));
3118 }
3119
3120 QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
3121 {
3122     // It makes no sense to put any kind of magic into expanding these
3123     return expandEnvVars(d->m_filevaluemap.value(pro).value(ProString(variableName)));
3124 }
3125
3126 QStringList ProFileEvaluator::absolutePathValues(
3127         const QString &variable, const QString &baseDirectory) const
3128 {
3129     QStringList result;
3130     foreach (const QString &el, values(variable)) {
3131         QString absEl = IoUtils::resolvePath(baseDirectory, el);
3132         if (IoUtils::fileType(absEl) == IoUtils::FileIsDir)
3133             result << QDir::cleanPath(absEl);
3134     }
3135     return result;
3136 }
3137
3138 QStringList ProFileEvaluator::absoluteFileValues(
3139         const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
3140         const ProFile *pro) const
3141 {
3142     QStringList result;
3143     foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
3144         QString absEl;
3145         if (IoUtils::isAbsolutePath(el)) {
3146             if (IoUtils::exists(el)) {
3147                 result << QDir::cleanPath(el);
3148                 goto next;
3149             }
3150             absEl = el;
3151         } else {
3152             foreach (const QString &dir, searchDirs) {
3153                 QString fn = dir + QLatin1Char('/') + el;
3154                 if (IoUtils::exists(fn)) {
3155                     result << QDir::cleanPath(fn);
3156                     goto next;
3157                 }
3158             }
3159             if (baseDirectory.isEmpty())
3160                 goto next;
3161             absEl = baseDirectory + QLatin1Char('/') + el;
3162         }
3163         {
3164             absEl = QDir::cleanPath(absEl);
3165             int nameOff = absEl.lastIndexOf(QLatin1Char('/'));
3166             QString absDir = d->m_tmp1.setRawData(absEl.constData(), nameOff);
3167             if (IoUtils::exists(absDir)) {
3168                 QString wildcard = d->m_tmp2.setRawData(absEl.constData() + nameOff + 1,
3169                                                         absEl.length() - nameOff - 1);
3170                 if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
3171                     QDir theDir(absDir);
3172                     foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
3173                         if (fn != statics.strDot && fn != statics.strDotDot)
3174                             result << absDir + QLatin1Char('/') + fn;
3175                 } // else if (acceptMissing)
3176             }
3177         }
3178       next: ;
3179     }
3180     return result;
3181 }
3182
3183 ProFileEvaluator::TemplateType ProFileEvaluator::templateType() const
3184 {
3185     const ProStringList &templ = d->values(statics.strTEMPLATE);
3186     if (templ.count() >= 1) {
3187         const QString &t = templ.at(0).toQString();
3188         if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
3189             return TT_Application;
3190         if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
3191             return TT_Library;
3192         if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
3193             return TT_Script;
3194         if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
3195             return TT_Subdirs;
3196     }
3197     return TT_Unknown;
3198 }
3199
3200 bool ProFileEvaluator::accept(ProFile *pro, LoadFlags flags)
3201 {
3202     return d->visitProFile(pro, ProFileEvaluatorHandler::EvalProjectFile, flags);
3203 }
3204
3205 QString ProFileEvaluator::propertyValue(const QString &name) const
3206 {
3207     return d->propertyValue(name, false);
3208 }
3209
3210 void ProFileEvaluator::setCumulative(bool on)
3211 {
3212     d->m_cumulative = on;
3213 }
3214
3215 void ProFileEvaluator::setOutputDir(const QString &dir)
3216 {
3217     d->m_outputDir = dir;
3218 }
3219
3220 void ProFileEvaluator::setConfigCommandLineArguments(const QStringList &addUserConfigCmdArgs, const QStringList &removeUserConfigCmdArgs)
3221 {
3222     d->m_addUserConfigCmdArgs = addUserConfigCmdArgs;
3223     d->m_removeUserConfigCmdArgs = removeUserConfigCmdArgs;
3224 }
3225
3226 QT_END_NAMESPACE