Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16569456
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/kio/kdirwatch.cpp b/kio/kdirwatch.cpp
index 4487f7a15f..10d561f5d1 100644
--- a/kio/kdirwatch.cpp
+++ b/kio/kdirwatch.cpp
@@ -1,426 +1,492 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <qtimer.h>
#include <qstringlist.h>
#include <qfile.h>
+#include <qdir.h>
#include <qsocketnotifier.h>
#include <kapp.h>
#include <kdebug.h>
#include <config.h>
#include "kdirwatch.h"
#include <iostream.h>
#ifdef HAVE_FAM
#include <fam.h>
#endif
+enum directoryStatus { Normal = 0, NonExistent };
+
class KDirWatchPrivate
{
public:
KDirWatchPrivate() { }
~KDirWatchPrivate() { }
struct Entry
{
time_t m_ctime;
int m_clients;
+ directoryStatus m_status;
#ifdef HAVE_FAM
FAMRequest fr;
+ QString watchPath;
#endif
};
typedef QMap<QString,Entry> EntryMap;
QTimer *timer;
EntryMap m_mapDirs;
int freq;
static KDirWatch* s_pSelf;
#ifdef HAVE_FAM
QSocketNotifier *sn;
FAMConnection fc;
bool use_fam;
bool emitEvents;
#endif
};
#define NO_NOTIFY (time_t) 0
KDirWatch* KDirWatch::s_pSelf = 0L;
// CHANGES:
// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
// May 23. 1998 - Removed static pointer - you can have more instances.
// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
// call (or need) KFM. No more URL's - just plain paths. (sven)
// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
// deep copies for list of dirs. (sven)
// Mar 28. 1998 - Created. (sven)
KDirWatch* KDirWatch::self()
{
if ( !s_pSelf )
s_pSelf = new KDirWatch;
return s_pSelf;
}
KDirWatch::KDirWatch (int _freq)
{
d = new KDirWatchPrivate;
d->timer = new QTimer(this);
connect (d->timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
d->freq = _freq;
#ifdef HAVE_FAM
// It's possible that FAM server can't be started
if (FAMOpen(&(d->fc)) ==0) {
kdDebug(7001) << "KDirWatch: Using FAM" << endl;
d->use_fam=true;
d->emitEvents = true;
d->sn = new QSocketNotifier( FAMCONNECTION_GETFD(&(d->fc)),
QSocketNotifier::Read, this);
connect( d->sn, SIGNAL(activated(int)),
this, SLOT(famEventReceived()) );
}
else {
kdDebug(7001) << "KDirWatch: Can't use FAM" << endl;
d->use_fam=false;
}
#endif
}
KDirWatch::~KDirWatch()
{
d->timer->stop();
// delete d->timer; timer was created with 'this' as parent!
#ifdef HAVE_FAM
if (d->use_fam) {
FAMClose(&(d->fc));
kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
}
#endif
delete d; d = 0;
}
+#ifdef HAVE_FAM
+static QString findWatchPath(const QString &path)
+{
+ struct stat statbuff;
+ QString watchPath = path;
+ while (stat(QFile::encodeName(watchPath), &statbuff) == -1)
+ watchPath = QDir::cleanDirPath(watchPath+"/..");
+ return watchPath;
+}
+#endif
+
void KDirWatch::addDir( const QString& _path )
{
QString path = _path;
if ( path.length() > 1 && path.right(1) == "/" )
path.truncate( path.length() - 1 );
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.find( path );
if ( it != d->m_mapDirs.end() )
{
(*it).m_clients++;
return;
}
struct stat statbuff;
- stat( QFile::encodeName(path), &statbuff );
KDirWatchPrivate::Entry e;
e.m_clients = 1;
- e.m_ctime = statbuff.st_ctime;
+ if (stat( QFile::encodeName(path), &statbuff ) == 0)
+ {
+ e.m_ctime = statbuff.st_ctime;
+ e.m_status = Normal;
+ }
+ else
+ {
+ e.m_ctime = NO_NOTIFY;
+ e.m_status = NonExistent;
+ }
#ifdef HAVE_FAM
+ QString famPath = path;
+ if (e.m_status == NonExistent)
+ {
+ // If the directory does not exist we watch the first parent directory
+ // that DOES exist and wait for the directory to be created.
+ famPath = findWatchPath(famPath);
+ e.watchPath = famPath;
+ }
if (d->use_fam) {
- FAMMonitorDirectory(&(d->fc), QFile::encodeName(path), &(e.fr), 0);
- // kdDebug(7001) << "KDirWatch added " <<
- // QFile::encodeName(path) << " -> FAMReq " << FAMREQUEST_GETREQNUM(&(e.fr)) << endl;
+ FAMMonitorDirectory(&(d->fc), QFile::encodeName(famPath), &(e.fr), 0);
+ kdDebug(7001) << "KDirWatch added " <<
+ QFile::encodeName(path) << " -> FAMReq " << FAMREQUEST_GETREQNUM(&(e.fr)) << endl;
}
#endif
d->m_mapDirs.insert( path, e );
#ifdef HAVE_FAM
// if FAM server can't be used, fall back to good old timer...
if (!d->use_fam)
#endif
if ( d->m_mapDirs.count() == 1 ) // if this was first entry (=timer was stopped)
d->timer->start(d->freq); // then start the timer
}
time_t KDirWatch::ctime( const QString &_path )
{
if ( d->m_mapDirs.isEmpty() )
return 0;
QString path = _path;
if ( path.right(1) == "/" )
path.truncate( path.length() - 1 );
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.find( path );
if ( it == d->m_mapDirs.end() )
return 0;
return (*it).m_ctime;
}
void KDirWatch::removeDir( const QString& _path )
{
if ( d->m_mapDirs.isEmpty() )
return;
QString path = _path;
if ( path.right(1) == "/" )
path.truncate( path.length() - 1 );
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.find( path );
if ( it == d->m_mapDirs.end() )
return;
(*it).m_clients--;
if ( (*it).m_clients > 0 )
return;
#ifdef HAVE_FAM
if (d->use_fam) {
FAMCancelMonitor(&(d->fc), &((*it).fr) );
// kdDebug(7001) << "KDirWatch deleted: " <<
// QFile::encodeName(path) << " (FAMReq " << FAMREQUEST_GETREQNUM(&((*it).fr)) << ")" << endl;
}
#endif
d->m_mapDirs.remove( it );
#ifdef HAVE_FAM
if (!d->use_fam)
#endif
if( d->m_mapDirs.isEmpty() )
d->timer->stop(); // stop timer if list empty
}
bool KDirWatch::stopDirScan( const QString& _path )
{
if ( d->m_mapDirs.isEmpty() )
return false;
QString path = _path;
if ( path.right(1) == "/" )
path.truncate( path.length() - 1 );
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.find( path );
if ( it == d->m_mapDirs.end() )
return false;
(*it).m_ctime = NO_NOTIFY;
#ifdef HAVE_FAM
if (d->use_fam) {
FAMSuspendMonitor(&(d->fc), &((*it).fr) );
}
#endif
return true;
}
bool KDirWatch::restartDirScan( const QString& _path )
{
if ( d->m_mapDirs.isEmpty() )
return false;
QString path = _path;
if ( path.right(1) == "/" )
path.truncate( path.length() - 1 );
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.find( path );
if ( it == d->m_mapDirs.end() )
return false;
struct stat statbuff;
stat( QFile::encodeName(path), &statbuff );
(*it).m_ctime = statbuff.st_ctime;
#ifdef HAVE_FAM
if (d->use_fam) {
FAMResumeMonitor(&(d->fc), &((*it).fr) );
}
#endif
return true;
}
void KDirWatch::stopScan()
{
#ifdef HAVE_FAM
if (d->use_fam)
d->emitEvents = false;
else
#endif
d->timer->stop();
}
void KDirWatch::startScan( bool notify, bool skippedToo )
{
if (!notify)
resetList(skippedToo);
#ifdef HAVE_FAM
if (d->use_fam)
d->emitEvents = true;
else
#endif
d->timer->start(d->freq);
}
// Protected:
void KDirWatch::resetList( bool skippedToo )
{
if ( d->m_mapDirs.isEmpty() )
return;
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.begin();
for( ; it != d->m_mapDirs.end(); ++it )
{
- if ( (*it).m_ctime != NO_NOTIFY || skippedToo )
+ if ( (*it).m_ctime != NO_NOTIFY || skippedToo)
{
struct stat statbuff;
- stat( QFile::encodeName(it.key()), &statbuff );
- (*it).m_ctime = statbuff.st_ctime;
+ if (stat( QFile::encodeName(it.key()), &statbuff ) == 0)
+ {
+ (*it).m_ctime = statbuff.st_ctime;
+ (*it).m_status = Normal;
+ }
+ else
+ {
+ (*it).m_ctime = NO_NOTIFY;
+ (*it).m_status = NonExistent;
+ }
}
}
}
void KDirWatch::slotRescan()
{
QStringList del;
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.begin();
for( ; it != d->m_mapDirs.end(); ++it )
{
struct stat statbuff;
if ( stat( QFile::encodeName(it.key()), &statbuff ) == -1 )
{
- kdDebug(7001) << "KDirWatch emitting deleted " << it.key() << endl;
- emit deleted( it.key() );
- del.append( it.key() );
+ if ((*it).m_status == Normal)
+ {
+ kdDebug(7001) << "KDirWatch emitting deleted " << it.key() << endl;
+ emit deleted( it.key() );
+ del.append( it.key() );
+ }
continue; // iterator incremented
}
- if ( statbuff.st_ctime != (*it).m_ctime &&
- (*it).m_ctime != NO_NOTIFY)
+ if ( ((statbuff.st_ctime != (*it).m_ctime) &&
+ ((*it).m_ctime != NO_NOTIFY)) ||
+ ((*it).m_status == NonExistent) )
{
(*it).m_ctime = statbuff.st_ctime;
+ (*it).m_status = Normal;
kdDebug(7001) << "KDirWatch emitting dirty " << it.key() << endl;
emit dirty( it.key() );
}
}
QStringList::Iterator it2 = del.begin();
for( ; it2 != del.end(); ++it2 )
d->m_mapDirs.remove( *it2 );
}
bool KDirWatch::contains( const QString& _path ) const
{
QString path = _path;
if ( path.right(1) == "/" )
path.truncate( path.length() - 1 );
return d->m_mapDirs.contains( path );
}
void KDirWatch::setFileDirty( const QString & _file )
{
emit fileDirty( _file );
}
#ifdef HAVE_FAM
void KDirWatch::famEventReceived()
{
if (!d->use_fam || !d->emitEvents) return;
FAMEvent fe;
if (FAMNextEvent(&(d->fc), &fe) == -1)
{
kdWarning(7001) << "FAM connection problem, switching to polling." << endl;
d->use_fam = false;
delete d->sn; d->sn = 0;
startScan(false, false);
return;
}
int reqNum = FAMREQUEST_GETREQNUM(&(fe.fr));
// Don't be too verbose ;-)
if ((fe.code == FAMExists) || (fe.code == FAMEndExist)) return;
kdDebug(7001) << "KDirWatch processing FAM event ("
<< ((fe.code == FAMChanged) ? "FAMChanged" :
(fe.code == FAMDeleted) ? "FAMDeleted" :
(fe.code == FAMStartExecuting) ? "FAMStartExecuting" :
(fe.code == FAMStopExecuting) ? "FAMStopExecuting" :
(fe.code == FAMCreated) ? "FAMCreated" :
(fe.code == FAMMoved) ? "FAMMoved" :
(fe.code == FAMAcknowledge) ? "FAMAcknowledge" :
(fe.code == FAMExists) ? "FAMExists" :
(fe.code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
<< ", " << &(fe.filename[0]) << ", Req " << reqNum << ")" << endl;
if (fe.code == FAMDeleted) {
// WABA: We ignore changes to ".directory*" files because they
// tend to be generated as a result of 'dirty' events. Which
// leads to a never ending stream of cause & result.
if (strncmp(fe.filename, ".directory", 10) == 0) return;
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.begin();
for( ; it != d->m_mapDirs.end(); ++it )
if ( FAMREQUEST_GETREQNUM( &((*it).fr) ) == reqNum ) {
+ if ((*it).m_status == NonExistent) return; // Ignore
if (fe.filename[0] == '/') {
kdDebug(7001) << "KDirWatch emitting deleted " << it.key() << endl;
emit deleted ( it.key() );
- d->m_mapDirs.remove( it.key() );
+ (*it).m_status = NonExistent;
+// d->m_mapDirs.remove( it.key() );
}
else {
kdDebug(7001) << "KDirWatch emitting dirty " << it.key() << endl;
emit dirty( it.key() );
}
return;
}
}
else if ((fe.code == FAMChanged) || (fe.code == FAMCreated)) {
// WABA: We ignore changes to ".directory*" files because they
// tend to be generated as a result of 'dirty' events. Which
// leads to a never ending stream of cause & result.
if (strncmp(fe.filename, ".directory", 10) == 0) return;
KDirWatchPrivate::EntryMap::Iterator it = d->m_mapDirs.begin();
- for( ; it != d->m_mapDirs.end(); ++it )
+ for( ; it != d->m_mapDirs.end();++it)
if ( FAMREQUEST_GETREQNUM( &((*it).fr) ) == reqNum ) {
- kdDebug(7001) << "KDirWatch emitting dirty " << it.key() << endl;
- emit dirty( it.key() );
+ QString path = it.key();
+ const KDirWatchPrivate::Entry &e = (*it);
+
+ if (e.m_status == NonExistent)
+ {
+ // A change occurred in the parent of a non-existing directory.
+ // check if we need to update the directory to watch and
+ // if the directory we are interested in happened to be created.
+ QString watchPath = findWatchPath(path);
+ if (watchPath != e.watchPath)
+ {
+ removeDir(path); // This deletes 'e' and invalidates 'it'!!
+ addDir(path);
+ }
+ if (path != watchPath)
+ return;
+ // If the watch path is the same as the path,
+ // we have been created!
+ }
+ kdDebug(7001) << "KDirWatch emitting dirty " << path << endl;
+ emit dirty( path );
return;
}
}
}
#else
void KDirWatch::famEventReceived() {}
#endif
#include "kdirwatch.moc"
//sven
diff --git a/kio/kdirwatch.h b/kio/kdirwatch.h
index 3e63e8e38f..3b41f91422 100644
--- a/kio/kdirwatch.h
+++ b/kio/kdirwatch.h
@@ -1,192 +1,195 @@
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef _KDIRWATCH_H
#define _KDIRWATCH_H
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <qtimer.h>
#include <qmap.h>
#define kdirwatch KDirWatch::self()
class KDirWatchPrivate;
/**
* Watch directories for changes.
*
* It uses @p stat (2) and
* compares stored and actual changed time of directories. If
* there is a difference it notifies about the change. Directories can be
* added, removed from the list and scanning of particular directories
* can be stopped and restarted. The whole class can be stopped and
* restarted. Directories can be added/removed from list in
* any state.
* When a watched directory is changed, @ref KDirWatch will emit
* the signal @ref dirty().
*
* If a watched directory gets deleted, @ref KDirwatch will remove it from
* the list, and emit the signal @ref deleted().
*
+ * It's possible to watch a directory that doesn't exist yet.
+ * @ref KDirWatch will emit a @ref dirty() signal when it is created.
+ *
* @short Class for watching directory changes.
* @author Sven Radej <sven@lisa.exp.univie.ac.at>
*/
class KDirWatch : public QObject
{
Q_OBJECT
public:
/**
* Constructor.
*
* Does not begin scanning until @ref startScan()
* is called. Default frequency is 500 ms. The created list of
* directories has deep copies.
*/
KDirWatch ( int freq = 500 );
/**
* Destructor.
*
* Stops scanning and cleans up.
*/
~KDirWatch();
/**
* Add a directory to the list of directories to be watched.
*
* The list makes deep copies.
*/
void addDir(const QString& path);
/**
* Retrieve the time the directory was last changed.
*/
time_t ctime(const QString& path);
/**
* Remove a directory from the list of scanned directories.
*
* If specified path is not in the list this does nothing.
*/
void removeDir(const QString& path);
/**
* Stop scanning the specified path.
*
* The @p path is not deleted from the interal just, it is just skipped.
* Call this function when you perform an huge operation
* on this directory (copy/move big files or many files). When finished,
* call @ref restartDirScan (path).
* Returns @p false if specified path is not in list, @p true otherwise.
*/
bool stopDirScan(const QString& path);
/**
* Restart scanning for specified path.
*
* Resets ctime. It doesn't notify
* the change (by emitted a signal), since the ctime value is reset.
*
* Call it when you are finished with big operations on that path,
* @em and when @em you have refreshed that path. Returns @p false
* if specified path is not in list, @p true otherwise.
*/
bool restartDirScan(const QString& path);
/**
* Start scanning of all dirs in list.
*
* If notify is @p true, all changed directories (since @ref
* stopScan() call) will be notified for refresh. If notify is
* @p false, all ctimes will be reset (except those who are stopped,
* but only if @p skippedToo is @p false) and changed dirs won't be
* notified. You can start scanning even if the list is
* empty. First call should be called with @p false or else all
* directories
* in list will be notified. If
* @p skippedToo is true, the skipped directoris (scanning of which was
* stopped with @ref stopDirScan() ) will be reset and notified
* for change. Otherwise, stopped directories will continue to be
* unnotified.
*/
void startScan( bool notify=false, bool skippedToo=false );
/**
* Stop scanning of all directories in internal list.
*
* The timer is stopped, but the list is not cleared.
*/
void stopScan();
bool contains( const QString& path ) const;
/** @ref signal fileDirty() */
void setFileDirty( const QString & _file );
static KDirWatch* self();
signals:
/**
* Emitted when a directory is changed.
*
* The new ctime is set
* before the signal is emited.
*/
void dirty (const QString& dir);
/**
* Emitted when @ref KDirWatch learns that the file
* @p _file has changed.
*
* This happens for instance when a .desktop file
* gets a new icon - but this isn't automatic, one has to call
* @ref setFileDirty() for this signal to be emitted.
*
* Note that KDirNotify is network transparent and
* broadcasts to all processes, so it sort of supersedes this.
*/
void fileDirty (const QString& _file);
/**
* Emitted when directory is deleted.
*
* When you receive
* this signal, the directory is not yet deleted from the list. You will
* receive this signal only once, because one directory cannot be
* deleted more than once. Please, forget the last^H^H^H^Hprevious
* sentence.
*/
void deleted (const QString& dir);
protected:
void resetList (bool reallyall);
protected slots:
void slotRescan();
void famEventReceived();
private:
KDirWatchPrivate *d;
static KDirWatch* s_pSelf;
};
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 1, 8:29 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10075038
Default Alt Text
(19 KB)
Attached To
Mode
rKL kdelibs
Attached
Detach File
Event Timeline
Log In to Comment