Logo Search packages:      
Sourcecode: kbear version File versions  Download package

kbeardeletejob.cpp

/***************************************************************************
                          kbeardeletejob.cpp  -  description
                             -------------------
    begin                : lör apr 27 2002
    copyright            : (C) 2002 by Björn Sahlström
    email                : kbjorn@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                                                          *
 *   This program is free software; you can redistribute it and/or modify     *
 *   it under the terms of the GNU General Public License as published by*
 *   the Free Software Foundation; either version 2 of the License, or        *
 *   (at your option) any later version.                                                  *
 *                                                                                                          *
 ***************************************************************************/

//////////////////////////////////////////////////////////////////////
// Qt specific include files
#include <qfile.h>
//////////////////////////////////////////////////////////////////////
// KDE specific include files
#include <dcopclient.h>
#include <kio/observer.h>
#include <kio/scheduler.h>
#include <kdebug.h>
#include <klocale.h>
#include <kdirwatch.h>
#include <kprotocolinfo.h>
#include <kapplication.h>
#include <kmessagebox.h>
//////////////////////////////////////////////////////////////////////
// Application specific include files
#include "kbeardeletejob.h"
#include "kbearlistjob.h"
#include "kbearconnectionmanager.h"
#include <assert.h>
#define REPORT_TIMEOUT 200

#define KIO_ARGS QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly ); stream
//-----------------------------------------------
00042 KBearDeleteJob::KBearDeleteJob( const KURL::List& src, bool shred, bool showProgressInfo )
      :     KIO::Job(showProgressInfo), m_totalSize( 0 ),
            m_processedSize( 0 ), m_fileProcessedSize( 0 ),
            m_processedFiles( 0 ), m_processedDirs( 0 ),
            m_totalFilesDirs( 0 ),  m_srcList(src),
            m_currentStat(m_srcList.begin()),
            m_shred(shred), m_reportTimer(0)
{
      if( showProgressInfo ) {
            connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
                              Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
            connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
                              Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );

            m_reportTimer=new QTimer(this);
            connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
            // we disconnect this, and call it from slotInfoMessage instead, since we don't want to flood
            // Observer with all log info
            disconnect( this, SIGNAL( infoMessage( KIO::Job*, const QString & ) ),
                 Observer::self(), SLOT( slotInfoMessage( KIO::Job*, const QString & ) ) );
/*
            connect( this, SIGNAL( infoMessage( KIO::Job*, const QString & ) ),
                 this, SLOT( slotInfoMessage( KIO::Job*, const QString & ) ) );
*/
            //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
            m_reportTimer->start(REPORT_TIMEOUT,false);
      }
/* will be called by connectionManager
      QTimer::singleShot(0, this, SLOT(slotStart()));
*/
}
//-----------------------------------------------
00074 KBearDeleteJob::~KBearDeleteJob() {
}
//-----------------------------------------------
00077 void KBearDeleteJob::slotInfoMessage( KIO::Job* job, const QString& message ) {
      if( message.left(4) != "resp" || message.left(7) != "command" || message.left(10) != "multi-line" )
            Observer::self()->slotInfoMessage( job, message );

      emit infoMessage( job, message );
}
//-----------------------------------------------
00084 void KBearDeleteJob::start(unsigned long connID) {
      // First thing to do is to notify that we started.
      // this is used to disable gui on filesys part if single connection
      // is selected.
      connectionManager->jobStarting( connID );
      m_connID = connID;
      statNextSrc();
}
//-----------------------------------------------
//this is called often, so calling the functions
//from Observer here directly might improve the performance a little bit
//aleXXX
00096 void KBearDeleteJob::slotReport()
{
   if (m_progressId==0)
      return;

   Observer * observer = Observer::self();

   emit deleting( this, m_currentURL );
   observer->slotDeleting(this,m_currentURL);

   switch( state ) {
        case STATE_STATING:
        case STATE_LISTING:
            emit totalSize( this, m_totalSize );
            emit totalFiles( this, files.count() );
            emit totalDirs( this, dirs.count() );
            break;
        case STATE_DELETING_DIRS:
            emit processedDirs( this, m_processedDirs );
            observer->slotProcessedDirs(this,m_processedDirs);
            emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
            break;
        case STATE_DELETING_FILES:
            observer->slotProcessedFiles(this,m_processedFiles);
            emit processedFiles( this, m_processedFiles );
            if (!m_shred)
               emitPercent( m_processedFiles, m_totalFilesDirs );
            break;
   }
}
//-----------------------------------------------
00127 void KBearDeleteJob::slotEntries(KIO::Job* job, const KIO::UDSEntryList& list) {
      KIO::UDSEntryListConstIterator it = list.begin();
      KIO::UDSEntryListConstIterator end = list.end();
      for( ; it != end; ++it ) {
            KIO::UDSEntry::ConstIterator it2 = (*it).begin();
            bool bDir = false;
            bool bLink = false;
            QString relName;
            int atomsFound(0);
            for( ; it2 != (*it).end(); it2++ ) {
                  switch ((*it2).m_uds) {
                        case KIO::UDS_FILE_TYPE:
                              bDir = S_ISDIR((*it2).m_long);
                              atomsFound++;
                              break;
                        case KIO::UDS_NAME:
                              relName = ((*it2).m_str);
                              atomsFound++;
                              break;
                        case KIO::UDS_LINK_DEST:
                              bLink = !(*it2).m_str.isEmpty();
                              atomsFound++;
                              break;
                        case KIO::UDS_SIZE:
                              m_totalSize += (off_t)((*it2).m_long);
                              atomsFound++;
                              break;
                        default:
                              break;
                  }
                  if( atomsFound == 4 )
                        break;
            }
            assert(!relName.isEmpty());
            if( relName != ".." && relName != ".") {
                  KURL url = ((KIO::SimpleJob *)job)->url(); // assumed to be a dir
                  url.addPath( relName );
                  //kdDebug(7007) << "KBearDeleteJob::slotEntries " << relName << " (" << url.prettyURL() << ")" << endl;
                  if( bLink )
                        symlinks.append( url );
                  else if( bDir )
                        dirs.append( url );
                  else
                        files.append( url );
            }
      }
}


void KBearDeleteJob::statNextSrc()
{
    //kdDebug(7007) << "statNextSrc" << endl;
    if ( m_currentStat != m_srcList.end() )
    {
        m_currentURL = (*m_currentStat);

        // if the file system doesn't support deleting, we do not even stat
        if (!KProtocolInfo::supportsDeleting(m_currentURL)) {
            KMessageBox::information( 0, KIO::buildErrorString(KIO::ERR_CANNOT_DELETE, m_currentURL.prettyURL()));
            ++m_currentStat;
            statNextSrc(); // we could use a loop instead of a recursive call :)
            return;
        }
        // Stat it
        state = STATE_STATING;
        KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false );
        connectionManager->scheduleJob( m_connID, job );
        //kdDebug(7007) << "KIO::stat (DeleteJob) " << m_currentURL.prettyURL() << endl;
        addSubjob(job);
        //if ( m_progressId ) // Did we get an ID from the observer ?
        //  Observer::self()->slotDeleting( this, *it ); // show asap
    } else
    {
        m_totalFilesDirs = files.count()+symlinks.count() + dirs.count();
        slotReport();
        // Now we know which dirs hold the files we're going to delete.
        // To speed things up and prevent double-notification, we disable KDirWatch
        // on those dirs temporarily (using KDirWatch::self, that's the instanced
        // used by e.g. kdirlister).
        for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
            KDirWatch::self()->stopDirScan( *it );
        state = STATE_DELETING_FILES;
      deleteNextFile();
    }
}
//-----------------------------------------------
void KBearDeleteJob::deleteNextFile() {
      //kdDebug(7007) << "deleteNextFile" << endl;
      if( ! files.isEmpty() || ! symlinks.isEmpty() ) {
            KIO::SimpleJob *job;
            do {
                  // Take first file to delete out of list
                  KURL::List::Iterator it = files.begin();
                  bool isLink = false;
                  if( it == files.end() ) { // No more files
                        it = symlinks.begin(); // Pick up a symlink to delete
                        isLink = true;
                  }
                  // Use shredding ?
                  if( m_shred && (*it).isLocalFile() && !isLink ) {
                        // KShred your KTie
                        KIO_ARGS << int(3) << (*it).path();
                        job = KIO::special(KURL("file:/"), packedArgs, false /*no GUI*/);
                        connectionManager->scheduleJob( m_connID, job );
                        m_currentURL=(*it);
                        connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
                                          this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
                  }
                  else {
                        // Normal deletion
                        // If local file, try do it directly
                        if( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
                              job = 0;
                              m_processedFiles++;
                              if( m_processedFiles % 300 == 0 ) { // update progress info every 300 files
                                    m_currentURL = *it;
                                    slotReport();
                              }
                        }
                        else { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
                              job = KIO::file_delete( *it, false /*no GUI*/);
                              connectionManager->scheduleJob( m_connID, job );
                              m_currentURL=(*it);
                        }
                  }
                  if( isLink )
                        symlinks.remove( it );
                  else
                        files.remove( it );
                  if( job ) {
                        addSubjob( job );
                        return;
                  }
                  // loop only if direct deletion worked (job=0) and there is something else to delete
            } while ( ! job && ( ! files.isEmpty() || ! symlinks.isEmpty() ) );
      }
      state = STATE_DELETING_DIRS;
      deleteNextDir();
}
//-----------------------------------------------
void KBearDeleteJob::deleteNextDir() {
      if( ! dirs.isEmpty() ) { // some dirs to delete ?
        do {
            // Take first dir to delete out of list - last ones first !
            KURL::List::Iterator it = dirs.fromLast();
            // If local dir, try to rmdir it directly
            if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {

                m_processedDirs++;
                if ( m_processedDirs % 100 == 0 ) { // update progress info every 100 dirs
                    m_currentURL = *it;
                    slotReport();
                }
            } else
            {
                KIO::SimpleJob *job = KIO::rmdir( *it );
                connectionManager->scheduleJob( m_connID, job );
                dirs.remove(it);
                addSubjob( job );
                return;
            }
            dirs.remove(it);
        } while ( !dirs.isEmpty() );
    }

    // Re-enable watching on the dirs that held the deleted files
    for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
        KDirWatch::self()->restartDirScan( *it );

    // Finished - tell the world
    if ( !m_srcList.isEmpty() )
    {
            QByteArray data;
            QDataStream arg( data, IO_WriteOnly );
            arg << m_srcList;
            kapp->dcopClient()->send( "*", "KDirNotify", "FilesRemoved(const KURL::List&)", data );
    }
    if (m_reportTimer!=0)
       m_reportTimer->stop();
    emitResult();
}
//-----------------------------------------------
00309 void KBearDeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size ) {
      // Note: this is the same implementation as CopyJob::slotProcessedSize but
      // it's different from FileCopyJob::slotProcessedSize - which is why this
      // is not in Job.

      m_fileProcessedSize = data_size;
      //kdDebug(7007) << "KBearDeleteJob::slotProcessedSize " << (unsigned int) (m_processedSize + m_fileProcessedSize) << endl;
      emit processedSize( this, m_processedSize + m_fileProcessedSize );
     // calculate percents
      unsigned long ipercent = m_percent;

      if( m_totalSize == 0 )
            m_percent = 100;
      else
            m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0);

      if( m_percent > ipercent ) {
            emit percent( this, m_percent );
            //kdDebug(7007) << "KBearDeleteJob::slotProcessedSize - percent =  " << (unsigned int) m_percent << endl;
      }
}

void KBearDeleteJob::slotResult( Job *job )
{
   switch ( state )
   {
   case STATE_STATING:
      {
         // Was there an error while stating ?
         if (job->error() )
         {
            // Probably : doesn't exist
            Job::slotResult( job ); // will set the error and emit result(this)
            return;
         }

         // Is it a file or a dir ?
         KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
         bool bDir = false;
         bool bLink = false;
         KIO::filesize_t size = (KIO::filesize_t)-1;
         KIO::UDSEntry::ConstIterator it2 = entry.begin();
         int atomsFound(0);
         for( ; it2 != entry.end(); it2++ )
         {
            if ( ((*it2).m_uds) == KIO::UDS_FILE_TYPE )
            {
               bDir = S_ISDIR( (mode_t)(*it2).m_long );
               atomsFound++;
            }
            else if ( ((*it2).m_uds) == KIO::UDS_LINK_DEST )
            {
               bLink = !((*it2).m_str.isEmpty());
               atomsFound++;
            }
            else if ( ((*it2).m_uds) == KIO::UDS_SIZE )
            {
               size = (*it2).m_long;
               atomsFound++;
            };
            if (atomsFound==3) break;
         }

         KURL url = ((KIO::SimpleJob*)job)->url();

         subjobs.remove( job );
         assert( subjobs.isEmpty() );

         if (bDir && !bLink)
         {
            // Add toplevel dir in list of dirs
            dirs.append( url );
            if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) )
                m_parentDirs.append( url.path(-1) );

            //kdDebug(7007) << " Target is a directory " << endl;
            // List it
            state = STATE_LISTING;
            KBearListJob *newjob = KBearListJob::listRecursive( m_connID, url, false, true );
            connectionManager->scheduleJob( m_connID, newjob );
            connect(newjob, SIGNAL(entries( KIO::Job *,
                                            const KIO::UDSEntryList& )),
                    SLOT( slotEntries( KIO::Job*,
                                       const KIO::UDSEntryList& )));
            addSubjob(newjob);
         }
         else
         {
            if ( bLink ) {
                //kdDebug(7007) << " Target is a symlink" << endl;
                symlinks.append( url );
            } else {
                //kdDebug(7007) << " Target is a file" << endl;
                files.append( url );
            }
            if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) )
                m_parentDirs.append( url.directory(-1) );
            ++m_currentStat;
            statNextSrc();
         }
      }
      break;
   case STATE_LISTING:
      if ( job->error() )
      {
         // Try deleting nonetheless, it may be empty (and non-listable)
      }
      subjobs.remove( job );
      assert( subjobs.isEmpty() );
      ++m_currentStat;
      statNextSrc();
      break;
   case STATE_DELETING_FILES:
      if ( job->error() )
      {
         Job::slotResult( job ); // will set the error and emit result(this)
         return;
      }
      subjobs.remove( job );
      assert( subjobs.isEmpty() );
      m_processedFiles++;

      deleteNextFile();
      break;
   case STATE_DELETING_DIRS:
      if ( job->error() )
      {
         Job::slotResult( job ); // will set the error and emit result(this)
         return;
      }
      subjobs.remove( job );
      assert( subjobs.isEmpty() );
      m_processedDirs++;
      //emit processedDirs( this, m_processedDirs );
      //if (!m_shred)
         //emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );

      deleteNextDir();
      break;
   default:
      assert(0);
   }
}
//-----------------------------------------------
00453 KBearDeleteJob* KBearDeleteJob::del( const KURL::List& src, bool shred, bool showProgressInfo ) {
      return new KBearDeleteJob( src, shred, showProgressInfo );
}
//-----------------------------------------------
#ifndef NO_INCLUDE_MOCFILES
#include "kbeardeletejob.moc"
#endif

Generated by  Doxygen 1.6.0   Back to index