diff options
author | axis <qt-info@nokia.com> | 2009-04-24 11:34:15 (GMT) |
---|---|---|
committer | axis <qt-info@nokia.com> | 2009-04-24 11:34:15 (GMT) |
commit | 8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (patch) | |
tree | a17e1a767a89542ab59907462206d7dcf2e504b2 /util/install/archive | |
download | Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.zip Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.tar.gz Qt-8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76.tar.bz2 |
Long live Qt for S60!
Diffstat (limited to 'util/install/archive')
-rw-r--r-- | util/install/archive/archive.pro | 9 | ||||
-rw-r--r-- | util/install/archive/qarchive.cpp | 471 | ||||
-rw-r--r-- | util/install/archive/qarchive.h | 138 |
3 files changed, 618 insertions, 0 deletions
diff --git a/util/install/archive/archive.pro b/util/install/archive/archive.pro new file mode 100644 index 0000000..e313a4a --- /dev/null +++ b/util/install/archive/archive.pro @@ -0,0 +1,9 @@ +TEMPLATE = lib +CONFIG += staticlib +CONFIG += qt x11 +CONFIG -= dll +TARGET = arq + +SOURCES += qarchive.cpp ../keygen/keyinfo.cpp +HEADERS += qarchive.h +!zlib:unix:LIBS += -lz diff --git a/util/install/archive/qarchive.cpp b/util/install/archive/qarchive.cpp new file mode 100644 index 0000000..dd51c65 --- /dev/null +++ b/util/install/archive/qarchive.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the utils 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 "qarchive.h" +#include <qdatastream.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qapplication.h> +#include "../../../src/3rdparty/zlib/zlib.h" +#include "../keygen/keyinfo.h" +#ifdef Q_OS_UNIX +# include <sys/stat.h> +# include <unistd.h> +# include <sys/types.h> +# include <utime.h> +#endif + +enum ChunkType { + ChunkDirectory = 0, + ChunkFile = 1, + ChunkSymlink = 2, + ChunkBeginHeader = 3, + ChunkEndHeader = 4 +}; + +static bool createDir( const QString& fullPath ) +{ + QStringList hierarchy = QStringList::split( QDir::separator(), fullPath ); + QString pathComponent, tmpPath; + QDir dirTmp; +#ifdef Q_OS_UNIX + dirTmp = "/"; +#endif + + for( QStringList::Iterator it = hierarchy.begin(); it != hierarchy.end(); ++it ) { + pathComponent = *it + QDir::separator(); + tmpPath += pathComponent; + if (!dirTmp.exists(tmpPath) && !dirTmp.mkdir(tmpPath)) + return false; + } + return true; +} + +QArchive::QArchive( const QString& archivePath ) +{ + setPath( archivePath ); + + bufferSize = 512 * 1024; +} + +QArchive::~QArchive() +{ +} + +void QArchive::setPath( const QString& archivePath ) +{ + QString fullName = archivePath; + if( fullName.right( 4 ) != ".arq" ) + fullName += ".arq"; + arcFile.setName( fullName ); +} + +bool QArchive::open( int mode ) +{ + switch( mode ) { + case IO_ReadOnly: + // Fallthrough intentional + case IO_WriteOnly: + if( arcFile.open( mode ) ) + return true; + break; + } + return false; +} + +void QArchive::close() +{ + if( arcFile.isOpen() ) + arcFile.close(); +} + +bool QArchive::writeFile( const QString& fileName, const QString& localPath ) +{ + if( arcFile.isOpen() ) { + QDataStream outStream( &arcFile ); + QFileInfo fi( fileName ); + + QFile inFile( fi.absFilePath() ); + QByteArray inBuffer; + QByteArray outBuffer( bufferSize ); + z_stream ztream; + bool continueCompressing; + + if(symbolicLinks() && fi.isSymLink()) { + outStream << (int)ChunkSymlink; + outStream << fi.fileName().latin1(); + outStream << fi.readLink().latin1(); + } else if( inFile.open( IO_ReadOnly ) ) { + if( inBuffer.resize( fi.size() ) ) { + outStream << (int)ChunkFile; + outStream << fi.fileName().latin1(); + outStream << fi.lastModified(); + { + int perm = -1; +#ifdef Q_OS_UNIX + struct stat st; + if(!::stat(fi.filePath().latin1(), &st)) + perm = (int)st.st_mode; +#endif + outStream << perm; + } + if( verbosityMode & Source ) + emit operationFeedback( "Deflating " + fi.absFilePath() + "..." ); + else if( verbosityMode & Destination ) + emit operationFeedback( "Deflating " + localPath + "/" + fi.fileName() + "..." ); + ztream.next_in = (unsigned char*)inBuffer.data(); + ztream.avail_in = inBuffer.size(); + ztream.total_in = 0; + ztream.next_out = (unsigned char*)outBuffer.data(); + ztream.avail_out = outBuffer.size(); + ztream.total_out = 0; + ztream.msg = NULL; + ztream.zalloc = (alloc_func)NULL; + ztream.zfree = (free_func)NULL; + ztream.opaque = (voidpf)NULL; + ztream.data_type = Z_BINARY; + deflateInit( &ztream, 9 ); + if ( inBuffer.data() ) + inFile.readBlock( inBuffer.data(), inBuffer.size() ); + + continueCompressing = true; + while( continueCompressing ) { + if(qApp) + qApp->processEvents(); + continueCompressing = ( deflate( &ztream, Z_FINISH ) == Z_OK ); + if( !ztream.avail_out ) { + if( !outBuffer.resize( outBuffer.size() + bufferSize ) ) + qFatal( "Could not allocate compression buffer!" ); + ztream.next_out = (unsigned char*)&outBuffer.data()[ ztream.total_out ]; + ztream.avail_out = bufferSize; + } + } + + emit operationFeedback( QString( "done. %1 => %2 (%3%)\n" ) + .arg( ztream.total_in ) + .arg( ztream.total_out ) + .arg( int( + double( ztream.total_out ) / double( ztream.total_in ) * 100 ) ) ); + deflateEnd( &ztream ); + // Now write the compressed data to the output + outStream << ztream.total_out; + outStream.writeRawBytes( outBuffer.data(), ztream.total_out ); + } + inFile.close(); + return true; + } else { + return false; + } + } + return false; +} + +bool QArchive::setDirectory( const QString& dirName ) +{ + if( arcFile.isOpen() ) { + QString fullName = dirName; + QDataStream outStream( &arcFile ); + if( fullName.right( 1 ) != "/" ) + fullName += "/"; + outStream << (int)ChunkDirectory; + outStream << fullName.latin1(); + return true; + } + return false; +} + +bool QArchive::writeHeader( const QArchiveHeader header ) +{ + if( arcFile.isOpen() ) { + QDataStream outStream( &arcFile ); + outStream << (int)ChunkBeginHeader; + outStream << header.mayorVersion(); + outStream << header.minorVersion(); + outStream << header.features(); + outStream << header.description(); + outStream << header.extraData; + outStream << (int)ChunkEndHeader; + return true; + } + return false; +} + +bool QArchive::writeDir( const QString &dirName1, bool includeLastComponent, const QString &localPath1 ) +{ + if( arcFile.isOpen() ) { + QString localPath = localPath1, dirName = dirName1; + if(localPath.right(1) == "/") + localPath.truncate(localPath.length()-1); + if(dirName.right(1) == "/") + dirName.truncate(dirName.length()-1); + + QFileInfo fi( dirName ); + + if( includeLastComponent ) + setDirectory( fi.fileName() ); + QDir dir( dirName ); + const QFileInfoList* dirEntries = dir.entryInfoList(); + QFileInfoListIterator dirIter( *dirEntries ); + QDataStream outStream( &arcFile ); + QFileInfo* pFi; + + dirIter.toLast(); + while( ( pFi = dirIter.current() ) ) { + if( pFi->fileName() != "." && pFi->fileName() != ".." ) { + if( pFi->isDir() ) + writeDir( pFi->absFilePath(), true, localPath + "/" + + pFi->fileName() ); // Subdirs should always get its name in the archive. + else + writeFile( pFi->absFilePath(), localPath ); + } + --dirIter; + } + setDirectory( ".." ); + return true; + } + return false; +} + +bool QArchive::writeFileList( const QStringList fileList ) +{ + for( QStringList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it ) { + if( !writeFile( (*it) ) ) + return false; + } + return true; +} + +bool QArchive::writeDirList( const QStringList dirList, bool includeLastComponent ) +{ + for( QStringList::ConstIterator it = dirList.begin(); it != dirList.end(); ++it ) { + QString lastComponent = (*it).mid( (*it).findRev( "/" ) + 1 ); + if( !writeDir( (*it), includeLastComponent, lastComponent ) ) + return false; + } + return true; +} + +void QArchive::setVerbosity( int verbosity ) +{ + verbosityMode = verbosity; +} + +QArchiveHeader* QArchive::readArchiveHeader() +{ + QDataStream inStream( &arcFile ); + return readArchiveHeader( &inStream ); +} + +/* + Reads the archive header and returns it on success. If an error occurs, it + returns 0. The caller has to delete the object. +*/ +QArchiveHeader* QArchive::readArchiveHeader( QDataStream *inStream ) +{ + int chunktype; + QArchiveHeader *header = new QArchiveHeader; + + *inStream >> chunktype; + if( chunktype == ChunkBeginHeader ) { + *inStream >> header->_mayorVersion; + *inStream >> header->_minorVersion; + if ( header->mayorVersion()!=1 || header->minorVersion()!=0 ) { + emit operationFeedback( "Incompatible package version" ); + delete header; + return 0; + } + *inStream >> header->_features; + *inStream >> header->_description; + *inStream >> header->extraData; + *inStream >> chunktype; + if ( chunktype != ChunkEndHeader ) { + emit operationFeedback( "Invalid package header" ); + delete header; + return 0; + } + } else { + emit operationFeedback( "No package header found." ); + delete header; + return 0; + } + return header; +} + +bool QArchive::readArchive( const QString &outpath, const QString &key ) +{ + QDataStream inStream( &arcFile ); + return readArchive( &inStream, outpath, key ); +} + +bool QArchive::readArchive( QDataStream *inStream, const QString &outpath, const QString &key ) +{ + QDataStream outStream; + QFile outFile; + QDir outDir; + QByteArray inBuffer; + QByteArray outBuffer( bufferSize ); + z_stream ztream; + bool continueDeCompressing; + QString entryName, dirName, symName; + int entryLength, chunktype; + + //get the key + QArchiveHeader *header = readArchiveHeader( inStream ); + if ( header == 0 ) + return false; + uint infeatures = featuresForKey( key ); + if( (header->features() & infeatures) != header->features()) { + emit operationFeedback( "Invalid key" ); + return false; + } + + // Set up the initial directory. + // If the dir does not exist, try to create it + dirName = QDir::toNativeSeparators( outpath ); + outDir.setPath( dirName ); + if( !outDir.exists( dirName ) && !createDir( dirName ) ) + return false; + outDir.cd( dirName ); + + while( !inStream->atEnd() ) { + //get our type + *inStream >> chunktype; + if(chunktype == ChunkDirectory) { + *inStream >> entryLength; + inBuffer.resize( entryLength ); + inStream->readRawBytes( inBuffer.data(), entryLength ); + entryName = inBuffer.data(); + + if( verbosityMode & Source ) + emit operationFeedback( "Directory " + entryName + "... " ); + if( entryName == "../" ) { + outDir.cdUp(); + } else { + dirName = QDir::toNativeSeparators( outDir.absPath() + + QString( "/" ) + entryName.left( entryName.length() - 1 ) ); + if( verbosityMode & Destination ) + emit operationFeedback( "Directory " + dirName + "... " ); + + if( !outDir.exists( dirName ) && !createDir( dirName ) ) { + emit operationFeedback( "Cannot create directory: " + dirName ); + return false; + } + outDir.cd( dirName ); + } + } else if(chunktype == ChunkFile) { + *inStream >> entryLength; + inBuffer.resize( entryLength ); + inStream->readRawBytes( inBuffer.data(), entryLength ); + entryName = inBuffer.data(); + + int filePerm; + QDateTime timeStamp; + QString fileName = QDir::toNativeSeparators( outDir.absPath() + QString( "/" ) + entryName ); + outFile.setName( fileName ); + if( outFile.open( IO_WriteOnly ) ) { + *inStream >> timeStamp; // Get timestamp from the archive + *inStream >> filePerm; + outStream.setDevice( &outFile ); + *inStream >> entryLength; + if( verbosityMode & Source ) + emit operationFeedback( "Expanding " + entryName + "..." ); + else if( verbosityMode & Destination ) + emit operationFeedback( "Expanding " + fileName + "..." ); + inBuffer.resize( entryLength ); + inStream->readRawBytes( inBuffer.data(), entryLength ); + ztream.next_in = (unsigned char*)inBuffer.data(); + ztream.avail_in = entryLength; + ztream.total_in = 0; + ztream.msg = NULL; + ztream.zalloc = (alloc_func)0; + ztream.zfree = (free_func)0; + ztream.opaque = (voidpf)0; + ztream.data_type = Z_BINARY; + inflateInit( &ztream ); + continueDeCompressing = true; + while( continueDeCompressing ) { + ztream.next_out = (unsigned char*)outBuffer.data(); + ztream.avail_out = outBuffer.size(); + ztream.total_out = 0; + continueDeCompressing = ( inflate( &ztream, Z_NO_FLUSH ) == Z_OK ); + outStream.writeRawBytes( outBuffer.data(), ztream.total_out ); + } + inflateEnd( &ztream ); + outFile.close(); +#ifdef Q_OS_UNIX + QDateTime t; t.setTime_t(0); //epoch + struct utimbuf tb; + tb.actime = tb.modtime = t.secsTo(timeStamp); + utime(fileName.local8Bit(), &tb); + if(filePerm != -1) + chmod(fileName.local8Bit(), (mode_t)filePerm); +#endif + } else { + emit operationFeedback( "Cannot open: " + fileName ); + return false; + } + } else if(chunktype == ChunkSymlink) { + *inStream >> entryLength; + inBuffer.resize( entryLength ); + inStream->readRawBytes( inBuffer.data(), entryLength ); + entryName = inBuffer.data(); + QString fileName = QDir::toNativeSeparators( outDir.absPath() + QString( "/" ) + entryName ); + + *inStream >> entryLength; + inBuffer.resize( entryLength ); + inStream->readRawBytes( inBuffer.data(), entryLength ); + symName = inBuffer.data(); + if( verbosityMode & Source ) + emit operationFeedback( "Linking " + symName + "... " ); + else if( verbosityMode & Destination ) + emit operationFeedback( "Linking " + fileName + "... " ); +#ifdef Q_OS_UNIX + symlink( symName.local8Bit(), fileName.local8Bit() ); +#endif + } else { + if( verbosityMode & Source ) + emit operationFeedback( QString("Unknown chunk: %d") .arg(chunktype) ); + } + if( verbosityMode & Progress ) + emit operationFeedback( inStream->device()->at() ); + } + return true; +} + diff --git a/util/install/archive/qarchive.h b/util/install/archive/qarchive.h new file mode 100644 index 0000000..351a48b --- /dev/null +++ b/util/install/archive/qarchive.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the utils 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 QARCHIVE_H +#define QARCHIVE_H + +#include <qstring.h> +#include <qfile.h> +#include <qstringlist.h> +#include <qobject.h> + +class QArchiveHeader +{ +public: + QArchiveHeader( uint feat, const QString& desc=QString(), uchar mayorVer = 1, uchar minorVer = 0 ) + : _features(feat), _description(desc), _mayorVersion(mayorVer), _minorVersion(minorVer) + {} + + uint features() const + { return _features; } + + QString description() const + { return _description; } + + uchar mayorVersion() const + { return _mayorVersion; } + + uchar minorVersion() const + { return _minorVersion; } + + void addExtraData( const QString& key, const QString& value ) + { extraData.insert( key, value ); } + + QString findExtraData( const QString& key ) const + { return extraData[key]; } + +private: + QArchiveHeader() + {} + + uint _features; + QString _description; + uchar _mayorVersion; + uchar _minorVersion; + QMap<QString,QString> extraData; + + friend class QArchive; +}; + +class QArchive : public QObject +{ + Q_OBJECT +public: + QArchive( const QString& archivePath = QString() ); + ~QArchive(); + + void setPath( const QString& archivePath ); + void setVerbosity( int verbosity ); + + bool symbolicLinks() const { return doSyms; } + void setSymbolicLinks(bool b) { doSyms = b; } + + bool open( int mode ); + void close(); + bool isOpen() { return arcFile.isOpen(); } + + bool writeHeader( const QArchiveHeader header ); + bool writeFile( const QString& fileName, const QString& localPath = QString() ); + bool writeFileList( const QStringList fileList ); + bool writeDir( const QString& dirName, bool includeLastComponent = false, + const QString& localPath = QString() ); + bool writeDirList( const QStringList dirList, bool includeLastComponent = true ); + + QArchiveHeader* readArchiveHeader(); + QArchiveHeader* readArchiveHeader( QDataStream *inStream ); + + bool readArchive( const QString &outpath, const QString &key = QString() ); + bool readArchive( QDataStream *inStream, const QString &outpath, const QString &key = QString() ); +private: + QFile arcFile; + + int bufferSize; + int verbosityMode; + uint doSyms : 1; + bool setDirectory( const QString& dirName ); +signals: + void operationFeedback( const QString& ); + void operationFeedback( int ); + +public: + enum { + NoFeedback = 0x00, + OnlyNames = 0x01, + Verbose = 0x02, + Source = 0x10, + Destination = 0x20, + Progress = 0x40 + }; +}; + +#endif |