+// get everything else (including the leading '_')
+static inline wxString ExtractNotLang(const wxString& langFull)
+{
+ return langFull.Mid(LEN_LANG);
+}
+
+#endif // __UNIX__
+
+
+// ----------------------------------------------------------------------------
+// Plural forms parser
+// ----------------------------------------------------------------------------
+
+/*
+ Simplified Grammar
+
+Expression:
+ LogicalOrExpression '?' Expression ':' Expression
+ LogicalOrExpression
+
+LogicalOrExpression:
+ LogicalAndExpression "||" LogicalOrExpression // to (a || b) || c
+ LogicalAndExpression
+
+LogicalAndExpression:
+ EqualityExpression "&&" LogicalAndExpression // to (a && b) && c
+ EqualityExpression
+
+EqualityExpression:
+ RelationalExpression "==" RelationalExperession
+ RelationalExpression "!=" RelationalExperession
+ RelationalExpression
+
+RelationalExpression:
+ MultiplicativeExpression '>' MultiplicativeExpression
+ MultiplicativeExpression '<' MultiplicativeExpression
+ MultiplicativeExpression ">=" MultiplicativeExpression
+ MultiplicativeExpression "<=" MultiplicativeExpression
+ MultiplicativeExpression
+
+MultiplicativeExpression:
+ PmExpression '%' PmExpression
+ PmExpression
+
+PmExpression:
+ N
+ Number
+ '(' Expression ')'
+*/
+
+class wxPluralFormsToken
+{
+public:
+ enum Type
+ {
+ T_ERROR, T_EOF, T_NUMBER, T_N, T_PLURAL, T_NPLURALS, T_EQUAL, T_ASSIGN,
+ T_GREATER, T_GREATER_OR_EQUAL, T_LESS, T_LESS_OR_EQUAL,
+ T_REMINDER, T_NOT_EQUAL,
+ T_LOGICAL_AND, T_LOGICAL_OR, T_QUESTION, T_COLON, T_SEMICOLON,
+ T_LEFT_BRACKET, T_RIGHT_BRACKET
+ };
+ Type type() const { return m_type; }
+ void setType(Type type) { m_type = type; }
+ // for T_NUMBER only
+ typedef int Number;
+ Number number() const { return m_number; }
+ void setNumber(Number num) { m_number = num; }
+private:
+ Type m_type;
+ Number m_number;
+};
+
+
+class wxPluralFormsScanner
+{
+public:
+ wxPluralFormsScanner(const char* s);
+ const wxPluralFormsToken& token() const { return m_token; }
+ bool nextToken(); // returns false if error
+private:
+ const char* m_s;
+ wxPluralFormsToken m_token;
+};
+
+wxPluralFormsScanner::wxPluralFormsScanner(const char* s) : m_s(s)
+{
+ nextToken();
+}
+
+bool wxPluralFormsScanner::nextToken()
+{
+ wxPluralFormsToken::Type type = wxPluralFormsToken::T_ERROR;
+ while (isspace(*m_s))
+ {
+ ++m_s;
+ }
+ if (*m_s == 0)
+ {
+ type = wxPluralFormsToken::T_EOF;
+ }
+ else if (isdigit(*m_s))
+ {
+ wxPluralFormsToken::Number number = *m_s++ - '0';
+ while (isdigit(*m_s))
+ {
+ number = number * 10 + (*m_s++ - '0');
+ }
+ m_token.setNumber(number);
+ type = wxPluralFormsToken::T_NUMBER;
+ }
+ else if (isalpha(*m_s))
+ {
+ const char* begin = m_s++;
+ while (isalnum(*m_s))
+ {
+ ++m_s;
+ }
+ size_t size = m_s - begin;
+ if (size == 1 && memcmp(begin, "n", size) == 0)
+ {
+ type = wxPluralFormsToken::T_N;
+ }
+ else if (size == 6 && memcmp(begin, "plural", size) == 0)
+ {
+ type = wxPluralFormsToken::T_PLURAL;
+ }
+ else if (size == 8 && memcmp(begin, "nplurals", size) == 0)
+ {
+ type = wxPluralFormsToken::T_NPLURALS;
+ }
+ }
+ else if (*m_s == '=')
+ {
+ ++m_s;
+ if (*m_s == '=')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_EQUAL;
+ }
+ else
+ {
+ type = wxPluralFormsToken::T_ASSIGN;
+ }
+ }
+ else if (*m_s == '>')
+ {
+ ++m_s;
+ if (*m_s == '=')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_GREATER_OR_EQUAL;
+ }
+ else
+ {
+ type = wxPluralFormsToken::T_GREATER;
+ }
+ }
+ else if (*m_s == '<')
+ {
+ ++m_s;
+ if (*m_s == '=')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_LESS_OR_EQUAL;
+ }
+ else
+ {
+ type = wxPluralFormsToken::T_LESS;
+ }
+ }
+ else if (*m_s == '%')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_REMINDER;
+ }
+ else if (*m_s == '!' && m_s[1] == '=')
+ {
+ m_s += 2;
+ type = wxPluralFormsToken::T_NOT_EQUAL;
+ }
+ else if (*m_s == '&' && m_s[1] == '&')
+ {
+ m_s += 2;
+ type = wxPluralFormsToken::T_LOGICAL_AND;
+ }
+ else if (*m_s == '|' && m_s[1] == '|')
+ {
+ m_s += 2;
+ type = wxPluralFormsToken::T_LOGICAL_OR;
+ }
+ else if (*m_s == '?')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_QUESTION;
+ }
+ else if (*m_s == ':')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_COLON;
+ } else if (*m_s == ';') {
+ ++m_s;
+ type = wxPluralFormsToken::T_SEMICOLON;
+ }
+ else if (*m_s == '(')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_LEFT_BRACKET;
+ }
+ else if (*m_s == ')')
+ {
+ ++m_s;
+ type = wxPluralFormsToken::T_RIGHT_BRACKET;
+ }
+ m_token.setType(type);
+ return type != wxPluralFormsToken::T_ERROR;
+}
+
+class wxPluralFormsNode;
+
+// NB: Can't use wxDEFINE_SCOPED_PTR_TYPE because wxPluralFormsNode is not
+// fully defined yet:
+class wxPluralFormsNodePtr
+{
+public:
+ wxPluralFormsNodePtr(wxPluralFormsNode *p = NULL) : m_p(p) {}
+ ~wxPluralFormsNodePtr();
+ wxPluralFormsNode& operator*() const { return *m_p; }
+ wxPluralFormsNode* operator->() const { return m_p; }
+ wxPluralFormsNode* get() const { return m_p; }
+ wxPluralFormsNode* release();
+ void reset(wxPluralFormsNode *p);
+
+private:
+ wxPluralFormsNode *m_p;
+};
+
+class wxPluralFormsNode
+{
+public:
+ wxPluralFormsNode(const wxPluralFormsToken& token) : m_token(token) {}
+ const wxPluralFormsToken& token() const { return m_token; }
+ const wxPluralFormsNode* node(size_t i) const
+ { return m_nodes[i].get(); }
+ void setNode(size_t i, wxPluralFormsNode* n);
+ wxPluralFormsNode* releaseNode(size_t i);
+ wxPluralFormsToken::Number evaluate(wxPluralFormsToken::Number n) const;
+
+private:
+ wxPluralFormsToken m_token;
+ wxPluralFormsNodePtr m_nodes[3];
+};
+
+wxPluralFormsNodePtr::~wxPluralFormsNodePtr()
+{
+ delete m_p;
+}
+wxPluralFormsNode* wxPluralFormsNodePtr::release()
+{
+ wxPluralFormsNode *p = m_p;
+ m_p = NULL;
+ return p;
+}
+void wxPluralFormsNodePtr::reset(wxPluralFormsNode *p)
+{
+ if (p != m_p)
+ {
+ delete m_p;
+ m_p = p;
+ }
+}
+
+
+void wxPluralFormsNode::setNode(size_t i, wxPluralFormsNode* n)
+{
+ m_nodes[i].reset(n);
+}
+
+wxPluralFormsNode* wxPluralFormsNode::releaseNode(size_t i)
+{
+ return m_nodes[i].release();
+}
+
+wxPluralFormsToken::Number
+wxPluralFormsNode::evaluate(wxPluralFormsToken::Number n) const
+{
+ switch (token().type())
+ {
+ // leaf
+ case wxPluralFormsToken::T_NUMBER:
+ return token().number();
+ case wxPluralFormsToken::T_N:
+ return n;
+ // 2 args
+ case wxPluralFormsToken::T_EQUAL:
+ return node(0)->evaluate(n) == node(1)->evaluate(n);
+ case wxPluralFormsToken::T_NOT_EQUAL:
+ return node(0)->evaluate(n) != node(1)->evaluate(n);
+ case wxPluralFormsToken::T_GREATER:
+ return node(0)->evaluate(n) > node(1)->evaluate(n);
+ case wxPluralFormsToken::T_GREATER_OR_EQUAL:
+ return node(0)->evaluate(n) >= node(1)->evaluate(n);
+ case wxPluralFormsToken::T_LESS:
+ return node(0)->evaluate(n) < node(1)->evaluate(n);
+ case wxPluralFormsToken::T_LESS_OR_EQUAL:
+ return node(0)->evaluate(n) <= node(1)->evaluate(n);
+ case wxPluralFormsToken::T_REMINDER:
+ {
+ wxPluralFormsToken::Number number = node(1)->evaluate(n);
+ if (number != 0)
+ {
+ return node(0)->evaluate(n) % number;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ case wxPluralFormsToken::T_LOGICAL_AND:
+ return node(0)->evaluate(n) && node(1)->evaluate(n);
+ case wxPluralFormsToken::T_LOGICAL_OR:
+ return node(0)->evaluate(n) || node(1)->evaluate(n);
+ // 3 args
+ case wxPluralFormsToken::T_QUESTION:
+ return node(0)->evaluate(n)
+ ? node(1)->evaluate(n)
+ : node(2)->evaluate(n);
+ default:
+ return 0;
+ }
+}
+
+
+class wxPluralFormsCalculator
+{
+public:
+ wxPluralFormsCalculator() : m_nplurals(0), m_plural(0) {}
+
+ // input: number, returns msgstr index
+ int evaluate(int n) const;
+
+ // input: text after "Plural-Forms:" (e.g. "nplurals=2; plural=(n != 1);"),
+ // if s == 0, creates default handler
+ // returns 0 if error
+ static wxPluralFormsCalculator* make(const char* s = 0);
+
+ ~wxPluralFormsCalculator() {}
+
+ void init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural);
+ wxString getString() const;
+
+private:
+ wxPluralFormsToken::Number m_nplurals;
+ wxPluralFormsNodePtr m_plural;
+};
+
+wxDEFINE_SCOPED_PTR_TYPE(wxPluralFormsCalculator);
+
+void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals,
+ wxPluralFormsNode* plural)
+{
+ m_nplurals = nplurals;
+ m_plural.reset(plural);
+}
+
+int wxPluralFormsCalculator::evaluate(int n) const
+{
+ if (m_plural.get() == 0)
+ {
+ return 0;
+ }
+ wxPluralFormsToken::Number number = m_plural->evaluate(n);
+ if (number < 0 || number > m_nplurals)
+ {
+ return 0;
+ }
+ return number;
+}
+
+
+class wxPluralFormsParser
+{
+public:
+ wxPluralFormsParser(wxPluralFormsScanner& scanner) : m_scanner(scanner) {}
+ bool parse(wxPluralFormsCalculator& rCalculator);
+
+private:
+ wxPluralFormsNode* parsePlural();
+ // stops at T_SEMICOLON, returns 0 if error
+ wxPluralFormsScanner& m_scanner;
+ const wxPluralFormsToken& token() const;
+ bool nextToken();
+
+ wxPluralFormsNode* expression();
+ wxPluralFormsNode* logicalOrExpression();
+ wxPluralFormsNode* logicalAndExpression();
+ wxPluralFormsNode* equalityExpression();
+ wxPluralFormsNode* multiplicativeExpression();
+ wxPluralFormsNode* relationalExpression();
+ wxPluralFormsNode* pmExpression();
+};
+
+bool wxPluralFormsParser::parse(wxPluralFormsCalculator& rCalculator)
+{
+ if (token().type() != wxPluralFormsToken::T_NPLURALS)
+ return false;
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_ASSIGN)
+ return false;
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_NUMBER)
+ return false;
+ wxPluralFormsToken::Number nplurals = token().number();
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_SEMICOLON)
+ return false;
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_PLURAL)
+ return false;
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_ASSIGN)
+ return false;
+ if (!nextToken())
+ return false;
+ wxPluralFormsNode* plural = parsePlural();
+ if (plural == 0)
+ return false;
+ if (token().type() != wxPluralFormsToken::T_SEMICOLON)
+ return false;
+ if (!nextToken())
+ return false;
+ if (token().type() != wxPluralFormsToken::T_EOF)
+ return false;
+ rCalculator.init(nplurals, plural);
+ return true;
+}
+
+wxPluralFormsNode* wxPluralFormsParser::parsePlural()
+{
+ wxPluralFormsNode* p = expression();
+ if (p == NULL)
+ {
+ return NULL;
+ }
+ wxPluralFormsNodePtr n(p);
+ if (token().type() != wxPluralFormsToken::T_SEMICOLON)
+ {
+ return NULL;
+ }
+ return n.release();
+}
+
+const wxPluralFormsToken& wxPluralFormsParser::token() const
+{
+ return m_scanner.token();
+}
+
+bool wxPluralFormsParser::nextToken()
+{
+ if (!m_scanner.nextToken())
+ return false;
+ return true;
+}
+
+wxPluralFormsNode* wxPluralFormsParser::expression()
+{
+ wxPluralFormsNode* p = logicalOrExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr n(p);
+ if (token().type() == wxPluralFormsToken::T_QUESTION)
+ {
+ wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return 0;
+ }
+ p = expression();
+ if (p == 0)
+ {
+ return 0;
+ }
+ qn->setNode(1, p);
+ if (token().type() != wxPluralFormsToken::T_COLON)
+ {
+ return 0;
+ }
+ if (!nextToken())
+ {
+ return 0;
+ }
+ p = expression();
+ if (p == 0)
+ {
+ return 0;
+ }
+ qn->setNode(2, p);
+ qn->setNode(0, n.release());
+ return qn.release();
+ }
+ return n.release();
+}
+
+wxPluralFormsNode*wxPluralFormsParser::logicalOrExpression()
+{
+ wxPluralFormsNode* p = logicalAndExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr ln(p);
+ if (token().type() == wxPluralFormsToken::T_LOGICAL_OR)
+ {
+ wxPluralFormsNodePtr un(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return 0;
+ }
+ p = logicalOrExpression();
+ if (p == 0)
+ {
+ return 0;
+ }
+ wxPluralFormsNodePtr rn(p); // right
+ if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_OR)
+ {
+ // see logicalAndExpression comment
+ un->setNode(0, ln.release());
+ un->setNode(1, rn->releaseNode(0));
+ rn->setNode(0, un.release());
+ return rn.release();
+ }
+
+
+ un->setNode(0, ln.release());
+ un->setNode(1, rn.release());
+ return un.release();
+ }
+ return ln.release();
+}
+
+wxPluralFormsNode* wxPluralFormsParser::logicalAndExpression()
+{
+ wxPluralFormsNode* p = equalityExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr ln(p); // left
+ if (token().type() == wxPluralFormsToken::T_LOGICAL_AND)
+ {
+ wxPluralFormsNodePtr un(new wxPluralFormsNode(token())); // up
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ p = logicalAndExpression();
+ if (p == 0)
+ {
+ return NULL;
+ }
+ wxPluralFormsNodePtr rn(p); // right
+ if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_AND)
+ {
+// transform 1 && (2 && 3) -> (1 && 2) && 3
+// u r
+// l r -> u 3
+// 2 3 l 2
+ un->setNode(0, ln.release());
+ un->setNode(1, rn->releaseNode(0));
+ rn->setNode(0, un.release());
+ return rn.release();
+ }
+
+ un->setNode(0, ln.release());
+ un->setNode(1, rn.release());
+ return un.release();
+ }
+ return ln.release();
+}
+
+wxPluralFormsNode* wxPluralFormsParser::equalityExpression()
+{
+ wxPluralFormsNode* p = relationalExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr n(p);
+ if (token().type() == wxPluralFormsToken::T_EQUAL
+ || token().type() == wxPluralFormsToken::T_NOT_EQUAL)
+ {
+ wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ p = relationalExpression();
+ if (p == NULL)
+ {
+ return NULL;
+ }
+ qn->setNode(1, p);
+ qn->setNode(0, n.release());
+ return qn.release();
+ }
+ return n.release();
+}
+
+wxPluralFormsNode* wxPluralFormsParser::relationalExpression()
+{
+ wxPluralFormsNode* p = multiplicativeExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr n(p);
+ if (token().type() == wxPluralFormsToken::T_GREATER
+ || token().type() == wxPluralFormsToken::T_LESS
+ || token().type() == wxPluralFormsToken::T_GREATER_OR_EQUAL
+ || token().type() == wxPluralFormsToken::T_LESS_OR_EQUAL)
+ {
+ wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ p = multiplicativeExpression();
+ if (p == NULL)
+ {
+ return NULL;
+ }
+ qn->setNode(1, p);
+ qn->setNode(0, n.release());
+ return qn.release();
+ }
+ return n.release();
+}
+
+wxPluralFormsNode* wxPluralFormsParser::multiplicativeExpression()
+{
+ wxPluralFormsNode* p = pmExpression();
+ if (p == NULL)
+ return NULL;
+ wxPluralFormsNodePtr n(p);
+ if (token().type() == wxPluralFormsToken::T_REMINDER)
+ {
+ wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ p = pmExpression();
+ if (p == NULL)
+ {
+ return NULL;
+ }
+ qn->setNode(1, p);
+ qn->setNode(0, n.release());
+ return qn.release();
+ }
+ return n.release();
+}
+
+wxPluralFormsNode* wxPluralFormsParser::pmExpression()
+{
+ wxPluralFormsNodePtr n;
+ if (token().type() == wxPluralFormsToken::T_N
+ || token().type() == wxPluralFormsToken::T_NUMBER)
+ {
+ n.reset(new wxPluralFormsNode(token()));
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ }
+ else if (token().type() == wxPluralFormsToken::T_LEFT_BRACKET) {
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ wxPluralFormsNode* p = expression();
+ if (p == NULL)
+ {
+ return NULL;
+ }
+ n.reset(p);
+ if (token().type() != wxPluralFormsToken::T_RIGHT_BRACKET)
+ {
+ return NULL;
+ }
+ if (!nextToken())
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+ return n.release();
+}
+
+wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s)