mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
kutils: new karchive library to replace KZip and KTar
it has to solve a few problems like: 1. blocking the current thread while listing, extracting or adding to archive 2. lack of support for some archive formats such as 7-zip in the previously available classes for dealing with archive 3. progress and error reporting a KArchiveJob class may have to be implemented to solve the above issues. the API is just as frustrating to use as KTar and KZip right now tho and I am not happy with that but the class itself is a 3-rd rewrite of what was once Python module, plugin for another project and now used as base in library. Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
c27d7e7ee6
commit
3ab3f3b47f
48 changed files with 1557 additions and 5929 deletions
|
@ -31,7 +31,7 @@
|
|||
# git has its own built in compression methods
|
||||
|
||||
# You can enable these (for most people's uses),
|
||||
# but keep in mind test file for KTar/KZip would
|
||||
# but keep in mind test file for KArchive would
|
||||
# obviously need these.
|
||||
#*.7z
|
||||
#*.dmg
|
||||
|
|
|
@ -270,7 +270,14 @@ set_package_properties(Speechd PROPERTIES
|
|||
DESCRIPTION "Speech Dispatcher provides a high-level device independent layer for speech synthesis"
|
||||
URL "https://freebsoft.org/speechd"
|
||||
TYPE RECOMMENDED
|
||||
PURPOSE "Text To Speech"
|
||||
PURPOSE "Text-To-Speech"
|
||||
)
|
||||
|
||||
kde4_optional_find_package(LibArchive 3.0.3)
|
||||
set_package_properties(LibArchive PROPERTIES
|
||||
DESCRIPTION "Multi-format archive and compression library"
|
||||
URL "https://libarchive.org/"
|
||||
PURPOSE "Archives in TAR, ZIP and other formats"
|
||||
)
|
||||
|
||||
################# configure checks and create the configured files #################
|
||||
|
|
|
@ -42,6 +42,7 @@ kde4_bool_to_01(X11_XSync_FOUND HAVE_XSYNC) # kidletim
|
|||
# check is to be added to get the proper set of headers.
|
||||
check_symbol_exists(strtoll "stdlib.h" HAVE_STRTOLL) # kioslave
|
||||
check_symbol_exists(getgrouplist "unistd.h;grp.h" HAVE_GETGROUPLIST) # kio
|
||||
check_symbol_exists(strmode "string.h" HAVE_STRMODE) # karchive
|
||||
|
||||
check_function_exists(backtrace HAVE_BACKTRACE) # kdecore, kio
|
||||
check_function_exists(fdatasync HAVE_FDATASYNC) # kdecore
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
# KDE4_KDNSSD_LIBS - the kdnssd library and all depending libraries
|
||||
# KDE4_KHTTP_LIBS - the khttp library and all depending libraries
|
||||
# KDE4_KSPEECH_LIBS - the kspeech library and all depending libraries
|
||||
# KDE4_KARCHIVE_LIBS - the kspeech library and all depending libraries
|
||||
#
|
||||
# The variable INSTALL_TARGETS_DEFAULT_ARGS can be used when installing libraries
|
||||
# or executables into the default locations.
|
||||
|
@ -243,6 +244,7 @@ set(_kde_libraries
|
|||
kdnssd
|
||||
khttp
|
||||
kspeech
|
||||
karchive
|
||||
kfile
|
||||
kidletime
|
||||
kio
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#cmakedefine HAVE_SETMNTENT 1
|
||||
#cmakedefine HAVE_STRTOLL 1
|
||||
#cmakedefine HAVE_GETGROUPLIST 1
|
||||
#cmakedefine HAVE_STRMODE 1
|
||||
|
||||
/* Define to 1 if you have the Xtest extension */
|
||||
#cmakedefine HAVE_XTEST 1
|
||||
|
|
|
@ -11,11 +11,8 @@ install(
|
|||
KActionMenu
|
||||
KActionSelector
|
||||
KApplication
|
||||
KAr
|
||||
KArchive
|
||||
KArchiveDirectory
|
||||
KArchiveEntry
|
||||
KArchiveFile
|
||||
KAssistantDialog
|
||||
KAuthorization
|
||||
KAutoMount
|
||||
|
@ -292,7 +289,6 @@ install(
|
|||
KSystemTrayIcon
|
||||
KTabBar
|
||||
KTabWidget
|
||||
KTar
|
||||
KTempDir
|
||||
KTemporaryFile
|
||||
KTextEdit
|
||||
|
@ -342,8 +338,6 @@ install(
|
|||
KXmlGuiWindow
|
||||
KXMessages
|
||||
KXYSelector
|
||||
KZip
|
||||
KZipFileEntry
|
||||
NET
|
||||
NETRootInfo
|
||||
NETWinInfo
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#include "../kar.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../karchive.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../karchive.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../ktar.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../kzip.h"
|
|
@ -1 +0,0 @@
|
|||
#include "../kzip.h"
|
|
@ -122,21 +122,16 @@ set(kdecore_LIB_SRCS
|
|||
date/klocalizeddate.cpp
|
||||
date/ktimezone.cpp
|
||||
date/ksystemtimezone.cpp
|
||||
io/kar.cpp
|
||||
io/karchive.cpp
|
||||
io/kdebug.cpp
|
||||
io/kdirwatch.cpp
|
||||
io/kfilesystemtype_p.cpp
|
||||
io/klimitediodevice.cpp
|
||||
io/kmessage.cpp
|
||||
io/kmountpoint.cpp
|
||||
io/kprocess.cpp
|
||||
io/ksavefile.cpp
|
||||
io/ktar.cpp
|
||||
io/ktempdir.cpp
|
||||
io/ktemporaryfile.cpp
|
||||
io/kurl.cpp
|
||||
io/kzip.cpp
|
||||
jobs/kcompositejob.cpp
|
||||
jobs/kjob.cpp
|
||||
jobs/kjobuidelegate.cpp
|
||||
|
@ -295,8 +290,6 @@ install(
|
|||
date/klocalizeddate.h
|
||||
date/ksystemtimezone.h
|
||||
date/ktimezone.h
|
||||
io/kar.h
|
||||
io/karchive.h
|
||||
io/kdebug.h
|
||||
io/kdirwatch.h
|
||||
io/kmessage.h
|
||||
|
@ -304,11 +297,9 @@ install(
|
|||
io/klockfile.h
|
||||
io/kmountpoint.h
|
||||
io/ksavefile.h
|
||||
io/ktar.h
|
||||
io/ktempdir.h
|
||||
io/ktemporaryfile.h
|
||||
io/kurl.h
|
||||
io/kzip.h
|
||||
jobs/kcompositejob.h
|
||||
jobs/kjob.h
|
||||
jobs/kjobuidelegate.h
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
|
||||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kar.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QDir>
|
||||
#include <time.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <kdebug.h>
|
||||
#include <kmimetype.h>
|
||||
#include <QtCore/QRegExp>
|
||||
|
||||
#include "kfilterdev.h"
|
||||
//#include "klimitediodevice_p.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////// KAr ///////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class KAr::KArPrivate
|
||||
{
|
||||
public:
|
||||
KArPrivate() {}
|
||||
};
|
||||
|
||||
KAr::KAr( const QString& filename )
|
||||
: KArchive( filename ), d(new KArPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KAr::KAr( QIODevice * dev )
|
||||
: KArchive( dev ), d(new KArPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KAr::~KAr()
|
||||
{
|
||||
if( isOpen() )
|
||||
close();
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KAr::doPrepareWriting( const QString&, const QString&, const QString&,
|
||||
qint64, mode_t, time_t, time_t, time_t )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doFinishWriting( qint64 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doWriteDir( const QString&, const QString&, const QString&,
|
||||
mode_t, time_t, time_t, time_t )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doWriteSymLink( const QString&, const QString&, const QString&,
|
||||
const QString&, mode_t, time_t, time_t, time_t )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::openArchive( QIODevice::OpenMode mode )
|
||||
{
|
||||
// Open archive
|
||||
|
||||
if ( mode == QIODevice::WriteOnly )
|
||||
return true;
|
||||
if ( mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite )
|
||||
{
|
||||
kWarning(7042) << "Unsupported mode " << mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
QIODevice* dev = device();
|
||||
if ( !dev )
|
||||
return false;
|
||||
|
||||
QByteArray magic = dev->read( 7 );
|
||||
if ( magic != "!<arch>" ) {
|
||||
kWarning(7042) << "Invalid main magic";
|
||||
return false;
|
||||
}
|
||||
|
||||
char *ar_longnames = 0;
|
||||
while (! dev->atEnd()) {
|
||||
QByteArray ar_header;
|
||||
ar_header.resize(61);
|
||||
QByteArray name;
|
||||
int date, uid, gid, mode;
|
||||
qint64 size;
|
||||
QString suid, sgid;
|
||||
|
||||
dev->seek( dev->pos() + (2 - (dev->pos() % 2)) % 2 ); // Ar headers are padded to byte boundary
|
||||
|
||||
if ( dev->read(ar_header.data(), 60) != 60 ) { // Read ar header
|
||||
kWarning(7042) << "Couldn't read header";
|
||||
delete[] ar_longnames;
|
||||
//return false;
|
||||
return true; // Probably EOF / trailing junk
|
||||
}
|
||||
|
||||
if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings
|
||||
kWarning(7042) << "Invalid magic";
|
||||
delete[] ar_longnames;
|
||||
return false;
|
||||
}
|
||||
|
||||
name = ar_header.mid( 0, 16 ); // Process header
|
||||
date = ar_header.mid( 16, 12 ).toInt();
|
||||
uid = ar_header.mid( 28, 6 ).toInt();
|
||||
gid = ar_header.mid( 34, 6 ).toInt();
|
||||
mode = ar_header.mid( 40, 8 ).toInt();
|
||||
size = ar_header.mid( 48, 10 ).toInt();
|
||||
|
||||
suid = QLatin1String(getpwuid(uid)->pw_name);
|
||||
sgid = QLatin1String(getgrgid(gid)->gr_name);
|
||||
|
||||
bool skip_entry = false; // Deal with special entries
|
||||
if (name.mid(0, 1) == "/") {
|
||||
if (name.mid(1, 1) == "/") { // Longfilename table entry
|
||||
delete[] ar_longnames;
|
||||
ar_longnames = new char[size + 1];
|
||||
ar_longnames[size] = '\0';
|
||||
dev->read(ar_longnames, size);
|
||||
skip_entry = true;
|
||||
kDebug(7042) << "Read in longnames entry";
|
||||
} else if (name.mid(1, 1) == " ") { // Symbol table entry
|
||||
kDebug(7042) << "Skipped symbol entry";
|
||||
dev->seek( dev->pos() + size );
|
||||
skip_entry = true;
|
||||
} else { // Longfilename
|
||||
kDebug(7042) << "Longfilename #" << name.mid(1, 15).toInt();
|
||||
if (! ar_longnames) {
|
||||
kWarning(7042) << "Invalid longfilename reference";
|
||||
delete[] ar_longnames;
|
||||
return false;
|
||||
}
|
||||
name = &ar_longnames[name.mid(1, 15).toInt()];
|
||||
name = name.left(name.indexOf("/"));
|
||||
}
|
||||
}
|
||||
if (skip_entry) continue;
|
||||
|
||||
name = name.trimmed(); // Process filename
|
||||
name.replace( '/', QByteArray() );
|
||||
kDebug(7042) << "Filename: " << name << " Size: " << size;
|
||||
|
||||
KArchiveEntry* entry = new KArchiveFile(this, QString::fromLocal8Bit(name), mode, date,
|
||||
suid, sgid, /*symlink*/ QString(),
|
||||
dev->pos(), size);
|
||||
rootDir()->addEntry(entry); // Ar files don't support directories, so everything in root
|
||||
|
||||
dev->seek( dev->pos() + size ); // Skip contents
|
||||
}
|
||||
delete[] ar_longnames;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KAr::closeArchive()
|
||||
{
|
||||
// Close the archive
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifndef KAR_H
|
||||
#define KAR_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* KAr is a class for reading archives in ar format. Writing
|
||||
* is not supported.
|
||||
* @short A class for reading ar archives.
|
||||
* @author Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
*/
|
||||
class KDECORE_EXPORT KAr : public KArchive
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/holger/myfile.ar")
|
||||
*/
|
||||
KAr( const QString& filename );
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KFilterDev) or not (QFile, etc.).
|
||||
* @param dev the device to read from
|
||||
*/
|
||||
KAr( QIODevice * dev );
|
||||
|
||||
/**
|
||||
* If the ar file is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
virtual ~KAr();
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
* Writing not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
virtual bool doPrepareWriting( const QString& name, const QString& user, const QString& group, qint64 size,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime );
|
||||
|
||||
/*
|
||||
* Writing not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
virtual bool doFinishWriting( qint64 size );
|
||||
|
||||
/*
|
||||
* Writing not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
virtual bool doWriteDir( const QString& name, const QString& user, const QString& group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime );
|
||||
|
||||
virtual bool doWriteSymLink( const QString &name, const QString &target,
|
||||
const QString &user, const QString &group, mode_t perm, time_t atime, time_t mtime, time_t ctime );
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
*
|
||||
*/
|
||||
virtual bool openArchive( QIODevice::OpenMode mode );
|
||||
virtual bool closeArchive();
|
||||
|
||||
private:
|
||||
class KArPrivate;
|
||||
KArPrivate* const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,849 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2000-2005 David Faure <faure@kde.org>
|
||||
Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.cpp by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "karchive.h"
|
||||
#include "klimitediodevice_p.h"
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <kdebug.h>
|
||||
#include <ksavefile.h>
|
||||
#include <kde_file.h>
|
||||
|
||||
#include <QStack>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h> // PATH_MAX
|
||||
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX _POSIX_PATH_MAX
|
||||
#endif
|
||||
|
||||
class KArchivePrivate
|
||||
{
|
||||
public:
|
||||
KArchivePrivate()
|
||||
: rootDir( 0 ),
|
||||
saveFile( 0 ),
|
||||
dev ( 0 ),
|
||||
fileName(),
|
||||
mode( QIODevice::NotOpen ),
|
||||
deviceOwned( false )
|
||||
{}
|
||||
~KArchivePrivate()
|
||||
{
|
||||
delete saveFile;
|
||||
delete rootDir;
|
||||
}
|
||||
void abortWriting();
|
||||
|
||||
KArchiveDirectory* rootDir;
|
||||
KSaveFile* saveFile;
|
||||
QIODevice * dev;
|
||||
QString fileName;
|
||||
QIODevice::OpenMode mode;
|
||||
bool deviceOwned; // if true, we (KArchive) own dev and must delete it
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////// KArchive ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
KArchive::KArchive( const QString& fileName )
|
||||
: d(new KArchivePrivate)
|
||||
{
|
||||
Q_ASSERT( !fileName.isEmpty() );
|
||||
d->fileName = fileName;
|
||||
// This constructor leaves the device set to 0.
|
||||
// This is for the use of KSaveFile, see open().
|
||||
}
|
||||
|
||||
KArchive::KArchive( QIODevice * dev )
|
||||
: d(new KArchivePrivate)
|
||||
{
|
||||
d->dev = dev;
|
||||
}
|
||||
|
||||
KArchive::~KArchive()
|
||||
{
|
||||
if ( isOpen() )
|
||||
close(); // WARNING: won't call the virtual method close in the derived class!!!
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KArchive::open( QIODevice::OpenMode mode )
|
||||
{
|
||||
Q_ASSERT( mode != QIODevice::NotOpen );
|
||||
|
||||
if ( isOpen() )
|
||||
close();
|
||||
|
||||
if ( !d->fileName.isEmpty() )
|
||||
{
|
||||
Q_ASSERT( !d->dev );
|
||||
if ( !createDevice( mode ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT( d->dev );
|
||||
|
||||
if ( !d->dev->isOpen() && !d->dev->open( mode ) )
|
||||
return false;
|
||||
|
||||
d->mode = mode;
|
||||
|
||||
Q_ASSERT( !d->rootDir );
|
||||
d->rootDir = 0;
|
||||
|
||||
return openArchive( mode );
|
||||
}
|
||||
|
||||
bool KArchive::createDevice( QIODevice::OpenMode mode )
|
||||
{
|
||||
switch( mode ) {
|
||||
case QIODevice::WriteOnly:
|
||||
if ( !d->fileName.isEmpty() ) {
|
||||
// The use of KSaveFile can't be done in the ctor (no mode known yet)
|
||||
//kDebug() << "Writing to a file using KSaveFile";
|
||||
d->saveFile = new KSaveFile( d->fileName );
|
||||
if ( !d->saveFile->open() ) {
|
||||
kWarning() << "KSaveFile creation for " << d->fileName << " failed, " << d->saveFile->errorString();
|
||||
delete d->saveFile;
|
||||
d->saveFile = 0;
|
||||
return false;
|
||||
}
|
||||
d->dev = d->saveFile;
|
||||
Q_ASSERT( d->dev );
|
||||
}
|
||||
break;
|
||||
case QIODevice::ReadOnly:
|
||||
case QIODevice::ReadWrite:
|
||||
// ReadWrite mode still uses QFile for now; we'd need to copy to the tempfile, in fact.
|
||||
if ( !d->fileName.isEmpty() ) {
|
||||
d->dev = new QFile( d->fileName );
|
||||
d->deviceOwned = true;
|
||||
}
|
||||
break; // continued below
|
||||
default:
|
||||
kWarning() << "Unsupported mode " << d->mode;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KArchive::close()
|
||||
{
|
||||
if ( !isOpen() )
|
||||
return false; // already closed (return false or true? arguable...)
|
||||
|
||||
// moved by holger to allow kzip to write the zip central dir
|
||||
// to the file in closeArchive()
|
||||
// DF: added d->dev so that we skip closeArchive if saving aborted.
|
||||
bool closeSucceeded = true;
|
||||
if ( d->dev ) {
|
||||
closeSucceeded = closeArchive();
|
||||
if ( d->mode == QIODevice::WriteOnly && !closeSucceeded )
|
||||
d->abortWriting();
|
||||
}
|
||||
|
||||
if ( d->dev )
|
||||
d->dev->close();
|
||||
|
||||
// if d->saveFile is not null then it is equal to d->dev.
|
||||
if ( d->saveFile ) {
|
||||
closeSucceeded = d->saveFile->finalize();
|
||||
delete d->saveFile;
|
||||
d->saveFile = 0;
|
||||
} if ( d->deviceOwned ) {
|
||||
delete d->dev; // we created it ourselves in open()
|
||||
}
|
||||
|
||||
delete d->rootDir;
|
||||
d->rootDir = 0;
|
||||
d->mode = QIODevice::NotOpen;
|
||||
d->dev = 0;
|
||||
return closeSucceeded;
|
||||
}
|
||||
|
||||
const KArchiveDirectory* KArchive::directory() const
|
||||
{
|
||||
// rootDir isn't const so that parsing-on-demand is possible
|
||||
return const_cast<KArchive *>(this)->rootDir();
|
||||
}
|
||||
|
||||
|
||||
bool KArchive::addLocalFile( const QString& fileName, const QString& destName )
|
||||
{
|
||||
QFileInfo fileInfo( fileName );
|
||||
if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
|
||||
{
|
||||
kWarning() << fileName << "doesn't exist or is not a regular file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
KDE_struct_stat fi;
|
||||
if (KDE::lstat(fileName,&fi) == -1) {
|
||||
kWarning() << "stat'ing" << fileName
|
||||
<< "failed:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileInfo.isSymLink()) {
|
||||
QString symLinkTarget;
|
||||
// Do NOT use fileInfo.readLink() for unix symlinks!
|
||||
// It returns the -full- path to the target, while we want the target string "as is".
|
||||
const QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
QByteArray s(PATH_MAX, Qt::Uninitialized);
|
||||
int len = readlink(encodedFileName.data(), s.data(), s.size() - 1);
|
||||
if ( len >= 0 ) {
|
||||
s[len] = '\0';
|
||||
symLinkTarget = QFile::decodeName(s);
|
||||
}
|
||||
if (symLinkTarget.isEmpty())
|
||||
symLinkTarget = fileInfo.readLink();
|
||||
return writeSymLink(destName, symLinkTarget, fileInfo.owner(),
|
||||
fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
|
||||
fi.st_ctime);
|
||||
}/*end if*/
|
||||
|
||||
qint64 size = fileInfo.size();
|
||||
|
||||
// the file must be opened before prepareWriting is called, otherwise
|
||||
// if the opening fails, no content will follow the already written
|
||||
// header and the tar file is effectively f*cked up
|
||||
QFile file( fileName );
|
||||
if ( !file.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
kWarning() << "couldn't open file " << fileName;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
|
||||
fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
|
||||
{
|
||||
kWarning() << " prepareWriting" << destName << "failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and write data in chunks to minimize memory usage
|
||||
QByteArray array;
|
||||
array.resize( int( qMin( qint64( 1024 * 1024 ), size ) ) );
|
||||
qint64 n;
|
||||
qint64 total = 0;
|
||||
while ( ( n = file.read( array.data(), array.size() ) ) > 0 )
|
||||
{
|
||||
if ( !writeData( array.data(), n ) )
|
||||
{
|
||||
kWarning() << "writeData failed";
|
||||
return false;
|
||||
}
|
||||
total += n;
|
||||
}
|
||||
Q_ASSERT( total == size );
|
||||
Q_UNUSED( total );
|
||||
|
||||
if ( !finishWriting( size ) )
|
||||
{
|
||||
kWarning() << "finishWriting failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KArchive::addLocalDirectory( const QString& path, const QString& destName )
|
||||
{
|
||||
QDir dir( path );
|
||||
if ( !dir.exists() )
|
||||
return false;
|
||||
dir.setFilter(dir.filter() | QDir::Hidden);
|
||||
const QStringList files = dir.entryList();
|
||||
for ( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
|
||||
{
|
||||
if ( *it != QLatin1String(".") && *it != QLatin1String("..") )
|
||||
{
|
||||
QString fileName = path + QLatin1Char('/') + *it;
|
||||
// kDebug() << "storing " << fileName;
|
||||
QString dest = destName.isEmpty() ? *it : (destName + QLatin1Char('/') + *it);
|
||||
QFileInfo fileInfo( fileName );
|
||||
|
||||
if ( fileInfo.isFile() || fileInfo.isSymLink() )
|
||||
addLocalFile( fileName, dest );
|
||||
else if ( fileInfo.isDir() )
|
||||
addLocalDirectory( fileName, dest );
|
||||
// We omit sockets
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KArchive::writeFile( const QString& name, const QString& user,
|
||||
const QString& group, const char* data, qint64 size,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime )
|
||||
{
|
||||
if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
|
||||
{
|
||||
kWarning() << "prepareWriting failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write data
|
||||
// Note: if data is 0L, don't call write, it would terminate the KFilterDev
|
||||
if ( data && size && !writeData( data, size ) )
|
||||
{
|
||||
kWarning() << "writeData failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !finishWriting( size ) )
|
||||
{
|
||||
kWarning() << "finishWriting failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KArchive::writeData( const char* data, qint64 size )
|
||||
{
|
||||
bool ok = device()->write( data, size ) == size;
|
||||
if ( !ok )
|
||||
d->abortWriting();
|
||||
return ok;
|
||||
}
|
||||
|
||||
// The writeDir -> doWriteDir pattern allows to avoid propagating the default
|
||||
// values into all virtual methods of subclasses, and it allows more extensibility:
|
||||
// if a new argument is needed, we can add a writeDir overload which stores the
|
||||
// additional argument in the d pointer, and doWriteDir reimplementations can fetch
|
||||
// it from there.
|
||||
|
||||
bool KArchive::writeDir( const QString& name, const QString& user, const QString& group,
|
||||
mode_t perm, time_t atime,
|
||||
time_t mtime, time_t ctime )
|
||||
{
|
||||
return doWriteDir( name, user, group, perm | 040000, atime, mtime, ctime );
|
||||
}
|
||||
|
||||
bool KArchive::writeSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm, time_t atime,
|
||||
time_t mtime, time_t ctime )
|
||||
{
|
||||
return doWriteSymLink( name, target, user, group, perm, atime, mtime, ctime );
|
||||
}
|
||||
|
||||
|
||||
bool KArchive::prepareWriting( const QString& name, const QString& user,
|
||||
const QString& group, qint64 size,
|
||||
mode_t perm, time_t atime,
|
||||
time_t mtime, time_t ctime )
|
||||
{
|
||||
bool ok = doPrepareWriting( name, user, group, size, perm, atime, mtime, ctime );
|
||||
if ( !ok )
|
||||
d->abortWriting();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool KArchive::finishWriting( qint64 size )
|
||||
{
|
||||
return doFinishWriting( size );
|
||||
}
|
||||
|
||||
KArchiveDirectory * KArchive::rootDir()
|
||||
{
|
||||
if ( !d->rootDir )
|
||||
{
|
||||
//kDebug() << "Making root dir ";
|
||||
struct passwd* pw = getpwuid( getuid() );
|
||||
struct group* grp = getgrgid( getgid() );
|
||||
QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
|
||||
QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
|
||||
|
||||
d->rootDir = new KArchiveDirectory( this, QLatin1String("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString() );
|
||||
}
|
||||
return d->rootDir;
|
||||
}
|
||||
|
||||
KArchiveDirectory * KArchive::findOrCreate( const QString & path )
|
||||
{
|
||||
//kDebug() << path;
|
||||
if ( path.isEmpty() || path == QLatin1String("/") || path == QLatin1String(".") ) // root dir => found
|
||||
{
|
||||
//kDebug() << "returning rootdir";
|
||||
return rootDir();
|
||||
}
|
||||
// Important note : for tar files containing absolute paths
|
||||
// (i.e. beginning with "/"), this means the leading "/" will
|
||||
// be removed (no KDirectory for it), which is exactly the way
|
||||
// the "tar" program works (though it displays a warning about it)
|
||||
// See also KArchiveDirectory::entry().
|
||||
|
||||
// Already created ? => found
|
||||
const KArchiveEntry* ent = rootDir()->entry( path );
|
||||
if ( ent )
|
||||
{
|
||||
if ( ent->isDirectory() )
|
||||
//kDebug() << "found it";
|
||||
return (KArchiveDirectory *) ent;
|
||||
else
|
||||
kWarning() << "Found" << path << "but it's not a directory";
|
||||
}
|
||||
|
||||
// Otherwise go up and try again
|
||||
int pos = path.lastIndexOf( QLatin1Char('/') );
|
||||
KArchiveDirectory * parent;
|
||||
QString dirname;
|
||||
if ( pos == -1 ) // no more slash => create in root dir
|
||||
{
|
||||
parent = rootDir();
|
||||
dirname = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString left = path.left( pos );
|
||||
dirname = path.mid( pos + 1 );
|
||||
parent = findOrCreate( left ); // recursive call... until we find an existing dir.
|
||||
}
|
||||
|
||||
//kDebug() << "found parent " << parent->name() << " adding " << dirname << " to ensure " << path;
|
||||
// Found -> add the missing piece
|
||||
KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
|
||||
d->rootDir->date(), d->rootDir->user(),
|
||||
d->rootDir->group(), QString() );
|
||||
parent->addEntry( e );
|
||||
return e; // now a directory to <path> exists
|
||||
}
|
||||
|
||||
void KArchive::setDevice( QIODevice * dev )
|
||||
{
|
||||
if ( d->deviceOwned )
|
||||
delete d->dev;
|
||||
d->dev = dev;
|
||||
d->deviceOwned = false;
|
||||
}
|
||||
|
||||
void KArchive::setRootDir( KArchiveDirectory *rootDir )
|
||||
{
|
||||
Q_ASSERT( !d->rootDir ); // Call setRootDir only once during parsing please ;)
|
||||
d->rootDir = rootDir;
|
||||
}
|
||||
|
||||
QIODevice::OpenMode KArchive::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
QIODevice * KArchive::device() const
|
||||
{
|
||||
return d->dev;
|
||||
}
|
||||
|
||||
bool KArchive::isOpen() const
|
||||
{
|
||||
return d->mode != QIODevice::NotOpen;
|
||||
}
|
||||
|
||||
QString KArchive::fileName() const
|
||||
{
|
||||
return d->fileName;
|
||||
}
|
||||
|
||||
void KArchivePrivate::abortWriting()
|
||||
{
|
||||
if ( saveFile ) {
|
||||
saveFile->abort();
|
||||
delete saveFile;
|
||||
saveFile = 0;
|
||||
dev = 0;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////// KArchiveEntry //////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class KArchiveEntryPrivate
|
||||
{
|
||||
public:
|
||||
KArchiveEntryPrivate( KArchive* _archive, const QString& _name, int _access,
|
||||
int _date, const QString& _user, const QString& _group,
|
||||
const QString& _symlink) :
|
||||
name(_name),
|
||||
date(_date),
|
||||
access(_access),
|
||||
user(_user),
|
||||
group(_group),
|
||||
symlink(_symlink),
|
||||
archive(_archive)
|
||||
{}
|
||||
QString name;
|
||||
int date;
|
||||
mode_t access;
|
||||
QString user;
|
||||
QString group;
|
||||
QString symlink;
|
||||
KArchive* archive;
|
||||
};
|
||||
|
||||
KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group, const
|
||||
QString& symlink) :
|
||||
d(new KArchiveEntryPrivate(t,name,access,date,user,group,symlink))
|
||||
{
|
||||
}
|
||||
|
||||
KArchiveEntry::~KArchiveEntry()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QDateTime KArchiveEntry::datetime() const
|
||||
{
|
||||
QDateTime datetimeobj;
|
||||
datetimeobj.setTime_t( d->date );
|
||||
return datetimeobj;
|
||||
}
|
||||
|
||||
int KArchiveEntry::date() const
|
||||
{
|
||||
return d->date;
|
||||
}
|
||||
|
||||
QString KArchiveEntry::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
mode_t KArchiveEntry::permissions() const
|
||||
{
|
||||
return d->access;
|
||||
}
|
||||
|
||||
QString KArchiveEntry::user() const
|
||||
{
|
||||
return d->user;
|
||||
}
|
||||
|
||||
QString KArchiveEntry::group() const
|
||||
{
|
||||
return d->group;
|
||||
}
|
||||
|
||||
QString KArchiveEntry::symLinkTarget() const
|
||||
{
|
||||
return d->symlink;
|
||||
}
|
||||
|
||||
bool KArchiveEntry::isFile() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KArchiveEntry::isDirectory() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
KArchive* KArchiveEntry::archive() const
|
||||
{
|
||||
return d->archive;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////// KArchiveFile ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class KArchiveFilePrivate
|
||||
{
|
||||
public:
|
||||
KArchiveFilePrivate( qint64 _pos, qint64 _size ) :
|
||||
pos(_pos),
|
||||
size(_size)
|
||||
{}
|
||||
qint64 pos;
|
||||
qint64 size;
|
||||
};
|
||||
|
||||
KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group,
|
||||
const QString & symlink,
|
||||
qint64 pos, qint64 size )
|
||||
: KArchiveEntry( t, name, access, date, user, group, symlink ),
|
||||
d( new KArchiveFilePrivate(pos, size) )
|
||||
{
|
||||
}
|
||||
|
||||
KArchiveFile::~KArchiveFile()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
qint64 KArchiveFile::position() const
|
||||
{
|
||||
return d->pos;
|
||||
}
|
||||
|
||||
qint64 KArchiveFile::size() const
|
||||
{
|
||||
return d->size;
|
||||
}
|
||||
|
||||
void KArchiveFile::setSize( qint64 s )
|
||||
{
|
||||
d->size = s;
|
||||
}
|
||||
|
||||
QByteArray KArchiveFile::data() const
|
||||
{
|
||||
bool ok = archive()->device()->seek( d->pos );
|
||||
if (!ok) {
|
||||
kWarning() << "Failed to sync to" << d->pos << "to read" << name();
|
||||
}
|
||||
|
||||
// Read content
|
||||
QByteArray arr;
|
||||
if ( d->size )
|
||||
{
|
||||
arr = archive()->device()->read( d->size );
|
||||
Q_ASSERT( arr.size() == d->size );
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
QIODevice * KArchiveFile::createDevice() const
|
||||
{
|
||||
return new KLimitedIODevice( archive()->device(), d->pos, d->size );
|
||||
}
|
||||
|
||||
bool KArchiveFile::isFile() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void KArchiveFile::copyTo(const QString& dest) const
|
||||
{
|
||||
QFile f( dest + QLatin1Char('/') + name() );
|
||||
if ( f.open( QIODevice::ReadWrite | QIODevice::Truncate ) )
|
||||
{
|
||||
QIODevice* inputDev = createDevice();
|
||||
|
||||
// Read and write data in chunks to minimize memory usage
|
||||
const qint64 chunkSize = 1024 * 1024;
|
||||
qint64 remainingSize = d->size;
|
||||
QByteArray array;
|
||||
array.resize( int( qMin( chunkSize, remainingSize ) ) );
|
||||
|
||||
while ( remainingSize > 0 ) {
|
||||
const qint64 currentChunkSize = qMin( chunkSize, remainingSize );
|
||||
const qint64 n = inputDev->read( array.data(), currentChunkSize );
|
||||
Q_ASSERT( n == currentChunkSize );
|
||||
Q_UNUSED( n );
|
||||
f.write( array.data(), currentChunkSize );
|
||||
remainingSize -= currentChunkSize;
|
||||
}
|
||||
f.close();
|
||||
|
||||
delete inputDev;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////// KArchiveDirectory /////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class KArchiveDirectoryPrivate
|
||||
{
|
||||
public:
|
||||
~KArchiveDirectoryPrivate()
|
||||
{
|
||||
qDeleteAll(entries);
|
||||
}
|
||||
QHash<QString, KArchiveEntry *> entries;
|
||||
};
|
||||
|
||||
KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
|
||||
int date,
|
||||
const QString& user, const QString& group,
|
||||
const QString &symlink)
|
||||
: KArchiveEntry( t, name, access, date, user, group, symlink ),
|
||||
d( new KArchiveDirectoryPrivate )
|
||||
{
|
||||
}
|
||||
|
||||
KArchiveDirectory::~KArchiveDirectory()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QStringList KArchiveDirectory::entries() const
|
||||
{
|
||||
return d->entries.keys();
|
||||
}
|
||||
|
||||
const KArchiveEntry* KArchiveDirectory::entry( const QString& _name ) const
|
||||
{
|
||||
QString name = QDir::cleanPath(_name);
|
||||
int pos = name.indexOf( QLatin1Char('/') );
|
||||
if ( pos == 0 ) // ouch absolute path (see also KArchive::findOrCreate)
|
||||
{
|
||||
if (name.length()>1)
|
||||
{
|
||||
name = name.mid( 1 ); // remove leading slash
|
||||
pos = name.indexOf( QLatin1Char('/') ); // look again
|
||||
}
|
||||
else // "/"
|
||||
return this;
|
||||
}
|
||||
// trailing slash ? -> remove
|
||||
if ( pos != -1 && pos == name.length()-1 )
|
||||
{
|
||||
name = name.left( pos );
|
||||
pos = name.indexOf( QLatin1Char('/') ); // look again
|
||||
}
|
||||
if ( pos != -1 )
|
||||
{
|
||||
const QString left = name.left(pos);
|
||||
const QString right = name.mid(pos + 1);
|
||||
|
||||
//kDebug() << "left=" << left << "right=" << right;
|
||||
|
||||
const KArchiveEntry* e = d->entries.value( left );
|
||||
if ( !e || !e->isDirectory() )
|
||||
return 0;
|
||||
return static_cast<const KArchiveDirectory*>(e)->entry( right );
|
||||
}
|
||||
|
||||
return d->entries.value( name );
|
||||
}
|
||||
|
||||
void KArchiveDirectory::addEntry( KArchiveEntry* entry )
|
||||
{
|
||||
if( entry->name().isEmpty() )
|
||||
return;
|
||||
|
||||
if( d->entries.value( entry->name() ) ) {
|
||||
kWarning() << "directory " << name()
|
||||
<< "has entry" << entry->name() << "already";
|
||||
return;
|
||||
}
|
||||
d->entries.insert( entry->name(), entry );
|
||||
}
|
||||
|
||||
void KArchiveDirectory::removeEntry( KArchiveEntry* entry )
|
||||
{
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
QHash<QString, KArchiveEntry*>::Iterator it = d->entries.find(entry->name());
|
||||
// nothing removed?
|
||||
if (it == d->entries.end()) {
|
||||
kWarning() << "directory " << name()
|
||||
<< "has no entry with name " << entry->name();
|
||||
return;
|
||||
}
|
||||
if (it.value() != entry) {
|
||||
kWarning() << "directory " << name()
|
||||
<< "has another entry for name " << entry->name();
|
||||
return;
|
||||
}
|
||||
d->entries.erase(it);
|
||||
}
|
||||
|
||||
bool KArchiveDirectory::isDirectory() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sortByPosition( const KArchiveFile* file1, const KArchiveFile* file2 ) {
|
||||
return file1->position() < file2->position();
|
||||
}
|
||||
|
||||
void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
|
||||
{
|
||||
QDir root;
|
||||
|
||||
QList<const KArchiveFile*> fileList;
|
||||
QMap<qint64, QString> fileToDir;
|
||||
|
||||
// placeholders for iterated items
|
||||
QStack<const KArchiveDirectory *> dirStack;
|
||||
QStack<QString> dirNameStack;
|
||||
|
||||
dirStack.push( this ); // init stack at current directory
|
||||
dirNameStack.push( dest ); // ... with given path
|
||||
do {
|
||||
const KArchiveDirectory* curDir = dirStack.pop();
|
||||
const QString curDirName = dirNameStack.pop();
|
||||
root.mkdir(curDirName);
|
||||
|
||||
const QStringList dirEntries = curDir->entries();
|
||||
foreach(const QString it, dirEntries) {
|
||||
const KArchiveEntry* curEntry = curDir->entry(it);
|
||||
if (!curEntry->symLinkTarget().isEmpty()) {
|
||||
const QString linkName = curDirName+QLatin1Char('/')+curEntry->name();
|
||||
#ifdef Q_OS_UNIX
|
||||
if (!::symlink(curEntry->symLinkTarget().toLocal8Bit(), linkName.toLocal8Bit())) {
|
||||
kDebug() << "symlink(" << curEntry->symLinkTarget() << ',' << linkName << ") failed:" << strerror(errno);
|
||||
}
|
||||
#else
|
||||
// TODO - how to create symlinks on other platforms?
|
||||
#endif
|
||||
} else {
|
||||
if ( curEntry->isFile() ) {
|
||||
const KArchiveFile* curFile = dynamic_cast<const KArchiveFile*>( curEntry );
|
||||
if (curFile) {
|
||||
fileList.append( curFile );
|
||||
fileToDir.insert( curFile->position(), curDirName );
|
||||
}
|
||||
}
|
||||
|
||||
if ( curEntry->isDirectory() && recursiveCopy ) {
|
||||
const KArchiveDirectory *ad = dynamic_cast<const KArchiveDirectory*>( curEntry );
|
||||
if (ad) {
|
||||
dirStack.push( ad );
|
||||
dirNameStack.push( curDirName + QLatin1Char('/') + curEntry->name() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!dirStack.isEmpty());
|
||||
|
||||
qSort( fileList.begin(), fileList.end(), sortByPosition ); // sort on d->pos, so we have a linear access
|
||||
|
||||
foreach(const KArchiveFile *it, fileList) {
|
||||
qint64 pos = it->position();
|
||||
it->copyTo( fileToDir[pos] );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,597 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2000-2005 David Faure <faure@kde.org>
|
||||
Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifndef KARCHIVE_H
|
||||
#define KARCHIVE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
|
||||
#include <kdecore_export.h>
|
||||
|
||||
class KArchiveDirectory;
|
||||
class KArchiveFile;
|
||||
|
||||
class KArchivePrivate;
|
||||
/**
|
||||
* KArchive is a base class for reading and writing archives.
|
||||
* @short generic class for reading/writing archives
|
||||
* @author David Faure <faure@kde.org>
|
||||
*/
|
||||
class KDECORE_EXPORT KArchive
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Base constructor (protected since this is a pure virtual class).
|
||||
* @param fileName is a local path (e.g. "/tmp/myfile.ext"),
|
||||
* from which the archive will be read from, or into which the archive
|
||||
* will be written, depending on the mode given to open().
|
||||
*/
|
||||
KArchive( const QString& fileName );
|
||||
|
||||
/**
|
||||
* Base constructor (protected since this is a pure virtual class).
|
||||
* @param dev the I/O device where the archive reads its data
|
||||
* Note that this can be a file, but also a data buffer, a compression filter, etc.
|
||||
* For a file in writing mode it is better to use the other constructor
|
||||
* though, to benefit from the use of KSaveFile when saving.
|
||||
*/
|
||||
KArchive( QIODevice * dev );
|
||||
|
||||
public:
|
||||
virtual ~KArchive();
|
||||
|
||||
/**
|
||||
* Opens the archive for reading or writing.
|
||||
* Inherited classes might want to reimplement openArchive instead.
|
||||
* @param mode may be QIODevice::ReadOnly or QIODevice::WriteOnly
|
||||
* @see close
|
||||
*/
|
||||
virtual bool open( QIODevice::OpenMode mode );
|
||||
|
||||
/**
|
||||
* Closes the archive.
|
||||
* Inherited classes might want to reimplement closeArchive instead.
|
||||
*
|
||||
* @return true if close succeeded without problems
|
||||
* @see open
|
||||
*/
|
||||
virtual bool close();
|
||||
|
||||
/**
|
||||
* Checks whether the archive is open.
|
||||
* @return true if the archive is opened
|
||||
*/
|
||||
bool isOpen() const;
|
||||
|
||||
/**
|
||||
* Returns the mode in which the archive was opened
|
||||
* @return the mode in which the archive was opened (QIODevice::ReadOnly or QIODevice::WriteOnly)
|
||||
* @see open()
|
||||
*/
|
||||
QIODevice::OpenMode mode() const;
|
||||
|
||||
/**
|
||||
* The underlying device.
|
||||
* @return the underlying device.
|
||||
*/
|
||||
QIODevice * device() const;
|
||||
|
||||
/**
|
||||
* The name of the archive file, as passed to the constructor that takes a
|
||||
* fileName, or an empty string if you used the QIODevice constructor.
|
||||
* @return the name of the file, or QString() if unknown
|
||||
*/
|
||||
QString fileName() const;
|
||||
|
||||
/**
|
||||
* If an archive is opened for reading, then the contents
|
||||
* of the archive can be accessed via this function.
|
||||
* @return the directory of the archive
|
||||
*/
|
||||
const KArchiveDirectory* directory() const;
|
||||
|
||||
/**
|
||||
* Writes a local file into the archive. The main difference with writeFile,
|
||||
* is that this method minimizes memory usage, by not loading the whole file
|
||||
* into memory in one go.
|
||||
*
|
||||
* If @p fileName is a symbolic link, it will be written as is, i. e.
|
||||
* it will not be resolved before.
|
||||
* @param fileName full path to an existing local file, to be added to the archive.
|
||||
* @param destName the resulting name (or relative path) of the file in the archive.
|
||||
*/
|
||||
bool addLocalFile( const QString& fileName, const QString& destName );
|
||||
|
||||
/**
|
||||
* Writes a local directory into the archive, including all its contents, recursively.
|
||||
* Calls addLocalFile for each file to be added.
|
||||
*
|
||||
* Since KDE 3.2 it will also add a @p path that is a symbolic link to a
|
||||
* directory. The symbolic link will be dereferenced and the content of the
|
||||
* directory it is pointing to added recursively. However, symbolic links
|
||||
* *under* @p path will be stored as is.
|
||||
* @param path full path to an existing local directory, to be added to the archive.
|
||||
* @param destName the resulting name (or relative path) of the file in the archive.
|
||||
*/
|
||||
bool addLocalDirectory( const QString& path, const QString& destName );
|
||||
|
||||
enum { UnknownTime = static_cast<time_t>( -1 ) };
|
||||
|
||||
/**
|
||||
* If an archive is opened for writing then you can add new directories
|
||||
* using this function. KArchive won't write one directory twice.
|
||||
*
|
||||
* This method also allows some file metadata to be set.
|
||||
* However, depending on the archive type not all metadata might be regarded.
|
||||
*
|
||||
* @param name the name of the directory
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
virtual bool writeDir( const QString& name, const QString& user, const QString& group,
|
||||
mode_t perm = 040755, time_t atime = UnknownTime,
|
||||
time_t mtime = UnknownTime, time_t ctime = UnknownTime );
|
||||
|
||||
/**
|
||||
* Writes a symbolic link to the archive if supported.
|
||||
* The archive must be opened for writing.
|
||||
*
|
||||
* @param name name of symbolic link
|
||||
* @param target target of symbolic link
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
virtual bool writeSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm = 0120755, time_t atime = UnknownTime,
|
||||
time_t mtime = UnknownTime, time_t ctime = UnknownTime );
|
||||
|
||||
/**
|
||||
* If an archive is opened for writing then you can add a new file
|
||||
* using this function. If the file name is for example "mydir/test1" then
|
||||
* the directory "mydir" is automatically appended first if that did not
|
||||
* happen yet.
|
||||
*
|
||||
* This method also allows some file metadata to be
|
||||
* set. However, depending on the archive type not all metadata might be
|
||||
* regarded.
|
||||
* @param name the name of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param data the data to write (@p size bytes)
|
||||
* @param size the size of the file
|
||||
* @param perm permissions of the file
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
virtual bool writeFile( const QString& name, const QString& user, const QString& group,
|
||||
const char* data, qint64 size,
|
||||
mode_t perm = 0100644, time_t atime = UnknownTime,
|
||||
time_t mtime = UnknownTime, time_t ctime = UnknownTime );
|
||||
|
||||
/**
|
||||
* Here's another way of writing a file into an archive:
|
||||
* Call prepareWriting(), then call writeData()
|
||||
* as many times as wanted then call finishWriting( totalSize ).
|
||||
* For tar.gz files, you need to know the size before hand, it is needed in the header!
|
||||
* For zip files, size isn't used.
|
||||
*
|
||||
* This method also allows some file metadata to be
|
||||
* set. However, depending on the archive type not all metadata might be
|
||||
* regarded.
|
||||
* @param name the name of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param size the size of the file
|
||||
* @param perm permissions of the file
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
virtual bool prepareWriting( const QString& name, const QString& user,
|
||||
const QString& group, qint64 size,
|
||||
mode_t perm = 0100644, time_t atime = UnknownTime,
|
||||
time_t mtime = UnknownTime, time_t ctime = UnknownTime );
|
||||
|
||||
/**
|
||||
* Write data into the current file - to be called after calling prepareWriting
|
||||
*/
|
||||
virtual bool writeData( const char* data, qint64 size );
|
||||
|
||||
/**
|
||||
* Call finishWriting after writing the data.
|
||||
* @param size the size of the file
|
||||
* @see prepareWriting()
|
||||
*/
|
||||
virtual bool finishWriting( qint64 size );
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Opens an archive for reading or writing.
|
||||
* Called by open.
|
||||
* @param mode may be QIODevice::ReadOnly or QIODevice::WriteOnly
|
||||
*/
|
||||
virtual bool openArchive( QIODevice::OpenMode mode ) = 0;
|
||||
|
||||
/**
|
||||
* Closes the archive.
|
||||
* Called by close.
|
||||
*/
|
||||
virtual bool closeArchive() = 0;
|
||||
|
||||
/**
|
||||
* Retrieves or create the root directory.
|
||||
* The default implementation assumes that openArchive() did the parsing,
|
||||
* so it creates a dummy rootdir if none was set (write mode, or no '/' in the archive).
|
||||
* Reimplement this to provide parsing/listing on demand.
|
||||
* @return the root directory
|
||||
*/
|
||||
virtual KArchiveDirectory* rootDir();
|
||||
|
||||
/**
|
||||
* Write a directory to the archive.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* Depending on the archive type not all metadata might be used.
|
||||
*
|
||||
* @param name the name of the directory
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory. Use 040755 if you don't have any other information.
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see writeDir
|
||||
*/
|
||||
virtual bool doWriteDir( const QString& name, const QString& user, const QString& group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime ) = 0;
|
||||
|
||||
/**
|
||||
* Writes a symbolic link to the archive.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* @param name name of symbolic link
|
||||
* @param target target of symbolic link
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see writeSymLink
|
||||
*/
|
||||
virtual bool doWriteSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime) = 0;
|
||||
|
||||
/**
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* Depending on the archive type not all metadata might be used.
|
||||
*
|
||||
* @param name the name of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param size the size of the file
|
||||
* @param perm permissions of the file. Use 0100644 if you don't have any more specific permissions to set.
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see prepareWriting
|
||||
*/
|
||||
virtual bool doPrepareWriting( const QString& name, const QString& user,
|
||||
const QString& group, qint64 size, mode_t perm,
|
||||
time_t atime, time_t mtime, time_t ctime ) = 0;
|
||||
|
||||
/**
|
||||
* Called after writing the data.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* @param size the size of the file
|
||||
* @see finishWriting()
|
||||
*/
|
||||
virtual bool doFinishWriting( qint64 size ) = 0;
|
||||
|
||||
/**
|
||||
* Ensures that @p path exists, create otherwise.
|
||||
* This handles e.g. tar files missing directory entries, like mico-2.3.0.tar.gz :)
|
||||
* @param path the path of the directory
|
||||
* @return the directory with the given @p path
|
||||
*/
|
||||
KArchiveDirectory * findOrCreate( const QString & path );
|
||||
|
||||
/**
|
||||
* Can be reimplemented in order to change the creation of the device
|
||||
* (when using the fileName constructor). By default this method uses
|
||||
* KSaveFile when saving, and a simple QFile on reading.
|
||||
* This method is called by open().
|
||||
*/
|
||||
virtual bool createDevice( QIODevice::OpenMode mode );
|
||||
|
||||
/**
|
||||
* Can be called by derived classes in order to set the underlying device.
|
||||
* Note that KArchive will -not- own the device, it must be deleted by the derived class.
|
||||
*/
|
||||
void setDevice( QIODevice *dev );
|
||||
|
||||
/**
|
||||
* Derived classes call setRootDir from openArchive,
|
||||
* to set the root directory after parsing an existing archive.
|
||||
*/
|
||||
void setRootDir( KArchiveDirectory *rootDir );
|
||||
|
||||
private:
|
||||
KArchivePrivate* const d;
|
||||
};
|
||||
|
||||
class KArchiveEntryPrivate;
|
||||
/**
|
||||
* A base class for entries in an KArchive.
|
||||
* @short Base class for the archive-file's directory structure.
|
||||
*
|
||||
* @see KArchiveFile
|
||||
* @see KArchiveDirectory
|
||||
*/
|
||||
class KDECORE_EXPORT KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new entry.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
*/
|
||||
KArchiveEntry( KArchive* archive, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group,
|
||||
const QString& symlink );
|
||||
|
||||
virtual ~KArchiveEntry();
|
||||
|
||||
/**
|
||||
* Creation date of the file.
|
||||
* @return the creation date
|
||||
*/
|
||||
QDateTime datetime() const;
|
||||
|
||||
/**
|
||||
* Creation date of the file.
|
||||
* @return the creation date in seconds since 1970
|
||||
*/
|
||||
int date() const;
|
||||
|
||||
/**
|
||||
* Name of the file without path.
|
||||
* @return the file name without path
|
||||
*/
|
||||
QString name() const;
|
||||
/**
|
||||
* The permissions and mode flags as returned by the stat() function
|
||||
* in st_mode.
|
||||
* @return the permissions
|
||||
*/
|
||||
mode_t permissions() const;
|
||||
/**
|
||||
* User who created the file.
|
||||
* @return the owner of the file
|
||||
*/
|
||||
QString user() const;
|
||||
/**
|
||||
* Group of the user who created the file.
|
||||
* @return the group of the file
|
||||
*/
|
||||
QString group() const;
|
||||
|
||||
/**
|
||||
* Symlink if there is one.
|
||||
* @return the symlink, or QString()
|
||||
*/
|
||||
QString symLinkTarget() const;
|
||||
|
||||
/**
|
||||
* Checks whether the entry is a file.
|
||||
* @return true if this entry is a file
|
||||
*/
|
||||
virtual bool isFile() const;
|
||||
|
||||
/**
|
||||
* Checks whether the entry is a directory.
|
||||
* @return true if this entry is a directory
|
||||
*/
|
||||
virtual bool isDirectory() const;
|
||||
|
||||
protected:
|
||||
KArchive* archive() const;
|
||||
|
||||
private:
|
||||
KArchiveEntryPrivate* const d;
|
||||
};
|
||||
|
||||
class KArchiveFilePrivate;
|
||||
/**
|
||||
* Represents a file entry in a KArchive.
|
||||
* @short A file in an archive.
|
||||
*
|
||||
* @see KArchive
|
||||
* @see KArchiveDirectory
|
||||
*/
|
||||
class KDECORE_EXPORT KArchiveFile : public KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new file entry. Do not call this, KArchive takes care of it.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
* @param pos the position of the file in the directory
|
||||
* @param size the size of the file
|
||||
*/
|
||||
KArchiveFile( KArchive* archive, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group, const QString &symlink,
|
||||
qint64 pos, qint64 size );
|
||||
|
||||
/**
|
||||
* Destructor. Do not call this, KArchive takes care of it.
|
||||
*/
|
||||
virtual ~KArchiveFile();
|
||||
|
||||
/**
|
||||
* Position of the data in the [uncompressed] archive.
|
||||
* @return the position of the file
|
||||
*/
|
||||
qint64 position() const;
|
||||
/**
|
||||
* Size of the data.
|
||||
* @return the size of the file
|
||||
*/
|
||||
qint64 size() const;
|
||||
/**
|
||||
* Set size of data, usually after writing the file.
|
||||
* @param s the new size of the file
|
||||
*/
|
||||
void setSize( qint64 s );
|
||||
|
||||
/**
|
||||
* Returns the data of the file.
|
||||
* Call data() with care (only once per file), this data isn't cached.
|
||||
* @return the content of this file.
|
||||
*/
|
||||
virtual QByteArray data() const;
|
||||
|
||||
/**
|
||||
* This method returns QIODevice (internal class: KLimitedIODevice)
|
||||
* on top of the underlying QIODevice. This is obviously for reading only.
|
||||
*
|
||||
* WARNING: Note that the ownership of the device is being transferred to the caller,
|
||||
* who will have to delete it.
|
||||
*
|
||||
* The returned device auto-opens (in readonly mode), no need to open it.
|
||||
* @return the QIODevice of the file
|
||||
*/
|
||||
virtual QIODevice *createDevice() const;
|
||||
|
||||
/**
|
||||
* Checks whether this entry is a file.
|
||||
* @return true, since this entry is a file
|
||||
*/
|
||||
virtual bool isFile() const;
|
||||
|
||||
/**
|
||||
* Extracts the file to the directory @p dest
|
||||
* @param dest the directory to extract to
|
||||
*/
|
||||
void copyTo(const QString& dest) const;
|
||||
|
||||
private:
|
||||
KArchiveFilePrivate* const d;
|
||||
};
|
||||
|
||||
class KArchiveDirectoryPrivate;
|
||||
/**
|
||||
* Represents a directory entry in a KArchive.
|
||||
* @short A directory in an archive.
|
||||
*
|
||||
* @see KArchive
|
||||
* @see KArchiveFile
|
||||
*/
|
||||
class KDECORE_EXPORT KArchiveDirectory : public KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new directory entry.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
*/
|
||||
KArchiveDirectory( KArchive* archive, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group,
|
||||
const QString& symlink);
|
||||
|
||||
virtual ~KArchiveDirectory();
|
||||
|
||||
/**
|
||||
* Returns a list of sub-entries.
|
||||
* Note that the list is not sorted, it's even in random order (due to using a hashtable).
|
||||
* Use sort() on the result to sort the list by filename.
|
||||
*
|
||||
* @return the names of all entries in this directory (filenames, no path).
|
||||
*/
|
||||
QStringList entries() const;
|
||||
/**
|
||||
* Returns the entry with the given name.
|
||||
* @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc.
|
||||
* @return a pointer to the entry in the directory.
|
||||
*/
|
||||
const KArchiveEntry* entry( const QString& name ) const;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Adds a new entry to the directory.
|
||||
*/
|
||||
void addEntry( KArchiveEntry* );
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Adds a new entry to the directory.
|
||||
*/
|
||||
void removeEntry( KArchiveEntry* );
|
||||
|
||||
/**
|
||||
* Checks whether this entry is a directory.
|
||||
* @return true, since this entry is a directory
|
||||
*/
|
||||
virtual bool isDirectory() const;
|
||||
|
||||
/**
|
||||
* Extracts all entries in this archive directory to the directory
|
||||
* @p dest.
|
||||
* @param dest the directory to extract to
|
||||
* @param recursive if set to true, subdirectories are extracted as well
|
||||
*/
|
||||
void copyTo(const QString& dest, bool recursive = true) const;
|
||||
|
||||
private:
|
||||
KArchiveDirectoryPrivate* const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2001, 2002, 2007 David Faure <faure@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <kdebug.h>
|
||||
#include "klimitediodevice_p.h"
|
||||
|
||||
KLimitedIODevice::KLimitedIODevice( QIODevice *dev, qint64 start, qint64 length )
|
||||
: m_dev( dev ), m_start( start ), m_length( length )
|
||||
{
|
||||
//kDebug(7005) << "start=" << start << "length=" << length;
|
||||
open( QIODevice::ReadOnly ); //krazy:exclude=syscalls
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::open( QIODevice::OpenMode m )
|
||||
{
|
||||
//kDebug(7005) << "m=" << m;
|
||||
if ( m & QIODevice::ReadOnly ) {
|
||||
/*bool ok = false;
|
||||
if ( m_dev->isOpen() )
|
||||
ok = ( m_dev->mode() == QIODevice::ReadOnly );
|
||||
else
|
||||
ok = m_dev->open( m );
|
||||
if ( ok )*/
|
||||
m_dev->seek( m_start ); // No concurrent access !
|
||||
}
|
||||
else
|
||||
kWarning(7005) << "KLimitedIODevice::open only supports QIODevice::ReadOnly!";
|
||||
setOpenMode( QIODevice::ReadOnly );
|
||||
return true;
|
||||
}
|
||||
|
||||
void KLimitedIODevice::close()
|
||||
{
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::size() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::readData( char * data, qint64 maxlen )
|
||||
{
|
||||
maxlen = qMin( maxlen, m_length - pos() ); // Apply upper limit
|
||||
return m_dev->read( data, maxlen );
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::seek( qint64 pos )
|
||||
{
|
||||
Q_ASSERT( pos <= m_length );
|
||||
pos = qMin( pos, m_length ); // Apply upper limit
|
||||
bool ret = m_dev->seek( m_start + pos );
|
||||
if ( ret ) {
|
||||
QIODevice::seek( pos );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::bytesAvailable() const
|
||||
{
|
||||
return QIODevice::bytesAvailable();
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::isSequential() const
|
||||
{
|
||||
return m_dev->isSequential();
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2001, 2002, 2007 David Faure <faure@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KLIMITEDIODEVICE_P_H
|
||||
#define KLIMITEDIODEVICE_P_H
|
||||
|
||||
#include <QtCore/qiodevice.h>
|
||||
/**
|
||||
* A readonly device that reads from an underlying device
|
||||
* from a given point to another (e.g. to give access to a single
|
||||
* file inside an archive).
|
||||
* @author David Faure <faure@kde.org>
|
||||
* @internal - used by KArchive
|
||||
*/
|
||||
class KLimitedIODevice : public QIODevice
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new KLimitedIODevice.
|
||||
* @param dev the underlying device, opened or not
|
||||
* This device itself auto-opens (in readonly mode), no need to open it.
|
||||
* @param start where to start reading (position in bytes)
|
||||
* @param length the length of the data to read (in bytes)
|
||||
*/
|
||||
KLimitedIODevice( QIODevice *dev, qint64 start, qint64 length );
|
||||
virtual ~KLimitedIODevice() {}
|
||||
|
||||
virtual bool isSequential() const;
|
||||
|
||||
virtual bool open( QIODevice::OpenMode m );
|
||||
virtual void close();
|
||||
|
||||
virtual qint64 size() const;
|
||||
|
||||
virtual qint64 readData ( char * data, qint64 maxlen );
|
||||
virtual qint64 writeData ( const char *, qint64 ) { return -1; } // unsupported
|
||||
virtual int putChar( int ) { return -1; } // unsupported
|
||||
|
||||
//virtual qint64 pos() const { return m_dev->pos() - m_start; }
|
||||
virtual bool seek( qint64 pos );
|
||||
virtual qint64 bytesAvailable() const;
|
||||
private:
|
||||
QIODevice* m_dev;
|
||||
qint64 m_start;
|
||||
qint64 m_length;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,860 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2000 David Faure <faure@kde.org>
|
||||
Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ktar.h"
|
||||
|
||||
#include <stdlib.h> // strtol
|
||||
#include <time.h> // time()
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h> // S_IFDIR
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <kdebug.h>
|
||||
#include <kmimetype.h>
|
||||
#include <ktemporaryfile.h>
|
||||
|
||||
#include <kfilterdev.h>
|
||||
#include <kfilterbase.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////// KTar ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Mime types of known filters
|
||||
static const char application_gzip[] = "application/x-gzip";
|
||||
static const char application_bzip[] = "application/x-bzip";
|
||||
static const char application_lzma[] = "application/x-lzma";
|
||||
static const char application_xz[] = "application/x-xz";
|
||||
|
||||
class KTar::KTarPrivate
|
||||
{
|
||||
public:
|
||||
KTarPrivate(KTar *parent)
|
||||
: q(parent),
|
||||
tarEnd( 0 ),
|
||||
tmpFile( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
KTar *q;
|
||||
QStringList dirList;
|
||||
qint64 tarEnd;
|
||||
KTemporaryFile* tmpFile;
|
||||
QString mimetype;
|
||||
QByteArray origFileName;
|
||||
|
||||
bool fillTempFile(const QString & fileName);
|
||||
bool writeBackTempFile( const QString & fileName );
|
||||
void fillBuffer( char * buffer, const char * mode, qint64 size, time_t mtime,
|
||||
char typeflag, const char * uname, const char * gname );
|
||||
void writeLonglink(char *buffer, const QByteArray &name, char typeflag,
|
||||
const char *uname, const char *gname);
|
||||
qint64 readRawHeader(char *buffer);
|
||||
bool readLonglink(char *buffer, QByteArray &longlink);
|
||||
qint64 readHeader(char *buffer, QString &name, QString &symlink);
|
||||
};
|
||||
|
||||
KTar::KTar( const QString& fileName, const QString & _mimetype )
|
||||
: KArchive( fileName ), d(new KTarPrivate(this))
|
||||
{
|
||||
d->mimetype = _mimetype;
|
||||
}
|
||||
|
||||
KTar::KTar( QIODevice * dev )
|
||||
: KArchive( dev ), d(new KTarPrivate(this))
|
||||
{
|
||||
Q_ASSERT( dev );
|
||||
}
|
||||
|
||||
// Only called when a filename was given
|
||||
bool KTar::createDevice(QIODevice::OpenMode mode)
|
||||
{
|
||||
if (d->mimetype.isEmpty()) {
|
||||
// Find out mimetype manually
|
||||
|
||||
KMimeType::Ptr mime;
|
||||
if (mode != QIODevice::WriteOnly && QFile::exists(fileName())) {
|
||||
// Give priority to file contents: if someone renames a .tar.bz2 to .tar.gz,
|
||||
// we can still do the right thing here.
|
||||
mime = KMimeType::findByFileContent(fileName());
|
||||
if (mime == KMimeType::defaultMimeTypePtr()) {
|
||||
// Unable to determine mimetype from contents, get it from file name
|
||||
mime = KMimeType::findByPath(fileName(), 0, true);
|
||||
}
|
||||
} else {
|
||||
mime = KMimeType::findByPath(fileName(), 0, true);
|
||||
}
|
||||
|
||||
//kDebug(7041) << mode << mime->name();
|
||||
|
||||
if (mime->is(QString::fromLatin1("application/x-compressed-tar")) || mime->is(QString::fromLatin1(application_gzip))) {
|
||||
// gzipped tar file (with possibly invalid file name), ask for gzip filter
|
||||
d->mimetype = QString::fromLatin1(application_gzip);
|
||||
} else if (mime->is(QString::fromLatin1("application/x-bzip-compressed-tar")) || mime->is(QString::fromLatin1(application_bzip))) {
|
||||
// bzipped2 tar file (with possibly invalid file name), ask for bz2 filter
|
||||
d->mimetype = QString::fromLatin1(application_bzip);
|
||||
} else if (mime->is(QString::fromLatin1("application/x-lzma-compressed-tar")) || mime->is(QString::fromLatin1(application_lzma))) {
|
||||
// lzma compressed tar file (with possibly invalid file name), ask for xz filter
|
||||
d->mimetype = QString::fromLatin1(application_lzma);
|
||||
} else if (mime->is(QString::fromLatin1("application/x-xz-compressed-tar")) || mime->is(QString::fromLatin1(application_xz))) {
|
||||
// xz compressed tar file (with possibly invalid name), ask for xz filter
|
||||
d->mimetype = QString::fromLatin1(application_xz);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->mimetype == QLatin1String("application/x-tar")) {
|
||||
return KArchive::createDevice(mode);
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
if (!KArchive::createDevice(mode))
|
||||
return false;
|
||||
if (!d->mimetype.isEmpty()) {
|
||||
// Create a compression filter on top of the KSaveFile device that KArchive created.
|
||||
//kDebug(7041) << "creating KFilterDev for" << d->mimetype;
|
||||
QIODevice *filterDev = KFilterDev::device(device(), d->mimetype);
|
||||
Q_ASSERT(filterDev);
|
||||
setDevice(filterDev);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// The compression filters are very slow with random access.
|
||||
// So instead of applying the filter to the device,
|
||||
// the file is completely extracted instead,
|
||||
// and we work on the extracted tar file.
|
||||
// This improves the extraction speed by the tar ioslave dramatically,
|
||||
// if the archive file contains many files.
|
||||
// This is because the tar ioslave extracts one file after the other and normally
|
||||
// has to walk through the decompression filter each time.
|
||||
// Which is in fact nearly as slow as a complete decompression for each file.
|
||||
|
||||
Q_ASSERT(!d->tmpFile);
|
||||
d->tmpFile = new KTemporaryFile();
|
||||
d->tmpFile->setPrefix(QLatin1String("ktar-"));
|
||||
d->tmpFile->setSuffix(QLatin1String(".tar"));
|
||||
d->tmpFile->open();
|
||||
//kDebug(7041) << "creating tempfile:" << d->tmpFile->fileName();
|
||||
|
||||
setDevice(d->tmpFile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
KTar::~KTar()
|
||||
{
|
||||
// mjarrett: Closes to prevent ~KArchive from aborting w/o device
|
||||
if( isOpen() )
|
||||
close();
|
||||
|
||||
delete d->tmpFile;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KTar::setOrigFileName( const QByteArray & fileName ) {
|
||||
if ( !isOpen() || !(mode() & QIODevice::WriteOnly) )
|
||||
{
|
||||
kWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
|
||||
return;
|
||||
}
|
||||
d->origFileName = fileName;
|
||||
}
|
||||
|
||||
qint64 KTar::KTarPrivate::readRawHeader( char *buffer ) {
|
||||
// Read header
|
||||
qint64 n = q->device()->read( buffer, 0x200 );
|
||||
// we need to test if there is a prefix value because the file name can be null
|
||||
// and the prefix can have a value and in this case we don't reset n.
|
||||
if ( n == 0x200 && (buffer[0] != 0 || buffer[0x159] != 0) ) {
|
||||
// Make sure this is actually a tar header
|
||||
if (strncmp(buffer + 257, "ustar", 5)) {
|
||||
// The magic isn't there (broken/old tars), but maybe a correct checksum?
|
||||
|
||||
int check = 0;
|
||||
for( uint j = 0; j < 0x200; ++j )
|
||||
check += buffer[j];
|
||||
|
||||
// adjust checksum to count the checksum fields as blanks
|
||||
for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
|
||||
check -= buffer[148 + j];
|
||||
check += 8 * ' ';
|
||||
|
||||
QByteArray s = QByteArray::number( check, 8 ); // octal
|
||||
|
||||
// only compare those of the 6 checksum digits that mean something,
|
||||
// because the other digits are filled with all sorts of different chars by different tars ...
|
||||
// Some tars right-justify the checksum so it could start in one of three places - we have to check each.
|
||||
if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
|
||||
&& strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
|
||||
&& strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
|
||||
kWarning(7041) << "KTar: invalid TAR file. Header is:" << QByteArray( buffer+257, 5 )
|
||||
<< "instead of ustar. Reading from wrong pos in file?"
|
||||
<< "checksum=" << QByteArray( buffer + 148 + 6 - s.length(), s.length() );
|
||||
return -1;
|
||||
}
|
||||
}/*end if*/
|
||||
} else {
|
||||
// reset to 0 if 0x200 because logical end of archive has been reached
|
||||
if (n == 0x200) n = 0;
|
||||
}/*end if*/
|
||||
return n;
|
||||
}
|
||||
|
||||
bool KTar::KTarPrivate::readLonglink(char *buffer,QByteArray &longlink) {
|
||||
qint64 n = 0;
|
||||
//kDebug() << "reading longlink from pos " << q->device()->pos();
|
||||
QIODevice *dev = q->device();
|
||||
// read size of longlink from size field in header
|
||||
// size is in bytes including the trailing null (which we ignore)
|
||||
qint64 size = QByteArray( buffer + 0x7c, 12 ).trimmed().toLongLong( 0, 8 /*octal*/ );
|
||||
|
||||
size--; // ignore trailing null
|
||||
longlink.resize(size);
|
||||
qint64 offset = 0;
|
||||
while (size > 0) {
|
||||
int chunksize = qMin(size, 0x200LL);
|
||||
n = dev->read( longlink.data() + offset, chunksize );
|
||||
if (n == -1) return false;
|
||||
size -= chunksize;
|
||||
offset += 0x200;
|
||||
}/*wend*/
|
||||
// jump over the rest
|
||||
const int skip = 0x200 - (n % 0x200);
|
||||
if (skip <= 0x200) {
|
||||
if (dev->read(buffer,skip) != skip)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 KTar::KTarPrivate::readHeader( char *buffer, QString &name, QString &symlink ) {
|
||||
name.truncate(0);
|
||||
symlink.truncate(0);
|
||||
while (true) {
|
||||
qint64 n = readRawHeader(buffer);
|
||||
if (n != 0x200) return n;
|
||||
|
||||
// is it a longlink?
|
||||
if (strcmp(buffer,"././@LongLink") == 0) {
|
||||
char typeflag = buffer[0x9c];
|
||||
QByteArray longlink;
|
||||
readLonglink(buffer,longlink);
|
||||
switch (typeflag) {
|
||||
case 'L': name = QFile::decodeName(longlink); break;
|
||||
case 'K': symlink = QFile::decodeName(longlink); break;
|
||||
}/*end switch*/
|
||||
} else {
|
||||
break;
|
||||
}/*end if*/
|
||||
}/*wend*/
|
||||
|
||||
// if not result of longlink, read names directly from the header
|
||||
if (name.isEmpty())
|
||||
// there are names that are exactly 100 bytes long
|
||||
// and neither longlink nor \0 terminated (bug:101472)
|
||||
name = QFile::decodeName(QByteArray(buffer, 100));
|
||||
if (symlink.isEmpty())
|
||||
symlink = QFile::decodeName(QByteArray(buffer + 0x9d /*?*/, 100));
|
||||
|
||||
return 0x200;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have created a temporary file, we have
|
||||
* to decompress the original file now and write
|
||||
* the contents to the temporary file.
|
||||
*/
|
||||
bool KTar::KTarPrivate::fillTempFile( const QString & fileName) {
|
||||
if ( ! tmpFile )
|
||||
return true;
|
||||
|
||||
//kDebug(7041) << "filling tmpFile of mimetype" << mimetype;
|
||||
|
||||
bool forced = false;
|
||||
if ( QLatin1String(application_gzip) == mimetype || QLatin1String(application_bzip) == mimetype )
|
||||
forced = true;
|
||||
|
||||
QIODevice *filterDev = KFilterDev::deviceForFile( fileName, mimetype, forced );
|
||||
|
||||
if( filterDev ) {
|
||||
QFile* file = tmpFile;
|
||||
Q_ASSERT(file->isOpen());
|
||||
Q_ASSERT(file->openMode() & QIODevice::WriteOnly);
|
||||
file->seek(0);
|
||||
QByteArray buffer;
|
||||
buffer.resize(8*1024);
|
||||
if ( ! filterDev->open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
delete filterDev;
|
||||
return false;
|
||||
}
|
||||
qint64 len = -1;
|
||||
while ( !filterDev->atEnd() && len != 0 ) {
|
||||
len = filterDev->read(buffer.data(),buffer.size());
|
||||
if ( len < 0 ) { // corrupted archive
|
||||
delete filterDev;
|
||||
return false;
|
||||
}
|
||||
if ( file->write(buffer.data(), len) != len ) { // disk full
|
||||
delete filterDev;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
filterDev->close();
|
||||
delete filterDev;
|
||||
|
||||
file->flush();
|
||||
file->seek(0);
|
||||
Q_ASSERT(file->isOpen());
|
||||
Q_ASSERT(file->openMode() & QIODevice::ReadOnly);
|
||||
} else {
|
||||
kDebug(7041) << "no filterdevice found!";
|
||||
}
|
||||
|
||||
//kDebug( 7041 ) << "filling tmpFile finished.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KTar::openArchive( QIODevice::OpenMode mode ) {
|
||||
|
||||
if ( !(mode & QIODevice::ReadOnly) )
|
||||
return true;
|
||||
|
||||
if ( !d->fillTempFile( fileName() ) )
|
||||
return false;
|
||||
|
||||
// We'll use the permission and user/group of d->rootDir
|
||||
// for any directory we emulate (see findOrCreate)
|
||||
//KDE_struct_stat buf;
|
||||
//KDE_stat( fileName(), &buf );
|
||||
|
||||
d->dirList.clear();
|
||||
QIODevice* dev = device();
|
||||
|
||||
if ( !dev )
|
||||
return false;
|
||||
|
||||
// read dir information
|
||||
char buffer[ 0x200 ];
|
||||
bool ende = false;
|
||||
do
|
||||
{
|
||||
QString name;
|
||||
QString symlink;
|
||||
|
||||
// Read header
|
||||
qint64 n = d->readHeader( buffer, name, symlink );
|
||||
if (n < 0) return false;
|
||||
if (n == 0x200)
|
||||
{
|
||||
bool isdir = false;
|
||||
|
||||
if ( name.endsWith( QLatin1Char( '/' ) ) )
|
||||
{
|
||||
isdir = true;
|
||||
name.truncate( name.length() - 1 );
|
||||
}
|
||||
|
||||
QByteArray prefix = QByteArray(buffer + 0x159, 155);
|
||||
if (prefix[0] != '\0') {
|
||||
name = (QString::fromLatin1(prefix.constData()) + QLatin1Char('/') + name);
|
||||
}
|
||||
|
||||
int pos = name.lastIndexOf( QLatin1Char('/') );
|
||||
QString nm = ( pos == -1 ) ? name : name.mid( pos + 1 );
|
||||
|
||||
// read access
|
||||
buffer[ 0x6b ] = 0;
|
||||
char *dummy;
|
||||
const char* p = buffer + 0x64;
|
||||
while( *p == ' ' ) ++p;
|
||||
int access = (int)strtol( p, &dummy, 8 );
|
||||
|
||||
// read user and group
|
||||
QString user = QString::fromLocal8Bit( buffer + 0x109 );
|
||||
QString group = QString::fromLocal8Bit( buffer + 0x129 );
|
||||
|
||||
// read time
|
||||
buffer[ 0x93 ] = 0;
|
||||
p = buffer + 0x88;
|
||||
while( *p == ' ' ) ++p;
|
||||
int time = (int)strtol( p, &dummy, 8 );
|
||||
|
||||
// read type flag
|
||||
char typeflag = buffer[ 0x9c ];
|
||||
// '0' for files, '1' hard link, '2' symlink, '5' for directory
|
||||
// (and 'L' for longlink fileNames, 'K' for longlink symlink targets)
|
||||
// 'D' for GNU tar extension DUMPDIR, 'x' for Extended header referring
|
||||
// to the next file in the archive and 'g' for Global extended header
|
||||
|
||||
if ( typeflag == '5' )
|
||||
isdir = true;
|
||||
|
||||
bool isDumpDir = false;
|
||||
if ( typeflag == 'D' )
|
||||
{
|
||||
isdir = false;
|
||||
isDumpDir = true;
|
||||
}
|
||||
//kDebug(7041) << nm << "isdir=" << isdir << "pos=" << dev->pos() << "typeflag=" << typeflag << " islink=" << ( typeflag == '1' || typeflag == '2' );
|
||||
|
||||
if (typeflag == 'x' || typeflag == 'g') { // pax extended header, or pax global extended header
|
||||
// Skip it for now. TODO: implement reading of extended header, as per http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
|
||||
(void)dev->read( buffer, 0x200 );
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isdir)
|
||||
access |= S_IFDIR; // f*cking broken tar files
|
||||
|
||||
KArchiveEntry* e;
|
||||
if ( isdir )
|
||||
{
|
||||
//kDebug(7041) << "directory" << nm;
|
||||
e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
|
||||
}
|
||||
else
|
||||
{
|
||||
// read size
|
||||
QByteArray sizeBuffer( buffer + 0x7c, 12 );
|
||||
qint64 size = sizeBuffer.trimmed().toLongLong( 0, 8 /*octal*/ );
|
||||
//kDebug(7041) << "sizeBuffer='" << sizeBuffer << "' -> size=" << size;
|
||||
|
||||
// for isDumpDir we will skip the additional info about that dirs contents
|
||||
if ( isDumpDir )
|
||||
{
|
||||
//kDebug(7041) << nm << "isDumpDir";
|
||||
e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Let's hack around hard links. Our classes don't support that, so make them symlinks
|
||||
if ( typeflag == '1' )
|
||||
{
|
||||
kDebug(7041) << "Hard link, setting size to 0 instead of" << size;
|
||||
size = 0; // no contents
|
||||
}
|
||||
|
||||
//kDebug(7041) << "file" << nm << "size=" << size;
|
||||
e = new KArchiveFile( this, nm, access, time, user, group, symlink,
|
||||
dev->pos(), size );
|
||||
}
|
||||
|
||||
// Skip contents + align bytes
|
||||
qint64 rest = size % 0x200;
|
||||
qint64 skip = size + (rest ? 0x200 - rest : 0);
|
||||
//kDebug(7041) << "pos()=" << dev->pos() << "rest=" << rest << "skipping" << skip;
|
||||
if (! dev->seek( dev->pos() + skip ) )
|
||||
kWarning(7041) << "skipping" << skip << "failed";
|
||||
}
|
||||
|
||||
if ( pos == -1 )
|
||||
{
|
||||
if (nm == QLatin1String(".")) { // special case
|
||||
Q_ASSERT( isdir );
|
||||
if (isdir) {
|
||||
setRootDir( static_cast<KArchiveDirectory *>( e ) );
|
||||
}
|
||||
} else {
|
||||
rootDir()->addEntry( e );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString path = QDir::cleanPath( name.left( pos ) );
|
||||
// Ensure container directory exists, create otherwise
|
||||
KArchiveDirectory * d = findOrCreate( path );
|
||||
d->addEntry( e );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//qDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
|
||||
d->tarEnd = dev->pos() - n; // Remember end of archive
|
||||
ende = true;
|
||||
}
|
||||
} while( !ende );
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes back the changes of the temporary file
|
||||
* to the original file.
|
||||
* Must only be called if in write mode, not in read mode
|
||||
*/
|
||||
bool KTar::KTarPrivate::writeBackTempFile( const QString & fileName )
|
||||
{
|
||||
if ( !tmpFile )
|
||||
return true;
|
||||
|
||||
//kDebug(7041) << "Write temporary file to compressed file" << fileName << mimetype;
|
||||
|
||||
bool forced = false;
|
||||
if (QLatin1String(application_gzip) == mimetype || QLatin1String(application_bzip) == mimetype ||
|
||||
QLatin1String(application_lzma) == mimetype || QLatin1String(application_xz) == mimetype)
|
||||
forced = true;
|
||||
|
||||
// #### TODO this should use KSaveFile to avoid problems on disk full
|
||||
// (KArchive uses KSaveFile by default, but the temp-uncompressed-file trick
|
||||
// circumvents that).
|
||||
|
||||
QIODevice *dev = KFilterDev::deviceForFile( fileName, mimetype, forced );
|
||||
if( dev ) {
|
||||
QFile* file = tmpFile;
|
||||
if ( !dev->open(QIODevice::WriteOnly) )
|
||||
{
|
||||
file->close();
|
||||
delete dev;
|
||||
return false;
|
||||
}
|
||||
if ( forced )
|
||||
static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
|
||||
file->seek(0);
|
||||
QByteArray buffer;
|
||||
buffer.resize(8*1024);
|
||||
qint64 len;
|
||||
while ( !file->atEnd()) {
|
||||
len = file->read(buffer.data(), buffer.size());
|
||||
dev->write(buffer.data(),len); // TODO error checking
|
||||
}
|
||||
file->close();
|
||||
dev->close();
|
||||
delete dev;
|
||||
}
|
||||
|
||||
//kDebug(7041) << "Write temporary file to compressed file done.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KTar::closeArchive() {
|
||||
d->dirList.clear();
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// If we are in readwrite mode and had created
|
||||
// a temporary tar file, we have to write
|
||||
// back the changes to the original file
|
||||
if (d->tmpFile && (mode() & QIODevice::WriteOnly)) {
|
||||
ok = d->writeBackTempFile( fileName() );
|
||||
delete d->tmpFile;
|
||||
d->tmpFile = 0;
|
||||
setDevice(0);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool KTar::doFinishWriting( qint64 size ) {
|
||||
// Write alignment
|
||||
int rest = size % 0x200;
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
d->tarEnd = device()->pos() + (rest ? 0x200 - rest : 0); // Record our new end of archive
|
||||
if ( rest )
|
||||
{
|
||||
char buffer[ 0x201 ];
|
||||
for( uint i = 0; i < 0x200; ++i )
|
||||
buffer[i] = 0;
|
||||
qint64 nwritten = device()->write( buffer, 0x200 - rest );
|
||||
return nwritten == 0x200 - rest;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*** Some help from the tar sources
|
||||
struct posix_header
|
||||
{ byte offset
|
||||
char name[100]; * 0 * 0x0
|
||||
char mode[8]; * 100 * 0x64
|
||||
char uid[8]; * 108 * 0x6c
|
||||
char gid[8]; * 116 * 0x74
|
||||
char size[12]; * 124 * 0x7c
|
||||
char mtime[12]; * 136 * 0x88
|
||||
char chksum[8]; * 148 * 0x94
|
||||
char typeflag; * 156 * 0x9c
|
||||
char linkname[100]; * 157 * 0x9d
|
||||
char magic[6]; * 257 * 0x101
|
||||
char version[2]; * 263 * 0x107
|
||||
char uname[32]; * 265 * 0x109
|
||||
char gname[32]; * 297 * 0x129
|
||||
char devmajor[8]; * 329 * 0x149
|
||||
char devminor[8]; * 337 * ...
|
||||
char prefix[155]; * 345 *
|
||||
* 500 *
|
||||
};
|
||||
*/
|
||||
|
||||
void KTar::KTarPrivate::fillBuffer( char * buffer,
|
||||
const char * mode, qint64 size, time_t mtime, char typeflag,
|
||||
const char * uname, const char * gname ) {
|
||||
// mode (as in stpos())
|
||||
assert( strlen(mode) == 6 );
|
||||
memcpy( buffer+0x64, mode, 6 );
|
||||
buffer[ 0x6a ] = ' ';
|
||||
buffer[ 0x6b ] = '\0';
|
||||
|
||||
// dummy uid
|
||||
strcpy( buffer + 0x6c, " 765 ");
|
||||
// dummy gid
|
||||
strcpy( buffer + 0x74, " 144 ");
|
||||
|
||||
// size
|
||||
QByteArray s = QByteArray::number( size, 8 ); // octal
|
||||
s = s.rightJustified( 11, '0' );
|
||||
memcpy( buffer + 0x7c, s.data(), 11 );
|
||||
buffer[ 0x87 ] = ' '; // space-terminate (no null after)
|
||||
|
||||
// modification time
|
||||
s = QByteArray::number( static_cast<qulonglong>(mtime), 8 ); // octal
|
||||
s = s.rightJustified( 11, '0' );
|
||||
memcpy( buffer + 0x88, s.data(), 11 );
|
||||
buffer[ 0x93 ] = ' '; // space-terminate (no null after) -- well current tar writes a null byte
|
||||
|
||||
// spaces, replaced by the check sum later
|
||||
buffer[ 0x94 ] = 0x20;
|
||||
buffer[ 0x95 ] = 0x20;
|
||||
buffer[ 0x96 ] = 0x20;
|
||||
buffer[ 0x97 ] = 0x20;
|
||||
buffer[ 0x98 ] = 0x20;
|
||||
buffer[ 0x99 ] = 0x20;
|
||||
|
||||
/* From the tar sources :
|
||||
Fill in the checksum field. It's formatted differently from the
|
||||
other fields: it has [6] digits, a null, then a space -- rather than
|
||||
digits, a space, then a null. */
|
||||
|
||||
buffer[ 0x9a ] = '\0';
|
||||
buffer[ 0x9b ] = ' ';
|
||||
|
||||
// type flag (dir, file, link)
|
||||
buffer[ 0x9c ] = typeflag;
|
||||
|
||||
// magic + version
|
||||
strcpy( buffer + 0x101, "ustar");
|
||||
strcpy( buffer + 0x107, "00" );
|
||||
|
||||
// user
|
||||
strcpy( buffer + 0x109, uname );
|
||||
// group
|
||||
strcpy( buffer + 0x129, gname );
|
||||
|
||||
// Header check sum
|
||||
int check = 32;
|
||||
for( uint j = 0; j < 0x200; ++j )
|
||||
check += buffer[j];
|
||||
s = QByteArray::number( check, 8 ); // octal
|
||||
s = s.rightJustified( 6, '0' );
|
||||
memcpy( buffer + 0x94, s.constData(), 6 );
|
||||
}
|
||||
|
||||
void KTar::KTarPrivate::writeLonglink(char *buffer, const QByteArray &name, char typeflag,
|
||||
const char *uname, const char *gname) {
|
||||
strcpy( buffer, "././@LongLink" );
|
||||
qint64 namelen = name.length() + 1;
|
||||
fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname );
|
||||
q->device()->write( buffer, 0x200 ); // TODO error checking
|
||||
qint64 offset = 0;
|
||||
while (namelen > 0) {
|
||||
int chunksize = qMin(namelen, 0x200LL);
|
||||
memcpy(buffer, name.data()+offset, chunksize);
|
||||
// write long name
|
||||
q->device()->write( buffer, 0x200 ); // TODO error checking
|
||||
// not even needed to reclear the buffer, tar doesn't do it
|
||||
namelen -= chunksize;
|
||||
offset += 0x200;
|
||||
}/*wend*/
|
||||
}
|
||||
|
||||
bool KTar::doPrepareWriting(const QString &name, const QString &user,
|
||||
const QString &group, qint64 size, mode_t perm,
|
||||
time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
|
||||
if ( !isOpen() )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file before writing to it\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !(mode() & QIODevice::WriteOnly) )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file for writing\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString fileName ( QDir::cleanPath( name ) );
|
||||
|
||||
/*
|
||||
// Create toplevel dirs
|
||||
// Commented out by David since it's not necessary, and if anybody thinks it is,
|
||||
// he needs to implement a findOrCreate equivalent in writeDir.
|
||||
// But as KTar and the "tar" program both handle tar files without
|
||||
// dir entries, there's really no need for that
|
||||
QString tmp ( fileName );
|
||||
int i = tmp.lastIndexOf( '/' );
|
||||
if ( i != -1 )
|
||||
{
|
||||
QString d = tmp.left( i + 1 ); // contains trailing slash
|
||||
if ( !m_dirList.contains( d ) )
|
||||
{
|
||||
tmp = tmp.mid( i + 1 );
|
||||
writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
char buffer[ 0x201 ];
|
||||
memset( buffer, 0, 0x200 );
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
|
||||
// provide converted stuff we need later on
|
||||
const QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
const QByteArray uname = user.toLocal8Bit();
|
||||
const QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 chars, we need to use the LongLink trick
|
||||
if ( fileName.length() > 99 )
|
||||
d->writeLonglink(buffer,encodedFileName,'L',uname,gname);
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy( buffer, encodedFileName, 99 );
|
||||
buffer[99] = 0;
|
||||
// zero out the rest (except for what gets filled anyways)
|
||||
memset(buffer+0x9d, 0, 0x200 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number( (unsigned int)perm, 8 );
|
||||
permstr = permstr.rightJustified(6, '0');
|
||||
d->fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
|
||||
|
||||
// Write header
|
||||
return device()->write( buffer, 0x200 ) == 0x200;
|
||||
}
|
||||
|
||||
bool KTar::doWriteDir(const QString &name, const QString &user,
|
||||
const QString &group, mode_t perm,
|
||||
time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
|
||||
if ( !isOpen() )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file before writing to it\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !(mode() & QIODevice::WriteOnly) )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file for writing\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./ => call cleanPath
|
||||
QString dirName ( QDir::cleanPath( name ) );
|
||||
|
||||
// Need trailing '/'
|
||||
if ( !dirName.endsWith( QLatin1Char( '/' ) ) )
|
||||
dirName += QLatin1Char( '/' );
|
||||
|
||||
if ( d->dirList.contains( dirName ) )
|
||||
return true; // already there
|
||||
|
||||
char buffer[ 0x201 ];
|
||||
memset( buffer, 0, 0x200 );
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
|
||||
// provide converted stuff we need lateron
|
||||
QByteArray encodedDirname = QFile::encodeName(dirName);
|
||||
QByteArray uname = user.toLocal8Bit();
|
||||
QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 chars, we need to use the LongLink trick
|
||||
if ( dirName.length() > 99 )
|
||||
d->writeLonglink(buffer,encodedDirname,'L',uname,gname);
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy( buffer, encodedDirname, 99 );
|
||||
buffer[99] = 0;
|
||||
// zero out the rest (except for what gets filled anyways)
|
||||
memset(buffer+0x9d, 0, 0x200 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number( (unsigned int)perm, 8 );
|
||||
permstr = permstr.rightJustified(6, ' ');
|
||||
d->fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
|
||||
|
||||
// Write header
|
||||
device()->write( buffer, 0x200 );
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
d->tarEnd = device()->pos();
|
||||
|
||||
d->dirList.append( dirName ); // contains trailing slash
|
||||
return true; // TODO if wanted, better error control
|
||||
}
|
||||
|
||||
bool KTar::doWriteSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
|
||||
if ( !isOpen() )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file before writing to it\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !(mode() & QIODevice::WriteOnly) )
|
||||
{
|
||||
kWarning(7041) << "You must open the tar file for writing\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString fileName ( QDir::cleanPath( name ) );
|
||||
|
||||
char buffer[ 0x201 ];
|
||||
memset( buffer, 0, 0x200 );
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
|
||||
// provide converted stuff we need lateron
|
||||
QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
QByteArray encodedTarget = QFile::encodeName(target);
|
||||
QByteArray uname = user.toLocal8Bit();
|
||||
QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 chars, we need to use the LongLink trick
|
||||
if (target.length() > 99)
|
||||
d->writeLonglink(buffer,encodedTarget,'K',uname,gname);
|
||||
if ( fileName.length() > 99 )
|
||||
d->writeLonglink(buffer,encodedFileName,'L',uname,gname);
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy( buffer, encodedFileName, 99 );
|
||||
buffer[99] = 0;
|
||||
// Write (potentially truncated) symlink target
|
||||
strncpy(buffer+0x9d, encodedTarget, 99);
|
||||
buffer[0x9d+99] = 0;
|
||||
// zero out the rest
|
||||
memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number( (unsigned int)perm, 8 );
|
||||
permstr = permstr.rightJustified(6, ' ');
|
||||
d->fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
|
||||
|
||||
// Write header
|
||||
bool retval = device()->write( buffer, 0x200 ) == 0x200;
|
||||
if ( ( mode() & QIODevice::ReadWrite ) == QIODevice::ReadWrite )
|
||||
d->tarEnd = device()->pos();
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2000-2005 David Faure <faure@kde.org>
|
||||
Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifndef KTAR_H
|
||||
#define KTAR_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* A class for reading / writing (optionally compressed) tar archives.
|
||||
*
|
||||
* KTar allows you to read and write tar archives, including those
|
||||
* that are compressed using gzip, bzip2 or xz.
|
||||
*
|
||||
* @author Torben Weis <weis@kde.org>, David Faure <faure@kde.org>
|
||||
*/
|
||||
class KDECORE_EXPORT KTar : public KArchive
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename
|
||||
* using the compression filter associated to given mimetype.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/weis/myfile.tgz")
|
||||
* @param mimetype "application/x-gzip", "application/x-bzip" or
|
||||
* "application/x-xz"
|
||||
* Do not use application/x-compressed-tar or similar - you only need to
|
||||
* specify the compression layer ! If the mimetype is omitted, it
|
||||
* will be determined from the filename.
|
||||
*/
|
||||
explicit KTar( const QString& filename,
|
||||
const QString& mimetype = QString() );
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KFilterDev) or not (QFile, etc.).
|
||||
* @warning Do not assume that giving a QFile here will decompress the file,
|
||||
* in case it's compressed!
|
||||
* @param dev the device to read from. If the source is compressed, the
|
||||
* QIODevice must take care of decompression
|
||||
*/
|
||||
explicit KTar( QIODevice * dev );
|
||||
|
||||
/**
|
||||
* If the tar ball is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
virtual ~KTar();
|
||||
|
||||
/**
|
||||
* Special function for setting the "original file name" in the gzip header,
|
||||
* when writing a tar.gz file. It appears when using in the "file" command,
|
||||
* for instance. Should only be called if the underlying device is a KFilterDev!
|
||||
* @param fileName the original file name
|
||||
*/
|
||||
void setOrigFileName( const QByteArray & fileName );
|
||||
|
||||
protected:
|
||||
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doWriteSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime);
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doWriteDir( const QString& name, const QString& user, const QString& group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime );
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doPrepareWriting( const QString& name, const QString& user,
|
||||
const QString& group, qint64 size, mode_t perm,
|
||||
time_t atime, time_t mtime, time_t ctime );
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doFinishWriting( qint64 size );
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
* @param mode the mode of the file
|
||||
*/
|
||||
virtual bool openArchive( QIODevice::OpenMode mode );
|
||||
virtual bool closeArchive();
|
||||
|
||||
virtual bool createDevice( QIODevice::OpenMode mode );
|
||||
|
||||
private:
|
||||
class KTarPrivate;
|
||||
KTarPrivate* const d;
|
||||
};
|
||||
|
||||
#endif
|
1381
kdecore/io/kzip.cpp
1381
kdecore/io/kzip.cpp
File diff suppressed because it is too large
Load diff
|
@ -1,220 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2 as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifndef KZIP_H
|
||||
#define KZIP_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
class KZipFileEntry;
|
||||
/**
|
||||
* A class for reading / writing zip archives.
|
||||
*
|
||||
* You can use it in QIODevice::ReadOnly or in QIODevice::WriteOnly mode, and it
|
||||
* behaves just as expected.
|
||||
* It can also be used in QIODevice::ReadWrite mode, in this case one can
|
||||
* append files to an existing zip archive. When you append new files, which
|
||||
* are not yet in the zip, it works as expected, i.e. the files are appended at the end.
|
||||
* When you append a file, which is already in the file, the reference to the
|
||||
* old file is dropped and the new one is added to the zip - but the
|
||||
* old data from the file itself is not deleted, it is still in the
|
||||
* zipfile. So when you want to have a small and garbage-free zipfile,
|
||||
* just read the contents of the appended zip file and write it to a new one
|
||||
* in QIODevice::WriteOnly mode. This is especially important when you don't want
|
||||
* to leak information of how intermediate versions of files in the zip
|
||||
* were looking.
|
||||
*
|
||||
* For more information on the zip fileformat go to
|
||||
* http://www.pkware.com/products/enterprise/white_papers/appnote.html
|
||||
* @author Holger Schroeder <holger-kde@holgis.net>
|
||||
*/
|
||||
class KDECORE_EXPORT KZip : public KArchive
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename.
|
||||
* using the compression filter associated to given mimetype.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/holger/myfile.zip")
|
||||
*/
|
||||
KZip( const QString& filename );
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KFilterDev) or not (QFile, etc.).
|
||||
* @warning Do not assume that giving a QFile here will decompress the file,
|
||||
* in case it's compressed!
|
||||
* @param dev the device to access
|
||||
*/
|
||||
KZip( QIODevice * dev );
|
||||
|
||||
/**
|
||||
* If the zip file is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
virtual ~KZip();
|
||||
|
||||
/**
|
||||
* Describes the contents of the "extra field" for a given file in the Zip archive.
|
||||
*/
|
||||
enum ExtraField { NoExtraField = 0, ///< No extra field
|
||||
ModificationTime = 1, ///< Modification time ("extended timestamp" header)
|
||||
DefaultExtraField = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Call this before writeFile or prepareWriting, to define what the next
|
||||
* file to be written should have in its extra field.
|
||||
* @param ef the type of "extra field"
|
||||
* @see extraField()
|
||||
*/
|
||||
void setExtraField( ExtraField ef );
|
||||
|
||||
/**
|
||||
* The current type of "extra field" that will be used for new files.
|
||||
* @return the current type of "extra field"
|
||||
* @see setExtraField()
|
||||
*/
|
||||
ExtraField extraField() const;
|
||||
|
||||
/**
|
||||
* Describes the compression type for a given file in the Zip archive.
|
||||
*/
|
||||
enum Compression { NoCompression = 0, ///< Uncompressed.
|
||||
DeflateCompression = 1 ///< Deflate compression method.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Call this before writeFile or prepareWriting, to define whether the next
|
||||
* files to be written should be compressed or not.
|
||||
* @param c the new compression mode
|
||||
* @see compression()
|
||||
*/
|
||||
void setCompression( Compression c );
|
||||
|
||||
/**
|
||||
* The current compression mode that will be used for new files.
|
||||
* @return the current compression mode
|
||||
* @see setCompression()
|
||||
*/
|
||||
Compression compression() const;
|
||||
|
||||
/**
|
||||
* Write data to a file that has been created using prepareWriting().
|
||||
* @param data a pointer to the data
|
||||
* @param size the size of the chunk
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
virtual bool writeData( const char* data, qint64 size );
|
||||
|
||||
protected:
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doWriteSymLink(const QString &name, const QString &target,
|
||||
const QString &user, const QString &group,
|
||||
mode_t perm, time_t atime, time_t mtime, time_t ctime);
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doPrepareWriting( const QString& name, const QString& user,
|
||||
const QString& group, qint64 size, mode_t perm,
|
||||
time_t atime, time_t mtime, time_t ctime );
|
||||
|
||||
/**
|
||||
* Write data to a file that has been created using prepareWriting().
|
||||
* @param size the size of the file
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
virtual bool doFinishWriting( qint64 size );
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
* @param mode the mode of the file
|
||||
*/
|
||||
virtual bool openArchive( QIODevice::OpenMode mode );
|
||||
|
||||
/// Closes the archive
|
||||
virtual bool closeArchive();
|
||||
|
||||
/// Reimplemented from KArchive
|
||||
virtual bool doWriteDir( const QString& name, const QString& user,
|
||||
const QString& group, mode_t perm, time_t atime,
|
||||
time_t mtime, time_t ctime );
|
||||
|
||||
private:
|
||||
class KZipPrivate;
|
||||
KZipPrivate * const d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A KZipFileEntry represents an file in a zip archive.
|
||||
*/
|
||||
class KDECORE_EXPORT KZipFileEntry : public KArchiveFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new zip file entry. Do not call this, KZip takes care of it.
|
||||
*/
|
||||
KZipFileEntry( KZip* zip, const QString& name, int access, int date,
|
||||
const QString& user, const QString& group, const QString& symlink,
|
||||
const QString& path, qint64 start, qint64 uncompressedSize,
|
||||
int encoding, qint64 compressedSize);
|
||||
|
||||
/**
|
||||
* Destructor. Do not call this.
|
||||
*/
|
||||
~KZipFileEntry();
|
||||
|
||||
int encoding() const;
|
||||
qint64 compressedSize() const;
|
||||
|
||||
/// Only used when writing
|
||||
void setCompressedSize(qint64 compressedSize);
|
||||
|
||||
/// Header start: only used when writing
|
||||
void setHeaderStart(qint64 headerstart);
|
||||
qint64 headerStart() const;
|
||||
|
||||
/// CRC: only used when writing
|
||||
unsigned long crc32() const;
|
||||
void setCRC32(unsigned long crc32);
|
||||
|
||||
/// Name with complete path - KArchiveFile::name() is the filename only (no path)
|
||||
const QString &path() const;
|
||||
|
||||
/**
|
||||
* @return the content of this file.
|
||||
* Call data() with care (only once per file), this data isn't cached.
|
||||
*/
|
||||
virtual QByteArray data() const;
|
||||
|
||||
/**
|
||||
* This method returns a QIODevice to read the file contents.
|
||||
* This is obviously for reading only.
|
||||
* Note that the ownership of the device is being transferred to the caller,
|
||||
* who will have to delete it.
|
||||
* The returned device auto-opens (in readonly mode), no need to open it.
|
||||
*/
|
||||
virtual QIODevice* createDevice() const;
|
||||
|
||||
private:
|
||||
class KZipFileEntryPrivate;
|
||||
KZipFileEntryPrivate * const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -138,9 +138,6 @@
|
|||
7031 KImageIO
|
||||
7032 kio (KUrlCompletion)
|
||||
7033 KFileMetaInfo
|
||||
7040 KZip
|
||||
7041 KTar
|
||||
7042 KAr
|
||||
7043 kio (bookmarks)
|
||||
7044 kio (AccessManager)
|
||||
|
||||
|
@ -193,6 +190,9 @@
|
|||
# kspeech
|
||||
51008 kspeech
|
||||
|
||||
# karchive
|
||||
51009 karchive
|
||||
|
||||
# kdemultimedia
|
||||
67100 kmix
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ ENDMACRO(KDECORE_EXECUTABLE_TESTS)
|
|||
########### next target ###############
|
||||
|
||||
KDECORE_UNIT_TESTS(
|
||||
# FIXME: due to issues with kgzipfilter the test bellow is disabled as it
|
||||
# causes a hang
|
||||
# karchivetest
|
||||
klocaletimeformattest
|
||||
klocalizedstringtest
|
||||
kmountpointtest
|
||||
|
@ -65,8 +62,6 @@ KDECORE_UNIT_TESTS(
|
|||
)
|
||||
|
||||
KDECORE_EXECUTABLE_TESTS(
|
||||
ktartest
|
||||
kziptest
|
||||
kdebugtest
|
||||
kcmdlineargstest
|
||||
dbuscalltest
|
||||
|
@ -95,11 +90,6 @@ set(kdatetimeformattertest_SRCS kdatetimeformattertest.cpp ../date/kdatetimeform
|
|||
kde4_add_test(kdecore-kdatetimeformattertest ${kdatetimeformattertest_SRCS})
|
||||
target_link_libraries(kdecore-kdatetimeformattertest ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY} )
|
||||
|
||||
########### klimitediodevicetest ###############
|
||||
|
||||
kde4_add_test(kdecore-klimitediodevicetest klimitediodevicetest.cpp ../io/klimitediodevice.cpp)
|
||||
target_link_libraries(kdecore-klimitediodevicetest ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY})
|
||||
|
||||
########### kmimetypetest ###############
|
||||
|
||||
# compile kmimemagicrule.cpp into the test since it's not exported and we call match().
|
||||
|
|
Binary file not shown.
|
@ -1,933 +0,0 @@
|
|||
/* This file is part of the KDE project
|
||||
Copyright (C) 2006, 2010 David Faure <faure@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <config-compression.h>
|
||||
|
||||
#include "karchivetest.h"
|
||||
#include <kmimetype.h>
|
||||
#include "moc_karchivetest.cpp"
|
||||
#include <ktar.h>
|
||||
#include <kzip.h>
|
||||
|
||||
#include <qtest_kde.h>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <kdebug.h>
|
||||
#include <kfilterdev.h>
|
||||
#include <ktempdir.h>
|
||||
#include <unistd.h> // symlink
|
||||
#include <errno.h>
|
||||
|
||||
QTEST_KDEMAIN_CORE( KArchiveTest )
|
||||
|
||||
static const int SIZE1 = 100;
|
||||
|
||||
/**
|
||||
* Writes test fileset specified archive
|
||||
* @param archive archive
|
||||
*/
|
||||
static void writeTestFilesToArchive( KArchive* archive )
|
||||
{
|
||||
QVERIFY( archive->writeFile( "empty", "weis", "users", "", 0 ) );
|
||||
QVERIFY( archive->writeFile( "test1", "weis", "users", "Hallo", 5, 0100440 ) );
|
||||
// Now let's try with the prepareWriting/writeData/finishWriting API
|
||||
QVERIFY( archive->prepareWriting( "test2", "weis", "users", 8 ) );
|
||||
QVERIFY( archive->writeData( "Hallo ", 6 ) );
|
||||
QVERIFY( archive->writeData( "Du", 2 ) );
|
||||
QVERIFY( archive->finishWriting( 8 ) );
|
||||
// Add local file
|
||||
QFile localFile( "test3" );
|
||||
QVERIFY( localFile.open( QIODevice::WriteOnly ) );
|
||||
QVERIFY( localFile.write( "Noch so einer", 13 ) == 13 );
|
||||
localFile.close();
|
||||
QVERIFY( archive->addLocalFile( "test3", "z/test3" ) );
|
||||
|
||||
// writeFile API
|
||||
QVERIFY( archive->writeFile( "my/dir/test3", "dfaure", "hackers", "I do not speak German\nDavid.", 29 ) );
|
||||
|
||||
// Now a medium file : 100 null bytes
|
||||
char medium[ SIZE1 ];
|
||||
memset( medium, 0, SIZE1 );
|
||||
QVERIFY( archive->writeFile( "mediumfile", "user", "group", medium, SIZE1 ) );
|
||||
// Another one, with an absolute path
|
||||
QVERIFY( archive->writeFile( "/dir/subdir/mediumfile2", "user", "group", medium, SIZE1 ) );
|
||||
|
||||
// Now a huge file : 20000 null bytes
|
||||
int n = 20000;
|
||||
char * huge = new char[ n ];
|
||||
memset( huge, 0, n );
|
||||
QVERIFY( archive->writeFile( "hugefile", "user", "group", huge, n ) );
|
||||
delete [] huge;
|
||||
|
||||
// Now an empty directory
|
||||
QVERIFY( archive->writeDir( "aaaemptydir", "user", "group" ) );
|
||||
|
||||
// Add local symlink
|
||||
QVERIFY( archive->addLocalFile( "test3_symlink", "z/test3_symlink") );
|
||||
}
|
||||
|
||||
enum { WithUserGroup = 1 }; // ListingFlags
|
||||
|
||||
static QStringList recursiveListEntries( const KArchiveDirectory * dir, const QString & path, int listingFlags )
|
||||
{
|
||||
QStringList ret;
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
Q_FOREACH(const QString& it, l) {
|
||||
const KArchiveEntry* entry = dir->entry(it);
|
||||
|
||||
QString descr;
|
||||
descr += QString("mode=") + QString::number( entry->permissions(), 8 ) + ' ';
|
||||
if ( listingFlags & WithUserGroup )
|
||||
{
|
||||
descr += QString("user=") + entry->user() + ' ';
|
||||
descr += QString("group=") + entry->group() + ' ';
|
||||
}
|
||||
descr += QString("path=") + path+(it) + ' ';
|
||||
descr += QString("type=") + ( entry->isDirectory() ? "dir" : "file" );
|
||||
if ( entry->isFile() )
|
||||
descr += QString(" size=") + QString::number( static_cast<const KArchiveFile *>(entry)->size() );
|
||||
if (!entry->symLinkTarget().isEmpty())
|
||||
descr += QString(" symlink=") + entry->symLinkTarget();
|
||||
|
||||
// TODO add date and time
|
||||
|
||||
//kDebug() << descr;
|
||||
ret.append( descr );
|
||||
|
||||
if (entry->isDirectory())
|
||||
ret += recursiveListEntries( (KArchiveDirectory *)entry, path+it+'/', listingFlags );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies contents of specified archive against test fileset
|
||||
* @param archive archive
|
||||
*/
|
||||
static void testFileData( KArchive* archive )
|
||||
{
|
||||
const KArchiveDirectory* dir = archive->directory();
|
||||
|
||||
const KArchiveEntry* e = dir->entry( "z/test3" );
|
||||
QVERIFY( e );
|
||||
QVERIFY( e->isFile() );
|
||||
const KArchiveFile* f = static_cast<const KArchiveFile*>( e );
|
||||
QByteArray arr( f->data() );
|
||||
QCOMPARE( arr.size(), 13 );
|
||||
QCOMPARE( arr, QByteArray( "Noch so einer" ) );
|
||||
|
||||
// Now test using createDevice()
|
||||
QIODevice *dev = f->createDevice();
|
||||
QByteArray contents = dev->readAll();
|
||||
QCOMPARE( contents, arr );
|
||||
delete dev;
|
||||
|
||||
dev = f->createDevice();
|
||||
contents = dev->read(5); // test reading in two chunks
|
||||
QCOMPARE(contents.size(), 5);
|
||||
contents += dev->read(50);
|
||||
QCOMPARE(contents.size(), 13);
|
||||
QCOMPARE( QString::fromLatin1(contents), QString::fromLatin1(arr) );
|
||||
delete dev;
|
||||
|
||||
e = dir->entry( "mediumfile" );
|
||||
QVERIFY( e && e->isFile() );
|
||||
f = (KArchiveFile*)e;
|
||||
QCOMPARE( f->data().size(), SIZE1 );
|
||||
|
||||
e = dir->entry( "hugefile" );
|
||||
QVERIFY( e && e->isFile() );
|
||||
f = (KArchiveFile*)e;
|
||||
QCOMPARE( f->data().size(), 20000 );
|
||||
|
||||
e = dir->entry( "aaaemptydir" );
|
||||
QVERIFY( e && e->isDirectory() );
|
||||
|
||||
e = dir->entry( "my/dir/test3" );
|
||||
QVERIFY( e && e->isFile() );
|
||||
f = (KArchiveFile*)e;
|
||||
dev = f->createDevice();
|
||||
QByteArray firstLine = dev->readLine();
|
||||
QCOMPARE(QString::fromLatin1(firstLine), QString::fromLatin1("I do not speak German\n"));
|
||||
QByteArray secondLine = dev->read(100);
|
||||
QCOMPARE(QString::fromLatin1(secondLine), QString::fromLatin1("David."));
|
||||
delete dev;
|
||||
e = dir->entry( "z/test3_symlink" );
|
||||
QVERIFY(e);
|
||||
QVERIFY(e->isFile());
|
||||
QCOMPARE(e->symLinkTarget(), QString("test3"));
|
||||
|
||||
// Test "./" prefix for KOffice (xlink:href="./ObjectReplacements/Object 1")
|
||||
e = dir->entry( "./hugefile" );
|
||||
QVERIFY( e && e->isFile() );
|
||||
e = dir->entry( "./my/dir/test3" );
|
||||
QVERIFY( e && e->isFile() );
|
||||
|
||||
// Test directory entries
|
||||
e = dir->entry( "my" );
|
||||
QVERIFY(e && e->isDirectory());
|
||||
e = dir->entry( "my/" );
|
||||
QVERIFY(e && e->isDirectory());
|
||||
e = dir->entry( "./my/" );
|
||||
QVERIFY(e && e->isDirectory());
|
||||
}
|
||||
|
||||
static void testReadWrite( KArchive* archive )
|
||||
{
|
||||
QVERIFY(archive->writeFile("newfile", "dfaure", "users", "New File", 8, 0100440));
|
||||
}
|
||||
|
||||
static void testCopyTo( KArchive* archive )
|
||||
{
|
||||
const KArchiveDirectory* dir = archive->directory();
|
||||
KTempDir tmpDir;
|
||||
const QString dirName = tmpDir.name();
|
||||
|
||||
dir->copyTo( dirName );
|
||||
|
||||
QVERIFY(QFile::exists(dirName+"dir"));
|
||||
QVERIFY(QFileInfo(dirName+"dir").isDir());
|
||||
|
||||
QFileInfo fileInfo1(dirName+"dir/subdir/mediumfile2");
|
||||
QVERIFY(fileInfo1.exists());
|
||||
QVERIFY(fileInfo1.isFile());
|
||||
QCOMPARE(fileInfo1.size(), Q_INT64_C(100));
|
||||
|
||||
QFileInfo fileInfo2(dirName+"hugefile");
|
||||
QVERIFY(fileInfo2.exists());
|
||||
QVERIFY(fileInfo2.isFile());
|
||||
QCOMPARE(fileInfo2.size(), Q_INT64_C(20000));
|
||||
|
||||
QFileInfo fileInfo3(dirName+"mediumfile");
|
||||
QVERIFY(fileInfo3.exists());
|
||||
QVERIFY(fileInfo3.isFile());
|
||||
QCOMPARE(fileInfo3.size(), Q_INT64_C(100));
|
||||
|
||||
QFileInfo fileInfo4(dirName+"my/dir/test3");
|
||||
QVERIFY(fileInfo4.exists());
|
||||
QVERIFY(fileInfo4.isFile());
|
||||
QCOMPARE(fileInfo4.size(), Q_INT64_C(29));
|
||||
|
||||
const QString fileName = dirName+"z/test3_symlink";
|
||||
const QFileInfo fileInfo5(fileName);
|
||||
QVERIFY(fileInfo5.exists());
|
||||
QVERIFY(fileInfo5.isFile());
|
||||
// Do not use fileInfo.readLink() for unix symlinks
|
||||
// It returns the -full- path to the target, while we want the target string "as is".
|
||||
QString symLinkTarget;
|
||||
const QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
QByteArray s;
|
||||
#if defined(PATH_MAX)
|
||||
s.resize(PATH_MAX+1);
|
||||
#else
|
||||
int path_max = pathconf(encodedFileName.data(), _PC_PATH_MAX);
|
||||
if (path_max <= 0) {
|
||||
path_max = 4096;
|
||||
}
|
||||
s.resize(path_max);
|
||||
#endif
|
||||
int len = readlink(encodedFileName.data(), s.data(), s.size() - 1);
|
||||
if ( len >= 0 ) {
|
||||
s[len] = '\0';
|
||||
symLinkTarget = QFile::decodeName(s);
|
||||
}
|
||||
QCOMPARE(symLinkTarget, QString("test3"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares dataset for archive filter tests
|
||||
*/
|
||||
void KArchiveTest::setupData()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::addColumn<QString>("mimeType");
|
||||
|
||||
QTest::newRow(".tar.gz") << "karchivetest.tar.gz" << "application/x-gzip";
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
QTest::newRow(".tar.bz2") << "karchivetest.tar.bz2" << "application/x-bzip";
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
QTest::newRow(".tar.lzma") << "karchivetest.tar.lzma" << "application/x-lzma";
|
||||
QTest::newRow(".tar.xz") << "karchivetest.tar.xz" << "application/x-xz";
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QTest::initTestCase()
|
||||
*/
|
||||
void KArchiveTest::initTestCase()
|
||||
{
|
||||
// Prepare local symlink
|
||||
QFile::remove("test3_symlink");
|
||||
if (::symlink("test3", "test3_symlink") != 0) {
|
||||
qDebug() << errno;
|
||||
QVERIFY(false);
|
||||
}
|
||||
|
||||
// For better benchmarks: initialize KMimeTypeFactory magic here
|
||||
KMimeType::findByContent(QByteArray("hello"));
|
||||
}
|
||||
|
||||
void KArchiveTest::testCreateTar_data()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::newRow(".tar") << "karchivetest.tar";
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider testCreateTar_data
|
||||
*/
|
||||
void KArchiveTest::testCreateTar()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
|
||||
// With tempfile: 0.7-0.8 ms, 994236 instr. loads
|
||||
// Without tempfile: 0.81 ms, 992541 instr. loads
|
||||
// Note: use ./karchivetest 2>&1 | grep ms
|
||||
// to avoid being slowed down by the kDebugs.
|
||||
QBENCHMARK {
|
||||
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::WriteOnly));
|
||||
|
||||
writeTestFilesToArchive(&tar);
|
||||
|
||||
QVERIFY(tar.close());
|
||||
|
||||
QFileInfo fileInfo(QFile::encodeName(fileName));
|
||||
QVERIFY(fileInfo.exists());
|
||||
// We can't check for an exact size because of the addLocalFile, whose data is system-dependent
|
||||
QVERIFY(fileInfo.size() > 450);
|
||||
|
||||
}
|
||||
|
||||
// NOTE The only .tar test, cleanup here
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testCreateTarXXX()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
|
||||
// With tempfile: 1.3-1.7 ms, 2555089 instr. loads
|
||||
// Without tempfile: 0.87 ms, 987915 instr. loads
|
||||
QBENCHMARK {
|
||||
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::WriteOnly));
|
||||
|
||||
writeTestFilesToArchive(&tar);
|
||||
|
||||
QVERIFY(tar.close());
|
||||
|
||||
QFileInfo fileInfo(QFile::encodeName(fileName));
|
||||
QVERIFY(fileInfo.exists());
|
||||
// We can't check for an exact size because of the addLocalFile, whose data is system-dependent
|
||||
QVERIFY(fileInfo.size() > 350);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testReadTar() // testCreateTarGz must have been run first.
|
||||
{
|
||||
QFETCH( QString, fileName );
|
||||
|
||||
// 1.6-1.7 ms per interaction, 2908428 instruction loads
|
||||
// After the "no tempfile when writing fix" this went down
|
||||
// to 0.9-1.0 ms, 1689059 instruction loads.
|
||||
// I guess it finds the data in the kernel cache now that no KTempFile is
|
||||
// used when writing.
|
||||
QBENCHMARK {
|
||||
|
||||
KTar tar( fileName );
|
||||
|
||||
QVERIFY( tar.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
const QStringList listing = recursiveListEntries( dir, "", WithUserGroup );
|
||||
|
||||
QFileInfo localFileData("test3");
|
||||
|
||||
const QString owner = localFileData.owner();
|
||||
const QString group = localFileData.group();
|
||||
|
||||
QCOMPARE( listing.count(), 15 );
|
||||
QCOMPARE( listing[ 0], QString("mode=40755 user=user group=group path=aaaemptydir type=dir") );
|
||||
QCOMPARE( listing[ 1], QString("mode=40777 user=%1 group=%2 path=dir type=dir").arg(owner, group) );
|
||||
QCOMPARE( listing[ 2], QString("mode=40777 user=%1 group=%2 path=dir/subdir type=dir").arg(owner,group) );
|
||||
QCOMPARE( listing[ 3], QString("mode=100644 user=user group=group path=dir/subdir/mediumfile2 type=file size=100") );
|
||||
QCOMPARE( listing[ 4], QString("mode=100644 user=weis group=users path=empty type=file size=0") );
|
||||
QCOMPARE( listing[ 5], QString("mode=100644 user=user group=group path=hugefile type=file size=20000") );
|
||||
QCOMPARE( listing[ 6], QString("mode=100644 user=user group=group path=mediumfile type=file size=100") );
|
||||
QCOMPARE( listing[ 7], QString("mode=40777 user=%1 group=%2 path=my type=dir").arg(owner, group) );
|
||||
QCOMPARE( listing[ 8], QString("mode=40777 user=%1 group=%2 path=my/dir type=dir").arg(owner, group) );
|
||||
QCOMPARE( listing[ 9], QString("mode=100644 user=dfaure group=hackers path=my/dir/test3 type=file size=29") );
|
||||
QCOMPARE( listing[10], QString("mode=100440 user=weis group=users path=test1 type=file size=5") );
|
||||
QCOMPARE( listing[11], QString("mode=100644 user=weis group=users path=test2 type=file size=8") );
|
||||
QCOMPARE( listing[12], QString("mode=40777 user=%1 group=%2 path=z type=dir").arg(owner, group) );
|
||||
// This one was added with addLocalFile, so ignore mode/user/group.
|
||||
QString str = listing[13];
|
||||
str.replace(QRegExp("mode.*path"), "path" );
|
||||
QCOMPARE( str, QString("path=z/test3 type=file size=13") );
|
||||
str = listing[14];
|
||||
str.replace(QRegExp("mode.*path"), "path" );
|
||||
QCOMPARE( str, QString("path=z/test3_symlink type=file size=0 symlink=test3") );
|
||||
|
||||
QVERIFY( tar.close() );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests the decompression using kfilterdev, basically.
|
||||
* To debug KTarPrivate::fillTempFile().
|
||||
*
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testUncompress()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
QFETCH(QString, mimeType);
|
||||
|
||||
// testCreateTar must have been run first.
|
||||
QVERIFY(QFile::exists(fileName));
|
||||
QIODevice *filterDev = KFilterDev::deviceForFile(fileName, mimeType, true);
|
||||
QVERIFY(filterDev);
|
||||
QByteArray buffer;
|
||||
buffer.resize(8*1024);
|
||||
kDebug() << "buffer.size()=" << buffer.size();
|
||||
QVERIFY(filterDev->open(QIODevice::ReadOnly));
|
||||
|
||||
qint64 totalSize = 0;
|
||||
qint64 len = -1;
|
||||
while (!filterDev->atEnd() && len != 0) {
|
||||
len = filterDev->read(buffer.data(), buffer.size());
|
||||
QVERIFY(len >= 0);
|
||||
totalSize += len;
|
||||
// kDebug() << "read len=" << len << " totalSize=" << totalSize;
|
||||
}
|
||||
filterDev->close();
|
||||
delete filterDev;
|
||||
// kDebug() << "totalSize=" << totalSize;
|
||||
QVERIFY(totalSize > 26000); // 27648 here when using gunzip
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testTarFileData()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
|
||||
// testCreateTar must have been run first.
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::ReadOnly));
|
||||
|
||||
testFileData(&tar);
|
||||
|
||||
QVERIFY(tar.close());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testTarCopyTo()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
|
||||
// testCreateTar must have been run first.
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::ReadOnly));
|
||||
|
||||
testCopyTo(&tar);
|
||||
|
||||
QVERIFY(tar.close());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setupData
|
||||
*/
|
||||
void KArchiveTest::testTarReadWrite()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
|
||||
// testCreateTar must have been run first.
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::ReadWrite));
|
||||
|
||||
testReadWrite(&tar);
|
||||
testFileData(&tar);
|
||||
|
||||
QVERIFY(tar.close());
|
||||
|
||||
// Reopen it and check it
|
||||
{
|
||||
KTar tar(fileName);
|
||||
QVERIFY(tar.open(QIODevice::ReadOnly));
|
||||
testFileData( &tar );
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
const KArchiveEntry* e = dir->entry("newfile");
|
||||
QVERIFY(e && e->isFile());
|
||||
const KArchiveFile* f = (KArchiveFile*)e;
|
||||
QCOMPARE(f->data().size(), 8);
|
||||
}
|
||||
|
||||
// NOTE This is the last test for this dataset. so cleanup here
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
|
||||
void KArchiveTest::testTarMaxLength_data()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::newRow("maxlength.tar.gz") << "karchivetest-maxlength.tar.gz";
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider testTarMaxLength_data
|
||||
*/
|
||||
void KArchiveTest::testTarMaxLength()
|
||||
{
|
||||
QFETCH( QString, fileName );
|
||||
|
||||
KTar tar( fileName );
|
||||
|
||||
QVERIFY( tar.open( QIODevice::WriteOnly ) );
|
||||
|
||||
// Generate long filenames of each possible length bigger than 98...
|
||||
// Also exceed 512 byte block size limit to see how well the ././@LongLink
|
||||
// implementation fares
|
||||
for (int i = 98; i < 514 ; i++ )
|
||||
{
|
||||
QString str, num;
|
||||
str.fill( 'a', i-10 );
|
||||
num.setNum( i );
|
||||
num = num.rightJustified( 10, '0' );
|
||||
tar.writeFile( str+num, "testu", "testg", "hum", 3 );
|
||||
}
|
||||
// Result of this test : works perfectly now (failed at 482 formerly and
|
||||
// before that at 154).
|
||||
QVERIFY( tar.close() );
|
||||
|
||||
QVERIFY( tar.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
const QStringList listing = recursiveListEntries( dir, "", WithUserGroup );
|
||||
|
||||
QCOMPARE( listing[ 0], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3") );
|
||||
QCOMPARE( listing[ 3], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3") );
|
||||
QCOMPARE( listing[ 4], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3") );
|
||||
|
||||
QCOMPARE( listing.count(), 416 );
|
||||
|
||||
QVERIFY( tar.close() );
|
||||
|
||||
// NOTE Cleanup here
|
||||
QFile::remove( fileName );
|
||||
}
|
||||
|
||||
void KArchiveTest::testTarGlobalHeader()
|
||||
{
|
||||
KTar tar( QString::fromLatin1(KDESRCDIR) + QLatin1String( "global_header_test.tar.bz2" ) );
|
||||
QVERIFY( tar.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
const QStringList listing = recursiveListEntries( dir, "", WithUserGroup );
|
||||
|
||||
QCOMPARE( listing[ 0], QString("mode=40775 user=root group=root path=Test type=dir") );
|
||||
QCOMPARE( listing[ 1], QString("mode=664 user=root group=root path=Test/test.txt type=file size=0") );
|
||||
|
||||
QCOMPARE( listing.count(), 2 );
|
||||
|
||||
QVERIFY( tar.close() );
|
||||
}
|
||||
|
||||
|
||||
void KArchiveTest::testTarPrefix()
|
||||
{
|
||||
KTar tar( QString::fromLatin1(KDESRCDIR) + QLatin1String( "tar_prefix_test.tar.bz2" ) );
|
||||
QVERIFY( tar.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
const QStringList listing = recursiveListEntries( dir, "", WithUserGroup );
|
||||
|
||||
QCOMPARE( listing[ 0], QString("mode=40775 user=root group=root path=Test type=dir") );
|
||||
QCOMPARE( listing[ 1], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7 type=dir") );
|
||||
QCOMPARE( listing[ 2], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples type=dir") );
|
||||
QCOMPARE( listing[ 3], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator type=dir") );
|
||||
QCOMPARE( listing[ 4], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original type=dir") );
|
||||
QCOMPARE( listing[ 5], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java type=dir") );
|
||||
QCOMPARE( listing[ 6], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com type=dir") );
|
||||
QCOMPARE( listing[ 7], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech type=dir") );
|
||||
QCOMPARE( listing[ 8], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples type=dir") );
|
||||
QCOMPARE( listing[ 9], QString("mode=664 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples/GeneratorExample.html type=file size=43086") );
|
||||
|
||||
QCOMPARE( listing.count(), 10 );
|
||||
|
||||
QVERIFY( tar.close() );
|
||||
}
|
||||
|
||||
void KArchiveTest::testTarDirectoryForgotten()
|
||||
{
|
||||
KTar tar( QString::fromLatin1(KDESRCDIR) + QLatin1String( "tar_directory_forgotten.tar.bz2" ) );
|
||||
QVERIFY( tar.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
const QStringList listing = recursiveListEntries( dir, "", WithUserGroup );
|
||||
|
||||
QVERIFY( listing[9].contains("trolltech/examples/generator") );
|
||||
QVERIFY( listing[10].contains("trolltech/examples/generator/GeneratorExample.html") );
|
||||
|
||||
QCOMPARE( listing.count(), 11 );
|
||||
|
||||
QVERIFY( tar.close() );
|
||||
}
|
||||
|
||||
void KArchiveTest::testTarRootDir() // bug 309463
|
||||
{
|
||||
KTar tar(QString::fromLatin1(KDESRCDIR) + QLatin1String("tar_rootdir.tar.bz2"));
|
||||
QVERIFY(tar.open(QIODevice::ReadOnly));
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
const QStringList listing = recursiveListEntries(dir, "", WithUserGroup);
|
||||
qDebug() << listing.join("\n");
|
||||
|
||||
QVERIFY(listing[0].contains("%{APPNAME}.cpp"));
|
||||
QVERIFY(listing[1].contains("%{APPNAME}.h"));
|
||||
QVERIFY(listing[5].contains("main.cpp"));
|
||||
|
||||
QCOMPARE(listing.count(), 10);
|
||||
}
|
||||
|
||||
void KArchiveTest::testTarDirectoryTwice() // bug 206994
|
||||
{
|
||||
KTar tar(QString::fromLatin1(KDESRCDIR) + QLatin1String("tar_directory_twice.tar.bz2"));
|
||||
QVERIFY(tar.open(QIODevice::ReadOnly));
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
const QStringList listing = recursiveListEntries(dir, "", WithUserGroup);
|
||||
qDebug() << listing.join("\n");
|
||||
|
||||
QVERIFY(listing[0].contains("path=d"));
|
||||
QVERIFY(listing[1].contains("path=d/f1.txt"));
|
||||
QVERIFY(listing[2].contains("path=d/f2.txt"));
|
||||
|
||||
QCOMPARE(listing.count(), 3);
|
||||
}
|
||||
///
|
||||
|
||||
static const char s_zipFileName[] = "karchivetest.zip";
|
||||
static const char s_zipMaxLengthFileName[] = "karchivetest-maxlength.zip";
|
||||
static const char s_zipLocaleFileName[] = "karchivetest-locale.zip";
|
||||
static const char s_zipMimeType[] = "application/vnd.oasis.opendocument.text";
|
||||
|
||||
void KArchiveTest::testCreateZip()
|
||||
{
|
||||
KZip zip( s_zipFileName );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::WriteOnly ) );
|
||||
|
||||
zip.setExtraField( KZip::NoExtraField );
|
||||
|
||||
zip.setCompression( KZip::NoCompression );
|
||||
QByteArray zipMimeType( s_zipMimeType );
|
||||
zip.writeFile( "mimetype", "", "", zipMimeType.data(), zipMimeType.size() );
|
||||
zip.setCompression( KZip::DeflateCompression );
|
||||
|
||||
writeTestFilesToArchive( &zip );
|
||||
|
||||
QVERIFY( zip.close() );
|
||||
|
||||
QFile zipFile( QFile::encodeName( s_zipFileName ) );
|
||||
QFileInfo fileInfo( zipFile );
|
||||
QVERIFY( fileInfo.exists() );
|
||||
QVERIFY( fileInfo.size() > 300 );
|
||||
|
||||
// Check that the header with no-compression and no-extrafield worked.
|
||||
// (This is for the "magic" for koffice documents)
|
||||
QVERIFY( zipFile.open( QIODevice::ReadOnly ) );
|
||||
QByteArray arr = zipFile.read( 4 );
|
||||
QCOMPARE( arr, QByteArray( "PK\003\004" ) );
|
||||
QVERIFY( zipFile.seek( 30 ) );
|
||||
arr = zipFile.read( 8 );
|
||||
QCOMPARE( arr, QByteArray( "mimetype" ) );
|
||||
arr = zipFile.read( zipMimeType.size() );
|
||||
QCOMPARE( arr, zipMimeType );
|
||||
}
|
||||
|
||||
void KArchiveTest::testCreateZipError()
|
||||
{
|
||||
// Giving a directory name to kzip must give an error case in close(), see #136630.
|
||||
// Otherwise we just lose data.
|
||||
KZip zip(QDir::currentPath());
|
||||
|
||||
QVERIFY(zip.open(QIODevice::WriteOnly));
|
||||
|
||||
writeTestFilesToArchive(&zip);
|
||||
|
||||
// try to add something as a file that is no file
|
||||
QVERIFY( !zip.addLocalFile( QDir::currentPath(), "bogusdir" ) );
|
||||
|
||||
QVERIFY(!zip.close());
|
||||
}
|
||||
|
||||
void KArchiveTest::testReadZipError()
|
||||
{
|
||||
QFile brokenZip( "broken.zip" );
|
||||
QVERIFY( brokenZip.open( QIODevice::WriteOnly ) );
|
||||
|
||||
// incomplete magic
|
||||
brokenZip.write( QByteArray( "PK\003" ) );
|
||||
|
||||
brokenZip.close();
|
||||
|
||||
KZip zip( "broken.zip" );
|
||||
|
||||
QVERIFY( !zip.open(QIODevice::ReadOnly) );
|
||||
|
||||
QVERIFY( brokenZip.open( QIODevice::WriteOnly | QIODevice::Append ) );
|
||||
|
||||
// add rest of magic, but still incomplete header
|
||||
brokenZip.write( QByteArray( "\004\000\000\000\000" ) );
|
||||
|
||||
brokenZip.close();
|
||||
|
||||
QVERIFY( !zip.open(QIODevice::ReadOnly) );
|
||||
|
||||
QVERIFY( brokenZip.remove() );
|
||||
}
|
||||
|
||||
void KArchiveTest::testReadZip()
|
||||
{
|
||||
// testCreateZip must have been run first.
|
||||
KZip zip( s_zipFileName );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
|
||||
// ZIP has no support for per-file user/group, so omit them from the listing
|
||||
const QStringList listing = recursiveListEntries( dir, "", 0 );
|
||||
|
||||
QCOMPARE( listing.count(), 16 );
|
||||
QCOMPARE( listing[ 0], QString("mode=40755 path=aaaemptydir type=dir") );
|
||||
QCOMPARE( listing[ 1], QString("mode=40777 path=dir type=dir") );
|
||||
QCOMPARE( listing[ 2], QString("mode=40777 path=dir/subdir type=dir") );
|
||||
QCOMPARE( listing[ 3], QString("mode=100644 path=dir/subdir/mediumfile2 type=file size=100") );
|
||||
QCOMPARE( listing[ 4], QString("mode=100644 path=empty type=file size=0") );
|
||||
QCOMPARE( listing[ 5], QString("mode=100644 path=hugefile type=file size=20000") );
|
||||
QCOMPARE( listing[ 6], QString("mode=100644 path=mediumfile type=file size=100") );
|
||||
QCOMPARE( listing[ 7], QString("mode=100644 path=mimetype type=file size=%1").arg(strlen(s_zipMimeType)) );
|
||||
QCOMPARE( listing[ 8], QString("mode=40777 path=my type=dir") );
|
||||
QCOMPARE( listing[ 9], QString("mode=40777 path=my/dir type=dir") );
|
||||
QCOMPARE( listing[10], QString("mode=100644 path=my/dir/test3 type=file size=29") );
|
||||
QCOMPARE( listing[11], QString("mode=100440 path=test1 type=file size=5") );
|
||||
QCOMPARE( listing[12], QString("mode=100644 path=test2 type=file size=8") );
|
||||
QCOMPARE( listing[13], QString("mode=40777 path=z type=dir") );
|
||||
// This one was added with addLocalFile, so ignore mode
|
||||
QString str = listing[14];
|
||||
str.replace(QRegExp("mode.*path"), "path" );
|
||||
QCOMPARE( str, QString("path=z/test3 type=file size=13") );
|
||||
str = listing[15];
|
||||
str.replace(QRegExp("mode.*path"), "path" );
|
||||
QCOMPARE( str, QString("path=z/test3_symlink type=file size=5 symlink=test3") );
|
||||
|
||||
QVERIFY( zip.close() );
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipFileData()
|
||||
{
|
||||
// testCreateZip must have been run first.
|
||||
KZip zip(s_zipFileName);
|
||||
QVERIFY(zip.open( QIODevice::ReadOnly));
|
||||
|
||||
testFileData(&zip);
|
||||
|
||||
QVERIFY(zip.close());
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipCopyTo()
|
||||
{
|
||||
// testCreateZip must have been run first.
|
||||
KZip zip(s_zipFileName);
|
||||
QVERIFY(zip.open(QIODevice::ReadOnly));
|
||||
|
||||
testCopyTo(&zip);
|
||||
|
||||
QVERIFY(zip.close());
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipMaxLength()
|
||||
{
|
||||
KZip zip( s_zipMaxLengthFileName );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::WriteOnly ) );
|
||||
|
||||
// Similar to testTarMaxLength just to make sure, but of course zip doesn't have
|
||||
// those limitations in the first place.
|
||||
for (int i = 98; i < 514 ; i++ )
|
||||
{
|
||||
QString str, num;
|
||||
str.fill( 'a', i-10 );
|
||||
num.setNum( i );
|
||||
num = num.rightJustified( 10, '0' );
|
||||
zip.writeFile( str+num, "testu", "testg", "hum", 3 );
|
||||
}
|
||||
QVERIFY( zip.close() );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
const QStringList listing = recursiveListEntries( dir, "", 0 );
|
||||
|
||||
QCOMPARE( listing[ 0], QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3") );
|
||||
QCOMPARE( listing[ 3], QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3") );
|
||||
QCOMPARE( listing[ 4], QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3") );
|
||||
|
||||
QCOMPARE( listing.count(), 514 - 98 );
|
||||
|
||||
QVERIFY( zip.close() );
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipWithNonLatinFileNames()
|
||||
{
|
||||
KZip zip( s_zipLocaleFileName );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::WriteOnly ) );
|
||||
|
||||
const QByteArray fileData("Test of data with a russian file name");
|
||||
const QString fileName = QString::fromUtf8( "Архитектура.okular" );
|
||||
const QString recodedFileName = QFile::decodeName( QFile::encodeName( fileName ) );
|
||||
QVERIFY( zip.writeFile( fileName, "pino", "users", fileData.constData(), fileData.size() ) );
|
||||
|
||||
QVERIFY( zip.close() );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
const QStringList listing = recursiveListEntries( dir, "", 0 );
|
||||
|
||||
QCOMPARE( listing.count(), 1 );
|
||||
QCOMPARE( listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(recodedFileName).arg(fileData.size()) );
|
||||
|
||||
const KArchiveFile* fileEntry = static_cast< const KArchiveFile* >( dir->entry( dir->entries()[0] ) );
|
||||
QCOMPARE( fileEntry->data(), fileData );
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipWithOverwrittenFileName()
|
||||
{
|
||||
KZip zip( s_zipFileName );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::WriteOnly ) );
|
||||
|
||||
const QByteArray fileData1("There could be a fire, if there is smoke.");
|
||||
const QString fileName = QLatin1String("wisdom");
|
||||
QVERIFY( zip.writeFile( fileName, "konqi", "dragons", fileData1.constData(), fileData1.size() ) );
|
||||
|
||||
// now overwrite it
|
||||
const QByteArray fileData2("If there is smoke, there could be a fire.");
|
||||
QVERIFY( zip.writeFile( fileName, "konqi", "dragons", fileData2.constData(), fileData2.size() ) );
|
||||
|
||||
QVERIFY( zip.close() );
|
||||
|
||||
QVERIFY( zip.open( QIODevice::ReadOnly ) );
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
QVERIFY( dir != 0 );
|
||||
const QStringList listing = recursiveListEntries( dir, "", 0 );
|
||||
|
||||
QCOMPARE( listing.count(), 1 );
|
||||
QCOMPARE( listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(fileName).arg(fileData2.size()) );
|
||||
|
||||
const KArchiveFile* fileEntry = static_cast< const KArchiveFile* >( dir->entry( dir->entries()[0] ) );
|
||||
QCOMPARE( fileEntry->data(), fileData2 );
|
||||
}
|
||||
|
||||
static bool writeFile(const QString& dirName, const QString& fileName, const QByteArray& data)
|
||||
{
|
||||
Q_ASSERT(dirName.endsWith('/'));
|
||||
QFile file(dirName + fileName);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return false;
|
||||
file.write(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void KArchiveTest::testZipAddLocalDirectory()
|
||||
{
|
||||
// Prepare local dir
|
||||
KTempDir tmpDir;
|
||||
const QString dirName = tmpDir.name();
|
||||
|
||||
const QByteArray file1Data = "Hello Shantanu";
|
||||
const QString file1 = QLatin1String("file1");
|
||||
QVERIFY(writeFile(dirName, file1, file1Data));
|
||||
|
||||
{
|
||||
KZip zip(s_zipFileName);
|
||||
|
||||
QVERIFY(zip.open(QIODevice::WriteOnly));
|
||||
QVERIFY(zip.addLocalDirectory(dirName, "."));
|
||||
QVERIFY(zip.close());
|
||||
}
|
||||
{
|
||||
KZip zip(s_zipFileName);
|
||||
|
||||
QVERIFY(zip.open(QIODevice::ReadOnly));
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
QVERIFY(dir != 0);
|
||||
|
||||
const KArchiveEntry* e = dir->entry(file1);
|
||||
QVERIFY(e && e->isFile());
|
||||
const KArchiveFile* f = (KArchiveFile*)e;
|
||||
QCOMPARE(f->data(), file1Data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QTest::cleanupTestCase()
|
||||
*/
|
||||
void KArchiveTest::cleanupTestCase()
|
||||
{
|
||||
QFile::remove(s_zipMaxLengthFileName);
|
||||
QFile::remove(s_zipFileName);
|
||||
QFile::remove(s_zipLocaleFileName);
|
||||
QFile::remove("test3_symlink");
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* This file is part of the KDE project
|
||||
Copyright (C) 2006 David Faure <faure@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KARCHIVETEST_H
|
||||
#define KARCHIVETEST_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class KArchiveTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void setupData();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testCreateTar_data();
|
||||
void testCreateTar();
|
||||
void testCreateTarXXX_data(){ setupData(); };
|
||||
void testCreateTarXXX();
|
||||
void testReadTar_data(){ setupData(); };
|
||||
void testReadTar();
|
||||
void testUncompress_data(){ setupData(); };
|
||||
void testUncompress();
|
||||
void testTarFileData_data(){ setupData(); };
|
||||
void testTarFileData();
|
||||
void testTarCopyTo_data(){ setupData(); };
|
||||
void testTarCopyTo();
|
||||
void testTarReadWrite_data(){ setupData(); };
|
||||
void testTarReadWrite();
|
||||
void testTarMaxLength_data();
|
||||
void testTarMaxLength();
|
||||
void testTarGlobalHeader();
|
||||
void testTarPrefix();
|
||||
void testTarDirectoryForgotten();
|
||||
void testTarRootDir();
|
||||
void testTarDirectoryTwice();
|
||||
|
||||
void testCreateZip();
|
||||
void testCreateZipError();
|
||||
void testReadZipError();
|
||||
void testReadZip();
|
||||
void testZipFileData();
|
||||
void testZipCopyTo();
|
||||
void testZipMaxLength();
|
||||
void testZipWithNonLatinFileNames();
|
||||
void testZipWithOverwrittenFileName();
|
||||
void testZipAddLocalDirectory();
|
||||
|
||||
void cleanupTestCase();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,79 +0,0 @@
|
|||
/* This file is part of the KDE project
|
||||
Copyright (C) 2009 Pino Toscano <pino@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "klimitediodevicetest.h"
|
||||
|
||||
#include "klimitediodevice_p.h"
|
||||
|
||||
#include <qtest_kde.h>
|
||||
|
||||
QTEST_KDEMAIN_CORE(KLimitedIODeviceTest)
|
||||
|
||||
void KLimitedIODeviceTest::addChunk(const QByteArray &chunk)
|
||||
{
|
||||
ChunkData cd;
|
||||
cd.data = chunk;
|
||||
cd.offset = m_chunks.isEmpty() ? 0 : m_chunks.last().offset + m_chunks.last().data.size();
|
||||
m_chunks.append(cd);
|
||||
m_data.append(chunk);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::initTestCase()
|
||||
{
|
||||
addChunk("Test of string");
|
||||
addChunk("second part of the large buffer");
|
||||
addChunk("... which will be used to test the KLimitedIODevice");
|
||||
|
||||
m_buffer.setBuffer(&m_data);
|
||||
m_buffer.open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testReadChunks_data()
|
||||
{
|
||||
QTest::addColumn<int>("index");
|
||||
|
||||
for (int i = 0; i < m_chunks.count(); ++i) {
|
||||
const ChunkData &d = m_chunks.at(i);
|
||||
QTest::newRow(d.data.constData()) << i;
|
||||
}
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testReadChunks()
|
||||
{
|
||||
QFETCH(int, index);
|
||||
|
||||
const ChunkData &chunk = m_chunks.at(index);
|
||||
|
||||
KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size());
|
||||
QVERIFY(dev.isOpen());
|
||||
QCOMPARE(dev.readAll(), chunk.data);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testSeeking()
|
||||
{
|
||||
const ChunkData &chunk = m_chunks.at(2);
|
||||
|
||||
KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size());
|
||||
QVERIFY(dev.seek(dev.size() - 16));
|
||||
QCOMPARE(dev.readAll(), chunk.data.right(16));
|
||||
QVERIFY(dev.seek(0));
|
||||
QCOMPARE(dev.readAll(), chunk.data);
|
||||
}
|
||||
|
||||
#include "moc_klimitediodevicetest.cpp"
|
|
@ -1,52 +0,0 @@
|
|||
/* This file is part of the KDE project
|
||||
Copyright (C) 2009 Pino Toscano <pino@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KLIMITEDIODEVICETEST_H
|
||||
#define KLIMITEDIODEVICETEST_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QBuffer>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
struct ChunkData
|
||||
{
|
||||
QByteArray data;
|
||||
int offset;
|
||||
};
|
||||
|
||||
class KLimitedIODeviceTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testReadChunks_data();
|
||||
void testReadChunks();
|
||||
void testSeeking();
|
||||
|
||||
private:
|
||||
void addChunk(const QByteArray &chunk);
|
||||
|
||||
QByteArray m_data;
|
||||
QBuffer m_buffer;
|
||||
QList<ChunkData> m_chunks;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2005 David Faure <faure@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License version 2 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ktar.h"
|
||||
#include <stdio.h>
|
||||
#include <kcomponentdata.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
void recursive_print( const KArchiveDirectory * dir, const QString & path )
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
QStringList::ConstIterator it = l.constBegin();
|
||||
for( ; it != l.constEnd(); ++it )
|
||||
{
|
||||
const KArchiveEntry* entry = dir->entry( (*it) );
|
||||
printf("mode=%07o %s %s %s%s %lld isdir=%d\n", entry->permissions(), entry->user().toLatin1().constData(), entry->group().toLatin1().constData(), path.toLatin1().constData(), (*it).toLatin1().constData(),
|
||||
entry->isFile() ? static_cast<const KArchiveFile *>(entry)->size() : 0,
|
||||
entry->isDirectory());
|
||||
if (!entry->symLinkTarget().isEmpty()) {
|
||||
printf(" (symlink to %s)\n",qPrintable(entry->symLinkTarget()));
|
||||
}
|
||||
if (entry->isDirectory())
|
||||
recursive_print( (KArchiveDirectory *)entry, path+(*it)+'/' );
|
||||
}
|
||||
}
|
||||
|
||||
// See karchivetest.cpp for the unittest that covers KTar.
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("\n"
|
||||
" Usage :\n"
|
||||
" ./ktartest /path/to/existing_file.tar.gz tests listing an existing tar.gz\n" );
|
||||
return 1;
|
||||
}
|
||||
KComponentData componentData(QByteArray("ktartest"));
|
||||
KTar tar( argv[1] );
|
||||
|
||||
if ( !tar.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[1] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory* dir = tar.directory();
|
||||
|
||||
//printf("calling recursive_print\n");
|
||||
recursive_print( dir, "" );
|
||||
//printf("recursive_print called\n");
|
||||
|
||||
tar.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,246 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002-2006 David Faure <faure@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License version 2 as published by the Free Software Foundation;
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kzip.h"
|
||||
#include <stdio.h>
|
||||
#include <kcomponentdata.h>
|
||||
#include <kdebug.h>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void recursive_print( const KArchiveDirectory * dir, const QString & path )
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
QStringList::Iterator it = l.begin();
|
||||
for( ; it != l.end(); ++it )
|
||||
{
|
||||
const KArchiveEntry* entry = dir->entry( (*it) );
|
||||
printf("mode=%07o %s %s size: %lld pos: %lld %s%s isdir=%d%s", entry->permissions(),
|
||||
entry->user().toLatin1().constData(), entry->group().toLatin1().constData(),
|
||||
entry->isDirectory() ? 0 : ((KArchiveFile*)entry)->size(),
|
||||
entry->isDirectory() ? 0 : ((KArchiveFile*)entry)->position(),
|
||||
path.toLatin1().constData(), (*it).toLatin1().constData(), entry->isDirectory(),
|
||||
entry->symLinkTarget().isEmpty() ? "" : QString(" symlink: %1").arg(entry->symLinkTarget()).toLatin1().constData() );
|
||||
|
||||
// if (!entry->isDirectory()) printf("%d", ((KArchiveFile*)entry)->size());
|
||||
printf("\n");
|
||||
if (entry->isDirectory())
|
||||
recursive_print( (KArchiveDirectory *)entry, path+(*it)+'/' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void recursive_transfer(const KArchiveDirectory * dir,
|
||||
const QString & path, KZip * zip)
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
QStringList::Iterator it = l.begin();
|
||||
for( ; it != l.end(); ++it )
|
||||
{
|
||||
const KArchiveEntry* e = dir->entry( (*it) );
|
||||
kDebug() << "actual file: " << e->name();
|
||||
if (e->isFile())
|
||||
{
|
||||
Q_ASSERT( e && e->isFile() );
|
||||
const KArchiveFile* f = (KArchiveFile*)e;
|
||||
printf("FILE=%s\n", e->name().toLatin1().constData());
|
||||
|
||||
QByteArray arr( f->data() );
|
||||
printf("SIZE=%i\n",arr.size() );
|
||||
QString str( arr );
|
||||
printf("DATA=%s\n", str.toLatin1().constData());
|
||||
|
||||
if (e->symLinkTarget().isEmpty()) {
|
||||
zip->writeFile( path+e->name().toLatin1().constData(),
|
||||
"holgi", "holgrp",
|
||||
f->data(), arr.size() );
|
||||
} else
|
||||
zip->writeSymLink(path+e->name(), e->symLinkTarget(), "leo", "leo",
|
||||
0120777, 1000000000l, 1000000000l, 1000000000l);
|
||||
}
|
||||
else if (e->isDirectory())
|
||||
{
|
||||
recursive_transfer((KArchiveDirectory *)e ,
|
||||
path+e->name()+'/', zip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
// ###### Note: please consider adding new tests to karchivetest (so that they can be automated)
|
||||
// rather than here (interactive)
|
||||
printf("\n"
|
||||
" Usage :\n"
|
||||
" ./kziptest list /path/to/existing_file.zip tests listing an existing zip\n"
|
||||
" ./kziptest print file.zip prints contents of all files.\n"
|
||||
" ./kziptest print2 file.zip filename prints contents of one file.\n"
|
||||
" ./kziptest update file.zip filename updates contents of one file.\n"
|
||||
" ./kziptest transfer file.zip newfile.zip complete transfer.\n" );
|
||||
return 1;
|
||||
}
|
||||
KComponentData componentData(QByteArray("kziptest"));
|
||||
QString command = argv[1];
|
||||
if ( command == "list" )
|
||||
{
|
||||
KZip zip( argv[2] );
|
||||
|
||||
if ( !zip.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
|
||||
//printf("calling recursive_print\n");
|
||||
recursive_print( dir, "" );
|
||||
//printf("recursive_print called\n");
|
||||
|
||||
zip.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (command == "print" )
|
||||
{
|
||||
KZip zip( argv[2] );
|
||||
kDebug() << "Opening zip file";
|
||||
if ( !zip.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
kDebug() << "Listing toplevel of zip file";
|
||||
QStringList l = dir->entries();
|
||||
QStringList::Iterator it = l.begin();
|
||||
for( ; it != l.end(); ++it )
|
||||
{
|
||||
const KArchiveEntry* e = dir->entry( (*it) );
|
||||
kDebug() << "Printing " << (*it);
|
||||
if (e->isFile())
|
||||
{
|
||||
Q_ASSERT( e && e->isFile() );
|
||||
const KArchiveFile* f = (KArchiveFile*)e;
|
||||
|
||||
QByteArray arr( f->data() );
|
||||
printf("SIZE=%i\n",arr.size() );
|
||||
QString str( arr );
|
||||
printf("DATA=%s\n", str.toLatin1().constData());
|
||||
}
|
||||
}
|
||||
zip.close();
|
||||
return 0;
|
||||
}
|
||||
else if (command == "print2" )
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
printf("usage: kziptest print2 archivename filename");
|
||||
return 1;
|
||||
}
|
||||
KZip zip( argv[2] );
|
||||
if ( !zip.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory* dir = zip.directory();
|
||||
const KArchiveEntry* e = dir->entry( argv[3] );
|
||||
Q_ASSERT( e && e->isFile() );
|
||||
const KArchiveFile* f = (KArchiveFile*)e;
|
||||
|
||||
QByteArray arr( f->data() );
|
||||
printf("SIZE=%i\n",arr.size() );
|
||||
QString str( arr );
|
||||
// printf("DATA=%s\n", str.toLatin1().constData());
|
||||
printf("%s", str.toLatin1().constData());
|
||||
zip.close();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
else if (command == "update" )
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
printf("usage: kziptest update archivename filename");
|
||||
return 1;
|
||||
}
|
||||
KZip zip( argv[2] );
|
||||
if ( !zip.open( QIODevice::ReadWrite ) )
|
||||
{
|
||||
printf("Could not open %s for read/write\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
// const KArchiveEntry* e = zip.directory()->entry( argv[3] );
|
||||
// Q_ASSERT( e && e->isFile() );
|
||||
// const KArchiveFile* f = (KArchiveFile*)e;
|
||||
|
||||
// QCString data( "This is some new data that goes into " );
|
||||
// data += argv[3];
|
||||
QFile f ( argv[3] );
|
||||
if (!f.open( QIODevice::ReadOnly ))
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
QDataStream s( &f );
|
||||
|
||||
zip.writeFile( argv[3], "", "", f.readAll(), f.size() );
|
||||
zip.close();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
else if (command == "transfer" )
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
printf("usage: kziptest transfer sourcefile destfile");
|
||||
return 1;
|
||||
}
|
||||
KZip zip1( argv[2] );
|
||||
KZip zip2( argv[3] );
|
||||
if ( !zip1.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
printf("Could not open %s for reading\n", argv[2] );
|
||||
return 1;
|
||||
}
|
||||
if ( !zip2.open( QIODevice::WriteOnly ) )
|
||||
{
|
||||
printf("Could not open %s for writing\n", argv[3] );
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory* dir1 = zip1.directory();
|
||||
|
||||
recursive_transfer(dir1, "", &zip2 );
|
||||
|
||||
zip1.close();
|
||||
zip2.close();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
else
|
||||
printf("Unknown command\n");
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -14,6 +14,7 @@ add_subdirectory(kpowermanager)
|
|||
add_subdirectory(kdnssd)
|
||||
add_subdirectory(khttp)
|
||||
add_subdirectory(kspeech)
|
||||
add_subdirectory(karchive)
|
||||
|
||||
######## kidletime ####################
|
||||
|
||||
|
|
45
kutils/karchive/CMakeLists.txt
Normal file
45
kutils/karchive/CMakeLists.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
if(LibArchive_FOUND)
|
||||
include_directories(${LibArchive_INCLUDE_DIRS})
|
||||
add_definitions(-DHAVE_LIBARCHIVE)
|
||||
endif()
|
||||
|
||||
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=51009)
|
||||
|
||||
set(karchive_LIB_SRCS
|
||||
karchive.cpp
|
||||
)
|
||||
|
||||
add_library(karchive ${LIBRARY_TYPE} ${karchive_LIB_SRCS})
|
||||
|
||||
target_link_libraries(karchive PUBLIC
|
||||
${KDE4_KDECORE_LIBS}
|
||||
)
|
||||
|
||||
if(LibArchive_FOUND)
|
||||
target_link_libraries(karchive PRIVATE ${LibArchive_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_target_properties(karchive PROPERTIES
|
||||
VERSION ${GENERIC_LIB_VERSION}
|
||||
SOVERSION ${GENERIC_LIB_SOVERSION}
|
||||
)
|
||||
|
||||
generate_export_header(karchive)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/karchive_export.h
|
||||
karchive.h
|
||||
DESTINATION ${KDE4_INCLUDE_INSTALL_DIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS karchive
|
||||
EXPORT kdelibsLibraryTargets
|
||||
${INSTALL_TARGETS_DEFAULT_ARGS}
|
||||
)
|
||||
|
||||
if(ENABLE_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
1116
kutils/karchive/karchive.cpp
Normal file
1116
kutils/karchive/karchive.cpp
Normal file
File diff suppressed because it is too large
Load diff
163
kutils/karchive/karchive.h
Normal file
163
kutils/karchive/karchive.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2018 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KARCHIVE_H
|
||||
#define KARCHIVE_H
|
||||
|
||||
#include "karchive_export.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*!
|
||||
Archive entry information holder, valid object is obtained via @p KArchive::entry
|
||||
|
||||
@note It is up to the programmer to keep the integrity of the structure
|
||||
@ingroup Types
|
||||
|
||||
@see KArchive
|
||||
@since 4.22
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchiveEntry
|
||||
{
|
||||
public:
|
||||
KArchiveEntry();
|
||||
|
||||
bool encrypted;
|
||||
qint64 size;
|
||||
qint64 gid;
|
||||
qint64 uid;
|
||||
mode_t mode;
|
||||
time_t atime;
|
||||
time_t ctime;
|
||||
time_t mtime;
|
||||
QByteArray hardlink;
|
||||
QByteArray symlink;
|
||||
QByteArray pathname;
|
||||
QByteArray groupname;
|
||||
QByteArray username;
|
||||
|
||||
//! @brief Returns if the entry is valid or not
|
||||
bool isNull() const;
|
||||
|
||||
//! @brief Fancy encrypted for the purpose of widgets
|
||||
QString fancyEncrypted() const;
|
||||
//! @brief Fancy size for the purpose of widgets
|
||||
QString fancySize() const;
|
||||
//! @brief Fancy mode for the purpose of widgets
|
||||
QString fancyMode() const;
|
||||
//! @brief Fancy access time for the purpose of widgets
|
||||
QString fancyATime() const;
|
||||
//! @brief Fancy creation time for the purpose of widgets
|
||||
QString fancyCTime() const;
|
||||
//! @brief Fancy modification time for the purpose of widgets
|
||||
QString fancyMTime() const;
|
||||
//! @brief Fancy type for the purpose of widgets
|
||||
QString fancyType() const;
|
||||
};
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
KARCHIVE_EXPORT QDebug operator<<(QDebug, const KArchiveEntry &entry);
|
||||
#endif
|
||||
QT_BEGIN_NAMESPACE
|
||||
Q_DECLARE_TYPEINFO(KArchiveEntry, Q_PRIMITIVE_TYPE);
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class KArchivePrivate;
|
||||
|
||||
|
||||
/*!
|
||||
Class for archives management.
|
||||
|
||||
Example:
|
||||
\code
|
||||
KArchiveManager archive("/home/joe/archive.tar.gz");
|
||||
kDebug() << archive.list();
|
||||
|
||||
QDir::mkpath("/tmp/destination");
|
||||
archive.extract("dir/in/archive/", "/tmp/destination");
|
||||
archive.delete("file/in/archive.txt");
|
||||
\endcode
|
||||
|
||||
@note Paths ending with "/" will be considered as directories
|
||||
@warning The operations are done on temporary file, copy of the orignal, which after
|
||||
successfull operation (add or remove) replaces the orignal thus if it is interrupted the
|
||||
source may get corrupted
|
||||
|
||||
@see KArchiveEntry
|
||||
@since 4.22
|
||||
@warning the API is subject to change
|
||||
|
||||
@todo set permissions on file after copy, same as the original
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchive
|
||||
{
|
||||
public:
|
||||
KArchive(const QString &path);
|
||||
~KArchive();
|
||||
|
||||
static bool isSupported();
|
||||
|
||||
/*!
|
||||
@brief Add paths to the archive
|
||||
@param strip string to remove from the start of every path
|
||||
@param destination relative path where paths should be added to
|
||||
*/
|
||||
bool add(const QStringList &paths, const QByteArray &strip = "/", const QByteArray &destination = "") const;
|
||||
//! @brief Remove paths from the archive
|
||||
bool remove(const QStringList &paths) const;
|
||||
/*!
|
||||
@brief Extract paths to destination
|
||||
@param destination existing directory, you can use @p QDir::mkpath(QString)
|
||||
@param preserve preserve advanced attributes (ACL/ATTR)
|
||||
*/
|
||||
bool extract(const QStringList &paths, const QString &destination, bool preserve = true) const;
|
||||
|
||||
/*!
|
||||
@brief List the content of the archive
|
||||
@param path filter, anything not starting with @p path will not be listed
|
||||
@note May return empty list on both failure and success
|
||||
@note Some formats list directories, some do not
|
||||
*/
|
||||
QList<KArchiveEntry> list(const QString &path = QString()) const;
|
||||
|
||||
//! @brief Get entry information for path in archive
|
||||
KArchiveEntry entry(const QString &path) const;
|
||||
|
||||
//! @brief Get data for path in archive
|
||||
QByteArray data(const QString &path) const;
|
||||
|
||||
//! @brief Returns if path is readable archive
|
||||
bool isReadable() const;
|
||||
//! @brief Returns if path is writable archive
|
||||
bool isWritable() const;
|
||||
|
||||
//! @brief Returns human-readable description of the error that occured
|
||||
QString errorString() const;
|
||||
|
||||
//! @brief Returns list of archive MIME types that are readable
|
||||
static QStringList readableMimeTypes();
|
||||
//! @brief Returns list of archive MIME types that are writable
|
||||
static QStringList writableMimeTypes();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KArchive);
|
||||
KArchivePrivate* const d;
|
||||
};
|
||||
|
||||
#endif // KARCHIVE_H
|
9
kutils/karchive/tests/CMakeLists.txt
Normal file
9
kutils/karchive/tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_BINARY_DIR}/..
|
||||
)
|
||||
|
||||
kde4_add_test(karchive-karchivetest
|
||||
karchivetest.cpp
|
||||
)
|
||||
target_link_libraries(karchive-karchivetest ${KDE4_KARCHIVE_LIBS} ${QT_QTTEST_LIBRARY})
|
207
kutils/karchive/tests/karchivetest.cpp
Normal file
207
kutils/karchive/tests/karchivetest.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "qtest_kde.h"
|
||||
#include "karchive.h"
|
||||
#include "ktemporaryfile.h"
|
||||
#include "ktempdir.h"
|
||||
#include "kdebug.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
static QString tmpName(const QString &archiveext)
|
||||
{
|
||||
KTemporaryFile ktempfile;
|
||||
ktempfile.setSuffix(archiveext);
|
||||
ktempfile.open();
|
||||
return ktempfile.fileName();
|
||||
}
|
||||
|
||||
static QString tmpCopy(const QString &archivepath)
|
||||
{
|
||||
const QString tmpdir = QFile::encodeName(KDEBINDIR);
|
||||
const QString tmpbase = QFileInfo(archivepath).fileName();
|
||||
const QString tmpcopy = QString::fromLatin1("%1/%2").arg(tmpdir).arg(tmpbase);
|
||||
QFile::remove(tmpcopy);
|
||||
QFile::copy(archivepath, tmpcopy);
|
||||
return tmpcopy;
|
||||
}
|
||||
|
||||
class KArchiveTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void list_data();
|
||||
void list();
|
||||
|
||||
void add_data();
|
||||
void add();
|
||||
|
||||
void remove_data();
|
||||
void remove();
|
||||
|
||||
void error_data();
|
||||
void error();
|
||||
};
|
||||
|
||||
QTEST_KDEMAIN_CORE(KArchiveTest)
|
||||
|
||||
void KArchiveTest::initTestCase()
|
||||
{
|
||||
if (!KArchive::isSupported()) {
|
||||
QSKIP("Built without LibArchive", SkipAll);
|
||||
}
|
||||
}
|
||||
|
||||
void KArchiveTest::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void KArchiveTest::list_data()
|
||||
{
|
||||
QTest::addColumn<QString>("archivepath");
|
||||
QTest::newRow(".tar.gz") << QFile::decodeName(KDESRCDIR "/tests.tar.gz");
|
||||
QTest::newRow(".zip") << QFile::decodeName(KDESRCDIR "/tests.zip");
|
||||
}
|
||||
|
||||
void KArchiveTest::list()
|
||||
{
|
||||
QFETCH(QString, archivepath);
|
||||
|
||||
KArchive karchive(archivepath);
|
||||
QVERIFY(karchive.isReadable());
|
||||
QList<KArchiveEntry> karchiveentries = karchive.list();
|
||||
QCOMPARE(karchiveentries.size(), 3);
|
||||
QCOMPARE(karchiveentries.at(0).pathname, QByteArray("tests/"));
|
||||
QVERIFY(S_ISDIR(karchiveentries.at(0).mode));
|
||||
QCOMPARE(karchiveentries.at(1).pathname, QByteArray("tests/CMakeLists.txt"));
|
||||
QVERIFY(S_ISREG(karchiveentries.at(1).mode));
|
||||
}
|
||||
|
||||
void KArchiveTest::add_data()
|
||||
{
|
||||
QTest::addColumn<QString>("archiveext");
|
||||
QTest::newRow(".tar.gz") << ".tar.gz";
|
||||
QTest::newRow(".zip") << ".zip";
|
||||
}
|
||||
|
||||
void KArchiveTest::add()
|
||||
{
|
||||
QFETCH(QString, archiveext);
|
||||
|
||||
{
|
||||
KArchive karchive(tmpName(archiveext));
|
||||
QVERIFY(karchive.isWritable());
|
||||
QStringList toadd = QStringList()
|
||||
<< QFile::decodeName(KDESRCDIR "/CMakeLists.txt");
|
||||
QVERIFY(karchive.add(toadd));
|
||||
QList<KArchiveEntry> karchiveentries = karchive.list();
|
||||
QCOMPARE(karchiveentries.size(), 1);
|
||||
QCOMPARE(karchiveentries.at(0).pathname, QByteArray(KDESRCDIR "CMakeLists.txt").mid(1));
|
||||
QVERIFY(S_ISREG(karchiveentries.at(0).mode));
|
||||
}
|
||||
|
||||
{
|
||||
KArchive karchive(tmpName(archiveext));
|
||||
QVERIFY(karchive.isWritable());
|
||||
QStringList toadd = QStringList()
|
||||
<< QFile::decodeName(KDESRCDIR "/CMakeLists.txt");
|
||||
QVERIFY(karchive.add(toadd, QByteArray(), QByteArray("dir")));
|
||||
QList<KArchiveEntry> karchiveentries = karchive.list();
|
||||
QCOMPARE(karchiveentries.size(), 1);
|
||||
QCOMPARE(karchiveentries.at(0).pathname, QByteArray("dir" KDESRCDIR "CMakeLists.txt"));
|
||||
QVERIFY(S_ISREG(karchiveentries.at(0).mode));
|
||||
}
|
||||
}
|
||||
|
||||
void KArchiveTest::remove_data()
|
||||
{
|
||||
QTest::addColumn<QString>("archivepath");
|
||||
QTest::newRow(".tar.gz") << QFile::decodeName(KDESRCDIR "/tests.tar.gz");
|
||||
QTest::newRow(".zip") << QFile::decodeName(KDESRCDIR "/tests.zip");
|
||||
}
|
||||
|
||||
void KArchiveTest::remove()
|
||||
{
|
||||
QFETCH(QString, archivepath);
|
||||
|
||||
KArchive karchive(tmpCopy(archivepath));
|
||||
QVERIFY(karchive.isReadable());
|
||||
QVERIFY(karchive.isWritable());
|
||||
QList<KArchiveEntry> karchiveentries = karchive.list();
|
||||
QCOMPARE(karchiveentries.size(), 3);
|
||||
QStringList toremove = QStringList()
|
||||
<< QFile::decodeName("tests/CMakeLists.txt");
|
||||
QVERIFY(karchive.remove(toremove));
|
||||
QList<KArchiveEntry> karchiveentries2 = karchive.list();
|
||||
QCOMPARE(karchiveentries2.size(), 2);
|
||||
QCOMPARE(karchiveentries2.at(0).pathname, QByteArray("tests/"));
|
||||
QVERIFY(S_ISDIR(karchiveentries2.at(0).mode));
|
||||
QCOMPARE(karchiveentries2.at(1).pathname, QByteArray("tests/karchivetest.cpp"));
|
||||
QVERIFY(S_ISREG(karchiveentries2.at(1).mode));
|
||||
}
|
||||
|
||||
void KArchiveTest::error_data()
|
||||
{
|
||||
QTest::addColumn<QString>("archivepath");
|
||||
QTest::addColumn<QString>("expectederror");
|
||||
QTest::addColumn<bool>("add");
|
||||
QTest::addColumn<bool>("remove");
|
||||
QTest::addColumn<bool>("extract");
|
||||
QTest::addColumn<QStringList>("pathslist");
|
||||
QTest::newRow("empty")
|
||||
<< QFile::decodeName(KDESRCDIR "/tests.tar.gz")
|
||||
<< QString()
|
||||
<< true << false << false
|
||||
<< QStringList();
|
||||
QTest::newRow("add_does_not_exist")
|
||||
<< QFile::decodeName(KDESRCDIR "/tests123.zip")
|
||||
<< QString::fromLatin1("lstat: No such file or directory")
|
||||
<< true << false << false
|
||||
<< (QStringList() << "does_not_exist");
|
||||
}
|
||||
|
||||
void KArchiveTest::error()
|
||||
{
|
||||
QFETCH(QString, archivepath);
|
||||
QFETCH(QString, expectederror);
|
||||
QFETCH(bool, add);
|
||||
QFETCH(bool, remove);
|
||||
QFETCH(bool, extract);
|
||||
QFETCH(QStringList, pathslist);
|
||||
|
||||
KArchive karchive(archivepath);
|
||||
QCOMPARE(karchive.errorString(), QString());
|
||||
if (add) {
|
||||
karchive.add(pathslist);
|
||||
QCOMPARE(karchive.errorString(), expectederror);
|
||||
} else if (remove) {
|
||||
karchive.remove(pathslist);
|
||||
QCOMPARE(karchive.errorString(), expectederror);
|
||||
} else if (extract) {
|
||||
KTempDir ktempdir;
|
||||
QVERIFY(ktempdir.exists());
|
||||
karchive.extract(pathslist, ktempdir.name());
|
||||
QCOMPARE(karchive.errorString(), expectederror);
|
||||
}
|
||||
}
|
||||
|
||||
#include "karchivetest.moc"
|
BIN
kutils/karchive/tests/tests.tar.gz
Normal file
BIN
kutils/karchive/tests/tests.tar.gz
Normal file
Binary file not shown.
BIN
kutils/karchive/tests/tests.zip
Normal file
BIN
kutils/karchive/tests/tests.zip
Normal file
Binary file not shown.
|
@ -30,7 +30,6 @@
|
|||
#include <kmimetype.h>
|
||||
#include <kplugininfo.h>
|
||||
#include <kstandarddirs.h>
|
||||
#include <ktar.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
#include "packagemetadata.h"
|
||||
|
|
|
@ -35,11 +35,8 @@
|
|||
#include <kmimetype.h>
|
||||
#include <kstandarddirs.h>
|
||||
#include <kservicetypetrader.h>
|
||||
#include <ktar.h>
|
||||
#include <ktemporaryfile.h>
|
||||
#include <ktempdir.h>
|
||||
#include <kurl.h>
|
||||
#include <kzip.h>
|
||||
|
||||
#include "package.h"
|
||||
#include "private/packages_p.h"
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#include <kdebug.h>
|
||||
#include <ktemporaryfile.h>
|
||||
#include <kzip.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <kzip.h>
|
||||
#include <kstandarddirs.h>
|
||||
|
||||
#include "plasma/applet.h"
|
||||
|
|
Loading…
Add table
Reference in a new issue