28 #include <tqcstring.h>
32 #include <kmimetype.h>
33 #include <tdetempfile.h>
35 #include <kfilterdev.h>
36 #include <kfilterbase.h>
39 #include <tdestandarddirs.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";
118 else if ( firstByte ==
'L' && secondByte ==
'Z' && thirdByte ==
'I' )
120 unsigned char fourthByte = file.getch();
121 unsigned char fifthByte = file.getch();
122 if ( fourthByte ==
'P' && fifthByte == 0x01 )
123 mimetype =
"application/x-lzip";
130 d->mimetype = mimetype;
132 prepareDevice( filename, mimetype, forced );
135 void KTar::prepareDevice(
const TQString & filename,
136 const TQString & mimetype,
bool )
138 if(
"application/x-tar" == mimetype )
139 setDevice(
new TQFile( filename ) );
151 d->tmpFile =
new KTempFile(locateLocal(
"tmp",
"ktar-"),
".tar");
152 kdDebug( 7041 ) <<
"KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
153 d->tmpFile->setAutoDelete(
true);
157 TQFile* file = d->tmpFile->file();
178 else if ( !m_filename.isEmpty() )
189 kdWarning(7041) <<
"KTar::setOrigFileName: File must be opened for writing first.\n";
195 TQ_LONG KTar::readRawHeader(
char *buffer) {
197 TQ_LONG n =
device()->readBlock( buffer, 0x200 );
198 if ( n == 0x200 && buffer[0] != 0 ) {
200 if (strncmp(buffer + 257,
"ustar", 5)) {
205 for( uint j = 0; j < 0x200; ++j )
209 for( uint j = 0; j < 8 ; j++ )
210 check -= buffer[148 + j];
213 s.sprintf(
"%o", check );
218 if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
219 && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
220 && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
221 kdWarning(7041) <<
"KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl;
227 if (n == 0x200) n = 0;
232 bool KTar::readLonglink(
char *buffer,TQCString &longlink) {
234 TQIODevice *dev =
device();
239 const char* p = buffer + 0x7c;
240 while( *p ==
' ' ) ++p;
241 int size = (int)strtol( p, &dummy, 8 );
243 longlink.resize(size);
245 dummy = longlink.data();
248 int chunksize = TQMIN(size, 0x200);
249 n = dev->readBlock( dummy + offset, chunksize );
250 if (n == -1)
return false;
255 int skip = 0x200 - (n % 0x200);
257 if (dev->readBlock(buffer,skip) != skip)
return false;
262 TQ_LONG KTar::readHeader(
char *buffer,TQString &name,TQString &symlink) {
266 TQ_LONG n = readRawHeader(buffer);
267 if (n != 0x200)
return n;
270 if (strcmp(buffer,
"././@LongLink") == 0) {
271 char typeflag = buffer[0x9c];
273 readLonglink(buffer,longlink);
275 case 'L': name = TQFile::decodeName(longlink);
break;
276 case 'K':
symlink = TQFile::decodeName(longlink);
break;
287 name = TQFile::decodeName(TQCString(buffer, 101));
289 symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
299 bool KTar::KTarPrivate::fillTempFile(
const TQString & filename) {
304 "KTar::openArchive: filling tmpFile of mimetype '" <<
mimetype <<
308 if(
"application/x-gzip" == mimetype
309 ||
"application/x-bzip2" == mimetype
310 ||
"application/x-lzma" == mimetype
311 ||
"application/x-xz" == mimetype
312 ||
"application/x-lzip" == mimetype)
318 TQFile* file = tmpFile->file();
320 if ( ! file->open( IO_WriteOnly ) )
325 TQByteArray buffer(8*1024);
326 if ( ! filterDev->open( IO_ReadOnly ) )
332 while ( !filterDev->atEnd() && len != 0) {
333 len = filterDev->readBlock(buffer.data(),buffer.size());
338 file->writeBlock(buffer.data(),len);
344 if ( ! file->open( IO_ReadOnly ) )
348 kdDebug( 7041 ) <<
"KTar::openArchive: no filterdevice found!" << endl;
350 kdDebug( 7041 ) <<
"KTar::openArchive: filling tmpFile finished." << endl;
356 kdDebug( 7041 ) <<
"KTar::openArchive" << endl;
357 if ( !(
mode & IO_ReadOnly) )
360 if ( !d->fillTempFile( m_filename ) )
369 TQIODevice* dev =
device();
375 char buffer[ 0x200 ];
383 TQ_LONG n = readHeader(buffer,name,symlink);
384 if (n < 0)
return false;
390 if ( name.right(1) ==
"/" )
393 name = name.left( name.length() - 1 );
396 int pos = name.findRev(
'/' );
400 nm = name.mid( pos + 1 );
405 const char* p = buffer + 0x64;
406 while( *p ==
' ' ) ++p;
407 int access = (int)strtol( p, &dummy, 8 );
410 TQString user( buffer + 0x109 );
411 TQString group( buffer + 0x129 );
416 while( *p ==
' ' ) ++p;
417 int time = (int)strtol( p, &dummy, 8 );
420 char typeflag = buffer[ 0x9c ];
424 if ( typeflag ==
'5' )
427 bool isDumpDir =
false;
428 if ( typeflag ==
'D' )
450 const char* p = buffer + 0x7c;
451 while( *p ==
' ' ) ++p;
452 int size = (int)strtol( p, &dummy, 8 );
464 if ( typeflag ==
'1' )
466 kdDebug(7041) <<
"HARD LINK, setting size to 0 instead of " << size << endl;
471 e =
new KArchiveFile(
this, nm, access, time, user, group, symlink,
476 int rest = size % 0x200;
477 int skip = size + (rest ? 0x200 - rest : 0);
479 if (! dev->at( dev->at() + skip ) )
480 kdWarning(7041) <<
"KTar::openArchive skipping " << skip <<
" failed" << endl;
497 TQString path = TQDir::cleanDirPath( name.left( pos ) );
506 d->tarEnd = dev->at() - n;
518 bool KTar::KTarPrivate::writeBackTempFile(
const TQString & filename ) {
522 kdDebug(7041) <<
"Write temporary file to compressed file" << endl;
523 kdDebug(7041) << filename <<
" " << mimetype << endl;
526 if(
"application/x-gzip" == mimetype
527 ||
"application/x-bzip2" == mimetype
528 ||
"application/x-lzma" == mimetype
529 ||
"application/x-xz" == mimetype
530 ||
"application/x-lzip" == mimetype)
535 TQFile* file = tmpFile->file();
537 if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
544 static_cast<KFilterDev *
>(dev)->setOrigFileName( origFileName );
545 TQByteArray buffer(8*1024);
547 while ( ! file->atEnd()) {
548 len = file->readBlock(buffer.data(),buffer.size());
549 dev->writeBlock(buffer.data(),len);
556 kdDebug(7041) <<
"Write temporary file to compressed file done." << endl;
567 if(
mode() == IO_WriteOnly)
568 return d->writeBackTempFile( m_filename );
573 bool KTar::writeDir(
const TQString& name,
const TQString& user,
const TQString& group )
575 mode_t perm = 040755;
576 time_t the_time = time(0);
577 return writeDir(name,user,group,perm,the_time,the_time,the_time);
581 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file before writing to it\n";
585 if ( !(
mode() & IO_WriteOnly) )
587 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file for writing\n";
592 TQString dirName ( TQDir::cleanDirPath( name ) );
595 if ( dirName.right(1) !=
"/" )
598 if ( d->dirList.contains( dirName ) )
601 char buffer[ 0x201 ];
602 memset( buffer, 0, 0x200 );
603 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
606 if ( dirName.length() > 99 )
608 strcpy( buffer,
"././@LongLink" );
609 fillBuffer( buffer,
" 0", dirName.length()+1,
'L', user.local8Bit(), group.local8Bit() );
610 device()->writeBlock( buffer, 0x200 );
611 strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
614 device()->writeBlock( buffer, 0x200 );
620 strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
624 fillBuffer( buffer,
" 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
627 device()->writeBlock( buffer, 0x200 );
628 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
630 d->dirList.append( dirName );
637 mode_t dflt_perm = 0100644;
638 time_t the_time = time(0);
640 the_time,the_time,the_time);
646 int rest = size % 0x200;
647 if (
mode() & IO_ReadWrite )
648 d->tarEnd =
device()->at() + (rest ? 0x200 - rest : 0);
651 char buffer[ 0x201 ];
652 for( uint i = 0; i < 0x200; ++i )
654 TQ_LONG nwritten =
device()->writeBlock( buffer, 0x200 - rest );
655 return nwritten == 0x200 - rest;
683 void KTar::fillBuffer(
char * buffer,
684 const char * mode,
int size, time_t mtime,
char typeflag,
685 const char * uname,
const char * gname )
688 assert( strlen(
mode) == 6 );
689 strcpy( buffer+0x64,
mode );
690 buffer[ 0x6a ] =
' ';
691 buffer[ 0x6b ] =
'\0';
694 strcpy( buffer + 0x6c,
" 765 ");
696 strcpy( buffer + 0x74,
" 144 ");
700 s.sprintf(
"%o", size);
701 s = s.rightJustify( 11,
' ' );
702 strcpy( buffer + 0x7c, s.data() );
703 buffer[ 0x87 ] =
' ';
706 s.sprintf(
"%lo",
static_cast<unsigned long>(mtime) );
707 s = s.rightJustify( 11,
' ' );
708 strcpy( buffer + 0x88, s.data() );
709 buffer[ 0x93 ] =
' ';
712 buffer[ 0x94 ] = 0x20;
713 buffer[ 0x95 ] = 0x20;
714 buffer[ 0x96 ] = 0x20;
715 buffer[ 0x97 ] = 0x20;
716 buffer[ 0x98 ] = 0x20;
717 buffer[ 0x99 ] = 0x20;
724 buffer[ 0x9a ] =
'\0';
725 buffer[ 0x9b ] =
' ';
728 buffer[ 0x9c ] = typeflag;
731 strcpy( buffer + 0x101,
"ustar");
732 strcpy( buffer + 0x107,
"00" );
735 strcpy( buffer + 0x109, uname );
737 strcpy( buffer + 0x129, gname );
741 for( uint j = 0; j < 0x200; ++j )
743 s.sprintf(
"%o", check );
744 s = s.rightJustify( 7,
' ' );
745 strcpy( buffer + 0x94, s.data() );
748 void KTar::writeLonglink(
char *buffer,
const TQCString &name,
char typeflag,
749 const char *uname,
const char *gname) {
750 strcpy( buffer,
"././@LongLink" );
751 int namelen = name.length() + 1;
752 fillBuffer( buffer,
" 0", namelen, 0, typeflag, uname, gname );
753 device()->writeBlock( buffer, 0x200 );
755 while (namelen > 0) {
756 int chunksize = TQMIN(namelen, 0x200);
757 memcpy(buffer, name.data()+offset, chunksize);
759 device()->writeBlock( buffer, 0x200 );
761 namelen -= chunksize;
767 const TQString& group, uint size, mode_t perm,
768 time_t atime, time_t mtime, time_t ctime) {
772 bool KTar::prepareWriting_impl(
const TQString &name,
const TQString &user,
773 const TQString &group, uint size, mode_t perm,
774 time_t , time_t mtime, time_t ) {
777 kdWarning(7041) <<
"KTar::prepareWriting: You must open the tar file before writing to it\n";
781 if ( !(
mode() & IO_WriteOnly) )
783 kdWarning(7041) <<
"KTar::prepareWriting: You must open the tar file for writing\n";
788 TQString
fileName ( TQDir::cleanDirPath( name ) );
809 char buffer[ 0x201 ];
810 memset( buffer, 0, 0x200 );
811 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
814 TQCString encodedFilename = TQFile::encodeName(
fileName);
815 TQCString uname = user.local8Bit();
816 TQCString gname = group.local8Bit();
820 writeLonglink(buffer,encodedFilename,
'L',uname,gname);
823 strncpy( buffer, encodedFilename, 99 );
826 memset(buffer+0x9d, 0, 0x200 - 0x9d);
829 permstr.sprintf(
"%o",perm);
830 permstr = permstr.rightJustify(6,
' ');
831 fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
834 return device()->writeBlock( buffer, 0x200 ) == 0x200;
838 const TQString& group, mode_t perm,
839 time_t atime, time_t mtime, time_t ctime) {
843 bool KTar::writeDir_impl(
const TQString &name,
const TQString &user,
844 const TQString &group, mode_t perm,
845 time_t , time_t mtime, time_t ) {
848 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file before writing to it\n";
852 if ( !(
mode() & IO_WriteOnly) )
854 kdWarning(7041) <<
"KTar::writeDir: You must open the tar file for writing\n";
859 TQString dirName ( TQDir::cleanDirPath( name ) );
862 if ( dirName.right(1) !=
"/" )
865 if ( d->dirList.contains( dirName ) )
868 char buffer[ 0x201 ];
869 memset( buffer, 0, 0x200 );
870 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
873 TQCString encodedDirname = TQFile::encodeName(dirName);
874 TQCString uname = user.local8Bit();
875 TQCString gname = group.local8Bit();
878 if ( dirName.length() > 99 )
879 writeLonglink(buffer,encodedDirname,
'L',uname,gname);
882 strncpy( buffer, encodedDirname, 99 );
885 memset(buffer+0x9d, 0, 0x200 - 0x9d);
888 permstr.sprintf(
"%o",perm);
889 permstr = permstr.rightJustify(6,
' ');
890 fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
893 device()->writeBlock( buffer, 0x200 );
894 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
896 d->dirList.append( dirName );
900 bool KTar::writeSymLink(
const TQString &name,
const TQString &target,
901 const TQString &user,
const TQString &group,
902 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
906 bool KTar::writeSymLink_impl(
const TQString &name,
const TQString &target,
907 const TQString &user,
const TQString &group,
908 mode_t perm, time_t , time_t mtime, time_t ) {
911 kdWarning(7041) <<
"KTar::writeSymLink: You must open the tar file before writing to it\n";
915 if ( !(
mode() & IO_WriteOnly) )
917 kdWarning(7041) <<
"KTar::writeSymLink: You must open the tar file for writing\n";
924 TQString
fileName ( TQDir::cleanDirPath( name ) );
926 char buffer[ 0x201 ];
927 memset( buffer, 0, 0x200 );
928 if (
mode() & IO_ReadWrite )
device()->at(d->tarEnd);
931 TQCString encodedFilename = TQFile::encodeName(
fileName);
932 TQCString encodedTarget = TQFile::encodeName(target);
933 TQCString uname = user.local8Bit();
934 TQCString gname = group.local8Bit();
937 if (target.length() > 99)
938 writeLonglink(buffer,encodedTarget,
'K',uname,gname);
940 writeLonglink(buffer,encodedFilename,
'L',uname,gname);
943 strncpy( buffer, encodedFilename, 99 );
946 strncpy(buffer+0x9d, encodedTarget, 99);
949 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
952 permstr.sprintf(
"%o",perm);
953 permstr = permstr.rightJustify(6,
' ');
954 fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
957 bool retval =
device()->writeBlock( buffer, 0x200 ) == 0x200;
958 if (
mode() & IO_ReadWrite ) d->tarEnd =
device()->at();
962 void KTar::virtual_hook(
int id,
void* data ) {
964 case VIRTUAL_WRITE_SYMLINK: {
965 WriteSymlinkParams *params =
reinterpret_cast<WriteSymlinkParams *
>(data);
966 params->retval = writeSymLink_impl(*params->name,*params->target,
967 *params->user,*params->group,params->perm,
968 params->atime,params->mtime,params->ctime);
971 case VIRTUAL_WRITE_DIR: {
972 WriteDirParams *params =
reinterpret_cast<WriteDirParams *
>(data);
973 params->retval = writeDir_impl(*params->name,*params->user,
974 *params->group,params->perm,
975 params->atime,params->mtime,params->ctime);
978 case VIRTUAL_PREPARE_WRITING: {
979 PrepareWritingParams *params =
reinterpret_cast<PrepareWritingParams *
>(data);
980 params->retval = prepareWriting_impl(*params->name,*params->user,
981 *params->group,params->size,params->perm,
982 params->atime,params->mtime,params->ctime);
986 KArchive::virtual_hook(
id, data );
Represents a directory entry in a KArchive.
A base class for entries in an KArchive.
Represents a file entry in a KArchive.
KArchive is a base class for reading and writing archives.
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.
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
KArchiveDirectory * findOrCreate(const TQString &path)
Ensures that path exists, create otherwise.
virtual void close()
Closes the archive.
int mode() const
Returns the mode in which the archive was opened.
bool isOpened() const
Checks whether the archive is open.
TQIODevice * device() const
The underlying device.
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 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...
A class for reading and writing compressed data onto a device (e.g.
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.
static Ptr findByPath(const TQString &path, mode_t mode=0, bool fast_mode=false)
Finds a KMimeType with the given _url.
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...
void setOrigFileName(const TQCString &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar....
TQString fileName()
The name of the tar file, as passed to the constructor Null if you used the TQIODevice constructor.
virtual bool doneWriting(uint size)
Call doneWriting after writing the data.
virtual bool openArchive(int mode)
Opens the archive for reading.
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
virtual bool closeArchive()
Closes the archive.
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...
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...
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.
TDEIO_EXPORT SimpleJob * symlink(const TQString &target, const KURL &dest, bool overwrite, bool showProgressInfo=true)
Create or move a symlink.
TDEIO_EXPORT MimetypeJob * mimetype(const KURL &url, bool showProgressInfo=true)
Find mimetype for one file or directory.