summaryrefslogtreecommitdiffstats
path: root/tools/linguist/shared
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/linguist/shared
Long live Qt 4.5!
Diffstat (limited to 'tools/linguist/shared')
-rw-r--r--tools/linguist/shared/abstractproitemvisitor.h70
-rw-r--r--tools/linguist/shared/cpp.cpp1074
-rw-r--r--tools/linguist/shared/formats.pri26
-rw-r--r--tools/linguist/shared/java.cpp655
-rwxr-xr-xtools/linguist/shared/make-qscript.sh14
-rw-r--r--tools/linguist/shared/numerus.cpp377
-rw-r--r--tools/linguist/shared/po.cpp662
-rw-r--r--tools/linguist/shared/profileevaluator.cpp1785
-rw-r--r--tools/linguist/shared/profileevaluator.h101
-rw-r--r--tools/linguist/shared/proitems.cpp328
-rw-r--r--tools/linguist/shared/proitems.h236
-rw-r--r--tools/linguist/shared/proparser.pri12
-rw-r--r--tools/linguist/shared/proparserutils.h272
-rw-r--r--tools/linguist/shared/qm.cpp717
-rw-r--r--tools/linguist/shared/qph.cpp171
-rw-r--r--tools/linguist/shared/qscript.cpp2408
-rw-r--r--tools/linguist/shared/qscript.g2039
-rw-r--r--tools/linguist/shared/simtexth.cpp277
-rw-r--r--tools/linguist/shared/simtexth.h100
-rw-r--r--tools/linguist/shared/translator.cpp559
-rw-r--r--tools/linguist/shared/translator.h224
-rw-r--r--tools/linguist/shared/translatormessage.cpp217
-rw-r--r--tools/linguist/shared/translatormessage.h181
-rw-r--r--tools/linguist/shared/translatortools.cpp505
-rw-r--r--tools/linguist/shared/translatortools.h77
-rw-r--r--tools/linguist/shared/translatortools.pri11
-rw-r--r--tools/linguist/shared/ts.cpp755
-rw-r--r--tools/linguist/shared/ts.dtd113
-rw-r--r--tools/linguist/shared/ui.cpp226
-rw-r--r--tools/linguist/shared/xliff.cpp828
30 files changed, 15020 insertions, 0 deletions
diff --git a/tools/linguist/shared/abstractproitemvisitor.h b/tools/linguist/shared/abstractproitemvisitor.h
new file mode 100644
index 0000000000..b108e7e8aa
--- /dev/null
+++ b/tools/linguist/shared/abstractproitemvisitor.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ABSTRACTPROITEMVISITOR
+#define ABSTRACTPROITEMVISITOR
+
+#include "proitems.h"
+
+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 bool visitBeginProFile(ProFile *value) = 0;
+ virtual bool visitEndProFile(ProFile *value) = 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;
+};
+
+QT_END_NAMESPACE
+
+#endif // ABSTRACTPROITEMVISITOR
+
diff --git a/tools/linguist/shared/cpp.cpp b/tools/linguist/shared/cpp.cpp
new file mode 100644
index 0000000000..28616cc4bb
--- /dev/null
+++ b/tools/linguist/shared/cpp.cpp
@@ -0,0 +1,1074 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <ctype.h> // for isXXX()
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+static const char MagicComment[] = "TRANSLATOR ";
+
+static QSet<QString> needs_Q_OBJECT;
+static QSet<QString> lacks_Q_OBJECT;
+
+static const int yyIdentMaxLen = 128;
+static const int yyCommentMaxLen = 65536;
+static const int yyStringMaxLen = 65536;
+
+#define STRINGIFY_INTERNAL(x) #x
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
+
+//#define DIAGNOSE_RETRANSLATABILITY
+/*
+ The first part of this source file is the C++ tokenizer. We skip
+ most of C++; the only tokens that interest us are defined here.
+ Thus, the code fragment
+
+ int main()
+ {
+ printf("Hello, world!\n");
+ return 0;
+ }
+
+ is broken down into the following tokens (Tok_ omitted):
+
+ Ident Ident LeftParen RightParen
+ LeftBrace
+ Ident LeftParen String RightParen Semicolon
+ return Semicolon
+ RightBrace.
+
+ The 0 doesn't produce any token.
+*/
+
+enum {
+ Tok_Eof, Tok_class, Tok_namespace, Tok_return,
+ Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8,
+ Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS,
+ Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon,
+ Tok_Equals,
+ Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon,
+ Tok_Integer = 40,
+ Tok_Other
+};
+
+/*
+ The tokenizer maintains the following global variables. The names
+ should be self-explanatory.
+*/
+static QString yyFileName;
+static int yyCh;
+static bool yyCodecIsUtf8;
+static bool yyForceUtf8;
+static QString yyIdent;
+static QString yyComment;
+static QString yyString;
+static qlonglong yyInteger;
+static QStack<int> yySavedBraceDepth;
+static QStack<int> yySavedParenDepth;
+static int yyBraceDepth;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+static int yyBraceLineNo;
+static int yyParenLineNo;
+static bool yyTokColonSeen = false;
+
+// the string to read from and current position in the string
+static QTextCodec *yySourceCodec;
+static bool yySourceIsUnicode;
+static QString yyInStr;
+static int yyInPos;
+
+static uint getChar()
+{
+ if (yyInPos >= yyInStr.size())
+ return EOF;
+ QChar c = yyInStr[yyInPos++];
+ if (c.unicode() == '\n')
+ ++yyCurLineNo;
+ return c.unicode();
+}
+
+static uint getToken()
+{
+ yyIdent.clear();
+ yyComment.clear();
+ yyString.clear();
+
+ while (yyCh != EOF) {
+ yyLineNo = yyCurLineNo;
+
+ if (isalpha(yyCh) || yyCh == '_') {
+ do {
+ yyIdent += yyCh;
+ yyCh = getChar();
+ } while (isalnum(yyCh) || yyCh == '_');
+
+ //qDebug() << "IDENT: " << yyIdent;
+
+ switch (yyIdent.at(0).unicode()) {
+ case 'Q':
+ if (yyIdent == QLatin1String("Q_OBJECT"))
+ return Tok_Q_OBJECT;
+ if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS"))
+ return Tok_Q_DECLARE_TR_FUNCTIONS;
+ if (yyIdent == QLatin1String("QT_TR_NOOP"))
+ return Tok_tr;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP"))
+ return Tok_translate;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3"))
+ return Tok_translate;
+ if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8"))
+ return Tok_trUtf8;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8"))
+ return Tok_translateUtf8;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8"))
+ return Tok_translateUtf8;
+ break;
+ case 'T':
+ // TR() for when all else fails
+ if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) {
+ return Tok_tr;
+ }
+ break;
+ case 'c':
+ if (yyIdent == QLatin1String("class"))
+ return Tok_class;
+ break;
+ case 'f':
+ /*
+ QTranslator::findMessage() has the same parameters as
+ QApplication::translate().
+ */
+ if (yyIdent == QLatin1String("findMessage"))
+ return Tok_translate;
+ break;
+ case 'n':
+ if (yyIdent == QLatin1String("namespace"))
+ return Tok_namespace;
+ break;
+ case 'r':
+ if (yyIdent == QLatin1String("return"))
+ return Tok_return;
+ break;
+ case 's':
+ if (yyIdent == QLatin1String("struct"))
+ return Tok_class;
+ break;
+ case 't':
+ if (yyIdent == QLatin1String("tr")) {
+ return Tok_tr;
+ }
+ if (yyIdent == QLatin1String("trUtf8")) {
+ return Tok_trUtf8;
+ }
+ if (yyIdent == QLatin1String("translate")) {
+ return Tok_translate;
+ }
+ }
+ return Tok_Ident;
+ } else {
+ switch (yyCh) {
+ case '#':
+ /*
+ Early versions of lupdate complained about
+ unbalanced braces in the following code:
+
+ #ifdef ALPHA
+ while (beta) {
+ #else
+ while (gamma) {
+ #endif
+ delta;
+ }
+
+ The code contains, indeed, two opening braces for
+ one closing brace; yet there's no reason to panic.
+
+ The solution is to remember yyBraceDepth as it was
+ when #if, #ifdef or #ifndef was met, and to set
+ yyBraceDepth to that value when meeting #elif or
+ #else.
+ */
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh) && yyCh != '\n');
+
+ switch (yyCh) {
+ case 'i':
+ yyCh = getChar();
+ if (yyCh == 'f') {
+ // if, ifdef, ifndef
+ yySavedBraceDepth.push(yyBraceDepth);
+ yySavedParenDepth.push(yyParenDepth);
+ }
+ break;
+ case 'e':
+ yyCh = getChar();
+ if (yyCh == 'l') {
+ // elif, else
+ if (!yySavedBraceDepth.isEmpty()) {
+ yyBraceDepth = yySavedBraceDepth.top();
+ yyParenDepth = yySavedParenDepth.top();
+ }
+ } else if (yyCh == 'n') {
+ // endif
+ if (!yySavedBraceDepth.isEmpty()) {
+ yySavedBraceDepth.pop();
+ yySavedParenDepth.pop();
+ }
+ }
+ }
+ while (isalnum(yyCh) || yyCh == '_')
+ yyCh = getChar();
+ break;
+ case '/':
+ yyCh = getChar();
+ if (yyCh == '/') {
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ break;
+ yyComment.append(yyCh);
+ } while (yyCh != '\n');
+ } else if (yyCh == '*') {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ while (!metAsterSlash) {
+ yyCh = getChar();
+ if (yyCh == EOF) {
+ qWarning("%s: Unterminated C++ comment starting at"
+ " line %d\n",
+ qPrintable(yyFileName), yyLineNo);
+ return Tok_Comment;
+ }
+ yyComment.append(yyCh);
+
+ if (yyCh == '*')
+ metAster = true;
+ else if (metAster && yyCh == '/')
+ metAsterSlash = true;
+ else
+ metAster = false;
+ }
+ yyCh = getChar();
+ yyComment.chop(2);
+ }
+ return Tok_Comment;
+ case '"':
+ yyCh = getChar();
+ while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
+ if (yyCh == '\\') {
+ yyCh = getChar();
+ if (yyString.size() < yyStringMaxLen) {
+ yyString.append(QLatin1Char('\\'));
+ yyString.append(yyCh);
+ }
+ } else {
+ if (yyString.size() < yyStringMaxLen)
+ yyString.append(yyCh);
+ }
+ yyCh = getChar();
+ }
+
+ if (yyCh != '"')
+ qWarning("%s:%d: Unterminated C++ string",
+ qPrintable(yyFileName), yyLineNo);
+
+ if (yyCh == EOF)
+ return Tok_Eof;
+ yyCh = getChar();
+ return Tok_String;
+ case '-':
+ yyCh = getChar();
+ if (yyCh == '>') {
+ yyCh = getChar();
+ return Tok_Arrow;
+ }
+ break;
+ case ':':
+ yyCh = getChar();
+ if (yyCh == ':') {
+ yyCh = getChar();
+ return Tok_ColonColon;
+ }
+ return Tok_Colon;
+ // Incomplete: '<' might be part of '<=' or of template syntax.
+ // The main intent of not completely ignoring it is to break
+ // parsing of things like std::cout << QObject::tr() as
+ // context std::cout::QObject (see Task 161106)
+ case '=':
+ yyCh = getChar();
+ return Tok_Equals;
+ case '>':
+ case '<':
+ yyCh = getChar();
+ return Tok_Other;
+ case '\'':
+ yyCh = getChar();
+ if (yyCh == '\\')
+ yyCh = getChar();
+
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && yyCh != '\'');
+ yyCh = getChar();
+ break;
+ case '{':
+ if (yyBraceDepth == 0)
+ yyBraceLineNo = yyCurLineNo;
+ yyBraceDepth++;
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ if (yyBraceDepth == 0)
+ yyBraceLineNo = yyCurLineNo;
+ yyBraceDepth--;
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ QByteArray ba;
+ ba += yyCh;
+ yyCh = getChar();
+ bool hex = yyCh == 'x';
+ if (hex) {
+ ba += yyCh;
+ yyCh = getChar();
+ }
+ while (hex ? isxdigit(yyCh) : isdigit(yyCh)) {
+ ba += yyCh;
+ yyCh = getChar();
+ }
+ bool ok;
+ yyInteger = ba.toLongLong(&ok);
+ if (ok)
+ return Tok_Integer;
+ break;
+ }
+ default:
+ yyCh = getChar();
+ break;
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+/*
+ The second part of this source file is the parser. It accomplishes
+ a very easy task: It finds all strings inside a tr() or translate()
+ call, and possibly finds out the context of the call. It supports
+ three cases: (1) the context is specified, as in
+ FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
+ (2) the call appears within an inlined function; (3) the call
+ appears within a function defined outside the class definition.
+*/
+
+static uint yyTok;
+
+static bool match(uint t)
+{
+ bool matches = (yyTok == t);
+ if (matches)
+ yyTok = getToken();
+ return matches;
+}
+
+static bool matchString(QString *s)
+{
+ bool matches = (yyTok == Tok_String);
+ s->clear();
+ while (yyTok == Tok_String) {
+ *s += yyString;
+ yyTok = getToken();
+ }
+ return matches;
+}
+
+static bool matchEncoding(bool *utf8)
+{
+ STRING(QApplication);
+ STRING(QCoreApplication);
+ STRING(UnicodeUTF8);
+ STRING(DefaultCodec);
+ STRING(CodecForTr);
+
+ if (yyTok != Tok_Ident)
+ return false;
+ if (yyIdent == strQApplication || yyIdent == strQCoreApplication) {
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ yyTok = getToken();
+ }
+ if (yyIdent == strUnicodeUTF8) {
+ *utf8 = true;
+ yyTok = getToken();
+ return true;
+ }
+ if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) {
+ *utf8 = false;
+ yyTok = getToken();
+ return true;
+ }
+ return false;
+}
+
+static bool matchInteger(qlonglong *number)
+{
+ bool matches = (yyTok == Tok_Integer);
+ if (matches) {
+ yyTok = getToken();
+ *number = yyInteger;
+ }
+ return matches;
+}
+
+static bool matchStringOrNull(QString *s)
+{
+ bool matches = matchString(s);
+ qlonglong num = 0;
+ if (!matches)
+ matches = matchInteger(&num);
+ return matches && num == 0;
+}
+
+/*
+ * match any expression that can return a number, which can be
+ * 1. Literal number (e.g. '11')
+ * 2. simple identifier (e.g. 'm_count')
+ * 3. simple function call (e.g. 'size()' )
+ * 4. function call on an object (e.g. 'list.size()')
+ * 5. function call on an object (e.g. 'list->size()')
+ *
+ * Other cases:
+ * size(2,4)
+ * list().size()
+ * list(a,b).size(2,4)
+ * etc...
+ */
+static bool matchExpression()
+{
+ if (match(Tok_Integer))
+ return true;
+
+ int parenlevel = 0;
+ while (match(Tok_Ident) || parenlevel > 0) {
+ if (yyTok == Tok_RightParen) {
+ if (parenlevel == 0) break;
+ --parenlevel;
+ yyTok = getToken();
+ } else if (yyTok == Tok_LeftParen) {
+ yyTok = getToken();
+ if (yyTok == Tok_RightParen) {
+ yyTok = getToken();
+ } else {
+ ++parenlevel;
+ }
+ } else if (yyTok == Tok_Ident) {
+ continue;
+ } else if (yyTok == Tok_Arrow) {
+ yyTok = getToken();
+ } else if (parenlevel == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static QStringList resolveNamespaces(
+ const QStringList &namespaces, const QHash<QString, QStringList> &namespaceAliases)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QStringList ns;
+ foreach (const QString &cns, namespaces) {
+ ns << cns;
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ return ns;
+}
+
+static QStringList getFullyQualifiedNamespaceName(
+ const QSet<QString> &allNamespaces, const QStringList &namespaces,
+ const QHash<QString, QStringList> &namespaceAliases,
+ const QStringList &segments)
+{
+ static QString strColons(QLatin1String("::"));
+
+ if (segments.first().isEmpty()) {
+ // fully qualified
+ QStringList segs = segments;
+ segs.removeFirst();
+ return resolveNamespaces(segs, namespaceAliases);
+ } else {
+ for (int n = namespaces.count(); --n >= -1; ) {
+ QStringList ns;
+ for (int i = 0; i <= n; ++i) // Note: n == -1 possible
+ ns << namespaces[i];
+ foreach (const QString &cns, segments) {
+ ns << cns;
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ if (allNamespaces.contains(ns.join(strColons)))
+ return ns;
+ }
+
+ // Fallback when the namespace was declared in a header, etc.
+ QStringList ns = namespaces;
+ ns += segments;
+ return ns;
+ }
+}
+
+static QString getFullyQualifiedClassName(
+ const QSet<QString> &allClasses, const QStringList &namespaces,
+ const QHash<QString, QStringList> &namespaceAliases,
+ const QString &ident, bool hasPrefix)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QString context = ident;
+ QStringList segments = context.split(strColons);
+ if (segments.first().isEmpty()) {
+ // fully qualified
+ segments.removeFirst();
+ context = resolveNamespaces(segments, namespaceAliases).join(strColons);
+ } else {
+ for (int n = namespaces.count(); --n >= -1; ) {
+ QStringList ns;
+ for (int i = 0; i <= n; ++i) // Note: n == -1 possible
+ ns.append(namespaces[i]);
+ foreach (const QString &cns, segments) {
+ ns.append(cns);
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ QString nctx = ns.join(strColons);
+ if (allClasses.contains(nctx)) {
+ context = nctx;
+ goto gotit;
+ }
+ }
+
+ if (!hasPrefix && namespaces.count())
+ context = namespaces.join(strColons) + strColons + context;
+ }
+gotit:
+ //qDebug() << "CLASSES:" << allClasses << "NAMEPACES:" << namespaces
+ // << "IDENT:" << ident << "CONTEXT:" << context;
+ return context;
+}
+
+
+static QString transcode(const QString &str, bool utf8)
+{
+ static const char tab[] = "abfnrtv";
+ static const char backTab[] = "\a\b\f\n\r\t\v";
+ const QString in = (!utf8 || yySourceIsUnicode)
+ ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data());
+ QString out;
+
+ out.reserve(in.length());
+ for (int i = 0; i < in.length();) {
+ ushort c = in[i++].unicode();
+ if (c == '\\') {
+ if (i >= in.length())
+ break;
+ c = in[i++].unicode();
+
+ if (c == '\n')
+ continue;
+
+ if (c == 'x') {
+ QByteArray hex;
+ while (i < in.length() && isxdigit((c = in[i].unicode()))) {
+ hex += c;
+ i++;
+ }
+ out += hex.toUInt(0, 16);
+ } else if (c >= '0' && c < '8') {
+ QByteArray oct;
+ int n = 0;
+ oct += c;
+ while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') {
+ i++;
+ n++;
+ oct += c;
+ }
+ out += oct.toUInt(0, 8);
+ } else {
+ const char *p = strchr(tab, c);
+ out += QChar(QLatin1Char(!p ? c : backTab[p - tab]));
+ }
+ } else {
+ out += c;
+ }
+ }
+ return out;
+}
+
+static void recordMessage(
+ Translator *tor, int line, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, bool utf8, bool plural)
+{
+ TranslatorMessage msg(
+ transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(),
+ yyFileName, line, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(transcode(extracomment.simplified(), utf8));
+ if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
+ msg.setUtf8(true);
+ tor->extend(msg);
+}
+
+static void parse(Translator *tor, const QString &initialContext, const QString &defaultContext)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QMap<QString, QString> qualifiedContexts;
+ QSet<QString> allClasses;
+ QSet<QString> allNamespaces;
+ QHash<QString, QStringList> namespaceAliases;
+ QStringList namespaces;
+ QString context;
+ QString text;
+ QString comment;
+ QString extracomment;
+ QString functionContext = initialContext;
+ QString prefix;
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ QString functionName;
+#endif
+ int line;
+ bool utf8 = false;
+ bool missing_Q_OBJECT = false;
+
+ yyTok = getToken();
+ while (yyTok != Tok_Eof) {
+ //qDebug() << "TOKEN: " << yyTok;
+ switch (yyTok) {
+ case Tok_class:
+ yyTokColonSeen = false;
+ /*
+ Partial support for inlined functions.
+ */
+ yyTok = getToken();
+ if (yyBraceDepth == namespaces.count() && yyParenDepth == 0) {
+ QStringList fct;
+ do {
+ /*
+ This code should execute only once, but we play
+ safe with impure definitions such as
+ 'class Q_EXPORT QMessageBox', in which case
+ 'QMessageBox' is the class name, not 'Q_EXPORT'.
+ */
+ fct = QStringList(yyIdent);
+ yyTok = getToken();
+ } while (yyTok == Tok_Ident);
+ while (yyTok == Tok_ColonColon) {
+ yyTok = getToken();
+ if (yyTok != Tok_Ident)
+ break; // Oops ...
+ fct += yyIdent;
+ yyTok = getToken();
+ }
+ functionContext = resolveNamespaces(namespaces + fct, namespaceAliases).join(strColons);
+ allClasses.insert(functionContext);
+
+ if (yyTok == Tok_Colon) {
+ missing_Q_OBJECT = true;
+ // Skip any token until '{' since lupdate might do things wrong if it finds
+ // a '::' token here.
+ do {
+ yyTok = getToken();
+ } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
+ } else {
+ //functionContext = defaultContext;
+ }
+ }
+ break;
+ case Tok_namespace:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ if (yyTok == Tok_Ident) {
+ QString ns = yyIdent;
+ yyTok = getToken();
+ if (yyTok == Tok_LeftBrace) {
+ if (yyBraceDepth == namespaces.count() + 1) {
+ namespaces.append(ns);
+ allNamespaces.insert(namespaces.join(strColons));
+ }
+ } else if (yyTok == Tok_Equals) {
+ // e.g. namespace Is = OuterSpace::InnerSpace;
+ QStringList alias = namespaces;
+ alias.append(ns);
+ QStringList fullName;
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ fullName.append(QString());
+ while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+ if (yyTok == Tok_Ident)
+ fullName.append(yyIdent);
+ yyTok = getToken();
+ }
+ namespaceAliases[alias.join(strColons)] =
+ getFullyQualifiedNamespaceName(allNamespaces, namespaces, namespaceAliases, fullName);
+ }
+ }
+ break;
+ case Tok_tr:
+ case Tok_trUtf8:
+ utf8 = (yyTok == Tok_trUtf8);
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) {
+ comment.clear();
+ bool plural = false;
+
+ if (match(Tok_RightParen)) {
+ // no comment
+ } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment
+ if (match(Tok_RightParen)) {
+ // ok,
+ } else if (match(Tok_Comma)) {
+ plural = true;
+ }
+ }
+ if (prefix.isEmpty()) {
+ context = functionContext;
+ } else {
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ int last = prefix.lastIndexOf(strColons);
+ QString className = prefix.mid(last == -1 ? 0 : last + 2);
+ if (!className.isEmpty() && className == functionName) {
+ qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ",
+ qPrintable(yyFileName), yyLineNo,
+ className.constData(), functionName.constData());
+ }
+#endif
+ prefix.chop(2);
+ context = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, true);
+ }
+ prefix.clear();
+ if (qualifiedContexts.contains(context))
+ context = qualifiedContexts[context];
+
+ if (!text.isEmpty())
+ recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
+
+ if (lacks_Q_OBJECT.contains(context)) {
+ qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
+ qPrintable(yyFileName), yyLineNo,
+ qPrintable(context));
+ lacks_Q_OBJECT.remove(context);
+ } else {
+ needs_Q_OBJECT.insert(context);
+ }
+ }
+ extracomment.clear();
+ break;
+ case Tok_translateUtf8:
+ case Tok_translate:
+ utf8 = (yyTok == Tok_translateUtf8);
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen)
+ && matchString(&context)
+ && match(Tok_Comma)
+ && matchString(&text))
+ {
+ comment.clear();
+ bool plural = false;
+ if (!match(Tok_RightParen)) {
+ // look for comment
+ if (match(Tok_Comma) && matchStringOrNull(&comment)) {
+ if (!match(Tok_RightParen)) {
+ // look for encoding
+ if (match(Tok_Comma)) {
+ if (matchEncoding(&utf8)) {
+ if (!match(Tok_RightParen)) {
+ // look for the plural quantifier,
+ // this can be a number, an identifier or
+ // a function call,
+ // so for simplicity we mark it as plural if
+ // we know we have a comma instead of an
+ // right parentheses.
+ plural = match(Tok_Comma);
+ }
+ } else {
+ // This can be a QTranslator::translate("context",
+ // "source", "comment", n) plural translation
+ if (matchExpression() && match(Tok_RightParen)) {
+ plural = true;
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
+ }
+ extracomment.clear();
+ break;
+ case Tok_Q_DECLARE_TR_FUNCTIONS:
+ case Tok_Q_OBJECT:
+ missing_Q_OBJECT = false;
+ yyTok = getToken();
+ break;
+ case Tok_Ident:
+ prefix += yyIdent;
+ yyTok = getToken();
+ if (yyTok != Tok_ColonColon)
+ prefix.clear();
+ break;
+ case Tok_Comment:
+ if (yyComment.startsWith(QLatin1Char(':'))) {
+ yyComment.remove(0, 1);
+ extracomment.append(yyComment);
+ } else {
+ comment = yyComment.simplified();
+ if (comment.startsWith(QLatin1String(MagicComment))) {
+ comment.remove(0, sizeof(MagicComment) - 1);
+ int k = comment.indexOf(QLatin1Char(' '));
+ if (k == -1) {
+ context = comment;
+ } else {
+ context = comment.left(k);
+ comment.remove(0, k + 1);
+ recordMessage(tor, yyLineNo, context, QString(), comment, extracomment, false, false);
+ }
+
+ /*
+ Provide a backdoor for people using "using
+ namespace". See the manual for details.
+ */
+ k = 0;
+ while ((k = context.indexOf(strColons, k)) != -1) {
+ qualifiedContexts.insert(context.mid(k + 2), context);
+ k++;
+ }
+ }
+ }
+ yyTok = getToken();
+ break;
+ case Tok_Arrow:
+ yyTok = getToken();
+ if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
+ qWarning("%s:%d: Cannot invoke tr() like this",
+ qPrintable(yyFileName), yyLineNo);
+ break;
+ case Tok_ColonColon:
+ if (yyBraceDepth == namespaces.count() && yyParenDepth == 0 && !yyTokColonSeen)
+ functionContext = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, false);
+ prefix += strColons;
+ yyTok = getToken();
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ if (yyTok == Tok_Ident && yyBraceDepth == namespaces.count() && yyParenDepth == 0)
+ functionName = yyIdent;
+#endif
+ break;
+ case Tok_RightBrace:
+ case Tok_Semicolon:
+ prefix.clear();
+ extracomment.clear();
+ yyTokColonSeen = false;
+ if (yyBraceDepth >= 0 && yyBraceDepth + 1 == namespaces.count())
+ namespaces.removeLast();
+ if (yyBraceDepth == namespaces.count()) {
+ if (missing_Q_OBJECT) {
+ if (needs_Q_OBJECT.contains(functionContext)) {
+ qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
+ qPrintable(yyFileName), yyLineNo,
+ qPrintable(functionContext));
+ } else {
+ lacks_Q_OBJECT.insert(functionContext);
+ }
+ }
+ functionContext = defaultContext;
+ missing_Q_OBJECT = false;
+ }
+ yyTok = getToken();
+ break;
+ case Tok_Colon:
+ yyTokColonSeen = true;
+ yyTok = getToken();
+ break;
+ case Tok_LeftParen:
+ case Tok_RightParen:
+ case Tok_LeftBrace:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ break;
+ default:
+ yyTok = getToken();
+ break;
+ }
+ }
+
+ if (yyBraceDepth != 0)
+ qWarning("%s:%d: Unbalanced braces in C++ code (or abuse of the C++"
+ " preprocessor)\n",
+ qPrintable(yyFileName), yyBraceLineNo);
+ else if (yyParenDepth != 0)
+ qWarning("%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++"
+ " preprocessor)\n",
+ qPrintable(yyFileName), yyParenLineNo);
+}
+
+/*
+ Fetches tr() calls in C++ code in UI files (inside "<function>"
+ tag). This mechanism is obsolete.
+*/
+void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context)
+{
+ yyInStr = in;
+ yyInPos = 0;
+ yyFileName = QString();
+ yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
+ yyForceUtf8 = true;
+ yySourceIsUnicode = true;
+ yySavedBraceDepth.clear();
+ yySavedParenDepth.clear();
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+ yyBraceLineNo = 1;
+ yyParenLineNo = 1;
+ yyCh = getChar();
+
+ parse(&translator, context, QString());
+}
+
+
+bool loadCPP(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QString defaultContext = cd.m_defaultContext;
+
+ yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
+ yyForceUtf8 = false;
+ QTextStream ts(&dev);
+ QByteArray codecName = cd.m_codecForSource.isEmpty()
+ ? translator.codecName() : cd.m_codecForSource;
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+ yySourceCodec = ts.codec();
+ if (yySourceCodec->name() == "UTF-16")
+ translator.setCodecName("System");
+ yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-");
+ yyInStr = ts.readAll();
+ yyInPos = 0;
+ yyFileName = cd.m_sourceFileName;
+ yySavedBraceDepth.clear();
+ yySavedParenDepth.clear();
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+ yyBraceLineNo = 1;
+ yyParenLineNo = 1;
+ yyCh = getChar();
+
+ parse(&translator, defaultContext, defaultContext);
+
+ return true;
+}
+
+int initCPP()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("cpp");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.description = QObject::tr("C++ source files");
+ format.loader = &loadCPP;
+ format.saver = 0;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initCPP)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/formats.pri b/tools/linguist/shared/formats.pri
new file mode 100644
index 0000000000..9c8072bbbe
--- /dev/null
+++ b/tools/linguist/shared/formats.pri
@@ -0,0 +1,26 @@
+
+# infrastructure
+QT *= xml
+
+INCLUDEPATH *= $$PWD
+
+SOURCES += \
+ $$PWD/numerus.cpp \
+ $$PWD/translator.cpp \
+ $$PWD/translatormessage.cpp
+
+HEADERS += \
+ $$PWD/translator.h \
+ $$PWD/translatormessage.h
+
+# "real" formats readers and writers
+SOURCES += \
+ $$PWD/qm.cpp \
+ $$PWD/qph.cpp \
+ $$PWD/po.cpp \
+ $$PWD/ts.cpp \
+ $$PWD/ui.cpp \
+ $$PWD/cpp.cpp \
+ $$PWD/java.cpp \
+ $$PWD/qscript.cpp \
+ $$PWD/xliff.cpp
diff --git a/tools/linguist/shared/java.cpp b/tools/linguist/shared/java.cpp
new file mode 100644
index 0000000000..912a8d7fe4
--- /dev/null
+++ b/tools/linguist/shared/java.cpp
@@ -0,0 +1,655 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QRegExp>
+#include <QtCore/QStack>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+enum { Tok_Eof, Tok_class, Tok_return, Tok_tr,
+ Tok_translate, Tok_Ident, Tok_Package,
+ Tok_Comment, Tok_String, Tok_Colon, Tok_Dot,
+ Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen,
+ Tok_RightParen, Tok_Comma, Tok_Semicolon,
+ Tok_Integer, Tok_Plus, Tok_PlusPlus, Tok_PlusEq };
+
+class Scope
+{
+ public:
+ QString name;
+ enum Type {Clazz, Function, Other} type;
+ int line;
+
+ Scope(const QString & name, Type type, int line) :
+ name(name),
+ type(type),
+ line(line)
+ {}
+
+ ~Scope()
+ {}
+};
+
+/*
+ The tokenizer maintains the following global variables. The names
+ should be self-explanatory.
+*/
+
+static QString yyFileName;
+static QChar yyCh;
+static QString yyIdent;
+static QString yyComment;
+static QString yyString;
+
+
+static qlonglong yyInteger;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+static int yyParenLineNo;
+static int yyTok;
+
+// the string to read from and current position in the string
+static QString yyInStr;
+static int yyInPos;
+
+// The parser maintains the following global variables.
+static QString yyPackage;
+static QStack<Scope*> yyScope;
+static QString yyDefaultContext;
+
+static QChar getChar()
+{
+ if (yyInPos >= yyInStr.size())
+ return EOF;
+ QChar c = yyInStr[yyInPos++];
+ if (c.unicode() == '\n')
+ ++yyCurLineNo;
+ return c.unicode();
+}
+
+static int getToken()
+{
+ const char tab[] = "bfnrt\"\'\\";
+ const char backTab[] = "\b\f\n\r\t\"\'\\";
+
+ yyIdent.clear();
+ yyComment.clear();
+ yyString.clear();
+
+ while ( yyCh != EOF ) {
+ yyLineNo = yyCurLineNo;
+
+ if ( yyCh.isLetter() || yyCh.toLatin1() == '_' ) {
+ do {
+ yyIdent.append(yyCh);
+ yyCh = getChar();
+ } while ( yyCh.isLetterOrNumber() || yyCh.toLatin1() == '_' );
+
+ if (yyTok != Tok_Dot) {
+ switch ( yyIdent.at(0).toLatin1() ) {
+ case 'r':
+ if ( yyIdent == QLatin1String("return") )
+ return Tok_return;
+ break;
+ case 'c':
+ if ( yyIdent == QLatin1String("class") )
+ return Tok_class;
+ break;
+ }
+ }
+ switch ( yyIdent.at(0).toLatin1() ) {
+ case 'T':
+ // TR() for when all else fails
+ if ( yyIdent == QLatin1String("TR") )
+ return Tok_tr;
+ break;
+ case 'p':
+ if( yyIdent == QLatin1String("package") )
+ return Tok_Package;
+ break;
+ case 't':
+ if ( yyIdent == QLatin1String("tr") )
+ return Tok_tr;
+ if ( yyIdent == QLatin1String("translate") )
+ return Tok_translate;
+ }
+ return Tok_Ident;
+ } else {
+ switch ( yyCh.toLatin1() ) {
+
+ case '/':
+ yyCh = getChar();
+ if ( yyCh == QLatin1Char('/') ) {
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ break;
+ yyComment.append(yyCh);
+ } while (yyCh != QLatin1Char('\n'));
+ return Tok_Comment;
+
+ } else if ( yyCh == QLatin1Char('*') ) {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ while ( !metAsterSlash ) {
+ yyCh = getChar();
+ if ( yyCh == EOF ) {
+ qFatal( "%s: Unterminated Java comment starting at"
+ " line %d\n",
+ qPrintable(yyFileName), yyLineNo );
+
+ return Tok_Comment;
+ }
+
+ yyComment.append( yyCh );
+
+ if ( yyCh == QLatin1Char('*') )
+ metAster = true;
+ else if ( metAster && yyCh == QLatin1Char('/') )
+ metAsterSlash = true;
+ else
+ metAster = false;
+ }
+ yyComment.chop(2);
+ yyCh = getChar();
+
+ return Tok_Comment;
+ }
+ break;
+ case '"':
+ yyCh = getChar();
+
+ while ( yyCh != EOF && yyCh != QLatin1Char('\n') && yyCh != QLatin1Char('"') ) {
+ if ( yyCh == QLatin1Char('\\') ) {
+ yyCh = getChar();
+ if ( yyCh == QLatin1Char('u') ) {
+ yyCh = getChar();
+ uint unicode(0);
+ for (int i = 4; i > 0; --i) {
+ unicode = unicode << 4;
+ if( yyCh.isDigit() ) {
+ unicode += yyCh.digitValue();
+ }
+ else {
+ int sub(yyCh.toLower().toAscii() - 87);
+ if( sub > 15 || sub < 10) {
+ qFatal( "%s:%d: Invalid Unicode",
+ qPrintable(yyFileName), yyLineNo );
+ }
+ unicode += sub;
+ }
+ yyCh = getChar();
+ }
+ yyString.append(QChar(unicode));
+ }
+ else if ( yyCh == QLatin1Char('\n') ) {
+ yyCh = getChar();
+ }
+ else {
+ yyString.append( QLatin1Char(backTab[strchr( tab, yyCh.toAscii() ) - tab]) );
+ yyCh = getChar();
+ }
+ } else {
+ yyString.append(yyCh);
+ yyCh = getChar();
+ }
+ }
+
+ if ( yyCh != QLatin1Char('"') )
+ qFatal( "%s:%d: Unterminated string",
+ qPrintable(yyFileName), yyLineNo );
+
+ yyCh = getChar();
+
+ return Tok_String;
+
+ case ':':
+ yyCh = getChar();
+ return Tok_Colon;
+ case '\'':
+ yyCh = getChar();
+
+ if ( yyCh == QLatin1Char('\\') )
+ yyCh = getChar();
+ do {
+ yyCh = getChar();
+ } while ( yyCh != EOF && yyCh != QLatin1Char('\'') );
+ yyCh = getChar();
+ break;
+ case '{':
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case '.':
+ yyCh = getChar();
+ return Tok_Dot;
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '+':
+ yyCh = getChar();
+ if (yyCh == QLatin1Char('+')) {
+ yyCh = getChar();
+ return Tok_PlusPlus;
+ }
+ if( yyCh == QLatin1Char('=') ){
+ yyCh = getChar();
+ return Tok_PlusEq;
+ }
+ return Tok_Plus;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ QByteArray ba;
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ bool hex = yyCh == QLatin1Char('x');
+ if ( hex ) {
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ }
+ while ( hex ? isxdigit(yyCh.toLatin1()) : yyCh.isDigit() ) {
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ }
+ bool ok;
+ yyInteger = ba.toLongLong(&ok);
+ if (ok) return Tok_Integer;
+ break;
+ }
+ default:
+ yyCh = getChar();
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+static bool match( int t )
+{
+ bool matches = ( yyTok == t );
+ if ( matches )
+ yyTok = getToken();
+ return matches;
+}
+
+static bool matchString( QString &s )
+{
+ if ( yyTok != Tok_String )
+ return false;
+
+ s = yyString;
+ yyTok = getToken();
+ while ( yyTok == Tok_Plus ) {
+ yyTok = getToken();
+ if (yyTok == Tok_String)
+ s += yyString;
+ else {
+ qWarning( "%s:%d: String used in translation can only contain strings"
+ " concatenated with other strings, not expressions or numbers.",
+ qPrintable(yyFileName), yyLineNo );
+ return false;
+ }
+ yyTok = getToken();
+ }
+ return true;
+}
+
+static bool matchInteger( qlonglong *number)
+{
+ bool matches = (yyTok == Tok_Integer);
+ if (matches) {
+ yyTok = getToken();
+ *number = yyInteger;
+ }
+ return matches;
+}
+
+static bool matchStringOrNull(QString &s)
+{
+ bool matches = matchString(s);
+ qlonglong num = 0;
+ if (!matches) matches = matchInteger(&num);
+ return matches && num == 0;
+}
+
+/*
+ * match any expression that can return a number, which can be
+ * 1. Literal number (e.g. '11')
+ * 2. simple identifier (e.g. 'm_count')
+ * 3. simple function call (e.g. 'size()' )
+ * 4. function call on an object (e.g. 'list.size()')
+ * 5. function call on an object (e.g. 'list->size()')
+ *
+ * Other cases:
+ * size(2,4)
+ * list().size()
+ * list(a,b).size(2,4)
+ * etc...
+ */
+static bool matchExpression()
+{
+ if (match(Tok_Integer)) {
+ return true;
+ }
+
+ int parenlevel = 0;
+ while (match(Tok_Ident) || parenlevel > 0) {
+ if (yyTok == Tok_RightParen) {
+ if (parenlevel == 0) break;
+ --parenlevel;
+ yyTok = getToken();
+ } else if (yyTok == Tok_LeftParen) {
+ yyTok = getToken();
+ if (yyTok == Tok_RightParen) {
+ yyTok = getToken();
+ } else {
+ ++parenlevel;
+ }
+ } else if (yyTok == Tok_Ident) {
+ continue;
+ } else if (parenlevel == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static const QString context()
+{
+ QString context(yyPackage);
+ bool innerClass = false;
+ for (int i = 0; i < yyScope.size(); ++i) {
+ if (yyScope.at(i)->type == Scope::Clazz) {
+ if (innerClass)
+ context.append(QLatin1String("$"));
+ else
+ context.append(QLatin1String("."));
+
+ context.append(yyScope.at(i)->name);
+ innerClass = true;
+ }
+ }
+ return context.isEmpty() ? yyDefaultContext : context;
+}
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, bool plural)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ yyFileName, yyLineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ tor->extend(msg);
+}
+
+static void parse( Translator *tor )
+{
+ QString text;
+ QString com;
+ QString extracomment;
+
+ yyCh = getChar();
+
+ yyTok = getToken();
+ while ( yyTok != Tok_Eof ) {
+ switch ( yyTok ) {
+ case Tok_class:
+ yyTok = getToken();
+ if(yyTok == Tok_Ident) {
+ yyScope.push(new Scope(yyIdent, Scope::Clazz, yyLineNo));
+ }
+ else {
+ qFatal( "%s:%d: Class must be followed by a classname",
+ qPrintable(yyFileName), yyLineNo );
+ }
+ while (!match(Tok_LeftBrace)) {
+ yyTok = getToken();
+ }
+ break;
+
+ case Tok_tr:
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) && matchString(text) ) {
+ com.clear();
+ bool plural = false;
+
+ if ( match(Tok_RightParen) ) {
+ // no comment
+ } else if (match(Tok_Comma) && matchStringOrNull(com)) { //comment
+ if ( match(Tok_RightParen)) {
+ // ok,
+ } else if (match(Tok_Comma)) {
+ plural = true;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, context(), text, com, extracomment, plural);
+ }
+ break;
+ case Tok_translate:
+ {
+ QString contextOverride;
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) &&
+ matchString(contextOverride) &&
+ match(Tok_Comma) &&
+ matchString(text) ) {
+
+ com.clear();
+ bool plural = false;
+ if (!match(Tok_RightParen)) {
+ // look for comment
+ if ( match(Tok_Comma) && matchStringOrNull(com)) {
+ if (!match(Tok_RightParen)) {
+ if (match(Tok_Comma) && matchExpression() && match(Tok_RightParen)) {
+ plural = true;
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, contextOverride, text, com, extracomment, plural);
+ }
+ }
+ break;
+
+ case Tok_Ident:
+ yyTok = getToken();
+ break;
+
+ case Tok_Comment:
+ if (yyComment.startsWith(QLatin1Char(':'))) {
+ yyComment.remove(0, 1);
+ extracomment.append(yyComment);
+ }
+ yyTok = getToken();
+ break;
+
+ case Tok_RightBrace:
+ if ( yyScope.isEmpty() ) {
+ qFatal( "%s:%d: Unbalanced right brace in Java code\n",
+ qPrintable(yyFileName), yyLineNo );
+ }
+ else
+ delete (yyScope.pop());
+ extracomment.clear();
+ yyTok = getToken();
+ break;
+
+ case Tok_LeftBrace:
+ yyScope.push(new Scope(QString(), Scope::Other, yyLineNo));
+ yyTok = getToken();
+ break;
+
+ case Tok_Semicolon:
+ extracomment.clear();
+ yyTok = getToken();
+ break;
+
+ case Tok_Package:
+ yyTok = getToken();
+ while(!match(Tok_Semicolon)) {
+ switch(yyTok) {
+ case Tok_Ident:
+ yyPackage.append(yyIdent);
+ break;
+ case Tok_Dot:
+ yyPackage.append(QLatin1String("."));
+ break;
+ default:
+ qFatal( "%s:%d: Package keyword should be followed by com.package.name;",
+ qPrintable(yyFileName), yyLineNo );
+ break;
+ }
+ yyTok = getToken();
+ }
+ break;
+
+ default:
+ yyTok = getToken();
+ }
+ }
+
+ if ( !yyScope.isEmpty() )
+ qFatal( "%s:%d: Unbalanced braces in Java code\n",
+ qPrintable(yyFileName), yyScope.top()->line );
+ else if ( yyParenDepth != 0 )
+ qFatal( "%s:%d: Unbalanced parentheses in Java code\n",
+ qPrintable(yyFileName), yyParenLineNo );
+}
+
+
+bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ //void LupdateApplication::fetchtr_java( const QString &fileName, Translator *tor,
+ //const QString &defaultContext, bool mustExist, const QByteArray &codecForSource )
+
+ yyDefaultContext = cd.m_defaultContext;
+ yyInPos = -1;
+ yyFileName = cd.m_sourceFileName;
+ yyPackage.clear();
+ yyScope.clear();
+ yyTok = -1;
+ yyParenDepth = 0;
+ yyCurLineNo = 0;
+ yyParenLineNo = 1;
+
+ QTextStream ts(&dev);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+ yyInStr = ts.readAll();
+ yyInPos = 0;
+ yyFileName = cd.m_sourceFileName;
+ yyCurLineNo = 1;
+ yyParenLineNo = 1;
+ yyCh = getChar();
+
+ parse(&translator);
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+int initJava()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("java");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.description = QObject::tr("Java source files");
+ format.loader = &loadJava;
+ format.saver = 0;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initJava)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/make-qscript.sh b/tools/linguist/shared/make-qscript.sh
new file mode 100755
index 0000000000..42cab7a05d
--- /dev/null
+++ b/tools/linguist/shared/make-qscript.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+me=$(dirname $0)
+mkdir -p $me/out
+(cd $me/out && ${QLALR-qlalr} --no-debug --troll --no-lines ../qscript.g)
+
+for f in $me/out/*.{h,cpp}; do
+ n=$(basename $f)
+ p4 open $me/../$n
+ cp $f $me/../$n
+done
+
+p4 revert -a $me/../...
+p4 diff -du $me/../...
diff --git a/tools/linguist/shared/numerus.cpp b/tools/linguist/shared/numerus.cpp
new file mode 100644
index 0000000000..f3a29ccaf7
--- /dev/null
+++ b/tools/linguist/shared/numerus.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMap>
+
+#include <private/qtranslator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static const uchar englishStyleRules[] =
+ { Q_EQ, 1 };
+static const uchar frenchStyleRules[] =
+ { Q_LEQ, 1 };
+static const uchar latvianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_NEQ, 0 };
+static const uchar irishStyleRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 2 };
+static const uchar czechRules[] =
+ { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 2, 4 };
+static const uchar slovakRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_BETWEEN, 2, 4 };
+static const uchar macedonianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_10 | Q_EQ, 2 };
+static const uchar lithuanianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_MOD_10 | Q_EQ, 2, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar russianStyleRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar polishRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar romanianRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 19 };
+static const uchar slovenianRules[] =
+ { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_100 | Q_EQ, 2, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 3, 4 };
+static const uchar malteseRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 10, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 11, 19 };
+static const uchar welshRules[] =
+ { Q_EQ, 0, Q_NEWRULE,
+ Q_EQ, 1, Q_NEWRULE,
+ Q_BETWEEN, 2, 5, Q_NEWRULE,
+ Q_EQ, 6 };
+static const uchar arabicRules[] =
+ { Q_EQ, 0, Q_NEWRULE,
+ Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 2, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE,
+ Q_MOD_100 | Q_NEQ, 0 };
+
+static const char * const japaneseStyleForms[] = { "Universal Form", 0 };
+static const char * const englishStyleForms[] = { "Singular", "Plural", 0 };
+static const char * const frenchStyleForms[] = { "Singular", "Plural", 0 };
+static const char * const latvianForms[] = { "Singular", "Plural", "Nullar", 0 };
+static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const czechForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const slovakForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const lithuanianForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const russianStyleForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const polishForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const romanianForms[] =
+ { "Singular", "Plural Form for 2 to 19", "Plural", 0 };
+static const char * const slovenianForms[] = { "Singular", "Dual", "Trial", "Plural", 0 };
+static const char * const malteseForms[] =
+ { "Singular", "Plural Form for 2 to 10", "Plural Form for 11 to 19", "Plural", 0 };
+static const char * const welshForms[] =
+ { "Nullar", "Singular", "Dual", "Sexal", "Plural", 0 };
+static const char * const arabicForms[] =
+ { "Nullar", "Singular", "Dual", "Minority Plural", "Plural", "Plural Form for 100, 200, ...", 0 };
+
+#define EOL QLocale::C
+
+static const QLocale::Language japaneseStyleLanguages[] = {
+ QLocale::Afan,
+ QLocale::Armenian,
+ QLocale::Bhutani,
+ QLocale::Bislama,
+ QLocale::Burmese,
+ QLocale::Chinese,
+ QLocale::FijiLanguage,
+ QLocale::Guarani,
+ QLocale::Hungarian,
+ QLocale::Indonesian,
+ QLocale::Japanese,
+ QLocale::Javanese,
+ QLocale::Korean,
+ QLocale::Malay,
+ QLocale::NauruLanguage,
+ QLocale::Persian,
+ QLocale::Sundanese,
+ QLocale::Thai,
+ QLocale::Tibetan,
+ QLocale::Vietnamese,
+ QLocale::Yoruba,
+ QLocale::Zhuang,
+ EOL
+};
+
+static const QLocale::Language englishStyleLanguages[] = {
+ QLocale::Abkhazian,
+ QLocale::Afar,
+ QLocale::Afrikaans,
+ QLocale::Albanian,
+ QLocale::Amharic,
+ QLocale::Assamese,
+ QLocale::Aymara,
+ QLocale::Azerbaijani,
+ QLocale::Bashkir,
+ QLocale::Basque,
+ QLocale::Bengali,
+ QLocale::Bihari,
+ // Missing: Bokmal,
+ QLocale::Bulgarian,
+ QLocale::Cambodian,
+ QLocale::Catalan,
+ QLocale::Cornish,
+ QLocale::Corsican,
+ QLocale::Danish,
+ QLocale::Dutch,
+ QLocale::English,
+ QLocale::Esperanto,
+ QLocale::Estonian,
+ QLocale::Faroese,
+ QLocale::Finnish,
+ // Missing: Friulian,
+ QLocale::Frisian,
+ QLocale::Galician,
+ QLocale::Georgian,
+ QLocale::German,
+ QLocale::Greek,
+ QLocale::Greenlandic,
+ QLocale::Gujarati,
+ QLocale::Hausa,
+ QLocale::Hebrew,
+ QLocale::Hindi,
+ QLocale::Icelandic,
+ QLocale::Interlingua,
+ QLocale::Interlingue,
+ QLocale::Italian,
+ QLocale::Kannada,
+ QLocale::Kashmiri,
+ QLocale::Kazakh,
+ QLocale::Kinyarwanda,
+ QLocale::Kirghiz,
+ QLocale::Kurdish,
+ QLocale::Kurundi,
+ QLocale::Laothian,
+ QLocale::Latin,
+ // Missing: Letzeburgesch,
+ QLocale::Lingala,
+ QLocale::Malagasy,
+ QLocale::Malayalam,
+ QLocale::Marathi,
+ QLocale::Mongolian,
+ // Missing: Nahuatl,
+ QLocale::Nepali,
+ // Missing: Northern Sotho,
+ QLocale::Norwegian,
+ QLocale::Nynorsk,
+ QLocale::Occitan,
+ QLocale::Oriya,
+ QLocale::Pashto,
+ QLocale::Portuguese,
+ QLocale::Punjabi,
+ QLocale::Quechua,
+ QLocale::RhaetoRomance,
+ QLocale::Sesotho,
+ QLocale::Setswana,
+ QLocale::Shona,
+ QLocale::Sindhi,
+ QLocale::Singhalese,
+ QLocale::Siswati,
+ QLocale::Somali,
+ QLocale::Spanish,
+ QLocale::Swahili,
+ QLocale::Swedish,
+ QLocale::Tagalog,
+ QLocale::Tajik,
+ QLocale::Tamil,
+ QLocale::Tatar,
+ QLocale::Telugu,
+ QLocale::TongaLanguage,
+ QLocale::Tsonga,
+ QLocale::Turkish,
+ QLocale::Turkmen,
+ QLocale::Twi,
+ QLocale::Uigur,
+ QLocale::Uzbek,
+ QLocale::Volapuk,
+ QLocale::Wolof,
+ QLocale::Xhosa,
+ QLocale::Yiddish,
+ QLocale::Zulu,
+ EOL
+};
+static const QLocale::Language frenchStyleLanguages[] = {
+ // keep synchronized with frenchStyleCountries
+ QLocale::Breton,
+ QLocale::French,
+ QLocale::Portuguese,
+ // Missing: Filipino,
+ QLocale::Tigrinya,
+ // Missing: Walloon
+ EOL
+};
+static const QLocale::Language latvianLanguage[] = { QLocale::Latvian, EOL };
+static const QLocale::Language irishStyleLanguages[] = {
+ QLocale::Divehi,
+ QLocale::Gaelic,
+ QLocale::Inuktitut,
+ QLocale::Inupiak,
+ QLocale::Irish,
+ QLocale::Manx,
+ QLocale::Maori,
+ // Missing: Sami,
+ QLocale::Samoan,
+ QLocale::Sanskrit,
+ EOL
+};
+static const QLocale::Language czechLanguage[] = { QLocale::Czech, EOL };
+static const QLocale::Language slovakLanguage[] = { QLocale::Slovak, EOL };
+static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL };
+static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL };
+static const QLocale::Language russianStyleLanguages[] = {
+ QLocale::Bosnian,
+ QLocale::Byelorussian,
+ QLocale::Croatian,
+ QLocale::Russian,
+ QLocale::Serbian,
+ QLocale::SerboCroatian,
+ QLocale::Ukrainian,
+ EOL
+};
+static const QLocale::Language polishLanguage[] = { QLocale::Polish, EOL };
+static const QLocale::Language romanianLanguages[] = {
+ QLocale::Moldavian,
+ QLocale::Romanian,
+ EOL
+};
+static const QLocale::Language slovenianLanguage[] = { QLocale::Slovenian, EOL };
+static const QLocale::Language malteseLanguage[] = { QLocale::Maltese, EOL };
+static const QLocale::Language welshLanguage[] = { QLocale::Welsh, EOL };
+static const QLocale::Language arabicLanguage[] = { QLocale::Arabic, EOL };
+
+static const QLocale::Country frenchStyleCountries[] = {
+ // keep synchronized with frenchStyleLanguages
+ QLocale::AnyCountry,
+ QLocale::AnyCountry,
+ QLocale::Brazil,
+ QLocale::AnyCountry
+};
+struct NumerusTableEntry {
+ const uchar *rules;
+ int rulesSize;
+ const char * const *forms;
+ const QLocale::Language *languages;
+ const QLocale::Country *countries;
+};
+
+static const NumerusTableEntry numerusTable[] = {
+ { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0 },
+ { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0 },
+ { frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages,
+ frenchStyleCountries },
+ { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0 },
+ { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0 },
+ { czechRules, sizeof(czechRules), czechForms, czechLanguage, 0 },
+ { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguage, 0 },
+ { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0 },
+ { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0 },
+ { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0 },
+ { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0 },
+ { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0 },
+ { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0 },
+ { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0 },
+ { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0 },
+ { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 }
+};
+
+static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]);
+
+// magic number for the file
+static const int MagicLength = 16;
+static const uchar magic[MagicLength] = {
+ 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
+ 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
+};
+
+bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
+ QByteArray *rules, QStringList *forms)
+{
+ while (true) {
+ for (int i = 0; i < NumerusTableSize; ++i) {
+ const NumerusTableEntry &entry = numerusTable[i];
+ for (int j = 0; entry.languages[j] != EOL; ++j) {
+ if (entry.languages[j] == language
+ && ((!entry.countries && country == QLocale::AnyCountry)
+ || (entry.countries && entry.countries[j] == country))) {
+ if (rules) {
+ *rules = QByteArray::fromRawData(reinterpret_cast<const char *>(entry.rules),
+ entry.rulesSize);
+ }
+ if (forms) {
+ forms->clear();
+ for (int k = 0; entry.forms[k]; ++k)
+ forms->append(QLatin1String(entry.forms[k]));
+ }
+ return true;
+ }
+ }
+ }
+
+ if (country == QLocale::AnyCountry)
+ break;
+ country = QLocale::AnyCountry;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp
new file mode 100644
index 0000000000..e9375e96f1
--- /dev/null
+++ b/tools/linguist/shared/po.cpp
@@ -0,0 +1,662 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QIODevice>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+#include <ctype.h>
+
+#define MAGIC_OBSOLETE_REFERENCE "Obsolete_PO_entries"
+
+// Uncomment if you wish to hard wrap long lines in .po files. Note that this
+// affects only msg strings, not comments.
+//#define HARD_WRAP_LONG_WORDS
+
+QT_BEGIN_NAMESPACE
+
+static const int MAX_LEN = 79;
+
+static QString poEscapedString(const QString &prefix, const QString &keyword,
+ bool noWrap, const QString &ba)
+{
+ QStringList lines;
+ int off = 0;
+ QString res;
+ while (off < ba.length()) {
+ ushort c = ba[off++].unicode();
+ switch (c) {
+ case '\n':
+ res += QLatin1String("\\n");
+ lines.append(res);
+ res.clear();
+ break;
+ case '\r':
+ res += QLatin1String("\\r");
+ break;
+ case '\t':
+ res += QLatin1String("\\t");
+ break;
+ case '\v':
+ res += QLatin1String("\\v");
+ break;
+ case '\a':
+ res += QLatin1String("\\a");
+ break;
+ case '\b':
+ res += QLatin1String("\\b");
+ break;
+ case '\f':
+ res += QLatin1String("\\f");
+ break;
+ case '"':
+ res += QLatin1String("\\\"");
+ break;
+ case '\\':
+ res += QLatin1String("\\\\");
+ break;
+ default:
+ if (c < 32) {
+ res += QLatin1String("\\x");
+ res += QString::number(c, 16);
+ if (off < ba.length() && isxdigit(ba[off].unicode()))
+ res += QLatin1String("\"\"");
+ } else {
+ res += QChar(c);
+ }
+ break;
+ }
+ }
+ if (!res.isEmpty())
+ lines.append(res);
+ if (!lines.isEmpty()) {
+ if (!noWrap) {
+ if (lines.count() != 1 ||
+ lines.first().length() > MAX_LEN - keyword.length() - prefix.length() - 3)
+ {
+ QStringList olines = lines;
+ lines = QStringList(QString());
+ const int maxlen = MAX_LEN - prefix.length() - 2;
+ foreach (const QString &line, olines) {
+ int off = 0;
+ while (off + maxlen < line.length()) {
+ int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1) + 1;
+ if (idx == off) {
+#ifdef HARD_WRAP_LONG_WORDS
+ // This doesn't seem too nice, but who knows ...
+ idx = off + maxlen;
+#else
+ idx = line.indexOf(QLatin1Char(' '), off + maxlen) + 1;
+ if (!idx)
+ break;
+#endif
+ }
+ lines.append(line.mid(off, idx - off));
+ off = idx;
+ }
+ lines.append(line.mid(off));
+ }
+ }
+ } else if (lines.count() > 1) {
+ lines.prepend(QString());
+ }
+ }
+ return prefix + keyword + QLatin1String(" \"") +
+ lines.join(QLatin1String("\"\n") + prefix + QLatin1Char('"')) +
+ QLatin1String("\"\n");
+}
+
+static QString poEscapedLines(const QString &prefix, bool addSpace, const QStringList &lines)
+{
+ QString out;
+ foreach (const QString &line, lines) {
+ out += prefix;
+ if (addSpace && !line.isEmpty())
+ out += QLatin1Char(' ' );
+ out += line;
+ out += QLatin1Char('\n');
+ }
+ return out;
+}
+
+static QString poEscapedLines(const QString &prefix, bool addSpace, const QString &in0)
+{
+ QString in = in0;
+ if (in.endsWith(QLatin1Char('\n')))
+ in.chop(1);
+ return poEscapedLines(prefix, addSpace, in.split(QLatin1Char('\n')));
+}
+
+static QString poWrappedEscapedLines(const QString &prefix, bool addSpace, const QString &line)
+{
+ const int maxlen = MAX_LEN - prefix.length();
+ QStringList lines;
+ int off = 0;
+ while (off + maxlen < line.length()) {
+ int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1);
+ if (idx < off) {
+#if 0 //def HARD_WRAP_LONG_WORDS
+ // This cannot work without messing up semantics, so do not even try.
+#else
+ idx = line.indexOf(QLatin1Char(' '), off + maxlen);
+ if (idx < 0)
+ break;
+#endif
+ }
+ lines.append(line.mid(off, idx - off));
+ off = idx + 1;
+ }
+ lines.append(line.mid(off));
+ return poEscapedLines(prefix, addSpace, lines);
+}
+
+struct PoItem
+{
+public:
+ PoItem()
+ : isPlural(false), isFuzzy(false)
+ {}
+
+
+public:
+ QString id;
+ QString context;
+ QString tscomment;
+ QString oldTscomment;
+ QString lineNumber;
+ QString fileName;
+ QString references;
+ QString translatorComments;
+ QString automaticComments;
+ QString msgId;
+ QString oldMsgId;
+ QStringList msgStr;
+ bool isPlural;
+ bool isFuzzy;
+ QHash<QString, QString> extra;
+};
+
+
+static bool isTranslationLine(const QString &line)
+{
+ return line.startsWith(QLatin1String("#~ msgstr"))
+ || line.startsWith(QLatin1String("msgstr"));
+}
+
+static QString slurpEscapedString(const QStringList &lines, int & l,
+ int offset, const QString &prefix, ConversionData &cd)
+{
+ QString msg;
+ int stoff;
+
+ for (; l < lines.size(); ++l) {
+ const QString &line = lines.at(l);
+ if (line.isEmpty() || !line.startsWith(prefix))
+ break;
+ while (line[offset].isSpace()) // No length check, as string has no trailing spaces.
+ offset++;
+ if (line[offset].unicode() != '"')
+ break;
+ offset++;
+ forever {
+ if (offset == line.length())
+ goto premature_eol;
+ ushort c = line[offset++].unicode();
+ if (c == '"') {
+ if (offset == line.length())
+ break;
+ while (line[offset].isSpace())
+ offset++;
+ if (line[offset++].unicode() != '"') {
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: extra characters on line %1.")
+ .arg(l + 1));
+ break;
+ }
+ continue;
+ }
+ if (c == '\\') {
+ if (offset == line.length())
+ goto premature_eol;
+ c = line[offset++].unicode();
+ switch (c) {
+ case 'r':
+ msg += QLatin1Char('\r'); // Maybe just throw it away?
+ break;
+ case 'n':
+ msg += QLatin1Char('\n');
+ break;
+ case 't':
+ msg += QLatin1Char('\t');
+ break;
+ case 'v':
+ msg += QLatin1Char('\v');
+ break;
+ case 'a':
+ msg += QLatin1Char('\a');
+ break;
+ case 'b':
+ msg += QLatin1Char('\b');
+ break;
+ case 'f':
+ msg += QLatin1Char('\f');
+ break;
+ case '"':
+ msg += QLatin1Char('"');
+ break;
+ case '\\':
+ msg += QLatin1Char('\\');
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ stoff = offset - 1;
+ while ((c = line[offset].unicode()) >= '0' && c <= '7')
+ if (++offset == line.length())
+ goto premature_eol;
+ msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 8));
+ break;
+ case 'x':
+ stoff = offset;
+ while (isxdigit(line[offset].unicode()))
+ if (++offset == line.length())
+ goto premature_eol;
+ msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 16));
+ break;
+ default:
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: invalid escape '\\%1' (line %2).")
+ .arg(QChar(c)).arg(l + 1));
+ msg += QLatin1Char('\\');
+ msg += QChar(c);
+ break;
+ }
+ } else {
+ msg += QChar(c);
+ }
+ }
+ offset = prefix.size();
+ }
+ --l;
+ return msg;
+
+premature_eol:
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: premature end of line %1.").arg(l + 1));
+ return QString();
+}
+
+static void slurpComment(QString &msg, const QStringList &lines, int & l)
+{
+ const QChar newline = QLatin1Char('\n');
+ QString prefix = lines.at(l);
+ for (int i = 1; ; i++) {
+ if (prefix.at(i).unicode() != ' ') {
+ prefix.truncate(i);
+ break;
+ }
+ }
+ for (; l < lines.size(); ++l) {
+ const QString &line = lines.at(l);
+ if (line.startsWith(prefix))
+ msg += line.mid(prefix.size());
+ else if (line != QLatin1String("#"))
+ break;
+ msg += newline;
+ }
+ --l;
+}
+
+bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ const QChar quote = QLatin1Char('"');
+ const QChar newline = QLatin1Char('\n');
+ QTextStream in(&dev);
+ bool error = false;
+
+ // format of a .po file entry:
+ // white-space
+ // # translator-comments
+ // #. automatic-comments
+ // #: reference...
+ // #, flag...
+ // #~ msgctxt, msgid*, msgstr - used for obsoleted messages
+ // #| msgctxt, msgid* previous untranslated-string - for fuzzy message
+ // msgctx string-context
+ // msgid untranslated-string
+ // -- For singular:
+ // msgstr translated-string
+ // -- For plural:
+ // msgid_plural untranslated-string-plural
+ // msgstr[0] translated-string
+ // ...
+
+ // we need line based lookahead below.
+ QStringList lines;
+ while (!in.atEnd())
+ lines.append(in.readLine().trimmed());
+ lines.append(QString());
+
+ int l = 0;
+ PoItem item;
+ for (; l != lines.size(); ++l) {
+ QString line = lines.at(l);
+ if (line.isEmpty())
+ continue;
+ if (isTranslationLine(line)) {
+ bool isObsolete = line.startsWith(QLatin1String("#~ msgstr"));
+ const QString prefix = QLatin1String(isObsolete ? "#~ " : "");
+ while (true) {
+ int idx = line.indexOf(QLatin1Char(' '), prefix.length());
+ item.msgStr.append(slurpEscapedString(lines, l, idx, prefix, cd));
+ if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
+ break;
+ ++l;
+ line = lines.at(l);
+ }
+ if (item.msgId.isEmpty()) {
+ QRegExp rx(QLatin1String("\\bX-Language: ([^\n]*)\n"));
+ int idx = rx.indexIn(item.msgStr.first());
+ if (idx >= 0) {
+ translator.setLanguageCode(rx.cap(1));
+ item.msgStr.first().remove(idx, rx.matchedLength());
+ }
+ QRegExp rx2(QLatin1String("\\bX-Source-Language: ([^\n]*)\n"));
+ int idx2 = rx2.indexIn(item.msgStr.first());
+ if (idx2 >= 0) {
+ translator.setSourceLanguageCode(rx2.cap(1));
+ item.msgStr.first().remove(idx2, rx2.matchedLength());
+ }
+ if (item.msgStr.first().indexOf(
+ QRegExp(QLatin1String("\\bX-Virgin-Header:[^\n]*\n"))) >= 0) {
+ item = PoItem();
+ continue;
+ }
+ }
+ // build translator message
+ TranslatorMessage msg;
+ msg.setContext(item.context);
+ if (!item.references.isEmpty()) {
+ foreach (const QString &ref,
+ item.references.split(QRegExp(QLatin1String("\\s")),
+ QString::SkipEmptyParts)) {
+ int pos = ref.lastIndexOf(QLatin1Char(':'));
+ if (pos != -1)
+ msg.addReference(ref.left(pos), ref.mid(pos + 1).toInt());
+ }
+ } else if (isObsolete) {
+ msg.setFileName(QLatin1String(MAGIC_OBSOLETE_REFERENCE));
+ }
+ msg.setId(item.id);
+ msg.setSourceText(item.msgId);
+ msg.setOldSourceText(item.oldMsgId);
+ msg.setComment(item.tscomment);
+ msg.setOldComment(item.oldTscomment);
+ msg.setExtraComment(item.automaticComments);
+ msg.setTranslatorComment(item.translatorComments);
+ msg.setPlural(item.isPlural || item.msgStr.size() > 1);
+ msg.setTranslations(item.msgStr);
+ if (isObsolete)
+ msg.setType(TranslatorMessage::Obsolete);
+ else if (item.isFuzzy)
+ msg.setType(TranslatorMessage::Unfinished);
+ else
+ msg.setType(TranslatorMessage::Finished);
+ msg.setExtras(item.extra);
+
+ //qDebug() << "WRITE: " << context;
+ //qDebug() << "SOURCE: " << msg.sourceText();
+ //qDebug() << flags << msg.m_extra;
+ translator.append(msg);
+ item = PoItem();
+ } else if (line.startsWith(QLatin1Char('#'))) {
+ switch(line.size() < 2 ? 0 : line.at(1).unicode()) {
+ case ':':
+ item.references += line.mid(3);
+ item.references += newline;
+ break;
+ case ',': {
+ QStringList flags =
+ line.mid(2).split(QRegExp(QLatin1String("[, ]")),
+ QString::SkipEmptyParts);
+ if (flags.removeOne(QLatin1String("fuzzy")))
+ item.isFuzzy = true;
+ TranslatorMessage::ExtraData::const_iterator it =
+ item.extra.find(QLatin1String("po-flags"));
+ if (it != item.extra.end())
+ flags.prepend(*it);
+ if (!flags.isEmpty())
+ item.extra[QLatin1String("po-flags")] = flags.join(QLatin1String(", "));
+ break;
+ }
+ case 0:
+ item.translatorComments += newline;
+ break;
+ case ' ':
+ slurpComment(item.translatorComments, lines, l);
+ break;
+ case '.':
+ if (line.startsWith(QLatin1String("#. ts-context "))) {
+ item.context = line.mid(14);
+ } else if (line.startsWith(QLatin1String("#. ts-id "))) {
+ item.id = line.mid(9);
+ } else {
+ item.automaticComments += line.mid(3);
+ item.automaticComments += newline;
+ }
+ break;
+ case '|':
+ if (line.startsWith(QLatin1String("#| msgid "))) {
+ item.oldMsgId = slurpEscapedString(lines, l, 9, QLatin1String("#| "), cd);
+ } else if (line.startsWith(QLatin1String("#| msgid_plural "))) {
+ QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#| "), cd);
+ if (extra != item.oldMsgId)
+ item.extra[QLatin1String("po-old_msgid_plural")] = extra;
+ } else if (line.startsWith(QLatin1String("#| msgctxt "))) {
+ item.oldTscomment = slurpEscapedString(lines, l, 11, QLatin1String("#| "), cd);
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
+ .arg(l + 1).arg(lines[l]));
+ error = true;
+ }
+ break;
+ case '~':
+ if (line.startsWith(QLatin1String("#~ msgid "))) {
+ item.msgId = slurpEscapedString(lines, l, 9, QLatin1String("#~ "), cd);
+ } else if (line.startsWith(QLatin1String("#~ msgid_plural "))) {
+ QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#~ "), cd);
+ if (extra != item.msgId)
+ item.extra[QLatin1String("po-msgid_plural")] = extra;
+ item.isPlural = true;
+ } else if (line.startsWith(QLatin1String("#~ msgctxt "))) {
+ item.tscomment = slurpEscapedString(lines, l, 11, QLatin1String("#~ "), cd);
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
+ .arg(l + 1).arg(lines[l]));
+ error = true;
+ }
+ break;
+ default:
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n"))
+ .arg(l + 1).arg(lines[l]));
+ error = true;
+ break;
+ }
+ } else if (line.startsWith(QLatin1String("msgctxt "))) {
+ item.tscomment = slurpEscapedString(lines, l, 8, QString(), cd);
+ } else if (line.startsWith(QLatin1String("msgid "))) {
+ item.msgId = slurpEscapedString(lines, l, 6, QString(), cd);
+ } else if (line.startsWith(QLatin1String("msgid_plural "))) {
+ QString extra = slurpEscapedString(lines, l, 13, QString(), cd);
+ if (extra != item.msgId)
+ item.extra[QLatin1String("po-msgid_plural")] = extra;
+ item.isPlural = true;
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'\n"))
+ .arg(l + 1).arg(lines[l]));
+ error = true;
+ }
+ }
+ return !error && cd.errors().isEmpty();
+}
+
+bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ bool ok = true;
+ QTextStream out(&dev);
+ //qDebug() << "OUT CODEC: " << out.codec()->name();
+
+ bool first = true;
+ if (translator.messages().isEmpty() || !translator.messages().first().sourceText().isEmpty()) {
+ out <<
+ "# SOME DESCRIPTIVE TITLE.\n"
+ "# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n"
+ "# This file is distributed under the same license as the PACKAGE package.\n"
+ "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"
+ "#\n"
+ "#, fuzzy\n"
+ "msgid \"\"\n"
+ "msgstr \"\"\n"
+ "\"X-Virgin-Header: remove this line if you change anything in the header.\\n\"\n";
+ if (!translator.languageCode().isEmpty())
+ out << "\"X-Language: " << translator.languageCode() << "\\n\"\n";
+ if (!translator.sourceLanguageCode().isEmpty())
+ out << "\"X-Source-Language: " << translator.sourceLanguageCode() << "\\n\"\n";
+ first = false;
+ }
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ if (!first)
+ out << endl;
+
+ if (!msg.translatorComment().isEmpty())
+ out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment());
+
+ if (!msg.extraComment().isEmpty())
+ out << poEscapedLines(QLatin1String("#."), true, msg.extraComment());
+
+ if (!msg.context().isEmpty())
+ out << QLatin1String("#. ts-context ") << msg.context() << '\n';
+ if (!msg.id().isEmpty())
+ out << QLatin1String("#. ts-id ") << msg.id() << '\n';
+
+ if (!msg.fileName().isEmpty() && msg.fileName() != QLatin1String(MAGIC_OBSOLETE_REFERENCE)) {
+ QStringList refs;
+ foreach (const TranslatorMessage::Reference &ref, msg.allReferences())
+ refs.append(QString(QLatin1String("%2:%1"))
+ .arg(ref.lineNumber()).arg(ref.fileName()));
+ out << poWrappedEscapedLines(QLatin1String("#:"), true, refs.join(QLatin1String(" ")));
+ }
+
+ bool noWrap = false;
+ QStringList flags;
+ if (msg.type() == TranslatorMessage::Unfinished)
+ flags.append(QLatin1String("fuzzy"));
+ TranslatorMessage::ExtraData::const_iterator itr =
+ msg.extras().find(QLatin1String("po-flags"));
+ if (itr != msg.extras().end()) {
+ if (itr->split(QLatin1String(", ")).contains(QLatin1String("no-wrap")))
+ noWrap = true;
+ flags.append(*itr);
+ }
+ if (!flags.isEmpty())
+ out << "#, " << flags.join(QLatin1String(", ")) << '\n';
+
+ QString prefix = QLatin1String("#| ");
+ if (!msg.oldComment().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.oldComment());
+ if (!msg.oldSourceText().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.oldSourceText());
+ QString plural = msg.extra(QLatin1String("po-old_msgid_plural"));
+ if (!plural.isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
+ prefix = QLatin1String((msg.type() == TranslatorMessage::Obsolete) ? "#~ " : "");
+ if (!msg.comment().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.comment());
+ out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText());
+ if (!msg.isPlural()) {
+ QString transl = msg.translation();
+ if (first) {
+ transl.remove(QRegExp(QLatin1String("\\bX-Language:[^\n]*\n")));
+ if (!translator.languageCode().isEmpty())
+ transl += QLatin1String("X-Language: ") + translator.languageCode() + QLatin1Char('\n');
+ }
+ out << poEscapedString(prefix, QLatin1String("msgstr"), noWrap, transl);
+ } else {
+ QString plural = msg.extra(QLatin1String("po-msgid_plural"));
+ if (plural.isEmpty())
+ plural = msg.sourceText();
+ out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
+ QStringList translations = translator.normalizedTranslations(msg, cd, &ok);
+ for (int i = 0; i != translations.size(); ++i) {
+ out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap,
+ translations.at(i));
+ }
+ }
+ first = false;
+ }
+ return ok;
+}
+
+int initPO()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("po");
+ format.description = QObject::tr("GNU Gettext localization files");
+ format.loader = &loadPO;
+ format.saver = &savePO;
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 1;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initPO)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp
new file mode 100644
index 0000000000..1e91f92ced
--- /dev/null
+++ b/tools/linguist/shared/profileevaluator.cpp
@@ -0,0 +1,1785 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "profileevaluator.h"
+#include "proparserutils.h"
+#include "proitems.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QList>
+#include <QtCore/QRegExp>
+#include <QtCore/QSet>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#else
+#define QT_POPEN popen
+#endif
+
+QT_BEGIN_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileEvaluator::Private
+//
+///////////////////////////////////////////////////////////////////////
+
+class ProFileEvaluator::Private : public AbstractProItemVisitor
+{
+public:
+ Private(ProFileEvaluator *q_);
+
+ bool read(ProFile *pro);
+
+ ProBlock *currentBlock();
+ void updateItem();
+ bool parseLine(const QString &line);
+ void insertVariable(const QString &line, int *i);
+ void insertOperator(const char op);
+ void insertComment(const QString &comment);
+ void enterScope(bool multiLine);
+ void leaveScope();
+ void finalizeBlock();
+
+ // 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);
+
+ QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
+ QStringList values(const QString &variableName) const;
+ QStringList values(const QString &variableName, const ProFile *pro) const;
+ QString propertyValue(const QString &val) const;
+
+ bool isActiveConfig(const QString &config, bool regex = false);
+ QStringList expandPattern(const QString &pattern);
+ void expandPatternHelper(const QString &relName, const QString &absName,
+ QStringList &sources_out);
+ QStringList expandVariableReferences(const QString &value);
+ QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
+ QString format(const char *format) const;
+
+ QString currentFileName() const;
+ QString getcwd() 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);
+
+ QStringList qmakeFeaturePaths();
+
+ ProFileEvaluator *q;
+
+ QStack<ProBlock *> m_blockstack;
+ ProBlock *m_block;
+
+ ProItem *m_commentItem;
+ QString m_proitem;
+ QString m_pendingComment;
+ bool m_syntaxError;
+ bool m_contNextLine;
+ bool m_condition;
+ bool m_invertNext;
+ QString m_lastVarName;
+ ProVariable::VariableOperator m_variableOperator;
+ int m_lineNo; // Error reporting
+ 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'
+
+ QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii.
+ QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
+ QHash<QString, QString> m_properties;
+ QString m_origfile;
+
+ int m_prevLineNo; // Checking whether we're assigning the same TARGET
+ ProFile *m_prevProFile; // See m_prevLineNo
+
+ bool m_verbose;
+};
+
+ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
+ : q(q_)
+{
+ m_prevLineNo = 0;
+ m_prevProFile = 0;
+ m_verbose = true;
+ m_block = 0;
+ m_commentItem = 0;
+ m_syntaxError = 0;
+ m_lineNo = 0;
+ m_contNextLine = false;
+}
+
+bool ProFileEvaluator::Private::read(ProFile *pro)
+{
+ QFile file(pro->fileName());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
+ return false;
+ }
+
+ m_syntaxError = false;
+ m_lineNo = 1;
+ m_blockstack.push(pro);
+
+ QTextStream ts(&file);
+ while (!ts.atEnd()) {
+ QString line = ts.readLine();
+ if (!parseLine(line)) {
+ q->errorMessage(format(".pro parse failure."));
+ return false;
+ }
+ ++m_lineNo;
+ }
+ return true;
+}
+
+bool ProFileEvaluator::Private::parseLine(const QString &line0)
+{
+ if (m_blockstack.isEmpty())
+ return false;
+
+ ushort quote = 0;
+ int parens = 0;
+ bool contNextLine = false;
+ QString line = line0.simplified();
+
+ for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
+ ushort c = line.at(i).unicode();
+ if (quote && c == quote)
+ quote = 0;
+ else if (c == '(')
+ ++parens;
+ else if (c == ')')
+ --parens;
+ else if (c == '"' && (i == 0 || line.at(i - 1).unicode() != '\\'))
+ quote = c;
+ else if (!parens && !quote) {
+ if (c == '#') {
+ insertComment(line.mid(i + 1));
+ contNextLine = m_contNextLine;
+ break;
+ }
+ if (c == '\\' && i >= line.count() - 1) {
+ updateItem();
+ contNextLine = true;
+ continue;
+ }
+ if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
+ if (c == ' ')
+ updateItem();
+ else
+ m_proitem += c;
+ continue;
+ }
+ if (c == ':') {
+ enterScope(false);
+ continue;
+ }
+ if (c == '{') {
+ enterScope(true);
+ continue;
+ }
+ if (c == '}') {
+ leaveScope();
+ continue;
+ }
+ if (c == '=') {
+ insertVariable(line, &i);
+ continue;
+ }
+ if (c == '|' || c == '!') {
+ insertOperator(c);
+ continue;
+ }
+ }
+
+ m_proitem += c;
+ }
+ m_contNextLine = contNextLine;
+
+ if (!m_syntaxError) {
+ updateItem();
+ if (!m_contNextLine)
+ finalizeBlock();
+ }
+ return !m_syntaxError;
+}
+
+void ProFileEvaluator::Private::finalizeBlock()
+{
+ if (m_blockstack.isEmpty()) {
+ m_syntaxError = true;
+ } else {
+ if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
+ leaveScope();
+ m_block = 0;
+ m_commentItem = 0;
+ }
+}
+
+void ProFileEvaluator::Private::insertVariable(const QString &line, int *i)
+{
+ ProVariable::VariableOperator opkind;
+
+ switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
+ case '+':
+ m_proitem.chop(1);
+ opkind = ProVariable::AddOperator;
+ break;
+ case '-':
+ m_proitem.chop(1);
+ opkind = ProVariable::RemoveOperator;
+ break;
+ case '*':
+ m_proitem.chop(1);
+ opkind = ProVariable::UniqueAddOperator;
+ break;
+ case '~':
+ m_proitem.chop(1);
+ opkind = ProVariable::ReplaceOperator;
+ break;
+ default:
+ opkind = ProVariable::SetOperator;
+ }
+
+ ProBlock *block = m_blockstack.top();
+ m_proitem = m_proitem.trimmed();
+ ProVariable *variable = new ProVariable(m_proitem, block);
+ variable->setLineNumber(m_lineNo);
+ variable->setVariableOperator(opkind);
+ block->appendItem(variable);
+ m_block = variable;
+
+ if (!m_pendingComment.isEmpty()) {
+ m_block->setComment(m_pendingComment);
+ m_pendingComment.clear();
+ }
+ m_commentItem = variable;
+
+ m_proitem.clear();
+
+ if (opkind == ProVariable::ReplaceOperator) {
+ // skip util end of line or comment
+ while (1) {
+ ++(*i);
+
+ // end of line?
+ if (*i >= line.count())
+ break;
+
+ // comment?
+ if (line.at(*i).unicode() == '#') {
+ --(*i);
+ break;
+ }
+
+ m_proitem += line.at(*i);
+ }
+ m_proitem = m_proitem.trimmed();
+ }
+}
+
+void ProFileEvaluator::Private::insertOperator(const char op)
+{
+ updateItem();
+
+ ProOperator::OperatorKind opkind;
+ switch(op) {
+ case '!':
+ opkind = ProOperator::NotOperator;
+ break;
+ case '|':
+ opkind = ProOperator::OrOperator;
+ break;
+ default:
+ opkind = ProOperator::OrOperator;
+ }
+
+ ProBlock * const block = currentBlock();
+ ProOperator * const proOp = new ProOperator(opkind);
+ proOp->setLineNumber(m_lineNo);
+ block->appendItem(proOp);
+ m_commentItem = proOp;
+}
+
+void ProFileEvaluator::Private::insertComment(const QString &comment)
+{
+ updateItem();
+
+ QString strComment;
+ if (!m_commentItem)
+ strComment = m_pendingComment;
+ else
+ strComment = m_commentItem->comment();
+
+ if (strComment.isEmpty())
+ strComment = comment;
+ else {
+ strComment += QLatin1Char('\n');
+ strComment += comment.trimmed();
+ }
+
+ strComment = strComment.trimmed();
+
+ if (!m_commentItem)
+ m_pendingComment = strComment;
+ else
+ m_commentItem->setComment(strComment);
+}
+
+void ProFileEvaluator::Private::enterScope(bool multiLine)
+{
+ updateItem();
+
+ ProBlock *parent = currentBlock();
+ ProBlock *block = new ProBlock(parent);
+ block->setLineNumber(m_lineNo);
+ parent->setBlockKind(ProBlock::ScopeKind);
+
+ parent->appendItem(block);
+
+ if (multiLine)
+ block->setBlockKind(ProBlock::ScopeContentsKind);
+ else
+ block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);
+
+ m_blockstack.push(block);
+ m_block = 0;
+}
+
+void ProFileEvaluator::Private::leaveScope()
+{
+ updateItem();
+ m_blockstack.pop();
+ finalizeBlock();
+}
+
+ProBlock *ProFileEvaluator::Private::currentBlock()
+{
+ if (m_block)
+ return m_block;
+
+ ProBlock *parent = m_blockstack.top();
+ m_block = new ProBlock(parent);
+ m_block->setLineNumber(m_lineNo);
+ parent->appendItem(m_block);
+
+ if (!m_pendingComment.isEmpty()) {
+ m_block->setComment(m_pendingComment);
+ m_pendingComment.clear();
+ }
+
+ m_commentItem = m_block;
+
+ return m_block;
+}
+
+void ProFileEvaluator::Private::updateItem()
+{
+ m_proitem = m_proitem.trimmed();
+ if (m_proitem.isEmpty())
+ return;
+
+ ProBlock *block = currentBlock();
+ if (block->blockKind() & ProBlock::VariableKind) {
+ m_commentItem = new ProValue(m_proitem, static_cast<ProVariable*>(block));
+ } else if (m_proitem.endsWith(QLatin1Char(')'))) {
+ m_commentItem = new ProFunction(m_proitem);
+ } else {
+ m_commentItem = new ProCondition(m_proitem);
+ }
+ m_commentItem->setLineNumber(m_lineNo);
+ block->appendItem(m_commentItem);
+
+ m_proitem.clear();
+}
+
+
+bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
+{
+ if (block->blockKind() == ProBlock::ScopeKind) {
+ m_invertNext = false;
+ m_condition = false;
+ }
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
+{
+ Q_UNUSED(block);
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
+{
+ m_lastVarName = variable->variable();
+ m_variableOperator = variable->variableOperator();
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
+{
+ Q_UNUSED(variable);
+ m_lastVarName.clear();
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
+{
+ m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
+{
+ if (!m_condition) {
+ if (m_invertNext)
+ m_condition |= !isActiveConfig(cond->text(), true);
+ else
+ m_condition |= isActiveConfig(cond->text(), true);
+ }
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
+{
+ PRE(pro);
+ bool ok = true;
+ m_lineNo = pro->lineNumber();
+ if (m_oldPath.isEmpty()) {
+ // change the working directory for the initial profile we visit, since
+ // that is *the* profile. All the other times we reach this function will be due to
+ // include(file) or load(file)
+ m_oldPath = QDir::currentPath();
+ m_profileStack.push(pro);
+ ok = QDir::setCurrent(pro->directoryName());
+ }
+
+ if (m_origfile.isEmpty())
+ m_origfile = pro->fileName();
+
+ return ok;
+}
+
+bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
+{
+ PRE(pro);
+ bool ok = true;
+ m_lineNo = pro->lineNumber();
+ if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
+ m_profileStack.pop();
+ ok = QDir::setCurrent(m_oldPath);
+ }
+ return ok;
+}
+
+bool ProFileEvaluator::Private::visitProValue(ProValue *value)
+{
+ PRE(value);
+ m_lineNo = value->lineNumber();
+ QString val = value->value();
+
+ QString varName = m_lastVarName;
+
+ QStringList v = expandVariableReferences(val);
+
+ // Since qmake combines different values for the TARGET variable, we join
+ // TARGET values that are on the same line. We can't do this later with all
+ // values because this parser isn't scope-aware, so we'd risk joining
+ // scope-specific targets together.
+ if (varName == QLatin1String("TARGET")
+ && m_lineNo == m_prevLineNo
+ && currentProFile() == m_prevProFile) {
+ QStringList targets = m_valuemap.value(QLatin1String("TARGET"));
+ m_valuemap.remove(QLatin1String("TARGET"));
+ QStringList lastTarget(targets.takeLast());
+ lastTarget << v.join(QLatin1String(" "));
+ targets.push_back(lastTarget.join(QLatin1String(" ")));
+ v = targets;
+ }
+ m_prevLineNo = m_lineNo;
+ m_prevProFile = currentProFile();
+
+ // The following two blocks fix bug 180128 by making all "interesting"
+ // file name absolute in each .pro file, not just the top most one
+ if (varName == QLatin1String("SOURCES")
+ || varName == QLatin1String("HEADERS")
+ || varName == QLatin1String("INTERFACES")
+ || varName == QLatin1String("FORMS")
+ || varName == QLatin1String("FORMS3")
+ || varName == QLatin1String("RESOURCES")) {
+ // matches only existent files, expand certain(?) patterns
+ QStringList vv;
+ for (int i = v.count(); --i >= 0; )
+ vv << expandPattern(v[i]);
+ v = vv;
+ }
+
+ if (varName == QLatin1String("TRANSLATIONS")) {
+ // also matches non-existent files, but does not expand pattern
+ QString dir = QFileInfo(currentFileName()).absolutePath();
+ dir += QLatin1Char('/');
+ for (int i = v.count(); --i >= 0; )
+ v[i] = QFileInfo(dir, v[i]).absoluteFilePath();
+ }
+
+ switch (m_variableOperator) {
+ case ProVariable::UniqueAddOperator: // *
+ insertUnique(&m_valuemap, varName, v, true);
+ insertUnique(&m_filevaluemap[currentProFile()], varName, v, true);
+ break;
+ case ProVariable::SetOperator: // =
+ case ProVariable::AddOperator: // +
+ insertUnique(&m_valuemap, varName, v, false);
+ insertUnique(&m_filevaluemap[currentProFile()], varName, v, false);
+ break;
+ case ProVariable::RemoveOperator: // -
+ // fix me: interaction between AddOperator and RemoveOperator
+ insertUnique(&m_valuemap, varName.prepend(QLatin1Char('-')), v, false);
+ insertUnique(&m_filevaluemap[currentProFile()],
+ varName.prepend(QLatin1Char('-')), v, false);
+ break;
+ case ProVariable::ReplaceOperator: // ~
+ {
+ // DEFINES ~= s/a/b/?[gqi]
+
+/* Create a superset by executing replacement + adding items that have changed
+ to original list. We're not sure if this is really the right approach, so for
+ the time being we will just do nothing ...
+
+ QChar sep = val.at(1);
+ QStringList func = val.split(sep);
+ if (func.count() < 3 || func.count() > 4) {
+ q->logMessage(format("'~= operator '(function s///) expects 3 or 4 arguments."));
+ return false;
+ }
+ if (func[0] != QLatin1String("s")) {
+ q->logMessage(format("~= operator can only handle s/// function."));
+ return false;
+ }
+
+ bool global = false, quote = false, case_sense = false;
+ if (func.count() == 4) {
+ global = func[3].indexOf(QLatin1Char('g')) != -1;
+ case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
+ quote = func[3].indexOf(QLatin1Char('q')) != -1;
+ }
+ QString pattern = func[1];
+ QString replace = func[2];
+ if (quote)
+ pattern = QRegExp::escape(pattern);
+
+ QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+ QStringList replaceList = replaceInList(m_valuemap.value(varName), regexp, replace,
+ global);
+ // Add changed entries to list
+ foreach (const QString &entry, replaceList)
+ if (!m_valuemap.value(varName).contains(entry))
+ insertUnique(&m_valuemap, varName, QStringList() << entry, false);
+
+ replaceList = replaceInList(m_filevaluemap[currentProFile()].value(varName), regexp,
+ replace, global);
+ foreach (const QString &entry, replaceList)
+ if (!m_filevaluemap[currentProFile()].value(varName).contains(entry))
+ insertUnique(&m_filevaluemap[currentProFile()], varName,
+ QStringList() << entry, false); */
+ }
+ break;
+
+ }
+ return true;
+}
+
+bool ProFileEvaluator::Private::visitProFunction(ProFunction *func)
+{
+ m_lineNo = func->lineNumber();
+ bool result = true;
+ bool ok = true;
+ QString text = func->text();
+ int lparen = text.indexOf(QLatin1Char('('));
+ int rparen = text.lastIndexOf(QLatin1Char(')'));
+ Q_ASSERT(lparen < rparen);
+
+ QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
+ QString funcName = text.left(lparen);
+ ok &= evaluateConditionalFunction(funcName.trimmed(), arguments, &result);
+ return ok;
+}
+
+
+QStringList ProFileEvaluator::Private::qmakeFeaturePaths()
+{
+ QStringList concat;
+ {
+ const QString base_concat = QDir::separator() + QString(QLatin1String("features"));
+ concat << base_concat + QDir::separator() + QLatin1String("mac");
+ concat << base_concat + QDir::separator() + QLatin1String("macx");
+ concat << base_concat + QDir::separator() + QLatin1String("unix");
+ concat << base_concat + QDir::separator() + QLatin1String("win32");
+ concat << base_concat + QDir::separator() + QLatin1String("mac9");
+ concat << base_concat + QDir::separator() + QLatin1String("qnx6");
+ concat << base_concat;
+ }
+ const QString mkspecs_concat = QDir::separator() + QString(QLatin1String("mkspecs"));
+ QStringList feature_roots;
+ QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
+ if (!mkspec_path.isNull())
+ feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
+ /*
+ if (prop)
+ feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
+ if (!Option::mkfile::cachefile.isEmpty()) {
+ QString path;
+ int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
+ if (last_slash != -1)
+ path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
+ foreach (const QString &concat_it, concat)
+ feature_roots << (path + concat_it);
+ }
+ */
+
+ QByteArray qmakepath = qgetenv("QMAKEPATH");
+ if (!qmakepath.isNull()) {
+ const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
+ foreach (const QString &item, lst) {
+ foreach (const QString &concat_it, concat)
+ feature_roots << (item + mkspecs_concat + concat_it);
+ }
+ }
+ //if (!Option::mkfile::qmakespec.isEmpty())
+ // feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
+ //if (!Option::mkfile::qmakespec.isEmpty()) {
+ // QFileInfo specfi(Option::mkfile::qmakespec);
+ // QDir specdir(specfi.absoluteFilePath());
+ // while (!specdir.isRoot()) {
+ // if (!specdir.cdUp() || specdir.isRoot())
+ // break;
+ // if (QFile::exists(specdir.path() + QDir::separator() + "features")) {
+ // foreach (const QString &concat_it, concat)
+ // feature_roots << (specdir.path() + concat_it);
+ // break;
+ // }
+ // }
+ //}
+ foreach (const QString &concat_it, concat)
+ feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
+ mkspecs_concat + concat_it);
+ foreach (const QString &concat_it, concat)
+ feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
+ mkspecs_concat + concat_it);
+ return feature_roots;
+}
+
+QString ProFileEvaluator::Private::propertyValue(const QString &name) const
+{
+ if (m_properties.contains(name))
+ return m_properties.value(name);
+ if (name == QLatin1String("QT_INSTALL_PREFIX"))
+ return QLibraryInfo::location(QLibraryInfo::PrefixPath);
+ if (name == QLatin1String("QT_INSTALL_DATA"))
+ return QLibraryInfo::location(QLibraryInfo::DataPath);
+ if (name == QLatin1String("QT_INSTALL_DOCS"))
+ return QLibraryInfo::location(QLibraryInfo::DocumentationPath);
+ if (name == QLatin1String("QT_INSTALL_HEADERS"))
+ return QLibraryInfo::location(QLibraryInfo::HeadersPath);
+ if (name == QLatin1String("QT_INSTALL_LIBS"))
+ return QLibraryInfo::location(QLibraryInfo::LibrariesPath);
+ if (name == QLatin1String("QT_INSTALL_BINS"))
+ return QLibraryInfo::location(QLibraryInfo::BinariesPath);
+ if (name == QLatin1String("QT_INSTALL_PLUGINS"))
+ return QLibraryInfo::location(QLibraryInfo::PluginsPath);
+ if (name == QLatin1String("QT_INSTALL_TRANSLATIONS"))
+ return QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ if (name == QLatin1String("QT_INSTALL_CONFIGURATION"))
+ return QLibraryInfo::location(QLibraryInfo::SettingsPath);
+ if (name == QLatin1String("QT_INSTALL_EXAMPLES"))
+ return QLibraryInfo::location(QLibraryInfo::ExamplesPath);
+ if (name == QLatin1String("QT_INSTALL_DEMOS"))
+ return QLibraryInfo::location(QLibraryInfo::DemosPath);
+ if (name == QLatin1String("QMAKE_MKSPECS"))
+ return qmake_mkspec_paths().join(Option::dirlist_sep);
+ if (name == QLatin1String("QMAKE_VERSION"))
+ return QLatin1String("1.0"); //### FIXME
+ //return qmake_version();
+#ifdef QT_VERSION_STR
+ if (name == QLatin1String("QT_VERSION"))
+ return QLatin1String(QT_VERSION_STR);
+#endif
+ return QLatin1String("UNKNOWN"); //###
+}
+
+ProFile *ProFileEvaluator::Private::currentProFile() const
+{
+ if (m_profileStack.count() > 0)
+ return m_profileStack.top();
+ return 0;
+}
+
+QString ProFileEvaluator::Private::currentFileName() const
+{
+ ProFile *pro = currentProFile();
+ if (pro)
+ return pro->fileName();
+ return QString();
+}
+
+QString ProFileEvaluator::Private::getcwd() const
+{
+ ProFile *cur = m_profileStack.top();
+ return cur->directoryName();
+}
+
+QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
+{
+ QStringList ret;
+// if (ok)
+// *ok = true;
+ if (str.isEmpty())
+ return ret;
+
+ const ushort LSQUARE = '[';
+ const ushort RSQUARE = ']';
+ const ushort LCURLY = '{';
+ const ushort RCURLY = '}';
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort DOLLAR = '$';
+ const ushort BACKSLASH = '\\';
+ const ushort UNDERSCORE = '_';
+ const ushort DOT = '.';
+ const ushort SPACE = ' ';
+ const ushort TAB = '\t';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+
+ ushort unicode, quote = 0;
+ const QChar *str_data = str.data();
+ const int str_len = str.length();
+
+ ushort term;
+ QString var, args;
+
+ int replaced = 0;
+ QString current;
+ for (int i = 0; i < str_len; ++i) {
+ unicode = str_data[i].unicode();
+ const int start_var = i;
+ if (unicode == DOLLAR && str_len > i+2) {
+ unicode = str_data[++i].unicode();
+ if (unicode == DOLLAR) {
+ term = 0;
+ var.clear();
+ args.clear();
+ enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
+ unicode = str_data[++i].unicode();
+ if (unicode == LSQUARE) {
+ unicode = str_data[++i].unicode();
+ term = RSQUARE;
+ var_type = PROPERTY;
+ } else if (unicode == LCURLY) {
+ unicode = str_data[++i].unicode();
+ var_type = VAR;
+ term = RCURLY;
+ } else if (unicode == LPAREN) {
+ unicode = str_data[++i].unicode();
+ var_type = ENVIRON;
+ term = RPAREN;
+ }
+ forever {
+ if (!(unicode & (0xFF<<8)) &&
+ unicode != DOT && unicode != UNDERSCORE &&
+ //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
+ (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
+ (unicode < '0' || unicode > '9'))
+ break;
+ var.append(QChar(unicode));
+ if (++i == str_len)
+ break;
+ unicode = str_data[i].unicode();
+ // at this point, i points to either the 'term' or 'next' character (which is in unicode)
+ }
+ if (var_type == VAR && unicode == LPAREN) {
+ var_type = FUNCTION;
+ int depth = 0;
+ forever {
+ if (++i == str_len)
+ break;
+ unicode = str_data[i].unicode();
+ if (unicode == LPAREN) {
+ depth++;
+ } else if (unicode == RPAREN) {
+ if (!depth)
+ break;
+ --depth;
+ }
+ args.append(QChar(unicode));
+ }
+ if (++i < str_len)
+ unicode = str_data[i].unicode();
+ else
+ unicode = 0;
+ // at this point i is pointing to the 'next' character (which is in unicode)
+ // this might actually be a term character since you can do $${func()}
+ }
+ if (term) {
+ if (unicode != term) {
+ q->logMessage(format("Missing %1 terminator [found %2]")
+ .arg(QChar(term))
+ .arg(unicode ? QString(unicode) : QString::fromLatin1(("end-of-line"))));
+// if (ok)
+// *ok = false;
+ return QStringList();
+ }
+ } else {
+ // move the 'cursor' back to the last char of the thing we were looking at
+ --i;
+ }
+ // since i never points to the 'next' character, there is no reason for this to be set
+ unicode = 0;
+
+ QStringList replacement;
+ if (var_type == ENVIRON) {
+ replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
+ } else if (var_type == PROPERTY) {
+ replacement << propertyValue(var);
+ } else if (var_type == FUNCTION) {
+ replacement << evaluateExpandFunction(var, args);
+ } else if (var_type == VAR) {
+ replacement = values(var);
+ }
+ if (!(replaced++) && start_var)
+ current = str.left(start_var);
+ if (!replacement.isEmpty()) {
+ if (quote) {
+ current += replacement.join(QString(Option::field_sep));
+ } else {
+ current += replacement.takeFirst();
+ if (!replacement.isEmpty()) {
+ if (!current.isEmpty())
+ ret.append(current);
+ current = replacement.takeLast();
+ if (!replacement.isEmpty())
+ ret += replacement;
+ }
+ }
+ }
+ } else {
+ if (replaced)
+ current.append(QLatin1Char('$'));
+ }
+ }
+ if (quote && unicode == quote) {
+ unicode = 0;
+ quote = 0;
+ } else if (unicode == BACKSLASH) {
+ bool escape = false;
+ const char *symbols = "[]{}()$\\'\"";
+ for (const char *s = symbols; *s; ++s) {
+ if (str_data[i+1].unicode() == (ushort)*s) {
+ i++;
+ escape = true;
+ if (!(replaced++))
+ current = str.left(start_var);
+ current.append(str.at(i));
+ break;
+ }
+ }
+ if (escape || !replaced)
+ unicode =0;
+ } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
+ quote = unicode;
+ unicode = 0;
+ if (!(replaced++) && i)
+ current = str.left(i);
+ } else if (!quote && (unicode == SPACE || unicode == TAB)) {
+ unicode = 0;
+ if (!current.isEmpty()) {
+ ret.append(current);
+ current.clear();
+ }
+ }
+ if (replaced && unicode)
+ current.append(QChar(unicode));
+ }
+ if (!replaced)
+ ret = QStringList(str);
+ else if (!current.isEmpty())
+ ret.append(current);
+ return ret;
+}
+
+bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
+{
+ // magic types for easy flipping
+ if (config == QLatin1String("true"))
+ return true;
+ if (config == QLatin1String("false"))
+ return false;
+
+ // mkspecs
+ if ((Option::target_mode == Option::TARG_MACX_MODE
+ || Option::target_mode == Option::TARG_QNX6_MODE
+ || Option::target_mode == Option::TARG_UNIX_MODE)
+ && config == QLatin1String("unix"))
+ return true;
+ if (Option::target_mode == Option::TARG_MACX_MODE && config == QLatin1String("macx"))
+ return true;
+ if (Option::target_mode == Option::TARG_QNX6_MODE && config == QLatin1String("qnx6"))
+ return true;
+ if (Option::target_mode == Option::TARG_MAC9_MODE && config == QLatin1String("mac9"))
+ return true;
+ if ((Option::target_mode == Option::TARG_MAC9_MODE
+ || Option::target_mode == Option::TARG_MACX_MODE)
+ && config == QLatin1String("mac"))
+ return true;
+ if (Option::target_mode == Option::TARG_WIN_MODE && config == QLatin1String("win32"))
+ return true;
+
+ QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
+ QString spec = Option::qmakespec;
+ if ((regex && re.exactMatch(spec)) || (!regex && spec == config))
+ return true;
+
+ return false;
+}
+
+QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
+{
+ QStringList argumentsList = split_arg_list(arguments);
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i)
+ args += expandVariableReferences(argumentsList[i]);
+
+ enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
+ E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+ E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
+ 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); //v (implemented)
+ expands->insert(QLatin1String("first"), E_FIRST); //v
+ expands->insert(QLatin1String("last"), E_LAST); //v
+ expands->insert(QLatin1String("cat"), E_CAT);
+ expands->insert(QLatin1String("fromfile"), E_FROMFILE);
+ expands->insert(QLatin1String("eval"), E_EVAL);
+ expands->insert(QLatin1String("list"), E_LIST);
+ expands->insert(QLatin1String("sprintf"), E_SPRINTF);
+ expands->insert(QLatin1String("join"), E_JOIN); //v
+ expands->insert(QLatin1String("split"), E_SPLIT); //v
+ expands->insert(QLatin1String("basename"), E_BASENAME); //v
+ expands->insert(QLatin1String("dirname"), E_DIRNAME); //v
+ expands->insert(QLatin1String("section"), E_SECTION);
+ expands->insert(QLatin1String("find"), E_FIND);
+ expands->insert(QLatin1String("system"), E_SYSTEM); //v
+ expands->insert(QLatin1String("unique"), E_UNIQUE);
+ expands->insert(QLatin1String("quote"), E_QUOTE); //v
+ 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);
+ expands->insert(QLatin1String("replace"), E_REPLACE);
+ }
+ ExpandFunc func_t = ExpandFunc(expands->value(func.toLower()));
+
+ QStringList ret;
+
+ switch (func_t) {
+ case E_BASENAME:
+ case E_DIRNAME:
+ case E_SECTION: {
+ bool regexp = false;
+ QString sep, var;
+ int beg = 0;
+ int end = -1;
+ if (func_t == E_SECTION) {
+ if (args.count() != 3 && args.count() != 4) {
+ q->logMessage(format("%1(var) section(var, sep, begin, end) "
+ "requires three or four arguments.").arg(func));
+ } else {
+ var = args[0];
+ sep = args[1];
+ beg = args[2].toInt();
+ if (args.count() == 4)
+ end = args[3].toInt();
+ }
+ } else {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(var) requires one argument.").arg(func));
+ } else {
+ var = args[0];
+ regexp = true;
+ sep = QLatin1String("[\\\\/]");
+ if (func_t == E_DIRNAME)
+ end = -2;
+ else
+ beg = -1;
+ }
+ }
+ if (!var.isNull()) {
+ foreach (const QString str, values(var)) {
+ if (regexp)
+ ret += str.section(QRegExp(sep), beg, end);
+ else
+ ret += str.section(sep, beg, end);
+ }
+ }
+ break;
+ }
+ case E_JOIN: {
+ if (args.count() < 1 || args.count() > 4) {
+ q->logMessage(format("join(var, glue, before, after) requires one to four arguments."));
+ } else {
+ QString glue, before, after;
+ if (args.count() >= 2)
+ glue = args[1];
+ if (args.count() >= 3)
+ before = args[2];
+ if (args.count() == 4)
+ after = args[3];
+ const QStringList &var = values(args.first());
+ if (!var.isEmpty())
+ ret.append(before + var.join(glue) + after);
+ }
+ break;
+ }
+ case E_SPLIT: {
+ if (args.count() != 2) {
+ q->logMessage(format("split(var, sep) requires two arguments"));
+ } else {
+ QString sep = args.at(1);
+ foreach (const QString &var, values(args.first()))
+ foreach (const QString &splt, var.split(sep))
+ ret.append(splt);
+ }
+ break;
+ }
+ case E_MEMBER: {
+ if (args.count() < 1 || args.count() > 3) {
+ q->logMessage(format("member(var, start, end) requires one to three arguments."));
+ } else {
+ bool ok = true;
+ const QStringList var = values(args.first());
+ int start = 0, end = 0;
+ if (args.count() >= 2) {
+ QString start_str = args[1];
+ start = start_str.toInt(&ok);
+ if (!ok) {
+ if (args.count() == 2) {
+ int dotdot = start_str.indexOf(QLatin1String(".."));
+ if (dotdot != -1) {
+ start = start_str.left(dotdot).toInt(&ok);
+ if (ok)
+ end = start_str.mid(dotdot+2).toInt(&ok);
+ }
+ }
+ if (!ok)
+ q->logMessage(format("member() argument 2 (start) '%2' invalid.")
+ .arg(start_str));
+ } else {
+ end = start;
+ if (args.count() == 3)
+ end = args[2].toInt(&ok);
+ if (!ok)
+ q->logMessage(format("member() argument 3 (end) '%2' invalid.\n")
+ .arg(args[2]));
+ }
+ }
+ if (ok) {
+ if (start < 0)
+ start += var.count();
+ if (end < 0)
+ end += var.count();
+ if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
+ //nothing
+ } else if (start < end) {
+ for (int i = start; i <= end && var.count() >= i; i++)
+ ret.append(var[i]);
+ } else {
+ for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
+ ret += var[i];
+ }
+ }
+ }
+ break;
+ }
+ case E_FIRST:
+ case E_LAST: {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(var) requires one argument.").arg(func));
+ } else {
+ const QStringList var = values(args.first());
+ if (!var.isEmpty()) {
+ if (func_t == E_FIRST)
+ ret.append(var[0]);
+ else
+ ret.append(var.last());
+ }
+ }
+ break;
+ }
+ case E_SYSTEM: {
+ if (m_condition) {
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("system(execute) requires one or two arguments."));
+ } else {
+ char buff[256];
+ FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
+ bool singleLine = true;
+ if (args.count() > 1)
+ singleLine = (args[1].toLower() == QLatin1String("true"));
+ QString output;
+ while (proc && !feof(proc)) {
+ int read_in = int(fread(buff, 1, 255, proc));
+ if (!read_in)
+ break;
+ for (int i = 0; i < read_in; i++) {
+ if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
+ buff[i] = ' ';
+ }
+ buff[read_in] = '\0';
+ output += QLatin1String(buff);
+ }
+ ret += split_value_list(output);
+ }
+ }
+ break; }
+ case E_QUOTE:
+ for (int i = 0; i < args.count(); ++i)
+ ret += QStringList(args.at(i));
+ break;
+ case 0:
+ q->logMessage(format("'%1' is not a function").arg(func));
+ break;
+ default:
+ q->logMessage(format("Function '%1' is not implemented").arg(func));
+ break;
+ }
+
+ return ret;
+}
+
+bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &function,
+ const QString &arguments, bool *result)
+{
+ QStringList argumentsList = split_arg_list(arguments);
+ QString sep;
+ sep.append(Option::field_sep);
+
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i)
+ args += expandVariableReferences(argumentsList[i]).join(sep);
+
+ enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE,
+ CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE};
+
+ static QHash<QString, int> *functions = 0;
+ if (!functions) {
+ functions = new QHash<QString, int>;
+ functions->insert(QLatin1String("load"), CF_LOAD); //v
+ functions->insert(QLatin1String("include"), CF_INCLUDE); //v
+ functions->insert(QLatin1String("message"), CF_MESSAGE); //v
+ functions->insert(QLatin1String("warning"), CF_MESSAGE); //v
+ functions->insert(QLatin1String("error"), CF_MESSAGE); //v
+ }
+
+ bool cond = false;
+ bool ok = true;
+
+ ConditionFunc func_t = (ConditionFunc)functions->value(function);
+
+ switch (func_t) {
+ case CF_CONFIG: {
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("CONFIG(config) requires one or two arguments."));
+ ok = false;
+ break;
+ }
+ if (args.count() == 1) {
+ //cond = isActiveConfig(args.first()); XXX
+ break;
+ }
+ const QStringList mutuals = args[1].split(QLatin1Char('|'));
+ const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
+ for (int i = configs.size() - 1 && ok; i >= 0; i--) {
+ for (int mut = 0; mut < mutuals.count(); mut++) {
+ if (configs[i] == mutuals[mut].trimmed()) {
+ cond = (configs[i] == args[0]);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case CF_CONTAINS: {
+ if (args.count() < 2 || args.count() > 3) {
+ q->logMessage(format("contains(var, val) requires two or three arguments."));
+ ok = false;
+ break;
+ }
+
+ QRegExp regx(args[1]);
+ const QStringList &l = values(args.first());
+ if (args.count() == 2) {
+ for (int i = 0; i < l.size(); ++i) {
+ const QString val = l[i];
+ if (regx.exactMatch(val) || val == args[1]) {
+ cond = true;
+ break;
+ }
+ }
+ } else {
+ const QStringList mutuals = args[2].split(QLatin1Char('|'));
+ for (int i = l.size() - 1; i >= 0; i--) {
+ 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]);
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case CF_COUNT: {
+ if (args.count() != 2 && args.count() != 3) {
+ q->logMessage(format("count(var, count) requires two or three arguments."));
+ ok = false;
+ break;
+ }
+ if (args.count() == 3) {
+ QString comp = args[2];
+ if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
+ cond = values(args.first()).count() > args[1].toInt();
+ } else if (comp == QLatin1String(">=")) {
+ cond = values(args.first()).count() >= args[1].toInt();
+ } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
+ cond = 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();
+ } else {
+ ok = false;
+ q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
+ }
+ break;
+ }
+ cond = values(args.first()).count() == args[1].toInt();
+ break;
+ }
+ case CF_INCLUDE: {
+ 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;
+ }
+ QString fileName = args.first();
+ // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
+ QDir currentProPath(getcwd());
+ fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
+ ok = evaluateFile(fileName, &ok);
+ break;
+ }
+ case CF_LOAD: {
+ QString parseInto;
+ bool ignore_error = false;
+ if (args.count() == 2) {
+ QString sarg = args[1];
+ ignore_error = (sarg.toLower() == QLatin1String("true") || sarg.toInt());
+ } else if (args.count() != 1) {
+ q->logMessage(format("load(feature) requires one or two arguments."));
+ ok = false;
+ break;
+ }
+ ok = evaluateFeatureFile( args.first(), &cond);
+ break;
+ }
+ case CF_MESSAGE: {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(message) requires one argument.").arg(function));
+ ok = false;
+ break;
+ }
+ QString msg = 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;
+ }
+ case CF_SYSTEM: {
+ if (args.count() != 1) {
+ q->logMessage(format("system(exec) requires one argument."));
+ ok = false;
+ break;
+ }
+ ok = system(args.first().toLatin1().constData()) == 0;
+ break;
+ }
+ case CF_ISEMPTY: {
+ if (args.count() != 1) {
+ q->logMessage(format("isEmpty(var) requires one argument."));
+ ok = false;
+ break;
+ }
+ QStringList sl = values(args.first());
+ if (sl.count() == 0) {
+ cond = true;
+ } else if (sl.count() > 0) {
+ QString var = sl.first();
+ cond = (var.isEmpty());
+ }
+ break;
+ }
+ case CF_EXISTS: {
+ if (args.count() != 1) {
+ q->logMessage(format("exists(file) requires one argument."));
+ ok = false;
+ break;
+ }
+ QString file = args.first();
+
+ file = QDir::cleanPath(file);
+
+ if (QFile::exists(file)) {
+ cond = true;
+ break;
+ }
+ //regular expression I guess
+ QString dirstr = getcwd();
+ int slsh = file.lastIndexOf(Option::dir_sep);
+ if (slsh != -1) {
+ dirstr = file.left(slsh+1);
+ file = file.right(file.length() - slsh - 1);
+ }
+ cond = QDir(dirstr).entryList(QStringList(file)).count();
+
+ break;
+ }
+ }
+
+ if (result)
+ *result = cond;
+
+ return ok;
+}
+
+QStringList ProFileEvaluator::Private::values(const QString &variableName) const
+{
+ if (variableName == QLatin1String("TARGET")) {
+ QStringList list = m_valuemap.value(variableName);
+ if (!m_origfile.isEmpty())
+ list.append(QFileInfo(m_origfile).baseName());
+ return list;
+ }
+ if (variableName == QLatin1String("PWD")) {
+ return QStringList(getcwd());
+ }
+ return m_valuemap.value(variableName);
+}
+
+QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
+{
+ if (variableName == QLatin1String("TARGET")) {
+ QStringList list = m_filevaluemap[pro].value(variableName);
+ if (!m_origfile.isEmpty())
+ list.append(QFileInfo(m_origfile).baseName());
+ return list;
+ }
+ if (variableName == QLatin1String("PWD")) {
+ return QStringList(QFileInfo(pro->fileName()).absoluteFilePath());
+ }
+ return m_filevaluemap[pro].value(variableName);
+}
+
+ProFile *ProFileEvaluator::parsedProFile(const QString &fileName)
+{
+ QFileInfo fi(fileName);
+ if (fi.exists()) {
+ ProFile *pro = new ProFile(fi.absoluteFilePath());
+ if (d->read(pro))
+ return pro;
+ delete pro;
+ }
+ return 0;
+}
+
+void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
+{
+ delete proFile;
+}
+
+bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *result)
+{
+ bool ok = true;
+ ProFile *pro = q->parsedProFile(fileName);
+ if (pro) {
+ m_profileStack.push(pro);
+ ok = (currentProFile() ? pro->Accept(this) : false);
+ m_profileStack.pop();
+ q->releaseParsedProFile(pro);
+
+ if (result)
+ *result = true;
+ } else {
+ if (result)
+ *result = false;
+ }
+/* if (ok && readFeatures) {
+ QStringList configs = values("CONFIG");
+ QSet<QString> processed;
+ foreach (const QString &fn, configs) {
+ if (!processed.contains(fn)) {
+ processed.insert(fn);
+ evaluateFeatureFile(fn, 0);
+ }
+ }
+ } */
+
+ return ok;
+}
+
+bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, bool *result)
+{
+ QString fn;
+ foreach (const QString &path, qmakeFeaturePaths()) {
+ QString fname = path + QLatin1Char('/') + fileName;
+ if (QFileInfo(fname).exists()) {
+ fn = fname;
+ break;
+ }
+ fname += QLatin1String(".prf");
+ if (QFileInfo(fname).exists()) {
+ fn = fname;
+ break;
+ }
+ }
+ return fn.isEmpty() ? false : evaluateFile(fn, result);
+}
+
+void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName,
+ QStringList &sources_out)
+{
+ const QStringList vpaths = values(QLatin1String("VPATH"))
+ + values(QLatin1String("QMAKE_ABSOLUTE_SOURCE_PATH"))
+ + values(QLatin1String("DEPENDPATH"))
+ + values(QLatin1String("VPATH_SOURCES"));
+
+ QFileInfo fi(absName);
+ bool found = fi.exists();
+ // Search in all vpaths
+ if (!found) {
+ foreach (const QString &vpath, vpaths) {
+ fi.setFile(vpath + QDir::separator() + relName);
+ if (fi.exists()) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (found) {
+ sources_out += fi.absoluteFilePath(); // Not resolving symlinks
+ } else {
+ QString val = relName;
+ QString dir;
+ QString wildcard = val;
+ QString real_dir;
+ if (wildcard.lastIndexOf(QLatin1Char('/')) != -1) {
+ dir = wildcard.left(wildcard.lastIndexOf(QLatin1Char('/')) + 1);
+ real_dir = dir;
+ wildcard = wildcard.right(wildcard.length() - dir.length());
+ }
+
+ if (real_dir.isEmpty() || QFileInfo(real_dir).exists()) {
+ QStringList files = QDir(real_dir).entryList(QStringList(wildcard));
+ if (files.isEmpty()) {
+ q->logMessage(format("Failure to find %1").arg(val));
+ } else {
+ QString a;
+ for (int i = files.count() - 1; i >= 0; --i) {
+ if (files[i] == QLatin1String(".") || files[i] == QLatin1String(".."))
+ continue;
+ a = dir + files[i];
+ sources_out += a;
+ }
+ }
+ } else {
+ q->logMessage(format("Cannot match %1/%2, as %3 does not exist.")
+ .arg(real_dir).arg(wildcard).arg(real_dir));
+ }
+ }
+}
+
+
+/*
+ * Lookup of files are done in this order:
+ * 1. look in pwd
+ * 2. look in vpaths
+ * 3. expand wild card files relative from the profiles folder
+ **/
+
+// FIXME: This code supports something that I'd consider a flaw in .pro file syntax
+// which is not even documented. So arguably this can be ditched completely...
+QStringList ProFileEvaluator::Private::expandPattern(const QString& pattern)
+{
+ if (!currentProFile())
+ return QStringList();
+
+ QStringList sources_out;
+ const QString absName = QDir::cleanPath(QDir::current().absoluteFilePath(pattern));
+
+ expandPatternHelper(pattern, absName, sources_out);
+ return sources_out;
+}
+
+QString ProFileEvaluator::Private::format(const char *fmt) const
+{
+ ProFile *pro = currentProFile();
+ QString fileName = pro ? pro->fileName() : QLatin1String("Not a file");
+ int lineNumber = pro ? m_lineNo : 0;
+ return QString::fromLatin1("%1(%2):").arg(fileName).arg(lineNumber) + QString::fromAscii(fmt);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileEvaluator
+//
+///////////////////////////////////////////////////////////////////////
+
+ProFileEvaluator::ProFileEvaluator()
+ : d(new Private(this))
+{
+ Option::init();
+}
+
+ProFileEvaluator::~ProFileEvaluator()
+{
+ delete d;
+}
+
+bool ProFileEvaluator::contains(const QString &variableName) const
+{
+ return d->m_valuemap.contains(variableName);
+}
+
+QStringList ProFileEvaluator::values(const QString &variableName) const
+{
+ return d->values(variableName);
+}
+
+QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
+{
+ return d->values(variableName, pro);
+}
+
+ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
+{
+ QStringList templ = values(QLatin1String("TEMPLATE"));
+ if (templ.count() >= 1) {
+ QString t = templ.last().toLower();
+ if (t == QLatin1String("app"))
+ return TT_Application;
+ if (t == QLatin1String("lib"))
+ return TT_Library;
+ if (t == QLatin1String("subdirs"))
+ return TT_Subdirs;
+ }
+ return TT_Unknown;
+}
+
+bool ProFileEvaluator::queryProFile(ProFile *pro)
+{
+ return d->read(pro);
+}
+
+bool ProFileEvaluator::accept(ProFile *pro)
+{
+ return pro->Accept(d);
+}
+
+QString ProFileEvaluator::propertyValue(const QString &name) const
+{
+ return d->propertyValue(name);
+}
+
+namespace {
+ template<class K, class T> void insert(QHash<K,T> *out, const QHash<K,T> &in)
+ {
+ typename QHash<K,T>::const_iterator i = in.begin();
+ while (i != in.end()) {
+ out->insert(i.key(), i.value());
+ ++i;
+ }
+ }
+} // anon namespace
+
+void ProFileEvaluator::addVariables(const QHash<QString, QStringList> &variables)
+{
+ insert(&(d->m_valuemap), variables);
+}
+
+void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties)
+{
+ insert(&(d->m_properties), properties);
+}
+
+void ProFileEvaluator::logMessage(const QString &message)
+{
+ if (d->m_verbose)
+ qWarning("%s", qPrintable(message));
+}
+
+void ProFileEvaluator::fileMessage(const QString &message)
+{
+ qWarning("%s", qPrintable(message));
+}
+
+void ProFileEvaluator::errorMessage(const QString &message)
+{
+ qWarning("%s", qPrintable(message));
+}
+
+void ProFileEvaluator::setVerbose(bool on)
+{
+ d->m_verbose = on;
+}
+
+void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap)
+{
+ QStringList sourceFiles;
+ QString codecForTr;
+ QString codecForSource;
+ QStringList tsFileNames;
+
+ // app/lib template
+ sourceFiles += visitor.values(QLatin1String("SOURCES"));
+ sourceFiles += visitor.values(QLatin1String("HEADERS"));
+ tsFileNames = visitor.values(QLatin1String("TRANSLATIONS"));
+
+ QStringList trcodec = visitor.values(QLatin1String("CODEC"))
+ + visitor.values(QLatin1String("DEFAULTCODEC"))
+ + visitor.values(QLatin1String("CODECFORTR"));
+ if (!trcodec.isEmpty())
+ codecForTr = trcodec.last();
+
+ QStringList srccodec = visitor.values(QLatin1String("CODECFORSRC"));
+ if (!srccodec.isEmpty())
+ codecForSource = srccodec.last();
+
+ QStringList forms = visitor.values(QLatin1String("INTERFACES"))
+ + visitor.values(QLatin1String("FORMS"))
+ + visitor.values(QLatin1String("FORMS3"));
+ sourceFiles << forms;
+
+ sourceFiles.sort();
+ sourceFiles.removeDuplicates();
+ tsFileNames.sort();
+ tsFileNames.removeDuplicates();
+
+ varMap->insert("SOURCES", sourceFiles);
+ varMap->insert("CODECFORTR", QStringList() << codecForTr);
+ varMap->insert("CODECFORSRC", QStringList() << codecForSource);
+ varMap->insert("TRANSLATIONS", tsFileNames);
+}
+
+bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap)
+{
+ QFileInfo fi(fileName);
+ if (!fi.exists())
+ return false;
+
+ ProFile pro(fi.absoluteFilePath());
+
+ ProFileEvaluator visitor;
+ visitor.setVerbose(verbose);
+
+ if (!visitor.queryProFile(&pro))
+ return false;
+
+ if (!visitor.accept(&pro))
+ return false;
+
+ evaluateProFile(visitor, varMap);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/profileevaluator.h b/tools/linguist/shared/profileevaluator.h
new file mode 100644
index 0000000000..beb16ea537
--- /dev/null
+++ b/tools/linguist/shared/profileevaluator.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROFILEEVALUATOR_H
+#define PROFILEEVALUATOR_H
+
+#include "proitems.h"
+#include "abstractproitemvisitor.h"
+
+#include <QtCore/QIODevice>
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QStack>
+
+QT_BEGIN_NAMESPACE
+
+class ProFile;
+class ProFileEvaluator;
+
+void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap);
+bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap);
+
+class ProFileEvaluator
+{
+public:
+ enum TemplateType {
+ TT_Unknown = 0,
+ TT_Application,
+ TT_Library,
+ TT_Subdirs
+ };
+
+ ProFileEvaluator();
+ virtual ~ProFileEvaluator();
+
+ ProFileEvaluator::TemplateType templateType();
+ virtual bool contains(const QString &variableName) const;
+ void setVerbose(bool on);
+
+ bool queryProFile(ProFile *pro);
+ bool accept(ProFile *pro);
+
+ void addVariables(const QHash<QString, QStringList> &variables);
+ void addProperties(const QHash<QString, QString> &properties);
+ QStringList values(const QString &variableName) const;
+ QStringList values(const QString &variableName, const ProFile *pro) const;
+ QString propertyValue(const QString &val) const;
+
+ // for our descendents
+ virtual ProFile *parsedProFile(const QString &fileName);
+ virtual void releaseParsedProFile(ProFile *proFile);
+ virtual void logMessage(const QString &msg);
+ virtual void errorMessage(const QString &msg); // .pro parse errors
+ virtual void fileMessage(const QString &msg); // error() and message() from .pro file
+
+private:
+ class Private;
+ Private *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // PROFILEEVALUATOR_H
diff --git a/tools/linguist/shared/proitems.cpp b/tools/linguist/shared/proitems.cpp
new file mode 100644
index 0000000000..1895852838
--- /dev/null
+++ b/tools/linguist/shared/proitems.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "proitems.h"
+#include "abstractproitemvisitor.h"
+
+#include <QtCore/QFileInfo>
+
+QT_BEGIN_NAMESPACE
+
+// --------------- ProItem ------------
+void ProItem::setComment(const QString &comment)
+{
+ m_comment = comment;
+}
+
+QString ProItem::comment() const
+{
+ return m_comment;
+}
+
+// --------------- ProBlock ----------------
+ProBlock::ProBlock(ProBlock *parent)
+{
+ m_blockKind = 0;
+ m_parent = parent;
+}
+
+ProBlock::~ProBlock()
+{
+ qDeleteAll(m_proitems);
+}
+
+void ProBlock::appendItem(ProItem *proitem)
+{
+ m_proitems << proitem;
+}
+
+void ProBlock::setItems(const QList<ProItem *> &proitems)
+{
+ m_proitems = proitems;
+}
+
+QList<ProItem *> ProBlock::items() const
+{
+ return m_proitems;
+}
+
+void ProBlock::setBlockKind(int blockKind)
+{
+ m_blockKind = blockKind;
+}
+
+int ProBlock::blockKind() const
+{
+ return m_blockKind;
+}
+
+void ProBlock::setParent(ProBlock *parent)
+{
+ m_parent = parent;
+}
+
+ProBlock *ProBlock::parent() const
+{
+ return m_parent;
+}
+
+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;
+ }
+ return visitor->visitEndProBlock(this);
+}
+
+// --------------- ProVariable ----------------
+ProVariable::ProVariable(const QString &name, ProBlock *parent)
+ : ProBlock(parent)
+{
+ setBlockKind(ProBlock::VariableKind);
+ m_variable = name;
+ m_variableKind = SetOperator;
+}
+
+void ProVariable::setVariableOperator(VariableOperator variableKind)
+{
+ m_variableKind = variableKind;
+}
+
+ProVariable::VariableOperator ProVariable::variableOperator() const
+{
+ return m_variableKind;
+}
+
+void ProVariable::setVariable(const QString &name)
+{
+ m_variable = name;
+}
+
+QString ProVariable::variable() const
+{
+ return m_variable;
+}
+
+bool ProVariable::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitBeginProVariable(this);
+ foreach (ProItem *item, m_proitems) {
+ if (!item->Accept(visitor))
+ return false;
+ }
+ return visitor->visitEndProVariable(this);
+}
+
+// --------------- ProValue ----------------
+ProValue::ProValue(const QString &value, ProVariable *variable)
+{
+ m_variable = variable;
+ m_value = value;
+}
+
+void ProValue::setValue(const QString &value)
+{
+ m_value = value;
+}
+
+QString ProValue::value() const
+{
+ return m_value;
+}
+
+void ProValue::setVariable(ProVariable *variable)
+{
+ m_variable = variable;
+}
+
+ProVariable *ProValue::variable() const
+{
+ return m_variable;
+}
+
+ProItem::ProItemKind ProValue::kind() const
+{
+ return ProItem::ValueKind;
+}
+
+bool ProValue::Accept(AbstractProItemVisitor *visitor)
+{
+ return visitor->visitProValue(this);
+}
+
+// --------------- ProFunction ----------------
+ProFunction::ProFunction(const QString &text)
+{
+ m_text = text;
+}
+
+void ProFunction::setText(const QString &text)
+{
+ m_text = text;
+}
+
+QString ProFunction::text() const
+{
+ return m_text;
+}
+
+ProItem::ProItemKind ProFunction::kind() const
+{
+ return ProItem::FunctionKind;
+}
+
+bool ProFunction::Accept(AbstractProItemVisitor *visitor)
+{
+ return visitor->visitProFunction(this);
+}
+
+// --------------- ProCondition ----------------
+ProCondition::ProCondition(const QString &text)
+{
+ m_text = text;
+}
+
+void ProCondition::setText(const QString &text)
+{
+ m_text = text;
+}
+
+QString ProCondition::text() const
+{
+ return m_text;
+}
+
+ProItem::ProItemKind ProCondition::kind() const
+{
+ return ProItem::ConditionKind;
+}
+
+bool ProCondition::Accept(AbstractProItemVisitor *visitor)
+{
+ return visitor->visitProCondition(this);
+}
+
+// --------------- ProOperator ----------------
+ProOperator::ProOperator(OperatorKind operatorKind)
+{
+ m_operatorKind = operatorKind;
+}
+
+void ProOperator::setOperatorKind(OperatorKind operatorKind)
+{
+ m_operatorKind = operatorKind;
+}
+
+ProOperator::OperatorKind ProOperator::operatorKind() const
+{
+ return m_operatorKind;
+}
+
+ProItem::ProItemKind ProOperator::kind() const
+{
+ return ProItem::OperatorKind;
+}
+
+bool ProOperator::Accept(AbstractProItemVisitor *visitor)
+{
+ return visitor->visitProOperator(this);
+}
+
+// --------------- ProFile ----------------
+ProFile::ProFile(const QString &fileName)
+ : ProBlock(0)
+{
+ m_modified = false;
+ setBlockKind(ProBlock::ProFileKind);
+ m_fileName = fileName;
+
+ QFileInfo fi(fileName);
+ m_displayFileName = fi.fileName();
+ m_directoryName = fi.absolutePath();
+}
+
+ProFile::~ProFile()
+{
+}
+
+QString ProFile::displayFileName() const
+{
+ return m_displayFileName;
+}
+
+QString ProFile::fileName() const
+{
+ return m_fileName;
+}
+
+QString ProFile::directoryName() const
+{
+ return m_directoryName;
+}
+
+void ProFile::setModified(bool modified)
+{
+ m_modified = modified;
+}
+
+bool ProFile::isModified() const
+{
+ return m_modified;
+}
+
+bool ProFile::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitBeginProFile(this);
+ foreach (ProItem *item, m_proitems) {
+ if (!item->Accept(visitor))
+ return false;
+ }
+ return visitor->visitEndProFile(this);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/proitems.h b/tools/linguist/shared/proitems.h
new file mode 100644
index 0000000000..befaa8857c
--- /dev/null
+++ b/tools/linguist/shared/proitems.h
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROITEMS_H
+#define PROITEMS_H
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+
+struct AbstractProItemVisitor;
+
+class ProItem
+{
+public:
+ enum ProItemKind {
+ ValueKind,
+ FunctionKind,
+ ConditionKind,
+ OperatorKind,
+ BlockKind
+ };
+
+ ProItem() : m_lineNumber(0) {}
+ virtual ~ProItem() {}
+
+ virtual ProItemKind kind() const = 0;
+
+ void setComment(const QString &comment);
+ QString comment() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor) = 0;
+ int lineNumber() const { return m_lineNumber; }
+ void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
+
+private:
+ QString m_comment;
+ int m_lineNumber;
+};
+
+class ProBlock : public ProItem
+{
+public:
+ enum ProBlockKind {
+ NormalKind = 0x00,
+ ScopeKind = 0x01,
+ ScopeContentsKind = 0x02,
+ VariableKind = 0x04,
+ ProFileKind = 0x08,
+ SingleLine = 0x10
+ };
+
+ ProBlock(ProBlock *parent);
+ ~ProBlock();
+
+ void appendItem(ProItem *proitem);
+ void setItems(const QList<ProItem *> &proitems);
+ QList<ProItem *> items() const;
+
+ void setBlockKind(int blockKind);
+ int blockKind() const;
+
+ void setParent(ProBlock *parent);
+ ProBlock *parent() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+protected:
+ QList<ProItem *> m_proitems;
+private:
+ ProBlock *m_parent;
+ int m_blockKind;
+};
+
+class ProVariable : public ProBlock
+{
+public:
+ enum VariableOperator {
+ AddOperator = 0,
+ RemoveOperator = 1,
+ ReplaceOperator = 2,
+ SetOperator = 3,
+ UniqueAddOperator = 4
+ };
+
+ ProVariable(const QString &name, ProBlock *parent);
+
+ void setVariableOperator(VariableOperator variableKind);
+ VariableOperator variableOperator() const;
+
+ void setVariable(const QString &name);
+ QString variable() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+private:
+ VariableOperator m_variableKind;
+ QString m_variable;
+};
+
+class ProValue : public ProItem
+{
+public:
+ ProValue(const QString &value, ProVariable *variable);
+
+ void setValue(const QString &value);
+ QString value() const;
+
+ void setVariable(ProVariable *variable);
+ ProVariable *variable() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_value;
+ ProVariable *m_variable;
+};
+
+class ProFunction : public ProItem
+{
+public:
+ explicit ProFunction(const QString &text);
+
+ void setText(const QString &text);
+ QString text() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_text;
+};
+
+class ProCondition : public ProItem
+{
+public:
+ explicit ProCondition(const QString &text);
+
+ void setText(const QString &text);
+ QString text() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_text;
+};
+
+class ProOperator : public ProItem
+{
+public:
+ enum OperatorKind {
+ OrOperator = 1,
+ NotOperator = 2
+ };
+
+ explicit ProOperator(OperatorKind operatorKind);
+
+ void setOperatorKind(OperatorKind operatorKind);
+ OperatorKind operatorKind() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+private:
+ OperatorKind m_operatorKind;
+};
+
+class ProFile : public QObject, public ProBlock
+{
+ Q_OBJECT
+
+public:
+ explicit ProFile(const QString &fileName);
+ ~ProFile();
+
+ QString displayFileName() const;
+ QString fileName() const;
+ QString directoryName() const;
+
+ void setModified(bool modified);
+ bool isModified() const;
+
+ virtual bool Accept(AbstractProItemVisitor *visitor);
+
+private:
+ QString m_fileName;
+ QString m_displayFileName;
+ QString m_directoryName;
+ bool m_modified;
+};
+
+QT_END_NAMESPACE
+
+#endif // PROITEMS_H
diff --git a/tools/linguist/shared/proparser.pri b/tools/linguist/shared/proparser.pri
new file mode 100644
index 0000000000..372247e46c
--- /dev/null
+++ b/tools/linguist/shared/proparser.pri
@@ -0,0 +1,12 @@
+
+INCLUDEPATH *= $$PWD
+
+HEADERS += \
+ $$PWD/abstractproitemvisitor.h \
+ $$PWD/proitems.h \
+ $$PWD/profileevaluator.h \
+ $$PWD/proparserutils.h
+
+SOURCES += \
+ $$PWD/proitems.cpp \
+ $$PWD/profileevaluator.cpp
diff --git a/tools/linguist/shared/proparserutils.h b/tools/linguist/shared/proparserutils.h
new file mode 100644
index 0000000000..8914a8ed9d
--- /dev/null
+++ b/tools/linguist/shared/proparserutils.h
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROPARSERUTILS_H
+#define PROPARSERUTILS_H
+
+#include <QtCore/QDir>
+#include <QtCore/QLibraryInfo>
+
+QT_BEGIN_NAMESPACE
+
+// Pre- and postcondition macros
+#define PRE(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
+#define POST(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
+
+// This struct is from qmake, but we are not using everything.
+struct Option
+{
+ //simply global convenience
+ //static QString libtool_ext;
+ //static QString pkgcfg_ext;
+ //static QString prf_ext;
+ //static QString prl_ext;
+ //static QString ui_ext;
+ //static QStringList h_ext;
+ //static QStringList cpp_ext;
+ //static QString h_moc_ext;
+ //static QString cpp_moc_ext;
+ //static QString obj_ext;
+ //static QString lex_ext;
+ //static QString yacc_ext;
+ //static QString h_moc_mod;
+ //static QString cpp_moc_mod;
+ //static QString lex_mod;
+ //static QString yacc_mod;
+ static QString dir_sep;
+ static QString dirlist_sep;
+ static QString qmakespec;
+ static QChar field_sep;
+
+ enum TARG_MODE { TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE, TARG_MAC9_MODE, TARG_QNX6_MODE };
+ static TARG_MODE target_mode;
+ //static QString pro_ext;
+ //static QString res_ext;
+
+ static void init()
+ {
+#ifdef Q_OS_WIN
+ Option::dirlist_sep = QLatin1Char(';');
+ Option::dir_sep = QLatin1Char('\\');
+#else
+ Option::dirlist_sep = QLatin1Char(':');
+ Option::dir_sep = QLatin1Char(QLatin1Char('/'));
+#endif
+ Option::qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data());
+ Option::field_sep = QLatin1Char(' ');
+ }
+};
+#if defined(Q_OS_WIN32)
+Option::TARG_MODE Option::target_mode = Option::TARG_WIN_MODE;
+#elif defined(Q_OS_MAC)
+Option::TARG_MODE Option::target_mode = Option::TARG_MACX_MODE;
+#elif defined(Q_OS_QNX6)
+Option::TARG_MODE Option::target_mode = Option::TARG_QNX6_MODE;
+#else
+Option::TARG_MODE Option::target_mode = Option::TARG_UNIX_MODE;
+#endif
+
+QString Option::qmakespec;
+QString Option::dirlist_sep;
+QString Option::dir_sep;
+QChar Option::field_sep;
+
+static void insertUnique(QHash<QString, QStringList> *map,
+ const QString &key, const QStringList &value, bool unique = true)
+{
+ QStringList &sl = (*map)[key];
+ if (!unique) {
+ sl += value;
+ } else {
+ for (int i = 0; i < value.count(); ++i) {
+ if (!sl.contains(value.at(i)))
+ sl.append(value.at(i));
+ }
+ }
+}
+
+/*
+ See ProFileEvaluator::Private::visitProValue(...)
+
+static QStringList replaceInList(const QStringList &varList, const QRegExp &regexp,
+ const QString &replace, bool global)
+{
+ QStringList resultList = varList;
+
+ for (QStringList::Iterator varit = resultList.begin(); varit != resultList.end();) {
+ if (varit->contains(regexp)) {
+ *varit = varit->replace(regexp, replace);
+ if (varit->isEmpty())
+ varit = resultList.erase(varit);
+ else
+ ++varit;
+ if (!global)
+ break;
+ } else {
+ ++varit;
+ }
+ }
+ return resultList;
+}
+*/
+
+inline QStringList splitPathList(const QString paths)
+{
+ return paths.split(Option::dirlist_sep);
+}
+
+static QStringList split_arg_list(QString params)
+{
+ int quote = 0;
+ QStringList args;
+
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+ const ushort COMMA = ',';
+ const ushort SPACE = ' ';
+ //const ushort TAB = '\t';
+
+ ushort unicode;
+ const QChar *params_data = params.data();
+ const int params_len = params.length();
+ int last = 0;
+ while (last < params_len && ((params_data+last)->unicode() == SPACE
+ /*|| (params_data+last)->unicode() == TAB*/))
+ ++last;
+ for (int x = last, parens = 0; x <= params_len; x++) {
+ unicode = (params_data+x)->unicode();
+ if (x == params_len) {
+ while (x && (params_data+(x-1))->unicode() == SPACE)
+ --x;
+ QString mid(params_data+last, x-last);
+ if (quote) {
+ if (mid[0] == quote && mid[(int)mid.length()-1] == quote)
+ mid = mid.mid(1, mid.length()-2);
+ quote = 0;
+ }
+ args << mid;
+ break;
+ }
+ if (unicode == LPAREN) {
+ --parens;
+ } else if (unicode == RPAREN) {
+ ++parens;
+ } else if (quote && unicode == quote) {
+ quote = 0;
+ } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
+ quote = unicode;
+ }
+ if (!parens && !quote && unicode == COMMA) {
+ QString mid = params.mid(last, x - last).trimmed();
+ args << mid;
+ last = x+1;
+ while (last < params_len && ((params_data+last)->unicode() == SPACE
+ /*|| (params_data+last)->unicode() == TAB*/))
+ ++last;
+ }
+ }
+ return args;
+}
+
+static QStringList split_value_list(const QString &vals, bool do_semicolon=false)
+{
+ QString build;
+ QStringList ret;
+ QStack<char> quote;
+
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+ const ushort BACKSLASH = '\\';
+ const ushort SEMICOLON = ';';
+
+ ushort unicode;
+ const QChar *vals_data = vals.data();
+ const int vals_len = vals.length();
+ for (int x = 0, parens = 0; x < vals_len; x++) {
+ unicode = vals_data[x].unicode();
+ if (x != (int)vals_len-1 && unicode == BACKSLASH &&
+ (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
+ build += vals_data[x++]; //get that 'escape'
+ } else if (!quote.isEmpty() && unicode == quote.top()) {
+ quote.pop();
+ } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+ quote.push(unicode);
+ } else if (unicode == RPAREN) {
+ --parens;
+ } else if (unicode == LPAREN) {
+ ++parens;
+ }
+
+ if (!parens && quote.isEmpty() && ((do_semicolon && unicode == SEMICOLON) ||
+ vals_data[x] == Option::field_sep)) {
+ ret << build;
+ build.clear();
+ } else {
+ build += vals_data[x];
+ }
+ }
+ if (!build.isEmpty())
+ ret << build;
+ return ret;
+}
+
+static QStringList qmake_mkspec_paths()
+{
+ QStringList ret;
+ const QString concat = QDir::separator() + QString(QLatin1String("mkspecs"));
+ QByteArray qmakepath = qgetenv("QMAKEPATH");
+ if (!qmakepath.isEmpty()) {
+ const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
+ for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it)
+ ret << ((*it) + concat);
+ }
+ ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat;
+
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // PROPARSERUTILS_H
diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp
new file mode 100644
index 0000000000..c197e2b1f2
--- /dev/null
+++ b/tools/linguist/shared/qm.cpp
@@ -0,0 +1,717 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+
+QT_BEGIN_NAMESPACE
+
+// magic number for the file
+static const int MagicLength = 16;
+static const uchar magic[MagicLength] = {
+ 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
+ 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
+};
+
+
+namespace {
+
+enum Tag {
+ Tag_End = 1,
+ Tag_SourceText16 = 2,
+ Tag_Translation = 3,
+ Tag_Context16 = 4,
+ Tag_Obsolete1 = 5,
+ Tag_SourceText = 6,
+ Tag_Context = 7,
+ Tag_Comment = 8,
+ Tag_Obsolete2 = 9
+};
+
+enum Prefix {
+ NoPrefix,
+ Hash,
+ HashContext,
+ HashContextSourceText,
+ HashContextSourceTextComment
+};
+
+} // namespace anon
+
+static uint elfHash(const QByteArray &ba)
+{
+ const uchar *k = (const uchar *)ba.data();
+ uint h = 0;
+ uint g;
+
+ if (k) {
+ while (*k) {
+ h = (h << 4) + *k++;
+ if ((g = (h & 0xf0000000)) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+ if (!h)
+ h = 1;
+ return h;
+}
+
+class Releaser
+{
+public:
+ struct Offset {
+ Offset()
+ : h(0), o(0)
+ {}
+ Offset(uint hash, uint offset)
+ : h(hash), o(offset)
+ {}
+
+ bool operator<(const Offset &other) const {
+ return (h != other.h) ? h < other.h : o < other.o;
+ }
+ bool operator==(const Offset &other) const {
+ return h == other.h && o == other.o;
+ }
+ uint h;
+ uint o;
+ };
+
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
+
+ Releaser() : m_codec(0) {}
+
+ void setCodecName(const QByteArray &codecName)
+ {
+ m_codec = QTextCodec::codecForName(codecName);
+ }
+
+ TranslatorMessage findMessage(const QString &context,
+ const QString &sourceText, const QString &comment,
+ const QString &fileName = QString(), int lineNumber = -1) const;
+
+ bool save(QIODevice *iod);
+
+ void insert(const TranslatorMessage &);
+ void remove(const TranslatorMessage &);
+
+ bool contains(const QString &context, const QString &sourceText,
+ const QString & comment) const;
+
+ bool contains(const QString &context, const QString &comment,
+ const QString &fileName, int lineNumber) const;
+
+ void squeeze(TranslatorSaveMode mode);
+
+ QList<TranslatorMessage> messages() const;
+
+ bool isEmpty() const;
+
+ void setNumerusRules(const QByteArray &rules);
+
+private:
+ Q_DISABLE_COPY(Releaser)
+
+ // This should reproduce the byte array fetched from the source file, which
+ // on turn should be the same as passed to the actual tr(...) calls
+ QByteArray originalBytes(const QString &str, bool isUtf8) const;
+
+ Prefix commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const;
+
+ uint msgHash(const TranslatorMessage &msg) const;
+
+ void writeMessage(const TranslatorMessage & msg, QDataStream & stream,
+ TranslatorSaveMode strip, Prefix prefix) const;
+
+ // for squeezed but non-file data, this is what needs to be deleted
+ QByteArray m_messageArray;
+ QByteArray m_offsetArray;
+ QByteArray m_contextArray;
+ QMap<TranslatorMessage, void *> m_messages;
+ QByteArray m_numerusRules;
+
+ // Used to reproduce the original bytes
+ QTextCodec *m_codec;
+};
+
+QByteArray Releaser::originalBytes(const QString &str, bool isUtf8) const
+{
+ if (str.isEmpty()) {
+ // Do not use QByteArray() here as the result of the serialization
+ // will be different.
+ return QByteArray("");
+ }
+ if (isUtf8)
+ return str.toUtf8();
+ return m_codec ? m_codec->fromUnicode(str) : str.toLatin1();
+}
+
+uint Releaser::msgHash(const TranslatorMessage &msg) const
+{
+ return elfHash(originalBytes(msg.sourceText() + msg.comment(), msg.isUtf8()));
+}
+
+Prefix Releaser::commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const
+{
+ if (msgHash(m1) != msgHash(m2))
+ return NoPrefix;
+ if (m1.context() != m2.context())
+ return Hash;
+ if (m1.sourceText() != m2.sourceText())
+ return HashContext;
+ if (m1.comment() != m2.comment())
+ return HashContextSourceText;
+ return HashContextSourceTextComment;
+}
+
+void Releaser::writeMessage(const TranslatorMessage & msg, QDataStream & stream,
+ TranslatorSaveMode mode, Prefix prefix) const
+{
+ for (int i = 0; i < msg.translations().count(); ++i) {
+ QString str = msg.translations().at(i);
+ str.replace(QChar(Translator::DefaultVariantSeparator),
+ QChar(Translator::InternalVariantSeparator));
+ stream << quint8(Tag_Translation) << str;
+ }
+
+ if (mode == SaveEverything)
+ prefix = HashContextSourceTextComment;
+
+ // lrelease produces "wrong" .qm files for QByteArrays that are .isNull().
+ switch (prefix) {
+ default:
+ case HashContextSourceTextComment:
+ stream << quint8(Tag_Comment) << originalBytes(msg.comment(), msg.isUtf8());
+ // fall through
+ case HashContextSourceText:
+ stream << quint8(Tag_SourceText) << originalBytes(msg.sourceText(), msg.isUtf8());
+ // fall through
+ case HashContext:
+ stream << quint8(Tag_Context) << originalBytes(msg.context(), msg.isUtf8());
+ ;
+ }
+
+ stream << quint8(Tag_End);
+}
+
+
+bool Releaser::save(QIODevice *iod)
+{
+ QDataStream s(iod);
+ s.writeRawData((const char *)magic, MagicLength);
+
+ if (!m_offsetArray.isEmpty()) {
+ quint32 oas = quint32(m_offsetArray.size());
+ s << quint8(Hashes) << oas;
+ s.writeRawData(m_offsetArray.constData(), oas);
+ }
+ if (!m_messageArray.isEmpty()) {
+ quint32 mas = quint32(m_messageArray.size());
+ s << quint8(Messages) << mas;
+ s.writeRawData(m_messageArray.constData(), mas);
+ }
+ if (!m_contextArray.isEmpty()) {
+ quint32 cas = quint32(m_contextArray.size());
+ s << quint8(Contexts) << cas;
+ s.writeRawData(m_contextArray.constData(), cas);
+ }
+ if (!m_numerusRules.isEmpty()) {
+ quint32 nrs = m_numerusRules.size();
+ s << quint8(NumerusRules) << nrs;
+ s.writeRawData(m_numerusRules.constData(), nrs);
+ }
+ return true;
+}
+
+void Releaser::squeeze(TranslatorSaveMode mode)
+{
+ if (m_messages.isEmpty() && mode == SaveEverything)
+ return;
+
+ QMap<TranslatorMessage, void *> messages = m_messages;
+
+ // re-build contents
+ m_messageArray.clear();
+ m_offsetArray.clear();
+ m_contextArray.clear();
+ m_messages.clear();
+
+ QMap<Offset, void *> offsets;
+
+ QDataStream ms(&m_messageArray, QIODevice::WriteOnly);
+ QMap<TranslatorMessage, void *>::const_iterator it, next;
+ int cpPrev = 0, cpNext = 0;
+ for (it = messages.constBegin(); it != messages.constEnd(); ++it) {
+ cpPrev = cpNext;
+ next = it;
+ ++next;
+ if (next == messages.constEnd())
+ cpNext = 0;
+ else
+ cpNext = commonPrefix(it.key(), next.key());
+ offsets.insert(Offset(msgHash(it.key()), ms.device()->pos()), (void *)0);
+ writeMessage(it.key(), ms, mode, Prefix(qMax(cpPrev, cpNext + 1)));
+ }
+
+ QMap<Offset, void *>::Iterator offset;
+ offset = offsets.begin();
+ QDataStream ds(&m_offsetArray, QIODevice::WriteOnly);
+ while (offset != offsets.end()) {
+ Offset k = offset.key();
+ ++offset;
+ ds << quint32(k.h) << quint32(k.o);
+ }
+
+ if (mode == SaveStripped) {
+ QMap<QString, int> contextSet;
+ for (it = messages.constBegin(); it != messages.constEnd(); ++it)
+ ++contextSet[it.key().context()];
+
+ quint16 hTableSize;
+ if (contextSet.size() < 200)
+ hTableSize = (contextSet.size() < 60) ? 151 : 503;
+ else if (contextSet.size() < 2500)
+ hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
+ else
+ hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
+
+ QMultiMap<int, QString> hashMap;
+ QMap<QString, int>::const_iterator c;
+ for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c)
+ hashMap.insert(elfHash(originalBytes(c.key(), false /*FIXME*/)) % hTableSize, c.key());
+
+ /*
+ The contexts found in this translator are stored in a hash
+ table to provide fast lookup. The context array has the
+ following format:
+
+ quint16 hTableSize;
+ quint16 hTable[hTableSize];
+ quint8 contextPool[...];
+
+ The context pool stores the contexts as Pascal strings:
+
+ quint8 len;
+ quint8 data[len];
+
+ Let's consider the look-up of context "FunnyDialog". A
+ hash value between 0 and hTableSize - 1 is computed, say h.
+ If hTable[h] is 0, "FunnyDialog" is not covered by this
+ translator. Else, we check in the contextPool at offset
+ 2 * hTable[h] to see if "FunnyDialog" is one of the
+ contexts stored there, until we find it or we meet the
+ empty string.
+ */
+ m_contextArray.resize(2 + (hTableSize << 1));
+ QDataStream t(&m_contextArray, QIODevice::WriteOnly);
+
+ quint16 *hTable = new quint16[hTableSize];
+ memset(hTable, 0, hTableSize * sizeof(quint16));
+
+ t << hTableSize;
+ t.device()->seek(2 + (hTableSize << 1));
+ t << quint16(0); // the entry at offset 0 cannot be used
+ uint upto = 2;
+
+ QMap<int, QString>::const_iterator entry = hashMap.constBegin();
+ while (entry != hashMap.constEnd()) {
+ int i = entry.key();
+ hTable[i] = quint16(upto >> 1);
+
+ do {
+ QString context = entry.value();
+ QByteArray ba = context.toUtf8();
+ const char *con = ba.data();
+ uint len = uint(qstrlen(con));
+ len = qMin(len, 255u);
+ t << quint8(len);
+ t.writeRawData(con, len);
+ upto += 1 + len;
+ ++entry;
+ } while (entry != hashMap.constEnd() && entry.key() == i);
+ if (upto & 0x1) {
+ // offsets have to be even
+ t << quint8(0); // empty string
+ ++upto;
+ }
+ }
+ t.device()->seek(2);
+ for (int j = 0; j < hTableSize; j++)
+ t << hTable[j];
+ delete [] hTable;
+
+ if (upto > 131072) {
+ qWarning("Releaser::squeeze: Too many contexts");
+ m_contextArray.clear();
+ }
+ }
+}
+
+bool Releaser::contains(const QString &context, const QString &sourceText,
+ const QString &comment) const
+{
+ return !findMessage(context, sourceText, comment).translation().isNull();
+}
+
+bool Releaser::contains(const QString &context, const QString &comment,
+ const QString &fileName, int lineNumber) const
+{
+ return !findMessage(context, QString(), comment, fileName, lineNumber).isNull();
+}
+
+void Releaser::insert(const TranslatorMessage &message)
+{
+ m_messages.insert(message, 0);
+}
+
+void Releaser::remove(const TranslatorMessage &message)
+{
+ m_messages.remove(message);
+}
+
+
+TranslatorMessage Releaser::findMessage(const QString &context,
+ const QString &sourceText, const QString &comment,
+ const QString &fileName, int lineNumber) const
+{
+ if (m_messages.isEmpty())
+ return TranslatorMessage();
+
+ QMap<TranslatorMessage, void *>::const_iterator it;
+
+ // Either we want to find an item that matches context, sourcetext
+ // (and optionally comment) Or we want to find an item that
+ // matches context, filename, linenumber (and optionally comment)
+ TranslatorMessage msg(context, sourceText, comment, QString(), fileName, lineNumber);
+ it = m_messages.constFind(msg);
+ if (it != m_messages.constEnd())
+ return it.key();
+
+ if (!comment.isEmpty()) {
+ it = m_messages.constFind(TranslatorMessage(context, sourceText, QString(), QString(), fileName, lineNumber));
+ if (it != m_messages.constEnd())
+ return it.key();
+ }
+
+ it = m_messages.constFind(TranslatorMessage(context, QString(), comment, QString(), fileName, lineNumber));
+ if (it != m_messages.constEnd())
+ return it.key();
+ if (comment.isEmpty())
+ return TranslatorMessage();
+
+ it = m_messages.constFind(TranslatorMessage(context, QString(), QString(), QString(), fileName, lineNumber));
+ if (it != m_messages.constEnd())
+ return it.key();
+ return TranslatorMessage();
+}
+
+bool Releaser::isEmpty() const
+{
+ return m_messageArray.isEmpty() && m_offsetArray.isEmpty()
+ && m_contextArray.isEmpty() && m_messages.isEmpty();
+}
+
+void Releaser::setNumerusRules(const QByteArray &rules)
+{
+ m_numerusRules = rules;
+}
+
+QList<TranslatorMessage> Releaser::messages() const
+{
+ return m_messages.keys();
+}
+
+static quint8 read8(const uchar *data)
+{
+ return *data;
+}
+
+static quint32 read32(const uchar *data)
+{
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
+}
+
+
+bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QByteArray ba = dev.readAll();
+ const uchar *data = (uchar*)ba.data();
+ int len = ba.size();
+ if (len < MagicLength || memcmp(data, magic, MagicLength) != 0) {
+ cd.appendError(QLatin1String("QM-Format error: magic marker missing"));
+ return false;
+ }
+
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
+
+ // for squeezed but non-file data, this is what needs to be deleted
+ const uchar *messageArray = 0;
+ const uchar *offsetArray = 0;
+ const uchar *contextArray = 0;
+ const uchar *numerusRulesArray = 0;
+ uint messageLength = 0;
+ uint offsetLength = 0;
+ uint contextLength = 0;
+ uint numerusRulesLength = 0;
+
+ bool ok = true;
+ const uchar *end = data + len;
+
+ data += MagicLength;
+
+ while (data < end - 4) {
+ quint8 tag = read8(data++);
+ quint32 blockLen = read32(data);
+ //qDebug() << "TAG:" << tag << "BLOCKLEN:" << blockLen;
+ data += 4;
+ if (!tag || !blockLen)
+ break;
+ if (data + blockLen > end) {
+ ok = false;
+ break;
+ }
+
+ if (tag == Contexts) {
+ contextArray = data;
+ contextLength = blockLen;
+ //qDebug() << "CONTEXTS: " << contextLength << QByteArray((const char *)contextArray, contextLength).toHex();
+ } else if (tag == Hashes) {
+ offsetArray = data;
+ offsetLength = blockLen;
+ //qDebug() << "HASHES: " << offsetLength << QByteArray((const char *)offsetArray, offsetLength).toHex();
+ } else if (tag == Messages) {
+ messageArray = data;
+ messageLength = blockLen;
+ //qDebug() << "MESSAGES: " << messageLength << QByteArray((const char *)messageArray, messageLength).toHex();
+ } else if (tag == NumerusRules) {
+ numerusRulesArray = data;
+ numerusRulesLength = blockLen;
+ //qDebug() << "NUMERUSRULES: " << numerusRulesLength << QByteArray((const char *)numerusRulesArray, numerusRulesLength).toHex();
+ }
+
+ data += blockLen;
+ }
+
+
+ size_t numItems = offsetLength / (2 * sizeof(quint32));
+ //qDebug() << "NUMITEMS: " << numItems;
+
+ TranslatorMessage msg;
+
+ // FIXME: that's just a guess, the original locale data is lost...
+ QTextCodec *codec = QTextCodec::codecForLocale();
+
+ for (const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
+ //quint32 hash = read32(start);
+ quint32 ro = read32(start + 4);
+ //qDebug() << "\nHASH:" << hash;
+ const uchar *m = messageArray + ro;
+
+ for (;;) {
+ uchar tag = read8(m++);
+ //qDebug() << "Tag:" << tag << " ADDR: " << m;
+ switch(tag) {
+ case Tag_End:
+ goto end;
+ case Tag_Translation: {
+ int len = read32(m);
+ if (len % 1) {
+ cd.appendError(QLatin1String("QM-Format error"));
+ return false;
+ }
+ m += 4;
+ QString str = QString::fromUtf16((const ushort *)m, len/2);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ for (int i = 0; i < str.length(); ++i)
+ str[i] = QChar((str.at(i).unicode() >> 8) +
+ ((str.at(i).unicode() << 8) & 0xff00));
+ }
+ str.replace(QChar(Translator::InternalVariantSeparator),
+ QChar(Translator::DefaultVariantSeparator));
+ msg.appendTranslation(str);
+ m += len;
+ break;
+ }
+ case Tag_Obsolete1:
+ m += 4;
+ //qDebug() << "OBSOLETE";
+ break;
+ case Tag_SourceText: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "SOURCE LEN: " << len;
+ //qDebug() << "SOURCE: " << QByteArray((const char*)m, len);
+ msg.setSourceText(codec->toUnicode(QByteArray((const char*)m, len)));
+ m += len;
+ break;
+ }
+ case Tag_Context: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "CONTEXT LEN: " << len;
+ //qDebug() << "CONTEXT: " << QByteArray((const char*)m, len);
+ msg.setContext(codec->toUnicode(QByteArray((const char*)m, len)));
+ m += len;
+ break;
+ }
+ case Tag_Comment: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "COMMENT LEN: " << len;
+ //qDebug() << "COMMENT: " << QByteArray((const char*)m, len);
+ msg.setComment(codec->toUnicode(QByteArray((const char*)m, len)));
+ m += len;
+ break;
+ }
+ default:
+ //qDebug() << "UNKNOWN TAG" << tag;
+ break;
+ }
+ }
+ end:;
+ msg.setType(TranslatorMessage::Finished);
+ translator.append(msg);
+ //qDebug() << "\nHASH:" << hash << msg.sourceText() << msg.context();
+ msg.setTranslations(QStringList());
+ }
+ return ok;
+}
+
+
+
+static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Releaser releaser;
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(translator.languageCode(), &l, &c);
+ QByteArray rules;
+ if (getNumerusInfo(l, c, &rules, 0))
+ releaser.setNumerusRules(rules);
+ releaser.setCodecName(translator.codecName());
+
+ int finished = 0;
+ int unfinished = 0;
+ int untranslated = 0;
+
+ for (int i = 0; i != translator.messageCount(); ++i) {
+ const TranslatorMessage &msg = translator.message(i);
+ TranslatorMessage::Type typ = msg.type();
+ if (typ != TranslatorMessage::Obsolete) {
+ if (typ == TranslatorMessage::Unfinished) {
+ if (msg.translation().isEmpty())
+ ++untranslated;
+ else
+ ++unfinished;
+ } else {
+ ++finished;
+ }
+ QString context = msg.context();
+ QString sourceText = msg.sourceText();
+ QString comment = msg.comment();
+ QStringList translations = msg.translations();
+
+ if (!cd.ignoreUnfinished() || typ != TranslatorMessage::Unfinished) {
+ /*
+ Drop the comment in (context, sourceText, comment),
+ unless the context is empty,
+ unless (context, sourceText, "") already exists or
+ unless we already dropped the comment of (context,
+ sourceText, comment0).
+ */
+ if (comment.isEmpty()
+ || context.isEmpty()
+ || translator.contains(context, sourceText, QString())
+ || !releaser.findMessage(context, sourceText, QString()).translation()
+ .isNull() ) {
+ releaser.insert(msg);
+ } else {
+ TranslatorMessage tm(context, sourceText, QString(),
+ QString(), QString(), -1, translations);
+ //filename and lineNumbers will be ignored from now.
+ releaser.insert(tm);
+ }
+ }
+ }
+ }
+
+ releaser.squeeze(cd.m_saveMode);
+ bool saved = releaser.save(&dev);
+ if (saved && cd.isVerbose()) {
+ int generatedCount = finished + unfinished;
+ cd.appendError(QCoreApplication::translate("LRelease",
+ " Generated %n translation(s) (%1 finished and %2 unfinished)\n", 0,
+ QCoreApplication::CodecForTr, generatedCount).arg(finished).arg(unfinished));
+ if (untranslated)
+ cd.appendError(QCoreApplication::translate("LRelease",
+ " Ignored %n untranslated source text(s)\n", 0,
+ QCoreApplication::CodecForTr, untranslated));
+ }
+ return saved;
+}
+
+int initQM()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("qm");
+ format.description = QObject::tr("Compiled Qt translations");
+ format.fileType = Translator::FileFormat::TranslationBinary;
+ format.priority = 0;
+ format.loader = &loadQM;
+ format.saver = &saveQM;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQM)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/qph.cpp b/tools/linguist/shared/qph.cpp
new file mode 100644
index 0000000000..45d3a20a28
--- /dev/null
+++ b/tools/linguist/shared/qph.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamAttribute>
+
+QT_BEGIN_NAMESPACE
+
+class QPHReader : public QXmlStreamReader
+{
+public:
+ QPHReader(QIODevice &dev, ConversionData &cd)
+ : QXmlStreamReader(&dev), m_cd(cd)
+ {}
+
+ // the "real thing"
+ bool read(Translator &translator);
+
+private:
+ bool elementStarts(const QString &str) const
+ {
+ return isStartElement() && name() == str;
+ }
+
+ bool isWhiteSpace() const
+ {
+ return isCharacters() && text().toString().trimmed().isEmpty();
+ }
+
+ // needed to expand <byte ... />
+ QString readContents();
+ // needed to join <lengthvariant>s
+ QString readTransContents();
+
+ void handleError();
+
+ ConversionData &m_cd;
+
+ enum DataField { NoField, SourceField, TargetField, DefinitionField };
+ DataField m_currentField;
+ QString m_currentSource;
+ QString m_currentTarget;
+ QString m_currentDefinition;
+};
+
+bool QPHReader::read(Translator &translator)
+{
+ m_currentField = NoField;
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isStartElement()) {
+ if (name() == QLatin1String("source"))
+ m_currentField = SourceField;
+ else if (name() == QLatin1String("target"))
+ m_currentField = TargetField;
+ else if (name() == QLatin1String("definition"))
+ m_currentField = DefinitionField;
+ else
+ m_currentField = NoField;
+ } else if (isWhiteSpace()) {
+ // ignore these
+ } else if (isCharacters()) {
+ if (m_currentField == SourceField)
+ m_currentSource += text();
+ else if (m_currentField == TargetField)
+ m_currentTarget += text();
+ else if (m_currentField == DefinitionField)
+ m_currentDefinition += text();
+ } else if (isEndElement() && name() == QLatin1String("phrase")) {
+ TranslatorMessage msg;
+ msg.setSourceText(m_currentSource);
+ msg.setTranslation(m_currentTarget);
+ msg.setTranslatorComment(m_currentDefinition);
+ translator.append(msg);
+ m_currentSource.clear();
+ m_currentTarget.clear();
+ m_currentDefinition.clear();
+ }
+ }
+ return true;
+}
+
+static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ translator.setLocationsType(Translator::NoLocations);
+ QPHReader reader(dev, cd);
+ return reader.read(translator);
+}
+
+static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QTextStream t(&dev);
+ t << "<!DOCTYPE QPH><QPH>\n";
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ t << "<phrase>\n";
+ t << " <source>" << msg.sourceText() << "</source>\n";
+ t << " <target>" << msg.translations().join(QLatin1String("@"))
+ << "</target>\n";
+ if (!msg.context().isEmpty() || !msg.comment().isEmpty())
+ t << " <definition>" << msg.context() << msg.comment()
+ << "</definition>\n";
+ t << "</phrase>\n";
+ }
+ t << "</QPH>\n";
+ return true;
+}
+
+int initQPH()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("qph");
+ format.description = QObject::tr("Qt Linguist 'Phrase Book'");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 0;
+ format.loader = &loadQPH;
+ format.saver = &saveQPH;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQPH)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/qscript.cpp b/tools/linguist/shared/qscript.cpp
new file mode 100644
index 0000000000..9ab5e160fd
--- /dev/null
+++ b/tools/linguist/shared/qscript.cpp
@@ -0,0 +1,2408 @@
+// This file was generated by qlalr - DO NOT EDIT!
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+class QScriptGrammar
+{
+public:
+ enum {
+ EOF_SYMBOL = 0,
+ T_AND = 1,
+ T_AND_AND = 2,
+ T_AND_EQ = 3,
+ T_AUTOMATIC_SEMICOLON = 62,
+ T_BREAK = 4,
+ T_CASE = 5,
+ T_CATCH = 6,
+ T_COLON = 7,
+ T_COMMA = 8,
+ T_CONST = 81,
+ T_CONTINUE = 9,
+ T_DEBUGGER = 82,
+ T_DEFAULT = 10,
+ T_DELETE = 11,
+ T_DIVIDE_ = 12,
+ T_DIVIDE_EQ = 13,
+ T_DO = 14,
+ T_DOT = 15,
+ T_ELSE = 16,
+ T_EQ = 17,
+ T_EQ_EQ = 18,
+ T_EQ_EQ_EQ = 19,
+ T_FALSE = 80,
+ T_FINALLY = 20,
+ T_FOR = 21,
+ T_FUNCTION = 22,
+ T_GE = 23,
+ T_GT = 24,
+ T_GT_GT = 25,
+ T_GT_GT_EQ = 26,
+ T_GT_GT_GT = 27,
+ T_GT_GT_GT_EQ = 28,
+ T_IDENTIFIER = 29,
+ T_IF = 30,
+ T_IN = 31,
+ T_INSTANCEOF = 32,
+ T_LBRACE = 33,
+ T_LBRACKET = 34,
+ T_LE = 35,
+ T_LPAREN = 36,
+ T_LT = 37,
+ T_LT_LT = 38,
+ T_LT_LT_EQ = 39,
+ T_MINUS = 40,
+ T_MINUS_EQ = 41,
+ T_MINUS_MINUS = 42,
+ T_NEW = 43,
+ T_NOT = 44,
+ T_NOT_EQ = 45,
+ T_NOT_EQ_EQ = 46,
+ T_NULL = 78,
+ T_NUMERIC_LITERAL = 47,
+ T_OR = 48,
+ T_OR_EQ = 49,
+ T_OR_OR = 50,
+ T_PLUS = 51,
+ T_PLUS_EQ = 52,
+ T_PLUS_PLUS = 53,
+ T_QUESTION = 54,
+ T_RBRACE = 55,
+ T_RBRACKET = 56,
+ T_REMAINDER = 57,
+ T_REMAINDER_EQ = 58,
+ T_RESERVED_WORD = 83,
+ T_RETURN = 59,
+ T_RPAREN = 60,
+ T_SEMICOLON = 61,
+ T_STAR = 63,
+ T_STAR_EQ = 64,
+ T_STRING_LITERAL = 65,
+ T_SWITCH = 66,
+ T_THIS = 67,
+ T_THROW = 68,
+ T_TILDE = 69,
+ T_TRUE = 79,
+ T_TRY = 70,
+ T_TYPEOF = 71,
+ T_VAR = 72,
+ T_VOID = 73,
+ T_WHILE = 74,
+ T_WITH = 75,
+ T_XOR = 76,
+ T_XOR_EQ = 77,
+
+ ACCEPT_STATE = 236,
+ RULE_COUNT = 267,
+ STATE_COUNT = 465,
+ TERMINAL_COUNT = 84,
+ NON_TERMINAL_COUNT = 88,
+
+ GOTO_INDEX_OFFSET = 465,
+ GOTO_INFO_OFFSET = 1374,
+ GOTO_CHECK_OFFSET = 1374
+ };
+
+ static const char *const spell [];
+ static const int lhs [];
+ static const int rhs [];
+ static const int goto_default [];
+ static const int action_default [];
+ static const int action_index [];
+ static const int action_info [];
+ static const int action_check [];
+
+ static inline int nt_action (int state, int nt)
+ {
+ const int *const goto_index = &action_index [GOTO_INDEX_OFFSET];
+ const int *const goto_check = &action_check [GOTO_CHECK_OFFSET];
+
+ const int yyn = goto_index [state] + nt;
+
+ if (yyn < 0 || goto_check [yyn] != nt)
+ return goto_default [nt];
+
+ const int *const goto_info = &action_info [GOTO_INFO_OFFSET];
+ return goto_info [yyn];
+ }
+
+ static inline int t_action (int state, int token)
+ {
+ const int yyn = action_index [state] + token;
+
+ if (yyn < 0 || action_check [yyn] != token)
+ return - action_default [state];
+
+ return action_info [yyn];
+ }
+};
+
+
+const char *const QScriptGrammar::spell [] = {
+ "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue",
+ "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
+ "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier",
+ "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=",
+ "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=",
+ "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return",
+ ")", ";", 0, "*", "*=", "string literal", "switch", "this", "throw", "~",
+ "try", "typeof", "var", "void", "while", "with", "^", "^=", "null", "true",
+ "false", "const", "debugger", "reserved word"};
+
+const int QScriptGrammar::lhs [] = {
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 87, 87, 91, 91, 86, 86,
+ 92, 92, 93, 93, 93, 93, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 95, 95, 96,
+ 96, 96, 96, 96, 99, 99, 100, 100, 100, 100,
+ 98, 98, 101, 101, 102, 102, 103, 103, 103, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 105,
+ 105, 105, 105, 106, 106, 106, 107, 107, 107, 107,
+ 108, 108, 108, 108, 108, 108, 108, 109, 109, 109,
+ 109, 109, 109, 110, 110, 110, 110, 110, 111, 111,
+ 111, 111, 111, 112, 112, 113, 113, 114, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119, 119, 120,
+ 120, 121, 121, 122, 122, 123, 123, 90, 90, 124,
+ 124, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 89, 89, 126, 126, 127, 127, 128,
+ 128, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+ 129, 129, 129, 129, 129, 129, 130, 146, 146, 145,
+ 145, 131, 131, 147, 147, 148, 148, 150, 150, 149,
+ 151, 154, 152, 152, 155, 153, 153, 132, 133, 133,
+ 134, 134, 135, 135, 135, 135, 135, 135, 135, 136,
+ 136, 136, 136, 137, 137, 137, 137, 138, 138, 139,
+ 141, 156, 156, 159, 159, 157, 157, 160, 158, 140,
+ 142, 142, 143, 143, 143, 161, 162, 144, 163, 97,
+ 167, 167, 164, 164, 165, 165, 168, 84, 169, 169,
+ 170, 170, 166, 166, 88, 88, 171};
+
+const int QScriptGrammar:: rhs[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ 3, 5, 3, 3, 2, 4, 1, 2, 0, 1,
+ 3, 5, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 3, 3, 1, 2, 2, 2, 4, 3,
+ 2, 3, 1, 3, 1, 1, 1, 2, 2, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
+ 3, 3, 3, 1, 3, 3, 1, 3, 3, 3,
+ 1, 3, 3, 3, 3, 3, 3, 1, 3, 3,
+ 3, 3, 3, 1, 3, 3, 3, 3, 1, 3,
+ 3, 3, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 3, 1, 5, 1, 5, 1, 3, 1,
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 0, 1, 1, 3, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 1, 2, 0,
+ 1, 3, 3, 1, 1, 1, 3, 1, 3, 2,
+ 2, 2, 0, 1, 2, 0, 1, 1, 2, 2,
+ 7, 5, 7, 7, 5, 9, 10, 7, 8, 2,
+ 2, 3, 3, 2, 2, 3, 3, 3, 3, 5,
+ 5, 3, 5, 1, 2, 0, 1, 4, 3, 3,
+ 3, 3, 3, 3, 4, 5, 2, 1, 8, 8,
+ 1, 3, 0, 1, 0, 1, 1, 1, 1, 2,
+ 1, 1, 0, 1, 0, 1, 2};
+
+const int QScriptGrammar::action_default [] = {
+ 0, 97, 164, 128, 136, 132, 172, 179, 76, 148,
+ 178, 186, 174, 124, 0, 175, 262, 61, 176, 177,
+ 182, 77, 140, 144, 65, 94, 75, 80, 60, 0,
+ 114, 180, 101, 259, 258, 261, 183, 0, 194, 0,
+ 248, 0, 8, 9, 0, 5, 0, 263, 2, 0,
+ 265, 19, 0, 0, 0, 0, 0, 3, 6, 0,
+ 0, 166, 208, 7, 0, 1, 0, 0, 4, 0,
+ 0, 195, 0, 0, 0, 184, 185, 90, 0, 173,
+ 181, 0, 0, 77, 96, 263, 2, 265, 79, 78,
+ 0, 0, 0, 92, 93, 91, 0, 264, 253, 254,
+ 0, 251, 0, 252, 0, 255, 256, 0, 257, 250,
+ 260, 0, 266, 0, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 23,
+ 41, 42, 43, 44, 45, 25, 46, 47, 24, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 0,
+ 21, 0, 0, 0, 22, 13, 95, 0, 125, 0,
+ 0, 0, 0, 115, 0, 0, 0, 0, 0, 0,
+ 105, 0, 0, 0, 99, 100, 98, 103, 107, 106,
+ 104, 102, 117, 116, 118, 0, 133, 0, 129, 68,
+ 0, 0, 0, 70, 59, 58, 0, 0, 69, 165,
+ 0, 73, 71, 0, 72, 74, 209, 210, 0, 161,
+ 154, 152, 159, 160, 158, 157, 163, 156, 155, 153,
+ 162, 149, 0, 137, 0, 0, 141, 0, 0, 145,
+ 67, 0, 0, 63, 0, 62, 267, 224, 0, 225,
+ 226, 227, 220, 0, 221, 222, 223, 81, 0, 0,
+ 0, 0, 0, 213, 214, 170, 168, 130, 138, 134,
+ 150, 126, 171, 0, 77, 142, 146, 119, 108, 0,
+ 0, 127, 0, 0, 0, 0, 120, 0, 0, 0,
+ 0, 0, 112, 110, 113, 111, 109, 122, 121, 123,
+ 0, 135, 0, 131, 0, 169, 77, 0, 151, 166,
+ 167, 0, 166, 0, 0, 216, 0, 0, 0, 218,
+ 0, 139, 0, 0, 143, 0, 0, 147, 206, 0,
+ 198, 207, 201, 0, 205, 0, 166, 199, 0, 166,
+ 0, 0, 217, 0, 0, 0, 219, 264, 253, 0,
+ 0, 255, 0, 249, 0, 240, 0, 0, 0, 212,
+ 0, 211, 188, 191, 0, 27, 30, 31, 248, 34,
+ 35, 5, 39, 40, 2, 41, 44, 3, 6, 166,
+ 7, 48, 1, 50, 4, 52, 53, 54, 55, 56,
+ 57, 189, 187, 65, 66, 64, 0, 228, 229, 0,
+ 0, 0, 231, 236, 234, 237, 0, 0, 235, 236,
+ 0, 232, 0, 233, 190, 239, 0, 190, 238, 0,
+ 241, 242, 0, 190, 243, 244, 0, 0, 245, 0,
+ 0, 0, 246, 247, 83, 82, 0, 0, 0, 215,
+ 0, 0, 0, 230, 0, 20, 0, 17, 19, 11,
+ 0, 16, 12, 18, 15, 10, 0, 14, 87, 85,
+ 89, 86, 84, 88, 203, 196, 0, 204, 200, 0,
+ 202, 192, 0, 193, 197};
+
+const int QScriptGrammar::goto_default [] = {
+ 29, 28, 436, 434, 113, 14, 2, 435, 112, 111,
+ 114, 193, 24, 17, 189, 26, 8, 200, 21, 27,
+ 77, 25, 1, 32, 30, 267, 13, 261, 3, 257,
+ 5, 259, 4, 258, 22, 265, 23, 266, 9, 260,
+ 256, 297, 386, 262, 263, 35, 6, 79, 12, 15,
+ 18, 19, 10, 7, 31, 80, 20, 36, 75, 76,
+ 11, 354, 353, 78, 456, 455, 319, 320, 458, 322,
+ 457, 321, 392, 396, 399, 395, 394, 414, 415, 16,
+ 100, 107, 96, 99, 106, 108, 33, 0};
+
+const int QScriptGrammar::action_index [] = {
+ 1210, 59, -84, 71, 41, -1, -84, -84, 148, -84,
+ -84, -84, -84, 201, 130, -84, -84, -84, -84, -84,
+ -84, 343, 67, 62, 122, 109, -84, -84, -84, 85,
+ 273, -84, 184, -84, 1210, -84, -84, 119, -84, 112,
+ -84, 521, -84, -84, 1130, -84, 45, 54, 58, 38,
+ 1290, 50, 521, 521, 521, 376, 521, -84, -84, 521,
+ 521, 521, -84, -84, 25, -84, 521, 521, -84, 43,
+ 521, -84, 521, 18, 15, -84, -84, -84, 24, -84,
+ -84, 521, 521, 64, 153, 27, -84, 1050, -84, -84,
+ 521, 521, 521, -84, -84, -84, 28, -84, 37, 55,
+ 19, -84, 33, -84, 34, 1210, -84, 16, 1210, -84,
+ -84, 39, 52, -3, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, 521,
+ -84, 1050, 125, 521, -84, -84, 155, 521, 189, 521,
+ 521, 521, 521, 248, 521, 521, 521, 521, 521, 521,
+ 243, 521, 521, 521, 75, 82, 94, 177, 184, 184,
+ 184, 184, 263, 283, 298, 521, 44, 521, 77, -84,
+ 970, 521, 817, -84, -84, -84, 95, 521, -84, -84,
+ 93, -84, -84, 521, -84, -84, -84, -84, 521, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, 521, 41, 521, 521, 68, 66, 521, -84,
+ -84, 970, 521, -84, 103, -84, -84, -84, 63, -84,
+ -84, -84, -84, 69, -84, -84, -84, -84, -27, 12,
+ 521, 92, 100, -84, -84, 890, -84, 31, -13, -45,
+ -84, 210, 32, -28, 387, 20, 73, 304, 117, -5,
+ 521, 212, 521, 521, 521, 521, 213, 521, 521, 521,
+ 521, 521, 151, 150, 176, 158, 168, 304, 304, 228,
+ 521, -72, 521, 4, 521, -84, 306, 521, -84, 521,
+ 8, -50, 521, -48, 1130, -84, 521, 80, 1130, -84,
+ 521, -33, 521, 521, 5, 48, 521, -84, 17, 88,
+ 11, -84, -84, 521, -84, -29, 521, -84, -41, 521,
+ -39, 1130, -84, 521, 87, 1130, -84, -8, -2, -35,
+ 10, 1210, -16, -84, 1130, -84, 521, 86, 1130, -14,
+ 1130, -84, -84, 1130, -36, 107, -21, 165, 3, 521,
+ 1130, 6, 14, 61, 7, -19, 448, -4, -6, 671,
+ 29, 13, 23, 521, 30, -10, 521, 9, 521, -30,
+ -18, -84, -84, 164, -84, -84, 46, -84, -84, 521,
+ 111, -24, -84, 36, -84, 40, 99, 521, -84, 21,
+ 22, -84, -11, -84, 1130, -84, 106, 1130, -84, 178,
+ -84, -84, 98, 1130, 57, -84, 56, 60, -84, 51,
+ 26, 35, -84, -84, -84, -84, 521, 97, 1130, -84,
+ 521, 90, 1130, -84, 79, 76, 744, -84, 49, -84,
+ 594, -84, -84, -84, -84, -84, 83, -84, -84, -84,
+ -84, -84, -84, -84, 42, -84, 162, -84, -84, 521,
+ -84, -84, 53, -84, -84,
+
+ -61, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -4, -88, -88, 22, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -51, -88, -88, -88, -88, -88,
+ -88, 105, -88, -88, -12, -88, -88, -88, -88, -88,
+ -7, -88, 35, 132, 62, 154, 79, -88, -88, 100,
+ 75, 36, -88, -88, -88, -88, 37, 70, -88, -1,
+ 86, -88, 92, -88, -88, -88, -88, -88, -88, -88,
+ -88, 90, 95, -88, -88, -88, -88, -88, -88, -88,
+ 87, 82, 74, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -47, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, 28,
+ -88, 20, -88, 19, -88, -88, -88, 39, -88, 42,
+ 43, 106, 61, -88, 63, 55, 52, 53, 91, 125,
+ -88, 120, 123, 118, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, 116, -88, 59, -88, -88,
+ 16, 18, 15, -88, -88, -88, -88, 21, -88, -88,
+ -88, -88, -88, 24, -88, -88, -88, -88, 38, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, 97, -88, 115, 25, -88, -88, 26, -88,
+ -88, 111, 14, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ 23, -88, -88, -88, -88, 108, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ 160, -88, 171, 163, 145, 179, -88, 135, 45, 41,
+ 66, 80, -88, -88, -88, -88, -88, -88, -88, -88,
+ 172, -88, 156, -88, 142, -88, -88, 144, -88, 122,
+ -88, -88, 114, -88, -23, -88, 48, -88, 29, -88,
+ 224, -88, 157, 175, -88, -88, 182, -88, -88, -88,
+ -88, -88, -88, 183, -88, -21, 134, -88, -88, 49,
+ -88, 3, -88, 44, -88, 2, -88, -88, -37, -88,
+ -88, -31, -88, -88, 10, -88, 47, -88, 17, -88,
+ 27, -88, -88, 13, -88, -88, -88, -88, -88, 117,
+ 6, -88, -88, -88, -88, -88, 154, -88, -88, 1,
+ -88, -88, -88, 7, -88, -35, 137, -88, 141, -88,
+ -88, -88, -88, -6, -88, -88, -88, -88, -88, 78,
+ -88, -88, -88, -88, -88, -69, -88, 11, -88, -59,
+ -88, -88, -88, -88, 83, -88, -88, 56, -88, -88,
+ -88, -88, -88, -40, -58, -88, -88, -29, -88, -88,
+ -88, -45, -88, -88, -88, -88, -3, -88, -42, -88,
+ -5, -88, -32, -88, -88, -88, 9, -88, 8, -88,
+ -2, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, 12,
+ -88, -88, -56, -88, -88};
+
+const int QScriptGrammar::action_info [] = {
+ 318, -25, 350, -45, 292, 270, 426, 310, -194, 393,
+ -32, 302, 304, -37, 344, 290, 197, 346, 430, 382,
+ 329, 331, 310, 413, 318, 340, 397, 101, 338, 404,
+ -49, 292, 270, 299, 323, 290, -24, -51, -195, 343,
+ 294, 397, 333, 341, 403, 397, 149, 249, 250, 389,
+ 255, 430, 155, 454, 426, 316, 97, 437, 437, 459,
+ 151, 389, 103, 102, 98, 344, 101, 105, 413, 222,
+ 222, 109, 157, 228, 346, 187, 413, 417, 157, 104,
+ 420, 255, 454, 337, 443, 236, 421, 438, 197, 185,
+ 97, 197, 419, 413, 197, 197, 325, -263, 197, 81,
+ 197, 203, 0, 197, 416, 197, 88, 388, 387, 400,
+ 82, 197, 224, 407, 197, 81, 225, 89, 417, 197,
+ 187, 90, 81, 312, 241, 240, 82, 313, 0, 0,
+ 246, 245, 153, 82, 81, 439, 238, 231, 197, 0,
+ 308, 243, 171, 447, 172, 82, 348, 335, 238, 326,
+ 432, 198, 252, 204, 401, 173, 232, 428, 192, 235,
+ 0, 254, 253, 190, 0, 90, 91, 90, 239, 237,
+ 462, 391, 92, 244, 242, 171, 171, 172, 172, 231,
+ 239, 237, 191, 171, 192, 172, 197, 0, 173, 173,
+ 0, 207, 206, 171, 243, 172, 173, 0, 232, 0,
+ 192, 171, 171, 172, 172, 0, 173, 159, 160, 171,
+ 91, 172, 91, 0, 173, 173, 92, 0, 92, 159,
+ 160, 0, 173, 463, 461, 0, 244, 242, 272, 273,
+ 272, 273, 0, 0, 161, 162, 277, 278, 0, 411,
+ 410, 0, 0, 0, 0, 279, 161, 162, 280, 0,
+ 281, 277, 278, 0, 0, 274, 275, 274, 275, 0,
+ 279, 0, 0, 280, 0, 281, 0, 0, 171, 0,
+ 172, 164, 165, 0, 0, 0, 0, 0, 0, 166,
+ 167, 173, 0, 168, 0, 169, 164, 165, 0, 0,
+ 0, 0, 0, 0, 166, 167, 164, 165, 168, 0,
+ 169, 0, 0, 0, 166, 167, 164, 165, 168, 209,
+ 169, 0, 0, 0, 166, 167, 0, 0, 168, 210,
+ 169, 164, 165, 211, 0, 0, 0, 277, 278, 166,
+ 167, 0, 212, 168, 213, 169, 279, 0, 0, 280,
+ 0, 281, 0, 0, 0, 214, 209, 215, 88, 0,
+ 0, 0, 0, 0, 0, 216, 210, 0, 217, 89,
+ 211, 0, 0, 0, 218, 0, 0, 0, 0, 212,
+ 219, 213, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 214, 220, 215, 88, 0, 0, 42, 43,
+ 209, 0, 216, 0, 0, 217, 89, 0, 85, 0,
+ 210, 218, 0, 0, 211, 86, 0, 219, 0, 87,
+ 51, 0, 52, 212, 0, 213, 0, 0, 306, 55,
+ 220, 0, 0, 58, 0, 0, 214, 0, 215, 88,
+ 0, 0, 0, 0, 0, 0, 216, 0, 0, 217,
+ 89, 63, 0, 65, 0, 218, 0, 0, 0, 0,
+ 0, 219, 0, 0, 57, 68, 45, 0, 0, 0,
+ 42, 43, 0, 0, 220, 0, 0, 0, 0, 0,
+ 85, 0, 0, 0, 0, 0, 0, 86, 0, 0,
+ 0, 87, 51, 0, 52, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 0, 58, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 63, 0, 65, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 57, 68, 45, 0,
+ 0, 0, 41, 42, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 87, 51, 0, 52, 0, 0,
+ 0, 53, 0, 54, 55, 56, 0, 0, 58, 0,
+ 0, 0, 59, 0, 60, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 63, 0, 65, 0,
+ 67, 0, 70, 0, 72, 0, 0, 0, 0, 57,
+ 68, 45, 0, 0, 0, 41, 42, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 0, 0, 0,
+ 0, 0, 0, 86, 0, 0, 0, 87, 51, 0,
+ 52, 0, 0, 0, 53, 0, 54, 55, 56, 0,
+ 0, 58, 0, 0, 0, 59, 0, 60, 0, 0,
+ 442, 0, 0, 0, 0, 0, 0, 0, 0, 63,
+ 0, 65, 0, 67, 0, 70, 0, 72, 0, 0,
+ 0, 0, 57, 68, 45, 0, 0, 0, -47, 0,
+ 0, 0, 41, 42, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 87, 51, 0, 52, 0, 0,
+ 0, 53, 0, 54, 55, 56, 0, 0, 58, 0,
+ 0, 0, 59, 0, 60, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 63, 0, 65, 0,
+ 67, 0, 70, 0, 72, 0, 0, 0, 0, 57,
+ 68, 45, 0, 0, 0, 41, 42, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 0, 0, 0,
+ 0, 0, 0, 86, 0, 0, 0, 87, 51, 0,
+ 52, 0, 0, 0, 53, 0, 54, 55, 56, 0,
+ 0, 58, 0, 0, 0, 59, 0, 60, 0, 0,
+ 445, 0, 0, 0, 0, 0, 0, 0, 0, 63,
+ 0, 65, 0, 67, 0, 70, 0, 72, 0, 0,
+ 0, 0, 57, 68, 45, 0, 0, 0, 41, 42,
+ 43, 0, 0, 0, 0, 0, 0, 0, 0, 85,
+ 0, 0, 0, 0, 0, 0, 86, 0, 0, 0,
+ 87, 51, 0, 52, 0, 0, 0, 53, 0, 54,
+ 55, 56, 0, 0, 58, 0, 0, 0, 59, 0,
+ 60, 0, 0, 0, 0, 0, 0, 202, 0, 0,
+ 0, 0, 63, 0, 65, 0, 67, 0, 70, 0,
+ 72, 0, 0, 0, 0, 57, 68, 45, 0, 0,
+ 0, 41, 42, 43, 0, 0, 0, 0, 0, 0,
+ 0, 0, 85, 0, 0, 0, 0, 0, 0, 86,
+ 0, 0, 0, 87, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 63, 0, 65, 0, 67,
+ 0, 70, 269, 72, 0, 0, 0, 0, 57, 68,
+ 45, 0, 0, 0, 115, 116, 117, 0, 0, 119,
+ 121, 122, 0, 0, 123, 0, 124, 0, 0, 0,
+ 126, 127, 128, 0, 0, 0, 0, 0, 0, 195,
+ 130, 131, 132, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 137,
+ 0, 0, 0, 0, 0, 0, 139, 140, 141, 0,
+ 143, 144, 145, 146, 147, 148, 0, 0, 134, 142,
+ 125, 118, 120, 136, 115, 116, 117, 0, 0, 119,
+ 121, 122, 0, 0, 123, 0, 124, 0, 0, 0,
+ 126, 127, 128, 0, 0, 0, 0, 0, 0, 129,
+ 130, 131, 132, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0, 0, 135, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 137,
+ 0, 0, 0, 0, 0, 138, 139, 140, 141, 0,
+ 143, 144, 145, 146, 147, 148, 0, 0, 134, 142,
+ 125, 118, 120, 136, 37, 0, 0, 0, 0, 39,
+ 0, 41, 42, 43, 44, 0, 0, 0, 0, 0,
+ 0, 46, 85, 0, 0, 0, 0, 0, 0, 48,
+ 49, 0, 0, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 61,
+ 0, 62, 0, 0, 0, 63, 64, 65, 66, 67,
+ 69, 70, 71, 72, 73, 74, 0, 0, 57, 68,
+ 45, 38, 40, 0, 37, 0, 0, 0, 0, 39,
+ 0, 41, 42, 43, 44, 0, 0, 0, 0, 0,
+ 0, 46, 47, 0, 0, 0, 0, 0, 0, 48,
+ 49, 0, 0, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 61,
+ 0, 62, 0, 0, 0, 63, 64, 65, 66, 67,
+ 69, 70, 71, 72, 73, 74, 0, 0, 57, 68,
+ 45, 38, 40, 0, 355, 116, 117, 0, 0, 357,
+ 121, 359, 42, 43, 360, 0, 124, 0, 0, 0,
+ 126, 362, 363, 0, 0, 0, 0, 0, 0, 364,
+ 365, 131, 132, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 366, 56, 0, 0, 368, 0, 0,
+ 0, 59, 0, 60, 0, -190, 0, 0, 0, 369,
+ 0, 62, 0, 0, 0, 370, 371, 372, 373, 67,
+ 375, 376, 377, 378, 379, 380, 0, 0, 367, 374,
+ 361, 356, 358, 136,
+
+ 431, 422, 427, 429, 441, 352, 300, 398, 385, 464,
+ 440, 412, 409, 433, 402, 444, 406, 423, 460, 234,
+ 418, 201, 305, 196, 34, 154, 194, 199, 251, 152,
+ 205, 227, 229, 248, 150, 110, 230, 208, 352, 110,
+ 446, 300, 409, 339, 221, 412, 327, 336, 332, 334,
+ 342, 248, 347, 307, 300, 345, 0, 83, 381, 83,
+ 83, 83, 349, 83, 284, 158, 163, 182, 283, 0,
+ 83, 83, 351, 83, 309, 178, 179, 83, 177, 83,
+ 83, 83, 449, 390, 83, 184, 170, 188, 83, 285,
+ 453, 330, 83, 83, 95, 452, 0, 83, 83, 450,
+ 83, 352, 94, 286, 83, 83, 424, 93, 83, 83,
+ 83, 84, 425, 83, 180, 83, 156, 408, 83, 300,
+ 451, 194, 233, 83, 83, 247, 264, 300, 352, 223,
+ 183, 268, 0, 83, 83, 83, 83, 247, 83, 300,
+ 176, 83, 174, 83, 405, 175, 186, 0, 181, 226,
+ 83, 0, 448, 83, 0, 83, 303, 424, 282, 83,
+ 296, 425, 296, 83, 301, 268, 383, 268, 268, 384,
+ 288, 0, 0, 0, 83, 83, 328, 0, 83, 268,
+ 268, 83, 295, 268, 298, 293, 268, 271, 287, 83,
+ 83, 0, 314, 296, 268, 268, 276, 83, 268, 0,
+ 296, 296, 268, 291, 289, 268, 268, 0, 0, 0,
+ 0, 0, 0, 0, 0, 315, 0, 0, 0, 0,
+ 0, 0, 317, 324, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 83, 0, 0, 0, 0, 268, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 311, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0};
+
+const int QScriptGrammar::action_check [] = {
+ 29, 7, 16, 7, 76, 1, 36, 2, 29, 33,
+ 7, 61, 60, 7, 7, 48, 8, 36, 36, 55,
+ 61, 60, 2, 33, 29, 60, 5, 29, 36, 7,
+ 7, 76, 1, 61, 17, 48, 7, 7, 29, 55,
+ 8, 5, 31, 33, 55, 5, 7, 74, 36, 36,
+ 36, 36, 55, 29, 36, 7, 29, 8, 8, 17,
+ 8, 36, 29, 8, 36, 7, 29, 33, 33, 2,
+ 2, 55, 1, 7, 36, 76, 33, 20, 1, 60,
+ 29, 36, 29, 29, 8, 0, 60, 8, 8, 48,
+ 29, 8, 36, 33, 8, 8, 8, 36, 8, 40,
+ 8, 8, -1, 8, 6, 8, 42, 61, 62, 10,
+ 51, 8, 50, 7, 8, 40, 54, 53, 20, 8,
+ 76, 12, 40, 50, 61, 62, 51, 54, -1, -1,
+ 61, 62, 7, 51, 40, 56, 29, 15, 8, -1,
+ 60, 29, 25, 60, 27, 51, 60, 60, 29, 61,
+ 60, 56, 60, 60, 55, 38, 34, 60, 36, 56,
+ -1, 61, 62, 15, -1, 12, 57, 12, 61, 62,
+ 8, 60, 63, 61, 62, 25, 25, 27, 27, 15,
+ 61, 62, 34, 25, 36, 27, 8, -1, 38, 38,
+ -1, 61, 62, 25, 29, 27, 38, -1, 34, -1,
+ 36, 25, 25, 27, 27, -1, 38, 18, 19, 25,
+ 57, 27, 57, -1, 38, 38, 63, -1, 63, 18,
+ 19, -1, 38, 61, 62, -1, 61, 62, 18, 19,
+ 18, 19, -1, -1, 45, 46, 23, 24, -1, 61,
+ 62, -1, -1, -1, -1, 32, 45, 46, 35, -1,
+ 37, 23, 24, -1, -1, 45, 46, 45, 46, -1,
+ 32, -1, -1, 35, -1, 37, -1, -1, 25, -1,
+ 27, 23, 24, -1, -1, -1, -1, -1, -1, 31,
+ 32, 38, -1, 35, -1, 37, 23, 24, -1, -1,
+ -1, -1, -1, -1, 31, 32, 23, 24, 35, -1,
+ 37, -1, -1, -1, 31, 32, 23, 24, 35, 3,
+ 37, -1, -1, -1, 31, 32, -1, -1, 35, 13,
+ 37, 23, 24, 17, -1, -1, -1, 23, 24, 31,
+ 32, -1, 26, 35, 28, 37, 32, -1, -1, 35,
+ -1, 37, -1, -1, -1, 39, 3, 41, 42, -1,
+ -1, -1, -1, -1, -1, 49, 13, -1, 52, 53,
+ 17, -1, -1, -1, 58, -1, -1, -1, -1, 26,
+ 64, 28, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 39, 77, 41, 42, -1, -1, 12, 13,
+ 3, -1, 49, -1, -1, 52, 53, -1, 22, -1,
+ 13, 58, -1, -1, 17, 29, -1, 64, -1, 33,
+ 34, -1, 36, 26, -1, 28, -1, -1, 31, 43,
+ 77, -1, -1, 47, -1, -1, 39, -1, 41, 42,
+ -1, -1, -1, -1, -1, -1, 49, -1, -1, 52,
+ 53, 65, -1, 67, -1, 58, -1, -1, -1, -1,
+ -1, 64, -1, -1, 78, 79, 80, -1, -1, -1,
+ 12, 13, -1, -1, 77, -1, -1, -1, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
+ -1, 33, 34, -1, 36, -1, -1, -1, -1, -1,
+ -1, 43, -1, -1, -1, 47, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 65, -1, 67, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 78, 79, 80, -1,
+ -1, -1, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, -1, 67, -1,
+ 69, -1, 71, -1, 73, -1, -1, -1, -1, 78,
+ 79, 80, -1, -1, -1, 11, 12, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, -1, 33, 34, -1,
+ 36, -1, -1, -1, 40, -1, 42, 43, 44, -1,
+ -1, 47, -1, -1, -1, 51, -1, 53, -1, -1,
+ 56, -1, -1, -1, -1, -1, -1, -1, -1, 65,
+ -1, 67, -1, 69, -1, 71, -1, 73, -1, -1,
+ -1, -1, 78, 79, 80, -1, -1, -1, 7, -1,
+ -1, -1, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, -1, 67, -1,
+ 69, -1, 71, -1, 73, -1, -1, -1, -1, 78,
+ 79, 80, -1, -1, -1, 11, 12, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, -1, 33, 34, -1,
+ 36, -1, -1, -1, 40, -1, 42, 43, 44, -1,
+ -1, 47, -1, -1, -1, 51, -1, 53, -1, -1,
+ 56, -1, -1, -1, -1, -1, -1, -1, -1, 65,
+ -1, 67, -1, 69, -1, 71, -1, 73, -1, -1,
+ -1, -1, 78, 79, 80, -1, -1, -1, 11, 12,
+ 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
+ -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
+ 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
+ 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
+ 53, -1, -1, -1, -1, -1, -1, 60, -1, -1,
+ -1, -1, 65, -1, 67, -1, 69, -1, 71, -1,
+ 73, -1, -1, -1, -1, 78, 79, 80, -1, -1,
+ -1, 11, 12, 13, -1, -1, -1, -1, -1, -1,
+ -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
+ -1, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 65, -1, 67, -1, 69,
+ -1, 71, 72, 73, -1, -1, -1, -1, 78, 79,
+ 80, -1, -1, -1, 4, 5, 6, -1, -1, 9,
+ 10, 11, -1, -1, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 43, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, 66, 67, 68, -1,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83, 4, 5, 6, -1, -1, 9,
+ 10, 11, -1, -1, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 43, -1, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, 65, 66, 67, 68, -1,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83, 4, -1, -1, -1, -1, 9,
+ -1, 11, 12, 13, 14, -1, -1, -1, -1, -1,
+ -1, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, -1, 4, -1, -1, -1, -1, 9,
+ -1, 11, 12, 13, 14, -1, -1, -1, -1, -1,
+ -1, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, -1, 4, 5, 6, -1, -1, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, 55, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83,
+
+ 5, 46, 5, 45, 6, 45, 5, 76, 14, 65,
+ 2, 46, 5, 45, 73, 6, 5, 46, 6, 5,
+ 78, 6, 45, 5, 85, 6, 10, 6, 5, 9,
+ 6, 6, 6, 45, 6, 86, 14, 41, 45, 86,
+ 5, 5, 5, 80, 6, 46, 67, 45, 45, 5,
+ 81, 45, 5, 5, 5, 45, -1, 18, 45, 18,
+ 18, 18, 45, 18, 23, 26, 24, 24, 23, -1,
+ 18, 18, 45, 18, 45, 23, 23, 18, 23, 18,
+ 18, 18, 20, 5, 18, 24, 23, 28, 18, 23,
+ 20, 42, 18, 18, 20, 20, -1, 18, 18, 20,
+ 18, 45, 20, 23, 18, 18, 20, 20, 18, 18,
+ 18, 21, 20, 18, 23, 18, 21, 61, 18, 5,
+ 20, 10, 11, 18, 18, 20, 18, 5, 45, 32,
+ 24, 23, -1, 18, 18, 18, 18, 20, 18, 5,
+ 22, 18, 22, 18, 61, 22, 30, -1, 23, 34,
+ 18, -1, 20, 18, -1, 18, 42, 20, 23, 18,
+ 18, 20, 18, 18, 42, 23, 12, 23, 23, 15,
+ 25, -1, -1, -1, 18, 18, 42, -1, 18, 23,
+ 23, 18, 40, 23, 40, 29, 23, 27, 25, 18,
+ 18, -1, 35, 18, 23, 23, 25, 18, 23, -1,
+ 18, 18, 23, 31, 25, 23, 23, -1, -1, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, 40, 40, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 18, -1, -1, -1, -1, 23, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 33, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1};
+
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE1 7
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE2 8
+
+#include "translator.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qvariant.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, bool plural, const QString &fileName, int lineNo)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ fileName, lineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ tor->replace(msg);
+}
+
+
+namespace QScript
+{
+
+class Lexer
+{
+public:
+ Lexer();
+ ~Lexer();
+
+ void setCode(const QString &c, int lineno);
+ int lex();
+
+ int currentLineNo() const { return yylineno; }
+ int currentColumnNo() const { return yycolumn; }
+
+ int startLineNo() const { return startlineno; }
+ int startColumnNo() const { return startcolumn; }
+
+ int endLineNo() const { return currentLineNo(); }
+ int endColumnNo() const
+ { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; }
+
+ bool prevTerminator() const { return terminator; }
+
+ enum State { Start,
+ Identifier,
+ InIdentifier,
+ InSingleLineComment,
+ InMultiLineComment,
+ InNum,
+ InNum0,
+ InHex,
+ InOctal,
+ InDecimal,
+ InExponentIndicator,
+ InExponent,
+ Hex,
+ Octal,
+ Number,
+ String,
+ Eof,
+ InString,
+ InEscapeSequence,
+ InHexEscape,
+ InUnicodeEscape,
+ Other,
+ Bad };
+
+ enum Error {
+ NoError,
+ IllegalCharacter,
+ UnclosedStringLiteral,
+ IllegalEscapeSequence,
+ IllegalUnicodeEscapeSequence,
+ UnclosedComment,
+ IllegalExponentIndicator,
+ IllegalIdentifier
+ };
+
+ enum ParenthesesState {
+ IgnoreParentheses,
+ CountParentheses,
+ BalancedParentheses
+ };
+
+ enum RegExpBodyPrefix {
+ NoPrefix,
+ EqualPrefix
+ };
+
+ bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
+
+ QString pattern;
+ int flags;
+
+ State lexerState() const
+ { return state; }
+
+ QString errorMessage() const
+ { return errmsg; }
+ void setErrorMessage(const QString &err)
+ { errmsg = err; }
+ void setErrorMessage(const char *err)
+ { setErrorMessage(QString::fromLatin1(err)); }
+
+ Error error() const
+ { return err; }
+ void clearError()
+ { err = NoError; }
+
+private:
+ int yylineno;
+ bool done;
+ char *buffer8;
+ QChar *buffer16;
+ uint size8, size16;
+ uint pos8, pos16;
+ bool terminator;
+ bool restrKeyword;
+ // encountered delimiter like "'" and "}" on last run
+ bool delimited;
+ int stackToken;
+
+ State state;
+ void setDone(State s);
+ uint pos;
+ void shift(uint p);
+ int lookupKeyword(const char *);
+
+ bool isWhiteSpace() const;
+ bool isLineTerminator() const;
+ bool isHexDigit(ushort c) const;
+ bool isOctalDigit(ushort c) const;
+
+ int matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ ushort singleEscape(ushort c) const;
+ ushort convertOctal(ushort c1, ushort c2,
+ ushort c3) const;
+public:
+ static unsigned char convertHex(ushort c1);
+ static unsigned char convertHex(ushort c1, ushort c2);
+ static QChar convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ static bool isIdentLetter(ushort c);
+ static bool isDecimalDigit(ushort c);
+
+ inline int ival() const { return qsyylval.toInt(); }
+ inline double dval() const { return qsyylval.toDouble(); }
+ inline QString ustr() const { return qsyylval.toString(); }
+ inline QVariant val() const { return qsyylval; }
+
+ const QChar *characterBuffer() const { return buffer16; }
+ int characterCount() const { return pos16; }
+
+private:
+ void record8(ushort c);
+ void record16(QChar c);
+ void recordStartPos();
+
+ int findReservedWord(const QChar *buffer, int size) const;
+
+ void syncProhibitAutomaticSemicolon();
+
+ const QChar *code;
+ uint length;
+ int yycolumn;
+ int startlineno;
+ int startcolumn;
+ int bol; // begin of line
+
+ QVariant qsyylval;
+
+ // current and following unicode characters
+ ushort current, next1, next2, next3;
+
+ struct keyword {
+ const char *name;
+ int token;
+ };
+
+ QString errmsg;
+ Error err;
+
+ bool wantRx;
+ bool check_reserved;
+
+ ParenthesesState parenthesesState;
+ int parenthesesCount;
+ bool prohibitAutomaticSemicolon;
+};
+
+} // namespace QScript
+
+extern double qstrtod(const char *s00, char const **se, bool *ok);
+
+#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1);
+
+namespace QScript {
+
+static int toDigit(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'a') && (c <= 'z'))
+ return 10 + c - 'a';
+ else if ((c >= 'A') && (c <= 'Z'))
+ return 10 + c - 'A';
+ return -1;
+}
+
+double integerFromString(const char *buf, int size, int radix)
+{
+ if (size == 0)
+ return qSNaN();
+
+ double sign = 1.0;
+ int i = 0;
+ if (buf[0] == '+') {
+ ++i;
+ } else if (buf[0] == '-') {
+ sign = -1.0;
+ ++i;
+ }
+
+ if (((size-i) >= 2) && (buf[i] == '0')) {
+ if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
+ && (radix < 34)) {
+ if ((radix != 0) && (radix != 16))
+ return 0;
+ radix = 16;
+ i += 2;
+ } else {
+ if (radix == 0) {
+ radix = 8;
+ ++i;
+ }
+ }
+ } else if (radix == 0) {
+ radix = 10;
+ }
+
+ int j = i;
+ for ( ; i < size; ++i) {
+ int d = toDigit(buf[i]);
+ if ((d == -1) || (d >= radix))
+ break;
+ }
+ double result;
+ if (j == i) {
+ if (!qstrcmp(buf, "Infinity"))
+ result = qInf();
+ else
+ result = qSNaN();
+ } else {
+ result = 0;
+ double multiplier = 1;
+ for (--i ; i >= j; --i, multiplier *= radix)
+ result += toDigit(buf[i]) * multiplier;
+ }
+ result *= sign;
+ return result;
+}
+
+} // namespace QScript
+
+QScript::Lexer::Lexer()
+ :
+ yylineno(0),
+ size8(128), size16(128), restrKeyword(false),
+ stackToken(-1), pos(0),
+ code(0), length(0),
+ bol(true),
+ current(0), next1(0), next2(0), next3(0),
+ err(NoError),
+ check_reserved(true),
+ parenthesesState(IgnoreParentheses),
+ prohibitAutomaticSemicolon(false)
+{
+ // allocate space for read buffers
+ buffer8 = new char[size8];
+ buffer16 = new QChar[size16];
+ flags = 0;
+
+}
+
+QScript::Lexer::~Lexer()
+{
+ delete [] buffer8;
+ delete [] buffer16;
+}
+
+void QScript::Lexer::setCode(const QString &c, int lineno)
+{
+ errmsg = QString();
+ yylineno = lineno;
+ yycolumn = 1;
+ restrKeyword = false;
+ delimited = false;
+ stackToken = -1;
+ pos = 0;
+ code = c.unicode();
+ length = c.length();
+ bol = true;
+
+ // read first characters
+ current = (length > 0) ? code[0].unicode() : 0;
+ next1 = (length > 1) ? code[1].unicode() : 0;
+ next2 = (length > 2) ? code[2].unicode() : 0;
+ next3 = (length > 3) ? code[3].unicode() : 0;
+}
+
+void QScript::Lexer::shift(uint p)
+{
+ while (p--) {
+ ++pos;
+ ++yycolumn;
+ current = next1;
+ next1 = next2;
+ next2 = next3;
+ next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0;
+ }
+}
+
+void QScript::Lexer::setDone(State s)
+{
+ state = s;
+ done = true;
+}
+
+int QScript::Lexer::findReservedWord(const QChar *c, int size) const
+{
+ switch (size) {
+ case 2: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o'))
+ return QScriptGrammar::T_DO;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f'))
+ return QScriptGrammar::T_IF;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n'))
+ return QScriptGrammar::T_IN;
+ } break;
+
+ case 3: {
+ if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_FOR;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w'))
+ return QScriptGrammar::T_NEW;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y'))
+ return QScriptGrammar::T_TRY;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_VAR;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 4: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_CASE;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_ELSE;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s'))
+ return QScriptGrammar::T_THIS;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d'))
+ return QScriptGrammar::T_VOID;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h'))
+ return QScriptGrammar::T_WITH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_TRUE;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l'))
+ return QScriptGrammar::T_NULL;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 5: {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('k'))
+ return QScriptGrammar::T_BREAK;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h'))
+ return QScriptGrammar::T_CATCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w'))
+ return QScriptGrammar::T_THROW;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_WHILE;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_CONST;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_FALSE;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 6: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_DELETE;
+ else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n'))
+ return QScriptGrammar::T_RETURN;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h'))
+ return QScriptGrammar::T_SWITCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f'))
+ return QScriptGrammar::T_TYPEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i')
+ && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 7: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('t'))
+ return QScriptGrammar::T_DEFAULT;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('y'))
+ return QScriptGrammar::T_FINALLY;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('n'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d')
+ && c[6] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 8: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_CONTINUE;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n'))
+ return QScriptGrammar::T_FUNCTION;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r'))
+ return QScriptGrammar::T_DEBUGGER;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 9: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f')
+ && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c')
+ && c[8] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n')
+ && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c')
+ && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 10: {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f'))
+ return QScriptGrammar::T_INSTANCEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 12: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z')
+ && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ } // switch
+
+ return -1;
+}
+
+int QScript::Lexer::lex()
+{
+ int token = 0;
+ state = Start;
+ ushort stringType = 0; // either single or double quotes
+ pos8 = pos16 = 0;
+ done = false;
+ terminator = false;
+
+ // did we push a token on the stack previously ?
+ // (after an automatic semicolon insertion)
+ if (stackToken >= 0) {
+ setDone(Other);
+ token = stackToken;
+ stackToken = -1;
+ }
+
+ while (!done) {
+ switch (state) {
+ case Start:
+ if (isWhiteSpace()) {
+ // do nothing
+ } else if (current == '/' && next1 == '/') {
+ recordStartPos();
+ shift(1);
+ state = InSingleLineComment;
+ } else if (current == '/' && next1 == '*') {
+ recordStartPos();
+ shift(1);
+ state = InMultiLineComment;
+ } else if (current == 0) {
+ syncProhibitAutomaticSemicolon();
+ if (!terminator && !delimited && !prohibitAutomaticSemicolon) {
+ // automatic semicolon insertion if program incomplete
+ token = QScriptGrammar::T_SEMICOLON;
+ stackToken = 0;
+ setDone(Other);
+ } else {
+ setDone(Eof);
+ }
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ bol = true;
+ terminator = true;
+ syncProhibitAutomaticSemicolon();
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ }
+ } else if (current == '"' || current == '\'') {
+ recordStartPos();
+ state = InString;
+ stringType = current;
+ } else if (isIdentLetter(current)) {
+ recordStartPos();
+ record16(current);
+ state = InIdentifier;
+ } else if (current == '0') {
+ recordStartPos();
+ record8(current);
+ state = InNum0;
+ } else if (isDecimalDigit(current)) {
+ recordStartPos();
+ record8(current);
+ state = InNum;
+ } else if (current == '.' && isDecimalDigit(next1)) {
+ recordStartPos();
+ record8(current);
+ state = InDecimal;
+ } else {
+ recordStartPos();
+ token = matchPunctuator(current, next1, next2, next3);
+ if (token != -1) {
+ if (terminator && !delimited && !prohibitAutomaticSemicolon
+ && (token == QScriptGrammar::T_PLUS_PLUS
+ || token == QScriptGrammar::T_MINUS_MINUS)) {
+ // automatic semicolon insertion
+ stackToken = token;
+ token = QScriptGrammar::T_SEMICOLON;
+ }
+ setDone(Other);
+ }
+ else {
+ setDone(Bad);
+ err = IllegalCharacter;
+ errmsg = QLatin1String("Illegal character");
+ }
+ }
+ break;
+ case InString:
+ if (current == stringType) {
+ shift(1);
+ setDone(String);
+ } else if (current == 0 || isLineTerminator()) {
+ setDone(Bad);
+ err = UnclosedStringLiteral;
+ errmsg = QLatin1String("Unclosed string at end of line");
+ } else if (current == '\\') {
+ state = InEscapeSequence;
+ } else {
+ record16(current);
+ }
+ break;
+ // Escape Sequences inside of strings
+ case InEscapeSequence:
+ if (isOctalDigit(current)) {
+ if (current >= '0' && current <= '3' &&
+ isOctalDigit(next1) && isOctalDigit(next2)) {
+ record16(convertOctal(current, next1, next2));
+ shift(2);
+ state = InString;
+ } else if (isOctalDigit(current) &&
+ isOctalDigit(next1)) {
+ record16(convertOctal('0', current, next1));
+ shift(1);
+ state = InString;
+ } else if (isOctalDigit(current)) {
+ record16(convertOctal('0', '0', current));
+ state = InString;
+ } else {
+ setDone(Bad);
+ err = IllegalEscapeSequence;
+ errmsg = QLatin1String("Illegal escape squence");
+ }
+ } else if (current == 'x')
+ state = InHexEscape;
+ else if (current == 'u')
+ state = InUnicodeEscape;
+ else {
+ record16(singleEscape(current));
+ state = InString;
+ }
+ break;
+ case InHexEscape:
+ if (isHexDigit(current) && isHexDigit(next1)) {
+ state = InString;
+ record16(QLatin1Char(convertHex(current, next1)));
+ shift(1);
+ } else if (current == stringType) {
+ record16(QLatin1Char('x'));
+ shift(1);
+ setDone(String);
+ } else {
+ record16(QLatin1Char('x'));
+ record16(current);
+ state = InString;
+ }
+ break;
+ case InUnicodeEscape:
+ if (isHexDigit(current) && isHexDigit(next1) &&
+ isHexDigit(next2) && isHexDigit(next3)) {
+ record16(convertUnicode(current, next1, next2, next3));
+ shift(3);
+ state = InString;
+ } else if (current == stringType) {
+ record16(QLatin1Char('u'));
+ shift(1);
+ setDone(String);
+ } else {
+ setDone(Bad);
+ err = IllegalUnicodeEscapeSequence;
+ errmsg = QLatin1String("Illegal unicode escape sequence");
+ }
+ break;
+ case InSingleLineComment:
+ if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ terminator = true;
+ bol = true;
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ } else
+ state = Start;
+ } else if (current == 0) {
+ setDone(Eof);
+ }
+ break;
+ case InMultiLineComment:
+ if (current == 0) {
+ setDone(Bad);
+ err = UnclosedComment;
+ errmsg = QLatin1String("Unclosed comment at end of file");
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ } else if (current == '*' && next1 == '/') {
+ state = Start;
+ shift(1);
+ }
+ break;
+ case InIdentifier:
+ if (isIdentLetter(current) || isDecimalDigit(current)) {
+ record16(current);
+ break;
+ }
+ setDone(Identifier);
+ break;
+ case InNum0:
+ if (current == 'x' || current == 'X') {
+ record8(current);
+ state = InHex;
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else if (isOctalDigit(current)) {
+ record8(current);
+ state = InOctal;
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InHex:
+ if (isHexDigit(current))
+ record8(current);
+ else
+ setDone(Hex);
+ break;
+ case InOctal:
+ if (isOctalDigit(current)) {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Octal);
+ }
+ break;
+ case InNum:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InDecimal:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InExponentIndicator:
+ if (current == '+' || current == '-') {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InExponent;
+ } else {
+ setDone(Bad);
+ err = IllegalExponentIndicator;
+ errmsg = QLatin1String("Illegal syntax for exponential number");
+ }
+ break;
+ case InExponent:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else {
+ setDone(Number);
+ }
+ break;
+ default:
+ Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement");
+ }
+
+ // move on to the next character
+ if (!done)
+ shift(1);
+ if (state != Start && state != InSingleLineComment)
+ bol = false;
+ }
+
+ // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
+ if ((state == Number || state == Octal || state == Hex)
+ && isIdentLetter(current)) {
+ state = Bad;
+ err = IllegalIdentifier;
+ errmsg = QLatin1String("Identifier cannot start with numeric literal");
+ }
+
+ // terminate string
+ buffer8[pos8] = '\0';
+
+ double dval = 0;
+ if (state == Number) {
+ dval = qstrtod(buffer8, 0, 0);
+ } else if (state == Hex) { // scan hex numbers
+ dval = QScript::integerFromString(buffer8, pos8, 16);
+ state = Number;
+ } else if (state == Octal) { // scan octal number
+ dval = QScript::integerFromString(buffer8, pos8, 8);
+ state = Number;
+ }
+
+ restrKeyword = false;
+ delimited = false;
+
+ switch (parenthesesState) {
+ case IgnoreParentheses:
+ break;
+ case CountParentheses:
+ if (token == QScriptGrammar::T_RPAREN) {
+ --parenthesesCount;
+ if (parenthesesCount == 0)
+ parenthesesState = BalancedParentheses;
+ } else if (token == QScriptGrammar::T_LPAREN) {
+ ++parenthesesCount;
+ }
+ break;
+ case BalancedParentheses:
+ parenthesesState = IgnoreParentheses;
+ break;
+ }
+
+ switch (state) {
+ case Eof:
+ return 0;
+ case Other:
+ if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON)
+ delimited = true;
+ return token;
+ case Identifier:
+ if ((token = findReservedWord(buffer16, pos16)) < 0) {
+ /* TODO: close leak on parse error. same holds true for String */
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_IDENTIFIER;
+ }
+ if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK
+ || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) {
+ restrKeyword = true;
+ } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR
+ || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) {
+ parenthesesState = CountParentheses;
+ parenthesesCount = 0;
+ } else if (token == QScriptGrammar::T_DO) {
+ parenthesesState = BalancedParentheses;
+ }
+ return token;
+ case String:
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_STRING_LITERAL;
+ case Number:
+ qsyylval = dval;
+ return QScriptGrammar::T_NUMERIC_LITERAL;
+ case Bad:
+ return -1;
+ default:
+ Q_ASSERT(!"unhandled numeration value in switch");
+ return -1;
+ }
+}
+
+bool QScript::Lexer::isWhiteSpace() const
+{
+ return (current == ' ' || current == '\t' ||
+ current == 0x0b || current == 0x0c);
+}
+
+bool QScript::Lexer::isLineTerminator() const
+{
+ return (current == '\n' || current == '\r');
+}
+
+bool QScript::Lexer::isIdentLetter(ushort c)
+{
+ /* TODO: allow other legitimate unicode chars */
+ return ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '$'
+ || c == '_');
+}
+
+bool QScript::Lexer::isDecimalDigit(ushort c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool QScript::Lexer::isHexDigit(ushort c) const
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+bool QScript::Lexer::isOctalDigit(ushort c) const
+{
+ return (c >= '0' && c <= '7');
+}
+
+int QScript::Lexer::matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
+ shift(4);
+ return QScriptGrammar::T_GT_GT_GT_EQ;
+ } else if (c1 == '=' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_EQ_EQ_EQ;
+ } else if (c1 == '!' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_NOT_EQ_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '>') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_GT;
+ } else if (c1 == '<' && c2 == '<' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_LT_LT_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_EQ;
+ } else if (c1 == '<' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_LE;
+ } else if (c1 == '>' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_GE;
+ } else if (c1 == '!' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_NOT_EQ;
+ } else if (c1 == '+' && c2 == '+') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_PLUS;
+ } else if (c1 == '-' && c2 == '-') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_MINUS;
+ } else if (c1 == '=' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_EQ_EQ;
+ } else if (c1 == '+' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_EQ;
+ } else if (c1 == '-' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_EQ;
+ } else if (c1 == '*' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_STAR_EQ;
+ } else if (c1 == '/' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_DIVIDE_EQ;
+ } else if (c1 == '&' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_AND_EQ;
+ } else if (c1 == '^' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_XOR_EQ;
+ } else if (c1 == '%' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_REMAINDER_EQ;
+ } else if (c1 == '|' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_OR_EQ;
+ } else if (c1 == '<' && c2 == '<') {
+ shift(2);
+ return QScriptGrammar::T_LT_LT;
+ } else if (c1 == '>' && c2 == '>') {
+ shift(2);
+ return QScriptGrammar::T_GT_GT;
+ } else if (c1 == '&' && c2 == '&') {
+ shift(2);
+ return QScriptGrammar::T_AND_AND;
+ } else if (c1 == '|' && c2 == '|') {
+ shift(2);
+ return QScriptGrammar::T_OR_OR;
+ }
+
+ switch(c1) {
+ case '=': shift(1); return QScriptGrammar::T_EQ;
+ case '>': shift(1); return QScriptGrammar::T_GT;
+ case '<': shift(1); return QScriptGrammar::T_LT;
+ case ',': shift(1); return QScriptGrammar::T_COMMA;
+ case '!': shift(1); return QScriptGrammar::T_NOT;
+ case '~': shift(1); return QScriptGrammar::T_TILDE;
+ case '?': shift(1); return QScriptGrammar::T_QUESTION;
+ case ':': shift(1); return QScriptGrammar::T_COLON;
+ case '.': shift(1); return QScriptGrammar::T_DOT;
+ case '+': shift(1); return QScriptGrammar::T_PLUS;
+ case '-': shift(1); return QScriptGrammar::T_MINUS;
+ case '*': shift(1); return QScriptGrammar::T_STAR;
+ case '/': shift(1); return QScriptGrammar::T_DIVIDE_;
+ case '&': shift(1); return QScriptGrammar::T_AND;
+ case '|': shift(1); return QScriptGrammar::T_OR;
+ case '^': shift(1); return QScriptGrammar::T_XOR;
+ case '%': shift(1); return QScriptGrammar::T_REMAINDER;
+ case '(': shift(1); return QScriptGrammar::T_LPAREN;
+ case ')': shift(1); return QScriptGrammar::T_RPAREN;
+ case '{': shift(1); return QScriptGrammar::T_LBRACE;
+ case '}': shift(1); return QScriptGrammar::T_RBRACE;
+ case '[': shift(1); return QScriptGrammar::T_LBRACKET;
+ case ']': shift(1); return QScriptGrammar::T_RBRACKET;
+ case ';': shift(1); return QScriptGrammar::T_SEMICOLON;
+
+ default: return -1;
+ }
+}
+
+ushort QScript::Lexer::singleEscape(ushort c) const
+{
+ switch(c) {
+ case 'b':
+ return 0x08;
+ case 't':
+ return 0x09;
+ case 'n':
+ return 0x0A;
+ case 'v':
+ return 0x0B;
+ case 'f':
+ return 0x0C;
+ case 'r':
+ return 0x0D;
+ case '"':
+ return 0x22;
+ case '\'':
+ return 0x27;
+ case '\\':
+ return 0x5C;
+ default:
+ return c;
+ }
+}
+
+ushort QScript::Lexer::convertOctal(ushort c1, ushort c2,
+ ushort c3) const
+{
+ return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2)
+{
+ return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ return QChar((convertHex(c3) << 4) + convertHex(c4),
+ (convertHex(c1) << 4) + convertHex(c2));
+}
+
+void QScript::Lexer::record8(ushort c)
+{
+ Q_ASSERT(c <= 0xff);
+
+ // enlarge buffer if full
+ if (pos8 >= size8 - 1) {
+ char *tmp = new char[2 * size8];
+ memcpy(tmp, buffer8, size8 * sizeof(char));
+ delete [] buffer8;
+ buffer8 = tmp;
+ size8 *= 2;
+ }
+
+ buffer8[pos8++] = (char) c;
+}
+
+void QScript::Lexer::record16(QChar c)
+{
+ // enlarge buffer if full
+ if (pos16 >= size16 - 1) {
+ QChar *tmp = new QChar[2 * size16];
+ memcpy(tmp, buffer16, size16 * sizeof(QChar));
+ delete [] buffer16;
+ buffer16 = tmp;
+ size16 *= 2;
+ }
+
+ buffer16[pos16++] = c;
+}
+
+void QScript::Lexer::recordStartPos()
+{
+ startlineno = yylineno;
+ startcolumn = yycolumn;
+}
+
+bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix)
+{
+ pos16 = 0;
+ bool lastWasEscape = false;
+
+ if (prefix == EqualPrefix)
+ record16(QLatin1Char('='));
+
+ while (1) {
+ if (isLineTerminator() || current == 0) {
+ errmsg = QLatin1String("Unterminated regular expression literal");
+ return false;
+ }
+ else if (current != '/' || lastWasEscape == true)
+ {
+ record16(current);
+ lastWasEscape = !lastWasEscape && (current == '\\');
+ }
+ else {
+ pattern = QString(buffer16, pos16);
+ pos16 = 0;
+ shift(1);
+ break;
+ }
+ shift(1);
+ }
+
+ flags = 0;
+ while (isIdentLetter(current)) {
+ record16(current);
+ shift(1);
+ }
+
+ return true;
+}
+
+void QScript::Lexer::syncProhibitAutomaticSemicolon()
+{
+ if (parenthesesState == BalancedParentheses) {
+ // we have seen something like "if (foo)", which means we should
+ // never insert an automatic semicolon at this point, since it would
+ // then be expanded into an empty statement (ECMA-262 7.9.1)
+ prohibitAutomaticSemicolon = true;
+ parenthesesState = IgnoreParentheses;
+ } else {
+ prohibitAutomaticSemicolon = false;
+ }
+}
+
+
+class Translator;
+
+class QScriptParser: protected QScriptGrammar
+{
+public:
+ QVariant val;
+
+ struct Location {
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ };
+
+public:
+ QScriptParser();
+ ~QScriptParser();
+
+ bool parse(QScript::Lexer *lexer,
+ const QString &fileName,
+ Translator *translator);
+
+ inline QString errorMessage() const
+ { return error_message; }
+ inline int errorLineNumber() const
+ { return error_lineno; }
+ inline int errorColumnNumber() const
+ { return error_column; }
+
+protected:
+ inline void reallocateStack();
+
+ inline QVariant &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline Location &loc(int index)
+ { return location_stack [tos + index - 2]; }
+
+protected:
+ int tos;
+ int stack_size;
+ QVector<QVariant> sym_stack;
+ int *state_stack;
+ Location *location_stack;
+ QString error_message;
+ int error_lineno;
+ int error_column;
+};
+
+inline void QScriptParser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack.resize(stack_size);
+ state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location)));
+}
+
+inline static bool automatic(QScript::Lexer *lexer, int token)
+{
+ return (token == QScriptGrammar::T_RBRACE)
+ || (token == 0)
+ || lexer->prevTerminator();
+}
+
+QScriptParser::QScriptParser():
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0)
+{
+}
+
+QScriptParser::~QScriptParser()
+{
+ if (stack_size) {
+ qFree(state_stack);
+ qFree(location_stack);
+ }
+}
+
+static inline QScriptParser::Location location(QScript::Lexer *lexer)
+{
+ QScriptParser::Location loc;
+ loc.startLine = lexer->startLineNo();
+ loc.startColumn = lexer->startColumnNo();
+ loc.endLine = lexer->endLineNo();
+ loc.endColumn = lexer->endColumnNo();
+ return loc;
+}
+
+bool QScriptParser::parse(QScript::Lexer *lexer,
+ const QString &fileName,
+ Translator *translator)
+{
+ const int INITIAL_STATE = 0;
+
+ int yytoken = -1;
+ int saved_yytoken = -1;
+ int identLineNo = -1;
+
+ reallocateStack();
+
+ tos = 0;
+ state_stack[++tos] = INITIAL_STATE;
+
+ while (true)
+ {
+ const int state = state_stack [tos];
+ if (yytoken == -1 && - TERMINAL_COUNT != action_index [state])
+ {
+ if (saved_yytoken == -1)
+ {
+ yytoken = lexer->lex();
+ location_stack [tos] = location(lexer);
+ }
+ else
+ {
+ yytoken = saved_yytoken;
+ saved_yytoken = -1;
+ }
+ }
+
+ int act = t_action (state, yytoken);
+
+ if (act == ACCEPT_STATE)
+ return true;
+
+ else if (act > 0)
+ {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ sym_stack [tos] = lexer->val ();
+ state_stack [tos] = act;
+ location_stack [tos] = location(lexer);
+ yytoken = -1;
+ }
+
+ else if (act < 0)
+ {
+ int r = - act - 1;
+
+ tos -= rhs [r];
+ act = state_stack [tos++];
+
+ switch (r) {
+
+case 1: {
+ sym(1) = sym(1).toByteArray();
+ identLineNo = lexer->startLineNo();
+} break;
+
+case 7: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+
+case 8: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+
+case 66: {
+ QString name = sym(1).toString();
+ if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) {
+ QVariantList args = sym(2).toList();
+ if (args.size() < 2) {
+ qWarning("%s:%d: %s() requires at least two arguments",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ if ((args.at(0).type() != QVariant::String)
+ || (args.at(1).type() != QVariant::String)) {
+ qWarning("%s:%d: %s(): both arguments must be literal strings",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ QString context = args.at(0).toString();
+ QString text = args.at(1).toString();
+ QString comment = args.value(2).toString();
+ QString extracomment;
+ bool plural = (args.size() > 4);
+ recordMessage(translator, context, text, comment, extracomment,
+ plural, fileName, identLineNo);
+ }
+ }
+ } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) {
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ qWarning("%s:%d: %s() requires at least one argument",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ qWarning("%s:%d: %s(): text to translate must be a literal string",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ QString context = QFileInfo(fileName).baseName();
+ QString text = args.at(0).toString();
+ QString comment = args.value(1).toString();
+ QString extracomment;
+ bool plural = (args.size() > 2);
+ recordMessage(translator, context, text, comment, extracomment,
+ plural, fileName, identLineNo);
+ }
+ }
+ }
+} break;
+
+case 70: {
+ sym(1) = QVariantList();
+} break;
+
+case 71: {
+ sym(1) = sym(2);
+} break;
+
+case 72: {
+ sym(1) = QVariantList() << sym(1);
+} break;
+
+case 73: {
+ sym(1) = sym(1).toList() << sym(3);
+} break;
+
+case 94: {
+ if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String))
+ sym(1) = sym(1).toString() + sym(3).toString();
+ else
+ sym(1) = QVariant();
+} break;
+
+ } // switch
+
+ state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
+
+ if (rhs[r] > 1) {
+ location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine;
+ location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn;
+ location_stack[tos] = location_stack[tos + rhs[r] - 1];
+ }
+ }
+
+ else
+ {
+ if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0)
+ {
+ saved_yytoken = yytoken;
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ else if ((state == INITIAL_STATE) && (yytoken == 0)) {
+ // accept empty input
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ int ers = state;
+ int shifts = 0;
+ int reduces = 0;
+ int expected_tokens [3];
+ for (int tk = 0; tk < TERMINAL_COUNT; ++tk)
+ {
+ int k = t_action (ers, tk);
+
+ if (! k)
+ continue;
+ else if (k < 0)
+ ++reduces;
+ else if (spell [tk])
+ {
+ if (shifts < 3)
+ expected_tokens [shifts] = tk;
+ ++shifts;
+ }
+ }
+
+ error_message.clear ();
+ if (shifts && shifts < 3)
+ {
+ bool first = true;
+
+ for (int s = 0; s < shifts; ++s)
+ {
+ if (first)
+ error_message += QLatin1String ("Expected ");
+ else
+ error_message += QLatin1String (", ");
+
+ first = false;
+ error_message += QLatin1String("`");
+ error_message += QLatin1String (spell [expected_tokens [s]]);
+ error_message += QLatin1String("'");
+ }
+ }
+
+ if (error_message.isEmpty())
+ error_message = lexer->errorMessage();
+
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+
+bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QTextStream ts(&dev);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+
+ QString code = ts.readAll();
+ QScript::Lexer lexer;
+ lexer.setCode(code, /*lineNumber=*/1);
+ QScriptParser parser;
+ if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) {
+ qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(),
+ qPrintable(parser.errorMessage()));
+ return false;
+ }
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Q_UNUSED(dev);
+ Q_UNUSED(translator);
+ cd.appendError(QLatin1String("Cannot save .js files"));
+ return false;
+}
+
+int initQScript()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("js");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.description = QObject::tr("Qt Script source files");
+ format.loader = &loadQScript;
+ format.saver = &saveQScript;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQScript)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/qscript.g b/tools/linguist/shared/qscript.g
new file mode 100644
index 0000000000..c7ee80db70
--- /dev/null
+++ b/tools/linguist/shared/qscript.g
@@ -0,0 +1,2039 @@
+----------------------------------------------------------------------------
+--
+-- Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+-- Contact: Qt Software Information (qt-info@nokia.com)
+--
+-- This file is part of the Qt Linguist of the Qt Toolkit.
+--
+-- $QT_BEGIN_LICENSE:LGPL$
+-- No Commercial Usage
+-- This file contains pre-release code and may not be distributed.
+-- You may use this file in accordance with the terms and conditions
+-- contained in the either Technology Preview License Agreement or the
+-- Beta Release License Agreement.
+--
+-- GNU Lesser General Public License Usage
+-- Alternatively, this file may be used under the terms of the GNU Lesser
+-- General Public License version 2.1 as published by the Free Software
+-- Foundation and appearing in the file LICENSE.LGPL included in the
+-- packaging of this file. Please review the following information to
+-- ensure the GNU Lesser General Public License version 2.1 requirements
+-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+--
+-- In addition, as a special exception, Nokia gives you certain
+-- additional rights. These rights are described in the Nokia Qt LGPL
+-- Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+-- package.
+--
+-- GNU General Public License Usage
+-- Alternatively, this file may be used under the terms of the GNU
+-- General Public License version 3.0 as published by the Free Software
+-- Foundation and appearing in the file LICENSE.GPL included in the
+-- packaging of this file. Please review the following information to
+-- ensure the GNU General Public License version 3.0 requirements will be
+-- met: http://www.gnu.org/copyleft/gpl.html.
+--
+-- If you are unsure which license is appropriate for your use, please
+-- contact the sales department at qt-sales@nokia.com.
+-- $QT_END_LICENSE$
+--
+-- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+-- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+--
+----------------------------------------------------------------------------
+
+%parser QScriptGrammar
+%merged_output qscript.cpp
+%expect 3
+%expect-rr 1
+
+%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&="
+%token T_BREAK "break" T_CASE "case" T_CATCH "catch"
+%token T_COLON ":" T_COMMA ";" T_CONTINUE "continue"
+%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/"
+%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "."
+%token T_ELSE "else" T_EQ "=" T_EQ_EQ "=="
+%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for"
+%token T_FUNCTION "function" T_GE ">=" T_GT ">"
+%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>"
+%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if"
+%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{"
+%token T_LBRACKET "[" T_LE "<=" T_LPAREN "("
+%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<="
+%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--"
+%token T_NEW "new" T_NOT "!" T_NOT_EQ "!="
+%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|"
+%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+"
+%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?"
+%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
+%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")"
+%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*"
+%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal"
+%token T_SWITCH "switch" T_THIS "this" T_THROW "throw"
+%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof"
+%token T_VAR "var" T_VOID "void" T_WHILE "while"
+%token T_WITH "with" T_XOR "^" T_XOR_EQ "^="
+%token T_NULL "null" T_TRUE "true" T_FALSE "false"
+%token T_CONST "const"
+%token T_DEBUGGER "debugger"
+%token T_RESERVED_WORD "reserved word"
+
+%start Program
+
+/.
+#include "translator.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qvariant.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, bool plural, const QString &fileName, int lineNo)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ fileName, lineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ tor->replace(msg);
+}
+
+
+namespace QScript
+{
+
+class Lexer
+{
+public:
+ Lexer();
+ ~Lexer();
+
+ void setCode(const QString &c, int lineno);
+ int lex();
+
+ int currentLineNo() const { return yylineno; }
+ int currentColumnNo() const { return yycolumn; }
+
+ int startLineNo() const { return startlineno; }
+ int startColumnNo() const { return startcolumn; }
+
+ int endLineNo() const { return currentLineNo(); }
+ int endColumnNo() const
+ { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; }
+
+ bool prevTerminator() const { return terminator; }
+
+ enum State { Start,
+ Identifier,
+ InIdentifier,
+ InSingleLineComment,
+ InMultiLineComment,
+ InNum,
+ InNum0,
+ InHex,
+ InOctal,
+ InDecimal,
+ InExponentIndicator,
+ InExponent,
+ Hex,
+ Octal,
+ Number,
+ String,
+ Eof,
+ InString,
+ InEscapeSequence,
+ InHexEscape,
+ InUnicodeEscape,
+ Other,
+ Bad };
+
+ enum Error {
+ NoError,
+ IllegalCharacter,
+ UnclosedStringLiteral,
+ IllegalEscapeSequence,
+ IllegalUnicodeEscapeSequence,
+ UnclosedComment,
+ IllegalExponentIndicator,
+ IllegalIdentifier
+ };
+
+ enum ParenthesesState {
+ IgnoreParentheses,
+ CountParentheses,
+ BalancedParentheses
+ };
+
+ enum RegExpBodyPrefix {
+ NoPrefix,
+ EqualPrefix
+ };
+
+ bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
+
+ QString pattern;
+ int flags;
+
+ State lexerState() const
+ { return state; }
+
+ QString errorMessage() const
+ { return errmsg; }
+ void setErrorMessage(const QString &err)
+ { errmsg = err; }
+ void setErrorMessage(const char *err)
+ { setErrorMessage(QString::fromLatin1(err)); }
+
+ Error error() const
+ { return err; }
+ void clearError()
+ { err = NoError; }
+
+private:
+ int yylineno;
+ bool done;
+ char *buffer8;
+ QChar *buffer16;
+ uint size8, size16;
+ uint pos8, pos16;
+ bool terminator;
+ bool restrKeyword;
+ // encountered delimiter like "'" and "}" on last run
+ bool delimited;
+ int stackToken;
+
+ State state;
+ void setDone(State s);
+ uint pos;
+ void shift(uint p);
+ int lookupKeyword(const char *);
+
+ bool isWhiteSpace() const;
+ bool isLineTerminator() const;
+ bool isHexDigit(ushort c) const;
+ bool isOctalDigit(ushort c) const;
+
+ int matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ ushort singleEscape(ushort c) const;
+ ushort convertOctal(ushort c1, ushort c2,
+ ushort c3) const;
+public:
+ static unsigned char convertHex(ushort c1);
+ static unsigned char convertHex(ushort c1, ushort c2);
+ static QChar convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ static bool isIdentLetter(ushort c);
+ static bool isDecimalDigit(ushort c);
+
+ inline int ival() const { return qsyylval.toInt(); }
+ inline double dval() const { return qsyylval.toDouble(); }
+ inline QString ustr() const { return qsyylval.toString(); }
+ inline QVariant val() const { return qsyylval; }
+
+ const QChar *characterBuffer() const { return buffer16; }
+ int characterCount() const { return pos16; }
+
+private:
+ void record8(ushort c);
+ void record16(QChar c);
+ void recordStartPos();
+
+ int findReservedWord(const QChar *buffer, int size) const;
+
+ void syncProhibitAutomaticSemicolon();
+
+ const QChar *code;
+ uint length;
+ int yycolumn;
+ int startlineno;
+ int startcolumn;
+ int bol; // begin of line
+
+ QVariant qsyylval;
+
+ // current and following unicode characters
+ ushort current, next1, next2, next3;
+
+ struct keyword {
+ const char *name;
+ int token;
+ };
+
+ QString errmsg;
+ Error err;
+
+ bool wantRx;
+ bool check_reserved;
+
+ ParenthesesState parenthesesState;
+ int parenthesesCount;
+ bool prohibitAutomaticSemicolon;
+};
+
+} // namespace QScript
+
+extern double qstrtod(const char *s00, char const **se, bool *ok);
+
+#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1);
+
+namespace QScript {
+
+static int toDigit(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'a') && (c <= 'z'))
+ return 10 + c - 'a';
+ else if ((c >= 'A') && (c <= 'Z'))
+ return 10 + c - 'A';
+ return -1;
+}
+
+double integerFromString(const char *buf, int size, int radix)
+{
+ if (size == 0)
+ return qSNaN();
+
+ double sign = 1.0;
+ int i = 0;
+ if (buf[0] == '+') {
+ ++i;
+ } else if (buf[0] == '-') {
+ sign = -1.0;
+ ++i;
+ }
+
+ if (((size-i) >= 2) && (buf[i] == '0')) {
+ if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
+ && (radix < 34)) {
+ if ((radix != 0) && (radix != 16))
+ return 0;
+ radix = 16;
+ i += 2;
+ } else {
+ if (radix == 0) {
+ radix = 8;
+ ++i;
+ }
+ }
+ } else if (radix == 0) {
+ radix = 10;
+ }
+
+ int j = i;
+ for ( ; i < size; ++i) {
+ int d = toDigit(buf[i]);
+ if ((d == -1) || (d >= radix))
+ break;
+ }
+ double result;
+ if (j == i) {
+ if (!qstrcmp(buf, "Infinity"))
+ result = qInf();
+ else
+ result = qSNaN();
+ } else {
+ result = 0;
+ double multiplier = 1;
+ for (--i ; i >= j; --i, multiplier *= radix)
+ result += toDigit(buf[i]) * multiplier;
+ }
+ result *= sign;
+ return result;
+}
+
+} // namespace QScript
+
+QScript::Lexer::Lexer()
+ :
+ yylineno(0),
+ size8(128), size16(128), restrKeyword(false),
+ stackToken(-1), pos(0),
+ code(0), length(0),
+ bol(true),
+ current(0), next1(0), next2(0), next3(0),
+ err(NoError),
+ check_reserved(true),
+ parenthesesState(IgnoreParentheses),
+ prohibitAutomaticSemicolon(false)
+{
+ // allocate space for read buffers
+ buffer8 = new char[size8];
+ buffer16 = new QChar[size16];
+ flags = 0;
+
+}
+
+QScript::Lexer::~Lexer()
+{
+ delete [] buffer8;
+ delete [] buffer16;
+}
+
+void QScript::Lexer::setCode(const QString &c, int lineno)
+{
+ errmsg = QString();
+ yylineno = lineno;
+ yycolumn = 1;
+ restrKeyword = false;
+ delimited = false;
+ stackToken = -1;
+ pos = 0;
+ code = c.unicode();
+ length = c.length();
+ bol = true;
+
+ // read first characters
+ current = (length > 0) ? code[0].unicode() : 0;
+ next1 = (length > 1) ? code[1].unicode() : 0;
+ next2 = (length > 2) ? code[2].unicode() : 0;
+ next3 = (length > 3) ? code[3].unicode() : 0;
+}
+
+void QScript::Lexer::shift(uint p)
+{
+ while (p--) {
+ ++pos;
+ ++yycolumn;
+ current = next1;
+ next1 = next2;
+ next2 = next3;
+ next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0;
+ }
+}
+
+void QScript::Lexer::setDone(State s)
+{
+ state = s;
+ done = true;
+}
+
+int QScript::Lexer::findReservedWord(const QChar *c, int size) const
+{
+ switch (size) {
+ case 2: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o'))
+ return QScriptGrammar::T_DO;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f'))
+ return QScriptGrammar::T_IF;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n'))
+ return QScriptGrammar::T_IN;
+ } break;
+
+ case 3: {
+ if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_FOR;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w'))
+ return QScriptGrammar::T_NEW;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y'))
+ return QScriptGrammar::T_TRY;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_VAR;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 4: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_CASE;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_ELSE;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s'))
+ return QScriptGrammar::T_THIS;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d'))
+ return QScriptGrammar::T_VOID;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h'))
+ return QScriptGrammar::T_WITH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_TRUE;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l'))
+ return QScriptGrammar::T_NULL;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 5: {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('k'))
+ return QScriptGrammar::T_BREAK;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h'))
+ return QScriptGrammar::T_CATCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w'))
+ return QScriptGrammar::T_THROW;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_WHILE;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_CONST;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_FALSE;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 6: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_DELETE;
+ else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n'))
+ return QScriptGrammar::T_RETURN;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h'))
+ return QScriptGrammar::T_SWITCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f'))
+ return QScriptGrammar::T_TYPEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i')
+ && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 7: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('t'))
+ return QScriptGrammar::T_DEFAULT;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('y'))
+ return QScriptGrammar::T_FINALLY;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('n'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d')
+ && c[6] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 8: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_CONTINUE;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n'))
+ return QScriptGrammar::T_FUNCTION;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r'))
+ return QScriptGrammar::T_DEBUGGER;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 9: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f')
+ && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c')
+ && c[8] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n')
+ && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c')
+ && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 10: {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f'))
+ return QScriptGrammar::T_INSTANCEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 12: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z')
+ && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ } // switch
+
+ return -1;
+}
+
+int QScript::Lexer::lex()
+{
+ int token = 0;
+ state = Start;
+ ushort stringType = 0; // either single or double quotes
+ pos8 = pos16 = 0;
+ done = false;
+ terminator = false;
+
+ // did we push a token on the stack previously ?
+ // (after an automatic semicolon insertion)
+ if (stackToken >= 0) {
+ setDone(Other);
+ token = stackToken;
+ stackToken = -1;
+ }
+
+ while (!done) {
+ switch (state) {
+ case Start:
+ if (isWhiteSpace()) {
+ // do nothing
+ } else if (current == '/' && next1 == '/') {
+ recordStartPos();
+ shift(1);
+ state = InSingleLineComment;
+ } else if (current == '/' && next1 == '*') {
+ recordStartPos();
+ shift(1);
+ state = InMultiLineComment;
+ } else if (current == 0) {
+ syncProhibitAutomaticSemicolon();
+ if (!terminator && !delimited && !prohibitAutomaticSemicolon) {
+ // automatic semicolon insertion if program incomplete
+ token = QScriptGrammar::T_SEMICOLON;
+ stackToken = 0;
+ setDone(Other);
+ } else {
+ setDone(Eof);
+ }
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ bol = true;
+ terminator = true;
+ syncProhibitAutomaticSemicolon();
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ }
+ } else if (current == '"' || current == '\'') {
+ recordStartPos();
+ state = InString;
+ stringType = current;
+ } else if (isIdentLetter(current)) {
+ recordStartPos();
+ record16(current);
+ state = InIdentifier;
+ } else if (current == '0') {
+ recordStartPos();
+ record8(current);
+ state = InNum0;
+ } else if (isDecimalDigit(current)) {
+ recordStartPos();
+ record8(current);
+ state = InNum;
+ } else if (current == '.' && isDecimalDigit(next1)) {
+ recordStartPos();
+ record8(current);
+ state = InDecimal;
+ } else {
+ recordStartPos();
+ token = matchPunctuator(current, next1, next2, next3);
+ if (token != -1) {
+ if (terminator && !delimited && !prohibitAutomaticSemicolon
+ && (token == QScriptGrammar::T_PLUS_PLUS
+ || token == QScriptGrammar::T_MINUS_MINUS)) {
+ // automatic semicolon insertion
+ stackToken = token;
+ token = QScriptGrammar::T_SEMICOLON;
+ }
+ setDone(Other);
+ }
+ else {
+ setDone(Bad);
+ err = IllegalCharacter;
+ errmsg = QLatin1String("Illegal character");
+ }
+ }
+ break;
+ case InString:
+ if (current == stringType) {
+ shift(1);
+ setDone(String);
+ } else if (current == 0 || isLineTerminator()) {
+ setDone(Bad);
+ err = UnclosedStringLiteral;
+ errmsg = QLatin1String("Unclosed string at end of line");
+ } else if (current == '\\') {
+ state = InEscapeSequence;
+ } else {
+ record16(current);
+ }
+ break;
+ // Escape Sequences inside of strings
+ case InEscapeSequence:
+ if (isOctalDigit(current)) {
+ if (current >= '0' && current <= '3' &&
+ isOctalDigit(next1) && isOctalDigit(next2)) {
+ record16(convertOctal(current, next1, next2));
+ shift(2);
+ state = InString;
+ } else if (isOctalDigit(current) &&
+ isOctalDigit(next1)) {
+ record16(convertOctal('0', current, next1));
+ shift(1);
+ state = InString;
+ } else if (isOctalDigit(current)) {
+ record16(convertOctal('0', '0', current));
+ state = InString;
+ } else {
+ setDone(Bad);
+ err = IllegalEscapeSequence;
+ errmsg = QLatin1String("Illegal escape squence");
+ }
+ } else if (current == 'x')
+ state = InHexEscape;
+ else if (current == 'u')
+ state = InUnicodeEscape;
+ else {
+ record16(singleEscape(current));
+ state = InString;
+ }
+ break;
+ case InHexEscape:
+ if (isHexDigit(current) && isHexDigit(next1)) {
+ state = InString;
+ record16(QLatin1Char(convertHex(current, next1)));
+ shift(1);
+ } else if (current == stringType) {
+ record16(QLatin1Char('x'));
+ shift(1);
+ setDone(String);
+ } else {
+ record16(QLatin1Char('x'));
+ record16(current);
+ state = InString;
+ }
+ break;
+ case InUnicodeEscape:
+ if (isHexDigit(current) && isHexDigit(next1) &&
+ isHexDigit(next2) && isHexDigit(next3)) {
+ record16(convertUnicode(current, next1, next2, next3));
+ shift(3);
+ state = InString;
+ } else if (current == stringType) {
+ record16(QLatin1Char('u'));
+ shift(1);
+ setDone(String);
+ } else {
+ setDone(Bad);
+ err = IllegalUnicodeEscapeSequence;
+ errmsg = QLatin1String("Illegal unicode escape sequence");
+ }
+ break;
+ case InSingleLineComment:
+ if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ terminator = true;
+ bol = true;
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ } else
+ state = Start;
+ } else if (current == 0) {
+ setDone(Eof);
+ }
+ break;
+ case InMultiLineComment:
+ if (current == 0) {
+ setDone(Bad);
+ err = UnclosedComment;
+ errmsg = QLatin1String("Unclosed comment at end of file");
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ } else if (current == '*' && next1 == '/') {
+ state = Start;
+ shift(1);
+ }
+ break;
+ case InIdentifier:
+ if (isIdentLetter(current) || isDecimalDigit(current)) {
+ record16(current);
+ break;
+ }
+ setDone(Identifier);
+ break;
+ case InNum0:
+ if (current == 'x' || current == 'X') {
+ record8(current);
+ state = InHex;
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else if (isOctalDigit(current)) {
+ record8(current);
+ state = InOctal;
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InHex:
+ if (isHexDigit(current))
+ record8(current);
+ else
+ setDone(Hex);
+ break;
+ case InOctal:
+ if (isOctalDigit(current)) {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Octal);
+ }
+ break;
+ case InNum:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InDecimal:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InExponentIndicator:
+ if (current == '+' || current == '-') {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InExponent;
+ } else {
+ setDone(Bad);
+ err = IllegalExponentIndicator;
+ errmsg = QLatin1String("Illegal syntax for exponential number");
+ }
+ break;
+ case InExponent:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else {
+ setDone(Number);
+ }
+ break;
+ default:
+ Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement");
+ }
+
+ // move on to the next character
+ if (!done)
+ shift(1);
+ if (state != Start && state != InSingleLineComment)
+ bol = false;
+ }
+
+ // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
+ if ((state == Number || state == Octal || state == Hex)
+ && isIdentLetter(current)) {
+ state = Bad;
+ err = IllegalIdentifier;
+ errmsg = QLatin1String("Identifier cannot start with numeric literal");
+ }
+
+ // terminate string
+ buffer8[pos8] = '\0';
+
+ double dval = 0;
+ if (state == Number) {
+ dval = qstrtod(buffer8, 0, 0);
+ } else if (state == Hex) { // scan hex numbers
+ dval = QScript::integerFromString(buffer8, pos8, 16);
+ state = Number;
+ } else if (state == Octal) { // scan octal number
+ dval = QScript::integerFromString(buffer8, pos8, 8);
+ state = Number;
+ }
+
+ restrKeyword = false;
+ delimited = false;
+
+ switch (parenthesesState) {
+ case IgnoreParentheses:
+ break;
+ case CountParentheses:
+ if (token == QScriptGrammar::T_RPAREN) {
+ --parenthesesCount;
+ if (parenthesesCount == 0)
+ parenthesesState = BalancedParentheses;
+ } else if (token == QScriptGrammar::T_LPAREN) {
+ ++parenthesesCount;
+ }
+ break;
+ case BalancedParentheses:
+ parenthesesState = IgnoreParentheses;
+ break;
+ }
+
+ switch (state) {
+ case Eof:
+ return 0;
+ case Other:
+ if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON)
+ delimited = true;
+ return token;
+ case Identifier:
+ if ((token = findReservedWord(buffer16, pos16)) < 0) {
+ /* TODO: close leak on parse error. same holds true for String */
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_IDENTIFIER;
+ }
+ if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK
+ || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) {
+ restrKeyword = true;
+ } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR
+ || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) {
+ parenthesesState = CountParentheses;
+ parenthesesCount = 0;
+ } else if (token == QScriptGrammar::T_DO) {
+ parenthesesState = BalancedParentheses;
+ }
+ return token;
+ case String:
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_STRING_LITERAL;
+ case Number:
+ qsyylval = dval;
+ return QScriptGrammar::T_NUMERIC_LITERAL;
+ case Bad:
+ return -1;
+ default:
+ Q_ASSERT(!"unhandled numeration value in switch");
+ return -1;
+ }
+}
+
+bool QScript::Lexer::isWhiteSpace() const
+{
+ return (current == ' ' || current == '\t' ||
+ current == 0x0b || current == 0x0c);
+}
+
+bool QScript::Lexer::isLineTerminator() const
+{
+ return (current == '\n' || current == '\r');
+}
+
+bool QScript::Lexer::isIdentLetter(ushort c)
+{
+ /* TODO: allow other legitimate unicode chars */
+ return ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '$'
+ || c == '_');
+}
+
+bool QScript::Lexer::isDecimalDigit(ushort c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool QScript::Lexer::isHexDigit(ushort c) const
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+bool QScript::Lexer::isOctalDigit(ushort c) const
+{
+ return (c >= '0' && c <= '7');
+}
+
+int QScript::Lexer::matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
+ shift(4);
+ return QScriptGrammar::T_GT_GT_GT_EQ;
+ } else if (c1 == '=' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_EQ_EQ_EQ;
+ } else if (c1 == '!' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_NOT_EQ_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '>') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_GT;
+ } else if (c1 == '<' && c2 == '<' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_LT_LT_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_EQ;
+ } else if (c1 == '<' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_LE;
+ } else if (c1 == '>' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_GE;
+ } else if (c1 == '!' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_NOT_EQ;
+ } else if (c1 == '+' && c2 == '+') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_PLUS;
+ } else if (c1 == '-' && c2 == '-') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_MINUS;
+ } else if (c1 == '=' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_EQ_EQ;
+ } else if (c1 == '+' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_EQ;
+ } else if (c1 == '-' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_EQ;
+ } else if (c1 == '*' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_STAR_EQ;
+ } else if (c1 == '/' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_DIVIDE_EQ;
+ } else if (c1 == '&' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_AND_EQ;
+ } else if (c1 == '^' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_XOR_EQ;
+ } else if (c1 == '%' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_REMAINDER_EQ;
+ } else if (c1 == '|' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_OR_EQ;
+ } else if (c1 == '<' && c2 == '<') {
+ shift(2);
+ return QScriptGrammar::T_LT_LT;
+ } else if (c1 == '>' && c2 == '>') {
+ shift(2);
+ return QScriptGrammar::T_GT_GT;
+ } else if (c1 == '&' && c2 == '&') {
+ shift(2);
+ return QScriptGrammar::T_AND_AND;
+ } else if (c1 == '|' && c2 == '|') {
+ shift(2);
+ return QScriptGrammar::T_OR_OR;
+ }
+
+ switch(c1) {
+ case '=': shift(1); return QScriptGrammar::T_EQ;
+ case '>': shift(1); return QScriptGrammar::T_GT;
+ case '<': shift(1); return QScriptGrammar::T_LT;
+ case ',': shift(1); return QScriptGrammar::T_COMMA;
+ case '!': shift(1); return QScriptGrammar::T_NOT;
+ case '~': shift(1); return QScriptGrammar::T_TILDE;
+ case '?': shift(1); return QScriptGrammar::T_QUESTION;
+ case ':': shift(1); return QScriptGrammar::T_COLON;
+ case '.': shift(1); return QScriptGrammar::T_DOT;
+ case '+': shift(1); return QScriptGrammar::T_PLUS;
+ case '-': shift(1); return QScriptGrammar::T_MINUS;
+ case '*': shift(1); return QScriptGrammar::T_STAR;
+ case '/': shift(1); return QScriptGrammar::T_DIVIDE_;
+ case '&': shift(1); return QScriptGrammar::T_AND;
+ case '|': shift(1); return QScriptGrammar::T_OR;
+ case '^': shift(1); return QScriptGrammar::T_XOR;
+ case '%': shift(1); return QScriptGrammar::T_REMAINDER;
+ case '(': shift(1); return QScriptGrammar::T_LPAREN;
+ case ')': shift(1); return QScriptGrammar::T_RPAREN;
+ case '{': shift(1); return QScriptGrammar::T_LBRACE;
+ case '}': shift(1); return QScriptGrammar::T_RBRACE;
+ case '[': shift(1); return QScriptGrammar::T_LBRACKET;
+ case ']': shift(1); return QScriptGrammar::T_RBRACKET;
+ case ';': shift(1); return QScriptGrammar::T_SEMICOLON;
+
+ default: return -1;
+ }
+}
+
+ushort QScript::Lexer::singleEscape(ushort c) const
+{
+ switch(c) {
+ case 'b':
+ return 0x08;
+ case 't':
+ return 0x09;
+ case 'n':
+ return 0x0A;
+ case 'v':
+ return 0x0B;
+ case 'f':
+ return 0x0C;
+ case 'r':
+ return 0x0D;
+ case '"':
+ return 0x22;
+ case '\'':
+ return 0x27;
+ case '\\':
+ return 0x5C;
+ default:
+ return c;
+ }
+}
+
+ushort QScript::Lexer::convertOctal(ushort c1, ushort c2,
+ ushort c3) const
+{
+ return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2)
+{
+ return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ return QChar((convertHex(c3) << 4) + convertHex(c4),
+ (convertHex(c1) << 4) + convertHex(c2));
+}
+
+void QScript::Lexer::record8(ushort c)
+{
+ Q_ASSERT(c <= 0xff);
+
+ // enlarge buffer if full
+ if (pos8 >= size8 - 1) {
+ char *tmp = new char[2 * size8];
+ memcpy(tmp, buffer8, size8 * sizeof(char));
+ delete [] buffer8;
+ buffer8 = tmp;
+ size8 *= 2;
+ }
+
+ buffer8[pos8++] = (char) c;
+}
+
+void QScript::Lexer::record16(QChar c)
+{
+ // enlarge buffer if full
+ if (pos16 >= size16 - 1) {
+ QChar *tmp = new QChar[2 * size16];
+ memcpy(tmp, buffer16, size16 * sizeof(QChar));
+ delete [] buffer16;
+ buffer16 = tmp;
+ size16 *= 2;
+ }
+
+ buffer16[pos16++] = c;
+}
+
+void QScript::Lexer::recordStartPos()
+{
+ startlineno = yylineno;
+ startcolumn = yycolumn;
+}
+
+bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix)
+{
+ pos16 = 0;
+ bool lastWasEscape = false;
+
+ if (prefix == EqualPrefix)
+ record16(QLatin1Char('='));
+
+ while (1) {
+ if (isLineTerminator() || current == 0) {
+ errmsg = QLatin1String("Unterminated regular expression literal");
+ return false;
+ }
+ else if (current != '/' || lastWasEscape == true)
+ {
+ record16(current);
+ lastWasEscape = !lastWasEscape && (current == '\\');
+ }
+ else {
+ pattern = QString(buffer16, pos16);
+ pos16 = 0;
+ shift(1);
+ break;
+ }
+ shift(1);
+ }
+
+ flags = 0;
+ while (isIdentLetter(current)) {
+ record16(current);
+ shift(1);
+ }
+
+ return true;
+}
+
+void QScript::Lexer::syncProhibitAutomaticSemicolon()
+{
+ if (parenthesesState == BalancedParentheses) {
+ // we have seen something like "if (foo)", which means we should
+ // never insert an automatic semicolon at this point, since it would
+ // then be expanded into an empty statement (ECMA-262 7.9.1)
+ prohibitAutomaticSemicolon = true;
+ parenthesesState = IgnoreParentheses;
+ } else {
+ prohibitAutomaticSemicolon = false;
+ }
+}
+
+
+class Translator;
+
+class QScriptParser: protected $table
+{
+public:
+ QVariant val;
+
+ struct Location {
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ };
+
+public:
+ QScriptParser();
+ ~QScriptParser();
+
+ bool parse(QScript::Lexer *lexer,
+ const QString &fileName,
+ Translator *translator);
+
+ inline QString errorMessage() const
+ { return error_message; }
+ inline int errorLineNumber() const
+ { return error_lineno; }
+ inline int errorColumnNumber() const
+ { return error_column; }
+
+protected:
+ inline void reallocateStack();
+
+ inline QVariant &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline Location &loc(int index)
+ { return location_stack [tos + index - 2]; }
+
+protected:
+ int tos;
+ int stack_size;
+ QVector<QVariant> sym_stack;
+ int *state_stack;
+ Location *location_stack;
+ QString error_message;
+ int error_lineno;
+ int error_column;
+};
+
+inline void QScriptParser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack.resize(stack_size);
+ state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location)));
+}
+
+inline static bool automatic(QScript::Lexer *lexer, int token)
+{
+ return (token == $table::T_RBRACE)
+ || (token == 0)
+ || lexer->prevTerminator();
+}
+
+QScriptParser::QScriptParser():
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0)
+{
+}
+
+QScriptParser::~QScriptParser()
+{
+ if (stack_size) {
+ qFree(state_stack);
+ qFree(location_stack);
+ }
+}
+
+static inline QScriptParser::Location location(QScript::Lexer *lexer)
+{
+ QScriptParser::Location loc;
+ loc.startLine = lexer->startLineNo();
+ loc.startColumn = lexer->startColumnNo();
+ loc.endLine = lexer->endLineNo();
+ loc.endColumn = lexer->endColumnNo();
+ return loc;
+}
+
+bool QScriptParser::parse(QScript::Lexer *lexer,
+ const QString &fileName,
+ Translator *translator)
+{
+ const int INITIAL_STATE = 0;
+
+ int yytoken = -1;
+ int saved_yytoken = -1;
+ int identLineNo = -1;
+
+ reallocateStack();
+
+ tos = 0;
+ state_stack[++tos] = INITIAL_STATE;
+
+ while (true)
+ {
+ const int state = state_stack [tos];
+ if (yytoken == -1 && - TERMINAL_COUNT != action_index [state])
+ {
+ if (saved_yytoken == -1)
+ {
+ yytoken = lexer->lex();
+ location_stack [tos] = location(lexer);
+ }
+ else
+ {
+ yytoken = saved_yytoken;
+ saved_yytoken = -1;
+ }
+ }
+
+ int act = t_action (state, yytoken);
+
+ if (act == ACCEPT_STATE)
+ return true;
+
+ else if (act > 0)
+ {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ sym_stack [tos] = lexer->val ();
+ state_stack [tos] = act;
+ location_stack [tos] = location(lexer);
+ yytoken = -1;
+ }
+
+ else if (act < 0)
+ {
+ int r = - act - 1;
+
+ tos -= rhs [r];
+ act = state_stack [tos++];
+
+ switch (r) {
+./
+
+PrimaryExpression: T_THIS ;
+
+PrimaryExpression: T_IDENTIFIER ;
+/.
+case $rule_number: {
+ sym(1) = sym(1).toByteArray();
+ identLineNo = lexer->startLineNo();
+} break;
+./
+
+PrimaryExpression: T_NULL ;
+PrimaryExpression: T_TRUE ;
+PrimaryExpression: T_FALSE ;
+PrimaryExpression: T_NUMERIC_LITERAL ;
+PrimaryExpression: T_STRING_LITERAL ;
+
+PrimaryExpression: T_DIVIDE_ ;
+/:
+#define Q_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+./
+
+PrimaryExpression: T_DIVIDE_EQ ;
+/:
+#define Q_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+./
+
+PrimaryExpression: T_LBRACKET ElisionOpt T_RBRACKET ;
+PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ;
+PrimaryExpression: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET ;
+PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ;
+PrimaryExpression: T_LPAREN Expression T_RPAREN ;
+ElementList: ElisionOpt AssignmentExpression ;
+ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression ;
+Elision: T_COMMA ;
+Elision: Elision T_COMMA ;
+ElisionOpt: ;
+ElisionOpt: Elision ;
+PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ;
+PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ;
+PropertyName: T_IDENTIFIER ;
+PropertyName: T_STRING_LITERAL ;
+PropertyName: T_NUMERIC_LITERAL ;
+PropertyName: ReservedIdentifier ;
+ReservedIdentifier: T_BREAK ;
+ReservedIdentifier: T_CASE ;
+ReservedIdentifier: T_CATCH ;
+ReservedIdentifier: T_CONST ;
+ReservedIdentifier: T_CONTINUE ;
+ReservedIdentifier: T_DEBUGGER ;
+ReservedIdentifier: T_DEFAULT ;
+ReservedIdentifier: T_DELETE ;
+ReservedIdentifier: T_DO ;
+ReservedIdentifier: T_ELSE ;
+ReservedIdentifier: T_FALSE ;
+ReservedIdentifier: T_FINALLY ;
+ReservedIdentifier: T_FOR ;
+ReservedIdentifier: T_FUNCTION ;
+ReservedIdentifier: T_IF ;
+ReservedIdentifier: T_IN ;
+ReservedIdentifier: T_INSTANCEOF ;
+ReservedIdentifier: T_NEW ;
+ReservedIdentifier: T_NULL ;
+ReservedIdentifier: T_RESERVED_WORD ;
+ReservedIdentifier: T_RETURN ;
+ReservedIdentifier: T_SWITCH ;
+ReservedIdentifier: T_THIS ;
+ReservedIdentifier: T_THROW ;
+ReservedIdentifier: T_TRUE ;
+ReservedIdentifier: T_TRY ;
+ReservedIdentifier: T_TYPEOF ;
+ReservedIdentifier: T_VAR ;
+ReservedIdentifier: T_VOID ;
+ReservedIdentifier: T_WHILE ;
+ReservedIdentifier: T_WITH ;
+PropertyIdentifier: T_IDENTIFIER ;
+PropertyIdentifier: ReservedIdentifier ;
+
+MemberExpression: PrimaryExpression ;
+MemberExpression: FunctionExpression ;
+MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ;
+MemberExpression: MemberExpression T_DOT PropertyIdentifier ;
+MemberExpression: T_NEW MemberExpression Arguments ;
+NewExpression: MemberExpression ;
+NewExpression: T_NEW NewExpression ;
+
+CallExpression: MemberExpression Arguments ;
+/.
+case $rule_number: {
+ QString name = sym(1).toString();
+ if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) {
+ QVariantList args = sym(2).toList();
+ if (args.size() < 2) {
+ qWarning("%s:%d: %s() requires at least two arguments",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ if ((args.at(0).type() != QVariant::String)
+ || (args.at(1).type() != QVariant::String)) {
+ qWarning("%s:%d: %s(): both arguments must be literal strings",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ QString context = args.at(0).toString();
+ QString text = args.at(1).toString();
+ QString comment = args.value(2).toString();
+ QString extracomment;
+ bool plural = (args.size() > 4);
+ recordMessage(translator, context, text, comment, extracomment,
+ plural, fileName, identLineNo);
+ }
+ }
+ } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) {
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ qWarning("%s:%d: %s() requires at least one argument",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ qWarning("%s:%d: %s(): text to translate must be a literal string",
+ qPrintable(fileName), identLineNo, qPrintable(name));
+ } else {
+ QString context = QFileInfo(fileName).baseName();
+ QString text = args.at(0).toString();
+ QString comment = args.value(1).toString();
+ QString extracomment;
+ bool plural = (args.size() > 2);
+ recordMessage(translator, context, text, comment, extracomment,
+ plural, fileName, identLineNo);
+ }
+ }
+ }
+} break;
+./
+
+CallExpression: CallExpression Arguments ;
+CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ;
+CallExpression: CallExpression T_DOT PropertyIdentifier ;
+
+Arguments: T_LPAREN T_RPAREN ;
+/.
+case $rule_number: {
+ sym(1) = QVariantList();
+} break;
+./
+
+Arguments: T_LPAREN ArgumentList T_RPAREN ;
+/.
+case $rule_number: {
+ sym(1) = sym(2);
+} break;
+./
+
+ArgumentList: AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1) = QVariantList() << sym(1);
+} break;
+./
+
+ArgumentList: ArgumentList T_COMMA AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1) = sym(1).toList() << sym(3);
+} break;
+./
+
+LeftHandSideExpression: NewExpression ;
+LeftHandSideExpression: CallExpression ;
+PostfixExpression: LeftHandSideExpression ;
+PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ;
+PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ;
+UnaryExpression: PostfixExpression ;
+UnaryExpression: T_DELETE UnaryExpression ;
+UnaryExpression: T_VOID UnaryExpression ;
+UnaryExpression: T_TYPEOF UnaryExpression ;
+UnaryExpression: T_PLUS_PLUS UnaryExpression ;
+UnaryExpression: T_MINUS_MINUS UnaryExpression ;
+UnaryExpression: T_PLUS UnaryExpression ;
+UnaryExpression: T_MINUS UnaryExpression ;
+UnaryExpression: T_TILDE UnaryExpression ;
+UnaryExpression: T_NOT UnaryExpression ;
+MultiplicativeExpression: UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ;
+AdditiveExpression: MultiplicativeExpression ;
+
+AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ;
+/.
+case $rule_number: {
+ if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String))
+ sym(1) = sym(1).toString() + sym(3).toString();
+ else
+ sym(1) = QVariant();
+} break;
+./
+
+AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ;
+ShiftExpression: AdditiveExpression ;
+ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ;
+RelationalExpression: ShiftExpression ;
+RelationalExpression: RelationalExpression T_LT ShiftExpression ;
+RelationalExpression: RelationalExpression T_GT ShiftExpression ;
+RelationalExpression: RelationalExpression T_LE ShiftExpression ;
+RelationalExpression: RelationalExpression T_GE ShiftExpression ;
+RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ;
+RelationalExpression: RelationalExpression T_IN ShiftExpression ;
+RelationalExpressionNotIn: ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ;
+EqualityExpression: RelationalExpression ;
+EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ;
+EqualityExpressionNotIn: RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ;
+BitwiseANDExpression: EqualityExpression ;
+BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ;
+BitwiseANDExpressionNotIn: EqualityExpressionNotIn ;
+BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ;
+BitwiseXORExpression: BitwiseANDExpression ;
+BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ;
+BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ;
+BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ;
+BitwiseORExpression: BitwiseXORExpression ;
+BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ;
+BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ;
+BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ;
+LogicalANDExpression: BitwiseORExpression ;
+LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ;
+LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ;
+LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ;
+LogicalORExpression: LogicalANDExpression ;
+LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ;
+LogicalORExpressionNotIn: LogicalANDExpressionNotIn ;
+LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ;
+ConditionalExpression: LogicalORExpression ;
+ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ;
+ConditionalExpressionNotIn: LogicalORExpressionNotIn ;
+ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ;
+AssignmentExpression: ConditionalExpression ;
+AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ;
+AssignmentExpressionNotIn: ConditionalExpressionNotIn ;
+AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ;
+AssignmentOperator: T_EQ ;
+AssignmentOperator: T_STAR_EQ ;
+AssignmentOperator: T_DIVIDE_EQ ;
+AssignmentOperator: T_REMAINDER_EQ ;
+AssignmentOperator: T_PLUS_EQ ;
+AssignmentOperator: T_MINUS_EQ ;
+AssignmentOperator: T_LT_LT_EQ ;
+AssignmentOperator: T_GT_GT_EQ ;
+AssignmentOperator: T_GT_GT_GT_EQ ;
+AssignmentOperator: T_AND_EQ ;
+AssignmentOperator: T_XOR_EQ ;
+AssignmentOperator: T_OR_EQ ;
+Expression: AssignmentExpression ;
+Expression: Expression T_COMMA AssignmentExpression ;
+ExpressionOpt: ;
+ExpressionOpt: Expression ;
+ExpressionNotIn: AssignmentExpressionNotIn ;
+ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ;
+ExpressionNotInOpt: ;
+ExpressionNotInOpt: ExpressionNotIn ;
+
+Statement: Block ;
+Statement: VariableStatement ;
+Statement: EmptyStatement ;
+Statement: ExpressionStatement ;
+Statement: IfStatement ;
+Statement: IterationStatement ;
+Statement: ContinueStatement ;
+Statement: BreakStatement ;
+Statement: ReturnStatement ;
+Statement: WithStatement ;
+Statement: LabelledStatement ;
+Statement: SwitchStatement ;
+Statement: ThrowStatement ;
+Statement: TryStatement ;
+Statement: DebuggerStatement ;
+
+Block: T_LBRACE StatementListOpt T_RBRACE ;
+StatementList: Statement ;
+StatementList: StatementList Statement ;
+StatementListOpt: ;
+StatementListOpt: StatementList ;
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ;
+VariableDeclarationKind: T_CONST ;
+VariableDeclarationKind: T_VAR ;
+VariableDeclarationList: VariableDeclaration ;
+VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ;
+VariableDeclarationListNotIn: VariableDeclarationNotIn ;
+VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ;
+VariableDeclaration: T_IDENTIFIER InitialiserOpt ;
+VariableDeclarationNotIn: T_IDENTIFIER InitialiserNotInOpt ;
+Initialiser: T_EQ AssignmentExpression ;
+InitialiserOpt: ;
+InitialiserOpt: Initialiser ;
+InitialiserNotIn: T_EQ AssignmentExpressionNotIn ;
+InitialiserNotInOpt: ;
+InitialiserNotInOpt: InitialiserNotIn ;
+EmptyStatement: T_SEMICOLON ;
+ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ExpressionStatement: Expression T_SEMICOLON ;
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ;
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ;
+IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ;
+ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE T_SEMICOLON ;
+ContinueStatement: T_CONTINUE T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE T_IDENTIFIER T_SEMICOLON ;
+BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK T_SEMICOLON ;
+BreakStatement: T_BREAK T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK T_IDENTIFIER T_SEMICOLON ;
+ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ;
+WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ;
+SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ;
+CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ;
+CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ;
+CaseClauses: CaseClause ;
+CaseClauses: CaseClauses CaseClause ;
+CaseClausesOpt: ;
+CaseClausesOpt: CaseClauses ;
+CaseClause: T_CASE Expression T_COLON StatementListOpt ;
+DefaultClause: T_DEFAULT T_COLON StatementListOpt ;
+LabelledStatement: T_IDENTIFIER T_COLON Statement ;
+ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ThrowStatement: T_THROW Expression T_SEMICOLON ;
+TryStatement: T_TRY Block Catch ;
+TryStatement: T_TRY Block Finally ;
+TryStatement: T_TRY Block Catch Finally ;
+Catch: T_CATCH T_LPAREN T_IDENTIFIER T_RPAREN Block ;
+Finally: T_FINALLY Block ;
+DebuggerStatement: T_DEBUGGER ;
+FunctionDeclaration: T_FUNCTION T_IDENTIFIER T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+FormalParameterList: T_IDENTIFIER ;
+FormalParameterList: FormalParameterList T_COMMA T_IDENTIFIER ;
+FormalParameterListOpt: ;
+FormalParameterListOpt: FormalParameterList ;
+FunctionBodyOpt: ;
+FunctionBodyOpt: FunctionBody ;
+FunctionBody: SourceElements ;
+Program: SourceElements ;
+SourceElements: SourceElement ;
+SourceElements: SourceElements SourceElement ;
+SourceElement: Statement ;
+SourceElement: FunctionDeclaration ;
+IdentifierOpt: ;
+IdentifierOpt: T_IDENTIFIER ;
+PropertyNameAndValueListOpt: ;
+PropertyNameAndValueListOpt: PropertyNameAndValueList ;
+
+/.
+ } // switch
+
+ state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
+
+ if (rhs[r] > 1) {
+ location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine;
+ location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn;
+ location_stack[tos] = location_stack[tos + rhs[r] - 1];
+ }
+ }
+
+ else
+ {
+ if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0)
+ {
+ saved_yytoken = yytoken;
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ else if ((state == INITIAL_STATE) && (yytoken == 0)) {
+ // accept empty input
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ int ers = state;
+ int shifts = 0;
+ int reduces = 0;
+ int expected_tokens [3];
+ for (int tk = 0; tk < TERMINAL_COUNT; ++tk)
+ {
+ int k = t_action (ers, tk);
+
+ if (! k)
+ continue;
+ else if (k < 0)
+ ++reduces;
+ else if (spell [tk])
+ {
+ if (shifts < 3)
+ expected_tokens [shifts] = tk;
+ ++shifts;
+ }
+ }
+
+ error_message.clear ();
+ if (shifts && shifts < 3)
+ {
+ bool first = true;
+
+ for (int s = 0; s < shifts; ++s)
+ {
+ if (first)
+ error_message += QLatin1String ("Expected ");
+ else
+ error_message += QLatin1String (", ");
+
+ first = false;
+ error_message += QLatin1String("`");
+ error_message += QLatin1String (spell [expected_tokens [s]]);
+ error_message += QLatin1String("'");
+ }
+ }
+
+ if (error_message.isEmpty())
+ error_message = lexer->errorMessage();
+
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+
+bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QTextStream ts(&dev);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+
+ QString code = ts.readAll();
+ QScript::Lexer lexer;
+ lexer.setCode(code, /*lineNumber=*/1);
+ QScriptParser parser;
+ if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) {
+ qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(),
+ qPrintable(parser.errorMessage()));
+ return false;
+ }
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Q_UNUSED(dev);
+ Q_UNUSED(translator);
+ cd.appendError(QLatin1String("Cannot save .js files"));
+ return false;
+}
+
+int initQScript()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("js");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.description = QObject::tr("Qt Script source files");
+ format.loader = &loadQScript;
+ format.saver = &saveQScript;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQScript)
+
+QT_END_NAMESPACE
+./
diff --git a/tools/linguist/shared/simtexth.cpp b/tools/linguist/shared/simtexth.cpp
new file mode 100644
index 0000000000..0556ed63a7
--- /dev/null
+++ b/tools/linguist/shared/simtexth.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "simtexth.h"
+#include "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+
+QT_BEGIN_NAMESPACE
+
+typedef QList<TranslatorMessage> TML;
+
+/*
+ How similar are two texts? The approach used here relies on co-occurrence
+ matrices and is very efficient.
+
+ Let's see with an example: how similar are "here" and "hither"? The
+ co-occurrence matrix M for "here" is M[h,e] = 1, M[e,r] = 1, M[r,e] = 1, and 0
+ elsewhere; the matrix N for "hither" is N[h,i] = 1, N[i,t] = 1, ...,
+ N[h,e] = 1, N[e,r] = 1, and 0 elsewhere. The union U of both matrices is the
+ matrix U[i,j] = max { M[i,j], N[i,j] }, and the intersection V is
+ V[i,j] = min { M[i,j], N[i,j] }. The score for a pair of texts is
+
+ score = (sum of V[i,j] over all i, j) / (sum of U[i,j] over all i, j),
+
+ a formula suggested by Arnt Gulbrandsen. Here we have
+
+ score = 2 / 6,
+
+ or one third.
+
+ The implementation differs from this in a few details. Most importantly,
+ repetitions are ignored; for input "xxx", M[x,x] equals 1, not 2.
+*/
+
+/*
+ Every character is assigned to one of 20 buckets so that the co-occurrence
+ matrix requires only 20 * 20 = 400 bits, not 256 * 256 = 65536 bits or even
+ more if we want the whole Unicode. Which character falls in which bucket is
+ arbitrary.
+
+ The second half of the table is a replica of the first half, because of
+ laziness.
+*/
+static const int indexOf[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// ! " # $ % & ' ( ) * + , - . /
+ 0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
+// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
+ 1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
+// @ A B C D E F G H I J K L M N O
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+// P Q R S T U V W X Y Z [ \ ] ^ _
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+// ` a b c d e f g h i j k l m n o
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+// p q r s t u v w x y z { | } ~
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
+ 1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0
+};
+
+/*
+ The entry bitCount[i] (for i between 0 and 255) is the number of bits used to
+ represent i in binary.
+*/
+static const int bitCount[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+struct CoMatrix
+{
+ /*
+ The matrix has 20 * 20 = 400 entries. This requires 50 bytes, or 13
+ words. Some operations are performed on words for more efficiency.
+ */
+ union {
+ quint8 b[52];
+ quint32 w[13];
+ };
+
+ CoMatrix() { memset( b, 0, 52 ); }
+
+ CoMatrix(const QString &str)
+ {
+ QByteArray ba = str.toUtf8();
+ const char *text = ba.constData();
+ char c = '\0', d;
+ memset( b, 0, 52 );
+ /*
+ The Knuth books are not in the office only for show; they help make
+ loops 30% faster and 20% as readable.
+ */
+ while ( (d = *text) != '\0' ) {
+ setCoOccurence( c, d );
+ if ( (c = *++text) != '\0' ) {
+ setCoOccurence( d, c );
+ text++;
+ }
+ }
+ }
+
+ void setCoOccurence( char c, char d ) {
+ int k = indexOf[(uchar) c] + 20 * indexOf[(uchar) d];
+ b[k >> 3] |= (1 << (k & 0x7));
+ }
+
+ int worth() const {
+ int w = 0;
+ for ( int i = 0; i < 50; i++ )
+ w += bitCount[b[i]];
+ return w;
+ }
+};
+
+static inline CoMatrix reunion(const CoMatrix &m, const CoMatrix &n)
+{
+ CoMatrix p;
+ for (int i = 0; i < 13; ++i)
+ p.w[i] = m.w[i] | n.w[i];
+ return p;
+}
+
+static inline CoMatrix intersection(const CoMatrix &m, const CoMatrix &n)
+{
+ CoMatrix p;
+ for (int i = 0; i < 13; ++i)
+ p.w[i] = m.w[i] & n.w[i];
+ return p;
+}
+
+StringSimilarityMatcher::StringSimilarityMatcher(const QString &stringToMatch)
+{
+ m_cm = new CoMatrix(stringToMatch);
+ m_length = stringToMatch.length();
+}
+
+int StringSimilarityMatcher::getSimilarityScore(const QString &strCandidate)
+{
+ CoMatrix cmTarget(strCandidate);
+ int delta = qAbs(m_length - strCandidate.size());
+ int score = ( (intersection(*m_cm, cmTarget).worth() + 1) << 10 ) /
+ ( reunion(*m_cm, cmTarget).worth() + (delta << 1) + 1 );
+ return score;
+}
+
+StringSimilarityMatcher::~StringSimilarityMatcher()
+{
+ delete m_cm;
+}
+
+/**
+ * Checks how similar two strings are.
+ * The return value is the score, and a higher score is more similar
+ * than one with a low score.
+ * Linguist considers a score over 190 to be a good match.
+ * \sa StringSimilarityMatcher
+ */
+int getSimilarityScore(const QString &str1, const QString &str2)
+{
+ CoMatrix cmTarget(str2);
+ CoMatrix cm(str1);
+ int delta = qAbs(str1.size() - str2.size());
+
+ int score = ( (intersection(cm, cmTarget).worth() + 1) << 10 )
+ / ( reunion(cm, cmTarget).worth() + (delta << 1) + 1 );
+
+ return score;
+}
+
+CandidateList similarTextHeuristicCandidates(const Translator *tor,
+ const QString &text, int maxCandidates)
+{
+ QList<int> scores;
+ CandidateList candidates;
+
+ TML all = tor->translatedMessages();
+
+ foreach (const TranslatorMessage &mtm, all) {
+ if (mtm.type() == TranslatorMessage::Unfinished
+ || mtm.translation().isEmpty())
+ continue;
+
+ QString s = mtm.sourceText();
+ int score = getSimilarityScore(s, text);
+
+ if (candidates.size() == maxCandidates && score > scores[maxCandidates - 1] )
+ candidates.removeLast();
+
+ if (candidates.size() < maxCandidates && score >= textSimilarityThreshold) {
+ Candidate cand( s, mtm.translation() );
+
+ int i;
+ for (i = 0; i < candidates.size(); i++) {
+ if (score >= scores.at(i)) {
+ if (score == scores.at(i)) {
+ if (candidates.at(i) == cand)
+ goto continue_outer_loop;
+ } else {
+ break;
+ }
+ }
+ }
+ scores.insert(i, score);
+ candidates.insert(i, cand);
+ }
+ continue_outer_loop:
+ ;
+ }
+ return candidates;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/simtexth.h b/tools/linguist/shared/simtexth.h
new file mode 100644
index 0000000000..e3cad91e59
--- /dev/null
+++ b/tools/linguist/shared/simtexth.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SIMTEXTH_H
+#define SIMTEXTH_H
+
+const int textSimilarityThreshold = 190;
+
+#include <QString>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class Translator;
+
+struct Candidate
+{
+ Candidate() {}
+ Candidate(const QString& source0, const QString &target0)
+ : source(source0), target(target0)
+ {}
+
+ QString source;
+ QString target;
+};
+
+inline bool operator==( const Candidate& c, const Candidate& d ) {
+ return c.target == d.target && c.source == d.source;
+}
+inline bool operator!=( const Candidate& c, const Candidate& d ) {
+ return !operator==( c, d );
+}
+
+typedef QList<Candidate> CandidateList;
+
+struct CoMatrix;
+/**
+ * This class is more efficient for searching through a large array of candidate strings, since we only
+ * have to construct the CoMatrix for the \a stringToMatch once,
+ * after that we just call getSimilarityScore(strCandidate).
+ * \sa getSimilarityScore
+ */
+class StringSimilarityMatcher {
+public:
+ StringSimilarityMatcher(const QString &stringToMatch);
+ ~StringSimilarityMatcher();
+ int getSimilarityScore(const QString &strCandidate);
+
+private:
+ CoMatrix *m_cm;
+ int m_length;
+};
+
+int getSimilarityScore(const QString &str1, const QString &str2);
+
+CandidateList similarTextHeuristicCandidates( const Translator *tor,
+ const QString &text,
+ int maxCandidates );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp
new file mode 100644
index 0000000000..3b5e8f3fba
--- /dev/null
+++ b/tools/linguist/shared/translator.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include "simtexth.h"
+
+#include <stdio.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <private/qtranslator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Translator::Translator() :
+ m_codecName("ISO-8859-1"),
+ m_locationsType(AbsoluteLocations)
+{
+}
+
+void Translator::registerFileFormat(const FileFormat &format)
+{
+ //qDebug() << "Translator: Registering format " << format.extension;
+ QList<Translator::FileFormat> &formats = registeredFileFormats();
+ for (int i = 0; i < formats.size(); ++i)
+ if (format.fileType == formats[i].fileType && format.priority < formats[i].priority) {
+ formats.insert(i, format);
+ return;
+ }
+ formats.append(format);
+}
+
+QList<Translator::FileFormat> &Translator::registeredFileFormats()
+{
+ static QList<Translator::FileFormat> theFormats;
+ return theFormats;
+}
+
+void Translator::replace(const TranslatorMessage &msg)
+{
+ int index = m_messages.indexOf(msg);
+ if (index == -1)
+ m_messages.append(msg);
+ else
+ m_messages[index] = msg;
+}
+
+void Translator::replaceSorted(const TranslatorMessage &msg)
+{
+ int index = m_messages.indexOf(msg);
+ if (index == -1)
+ appendSorted(msg);
+ else
+ m_messages[index] = msg;
+}
+
+void Translator::extend(const TranslatorMessage &msg)
+{
+ int index = m_messages.indexOf(msg);
+ if (index == -1) {
+ m_messages.append(msg);
+ } else {
+ m_messages[index].addReferenceUniq(msg.fileName(), msg.lineNumber());
+ if (!msg.extraComment().isEmpty()) {
+ QString cmt = m_messages[index].extraComment();
+ if (!cmt.isEmpty())
+ cmt.append(QLatin1String("\n----------\n"));
+ cmt.append(msg.extraComment());
+ m_messages[index].setExtraComment(cmt);
+ }
+ }
+}
+
+void Translator::append(const TranslatorMessage &msg)
+{
+ m_messages.append(msg);
+}
+
+void Translator::appendSorted(const TranslatorMessage &msg)
+{
+ int msgLine = msg.lineNumber();
+ if (msgLine < 0) {
+ m_messages.append(msg);
+ return;
+ }
+
+ int bestIdx = 0; // Best insertion point found so far
+ int bestScore = 0; // Its category: 0 = no hit, 1 = pre or post, 2 = middle
+ int bestSize = 0; // The length of the region. Longer is better within one category.
+
+ // The insertion point to use should this region turn out to be the best one so far
+ int thisIdx = 0;
+ int thisScore = 0;
+ int thisSize = 0;
+ // Working vars
+ int prevLine = 0;
+ int curIdx = 0;
+ foreach (const TranslatorMessage &mit, m_messages) {
+ bool sameFile = mit.fileName() == msg.fileName();
+ int curLine;
+ if (sameFile && (curLine = mit.lineNumber()) >= prevLine) {
+ if (msgLine >= prevLine && msgLine < curLine) {
+ thisIdx = curIdx;
+ thisScore = thisSize ? 2 : 1;
+ }
+ ++thisSize;
+ prevLine = curLine;
+ } else {
+ if (thisSize) {
+ if (!thisScore) {
+ thisIdx = curIdx;
+ thisScore = 1;
+ }
+ if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize)) {
+ bestIdx = thisIdx;
+ bestScore = thisScore;
+ bestSize = thisSize;
+ }
+ thisScore = 0;
+ thisSize = sameFile ? 1 : 0;
+ prevLine = 0;
+ }
+ }
+ ++curIdx;
+ }
+ if (thisSize && !thisScore) {
+ thisIdx = curIdx;
+ thisScore = 1;
+ }
+ if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize))
+ m_messages.insert(thisIdx, msg);
+ else if (bestScore)
+ m_messages.insert(bestIdx, msg);
+ else
+ m_messages.append(msg);
+}
+
+static QString guessFormat(const QString &filename, const QString &format)
+{
+ if (format != QLatin1String("auto"))
+ return format;
+
+ foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
+ if (filename.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive))
+ return fmt.extension;
+ }
+
+ // the default format.
+ // FIXME: change to something more widely distributed later.
+ return QLatin1String("ts");
+}
+
+bool Translator::load(const QString &filename, ConversionData &cd, const QString &format)
+{
+ cd.m_sourceDir = QFileInfo(filename).absoluteDir();
+ cd.m_sourceFileName = filename;
+
+ QFile file;
+ if (filename.isEmpty() || filename == QLatin1String("-")) {
+ if (!file.open(stdin, QIODevice::ReadOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open stdin!? (%1)")
+ .arg(file.errorString()));
+ return false;
+ }
+ } else {
+ file.setFileName(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open %1: %2")
+ .arg(filename, file.errorString()));
+ return false;
+ }
+ }
+
+ QString fmt = guessFormat(filename, format);
+
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (fmt == format.extension) {
+ if (format.loader)
+ return (*format.loader)(*this, file, cd);
+ cd.appendError(QString(QLatin1String("No loader for format %1 found"))
+ .arg(fmt));
+ return false;
+ }
+ }
+
+ cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
+ .arg(format, filename));
+ return false;
+}
+
+
+bool Translator::save(const QString &filename, ConversionData &cd, const QString &format) const
+{
+ QFile file;
+ if (filename.isEmpty() || filename == QLatin1String("-")) {
+ if (!file.open(stdout, QIODevice::WriteOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open stdout!? (%1)")
+ .arg(file.errorString()));
+ return false;
+ }
+ } else {
+ file.setFileName(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot create %1: %2")
+ .arg(filename, file.errorString()));
+ return false;
+ }
+ }
+
+ QString fmt = guessFormat(filename, format);
+ cd.m_targetDir = QFileInfo(filename).absoluteDir();
+
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (fmt == format.extension) {
+ if (format.saver)
+ return (*format.saver)(*this, file, cd);
+ cd.appendError(QString(QLatin1String("Cannot save %1 files")).arg(fmt));
+ return false;
+ }
+ }
+
+ cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
+ .arg(format).arg(filename));
+ return false;
+}
+
+QString Translator::makeLanguageCode(QLocale::Language language, QLocale::Country country)
+{
+ QLocale locale(language, country);
+ if (country == QLocale::AnyCountry) {
+ QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0);
+ if (languageCode.length() <= 3)
+ return languageCode;
+ return QString();
+ } else {
+ return locale.name();
+ }
+}
+
+void Translator::languageAndCountry(const QString &languageCode,
+ QLocale::Language *lang, QLocale::Country *country)
+{
+ QLocale locale(languageCode);
+ if (lang)
+ *lang = locale.language();
+
+ if (country) {
+ if (languageCode.indexOf(QLatin1Char('_')) != -1)
+ *country = locale.country();
+ else
+ *country = QLocale::AnyCountry;
+ }
+}
+
+bool Translator::release(QFile *iod, ConversionData &cd) const
+{
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (format.extension == QLatin1String("qm"))
+ return (*format.saver)(*this, *iod, cd);
+ }
+ cd.appendError(QLatin1String("No .qm saver available."));
+ return false;
+}
+
+bool Translator::contains(const QString &context,
+ const QString &sourceText, const QString &comment) const
+{
+ return m_messages.contains(TranslatorMessage(context, sourceText, comment,
+ QString(), QString(), 0));
+}
+
+TranslatorMessage Translator::find(const QString &context,
+ const QString &sourceText, const QString &comment) const
+{
+ TranslatorMessage needle(context, sourceText, comment, QString(), QString(), 0);
+ int index = m_messages.indexOf(needle);
+ return index == -1 ? TranslatorMessage() : m_messages.at(index);
+}
+
+TranslatorMessage Translator::find(const QString &context,
+ const QString &comment, const TranslatorMessage::References &refs) const
+{
+ if (!refs.isEmpty()) {
+ for (TMM::ConstIterator it = m_messages.constBegin(); it != m_messages.constEnd(); ++it) {
+ if (it->context() == context && it->comment() == comment)
+ foreach (const TranslatorMessage::Reference &itref, it->allReferences())
+ foreach (const TranslatorMessage::Reference &ref, refs)
+ if (itref == ref)
+ return *it;
+ }
+ }
+ return TranslatorMessage();
+}
+
+bool Translator::contains(const QString &context) const
+{
+ foreach (const TranslatorMessage &msg, m_messages)
+ if (msg.context() == context && msg.sourceText().isEmpty())
+ return true;
+ return false;
+}
+
+TranslatorMessage Translator::find(const QString &context) const
+{
+ foreach (const TranslatorMessage &msg, m_messages)
+ if (msg.context() == context && msg.sourceText().isEmpty())
+ return msg;
+ return TranslatorMessage();
+}
+
+void Translator::stripObsoleteMessages()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() != TranslatorMessage::Obsolete)
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripFinishedMessages()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() != TranslatorMessage::Finished)
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripEmptyContexts()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->sourceText() != QLatin1String(ContextComment))
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripNonPluralForms()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->isPlural())
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripIdenticalSourceTranslations()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ // we need to have just one translation, and it be equal to the source
+ if (it->translations().count() != 1)
+ newmm.append(*it);
+ else if (it->translation() != it->sourceText())
+ newmm.append(*it);
+ }
+ m_messages = newmm;
+}
+
+void Translator::dropTranslations()
+{
+ for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ if (it->type() == TranslatorMessage::Finished)
+ it->setType(TranslatorMessage::Unfinished);
+ it->setTranslation(QString());
+ }
+}
+
+QList<TranslatorMessage> Translator::findDuplicates() const
+{
+ QHash<TranslatorMessage, int> dups;
+ foreach (const TranslatorMessage &msg, m_messages)
+ dups[msg]++;
+ QList<TranslatorMessage> ret;
+ QHash<TranslatorMessage, int>::ConstIterator it = dups.constBegin(), end = dups.constEnd();
+ for (; it != end; ++it)
+ if (it.value() > 1)
+ ret.append(it.key());
+ return ret;
+}
+
+// Used by lupdate to be able to search using absolute paths during merging
+void Translator::makeFileNamesAbsolute(const QDir &originalPath)
+{
+ TMM newmm;
+ for (TMM::iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ TranslatorMessage msg = *it;
+ msg.setReferences(TranslatorMessage::References());
+ foreach (const TranslatorMessage::Reference &ref, it->allReferences()) {
+ QString fileName = ref.fileName();
+ QFileInfo fi (fileName);
+ if (fi.isRelative())
+ fileName = originalPath.absoluteFilePath(fileName);
+ msg.addReference(fileName, ref.lineNumber());
+ }
+ newmm.append(msg);
+ }
+ m_messages = newmm;
+}
+
+QList<TranslatorMessage> Translator::messages() const
+{
+ return m_messages;
+}
+
+QList<TranslatorMessage> Translator::translatedMessages() const
+{
+ TMM result;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() == TranslatorMessage::Finished)
+ result.append(*it);
+ return result;
+}
+
+QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
+ QLocale::Language language, QLocale::Country country)
+{
+ 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
+ }
+
+ // make sure that the stringlist always have the size of the
+ // language's current numerus, or 1 if its not plural
+ if (translations.count() > numTranslations) {
+ for (int i = translations.count(); i > numTranslations; --i)
+ translations.removeLast();
+ } else if (translations.count() < numTranslations) {
+ for (int i = translations.count(); i < numTranslations; ++i)
+ translations.append(QString());
+ }
+ return translations;
+}
+
+QStringList Translator::normalizedTranslations(const TranslatorMessage &msg,
+ ConversionData &cd, bool *ok) const
+{
+ QLocale::Language l;
+ QLocale::Country c;
+ languageAndCountry(languageCode(), &l, &c);
+ QStringList translns = normalizedTranslations(msg, l, c);
+ if (msg.translations().size() > translns.size() && ok) {
+ 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)
+{
+ QString str = filename;
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (str.endsWith(format.extension)) {
+ str = str.left(str.size() - format.extension.size() - 1);
+ break;
+ }
+ }
+ static QRegExp re(QLatin1String("[\\._]"));
+ while (true) {
+ QLocale locale(str);
+ //qDebug() << "LANGUAGE FROM " << str << "LANG: " << locale.language();
+ if (locale.language() != QLocale::C) {
+ //qDebug() << "FOUND " << locale.name();
+ return locale.name();
+ }
+ int pos = str.indexOf(re);
+ if (pos == -1)
+ break;
+ str = str.mid(pos + 1);
+ }
+ //qDebug() << "LANGUAGE GUESSING UNSUCCESSFUL";
+ return QString();
+}
+
+bool Translator::hasExtra(const QString &key) const
+{
+ return m_extra.contains(key);
+}
+
+QString Translator::extra(const QString &key) const
+{
+ return m_extra[key];
+}
+
+void Translator::setExtra(const QString &key, const QString &value)
+{
+ m_extra[key] = value;
+}
+
+void Translator::setCodecName(const QByteArray &name)
+{
+ QTextCodec *codec = QTextCodec::codecForName(name);
+ if (!codec) {
+ if (!name.isEmpty())
+ qWarning("No QTextCodec for %s available. Using Latin1\n", name.constData());
+ m_codecName.clear();
+ } else {
+ m_codecName = codec->name();
+ }
+}
+
+void Translator::dump() const
+{
+ for (int i = 0; i != messageCount(); ++i)
+ message(i).dump();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h
new file mode 100644
index 0000000000..f4279da436
--- /dev/null
+++ b/tools/linguist/shared/translator.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METATRANSLATOR_H
+#define METATRANSLATOR_H
+
+#include "translatormessage.h"
+
+#include <QDir>
+#include <QList>
+#include <QLocale>
+#include <QString>
+
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE);
+
+class QIODevice;
+
+// A struct of "interesting" data passed to and from the load and save routines
+class ConversionData
+{
+public:
+ ConversionData() :
+ m_verbose(false),
+ m_ignoreUnfinished(false),
+ m_sortContexts(false),
+ m_noUiLines(false),
+ m_saveMode(SaveEverything)
+ {}
+
+ // tag manipulation
+ const QStringList &dropTags() const { return m_dropTags; }
+ QStringList &dropTags() { return m_dropTags; }
+ const QDir &targetDir() const { return m_targetDir; }
+ bool isVerbose() const { return m_verbose; }
+ bool ignoreUnfinished() const { return m_ignoreUnfinished; }
+ bool sortContexts() const { return m_sortContexts; }
+
+ void appendError(const QString &error) { m_errors.append(error); }
+ QString error() const { return m_errors.join(QLatin1String("\n")); }
+ QStringList errors() const { return m_errors; }
+ void clearErrors() { m_errors.clear(); }
+
+public:
+ QString m_defaultContext;
+ QByteArray m_codecForSource; // CPP specific
+ QString m_sourceFileName;
+ QString m_targetFileName;
+ QDir m_sourceDir;
+ QDir m_targetDir; // FIXME: TS spefic
+ QStringList m_dropTags; // tags to be dropped
+ QStringList m_errors;
+ bool m_verbose;
+ bool m_ignoreUnfinished;
+ bool m_sortContexts;
+ bool m_noUiLines;
+ TranslatorSaveMode m_saveMode;
+};
+
+class Translator
+{
+public:
+ Translator();
+
+ bool load(const QString &filename, ConversionData &err, const QString &format /*= "auto"*/);
+ bool save(const QString &filename, ConversionData &err, const QString &format /*= "auto"*/) const;
+ bool release(QFile *iod, ConversionData &cd) const;
+
+ bool contains(const QString &context, const QString &sourceText,
+ const QString &comment) const;
+ TranslatorMessage find(const QString &context,
+ const QString &sourceText, const QString &comment) const;
+
+ TranslatorMessage find(const QString &context,
+ const QString &comment, const TranslatorMessage::References &refs) const;
+
+ bool contains(const QString &context) const;
+ TranslatorMessage find(const QString &context) const;
+
+ void replace(const TranslatorMessage &msg);
+ void replaceSorted(const TranslatorMessage &msg);
+ void extend(const TranslatorMessage &msg); // Only for single-location messages
+ void append(const TranslatorMessage &msg);
+ void appendSorted(const TranslatorMessage &msg);
+
+ void stripObsoleteMessages();
+ void stripFinishedMessages();
+ void stripEmptyContexts();
+ void stripNonPluralForms();
+ void stripIdenticalSourceTranslations();
+ void dropTranslations();
+ QList<TranslatorMessage> findDuplicates() const;
+ void makeFileNamesAbsolute(const QDir &originalPath);
+
+ void setCodecName(const QByteArray &name);
+ QByteArray codecName() const { return m_codecName; }
+
+ QString languageCode() const { return m_language; }
+ QString sourceLanguageCode() const { return m_sourceLanguage; }
+
+ enum LocationsType { NoLocations, RelativeLocations, AbsoluteLocations };
+ void setLocationsType(LocationsType lt) { m_locationsType = lt; }
+ LocationsType locationsType() const { return m_locationsType; }
+
+ static QString makeLanguageCode(QLocale::Language language, QLocale::Country country);
+ static void languageAndCountry(const QString &languageCode,
+ QLocale::Language *lang, QLocale::Country *country);
+ void setLanguageCode(const QString &languageCode) { m_language = languageCode; }
+ void setSourceLanguageCode(const QString &languageCode) { m_sourceLanguage = languageCode; }
+ 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);
+ QStringList normalizedTranslations(const TranslatorMessage &m, ConversionData &cd, bool *ok) const;
+
+ int messageCount() const { return m_messages.size(); }
+ TranslatorMessage &message(int i) { return m_messages[i]; }
+ const TranslatorMessage &message(int i) const { return m_messages.at(i); }
+ void dump() const;
+
+ // additional file format specific data
+ // note: use '<fileformat>:' as prefix for file format specific members,
+ // e.g. "po-flags", "po-msgid_plural"
+ typedef TranslatorMessage::ExtraData ExtraData;
+ QString extra(const QString &ba) const;
+ void setExtra(const QString &ba, const QString &var);
+ bool hasExtra(const QString &ba) const;
+ const ExtraData &extras() const { return m_extra; }
+ void setExtras(const ExtraData &extras) { m_extra = extras; }
+
+ // registration of file formats
+ typedef bool (*SaveFunction)(const Translator &, QIODevice &out, ConversionData &data);
+ typedef bool (*LoadFunction)(Translator &, QIODevice &in, ConversionData &data);
+ struct FileFormat {
+ FileFormat() : loader(0), saver(0), priority(-1) {}
+ QString extension; // such as "ts", "xlf", ...
+ QString description; // human-readable description
+ LoadFunction loader;
+ SaveFunction saver;
+ enum FileType { SourceCode, TranslationSource, TranslationBinary } fileType;
+ int priority; // 0 = highest, -1 = invisible
+ };
+ static void registerFileFormat(const FileFormat &format);
+ static QList<FileFormat> &registeredFileFormats();
+
+ enum VariantSeparators {
+ DefaultVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
+ InternalVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
+ };
+
+private:
+ typedef QList<TranslatorMessage> TMM; // int stores the sequence position.
+
+ TMM m_messages;
+ QByteArray m_codecName;
+ LocationsType m_locationsType;
+
+ // A string beginning with a 2 or 3 letter language code (ISO 639-1
+ // or ISO-639-2), followed by the optional country variant to distinguish
+ // between country-specific variations of the language. The language code
+ // and country code are always separated by '_'
+ // Note that the language part can also be a 3-letter ISO 639-2 code.
+ // Legal examples:
+ // 'pt' portuguese, assumes portuguese from portugal
+ // 'pt_BR' Brazilian portuguese (ISO 639-1 language code)
+ // 'por_BR' Brazilian portuguese (ISO 639-2 language code)
+ QString m_language;
+ QString m_sourceLanguage;
+ ExtraData m_extra;
+};
+
+bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
+ QByteArray *rules, QStringList *forms);
+
+/*
+ This is a quick hack. The proper way to handle this would be
+ to extend Translator's interface.
+*/
+#define ContextComment "QT_LINGUIST_INTERNAL_CONTEXT_COMMENT"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/linguist/shared/translatormessage.cpp b/tools/linguist/shared/translatormessage.cpp
new file mode 100644
index 0000000000..ab4301fbb3
--- /dev/null
+++ b/tools/linguist/shared/translatormessage.cpp
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translatormessage.h"
+
+#include <qplatformdefs.h>
+
+#ifndef QT_NO_TRANSLATION
+
+#include <QDataStream>
+#include <QDebug>
+
+#include <stdlib.h>
+
+
+QT_BEGIN_NAMESPACE
+
+TranslatorMessage::TranslatorMessage()
+ : m_lineNumber(-1), m_type(Unfinished), m_utf8(false), m_plural(false)
+{
+}
+
+TranslatorMessage::TranslatorMessage(const QString &context,
+ const QString &sourceText, const QString &comment,
+ const QString &userData,
+ const QString &fileName, int lineNumber, const QStringList &translations,
+ Type type, bool plural)
+ : m_context(context), m_sourcetext(sourceText), m_comment(comment),
+ m_userData(userData),
+ m_translations(translations), m_fileName(fileName), m_lineNumber(lineNumber),
+ m_type(type), m_utf8(false), m_plural(plural)
+{
+}
+
+void TranslatorMessage::addReference(const QString &fileName, int lineNumber)
+{
+ if (m_fileName.isEmpty()) {
+ m_fileName = fileName;
+ m_lineNumber = lineNumber;
+ } else {
+ m_extraRefs.append(Reference(fileName, lineNumber));
+ }
+}
+
+void TranslatorMessage::addReferenceUniq(const QString &fileName, int lineNumber)
+{
+ if (m_fileName.isEmpty()) {
+ m_fileName = fileName;
+ m_lineNumber = lineNumber;
+ } else {
+ if (fileName == m_fileName && lineNumber == m_lineNumber)
+ return;
+ if (!m_extraRefs.isEmpty()) // Rather common case, so special-case it
+ foreach (const Reference &ref, m_extraRefs)
+ if (fileName == ref.fileName() && lineNumber == ref.lineNumber())
+ return;
+ m_extraRefs.append(Reference(fileName, lineNumber));
+ }
+}
+
+void TranslatorMessage::clearReferences()
+{
+ m_fileName.clear();
+ m_lineNumber = -1;
+ m_extraRefs.clear();
+}
+
+void TranslatorMessage::setReferences(const TranslatorMessage::References &refs0)
+{
+ if (!refs0.isEmpty()) {
+ References refs = refs0;
+ const Reference &ref = refs.takeFirst();
+ m_fileName = ref.fileName();
+ m_lineNumber = ref.lineNumber();
+ m_extraRefs = refs;
+ } else {
+ clearReferences();
+ }
+}
+
+TranslatorMessage::References TranslatorMessage::allReferences() const
+{
+ References refs;
+ if (!m_fileName.isEmpty()) {
+ refs.append(Reference(m_fileName, m_lineNumber));
+ refs += m_extraRefs;
+ }
+ return refs;
+}
+
+static bool needs8BitHelper(const QString &ba)
+{
+ for (int i = ba.size(); --i >= 0; )
+ if (ba.at(i).unicode() >= 0x80)
+ return true;
+ return false;
+}
+
+bool TranslatorMessage::needs8Bit() const
+{
+ //dump();
+ return needs8BitHelper(m_sourcetext)
+ || needs8BitHelper(m_comment)
+ || needs8BitHelper(m_context);
+}
+
+
+bool TranslatorMessage::operator==(const TranslatorMessage& m) const
+{
+ static QString msgIdPlural = QLatin1String("po-msgid_plural");
+
+ // Special treatment for context comments (empty source).
+ return (m_context == m.m_context)
+ && m_sourcetext == m.m_sourcetext
+ && m_extra[msgIdPlural] == m.m_extra[msgIdPlural]
+ && (m_sourcetext.isEmpty() || m_comment == m.m_comment);
+}
+
+
+bool TranslatorMessage::operator<(const TranslatorMessage& m) const
+{
+ if (m_context != m.m_context)
+ return m_context < m.m_context;
+ if (m_sourcetext != m.m_sourcetext)
+ return m_sourcetext < m.m_sourcetext;
+ return m_comment < m.m_comment;
+}
+
+int qHash(const TranslatorMessage &msg)
+{
+ return
+ qHash(msg.context()) ^
+ qHash(msg.sourceText()) ^
+ qHash(msg.extra(QLatin1String("po-msgid_plural"))) ^
+ qHash(msg.comment());
+}
+
+bool TranslatorMessage::hasExtra(const QString &key) const
+{
+ return m_extra.contains(key);
+}
+
+QString TranslatorMessage::extra(const QString &key) const
+{
+ return m_extra[key];
+}
+
+void TranslatorMessage::setExtra(const QString &key, const QString &value)
+{
+ m_extra[key] = value;
+}
+
+void TranslatorMessage::unsetExtra(const QString &key)
+{
+ m_extra.remove(key);
+}
+
+void TranslatorMessage::dump() const
+{
+ qDebug()
+ << "\nId : " << m_id
+ << "\nContext : " << m_context
+ << "\nSource : " << m_sourcetext
+ << "\nComment : " << m_comment
+ << "\nUserData : " << m_userData
+ << "\nExtraComment : " << m_extraComment
+ << "\nTranslatorComment : " << m_translatorComment
+ << "\nTranslations : " << m_translations
+ << "\nFileName : " << m_fileName
+ << "\nLineNumber : " << m_lineNumber
+ << "\nType : " << m_type
+ << "\nPlural : " << m_plural
+ << "\nExtra : " << m_extra;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TRANSLATION
diff --git a/tools/linguist/shared/translatormessage.h b/tools/linguist/shared/translatormessage.h
new file mode 100644
index 0000000000..fa37ff5ce7
--- /dev/null
+++ b/tools/linguist/shared/translatormessage.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRANSLATORMESSAGE_H
+#define TRANSLATORMESSAGE_H
+
+#ifndef QT_NO_TRANSLATION
+
+#include <QString>
+#include <QStringList>
+#include <QHash>
+
+
+QT_BEGIN_NAMESPACE
+
+enum TranslatorSaveMode { SaveEverything, SaveStripped };
+
+class TranslatorMessage
+{
+public:
+ enum Type { Unfinished, Finished, Obsolete };
+ typedef QHash<QString, QString> ExtraData;
+ class Reference
+ {
+ QString m_fileName;
+ int m_lineNumber;
+ public:
+ Reference(const QString &n, int l) : m_fileName(n), m_lineNumber(l) {}
+ bool operator==(const Reference &other) const
+ { return fileName() == other.fileName() && lineNumber() == other.lineNumber(); }
+ QString fileName() const { return m_fileName; }
+ int lineNumber() const { return m_lineNumber; }
+ };
+ typedef QList<Reference> References;
+
+ TranslatorMessage();
+ TranslatorMessage(const QString &context, const QString &sourceText,
+ const QString &comment, const QString &userData,
+ const QString &fileName, int lineNumber,
+ const QStringList &translations = QStringList(),
+ Type type = Unfinished, bool plural = false);
+
+ uint hash() const;
+
+ QString id() const { return m_id; }
+ void setId(const QString &id) { m_id = id; }
+
+ QString context() const { return m_context; }
+ void setContext(const QString &context) { m_context = context; }
+
+ QString sourceText() const { return m_sourcetext; }
+ void setSourceText(const QString &sourcetext) { m_sourcetext = sourcetext; }
+ QString oldSourceText() const { return m_oldsourcetext; }
+ void setOldSourceText(const QString &oldsourcetext) { m_oldsourcetext = oldsourcetext; }
+
+ QString comment() const { return m_comment; }
+ void setComment(const QString &comment) { m_comment = comment; }
+ QString oldComment() const { return m_oldcomment; }
+ void setOldComment(const QString &oldcomment) { m_oldcomment = oldcomment; }
+
+ QStringList translations() const { return m_translations; }
+ void setTranslations(const QStringList &translations) { m_translations = translations; }
+ QString translation() const { return m_translations.value(0); }
+ void setTranslation(const QString &translation) { m_translations = QStringList(translation); }
+ void appendTranslation(const QString &translation) { m_translations.append(translation); }
+ bool isTranslated() const
+ {
+ foreach (const QString &trans, m_translations)
+ if (!trans.isEmpty())
+ return true;
+ return false;
+ }
+
+ bool operator==(const TranslatorMessage& m) const;
+ bool operator<(const TranslatorMessage& m) const;
+
+ QString fileName() const { return m_fileName; }
+ void setFileName(const QString &fileName) { m_fileName = fileName; }
+ int lineNumber() const { return m_lineNumber; }
+ void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
+ void clearReferences();
+ void setReferences(const References &refs);
+ void addReference(const QString &fileName, int lineNumber);
+ void addReference(const Reference &ref) { addReference(ref.fileName(), ref.lineNumber()); }
+ void addReferenceUniq(const QString &fileName, int lineNumber);
+ References extraReferences() const { return m_extraRefs; }
+ References allReferences() const;
+ QString userData() const { return m_userData; }
+ void setUserData(const QString &userData) { m_userData = userData; }
+ QString extraComment() const { return m_extraComment; }
+ void setExtraComment(const QString &extraComment) { m_extraComment = extraComment; }
+ QString translatorComment() const { return m_translatorComment; }
+ void setTranslatorComment(const QString &translatorComment) { m_translatorComment = translatorComment; }
+
+ bool isNull() const { return m_sourcetext.isNull() && m_lineNumber == -1 && m_translations.isEmpty(); }
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+ bool isUtf8() const { return m_utf8; } // codecForTr override
+ void setUtf8(bool on) { m_utf8 = on; }
+ bool isPlural() const { return m_plural; }
+ void setPlural(bool isplural) { m_plural = isplural; }
+
+ // note: use '<fileformat>:' as prefix for file format specific members,
+ // e.g. "po-msgid_plural"
+ QString extra(const QString &ba) const;
+ void setExtra(const QString &ba, const QString &var);
+ bool hasExtra(const QString &ba) const;
+ const ExtraData &extras() const { return m_extra; }
+ void setExtras(const ExtraData &extras) { m_extra = extras; }
+ void unsetExtra(const QString &key);
+
+ bool needs8Bit() const;
+ void dump() const;
+
+private:
+ QString m_id;
+ QString m_context;
+ QString m_sourcetext;
+ QString m_oldsourcetext;
+ QString m_comment;
+ QString m_oldcomment;
+ QString m_userData;
+ ExtraData m_extra; // PO flags, PO plurals
+ QString m_extraComment;
+ QString m_translatorComment;
+ QStringList m_translations;
+ QString m_fileName;
+ int m_lineNumber;
+ References m_extraRefs;
+
+ Type m_type;
+ bool m_utf8;
+ bool m_plural;
+};
+
+int qHash(const TranslatorMessage &msg);
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TRANSLATION
+
+#endif // TRANSLATORMESSAGE_H
diff --git a/tools/linguist/shared/translatortools.cpp b/tools/linguist/shared/translatortools.cpp
new file mode 100644
index 0000000000..dcff5468b4
--- /dev/null
+++ b/tools/linguist/shared/translatortools.cpp
@@ -0,0 +1,505 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translatortools.h"
+
+#include "simtexth.h"
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMap>
+#include <QtCore/QStringList>
+#include <QtCore/QTextCodec>
+#include <QtCore/QVector>
+
+typedef QList<TranslatorMessage> TML;
+typedef QMap<QString, TranslatorMessage> TMM;
+
+
+QT_BEGIN_NAMESPACE
+
+static bool isDigitFriendly(QChar c)
+{
+ return c.isPunct() || c.isSpace();
+}
+
+static int numberLength(const QString &s, int i)
+{
+ if (i < s.size() || !s.at(i).isDigit())
+ return 0;
+
+ int pos = i;
+ do {
+ ++i;
+ } while (i < s.size()
+ && (s.at(i).isDigit()
+ || (isDigitFriendly(s[i])
+ && i + 1 < s.size()
+ && (s[i + 1].isDigit()
+ || (isDigitFriendly(s[i + 1])
+ && i + 2 < s.size()
+ && s[i + 2].isDigit())))));
+ return i - pos;
+}
+
+
+/*
+ Returns a version of 'key' where all numbers have been replaced by zeroes. If
+ there were none, returns "".
+*/
+static QString zeroKey(const QString &key)
+{
+ QString zeroed;
+ bool metSomething = false;
+
+ for (int i = 0; i != key.size(); ++i) {
+ int len = numberLength(key, i);
+ if (len > 0) {
+ i += len;
+ zeroed.append(QLatin1Char('0'));
+ metSomething = true;
+ } else {
+ zeroed.append(key.at(i));
+ }
+ }
+ return metSomething ? zeroed : QString();
+}
+
+static QString translationAttempt(const QString &oldTranslation,
+ const QString &oldSource, const QString &newSource)
+{
+ int p = zeroKey(oldSource).count(QLatin1Char('0'));
+ QString attempt;
+ QStringList oldNumbers;
+ QStringList newNumbers;
+ QVector<bool> met(p);
+ QVector<int> matchedYet(p);
+ int i, j;
+ int k = 0, ell, best;
+ int m, n;
+ int pass;
+
+ /*
+ This algorithm is hard to follow, so we'll consider an example
+ all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0"
+ and newSource is "XeT 3.1".
+
+ First, we set up two tables: oldNumbers and newNumbers. In our
+ example, oldNumber[0] is "3.0" and newNumber[0] is "3.1".
+ */
+ for (i = 0, j = 0; i < oldSource.size(); i++, j++) {
+ m = numberLength(oldSource, i);
+ n = numberLength(newSource, j);
+ if (m > 0) {
+ oldNumbers.append(oldSource.mid(i, m + 1));
+ newNumbers.append(newSource.mid(j, n + 1));
+ i += m;
+ j += n;
+ met[k] = false;
+ matchedYet[k] = 0;
+ k++;
+ }
+ }
+
+ /*
+ We now go over the old translation, "XeT 3.0", one letter at a
+ time, looking for numbers found in oldNumbers. Whenever such a
+ number is met, it is replaced with its newNumber equivalent. In
+ our example, the "3.0" of "XeT 3.0" becomes "3.1".
+ */
+ for (i = 0; i < oldTranslation.length(); i++) {
+ attempt += oldTranslation[i];
+ for (k = 0; k < p; k++) {
+ if (oldTranslation[i] == oldNumbers[k][matchedYet[k]])
+ matchedYet[k]++;
+ else
+ matchedYet[k] = 0;
+ }
+
+ /*
+ Let's find out if the last character ended a match. We make
+ two passes over the data. In the first pass, we try to
+ match only numbers that weren't matched yet; if that fails,
+ the second pass does the trick. This is useful in some
+ suspicious cases, flagged below.
+ */
+ for (pass = 0; pass < 2; pass++) {
+ best = p; // an impossible value
+ for (k = 0; k < p; k++) {
+ if ((!met[k] || pass > 0) &&
+ matchedYet[k] == oldNumbers[k].length() &&
+ numberLength(oldTranslation, i + 1 - matchedYet[k]) == matchedYet[k]) {
+ // the longer the better
+ if (best == p || matchedYet[k] > matchedYet[best])
+ best = k;
+ }
+ }
+ if (best != p) {
+ attempt.truncate(attempt.length() - matchedYet[best]);
+ attempt += newNumbers[best];
+ met[best] = true;
+ for (k = 0; k < p; k++)
+ matchedYet[k] = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ We flag two kinds of suspicious cases. They are identified as
+ such with comments such as "{2000?}" at the end.
+
+ Example of the first kind: old source text "TeX 3.0" translated
+ as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the
+ new text is.
+ */
+ for (k = 0; k < p; k++) {
+ if (!met[k])
+ attempt += QString(QLatin1String(" {")) + newNumbers[k] + QString(QLatin1String("?}"));
+ }
+
+ /*
+ Example of the second kind: "1 of 1" translated as "1 af 1",
+ with new source text "1 of 2", generates "1 af 2 {1 or 2?}"
+ because it's not clear which of "1 af 2" and "2 af 1" is right.
+ */
+ for (k = 0; k < p; k++) {
+ for (ell = 0; ell < p; ell++) {
+ if (k != ell && oldNumbers[k] == oldNumbers[ell] &&
+ newNumbers[k] < newNumbers[ell])
+ attempt += QString(QLatin1String(" {")) + newNumbers[k] + QString(QLatin1String(" or ")) +
+ newNumbers[ell] + QString(QLatin1String("?}"));
+ }
+ }
+ return attempt;
+}
+
+
+/*
+ Augments a Translator with translations easily derived from
+ similar existing (probably obsolete) translations.
+
+ For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1"
+ has no translation, "XeT 3.1" is added to the translator and is
+ marked Unfinished.
+
+ Returns the number of additional messages that this heuristic translated.
+*/
+int applyNumberHeuristic(Translator &tor)
+{
+ TMM translated, untranslated;
+ TMM::Iterator t, u;
+ TML all = tor.messages();
+ TML::Iterator it;
+ int inserted = 0;
+
+ for (it = all.begin(); it != all.end(); ++it) {
+ bool hasTranslation = it->isTranslated();
+ if (it->type() == TranslatorMessage::Unfinished) {
+ if (!hasTranslation)
+ untranslated.insert(it->context() + QLatin1Char('\n')
+ + it->sourceText() + QLatin1Char('\n')
+ + it->comment(), *it);
+ } else if (hasTranslation && it->translations().count() == 1) {
+ translated.insert(zeroKey(it->sourceText()), *it);
+ }
+ }
+
+ for (u = untranslated.begin(); u != untranslated.end(); ++u) {
+ t = translated.find(zeroKey((*u).sourceText()));
+ if (t != translated.end() && !t.key().isEmpty()
+ && t->sourceText() != u->sourceText()) {
+ TranslatorMessage m = *u;
+ m.setTranslation(translationAttempt(t->translation(), t->sourceText(),
+ u->sourceText()));
+ tor.replace(m);
+ inserted++;
+ }
+ }
+ return inserted;
+}
+
+
+/*
+ Augments a Translator with trivially derived translations.
+
+ For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no
+ matter the context or the comment, "Eingeschaltet:" is added as the
+ translation of any untranslated "Enabled:" text and is marked Unfinished.
+
+ Returns the number of additional messages that this heuristic translated.
+*/
+
+int applySameTextHeuristic(Translator &tor)
+{
+ TMM translated;
+ TMM avoid;
+ TMM::Iterator t;
+ TML untranslated;
+ TML::Iterator u;
+ TML all = tor.messages();
+ TML::Iterator it;
+ int inserted = 0;
+
+ for (it = all.begin(); it != all.end(); ++it) {
+ if (!it->isTranslated()) {
+ if (it->type() == TranslatorMessage::Unfinished)
+ untranslated.append(*it);
+ } else {
+ QString key = it->sourceText();
+ t = translated.find(key);
+ if (t != translated.end()) {
+ /*
+ The same source text is translated at least two
+ different ways. Do nothing then.
+ */
+ if (t->translations() != it->translations()) {
+ translated.remove(key);
+ avoid.insert(key, *it);
+ }
+ } else if (!avoid.contains(key)) {
+ translated.insert(key, *it);
+ }
+ }
+ }
+
+ for (u = untranslated.begin(); u != untranslated.end(); ++u) {
+ QString key = u->sourceText();
+ t = translated.find(key);
+ if (t != translated.end()) {
+ TranslatorMessage m = *u;
+ m.setTranslations(t->translations());
+ tor.replace(m);
+ ++inserted;
+ }
+ }
+ return inserted;
+}
+
+
+
+/*
+ Merges two Translator objects. The first one
+ is a set of source texts and translations for a previous version of
+ the internationalized program; the second one is a set of fresh
+ source texts newly extracted from the source code, without any
+ translation yet.
+*/
+
+Translator merge(const Translator &tor, const Translator &virginTor,
+ UpdateOptions options, QString &err)
+{
+ int known = 0;
+ int neww = 0;
+ int obsoleted = 0;
+ int similarTextHeuristicCount = 0;
+
+ Translator outTor;
+ outTor.setLanguageCode(tor.languageCode());
+ outTor.setSourceLanguageCode(tor.sourceLanguageCode());
+ outTor.setLocationsType(tor.locationsType());
+ outTor.setCodecName(tor.codecName());
+
+ /*
+ The types of all the messages from the vernacular translator
+ are updated according to the virgin translator.
+ */
+ foreach (TranslatorMessage m, tor.messages()) {
+ TranslatorMessage::Type newType = TranslatorMessage::Finished;
+
+ if (m.sourceText().isEmpty()) {
+ // context/file comment
+ TranslatorMessage mv = virginTor.find(m.context());
+ if (!mv.isNull())
+ m.setComment(mv.comment());
+ } else {
+ TranslatorMessage mv = virginTor.find(m.context(), m.sourceText(), m.comment());
+ if (mv.isNull()) {
+ if (!(options & HeuristicSimilarText)) {
+ newType = TranslatorMessage::Obsolete;
+ if (m.type() != TranslatorMessage::Obsolete)
+ obsoleted++;
+ m.clearReferences();
+ } else {
+ mv = virginTor.find(m.context(), m.comment(), m.allReferences());
+ if (mv.isNull()) {
+ // did not find it in the virgin, mark it as obsolete
+ newType = TranslatorMessage::Obsolete;
+ if (m.type() != TranslatorMessage::Obsolete)
+ obsoleted++;
+ m.clearReferences();
+ } else {
+ // Do not just accept it if its on the same line number,
+ // but different source text.
+ // Also check if the texts are more or less similar before
+ // we consider them to represent the same message...
+ if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold) {
+ // It is just slightly modified, assume that it is the same string
+
+ // Mark it as unfinished. (Since the source text
+ // was changed it might require re-translating...)
+ newType = TranslatorMessage::Unfinished;
+ ++similarTextHeuristicCount;
+ neww++;
+
+ m.setOldSourceText(m.sourceText());
+ m.setSourceText(mv.sourceText());
+ const QString &oldpluralsource = m.extra(QLatin1String("po-msgid_plural"));
+ if (!oldpluralsource.isEmpty()) {
+ m.setExtra(QLatin1String("po-old_msgid_plural"), oldpluralsource);
+ m.unsetExtra(QLatin1String("po-msgid_plural"));
+ }
+ m.setReferences(mv.allReferences()); // Update secondary references
+ m.setPlural(mv.isPlural());
+ m.setUtf8(mv.isUtf8());
+ m.setExtraComment(mv.extraComment());
+ } else {
+ // The virgin and vernacular sourceTexts are so
+ // different that we could not find it.
+ newType = TranslatorMessage::Obsolete;
+ if (m.type() != TranslatorMessage::Obsolete)
+ obsoleted++;
+ m.clearReferences();
+ }
+ }
+ }
+ } else {
+ switch (m.type()) {
+ case TranslatorMessage::Finished:
+ default:
+ if (m.isPlural() == mv.isPlural()) {
+ newType = TranslatorMessage::Finished;
+ } else {
+ newType = TranslatorMessage::Unfinished;
+ }
+ known++;
+ break;
+ case TranslatorMessage::Unfinished:
+ newType = TranslatorMessage::Unfinished;
+ known++;
+ break;
+ case TranslatorMessage::Obsolete:
+ newType = TranslatorMessage::Unfinished;
+ neww++;
+ }
+
+ // Always get the filename and linenumber info from the
+ // virgin Translator, in case it has changed location.
+ // This should also enable us to read a file that does not
+ // have the <location> element.
+ // why not use operator=()? Because it overwrites e.g. userData.
+ m.setReferences(mv.allReferences());
+ m.setPlural(mv.isPlural());
+ m.setUtf8(mv.isUtf8());
+ m.setExtraComment(mv.extraComment());
+ }
+ }
+
+ m.setType(newType);
+ outTor.append(m);
+ }
+
+ /*
+ Messages found only in the virgin translator are added to the
+ vernacular translator.
+ */
+ foreach (const TranslatorMessage &mv, virginTor.messages()) {
+ if (mv.sourceText().isEmpty()) {
+ if (tor.contains(mv.context()))
+ continue;
+ } else {
+ if (tor.contains(mv.context(), mv.sourceText(), mv.comment()))
+ continue;
+ if (options & HeuristicSimilarText) {
+ TranslatorMessage m = tor.find(mv.context(), mv.comment(), mv.allReferences());
+ if (!m.isNull()) {
+ if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold)
+ continue;
+ }
+ }
+ }
+ if (options & NoLocations)
+ outTor.append(mv);
+ else
+ outTor.appendSorted(mv);
+ if (!mv.sourceText().isEmpty())
+ ++neww;
+ }
+
+ /*
+ The same-text heuristic handles cases where a message has an
+ obsolete counterpart with a different context or comment.
+ */
+ int sameTextHeuristicCount = (options & HeuristicSameText) ? applySameTextHeuristic(outTor) : 0;
+
+ /*
+ The number heuristic handles cases where a message has an
+ obsolete counterpart with mostly numbers differing in the
+ source text.
+ */
+ int sameNumberHeuristicCount = (options & HeuristicNumber) ? applyNumberHeuristic(outTor) : 0;
+
+ if (options & Verbose) {
+ int totalFound = neww + known;
+ err += QObject::tr(" Found %n source text(s) (%1 new and %2 already existing)\n", 0, totalFound).arg(neww).arg(known);
+
+ if (obsoleted) {
+ if (options & NoObsolete) {
+ err += QObject::tr(" Removed %n obsolete entries\n", 0, obsoleted);
+ } else {
+ err += QObject::tr(" Kept %n obsolete entries\n", 0, obsoleted);
+ }
+ }
+
+ if (sameNumberHeuristicCount)
+ err += QObject::tr(" Number heuristic provided %n translation(s)\n",
+ 0, sameNumberHeuristicCount);
+ if (sameTextHeuristicCount)
+ err += QObject::tr(" Same-text heuristic provided %n translation(s)\n",
+ 0, sameTextHeuristicCount);
+ if (similarTextHeuristicCount)
+ err += QObject::tr(" Similar-text heuristic provided %n translation(s)\n",
+ 0, similarTextHeuristicCount);
+ }
+ return outTor;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/translatortools.h b/tools/linguist/shared/translatortools.h
new file mode 100644
index 0000000000..9eaf024061
--- /dev/null
+++ b/tools/linguist/shared/translatortools.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LUPDATE_H
+#define LUPDATE_H
+
+#include "qglobal.h"
+
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+class Translator;
+class TranslatorMessage;
+
+enum UpdateOption {
+ Verbose = 1,
+ NoObsolete = 2,
+ PluralOnly = 4,
+ NoSort = 8,
+ HeuristicSameText = 16,
+ HeuristicSimilarText = 32,
+ HeuristicNumber = 64,
+ AbsoluteLocations = 256,
+ RelativeLocations = 512,
+ NoLocations = 1024,
+ NoUiLines = 2048
+};
+
+Q_DECLARE_FLAGS(UpdateOptions, UpdateOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions)
+
+Translator merge(const Translator &tor, const Translator &virginTor,
+ UpdateOptions options, QString &err);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tools/linguist/shared/translatortools.pri b/tools/linguist/shared/translatortools.pri
new file mode 100644
index 0000000000..2b6de8caa9
--- /dev/null
+++ b/tools/linguist/shared/translatortools.pri
@@ -0,0 +1,11 @@
+
+
+INCLUDEPATH *= $$PWD
+
+SOURCES += \
+ $$PWD/translatortools.cpp \
+ $$PWD/simtexth.cpp
+
+HEADERS += \
+ $$PWD/translatortools.h
+
diff --git a/tools/linguist/shared/ts.cpp b/tools/linguist/shared/ts.cpp
new file mode 100644
index 0000000000..2e7d40f331
--- /dev/null
+++ b/tools/linguist/shared/ts.cpp
@@ -0,0 +1,755 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamAttribute>
+
+#define STRINGIFY_INTERNAL(x) #x
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * The encodings are a total mess.
+ * A Translator has a codecForTr(). Each message's text will be passed to tr()
+ * in that encoding or as UTF-8 to trUtf8() if it is flagged as such.
+ * For ts 2.0, the file content is always uniformly in UTF-8. The file stores
+ * the codecForTr default and marks deviating messages accordingly.
+ * For ts 1.1, the file content is in mixed encoding. Each message is encoded
+ * the way it will be passed to tr() (with 8-bit characters encoded as numeric
+ * entities) or trUtf8(). The file stores the encoding and codecForTr in one
+ * attribute, for both the default and each deviating message.
+ */
+
+
+QDebug &operator<<(QDebug &d, const QXmlStreamAttribute &attr)
+{
+ return d << "[" << attr.name().toString() << "," << attr.value().toString() << "]";
+}
+
+
+class TSReader : public QXmlStreamReader
+{
+public:
+ TSReader(QIODevice &dev, ConversionData &cd)
+ : QXmlStreamReader(&dev), m_cd(cd)
+ {}
+
+ // the "real thing"
+ bool read(Translator &translator);
+
+private:
+ bool elementStarts(const QString &str) const
+ {
+ return isStartElement() && name() == str;
+ }
+
+ bool isWhiteSpace() const
+ {
+ return isCharacters() && text().toString().trimmed().isEmpty();
+ }
+
+ // needed to expand <byte ... />
+ QString readContents();
+ // needed to join <lengthvariant>s
+ QString readTransContents();
+
+ void handleError();
+
+ ConversionData &m_cd;
+};
+
+void TSReader::handleError()
+{
+ if (isComment())
+ return;
+ if (hasError() && error() == CustomError) // raised by readContents
+ return;
+
+ const QString loc = QString::fromLatin1("at %3:%1:%2")
+ .arg(lineNumber()).arg(columnNumber()).arg(m_cd.m_sourceFileName);
+
+ switch (tokenType()) {
+ case NoToken: // Cannot happen
+ default: // likewise
+ case Invalid:
+ raiseError(QString::fromLatin1("Parse error %1: %2").arg(loc, errorString()));
+ break;
+ case StartElement:
+ raiseError(QString::fromLatin1("Unexpected tag <%1> %2").arg(name().toString(), loc));
+ break;
+ case Characters:
+ {
+ QString tok = text().toString();
+ if (tok.length() > 30)
+ tok = tok.left(30) + QLatin1String("[...]");
+ raiseError(QString::fromLatin1("Unexpected characters '%1' %2").arg(tok, loc));
+ }
+ break;
+ case EntityReference:
+ raiseError(QString::fromLatin1("Unexpected entity '&%1;' %2").arg(name().toString(), loc));
+ break;
+ case ProcessingInstruction:
+ raiseError(QString::fromLatin1("Unexpected processing instruction %1").arg(loc));
+ break;
+ }
+}
+
+static QString byteValue(QString value)
+{
+ int base = 10;
+ if (value.startsWith(QLatin1String("x"))) {
+ base = 16;
+ value.remove(0, 1);
+ }
+ int n = value.toUInt(0, base);
+ return (n != 0) ? QString(QChar(n)) : QString();
+}
+
+QString TSReader::readContents()
+{
+ STRING(byte);
+ STRING(value);
+
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isCharacters()) {
+ result += text();
+ } else if (elementStarts(strbyte)) {
+ // <byte value="...">
+ result += byteValue(attributes().value(strvalue).toString());
+ readNext();
+ if (!isEndElement()) {
+ handleError();
+ break;
+ }
+ } else {
+ handleError();
+ break;
+ }
+ }
+ //qDebug() << "TEXT: " << result;
+ return result;
+}
+
+QString TSReader::readTransContents()
+{
+ STRING(lengthvariant);
+ STRING(variants);
+ STRING(yes);
+
+ if (attributes().value(strvariants) == stryes) {
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strlengthvariant)) {
+ if (!result.isEmpty())
+ result += QChar(Translator::DefaultVariantSeparator);
+ result += readContents();
+ } else {
+ handleError();
+ break;
+ }
+ }
+ return result;
+ } else {
+ return readContents();
+ }
+}
+
+bool TSReader::read(Translator &translator)
+{
+ STRING(byte);
+ STRING(comment);
+ STRING(context);
+ STRING(defaultcodec);
+ STRING(encoding);
+ STRING(extracomment);
+ STRING(filename);
+ STRING(id);
+ STRING(language);
+ STRING(line);
+ STRING(location);
+ STRING(message);
+ STRING(name);
+ STRING(numerus);
+ STRING(numerusform);
+ STRING(obsolete);
+ STRING(oldcomment);
+ STRING(oldsource);
+ STRING(source);
+ STRING(sourcelanguage);
+ STRING(translation);
+ STRING(translatorcomment);
+ STRING(true);
+ STRING(TS);
+ STRING(type);
+ STRING(unfinished);
+ STRING(userdata);
+ STRING(utf8);
+ STRING(value);
+ STRING(version);
+ STRING(yes);
+
+ static const QString strextrans(QLatin1String("extra-"));
+ static const QString strUtf8(QLatin1String("UTF-8"));
+
+ while (!atEnd()) {
+ readNext();
+ if (isStartDocument()) {
+ // <!DOCTYPE TS>
+ //qDebug() << attributes();
+ } else if (isEndDocument()) {
+ // <!DOCTYPE TS>
+ //qDebug() << attributes();
+ } else if (isDTD()) {
+ // <!DOCTYPE TS>
+ //qDebug() << tokenString();
+ } else if (elementStarts(strTS)) {
+ // <TS>
+ //qDebug() << "TS " << attributes();
+ QHash<QString, int> currentLine;
+ QString currentFile;
+
+ QXmlStreamAttributes atts = attributes();
+ //QString version = atts.value(strversion).toString();
+ translator.setLanguageCode(atts.value(strlanguage).toString());
+ translator.setSourceLanguageCode(atts.value(strsourcelanguage).toString());
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </TS> found, finish local loop
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strdefaultcodec)) {
+ // <defaultcodec>
+ translator.setCodecName(readElementText().toLatin1());
+ // </defaultcodec>
+ } else if (isStartElement()
+ && name().toString().startsWith(strextrans)) {
+ // <extra-...>
+ QString tag = name().toString();
+ translator.setExtra(tag.mid(6), readContents());
+ // </extra-...>
+ } else if (elementStarts(strcontext)) {
+ // <context>
+ QString context;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </context> found, finish local loop
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strname)) {
+ // <name>
+ context = readElementText();
+ // </name>
+ } else if (elementStarts(strmessage)) {
+ // <message>
+ TranslatorMessage::References refs;
+ QString currentMsgFile = currentFile;
+
+ TranslatorMessage msg;
+ msg.setId(attributes().value(strid).toString());
+ msg.setContext(context);
+ msg.setType(TranslatorMessage::Finished);
+ msg.setPlural(attributes().value(strnumerus) == stryes);
+ msg.setUtf8(attributes().value(strutf8) == strtrue
+ || attributes().value(strencoding) == strUtf8);
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </message> found, finish local loop
+ msg.setReferences(refs);
+ translator.append(msg);
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strsource)) {
+ // <source>...</source>
+ msg.setSourceText(readContents());
+ } else if (elementStarts(stroldsource)) {
+ // <oldsource>...</oldsource>
+ msg.setOldSourceText(readContents());
+ } else if (elementStarts(stroldcomment)) {
+ // <oldcomment>...</oldcomment>
+ msg.setOldComment(readContents());
+ } else if (elementStarts(strextracomment)) {
+ // <extracomment>...</extracomment>
+ msg.setExtraComment(readContents());
+ } else if (elementStarts(strtranslatorcomment)) {
+ // <translatorcomment>...</translatorcomment>
+ msg.setTranslatorComment(readContents());
+ } else if (elementStarts(strlocation)) {
+ // <location/>
+ QXmlStreamAttributes atts = attributes();
+ QString fileName = atts.value(strfilename).toString();
+ if (fileName.isEmpty()) {
+ fileName = currentMsgFile;
+ } else {
+ if (refs.isEmpty())
+ currentFile = fileName;
+ currentMsgFile = fileName;
+ }
+ const QString lin = atts.value(strline).toString();
+ if (lin.isEmpty()) {
+ translator.setLocationsType(Translator::RelativeLocations);
+ refs.append(TranslatorMessage::Reference(fileName, -1));
+ } else {
+ bool bOK;
+ int lineNo = lin.toInt(&bOK);
+ if (bOK) {
+ if (lin.startsWith(QLatin1Char('+')) || lin.startsWith(QLatin1Char('-'))) {
+ lineNo = (currentLine[fileName] += lineNo);
+ translator.setLocationsType(Translator::RelativeLocations);
+ } else {
+ translator.setLocationsType(Translator::AbsoluteLocations);
+ }
+ refs.append(TranslatorMessage::Reference(fileName, lineNo));
+ }
+ }
+ readContents();
+ } else if (elementStarts(strcomment)) {
+ // <comment>...</comment>
+ msg.setComment(readContents());
+ } else if (elementStarts(struserdata)) {
+ // <userdata>...</userdata>
+ msg.setUserData(readContents());
+ } else if (elementStarts(strtranslation)) {
+ // <translation>
+ QXmlStreamAttributes atts = attributes();
+ QStringRef type = atts.value(strtype);
+ if (type == strunfinished)
+ msg.setType(TranslatorMessage::Unfinished);
+ else if (type == strobsolete)
+ msg.setType(TranslatorMessage::Obsolete);
+ if (msg.isPlural()) {
+ QStringList translations;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strnumerusform)) {
+ translations.append(readTransContents());
+ } else {
+ handleError();
+ break;
+ }
+ }
+ msg.setTranslations(translations);
+ } else {
+ msg.setTranslation(readTransContents());
+ }
+ // </translation>
+ } else if (isStartElement()
+ && name().toString().startsWith(strextrans)) {
+ // <extra-...>
+ QString tag = name().toString();
+ msg.setExtra(tag.mid(6), readContents());
+ // </extra-...>
+ } else {
+ handleError();
+ }
+ }
+ // </message>
+ } else {
+ handleError();
+ }
+ }
+ // </context>
+ } else {
+ handleError();
+ }
+ } // </TS>
+ } else {
+ handleError();
+ }
+ }
+ if (hasError()) {
+ m_cd.appendError(errorString());
+ return false;
+ }
+ return true;
+}
+
+static QString numericEntity(int ch)
+{
+ return QString(ch <= 0x20 ? QLatin1String("<byte value=\"x%1\"/>")
+ : QLatin1String("&#x%1;")) .arg(ch, 0, 16);
+}
+
+static QString protect(const QString &str)
+{
+ QString result;
+ result.reserve(str.length() * 12 / 10);
+ for (int i = 0; i != str.size(); ++i) {
+ uint c = str.at(i).unicode();
+ switch (c) {
+ case '\"':
+ result += QLatin1String("&quot;");
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '\'':
+ result += QLatin1String("&apos;");
+ break;
+ default:
+ if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
+ result += numericEntity(c);
+ else // this also covers surrogates
+ result += QChar(c);
+ }
+ }
+ return result;
+}
+
+static QString evilBytes(const QString& str,
+ bool isUtf8, int format, const QByteArray &codecName)
+{
+ //qDebug() << "EVIL: " << str << isUtf8 << format << codecName;
+ if (isUtf8)
+ return protect(str);
+ if (format == 20)
+ return protect(str);
+ if (codecName == "UTF-8")
+ return protect(str);
+ QTextCodec *codec = QTextCodec::codecForName(codecName);
+ if (!codec)
+ return protect(str);
+ QString t = QString::fromLatin1(codec->fromUnicode(protect(str)).data());
+ int len = (int) t.length();
+ QString result;
+ // FIXME: Factor is sensible only for latin scripts, probably.
+ result.reserve(t.length() * 2);
+ for (int k = 0; k < len; k++) {
+ if (t[k].unicode() >= 0x7f)
+ result += numericEntity(t[k].unicode());
+ else
+ result += t[k];
+ }
+ return result;
+}
+
+static void writeExtras(QTextStream &t, const char *indent,
+ const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
+{
+ for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
+ if (!drops.exactMatch(it.key())) {
+ t << indent << "<extra-" << it.key() << '>'
+ << protect(it.value())
+ << "</extra-" << it.key() << ">\n";
+ }
+ }
+}
+
+static void writeVariants(QTextStream &t, const char *indent, const QString &input)
+{
+ int offset;
+ if ((offset = input.indexOf(QChar(Translator::DefaultVariantSeparator))) >= 0) {
+ t << " variants=\"yes\">";
+ int start = 0;
+ forever {
+ t << "\n " << indent << "<lengthvariant>"
+ << protect(input.mid(start, offset - start))
+ << "</lengthvariant>";
+ if (offset == input.length())
+ break;
+ start = offset + 1;
+ offset = input.indexOf(QChar(Translator::DefaultVariantSeparator), start);
+ if (offset < 0)
+ offset = input.length();
+ }
+ t << "\n" << indent;
+ } else {
+ t << ">" << protect(input);
+ }
+}
+
+bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, int format)
+{
+ bool result = true;
+ QTextStream t(&dev);
+ t.setCodec(QTextCodec::codecForName("UTF-8"));
+ bool trIsUtf8 = (translator.codecName() == "UTF-8");
+ //qDebug() << translator.codecName();
+ bool fileIsUtf8 = (format == 20 || trIsUtf8);
+
+ // The xml prolog allows processors to easily detect the correct encoding
+ t << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n";
+
+ if (format == 11)
+ t << "<TS version=\"1.1\"";
+ else
+ t << "<TS version=\"2.0\"";
+
+ QString languageCode = translator.languageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " language=\"" << languageCode << "\"";
+ if (format == 20) {
+ languageCode = translator.sourceLanguageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " sourcelanguage=\"" << languageCode << "\"";
+ }
+ t << ">\n";
+
+ QByteArray codecName = translator.codecName();
+ if (codecName != "ISO-8859-1")
+ t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
+
+ QRegExp drops(cd.dropTags().join(QLatin1String("|")));
+
+ if (format == 20)
+ writeExtras(t, " ", translator.extras(), drops);
+
+ QHash<QString, QList<TranslatorMessage> > messageOrder;
+ QList<QString> contextOrder;
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ // no need for such noise
+ if (msg.type() == TranslatorMessage::Obsolete && msg.translation().isEmpty())
+ continue;
+
+ QList<TranslatorMessage> &context = messageOrder[msg.context()];
+ if (context.isEmpty())
+ contextOrder.append(msg.context());
+ context.append(msg);
+ }
+ if (cd.sortContexts())
+ qSort(contextOrder);
+
+ QHash<QString, int> currentLine;
+ QString currentFile;
+ foreach (const QString &context, contextOrder) {
+ const TranslatorMessage &firstMsg = messageOrder[context].first();
+ t << "<context" << ((!fileIsUtf8 && firstMsg.isUtf8()) ? " encoding=\"UTF-8\"" : "") << ">\n";
+
+ t << " <name>"
+ << evilBytes(context, firstMsg.isUtf8() || fileIsUtf8, format, codecName)
+ << "</name>\n";
+ foreach (const TranslatorMessage &msg, messageOrder[context]) {
+ //msg.dump();
+
+ t << " <message";
+ if (!msg.id().isEmpty())
+ t << " id=\"" << msg.id() << "\"";
+ if (format == 11 && !trIsUtf8 && msg.isUtf8())
+ t << " encoding=\"UTF-8\"";
+ if (format == 20 && !trIsUtf8 && msg.isUtf8())
+ t << " utf8=\"true\"";
+ if (msg.isPlural())
+ t << " numerus=\"yes\"";
+ t << ">\n";
+ if (translator.locationsType() != Translator::NoLocations) {
+ QString cfile = currentFile;
+ bool first = true;
+ foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) {
+ QString fn = cd.m_targetDir.relativeFilePath(ref.fileName())
+ .replace(QLatin1Char('\\'),QLatin1Char('/'));
+ int ln = ref.lineNumber();
+ QString ld;
+ if (translator.locationsType() == Translator::RelativeLocations) {
+ if (ln != -1) {
+ int dlt = ln - currentLine[fn];
+ if (dlt >= 0)
+ ld.append(QLatin1Char('+'));
+ ld.append(QString::number(dlt));
+ currentLine[fn] = ln;
+ }
+
+ if (fn != cfile) {
+ if (first)
+ currentFile = fn;
+ cfile = fn;
+ } else {
+ fn.clear();
+ }
+ first = false;
+ } else {
+ if (ln != -1)
+ ld = QString::number(ln);
+ }
+ t << " <location";
+ if (!fn.isEmpty())
+ t << " filename=\"" << fn << "\"";
+ if (!ld.isEmpty())
+ t << " line=\"" << ld << "\"";
+ t << "/>\n";
+ }
+ }
+
+ t << " <source>"
+ << evilBytes(msg.sourceText(), msg.isUtf8(), format, codecName)
+ << "</source>\n";
+
+ if (format != 11 && !msg.oldSourceText().isEmpty())
+ t << " <oldsource>" << protect(msg.oldSourceText()) << "</oldsource>\n";
+
+ if (!msg.comment().isEmpty()) {
+ t << " <comment>"
+ << evilBytes(msg.comment(), msg.isUtf8(), format, codecName)
+ << "</comment>\n";
+ }
+
+ if (format != 11) {
+
+ if (!msg.oldComment().isEmpty())
+ t << " <oldcomment>" << protect(msg.oldComment()) << "</oldcomment>\n";
+
+ if (!msg.extraComment().isEmpty())
+ t << " <extracomment>" << protect(msg.extraComment())
+ << "</extracomment>\n";
+
+ if (!msg.translatorComment().isEmpty())
+ t << " <translatorcomment>" << protect(msg.translatorComment())
+ << "</translatorcomment>\n";
+
+ }
+
+ t << " <translation";
+ if (msg.type() == TranslatorMessage::Unfinished)
+ t << " type=\"unfinished\"";
+ else if (msg.type() == TranslatorMessage::Obsolete)
+ t << " type=\"obsolete\"";
+ if (msg.isPlural()) {
+ t << ">";
+ QStringList translns = translator.normalizedTranslations(msg, cd, &result);
+ for (int j = 0; j < qMax(1, translns.count()); ++j) {
+ t << "\n <numerusform";
+ writeVariants(t, " ", translns[j]);
+ t << "</numerusform>";
+ }
+ t << "\n ";
+ } else {
+ writeVariants(t, " ", msg.translation());
+ }
+ t << "</translation>\n";
+
+ if (format != 11)
+ writeExtras(t, " ", msg.extras(), drops);
+
+ if (!msg.userData().isEmpty())
+ t << " <userdata>" << msg.userData() << "</userdata>\n";
+ t << " </message>\n";
+ }
+ t << "</context>\n";
+ }
+
+ t << "</TS>\n";
+ return result;
+}
+
+bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ translator.setLocationsType(Translator::NoLocations);
+ TSReader reader(dev, cd);
+ return reader.read(translator);
+}
+
+bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ return saveTS(translator, dev, cd, 11);
+}
+
+bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ return saveTS(translator, dev, cd, 20);
+}
+
+int initTS()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("ts11");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ format.description = QObject::tr("Qt translation sources (format 1.1)");
+ format.loader = &loadTS;
+ format.saver = &saveTS11;
+ Translator::registerFileFormat(format);
+
+ format.extension = QLatin1String("ts20");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ format.description = QObject::tr("Qt translation sources (format 2.0)");
+ format.loader = &loadTS;
+ format.saver = &saveTS20;
+ Translator::registerFileFormat(format);
+
+ // "ts" is always the latest. right now it's ts20.
+ format.extension = QLatin1String("ts");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 0;
+ format.description = QObject::tr("Qt translation sources (latest format)");
+ format.loader = &loadTS;
+ format.saver = &saveTS20;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initTS)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/ts.dtd b/tools/linguist/shared/ts.dtd
new file mode 100644
index 0000000000..ab77f64604
--- /dev/null
+++ b/tools/linguist/shared/ts.dtd
@@ -0,0 +1,113 @@
+<!--
+ !
+ ! Some notes to the DTD:
+ !
+ ! The location element is set as optional since it was introduced first in Qt 4.2.
+ ! The userdata element is set as optional since it was introduced first in Qt 4.4.
+ ! The source and translation elements are optional starting with version 3.0
+ ! (Qt 4.6) to support S60 blank messages.
+ !
+ -->
+<!--
+ ! Macro used in order to escape byte entities not allowed in an xml document
+ ! for instance, only #x9, #xA and #xD are allowed characters below #x20.
+ -->
+<!ENTITY % evilstring '(#PCDATA | byte)*' >
+<!ELEMENT byte EMPTY>
+<!-- value contains decimal (e.g. 1000) or hex (e.g. x3e8) unicode encoding of one char -->
+<!ATTLIST byte
+ value CDATA #REQUIRED>
+<!--
+ ! This element wildcard is no valid DTD. No better solution available.
+ ! extra elements may appear in TS and message elements. Each element may appear
+ ! only once within each scope. The contents are preserved verbatim; any
+ ! attributes are dropped. Currently recognized extra tags include:
+ ! extra-po-msgid_plural, extra-po-old_msgid_plural
+ ! extra-po-flags (comma-space separated list)
+ ! extra-loc-layout_id
+ ! extra-loc-feature
+ ! extra-loc-blank
+ -->
+<!ELEMENT extra-* %evilstring; >
+<!ELEMENT TS (defaultcodec?, extra-**, (context|message)+) >
+<!ATTLIST TS
+ version CDATA #IMPLIED
+ sourcelanguage CDATA #IMPLIED
+ language CDATA #IMPLIED>
+<!-- The encoding to use in the .qm file by default. Default is ISO-8859-1. -->
+<!ELEMENT defaultcodec (#PCDATA) >
+<!ELEMENT context (name?, comment?, (context|message)+) >
+<!ATTLIST context
+ encoding CDATA #IMPLIED>
+<!ELEMENT name %evilstring; >
+<!-- If "no", then the context nesting is for informational puposes only -->
+<!ATTLIST name
+ nest (yes|no) "yes">
+<!-- This is "disambiguation" in the (new) API, or "msgctxt" in gettext speak -->
+<!ELEMENT comment %evilstring; >
+<!-- Previous content of comment (result of merge) -->
+<!ELEMENT oldcomment %evilstring; >
+<!-- The real comment (added by developer/designer) -->
+<!ELEMENT extracomment %evilstring; >
+<!-- Comment added by translator -->
+<!ELEMENT translatorcomment %evilstring; >
+<!ELEMENT message (location*, source?, oldsource?, comment?, oldcomment?, extracomment?, translatorcomment?, translation?, userdata?, extra-**) >
+<!--
+ ! If utf8 is true, the defaultcodec is overridden and the message is encoded
+ ! in UTF-8 in the .qm file.
+ -->
+<!ATTLIST message
+ id CDATA #IMPLIED
+ utf8 (true|false) "false"
+ numerus (yes|no) "no">
+<!ELEMENT location EMPTY>
+<!--
+ ! If the line is omitted, the location specifies only a file.
+ !
+ ! location supports relative specifications as well. Line numbers are
+ ! relative (explicitly positive or negative) to the last reference to a
+ ! given filename; each file starts with current line 0. If the filename
+ ! is omitted, the "current" one is used. For the 1st location in a message,
+ ! "current" is the filename used for the 1st location of the previous message.
+ ! For subsequent locations, it is the filename used for the previous location.
+ ! A single .ts file has either all absolute or all relative locations.
+ -->
+<!ATTLIST location
+ filename CDATA #IMPLIED
+ line CDATA #IMPLIED>
+<!ELEMENT source %evilstring;>
+<!-- Previous content of source (result of merge) -->
+<!ELEMENT oldsource %evilstring;>
+<!--
+ ! The following should really say one evilstring macro or several
+ ! numerusform or lengthvariant elements, but the DTD can't express this.
+ -->
+<!ELEMENT translation (#PCDATA|byte|numerusform|lengthvariant)* >
+<!--
+ ! If no type is set, the message is "finished".
+ ! Length variants must be ordered by falling display length.
+ ! variants may not be yes if the message has numerus yes.
+ -->
+<!ATTLIST translation
+ type (unfinished|obsolete) #IMPLIED
+ variants (yes|no) "no">
+<!-- Deprecated. Use extra-* -->
+<!ELEMENT userdata (#PCDATA)* >
+<!--
+ ! The following should really say one evilstring macro or several
+ ! lengthvariant elements, but the DTD can't express this.
+ ! Length variants must be ordered by falling display length.
+ -->
+<!ELEMENT numerusform (#PCDATA|byte|lengthvariant)* >
+<!ATTLIST numerusform
+ plurality (nullar|singular|dual|trial|paucal|greaterpaucal|plural|greaterplural) #IMPLIED>
+ variants (yes|no) "no">
+<!ELEMENT lengthvariant %evilstring; >
+<!--
+ ! The translation variants have a priority between 1 ("highest") and 9 ("lowest")
+ ! Typically longer translations get a higher priority.
+ ! If omitted, the order of appearance of the variants in the .ts files is used.
+ -->
+<!ATTLIST lengthvariant
+ priority (1|2|3|4|5|6|7|8|9) #IMPLIED>
+
diff --git a/tools/linguist/shared/ui.cpp b/tools/linguist/shared/ui.cpp
new file mode 100644
index 0000000000..4b86714ee1
--- /dev/null
+++ b/tools/linguist/shared/ui.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include <QtXml/QXmlAttributes>
+#include <QtXml/QXmlDefaultHandler>
+#include <QtXml/QXmlLocator>
+#include <QtXml/QXmlParseException>
+
+
+QT_BEGIN_NAMESPACE
+
+// in cpp.cpp
+void fetchtrInlinedCpp(const QString &in, Translator &tor, const QString &context);
+
+class UiReader : public QXmlDefaultHandler
+{
+public:
+ UiReader(Translator &translator, ConversionData &cd)
+ : m_translator(translator), m_cd(cd), m_lineNumber(-1),
+ m_needUtf8(translator.codecName() != "UTF-8")
+ {}
+
+ bool startElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &atts);
+ bool endElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName);
+ bool characters(const QString &ch);
+ bool fatalError(const QXmlParseException &exception);
+
+ void setDocumentLocator(QXmlLocator *locator) { m_locator = locator; }
+
+private:
+ void flush();
+
+ Translator &m_translator;
+ ConversionData &m_cd;
+ QString m_context;
+ QString m_source;
+ QString m_comment;
+ QXmlLocator *m_locator;
+
+ QString m_accum;
+ int m_lineNumber;
+ bool m_isTrString;
+ bool m_needUtf8;
+};
+
+bool UiReader::startElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName, const QXmlAttributes &atts)
+{
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+
+ if (qName == QLatin1String("item")) {
+ flush();
+ if (!atts.value(QLatin1String("text")).isEmpty())
+ m_source = atts.value(QLatin1String("text"));
+ } else if (qName == QLatin1String("string")) {
+ flush();
+ if (atts.value(QLatin1String("notr")).isEmpty() ||
+ atts.value(QLatin1String("notr")) != QLatin1String("true")) {
+ m_isTrString = true;
+ m_comment = atts.value(QLatin1String("comment"));
+ } else {
+ m_isTrString = false;
+ }
+ }
+ if (m_isTrString && !m_cd.m_noUiLines)
+ m_lineNumber = m_locator->lineNumber();
+ m_accum.clear();
+ return true;
+}
+
+bool UiReader::endElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName)
+{
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+
+ m_accum.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+
+ if (qName == QLatin1String("class")) {
+ if (m_context.isEmpty())
+ m_context = m_accum;
+ } else if (qName == QLatin1String("string") && m_isTrString) {
+ m_source = m_accum;
+ } else if (qName == QLatin1String("comment")) {
+ m_comment = m_accum;
+ flush();
+ } else if (qName == QLatin1String("function")) {
+ fetchtrInlinedCpp(m_accum, m_translator, m_context);
+ } else {
+ flush();
+ }
+ return true;
+}
+
+bool UiReader::characters(const QString &ch)
+{
+ m_accum += ch;
+ return true;
+}
+
+bool UiReader::fatalError(const QXmlParseException &exception)
+{
+ QString msg;
+ msg.sprintf("XML error: Parse error at line %d, column %d (%s).",
+ exception.lineNumber(), exception.columnNumber(),
+ exception.message().toLatin1().data());
+ m_cd.appendError(msg);
+ return false;
+}
+
+void UiReader::flush()
+{
+ if (!m_context.isEmpty() && !m_source.isEmpty()) {
+ TranslatorMessage msg(m_context, m_source,
+ m_comment, QString(), m_cd.m_sourceFileName,
+ m_lineNumber, QStringList());
+ if (m_needUtf8 && msg.needs8Bit())
+ msg.setUtf8(true);
+ m_translator.extend(msg);
+ }
+ m_source.clear();
+ m_comment.clear();
+}
+
+bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QXmlInputSource in(&dev);
+ QXmlSimpleReader reader;
+ reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false);
+ reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true);
+ reader.setFeature(QLatin1String(
+ "http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
+ UiReader handler(translator, cd);
+ reader.setContentHandler(&handler);
+ reader.setErrorHandler(&handler);
+ bool result = reader.parse(in);
+ if (!result)
+ cd.appendError(QLatin1String("Parse error in UI file"));
+ reader.setContentHandler(0);
+ reader.setErrorHandler(0);
+ return result;
+}
+
+bool saveUI(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Q_UNUSED(dev);
+ Q_UNUSED(translator);
+ cd.appendError(QLatin1String("Cannot save .ui files"));
+ return false;
+}
+
+int initUI()
+{
+ Translator::FileFormat format;
+
+ // "real" Qt Designer
+ format.extension = QLatin1String("ui");
+ format.description = QObject::tr("Qt Designer form files");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.loader = &loadUI;
+ format.saver = &saveUI;
+ Translator::registerFileFormat(format);
+
+ // same for jambi
+ format.extension = QLatin1String("jui");
+ format.description = QObject::tr("Qt Jambi form files");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.loader = &loadUI;
+ format.saver = &saveUI;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initUI)
+
+QT_END_NAMESPACE
diff --git a/tools/linguist/shared/xliff.cpp b/tools/linguist/shared/xliff.cpp
new file mode 100644
index 0000000000..6acf67abf3
--- /dev/null
+++ b/tools/linguist/shared/xliff.cpp
@@ -0,0 +1,828 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMap>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlAttributes>
+#include <QtXml/QXmlDefaultHandler>
+#include <QtXml/QXmlParseException>
+
+
+QT_BEGIN_NAMESPACE
+
+/**
+ * Implementation of XLIFF file format for Linguist
+ */
+//static const char *restypeDomain = "x-gettext-domain";
+static const char *restypeContext = "x-trolltech-linguist-context";
+static const char *restypePlurals = "x-gettext-plurals";
+static const char *restypeDummy = "x-dummy";
+static const char *dataTypeUIFile = "x-trolltech-designer-ui";
+static const char *contextMsgctxt = "x-gettext-msgctxt"; // XXX Troll invention, so far.
+static const char *contextOldMsgctxt = "x-gettext-previous-msgctxt"; // XXX Troll invention, so far.
+static const char *attribPlural = "trolltech:plural";
+static const char *XLIFF11namespaceURI = "urn:oasis:names:tc:xliff:document:1.1";
+static const char *XLIFF12namespaceURI = "urn:oasis:names:tc:xliff:document:1.2";
+static const char *TrollTsNamespaceURI = "urn:trolltech:names:ts:document:1.0";
+
+#define COMBINE4CHARS(c1, c2, c3, c4) \
+ (int(c1) << 24 | int(c2) << 16 | int(c3) << 8 | int(c4) )
+
+static QString dataType(const TranslatorMessage &m)
+{
+ QByteArray fileName = m.fileName().toAscii();
+ unsigned int extHash = 0;
+ int pos = fileName.count() - 1;
+ for (int pass = 0; pass < 4 && pos >=0; ++pass, --pos) {
+ if (fileName.at(pos) == '.')
+ break;
+ extHash |= ((int)fileName.at(pos) << (8*pass));
+ }
+
+ switch (extHash) {
+ case COMBINE4CHARS(0,'c','p','p'):
+ case COMBINE4CHARS(0,'c','x','x'):
+ case COMBINE4CHARS(0,'c','+','+'):
+ case COMBINE4CHARS(0,'h','p','p'):
+ case COMBINE4CHARS(0,'h','x','x'):
+ case COMBINE4CHARS(0,'h','+','+'):
+ return QLatin1String("cpp");
+ case COMBINE4CHARS(0, 0 , 0 ,'c'):
+ case COMBINE4CHARS(0, 0 , 0 ,'h'):
+ case COMBINE4CHARS(0, 0 ,'c','c'):
+ case COMBINE4CHARS(0, 0 ,'c','h'):
+ case COMBINE4CHARS(0, 0 ,'h','h'):
+ return QLatin1String("c");
+ case COMBINE4CHARS(0, 0 ,'u','i'):
+ return QLatin1String(dataTypeUIFile); //### form?
+ default:
+ return QLatin1String("plaintext"); // we give up
+ }
+}
+
+static void writeIndent(QTextStream &ts, int indent)
+{
+ ts << QString().fill(QLatin1Char(' '), indent * 2);
+}
+
+struct CharMnemonic
+{
+ char ch;
+ char escape;
+ const char *mnemonic;
+};
+
+static const CharMnemonic charCodeMnemonics[] = {
+ {0x07, 'a', "bel"},
+ {0x08, 'b', "bs"},
+ {0x09, 't', "tab"},
+ {0x0a, 'n', "lf"},
+ {0x0b, 'v', "vt"},
+ {0x0c, 'f', "ff"},
+ {0x0d, 'r', "cr"}
+};
+
+static char charFromEscape(char escape)
+{
+ for (uint i = 0; i < sizeof(charCodeMnemonics)/sizeof(CharMnemonic); ++i) {
+ CharMnemonic cm = charCodeMnemonics[i];
+ if (cm.escape == escape)
+ return cm.ch;
+ }
+ Q_ASSERT(0);
+ return escape;
+}
+
+static QString numericEntity(int ch, bool makePhs)
+{
+ // ### This needs to be reviewed, to reflect the updated XLIFF-PO spec.
+ if (!makePhs || ch < 7 || ch > 0x0d)
+ return QString::fromAscii("&#x%1;").arg(QString::number(ch, 16));
+
+ CharMnemonic cm = charCodeMnemonics[int(ch) - 7];
+ QString name = QLatin1String(cm.mnemonic);
+ char escapechar = cm.escape;
+
+ static int id = 0;
+ return QString::fromAscii("<ph id=\"ph%1\" ctype=\"x-ch-%2\">\\%3</ph>")
+ .arg(++id) .arg(name) .arg(escapechar);
+}
+
+static QString protect(const QString &str, bool makePhs = true)
+{
+ QString result;
+ int len = str.size();
+ for (int i = 0; i != len; ++i) {
+ uint c = str.at(i).unicode();
+ switch (c) {
+ case '\"':
+ result += QLatin1String("&quot;");
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '\'':
+ result += QLatin1String("&apos;");
+ break;
+ default:
+ if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
+ result += numericEntity(c, makePhs);
+ else // this also covers surrogates
+ result += QChar(c);
+ }
+ }
+ return result;
+}
+
+
+static void writeExtras(QTextStream &ts, int indent,
+ const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
+{
+ for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
+ if (!drops.exactMatch(it.key())) {
+ writeIndent(ts, indent);
+ ts << "<trolltech:" << it.key() << '>'
+ << protect(it.value())
+ << "</trolltech:" << it.key() << ">\n";
+ }
+ }
+}
+
+static void writeLineNumber(QTextStream &ts, const TranslatorMessage &msg, int indent)
+{
+ if (msg.lineNumber() == -1)
+ return;
+ writeIndent(ts, indent);
+ ts << "<context-group purpose=\"location\"><context context-type=\"linenumber\">"
+ << msg.lineNumber() << "</context></context-group>\n";
+ foreach (const TranslatorMessage::Reference &ref, msg.extraReferences()) {
+ writeIndent(ts, indent);
+ ts << "<context-group purpose=\"location\">";
+ if (ref.fileName() != msg.fileName())
+ ts << "<context context-type=\"sourcefile\">" << ref.fileName() << "</context>";
+ ts << "<context context-type=\"linenumber\">" << ref.lineNumber()
+ << "</context></context-group>\n";
+ }
+}
+
+static void writeComment(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
+{
+ if (!msg.comment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<context-group><context context-type=\"" << contextMsgctxt << "\">"
+ << protect(msg.comment(), false)
+ << "</context></context-group>\n";
+ }
+ if (!msg.oldComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<context-group><context context-type=\"" << contextOldMsgctxt << "\">"
+ << protect(msg.oldComment(), false)
+ << "</context></context-group>\n";
+ }
+ writeExtras(ts, indent, msg.extras(), drops);
+ if (!msg.extraComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<note annotates=\"source\" from=\"developer\">"
+ << protect(msg.extraComment()) << "</note>\n";
+ }
+ if (!msg.translatorComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<note from=\"translator\">"
+ << protect(msg.translatorComment()) << "</note>\n";
+ }
+}
+
+static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
+ const Translator &translator, ConversionData &cd, bool *ok)
+{
+ static int msgid;
+ QString msgidstr = !msg.id().isEmpty() ? msg.id() : QString::fromAscii("_msg%1").arg(++msgid);
+
+ QStringList translns = translator.normalizedTranslations(msg, cd, ok);
+ QHash<QString, QString>::const_iterator it;
+ QString pluralStr;
+ QStringList sources(msg.sourceText());
+ if ((it = msg.extras().find(QString::fromLatin1("po-msgid_plural"))) != msg.extras().end())
+ sources.append(*it);
+ QStringList oldsources;
+ if (!msg.oldSourceText().isEmpty())
+ oldsources.append(msg.oldSourceText());
+ if ((it = msg.extras().find(QString::fromLatin1("po-old_msgid_plural"))) != msg.extras().end()) {
+ if (oldsources.isEmpty()) {
+ if (sources.count() == 2)
+ oldsources.append(QString());
+ else
+ pluralStr = QLatin1Char(' ') + QLatin1String(attribPlural) + QLatin1String("=\"yes\"");
+ }
+ oldsources.append(*it);
+ }
+
+ QStringList::const_iterator
+ srcit = sources.begin(), srcend = sources.end(),
+ oldsrcit = oldsources.begin(), oldsrcend = oldsources.end(),
+ transit = translns.begin(), transend = translns.end();
+ int plural = 0;
+ QString source;
+ while (srcit != srcend || oldsrcit != oldsrcend || transit != transend) {
+ QByteArray attribs;
+ QByteArray state;
+ if (msg.type() == TranslatorMessage::Obsolete) {
+ if (!msg.isPlural())
+ attribs = " translate=\"no\"";
+ } else if (msg.type() == TranslatorMessage::Finished) {
+ attribs = " approved=\"yes\"";
+ } else if (transit != transend && !transit->isEmpty()) {
+ state = " state=\"needs-review-translation\"";
+ }
+ writeIndent(ts, indent);
+ ts << "<trans-unit id=\"" << msgidstr;
+ if (msg.isPlural())
+ ts << "[" << plural++ << "]";
+ ts << "\"" << attribs << ">\n";
+ ++indent;
+
+ writeIndent(ts, indent);
+ if (srcit != srcend) {
+ source = *srcit;
+ ++srcit;
+ } // else just repeat last element
+ ts << "<source xml:space=\"preserve\">" << protect(source) << "</source>\n";
+
+ bool puttrans = false;
+ QString translation;
+ if (transit != transend) {
+ translation = *transit;
+ ++transit;
+ puttrans = true;
+ }
+ do {
+ if (oldsrcit != oldsrcend && !oldsrcit->isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<alt-trans>\n";
+ ++indent;
+ writeIndent(ts, indent);
+ ts << "<source xml:space=\"preserve\"" << pluralStr << '>' << protect(*oldsrcit) << "</source>\n";
+ if (!puttrans) {
+ writeIndent(ts, indent);
+ ts << "<target restype=\"" << restypeDummy << "\"/>\n";
+ }
+ }
+
+ if (puttrans) {
+ writeIndent(ts, indent);
+ ts << "<target xml:space=\"preserve\"" << state << ">" << protect(translation) << "</target>\n";
+ }
+
+ if (oldsrcit != oldsrcend) {
+ if (!oldsrcit->isEmpty()) {
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</alt-trans>\n";
+ }
+ ++oldsrcit;
+ }
+
+ puttrans = false;
+ } while (srcit == srcend && oldsrcit != oldsrcend);
+
+ if (!msg.isPlural()) {
+ writeLineNumber(ts, msg, indent);
+ writeComment(ts, msg, drops, indent);
+ }
+
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</trans-unit>\n";
+ }
+}
+
+static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent,
+ const Translator &translator, ConversionData &cd, bool *ok)
+{
+ if (msg.isPlural()) {
+ writeIndent(ts, indent);
+ ts << "<group restype=\"" << restypePlurals << "\"";
+ if (!msg.id().isEmpty())
+ ts << " id=\"" << msg.id() << "\"";
+ if (msg.type() == TranslatorMessage::Obsolete)
+ ts << " translate=\"no\"";
+ ts << ">\n";
+ ++indent;
+ writeLineNumber(ts, msg, indent);
+ writeComment(ts, msg, drops, indent);
+
+ writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</group>\n";
+ } else {
+ writeTransUnits(ts, msg, drops, indent, translator, cd, ok);
+ }
+}
+
+
+class XLIFFHandler : public QXmlDefaultHandler
+{
+public:
+ XLIFFHandler(Translator &translator, ConversionData &cd);
+
+ bool startElement(const QString& namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &atts );
+ bool endElement(const QString& namespaceURI, const QString &localName,
+ const QString &qName );
+ bool characters(const QString &ch);
+ bool fatalError(const QXmlParseException &exception);
+
+ bool endDocument();
+
+private:
+ enum XliffContext {
+ XC_xliff,
+ XC_group,
+ XC_trans_unit,
+ XC_context_group,
+ XC_context_group_any,
+ XC_context,
+ XC_context_filename,
+ XC_context_linenumber,
+ XC_context_context,
+ XC_context_comment,
+ XC_context_old_comment,
+ XC_ph,
+ XC_extra_comment,
+ XC_translator_comment,
+ XC_restype_context,
+ XC_restype_translation,
+ XC_restype_plurals,
+ XC_alt_trans
+ };
+ void pushContext(XliffContext ctx);
+ bool popContext(XliffContext ctx);
+ XliffContext currentContext() const;
+ bool hasContext(XliffContext ctx) const;
+ bool finalizeMessage(bool isPlural);
+
+private:
+ Translator &m_translator;
+ ConversionData &m_cd;
+ TranslatorMessage::Type m_type;
+ QString m_language;
+ QString m_sourceLanguage;
+ QString m_context;
+ QString m_id;
+ QStringList m_sources;
+ QStringList m_oldSources;
+ QString m_comment;
+ QString m_oldComment;
+ QString m_extraComment;
+ QString m_translatorComment;
+ bool m_isPlural;
+ bool m_hadAlt;
+ QStringList m_translations;
+ QString m_fileName;
+ int m_lineNumber;
+ QString m_extraFileName;
+ TranslatorMessage::References m_refs;
+ TranslatorMessage::ExtraData m_extra;
+
+ QString accum;
+ QString m_ctype;
+ const QString m_URITT; // convenience and efficiency
+ const QString m_URI; // ...
+ const QString m_URI12; // ...
+ QStack<int> m_contextStack;
+};
+
+XLIFFHandler::XLIFFHandler(Translator &translator, ConversionData &cd)
+ : m_translator(translator), m_cd(cd),
+ m_type(TranslatorMessage::Finished),
+ m_lineNumber(-1),
+ m_URITT(QLatin1String(TrollTsNamespaceURI)),
+ m_URI(QLatin1String(XLIFF11namespaceURI)),
+ m_URI12(QLatin1String(XLIFF12namespaceURI))
+{}
+
+
+void XLIFFHandler::pushContext(XliffContext ctx)
+{
+ m_contextStack.push_back(ctx);
+}
+
+// Only pops it off if the top of the stack contains ctx
+bool XLIFFHandler::popContext(XliffContext ctx)
+{
+ if (!m_contextStack.isEmpty() && m_contextStack.top() == ctx) {
+ m_contextStack.pop();
+ return true;
+ }
+ return false;
+}
+
+XLIFFHandler::XliffContext XLIFFHandler::currentContext() const
+{
+ if (!m_contextStack.isEmpty())
+ return (XliffContext)m_contextStack.top();
+ return XC_xliff;
+}
+
+// traverses to the top to check all of the parent contexes.
+bool XLIFFHandler::hasContext(XliffContext ctx) const
+{
+ for (int i = m_contextStack.count() - 1; i >= 0; --i) {
+ if (m_contextStack.at(i) == ctx)
+ return true;
+ }
+ return false;
+}
+
+bool XLIFFHandler::startElement(const QString& namespaceURI,
+ const QString &localName, const QString &qName, const QXmlAttributes &atts )
+{
+ Q_UNUSED(qName);
+ if (namespaceURI == m_URITT)
+ goto bail;
+ if (namespaceURI != m_URI && namespaceURI != m_URI12)
+ return false;
+ if (localName == QLatin1String("xliff")) {
+ // make sure that the stack is not empty during parsing
+ pushContext(XC_xliff);
+ } else if (localName == QLatin1String("file")) {
+ m_fileName = atts.value(QLatin1String("original"));
+ m_language = atts.value(QLatin1String("target-language"));
+ m_sourceLanguage = atts.value(QLatin1String("source-language"));
+ } else if (localName == QLatin1String("group")) {
+ if (atts.value(QLatin1String("restype")) == QLatin1String(restypeContext)) {
+ m_context = atts.value(QLatin1String("resname"));
+ pushContext(XC_restype_context);
+ } else {
+ if (atts.value(QLatin1String("restype")) == QLatin1String(restypePlurals)) {
+ pushContext(XC_restype_plurals);
+ m_id = atts.value(QLatin1String("id"));
+ if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
+ m_type = TranslatorMessage::Obsolete;
+ } else {
+ pushContext(XC_group);
+ }
+ }
+ } else if (localName == QLatin1String("trans-unit")) {
+ if (!hasContext(XC_restype_plurals) || m_sources.isEmpty() /* who knows ... */)
+ if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
+ m_type = TranslatorMessage::Obsolete;
+ if (!hasContext(XC_restype_plurals)) {
+ m_id = atts.value(QLatin1String("id"));
+ if (m_id.startsWith(QLatin1String("_msg")))
+ m_id.clear();
+ }
+ if (m_type != TranslatorMessage::Obsolete &&
+ atts.value(QLatin1String("approved")) != QLatin1String("yes"))
+ m_type = TranslatorMessage::Unfinished;
+ pushContext(XC_trans_unit);
+ m_hadAlt = false;
+ } else if (localName == QLatin1String("alt-trans")) {
+ pushContext(XC_alt_trans);
+ } else if (localName == QLatin1String("source")) {
+ m_isPlural = atts.value(QLatin1String(attribPlural)) == QLatin1String("yes");
+ } else if (localName == QLatin1String("target")) {
+ if (atts.value(QLatin1String("restype")) != QLatin1String(restypeDummy))
+ pushContext(XC_restype_translation);
+ } else if (localName == QLatin1String("context-group")) {
+ QString purpose = atts.value(QLatin1String("purpose"));
+ if (purpose == QLatin1String("location"))
+ pushContext(XC_context_group);
+ else
+ pushContext(XC_context_group_any);
+ } else if (currentContext() == XC_context_group && localName == QLatin1String("context")) {
+ QString ctxtype = atts.value(QLatin1String("context-type"));
+ if (ctxtype == QLatin1String("linenumber"))
+ pushContext(XC_context_linenumber);
+ else if (ctxtype == QLatin1String("sourcefile"))
+ pushContext(XC_context_filename);
+ } else if (currentContext() == XC_context_group_any && localName == QLatin1String("context")) {
+ QString ctxtype = atts.value(QLatin1String("context-type"));
+ if (ctxtype == QLatin1String(contextMsgctxt))
+ pushContext(XC_context_comment);
+ else if (ctxtype == QLatin1String(contextOldMsgctxt))
+ pushContext(XC_context_old_comment);
+ } else if (localName == QLatin1String("note")) {
+ if (atts.value(QLatin1String("annotates")) == QLatin1String("source") &&
+ atts.value(QLatin1String("from")) == QLatin1String("developer"))
+ pushContext(XC_extra_comment);
+ else
+ pushContext(XC_translator_comment);
+ } else if (localName == QLatin1String("ph")) {
+ QString ctype = atts.value(QLatin1String("ctype"));
+ if (ctype.startsWith(QLatin1String("x-ch-")))
+ m_ctype = ctype.mid(5);
+ pushContext(XC_ph);
+ }
+bail:
+ if (currentContext() != XC_ph)
+ accum.clear();
+ return true;
+}
+
+bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localName,
+ const QString &qName)
+{
+ Q_UNUSED(qName);
+ if (namespaceURI == m_URITT) {
+ if (hasContext(XC_trans_unit) || hasContext(XC_restype_plurals))
+ m_extra[localName] = accum;
+ else
+ m_translator.setExtra(localName, accum);
+ return true;
+ }
+ if (namespaceURI != m_URI && namespaceURI != m_URI12)
+ return false;
+ //qDebug() << "URI:" << namespaceURI << "QNAME:" << qName;
+ if (localName == QLatin1String("xliff")) {
+ popContext(XC_xliff);
+ } else if (localName == QLatin1String("source")) {
+ if (hasContext(XC_alt_trans)) {
+ if (m_isPlural && m_oldSources.isEmpty())
+ m_oldSources.append(QString());
+ m_oldSources.append(accum);
+ m_hadAlt = true;
+ } else {
+ m_sources.append(accum);
+ }
+ } else if (localName == QLatin1String("target")) {
+ if (popContext(XC_restype_translation))
+ m_translations.append(accum);
+ } else if (localName == QLatin1String("context-group")) {
+ if (popContext(XC_context_group)) {
+ m_refs.append(TranslatorMessage::Reference(
+ m_extraFileName.isEmpty() ? m_fileName : m_extraFileName, m_lineNumber));
+ m_extraFileName.clear();
+ m_lineNumber = -1;
+ } else {
+ popContext(XC_context_group_any);
+ }
+ } else if (localName == QLatin1String("context")) {
+ if (popContext(XC_context_linenumber)) {
+ bool ok;
+ m_lineNumber = accum.trimmed().toInt(&ok);
+ if (!ok)
+ m_lineNumber = -1;
+ } else if (popContext(XC_context_filename)) {
+ m_extraFileName = accum;
+ } else if (popContext(XC_context_comment)) {
+ m_comment = accum;
+ } else if (popContext(XC_context_old_comment)) {
+ m_oldComment = accum;
+ }
+ } else if (localName == QLatin1String("note")) {
+ if (popContext(XC_extra_comment))
+ m_extraComment = accum;
+ else if (popContext(XC_translator_comment))
+ m_translatorComment = accum;
+ } else if (localName == QLatin1String("ph")) {
+ m_ctype.clear();
+ popContext(XC_ph);
+ } else if (localName == QLatin1String("trans-unit")) {
+ popContext(XC_trans_unit);
+ if (!m_hadAlt)
+ m_oldSources.append(QString());
+ if (!hasContext(XC_restype_plurals)) {
+ if (!finalizeMessage(false))
+ return false;
+ }
+ } else if (localName == QLatin1String("alt-trans")) {
+ popContext(XC_alt_trans);
+ } else if (localName == QLatin1String("group")) {
+ if (popContext(XC_restype_plurals)) {
+ if (!finalizeMessage(true))
+ return false;
+ } else if (popContext(XC_restype_context)) {
+ m_context.clear();
+ } else {
+ popContext(XC_group);
+ }
+ }
+ return true;
+}
+
+bool XLIFFHandler::characters(const QString &ch)
+{
+ if (currentContext() == XC_ph) {
+ // handle the content of <ph> elements
+ for (int i = 0; i < ch.count(); ++i) {
+ QChar chr = ch.at(i);
+ if (accum.endsWith(QLatin1Char('\\')))
+ accum[accum.size() - 1] = QLatin1Char(charFromEscape(chr.toAscii()));
+ else
+ accum.append(chr);
+ }
+ } else {
+ QString t = ch;
+ t.replace(QLatin1String("\r"), QLatin1String(""));
+ accum.append(t);
+ }
+ return true;
+}
+
+bool XLIFFHandler::endDocument()
+{
+ m_translator.setLanguageCode(m_language);
+ m_translator.setSourceLanguageCode(m_sourceLanguage);
+ return true;
+}
+
+bool XLIFFHandler::finalizeMessage(bool isPlural)
+{
+ if (m_sources.isEmpty()) {
+ m_cd.appendError(QLatin1String("XLIFF syntax error: Message without source string."));
+ return false;
+ }
+ TranslatorMessage msg(m_context, m_sources[0],
+ m_comment, QString(), QString(), -1,
+ m_translations, m_type, isPlural);
+ msg.setId(m_id);
+ msg.setReferences(m_refs);
+ msg.setOldComment(m_oldComment);
+ msg.setExtraComment(m_extraComment);
+ msg.setTranslatorComment(m_translatorComment);
+ if (m_sources.count() > 1 && m_sources[1] != m_sources[0])
+ m_extra.insert(QLatin1String("po-msgid_plural"), m_sources[1]);
+ if (!m_oldSources.isEmpty()) {
+ if (!m_oldSources[0].isEmpty())
+ msg.setOldSourceText(m_oldSources[0]);
+ if (m_oldSources.count() > 1 && m_oldSources[1] != m_oldSources[0])
+ m_extra.insert(QLatin1String("po-old_msgid_plural"), m_oldSources[1]);
+ }
+ msg.setExtras(m_extra);
+ m_translator.append(msg);
+
+ m_id.clear();
+ m_sources.clear();
+ m_oldSources.clear();
+ m_translations.clear();
+ m_comment.clear();
+ m_oldComment.clear();
+ m_extraComment.clear();
+ m_translatorComment.clear();
+ m_extra.clear();
+ m_refs.clear();
+ m_type = TranslatorMessage::Finished;
+ return true;
+}
+
+bool XLIFFHandler::fatalError(const QXmlParseException &exception)
+{
+ QString msg;
+ msg.sprintf("XML error: Parse error at line %d, column %d (%s).\n",
+ exception.lineNumber(), exception.columnNumber(),
+ exception.message().toLatin1().data() );
+ m_cd.appendError(msg);
+ return false;
+}
+
+bool loadXLIFF(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QXmlInputSource in(&dev);
+ QXmlSimpleReader reader;
+ XLIFFHandler hand(translator, cd);
+ reader.setContentHandler(&hand);
+ reader.setErrorHandler(&hand);
+ return reader.parse(in);
+}
+
+bool saveXLIFF(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ bool ok = true;
+ int indent = 0;
+
+ QTextStream ts(&dev);
+ ts.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ QStringList dtgs = cd.dropTags();
+ dtgs << QLatin1String("po-(old_)?msgid_plural");
+ QRegExp drops(dtgs.join(QLatin1String("|")));
+
+ QHash<QString, QHash<QString, QList<TranslatorMessage> > > messageOrder;
+ QHash<QString, QList<QString> > contextOrder;
+ QList<QString> fileOrder;
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ QHash<QString, QList<TranslatorMessage> > &file = messageOrder[msg.fileName()];
+ if (file.isEmpty())
+ fileOrder.append(msg.fileName());
+ QList<TranslatorMessage> &context = file[msg.context()];
+ if (context.isEmpty())
+ contextOrder[msg.fileName()].append(msg.context());
+ context.append(msg);
+ }
+
+ ts.setFieldAlignment(QTextStream::AlignRight);
+ ts << "<?xml version=\"1.0\"";
+ ts << " encoding=\"utf-8\"?>\n";
+ ts << "<xliff version=\"1.2\" xmlns=\"" << XLIFF12namespaceURI
+ << "\" xmlns:trolltech=\"" << TrollTsNamespaceURI << "\">\n";
+ ++indent;
+ writeExtras(ts, indent, translator.extras(), drops);
+ foreach (const QString &fn, fileOrder) {
+ writeIndent(ts, indent);
+ ts << "<file original=\"" << fn << "\""
+ << " datatype=\"" << dataType(messageOrder[fn].begin()->first()) << "\""
+ << " source-language=\""
+ << (translator.sourceLanguageCode().isEmpty() ?
+ QByteArray("en") : translator.sourceLanguageCode().toLatin1()) << "\""
+ << " target-language=\"" << translator.languageCode() << "\""
+ << "><body>\n";
+ ++indent;
+
+ foreach (const QString &ctx, contextOrder[fn]) {
+ if (!ctx.isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<group restype=\"" << restypeContext << "\""
+ << " resname=\"" << protect(ctx) << "\">\n";
+ ++indent;
+ }
+
+ foreach (const TranslatorMessage &msg, messageOrder[fn][ctx])
+ writeMessage(ts, msg, drops, indent, translator, cd, &ok);
+
+ if (!ctx.isEmpty()) {
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</group>\n";
+ }
+ }
+
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</body></file>\n";
+ }
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</xliff>\n";
+
+ return ok;
+}
+
+int initXLIFF()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("xlf");
+ format.description = QObject::tr("XLIFF localization files");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 1;
+ format.loader = &loadXLIFF;
+ format.saver = &saveXLIFF;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initXLIFF)
+
+QT_END_NAMESPACE