kio Library API Documentation

kdirwatch.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /* This file is part of the KDE libraries
00003    Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 
00021 // CHANGES:
00022 // Februar 2002 - Add file watching and remote mount check for STAT
00023 // Mar 30, 2001 - Native support for Linux dir change notification.
00024 // Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
00025 // May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
00026 // May 23. 1998 - Removed static pointer - you can have more instances.
00027 // It was Needed for KRegistry. KDirWatch now emits signals and doesn't
00028 // call (or need) KFM. No more URL's - just plain paths. (sven)
00029 // Mar 29. 1998 - added docs, stop/restart for particular Dirs and
00030 // deep copies for list of dirs. (sven)
00031 // Mar 28. 1998 - Created.  (sven)
00032 
00033 
00034 #include <config.h>
00035 
00036 #ifdef HAVE_DNOTIFY
00037 #include <unistd.h>
00038 #include <time.h>
00039 #include <fcntl.h>
00040 #include <signal.h>
00041 #include <errno.h>
00042 #endif
00043 
00044 #include <sys/stat.h>
00045 #include <assert.h>
00046 #include <qdir.h>
00047 #include <qfile.h>
00048 #include <qintdict.h>
00049 #include <qptrlist.h>
00050 #include <qsocketnotifier.h>
00051 #include <qstringlist.h>
00052 #include <qtimer.h>
00053 
00054 #include <kapplication.h>
00055 #include <kdebug.h>
00056 #include <kconfig.h>
00057 #include <kglobal.h>
00058 #include <kstaticdeleter.h>
00059 
00060 #include "kdirwatch.h"
00061 #include "kdirwatch_p.h"
00062 #include "global.h" //  KIO::probably_slow_mounted
00063 
00064 #define NO_NOTIFY (time_t) 0
00065 
00066 static KDirWatchPrivate* dwp_self = 0;
00067 
00068 #ifdef HAVE_DNOTIFY
00069 
00070 #include <sys/utsname.h>
00071 
00072 static int dnotify_signal = 0;
00073 
00074 /* DNOTIFY signal handler
00075  *
00076  * As this is called asynchronously, only a flag is set and
00077  * a rescan is requested.
00078  * This is done by writing into a pipe to trigger a QSocketNotifier
00079  * watching on this pipe: a timer is started and after a timeout,
00080  * the rescan is done.
00081  */
00082 void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
00083 {
00084   if (!dwp_self) return;
00085 
00086   // write might change errno, we have to save it and restore it
00087   // (Richard Stevens, Advanced programming in the Unix Environment)
00088   int saved_errno = errno;
00089 
00090   Entry* e = dwp_self->fd_Entry.find(si->si_fd);
00091 
00092 //  kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
00093 //      << QString(e ? e->path:"unknown") << endl;
00094 
00095   if(!e || e->dn_fd != si->si_fd) {
00096     qDebug("fatal error in KDirWatch");
00097   } else
00098     e->dirty = true;
00099 
00100   char c = 0;
00101   write(dwp_self->mPipe[1], &c, 1);
00102   errno = saved_errno;
00103 }
00104 
00105 static struct sigaction old_sigio_act;
00106 /* DNOTIFY SIGIO signal handler
00107  *
00108  * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
00109  */
00110 void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
00111 {
00112   if (dwp_self)
00113   {
00114     // write might change errno, we have to save it and restore it
00115     // (Richard Stevens, Advanced programming in the Unix Environment)
00116     int saved_errno = errno;
00117 
00118     dwp_self->rescan_all = true;
00119     char c = 0;
00120     write(dwp_self->mPipe[1], &c, 1);
00121 
00122     errno = saved_errno;
00123   }
00124 
00125   // Call previous signal handler
00126   if (old_sigio_act.sa_flags & SA_SIGINFO)
00127   {
00128     if (old_sigio_act.sa_sigaction)
00129       (*old_sigio_act.sa_sigaction)(sig, si, p);
00130   }
00131   else
00132   {
00133     if ((old_sigio_act.sa_handler != SIG_DFL) &&
00134         (old_sigio_act.sa_handler != SIG_IGN))
00135       (*old_sigio_act.sa_handler)(sig);
00136   }
00137 }
00138 #endif
00139 
00140 
00141 //
00142 // Class KDirWatchPrivate (singleton)
00143 //
00144 
00145 /* All entries (files/directories) to be watched in the
00146  * application (coming from multiple KDirWatch instances)
00147  * are registered in a single KDirWatchPrivate instance.
00148  *
00149  * At the moment, the following methods for file watching
00150  * are supported:
00151  * - Polling: All files to be watched are polled regularly
00152  *   using stat (more precise: QFileInfo.lastModified()).
00153  *   The polling frequency is determined from global kconfig
00154  *   settings, defaulting to 500 ms for local directories
00155  *   and 5000 ms for remote mounts
00156  * - FAM (File Alternation Monitor): first used on IRIX, SGI
00157  *   has ported this method to LINUX. It uses a kernel part
00158  *   (IMON, sending change events to /dev/imon) and a user
00159  *   level damon (fam), to which applications connect for
00160  *   notification of file changes. For NFS, the fam damon
00161  *   on the NFS server machine is used; if IMON is not built
00162  *   into the kernel, fam uses polling for local files.
00163  * - DNOTIFY: In late LINUX 2.3.x, directory notification was
00164  *   introduced. By opening a directory, you can request for
00165  *   UNIX signals to be sent to the process when a directory
00166  *   is changed.
00167  */
00168 
00169 KDirWatchPrivate::KDirWatchPrivate()
00170 {
00171   timer = new QTimer(this);
00172   connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00173   freq = 3600000; // 1 hour as upper bound
00174   statEntries = 0;
00175   delayRemove = false;
00176   m_ref = 0;
00177 
00178   KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
00179   m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
00180   m_PollInterval = config.readNumEntry("PollInterval", 500);
00181 
00182   QString available("Stat");
00183 
00184   // used for FAM and DNOTIFY
00185   rescan_all = false;
00186   connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
00187 
00188 #ifdef HAVE_FAM
00189   // It's possible that FAM server can't be started
00190   if (FAMOpen(&fc) ==0) {
00191     available += ", FAM";
00192     use_fam=true;
00193     sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
00194                   QSocketNotifier::Read, this);
00195     connect( sn, SIGNAL(activated(int)),
00196          this, SLOT(famEventReceived()) );
00197   }
00198   else {
00199     kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
00200     use_fam=false;
00201   }
00202 #endif
00203 
00204 #ifdef HAVE_DNOTIFY
00205   supports_dnotify = true; // not guilty until proven guilty
00206   struct utsname uts;
00207   int major, minor, patch;
00208   if (uname(&uts) < 0)
00209     supports_dnotify = false; // *shrug*
00210   else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
00211     supports_dnotify = false; // *shrug*
00212   else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
00213     kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
00214     supports_dnotify = false;
00215   }
00216 
00217   if( supports_dnotify ) {
00218     available += ", DNotify";
00219 
00220     pipe(mPipe);
00221     fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
00222     fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
00223     mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
00224     connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
00225     // Install the signal handler only once
00226     if ( dnotify_signal == 0 )
00227     {
00228        dnotify_signal = SIGRTMIN + 8;
00229 
00230        struct sigaction act;
00231        act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
00232        sigemptyset(&act.sa_mask);
00233        act.sa_flags = SA_SIGINFO;
00234 #ifdef SA_RESTART
00235        act.sa_flags |= SA_RESTART;
00236 #endif
00237        sigaction(dnotify_signal, &act, NULL);
00238 
00239        act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
00240        sigaction(SIGIO, &act, &old_sigio_act);
00241     }
00242   }
00243   else
00244   {
00245     mPipe[0] = -1;
00246     mPipe[1] = -1;
00247   }
00248 #endif
00249 
00250   kdDebug(7001) << "Available methods: " << available << endl;
00251 }
00252 
00253 /* This should never be called, but doesn't harm */
00254 KDirWatchPrivate::~KDirWatchPrivate()
00255 {
00256   timer->stop();
00257 
00258   /* remove all entries being watched */
00259   removeEntries(0);
00260 
00261 #ifdef HAVE_FAM
00262   if (use_fam) {
00263     FAMClose(&fc);
00264     kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
00265   }
00266 #endif
00267 #ifdef HAVE_DNOTIFY
00268   close(mPipe[0]);
00269   close(mPipe[1]);
00270 #endif
00271 }
00272 
00273 void KDirWatchPrivate::slotActivated()
00274 {
00275 #ifdef HAVE_DNOTIFY
00276    char dummy_buf[100];
00277    read(mPipe[0], &dummy_buf, 100);
00278 
00279    if (!rescan_timer.isActive())
00280       rescan_timer.start(m_PollInterval, true);
00281 #endif
00282 }
00283 
00284 /* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned.
00285  * We first need to mark all yet nonexistent, but possible created
00286  * entries as dirty...
00287  */
00288 void KDirWatchPrivate::Entry::propagate_dirty()
00289 {
00290   Entry* sub_entry;
00291   for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
00292   {
00293      if (!sub_entry->dirty)
00294      {
00295         sub_entry->dirty = true;
00296         sub_entry->propagate_dirty();
00297      }
00298   }
00299 }
00300 
00301 
00302 /* A KDirWatch instance is interested in getting events for
00303  * this file/Dir entry.
00304  */
00305 void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
00306 {
00307   Client* client = m_clients.first();
00308   for(;client; client = m_clients.next())
00309     if (client->instance == instance) break;
00310 
00311   if (client) {
00312     client->count++;
00313     return;
00314   }
00315 
00316   client = new Client;
00317   client->instance = instance;
00318   client->count = 1;
00319   client->watchingStopped = instance->isStopped();
00320   client->pending = NoChange;
00321 
00322   m_clients.append(client);
00323 }
00324 
00325 void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
00326 {
00327   Client* client = m_clients.first();
00328   for(;client; client = m_clients.next())
00329     if (client->instance == instance) break;
00330 
00331   if (client) {
00332     client->count--;
00333     if (client->count == 0) {
00334       m_clients.removeRef(client);
00335       delete client;
00336     }
00337   }
00338 }
00339 
00340 /* get number of clients */
00341 int KDirWatchPrivate::Entry::clients()
00342 {
00343   int clients = 0;
00344   Client* client = m_clients.first();
00345   for(;client; client = m_clients.next())
00346     clients += client->count;
00347 
00348   return clients;
00349 }
00350 
00351 
00352 KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
00353 {
00354 // we only support absolute paths
00355   if (_path.left(1) != "/") {
00356     return 0;
00357   }
00358 
00359   QString path = _path;
00360 
00361   if ( path.length() > 1 && path.right(1) == "/" )
00362     path.truncate( path.length() - 1 );
00363 
00364   EntryMap::Iterator it = m_mapEntries.find( path );
00365   if ( it == m_mapEntries.end() )
00366     return 0;
00367   else
00368     return &(*it);
00369 }
00370 
00371 // set polling frequency for a entry and adjust global freq if needed
00372 void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
00373 {
00374   e->freq = newFreq;
00375 
00376   // a reasonable frequency for the global polling timer
00377   if (e->freq < freq) {
00378     freq = e->freq;
00379     if (timer->isActive()) timer->changeInterval(freq);
00380     kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
00381   }
00382 }
00383 
00384 
00385 #if defined(HAVE_FAM)
00386 // setup FAM notification, returns false if not possible
00387 bool KDirWatchPrivate::useFAM(Entry* e)
00388 {
00389   if (!use_fam) return false;
00390 
00391   e->m_mode = FAMMode;
00392   e->dirty = false;
00393 
00394   if (e->isDir) {
00395     if (e->m_status == NonExistent) {
00396       // If the directory does not exist we watch the parent directory
00397       addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00398     }
00399     else {
00400       int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
00401                    &(e->fr), e);
00402       if (res<0) {
00403     e->m_mode = UnknownMode;
00404     use_fam=false;
00405     return false;
00406       }
00407       kdDebug(7001) << " Setup FAM (Req "
00408             << FAMREQUEST_GETREQNUM(&(e->fr))
00409             << ") for " << e->path << endl;
00410     }
00411   }
00412   else {
00413     if (e->m_status == NonExistent) {
00414       // If the file does not exist we watch the directory
00415       addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00416     }
00417     else {
00418       int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
00419                    &(e->fr), e);
00420       if (res<0) {
00421     e->m_mode = UnknownMode;
00422     use_fam=false;
00423     return false;
00424       }
00425 
00426       kdDebug(7001) << " Setup FAM (Req "
00427             << FAMREQUEST_GETREQNUM(&(e->fr))
00428             << ") for " << e->path << endl;
00429     }
00430   }
00431 
00432   // handle FAM events to avoid deadlock
00433   // (FAM sends back all files in a directory when monitoring)
00434   famEventReceived();
00435 
00436   return true;
00437 }
00438 #endif
00439 
00440 
00441 #ifdef HAVE_DNOTIFY
00442 // setup DNotify notification, returns false if not possible
00443 bool KDirWatchPrivate::useDNotify(Entry* e)
00444 {
00445   e->dn_fd = 0;
00446   if (!supports_dnotify) return false;
00447 
00448   e->m_mode = DNotifyMode;
00449 
00450   if (e->isDir) {
00451     e->dirty = false;
00452     if (e->m_status == Normal) {
00453       int fd = open(QFile::encodeName(e->path).data(), O_RDONLY);
00454       // Migrate fd to somewhere above 128. Some libraries have
00455       // constructs like:
00456       //    fd = socket(...)
00457       //    if (fd > ARBITRARY_LIMIT)
00458       //       return error;
00459       //
00460       // Since programs might end up using a lot of KDirWatch objects
00461       // for a rather long time the above braindamage could get
00462       // triggered.
00463       //
00464       // By moving the kdirwatch fd's to > 128, calls like socket() will keep
00465       // returning fd's < ARBITRARY_LIMIT for a bit longer.
00466       int fd2 = fcntl(fd, F_DUPFD, 128);
00467       if (fd2 >= 0)
00468       {
00469         close(fd);
00470         fd = fd2;
00471       }
00472       if (fd<0) {
00473     e->m_mode = UnknownMode;
00474     return false;
00475       }
00476 
00477       int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00478       // if dependant is a file watch, we check for MODIFY & ATTRIB too
00479       for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00480     if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00481 
00482       if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
00483      fcntl(fd, F_NOTIFY, mask) < 0) {
00484 
00485     kdDebug(7001) << "Not using Linux Directory Notifications."
00486               << endl;
00487     supports_dnotify = false;
00488     ::close(fd);
00489     e->m_mode = UnknownMode;
00490     return false;
00491       }
00492 
00493       fd_Entry.replace(fd, e);
00494       e->dn_fd = fd;
00495 
00496       kdDebug(7001) << " Setup DNotify (fd " << fd
00497             << ") for " << e->path << endl;
00498     }
00499     else { // NotExisting
00500       addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
00501     }
00502   }
00503   else { // File
00504     // we always watch the directory (DNOTIFY can't watch files alone)
00505     // this notifies us about changes of files therein
00506     addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
00507   }
00508 
00509   return true;
00510 }
00511 #endif
00512 
00513 
00514 bool KDirWatchPrivate::useStat(Entry* e)
00515 {
00516   if ( e->path.startsWith("/mnt/") || (e->path == "/mnt")
00517        || (KIO::probably_slow_mounted(e->path)) )
00518     useFreq(e, m_nfsPollInterval);
00519   else
00520     useFreq(e, m_PollInterval);
00521 
00522   if (e->m_mode != StatMode) {
00523     e->m_mode = StatMode;
00524     statEntries++;
00525 
00526     if ( statEntries == 1 ) {
00527       // if this was first STAT entry (=timer was stopped)
00528       timer->start(freq);      // then start the timer
00529       kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
00530     }
00531   }
00532 
00533   kdDebug(7001) << " Setup Stat (freq " << e->freq
00534         << ") for " << e->path << endl;
00535 
00536   return true;
00537 }
00538 
00539 
00540 /* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
00541  * providing in <isDir> the type of the entry to be watched.
00542  * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
00543  * this entry needs another entry to watch himself (when notExistent).
00544  */
00545 void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
00546                 Entry* sub_entry, bool isDir)
00547 {
00548   QString path = _path;
00549   if (path.startsWith("/dev/") || (path == "/dev"))
00550     return; // Don't even go there.
00551 
00552   if ( path.length() > 1 && path.right(1) == "/" )
00553     path.truncate( path.length() - 1 );
00554 
00555   EntryMap::Iterator it = m_mapEntries.find( path );
00556   if ( it != m_mapEntries.end() )
00557   {
00558     if (sub_entry) {
00559        (*it).m_entries.append(sub_entry);
00560        kdDebug(7001) << "Added already watched Entry " << path
00561              << " (for " << sub_entry->path << ")" << endl;
00562 #ifdef HAVE_DNOTIFY
00563        Entry* e = &(*it);
00564        if( e->dn_fd > 0 ) {
00565          int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
00566          // if dependant is a file watch, we check for MODIFY & ATTRIB too
00567          for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
00568            if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
00569      if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
00570        ::close(e->dn_fd);
00571        e->m_mode = UnknownMode;
00572        fd_Entry.remove(e->dn_fd);
00573            e->dn_fd = 0;
00574            useStat( e );
00575          }
00576        }
00577 #endif
00578     }
00579     else {
00580        (*it).addClient(instance);
00581        kdDebug(7001) << "Added already watched Entry " << path
00582              << " (now " <<  (*it).clients() << " clients)"
00583              << QString(" [%1]").arg(instance->name()) << endl;
00584     }
00585     return;
00586   }
00587 
00588   // we have a new path to watch
00589 
00590   struct stat stat_buf;
00591   QCString tpath = QFile::encodeName(path);
00592   bool exists = (stat(tpath, &stat_buf) == 0);
00593 
00594   Entry newEntry;
00595   m_mapEntries.insert( path, newEntry );
00596   // the insert does a copy, so we have to use <e> now
00597   Entry* e = &(m_mapEntries[path]);
00598 
00599   if (exists) {
00600     e->isDir = S_ISDIR(stat_buf.st_mode);
00601 
00602     if (e->isDir && !isDir)
00603       qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
00604     else if (!e->isDir && isDir)
00605       qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
00606 
00607     e->m_ctime = stat_buf.st_ctime;
00608     e->m_status = Normal;
00609     e->m_nlink = stat_buf.st_nlink;
00610   }
00611   else {
00612     e->isDir = isDir;
00613     e->m_ctime = invalid_ctime;
00614     e->m_status = NonExistent;
00615     e->m_nlink = 0;
00616   }
00617 
00618   e->path = path;
00619   if (sub_entry)
00620      e->m_entries.append(sub_entry);
00621   else
00622     e->addClient(instance);
00623 
00624   kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
00625         << (e->m_status == NonExistent ? " NotExisting" : "")
00626         << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00627         << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00628         << endl;
00629 
00630 
00631   // now setup the notification method
00632   e->m_mode = UnknownMode;
00633   e->msecLeft = 0;
00634 
00635   if ( isNoisyFile( tpath ) )
00636     return;
00637 
00638 #if defined(HAVE_FAM)
00639   if ( ! (path.startsWith("/mnt/") || (path == "/mnt")) )
00640      if (useFAM(e)) return;
00641 #endif
00642 
00643 #ifdef HAVE_DNOTIFY
00644   if ( ! (path.startsWith("/mnt/") || (path == "/mnt")) )
00645      if (useDNotify(e)) return;
00646 #endif
00647 
00648   useStat(e);
00649 }
00650 
00651 
00652 void KDirWatchPrivate::removeEntry( KDirWatch* instance,
00653                     const QString& _path, Entry* sub_entry )
00654 {
00655   Entry* e = entry(_path);
00656   if (!e) {
00657     kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
00658     return;
00659   }
00660 
00661   if (sub_entry)
00662     e->m_entries.removeRef(sub_entry);
00663   else
00664     e->removeClient(instance);
00665 
00666   if (e->m_clients.count() || e->m_entries.count())
00667     return;
00668 
00669   if (delayRemove) {
00670     // removeList is allowed to contain any entry at most once
00671     if (removeList.findRef(e)==-1)
00672     removeList.append(e);
00673     // now e->isValid() is false
00674     return;
00675   }
00676 
00677 #ifdef HAVE_FAM
00678   if (e->m_mode == FAMMode) {
00679     if ( e->m_status == Normal) {
00680       FAMCancelMonitor(&fc, &(e->fr) );
00681       kdDebug(7001) << "Cancelled FAM (Req "
00682             << FAMREQUEST_GETREQNUM(&(e->fr))
00683             << ") for " << e->path << endl;
00684     }
00685     else {
00686       if (e->isDir)
00687     removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00688       else
00689     removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00690     }
00691   }
00692 #endif
00693 
00694 #ifdef HAVE_DNOTIFY
00695   if (e->m_mode == DNotifyMode) {
00696     if (!e->isDir) {
00697       removeEntry(0, QFileInfo(e->path).dirPath(true), e);
00698     }
00699     else { // isDir
00700       // must close the FD.
00701       if ( e->m_status == Normal) {
00702     if (e->dn_fd) {
00703       ::close(e->dn_fd);
00704       fd_Entry.remove(e->dn_fd);
00705 
00706       kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
00707             << ") for " << e->path << endl;
00708       e->dn_fd = 0;
00709 
00710     }
00711       }
00712       else {
00713     removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
00714       }
00715     }
00716   }
00717 #endif
00718 
00719   if (e->m_mode == StatMode) {
00720     statEntries--;
00721     if ( statEntries == 0 ) {
00722       timer->stop(); // stop timer if lists are empty
00723       kdDebug(7001) << " Stopped Polling Timer" << endl;
00724     }
00725   }
00726 
00727   kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
00728         << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
00729         << (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
00730         << endl;
00731   m_mapEntries.remove( e->path ); // <e> not valid any more
00732 }
00733 
00734 
00735 /* Called from KDirWatch destructor:
00736  * remove <instance> as client from all entries
00737  */
00738 void KDirWatchPrivate::removeEntries( KDirWatch* instance )
00739 {
00740   QPtrList<Entry> list;
00741   int minfreq = 3600000;
00742 
00743   // put all entries where instance is a client in list
00744   EntryMap::Iterator it = m_mapEntries.begin();
00745   for( ; it != m_mapEntries.end(); ++it ) {
00746     Client* c = (*it).m_clients.first();
00747     for(;c;c=(*it).m_clients.next())
00748       if (c->instance == instance) break;
00749     if (c) {
00750       c->count = 1; // forces deletion of instance as client
00751       list.append(&(*it));
00752     }
00753     else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
00754       minfreq = (*it).freq;
00755   }
00756 
00757   for(Entry* e=list.first();e;e=list.next())
00758     removeEntry(instance, e->path, 0);
00759 
00760   if (minfreq > freq) {
00761     // we can decrease the global polling frequency
00762     freq = minfreq;
00763     if (timer->isActive()) timer->changeInterval(freq);
00764     kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
00765   }
00766 }
00767 
00768 // instance ==0: stop scanning for all instances
00769 bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
00770 {
00771   int stillWatching = 0;
00772   Client* c = e->m_clients.first();
00773   for(;c;c=e->m_clients.next()) {
00774     if (!instance || instance == c->instance)
00775       c->watchingStopped = true;
00776     else if (!c->watchingStopped)
00777       stillWatching += c->count;
00778   }
00779 
00780   kdDebug(7001) << instance->name() << " stopped scanning " << e->path
00781         << " (now " << stillWatching << " watchers)" << endl;
00782 
00783   if (stillWatching == 0) {
00784     // if nobody is interested, we don't watch
00785     e->m_ctime = invalid_ctime; // invalid
00786     //    e->m_status = Normal;
00787   }
00788   return true;
00789 }
00790 
00791 // instance ==0: start scanning for all instances
00792 bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
00793                      bool notify)
00794 {
00795   int wasWatching = 0, newWatching = 0;
00796   Client* c = e->m_clients.first();
00797   for(;c;c=e->m_clients.next()) {
00798     if (!c->watchingStopped)
00799       wasWatching += c->count;
00800     else if (!instance || instance == c->instance) {
00801       c->watchingStopped = false;
00802       newWatching += c->count;
00803     }
00804   }
00805   if (newWatching == 0)
00806     return false;
00807 
00808   kdDebug(7001) << instance->name() << " restarted scanning " << e->path
00809         << " (now " << wasWatching+newWatching << " watchers)" << endl;
00810 
00811   // restart watching and emit pending events
00812 
00813   int ev = NoChange;
00814   if (wasWatching == 0) {
00815     if (!notify) {
00816       struct stat stat_buf;
00817       bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00818       if (exists) {
00819     e->m_ctime = stat_buf.st_ctime;
00820     e->m_status = Normal;
00821         e->m_nlink = stat_buf.st_nlink;
00822       }
00823       else {
00824     e->m_ctime = invalid_ctime;
00825     e->m_status = NonExistent;
00826         e->m_nlink = 0;
00827       }
00828     }
00829     e->msecLeft = 0;
00830     ev = scanEntry(e);
00831   }
00832   emitEvent(e,ev);
00833 
00834   return true;
00835 }
00836 
00837 // instance ==0: stop scanning for all instances
00838 void KDirWatchPrivate::stopScan(KDirWatch* instance)
00839 {
00840   EntryMap::Iterator it = m_mapEntries.begin();
00841   for( ; it != m_mapEntries.end(); ++it )
00842     stopEntryScan(instance, &(*it));
00843 }
00844 
00845 
00846 void KDirWatchPrivate::startScan(KDirWatch* instance,
00847                  bool notify, bool skippedToo )
00848 {
00849   if (!notify)
00850     resetList(instance,skippedToo);
00851 
00852   EntryMap::Iterator it = m_mapEntries.begin();
00853   for( ; it != m_mapEntries.end(); ++it )
00854     restartEntryScan(instance, &(*it), notify);
00855 
00856   // timer should still be running when in polling mode
00857 }
00858 
00859 
00860 // clear all pending events, also from stopped
00861 void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
00862                   bool skippedToo )
00863 {
00864   EntryMap::Iterator it = m_mapEntries.begin();
00865   for( ; it != m_mapEntries.end(); ++it ) {
00866 
00867     Client* c = (*it).m_clients.first();
00868     for(;c;c=(*it).m_clients.next())
00869       if (!c->watchingStopped || skippedToo)
00870     c->pending = NoChange;
00871   }
00872 }
00873 
00874 // Return event happened on <e>
00875 //
00876 int KDirWatchPrivate::scanEntry(Entry* e)
00877 {
00878 #ifdef HAVE_FAM
00879   if (e->m_mode == FAMMode) {
00880     // we know nothing has changed, no need to stat
00881     if(!e->dirty) return NoChange;
00882     e->dirty = false;
00883   }
00884 #endif
00885 
00886   // Shouldn't happen: Ignore "unknown" notification method
00887   if (e->m_mode == UnknownMode) return NoChange;
00888 
00889 #ifdef HAVE_DNOTIFY
00890   if (e->m_mode == DNotifyMode) {
00891     // we know nothing has changed, no need to stat
00892     if(!e->dirty) return NoChange;
00893     e->dirty = false;
00894   }
00895 #endif
00896 
00897   if (e->m_mode == StatMode) {
00898     // only scan if timeout on entry timer happens;
00899     // e.g. when using 500msec global timer, a entry
00900     // with freq=5000 is only watched every 10th time
00901 
00902     e->msecLeft -= freq;
00903     if (e->msecLeft>0) return NoChange;
00904     e->msecLeft += e->freq;
00905   }
00906 
00907   struct stat stat_buf;
00908   bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
00909   if (exists) {
00910 
00911     if (e->m_status == NonExistent) {
00912       e->m_ctime = stat_buf.st_ctime;
00913       e->m_status = Normal;
00914       e->m_nlink = stat_buf.st_nlink;
00915       return Created;
00916     }
00917 
00918     if ( (e->m_ctime != invalid_ctime) &&
00919      ((stat_buf.st_ctime != e->m_ctime) ||
00920       (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
00921       e->m_ctime = stat_buf.st_ctime;
00922       e->m_nlink = stat_buf.st_nlink;
00923       return Changed;
00924     }
00925 
00926     return NoChange;
00927   }
00928 
00929   // dir/file doesn't exist
00930 
00931   if (e->m_ctime == invalid_ctime)
00932     return NoChange;
00933 
00934   e->m_ctime = invalid_ctime;
00935   e->m_nlink = 0;
00936   e->m_status = NonExistent;
00937 
00938   return Deleted;
00939 }
00940 
00941 /* Notify all interested KDirWatch instances about a given event on an entry
00942  * and stored pending events. When watching is stopped, the event is
00943  * added to the pending events.
00944  */
00945 void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
00946 {
00947   QString path = e->path;
00948   if (!fileName.isEmpty()) {
00949     if (fileName[0] == '/')
00950       path = fileName;
00951     else
00952       path += "/" + fileName;
00953   }
00954 
00955   Client* c = e->m_clients.first();
00956   for(;c;c=e->m_clients.next()) {
00957     if (c->instance==0 || c->count==0) continue;
00958 
00959     if (c->watchingStopped) {
00960       // add event to pending...
00961       if (event == Changed)
00962     c->pending |= event;
00963       else if (event == Created || event == Deleted)
00964     c->pending = event;
00965       continue;
00966     }
00967     // not stopped
00968     if (event == NoChange || event == Changed)
00969       event |= c->pending;
00970     c->pending = NoChange;
00971     if (event == NoChange) continue;
00972 
00973     if (event & Deleted) {
00974       c->instance->setDeleted(path);
00975       // emit only Deleted event...
00976       continue;
00977     }
00978 
00979     if (event & Created) {
00980       c->instance->setCreated(path);
00981       // possible emit Change event after creation
00982     }
00983 
00984     if (event & Changed)
00985       c->instance->setDirty(path);
00986   }
00987 }
00988 
00989 // Remove entries which were marked to be removed
00990 void KDirWatchPrivate::slotRemoveDelayed()
00991 {
00992   Entry* e;
00993   delayRemove = false;
00994   for(e=removeList.first();e;e=removeList.next())
00995     removeEntry(0, e->path, 0);
00996   removeList.clear();
00997 }
00998 
00999 /* Scan all entries to be watched for changes. This is done regularly
01000  * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
01001  */
01002 void KDirWatchPrivate::slotRescan()
01003 {
01004   EntryMap::Iterator it;
01005 
01006   // People can do very long things in the slot connected to dirty(),
01007   // like showing a message box. We don't want to keep polling during
01008   // that time, otherwise the value of 'delayRemove' will be reset.
01009   bool timerRunning = timer->isActive();
01010   if ( timerRunning )
01011     timer->stop();
01012 
01013   // We delay deletions of entries this way.
01014   // removeDir(), when called in slotDirty(), can cause a crash otherwise
01015   delayRemove = true;
01016 
01017 #ifdef HAVE_DNOTIFY
01018   QPtrList<Entry> dList, cList;
01019 #endif
01020 
01021   if (rescan_all)
01022   {
01023     // mark all as dirty
01024     it = m_mapEntries.begin();
01025     for( ; it != m_mapEntries.end(); ++it )
01026       (*it).dirty = true;
01027     rescan_all = false;
01028   }
01029   else
01030   {
01031     // progate dirty flag to dependant entries (e.g. file watches)
01032     it = m_mapEntries.begin();
01033     for( ; it != m_mapEntries.end(); ++it )
01034       if ( ((*it).m_mode == DNotifyMode) && (*it).dirty )
01035         (*it).propagate_dirty();
01036   }
01037 
01038   it = m_mapEntries.begin();
01039   for( ; it != m_mapEntries.end(); ++it ) {
01040     // we don't check invalid entries (i.e. remove delayed)
01041     if (!(*it).isValid()) continue;
01042 
01043     int ev = scanEntry( &(*it) );
01044 
01045 #ifdef HAVE_DNOTIFY
01046     if ((*it).m_mode == DNotifyMode) {
01047       if ((*it).isDir && (ev == Deleted)) {
01048     dList.append( &(*it) );
01049 
01050     // must close the FD.
01051     if ((*it).dn_fd) {
01052       ::close((*it).dn_fd);
01053       fd_Entry.remove((*it).dn_fd);
01054       (*it).dn_fd = 0;
01055     }
01056       }
01057 
01058       else if ((*it).isDir && (ev == Created)) {
01059     // For created, but yet without DNOTIFYing ...
01060     if ( (*it).dn_fd == 0) {
01061       cList.append( &(*it) );
01062       if (! useDNotify( &(*it) )) {
01063         // if DNotify setup fails...
01064         useStat( &(*it) );
01065       }
01066     }
01067       }
01068     }
01069 #endif
01070 
01071     if ( ev != NoChange )
01072       emitEvent( &(*it), ev);
01073   }
01074 
01075 
01076 #ifdef HAVE_DNOTIFY
01077   // Scan parent of deleted directories for new creation
01078   Entry* e;
01079   for(e=dList.first();e;e=dList.next())
01080     addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01081 
01082   // Remove watch of parent of new created directories
01083   for(e=cList.first();e;e=cList.next())
01084     removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
01085 #endif
01086 
01087   if ( timerRunning )
01088     timer->start(freq);
01089 
01090   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01091 }
01092 
01093 bool KDirWatchPrivate::isNoisyFile( const char * filename )
01094 {
01095   // $HOME/.X.err grows with debug output, so don't notify change
01096   if ( *filename == '.') {
01097     if (strncmp(filename, ".X.err", 6) == 0) return true;
01098     if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
01099     // fontconfig updates the cache on every KDE app start
01100     // (inclusive kio_thumbnail slaves)
01101     if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
01102   }
01103 
01104   return false;
01105 }
01106 
01107 #ifdef HAVE_FAM
01108 void KDirWatchPrivate::famEventReceived()
01109 {
01110   static FAMEvent fe;
01111 
01112   delayRemove = true;
01113 
01114   while(use_fam && FAMPending(&fc)) {
01115     if (FAMNextEvent(&fc, &fe) == -1) {
01116       kdWarning(7001) << "FAM connection problem, switching to polling."
01117               << endl;
01118       use_fam = false;
01119       delete sn; sn = 0;
01120 
01121       // Replace all FAMMode entries with DNotify/Stat
01122       EntryMap::Iterator it;
01123       it = m_mapEntries.begin();
01124       for( ; it != m_mapEntries.end(); ++it )
01125     if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
01126 #ifdef HAVE_DNOTIFY
01127       if (useDNotify( &(*it) )) continue;
01128 #endif
01129       useStat( &(*it) );
01130     }
01131     }
01132     else
01133       checkFAMEvent(&fe);
01134   }
01135 
01136   QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
01137 }
01138 
01139 void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
01140 {
01141   // Don't be too verbose ;-)
01142   if ((fe->code == FAMExists) ||
01143       (fe->code == FAMEndExist) ||
01144       (fe->code == FAMAcknowledge)) return;
01145 
01146   if ( isNoisyFile( fe->filename ) )
01147     return;
01148 
01149   Entry* e = 0;
01150   EntryMap::Iterator it = m_mapEntries.begin();
01151   for( ; it != m_mapEntries.end(); ++it )
01152     if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
01153        FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
01154       e = &(*it);
01155       break;
01156     }
01157 
01158   // Entry* e = static_cast<Entry*>(fe->userdata);
01159 
01160   kdDebug(7001) << "Processing FAM event ("
01161         << ((fe->code == FAMChanged) ? "FAMChanged" :
01162             (fe->code == FAMDeleted) ? "FAMDeleted" :
01163             (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
01164             (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
01165             (fe->code == FAMCreated) ? "FAMCreated" :
01166             (fe->code == FAMMoved) ? "FAMMoved" :
01167             (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
01168             (fe->code == FAMExists) ? "FAMExists" :
01169             (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
01170         << ", " << fe->filename
01171         << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
01172         << ")" << endl;
01173 
01174   if (!e) {
01175     // this happens e.g. for FAMAcknowledge after deleting a dir...
01176     //    kdDebug(7001) << "No entry for FAM event ?!" << endl;
01177     return;
01178   }
01179 
01180   if (e->m_status == NonExistent) {
01181     kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
01182     return;
01183   }
01184 
01185   // Delayed handling. This rechecks changes with own stat calls.
01186   e->dirty = true;
01187   if (!rescan_timer.isActive())
01188     rescan_timer.start(m_PollInterval, true);
01189 
01190   // needed FAM control actions on FAM events
01191   if (e->isDir)
01192     switch (fe->code)
01193     {
01194       case FAMDeleted:
01195        // file absolute: watched dir
01196         if (fe->filename[0] == '/')
01197         {
01198           // a watched directory was deleted
01199 
01200           e->m_status = NonExistent;
01201           FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
01202           kdDebug(7001) << "Cancelled FAMReq "
01203                         << FAMREQUEST_GETREQNUM(&(e->fr))
01204                         << " for " << e->path << endl;
01205           // Scan parent for a new creation
01206           addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
01207         }
01208         break;
01209 
01210       case FAMCreated: {
01211           // check for creation of a directory we have to watch
01212           Entry *sub_entry = e->m_entries.first();
01213           for(;sub_entry; sub_entry = e->m_entries.next())
01214             if (sub_entry->path == e->path + "/" + fe->filename) break;
01215           if (sub_entry && sub_entry->isDir) {
01216             QString path = e->path;
01217             removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
01218             sub_entry->m_status = Normal;
01219             if (!useFAM(sub_entry))
01220               useStat(sub_entry);
01221           }
01222           break;
01223         }
01224 
01225       default:
01226         break;
01227     }
01228 }
01229 #else
01230 void KDirWatchPrivate::famEventReceived() {}
01231 #endif
01232 
01233 
01234 void KDirWatchPrivate::statistics()
01235 {
01236   EntryMap::Iterator it;
01237 
01238   kdDebug(7001) << "Entries watched:" << endl;
01239   if (m_mapEntries.count()==0) {
01240     kdDebug(7001) << "  None." << endl;
01241   }
01242   else {
01243     it = m_mapEntries.begin();
01244     for( ; it != m_mapEntries.end(); ++it ) {
01245       Entry* e = &(*it);
01246       kdDebug(7001) << "  " << e->path << " ("
01247             << ((e->m_status==Normal)?"":"Nonexistent ")
01248             << (e->isDir ? "Dir":"File") << ", using "
01249             << ((e->m_mode == FAMMode) ? "FAM" :
01250             (e->m_mode == DNotifyMode) ? "DNotify" :
01251             (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
01252             << ")" << endl;
01253 
01254       Client* c = e->m_clients.first();
01255       for(;c; c = e->m_clients.next()) {
01256     QString pending;
01257     if (c->watchingStopped) {
01258       if (c->pending & Deleted) pending += "deleted ";
01259       if (c->pending & Created) pending += "created ";
01260       if (c->pending & Changed) pending += "changed ";
01261       if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
01262       pending = ", stopped" + pending;
01263     }
01264     kdDebug(7001) << "    by " << c->instance->name()
01265               << " (" << c->count << " times)"
01266               << pending << endl;
01267       }
01268       if (e->m_entries.count()>0) {
01269     kdDebug(7001) << "    dependent entries:" << endl;
01270     Entry* d = e->m_entries.first();
01271     for(;d; d = e->m_entries.next()) {
01272       kdDebug(7001) << "      " << d->path << endl;
01273     }
01274       }
01275     }
01276   }
01277 }
01278 
01279 
01280 //
01281 // Class KDirWatch
01282 //
01283 
01284 static KStaticDeleter<KDirWatch> sd_dw;
01285 KDirWatch* KDirWatch::s_pSelf = 0L;
01286 
01287 KDirWatch* KDirWatch::self()
01288 {
01289   if ( !s_pSelf ) {
01290     sd_dw.setObject( s_pSelf, new KDirWatch );
01291   }
01292 
01293   return s_pSelf;
01294 }
01295 
01296 bool KDirWatch::exists()
01297 {
01298   return s_pSelf != 0;
01299 }
01300 
01301 KDirWatch::KDirWatch (QObject* parent, const char* name)
01302   : QObject(parent,name)
01303 {
01304   if (!name) {
01305     static int nameCounter = 0;
01306 
01307     nameCounter++;
01308     setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
01309   }
01310 
01311   if (!dwp_self)
01312     dwp_self = new KDirWatchPrivate;
01313   d = dwp_self;
01314   d->ref();
01315 
01316   _isStopped = false;
01317 }
01318 
01319 KDirWatch::~KDirWatch()
01320 {
01321   if (d) d->removeEntries(this);
01322   if ( d->deref() )
01323   {
01324     // delete it if it's the last one
01325     delete d;
01326     dwp_self = 0L;
01327   }
01328 }
01329 
01330 
01331 // TODO: add watchFiles/recursive support
01332 void KDirWatch::addDir( const QString& _path,
01333             bool watchFiles, bool recursive)
01334 {
01335   if (watchFiles || recursive) {
01336     kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
01337           << endl;
01338   }
01339   if (d) d->addEntry(this, _path, 0, true);
01340 }
01341 
01342 void KDirWatch::addFile( const QString& _path )
01343 {
01344   if (d) d->addEntry(this, _path, 0, false);
01345 }
01346 
01347 QDateTime KDirWatch::ctime( const QString &_path )
01348 {
01349   KDirWatchPrivate::Entry* e = d->entry(_path);
01350 
01351   if (!e)
01352     return QDateTime();
01353 
01354   QDateTime result;
01355   result.setTime_t(e->m_ctime);
01356   return result;
01357 }
01358 
01359 void KDirWatch::removeDir( const QString& _path )
01360 {
01361   if (d) d->removeEntry(this, _path, 0);
01362 }
01363 
01364 void KDirWatch::removeFile( const QString& _path )
01365 {
01366   if (d) d->removeEntry(this, _path, 0);
01367 }
01368 
01369 bool KDirWatch::stopDirScan( const QString& _path )
01370 {
01371   if (d) {
01372     KDirWatchPrivate::Entry *e = d->entry(_path);
01373     if (e && e->isDir) return d->stopEntryScan(this, e);
01374   }
01375   return false;
01376 }
01377 
01378 bool KDirWatch::restartDirScan( const QString& _path )
01379 {
01380   if (d) {
01381     KDirWatchPrivate::Entry *e = d->entry(_path);
01382     if (e && e->isDir)
01383       // restart without notifying pending events
01384       return d->restartEntryScan(this, e, false);
01385   }
01386   return false;
01387 }
01388 
01389 void KDirWatch::stopScan()
01390 {
01391   if (d) d->stopScan(this);
01392   _isStopped = true;
01393 }
01394 
01395 void KDirWatch::startScan( bool notify, bool skippedToo )
01396 {
01397   _isStopped = false;
01398   if (d) d->startScan(this, notify, skippedToo);
01399 }
01400 
01401 
01402 bool KDirWatch::contains( const QString& _path ) const
01403 {
01404   KDirWatchPrivate::Entry* e = d->entry(_path);
01405   if (!e)
01406      return false;
01407 
01408   KDirWatchPrivate::Client* c = e->m_clients.first();
01409   for(;c;c=e->m_clients.next())
01410     if (c->instance == this) return true;
01411 
01412   return false;
01413 }
01414 
01415 void KDirWatch::statistics()
01416 {
01417   if (!dwp_self) {
01418     kdDebug(7001) << "KDirWatch not used" << endl;
01419     return;
01420   }
01421   dwp_self->statistics();
01422 }
01423 
01424 
01425 void KDirWatch::setCreated( const QString & _file )
01426 {
01427   kdDebug(7001) << name() << " emitting created " << _file << endl;
01428   emit created( _file );
01429 }
01430 
01431 void KDirWatch::setDirty( const QString & _file )
01432 {
01433   kdDebug(7001) << name() << " emitting dirty " << _file << endl;
01434   emit dirty( _file );
01435 }
01436 
01437 void KDirWatch::setDeleted( const QString & _file )
01438 {
01439   kdDebug(7001) << name() << " emitting deleted " << _file << endl;
01440   emit deleted( _file );
01441 }
01442 
01443 KDirWatch::Method KDirWatch::internalMethod()
01444 {
01445 #ifdef HAVE_FAM
01446   if (d->use_fam)
01447      return KDirWatch::FAM;
01448 #endif
01449 #ifdef HAVE_DNOTIFY
01450   if (d->supports_dnotify)
01451      return KDirWatch::DNotify;
01452 #endif
01453   return KDirWatch::Stat;
01454 }
01455 
01456 
01457 #include "kdirwatch.moc"
01458 #include "kdirwatch_p.moc"
01459 
01460 //sven
01461 
01462 // vim: sw=2 ts=8 et
KDE Logo
This file is part of the documentation for kio Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:17:15 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003