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

kdirreadjob.cpp

/*
 *   File name:   kdirreadjob.cpp
 *   Summary:     Support classes for KDirStat
 *   License:     LGPL - See file COPYING.LIB for details.
 *   Author:      Stefan Hundhammer <sh@suse.de>
 *              Joshua Hodosh <kdirstat@grumpypenguin.org>
 *
 *   Updated:     2010-02-01
 */


#ifdef HAVE_CONFIG_H
#   include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <stdio.h>
#include <sys/errno.h>
#include <klocale.h>
#include <kapplication.h>
#include <kio/job.h>
#include <kio/netaccess.h>

#include "kdirtree.h"
#include "kdirreadjob.h"
#include "kdirtreecache.h"
#include "kexcluderules.h"


using namespace KDirStat;


00036 KDirReadJob::KDirReadJob( KDirTree * tree,
                    KDirInfo * dir  )
    : _tree( tree )
    , _dir( dir )
{
    _queue  = 0;
    _started      = false;

    if ( _dir )
      _dir->readJobAdded();
}


00049 KDirReadJob::~KDirReadJob()
{
    if ( _dir )
      _dir->readJobFinished();
}


/**
 * Default implementation - derived classes should overwrite this method or
 * startReading() (or both).
 **/

void
00062 KDirReadJob::read()
{
    if ( ! _started )
    {
      _started = true;
      startReading();

      // Don't do anything after startReading() - startReading() might call
      // finished() which in turn makes the queue destroy this object
    }
}


void
00076 KDirReadJob::setDir( KDirInfo * dir )
{
    _dir = dir;
}


void
00083 KDirReadJob::finished()
{
    if ( _queue )
      _queue->jobFinishedNotify( this );
    else
      kdError() << "No job queue for " << _dir << endl;
}


void
00093 KDirReadJob::childAdded( KFileInfo *newChild )
{
    _tree->childAddedNotify( newChild );
}


void
00100 KDirReadJob::deletingChild( KFileInfo *deletedChild )
{
    _tree->deletingChildNotify( deletedChild );
}





00109 KLocalDirReadJob::KLocalDirReadJob( KDirTree *  tree,
                            KDirInfo *    dir )
    : KDirReadJob( tree, dir )
    , _diskDir( 0 )
{
}


00117 KLocalDirReadJob::~KLocalDirReadJob()
{
}


void
00123 KLocalDirReadJob::startReading()
{
    struct dirent *     entry;
    struct stat         statInfo;
    QString       dirName            = _dir->url();
    QString       defaultCacheName = DEFAULT_CACHE_NAME.url();

    if ( ( _diskDir = opendir( dirName ) ) )
    {
      _tree->sendProgressInfo( dirName );
      _dir->setReadState( KDirReading );

      while ( ( entry = readdir( _diskDir ) ) )
      {
          QString entryName = entry->d_name;

          if ( entryName != "."  &&
             entryName != ".."   )
          {
            QString fullName = dirName + "/" + entryName;

            if ( lstat( fullName, &statInfo ) == 0 )  // lstat() OK
            {
                if ( S_ISDIR( statInfo.st_mode ) )    // directory child?
                {
                  KDirInfo *subDir = new KDirInfo( entryName, &statInfo, _tree, _dir );
                  _dir->insertChild( subDir );
                  childAdded( subDir );

                  if ( KExcludeRules::excludeRules()->match( fullName ) )
                  {
                      subDir->setExcluded();
                      subDir->setReadState( KDirOnRequestOnly );
                      _tree->sendFinalizeLocal( subDir );
                      subDir->finalizeLocal();
                  }
                  else // No exclude rule matched
                  {
                      if ( _dir->device() == subDir->device()     )     // normal case
                      {
                        _tree->addJob( new KLocalDirReadJob( _tree, subDir ) );
                      }
                      else    // The subdirectory we just found is a mount point.
                      {
                        // kdDebug() << "Found mount point " << subDir << endl;
                        subDir->setMountPoint();

                        if ( _tree->crossFileSystems() )
                        {
                            _tree->addJob( new KLocalDirReadJob( _tree, subDir ) );
                        }
                        else
                        {
                            subDir->setReadState( KDirOnRequestOnly );
                            _tree->sendFinalizeLocal( subDir );
                            subDir->finalizeLocal();
                        }
                      }
                  }
                }
                else          // non-directory child
                {
                  if ( entryName == defaultCacheName )      // .kdirstat.cache.gz found?
                  {
                      //
                      // Read content of this subdirectory from cache file
                      //

                      
                      KCacheReadJob * cacheReadJob = new KCacheReadJob( _tree, _dir->parent(), fullName );
                      Q_CHECK_PTR( cacheReadJob );
                      QString firstDirInCache = cacheReadJob->reader()->firstDir();

                      if ( firstDirInCache == dirName )     // Does this cache file match this directory?
                      {
                        kdDebug() << "Using cache file " << fullName << " for " << dirName << endl;

                        cacheReadJob->reader()->rewind();   // Read offset was moved by firstDir()
                        _tree->addJob( cacheReadJob );      // Job queue will assume ownership of cacheReadJob

                        //
                        // Clean up partially read directory content
                        //

                        KDirTree * tree = _tree;      // Copy data members to local variables:
                        KDirInfo * dir  = _dir;       // This object will be deleted soon by killAll()

                        _queue->killAll( dir );       // Will delete this job as well!
                        // All data members of this object are invalid from here on!

                        tree->deleteSubtree( dir );

                        return;
                      }
                      else
                      {
                        kdDebug() << "NOT using cache file " << fullName
                                << " with dir " << firstDirInCache
                                << " for " << dirName
                                << endl;

                        delete cacheReadJob;
                      }
                  }
                  else
                  {
                      KFileInfo *child = new KFileInfo( entryName, &statInfo, _tree, _dir );
                      _dir->insertChild( child );
                      childAdded( child );
                  }
                }
            }
            else              // lstat() error
            {
                kdWarning() << "lstat(" << fullName << ") failed: " << strerror( errno ) << endl;

                /*
                 * Not much we can do when lstat() didn't work; let's at
                 * least create an (almost empty) entry as a placeholder.
                 */
                KDirInfo *child = new KDirInfo( _tree, _dir, entry->d_name );
                child->setReadState( KDirError );
                _dir->insertChild( child );
                childAdded( child );
            }
          }
      }

      closedir( _diskDir );
      // kdDebug() << "Finished reading " << _dir << endl;
      _dir->setReadState( KDirFinished );
      _tree->sendFinalizeLocal( _dir );
      _dir->finalizeLocal();
    }
    else
    {
      _dir->setReadState( KDirError );
      _tree->sendFinalizeLocal( _dir );
      _dir->finalizeLocal();
      // kdWarning() << k_funcinfo << "opendir(" << dirName << ") failed" << endl;
      // opendir() doesn't set 'errno' according to POSIX  :-(
    }

    finished();
    // Don't add anything after finished() since this deletes this job!
}



KFileInfo *
00273 KLocalDirReadJob::stat( const KUrl &      url,
                  KDirTree  * tree,
                  KDirInfo *  parent )
{
    struct stat statInfo;

    if ( lstat( url.path(), &statInfo ) == 0 )        // lstat() OK
    {
      QString name = parent ? url.fileName() : url.path();

      if ( S_ISDIR( statInfo.st_mode ) )        // directory?
      {
          KDirInfo * dir = new KDirInfo( name, &statInfo, tree, parent );

          if ( dir && parent && dir->device() != parent->device() )
            dir->setMountPoint();

          return dir;
      }
      else                                // no directory
          return new KFileInfo( name, &statInfo, tree, parent );
    }
    else    // lstat() failed
      return 0;
}






00304 KioDirReadJob::KioDirReadJob( KDirTree *  tree,
                        KDirInfo *  dir )
    : KObjDirReadJob( tree, dir )
{
    _job = 0;
}


00312 KioDirReadJob::~KioDirReadJob()
{
#if 0
    if ( _job )
      _job->kill( true );     // quietly
#endif
}


void
00322 KioDirReadJob::startReading()
{
    KUrl url( _dir->url() );

    if ( ! url.isValid() )
    {
      kdWarning() << k_funcinfo << "URL malformed: " << _dir->url() << endl;
    }

    _job = KIO::listDir( url,
                   false );   // showProgressInfo

    connect( _job, SIGNAL( entries( KIO::Job *, const KIO::UDSEntryList& ) ),
             this, SLOT  ( entries( KIO::Job *, const KIO::UDSEntryList& ) ) );

    connect( _job, SIGNAL( result  ( KIO::Job * ) ),
           this, SLOT  ( finished( KIO::Job * ) ) );

    connect( _job, SIGNAL( canceled( KIO::Job * ) ),
           this, SLOT  ( finished( KIO::Job * ) ) );
}


void
00346 KioDirReadJob::entries ( KIO::Job *             job,
                   const KIO::UDSEntryList &    entryList )
{
    NOT_USED( job );
    KUrl url( _dir->url() );  // Cache this - it's expensive!

    if ( ! url.isValid() )
    {
      kdWarning() << k_funcinfo << "URL malformed: " << _dir->url() << endl;
    }

    KIO::UDSEntryList::ConstIterator it = entryList.begin();

    while ( it != entryList.end() )
    {
      KFileItem entry( *it,
                   url,
                   true,            // determineMimeTypeOnDemand
                   true );    // URL is parent directory

      if ( entry.name() != "." &&
           entry.name() != ".."  )
      {
          // kdDebug() << "Found " << entry.url().url() << endl;

          if ( entry.isDir()    &&  // Directory child
             ! entry.isLink()   )   // and not a symlink?
          {
            KDirInfo *subDir = new KDirInfo( &entry, _tree, _dir );
            _dir->insertChild( subDir );
            childAdded( subDir );

            if ( KExcludeRules::excludeRules()->match( url.path() ) )
            {
                subDir->setExcluded();
                subDir->setReadState( KDirOnRequestOnly );
                _tree->sendFinalizeLocal( subDir );
                subDir->finalizeLocal();
            }
            else // No exclude rule matched
            {
                _tree->addJob( new KioDirReadJob( _tree, subDir ) );
            }
          }
          else    // non-directory child
          {
            KFileInfo *child = new KFileInfo( &entry, _tree, _dir );
            _dir->insertChild( child );
            childAdded( child );
          }
      }

      ++it;
    }
}


void
00404 KioDirReadJob::finished( KIO::Job * job )
{
    if ( job->error() )
      _dir->setReadState( KDirError );
    else
      _dir->setReadState( KDirFinished );

    _tree->sendFinalizeLocal( _dir );
    _dir->finalizeLocal();
    _job = 0;     // The job deletes itself after this signal!

    KDirReadJob::finished();
    // Don't add anything after finished() since this deletes this job!
}



KFileInfo *
00422 KioDirReadJob::stat( const KUrl &   url,
                  KDirTree  *       tree,
                  KDirInfo  *       parent )
{
    KIO::UDSEntry uds_entry;

    if ( KIO::NetAccess::stat( url, uds_entry, qApp->mainWidget() ) )   // remote stat() OK?
    {
      KFileItem entry( uds_entry, url,
                   true,            // determine MIME type on demand
                   false );   // URL specifies parent directory

      return entry.isDir() ? new KDirInfo ( &entry, tree, parent ) : new KFileInfo( &entry, tree, parent );
    }
    else    // remote stat() failed
      return 0;
}


QString
00442 KioDirReadJob::owner( KUrl url )
{
    KIO::UDSEntry uds_entry;

    if ( KIO::NetAccess::stat( url, uds_entry, qApp->mainWidget() ) )   // remote stat() OK?
    {
      KFileItem entry( uds_entry, url,
                   true,            // determine MIME type on demand
                   false );   // URL specifies parent directory

      return entry.user();
    }

    return QString();
}





00462 KCacheReadJob::KCacheReadJob( KDirTree *  tree,
                        KDirInfo *  parent,
                        KCacheReader *    reader )
    : KObjDirReadJob( tree, parent )
    , _reader( reader )
{
    if ( _reader )
      _reader->rewind();

    init();
}


00475 KCacheReadJob::KCacheReadJob( KDirTree *  tree,
                        KDirInfo *  parent,
                        const QString &   cacheFileName )
    : KObjDirReadJob( tree, parent )
{
    _reader = new KCacheReader( cacheFileName, tree, parent );
    Q_CHECK_PTR( _reader );

    init();
}


void
00488 KCacheReadJob::init()
{
    if ( _reader )
    {
      if ( _reader->ok() )
      {
          connect( _reader,   SIGNAL( childAdded    ( KFileInfo * ) ),
                 this,  SLOT  ( slotChildAdded( KFileInfo * ) ) );
      }
      else
      {
          delete _reader;
          _reader = 0;
      }
    }
}


00506 KCacheReadJob::~KCacheReadJob()
{
    if ( _reader )
      delete _reader;
}


void
00514 KCacheReadJob::read()
{
    /*
     * This will be called repeatedly from KDirTree::timeSlicedRead() until
     * finished() is called.
     */

    if ( ! _reader )
      finished();

    // kdDebug() << "Reading 1000 cache lines" << endl;
    _reader->read( 1000 );
    _tree->sendProgressInfo( "" );

    if ( _reader->eof() || ! _reader->ok() )
    {
      // kdDebug() << "Cache reading finished - ok: " << _reader->ok() << endl;
      finished();
    }
}





00539 KDirReadJobQueue::KDirReadJobQueue()
    : QObject()
{

    connect( &_timer, SIGNAL( timeout() ),
           this,    SLOT  ( timeSlicedRead() ) );
}


00548 KDirReadJobQueue::~KDirReadJobQueue()
{
    clear();
}


void
00555 KDirReadJobQueue::enqueue( KDirReadJob * job )
{
    if ( job )
    {
      _queue.append( job );
      job->setQueue( this );

      if ( ! _timer.isActive() )
      {
          // kdDebug() << "First job queued" << endl;
          emit startingReading();
          _timer.start( 0 );
      }
    }
}


KDirReadJob *
00573 KDirReadJobQueue::dequeue()
{
    KDirReadJob * job = _queue.first();
    _queue.removeFirst();

    if ( job )
      job->setQueue( 0 );

    return job;
}


void
00586 KDirReadJobQueue::clear()
{
    QMutableListIterator<KDirReadJob *> i(_queue);
    while ( i.hasNext()){
        KDirReadJob * job = i.next();
        delete job;
        i.remove();
    }
}


void
00598 KDirReadJobQueue::abort()
{
    while ( ! _queue.isEmpty() )
    {
      KDirReadJob * job = _queue.first();

      if ( job->dir() )
          job->dir()->readJobAborted();

      _queue.removeFirst();
      delete job;
    }
}


void
00614 KDirReadJobQueue::killAll( KDirInfo * subtree )
{
    if ( ! subtree )
      return;
    
    QMutableListIterator<KDirReadJob *> i(_queue);
    while (i.hasNext()){
        KDirReadJob * job = i.next();
        if (job->dir() && job->dir()->isInSubtree( subtree ) )
        {
            i.remove();
            delete job;
        }
        else
        {

        }


    }
}


void
00638 KDirReadJobQueue::timeSlicedRead()
{
    if ( ! _queue.isEmpty() )
      _queue.first()->read();
}


void
00646 KDirReadJobQueue::jobFinishedNotify( KDirReadJob *job )
{
    // Get rid of the old (finished) job.

    _queue.removeFirst();
    delete job;

    // Look for a new job.

    if ( _queue.isEmpty() )   // No new job available - we're done.
    {
      _timer.stop();
      // kdDebug() << "No more jobs - finishing" << endl;
      emit finished();
    }
}




// EOF

Generated by  Doxygen 1.6.0   Back to index