summaryrefslogtreecommitdiffstats
path: root/tdeio/tdeio/ktar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdeio/tdeio/ktar.cpp')
-rw-r--r--tdeio/tdeio/ktar.cpp980
1 files changed, 980 insertions, 0 deletions
diff --git a/tdeio/tdeio/ktar.cpp b/tdeio/tdeio/ktar.cpp
new file mode 100644
index 000000000..9bde2873a
--- /dev/null
+++ b/tdeio/tdeio/ktar.cpp
@@ -0,0 +1,980 @@
+/* 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 <stdio.h>
+#include <stdlib.h> // strtol
+#include <time.h> // time()
+/*#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>*/
+#include <assert.h>
+
+#include <tqcstring.h>
+#include <tqdir.h>
+#include <tqfile.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <ktempfile.h>
+
+#include <kfilterdev.h>
+#include <kfilterbase.h>
+
+#include "ktar.h"
+#include <kstandarddirs.h>
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// KTar ///////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+class KTar::KTarPrivate
+{
+public:
+ KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {}
+ TQStringList dirList;
+ int tarEnd;
+ KTempFile* tmpFile;
+ TQString mimetype;
+ TQCString origFileName;
+
+ bool fillTempFile(const TQString & filename);
+ bool writeBackTempFile( const TQString & filename );
+};
+
+KTar::KTar( const TQString& filename, const TQString & _mimetype )
+ : KArchive( 0 )
+{
+ m_filename = filename;
+ d = new KTarPrivate;
+ TQString mimetype( _mimetype );
+ bool forced = true;
+ if ( mimetype.isEmpty() ) // Find out mimetype manually
+ {
+ if ( TQFile::exists( filename ) )
+ mimetype = KMimeType::findByFileContent( filename )->name();
+ else
+ mimetype = KMimeType::findByPath( filename, 0, true )->name();
+ kdDebug(7041) << "KTar::KTar mimetype = " << mimetype << endl;
+
+ // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
+ if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
+ mimetype == "application/x-webarchive" )
+ {
+ // that's a gzipped tar file, so ask for gzip filter
+ mimetype = "application/x-gzip";
+ }
+ else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter
+ {
+ mimetype = "application/x-bzip2";
+ }
+ else
+ {
+ // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
+ TQFile file( filename );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ unsigned char firstByte = file.getch();
+ unsigned char secondByte = file.getch();
+ unsigned char thirdByte = file.getch();
+ if ( firstByte == 0037 && secondByte == 0213 )
+ mimetype = "application/x-gzip";
+ else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' )
+ mimetype = "application/x-bzip2";
+ else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 )
+ {
+ unsigned char fourthByte = file.getch();
+ if ( fourthByte == 4 )
+ mimetype = "application/x-zip";
+ }
+ else if ( firstByte == 0xfd && secondByte == '7' && thirdByte == 'z' )
+ {
+ unsigned char fourthByte = file.getch();
+ unsigned char fifthByte = file.getch();
+ unsigned char sixthByte = file.getch();
+ if ( fourthByte == 'X' && fifthByte == 'Z' && sixthByte == 0x00 )
+ mimetype = "application/x-xz";
+ }
+ else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 )
+ {
+ unsigned char fourthByte = file.getch();
+ if ( fourthByte == 0x80 )
+ mimetype = "application/x-lzma";
+ }
+ }
+ file.close();
+ }
+ forced = false;
+ }
+ d->mimetype = mimetype;
+
+ prepareDevice( filename, mimetype, forced );
+}
+
+void KTar::prepareDevice( const TQString & filename,
+ const TQString & mimetype, bool /*forced*/ )
+{
+ if( "application/x-tar" == mimetype )
+ setDevice( TQT_TQIODEVICE(new TQFile( filename )) );
+ else
+ {
+ // The compression filters are very slow with random access.
+ // So instead of applying the filter to the device,
+ // the file is completly 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.
+ d->tmpFile = new KTempFile(locateLocal("tmp", "ktar-"),".tar");
+ kdDebug( 7041 ) << "KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
+ d->tmpFile->setAutoDelete(true);
+
+ // KTempFile opens the file automatically,
+ // the device must be closed, however, for KArchive.setDevice()
+ TQFile* file = d->tmpFile->file();
+ file->close();
+ setDevice(TQT_TQIODEVICE(file));
+ }
+}
+
+KTar::KTar( TQIODevice * dev )
+ : KArchive( dev )
+{
+ Q_ASSERT( dev );
+ d = new KTarPrivate;
+}
+
+KTar::~KTar()
+{
+ // mjarrett: Closes to prevent ~KArchive from aborting w/o device
+ if( isOpened() )
+ close();
+
+ if (d->tmpFile)
+ delete d->tmpFile; // will delete the device
+ else if ( !m_filename.isEmpty() )
+ delete device(); // we created it ourselves
+
+
+ delete d;
+}
+
+void KTar::setOrigFileName( const TQCString & fileName )
+{
+ if ( !isOpened() || !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
+ return;
+ }
+ d->origFileName = fileName;
+}
+
+TQ_LONG KTar::readRawHeader(char *buffer) {
+ // Read header
+ TQ_LONG n = device()->readBlock( buffer, 0x200 );
+ if ( n == 0x200 && buffer[0] != 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?
+ TQCString s;
+
+ 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 * ' ';
+
+ s.sprintf("%o", check );
+
+ // 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() ) ) {
+ kdWarning(7041) << "KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl;
+ 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::readLonglink(char *buffer,TQCString &longlink) {
+ TQ_LONG n = 0;
+ TQIODevice *dev = device();
+ // read size of longlink from size field in header
+ // size is in bytes including the trailing null (which we ignore)
+ buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
+ char *dummy;
+ const char* p = buffer + 0x7c;
+ while( *p == ' ' ) ++p;
+ int size = (int)strtol( p, &dummy, 8 );
+
+ longlink.resize(size);
+ size--; // ignore trailing null
+ dummy = longlink.data();
+ int offset = 0;
+ while (size > 0) {
+ int chunksize = QMIN(size, 0x200);
+ n = dev->readBlock( dummy + offset, chunksize );
+ if (n == -1) return false;
+ size -= chunksize;
+ offset += 0x200;
+ }/*wend*/
+ // jump over the rest
+ int skip = 0x200 - (n % 0x200);
+ if (skip < 0x200) {
+ if (dev->readBlock(buffer,skip) != skip) return false;
+ }
+ return true;
+}
+
+TQ_LONG KTar::readHeader(char *buffer,TQString &name,TQString &symlink) {
+ name.truncate(0);
+ symlink.truncate(0);
+ while (true) {
+ TQ_LONG n = readRawHeader(buffer);
+ if (n != 0x200) return n;
+
+ // is it a longlink?
+ if (strcmp(buffer,"././@LongLink") == 0) {
+ char typeflag = buffer[0x9c];
+ TQCString longlink;
+ readLonglink(buffer,longlink);
+ switch (typeflag) {
+ case 'L': name = TQFile::decodeName(longlink); break;
+ case 'K': symlink = TQFile::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 = TQFile::decodeName(TQCString(buffer, 101));
+ if (symlink.isEmpty())
+ symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
+
+ 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 TQString & filename) {
+ if ( ! tmpFile )
+ return true;
+
+ kdDebug( 7041 ) <<
+ "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
+ "' ... " << endl;
+
+ bool forced = false;
+ if( "application/x-gzip" == mimetype
+ || "application/x-bzip2" == mimetype
+ || "application/x-lzma" == mimetype
+ || "application/x-xz" == mimetype)
+ forced = true;
+
+ TQIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced );
+
+ if( filterDev ) {
+ TQFile* file = tmpFile->file();
+ file->close();
+ if ( ! file->open( IO_WriteOnly ) )
+ {
+ delete filterDev;
+ return false;
+ }
+ TQByteArray buffer(8*1024);
+ if ( ! filterDev->open( IO_ReadOnly ) )
+ {
+ delete filterDev;
+ return false;
+ }
+ TQ_LONG len = -1;
+ while ( !filterDev->atEnd() && len != 0) {
+ len = filterDev->readBlock(buffer.data(),buffer.size());
+ if ( len < 0 ) { // corrupted archive
+ delete filterDev;
+ return false;
+ }
+ file->writeBlock(buffer.data(),len);
+ }
+ filterDev->close();
+ delete filterDev;
+
+ file->close();
+ if ( ! file->open( IO_ReadOnly ) )
+ return false;
+ }
+ else
+ kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl;
+
+ kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl;
+ return true;
+}
+
+bool KTar::openArchive( int mode )
+{
+ kdDebug( 7041 ) << "KTar::openArchive" << endl;
+ if ( !(mode & IO_ReadOnly) )
+ return true;
+
+ if ( !d->fillTempFile( m_filename ) )
+ return false;
+
+ // We'll use the permission and user/group of d->rootDir
+ // for any directory we emulate (see findOrCreate)
+ //struct stat buf;
+ //stat( m_filename, &buf );
+
+ d->dirList.clear();
+ TQIODevice* dev = device();
+
+ if ( !dev )
+ return false;
+
+ // read dir infos
+ char buffer[ 0x200 ];
+ bool ende = false;
+ do
+ {
+ TQString name;
+ TQString symlink;
+
+ // Read header
+ TQ_LONG n = readHeader(buffer,name,symlink);
+ if (n < 0) return false;
+ if (n == 0x200)
+ {
+ bool isdir = false;
+ TQString nm;
+
+ if ( name.right(1) == "/" )
+ {
+ isdir = true;
+ name = name.left( name.length() - 1 );
+ }
+
+ int pos = name.findRev( '/' );
+ if ( pos == -1 )
+ nm = name;
+ else
+ nm = 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
+ TQString user( buffer + 0x109 );
+ TQString group( 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)
+ // and 'D' for GNU tar extension DUMPDIR
+ if ( typeflag == '5' )
+ isdir = true;
+
+ bool isDumpDir = false;
+ if ( typeflag == 'D' )
+ {
+ isdir = false;
+ isDumpDir = true;
+ }
+ //bool islink = ( typeflag == '1' || typeflag == '2' );
+ //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl;
+
+ if (isdir)
+ access |= S_IFDIR; // f*cking broken tar files
+
+ KArchiveEntry* e;
+ if ( isdir )
+ {
+ //kdDebug(7041) << "KTar::openArchive directory " << nm << endl;
+ e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
+ }
+ else
+ {
+ // read size
+ buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
+ char *dummy;
+ const char* p = buffer + 0x7c;
+ while( *p == ' ' ) ++p;
+ int size = (int)strtol( p, &dummy, 8 );
+
+ // for isDumpDir we will skip the additional info about that dirs contents
+ if ( isDumpDir )
+ {
+ //kdDebug(7041) << "KTar::openArchive " << nm << " isDumpDir" << endl;
+ 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' )
+ {
+ kdDebug(7041) << "HARD LINK, setting size to 0 instead of " << size << endl;
+ size = 0; // no contents
+ }
+
+ //kdDebug(7041) << "KTar::openArchive file " << nm << " size=" << size << endl;
+ e = new KArchiveFile( this, nm, access, time, user, group, symlink,
+ dev->at(), size );
+ }
+
+ // Skip contents + align bytes
+ int rest = size % 0x200;
+ int skip = size + (rest ? 0x200 - rest : 0);
+ //kdDebug(7041) << "KTar::openArchive, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl;
+ if (! dev->at( dev->at() + skip ) )
+ kdWarning(7041) << "KTar::openArchive skipping " << skip << " failed" << endl;
+ }
+
+ if ( pos == -1 )
+ {
+ if ( nm == "." ) // 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 cleanDirPath
+ TQString path = TQDir::cleanDirPath( name.left( pos ) );
+ // Ensure container directory exists, create otherwise
+ KArchiveDirectory * d = findOrCreate( path );
+ d->addEntry( e );
+ }
+ }
+ else
+ {
+ //tqDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
+ d->tarEnd = dev->at() - 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 IO_WriteOnly mode
+ */
+bool KTar::KTarPrivate::writeBackTempFile( const TQString & filename ) {
+ if ( ! tmpFile )
+ return true;
+
+ kdDebug(7041) << "Write temporary file to compressed file" << endl;
+ kdDebug(7041) << filename << " " << mimetype << endl;
+
+ bool forced = false;
+ if( "application/x-gzip" == mimetype
+ || "application/x-bzip2" == mimetype
+ || "application/x-lzma" == mimetype
+ || "application/x-xz" == mimetype)
+ forced = true;
+
+ TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
+ if( dev ) {
+ TQFile* file = tmpFile->file();
+ file->close();
+ if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
+ {
+ file->close();
+ delete dev;
+ return false;
+ }
+ if ( forced )
+ static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
+ TQByteArray buffer(8*1024);
+ TQ_LONG len;
+ while ( ! file->atEnd()) {
+ len = file->readBlock(buffer.data(),buffer.size());
+ dev->writeBlock(buffer.data(),len);
+ }
+ file->close();
+ dev->close();
+ delete dev;
+ }
+
+ kdDebug(7041) << "Write temporary file to compressed file done." << endl;
+ return true;
+}
+
+bool KTar::closeArchive()
+{
+ d->dirList.clear();
+
+ // If we are in write mode and had created
+ // a temporary tar file, we have to write
+ // back the changes to the original file
+ if( mode() == IO_WriteOnly)
+ return d->writeBackTempFile( m_filename );
+
+ return true;
+}
+
+bool KTar::writeDir( const TQString& name, const TQString& user, const TQString& group )
+{
+ mode_t perm = 040755;
+ time_t the_time = time(0);
+ return writeDir(name,user,group,perm,the_time,the_time,the_time);
+#if 0
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./ => call cleanDirPath
+ TQString dirName ( TQDir::cleanDirPath( name ) );
+
+ // Need trailing '/'
+ if ( dirName.right(1) != "/" )
+ dirName += "/";
+
+ if ( d->dirList.contains( dirName ) )
+ return true; // already there
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( dirName.length() > 99 )
+ {
+ strcpy( buffer, "././@LongLink" );
+ fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
+ device()->writeBlock( buffer, 0x200 );
+ strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
+ buffer[0x200] = 0;
+ // write long name
+ device()->writeBlock( buffer, 0x200 );
+ // not even needed to reclear the buffer, tar doesn't do it
+ }
+ else
+ {
+ // Write name
+ strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
+ buffer[0x200] = 0;
+ }
+
+ fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
+
+ // Write header
+ device()->writeBlock( buffer, 0x200 );
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+
+ d->dirList.append( dirName ); // contains trailing slash
+ return true; // TODO if wanted, better error control
+#endif
+}
+
+bool KTar::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
+{
+ mode_t dflt_perm = 0100644;
+ time_t the_time = time(0);
+ return prepareWriting(name,user,group,size,dflt_perm,
+ the_time,the_time,the_time);
+}
+
+bool KTar::doneWriting( uint size )
+{
+ // Write alignment
+ int rest = size % 0x200;
+ if ( mode() & IO_ReadWrite )
+ d->tarEnd = device()->at() + (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;
+ TQ_LONG nwritten = device()->writeBlock( 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::fillBuffer( char * buffer,
+ const char * mode, int size, time_t mtime, char typeflag,
+ const char * uname, const char * gname )
+{
+ // mode (as in stat())
+ assert( strlen(mode) == 6 );
+ strcpy( buffer+0x64, mode );
+ buffer[ 0x6a ] = ' ';
+ buffer[ 0x6b ] = '\0';
+
+ // dummy uid
+ strcpy( buffer + 0x6c, " 765 ");
+ // dummy gid
+ strcpy( buffer + 0x74, " 144 ");
+
+ // size
+ TQCString s;
+ s.sprintf("%o", size); // OCT
+ s = s.rightJustify( 11, ' ' );
+ strcpy( buffer + 0x7c, s.data() );
+ buffer[ 0x87 ] = ' '; // space-terminate (no null after)
+
+ // modification time
+ s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT
+ s = s.rightJustify( 11, ' ' );
+ strcpy( buffer + 0x88, s.data() );
+ buffer[ 0x93 ] = ' '; // space-terminate (no null after)
+
+ // 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.sprintf("%o", check ); // OCT
+ s = s.rightJustify( 7, ' ' );
+ strcpy( buffer + 0x94, s.data() );
+}
+
+void KTar::writeLonglink(char *buffer, const TQCString &name, char typeflag,
+ const char *uname, const char *gname) {
+ strcpy( buffer, "././@LongLink" );
+ int namelen = name.length() + 1;
+ fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname );
+ device()->writeBlock( buffer, 0x200 );
+ int offset = 0;
+ while (namelen > 0) {
+ int chunksize = QMIN(namelen, 0x200);
+ memcpy(buffer, name.data()+offset, chunksize);
+ // write long name
+ device()->writeBlock( buffer, 0x200 );
+ // not even needed to reclear the buffer, tar doesn't do it
+ namelen -= chunksize;
+ offset += 0x200;
+ }/*wend*/
+}
+
+bool KTar::prepareWriting(const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
+}
+
+bool KTar::prepareWriting_impl(const TQString &name, const TQString &user,
+ const TQString &group, uint size, mode_t perm,
+ time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString fileName ( TQDir::cleanDirPath( 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
+ TQString tmp ( fileName );
+ int i = tmp.findRev( '/' );
+ if ( i != -1 )
+ {
+ TQString 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() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedFilename = TQFile::encodeName(fileName);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( fileName.length() > 99 )
+ 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);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
+
+ // Write header
+ return device()->writeBlock( buffer, 0x200 ) == 0x200;
+}
+
+bool KTar::writeDir(const TQString& name, const TQString& user,
+ const TQString& group, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime);
+}
+
+bool KTar::writeDir_impl(const TQString &name, const TQString &user,
+ const TQString &group, mode_t perm,
+ time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./ => call cleanDirPath
+ TQString dirName ( TQDir::cleanDirPath( name ) );
+
+ // Need trailing '/'
+ if ( dirName.right(1) != "/" )
+ dirName += "/";
+
+ if ( d->dirList.contains( dirName ) )
+ return true; // already there
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedDirname = TQFile::encodeName(dirName);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( dirName.length() > 99 )
+ 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);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
+
+ // Write header
+ device()->writeBlock( buffer, 0x200 );
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+
+ d->dirList.append( dirName ); // contains trailing slash
+ return true; // TODO if wanted, better error control
+}
+
+bool KTar::writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
+}
+
+bool KTar::writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n";
+ return false;
+ }
+
+ device()->flush();
+
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString fileName ( TQDir::cleanDirPath( name ) );
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedFilename = TQFile::encodeName(fileName);
+ TQCString encodedTarget = TQFile::encodeName(target);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if (target.length() > 99)
+ writeLonglink(buffer,encodedTarget,'K',uname,gname);
+ if ( fileName.length() > 99 )
+ 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);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
+
+ // Write header
+ bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200;
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+ return retval;
+}
+
+void KTar::virtual_hook( int id, void* data ) {
+ switch (id) {
+ case VIRTUAL_WRITE_SYMLINK: {
+ WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
+ params->retval = writeSymLink_impl(*params->name,*params->target,
+ *params->user,*params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_WRITE_DIR: {
+ WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
+ params->retval = writeDir_impl(*params->name,*params->user,
+ *params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_PREPARE_WRITING: {
+ PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
+ params->retval = prepareWriting_impl(*params->name,*params->user,
+ *params->group,params->size,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ default:
+ KArchive::virtual_hook( id, data );
+ }/*end switch*/
+}
+