Skip to content

Commit

Permalink
Added exclude rules matching any file child (GitHub issue #90)
Browse files Browse the repository at this point in the history
  • Loading branch information
shundhammer committed Mar 15, 2019
1 parent 087cbcb commit 77a2185
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 85 deletions.
141 changes: 84 additions & 57 deletions src/DirReadJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,35 @@ void LocalDirReadJob::startReading()
}

closedir( _diskDir );
finishReading( _dir, DirFinished );
DirReadState readState = DirFinished;

//
// Check all entries against exclude rules that match against any
// direct non-directory entry.
//
// Doing this now is a performance optimization: This could also be
// done immediately after each entry is read, but that would mean
// iterating over all exclude rules for every single directory entry,
// even if there are no exclude rules that match against any
// files, so it would be a general performance penalty.
//
// Doing this after all entries are read means more cleanup if any
// exclude rule does match, but that is the exceptional case; if there
// are no such rules to begin with, the match function returns 'false'
// immediately, so the performance impact is minimal.
//

if ( ExcludeRules::instance()->matchDirectChildren( _dir ) )
{
// Kill all queued jobs for this dir except this one
_queue->killAll( _dir, this );

_tree->clearSubtree( _dir );
_dir->setExcluded();
readState = DirOnRequestOnly;
}

finishReading( _dir, readState );
}

finished();
Expand All @@ -256,53 +284,6 @@ void LocalDirReadJob::finishReading( DirInfo * dir, DirReadState readState )
}



FileInfo * LocalDirReadJob::stat( const QString & url,
DirTree * tree,
DirInfo * parent )
{
struct stat statInfo;
logDebug() << "url: \"" << url << "\"" << endl;

if ( lstat( url.toUtf8(), &statInfo ) == 0 ) // lstat() OK
{
if ( S_ISDIR( statInfo.st_mode ) ) // directory?
{
DirInfo * dir = new DirInfo( url, &statInfo, tree, parent );
CHECK_NEW( dir );

if ( parent )
parent->insertChild( dir );

if ( dir && parent &&
! tree->isTopLevel( dir )
&& dir->device() != parent->device() )
{
logDebug() << dir << " is a mount point" << endl;
dir->setMountPoint();
}

return dir;
}
else // no directory
{
FileInfo * file = new FileInfo( url, &statInfo, tree, parent );
CHECK_NEW( file );

if ( parent )
parent->insertChild( file );

return file;
}
}
else // lstat() failed
{
THROW( SysCallFailedException( "lstat", url ) );
return 0; // NOTREACHED
}
}


void LocalDirReadJob::processSubDir( const QString & entryName, DirInfo * subDir )
{
_dir->insertChild( subDir );
Expand Down Expand Up @@ -340,15 +321,6 @@ void LocalDirReadJob::processSubDir( const QString & entryName, DirInfo * subDir
}


QString LocalDirReadJob::fullName( const QString & entryName ) const
{
QString result = _dirName == "/" ? "" : _dirName; // Avoid leading // when in root dir
result += "/" + entryName;

return result;
}


bool LocalDirReadJob::readCacheFile( const QString & cacheFileName )
{
QString cacheFullName = fullName( cacheFileName );
Expand Down Expand Up @@ -426,6 +398,61 @@ void LocalDirReadJob::handleLstatError( const QString & entryName )
}


QString LocalDirReadJob::fullName( const QString & entryName ) const
{
QString result = _dirName == "/" ? "" : _dirName; // Avoid leading // when in root dir
result += "/" + entryName;

return result;
}


FileInfo * LocalDirReadJob::stat( const QString & url,
DirTree * tree,
DirInfo * parent )
{
struct stat statInfo;
logDebug() << "url: \"" << url << "\"" << endl;

if ( lstat( url.toUtf8(), &statInfo ) == 0 ) // lstat() OK
{
if ( S_ISDIR( statInfo.st_mode ) ) // directory?
{
DirInfo * dir = new DirInfo( url, &statInfo, tree, parent );
CHECK_NEW( dir );

if ( parent )
parent->insertChild( dir );

if ( dir && parent &&
! tree->isTopLevel( dir )
&& dir->device() != parent->device() )
{
logDebug() << dir << " is a mount point" << endl;
dir->setMountPoint();
}

return dir;
}
else // no directory
{
FileInfo * file = new FileInfo( url, &statInfo, tree, parent );
CHECK_NEW( file );

if ( parent )
parent->insertChild( file );

return file;
}
}
else // lstat() failed
{
THROW( SysCallFailedException( "lstat", url ) );
return 0; // NOTREACHED
}
}





Expand Down
18 changes: 12 additions & 6 deletions src/DirTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,7 @@ void DirTree::refresh( DirInfo * subtree )
{
// logDebug() << "Refreshing subtree " << subtree << endl;

if ( subtree->hasChildren() )
{
emit clearingSubtree( subtree );
subtree->clear();
emit subtreeCleared( subtree );
}
clearSubtree( subtree );

subtree->reset();
subtree->setExcluded( false );
Expand Down Expand Up @@ -312,6 +307,17 @@ void DirTree::deleteSubtree( FileInfo *subtree )
}


void DirTree::clearSubtree( DirInfo * subtree )
{
if ( subtree->hasChildren() )
{
emit clearingSubtree( subtree );
subtree->clear();
emit subtreeCleared( subtree );
}
}


void DirTree::addJob( DirReadJob * job )
{
_jobQueue.enqueue( job );
Expand Down
6 changes: 6 additions & 0 deletions src/DirTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ namespace QDirStat
**/
void deleteSubtree( FileInfo * subtree );

/**
* Delete all children of a subtree, but leave the subtree inself
* intact.
**/
void clearSubtree( DirInfo * subtree );


public:

Expand Down
90 changes: 75 additions & 15 deletions src/ExcludeRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@


#include "ExcludeRules.h"
#include "DirInfo.h"
#include "FileInfoIterator.h"
#include "Settings.h"
#include "SettingsHelpers.h"
#include "Logger.h"
Expand All @@ -19,17 +21,23 @@
using namespace QDirStat;


ExcludeRule::ExcludeRule( const QRegExp & regexp, bool useFullPath ):
ExcludeRule::ExcludeRule( const QRegExp & regexp,
bool useFullPath,
bool checkAnyFileChild ):
_regexp( regexp ),
_useFullPath( useFullPath )
_useFullPath( useFullPath ),
_checkAnyFileChild( checkAnyFileChild )
{
// NOP
}


ExcludeRule::ExcludeRule( const QString & regexp, bool useFullPath ):
ExcludeRule::ExcludeRule( const QString & regexp,
bool useFullPath,
bool checkAnyFileChild ):
_regexp( QRegExp( regexp ) ),
_useFullPath( useFullPath )
_useFullPath( useFullPath ),
_checkAnyFileChild( checkAnyFileChild )
{
// NOP
}
Expand All @@ -55,6 +63,28 @@ bool ExcludeRule::match( const QString & fullPath, const QString & fileName )
}


bool ExcludeRule::matchDirectChildren( DirInfo * dir )
{
if ( ! _checkAnyFileChild || ! dir )
return false;

if ( _regexp.pattern().isEmpty() )
return false;

FileInfoIterator it( dir->dotEntry() ? dir->dotEntry() : dir );

while ( *it )
{
if ( ! (*it)->isDir() )
return _regexp.exactMatch( (*it)->name() );

++it;
}

return false;
}


//
//---------------------------------------------------------------------------
//
Expand Down Expand Up @@ -104,18 +134,22 @@ void ExcludeRules::add( ExcludeRule * rule )
}


void ExcludeRules::add( const QRegExp & regexp, bool useFullPath )
void ExcludeRules::add( const QRegExp & regexp,
bool useFullPath,
bool checkAnyFileChild )
{
ExcludeRule * rule = new ExcludeRule( regexp, useFullPath );
ExcludeRule * rule = new ExcludeRule( regexp, useFullPath, checkAnyFileChild );
CHECK_NEW( rule );

instance()->add( rule );
}


void ExcludeRules::add( const QString & regexp, bool useFullPath )
void ExcludeRules::add( const QString & regexp,
bool useFullPath,
bool checkAnyFileChild )
{
add( QRegExp( regexp ), useFullPath );
add( QRegExp( regexp ), useFullPath, checkAnyFileChild );
}


Expand Down Expand Up @@ -152,6 +186,30 @@ bool ExcludeRules::match( const QString & fullPath, const QString & fileName )
}


bool ExcludeRules::matchDirectChildren( DirInfo * dir )
{
_lastMatchingRule = 0;
if ( ! dir )
return false;

foreach ( ExcludeRule * rule, _rules )
{
if ( rule->matchDirectChildren( dir ) )
{
_lastMatchingRule = rule;
#if VERBOSE_EXCLUDE_MATCHES

logDebug() << dir << " matches " << rule << endl;

#endif
return true;
}
}

return false;
}


const ExcludeRule * ExcludeRules::matchingRule( const QString & fullPath,
const QString & fileName )
{
Expand Down Expand Up @@ -223,9 +281,10 @@ void ExcludeRules::readSettings()

// Read one exclude rule

QString pattern = settings.value( "Pattern" ).toString();
bool caseSensitive = settings.value( "CaseSensitive", true ).toBool();
bool useFullPath = settings.value( "UseFullPath", false ).toBool();
QString pattern = settings.value( "Pattern" ).toString();
bool caseSensitive = settings.value( "CaseSensitive", true ).toBool();
bool useFullPath = settings.value( "UseFullPath", false ).toBool();
bool checkAnyFileChild = settings.value( "CheckAnyFileChild", false ).toBool();
int syntax = readEnumEntry( settings, "Syntax",
QRegExp::RegExp,
patternSyntaxMapping() );
Expand All @@ -235,7 +294,7 @@ void ExcludeRules::readSettings()
static_cast<QRegExp::PatternSyntax>( syntax ) );

if ( ! pattern.isEmpty() && regexp.isValid() )
add( regexp, useFullPath );
add( regexp, useFullPath, checkAnyFileChild );
else
{
logError() << "Invalid regexp: \"" << regexp.pattern()
Expand Down Expand Up @@ -275,9 +334,10 @@ void ExcludeRules::writeSettings()
groupName.sprintf( "ExcludeRule_%02d", i+1 );
settings.beginGroup( groupName );

settings.setValue( "Pattern", regexp.pattern() );
settings.setValue( "CaseSensitive", regexp.caseSensitivity() == Qt::CaseSensitive );
settings.setValue( "UseFullPath", rule->useFullPath() );
settings.setValue( "Pattern", regexp.pattern() );
settings.setValue( "CaseSensitive", regexp.caseSensitivity() == Qt::CaseSensitive );
settings.setValue( "UseFullPath", rule->useFullPath() );
settings.setValue( "CheckAnyFileChild", rule->checkAnyFileChild() );

writeEnumEntry( settings, "Syntax",
regexp.patternSyntax(),
Expand Down
Loading

0 comments on commit 77a2185

Please sign in to comment.