Page MenuHomePhorge

No OneTemporary

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

Mime Type
text/x-diff
Expires
Fri, Nov 1, 8:29 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10075038
Default Alt Text
(19 KB)

Event Timeline