kio Library API Documentation

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 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:28 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001