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

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
9 deleted
22 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/tools/linguist/shared/abstractproitemvisitor.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5050{
    5151    virtual ~AbstractProItemVisitor() {}
    52     virtual bool visitBeginProBlock(ProBlock *block) = 0;
    53     virtual bool visitEndProBlock(ProBlock *block) = 0;
    5452
    55     virtual bool visitBeginProVariable(ProVariable *variable) = 0;
    56     virtual bool visitEndProVariable(ProVariable *variable) = 0;
     53    virtual ) = 0;
     54    virtual ) = 0;
    5755
    58     virtual bool visitBeginProFile(ProFile *value) = 0;
    59     virtual bool visitEndProFile(ProFile *value) = 0;
     56    virtual ) = 0;
     57    virtual ) = 0;
    6058
    61     virtual bool visitProValue(ProValue *value) = 0;
    62     virtual bool visitProFunction(ProFunction *function) = 0;
    63     virtual bool visitProOperator(ProOperator *function) = 0;
    64     virtual bool visitProCondition(ProCondition *function) = 0;
     59    virtual void visitBeginProVariable(ProVariable *variable) = 0;
     60    virtual void visitEndProVariable(ProVariable *variable) = 0;
     61
     62    virtual ProItem::ProItemReturn visitBeginProFile(ProFile *value) = 0;
     63    virtual ProItem::ProItemReturn visitEndProFile(ProFile *value) = 0;
     64
     65    virtual void visitProValue(ProValue *value) = 0;
     66    virtual ProItem::ProItemReturn visitProFunction(ProFunction *function) = 0;
     67    virtual void visitProOperator(ProOperator *function) = 0;
     68    virtual void visitProCondition(ProCondition *function) = 0;
    6569};
    6670
  • trunk/tools/linguist/shared/formats.pri

    r2 r561  
    2020    $$PWD/po.cpp \
    2121    $$PWD/ts.cpp \
    22     $$PWD/ui.cpp \
    23     $$PWD/cpp.cpp \
    24     $$PWD/java.cpp \
    25     $$PWD/qscript.cpp \
    2622    $$PWD/xliff.cpp
  • trunk/tools/linguist/shared/numerus.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4242#include "translator.h"
    4343
    44 #include <QtCore/QCoreApplication>
    4544#include <QtCore/QByteArray>
    4645#include <QtCore/QDebug>
     
    6160    { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
    6261      Q_NEQ, 0 };
     62
     63
    6364static const uchar irishStyleRules[] =
    6465    { Q_EQ, 1, Q_NEWRULE,
    6566      Q_EQ, 2 };
    66 static const uchar czechRules[] =
    67     { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE,
    68       Q_MOD_100 | Q_BETWEEN, 2, 4 };
    6967static const uchar slovakRules[] =
    7068    { Q_EQ, 1, Q_NEWRULE,
     
    7573static const uchar lithuanianRules[] =
    7674    { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
    77       Q_MOD_10 | Q_EQ, 2, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
     75      Q_MOD_10 | Q_, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
    7876static const uchar russianStyleRules[] =
    7977    { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
     
    103101      Q_EQ, 2, Q_NEWRULE,
    104102      Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE,
    105       Q_MOD_100 | Q_NEQ, 0 };
     103      Q_MOD_100 | Q_NOT | Q_BETWEEN, 0, 2 };
     104static const uchar tagalogRules[] =
     105    { Q_LEQ, 1, Q_NEWRULE,
     106      Q_MOD_10 | Q_EQ, 4, Q_OR, Q_MOD_10 | Q_EQ, 6, Q_OR, Q_MOD_10 | Q_EQ, 9 };
     107static const uchar catalanRules[] =
     108    { Q_EQ, 1, Q_NEWRULE,
     109      Q_LEAD_1000 | Q_EQ, 11 };
    106110
    107111static const char * const japaneseStyleForms[] = { "Universal Form", 0 };
    108112static const char * const englishStyleForms[] = { "Singular", "Plural", 0 };
    109113static const char * const frenchStyleForms[] = { "Singular", "Plural", 0 };
     114
    110115static const char * const latvianForms[] = { "Singular", "Plural", "Nullar", 0 };
    111116static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 };
    112 static const char * const czechForms[] = { "Singular", "Dual", "Plural", 0 };
    113 static const char * const slovakForms[] = { "Singular", "Dual", "Plural", 0 };
     117static const char * const slovakForms[] = { "Singular", "Paucal", "Plural", 0 };
    114118static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 };
    115 static const char * const lithuanianForms[] = { "Singular", "Dual", "Plural", 0 };
     119static const char * const lithuanianForms[] = { "Singular", "al", "Plural", 0 };
    116120static const char * const russianStyleForms[] = { "Singular", "Dual", "Plural", 0 };
    117121static const char * const polishForms[] = { "Singular", "Paucal", "Plural", 0 };
    118 static const char * const romanianForms[] =
    119     { "Singular", "Plural Form for 2 to 19", "Plural", 0 };
     122static const char * const romanianForms[] = { "Singular", "Paucal", "Plural", 0 };
    120123static const char * const slovenianForms[] = { "Singular", "Dual", "Trial", "Plural", 0 };
    121124static const char * const malteseForms[] =
    122     { "Singular", "Plural Form for 2 to 10", "Plural Form for 11 to 19", "Plural", 0 };
     125    { "Singular", "P", "Plural", 0 };
    123126static const char * const welshForms[] =
    124127    { "Nullar", "Singular", "Dual", "Sexal", "Plural", 0 };
    125128static const char * const arabicForms[] =
    126     { "Nullar", "Singular", "Dual", "Minority Plural", "Plural", "Plural Form for 100, 200, ...", 0 };
     129    { "Nullar", "Singular", "Dual", "Minority Plural", "Plural", "Plural (100-102, ...)", 0 };
     130static const char * const tagalogForms[] =
     131    { "Singular", "Plural (consonant-ended)", "Plural (vowel-ended)", 0 };
     132static const char * const catalanForms[] = { "Singular", "Undecal (11)", "Plural", 0 };
    127133
    128134#define EOL QLocale::C
     
    148154    QLocale::Thai,
    149155    QLocale::Tibetan,
     156
    150157    QLocale::Vietnamese,
    151158    QLocale::Yoruba,
     
    170177    QLocale::Bulgarian,
    171178    QLocale::Cambodian,
    172     QLocale::Catalan,
    173179    QLocale::Cornish,
    174180    QLocale::Corsican,
     
    191197    QLocale::Hebrew,
    192198    QLocale::Hindi,
    193     QLocale::Icelandic,
    194199    QLocale::Interlingua,
    195200    QLocale::Interlingue,
     
    232237    QLocale::Swahili,
    233238    QLocale::Swedish,
    234     QLocale::Tagalog,
    235239    QLocale::Tajik,
    236240    QLocale::Tamil,
     
    239243    QLocale::TongaLanguage,
    240244    QLocale::Tsonga,
    241     QLocale::Turkish,
    242245    QLocale::Turkmen,
    243246    QLocale::Twi,
    244247    QLocale::Uigur,
     248
    245249    QLocale::Uzbek,
    246250    QLocale::Volapuk,
     
    262266};
    263267static const QLocale::Language latvianLanguage[] = { QLocale::Latvian, EOL };
     268
    264269static const QLocale::Language irishStyleLanguages[] = {
    265270    QLocale::Divehi,
     
    275280    EOL
    276281};
    277 static const QLocale::Language czechLanguage[] = { QLocale::Czech, EOL };
    278 static const QLocale::Language slovakLanguage[] = { QLocale::Slovak, EOL };
     282static const QLocale::Language slovakLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL };
    279283static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL };
    280284static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL };
     
    299303static const QLocale::Language welshLanguage[] = { QLocale::Welsh, EOL };
    300304static const QLocale::Language arabicLanguage[] = { QLocale::Arabic, EOL };
     305
     306
    301307
    302308static const QLocale::Country frenchStyleCountries[] = {
     
    321327      frenchStyleCountries },
    322328    { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0 },
     329
    323330    { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0 },
    324     { czechRules, sizeof(czechRules), czechForms, czechLanguage, 0 },
    325     { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguage, 0 },
     331    { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguages, 0 },
    326332    { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0 },
    327333    { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0 },
     
    332338    { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0 },
    333339    { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0 },
    334     { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 }
     340    { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 },
     341    { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0 },
     342    { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0 }
    335343};
    336344
  • trunk/tools/linguist/shared/po.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    360360    const QChar newline = QLatin1Char('\n');
    361361    QTextStream in(&dev);
     362
    362363    bool error = false;
    363364
     
    396397            while (true) {
    397398                int idx = line.indexOf(QLatin1Char(' '), prefix.length());
    398                 item.msgStr.append(slurpEscapedString(lines, l, idx, prefix, cd));
     399                QString str = slurpEscapedString(lines, l, idx, prefix, cd);
     400                str.replace(QChar(Translator::TextVariantSeparator),
     401                            QChar(Translator::BinaryVariantSeparator));
     402                item.msgStr.append(str);
    399403                if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
    400404                    break;
     
    552556    bool ok = true;
    553557    QTextStream out(&dev);
    554     //qDebug() << "OUT CODEC: " << out.codec()->name();
     558    );
    555559
    556560    bool first = true;
     
    634638                plural = msg.sourceText();
    635639            out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
    636             QStringList translations = translator.normalizedTranslations(msg, cd, &ok);
     640            );
    637641            for (int i = 0; i != translations.size(); ++i) {
     642
     643
     644
    638645                out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap,
    639                                        translations.at(i));
     646                                       );
    640647            }
    641648        }
  • trunk/tools/linguist/shared/profileevaluator.cpp

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

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5151#include <QtCore/QStack>
    5252
     53
     54
     55
     56
    5357QT_BEGIN_NAMESPACE
    54 
    55 class ProFile;
    56 class ProFileEvaluator;
    57 
    58 void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap);
    59 bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap);
    6058
    6159class ProFileEvaluator
    6260{
     61
     62
     63
     64
     65
     66
     67
    6368public:
    6469    enum TemplateType {
     
    6671        TT_Application,
    6772        TT_Library,
     73
    6874        TT_Subdirs
    6975    };
     
    7480    ProFileEvaluator::TemplateType templateType();
    7581    virtual bool contains(const QString &variableName) const;
    76     void setVerbose(bool on);
     82    void setVerbose(bool on); // Default is false
     83    void setCumulative(bool on); // Default is true!
     84    void setOutputDir(const QString &dir); // Default is empty
    7785
    7886    bool queryProFile(ProFile *pro);
     
    8391    QStringList values(const QString &variableName) const;
    8492    QStringList values(const QString &variableName, const ProFile *pro) const;
     93
     94
     95
     96
    8597    QString propertyValue(const QString &val) const;
    8698
     
    93105
    94106private:
    95     class Private;
    96107    Private *d;
     108
     109
     110
     111
    97112};
    98113
  • trunk/tools/linguist/shared/proitems.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5959
    6060// --------------- ProBlock ----------------
     61
    6162ProBlock::ProBlock(ProBlock *parent)
    6263{
    6364    m_blockKind = 0;
    6465    m_parent = parent;
     66
    6567}
    6668
    6769ProBlock::~ProBlock()
    6870{
    69     qDeleteAll(m_proitems);
     71    foreach (ProItem *itm, m_proitems)
     72        if (itm->kind() == BlockKind)
     73            static_cast<ProBlock *>(itm)->deref();
     74        else
     75            delete itm;
    7076}
    7177
     
    110116}
    111117
    112 bool ProBlock::Accept(AbstractProItemVisitor *visitor)
    113 {
    114     visitor->visitBeginProBlock(this);
    115     foreach (ProItem *item, m_proitems) {
    116         if (!item->Accept(visitor))
    117             return false;
     118ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
     119{
     120    if (visitor->visitBeginProBlock(this) == ReturnSkip)
     121        return ReturnTrue;
     122    ProItemReturn rt = ReturnTrue;
     123    for (int i = 0; i < m_proitems.count(); ++i) {
     124        rt = m_proitems.at(i)->Accept(visitor);
     125        if (rt != ReturnTrue && rt != ReturnFalse) {
     126            if (rt == ReturnLoop) {
     127                rt = ReturnTrue;
     128                while (visitor->visitProLoopIteration())
     129                    for (int j = i; ++j < m_proitems.count(); ) {
     130                        rt = m_proitems.at(j)->Accept(visitor);
     131                        if (rt != ReturnTrue && rt != ReturnFalse) {
     132                            if (rt == ReturnNext) {
     133                                rt = ReturnTrue;
     134                                break;
     135                            }
     136                            if (rt == ReturnBreak)
     137                                rt = ReturnTrue;
     138                            goto do_break;
     139                        }
     140                    }
     141              do_break:
     142                visitor->visitProLoopCleanup();
     143            }
     144            break;
     145        }
    118146    }
    119     return visitor->visitEndProBlock(this);
     147    visitor->visitEndProBlock(this);
     148    return rt;
    120149}
    121150
     
    149178}
    150179
    151 bool ProVariable::Accept(AbstractProItemVisitor *visitor)
     180 ProVariable::Accept(AbstractProItemVisitor *visitor)
    152181{
    153182    visitor->visitBeginProVariable(this);
    154     foreach (ProItem *item, m_proitems) {
    155         if (!item->Accept(visitor))
    156             return false;
    157     }
    158     return visitor->visitEndProVariable(this);
     183    foreach (ProItem *item, m_proitems)
     184        item->Accept(visitor); // cannot fail
     185    visitor->visitEndProVariable(this);
     186    return ReturnTrue;
    159187}
    160188
     
    191219}
    192220
    193 bool ProValue::Accept(AbstractProItemVisitor *visitor)
    194 {
    195     return visitor->visitProValue(this);
     221ProItem::ProItemReturn ProValue::Accept(AbstractProItemVisitor *visitor)
     222{
     223    visitor->visitProValue(this);
     224    return ReturnTrue;
    196225}
    197226
     
    217246}
    218247
    219 bool ProFunction::Accept(AbstractProItemVisitor *visitor)
     248 ProFunction::Accept(AbstractProItemVisitor *visitor)
    220249{
    221250    return visitor->visitProFunction(this);
     
    243272}
    244273
    245 bool ProCondition::Accept(AbstractProItemVisitor *visitor)
    246 {
    247     return visitor->visitProCondition(this);
     274ProItem::ProItemReturn ProCondition::Accept(AbstractProItemVisitor *visitor)
     275{
     276    visitor->visitProCondition(this);
     277    return ReturnTrue;
    248278}
    249279
     
    269299}
    270300
    271 bool ProOperator::Accept(AbstractProItemVisitor *visitor)
    272 {
    273     return visitor->visitProOperator(this);
     301ProItem::ProItemReturn ProOperator::Accept(AbstractProItemVisitor *visitor)
     302{
     303    visitor->visitProOperator(this);
     304    return ReturnTrue;
    274305}
    275306
     
    316347}
    317348
    318 bool ProFile::Accept(AbstractProItemVisitor *visitor)
    319 {
    320     visitor->visitBeginProFile(this);
    321     foreach (ProItem *item, m_proitems) {
    322         if (!item->Accept(visitor))
    323             return false;
    324     }
     349ProItem::ProItemReturn ProFile::Accept(AbstractProItemVisitor *visitor)
     350{
     351    ProItemReturn rt;
     352    if ((rt = visitor->visitBeginProFile(this)) != ReturnTrue)
     353        return rt;
     354    ProBlock::Accept(visitor); // cannot fail
    325355    return visitor->visitEndProFile(this);
    326356}
  • trunk/tools/linguist/shared/proitems.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4343#define PROITEMS_H
    4444
    45 #include <QtCore/QObject>
    4645#include <QtCore/QString>
    4746#include <QtCore/QList>
     
    6261    };
    6362
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
    6473    ProItem() : m_lineNumber(0) {}
    6574    virtual ~ProItem() {}
     
    7079    QString comment() const;
    7180
    72     virtual bool Accept(AbstractProItemVisitor *visitor) = 0;
     81    virtual Accept(AbstractProItemVisitor *visitor) = 0;
    7382    int lineNumber() const { return m_lineNumber; }
    7483    void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
     
    8897        VariableKind        = 0x04,
    8998        ProFileKind         = 0x08,
    90         SingleLine          = 0x10
     99        FunctionBodyKind    = 0x10,
     100        SingleLine          = 0x80
    91101    };
    92102
     
    104114    ProBlock *parent() const;
    105115
    106     ProItem::ProItemKind kind() const;
    107 
    108     virtual bool Accept(AbstractProItemVisitor *visitor);
     116    void ref() { ++m_refCount; }
     117    void deref() { if (!--m_refCount) delete this; }
     118
     119    ProItem::ProItemKind kind() const;
     120
     121    virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
    109122protected:
    110123    QList<ProItem *> m_proitems;
     
    112125    ProBlock *m_parent;
    113126    int m_blockKind;
     127
    114128};
    115129
     
    133147    QString variable() const;
    134148
    135     virtual bool Accept(AbstractProItemVisitor *visitor);
     149    virtual Accept(AbstractProItemVisitor *visitor);
    136150private:
    137151    VariableOperator m_variableKind;
     
    152166    ProItem::ProItemKind kind() const;
    153167
    154     virtual bool Accept(AbstractProItemVisitor *visitor);
     168    virtual Accept(AbstractProItemVisitor *visitor);
    155169private:
    156170    QString m_value;
     
    168182    ProItem::ProItemKind kind() const;
    169183
    170     virtual bool Accept(AbstractProItemVisitor *visitor);
     184    virtual Accept(AbstractProItemVisitor *visitor);
    171185private:
    172186    QString m_text;
     
    183197    ProItem::ProItemKind kind() const;
    184198
    185     virtual bool Accept(AbstractProItemVisitor *visitor);
     199    virtual Accept(AbstractProItemVisitor *visitor);
    186200private:
    187201    QString m_text;
     
    203217    ProItem::ProItemKind kind() const;
    204218
    205     virtual bool Accept(AbstractProItemVisitor *visitor);
     219    virtual Accept(AbstractProItemVisitor *visitor);
    206220private:
    207221    OperatorKind m_operatorKind;
    208222};
    209223
    210 class ProFile : public QObject, public ProBlock
    211 {
    212     Q_OBJECT
    213 
     224class ProFile : public ProBlock
     225{
    214226public:
    215227    explicit ProFile(const QString &fileName);
     
    223235    bool isModified() const;
    224236
    225     virtual bool Accept(AbstractProItemVisitor *visitor);
     237    virtual Accept(AbstractProItemVisitor *visitor);
    226238
    227239private:
  • trunk/tools/linguist/shared/proparser.pri

    r2 r561  
    33
    44HEADERS += \
     5
    56    $$PWD/abstractproitemvisitor.h \
    67    $$PWD/proitems.h \
     
    910
    1011SOURCES += \
     12
    1113    $$PWD/proitems.cpp \
    1214    $$PWD/profileevaluator.cpp
  • trunk/tools/linguist/shared/proparserutils.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4444
    4545#include <QtCore/QDir>
     46
    4647#include <QtCore/QLibraryInfo>
     48
    4749
    4850QT_BEGIN_NAMESPACE
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
    4974
    5075// Pre- and postcondition macros
     
    94119        Option::field_sep = QLatin1Char(' ');
    95120    }
     121
     122
     123
     124
     125
     126
     127
     128
     129
     130
     131
     132
     133
     134
     135
     136
     137
     138
     139
    96140};
    97141#if defined(Q_OS_WIN32)
     
    111155
    112156static void insertUnique(QHash<QString, QStringList> *map,
    113     const QString &key, const QStringList &value, bool unique = true)
     157    const QString &key, const QStringList &value)
    114158{
    115159    QStringList &sl = (*map)[key];
    116     if (!unique) {
    117         sl += value;
    118     } else {
    119         for (int i = 0; i < value.count(); ++i) {
    120             if (!sl.contains(value.at(i)))
    121                 sl.append(value.at(i));
    122         }
    123     }
     160    foreach (const QString &str, value)
     161        if (!sl.contains(str))
     162            sl.append(str);
     163}
     164
     165static void removeEach(QHash<QString, QStringList> *map,
     166    const QString &key, const QStringList &value)
     167{
     168    QStringList &sl = (*map)[key];
     169    foreach (const QString &str, value)
     170        sl.removeAll(str);
    124171}
    125172
     
    149196*/
    150197
    151 inline QStringList splitPathList(const QString paths)
     198inline QString fixEnvVariables(const QString &x)
     199{
     200    return Option::fixString(x, Option::FixEnvVars);
     201}
     202
     203inline QStringList splitPathList(const QString &paths)
    152204{
    153205    return paths.split(Option::dirlist_sep);
     
    256308{
    257309    QStringList ret;
    258     const QString concat = QDir::separator() + QString(QLatin1String("mkspecs"));
     310    const QString concat = QDir::separator() + Q);
    259311    QByteArray qmakepath = qgetenv("QMAKEPATH");
    260312    if (!qmakepath.isEmpty()) {
  • trunk/tools/linguist/shared/qm.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4242#include "translator.h"
    4343
     44
    4445#include <QtCore/QCoreApplication>
     46
    4547#include <QtCore/QDebug>
    4648#include <QtCore/QDir>
     
    173175    bool save(QIODevice *iod);
    174176
    175     void insert(const TranslatorMessage &msg, bool forceComment);
     177    void insert(const TranslatorMessage &msg, const QStringList &tlns, bool forceComment);
     178    void insertIdBased(const TranslatorMessage &message, const QStringList &tlns);
    176179
    177180    void squeeze(TranslatorSaveMode mode);
     
    186189    QByteArray originalBytes(const QString &str, bool isUtf8) const;
    187190
    188     void insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8);
     191    void insertInternal(const TranslatorMessage &message, const QStringList &tlns,
     192                        bool forceComment, bool isUtf8);
    189193
    190194    static Prefix commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2);
     
    239243    TranslatorSaveMode mode, Prefix prefix) const
    240244{
    241     for (int i = 0; i < msg.translations().count(); ++i) {
    242         QString str = msg.translations().at(i);
    243         str.replace(QChar(Translator::DefaultVariantSeparator),
    244                     QChar(Translator::InternalVariantSeparator));
    245         stream << quint8(Tag_Translation) << str;
    246     }
     245    for (int i = 0; i < msg.translations().count(); ++i)
     246        stream << quint8(Tag_Translation) << msg.translations().at(i);
    247247
    248248    if (mode == SaveEverything)
    249249        prefix = HashContextSourceTextComment;
    250250
    251     // lrelease produces "wrong" .qm files for QByteArrays that are .isNull().
     251    // lrelease produces "wrong" files for QByteArrays that are .isNull().
    252252    switch (prefix) {
    253253    default:
     
    417417}
    418418
    419 void Releaser::insertInternal(const TranslatorMessage &message, bool forceComment, bool isUtf8)
     419void Releaser::insertInternal(const TranslatorMessage &message, const QStringList &tlns,
     420                              bool forceComment, bool isUtf8)
    420421{
    421422    ByteTranslatorMessage bmsg(originalBytes(message.context(), isUtf8),
    422423                               originalBytes(message.sourceText(), isUtf8),
    423424                               originalBytes(message.comment(), isUtf8),
    424                                message.translations());
     425                               );
    425426    if (!forceComment) {
    426427        ByteTranslatorMessage bmsg2(
     
    434435}
    435436
    436 void Releaser::insert(const TranslatorMessage &message, bool forceComment)
    437 {
    438     insertInternal(message, forceComment, message.isUtf8());
     437void Releaser::insert(const TranslatorMessage &message, bool forceComment)
     438{
     439    insertInternal(message, forceComment, message.isUtf8());
    439440    if (message.isUtf8() && message.isNonUtf8())
    440         insertInternal(message, forceComment, false);
     441        insertInternal(message, tlns, forceComment, false);
     442}
     443
     444void Releaser::insertIdBased(const TranslatorMessage &message, const QStringList &tlns)
     445{
     446    ByteTranslatorMessage bmsg("", originalBytes(message.id(), false), "", tlns);
     447    m_messages.insert(bmsg, 0);
    441448}
    442449
     
    546553    //qDebug() << "NUMITEMS: " << numItems;
    547554
    548     // FIXME: that's just a guess, the original locale data is lost...
    549     QTextCodec *codec = QTextCodec::codecForLocale();
     555   
     556    );
    550557    QTextCodec *utf8Codec = 0;
    551558    if (codec->name() != "UTF-8")
    552559        utf8Codec = QTextCodec::codecForName("UTF-8");
     560
     561
     562
     563
     564
     565
     566
     567
     568
    553569
    554570    QString context, contextUtf8;
     
    585601                            ((str.at(i).unicode() << 8) & 0xff00));
    586602                }
    587                 str.replace(QChar(Translator::InternalVariantSeparator),
    588                             QChar(Translator::DefaultVariantSeparator));
    589603                translations << str;
    590604                m += len;
     
    636650        TranslatorMessage msg;
    637651        msg.setType(TranslatorMessage::Finished);
     652
     653
     654
     655
     656
     657
     658
     659
     660
    638661        msg.setTranslations(translations);
    639662        translations.clear();
     
    650673            if (!(contextIsSystem && sourcetextIsSystem && commentIsSystem)) {
    651674                cd.appendError(QLatin1String(
    652                         "Cannot read file with current system character codec"));
     675                        "Cannot read file with codec"));
    653676                return false;
    654677            }
     
    664687
    665688
     689
     690
     691
     692
     693
     694
     695
     696
     697
     698
    666699
    667700static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
     
    679712    int unfinished = 0;
    680713    int untranslated = 0;
     714
     715
    681716
    682717    for (int i = 0; i != translator.messageCount(); ++i) {
     
    684719        TranslatorMessage::Type typ = msg.type();
    685720        if (typ != TranslatorMessage::Obsolete) {
     721
     722
     723
     724
    686725            if (typ == TranslatorMessage::Unfinished) {
    687                 if (msg.translation().isEmpty()) {
     726                if (msg.translation().isEmpty()) {
    688727                    ++untranslated;
    689728                    continue;
     
    696735                ++finished;
    697736            }
    698             // Drop the comment in (context, sourceText, comment),
    699             // unless the context is empty,
    700             // unless (context, sourceText, "") already exists or
    701             // unless we already dropped the comment of (context,
    702             // sourceText, comment0).
    703             bool forceComment =
    704                     msg.comment().isEmpty()
    705                     || msg.context().isEmpty()
    706                     || translator.contains(msg.context(), msg.sourceText(), QString());
    707             releaser.insert(msg, forceComment);
    708         }
    709     }
     737            QStringList tlns = msg.translations();
     738            if (msg.type() == TranslatorMessage::Unfinished
     739                && (cd.m_idBased || !cd.m_unTrPrefix.isEmpty()))
     740                for (int j = 0; j < tlns.size(); ++j)
     741                    if (tlns.at(j).isEmpty())
     742                        tlns[j] = cd.m_unTrPrefix + msg.sourceText();
     743            if (cd.m_idBased) {
     744                if (!msg.context().isEmpty() || !msg.comment().isEmpty())
     745                    ++droppedData;
     746                releaser.insertIdBased(msg, tlns);
     747            } else {
     748                // Drop the comment in (context, sourceText, comment),
     749                // unless the context is empty,
     750                // unless (context, sourceText, "") already exists or
     751                // unless we already dropped the comment of (context,
     752                // sourceText, comment0).
     753                bool forceComment =
     754                        msg.comment().isEmpty()
     755                        || msg.context().isEmpty()
     756                        || containsStripped(translator, msg);
     757                releaser.insert(msg, tlns, forceComment);
     758            }
     759        }
     760    }
     761
     762    if (missingIds)
     763        cd.appendError(QCoreApplication::translate("LRelease",
     764            "Dropped %n message(s) which had no ID.", 0,
     765            QCoreApplication::CodecForTr, missingIds));
     766    if (droppedData)
     767        cd.appendError(QCoreApplication::translate("LRelease",
     768            "Excess context/disambiguation dropped from %n message(s).", 0,
     769            QCoreApplication::CodecForTr, droppedData));
    710770
    711771    releaser.squeeze(cd.m_saveMode);
  • trunk/tools/linguist/shared/qph.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5555{
    5656public:
    57     QPHReader(QIODevice &dev, ConversionData &cd)
    58       : QXmlStreamReader(&dev), m_cd(cd)
     57    QPHReader(QIODevice &dev)
     58      : QXmlStreamReader(&dev)
    5959    {}
    6060
     
    6363
    6464private:
    65     bool elementStarts(const QString &str) const
    66     {
    67         return isStartElement() && name() == str;
    68     }
    69 
    7065    bool isWhiteSpace() const
    7166    {
    7267        return isCharacters() && text().toString().trimmed().isEmpty();
    7368    }
    74 
    75     // needed to expand <byte ... />
    76     QString readContents();
    77     // needed to join <lengthvariant>s
    78     QString readTransContents();
    79 
    80     void handleError();
    81 
    82     ConversionData &m_cd;
    8369
    8470    enum DataField { NoField, SourceField, TargetField, DefinitionField };
     
    9682        readNext();
    9783        if (isStartElement()) {
    98             if (name() == QLatin1String("source"))
     84            if (name() == QLatin1String("source"))
    9985                m_currentField = SourceField;
    100             else if (name() == QLatin1String("target"))
     86           
    10187                m_currentField = TargetField;
    102             else if (name() == QLatin1String("definition"))
     88           
    10389                m_currentField = DefinitionField;
    104             else
     90           
    10591                m_currentField = NoField;
     92
     93
     94
     95
     96
     97
    10698        } else if (isWhiteSpace()) {
    10799            // ignore these
     
    114106                m_currentDefinition += text();
    115107        } else if (isEndElement() && name() == QLatin1String("phrase")) {
     108
     109
    116110            TranslatorMessage msg;
    117111            msg.setSourceText(m_currentSource);
    118112            msg.setTranslation(m_currentTarget);
    119             msg.setTranslatorComment(m_currentDefinition);
     113            msg.setComment(m_currentDefinition);
    120114            translator.append(msg);
    121115            m_currentSource.clear();
     
    127121}
    128122
    129 static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &cd)
     123static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &)
    130124{
    131125    translator.setLocationsType(Translator::NoLocations);
    132     QPHReader reader(dev, cd);
     126    QPHReader reader(dev);
    133127    return reader.read(translator);
    134128}
     
    170164    QTextStream t(&dev);
    171165    t.setCodec(QTextCodec::codecForName("UTF-8"));
    172     t << "<!DOCTYPE QPH>\n<QPH>\n";
     166    t << "<!DOCTYPE QPH>\n<QPH";
     167    QString languageCode = translator.languageCode();
     168    if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
     169        t << " language=\"" << languageCode << "\"";
     170    languageCode = translator.sourceLanguageCode();
     171    if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
     172        t << " sourcelanguage=\"" << languageCode << "\"";
     173    t << ">\n";
    173174    foreach (const TranslatorMessage &msg, translator.messages()) {
    174175        t << "<phrase>\n";
    175176        t << "    <source>" << protect(msg.sourceText()) << "</source>\n";
    176         t << "    <target>" << protect(msg.translations().join(QLatin1String("@")))
     177        QString str = msg.translations().join(QLatin1String("@"));
     178        str.replace(QChar(Translator::BinaryVariantSeparator),
     179                    QChar(Translator::TextVariantSeparator));
     180        t << "    <target>" << protect(str)
    177181            << "</target>\n";
    178         if (!msg.context().isEmpty() || !msg.comment().isEmpty())
    179             t << "    <definition>" << msg.context() << msg.comment()
    180                 << "</definition>\n";
     182        if (!msg.comment().isEmpty())
     183            t << "    <definition>" << protect(msg.comment()) << "</definition>\n";
    181184        t << "</phrase>\n";
    182185    }
  • trunk/tools/linguist/shared/simtexth.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/tools/linguist/shared/simtexth.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/tools/linguist/shared/translator.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4646#include <stdio.h>
    4747
    48 #include <QtCore/QCoreApplication>
    4948#include <QtCore/QDebug>
    5049#include <QtCore/QDir>
     
    5857QT_BEGIN_NAMESPACE
    5958
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
    6069Translator::Translator() :
    61     m_codecName("ISO-8859-1"),
     70    m_codec),
    6271    m_locationsType(AbsoluteLocations)
    6372{
     
    8291}
    8392
    84 void Translator::replace(const TranslatorMessage &msg)
    85 {
    86     int index = m_messages.indexOf(msg);
    87     if (index == -1)
    88         m_messages.append(msg);
    89     else
    90         m_messages[index] = msg;
    91 }
    92 
    9393void Translator::replaceSorted(const TranslatorMessage &msg)
    9494{
    95     int index = m_messages.indexOf(msg);
     95    int index = (msg);
    9696    if (index == -1)
    9797        appendSorted(msg);
     
    102102void Translator::extend(const TranslatorMessage &msg)
    103103{
    104     int index = m_messages.indexOf(msg);
     104    int index = (msg);
    105105    if (index == -1) {
    106106        m_messages.append(msg);
     
    147147    int curIdx = 0;
    148148    foreach (const TranslatorMessage &mit, m_messages) {
    149         bool sameFile = mit.fileName() == msg.fileName();
     149        bool sameFile = mit.fileName() == msg.fileName();
    150150        int curLine;
    151151        if (sameFile && (curLine = mit.lineNumber()) >= prevLine) {
     
    313313}
    314314
    315 bool Translator::contains(const QString &context,
    316     const QString &sourceText, const QString &comment) const
    317 {
    318     return m_messages.contains(TranslatorMessage(context, sourceText, comment,
    319         QString(), QString(), 0));
    320 }
    321 
    322 TranslatorMessage Translator::find(const QString &context,
    323     const QString &sourceText, const QString &comment) const
    324 {
    325     TranslatorMessage needle(context, sourceText, comment, QString(), QString(), 0);
    326     int index = m_messages.indexOf(needle);
    327     return index == -1 ? TranslatorMessage() : m_messages.at(index);
     315int Translator::find(const TranslatorMessage &msg) const
     316{
     317    for (int i = 0; i < m_messages.count(); ++i) {
     318        const TranslatorMessage &tmsg = m_messages.at(i);
     319        if (msg.id().isEmpty() || tmsg.id().isEmpty()) {
     320            if (msg.context() == tmsg.context()
     321                && msg.sourceText() == tmsg.sourceText()
     322                && msg.comment() == tmsg.comment())
     323                return i;
     324        } else {
     325            if (msg.id() == tmsg.id())
     326                return i;
     327        }
     328    }
     329    return -1;
    328330}
    329331
     
    346348{
    347349    foreach (const TranslatorMessage &msg, m_messages)
    348         if (msg.context() == context && msg.sourceText().isEmpty())
     350        if (msg.context() == context && msg.sourceText().isEmpty())
    349351            return true;
    350352    return false;
     
    354356{
    355357    foreach (const TranslatorMessage &msg, m_messages)
    356         if (msg.context() == context && msg.sourceText().isEmpty())
     358        if (msg.context() == context && msg.sourceText().isEmpty())
    357359            return msg;
    358360    return TranslatorMessage();
     
    417419}
    418420
    419 QSet<TranslatorMessagePtr> Translator::resolveDuplicates()
    420 {
    421     QSet<TranslatorMessagePtr> dups;
    422     QHash<TranslatorMessagePtr, int> refs;
     421void Translator::dropUiLines()
     422{
     423    QString uiXt = QLatin1String(".ui");
     424    QString juiXt = QLatin1String(".jui");
     425    for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
     426        QHash<QString, int> have;
     427        QList<TranslatorMessage::Reference> refs;
     428        foreach (const TranslatorMessage::Reference &itref, it->allReferences()) {
     429            const QString &fn = itref.fileName();
     430            if (fn.endsWith(uiXt) || fn.endsWith(juiXt)) {
     431                if (++have[fn] == 1)
     432                    refs.append(TranslatorMessage::Reference(fn, -1));
     433            } else {
     434                refs.append(itref);
     435            }
     436        }
     437        it->setReferences(refs);
     438    }
     439}
     440
     441struct TranslatorMessageIdPtr {
     442    explicit TranslatorMessageIdPtr(const TranslatorMessage &tm)
     443    {
     444        ptr = &tm;
     445    }
     446
     447    inline const TranslatorMessage *operator->() const
     448    {
     449        return ptr;
     450    }
     451
     452    const TranslatorMessage *ptr;
     453};
     454
     455Q_DECLARE_TYPEINFO(TranslatorMessageIdPtr, Q_MOVABLE_TYPE);
     456
     457inline int qHash(TranslatorMessageIdPtr tmp)
     458{
     459    return qHash(tmp->id());
     460}
     461
     462inline bool operator==(TranslatorMessageIdPtr tmp1, TranslatorMessageIdPtr tmp2)
     463{
     464    return tmp1->id() == tmp2->id();
     465}
     466
     467struct TranslatorMessageContentPtr {
     468    explicit TranslatorMessageContentPtr(const TranslatorMessage &tm)
     469    {
     470        ptr = &tm;
     471    }
     472
     473    inline const TranslatorMessage *operator->() const
     474    {
     475        return ptr;
     476    }
     477
     478    const TranslatorMessage *ptr;
     479};
     480
     481Q_DECLARE_TYPEINFO(TranslatorMessageContentPtr, Q_MOVABLE_TYPE);
     482
     483inline int qHash(TranslatorMessageContentPtr tmp)
     484{
     485    int hash = qHash(tmp->context()) ^ qHash(tmp->sourceText());
     486    if (!tmp->sourceText().isEmpty())
     487        // Special treatment for context comments (empty source).
     488        hash ^= qHash(tmp->comment());
     489    return hash;
     490}
     491
     492inline bool operator==(TranslatorMessageContentPtr tmp1, TranslatorMessageContentPtr tmp2)
     493{
     494    if (tmp1->context() != tmp2->context() || tmp1->sourceText() != tmp2->sourceText())
     495        return false;
     496    // Special treatment for context comments (empty source).
     497    if (tmp1->sourceText().isEmpty())
     498        return true;
     499    return tmp1->comment() == tmp2->comment();
     500}
     501
     502Translator::Duplicates Translator::resolveDuplicates()
     503{
     504    Duplicates dups;
     505    QHash<TranslatorMessageIdPtr, int> idRefs;
     506    QHash<TranslatorMessageContentPtr, int> contentRefs;
    423507    for (int i = 0; i < m_messages.count();) {
    424508        const TranslatorMessage &msg = m_messages.at(i);
    425         QHash<TranslatorMessagePtr, int>::ConstIterator it = refs.constFind(msg);
    426         if (it != refs.constEnd()) {
    427             TranslatorMessage &omsg = m_messages[*it];
    428             if (omsg.isUtf8() != msg.isUtf8() && !omsg.isNonUtf8()) {
    429                 // Dual-encoded message
    430                 omsg.setUtf8(true);
    431                 omsg.setNonUtf8(true);
    432             } else {
    433                 // Duplicate
    434                 dups.insert(omsg);
     509        TranslatorMessage *omsg;
     510        int oi;
     511        QSet<int> *pDup;
     512        if (!msg.id().isEmpty()) {
     513            QHash<TranslatorMessageIdPtr, int>::ConstIterator it =
     514                    idRefs.constFind(TranslatorMessageIdPtr(msg));
     515            if (it != idRefs.constEnd()) {
     516                oi = *it;
     517                omsg = &m_messages[oi];
     518                pDup = &dups.byId;
     519                goto gotDupe;
    435520            }
    436             if (!omsg.isTranslated() && msg.isTranslated())
    437                 omsg.setTranslations(msg.translations());
    438             m_messages.removeAt(i);
     521        }
     522        {
     523            QHash<TranslatorMessageContentPtr, int>::ConstIterator it =
     524                    contentRefs.constFind(TranslatorMessageContentPtr(msg));
     525            if (it != contentRefs.constEnd()) {
     526                oi = *it;
     527                omsg = &m_messages[oi];
     528                if (msg.id().isEmpty() || omsg->id().isEmpty()) {
     529                    if (!msg.id().isEmpty() && omsg->id().isEmpty()) {
     530                        omsg->setId(msg.id());
     531                        idRefs[TranslatorMessageIdPtr(*omsg)] = oi;
     532                    }
     533                    pDup = &dups.byContents;
     534                    goto gotDupe;
     535                }
     536                // This is really a content dupe, but with two distinct IDs.
     537            }
     538        }
     539        if (!msg.id().isEmpty())
     540            idRefs[TranslatorMessageIdPtr(msg)] = i;
     541        contentRefs[TranslatorMessageContentPtr(msg)] = i;
     542        ++i;
     543        continue;
     544      gotDupe:
     545        if (omsg->isUtf8() != msg.isUtf8() && !omsg->isNonUtf8()) {
     546            // Dual-encoded message
     547            omsg->setUtf8(true);
     548            omsg->setNonUtf8(true);
    439549        } else {
    440             refs[msg] = i;
    441             ++i;
    442         }
     550            // Duplicate
     551            pDup->insert(oi);
     552        }
     553        if (!omsg->isTranslated() && msg.isTranslated())
     554            omsg->setTranslations(msg.translations());
     555        m_messages.removeAt(i);
    443556    }
    444557    return dups;
    445558}
    446559
    447 void Translator::reportDuplicates(const QSet<TranslatorMessagePtr> &dupes,
     560void Translator::reportDuplicates(const &dupes,
    448561                                  const QString &fileName, bool verbose)
    449562{
    450     if (!dupes.isEmpty()) {
     563    if (!dupes.isEmpty()) {
    451564        if (!verbose) {
    452565            qWarning("Warning: dropping duplicate messages in '%s'\n(try -verbose for more info).",
     
    454567        } else {
    455568            qWarning("Warning: dropping duplicate messages in '%s':", qPrintable(fileName));
    456             foreach (const TranslatorMessagePtr &msg, dupes) {
     569            foreach (int i, dupes.byId)
     570                qWarning("\n* ID: %s", qPrintable(message(i).id()));
     571            foreach (int j, dupes.byContents) {
     572                const TranslatorMessage &msg = message(j);
    457573                qWarning("\n* Context: %s\n* Source: %s",
    458                         qPrintable(msg->context()),
    459                         qPrintable(msg->sourceText()));
    460                 if (!msg->comment().isEmpty())
    461                     qWarning("* Comment: %s", qPrintable(msg->comment()));
     574                        qPrintable(msgcontext()),
     575                        qPrintable(msgsourceText()));
     576                if (!msgcomment().isEmpty())
     577                    qWarning("* Comment: %s", qPrintable(msgcomment()));
    462578            }
    463579            qWarning();
     
    499615}
    500616
    501 QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
    502     QLocale::Language language, QLocale::Country country)
     617QStringList Translator::normalizedTranslations(const TranslatorMessage &msg, int numPlurals)
    503618{
    504619    QStringList translations = msg.translations();
    505     int numTranslations = 1;
    506     if (msg.isPlural() && language != QLocale::C) {
    507         QStringList forms;
    508         if (getNumerusInfo(language, country, 0, &forms))
    509             numTranslations = forms.count(); // includes singular
    510     }
     620    int numTranslations = msg.isPlural() ? numPlurals : 1;
    511621
    512622    // make sure that the stringlist always have the size of the
     
    522632}
    523633
    524 QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
    525     ConversionData &cd, bool *ok) const
    526 {
     634void Translator::normalizeTranslations(ConversionData &cd)
     635{
     636    bool truncated = false;
    527637    QLocale::Language l;
    528638    QLocale::Country c;
    529639    languageAndCountry(languageCode(), &l, &c);
    530     QStringList translns = normalizedTranslations(msg, l, c);
    531     if (msg.translations().size() > translns.size() && ok) {
     640    int numPlurals = 1;
     641    if (l != QLocale::C) {
     642        QStringList forms;
     643        if (getNumerusInfo(l, c, 0, &forms))
     644            numPlurals = forms.count(); // includes singular
     645    }
     646    for (int i = 0; i < m_messages.count(); ++i) {
     647        const TranslatorMessage &msg = m_messages.at(i);
     648        QStringList tlns = msg.translations();
     649        int ccnt = msg.isPlural() ? numPlurals : 1;
     650        if (tlns.count() != ccnt) {
     651            while (tlns.count() < ccnt)
     652                tlns.append(QString());
     653            while (tlns.count() > ccnt) {
     654                tlns.removeLast();
     655                truncated = true;
     656            }
     657            TranslatorMessage msg2(msg);
     658            msg2.setTranslations(tlns);
     659            m_messages[i] = msg2;
     660        }
     661    }
     662    if (truncated)
    532663        cd.appendError(QLatin1String(
    533664            "Removed plural forms as the target language has less "
    534665            "forms.\nIf this sounds wrong, possibly the target language is "
    535666            "not set or recognized.\n"));
    536         *ok = false;
    537     }
    538     return translns;
    539667}
    540668
     
    586714        if (!name.isEmpty())
    587715            qWarning("No QTextCodec for %s available. Using Latin1\n", name.constData());
    588         m_codecName = "ISO-8859-1";
     716        m_codec;
    589717    } else {
    590         m_codecName = codec->name();
    591     }
     718        m_codec = codec;
     719    }
     720}
     721
     722QByteArray Translator::codecName() const
     723{
     724    return m_codec->name();
    592725}
    593726
  • trunk/tools/linguist/shared/translator.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4848#include <QList>
    4949#include <QLocale>
     50
    5051#include <QString>
     52
    5153
    5254
    5355QT_BEGIN_NAMESPACE
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
    5470
    5571class QIODevice;
     
    6480        m_sortContexts(false),
    6581        m_noUiLines(false),
     82
    6683        m_saveMode(SaveEverything)
    6784    {}
     
    8299public:
    83100    QString m_defaultContext;
    84     QByteArray m_codecForSource; // CPP specific
     101    QByteArray m_codecForSource; // CPP, PO & QM specific
     102    QByteArray m_outputCodec; // CPP & PO specific
     103    QString m_unTrPrefix; // QM specific
    85104    QString m_sourceFileName;
    86105    QString m_targetFileName;
    87106    QDir m_sourceDir;
    88     QDir m_targetDir; // FIXME: TS spefic
     107    QDir m_targetDir; // FIXME: TS specific
     108    QSet<QString> m_projectRoots;
     109    QMultiHash<QString, QString> m_allCSources;
     110    QStringList m_includePath;
    89111    QStringList m_dropTags;  // tags to be dropped
    90112    QStringList m_errors;
     
    93115    bool m_sortContexts;
    94116    bool m_noUiLines;
     117
    95118    TranslatorSaveMode m_saveMode;
    96119};
     
    105128    bool release(QFile *iod, ConversionData &cd) const;
    106129
    107     bool contains(const QString &context, const QString &sourceText,
    108         const QString &comment) const;
    109     TranslatorMessage find(const QString &context,
    110         const QString &sourceText, const QString &comment) const;
    111 
     130    int find(const TranslatorMessage &msg) const;
    112131    TranslatorMessage find(const QString &context,
    113132        const QString &comment, const TranslatorMessage::References &refs) const;
     
    116135    TranslatorMessage find(const QString &context) const;
    117136
    118     void replace(const TranslatorMessage &msg);
    119137    void replaceSorted(const TranslatorMessage &msg);
    120138    void extend(const TranslatorMessage &msg); // Only for single-location messages
     
    128146    void stripIdenticalSourceTranslations();
    129147    void dropTranslations();
     148
    130149    void makeFileNamesAbsolute(const QDir &originalPath);
    131     QSet<TranslatorMessagePtr> resolveDuplicates();
    132     static void reportDuplicates(const QSet<TranslatorMessagePtr> &dupes,
    133                                  const QString &fileName, bool verbose);
     150
     151    struct Duplicates { QSet<int> byId, byContents; };
     152    Duplicates resolveDuplicates();
     153    void reportDuplicates(const Duplicates &dupes, const QString &fileName, bool verbose);
    134154
    135155    void setCodecName(const QByteArray &name);
    136     QByteArray codecName() const { return m_codecName; }
     156    QByteArray codecName() const;
     157    QTextCodec *codec() const { return m_codec; }
    137158
    138159    QString languageCode() const { return m_language; }
    139160    QString sourceLanguageCode() const { return m_sourceLanguage; }
    140161
    141     enum LocationsType { NoLocations, RelativeLocations, AbsoluteLocations };
     162    enum LocationsType { NoLocations, RelativeLocations, AbsoluteLocations };
    142163    void setLocationsType(LocationsType lt) { m_locationsType = lt; }
    143164    LocationsType locationsType() const { return m_locationsType; }
     
    151172    QList<TranslatorMessage> messages() const;
    152173    QList<TranslatorMessage> translatedMessages() const;
    153     static QStringList normalizedTranslations(const TranslatorMessage &m,
    154         QLocale::Language lang, QLocale::Country country);
     174    static QStringList normalizedTranslations(const TranslatorMessage &m,
     175    );
    155176    QStringList normalizedTranslations(const TranslatorMessage &m, ConversionData &cd, bool *ok) const;
    156177
     
    179200        LoadFunction loader;
    180201        SaveFunction saver;
    181         enum FileType { SourceCode, TranslationSource, TranslationBinary } fileType;
     202        enum FileType { TranslationSource, TranslationBinary } fileType;
    182203        int priority; // 0 = highest, -1 = invisible
    183204    };
     
    185206    static QList<FileFormat> &registeredFileFormats();
    186207
    187     enum VariantSeparators {
    188         DefaultVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
    189         InternalVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
     208    enum {
     209        tVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
     210        VariantSeparator = 0x9c // unicode "STRING TERMINATOR"
    190211    };
    191212
     
    194215
    195216    TMM m_messages;
    196     QByteArray m_codecName;
     217    Q;
    197218    LocationsType m_locationsType;
    198219
  • trunk/tools/linguist/shared/translatormessage.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    144144
    145145
    146 bool TranslatorMessage::operator==(const TranslatorMessage& m) const
    147 {
    148     static QString msgIdPlural = QLatin1String("po-msgid_plural");
    149 
    150     // Special treatment for context comments (empty source).
    151     return (m_context == m.m_context)
    152         && m_sourcetext == m.m_sourcetext
    153         && m_extra[msgIdPlural] == m.m_extra[msgIdPlural]
    154         && (m_sourcetext.isEmpty() || m_comment == m.m_comment);
    155 }
    156 
    157 
    158 bool TranslatorMessage::operator<(const TranslatorMessage& m) const
    159 {
    160     if (m_context != m.m_context)
    161         return m_context < m.m_context;
    162     if (m_sourcetext != m.m_sourcetext)
    163         return m_sourcetext < m.m_sourcetext;
    164     return m_comment < m.m_comment;
    165 }
    166 
    167 int qHash(const TranslatorMessage &msg)
    168 {
    169     return
    170         qHash(msg.context()) ^
    171         qHash(msg.sourceText()) ^
    172         qHash(msg.extra(QLatin1String("po-msgid_plural"))) ^
    173         qHash(msg.comment());
    174 }
    175 
    176146bool TranslatorMessage::hasExtra(const QString &key) const
    177147{
  • trunk/tools/linguist/shared/translatormessage.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    110110    }
    111111
    112     bool operator==(const TranslatorMessage& m) const;
    113     bool operator<(const TranslatorMessage& m) const;
    114 
    115112    QString fileName() const { return m_fileName; }
    116113    void setFileName(const QString &fileName) { m_fileName = fileName; }
     
    178175Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE);
    179176
    180 int qHash(const TranslatorMessage &msg);
    181 
    182 struct TranslatorMessagePtr {
    183     TranslatorMessagePtr(const TranslatorMessage &tm)
    184     {
    185         ptr = &tm;
    186     }
    187 
    188     inline const TranslatorMessage *operator->() const
    189     {
    190         return ptr;
    191     }
    192 
    193     const TranslatorMessage *ptr;
    194 };
    195 
    196 Q_DECLARE_TYPEINFO(TranslatorMessagePtr, Q_MOVABLE_TYPE);
    197 
    198 inline int qHash(TranslatorMessagePtr tmp)
    199 {
    200     return qHash(*tmp.ptr);
    201 }
    202 
    203 inline bool operator==(TranslatorMessagePtr tmp1, TranslatorMessagePtr tmp2)
    204 {
    205     return *tmp1.ptr == *tmp2.ptr;
    206 }
    207 
    208177QT_END_NAMESPACE
    209178
  • trunk/tools/linguist/shared/ts.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    198198            } else if (elementStarts(strlengthvariant)) {
    199199                if (!result.isEmpty())
    200                     result += QChar(Translator::DefaultVariantSeparator);
     200                    result += QChar(Translator::VariantSeparator);
    201201                result += readContents();
    202202            } else {
     
    515515{
    516516    int offset;
    517     if ((offset = input.indexOf(QChar(Translator::DefaultVariantSeparator))) >= 0) {
     517    if ((offset = input.indexOf(QChar(Translator::VariantSeparator))) >= 0) {
    518518        t << " variants=\"yes\">";
    519519        int start = 0;
     
    525525                break;
    526526            start = offset + 1;
    527             offset = input.indexOf(QChar(Translator::DefaultVariantSeparator), start);
     527            offset = input.indexOf(QChar(Translator::VariantSeparator), start);
    528528            if (offset < 0)
    529529                offset = input.length();
     
    694694                if (msg.isPlural()) {
    695695                    t << ">";
    696                     QStringList translns = translator.normalizedTranslations(msg, cd, &result);
    697                     for (int j = 0; j < qMax(1, translns.count()); ++j) {
     696                    );
     697                    for (int j = 0; j < ); ++j) {
    698698                        t << "\n            <numerusform";
    699699                        writeVariants(t, "            ", translns[j]);
  • trunk/tools/linguist/shared/ts.dtd

    r2 r561  
    3535          sourcelanguage CDATA #IMPLIED
    3636          language CDATA #IMPLIED>
    37 <!-- The encoding to use in the .qm file by default. Default is ISO-8859-1. -->
     37<!-- The encoding to use in the file by default. Default is ISO-8859-1. -->
    3838<!ELEMENT defaultcodec (#PCDATA) >
    3939<!ELEMENT context (name?, comment?, (context|message)+) >
     
    5555<!--
    5656 ! If utf8 is true, the defaultcodec is overridden and the message is encoded
    57  ! in UTF-8 in the .qm file.
     57 ! in UTF-8 in the file.
    5858  -->
    5959<!ATTLIST message
     
    7171 ! "current" is the filename used for the 1st location of the previous message.
    7272 ! For subsequent locations, it is the filename used for the previous location.
    73  ! A single .ts file has either all absolute or all relative locations.
     73 ! A single file has either all absolute or all relative locations.
    7474  -->
    7575<!ATTLIST location
     
    107107 ! The translation variants have a priority between 1 ("highest") and 9 ("lowest")
    108108 ! Typically longer translations get a higher priority.
    109  ! If omitted, the order of appearance of the variants in the .ts files is used.
     109 ! If omitted, the order of appearance of the variants in the files is used.
    110110  -->
    111111<!ATTLIST lengthvariant
  • trunk/tools/linguist/shared/xliff.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    244244}
    245245
    246 static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
    247                             const Translator &translator, ConversionData &cd, bool *ok)
     246static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
    248247{
    249248    static int msgid;
    250249    QString msgidstr = !msg.id().isEmpty() ? msg.id() : QString::fromAscii("_msg%1").arg(++msgid);
    251250
    252     QStringList translns = translator.normalizedTranslations(msg, cd, ok);
     251    QStringList translns = );
    253252    QHash<QString, QString>::const_iterator it;
    254253    QString pluralStr;
     
    304303        if (transit != transend) {
    305304            translation = *transit;
     305
     306
    306307            ++transit;
    307308            puttrans = true;
     
    348349}
    349350
    350 static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
    351                          const Translator &translator, ConversionData &cd, bool *ok)
     351static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
    352352{
    353353    if (msg.isPlural()) {
     
    363363        writeComment(ts, msg, drops, indent);
    364364
    365         writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
     365        writeTransUnits(ts, msg, drops, indent);
    366366        --indent;
    367367        writeIndent(ts, indent);
    368368        ts << "</group>\n";
    369369    } else {
    370         writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
     370        writeTransUnits(ts, msg, drops, indent);
    371371    }
    372372}
     
    501501        m_fileName = atts.value(QLatin1String("original"));
    502502        m_language = atts.value(QLatin1String("target-language"));
     503
    503504        m_sourceLanguage = atts.value(QLatin1String("source-language"));
     505
    504506    } else if (localName == QLatin1String("group")) {
    505507        if (atts.value(QLatin1String("restype")) == QLatin1String(restypeContext)) {
     
    599601        }
    600602    } else if (localName == QLatin1String("target")) {
    601         if (popContext(XC_restype_translation))
     603        if (popContext(XC_restype_translation)) {
     604            accum.replace(QChar(Translator::TextVariantSeparator),
     605                          QChar(Translator::BinaryVariantSeparator));
    602606            m_translations.append(accum);
     607
    603608    } else if (localName == QLatin1String("context-group")) {
    604609        if (popContext(XC_context_group)) {
     
    771776    ++indent;
    772777    writeExtras(ts, indent, translator.extras(), drops);
     778
     779
     780
     781
     782
     783
     784
    773785    foreach (const QString &fn, fileOrder) {
    774786        writeIndent(ts, indent);
    775787        ts << "<file original=\"" << fn << "\""
    776788            << " datatype=\"" << dataType(messageOrder[fn].begin()->first()) << "\""
    777             << " source-language=\""
    778                 << (translator.sourceLanguageCode().isEmpty() ?
    779                     QByteArray("en") : translator.sourceLanguageCode().toLatin1()) << "\""
    780             << " target-language=\"" << translator.languageCode() << "\""
     789            << " source-language=\"" << sourceLanguageCode.toLatin1() << "\""
     790            << " target-language=\"" << languageCode.toLatin1() << "\""
    781791            << "><body>\n";
    782792        ++indent;
     
    791801
    792802            foreach (const TranslatorMessage &msg, messageOrder[fn][ctx])
    793                 writeMessage(ts, msg, drops, indent, translator, cd, &ok);
     803                writeMessage(ts, msg, drops, indent);
    794804
    795805            if (!ctx.isEmpty()) {
Note: See TracChangeset for help on using the changeset viewer.