28 #include <tqcstring.h>
32 #include <kmimetype.h>
33 #include <tdetempfile.h>
35 #include <kfilterdev.h>
36 #include <kfilterbase.h>
39 #include <kstandarddirs.h>
45 class KTar::KTarPrivate
48 KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {}
53 TQCString origFileName;
55 bool fillTempFile(
const TQString & filename);
56 bool writeBackTempFile(
const TQString & filename );
59 KTar::KTar(
const TQString& filename,
const TQString & _mimetype )
62 m_filename = filename;
64 TQString mimetype( _mimetype );
66 if ( mimetype.isEmpty() )
68 if ( TQFile::exists( filename ) )
72 kdDebug(7041) <<
"KTar::KTar mimetype = " << mimetype << endl;
75 if ( mimetype ==
"application/x-tgz" || mimetype ==
"application/x-targz" ||
76 mimetype ==
"application/x-webarchive" )
79 mimetype =
"application/x-gzip";
81 else if ( mimetype ==
"application/x-tbz" )
83 mimetype =
"application/x-bzip2";
88 TQFile file( filename );
89 if ( file.open( IO_ReadOnly ) )
91 unsigned char firstByte = file.getch();
92 unsigned char secondByte = file.getch();
93 unsigned char thirdByte = file.getch();
94 if ( firstByte == 0037 && secondByte == 0213 )
95 mimetype =
"application/x-gzip";
96 else if ( firstByte ==
'B' && secondByte ==
'Z' && thirdByte ==
'h' )
97 mimetype =
"application/x-bzip2";
98 else if ( firstByte ==
'P' && secondByte ==
'K' && thirdByte == 3 )
100 unsigned char fourthByte = file.getch();
101 if ( fourthByte == 4 )
102 mimetype =
"application/x-zip";
104 else if ( firstByte == 0xfd && secondByte ==
'7' && thirdByte ==
'z' )
106 unsigned char fourthByte = file.getch();
107 unsigned char fifthByte = file.getch();
108 unsigned char sixthByte = file.getch();
109 if ( fourthByte ==
'X' && fifthByte ==
'Z' && sixthByte == 0x00 )
110 mimetype =
"application/x-xz";
112 else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 )
114 unsigned char fourthByte = file.getch();
115 if ( fourthByte == 0x80 )
116 mimetype =
"application/x-lzma";
123 d->mimetype = mimetype;
125 prepareDevice( filename, mimetype, forced );
128 void KTar::prepareDevice(
const TQString & filename,
129 const TQString & mimetype,
bool )
131 if(
"application/x-tar" == mimetype )
132 setDevice( TQT_TQIODEVICE(
new TQFile( filename )) );
144 d->tmpFile =
new KTempFile(locateLocal(
"tmp",
"ktar-"),
".tar");
145 kdDebug( 7041 ) <<
"KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
146 d->tmpFile->setAutoDelete(
true);
150 TQFile* file = d->tmpFile->file();
152 setDevice(TQT_TQIODEVICE(file));
171 else if ( !m_filename.isEmpty() )
182 kdWarning(7041) <<
"KTar::setOrigFileName: File must be opened for writing first.\n";
188 TQ_LONG KTar::readRawHeader(
char *buffer) {
190 TQ_LONG n =
device()->readBlock( buffer, 0x200 );
191 if ( n == 0x200 && buffer[0] != 0 ) {
193 if (strncmp(buffer + 257,
"ustar", 5)) {
198 for( uint j = 0; j < 0x200; ++j )
202 for( uint j = 0; j < 8 ; j++ )
203 check -= buffer[148 + j];
206 s.sprintf(
"%o", check );
211 if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
212 && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
213 && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
214 kdWarning(7041) <<
"KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl;
220 if (n == 0x200) n = 0;
225 bool KTar::readLonglink(
char *buffer,TQCString &longlink) {
227 TQIODevice *dev =
device();
232 const char* p = buffer + 0x7c;
233 while( *p ==
' ' ) ++p;
234 int size = (int)strtol( p, &dummy, 8 );
236 longlink.resize(size);
238 dummy = longlink.data();
241 int chunksize = TQMIN(size, 0x200);
242 n = dev->readBlock( dummy + offset, chunksize );
243 if (n == -1)
return false;
248 int skip = 0x200 - (n % 0x200);
250 if (dev->readBlock(buffer,skip) != skip)
return false;
255 TQ_LONG KTar::readHeader(
char *buffer,TQString &name,TQString &symlink) {
259 TQ_LONG n = readRawHeader(buffer);
260 if (n != 0x200)
return n;
263 if (strcmp(buffer,
"././@LongLink") == 0) {
264 char typeflag = buffer[0x9c];
266 readLonglink(buffer,longlink);
268 case 'L': name = TQFile::decodeName(longlink);
break;
269 case 'K': symlink = TQFile::decodeName(longlink);
break;
280 name = TQFile::decodeName(TQCString(buffer, 101));
281 if (symlink.isEmpty())
282 symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
292 bool KTar::KTarPrivate::fillTempFile(
const TQString & filename) {
297 "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
301 if(
"application/x-gzip" == mimetype
302 ||
"application/x-bzip2" == mimetype
303 ||
"application/x-lzma" == mimetype
304 ||
"application/x-xz" == mimetype)
310 TQFile* file = tmpFile->file();
312 if ( ! file->open( IO_WriteOnly ) )
317 TQByteArray buffer(8*1024);
318 if ( ! filterDev->open( IO_ReadOnly ) )
324 while ( !filterDev->atEnd() && len != 0) {
325 len = filterDev->readBlock(buffer.data(),buffer.size());
330 file->writeBlock(buffer.data(),len);
336 if ( ! file->open( IO_ReadOnly ) )
340 kdDebug( 7041 ) <<
"KTar::openArchive: no filterdevice found!" << endl;
342 kdDebug( 7041 ) <<
"KTar::openArchive: filling tmpFile finished." << endl;
348 kdDebug( 7041 ) <<
"KTar::openArchive" << endl;
349 if ( !(mode & IO_ReadOnly) )
352 if ( !d->fillTempFile( m_filename ) )
361 TQIODevice* dev =
device();
367 char buffer[ 0x200 ];
375 TQ_LONG n = readHeader(buffer,name,symlink);
376 if (n < 0)
return false;
382 if ( name.right(1) ==
"/" )
385 name = name.left( name.length() - 1 );
388 int pos = name.findRev(
'/' );
392 nm = name.mid( pos + 1 );
397 const char* p = buffer + 0x64;
398 while( *p ==
' ' ) ++p;
399 int access = (int)strtol( p, &dummy, 8 );
402 TQString user( buffer + 0x109 );
403 TQString group( buffer + 0x129 );
408 while( *p ==
' ' ) ++p;
409 int time = (int)strtol( p, &dummy, 8 );
412 char typeflag = buffer[ 0x9c ];
416 if ( typeflag ==
'5' )
419 bool isDumpDir =
false;
420 if ( typeflag ==
'D' )
442 const char* p = buffer + 0x7c;
443 while( *p ==
' ' ) ++p;
444 int size = (int)strtol( p, &dummy, 8 );
456 if ( typeflag ==
'1' )
458 kdDebug(7041) <<
"HARD LINK, setting size to 0 instead of " << size << endl;
463 e =
new KArchiveFile(
this, nm, access, time, user, group, symlink,
468 int rest = size % 0x200;
469 int skip = size + (rest ? 0x200 - rest : 0);
471 if (! dev->at( dev->at() + skip ) )
472 kdWarning(7041) <<
"KTar::openArchive skipping " << skip <<
" failed" << endl;
481 setRootDir( static_cast<KArchiveDirectory *>( e ) );
489 TQString path = TQDir::cleanDirPath( name.left( pos ) );
498 d->tarEnd = dev->at() - n;
510 bool KTar::KTarPrivate::writeBackTempFile(
const TQString & filename ) {
514 kdDebug(7041) <<
"Write temporary file to compressed file" << endl;
515 kdDebug(7041) << filename <<
" " << mimetype << endl;
518 if(
"application/x-gzip" == mimetype
519 ||
"application/x-bzip2" == mimetype
520 ||
"application/x-lzma" == mimetype
521 ||
"application/x-xz" == mimetype)
526 TQFile* file = tmpFile->file();
528 if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
535 static_cast<KFilterDev *
>(dev)->setOrigFileName( origFileName );
536 TQByteArray buffer(8*1024);
538 while ( ! file->atEnd()) {
539 len = file->readBlock(buffer.data(),buffer.size());
540 dev->writeBlock(buffer.data(),len);
547 kdDebug(7041) <<
"Write temporary file to compressed file done." << endl;
558 if(
mode() == IO_WriteOnly)
559 return d->writeBackTempFile( m_filename );
564 bool KTar::writeDir(
const TQString& name,
const TQString& user,
const TQString& group )
566 mode_t perm = 040755;
567 time_t the_time = time(0);
568 return writeDir(name,user,group,perm,the_time,the_time,the_time);
572 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file before writing to it\n";
576 if ( !(
mode() & IO_WriteOnly) )
578 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file for writing\n";
583 TQString dirName ( TQDir::cleanDirPath( name ) );
586 if ( dirName.right(1) !=
"/" )
589 if ( d->dirList.contains( dirName ) )
592 char buffer[ 0x201 ];
593 memset( buffer, 0, 0x200 );
594 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
597 if ( dirName.length() > 99 )
599 strcpy( buffer,
"././@LongLink" );
600 fillBuffer( buffer,
" 0", dirName.length()+1,
'L', user.local8Bit(), group.local8Bit() );
601 device()->writeBlock( buffer, 0x200 );
602 strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
605 device()->writeBlock( buffer, 0x200 );
611 strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
615 fillBuffer( buffer,
" 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
618 device()->writeBlock( buffer, 0x200 );
619 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
621 d->dirList.append( dirName );
628 mode_t dflt_perm = 0100644;
629 time_t the_time = time(0);
631 the_time,the_time,the_time);
637 int rest = size % 0x200;
638 if (
mode() & IO_ReadWrite )
639 d->tarEnd =
device()->at() + (rest ? 0x200 - rest : 0);
642 char buffer[ 0x201 ];
643 for( uint i = 0; i < 0x200; ++i )
645 TQ_LONG nwritten =
device()->writeBlock( buffer, 0x200 - rest );
646 return nwritten == 0x200 - rest;
674 void KTar::fillBuffer(
char * buffer,
675 const char * mode,
int size, time_t mtime,
char typeflag,
676 const char * uname,
const char * gname )
679 assert( strlen(mode) == 6 );
680 strcpy( buffer+0x64, mode );
681 buffer[ 0x6a ] =
' ';
682 buffer[ 0x6b ] =
'\0';
685 strcpy( buffer + 0x6c,
" 765 ");
687 strcpy( buffer + 0x74,
" 144 ");
691 s.sprintf(
"%o", size);
692 s = s.rightJustify( 11,
' ' );
693 strcpy( buffer + 0x7c, s.data() );
694 buffer[ 0x87 ] =
' ';
697 s.sprintf(
"%lo", static_cast<unsigned long>(mtime) );
698 s = s.rightJustify( 11,
' ' );
699 strcpy( buffer + 0x88, s.data() );
700 buffer[ 0x93 ] =
' ';
703 buffer[ 0x94 ] = 0x20;
704 buffer[ 0x95 ] = 0x20;
705 buffer[ 0x96 ] = 0x20;
706 buffer[ 0x97 ] = 0x20;
707 buffer[ 0x98 ] = 0x20;
708 buffer[ 0x99 ] = 0x20;
715 buffer[ 0x9a ] =
'\0';
716 buffer[ 0x9b ] =
' ';
719 buffer[ 0x9c ] = typeflag;
722 strcpy( buffer + 0x101,
"ustar");
723 strcpy( buffer + 0x107,
"00" );
726 strcpy( buffer + 0x109, uname );
728 strcpy( buffer + 0x129, gname );
732 for( uint j = 0; j < 0x200; ++j )
734 s.sprintf(
"%o", check );
735 s = s.rightJustify( 7,
' ' );
736 strcpy( buffer + 0x94, s.data() );
739 void KTar::writeLonglink(
char *buffer,
const TQCString &name,
char typeflag,
740 const char *uname,
const char *gname) {
741 strcpy( buffer,
"././@LongLink" );
742 int namelen = name.length() + 1;
743 fillBuffer( buffer,
" 0", namelen, 0, typeflag, uname, gname );
744 device()->writeBlock( buffer, 0x200 );
746 while (namelen > 0) {
747 int chunksize = TQMIN(namelen, 0x200);
748 memcpy(buffer, name.data()+offset, chunksize);
750 device()->writeBlock( buffer, 0x200 );
752 namelen -= chunksize;
758 const TQString& group, uint size, mode_t perm,
759 time_t atime, time_t mtime, time_t ctime) {
763 bool KTar::prepareWriting_impl(
const TQString &name,
const TQString &user,
764 const TQString &group, uint size, mode_t perm,
765 time_t , time_t mtime, time_t ) {
768 kdWarning(7041) <<
"KTar::prepareWriting: You must open the tar file before writing to it\n";
772 if ( !(
mode() & IO_WriteOnly) )
774 kdWarning(7041) <<
"KTar::prepareWriting: You must open the tar file for writing\n";
779 TQString
fileName ( TQDir::cleanDirPath( name ) );
800 char buffer[ 0x201 ];
801 memset( buffer, 0, 0x200 );
802 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
805 TQCString encodedFilename = TQFile::encodeName(
fileName);
806 TQCString uname = user.local8Bit();
807 TQCString gname = group.local8Bit();
811 writeLonglink(buffer,encodedFilename,
'L',uname,gname);
814 strncpy( buffer, encodedFilename, 99 );
817 memset(buffer+0x9d, 0, 0x200 - 0x9d);
820 permstr.sprintf(
"%o",perm);
821 permstr = permstr.rightJustify(6,
' ');
822 fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
825 return device()->writeBlock( buffer, 0x200 ) == 0x200;
829 const TQString& group, mode_t perm,
830 time_t atime, time_t mtime, time_t ctime) {
834 bool KTar::writeDir_impl(
const TQString &name,
const TQString &user,
835 const TQString &group, mode_t perm,
836 time_t , time_t mtime, time_t ) {
839 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file before writing to it\n";
843 if ( !(
mode() & IO_WriteOnly) )
845 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file for writing\n";
850 TQString dirName ( TQDir::cleanDirPath( name ) );
853 if ( dirName.right(1) !=
"/" )
856 if ( d->dirList.contains( dirName ) )
859 char buffer[ 0x201 ];
860 memset( buffer, 0, 0x200 );
861 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
864 TQCString encodedDirname = TQFile::encodeName(dirName);
865 TQCString uname = user.local8Bit();
866 TQCString gname = group.local8Bit();
869 if ( dirName.length() > 99 )
870 writeLonglink(buffer,encodedDirname,
'L',uname,gname);
873 strncpy( buffer, encodedDirname, 99 );
876 memset(buffer+0x9d, 0, 0x200 - 0x9d);
879 permstr.sprintf(
"%o",perm);
880 permstr = permstr.rightJustify(6,
' ');
881 fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
884 device()->writeBlock( buffer, 0x200 );
885 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
887 d->dirList.append( dirName );
891 bool KTar::writeSymLink(
const TQString &name,
const TQString &target,
892 const TQString &user,
const TQString &group,
893 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
897 bool KTar::writeSymLink_impl(
const TQString &name,
const TQString &target,
898 const TQString &user,
const TQString &group,
899 mode_t perm, time_t , time_t mtime, time_t ) {
902 kdWarning(7041) <<
"KTar::writeSymLink: You must open the tar file before writing to it\n";
906 if ( !(
mode() & IO_WriteOnly) )
908 kdWarning(7041) <<
"KTar::writeSymLink: You must open the tar file for writing\n";
915 TQString
fileName ( TQDir::cleanDirPath( name ) );
917 char buffer[ 0x201 ];
918 memset( buffer, 0, 0x200 );
919 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
922 TQCString encodedFilename = TQFile::encodeName(
fileName);
923 TQCString encodedTarget = TQFile::encodeName(target);
924 TQCString uname = user.local8Bit();
925 TQCString gname = group.local8Bit();
928 if (target.length() > 99)
929 writeLonglink(buffer,encodedTarget,
'K',uname,gname);
931 writeLonglink(buffer,encodedFilename,
'L',uname,gname);
934 strncpy( buffer, encodedFilename, 99 );
937 strncpy(buffer+0x9d, encodedTarget, 99);
940 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
943 permstr.sprintf(
"%o",perm);
944 permstr = permstr.rightJustify(6,
' ');
945 fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
948 bool retval =
device()->writeBlock( buffer, 0x200 ) == 0x200;
949 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
953 void KTar::virtual_hook(
int id,
void* data ) {
955 case VIRTUAL_WRITE_SYMLINK: {
956 WriteSymlinkParams *params =
reinterpret_cast<WriteSymlinkParams *
>(data);
957 params->retval = writeSymLink_impl(*params->name,*params->target,
958 *params->user,*params->group,params->perm,
959 params->atime,params->mtime,params->ctime);
962 case VIRTUAL_WRITE_DIR: {
963 WriteDirParams *params =
reinterpret_cast<WriteDirParams *
>(data);
964 params->retval = writeDir_impl(*params->name,*params->user,
965 *params->group,params->perm,
966 params->atime,params->mtime,params->ctime);
969 case VIRTUAL_PREPARE_WRITING: {
970 PrepareWritingParams *params =
reinterpret_cast<PrepareWritingParams *
>(data);
971 params->retval = prepareWriting_impl(*params->name,*params->user,
972 *params->group,params->size,params->perm,
973 params->atime,params->mtime,params->ctime);
977 KArchive::virtual_hook(
id, data );
KTar(const TQString &filename, const TQString &mimetype=TQString::null)
Creates an instance that operates on the given filename using the compression filter associated to gi...
bool 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)
Writes a symbolic link to the archive if the archive must be opened for writing.
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
virtual void close()
Closes the archive.
KArchive is a base class for reading and writing archives.
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
TQIODevice * device() const
The underlying device.
static Ptr findByFileContent(const TQString &fileName, int *accuracy=0)
Tries to find out the MIME type of a file by looking for certain magic numbers and characteristic str...
static Ptr findByPath(const TQString &path, mode_t mode=0, bool fast_mode=false)
Finds a KMimeType with the given _url.
KArchiveDirectory * findOrCreate(const TQString &path)
Ensures that path exists, create otherwise.
A class for reading and writing compressed data onto a device (e.g.
virtual bool doneWriting(uint size)
Call doneWriting after writing the data.
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)
Here's another way of writing a file into an archive: Call prepareWriting, then call writeData() as m...
A base class for entries in an KArchive.
TQString fileName()
The name of the tar file, as passed to the constructor Null if you used the TQIODevice constructor...
TDEIO_EXPORT MimetypeJob * mimetype(const KURL &url, bool showProgressInfo=true)
Find mimetype for one file or directory.
virtual bool closeArchive()
Closes the archive.
int mode() const
Returns the mode in which the archive was opened.
virtual bool writeDir(const TQString &name, const TQString &user, const TQString &group)
If an archive is opened for writing then you can add new directories using this function.
Represents a directory entry in a KArchive.
virtual bool openArchive(int mode)
Opens the archive for reading.
void setOrigFileName(const TQCString &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar...
bool isOpened() const
Checks whether the archive is open.
Represents a file entry in a KArchive.
virtual bool writeDir(const TQString &name, const TQString &user, const TQString &group)=0
If an archive is opened for writing then you can add new directories using this function.
static TQIODevice * deviceForFile(const TQString &fileName, const TQString &mimetype=TQString::null, bool forceFilter=false)
Creates an i/o device that is able to read from fileName, whether it's compressed or not...
virtual bool prepareWriting(const TQString &name, const TQString &user, const TQString &group, uint size)=0
Here's another way of writing a file into an archive: Call prepareWriting, then call writeData() as m...