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

trunk: Merged in qt 4.6.1 sources.

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

    r2 r561  
    33
    44HEADERS += \
     5    $$PWD/proreader.h \
    56    $$PWD/abstractproitemvisitor.h \
    67    $$PWD/proitems.h \
     
    910
    1011SOURCES += \
     12    $$PWD/proreader.cpp \
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4444
    4545#include <QtCore/QDir>
     46#ifndef QT_BOOTSTRAPPED
    4647#include <QtCore/QLibraryInfo>
     48#endif
    4749
    4850QT_BEGIN_NAMESPACE
     51
     52#ifdef QT_BOOTSTRAPPED
     53// this is a stripped down version of the one found in QtCore
     54class QLibraryInfo
     55{
     56public:
     57    enum LibraryLocation
     58    {
     59        PrefixPath,
     60        DocumentationPath,
     61        HeadersPath,
     62        LibrariesPath,
     63        BinariesPath,
     64        PluginsPath,
     65        DataPath,
     66        TranslationsPath,
     67        SettingsPath,
     68        DemosPath,
     69        ExamplesPath
     70    };
     71    static QString location(LibraryLocation);
     72};
     73#endif
    4974
    5075// Pre- and postcondition macros
     
    94119        Option::field_sep = QLatin1Char(' ');
    95120    }
     121
     122    enum StringFixFlags {
     123        FixNone                 = 0x00,
     124        FixEnvVars              = 0x01,
     125        FixPathCanonicalize     = 0x02,
     126        FixPathToLocalSeparators  = 0x04,
     127        FixPathToTargetSeparators = 0x08
     128    };
     129    static QString fixString(QString string, uchar flags);
     130
     131    inline static QString fixPathToLocalOS(const QString &in, bool fix_env = true, bool canonical = true)
     132    {
     133        uchar flags = FixPathToLocalSeparators;
     134        if (fix_env)
     135            flags |= FixEnvVars;
     136        if (canonical)
     137            flags |= FixPathCanonicalize;
     138        return fixString(in, flags);
     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() + QLatin1String("mkspecs");
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4242#include "translator.h"
    4343
     44#ifndef QT_BOOTSTRAPPED
    4445#include <QtCore/QCoreApplication>
     46#endif
    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" QM 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                               tlns);
    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, const QStringList &tlns, bool forceComment)
     438{
     439    insertInternal(message, tlns, 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    QTextCodec *codec = QTextCodec::codecForName(
     556        cd.m_codecForSource.isEmpty() ? QByteArray("Latin1") : cd.m_codecForSource);
    550557    QTextCodec *utf8Codec = 0;
    551558    if (codec->name() != "UTF-8")
    552559        utf8Codec = QTextCodec::codecForName("UTF-8");
     560
     561    QString strProN = QLatin1String("%n");
     562    QLocale::Language l;
     563    QLocale::Country c;
     564    Translator::languageAndCountry(translator.languageCode(), &l, &c);
     565    QStringList numerusForms;
     566    bool guessPlurals = true;
     567    if (getNumerusInfo(l, c, 0, &numerusForms))
     568        guessPlurals = (numerusForms.count() == 1);
    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        if (translations.count() > 1) {
     653            // If guessPlurals is not false here, plural form discard messages
     654            // will be spewn out later.
     655            msg.setPlural(true);
     656        } else if (guessPlurals) {
     657            // This might cause false positives, so it is a fallback only.
     658            if (sourcetext.contains(strProN))
     659                msg.setPlural(true);
     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 specified input codec"));
    653676                return false;
    654677            }
     
    664687
    665688
     689
     690static bool containsStripped(const Translator &translator, const TranslatorMessage &msg)
     691{
     692    foreach (const TranslatorMessage &tmsg, translator.messages())
     693        if (tmsg.sourceText() == msg.sourceText()
     694            && tmsg.context() == msg.context()
     695            && tmsg.comment().isEmpty())
     696        return true;
     697    return false;
     698}
    666699
    667700static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
     
    679712    int unfinished = 0;
    680713    int untranslated = 0;
     714    int missingIds = 0;
     715    int droppedData = 0;
    681716
    682717    for (int i = 0; i != translator.messageCount(); ++i) {
     
    684719        TranslatorMessage::Type typ = msg.type();
    685720        if (typ != TranslatorMessage::Obsolete) {
     721            if (cd.m_idBased && msg.id().isEmpty()) {
     722                ++missingIds;
     723                continue;
     724            }
    686725            if (typ == TranslatorMessage::Unfinished) {
    687                 if (msg.translation().isEmpty()) {
     726                if (!cd.m_idBased && 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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    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            } else if (name() == QLatin1String("target")) {
    10187                m_currentField = TargetField;
    102             else if (name() == QLatin1String("definition"))
     88            } else if (name() == QLatin1String("definition")) {
    10389                m_currentField = DefinitionField;
    104             else
     90            } else {
    10591                m_currentField = NoField;
     92                if (name() == QLatin1String("QPH")) {
     93                    QXmlStreamAttributes atts = attributes();
     94                    translator.setLanguageCode(atts.value(QLatin1String("language")).toString());
     95                    translator.setSourceLanguageCode(atts.value(QLatin1String("sourcelanguage")).toString());
     96                }
     97            }
    10698        } else if (isWhiteSpace()) {
    10799            // ignore these
     
    114106                m_currentDefinition += text();
    115107        } else if (isEndElement() && name() == QLatin1String("phrase")) {
     108            m_currentTarget.replace(QChar(Translator::TextVariantSeparator),
     109                                    QChar(Translator::BinaryVariantSeparator));
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • 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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • 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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4646#include <stdio.h>
    4747
    48 #include <QtCore/QCoreApplication>
    4948#include <QtCore/QDebug>
    5049#include <QtCore/QDir>
     
    5857QT_BEGIN_NAMESPACE
    5958
     59#ifdef QT_BOOTSTRAPPED
     60QString QObject::tr(const char *sourceText, const char *, int n)
     61{
     62    QString ret = QString::fromLatin1(sourceText);
     63    if (n >= 0)
     64        ret.replace(QLatin1String("%n"), QString::number(n));
     65    return ret;
     66}
     67#endif
     68
    6069Translator::Translator() :
    61     m_codecName("ISO-8859-1"),
     70    m_codec(QTextCodec::codecForName("ISO-8859-1")),
    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 = find(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 = find(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() && mit.context() == msg.context();
    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() && msg.id().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() && msg.id().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 Duplicates &dupes,
    448561                                  const QString &fileName, bool verbose)
    449562{
    450     if (!dupes.isEmpty()) {
     563    if (!dupes.byId.isEmpty() || !dupes.byContents.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(msg.context()),
     575                        qPrintable(msg.sourceText()));
     576                if (!msg.comment().isEmpty())
     577                    qWarning("* Comment: %s", qPrintable(msg.comment()));
    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 = QTextCodec::codecForName("ISO-8859-1");
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4848#include <QList>
    4949#include <QLocale>
     50#include <QMultiHash>
    5051#include <QString>
     52#include <QSet>
    5153
    5254
    5355QT_BEGIN_NAMESPACE
     56
     57#ifdef QT_BOOTSTRAPPED
     58class QObject {
     59public:
     60    static QString tr(const char *sourceText, const char * = 0, int n = -1);
     61};
     62class QCoreApplication : public QObject {
     63public:
     64    enum Encoding { CodecForTr };
     65    static QString translate(const char *, const char *sourceText, const char * = 0,
     66                             Encoding = CodecForTr, int n = -1)
     67        { return tr(sourceText, 0, n); }
     68};
     69#endif
    5470
    5571class QIODevice;
     
    6480        m_sortContexts(false),
    6581        m_noUiLines(false),
     82        m_idBased(false),
    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    bool m_idBased;
    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    void dropUiLines();
    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 { DefaultLocations, 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, int numPlurals);
     175    void normalizeTranslations(ConversionData &cd);
    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        TextVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
     210        BinaryVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
    190211    };
    191212
     
    194215
    195216    TMM m_messages;
    196     QByteArray m_codecName;
     217    QTextCodec *m_codec;
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    198198            } else if (elementStarts(strlengthvariant)) {
    199199                if (!result.isEmpty())
    200                     result += QChar(Translator::DefaultVariantSeparator);
     200                    result += QChar(Translator::BinaryVariantSeparator);
    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::BinaryVariantSeparator))) >= 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::BinaryVariantSeparator), 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                    const QStringList &translns = msg.translations();
     697                    for (int j = 0; j < translns.count(); ++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 QM 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 QM 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 TS 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 TS 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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the Qt Linguist of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    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 = msg.translations();
    253252    QHash<QString, QString>::const_iterator it;
    254253    QString pluralStr;
     
    304303        if (transit != transend) {
    305304            translation = *transit;
     305            translation.replace(QChar(Translator::BinaryVariantSeparator),
     306                                QChar(Translator::TextVariantSeparator));
    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        m_language.replace(QLatin1Char('-'), QLatin1Char('_'));
    503504        m_sourceLanguage = atts.value(QLatin1String("source-language"));
     505        m_sourceLanguage.replace(QLatin1Char('-'), QLatin1Char('_'));
    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    QString sourceLanguageCode = translator.sourceLanguageCode();
     779    if (sourceLanguageCode.isEmpty() || sourceLanguageCode == QLatin1String("C"))
     780        sourceLanguageCode = QLatin1String("en");
     781    else
     782        sourceLanguageCode.replace(QLatin1Char('_'), QLatin1Char('-'));
     783    QString languageCode = translator.languageCode();
     784    languageCode.replace(QLatin1Char('_'), QLatin1Char('-'));
    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.