• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

  • tdeio
  • tdeio
ktar.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 2000 David Faure <faure@kde.org>
3 Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20//#include <stdio.h>
21#include <stdlib.h> // strtol
22#include <time.h> // time()
23/*#include <unistd.h>
24#include <grp.h>
25#include <pwd.h>*/
26#include <assert.h>
27
28#include <tqcstring.h>
29#include <tqdir.h>
30#include <tqfile.h>
31#include <kdebug.h>
32#include <kmimetype.h>
33#include <tdetempfile.h>
34
35#include <kfilterdev.h>
36#include <kfilterbase.h>
37
38#include "ktar.h"
39#include <tdestandarddirs.h>
40
44
45class KTar::KTarPrivate
46{
47public:
48 KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {}
49 TQStringList dirList;
50 int tarEnd;
51 KTempFile* tmpFile;
52 TQString mimetype;
53 TQCString origFileName;
54
55 bool fillTempFile(const TQString & filename);
56 bool writeBackTempFile( const TQString & filename );
57};
58
59KTar::KTar( const TQString& filename, const TQString & _mimetype )
60 : KArchive( 0 )
61{
62 m_filename = filename;
63 d = new KTarPrivate;
64 TQString mimetype( _mimetype );
65 bool forced = true;
66 if ( mimetype.isEmpty() ) // Find out mimetype manually
67 {
68 if ( TQFile::exists( filename ) )
69 mimetype = KMimeType::findByFileContent( filename )->name();
70 else
71 mimetype = KMimeType::findByPath( filename, 0, true )->name();
72 kdDebug(7041) << "KTar::KTar mimetype = " << mimetype << endl;
73
74 // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
75 if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
76 mimetype == "application/x-webarchive" )
77 {
78 // that's a gzipped tar file, so ask for gzip filter
79 mimetype = "application/x-gzip";
80 }
81 else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter
82 {
83 mimetype = "application/x-bzip2";
84 }
85 else
86 {
87 // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
88 TQFile file( filename );
89 if ( file.open( IO_ReadOnly ) )
90 {
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 )
99 {
100 unsigned char fourthByte = file.getch();
101 if ( fourthByte == 4 )
102 mimetype = "application/x-zip";
103 }
104 else if ( firstByte == 0xfd && secondByte == '7' && thirdByte == 'z' )
105 {
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";
111 }
112 else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 )
113 {
114 unsigned char fourthByte = file.getch();
115 if ( fourthByte == 0x80 )
116 mimetype = "application/x-lzma";
117 }
118 }
119 file.close();
120 }
121 forced = false;
122 }
123 d->mimetype = mimetype;
124
125 prepareDevice( filename, mimetype, forced );
126}
127
128void KTar::prepareDevice( const TQString & filename,
129 const TQString & mimetype, bool /*forced*/ )
130{
131 if( "application/x-tar" == mimetype )
132 setDevice( new TQFile( filename ) );
133 else
134 {
135 // The compression filters are very slow with random access.
136 // So instead of applying the filter to the device,
137 // the file is completly extracted instead,
138 // and we work on the extracted tar file.
139 // This improves the extraction speed by the tar ioslave dramatically,
140 // if the archive file contains many files.
141 // This is because the tar ioslave extracts one file after the other and normally
142 // has to walk through the decompression filter each time.
143 // Which is in fact nearly as slow as a complete decompression for each file.
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);
147
148 // KTempFile opens the file automatically,
149 // the device must be closed, however, for KArchive.setDevice()
150 TQFile* file = d->tmpFile->file();
151 file->close();
152 setDevice(file);
153 }
154}
155
156KTar::KTar( TQIODevice * dev )
157 : KArchive( dev )
158{
159 Q_ASSERT( dev );
160 d = new KTarPrivate;
161}
162
163KTar::~KTar()
164{
165 // mjarrett: Closes to prevent ~KArchive from aborting w/o device
166 if( isOpened() )
167 close();
168
169 if (d->tmpFile)
170 delete d->tmpFile; // will delete the device
171 else if ( !m_filename.isEmpty() )
172 delete device(); // we created it ourselves
173
174
175 delete d;
176}
177
178void KTar::setOrigFileName( const TQCString & fileName )
179{
180 if ( !isOpened() || !(mode() & IO_WriteOnly) )
181 {
182 kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
183 return;
184 }
185 d->origFileName = fileName;
186}
187
188TQ_LONG KTar::readRawHeader(char *buffer) {
189 // Read header
190 TQ_LONG n = device()->readBlock( buffer, 0x200 );
191 if ( n == 0x200 && buffer[0] != 0 ) {
192 // Make sure this is actually a tar header
193 if (strncmp(buffer + 257, "ustar", 5)) {
194 // The magic isn't there (broken/old tars), but maybe a correct checksum?
195 TQCString s;
196
197 int check = 0;
198 for( uint j = 0; j < 0x200; ++j )
199 check += buffer[j];
200
201 // adjust checksum to count the checksum fields as blanks
202 for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
203 check -= buffer[148 + j];
204 check += 8 * ' ';
205
206 s.sprintf("%o", check );
207
208 // only compare those of the 6 checksum digits that mean something,
209 // because the other digits are filled with all sorts of different chars by different tars ...
210 // Some tars right-justify the checksum so it could start in one of three places - we have to check each.
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;
215 return -1;
216 }
217 }/*end if*/
218 } else {
219 // reset to 0 if 0x200 because logical end of archive has been reached
220 if (n == 0x200) n = 0;
221 }/*end if*/
222 return n;
223}
224
225bool KTar::readLonglink(char *buffer,TQCString &longlink) {
226 TQ_LONG n = 0;
227 TQIODevice *dev = device();
228 // read size of longlink from size field in header
229 // size is in bytes including the trailing null (which we ignore)
230 buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
231 char *dummy;
232 const char* p = buffer + 0x7c;
233 while( *p == ' ' ) ++p;
234 int size = (int)strtol( p, &dummy, 8 );
235
236 longlink.resize(size);
237 size--; // ignore trailing null
238 dummy = longlink.data();
239 int offset = 0;
240 while (size > 0) {
241 int chunksize = TQMIN(size, 0x200);
242 n = dev->readBlock( dummy + offset, chunksize );
243 if (n == -1) return false;
244 size -= chunksize;
245 offset += 0x200;
246 }/*wend*/
247 // jump over the rest
248 int skip = 0x200 - (n % 0x200);
249 if (skip < 0x200) {
250 if (dev->readBlock(buffer,skip) != skip) return false;
251 }
252 return true;
253}
254
255TQ_LONG KTar::readHeader(char *buffer,TQString &name,TQString &symlink) {
256 name.truncate(0);
257 symlink.truncate(0);
258 while (true) {
259 TQ_LONG n = readRawHeader(buffer);
260 if (n != 0x200) return n;
261
262 // is it a longlink?
263 if (strcmp(buffer,"././@LongLink") == 0) {
264 char typeflag = buffer[0x9c];
265 TQCString longlink;
266 readLonglink(buffer,longlink);
267 switch (typeflag) {
268 case 'L': name = TQFile::decodeName(longlink); break;
269 case 'K': symlink = TQFile::decodeName(longlink); break;
270 }/*end switch*/
271 } else {
272 break;
273 }/*end if*/
274 }/*wend*/
275
276 // if not result of longlink, read names directly from the header
277 if (name.isEmpty())
278 // there are names that are exactly 100 bytes long
279 // and neither longlink nor \0 terminated (bug:101472)
280 name = TQFile::decodeName(TQCString(buffer, 101));
281 if (symlink.isEmpty())
282 symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
283
284 return 0x200;
285}
286
287/*
288 * If we have created a temporary file, we have
289 * to decompress the original file now and write
290 * the contents to the temporary file.
291 */
292bool KTar::KTarPrivate::fillTempFile( const TQString & filename) {
293 if ( ! tmpFile )
294 return true;
295
296 kdDebug( 7041 ) <<
297 "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
298 "' ... " << endl;
299
300 bool forced = false;
301 if( "application/x-gzip" == mimetype
302 || "application/x-bzip2" == mimetype
303 || "application/x-lzma" == mimetype
304 || "application/x-xz" == mimetype)
305 forced = true;
306
307 TQIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced );
308
309 if( filterDev ) {
310 TQFile* file = tmpFile->file();
311 file->close();
312 if ( ! file->open( IO_WriteOnly ) )
313 {
314 delete filterDev;
315 return false;
316 }
317 TQByteArray buffer(8*1024);
318 if ( ! filterDev->open( IO_ReadOnly ) )
319 {
320 delete filterDev;
321 return false;
322 }
323 TQ_LONG len = -1;
324 while ( !filterDev->atEnd() && len != 0) {
325 len = filterDev->readBlock(buffer.data(),buffer.size());
326 if ( len < 0 ) { // corrupted archive
327 delete filterDev;
328 return false;
329 }
330 file->writeBlock(buffer.data(),len);
331 }
332 filterDev->close();
333 delete filterDev;
334
335 file->close();
336 if ( ! file->open( IO_ReadOnly ) )
337 return false;
338 }
339 else
340 kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl;
341
342 kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl;
343 return true;
344}
345
346bool KTar::openArchive( int mode )
347{
348 kdDebug( 7041 ) << "KTar::openArchive" << endl;
349 if ( !(mode & IO_ReadOnly) )
350 return true;
351
352 if ( !d->fillTempFile( m_filename ) )
353 return false;
354
355 // We'll use the permission and user/group of d->rootDir
356 // for any directory we emulate (see findOrCreate)
357 //struct stat buf;
358 //stat( m_filename, &buf );
359
360 d->dirList.clear();
361 TQIODevice* dev = device();
362
363 if ( !dev )
364 return false;
365
366 // read dir infos
367 char buffer[ 0x200 ];
368 bool ende = false;
369 do
370 {
371 TQString name;
372 TQString symlink;
373
374 // Read header
375 TQ_LONG n = readHeader(buffer,name,symlink);
376 if (n < 0) return false;
377 if (n == 0x200)
378 {
379 bool isdir = false;
380 TQString nm;
381
382 if ( name.right(1) == "/" )
383 {
384 isdir = true;
385 name = name.left( name.length() - 1 );
386 }
387
388 int pos = name.findRev( '/' );
389 if ( pos == -1 )
390 nm = name;
391 else
392 nm = name.mid( pos + 1 );
393
394 // read access
395 buffer[ 0x6b ] = 0;
396 char *dummy;
397 const char* p = buffer + 0x64;
398 while( *p == ' ' ) ++p;
399 int access = (int)strtol( p, &dummy, 8 );
400
401 // read user and group
402 TQString user( buffer + 0x109 );
403 TQString group( buffer + 0x129 );
404
405 // read time
406 buffer[ 0x93 ] = 0;
407 p = buffer + 0x88;
408 while( *p == ' ' ) ++p;
409 int time = (int)strtol( p, &dummy, 8 );
410
411 // read type flag
412 char typeflag = buffer[ 0x9c ];
413 // '0' for files, '1' hard link, '2' symlink, '5' for directory
414 // (and 'L' for longlink filenames, 'K' for longlink symlink targets)
415 // and 'D' for GNU tar extension DUMPDIR
416 if ( typeflag == '5' )
417 isdir = true;
418
419 bool isDumpDir = false;
420 if ( typeflag == 'D' )
421 {
422 isdir = false;
423 isDumpDir = true;
424 }
425 //bool islink = ( typeflag == '1' || typeflag == '2' );
426 //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl;
427
428 if (isdir)
429 access |= S_IFDIR; // f*cking broken tar files
430
431 KArchiveEntry* e;
432 if ( isdir )
433 {
434 //kdDebug(7041) << "KTar::openArchive directory " << nm << endl;
435 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
436 }
437 else
438 {
439 // read size
440 buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
441 char *dummy;
442 const char* p = buffer + 0x7c;
443 while( *p == ' ' ) ++p;
444 int size = (int)strtol( p, &dummy, 8 );
445
446 // for isDumpDir we will skip the additional info about that dirs contents
447 if ( isDumpDir )
448 {
449 //kdDebug(7041) << "KTar::openArchive " << nm << " isDumpDir" << endl;
450 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
451 }
452 else
453 {
454
455 // Let's hack around hard links. Our classes don't support that, so make them symlinks
456 if ( typeflag == '1' )
457 {
458 kdDebug(7041) << "HARD LINK, setting size to 0 instead of " << size << endl;
459 size = 0; // no contents
460 }
461
462 //kdDebug(7041) << "KTar::openArchive file " << nm << " size=" << size << endl;
463 e = new KArchiveFile( this, nm, access, time, user, group, symlink,
464 dev->at(), size );
465 }
466
467 // Skip contents + align bytes
468 int rest = size % 0x200;
469 int skip = size + (rest ? 0x200 - rest : 0);
470 //kdDebug(7041) << "KTar::openArchive, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl;
471 if (! dev->at( dev->at() + skip ) )
472 kdWarning(7041) << "KTar::openArchive skipping " << skip << " failed" << endl;
473 }
474
475 if ( pos == -1 )
476 {
477 if ( nm == "." ) // special case
478 {
479 Q_ASSERT( isdir );
480 if ( isdir )
481 setRootDir( static_cast<KArchiveDirectory *>( e ) );
482 }
483 else
484 rootDir()->addEntry( e );
485 }
486 else
487 {
488 // In some tar files we can find dir/./file => call cleanDirPath
489 TQString path = TQDir::cleanDirPath( name.left( pos ) );
490 // Ensure container directory exists, create otherwise
491 KArchiveDirectory * d = findOrCreate( path );
492 d->addEntry( e );
493 }
494 }
495 else
496 {
497 //tqDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
498 d->tarEnd = dev->at() - n; // Remember end of archive
499 ende = true;
500 }
501 } while( !ende );
502 return true;
503}
504
505/*
506 * Writes back the changes of the temporary file
507 * to the original file.
508 * Must only be called if in IO_WriteOnly mode
509 */
510bool KTar::KTarPrivate::writeBackTempFile( const TQString & filename ) {
511 if ( ! tmpFile )
512 return true;
513
514 kdDebug(7041) << "Write temporary file to compressed file" << endl;
515 kdDebug(7041) << filename << " " << mimetype << endl;
516
517 bool forced = false;
518 if( "application/x-gzip" == mimetype
519 || "application/x-bzip2" == mimetype
520 || "application/x-lzma" == mimetype
521 || "application/x-xz" == mimetype)
522 forced = true;
523
524 TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
525 if( dev ) {
526 TQFile* file = tmpFile->file();
527 file->close();
528 if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
529 {
530 file->close();
531 delete dev;
532 return false;
533 }
534 if ( forced )
535 static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
536 TQByteArray buffer(8*1024);
537 TQ_LONG len;
538 while ( ! file->atEnd()) {
539 len = file->readBlock(buffer.data(),buffer.size());
540 dev->writeBlock(buffer.data(),len);
541 }
542 file->close();
543 dev->close();
544 delete dev;
545 }
546
547 kdDebug(7041) << "Write temporary file to compressed file done." << endl;
548 return true;
549}
550
551bool KTar::closeArchive()
552{
553 d->dirList.clear();
554
555 // If we are in write mode and had created
556 // a temporary tar file, we have to write
557 // back the changes to the original file
558 if( mode() == IO_WriteOnly)
559 return d->writeBackTempFile( m_filename );
560
561 return true;
562}
563
564bool KTar::writeDir( const TQString& name, const TQString& user, const TQString& group )
565{
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);
569#if 0
570 if ( !isOpened() )
571 {
572 kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
573 return false;
574 }
575
576 if ( !(mode() & IO_WriteOnly) )
577 {
578 kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
579 return false;
580 }
581
582 // In some tar files we can find dir/./ => call cleanDirPath
583 TQString dirName ( TQDir::cleanDirPath( name ) );
584
585 // Need trailing '/'
586 if ( dirName.right(1) != "/" )
587 dirName += "/";
588
589 if ( d->dirList.contains( dirName ) )
590 return true; // already there
591
592 char buffer[ 0x201 ];
593 memset( buffer, 0, 0x200 );
594 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
595
596 // If more than 100 chars, we need to use the LongLink trick
597 if ( dirName.length() > 99 )
598 {
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 );
603 buffer[0x200] = 0;
604 // write long name
605 device()->writeBlock( buffer, 0x200 );
606 // not even needed to reclear the buffer, tar doesn't do it
607 }
608 else
609 {
610 // Write name
611 strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
612 buffer[0x200] = 0;
613 }
614
615 fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
616
617 // Write header
618 device()->writeBlock( buffer, 0x200 );
619 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
620
621 d->dirList.append( dirName ); // contains trailing slash
622 return true; // TODO if wanted, better error control
623#endif
624}
625
626bool KTar::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
627{
628 mode_t dflt_perm = 0100644;
629 time_t the_time = time(0);
630 return prepareWriting(name,user,group,size,dflt_perm,
631 the_time,the_time,the_time);
632}
633
634bool KTar::doneWriting( uint size )
635{
636 // Write alignment
637 int rest = size % 0x200;
638 if ( mode() & IO_ReadWrite )
639 d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive
640 if ( rest )
641 {
642 char buffer[ 0x201 ];
643 for( uint i = 0; i < 0x200; ++i )
644 buffer[i] = 0;
645 TQ_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest );
646 return nwritten == 0x200 - rest;
647 }
648 return true;
649}
650
651/*** Some help from the tar sources
652struct posix_header
653{ byte offset
654 char name[100]; * 0 * 0x0
655 char mode[8]; * 100 * 0x64
656 char uid[8]; * 108 * 0x6c
657 char gid[8]; * 116 * 0x74
658 char size[12]; * 124 * 0x7c
659 char mtime[12]; * 136 * 0x88
660 char chksum[8]; * 148 * 0x94
661 char typeflag; * 156 * 0x9c
662 char linkname[100]; * 157 * 0x9d
663 char magic[6]; * 257 * 0x101
664 char version[2]; * 263 * 0x107
665 char uname[32]; * 265 * 0x109
666 char gname[32]; * 297 * 0x129
667 char devmajor[8]; * 329 * 0x149
668 char devminor[8]; * 337 * ...
669 char prefix[155]; * 345 *
670 * 500 *
671};
672*/
673
674void KTar::fillBuffer( char * buffer,
675 const char * mode, int size, time_t mtime, char typeflag,
676 const char * uname, const char * gname )
677{
678 // mode (as in stat())
679 assert( strlen(mode) == 6 );
680 strcpy( buffer+0x64, mode );
681 buffer[ 0x6a ] = ' ';
682 buffer[ 0x6b ] = '\0';
683
684 // dummy uid
685 strcpy( buffer + 0x6c, " 765 ");
686 // dummy gid
687 strcpy( buffer + 0x74, " 144 ");
688
689 // size
690 TQCString s;
691 s.sprintf("%o", size); // OCT
692 s = s.rightJustify( 11, ' ' );
693 strcpy( buffer + 0x7c, s.data() );
694 buffer[ 0x87 ] = ' '; // space-terminate (no null after)
695
696 // modification time
697 s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT
698 s = s.rightJustify( 11, ' ' );
699 strcpy( buffer + 0x88, s.data() );
700 buffer[ 0x93 ] = ' '; // space-terminate (no null after)
701
702 // spaces, replaced by the check sum later
703 buffer[ 0x94 ] = 0x20;
704 buffer[ 0x95 ] = 0x20;
705 buffer[ 0x96 ] = 0x20;
706 buffer[ 0x97 ] = 0x20;
707 buffer[ 0x98 ] = 0x20;
708 buffer[ 0x99 ] = 0x20;
709
710 /* From the tar sources :
711 Fill in the checksum field. It's formatted differently from the
712 other fields: it has [6] digits, a null, then a space -- rather than
713 digits, a space, then a null. */
714
715 buffer[ 0x9a ] = '\0';
716 buffer[ 0x9b ] = ' ';
717
718 // type flag (dir, file, link)
719 buffer[ 0x9c ] = typeflag;
720
721 // magic + version
722 strcpy( buffer + 0x101, "ustar");
723 strcpy( buffer + 0x107, "00" );
724
725 // user
726 strcpy( buffer + 0x109, uname );
727 // group
728 strcpy( buffer + 0x129, gname );
729
730 // Header check sum
731 int check = 32;
732 for( uint j = 0; j < 0x200; ++j )
733 check += buffer[j];
734 s.sprintf("%o", check ); // OCT
735 s = s.rightJustify( 7, ' ' );
736 strcpy( buffer + 0x94, s.data() );
737}
738
739void 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 );
745 int offset = 0;
746 while (namelen > 0) {
747 int chunksize = TQMIN(namelen, 0x200);
748 memcpy(buffer, name.data()+offset, chunksize);
749 // write long name
750 device()->writeBlock( buffer, 0x200 );
751 // not even needed to reclear the buffer, tar doesn't do it
752 namelen -= chunksize;
753 offset += 0x200;
754 }/*wend*/
755}
756
757bool KTar::prepareWriting(const TQString& name, const TQString& user,
758 const TQString& group, uint size, mode_t perm,
759 time_t atime, time_t mtime, time_t ctime) {
760 return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
761}
762
763bool KTar::prepareWriting_impl(const TQString &name, const TQString &user,
764 const TQString &group, uint size, mode_t perm,
765 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
766 if ( !isOpened() )
767 {
768 kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n";
769 return false;
770 }
771
772 if ( !(mode() & IO_WriteOnly) )
773 {
774 kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n";
775 return false;
776 }
777
778 // In some tar files we can find dir/./file => call cleanDirPath
779 TQString fileName ( TQDir::cleanDirPath( name ) );
780
781 /*
782 // Create toplevel dirs
783 // Commented out by David since it's not necessary, and if anybody thinks it is,
784 // he needs to implement a findOrCreate equivalent in writeDir.
785 // But as KTar and the "tar" program both handle tar files without
786 // dir entries, there's really no need for that
787 TQString tmp ( fileName );
788 int i = tmp.findRev( '/' );
789 if ( i != -1 )
790 {
791 TQString d = tmp.left( i + 1 ); // contains trailing slash
792 if ( !m_dirList.contains( d ) )
793 {
794 tmp = tmp.mid( i + 1 );
795 writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
796 }
797 }
798 */
799
800 char buffer[ 0x201 ];
801 memset( buffer, 0, 0x200 );
802 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
803
804 // provide converted stuff we need lateron
805 TQCString encodedFilename = TQFile::encodeName(fileName);
806 TQCString uname = user.local8Bit();
807 TQCString gname = group.local8Bit();
808
809 // If more than 100 chars, we need to use the LongLink trick
810 if ( fileName.length() > 99 )
811 writeLonglink(buffer,encodedFilename,'L',uname,gname);
812
813 // Write (potentially truncated) name
814 strncpy( buffer, encodedFilename, 99 );
815 buffer[99] = 0;
816 // zero out the rest (except for what gets filled anyways)
817 memset(buffer+0x9d, 0, 0x200 - 0x9d);
818
819 TQCString permstr;
820 permstr.sprintf("%o",perm);
821 permstr = permstr.rightJustify(6, ' ');
822 fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
823
824 // Write header
825 return device()->writeBlock( buffer, 0x200 ) == 0x200;
826}
827
828bool KTar::writeDir(const TQString& name, const TQString& user,
829 const TQString& group, mode_t perm,
830 time_t atime, time_t mtime, time_t ctime) {
831 return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime);
832}
833
834bool KTar::writeDir_impl(const TQString &name, const TQString &user,
835 const TQString &group, mode_t perm,
836 time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
837 if ( !isOpened() )
838 {
839 kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
840 return false;
841 }
842
843 if ( !(mode() & IO_WriteOnly) )
844 {
845 kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
846 return false;
847 }
848
849 // In some tar files we can find dir/./ => call cleanDirPath
850 TQString dirName ( TQDir::cleanDirPath( name ) );
851
852 // Need trailing '/'
853 if ( dirName.right(1) != "/" )
854 dirName += "/";
855
856 if ( d->dirList.contains( dirName ) )
857 return true; // already there
858
859 char buffer[ 0x201 ];
860 memset( buffer, 0, 0x200 );
861 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
862
863 // provide converted stuff we need lateron
864 TQCString encodedDirname = TQFile::encodeName(dirName);
865 TQCString uname = user.local8Bit();
866 TQCString gname = group.local8Bit();
867
868 // If more than 100 chars, we need to use the LongLink trick
869 if ( dirName.length() > 99 )
870 writeLonglink(buffer,encodedDirname,'L',uname,gname);
871
872 // Write (potentially truncated) name
873 strncpy( buffer, encodedDirname, 99 );
874 buffer[99] = 0;
875 // zero out the rest (except for what gets filled anyways)
876 memset(buffer+0x9d, 0, 0x200 - 0x9d);
877
878 TQCString permstr;
879 permstr.sprintf("%o",perm);
880 permstr = permstr.rightJustify(6, ' ');
881 fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
882
883 // Write header
884 device()->writeBlock( buffer, 0x200 );
885 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
886
887 d->dirList.append( dirName ); // contains trailing slash
888 return true; // TODO if wanted, better error control
889}
890
891bool 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) {
894 return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
895}
896
897bool KTar::writeSymLink_impl(const TQString &name, const TQString &target,
898 const TQString &user, const TQString &group,
899 mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
900 if ( !isOpened() )
901 {
902 kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n";
903 return false;
904 }
905
906 if ( !(mode() & IO_WriteOnly) )
907 {
908 kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n";
909 return false;
910 }
911
912 device()->flush();
913
914 // In some tar files we can find dir/./file => call cleanDirPath
915 TQString fileName ( TQDir::cleanDirPath( name ) );
916
917 char buffer[ 0x201 ];
918 memset( buffer, 0, 0x200 );
919 if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
920
921 // provide converted stuff we need lateron
922 TQCString encodedFilename = TQFile::encodeName(fileName);
923 TQCString encodedTarget = TQFile::encodeName(target);
924 TQCString uname = user.local8Bit();
925 TQCString gname = group.local8Bit();
926
927 // If more than 100 chars, we need to use the LongLink trick
928 if (target.length() > 99)
929 writeLonglink(buffer,encodedTarget,'K',uname,gname);
930 if ( fileName.length() > 99 )
931 writeLonglink(buffer,encodedFilename,'L',uname,gname);
932
933 // Write (potentially truncated) name
934 strncpy( buffer, encodedFilename, 99 );
935 buffer[99] = 0;
936 // Write (potentially truncated) symlink target
937 strncpy(buffer+0x9d, encodedTarget, 99);
938 buffer[0x9d+99] = 0;
939 // zero out the rest
940 memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
941
942 TQCString permstr;
943 permstr.sprintf("%o",perm);
944 permstr = permstr.rightJustify(6, ' ');
945 fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
946
947 // Write header
948 bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200;
949 if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
950 return retval;
951}
952
953void KTar::virtual_hook( int id, void* data ) {
954 switch (id) {
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);
960 break;
961 }
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);
967 break;
968 }
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);
974 break;
975 }
976 default:
977 KArchive::virtual_hook( id, data );
978 }/*end switch*/
979}
980
KArchiveDirectory
Represents a directory entry in a KArchive.
Definition: karchive.h:574
KArchiveEntry
A base class for entries in an KArchive.
Definition: karchive.h:396
KArchiveFile
Represents a file entry in a KArchive.
Definition: karchive.h:491
KArchive
KArchive is a base class for reading and writing archives.
Definition: karchive.h:43
KArchive::writeDir
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.
KArchive::rootDir
virtual KArchiveDirectory * rootDir()
Retrieves or create the root directory.
Definition: karchive.cpp:368
KArchive::findOrCreate
KArchiveDirectory * findOrCreate(const TQString &path)
Ensures that path exists, create otherwise.
Definition: karchive.cpp:383
KArchive::close
virtual void close()
Closes the archive.
Definition: karchive.cpp:108
KArchive::mode
int mode() const
Returns the mode in which the archive was opened.
Definition: karchive.h:90
KArchive::device
TQIODevice * device() const
The underlying device.
Definition: karchive.h:96
KArchive::isOpened
bool isOpened() const
Checks whether the archive is open.
Definition: karchive.h:83
KArchive::writeSymLink
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.
Definition: karchive.cpp:327
KArchive::prepareWriting
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...
KFilterDev
A class for reading and writing compressed data onto a device (e.g.
Definition: kfilterdev.h:37
KFilterDev::deviceForFile
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.
Definition: kfilterdev.cpp:81
KMimeType::findByPath
static Ptr findByPath(const TQString &path, mode_t mode=0, bool fast_mode=false)
Finds a KMimeType with the given _url.
Definition: kmimetype.cpp:308
KMimeType::findByFileContent
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...
Definition: kmimetype.cpp:323
KTar::setOrigFileName
void setOrigFileName(const TQCString &fileName)
Special function for setting the "original file name" in the gzip header, when writing a tar....
Definition: ktar.cpp:178
KTar::fileName
TQString fileName()
The name of the tar file, as passed to the constructor Null if you used the TQIODevice constructor.
Definition: ktar.h:77
KTar::doneWriting
virtual bool doneWriting(uint size)
Call doneWriting after writing the data.
Definition: ktar.cpp:634
KTar::openArchive
virtual bool openArchive(int mode)
Opens the archive for reading.
Definition: ktar.cpp:346
KTar::~KTar
virtual ~KTar()
If the tar ball is still opened, then it will be closed automatically by the destructor.
Definition: ktar.cpp:163
KTar::closeArchive
virtual bool closeArchive()
Closes the archive.
Definition: ktar.cpp:551
KTar::KTar
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...
Definition: ktar.cpp:59
KTar::prepareWriting
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...
Definition: ktar.cpp:626
KTar::writeDir
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.
Definition: ktar.cpp:564
TDEIO::symlink
TDEIO_EXPORT SimpleJob * symlink(const TQString &target, const KURL &dest, bool overwrite, bool showProgressInfo=true)
Create or move a symlink.
Definition: job.cpp:808
TDEIO::mimetype
TDEIO_EXPORT MimetypeJob * mimetype(const KURL &url, bool showProgressInfo=true)
Find mimetype for one file or directory.
Definition: job.cpp:1573

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.9.4
This website is maintained by Timothy Pearson.