# kio Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# karchive.cpp

    00001 /* This file is part of the KDE libraries
    00002    Copyright (C) 2000 David Faure <faure@kde.org>
    00003
    00004    Moved from ktar.cpp by Roberto Teixeira <maragato@kde.org>
    00005
    00006    This library is free software; you can redistribute it and/or
    00007    modify it under the terms of the GNU Library General Public
    00008    License version 2 as published by the Free Software Foundation.
    00009
    00010    This library is distributed in the hope that it will be useful,
    00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00013    Library General Public License for more details.
    00014
    00015    You should have received a copy of the GNU Library General Public License
    00016    along with this library; see the file COPYING.LIB.  If not, write to
    00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00018    Boston, MA 02111-1307, USA.
    00019 */
    00020
    00021 #include <stdio.h>
    00022 #include <stdlib.h>
    00023 #include <time.h>
    00024 #include <unistd.h>
    00025 #include <grp.h>
    00026 #include <pwd.h>
    00027 #include <assert.h>
    00028
    00029 #include <qptrlist.h>
    00030 #include <qptrstack.h>
    00031 #include <qvaluestack.h>
    00032 #include <qmap.h>
    00033 #include <qcstring.h>
    00034 #include <qdir.h>
    00035 #include <qfile.h>
    00036
    00037 #include <kdebug.h>
    00038 #include <kfilterdev.h>
    00039 #include <kfilterbase.h>
    00040
    00041 #include "karchive.h"
    00042 #include "klimitediodevice.h"
    00043
    00044 template class QDict<KArchiveEntry>;
    00045
    00046
    00047 class KArchive::KArchivePrivate
    00048 {
    00049 public:
    00050     KArchiveDirectory* rootDir;
    00051 };
    00052
    00053 class PosSortedPtrList : public QPtrList<KArchiveFile> {
    00054 protected:
    00055     int compareItems( QPtrCollection::Item i1,
    00056                       QPtrCollection::Item i2 )
    00057     {
    00058         int pos1 = static_cast<KArchiveFile*>( i1 )->position();
    00059         int pos2 = static_cast<KArchiveFile*>( i2 )->position();
    00060         return ( pos1 - pos2 );
    00061     }
    00062 };
    00063
    00064
    00068
    00069 KArchive::KArchive( QIODevice * dev )
    00070 {
    00071     d = new KArchivePrivate;
    00072     d->rootDir = 0;
    00073     m_dev = dev;
    00074     m_open = false;
    00075 }
    00076
    00077 KArchive::~KArchive()
    00078 {
    00079     if ( m_open )
    00080         close();
    00081     delete d->rootDir;
    00082     delete d;
    00083 }
    00084
    00085 bool KArchive::open( int mode )
    00086 {
    00087     if(0 == m_dev)
    00088         return false; // Fail w/o segfaulting if the device is no good
    00089
    00090     if ( !m_dev->open( mode ) )
    00091         return false;
    00092
    00093     if ( m_open )
    00094         close();
    00095
    00096     m_mode = mode;
    00097     m_open = true;
    00098
    00099     Q_ASSERT( d->rootDir == 0L );
    00100     d->rootDir = 0L;
    00101
    00102     return openArchive( mode );
    00103 }
    00104
    00105 void KArchive::close()
    00106 {
    00107     if ( !m_open )
    00108         return;
    00109     // moved by holger to allow kzip to write the zip central dir
    00110     // to the file in closeArchive()
    00111     closeArchive();
    00112
    00113     m_dev->close();
    00114
    00115     delete d->rootDir;
    00116     d->rootDir = 0;
    00117     m_open = false;
    00118 }
    00119
    00120 const KArchiveDirectory* KArchive::directory() const
    00121 {
    00122     // rootDir isn't const so that parsing-on-demand is possible
    00123     return const_cast<KArchive *>(this)->rootDir();
    00124 }
    00125
    00126 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
    00127 {
    00128
    00129     if ( !prepareWriting( name, user, group, size ) )
    00130     {
    00131         kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
    00132         return false;
    00133     }
    00134
    00135     // Write data
    00136     // Note: if data is 0L, don't call writeBlock, it would terminate the KFilterDev
    00137     if ( data && device()->writeBlock( data, size ) != (int)size )
    00138     {
    00139         kdWarning() << "KArchive::writeFile writeBlock failed" << endl;
    00140         return false;
    00141     }
    00142
    00143     if ( ! doneWriting( size ) )
    00144     {
    00145         kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
    00146         return false;
    00147     }
    00148     return true;
    00149 }
    00150
    00151 KArchiveDirectory * KArchive::rootDir()
    00152 {
    00153     if ( !d->rootDir )
    00154     {
    00155         //kdDebug() << "Making root dir " << endl;
    00156         struct passwd* pw =  getpwuid( getuid() );
    00157         struct group* grp = getgrgid( getgid() );
    00158         QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
    00159         QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
    00160
    00161         d->rootDir = new KArchiveDirectory( this, QString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString::null );
    00162     }
    00163     return d->rootDir;
    00164 }
    00165
    00166 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
    00167 {
    00168     //kdDebug() << "KArchive::findOrCreate " << path << endl;
    00169     if ( path == "" || path == "/" || path == "." ) // root dir => found
    00170     {
    00171         //kdDebug() << "KArchive::findOrCreate returning rootdir" << endl;
    00172         return rootDir();
    00173     }
    00174     // Important note : for tar files containing absolute paths
    00175     // (i.e. beginning with "/"), this means the leading "/" will
    00176     // be removed (no KDirectory for it), which is exactly the way
    00177     // the "tar" program works (though it displays a warning about it)
    00178     // See also KArchiveDirectory::entry().
    00179
    00180     // Already created ? => found
    00181     KArchiveEntry* ent = rootDir()->entry( path );
    00182     if ( ent && ent->isDirectory() )
    00183     {
    00184         //kdDebug() << "KArchive::findOrCreate found it" << endl;
    00185         return (KArchiveDirectory *) ent;
    00186     }
    00187
    00188     // Otherwise go up and try again
    00189     int pos = path.findRev( '/' );
    00190     KArchiveDirectory * parent;
    00191     QString dirname;
    00192     if ( pos == -1 ) // no more slash => create in root dir
    00193     {
    00194         parent =  rootDir();
    00195         dirname = path;
    00196     }
    00197     else
    00198     {
    00199         QString left = path.left( pos );
    00200         dirname = path.mid( pos + 1 );
    00201         parent = findOrCreate( left ); // recursive call... until we find an existing dir.
    00202     }
    00203
    00204     //kdDebug() << "KTar : found parent " << parent->name() << " adding " << dirname << " to ensure " << path << endl;
    00205     // Found -> add the missing piece
    00206     KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
    00207                                                    d->rootDir->date(), d->rootDir->user(),
    00208                                                    d->rootDir->group(), QString::null );
    00209     parent->addEntry( e );
    00210     return e; // now a directory to <path> exists
    00211 }
    00212
    00213 void KArchive::setDevice( QIODevice * dev )
    00214 {
    00215     m_dev = dev;
    00216 }
    00217
    00218 void KArchive::setRootDir( KArchiveDirectory *rootDir )
    00219 {
    00220     Q_ASSERT( !d->rootDir ); // Call setRootDir only once during parsing please ;)
    00221     d->rootDir = rootDir;
    00222 }
    00223
    00227 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
    00228                       const QString& user, const QString& group, const
    00229                       QString& symlink)
    00230 {
    00231   m_name = name;
    00232   m_access = access;
    00233   m_date = date;
    00234   m_user = user;
    00235   m_group = group;
    00236   m_symlink = symlink;
    00237   m_archive = t;
    00238
    00239 }
    00240
    00241 QDateTime KArchiveEntry::datetime() const
    00242 {
    00243   QDateTime d;
    00244   d.setTime_t( m_date );
    00245   return d;
    00246 }
    00247
    00251
    00252 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
    00253                     const QString& user, const QString& group,
    00254                     const QString & symlink,
    00255                     int pos, int size )
    00256   : KArchiveEntry( t, name, access, date, user, group, symlink )
    00257 {
    00258   m_pos = pos;
    00259   m_size = size;
    00260 }
    00261
    00262 int KArchiveFile::position() const
    00263 {
    00264   return m_pos;
    00265 }
    00266
    00267 int KArchiveFile::size() const
    00268 {
    00269   return m_size;
    00270 }
    00271
    00272 QByteArray KArchiveFile::data() const
    00273 {
    00274   archive()->device()->at( m_pos );
    00275
    00276   // Read content
    00277   QByteArray arr( m_size );
    00278   if ( m_size )
    00279   {
    00280     assert( arr.data() );
    00281     int n = archive()->device()->readBlock( arr.data(), m_size );
    00282     if ( n != m_size )
    00283       arr.resize( n );
    00284   }
    00285   return arr;
    00286 }
    00287
    00288 // ** This should be a virtual method, and this code should be in ktar.cpp
    00289 QIODevice *KArchiveFile::device() const
    00290 {
    00291     return new KLimitedIODevice( archive()->device(), m_pos, m_size );
    00292 }
    00293
    00294 void KArchiveFile::copyTo(const QString& dest) const
    00295 {
    00296   QFile f( dest + "/"  + name() );
    00297   f.open( IO_ReadWrite | IO_Truncate );
    00298   f.writeBlock( data() );
    00299   f.close();
    00300 }
    00301
    00305
    00306
    00307 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
    00308                               int date,
    00309                               const QString& user, const QString& group,
    00310                               const QString &symlink)
    00311   : KArchiveEntry( t, name, access, date, user, group, symlink )
    00312 {
    00313   m_entries.setAutoDelete( true );
    00314 }
    00315
    00316 QStringList KArchiveDirectory::entries() const
    00317 {
    00318   QStringList l;
    00319
    00320   QDictIterator<KArchiveEntry> it( m_entries );
    00321   for( ; it.current(); ++it )
    00322     l.append( it.currentKey() );
    00323
    00324   return l;
    00325 }
    00326
    00327 KArchiveEntry* KArchiveDirectory::entry( QString name )
    00328   // not "const QString & name" since we want a local copy
    00329   // (to remove leading slash if any)
    00330 {
    00331   int pos = name.find( '/' );
    00332   if ( pos == 0 ) // ouch absolute path (see also KArchive::findOrCreate)
    00333   {
    00334     if (name.length()>1)
    00335     {
    00336       name = name.mid( 1 ); // remove leading slash
    00337       pos = name.find( '/' ); // look again
    00338     }
    00339     else // "/"
    00340       return this;
    00341   }
    00342   // trailing slash ? -> remove
    00343   if ( pos != -1 && pos == (int)name.length()-1 )
    00344   {
    00345     name = name.left( pos );
    00346     pos = name.find( '/' ); // look again
    00347   }
    00348   if ( pos != -1 )
    00349   {
    00350     QString left = name.left( pos );
    00351     QString right = name.mid( pos + 1 );
    00352
    00353     //kdDebug() << "KArchiveDirectory::entry left=" << left << " right=" << right << endl;
    00354
    00355     KArchiveEntry* e = m_entries[ left ];
    00356     if ( !e || !e->isDirectory() )
    00357       return 0;
    00358     return ((KArchiveDirectory*)e)->entry( right );
    00359   }
    00360
    00361   return m_entries[ name ];
    00362 }
    00363
    00364 const KArchiveEntry* KArchiveDirectory::entry( QString name ) const
    00365 {
    00366   return ((KArchiveDirectory*)this)->entry( name );
    00367 }
    00368
    00369 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
    00370 {
    00371   Q_ASSERT( !entry->name().isEmpty() );
    00372   m_entries.insert( entry->name(), entry );
    00373 }
    00374
    00375 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
    00376 {
    00377   QDir root;
    00378
    00379   PosSortedPtrList fileList;
    00380   QMap<int, QString> fileToDir;
    00381
    00382   QStringList::Iterator it;
    00383
    00384   // placeholders for iterated items
    00385   KArchiveDirectory* curDir;
    00386   QString curDirName;
    00387
    00388   QStringList dirEntries;
    00389   KArchiveEntry* curEntry;
    00390   KArchiveFile* curFile;
    00391
    00392
    00393   QPtrStack<KArchiveDirectory> dirStack;
    00394   QValueStack<QString> dirNameStack;
    00395
    00396   dirStack.push( this );     // init stack at current directory
    00397   dirNameStack.push( dest ); // ... with given path
    00398   do {
    00399     curDir = dirStack.pop();
    00400     curDirName = dirNameStack.pop();
    00401     root.mkdir(curDirName);
    00402
    00403     dirEntries = curDir->entries();
    00404     for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
    00405       curEntry = curDir->entry(*it);
    00406       if ( curEntry->isFile() ) {
    00407         curFile = dynamic_cast<KArchiveFile*>( curEntry );
    00408         fileList.append( curFile );
    00409         fileToDir.insert( curFile->position(), curDirName );
    00410       }
    00411
    00412       if ( curEntry->isDirectory() )
    00413         if ( recursiveCopy ) {
    00414           dirStack.push( dynamic_cast<KArchiveDirectory*>( curEntry ) );
    00415           dirNameStack.push( curDirName + "/" + curEntry->name() );
    00416         }
    00417     }
    00418   } while (!dirStack.isEmpty());
    00419
    00420   fileList.sort();  // sort on m_pos, so we have a linear access
    00421
    00422   KArchiveFile* f;
    00423   for ( f = fileList.first(); f; f = fileList.next() ) {
    00424     int pos = f->position();
    00425     f->copyTo( fileToDir[pos] );
    00426   }
    00427 }
    00428
    00429
    00430 void KArchive::virtual_hook( int, void* )
    00431 { /*BASE::virtual_hook( id, data );*/ }
    00432
    00433 void KArchiveEntry::virtual_hook( int, void* )
    00434 { /*BASE::virtual_hook( id, data );*/ }
    00435
    00436 void KArchiveFile::virtual_hook( int id, void* data )
    00437 { KArchiveEntry::virtual_hook( id, data ); }
    00438
    00439 void KArchiveDirectory::virtual_hook( int id, void* data )
    00440 { KArchiveEntry::virtual_hook( id, data ); }
    00441

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:13:07 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001