diff options
author | Timothy Pearson <[email protected]> | 2011-11-08 12:31:36 -0600 |
---|---|---|
committer | Timothy Pearson <[email protected]> | 2011-11-08 12:31:36 -0600 |
commit | d796c9dd933ab96ec83b9a634feedd5d32e1ba3f (patch) | |
tree | 6e3dcca4f77e20ec8966c666aac7c35bd4704053 /tools/linguist/shared | |
download | tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.tar.gz tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.zip |
Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731
Diffstat (limited to 'tools/linguist/shared')
-rw-r--r-- | tools/linguist/shared/metatranslator.cpp | 601 | ||||
-rw-r--r-- | tools/linguist/shared/metatranslator.h | 120 | ||||
-rw-r--r-- | tools/linguist/shared/proparser.cpp | 217 | ||||
-rw-r--r-- | tools/linguist/shared/proparser.h | 42 |
4 files changed, 980 insertions, 0 deletions
diff --git a/tools/linguist/shared/metatranslator.cpp b/tools/linguist/shared/metatranslator.cpp new file mode 100644 index 000000000..2e02f1c08 --- /dev/null +++ b/tools/linguist/shared/metatranslator.cpp @@ -0,0 +1,601 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of TQt Linguist. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** Licensees holding valid TQt Commercial licenses may use this file in +** accordance with the TQt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "metatranslator.h" + +#include <qapplication.h> +#include <qcstring.h> +#include <qfile.h> +#include <qmessagebox.h> +#include <qtextcodec.h> +#include <qtextstream.h> +#include <qxml.h> + +static bool encodingIsUtf8( const TQXmlAttributes& atts ) +{ + for ( int i = 0; i < atts.length(); i++ ) { + // utf8="true" is a pre-3.0 syntax + if ( atts.qName(i) == TQString("utf8") ) { + return ( atts.value(i) == TQString("true") ); + } else if ( atts.qName(i) == TQString("encoding") ) { + return ( atts.value(i) == TQString("UTF-8") ); + } + } + return FALSE; +} + +class TsHandler : public TQXmlDefaultHandler +{ +public: + TsHandler( MetaTranslator *translator ) + : tor( translator ), type( MetaTranslatorMessage::Finished ), + inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ), + messageIsUtf8( FALSE ) { } + + virtual bool startElement( const TQString& namespaceURI, + const TQString& localName, const TQString& qName, + const TQXmlAttributes& atts ); + virtual bool endElement( const TQString& namespaceURI, + const TQString& localName, const TQString& qName ); + virtual bool characters( const TQString& ch ); + virtual bool fatalError( const TQXmlParseException& exception ); + +private: + MetaTranslator *tor; + MetaTranslatorMessage::Type type; + bool inMessage; + TQString context; + TQString source; + TQString comment; + TQString translation; + + TQString accum; + int ferrorCount; + bool contextIsUtf8; + bool messageIsUtf8; +}; + +bool TsHandler::startElement( const TQString& /* namespaceURI */, + const TQString& /* localName */, + const TQString& qName, + const TQXmlAttributes& atts ) +{ + if ( qName == TQString("byte") ) { + for ( int i = 0; i < atts.length(); i++ ) { + if ( atts.qName(i) == TQString("value") ) { + TQString value = atts.value( i ); + int base = 10; + if ( value.startsWith("x") ) { + base = 16; + value = value.mid( 1 ); + } + int n = value.toUInt( 0, base ); + if ( n != 0 ) + accum += TQChar( n ); + } + } + } else { + if ( qName == TQString("context") ) { + context.truncate( 0 ); + source.truncate( 0 ); + comment.truncate( 0 ); + translation.truncate( 0 ); + contextIsUtf8 = encodingIsUtf8( atts ); + } else if ( qName == TQString("message") ) { + inMessage = TRUE; + type = MetaTranslatorMessage::Finished; + source.truncate( 0 ); + comment.truncate( 0 ); + translation.truncate( 0 ); + messageIsUtf8 = encodingIsUtf8( atts ); + } else if ( qName == TQString("translation") ) { + for ( int i = 0; i < atts.length(); i++ ) { + if ( atts.qName(i) == TQString("type") ) { + if ( atts.value(i) == TQString("unfinished") ) + type = MetaTranslatorMessage::Unfinished; + else if ( atts.value(i) == TQString("obsolete") ) + type = MetaTranslatorMessage::Obsolete; + else + type = MetaTranslatorMessage::Finished; + } + } + } + accum.truncate( 0 ); + } + return TRUE; +} + +bool TsHandler::endElement( const TQString& /* namespaceURI */, + const TQString& /* localName */, + const TQString& qName ) +{ + if ( qName == TQString("codec") || qName == TQString("defaultcodec") ) { + // "codec" is a pre-3.0 syntax + tor->setCodec( accum ); + } else if ( qName == TQString("name") ) { + context = accum; + } else if ( qName == TQString("source") ) { + source = accum; + } else if ( qName == TQString("comment") ) { + if ( inMessage ) { + comment = accum; + } else { + if ( contextIsUtf8 ) + tor->insert( MetaTranslatorMessage(context.utf8(), + ContextComment, + accum.utf8(), TQString::null, TRUE, + MetaTranslatorMessage::Unfinished) ); + else + tor->insert( MetaTranslatorMessage(context.ascii(), + ContextComment, + accum.ascii(), TQString::null, FALSE, + MetaTranslatorMessage::Unfinished) ); + } + } else if ( qName == TQString("translation") ) { + translation = accum; + } else if ( qName == TQString("message") ) { + if ( messageIsUtf8 ) + tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), + comment.utf8(), translation, + TRUE, type) ); + else + tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(), + comment.ascii(), translation, + FALSE, type) ); + inMessage = FALSE; + } + return TRUE; +} + +bool TsHandler::characters( const TQString& ch ) +{ + TQString t = ch; + t.replace( "\r", "" ); + accum += t; + return TRUE; +} + +bool TsHandler::fatalError( const TQXmlParseException& exception ) +{ + if ( ferrorCount++ == 0 ) { + TQString msg; + msg.sprintf( "Parse error at line %d, column %d (%s).", + exception.lineNumber(), exception.columnNumber(), + exception.message().latin1() ); + if ( qApp == 0 ) + fprintf( stderr, "XML error: %s\n", msg.latin1() ); + else + TQMessageBox::information( qApp->mainWidget(), + TQObject::tr("TQt Linguist"), msg ); + } + return FALSE; +} + +static TQString numericEntity( int ch ) +{ + return TQString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" ) + .arg( ch, 0, 16 ); +} + +static TQString protect( const TQCString& str ) +{ + TQString result; + int len = (int) str.length(); + for ( int k = 0; k < len; k++ ) { + switch( str[k] ) { + case '\"': + result += TQString( """ ); + break; + case '&': + result += TQString( "&" ); + break; + case '>': + result += TQString( ">" ); + break; + case '<': + result += TQString( "<" ); + break; + case '\'': + result += TQString( "'" ); + break; + default: + if ( (uchar) str[k] < 0x20 && str[k] != '\n' ) + result += numericEntity( (uchar) str[k] ); + else + result += str[k]; + } + } + return result; +} + +static TQString evilBytes( const TQCString& str, bool utf8 ) +{ + if ( utf8 ) { + return protect( str ); + } else { + TQString result; + TQCString t = protect( str ).latin1(); + int len = (int) t.length(); + for ( int k = 0; k < len; k++ ) { + if ( (uchar) t[k] >= 0x7f ) + result += numericEntity( (uchar) t[k] ); + else + result += TQChar( t[k] ); + } + return result; + } +} + +MetaTranslatorMessage::MetaTranslatorMessage() + : utfeight( FALSE ), ty( Unfinished ) +{ +} + +MetaTranslatorMessage::MetaTranslatorMessage( const char *context, + const char *sourceText, + const char *comment, + const TQString& translation, + bool utf8, Type type ) + : TQTranslatorMessage( context, sourceText, comment, translation ), + utfeight( FALSE ), ty( type ) +{ + /* + Don't use UTF-8 if it makes no difference. UTF-8 should be + reserved for the real problematic case: non-ASCII (possibly + non-Latin-1) characters in .ui files. + */ + if ( utf8 ) { + if ( sourceText != 0 ) { + int i = 0; + while ( sourceText[i] != '\0' ) { + if ( (uchar) sourceText[i] >= 0x80 ) { + utfeight = TRUE; + break; + } + i++; + } + } + if ( !utfeight && comment != 0 ) { + int i = 0; + while ( comment[i] != '\0' ) { + if ( (uchar) comment[i] >= 0x80 ) { + utfeight = TRUE; + break; + } + i++; + } + } + } +} + +MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m ) + : TQTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty ) +{ +} + +MetaTranslatorMessage& MetaTranslatorMessage::operator=( + const MetaTranslatorMessage& m ) +{ + TQTranslatorMessage::operator=( m ); + utfeight = m.utfeight; + ty = m.ty; + return *this; +} + +bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const +{ + return qstrcmp( context(), m.context() ) == 0 && + qstrcmp( sourceText(), m.sourceText() ) == 0 && + qstrcmp( comment(), m.comment() ) == 0; +} + +bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const +{ + int delta = qstrcmp( context(), m.context() ); + if ( delta == 0 ) + delta = qstrcmp( sourceText(), m.sourceText() ); + if ( delta == 0 ) + delta = qstrcmp( comment(), m.comment() ); + return delta < 0; +} + +MetaTranslator::MetaTranslator() +{ + clear(); +} + +MetaTranslator::MetaTranslator( const MetaTranslator& tor ) + : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec ) +{ +} + +MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor ) +{ + mm = tor.mm; + codecName = tor.codecName; + codec = tor.codec; + return *this; +} + +void MetaTranslator::clear() +{ + mm.clear(); + codecName = "ISO-8859-1"; + codec = 0; +} + +bool MetaTranslator::load( const TQString& filename ) +{ + TQFile f( filename ); + if ( !f.open(IO_ReadOnly) ) + return FALSE; + + TQTextStream t( &f ); + TQXmlInputSource in( t ); + TQXmlSimpleReader reader; + reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE ); + reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE ); + TQXmlDefaultHandler *hand = new TsHandler( this ); + reader.setContentHandler( hand ); + reader.setErrorHandler( hand ); + + bool ok = reader.parse( in ); + reader.setContentHandler( 0 ); + reader.setErrorHandler( 0 ); + delete hand; + f.close(); + return ok; +} + +bool MetaTranslator::save( const TQString& filename ) const +{ + TQFile f( filename ); + if ( !f.open(IO_WriteOnly) ) + return FALSE; + + TQTextStream t( &f ); + t.setCodec( TQTextCodec::codecForName("ISO-8859-1") ); + + t << "<!DOCTYPE TS><TS>\n"; + if ( codecName != "ISO-8859-1" ) + t << "<defaultcodec>" << codecName << "</defaultcodec>\n"; + TMM::ConstIterator m = mm.begin(); + while ( m != mm.end() ) { + TMMInv inv; + TMMInv::Iterator i; + bool contextIsUtf8 = m.key().utf8(); + TQCString context = m.key().context(); + TQCString comment = ""; + + do { + if ( TQCString(m.key().sourceText()) == ContextComment ) { + if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { + contextIsUtf8 = m.key().utf8(); + comment = TQCString( m.key().comment() ); + } + } else { + inv.insert( *m, m.key() ); + } + } while ( ++m != mm.end() && TQCString(m.key().context()) == context ); + + t << "<context"; + if ( contextIsUtf8 ) + t << " encoding=\"UTF-8\""; + t << ">\n"; + t << " <name>" << evilBytes( context, contextIsUtf8 ) + << "</name>\n"; + if ( !comment.isEmpty() ) + t << " <comment>" << evilBytes( comment, contextIsUtf8 ) + << "</comment>\n"; + + for ( i = inv.begin(); i != inv.end(); ++i ) { + // no need for such noise + if ( (*i).type() == MetaTranslatorMessage::Obsolete && + (*i).translation().isEmpty() ) + continue; + + t << " <message"; + if ( (*i).utf8() ) + t << " encoding=\"UTF-8\""; + t << ">\n" + << " <source>" << evilBytes( (*i).sourceText(), + (*i).utf8() ) + << "</source>\n"; + if ( !TQCString((*i).comment()).isEmpty() ) + t << " <comment>" << evilBytes( (*i).comment(), + (*i).utf8() ) + << "</comment>\n"; + t << " <translation"; + if ( (*i).type() == MetaTranslatorMessage::Unfinished ) + t << " type=\"unfinished\""; + else if ( (*i).type() == MetaTranslatorMessage::Obsolete ) + t << " type=\"obsolete\""; + t << ">" << protect( (*i).translation().utf8() ) + << "</translation>\n"; + t << " </message>\n"; + } + t << "</context>\n"; + } + t << "</TS>\n"; + f.close(); + return TRUE; +} + +bool MetaTranslator::release( const TQString& filename, bool verbose, + TQTranslator::SaveMode mode ) const +{ + TQTranslator tor( 0 ); + int finished = 0; + int unfinished = 0; + int untranslated = 0; + TMM::ConstIterator m; + + for ( m = mm.begin(); m != mm.end(); ++m ) { + if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { + if ( m.key().translation().isEmpty() ) { + untranslated++; + } else { + if ( m.key().type() == MetaTranslatorMessage::Unfinished ) + unfinished++; + else + finished++; + + TQCString context = m.key().context(); + TQCString sourceText = m.key().sourceText(); + TQCString comment = m.key().comment(); + TQString translation = m.key().translation(); + + /* + Drop the comment in (context, sourceText, comment), + unless (context, sourceText, "") already exists, or + unless we already dropped the comment of (context, + sourceText, comment0). + */ + if ( comment.isEmpty() + || contains(context, sourceText, "") + || !tor.findMessage(context, sourceText, "").translation() + .isNull() ) { + tor.insert( m.key() ); + } else { + tor.insert( TQTranslatorMessage(context, sourceText, "", + translation) ); + } + } + } + } + + bool saved = tor.save( filename, mode ); + if ( saved && verbose ) + fprintf( stderr, + " %d finished, %d unfinished and %d untranslated messages\n", + finished, unfinished, untranslated ); + + return saved; +} + +bool MetaTranslator::contains( const char *context, const char *sourceText, + const char *comment ) const +{ + return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) != + mm.end(); +} + +void MetaTranslator::insert( const MetaTranslatorMessage& m ) +{ + int pos = (int)mm.count(); + TMM::Iterator n = mm.find( m ); + if ( n != mm.end() ) + pos = *n; + mm.replace( m, pos ); +} + +void MetaTranslator::stripObsoleteMessages() +{ + TMM newmm; + + TMM::Iterator m = mm.begin(); + while ( m != mm.end() ) { + if ( m.key().type() != MetaTranslatorMessage::Obsolete ) + newmm.insert( m.key(), *m ); + ++m; + } + mm = newmm; +} + +void MetaTranslator::stripEmptyContexts() +{ + TMM newmm; + + TMM::Iterator m = mm.begin(); + while ( m != mm.end() ) { + if ( TQCString(m.key().sourceText()) == ContextComment ) { + TMM::Iterator n = m; + ++n; + // the context comment is followed by other messages + if ( n != newmm.end() && + qstrcmp(m.key().context(), n.key().context()) == 0 ) + newmm.insert( m.key(), *m ); + } else { + newmm.insert( m.key(), *m ); + } + ++m; + } + mm = newmm; +} + +void MetaTranslator::setCodec( const char *name ) +{ + const int latin1 = 4; + + codecName = name; + codec = TQTextCodec::codecForName( name ); + if ( codec == 0 || codec->mibEnum() == latin1 ) + codec = 0; +} + +TQString MetaTranslator::toUnicode( const char *str, bool utf8 ) const +{ + if ( utf8 ) + return TQString::fromUtf8( str ); + else if ( codec == 0 ) + return TQString( str ); + else + return codec->toUnicode( str ); +} + +TQValueList<MetaTranslatorMessage> MetaTranslator::messages() const +{ + int n = (int)mm.count(); + TMM::ConstIterator *t = new TMM::ConstIterator[n + 1]; + TMM::ConstIterator m; + for ( m = mm.begin(); m != mm.end(); ++m ) + t[*m] = m; + + TQValueList<MetaTranslatorMessage> val; + for ( int i = 0; i < n; i++ ) + val.append( t[i].key() ); + + delete[] t; + return val; +} + +TQValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const +{ + TQValueList<MetaTranslatorMessage> val; + TMM::ConstIterator m; + for ( m = mm.begin(); m != mm.end(); ++m ) { + if ( m.key().type() == MetaTranslatorMessage::Finished ) + val.append( m.key() ); + } + return val; +} diff --git a/tools/linguist/shared/metatranslator.h b/tools/linguist/shared/metatranslator.h new file mode 100644 index 000000000..e4e3199d4 --- /dev/null +++ b/tools/linguist/shared/metatranslator.h @@ -0,0 +1,120 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of TQt Linguist. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** Licensees holding valid TQt Commercial licenses may use this file in +** accordance with the TQt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef METATRANSLATOR_H +#define METATRANSLATOR_H + +#include <qmap.h> +#include <qstring.h> +#include <qtranslator.h> +#include <qvaluelist.h> + +class TQTextCodec; + +class MetaTranslatorMessage : public TQTranslatorMessage +{ +public: + enum Type { Unfinished, Finished, Obsolete }; + + MetaTranslatorMessage(); + MetaTranslatorMessage( const char *context, const char *sourceText, + const char *comment, + const TQString& translation = TQString::null, + bool utf8 = FALSE, Type type = Unfinished ); + MetaTranslatorMessage( const MetaTranslatorMessage& m ); + + MetaTranslatorMessage& operator=( const MetaTranslatorMessage& m ); + + void setType( Type nt ) { ty = nt; } + Type type() const { return ty; } + bool utf8() const { return utfeight; } + + bool operator==( const MetaTranslatorMessage& m ) const; + bool operator!=( const MetaTranslatorMessage& m ) const + { return !operator==( m ); } + bool operator<( const MetaTranslatorMessage& m ) const; + bool operator<=( const MetaTranslatorMessage& m ) + { return !operator>( m ); } + bool operator>( const MetaTranslatorMessage& m ) const + { return this->operator<( m ); } + bool operator>=( const MetaTranslatorMessage& m ) const + { return !operator<( m ); } + +private: + bool utfeight; + Type ty; +}; + +class MetaTranslator +{ +public: + MetaTranslator(); + MetaTranslator( const MetaTranslator& tor ); + + MetaTranslator& operator=( const MetaTranslator& tor ); + + void clear(); + bool load( const TQString& filename ); + bool save( const TQString& filename ) const; + bool release( const TQString& filename, bool verbose = FALSE, + TQTranslator::SaveMode mode = TQTranslator::Stripped ) const; + + bool contains( const char *context, const char *sourceText, + const char *comment ) const; + void insert( const MetaTranslatorMessage& m ); + + void stripObsoleteMessages(); + void stripEmptyContexts(); + + void setCodec( const char *name ); + TQString toUnicode( const char *str, bool utf8 ) const; + + TQValueList<MetaTranslatorMessage> messages() const; + TQValueList<MetaTranslatorMessage> translatedMessages() const; + +private: + typedef TQMap<MetaTranslatorMessage, int> TMM; + typedef TQMap<int, MetaTranslatorMessage> TMMInv; + + TMM mm; + TQCString codecName; + TQTextCodec *codec; +}; + +/* + This is a tquick hack. The proper way to handle this would be + to extend MetaTranslator's interface. +*/ +#define ContextComment "QT_LINGUIST_INTERNAL_CONTEXT_COMMENT" + +#endif diff --git a/tools/linguist/shared/proparser.cpp b/tools/linguist/shared/proparser.cpp new file mode 100644 index 000000000..ded46b5c1 --- /dev/null +++ b/tools/linguist/shared/proparser.cpp @@ -0,0 +1,217 @@ +/********************************************************************** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of TQt Linguist. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** Licensees holding valid TQt Commercial licenses may use this file in +** accordance with the TQt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "proparser.h" + +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> +#include <qstringlist.h> +#include <qtextstream.h> + +#ifdef Q_OS_UNIX +#include <unistd.h> +#endif + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#else +#define QT_POPEN popen +#endif + +TQString loadFile( const TQString &fileName ) +{ + TQFile file( fileName ); + if ( !file.open(IO_ReadOnly) ) { + fprintf( stderr, "error: Cannot load '%s': %s\n", + file.name().latin1(), + file.errorString().latin1() ); + return TQString(); + } + + TQTextStream in( &file ); + return in.read(); +} + +TQMap<TQString, TQString> proFileTagMap( const TQString& text ) +{ + TQString t = text; + TQMap<TQString, TQString> tagMap; + bool stillProcess = true; // If include() has a $$tag then we need to reprocess + + while(stillProcess) { + + /* + Strip any commments before we try to include. We + still need to do it after we include to make sure the + included file does not have comments + */ + t.replace( TQRegExp(TQString("#[^\n]*\n")), TQString(" ") ); + + /* + Process include() commands. + $$PWD is a special case so we have to change it while + we know where the included file is. + */ + TQRegExp callToInclude("include\\s*\\(\\s*([^()\\s]+)\\s*\\)"); + int i = 0; + while ( (i = callToInclude.search(t, i)) != -1 ) { + bool doneWithVar = false; + TQString fileName = callToInclude.cap(1); + TQString after = fileName.replace("$$PWD", TQDir::currentDirPath()); + if (!tagMap.isEmpty() && after.contains("$$")) { + TQRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" ); + int ii = 0; + while ((ii = after.find(var, ii)) != -1) { + if (tagMap.contains(var.cap(1))) { + after.replace(ii, var.cap(0).length(), tagMap[var.cap(1)]); + } else { // Couldn't find it + doneWithVar = true; + break; + } + } + + } + if (doneWithVar || !after.contains("$$")) { + after = loadFile(after); + TQFileInfo fi(callToInclude.cap(1)); + after.replace("$$PWD", fi.dirPath()); + t.replace( i, callToInclude.matchedLength(), after ); + } + i += after.length(); + } + + /* + Strip comments, merge lines ending with backslash, add + spaces around '=' and '+=', replace '\n' with ';', and + simplify white spaces. + */ + t.replace( TQRegExp(TQString("#[^\n]*\n")), TQString(" ") ); + t.replace( TQRegExp(TQString("\\\\[^\n\\S]*\n")), TQString(" ") ); + t.replace( "=", TQString(" = ") ); + t.replace( "+ =", TQString(" += ") ); + t.replace( "\n", TQString(";") ); + t = t.simplifyWhiteSpace(); + + /* + Populate tagMap with 'key = value' entries. + */ + TQStringList lines = TQStringList::split( TQChar(';'), t ); + TQStringList::Iterator line; + for ( line = lines.begin(); line != lines.end(); ++line ) { + TQStringList toks = TQStringList::split( TQChar(' '), *line ); + + if ( toks.count() >= 3 && + (toks[1] == TQString("=") || toks[1] == TQString("+=")) ) { + TQString tag = toks.first(); + int k = tag.findRev( TQChar(':') ); // as in 'unix:' + if ( k != -1 ) + tag = tag.mid( k + 1 ); + toks.remove( toks.begin() ); + + TQString action = toks.first(); + toks.remove( toks.begin() ); + + if ( tagMap.contains(tag) ) { + if ( action == TQString("=") ) + tagMap.replace( tag, toks.join(TQChar(' ')) ); + else + tagMap[tag] += TQChar( ' ' ) + toks.join( TQChar(' ') ); + } else { + tagMap[tag] = toks.join( TQChar(' ') ); + } + } + } + + /* + Expand $$variables within the 'value' part of a 'key = value' + pair. + */ + TQRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" ); + TQMap<TQString, TQString>::Iterator it; + for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { + int i = 0; + while ( (i = var.search((*it), i)) != -1 ) { + int len = var.matchedLength(); + TQString invocation = var.cap(1); + TQString after; + + if ( invocation == "system" ) { + // skip system(); it will be handled in the next pass + ++i; + } else { + if ( tagMap.contains(invocation) ) + after = tagMap[invocation]; + else if (invocation.lower() == "pwd") + after = TQDir::currentDirPath(); + (*it).replace( i, len, after ); + i += after.length(); + } + } + } + + /* + Execute system() calls. + */ + TQRegExp callToSystem( "\\$\\$system\\s*\\(([^()]*)\\)" ); + for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { + int i = 0; + while ( (i = callToSystem.search((*it), i)) != -1 ) { + /* + This code is stolen from qmake's project.cpp file. + Ideally we would use the same parser, so we wouldn't + have this code duplication. + */ + TQString after; + char buff[256]; + FILE *proc = QT_POPEN( callToSystem.cap(1).latin1(), "r" ); + 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 ( buff[i] == '\n' || buff[i] == '\t' ) + buff[i] = ' '; + } + buff[read_in] = '\0'; + after += buff; + } + (*it).replace( i, callToSystem.matchedLength(), after ); + i += after.length(); + } + } + stillProcess = callToInclude.search(t) != -1; + } + return tagMap; +} diff --git a/tools/linguist/shared/proparser.h b/tools/linguist/shared/proparser.h new file mode 100644 index 000000000..8899443be --- /dev/null +++ b/tools/linguist/shared/proparser.h @@ -0,0 +1,42 @@ +/********************************************************************** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of TQt Linguist. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** Licensees holding valid TQt Commercial licenses may use this file in +** accordance with the TQt Commercial License Agreement provided with +** the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef PROPARSER_H +#define PROPARSER_H + +#include <qmap.h> +#include <qstring.h> + +TQMap<TQString, TQString> proFileTagMap( const TQString& text ); + +#endif |