KDECore
karchive.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000-2005 David Faure <faure@kde.org> 00003 Copyright (C) 2003 Leo Savernik <l.savernik@aon.at> 00004 00005 Moved from ktar.cpp by Roberto Teixeira <maragato@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License version 2 as published by the Free Software Foundation. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "karchive.h" 00023 #include "klimitediodevice_p.h" 00024 00025 #include <config.h> 00026 00027 #include <kdebug.h> 00028 #include <ksavefile.h> 00029 #include <kde_file.h> 00030 00031 #include <QStack> 00032 #include <QtCore/QMap> 00033 #include <QtCore/QDir> 00034 #include <QtCore/QFile> 00035 00036 #include <stdio.h> 00037 #include <stdlib.h> 00038 #include <time.h> 00039 #include <unistd.h> 00040 #include <errno.h> 00041 #include <grp.h> 00042 #include <pwd.h> 00043 #include <assert.h> 00044 #include <sys/types.h> 00045 #include <sys/stat.h> 00046 #ifdef Q_OS_UNIX 00047 #include <limits.h> // PATH_MAX 00048 #endif 00049 00050 class KArchivePrivate 00051 { 00052 public: 00053 KArchivePrivate() 00054 : rootDir( 0 ), 00055 saveFile( 0 ), 00056 dev ( 0 ), 00057 fileName(), 00058 mode( QIODevice::NotOpen ), 00059 deviceOwned( false ) 00060 {} 00061 ~KArchivePrivate() 00062 { 00063 delete saveFile; 00064 delete rootDir; 00065 } 00066 void abortWriting(); 00067 00068 KArchiveDirectory* rootDir; 00069 KSaveFile* saveFile; 00070 QIODevice * dev; 00071 QString fileName; 00072 QIODevice::OpenMode mode; 00073 bool deviceOwned; // if true, we (KArchive) own dev and must delete it 00074 }; 00075 00076 00080 00081 KArchive::KArchive( const QString& fileName ) 00082 : d(new KArchivePrivate) 00083 { 00084 Q_ASSERT( !fileName.isEmpty() ); 00085 d->fileName = fileName; 00086 // This constructor leaves the device set to 0. 00087 // This is for the use of KSaveFile, see open(). 00088 } 00089 00090 KArchive::KArchive( QIODevice * dev ) 00091 : d(new KArchivePrivate) 00092 { 00093 d->dev = dev; 00094 } 00095 00096 KArchive::~KArchive() 00097 { 00098 if ( isOpen() ) 00099 close(); // WARNING: won't call the virtual method close in the derived class!!! 00100 00101 delete d; 00102 } 00103 00104 bool KArchive::open( QIODevice::OpenMode mode ) 00105 { 00106 Q_ASSERT( mode != QIODevice::NotOpen ); 00107 00108 if ( isOpen() ) 00109 close(); 00110 00111 if ( !d->fileName.isEmpty() ) 00112 { 00113 Q_ASSERT( !d->dev ); 00114 if ( !createDevice( mode ) ) 00115 return false; 00116 } 00117 00118 Q_ASSERT( d->dev ); 00119 00120 if ( !d->dev->isOpen() && !d->dev->open( mode ) ) 00121 return false; 00122 00123 d->mode = mode; 00124 00125 Q_ASSERT( !d->rootDir ); 00126 d->rootDir = 0; 00127 00128 return openArchive( mode ); 00129 } 00130 00131 bool KArchive::createDevice( QIODevice::OpenMode mode ) 00132 { 00133 switch( mode ) { 00134 case QIODevice::WriteOnly: 00135 if ( !d->fileName.isEmpty() ) { 00136 // The use of KSaveFile can't be done in the ctor (no mode known yet) 00137 //kDebug() << "Writing to a file using KSaveFile"; 00138 d->saveFile = new KSaveFile( d->fileName ); 00139 if ( !d->saveFile->open() ) { 00140 kWarning() << "KSaveFile creation for " << d->fileName << " failed, " << d->saveFile->errorString(); 00141 delete d->saveFile; 00142 d->saveFile = 0; 00143 return false; 00144 } 00145 d->dev = d->saveFile; 00146 Q_ASSERT( d->dev ); 00147 } 00148 break; 00149 case QIODevice::ReadOnly: 00150 case QIODevice::ReadWrite: 00151 // ReadWrite mode still uses QFile for now; we'd need to copy to the tempfile, in fact. 00152 if ( !d->fileName.isEmpty() ) { 00153 d->dev = new QFile( d->fileName ); 00154 d->deviceOwned = true; 00155 } 00156 break; // continued below 00157 default: 00158 kWarning() << "Unsupported mode " << d->mode; 00159 return false; 00160 } 00161 return true; 00162 } 00163 00164 bool KArchive::close() 00165 { 00166 if ( !isOpen() ) 00167 return false; // already closed (return false or true? arguable...) 00168 00169 // moved by holger to allow kzip to write the zip central dir 00170 // to the file in closeArchive() 00171 // DF: added d->dev so that we skip closeArchive if saving aborted. 00172 bool closeSucceeded = true; 00173 if ( d->dev ) { 00174 closeSucceeded = closeArchive(); 00175 if ( d->mode == QIODevice::WriteOnly && !closeSucceeded ) 00176 d->abortWriting(); 00177 } 00178 00179 if ( d->dev ) 00180 d->dev->close(); 00181 00182 if ( d->deviceOwned ) { 00183 delete d->dev; // we created it ourselves in open() 00184 } 00185 if ( d->saveFile ) { 00186 closeSucceeded = d->saveFile->finalize(); 00187 delete d->saveFile; 00188 d->saveFile = 0; 00189 } 00190 00191 delete d->rootDir; 00192 d->rootDir = 0; 00193 d->mode = QIODevice::NotOpen; 00194 d->dev = 0; 00195 return closeSucceeded; 00196 } 00197 00198 const KArchiveDirectory* KArchive::directory() const 00199 { 00200 // rootDir isn't const so that parsing-on-demand is possible 00201 return const_cast<KArchive *>(this)->rootDir(); 00202 } 00203 00204 00205 bool KArchive::addLocalFile( const QString& fileName, const QString& destName ) 00206 { 00207 QFileInfo fileInfo( fileName ); 00208 if ( !fileInfo.isFile() && !fileInfo.isSymLink() ) 00209 { 00210 kWarning() << fileName << "doesn't exist or is not a regular file."; 00211 return false; 00212 } 00213 00214 KDE_struct_stat fi; 00215 if (KDE::lstat(fileName,&fi) == -1) { 00216 kWarning() << "stat'ing" << fileName 00217 << "failed:" << strerror(errno); 00218 return false; 00219 } 00220 00221 if (fileInfo.isSymLink()) { 00222 QString symLinkTarget; 00223 // Do NOT use fileInfo.readLink() for unix symlinks! 00224 // It returns the -full- path to the target, while we want the target string "as is". 00225 #if defined(Q_OS_UNIX) && !defined(Q_OS_OS2EMX) 00226 const QByteArray encodedFileName = QFile::encodeName(fileName); 00227 QByteArray s; 00228 #if defined(PATH_MAX) 00229 s.resize(PATH_MAX+1); 00230 #else 00231 int path_max = pathconf(encodedFileName.data(), _PC_PATH_MAX); 00232 if (path_max <= 0) { 00233 path_max = 4096; 00234 } 00235 s.resize(path_max); 00236 #endif 00237 int len = readlink(encodedFileName.data(), s.data(), s.size() - 1); 00238 if ( len >= 0 ) { 00239 s[len] = '\0'; 00240 symLinkTarget = QFile::decodeName(s); 00241 } 00242 #endif 00243 if (symLinkTarget.isEmpty()) // Mac or Windows 00244 symLinkTarget = fileInfo.symLinkTarget(); 00245 return writeSymLink(destName, symLinkTarget, fileInfo.owner(), 00246 fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime, 00247 fi.st_ctime); 00248 }/*end if*/ 00249 00250 qint64 size = fileInfo.size(); 00251 00252 // the file must be opened before prepareWriting is called, otherwise 00253 // if the opening fails, no content will follow the already written 00254 // header and the tar file is effectively f*cked up 00255 QFile file( fileName ); 00256 if ( !file.open( QIODevice::ReadOnly ) ) 00257 { 00258 kWarning() << "couldn't open file " << fileName; 00259 return false; 00260 } 00261 00262 if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size, 00263 fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) ) 00264 { 00265 kWarning() << " prepareWriting" << destName << "failed"; 00266 return false; 00267 } 00268 00269 // Read and write data in chunks to minimize memory usage 00270 QByteArray array; 00271 array.resize( int( qMin( qint64( 1024 * 1024 ), size ) ) ); 00272 qint64 n; 00273 qint64 total = 0; 00274 while ( ( n = file.read( array.data(), array.size() ) ) > 0 ) 00275 { 00276 if ( !writeData( array.data(), n ) ) 00277 { 00278 kWarning() << "writeData failed"; 00279 return false; 00280 } 00281 total += n; 00282 } 00283 Q_ASSERT( total == size ); 00284 00285 if ( !finishWriting( size ) ) 00286 { 00287 kWarning() << "finishWriting failed"; 00288 return false; 00289 } 00290 return true; 00291 } 00292 00293 bool KArchive::addLocalDirectory( const QString& path, const QString& destName ) 00294 { 00295 QDir dir( path ); 00296 if ( !dir.exists() ) 00297 return false; 00298 dir.setFilter(dir.filter() | QDir::Hidden); 00299 const QStringList files = dir.entryList(); 00300 for ( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it ) 00301 { 00302 if ( *it != QLatin1String(".") && *it != QLatin1String("..") ) 00303 { 00304 QString fileName = path + QLatin1Char('/') + *it; 00305 // kDebug() << "storing " << fileName; 00306 QString dest = destName.isEmpty() ? *it : (destName + QLatin1Char('/') + *it); 00307 QFileInfo fileInfo( fileName ); 00308 00309 if ( fileInfo.isFile() || fileInfo.isSymLink() ) 00310 addLocalFile( fileName, dest ); 00311 else if ( fileInfo.isDir() ) 00312 addLocalDirectory( fileName, dest ); 00313 // We omit sockets 00314 } 00315 } 00316 return true; 00317 } 00318 00319 bool KArchive::writeFile( const QString& name, const QString& user, 00320 const QString& group, const char* data, qint64 size, 00321 mode_t perm, time_t atime, time_t mtime, time_t ctime ) 00322 { 00323 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) ) 00324 { 00325 kWarning() << "prepareWriting failed"; 00326 return false; 00327 } 00328 00329 // Write data 00330 // Note: if data is 0L, don't call write, it would terminate the KFilterDev 00331 if ( data && size && !writeData( data, size ) ) 00332 { 00333 kWarning() << "writeData failed"; 00334 return false; 00335 } 00336 00337 if ( !finishWriting( size ) ) 00338 { 00339 kWarning() << "finishWriting failed"; 00340 return false; 00341 } 00342 return true; 00343 } 00344 00345 bool KArchive::writeData( const char* data, qint64 size ) 00346 { 00347 bool ok = device()->write( data, size ) == size; 00348 if ( !ok ) 00349 d->abortWriting(); 00350 return ok; 00351 } 00352 00353 // The writeDir -> doWriteDir pattern allows to avoid propagating the default 00354 // values into all virtual methods of subclasses, and it allows more extensibility: 00355 // if a new argument is needed, we can add a writeDir overload which stores the 00356 // additional argument in the d pointer, and doWriteDir reimplementations can fetch 00357 // it from there. 00358 00359 bool KArchive::writeDir( const QString& name, const QString& user, const QString& group, 00360 mode_t perm, time_t atime, 00361 time_t mtime, time_t ctime ) 00362 { 00363 return doWriteDir( name, user, group, perm | 040000, atime, mtime, ctime ); 00364 } 00365 00366 bool KArchive::writeSymLink(const QString &name, const QString &target, 00367 const QString &user, const QString &group, 00368 mode_t perm, time_t atime, 00369 time_t mtime, time_t ctime ) 00370 { 00371 return doWriteSymLink( name, target, user, group, perm, atime, mtime, ctime ); 00372 } 00373 00374 00375 bool KArchive::prepareWriting( const QString& name, const QString& user, 00376 const QString& group, qint64 size, 00377 mode_t perm, time_t atime, 00378 time_t mtime, time_t ctime ) 00379 { 00380 bool ok = doPrepareWriting( name, user, group, size, perm, atime, mtime, ctime ); 00381 if ( !ok ) 00382 d->abortWriting(); 00383 return ok; 00384 } 00385 00386 bool KArchive::finishWriting( qint64 size ) 00387 { 00388 return doFinishWriting( size ); 00389 } 00390 00391 KArchiveDirectory * KArchive::rootDir() 00392 { 00393 if ( !d->rootDir ) 00394 { 00395 //kDebug() << "Making root dir "; 00396 struct passwd* pw = getpwuid( getuid() ); 00397 struct group* grp = getgrgid( getgid() ); 00398 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() ); 00399 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() ); 00400 00401 d->rootDir = new KArchiveDirectory( this, QLatin1String("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString() ); 00402 } 00403 return d->rootDir; 00404 } 00405 00406 KArchiveDirectory * KArchive::findOrCreate( const QString & path ) 00407 { 00408 //kDebug() << path; 00409 if ( path.isEmpty() || path == QLatin1String("/") || path == QLatin1String(".") ) // root dir => found 00410 { 00411 //kDebug() << "returning rootdir"; 00412 return rootDir(); 00413 } 00414 // Important note : for tar files containing absolute paths 00415 // (i.e. beginning with "/"), this means the leading "/" will 00416 // be removed (no KDirectory for it), which is exactly the way 00417 // the "tar" program works (though it displays a warning about it) 00418 // See also KArchiveDirectory::entry(). 00419 00420 // Already created ? => found 00421 const KArchiveEntry* ent = rootDir()->entry( path ); 00422 if ( ent ) 00423 { 00424 if ( ent->isDirectory() ) 00425 //kDebug() << "found it"; 00426 return (KArchiveDirectory *) ent; 00427 else 00428 kWarning() << "Found" << path << "but it's not a directory"; 00429 } 00430 00431 // Otherwise go up and try again 00432 int pos = path.lastIndexOf( QLatin1Char('/') ); 00433 KArchiveDirectory * parent; 00434 QString dirname; 00435 if ( pos == -1 ) // no more slash => create in root dir 00436 { 00437 parent = rootDir(); 00438 dirname = path; 00439 } 00440 else 00441 { 00442 QString left = path.left( pos ); 00443 dirname = path.mid( pos + 1 ); 00444 parent = findOrCreate( left ); // recursive call... until we find an existing dir. 00445 } 00446 00447 //kDebug() << "found parent " << parent->name() << " adding " << dirname << " to ensure " << path; 00448 // Found -> add the missing piece 00449 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(), 00450 d->rootDir->date(), d->rootDir->user(), 00451 d->rootDir->group(), QString() ); 00452 parent->addEntry( e ); 00453 return e; // now a directory to <path> exists 00454 } 00455 00456 void KArchive::setDevice( QIODevice * dev ) 00457 { 00458 if ( d->deviceOwned ) 00459 delete d->dev; 00460 d->dev = dev; 00461 d->deviceOwned = false; 00462 } 00463 00464 void KArchive::setRootDir( KArchiveDirectory *rootDir ) 00465 { 00466 Q_ASSERT( !d->rootDir ); // Call setRootDir only once during parsing please ;) 00467 d->rootDir = rootDir; 00468 } 00469 00470 QIODevice::OpenMode KArchive::mode() const 00471 { 00472 return d->mode; 00473 } 00474 00475 QIODevice * KArchive::device() const 00476 { 00477 return d->dev; 00478 } 00479 00480 bool KArchive::isOpen() const 00481 { 00482 return d->mode != QIODevice::NotOpen; 00483 } 00484 00485 QString KArchive::fileName() const 00486 { 00487 return d->fileName; 00488 } 00489 00490 void KArchivePrivate::abortWriting() 00491 { 00492 if ( saveFile ) { 00493 saveFile->abort(); 00494 delete saveFile; 00495 saveFile = 0; 00496 dev = 0; 00497 } 00498 } 00499 00503 00504 class KArchiveEntryPrivate 00505 { 00506 public: 00507 KArchiveEntryPrivate( KArchive* _archive, const QString& _name, int _access, 00508 int _date, const QString& _user, const QString& _group, 00509 const QString& _symlink) : 00510 name(_name), 00511 date(_date), 00512 access(_access), 00513 user(_user), 00514 group(_group), 00515 symlink(_symlink), 00516 archive(_archive) 00517 {} 00518 QString name; 00519 int date; 00520 mode_t access; 00521 QString user; 00522 QString group; 00523 QString symlink; 00524 KArchive* archive; 00525 }; 00526 00527 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date, 00528 const QString& user, const QString& group, const 00529 QString& symlink) : 00530 d(new KArchiveEntryPrivate(t,name,access,date,user,group,symlink)) 00531 { 00532 } 00533 00534 KArchiveEntry::~KArchiveEntry() 00535 { 00536 delete d; 00537 } 00538 00539 QDateTime KArchiveEntry::datetime() const 00540 { 00541 QDateTime datetimeobj; 00542 datetimeobj.setTime_t( d->date ); 00543 return datetimeobj; 00544 } 00545 00546 int KArchiveEntry::date() const 00547 { 00548 return d->date; 00549 } 00550 00551 QString KArchiveEntry::name() const 00552 { 00553 return d->name; 00554 } 00555 00556 mode_t KArchiveEntry::permissions() const 00557 { 00558 return d->access; 00559 } 00560 00561 QString KArchiveEntry::user() const 00562 { 00563 return d->user; 00564 } 00565 00566 QString KArchiveEntry::group() const 00567 { 00568 return d->group; 00569 } 00570 00571 QString KArchiveEntry::symLinkTarget() const 00572 { 00573 return d->symlink; 00574 } 00575 00576 bool KArchiveEntry::isFile() const 00577 { 00578 return false; 00579 } 00580 00581 bool KArchiveEntry::isDirectory() const 00582 { 00583 return false; 00584 } 00585 00586 KArchive* KArchiveEntry::archive() const 00587 { 00588 return d->archive; 00589 } 00590 00594 00595 class KArchiveFilePrivate 00596 { 00597 public: 00598 KArchiveFilePrivate( qint64 _pos, qint64 _size ) : 00599 pos(_pos), 00600 size(_size) 00601 {} 00602 qint64 pos; 00603 qint64 size; 00604 }; 00605 00606 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date, 00607 const QString& user, const QString& group, 00608 const QString & symlink, 00609 qint64 pos, qint64 size ) 00610 : KArchiveEntry( t, name, access, date, user, group, symlink ), 00611 d( new KArchiveFilePrivate(pos, size) ) 00612 { 00613 } 00614 00615 KArchiveFile::~KArchiveFile() 00616 { 00617 delete d; 00618 } 00619 00620 qint64 KArchiveFile::position() const 00621 { 00622 return d->pos; 00623 } 00624 00625 qint64 KArchiveFile::size() const 00626 { 00627 return d->size; 00628 } 00629 00630 void KArchiveFile::setSize( qint64 s ) 00631 { 00632 d->size = s; 00633 } 00634 00635 QByteArray KArchiveFile::data() const 00636 { 00637 bool ok = archive()->device()->seek( d->pos ); 00638 if (!ok) { 00639 kWarning() << "Failed to sync to" << d->pos << "to read" << name(); 00640 } 00641 00642 // Read content 00643 QByteArray arr; 00644 if ( d->size ) 00645 { 00646 arr = archive()->device()->read( d->size ); 00647 Q_ASSERT( arr.size() == d->size ); 00648 } 00649 return arr; 00650 } 00651 00652 QIODevice * KArchiveFile::createDevice() const 00653 { 00654 return new KLimitedIODevice( archive()->device(), d->pos, d->size ); 00655 } 00656 00657 bool KArchiveFile::isFile() const 00658 { 00659 return true; 00660 } 00661 00662 void KArchiveFile::copyTo(const QString& dest) const 00663 { 00664 QFile f( dest + QLatin1Char('/') + name() ); 00665 if ( f.open( QIODevice::ReadWrite | QIODevice::Truncate ) ) 00666 { 00667 QIODevice* inputDev = createDevice(); 00668 00669 // Read and write data in chunks to minimize memory usage 00670 const qint64 chunkSize = 1024 * 1024; 00671 qint64 remainingSize = d->size; 00672 QByteArray array; 00673 array.resize( int( qMin( chunkSize, remainingSize ) ) ); 00674 00675 while ( remainingSize > 0 ) { 00676 const qint64 currentChunkSize = qMin( chunkSize, remainingSize ); 00677 const qint64 n = inputDev->read( array.data(), currentChunkSize ); 00678 Q_ASSERT( n == currentChunkSize ); 00679 f.write( array.data(), currentChunkSize ); 00680 remainingSize -= currentChunkSize; 00681 } 00682 f.close(); 00683 00684 delete inputDev; 00685 } 00686 } 00687 00691 00692 class KArchiveDirectoryPrivate 00693 { 00694 public: 00695 ~KArchiveDirectoryPrivate() 00696 { 00697 qDeleteAll(entries); 00698 } 00699 QHash<QString, KArchiveEntry *> entries; 00700 }; 00701 00702 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access, 00703 int date, 00704 const QString& user, const QString& group, 00705 const QString &symlink) 00706 : KArchiveEntry( t, name, access, date, user, group, symlink ), 00707 d( new KArchiveDirectoryPrivate ) 00708 { 00709 } 00710 00711 KArchiveDirectory::~KArchiveDirectory() 00712 { 00713 delete d; 00714 } 00715 00716 QStringList KArchiveDirectory::entries() const 00717 { 00718 return d->entries.keys(); 00719 } 00720 00721 const KArchiveEntry* KArchiveDirectory::entry( const QString& _name ) const 00722 { 00723 QString name = QDir::cleanPath(_name); 00724 int pos = name.indexOf( QLatin1Char('/') ); 00725 if ( pos == 0 ) // ouch absolute path (see also KArchive::findOrCreate) 00726 { 00727 if (name.length()>1) 00728 { 00729 name = name.mid( 1 ); // remove leading slash 00730 pos = name.indexOf( QLatin1Char('/') ); // look again 00731 } 00732 else // "/" 00733 return this; 00734 } 00735 // trailing slash ? -> remove 00736 if ( pos != -1 && pos == name.length()-1 ) 00737 { 00738 name = name.left( pos ); 00739 pos = name.indexOf( QLatin1Char('/') ); // look again 00740 } 00741 if ( pos != -1 ) 00742 { 00743 const QString left = name.left(pos); 00744 const QString right = name.mid(pos + 1); 00745 00746 //kDebug() << "left=" << left << "right=" << right; 00747 00748 const KArchiveEntry* e = d->entries.value( left ); 00749 if ( !e || !e->isDirectory() ) 00750 return 0; 00751 return static_cast<const KArchiveDirectory*>(e)->entry( right ); 00752 } 00753 00754 return d->entries.value( name ); 00755 } 00756 00757 void KArchiveDirectory::addEntry( KArchiveEntry* entry ) 00758 { 00759 if( entry->name().isEmpty() ) 00760 return; 00761 00762 if( d->entries.value( entry->name() ) ) { 00763 kWarning() << "directory " << name() 00764 << "has entry" << entry->name() << "already"; 00765 } 00766 d->entries.insert( entry->name(), entry ); 00767 } 00768 00769 bool KArchiveDirectory::isDirectory() const 00770 { 00771 return true; 00772 } 00773 00774 static bool sortByPosition( const KArchiveFile* file1, const KArchiveFile* file2 ) { 00775 return file1->position() < file2->position(); 00776 } 00777 00778 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const 00779 { 00780 QDir root; 00781 00782 QList<const KArchiveFile*> fileList; 00783 QMap<qint64, QString> fileToDir; 00784 00785 // placeholders for iterated items 00786 QStack<const KArchiveDirectory *> dirStack; 00787 QStack<QString> dirNameStack; 00788 00789 dirStack.push( this ); // init stack at current directory 00790 dirNameStack.push( dest ); // ... with given path 00791 do { 00792 const KArchiveDirectory* curDir = dirStack.pop(); 00793 const QString curDirName = dirNameStack.pop(); 00794 root.mkdir(curDirName); 00795 00796 const QStringList dirEntries = curDir->entries(); 00797 for ( QStringList::const_iterator it = dirEntries.begin(); it != dirEntries.end(); ++it ) { 00798 const KArchiveEntry* curEntry = curDir->entry(*it); 00799 if (!curEntry->symLinkTarget().isEmpty()) { 00800 const QString linkName = curDirName+QLatin1Char('/')+curEntry->name(); 00801 #ifdef Q_OS_UNIX 00802 if (!::symlink(curEntry->symLinkTarget().toLocal8Bit(), linkName.toLocal8Bit())) { 00803 kDebug() << "symlink(" << curEntry->symLinkTarget() << ',' << linkName << ") failed:" << strerror(errno); 00804 } 00805 #else 00806 // TODO - how to create symlinks on other platforms? 00807 #endif 00808 } else { 00809 if ( curEntry->isFile() ) { 00810 const KArchiveFile* curFile = dynamic_cast<const KArchiveFile*>( curEntry ); 00811 if (curFile) { 00812 fileList.append( curFile ); 00813 fileToDir.insert( curFile->position(), curDirName ); 00814 } 00815 } 00816 00817 if ( curEntry->isDirectory() && recursiveCopy ) { 00818 const KArchiveDirectory *ad = dynamic_cast<const KArchiveDirectory*>( curEntry ); 00819 if (ad) { 00820 dirStack.push( ad ); 00821 dirNameStack.push( curDirName + QLatin1Char('/') + curEntry->name() ); 00822 } 00823 } 00824 } 00825 } 00826 } while (!dirStack.isEmpty()); 00827 00828 qSort( fileList.begin(), fileList.end(), sortByPosition ); // sort on d->pos, so we have a linear access 00829 00830 for ( QList<const KArchiveFile*>::const_iterator it = fileList.constBegin(), end = fileList.constEnd() ; 00831 it != end ; ++it ) { 00832 const KArchiveFile* f = *it; 00833 qint64 pos = f->position(); 00834 f->copyTo( fileToDir[pos] ); 00835 } 00836 } 00837 00838 void KArchive::virtual_hook( int, void* ) 00839 { /*BASE::virtual_hook( id, data )*/; } 00840 00841 void KArchiveEntry::virtual_hook( int, void* ) 00842 { /*BASE::virtual_hook( id, data );*/ } 00843 00844 void KArchiveFile::virtual_hook( int id, void* data ) 00845 { KArchiveEntry::virtual_hook( id, data ); } 00846 00847 void KArchiveDirectory::virtual_hook( int id, void* data ) 00848 { KArchiveEntry::virtual_hook( id, data ); }
KDE 4.6 API Reference