Introduce TDEStringMatcher, a general purpose iterative string

matching class. Additional information about the class can be
found in tdecore/README.tdestringmatcher.

Introduce concept of a "hidden file matcher" instance of the
TDEStringMatcher class that can be used to extend the definition
"hidden" files beyond traditional "dotfiles".

Create a global default instance of a "hidden file matcher"
for use in applications that don't need their own.

Modify KFileItem class to utilize a hidden file matcher when
evaluating whether or not a filesystem object is hidden.

This commit partially addresses issue # 273. Additional changes
to tdelibs, tdebase/libkonq, and tdebase/konqueror will be
required to resolve that issue.

Signed-off-by: Vincent Reher <tde@4reher.org>
pull/179/head
Vincent Reher 3 years ago
parent 682b8acae4
commit 78905ffa6a

@ -72,7 +72,7 @@ install( FILES
kcalendarsystem.h kcalendarsystemfactory.h kmacroexpander.h
kmanagerselection.h kmountpoint.h kuser.h klockfile.h
kidna.h ktempdir.h kshell.h fixx11h.h kxerrorhandler.h
tdelibs_export.h kde_file.h ktimezones.h
tdelibs_export.h kde_file.h ktimezones.h tdestringmatcher.h
${CMAKE_CURRENT_BINARY_DIR}/kdemacros.h
DESTINATION ${INCLUDE_INSTALL_DIR} )
@ -135,6 +135,7 @@ set( ${target}_SRCS
kprotocolinfo_tdecore.cpp kprotocolinfofactory.cpp kxerrorhandler.cpp
kuser.cpp tdeconfigskeleton.cpp tdeconfigdialogmanager.cpp klockfile.cpp
kqiodevicegzip_p.cpp ktimezones.cpp ksimpledirwatch.cpp
tdestringmatcher.cpp
)
tde_add_library( ${target} SHARED AUTOMOC

@ -0,0 +1,63 @@
The TDEStringMatcher class provides string matching against a list of
one or more TQRegExp objects. The matching functions currently provided
are:
matchAny(): strings match if they match any TQRegExp object in list.
matchAll(): strings match only if the match all TQRegExp objects in list.
The list of TQRegExp objects is constructed using a specially formatted string
that is passed by applications via the generatePatternList() function. This
string is referred to as the "patternString" and is formatted as follows:
* The first character of the patternString defines the character that
is used to split the remainder of the string into match specification
strings. It is recommended (but not required) that this be a character
that does not occur in a match pattern (see below).
* Each match specification string consists of an initial character that
designates the match specification type followed by the corresponding
match specification itself.
* Match specification strings starting with 'o' are followed by zero or
or more letters, each of which are interpreted as setting or unsetting
a match option. Match options remain in effect until explicitly overridden
by subsequent match options. The match option letters that are currently
recognized and processed are:
'w' - Match patterns are to be interpreted as "wildcards"
'r' - Match patterns are to be interpreted as "regexes" (TQRegExp default)
'c' - Matching will be case-sensitive (TQRegExp default)
'c' - Matching will be case-INsensitive
'm' - Regex matching will be "minimal" versus "greedy"
'g' - Regex matching will be "greedy" (TQRegExp default)
* Match specification strings starting with 'p' are followed by one or
more characters that constitute a match pattern. Each match pattern
together with the match options in effect are used to construct
a single TQRegExp object to be used for string matching. This object
will be validated before acceptance.
* Match specification strings starting with any other character are ignored.
Some examples of patternString:
* |ow|p.*|p*~
Dotfiles and kwrite backup files will be matched via wildcards
* /or/p[.][0-9]+$/ow/p.*
Files with a version number suffix will be matched via regex
and dotfiles will be matched via wildcard
Current and potential use of the TDEStringMatcher class include:
* Expansion of definition of "hidden" files, addressing issue # 270.
* Creation of a subclass that provides string parsing functions.
Miscellaneous implementation notes:
* It is the responsibility of applications that use this class to provide
patternString editing (perhaps via UI), storage, and retrieval.
* Regex patterns use TQRegExp::search for matching but wildcard patterns
must use TQRegExp::exactMatch in order for matching to work correctly.

@ -33,6 +33,7 @@
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kcharsets.h>
#include <tdestringmatcher.h>
#include <kiconloader.h>
#ifdef __TDE_HAVE_TDEHWLIB
#include <tdehardwaredevices.h>
@ -131,6 +132,20 @@ KCharsets *TDEGlobal::charsets()
return _charsets;
}
TDEStringMatcher *TDEGlobal::hiddenFileMatcher()
{
if( _hiddenFileMatcher == 0 ) {
TSMTRACE << "TDEGlobal::hiddenFileMatcher(): Global HFM initialization STARTED" << endl;
_hiddenFileMatcher = new TDEStringMatcher();
TDEGlobal::config()->setGroup( "General" );
TQString settings = TDEGlobal::config()->readEntry( "globalHiddenFileSpec", "/oW/.*" );
TSMTRACE << "TDEGlobal::hiddenFileMatcher(): using retrieved patternString = '" << settings << "'" << endl;
_hiddenFileMatcher->generatePatternList( settings );
}
return _hiddenFileMatcher;
}
void TDEGlobal::setActiveInstance(TDEInstance *i)
{
_activeInstance = i;
@ -223,6 +238,8 @@ TDEInstance *TDEGlobal::_instance = 0;
TDEInstance *TDEGlobal::_activeInstance = 0;
TDELocale *TDEGlobal::_locale = 0;
KCharsets *TDEGlobal::_charsets = 0;
TDEStringMatcher *TDEGlobal::_hiddenFileMatcher = 0;
KStaticDeleterList *TDEGlobal::_staticDeleters = 0;
#ifdef WIN32

@ -30,6 +30,7 @@ class TDEHardwareDevices;
class TDEGlobalNetworkManager;
#endif
class TDELocale;
class TDEStringMatcher;
class TDEStandardDirs;
class KStaticDeleterBase;
class KStaticDeleterList;
@ -107,6 +108,12 @@ public:
*/
static KCharsets *charsets();
/**
* The global hidden file matcher.
* @return the global hidden file matcher
*/
static TDEStringMatcher *hiddenFileMatcher();
/**
* Creates a static TQString.
*
@ -174,6 +181,7 @@ public:
static TDEInstance *_instance;
static TDELocale *_locale;
static KCharsets *_charsets;
static TDEStringMatcher *_hiddenFileMatcher;
static KStaticDeleterList *_staticDeleters;
/**

@ -0,0 +1,169 @@
#include "tdestringmatcher.h"
#include <tqregexp.h>
#include <kdebug.h>
class TDEStringMatcher::TDEStringMatcherPrivate {
public:
TQString patternString;
}; // FIXME: This may be too small to warrant a private class :\
TDEStringMatcher::TDEStringMatcher()
{
p = new TDEStringMatcherPrivate;
TSMTRACE << "TDEStringMatcher::TDEStringMatcher: New instance created: " << this << endl;
}
TDEStringMatcher::~TDEStringMatcher()
{
patternList.setAutoDelete( true );
patternList.clear();
delete p;
TSMTRACE << "TDEStringMatcher::TDEStringMatcher: Instance destroyed: " << this << endl;
}
TQString TDEStringMatcher::getPatternString()
{
return p->patternString;
}
bool TDEStringMatcher::generatePatternList( TQString newPatternString )
{
if ( newPatternString == p->patternString )
return true;
TSMTRACE << "TDEStringMatcher::generatePatternList: Proposed pattern string: <" << newPatternString << ">" << endl;
if ( newPatternString.length() < 2 ) {
TSMTRACE << " Input string too short to be interpreted, patterns will be cleared" << endl;
patternList.clear();
p->patternString = "" ;
#ifdef TSMSIGNALS
emit patternsChanged();
#endif // TSMSIGNALS
return true;
}
TQChar patternStringDivider = newPatternString[0];
TSMTRACE << " patternStringDivider = '" << patternStringDivider << "'" << endl;
TQStringList specList = TQStringList::split( patternStringDivider, newPatternString.mid(1), true );
TQRegExp rxWork;
TQPtrList<TQRegExp> rxPatternList;
for ( const TQString &specification : specList ) {
TSMTRACE << " Processing specification string: '" << specification << "'" << endl;
TQChar specificationType = specification[0].lower();
switch ( specificationType ) {
case 'o' : {
TQString optionString = specification.mid(1).lower();
TSMTRACE << " Processing match option string: '" << optionString << "'" << endl;
for ( int i = 0 ; i < optionString.length() ; i++ ) {
TQChar optionChar = optionString[i];
TSMTRACE << " Option character: '" << optionChar << "'" << endl;
switch ( optionChar ) {
case 'w' : rxWork.setWildcard( true ); break;
case 'r' : rxWork.setWildcard( false ); break;
case 'c' : rxWork.setCaseSensitive( true ); break;
case 'i' : rxWork.setCaseSensitive( false ); break;
case 'm' : rxWork.setMinimal( true ); break;
case 'g' : rxWork.setMinimal( false ); break;
default: break;
}
}
TSMTRACE << " Wildcard/CaseSensitive settings: " << rxWork.wildcard() << "/" << rxWork.caseSensitive() << endl;
}
break;
case 'p' : {
TQString pattern = specification.mid(1);
TSMTRACE << " Processing match pattern: '" << pattern << "'" << endl;
if ( pattern.isEmpty() ) {
TSMTRACE << " Empty patterns are not allowed" << endl;
rxPatternList.clear();
return false;
}
rxWork.setPattern( pattern );
if (! rxWork.isValid() ) {
TSMTRACE << " Invalid pattern" << endl;
rxPatternList.clear();
return false;
}
TQRegExp *rxPattern = new TQRegExp( rxWork );
rxPatternList.append( rxPattern );
}
break;
default :
TSMTRACE << " Ignoring unknown specification type '" << specificationType << "'" << endl;
//-Relax, don't overreact: rxPatternList.clear();
//-Relax, don't overreact: return false;
break;
}
}
// patternList.clear(); // no need to do this?
patternList.setAutoDelete( true );
patternList = rxPatternList;
p->patternString = newPatternString;
// rxPatternList.clear(); // no need to do this?
TSMTRACE << " Final patternString: '" << p->patternString << "'" << endl;
TSMTRACE << " Number of regex match patterns in list: '" << patternList.count() << "'" << endl;
#ifdef TSMSIGNALS
TSMTRACE << " Notifying slots of pattern change" << endl;
emit patternsChanged();
TSMTRACE << " All slots have been notified" << endl;
#endif // TSMSIGNALS
TSMTRACE << "TDEStringMatcher::generatePatternList: Patterns were successfully regenerated" << endl << endl;
return true;
}
bool TDEStringMatcher::matchAny( const TQString& stringToMatch )
{
//-Debug: TSMTRACE << "Attempting to match string '" << stringToMatch << "' against stored patterns" << endl;
for ( const TQRegExp *rxPattern : patternList ) {
if (
( rxPattern->wildcard() && rxPattern->exactMatch( stringToMatch ) ) ||
( ! rxPattern->wildcard() && rxPattern->search( stringToMatch ) >= 0)
)
{
//-Debug: TSMTRACE << "String matched pattern: '" << rxPattern->pattern() << "'" << endl;
return true;
}
}
if ( patternList.isEmpty() ) {
//-Debug: TSMTRACE << "Match failed on empty pattern list!" << endl;
return false;
}
else {
//-Debug: TSMTRACE << "Match failed, no pattern matched!" << endl;
return false;
}
}
bool TDEStringMatcher::matchAll( const TQString& stringToMatch )
{
//-Debug: TSMTRACE << "Attempting to match string '" << stringToMatch << "' against ALL stored patterns" << endl;
for ( const TQRegExp *rxPattern : patternList ) {
if ( !
( rxPattern->wildcard() && rxPattern->exactMatch( stringToMatch ) ) ||
( ! rxPattern->wildcard() && rxPattern->search( stringToMatch ) >= 0)
)
{
//-Debug: TSMTRACE << "String failed to match pattern: '" << rxPattern->pattern() << "'" << endl;
return false;
}
}
if ( patternList.isEmpty() ) {
//-Debug: TSMTRACE << "Match failed on empty pattern list!" << endl;
return false;
}
else {
//-Debug: TSMTRACE << "Match succeeded, all patterns matched!" << endl;
return true;
}
}
#include "tdestringmatcher.moc"

@ -0,0 +1,60 @@
#ifndef TDESTRINGMATCHER_H
#define TDESTRINGMATCHER_H
#include "tdelibs_export.h"
#include <tqstring.h>
#include <tqptrlist.h>
#include <tqobject.h>
#define TSMTRACE kdDebug() << "<TSMTRACE> "
#define TSMSIGNALS
/**
*
* Generic string matcher class.
*/
class TDECORE_EXPORT TDEStringMatcher : public TQObject
{
Q_OBJECT
public:
TDEStringMatcher();
~TDEStringMatcher();
/**
Use @param newPatternString to generate @property patternList. Refer to
file README.tdestringmatcher for more information on how the input
string should be formatted.
*/
bool generatePatternList( TQString newPatternString );
/**
Return pattern string from which @property patternList was created.
String is stored in @property TDEStringMatcherPrivate::patternString.
*/
TQString getPatternString();
/**
Methods that determine whether or not @param stringToMatch match
any/all of the TQRegExp objects contained in @property patternList.
*/
bool matchAny( const TQString& stringToMatch );
bool matchAll( const TQString& stringToMatch );
signals:
void patternsChanged();
protected:
TQPtrList<TQRegExp> patternList;
private:
class TDEStringMatcherPrivate;
TDEStringMatcherPrivate *p;
};
#endif

@ -204,6 +204,9 @@ void KFileItem::init( bool _determineMimeTypeOnDemand )
// otherwise, determineMimeType will be able to do better.
m_bMimeTypeKnown = (!_determineMimeTypeOnDemand) || accurate;
}
// Initialize hidden file matching apparatus
setHiddenFileMatcher( TDEGlobal::hiddenFileMatcher() );
}
void KFileItem::readUDSEntry( bool _urlIsDirectory )
@ -830,15 +833,61 @@ bool KFileItem::isWritable() const
return true;
}
bool KFileItem::isHidden() const
void KFileItem::resetHiddenFileMatcher()
{
if ( m_hidden != Auto )
return m_hidden == Hidden;
setHiddenFileMatcher( TDEGlobal::hiddenFileMatcher() );
}
void KFileItem::setHiddenFileMatcher( TDEStringMatcher *hiddenFileMatcher )
{
TSMTRACE << "KFileItem::setHiddenFileMatcher(...) called for " << m_url.fileName() << " [" << hiddenFileMatcher->getPatternString() << "]" <<endl ;
if ( hiddenFileMatcher == m_pHiddenFileMatcher )
return;
if ( hiddenFileMatcher == 0 || hiddenFileMatcher == nullptr ) {
kdWarning() << "KFileItem::setHiddenFileMatcher: refusing to process null pointer passed by caller" << endl;
return;
}
#ifdef TSMSIGNALS
if ( m_pHiddenFileMatcher != 0 && m_pHiddenFileMatcher != nullptr ) {
TSMTRACE << " Attempting to disconnect slots from hidden file matcher signals ... " << endl;
if ( disconnect( m_pHiddenFileMatcher, 0, 0, 0 ) )
TSMTRACE << " ... all slots successfully disconnected" << endl;
}
#endif // TSMSIGNALS
TSMTRACE << " Changing hidden file matcher from " << m_pHiddenFileMatcher << " to " << hiddenFileMatcher << endl;
m_pHiddenFileMatcher = hiddenFileMatcher;
#ifdef TSMSIGNALS
TSMTRACE << " Attempting to reconnect slots to hidden file matcher signals ... " << endl;
if ( connect( m_pHiddenFileMatcher, TQT_SIGNAL( destroyed() ), this, TQT_SLOT( resetHiddenFileMatcher() ) ) )
TSMTRACE << " Connected slot resethiddenFileMatcher() to signal destroyed()" << endl;
if ( connect( m_pHiddenFileMatcher, TQT_SIGNAL( patternsChanged() ), this, TQT_SLOT( reEvaluateHidden() ) ) )
TSMTRACE << " Connected slot reEvaluateHidden() to signal patternsChanged()" << endl;
#endif // TSMSIGNALS
TSMTRACE << "KFileItem::setHiddenFileMatcher(...) finished, calling reEvaluateHidden()" <<endl ;
reEvaluateHidden();
}
void KFileItem::reEvaluateHidden()
{
TSMTRACE << "KFileItem::reEvaluateHidden() called for " << m_url.fileName() <<endl ;
if ( !m_url.isEmpty() )
return m_url.fileName()[0] == '.';
m_bHiddenByMatcher = m_pHiddenFileMatcher->matchAny( m_url.fileName() );
else // should never happen
return m_strName[0] == '.';
m_bHiddenByMatcher = m_pHiddenFileMatcher->matchAny( m_strName );
TSMTRACE << "KFileItem::reEvaluateHidden() completed for " << m_url.fileName() << " [" << m_bHiddenByMatcher << "]" <<endl ;
}
bool KFileItem::isHidden() const
{
if ( m_hidden != Auto )
return m_hidden == Hidden;
// TSMTRACE << "KFileItem::isHidden() called for " << m_url.fileName() << " [" << m_bHiddenByMatcher << "]" <<endl ;
return m_bHiddenByMatcher;
}
bool KFileItem::isDir() const
@ -1276,3 +1325,7 @@ TQDataStream & operator>> ( TQDataStream & s, KFileItem & a )
a.refresh();
return s;
}
//#ifdef TSMSIGNALS
#include "tdefileitem.moc"
//#endif // TSMSIGNALS

@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <tqptrlist.h>
#include <tdestringmatcher.h>
#include <tdeio/global.h>
#include <kurl.h>
#include <kacl.h>
@ -38,8 +39,9 @@
* (UDSEntry isn't very friendly to use).
* It includes many file attributes such as mimetype, icon, text, mode, link...
*/
class TDEIO_EXPORT KFileItem
class TDEIO_EXPORT KFileItem : public TQObject
{
Q_OBJECT
public:
enum { Unknown = (mode_t) - 1 };
@ -229,15 +231,34 @@ public:
bool isWritable() const;
/**
* Checks whether the file is hidden.
* @return true if the file is hidden.
* Sets object that encapsulates criteria for determining whether or not
* a filesystem entity is hidden based on characteristics of its name.
* Object is stored in @property m_pHiddenFileMatcher.
*/
bool isHidden() const;
void setHiddenFileMatcher( TDEStringMatcher *hiddenFileMatcher );
public slots:
/**
* Sets @property m_pHiddenFileMatcher to the global hidden file matcher.
*/
void resetHiddenFileMatcher();
/**
* Checks whether or not the current filesystem object is "hidden" by
* calling the matchAny() method of the TDEStringMatcher object stored
* in @property m_pHiddenFileMatcher. Result of this check is cached in
* @property m_bHiddenByMatcher.
*/
void reEvaluateHidden();
public:
/**
* Returns the link destination if isLink() == true.
* @return the link destination. TQString::null if the item is not a link
* @return true if the file is "hidden".
*/
bool isHidden() const;
TQString linkDest() const;
/**
@ -667,9 +688,21 @@ private:
bool m_bMimeTypeKnown:1;
// Auto: check leading dot.
// Auto: always check if hidden.
enum { Auto, Hidden, Shown } m_hidden:3;
/**
* Caches result of most recent call to method reEvaluateHidden().
*/
bool m_bHiddenByMatcher:1;
/**
* Object that encapsulates criteria for determining whether or not
* this filesystem entity is hidden based on characteristics of its
* name. This property is set by method setHiddenFileMatcher().
*/
TDEStringMatcher *m_pHiddenFileMatcher = nullptr;
// For special case like link to dirs over FTP
TQString m_guessedMimeType;
mutable TQString m_access;

Loading…
Cancel
Save