summaryrefslogtreecommitdiffstats
path: root/tools/linguist
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-07-23 05:01:40 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-07-23 05:01:40 (GMT)
commitd1072c333225ded9e46cf857bef701cc67f6fc1d (patch)
treed20b15a94385a78f907350a0f74b9faa905eff3d /tools/linguist
parent4f35294d737d9059398b2c8f714c8e0ea37079ed (diff)
parentc42f7058dfd7ea551b2d3bca5651b0c802c91259 (diff)
downloadQt-d1072c333225ded9e46cf857bef701cc67f6fc1d.zip
Qt-d1072c333225ded9e46cf857bef701cc67f6fc1d.tar.gz
Qt-d1072c333225ded9e46cf857bef701cc67f6fc1d.tar.bz2
Merge branch 'master' of git@scm.dev.nokia.troll.no:qt/qt into kinetic-declarativeui
Conflicts: configure configure.exe src/gui/kernel/qaction.h
Diffstat (limited to 'tools/linguist')
-rw-r--r--tools/linguist/lconvert/main.cpp17
-rw-r--r--tools/linguist/linguist/linguist.rc31
-rw-r--r--tools/linguist/linguist/messagemodel.cpp2
-rw-r--r--tools/linguist/lrelease/main.cpp1
-rw-r--r--tools/linguist/lupdate/main.cpp5
-rw-r--r--tools/linguist/shared/abstractproitemvisitor.h24
-rw-r--r--tools/linguist/shared/po.cpp5
-rw-r--r--tools/linguist/shared/profileevaluator.cpp886
-rw-r--r--tools/linguist/shared/profileevaluator.h3
-rw-r--r--tools/linguist/shared/proitems.cpp84
-rw-r--r--tools/linguist/shared/proitems.h33
-rw-r--r--tools/linguist/shared/qm.cpp3
-rw-r--r--tools/linguist/shared/translator.cpp42
-rw-r--r--tools/linguist/shared/translator.h7
-rw-r--r--tools/linguist/shared/ts.cpp4
-rw-r--r--tools/linguist/shared/xliff.cpp14
16 files changed, 813 insertions, 348 deletions
diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp
index 4bed02f..1381595 100644
--- a/tools/linguist/lconvert/main.cpp
+++ b/tools/linguist/lconvert/main.cpp
@@ -81,8 +81,11 @@ static int usage(const QStringList &args)
" --output-format <outformat>\n"
" Specify output format. See -if.\n\n"
" --input-codec <codec>\n"
- " Specify encoding for QM input files. Default is 'Latin1'.\n"
- " UTF-8 is always tried as well, corresponding to the trUtf8() function.\n\n"
+ " Specify encoding for QM and PO input files. Default is 'Latin1'\n"
+ " for QM and 'UTF-8' for PO files. UTF-8 is always tried as well for\n"
+ " QM, corresponding to the possible use of the trUtf8() function.\n\n"
+ " --output-codec <codec>\n"
+ " Specify encoding for PO output files. Default is 'UTF-8'.\n\n"
" --drop-tags <regexp>\n"
" Drop named extra tags when writing TS or XLIFF files.\n"
" May be specified repeatedly.\n\n"
@@ -141,7 +144,6 @@ int main(int argc, char *argv[])
Translator::LocationsType locations = Translator::DefaultLocations;
ConversionData cd;
- cd.m_codecForSource = "Latin1";
Translator tr;
for (int i = 1; i < args.size(); ++i) {
@@ -174,6 +176,10 @@ int main(int argc, char *argv[])
if (++i >= args.size())
return usage(args);
cd.m_codecForSource = args[i].toLatin1();
+ } else if (args[i] == QLatin1String("-output-codec")) {
+ if (++i >= args.size())
+ return usage(args);
+ cd.m_outputCodec = args[i].toLatin1();
} else if (args[i] == QLatin1String("-drop-tag")) {
if (++i >= args.size())
return usage(args);
@@ -257,6 +263,11 @@ int main(int argc, char *argv[])
if (locations != Translator::DefaultLocations)
tr.setLocationsType(locations);
+ tr.normalizeTranslations(cd);
+ if (!cd.errors().isEmpty()) {
+ qWarning("%s", qPrintable(cd.error()));
+ cd.clearErrors();
+ }
if (!tr.save(outFileName, cd, outFormat)) {
qWarning("%s", qPrintable(cd.error()));
return 3;
diff --git a/tools/linguist/linguist/linguist.rc b/tools/linguist/linguist/linguist.rc
index 865e021..5ff37ca 100644
--- a/tools/linguist/linguist/linguist.rc
+++ b/tools/linguist/linguist/linguist.rc
@@ -1 +1,32 @@
+#include "winver.h"
+
IDI_ICON1 ICON DISCARDABLE "linguist.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGS 0x0L
+ FILEFLAGSMASK 0x3fL
+ FILEOS 0x00040004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)"
+ VALUE "FileDescription", "Qt Linguist"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)."
+ VALUE "InternalName", "linguist"
+ VALUE "OriginalFilename", "linguist.exe"
+ VALUE "ProductName", "Qt Linguist"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
diff --git a/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp
index 6bbf6f3..9995220 100644
--- a/tools/linguist/linguist/messagemodel.cpp
+++ b/tools/linguist/linguist/messagemodel.cpp
@@ -139,7 +139,7 @@ DataModel::DataModel(QObject *parent)
QStringList DataModel::normalizedTranslations(const MessageItem &m) const
{
- return Translator::normalizedTranslations(m.message(), m_language, m_country);
+ return Translator::normalizedTranslations(m.message(), m_numerusForms.count());
}
ContextItem *DataModel::contextItem(int context) const
diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp
index d3b9937..5cb9e1a 100644
--- a/tools/linguist/lrelease/main.cpp
+++ b/tools/linguist/lrelease/main.cpp
@@ -121,6 +121,7 @@ static bool releaseTranslator(Translator &tor, const QString &qmFileName,
}
ConversionData cd;
+ tor.normalizeTranslations(cd);
cd.m_verbose = verbose;
cd.m_ignoreUnfinished = ignoreUnfinished;
cd.m_idBased = idBased;
diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp
index 7cf7b54..e2acbbb 100644
--- a/tools/linguist/lupdate/main.cpp
+++ b/tools/linguist/lupdate/main.cpp
@@ -203,6 +203,11 @@ static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFil
out.stripObsoleteMessages();
out.stripEmptyContexts();
+ out.normalizeTranslations(cd);
+ if (!cd.errors().isEmpty()) {
+ printOut(cd.error());
+ cd.clearErrors();
+ }
if (!out.save(fileName, cd, QLatin1String("auto"))) {
printOut(cd.error());
*fail = true;
diff --git a/tools/linguist/shared/abstractproitemvisitor.h b/tools/linguist/shared/abstractproitemvisitor.h
index 0691fdc..43e79e0 100644
--- a/tools/linguist/shared/abstractproitemvisitor.h
+++ b/tools/linguist/shared/abstractproitemvisitor.h
@@ -49,19 +49,23 @@ QT_BEGIN_NAMESPACE
struct AbstractProItemVisitor
{
virtual ~AbstractProItemVisitor() {}
- virtual bool visitBeginProBlock(ProBlock *block) = 0;
- virtual bool visitEndProBlock(ProBlock *block) = 0;
- virtual bool visitBeginProVariable(ProVariable *variable) = 0;
- virtual bool visitEndProVariable(ProVariable *variable) = 0;
+ virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
+ virtual void visitEndProBlock(ProBlock *block) = 0;
- virtual bool visitBeginProFile(ProFile *value) = 0;
- virtual bool visitEndProFile(ProFile *value) = 0;
+ virtual ProItem::ProItemReturn visitProLoopIteration() = 0;
+ virtual void visitProLoopCleanup() = 0;
- virtual bool visitProValue(ProValue *value) = 0;
- virtual bool visitProFunction(ProFunction *function) = 0;
- virtual bool visitProOperator(ProOperator *function) = 0;
- virtual bool visitProCondition(ProCondition *function) = 0;
+ virtual void visitBeginProVariable(ProVariable *variable) = 0;
+ virtual void visitEndProVariable(ProVariable *variable) = 0;
+
+ virtual ProItem::ProItemReturn visitBeginProFile(ProFile *value) = 0;
+ virtual ProItem::ProItemReturn visitEndProFile(ProFile *value) = 0;
+
+ virtual void visitProValue(ProValue *value) = 0;
+ virtual ProItem::ProItemReturn visitProFunction(ProFunction *function) = 0;
+ virtual void visitProOperator(ProOperator *function) = 0;
+ virtual void visitProCondition(ProCondition *function) = 0;
};
QT_END_NAMESPACE
diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp
index e22aa7d..796d012 100644
--- a/tools/linguist/shared/po.cpp
+++ b/tools/linguist/shared/po.cpp
@@ -359,6 +359,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
const QChar quote = QLatin1Char('"');
const QChar newline = QLatin1Char('\n');
QTextStream in(&dev);
+ in.setCodec(cd.m_codecForSource.isEmpty() ? "UTF-8" : cd.m_codecForSource);
bool error = false;
// format of a .po file entry:
@@ -554,7 +555,7 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
{
bool ok = true;
QTextStream out(&dev);
- //qDebug() << "OUT CODEC: " << out.codec()->name();
+ out.setCodec(cd.m_outputCodec.isEmpty() ? "UTF-8" : cd.m_outputCodec);
bool first = true;
if (translator.messages().isEmpty() || !translator.messages().first().sourceText().isEmpty()) {
@@ -636,7 +637,7 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
if (plural.isEmpty())
plural = msg.sourceText();
out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
- QStringList translations = translator.normalizedTranslations(msg, cd, &ok);
+ const QStringList &translations = msg.translations();
for (int i = 0; i != translations.size(); ++i) {
QString str = translations.at(i);
str.replace(QChar(Translator::BinaryVariantSeparator),
diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp
index 0ce27af..5a9095a 100644
--- a/tools/linguist/shared/profileevaluator.cpp
+++ b/tools/linguist/shared/profileevaluator.cpp
@@ -60,7 +60,7 @@
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
-#elif defined(Q_OS_WIN32)
+#else
#include <Windows.h>
#endif
#include <stdio.h>
@@ -171,16 +171,18 @@ public:
/////////////// Evaluating pro file contents
// implementation of AbstractProItemVisitor
- bool visitBeginProBlock(ProBlock *block);
- bool visitEndProBlock(ProBlock *block);
- bool visitBeginProVariable(ProVariable *variable);
- bool visitEndProVariable(ProVariable *variable);
- bool visitBeginProFile(ProFile *value);
- bool visitEndProFile(ProFile *value);
- bool visitProValue(ProValue *value);
- bool visitProFunction(ProFunction *function);
- bool visitProOperator(ProOperator *oper);
- bool visitProCondition(ProCondition *condition);
+ ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
+ void visitEndProBlock(ProBlock *block);
+ ProItem::ProItemReturn visitProLoopIteration();
+ void visitProLoopCleanup();
+ void visitBeginProVariable(ProVariable *variable);
+ void visitEndProVariable(ProVariable *variable);
+ ProItem::ProItemReturn visitBeginProFile(ProFile *value);
+ ProItem::ProItemReturn visitEndProFile(ProFile *value);
+ void visitProValue(ProValue *value);
+ ProItem::ProItemReturn visitProFunction(ProFunction *function);
+ void visitProOperator(ProOperator *oper);
+ void visitProCondition(ProCondition *condition);
QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
QStringList values(const QString &variableName) const;
@@ -191,6 +193,7 @@ public:
bool isActiveConfig(const QString &config, bool regex = false);
QStringList expandVariableReferences(const QString &value);
+ void doVariableReplace(QString *str);
QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
QString format(const char *format) const;
@@ -198,17 +201,22 @@ public:
QString currentDirectory() const;
ProFile *currentProFile() const;
- bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result);
- bool evaluateFile(const QString &fileName, bool *result);
- bool evaluateFeatureFile(const QString &fileName, bool *result);
+ ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
+ bool evaluateFile(const QString &fileName);
+ bool evaluateFeatureFile(const QString &fileName);
+
+ static inline ProItem::ProItemReturn returnBool(bool b)
+ { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }
+
+ QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);
QStringList qmakeFeaturePaths();
- enum { ConditionTrue, ConditionFalse, ConditionElse };
- int m_condition;
- int m_prevCondition;
- bool m_updateCondition;
- bool m_invertNext;
+ struct State {
+ bool condition;
+ bool prevCondition;
+ } m_sts;
+ bool m_invertNext; // Short-lived, so not in State
int m_skipLevel;
bool m_cumulative;
bool m_isFirstVariableValue;
@@ -217,6 +225,14 @@ public:
QString m_origfile;
QString m_oldPath; // To restore the current path to the path
QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
+ struct ProLoop {
+ QString variable;
+ QStringList oldVarVal;
+ QStringList list;
+ int index;
+ bool infinite;
+ };
+ QStack<ProLoop> m_loopStack;
// we need the following two variables for handling
// CONFIG = foo bar $$CONFIG
@@ -228,10 +244,23 @@ public:
QHash<QString, QString> m_properties;
QString m_outputDir;
+ bool m_definingTest;
+ QString m_definingFunc;
+ QHash<QString, ProBlock *> m_testFunctions;
+ QHash<QString, ProBlock *> m_replaceFunctions;
+ QStringList m_returnValue;
+ QStack<QHash<QString, QStringList> > m_valuemapStack;
+ QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;
+
int m_prevLineNo; // Checking whether we're assigning the same TARGET
ProFile *m_prevProFile; // See m_prevLineNo
};
+#if (!defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) && !defined(__SUNPRO_CC)
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
+#endif
+
ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
: q(q_)
{
@@ -244,11 +273,12 @@ ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
m_cumulative = true;
// Evaluator state
- m_updateCondition = false;
- m_condition = ConditionFalse;
+ m_sts.condition = false;
+ m_sts.prevCondition = false;
m_invertNext = false;
m_skipLevel = 0;
m_isFirstVariableValue = true;
+ m_definingFunc.clear();
}
bool ProFileEvaluator::Private::read(ProFile *pro)
@@ -560,91 +590,125 @@ void ProFileEvaluator::Private::updateItem()
}
-bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
+ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
{
- if (block->blockKind() == ProBlock::ScopeKind) {
- m_updateCondition = true;
+ if (block->blockKind() & ProBlock::ScopeContentsKind) {
+ if (!m_definingFunc.isEmpty()) {
+ if (!m_skipLevel || m_cumulative) {
+ QHash<QString, ProBlock *> *hash =
+ (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
+ if (ProBlock *def = hash->value(m_definingFunc))
+ def->deref();
+ hash->insert(m_definingFunc, block);
+ block->ref();
+ block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
+ }
+ m_definingFunc.clear();
+ return ProItem::ReturnSkip;
+ } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
+ if (!m_sts.condition)
+ ++m_skipLevel;
+ else
+ Q_ASSERT(!m_skipLevel);
+ }
+ } else {
if (!m_skipLevel) {
- m_prevCondition = m_condition;
- m_condition = ConditionFalse;
+ if (m_sts.condition) {
+ m_sts.prevCondition = true;
+ m_sts.condition = false;
+ }
} else {
- Q_ASSERT(m_condition != ConditionTrue);
+ Q_ASSERT(!m_sts.condition);
}
- } else if (block->blockKind() & ProBlock::ScopeContentsKind) {
- m_updateCondition = false;
- if (m_condition != ConditionTrue)
- ++m_skipLevel;
- else
- Q_ASSERT(!m_skipLevel);
}
- return true;
+ return ProItem::ReturnTrue;
}
-bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
+void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
{
- if (block->blockKind() & ProBlock::ScopeContentsKind) {
+ if ((block->blockKind() & ProBlock::ScopeContentsKind)
+ && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
if (m_skipLevel) {
- Q_ASSERT(m_condition != ConditionTrue);
+ Q_ASSERT(!m_sts.condition);
--m_skipLevel;
- } else {
+ } else if (!(block->blockKind() & ProBlock::SingleLine)) {
// Conditionals contained inside this block may have changed the state.
// So we reset it here to make an else following us do the right thing.
- m_condition = ConditionTrue;
+ m_sts.condition = true;
}
}
- return true;
}
-bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
+ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
+{
+ ProLoop &loop = m_loopStack.top();
+
+ if (loop.infinite) {
+ if (!loop.variable.isEmpty())
+ m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
+ if (loop.index > 1000) {
+ q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
+ return ProItem::ReturnFalse;
+ }
+ } else {
+ QString val;
+ do {
+ if (loop.index >= loop.list.count())
+ return ProItem::ReturnFalse;
+ val = loop.list.at(loop.index++);
+ } while (val.isEmpty()); // stupid, but qmake is like that
+ m_valuemap[loop.variable] = QStringList(val);
+ }
+ return ProItem::ReturnTrue;
+}
+
+void ProFileEvaluator::Private::visitProLoopCleanup()
+{
+ ProLoop &loop = m_loopStack.top();
+ m_valuemap[loop.variable] = loop.oldVarVal;
+ m_loopStack.pop_back();
+}
+
+void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
{
m_lastVarName = variable->variable();
m_variableOperator = variable->variableOperator();
m_isFirstVariableValue = true;
m_tempValuemap = m_valuemap;
m_tempFilevaluemap = m_filevaluemap;
- return true;
}
-bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
+void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
{
Q_UNUSED(variable);
m_valuemap = m_tempValuemap;
m_filevaluemap = m_tempFilevaluemap;
m_lastVarName.clear();
- return true;
}
-bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
+void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
{
m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
- return true;
}
-bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
+void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
{
if (!m_skipLevel) {
- if (cond->text().toLower() == QLatin1String("else")) {
- // The state ConditionElse makes sure that subsequential elses are ignored.
- // That's braindead, but qmake is like that.
- if (m_prevCondition == ConditionTrue)
- m_condition = ConditionElse;
- else if (m_prevCondition == ConditionFalse)
- m_condition = ConditionTrue;
- } else if (m_condition == ConditionFalse) {
- if (isActiveConfig(cond->text(), true) ^ m_invertNext)
- m_condition = ConditionTrue;
+ if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
+ m_sts.condition = !m_sts.prevCondition;
+ } else {
+ m_sts.prevCondition = false;
+ if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
+ m_sts.condition = true;
}
}
m_invertNext = false;
- return true;
}
-bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
+ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
{
PRE(pro);
- bool ok = true;
m_lineNo = pro->lineNumber();
-
if (m_origfile.isEmpty())
m_origfile = pro->fileName();
if (m_oldPath.isEmpty()) {
@@ -662,21 +726,20 @@ bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
m_cumulative = false;
// This is what qmake does, everything set in the mkspec is also set
// But this also creates a lot of problems
- evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"), &ok);
- evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"), &ok);
+ evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"));
+ evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"));
m_cumulative = cumulative;
}
- ok = QDir::setCurrent(pro->directoryName());
+ return returnBool(QDir::setCurrent(pro->directoryName()));
}
- return ok;
+ return ProItem::ReturnTrue;
}
-bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
+ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
{
PRE(pro);
- bool ok = true;
m_lineNo = pro->lineNumber();
if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
@@ -684,7 +747,7 @@ bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
bool cumulative = m_cumulative;
m_cumulative = false;
- evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"), &ok);
+ evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"));
QSet<QString> processed;
forever {
@@ -694,9 +757,8 @@ bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
const QString config = configs[i].toLower();
if (!processed.contains(config)) {
processed.insert(config);
- evaluateFile(mkspecDirectory + QLatin1String("/features/")
- + config + QLatin1String(".prf"), &ok);
- if (ok) {
+ if (evaluateFile(mkspecDirectory + QLatin1String("/features/")
+ + config + QLatin1String(".prf"))) {
finished = false;
break;
}
@@ -706,13 +768,21 @@ bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
break;
}
+ foreach (ProBlock *itm, m_replaceFunctions)
+ itm->deref();
+ m_replaceFunctions.clear();
+ foreach (ProBlock *itm, m_testFunctions)
+ itm->deref();
+ m_testFunctions.clear();
+
m_cumulative = cumulative;
}
m_profileStack.pop();
- ok = QDir::setCurrent(m_oldPath);
+ return returnBool(QDir::setCurrent(m_oldPath));
}
- return ok;
+
+ return ProItem::ReturnTrue;
}
static void replaceInList(QStringList *varlist,
@@ -733,7 +803,7 @@ static void replaceInList(QStringList *varlist,
}
}
-bool ProFileEvaluator::Private::visitProValue(ProValue *value)
+void ProFileEvaluator::Private::visitProValue(ProValue *value)
{
PRE(value);
m_lineNo = value->lineNumber();
@@ -804,16 +874,16 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value)
{
// DEFINES ~= s/a/b/?[gqi]
- // FIXME: qmake variable-expands val first.
+ doVariableReplace(&val);
if (val.length() < 4 || val[0] != QLatin1Char('s')) {
q->logMessage(format("the ~= operator can handle only the s/// function."));
- return false;
+ break;
}
QChar sep = val.at(1);
QStringList func = val.split(sep);
if (func.count() < 3 || func.count() > 4) {
q->logMessage(format("the s/// function expects 3 or 4 arguments."));
- return false;
+ break;
}
bool global = false, quote = false, case_sense = false;
@@ -840,12 +910,16 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value)
}
m_isFirstVariableValue = false;
- return true;
}
-bool ProFileEvaluator::Private::visitProFunction(ProFunction *func)
+ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
{
- if (!m_updateCondition || m_condition == ConditionFalse) {
+ // Make sure that called subblocks don't inherit & destroy the state
+ bool invertThis = m_invertNext;
+ m_invertNext = false;
+ if (!m_skipLevel)
+ m_sts.prevCondition = false;
+ if (m_cumulative || !m_sts.condition) {
QString text = func->text();
int lparen = text.indexOf(QLatin1Char('('));
int rparen = text.lastIndexOf(QLatin1Char(')'));
@@ -853,16 +927,13 @@ bool ProFileEvaluator::Private::visitProFunction(ProFunction *func)
QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
QString funcName = text.left(lparen);
m_lineNo = func->lineNumber();
- bool result;
- if (!evaluateConditionalFunction(funcName.trimmed(), arguments, &result)) {
- m_invertNext = false;
- return false;
- }
- if (!m_skipLevel && (result ^ m_invertNext))
- m_condition = ConditionTrue;
+ ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
+ if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
+ return result;
+ if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
+ m_sts.condition = true;
}
- m_invertNext = false;
- return true;
+ return ProItem::ReturnTrue;
}
@@ -988,6 +1059,11 @@ QString ProFileEvaluator::Private::currentDirectory() const
return cur->directoryName();
}
+void ProFileEvaluator::Private::doVariableReplace(QString *str)
+{
+ *str = expandVariableReferences(*str).join(QString(Option::field_sep));
+}
+
QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
{
QStringList ret;
@@ -1202,10 +1278,49 @@ bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex
return false;
}
+QStringList ProFileEvaluator::Private::evaluateFunction(
+ ProBlock *funcPtr, const QStringList &argumentsList, bool *ok)
+{
+ bool oki;
+ QStringList ret;
+
+ if (m_valuemapStack.count() >= 100) {
+ q->errorMessage(format("ran into infinite recursion (depth > 100)."));
+ oki = false;
+ } else {
+ State sts = m_sts;
+ m_valuemapStack.push(m_valuemap);
+ m_filevaluemapStack.push(m_filevaluemap);
+
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i) {
+ QStringList theArgs = expandVariableReferences(argumentsList[i]);
+ args += theArgs;
+ m_valuemap[QString::number(i+1)] = theArgs;
+ }
+ m_valuemap[QLatin1String("ARGS")] = args;
+ oki = (funcPtr->Accept(this) != ProItem::ReturnFalse); // True || Return
+ ret = m_returnValue;
+ m_returnValue.clear();
+
+ m_valuemap = m_valuemapStack.pop();
+ m_filevaluemap = m_filevaluemapStack.pop();
+ m_sts = sts;
+ }
+ if (ok)
+ *ok = oki;
+ if (oki)
+ return ret;
+ return QStringList();
+}
+
QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
{
QStringList argumentsList = split_arg_list(arguments);
+ if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
+ return evaluateFunction(funcPtr, argumentsList, 0);
+
QStringList args;
for (int i = 0; i < argumentsList.count(); ++i)
args += expandVariableReferences(argumentsList[i]).join(Option::field_sep);
@@ -1216,35 +1331,34 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
E_REPLACE };
- static QHash<QString, int> *expands = 0;
- if (!expands) {
- expands = new QHash<QString, int>;
- expands->insert(QLatin1String("member"), E_MEMBER);
- expands->insert(QLatin1String("first"), E_FIRST);
- expands->insert(QLatin1String("last"), E_LAST);
- expands->insert(QLatin1String("cat"), E_CAT);
- expands->insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
- expands->insert(QLatin1String("eval"), E_EVAL);
- expands->insert(QLatin1String("list"), E_LIST);
- expands->insert(QLatin1String("sprintf"), E_SPRINTF);
- expands->insert(QLatin1String("join"), E_JOIN);
- expands->insert(QLatin1String("split"), E_SPLIT);
- expands->insert(QLatin1String("basename"), E_BASENAME);
- expands->insert(QLatin1String("dirname"), E_DIRNAME);
- expands->insert(QLatin1String("section"), E_SECTION);
- expands->insert(QLatin1String("find"), E_FIND);
- expands->insert(QLatin1String("system"), E_SYSTEM);
- expands->insert(QLatin1String("unique"), E_UNIQUE);
- expands->insert(QLatin1String("quote"), E_QUOTE);
- expands->insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
- expands->insert(QLatin1String("upper"), E_UPPER);
- expands->insert(QLatin1String("lower"), E_LOWER);
- expands->insert(QLatin1String("re_escape"), E_RE_ESCAPE);
- expands->insert(QLatin1String("files"), E_FILES);
- expands->insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
- expands->insert(QLatin1String("replace"), E_REPLACE);
+ static QHash<QString, int> expands;
+ if (expands.isEmpty()) {
+ expands.insert(QLatin1String("member"), E_MEMBER);
+ expands.insert(QLatin1String("first"), E_FIRST);
+ expands.insert(QLatin1String("last"), E_LAST);
+ expands.insert(QLatin1String("cat"), E_CAT);
+ expands.insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
+ expands.insert(QLatin1String("eval"), E_EVAL);
+ expands.insert(QLatin1String("list"), E_LIST);
+ expands.insert(QLatin1String("sprintf"), E_SPRINTF);
+ expands.insert(QLatin1String("join"), E_JOIN);
+ expands.insert(QLatin1String("split"), E_SPLIT);
+ expands.insert(QLatin1String("basename"), E_BASENAME);
+ expands.insert(QLatin1String("dirname"), E_DIRNAME);
+ expands.insert(QLatin1String("section"), E_SECTION);
+ expands.insert(QLatin1String("find"), E_FIND);
+ expands.insert(QLatin1String("system"), E_SYSTEM);
+ expands.insert(QLatin1String("unique"), E_UNIQUE);
+ expands.insert(QLatin1String("quote"), E_QUOTE);
+ expands.insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
+ expands.insert(QLatin1String("upper"), E_UPPER);
+ expands.insert(QLatin1String("lower"), E_LOWER);
+ expands.insert(QLatin1String("re_escape"), E_RE_ESCAPE);
+ expands.insert(QLatin1String("files"), E_FILES);
+ expands.insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
+ expands.insert(QLatin1String("replace"), E_REPLACE);
}
- ExpandFunc func_t = ExpandFunc(expands->value(func.toLower()));
+ ExpandFunc func_t = ExpandFunc(expands.value(func.toLower()));
QStringList ret;
@@ -1401,7 +1515,7 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
bool singleLine = true;
if (args.count() > 1)
- singleLine = (args[1].toLower() == QLatin1String("true"));
+ singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
QFile qfile(file);
if (qfile.open(QIODevice::ReadOnly)) {
@@ -1475,7 +1589,7 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
bool singleLine = true;
if (args.count() > 1)
- singleLine = (args[1].toLower() == QLatin1String("true"));
+ singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
QString output;
while (proc && !feof(proc)) {
int read_in = int(fread(buff, 1, 255, proc));
@@ -1557,7 +1671,7 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
} else {
bool recursive = false;
if (args.count() == 2)
- recursive = (args[1].toLower() == QLatin1String("true") || args[1].toInt());
+ recursive = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive) || args[1].toInt());
QStringList dirs;
QString r = Option::fixPathToLocalOS(args[0]);
int slash = r.lastIndexOf(QDir::separator());
@@ -1610,13 +1724,40 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun
return ret;
}
-bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &function,
- const QString &arguments, bool *result)
+ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
+ const QString &function, const QString &arguments)
{
QStringList argumentsList = split_arg_list(arguments);
+
+ if (ProBlock *funcPtr = m_testFunctions.value(function, 0)) {
+ bool ok;
+ QStringList ret = evaluateFunction(funcPtr, argumentsList, &ok);
+ if (ok) {
+ if (ret.isEmpty()) {
+ return ProItem::ReturnTrue;
+ } else {
+ if (ret.first() != QLatin1String("false")) {
+ if (ret.first() == QLatin1String("true")) {
+ return ProItem::ReturnTrue;
+ } else {
+ bool ok;
+ int val = ret.first().toInt(&ok);
+ if (ok) {
+ if (val)
+ return ProItem::ReturnTrue;
+ } else {
+ q->logMessage(format("Unexpected return value from test '%1': %2")
+ .arg(function).arg(ret.join(QLatin1String(" :: "))));
+ }
+ }
+ }
+ }
+ }
+ return ProItem::ReturnFalse;
+ }
+
QString sep;
sep.append(Option::field_sep);
-
QStringList args;
for (int i = 0; i < argumentsList.count(); ++i)
args += expandVariableReferences(argumentsList[i]).join(sep);
@@ -1624,91 +1765,282 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct
enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
- T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF };
-
- static QHash<QString, int> *functions = 0;
- if (!functions) {
- functions = new QHash<QString, int>;
- functions->insert(QLatin1String("requires"), T_REQUIRES);
- functions->insert(QLatin1String("greaterThan"), T_GREATERTHAN);
- functions->insert(QLatin1String("lessThan"), T_LESSTHAN);
- functions->insert(QLatin1String("equals"), T_EQUALS);
- functions->insert(QLatin1String("isEqual"), T_EQUALS);
- functions->insert(QLatin1String("exists"), T_EXISTS);
- functions->insert(QLatin1String("export"), T_EXPORT);
- functions->insert(QLatin1String("clear"), T_CLEAR);
- functions->insert(QLatin1String("unset"), T_UNSET);
- functions->insert(QLatin1String("eval"), T_EVAL);
- functions->insert(QLatin1String("CONFIG"), T_CONFIG);
- functions->insert(QLatin1String("if"), T_IF);
- functions->insert(QLatin1String("isActiveConfig"), T_CONFIG);
- functions->insert(QLatin1String("system"), T_SYSTEM);
- functions->insert(QLatin1String("return"), T_RETURN);
- functions->insert(QLatin1String("break"), T_BREAK);
- functions->insert(QLatin1String("next"), T_NEXT);
- functions->insert(QLatin1String("defined"), T_DEFINED);
- functions->insert(QLatin1String("contains"), T_CONTAINS);
- functions->insert(QLatin1String("infile"), T_INFILE);
- functions->insert(QLatin1String("count"), T_COUNT);
- functions->insert(QLatin1String("isEmpty"), T_ISEMPTY);
- functions->insert(QLatin1String("load"), T_LOAD); //v
- functions->insert(QLatin1String("include"), T_INCLUDE); //v
- functions->insert(QLatin1String("debug"), T_DEBUG);
- functions->insert(QLatin1String("message"), T_MESSAGE); //v
- functions->insert(QLatin1String("warning"), T_MESSAGE); //v
- functions->insert(QLatin1String("error"), T_MESSAGE); //v
+ T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
+ T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
+
+ static QHash<QString, int> functions;
+ if (functions.isEmpty()) {
+ functions.insert(QLatin1String("requires"), T_REQUIRES);
+ functions.insert(QLatin1String("greaterThan"), T_GREATERTHAN);
+ functions.insert(QLatin1String("lessThan"), T_LESSTHAN);
+ functions.insert(QLatin1String("equals"), T_EQUALS);
+ functions.insert(QLatin1String("isEqual"), T_EQUALS);
+ functions.insert(QLatin1String("exists"), T_EXISTS);
+ functions.insert(QLatin1String("export"), T_EXPORT);
+ functions.insert(QLatin1String("clear"), T_CLEAR);
+ functions.insert(QLatin1String("unset"), T_UNSET);
+ functions.insert(QLatin1String("eval"), T_EVAL);
+ functions.insert(QLatin1String("CONFIG"), T_CONFIG);
+ functions.insert(QLatin1String("if"), T_IF);
+ functions.insert(QLatin1String("isActiveConfig"), T_CONFIG);
+ functions.insert(QLatin1String("system"), T_SYSTEM);
+ functions.insert(QLatin1String("return"), T_RETURN);
+ functions.insert(QLatin1String("break"), T_BREAK);
+ functions.insert(QLatin1String("next"), T_NEXT);
+ functions.insert(QLatin1String("defined"), T_DEFINED);
+ functions.insert(QLatin1String("contains"), T_CONTAINS);
+ functions.insert(QLatin1String("infile"), T_INFILE);
+ functions.insert(QLatin1String("count"), T_COUNT);
+ functions.insert(QLatin1String("isEmpty"), T_ISEMPTY);
+ functions.insert(QLatin1String("load"), T_LOAD); //v
+ functions.insert(QLatin1String("include"), T_INCLUDE); //v
+ functions.insert(QLatin1String("debug"), T_DEBUG);
+ functions.insert(QLatin1String("message"), T_MESSAGE); //v
+ functions.insert(QLatin1String("warning"), T_MESSAGE); //v
+ functions.insert(QLatin1String("error"), T_MESSAGE); //v
+ functions.insert(QLatin1String("for"), T_FOR); //v
+ functions.insert(QLatin1String("defineTest"), T_DEFINE_TEST); //v
+ functions.insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v
}
- bool cond = false;
- bool ok = true;
-
- TestFunc func_t = (TestFunc)functions->value(function);
+ TestFunc func_t = (TestFunc)functions.value(function);
switch (func_t) {
+ case T_DEFINE_TEST:
+ m_definingTest = true;
+ goto defineFunc;
+ case T_DEFINE_REPLACE:
+ m_definingTest = false;
+ defineFunc:
+ if (args.count() != 1) {
+ q->logMessage(format("%s(function) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ m_definingFunc = args.first();
+ return ProItem::ReturnTrue;
+ case T_DEFINED:
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("defined(function, [\"test\"|\"replace\"])"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ if (args.count() > 1) {
+ if (args[1] == QLatin1String("test"))
+ return returnBool(m_testFunctions.contains(args[0]));
+ else if (args[1] == QLatin1String("replace"))
+ return returnBool(m_replaceFunctions.contains(args[0]));
+ q->logMessage(format("defined(function, type):"
+ " unexpected type [%1].\n").arg(args[1]));
+ return ProItem::ReturnFalse;
+ }
+ return returnBool(m_replaceFunctions.contains(args[0])
+ || m_testFunctions.contains(args[0]));
+ case T_RETURN:
+ m_returnValue = args;
+ // It is "safe" to ignore returns - due to qmake brokeness
+ // they cannot be used to terminate loops anyway.
+ if (m_skipLevel || m_cumulative)
+ return ProItem::ReturnTrue;
+ if (m_valuemapStack.isEmpty()) {
+ q->logMessage(format("unexpected return()."));
+ return ProItem::ReturnFalse;
+ }
+ return ProItem::ReturnReturn;
+ case T_EXPORT:
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnTrue;
+ if (args.count() != 1) {
+ q->logMessage(format("export(variable) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ for (int i = 0; i < m_valuemapStack.size(); ++i) {
+ m_valuemapStack[i][args[0]] = m_valuemap[args[0]];
+ m_filevaluemapStack[i][currentProFile()][args[0]] =
+ m_filevaluemap[currentProFile()][args[0]];
+ }
+ return ProItem::ReturnTrue;
#if 0
case T_INFILE:
case T_REQUIRES:
- case T_GREATERTHAN:
- case T_LESSTHAN:
- case T_EQUALS:
- case T_EXPORT:
- case T_CLEAR:
- case T_UNSET:
case T_EVAL:
- case T_IF:
- case T_RETURN:
+#endif
+ case T_FOR: {
+ if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
+ return ProItem::ReturnTrue;
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (args.count() > 2 || args.count() < 1) {
+ q->logMessage(format("for({var, list|var, forever|ever})"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ ProLoop loop;
+ loop.infinite = false;
+ loop.index = 0;
+ QString it_list;
+ if (args.count() == 1) {
+ doVariableReplace(&args[0]);
+ it_list = args[0];
+ if (args[0] != QLatin1String("ever")) {
+ q->logMessage(format("for({var, list|var, forever|ever})"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ it_list = QLatin1String("forever");
+ } else {
+ loop.variable = args[0];
+ loop.oldVarVal = m_valuemap.value(loop.variable);
+ doVariableReplace(&args[1]);
+ it_list = args[1];
+ }
+ loop.list = m_valuemap[it_list];
+ if (loop.list.isEmpty()) {
+ if (it_list == QLatin1String("forever")) {
+ loop.infinite = true;
+ } else {
+ int dotdot = it_list.indexOf(QLatin1String(".."));
+ if (dotdot != -1) {
+ bool ok;
+ int start = it_list.left(dotdot).toInt(&ok);
+ if (ok) {
+ int end = it_list.mid(dotdot+2).toInt(&ok);
+ if (ok) {
+ if (start < end) {
+ for (int i = start; i <= end; i++)
+ loop.list << QString::number(i);
+ } else {
+ for (int i = start; i >= end; i--)
+ loop.list << QString::number(i);
+ }
+ }
+ }
+ }
+ }
+ }
+ m_loopStack.push(loop);
+ m_sts.condition = true;
+ return ProItem::ReturnLoop;
+ }
case T_BREAK:
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (!m_loopStack.isEmpty())
+ return ProItem::ReturnBreak;
+ // ### missing: breaking out of multiline blocks
+ q->logMessage(format("unexpected break()."));
+ return ProItem::ReturnFalse;
case T_NEXT:
- case T_DEFINED:
-#endif
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (!m_loopStack.isEmpty())
+ return ProItem::ReturnNext;
+ q->logMessage(format("unexpected next()."));
+ return ProItem::ReturnFalse;
+ case T_IF: {
+ if (args.count() != 1) {
+ q->logMessage(format("if(condition) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ QString cond = args.first();
+ bool escaped = false; // This is more than qmake does
+ bool quoted = false;
+ bool ret = true;
+ bool orOp = false;
+ bool invert = false;
+ bool isFunc = false;
+ int parens = 0;
+ QString test;
+ test.reserve(20);
+ QString args;
+ args.reserve(50);
+ const QChar *d = cond.unicode();
+ const QChar *ed = d + cond.length();
+ while (d < ed) {
+ ushort c = (d++)->unicode();
+ if (!escaped) {
+ if (c == '\\') {
+ escaped = true;
+ args += c; // Assume no-one quotes the test name
+ continue;
+ } else if (c == '"') {
+ quoted = !quoted;
+ args += c; // Ditto
+ continue;
+ }
+ } else {
+ escaped = false;
+ }
+ if (quoted) {
+ args += c; // Ditto
+ } else {
+ bool isOp = false;
+ if (c == '(') {
+ isFunc = true;
+ if (parens)
+ args += c;
+ ++parens;
+ } else if (c == ')') {
+ --parens;
+ if (parens)
+ args += c;
+ } else if (!parens) {
+ if (c == ':' || c == '|')
+ isOp = true;
+ else if (c == '!')
+ invert = true;
+ else
+ test += c;
+ } else {
+ args += c;
+ }
+ if (!parens && (isOp || d == ed)) {
+ // Yes, qmake doesn't shortcut evaluations here. We can't, either,
+ // as some test functions have side effects.
+ bool success;
+ if (isFunc) {
+ success = evaluateConditionalFunction(test, args);
+ } else {
+ success = isActiveConfig(test, true);
+ }
+ success ^= invert;
+ if (orOp)
+ ret |= success;
+ else
+ ret &= success;
+ orOp = (c == '|');
+ invert = false;
+ isFunc = false;
+ test.clear();
+ args.clear();
+ }
+ }
+ }
+ return returnBool(ret);
+ }
case T_CONFIG: {
if (args.count() < 1 || args.count() > 2) {
q->logMessage(format("CONFIG(config) requires one or two arguments."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
if (args.count() == 1) {
//cond = isActiveConfig(args.first()); XXX
- break;
+ return ProItem::ReturnFalse;
}
const QStringList mutuals = args[1].split(QLatin1Char('|'));
const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
for (int i = configs.size() - 1; i >= 0; i--) {
for (int mut = 0; mut < mutuals.count(); mut++) {
if (configs[i] == mutuals[mut].trimmed()) {
- cond = (configs[i] == args[0]);
- goto done_T_CONFIG;
+ return returnBool(configs[i] == args[0]);
}
}
}
- done_T_CONFIG:
- break;
+ return ProItem::ReturnFalse;
}
case T_CONTAINS: {
if (args.count() < 2 || args.count() > 3) {
q->logMessage(format("contains(var, val) requires two or three arguments."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
QRegExp regx(args[1]);
@@ -1717,8 +2049,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct
for (int i = 0; i < l.size(); ++i) {
const QString val = l[i];
if (regx.exactMatch(val) || val == args[1]) {
- cond = true;
- break;
+ return ProItem::ReturnTrue;
}
}
} else {
@@ -1727,140 +2058,172 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct
const QString val = l[i];
for (int mut = 0; mut < mutuals.count(); mut++) {
if (val == mutuals[mut].trimmed()) {
- cond = (regx.exactMatch(val) || val == args[1]);
- goto done_T_CONTAINS;
+ return returnBool(regx.exactMatch(val) || val == args[1]);
}
}
}
}
- done_T_CONTAINS:
- break;
+ return ProItem::ReturnFalse;
}
case T_COUNT: {
if (args.count() != 2 && args.count() != 3) {
q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
if (args.count() == 3) {
QString comp = args[2];
if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
- cond = values(args.first()).count() > args[1].toInt();
+ return returnBool(values(args.first()).count() > args[1].toInt());
} else if (comp == QLatin1String(">=")) {
- cond = values(args.first()).count() >= args[1].toInt();
+ return returnBool(values(args.first()).count() >= args[1].toInt());
} else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
- cond = values(args.first()).count() < args[1].toInt();
+ return returnBool(values(args.first()).count() < args[1].toInt());
} else if (comp == QLatin1String("<=")) {
- cond = values(args.first()).count() <= args[1].toInt();
- } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual") || comp == QLatin1String("=") || comp == QLatin1String("==")) {
- cond = values(args.first()).count() == args[1].toInt();
+ return returnBool(values(args.first()).count() <= args[1].toInt());
+ } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
+ || comp == QLatin1String("=") || comp == QLatin1String("==")) {
+ return returnBool(values(args.first()).count() == args[1].toInt());
} else {
- ok = false;
q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
+ return ProItem::ReturnFalse;
}
- break;
}
- cond = values(args.first()).count() == args[1].toInt();
- break;
+ return returnBool(values(args.first()).count() == args[1].toInt());
+ }
+ case T_GREATERTHAN:
+ case T_LESSTHAN: {
+ if (args.count() != 2) {
+ q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QString rhs(args[1]), lhs(values(args[0]).join(QString(Option::field_sep)));
+ bool ok;
+ int rhs_int = rhs.toInt(&ok);
+ if (ok) { // do integer compare
+ int lhs_int = lhs.toInt(&ok);
+ if (ok) {
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs_int > rhs_int);
+ return returnBool(lhs_int < rhs_int);
+ }
+ }
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs > rhs);
+ return returnBool(lhs < rhs);
+ }
+ case T_EQUALS:
+ if (args.count() != 2) {
+ q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ return returnBool(values(args[0]).join(QString(Option::field_sep)) == args[1]);
+ case T_CLEAR: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ if (args.count() != 1) {
+ q->logMessage(format("%1(variable) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
+ if (it == m_valuemap.end())
+ return ProItem::ReturnFalse;
+ it->clear();
+ return ProItem::ReturnTrue;
+ }
+ case T_UNSET: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ if (args.count() != 1) {
+ q->logMessage(format("%1(variable) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
+ if (it == m_valuemap.end())
+ return ProItem::ReturnFalse;
+ m_valuemap.erase(it);
+ return ProItem::ReturnTrue;
}
case T_INCLUDE: {
if (m_skipLevel && !m_cumulative)
- break;
+ return ProItem::ReturnFalse;
QString parseInto;
if (args.count() == 2) {
parseInto = args[1];
} else if (args.count() != 1) {
q->logMessage(format("include(file) requires one or two arguments."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
QString fileName = args.first();
// ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
QDir currentProPath(currentDirectory());
fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
- ok = evaluateFile(fileName, &ok);
- break;
+ State sts = m_sts;
+ bool ok = evaluateFile(fileName);
+ m_sts = sts;
+ return returnBool(ok);
}
case T_LOAD: {
if (m_skipLevel && !m_cumulative)
- break;
+ return ProItem::ReturnFalse;
QString parseInto;
bool ignore_error = false;
if (args.count() == 2) {
QString sarg = args[1];
- ignore_error = (sarg.toLower() == QLatin1String("true") || sarg.toInt());
+ ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
} else if (args.count() != 1) {
q->logMessage(format("load(feature) requires one or two arguments."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
- ok = evaluateFeatureFile( args.first(), &cond);
- break;
+ // XXX ignore_error unused
+ return returnBool(evaluateFeatureFile(args.first()));
}
case T_DEBUG:
// Yup - do nothing. Nothing is going to enable debug output anyway.
- break;
+ return ProItem::ReturnFalse;
case T_MESSAGE: {
if (args.count() != 1) {
q->logMessage(format("%1(message) requires one argument.").arg(function));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
QString msg = fixEnvVariables(args.first());
- if (function == QLatin1String("error")) {
- QStringList parents;
- foreach (ProFile *proFile, m_profileStack)
- parents.append(proFile->fileName());
- if (!parents.isEmpty())
- parents.takeLast();
- if (parents.isEmpty())
- q->fileMessage(format("Project ERROR: %1").arg(msg));
- else
- q->fileMessage(format("Project ERROR: %1. File was included from: '%2'")
- .arg(msg).arg(parents.join(QLatin1String("', '"))));
- } else {
- q->fileMessage(format("Project MESSAGE: %1").arg(msg));
- }
- break;
+ q->fileMessage(QString::fromLatin1("Project %1: %2").arg(function.toUpper(), msg));
+ // ### Consider real termination in non-cumulative mode
+ return returnBool(function != QLatin1String("error"));
}
#if 0 // Way too dangerous to enable.
case T_SYSTEM: {
if (args.count() != 1) {
q->logMessage(format("system(exec) requires one argument."));
- ok = false;
- break;
+ ProItem::ReturnFalse;
}
- ok = system(args.first().toLatin1().constData()) == 0;
- break;
+ return returnBool(system(args.first().toLatin1().constData()) == 0);
}
#endif
case T_ISEMPTY: {
if (args.count() != 1) {
q->logMessage(format("isEmpty(var) requires one argument."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
QStringList sl = values(args.first());
if (sl.count() == 0) {
- cond = true;
+ return ProItem::ReturnTrue;
} else if (sl.count() > 0) {
QString var = sl.first();
- cond = (var.isEmpty());
+ if (var.isEmpty())
+ return ProItem::ReturnTrue;
}
- break;
+ return ProItem::ReturnFalse;
}
case T_EXISTS: {
if (args.count() != 1) {
q->logMessage(format("exists(file) requires one argument."));
- ok = false;
- break;
+ return ProItem::ReturnFalse;
}
QString file = args.first();
file = Option::fixPathToLocalOS(file);
if (QFile::exists(file)) {
- cond = true;
- break;
+ return ProItem::ReturnTrue;
}
//regular expression I guess
QString dirstr = currentDirectory();
@@ -1870,23 +2233,18 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct
file = file.right(file.length() - slsh - 1);
}
if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?')))
- cond = QDir(dirstr).entryList(QStringList(file)).count();
+ if (!QDir(dirstr).entryList(QStringList(file)).isEmpty())
+ return ProItem::ReturnTrue;
- break;
+ return ProItem::ReturnFalse;
}
case 0:
- // This is too chatty currently (missing defineTest and defineReplace)
- //q->logMessage(format("'%1' is not a recognized test function").arg(function));
- break;
+ q->logMessage(format("'%1' is not a recognized test function").arg(function));
+ return ProItem::ReturnFalse;
default:
q->logMessage(format("Function '%1' is not implemented").arg(function));
- break;
+ return ProItem::ReturnFalse;
}
-
- if (result)
- *result = cond;
-
- return ok;
}
QStringList ProFileEvaluator::Private::values(const QString &variableName,
@@ -2039,27 +2397,21 @@ void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
delete proFile;
}
-bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *result)
+bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
{
- bool ok = true;
ProFile *pro = q->parsedProFile(fileName);
if (pro) {
m_profileStack.push(pro);
- ok = pro->Accept(this);
+ bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
m_profileStack.pop();
q->releaseParsedProFile(pro);
-
- if (result)
- *result = true;
+ return ok;
} else {
- if (result)
- *result = false;
+ return false;
}
-
- return ok;
}
-bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, bool *result)
+bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
{
QString fn;
foreach (const QString &path, qmakeFeaturePaths()) {
@@ -2078,7 +2430,7 @@ bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, boo
return false;
bool cumulative = m_cumulative;
m_cumulative = false;
- bool ok = evaluateFile(fn, result);
+ bool ok = evaluateFile(fn);
m_cumulative = cumulative;
return ok;
}
@@ -2190,14 +2542,14 @@ ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
{
QStringList templ = values(QLatin1String("TEMPLATE"));
if (templ.count() >= 1) {
- QString t = templ.last().toLower();
- if (t == QLatin1String("app"))
+ const QString &t = templ.last();
+ if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
return TT_Application;
- if (t == QLatin1String("lib"))
+ if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
return TT_Library;
- if (t == QLatin1String("script"))
+ if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
return TT_Script;
- if (t == QLatin1String("subdirs"))
+ if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
return TT_Subdirs;
}
return TT_Unknown;
diff --git a/tools/linguist/shared/profileevaluator.h b/tools/linguist/shared/profileevaluator.h
index 688022b..f3498c1 100644
--- a/tools/linguist/shared/profileevaluator.h
+++ b/tools/linguist/shared/profileevaluator.h
@@ -95,6 +95,9 @@ public:
private:
class Private;
Private *d;
+
+ // This doesn't help gcc 3.3 and sunpro ...
+ template<typename T> friend class QTypeInfo;
};
QT_END_NAMESPACE
diff --git a/tools/linguist/shared/proitems.cpp b/tools/linguist/shared/proitems.cpp
index 471417e..905c67e 100644
--- a/tools/linguist/shared/proitems.cpp
+++ b/tools/linguist/shared/proitems.cpp
@@ -58,15 +58,21 @@ QString ProItem::comment() const
}
// --------------- ProBlock ----------------
+
ProBlock::ProBlock(ProBlock *parent)
{
m_blockKind = 0;
m_parent = parent;
+ m_refCount = 1;
}
ProBlock::~ProBlock()
{
- qDeleteAll(m_proitems);
+ foreach (ProItem *itm, m_proitems)
+ if (itm->kind() == BlockKind)
+ static_cast<ProBlock *>(itm)->deref();
+ else
+ delete itm;
}
void ProBlock::appendItem(ProItem *proitem)
@@ -109,14 +115,37 @@ ProItem::ProItemKind ProBlock::kind() const
return ProItem::BlockKind;
}
-bool ProBlock::Accept(AbstractProItemVisitor *visitor)
-{
- visitor->visitBeginProBlock(this);
- foreach (ProItem *item, m_proitems) {
- if (!item->Accept(visitor))
- return false;
+ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
+{
+ if (visitor->visitBeginProBlock(this) == ReturnSkip)
+ return ReturnTrue;
+ ProItemReturn rt = ReturnTrue;
+ for (int i = 0; i < m_proitems.count(); ++i) {
+ rt = m_proitems.at(i)->Accept(visitor);
+ if (rt != ReturnTrue && rt != ReturnFalse) {
+ if (rt == ReturnLoop) {
+ rt = ReturnTrue;
+ while (visitor->visitProLoopIteration())
+ for (int j = i; ++j < m_proitems.count(); ) {
+ rt = m_proitems.at(j)->Accept(visitor);
+ if (rt != ReturnTrue && rt != ReturnFalse) {
+ if (rt == ReturnNext) {
+ rt = ReturnTrue;
+ break;
+ }
+ if (rt == ReturnBreak)
+ rt = ReturnTrue;
+ goto do_break;
+ }
+ }
+ do_break:
+ visitor->visitProLoopCleanup();
+ }
+ break;
+ }
}
- return visitor->visitEndProBlock(this);
+ visitor->visitEndProBlock(this);
+ return rt;
}
// --------------- ProVariable ----------------
@@ -148,14 +177,13 @@ QString ProVariable::variable() const
return m_variable;
}
-bool ProVariable::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProVariable::Accept(AbstractProItemVisitor *visitor)
{
visitor->visitBeginProVariable(this);
- foreach (ProItem *item, m_proitems) {
- if (!item->Accept(visitor))
- return false;
- }
- return visitor->visitEndProVariable(this);
+ foreach (ProItem *item, m_proitems)
+ item->Accept(visitor); // cannot fail
+ visitor->visitEndProVariable(this);
+ return ReturnTrue;
}
// --------------- ProValue ----------------
@@ -190,9 +218,10 @@ ProItem::ProItemKind ProValue::kind() const
return ProItem::ValueKind;
}
-bool ProValue::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProValue::Accept(AbstractProItemVisitor *visitor)
{
- return visitor->visitProValue(this);
+ visitor->visitProValue(this);
+ return ReturnTrue;
}
// --------------- ProFunction ----------------
@@ -216,7 +245,7 @@ ProItem::ProItemKind ProFunction::kind() const
return ProItem::FunctionKind;
}
-bool ProFunction::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProFunction::Accept(AbstractProItemVisitor *visitor)
{
return visitor->visitProFunction(this);
}
@@ -242,9 +271,10 @@ ProItem::ProItemKind ProCondition::kind() const
return ProItem::ConditionKind;
}
-bool ProCondition::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProCondition::Accept(AbstractProItemVisitor *visitor)
{
- return visitor->visitProCondition(this);
+ visitor->visitProCondition(this);
+ return ReturnTrue;
}
// --------------- ProOperator ----------------
@@ -268,9 +298,10 @@ ProItem::ProItemKind ProOperator::kind() const
return ProItem::OperatorKind;
}
-bool ProOperator::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProOperator::Accept(AbstractProItemVisitor *visitor)
{
- return visitor->visitProOperator(this);
+ visitor->visitProOperator(this);
+ return ReturnTrue;
}
// --------------- ProFile ----------------
@@ -315,13 +346,12 @@ bool ProFile::isModified() const
return m_modified;
}
-bool ProFile::Accept(AbstractProItemVisitor *visitor)
+ProItem::ProItemReturn ProFile::Accept(AbstractProItemVisitor *visitor)
{
- visitor->visitBeginProFile(this);
- foreach (ProItem *item, m_proitems) {
- if (!item->Accept(visitor))
- return false;
- }
+ ProItemReturn rt;
+ if ((rt = visitor->visitBeginProFile(this)) != ReturnTrue)
+ return rt;
+ ProBlock::Accept(visitor); // cannot fail
return visitor->visitEndProFile(this);
}
diff --git a/tools/linguist/shared/proitems.h b/tools/linguist/shared/proitems.h
index aad0ba2..7833be1 100644
--- a/tools/linguist/shared/proitems.h
+++ b/tools/linguist/shared/proitems.h
@@ -60,6 +60,16 @@ public:
BlockKind
};
+ enum ProItemReturn {
+ ReturnFalse,
+ ReturnTrue,
+ ReturnBreak,
+ ReturnNext,
+ ReturnLoop,
+ ReturnSkip,
+ ReturnReturn
+ };
+
ProItem() : m_lineNumber(0) {}
virtual ~ProItem() {}
@@ -68,7 +78,7 @@ public:
void setComment(const QString &comment);
QString comment() const;
- virtual bool Accept(AbstractProItemVisitor *visitor) = 0;
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor) = 0;
int lineNumber() const { return m_lineNumber; }
void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
@@ -86,7 +96,8 @@ public:
ScopeContentsKind = 0x02,
VariableKind = 0x04,
ProFileKind = 0x08,
- SingleLine = 0x10
+ FunctionBodyKind = 0x10,
+ SingleLine = 0x80
};
ProBlock(ProBlock *parent);
@@ -102,14 +113,18 @@ public:
void setParent(ProBlock *parent);
ProBlock *parent() const;
+ void ref() { ++m_refCount; }
+ void deref() { if (!--m_refCount) delete this; }
+
ProItem::ProItemKind kind() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
protected:
QList<ProItem *> m_proitems;
private:
ProBlock *m_parent;
int m_blockKind;
+ int m_refCount;
};
class ProVariable : public ProBlock
@@ -131,7 +146,7 @@ public:
void setVariable(const QString &name);
QString variable() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
VariableOperator m_variableKind;
QString m_variable;
@@ -150,7 +165,7 @@ public:
ProItem::ProItemKind kind() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
QString m_value;
ProVariable *m_variable;
@@ -166,7 +181,7 @@ public:
ProItem::ProItemKind kind() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
QString m_text;
};
@@ -181,7 +196,7 @@ public:
ProItem::ProItemKind kind() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
QString m_text;
};
@@ -201,7 +216,7 @@ public:
ProItem::ProItemKind kind() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
OperatorKind m_operatorKind;
};
@@ -219,7 +234,7 @@ public:
void setModified(bool modified);
bool isModified() const;
- virtual bool Accept(AbstractProItemVisitor *visitor);
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
private:
QString m_fileName;
diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp
index 9523fde..638e997 100644
--- a/tools/linguist/shared/qm.cpp
+++ b/tools/linguist/shared/qm.cpp
@@ -552,7 +552,8 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
size_t numItems = offsetLength / (2 * sizeof(quint32));
//qDebug() << "NUMITEMS: " << numItems;
- QTextCodec *codec = QTextCodec::codecForName(cd.m_codecForSource);
+ QTextCodec *codec = QTextCodec::codecForName(
+ cd.m_codecForSource.isEmpty() ? "Latin1" : cd.m_codecForSource);
QTextCodec *utf8Codec = 0;
if (codec->name() != "UTF-8")
utf8Codec = QTextCodec::codecForName("UTF-8");
diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp
index 305681d..62f4d10 100644
--- a/tools/linguist/shared/translator.cpp
+++ b/tools/linguist/shared/translator.cpp
@@ -517,16 +517,10 @@ QList<TranslatorMessage> Translator::translatedMessages() const
return result;
}
-QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
- QLocale::Language language, QLocale::Country country)
+QStringList Translator::normalizedTranslations(const TranslatorMessage &msg, int numPlurals)
{
QStringList translations = msg.translations();
- int numTranslations = 1;
- if (msg.isPlural() && language != QLocale::C) {
- QStringList forms;
- if (getNumerusInfo(language, country, 0, &forms))
- numTranslations = forms.count(); // includes singular
- }
+ int numTranslations = msg.isPlural() ? numPlurals : 1;
// make sure that the stringlist always have the size of the
// language's current numerus, or 1 if its not plural
@@ -540,21 +534,39 @@ QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
return translations;
}
-QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
- ConversionData &cd, bool *ok) const
+void Translator::normalizeTranslations(ConversionData &cd)
{
+ bool truncated = false;
QLocale::Language l;
QLocale::Country c;
languageAndCountry(languageCode(), &l, &c);
- QStringList translns = normalizedTranslations(msg, l, c);
- if (msg.translations().size() > translns.size() && ok) {
+ int numPlurals = 1;
+ if (l != QLocale::C) {
+ QStringList forms;
+ if (getNumerusInfo(l, c, 0, &forms))
+ numPlurals = forms.count(); // includes singular
+ }
+ for (int i = 0; i < m_messages.count(); ++i) {
+ const TranslatorMessage &msg = m_messages.at(i);
+ QStringList tlns = msg.translations();
+ int ccnt = msg.isPlural() ? numPlurals : 1;
+ if (tlns.count() != ccnt) {
+ while (tlns.count() < ccnt)
+ tlns.append(QString());
+ while (tlns.count() > ccnt) {
+ tlns.removeLast();
+ truncated = true;
+ }
+ TranslatorMessage msg2(msg);
+ msg2.setTranslations(tlns);
+ m_messages[i] = msg2;
+ }
+ }
+ if (truncated)
cd.appendError(QLatin1String(
"Removed plural forms as the target language has less "
"forms.\nIf this sounds wrong, possibly the target language is "
"not set or recognized.\n"));
- *ok = false;
- }
- return translns;
}
QString Translator::guessLanguageCodeFromFileName(const QString &filename)
diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h
index fb17fd1..d0903a9 100644
--- a/tools/linguist/shared/translator.h
+++ b/tools/linguist/shared/translator.h
@@ -84,7 +84,8 @@ public:
public:
QString m_defaultContext;
- QByteArray m_codecForSource; // CPP specific
+ QByteArray m_codecForSource; // CPP, PO & QM specific
+ QByteArray m_outputCodec; // PO specific
QString m_sourceFileName;
QString m_targetFileName;
QDir m_sourceDir;
@@ -158,8 +159,8 @@ public:
static QString guessLanguageCodeFromFileName(const QString &fileName);
QList<TranslatorMessage> messages() const;
QList<TranslatorMessage> translatedMessages() const;
- static QStringList normalizedTranslations(const TranslatorMessage &m,
- QLocale::Language lang, QLocale::Country country);
+ static QStringList normalizedTranslations(const TranslatorMessage &m, int numPlurals);
+ void normalizeTranslations(ConversionData &cd);
QStringList normalizedTranslations(const TranslatorMessage &m, ConversionData &cd, bool *ok) const;
int messageCount() const { return m_messages.size(); }
diff --git a/tools/linguist/shared/ts.cpp b/tools/linguist/shared/ts.cpp
index 3efce15..5884997 100644
--- a/tools/linguist/shared/ts.cpp
+++ b/tools/linguist/shared/ts.cpp
@@ -693,8 +693,8 @@ bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, in
t << " type=\"obsolete\"";
if (msg.isPlural()) {
t << ">";
- QStringList translns = translator.normalizedTranslations(msg, cd, &result);
- for (int j = 0; j < qMax(1, translns.count()); ++j) {
+ const QStringList &translns = msg.translations();
+ for (int j = 0; j < translns.count(); ++j) {
t << "\n <numerusform";
writeVariants(t, " ", translns[j]);
t << "</numerusform>";
diff --git a/tools/linguist/shared/xliff.cpp b/tools/linguist/shared/xliff.cpp
index 1313172..c222b8d 100644
--- a/tools/linguist/shared/xliff.cpp
+++ b/tools/linguist/shared/xliff.cpp
@@ -243,13 +243,12 @@ static void writeComment(QTextStream &ts, const TranslatorMessage &msg, const QR
}
}
-static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
- const Translator &translator, ConversionData &cd, bool *ok)
+static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
{
static int msgid;
QString msgidstr = !msg.id().isEmpty() ? msg.id() : QString::fromAscii("_msg%1").arg(++msgid);
- QStringList translns = translator.normalizedTranslations(msg, cd, ok);
+ QStringList translns = msg.translations();
QHash<QString, QString>::const_iterator it;
QString pluralStr;
QStringList sources(msg.sourceText());
@@ -349,8 +348,7 @@ static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const
}
}
-static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
- const Translator &translator, ConversionData &cd, bool *ok)
+static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
{
if (msg.isPlural()) {
writeIndent(ts, indent);
@@ -364,12 +362,12 @@ static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QR
writeLineNumber(ts, msg, indent);
writeComment(ts, msg, drops, indent);
- writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
+ writeTransUnits(ts, msg, drops, indent);
--indent;
writeIndent(ts, indent);
ts << "</group>\n";
} else {
- writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
+ writeTransUnits(ts, msg, drops, indent);
}
}
@@ -795,7 +793,7 @@ bool saveXLIFF(const Translator &translator, QIODevice &dev, ConversionData &cd)
}
foreach (const TranslatorMessage &msg, messageOrder[fn][ctx])
- writeMessage(ts, msg, drops, indent, translator, cd, &ok);
+ writeMessage(ts, msg, drops, indent);
if (!ctx.isEmpty()) {
--indent;